xref: /haiku/src/add-ons/kernel/busses/pci/designware/DWPCIController.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
1 /*
2  * Copyright 2022, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "DWPCIController.h"
8 #include <bus/FDT.h>
9 
10 #include <AutoDeleterDrivers.h>
11 #include <util/AutoLock.h>
12 
13 #include <string.h>
14 #include <new>
15 
16 
17 static uint32
18 ReadReg8(addr_t adr)
19 {
20 	uint32 ofs = adr % 4;
21 	adr = adr / 4 * 4;
22 	union {
23 		uint32 in;
24 		uint8 out[4];
25 	} val{.in = *(vuint32*)adr};
26 	return val.out[ofs];
27 }
28 
29 
30 static uint32
31 ReadReg16(addr_t adr)
32 {
33 	uint32 ofs = adr / 2 % 2;
34 	adr = adr / 4 * 4;
35 	union {
36 		uint32 in;
37 		uint16 out[2];
38 	} val{.in = *(vuint32*)adr};
39 	return val.out[ofs];
40 }
41 
42 
43 static void
44 WriteReg8(addr_t adr, uint32 value)
45 {
46 	uint32 ofs = adr % 4;
47 	adr = adr / 4 * 4;
48 	union {
49 		uint32 in;
50 		uint8 out[4];
51 	} val{.in = *(vuint32*)adr};
52 	val.out[ofs] = (uint8)value;
53 	*(vuint32*)adr = val.in;
54 }
55 
56 
57 static void
58 WriteReg16(addr_t adr, uint32 value)
59 {
60 	uint32 ofs = adr / 2 % 2;
61 	adr = adr / 4 * 4;
62 	union {
63 		uint32 in;
64 		uint16 out[2];
65 	} val{.in = *(vuint32*)adr};
66 	val.out[ofs] = (uint16)value;
67 	*(vuint32*)adr = val.in;
68 }
69 
70 
71 //#pragma mark - driver
72 
73 
74 float
75 DWPCIController::SupportsDevice(device_node* parent)
76 {
77 	const char* bus;
78 	status_t status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false);
79 	if (status < B_OK)
80 		return -1.0f;
81 
82 	if (strcmp(bus, "fdt") != 0)
83 		return 0.0f;
84 
85 	const char* compatible;
86 	status = gDeviceManager->get_attr_string(parent, "fdt/compatible", &compatible, false);
87 	if (status < B_OK)
88 		return -1.0f;
89 
90 	// Support only a variant used in HiFive Unmatched board.
91 	// TODO: Support more Synapsis Designware IP core based PCIe host controllers.
92 	if (strcmp(compatible, "sifive,fu740-pcie") != 0)
93 		return 0.0f;
94 
95 	return 1.0f;
96 }
97 
98 
99 status_t
100 DWPCIController::RegisterDevice(device_node* parent)
101 {
102 	device_attr attrs[] = {
103 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "Designware PCI Host Controller"} },
104 		{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {.string = "bus_managers/pci/root/driver_v1"} },
105 		{}
106 	};
107 
108 	return gDeviceManager->register_node(parent, DESIGNWARE_PCI_DRIVER_MODULE_NAME, attrs, NULL,
109 		NULL);
110 }
111 
112 
113 status_t
114 DWPCIController::InitDriver(device_node* node, DWPCIController*& outDriver)
115 {
116 	ObjectDeleter<DWPCIController> driver(new(std::nothrow) DWPCIController());
117 	if (!driver.IsSet())
118 		return B_NO_MEMORY;
119 
120 	CHECK_RET(driver->InitDriverInt(node));
121 	outDriver = driver.Detach();
122 	return B_OK;
123 }
124 
125 
126 status_t
127 DWPCIController::ReadResourceInfo()
128 {
129 	DeviceNodePutter<&gDeviceManager> fdtNode(gDeviceManager->get_parent_node(fNode));
130 
131 	const char* bus;
132 	CHECK_RET(gDeviceManager->get_attr_string(fdtNode.Get(), B_DEVICE_BUS, &bus, false));
133 	if (strcmp(bus, "fdt") != 0)
134 		return B_ERROR;
135 
136 	fdt_device_module_info *fdtModule;
137 	fdt_device* fdtDev;
138 	CHECK_RET(gDeviceManager->get_driver(fdtNode.Get(),
139 		(driver_module_info**)&fdtModule, (void**)&fdtDev));
140 
141 	const void* prop;
142 	int propLen;
143 
144 	prop = fdtModule->get_prop(fdtDev, "bus-range", &propLen);
145 	if (prop != NULL && propLen == 8) {
146 		uint32 busBeg = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 0));
147 		uint32 busEnd = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 1));
148 		dprintf("  bus-range: %" B_PRIu32 " - %" B_PRIu32 "\n", busBeg, busEnd);
149 	}
150 
151 	prop = fdtModule->get_prop(fdtDev, "interrupt-map-mask", &propLen);
152 	if (prop == NULL || propLen != 4 * 4) {
153 		dprintf("  \"interrupt-map-mask\" property not found or invalid");
154 		return B_ERROR;
155 	}
156 	fInterruptMapMask.childAdr = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 0));
157 	fInterruptMapMask.childIrq = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 3));
158 
159 	prop = fdtModule->get_prop(fdtDev, "interrupt-map", &propLen);
160 	fInterruptMapLen = (uint32)propLen / (6 * 4);
161 	fInterruptMap.SetTo(new(std::nothrow) InterruptMap[fInterruptMapLen]);
162 	if (!fInterruptMap.IsSet())
163 		return B_NO_MEMORY;
164 
165 	for (uint32_t *it = (uint32_t*)prop; (uint8_t*)it - (uint8_t*)prop < propLen; it += 6) {
166 		size_t i = (it - (uint32_t*)prop) / 6;
167 
168 		fInterruptMap[i].childAdr = B_BENDIAN_TO_HOST_INT32(*(it + 0));
169 		fInterruptMap[i].childIrq = B_BENDIAN_TO_HOST_INT32(*(it + 3));
170 		fInterruptMap[i].parentIrqCtrl = B_BENDIAN_TO_HOST_INT32(*(it + 4));
171 		fInterruptMap[i].parentIrq = B_BENDIAN_TO_HOST_INT32(*(it + 5));
172 	}
173 
174 	dprintf("  interrupt-map:\n");
175 	for (size_t i = 0; i < fInterruptMapLen; i++) {
176 		dprintf("    ");
177 		// child unit address
178 		PciAddress pciAddress{.val = fInterruptMap[i].childAdr};
179 		dprintf("bus: %" B_PRIu32, pciAddress.bus);
180 		dprintf(", dev: %" B_PRIu32, pciAddress.device);
181 		dprintf(", fn: %" B_PRIu32, pciAddress.function);
182 
183 		dprintf(", childIrq: %" B_PRIu32, fInterruptMap[i].childIrq);
184 		dprintf(", parentIrq: (%" B_PRIu32, fInterruptMap[i].parentIrqCtrl);
185 		dprintf(", %" B_PRIu32, fInterruptMap[i].parentIrq);
186 		dprintf(")\n");
187 		if (i % 4 == 3 && (i + 1 < fInterruptMapLen))
188 			dprintf("\n");
189 	}
190 
191 	prop = fdtModule->get_prop(fdtDev, "ranges", &propLen);
192 	if (prop == NULL) {
193 		dprintf("  \"ranges\" property not found");
194 		return B_ERROR;
195 	}
196 	dprintf("  ranges:\n");
197 	for (uint32_t *it = (uint32_t*)prop; (uint8_t*)it - (uint8_t*)prop < propLen; it += 7) {
198 		dprintf("    ");
199 		uint32_t type      = B_BENDIAN_TO_HOST_INT32(*(it + 0));
200 		uint64_t childAdr  = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 1));
201 		uint64_t parentAdr = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 3));
202 		uint64_t len       = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 5));
203 
204 		uint32 outType = kPciRangeInvalid;
205 		switch (type & fdtPciRangeTypeMask) {
206 		case fdtPciRangeIoPort:
207 			outType = kPciRangeIoPort;
208 			break;
209 		case fdtPciRangeMmio32Bit:
210 			outType = kPciRangeMmio;
211 			break;
212 		case fdtPciRangeMmio64Bit:
213 			outType = kPciRangeMmio + kPciRangeMmio64Bit;
214 			break;
215 		}
216 		if (outType >= kPciRangeMmio && outType < kPciRangeMmioEnd
217 			&& (fdtPciRangePrefechable & type) != 0)
218 			outType += kPciRangeMmioPrefetch;
219 
220 		if (outType != kPciRangeInvalid) {
221 			fResourceRanges[outType].type = outType;
222 			fResourceRanges[outType].host_addr = parentAdr;
223 			fResourceRanges[outType].pci_addr = childAdr;
224 			fResourceRanges[outType].size = len;
225 		}
226 
227 		switch (type & fdtPciRangeTypeMask) {
228 		case fdtPciRangeConfig:    dprintf("CONFIG"); break;
229 		case fdtPciRangeIoPort:    dprintf("IOPORT"); break;
230 		case fdtPciRangeMmio32Bit: dprintf("MMIO32"); break;
231 		case fdtPciRangeMmio64Bit: dprintf("MMIO64"); break;
232 		}
233 
234 		dprintf(" (0x%08" B_PRIx32 "): ", type);
235 		dprintf("child: %08" B_PRIx64, childAdr);
236 		dprintf(", parent: %08" B_PRIx64, parentAdr);
237 		dprintf(", len: %" B_PRIx64 "\n", len);
238 	}
239 	return B_OK;
240 }
241 
242 
243 status_t
244 DWPCIController::InitDriverInt(device_node* node)
245 {
246 	fNode = node;
247 	dprintf("+DWPCIController::InitDriver()\n");
248 
249 	CHECK_RET(ReadResourceInfo());
250 
251 	DeviceNodePutter<&gDeviceManager> fdtNode(gDeviceManager->get_parent_node(node));
252 
253 	fdt_device_module_info *fdtModule;
254 	fdt_device* fdtDev;
255 	CHECK_RET(gDeviceManager->get_driver(fdtNode.Get(),
256 		(driver_module_info**)&fdtModule, (void**)&fdtDev));
257 
258 	if (!fdtModule->get_reg(fdtDev, 0, &fDbiPhysBase, &fDbiSize))
259 		return B_ERROR;
260 	dprintf("  DBI: %08" B_PRIx64 ", %08" B_PRIx64 "\n", fDbiPhysBase, fDbiSize);
261 
262 	if (!fdtModule->get_reg(fdtDev, 1, &fConfigPhysBase, &fConfigSize))
263 		return B_ERROR;
264 	dprintf("  config: %08" B_PRIx64 ", %08" B_PRIx64 "\n", fConfigPhysBase, fConfigSize);
265 
266 	uint64 msiIrq;
267 	if (!fdtModule->get_interrupt(fdtDev, 0, NULL, &msiIrq))
268 		return B_ERROR;
269 
270 	fDbiArea.SetTo(map_physical_memory("PCI DBI MMIO", fDbiPhysBase, fDbiSize, B_ANY_KERNEL_ADDRESS,
271 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fDbiBase));
272 	CHECK_RET(fDbiArea.Get());
273 
274 	fConfigArea.SetTo(map_physical_memory("PCI Config MMIO", fConfigPhysBase, fConfigSize,
275 		B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fConfigBase));
276 	CHECK_RET(fConfigArea.Get());
277 
278 	CHECK_RET(fIrqCtrl.Init(GetDbuRegs(), msiIrq));
279 
280 	AtuDump();
281 
282 	dprintf("-DWPCIController::InitDriver()\n");
283 	return B_OK;
284 }
285 
286 
287 void
288 DWPCIController::UninitDriver()
289 {
290 	delete this;
291 }
292 
293 
294 addr_t
295 DWPCIController::ConfigAddress(uint8 bus, uint8 device, uint8 function, uint16 offset)
296 {
297 	uint32 atuType;
298 	if (bus == 0) {
299 		if (device != 0 || function != 0)
300 			return 0;
301 		return fDbiBase + offset;
302 	} else if (bus == 1)
303 		atuType = kPciAtuTypeCfg0;
304 	else
305 		atuType = kPciAtuTypeCfg1;
306 
307 	uint64 address = (uint64)(PciAddress {
308 		.function = function,
309 		.device = device,
310 		.bus = bus
311 	}.val) << 8;
312 
313 	status_t res = AtuMap(1, kPciAtuOutbound, atuType, fConfigPhysBase, address, fConfigSize);
314 	if (res < B_OK)
315 		return 0;
316 
317 	return fConfigBase + offset;
318 }
319 
320 
321 //#pragma mark - PCI controller
322 
323 
324 status_t
325 DWPCIController::ReadConfig(uint8 bus, uint8 device, uint8 function,
326 	uint16 offset, uint8 size, uint32& value)
327 {
328 	InterruptsSpinLocker lock(fLock);
329 
330 	addr_t address = ConfigAddress(bus, device, function, offset);
331 	if (address == 0)
332 		return B_ERROR;
333 
334 	switch (size) {
335 		case 1: value = ReadReg8(address); break;
336 		case 2: value = ReadReg16(address); break;
337 		case 4: value = *(vuint32*)address; break;
338 		default:
339 			return B_ERROR;
340 	}
341 
342 	return B_OK;
343 }
344 
345 
346 status_t
347 DWPCIController::WriteConfig(uint8 bus, uint8 device, uint8 function,
348 	uint16 offset, uint8 size, uint32 value)
349 {
350 	InterruptsSpinLocker lock(fLock);
351 
352 	addr_t address = ConfigAddress(bus, device, function, offset);
353 	if (address == 0)
354 		return B_ERROR;
355 
356 	switch (size) {
357 		case 1: WriteReg8(address, value); break;
358 		case 2: WriteReg16(address, value); break;
359 		case 4: *(vuint32*)address = value; break;
360 		default:
361 			return B_ERROR;
362 	}
363 
364 	return B_OK;
365 }
366 
367 
368 status_t
369 DWPCIController::GetMaxBusDevices(int32& count)
370 {
371 	count = 32;
372 	return B_OK;
373 }
374 
375 
376 status_t
377 DWPCIController::ReadIrq(uint8 bus, uint8 device, uint8 function,
378 	uint8 pin, uint8& irq)
379 {
380 	return B_UNSUPPORTED;
381 }
382 
383 
384 status_t
385 DWPCIController::WriteIrq(uint8 bus, uint8 device, uint8 function,
386 	uint8 pin, uint8 irq)
387 {
388 	return B_UNSUPPORTED;
389 }
390 
391 
392 status_t
393 DWPCIController::GetRange(uint32 index, pci_resource_range* range)
394 {
395 	if (index >= kPciRangeEnd)
396 		return B_BAD_INDEX;
397 
398 	*range = fResourceRanges[index];
399 	return B_OK;
400 }
401 
402 
403 //#pragma mark - DWPCIController
404 
405 
406 status_t
407 DWPCIController::AtuMap(uint32 index, uint32 direction, uint32 type, uint64 parentAdr,
408 	uint64 childAdr, uint32 size)
409 {
410 	/*
411 	dprintf("AtuMap(%" B_PRIu32 ", %" B_PRIu32 ", %#" B_PRIx64 ", %#" B_PRIx64 ", "
412 		"%#" B_PRIx32 ")\n", index, type, parentAdr, childAdr, size);
413 	*/
414 	volatile PciAtuRegs* atu = (PciAtuRegs*)(fDbiBase + kPciAtuOffset
415 		+ (2 * index + direction) * sizeof(PciAtuRegs));
416 
417 	atu->baseLo = (uint32)parentAdr;
418 	atu->baseHi = (uint32)(parentAdr >> 32);
419 	atu->limit = (uint32)(parentAdr + size - 1);
420 	atu->targetLo = (uint32)childAdr;
421 	atu->targetHi = (uint32)(childAdr >> 32);
422 	atu->ctrl1 = type;
423 	atu->ctrl2 = kPciAtuEnable;
424 
425 	for (;;) {
426 		if ((atu->ctrl2 & kPciAtuEnable) != 0)
427 			break;
428 	}
429 
430 	return B_OK;
431 }
432 
433 
434 void
435 DWPCIController::AtuDump()
436 {
437 	dprintf("ATU:\n");
438 	for (uint32 direction = 0; direction < 2; direction++) {
439 		switch (direction) {
440 			case kPciAtuOutbound:
441 				dprintf("  outbound:\n");
442 				break;
443 			case kPciAtuInbound:
444 				dprintf("  inbound:\n");
445 				break;
446 		}
447 
448 		for (uint32 index = 0; index < 8; index++) {
449 			volatile PciAtuRegs* atu = (PciAtuRegs*)(fDbiBase
450 				+ kPciAtuOffset + (2 * index + direction) * sizeof(PciAtuRegs));
451 
452 			dprintf("    %" B_PRIu32 ": ", index);
453 			dprintf("base: %#08" B_PRIx64, atu->baseLo + ((uint64)atu->baseHi << 32));
454 			dprintf(", limit: %#08" B_PRIx32, atu->limit);
455 			dprintf(", target: %#08" B_PRIx64, atu->targetLo
456 				+ ((uint64)atu->targetHi << 32));
457 			dprintf(", ctrl1: ");
458 			uint32 ctrl1 = atu->ctrl1;
459 			switch (ctrl1) {
460 				case kPciAtuTypeMem:
461 					dprintf("mem");
462 					break;
463 				case kPciAtuTypeIo:
464 					dprintf("io");
465 					break;
466 				case kPciAtuTypeCfg0:
467 					dprintf("cfg0");
468 					break;
469 				case kPciAtuTypeCfg1:
470 					dprintf("cfg1");
471 					break;
472 				default:
473 					dprintf("? (%#" B_PRIx32 ")", ctrl1);
474 			}
475 			dprintf(", ctrl2: {");
476 			uint32 ctrl2 = atu->ctrl2;
477 			bool first = true;
478 			for (uint32 i = 0; i < 32; i++) {
479 				if (((1 << i) & ctrl2) != 0) {
480 					if (first)
481 						first = false;
482 					else
483 						dprintf(", ");
484 					switch (i) {
485 						case 30:
486 							dprintf("barModeEnable");
487 							break;
488 						case 31:
489 							dprintf("enable");
490 							break;
491 						default:
492 							dprintf("? (%" B_PRIu32 ")", i);
493 							break;
494 					}
495 				}
496 			}
497 			dprintf("}\n");
498 		}
499 	}
500 }
501