xref: /haiku/src/add-ons/kernel/busses/pci/ecam/ECAMPCIControllerFDT.cpp (revision 1978089f7cec856677e46204e992c7273d70b9af)
1 /*
2  * Copyright 2022, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ECAMPCIController.h"
8 
9 #include <AutoDeleterDrivers.h>
10 
11 #include <string.h>
12 
13 
14 status_t
15 ECAMPCIControllerFDT::ReadResourceInfo()
16 {
17 	DeviceNodePutter<&gDeviceManager> fdtNode(gDeviceManager->get_parent_node(fNode));
18 
19 	fdt_device_module_info *fdtModule;
20 	fdt_device* fdtDev;
21 	CHECK_RET(gDeviceManager->get_driver(fdtNode.Get(),
22 		(driver_module_info**)&fdtModule, (void**)&fdtDev));
23 
24 	const void* prop;
25 	int propLen;
26 
27 	prop = fdtModule->get_prop(fdtDev, "bus-range", &propLen);
28 	if (prop != NULL && propLen == 8) {
29 		uint32 busBeg = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 0));
30 		uint32 busEnd = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 1));
31 		dprintf("  bus-range: %" B_PRIu32 " - %" B_PRIu32 "\n", busBeg, busEnd);
32 	}
33 
34 	prop = fdtModule->get_prop(fdtDev, "ranges", &propLen);
35 	if (prop == NULL) {
36 		dprintf("  \"ranges\" property not found");
37 		return B_ERROR;
38 	}
39 	dprintf("  ranges:\n");
40 	for (uint32_t *it = (uint32_t*)prop; (uint8_t*)it - (uint8_t*)prop < propLen; it += 7) {
41 		dprintf("    ");
42 		uint32_t type      = B_BENDIAN_TO_HOST_INT32(*(it + 0));
43 		uint64_t childAdr  = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 1));
44 		uint64_t parentAdr = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 3));
45 		uint64_t len       = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 5));
46 
47 		pci_resource_range range = {};
48 		range.host_address = parentAdr;
49 		range.pci_address = childAdr;
50 		range.size = len;
51 
52 		if ((type & fdtPciRangePrefechable) != 0)
53 			range.address_type |= PCI_address_prefetchable;
54 
55 		switch (type & fdtPciRangeTypeMask) {
56 		case fdtPciRangeIoPort:
57 			range.type = B_IO_PORT;
58 			fResourceRanges.Add(range);
59 			break;
60 		case fdtPciRangeMmio32Bit:
61 			range.type = B_IO_MEMORY;
62 			range.address_type |= PCI_address_type_32;
63 			fResourceRanges.Add(range);
64 			break;
65 		case fdtPciRangeMmio64Bit:
66 			range.type = B_IO_MEMORY;
67 			range.address_type |= PCI_address_type_64;
68 			fResourceRanges.Add(range);
69 			break;
70 		}
71 
72 		switch (type & fdtPciRangeTypeMask) {
73 		case fdtPciRangeConfig:    dprintf("CONFIG"); break;
74 		case fdtPciRangeIoPort:    dprintf("IOPORT"); break;
75 		case fdtPciRangeMmio32Bit: dprintf("MMIO32"); break;
76 		case fdtPciRangeMmio64Bit: dprintf("MMIO64"); break;
77 		}
78 
79 		dprintf(" (0x%08" B_PRIx32 "): ", type);
80 		dprintf("child: %08" B_PRIx64, childAdr);
81 		dprintf(", parent: %08" B_PRIx64, parentAdr);
82 		dprintf(", len: %" B_PRIx64 "\n", len);
83 	}
84 
85 	uint64 regs = 0;
86 	if (!fdtModule->get_reg(fdtDev, 0, &regs, &fRegsLen))
87 		return B_ERROR;
88 
89 	fRegsArea.SetTo(map_physical_memory("PCI Config MMIO", regs, fRegsLen, B_ANY_KERNEL_ADDRESS,
90 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fRegs));
91 	CHECK_RET(fRegsArea.Get());
92 
93 	return B_OK;
94 }
95 
96 
97 status_t
98 ECAMPCIControllerFDT::Finalize()
99 {
100 	dprintf("finalize PCI controller from FDT\n");
101 
102 	DeviceNodePutter<&gDeviceManager> parent(gDeviceManager->get_parent_node(fNode));
103 
104 	fdt_device_module_info* parentModule;
105 	fdt_device* parentDev;
106 
107 	CHECK_RET(gDeviceManager->get_driver(parent.Get(), (driver_module_info**)&parentModule,
108 		(void**)&parentDev));
109 
110 	struct fdt_interrupt_map* interruptMap = parentModule->get_interrupt_map(parentDev);
111 	parentModule->print_interrupt_map(interruptMap);
112 
113 	for (int bus = 0; bus < 8; bus++) {
114 		// TODO: Proper multiple domain handling. (domain, bus) pair should be converted to virtual
115 		// bus before calling PCI module interface.
116 		for (int device = 0; device < 32; device++) {
117 			uint32 vendorID = gPCI->read_pci_config(bus, device, 0, PCI_vendor_id, 2);
118 			if ((vendorID != 0xffffffff) && (vendorID != 0xffff)) {
119 				uint32 headerType = gPCI->read_pci_config(bus, device, 0, PCI_header_type, 1);
120 				if ((headerType & 0x80) != 0) {
121 					for (int function = 0; function < 8; function++) {
122 						FinalizeInterrupts(parentModule, interruptMap, bus, device, function);
123 					}
124 				} else {
125 					FinalizeInterrupts(parentModule, interruptMap, bus, device, 0);
126 				}
127 			}
128 		}
129 	}
130 
131 	return B_OK;
132 }
133 
134 
135 void
136 ECAMPCIControllerFDT::FinalizeInterrupts(fdt_device_module_info* fdtModule,
137 	struct fdt_interrupt_map* interruptMap, int bus, int device, int function)
138 {
139 	uint32 childAddr = ((bus & 0xff) << 16) | ((device & 0x1f) << 11) | ((function & 0x07) << 8);
140 	uint32 interruptPin = gPCI->read_pci_config(bus, device, function, PCI_interrupt_pin, 1);
141 
142 	if (interruptPin == 0xffffffff) {
143 		dprintf("Error: Unable to read interrupt pin!\n");
144 		return;
145 	}
146 
147 	uint32 irq = fdtModule->lookup_interrupt_map(interruptMap, childAddr, interruptPin);
148 	if (irq == 0xffffffff) {
149 		dprintf("no interrupt mapping for childAddr: (%d:%d:%d), childIrq: %d)\n",
150 			bus, device, function, interruptPin);
151 	} else {
152 		dprintf("configure interrupt (%d,%d,%d) --> %d\n",
153 			bus, device, function, irq);
154 		gPCI->update_interrupt_line(bus, device, function, irq);
155 	}
156 }
157