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