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