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