diff options
-rw-r--r-- | sys/dev/acpica/acpi_pci_link.c | 445 | ||||
-rw-r--r-- | sys/dev/acpica/acpi_pcib.c | 310 | ||||
-rw-r--r-- | sys/dev/acpica/acpi_pcib_acpi.c | 8 | ||||
-rw-r--r-- | sys/dev/acpica/acpi_pcib_pci.c | 5 | ||||
-rw-r--r-- | sys/dev/acpica/acpi_pcibvar.h | 39 |
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 |