Index: src/sys/dev/i2c/dbcool.c =================================================================== RCS file: /cvsroot/src/sys/dev/i2c/dbcool.c,v retrieving revision 1.69 diff -u -r1.69 dbcool.c --- src/sys/dev/i2c/dbcool.c 7 Jul 2026 12:42:58 -0000 1.69 +++ src/sys/dev/i2c/dbcool.c 29 Jul 2026 09:22:30 -0000 @@ -73,7 +73,7 @@ /* Sensor read functions */ static void dbcool_refresh(struct sysmon_envsys *, envsys_data_t *); -static int dbcool_read_rpm(struct dbcool_softc *, uint8_t); +static int dbcool_read_rpm(struct dbcool_softc *, uint8_t, int); static int dbcool_read_temp(struct dbcool_softc *, uint8_t, bool); static int dbcool_read_volt(struct dbcool_softc *, uint8_t, int, bool); @@ -107,8 +107,9 @@ static int sysctl_dbcool_thyst(SYSCTLFN_PROTO); /* Set-up subroutines */ +static int dbcool_set_fan_divider(struct dbcool_chipset *, int, uint8_t); static void dbcool_setup_controllers(struct dbcool_softc *); -static int dbcool_setup_sensors(struct dbcool_softc *); +static int dbcool_setup_sensors(struct dbcool_softc *, prop_dictionary_t); static int dbcool_attach_sensor(struct dbcool_softc *, int); static int dbcool_attach_temp_control(struct dbcool_softc *, int, struct chip_id *); @@ -538,6 +539,12 @@ { DBC_EOF, {0, 0, 0 }, 0, 0, 0 } }; +struct dbcool_fan_div ADM1030_fan_div_table[] = { + { 0x08, 0x20 }, + { 0x09, 0x21 }, + { 0, 0 } +}; + struct dbcool_power_control ADM1030_power_table[] = { { { DBCOOL_ADM1030_CFG1, DBCOOL_NO_REG, DBCOOL_NO_REG, DBCOOL_ADM1030_FAN_SPEED_CFG }, @@ -664,60 +671,62 @@ struct chip_id chip_table[] = { { DBCOOL_COMPANYID, ADT7490_DEVICEID, ADT7490_REV_ID, - ADT7490_sensor_table, ADT7475_power_table, + ADT7490_sensor_table, ADT7475_power_table, NULL, DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_MAXDUTY | DBCFLAG_HAS_PECI, 90000 * 60, "ADT7490" }, { DBCOOL_COMPANYID, ADT7476_DEVICEID, 0xff, - ADT7476_sensor_table, ADT7475_power_table, + ADT7476_sensor_table, ADT7475_power_table, NULL, DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_MAXDUTY, 90000 * 60, "ADT7476" }, { DBCOOL_COMPANYID, ADT7475_DEVICEID, 0xff, - ADT7475_sensor_table, ADT7475_power_table, + ADT7475_sensor_table, ADT7475_power_table, NULL, DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_MAXDUTY | DBCFLAG_HAS_SHDN, 90000 * 60, "ADT7475" }, { DBCOOL_COMPANYID, ADT7473_DEVICEID, ADT7473_REV_ID1, - ADT7475_sensor_table, ADT7475_power_table, + ADT7475_sensor_table, ADT7475_power_table, NULL, DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_MAXDUTY | DBCFLAG_HAS_SHDN, 90000 * 60, "ADT7460/ADT7463" }, { DBCOOL_COMPANYID, ADT7473_DEVICEID, ADT7473_REV_ID2, - ADT7475_sensor_table, ADT7475_power_table, + ADT7475_sensor_table, ADT7475_power_table, NULL, DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_MAXDUTY | DBCFLAG_HAS_SHDN, 90000 * 60, "ADT7463-1" }, { DBCOOL_COMPANYID, ADT7468_DEVICEID, 0xff, - ADT7476_sensor_table, ADT7475_power_table, + ADT7476_sensor_table, ADT7475_power_table, NULL, DBCFLAG_TEMPOFFSET | DBCFLAG_MULTI_VCC | DBCFLAG_HAS_MAXDUTY | DBCFLAG_4BIT_VER | DBCFLAG_HAS_SHDN, 90000 * 60, "ADT7467/ADT7468" }, { DBCOOL_COMPANYID, ADT7466_DEVICEID, 0xff, - ADT7466_sensor_table, NULL, + ADT7466_sensor_table, NULL, NULL, DBCFLAG_ADT7466 | DBCFLAG_TEMPOFFSET | DBCFLAG_HAS_SHDN, 82000 * 60, "ADT7466" }, { DBCOOL_COMPANYID, ADT7463_DEVICEID, ADT7463_REV_ID1, - ADM1027_sensor_table, ADT7475_power_table, + ADM1027_sensor_table, ADT7475_power_table, NULL, DBCFLAG_MULTI_VCC | DBCFLAG_4BIT_VER | DBCFLAG_HAS_SHDN, 90000 * 60, "ADT7463" }, { DBCOOL_COMPANYID, ADT7463_DEVICEID, ADT7463_REV_ID2, - ADM1027_sensor_table, ADT7475_power_table, + ADM1027_sensor_table, ADT7475_power_table, NULL, DBCFLAG_MULTI_VCC | DBCFLAG_4BIT_VER | DBCFLAG_HAS_SHDN | DBCFLAG_HAS_VID_SEL, 90000 * 60, "ADT7463" }, { DBCOOL_COMPANYID, ADM1027_DEVICEID, ADM1027_REV_ID, - ADM1027_sensor_table, ADT7475_power_table, + ADM1027_sensor_table, ADT7475_power_table, NULL, DBCFLAG_MULTI_VCC | DBCFLAG_4BIT_VER, 90000 * 60, "ADM1027" }, { DBCOOL_COMPANYID, ADM1030_DEVICEID, 0xff, ADM1030_sensor_table, ADM1030_power_table, - DBCFLAG_ADM1030 | DBCFLAG_NO_READBYTE, + ADM1030_fan_div_table, + DBCFLAG_ADM1030 | DBCFLAG_NO_READBYTE | DBCFLAG_HAS_FANDIV, 11250 * 60, "ADM1030" }, { DBCOOL_COMPANYID, ADM1031_DEVICEID, 0xff, ADM1031_sensor_table, ADM1030_power_table, - DBCFLAG_ADM1030 | DBCFLAG_NO_READBYTE, + ADM1030_fan_div_table, + DBCFLAG_ADM1030 | DBCFLAG_NO_READBYTE | DBCFLAG_HAS_FANDIV, 11250 * 60, "ADM1031" }, { SMSC_COMPANYID, EMC6D103S_DEVICEID, EMC6D103S_REV_ID, - EMC6D103S_sensor_table, ADT7475_power_table, + EMC6D103S_sensor_table, ADT7475_power_table, NULL, DBCFLAG_4BIT_VER, 90000 * 60, "EMC6D103S" }, - { 0, 0, 0, NULL, NULL, 0, 0, NULL } + { 0, 0, 0, NULL, NULL, NULL, 0, 0, NULL } }; static const char *behavior[] = { @@ -1004,7 +1013,7 @@ } static int -dbcool_read_rpm(struct dbcool_softc *sc, uint8_t reg) +dbcool_read_rpm(struct dbcool_softc *sc, uint8_t reg, int fan_div) { int rpm; uint8_t rpm_lo, rpm_hi; @@ -1020,7 +1029,8 @@ return 0; /* 0xffff indicates stalled/failed fan */ /* don't divide by zero */ - return (rpm == 0)? 0 : (sc->sc_dc.dc_chip->rpm_dividend / rpm); + return (rpm == 0)? 0 : + (sc->sc_dc.dc_chip->rpm_dividend / rpm / fan_div); } /* Provide chip's supply voltage, in microvolts */ @@ -1536,7 +1546,7 @@ /* Create the sensors for this device */ sc->sc_sme = sysmon_envsys_create(); - if (dbcool_setup_sensors(sc)) + if (dbcool_setup_sensors(sc, device_properties(self))) goto out; if (sc->sc_root_sysctl_num != 0) { @@ -1602,13 +1612,47 @@ } static int -dbcool_setup_sensors(struct dbcool_softc *sc) +dbcool_set_fan_divider(struct dbcool_chipset *dcp, int flags, uint8_t fanchar_reg) { - int i; + uint8_t val; + int div; + + div = 1; + val = dcp->dc_readreg(dcp, fanchar_reg); + + if (flags & DBCFLAG_ADM1030) { + val &= DBCOOL_ADM1030_FANDIV_MASK; + switch (val) + { + case DBCOOL_ADM1030_FANDIV_8: + div = 8; + break; + case DBCOOL_ADM1030_FANDIV_4: + div = 4; + break; + case DBCOOL_ADM1030_FANDIV_2: + div = 2; + break; + case DBCOOL_ADM1030_FANDIV_1: + default: + div = 1; + break; + } + } + return div; +} + +static int +dbcool_setup_sensors(struct dbcool_softc *sc, prop_dictionary_t props) +{ + int i, j; int error = 0; - uint8_t vid_reg, vid_val; + uint8_t vid_reg, vid_val, fanchar_reg; + uint16_t props_fan_div, div_val; struct chip_id *chip = sc->sc_dc.dc_chip; + struct dbcool_fan_div *fan_div_table = chip->fan_div; + sc->sc_fan_num = 0; for (i=0; chip->table[i].type != DBC_EOF; i++) { if (i < DBCOOL_MAXSENSORS) sc->sc_sysctl_num[i] = -1; @@ -1642,11 +1686,43 @@ error = dbcool_attach_sensor(sc, i); break; case DBC_FAN: + /* Do we have a fan divider on the chip? */ + fanchar_reg = 0; + if (chip->flags & DBCFLAG_HAS_FANDIV && + fan_div_table != NULL) { + for (j=0; fan_div_table[j].fan_reg != 0; j++) + if (chip->table[i].reg.val_reg == + fan_div_table[j].fan_reg) + fanchar_reg = + fan_div_table[j].char_reg; + } + if (fanchar_reg) + sc->sc_fan_div[i] = + dbcool_set_fan_divider(&sc->sc_dc, + sc->sc_dc.dc_chip->flags, fanchar_reg); + else + sc->sc_fan_div[i] = 1; + + /* + * Do we have a fan divider property? + * Each 4 bits contains a divider for that fan. + * We only accept dividers up to 8x + */ + if (prop_dictionary_get_uint16(props, + "fan_div", &props_fan_div) != 0) { + div_val = + (props_fan_div >> (sc->sc_fan_num * 4)) + & 0x0f; + if (div_val > 1 && div_val < 9) + sc->sc_fan_div[i] *= div_val; + } + sc->sc_sensor[i].units = ENVSYS_SFANRPM; sc->sc_sensor[i].state = ENVSYS_SINVALID; sc->sc_sensor[i].flags |= ENVSYS_FMONLIMITS; sc->sc_sensor[i].flags |= ENVSYS_FHAS_ENTROPY; error = dbcool_attach_sensor(sc, i); + sc->sc_fan_num++; break; case DBC_VID: sc->sc_sensor[i].units = ENVSYS_INTEGER; @@ -1811,7 +1887,7 @@ dbcool_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) { struct dbcool_softc *sc=sme->sme_cookie; - int i, nom_volt_idx, cur; + int i, nom_volt_idx, fan_div, cur; struct reg_list *reg; i = edata->sensor; @@ -1829,7 +1905,8 @@ true); break; case ENVSYS_SFANRPM: - cur = dbcool_read_rpm(sc, reg->val_reg); + fan_div = sc->sc_fan_div[i]; + cur = dbcool_read_rpm(sc, reg->val_reg, fan_div); break; case ENVSYS_INTEGER: return; @@ -2015,9 +2092,10 @@ sysmon_envsys_lim_t *lims, uint32_t *props) { struct reg_list *reg = sc->sc_regs[idx]; + int fan_div = sc->sc_fan_div[idx]; int32_t limit; - limit = dbcool_read_rpm(sc, reg->lo_lim_reg); + limit = dbcool_read_rpm(sc, reg->lo_lim_reg, fan_div); if (limit) { lims->sel_critmin = limit; *props |= PROP_CRITMIN; Index: src/sys/dev/i2c/dbcool_reg.h =================================================================== RCS file: /cvsroot/src/sys/dev/i2c/dbcool_reg.h,v retrieving revision 1.9 diff -u -r1.9 dbcool_reg.h --- src/sys/dev/i2c/dbcool_reg.h 16 Apr 2020 23:29:53 -0000 1.9 +++ src/sys/dev/i2c/dbcool_reg.h 29 Jul 2026 09:22:30 -0000 @@ -362,6 +362,13 @@ #define DBCOOL_ADM1030_COMPANYID DBCOOL_COMPANYID_REG #define DBCOOL_ADM1030_REVISION DBCOOL_REVISION_REG +/* ADM1030 fan characteristics registers: fan speed range (divider) */ +#define DBCOOL_ADM1030_FANDIV_MASK 0xc0 +#define DBCOOL_ADM1030_FANDIV_1 0x00 +#define DBCOOL_ADM1030_FANDIV_2 0x40 +#define DBCOOL_ADM1030_FANDIV_4 0x80 +#define DBCOOL_ADM1030_FANDIV_8 0xc0 + /* * Macros to locate limit registers for the various sensor types */ Index: src/sys/dev/i2c/dbcool_var.h =================================================================== RCS file: /cvsroot/src/sys/dev/i2c/dbcool_var.h,v retrieving revision 1.18 diff -u -r1.18 dbcool_var.h --- src/sys/dev/i2c/dbcool_var.h 21 Sep 2025 13:54:56 -0000 1.18 +++ src/sys/dev/i2c/dbcool_var.h 29 Jul 2026 09:22:30 -0000 @@ -74,6 +74,7 @@ #define DBCFLAG_OBSOLETE 0x0020 /* was DBCFLAG_HAS_VID */ #define DBCFLAG_HAS_VID_SEL 0x0040 #define DBCFLAG_HAS_PECI 0x0080 +#define DBCFLAG_HAS_FANDIV 0x0100 #define DBCFLAG_NO_READBYTE 0x1000 #define DBCFLAG_ADM1030 0x2000 #define DBCFLAG_ADT7466 0x4000 @@ -95,6 +96,11 @@ int nom_volt_index; }; +struct dbcool_fan_div { + uint8_t fan_reg; + uint8_t char_reg; +}; + /* * The members of dbcool_power_control need to stay in the same order * as the enum dbc_pwm_params above @@ -125,6 +131,8 @@ int sc_sysctl_num[DBCOOL_MAXSENSORS]; struct reg_list *sc_regs[DBCOOL_MAXSENSORS]; int sc_nom_volt[DBCOOL_MAXSENSORS]; + int sc_fan_num; + int sc_fan_div[DBCOOL_MAXSENSORS]; int sc_temp_offset; int64_t sc_supply_voltage; bool sc_suspend; @@ -140,6 +148,7 @@ uint8_t rev; struct dbcool_sensor *table; struct dbcool_power_control *power; + struct dbcool_fan_div *fan_div; int flags; int rpm_dividend; const char *name;