diff --git a/sys/dev/acpica5/Makefile b/sys/dev/acpica5/Makefile index 958323d..ff7f8f9 100644 --- a/sys/dev/acpica5/Makefile +++ b/sys/dev/acpica5/Makefile @@ -69,7 +69,7 @@ SRCS+= acpi_package.c # SRCS+= acpi_pci.c acpi_pcib.c acpi_pcib_acpi.c acpi_pcib_pci.c # SRCS+= acpi_pci_link.c SRCS+= acpi_powerres.c acpi_resource.c acpi_thermal.c -SRCS+= acpi_timer.c acpi_if.c +SRCS+= acpi_timer.c acpi_hpet.c acpi_if.c SRCS+= OsdDebug.c SRCS+= OsdHardware.c OsdInterface.c OsdInterrupt.c OsdMemory.c OsdSchedule.c SRCS+= OsdStream.c OsdSynch.c OsdTable.c OsdEnvironment.c diff --git a/sys/dev/acpica5/acpi_hpet.c b/sys/dev/acpica5/acpi_hpet.c new file mode 100644 index 0000000..df01047 --- /dev/null +++ b/sys/dev/acpica5/acpi_hpet.c @@ -0,0 +1,349 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp + * 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: src/sys/dev/acpica/acpi_hpet.c,v 1.12.2.1.2.1 2008/11/25 02:59:29 kensmith Exp $ + */ + +#include "opt_acpi.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "acpi.h" +#include "acpivar.h" +#include "acpi_hpet.h" + +/* Hooks for the ACPI CA debugging infrastructure */ +#define _COMPONENT ACPI_TIMER +ACPI_MODULE_NAME("HPET") + +static bus_space_handle_t acpi_hpet_bsh; +static bus_space_tag_t acpi_hpet_bst; + +struct acpi_hpet_softc { + device_t dev; + struct resource *mem_res; + ACPI_HANDLE handle; +}; + +#define DEV_HPET(x) (acpi_get_magic(x) == (uintptr_t)&acpi_hpet_devclass) + +static int acpi_hpet_resume(device_t dev); +static int acpi_hpet_suspend(device_t dev); +static void hpet_enable(struct acpi_hpet_softc *sc); +static void hpet_disable(struct acpi_hpet_softc *sc); + +static sysclock_t acpi_hpet_get_timecount(void); +static void acpi_hpet_construct(struct cputimer *timer, sysclock_t oldclock); + +static char *hpet_ids[] = { "PNP0103", NULL }; + +static struct cputimer acpi_hpet_timer = { + SLIST_ENTRY_INITIALIZER, + "HPET", + CPUTIMER_PRI_ACPI, + CPUTIMER_ACPI, + acpi_hpet_get_timecount, + cputimer_default_fromhz, + cputimer_default_fromus, + acpi_hpet_construct, + cputimer_default_destruct, + 900, + 0, 0, 0 +}; + +static int acpi_hpet_identify(driver_t *driver, device_t parent); +static int acpi_hpet_probe(device_t dev); +static int acpi_hpet_attach(device_t dev); + +static void acpi_hpet_test(struct acpi_hpet_softc *sc); + +static device_method_t acpi_hpet_methods[] = { + DEVMETHOD(device_identify, acpi_hpet_identify), + DEVMETHOD(device_probe, acpi_hpet_probe), + DEVMETHOD(device_attach, acpi_hpet_attach), + DEVMETHOD(device_suspend, acpi_hpet_suspend), + DEVMETHOD(device_resume, acpi_hpet_resume), + {0, 0} +}; + +static driver_t acpi_hpet_driver = { + "acpi_hpet", + acpi_hpet_methods, + 0, +}; + +static devclass_t acpi_hpet_devclass; +DRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0); +MODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1); + +static u_int +acpi_hpet_read(void) +{ + return (bus_space_read_4(acpi_hpet_bst, acpi_hpet_bsh, HPET_MAIN_COUNTER)); +} + +/* + * Locate the ACPI timer using the FADT, set up and allocate the I/O resources + * we will be using. + */ +static int +acpi_hpet_identify(driver_t *driver, device_t parent) +{ + ACPI_TABLE_HPET *hpet; + ACPI_TABLE_HEADER *hdr; + ACPI_STATUS status; + device_t child; + + /* Only one HPET device can be added. */ + if (devclass_get_device(acpi_hpet_devclass, 0)) + return (ENXIO); + + /* Currently, ID and minimum clock tick info is unused. */ + + status = AcpiGetTable(ACPI_SIG_HPET, 1, (ACPI_TABLE_HEADER **)&hdr); + if (ACPI_FAILURE(status)) + return (ENXIO); + + /* + * The unit number could be derived from hdr->Sequence but we only + * support one HPET device. + */ + hpet = (ACPI_TABLE_HPET *)hdr; + if (hpet->Sequence != 0) + kprintf("ACPI HPET table warning: Sequence is non-zero (%d)\n", + hpet->Sequence); + child = BUS_ADD_CHILD(parent, parent, 0, "acpi_hpet", 0); + if (child == NULL) { + kprintf("%s: can't add child\n", __func__); + return (ENXIO); + } + + /* Record a magic value so we can detect this device later. */ + acpi_set_magic(child, (uintptr_t)&acpi_hpet_devclass); + bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address, + HPET_MEM_WIDTH); + return (0); +} + +static int +acpi_hpet_probe(device_t dev) +{ + ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); + + if (!acpi_enabled("hpet")) + return (ENXIO); + if (!DEV_HPET(dev) && + (ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL || + device_get_unit(dev) != 0)) + return (ENXIO); + + device_set_desc(dev, "High Precision Event Timer"); + return (0); +} + +static int +acpi_hpet_attach(device_t dev) +{ + struct acpi_hpet_softc *sc; + int rid; + uint32_t val, val2; + uintmax_t freq; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); + + sc = device_get_softc(dev); + sc->dev = dev; + sc->handle = acpi_get_handle(dev); + + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) + return (ENOMEM); + + /* Validate that we can access the whole region. */ + if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) { + device_printf(dev, "memory region width %ld too small\n", + rman_get_size(sc->mem_res)); + bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res); + return (ENXIO); + } + + acpi_hpet_bsh = rman_get_bushandle(sc->mem_res); + acpi_hpet_bst = rman_get_bustag(sc->mem_res); + + /* Be sure timer is enabled. */ + hpet_enable(sc); + + /* Read basic statistics about the timer. */ + val = bus_read_4(sc->mem_res, HPET_PERIOD); + if (val == 0) { + device_printf(dev, "invalid period\n"); + hpet_disable(sc); + bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res); + return (ENXIO); + } + + freq = (1000000000000000LL + val / 2) / val; + if (bootverbose) { + val = bus_read_4(sc->mem_res, HPET_CAPABILITIES); + device_printf(dev, + "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n", + val >> 16, val & HPET_CAP_REV_ID, + (val & HPET_CAP_NUM_TIM) >> 8, freq, + (val & HPET_CAP_LEG_RT) ? " legacy_route" : "", + (val & HPET_CAP_COUNT_SIZE) ? " 64-bit" : ""); + } + + if (ktestenv("debug.acpi.hpet_test")) + acpi_hpet_test(sc); + + /* + * Don't attach if the timer never increments. Since the spec + * requires it to be at least 10 MHz, it has to change in 1 us. + */ + val = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER); + DELAY(1); + val2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER); + if (val == val2) { + device_printf(dev, "HPET never increments, disabling\n"); + hpet_disable(sc); + bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->mem_res); + return (ENXIO); + } + + acpi_hpet_timer.freq = freq; + + cputimer_register(&acpi_hpet_timer); + +#if 0 +/* We don't want setting HEPT default timer*/ + cputimer_select(&acpi_hpet_timer, 0); +#endif + + return (0); + +} + +/* + * Construct the timer. Adjust the base so the system clock does not + * jump weirdly. + */ +static void +acpi_hpet_construct(struct cputimer *timer, sysclock_t oldclock) +{ + timer->base = 0; + timer->base = oldclock - acpi_hpet_get_timecount(); +} + +static sysclock_t +acpi_hpet_get_timecount(void) +{ + return acpi_hpet_read() + acpi_hpet_timer.base; +} + +static void +hpet_enable(struct acpi_hpet_softc *sc) +{ + uint32_t val; + + val = bus_read_4(sc->mem_res, HPET_CONFIG); + bus_write_4(sc->mem_res, HPET_CONFIG, val | HPET_CNF_ENABLE); +} + +static void +hpet_disable(struct acpi_hpet_softc *sc) +{ + uint32_t val; + + val = bus_read_4(sc->mem_res, HPET_CONFIG); + bus_write_4(sc->mem_res, HPET_CONFIG, val & ~HPET_CNF_ENABLE); +} + +static int +acpi_hpet_suspend(device_t dev) +{ + struct acpi_hpet_softc *sc; + + /* + * Disable the timer during suspend. The timer will not lose + * its state in S1 or S2, but we are required to disable + * it. + */ + sc = device_get_softc(dev); + hpet_disable(sc); + + return (0); +} + +static int +acpi_hpet_resume(device_t dev) +{ + struct acpi_hpet_softc *sc; + + /* Re-enable the timer after a resume to keep the clock advancing. */ + sc = device_get_softc(dev); + hpet_enable(sc); + + return (0); +} + +/* Print some basic latency/rate information to assist in debugging. */ +static void +acpi_hpet_test(struct acpi_hpet_softc *sc) +{ + int i; + uint32_t u1, u2; + struct timeval b0, b1, b2; + struct timespec ts; + + microuptime(&b0); + microuptime(&b0); + microuptime(&b1); + u1 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER); + for (i = 1; i < 1000; i++) + u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER); + microuptime(&b2); + u2 = bus_read_4(sc->mem_res, HPET_MAIN_COUNTER); + + timevalsub(&b2, &b1); + timevalsub(&b1, &b0); + timevalsub(&b2, &b1); + + TIMEVAL_TO_TIMESPEC(&b2, &ts); + + device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n", + (long)b2.tv_sec, b2.tv_usec, u1, u2, u2 - u1); + + device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000); +} diff --git a/sys/dev/acpica5/acpi_hpet.h b/sys/dev/acpica5/acpi_hpet.h new file mode 100644 index 0000000..f66c6c5 --- /dev/null +++ b/sys/dev/acpica5/acpi_hpet.h @@ -0,0 +1,71 @@ +/*- + * Copyright (c) 2005 Poul-Henning Kamp + * 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: src/sys/dev/acpica/acpi_hpet.h,v 1.1.2.1.2.1 2008/11/25 02:59:29 kensmith Exp $ + */ + +#ifndef __ACPI_HPET_H__ +#define __ACPI_HPET_H__ + +#define HPET_MEM_WIDTH 0x400 /* Expected memory region size */ + +/* General registers */ +#define HPET_CAPABILITIES 0x0 /* General capabilities and ID */ +#define HPET_CAP_VENDOR_ID 0xffff0000 +#define HPET_CAP_LEG_RT 0x00008000 +#define HPET_CAP_COUNT_SIZE 0x00002000 /* 1 = 64-bit, 0 = 32-bit */ +#define HPET_CAP_NUM_TIM 0x00001f00 +#define HPET_CAP_REV_ID 0x000000ff +#define HPET_PERIOD 0x4 /* Period (1/hz) of timer */ +#define HPET_CONFIG 0x10 /* General configuration register */ +#define HPET_CNF_LEG_RT 0x00000002 +#define HPET_CNF_ENABLE 0x00000001 +#define HPET_ISR 0x20 /* General interrupt status register */ +#define HPET_MAIN_COUNTER 0xf0 /* Main counter register */ + +/* Timer registers */ +#define HPET_TIMER_CAP_CNF(x) ((x) * 0x20 + 0x100) +#define HPET_TCAP_INT_ROUTE 0xffffffff00000000 +#define HPET_TCAP_FSB_INT_DEL 0x00008000 +#define HPET_TCNF_FSB_EN 0x00004000 +#define HPET_TCNF_INT_ROUTE 0x00003e00 +#define HPET_TCNF_32MODE 0x00000100 +#define HPET_TCNF_VAL_SET 0x00000040 +#define HPET_TCAP_SIZE 0x00000020 /* 1 = 64-bit, 0 = 32-bit */ +#define HPET_TCAP_PER_INT 0x00000010 /* Supports periodic interrupts */ +#define HPET_TCNF_TYPE 0x00000008 /* 1 = periodic, 0 = one-shot */ +#define HPET_TCNF_INT_ENB 0x00000004 +#define HPET_TCNT_INT_TYPE 0x00000002 /* 1 = level triggered, 0 = edge */ +#define HPET_TIMER_COMPARATOR(x) ((x) * 0x20 + 0x108) +#define HPET_TIMER_FSB_VAL(x) ((x) * 0x20 + 0x110) +#define HPET_TIMER_FSB_ADDR(x) ((x) * 0x20 + 0x114) + + +#define bus_read_4(r, o) \ + bus_space_read_4((r)->r_bustag, (r)->r_bushandle, (o)) +#define bus_write_4(r, o, v) \ + bus_space_write_4((r)->r_bustag, (r)->r_bushandle, (o), (v)) + +#endif /* !__ACPI_HPET_H__ */