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