1 /* $OpenBSD: acpihpet.c,v 1.7 2009/01/20 20:23:57 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/malloc.h> 22 #ifdef __HAVE_TIMECOUNTER 23 #include <sys/timetc.h> 24 #endif 25 26 #include <machine/bus.h> 27 28 #include <dev/acpi/acpireg.h> 29 #include <dev/acpi/acpivar.h> 30 #include <dev/acpi/acpidev.h> 31 32 int acpihpet_match(struct device *, void *, void *); 33 void acpihpet_attach(struct device *, struct device *, void *); 34 35 #ifdef __HAVE_TIMECOUNTER 36 u_int acpihpet_gettime(struct timecounter *tc); 37 38 static struct timecounter hpet_timecounter = { 39 acpihpet_gettime, /* get_timecount */ 40 0, /* no poll_pps */ 41 0xffffffff, /* counter_mask (24 bits) */ 42 0, /* frequency */ 43 0, /* name */ 44 1000 /* quality */ 45 }; 46 #endif 47 48 struct acpihpet_softc { 49 struct device sc_dev; 50 51 bus_space_tag_t sc_iot; 52 bus_space_handle_t sc_ioh; 53 }; 54 55 struct cfattach acpihpet_ca = { 56 sizeof(struct acpihpet_softc), acpihpet_match, acpihpet_attach 57 }; 58 59 struct cfdriver acpihpet_cd = { 60 NULL, "acpihpet", DV_DULL 61 }; 62 63 int 64 acpihpet_match(struct device *parent, void *match, void *aux) 65 { 66 struct acpi_attach_args *aaa = aux; 67 struct acpi_table_header *hdr; 68 69 /* 70 * If we do not have a table, it is not us 71 */ 72 if (aaa->aaa_table == NULL) 73 return (0); 74 75 /* 76 * If it is an HPET table, we can attach 77 */ 78 hdr = (struct acpi_table_header *)aaa->aaa_table; 79 if (memcmp(hdr->signature, HPET_SIG, sizeof(HPET_SIG) - 1) != 0) 80 return (0); 81 82 return (1); 83 } 84 85 void 86 acpihpet_attach(struct device *parent, struct device *self, void *aux) 87 { 88 struct acpihpet_softc *sc = (struct acpihpet_softc *) self; 89 struct acpi_softc *psc = (struct acpi_softc *)parent; 90 struct acpi_attach_args *aaa = aux; 91 struct acpi_hpet *hpet = (struct acpi_hpet *)aaa->aaa_table; 92 u_int64_t period, freq; /* timer period in femtoseconds (10^-15) */ 93 u_int32_t v1, v2; 94 int timeout; 95 96 if (acpi_map_address(psc, &hpet->base_address, 0, HPET_REG_SIZE, 97 &sc->sc_ioh, &sc->sc_iot)) { 98 printf(": can't map i/o space\n"); 99 return; 100 } 101 102 /* 103 * Revisions 0x30 through 0x3a of the AMD SB700, with spread 104 * spectrum enabled, have an SMM based HPET emulation that's 105 * subtly broken. The hardware is initialized upon first 106 * access of the configuration register. Initialization takes 107 * some time during which the configuration register returns 108 * 0xffffffff. 109 */ 110 timeout = 1000; 111 do { 112 if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, 113 HPET_CONFIGURATION) != 0xffffffff) 114 break; 115 } while(--timeout > 0); 116 117 if (timeout == 0) { 118 printf(": disabled\n"); 119 return; 120 } 121 122 /* enable hpet */ 123 bus_space_write_4(sc->sc_iot, sc->sc_ioh, HPET_CONFIGURATION, 1); 124 125 /* make sure hpet is working */ 126 v1 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER); 127 delay(1); 128 v2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER); 129 if (v1 == v2) { 130 printf(": counter not incrementing\n"); 131 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 132 HPET_CONFIGURATION, 0); 133 return; 134 } 135 136 period = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 137 HPET_CAPABILITIES + sizeof(u_int32_t)); 138 if (period == 0) { 139 printf(": invalid period\n"); 140 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 141 HPET_CONFIGURATION, 0); 142 return; 143 } 144 freq = 1000000000000000ull / period; 145 printf(": %lld Hz\n", freq); 146 147 #ifdef __HAVE_TIMECOUNTER 148 hpet_timecounter.tc_frequency = (u_int32_t)freq; 149 hpet_timecounter.tc_priv = sc; 150 hpet_timecounter.tc_name = sc->sc_dev.dv_xname; 151 tc_init(&hpet_timecounter); 152 #endif 153 } 154 155 #ifdef __HAVE_TIMECOUNTER 156 u_int 157 acpihpet_gettime(struct timecounter *tc) 158 { 159 struct acpihpet_softc *sc = tc->tc_priv; 160 161 return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER)); 162 } 163 #endif 164