summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpica/acpi_pcib.c
diff options
context:
space:
mode:
authornjl <njl@FreeBSD.org>2004-08-11 14:52:50 +0000
committernjl <njl@FreeBSD.org>2004-08-11 14:52:50 +0000
commit41ff46537cc56d49f3be8a58ae58e860a104731e (patch)
tree4fd6cd3f4ebc6ed6d38e51dc54fdd81b2e7723a9 /sys/dev/acpica/acpi_pcib.c
parentf25be330cc4ca73242e080abbd5632df39cf6f55 (diff)
downloadFreeBSD-src-41ff46537cc56d49f3be8a58ae58e860a104731e.zip
FreeBSD-src-41ff46537cc56d49f3be8a58ae58e860a104731e.tar.gz
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 <luohong99_at_mails.tsinghua.edu.cn>, Jeffrey Katcher <jmkatcher_at_yahoo.com> Info from: jhb, Len Brown (Intel)
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