xref: /freebsd/sys/dev/acpica/acpi_pci_link.c (revision cec50dea12481dc578c0805c887ab2097e1c06c5)
1 /*-
2  * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/bus.h>
34 
35 #include "acpi.h"
36 #include <dev/acpica/acpivar.h>
37 #include <dev/acpica/acpi_pcibvar.h>
38 
39 #include <dev/pci/pcivar.h>
40 #include "pcib_if.h"
41 
42 /* Hooks for the ACPI CA debugging infrastructure. */
43 #define _COMPONENT	ACPI_BUS
44 ACPI_MODULE_NAME("PCI_LINK")
45 
46 TAILQ_HEAD(acpi_pci_link_entries, acpi_pci_link_entry);
47 static struct acpi_pci_link_entries acpi_pci_link_entries;
48 ACPI_SERIAL_DECL(pci_link, "ACPI PCI link");
49 
50 TAILQ_HEAD(acpi_prt_entries, acpi_prt_entry);
51 static struct acpi_prt_entries acpi_prt_entries;
52 
53 static int	irq_penalty[MAX_ACPI_INTERRUPTS];
54 
55 static int	acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link,
56 		    UINT8 irq);
57 static void	acpi_pci_link_update_irq_penalty(device_t dev, int busno);
58 static void	acpi_pci_link_set_bootdisabled_priority(void);
59 static void	acpi_pci_link_fixup_bootdisabled_link(void);
60 
61 /*
62  * PCI link object management
63  */
64 
65 static void
66 acpi_pci_link_dump_polarity(UINT32 ActiveHighLow)
67 {
68 
69 	switch (ActiveHighLow) {
70 	case ACPI_ACTIVE_HIGH:
71 		printf("high,");
72 		break;
73 	case ACPI_ACTIVE_LOW:
74 		printf("low,");
75 		break;
76 	default:
77 		printf("unknown,");
78 		break;
79 	}
80 }
81 
82 static void
83 acpi_pci_link_dump_trigger(UINT32 EdgeLevel)
84 {
85 
86 	switch (EdgeLevel) {
87 	case ACPI_EDGE_SENSITIVE:
88 		printf("edge,");
89 		break;
90 	case ACPI_LEVEL_SENSITIVE:
91 		printf("level,");
92 		break;
93 	default:
94 		printf("unknown,");
95 		break;
96 	}
97 }
98 
99 static void
100 acpi_pci_link_dump_sharemode(UINT32 SharedExclusive)
101 {
102 
103 	switch (SharedExclusive) {
104 	case ACPI_EXCLUSIVE:
105 		printf("exclusive");
106 		break;
107 	case ACPI_SHARED:
108 		printf("sharable");
109 		break;
110 	default:
111 		printf("unknown");
112 		break;
113 	}
114 }
115 
116 static void
117 acpi_pci_link_entry_dump(struct acpi_prt_entry *entry)
118 {
119 	UINT8			i;
120 	ACPI_RESOURCE_IRQ	*Irq;
121 	ACPI_RESOURCE_EXT_IRQ	*ExtIrq;
122 	struct acpi_pci_link_entry *link;
123 
124 	if (entry == NULL || entry->pci_link == NULL)
125 		return;
126 	link = entry->pci_link;
127 
128 	printf("%s irq%c%2d: ", acpi_name(link->handle),
129 	    (link->flags & ACPI_LINK_ROUTED) ? '*' : ' ', link->current_irq);
130 
131 	printf("[");
132 	if (link->number_of_interrupts)
133 		printf("%2d", link->interrupts[0]);
134 	for (i = 1; i < link->number_of_interrupts; i++)
135 		printf("%3d", link->interrupts[i]);
136 	printf("] %2d+ ", link->initial_irq);
137 
138 	switch (link->possible_resources.Id) {
139 	case ACPI_RSTYPE_IRQ:
140 		Irq = &link->possible_resources.Data.Irq;
141 		acpi_pci_link_dump_polarity(Irq->ActiveHighLow);
142 		acpi_pci_link_dump_trigger(Irq->EdgeLevel);
143 		acpi_pci_link_dump_sharemode(Irq->SharedExclusive);
144 		break;
145 	case ACPI_RSTYPE_EXT_IRQ:
146 		ExtIrq = &link->possible_resources.Data.ExtendedIrq;
147 		acpi_pci_link_dump_polarity(ExtIrq->ActiveHighLow);
148 		acpi_pci_link_dump_trigger(ExtIrq->EdgeLevel);
149 		acpi_pci_link_dump_sharemode(ExtIrq->SharedExclusive);
150 		break;
151 	}
152 
153 	printf(" %d.%d.%d\n", entry->busno,
154 	    (int)((entry->prt.Address & 0xffff0000) >> 16),
155 	    (int)entry->prt.Pin);
156 }
157 
158 static ACPI_STATUS
159 acpi_pci_link_get_object_status(ACPI_HANDLE handle, UINT32 *sta)
160 {
161 	ACPI_DEVICE_INFO	*devinfo;
162 	ACPI_BUFFER		buf;
163 	ACPI_STATUS		error;
164 
165 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
166 
167 	if (handle == NULL || sta == NULL) {
168 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
169 		return_ACPI_STATUS (AE_BAD_PARAMETER);
170 	}
171 
172 	buf.Pointer = NULL;
173 	buf.Length = ACPI_ALLOCATE_BUFFER;
174 	error = AcpiGetObjectInfo(handle, &buf);
175 	if (ACPI_FAILURE(error)) {
176 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
177 		    "couldn't get object info %s - %s\n",
178 		    acpi_name(handle), AcpiFormatException(error)));
179 		return_ACPI_STATUS (error);
180 	}
181 
182 	devinfo = (ACPI_DEVICE_INFO *)buf.Pointer;
183 	if ((devinfo->Valid & ACPI_VALID_HID) == 0 ||
184 	    strcmp(devinfo->HardwareId.Value, "PNP0C0F") != 0) {
185 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid hardware ID - %s\n",
186 		    acpi_name(handle)));
187 		AcpiOsFree(buf.Pointer);
188 		return_ACPI_STATUS (AE_TYPE);
189 	}
190 
191 	if ((devinfo->Valid & ACPI_VALID_STA) != 0) {
192 		*sta = devinfo->CurrentStatus;
193 	} else {
194 		ACPI_DEBUG_PRINT((ACPI_DB_WARN, "invalid status - %s\n",
195 		    acpi_name(handle)));
196 		*sta = 0;
197 	}
198 
199 	AcpiOsFree(buf.Pointer);
200 	return_ACPI_STATUS (AE_OK);
201 }
202 
203 static ACPI_STATUS
204 acpi_pci_link_get_irq_resources(ACPI_RESOURCE *resources,
205     UINT8 *number_of_interrupts, UINT8 interrupts[])
206 {
207 	UINT8			count;
208 	UINT8			i;
209 	UINT32			NumberOfInterrupts;
210 	UINT32			*Interrupts;
211 
212 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
213 
214 	if (resources == NULL || number_of_interrupts == NULL) {
215 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
216 		return_ACPI_STATUS (AE_BAD_PARAMETER);
217 	}
218 
219 	*number_of_interrupts = 0;
220 	NumberOfInterrupts = 0;
221 	Interrupts = NULL;
222 
223 	if (resources->Id == ACPI_RSTYPE_START_DPF)
224 		resources = ACPI_NEXT_RESOURCE(resources);
225 
226 	if (resources->Id != ACPI_RSTYPE_IRQ &&
227 	    resources->Id != ACPI_RSTYPE_EXT_IRQ) {
228 		printf("acpi link get: resource %d is not an IRQ\n",
229 		    resources->Id);
230 		return_ACPI_STATUS (AE_TYPE);
231 	}
232 
233 	switch (resources->Id) {
234 	case ACPI_RSTYPE_IRQ:
235 		NumberOfInterrupts = resources->Data.Irq.NumberOfInterrupts;
236 		Interrupts = resources->Data.Irq.Interrupts;
237 		break;
238 	case ACPI_RSTYPE_EXT_IRQ:
239 		NumberOfInterrupts =
240 		    resources->Data.ExtendedIrq.NumberOfInterrupts;
241 		Interrupts = resources->Data.ExtendedIrq.Interrupts;
242 		break;
243 	}
244 
245 	if (NumberOfInterrupts == 0)
246 		return_ACPI_STATUS (AE_NULL_ENTRY);
247 
248 	count = 0;
249 	for (i = 0; i < NumberOfInterrupts; i++) {
250 		if (i >= MAX_POSSIBLE_INTERRUPTS) {
251 			ACPI_DEBUG_PRINT((ACPI_DB_WARN, "too many IRQs (%d)\n",
252 			    i));
253 			break;
254 		}
255 		if (Interrupts[i] == 0) {
256 			ACPI_DEBUG_PRINT((ACPI_DB_WARN, "invalid IRQ %d\n",
257 			    Interrupts[i]));
258 			continue;
259 		}
260 		interrupts[count] = Interrupts[i];
261 		count++;
262 	}
263 	*number_of_interrupts = count;
264 
265 	return_ACPI_STATUS (AE_OK);
266 }
267 
268 static ACPI_STATUS
269 acpi_pci_link_get_current_irq(struct acpi_pci_link_entry *link, UINT8 *irq)
270 {
271 	ACPI_STATUS		error;
272 	ACPI_BUFFER		buf;
273 	ACPI_RESOURCE		*resources;
274 	UINT8			number_of_interrupts;
275 	UINT8			interrupts[MAX_POSSIBLE_INTERRUPTS];;
276 
277 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
278 
279 	if (link == NULL || irq == NULL) {
280 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
281 		return_ACPI_STATUS (AE_BAD_PARAMETER);
282 	}
283 
284 	*irq = 0;
285 	buf.Pointer = NULL;
286 	buf.Length = ACPI_ALLOCATE_BUFFER;
287 	error = AcpiGetCurrentResources(link->handle, &buf);
288 	if (ACPI_FAILURE(error)) {
289 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
290 		    "couldn't get PCI interrupt link device _CRS %s - %s\n",
291 		    acpi_name(link->handle), AcpiFormatException(error)));
292 		return_ACPI_STATUS (error);
293 	}
294 	if (buf.Pointer == NULL) {
295 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
296 		    "couldn't allocate memory - %s\n",
297 		    acpi_name(link->handle)));
298 		return_ACPI_STATUS (AE_NO_MEMORY);
299 	}
300 
301 	resources = (ACPI_RESOURCE *) buf.Pointer;
302 	number_of_interrupts = 0;
303 	bzero(interrupts, sizeof(interrupts));
304 	error = acpi_pci_link_get_irq_resources(resources,
305 		    &number_of_interrupts, interrupts);
306 	AcpiOsFree(buf.Pointer);
307 
308 	if (ACPI_FAILURE(error)) {
309 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
310 		    "couldn't get current IRQ from interrupt link %s - %s\n",
311 		    acpi_name(link->handle), AcpiFormatException(error)));
312 		return_ACPI_STATUS (error);
313 	}
314 
315 	if (number_of_interrupts == 0) {
316 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
317 		    "PCI interrupt link device _CRS data is corrupted - %s\n",
318 		    acpi_name(link->handle)));
319 		return_ACPI_STATUS (AE_NULL_ENTRY);
320 	}
321 
322 	*irq = interrupts[0];
323 
324 	return_ACPI_STATUS (AE_OK);
325 }
326 
327 static ACPI_STATUS
328 acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry)
329 {
330 	ACPI_STATUS		error;
331 	ACPI_BUFFER		buf;
332 	ACPI_RESOURCE		*resources;
333 	struct acpi_pci_link_entry *link;
334 
335 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
336 	ACPI_SERIAL_ASSERT(pci_link);
337 
338 	entry->pci_link = NULL;
339 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
340 		if (link->handle == handle) {
341 			entry->pci_link = link;
342 			link->references++;
343 			return_ACPI_STATUS (AE_OK);
344 		}
345 	}
346 
347 	link = AcpiOsAllocate(sizeof(struct acpi_pci_link_entry));
348 	if (link == NULL) {
349 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
350 		    "couldn't allocate memory - %s\n", acpi_name(handle)));
351 		return_ACPI_STATUS (AE_NO_MEMORY);
352 	}
353 
354 	buf.Pointer = NULL;
355 	buf.Length = ACPI_ALLOCATE_BUFFER;
356 
357 	bzero(link, sizeof(struct acpi_pci_link_entry));
358 	link->handle = handle;
359 
360 	/*
361 	 * Get the IRQ configured at boot-time.  If successful, set this
362 	 * as the initial IRQ.
363 	 */
364 	error = acpi_pci_link_get_current_irq(link, &link->current_irq);
365 	if (ACPI_SUCCESS(error)) {
366 		link->initial_irq = link->current_irq;
367 	} else {
368 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
369 		    "couldn't get current IRQ from interrupt link %s - %s\n",
370 		    acpi_name(handle), AcpiFormatException(error)));
371 		link->initial_irq = 0;
372 	}
373 
374 	error = AcpiGetPossibleResources(handle, &buf);
375 	if (ACPI_FAILURE(error)) {
376 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
377 		    "couldn't get interrupt link device _PRS data %s - %s\n",
378 		    acpi_name(handle), AcpiFormatException(error)));
379 		goto out;
380 	}
381 	if (buf.Pointer == NULL) {
382 		ACPI_DEBUG_PRINT((ACPI_DB_WARN,
383 		    "_PRS buffer is empty - %s\n", acpi_name(handle)));
384 		error = AE_NO_MEMORY;
385 		goto out;
386 	}
387 
388 	/* Skip any DPF descriptors.  XXX We should centralize this code. */
389 	resources = (ACPI_RESOURCE *) buf.Pointer;
390 	if (resources->Id == ACPI_RSTYPE_START_DPF)
391 		resources = ACPI_NEXT_RESOURCE(resources);
392 
393 	/* XXX This only handles one resource, ignoring SourceIndex. */
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 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 		    "interrupt link device _PRS data is corrupted - %s\n",
409 		    acpi_name(handle)));
410 		error = AE_NULL_ENTRY;
411 		goto out;
412 	}
413 
414 	/*
415 	 * Try to disable this link.  If successful, set the current IRQ to
416 	 * zero and flags to indicate this link is not routed.  If we can't
417 	 * run _DIS (i.e., the method doesn't exist), assume the initial
418 	 * IRQ was routed by the BIOS.
419 	 */
420 	if (ACPI_SUCCESS(AcpiEvaluateObject(handle, "_DIS", NULL, NULL))) {
421 		link->current_irq = 0;
422 		link->flags = ACPI_LINK_NONE;
423 	} else
424 		link->flags = ACPI_LINK_ROUTED;
425 
426 	/*
427 	 * If the initial IRQ is invalid (not in _PRS), set it to 0 and
428 	 * mark this link as not routed.  We won't use it as the preferred
429 	 * interrupt later when we route.
430 	 */
431 	if (!acpi_pci_link_is_valid_irq(link, link->initial_irq) &&
432 	    link->initial_irq != 0) {
433 		printf("ACPI link %s has invalid initial irq %d, ignoring\n",
434 		    acpi_name(handle), link->initial_irq);
435 		link->initial_irq = 0;
436 		link->flags = ACPI_LINK_NONE;
437 	}
438 
439 	link->references++;
440 
441 	TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
442 	entry->pci_link = link;
443 
444 	error = AE_OK;
445 out:
446 	if (buf.Pointer != NULL)
447 		AcpiOsFree(buf.Pointer);
448 	if (error != AE_OK && link != NULL)
449 		AcpiOsFree(link);
450 
451 	return_ACPI_STATUS (error);
452 }
453 
454 static ACPI_STATUS
455 acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno)
456 {
457 	ACPI_HANDLE		handle;
458 	ACPI_STATUS		error;
459 	UINT32			sta;
460 	struct acpi_prt_entry	*entry;
461 
462 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
463 	ACPI_SERIAL_ASSERT(pci_link);
464 
465 	if (prt == NULL) {
466 		device_printf(pcidev, "NULL PRT entry\n");
467 		return_ACPI_STATUS (AE_BAD_PARAMETER);
468 	}
469 
470 	/* Bail out if attempting to add a duplicate PRT entry. */
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 			    "PRT entry already exists\n"));
477 			return_ACPI_STATUS (AE_ALREADY_EXISTS);
478 		}
479 	}
480 
481 	/* Allocate and initialize our new PRT entry. */
482 	entry = AcpiOsAllocate(sizeof(struct acpi_prt_entry));
483 	if (entry == NULL) {
484 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "can't allocate memory\n"));
485 		return_ACPI_STATUS (AE_NO_MEMORY);
486 	}
487 	bzero(entry, sizeof(struct acpi_prt_entry));
488 
489 	/*
490 	 * If the source link is NULL, then this IRQ is hardwired so skip
491 	 * initializing the link but still add it to the list.
492 	 */
493 	if (prt->Source[0] != '\0') {
494 		/* Get a handle for the link source. */
495 		error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source,
496 		    &handle);
497 		if (ACPI_FAILURE(error)) {
498 			device_printf(pcidev, "get handle for %s - %s\n",
499 			    prt->Source, AcpiFormatException(error));
500 			goto out;
501 		}
502 
503 		error = acpi_pci_link_get_object_status(handle, &sta);
504 		if (ACPI_FAILURE(error)) {
505 			device_printf(pcidev, "can't get status for %s - %s\n",
506 			    acpi_name(handle), AcpiFormatException(error));
507 			goto out;
508 		}
509 
510 		/* Probe/initialize the link. */
511 		error = acpi_pci_link_add_link(handle, entry);
512 		if (ACPI_FAILURE(error)) {
513 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
514 			    "couldn't add _PRT entry to link %s - %s\n",
515 			    acpi_name(handle), AcpiFormatException(error)));
516 			goto out;
517 		}
518 	}
519 
520 	entry->pcidev = pcidev;
521 	entry->busno = busno;
522 	bcopy(prt, &entry->prt, sizeof(entry->prt));
523 
524 	/*
525 	 * Make sure the Source value is null-terminated.  It is really a
526 	 * variable-length string (with a fixed size in the struct) so when
527 	 * we copy the entire struct, we truncate the string.  Instead of
528 	 * trying to make a variable-sized PRT object to handle the string,
529 	 * we store its handle in prt_source.  Callers should use that to
530 	 * look up the link object.
531 	 */
532 	entry->prt.Source[sizeof(prt->Source) - 1] = '\0';
533 	entry->prt_source = handle;
534 
535 	TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links);
536 	error = AE_OK;
537 
538 out:
539 	if (error != AE_OK && entry != NULL)
540 		AcpiOsFree(entry);
541 
542 	return_ACPI_STATUS (error);
543 }
544 
545 /*
546  * Look up the given interrupt in the list of possible settings for
547  * this link.  We don't special-case the initial link setting.  Some
548  * systems return current settings that are outside the list of valid
549  * settings so only allow choices explicitly specified in _PRS.
550  */
551 static int
552 acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
553 {
554 	UINT8			i;
555 
556 	if (irq == 0)
557 		return (FALSE);
558 
559 	for (i = 0; i < link->number_of_interrupts; i++) {
560 		if (link->interrupts[i] == irq)
561 			return (TRUE);
562 	}
563 
564 	return (FALSE);
565 }
566 
567 static ACPI_STATUS
568 acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
569 {
570 	ACPI_STATUS		error;
571 	ACPI_RESOURCE		resbuf;
572 	ACPI_BUFFER		crsbuf;
573 
574 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
575 	ACPI_SERIAL_ASSERT(pci_link);
576 
577 	/* Make sure the new IRQ is valid before routing. */
578 	if (!acpi_pci_link_is_valid_irq(link, irq)) {
579 		printf("acpi link set: invalid IRQ %d on %s\n",
580 		    irq, acpi_name(link->handle));
581 		return_ACPI_STATUS (AE_BAD_PARAMETER);
582 	}
583 
584 	/* If this this link has already been routed, just return. */
585 	if (link->flags & ACPI_LINK_ROUTED) {
586 		printf("acpi link set: %s already routed to %d\n",
587 		    acpi_name(link->handle), link->current_irq);
588 		return_ACPI_STATUS (AE_OK);
589 	}
590 
591 	/* Set up the IRQ resource for _SRS. */
592 	bzero(&resbuf, sizeof(resbuf));
593 	crsbuf.Pointer = NULL;
594 
595 	switch (link->possible_resources.Id) {
596 	case ACPI_RSTYPE_IRQ:
597 		resbuf.Id = ACPI_RSTYPE_IRQ;
598 		resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
599 
600 		/* structure copy other fields */
601 		resbuf.Data.Irq = link->possible_resources.Data.Irq;
602 		resbuf.Data.Irq.NumberOfInterrupts = 1;
603 		resbuf.Data.Irq.Interrupts[0] = irq;
604 		break;
605 	case ACPI_RSTYPE_EXT_IRQ:
606 		resbuf.Id = ACPI_RSTYPE_EXT_IRQ;
607 		resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_EXT_IRQ);
608 
609 		/* structure copy other fields */
610 		resbuf.Data.ExtendedIrq =
611 		    link->possible_resources.Data.ExtendedIrq;
612 		resbuf.Data.ExtendedIrq.NumberOfInterrupts = 1;
613 		resbuf.Data.ExtendedIrq.Interrupts[0] = irq;
614 		break;
615 	default:
616 		printf("acpi link set: %s resource is not an IRQ (%d)\n",
617 		    acpi_name(link->handle), link->possible_resources.Id);
618 		return_ACPI_STATUS (AE_TYPE);
619 	}
620 
621 	error = acpi_AppendBufferResource(&crsbuf, &resbuf);
622 	if (ACPI_FAILURE(error)) {
623 		printf("acpi link set: AppendBuffer failed for %s\n",
624 		    acpi_name(link->handle));
625 		return_ACPI_STATUS (error);
626 	}
627 	if (crsbuf.Pointer == NULL) {
628 		printf("acpi link set: AppendBuffer returned empty for %s\n",
629 		    acpi_name(link->handle));
630 		return_ACPI_STATUS (AE_NO_MEMORY);
631 	}
632 
633 	/* Make the new IRQ active via the link's _SRS method. */
634 	error = AcpiSetCurrentResources(link->handle, &crsbuf);
635 	if (ACPI_FAILURE(error)) {
636 		printf("acpi link set: _SRS failed for link %s - %s\n",
637 		    acpi_name(link->handle), AcpiFormatException(error));
638 		goto out;
639 	}
640 	link->flags |= ACPI_LINK_ROUTED;
641 	link->current_irq = 0;
642 
643 	/*
644 	 * Many systems always return invalid values for current settings
645 	 * (_CRS).  Since we can't trust the value returned, we have to
646 	 * assume we were successful.
647 	 */
648 	error = acpi_pci_link_get_current_irq(link, &link->current_irq);
649 	if (ACPI_FAILURE(error)) {
650 		if (bootverbose)
651 			printf("acpi link set: _CRS failed for link %s - %s\n",
652 			    acpi_name(link->handle),
653 			    AcpiFormatException(error));
654 		error = AE_OK;
655 	}
656 	if (link->current_irq != irq) {
657 		if (bootverbose)
658 			printf("acpi link set: curr irq %d != %d for %s\n",
659 			    link->current_irq, irq, acpi_name(link->handle));
660 		link->current_irq = irq;
661 	}
662 
663 out:
664 	if (crsbuf.Pointer)
665 		AcpiOsFree(crsbuf.Pointer);
666 	return_ACPI_STATUS (error);
667 }
668 
669 /*
670  * Auto arbitration for boot-disabled devices
671  */
672 
673 static void
674 acpi_pci_link_bootdisabled_dump(void)
675 {
676 	int			i;
677 	int			irq;
678 	struct acpi_pci_link_entry *link;
679 
680 	ACPI_SERIAL_ASSERT(pci_link);
681 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
682 		/* boot-disabled link only. */
683 		if (link->current_irq != 0)
684 			continue;
685 
686 		printf("%s (references %d, priority %d):\n",
687 		    acpi_name(link->handle), link->references, link->priority);
688 		printf("\tinterrupts:\t");
689 		for (i = 0; i < link->number_of_interrupts; i++) {
690 			irq = link->sorted_irq[i];
691 			printf("%6d", irq);
692 		}
693 		printf("\n");
694 		printf("\tpenalty:\t");
695 		for (i = 0; i < link->number_of_interrupts; i++) {
696 			irq = link->sorted_irq[i];
697 			printf("%6d", irq_penalty[irq]);
698 		}
699 		printf("\n");
700 	}
701 }
702 
703 /*
704  * Heuristics for choosing IRQs.  We start with some static penalties,
705  * update them based on what IRQs are currently in use, then sort the
706  * result.  This works ok but is not perfect.
707  *
708  * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI
709  * doesn't seem to offer a similar mechanism, so picking a good
710  * interrupt here is a difficult task.
711  */
712 static void
713 acpi_pci_link_init_irq_penalty(void)
714 {
715 
716 	bzero(irq_penalty, sizeof(irq_penalty));
717 
718 	/* 0, 1, 2, 8:  timer, keyboard, cascade, RTC */
719 	irq_penalty[0] = 100000;
720 	irq_penalty[1] = 100000;
721 	irq_penalty[2] = 100000;
722 	irq_penalty[8] = 100000;
723 
724 	/* 13, 14, 15:  npx, ATA controllers */
725 	irq_penalty[13] = 50000;
726 	irq_penalty[14] = 50000;
727 	irq_penalty[15] = 50000;
728 
729 	/* 3, 4, 6, 7, 12:  typically used by legacy hardware */
730 	irq_penalty[3] =   5000;
731 	irq_penalty[4] =   5000;
732 	irq_penalty[6] =   5000;
733 	irq_penalty[7] =   5000;
734 	irq_penalty[12] =  5000;
735 
736 	/* 5:  sometimes legacy sound cards */
737 	irq_penalty[5] =     50;
738 }
739 
740 static int
741 link_exclusive(ACPI_RESOURCE *res)
742 {
743 
744 	if (res == NULL ||
745 	    (res->Id != ACPI_RSTYPE_IRQ &&
746 	    res->Id != ACPI_RSTYPE_EXT_IRQ))
747 		return (FALSE);
748 
749 	if ((res->Id == ACPI_RSTYPE_IRQ &&
750 	    res->Data.Irq.SharedExclusive == ACPI_EXCLUSIVE) ||
751 	    (res->Id == ACPI_RSTYPE_EXT_IRQ &&
752 	    res->Data.ExtendedIrq.SharedExclusive == ACPI_EXCLUSIVE))
753 		return (TRUE);
754 
755 	return (FALSE);
756 }
757 
758 static void
759 acpi_pci_link_update_irq_penalty(device_t dev, int busno)
760 {
761 	int			i;
762 	int			irq;
763 	int			rid;
764 	struct resource		*res;
765 	struct acpi_prt_entry	*entry;
766 	struct acpi_pci_link_entry *link;
767 
768 	ACPI_SERIAL_ASSERT(pci_link);
769 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
770 		if (entry->busno != busno)
771 			continue;
772 
773 		/* Impossible? */
774 		link = entry->pci_link;
775 		if (link == NULL)
776 			continue;
777 
778 		/* Update penalties for all possible settings of this link. */
779 		for (i = 0; i < link->number_of_interrupts; i++) {
780 			/* give 10 for each possible IRQs. */
781 			irq = link->interrupts[i];
782 			irq_penalty[irq] += 10;
783 
784 			/* higher penalty if exclusive. */
785 			if (link_exclusive(&link->possible_resources))
786 				irq_penalty[irq] += 100;
787 
788 			/* XXX try to get this IRQ in non-sharable mode. */
789 			rid = 0;
790 			res = bus_alloc_resource(dev, SYS_RES_IRQ,
791 						 &rid, irq, irq, 1, 0);
792 			if (res != NULL) {
793 				bus_release_resource(dev, SYS_RES_IRQ,
794 				    rid, res);
795 			} else {
796 				/* this is in use, give 10. */
797 				irq_penalty[irq] += 10;
798 			}
799 		}
800 
801 		/* initialize `sorted' possible IRQs. */
802 		bcopy(link->interrupts, link->sorted_irq,
803 		    sizeof(link->sorted_irq));
804 	}
805 }
806 
807 static void
808 acpi_pci_link_set_bootdisabled_priority(void)
809 {
810 	int			sum_penalty;
811 	int			i;
812 	int			irq;
813 	struct acpi_pci_link_entry *link, *link_pri;
814 	TAILQ_HEAD(, acpi_pci_link_entry) sorted_list;
815 
816 	ACPI_SERIAL_ASSERT(pci_link);
817 
818 	/* reset priority for all links. */
819 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links)
820 		link->priority = 0;
821 
822 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
823 		/* If already routed, don't include in arbitration. */
824 		if (link->flags & ACPI_LINK_ROUTED) {
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) /
842 		    link->number_of_interrupts;
843 	}
844 
845 	/*
846 	 * Sort PCI links based on the priority.
847 	 * XXX Any other better ways rather than using work list?
848 	 */
849 	TAILQ_INIT(&sorted_list);
850 	while (!TAILQ_EMPTY(&acpi_pci_link_entries)) {
851 		link = TAILQ_FIRST(&acpi_pci_link_entries);
852 		/* find an entry which has the highest priority. */
853 		TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links)
854 			if (link->priority < link_pri->priority)
855 				link = link_pri;
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 
877 	ACPI_SERIAL_ASSERT(pci_link);
878 
879 	TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
880 		/* Ignore links that have been routed already. */
881 		if (link->flags & ACPI_LINK_ROUTED)
882 			continue;
883 
884 		/* sort IRQs based on their penalty descending. */
885 		for (i = 0; i < link->number_of_interrupts; i++) {
886 			irq1 = link->sorted_irq[i];
887 			for (j = i + 1; j < link->number_of_interrupts; j++) {
888 				irq2 = link->sorted_irq[j];
889 				if (irq_penalty[irq1] < irq_penalty[irq2]) {
890 					continue;
891 				}
892 				link->sorted_irq[i] = irq2;
893 				link->sorted_irq[j] = irq1;
894 				irq1 = irq2;
895 			}
896 		}
897 	}
898 
899 	if (bootverbose) {
900 		printf("ACPI PCI link arbitrated settings:\n");
901 		acpi_pci_link_bootdisabled_dump();
902 	}
903 }
904 
905 /*
906  * Public interface
907  */
908 
909 int
910 acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno)
911 {
912 	struct acpi_prt_entry	*entry;
913 	ACPI_PCI_ROUTING_TABLE	*prt;
914 	u_int8_t		*prtp;
915 	ACPI_STATUS		error;
916 	int			ret;
917 	static int		first_time = 1;
918 
919 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
920 
921 	if (acpi_disabled("pci_link"))
922 		return (0);
923 
924 	ret = -1;
925 	ACPI_SERIAL_BEGIN(pci_link);
926 	if (first_time) {
927 		TAILQ_INIT(&acpi_prt_entries);
928 		TAILQ_INIT(&acpi_pci_link_entries);
929 		acpi_pci_link_init_irq_penalty();
930 		first_time = 0;
931 	}
932 
933 	if (prtbuf == NULL)
934 		goto out;
935 
936 	prtp = prtbuf->Pointer;
937 	if (prtp == NULL)		/* didn't get routing table */
938 		goto out;
939 
940 	/* scan the PCI Routing Table */
941 	for (;;) {
942 		prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
943 
944 		if (prt->Length == 0)	/* end of table */
945 		    break;
946 
947 		error = acpi_pci_link_add_prt(dev, prt, busno);
948 		if (ACPI_FAILURE(error)) {
949 			ACPI_DEBUG_PRINT((ACPI_DB_WARN,
950 			    "couldn't add PCI interrupt link entry - %s\n",
951 			    AcpiFormatException(error)));
952 		}
953 
954 		/* skip to next entry */
955 		prtp += prt->Length;
956 	}
957 
958 	if (bootverbose) {
959 		printf("ACPI PCI link initial configuration:\n");
960 		TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
961 			if (entry->busno != busno)
962 				continue;
963 			acpi_pci_link_entry_dump(entry);
964 		}
965 	}
966 
967 	/* manual configuration. */
968 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
969 		int			irq;
970 		char			prthint[32];
971 
972 		if (entry->busno != busno)
973 			continue;
974 
975 		snprintf(prthint, sizeof(prthint),
976 		    "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno,
977 		    (int)((entry->prt.Address & 0xffff0000) >> 16),
978 		    (int)entry->prt.Pin);
979 
980 		if (getenv_int(prthint, &irq) == 0)
981 			continue;
982 
983 		if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) {
984 			error = acpi_pci_link_set_irq(entry->pci_link, irq);
985 			if (ACPI_FAILURE(error)) {
986 				ACPI_DEBUG_PRINT((ACPI_DB_WARN,
987 				    "couldn't set IRQ to link entry %s - %s\n",
988 				    acpi_name(entry->pci_link->handle),
989 				    AcpiFormatException(error)));
990 			}
991 			continue;
992 		}
993 
994 		/*
995 		 * Do auto arbitration for this device's PCI link
996 		 * if hint value 0 is specified.
997 		 */
998 		if (irq == 0)
999 			entry->pci_link->current_irq = 0;
1000 	}
1001 	ret = 0;
1002 
1003 out:
1004 	ACPI_SERIAL_END(pci_link);
1005 	return (ret);
1006 }
1007 
1008 int
1009 acpi_pci_link_resume(device_t dev)
1010 {
1011 	struct acpi_prt_entry	*entry;
1012 	struct acpi_pci_link_entry *link;
1013 	ACPI_STATUS		error;
1014 
1015 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1016 
1017 	if (acpi_disabled("pci_link"))
1018 		return (0);
1019 
1020 	/* Walk through all PRT entries for this PCI bridge. */
1021 	ACPI_SERIAL_BEGIN(pci_link);
1022 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1023 		if (entry->pcidev != dev || entry->pci_link == NULL)
1024 			continue;
1025 		link = entry->pci_link;
1026 
1027 		/* If it's not routed, skip re-programming. */
1028 		if ((link->flags & ACPI_LINK_ROUTED) == 0)
1029 			continue;
1030 		link->flags &= ~ACPI_LINK_ROUTED;
1031 
1032 		/* Program it to the same setting as before suspend. */
1033 		error = acpi_pci_link_set_irq(link, link->current_irq);
1034 		if (ACPI_FAILURE(error)) {
1035 			ACPI_DEBUG_PRINT((ACPI_DB_WARN,
1036 			    "couldn't set IRQ to link entry %s - %s\n",
1037 			    acpi_name(link->handle),
1038 			    AcpiFormatException(error)));
1039 		}
1040 	}
1041 	ACPI_SERIAL_END(pci_link);
1042 
1043 	return (0);
1044 }
1045 
1046 /*
1047  * Look up a PRT entry for the given device.  We match based on the slot
1048  * number (high word of Address) and pin number (note that ACPI uses 0
1049  * for INTA).
1050  *
1051  * Note that the low word of the Address field (function number) is
1052  * required by the specification to be 0xffff.  We don't risk checking
1053  * it here.
1054  */
1055 struct acpi_prt_entry *
1056 acpi_pci_find_prt(device_t pcibdev, device_t dev, int pin)
1057 {
1058 	struct acpi_prt_entry *entry;
1059 	ACPI_PCI_ROUTING_TABLE *prt;
1060 
1061 	ACPI_SERIAL_BEGIN(pci_link);
1062 	TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1063 		prt = &entry->prt;
1064 		if (entry->busno == pci_get_bus(dev) &&
1065 		    (prt->Address & 0xffff0000) >> 16 == pci_get_slot(dev) &&
1066 		    prt->Pin == pin)
1067 			break;
1068 	}
1069 	ACPI_SERIAL_END(pci_link);
1070 	return (entry);
1071 }
1072 
1073 /*
1074  * Perform the actual programming for this link.  We attempt to route an
1075  * IRQ, first the one set by the BIOS, and then a priority-sorted list.
1076  * Only do the programming once per link.
1077  */
1078 int
1079 acpi_pci_link_route(device_t dev, struct acpi_prt_entry *prt)
1080 {
1081 	struct acpi_pci_link_entry *link;
1082 	int busno, i, irq;
1083 	ACPI_RESOURCE crsres;
1084 	ACPI_STATUS status;
1085 
1086 	busno = pci_get_bus(dev);
1087 	link = prt->pci_link;
1088 	irq = PCI_INVALID_IRQ;
1089 	ACPI_SERIAL_BEGIN(pci_link);
1090 	if (link == NULL || link->number_of_interrupts == 0)
1091 		goto out;
1092 
1093 	/* If already routed, just return the current setting. */
1094 	if (link->flags & ACPI_LINK_ROUTED) {
1095 		irq = link->current_irq;
1096 		goto out;
1097 	}
1098 
1099 	/* Update all IRQ weights to determine our priority list. */
1100 	acpi_pci_link_update_irq_penalty(prt->pcidev, busno);
1101 	acpi_pci_link_set_bootdisabled_priority();
1102 	acpi_pci_link_fixup_bootdisabled_link();
1103 
1104 	/*
1105 	 * First, attempt to route the initial IRQ, if valid, since it was
1106 	 * the one set up by the BIOS.  If this fails, route according to
1107 	 * our priority-sorted list of IRQs.
1108 	 */
1109 	status = AE_NOT_FOUND;
1110 	irq = link->initial_irq;
1111 	if (irq)
1112 		status = acpi_pci_link_set_irq(link, irq);
1113 	for (i = 0; ACPI_FAILURE(status) && i < link->number_of_interrupts;
1114 	    i++) {
1115 		irq = link->sorted_irq[i];
1116 		status = acpi_pci_link_set_irq(link, irq);
1117 		if (ACPI_FAILURE(status)) {
1118 			device_printf(dev, "_SRS failed, irq %d via %s\n",
1119 			    irq, acpi_name(link->handle));
1120 		}
1121 	}
1122 	if (ACPI_FAILURE(status)) {
1123 		irq = PCI_INVALID_IRQ;
1124 		goto out;
1125 	}
1126 
1127 	/* Update the penalty now that there's another user for this IRQ. */
1128 	irq_penalty[irq] += 10 * link->references;
1129 
1130 	/* Configure trigger/polarity for the new IRQ. */
1131 	bcopy(&link->possible_resources, &crsres, sizeof(crsres));
1132 	if (crsres.Id == ACPI_RSTYPE_IRQ) {
1133 		crsres.Data.Irq.NumberOfInterrupts = 1;
1134 		crsres.Data.Irq.Interrupts[0] = irq;
1135 	} else {
1136 		crsres.Data.ExtendedIrq.NumberOfInterrupts = 1;
1137 		crsres.Data.ExtendedIrq.Interrupts[0] = irq;
1138 	}
1139 	acpi_config_intr(dev, &crsres);
1140 
1141 out:
1142 	ACPI_SERIAL_END(pci_link);
1143 	return (irq);
1144 }
1145