summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2006-11-15 21:39:07 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2006-11-15 21:39:07 +0000
commit4cd25a3aad0e44c23128ae417e6a4d9b02201940 (patch)
tree12762b2fcd95e4ef4129d8cd2a5c5f48c37bcd9f /sys
parentd3f0e57c47e6176c8c34356a2c7fd5ffc1bd396f (diff)
dd pseudo-devices to handle acpi apic interrupt routing.
ok jordan@
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/acpi/acpimadt.c249
-rw-r--r--sys/dev/acpi/acpiprt.c214
-rw-r--r--sys/dev/acpi/files.acpi12
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