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
18static uint32
19ReadReg8(addr_t adr)
20{
21	uint32 ofs = adr % 4;
22	adr = adr / 4 * 4;
23	union {
24		uint32 in;
25		uint8 out[4];
26	} val{.in = *(vuint32*)adr};
27	return val.out[ofs];
28}
29
30
31static uint32
32ReadReg16(addr_t adr)
33{
34	uint32 ofs = adr / 2 % 2;
35	adr = adr / 4 * 4;
36	union {
37		uint32 in;
38		uint16 out[2];
39	} val{.in = *(vuint32*)adr};
40	return val.out[ofs];
41}
42
43
44static void
45WriteReg8(addr_t adr, uint32 value)
46{
47	uint32 ofs = adr % 4;
48	adr = adr / 4 * 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
58static void
59WriteReg16(addr_t adr, uint32 value)
60{
61	uint32 ofs = adr / 2 % 2;
62	adr = adr / 4 * 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
75float
76ECAMPCIController::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
110status_t
111ECAMPCIController::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)
124status_t
125ECAMPCIController::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
154void
155ECAMPCIController::UninitDriver()
156{
157	delete this;
158}
159#endif
160
161
162addr_t
163ECAMPCIController::ConfigAddress(uint8 bus, uint8 device, uint8 function, uint16 offset)
164{
165	PciAddressEcam address {
166		.offset = offset,
167		.function = function,
168		.device = device,
169		.bus = bus
170	};
171	if ((address.val + 4) > fRegsLen)
172		return 0;
173
174	return (addr_t)fRegs + address.val;
175}
176
177
178//#pragma mark - PCI controller
179
180
181status_t
182ECAMPCIController::ReadConfig(uint8 bus, uint8 device, uint8 function,
183	uint16 offset, uint8 size, uint32& value)
184{
185	addr_t address = ConfigAddress(bus, device, function, offset);
186	if (address == 0)
187		return B_ERROR;
188
189	switch (size) {
190		case 1: value = ReadReg8(address); break;
191		case 2: value = ReadReg16(address); break;
192		case 4: value = *(vuint32*)address; break;
193		default:
194			return B_ERROR;
195	}
196
197	return B_OK;
198}
199
200
201status_t
202ECAMPCIController::WriteConfig(uint8 bus, uint8 device, uint8 function,
203	uint16 offset, uint8 size, uint32 value)
204{
205	addr_t address = ConfigAddress(bus, device, function, offset);
206	if (address == 0)
207		return B_ERROR;
208
209	switch (size) {
210		case 1: WriteReg8(address, value); break;
211		case 2: WriteReg16(address, value); break;
212		case 4: *(vuint32*)address = value; break;
213		default:
214			return B_ERROR;
215	}
216
217	return B_OK;
218}
219
220
221status_t
222ECAMPCIController::GetMaxBusDevices(int32& count)
223{
224	count = 32;
225	return B_OK;
226}
227
228
229status_t
230ECAMPCIController::ReadIrq(uint8 bus, uint8 device, uint8 function,
231	uint8 pin, uint8& irq)
232{
233	return B_UNSUPPORTED;
234}
235
236
237status_t
238ECAMPCIController::WriteIrq(uint8 bus, uint8 device, uint8 function,
239	uint8 pin, uint8 irq)
240{
241	return B_UNSUPPORTED;
242}
243
244
245status_t
246ECAMPCIController::GetRange(uint32 index, pci_resource_range* range)
247{
248	if (index >= (uint32)fResourceRanges.Count())
249		return B_BAD_INDEX;
250
251	*range = fResourceRanges[index];
252	return B_OK;
253}
254