diff options
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/acpi/acpi.c | 249 | ||||
| -rw-r--r-- | sys/dev/acpi/acpi_pci_link.c | 1189 | ||||
| -rw-r--r-- | sys/dev/acpi/acpi_quirks.c | 82 | ||||
| -rw-r--r-- | sys/dev/acpi/acpivar.h | 31 | ||||
| -rw-r--r-- | sys/dev/acpi/files.acpi | 8 | ||||
| -rw-r--r-- | sys/dev/pci/pccbb.c | 9 |
6 files changed, 1328 insertions, 240 deletions
diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c index 50b29e12832..ecae52d79a5 100644 --- a/sys/dev/acpi/acpi.c +++ b/sys/dev/acpi/acpi.c @@ -1,4 +1,4 @@ -/* $NetBSD: acpi.c,v 1.92 2006/07/01 21:44:13 christos Exp $ */ +/* $NetBSD: acpi.c,v 1.93 2006/07/04 00:30:23 christos Exp $ */ /*- * Copyright (c) 2003 The NetBSD Foundation, Inc. @@ -77,7 +77,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.92 2006/07/01 21:44:13 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.93 2006/07/04 00:30:23 christos Exp $"); #include "opt_acpi.h" #include "opt_pcifixup.h" @@ -100,10 +100,10 @@ __KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.92 2006/07/01 21:44:13 christos Exp $"); #endif #if defined(ACPI_PCI_FIXUP) -#error The option ACPI_PCI_FIXUP has been obsoleted by PCI_INTR_FIXUP. Please adjust your kernel configuration file. +#error The option ACPI_PCI_FIXUP has been obsoleted by PCI_INTR_FIXUP_DISABLED. Please adjust your kernel configuration file. #endif -#ifdef PCI_INTR_FIXUP +#ifdef PCI_INTR_FIXUP_DISABLED #include <dev/pci/pcidevs.h> #endif @@ -139,6 +139,7 @@ CFATTACH_DECL(acpi, sizeof(struct acpi_softc), * subsystems that ACPI supercedes) when ACPI is active. */ int acpi_active; +int acpi_force_load; /* * Pointer to the ACPI subsystem's state. There can be only @@ -168,12 +169,6 @@ static void acpi_build_tree(struct acpi_softc *); static ACPI_STATUS acpi_make_devnode(ACPI_HANDLE, UINT32, void *, void **); static void acpi_enable_fixed_events(struct acpi_softc *); -#ifdef PCI_INTR_FIXUP -void acpi_pci_fixup(struct acpi_softc *); -#endif -#if defined(PCI_INTR_FIXUP) || defined(ACPI_ACTIVATE_DEV) -static ACPI_STATUS acpi_allocate_resources(ACPI_HANDLE handle); -#endif /* * acpi_probe: @@ -224,6 +219,19 @@ acpi_probe(void) return 0; } + + if (!acpi_force_load && (acpi_find_quirks() & ACPI_QUIRK_BROKEN)) { + printf("ACPI: BIOS implementation in listed as broken:\n"); + printf("ACPI: X/RSDT: OemId <%6.6s,%8.8s,%08x>, " + "AslId <%4.4s,%08x>\n", + AcpiGbl_XSDT->OemId, AcpiGbl_XSDT->OemTableId, + AcpiGbl_XSDT->OemRevision, + AcpiGbl_XSDT->AslCompilerId, + AcpiGbl_XSDT->AslCompilerRevision); + printf("ACPI: not used. set acpi_force_load to use anyway.\n"); + return 0; + } + /* * Looks like we have ACPI! */ @@ -355,14 +363,6 @@ acpi_attach(struct device *parent, struct device *self, void *aux) acpitimer_init(); /* - * Fix up PCI devices. - */ -#ifdef PCI_INTR_FIXUP - if ((sc->sc_quirks & (ACPI_QUIRK_BADPCI | ACPI_QUIRK_BADIRQ)) == 0) - acpi_pci_fixup(sc); -#endif - - /* * Scan the namespace and build our device tree. */ #ifdef ACPI_DEBUGGER @@ -1114,204 +1114,15 @@ acpi_enter_sleep_state(struct acpi_softc *sc, int state) return ret; } -#ifdef PCI_INTR_FIXUP -ACPI_STATUS acpi_pci_fixup_bus(ACPI_HANDLE, UINT32, void *, void **); -/* - * acpi_pci_fixup: - * - * Set up PCI devices that BIOS didn't handle right. - * Iterate through all devices and try to get the _PTR - * (PCI Routing Table). If it exists then make sure all - * interrupt links that it uses are working. - */ -void -acpi_pci_fixup(struct acpi_softc *sc) -{ - ACPI_HANDLE parent; - ACPI_STATUS rv; - -#ifdef ACPI_DEBUG - printf("acpi_pci_fixup starts:\n"); -#endif - rv = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &parent); - if (ACPI_FAILURE(rv)) - return; - sc->sc_pci_bus = 0; - AcpiWalkNamespace(ACPI_TYPE_DEVICE, parent, 100, - acpi_pci_fixup_bus, sc, NULL); -} - -static uint -acpi_get_intr(ACPI_HANDLE handle) -{ - ACPI_BUFFER ret; - ACPI_STATUS rv; - ACPI_RESOURCE *res; - ACPI_RESOURCE_IRQ *irq; - uint intr; - - intr = -1; - rv = acpi_get(handle, &ret, AcpiGetCurrentResources); - if (ACPI_FAILURE(rv)) - return intr; - for (res = ret.Pointer; res->Type != ACPI_RESOURCE_TYPE_END_TAG; - res = ACPI_NEXT_RESOURCE(res)) { - if (res->Type == ACPI_RESOURCE_TYPE_IRQ) { - irq = (ACPI_RESOURCE_IRQ *)&res->Data; - if (irq->InterruptCount == 1) - intr = irq->Interrupts[0]; - break; - } - } - AcpiOsFree(ret.Pointer); - return intr; -} - -static void -acpi_pci_set_line(int bus, int dev, int pin, int line) -{ - ACPI_STATUS err; - ACPI_PCI_ID pid; - UINT32 intr, id, bhlc; - int func, nfunc; - - pid.Bus = bus; - pid.Device = dev; - pid.Function = 0; - - err = AcpiOsReadPciConfiguration(&pid, PCI_BHLC_REG, &bhlc, 32); - if (err) - return; - if (PCI_HDRTYPE_MULTIFN(bhlc)) - nfunc = 8; - else - nfunc = 1; - - for (func = 0; func < nfunc; func++) { - pid.Function = func; - - err = AcpiOsReadPciConfiguration(&pid, PCI_ID_REG, &id, 32); - if (err || PCI_VENDOR(id) == PCI_VENDOR_INVALID || - PCI_VENDOR(id) == 0) - continue; - - err = AcpiOsReadPciConfiguration(&pid, PCI_INTERRUPT_REG, - &intr, 32); - if (err) { - printf("AcpiOsReadPciConfiguration failed %d\n", err); - return; - } - if (pin == PCI_INTERRUPT_PIN(intr) && - line != PCI_INTERRUPT_LINE(intr)) { -#ifdef ACPI_DEBUG - printf("acpi fixup pci intr: %d:%d:%d %c: %d -> %d\n", - bus, dev, func, - pin + '@', PCI_INTERRUPT_LINE(intr), - line); -#endif - intr &= ~(PCI_INTERRUPT_LINE_MASK << - PCI_INTERRUPT_LINE_SHIFT); - intr |= line << PCI_INTERRUPT_LINE_SHIFT; - err = AcpiOsWritePciConfiguration(&pid, - PCI_INTERRUPT_REG, intr, 32); - if (err) { - printf("AcpiOsWritePciConfiguration failed" - " %d\n", err); - return; - } - } - } -} - -ACPI_STATUS -acpi_pci_fixup_bus(ACPI_HANDLE handle, UINT32 level, void *context, - void **status) -{ - struct acpi_softc *sc = context; - ACPI_STATUS rv; - ACPI_BUFFER buf; - UINT8 *Buffer; - ACPI_PCI_ROUTING_TABLE *PrtElement; - ACPI_HANDLE link; - uint line; - ACPI_INTEGER val; - - rv = acpi_get(handle, &buf, AcpiGetIrqRoutingTable); - if (ACPI_FAILURE(rv)) - return AE_OK; - - /* - * If at level 1, this is a PCI root bus. Try the _BBN method - * to get the right PCI bus numbering for the following - * busses (this is a depth-first walk). It may fail, - * for example if there's only one root bus, but that - * case should be ok, so we'll ignore that. - */ - if (level == 1) { - rv = acpi_eval_integer(handle, METHOD_NAME__BBN, &val); - if (!ACPI_FAILURE(rv)) { -#ifdef ACPI_DEBUG - printf("%s: fixup: _BBN success, bus # was %d now %d\n", - sc->sc_dev.dv_xname, sc->sc_pci_bus, - ACPI_LOWORD(val)); -#endif - sc->sc_pci_bus = ACPI_LOWORD(val); - } - } - - -#ifdef ACPI_DEBUG - printf("%s: fixing up PCI bus %d at level %u\n", sc->sc_dev.dv_xname, - sc->sc_pci_bus, level); -#endif - - for (Buffer = buf.Pointer; ; Buffer += PrtElement->Length) { - PrtElement = (ACPI_PCI_ROUTING_TABLE *)Buffer; - if (PrtElement->Length == 0) - break; - if (PrtElement->Source[0] == 0) - continue; - - rv = AcpiGetHandle(NULL, PrtElement->Source, &link); - if (ACPI_FAILURE(rv)) - continue; - line = acpi_get_intr(link); - if (line == (uint)-1 || line == 0) { - printf("%s: fixing up intr link %s\n", - sc->sc_dev.dv_xname, PrtElement->Source); - rv = acpi_allocate_resources(link); - if (ACPI_FAILURE(rv)) { - printf("%s: interrupt allocation failed %s\n", - sc->sc_dev.dv_xname, PrtElement->Source); - continue; - } - line = acpi_get_intr(link); - if (line == (uint)-1) { - printf("%s: get intr failed %s\n", - sc->sc_dev.dv_xname, PrtElement->Source); - continue; - } - } - - acpi_pci_set_line(sc->sc_pci_bus, PrtElement->Address >> 16, - PrtElement->Pin + 1, line); - } - - sc->sc_pci_bus++; - - AcpiOsFree(buf.Pointer); - return AE_OK; -} -#endif /* PCI_INTR_FIXUP */ - -#if defined(PCI_INTR_FIXUP) || defined(ACPI_ACTIVATE_DEV) +#if defined(ACPI_ACTIVATE_DEV) /* XXX This very incomplete */ -static ACPI_STATUS +ACPI_STATUS acpi_allocate_resources(ACPI_HANDLE handle) { ACPI_BUFFER bufp, bufc, bufn; ACPI_RESOURCE *resp, *resc, *resn; ACPI_RESOURCE_IRQ *irq; + ACPI_RESOURCE_EXTENDED_IRQ *xirq; ACPI_STATUS rv; uint delta; @@ -1346,6 +1157,22 @@ acpi_allocate_resources(ACPI_HANDLE handle) irq->InterruptCount = 1; resn->Length = ACPI_RS_SIZE(ACPI_RESOURCE_IRQ); break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + memcpy(&resn->Data, &resp->Data, + sizeof(ACPI_RESOURCE_EXTENDED_IRQ)); + xirq = (ACPI_RESOURCE_EXTENDED_IRQ *)&resn->Data; +#if 0 + /* + * XXX not duplicating the interrupt logic above + * because its not clear what it accomplishes. + */ + xirq->Interrupts[0] = + ((ACPI_RESOURCE_EXT_IRQ *)&resp->Data)-> + Interrupts[irq->NumberOfInterrupts-1]; + xirq->NumberOfInterrupts = 1; +#endif + resn->Length = ACPI_RS_SIZE(ACPI_RESOURCE_EXTENDED_IRQ); + break; case ACPI_RESOURCE_TYPE_IO: memcpy(&resn->Data, &resp->Data, sizeof(ACPI_RESOURCE_IO)); @@ -1390,7 +1217,7 @@ out1: out: return rv; } -#endif /* PCI_INTR_FIXUP || ACPI_ACTIVATE_DEV */ +#endif /* ACPI_ACTIVATE_DEV */ SYSCTL_SETUP(sysctl_acpi_setup, "sysctl hw.acpi subtree setup") { diff --git a/sys/dev/acpi/acpi_pci_link.c b/sys/dev/acpi/acpi_pci_link.c new file mode 100644 index 00000000000..f148f6c19ea --- /dev/null +++ b/sys/dev/acpi/acpi_pci_link.c @@ -0,0 +1,1189 @@ +/* $NetBSD: acpi_pci_link.c,v 1.1 2006/07/04 00:30:23 christos Exp $ */ + +/*- + * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#include "opt_acpi.h" +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/reboot.h> + +#include <dev/acpi/acpica.h> +#include <dev/acpi/acpireg.h> +#include <dev/acpi/acpivar.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#define NUM_ISA_INTERRUPTS 16 +#define NUM_ACPI_INTERRUPTS 256 + +#define PCI_INVALID_IRQ 255 +#define PCI_INTERRUPT_VALID(x) ((x) != PCI_INVALID_IRQ && (x) != 0) + +#define ACPI_SERIAL_BEGIN(x) +#define ACPI_SERIAL_END(x) + +/* + * 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 { + int pl_num_links; + int pl_crs_bad; + struct link *pl_links; + char pl_name[32]; + ACPI_HANDLE pl_handle; + void *pl_powerhook; + TAILQ_ENTRY(acpi_pci_link_softc) pl_list; +}; + +static TAILQ_HEAD(, acpi_pci_link_softc) acpi_pci_linkdevs = + TAILQ_HEAD_INITIALIZER(acpi_pci_linkdevs); + + +struct link { + struct acpi_pci_link_softc *l_sc; + uint8_t l_bios_irq; + uint8_t l_irq; + uint8_t l_trig; + uint8_t l_pol; + uint8_t l_initial_irq; + int l_res_index; + int l_num_irqs; + int *l_irqs; + int l_references; + int l_routed:1; + int l_isa_irq:1; + 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 in_dpf; + int res_index; + int link_index; +}; + +MALLOC_DEFINE(M_PCI_LINK, "pci_link", "ACPI PCI Link structures"); + +static int pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS]; +static int pci_link_bios_isa_irqs; + +static ACPI_STATUS acpi_count_irq_resources(ACPI_RESOURCE *, void *); +static ACPI_STATUS link_add_crs(ACPI_RESOURCE *, void *); +static ACPI_STATUS link_add_prs(ACPI_RESOURCE *, void *); +static int link_valid_irq(struct link *, int); +static void acpi_pci_link_dump(struct acpi_pci_link_softc *); +static int acpi_pci_link_attach(struct acpi_pci_link_softc *); +static uint8_t acpi_pci_link_search_irq(struct acpi_pci_link_softc *, int, int, + int); +static void acpi_pci_link_resume(int, void *); +static struct link *acpi_pci_link_lookup(struct acpi_pci_link_softc *, int); +static ACPI_STATUS acpi_pci_link_srs(struct acpi_pci_link_softc *, + ACPI_BUFFER *); +static ACPI_STATUS acpi_AppendBufferResource(ACPI_BUFFER *, ACPI_RESOURCE *); + +static ACPI_STATUS +acpi_count_irq_resources(ACPI_RESOURCE *res, void *context) +{ + struct link_count_request *req; + + req = (struct link_count_request *)context; + switch (res->Type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + 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_RESOURCE_TYPE_END_DEPENDENT: + /* We are finished with DPF parsing. */ + KASSERT(req->in_dpf != DPF_OUTSIDE); + req->in_dpf = DPF_OUTSIDE; + break; + case ACPI_RESOURCE_TYPE_IRQ: + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + /* + * 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); +} + +static ACPI_STATUS +link_add_crs(ACPI_RESOURCE *res, void *context) +{ + struct link_res_request *req; + struct link *link; + + req = (struct link_res_request *)context; + switch (res->Type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + 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_RESOURCE_TYPE_END_DEPENDENT: + /* We are finished with DPF parsing. */ + KASSERT(req->in_dpf != DPF_OUTSIDE); + req->in_dpf = DPF_OUTSIDE; + break; + case ACPI_RESOURCE_TYPE_IRQ: + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + KASSERT(req->link_index < req->sc->pl_num_links); + link = &req->sc->pl_links[req->link_index]; + link->l_res_index = req->res_index; + req->link_index++; + req->res_index++; + + /* + * Only use the current value if there's one IRQ. Some + * systems return multiple IRQs (which is nonsense for _CRS) + * when the link hasn't been programmed. + */ + if (res->Type == ACPI_RESOURCE_TYPE_IRQ) { + if (res->Data.Irq.InterruptCount == 1) { + link->l_irq = res->Data.Irq.Interrupts[0]; + link->l_trig = res->Data.Irq.Triggering; + link->l_pol = res->Data.Irq.Polarity; + } + } else if (res->Data.ExtendedIrq.InterruptCount == 1) { + link->l_irq = res->Data.ExtendedIrq.Interrupts[0]; + link->l_trig = res->Data.ExtendedIrq.Triggering; + link->l_pol = res->Data.ExtendedIrq.Polarity; + } + + /* + * An IRQ of zero means that the link isn't routed. + */ + if (link->l_irq == 0) + link->l_irq = PCI_INVALID_IRQ; + break; + default: + req->res_index++; + } + return (AE_OK); +} + +/* + * Populate the set of possible IRQs for each device. + */ +static ACPI_STATUS +link_add_prs(ACPI_RESOURCE *res, void *context) +{ + struct link_res_request *req; + struct link *link; + UINT8 *irqs = NULL; + UINT32 *ext_irqs = NULL; + int i, is_ext_irq = 1; + + req = (struct link_res_request *)context; + switch (res->Type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + 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_RESOURCE_TYPE_END_DEPENDENT: + /* We are finished with DPF parsing. */ + KASSERT(req->in_dpf != DPF_OUTSIDE); + req->in_dpf = DPF_OUTSIDE; + break; + case ACPI_RESOURCE_TYPE_IRQ: + is_ext_irq = 0; + /* fall through */ + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + /* + * 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); + link = &req->sc->pl_links[req->link_index]; + if (link->l_res_index == -1) { + KASSERT(req->sc->pl_crs_bad); + link->l_res_index = req->res_index; + } + req->link_index++; + req->res_index++; + + /* + * Stash a copy of the resource for later use when doing + * _SRS. + */ + memcpy(&link->l_prs_template, res, sizeof(ACPI_RESOURCE)); + if (is_ext_irq) { + link->l_num_irqs = + res->Data.ExtendedIrq.InterruptCount; + link->l_trig = res->Data.ExtendedIrq.Triggering; + link->l_pol = res->Data.ExtendedIrq.Polarity; + ext_irqs = res->Data.ExtendedIrq.Interrupts; + } else { + link->l_num_irqs = res->Data.Irq.InterruptCount; + link->l_trig = res->Data.Irq.Triggering; + link->l_pol = res->Data.Irq.Polarity; + irqs = res->Data.Irq.Interrupts; + } + if (link->l_num_irqs == 0) + break; + + /* + * Save a list of the valid IRQs. Also, if all of the + * valid IRQs are ISA IRQs, then mark this link as + * routed via an ISA interrupt. + */ + link->l_isa_irq = TRUE; + link->l_irqs = malloc(sizeof(int) * link->l_num_irqs, + M_PCI_LINK, M_WAITOK | M_ZERO); + for (i = 0; i < link->l_num_irqs; i++) { + if (is_ext_irq) { + link->l_irqs[i] = ext_irqs[i]; + if (ext_irqs[i] >= NUM_ISA_INTERRUPTS) + link->l_isa_irq = FALSE; + } else { + link->l_irqs[i] = irqs[i]; + if (irqs[i] >= NUM_ISA_INTERRUPTS) + link->l_isa_irq = FALSE; + } + } + break; + default: + if (req->in_dpf == DPF_IGNORE) + break; + if (req->sc->pl_crs_bad) + aprint_normal("%s: Warning: possible resource %d " + "will be lost during _SRS\n", req->sc->pl_name, + req->res_index); + req->res_index++; + } + return (AE_OK); +} + +static int +link_valid_irq(struct link *link, int irq) +{ + int i; + + /* Invalid interrupts are never valid. */ + if (!PCI_INTERRUPT_VALID(irq)) + return (FALSE); + + /* Any interrupt in the list of possible interrupts is valid. */ + for (i = 0; i < link->l_num_irqs; i++) + if (link->l_irqs[i] == irq) + return (TRUE); + + /* + * For links routed via an ISA interrupt, if the SCI is routed via + * an ISA interrupt, the SCI is always treated as a valid IRQ. + */ + if (link->l_isa_irq && AcpiGbl_FADT->SciInt == irq && + irq < NUM_ISA_INTERRUPTS) + return (TRUE); + + /* If the interrupt wasn't found in the list it is not valid. */ + return (FALSE); +} + +void +acpi_pci_link_state(void) +{ + struct acpi_pci_link_softc *sc; + + TAILQ_FOREACH(sc, &acpi_pci_linkdevs, pl_list) { + acpi_pci_link_dump(sc); + } +} + +static void +acpi_pci_link_dump(struct acpi_pci_link_softc *sc) +{ + struct link *link; + int i, j; + + printf("Link Device %s:\n", sc->pl_name); + printf("Index IRQ Rtd Ref IRQs\n"); + for (i = 0; i < sc->pl_num_links; i++) { + link = &sc->pl_links[i]; + printf("%5d %3d %c %3d ", i, link->l_irq, + link->l_routed ? 'Y' : 'N', link->l_references); + if (link->l_num_irqs == 0) + printf(" none"); + else for (j = 0; j < link->l_num_irqs; j++) + printf(" %d", link->l_irqs[j]); + printf("\n"); + } + printf("\n"); +} + +static int +acpi_pci_link_attach(struct acpi_pci_link_softc *sc) +{ + struct link_count_request creq; + struct link_res_request rreq; + ACPI_STATUS status; + int i; + + ACPI_SERIAL_BEGIN(pci_link); + + /* + * Count the number of current resources so we know how big of + * 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(sc->pl_handle, "_CRS", + acpi_count_irq_resources, &creq); + sc->pl_crs_bad = ACPI_FAILURE(status); + if (sc->pl_crs_bad) { + creq.in_dpf = DPF_OUTSIDE; + creq.count = 0; + status = AcpiWalkResources(sc->pl_handle, "_PRS", + acpi_count_irq_resources, &creq); + if (ACPI_FAILURE(status)) { + aprint_error("%s: Unable to parse _CRS or _PRS: %s\n", + sc->pl_name, AcpiFormatException(status)); + ACPI_SERIAL_END(pci_link); + return (ENXIO); + } + } + sc->pl_num_links = creq.count; + if (creq.count == 0) { + ACPI_SERIAL_END(pci_link); + return (0); + } + sc->pl_links = malloc(sizeof(struct link) * sc->pl_num_links, + M_PCI_LINK, M_WAITOK | M_ZERO); + + /* Initialize the child links. */ + 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_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(sc->pl_handle, "_CRS", + link_add_crs, &rreq); + if (ACPI_FAILURE(status)) { + aprint_error("%s: Unable to parse _CRS: %s\n", + sc->pl_name, 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(sc->pl_handle, "_PRS", + link_add_prs, &rreq); + if (ACPI_FAILURE(status) && + (status != AE_NOT_FOUND || sc->pl_crs_bad)) { + aprint_error("%s: Unable to parse _PRS: %s\n", + sc->pl_name, AcpiFormatException(status)); + goto fail; + } + if (boothowto & AB_VERBOSE) { + aprint_normal("%s: Links after initial probe:\n", sc->pl_name); + acpi_pci_link_dump(sc); + } + + /* Verify initial IRQs if we have _PRS. */ + if (status != AE_NOT_FOUND) + for (i = 0; i < sc->pl_num_links; i++) + if (!link_valid_irq(&sc->pl_links[i], + sc->pl_links[i].l_irq)) + sc->pl_links[i].l_irq = PCI_INVALID_IRQ; + if (boothowto & AB_VERBOSE) { + printf("%s: Links after initial validation:\n", sc->pl_name); + acpi_pci_link_dump(sc); + } + + /* Save initial IRQs. */ + for (i = 0; i < sc->pl_num_links; i++) + sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq; + + /* + * Try to disable this link. If successful, set the current IRQ to + * zero and flags to indicate this link is not routed. If we can't + * run _DIS (i.e., the method doesn't exist), assume the initial + * IRQ was routed by the BIOS. + */ + if (ACPI_SUCCESS(AcpiEvaluateObject(sc->pl_handle, "_DIS", NULL, + NULL))) + for (i = 0; i < sc->pl_num_links; i++) + sc->pl_links[i].l_irq = PCI_INVALID_IRQ; + else + for (i = 0; i < sc->pl_num_links; i++) + if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq)) + sc->pl_links[i].l_routed = TRUE; + if (boothowto & AB_VERBOSE) { + printf("%s: Links after disable:\n", sc->pl_name); + acpi_pci_link_dump(sc); + } + ACPI_SERIAL_END(pci_link); + return (0); +fail: + ACPI_SERIAL_END(pci_link); + for (i = 0; i < sc->pl_num_links; i++) + if (sc->pl_links[i].l_irqs != NULL) + free(sc->pl_links[i].l_irqs, M_PCI_LINK); + free(sc->pl_links, M_PCI_LINK); + return (ENXIO); +} + +static uint8_t +acpi_pci_link_search_irq(struct acpi_pci_link_softc *sc, int bus, int device, + int pin) +{ + uint32_t value; + uint8_t func, maxfunc, ipin, iline; + pcitag_t tag; + + tag = pci_make_tag(acpi_softc->sc_pc, bus, device, 0); + /* See if we have a valid device at function 0. */ + value = pci_conf_read(acpi_softc->sc_pc, tag, PCI_BHLC_REG); + if (PCI_HDRTYPE_TYPE(value) > PCI_HDRTYPE_PCB) + return (PCI_INVALID_IRQ); + if (PCI_HDRTYPE_MULTIFN(value)) + maxfunc = 7; + else + maxfunc = 0; + + /* Scan all possible functions at this device. */ + for (func = 0; func <= maxfunc; func++) { + tag = pci_make_tag(acpi_softc->sc_pc, bus, device, func); + value = pci_conf_read(acpi_softc->sc_pc, tag, PCI_ID_REG); + if (PCI_VENDOR(value) == 0xffff) + continue; + value = pci_conf_read(acpi_softc->sc_pc, tag, + PCI_INTERRUPT_REG); + ipin = PCI_INTERRUPT_PIN(value); + iline = PCI_INTERRUPT_LINE(value); + + /* + * See if it uses the pin in question. Note that the passed + * in pin uses 0 for A, .. 3 for D whereas the intpin + * register uses 0 for no interrupt, 1 for A, .. 4 for D. + */ + if (ipin != pin + 1) + continue; + aprint_verbose( + "%s: ACPI: Found matching pin for %d.%d.INT%c" + " at func %d: %d\n", + sc->pl_name, bus, device, pin + 'A', func, iline); + if (PCI_INTERRUPT_VALID(iline)) + return (iline); + } + 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(struct acpi_pci_link_softc *sc, int source_index) +{ + int i; + + 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(void *v, int index, int bus, int slot, int pin) +{ + struct acpi_pci_link_softc *sc = v; + struct link *link; + uint8_t bios_irq; + + /* Bump the reference count. */ + ACPI_SERIAL_BEGIN(pci_link); + link = acpi_pci_link_lookup(sc, index); + if (link == NULL) { + printf("%s: apparently invalid index %d\n", sc->pl_name, index); + ACPI_SERIAL_END(pci_link); + return; + } + link->l_references++; + if (link->l_routed) + pci_link_interrupt_weights[link->l_irq]++; + + /* + * The BIOS only routes interrupts via ISA IRQs using the ATPICs + * (8259As). Thus, if this link is routed via an ISA IRQ, go + * look to see if the BIOS routed an IRQ for this link at the + * indicated (bus, slot, pin). If so, we prefer that IRQ for + * this link and add that IRQ to our list of known-good IRQs. + * This provides a good work-around for link devices whose _CRS + * method is either broken or bogus. We only use the value + * returned by _CRS if we can't find a valid IRQ via this method + * in fact. + * + * If this link is not routed via an ISA IRQ (because we are using + * APIC for example), then don't bother looking up the BIOS IRQ + * as if we find one it won't be valid anyway. + */ + if (!link->l_isa_irq) { + ACPI_SERIAL_END(pci_link); + return; + } + + /* Try to find a BIOS IRQ setting from any matching devices. */ + bios_irq = acpi_pci_link_search_irq(sc, bus, slot, pin); + if (!PCI_INTERRUPT_VALID(bios_irq)) { + ACPI_SERIAL_END(pci_link); + return; + } + + /* Validate the BIOS IRQ. */ + if (!link_valid_irq(link, bios_irq)) { + printf("%s: BIOS IRQ %u for %d.%d.INT%c is invalid\n", + sc->pl_name, bios_irq, (int)bus, slot, pin + 'A'); + } else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) { + link->l_bios_irq = bios_irq; + if (bios_irq < NUM_ISA_INTERRUPTS) + pci_link_bios_isa_irqs |= (1 << bios_irq); + if (bios_irq != link->l_initial_irq && + PCI_INTERRUPT_VALID(link->l_initial_irq)) + printf( + "%s: BIOS IRQ %u does not match initial IRQ %u\n", + sc->pl_name, bios_irq, link->l_initial_irq); + } else if (bios_irq != link->l_bios_irq) + printf( + "%s: BIOS IRQ %u for %d.%d.INT%c does not match " + "previous BIOS IRQ %u\n", + sc->pl_name, bios_irq, (int)bus, slot, pin + 'A', + link->l_bios_irq); + ACPI_SERIAL_END(pci_link); +} + +static ACPI_STATUS +acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf) +{ + ACPI_RESOURCE *resource, *end, newres, *resptr; + ACPI_BUFFER crsbuf; + ACPI_STATUS status; + struct link *link; + int i, in_dpf; + + /* Fetch the _CRS. */ + crsbuf.Pointer = NULL; + crsbuf.Length = ACPI_ALLOCATE_BUFFER; + status = AcpiGetCurrentResources(sc->pl_handle, &crsbuf); + if (ACPI_SUCCESS(status) && crsbuf.Pointer == NULL) + status = AE_NO_MEMORY; + if (ACPI_FAILURE(status)) { + aprint_verbose("%s: Unable to fetch current resources: %s\n", + sc->pl_name, AcpiFormatException(status)); + return (status); + } + + /* Fill in IRQ resources via link structures. */ + 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->Type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + 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_RESOURCE_TYPE_END_DEPENDENT: + /* We are finished with DPF parsing. */ + KASSERT(in_dpf != DPF_OUTSIDE); + in_dpf = DPF_OUTSIDE; + resptr = NULL; + break; + case ACPI_RESOURCE_TYPE_IRQ: + newres = link->l_prs_template; + resptr = &newres; + resptr->Data.Irq.InterruptCount = 1; + if (PCI_INTERRUPT_VALID(link->l_irq)) { + KASSERT(link->l_irq < NUM_ISA_INTERRUPTS); + resptr->Data.Irq.Interrupts[0] = link->l_irq; + resptr->Data.Irq.Triggering = link->l_trig; + resptr->Data.Irq.Polarity = link->l_pol; + } else + resptr->Data.Irq.Interrupts[0] = 0; + link++; + i++; + break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + newres = link->l_prs_template; + resptr = &newres; + resptr->Data.ExtendedIrq.InterruptCount = 1; + if (PCI_INTERRUPT_VALID(link->l_irq)) { + resptr->Data.ExtendedIrq.Interrupts[0] = + link->l_irq; + resptr->Data.ExtendedIrq.Triggering = + link->l_trig; + resptr->Data.ExtendedIrq.Polarity = link->l_pol; + } else + resptr->Data.ExtendedIrq.Interrupts[0] = 0; + link++; + i++; + break; + default: + resptr = resource; + } + if (resptr != NULL) { + status = acpi_AppendBufferResource(srsbuf, resptr); + if (ACPI_FAILURE(status)) { + printf("%s: Unable to build resources: %s\n", + sc->pl_name, AcpiFormatException(status)); + if (srsbuf->Pointer != NULL) + AcpiOsFree(srsbuf->Pointer); + AcpiOsFree(crsbuf.Pointer); + return (status); + } + } + if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG) + break; + resource = ACPI_NEXT_RESOURCE(resource); + 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.Type == ACPI_RESOURCE_TYPE_IRQ) { + + /* Build an IRQ resource. */ + newres.Data.Irq.InterruptCount = 1; + if (PCI_INTERRUPT_VALID(link->l_irq)) { + KASSERT(link->l_irq < NUM_ISA_INTERRUPTS); + newres.Data.Irq.Interrupts[0] = link->l_irq; + newres.Data.Irq.Triggering = link->l_trig; + newres.Data.Irq.Polarity = link->l_pol; + } else + newres.Data.Irq.Interrupts[0] = 0; + } else { + + /* Build an ExtIRQ resuorce. */ + newres.Data.ExtendedIrq.InterruptCount = 1; + if (PCI_INTERRUPT_VALID(link->l_irq)) { + newres.Data.ExtendedIrq.Interrupts[0] = + link->l_irq; + newres.Data.ExtendedIrq.Triggering = + link->l_trig; + newres.Data.ExtendedIrq.Polarity = + link->l_pol; + } 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)) { + printf("%s: Unable to build resources: %s\n", + sc->pl_name, AcpiFormatException(status)); + if (srsbuf->Pointer != NULL) + AcpiOsFree(srsbuf->Pointer); + return (status); + } + } + return (AE_OK); +} + +static ACPI_STATUS +acpi_pci_link_srs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf) +{ + ACPI_STATUS status; + + 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. */ + return AcpiSetCurrentResources(sc->pl_handle, srsbuf); +} + +static ACPI_STATUS +acpi_pci_link_route_irqs(struct acpi_pci_link_softc *sc, int *irq, int *pol, + int *trig) +{ + ACPI_RESOURCE *resource, *end; + ACPI_BUFFER srsbuf; + ACPI_STATUS status; + struct link *link; + int i, is_ext = 0; + + status = acpi_pci_link_srs(sc, &srsbuf); + if (ACPI_FAILURE(status)) { + printf("%s: _SRS failed: %s\n", + sc->pl_name, AcpiFormatException(status)); + return (status); + } + /* + * Perform acpi_config_intr() on each IRQ resource if it was just + * routed for the first time. + */ + link = sc->pl_links; + i = 0; + resource = (ACPI_RESOURCE *)srsbuf.Pointer; + end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length); + for (;;) { + if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG) + break; + switch (resource->Type) { + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + is_ext = 1; + /* FALLTHROUGH */ + case ACPI_RESOURCE_TYPE_IRQ: + /* + * 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)) { + *trig = is_ext ? + resource->Data.ExtendedIrq.Triggering : + resource->Data.Irq.Triggering; + *pol = is_ext ? + resource->Data.ExtendedIrq.Polarity : + resource->Data.Irq.Polarity; + *irq = is_ext ? + resource->Data.ExtendedIrq.Interrupts[0] : + resource->Data.Irq.Interrupts[0]; + link->l_routed = TRUE; + pci_link_interrupt_weights[link->l_irq] += + link->l_references; + } + link++; + i++; + break; + } + resource = ACPI_NEXT_RESOURCE(resource); + if (resource >= end) + break; + } + AcpiOsFree(srsbuf.Pointer); + return (AE_OK); +} + +static void +acpi_pci_link_resume(int why, void *arg) +{ + struct acpi_pci_link_softc *sc = arg; + ACPI_BUFFER srsbuf; + + switch (why) { + case PWR_RESUME: + ACPI_SERIAL_BEGIN(pci_link); + if (ACPI_SUCCESS(acpi_pci_link_srs(sc, &srsbuf))) + AcpiOsFree(srsbuf.Pointer); + ACPI_SERIAL_END(pci_link); + default: + break; + } +} + +/* + * Pick an IRQ to use for this unrouted link. + */ +static uint8_t +acpi_pci_link_choose_irq(struct acpi_pci_link_softc *sc, struct link *link) +{ + u_int8_t best_irq, pos_irq; + int best_weight, pos_weight, i; + + KASSERT(!link->l_routed); + KASSERT(!PCI_INTERRUPT_VALID(link->l_irq)); + + /* + * If we have a valid BIOS IRQ, use that. We trust what the BIOS + * says it routed over what _CRS says the link thinks is routed. + */ + if (PCI_INTERRUPT_VALID(link->l_bios_irq)) + return (link->l_bios_irq); + + /* + * If we don't have a BIOS IRQ but do have a valid IRQ from _CRS, + * then use that. + */ + if (PCI_INTERRUPT_VALID(link->l_initial_irq)) + return (link->l_initial_irq); + + /* + * Ok, we have no useful hints, so we have to pick from the + * possible IRQs. For ISA IRQs we only use interrupts that + * have already been used by the BIOS. + */ + best_irq = PCI_INVALID_IRQ; + best_weight = INT_MAX; + for (i = 0; i < link->l_num_irqs; i++) { + pos_irq = link->l_irqs[i]; + if (pos_irq < NUM_ISA_INTERRUPTS && + (pci_link_bios_isa_irqs & 1 << pos_irq) == 0) + continue; + pos_weight = pci_link_interrupt_weights[pos_irq]; + if (pos_weight < best_weight) { + best_weight = pos_weight; + best_irq = pos_irq; + } + } + + /* + * If this is an ISA IRQ, try using the SCI if it is also an ISA + * interrupt as a fallback. + */ + if (link->l_isa_irq) { + pos_irq = AcpiGbl_FADT->SciInt; + pos_weight = pci_link_interrupt_weights[pos_irq]; + if (pos_weight < best_weight) { + best_weight = pos_weight; + best_irq = pos_irq; + } + } + + if (PCI_INTERRUPT_VALID(best_irq)) { + aprint_verbose("%s: Picked IRQ %u with weight %d\n", + sc->pl_name, best_irq, best_weight); + } else + printf("%s: Unable to choose an IRQ\n", sc->pl_name); + return (best_irq); +} + +int +acpi_pci_link_route_interrupt(void *v, int index, int *irq, int *pol, int *trig) +{ + struct acpi_pci_link_softc *sc = v; + struct link *link; + + ACPI_SERIAL_BEGIN(pci_link); + link = acpi_pci_link_lookup(sc, index); + if (link == NULL) + panic("%s: apparently invalid index %d", __func__, index); + + /* + * If this link device is already routed to an interrupt, just return + * the interrupt it is routed to. + */ + if (link->l_routed) { + KASSERT(PCI_INTERRUPT_VALID(link->l_irq)); + ACPI_SERIAL_END(pci_link); + *irq = link->l_irq; + *pol = link->l_pol; + *trig = link->l_trig; + return (link->l_irq); + } + + /* Choose an IRQ if we need one. */ + if (!PCI_INTERRUPT_VALID(link->l_irq)) { + link->l_irq = acpi_pci_link_choose_irq(sc, link); + + /* + * Try to route the interrupt we picked. If it fails, then + * assume the interrupt is not routed. + */ + if (PCI_INTERRUPT_VALID(link->l_irq)) { + acpi_pci_link_route_irqs(sc, irq, pol, trig); + if (!link->l_routed) + link->l_irq = PCI_INVALID_IRQ; + else { + link->l_pol = *pol; + link->l_trig = *trig; + } + } + } + ACPI_SERIAL_END(pci_link); + + return (link->l_irq); +} + +/* + * This is gross, but we abuse the identify routine to perform one-time + * SYSINIT() style initialization for the driver. + */ +static void +acpi_pci_link_init(struct acpi_pci_link_softc *sc) +{ + ACPI_BUFFER buf; + + /* + * If the SCI is an ISA IRQ, add it to the bitmask of known good + * ISA IRQs. + * + * XXX: If we are using the APIC, the SCI might have been + * rerouted to an APIC pin in which case this is invalid. However, + * if we are using the APIC, we also shouldn't be having any PCI + * interrupts routed via ISA IRQs, so this is probably ok. + */ + if (AcpiGbl_FADT->SciInt < NUM_ISA_INTERRUPTS) + pci_link_bios_isa_irqs |= (1 << AcpiGbl_FADT->SciInt); + + sc->pl_powerhook = powerhook_establish(acpi_pci_link_resume, sc); + if (sc->pl_powerhook == NULL) + aprint_normal("can't establish powerhook\n"); + + buf.Length = sizeof (sc->pl_name); + buf.Pointer = sc->pl_name; + + if (ACPI_FAILURE(AcpiGetName(sc->pl_handle, ACPI_SINGLE_NAME, &buf))) + snprintf(sc->pl_name, sizeof (sc->pl_name), "%s", + "ACPI link device"); + + acpi_pci_link_attach(sc); +} + +void * +acpi_pci_link_devbyhandle(ACPI_HANDLE handle) +{ + struct acpi_pci_link_softc *sc; + + TAILQ_FOREACH(sc, &acpi_pci_linkdevs, pl_list) { + if (sc->pl_handle == handle) + return sc; + } + + sc = malloc(sizeof (*sc), M_PCI_LINK, M_NOWAIT|M_ZERO); + if (sc == NULL) + return NULL; + + sc->pl_handle = handle; + + acpi_pci_link_init(sc); + + TAILQ_INSERT_TAIL(&acpi_pci_linkdevs, sc, pl_list); + + return (void *)sc; +} + +ACPI_HANDLE +acpi_pci_link_handle(void *v) +{ + struct acpi_pci_link_softc *sc = v; + + return sc->pl_handle; +} + +char * +acpi_pci_link_name(void *v) +{ + struct acpi_pci_link_softc *sc = v; + + return sc->pl_name; +} + + +/* + * Append an ACPI_RESOURCE to an ACPI_BUFFER. + * + * Given a pointer to an ACPI_RESOURCE structure, expand the ACPI_BUFFER + * provided to contain it. If the ACPI_BUFFER is empty, allocate a sensible + * backing block. If the ACPI_RESOURCE is NULL, return an empty set of + * resources. + */ +#define ACPI_INITIAL_RESOURCE_BUFFER_SIZE 512 + +static ACPI_STATUS +acpi_AppendBufferResource(ACPI_BUFFER *buf, ACPI_RESOURCE *res) +{ + ACPI_RESOURCE *rp; + void *newp; + + /* Initialise the buffer if necessary. */ + if (buf->Pointer == NULL) { + buf->Length = ACPI_INITIAL_RESOURCE_BUFFER_SIZE; + if ((buf->Pointer = AcpiOsAllocate(buf->Length)) == NULL) + return (AE_NO_MEMORY); + rp = (ACPI_RESOURCE *)buf->Pointer; + rp->Type = ACPI_RESOURCE_TYPE_END_TAG; + rp->Length = 0; + } + + if (res == NULL) + return (AE_OK); + + /* + * Scan the current buffer looking for the terminator. + * This will either find the terminator or hit the end + * of the buffer and return an error. + */ + rp = (ACPI_RESOURCE *)buf->Pointer; + for (;;) { + /* Range check, don't go outside the buffer */ + if (rp >= (ACPI_RESOURCE *)((u_int8_t *)buf->Pointer + + buf->Length)) + return (AE_BAD_PARAMETER); + if (rp->Type == ACPI_RESOURCE_TYPE_END_TAG || rp->Length == 0) + break; + rp = ACPI_NEXT_RESOURCE(rp); + } + + /* + * Check the size of the buffer and expand if required. + * + * Required size is: + * size of existing resources before terminator + + * size of new resource and header + + * size of terminator. + * + * Note that this loop should really only run once, unless + * for some reason we are stuffing a *really* huge resource. + */ + while ((((u_int8_t *)rp - (u_int8_t *)buf->Pointer) + + res->Length + ACPI_RS_SIZE_NO_DATA + + ACPI_RS_SIZE_MIN) >= buf->Length) { + if ((newp = AcpiOsAllocate(buf->Length * 2)) == NULL) + return (AE_NO_MEMORY); + memcpy(newp, buf->Pointer, buf->Length); + rp = (ACPI_RESOURCE *)((u_int8_t *)newp + + ((u_int8_t *)rp - (u_int8_t *)buf->Pointer)); + AcpiOsFree(buf->Pointer); + buf->Pointer = newp; + buf->Length += buf->Length; + } + + /* Insert the new resource. */ + memcpy(rp, res, res->Length + ACPI_RS_SIZE_NO_DATA); + + /* And add the terminator. */ + rp = ACPI_NEXT_RESOURCE(rp); + rp->Type = ACPI_RESOURCE_TYPE_END_TAG; + rp->Length = 0; + + return (AE_OK); +} diff --git a/sys/dev/acpi/acpi_quirks.c b/sys/dev/acpi/acpi_quirks.c index 3f31c87d3f8..818a38b7ca0 100644 --- a/sys/dev/acpi/acpi_quirks.c +++ b/sys/dev/acpi/acpi_quirks.c @@ -1,4 +1,4 @@ -/* $NetBSD: acpi_quirks.c,v 1.6 2005/12/11 12:21:02 christos Exp $ */ +/* $NetBSD: acpi_quirks.c,v 1.7 2006/07/04 00:30:23 christos Exp $ */ /* * Copyright 2002 Wasabi Systems, Inc. @@ -37,7 +37,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: acpi_quirks.c,v 1.6 2005/12/11 12:21:02 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: acpi_quirks.c,v 1.7 2006/07/04 00:30:23 christos Exp $"); #include "opt_acpi.h" @@ -52,20 +52,49 @@ __KERNEL_RCSID(0, "$NetBSD: acpi_quirks.c,v 1.6 2005/12/11 12:21:02 christos Exp #include <dev/acpi/acpireg.h> #include <dev/acpi/acpivar.h> +static int acpi_rev_cmp(uint32_t, uint32_t, int); + +/* + * XXX add more + */ static struct acpi_quirk acpi_quirks[] = { - /* - * This implementation seems to be in widespread use, but - * unfortunately, on some systems, it constructs a PCI hierarchy - * that doesn't match reality at all (like on SuperMicro boards). - */ - { "PTLTD ", 0x06040000, ACPI_QUIRK_BADPCI | ACPI_QUIRK_BADIRQ }, - /* - * This is on my Appro 1224 Xi. It does not find all the busses - * in the ACPI tables. - */ - { "A M I ", 0x02000304, ACPI_QUIRK_BADPCI | ACPI_QUIRK_BADIRQ }, + { ACPI_TABLE_FADT, "PTLTD ", 0x06040000, AQ_LTE, " FACP ", + ACPI_QUIRK_BROKEN }, }; +static int +acpi_rev_cmp(uint32_t tabval, uint32_t wanted, int op) +{ + switch (op) { + case AQ_GT: + if (tabval > wanted) + return 0; + else + return 1; + case AQ_LT: + if (tabval < wanted) + return 0; + else + return 1; + case AQ_LTE: + if (tabval <= wanted) + return 0; + else + return 1; + case AQ_GTE: + if (tabval >= wanted) + return 0; + else + return 1; + case AQ_EQ: + if (tabval == wanted) + return 0; + else + return 1; + } + return 1; +} + /* * Simple function to search the quirk table. Only to be used after * AcpiLoadTables has been successfully called. @@ -75,14 +104,35 @@ acpi_find_quirks(void) { int i, nquirks; struct acpi_quirk *aqp; + ACPI_TABLE_HEADER *hdr; nquirks = sizeof(acpi_quirks) / sizeof(struct acpi_quirk); for (i = 0; i < nquirks; i++) { aqp = &acpi_quirks[i]; - if (!strncmp(aqp->aq_oemid, AcpiGbl_XSDT->OemId, strlen(aqp->aq_oemid)) && - aqp->aq_oemrev == AcpiGbl_XSDT->OemRevision) - return aqp->aq_quirks; + /* XXX AcpiGetTableHeader doesn't work for some reason */ + switch (aqp->aq_tabletype) { + case ACPI_TABLE_DSDT: + hdr = (ACPI_TABLE_HEADER *)AcpiGbl_DSDT; + break; + case ACPI_TABLE_XSDT: + hdr = (ACPI_TABLE_HEADER *)AcpiGbl_XSDT; + break; + case ACPI_TABLE_FADT: + hdr = (ACPI_TABLE_HEADER *)AcpiGbl_FADT; + break; + default: + continue; + } + if (strncmp(aqp->aq_oemid, hdr->OemId, strlen(aqp->aq_oemid))) + continue; + if (acpi_rev_cmp(aqp->aq_oemrev, hdr->OemRevision, + aqp->aq_cmpop)) + continue; + if (strncmp(aqp->aq_tabid, hdr->OemTableId, + strlen(aqp->aq_tabid))) + continue; + return aqp->aq_quirks; } return 0; } diff --git a/sys/dev/acpi/acpivar.h b/sys/dev/acpi/acpivar.h index ea07e9397e6..fd3417aa41f 100644 --- a/sys/dev/acpi/acpivar.h +++ b/sys/dev/acpi/acpivar.h @@ -1,4 +1,4 @@ -/* $NetBSD: acpivar.h,v 1.25 2005/12/12 15:04:50 cube Exp $ */ +/* $NetBSD: acpivar.h,v 1.26 2006/07/04 00:30:23 christos Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. @@ -276,9 +276,20 @@ ACPI_STATUS acpi_resource_parse(struct device *, ACPI_HANDLE, const char *, void *, const struct acpi_resource_parse_ops *); void acpi_resource_print(struct device *, struct acpi_resources *); void acpi_resource_cleanup(struct acpi_resources *); +ACPI_STATUS acpi_allocate_resources(ACPI_HANDLE); ACPI_STATUS acpi_pwr_switch_consumer(ACPI_HANDLE, int); +void * acpi_pci_link_devbyhandle(ACPI_HANDLE); +void acpi_pci_link_add_reference(void *, int, int, int, int); +int acpi_pci_link_route_interrupt(void *, int, int *, int *, int *); +char * acpi_pci_link_name(void *); +ACPI_HANDLE acpi_pci_link_handle(void *); +void acpi_pci_link_state(void); + + + + #if defined(_KERNEL_OPT) #include "acpiec.h" @@ -305,12 +316,22 @@ ACPI_STATUS acpi_enter_sleep_state(struct acpi_softc *, int); * quirk handling */ struct acpi_quirk { - const char *aq_oemid; /* compared against the X/RSDT OemId */ - int aq_oemrev; /* compared against the X/RSDT OemRev */ + uint32_t aq_tabletype; /* what type of table (FADT, DSDT, etc) */ + const char *aq_oemid; /* compared against the table OemId */ + int aq_oemrev; /* compared against the table OemRev */ + int aq_cmpop; /* how to compare the oemrev number */ + const char *aq_tabid; /* compared against the table TableId */ int aq_quirks; /* the actual quirks */ }; -#define ACPI_QUIRK_BADPCI 0x00000001 /* bad PCI hierarchy */ -#define ACPI_QUIRK_BADIRQ 0x00000002 /* bad IRQ information */ +#define AQ_GT 0 /* > */ +#define AQ_LT 1 /* < */ +#define AQ_GTE 2 /* >= */ +#define AQ_LTE 3 /* <= */ +#define AQ_EQ 4 /* == */ + +#define ACPI_QUIRK_BROKEN 0x00000001 /* totally broken */ +#define ACPI_QUIRK_BADPCI 0x00000002 /* bad PCI hierarchy */ +#define ACPI_QUIRK_BADBBN 0x00000004 /* _BBN broken */ int acpi_find_quirks(void); diff --git a/sys/dev/acpi/files.acpi b/sys/dev/acpi/files.acpi index 445341523ae..4a0ff6b3166 100644 --- a/sys/dev/acpi/files.acpi +++ b/sys/dev/acpi/files.acpi @@ -1,8 +1,9 @@ -# $NetBSD: files.acpi,v 1.36 2006/06/21 17:47:23 drochner Exp $ +# $NetBSD: files.acpi,v 1.37 2006/07/04 00:30:23 christos Exp $ include "dev/acpi/acpica/files.acpica" -defflag opt_acpi.h ACPIVERBOSE ACPI_DEBUG ACPI_ACTIVATE_DEV ACPI_DSDT_OVERRIDE +defflag opt_acpi.h ACPIVERBOSE ACPI_DEBUG ACPI_ACTIVATE_DEV + ACPI_DSDT_OVERRIDE ACPI_SCANPCI defparam opt_acpi.h ACPI_DSDT_FILE device acpi { }: sysmon_power, sysmon_taskq @@ -10,7 +11,8 @@ attach acpi at acpibus file dev/acpi/acpi.c acpi needs-flag file dev/acpi/acpi_resource.c acpi file dev/acpi/acpi_powerres.c acpi -file dev/acpi/acpi_madt.c acpi & mpacpi +file dev/acpi/acpi_madt.c acpi +file dev/acpi/acpi_pci_link.c acpi file dev/acpi/acpi_quirks.c acpi file dev/acpi/acpi_timer.c acpi diff --git a/sys/dev/pci/pccbb.c b/sys/dev/pci/pccbb.c index 59db2077090..7cda103af2e 100644 --- a/sys/dev/pci/pccbb.c +++ b/sys/dev/pci/pccbb.c @@ -1,4 +1,4 @@ -/* $NetBSD: pccbb.c,v 1.129 2006/06/17 17:06:51 jmcneill Exp $ */ +/* $NetBSD: pccbb.c,v 1.130 2006/07/04 00:30:23 christos Exp $ */ /* * Copyright (c) 1998, 1999 and 2000 @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pccbb.c,v 1.129 2006/06/17 17:06:51 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pccbb.c,v 1.130 2006/07/04 00:30:23 christos Exp $"); /* #define CBB_DEBUG @@ -80,6 +80,7 @@ __KERNEL_RCSID(0, "$NetBSD: pccbb.c,v 1.129 2006/06/17 17:06:51 jmcneill Exp $") #if defined(__i386__) #include "ioapic.h" +#include "acpi.h" #endif #ifndef __NetBSD_Version__ @@ -525,9 +526,7 @@ pccbbattach(parent, self, aux) * may well be zero, with the interrupt routed through the apic. */ -#if NIOAPIC > 0 - printf("%s: using ioapic for interrupt\n", sc->sc_dev.dv_xname); -#else +#if NIOAPIC == 0 && ACPI == 0 if ((0 == pa->pa_intrline) || (255 == pa->pa_intrline)) { printf("%s: NOT USED because of unconfigured interrupt\n", sc->sc_dev.dv_xname); |
