diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2011-07-03 21:30:21 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2011-07-03 21:30:21 +0000 |
commit | 7ce3d2de64ba5c7f8c3fe3c77f847c6931684e83 (patch) | |
tree | 6a90b914e2e63945704a59e520dfe643be778a04 /sys | |
parent | bf1a7e08ac712ab9f9e616b4d7cbe4ef5d9afe52 (diff) |
Enter nvt(4), a driver for the W83795G and W83795ADG hardware monitor.
ok deraadt@, miod@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/i2c/files.i2c | 7 | ||||
-rw-r--r-- | sys/dev/i2c/w83795g.c | 297 |
2 files changed, 303 insertions, 1 deletions
diff --git a/sys/dev/i2c/files.i2c b/sys/dev/i2c/files.i2c index ae5e5376af5..6aaa6ea20f4 100644 --- a/sys/dev/i2c/files.i2c +++ b/sys/dev/i2c/files.i2c @@ -1,4 +1,4 @@ -# $OpenBSD: files.i2c,v 1.49 2010/03/22 21:20:58 miod Exp $ +# $OpenBSD: files.i2c,v 1.50 2011/07/03 21:30:20 kettenis Exp $ # $NetBSD: files.i2c,v 1.3 2003/10/20 16:24:10 briggs Exp $ define i2c {[addr = -1], [size = -1]} @@ -113,6 +113,11 @@ device wbng attach wbng at i2c file dev/i2c/w83793g.c wbng +# Nuvoton W83795G +device nvt +attach nvt at i2c +file dev/i2c/w83795g.c nvt + # Genesys Logic GL518SM device glenv attach glenv at i2c diff --git a/sys/dev/i2c/w83795g.c b/sys/dev/i2c/w83795g.c new file mode 100644 index 00000000000..1a8a7163ff6 --- /dev/null +++ b/sys/dev/i2c/w83795g.c @@ -0,0 +1,297 @@ +/* $OpenBSD: w83795g.c,v 1.1 2011/07/03 21:30:20 kettenis Exp $ */ + +/* + * Copyright (c) 2011 Mark Kettenis + * + * 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> + +/* Nuvoton W83795G Hardware Monitor */ + +#define NVT_BANKSELECT 0x00 +#define NVT_CONFIG 0x01 +#define NVT_CONFIG_48 0x04 +#define NVT_VOLT_CTRL1 0x02 +#define NVT_VOLT_CTRL2 0x03 +#define NVT_TEMP_CTRL1 0x04 +#define NVT_TEMP_CTRL2 0x05 +#define NVT_FANIN_CTRL1 0x06 +#define NVT_FANIN_CTRL2 0x07 +#define NVT_VSEN1 0x10 +#define NVT_3VDD 0x1c +#define NVT_3VSB 0x1d +#define NVT_VBAT 0x1e +#define NVT_TR5 0x1f +#define NVT_TR6 0x20 +#define NVT_TD1 0x21 +#define NVT_TD2 0x22 +#define NVT_TD3 0x23 +#define NVT_TD4 0x24 +#define NVT_FANIN1_COUNT 0x2e +#define NVT_VRLSB 0x3c + +/* Voltage */ +#define NVT_NUM_VOLTS 15 + +static const char *nvt_volt_desc[NVT_NUM_VOLTS] = { + "", "", "", "", "", "", "", "", "", "", "", + "VTT", "3VDD", "3VSB", "VBat" +}; + +/* Temperature */ +#define NVT_NUM_TEMPS 6 +#define NVT_NUM_TR 2 +#define NVT_NUM_TD 4 + +/* Fan */ +#define NVT_NUM_FANS 14 + +#define NVT_NUM_SENSORS (NVT_NUM_VOLTS + NVT_NUM_TEMPS + NVT_NUM_FANS) + +struct nvt_softc { + struct device sc_dev; + i2c_tag_t sc_tag; + i2c_addr_t sc_addr; + + uint16_t sc_vctrl; + uint16_t sc_tctrl1, sc_tctrl2; + uint16_t sc_fctrl; + + struct ksensor sc_sensors[NVT_NUM_SENSORS]; + struct ksensordev sc_sensordev; +}; + + +int nvt_match(struct device *, void *, void *); +void nvt_attach(struct device *, struct device *, void *); +void nvt_refresh(void *); + +void nvt_refresh_volts(struct nvt_softc *); +void nvt_refresh_temps(struct nvt_softc *); +void nvt_refresh_fans(struct nvt_softc *); + +uint8_t nvt_readreg(struct nvt_softc *, uint8_t); +void nvt_writereg(struct nvt_softc *, uint8_t, uint8_t); + + +struct cfattach nvt_ca = { + sizeof(struct nvt_softc), nvt_match, nvt_attach +}; + +struct cfdriver nvt_cd = { + NULL, "nvt", DV_DULL +}; + + +int +nvt_match(struct device *parent, void *match, void *aux) +{ + struct i2c_attach_args *ia = aux; + + if (strcmp(ia->ia_name, "w83795g") == 0) + return (1); + return (0); +} + +void +nvt_attach(struct device *parent, struct device *self, void *aux) +{ + struct nvt_softc *sc = (struct nvt_softc *)self; + struct i2c_attach_args *ia = aux; + uint8_t cfg, vctrl1, vctrl2; + uint8_t tctrl1, tctrl2, fctrl1, fctrl2; + int i, j; + + sc->sc_tag = ia->ia_tag; + sc->sc_addr = ia->ia_addr; + + cfg = nvt_readreg(sc, NVT_CONFIG); + if (cfg & NVT_CONFIG_48) + printf(": W83795ADG"); + else + printf(": W83795G"); + + vctrl1 = nvt_readreg(sc, NVT_VOLT_CTRL1); + vctrl2 = nvt_readreg(sc, NVT_VOLT_CTRL2); + tctrl1 = nvt_readreg(sc, NVT_TEMP_CTRL1); + tctrl2 = nvt_readreg(sc, NVT_TEMP_CTRL2); + fctrl1 = nvt_readreg(sc, NVT_FANIN_CTRL1); + fctrl2 = nvt_readreg(sc, NVT_FANIN_CTRL2); + + sc->sc_vctrl = vctrl2 << 8 | vctrl1; + sc->sc_tctrl1 = tctrl1; + sc->sc_tctrl2 = tctrl2; + sc->sc_fctrl = fctrl2 << 8 | fctrl1; + + strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, + sizeof(sc->sc_sensordev.xname)); + + for (i = 0; i < NVT_NUM_VOLTS; i++) { + strlcpy(sc->sc_sensors[i].desc, nvt_volt_desc[i], + sizeof(sc->sc_sensors[i].desc)); + sc->sc_sensors[i].type = SENSOR_VOLTS_DC; + } + + for (j = i + NVT_NUM_TEMPS; i < j; i++) + sc->sc_sensors[i].type = SENSOR_TEMP; + + for (j = i + NVT_NUM_FANS; i < j; i++) + sc->sc_sensors[i].type = SENSOR_FANRPM; + + for (i = 0; i < NVT_NUM_VOLTS + NVT_NUM_TEMPS + NVT_NUM_FANS; i++) + sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]); + + if (sensor_task_register(sc, nvt_refresh, 5) == NULL) { + printf(", unable to register update task\n"); + return; + } + + sensordev_install(&sc->sc_sensordev); + printf("\n"); +} + +void +nvt_refresh(void *arg) +{ + struct nvt_softc *sc = arg; + uint8_t bsr; + + iic_acquire_bus(sc->sc_tag, 0); + + bsr = nvt_readreg(sc, NVT_BANKSELECT); + if ((bsr & 0x07) != 0x00) + nvt_writereg(sc, NVT_BANKSELECT, bsr & 0xf8); + + nvt_refresh_volts(sc); + nvt_refresh_temps(sc); + nvt_refresh_fans(sc); + + if ((bsr & 0x07) != 0x00) + nvt_writereg(sc, NVT_BANKSELECT, bsr); + + iic_release_bus(sc->sc_tag, 0); +} + +void +nvt_refresh_volts(struct nvt_softc *sc) +{ + struct ksensor *s = &sc->sc_sensors[0]; + uint8_t vrlsb, data; + int i, reg; + + for (i = 0; i < NVT_NUM_VOLTS; i++) { + if ((sc->sc_vctrl & (1 << i)) == 0) { + s[i].flags |= SENSOR_FINVALID; + s[i].value = 0; + continue; + } + + reg = NVT_VSEN1 + i; + data = nvt_readreg(sc, reg); + vrlsb = nvt_readreg(sc, NVT_VRLSB); + if (reg != NVT_3VDD && reg != NVT_3VSB && reg != NVT_VBAT) + s[i].value = 10000000 - ((data << 3) | (vrlsb >> 6)) * 2000; + else + s[i].value = 10000000 - ((data << 3) | (vrlsb >> 6)) * 6000; + s[i].flags &= ~SENSOR_FINVALID; + } +} + +void +nvt_refresh_temps(struct nvt_softc *sc) +{ + struct ksensor *s = &sc->sc_sensors[NVT_NUM_VOLTS]; + uint8_t vrlsb; + int8_t data; + int i; + + for (i = 0; i < NVT_NUM_TEMPS; i++) { + if (i < NVT_NUM_TR + && (sc->sc_tctrl1 & (1 << (2 * i))) == 0) { + s[i].flags |= SENSOR_FINVALID; + s[i].value = 0; + continue; + } + + if (i >= NVT_NUM_TR + && (sc->sc_tctrl2 & (1 << (2 * (i - NVT_NUM_TR)))) == 0) { + s[i].flags |= SENSOR_FINVALID; + s[i].value = 0; + continue; + } + + data = nvt_readreg(sc, NVT_TR5 + i); + vrlsb = nvt_readreg(sc, NVT_VRLSB); + if (data == -128 && (vrlsb >> 6) == 0) { + s[i].flags |= SENSOR_FINVALID; + s[i].value = 0; + continue; + } + s[i].value = data * 1000000 + (vrlsb >> 6) * 250000; + s[i].value += 273150000; + s[i].flags &= ~SENSOR_FINVALID; + } +} + +void +nvt_refresh_fans(struct nvt_softc *sc) +{ + struct ksensor *s = &sc->sc_sensors[NVT_NUM_VOLTS + NVT_NUM_TEMPS]; + uint8_t data, vrlsb; + uint16_t count; + int i; + + for (i = 0; i < NVT_NUM_FANS; i++) { + if ((sc->sc_fctrl & (1 << i)) == 0) { + s[i].flags |= SENSOR_FINVALID; + s[i].value = 0; + continue; + } + + data = nvt_readreg(sc, NVT_FANIN1_COUNT + i); + vrlsb = nvt_readreg(sc, NVT_VRLSB); + count = (data << 4) + (vrlsb >> 4); + if (count == 0) { + s[i].flags |= SENSOR_FINVALID; + s[i].value = 0; + continue; + } + s[i].value = 1350000 / (count * 2); + s[i].flags &= ~SENSOR_FINVALID; + } +} + +uint8_t +nvt_readreg(struct nvt_softc *sc, uint8_t reg) +{ + uint8_t data; + + iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0); + + return data; +} + +void +nvt_writereg(struct nvt_softc *sc, uint8_t reg, uint8_t data) +{ + iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, + sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0); +} |