Deleted Added
full compact
1/*-
2 * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_pci_link.c 148449 2005-07-27 15:21:32Z jhb $");
28__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_pci_link.c 150003 2005-09-11 18:39:03Z obrien $");
29
30#include "opt_acpi.h"
31#include <sys/param.h>
32#include <sys/bus.h>
33#include <sys/kernel.h>
34#include <sys/limits.h>
35#include <sys/malloc.h>
36#include <sys/module.h>
37
38#include "acpi.h"
38#include <contrib/dev/acpica/acpi.h>
39#include <dev/acpica/acpivar.h>
40#include <dev/acpica/acpi_pcibvar.h>
41
42#include <machine/pci_cfgreg.h>
43#include <dev/pci/pcireg.h>
44#include <dev/pci/pcivar.h>
45#include "pcib_if.h"
46
47/* Hooks for the ACPI CA debugging infrastructure. */
48#define _COMPONENT ACPI_BUS
49ACPI_MODULE_NAME("PCI_LINK")
50
51ACPI_SERIAL_DECL(pci_link, "ACPI PCI link");
52
53#define NUM_ISA_INTERRUPTS 16
54#define NUM_ACPI_INTERRUPTS 256
55
56/*
57 * An ACPI PCI link device may contain multiple links. Each link has its
58 * own ACPI resource. _PRT entries specify which link is being used via
59 * the Source Index.
60 *
61 * XXX: A note about Source Indices and DPFs: Currently we assume that
62 * the DPF start and end tags are not counted towards the index that
63 * Source Index corresponds to. Also, we assume that when DPFs are in use
64 * they various sets overlap in terms of Indices. Here's an example
65 * resource list indicating these assumptions:
66 *
67 * Resource Index
68 * -------- -----
69 * I/O Port 0
70 * Start DPF -
71 * IRQ 1
72 * MemIO 2
73 * Start DPF -
74 * IRQ 1
75 * MemIO 2
76 * End DPF -
77 * DMA Channel 3
78 *
79 * The XXX is because I'm not sure if this is a valid assumption to make.
80 */
81
82/* States during DPF processing. */
83#define DPF_OUTSIDE 0
84#define DPF_FIRST 1
85#define DPF_IGNORE 2
86
87struct link;
88
89struct acpi_pci_link_softc {
90 int pl_num_links;
91 int pl_crs_bad;
92 struct link *pl_links;
93 device_t pl_dev;
94};
95
96struct link {
97 struct acpi_pci_link_softc *l_sc;
98 uint8_t l_bios_irq;
99 uint8_t l_irq;
100 uint8_t l_initial_irq;
101 int l_res_index;
102 int l_num_irqs;
103 int *l_irqs;
104 int l_references;
105 int l_routed:1;
106 int l_isa_irq:1;
107 ACPI_RESOURCE l_prs_template;
108};
109
110struct link_count_request {
111 int in_dpf;
112 int count;
113};
114
115struct link_res_request {
116 struct acpi_pci_link_softc *sc;
117 int in_dpf;
118 int res_index;
119 int link_index;
120};
121
122MALLOC_DEFINE(M_PCI_LINK, "PCI Link", "ACPI PCI Link structures");
123
124static int pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS];
125static int pci_link_bios_isa_irqs;
126
127static char *pci_link_ids[] = { "PNP0C0F", NULL };
128
129/*
130 * Fetch the short name associated with an ACPI handle and save it in the
131 * passed in buffer.
132 */
133static ACPI_STATUS
134acpi_short_name(ACPI_HANDLE handle, char *buffer, size_t buflen)
135{
136 ACPI_BUFFER buf;
137
138 buf.Length = buflen;
139 buf.Pointer = buffer;
140 return (AcpiGetName(handle, ACPI_SINGLE_NAME, &buf));
141}
142
143static int
144acpi_pci_link_probe(device_t dev)
145{
146 char descr[28], name[12];
147
148 /*
149 * We explicitly do not check _STA since not all systems set it to
150 * sensible values.
151 */
152 if (acpi_disabled("pci_link") ||
153 ACPI_ID_PROBE(device_get_parent(dev), dev, pci_link_ids) == NULL)
154 return (ENXIO);
155
156 if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), name,
157 sizeof(name)))) {
158 snprintf(descr, sizeof(descr), "ACPI PCI Link %s", name);
159 device_set_desc_copy(dev, descr);
160 } else
161 device_set_desc(dev, "ACPI PCI Link");
162 return (0);
163}
164
165static ACPI_STATUS
166acpi_count_irq_resources(ACPI_RESOURCE *res, void *context)
167{
168 struct link_count_request *req;
169
170 req = (struct link_count_request *)context;
171 switch (res->Id) {
172 case ACPI_RSTYPE_START_DPF:
173 switch (req->in_dpf) {
174 case DPF_OUTSIDE:
175 /* We've started the first DPF. */
176 req->in_dpf = DPF_FIRST;
177 break;
178 case DPF_FIRST:
179 /* We've started the second DPF. */
180 req->in_dpf = DPF_IGNORE;
181 break;
182 }
183 break;
184 case ACPI_RSTYPE_END_DPF:
185 /* We are finished with DPF parsing. */
186 KASSERT(req->in_dpf != DPF_OUTSIDE,
187 ("%s: end dpf when not parsing a dpf", __func__));
188 req->in_dpf = DPF_OUTSIDE;
189 break;
190 case ACPI_RSTYPE_IRQ:
191 case ACPI_RSTYPE_EXT_IRQ:
192 /*
193 * Don't count resources if we are in a DPF set that we are
194 * ignoring.
195 */
196 if (req->in_dpf != DPF_IGNORE)
197 req->count++;
198 }
199 return (AE_OK);
200}
201
202static ACPI_STATUS
203link_add_crs(ACPI_RESOURCE *res, void *context)
204{
205 struct link_res_request *req;
206 struct link *link;
207
208 ACPI_SERIAL_ASSERT(pci_link);
209 req = (struct link_res_request *)context;
210 switch (res->Id) {
211 case ACPI_RSTYPE_START_DPF:
212 switch (req->in_dpf) {
213 case DPF_OUTSIDE:
214 /* We've started the first DPF. */
215 req->in_dpf = DPF_FIRST;
216 break;
217 case DPF_FIRST:
218 /* We've started the second DPF. */
219 panic(
220 "%s: Multiple dependent functions within a current resource",
221 __func__);
222 break;
223 }
224 break;
225 case ACPI_RSTYPE_END_DPF:
226 /* We are finished with DPF parsing. */
227 KASSERT(req->in_dpf != DPF_OUTSIDE,
228 ("%s: end dpf when not parsing a dpf", __func__));
229 req->in_dpf = DPF_OUTSIDE;
230 break;
231 case ACPI_RSTYPE_IRQ:
232 case ACPI_RSTYPE_EXT_IRQ:
233 KASSERT(req->link_index < req->sc->pl_num_links,
234 ("%s: array boundary violation", __func__));
235 link = &req->sc->pl_links[req->link_index];
236 link->l_res_index = req->res_index;
237 req->link_index++;
238 req->res_index++;
239
240 /*
241 * Only use the current value if there's one IRQ. Some
242 * systems return multiple IRQs (which is nonsense for _CRS)
243 * when the link hasn't been programmed.
244 */
245 if (res->Id == ACPI_RSTYPE_IRQ) {
246 if (res->Data.Irq.NumberOfInterrupts == 1)
247 link->l_irq = res->Data.Irq.Interrupts[0];
248 } else if (res->Data.ExtendedIrq.NumberOfInterrupts == 1)
249 link->l_irq = res->Data.ExtendedIrq.Interrupts[0];
250
251 /*
252 * An IRQ of zero means that the link isn't routed.
253 */
254 if (link->l_irq == 0)
255 link->l_irq = PCI_INVALID_IRQ;
256 break;
257 default:
258 req->res_index++;
259 }
260 return (AE_OK);
261}
262
263/*
264 * Populate the set of possible IRQs for each device.
265 */
266static ACPI_STATUS
267link_add_prs(ACPI_RESOURCE *res, void *context)
268{
269 struct link_res_request *req;
270 struct link *link;
271 UINT32 *irqs;
272 int i;
273
274 ACPI_SERIAL_ASSERT(pci_link);
275 req = (struct link_res_request *)context;
276 switch (res->Id) {
277 case ACPI_RSTYPE_START_DPF:
278 switch (req->in_dpf) {
279 case DPF_OUTSIDE:
280 /* We've started the first DPF. */
281 req->in_dpf = DPF_FIRST;
282 break;
283 case DPF_FIRST:
284 /* We've started the second DPF. */
285 req->in_dpf = DPF_IGNORE;
286 break;
287 }
288 break;
289 case ACPI_RSTYPE_END_DPF:
290 /* We are finished with DPF parsing. */
291 KASSERT(req->in_dpf != DPF_OUTSIDE,
292 ("%s: end dpf when not parsing a dpf", __func__));
293 req->in_dpf = DPF_OUTSIDE;
294 break;
295 case ACPI_RSTYPE_IRQ:
296 case ACPI_RSTYPE_EXT_IRQ:
297 /*
298 * Don't parse resources if we are in a DPF set that we are
299 * ignoring.
300 */
301 if (req->in_dpf == DPF_IGNORE)
302 break;
303
304 KASSERT(req->link_index < req->sc->pl_num_links,
305 ("%s: array boundary violation", __func__));
306 link = &req->sc->pl_links[req->link_index];
307 if (link->l_res_index == -1) {
308 KASSERT(req->sc->pl_crs_bad,
309 ("res_index should be set"));
310 link->l_res_index = req->res_index;
311 }
312 req->link_index++;
313 req->res_index++;
314
315 /*
316 * Stash a copy of the resource for later use when doing
317 * _SRS.
318 */
319 bcopy(res, &link->l_prs_template, sizeof(ACPI_RESOURCE));
320 if (res->Id == ACPI_RSTYPE_IRQ) {
321 link->l_num_irqs = res->Data.Irq.NumberOfInterrupts;
322 irqs = res->Data.Irq.Interrupts;
323 } else {
324 link->l_num_irqs =
325 res->Data.ExtendedIrq.NumberOfInterrupts;
326 irqs = res->Data.ExtendedIrq.Interrupts;
327 }
328 if (link->l_num_irqs == 0)
329 break;
330
331 /*
332 * Save a list of the valid IRQs. Also, if all of the
333 * valid IRQs are ISA IRQs, then mark this link as
334 * routed via an ISA interrupt.
335 */
336 link->l_isa_irq = TRUE;
337 link->l_irqs = malloc(sizeof(int) * link->l_num_irqs,
338 M_PCI_LINK, M_WAITOK | M_ZERO);
339 for (i = 0; i < link->l_num_irqs; i++) {
340 link->l_irqs[i] = irqs[i];
341 if (irqs[i] >= NUM_ISA_INTERRUPTS)
342 link->l_isa_irq = FALSE;
343 }
344 break;
345 default:
346 if (req->in_dpf == DPF_IGNORE)
347 break;
348 if (req->sc->pl_crs_bad)
349 device_printf(req->sc->pl_dev,
350 "Warning: possible resource %d will be lost during _SRS\n",
351 req->res_index);
352 req->res_index++;
353 }
354 return (AE_OK);
355}
356
357static int
358link_valid_irq(struct link *link, int irq)
359{
360 int i;
361
362 ACPI_SERIAL_ASSERT(pci_link);
363
364 /* Invalid interrupts are never valid. */
365 if (!PCI_INTERRUPT_VALID(irq))
366 return (FALSE);
367
368 /* Any interrupt in the list of possible interrupts is valid. */
369 for (i = 0; i < link->l_num_irqs; i++)
370 if (link->l_irqs[i] == irq)
371 return (TRUE);
372
373 /*
374 * For links routed via an ISA interrupt, if the SCI is routed via
375 * an ISA interrupt, the SCI is always treated as a valid IRQ.
376 */
377 if (link->l_isa_irq && AcpiGbl_FADT->SciInt == irq &&
378 irq < NUM_ISA_INTERRUPTS)
379 return (TRUE);
380
381 /* If the interrupt wasn't found in the list it is not valid. */
382 return (FALSE);
383}
384
385static void
386acpi_pci_link_dump(struct acpi_pci_link_softc *sc)
387{
388 struct link *link;
389 int i, j;
390
391 ACPI_SERIAL_ASSERT(pci_link);
392 printf("Index IRQ Rtd Ref IRQs\n");
393 for (i = 0; i < sc->pl_num_links; i++) {
394 link = &sc->pl_links[i];
395 printf("%5d %3d %c %3d ", i, link->l_irq,
396 link->l_routed ? 'Y' : 'N', link->l_references);
397 if (link->l_num_irqs == 0)
398 printf(" none");
399 else for (j = 0; j < link->l_num_irqs; j++)
400 printf(" %d", link->l_irqs[j]);
401 printf("\n");
402 }
403}
404
405static int
406acpi_pci_link_attach(device_t dev)
407{
408 struct acpi_pci_link_softc *sc;
409 struct link_count_request creq;
410 struct link_res_request rreq;
411 ACPI_STATUS status;
412 int i;
413
414 sc = device_get_softc(dev);
415 sc->pl_dev = dev;
416 ACPI_SERIAL_BEGIN(pci_link);
417
418 /*
419 * Count the number of current resources so we know how big of
420 * a link array to allocate. On some systems, _CRS is broken,
421 * so for those systems try to derive the count from _PRS instead.
422 */
423 creq.in_dpf = DPF_OUTSIDE;
424 creq.count = 0;
425 status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
426 acpi_count_irq_resources, &creq);
427 sc->pl_crs_bad = ACPI_FAILURE(status);
428 if (sc->pl_crs_bad) {
429 creq.in_dpf = DPF_OUTSIDE;
430 creq.count = 0;
431 status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
432 acpi_count_irq_resources, &creq);
433 if (ACPI_FAILURE(status)) {
434 device_printf(dev,
435 "Unable to parse _CRS or _PRS: %s\n",
436 AcpiFormatException(status));
437 ACPI_SERIAL_END(pci_link);
438 return (ENXIO);
439 }
440 }
441 sc->pl_num_links = creq.count;
442 if (creq.count == 0)
443 return (0);
444 sc->pl_links = malloc(sizeof(struct link) * sc->pl_num_links,
445 M_PCI_LINK, M_WAITOK | M_ZERO);
446
447 /* Initialize the child links. */
448 for (i = 0; i < sc->pl_num_links; i++) {
449 sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
450 sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
451 sc->pl_links[i].l_sc = sc;
452 sc->pl_links[i].l_isa_irq = FALSE;
453 sc->pl_links[i].l_res_index = -1;
454 }
455
456 /* Try to read the current settings from _CRS if it is valid. */
457 if (!sc->pl_crs_bad) {
458 rreq.in_dpf = DPF_OUTSIDE;
459 rreq.link_index = 0;
460 rreq.res_index = 0;
461 rreq.sc = sc;
462 status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
463 link_add_crs, &rreq);
464 if (ACPI_FAILURE(status)) {
465 device_printf(dev, "Unable to parse _CRS: %s\n",
466 AcpiFormatException(status));
467 goto fail;
468 }
469 }
470
471 /*
472 * Try to read the possible settings from _PRS. Note that if the
473 * _CRS is toast, we depend on having a working _PRS. However, if
474 * _CRS works, then it is ok for _PRS to be missing.
475 */
476 rreq.in_dpf = DPF_OUTSIDE;
477 rreq.link_index = 0;
478 rreq.res_index = 0;
479 rreq.sc = sc;
480 status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
481 link_add_prs, &rreq);
482 if (ACPI_FAILURE(status) &&
483 (status != AE_NOT_FOUND || sc->pl_crs_bad)) {
484 device_printf(dev, "Unable to parse _PRS: %s\n",
485 AcpiFormatException(status));
486 goto fail;
487 }
488 if (bootverbose) {
489 device_printf(dev, "Links after initial probe:\n");
490 acpi_pci_link_dump(sc);
491 }
492
493 /* Verify initial IRQs if we have _PRS. */
494 if (status != AE_NOT_FOUND)
495 for (i = 0; i < sc->pl_num_links; i++)
496 if (!link_valid_irq(&sc->pl_links[i],
497 sc->pl_links[i].l_irq))
498 sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
499 if (bootverbose) {
500 device_printf(dev, "Links after initial validation:\n");
501 acpi_pci_link_dump(sc);
502 }
503
504 /* Save initial IRQs. */
505 for (i = 0; i < sc->pl_num_links; i++)
506 sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq;
507
508 /*
509 * Try to disable this link. If successful, set the current IRQ to
510 * zero and flags to indicate this link is not routed. If we can't
511 * run _DIS (i.e., the method doesn't exist), assume the initial
512 * IRQ was routed by the BIOS.
513 */
514 if (ACPI_SUCCESS(AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL,
515 NULL)))
516 for (i = 0; i < sc->pl_num_links; i++)
517 sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
518 else
519 for (i = 0; i < sc->pl_num_links; i++)
520 if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq))
521 sc->pl_links[i].l_routed = TRUE;
522 if (bootverbose) {
523 device_printf(dev, "Links after disable:\n");
524 acpi_pci_link_dump(sc);
525 }
526 ACPI_SERIAL_END(pci_link);
527 return (0);
528fail:
529 ACPI_SERIAL_END(pci_link);
530 for (i = 0; i < sc->pl_num_links; i++)
531 if (sc->pl_links[i].l_irqs != NULL)
532 free(sc->pl_links[i].l_irqs, M_PCI_LINK);
533 free(sc->pl_links, M_PCI_LINK);
534 return (ENXIO);
535}
536
537/* XXX: Note that this is identical to pci_pir_search_irq(). */
538static uint8_t
539acpi_pci_link_search_irq(int bus, int device, int pin)
540{
541 uint32_t value;
542 uint8_t func, maxfunc;
543
544 /* See if we have a valid device at function 0. */
545 value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1);
546 if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
547 return (PCI_INVALID_IRQ);
548 if (value & PCIM_MFDEV)
549 maxfunc = PCI_FUNCMAX;
550 else
551 maxfunc = 0;
552
553 /* Scan all possible functions at this device. */
554 for (func = 0; func <= maxfunc; func++) {
555 value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4);
556 if (value == 0xffffffff)
557 continue;
558 value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1);
559
560 /*
561 * See if it uses the pin in question. Note that the passed
562 * in pin uses 0 for A, .. 3 for D whereas the intpin
563 * register uses 0 for no interrupt, 1 for A, .. 4 for D.
564 */
565 if (value != pin + 1)
566 continue;
567 value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1);
568 if (bootverbose)
569 printf(
570 "ACPI: Found matching pin for %d.%d.INT%c at func %d: %d\n",
571 bus, device, pin + 'A', func, value);
572 if (value != PCI_INVALID_IRQ)
573 return (value);
574 }
575 return (PCI_INVALID_IRQ);
576}
577
578/*
579 * Find the link structure that corresponds to the resource index passed in
580 * via 'source_index'.
581 */
582static struct link *
583acpi_pci_link_lookup(device_t dev, int source_index)
584{
585 struct acpi_pci_link_softc *sc;
586 int i;
587
588 ACPI_SERIAL_ASSERT(pci_link);
589 sc = device_get_softc(dev);
590 for (i = 0; i < sc->pl_num_links; i++)
591 if (sc->pl_links[i].l_res_index == source_index)
592 return (&sc->pl_links[i]);
593 return (NULL);
594}
595
596void
597acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot,
598 int pin)
599{
600 struct link *link;
601 uint8_t bios_irq;
602
603 /* Bump the reference count. */
604 ACPI_SERIAL_BEGIN(pci_link);
605 link = acpi_pci_link_lookup(dev, index);
606 if (link == NULL)
607 panic("%s: apparently invalid index %d", __func__, index);
608 link->l_references++;
609 if (link->l_routed)
610 pci_link_interrupt_weights[link->l_irq]++;
611
612 /* Try to find a BIOS IRQ setting from any matching devices. */
613 bios_irq = acpi_pci_link_search_irq(pcib_get_bus(pcib), slot, pin);
614 if (!PCI_INTERRUPT_VALID(bios_irq)) {
615 ACPI_SERIAL_END(pci_link);
616 return;
617 }
618
619 /* Validate the BIOS IRQ. */
620 if (!link_valid_irq(link, bios_irq)) {
621 device_printf(dev, "BIOS IRQ %u for %d.%d.INT%c is invalid\n",
622 bios_irq, pcib_get_bus(pcib), slot, pin + 'A');
623 } else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) {
624 link->l_bios_irq = bios_irq;
625 if (bios_irq < NUM_ISA_INTERRUPTS)
626 pci_link_bios_isa_irqs |= (1 << bios_irq);
627 if (bios_irq != link->l_initial_irq &&
628 PCI_INTERRUPT_VALID(link->l_initial_irq))
629 device_printf(dev,
630 "BIOS IRQ %u does not match initial IRQ %u\n",
631 bios_irq, link->l_initial_irq);
632 } else if (bios_irq != link->l_bios_irq)
633 device_printf(dev,
634 "BIOS IRQ %u for %d.%d.INT%c does not match previous BIOS IRQ %u\n",
635 bios_irq, pcib_get_bus(pcib), slot, pin + 'A',
636 link->l_bios_irq);
637 ACPI_SERIAL_END(pci_link);
638}
639
640static ACPI_STATUS
641acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
642{
643 ACPI_RESOURCE *resource, *end, newres, *resptr;
644 ACPI_BUFFER crsbuf;
645 ACPI_STATUS status;
646 struct link *link;
647 int i, in_dpf;
648
649 /* Fetch the _CRS. */
650 ACPI_SERIAL_ASSERT(pci_link);
651 crsbuf.Pointer = NULL;
652 crsbuf.Length = ACPI_ALLOCATE_BUFFER;
653 status = AcpiGetCurrentResources(acpi_get_handle(sc->pl_dev), &crsbuf);
654 if (ACPI_SUCCESS(status) && crsbuf.Pointer == NULL)
655 status = AE_NO_MEMORY;
656 if (ACPI_FAILURE(status)) {
657 if (bootverbose)
658 device_printf(sc->pl_dev,
659 "Unable to fetch current resources: %s\n",
660 AcpiFormatException(status));
661 return (status);
662 }
663
664 /* Fill in IRQ resources via link structures. */
665 srsbuf->Pointer = NULL;
666 link = sc->pl_links;
667 i = 0;
668 in_dpf = DPF_OUTSIDE;
669 resource = (ACPI_RESOURCE *)crsbuf.Pointer;
670 end = (ACPI_RESOURCE *)((char *)crsbuf.Pointer + crsbuf.Length);
671 for (;;) {
672 switch (resource->Id) {
673 case ACPI_RSTYPE_START_DPF:
674 switch (in_dpf) {
675 case DPF_OUTSIDE:
676 /* We've started the first DPF. */
677 in_dpf = DPF_FIRST;
678 break;
679 case DPF_FIRST:
680 /* We've started the second DPF. */
681 panic(
682 "%s: Multiple dependent functions within a current resource",
683 __func__);
684 break;
685 }
686 resptr = NULL;
687 break;
688 case ACPI_RSTYPE_END_DPF:
689 /* We are finished with DPF parsing. */
690 KASSERT(in_dpf != DPF_OUTSIDE,
691 ("%s: end dpf when not parsing a dpf", __func__));
692 in_dpf = DPF_OUTSIDE;
693 resptr = NULL;
694 break;
695 case ACPI_RSTYPE_IRQ:
696 MPASS(i < sc->pl_num_links);
697 MPASS(link->l_prs_template.Id == ACPI_RSTYPE_IRQ);
698 newres = link->l_prs_template;
699 resptr = &newres;
700 resptr->Data.Irq.NumberOfInterrupts = 1;
701 if (PCI_INTERRUPT_VALID(link->l_irq)) {
702 KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
703 ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
704 __func__, link->l_irq));
705 resptr->Data.Irq.Interrupts[0] = link->l_irq;
706 } else
707 resptr->Data.Irq.Interrupts[0] = 0;
708 link++;
709 i++;
710 break;
711 case ACPI_RSTYPE_EXT_IRQ:
712 MPASS(i < sc->pl_num_links);
713 MPASS(link->l_prs_template.Id == ACPI_RSTYPE_EXT_IRQ);
714 newres = link->l_prs_template;
715 resptr = &newres;
716 resptr->Data.ExtendedIrq.NumberOfInterrupts = 1;
717 if (PCI_INTERRUPT_VALID(link->l_irq))
718 resptr->Data.ExtendedIrq.Interrupts[0] =
719 link->l_irq;
720 else
721 resptr->Data.ExtendedIrq.Interrupts[0] = 0;
722 link++;
723 i++;
724 break;
725 default:
726 resptr = resource;
727 }
728 if (resptr != NULL) {
729 status = acpi_AppendBufferResource(srsbuf, resptr);
730 if (ACPI_FAILURE(status)) {
731 device_printf(sc->pl_dev,
732 "Unable to build resources: %s\n",
733 AcpiFormatException(status));
734 if (srsbuf->Pointer != NULL)
735 AcpiOsFree(srsbuf->Pointer);
736 AcpiOsFree(crsbuf.Pointer);
737 return (status);
738 }
739 }
740 if (resource->Id == ACPI_RSTYPE_END_TAG)
741 break;
742 resource = ACPI_NEXT_RESOURCE(resource);
743 if (resource >= end)
744 break;
745 }
746 AcpiOsFree(crsbuf.Pointer);
747 return (AE_OK);
748}
749
750static ACPI_STATUS
751acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc,
752 ACPI_BUFFER *srsbuf)
753{
754 ACPI_RESOURCE newres;
755 ACPI_STATUS status;
756 struct link *link;
757 int i;
758
759 /* Start off with an empty buffer. */
760 srsbuf->Pointer = NULL;
761 link = sc->pl_links;
762 for (i = 0; i < sc->pl_num_links; i++) {
763
764 /* Add a new IRQ resource from each link. */
765 link = &sc->pl_links[i];
766 newres = link->l_prs_template;
767 if (newres.Id == ACPI_RSTYPE_IRQ) {
768
769 /* Build an IRQ resource. */
770 newres.Data.Irq.NumberOfInterrupts = 1;
771 if (PCI_INTERRUPT_VALID(link->l_irq)) {
772 KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
773 ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
774 __func__, link->l_irq));
775 newres.Data.Irq.Interrupts[0] = link->l_irq;
776 } else
777 newres.Data.Irq.Interrupts[0] = 0;
778 } else {
779
780 /* Build an ExtIRQ resuorce. */
781 newres.Data.ExtendedIrq.NumberOfInterrupts = 1;
782 if (PCI_INTERRUPT_VALID(link->l_irq))
783 newres.Data.ExtendedIrq.Interrupts[0] =
784 link->l_irq;
785 else
786 newres.Data.ExtendedIrq.Interrupts[0] = 0;
787 }
788
789 /* Add the new resource to the end of the _SRS buffer. */
790 status = acpi_AppendBufferResource(srsbuf, &newres);
791 if (ACPI_FAILURE(status)) {
792 device_printf(sc->pl_dev,
793 "Unable to build resources: %s\n",
794 AcpiFormatException(status));
795 if (srsbuf->Pointer != NULL)
796 AcpiOsFree(srsbuf->Pointer);
797 return (status);
798 }
799 }
800 return (AE_OK);
801}
802
803static ACPI_STATUS
804acpi_pci_link_route_irqs(device_t dev)
805{
806 struct acpi_pci_link_softc *sc;
807 ACPI_RESOURCE *resource, *end;
808 ACPI_BUFFER srsbuf;
809 ACPI_STATUS status;
810 struct link *link;
811 int i;
812
813 ACPI_SERIAL_ASSERT(pci_link);
814 sc = device_get_softc(dev);
815 if (sc->pl_crs_bad)
816 status = acpi_pci_link_srs_from_links(sc, &srsbuf);
817 else
818 status = acpi_pci_link_srs_from_crs(sc, &srsbuf);
819
820 /* Write out new resources via _SRS. */
821 status = AcpiSetCurrentResources(acpi_get_handle(dev), &srsbuf);
822 if (ACPI_FAILURE(status)) {
823 device_printf(dev, "Unable to route IRQs: %s\n",
824 AcpiFormatException(status));
825 AcpiOsFree(srsbuf.Pointer);
826 return (status);
827 }
828
829 /*
830 * Perform acpi_config_intr() on each IRQ resource if it was just
831 * routed for the first time.
832 */
833 link = sc->pl_links;
834 i = 0;
835 resource = (ACPI_RESOURCE *)srsbuf.Pointer;
836 end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length);
837 for (;;) {
838 if (resource->Id == ACPI_RSTYPE_END_TAG)
839 break;
840 switch (resource->Id) {
841 case ACPI_RSTYPE_IRQ:
842 case ACPI_RSTYPE_EXT_IRQ:
843 MPASS(i < sc->pl_num_links);
844
845 /*
846 * Only configure the interrupt and update the
847 * weights if this link has a valid IRQ and was
848 * previously unrouted.
849 */
850 if (!link->l_routed &&
851 PCI_INTERRUPT_VALID(link->l_irq)) {
852 link->l_routed = TRUE;
853 acpi_config_intr(dev, resource);
854 pci_link_interrupt_weights[link->l_irq] +=
855 link->l_references;
856 }
857 link++;
858 i++;
859 break;
860 }
861 resource = ACPI_NEXT_RESOURCE(resource);
862 if (resource >= end)
863 break;
864 }
865 AcpiOsFree(srsbuf.Pointer);
866 return (AE_OK);
867}
868
869static int
870acpi_pci_link_resume(device_t dev)
871{
872 ACPI_STATUS status;
873
874 ACPI_SERIAL_BEGIN(pci_link);
875 status = acpi_pci_link_route_irqs(dev);
876 ACPI_SERIAL_END(pci_link);
877 if (ACPI_FAILURE(status))
878 return (ENXIO);
879 else
880 return (0);
881}
882
883/*
884 * Pick an IRQ to use for this unrouted link.
885 */
886static uint8_t
887acpi_pci_link_choose_irq(device_t dev, struct link *link)
888{
889 char tunable_buffer[64], link_name[5];
890 u_int8_t best_irq, pos_irq;
891 int best_weight, pos_weight, i;
892
893 KASSERT(!link->l_routed, ("%s: link already routed", __func__));
894 KASSERT(!PCI_INTERRUPT_VALID(link->l_irq),
895 ("%s: link already has an IRQ", __func__));
896
897 /* Check for a tunable override and use it if it is valid. */
898 if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), link_name,
899 sizeof(link_name)))) {
900 snprintf(tunable_buffer, sizeof(tunable_buffer),
901 "hw.pci.link.%s.%d.irq", link_name, link->l_res_index);
902 if (getenv_int(tunable_buffer, &i) &&
903 PCI_INTERRUPT_VALID(i) && link_valid_irq(link, i))
904 return (i);
905 snprintf(tunable_buffer, sizeof(tunable_buffer),
906 "hw.pci.link.%s.irq", link_name);
907 if (getenv_int(tunable_buffer, &i) &&
908 PCI_INTERRUPT_VALID(i) && link_valid_irq(link, i))
909 return (i);
910 }
911
912 /*
913 * If we have a valid BIOS IRQ, use that. We trust what the BIOS
914 * says it routed over what _CRS says the link thinks is routed.
915 */
916 if (PCI_INTERRUPT_VALID(link->l_bios_irq))
917 return (link->l_bios_irq);
918
919 /*
920 * If we don't have a BIOS IRQ but do have a valid IRQ from _CRS,
921 * then use that.
922 */
923 if (PCI_INTERRUPT_VALID(link->l_initial_irq))
924 return (link->l_initial_irq);
925
926 /*
927 * Ok, we have no useful hints, so we have to pick from the
928 * possible IRQs. For ISA IRQs we only use interrupts that
929 * have already been used by the BIOS.
930 */
931 best_irq = PCI_INVALID_IRQ;
932 best_weight = INT_MAX;
933 for (i = 0; i < link->l_num_irqs; i++) {
934 pos_irq = link->l_irqs[i];
935 if (pos_irq < NUM_ISA_INTERRUPTS &&
936 (pci_link_bios_isa_irqs & 1 << pos_irq) == 0)
937 continue;
938 pos_weight = pci_link_interrupt_weights[pos_irq];
939 if (pos_weight < best_weight) {
940 best_weight = pos_weight;
941 best_irq = pos_irq;
942 }
943 }
944
945 /*
946 * If this is an ISA IRQ, try using the SCI if it is also an ISA
947 * interrupt as a fallback.
948 */
949 if (link->l_isa_irq) {
950 pos_irq = AcpiGbl_FADT->SciInt;
951 pos_weight = pci_link_interrupt_weights[pos_irq];
952 if (pos_weight < best_weight) {
953 best_weight = pos_weight;
954 best_irq = pos_irq;
955 }
956 }
957
958 if (PCI_INTERRUPT_VALID(best_irq)) {
959 if (bootverbose)
960 device_printf(dev, "Picked IRQ %u with weight %d\n",
961 best_irq, best_weight);
962 } else
963 device_printf(dev, "Unable to choose an IRQ\n");
964 return (best_irq);
965}
966
967int
968acpi_pci_link_route_interrupt(device_t dev, int index)
969{
970 struct link *link;
971
972 ACPI_SERIAL_BEGIN(pci_link);
973 link = acpi_pci_link_lookup(dev, index);
974 if (link == NULL)
975 panic("%s: apparently invalid index %d", __func__, index);
976
977 /*
978 * If this link device is already routed to an interrupt, just return
979 * the interrupt it is routed to.
980 */
981 if (link->l_routed) {
982 KASSERT(PCI_INTERRUPT_VALID(link->l_irq),
983 ("%s: link is routed but has an invalid IRQ", __func__));
984 ACPI_SERIAL_END(pci_link);
985 return (link->l_irq);
986 }
987
988 /* Choose an IRQ if we need one. */
989 if (!PCI_INTERRUPT_VALID(link->l_irq)) {
990 link->l_irq = acpi_pci_link_choose_irq(dev, link);
991
992 /*
993 * Try to route the interrupt we picked. If it fails, then
994 * assume the interrupt is not routed.
995 */
996 if (PCI_INTERRUPT_VALID(link->l_irq)) {
997 acpi_pci_link_route_irqs(dev);
998 if (!link->l_routed)
999 link->l_irq = PCI_INVALID_IRQ;
1000 }
1001 }
1002 ACPI_SERIAL_END(pci_link);
1003
1004 return (link->l_irq);
1005}
1006
1007/*
1008 * This is gross, but we abuse the identify routine to perform one-time
1009 * SYSINIT() style initialization for the driver.
1010 */
1011static void
1012acpi_pci_link_identify(driver_t *driver, device_t parent)
1013{
1014
1015 /*
1016 * If the SCI is an ISA IRQ, add it to the bitmask of known good
1017 * ISA IRQs.
1018 *
1019 * XXX: If we are using the APIC, the SCI might have been
1020 * rerouted to an APIC pin in which case this is invalid. However,
1021 * if we are using the APIC, we also shouldn't be having any PCI
1022 * interrupts routed via ISA IRQs, so this is probably ok.
1023 */
1024 if (AcpiGbl_FADT->SciInt < NUM_ISA_INTERRUPTS)
1025 pci_link_bios_isa_irqs |= (1 << AcpiGbl_FADT->SciInt);
1026}
1027
1028static device_method_t acpi_pci_link_methods[] = {
1029 /* Device interface */
1030 DEVMETHOD(device_identify, acpi_pci_link_identify),
1031 DEVMETHOD(device_probe, acpi_pci_link_probe),
1032 DEVMETHOD(device_attach, acpi_pci_link_attach),
1033 DEVMETHOD(device_resume, acpi_pci_link_resume),
1034
1035 {0, 0}
1036};
1037
1038static driver_t acpi_pci_link_driver = {
1039 "pci_link",
1040 acpi_pci_link_methods,
1041 sizeof(struct acpi_pci_link_softc),
1042};
1043
1044static devclass_t pci_link_devclass;
1045
1046DRIVER_MODULE(acpi_pci_link, acpi, acpi_pci_link_driver, pci_link_devclass, 0,
1047 0);
1048MODULE_DEPEND(acpi_pci_link, acpi, 1, 1, 1);