acpi_hpet.c revision 175361
155714Skris/*- 255714Skris * Copyright (c) 2005 Poul-Henning Kamp 355714Skris * All rights reserved. 455714Skris * 555714Skris * Redistribution and use in source and binary forms, with or without 655714Skris * modification, are permitted provided that the following conditions 755714Skris * are met: 8280297Sjkim * 1. Redistributions of source code must retain the above copyright 955714Skris * notice, this list of conditions and the following disclaimer. 1055714Skris * 2. Redistributions in binary form must reproduce the above copyright 1155714Skris * notice, this list of conditions and the following disclaimer in the 1255714Skris * documentation and/or other materials provided with the distribution. 1355714Skris * 1455714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15280297Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1655714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1755714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22280297Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2455714Skris * SUCH DAMAGE. 2555714Skris */ 2655714Skris 2755714Skris#include <sys/cdefs.h> 2855714Skris__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_hpet.c 175361 2008-01-15 18:50:47Z jhb $"); 2955714Skris 3055714Skris#include "opt_acpi.h" 3155714Skris#include <sys/param.h> 3255714Skris#include <sys/bus.h> 3355714Skris#include <sys/kernel.h> 3455714Skris#include <sys/module.h> 3555714Skris#include <sys/rman.h> 3655714Skris#include <sys/time.h> 37280297Sjkim#include <sys/timetc.h> 3855714Skris 3955714Skris#include <contrib/dev/acpica/acpi.h> 40280297Sjkim#include <dev/acpica/acpivar.h> 4155714Skris 4255714SkrisACPI_SERIAL_DECL(hpet, "ACPI HPET support"); 4355714Skris 4455714Skrisstatic devclass_t acpi_hpet_devclass; 4555714Skris 4655714Skris/* ACPI CA debugging */ 4755714Skris#define _COMPONENT ACPI_TIMER 4855714SkrisACPI_MODULE_NAME("HPET") 4955714Skris 5055714Skrisstruct acpi_hpet_softc { 5155714Skris device_t dev; 52280297Sjkim struct resource *mem_res; 5355714Skris ACPI_HANDLE handle; 5455714Skris}; 5555714Skris 5655714Skrisstatic u_int hpet_get_timecount(struct timecounter *tc); 5755714Skrisstatic void acpi_hpet_test(struct acpi_hpet_softc *sc); 5855714Skris 59280297Sjkimstatic char *hpet_ids[] = { "PNP0103", NULL }; 60280297Sjkim 61280297Sjkim#define HPET_MEM_WIDTH 0x400 /* Expected memory region size */ 62280297Sjkim#define HPET_OFFSET_INFO 0 /* Location of info in region */ 63280297Sjkim#define HPET_OFFSET_PERIOD 4 /* Location of period (1/hz) */ 64160814Ssimon#define HPET_OFFSET_ENABLE 0x10 /* Location of enable word */ 65280297Sjkim#define HPET_OFFSET_VALUE 0xf0 /* Location of actual timer value */ 66160814Ssimon 67160814Ssimon#define DEV_HPET(x) (acpi_get_magic(x) == (uintptr_t)&acpi_hpet_devclass) 68109998Smarkm 69280297Sjkimstruct timecounter hpet_timecounter = { 70280297Sjkim .tc_get_timecount = hpet_get_timecount, 71280297Sjkim .tc_counter_mask = ~0u, 72280297Sjkim .tc_name = "HPET", 73280297Sjkim .tc_quality = 900, 74280297Sjkim}; 75280297Sjkim 76280297Sjkimstatic u_int 77280297Sjkimhpet_get_timecount(struct timecounter *tc) 78280297Sjkim{ 79280297Sjkim struct acpi_hpet_softc *sc; 80280297Sjkim 8155714Skris sc = tc->tc_priv; 82280297Sjkim return (bus_read_4(sc->mem_res, HPET_OFFSET_VALUE)); 83280297Sjkim} 8455714Skris 85280297Sjkimstatic void 86280297Sjkimhpet_enable(struct acpi_hpet_softc *sc) 8755714Skris{ 88280297Sjkim uint32_t val; 89280297Sjkim 9055714Skris val = bus_read_4(sc->mem_res, HPET_OFFSET_ENABLE); 9155714Skris bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, val | 1); 9255714Skris} 9355714Skris 9455714Skrisstatic void 95160814Ssimonhpet_disable(struct acpi_hpet_softc *sc) 96160814Ssimon{ 97160814Ssimon uint32_t val; 9855714Skris 9955714Skris val = bus_read_4(sc->mem_res, HPET_OFFSET_ENABLE); 100280297Sjkim bus_write_4(sc->mem_res, HPET_OFFSET_ENABLE, val & ~1); 10159191Skris} 102160814Ssimon 103160814Ssimon/* Discover the HPET via the ACPI table of the same name. */ 104160814Ssimonstatic void 105280297Sjkimacpi_hpet_identify(driver_t *driver, device_t parent) 106280297Sjkim{ 107280297Sjkim ACPI_TABLE_HPET *hpet; 108160814Ssimon ACPI_TABLE_HEADER *hdr; 109280297Sjkim ACPI_STATUS status; 110160814Ssimon device_t child; 111160814Ssimon 112160814Ssimon /* Only one HPET device can be added. */ 11359191Skris if (devclass_get_device(acpi_hpet_devclass, 0)) 11459191Skris return; 11555714Skris 116280297Sjkim /* Currently, ID and minimum clock tick info is unused. */ 117280297Sjkim 118280297Sjkim status = AcpiGetTable(ACPI_SIG_HPET, 1, (ACPI_TABLE_HEADER **)&hdr); 119280297Sjkim if (ACPI_FAILURE(status)) 120280297Sjkim return; 121280297Sjkim 122280297Sjkim /* 123280297Sjkim * The unit number could be derived from hdr->Sequence but we only 124280297Sjkim * support one HPET device. 125312826Sjkim */ 126280297Sjkim hpet = (ACPI_TABLE_HPET *)hdr; 127280297Sjkim if (hpet->Sequence != 0) 128280297Sjkim printf("ACPI HPET table warning: Sequence is non-zero (%d)\n", 12955714Skris hpet->Sequence); 130280297Sjkim child = BUS_ADD_CHILD(parent, ACPI_DEV_BASE_ORDER, "acpi_hpet", 0); 13155714Skris if (child == NULL) { 132280297Sjkim printf("%s: can't add child\n", __func__); 133280297Sjkim return; 134280297Sjkim } 13555714Skris 136280297Sjkim /* Record a magic value so we can detect this device later. */ 137280297Sjkim acpi_set_magic(child, (uintptr_t)&acpi_hpet_devclass); 138109998Smarkm bus_set_resource(child, SYS_RES_MEMORY, 0, hpet->Address.Address, 139280297Sjkim HPET_MEM_WIDTH); 140280297Sjkim} 141280297Sjkim 142280297Sjkimstatic int 14355714Skrisacpi_hpet_probe(device_t dev) 144280297Sjkim{ 145280297Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 146280297Sjkim 147280297Sjkim if (acpi_disabled("hpet")) 148280297Sjkim return (ENXIO); 149280297Sjkim if (!DEV_HPET(dev) && 150280297Sjkim (ACPI_ID_PROBE(device_get_parent(dev), dev, hpet_ids) == NULL || 151280297Sjkim device_get_unit(dev) != 0)) 152280297Sjkim return (ENXIO); 153280297Sjkim 154280297Sjkim device_set_desc(dev, "High Precision Event Timer"); 155280297Sjkim return (0); 156280297Sjkim} 157280297Sjkim 158280297Sjkimstatic int 159280297Sjkimacpi_hpet_attach(device_t dev) 160280297Sjkim{ 161280297Sjkim struct acpi_hpet_softc *sc; 162280297Sjkim int rid; 163280297Sjkim uint32_t val, val2; 164280297Sjkim uintmax_t freq; 165280297Sjkim 166280297Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 167280297Sjkim 168280297Sjkim sc = device_get_softc(dev); 169280297Sjkim sc->dev = dev; 170280297Sjkim sc->handle = acpi_get_handle(dev); 171280297Sjkim 172280297Sjkim rid = 0; 173280297Sjkim sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 174280297Sjkim RF_ACTIVE); 175280297Sjkim if (sc->mem_res == NULL) 176280297Sjkim return (ENOMEM); 177280297Sjkim 178280297Sjkim /* Validate that we can access the whole region. */ 179280297Sjkim if (rman_get_size(sc->mem_res) < HPET_MEM_WIDTH) { 180280297Sjkim device_printf(dev, "memory region width %ld too small\n", 181280297Sjkim rman_get_size(sc->mem_res)); 182280297Sjkim bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res); 183280297Sjkim return (ENXIO); 184280297Sjkim } 185280297Sjkim 186280297Sjkim /* Be sure timer is enabled. */ 187280297Sjkim hpet_enable(sc); 188280297Sjkim 189280297Sjkim /* Read basic statistics about the timer. */ 190280297Sjkim val = bus_read_4(sc->mem_res, HPET_OFFSET_PERIOD); 191280297Sjkim if (val == 0) { 192280297Sjkim device_printf(dev, "invalid period\n"); 193280297Sjkim hpet_disable(sc); 194280297Sjkim bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res); 195280297Sjkim return (ENXIO); 196280297Sjkim } 197280297Sjkim 198280297Sjkim freq = (1000000000000000LL + val / 2) / val; 199280297Sjkim if (bootverbose) { 200280297Sjkim val = bus_read_4(sc->mem_res, HPET_OFFSET_INFO); 201280297Sjkim device_printf(dev, 202280297Sjkim "vend: 0x%x rev: 0x%x num: %d hz: %jd opts:%s%s\n", 203280297Sjkim val >> 16, val & 0xff, (val >> 18) & 0xf, freq, 204280297Sjkim ((val >> 15) & 1) ? " leg_route" : "", 20555714Skris ((val >> 13) & 1) ? " count_size" : ""); 206280297Sjkim } 207280297Sjkim 208280297Sjkim if (testenv("debug.acpi.hpet_test")) 209280297Sjkim acpi_hpet_test(sc); 210280297Sjkim 211280297Sjkim /* 212280297Sjkim * Don't attach if the timer never increments. Since the spec 213280297Sjkim * requires it to be at least 10 MHz, it has to change in 1 us. 214280297Sjkim */ 215280297Sjkim val = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE); 216280297Sjkim DELAY(1); 217280297Sjkim val2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE); 218280297Sjkim if (val == val2) { 219280297Sjkim device_printf(dev, "HPET never increments, disabling\n"); 220280297Sjkim hpet_disable(sc); 221280297Sjkim bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res); 222280297Sjkim return (ENXIO); 223280297Sjkim } 224280297Sjkim 225280297Sjkim hpet_timecounter.tc_frequency = freq; 226280297Sjkim hpet_timecounter.tc_priv = sc; 227280297Sjkim tc_init(&hpet_timecounter); 228280297Sjkim 229280297Sjkim return (0); 230280297Sjkim} 231280297Sjkim 23255714Skrisstatic int 233280297Sjkimacpi_hpet_detach(device_t dev) 23455714Skris{ 235280297Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 236280297Sjkim 237280297Sjkim /* XXX Without a tc_remove() function, we can't detach. */ 238280297Sjkim return (EBUSY); 239280297Sjkim} 240280297Sjkim 24155714Skrisstatic int 242280297Sjkimacpi_hpet_suspend(device_t dev) 243280297Sjkim{ 244280297Sjkim struct acpi_hpet_softc *sc; 245280297Sjkim 246280297Sjkim /* 247280297Sjkim * Disable the timer during suspend. The timer will not lose 248280297Sjkim * its state in S1 or S2, but we are required to disable 249280297Sjkim * it. 250280297Sjkim */ 251280297Sjkim sc = device_get_softc(dev); 252280297Sjkim hpet_disable(sc); 253280297Sjkim 254280297Sjkim return (0); 255280297Sjkim} 256280297Sjkim 257280297Sjkimstatic int 258280297Sjkimacpi_hpet_resume(device_t dev) 259280297Sjkim{ 260280297Sjkim struct acpi_hpet_softc *sc; 261280297Sjkim 262280297Sjkim /* Re-enable the timer after a resume to keep the clock advancing. */ 263280297Sjkim sc = device_get_softc(dev); 26455714Skris hpet_enable(sc); 265312826Sjkim 266109998Smarkm return (0); 267280297Sjkim} 268280297Sjkim 269280297Sjkim/* Print some basic latency/rate information to assist in debugging. */ 270280297Sjkimstatic void 271280297Sjkimacpi_hpet_test(struct acpi_hpet_softc *sc) 272280297Sjkim{ 27359191Skris int i; 274280297Sjkim uint32_t u1, u2; 275280297Sjkim struct bintime b0, b1, b2; 276280297Sjkim struct timespec ts; 277280297Sjkim 278280297Sjkim binuptime(&b0); 279280297Sjkim binuptime(&b0); 280280297Sjkim binuptime(&b1); 281280297Sjkim u1 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE); 282280297Sjkim for (i = 1; i < 1000; i++) 283280297Sjkim u2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE); 284280297Sjkim binuptime(&b2); 285280297Sjkim u2 = bus_read_4(sc->mem_res, HPET_OFFSET_VALUE); 286280297Sjkim 287280297Sjkim bintime_sub(&b2, &b1); 288280297Sjkim bintime_sub(&b1, &b0); 289280297Sjkim bintime_sub(&b2, &b1); 290280297Sjkim bintime2timespec(&b2, &ts); 291280297Sjkim 292280297Sjkim device_printf(sc->dev, "%ld.%09ld: %u ... %u = %u\n", 293280297Sjkim (long)ts.tv_sec, ts.tv_nsec, u1, u2, u2 - u1); 294280297Sjkim 295280297Sjkim device_printf(sc->dev, "time per call: %ld ns\n", ts.tv_nsec / 1000); 296280297Sjkim} 297280297Sjkim 298280297Sjkimstatic device_method_t acpi_hpet_methods[] = { 299280297Sjkim /* Device interface */ 300280297Sjkim DEVMETHOD(device_identify, acpi_hpet_identify), 301280297Sjkim DEVMETHOD(device_probe, acpi_hpet_probe), 302280297Sjkim DEVMETHOD(device_attach, acpi_hpet_attach), 303280297Sjkim DEVMETHOD(device_detach, acpi_hpet_detach), 304280297Sjkim DEVMETHOD(device_suspend, acpi_hpet_suspend), 305280297Sjkim DEVMETHOD(device_resume, acpi_hpet_resume), 306280297Sjkim 307280297Sjkim {0, 0} 308280297Sjkim}; 309280297Sjkim 310280297Sjkimstatic driver_t acpi_hpet_driver = { 311280297Sjkim "acpi_hpet", 312280297Sjkim acpi_hpet_methods, 313280297Sjkim sizeof(struct acpi_hpet_softc), 314280297Sjkim}; 315280297Sjkim 316280297Sjkim 317280297SjkimDRIVER_MODULE(acpi_hpet, acpi, acpi_hpet_driver, acpi_hpet_devclass, 0, 0); 318280297SjkimMODULE_DEPEND(acpi_hpet, acpi, 1, 1, 1); 319280297Sjkim