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
17static uint32
18ReadReg8(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
30static uint32
31ReadReg16(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
43static void
44WriteReg8(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
57static void
58WriteReg16(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
74float
75DWPCIController::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
99status_t
100DWPCIController::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
113status_t
114DWPCIController::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
126status_t
127DWPCIController::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
245status_t
246DWPCIController::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
289void
290DWPCIController::UninitDriver()
291{
292	delete this;
293}
294
295
296addr_t
297DWPCIController::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
326status_t
327DWPCIController::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
348status_t
349DWPCIController::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
370status_t
371DWPCIController::GetMaxBusDevices(int32& count)
372{
373	count = 32;
374	return B_OK;
375}
376
377
378status_t
379DWPCIController::ReadIrq(uint8 bus, uint8 device, uint8 function,
380	uint8 pin, uint8& irq)
381{
382	return B_UNSUPPORTED;
383}
384
385
386status_t
387DWPCIController::WriteIrq(uint8 bus, uint8 device, uint8 function,
388	uint8 pin, uint8 irq)
389{
390	return B_UNSUPPORTED;
391}
392
393
394status_t
395DWPCIController::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
408status_t
409DWPCIController::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
436void
437DWPCIController::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