Date: Mon, 09 Aug 2004 18:09:40 -0700 From: Nate Lawson <nate@root.org> To: current@freebsd.org Cc: Luo Hong <luohong99@mails.tsinghua.edu.cn> Subject: Re: PLEASE TEST: acpi pci irq routing Message-ID: <41182054.7090109@root.org> In-Reply-To: <291946042.22742@mails.tsinghua.edu.cn> References: <41131EEC.4050909@root.org> <291946042.22742@mails.tsinghua.edu.cn>
next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format. --------------090409010109040308080807 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Thanks for all the feedback to everyone who responded. I have finished reworking our PCI irq routing approach and the result is much simpler while retaining the existing weighting algorithm from acpi_pci_link.c (around since Oct. 2002). It removes the hack which causes the first IRQ to be chosen from possible IRQs if the BIOS hasn't routed an interrupt. The selection algorithm is to always use the BIOS irq, if valid, and then select from the weighted priority list if that fails. I've tested both approaches fully. Please test this patch to be sure all PCI devices continue (or begin) to function as expected. If something fails for you, please send me the dmesg from boot -v. Thanks, Nate --------------090409010109040308080807 Content-Type: text/plain; name="pci_link.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="pci_link.diff" Index: acpi_pci_link.c =================================================================== RCS file: /home/ncvs/src/sys/dev/acpica/acpi_pci_link.c,v retrieving revision 1.18 diff -u -r1.18 acpi_pci_link.c --- acpi_pci_link.c 6 Aug 2004 04:50:56 -0000 1.18 +++ acpi_pci_link.c 10 Aug 2004 00:56:52 -0000 @@ -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: src/sys/dev/acpica/acpi_pci_link.c,v 1.18 2004/08/06 04:50:56 njl Exp $"); @@ -39,43 +36,27 @@ #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 @@ 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 @@ 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 @@ 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 @@ 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 @@ * 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 @@ 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 @@ 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 @@ 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_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 @@ 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_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,55 @@ 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] = 10000; + irq_penalty[14] = 10000; + irq_penalty[15] = 10000; + + /* 3, 4, 6, 7, 12: typically used by legacy hardware */ + irq_penalty[3] = 1000; + irq_penalty[4] = 1000; + irq_penalty[6] = 1000; + irq_penalty[7] = 1000; + irq_penalty[12] = 1000; } static int @@ -761,15 +738,15 @@ 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 +768,7 @@ 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 +786,8 @@ 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 +806,13 @@ 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 +864,10 @@ 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 +883,10 @@ 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 +902,7 @@ 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 +985,14 @@ 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 +1000,103 @@ 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_STATUS status; + + busno = pci_get_bus(dev); + link = prt->pci_link; + if (link == NULL || link->number_of_interrupts == 0) + return (PCI_INVALID_IRQ); + + /* If already routed, just return the current setting. */ + if (link->flags & ACPI_LINK_ROUTED) + return (link->current_irq); + + /* 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)) + return (PCI_INVALID_IRQ); + + /* Update the penalty now that there's another user for this IRQ. */ + irq_penalty[irq] += 100 * link->references; + + return (irq); +} Index: acpi_pcib.c =================================================================== RCS file: /home/ncvs/src/sys/dev/acpica/acpi_pcib.c,v retrieving revision 1.46 diff -u -r1.46 acpi_pcib.c --- acpi_pcib.c 1 Jul 2004 07:46:27 -0000 1.46 +++ acpi_pcib.c 10 Aug 2004 00:04:33 -0000 @@ -90,316 +90,106 @@ } 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_RESOURCE *prsres; 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; + + prsres = 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) - 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; - } + /* Look up the PRT entry for this device. */ + entry = acpi_pci_find_prt(pcib, dev, pin); + if (entry == NULL) + return (AE_ERROR); + 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"); + prt->SourceIndex); + if (prt->SourceIndex) + interrupt = prt->SourceIndex; + else + device_printf(pcib, "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]; - 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"); + device_printf(pcib, "slot %d INT%c is already routed to irq %d\n", + pci_get_slot(dev), 'A' + pin, interrupt); 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); - - /* 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. */ + /* + * Perform the link routing. If successful, use the _PRS value for + * setting the trigger/polarity via acpi_config_intr() below. + */ + interrupt = acpi_pci_link_route(dev, entry); + if (interrupt) + prsres = &link->possible_resources; + if (bootverbose) 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); + if (PCI_INTERRUPT_VALID(interrupt) && prsres) + acpi_config_intr(dev, prsres); return_VALUE (interrupt); } Index: acpi_pcib_acpi.c =================================================================== RCS file: /home/ncvs/src/sys/dev/acpica/acpi_pcib_acpi.c,v retrieving revision 1.38 diff -u -r1.38 acpi_pcib_acpi.c --- acpi_pcib_acpi.c 4 Jul 2004 16:23:25 -0000 1.38 +++ acpi_pcib_acpi.c 7 Aug 2004 04:58:12 -0000 @@ -234,9 +234,8 @@ 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 @@ 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 * Index: acpi_pcib_pci.c =================================================================== RCS file: /home/ncvs/src/sys/dev/acpica/acpi_pcib_pci.c,v retrieving revision 1.9 diff -u -r1.9 acpi_pcib_pci.c --- acpi_pcib_pci.c 30 May 2004 20:08:23 -0000 1.9 +++ acpi_pcib_pci.c 7 Aug 2004 04:57:49 -0000 @@ -139,9 +139,8 @@ 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 @@ 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)); } Index: acpi_pcibvar.h =================================================================== RCS file: /home/ncvs/src/sys/dev/acpica/acpi_pcibvar.h,v retrieving revision 1.2 diff -u -r1.2 acpi_pcibvar.h --- acpi_pcibvar.h 5 Oct 2002 02:01:02 -0000 1.2 +++ acpi_pcibvar.h 10 Aug 2004 00:48:31 -0000 @@ -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 --------------090409010109040308080807--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?41182054.7090109>