/* $OpenBSD: acpi_machdep.c,v 1.6 2007/02/22 07:39:55 jordan Exp $ */
/*
* Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com>
*
* 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/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <uvm/uvm_extern.h>
#include <machine/bus.h>
#include <machine/biosvar.h>
#include <dev/isa/isareg.h>
#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
#include "ioapic.h"
#if NIOAPIC > 0
#include <machine/i82093var.h>
#include <machine/mpbiosvar.h>
#endif
#define ACPI_BIOS_RSDP_WINDOW_BASE 0xe0000
#define ACPI_BIOS_RSDP_WINDOW_SIZE 0x20000
u_int8_t *acpi_scan(struct acpi_mem_map *, paddr_t, size_t);
int
acpi_map(paddr_t pa, size_t len, struct acpi_mem_map *handle)
{
paddr_t pgpa = trunc_page(pa);
paddr_t endpa = round_page(pa + len);
vaddr_t va = uvm_km_valloc(kernel_map, endpa - pgpa);
if (va == 0)
return (ENOMEM);
handle->baseva = va;
handle->va = (u_int8_t *)(va + (pa & PGOFSET));
handle->vsize = endpa - pgpa;
handle->pa = pa;
do {
pmap_kenter_pa(va, pgpa, VM_PROT_READ | VM_PROT_WRITE);
va += NBPG;
pgpa += NBPG;
} while (pgpa < endpa);
return 0;
}
void
acpi_unmap(struct acpi_mem_map *handle)
{
pmap_kremove(handle->baseva, handle->vsize);
uvm_km_free(kernel_map, handle->baseva, handle->vsize);
}
u_int8_t *
acpi_scan(struct acpi_mem_map *handle, paddr_t pa, size_t len)
{
size_t i;
u_int8_t *ptr;
struct acpi_rsdp1 *rsdp;
if (acpi_map(pa, len, handle))
return (NULL);
for (ptr = handle->va, i = 0;
i < len;
ptr += 16, i += 16)
if (memcmp(ptr, RSDP_SIG, sizeof(RSDP_SIG) - 1) == 0) {
rsdp = (struct acpi_rsdp1 *)ptr;
/*
* Only checksum whichever portion of the
* RSDP that is actually present
*/
if (rsdp->revision == 0 &&
acpi_checksum(ptr, sizeof(struct acpi_rsdp1)) == 0)
return (ptr);
else if (rsdp->revision == 2 &&
acpi_checksum(ptr, sizeof(struct acpi_rsdp)) == 0)
return (ptr);
}
acpi_unmap(handle);
return (NULL);
}
int
acpi_probe(struct device *parent, struct cfdata *match, struct acpi_attach_args *aaa)
{
struct acpi_mem_map handle;
u_int8_t *ptr;
paddr_t ebda;
bios_memmap_t *im;
/*
* First look for ACPI entries in the BIOS memory map
*/
for (im = bios_memmap; im->type != BIOS_MAP_END; im++)
if (im->type == BIOS_MAP_ACPI) {
if ((ptr = acpi_scan(&handle, im->addr, im->size)))
goto havebase;
}
/*
* Next try to find ACPI table entries in the EBDA
*/
if (acpi_map(0, NBPG, &handle))
printf("acpi: failed to map BIOS data area\n");
else {
ebda = *(const u_int16_t *)(&handle.va[0x40e]);
ebda <<= 4;
acpi_unmap(&handle);
if (ebda && ebda < IOM_BEGIN) {
if ((ptr = acpi_scan(&handle, ebda, 1024)))
goto havebase;
}
}
/*
* Finally try to find the ACPI table entries in the
* BIOS memory
*/
if ((ptr = acpi_scan(&handle, ACPI_BIOS_RSDP_WINDOW_BASE,
ACPI_BIOS_RSDP_WINDOW_SIZE)))
goto havebase;
return (0);
havebase:
aaa->aaa_pbase = ptr - handle.va + handle.pa;
acpi_unmap(&handle);
return (1);
}
void
acpi_attach_machdep(struct acpi_softc *sc)
{
#ifdef ACPI_ENABLE
struct pic *pic;
int pin;
int irq;
pic = &i8259_pic;
pin = sc->sc_fadt->sci_int;
irq = sc->sc_fadt->sci_int;
#if NIOAPIC > 0
pic = (struct pic *)ioapic_find_bybase(sc->sc_fadt->sci_int);
if (pic == NULL) {
printf("error: can't establish ACPI interrupt!\n");
return;
}
#endif
sc->sc_interrupt = intr_establish(irq, pic, pin, IST_LEVEL, IPL_TTY,
acpi_interrupt, sc, "acpi");
#endif
}