xref: /freebsd/sys/dev/acpica/acpi_pci_link.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
1 /*-
2  * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
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/acpica/acpivar.h>
37 #include <dev/acpica/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 PCI 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 
719 	if (res == NULL ||
720 	    (res->Id != ACPI_RSTYPE_IRQ &&
721 	    res->Id != ACPI_RSTYPE_EXT_IRQ))
722 		return (0);
723 
724 	if ((res->Id == ACPI_RSTYPE_IRQ &&
725 	    res->Data.Irq.SharedExclusive == ACPI_EXCLUSIVE) ||
726 	    (res->Id == ACPI_RSTYPE_EXT_IRQ &&
727 	    res->Data.ExtendedIrq.SharedExclusive == ACPI_EXCLUSIVE))
728 		return (1);
729 
730 	return (0);
731 }
732 
733 static void
734 acpi_pci_link_update_irq_penalty(device_t dev, int busno)
735 {
736 	int			i;
737 	int			irq;
738 	int			rid;
739 	struct resource		*res;
740 	struct acpi_prt_entry	*entry;
741 	struct acpi_pci_link_entry *link;
742 
743 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
744 		if (entry->busno != busno)
745 			continue;
746 
747 		/* Impossible? */
748 		link = entry->pci_link;
749 		if (link == NULL)
750 			continue;
751 
752 		if (link->current_irq != 0) {
753 			/* not boot-disabled link, we will use this IRQ. */
754 			irq_penalty[link->current_irq] += 100;
755 			continue;
756 		}
757 
758 		/* boot-disabled link */
759 		for (i = 0; i < link->number_of_interrupts; i++) {
760 			/* give 10 for each possible IRQs. */
761 			irq = link->interrupts[i];
762 			irq_penalty[irq] += 10;
763 
764 			/* higher penalty if exclusive. */
765 			if (link_exclusive(&link->possible_resources))
766 				irq_penalty[irq] += 100;
767 
768 			/* XXX try to get this IRQ in non-sharable mode. */
769 			rid = 0;
770 			res = bus_alloc_resource(dev, SYS_RES_IRQ,
771 						 &rid, irq, irq, 1, 0);
772 			if (res != NULL) {
773 				bus_release_resource(dev, SYS_RES_IRQ,
774 				    rid, res);
775 			} else {
776 				/* this is in use, give 100. */
777 				irq_penalty[irq] += 100;
778 			}
779 		}
780 
781 		/* initialize `sorted' possible IRQs. */
782 		bcopy(link->interrupts, link->sorted_irq,
783 		    sizeof(link->sorted_irq));
784 	}
785 }
786 
787 static void
788 acpi_pci_link_set_bootdisabled_priority(void)
789 {
790 	int			sum_penalty;
791 	int			i;
792 	int			irq;
793 	struct acpi_pci_link_entry *link, *link_pri;
794 	TAILQ_HEAD(, acpi_pci_link_entry) sorted_list;
795 
796 	if (bootverbose) {
797 		printf("ACPI PCI link before setting link priority:\n");
798 		acpi_pci_link_bootdisabled_dump();
799 	}
800 
801 	/* reset priority for all links. */
802 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links)
803 		link->priority = 0;
804 
805 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
806 		/* not boot-disabled link, give no chance to be arbitrated. */
807 		if (link->current_irq != 0) {
808 			link->priority = 0;
809 			continue;
810 		}
811 
812 		/*
813 		 * Calculate the priority for each boot-disabled links.
814 		 * o IRQ penalty indicates difficulty to use.
815 		 * o #references for devices indicates importance of the link.
816 		 * o #interrupts indicates flexibility of the link.
817 		 */
818 		sum_penalty = 0;
819 		for (i = 0; i < link->number_of_interrupts; i++) {
820 			irq = link->interrupts[i];
821 			sum_penalty += irq_penalty[irq];
822 		}
823 
824 		link->priority = (sum_penalty * link->references) /
825 		    link->number_of_interrupts;
826 	}
827 
828 	/*
829 	 * Sort PCI links based on the priority.
830 	 * XXX Any other better ways rather than using work list?
831 	 */
832 	TAILQ_INIT(&sorted_list);
833 	while (!TAILQ_EMPTY(&acpi_pci_link_entries)) {
834 		link = TAILQ_FIRST(&acpi_pci_link_entries);
835 		/* find an entry which has the highest priority. */
836 		TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links)
837 			if (link->priority < link_pri->priority)
838 				link = link_pri;
839 
840 		/* move to work list. */
841 		TAILQ_REMOVE(&acpi_pci_link_entries, link, links);
842 		TAILQ_INSERT_TAIL(&sorted_list, link, links);
843 	}
844 
845 	while (!TAILQ_EMPTY(&sorted_list)) {
846 		/* move them back to the list, one by one... */
847 		link = TAILQ_FIRST(&sorted_list);
848 		TAILQ_REMOVE(&sorted_list, link, links);
849 		TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
850 	}
851 }
852 
853 static void
854 acpi_pci_link_fixup_bootdisabled_link(void)
855 {
856 	int			i, j;
857 	int			irq1, irq2;
858 	struct acpi_pci_link_entry *link;
859 	ACPI_STATUS		error;
860 
861 	if (bootverbose) {
862 		printf("ACPI PCI link before fixup for boot-disabled links:\n");
863 		acpi_pci_link_bootdisabled_dump();
864 	}
865 
866 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
867 		/* ignore non boot-disabled links. */
868 		if (link->current_irq != 0)
869 			continue;
870 
871 		/* sort IRQs based on their penalty descending. */
872 		for (i = 0; i < link->number_of_interrupts; i++) {
873 			irq1 = link->sorted_irq[i];
874 			for (j = i + 1; j < link->number_of_interrupts; j++) {
875 				irq2 = link->sorted_irq[j];
876 				if (irq_penalty[irq1] < irq_penalty[irq2]) {
877 					continue;
878 				}
879 				link->sorted_irq[i] = irq2;
880 				link->sorted_irq[j] = irq1;
881 				irq1 = irq2;
882 			}
883 		}
884 
885 		/* try with lower penalty IRQ. */
886 		for (i = 0; i < link->number_of_interrupts; i++) {
887 			irq1 = link->sorted_irq[i];
888 			error = acpi_pci_link_set_irq(link, irq1);
889 			if (error == AE_OK) {
890 				/* OK, we use this.  give another penalty. */
891 				irq_penalty[irq1] += 100 * link->references;
892 				break;
893 			}
894 		}
895 	}
896 
897 	if (bootverbose) {
898 		printf("ACPI PCI link after fixup for boot-disabled links:\n");
899 		acpi_pci_link_bootdisabled_dump();
900 	}
901 }
902 
903 /*
904  * Public interface
905  */
906 
907 int
908 acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno)
909 {
910 	struct acpi_prt_entry	*entry;
911 	ACPI_PCI_ROUTING_TABLE	*prt;
912 	u_int8_t		*prtp;
913 	ACPI_STATUS		error;
914 	static int		first_time =1;
915 
916 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
917 
918 	if (acpi_disabled("pci_link"))
919 		return (0);
920 
921 	if (first_time) {
922 		TAILQ_INIT(&acpi_prt_entries);
923 		TAILQ_INIT(&acpi_pci_link_entries);
924 		acpi_pci_link_init_irq_penalty();
925 		first_time = 0;
926 	}
927 
928 	if (prtbuf == NULL)
929 		return (-1);
930 
931 	prtp = prtbuf->Pointer;
932 	if (prtp == NULL)		/* didn't get routing table */
933 		return (-1);
934 
935 	/* scan the PCI Routing Table */
936 	for (;;) {
937 		prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
938 
939 		if (prt->Length == 0)	/* end of table */
940 		    break;
941 
942 		error = acpi_pci_link_add_prt(dev, prt, busno);
943 		if (ACPI_FAILURE(error)) {
944 			ACPI_DEBUG_PRINT((ACPI_DB_WARN,
945 			    "couldn't add PCI interrupt link entry - %s\n",
946 			    AcpiFormatException(error)));
947 		}
948 
949 		/* skip to next entry */
950 		prtp += prt->Length;
951 	}
952 
953 	if (bootverbose) {
954 		printf("ACPI PCI link initial configuration:\n");
955 		TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
956 			if (entry->busno != busno)
957 				continue;
958 			acpi_pci_link_entry_dump(entry);
959 		}
960 	}
961 
962 	/* manual configuration. */
963 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
964 		int			irq;
965 		char			prthint[32];
966 
967 		if (entry->busno != busno)
968 			continue;
969 
970 		snprintf(prthint, sizeof(prthint),
971 		    "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno,
972 		    (int)((entry->prt.Address & 0xffff0000) >> 16),
973 		    (int)entry->prt.Pin);
974 
975 		if (getenv_int(prthint, &irq) == 0)
976 			continue;
977 
978 		if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) {
979 			error = acpi_pci_link_set_irq(entry->pci_link, irq);
980 			if (ACPI_FAILURE(error)) {
981 				ACPI_DEBUG_PRINT((ACPI_DB_WARN,
982 				    "couldn't set IRQ to link entry %s - %s\n",
983 				    acpi_name(entry->pci_link->handle),
984 				    AcpiFormatException(error)));
985 			}
986 			continue;
987 		}
988 
989 		/*
990 		 * Do auto arbitration for this device's PCI link
991 		 * if hint value 0 is specified.
992 		 */
993 		if (irq == 0)
994 			entry->pci_link->current_irq = 0;
995 	}
996 
997 	/* auto arbitration */
998 	acpi_pci_link_update_irq_penalty(dev, busno);
999 	acpi_pci_link_set_bootdisabled_priority();
1000 	acpi_pci_link_fixup_bootdisabled_link();
1001 
1002 	if (bootverbose) {
1003 		printf("ACPI PCI link arbitrated configuration:\n");
1004 		TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1005 			if (entry->busno != busno)
1006 				continue;
1007 			acpi_pci_link_entry_dump(entry);
1008 		}
1009 	}
1010 
1011 	return (0);
1012 }
1013 
1014 int
1015 acpi_pci_link_resume(device_t dev, ACPI_BUFFER *prtbuf, int busno)
1016 {
1017 	struct acpi_prt_entry	*entry;
1018 	ACPI_STATUS		error;
1019 
1020 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1021 
1022 	if (acpi_disabled("pci_link"))
1023 		return (0);
1024 
1025 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1026 		if (entry->pcidev != dev)
1027 			continue;
1028 
1029 		error = acpi_pci_link_set_irq(entry->pci_link,
1030 			    entry->pci_link->current_irq);
1031 		if (ACPI_FAILURE(error)) {
1032 			ACPI_DEBUG_PRINT((ACPI_DB_WARN,
1033 			    "couldn't set IRQ to link entry %s - %s\n",
1034 			    acpi_name(entry->pci_link->handle),
1035 			    AcpiFormatException(error)));
1036 		}
1037 	}
1038 
1039 	return (0);
1040 }
1041