diff options
author | jhb <jhb@FreeBSD.org> | 2004-12-01 21:05:02 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2004-12-01 21:05:02 +0000 |
commit | 931ffd7d22e27213113c91848a0420789d44af2b (patch) | |
tree | 61dac50a90ac0ad054c271f83b4a256c078015e2 /sys/dev/acpica | |
parent | 24f769137d86a63519d3303ea714b5270c8f9716 (diff) | |
download | FreeBSD-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.c | 287 |
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 |