diff --git a/sys/conf/files b/sys/conf/files index 6804c9c81c6..fac94252a36 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 00000000000..772384aa57a --- /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 +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* + * 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, ®); + if (error != 0) + return (false); + return ((reg & TD_MD_M(sensor)) != 0); + } else { + error = w83793g_readreg(sc->sc_dev, WB_TR_MD, ®); + 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, ®); + 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 ff4536694df..64b22db3319 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 00000000000..d4329b00d30 --- /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