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