diff options
Diffstat (limited to 'sys/dev/acpica/acpi_pcib.c')
-rw-r--r-- | sys/dev/acpica/acpi_pcib.c | 310 |
1 files changed, 47 insertions, 263 deletions
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); } |