xref: /haiku/src/add-ons/kernel/busses/pci/ecam/ECAMPCIController.cpp (revision ba0223da5d79c5cd27496ee0e5712921cebb7642)
1 /*
2  * Copyright 2022, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ECAMPCIController.h"
8 #include <acpi.h>
9 
10 #include <AutoDeleterDrivers.h>
11 
12 #include "acpi_irq_routing_table.h"
13 
14 #include <string.h>
15 #include <new>
16 
17 
18 static uint32
19 ReadReg8(addr_t adr)
20 {
21 	uint32 ofs = adr % 4;
22 	adr = adr / 4 * 4;
23 	union {
24 		uint32 in;
25 		uint8 out[4];
26 	} val{.in = *(vuint32*)adr};
27 	return val.out[ofs];
28 }
29 
30 
31 static uint32
32 ReadReg16(addr_t adr)
33 {
34 	uint32 ofs = adr / 2 % 2;
35 	adr = adr / 4 * 4;
36 	union {
37 		uint32 in;
38 		uint16 out[2];
39 	} val{.in = *(vuint32*)adr};
40 	return val.out[ofs];
41 }
42 
43 
44 static void
45 WriteReg8(addr_t adr, uint32 value)
46 {
47 	uint32 ofs = adr % 4;
48 	adr = adr / 4 * 4;
49 	union {
50 		uint32 in;
51 		uint8 out[4];
52 	} val{.in = *(vuint32*)adr};
53 	val.out[ofs] = (uint8)value;
54 	*(vuint32*)adr = val.in;
55 }
56 
57 
58 static void
59 WriteReg16(addr_t adr, uint32 value)
60 {
61 	uint32 ofs = adr / 2 % 2;
62 	adr = adr / 4 * 4;
63 	union {
64 		uint32 in;
65 		uint16 out[2];
66 	} val{.in = *(vuint32*)adr};
67 	val.out[ofs] = (uint16)value;
68 	*(vuint32*)adr = val.in;
69 }
70 
71 
72 //#pragma mark - driver
73 
74 
75 float
76 ECAMPCIController::SupportsDevice(device_node* parent)
77 {
78 	const char* bus;
79 	status_t status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false);
80 	if (status < B_OK)
81 		return -1.0f;
82 
83 	if (strcmp(bus, "fdt") == 0) {
84 		const char* compatible;
85 		status = gDeviceManager->get_attr_string(parent, "fdt/compatible", &compatible, false);
86 		if (status < B_OK)
87 			return -1.0f;
88 
89 		if (strcmp(compatible, "pci-host-ecam-generic") != 0)
90 			return 0.0f;
91 
92 		return 1.0f;
93 	}
94 
95 	if (strcmp(bus, "acpi") == 0) {
96 		const char* hid;
97 		if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, false) < B_OK)
98 			return -1.0f;
99 
100 		if (strcmp(hid, "PNP0A03") != 0 && strcmp(hid, "PNP0A08") != 0)
101 			return 0.0f;
102 
103 		return 1.0f;
104 	}
105 
106 	return 0.0f;
107 }
108 
109 
110 status_t
111 ECAMPCIController::RegisterDevice(device_node* parent)
112 {
113 	device_attr attrs[] = {
114 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "ECAM PCI Host Controller"} },
115 		{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {.string = "bus_managers/pci/root/driver_v1"} },
116 		{}
117 	};
118 
119 	return gDeviceManager->register_node(parent, ECAM_PCI_DRIVER_MODULE_NAME, attrs, NULL, NULL);
120 }
121 
122 
123 #if !defined(ECAM_PCI_CONTROLLER_NO_INIT)
124 status_t
125 ECAMPCIController::InitDriver(device_node* node, ECAMPCIController*& outDriver)
126 {
127 	dprintf("+ECAMPCIController::InitDriver()\n");
128 	DeviceNodePutter<&gDeviceManager> parentNode(gDeviceManager->get_parent_node(node));
129 
130 	ObjectDeleter<ECAMPCIController> driver;
131 
132 	const char* bus;
133 	CHECK_RET(gDeviceManager->get_attr_string(parentNode.Get(), B_DEVICE_BUS, &bus, false));
134 	if (strcmp(bus, "fdt") == 0)
135 		driver.SetTo(new(std::nothrow) ECAMPCIControllerFDT());
136 	else if (strcmp(bus, "acpi") == 0)
137 		driver.SetTo(new(std::nothrow) ECAMPCIControllerACPI());
138 	else
139 		return B_ERROR;
140 
141 	if (!driver.IsSet())
142 		return B_NO_MEMORY;
143 
144 	driver->fNode = node;
145 
146 	CHECK_RET(driver->ReadResourceInfo());
147 	outDriver = driver.Detach();
148 
149 	dprintf("-ECAMPCIController::InitDriver()\n");
150 	return B_OK;
151 }
152 
153 
154 void
155 ECAMPCIController::UninitDriver()
156 {
157 	delete this;
158 }
159 #endif
160 
161 
162 addr_t
163 ECAMPCIController::ConfigAddress(uint8 bus, uint8 device, uint8 function, uint16 offset)
164 {
165 	PciAddressEcam address {
166 		.offset = offset,
167 		.function = function,
168 		.device = device,
169 		.bus = bus
170 	};
171 	if ((address.val + 4) > fRegsLen)
172 		return 0;
173 
174 	return (addr_t)fRegs + address.val;
175 }
176 
177 
178 //#pragma mark - PCI controller
179 
180 
181 status_t
182 ECAMPCIController::ReadConfig(uint8 bus, uint8 device, uint8 function,
183 	uint16 offset, uint8 size, uint32& value)
184 {
185 	addr_t address = ConfigAddress(bus, device, function, offset);
186 	if (address == 0)
187 		return ERANGE;
188 
189 	switch (size) {
190 		case 1: value = ReadReg8(address); break;
191 		case 2: value = ReadReg16(address); break;
192 		case 4: value = *(vuint32*)address; break;
193 		default:
194 			return B_BAD_VALUE;
195 	}
196 
197 	return B_OK;
198 }
199 
200 
201 status_t
202 ECAMPCIController::WriteConfig(uint8 bus, uint8 device, uint8 function,
203 	uint16 offset, uint8 size, uint32 value)
204 {
205 	addr_t address = ConfigAddress(bus, device, function, offset);
206 	if (address == 0)
207 		return ERANGE;
208 
209 	switch (size) {
210 		case 1: WriteReg8(address, value); break;
211 		case 2: WriteReg16(address, value); break;
212 		case 4: *(vuint32*)address = value; break;
213 		default:
214 			return B_BAD_VALUE;
215 	}
216 
217 	return B_OK;
218 }
219 
220 
221 status_t
222 ECAMPCIController::GetMaxBusDevices(int32& count)
223 {
224 	count = 32;
225 	return B_OK;
226 }
227 
228 
229 status_t
230 ECAMPCIController::ReadIrq(uint8 bus, uint8 device, uint8 function,
231 	uint8 pin, uint8& irq)
232 {
233 	return B_UNSUPPORTED;
234 }
235 
236 
237 status_t
238 ECAMPCIController::WriteIrq(uint8 bus, uint8 device, uint8 function,
239 	uint8 pin, uint8 irq)
240 {
241 	return B_UNSUPPORTED;
242 }
243 
244 
245 status_t
246 ECAMPCIController::GetRange(uint32 index, pci_resource_range* range)
247 {
248 	if (index >= (uint32)fResourceRanges.Count())
249 		return B_BAD_INDEX;
250 
251 	*range = fResourceRanges[index];
252 	return B_OK;
253 }
254