The branch main has been updated by jhibbits:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=cd3cc6e910c0f739925c57e42fae6781d693db02

commit cd3cc6e910c0f739925c57e42fae6781d693db02
Author:     Justin Hibbits <[email protected]>
AuthorDate: 2026-05-20 02:04:30 +0000
Commit:     Justin Hibbits <[email protected]>
CommitDate: 2026-05-20 02:05:00 +0000

    i2c/sensors: Add driver for W83793 hardware monitor
    
    The Winbond/Nuvoton W83793G system monitor chip includes many features
    not currently supported by this driver.  The following are currently
    supported:
    
    * Up to 6 temperature sensors, 4 of which have 10-bit resolution
      (8.2),two with 8-bit resolution (no decimal component)
    * Up to 12 fans
      - Fans 0-4 (1-5 on the datasheet) are always enabled.  The remaining 7
        fans are individually enabled.
    * Multiple voltage sensors, reading up to 10 voltage sources.  Sysctls
      are labeled to match the datasheet.
    * Chassis open detection.
    
    The W83793AG is a feature-reduced version, which lacks 3 thermal diodes
    and 2 voltage monitors.  Since there is no way to tell the difference
    between the W83793AG and W83793G programmatically, sensors reported on
    the W83793AG will report strange values.
    
    Temperature sensors and 7 of the fans can be individually enabled on the
    chip, but currently not configured by this driver.  The driver only
    reports what was configured by the firmware.  Additionally, this driver
    numbers the sensors and fans according to the datasheet, so even if, for
    instance, fan 8 is disabled, it would skip from fan 7 to fan 9, it does
    not renumber.  This makes it easier to follow for hardware debugging
    purposes.
    
    Missing features:
    * Smart Fan support
    * Fan PWM control
    * ASF (Alert Standard Format)
    
    Only Bank 0 registers are used at this time.
    
    Reviewed by:    adrian
    Differential Revision:  https://reviews.freebsd.org/D56776
---
 sys/conf/files                   |   1 +
 sys/dev/iicbus/sensor/w83793g.c  | 366 +++++++++++++++++++++++++++++++++++++++
 sys/modules/i2c/Makefile         |   3 +-
 sys/modules/i2c/w83793g/Makefile |  14 ++
 4 files changed, 383 insertions(+), 1 deletion(-)

diff --git a/sys/conf/files b/sys/conf/files
index 6804c9c81c69..fac94252a362 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1866,6 +1866,7 @@ dev/iicbus/rtc/s35390a.c  optional s35390a
 dev/iicbus/sensor/htu21.c      optional htu21
 dev/iicbus/sensor/lm75.c       optional lm75
 dev/iicbus/sensor/max44009.c   optional max44009
+dev/iicbus/sensor/w83793g.c    optional w83793g
 dev/iicbus/gpio/pcf8574.c      optional pcf8574
 dev/iicbus/gpio/tca64xx.c      optional tca64xx fdt gpio
 dev/iicbus/pmic/fan53555.c     optional fan53555 fdt | tcs4525 fdt
diff --git a/sys/dev/iicbus/sensor/w83793g.c b/sys/dev/iicbus/sensor/w83793g.c
new file mode 100644
index 000000000000..772384aa57a0
--- /dev/null
+++ b/sys/dev/iicbus/sensor/w83793g.c
@@ -0,0 +1,366 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Justin Hibbits
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+/*
+ * Driver for the Winbond W83793G hardware monitor.
+ *
+ * The hardware monitor supports the following sensors:
+ * - 6 temperature sensors
+ *   - 4 with 1/4 integer precision
+ *   - 2 with integer precision
+ * - 11 voltage sensors
+ * - 12 fan sensors
+ *   - FanIn 6-12 are on multifunction pins, so may not be enabled.
+ *   8 DC/PWM fan outputs for fan speed control
+ * - Case open detection
+ */
+
+#define        WB_TD_BASE      0x1c
+#define        WB_TLOW         0x22
+
+#define        WB_VCORE_A      0x10
+#define        WB_VCORE_B      0x11
+#define        WB_VTT          0x12
+#define        WB_VSEN1        0x14
+#define        WB_VSEN2        0x15
+#define        WB_VSEN3        0x16
+#define        WB_VSEN4        0x17
+#define        WB_5VDD         0x18
+#define        WB_5VSB         0x19
+#define        WB_VBAT         0x1a
+#define        WB_VLOW         0x1b
+#define        WB_FAN_BASE     0x23
+
+#define        INT_STS1        0x41
+#define        INT_STS2        0x42
+#define        INT_STS3        0x43
+#define        INT_STS4        0x44
+#define          CHASSIS         0x40
+#define        INT_STS5        0x45
+#define        INT_MASK1       0x46
+#define        INT_MASK2       0x47
+#define        INT_MASK3       0x48
+#define        INT_MASK4       0x49
+#define          CLR_CHS         0x80
+#define        INT_MASK5       0x4a
+
+#define        WB_MFC          0x58    /* Multi-function pin control */
+#define          MFC_VIDBSEL     0x80
+#define          MFC_SIB_SEL     0x40
+#define          MFC_SID_SEL_M   0x30
+#define          MFC_SID_VID     0x00
+#define          MFC_SID_FANIN   0x20
+#define          MFC_SIC_SEL_M   0x0c
+#define          MFC_SIC_VID     0x00
+#define          MFC_SIC_FANIN   0x08
+#define          MFC_SIA_SEL     0x02
+#define          MFC_FAN8SEL     0x01
+#define        WB_FANIN_CTRL   0x5c
+#define          FANIN_EN_12     0x40
+#define          FANIN_EN_11     0x20
+#define          FANIN_EN_10     0x10
+#define          FANIN_EN_9      0x08
+#define          FANIN_EN_8      0x04
+#define          FANIN_EN_7      0x02
+#define          FANIN_EN_6      0x01
+#define        WB_FANIN_SEL    0x5d
+#define        WB_TD_MD        0x5e    /* TD mode select register */
+#define          TD_MD_M(n)      (0x3 << ((n) * 2))
+#define          TD_MD_S(n)      ((n) * 2)
+#define          TD_STOP_M       0x0
+#define          TD_INT_MD       0x1
+#define          TD_EXT_MD       0x2
+#define        WB_TR_MD        0x5f
+#define          TR2_MD          0x2
+#define          TR1_MD          0x1
+
+#define        WB_TEMP_COUNT           6       /* Total temperature sensors */
+#define        WB_TD_COUNT             4       /* Temp sensors with "low" part 
*/
+#define        WB_TR_COUNT             2
+#define        WB_FAN_COUNT            12
+#define        WB_FAN_ALWAYS_ON        5       /* First 5 are not controlled */
+#define        WB_V_COUNT              11
+
+static const struct wb_vsens {
+       const char      *name;
+       int             reg;
+       int             scale;  /* Scale in millivolts */
+       int             add;    /* Scale in millivolts */
+       int             left_low;       /* left bit in VLOW, if applicable */
+} voltages[] = {
+       { "v_core_a", WB_VCORE_A, 2, 0, 1 },
+       { "v_core_b", WB_VCORE_B, 2, 0, 3 },
+       { "v_tt", WB_VTT, 2, 0, 5 },
+       { "v_sen_1", WB_VSEN1, 16 },
+       { "v_sen_2", WB_VSEN2, 16 },
+       { "v_sen_3", WB_VSEN3, 16 },
+       { "v_sen_4", WB_VSEN4, 8 },
+       { "5v", WB_5VDD, 24, 150 },
+       { "5v_sb", WB_5VSB, 24, 150 },
+       { "v_bat", WB_VBAT, 16 }
+};
+
+struct w83793g_softc {
+       device_t        sc_dev;
+
+};
+
+static device_probe_t  w83793g_probe;
+static device_attach_t w83793g_attach;
+static device_detach_t w83793g_detach;
+static int w83793g_temp_sysctl(SYSCTL_HANDLER_ARGS);
+static int w83793g_fan_sysctl(SYSCTL_HANDLER_ARGS);
+static int w83793g_voltage_sysctl(SYSCTL_HANDLER_ARGS);
+static int w83793g_case_sysctl(SYSCTL_HANDLER_ARGS);
+
+static device_method_t w83793g_methods[] = {
+       DEVMETHOD(device_probe,         w83793g_probe),
+       DEVMETHOD(device_attach,        w83793g_attach),
+       DEVMETHOD(device_detach,        w83793g_detach),
+
+       DEVMETHOD_END
+};
+
+static struct ofw_compat_data compat[] = {
+       { "winbond,w83793", 1 },
+       { NULL, 0 }
+};
+
+DEFINE_CLASS_0(w83793g, w83793g_driver, w83793g_methods,
+    sizeof(struct w83793g_softc));
+DRIVER_MODULE(w83793g, iicbus, w83793g_driver, NULL, NULL);
+MODULE_VERSION(w83793g, 1);
+MODULE_DEPEND(w83793g, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
+IICBUS_FDT_PNP_INFO(compat);
+
+static int
+w83793g_readreg(device_t dev, int reg, uint8_t *output)
+{
+       return (iicdev_readfrom(dev, reg, output, sizeof(*output), IIC_WAIT));
+}
+
+static int
+w83793g_writereg(device_t dev, int reg, uint8_t *output)
+{
+       return (iicdev_writeto(dev, reg, output, sizeof(*output), IIC_WAIT));
+}
+
+static bool
+temp_enabled(struct w83793g_softc *sc, int sensor)
+{
+       uint8_t reg;
+       int error;
+
+       if (sensor < WB_TD_COUNT) {
+               error = w83793g_readreg(sc->sc_dev, WB_TD_MD, &reg);
+               if (error != 0)
+                       return (false);
+               return ((reg & TD_MD_M(sensor)) != 0);
+       } else {
+               error = w83793g_readreg(sc->sc_dev, WB_TR_MD, &reg);
+               sensor -= WB_TD_COUNT;
+               if (error != 0)
+                       return (false);
+               return ((reg & (1 << sensor)) != 0);
+       }
+}
+
+static bool
+fan_enabled(struct w83793g_softc *sc, int fan)
+{
+       int error;
+       uint8_t fanin_ctl;
+
+       if (fan < WB_FAN_ALWAYS_ON)
+               return (true);
+
+       error = w83793g_readreg(sc->sc_dev, WB_FANIN_CTRL, &fanin_ctl);
+       if (error != 0)
+               return (false);
+
+       fan -= WB_FAN_ALWAYS_ON;
+
+       return ((fanin_ctl & (1 << fan)) != 0);
+}
+
+static int
+w83793g_probe(device_t dev)
+{
+       if (ofw_bus_search_compatible(dev, compat)->ocd_data == 0)
+               return (ENXIO);
+
+       device_set_desc(dev, "Winbond W83793 Hardware Monitor");
+
+       return (BUS_PROBE_DEFAULT);
+}
+
+static int
+w83793g_attach(device_t dev)
+{
+       struct w83793g_softc *sc = device_get_softc(dev);
+       struct sysctl_oid *root = device_get_sysctl_tree(dev);
+       struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
+       struct sysctl_oid *node;
+       int i;
+
+       sc->sc_dev = dev;
+       node = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(root), OID_AUTO, "voltages",
+           CTLFLAG_RD, NULL, NULL);
+       for (i = 0; i < nitems(voltages); i++) {
+               SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO,
+                   voltages[i].name, CTLTYPE_INT | CTLFLAG_RD, sc,
+                   i, w83793g_voltage_sysctl, "I",
+                   "voltage (millivolts)");
+       }
+       node = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(root), OID_AUTO, "temp",
+           CTLFLAG_RD, NULL, NULL);
+       for (i = 0; i < WB_TEMP_COUNT; i++) {
+               /* Only supports single-digit sensors. */
+               char name[sizeof("sensor_") + 1];
+
+               if (!temp_enabled(sc, i))
+                       continue;
+               snprintf(name, sizeof(name), "sensor_%d", i);
+               SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, name,
+                   CTLTYPE_INT | CTLFLAG_RD, sc, WB_TD_BASE + i,
+                   w83793g_temp_sysctl, "IK2", NULL);
+       }
+       node = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(root), OID_AUTO, "fans",
+           CTLFLAG_RD, NULL, NULL);
+       for (i = 0; i < WB_FAN_COUNT; i++) {
+               /* Supports up to 12 fans */
+               char name[sizeof("fan_") + 2];
+
+               if (!fan_enabled(sc, i))
+                       continue;
+               snprintf(name, sizeof(name), "fan_%d", i);
+               SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, name,
+                   CTLTYPE_INT | CTLFLAG_RD, sc, WB_FAN_BASE + i,
+                   w83793g_fan_sysctl, "I", NULL);
+       }
+       SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(root), OID_AUTO, "chassis_open",
+           CTLTYPE_U8 | CTLFLAG_RD, sc, 0, w83793g_case_sysctl, "CU",
+           "report if the chassis_open was latched");
+       return (0);
+}
+
+static int
+w83793g_detach(device_t dev)
+{
+       return (ENXIO);
+}
+
+static int
+w83793g_temp_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       struct w83793g_softc *sc = arg1;
+       int reg = arg2;
+       int temp;
+       int error;
+       int8_t t_reg;
+       uint8_t t_low;
+
+       error = w83793g_readreg(sc->sc_dev, reg, &t_reg);
+       if (error != 0)
+               return (error);
+
+       if (reg < WB_TD_BASE + WB_TD_COUNT) {
+               error = w83793g_readreg(sc->sc_dev, WB_TLOW, &t_low);
+               if (error != 0)
+                       return (error);
+       } else
+               t_low = 0;
+
+       temp = (int)t_reg * 100;
+       temp += (t_low >> (2 * (reg - WB_TD_BASE)) & 0x3) * 25;
+       temp += 27315;  /* Convert celsius to kelvin */
+
+       error = sysctl_handle_int(oidp, &temp, 0, req);
+
+       return (error);
+}
+
+static int
+w83793g_fan_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       struct w83793g_softc *sc = arg1;
+       int reg = arg2;
+       int count;
+       int error;
+       uint8_t reg_vals[2];    /* Fan count is 2 bytes */
+
+       error = iicdev_readfrom(sc->sc_dev, reg, reg_vals, sizeof(reg_vals),
+           IIC_WAIT);
+       if (error != 0)
+               return (error);
+
+       count = ((int)reg_vals[0] << 8) | reg_vals[1];
+       error = sysctl_handle_int(oidp, &count, 0, req);
+
+       return (error);
+}
+
+static int
+w83793g_voltage_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       struct w83793g_softc *sc = arg1;
+       const struct wb_vsens *sensor;
+       int index = arg2;
+       int volts;
+       int error;
+       uint8_t v_reg;
+       uint8_t v_low;
+
+       sensor = &voltages[index];
+       error = w83793g_readreg(sc->sc_dev, sensor->reg, &v_reg);
+       if (error != 0)
+               return (error);
+
+       volts = v_reg;
+       if (sensor->left_low != 0) {
+               volts <<= 2;
+               error = w83793g_readreg(sc->sc_dev, WB_VLOW, &v_low);
+               if (error != 0)
+                       return (error);
+               volts |= (v_low >> (sensor->left_low - 1) & 0x3);
+       }
+
+       volts *= sensor->scale;
+       volts += sensor->add;
+
+       error = sysctl_handle_int(oidp, &volts, 0, req);
+
+       return (error);
+}
+
+static int
+w83793g_case_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       struct w83793g_softc *sc = arg1;
+       int error;
+       uint8_t reg;
+       bool chassis;
+
+       error = w83793g_readreg(sc->sc_dev, INT_STS4, &reg);
+       if (error != 0)
+               return (error);
+
+       chassis = ((reg & CHASSIS) != 0);
+
+       return (sysctl_handle_bool(oidp, &chassis, 0, req));
+}
diff --git a/sys/modules/i2c/Makefile b/sys/modules/i2c/Makefile
index ff4536694dfc..64b22db3319d 100644
--- a/sys/modules/i2c/Makefile
+++ b/sys/modules/i2c/Makefile
@@ -32,7 +32,8 @@ SUBDIR += hym8563 \
          rv3032 \
          rx8803 \
          tca64xx \
-         tmp461
+         tmp461 \
+         w83793g
 .endif
 
 .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \
diff --git a/sys/modules/i2c/w83793g/Makefile b/sys/modules/i2c/w83793g/Makefile
new file mode 100644
index 000000000000..d4329b00d30a
--- /dev/null
+++ b/sys/modules/i2c/w83793g/Makefile
@@ -0,0 +1,14 @@
+.PATH: ${SRCTOP}/sys/dev/iicbus/sensor/
+KMOD   = w83793g
+SRCS   = w83793g.c
+
+# Generated files...
+SRCS+= \
+       bus_if.h \
+       clock_if.h \
+       device_if.h \
+       iicbus_if.h \
+       opt_platform.h \
+       ofw_bus_if.h \
+
+.include <bsd.kmod.mk>

Reply via email to