summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpica
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2005-01-18 20:18:46 +0000
committerjhb <jhb@FreeBSD.org>2005-01-18 20:18:46 +0000
commit0307a381d7831dd80319bc6516965ca63471668b (patch)
treeab5858aa335dc392ec3c98e45449f4531e7f0e4c /sys/dev/acpica
parent6495462157d01b64d30e761bbfef7f90e26f3e50 (diff)
downloadFreeBSD-src-0307a381d7831dd80319bc6516965ca63471668b.zip
FreeBSD-src-0307a381d7831dd80319bc6516965ca63471668b.tar.gz
- Add support for link devices where _CRS just outright fails to execute.
For such devices, we require _PRS to exist and we warn if any of the resources in _PRS are not IRQ resources (since we'll have no way of knowing which of those resources to use without a working _CRS). When it does come time to set resources, we build up a resource buffer from scratch as we do for devices with _CRS that only have IRQ resources. - Fix a bug with setting extended IRQ resources where we set the IRQ value in the wrong resource structure meaning that whichever IRQ was listed in _PRS was used instead. This might fix some weird issues on certain boxes where IRQs > 16 don't seem to work when using ACPI. - Fix a bug with how we walked the resource buffer after _SRS to call config_intr() in that the 'end' variable was not properly updated, so we could either terminate the loop early or loop after the end of the buffer. Tested by: pjd
Diffstat (limited to 'sys/dev/acpica')
-rw-r--r--sys/dev/acpica/acpi_pci_link.c175
1 files changed, 147 insertions, 28 deletions
diff --git a/sys/dev/acpica/acpi_pci_link.c b/sys/dev/acpica/acpi_pci_link.c
index 22c6f27..b7b8b0a 100644
--- a/sys/dev/acpica/acpi_pci_link.c
+++ b/sys/dev/acpica/acpi_pci_link.c
@@ -88,7 +88,9 @@ struct link;
struct acpi_pci_link_softc {
int pl_num_links;
+ int pl_crs_bad;
struct link *pl_links;
+ device_t pl_dev;
};
struct link {
@@ -302,7 +304,13 @@ link_add_prs(ACPI_RESOURCE *res, void *context)
KASSERT(req->link_index < req->sc->pl_num_links,
("%s: array boundary violation", __func__));
link = &req->sc->pl_links[req->link_index];
+ if (link->l_res_index == -1) {
+ KASSERT(req->sc->pl_crs_bad,
+ ("res_index should be set"));
+ link->l_res_index = req->res_index;
+ }
req->link_index++;
+ req->res_index++;
/*
* Stash a copy of the resource for later use when doing
@@ -334,6 +342,14 @@ link_add_prs(ACPI_RESOURCE *res, void *context)
link->l_isa_irq = FALSE;
}
break;
+ default:
+ if (req->in_dpf == DPF_IGNORE)
+ break;
+ if (req->sc->pl_crs_bad)
+ device_printf(req->sc->pl_dev,
+ "Warning: possible resource %d will be lost during _SRS\n",
+ req->res_index);
+ req->res_index++;
}
return (AE_OK);
}
@@ -396,21 +412,35 @@ acpi_pci_link_attach(device_t dev)
int i;
sc = device_get_softc(dev);
+ sc->pl_dev = dev;
ACPI_SERIAL_BEGIN(pci_link);
/*
* Count the number of current resources so we know how big of
- * a link array to allocate.
+ * a link array to allocate. On some systems, _CRS is broken,
+ * so for those systems try to derive the count from _PRS instead.
*/
creq.in_dpf = DPF_OUTSIDE;
creq.count = 0;
status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
acpi_count_irq_resources, &creq);
- if (ACPI_FAILURE(status))
- return (ENXIO);
+ sc->pl_crs_bad = ACPI_FAILURE(status);
+ if (sc->pl_crs_bad) {
+ creq.in_dpf = DPF_OUTSIDE;
+ creq.count = 0;
+ status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
+ acpi_count_irq_resources, &creq);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev,
+ "Unable to parse _CRS or _PRS: %s\n",
+ AcpiFormatException(status));
+ ACPI_SERIAL_END(pci_link);
+ return (ENXIO);
+ }
+ }
+ sc->pl_num_links = creq.count;
if (creq.count == 0)
return (0);
- sc->pl_num_links = creq.count;
sc->pl_links = malloc(sizeof(struct link) * sc->pl_num_links,
M_PCI_LINK, M_WAITOK | M_ZERO);
@@ -420,22 +450,41 @@ acpi_pci_link_attach(device_t dev)
sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
sc->pl_links[i].l_sc = sc;
sc->pl_links[i].l_isa_irq = FALSE;
+ sc->pl_links[i].l_res_index = -1;
+ }
+
+ /* Try to read the current settings from _CRS if it is valid. */
+ if (!sc->pl_crs_bad) {
+ rreq.in_dpf = DPF_OUTSIDE;
+ rreq.link_index = 0;
+ rreq.res_index = 0;
+ rreq.sc = sc;
+ status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
+ link_add_crs, &rreq);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev, "Unable to parse _CRS: %s\n",
+ AcpiFormatException(status));
+ goto fail;
+ }
}
+
+ /*
+ * Try to read the possible settings from _PRS. Note that if the
+ * _CRS is toast, we depend on having a working _PRS. However, if
+ * _CRS works, then it is ok for _PRS to be missing.
+ */
rreq.in_dpf = DPF_OUTSIDE;
rreq.link_index = 0;
rreq.res_index = 0;
rreq.sc = sc;
- status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
- link_add_crs, &rreq);
- if (ACPI_FAILURE(status))
- goto fail;
- rreq.in_dpf = DPF_OUTSIDE;
- rreq.link_index = 0;
- rreq.res_index = 0;
status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
link_add_prs, &rreq);
- if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
+ if (ACPI_FAILURE(status) &&
+ (status != AE_NOT_FOUND || sc->pl_crs_bad)) {
+ device_printf(dev, "Unable to parse _PRS: %s\n",
+ AcpiFormatException(status));
goto fail;
+ }
if (bootverbose) {
device_printf(dev, "Links after initial probe:\n");
acpi_pci_link_dump(sc);
@@ -589,33 +638,31 @@ acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot,
}
static ACPI_STATUS
-acpi_pci_link_route_irqs(device_t dev)
+acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
{
- struct acpi_pci_link_softc *sc;
ACPI_RESOURCE *resource, *end, newres, *resptr;
- ACPI_BUFFER crsbuf, srsbuf;
+ ACPI_BUFFER crsbuf;
ACPI_STATUS status;
struct link *link;
int i, in_dpf;
/* Fetch the _CRS. */
ACPI_SERIAL_ASSERT(pci_link);
- sc = device_get_softc(dev);
crsbuf.Pointer = NULL;
crsbuf.Length = ACPI_ALLOCATE_BUFFER;
- status = AcpiGetCurrentResources(acpi_get_handle(dev), &crsbuf);
+ status = AcpiGetCurrentResources(acpi_get_handle(sc->pl_dev), &crsbuf);
if (ACPI_SUCCESS(status) && crsbuf.Pointer == NULL)
status = AE_NO_MEMORY;
if (ACPI_FAILURE(status)) {
if (bootverbose)
- device_printf(dev,
+ device_printf(sc->pl_dev,
"Unable to fetch current resources: %s\n",
AcpiFormatException(status));
return (status);
}
/* Fill in IRQ resources via link structures. */
- srsbuf.Pointer = NULL;
+ srsbuf->Pointer = NULL;
link = sc->pl_links;
i = 0;
in_dpf = DPF_OUTSIDE;
@@ -668,10 +715,10 @@ acpi_pci_link_route_irqs(device_t dev)
resptr = &newres;
resptr->Data.ExtendedIrq.NumberOfInterrupts = 1;
if (PCI_INTERRUPT_VALID(link->l_irq))
- resource->Data.ExtendedIrq.Interrupts[0] =
+ resptr->Data.ExtendedIrq.Interrupts[0] =
link->l_irq;
else
- resource->Data.ExtendedIrq.Interrupts[0] = 0;
+ resptr->Data.ExtendedIrq.Interrupts[0] = 0;
link++;
i++;
break;
@@ -679,13 +726,13 @@ acpi_pci_link_route_irqs(device_t dev)
resptr = resource;
}
if (resptr != NULL) {
- status = acpi_AppendBufferResource(&srsbuf, resptr);
+ status = acpi_AppendBufferResource(srsbuf, resptr);
if (ACPI_FAILURE(status)) {
- device_printf(dev,
- "Unable to build reousrces: %s\n",
+ device_printf(sc->pl_dev,
+ "Unable to build resources: %s\n",
AcpiFormatException(status));
- if (srsbuf.Pointer != NULL)
- AcpiOsFree(srsbuf.Pointer);
+ if (srsbuf->Pointer != NULL)
+ AcpiOsFree(srsbuf->Pointer);
AcpiOsFree(crsbuf.Pointer);
return (status);
}
@@ -696,17 +743,88 @@ acpi_pci_link_route_irqs(device_t dev)
if (resource >= end)
break;
}
+ AcpiOsFree(crsbuf.Pointer);
+ return (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc,
+ ACPI_BUFFER *srsbuf)
+{
+ ACPI_RESOURCE newres;
+ ACPI_STATUS status;
+ struct link *link;
+ int i;
+
+ /* Start off with an empty buffer. */
+ srsbuf->Pointer = NULL;
+ link = sc->pl_links;
+ for (i = 0; i < sc->pl_num_links; i++) {
+
+ /* Add a new IRQ resource from each link. */
+ link = &sc->pl_links[i];
+ newres = link->l_prs_template;
+ if (newres.Id == ACPI_RSTYPE_IRQ) {
+
+ /* Build an IRQ resource. */
+ newres.Data.Irq.NumberOfInterrupts = 1;
+ if (PCI_INTERRUPT_VALID(link->l_irq)) {
+ KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
+ ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
+ __func__, link->l_irq));
+ newres.Data.Irq.Interrupts[0] = link->l_irq;
+ } else
+ newres.Data.Irq.Interrupts[0] = 0;
+ } else {
+
+ /* Build an ExtIRQ resuorce. */
+ newres.Data.ExtendedIrq.NumberOfInterrupts = 1;
+ if (PCI_INTERRUPT_VALID(link->l_irq))
+ newres.Data.ExtendedIrq.Interrupts[0] =
+ link->l_irq;
+ else
+ newres.Data.ExtendedIrq.Interrupts[0] = 0;
+ }
+
+ /* Add the new resource to the end of the _SRS buffer. */
+ status = acpi_AppendBufferResource(srsbuf, &newres);
+ if (ACPI_FAILURE(status)) {
+ device_printf(sc->pl_dev,
+ "Unable to build resources: %s\n",
+ AcpiFormatException(status));
+ if (srsbuf->Pointer != NULL)
+ AcpiOsFree(srsbuf->Pointer);
+ return (status);
+ }
+ }
+ return (AE_OK);
+}
+
+static ACPI_STATUS
+acpi_pci_link_route_irqs(device_t dev)
+{
+ struct acpi_pci_link_softc *sc;
+ ACPI_RESOURCE *resource, *end;
+ ACPI_BUFFER srsbuf;
+ ACPI_STATUS status;
+ struct link *link;
+ int i;
+
+ ACPI_SERIAL_ASSERT(pci_link);
+ sc = device_get_softc(dev);
+ if (sc->pl_crs_bad)
+ status = acpi_pci_link_srs_from_links(sc, &srsbuf);
+ else
+ status = acpi_pci_link_srs_from_crs(sc, &srsbuf);
/* Write out new resources via _SRS. */
status = AcpiSetCurrentResources(acpi_get_handle(dev), &srsbuf);
if (ACPI_FAILURE(status)) {
device_printf(dev, "Unable to route IRQs: %s\n",
AcpiFormatException(status));
- AcpiOsFree(crsbuf.Pointer);
AcpiOsFree(srsbuf.Pointer);
return (status);
}
- AcpiOsFree(crsbuf.Pointer);
/*
* Perform acpi_config_intr() on each IRQ resource if it was just
@@ -715,6 +833,7 @@ acpi_pci_link_route_irqs(device_t dev)
link = sc->pl_links;
i = 0;
resource = (ACPI_RESOURCE *)srsbuf.Pointer;
+ end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length);
for (;;) {
if (resource->Id == ACPI_RSTYPE_END_TAG)
break;
OpenPOWER on IntegriCloud