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.3/sys/dev/acpica/acpi_pci_link.c 135500 2004-09-20 05:52:19Z 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)((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 }
Cache object: 7a37dc83835b1069e8072b9eef5b6f58
|