xref: /haiku/src/add-ons/kernel/busses/pci/designware/DWPCIController.cpp (revision dd2a1e350b303b855a50fd64e6cb55618be1ae6a)
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 		pci_resource_range range = {};
205 		range.host_address = parentAdr;
206 		range.pci_address = childAdr;
207 		range.size = len;
208 
209 		if ((type & fdtPciRangePrefechable) != 0)
210 			range.address_type |= PCI_address_prefetchable;
211 
212 		switch (type & fdtPciRangeTypeMask) {
213 		case fdtPciRangeIoPort:
214 			range.type = B_IO_PORT;
215 			fResourceRanges.Add(range);
216 			break;
217 		case fdtPciRangeMmio32Bit:
218 			range.type = B_IO_MEMORY;
219 			range.address_type |= PCI_address_type_32;
220 			fResourceRanges.Add(range);
221 			break;
222 		case fdtPciRangeMmio64Bit:
223 			range.type = B_IO_MEMORY;
224 			range.address_type |= PCI_address_type_64;
225 			fResourceRanges.Add(range);
226 			break;
227 		}
228 
229 		switch (type & fdtPciRangeTypeMask) {
230 		case fdtPciRangeConfig:    dprintf("CONFIG"); break;
231 		case fdtPciRangeIoPort:    dprintf("IOPORT"); break;
232 		case fdtPciRangeMmio32Bit: dprintf("MMIO32"); break;
233 		case fdtPciRangeMmio64Bit: dprintf("MMIO64"); break;
234 		}
235 
236 		dprintf(" (0x%08" B_PRIx32 "): ", type);
237 		dprintf("child: %08" B_PRIx64, childAdr);
238 		dprintf(", parent: %08" B_PRIx64, parentAdr);
239 		dprintf(", len: %" B_PRIx64 "\n", len);
240 	}
241 	return B_OK;
242 }
243 
244 
245 status_t
246 DWPCIController::InitDriverInt(device_node* node)
247 {
248 	fNode = node;
249 	dprintf("+DWPCIController::InitDriver()\n");
250 
251 	CHECK_RET(ReadResourceInfo());
252 
253 	DeviceNodePutter<&gDeviceManager> fdtNode(gDeviceManager->get_parent_node(node));
254 
255 	fdt_device_module_info *fdtModule;
256 	fdt_device* fdtDev;
257 	CHECK_RET(gDeviceManager->get_driver(fdtNode.Get(),
258 		(driver_module_info**)&fdtModule, (void**)&fdtDev));
259 
260 	if (!fdtModule->get_reg(fdtDev, 0, &fDbiPhysBase, &fDbiSize))
261 		return B_ERROR;
262 	dprintf("  DBI: %08" B_PRIx64 ", %08" B_PRIx64 "\n", fDbiPhysBase, fDbiSize);
263 
264 	if (!fdtModule->get_reg(fdtDev, 1, &fConfigPhysBase, &fConfigSize))
265 		return B_ERROR;
266 	dprintf("  config: %08" B_PRIx64 ", %08" B_PRIx64 "\n", fConfigPhysBase, fConfigSize);
267 
268 	uint64 msiIrq;
269 	if (!fdtModule->get_interrupt(fdtDev, 0, NULL, &msiIrq))
270 		return B_ERROR;
271 
272 	fDbiArea.SetTo(map_physical_memory("PCI DBI MMIO", fDbiPhysBase, fDbiSize, B_ANY_KERNEL_ADDRESS,
273 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fDbiBase));
274 	CHECK_RET(fDbiArea.Get());
275 
276 	fConfigArea.SetTo(map_physical_memory("PCI Config MMIO", fConfigPhysBase, fConfigSize,
277 		B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fConfigBase));
278 	CHECK_RET(fConfigArea.Get());
279 
280 	CHECK_RET(fIrqCtrl.Init(GetDbuRegs(), msiIrq));
281 
282 	AtuDump();
283 
284 	dprintf("-DWPCIController::InitDriver()\n");
285 	return B_OK;
286 }
287 
288 
289 void
290 DWPCIController::UninitDriver()
291 {
292 	delete this;
293 }
294 
295 
296 addr_t
297 DWPCIController::ConfigAddress(uint8 bus, uint8 device, uint8 function, uint16 offset)
298 {
299 	uint32 atuType;
300 	if (bus == 0) {
301 		if (device != 0 || function != 0)
302 			return 0;
303 		return fDbiBase + offset;
304 	} else if (bus == 1)
305 		atuType = kPciAtuTypeCfg0;
306 	else
307 		atuType = kPciAtuTypeCfg1;
308 
309 	uint64 address = (uint64)(PciAddress {
310 		.function = function,
311 		.device = device,
312 		.bus = bus
313 	}.val) << 8;
314 
315 	status_t res = AtuMap(1, kPciAtuOutbound, atuType, fConfigPhysBase, address, fConfigSize);
316 	if (res < B_OK)
317 		return 0;
318 
319 	return fConfigBase + offset;
320 }
321 
322 
323 //#pragma mark - PCI controller
324 
325 
326 status_t
327 DWPCIController::ReadConfig(uint8 bus, uint8 device, uint8 function,
328 	uint16 offset, uint8 size, uint32& value)
329 {
330 	InterruptsSpinLocker lock(fLock);
331 
332 	addr_t address = ConfigAddress(bus, device, function, offset);
333 	if (address == 0)
334 		return B_ERROR;
335 
336 	switch (size) {
337 		case 1: value = ReadReg8(address); break;
338 		case 2: value = ReadReg16(address); break;
339 		case 4: value = *(vuint32*)address; break;
340 		default:
341 			return B_ERROR;
342 	}
343 
344 	return B_OK;
345 }
346 
347 
348 status_t
349 DWPCIController::WriteConfig(uint8 bus, uint8 device, uint8 function,
350 	uint16 offset, uint8 size, uint32 value)
351 {
352 	InterruptsSpinLocker lock(fLock);
353 
354 	addr_t address = ConfigAddress(bus, device, function, offset);
355 	if (address == 0)
356 		return B_ERROR;
357 
358 	switch (size) {
359 		case 1: WriteReg8(address, value); break;
360 		case 2: WriteReg16(address, value); break;
361 		case 4: *(vuint32*)address = value; break;
362 		default:
363 			return B_ERROR;
364 	}
365 
366 	return B_OK;
367 }
368 
369 
370 status_t
371 DWPCIController::GetMaxBusDevices(int32& count)
372 {
373 	count = 32;
374 	return B_OK;
375 }
376 
377 
378 status_t
379 DWPCIController::ReadIrq(uint8 bus, uint8 device, uint8 function,
380 	uint8 pin, uint8& irq)
381 {
382 	return B_UNSUPPORTED;
383 }
384 
385 
386 status_t
387 DWPCIController::WriteIrq(uint8 bus, uint8 device, uint8 function,
388 	uint8 pin, uint8 irq)
389 {
390 	return B_UNSUPPORTED;
391 }
392 
393 
394 status_t
395 DWPCIController::GetRange(uint32 index, pci_resource_range* range)
396 {
397 	if (index >= (uint32)fResourceRanges.Count())
398 		return B_BAD_INDEX;
399 
400 	*range = fResourceRanges[index];
401 	return B_OK;
402 }
403 
404 
405 //#pragma mark - DWPCIController
406 
407 
408 status_t
409 DWPCIController::AtuMap(uint32 index, uint32 direction, uint32 type, uint64 parentAdr,
410 	uint64 childAdr, uint32 size)
411 {
412 	/*
413 	dprintf("AtuMap(%" B_PRIu32 ", %" B_PRIu32 ", %#" B_PRIx64 ", %#" B_PRIx64 ", "
414 		"%#" B_PRIx32 ")\n", index, type, parentAdr, childAdr, size);
415 	*/
416 	volatile PciAtuRegs* atu = (PciAtuRegs*)(fDbiBase + kPciAtuOffset
417 		+ (2 * index + direction) * sizeof(PciAtuRegs));
418 
419 	atu->baseLo = (uint32)parentAdr;
420 	atu->baseHi = (uint32)(parentAdr >> 32);
421 	atu->limit = (uint32)(parentAdr + size - 1);
422 	atu->targetLo = (uint32)childAdr;
423 	atu->targetHi = (uint32)(childAdr >> 32);
424 	atu->ctrl1 = type;
425 	atu->ctrl2 = kPciAtuEnable;
426 
427 	for (;;) {
428 		if ((atu->ctrl2 & kPciAtuEnable) != 0)
429 			break;
430 	}
431 
432 	return B_OK;
433 }
434 
435 
436 void
437 DWPCIController::AtuDump()
438 {
439 	dprintf("ATU:\n");
440 	for (uint32 direction = 0; direction < 2; direction++) {
441 		switch (direction) {
442 			case kPciAtuOutbound:
443 				dprintf("  outbound:\n");
444 				break;
445 			case kPciAtuInbound:
446 				dprintf("  inbound:\n");
447 				break;
448 		}
449 
450 		for (uint32 index = 0; index < 8; index++) {
451 			volatile PciAtuRegs* atu = (PciAtuRegs*)(fDbiBase
452 				+ kPciAtuOffset + (2 * index + direction) * sizeof(PciAtuRegs));
453 
454 			dprintf("    %" B_PRIu32 ": ", index);
455 			dprintf("base: %#08" B_PRIx64, atu->baseLo + ((uint64)atu->baseHi << 32));
456 			dprintf(", limit: %#08" B_PRIx32, atu->limit);
457 			dprintf(", target: %#08" B_PRIx64, atu->targetLo
458 				+ ((uint64)atu->targetHi << 32));
459 			dprintf(", ctrl1: ");
460 			uint32 ctrl1 = atu->ctrl1;
461 			switch (ctrl1) {
462 				case kPciAtuTypeMem:
463 					dprintf("mem");
464 					break;
465 				case kPciAtuTypeIo:
466 					dprintf("io");
467 					break;
468 				case kPciAtuTypeCfg0:
469 					dprintf("cfg0");
470 					break;
471 				case kPciAtuTypeCfg1:
472 					dprintf("cfg1");
473 					break;
474 				default:
475 					dprintf("? (%#" B_PRIx32 ")", ctrl1);
476 			}
477 			dprintf(", ctrl2: {");
478 			uint32 ctrl2 = atu->ctrl2;
479 			bool first = true;
480 			for (uint32 i = 0; i < 32; i++) {
481 				if (((1 << i) & ctrl2) != 0) {
482 					if (first)
483 						first = false;
484 					else
485 						dprintf(", ");
486 					switch (i) {
487 						case 30:
488 							dprintf("barModeEnable");
489 							break;
490 						case 31:
491 							dprintf("enable");
492 							break;
493 						default:
494 							dprintf("? (%" B_PRIu32 ")", i);
495 							break;
496 					}
497 				}
498 			}
499 			dprintf("}\n");
500 		}
501 	}
502 }
503