xref: /haiku/src/add-ons/kernel/busses/pci/x86/X86PCIController.cpp (revision 909af08f4328301fbdef1ffb41f566c3b5bec0c7)
1 /*
2  * Copyright 2022, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "X86PCIController.h"
8 
9 #include <ioapic.h>
10 
11 #include <AutoDeleterDrivers.h>
12 #include <util/AutoLock.h>
13 
14 #include <string.h>
15 #include <new>
16 
17 
18 #define PCI_MECH1_REQ_PORT				0xCF8
19 #define PCI_MECH1_DATA_PORT 			0xCFC
20 #define PCI_MECH1_REQ_DATA(bus, device, func, offset) \
21 	(0x80000000 | (bus << 16) | (device << 11) | (func << 8) | (offset & ~3))
22 
23 #define PCI_MECH2_ENABLE_PORT			0x0cf8
24 #define PCI_MECH2_FORWARD_PORT			0x0cfa
25 #define PCI_MECH2_CONFIG_PORT(dev, offset) \
26 	(uint16)(0xC00 | (dev << 8) | offset)
27 
28 
29 //#pragma mark - driver
30 
31 
32 float
33 X86PCIController::SupportsDevice(device_node* parent)
34 {
35 	const char* bus;
36 	if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) < B_OK)
37 		return -1.0f;
38 
39 	if (strcmp(bus, "root") == 0)
40 		return 1.0f;
41 
42 	return 0.0;
43 }
44 
45 
46 status_t
47 X86PCIController::RegisterDevice(device_node* parent)
48 {
49 	device_attr attrs[] = {
50 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "X86 PCI Host Controller"} },
51 		{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {.string = "bus_managers/pci/root/driver_v1"} },
52 		{}
53 	};
54 
55 	return gDeviceManager->register_node(parent, PCI_X86_DRIVER_MODULE_NAME, attrs, NULL, NULL);
56 }
57 
58 
59 status_t
60 X86PCIController::InitDriver(device_node* node, X86PCIController*& outDriver)
61 {
62 	bool search_mech1 = true;
63 	bool search_mech2 = true;
64 	bool search_mechpcie = true;
65 	void *config = NULL;
66 
67 	config = load_driver_settings("pci");
68 	if (config) {
69 		const char *mech = get_driver_parameter(config, "mechanism", NULL, NULL);
70 		if (mech) {
71 			search_mech1 = search_mech2 = search_mechpcie = false;
72 			if (strcmp(mech, "1") == 0)
73 				search_mech1 = true;
74 			else if (strcmp(mech, "2") == 0)
75 				search_mech2 = true;
76 			else if (strcmp(mech, "pcie") == 0)
77 				search_mechpcie = true;
78 			else
79 				panic("Unknown pci config mechanism setting %s\n", mech);
80 		}
81 		unload_driver_settings(config);
82 	}
83 
84 	// PCI configuration mechanism PCIe is the preferred one.
85 	// If it doesn't work, try mechanism 1.
86 	// If it doesn't work, try mechanism 2.
87 
88 	if (search_mechpcie) {
89 		if (CreateDriver(node, new(std::nothrow) X86PCIControllerMethPcie(), outDriver) >= B_OK)
90 			return B_OK;
91 	}
92 	if (search_mech1) {
93 		if (CreateDriver(node, new(std::nothrow) X86PCIControllerMeth1(), outDriver) >= B_OK)
94 			return B_OK;
95 	}
96 	if (search_mech2) {
97 		if (CreateDriver(node, new(std::nothrow) X86PCIControllerMeth2(), outDriver) >= B_OK)
98 			return B_OK;
99 	}
100 
101 	dprintf("PCI: no configuration mechanism found\n");
102 	return B_ERROR;
103 }
104 
105 
106 status_t
107 X86PCIController::CreateDriver(device_node* node, X86PCIController* driverIn,
108 	X86PCIController*& driverOut)
109 {
110 	ObjectDeleter<X86PCIController> driver(driverIn);
111 	if (!driver.IsSet())
112 		return B_NO_MEMORY;
113 
114 	CHECK_RET(driver->InitDriverInt(node));
115 	driverOut = driver.Detach();
116 	return B_OK;
117 }
118 
119 
120 status_t
121 X86PCIController::InitDriverInt(device_node* node)
122 {
123 	fNode = node;
124 	return B_OK;
125 }
126 
127 
128 void
129 X86PCIController::UninitDriver()
130 {
131 	delete this;
132 }
133 
134 
135 //#pragma mark - PCI controller
136 
137 
138 status_t
139 X86PCIController::ReadIrq(uint8 bus, uint8 device, uint8 function,
140 	uint8 pin, uint8& irq)
141 {
142 	return B_UNSUPPORTED;
143 }
144 
145 
146 status_t
147 X86PCIController::WriteIrq(uint8 bus, uint8 device, uint8 function,
148 	uint8 pin, uint8 irq)
149 {
150 	return B_UNSUPPORTED;
151 }
152 
153 
154 status_t
155 X86PCIController::GetRange(uint32 index, pci_resource_range* range)
156 {
157 	return B_BAD_INDEX;
158 }
159 
160 
161 status_t
162 X86PCIController::Finalize()
163 {
164 	ioapic_init();
165 	return B_OK;
166 }
167 
168 
169 //#pragma mark - X86PCIControllerMeth1
170 
171 
172 status_t
173 X86PCIControllerMeth1::InitDriverInt(device_node* node)
174 {
175 	CHECK_RET(X86PCIController::InitDriverInt(node));
176 
177 	// check for mechanism 1
178 	out32(0x80000000, PCI_MECH1_REQ_PORT);
179 	if (0x80000000 == in32(PCI_MECH1_REQ_PORT)) {
180 		dprintf("PCI: mechanism 1 controller found\n");
181 		return B_OK;
182 	}
183 	return B_ERROR;
184 }
185 
186 
187 status_t
188 X86PCIControllerMeth1::ReadConfig(
189 	uint8 bus, uint8 device, uint8 function,
190 	uint16 offset, uint8 size, uint32 &value)
191 {
192 	if (offset > 0xff)
193 		return ERANGE;
194 
195 	InterruptsSpinLocker lock(fLock);
196 	out32(PCI_MECH1_REQ_DATA(bus, device, function, offset), PCI_MECH1_REQ_PORT);
197 	switch (size) {
198 		case 1:
199 			value = in8(PCI_MECH1_DATA_PORT + (offset & 3));
200 			break;
201 		case 2:
202 			value = in16(PCI_MECH1_DATA_PORT + (offset & 3));
203 			break;
204 		case 4:
205 			value = in32(PCI_MECH1_DATA_PORT);
206 			break;
207 		default:
208 			return B_BAD_VALUE;
209 	}
210 
211 	return B_OK;
212 }
213 
214 
215 status_t
216 X86PCIControllerMeth1::WriteConfig(
217 	uint8 bus, uint8 device, uint8 function,
218 	uint16 offset, uint8 size, uint32 value)
219 {
220 	if (offset > 0xff)
221 		return ERANGE;
222 
223 	InterruptsSpinLocker lock(fLock);
224 	out32(PCI_MECH1_REQ_DATA(bus, device, function, offset), PCI_MECH1_REQ_PORT);
225 	switch (size) {
226 		case 1:
227 			out8(value, PCI_MECH1_DATA_PORT + (offset & 3));
228 			break;
229 		case 2:
230 			out16(value, PCI_MECH1_DATA_PORT + (offset & 3));
231 			break;
232 		case 4:
233 			out32(value, PCI_MECH1_DATA_PORT);
234 			break;
235 		default:
236 			return B_BAD_VALUE;
237 	}
238 
239 	return B_OK;
240 }
241 
242 
243 status_t X86PCIControllerMeth1::GetMaxBusDevices(int32& count)
244 {
245 	count = 32;
246 	return B_OK;
247 }
248 
249 
250 //#pragma mark - X86PCIControllerMeth2
251 
252 
253 status_t
254 X86PCIControllerMeth2::InitDriverInt(device_node* node)
255 {
256 	CHECK_RET(X86PCIController::InitDriverInt(node));
257 
258 	// check for mechanism 2
259 	out8(0x00, 0xCFB);
260 	out8(0x00, 0xCF8);
261 	out8(0x00, 0xCFA);
262 	if (in8(0xCF8) == 0x00 && in8(0xCFA) == 0x00) {
263 		dprintf("PCI: mechanism 2 controller found\n");
264 		return B_OK;
265 	}
266 	return B_ERROR;
267 }
268 
269 
270 status_t
271 X86PCIControllerMeth2::ReadConfig(
272 	uint8 bus, uint8 device, uint8 function,
273 	uint16 offset, uint8 size, uint32 &value)
274 {
275 	if (offset > 0xff)
276 		return ERANGE;
277 
278 	InterruptsSpinLocker lock(fLock);
279 	out8((uint8)(0xf0 | (function << 1)), PCI_MECH2_ENABLE_PORT);
280 	out8(bus, PCI_MECH2_FORWARD_PORT);
281 	switch (size) {
282 		case 1:
283 			value = in8(PCI_MECH2_CONFIG_PORT(device, offset));
284 			break;
285 		case 2:
286 			value = in16(PCI_MECH2_CONFIG_PORT(device, offset));
287 			break;
288 		case 4:
289 			value = in32(PCI_MECH2_CONFIG_PORT(device, offset));
290 			break;
291 		default:
292 			return B_BAD_VALUE;
293 	}
294 	out8(0, PCI_MECH2_ENABLE_PORT);
295 
296 	return B_OK;
297 }
298 
299 
300 status_t
301 X86PCIControllerMeth2::WriteConfig(
302 	uint8 bus, uint8 device, uint8 function,
303 	uint16 offset, uint8 size, uint32 value)
304 {
305 	if (offset > 0xff)
306 		return ERANGE;
307 
308 	InterruptsSpinLocker lock(fLock);
309 	out8((uint8)(0xf0 | (function << 1)), PCI_MECH2_ENABLE_PORT);
310 	out8(bus, PCI_MECH2_FORWARD_PORT);
311 	switch (size) {
312 		case 1:
313 			out8(value, PCI_MECH2_CONFIG_PORT(device, offset));
314 			break;
315 		case 2:
316 			out16(value, PCI_MECH2_CONFIG_PORT(device, offset));
317 			break;
318 		case 4:
319 			out32(value, PCI_MECH2_CONFIG_PORT(device, offset));
320 			break;
321 		default:
322 			return B_BAD_VALUE;
323 	}
324 	out8(0, PCI_MECH2_ENABLE_PORT);
325 
326 	return B_OK;
327 }
328 
329 
330 status_t X86PCIControllerMeth2::GetMaxBusDevices(int32& count)
331 {
332 	count = 16;
333 	return B_OK;
334 }
335 
336 
337 //#pragma mark - X86PCIControllerMethPcie
338 
339 
340 status_t
341 X86PCIControllerMethPcie::InitDriverInt(device_node* node)
342 {
343 	status_t status = X86PCIController::InitDriverInt(node);
344 	if (status != B_OK)
345 		return status;
346 
347 	// search ACPI
348 	device_node *acpiNode = NULL;
349 	{
350 		device_node* deviceRoot = gDeviceManager->get_root_node();
351 		device_attr acpiAttrs[] = {
352 			{ B_DEVICE_BUS, B_STRING_TYPE, { .string = "acpi" }},
353 			{ ACPI_DEVICE_HID_ITEM, B_STRING_TYPE, { .string = "PNP0A08" }},
354 			{ NULL }
355 		};
356 		if (gDeviceManager->find_child_node(deviceRoot, acpiAttrs, &acpiNode) != B_OK)
357 			return ENODEV;
358 	}
359 
360 	status = fECAMPCIController.ReadResourceInfo(acpiNode);
361 	gDeviceManager->put_node(acpiNode);
362 	return status;
363 }
364 
365 
366 status_t
367 X86PCIControllerMethPcie::ReadConfig(
368 	uint8 bus, uint8 device, uint8 function,
369 	uint16 offset, uint8 size, uint32 &value)
370 {
371 	// fallback to mechanism 1 for out of range busses
372 	if (bus < fECAMPCIController.fStartBusNumber || bus > fECAMPCIController.fEndBusNumber) {
373 		return X86PCIControllerMeth1::ReadConfig(bus, device, function, offset,
374 			size, value);
375 	}
376 
377 	return fECAMPCIController.ReadConfig(bus, device, function, offset, size, value);
378 }
379 
380 
381 status_t
382 X86PCIControllerMethPcie::WriteConfig(
383 	uint8 bus, uint8 device, uint8 function,
384 	uint16 offset, uint8 size, uint32 value)
385 {
386 	// fallback to mechanism 1 for out of range busses
387 	if (bus < fECAMPCIController.fStartBusNumber || bus > fECAMPCIController.fEndBusNumber) {
388 		return X86PCIControllerMeth1::WriteConfig(bus, device, function, offset,
389 			size, value);
390 	}
391 
392 	return fECAMPCIController.WriteConfig(bus, device, function, offset, size, value);
393 }
394 
395 
396 status_t
397 X86PCIControllerMethPcie::GetMaxBusDevices(int32& count)
398 {
399 	return fECAMPCIController.GetMaxBusDevices(count);
400 }
401 
402 
403 status_t
404 X86PCIControllerMethPcie::GetRange(uint32 index, pci_resource_range* range)
405 {
406 	return fECAMPCIController.GetRange(index, range);
407 }
408