1 /*-
2 * Copyright (c) 2000 Michael Smith
3 * Copyright (c) 2000 BSDi
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: releng/5.0/sys/dev/acpica/acpi_pcib.c 106158 2002-10-29 19:08:55Z jhb $
28 */
29 #include "opt_acpi.h"
30 #include <sys/param.h>
31 #include <sys/bus.h>
32 #include <sys/malloc.h>
33 #include <sys/kernel.h>
34
35 #include "acpi.h"
36
37 #include <dev/acpica/acpivar.h>
38 #include <dev/acpica/acpi_pcibvar.h>
39
40 #include <machine/pci_cfgreg.h>
41 #include <pci/pcivar.h>
42 #include <pci/pcib_private.h>
43 #include "pcib_if.h"
44
45 /*
46 * Hooks for the ACPI CA debugging infrastructure
47 */
48 #define _COMPONENT ACPI_BUS
49 ACPI_MODULE_NAME("PCI")
50
51 int
52 acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno)
53 {
54 device_t child;
55 ACPI_STATUS status;
56
57 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
58
59 /*
60 * Don't attach if we're not really there.
61 *
62 * XXX: This isn't entirely correct since we may be a PCI bus
63 * on a hot-plug docking station, etc.
64 */
65 if (!acpi_DeviceIsPresent(dev))
66 return_VALUE(ENXIO);
67
68 /*
69 * Get the PCI interrupt routing table for this bus.
70 */
71 prt->Length = ACPI_ALLOCATE_BUFFER;
72 status = AcpiGetIrqRoutingTable(acpi_get_handle(dev), prt);
73 if (ACPI_FAILURE(status))
74 /* This is not an error, but it may reduce functionality. */
75 device_printf(dev,
76 "could not get PCI interrupt routing table for %s - %s\n",
77 acpi_name(acpi_get_handle(dev)), AcpiFormatException(status));
78
79 /*
80 * Attach the PCI bus proper.
81 */
82 if ((child = device_add_child(dev, "pci", busno)) == NULL) {
83 device_printf(device_get_parent(dev), "couldn't attach pci bus\n");
84 return_VALUE(ENXIO);
85 }
86
87 /*
88 * Now go scan the bus.
89 */
90 acpi_pci_link_config(dev, prt, busno);
91 return_VALUE(bus_generic_attach(dev));
92 }
93
94 int
95 acpi_pcib_resume(device_t dev, ACPI_BUFFER *prt, int busno)
96 {
97 acpi_pci_link_resume(dev, prt, busno);
98 return (bus_generic_resume(dev));
99 }
100
101 /*
102 * Route an interrupt for a child of the bridge.
103 *
104 * XXX clean up error messages
105 *
106 * XXX this function is somewhat bulky
107 */
108 int
109 acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
110 ACPI_BUFFER *prtbuf)
111 {
112 ACPI_PCI_ROUTING_TABLE *prt;
113 ACPI_HANDLE lnkdev;
114 ACPI_BUFFER crsbuf, prsbuf;
115 ACPI_RESOURCE *crsres, *prsres, resbuf;
116 ACPI_DEVICE_INFO devinfo;
117 ACPI_STATUS status;
118 u_int8_t *prtp;
119 int interrupt;
120 int i;
121
122 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
123
124 crsbuf.Pointer = NULL;
125 prsbuf.Pointer = NULL;
126 interrupt = 255;
127
128 /* ACPI numbers pins 0-3, not 1-4 like the BIOS */
129 pin--;
130
131 prtp = prtbuf->Pointer;
132 if (prtp == NULL) /* didn't get routing table */
133 goto out;
134
135 /* scan the table looking for this device */
136 for (;;) {
137 prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
138
139 if (prt->Length == 0) /* end of table */
140 goto out;
141
142 /*
143 * Compare the slot number (high word of Address) and pin number
144 * (note that ACPI uses 0 for INTA) to check for a match.
145 *
146 * Note that the low word of the Address field (function number)
147 * is required by the specification to be 0xffff. We don't risk
148 * checking it here.
149 */
150 if ((((prt->Address & 0xffff0000) >> 16) == pci_get_slot(dev)) &&
151 (prt->Pin == pin)) {
152 if (bootverbose)
153 device_printf(pcib, "matched entry for %d.%d.INT%c (source %s)\n",
154 pci_get_bus(dev), pci_get_slot(dev), 'A' + pin, prt->Source);
155 break;
156 }
157
158 /* skip to next entry */
159 prtp += prt->Length;
160 }
161
162 /*
163 * If source is empty/NULL, the source index is the global IRQ number.
164 */
165 if ((prt->Source == NULL) || (prt->Source[0] == '\0')) {
166 if (bootverbose)
167 device_printf(pcib, "device is hardwired to IRQ %d\n",
168 prt->SourceIndex);
169 interrupt = prt->SourceIndex;
170 goto out;
171 }
172
173 /*
174 * We have to find the source device (PCI interrupt link device)
175 */
176 if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) {
177 device_printf(pcib, "couldn't find PCI interrupt link device %s\n",
178 prt->Source);
179 goto out;
180 }
181
182 /*
183 * Verify that this is a PCI link device, and that it's present.
184 */
185 if (ACPI_FAILURE(AcpiGetObjectInfo(lnkdev, &devinfo))) {
186 device_printf(pcib, "couldn't validate PCI interrupt link device %s\n",
187 prt->Source);
188 goto out;
189 }
190 if (!(devinfo.Valid & ACPI_VALID_HID) || strcmp("PNP0C0F", devinfo.HardwareId)) {
191 device_printf(pcib, "PCI interrupt link device %s has wrong _HID (%s)\n",
192 prt->Source, devinfo.HardwareId);
193 goto out;
194 }
195 if (devinfo.Valid & ACPI_VALID_STA && (devinfo.CurrentStatus & 0x9) != 0x9) {
196 device_printf(pcib, "PCI interrupt link device %s not present\n",
197 prt->Source);
198 goto out;
199 }
200
201 /*
202 * Get the current and possible resources for the interrupt link device.
203 */
204 crsbuf.Length = ACPI_ALLOCATE_BUFFER;
205 if (ACPI_FAILURE(status = AcpiGetCurrentResources(lnkdev, &crsbuf))) {
206 device_printf(pcib, "couldn't get PCI interrupt link device _CRS data - %s\n",
207 AcpiFormatException(status));
208 goto out; /* this is fatal */
209 }
210 prsbuf.Length = ACPI_ALLOCATE_BUFFER;
211 if (ACPI_FAILURE(status = AcpiGetPossibleResources(lnkdev, &prsbuf))) {
212 device_printf(pcib, "couldn't get PCI interrupt link device _PRS data - %s\n",
213 AcpiFormatException(status));
214 /* this is not fatal, since it may be hardwired */
215 }
216 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._CRS\n",
217 (long)crsbuf.Length, acpi_name(lnkdev)));
218 ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._PRS\n",
219 (long)prsbuf.Length, acpi_name(lnkdev)));
220
221 /*
222 * The interrupt may already be routed, so check _CRS first. We don't check the
223 * 'decoding' bit in the _STA result, since there's nothing in the spec that
224 * mandates it be set, however some BIOS' will set it if the decode is active.
225 *
226 * The Source Index points to the particular resource entry we're interested in.
227 */
228 if (ACPI_FAILURE(acpi_FindIndexedResource(&crsbuf, prt->SourceIndex, &crsres))) {
229 device_printf(pcib, "_CRS buffer corrupt, cannot route interrupt\n");
230 goto out;
231 }
232
233 /* type-check the resource we've got */
234 if (crsres->Id != ACPI_RSTYPE_IRQ) { /* XXX ACPI_RSTYPE_EXT_IRQ */
235 device_printf(pcib, "_CRS resource entry has unsupported type %d\n",
236 crsres->Id);
237 goto out;
238 }
239
240 /* if there's more than one interrupt, we are confused */
241 if (crsres->Data.Irq.NumberOfInterrupts > 1) {
242 device_printf(pcib, "device has too many interrupts (%d)\n",
243 crsres->Data.Irq.NumberOfInterrupts);
244 goto out;
245 }
246
247 /*
248 * If there's only one interrupt, and it's not zero, then we're already routed.
249 *
250 * Note that we could also check the 'decoding' bit in _STA, but can't depend on
251 * it since it's not part of the spec.
252 *
253 * XXX check ASL examples to see if this is an acceptable set of tests
254 */
255 if ((crsres->Data.Irq.NumberOfInterrupts == 1) && (crsres->Data.Irq.Interrupts[0] != 0)) {
256 device_printf(pcib, "slot %d INT%c is routed to irq %d\n",
257 pci_get_slot(dev), 'A' + pin, crsres->Data.Irq.Interrupts[0]);
258 interrupt = crsres->Data.Irq.Interrupts[0];
259 goto out;
260 }
261
262 /*
263 * There isn't an interrupt, so we have to look at _PRS to get one.
264 * Get the set of allowed interrupts from the _PRS resource indexed by SourceIndex.
265 */
266 if (prsbuf.Pointer == NULL) {
267 device_printf(pcib, "device has no routed interrupt and no _PRS on PCI interrupt link device\n");
268 goto out;
269 }
270 if (ACPI_FAILURE(acpi_FindIndexedResource(&prsbuf, prt->SourceIndex, &prsres))) {
271 device_printf(pcib, "_PRS buffer corrupt, cannot route interrupt\n");
272 goto out;
273 }
274
275 /* type-check the resource we've got */
276 if (prsres->Id != ACPI_RSTYPE_IRQ) { /* XXX ACPI_RSTYPE_EXT_IRQ */
277 device_printf(pcib, "_PRS resource entry has unsupported type %d\n",
278 prsres->Id);
279 goto out;
280 }
281
282 /* there has to be at least one interrupt available */
283 if (prsres->Data.Irq.NumberOfInterrupts < 1) {
284 device_printf(pcib, "device has no interrupts\n");
285 goto out;
286 }
287
288 /*
289 * Pick an interrupt to use. Note that a more scientific approach than just
290 * taking the first one available would be desirable.
291 *
292 * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI doesn't
293 * seem to offer a similar mechanism, so picking a "good" interrupt here is a
294 * difficult task.
295 *
296 * Build a resource buffer and pass it to AcpiSetCurrentResources to route the
297 * new interrupt.
298 */
299 device_printf(pcib, "possible interrupts:");
300 for (i = 0; i < prsres->Data.Irq.NumberOfInterrupts; i++)
301 printf(" %d", prsres->Data.Irq.Interrupts[i]);
302 printf("\n");
303
304 if (crsbuf.Pointer != NULL) /* should never happen */
305 AcpiOsFree(crsbuf.Pointer);
306 crsbuf.Pointer = NULL;
307 resbuf.Id = ACPI_RSTYPE_IRQ;
308 resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
309 resbuf.Data.Irq = prsres->Data.Irq; /* structure copy other fields */
310 resbuf.Data.Irq.NumberOfInterrupts = 1;
311 resbuf.Data.Irq.Interrupts[0] = prsres->Data.Irq.Interrupts[0]; /* just take first... */
312 if (ACPI_FAILURE(status = acpi_AppendBufferResource(&crsbuf, &resbuf))) {
313 device_printf(pcib, "couldn't route interrupt %d via %s, interrupt resource build failed - %s\n",
314 prsres->Data.Irq.Interrupts[0], acpi_name(lnkdev), AcpiFormatException(status));
315 goto out;
316 }
317 if (ACPI_FAILURE(status = AcpiSetCurrentResources(lnkdev, &crsbuf))) {
318 device_printf(pcib, "couldn't route interrupt %d via %s - %s\n",
319 prsres->Data.Irq.Interrupts[0], acpi_name(lnkdev), AcpiFormatException(status));
320 goto out;
321 }
322
323 /* successful, return the interrupt we just routed */
324 device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n",
325 pci_get_slot(dev), 'A' + pin, prsres->Data.Irq.Interrupts[0],
326 acpi_name(lnkdev));
327 interrupt = prsres->Data.Irq.Interrupts[0];
328
329 out:
330 if (crsbuf.Pointer != NULL)
331 AcpiOsFree(crsbuf.Pointer);
332 if (prsbuf.Pointer != NULL)
333 AcpiOsFree(prsbuf.Pointer);
334
335 /* XXX APIC_IO interrupt mapping? */
336 return_VALUE(interrupt);
337 }
338
Cache object: ef53d2ccaf46bc14b747b32ffca52e63
|