1 /* $OpenBSD: acpihpet.c,v 1.13 2011/01/10 13:36:57 mikeb 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_attached; 33 34 int acpihpet_match(struct device *, void *, void *); 35 void acpihpet_attach(struct device *, struct device *, void *); 36 int acpihpet_activate(struct device *, int); 37 38 #ifdef __HAVE_TIMECOUNTER 39 u_int acpihpet_gettime(struct timecounter *tc); 40 41 static struct timecounter hpet_timecounter = { 42 acpihpet_gettime, /* get_timecount */ 43 0, /* no poll_pps */ 44 0xffffffff, /* counter_mask (24 bits) */ 45 0, /* frequency */ 46 0, /* name */ 47 1000 /* quality */ 48 }; 49 #endif 50 51 struct acpihpet_softc { 52 struct device sc_dev; 53 54 bus_space_tag_t sc_iot; 55 bus_space_handle_t sc_ioh; 56 }; 57 58 struct cfattach acpihpet_ca = { 59 sizeof(struct acpihpet_softc), 60 acpihpet_match, 61 acpihpet_attach, 62 NULL, 63 acpihpet_activate 64 }; 65 66 struct cfdriver acpihpet_cd = { 67 NULL, "acpihpet", DV_DULL 68 }; 69 70 int 71 acpihpet_activate(struct device *self, int act) 72 { 73 struct acpihpet_softc *sc = (struct acpihpet_softc *) self; 74 75 switch (act) { 76 case DVACT_RESUME: 77 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 78 HPET_CONFIGURATION, 1); 79 break; 80 } 81 82 return 0; 83 } 84 85 int 86 acpihpet_match(struct device *parent, void *match, void *aux) 87 { 88 struct acpi_attach_args *aaa = aux; 89 struct acpi_table_header *hdr; 90 91 /* 92 * If we do not have a table, it is not us; attach only once 93 */ 94 if (acpihpet_attached || aaa->aaa_table == NULL) 95 return (0); 96 97 /* 98 * If it is an HPET table, we can attach 99 */ 100 hdr = (struct acpi_table_header *)aaa->aaa_table; 101 if (memcmp(hdr->signature, HPET_SIG, sizeof(HPET_SIG) - 1) != 0) 102 return (0); 103 104 return (1); 105 } 106 107 void 108 acpihpet_attach(struct device *parent, struct device *self, void *aux) 109 { 110 struct acpihpet_softc *sc = (struct acpihpet_softc *) self; 111 struct acpi_softc *psc = (struct acpi_softc *)parent; 112 struct acpi_attach_args *aaa = aux; 113 struct acpi_hpet *hpet = (struct acpi_hpet *)aaa->aaa_table; 114 u_int64_t period, freq; /* timer period in femtoseconds (10^-15) */ 115 u_int32_t v1, v2; 116 int timeout; 117 118 if (acpi_map_address(psc, &hpet->base_address, 0, HPET_REG_SIZE, 119 &sc->sc_ioh, &sc->sc_iot)) { 120 printf(": can't map i/o space\n"); 121 return; 122 } 123 124 /* 125 * Revisions 0x30 through 0x3a of the AMD SB700, with spread 126 * spectrum enabled, have an SMM based HPET emulation that's 127 * subtly broken. The hardware is initialized upon first 128 * access of the configuration register. Initialization takes 129 * some time during which the configuration register returns 130 * 0xffffffff. 131 */ 132 timeout = 1000; 133 do { 134 if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, 135 HPET_CONFIGURATION) != 0xffffffff) 136 break; 137 } while(--timeout > 0); 138 139 if (timeout == 0) { 140 printf(": disabled\n"); 141 return; 142 } 143 144 /* enable hpet */ 145 bus_space_write_4(sc->sc_iot, sc->sc_ioh, HPET_CONFIGURATION, 1); 146 147 /* make sure hpet is working */ 148 v1 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER); 149 delay(1); 150 v2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER); 151 if (v1 == v2) { 152 printf(": counter not incrementing\n"); 153 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 154 HPET_CONFIGURATION, 0); 155 return; 156 } 157 158 period = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 159 HPET_CAPABILITIES + sizeof(u_int32_t)); 160 if (period == 0) { 161 printf(": invalid period\n"); 162 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 163 HPET_CONFIGURATION, 0); 164 return; 165 } 166 freq = 1000000000000000ull / period; 167 printf(": %lld Hz\n", freq); 168 169 #ifdef __HAVE_TIMECOUNTER 170 hpet_timecounter.tc_frequency = (u_int32_t)freq; 171 hpet_timecounter.tc_priv = sc; 172 hpet_timecounter.tc_name = sc->sc_dev.dv_xname; 173 tc_init(&hpet_timecounter); 174 #endif 175 acpihpet_attached++; 176 } 177 178 #ifdef __HAVE_TIMECOUNTER 179 u_int 180 acpihpet_gettime(struct timecounter *tc) 181 { 182 struct acpihpet_softc *sc = tc->tc_priv; 183 184 return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER)); 185 } 186 #endif 187