acpihpet.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /* $OpenBSD: acpihpet.c,v 1.19 2015/08/04 22:22:28 deraadt 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. #include <sys/param.h>
  18. #include <sys/systm.h>
  19. #include <sys/device.h>
  20. #include <sys/timetc.h>
  21. #include <machine/bus.h>
  22. #include <dev/acpi/acpireg.h>
  23. #include <dev/acpi/acpivar.h>
  24. #include <dev/acpi/acpidev.h>
  25. int acpihpet_attached;
  26. int acpihpet_match(struct device *, void *, void *);
  27. void acpihpet_attach(struct device *, struct device *, void *);
  28. int acpihpet_activate(struct device *, int);
  29. u_int acpihpet_gettime(struct timecounter *tc);
  30. u_int64_t acpihpet_r(bus_space_tag_t _iot, bus_space_handle_t _ioh,
  31. bus_size_t _ioa);
  32. void acpihpet_w(bus_space_tag_t _iot, bus_space_handle_t _ioh,
  33. bus_size_t _ioa, u_int64_t _val);
  34. static struct timecounter hpet_timecounter = {
  35. acpihpet_gettime, /* get_timecount */
  36. 0, /* no poll_pps */
  37. 0xffffffff, /* counter_mask (24 bits) */
  38. 0, /* frequency */
  39. 0, /* name */
  40. 1000 /* quality */
  41. };
  42. #define HPET_TIMERS 3
  43. struct hpet_regs {
  44. u_int64_t configuration;
  45. u_int64_t interrupt_status;
  46. u_int64_t main_counter;
  47. struct { /* timers */
  48. u_int64_t config;
  49. u_int64_t compare;
  50. u_int64_t interrupt;
  51. } timers[HPET_TIMERS];
  52. };
  53. struct acpihpet_softc {
  54. struct device sc_dev;
  55. bus_space_tag_t sc_iot;
  56. bus_space_handle_t sc_ioh;
  57. u_int32_t sc_conf;
  58. struct hpet_regs sc_save;
  59. };
  60. struct cfattach acpihpet_ca = {
  61. sizeof(struct acpihpet_softc), acpihpet_match, acpihpet_attach,
  62. NULL, acpihpet_activate
  63. };
  64. struct cfdriver acpihpet_cd = {
  65. NULL, "acpihpet", DV_DULL
  66. };
  67. u_int64_t
  68. acpihpet_r(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t ioa)
  69. {
  70. u_int64_t val;
  71. val = bus_space_read_4(iot, ioh, ioa + 4);
  72. val = val << 32;
  73. val |= bus_space_read_4(iot, ioh, ioa);
  74. return (val);
  75. }
  76. void
  77. acpihpet_w(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t ioa,
  78. u_int64_t val)
  79. {
  80. bus_space_write_4(iot, ioh, ioa + 4, val >> 32);
  81. bus_space_write_4(iot, ioh, ioa, val & 0xffffffff);
  82. }
  83. int
  84. acpihpet_activate(struct device *self, int act)
  85. {
  86. struct acpihpet_softc *sc = (struct acpihpet_softc *) self;
  87. switch (act) {
  88. case DVACT_SUSPEND:
  89. /* stop, then save */
  90. bus_space_write_4(sc->sc_iot, sc->sc_ioh,
  91. HPET_CONFIGURATION, sc->sc_conf);
  92. sc->sc_save.configuration = acpihpet_r(sc->sc_iot,
  93. sc->sc_ioh, HPET_CONFIGURATION);
  94. sc->sc_save.interrupt_status = acpihpet_r(sc->sc_iot,
  95. sc->sc_ioh, HPET_INTERRUPT_STATUS);
  96. sc->sc_save.main_counter = acpihpet_r(sc->sc_iot,
  97. sc->sc_ioh, HPET_MAIN_COUNTER);
  98. sc->sc_save.timers[0].config = acpihpet_r(sc->sc_iot,
  99. sc->sc_ioh, HPET_TIMER0_CONFIG);
  100. sc->sc_save.timers[0].interrupt = acpihpet_r(sc->sc_iot,
  101. sc->sc_ioh, HPET_TIMER0_INTERRUPT);
  102. sc->sc_save.timers[0].compare = acpihpet_r(sc->sc_iot,
  103. sc->sc_ioh, HPET_TIMER0_COMPARE);
  104. sc->sc_save.timers[1].config = acpihpet_r(sc->sc_iot,
  105. sc->sc_ioh, HPET_TIMER1_CONFIG);
  106. sc->sc_save.timers[1].interrupt = acpihpet_r(sc->sc_iot,
  107. sc->sc_ioh, HPET_TIMER1_INTERRUPT);
  108. sc->sc_save.timers[1].compare = acpihpet_r(sc->sc_iot,
  109. sc->sc_ioh, HPET_TIMER1_COMPARE);
  110. sc->sc_save.timers[2].config = acpihpet_r(sc->sc_iot,
  111. sc->sc_ioh, HPET_TIMER2_CONFIG);
  112. sc->sc_save.timers[2].interrupt = acpihpet_r(sc->sc_iot,
  113. sc->sc_ioh, HPET_TIMER2_INTERRUPT);
  114. sc->sc_save.timers[2].compare = acpihpet_r(sc->sc_iot,
  115. sc->sc_ioh, HPET_TIMER2_COMPARE);
  116. break;
  117. case DVACT_RESUME:
  118. /* stop, restore, then restart */
  119. bus_space_write_4(sc->sc_iot, sc->sc_ioh,
  120. HPET_CONFIGURATION, sc->sc_conf);
  121. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  122. HPET_CONFIGURATION, sc->sc_save.configuration);
  123. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  124. HPET_INTERRUPT_STATUS, sc->sc_save.interrupt_status);
  125. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  126. HPET_MAIN_COUNTER, sc->sc_save.main_counter);
  127. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  128. HPET_TIMER0_CONFIG, sc->sc_save.timers[0].config);
  129. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  130. HPET_TIMER0_INTERRUPT, sc->sc_save.timers[0].interrupt);
  131. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  132. HPET_TIMER0_COMPARE, sc->sc_save.timers[0].compare);
  133. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  134. HPET_TIMER1_CONFIG, sc->sc_save.timers[1].config);
  135. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  136. HPET_TIMER1_INTERRUPT, sc->sc_save.timers[1].interrupt);
  137. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  138. HPET_TIMER1_COMPARE, sc->sc_save.timers[1].compare);
  139. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  140. HPET_TIMER2_CONFIG, sc->sc_save.timers[2].config);
  141. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  142. HPET_TIMER2_INTERRUPT, sc->sc_save.timers[2].interrupt);
  143. acpihpet_w(sc->sc_iot, sc->sc_ioh,
  144. HPET_TIMER2_COMPARE, sc->sc_save.timers[2].compare);
  145. bus_space_write_4(sc->sc_iot, sc->sc_ioh,
  146. HPET_CONFIGURATION, sc->sc_conf | 1);
  147. break;
  148. }
  149. return 0;
  150. }
  151. int
  152. acpihpet_match(struct device *parent, void *match, void *aux)
  153. {
  154. struct acpi_attach_args *aaa = aux;
  155. struct acpi_table_header *hdr;
  156. /*
  157. * If we do not have a table, it is not us; attach only once
  158. */
  159. if (acpihpet_attached || aaa->aaa_table == NULL)
  160. return (0);
  161. /*
  162. * If it is an HPET table, we can attach
  163. */
  164. hdr = (struct acpi_table_header *)aaa->aaa_table;
  165. if (memcmp(hdr->signature, HPET_SIG, sizeof(HPET_SIG) - 1) != 0)
  166. return (0);
  167. return (1);
  168. }
  169. void
  170. acpihpet_attach(struct device *parent, struct device *self, void *aux)
  171. {
  172. struct acpihpet_softc *sc = (struct acpihpet_softc *) self;
  173. struct acpi_softc *psc = (struct acpi_softc *)parent;
  174. struct acpi_attach_args *aaa = aux;
  175. struct acpi_hpet *hpet = (struct acpi_hpet *)aaa->aaa_table;
  176. u_int64_t period, freq; /* timer period in femtoseconds (10^-15) */
  177. u_int32_t v1, v2;
  178. int timeout;
  179. if (acpi_map_address(psc, &hpet->base_address, 0, HPET_REG_SIZE,
  180. &sc->sc_ioh, &sc->sc_iot)) {
  181. printf(": can't map i/o space\n");
  182. return;
  183. }
  184. /*
  185. * Revisions 0x30 through 0x3a of the AMD SB700, with spread
  186. * spectrum enabled, have an SMM based HPET emulation that's
  187. * subtly broken. The hardware is initialized upon first
  188. * access of the configuration register. Initialization takes
  189. * some time during which the configuration register returns
  190. * 0xffffffff.
  191. */
  192. timeout = 1000;
  193. do {
  194. if (bus_space_read_4(sc->sc_iot, sc->sc_ioh,
  195. HPET_CONFIGURATION) != 0xffffffff)
  196. break;
  197. } while(--timeout > 0);
  198. if (timeout == 0) {
  199. printf(": disabled\n");
  200. return;
  201. }
  202. /* enable hpet */
  203. sc->sc_conf = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
  204. HPET_CONFIGURATION) & ~1;
  205. bus_space_write_4(sc->sc_iot, sc->sc_ioh, HPET_CONFIGURATION,
  206. sc->sc_conf | 1);
  207. /* make sure hpet is working */
  208. v1 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER);
  209. delay(1);
  210. v2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER);
  211. if (v1 == v2) {
  212. printf(": counter not incrementing\n");
  213. bus_space_write_4(sc->sc_iot, sc->sc_ioh,
  214. HPET_CONFIGURATION, sc->sc_conf);
  215. return;
  216. }
  217. period = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
  218. HPET_CAPABILITIES + sizeof(u_int32_t));
  219. if (period == 0) {
  220. printf(": invalid period\n");
  221. bus_space_write_4(sc->sc_iot, sc->sc_ioh,
  222. HPET_CONFIGURATION, sc->sc_conf);
  223. return;
  224. }
  225. freq = 1000000000000000ull / period;
  226. printf(": %lld Hz\n", freq);
  227. hpet_timecounter.tc_frequency = (u_int32_t)freq;
  228. hpet_timecounter.tc_priv = sc;
  229. hpet_timecounter.tc_name = sc->sc_dev.dv_xname;
  230. tc_init(&hpet_timecounter);
  231. acpihpet_attached++;
  232. }
  233. u_int
  234. acpihpet_gettime(struct timecounter *tc)
  235. {
  236. struct acpihpet_softc *sc = tc->tc_priv;
  237. return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, HPET_MAIN_COUNTER));
  238. }