summaryrefslogtreecommitdiffstats
path: root/sys/dev/acpica
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2004-12-01 21:05:02 +0000
committerjhb <jhb@FreeBSD.org>2004-12-01 21:05:02 +0000
commit931ffd7d22e27213113c91848a0420789d44af2b (patch)
tree61dac50a90ac0ad054c271f83b4a256c078015e2 /sys/dev/acpica
parent24f769137d86a63519d3303ea714b5270c8f9716 (diff)
downloadFreeBSD-src-931ffd7d22e27213113c91848a0420789d44af2b.zip
FreeBSD-src-931ffd7d22e27213113c91848a0420789d44af2b.tar.gz
- Do a better job of handling any Dependent Functions (aka DPFs) that appear
in the _PRS or _CRS of link devices. If faced with multiple DPFs in a _PRS, we just use the first one. We assume that if _CRS has DPF tags they only contain a single set since multiple DPFs wouldn't make any sense. In practice, the only DPFs I've seen so far for link devices are that the one IRQ resource is surrounded by a DPF tag pair for no apparent reason, and this should handle that case fine now. - Only allocate link structures for IRQ resources for link devices rather than allocating a link structure for every resource. Reviewed by: njl Tested by: phk
Diffstat (limited to 'sys/dev/acpica')
-rw-r--r--sys/dev/acpica/acpi_pci_link.c287
1 files changed, 218 insertions, 69 deletions
diff --git a/sys/dev/acpica/acpi_pci_link.c b/sys/dev/acpica/acpi_pci_link.c
index e6675f1..e0a4da7 100644
--- a/sys/dev/acpica/acpi_pci_link.c
+++ b/sys/dev/acpica/acpi_pci_link.c
@@ -57,8 +57,33 @@ ACPI_SERIAL_DECL(pci_link, "ACPI PCI link");
* An ACPI PCI link device may contain multiple links. Each link has its
* own ACPI resource. _PRT entries specify which link is being used via
* the Source Index.
+ *
+ * XXX: A note about Source Indices and DPFs: Currently we assume that
+ * the DPF start and end tags are not counted towards the index that
+ * Source Index corresponds to. Also, we assume that when DPFs are in use
+ * they various sets overlap in terms of Indices. Here's an example
+ * resource list indicating these assumptions:
+ *
+ * Resource Index
+ * -------- -----
+ * I/O Port 0
+ * Start DPF -
+ * IRQ 1
+ * MemIO 2
+ * Start DPF -
+ * IRQ 1
+ * MemIO 2
+ * End DPF -
+ * DMA Channel 3
+ *
+ * The XXX is because I'm not sure if this is a valid assumption to make.
*/
+/* States during DPF processing. */
+#define DPF_OUTSIDE 0
+#define DPF_FIRST 1
+#define DPF_IGNORE 2
+
struct link;
struct acpi_pci_link_softc {
@@ -80,9 +105,16 @@ struct link {
ACPI_RESOURCE l_prs_template;
};
+struct link_count_request {
+ int in_dpf;
+ int count;
+};
+
struct link_res_request {
struct acpi_pci_link_softc *sc;
- int count;
+ int in_dpf;
+ int res_index;
+ int link_index;
};
MALLOC_DEFINE(M_PCI_LINK, "PCI Link", "ACPI PCI Link structures");
@@ -133,13 +165,36 @@ acpi_pci_link_probe(device_t dev)
static ACPI_STATUS
acpi_count_irq_resources(ACPI_RESOURCE *res, void *context)
{
- int *count;
+ struct link_count_request *req;
- count = (int *)context;
+ req = (struct link_count_request *)context;
switch (res->Id) {
+ case ACPI_RSTYPE_START_DPF:
+ switch (req->in_dpf) {
+ case DPF_OUTSIDE:
+ /* We've started the first DPF. */
+ req->in_dpf = DPF_FIRST;
+ break;
+ case DPF_FIRST:
+ /* We've started the second DPF. */
+ req->in_dpf = DPF_IGNORE;
+ break;
+ }
+ break;
+ case ACPI_RSTYPE_END_DPF:
+ /* We are finished with DPF parsing. */
+ KASSERT(req->in_dpf != DPF_OUTSIDE,
+ ("%s: end dpf when not parsing a dpf", __func__));
+ req->in_dpf = DPF_OUTSIDE;
+ break;
case ACPI_RSTYPE_IRQ:
case ACPI_RSTYPE_EXT_IRQ:
- (*count)++;
+ /*
+ * Don't count resources if we are in a DPF set that we are
+ * ignoring.
+ */
+ if (req->in_dpf != DPF_IGNORE)
+ req->count++;
}
return (AE_OK);
}
@@ -153,12 +208,34 @@ link_add_crs(ACPI_RESOURCE *res, void *context)
ACPI_SERIAL_ASSERT(pci_link);
req = (struct link_res_request *)context;
switch (res->Id) {
+ case ACPI_RSTYPE_START_DPF:
+ switch (req->in_dpf) {
+ case DPF_OUTSIDE:
+ /* We've started the first DPF. */
+ req->in_dpf = DPF_FIRST;
+ break;
+ case DPF_FIRST:
+ /* We've started the second DPF. */
+ panic(
+ "%s: Multiple dependent functions within a current resource",
+ __func__);
+ break;
+ }
+ break;
+ case ACPI_RSTYPE_END_DPF:
+ /* We are finished with DPF parsing. */
+ KASSERT(req->in_dpf != DPF_OUTSIDE,
+ ("%s: end dpf when not parsing a dpf", __func__));
+ req->in_dpf = DPF_OUTSIDE;
+ break;
case ACPI_RSTYPE_IRQ:
case ACPI_RSTYPE_EXT_IRQ:
- KASSERT(req->count < req->sc->pl_num_links,
- ("link_add_crs: array boundary violation"));
- link = &req->sc->pl_links[req->count];
- req->count++;
+ KASSERT(req->link_index < req->sc->pl_num_links,
+ ("%s: array boundary violation", __func__));
+ link = &req->sc->pl_links[req->link_index];
+ link->l_res_index = req->res_index;
+ req->link_index++;
+ req->res_index++;
if (res->Id == ACPI_RSTYPE_IRQ) {
if (res->Data.Irq.NumberOfInterrupts > 0) {
KASSERT(res->Data.Irq.NumberOfInterrupts == 1,
@@ -177,6 +254,8 @@ link_add_crs(ACPI_RESOURCE *res, void *context)
if (link->l_irq == 0)
link->l_irq = PCI_INVALID_IRQ;
break;
+ default:
+ req->res_index++;
}
return (AE_OK);
}
@@ -195,12 +274,37 @@ link_add_prs(ACPI_RESOURCE *res, void *context)
ACPI_SERIAL_ASSERT(pci_link);
req = (struct link_res_request *)context;
switch (res->Id) {
+ case ACPI_RSTYPE_START_DPF:
+ switch (req->in_dpf) {
+ case DPF_OUTSIDE:
+ /* We've started the first DPF. */
+ req->in_dpf = DPF_FIRST;
+ break;
+ case DPF_FIRST:
+ /* We've started the second DPF. */
+ req->in_dpf = DPF_IGNORE;
+ break;
+ }
+ break;
+ case ACPI_RSTYPE_END_DPF:
+ /* We are finished with DPF parsing. */
+ KASSERT(req->in_dpf != DPF_OUTSIDE,
+ ("%s: end dpf when not parsing a dpf", __func__));
+ req->in_dpf = DPF_OUTSIDE;
+ break;
case ACPI_RSTYPE_IRQ:
case ACPI_RSTYPE_EXT_IRQ:
- KASSERT(req->count < req->sc->pl_num_links,
- ("link_add_prs: array boundary violation"));
- link = &req->sc->pl_links[req->count];
- req->count++;
+ /*
+ * Don't parse resources if we are in a DPF set that we are
+ * ignoring.
+ */
+ if (req->in_dpf == DPF_IGNORE)
+ break;
+
+ KASSERT(req->link_index < req->sc->pl_num_links,
+ ("%s: array boundary violation", __func__));
+ link = &req->sc->pl_links[req->link_index];
+ req->link_index++;
/*
* Stash a copy of the resource for later use when doing
@@ -288,10 +392,10 @@ static int
acpi_pci_link_attach(device_t dev)
{
struct acpi_pci_link_softc *sc;
- struct link_res_request req;
+ struct link_count_request creq;
+ struct link_res_request rreq;
ACPI_STATUS status;
int i;
- int prslinks;
sc = device_get_softc(dev);
ACPI_SERIAL_BEGIN(pci_link);
@@ -300,25 +404,15 @@ acpi_pci_link_attach(device_t dev)
* Count the number of current resources so we know how big of
* a link array to allocate.
*/
+ creq.in_dpf = DPF_OUTSIDE;
+ creq.count = 0;
status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
- acpi_count_irq_resources, &sc->pl_num_links);
+ acpi_count_irq_resources, &creq);
if (ACPI_FAILURE(status))
- return (ENXIO);
- if (sc->pl_num_links == 0)
+ return (ENXIO);
+ if (creq.count == 0)
return (0);
-
- /*
- * Try to make the number of resources sufficiently large
- * for traversal of both _PRS and _CRS.
- *
- * XXX Temporary fix for out-of-bounds access in prs_add_links().
- * We really need to handle these in separate arrays. -- njl
- */
- prslinks = 0;
- status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
- acpi_count_irq_resources, &prslinks);
- if (prslinks > sc->pl_num_links)
- sc->pl_num_links = prslinks;
+ sc->pl_num_links = creq.count;
sc->pl_links = malloc(sizeof(struct link) * sc->pl_num_links,
M_PCI_LINK, M_WAITOK | M_ZERO);
@@ -326,19 +420,22 @@ acpi_pci_link_attach(device_t dev)
for (i = 0; i < sc->pl_num_links; i++) {
sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
- sc->pl_links[i].l_res_index = i;
sc->pl_links[i].l_sc = sc;
sc->pl_links[i].l_isa_irq = 0;
}
- req.count = 0;
- req.sc = sc;
+ 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, &req);
+ link_add_crs, &rreq);
if (ACPI_FAILURE(status))
goto fail;
- req.count = 0;
+ rreq.in_dpf = DPF_OUTSIDE;
+ rreq.link_index = 0;
+ rreq.res_index = 0;
status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
- link_add_prs, &req);
+ link_add_prs, &rreq);
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
goto fail;
if (bootverbose) {
@@ -432,20 +529,36 @@ acpi_pci_link_search_irq(int bus, int device, int pin)
return (PCI_INVALID_IRQ);
}
+/*
+ * Find the link structure that corresponds to the resource index passed in
+ * via 'source_index'.
+ */
+static struct link *
+acpi_pci_link_lookup(device_t dev, int source_index)
+{
+ struct acpi_pci_link_softc *sc;
+ int i;
+
+ ACPI_SERIAL_ASSERT(pci_link);
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->pl_num_links; i++)
+ if (sc->pl_links[i].l_res_index == source_index)
+ return (&sc->pl_links[i]);
+ return (NULL);
+}
+
void
acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot,
int pin)
{
- struct acpi_pci_link_softc *sc;
struct link *link;
uint8_t bios_irq;
/* Bump the reference count. */
ACPI_SERIAL_BEGIN(pci_link);
- sc = device_get_softc(dev);
- KASSERT(index >= 0 && index < sc->pl_num_links,
- ("%s: invalid index %d", __func__, index));
- link = &sc->pl_links[index];
+ link = acpi_pci_link_lookup(dev, index);
+ if (link == NULL)
+ panic("%s: apparently invalid index %d", __func__, index);
link->l_references++;
if (link->l_routed)
pci_link_interrupt_weights[link->l_irq]++;
@@ -486,7 +599,7 @@ acpi_pci_link_route_irqs(device_t dev)
ACPI_BUFFER crsbuf, srsbuf;
ACPI_STATUS status;
struct link *link;
- int i;
+ int i, in_dpf;
/* Fetch the _CRS. */
ACPI_SERIAL_ASSERT(pci_link);
@@ -508,20 +621,48 @@ acpi_pci_link_route_irqs(device_t dev)
srsbuf.Pointer = NULL;
link = sc->pl_links;
i = 0;
+ in_dpf = DPF_OUTSIDE;
resource = (ACPI_RESOURCE *)crsbuf.Pointer;
end = (ACPI_RESOURCE *)((char *)crsbuf.Pointer + crsbuf.Length);
for (;;) {
switch (resource->Id) {
+ case ACPI_RSTYPE_START_DPF:
+ switch (in_dpf) {
+ case DPF_OUTSIDE:
+ /* We've started the first DPF. */
+ in_dpf = DPF_FIRST;
+ break;
+ case DPF_FIRST:
+ /* We've started the second DPF. */
+ panic(
+ "%s: Multiple dependent functions within a current resource",
+ __func__);
+ break;
+ }
+ resptr = NULL;
+ break;
+ case ACPI_RSTYPE_END_DPF:
+ /* We are finished with DPF parsing. */
+ KASSERT(in_dpf != DPF_OUTSIDE,
+ ("%s: end dpf when not parsing a dpf", __func__));
+ in_dpf = DPF_OUTSIDE;
+ resptr = NULL;
+ break;
case ACPI_RSTYPE_IRQ:
MPASS(i < sc->pl_num_links);
MPASS(link->l_prs_template.Id == ACPI_RSTYPE_IRQ);
newres = link->l_prs_template;
resptr = &newres;
resptr->Data.Irq.NumberOfInterrupts = 1;
- if (PCI_INTERRUPT_VALID(link->l_irq))
+ 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));
resptr->Data.Irq.Interrupts[0] = link->l_irq;
- else
+ } else
resptr->Data.Irq.Interrupts[0] = 0;
+ link++;
+ i++;
break;
case ACPI_RSTYPE_EXT_IRQ:
MPASS(i < sc->pl_num_links);
@@ -534,24 +675,27 @@ acpi_pci_link_route_irqs(device_t dev)
link->l_irq;
else
resource->Data.ExtendedIrq.Interrupts[0] = 0;
+ link++;
+ i++;
break;
default:
resptr = resource;
}
- status = acpi_AppendBufferResource(&srsbuf, resptr);
- if (ACPI_FAILURE(status)) {
- device_printf(dev, "Unable to build reousrces: %s\n",
- AcpiFormatException(status));
- if (srsbuf.Pointer != NULL)
- AcpiOsFree(srsbuf.Pointer);
- AcpiOsFree(crsbuf.Pointer);
- return (status);
+ if (resptr != NULL) {
+ status = acpi_AppendBufferResource(&srsbuf, resptr);
+ if (ACPI_FAILURE(status)) {
+ device_printf(dev,
+ "Unable to build reousrces: %s\n",
+ AcpiFormatException(status));
+ if (srsbuf.Pointer != NULL)
+ AcpiOsFree(srsbuf.Pointer);
+ AcpiOsFree(crsbuf.Pointer);
+ return (status);
+ }
}
if (resource->Id == ACPI_RSTYPE_END_TAG)
break;
resource = ACPI_NEXT_RESOURCE(resource);
- link++;
- i++;
if (resource >= end)
break;
}
@@ -577,21 +721,28 @@ acpi_pci_link_route_irqs(device_t dev)
for (;;) {
if (resource->Id == ACPI_RSTYPE_END_TAG)
break;
- MPASS(i < sc->pl_num_links);
- if (link->l_routed || !PCI_INTERRUPT_VALID(link->l_irq))
- continue;
switch (resource->Id) {
case ACPI_RSTYPE_IRQ:
case ACPI_RSTYPE_EXT_IRQ:
- link->l_routed = 1;
- acpi_config_intr(dev, resource);
- pci_link_interrupt_weights[link->l_irq] +=
- link->l_references;
+ MPASS(i < sc->pl_num_links);
+
+ /*
+ * Only configure the interrupt and update the
+ * weights if this link has a valid IRQ and was
+ * previously unrouted.
+ */
+ if (!link->l_routed &&
+ PCI_INTERRUPT_VALID(link->l_irq)) {
+ link->l_routed = 1;
+ acpi_config_intr(dev, resource);
+ pci_link_interrupt_weights[link->l_irq] +=
+ link->l_references;
+ }
+ link++;
+ i++;
break;
}
resource = ACPI_NEXT_RESOURCE(resource);
- link++;
- i++;
if (resource >= end)
break;
}
@@ -702,14 +853,12 @@ acpi_pci_link_choose_irq(device_t dev, struct link *link)
int
acpi_pci_link_route_interrupt(device_t dev, int index)
{
- struct acpi_pci_link_softc *sc;
struct link *link;
- ACPI_SERIAL_BEGIN(pci_link);
- sc = device_get_softc(dev);
- KASSERT(index >= 0 && index < sc->pl_num_links,
- ("%s: invalid index %d", __func__, index));
- link = &sc->pl_links[index];
+ ACPI_SERIAL_BEGIN(pci_link);
+ link = acpi_pci_link_lookup(dev, index);
+ if (link == NULL)
+ panic("%s: apparently invalid index %d", __func__, index);
/*
* If this link device is already routed to an interrupt, just return
OpenPOWER on IntegriCloud