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