diff options
Diffstat (limited to 'sys/dev/acpica/acpi_apic.c')
-rw-r--r-- | sys/dev/acpica/acpi_apic.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/sys/dev/acpica/acpi_apic.c b/sys/dev/acpica/acpi_apic.c new file mode 100644 index 0000000..1a83b1d --- /dev/null +++ b/sys/dev/acpica/acpi_apic.c @@ -0,0 +1,159 @@ +/*- + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 BSDi + * 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. + * + * $FreeBSD$ + */ + +/* + * XXX This is all pretty dubious, since we really want the APIC and co. + * up and running long before attaching interrupts, etc. + */ + +#include "opt_acpi.h" +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/bus.h> + +#include "acpi.h" + +#include <dev/acpica/acpivar.h> + +#define APIC_MAGIC 0x43495041 /* "APIC" */ + +struct acpi_apic_softc { + device_t apic_dev; + IO_APIC *apic_ioapic; +}; + +static void acpi_apic_identify(driver_t *driver, device_t bus); +static int acpi_apic_probe(device_t dev); +static int acpi_apic_attach(device_t dev); + +static device_method_t acpi_apic_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, acpi_apic_identify), + DEVMETHOD(device_probe, acpi_apic_probe), + DEVMETHOD(device_attach, acpi_apic_attach), + + {0, 0} +}; + +static driver_t acpi_apic_driver = { + "acpi_apic", + acpi_apic_methods, + sizeof(struct acpi_apic_softc), +}; + +devclass_t acpi_apic_devclass; +DRIVER_MODULE(acpi_apic, acpi, acpi_apic_driver, acpi_apic_devclass, 0, 0); + +static void +acpi_apic_identify(driver_t *driver, device_t bus) +{ + ACPI_BUFFER buf; + ACPI_STATUS status; + APIC_HEADER *hdr; + APIC_TABLE *tbl; + device_t child; + int len; + void *private; + + /* + * Perform the tedious double-get to fetch the actual table. + */ + buf.Length = 0; + buf.Pointer = NULL; + if ((status = AcpiGetTable(ACPI_TABLE_APIC, 1, &buf)) != AE_BUFFER_OVERFLOW) { + if (status != AE_NOT_EXIST) + device_printf(bus, "error sizing APIC table - %s\n", acpi_strerror(status)); + return; + } + if ((buf.Pointer = AcpiOsAllocate(buf.Length)) == NULL) + return; + if ((status = AcpiGetTable(ACPI_TABLE_APIC, 1, &buf)) != AE_OK) { + device_printf(bus, "error fetching APIC table - %s\n", acpi_strerror(status)); + return; + } + + /* + * Scan the tables, create child devices for each I/O APIC found + */ + tbl = (APIC_TABLE *)buf.Pointer; + len = tbl->header.Length - sizeof(APIC_TABLE); + hdr = (APIC_HEADER *)((char *)buf.Pointer + sizeof(APIC_TABLE)); + while(len > 0) { + if (hdr->Length > len) { + device_printf(bus, "APIC header corrupt (claims %d bytes where only %d left in structure)\n", + hdr->Length, len); + break; + } + switch (hdr->Type) { + case APIC_IO: + if ((child = BUS_ADD_CHILD(bus, 0, "acpi_apic", -1)) == NULL) { + device_printf(bus, "could not create I/O APIC device"); + break; + } + if ((private = AcpiOsAllocate(hdr->Length)) == NULL) { + device_printf(bus, "could not allocate memory for APIC child"); + break; + } + bcopy(hdr, private, hdr->Length); + acpi_set_magic(child, APIC_MAGIC); + acpi_set_private(child, private); + device_set_desc(child, "I/O APIC"); + break; + } + len -= hdr->Length; + hdr = (APIC_HEADER *)((char *)hdr + hdr->Length); + } + + AcpiOsFree(buf.Pointer); +} + +static int +acpi_apic_probe(device_t dev) +{ + if (acpi_get_magic(dev) == APIC_MAGIC) + return(0); + return(ENXIO); +} + +static int +acpi_apic_attach(device_t dev) +{ + struct acpi_apic_softc *sc; + + sc = device_get_softc(dev); + sc->apic_dev = dev; + + /* + * Fetch our parameters. + */ + sc->apic_ioapic = acpi_get_private(dev); + device_printf(dev, "I/O APIC ID %d at 0x%08x vectors 0%x\n", + sc->apic_ioapic->IoApicId, sc->apic_ioapic->IoApicAddress, sc->apic_ioapic->Vector); + return(0); +} |