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