asc7611.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /* $OpenBSD: asc7611.c,v 1.2 2009/01/26 15:07:49 kettenis Exp $ */
  2. /*
  3. * Copyright (c) 2008 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. /*
  23. * Andigilog aSC7611
  24. * Hardware Monitor with Integrated Fan Control
  25. * http://www.andigilog.com/downloads/aSC7611_70A05007.pdf
  26. * October 2006
  27. */
  28. /* Temperature */
  29. #define ANDL_NUM_TEMPS 3
  30. static const struct {
  31. const char *name;
  32. const uint8_t mreg;
  33. const uint8_t lreg;
  34. } andl_temp[ANDL_NUM_TEMPS] = {
  35. { "External", 0x25, 0x10 },
  36. { "Internal", 0x26, 0x15 },
  37. { "External", 0x27, 0x0e }
  38. };
  39. /* Voltage */
  40. #define ANDL_NUM_VOLTS 5
  41. static const struct {
  42. const char *name;
  43. const short nominal;
  44. const uint8_t mreg;
  45. } andl_volt[ANDL_NUM_VOLTS] = {
  46. { "+2.5V", 2500, 0x20 },
  47. { "Vccp", 2250, 0x21 },
  48. { "+3.3V", 3300, 0x22 },
  49. { "+5V", 5000, 0x23 },
  50. { "+12V", 12000, 0x24 }
  51. };
  52. /* Fan */
  53. #define ANDL_NUM_TACHS 4
  54. #define ANDL_TACH_START 0x28
  55. #define ANDL_NUM_TOTAL (ANDL_NUM_TEMPS + ANDL_NUM_VOLTS + ANDL_NUM_TACHS)
  56. struct andl_softc {
  57. struct device sc_dev;
  58. i2c_tag_t sc_tag;
  59. i2c_addr_t sc_addr;
  60. struct ksensor sc_sensors[ANDL_NUM_TOTAL];
  61. struct ksensordev sc_sensordev;
  62. };
  63. int andl_match(struct device *, void *, void *);
  64. void andl_attach(struct device *, struct device *, void *);
  65. void andl_refresh(void *);
  66. int andl_refresh_temps(struct andl_softc *, struct ksensor *);
  67. int andl_refresh_volts(struct andl_softc *, struct ksensor *);
  68. int andl_refresh_tachs(struct andl_softc *, struct ksensor *);
  69. uint8_t andl_readreg(struct andl_softc *, uint8_t);
  70. void andl_writereg(struct andl_softc *, uint8_t, uint8_t);
  71. struct cfattach andl_ca = {
  72. sizeof(struct andl_softc), andl_match, andl_attach
  73. };
  74. struct cfdriver andl_cd = {
  75. NULL, "andl", DV_DULL
  76. };
  77. int
  78. andl_match(struct device *parent, void *match, void *aux)
  79. {
  80. struct i2c_attach_args *ia = aux;
  81. if (strcmp(ia->ia_name, "asc7611") == 0)
  82. return 1;
  83. return 0;
  84. }
  85. void
  86. andl_attach(struct device *parent, struct device *self, void *aux)
  87. {
  88. struct andl_softc *sc = (struct andl_softc *)self;
  89. struct i2c_attach_args *ia = aux;
  90. int i, j;
  91. sc->sc_tag = ia->ia_tag;
  92. sc->sc_addr = ia->ia_addr;
  93. printf(": %s", ia->ia_name);
  94. strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
  95. sizeof(sc->sc_sensordev.xname));
  96. for (i = 0; i < ANDL_NUM_TEMPS; i++) {
  97. strlcpy(sc->sc_sensors[i].desc, andl_temp[i].name,
  98. sizeof(sc->sc_sensors[i].desc));
  99. sc->sc_sensors[i].type = SENSOR_TEMP;
  100. }
  101. for (j = i; i < j + ANDL_NUM_VOLTS; i++) {
  102. strlcpy(sc->sc_sensors[i].desc, andl_volt[i - j].name,
  103. sizeof(sc->sc_sensors[i].desc));
  104. sc->sc_sensors[i].type = SENSOR_VOLTS_DC;
  105. }
  106. for (j = i + ANDL_NUM_TACHS; i < j; i++)
  107. sc->sc_sensors[i].type = SENSOR_FANRPM;
  108. for (i = 0; i < j; i++)
  109. sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
  110. if (sensor_task_register(sc, andl_refresh, 5) == NULL) {
  111. printf(", unable to register update task\n");
  112. return;
  113. }
  114. sensordev_install(&sc->sc_sensordev);
  115. printf("\n");
  116. }
  117. void
  118. andl_refresh(void *arg)
  119. {
  120. struct andl_softc *sc = arg;
  121. struct ksensor *s = sc->sc_sensors;
  122. iic_acquire_bus(sc->sc_tag, 0);
  123. s += andl_refresh_temps(sc, s);
  124. s += andl_refresh_volts(sc, s);
  125. s += andl_refresh_tachs(sc, s);
  126. iic_release_bus(sc->sc_tag, 0);
  127. }
  128. int
  129. andl_refresh_temps(struct andl_softc *sc, struct ksensor *s)
  130. {
  131. int i;
  132. for (i = 0; i < ANDL_NUM_TEMPS; i++) {
  133. uint8_t m = andl_readreg(sc, andl_temp[i].mreg);
  134. uint8_t l = andl_readreg(sc, andl_temp[i].lreg);
  135. int32_t t = (m << 8 | l) >> (16 - 10);
  136. if (t & 0x200)
  137. t -= 0x400;
  138. t *= 250;
  139. if (t < -55000 || t > 125000) {
  140. s[i].flags |= SENSOR_FINVALID;
  141. s[i].value = 0;
  142. } else {
  143. s[i].value = t * 1000 + 273150000;
  144. s[i].flags &= ~SENSOR_FINVALID;
  145. }
  146. }
  147. return i;
  148. }
  149. int
  150. andl_refresh_volts(struct andl_softc *sc, struct ksensor *s)
  151. {
  152. int i;
  153. for (i = 0; i < ANDL_NUM_VOLTS; i++)
  154. s[i].value = 1000ll * andl_readreg(sc, andl_volt[i].mreg) *
  155. andl_volt[i].nominal / 0xc0;
  156. return i;
  157. }
  158. int
  159. andl_refresh_tachs(struct andl_softc *sc, struct ksensor *s)
  160. {
  161. int i;
  162. for (i = 0; i < ANDL_NUM_TACHS; i++) {
  163. uint8_t l = andl_readreg(sc, ANDL_TACH_START + i * 2);
  164. uint8_t m = andl_readreg(sc, ANDL_TACH_START + i * 2 + 1);
  165. uint16_t b = m << 8 | l;
  166. if (b >= 0xfffc || b == 0) {
  167. s[i].flags |= SENSOR_FINVALID;
  168. s[i].value = 0;
  169. } else {
  170. s[i].value = (90000 * 60) / b;
  171. s[i].flags &= ~SENSOR_FINVALID;
  172. }
  173. }
  174. return i;
  175. }
  176. uint8_t
  177. andl_readreg(struct andl_softc *sc, uint8_t reg)
  178. {
  179. uint8_t data;
  180. iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  181. sc->sc_addr, &reg, sizeof reg, &data, sizeof data, 0);
  182. return data;
  183. }
  184. void
  185. andl_writereg(struct andl_softc *sc, uint8_t reg, uint8_t data)
  186. {
  187. iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
  188. sc->sc_addr, &reg, sizeof reg, &data, sizeof data, 0);
  189. }