w83l784r.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /* $OpenBSD: w83l784r.c,v 1.12 2007/06/24 05:34:35 dlg Exp $ */
  2. /*
  3. * Copyright (c) 2006 Mark Kettenis
  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. /* W83L784R registers */
  23. #define W83L784R_VCORE 0x20
  24. #define W83L784R_VBAT 0x21
  25. #define W83L784R_3_3V 0x22
  26. #define W83L784R_VCC 0x23
  27. #define W83L784R_TEMP1 0x27
  28. #define W83L784R_FAN1 0x28
  29. #define W83L784R_FAN2 0x29
  30. #define W83L784R_CONFIG 0x40
  31. #define W83L784R_FANDIV 0x49
  32. #define W83L784R_T23ADDR 0x4b
  33. #define W83L784R_CHIPID 0x4e
  34. #define W83L784R_TEMP23 0x00
  35. /* W83L785R registers */
  36. #define W83L785R_2_5V 0x21
  37. #define W83L785R_1_5V 0x22
  38. #define W83L785R_VCC 0x23
  39. #define W83L785R_TEMP2 0x26
  40. #define W83L785R_FANDIV 0x47
  41. /* Chip IDs */
  42. #define WBENV_CHIPID_W83L784R 0x50
  43. #define WBENV_CHIPID_W83L785R 0x60
  44. #define WBENV_CHIPID_W83L785TS_L 0x70
  45. #define WBENV_MAX_SENSORS 9
  46. /*
  47. * The W83L784R/W83L785R can measure voltages up to 4.096/2.048 V.
  48. * To measure higher voltages the input is attenuated with (external)
  49. * resistors. So we have to convert the sensor values back to real
  50. * voltages by applying the appropriate resistor factor.
  51. */
  52. #define RFACT_NONE 10000
  53. #define RFACT(x, y) (RFACT_NONE * ((x) + (y)) / (y))
  54. struct wbenv_softc;
  55. struct wbenv_sensor {
  56. char *desc;
  57. enum sensor_type type;
  58. u_int8_t reg;
  59. void (*refresh)(struct wbenv_softc *, int);
  60. int rfact;
  61. };
  62. struct wbenv_softc {
  63. struct device sc_dev;
  64. i2c_tag_t sc_tag;
  65. i2c_addr_t sc_addr[3];
  66. u_int8_t sc_chip_id;
  67. struct ksensor sc_sensors[WBENV_MAX_SENSORS];
  68. struct ksensordev sc_sensordev;
  69. struct wbenv_sensor *sc_wbenv_sensors;
  70. int sc_numsensors;
  71. };
  72. int wbenv_match(struct device *, void *, void *);
  73. void wbenv_attach(struct device *, struct device *, void *);
  74. void wbenv_setup_sensors(struct wbenv_softc *, struct wbenv_sensor *);
  75. void wbenv_refresh(void *);
  76. void w83l784r_refresh_volt(struct wbenv_softc *, int);
  77. void w83l785r_refresh_volt(struct wbenv_softc *, int);
  78. void wbenv_refresh_temp(struct wbenv_softc *, int);
  79. void w83l784r_refresh_temp(struct wbenv_softc *, int);
  80. void w83l784r_refresh_fanrpm(struct wbenv_softc *, int);
  81. void w83l785r_refresh_fanrpm(struct wbenv_softc *, int);
  82. u_int8_t wbenv_readreg(struct wbenv_softc *, u_int8_t);
  83. void wbenv_writereg(struct wbenv_softc *, u_int8_t, u_int8_t);
  84. struct cfattach wbenv_ca = {
  85. sizeof(struct wbenv_softc), wbenv_match, wbenv_attach
  86. };
  87. struct cfdriver wbenv_cd = {
  88. NULL, "wbenv", DV_DULL
  89. };
  90. struct wbenv_sensor w83l784r_sensors[] =
  91. {
  92. { "VCore", SENSOR_VOLTS_DC, W83L784R_VCORE, w83l784r_refresh_volt, RFACT_NONE },
  93. { "VBAT", SENSOR_VOLTS_DC, W83L784R_VBAT, w83l784r_refresh_volt, RFACT(232, 99) },
  94. { "+3.3V", SENSOR_VOLTS_DC, W83L784R_3_3V, w83l784r_refresh_volt, RFACT_NONE },
  95. { "+5V", SENSOR_VOLTS_DC, W83L784R_VCC, w83l784r_refresh_volt, RFACT(50, 34) },
  96. { "", SENSOR_TEMP, W83L784R_TEMP1, wbenv_refresh_temp },
  97. { "", SENSOR_TEMP, 1, w83l784r_refresh_temp },
  98. { "", SENSOR_TEMP, 2, w83l784r_refresh_temp },
  99. { "", SENSOR_FANRPM, W83L784R_FAN1, w83l784r_refresh_fanrpm },
  100. { "", SENSOR_FANRPM, W83L784R_FAN2, w83l784r_refresh_fanrpm },
  101. { NULL }
  102. };
  103. struct wbenv_sensor w83l785r_sensors[] =
  104. {
  105. { "VCore", SENSOR_VOLTS_DC, W83L784R_VCORE, w83l785r_refresh_volt, RFACT_NONE },
  106. { "+2.5V", SENSOR_VOLTS_DC, W83L785R_2_5V, w83l785r_refresh_volt, RFACT(100, 100) },
  107. { "+1.5V", SENSOR_VOLTS_DC, W83L785R_1_5V, w83l785r_refresh_volt, RFACT_NONE },
  108. { "+3.3V", SENSOR_VOLTS_DC, W83L785R_VCC, w83l785r_refresh_volt, RFACT(20, 40) },
  109. { "", SENSOR_TEMP, W83L784R_TEMP1, wbenv_refresh_temp },
  110. { "", SENSOR_TEMP, W83L785R_TEMP2, wbenv_refresh_temp },
  111. { "", SENSOR_FANRPM, W83L784R_FAN1, w83l785r_refresh_fanrpm },
  112. { "", SENSOR_FANRPM, W83L784R_FAN2, w83l785r_refresh_fanrpm },
  113. { NULL }
  114. };
  115. struct wbenv_sensor w83l785ts_l_sensors[] =
  116. {
  117. { "", SENSOR_TEMP, W83L784R_TEMP1, wbenv_refresh_temp },
  118. { NULL }
  119. };
  120. int
  121. wbenv_match(struct device *parent, void *match, void *aux)
  122. {
  123. struct i2c_attach_args *ia = aux;
  124. if (strcmp(ia->ia_name, "w83l784r") == 0 ||
  125. strcmp(ia->ia_name, "w83l785r") == 0 ||
  126. strcmp(ia->ia_name, "w83l785ts-l") == 0)
  127. return (1);
  128. return (0);
  129. }
  130. void
  131. wbenv_attach(struct device *parent, struct device *self, void *aux)
  132. {
  133. struct wbenv_softc *sc = (struct wbenv_softc *)self;
  134. struct i2c_attach_args *ia = aux;
  135. u_int8_t cmd, data, config;
  136. int i;
  137. sc->sc_tag = ia->ia_tag;
  138. sc->sc_addr[0] = ia->ia_addr;
  139. iic_acquire_bus(sc->sc_tag, 0);
  140. cmd = W83L784R_CHIPID;
  141. if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  142. sc->sc_addr[0], &cmd, sizeof cmd, &data, sizeof data, 0)) {
  143. iic_release_bus(sc->sc_tag, 0);
  144. printf(": cannot read chip ID register\n");
  145. return;
  146. }
  147. iic_release_bus(sc->sc_tag, 0);
  148. sc->sc_chip_id = data;
  149. switch (sc->sc_chip_id) {
  150. case WBENV_CHIPID_W83L784R:
  151. printf(": W83L784R\n");
  152. wbenv_setup_sensors(sc, w83l784r_sensors);
  153. break;
  154. case WBENV_CHIPID_W83L785R:
  155. printf(": W83L785R\n");
  156. wbenv_setup_sensors(sc, w83l785r_sensors);
  157. goto start;
  158. case WBENV_CHIPID_W83L785TS_L:
  159. printf(": W83L785TS-L\n");
  160. wbenv_setup_sensors(sc, w83l785ts_l_sensors);
  161. goto start;
  162. default:
  163. printf(": unknown Winbond chip (ID 0x%x)\n", sc->sc_chip_id);
  164. return;
  165. }
  166. iic_acquire_bus(sc->sc_tag, 0);
  167. cmd = W83L784R_T23ADDR;
  168. if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  169. sc->sc_addr[0], &cmd, sizeof cmd, &data, sizeof data, 0)) {
  170. iic_release_bus(sc->sc_tag, 0);
  171. printf(": cannot read address register\n");
  172. return;
  173. }
  174. iic_release_bus(sc->sc_tag, 0);
  175. sc->sc_addr[1] = 0x48 + (data & 0x7);
  176. sc->sc_addr[2] = 0x48 + ((data >> 4) & 0x7);
  177. /* Make the bus scan ignore the satellites. */
  178. iic_ignore_addr(sc->sc_addr[1]);
  179. iic_ignore_addr(sc->sc_addr[2]);
  180. start:
  181. if (sensor_task_register(sc, wbenv_refresh, 5) == NULL) {
  182. printf("%s: unable to register update task\n",
  183. sc->sc_dev.dv_xname);
  184. return;
  185. }
  186. /* Start the monitoring loop */
  187. config = wbenv_readreg(sc, W83L784R_CONFIG);
  188. wbenv_writereg(sc, W83L784R_CONFIG, config | 0x01);
  189. /* Add sensors */
  190. for (i = 0; i < sc->sc_numsensors; ++i)
  191. sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
  192. sensordev_install(&sc->sc_sensordev);
  193. }
  194. void
  195. wbenv_setup_sensors(struct wbenv_softc *sc, struct wbenv_sensor *sensors)
  196. {
  197. int i;
  198. strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
  199. sizeof(sc->sc_sensordev.xname));
  200. for (i = 0; sensors[i].desc; i++) {
  201. sc->sc_sensors[i].type = sensors[i].type;
  202. strlcpy(sc->sc_sensors[i].desc, sensors[i].desc,
  203. sizeof(sc->sc_sensors[i].desc));
  204. sc->sc_numsensors++;
  205. }
  206. sc->sc_wbenv_sensors = sensors;
  207. }
  208. void
  209. wbenv_refresh(void *arg)
  210. {
  211. struct wbenv_softc *sc = arg;
  212. int i;
  213. iic_acquire_bus(sc->sc_tag, 0);
  214. for (i = 0; i < sc->sc_numsensors; i++)
  215. sc->sc_wbenv_sensors[i].refresh(sc, i);
  216. iic_release_bus(sc->sc_tag, 0);
  217. }
  218. void
  219. w83l784r_refresh_volt(struct wbenv_softc *sc, int n)
  220. {
  221. struct ksensor *sensor = &sc->sc_sensors[n];
  222. int data, reg = sc->sc_wbenv_sensors[n].reg;
  223. data = wbenv_readreg(sc, reg);
  224. sensor->value = (data << 4); /* 16 mV LSB */
  225. sensor->value *= sc->sc_wbenv_sensors[n].rfact;
  226. sensor->value /= 10;
  227. }
  228. void
  229. w83l785r_refresh_volt(struct wbenv_softc *sc, int n)
  230. {
  231. struct ksensor *sensor = &sc->sc_sensors[n];
  232. int data, reg = sc->sc_wbenv_sensors[n].reg;
  233. data = wbenv_readreg(sc, reg);
  234. sensor->value = (data << 3); /* 8 mV LSB */
  235. sensor->value *= sc->sc_wbenv_sensors[n].rfact;
  236. sensor->value /= 10;
  237. }
  238. void
  239. wbenv_refresh_temp(struct wbenv_softc *sc, int n)
  240. {
  241. struct ksensor *sensor = &sc->sc_sensors[n];
  242. int sdata;
  243. sdata = wbenv_readreg(sc, sc->sc_wbenv_sensors[n].reg);
  244. if (sdata & 0x80)
  245. sdata -= 0x100;
  246. sensor->value = sdata * 1000000 + 273150000;
  247. }
  248. void
  249. w83l784r_refresh_temp(struct wbenv_softc *sc, int n)
  250. {
  251. struct ksensor *sensor = &sc->sc_sensors[n];
  252. int16_t sdata;
  253. u_int8_t cmd = 0;
  254. iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  255. sc->sc_addr[sc->sc_wbenv_sensors[n].reg],
  256. &cmd, sizeof cmd, &sdata, sizeof sdata, 0);
  257. sensor->value = (sdata >> 7) * 500000 + 273150000;
  258. }
  259. void
  260. w83l784r_refresh_fanrpm(struct wbenv_softc *sc, int n)
  261. {
  262. struct ksensor *sensor = &sc->sc_sensors[n];
  263. int data, divisor;
  264. data = wbenv_readreg(sc, W83L784R_FANDIV);
  265. if (sc->sc_wbenv_sensors[n].reg == W83L784R_FAN1)
  266. divisor = data & 0x07;
  267. else
  268. divisor = (data >> 4) & 0x07;
  269. data = wbenv_readreg(sc, sc->sc_wbenv_sensors[n].reg);
  270. if (data == 0xff || data == 0x00) {
  271. sensor->flags |= SENSOR_FINVALID;
  272. sensor->value = 0;
  273. } else {
  274. sensor->flags &= ~SENSOR_FINVALID;
  275. sensor->value = 1350000 / (data << divisor);
  276. }
  277. }
  278. void
  279. w83l785r_refresh_fanrpm(struct wbenv_softc *sc, int n)
  280. {
  281. struct ksensor *sensor = &sc->sc_sensors[n];
  282. int data, divisor;
  283. data = wbenv_readreg(sc, W83L785R_FANDIV);
  284. if (sc->sc_wbenv_sensors[n].reg == W83L784R_FAN1)
  285. divisor = data & 0x07;
  286. else
  287. divisor = (data >> 4) & 0x07;
  288. data = wbenv_readreg(sc, sc->sc_wbenv_sensors[n].reg);
  289. if (data == 0xff || data == 0x00) {
  290. sensor->flags |= SENSOR_FINVALID;
  291. sensor->value = 0;
  292. } else {
  293. sensor->flags &= ~SENSOR_FINVALID;
  294. sensor->value = 1350000 / (data << divisor);
  295. }
  296. }
  297. u_int8_t
  298. wbenv_readreg(struct wbenv_softc *sc, u_int8_t reg)
  299. {
  300. u_int8_t data;
  301. iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  302. sc->sc_addr[0], &reg, sizeof reg, &data, sizeof data, 0);
  303. return data;
  304. }
  305. void
  306. wbenv_writereg(struct wbenv_softc *sc, u_int8_t reg, u_int8_t data)
  307. {
  308. iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
  309. sc->sc_addr[0], &reg, sizeof reg, &data, sizeof data, 0);
  310. }