1*433d6423SLionel Sambuc #include <minix/driver.h>
2*433d6423SLionel Sambuc #include <acpi.h>
3*433d6423SLionel Sambuc #include <assert.h>
4*433d6423SLionel Sambuc #include <minix/acpi.h>
5*433d6423SLionel Sambuc
6*433d6423SLionel Sambuc #include "acpi_globals.h"
7*433d6423SLionel Sambuc
8*433d6423SLionel Sambuc #define PCI_MAX_DEVICES 32
9*433d6423SLionel Sambuc #define PCI_MAX_PINS 4
10*433d6423SLionel Sambuc
11*433d6423SLionel Sambuc #define IRQ_TABLE_ENTRIES (PCI_MAX_DEVICES * PCI_MAX_PINS)
12*433d6423SLionel Sambuc
13*433d6423SLionel Sambuc struct pci_bridge {
14*433d6423SLionel Sambuc ACPI_HANDLE handle;
15*433d6423SLionel Sambuc int irqtable[IRQ_TABLE_ENTRIES];
16*433d6423SLionel Sambuc int primary_bus;
17*433d6423SLionel Sambuc int secondary_bus;
18*433d6423SLionel Sambuc unsigned device;
19*433d6423SLionel Sambuc struct pci_bridge * parent;
20*433d6423SLionel Sambuc struct pci_bridge * children[PCI_MAX_DEVICES];
21*433d6423SLionel Sambuc };
22*433d6423SLionel Sambuc
23*433d6423SLionel Sambuc static struct pci_bridge pci_root_bridge;
24*433d6423SLionel Sambuc
25*433d6423SLionel Sambuc struct irq_resource {
26*433d6423SLionel Sambuc struct pci_bridge * bridge;
27*433d6423SLionel Sambuc ACPI_PCI_ROUTING_TABLE * tbl;
28*433d6423SLionel Sambuc };
29*433d6423SLionel Sambuc
find_bridge(struct pci_bridge * root,int pbnr,int dev,int sbnr)30*433d6423SLionel Sambuc static struct pci_bridge * find_bridge(struct pci_bridge * root,
31*433d6423SLionel Sambuc int pbnr,
32*433d6423SLionel Sambuc int dev,
33*433d6423SLionel Sambuc int sbnr)
34*433d6423SLionel Sambuc {
35*433d6423SLionel Sambuc if (!root)
36*433d6423SLionel Sambuc return NULL;
37*433d6423SLionel Sambuc
38*433d6423SLionel Sambuc if (sbnr == -1) {
39*433d6423SLionel Sambuc if (root->secondary_bus == pbnr)
40*433d6423SLionel Sambuc return root->children[dev];
41*433d6423SLionel Sambuc else {
42*433d6423SLionel Sambuc /* serach all children */
43*433d6423SLionel Sambuc unsigned d;
44*433d6423SLionel Sambuc for (d = 0; d < PCI_MAX_DEVICES; d++) {
45*433d6423SLionel Sambuc struct pci_bridge * b;
46*433d6423SLionel Sambuc b = find_bridge(root->children[d],
47*433d6423SLionel Sambuc pbnr, dev, sbnr);
48*433d6423SLionel Sambuc if (b)
49*433d6423SLionel Sambuc return b;
50*433d6423SLionel Sambuc }
51*433d6423SLionel Sambuc }
52*433d6423SLionel Sambuc } else {
53*433d6423SLionel Sambuc if (root->secondary_bus == sbnr)
54*433d6423SLionel Sambuc return root;
55*433d6423SLionel Sambuc else {
56*433d6423SLionel Sambuc /* check all children */
57*433d6423SLionel Sambuc unsigned d;
58*433d6423SLionel Sambuc for (d = 0; d < PCI_MAX_DEVICES; d++) {
59*433d6423SLionel Sambuc struct pci_bridge * b;
60*433d6423SLionel Sambuc b = find_bridge(root->children[d],
61*433d6423SLionel Sambuc pbnr, dev, sbnr);
62*433d6423SLionel Sambuc if (b)
63*433d6423SLionel Sambuc return b;
64*433d6423SLionel Sambuc }
65*433d6423SLionel Sambuc }
66*433d6423SLionel Sambuc }
67*433d6423SLionel Sambuc
68*433d6423SLionel Sambuc return NULL;
69*433d6423SLionel Sambuc }
70*433d6423SLionel Sambuc
do_map_bridge(message * m)71*433d6423SLionel Sambuc void do_map_bridge(message *m)
72*433d6423SLionel Sambuc {
73*433d6423SLionel Sambuc int err = OK;
74*433d6423SLionel Sambuc unsigned dev = ((struct acpi_map_bridge_req *)m)->device;
75*433d6423SLionel Sambuc unsigned pbnr = ((struct acpi_map_bridge_req *)m)->primary_bus;
76*433d6423SLionel Sambuc unsigned sbnr = ((struct acpi_map_bridge_req *)m)->secondary_bus;
77*433d6423SLionel Sambuc
78*433d6423SLionel Sambuc struct pci_bridge * bridge;
79*433d6423SLionel Sambuc
80*433d6423SLionel Sambuc bridge = find_bridge(&pci_root_bridge, pbnr, dev, -1);
81*433d6423SLionel Sambuc
82*433d6423SLionel Sambuc if (!bridge) {
83*433d6423SLionel Sambuc err = ENODEV;
84*433d6423SLionel Sambuc goto map_error;
85*433d6423SLionel Sambuc }
86*433d6423SLionel Sambuc
87*433d6423SLionel Sambuc bridge->primary_bus = pbnr;
88*433d6423SLionel Sambuc bridge->secondary_bus = sbnr;
89*433d6423SLionel Sambuc
90*433d6423SLionel Sambuc map_error:
91*433d6423SLionel Sambuc ((struct acpi_map_bridge_resp *)m)->err = err;
92*433d6423SLionel Sambuc }
93*433d6423SLionel Sambuc
94*433d6423SLionel Sambuc #if 0
95*433d6423SLionel Sambuc static ACPI_STATUS device_get_int(ACPI_HANDLE handle,
96*433d6423SLionel Sambuc char * name,
97*433d6423SLionel Sambuc ACPI_INTEGER * val)
98*433d6423SLionel Sambuc {
99*433d6423SLionel Sambuc ACPI_STATUS status;
100*433d6423SLionel Sambuc char buff[sizeof(ACPI_OBJECT)];
101*433d6423SLionel Sambuc ACPI_BUFFER abuff;
102*433d6423SLionel Sambuc
103*433d6423SLionel Sambuc abuff.Length = sizeof(buff);
104*433d6423SLionel Sambuc abuff.Pointer = buff;
105*433d6423SLionel Sambuc
106*433d6423SLionel Sambuc status = AcpiEvaluateObjectTyped(handle, name, NULL,
107*433d6423SLionel Sambuc &abuff, ACPI_TYPE_INTEGER);
108*433d6423SLionel Sambuc if (ACPI_SUCCESS(status)) {
109*433d6423SLionel Sambuc *val = ((ACPI_OBJECT *)abuff.Pointer)->Integer.Value;
110*433d6423SLionel Sambuc }
111*433d6423SLionel Sambuc
112*433d6423SLionel Sambuc return status;
113*433d6423SLionel Sambuc }
114*433d6423SLionel Sambuc #endif
115*433d6423SLionel Sambuc
do_get_irq(message * m)116*433d6423SLionel Sambuc void do_get_irq(message *m)
117*433d6423SLionel Sambuc {
118*433d6423SLionel Sambuc struct pci_bridge * bridge;
119*433d6423SLionel Sambuc int irq;
120*433d6423SLionel Sambuc
121*433d6423SLionel Sambuc unsigned bus = ((struct acpi_get_irq_req *)m)->bus;
122*433d6423SLionel Sambuc unsigned dev = ((struct acpi_get_irq_req *)m)->dev;
123*433d6423SLionel Sambuc unsigned pin = ((struct acpi_get_irq_req *)m)->pin;
124*433d6423SLionel Sambuc
125*433d6423SLionel Sambuc assert(dev < PCI_MAX_DEVICES && pin < PCI_MAX_PINS);
126*433d6423SLionel Sambuc
127*433d6423SLionel Sambuc bridge = find_bridge(&pci_root_bridge, -1, -1, bus);
128*433d6423SLionel Sambuc
129*433d6423SLionel Sambuc if (!bridge)
130*433d6423SLionel Sambuc irq = -1;
131*433d6423SLionel Sambuc else
132*433d6423SLionel Sambuc irq = bridge->irqtable[dev * PCI_MAX_PINS + pin];
133*433d6423SLionel Sambuc
134*433d6423SLionel Sambuc ((struct acpi_get_irq_resp *)m)->irq = irq;
135*433d6423SLionel Sambuc }
136*433d6423SLionel Sambuc
add_irq(struct pci_bridge * bridge,unsigned dev,unsigned pin,u8_t irq)137*433d6423SLionel Sambuc static void add_irq(struct pci_bridge * bridge,
138*433d6423SLionel Sambuc unsigned dev,
139*433d6423SLionel Sambuc unsigned pin,
140*433d6423SLionel Sambuc u8_t irq)
141*433d6423SLionel Sambuc {
142*433d6423SLionel Sambuc assert(dev < PCI_MAX_DEVICES && pin < PCI_MAX_PINS);
143*433d6423SLionel Sambuc
144*433d6423SLionel Sambuc bridge->irqtable[dev * PCI_MAX_PINS + pin] = irq;
145*433d6423SLionel Sambuc }
146*433d6423SLionel Sambuc
get_irq_resource(ACPI_RESOURCE * res,void * context)147*433d6423SLionel Sambuc static ACPI_STATUS get_irq_resource(ACPI_RESOURCE *res, void *context)
148*433d6423SLionel Sambuc {
149*433d6423SLionel Sambuc struct irq_resource * ires = (struct irq_resource *) context;
150*433d6423SLionel Sambuc
151*433d6423SLionel Sambuc if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
152*433d6423SLionel Sambuc ACPI_RESOURCE_IRQ *irq;
153*433d6423SLionel Sambuc
154*433d6423SLionel Sambuc irq = &res->Data.Irq;
155*433d6423SLionel Sambuc add_irq(ires->bridge, ires->tbl->Address >> 16, ires->tbl->Pin,
156*433d6423SLionel Sambuc irq->Interrupts[ires->tbl->SourceIndex]);
157*433d6423SLionel Sambuc } else if (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
158*433d6423SLionel Sambuc ACPI_RESOURCE_EXTENDED_IRQ *irq;
159*433d6423SLionel Sambuc
160*433d6423SLionel Sambuc irq = &res->Data.ExtendedIrq;
161*433d6423SLionel Sambuc add_irq(ires->bridge, ires->tbl->Address >> 16, ires->tbl->Pin,
162*433d6423SLionel Sambuc irq->Interrupts[ires->tbl->SourceIndex]);
163*433d6423SLionel Sambuc }
164*433d6423SLionel Sambuc
165*433d6423SLionel Sambuc return AE_OK;
166*433d6423SLionel Sambuc }
167*433d6423SLionel Sambuc
get_pci_irq_routing(struct pci_bridge * bridge)168*433d6423SLionel Sambuc static ACPI_STATUS get_pci_irq_routing(struct pci_bridge * bridge)
169*433d6423SLionel Sambuc {
170*433d6423SLionel Sambuc ACPI_STATUS status;
171*433d6423SLionel Sambuc ACPI_BUFFER abuff;
172*433d6423SLionel Sambuc char buff[4096];
173*433d6423SLionel Sambuc ACPI_PCI_ROUTING_TABLE *tbl;
174*433d6423SLionel Sambuc ACPI_DEVICE_INFO *info;
175*433d6423SLionel Sambuc int i;
176*433d6423SLionel Sambuc
177*433d6423SLionel Sambuc abuff.Length = sizeof(buff);
178*433d6423SLionel Sambuc abuff.Pointer = buff;
179*433d6423SLionel Sambuc
180*433d6423SLionel Sambuc status = AcpiGetIrqRoutingTable(bridge->handle, &abuff);
181*433d6423SLionel Sambuc if (ACPI_FAILURE(status)) {
182*433d6423SLionel Sambuc return AE_OK;
183*433d6423SLionel Sambuc }
184*433d6423SLionel Sambuc
185*433d6423SLionel Sambuc info = abuff.Pointer;
186*433d6423SLionel Sambuc status = AcpiGetObjectInfo(bridge->handle, &info);
187*433d6423SLionel Sambuc if (ACPI_FAILURE(status))
188*433d6423SLionel Sambuc return status;
189*433d6423SLionel Sambuc /*
190*433d6423SLionel Sambuc * Decode the device number (upper half of the address) and attach the
191*433d6423SLionel Sambuc * new bridge in the children list of its parent
192*433d6423SLionel Sambuc */
193*433d6423SLionel Sambuc bridge->device = info->Address >> 16;
194*433d6423SLionel Sambuc if (bridge != &pci_root_bridge) {
195*433d6423SLionel Sambuc bridge->parent->children[bridge->device] = bridge;
196*433d6423SLionel Sambuc bridge->primary_bus = bridge->secondary_bus = -1;
197*433d6423SLionel Sambuc }
198*433d6423SLionel Sambuc
199*433d6423SLionel Sambuc
200*433d6423SLionel Sambuc for (i = 0; i < PCI_MAX_DEVICES; i++)
201*433d6423SLionel Sambuc bridge->children[i] = NULL;
202*433d6423SLionel Sambuc
203*433d6423SLionel Sambuc for (tbl = (ACPI_PCI_ROUTING_TABLE *)abuff.Pointer; tbl->Length;
204*433d6423SLionel Sambuc tbl = (ACPI_PCI_ROUTING_TABLE *)
205*433d6423SLionel Sambuc ((char *)tbl + tbl->Length)) {
206*433d6423SLionel Sambuc ACPI_HANDLE src_handle;
207*433d6423SLionel Sambuc struct irq_resource ires;
208*433d6423SLionel Sambuc
209*433d6423SLionel Sambuc if (*(char*)tbl->Source == '\0') {
210*433d6423SLionel Sambuc add_irq(bridge, tbl->Address >> 16,
211*433d6423SLionel Sambuc tbl->Pin, tbl->SourceIndex);
212*433d6423SLionel Sambuc continue;
213*433d6423SLionel Sambuc }
214*433d6423SLionel Sambuc
215*433d6423SLionel Sambuc status = AcpiGetHandle(bridge->handle, tbl->Source, &src_handle);
216*433d6423SLionel Sambuc if (ACPI_FAILURE(status)) {
217*433d6423SLionel Sambuc printf("Failed AcpiGetHandle\n");
218*433d6423SLionel Sambuc continue;
219*433d6423SLionel Sambuc }
220*433d6423SLionel Sambuc ires.bridge = bridge;
221*433d6423SLionel Sambuc ires.tbl = tbl;
222*433d6423SLionel Sambuc status = AcpiWalkResources(src_handle, METHOD_NAME__CRS,
223*433d6423SLionel Sambuc get_irq_resource, &ires);
224*433d6423SLionel Sambuc if (ACPI_FAILURE(status)) {
225*433d6423SLionel Sambuc printf("Failed IRQ resource\n");
226*433d6423SLionel Sambuc continue;
227*433d6423SLionel Sambuc }
228*433d6423SLionel Sambuc }
229*433d6423SLionel Sambuc
230*433d6423SLionel Sambuc return AE_OK;
231*433d6423SLionel Sambuc }
232*433d6423SLionel Sambuc
bridge_init_irqtable(struct pci_bridge * bridge)233*433d6423SLionel Sambuc static void bridge_init_irqtable(struct pci_bridge * bridge)
234*433d6423SLionel Sambuc {
235*433d6423SLionel Sambuc int i;
236*433d6423SLionel Sambuc
237*433d6423SLionel Sambuc for (i = 0; i < IRQ_TABLE_ENTRIES; i++)
238*433d6423SLionel Sambuc bridge->irqtable[i] = -1;
239*433d6423SLionel Sambuc }
240*433d6423SLionel Sambuc
add_pci_dev(ACPI_HANDLE handle,UINT32 level,void * context,void ** retval)241*433d6423SLionel Sambuc static ACPI_STATUS add_pci_dev(ACPI_HANDLE handle,
242*433d6423SLionel Sambuc UINT32 level,
243*433d6423SLionel Sambuc void *context,
244*433d6423SLionel Sambuc void **retval)
245*433d6423SLionel Sambuc {
246*433d6423SLionel Sambuc ACPI_STATUS status;
247*433d6423SLionel Sambuc ACPI_BUFFER abuff;
248*433d6423SLionel Sambuc char buff[4096];
249*433d6423SLionel Sambuc ACPI_HANDLE parent_handle;
250*433d6423SLionel Sambuc struct pci_bridge * bridge;
251*433d6423SLionel Sambuc struct pci_bridge * parent_bridge = (struct pci_bridge *) context;
252*433d6423SLionel Sambuc
253*433d6423SLionel Sambuc
254*433d6423SLionel Sambuc /* skip pci root when we get to it again */
255*433d6423SLionel Sambuc if (handle == pci_root_bridge.handle)
256*433d6423SLionel Sambuc return AE_OK;
257*433d6423SLionel Sambuc
258*433d6423SLionel Sambuc status = AcpiGetParent(handle, &parent_handle);
259*433d6423SLionel Sambuc if (!ACPI_SUCCESS(status))
260*433d6423SLionel Sambuc return status;
261*433d6423SLionel Sambuc /* skip devices that have a different parent */
262*433d6423SLionel Sambuc if (parent_handle != parent_bridge->handle)
263*433d6423SLionel Sambuc return AE_OK;
264*433d6423SLionel Sambuc
265*433d6423SLionel Sambuc abuff.Length = sizeof(buff);
266*433d6423SLionel Sambuc abuff.Pointer = buff;
267*433d6423SLionel Sambuc
268*433d6423SLionel Sambuc bridge = malloc(sizeof(struct pci_bridge));
269*433d6423SLionel Sambuc if (!bridge)
270*433d6423SLionel Sambuc return AE_NO_MEMORY;
271*433d6423SLionel Sambuc bridge->handle = handle;
272*433d6423SLionel Sambuc bridge->parent = parent_bridge;
273*433d6423SLionel Sambuc bridge_init_irqtable(bridge);
274*433d6423SLionel Sambuc
275*433d6423SLionel Sambuc status = get_pci_irq_routing(bridge);
276*433d6423SLionel Sambuc if (!(ACPI_SUCCESS(status))) {
277*433d6423SLionel Sambuc free(bridge);
278*433d6423SLionel Sambuc return status;
279*433d6423SLionel Sambuc }
280*433d6423SLionel Sambuc
281*433d6423SLionel Sambuc /* get the pci bridges */
282*433d6423SLionel Sambuc status = AcpiGetDevices(NULL, add_pci_dev, bridge, NULL);
283*433d6423SLionel Sambuc return status;
284*433d6423SLionel Sambuc }
285*433d6423SLionel Sambuc
add_pci_root_dev(ACPI_HANDLE handle,UINT32 level,void * context,void ** retval)286*433d6423SLionel Sambuc static ACPI_STATUS add_pci_root_dev(ACPI_HANDLE handle,
287*433d6423SLionel Sambuc UINT32 level,
288*433d6423SLionel Sambuc void *context,
289*433d6423SLionel Sambuc void **retval)
290*433d6423SLionel Sambuc {
291*433d6423SLionel Sambuc static unsigned called;
292*433d6423SLionel Sambuc ACPI_STATUS status;
293*433d6423SLionel Sambuc
294*433d6423SLionel Sambuc if (++called > 1) {
295*433d6423SLionel Sambuc printf("ACPI: Warning! Multi rooted PCI is not supported!\n");
296*433d6423SLionel Sambuc return AE_OK;
297*433d6423SLionel Sambuc }
298*433d6423SLionel Sambuc
299*433d6423SLionel Sambuc pci_root_bridge.handle = handle;
300*433d6423SLionel Sambuc pci_root_bridge.primary_bus = -1; /* undefined */
301*433d6423SLionel Sambuc pci_root_bridge.secondary_bus = 0; /* root bus is 0 in a single root
302*433d6423SLionel Sambuc system */
303*433d6423SLionel Sambuc bridge_init_irqtable(&pci_root_bridge);
304*433d6423SLionel Sambuc
305*433d6423SLionel Sambuc status = get_pci_irq_routing(&pci_root_bridge);
306*433d6423SLionel Sambuc if (!ACPI_SUCCESS(status))
307*433d6423SLionel Sambuc return status;
308*433d6423SLionel Sambuc
309*433d6423SLionel Sambuc /* get the pci bridges */
310*433d6423SLionel Sambuc status = AcpiGetDevices(NULL, add_pci_dev, &pci_root_bridge, NULL);
311*433d6423SLionel Sambuc return status;
312*433d6423SLionel Sambuc }
313*433d6423SLionel Sambuc
pci_scan_devices(void)314*433d6423SLionel Sambuc void pci_scan_devices(void)
315*433d6423SLionel Sambuc {
316*433d6423SLionel Sambuc ACPI_STATUS status;
317*433d6423SLionel Sambuc
318*433d6423SLionel Sambuc /* do not scan devices in PIC mode */
319*433d6423SLionel Sambuc if (!machine.apic_enabled)
320*433d6423SLionel Sambuc return;
321*433d6423SLionel Sambuc
322*433d6423SLionel Sambuc /* get the root first */
323*433d6423SLionel Sambuc status = AcpiGetDevices("PNP0A03", add_pci_root_dev, NULL, NULL);
324*433d6423SLionel Sambuc assert(ACPI_SUCCESS(status));
325*433d6423SLionel Sambuc }
326