/* $OpenBSD: asc7621.c,v 1.4 2007/10/31 20:46:17 cnst Exp $ */ /* * Copyright (c) 2007 Mike Belopuhov * Copyright (c) 2007 Theo de Raadt * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <sys/param.h> #include <sys/systm.h> #include <sys/device.h> #include <sys/sensors.h> #include <dev/i2c/i2cvar.h> /* ASC7621 registers */ #define ASC7621_PECI 0x40 /* Check for PECI monitoring */ #define ASC7621_PECI_MASK 0x10 /* 00010000 */ #define ASC7621_LEGACY 0x36 /* Check for legacy mode */ #define ASC7621_LEGACY_MASK 0x10 /* 00010000 */ #define ASC7621_TEMP1H 0x25 /* Zone 1 Temperature (MS Byte) */ #define ASC7621_TEMP1L 0x10 /* Zone 1 Temperature (LS Byte) */ #define ASC7621_TEMP2H 0x26 /* Zone 2 Temperature (MS Byte) */ #define ASC7621_TEMP2L 0x15 /* Zone 2 Temperature (LS Byte) */ #define ASC7621_TEMP3H 0x27 /* Zone 3 Temperature (MS Byte) */ #define ASC7621_TEMP3L 0x16 /* Zone 3 Temperature (LS Byte) */ #define ASC7621_TEMP4H 0x33 /* Zone 4 Temperature (MS Byte) */ #define ASC7621_TEMP4L 0x17 /* Zone 4 Temperature (LS Byte) */ #define ASC7621_TEMP_NA 0x80 /* Not plugged */ #define ASC7621_IN1_VH 0x20 /* 2.5V (MS Byte) */ #define ASC7621_IN1_VL 0x13 /* 2.5V (LS Byte) */ #define ASC7621_IN2_VH 0x21 /* VCCP (MS Byte) */ #define ASC7621_IN2_VL 0x18 /* VCCP (LS Byte) */ #define ASC7621_IN3_VH 0x22 /* 3.3V (MS Byte) */ #define ASC7621_IN3_VL 0x11 /* 2.3V (LS Byte) */ #define ASC7621_IN4_VH 0x23 /* 5V (MS Byte) */ #define ASC7621_IN4_VL 0x12 /* 5V (LS Byte) */ #define ASC7621_IN5_VH 0x24 /* 12V (MS Byte) */ #define ASC7621_IN5_VL 0x14 /* 12V (LS Byte) */ #define ASC7621_TACH1H 0x29 /* Tachometer 1 (MS Byte) */ #define ASC7621_TACH1L 0x28 /* Tachometer 1 (LS Byte) */ #define ASC7621_TACH2H 0x2b /* Tachometer 2 (MS Byte) */ #define ASC7621_TACH2L 0x2a /* Tachometer 2 (LS Byte) */ #define ASC7621_TACH3H 0x2d /* Tachometer 3 (MS Byte) */ #define ASC7621_TACH3L 0x2c /* Tachometer 3 (LS Byte) */ #define ASC7621_TACH4H 0x2f /* Tachometer 4 (MS Byte) */ #define ASC7621_TACH4L 0x2e /* Tachometer 4 (LS Byte) */ /* Sensors */ #define ADL_TEMP1 0 #define ADL_TEMP2 1 #define ADL_TEMP3 2 #define ADL_TEMP4 3 #define ADL_IN1_V 4 #define ADL_IN2_V 5 #define ADL_IN3_V 6 #define ADL_IN4_V 7 #define ADL_IN5_V 8 #define ADL_TACH1 9 #define ADL_TACH2 10 #define ADL_TACH3 11 #define ADL_TACH4 12 #define ADL_NUM_SENSORS 13 struct { char sensor; u_int8_t hreg; /* MS-byte register */ u_int8_t lreg; /* LS-byte register */ char *name; u_short mVscale; u_short tempscale; /* else a fan */ } adl_worklist[] = { { ADL_TEMP1, ASC7621_TEMP1H, ASC7621_TEMP1L, "CPU", 0, 1 }, { ADL_TEMP2, ASC7621_TEMP2H, ASC7621_TEMP2L, "CPU", 0, 1 }, { ADL_TEMP3, ASC7621_TEMP3H, ASC7621_TEMP3L, "Internal", 0, 1 }, { ADL_TEMP4, ASC7621_TEMP4H, ASC7621_TEMP4L, "External", 0, 1 }, { ADL_IN1_V, ASC7621_IN1_VH, ASC7621_IN1_VL, "+1.5V", 2500, 0 }, { ADL_IN2_V, ASC7621_IN2_VH, ASC7621_IN2_VL, "Vccp", 2250, 0 }, { ADL_IN3_V, ASC7621_IN3_VH, ASC7621_IN3_VL, "+3.3V", 3300, 0 }, { ADL_IN4_V, ASC7621_IN4_VH, ASC7621_IN4_VL, "+5V", 5000, 0 }, { ADL_IN5_V, ASC7621_IN5_VH, ASC7621_IN5_VL, "+12V", 12000, 0 }, { ADL_TACH1, ASC7621_TACH1L, ASC7621_TACH1H, "", 0, 0 }, { ADL_TACH2, ASC7621_TACH2L, ASC7621_TACH2H, "", 0, 0 }, { ADL_TACH3, ASC7621_TACH3L, ASC7621_TACH3H, "", 0, 0 }, { ADL_TACH4, ASC7621_TACH4L, ASC7621_TACH4H, "", 0, 0 } }; struct adl_softc { struct device sc_dev; i2c_tag_t sc_tag; i2c_addr_t sc_addr; u_int8_t sc_conf; struct ksensor sc_sensor[ADL_NUM_SENSORS]; struct ksensordev sc_sensordev; }; #if 0 static int peci_enabled; static int legacy_mode; #endif int adl_match(struct device *, void *, void *); void adl_attach(struct device *, struct device *, void *); void adl_refresh(void *); struct cfattach adl_ca = { sizeof(struct adl_softc), adl_match, adl_attach }; struct cfdriver adl_cd = { NULL, "adl", DV_DULL }; int adl_match(struct device *parent, void *match, void *aux) { struct i2c_attach_args *ia = aux; if (strcmp(ia->ia_name, "asc7621") == 0) return (1); return (0); } void adl_attach(struct device *parent, struct device *self, void *aux) { struct adl_softc *sc = (struct adl_softc *)self; struct i2c_attach_args *ia = aux; u_int8_t cmd, data; int i; sc->sc_tag = ia->ia_tag; sc->sc_addr = ia->ia_addr; printf(": %s", ia->ia_name); /* Initialize sensor data. */ strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, sizeof(sc->sc_sensordev.xname)); /* Check for PECI mode */ cmd = ASC7621_PECI; (void)iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &cmd, sizeof(cmd), &data, sizeof(data), 0); if (data & ASC7621_PECI_MASK) printf(", PECI enabled\n"); #if 0 /* Check for legacy mode */ cmd = ASC7621_LEGACY; if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &cmd, sizeof(cmd), &data, sizeof(data), 0)) { printf(", unable to read PECI configuration register"); } if (data & ASC7621_LEGACY_MASK) legacy_mode = 1; #endif if (sensor_task_register(sc, adl_refresh, 5) == NULL) { printf(", unable to register update task\n"); return; } for (i = 0; i < ADL_NUM_SENSORS; i++) { if (adl_worklist[i].tempscale) sc->sc_sensor[i].type = SENSOR_TEMP; else if (adl_worklist[i].mVscale) sc->sc_sensor[i].type = SENSOR_VOLTS_DC; else sc->sc_sensor[i].type = SENSOR_FANRPM; strlcpy(sc->sc_sensor[i].desc, adl_worklist[i].name, sizeof(sc->sc_sensor[i].desc)); sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); } sensordev_install(&sc->sc_sensordev); printf("\n"); } void adl_refresh(void *arg) { struct adl_softc *sc = arg; int64_t temp, volt; u_int8_t hdata, ldata, hreg, lreg; u_int16_t fan; int i; iic_acquire_bus(sc->sc_tag, 0); for (i = 0; i < sizeof adl_worklist / sizeof(adl_worklist[0]); i++) { hreg = adl_worklist[i].hreg; if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &hreg, sizeof hreg, &hdata, sizeof hdata, 0)) { sc->sc_sensor[i].flags |= SENSOR_FINVALID; continue; } lreg = adl_worklist[i].lreg; if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &lreg, sizeof lreg, &ldata, sizeof ldata, 0)) { sc->sc_sensor[i].flags |= SENSOR_FINVALID; continue; } sc->sc_sensor[i].flags &= ~SENSOR_FINVALID; if (adl_worklist[i].tempscale) { if (hdata == ASC7621_TEMP_NA) sc->sc_sensor[i].flags |= SENSOR_FINVALID; else { /* * 10-bit two's complement integer in * steps of 0.25 */ temp = ((hdata << 8 | ldata)) >> (16 - 10); temp = temp * 250000 + 273150000; sc->sc_sensor[i].value = temp; } } else if (adl_worklist[i].mVscale) { volt = ((hdata << 8 | ldata)) >> (16 - 10); volt = volt * adl_worklist[i].mVscale / (192 << 2); sc->sc_sensor[i].value = volt * 1000; } else { /* * Inversed to ensure that the LS byte will be read * before MS byte. */ fan = hdata + (ldata << 8); if (fan == 0 || fan == 0xffff) sc->sc_sensor[i].flags |= SENSOR_FINVALID; else sc->sc_sensor[i].value = (90000 * 60) / fan; } } iic_release_bus(sc->sc_tag, 0); }