diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2006-11-15 21:39:07 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2006-11-15 21:39:07 +0000 |
commit | 4cd25a3aad0e44c23128ae417e6a4d9b02201940 (patch) | |
tree | 12762b2fcd95e4ef4129d8cd2a5c5f48c37bcd9f | |
parent | d3f0e57c47e6176c8c34356a2c7fd5ffc1bd396f (diff) |
dd pseudo-devices to handle acpi apic interrupt routing.
ok jordan@
-rw-r--r-- | sys/dev/acpi/acpimadt.c | 249 | ||||
-rw-r--r-- | sys/dev/acpi/acpiprt.c | 214 | ||||
-rw-r--r-- | sys/dev/acpi/files.acpi | 12 |
3 files changed, 474 insertions, 1 deletions
diff --git a/sys/dev/acpi/acpimadt.c b/sys/dev/acpi/acpimadt.c new file mode 100644 index 00000000000..e43f49d864f --- /dev/null +++ b/sys/dev/acpi/acpimadt.c @@ -0,0 +1,249 @@ +/* $OpenBSD: acpimadt.c,v 1.1 2006/11/15 21:39:06 kettenis Exp $ */ +/* + * Copyright (c) 2006 Mark Kettenis <kettenis@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <machine/apicvar.h> +#include <machine/cpuvar.h> +#include <machine/bus.h> + +#include <dev/acpi/acpireg.h> +#include <dev/acpi/acpivar.h> +#include <dev/acpi/acpidev.h> + +#include <machine/i8259.h> +#include <machine/i82093reg.h> +#include <machine/i82093var.h> + +#include <machine/mpbiosvar.h> + +#include "ioapic.h" + +int acpimadt_match(struct device *, void *, void *); +void acpimadt_attach(struct device *, struct device *, void *); + +struct cfattach acpimadt_ca = { + sizeof(struct device), acpimadt_match, acpimadt_attach +}; + +struct cfdriver acpimadt_cd = { + NULL, "acpimadt", DV_DULL +}; + +void acpimadt_cfg_intr(int, u_int32_t *); +int acpimadt_print(void *, const char *); + +int +acpimadt_match(struct device *parent, void *match, void *aux) +{ + struct acpi_attach_args *aaa = aux; + struct acpi_table_header *hdr; + + /* + * If we do not have a table, it is not us + */ + if (aaa->aaa_table == NULL) + return (0); + + /* + * If it is an MADT table, we can attach + */ + hdr = (struct acpi_table_header *)aaa->aaa_table; + if (memcmp(hdr->signature, MADT_SIG, sizeof(MADT_SIG) - 1) != 0) + return (0); + + return (1); +} + +struct mp_bus acpimadt_busses[256]; +struct mp_bus acpimadt_isa_bus; + +void +acpimadt_cfg_intr(int flags, u_int32_t *redir) +{ + int mpspo = flags & 0x03; /* XXX magic */ + int mpstrig = (flags >> 2) & 0x03; /* XXX magic */ + + *redir &= ~IOAPIC_REDLO_DEL_MASK; + switch (mpspo) { + case MPS_INTPO_DEF: + case MPS_INTPO_ACTHI: + *redir &= ~IOAPIC_REDLO_ACTLO; + break; + case MPS_INTPO_ACTLO: + *redir |= IOAPIC_REDLO_ACTLO; + break; + default: + panic("unknown MPS interrupt polarity %d", mpspo); + } + + *redir |= (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT); + + switch (mpstrig) { + case MPS_INTTR_LEVEL: + *redir |= IOAPIC_REDLO_LEVEL; + break; + case MPS_INTTR_DEF: + case MPS_INTTR_EDGE: + *redir &= ~IOAPIC_REDLO_LEVEL; + break; + default: + panic("unknown MPS interrupt trigger %d", mpstrig); + } +} + +void +acpimadt_attach(struct device *parent, struct device *self, void *aux) +{ + struct device *mainbus = parent->dv_parent; + struct acpi_attach_args *aaa = aux; + struct acpi_madt *madt = (struct acpi_madt *)aaa->aaa_table; + caddr_t addr = (caddr_t)(madt + 1); + struct mp_intr_map *map; + struct ioapic_softc *apic; + int pin; + + printf(" addr 0x%x", madt->local_apic_address); + if (madt->flags & ACPI_APIC_PCAT_COMPAT) + printf(": PC-AT compat"); + printf("\n"); + + mp_busses = acpimadt_busses; + mp_isa_bus = &acpimadt_isa_bus; + + lapic_boot_init(madt->local_apic_address); + + while (addr < (caddr_t)madt + madt->hdr.length) { + union acpi_madt_entry *entry = (union acpi_madt_entry *)addr; + + switch(entry->madt_lapic.apic_type) { + case ACPI_MADT_LAPIC: + printf("LAPIC: acpi_proc_id %x, apic_id %x, flags 0x%x\n", + entry->madt_lapic.acpi_proc_id, + entry->madt_lapic.apic_id, + entry->madt_lapic.flags); + { + struct cpu_attach_args caa; + + if ((entry->madt_lapic.flags & ACPI_PROC_ENABLE) == 0) + break; + + memset(&caa, 0, sizeof(struct cpu_attach_args)); + if (entry->madt_lapic.apic_id == 0) + caa.cpu_role = CPU_ROLE_BP; + else + caa.cpu_role = CPU_ROLE_AP; + caa.caa_name = "cpu"; + caa.cpu_number = entry->madt_lapic.apic_id; + caa.cpu_func = &mp_cpu_funcs; + extern int cpu_id, cpu_feature; + caa.cpu_signature = cpu_id; + caa.feature_flags = cpu_feature; + + config_found(mainbus, &caa, acpimadt_print); + } + break; + case ACPI_MADT_IOAPIC: + printf("IOAPIC: acpi_ioapic_id %x, address 0x%x, global_int_base 0x%x\n", + entry->madt_ioapic.acpi_ioapic_id, + entry->madt_ioapic.address, + entry->madt_ioapic.global_int_base); + + { + struct apic_attach_args aaa; + + memset(&aaa, 0, sizeof(struct apic_attach_args)); + aaa.aaa_name = "ioapic"; + aaa.apic_id = entry->madt_ioapic.acpi_ioapic_id; + aaa.apic_address = entry->madt_ioapic.address; + aaa.apic_vecbase = entry->madt_ioapic.global_int_base; + + config_found(mainbus, &aaa, acpimadt_print); + } + break; + + case ACPI_MADT_OVERRIDE: + printf("OVERRIDE: bus %x, source %x, global_int %x, flags %x\n", + entry->madt_override.bus, + entry->madt_override.source, + entry->madt_override.global_int, + entry->madt_override.flags); + + pin = entry->madt_override.global_int; + apic = ioapic_find_bybase(pin); + + map = malloc(sizeof (struct mp_intr_map), M_DEVBUF, M_NOWAIT); + if (map == NULL) + return; + + memset(map, 0, sizeof *map); + map->bus_pin = entry->madt_override.source; + map->flags = entry->madt_override.flags; + acpimadt_cfg_intr(entry->madt_override.flags, &map->redir); + + map->ioapic_ih = APIC_INT_VIA_APIC | + ((apic->sc_apicid << APIC_INT_APIC_SHIFT) | (pin << APIC_INT_PIN_SHIFT)); + + apic->sc_pins[pin].ip_map = map; + + map->next = mp_isa_bus->mb_intrs; + mp_isa_bus->mb_intrs = map; + break; + + default: + printf("apic_type %x\n", entry->madt_lapic.apic_type); + } + + addr += entry->madt_lapic.length; + } + + for (pin = 0; pin < ICU_LEN; pin++) { + apic = ioapic_find_bybase(pin); + if (apic->sc_pins[pin].ip_map != NULL) + continue; + + map = malloc(sizeof (struct mp_intr_map), M_DEVBUF, M_NOWAIT); + if (map == NULL) + return; + + memset(map, 0, sizeof *map); + map->bus_pin = pin; + map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT); + + map->ioapic_ih = APIC_INT_VIA_APIC | + ((apic->sc_apicid << APIC_INT_APIC_SHIFT) | (pin << APIC_INT_PIN_SHIFT)); + + apic->sc_pins[pin].ip_map = map; + + map->next = mp_isa_bus->mb_intrs; + mp_isa_bus->mb_intrs = map; + } +} + +int +acpimadt_print(void *aux, const char *pnp) +{ + struct apic_attach_args *aaa = aux; + + if (pnp) + printf("%s at %s:", aaa->aaa_name, pnp); + + return (UNCONF); +} diff --git a/sys/dev/acpi/acpiprt.c b/sys/dev/acpi/acpiprt.c new file mode 100644 index 00000000000..a2c6fdcf259 --- /dev/null +++ b/sys/dev/acpi/acpiprt.c @@ -0,0 +1,214 @@ +/* $OpenBSD: acpiprt.c,v 1.1 2006/11/15 21:39:06 kettenis Exp $ */ +/* + * Copyright (c) 2006 Mark Kettenis <kettenis@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <machine/bus.h> + +#include <dev/acpi/acpireg.h> +#include <dev/acpi/acpivar.h> +#include <dev/acpi/acpidev.h> +#include <dev/acpi/amltypes.h> +#include <dev/acpi/dsdt.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/ppbreg.h> + +#include <machine/i82093reg.h> +#include <machine/i82093var.h> + +#include <machine/mpbiosvar.h> + +int acpiprt_match(struct device *, void *, void *); +void acpiprt_attach(struct device *, struct device *, void *); + +struct acpiprt_softc { + struct device sc_dev; + + struct acpi_softc *sc_acpi; + struct aml_node *sc_devnode; + + int sc_bus; +}; + +struct cfattach acpiprt_ca = { + sizeof(struct acpiprt_softc), acpiprt_match, acpiprt_attach +}; + +struct cfdriver acpiprt_cd = { + NULL, "acpiprt", DV_DULL +}; + +void acpiprt_prt_add(struct acpiprt_softc *, struct aml_value *); +int acpiprt_getpcibus(struct acpiprt_softc *, struct aml_node *); + +int +acpiprt_match(struct device *parent, void *match, void *aux) +{ + struct acpi_attach_args *aa = aux; + struct cfdata *cf = match; + + /* sanity */ + if (aa->aaa_name == NULL || + strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 || + aa->aaa_table != NULL) + return (0); + + return (1); +} + +void +acpiprt_attach(struct device *parent, struct device *self, void *aux) +{ + struct acpiprt_softc *sc = (struct acpiprt_softc *)self; + struct acpi_attach_args *aa = aux; + struct aml_value res; + int i; + + sc->sc_acpi = (struct acpi_softc *)parent; + sc->sc_devnode = aa->aaa_node->child; + sc->sc_bus = acpiprt_getpcibus(sc, sc->sc_devnode); + + printf(": bus %d (%s)", sc->sc_bus, sc->sc_devnode->parent->name); + + if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PRT", 0, NULL, &res)) { + printf(": no PCI interrupt routing table\n"); + return; + } + + if (res.type != AML_OBJTYPE_PACKAGE) { + printf(": _PRT is not a package\n"); + return; + } + + printf("\n"); + + for (i = 0; i < res.length; i++) + acpiprt_prt_add(sc, res.v_package[i]); +} + +void +acpiprt_prt_add(struct acpiprt_softc *sc, struct aml_value *v) +{ + struct aml_node *node; + struct aml_value res; + int addr, pin, irq; + struct mp_intr_map *map; + struct ioapic_softc *apic; + + if (v->type != AML_OBJTYPE_PACKAGE || v->length != 4) { + printf("invalid mapping object\n"); + return; + } + + addr = aml_val2int(v->v_package[0]); + pin = aml_val2int(v->v_package[1]); + + if (v->v_package[2]->type == AML_OBJTYPE_NAMEREF || + v->v_package[2]->type == AML_OBJTYPE_DEVICE) { + if (v->v_package[2]->type == AML_OBJTYPE_NAMEREF) + node = aml_searchname(sc->sc_devnode, + v->v_package[2]->v_nameref); + else + node = v->v_package[2]->node; + if (node == NULL) { + printf(" invalid node!\n"); + return; + } + + if (aml_evalname(sc->sc_acpi, node, "_STA", 0, NULL, NULL)) { + printf("no _STA method\n"); + } + + if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) { + printf("no _CRS method\n"); + } + + if (res.type != AML_OBJTYPE_BUFFER || res.length < 6) { + printf("invalid _CRS object\n"); + return; + } + + if ((res.v_buffer[0] >> 3) == 0x4) { + irq = res.v_buffer[1] + (res.v_buffer[2] << 8); + irq = ffs(irq) - 1; + } + } else + irq = aml_val2int(v->v_package[3]); + + apic = ioapic_find_bybase(irq); + + map = malloc(sizeof (struct mp_intr_map), M_DEVBUF, M_NOWAIT); + if (map == NULL) + return; + +#if ACPI_DEBUG + printf("%s: addr 0x%x pin %d irq %d\n", DEVNAME(sc), addr, pin, irq); +#endif + + memset(map, 0, sizeof *map); + map->bus_pin = ((addr >> 14) & 0x7c) | (pin & 0x3); + map->redir = IOAPIC_REDLO_ACTLO | IOAPIC_REDLO_LEVEL; + map->redir |= (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT); + + map->ioapic_ih = APIC_INT_VIA_APIC | + ((apic->sc_apicid << APIC_INT_APIC_SHIFT) | (irq << APIC_INT_PIN_SHIFT)); + + apic->sc_pins[irq].ip_map = map; + + map->next = mp_busses[sc->sc_bus].mb_intrs; + mp_busses[sc->sc_bus].mb_intrs = map; +} + +int +acpiprt_getpcibus(struct acpiprt_softc *sc, struct aml_node *node) +{ + struct aml_node *parent = node->parent; + struct aml_value res; + pci_chipset_tag_t pc = NULL; + pcitag_t tag; + pcireg_t reg; + int bus, dev, func; + + if (parent == NULL) + return 0; + + if (aml_evalname(sc->sc_acpi, parent, "_ADR", 0, NULL, &res) == 0) { + bus = acpiprt_getpcibus(sc, parent); + dev = ACPI_PCI_DEV(aml_val2int(&res) << 16); + func = ACPI_PCI_FN(aml_val2int(&res) << 16); + tag = pci_make_tag(pc, bus, dev, func); + + reg = pci_conf_read(pc, tag, PCI_CLASS_REG); + if (PCI_CLASS(reg) == PCI_CLASS_BRIDGE && + PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_PCI) { + reg = pci_conf_read(pc, tag, PPB_REG_BUSINFO); + return PPB_BUSINFO_SECONDARY(reg); + } + } + + if (aml_evalname(sc->sc_acpi, parent, "_BBN", 0, NULL, &res) == 0) { + return aml_val2int(&res); + } + + return 0; +} diff --git a/sys/dev/acpi/files.acpi b/sys/dev/acpi/files.acpi index 53018f9e704..906c6595675 100644 --- a/sys/dev/acpi/files.acpi +++ b/sys/dev/acpi/files.acpi @@ -1,4 +1,4 @@ -# $OpenBSD: files.acpi,v 1.12 2006/05/29 00:54:23 canacar Exp $ +# $OpenBSD: files.acpi,v 1.13 2006/11/15 21:39:06 kettenis Exp $ # # Config file and device description for machine-independent ACPI code. # Included by ports that need it. @@ -50,3 +50,13 @@ file dev/acpi/acpiec.c acpiec device acpitz attach acpitz at acpi file dev/acpi/acpitz.c acpitz + +# Multiple APIC Description Table +device acpimadt +attach acpimadt at acpi +file dev/acpi/acpimadt.c acpimadt + +# PCI Routing Table +device acpiprt +attach acpiprt at acpi +file dev/acpi/acpiprt.c acpiprt needs-flag |