Bring in YONETANI Tomokazu's acpi-update-2.patch (27-May-2004), a major
[dragonfly.git] / sys / dev / acpica5 / acpi_pci_link.c
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  * $FreeBSD: src/sys/dev/acpica/acpi_pci_link.c,v 1.14 2004/04/14 03:34:11 njl Exp $
27  * $DragonFly: src/sys/dev/acpica5/acpi_pci_link.c,v 1.2 2004/06/27 08:52:39 dillon Exp $
28  */
29
30 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/bus.h>
34
35 #include "acpi.h"
36 #include <dev/acpica5/acpivar.h>
37 #include <dev/acpica5/acpi_pcibvar.h>
38
39 /* Hooks for the ACPI CA debugging infrastructure. */
40 #define _COMPONENT      ACPI_BUS
41 ACPI_MODULE_NAME("PCI_LINK")
42
43 #define MAX_POSSIBLE_INTERRUPTS 16
44 #define MAX_ISA_INTERRUPTS      16
45 #define MAX_ACPI_INTERRUPTS     255
46
47 struct acpi_pci_link_entry {
48         TAILQ_ENTRY(acpi_pci_link_entry) links;
49         ACPI_HANDLE     handle;
50         UINT8           current_irq;
51         UINT8           initial_irq;
52         ACPI_RESOURCE   possible_resources;
53         UINT8           number_of_interrupts;
54         UINT8           interrupts[MAX_POSSIBLE_INTERRUPTS];
55         UINT8           sorted_irq[MAX_POSSIBLE_INTERRUPTS];
56         int             references;
57         int             priority;
58 };
59
60 TAILQ_HEAD(acpi_pci_link_entries, acpi_pci_link_entry);
61 static struct acpi_pci_link_entries acpi_pci_link_entries;
62
63 struct acpi_prt_entry {
64         TAILQ_ENTRY(acpi_prt_entry) links;
65         device_t        pcidev;
66         int             busno;
67         ACPI_PCI_ROUTING_TABLE prt;
68         struct acpi_pci_link_entry *pci_link;
69 };
70
71 TAILQ_HEAD(acpi_prt_entries, acpi_prt_entry);
72 static struct acpi_prt_entries acpi_prt_entries;
73
74 static int      irq_penalty[MAX_ACPI_INTERRUPTS];
75
76 #define ACPI_STA_PRESENT        0x00000001
77 #define ACPI_STA_ENABLE         0x00000002
78 #define ACPI_STA_SHOWINUI       0x00000004
79 #define ACPI_STA_FUNCTIONAL     0x00000008
80
81 /*
82  * PCI link object management
83  */
84
85 static void
86 acpi_pci_link_dump_polarity(UINT32 ActiveHighLow)
87 {
88
89         switch (ActiveHighLow) {
90         case ACPI_ACTIVE_HIGH:
91                 printf("high,");
92                 break;
93         case ACPI_ACTIVE_LOW:
94                 printf("low,");
95                 break;
96         default:
97                 printf("unknown,");
98                 break;
99         }
100 }
101
102 static void
103 acpi_pci_link_dump_trigger(UINT32 EdgeLevel)
104 {
105
106         switch (EdgeLevel) {
107         case ACPI_EDGE_SENSITIVE:
108                 printf("edge,");
109                 break;
110         case ACPI_LEVEL_SENSITIVE:
111                 printf("level,");
112                 break;
113         default:
114                 printf("unknown,");
115                 break;
116         }
117 }
118
119 static void
120 acpi_pci_link_dump_sharemode(UINT32 SharedExclusive)
121 {
122
123         switch (SharedExclusive) {
124         case ACPI_EXCLUSIVE:
125                 printf("exclusive");
126                 break;
127         case ACPI_SHARED:
128                 printf("sharable");
129                 break;
130         default:
131                 printf("unknown");
132                 break;
133         }
134 }
135
136 static void
137 acpi_pci_link_entry_dump(struct acpi_prt_entry *entry)
138 {
139         UINT8                   i;
140         ACPI_RESOURCE_IRQ       *Irq;
141         ACPI_RESOURCE_EXT_IRQ   *ExtIrq;
142
143         if (entry == NULL || entry->pci_link == NULL)
144                 return;
145
146         printf("%s irq %3d: ", acpi_name(entry->pci_link->handle),
147             entry->pci_link->current_irq);
148
149         printf("[");
150         for (i = 0; i < entry->pci_link->number_of_interrupts; i++)
151                 printf("%3d", entry->pci_link->interrupts[i]);
152         printf("] ");
153
154         switch (entry->pci_link->possible_resources.Id) {
155         case ACPI_RSTYPE_IRQ:
156                 Irq = &entry->pci_link->possible_resources.Data.Irq;
157                 acpi_pci_link_dump_polarity(Irq->ActiveHighLow);
158                 acpi_pci_link_dump_trigger(Irq->EdgeLevel);
159                 acpi_pci_link_dump_sharemode(Irq->SharedExclusive);
160                 break;
161         case ACPI_RSTYPE_EXT_IRQ:
162                 ExtIrq = &entry->pci_link->possible_resources.Data.ExtendedIrq;
163                 acpi_pci_link_dump_polarity(ExtIrq->ActiveHighLow);
164                 acpi_pci_link_dump_trigger(ExtIrq->EdgeLevel);
165                 acpi_pci_link_dump_sharemode(ExtIrq->SharedExclusive);
166                 break;
167         }
168
169         printf(" %d.%d.%d\n", entry->busno,
170             (int)((entry->prt.Address & 0xffff0000) >> 16),
171             (int)entry->prt.Pin);
172 }
173
174 static ACPI_STATUS
175 acpi_pci_link_get_object_status(ACPI_HANDLE handle, UINT32 *sta)
176 {
177         ACPI_DEVICE_INFO        *devinfo;
178         ACPI_BUFFER             buf;
179         ACPI_STATUS             error;
180
181         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
182
183         if (handle == NULL || sta == NULL) {
184                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
185                 return_ACPI_STATUS (AE_BAD_PARAMETER);
186         }
187
188         buf.Pointer = NULL;
189         buf.Length = ACPI_ALLOCATE_BUFFER;
190         error = AcpiGetObjectInfo(handle, &buf);
191         if (ACPI_FAILURE(error)) {
192                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
193                     "couldn't get object info %s - %s\n",
194                     acpi_name(handle), AcpiFormatException(error)));
195                 return_ACPI_STATUS (error);
196         }
197
198         devinfo = (ACPI_DEVICE_INFO *)buf.Pointer;
199         if ((devinfo->Valid & ACPI_VALID_HID) == 0 ||
200             strcmp(devinfo->HardwareId.Value, "PNP0C0F") != 0) {
201                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid hardware ID - %s\n",
202                     acpi_name(handle)));
203                 AcpiOsFree(buf.Pointer);
204                 return_ACPI_STATUS (AE_TYPE);
205         }
206
207         if ((devinfo->Valid & ACPI_VALID_STA) != 0) {
208                 *sta = devinfo->CurrentStatus;
209         } else {
210                 ACPI_DEBUG_PRINT((ACPI_DB_WARN, "invalid status - %s\n",
211                     acpi_name(handle)));
212                 *sta = 0;
213         }
214
215         AcpiOsFree(buf.Pointer);
216         return_ACPI_STATUS (AE_OK);
217 }
218
219 static ACPI_STATUS
220 acpi_pci_link_get_irq_resources(ACPI_RESOURCE *resources,
221     UINT8 *number_of_interrupts, UINT8 interrupts[])
222 {
223         UINT8                   count;
224         UINT8                   i;
225         UINT32                  NumberOfInterrupts;
226         UINT32                  *Interrupts;
227
228         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
229
230         if (resources == NULL || number_of_interrupts == NULL) {
231                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
232                 return_ACPI_STATUS (AE_BAD_PARAMETER);
233         }
234
235         *number_of_interrupts = 0;
236         NumberOfInterrupts = 0;
237         Interrupts = NULL;
238
239         if (resources->Id == ACPI_RSTYPE_START_DPF)
240                 resources = ACPI_NEXT_RESOURCE(resources);
241
242         if (resources->Id != ACPI_RSTYPE_IRQ &&
243             resources->Id != ACPI_RSTYPE_EXT_IRQ) {
244                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
245                     "Resource is not an IRQ entry - %d\n", resources->Id));
246                 return_ACPI_STATUS (AE_TYPE);
247         }
248
249         switch (resources->Id) {
250         case ACPI_RSTYPE_IRQ:
251                 NumberOfInterrupts = resources->Data.Irq.NumberOfInterrupts;
252                 Interrupts = resources->Data.Irq.Interrupts;
253                 break;
254         case ACPI_RSTYPE_EXT_IRQ:
255                 NumberOfInterrupts =
256                     resources->Data.ExtendedIrq.NumberOfInterrupts;
257                 Interrupts = resources->Data.ExtendedIrq.Interrupts;
258                 break;
259         }
260         
261         if (NumberOfInterrupts == 0) {
262                 ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Blank IRQ resource\n"));
263                 return_ACPI_STATUS (AE_NULL_ENTRY);
264         }
265
266         count = 0;
267         for (i = 0; i < NumberOfInterrupts; i++) {
268                 if (i >= MAX_POSSIBLE_INTERRUPTS) {
269                         ACPI_DEBUG_PRINT((ACPI_DB_WARN, "too many IRQs (%d)\n",
270                             i));
271                         break;
272                 }
273                 if (Interrupts[i] == 0) {
274                         ACPI_DEBUG_PRINT((ACPI_DB_WARN, "invalid IRQ %d\n",
275                             Interrupts[i]));
276                         continue;
277                 }
278                 interrupts[count] = Interrupts[i];
279                 count++;
280         }
281         *number_of_interrupts = count;
282
283         return_ACPI_STATUS (AE_OK);
284 }
285
286 static ACPI_STATUS
287 acpi_pci_link_get_current_irq(struct acpi_pci_link_entry *link, UINT8 *irq)
288 {
289         ACPI_STATUS             error;
290         ACPI_BUFFER             buf;
291         ACPI_RESOURCE           *resources;
292         UINT8                   number_of_interrupts;
293         UINT8                   interrupts[MAX_POSSIBLE_INTERRUPTS];;
294
295         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
296
297         if (link == NULL || irq == NULL) {
298                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
299                 return_ACPI_STATUS (AE_BAD_PARAMETER);
300         }
301
302         *irq = 0;
303         buf.Pointer = NULL;
304         buf.Length = ACPI_ALLOCATE_BUFFER;
305         error = AcpiGetCurrentResources(link->handle, &buf);
306         if (ACPI_FAILURE(error)) {
307                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
308                     "couldn't get PCI interrupt link device _CRS %s - %s\n",
309                     acpi_name(link->handle), AcpiFormatException(error)));
310                 return_ACPI_STATUS (error);
311         }
312         if (buf.Pointer == NULL) {
313                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
314                     "couldn't allocate memory - %s\n",
315                     acpi_name(link->handle)));
316                 return_ACPI_STATUS (AE_NO_MEMORY);
317         }
318
319         resources = (ACPI_RESOURCE *) buf.Pointer;
320         number_of_interrupts = 0;
321         bzero(interrupts, sizeof(interrupts));
322         error = acpi_pci_link_get_irq_resources(resources,
323                     &number_of_interrupts, interrupts);
324         AcpiOsFree(buf.Pointer);
325
326         if (ACPI_FAILURE(error)) {
327                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
328                     "couldn't get current IRQ from interrupt link %s - %s\n",
329                     acpi_name(link->handle), AcpiFormatException(error)));
330                 return_ACPI_STATUS (error);
331         }
332
333         if (number_of_interrupts == 0) {
334                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
335                     "PCI interrupt link device _CRS data is corrupted - %s\n",
336                     acpi_name(link->handle)));
337                 return_ACPI_STATUS (AE_NULL_ENTRY);
338         }
339
340         *irq = interrupts[0];
341
342         return_ACPI_STATUS (AE_OK);
343 }
344
345 static ACPI_STATUS
346 acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry)
347 {
348         ACPI_STATUS             error;
349         ACPI_BUFFER             buf;
350         ACPI_RESOURCE           *resources;
351         struct acpi_pci_link_entry *link;
352
353         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
354
355         entry->pci_link = NULL;
356         TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
357                 if (link->handle == handle) {
358                         entry->pci_link = link;
359                         link->references++;
360                         return_ACPI_STATUS (AE_OK);
361                 }
362         }
363
364         link = AcpiOsAllocate(sizeof(struct acpi_pci_link_entry));
365         if (link == NULL) {
366                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
367                     "couldn't allocate memory - %s\n", acpi_name(handle)));
368                 return_ACPI_STATUS (AE_NO_MEMORY);
369         }
370
371         buf.Pointer = NULL;
372         buf.Length = ACPI_ALLOCATE_BUFFER;
373
374         bzero(link, sizeof(struct acpi_pci_link_entry));
375
376         link->handle = handle;
377
378         error = acpi_pci_link_get_current_irq(link, &link->current_irq);
379         if (ACPI_FAILURE(error)) {
380                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
381                     "couldn't get current IRQ from interrupt link %s - %s\n",
382                     acpi_name(handle), AcpiFormatException(error)));
383         }
384
385         link->initial_irq = link->current_irq;
386
387         error = AcpiGetPossibleResources(handle, &buf);
388         if (ACPI_FAILURE(error)) {
389                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
390                     "couldn't get interrupt link device _PRS data %s - %s\n",
391                     acpi_name(handle), AcpiFormatException(error)));
392                 goto out;
393         }
394         if (buf.Pointer == NULL) {
395                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
396                     "_PRS nuffer is empty - %s\n", acpi_name(handle)));
397                 error = AE_NO_MEMORY;
398                 goto out;
399         }
400
401         resources = (ACPI_RESOURCE *) buf.Pointer;
402         bcopy(resources, &link->possible_resources,
403             sizeof(link->possible_resources));
404
405         error = acpi_pci_link_get_irq_resources(resources,
406             &link->number_of_interrupts, link->interrupts);
407         if (ACPI_FAILURE(error)) {
408                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
409                     "couldn't get possible IRQs from interrupt link %s - %s\n",
410                     acpi_name(handle), AcpiFormatException(error)));
411                 goto out;
412         }
413
414         if (link->number_of_interrupts == 0) {
415                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
416                     "interrupt link device _PRS data is corrupted - %s\n",
417                     acpi_name(handle)));
418                 error = AE_NULL_ENTRY;
419                 goto out;
420         }
421
422         link->references++;
423
424         TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
425         entry->pci_link = link;
426
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);
433
434         return_ACPI_STATUS (error);
435 }
436
437 static ACPI_STATUS
438 acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno)
439 {
440         ACPI_HANDLE             handle;
441         ACPI_STATUS             error;
442         UINT32                  sta;
443         struct acpi_prt_entry   *entry;
444
445         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
446
447         if (prt == NULL || prt->Source == NULL || prt->Source[0] == '\0') {
448                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
449                     "couldn't handle this routing table - hardwired\n"));
450                 return_ACPI_STATUS (AE_BAD_PARAMETER);
451         }
452
453         error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source, &handle);
454         if (ACPI_FAILURE(error)) {
455                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "couldn't get handle - %s\n",
456                     AcpiFormatException(error)));
457                 return_ACPI_STATUS (error);
458         }
459
460         error = acpi_pci_link_get_object_status(handle, &sta);
461         if (ACPI_FAILURE(error)) {
462                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
463                     "couldn't get object status %s - %s\n",
464                     acpi_name(handle), AcpiFormatException(error)));
465                 return_ACPI_STATUS (error);
466         }
467
468         if ((sta & (ACPI_STA_PRESENT | ACPI_STA_FUNCTIONAL)) == 0) {
469                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
470                     "interrupt link is not functional - %s\n",
471                     acpi_name(handle)));
472                 return_ACPI_STATUS (AE_ERROR);
473         }
474
475         TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
476                 if (entry->busno == busno &&
477                     entry->prt.Address == prt->Address &&
478                     entry->prt.Pin == prt->Pin) {
479                         ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
480                             "interrupt link entry already exists - %s\n",
481                             acpi_name(handle)));
482                         return_ACPI_STATUS (AE_ALREADY_EXISTS);
483                 }
484         }
485
486         entry = AcpiOsAllocate(sizeof(struct acpi_prt_entry));
487         if (entry == NULL) {
488                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
489                     "couldn't allocate memory - %s\n", acpi_name(handle)));
490                 return_ACPI_STATUS (AE_NO_MEMORY);
491         }
492         bzero(entry, sizeof(struct acpi_prt_entry));
493
494         entry->pcidev = pcidev;
495         entry->busno = busno;
496         bcopy(prt, &entry->prt, sizeof(entry->prt));
497
498         error = acpi_pci_link_add_link(handle, entry);
499         if (ACPI_FAILURE(error)) {
500                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
501                     "couldn't add _PRT entry to link %s - %s\n",
502                     acpi_name(handle), AcpiFormatException(error)));
503                 goto out;
504         }
505
506         TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links);
507         error = AE_OK;
508
509 out:
510         if (error != AE_OK && entry != NULL)
511                 AcpiOsFree(entry);
512
513         return_ACPI_STATUS (error);
514 }
515
516 static int
517 acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
518 {
519         UINT8                   i;
520
521         if (irq == 0)
522                 return (0);
523
524         for (i = 0; i < link->number_of_interrupts; i++) {
525                 if (link->interrupts[i] == irq)
526                         return (1);
527         }
528
529         /* allow initial IRQ as valid one. */
530         if (link->initial_irq == irq)
531                 return (1);
532
533         return (0);
534 }
535
536 static ACPI_STATUS
537 acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
538 {
539         ACPI_STATUS             error;
540         ACPI_RESOURCE           resbuf;
541         ACPI_BUFFER             crsbuf;
542         UINT32                  sta;
543
544         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
545
546         if (!acpi_pci_link_is_valid_irq(link, irq)) {
547                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
548                     "couldn't set invalid IRQ %d - %s\n", irq,
549                     acpi_name(link->handle)));
550                 return_ACPI_STATUS (AE_BAD_PARAMETER);
551         }
552
553         error = acpi_pci_link_get_current_irq(link, &link->current_irq);
554         if (ACPI_FAILURE(error)) {
555                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
556                     "couldn't get current IRQ from interrupt link %s - %s\n",
557                     acpi_name(link->handle), AcpiFormatException(error)));
558         }
559
560         if (link->current_irq == irq)
561                 return_ACPI_STATUS (AE_OK);
562
563         bzero(&resbuf, sizeof(resbuf));
564         crsbuf.Pointer = NULL;
565
566         switch (link->possible_resources.Id) {
567         case ACPI_RSTYPE_IRQ:
568                 resbuf.Id = ACPI_RSTYPE_IRQ;
569                 resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
570
571                 /* structure copy other fields */
572                 resbuf.Data.Irq = link->possible_resources.Data.Irq;
573                 resbuf.Data.Irq.NumberOfInterrupts = 1;
574                 resbuf.Data.Irq.Interrupts[0] = irq;
575                 break;
576         case ACPI_RSTYPE_EXT_IRQ:
577                 resbuf.Id = ACPI_RSTYPE_EXT_IRQ;
578                 resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_EXT_IRQ);
579
580                 /* structure copy other fields */
581                 resbuf.Data.ExtendedIrq =
582                     link->possible_resources.Data.ExtendedIrq;
583                 resbuf.Data.ExtendedIrq.NumberOfInterrupts = 1;
584                 resbuf.Data.ExtendedIrq.Interrupts[0] = irq;
585                 break;
586         default:
587                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
588                     "Resource is not an IRQ entry %s - %d\n",
589                     acpi_name(link->handle), link->possible_resources.Id));
590                 return_ACPI_STATUS (AE_TYPE);
591         }
592
593         error = acpi_AppendBufferResource(&crsbuf, &resbuf);
594         if (ACPI_FAILURE(error)) {
595                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
596                     "couldn't setup buffer by acpi_AppendBufferResource - %s\n",
597                     acpi_name(link->handle)));
598                 return_ACPI_STATUS (error);
599         }
600         if (crsbuf.Pointer == NULL) {
601                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
602                     "appended buffer for %s is corrupted\n",
603                     acpi_name(link->handle)));
604                 return_ACPI_STATUS (AE_NO_MEMORY);
605         }
606
607         error = AcpiSetCurrentResources(link->handle, &crsbuf);
608         if (ACPI_FAILURE(error)) {
609                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
610                     "couldn't set link device _SRS %s - %s\n",
611                     acpi_name(link->handle), AcpiFormatException(error)));
612                 return_ACPI_STATUS (error);
613         }
614
615         AcpiOsFree(crsbuf.Pointer);
616         link->current_irq = 0;
617
618         error = acpi_pci_link_get_object_status(link->handle, &sta);
619         if (ACPI_FAILURE(error)) {
620                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
621                     "couldn't get object status %s - %s\n",
622                     acpi_name(link->handle), AcpiFormatException(error)));
623                 return_ACPI_STATUS (error);
624         }
625
626         if ((sta & ACPI_STA_ENABLE) == 0) {
627                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
628                     "interrupt link %s is disabled\n",
629                     acpi_name(link->handle)));
630                 return_ACPI_STATUS (AE_ERROR);
631         }
632
633         error = acpi_pci_link_get_current_irq(link, &link->current_irq);
634         if (ACPI_FAILURE(error)) {
635                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
636                     "couldn't get current IRQ from interrupt link %s - %s\n",
637                     acpi_name(link->handle), AcpiFormatException(error)));
638                 return_ACPI_STATUS (error);
639         }
640
641         if (link->current_irq == irq) {
642                 error = AE_OK;
643         } else {
644                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
645                     "couldn't set IRQ %d to interrupt link %d - %s\n",
646                     irq, link->current_irq, acpi_name(link->handle)));
647                 link->current_irq = 0;
648                 error = AE_ERROR;
649         }
650
651         return_ACPI_STATUS (error);
652 }
653
654 /*
655  * Auto arbitration for boot-disabled devices
656  */
657
658 static void
659 acpi_pci_link_bootdisabled_dump(void)
660 {
661         int                     i;
662         int                     irq;
663         struct acpi_pci_link_entry *link;
664
665         TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
666                 /* boot-disabled link only. */
667                 if (link->current_irq != 0)
668                         continue;
669
670                 printf("%s:\n", acpi_name(link->handle));
671                 printf("        interrupts:     ");
672                 for (i = 0; i < link->number_of_interrupts; i++) {
673                         irq = link->sorted_irq[i];
674                         printf("%6d", irq);
675                 }
676                 printf("\n");
677                 printf("        penalty:        ");
678                 for (i = 0; i < link->number_of_interrupts; i++) {
679                         irq = link->sorted_irq[i];
680                         printf("%6d", irq_penalty[irq]);
681                 }
682                 printf("\n");
683                 printf("        references:     %d\n", link->references);
684                 printf("        priority:       %d\n", link->priority);
685         }
686 }
687
688 static void
689 acpi_pci_link_init_irq_penalty(void)
690 {
691         int                     irq;
692
693         bzero(irq_penalty, sizeof(irq_penalty));
694         for (irq = 0; irq < MAX_ISA_INTERRUPTS; irq++) {
695                 /* 0, 1, 2, 8:  timer, keyboard, cascade */
696                 if (irq == 0 || irq == 1 || irq == 2 || irq == 8) {
697                         irq_penalty[irq] = 100000;
698                         continue;
699                 }
700
701                 /* 13, 14, 15:  npx, ATA controllers */
702                 if (irq == 13 || irq == 14 || irq == 15) {
703                         irq_penalty[irq] = 10000;
704                         continue;
705                 }
706
707                 /* 3,4,6,7,12:  typicially used by legacy hardware */
708                 if (irq == 3 || irq == 4 || irq == 6 || irq == 7 || irq == 12) {
709                         irq_penalty[irq] = 1000;
710                         continue;
711                 }
712         }
713 }
714
715 static int
716 link_exclusive(ACPI_RESOURCE *res)
717 {
718         if (res == NULL ||
719             (res->Id != ACPI_RSTYPE_IRQ &&
720             res->Id != ACPI_RSTYPE_EXT_IRQ))
721                 return (0);
722
723         if ((res->Id == ACPI_RSTYPE_IRQ &&
724             res->Data.Irq.SharedExclusive == ACPI_EXCLUSIVE) ||
725             (res->Id == ACPI_RSTYPE_EXT_IRQ &&
726             res->Data.ExtendedIrq.SharedExclusive == ACPI_EXCLUSIVE))
727                 return (1);
728
729         return (0);
730 }
731
732 static void
733 acpi_pci_link_update_irq_penalty(device_t dev, int busno)
734 {
735         int                     i;
736         int                     irq;
737         int                     rid;
738         struct resource         *res;
739         struct acpi_prt_entry   *entry;
740         struct acpi_pci_link_entry *link;
741
742         TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
743                 if (entry->busno != busno)
744                         continue;
745
746                 /* Impossible? */
747                 link = entry->pci_link;
748                 if (link == NULL)
749                         continue;
750
751                 if (link->current_irq != 0) {
752                         /* not boot-disabled link, we will use this IRQ. */
753                         irq_penalty[link->current_irq] += 100;
754                         continue;
755                 }
756
757                 /* boot-disabled link */
758                 for (i = 0; i < link->number_of_interrupts; i++) {
759                         /* give 10 for each possible IRQs. */
760                         irq = link->interrupts[i];
761                         irq_penalty[irq] += 10;
762
763                         /* higher penalty if exclusive. */
764                         if (link_exclusive(&link->possible_resources))
765                                 irq_penalty[irq] += 100;
766
767                         /* XXX try to get this IRQ in non-sharable mode. */
768                         rid = 0;
769                         res = bus_alloc_resource(dev, SYS_RES_IRQ,
770                                                  &rid, irq, irq, 1, 0);
771                         if (res != NULL) {
772                                 bus_release_resource(dev, SYS_RES_IRQ,
773                                     rid, res);
774                         } else {
775                                 /* this is in use, give 100. */
776                                 irq_penalty[irq] += 100;
777                         }
778                 }
779
780                 /* initialize `sorted' possible IRQs. */
781                 bcopy(link->interrupts, link->sorted_irq,
782                     sizeof(link->sorted_irq));
783         }
784 }
785
786 static void
787 acpi_pci_link_set_bootdisabled_priority(void)
788 {
789         int                     sum_penalty;
790         int                     i;
791         int                     irq;
792         struct acpi_pci_link_entry *link, *link_pri;
793         TAILQ_HEAD(, acpi_pci_link_entry) sorted_list;
794
795         if (bootverbose) {
796                 printf("ACPI PCI link before setting link priority:\n");
797                 acpi_pci_link_bootdisabled_dump();
798         }
799
800         /* reset priority for all links. */
801         TAILQ_FOREACH(link, &acpi_pci_link_entries, links)
802                 link->priority = 0;
803
804         TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
805                 /* not boot-disabled link, give no chance to be arbitrated. */
806                 if (link->current_irq != 0) {
807                         link->priority = 0;
808                         continue;
809                 }
810
811                 /*
812                  * Calculate the priority for each boot-disabled links.
813                  * o IRQ penalty indicates difficulty to use. 
814                  * o #references for devices indicates importance of the link.
815                  * o #interrupts indicates flexibility of the link.
816                  */
817                 sum_penalty = 0;
818                 for (i = 0; i < link->number_of_interrupts; i++) {
819                         irq = link->interrupts[i];
820                         sum_penalty += irq_penalty[irq];
821                 }
822
823                 link->priority = (sum_penalty * link->references) /
824                     link->number_of_interrupts;
825         }
826
827         /*
828          * Sort PCI links based on the priority.
829          * XXX Any other better ways rather than using work list?
830          */
831         TAILQ_INIT(&sorted_list);
832         while (!TAILQ_EMPTY(&acpi_pci_link_entries)) {
833                 link = TAILQ_FIRST(&acpi_pci_link_entries);
834                 /* find an entry which has the highest priority. */
835                 TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links)
836                         if (link->priority < link_pri->priority)
837                                 link = link_pri;
838
839                 /* move to work list. */
840                 TAILQ_REMOVE(&acpi_pci_link_entries, link, links);
841                 TAILQ_INSERT_TAIL(&sorted_list, link, links);
842         }
843
844         while (!TAILQ_EMPTY(&sorted_list)) {
845                 /* move them back to the list, one by one... */
846                 link = TAILQ_FIRST(&sorted_list);
847                 TAILQ_REMOVE(&sorted_list, link, links);
848                 TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
849         }
850 }
851
852 static void
853 acpi_pci_link_fixup_bootdisabled_link(void)
854 {
855         int                     i, j;
856         int                     irq1, irq2;
857         struct acpi_pci_link_entry *link;
858         ACPI_STATUS             error;
859
860         if (bootverbose) {
861                 printf("ACPI PCI link before fixup for boot-disabled links:\n");
862                 acpi_pci_link_bootdisabled_dump();
863         }
864
865         TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
866                 /* ignore non boot-disabled links. */
867                 if (link->current_irq != 0)
868                         continue;
869
870                 /* sort IRQs based on their penalty descending. */
871                 for (i = 0; i < link->number_of_interrupts; i++) {
872                         irq1 = link->sorted_irq[i];
873                         for (j = i + 1; j < link->number_of_interrupts; j++) {
874                                 irq2 = link->sorted_irq[j];
875                                 if (irq_penalty[irq1] < irq_penalty[irq2]) {
876                                         continue;
877                                 }
878                                 link->sorted_irq[i] = irq2;
879                                 link->sorted_irq[j] = irq1;
880                                 irq1 = irq2;
881                         }
882                 }
883
884                 /* try with lower penalty IRQ. */
885                 for (i = 0; i < link->number_of_interrupts; i++) {
886                         irq1 = link->sorted_irq[i];
887                         error = acpi_pci_link_set_irq(link, irq1);
888                         if (error == AE_OK) {
889                                 /* OK, we use this.  give another penalty. */
890                                 irq_penalty[irq1] += 100 * link->references;
891                                 break;
892                         }
893                 }
894         }
895
896         if (bootverbose) {
897                 printf("ACPI PCI link after fixup for boot-disabled links:\n");
898                 acpi_pci_link_bootdisabled_dump();
899         }
900 }
901
902 /*
903  * Public interface
904  */
905
906 int
907 acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno)
908 {
909         struct acpi_prt_entry   *entry;
910         ACPI_PCI_ROUTING_TABLE  *prt;
911         u_int8_t                *prtp;
912         ACPI_STATUS             error;
913         static int              first_time =1;
914
915         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
916
917         if (acpi_disabled("pci_link"))
918                 return (0);
919
920         if (first_time) {
921                 TAILQ_INIT(&acpi_prt_entries);
922                 TAILQ_INIT(&acpi_pci_link_entries);
923                 acpi_pci_link_init_irq_penalty();
924                 first_time = 0;
925         }
926
927         if (prtbuf == NULL)
928                 return (-1);
929
930         prtp = prtbuf->Pointer;
931         if (prtp == NULL)               /* didn't get routing table */
932                 return (-1);
933
934         /* scan the PCI Routing Table */
935         for (;;) {
936                 prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
937
938                 if (prt->Length == 0)   /* end of table */
939                     break;
940
941                 error = acpi_pci_link_add_prt(dev, prt, busno);
942                 if (ACPI_FAILURE(error)) {
943                         ACPI_DEBUG_PRINT((ACPI_DB_WARN,
944                             "couldn't add PCI interrupt link entry - %s\n",
945                             AcpiFormatException(error)));
946                 }
947
948                 /* skip to next entry */
949                 prtp += prt->Length;
950         }
951
952         if (bootverbose) {
953                 printf("ACPI PCI link initial configuration:\n");
954                 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
955                         if (entry->busno != busno)
956                                 continue;
957                         acpi_pci_link_entry_dump(entry);
958                 }
959         }
960
961         /* manual configuration. */
962         TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
963                 int                     irq;
964                 char                    prthint[32];
965
966                 if (entry->busno != busno)
967                         continue;
968
969                 snprintf(prthint, sizeof(prthint),
970                     "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno,
971                     (int)((entry->prt.Address & 0xffff0000) >> 16),
972                     (int)entry->prt.Pin);
973
974                 if (getenv_int(prthint, &irq) == 0)
975                         continue;
976
977                 if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) {
978                         error = acpi_pci_link_set_irq(entry->pci_link, irq);
979                         if (ACPI_FAILURE(error)) {
980                                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
981                                     "couldn't set IRQ to link entry %s - %s\n",
982                                     acpi_name(entry->pci_link->handle),
983                                     AcpiFormatException(error)));
984                         }
985                         continue;
986                 }
987
988                 /*
989                  * Do auto arbitration for this device's PCI link
990                  * if hint value 0 is specified.
991                  */
992                 if (irq == 0)
993                         entry->pci_link->current_irq = 0;
994         }
995
996         /* auto arbitration */
997         acpi_pci_link_update_irq_penalty(dev, busno);
998         acpi_pci_link_set_bootdisabled_priority();
999         acpi_pci_link_fixup_bootdisabled_link();
1000
1001         if (bootverbose) {
1002                 printf("ACPI PCI link arbitrated configuration:\n");
1003                 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1004                         if (entry->busno != busno)
1005                                 continue;
1006                         acpi_pci_link_entry_dump(entry);
1007                 }
1008         }
1009
1010         return (0);
1011 }
1012
1013 int
1014 acpi_pci_link_resume(device_t dev, ACPI_BUFFER *prtbuf, int busno)
1015 {
1016         struct acpi_prt_entry   *entry;
1017         ACPI_STATUS             error;
1018
1019         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1020
1021         if (acpi_disabled("pci_link"))
1022                 return (0);
1023
1024         TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1025                 if (entry->pcidev != dev)
1026                         continue;
1027
1028                 error = acpi_pci_link_set_irq(entry->pci_link,
1029                             entry->pci_link->current_irq);
1030                 if (ACPI_FAILURE(error)) {
1031                         ACPI_DEBUG_PRINT((ACPI_DB_WARN,
1032                             "couldn't set IRQ to link entry %s - %s\n",
1033                             acpi_name(entry->pci_link->handle),
1034                             AcpiFormatException(error)));
1035                 }
1036         }
1037
1038         return (0);
1039 }