acpi_hpet.c revision 151912
1/*- 2 * Copyright (c) 2005 Poul-Henning Kamp 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_hpet.c 151912 2005-10-31 21:39:50Z phk $"); 29 30#include "opt_acpi.h" 31#include <sys/param.h> 32#include <sys/kernel.h> 33#include <sys/module.h> 34#include <sys/rman.h> 35#include <sys/time.h> 36#include <sys/timetc.h> 37#include <sys/bus.h> 38#include <contrib/dev/acpica/acpi.h> 39#include "acpi_if.h" 40#include <dev/acpica/acpivar.h> 41 42ACPI_SERIAL_DECL(hpet, "ACPI HPET support"); 43 44struct acpi_hpet_softc { 45 device_t dev; 46 struct resource *res[1]; 47 ACPI_HANDLE handle; 48}; 49 50static unsigned hpet_get_timecount(struct timecounter *tc); 51 52struct timecounter hpet_timecounter = { 53 .tc_get_timecount = hpet_get_timecount, 54 .tc_counter_mask = ~0u, 55 .tc_name = "HPET", 56 .tc_quality = -200, 57}; 58 59static char *hpet_ids[] = { "PNP0103", NULL }; 60 61static unsigned 62hpet_get_timecount(struct timecounter *tc) 63{ 64 struct acpi_hpet_softc *sc; 65 66 sc = tc->tc_priv; 67 return (bus_read_4(sc->res[0], 0xf0)); 68} 69 70static int 71acpi_hpet_probe(device_t dev) 72{ 73 if (acpi_disabled("hpet") || 74 ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL || 75 device_get_unit(dev) != 0) 76 return (ENXIO); 77 78 device_set_desc(dev, "HPET - High Precision Event Timers"); 79 return (0); 80} 81 82static struct resource_spec hpet_res_spec[] = { 83 { SYS_RES_MEMORY, 0, RF_ACTIVE}, 84 { -1, 0, 0} 85}; 86 87static int 88acpi_hpet_attach(device_t dev) 89{ 90 struct acpi_hpet_softc *sc; 91 int error; 92 uint32_t u; 93 94 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 95 96 sc = device_get_softc(dev); 97 sc->dev = dev; 98 sc->handle = acpi_get_handle(dev); 99 100 error = bus_alloc_resources(dev, hpet_res_spec, sc->res); 101 if (error) 102 return (error); 103 104 u = bus_read_4(sc->res[0], 0x0); 105 device_printf(dev, "Vendor: 0x%x\n", u >> 16); 106 device_printf(dev, "Leg_Route_Cap: %d\n", (u >> 15) & 1); 107 device_printf(dev, "Count_Size_Cap: %d\n", (u >> 13) & 1); 108 device_printf(dev, "Num_Tim_Cap: %d\n", (u >> 18) & 0xf); 109 device_printf(dev, "Rev_id: 0x%x\n", u & 0xff); 110 111 u = bus_read_4(sc->res[0], 0x4); 112 device_printf(dev, "Period: %d fs (%jd Hz)\n", 113 u, (intmax_t)((1000000000000000LL + u / 2) / u)); 114 115 hpet_timecounter.tc_frequency = (1000000000000000LL + u / 2) / u; 116 117 bus_write_4(sc->res[0], 0x10, 1); 118 119#if 0 120 { 121 int i; 122 uint32_t u1, u2; 123 struct bintime b0, b1, b2; 124 struct timespec ts; 125 126 binuptime(&b0); 127 binuptime(&b0); 128 binuptime(&b1); 129 u1 = bus_read_4(sc->res[0], 0xf0); 130 for (i = 1; i < 1000; i++) 131 u2 = bus_read_4(sc->res[0], 0xf0); 132 binuptime(&b2); 133 u2 = bus_read_4(sc->res[0], 0xf0); 134 135 bintime_sub(&b2, &b1); 136 bintime_sub(&b1, &b0); 137 bintime_sub(&b2, &b1); 138 bintime2timespec(&b2, &ts); 139 140 device_printf(dev, "%ld.%09ld: %u ... %u = %u\n", 141 (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1); 142 143 device_printf(dev, "time per call: %ld ns\n", ts.tv_nsec / 1000); 144 } 145#endif 146 147 device_printf(sc->dev, "HPET attach\n"); 148 149 hpet_timecounter.tc_priv = sc; 150 151 tc_init(&hpet_timecounter); 152 153 return (0); 154} 155 156static int 157acpi_hpet_detach(device_t dev) 158{ 159 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 160 161#if 1 162 return (EBUSY); 163#else 164 struct acpi_hpet_softc *sc = device_get_softc(dev); 165 bus_release_resources(dev, hpet_res_spec, sc->res); 166 167 device_printf(sc->dev, "HPET detach\n"); 168 return (0); 169#endif 170} 171 172static device_method_t acpi_hpet_methods[] = { 173 /* Device interface */ 174 DEVMETHOD(device_probe, acpi_hpet_probe), 175 DEVMETHOD(device_attach, acpi_hpet_attach), 176 DEVMETHOD(device_detach, acpi_hpet_detach), 177 178 {0, 0} 179}; 180 181static driver_t acpi_hpet_driver = { 182 "acpi_hpet", 183 acpi_hpet_methods, 184 sizeof(struct acpi_hpet_softc), 185}; 186 187static devclass_t acpi_hpet_devclass; 188 189DRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0); 190MODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1); 191