From e4116e931c45b07fab40946a0998c027ab77d922 Mon Sep 17 00:00:00 2001 From: Nate Lawson Date: Wed, 11 Aug 2004 14:52:50 +0000 Subject: [PATCH] Re-work ACPI PCI IRQ routing (_PRT, link devices). The old approach was incomplete in that the PRT routing was not aware of link programming. Fix this by doing all routing through the link devices. The new algorithm for setting up links is: 1. Read _CRS to get current setting. If invalid (not in _PRS), then set to 0. 2. Attempt to call _DIS on the link. If successful, mark the link as not routed. Otherwise, assume it still is. Then when a routing request occurs: 3. Update weights for all IRQs 4. Attempt to route the initial IRQ if valid 5. If that fails, walk through the sorted list, attempting to route IRQs. 6. Configure the trigger/polarity based on _PRS. Other changes: * Add acpi_pci_find_prt() to look up the PRT entry for a given device and acpi_pci_link_route() to select/route the best IRQ for it. * Remove duplicated code in acpi_pcib_route_interrupt() that picked the first IRQ from _PRS. * Remove unneeded arguments from acpi_pcib_resume() and friends. * Ignore _STA on link devices but report if it seems strange. * Add a prt_source handle to the PRT structure since the ACPI struct ACPI_PCI_ROUTING_TABLE uses a fixed-size entry for it. We'll need to dynamically size this object if we want to use it the same way ACPI-CA does. Null-terminate the source. Tested by: Luo Hong , Jeffrey Katcher Info from: jhb, Len Brown (Intel) --- sys/dev/acpica/acpi_pci_link.c | 443 +++++++++++++++++--------------- sys/dev/acpica/acpi_pcib.c | 316 ++++------------------- sys/dev/acpica/acpi_pcib_acpi.c | 8 +- sys/dev/acpica/acpi_pcib_pci.c | 5 +- sys/dev/acpica/acpi_pcibvar.h | 39 ++- 5 files changed, 331 insertions(+), 480 deletions(-) diff --git a/sys/dev/acpica/acpi_pci_link.c b/sys/dev/acpica/acpi_pci_link.c index 8b45621813f5..2b5c4ecba3ca 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 __FBSDID("$FreeBSD$"); @@ -39,43 +36,27 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#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 @@ acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno) 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 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); } - if (link->current_irq == 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; - } + /* 0, 1, 2, 8: timer, keyboard, cascade, RTC */ + irq_penalty[0] = 100000; + irq_penalty[1] = 100000; + irq_penalty[2] = 100000; + irq_penalty[8] = 100000; - /* 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; - } - } + /* 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; - error = acpi_pci_link_set_irq(entry->pci_link, - entry->pci_link->current_irq); + /* If it's not routed, skip re-programming. */ + if ((link->flags & ACPI_LINK_ROUTED) == 0) + continue; + link->flags &= ~ACPI_LINK_ROUTED; + + /* 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 93926899f8d5..893952f537d8 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); + 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; } - /* - * 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); + /* 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; } - 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"); - 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); - 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); - 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 (NumberOfInterrupts == 1 && Interrupts[0] != 0) { - 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]; - 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) { + if (link->number_of_interrupts == 0) { 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 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 (link->flags & ACPI_LINK_ROUTED) { + interrupt = link->current_irq; + if (bootverbose) + device_printf(pcib, "slot %d INT%c is already routed to irq %d\n", + pci_get_slot(dev), 'A' + pin, interrupt); + goto out; + } + 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 57fc73d6023a..d37db32b0c95 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 9df0cb807b14..8686cf7c253c 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 a7a5f206d7b0..2c59fae66d0b 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