summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2011-07-03 21:30:21 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2011-07-03 21:30:21 +0000
commit7ce3d2de64ba5c7f8c3fe3c77f847c6931684e83 (patch)
tree6a90b914e2e63945704a59e520dfe643be778a04 /sys
parentbf1a7e08ac712ab9f9e616b4d7cbe4ef5d9afe52 (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.i2c7
-rw-r--r--sys/dev/i2c/w83795g.c297
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, &reg, 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, &reg, sizeof reg, &data, sizeof data, 0);
+}