1 /* $OpenBSD: asc7611.c,v 1.2 2009/01/26 15:07:49 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/sensors.h> 23 24 #include <dev/i2c/i2cvar.h> 25 26 /* 27 * Andigilog aSC7611 28 * Hardware Monitor with Integrated Fan Control 29 * http://www.andigilog.com/downloads/aSC7611_70A05007.pdf 30 * October 2006 31 */ 32 33 /* Temperature */ 34 #define ANDL_NUM_TEMPS 3 35 static const struct { 36 const char *name; 37 const uint8_t mreg; 38 const uint8_t lreg; 39 } andl_temp[ANDL_NUM_TEMPS] = { 40 { "External", 0x25, 0x10 }, 41 { "Internal", 0x26, 0x15 }, 42 { "External", 0x27, 0x0e } 43 }; 44 45 /* Voltage */ 46 #define ANDL_NUM_VOLTS 5 47 static const struct { 48 const char *name; 49 const short nominal; 50 const uint8_t mreg; 51 } andl_volt[ANDL_NUM_VOLTS] = { 52 { "+2.5V", 2500, 0x20 }, 53 { "Vccp", 2250, 0x21 }, 54 { "+3.3V", 3300, 0x22 }, 55 { "+5V", 5000, 0x23 }, 56 { "+12V", 12000, 0x24 } 57 }; 58 59 /* Fan */ 60 #define ANDL_NUM_TACHS 4 61 #define ANDL_TACH_START 0x28 62 63 #define ANDL_NUM_TOTAL (ANDL_NUM_TEMPS + ANDL_NUM_VOLTS + ANDL_NUM_TACHS) 64 65 struct andl_softc { 66 struct device sc_dev; 67 i2c_tag_t sc_tag; 68 i2c_addr_t sc_addr; 69 70 struct ksensor sc_sensors[ANDL_NUM_TOTAL]; 71 struct ksensordev sc_sensordev; 72 }; 73 74 75 int andl_match(struct device *, void *, void *); 76 void andl_attach(struct device *, struct device *, void *); 77 void andl_refresh(void *); 78 79 int andl_refresh_temps(struct andl_softc *, struct ksensor *); 80 int andl_refresh_volts(struct andl_softc *, struct ksensor *); 81 int andl_refresh_tachs(struct andl_softc *, struct ksensor *); 82 83 uint8_t andl_readreg(struct andl_softc *, uint8_t); 84 void andl_writereg(struct andl_softc *, uint8_t, uint8_t); 85 86 87 struct cfattach andl_ca = { 88 sizeof(struct andl_softc), andl_match, andl_attach 89 }; 90 91 struct cfdriver andl_cd = { 92 NULL, "andl", DV_DULL 93 }; 94 95 96 int 97 andl_match(struct device *parent, void *match, void *aux) 98 { 99 struct i2c_attach_args *ia = aux; 100 101 if (strcmp(ia->ia_name, "asc7611") == 0) 102 return 1; 103 return 0; 104 } 105 106 void 107 andl_attach(struct device *parent, struct device *self, void *aux) 108 { 109 struct andl_softc *sc = (struct andl_softc *)self; 110 struct i2c_attach_args *ia = aux; 111 int i, j; 112 113 sc->sc_tag = ia->ia_tag; 114 sc->sc_addr = ia->ia_addr; 115 116 printf(": %s", ia->ia_name); 117 118 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 119 sizeof(sc->sc_sensordev.xname)); 120 121 for (i = 0; i < ANDL_NUM_TEMPS; i++) { 122 strlcpy(sc->sc_sensors[i].desc, andl_temp[i].name, 123 sizeof(sc->sc_sensors[i].desc)); 124 sc->sc_sensors[i].type = SENSOR_TEMP; 125 } 126 127 for (j = i; i < j + ANDL_NUM_VOLTS; i++) { 128 strlcpy(sc->sc_sensors[i].desc, andl_volt[i - j].name, 129 sizeof(sc->sc_sensors[i].desc)); 130 sc->sc_sensors[i].type = SENSOR_VOLTS_DC; 131 } 132 133 for (j = i + ANDL_NUM_TACHS; i < j; i++) 134 sc->sc_sensors[i].type = SENSOR_FANRPM; 135 136 for (i = 0; i < j; i++) 137 sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]); 138 139 if (sensor_task_register(sc, andl_refresh, 5) == NULL) { 140 printf(", unable to register update task\n"); 141 return; 142 } 143 144 sensordev_install(&sc->sc_sensordev); 145 printf("\n"); 146 } 147 148 void 149 andl_refresh(void *arg) 150 { 151 struct andl_softc *sc = arg; 152 struct ksensor *s = sc->sc_sensors; 153 154 iic_acquire_bus(sc->sc_tag, 0); 155 156 s += andl_refresh_temps(sc, s); 157 s += andl_refresh_volts(sc, s); 158 s += andl_refresh_tachs(sc, s); 159 160 iic_release_bus(sc->sc_tag, 0); 161 } 162 163 int 164 andl_refresh_temps(struct andl_softc *sc, struct ksensor *s) 165 { 166 int i; 167 168 for (i = 0; i < ANDL_NUM_TEMPS; i++) { 169 uint8_t m = andl_readreg(sc, andl_temp[i].mreg); 170 uint8_t l = andl_readreg(sc, andl_temp[i].lreg); 171 int32_t t = (m << 8 | l) >> (16 - 10); 172 173 if (t & 0x200) 174 t -= 0x400; 175 t *= 250; 176 if (t < -55000 || t > 125000) { 177 s[i].flags |= SENSOR_FINVALID; 178 s[i].value = 0; 179 } else { 180 s[i].value = t * 1000 + 273150000; 181 s[i].flags &= ~SENSOR_FINVALID; 182 } 183 } 184 return i; 185 } 186 187 int 188 andl_refresh_volts(struct andl_softc *sc, struct ksensor *s) 189 { 190 int i; 191 192 for (i = 0; i < ANDL_NUM_VOLTS; i++) 193 s[i].value = 1000ll * andl_readreg(sc, andl_volt[i].mreg) * 194 andl_volt[i].nominal / 0xc0; 195 return i; 196 } 197 198 int 199 andl_refresh_tachs(struct andl_softc *sc, struct ksensor *s) 200 { 201 int i; 202 203 for (i = 0; i < ANDL_NUM_TACHS; i++) { 204 uint8_t l = andl_readreg(sc, ANDL_TACH_START + i * 2); 205 uint8_t m = andl_readreg(sc, ANDL_TACH_START + i * 2 + 1); 206 uint16_t b = m << 8 | l; 207 208 if (b >= 0xfffc || b == 0) { 209 s[i].flags |= SENSOR_FINVALID; 210 s[i].value = 0; 211 } else { 212 s[i].value = (90000 * 60) / b; 213 s[i].flags &= ~SENSOR_FINVALID; 214 } 215 } 216 return i; 217 } 218 219 uint8_t 220 andl_readreg(struct andl_softc *sc, uint8_t reg) 221 { 222 uint8_t data; 223 224 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 225 sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0); 226 227 return data; 228 } 229 230 void 231 andl_writereg(struct andl_softc *sc, uint8_t reg, uint8_t data) 232 { 233 iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 234 sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0); 235 } 236