xref: /haiku/src/add-ons/kernel/busses/pci/ecam/ECAMPCIController.cpp (revision eea5774f46bba925156498abf9cb1a1165647bf7)
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 #include <new>
16 
17 
18 static uint32
19 ReadReg8(addr_t adr)
20 {
21 	uint32 ofs = adr % 4;
22 	adr = ROUNDDOWN(adr, 4);
23 	union {
24 		uint32 in;
25 		uint8 out[4];
26 	} val{.in = *(vuint32*)adr};
27 	return val.out[ofs];
28 }
29 
30 
31 static uint32
32 ReadReg16(addr_t adr)
33 {
34 	uint32 ofs = adr / 2 % 2;
35 	adr = ROUNDDOWN(adr, 4);
36 	union {
37 		uint32 in;
38 		uint16 out[2];
39 	} val{.in = *(vuint32*)adr};
40 	return val.out[ofs];
41 }
42 
43 
44 static void
45 WriteReg8(addr_t adr, uint32 value)
46 {
47 	uint32 ofs = adr % 4;
48 	adr = ROUNDDOWN(adr, 4);
49 	union {
50 		uint32 in;
51 		uint8 out[4];
52 	} val{.in = *(vuint32*)adr};
53 	val.out[ofs] = (uint8)value;
54 	*(vuint32*)adr = val.in;
55 }
56 
57 
58 static void
59 WriteReg16(addr_t adr, uint32 value)
60 {
61 	uint32 ofs = adr / 2 % 2;
62 	adr = ROUNDDOWN(adr, 4);
63 	union {
64 		uint32 in;
65 		uint16 out[2];
66 	} val{.in = *(vuint32*)adr};
67 	val.out[ofs] = (uint16)value;
68 	*(vuint32*)adr = val.in;
69 }
70 
71 
72 //#pragma mark - driver
73 
74 
75 float
76 ECAMPCIController::SupportsDevice(device_node* parent)
77 {
78 	const char* bus;
79 	status_t status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false);
80 	if (status < B_OK)
81 		return -1.0f;
82 
83 	if (strcmp(bus, "fdt") == 0) {
84 		const char* compatible;
85 		status = gDeviceManager->get_attr_string(parent, "fdt/compatible", &compatible, false);
86 		if (status < B_OK)
87 			return -1.0f;
88 
89 		if (strcmp(compatible, "pci-host-ecam-generic") != 0)
90 			return 0.0f;
91 
92 		return 1.0f;
93 	}
94 
95 	if (strcmp(bus, "acpi") == 0) {
96 		const char* hid;
97 		if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, false) < B_OK)
98 			return -1.0f;
99 
100 		if (strcmp(hid, "PNP0A03") != 0 && strcmp(hid, "PNP0A08") != 0)
101 			return 0.0f;
102 
103 		return 1.0f;
104 	}
105 
106 	return 0.0f;
107 }
108 
109 
110 status_t
111 ECAMPCIController::RegisterDevice(device_node* parent)
112 {
113 	device_attr attrs[] = {
114 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "ECAM PCI Host Controller"} },
115 		{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {.string = "bus_managers/pci/root/driver_v1"} },
116 		{}
117 	};
118 
119 	return gDeviceManager->register_node(parent, ECAM_PCI_DRIVER_MODULE_NAME, attrs, NULL, NULL);
120 }
121 
122 
123 #if !defined(ECAM_PCI_CONTROLLER_NO_INIT)
124 status_t
125 ECAMPCIController::InitDriver(device_node* node, ECAMPCIController*& outDriver)
126 {
127 	dprintf("+ECAMPCIController::InitDriver()\n");
128 	DeviceNodePutter<&gDeviceManager> parentNode(gDeviceManager->get_parent_node(node));
129 
130 	ObjectDeleter<ECAMPCIController> driver;
131 
132 	const char* bus;
133 	CHECK_RET(gDeviceManager->get_attr_string(parentNode.Get(), B_DEVICE_BUS, &bus, false));
134 	if (strcmp(bus, "fdt") == 0)
135 		driver.SetTo(new(std::nothrow) ECAMPCIControllerFDT());
136 	else if (strcmp(bus, "acpi") == 0)
137 		driver.SetTo(new(std::nothrow) ECAMPCIControllerACPI());
138 	else
139 		return B_ERROR;
140 
141 	if (!driver.IsSet())
142 		return B_NO_MEMORY;
143 
144 	driver->fNode = node;
145 
146 	CHECK_RET(driver->ReadResourceInfo());
147 	outDriver = driver.Detach();
148 
149 	dprintf("-ECAMPCIController::InitDriver()\n");
150 	return B_OK;
151 }
152 
153 
154 void
155 ECAMPCIController::UninitDriver()
156 {
157 	delete this;
158 }
159 #endif
160 
161 
162 /** Compute the virtual address for accessing a PCI ECAM register.
163  *
164  * \returns NULL if the address is out of bounds.
165  */
166 addr_t
167 ECAMPCIController::ConfigAddress(uint8 bus, uint8 device, uint8 function, uint16 offset)
168 {
169 	PciAddressEcam address {
170 		.offset = offset,
171 		.function = function,
172 		.device = device,
173 		.bus = bus
174 	};
175 	if ((ROUNDDOWN(address.val, 4) + 4) > fRegsLen)
176 		return 0;
177 
178 	return (addr_t)fRegs + address.val;
179 }
180 
181 
182 //#pragma mark - PCI controller
183 
184 
185 status_t
186 ECAMPCIController::ReadConfig(uint8 bus, uint8 device, uint8 function,
187 	uint16 offset, uint8 size, uint32& value)
188 {
189 	addr_t address = ConfigAddress(bus, device, function, offset);
190 	if (address == 0)
191 		return ERANGE;
192 
193 	switch (size) {
194 		case 1: value = ReadReg8(address); break;
195 		case 2: value = ReadReg16(address); break;
196 		case 4: value = *(vuint32*)address; break;
197 		default:
198 			return B_BAD_VALUE;
199 	}
200 
201 	return B_OK;
202 }
203 
204 
205 status_t
206 ECAMPCIController::WriteConfig(uint8 bus, uint8 device, uint8 function,
207 	uint16 offset, uint8 size, uint32 value)
208 {
209 	addr_t address = ConfigAddress(bus, device, function, offset);
210 	if (address == 0)
211 		return ERANGE;
212 
213 	switch (size) {
214 		case 1: WriteReg8(address, value); break;
215 		case 2: WriteReg16(address, value); break;
216 		case 4: *(vuint32*)address = value; break;
217 		default:
218 			return B_BAD_VALUE;
219 	}
220 
221 	return B_OK;
222 }
223 
224 
225 status_t
226 ECAMPCIController::GetMaxBusDevices(int32& count)
227 {
228 	count = 32;
229 	return B_OK;
230 }
231 
232 
233 status_t
234 ECAMPCIController::ReadIrq(uint8 bus, uint8 device, uint8 function,
235 	uint8 pin, uint8& irq)
236 {
237 	return B_UNSUPPORTED;
238 }
239 
240 
241 status_t
242 ECAMPCIController::WriteIrq(uint8 bus, uint8 device, uint8 function,
243 	uint8 pin, uint8 irq)
244 {
245 	return B_UNSUPPORTED;
246 }
247 
248 
249 status_t
250 ECAMPCIController::GetRange(uint32 index, pci_resource_range* range)
251 {
252 	if (index >= (uint32)fResourceRanges.Count())
253 		return B_BAD_INDEX;
254 
255 	*range = fResourceRanges[index];
256 	return B_OK;
257 }
258