summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/acpica/acpi_pci_link.c445
-rw-r--r--sys/dev/acpica/acpi_pcib.c310
-rw-r--r--sys/dev/acpica/acpi_pcib_acpi.c8
-rw-r--r--sys/dev/acpica/acpi_pcib_pci.c5
-rw-r--r--sys/dev/acpica/acpi_pcibvar.h39
5 files changed, 329 insertions, 478 deletions
diff --git a/sys/dev/acpica/acpi_pci_link.c b/sys/dev/acpica/acpi_pci_link.c
index 8b45621..2b5c4ec 100644
--- a/sys/dev/acpica/acpi_pci_link.c
+++ b/sys/dev/acpica/acpi_pci_link.c
@@ -24,9 +24,6 @@
* SUCH DAMAGE.
*/
-/* XXX Uncomment this if you have new PCI IRQ problems starting 2004/8/5. */
-/* #define ACPI_OLD_PCI_LINK 1 */
-
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@@ -39,43 +36,27 @@ __FBSDID("$FreeBSD$");
#include <dev/acpica/acpivar.h>
#include <dev/acpica/acpi_pcibvar.h>
+#include <dev/pci/pcivar.h>
+#include "pcib_if.h"
+
/* Hooks for the ACPI CA debugging infrastructure. */
#define _COMPONENT ACPI_BUS
ACPI_MODULE_NAME("PCI_LINK")
-#define MAX_POSSIBLE_INTERRUPTS 16
-#define MAX_ISA_INTERRUPTS 16
-#define MAX_ACPI_INTERRUPTS 255
-
-struct acpi_pci_link_entry {
- TAILQ_ENTRY(acpi_pci_link_entry) links;
- ACPI_HANDLE handle;
- UINT8 current_irq;
- UINT8 initial_irq;
- ACPI_RESOURCE possible_resources;
- UINT8 number_of_interrupts;
- UINT8 interrupts[MAX_POSSIBLE_INTERRUPTS];
- UINT8 sorted_irq[MAX_POSSIBLE_INTERRUPTS];
- int references;
- int priority;
-};
-
TAILQ_HEAD(acpi_pci_link_entries, acpi_pci_link_entry);
static struct acpi_pci_link_entries acpi_pci_link_entries;
-struct acpi_prt_entry {
- TAILQ_ENTRY(acpi_prt_entry) links;
- device_t pcidev;
- int busno;
- ACPI_PCI_ROUTING_TABLE prt;
- struct acpi_pci_link_entry *pci_link;
-};
-
TAILQ_HEAD(acpi_prt_entries, acpi_prt_entry);
static struct acpi_prt_entries acpi_prt_entries;
static int irq_penalty[MAX_ACPI_INTERRUPTS];
+static int acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link,
+ UINT8 irq);
+static void acpi_pci_link_update_irq_penalty(device_t dev, int busno);
+static void acpi_pci_link_set_bootdisabled_priority(void);
+static void acpi_pci_link_fixup_bootdisabled_link(void);
+
/*
* PCI link object management
*/
@@ -137,27 +118,31 @@ acpi_pci_link_entry_dump(struct acpi_prt_entry *entry)
UINT8 i;
ACPI_RESOURCE_IRQ *Irq;
ACPI_RESOURCE_EXT_IRQ *ExtIrq;
+ struct acpi_pci_link_entry *link;
if (entry == NULL || entry->pci_link == NULL)
return;
+ link = entry->pci_link;
- printf("%s irq %3d: ", acpi_name(entry->pci_link->handle),
- entry->pci_link->current_irq);
+ printf("%s irq%c%2d: ", acpi_name(link->handle),
+ (link->flags & ACPI_LINK_ROUTED) ? '*' : ' ', link->current_irq);
printf("[");
- for (i = 0; i < entry->pci_link->number_of_interrupts; i++)
- printf("%3d", entry->pci_link->interrupts[i]);
- printf("] ");
+ if (link->number_of_interrupts)
+ printf("%2d", link->interrupts[0]);
+ for (i = 1; i < link->number_of_interrupts; i++)
+ printf("%3d", link->interrupts[i]);
+ printf("] %2d+ ", link->initial_irq);
- switch (entry->pci_link->possible_resources.Id) {
+ switch (link->possible_resources.Id) {
case ACPI_RSTYPE_IRQ:
- Irq = &entry->pci_link->possible_resources.Data.Irq;
+ Irq = &link->possible_resources.Data.Irq;
acpi_pci_link_dump_polarity(Irq->ActiveHighLow);
acpi_pci_link_dump_trigger(Irq->EdgeLevel);
acpi_pci_link_dump_sharemode(Irq->SharedExclusive);
break;
case ACPI_RSTYPE_EXT_IRQ:
- ExtIrq = &entry->pci_link->possible_resources.Data.ExtendedIrq;
+ ExtIrq = &link->possible_resources.Data.ExtendedIrq;
acpi_pci_link_dump_polarity(ExtIrq->ActiveHighLow);
acpi_pci_link_dump_trigger(ExtIrq->EdgeLevel);
acpi_pci_link_dump_sharemode(ExtIrq->SharedExclusive);
@@ -370,17 +355,34 @@ acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry)
buf.Length = ACPI_ALLOCATE_BUFFER;
bzero(link, sizeof(struct acpi_pci_link_entry));
-
link->handle = handle;
+ /*
+ * Get the IRQ configured at boot-time. If successful, set this
+ * as the initial IRQ.
+ */
error = acpi_pci_link_get_current_irq(link, &link->current_irq);
- if (ACPI_FAILURE(error)) {
+ if (ACPI_SUCCESS(error)) {
+ link->initial_irq = link->current_irq;
+ } else {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"couldn't get current IRQ from interrupt link %s - %s\n",
acpi_name(handle), AcpiFormatException(error)));
+ link->initial_irq = 0;
}
- link->initial_irq = link->current_irq;
+ /*
+ * Try to disable this link. If successful, set the current IRQ to
+ * zero and flags to indicate this link is not routed. If we can't
+ * run _DIS (i.e., the method doesn't exist), assume the initial
+ * IRQ was routed by the BIOS.
+ */
+ if (ACPI_SUCCESS(AcpiEvaluateObject(handle, "_DIS", NULL, NULL))) {
+ link->current_irq = 0;
+ link->flags = ACPI_LINK_NONE;
+ } else {
+ link->flags = ACPI_LINK_ROUTED;
+ }
error = AcpiGetPossibleResources(handle, &buf);
if (ACPI_FAILURE(error)) {
@@ -396,6 +398,7 @@ acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry)
goto out;
}
+ /* XXX This only handles one resource, ignoring SourceIndex. */
resources = (ACPI_RESOURCE *) buf.Pointer;
bcopy(resources, &link->possible_resources,
sizeof(link->possible_resources));
@@ -417,6 +420,19 @@ acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry)
goto out;
}
+ /*
+ * If the initial IRQ is invalid (not in _PRS), set it to 0 and
+ * mark this link as not routed. We won't use it as the preferred
+ * interrupt later when we route.
+ */
+ if (!acpi_pci_link_is_valid_irq(link, link->initial_irq) &&
+ link->initial_irq != 0) {
+ printf("ACPI link %s has invalid initial irq %d, ignoring\n",
+ acpi_name(handle), link->initial_irq);
+ link->initial_irq = 0;
+ link->flags = ACPI_LINK_NONE;
+ }
+
link->references++;
TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
@@ -467,17 +483,9 @@ acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno)
* PCI link status (_STA) is unreliable. Many systems return
* erroneous values so we ignore it.
*/
- if ((sta & (ACPI_STA_PRESENT | ACPI_STA_FUNCTIONAL)) == 0) {
-#ifndef ACPI_OLD_PCI_LINK
+ if ((sta & (ACPI_STA_PRESENT | ACPI_STA_FUNCTIONAL)) == 0)
device_printf(pcidev, "acpi PRT ignoring status for %s\n",
acpi_name(handle));
-#else
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
- "interrupt link is not functional - %s\n",
- acpi_name(handle)));
- return_ACPI_STATUS (AE_ERROR);
-#endif /* !ACPI_OLD_PCI_LINK */
- }
TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
if (entry->busno == busno &&
@@ -502,6 +510,17 @@ acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno)
entry->busno = busno;
bcopy(prt, &entry->prt, sizeof(entry->prt));
+ /*
+ * Make sure the Source value is null-terminated. It is really a
+ * variable-length string (with a fixed size in the struct) so when
+ * we copy the entire struct, we truncate the string. Instead of
+ * trying to make a variable-sized PRT object to handle the string,
+ * we store its handle in prt_source. Callers should use that to
+ * look up the link object.
+ */
+ entry->prt.Source[sizeof(prt->Source) - 1] = '\0';
+ entry->prt_source = handle;
+
error = acpi_pci_link_add_link(handle, entry);
if (ACPI_FAILURE(error)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
@@ -520,6 +539,12 @@ out:
return_ACPI_STATUS (error);
}
+/*
+ * Look up the given interrupt in the list of possible settings for
+ * this link. We don't special-case the initial link setting. Some
+ * systems return current settings that are outside the list of valid
+ * settings so only allow choices explicitly specified in _PRS.
+ */
static int
acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
{
@@ -528,28 +553,11 @@ acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
if (irq == 0)
return (FALSE);
-#ifndef ACPI_OLD_PCI_LINK
- /*
- * Look up the given interrupt in the list of possible settings for
- * this link. We don't special-case the initial link setting. Some
- * systems return current settings that are outside the list of valid
- * settings so only allow choices explicitly specified in _PRS.
- */
-#endif
for (i = 0; i < link->number_of_interrupts; i++) {
if (link->interrupts[i] == irq)
return (TRUE);
}
- /* allow initial IRQ as valid one. */
- if (link->initial_irq == irq)
-#ifndef ACPI_OLD_PCI_LINK
- printf("acpi link check: %d initial irq, %d irq to route\n",
- link->initial_irq, irq);
-#else
- return (TRUE);
-#endif
-
return (FALSE);
}
@@ -559,27 +567,24 @@ acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
ACPI_STATUS error;
ACPI_RESOURCE resbuf;
ACPI_BUFFER crsbuf;
- UINT32 sta;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ /* Make sure the new IRQ is valid before routing. */
if (!acpi_pci_link_is_valid_irq(link, irq)) {
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
- "couldn't set invalid IRQ %d - %s\n", irq,
- acpi_name(link->handle)));
+ printf("acpi link: can't set invalid IRQ %d on %s\n",
+ irq, acpi_name(link->handle));
return_ACPI_STATUS (AE_BAD_PARAMETER);
}
- error = acpi_pci_link_get_current_irq(link, &link->current_irq);
- if (ACPI_FAILURE(error)) {
- ACPI_DEBUG_PRINT((ACPI_DB_WARN,
- "couldn't get current IRQ from interrupt link %s - %s\n",
- acpi_name(link->handle), AcpiFormatException(error)));
- }
-
- if (link->current_irq == irq)
+ /* If this this link has already been routed, just return. */
+ if (link->flags & ACPI_LINK_ROUTED) {
+ printf("link %s already routed to %d\n",
+ acpi_name(link->handle), link->current_irq);
return_ACPI_STATUS (AE_OK);
+ }
+ /* Set up the IRQ resource for _SRS. */
bzero(&resbuf, sizeof(resbuf));
crsbuf.Pointer = NULL;
@@ -624,41 +629,16 @@ acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
return_ACPI_STATUS (AE_NO_MEMORY);
}
+ /* Make the new IRQ active via the link's _SRS method. */
error = AcpiSetCurrentResources(link->handle, &crsbuf);
if (ACPI_FAILURE(error)) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"couldn't set link device _SRS %s - %s\n",
acpi_name(link->handle), AcpiFormatException(error)));
- return_ACPI_STATUS (error);
+ goto out;
}
-
- AcpiOsFree(crsbuf.Pointer);
+ link->flags |= ACPI_LINK_ROUTED;
link->current_irq = 0;
- error = AE_OK;
-
- /*
- * PCI link status (_STA) is unreliable. Many systems return
- * erroneous values so we ignore it.
- */
- error = acpi_pci_link_get_object_status(link->handle, &sta);
- if (ACPI_FAILURE(error)) {
- ACPI_DEBUG_PRINT((ACPI_DB_WARN,
- "couldn't get object status %s - %s\n",
- acpi_name(link->handle), AcpiFormatException(error)));
- return_ACPI_STATUS (error);
- }
-
- if ((sta & ACPI_STA_ENABLED) == 0) {
-#ifndef ACPI_OLD_PCI_LINK
- printf("acpi link set: ignoring status for %s\n",
- acpi_name(link->handle));
-#else
- ACPI_DEBUG_PRINT((ACPI_DB_WARN,
- "interrupt link %s is disabled\n",
- acpi_name(link->handle)));
- return_ACPI_STATUS (AE_ERROR);
-#endif /* !ACPI_OLD_PCI_LINK */
- }
/*
* Many systems always return invalid values for current settings
@@ -670,26 +650,17 @@ acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"couldn't get current IRQ from interrupt link %s - %s\n",
acpi_name(link->handle), AcpiFormatException(error)));
- return_ACPI_STATUS (error);
+ goto out;
}
-
- if (link->current_irq == irq) {
- error = AE_OK;
- } else {
-#ifndef ACPI_OLD_PCI_LINK
+ if (link->current_irq != irq) {
printf("acpi link set: curr irq %d != %d for %s (ignoring)\n",
link->current_irq, irq, acpi_name(link->handle));
link->current_irq = irq;
- error = AE_OK;
-#else
- ACPI_DEBUG_PRINT((ACPI_DB_WARN,
- "couldn't set IRQ %d to PCI interrupt link %d - %s\n",
- irq, link->current_irq, acpi_name(link->handle)));
- link->current_irq = 0;
- error = AE_ERROR;
-#endif /* !ACPI_OLD_PCI_LINK */
}
+out:
+ if (crsbuf.Pointer)
+ AcpiOsFree(crsbuf.Pointer);
return_ACPI_STATUS (error);
}
@@ -709,49 +680,58 @@ acpi_pci_link_bootdisabled_dump(void)
if (link->current_irq != 0)
continue;
- printf("%s:\n", acpi_name(link->handle));
- printf(" interrupts: ");
+ printf("%s (references %d, priority %d):\n",
+ acpi_name(link->handle), link->references, link->priority);
+ printf("\tinterrupts:\t");
for (i = 0; i < link->number_of_interrupts; i++) {
irq = link->sorted_irq[i];
printf("%6d", irq);
}
printf("\n");
- printf(" penalty: ");
+ printf("\tpenalty:\t");
for (i = 0; i < link->number_of_interrupts; i++) {
irq = link->sorted_irq[i];
printf("%6d", irq_penalty[irq]);
}
printf("\n");
- printf(" references: %d\n", link->references);
- printf(" priority: %d\n", link->priority);
}
}
+/*
+ * Heuristics for choosing IRQs. We start with some static penalties,
+ * update them based on what IRQs are currently in use, then sort the
+ * result. This works ok but is not perfect.
+ *
+ * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI
+ * doesn't seem to offer a similar mechanism, so picking a good
+ * interrupt here is a difficult task.
+ */
static void
acpi_pci_link_init_irq_penalty(void)
{
- int irq;
bzero(irq_penalty, sizeof(irq_penalty));
- for (irq = 0; irq < MAX_ISA_INTERRUPTS; irq++) {
- /* 0, 1, 2, 8: timer, keyboard, cascade */
- if (irq == 0 || irq == 1 || irq == 2 || irq == 8) {
- irq_penalty[irq] = 100000;
- continue;
- }
-
- /* 13, 14, 15: npx, ATA controllers */
- if (irq == 13 || irq == 14 || irq == 15) {
- irq_penalty[irq] = 10000;
- continue;
- }
- /* 3,4,6,7,12: typicially used by legacy hardware */
- if (irq == 3 || irq == 4 || irq == 6 || irq == 7 || irq == 12) {
- irq_penalty[irq] = 1000;
- continue;
- }
- }
+ /* 0, 1, 2, 8: timer, keyboard, cascade, RTC */
+ irq_penalty[0] = 100000;
+ irq_penalty[1] = 100000;
+ irq_penalty[2] = 100000;
+ irq_penalty[8] = 100000;
+
+ /* 13, 14, 15: npx, ATA controllers */
+ irq_penalty[13] = 50000;
+ irq_penalty[14] = 50000;
+ irq_penalty[15] = 50000;
+
+ /* 3, 4, 6, 7, 12: typically used by legacy hardware */
+ irq_penalty[3] = 5000;
+ irq_penalty[4] = 5000;
+ irq_penalty[6] = 5000;
+ irq_penalty[7] = 5000;
+ irq_penalty[12] = 5000;
+
+ /* 5: sometimes legacy sound cards */
+ irq_penalty[5] = 50;
}
static int
@@ -761,15 +741,15 @@ link_exclusive(ACPI_RESOURCE *res)
if (res == NULL ||
(res->Id != ACPI_RSTYPE_IRQ &&
res->Id != ACPI_RSTYPE_EXT_IRQ))
- return (0);
+ return (FALSE);
if ((res->Id == ACPI_RSTYPE_IRQ &&
res->Data.Irq.SharedExclusive == ACPI_EXCLUSIVE) ||
(res->Id == ACPI_RSTYPE_EXT_IRQ &&
res->Data.ExtendedIrq.SharedExclusive == ACPI_EXCLUSIVE))
- return (1);
+ return (TRUE);
- return (0);
+ return (FALSE);
}
static void
@@ -791,13 +771,7 @@ acpi_pci_link_update_irq_penalty(device_t dev, int busno)
if (link == NULL)
continue;
- if (link->current_irq != 0) {
- /* not boot-disabled link, we will use this IRQ. */
- irq_penalty[link->current_irq] += 100;
- continue;
- }
-
- /* boot-disabled link */
+ /* Update penalties for all possible settings of this link. */
for (i = 0; i < link->number_of_interrupts; i++) {
/* give 10 for each possible IRQs. */
irq = link->interrupts[i];
@@ -815,8 +789,8 @@ acpi_pci_link_update_irq_penalty(device_t dev, int busno)
bus_release_resource(dev, SYS_RES_IRQ,
rid, res);
} else {
- /* this is in use, give 100. */
- irq_penalty[irq] += 100;
+ /* this is in use, give 10. */
+ irq_penalty[irq] += 10;
}
}
@@ -835,18 +809,13 @@ acpi_pci_link_set_bootdisabled_priority(void)
struct acpi_pci_link_entry *link, *link_pri;
TAILQ_HEAD(, acpi_pci_link_entry) sorted_list;
- if (bootverbose) {
- printf("ACPI PCI link before setting link priority:\n");
- acpi_pci_link_bootdisabled_dump();
- }
-
/* reset priority for all links. */
TAILQ_FOREACH(link, &acpi_pci_link_entries, links)
link->priority = 0;
TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
- /* not boot-disabled link, give no chance to be arbitrated. */
- if (link->current_irq != 0) {
+ /* If already routed, don't include in arbitration. */
+ if (link->flags & ACPI_LINK_ROUTED) {
link->priority = 0;
continue;
}
@@ -898,16 +867,10 @@ acpi_pci_link_fixup_bootdisabled_link(void)
int i, j;
int irq1, irq2;
struct acpi_pci_link_entry *link;
- ACPI_STATUS error;
-
- if (bootverbose) {
- printf("ACPI PCI link before fixup for boot-disabled links:\n");
- acpi_pci_link_bootdisabled_dump();
- }
TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
- /* ignore non boot-disabled links. */
- if (link->current_irq != 0)
+ /* Ignore links that have been routed already. */
+ if (link->flags & ACPI_LINK_ROUTED)
continue;
/* sort IRQs based on their penalty descending. */
@@ -923,21 +886,10 @@ acpi_pci_link_fixup_bootdisabled_link(void)
irq1 = irq2;
}
}
-
- /* try with lower penalty IRQ. */
- for (i = 0; i < link->number_of_interrupts; i++) {
- irq1 = link->sorted_irq[i];
- error = acpi_pci_link_set_irq(link, irq1);
- if (error == AE_OK) {
- /* OK, we use this. give another penalty. */
- irq_penalty[irq1] += 100 * link->references;
- break;
- }
- }
}
if (bootverbose) {
- printf("ACPI PCI link after fixup for boot-disabled links:\n");
+ printf("ACPI PCI link arbitrated settings:\n");
acpi_pci_link_bootdisabled_dump();
}
}
@@ -953,7 +905,7 @@ acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno)
ACPI_PCI_ROUTING_TABLE *prt;
u_int8_t *prtp;
ACPI_STATUS error;
- static int first_time =1;
+ static int first_time = 1;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
@@ -1036,27 +988,14 @@ acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno)
entry->pci_link->current_irq = 0;
}
- /* auto arbitration */
- acpi_pci_link_update_irq_penalty(dev, busno);
- acpi_pci_link_set_bootdisabled_priority();
- acpi_pci_link_fixup_bootdisabled_link();
-
- if (bootverbose) {
- printf("ACPI PCI link arbitrated configuration:\n");
- TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
- if (entry->busno != busno)
- continue;
- acpi_pci_link_entry_dump(entry);
- }
- }
-
return (0);
}
int
-acpi_pci_link_resume(device_t dev, ACPI_BUFFER *prtbuf, int busno)
+acpi_pci_link_resume(device_t dev)
{
struct acpi_prt_entry *entry;
+ struct acpi_pci_link_entry *link;
ACPI_STATUS error;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
@@ -1064,19 +1003,121 @@ acpi_pci_link_resume(device_t dev, ACPI_BUFFER *prtbuf, int busno)
if (acpi_disabled("pci_link"))
return (0);
+ /* Walk through all PRT entries for this PCI bridge. */
TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
- if (entry->pcidev != dev)
+ if (entry->pcidev == dev || entry->pci_link == NULL)
continue;
+ link = entry->pci_link;
+
+ /* If it's not routed, skip re-programming. */
+ if ((link->flags & ACPI_LINK_ROUTED) == 0)
+ continue;
+ link->flags &= ~ACPI_LINK_ROUTED;
- error = acpi_pci_link_set_irq(entry->pci_link,
- entry->pci_link->current_irq);
+ /* Program it to the same setting as before suspend. */
+ error = acpi_pci_link_set_irq(link, link->current_irq);
if (ACPI_FAILURE(error)) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN,
"couldn't set IRQ to link entry %s - %s\n",
- acpi_name(entry->pci_link->handle),
+ acpi_name(link->handle),
AcpiFormatException(error)));
}
}
return (0);
}
+
+/*
+ * Look up a PRT entry for the given device. We match based on the slot
+ * number (high word of Address) and pin number (note that ACPI uses 0
+ * for INTA).
+ *
+ * Note that the low word of the Address field (function number) is
+ * required by the specification to be 0xffff. We don't risk checking
+ * it here.
+ */
+struct acpi_prt_entry *
+acpi_pci_find_prt(device_t pcibdev, device_t dev, int pin)
+{
+ struct acpi_prt_entry *entry;
+ ACPI_PCI_ROUTING_TABLE *prt;
+
+ TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
+ prt = &entry->prt;
+ if ((prt->Address & 0xffff0000) >> 16 == pci_get_slot(dev) &&
+ prt->Pin == pin)
+ return (entry);
+ }
+ return (NULL);
+}
+
+/*
+ * Perform the actual programming for this link. We attempt to route an
+ * IRQ, first the one set by the BIOS, and then a priority-sorted list.
+ * Only do the programming once per link.
+ */
+int
+acpi_pci_link_route(device_t dev, struct acpi_prt_entry *prt)
+{
+ struct acpi_pci_link_entry *link;
+ int busno, i, irq;
+ ACPI_RESOURCE crsres;
+ ACPI_STATUS status;
+
+ busno = pci_get_bus(dev);
+ link = prt->pci_link;
+ irq = PCI_INVALID_IRQ;
+ if (link == NULL || link->number_of_interrupts == 0)
+ goto out;
+
+ /* If already routed, just return the current setting. */
+ if (link->flags & ACPI_LINK_ROUTED) {
+ irq = link->current_irq;
+ goto out;
+ }
+
+ /* Update all IRQ weights to determine our priority list. */
+ acpi_pci_link_update_irq_penalty(prt->pcidev, busno);
+ acpi_pci_link_set_bootdisabled_priority();
+ acpi_pci_link_fixup_bootdisabled_link();
+
+ /*
+ * First, attempt to route the initial IRQ, if valid, since it was
+ * the one set up by the BIOS. If this fails, route according to
+ * our priority-sorted list of IRQs.
+ */
+ status = AE_NOT_FOUND;
+ irq = link->initial_irq;
+ if (irq)
+ status = acpi_pci_link_set_irq(link, irq);
+ for (i = 0; ACPI_FAILURE(status) && i < link->number_of_interrupts;
+ i++) {
+ irq = link->sorted_irq[i];
+ status = acpi_pci_link_set_irq(link, irq);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "_SRS failed, irq %d via %s\n",
+ irq, acpi_name(link->handle));
+ }
+ }
+ if (ACPI_FAILURE(status)) {
+ irq = PCI_INVALID_IRQ;
+ goto out;
+ }
+
+ /* Update the penalty now that there's another user for this IRQ. */
+ irq_penalty[irq] += 10 * link->references;
+
+ /* Configure trigger/polarity for the new IRQ. */
+ bcopy(&link->possible_resources, &crsres, sizeof(crsres));
+ if (crsres.Id == ACPI_RSTYPE_IRQ) {
+ crsres.Data.Irq.NumberOfInterrupts = 1;
+ crsres.Data.Irq.Interrupts[0] = irq;
+ } else {
+ crsres.Data.ExtendedIrq.NumberOfInterrupts = 1;
+ crsres.Data.ExtendedIrq.Interrupts[0] = irq;
+ }
+ acpi_config_intr(dev, &crsres);
+
+out:
+ return (irq);
+}
diff --git a/sys/dev/acpica/acpi_pcib.c b/sys/dev/acpica/acpi_pcib.c
index 9392689..893952f 100644
--- a/sys/dev/acpica/acpi_pcib.c
+++ b/sys/dev/acpica/acpi_pcib.c
@@ -90,316 +90,100 @@ acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno)
}
int
-acpi_pcib_resume(device_t dev, ACPI_BUFFER *prt, int busno)
+acpi_pcib_resume(device_t dev)
{
- acpi_pci_link_resume(dev, prt, busno);
+ acpi_pci_link_resume(dev);
return (bus_generic_resume(dev));
}
/*
* Route an interrupt for a child of the bridge.
- *
- * XXX clean up error messages
- *
- * XXX this function is somewhat bulky
*/
int
-acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
- ACPI_BUFFER *prtbuf)
+acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin)
{
+ struct acpi_prt_entry *entry;
+ int i, interrupt;
+ struct acpi_pci_link_entry *link;
ACPI_PCI_ROUTING_TABLE *prt;
- ACPI_HANDLE lnkdev;
- ACPI_BUFFER crsbuf, prsbuf, buf;
- ACPI_RESOURCE *crsres, *prsres, resbuf;
- ACPI_DEVICE_INFO *devinfo;
- ACPI_STATUS status;
- UINT32 NumberOfInterrupts;
- UINT32 *Interrupts;
- u_int8_t *prtp;
- int interrupt;
- int i;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
-
- crsres = NULL;
- buf.Pointer = NULL;
- crsbuf.Pointer = NULL;
- prsbuf.Pointer = NULL;
+
interrupt = PCI_INVALID_IRQ;
/* ACPI numbers pins 0-3, not 1-4 like the BIOS. */
pin--;
- /* We failed to retrieve the routing table. */
- prtp = prtbuf->Pointer;
- if (prtp == NULL)
+ /* Look up the PRT entry for this device. */
+ entry = acpi_pci_find_prt(pcib, dev, pin);
+ if (entry == NULL)
goto out;
-
- /* Scan the table to look for this device. */
- for (;;) {
- prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
-
- /* We hit the end of the table. */
- if (prt->Length == 0)
- goto out;
-
- /*
- * Compare the slot number (high word of Address) and pin number
- * (note that ACPI uses 0 for INTA) to check for a match.
- *
- * Note that the low word of the Address field (function number)
- * is required by the specification to be 0xffff. We don't risk
- * checking it here.
- */
- if (((prt->Address & 0xffff0000) >> 16) == pci_get_slot(dev) &&
- prt->Pin == pin) {
- if (bootverbose)
- device_printf(pcib, "matched entry for %d.%d.INT%c (src %s)\n",
- pci_get_bus(dev), pci_get_slot(dev), 'A' + pin,
- prt->Source);
- break;
- }
-
- /* Skip to the next entry. */
- prtp += prt->Length;
- }
+ prt = &entry->prt;
+ link = entry->pci_link;
+ if (bootverbose)
+ device_printf(pcib, "matched entry for %d.%d.INT%c (src %s)\n",
+ pci_get_bus(dev), pci_get_slot(dev), 'A' + pin,
+ acpi_name(entry->prt_source));
/*
- * If source is empty/NULL, the source index is the global IRQ number.
+ * If source is empty/NULL, the source index is a global IRQ number
+ * and it's hard-wired so we're done.
*/
if (prt->Source == NULL || prt->Source[0] == '\0') {
if (bootverbose)
- device_printf(pcib, "device is hardwired to IRQ %d\n",
- prt->SourceIndex);
- interrupt = prt->SourceIndex;
- goto out;
- }
-
- /*
- * We have to find the source device (PCI interrupt link device).
- */
- if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) {
- device_printf(pcib, "couldn't find PCI interrupt link device %s\n",
- prt->Source);
- goto out;
- }
-
- /*
- * Verify that this is a PCI link device and that it's present.
- */
- buf.Length = ACPI_ALLOCATE_BUFFER;
- if (ACPI_FAILURE(AcpiGetObjectInfo(lnkdev, &buf))) {
- device_printf(pcib, "couldn't validate PCI interrupt link device %s\n",
- prt->Source);
- goto out;
- }
- devinfo = (ACPI_DEVICE_INFO *)buf.Pointer;
- if ((devinfo->Valid & ACPI_VALID_HID) == 0 ||
- strcmp("PNP0C0F", devinfo->HardwareId.Value) != 0) {
- device_printf(pcib, "PCI interrupt link %s has invalid _HID (%s)\n",
- prt->Source, devinfo->HardwareId.Value);
- goto out;
- }
- if ((devinfo->Valid & ACPI_VALID_STA) != 0 &&
- (devinfo->CurrentStatus & 0x9) != 0x9) {
- device_printf(pcib, "PCI interrupt link device %s not present\n",
- prt->Source);
- goto out;
- }
-
- /*
- * Get the current and possible resources for the interrupt link device.
- * If we fail to get the current resources, this is a fatal error.
- */
- crsbuf.Length = ACPI_ALLOCATE_BUFFER;
- if (ACPI_FAILURE(status = AcpiGetCurrentResources(lnkdev, &crsbuf))) {
- device_printf(pcib, "PCI interrupt link device _CRS failed - %s\n",
- AcpiFormatException(status));
- goto out;
- }
- prsbuf.Length = ACPI_ALLOCATE_BUFFER;
- if (ACPI_FAILURE(status = AcpiGetPossibleResources(lnkdev, &prsbuf))) {
- device_printf(pcib, "PCI interrupt link device _PRS failed - %s\n",
- AcpiFormatException(status));
- }
- ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._CRS\n",
- (long)crsbuf.Length, acpi_name(lnkdev)));
- ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._PRS\n",
- (long)prsbuf.Length, acpi_name(lnkdev)));
-
- /*
- * The interrupt may already be routed, so check _CRS first. We don't
- * check the 'decoding' bit in the _STA result, since there's nothing in
- * the spec that mandates it be set, however some BIOS' will set it if
- * the decode is active.
- *
- * The Source Index points to the particular resource entry we're
- * interested in.
- */
- if (ACPI_FAILURE(acpi_FindIndexedResource(&crsbuf, prt->SourceIndex,
- &crsres))) {
- device_printf(pcib, "_CRS buffer corrupt, cannot route interrupt\n");
+ device_printf(pcib, "slot %d INT%c hardwired to IRQ %d\n",
+ pci_get_slot(dev), 'A' + pin, prt->SourceIndex);
+ if (prt->SourceIndex)
+ interrupt = prt->SourceIndex;
+ else
+ device_printf(pcib, "error: invalid hard-wired IRQ of 0\n");
goto out;
}
- /* Type-check the resource we've found. */
- if (crsres->Id != ACPI_RSTYPE_IRQ && crsres->Id != ACPI_RSTYPE_EXT_IRQ) {
- device_printf(pcib, "_CRS resource entry has unsupported type %d\n",
- crsres->Id);
+ /* XXX Support for multiple resources must be added to the link code. */
+ if (prt->SourceIndex) {
+ device_printf(pcib, "src index %d not yet supported\n",
+ prt->SourceIndex);
goto out;
}
- /* Set variables based on resource type. */
- if (crsres->Id == ACPI_RSTYPE_IRQ) {
- NumberOfInterrupts = crsres->Data.Irq.NumberOfInterrupts;
- Interrupts = crsres->Data.Irq.Interrupts;
- } else {
- NumberOfInterrupts = crsres->Data.ExtendedIrq.NumberOfInterrupts;
- Interrupts = crsres->Data.ExtendedIrq.Interrupts;
- }
-
- /* If there's more than one interrupt, this is an error. */
- if (NumberOfInterrupts > 1) {
- device_printf(pcib, "device has too many interrupts (%d)\n",
- NumberOfInterrupts);
+ /* There has to be at least one interrupt available. */
+ if (link->number_of_interrupts == 0) {
+ device_printf(pcib, "device has no interrupts\n");
goto out;
}
/*
- * If there's only one interrupt, and it's not zero, then it's already
- * routed.
- *
- * Note that we could also check the 'decoding' bit in _STA, but can't
- * depend on it since it's not part of the spec.
- *
- * XXX check ASL examples to see if this is an acceptable set of tests
+ * If the current interrupt has been routed, we're done. This is the
+ * case when the BIOS initializes it and we didn't disable it.
*/
- if (NumberOfInterrupts == 1 && Interrupts[0] != 0) {
+ if (link->flags & ACPI_LINK_ROUTED) {
+ interrupt = link->current_irq;
if (bootverbose)
- device_printf(pcib, "slot %d INT%c is routed to irq %d\n",
- pci_get_slot(dev), 'A' + pin, Interrupts[0]);
- interrupt = Interrupts[0];
+ device_printf(pcib, "slot %d INT%c is already routed to irq %d\n",
+ pci_get_slot(dev), 'A' + pin, interrupt);
goto out;
}
-
- /*
- * There isn't an interrupt, so we have to look at _PRS to get one.
- * Get the set of allowed interrupts from the _PRS resource indexed
- * by SourceIndex.
- */
- if (prsbuf.Pointer == NULL) {
- device_printf(pcib, "no routed irq and no _PRS on irq link device\n");
- goto out;
- }
-
- /*
- * Search through the _PRS resources, looking for an IRQ or extended
- * IRQ resource. Skip dependent function resources for now. In the
- * future, we might use these for priority but this is good enough for
- * now until BIOS vendors actually mean something by using them.
- */
- prsres = NULL;
- for (i = prt->SourceIndex; prsres == NULL; i++) {
- if (ACPI_FAILURE(acpi_FindIndexedResource(&prsbuf, i, &prsres))) {
- device_printf(pcib, "_PRS lacks IRQ resource, routing failed\n");
- goto out;
- }
- switch (prsres->Id) {
- case ACPI_RSTYPE_IRQ:
- NumberOfInterrupts = prsres->Data.Irq.NumberOfInterrupts;
- Interrupts = prsres->Data.Irq.Interrupts;
- break;
- case ACPI_RSTYPE_EXT_IRQ:
- NumberOfInterrupts = prsres->Data.ExtendedIrq.NumberOfInterrupts;
- Interrupts = prsres->Data.ExtendedIrq.Interrupts;
- break;
- case ACPI_RSTYPE_START_DPF:
- prsres = NULL;
- continue;
- default:
- device_printf(pcib, "_PRS has invalid type %d\n", prsres->Id);
- goto out;
- }
- }
- /* There has to be at least one interrupt available. */
- if (NumberOfInterrupts < 1) {
- device_printf(pcib, "device has no interrupts\n");
- goto out;
- }
-
- /*
- * Pick an interrupt to use. Note that a more scientific approach than
- * just taking the first one available would be desirable.
- *
- * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI
- * doesn't seem to offer a similar mechanism, so picking a "good"
- * interrupt here is a difficult task.
- *
- * Build a resource buffer and pass it to AcpiSetCurrentResources to
- * route the new interrupt.
- */
if (bootverbose) {
device_printf(pcib, "possible interrupts:");
- for (i = 0; i < NumberOfInterrupts; i++)
- printf(" %d", Interrupts[i]);
+ for (i = 0; i < link->number_of_interrupts; i++)
+ printf("%3d", link->interrupts[i]);
printf("\n");
}
- /* This should never happen. */
- if (crsbuf.Pointer != NULL)
- AcpiOsFree(crsbuf.Pointer);
+ /*
+ * Perform the link routing. The link code will pick the best IRQ
+ * for this pin and configure it.
+ */
+ interrupt = acpi_pci_link_route(dev, entry);
- /* XXX Data.Irq and Data.ExtendedIrq are implicitly structure-copied. */
- crsbuf.Pointer = NULL;
- crsres = NULL;
- if (prsres->Id == ACPI_RSTYPE_IRQ) {
- resbuf.Id = ACPI_RSTYPE_IRQ;
- resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
- resbuf.Data.Irq = prsres->Data.Irq;
- resbuf.Data.Irq.NumberOfInterrupts = 1;
- resbuf.Data.Irq.Interrupts[0] = Interrupts[0];
- } else {
- resbuf.Id = ACPI_RSTYPE_EXT_IRQ;
- resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_EXT_IRQ);
- resbuf.Data.ExtendedIrq = prsres->Data.ExtendedIrq;
- resbuf.Data.ExtendedIrq.NumberOfInterrupts = 1;
- resbuf.Data.ExtendedIrq.Interrupts[0] = Interrupts[0];
- }
- if (ACPI_FAILURE(status = acpi_AppendBufferResource(&crsbuf, &resbuf))) {
- device_printf(pcib, "buf append failed for interrupt %d via %s - %s\n",
- Interrupts[0], acpi_name(lnkdev),
- AcpiFormatException(status));
- goto out;
- }
- /* XXX Figure out how this is happening when the append succeeds. */
- if (crsbuf.Pointer == NULL) {
- device_printf(pcib, "_CRS buf NULL after append?\n");
- goto out;
- }
- if (ACPI_FAILURE(status = AcpiSetCurrentResources(lnkdev, &crsbuf))) {
- device_printf(pcib, "_SRS failed for interrupt %d via %s - %s\n",
- Interrupts[0], acpi_name(lnkdev),
- AcpiFormatException(status));
- goto out;
- }
- crsres = &resbuf;
-
- /* Return the interrupt we just routed. */
- if (bootverbose)
+ if (bootverbose && PCI_INTERRUPT_VALID(interrupt))
device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n",
- pci_get_slot(dev), 'A' + pin, Interrupts[0], acpi_name(lnkdev));
- interrupt = Interrupts[0];
+ pci_get_slot(dev), 'A' + pin, interrupt,
+ acpi_name(entry->prt_source));
out:
- if (PCI_INTERRUPT_VALID(interrupt) && crsres != NULL)
- acpi_config_intr(dev, crsres);
- if (crsbuf.Pointer != NULL)
- AcpiOsFree(crsbuf.Pointer);
- if (prsbuf.Pointer != NULL)
- AcpiOsFree(prsbuf.Pointer);
- if (buf.Pointer != NULL)
- AcpiOsFree(buf.Pointer);
return_VALUE (interrupt);
}
diff --git a/sys/dev/acpica/acpi_pcib_acpi.c b/sys/dev/acpica/acpi_pcib_acpi.c
index 57fc73d..d37db32 100644
--- a/sys/dev/acpica/acpi_pcib_acpi.c
+++ b/sys/dev/acpica/acpi_pcib_acpi.c
@@ -234,9 +234,8 @@ acpi_pcib_acpi_attach(device_t dev)
static int
acpi_pcib_acpi_resume(device_t dev)
{
- struct acpi_hpcib_softc *sc = device_get_softc(dev);
- return (acpi_pcib_resume(dev, &sc->ap_prt, sc->ap_bus));
+ return (acpi_pcib_resume(dev));
}
/*
@@ -297,11 +296,8 @@ acpi_pcib_write_config(device_t dev, int bus, int slot, int func, int reg,
static int
acpi_pcib_acpi_route_interrupt(device_t pcib, device_t dev, int pin)
{
- struct acpi_hpcib_softc *sc;
- /* Find the bridge softc. */
- sc = device_get_softc(pcib);
- return (acpi_pcib_route_interrupt(pcib, dev, pin, &sc->ap_prt));
+ return (acpi_pcib_route_interrupt(pcib, dev, pin));
}
struct resource *
diff --git a/sys/dev/acpica/acpi_pcib_pci.c b/sys/dev/acpica/acpi_pcib_pci.c
index 9df0cb8..8686cf7 100644
--- a/sys/dev/acpica/acpi_pcib_pci.c
+++ b/sys/dev/acpica/acpi_pcib_pci.c
@@ -139,9 +139,8 @@ acpi_pcib_pci_attach(device_t dev)
static int
acpi_pcib_pci_resume(device_t dev)
{
- struct acpi_pcib_softc *sc = device_get_softc(dev);
- return (acpi_pcib_resume(dev, &sc->ap_prt, sc->ap_pcibsc.secbus));
+ return (acpi_pcib_resume(dev));
}
static int
@@ -171,5 +170,5 @@ acpi_pcib_pci_route_interrupt(device_t pcib, device_t dev, int pin)
if (sc->ap_prt.Pointer == NULL)
return (pcib_route_interrupt(pcib, dev, pin));
else
- return (acpi_pcib_route_interrupt(pcib, dev, pin, &sc->ap_prt));
+ return (acpi_pcib_route_interrupt(pcib, dev, pin));
}
diff --git a/sys/dev/acpica/acpi_pcibvar.h b/sys/dev/acpica/acpi_pcibvar.h
index a7a5f20..2c59fae 100644
--- a/sys/dev/acpica/acpi_pcibvar.h
+++ b/sys/dev/acpica/acpi_pcibvar.h
@@ -31,11 +31,42 @@
#define _ACPI_PCIBVAR_H_
int acpi_pcib_attach(device_t bus, ACPI_BUFFER *prt, int busno);
-int acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
- ACPI_BUFFER *ptrbuf);
-int acpi_pcib_resume(device_t bus, ACPI_BUFFER *prt, int busno);
+int acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin);
+int acpi_pcib_resume(device_t dev);
+
+#define MAX_POSSIBLE_INTERRUPTS 16
+#define MAX_ISA_INTERRUPTS 16
+#define MAX_ACPI_INTERRUPTS 255
+
+struct acpi_pci_link_entry {
+ TAILQ_ENTRY(acpi_pci_link_entry) links;
+ ACPI_HANDLE handle;
+ UINT8 current_irq;
+ UINT8 initial_irq;
+ ACPI_RESOURCE possible_resources;
+ UINT8 number_of_interrupts;
+ UINT8 interrupts[MAX_POSSIBLE_INTERRUPTS];
+ UINT8 sorted_irq[MAX_POSSIBLE_INTERRUPTS];
+ int references;
+ int priority;
+ int flags;
+#define ACPI_LINK_NONE 0
+#define ACPI_LINK_ROUTED (1 << 0)
+};
+
+struct acpi_prt_entry {
+ TAILQ_ENTRY(acpi_prt_entry) links;
+ device_t pcidev;
+ int busno;
+ ACPI_PCI_ROUTING_TABLE prt;
+ ACPI_HANDLE prt_source;
+ struct acpi_pci_link_entry *pci_link;
+};
int acpi_pci_link_config(device_t pcib, ACPI_BUFFER *prt, int busno);
-int acpi_pci_link_resume(device_t pcib, ACPI_BUFFER *prt, int busno);
+int acpi_pci_link_resume(device_t pcib);
+struct acpi_prt_entry *acpi_pci_find_prt(device_t pcibdev, device_t dev,
+ int pin);
+int acpi_pci_link_route(device_t dev, struct acpi_prt_entry *prt);
#endif
OpenPOWER on IntegriCloud