xref: /haiku/src/add-ons/kernel/busses/pci/ecam/ECAMPCIControllerACPI.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 #include <acpi.h>
9 
10 #include <AutoDeleterDrivers.h>
11 
12 #include "acpi_irq_routing_table.h"
13 
14 #include <string.h>
15 
16 
17 status_t
18 ECAMPCIControllerACPI::ReadResourceInfo()
19 {
20 	DeviceNodePutter<&gDeviceManager> parent(gDeviceManager->get_parent_node(fNode));
21 	return ReadResourceInfo(parent.Get());
22 }
23 
24 
25 status_t
26 ECAMPCIControllerACPI::ReadResourceInfo(device_node* parent)
27 {
28 	dprintf("initialize PCI controller from ACPI\n");
29 
30 	acpi_module_info* acpiModule;
31 	acpi_device_module_info* acpiDeviceModule;
32 	acpi_device acpiDevice;
33 
34 	CHECK_RET(get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule));
35 
36 	acpi_mcfg *mcfg;
37 	CHECK_RET(acpiModule->get_table(ACPI_MCFG_SIGNATURE, 0, (void**)&mcfg));
38 
39 	CHECK_RET(gDeviceManager->get_driver(parent, (driver_module_info**)&acpiDeviceModule,
40 		(void**)&acpiDevice));
41 
42 	acpi_status acpi_res = acpiDeviceModule->walk_resources(acpiDevice, (char *)"_CRS",
43 		AcpiCrsScanCallback, this);
44 
45 	if (acpi_res != 0)
46 		return B_ERROR;
47 
48 	acpi_mcfg_allocation *end = (acpi_mcfg_allocation *) ((char*)mcfg + mcfg->header.length);
49 	acpi_mcfg_allocation *alloc = (acpi_mcfg_allocation *) (mcfg + 1);
50 
51 	if (alloc + 1 != end)
52 		dprintf("PCI: multiple host bridges not supported!");
53 
54 	for (; alloc < end; alloc++) {
55 		dprintf("PCI: mechanism addr: %" B_PRIx64 ", seg: %x, start: %x, end: %x\n",
56 			alloc->address, alloc->pci_segment, alloc->start_bus_number, alloc->end_bus_number);
57 
58 		if (alloc->pci_segment != 0) {
59 			dprintf("PCI: multiple segments not supported!");
60 			continue;
61 		}
62 
63 		fStartBusNumber = alloc->start_bus_number;
64 		fEndBusNumber = alloc->end_bus_number;
65 
66 		fRegsLen = (fEndBusNumber - fStartBusNumber + 1) << 20;
67 		fRegsArea.SetTo(map_physical_memory("PCI Config MMIO",
68 			alloc->address, fRegsLen, B_ANY_KERNEL_ADDRESS,
69 			B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&fRegs));
70 		CHECK_RET(fRegsArea.Get());
71 
72 		return B_OK;
73 	}
74 
75 	return B_ERROR;
76 }
77 
78 
79 acpi_status
80 ECAMPCIControllerACPI::AcpiCrsScanCallback(acpi_resource *res, void *context)
81 {
82 	return static_cast<ECAMPCIControllerACPI*>(context)->AcpiCrsScanCallbackInt(res);
83 }
84 
85 
86 /** Convert an ACPI address resource descriptor into a pci_resource_range.
87  *
88  * This is a template because ACPI resources can be encoded using 8, 16, 32 or 64 bit values.
89  */
90 template<typename T> void
91 DecodeAddress(const T& resource, pci_resource_range& range)
92 {
93 	const auto& acpiRange = resource.address;
94 	dprintf("PCI: range from ACPI [%lx(%d),%lx(%d)] with length %lx\n",
95 		(unsigned long)acpiRange.minimum, resource.minAddress_fixed,
96 		(unsigned long)acpiRange.maximum, resource.maxAddress_fixed,
97 		(unsigned long)acpiRange.address_length);
98 
99 	// If address_length isn't set, compute it from minimum and maximum
100 	// If maximum isn't set, compute it from minimum and length
101 	auto addressLength = acpiRange.address_length;
102 	phys_addr_t addressMaximum = acpiRange.maximum;
103 	if (addressLength == 0)
104 		addressLength = acpiRange.maximum - acpiRange.minimum + 1;
105 	else if (!resource.maxAddress_fixed)
106 		addressMaximum = acpiRange.minimum + addressLength - 1;
107 	ASSERT((phys_addr_t)(acpiRange.minimum + addressLength - 1) == addressMaximum);
108 
109 	range.host_address = acpiRange.minimum + acpiRange.translation_offset;
110 	range.pci_address  = acpiRange.minimum;
111 	range.size = addressLength;
112 }
113 
114 
115 acpi_status
116 ECAMPCIControllerACPI::AcpiCrsScanCallbackInt(acpi_resource *res)
117 {
118 	pci_resource_range range = {};
119 
120 	switch (res->type) {
121 		case ACPI_RESOURCE_TYPE_ADDRESS16: {
122 			const auto& address = res->data.address16;
123 			DecodeAddress(address, range);
124 			break;
125 		}
126 		case ACPI_RESOURCE_TYPE_ADDRESS32: {
127 			const auto& address = res->data.address32;
128 			DecodeAddress(address, range);
129 			range.address_type |= PCI_address_type_32;
130 			break;
131 		}
132 		case ACPI_RESOURCE_TYPE_ADDRESS64: {
133 			const auto& address = res->data.address64;
134 			DecodeAddress(address, range);
135 			range.address_type |= PCI_address_type_64;
136 			break;
137 		}
138 
139 		default:
140 			return B_OK;
141 	}
142 
143 	switch (res->data.address.resource_type) {
144 		case 0: // ACPI_MEMORY_RANGE
145 			range.type = B_IO_MEMORY;
146 			if (res->data.address.info.mem.caching == 3 /*ACPI_PREFETCHABLE_MEMORY*/)
147 				range.address_type |= PCI_address_prefetchable;
148 			break;
149 		case 1: // ACPI_IO_RANGE
150 			range.type = B_IO_PORT;
151 			break;
152 
153 		default:
154 			return B_OK;
155 	}
156 
157 	fResourceRanges.Add(range);
158 	return B_OK;
159 }
160 
161 
162 status_t
163 ECAMPCIControllerACPI::Finalize()
164 {
165 	dprintf("finalize PCI controller from ACPI\n");
166 
167 	acpi_module_info *acpiModule;
168 	CHECK_RET(get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule));
169 
170 	IRQRoutingTable table;
171 	CHECK_RET(prepare_irq_routing(acpiModule, table, [](int32 gsi) {return true;}));
172 
173 	CHECK_RET(enable_irq_routing(acpiModule, table));
174 
175 	print_irq_routing_table(table);
176 
177 	return B_OK;
178 }
179