w83793g.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /* $OpenBSD: w83793g.c,v 1.5 2009/01/26 15:07:49 kettenis Exp $ */
  2. /*
  3. * Copyright (c) 2007 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
  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/sensors.h>
  21. #include <dev/i2c/i2cvar.h>
  22. /* Winbond W83793G Hardware Monitor */
  23. #define WB_BANKSELECT 0x00
  24. /* Voltage */
  25. #define WB_NUM_VOLTS 10
  26. static const char *wb_volt_desc[WB_NUM_VOLTS] = {
  27. "VCore", "VCore", "VTT",
  28. "", "", "3.3V", "12V", "5VDD", "5VSB", "VBat"
  29. };
  30. #define WB_VCOREA 0x10
  31. #define WB_VCOREB 0x11
  32. #define WB_VTT 0x12
  33. #define WB_VLOW 0x1b
  34. #define WB_VSENS1 0x14
  35. #define WB_VSENS2 0x15
  36. #define WB_3VSEN 0x16
  37. #define WB_12VSEN 0x17
  38. #define WB_5VDD 0x18
  39. #define WB_5VSB 0x19
  40. #define WB_VBAT 0x1a
  41. /* Temperature */
  42. #define WB_NUM_TEMPS 6
  43. #define WB_TD_COUNT 4
  44. #define WB_TD_START 0x1c
  45. #define WB_TDLOW 0x22
  46. #define WB_TR_COUNT 2
  47. #define WB_TR_START 0x20
  48. /* Fan */
  49. #define WB_NUM_FANS 12
  50. #define WB_FAN_START 0x23
  51. struct wbng_softc {
  52. struct device sc_dev;
  53. i2c_tag_t sc_tag;
  54. i2c_addr_t sc_addr;
  55. struct ksensor sc_sensors[WB_NUM_VOLTS + WB_NUM_TEMPS + WB_NUM_FANS];
  56. struct ksensordev sc_sensordev;
  57. };
  58. int wbng_match(struct device *, void *, void *);
  59. void wbng_attach(struct device *, struct device *, void *);
  60. void wbng_refresh(void *);
  61. void wbng_refresh_volts(struct wbng_softc *);
  62. void wbng_refresh_temps(struct wbng_softc *);
  63. void wbng_refresh_fans(struct wbng_softc *);
  64. uint8_t wbng_readreg(struct wbng_softc *, uint8_t);
  65. void wbng_writereg(struct wbng_softc *, uint8_t, uint8_t);
  66. struct cfattach wbng_ca = {
  67. sizeof(struct wbng_softc), wbng_match, wbng_attach
  68. };
  69. struct cfdriver wbng_cd = {
  70. NULL, "wbng", DV_DULL
  71. };
  72. int
  73. wbng_match(struct device *parent, void *match, void *aux)
  74. {
  75. struct i2c_attach_args *ia = aux;
  76. if (strcmp(ia->ia_name, "w83793g") == 0)
  77. return 1;
  78. return 0;
  79. }
  80. void
  81. wbng_attach(struct device *parent, struct device *self, void *aux)
  82. {
  83. struct wbng_softc *sc = (struct wbng_softc *)self;
  84. struct i2c_attach_args *ia = aux;
  85. int i, j;
  86. sc->sc_tag = ia->ia_tag;
  87. sc->sc_addr = ia->ia_addr;
  88. printf(": %s", ia->ia_name);
  89. strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
  90. sizeof(sc->sc_sensordev.xname));
  91. for (i = 0; i < WB_NUM_VOLTS; i++) {
  92. strlcpy(sc->sc_sensors[i].desc, wb_volt_desc[i],
  93. sizeof(sc->sc_sensors[i].desc));
  94. sc->sc_sensors[i].type = SENSOR_VOLTS_DC;
  95. }
  96. for (j = i + WB_NUM_TEMPS; i < j; i++)
  97. sc->sc_sensors[i].type = SENSOR_TEMP;
  98. for (j = i + WB_NUM_FANS; i < j; i++)
  99. sc->sc_sensors[i].type = SENSOR_FANRPM;
  100. for (i = 0; i < WB_NUM_VOLTS + WB_NUM_TEMPS + WB_NUM_FANS; i++)
  101. sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
  102. if (sensor_task_register(sc, wbng_refresh, 5) == NULL) {
  103. printf(", unable to register update task\n");
  104. return;
  105. }
  106. sensordev_install(&sc->sc_sensordev);
  107. printf("\n");
  108. }
  109. void
  110. wbng_refresh(void *arg)
  111. {
  112. struct wbng_softc *sc = arg;
  113. uint8_t bsr;
  114. iic_acquire_bus(sc->sc_tag, 0);
  115. bsr = wbng_readreg(sc, WB_BANKSELECT);
  116. if ((bsr & 0x07) != 0x0)
  117. wbng_writereg(sc, WB_BANKSELECT, bsr & 0xf8);
  118. wbng_refresh_volts(sc);
  119. wbng_refresh_temps(sc);
  120. wbng_refresh_fans(sc);
  121. if ((bsr & 0x07) != 0x0)
  122. wbng_writereg(sc, WB_BANKSELECT, bsr);
  123. iic_release_bus(sc->sc_tag, 0);
  124. }
  125. void
  126. wbng_refresh_volts(struct wbng_softc *sc)
  127. {
  128. struct ksensor *s = &sc->sc_sensors[0];
  129. uint8_t vlow, data;
  130. /* high precision voltage sensors */
  131. vlow = wbng_readreg(sc, WB_VLOW);
  132. data = wbng_readreg(sc, WB_VCOREA);
  133. s[0].value = ((data << 3) | (((vlow & 0x03)) << 1)) * 1000;
  134. data = wbng_readreg(sc, WB_VCOREB);
  135. s[1].value = ((data << 3) | (((vlow & 0x0c) >> 2) << 1)) * 1000;
  136. data = wbng_readreg(sc, WB_VTT);
  137. s[2].value = ((data << 3) | (((vlow & 0x30) >> 4) << 1)) * 1000;
  138. /* low precision voltage sensors */
  139. data = wbng_readreg(sc, WB_VSENS1);
  140. s[3].value = (data << 4) * 1000;
  141. data = wbng_readreg(sc, WB_VSENS2);
  142. s[4].value = (data << 4) * 1000;
  143. data = wbng_readreg(sc, WB_3VSEN);
  144. s[5].value = (data << 4) * 1000;
  145. data = wbng_readreg(sc, WB_12VSEN);
  146. s[6].value = (data << 4) * 6100; /*XXX, the factor is a guess */
  147. data = wbng_readreg(sc, WB_5VDD);
  148. s[7].value = (data << 4) * 1500 + 150000;
  149. data = wbng_readreg(sc, WB_5VSB);
  150. s[8].value = (data << 4) * 1500 + 150000;
  151. data = wbng_readreg(sc, WB_VBAT);
  152. s[9].value = (data << 4) * 1000;
  153. }
  154. void
  155. wbng_refresh_temps(struct wbng_softc *sc)
  156. {
  157. struct ksensor *s = &sc->sc_sensors[WB_NUM_VOLTS];
  158. int data, i;
  159. uint8_t tdlow, low;
  160. /* high precision temperature sensors */
  161. tdlow = wbng_readreg(sc, WB_TDLOW);
  162. for (i = 0; i < WB_TD_COUNT; i++) {
  163. data = wbng_readreg(sc, WB_TD_START + i);
  164. /*
  165. * XXX: datasheet says nothing about acceptable values,
  166. * let's consider only values between -55 degC and +125 degC.
  167. */
  168. if (data > 0x7f && data < 0xc9) {
  169. s[i].flags |= SENSOR_FINVALID;
  170. s[i].value = 0;
  171. continue;
  172. }
  173. if (data & 0x80)
  174. data -= 0x100;
  175. low = (tdlow & (0x03 << (i * 2))) >> (i * 2);
  176. s[i].value = data * 1000000 + low * 250000 + 273150000;
  177. s[i].flags &= ~SENSOR_FINVALID;
  178. }
  179. s += i;
  180. /* low precision temperature sensors */
  181. for (i = 0; i < WB_TR_COUNT; i++) {
  182. data = wbng_readreg(sc, WB_TR_START + i);
  183. /*
  184. * XXX: datasheet says nothing about acceptable values,
  185. * let's consider only values between -55 degC and +125 degC.
  186. */
  187. if (data > 0x7f && data < 0xc9) {
  188. s[i].flags |= SENSOR_FINVALID;
  189. s[i].value = 0;
  190. continue;
  191. }
  192. if (data & 0x80)
  193. data -= 0x100;
  194. s[i].value = data * 1000000 + 273150000;
  195. s[i].flags &= ~SENSOR_FINVALID;
  196. }
  197. }
  198. void
  199. wbng_refresh_fans(struct wbng_softc *sc)
  200. {
  201. struct ksensor *s = &sc->sc_sensors[WB_NUM_VOLTS + WB_NUM_TEMPS];
  202. int i;
  203. for (i = 0; i < WB_NUM_FANS; i++) {
  204. uint8_t h = wbng_readreg(sc, WB_FAN_START + i * 2);
  205. uint8_t l = wbng_readreg(sc, WB_FAN_START + i * 2 + 1);
  206. uint16_t b = h << 8 | l;
  207. if (b >= 0x0fff || b == 0x0f00 || b == 0x0000) {
  208. s[i].flags |= SENSOR_FINVALID;
  209. s[i].value = 0;
  210. } else {
  211. s[i].flags &= ~SENSOR_FINVALID;
  212. s[i].value = 1350000 / b;
  213. }
  214. }
  215. }
  216. uint8_t
  217. wbng_readreg(struct wbng_softc *sc, uint8_t reg)
  218. {
  219. uint8_t data;
  220. iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  221. sc->sc_addr, &reg, sizeof reg, &data, sizeof data, 0);
  222. return data;
  223. }
  224. void
  225. wbng_writereg(struct wbng_softc *sc, uint8_t reg, uint8_t data)
  226. {
  227. iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
  228. sc->sc_addr, &reg, sizeof reg, &data, sizeof data, 0);
  229. }