summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpica/acpi_pcib.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/acpica/acpi_pcib.c')
-rw-r--r--sys/dev/acpica/acpi_pcib.c310
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);
}
OpenPOWER on IntegriCloud