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: releng/5.4/sys/dev/acpica/acpi_pci_link.c 137351 2004-11-07 20:24:06Z njl $");
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)(ACPI_ADR_PCI_SLOT(entry->prt.Address)),
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 * XXX Since we detect link devices via _PRT entries but run long
421 * after APIC mode has been enabled, we don't get a chance to
422 * disable links that will be unused (especially in APIC mode).
423 * Leaving them enabled can cause duplicate interrupts for some
424 * devices. The right fix is to probe links via their PNPID, so we
425 * see them no matter what the _PRT says.
426 */
427 if (ACPI_SUCCESS(AcpiEvaluateObject(handle, "_DIS", NULL, NULL))) {
428 link->current_irq = 0;
429 link->flags = ACPI_LINK_NONE;
430 } else
431 link->flags = ACPI_LINK_ROUTED;
432
433 /*
434 * If the initial IRQ is invalid (not in _PRS), set it to 0 and
435 * mark this link as not routed. We won't use it as the preferred
436 * interrupt later when we route.
437 */
438 if (!acpi_pci_link_is_valid_irq(link, link->initial_irq) &&
439 link->initial_irq != 0) {
440 printf("ACPI link %s has invalid initial irq %d, ignoring\n",
441 acpi_name(handle), link->initial_irq);
442 link->initial_irq = 0;
443 link->flags = ACPI_LINK_NONE;
444 }
445
446 link->references++;
447
448 TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
449 entry->pci_link = link;
450
451 error = AE_OK;
452 out:
453 if (buf.Pointer != NULL)
454 AcpiOsFree(buf.Pointer);
455 if (error != AE_OK && link != NULL)
456 AcpiOsFree(link);
457
458 return_ACPI_STATUS (error);
459 }
460
461 static ACPI_STATUS
462 acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno)
463 {
464 ACPI_HANDLE handle;
465 ACPI_STATUS error;
466 UINT32 sta;
467 struct acpi_prt_entry *entry;
468
469 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
470 ACPI_SERIAL_ASSERT(pci_link);
471
472 if (prt == NULL) {
473 device_printf(pcidev, "NULL PRT entry\n");
474 return_ACPI_STATUS (AE_BAD_PARAMETER);
475 }
476
477 /* Bail out if attempting to add a duplicate PRT entry. */
478 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
479 if (entry->busno == busno &&
480 entry->prt.Address == prt->Address &&
481 entry->prt.Pin == prt->Pin) {
482 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
483 "PRT entry already exists\n"));
484 return_ACPI_STATUS (AE_ALREADY_EXISTS);
485 }
486 }
487
488 /* Allocate and initialize our new PRT entry. */
489 entry = AcpiOsAllocate(sizeof(struct acpi_prt_entry));
490 if (entry == NULL) {
491 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "can't allocate memory\n"));
492 return_ACPI_STATUS (AE_NO_MEMORY);
493 }
494 bzero(entry, sizeof(struct acpi_prt_entry));
495
496 /*
497 * If the source link is NULL, then this IRQ is hardwired so skip
498 * initializing the link but still add it to the list.
499 */
500 if (prt->Source[0] != '\0') {
501 /* Get a handle for the link source. */
502 error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source,
503 &handle);
504 if (ACPI_FAILURE(error)) {
505 device_printf(pcidev, "get handle for %s - %s\n",
506 prt->Source, AcpiFormatException(error));
507 goto out;
508 }
509
510 error = acpi_pci_link_get_object_status(handle, &sta);
511 if (ACPI_FAILURE(error)) {
512 device_printf(pcidev, "can't get status for %s - %s\n",
513 acpi_name(handle), AcpiFormatException(error));
514 goto out;
515 }
516
517 /* Probe/initialize the link. */
518 error = acpi_pci_link_add_link(handle, entry);
519 if (ACPI_FAILURE(error)) {
520 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
521 "couldn't add _PRT entry to link %s - %s\n",
522 acpi_name(handle), AcpiFormatException(error)));
523 goto out;
524 }
525 }
526
527 entry->pcidev = pcidev;
528 entry->busno = busno;
529 bcopy(prt, &entry->prt, sizeof(entry->prt));
530
531 /*
532 * Make sure the Source value is null-terminated. It is really a
533 * variable-length string (with a fixed size in the struct) so when
534 * we copy the entire struct, we truncate the string. Instead of
535 * trying to make a variable-sized PRT object to handle the string,
536 * we store its handle in prt_source. Callers should use that to
537 * look up the link object.
538 */
539 entry->prt.Source[sizeof(prt->Source) - 1] = '\0';
540 entry->prt_source = handle;
541
542 TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links);
543 error = AE_OK;
544
545 out:
546 if (error != AE_OK && entry != NULL)
547 AcpiOsFree(entry);
548
549 return_ACPI_STATUS (error);
550 }
551
552 /*
553 * Look up the given interrupt in the list of possible settings for
554 * this link. We don't special-case the initial link setting. Some
555 * systems return current settings that are outside the list of valid
556 * settings so only allow choices explicitly specified in _PRS.
557 */
558 static int
559 acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
560 {
561 UINT8 i;
562
563 if (irq == 0)
564 return (FALSE);
565
566 /*
567 * Some systems have the initial irq set to the SCI but don't list
568 * it in the valid IRQs. Add a special case to allow routing to the
569 * SCI if the system really wants to. This is similar to how
570 * Windows often stacks all PCI IRQs on the SCI (and this is vital
571 * on some systems.)
572 */
573 if (irq == AcpiGbl_FADT->SciInt)
574 return (TRUE);
575
576 for (i = 0; i < link->number_of_interrupts; i++) {
577 if (link->interrupts[i] == irq)
578 return (TRUE);
579 }
580
581 return (FALSE);
582 }
583
584 static ACPI_STATUS
585 acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
586 {
587 ACPI_STATUS error;
588 ACPI_RESOURCE resbuf;
589 ACPI_BUFFER crsbuf;
590
591 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
592 ACPI_SERIAL_ASSERT(pci_link);
593
594 /* Make sure the new IRQ is valid before routing. */
595 if (!acpi_pci_link_is_valid_irq(link, irq)) {
596 printf("acpi link set: invalid IRQ %d on %s\n",
597 irq, acpi_name(link->handle));
598 return_ACPI_STATUS (AE_BAD_PARAMETER);
599 }
600
601 /* If this this link has already been routed, just return. */
602 if (link->flags & ACPI_LINK_ROUTED) {
603 printf("acpi link set: %s already routed to %d\n",
604 acpi_name(link->handle), link->current_irq);
605 return_ACPI_STATUS (AE_OK);
606 }
607
608 /* Set up the IRQ resource for _SRS. */
609 bzero(&resbuf, sizeof(resbuf));
610 crsbuf.Pointer = NULL;
611
612 switch (link->possible_resources.Id) {
613 case ACPI_RSTYPE_IRQ:
614 resbuf.Id = ACPI_RSTYPE_IRQ;
615 resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
616
617 /* structure copy other fields */
618 resbuf.Data.Irq = link->possible_resources.Data.Irq;
619 resbuf.Data.Irq.NumberOfInterrupts = 1;
620 resbuf.Data.Irq.Interrupts[0] = irq;
621 break;
622 case ACPI_RSTYPE_EXT_IRQ:
623 resbuf.Id = ACPI_RSTYPE_EXT_IRQ;
624 resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_EXT_IRQ);
625
626 /* structure copy other fields */
627 resbuf.Data.ExtendedIrq =
628 link->possible_resources.Data.ExtendedIrq;
629 resbuf.Data.ExtendedIrq.NumberOfInterrupts = 1;
630 resbuf.Data.ExtendedIrq.Interrupts[0] = irq;
631 break;
632 default:
633 printf("acpi link set: %s resource is not an IRQ (%d)\n",
634 acpi_name(link->handle), link->possible_resources.Id);
635 return_ACPI_STATUS (AE_TYPE);
636 }
637
638 error = acpi_AppendBufferResource(&crsbuf, &resbuf);
639 if (ACPI_FAILURE(error)) {
640 printf("acpi link set: AppendBuffer failed for %s\n",
641 acpi_name(link->handle));
642 return_ACPI_STATUS (error);
643 }
644 if (crsbuf.Pointer == NULL) {
645 printf("acpi link set: AppendBuffer returned empty for %s\n",
646 acpi_name(link->handle));
647 return_ACPI_STATUS (AE_NO_MEMORY);
648 }
649
650 /* Make the new IRQ active via the link's _SRS method. */
651 error = AcpiSetCurrentResources(link->handle, &crsbuf);
652 if (ACPI_FAILURE(error)) {
653 printf("acpi link set: _SRS failed for link %s - %s\n",
654 acpi_name(link->handle), AcpiFormatException(error));
655 goto out;
656 }
657 link->flags |= ACPI_LINK_ROUTED;
658 link->current_irq = 0;
659
660 /*
661 * Many systems always return invalid values for current settings
662 * (_CRS). Since we can't trust the value returned, we have to
663 * assume we were successful.
664 */
665 error = acpi_pci_link_get_current_irq(link, &link->current_irq);
666 if (ACPI_FAILURE(error)) {
667 if (bootverbose)
668 printf("acpi link set: _CRS failed for link %s - %s\n",
669 acpi_name(link->handle),
670 AcpiFormatException(error));
671 error = AE_OK;
672 }
673 if (link->current_irq != irq) {
674 if (bootverbose)
675 printf("acpi link set: curr irq %d != %d for %s\n",
676 link->current_irq, irq, acpi_name(link->handle));
677 link->current_irq = irq;
678 }
679
680 out:
681 if (crsbuf.Pointer)
682 AcpiOsFree(crsbuf.Pointer);
683 return_ACPI_STATUS (error);
684 }
685
686 /*
687 * Auto arbitration for boot-disabled devices
688 */
689
690 static void
691 acpi_pci_link_bootdisabled_dump(void)
692 {
693 int i;
694 int irq;
695 struct acpi_pci_link_entry *link;
696
697 ACPI_SERIAL_ASSERT(pci_link);
698 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
699 /* boot-disabled link only. */
700 if (link->current_irq != 0)
701 continue;
702
703 printf("%s (references %d, priority %d):\n",
704 acpi_name(link->handle), link->references, link->priority);
705 printf("\tinterrupts:\t");
706 for (i = 0; i < link->number_of_interrupts; i++) {
707 irq = link->sorted_irq[i];
708 printf("%6d", irq);
709 }
710 printf("\n");
711 printf("\tpenalty:\t");
712 for (i = 0; i < link->number_of_interrupts; i++) {
713 irq = link->sorted_irq[i];
714 printf("%6d", irq_penalty[irq]);
715 }
716 printf("\n");
717 }
718 }
719
720 /*
721 * Heuristics for choosing IRQs. We start with some static penalties,
722 * update them based on what IRQs are currently in use, then sort the
723 * result. This works ok but is not perfect.
724 *
725 * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI
726 * doesn't seem to offer a similar mechanism, so picking a good
727 * interrupt here is a difficult task.
728 */
729 static void
730 acpi_pci_link_init_irq_penalty(void)
731 {
732
733 bzero(irq_penalty, sizeof(irq_penalty));
734
735 /* 0, 1, 2, 8: timer, keyboard, cascade, RTC */
736 irq_penalty[0] = 100000;
737 irq_penalty[1] = 100000;
738 irq_penalty[2] = 100000;
739 irq_penalty[8] = 100000;
740
741 /* 13, 14, 15: npx, ATA controllers */
742 irq_penalty[13] = 50000;
743 irq_penalty[14] = 50000;
744 irq_penalty[15] = 50000;
745
746 /* 3, 4, 6, 7, 12: typically used by legacy hardware */
747 irq_penalty[3] = 5000;
748 irq_penalty[4] = 5000;
749 irq_penalty[6] = 5000;
750 irq_penalty[7] = 5000;
751 irq_penalty[12] = 5000;
752
753 /* 5: sometimes legacy sound cards */
754 irq_penalty[5] = 50;
755 }
756
757 static int
758 link_exclusive(ACPI_RESOURCE *res)
759 {
760
761 if (res == NULL ||
762 (res->Id != ACPI_RSTYPE_IRQ &&
763 res->Id != ACPI_RSTYPE_EXT_IRQ))
764 return (FALSE);
765
766 if ((res->Id == ACPI_RSTYPE_IRQ &&
767 res->Data.Irq.SharedExclusive == ACPI_EXCLUSIVE) ||
768 (res->Id == ACPI_RSTYPE_EXT_IRQ &&
769 res->Data.ExtendedIrq.SharedExclusive == ACPI_EXCLUSIVE))
770 return (TRUE);
771
772 return (FALSE);
773 }
774
775 static void
776 acpi_pci_link_update_irq_penalty(device_t dev, int busno)
777 {
778 int i;
779 int irq;
780 int rid;
781 struct resource *res;
782 struct acpi_prt_entry *entry;
783 struct acpi_pci_link_entry *link;
784
785 ACPI_SERIAL_ASSERT(pci_link);
786 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
787 if (entry->busno != busno)
788 continue;
789
790 /* Impossible? */
791 link = entry->pci_link;
792 if (link == NULL)
793 continue;
794
795 /* Update penalties for all possible settings of this link. */
796 for (i = 0; i < link->number_of_interrupts; i++) {
797 /* give 10 for each possible IRQs. */
798 irq = link->interrupts[i];
799 irq_penalty[irq] += 10;
800
801 /* higher penalty if exclusive. */
802 if (link_exclusive(&link->possible_resources))
803 irq_penalty[irq] += 100;
804
805 /* XXX try to get this IRQ in non-sharable mode. */
806 rid = 0;
807 res = bus_alloc_resource(dev, SYS_RES_IRQ,
808 &rid, irq, irq, 1, 0);
809 if (res != NULL) {
810 bus_release_resource(dev, SYS_RES_IRQ,
811 rid, res);
812 } else {
813 /* this is in use, give 10. */
814 irq_penalty[irq] += 10;
815 }
816 }
817
818 /* initialize `sorted' possible IRQs. */
819 bcopy(link->interrupts, link->sorted_irq,
820 sizeof(link->sorted_irq));
821 }
822 }
823
824 static void
825 acpi_pci_link_set_bootdisabled_priority(void)
826 {
827 int sum_penalty;
828 int i;
829 int irq;
830 struct acpi_pci_link_entry *link, *link_pri;
831 TAILQ_HEAD(, acpi_pci_link_entry) sorted_list;
832
833 ACPI_SERIAL_ASSERT(pci_link);
834
835 /* reset priority for all links. */
836 TAILQ_FOREACH(link, &acpi_pci_link_entries, links)
837 link->priority = 0;
838
839 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
840 /* If already routed, don't include in arbitration. */
841 if (link->flags & ACPI_LINK_ROUTED) {
842 link->priority = 0;
843 continue;
844 }
845
846 /*
847 * Calculate the priority for each boot-disabled links.
848 * o IRQ penalty indicates difficulty to use.
849 * o #references for devices indicates importance of the link.
850 * o #interrupts indicates flexibility of the link.
851 */
852 sum_penalty = 0;
853 for (i = 0; i < link->number_of_interrupts; i++) {
854 irq = link->interrupts[i];
855 sum_penalty += irq_penalty[irq];
856 }
857
858 link->priority = (sum_penalty * link->references) /
859 link->number_of_interrupts;
860 }
861
862 /*
863 * Sort PCI links based on the priority.
864 * XXX Any other better ways rather than using work list?
865 */
866 TAILQ_INIT(&sorted_list);
867 while (!TAILQ_EMPTY(&acpi_pci_link_entries)) {
868 link = TAILQ_FIRST(&acpi_pci_link_entries);
869 /* find an entry which has the highest priority. */
870 TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links)
871 if (link->priority < link_pri->priority)
872 link = link_pri;
873
874 /* move to work list. */
875 TAILQ_REMOVE(&acpi_pci_link_entries, link, links);
876 TAILQ_INSERT_TAIL(&sorted_list, link, links);
877 }
878
879 while (!TAILQ_EMPTY(&sorted_list)) {
880 /* move them back to the list, one by one... */
881 link = TAILQ_FIRST(&sorted_list);
882 TAILQ_REMOVE(&sorted_list, link, links);
883 TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
884 }
885 }
886
887 static void
888 acpi_pci_link_fixup_bootdisabled_link(void)
889 {
890 int i, j;
891 int irq1, irq2;
892 struct acpi_pci_link_entry *link;
893
894 ACPI_SERIAL_ASSERT(pci_link);
895
896 TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
897 /* Ignore links that have been routed already. */
898 if (link->flags & ACPI_LINK_ROUTED)
899 continue;
900
901 /* sort IRQs based on their penalty descending. */
902 for (i = 0; i < link->number_of_interrupts; i++) {
903 irq1 = link->sorted_irq[i];
904 for (j = i + 1; j < link->number_of_interrupts; j++) {
905 irq2 = link->sorted_irq[j];
906 if (irq_penalty[irq1] < irq_penalty[irq2]) {
907 continue;
908 }
909 link->sorted_irq[i] = irq2;
910 link->sorted_irq[j] = irq1;
911 irq1 = irq2;
912 }
913 }
914 }
915
916 if (bootverbose) {
917 printf("ACPI PCI link arbitrated settings:\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 int ret;
934 static int first_time = 1;
935
936 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
937
938 if (acpi_disabled("pci_link"))
939 return (0);
940
941 ret = -1;
942 ACPI_SERIAL_BEGIN(pci_link);
943 if (first_time) {
944 TAILQ_INIT(&acpi_prt_entries);
945 TAILQ_INIT(&acpi_pci_link_entries);
946 acpi_pci_link_init_irq_penalty();
947 first_time = 0;
948 }
949
950 if (prtbuf == NULL)
951 goto out;
952
953 prtp = prtbuf->Pointer;
954 if (prtp == NULL) /* didn't get routing table */
955 goto out;
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("ACPI PCI link initial configuration:\n");
977 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
978 if (entry->busno != busno)
979 continue;
980 acpi_pci_link_entry_dump(entry);
981 }
982 }
983
984 /* manual configuration. */
985 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
986 int irq;
987 char prthint[32];
988
989 if (entry->busno != busno)
990 continue;
991
992 snprintf(prthint, sizeof(prthint),
993 "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno,
994 (int)(ACPI_ADR_PCI_SLOT(entry->prt.Address)),
995 (int)entry->prt.Pin);
996
997 if (getenv_int(prthint, &irq) == 0)
998 continue;
999
1000 if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) {
1001 error = acpi_pci_link_set_irq(entry->pci_link, irq);
1002 if (ACPI_FAILURE(error)) {
1003 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
1004 "couldn't set IRQ to link entry %s - %s\n",
1005 acpi_name(entry->pci_link->handle),
1006 AcpiFormatException(error)));
1007 }
1008 continue;
1009 }
1010
1011 /*
1012 * Do auto arbitration for this device's PCI link
1013 * if hint value 0 is specified.
1014 */
1015 if (irq == 0)
1016 entry->pci_link->current_irq = 0;
1017 }
1018 ret = 0;
1019
1020 out:
1021 ACPI_SERIAL_END(pci_link);
1022 return (ret);
1023 }
1024
1025 int
1026 acpi_pci_link_resume(device_t dev)
1027 {
1028 struct acpi_prt_entry *entry;
1029 struct acpi_pci_link_entry *link;
1030 ACPI_STATUS error;
1031
1032 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1033
1034 if (acpi_disabled("pci_link"))
1035 return (0);
1036
1037 /* Walk through all PRT entries for this PCI bridge. */
1038 ACPI_SERIAL_BEGIN(pci_link);
1039 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1040 if (entry->pcidev != dev || entry->pci_link == NULL)
1041 continue;
1042 link = entry->pci_link;
1043
1044 /* If it's not routed, skip re-programming. */
1045 if ((link->flags & ACPI_LINK_ROUTED) == 0)
1046 continue;
1047 link->flags &= ~ACPI_LINK_ROUTED;
1048
1049 /* Program it to the same setting as before suspend. */
1050 error = acpi_pci_link_set_irq(link, link->current_irq);
1051 if (ACPI_FAILURE(error)) {
1052 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
1053 "couldn't set IRQ to link entry %s - %s\n",
1054 acpi_name(link->handle),
1055 AcpiFormatException(error)));
1056 }
1057 }
1058 ACPI_SERIAL_END(pci_link);
1059
1060 return (0);
1061 }
1062
1063 /*
1064 * Look up a PRT entry for the given device. We match based on the slot
1065 * number (high word of Address) and pin number (note that ACPI uses 0
1066 * for INTA).
1067 *
1068 * Note that the low word of the Address field (function number) is
1069 * required by the specification to be 0xffff. We don't risk checking
1070 * it here.
1071 */
1072 struct acpi_prt_entry *
1073 acpi_pci_find_prt(device_t pcibdev, device_t dev, int pin)
1074 {
1075 struct acpi_prt_entry *entry;
1076 ACPI_PCI_ROUTING_TABLE *prt;
1077
1078 ACPI_SERIAL_BEGIN(pci_link);
1079 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
1080 prt = &entry->prt;
1081 if (entry->busno == pci_get_bus(dev) &&
1082 ACPI_ADR_PCI_SLOT(prt->Address) == pci_get_slot(dev) &&
1083 prt->Pin == pin)
1084 break;
1085 }
1086 ACPI_SERIAL_END(pci_link);
1087 return (entry);
1088 }
1089
1090 /*
1091 * Perform the actual programming for this link. We attempt to route an
1092 * IRQ, first the one set by the BIOS, and then a priority-sorted list.
1093 * Only do the programming once per link.
1094 */
1095 int
1096 acpi_pci_link_route(device_t dev, struct acpi_prt_entry *prt)
1097 {
1098 struct acpi_pci_link_entry *link;
1099 int busno, i, irq;
1100 ACPI_RESOURCE crsres;
1101 ACPI_STATUS status;
1102
1103 busno = pci_get_bus(dev);
1104 link = prt->pci_link;
1105 irq = PCI_INVALID_IRQ;
1106 ACPI_SERIAL_BEGIN(pci_link);
1107 if (link == NULL || link->number_of_interrupts == 0)
1108 goto out;
1109
1110 /* If already routed, just return the current setting. */
1111 if (link->flags & ACPI_LINK_ROUTED) {
1112 irq = link->current_irq;
1113 goto out;
1114 }
1115
1116 /* Update all IRQ weights to determine our priority list. */
1117 acpi_pci_link_update_irq_penalty(prt->pcidev, busno);
1118 acpi_pci_link_set_bootdisabled_priority();
1119 acpi_pci_link_fixup_bootdisabled_link();
1120
1121 /*
1122 * First, attempt to route the initial IRQ, if valid, since it was
1123 * the one set up by the BIOS. If this fails, route according to
1124 * our priority-sorted list of IRQs.
1125 */
1126 status = AE_NOT_FOUND;
1127 irq = link->initial_irq;
1128 if (irq)
1129 status = acpi_pci_link_set_irq(link, irq);
1130 for (i = 0; ACPI_FAILURE(status) && i < link->number_of_interrupts;
1131 i++) {
1132 irq = link->sorted_irq[i];
1133 status = acpi_pci_link_set_irq(link, irq);
1134 if (ACPI_FAILURE(status)) {
1135 device_printf(dev, "_SRS failed, irq %d via %s\n",
1136 irq, acpi_name(link->handle));
1137 }
1138 }
1139 if (ACPI_FAILURE(status)) {
1140 irq = PCI_INVALID_IRQ;
1141 goto out;
1142 }
1143
1144 /* Update the penalty now that there's another user for this IRQ. */
1145 irq_penalty[irq] += 10 * link->references;
1146
1147 /* Configure trigger/polarity for the new IRQ. */
1148 bcopy(&link->possible_resources, &crsres, sizeof(crsres));
1149 if (crsres.Id == ACPI_RSTYPE_IRQ) {
1150 crsres.Data.Irq.NumberOfInterrupts = 1;
1151 crsres.Data.Irq.Interrupts[0] = irq;
1152 } else {
1153 crsres.Data.ExtendedIrq.NumberOfInterrupts = 1;
1154 crsres.Data.ExtendedIrq.Interrupts[0] = irq;
1155 }
1156 acpi_config_intr(dev, &crsres);
1157
1158 out:
1159 ACPI_SERIAL_END(pci_link);
1160 return (irq);
1161 }
Cache object: 88a0da09f6da0d77cb3cef172f6b97fe
|