xref: /freebsd/sys/dev/acpica/acpi_pci_link.c (revision 6780ab54325a71e7e70112b11657973edde8655e)
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 	}
374 
375 	link->initial_irq = link->current_irq;
376 
377 	error = AcpiGetPossibleResources(handle, &buf);
378 	if (ACPI_FAILURE(error)) {
379 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
380 		    "couldn't get PCI interrupt link device _PRS data %s - %s\n",
381 		    acpi_name(handle), AcpiFormatException(error)));
382 		goto out;
383 	}
384 
385 	if (buf.Pointer == NULL) {
386 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
387 		    "_PRS nuffer is empty - %s\n", acpi_name(handle)));
388 		error = AE_NO_MEMORY;
389 		goto out;
390 	}
391 
392 	resources = (ACPI_RESOURCE *) buf.Pointer;
393 	bcopy(resources, &link->possible_resources,
394 	    sizeof(link->possible_resources));
395 
396 	error = acpi_pci_link_get_irq_resources(resources,
397 	    &link->number_of_interrupts, link->interrupts);
398 	if (ACPI_FAILURE(error)) {
399 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
400 		    "couldn't get possible IRQs from PCI interrupt link %s - %s\n",
401 		    acpi_name(handle), AcpiFormatException(error)));
402 		goto out;
403 	}
404 
405 	if (link->number_of_interrupts == 0) {
406 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
407 		    "PCI interrupt link device _PRS data is corrupted - %s\n",
408 		    acpi_name(handle)));
409 		error = AE_NULL_ENTRY;
410 		goto out;
411 	}
412 
413 	link->references++;
414 
415 	TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
416 	entry->pci_link = link;
417 
418 	error = AE_OK;
419 out:
420 	if (buf.Pointer != NULL) {
421 		AcpiOsFree(buf.Pointer);
422 	}
423 
424 	if (error != AE_OK && link != NULL) {
425 		AcpiOsFree(link);
426 	}
427 
428 	return_ACPI_STATUS (error);
429 }
430 
431 static ACPI_STATUS
432 acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno)
433 {
434 	ACPI_HANDLE		handle;
435 	ACPI_STATUS		error;
436 	UINT32			sta;
437 	struct acpi_prt_entry	*entry;
438 
439 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
440 
441 	if ((prt == NULL) || (prt->Source == NULL) || (prt->Source[0] == '\0')) {
442 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
443 		    "couldn't handle this routing table - hardwired\n"));
444 		return_ACPI_STATUS (AE_BAD_PARAMETER);
445 	}
446 
447 	error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source, &handle);
448 	if (ACPI_FAILURE(error)) {
449 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
450 		    "couldn't get acpi handle - %s\n",
451 		    AcpiFormatException(error)));
452 		return_ACPI_STATUS (error);
453 	}
454 
455 	error = acpi_pci_link_get_object_status(handle, &sta);
456 	if (ACPI_FAILURE(error)) {
457 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
458 		    "couldn't get object status %s - %s\n",
459 		    acpi_name(handle), AcpiFormatException(error)));
460 		return_ACPI_STATUS (error);
461 	}
462 
463 	if (!(sta & (ACPI_STA_PRESENT | ACPI_STA_FUNCTIONAL))) {
464 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
465 		    "PCI interrupt link is not functional - %s\n",
466 		    acpi_name(handle)));
467 		return_ACPI_STATUS (AE_ERROR);
468 	}
469 
470 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
471 		if (entry->busno == busno &&
472 		    entry->prt.Address == prt->Address &&
473 		    entry->prt.Pin == prt->Pin) {
474 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
475 			    "PCI interrupt link entry already exists - %s\n",
476 			    acpi_name(handle)));
477 			return_ACPI_STATUS (AE_ALREADY_EXISTS);
478 		}
479 	}
480 
481 	entry = AcpiOsAllocate(sizeof(struct acpi_prt_entry));
482 	if (entry == NULL) {
483 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
484 		    "couldn't allocate memory - %s\n", acpi_name(handle)));
485 		return_ACPI_STATUS (AE_NO_MEMORY);
486 	}
487 
488 	bzero(entry, sizeof(struct acpi_prt_entry));
489 
490 	entry->pcidev = pcidev;
491 	entry->busno = busno;
492 	bcopy(prt, &entry->prt, sizeof(entry->prt));
493 
494 	error = acpi_pci_link_add_link(handle, entry);
495 	if (ACPI_FAILURE(error)) {
496 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
497 		    "couldn't add prt entry to pci link %s - %s\n",
498 		    acpi_name(handle), AcpiFormatException(error)));
499 		goto out;
500 	}
501 
502 	TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links);
503 	error = AE_OK;
504 
505 out:
506 	if (error != AE_OK && entry != NULL) {
507 		AcpiOsFree(entry);
508 	}
509 
510 	return_ACPI_STATUS (error);
511 }
512 
513 static int
514 acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
515 {
516 	UINT8			i;
517 
518 	if (irq == 0) {
519 		return (0);
520 	}
521 
522 	for (i = 0; i < link->number_of_interrupts; i++) {
523 		if (link->interrupts[i] == irq) {
524 			return (1);
525 		}
526 	}
527 
528 	/* allow initial IRQ as valid one. */
529 	if (link->initial_irq == irq) {
530 		return (1);
531 	}
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 PCI 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 
564 	bzero(&resbuf, sizeof(resbuf));
565 	crsbuf.Pointer = NULL;
566 	resbuf.Id = ACPI_RSTYPE_IRQ;
567 	resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
568 
569 	if (link->possible_resources.Id != ACPI_RSTYPE_IRQ &&
570 	    link->possible_resources.Id != ACPI_RSTYPE_EXT_IRQ) {
571 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
572 		    "Resource is not an IRQ entry %s - %d\n",
573 		    acpi_name(link->handle), link->possible_resources.Id));
574 		return_ACPI_STATUS (AE_TYPE);
575 	}
576 
577 	switch (link->possible_resources.Id) {
578 	case ACPI_RSTYPE_IRQ:
579 		/* structure copy other fields */
580 		resbuf.Data.Irq = link->possible_resources.Data.Irq;
581 		break;
582 
583 	case ACPI_RSTYPE_EXT_IRQ:
584 		/* XXX */
585 		resbuf.Data.Irq.EdgeLevel = ACPI_LEVEL_SENSITIVE;
586 		resbuf.Data.Irq.ActiveHighLow = ACPI_ACTIVE_LOW;
587 		resbuf.Data.Irq.SharedExclusive = ACPI_SHARED;
588 		break;
589 	}
590 
591 	resbuf.Data.Irq.NumberOfInterrupts = 1;
592 	resbuf.Data.Irq.Interrupts[0] = irq;
593 
594 	error = acpi_AppendBufferResource(&crsbuf, &resbuf);
595 	if (ACPI_FAILURE(error)) {
596 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
597 		    "couldn't setup buffer by acpi_AppendBufferResource - %s\n",
598 		    acpi_name(link->handle)));
599 		return_ACPI_STATUS (error);
600 	}
601 
602 	if (crsbuf.Pointer == NULL) {
603 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
604 		    "buffer setup by acpi_AppendBufferResource is corrupted - %s\n",
605 		    acpi_name(link->handle)));
606 		return_ACPI_STATUS (AE_NO_MEMORY);
607 	}
608 
609 	error = AcpiSetCurrentResources(link->handle, &crsbuf);
610 	if (ACPI_FAILURE(error)) {
611 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
612 		    "couldn't set PCI interrupt link device _SRS %s - %s\n",
613 		    acpi_name(link->handle), AcpiFormatException(error)));
614 		return_ACPI_STATUS (error);
615 	}
616 
617 	AcpiOsFree(crsbuf.Pointer);
618 	link->current_irq = 0;
619 
620 	error = acpi_pci_link_get_object_status(link->handle, &sta);
621 	if (ACPI_FAILURE(error)) {
622 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
623 		    "couldn't get object status %s - %s\n",
624 		    acpi_name(link->handle), AcpiFormatException(error)));
625 		return_ACPI_STATUS (error);
626 	}
627 
628 	if (!(sta & ACPI_STA_ENABLE)) {
629 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
630 		    "PCI interrupt link is disabled - %s\n",
631 		    acpi_name(link->handle)));
632 		return_ACPI_STATUS (AE_ERROR);
633 	}
634 
635 	error = acpi_pci_link_get_current_irq(link, &link->current_irq);
636 	if (ACPI_FAILURE(error)) {
637 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
638 		    "couldn't get current IRQ from PCI interrupt link %s - %s\n",
639 		    acpi_name(link->handle), AcpiFormatException(error)));
640 		return_ACPI_STATUS (error);
641 	}
642 
643 	if (link->current_irq == irq) {
644 		error = AE_OK;
645 	} else {
646 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
647 		    "couldn't set IRQ %d to PCI interrupt link %d - %s\n",
648 		    irq, link->current_irq, acpi_name(link->handle)));
649 
650 		link->current_irq = 0;
651 		error = AE_ERROR;
652 	}
653 
654 	return_ACPI_STATUS (error);
655 }
656 
657 /*
658  * Auto arbitration for boot-disabled devices
659  */
660 
661 static void
662 acpi_pci_link_bootdisabled_dump(void)
663 
664 {
665 	int			i;
666 	int			irq;
667 	struct acpi_pci_link_entry *link;
668 
669 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
670 		/* boot-disabled link only. */
671 		if (link->current_irq != 0) {
672 			continue;
673 		}
674 
675 		printf("%s:\n", acpi_name(link->handle));
676 		printf("	interrupts:	");
677 		for (i = 0; i < link->number_of_interrupts; i++) {
678 			irq = link->sorted_irq[i];
679 			printf("%6d", irq);
680 		}
681 		printf("\n");
682 		printf("	penalty:	");
683 		for (i = 0; i < link->number_of_interrupts; i++) {
684 			irq = link->sorted_irq[i];
685 			printf("%6d", irq_penalty[irq]);
686 		}
687 		printf("\n");
688 		printf("	references:	%d\n", link->references);
689 		printf("	priority:	%d\n", link->priority);
690 	}
691 }
692 
693 static void
694 acpi_pci_link_init_irq_penalty(void)
695 {
696 	int			irq;
697 
698 	bzero(irq_penalty, sizeof(irq_penalty));
699 	for (irq = 0; irq < MAX_ISA_INTERRUPTS; irq++) {
700 		/* 0, 1, 2, 8:	timer, keyboard, cascade */
701 		if (irq == 0 || irq == 1 || irq == 2 || irq == 8) {
702 			irq_penalty[irq] = 100000;
703 			continue;
704 		}
705 
706 		/* 13, 14, 15:	npx, ATA controllers */
707 		if (irq == 13 || irq == 14 || irq == 15) {
708 			irq_penalty[irq] = 10000;
709 			continue;
710 		}
711 
712 		/* 3,4,6,7,12:	typicially used by legacy hardware */
713 		if (irq == 3 || irq == 4 || irq == 6 || irq == 7 || irq == 12) {
714 			irq_penalty[irq] = 1000;
715 			continue;
716 		}
717 	}
718 }
719 
720 static int
721 acpi_pci_link_is_irq_exclusive(ACPI_RESOURCE *res)
722 {
723 	if (res == NULL) {
724 		return (0);
725 	}
726 
727 	if (res->Id != ACPI_RSTYPE_IRQ &&
728 	    res->Id != ACPI_RSTYPE_EXT_IRQ) {
729 		return (0);
730 	}
731 
732 	if (res->Id == ACPI_RSTYPE_IRQ &&
733 	    res->Data.Irq.SharedExclusive == ACPI_EXCLUSIVE) {
734 		return (1);
735 	}
736 
737 	if (res->Id == ACPI_RSTYPE_EXT_IRQ &&
738 	    res->Data.ExtendedIrq.SharedExclusive == ACPI_EXCLUSIVE) {
739 		return (1);
740 	}
741 
742 	return (0);
743 }
744 
745 static void
746 acpi_pci_link_update_irq_penalty(device_t dev, int busno)
747 {
748 	int			i;
749 	int			irq;
750 	int			rid;
751 	struct resource		*res;
752 	struct acpi_prt_entry	*entry;
753 	struct acpi_pci_link_entry *link;
754 
755 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
756 		if (entry->busno != busno) {
757 			continue;
758 		}
759 
760 		link = entry->pci_link;
761 		if (link == NULL) {
762 			continue;	/* impossible... */
763 		}
764 
765 		if (link->current_irq != 0) {
766 			/* not boot-disabled link, we will use this IRQ. */
767 			irq_penalty[link->current_irq] += 100;
768 			continue;
769 		}
770 
771 		/* boot-disabled link */
772 		for (i = 0; i < link->number_of_interrupts; i++) {
773 			/* give 10 for each possible IRQs. */
774 			irq = link->interrupts[i];
775 			irq_penalty[irq] += 10;
776 
777 			/* higher penalty if exclusive. */
778 			if (acpi_pci_link_is_irq_exclusive(&link->possible_resources)) {
779 				irq_penalty[irq] += 100;
780 			}
781 
782 			/* XXX try to get this IRQ in non-sharable mode. */
783 			rid = 0;
784 			res = bus_alloc_resource(dev, SYS_RES_IRQ,
785 						 &rid, irq, irq, 1, 0);
786 			if (res != NULL) {
787 				bus_release_resource(dev, SYS_RES_IRQ,
788 				    rid, res);
789 			} else {
790 				/* this is in use, give 100. */
791 				irq_penalty[irq] += 100;
792 			}
793 		}
794 
795 		/* initialize `sorted' possible IRQs. */
796 		bcopy(link->interrupts, link->sorted_irq,
797 		    sizeof(link->sorted_irq));
798 	}
799 }
800 
801 static void
802 acpi_pci_link_set_bootdisabled_priority(void)
803 {
804 	int			sum_penalty;
805 	int			i;
806 	int			irq;
807 	struct acpi_pci_link_entry *link, *link_pri;
808 	TAILQ_HEAD(, acpi_pci_link_entry) sorted_list;
809 
810 	if (bootverbose) {
811 		printf("---- before setting priority for links ------------\n");
812 		acpi_pci_link_bootdisabled_dump();
813 	}
814 
815 	/* reset priority for all links. */
816 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
817 		link->priority = 0;
818 	}
819 
820 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
821 		/* not boot-disabled link, give no chance to be arbitrated. */
822 		if (link->current_irq != 0) {
823 			link->priority = 0;
824 			continue;
825 		}
826 
827 		/*
828 		 * Calculate the priority for each boot-disabled links.
829 		 * o IRQ penalty indicates difficulty to use.
830 		 * o #references for devices indicates importance of the link.
831 		 * o #interrupts indicates flexibility of the link.
832 		 */
833 		sum_penalty = 0;
834 		for (i = 0; i < link->number_of_interrupts; i++) {
835 			irq = link->interrupts[i];
836 			sum_penalty += irq_penalty[irq];
837 		}
838 
839 		link->priority = (sum_penalty * link->references) / link->number_of_interrupts;
840 	}
841 
842 	/*
843 	 * Sort PCI links based on the priority.
844 	 * XXX Any other better ways rather than using work list?
845 	 */
846 	TAILQ_INIT(&sorted_list);
847 	while (!TAILQ_EMPTY(&acpi_pci_link_entries)) {
848 		link = TAILQ_FIRST(&acpi_pci_link_entries);
849 		/* find an entry which has the highest priority. */
850 		TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links) {
851 			if (link->priority < link_pri->priority) {
852 				link = link_pri;
853 			}
854 		}
855 		/* move to work list. */
856 		TAILQ_REMOVE(&acpi_pci_link_entries, link, links);
857 		TAILQ_INSERT_TAIL(&sorted_list, link, links);
858 	}
859 
860 	while (!TAILQ_EMPTY(&sorted_list)) {
861 		/* move them back to the list, one by one... */
862 		link = TAILQ_FIRST(&sorted_list);
863 		TAILQ_REMOVE(&sorted_list, link, links);
864 		TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
865 	}
866 }
867 
868 static void
869 acpi_pci_link_fixup_bootdisabled_link(void)
870 {
871 	int			i, j;
872 	int			irq1, irq2;
873 	struct acpi_pci_link_entry *link;
874 	ACPI_STATUS		error;
875 
876 	if (bootverbose) {
877 		printf("---- before fixup boot-disabled links -------------\n");
878 		acpi_pci_link_bootdisabled_dump();
879 	}
880 
881 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
882 		/* ignore non boot-disabled links. */
883 		if (link->current_irq != 0) {
884 			continue;
885 		}
886 
887 		/* sort IRQs based on their penalty descending. */
888 		for (i = 0; i < link->number_of_interrupts; i++) {
889 			irq1 = link->sorted_irq[i];
890 			for (j = i + 1; j < link->number_of_interrupts; j++) {
891 				irq2 = link->sorted_irq[j];
892 				if (irq_penalty[irq1] < irq_penalty[irq2]) {
893 					continue;
894 				}
895 				link->sorted_irq[i] = irq2;
896 				link->sorted_irq[j] = irq1;
897 				irq1 = irq2;
898 			}
899 		}
900 
901 		/* try with lower penalty IRQ. */
902 		for (i = 0; i < link->number_of_interrupts - 1; i++) {
903 			irq1 = link->sorted_irq[i];
904 			error = acpi_pci_link_set_irq(link, irq1);
905 			if (error == AE_OK) {
906 				/* OK, we use this.  give another penalty. */
907 				irq_penalty[irq1] += 100 * link->references;
908 				break;
909 			}
910 			/* NG, try next IRQ... */
911 		}
912 	}
913 
914 	if (bootverbose) {
915 		printf("---- after fixup boot-disabled links --------------\n");
916 		acpi_pci_link_bootdisabled_dump();
917 	}
918 }
919 
920 /*
921  * Public interface
922  */
923 
924 int
925 acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno)
926 {
927 	struct acpi_prt_entry	*entry;
928 	ACPI_PCI_ROUTING_TABLE	*prt;
929 	u_int8_t		*prtp;
930 	ACPI_STATUS		error;
931 	static int		first_time =1;
932 
933 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
934 
935 	if (acpi_disabled("pci_link")) {
936 		return (0);
937 	}
938 
939 	if (first_time) {
940 		TAILQ_INIT(&acpi_prt_entries);
941 		TAILQ_INIT(&acpi_pci_link_entries);
942 		acpi_pci_link_init_irq_penalty();
943 		first_time = 0;
944 	}
945 
946 	if (prtbuf == NULL) {
947 		return (-1);
948 	}
949 
950 	prtp = prtbuf->Pointer;
951 	if (prtp == NULL) {		/* didn't get routing table */
952 		return (-1);
953 	}
954 
955 	/* scan the PCI Routing Table */
956 	for (;;) {
957 		prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
958 
959 		if (prt->Length == 0)	/* end of table */
960 		    break;
961 
962 		error = acpi_pci_link_add_prt(dev, prt, busno);
963 		if (ACPI_FAILURE(error)) {
964 			ACPI_DEBUG_PRINT((ACPI_DB_WARN,
965 			    "couldn't add PCI interrupt link entry - %s\n",
966 			    AcpiFormatException(error)));
967 		}
968 
969 		/* skip to next entry */
970 		prtp += prt->Length;
971 	}
972 
973 	if (bootverbose) {
974 		printf("---- initial configuration ------------------------\n");
975 		TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
976 			if (entry->busno != busno) {
977 				continue;
978 			}
979 
980 			acpi_pci_link_entry_dump(entry);
981 		}
982 	}
983 
984 	/* manual configuration. */
985 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
986 		UINT8			irq;
987 		char			*irqstr, *op;
988 		char			prthint[32];
989 
990 		if (entry->busno != busno) {
991 			continue;
992 		}
993 
994 		snprintf(prthint, sizeof(prthint),
995 		    "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno,
996 		    (int)((entry->prt.Address & 0xffff0000) >> 16),
997 		    (int)entry->prt.Pin);
998 
999 		irqstr = getenv(prthint);
1000 		if (irqstr == NULL) {
1001 			continue;
1002 		}
1003 
1004 		irq = strtoul(irqstr, &op, 0);
1005 		if (*op != '\0') {
1006 			continue;
1007 		}
1008 
1009 		if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) {
1010 			error = acpi_pci_link_set_irq(entry->pci_link, irq);
1011 			if (ACPI_FAILURE(error)) {
1012 				ACPI_DEBUG_PRINT((ACPI_DB_WARN,
1013 				    "couldn't set IRQ to PCI interrupt link entry %s - %s\n",
1014 				    acpi_name(entry->pci_link->handle),
1015 				    AcpiFormatException(error)));
1016 			}
1017 			continue;
1018 		}
1019 
1020 		/*
1021 		 * Do auto arbitration for this device's PCI link
1022 		 * if hint value 0 is specified.
1023 		 */
1024 		if (irq == 0) {
1025 			entry->pci_link->current_irq = 0;
1026 		}
1027 	}
1028 
1029 	/* auto arbitration */
1030 	acpi_pci_link_update_irq_penalty(dev, busno);
1031 	acpi_pci_link_set_bootdisabled_priority();
1032 	acpi_pci_link_fixup_bootdisabled_link();
1033 
1034 	if (bootverbose) {
1035 		printf("---- arbitrated configuration ---------------------\n");
1036 		TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1037 			if (entry->busno != busno) {
1038 				continue;
1039 			}
1040 
1041 			acpi_pci_link_entry_dump(entry);
1042 		}
1043 	}
1044 
1045 	return (0);
1046 }
1047 
1048 int
1049 acpi_pci_link_resume(device_t dev, ACPI_BUFFER *prtbuf, int busno)
1050 {
1051 	struct acpi_prt_entry	*entry;
1052 	ACPI_STATUS		error;
1053 
1054 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1055 
1056 	if (acpi_disabled("pci_link")) {
1057 		return (0);
1058 	}
1059 
1060 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1061 		if (entry->pcidev != dev) {
1062 			continue;
1063 		}
1064 
1065 		error = acpi_pci_link_set_irq(entry->pci_link,
1066 			    entry->pci_link->current_irq);
1067 		if (ACPI_FAILURE(error)) {
1068 			ACPI_DEBUG_PRINT((ACPI_DB_WARN,
1069 			    "couldn't set IRQ to PCI interrupt link entry %s - %s\n",
1070 			    acpi_name(entry->pci_link->handle),
1071 			    AcpiFormatException(error)));
1072 		}
1073 	}
1074 
1075 	return (0);
1076 }
1077 
1078