Merge commit 'crater/master'
[dragonfly.git] / sys / dev / acpica5 / acpi_pci_link.c
blob42e0ca800fd817851eac932b177e4010d914cb02
1 /*-
2 * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
3 * All rights reserved.
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.
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.
26 * $FreeBSD: src/sys/dev/acpica/acpi_pci_link.c,v 1.16 2004/06/14 18:54:14 jhb Exp $
27 * $DragonFly: src/sys/dev/acpica5/acpi_pci_link.c,v 1.7 2006/12/22 23:26:14 swildner Exp $
30 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/bus.h>
35 #include "acpi.h"
36 #include "accommon.h"
37 #include <dev/acpica5/acpivar.h>
38 #include <dev/acpica5/acpi_pcibvar.h>
40 /* Hooks for the ACPI CA debugging infrastructure. */
41 #define _COMPONENT ACPI_BUS
42 ACPI_MODULE_NAME("PCI_LINK")
44 #define MAX_POSSIBLE_INTERRUPTS 16
45 #define MAX_ISA_INTERRUPTS 16
46 #define MAX_ACPI_INTERRUPTS 255
48 struct acpi_pci_link_entry {
49 TAILQ_ENTRY(acpi_pci_link_entry) links;
50 ACPI_HANDLE handle;
51 UINT8 current_irq;
52 UINT8 initial_irq;
53 ACPI_RESOURCE possible_resources;
54 UINT8 number_of_interrupts;
55 UINT8 interrupts[MAX_POSSIBLE_INTERRUPTS];
56 UINT8 sorted_irq[MAX_POSSIBLE_INTERRUPTS];
57 int references;
58 int priority;
61 TAILQ_HEAD(acpi_pci_link_entries, acpi_pci_link_entry);
62 static struct acpi_pci_link_entries acpi_pci_link_entries;
64 struct acpi_prt_entry {
65 TAILQ_ENTRY(acpi_prt_entry) links;
66 device_t pcidev;
67 int busno;
68 ACPI_PCI_ROUTING_TABLE prt;
69 struct acpi_pci_link_entry *pci_link;
72 TAILQ_HEAD(acpi_prt_entries, acpi_prt_entry);
73 static struct acpi_prt_entries acpi_prt_entries;
75 static int irq_penalty[MAX_ACPI_INTERRUPTS];
77 #define ACPI_STA_PRESENT 0x00000001
78 #define ACPI_STA_ENABLE 0x00000002
79 #define ACPI_STA_SHOWINUI 0x00000004
80 #define ACPI_STA_FUNCTIONAL 0x00000008
83 * PCI link object management
86 static void
87 acpi_pci_link_dump_polarity(UINT32 Polarity)
90 switch (Polarity) {
91 case ACPI_ACTIVE_HIGH:
92 kprintf("high,");
93 break;
94 case ACPI_ACTIVE_LOW:
95 kprintf("low,");
96 break;
97 default:
98 kprintf("unknown,");
99 break;
103 static void
104 acpi_pci_link_dump_trigger(UINT32 Triggering)
107 switch (Triggering) {
108 case ACPI_EDGE_SENSITIVE:
109 kprintf("edge,");
110 break;
111 case ACPI_LEVEL_SENSITIVE:
112 kprintf("level,");
113 break;
114 default:
115 kprintf("unknown,");
116 break;
120 static void
121 acpi_pci_link_dump_sharemode(UINT32 SharedExclusive)
124 switch (SharedExclusive) {
125 case ACPI_EXCLUSIVE:
126 kprintf("exclusive");
127 break;
128 case ACPI_SHARED:
129 kprintf("sharable");
130 break;
131 default:
132 kprintf("unknown");
133 break;
137 static void
138 acpi_pci_link_entry_dump(struct acpi_prt_entry *entry)
140 UINT8 i;
141 ACPI_RESOURCE_IRQ *Irq;
142 ACPI_RESOURCE_EXTENDED_IRQ *ExtIrq;
144 if (entry == NULL || entry->pci_link == NULL)
145 return;
147 kprintf("%s irq %3d: ", acpi_name(entry->pci_link->handle),
148 entry->pci_link->current_irq);
150 kprintf("[");
151 for (i = 0; i < entry->pci_link->number_of_interrupts; i++)
152 kprintf("%3d", entry->pci_link->interrupts[i]);
153 kprintf("] ");
155 switch (entry->pci_link->possible_resources.Type) {
156 case ACPI_RESOURCE_TYPE_IRQ:
157 Irq = &entry->pci_link->possible_resources.Data.Irq;
158 acpi_pci_link_dump_polarity(Irq->Polarity);
159 acpi_pci_link_dump_trigger(Irq->Triggering);
160 acpi_pci_link_dump_sharemode(Irq->Sharable);
161 break;
162 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
163 ExtIrq = &entry->pci_link->possible_resources.Data.ExtendedIrq;
164 acpi_pci_link_dump_polarity(ExtIrq->Polarity);
165 acpi_pci_link_dump_trigger(ExtIrq->Triggering);
166 acpi_pci_link_dump_sharemode(ExtIrq->Sharable);
167 break;
170 kprintf(" %d.%d.%d\n", entry->busno,
171 (int)((entry->prt.Address & 0xffff0000) >> 16),
172 (int)entry->prt.Pin);
175 static ACPI_STATUS
176 acpi_pci_link_get_object_status(ACPI_HANDLE handle, UINT32 *sta)
178 ACPI_DEVICE_INFO *devinfo;
179 ACPI_BUFFER buf;
180 ACPI_STATUS error;
182 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
184 if (handle == NULL || sta == NULL) {
185 kprintf("invalid argument\n");
186 return_ACPI_STATUS (AE_BAD_PARAMETER);
189 buf.Pointer = NULL;
190 buf.Length = ACPI_ALLOCATE_BUFFER;
191 error = AcpiGetObjectInfo(handle, &buf);
192 if (ACPI_FAILURE(error)) {
193 kprintf("couldn't get object info %s - %s\n",
194 acpi_name(handle), AcpiFormatException(error));
195 return_ACPI_STATUS (error);
198 devinfo = (ACPI_DEVICE_INFO *)buf.Pointer;
199 if ((devinfo->Valid & ACPI_VALID_HID) == 0 ||
200 strcmp(devinfo->HardwareId.Value, "PNP0C0F") != 0) {
201 kprintf("invalid hardware ID - %s\n", acpi_name(handle));
202 AcpiOsFree(buf.Pointer);
203 return_ACPI_STATUS (AE_TYPE);
206 if ((devinfo->Valid & ACPI_VALID_STA) != 0) {
207 *sta = devinfo->CurrentStatus;
208 } else {
209 kprintf("invalid status - %s\n", acpi_name(handle));
210 *sta = 0;
213 AcpiOsFree(buf.Pointer);
214 return_ACPI_STATUS (AE_OK);
217 static ACPI_STATUS
218 acpi_pci_link_get_irq_resources(ACPI_RESOURCE *resources,
219 UINT8 *number_of_interrupts, UINT8 interrupts[])
221 UINT8 count;
222 UINT8 i;
223 UINT32 InterruptCount;
224 UINT32 *Interrupts32;
225 UINT8 *Interrupts8;
227 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
229 if (resources == NULL || number_of_interrupts == NULL) {
230 kprintf("invalid argument\n");
231 return_ACPI_STATUS (AE_BAD_PARAMETER);
234 *number_of_interrupts = 0;
235 InterruptCount = 0;
236 Interrupts8 = NULL;
237 Interrupts32 = NULL;
239 if (resources->Type == ACPI_RESOURCE_TYPE_START_DEPENDENT)
240 resources = ACPI_NEXT_RESOURCE(resources);
242 if (resources->Type != ACPI_RESOURCE_TYPE_IRQ &&
243 resources->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) {
244 kprintf("Resource is not an IRQ entry - %d\n", resources->Type);
245 return_ACPI_STATUS (AE_TYPE);
248 switch (resources->Type) {
249 case ACPI_RESOURCE_TYPE_IRQ:
250 InterruptCount = resources->Data.Irq.InterruptCount;
251 Interrupts8 = resources->Data.Irq.Interrupts;
252 break;
253 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
254 InterruptCount =
255 resources->Data.ExtendedIrq.InterruptCount;
256 Interrupts32 = resources->Data.ExtendedIrq.Interrupts;
257 break;
260 if (InterruptCount == 0) {
261 kprintf("Blank IRQ resource\n");
262 return_ACPI_STATUS (AE_NULL_ENTRY);
265 count = 0;
266 for (i = 0; i < InterruptCount; i++) {
267 UINT32 intr;
269 if (i >= MAX_POSSIBLE_INTERRUPTS) {
270 kprintf("too many IRQs (%d)\n", i);
271 break;
274 KKASSERT(Interrupts8 != NULL || Interrupts32 != NULL);
275 if (Interrupts8 != NULL)
276 intr = Interrupts8[i];
277 else
278 intr = Interrupts32[i];
280 if (intr == 0) {
281 kprintf("invalid IRQ %d\n", intr);
282 continue;
284 interrupts[count] = intr;
285 count++;
287 *number_of_interrupts = count;
289 return_ACPI_STATUS (AE_OK);
292 static ACPI_STATUS
293 acpi_pci_link_get_current_irq(struct acpi_pci_link_entry *link, UINT8 *irq)
295 ACPI_STATUS error;
296 ACPI_BUFFER buf;
297 ACPI_RESOURCE *resources;
298 UINT8 number_of_interrupts;
299 UINT8 interrupts[MAX_POSSIBLE_INTERRUPTS];
301 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
303 if (link == NULL || irq == NULL) {
304 kprintf("invalid argument\n");
305 return_ACPI_STATUS (AE_BAD_PARAMETER);
308 *irq = 0;
309 buf.Pointer = NULL;
310 buf.Length = ACPI_ALLOCATE_BUFFER;
311 error = AcpiGetCurrentResources(link->handle, &buf);
312 if (ACPI_FAILURE(error)) {
313 kprintf("couldn't get PCI interrupt link device _CRS %s - %s\n",
314 acpi_name(link->handle), AcpiFormatException(error));
315 return_ACPI_STATUS (error);
317 if (buf.Pointer == NULL) {
318 kprintf("couldn't allocate memory - %s\n",
319 acpi_name(link->handle));
320 return_ACPI_STATUS (AE_NO_MEMORY);
323 resources = (ACPI_RESOURCE *) buf.Pointer;
324 number_of_interrupts = 0;
325 bzero(interrupts, sizeof(interrupts));
326 error = acpi_pci_link_get_irq_resources(resources,
327 &number_of_interrupts, interrupts);
328 AcpiOsFree(buf.Pointer);
330 if (ACPI_FAILURE(error)) {
331 kprintf("couldn't get current IRQ from "
332 "interrupt link %s - %s\n",
333 acpi_name(link->handle), AcpiFormatException(error));
334 return_ACPI_STATUS (error);
337 if (number_of_interrupts == 0) {
338 kprintf("PCI interrupt link device _CRS data is corrupted - "
339 "%s\n", acpi_name(link->handle));
340 return_ACPI_STATUS (AE_NULL_ENTRY);
343 *irq = interrupts[0];
345 return_ACPI_STATUS (AE_OK);
348 static ACPI_STATUS
349 acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry)
351 ACPI_STATUS error;
352 ACPI_BUFFER buf;
353 ACPI_RESOURCE *resources;
354 struct acpi_pci_link_entry *link;
356 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
358 entry->pci_link = NULL;
359 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
360 if (link->handle == handle) {
361 entry->pci_link = link;
362 link->references++;
363 return_ACPI_STATUS (AE_OK);
367 link = AcpiOsAllocate(sizeof(struct acpi_pci_link_entry));
368 if (link == NULL) {
369 kprintf("couldn't allocate memory - %s\n", acpi_name(handle));
370 return_ACPI_STATUS (AE_NO_MEMORY);
373 buf.Pointer = NULL;
374 buf.Length = ACPI_ALLOCATE_BUFFER;
376 bzero(link, sizeof(struct acpi_pci_link_entry));
378 link->handle = handle;
380 error = acpi_pci_link_get_current_irq(link, &link->current_irq);
381 if (ACPI_FAILURE(error)) {
382 kprintf("couldn't get current IRQ from "
383 "interrupt link %s - %s\n",
384 acpi_name(handle), AcpiFormatException(error));
387 link->initial_irq = link->current_irq;
389 error = AcpiGetPossibleResources(handle, &buf);
390 if (ACPI_FAILURE(error)) {
391 kprintf("couldn't get interrupt link device _PRS "
392 "data %s - %s\n",
393 acpi_name(handle), AcpiFormatException(error));
394 goto out;
396 if (buf.Pointer == NULL) {
397 kprintf("_PRS nuffer is empty - %s\n", acpi_name(handle));
398 error = AE_NO_MEMORY;
399 goto out;
402 resources = (ACPI_RESOURCE *) buf.Pointer;
403 bcopy(resources, &link->possible_resources,
404 sizeof(link->possible_resources));
406 error = acpi_pci_link_get_irq_resources(resources,
407 &link->number_of_interrupts, link->interrupts);
408 if (ACPI_FAILURE(error)) {
409 kprintf("couldn't get possible IRQs from "
410 "interrupt link %s - %s\n",
411 acpi_name(handle), AcpiFormatException(error));
412 goto out;
415 if (link->number_of_interrupts == 0) {
416 kprintf("interrupt link device _PRS data is corrupted - %s\n",
417 acpi_name(handle));
418 error = AE_NULL_ENTRY;
419 goto out;
422 link->references++;
424 TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
425 entry->pci_link = link;
427 error = AE_OK;
428 out:
429 if (buf.Pointer != NULL)
430 AcpiOsFree(buf.Pointer);
431 if (error != AE_OK && link != NULL)
432 AcpiOsFree(link);
434 return_ACPI_STATUS (error);
437 static ACPI_STATUS
438 acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno)
440 ACPI_HANDLE handle;
441 ACPI_STATUS error;
442 UINT32 sta;
443 struct acpi_prt_entry *entry;
445 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
447 if (prt == NULL || prt->Source == NULL || prt->Source[0] == '\0') {
448 kprintf("couldn't handle this routing table - hardwired\n");
449 return_ACPI_STATUS (AE_BAD_PARAMETER);
452 error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source, &handle);
453 if (ACPI_FAILURE(error)) {
454 kprintf("couldn't get handle - %s\n",
455 AcpiFormatException(error));
456 return_ACPI_STATUS (error);
459 error = acpi_pci_link_get_object_status(handle, &sta);
460 if (ACPI_FAILURE(error)) {
461 kprintf("couldn't get object status %s - %s\n",
462 acpi_name(handle), AcpiFormatException(error));
463 return_ACPI_STATUS (error);
466 if ((sta & (ACPI_STA_PRESENT | ACPI_STA_FUNCTIONAL)) == 0) {
467 kprintf("interrupt link is not functional - %s\n",
468 acpi_name(handle));
469 return_ACPI_STATUS (AE_ERROR);
472 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
473 if (entry->busno == busno &&
474 entry->prt.Address == prt->Address &&
475 entry->prt.Pin == prt->Pin) {
476 kprintf("interrupt link entry already exists - %s\n",
477 acpi_name(handle));
478 return_ACPI_STATUS (AE_ALREADY_EXISTS);
482 entry = AcpiOsAllocate(sizeof(struct acpi_prt_entry));
483 if (entry == NULL) {
484 kprintf("couldn't allocate memory - %s\n", acpi_name(handle));
485 return_ACPI_STATUS (AE_NO_MEMORY);
487 bzero(entry, sizeof(struct acpi_prt_entry));
489 entry->pcidev = pcidev;
490 entry->busno = busno;
491 bcopy(prt, &entry->prt, sizeof(entry->prt));
493 error = acpi_pci_link_add_link(handle, entry);
494 if (ACPI_FAILURE(error)) {
495 kprintf("couldn't add _PRT entry to link %s - %s\n",
496 acpi_name(handle), AcpiFormatException(error));
497 goto out;
500 TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links);
501 error = AE_OK;
503 out:
504 if (error != AE_OK && entry != NULL)
505 AcpiOsFree(entry);
507 return_ACPI_STATUS (error);
510 static int
511 acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
513 UINT8 i;
515 if (irq == 0)
516 return (0);
518 for (i = 0; i < link->number_of_interrupts; i++) {
519 if (link->interrupts[i] == irq)
520 return (1);
523 /* allow initial IRQ as valid one. */
524 if (link->initial_irq == irq)
525 return (1);
527 return (0);
530 static ACPI_STATUS
531 acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
533 ACPI_STATUS error;
534 ACPI_RESOURCE resbuf;
535 ACPI_BUFFER crsbuf;
536 UINT32 sta;
538 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
540 if (!acpi_pci_link_is_valid_irq(link, irq)) {
541 kprintf("couldn't set invalid IRQ %d - %s\n", irq,
542 acpi_name(link->handle));
543 return_ACPI_STATUS (AE_BAD_PARAMETER);
546 error = acpi_pci_link_get_current_irq(link, &link->current_irq);
547 if (ACPI_FAILURE(error)) {
548 kprintf("couldn't get current IRQ from "
549 "interrupt link %s - %s\n",
550 acpi_name(link->handle), AcpiFormatException(error));
553 if (link->current_irq == irq)
554 return_ACPI_STATUS (AE_OK);
556 bzero(&resbuf, sizeof(resbuf));
557 crsbuf.Pointer = NULL;
559 switch (link->possible_resources.Type) {
560 case ACPI_RESOURCE_TYPE_IRQ:
561 resbuf.Type = ACPI_RESOURCE_TYPE_IRQ;
562 resbuf.Length = sizeof(ACPI_RESOURCE_IRQ);
564 /* structure copy other fields */
565 resbuf.Data.Irq = link->possible_resources.Data.Irq;
566 resbuf.Data.Irq.InterruptCount = 1;
567 resbuf.Data.Irq.Interrupts[0] = irq;
568 break;
569 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
570 resbuf.Type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ;
571 resbuf.Length = sizeof(ACPI_RESOURCE_EXTENDED_IRQ);
573 /* structure copy other fields */
574 resbuf.Data.ExtendedIrq =
575 link->possible_resources.Data.ExtendedIrq;
576 resbuf.Data.ExtendedIrq.InterruptCount = 1;
577 resbuf.Data.ExtendedIrq.Interrupts[0] = irq;
578 break;
579 default:
580 kprintf("Resource is not an IRQ entry %s - %d\n",
581 acpi_name(link->handle), link->possible_resources.Type);
582 return_ACPI_STATUS (AE_TYPE);
585 error = acpi_AppendBufferResource(&crsbuf, &resbuf);
586 if (ACPI_FAILURE(error)) {
587 kprintf("couldn't setup buffer by "
588 "acpi_AppendBufferResource - %s\n",
589 acpi_name(link->handle));
590 return_ACPI_STATUS (error);
592 if (crsbuf.Pointer == NULL) {
593 kprintf("appended buffer for %s is corrupted\n",
594 acpi_name(link->handle));
595 return_ACPI_STATUS (AE_NO_MEMORY);
598 error = AcpiSetCurrentResources(link->handle, &crsbuf);
599 if (ACPI_FAILURE(error)) {
600 kprintf("couldn't set link device _SRS %s - %s\n",
601 acpi_name(link->handle), AcpiFormatException(error));
602 return_ACPI_STATUS (error);
605 AcpiOsFree(crsbuf.Pointer);
606 link->current_irq = 0;
608 error = acpi_pci_link_get_object_status(link->handle, &sta);
609 if (ACPI_FAILURE(error)) {
610 kprintf("couldn't get object status %s - %s\n",
611 acpi_name(link->handle), AcpiFormatException(error));
612 return_ACPI_STATUS (error);
615 if ((sta & ACPI_STA_ENABLE) == 0) {
616 kprintf("interrupt link %s is disabled\n",
617 acpi_name(link->handle));
618 return_ACPI_STATUS (AE_ERROR);
621 error = acpi_pci_link_get_current_irq(link, &link->current_irq);
622 if (ACPI_FAILURE(error)) {
623 kprintf("couldn't get current IRQ from "
624 "interrupt link %s - %s\n",
625 acpi_name(link->handle), AcpiFormatException(error));
626 return_ACPI_STATUS (error);
629 if (link->current_irq == irq) {
630 error = AE_OK;
631 } else {
632 kprintf("couldn't set IRQ %d to PCI interrupt link %d - %s\n",
633 irq, link->current_irq, acpi_name(link->handle));
634 link->current_irq = 0;
635 error = AE_ERROR;
638 return_ACPI_STATUS (error);
642 * Auto arbitration for boot-disabled devices
645 static void
646 acpi_pci_link_bootdisabled_dump(void)
648 int i;
649 int irq;
650 struct acpi_pci_link_entry *link;
652 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
653 /* boot-disabled link only. */
654 if (link->current_irq != 0)
655 continue;
657 kprintf("%s:\n", acpi_name(link->handle));
658 kprintf(" interrupts: ");
659 for (i = 0; i < link->number_of_interrupts; i++) {
660 irq = link->sorted_irq[i];
661 kprintf("%6d", irq);
663 kprintf("\n");
664 kprintf(" penalty: ");
665 for (i = 0; i < link->number_of_interrupts; i++) {
666 irq = link->sorted_irq[i];
667 kprintf("%6d", irq_penalty[irq]);
669 kprintf("\n");
670 kprintf(" references: %d\n", link->references);
671 kprintf(" priority: %d\n", link->priority);
675 static void
676 acpi_pci_link_init_irq_penalty(void)
678 int irq;
680 bzero(irq_penalty, sizeof(irq_penalty));
681 for (irq = 0; irq < MAX_ISA_INTERRUPTS; irq++) {
682 /* 0, 1, 2, 8: timer, keyboard, cascade */
683 if (irq == 0 || irq == 1 || irq == 2 || irq == 8) {
684 irq_penalty[irq] = 100000;
685 continue;
688 /* 13, 14, 15: npx, ATA controllers */
689 if (irq == 13 || irq == 14 || irq == 15) {
690 irq_penalty[irq] = 10000;
691 continue;
694 /* 3,4,6,7,12: typicially used by legacy hardware */
695 if (irq == 3 || irq == 4 || irq == 6 || irq == 7 || irq == 12) {
696 irq_penalty[irq] = 1000;
697 continue;
702 static int
703 link_exclusive(ACPI_RESOURCE *res)
705 if (res == NULL ||
706 (res->Type != ACPI_RESOURCE_TYPE_IRQ &&
707 res->Type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ))
708 return (0);
710 if ((res->Type == ACPI_RESOURCE_TYPE_IRQ &&
711 res->Data.Irq.Sharable == ACPI_EXCLUSIVE) ||
712 (res->Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ &&
713 res->Data.ExtendedIrq.Sharable == ACPI_EXCLUSIVE))
714 return (1);
716 return (0);
719 static void
720 acpi_pci_link_update_irq_penalty(device_t dev, int busno)
722 int i;
723 int irq;
724 int rid;
725 struct resource *res;
726 struct acpi_prt_entry *entry;
727 struct acpi_pci_link_entry *link;
729 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
730 if (entry->busno != busno)
731 continue;
733 /* Impossible? */
734 link = entry->pci_link;
735 if (link == NULL)
736 continue;
738 if (link->current_irq != 0) {
739 /* not boot-disabled link, we will use this IRQ. */
740 irq_penalty[link->current_irq] += 100;
741 continue;
744 /* boot-disabled link */
745 for (i = 0; i < link->number_of_interrupts; i++) {
746 /* give 10 for each possible IRQs. */
747 irq = link->interrupts[i];
748 irq_penalty[irq] += 10;
750 /* higher penalty if exclusive. */
751 if (link_exclusive(&link->possible_resources))
752 irq_penalty[irq] += 100;
754 /* XXX try to get this IRQ in non-sharable mode. */
755 rid = 0;
756 res = bus_alloc_resource(dev, SYS_RES_IRQ,
757 &rid, irq, irq, 1, 0);
758 if (res != NULL) {
759 bus_release_resource(dev, SYS_RES_IRQ,
760 rid, res);
761 } else {
762 /* this is in use, give 100. */
763 irq_penalty[irq] += 100;
767 /* initialize `sorted' possible IRQs. */
768 bcopy(link->interrupts, link->sorted_irq,
769 sizeof(link->sorted_irq));
773 static void
774 acpi_pci_link_set_bootdisabled_priority(void)
776 int sum_penalty;
777 int i;
778 int irq;
779 struct acpi_pci_link_entry *link, *link_pri;
780 TAILQ_HEAD(, acpi_pci_link_entry) sorted_list;
782 if (bootverbose) {
783 kprintf("ACPI PCI link before setting link priority:\n");
784 acpi_pci_link_bootdisabled_dump();
787 /* reset priority for all links. */
788 TAILQ_FOREACH(link, &acpi_pci_link_entries, links)
789 link->priority = 0;
791 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
792 /* not boot-disabled link, give no chance to be arbitrated. */
793 if (link->current_irq != 0) {
794 link->priority = 0;
795 continue;
799 * Calculate the priority for each boot-disabled links.
800 * o IRQ penalty indicates difficulty to use.
801 * o #references for devices indicates importance of the link.
802 * o #interrupts indicates flexibility of the link.
804 sum_penalty = 0;
805 for (i = 0; i < link->number_of_interrupts; i++) {
806 irq = link->interrupts[i];
807 sum_penalty += irq_penalty[irq];
810 link->priority = (sum_penalty * link->references) /
811 link->number_of_interrupts;
815 * Sort PCI links based on the priority.
816 * XXX Any other better ways rather than using work list?
818 TAILQ_INIT(&sorted_list);
819 while (!TAILQ_EMPTY(&acpi_pci_link_entries)) {
820 link = TAILQ_FIRST(&acpi_pci_link_entries);
821 /* find an entry which has the highest priority. */
822 TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links)
823 if (link->priority < link_pri->priority)
824 link = link_pri;
826 /* move to work list. */
827 TAILQ_REMOVE(&acpi_pci_link_entries, link, links);
828 TAILQ_INSERT_TAIL(&sorted_list, link, links);
831 while (!TAILQ_EMPTY(&sorted_list)) {
832 /* move them back to the list, one by one... */
833 link = TAILQ_FIRST(&sorted_list);
834 TAILQ_REMOVE(&sorted_list, link, links);
835 TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
839 static void
840 acpi_pci_link_fixup_bootdisabled_link(void)
842 int i, j;
843 int irq1, irq2;
844 struct acpi_pci_link_entry *link;
845 ACPI_STATUS error;
847 if (bootverbose) {
848 kprintf("ACPI PCI link before fixup for boot-disabled links:\n");
849 acpi_pci_link_bootdisabled_dump();
852 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
853 /* ignore non boot-disabled links. */
854 if (link->current_irq != 0)
855 continue;
857 /* sort IRQs based on their penalty descending. */
858 for (i = 0; i < link->number_of_interrupts; i++) {
859 irq1 = link->sorted_irq[i];
860 for (j = i + 1; j < link->number_of_interrupts; j++) {
861 irq2 = link->sorted_irq[j];
862 if (irq_penalty[irq1] < irq_penalty[irq2]) {
863 continue;
865 link->sorted_irq[i] = irq2;
866 link->sorted_irq[j] = irq1;
867 irq1 = irq2;
871 /* try with lower penalty IRQ. */
872 for (i = 0; i < link->number_of_interrupts; i++) {
873 irq1 = link->sorted_irq[i];
874 error = acpi_pci_link_set_irq(link, irq1);
875 if (error == AE_OK) {
876 /* OK, we use this. give another penalty. */
877 irq_penalty[irq1] += 100 * link->references;
878 break;
883 if (bootverbose) {
884 kprintf("ACPI PCI link after fixup for boot-disabled links:\n");
885 acpi_pci_link_bootdisabled_dump();
890 * Public interface
894 acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno)
896 struct acpi_prt_entry *entry;
897 ACPI_PCI_ROUTING_TABLE *prt;
898 u_int8_t *prtp;
899 ACPI_STATUS error;
900 static int first_time =1;
902 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
904 if (acpi_disabled("pci_link"))
905 return (0);
907 if (first_time) {
908 TAILQ_INIT(&acpi_prt_entries);
909 TAILQ_INIT(&acpi_pci_link_entries);
910 acpi_pci_link_init_irq_penalty();
911 first_time = 0;
914 if (prtbuf == NULL)
915 return (-1);
917 prtp = prtbuf->Pointer;
918 if (prtp == NULL) /* didn't get routing table */
919 return (-1);
921 /* scan the PCI Routing Table */
922 for (;;) {
923 prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
925 if (prt->Length == 0) /* end of table */
926 break;
928 error = acpi_pci_link_add_prt(dev, prt, busno);
929 if (ACPI_FAILURE(error)) {
930 kprintf("couldn't add PCI interrupt link entry - %s\n",
931 AcpiFormatException(error));
934 /* skip to next entry */
935 prtp += prt->Length;
938 if (bootverbose) {
939 kprintf("ACPI PCI link initial configuration:\n");
940 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
941 if (entry->busno != busno)
942 continue;
943 acpi_pci_link_entry_dump(entry);
947 /* manual configuration. */
948 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
949 int irq;
950 char prthint[32];
952 if (entry->busno != busno)
953 continue;
955 ksnprintf(prthint, sizeof(prthint),
956 "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno,
957 (int)((entry->prt.Address & 0xffff0000) >> 16),
958 (int)entry->prt.Pin);
960 if (kgetenv_int(prthint, &irq) == 0)
961 continue;
963 if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) {
964 error = acpi_pci_link_set_irq(entry->pci_link, irq);
965 if (ACPI_FAILURE(error)) {
966 kprintf("couldn't set IRQ to "
967 "link entry %s - %s\n",
968 acpi_name(entry->pci_link->handle),
969 AcpiFormatException(error));
971 continue;
975 * Do auto arbitration for this device's PCI link
976 * if hint value 0 is specified.
978 if (irq == 0)
979 entry->pci_link->current_irq = 0;
982 /* auto arbitration */
983 acpi_pci_link_update_irq_penalty(dev, busno);
984 acpi_pci_link_set_bootdisabled_priority();
985 acpi_pci_link_fixup_bootdisabled_link();
987 if (bootverbose) {
988 kprintf("ACPI PCI link arbitrated configuration:\n");
989 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
990 if (entry->busno != busno)
991 continue;
992 acpi_pci_link_entry_dump(entry);
996 return (0);
1000 acpi_pci_link_resume(device_t dev, ACPI_BUFFER *prtbuf, int busno)
1002 struct acpi_prt_entry *entry;
1003 ACPI_STATUS error;
1005 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1007 if (acpi_disabled("pci_link"))
1008 return (0);
1010 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1011 if (entry->pcidev != dev)
1012 continue;
1014 error = acpi_pci_link_set_irq(entry->pci_link,
1015 entry->pci_link->current_irq);
1016 if (ACPI_FAILURE(error)) {
1017 kprintf("couldn't set IRQ to link entry %s - %s\n",
1018 acpi_name(entry->pci_link->handle),
1019 AcpiFormatException(error));
1023 return (0);