1*471aeecfSnaddy /* $OpenBSD: w83795g.c,v 1.2 2022/04/06 18:59:28 naddy Exp $ */
22db8573fSkettenis
32db8573fSkettenis /*
42db8573fSkettenis * Copyright (c) 2011 Mark Kettenis
52db8573fSkettenis *
62db8573fSkettenis * Permission to use, copy, modify, and distribute this software for any
72db8573fSkettenis * purpose with or without fee is hereby granted, provided that the above
82db8573fSkettenis * copyright notice and this permission notice appear in all copies.
92db8573fSkettenis *
102db8573fSkettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
112db8573fSkettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
122db8573fSkettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
132db8573fSkettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
142db8573fSkettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
152db8573fSkettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
162db8573fSkettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
172db8573fSkettenis */
182db8573fSkettenis
192db8573fSkettenis #include <sys/param.h>
202db8573fSkettenis #include <sys/systm.h>
212db8573fSkettenis #include <sys/device.h>
222db8573fSkettenis #include <sys/sensors.h>
232db8573fSkettenis
242db8573fSkettenis #include <dev/i2c/i2cvar.h>
252db8573fSkettenis
262db8573fSkettenis /* Nuvoton W83795G Hardware Monitor */
272db8573fSkettenis
282db8573fSkettenis #define NVT_BANKSELECT 0x00
292db8573fSkettenis #define NVT_CONFIG 0x01
302db8573fSkettenis #define NVT_CONFIG_48 0x04
312db8573fSkettenis #define NVT_VOLT_CTRL1 0x02
322db8573fSkettenis #define NVT_VOLT_CTRL2 0x03
332db8573fSkettenis #define NVT_TEMP_CTRL1 0x04
342db8573fSkettenis #define NVT_TEMP_CTRL2 0x05
352db8573fSkettenis #define NVT_FANIN_CTRL1 0x06
362db8573fSkettenis #define NVT_FANIN_CTRL2 0x07
372db8573fSkettenis #define NVT_VSEN1 0x10
382db8573fSkettenis #define NVT_3VDD 0x1c
392db8573fSkettenis #define NVT_3VSB 0x1d
402db8573fSkettenis #define NVT_VBAT 0x1e
412db8573fSkettenis #define NVT_TR5 0x1f
422db8573fSkettenis #define NVT_TR6 0x20
432db8573fSkettenis #define NVT_TD1 0x21
442db8573fSkettenis #define NVT_TD2 0x22
452db8573fSkettenis #define NVT_TD3 0x23
462db8573fSkettenis #define NVT_TD4 0x24
472db8573fSkettenis #define NVT_FANIN1_COUNT 0x2e
482db8573fSkettenis #define NVT_VRLSB 0x3c
492db8573fSkettenis
502db8573fSkettenis /* Voltage */
512db8573fSkettenis #define NVT_NUM_VOLTS 15
522db8573fSkettenis
532db8573fSkettenis static const char *nvt_volt_desc[NVT_NUM_VOLTS] = {
542db8573fSkettenis "", "", "", "", "", "", "", "", "", "", "",
552db8573fSkettenis "VTT", "3VDD", "3VSB", "VBat"
562db8573fSkettenis };
572db8573fSkettenis
582db8573fSkettenis /* Temperature */
592db8573fSkettenis #define NVT_NUM_TEMPS 6
602db8573fSkettenis #define NVT_NUM_TR 2
612db8573fSkettenis #define NVT_NUM_TD 4
622db8573fSkettenis
632db8573fSkettenis /* Fan */
642db8573fSkettenis #define NVT_NUM_FANS 14
652db8573fSkettenis
662db8573fSkettenis #define NVT_NUM_SENSORS (NVT_NUM_VOLTS + NVT_NUM_TEMPS + NVT_NUM_FANS)
672db8573fSkettenis
682db8573fSkettenis struct nvt_softc {
692db8573fSkettenis struct device sc_dev;
702db8573fSkettenis i2c_tag_t sc_tag;
712db8573fSkettenis i2c_addr_t sc_addr;
722db8573fSkettenis
732db8573fSkettenis uint16_t sc_vctrl;
742db8573fSkettenis uint16_t sc_tctrl1, sc_tctrl2;
752db8573fSkettenis uint16_t sc_fctrl;
762db8573fSkettenis
772db8573fSkettenis struct ksensor sc_sensors[NVT_NUM_SENSORS];
782db8573fSkettenis struct ksensordev sc_sensordev;
792db8573fSkettenis };
802db8573fSkettenis
812db8573fSkettenis
822db8573fSkettenis int nvt_match(struct device *, void *, void *);
832db8573fSkettenis void nvt_attach(struct device *, struct device *, void *);
842db8573fSkettenis void nvt_refresh(void *);
852db8573fSkettenis
862db8573fSkettenis void nvt_refresh_volts(struct nvt_softc *);
872db8573fSkettenis void nvt_refresh_temps(struct nvt_softc *);
882db8573fSkettenis void nvt_refresh_fans(struct nvt_softc *);
892db8573fSkettenis
902db8573fSkettenis uint8_t nvt_readreg(struct nvt_softc *, uint8_t);
912db8573fSkettenis void nvt_writereg(struct nvt_softc *, uint8_t, uint8_t);
922db8573fSkettenis
932db8573fSkettenis
94*471aeecfSnaddy const struct cfattach nvt_ca = {
952db8573fSkettenis sizeof(struct nvt_softc), nvt_match, nvt_attach
962db8573fSkettenis };
972db8573fSkettenis
982db8573fSkettenis struct cfdriver nvt_cd = {
992db8573fSkettenis NULL, "nvt", DV_DULL
1002db8573fSkettenis };
1012db8573fSkettenis
1022db8573fSkettenis
1032db8573fSkettenis int
nvt_match(struct device * parent,void * match,void * aux)1042db8573fSkettenis nvt_match(struct device *parent, void *match, void *aux)
1052db8573fSkettenis {
1062db8573fSkettenis struct i2c_attach_args *ia = aux;
1072db8573fSkettenis
1082db8573fSkettenis if (strcmp(ia->ia_name, "w83795g") == 0)
1092db8573fSkettenis return (1);
1102db8573fSkettenis return (0);
1112db8573fSkettenis }
1122db8573fSkettenis
1132db8573fSkettenis void
nvt_attach(struct device * parent,struct device * self,void * aux)1142db8573fSkettenis nvt_attach(struct device *parent, struct device *self, void *aux)
1152db8573fSkettenis {
1162db8573fSkettenis struct nvt_softc *sc = (struct nvt_softc *)self;
1172db8573fSkettenis struct i2c_attach_args *ia = aux;
1182db8573fSkettenis uint8_t cfg, vctrl1, vctrl2;
1192db8573fSkettenis uint8_t tctrl1, tctrl2, fctrl1, fctrl2;
1202db8573fSkettenis int i, j;
1212db8573fSkettenis
1222db8573fSkettenis sc->sc_tag = ia->ia_tag;
1232db8573fSkettenis sc->sc_addr = ia->ia_addr;
1242db8573fSkettenis
1252db8573fSkettenis cfg = nvt_readreg(sc, NVT_CONFIG);
1262db8573fSkettenis if (cfg & NVT_CONFIG_48)
1272db8573fSkettenis printf(": W83795ADG");
1282db8573fSkettenis else
1292db8573fSkettenis printf(": W83795G");
1302db8573fSkettenis
1312db8573fSkettenis vctrl1 = nvt_readreg(sc, NVT_VOLT_CTRL1);
1322db8573fSkettenis vctrl2 = nvt_readreg(sc, NVT_VOLT_CTRL2);
1332db8573fSkettenis tctrl1 = nvt_readreg(sc, NVT_TEMP_CTRL1);
1342db8573fSkettenis tctrl2 = nvt_readreg(sc, NVT_TEMP_CTRL2);
1352db8573fSkettenis fctrl1 = nvt_readreg(sc, NVT_FANIN_CTRL1);
1362db8573fSkettenis fctrl2 = nvt_readreg(sc, NVT_FANIN_CTRL2);
1372db8573fSkettenis
1382db8573fSkettenis sc->sc_vctrl = vctrl2 << 8 | vctrl1;
1392db8573fSkettenis sc->sc_tctrl1 = tctrl1;
1402db8573fSkettenis sc->sc_tctrl2 = tctrl2;
1412db8573fSkettenis sc->sc_fctrl = fctrl2 << 8 | fctrl1;
1422db8573fSkettenis
1432db8573fSkettenis strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
1442db8573fSkettenis sizeof(sc->sc_sensordev.xname));
1452db8573fSkettenis
1462db8573fSkettenis for (i = 0; i < NVT_NUM_VOLTS; i++) {
1472db8573fSkettenis strlcpy(sc->sc_sensors[i].desc, nvt_volt_desc[i],
1482db8573fSkettenis sizeof(sc->sc_sensors[i].desc));
1492db8573fSkettenis sc->sc_sensors[i].type = SENSOR_VOLTS_DC;
1502db8573fSkettenis }
1512db8573fSkettenis
1522db8573fSkettenis for (j = i + NVT_NUM_TEMPS; i < j; i++)
1532db8573fSkettenis sc->sc_sensors[i].type = SENSOR_TEMP;
1542db8573fSkettenis
1552db8573fSkettenis for (j = i + NVT_NUM_FANS; i < j; i++)
1562db8573fSkettenis sc->sc_sensors[i].type = SENSOR_FANRPM;
1572db8573fSkettenis
1582db8573fSkettenis for (i = 0; i < NVT_NUM_VOLTS + NVT_NUM_TEMPS + NVT_NUM_FANS; i++)
1592db8573fSkettenis sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
1602db8573fSkettenis
1612db8573fSkettenis if (sensor_task_register(sc, nvt_refresh, 5) == NULL) {
1622db8573fSkettenis printf(", unable to register update task\n");
1632db8573fSkettenis return;
1642db8573fSkettenis }
1652db8573fSkettenis
1662db8573fSkettenis sensordev_install(&sc->sc_sensordev);
1672db8573fSkettenis printf("\n");
1682db8573fSkettenis }
1692db8573fSkettenis
1702db8573fSkettenis void
nvt_refresh(void * arg)1712db8573fSkettenis nvt_refresh(void *arg)
1722db8573fSkettenis {
1732db8573fSkettenis struct nvt_softc *sc = arg;
1742db8573fSkettenis uint8_t bsr;
1752db8573fSkettenis
1762db8573fSkettenis iic_acquire_bus(sc->sc_tag, 0);
1772db8573fSkettenis
1782db8573fSkettenis bsr = nvt_readreg(sc, NVT_BANKSELECT);
1792db8573fSkettenis if ((bsr & 0x07) != 0x00)
1802db8573fSkettenis nvt_writereg(sc, NVT_BANKSELECT, bsr & 0xf8);
1812db8573fSkettenis
1822db8573fSkettenis nvt_refresh_volts(sc);
1832db8573fSkettenis nvt_refresh_temps(sc);
1842db8573fSkettenis nvt_refresh_fans(sc);
1852db8573fSkettenis
1862db8573fSkettenis if ((bsr & 0x07) != 0x00)
1872db8573fSkettenis nvt_writereg(sc, NVT_BANKSELECT, bsr);
1882db8573fSkettenis
1892db8573fSkettenis iic_release_bus(sc->sc_tag, 0);
1902db8573fSkettenis }
1912db8573fSkettenis
1922db8573fSkettenis void
nvt_refresh_volts(struct nvt_softc * sc)1932db8573fSkettenis nvt_refresh_volts(struct nvt_softc *sc)
1942db8573fSkettenis {
1952db8573fSkettenis struct ksensor *s = &sc->sc_sensors[0];
1962db8573fSkettenis uint8_t vrlsb, data;
1972db8573fSkettenis int i, reg;
1982db8573fSkettenis
1992db8573fSkettenis for (i = 0; i < NVT_NUM_VOLTS; i++) {
2002db8573fSkettenis if ((sc->sc_vctrl & (1 << i)) == 0) {
2012db8573fSkettenis s[i].flags |= SENSOR_FINVALID;
2022db8573fSkettenis s[i].value = 0;
2032db8573fSkettenis continue;
2042db8573fSkettenis }
2052db8573fSkettenis
2062db8573fSkettenis reg = NVT_VSEN1 + i;
2072db8573fSkettenis data = nvt_readreg(sc, reg);
2082db8573fSkettenis vrlsb = nvt_readreg(sc, NVT_VRLSB);
2092db8573fSkettenis if (reg != NVT_3VDD && reg != NVT_3VSB && reg != NVT_VBAT)
2102db8573fSkettenis s[i].value = 10000000 - ((data << 3) | (vrlsb >> 6)) * 2000;
2112db8573fSkettenis else
2122db8573fSkettenis s[i].value = 10000000 - ((data << 3) | (vrlsb >> 6)) * 6000;
2132db8573fSkettenis s[i].flags &= ~SENSOR_FINVALID;
2142db8573fSkettenis }
2152db8573fSkettenis }
2162db8573fSkettenis
2172db8573fSkettenis void
nvt_refresh_temps(struct nvt_softc * sc)2182db8573fSkettenis nvt_refresh_temps(struct nvt_softc *sc)
2192db8573fSkettenis {
2202db8573fSkettenis struct ksensor *s = &sc->sc_sensors[NVT_NUM_VOLTS];
2212db8573fSkettenis uint8_t vrlsb;
2222db8573fSkettenis int8_t data;
2232db8573fSkettenis int i;
2242db8573fSkettenis
2252db8573fSkettenis for (i = 0; i < NVT_NUM_TEMPS; i++) {
2262db8573fSkettenis if (i < NVT_NUM_TR
2272db8573fSkettenis && (sc->sc_tctrl1 & (1 << (2 * i))) == 0) {
2282db8573fSkettenis s[i].flags |= SENSOR_FINVALID;
2292db8573fSkettenis s[i].value = 0;
2302db8573fSkettenis continue;
2312db8573fSkettenis }
2322db8573fSkettenis
2332db8573fSkettenis if (i >= NVT_NUM_TR
2342db8573fSkettenis && (sc->sc_tctrl2 & (1 << (2 * (i - NVT_NUM_TR)))) == 0) {
2352db8573fSkettenis s[i].flags |= SENSOR_FINVALID;
2362db8573fSkettenis s[i].value = 0;
2372db8573fSkettenis continue;
2382db8573fSkettenis }
2392db8573fSkettenis
2402db8573fSkettenis data = nvt_readreg(sc, NVT_TR5 + i);
2412db8573fSkettenis vrlsb = nvt_readreg(sc, NVT_VRLSB);
2422db8573fSkettenis if (data == -128 && (vrlsb >> 6) == 0) {
2432db8573fSkettenis s[i].flags |= SENSOR_FINVALID;
2442db8573fSkettenis s[i].value = 0;
2452db8573fSkettenis continue;
2462db8573fSkettenis }
2472db8573fSkettenis s[i].value = data * 1000000 + (vrlsb >> 6) * 250000;
2482db8573fSkettenis s[i].value += 273150000;
2492db8573fSkettenis s[i].flags &= ~SENSOR_FINVALID;
2502db8573fSkettenis }
2512db8573fSkettenis }
2522db8573fSkettenis
2532db8573fSkettenis void
nvt_refresh_fans(struct nvt_softc * sc)2542db8573fSkettenis nvt_refresh_fans(struct nvt_softc *sc)
2552db8573fSkettenis {
2562db8573fSkettenis struct ksensor *s = &sc->sc_sensors[NVT_NUM_VOLTS + NVT_NUM_TEMPS];
2572db8573fSkettenis uint8_t data, vrlsb;
2582db8573fSkettenis uint16_t count;
2592db8573fSkettenis int i;
2602db8573fSkettenis
2612db8573fSkettenis for (i = 0; i < NVT_NUM_FANS; i++) {
2622db8573fSkettenis if ((sc->sc_fctrl & (1 << i)) == 0) {
2632db8573fSkettenis s[i].flags |= SENSOR_FINVALID;
2642db8573fSkettenis s[i].value = 0;
2652db8573fSkettenis continue;
2662db8573fSkettenis }
2672db8573fSkettenis
2682db8573fSkettenis data = nvt_readreg(sc, NVT_FANIN1_COUNT + i);
2692db8573fSkettenis vrlsb = nvt_readreg(sc, NVT_VRLSB);
2702db8573fSkettenis count = (data << 4) + (vrlsb >> 4);
2712db8573fSkettenis if (count == 0) {
2722db8573fSkettenis s[i].flags |= SENSOR_FINVALID;
2732db8573fSkettenis s[i].value = 0;
2742db8573fSkettenis continue;
2752db8573fSkettenis }
2762db8573fSkettenis s[i].value = 1350000 / (count * 2);
2772db8573fSkettenis s[i].flags &= ~SENSOR_FINVALID;
2782db8573fSkettenis }
2792db8573fSkettenis }
2802db8573fSkettenis
2812db8573fSkettenis uint8_t
nvt_readreg(struct nvt_softc * sc,uint8_t reg)2822db8573fSkettenis nvt_readreg(struct nvt_softc *sc, uint8_t reg)
2832db8573fSkettenis {
2842db8573fSkettenis uint8_t data;
2852db8573fSkettenis
2862db8573fSkettenis iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
2872db8573fSkettenis sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0);
2882db8573fSkettenis
2892db8573fSkettenis return data;
2902db8573fSkettenis }
2912db8573fSkettenis
2922db8573fSkettenis void
nvt_writereg(struct nvt_softc * sc,uint8_t reg,uint8_t data)2932db8573fSkettenis nvt_writereg(struct nvt_softc *sc, uint8_t reg, uint8_t data)
2942db8573fSkettenis {
2952db8573fSkettenis iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
2962db8573fSkettenis sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0);
2972db8573fSkettenis }
298