--- src/sys/dev/i2c/adt7462.c.dist 2026-02-10 22:19:38.439986424 +0100 +++ src/sys/dev/i2c/adt7462.c 2026-02-11 13:54:00.639978756 +0100 @@ -0,0 +1,503 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2026 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: adm1026.c,v 1.15 2026/02/06 16:45:36 jdc Exp $"); + +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* Maximum number of each type of sensor */ +#define ADT7462_MAX_FANS 8 +#define ADT7462_MAX_TEMPS 4 +#define ADT7462_MAX_VOLTS 13 +#define ADT7462_MAX_SENSORS \ + (ADT7462_MAX_FANS + ADT7462_MAX_TEMPS + ADT7462_MAX_VOLTS) + +/* Fan conversions */ +#define VAL_TO_SPEED(msb, lsb) \ + (ADT7462_TACH_PERIOD / ((msb << 8) + lsb)) + +/* Temperature conversions */ +#define VAL_TO_TEMP(msb, lsb) \ + (ADT7462_TEMP_BASE + msb * 1000000 + (lsb >> 6) * 250000) +#define TEMP_TO_MSB(temp) (((temp - ADT7462_TEMP_BASE) / 1000000) & 0xff) + +/* Fan/temp/volt sensor offsets */ +#define ADT7462_FAN_NUM(x) (x) +#define ADT7462_TEMP_NUM(x) (x + ADT7462_MAX_FANS) +#define ADT7462_VOLT_NUM(x) (x + ADT7462_MAX_FANS + ADT7462_MAX_TEMPS) + +struct adt7462_softc { + device_t sc_dev; + i2c_tag_t sc_tag; + int sc_address; + bool sc_monitor; + + int sc_nfans, sc_ntemps, sc_nvolts; + int sc_map[ADT7462_MAX_SENSORS]; /* Map sysmon numbers to sensors */ + struct sysmon_envsys *sc_sme; + envsys_data_t sc_sensor[ADT7462_MAX_SENSORS]; +}; + +static int adt7462_match(device_t, cfdata_t, void *); +static int adt7462_ident(i2c_tag_t, i2c_addr_t, int, uint8_t*); +static void adt7462_attach(device_t, device_t, void *); +static int adt7462_detach(device_t, int); +bool adt7462_pmf_suspend(device_t, const pmf_qual_t *); +bool adt7462_pmf_resume(device_t, const pmf_qual_t *); + +static int adt7462_setup_fans(struct adt7462_softc *, uint8_t *, uint8_t); +static int adt7462_setup_temps(struct adt7462_softc *, uint8_t *); +static int adt7462_setup_volts(struct adt7462_softc *, uint8_t *); + +void adt7462_refresh(struct sysmon_envsys *, envsys_data_t *); +static void adt7462_read_fan(struct adt7462_softc *, envsys_data_t *); +static void adt7462_read_temp(struct adt7462_softc *, envsys_data_t *); +static void adt7462_read_volt(struct adt7462_softc *, envsys_data_t *); + +static int adt7462_read_reg_int(i2c_tag_t, i2c_addr_t, uint8_t, uint8_t *); +static int adt7462_write_reg(struct adt7462_softc *, uint8_t, uint8_t); + +static inline int +adt7462_read_reg(struct adt7462_softc *sc, uint8_t reg, uint8_t *val) +{ + return adt7462_read_reg_int(sc->sc_tag, sc->sc_address, reg, val); +} + +CFATTACH_DECL_NEW(adt7462sm, sizeof(struct adt7462_softc), + adt7462_match, adt7462_attach, adt7462_detach, NULL); + +static const struct device_compatible_entry compat_data[] = { + { .compat = "i2c-adt7462" }, + DEVICE_COMPAT_EOL +}; + +static int +adt7462_match(device_t parent, cfdata_t cf, void *aux) +{ + struct i2c_attach_args *ia = aux; + int match_result; + uint8_t rev; + + if (iic_use_direct_match(ia, cf, compat_data, &match_result)) + return match_result; + + if ((ia->ia_addr == ADT7462_ADDR1 || ia->ia_addr == ADT7462_ADDR2) + && adt7462_ident(ia->ia_tag, ia->ia_addr, 1, &rev)) + return I2C_MATCH_ADDRESS_AND_PROBE; + + return 0; +} + +static int +adt7462_ident(i2c_tag_t tag, i2c_addr_t addr, int probe_only, uint8_t *rev) +{ + uint8_t reg, val; + int err; + + /* Device, company and revision ID */ + reg = ADT7462_DEV_ID; + err = adt7462_read_reg_int(tag, addr, reg, &val); + if (err || val != ADT7462_DEV_ID_VAL) { + if (!probe_only) + aprint_verbose("adt7462_ident: " + "device ID invalid or missing\n"); + return 0; + } + reg = ADT7462_COMP_ID; + err = adt7462_read_reg_int(tag, addr, reg, &val); + if (err || val != ADT7462_COMP_ID_VAL) { + if (!probe_only) + aprint_verbose("adt7462_ident: " + "company ID invalid or missing\n"); + return 0; + } + reg = ADT7462_REV_ID; + err = adt7462_read_reg_int(tag, addr, reg, rev); + if (err || val != ADT7462_REV_ID_VAL) { + if (!probe_only) + aprint_verbose("adt7462_ident: " + "revision revision invalid or missing\n"); + return 0; + } + return 1; +} + +static void +adt7462_attach(device_t parent, device_t self, void *aux) +{ + struct adt7462_softc *sc = device_private(self); + struct i2c_attach_args *ia = aux; + prop_dictionary_t props = device_properties(self); + uint8_t reg, rev, val, fan_conf, pin_cfg[4]; + + sc->sc_tag = ia->ia_tag; + sc->sc_address = ia->ia_addr; + sc->sc_dev = self; + + if (prop_dictionary_get_uint8(props, "fan_conf", &fan_conf) == 0) + fan_conf = 0xff; /* 4 + 4 fans */ + + (void) adt7462_ident(sc->sc_tag, sc->sc_address, 0, &rev); + aprint_normal(": ADT7462 system monitor: rev. 0x%x\n", rev); + + /* + * Start monitoring if not already monitoring. + * Wait 1.0s for the fan readings to stabilise. + */ + reg = ADT7462_CONF1; + if (adt7462_read_reg(sc, reg, &val) != 0) { + aprint_error_dev(sc->sc_dev, ": unable to read conf1\n"); + return; + } + if (!(val & ADT7462_CONF1_MONITOR)) { + sc->sc_monitor = 1; + aprint_normal_dev(sc->sc_dev, + ": starting monitoring, waiting 1.0s for readings\n"); + val |= ADT7462_CONF1_MONITOR; + if (adt7462_write_reg(sc, reg, val) != 0) { + aprint_error_dev(sc->sc_dev, + ": unable to write conf1\n"); + return; + } + delay(1000000); + } else + sc->sc_monitor = 0; + + /* Read the pin config registers for fan/temp/volt setup. */ + reg = ADT7462_PIN_CONF1; + if (adt7462_read_reg(sc, reg, &val) != 0) { + aprint_error_dev(sc->sc_dev, ": unable to read pin conf1\n"); + return; + } + pin_cfg[0] = val; + reg = ADT7462_PIN_CONF2; + if (adt7462_read_reg(sc, reg, &val) != 0) { + aprint_error_dev(sc->sc_dev, ": unable to read pin conf2\n"); + return; + } + pin_cfg[1] = val; + reg = ADT7462_PIN_CONF3; + if (adt7462_read_reg(sc, reg, &val) != 0) { + aprint_error_dev(sc->sc_dev, ": unable to read pin conf3\n"); + return; + } + pin_cfg[2] = val; + reg = ADT7462_PIN_CONF4; + if (adt7462_read_reg(sc, reg, &val) != 0) { + aprint_error_dev(sc->sc_dev, ": unable to read pin conf4\n"); + return; + } + pin_cfg[3] = val; + + sc->sc_sme = sysmon_envsys_create(); + + sc->sc_nfans = 0; + sc->sc_ntemps = 0; + sc->sc_nvolts = 0; + if (adt7462_setup_fans(sc, pin_cfg, fan_conf)) + goto bad; + if (adt7462_setup_temps(sc, pin_cfg)) + goto bad; + if (adt7462_setup_volts(sc, pin_cfg)) + goto bad; + aprint_normal_dev(self, "%d fans, %d temperatures, %d voltages\n", + sc->sc_nfans, sc->sc_ntemps, sc->sc_nvolts); + + sc->sc_sme->sme_name = device_xname(self); + sc->sc_sme->sme_cookie = sc; + sc->sc_sme->sme_refresh = adt7462_refresh; + if (sysmon_envsys_register(sc->sc_sme)) { + aprint_error_dev(self, + "unable to register with sysmon\n"); + goto bad; + } + + if (!pmf_device_register(self, adt7462_pmf_suspend, adt7462_pmf_resume)) + aprint_error_dev(self, "couldn't establish power handler\n"); + + return; + +bad: + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; + return; +} + +bool +adt7462_pmf_suspend(device_t dev, const pmf_qual_t *qual) +{ + /* XXX stop monitor */ + return true; +} + +bool +adt7462_pmf_resume(device_t dev, const pmf_qual_t *qual) +{ + /* XXX start monitor */ + return true; +} + +static int +adt7462_detach(device_t self, int flags) +{ + struct adt7462_softc *sc = device_private(self); + + pmf_device_deregister(self); + + if (sc->sc_sme != NULL) + sysmon_envsys_unregister(sc->sc_sme); + + return 0; +} + +static int +adt7462_setup_fans(struct adt7462_softc *sc, uint8_t *pin_cfg, + uint8_t fan_conf) +{ + int i, map, snum; + uint8_t reg, val, fans; + + /* Check tach enable register to see which tachs are enabled. */ + reg = ADT7462_TACH_EN; + if (adt7462_read_reg(sc, reg, &val) != 0) { + aprint_error_dev(sc->sc_dev, "unable to read tach enable\n"); + return 1; + } + fans = val & fan_conf; + + /* Don't check the fan present register - it might not be set up. */ + + for (i = 0; i < ADT7462_MAX_FANS; i++) { + /* Check tach mask and pin1/pin2 configurations. */ + if (!ADT7462_FAN_TACH_EN(fans, i) || + !ADT7462_PIN1_TACH(pin_cfg[0], i) || + !ADT7462_PIN2_TACH(pin_cfg[1], i)) + continue; + + snum = ADT7462_FAN_NUM(i); + + /* Set up sysmon sensor */ + sc->sc_sensor[snum].units = ENVSYS_SFANRPM; + sc->sc_sensor[snum].state = ENVSYS_SINVALID; + snprintf(sc->sc_sensor[snum].desc, + sizeof(sc->sc_sensor[snum].desc), "fan %d", snum); + if (sysmon_envsys_sensor_attach( + sc->sc_sme, &sc->sc_sensor[snum])) { + aprint_error_dev(sc->sc_dev, + "unable to attach fan %d at sysmon\n", i); + return 1; + } + map = sc->sc_sensor[snum].sensor; + sc->sc_map[map] = i; + sc->sc_nfans++; + } + return 0; +} + +static int +adt7462_setup_temps(struct adt7462_softc *sc, uint8_t *pin_cfg) +{ + int i, map, snum; + + strlcpy(sc->sc_sensor[ADT7462_TEMP_NUM(0)].desc, "local", + sizeof(sc->sc_sensor[ADT7462_TEMP_NUM(0)].desc)); + strlcpy(sc->sc_sensor[ADT7462_TEMP_NUM(1)].desc, "remote 1", + sizeof(sc->sc_sensor[ADT7462_TEMP_NUM(1)].desc)); + strlcpy(sc->sc_sensor[ADT7462_TEMP_NUM(2)].desc, "remote 2", + sizeof(sc->sc_sensor[ADT7462_TEMP_NUM(2)].desc)); + strlcpy(sc->sc_sensor[ADT7462_TEMP_NUM(3)].desc, "remote 3", + sizeof(sc->sc_sensor[ADT7462_TEMP_NUM(3)].desc)); + + for (i = 0; i < ADT7462_MAX_TEMPS; i++) { + /* Check pin1 configurations. */ + if (!ADT7462_PIN1_TEMP(pin_cfg[0], i)) + continue; + + snum = ADT7462_TEMP_NUM(i); + + /* Set up sysmon sensor */ + sc->sc_sensor[snum].units = ENVSYS_STEMP; + sc->sc_sensor[snum].state = ENVSYS_SINVALID; + if (sysmon_envsys_sensor_attach( + sc->sc_sme, &sc->sc_sensor[snum])) { + aprint_error_dev(sc->sc_dev, + "unable to attach temp %d at sysmon\n", i); + return 1; + } + map = sc->sc_sensor[ADT7462_TEMP_NUM(i)].sensor; + sc->sc_map[map] = i; + sc->sc_ntemps++; + } + return 0; +} + +static int +adt7462_setup_volts(struct adt7462_softc *sc, uint8_t *pin_cfg) +{ +/* + int i, map, snum; + + for (i = 0; i < ADT7462_MAX_VOLTS; i++) { + snum = ADT7462_VOLT_NUM(i); + +*/ + /* Set up sysmon sensor */ +/* + sc->sc_sensor[snum].units = ENVSYS_SVOLTS_DC; + sc->sc_sensor[snum].state = ENVSYS_SINVALID; + strlcpy(sc->sc_sensor[snum].desc, + adt7462_volts_table[i].desc, + sizeof(sc->sc_sensor[snum].desc)); + if (sysmon_envsys_sensor_attach( + sc->sc_sme, &sc->sc_sensor[snum])) { + aprint_error_dev(sc->sc_dev, + "unable to attach volts %d at sysmon\n", i); + return 1; + } + map = sc->sc_sensor[snum].sensor; + sc->sc_map[map] = i; + sc->sc_nvolts++; + } +*/ + return 0; +} + +void +adt7462_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) +{ + struct adt7462_softc *sc = sme->sme_cookie; + + if (edata->sensor < sc->sc_nfans) + adt7462_read_fan(sc, edata); + else if (edata->sensor < sc->sc_nfans + sc->sc_ntemps) + adt7462_read_temp(sc, edata); + else + adt7462_read_volt(sc, edata); +} + +static void +adt7462_read_fan(struct adt7462_softc *sc, envsys_data_t *edata) +{ + int fan = sc->sc_map[edata->sensor]; + uint8_t reg, lsb, msb; + + /* Read LSB then MSB to ensure correct reading */ + reg = ADT7462_TACH_VAL_LSB(fan); + if (adt7462_read_reg(sc, reg, &lsb) != 0) { + edata->state = ENVSYS_SINVALID; + return; + } + reg = ADT7462_TACH_VAL_MSB(fan); + if (adt7462_read_reg(sc, reg, &msb) != 0) { + edata->state = ENVSYS_SINVALID; + return; + } + if ((msb == 0xff && lsb == 0xff) || (msb == 0x00 && lsb == 0x00)) + /* Fan missing or stopped */ + edata->value_cur = 0; + else + edata->value_cur = VAL_TO_SPEED(msb, lsb); + edata->state = ENVSYS_SVALID; +} + +static void +adt7462_read_temp(struct adt7462_softc *sc, envsys_data_t *edata) +{ + int temp = sc->sc_map[edata->sensor]; + uint8_t reg, lsb, msb; + + /* Read LSB then MSB to ensure correct reading */ + reg = ADT7462_TEMP_VAL_LSB(temp); + if (adt7462_read_reg(sc, reg, &lsb) != 0) { + edata->state = ENVSYS_SINVALID; + return; + } + reg = ADT7462_TEMP_VAL_MSB(temp); + if (adt7462_read_reg(sc, reg, &msb) != 0) { + edata->state = ENVSYS_SINVALID; + return; + } + + edata->value_cur = VAL_TO_TEMP(msb, lsb); + edata->state = ENVSYS_SVALID; +} + +static void +adt7462_read_volt(struct adt7462_softc *sc, envsys_data_t *edata) +{ +/* + int volt = sc->sc_map[edata->sensor]; + uint8_t reg, val; + + reg = adt7462_volts_table[volt].reg; + if (adt7462_read_reg(sc, reg, &val) != 0) { + edata->state = ENVSYS_SINVALID; + return; + } + edata->value_cur = (int) val * adt7462_volts_table[volt].incr; + edata->state = ENVSYS_SVALID; +*/ +} + +static int +adt7462_read_reg_int(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t *val) +{ + int err = 0; + + if ((err = iic_acquire_bus(tag, 0)) != 0) + return err; + err = iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, ®, 1, val, 1, 0); + iic_release_bus(tag, 0); + return err; +} + +static int +adt7462_write_reg(struct adt7462_softc *sc, uint8_t reg, uint8_t val) +{ + int err = 0; + + if ((err = iic_acquire_bus(sc->sc_tag, 0)) != 0) + return err; + err = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, + ®, 1, &val, 1, 0); + iic_release_bus(sc->sc_tag, 0); + return err; +} --- src/sys/dev/i2c/adt7462reg.h.dist 2026-02-10 22:19:43.319986421 +0100 +++ src/sys/dev/i2c/adt7462reg.h 2026-02-11 12:53:47.009980910 +0100 @@ -0,0 +1,711 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2026 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_I2C_ADT7462REG_H_ +#define _DEV_I2C_ADT7462REG_H_ + +/* + * Register definitions for "ADT7462 Flexible Temperature, Voltage Monitor, + * and System Fan Controller". + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#define ADT7462_ADDR1 0x58 +#define ADT7462_ADDR2 0x5c + +#define ADT7462_CONF0 0x00 +#define ADT7462_CONF1 0x01 +#define ADT7462_CONF2 0x02 +#define ADT7462_CONF3 0x03 +#define ADT7462_TACH_EN 0x07 +#define ADT7462_TACH_CFG 0x08 +#define ADT7462_GPIO1_CFG 0x09 +#define ADT7462_GPIO2_CFG 0x0a +#define ADT7462_TMIN_CAL1 0x0b +#define ADT7462_TMIN_CAL2 0x0c +#define ADT7462_THERM_CONF 0x0d +#define ADT7462_CONF_THERM1 0x0e +#define ADT7462_CONF_THERM2 0x0f +#define ADT7462_PIN_CONF1 0x10 +#define ADT7462_PIN_CONF2 0x11 +#define ADT7462_PIN_CONF3 0x12 +#define ADT7462_PIN_CONF4 0x13 +#define ADT7462_EASY_CONF 0x14 +#define ADT7462_EDO_ENABLE 0x16 +#define ADT7462_ATTEN1_EN 0x18 +#define ADT7462_ATTEN2_EN 0x19 +#define ADT7462_ACCOUSTICS1 0x1a +#define ADT7462_ACCOUSTICS2 0x1b +#define ADT7462_FAN_TEST 0x1c +#define ADT7462_FANS_PRESENT 0x1d +#define ADT7462_FAN_TEST_EN 0x1e +#define ADT7462_PWM1_CFG 0x21 +#define ADT7462_PWM2_CFG 0x22 +#define ADT7462_PWM3_CFG 0x23 +#define ADT7462_PWM4_CFG 0x24 +#define ADT7462_PWM12_FREQ 0x25 +#define ADT7462_PWM34_FREQ 0x26 +#define ADT7462_PWM1_MIN 0x28 /* Minimum PWM1 duty cycle */ +#define ADT7462_PWM2_MIN 0x29 /* Minimum PWM2 duty cycle */ +#define ADT7462_PWM3_MIN 0x2a /* Minimum PWM3 duty cycle */ +#define ADT7462_PWM4_MIN 0x2b /* Minimum PWM4 duty cycle */ +#define ADT7462_PWM1234_MAX 0x2c /* Maximum all PWM duty cycle */ +#define ADT7462_THERM_MASK1 0x30 +#define ADT7462_THERM_MASK2 0x31 +#define ADT7462_VOLT_MASK1 0x32 +#define ADT7462_VOLT_MASK2 0x33 +#define ADT7462_FAN_MASK 0x34 +#define ADT7462_DIG_MASK 0x35 +#define ADT7462_GPIO_MASK 0x36 +#define ADT7462_EDO_MASK1 0x37 +#define ADT7462_EDO_MASK2 0x38 +#define ADT7462_DEV_ID 0x3d +#define ADT7462_COMP_ID 0x3e +#define ADT7462_REV_ID 0x3f +#define ADT7462_LOCAL_LOW 0x44 +#define ADT7462_REM1_LOW 0x45 /* Register contents depend on */ +#define ADT7462_PIN15V_LOW 0x45 /* pin configuration. */ +#define ADT7462_REM2_LOW 0x46 +#define ADT7462_REM3_LOW 0x47 +#define ADT7462_PIN19V_LOW 0x47 +#define ADT7462_LOCAL_HIGH 0x48 +#define ADT7462_REM1_HIGH 0x49 +#define ADT7462_PIN15V_HIGH 0x49 +#define ADT7462_REM2_HIGH 0x4a +#define ADT7462_REM3_HIGH 0x4b +#define ADT7462_PIN19V_HIGH 0x4b +#define ADT7462_LOCAL_THERM 0x4c +#define ADT7462_15V2_HIGH 0x4c +#define ADT7462_REM1_THERM 0x4d +#define ADT7462_REM2_THERM 0x4e +#define ADT7462_REM3_THERM 0x4f +#define ADT7462_LOCAL_THERM2 0x50 +#define ADT7462_15V1_HIGH 0x50 +#define ADT7462_REM1_THERM2 0x51 +#define ADT7462_REM2_THERM2 0x52 +#define ADT7462_REM3_THERM2 0x53 +#define ADT7462_LOCREM1_HYST 0x54 +#define ADT7462_REM23_HYST 0x55 +#define ADT7462_LOCAL_OFF 0x56 +#define ADT7462_REM1_OFF 0x57 +#define ADT7462_REM2_OFF 0x58 +#define ADT7462_REM3_OFF 0x59 +#define ADT7462_REM1_OPP 0x5a +#define ADT7462_REM2_OPP 0x5b +#define ADT7462_LOCAL_MIN 0x5c +#define ADT7462_REM1_MIN 0x5d +#define ADT7462_REM2_MIN 0x5e +#define ADT7462_REM3_MIN 0x5f +#define ADT7462_LOCAL_RANGE 0x60 +#define ADT7462_REM1_RANGE 0x61 +#define ADT7462_REM2_RANGE 0x62 +#define ADT7462_REM3_RANGE 0x63 +#define ADT7462_OPP_HYST 0x64 +#define ADT7462_33V_HIGH 0x68 +#define ADT7462_PIN23V_HIGH 0x69 +#define ADT7462_PIN24V_HIGH 0x6a +#define ADT7462_PIN25V_HIGH 0x6b +#define ADT7462_PIN26V_HIGH 0x6c +#define ADT7462_12V1_LOW 0x6d +#define ADT7462_12V2_LOW 0x6e +#define ADT7462_12V3_LOW 0x6f +#define ADT7462_33V_LOW 0x70 +#define ADT7462_5V_LOW 0x71 +#define ADT7462_PIN23V_LOW 0x72 +#define ADT7462_PIN24V_LOW 0x73 +#define ADT7462_PIN25V_LOW 0x74 +#define ADT7462_PIN26V_LOW 0x75 +#define ADT7462_15V1_LOW 0x76 +#define ADT7462_15V2_LOW 0x77 +#define ADT7462_TACH1_LIMIT 0x78 +#define ADT7462_VID_LIMIT 0x78 +#define ADT7462_TACH2_LIMIT 0x79 +#define ADT7462_TACH3_LIMIT 0x7a +#define ADT7462_TACH4_LIMIT 0x7b +#define ADT7462_TACH5_LIMIT 0x7c +#define ADT7462_12V1_HIGH 0x7c +#define ADT7462_TACH6_LIMIT 0x7d +#define ADT7462_12V2_HIGH 0x7d +#define ADT7462_TACH7_LIMIT 0x7e +#define ADT7462_5V_HIGH 0x7e +#define ADT7462_TACH8_LIMIT 0x7f +#define ADT7462_12V3_HIGH 0x7f +#define ADT7462_THERM1_TIMELIM 0x80 +#define ADT7462_THERM2_TIMELIM 0x81 +#define ADT7462_LOCAL_VAL_LSB 0x88 +#define ADT7462_LOCAL_VAL_MSB 0x89 +#define ADT7462_REM1_VAL_LSB 0x8a +#define ADT7462_REM1_VAL_MSB 0x8b +#define ADT7462_PIN15V_VAL 0x8b +#define ADT7462_REM2_VAL_LSB 0x8c +#define ADT7462_REM2_VAL_MSB 0x8d +#define ADT7462_REM3_VAL_LSB 0x8e +#define ADT7462_REM3_VAL_MSB 0x8f +#define ADT7462_PIN23V_VAL 0x90 +#define ADT7462_PIN24V_VAL 0x91 +#define ADT7462_PIN25V_VAL 0x92 +#define ADT7462_PIN26V_VAL 0x93 +#define ADT7462_15V1_VAL 0x94 +#define ADT7462_15V2_VAL 0x95 +#define ADT7462_33V_VAL 0x96 +#define ADT7462_VID_VAL 0x97 +#define ADT7462_TACH1_VAL_LSB 0x98 +#define ADT7462_TACH1_VAL_MSB 0x99 +#define ADT7462_TACH2_VAL_LSB 0x9a +#define ADT7462_TACH2_VAL_MSB 0x9b +#define ADT7462_TACH3_VAL_LSB 0x9c +#define ADT7462_TACH3_VAL_MSB 0x9d +#define ADT7462_TACH4_VAL_LSB 0x9e +#define ADT7462_TACH4_VAL_MSB 0x9f +#define ADT7462_TACH5_VAL_LSB 0xa2 +#define ADT7462_TACH5_VAL_MSB 0xa3 +#define ADT7462_12V1_VAL 0xa3 +#define ADT7462_TACH6_VAL_LSB 0xa4 +#define ADT7462_TACH6_VAL_MSB 0xa5 +#define ADT7462_12V2_VAL 0xa5 +#define ADT7462_TACH7_VAL_LSB 0xa6 +#define ADT7462_TACH7_VAL_MSB 0xa7 +#define ADT7462_5V_VAL 0xa7 +#define ADT7462_TACH8_VAL_LSB 0xa8 +#define ADT7462_TACH8_VAL_MSB 0xa9 +#define ADT7462_12V3_VAL 0xa9 +#define ADT7462_PWM1_CYCLE 0xaa +#define ADT7462_PWM2_CYCLE 0xab +#define ADT7462_PWM3_CYCLE 0xac +#define ADT7462_PWM4_CYCLE 0xad +#define ADT7462_THERM1_ON_TIM 0xae +#define ADT7462_THERM2_ON_TIM 0xaf +#define ADT7462_TEMP1_STAT_H 0xb8 +#define ADT7462_TEMP2_STAT_H 0xb9 +#define ADT7462_TEMP3_STAT_H 0xba +#define ADT7462_VOLT1_STAT_H 0xbb +#define ADT7462_VOLT2_STAT_H 0xbc +#define ADT7462_FAN_STAT_H 0xbd +#define ADT7462_DIG_STAT_H 0xbe +#define ADT7462_GPIO_STAT_H 0xbf +#define ADT7462_TEMP1_STAT_B 0xc0 +#define ADT7462_TEMP2_STAT_B 0xc1 +#define ADT7462_VOLT1_STAT_B 0xc3 +#define ADT7462_VOLT2_STAT_B 0xc4 +#define ADT7462_FAN_STAT_B 0xc5 +#define ADT7462_DIG_STAT_B 0xc6 + +/* 0x00: Configuration register 0 */ +#define ADT7462_CONF0_BLK_MASK 0x3f /* # registers for block read */ +#define ADT7462_CONF0_VID_SPEC 0x40 /* 0 = VR10, 1 = VR11 */ +#define ADT7462_CONF0_RESET 0x80 /* Reset all unlocked registers */ + +/* 0x01: Configuration register 1 */ +#define ADT7462_CONF1_MONITOR 0x01 /* Start monitoring temps and volts */ +#define ADT7462_CONF1_ALERT 0x08 /* 0 = SMB, 1 = comparator */ +#define ADT7462_CONF1_FAST_SPIN 0x10 /* 1 = no fast spin-up */ +#define ADT7462_CONF1_COMPLETE 0x20 /* Setup complete, start monitoring */ +#define ADT7462_CONF1_LOCK 0x40 /* Lock limit registers */ +#define ADT7462_CONF1_READY 0x80 /* Ready to start monitoring */ + +/* 0x02: Configuration register 2 */ +#define ADT7462_CONF2_FAN_HF 0x01 /* Fast fan measurements */ +#define ADT7462_CONF2_PWM_HF 0x04 /* High frequency PWM measurements */ +#define ADT7462_CONF2_VRD1 0x08 /* Full speed fans on VRD1 */ +#define ADT7462_CONF2_VRD2 0x10 /* Full speed fans on VRD2 */ +#define ADT7462_CONF2_FFULL 0x20 /* Full speed fans */ +#define ADT7462_CONF2_TACH 0xc0 /* Tach pulse to measure */ + +/* 0x03: Configuration register 3 */ +#define ADT7462_CONF3_GPIO_EN 0x01 /* Enable GPIO's */ +#define ADT7462_CONF3_SCL_TO 0x02 /* Enable SCL timeout */ +#define ADT7462_CONF3_SDA_TO 0x04 /* Enable SDA timeout */ +#define ADT7462_CONF3_VID_LVL 0x08 /* Set low VID threshold */ +#define ADT7462_CONF3_THERM_LVL 0x10 /* Set low therm threshold */ +#define ADT7462_CONF3_CI_RESET 0x20 /* Reset chassis intrusion circuit */ +#define ADT7462_CONF3_XOR_TEST 0x40 /* Enable XOR tree test */ +#define ADT7462_CONF3_VCORE_LOW 0x80 /* Set V_core_low */ + +/* 0x07: Tach enable register */ +#define ADT7462_TACH1_ENABLE 0x01 /* Enable tach1 measurement */ +#define ADT7462_TACH2_ENABLE 0x02 /* Enable tach2 measurement */ +#define ADT7462_TACH3_ENABLE 0x04 /* Enable tach3 measurement */ +#define ADT7462_TACH4_ENABLE 0x08 /* Enable tach4 measurement */ +#define ADT7462_TACH5_ENABLE 0x10 /* Enable tach5 measurement */ +#define ADT7462_TACH6_ENABLE 0x20 /* Enable tach6 measurement */ +#define ADT7462_TACH7_ENABLE 0x40 /* Enable tach7 measurement */ +#define ADT7462_TACH8_ENABLE 0x80 /* Enable tach8 measurement */ + +#define ADT7462_FAN_TACH_EN(val, x) ((val & (1 << x)) != 0) + +/* 0x08: Tach configuration register */ +#define ADT7462_TACH15_CONT 0x01 /* Continuous tach1+5 measurement */ +#define ADT7462_TACH26_CONT 0x02 /* Continuous tach2+6 measurement */ +#define ADT7462_TACH37_CONT 0x04 /* Continuous tach3+7 measurement */ +#define ADT7462_TACH48_CONT 0x08 /* Continuous tach4+8 measurement */ + +/* 0x09: GPIO configuration register 1 */ +#define ADT7462_GPIO1_POL 0x01 /* GPIO1 polarity (0 low, 1 high) */ +#define ADT7462_GPIO1_DIR 0x02 /* GPIO1 direction (0 in, 1 out) */ +#define ADT7462_GPIO2_POL 0x04 /* GPIO2 polarity (0 low, 1 high) */ +#define ADT7462_GPIO2_DIR 0x08 /* GPIO2 direction (0 in, 1 out) */ +#define ADT7462_GPIO3_POL 0x10 /* GPIO3 polarity (0 low, 1 high) */ +#define ADT7462_GPIO3_DIR 0x20 /* GPIO3 direction (0 in, 1 out) */ +#define ADT7462_GPIO4_POL 0x40 /* GPIO4 polarity (0 low, 1 high) */ +#define ADT7462_GPIO4_DIR 0x80 /* GPIO4 direction (0 in, 1 out) */ + +/* 0x0a: GPIO configuration register 2 */ +#define ADT7462_GPIO5_POL 0x01 /* GPIO5 polarity (0 low, 1 high) */ +#define ADT7462_GPIO5_DIR 0x02 /* GPIO5 direction (0 in, 1 out) */ +#define ADT7462_GPIO6_POL 0x04 /* GPIO6 polarity (0 low, 1 high) */ +#define ADT7462_GPIO6_DIR 0x08 /* GPIO6 direction (0 in, 1 out) */ +#define ADT7462_GPIO7_POL 0x10 /* GPIO7 polarity (0 low, 1 high) */ +#define ADT7462_GPIO7_DIR 0x20 /* GPIO7 direction (0 in, 1 out) */ +#define ADT7462_GPIO8_POL 0x40 /* GPIO8 polarity (0 low, 1 high) */ +#define ADT7462_GPIO8_DIR 0x80 /* GPIO8 direction (0 in, 1 out) */ + +/* 0x0b: Dynamix Tmin control register 1 */ +#define ADT7462_REM1_EN 0x01 /* Enable dynamic rem 1 Tmin control */ +#define ADT7462_REM2_EN 0x02 /* Enable dynamic rem 2 Tmin control */ +#define ADT7462_P1_R1 0x04 /* Copy rem 1 val to op if therm1 */ +#define ADT7462_P1_R2 0x08 /* Copy rem 2 val to op if therm1 */ +#define ADT7462_P2_R1 0x10 /* Copy rem 1 val to op if therm2 */ +#define ADT7462_P2_R2 0x20 /* Copy rem 2 val to op if therm2 */ + +/* 0x0c: Dynamix Tmin control register 2 */ +#define ADT7462_REM1_CYCLE 0x07 /* Rem 1 cycle mask */ +#define ADT7462_REM2_CYCLE 0x38 /* Rem 2 cycle mask */ +#define ADT7462_CTRL_LOOP 0x40 /* Control loop select */ + +/* 0x0d: Therm configuration register */ +#define ADT7462_BOOST1 0x01 /* Max fan PWM if therm1 */ +#define ADT7462_BOOST2 0x02 /* Max fan PWM if therm2 */ +#define ADT7462_THERM1_TIMER 0x1c /* Therm1 timer window mask */ +#define ADT7462_THERM2_TIMER 0xe0 /* Therm2 timer window mask */ + +/* 0x0e: Therm1 configuration register */ +#define ADT7462_THERM1_TIM_EN 0x01 /* Enable therm1 timer circuit */ +#define ADT7462_THERM1_LOCAL 0x02 /* Therm1 assert on local */ +#define ADT7462_THERM1_REM1 0x04 /* Therm1 assert on remote1 */ +#define ADT7462_THERM1_REM2 0x08 /* Therm1 assert on remote2 */ +#define ADT7462_THERM1_REM3 0x10 /* Therm1 assert on remote3 */ + +/* 0x0f: Therm2 configuration register */ +#define ADT7462_THERM2_TIM_EN 0x01 /* Enable therm2 timer circuit */ +#define ADT7462_THERM2_LOCAL 0x02 /* Therm2 assert on local */ +#define ADT7462_THERM2_REM1 0x04 /* Therm2 assert on remote1 */ +#define ADT7462_THERM2_REM2 0x08 /* Therm2 assert on remote2 */ +#define ADT7462_THERM2_REM3 0x10 /* Therm2 assert on remote3 */ + +/* 0x10: Pin configuration register 1 */ +#define ADT7462_PIN7_CONF 0x01 /* 0 = +12v1, 1 = Tach5 */ +#define ADT7462_PIN4_CONF 0x02 /* 0 = GPIO4, 1 = Tach4 */ +#define ADT7462_PIN3_CONF 0x04 /* 0 = GPIO3, 1 = Tach3 */ +#define ADT7462_PIN2_CONF 0x08 /* 0 = GPIO2, 1 = Tach2 */ +#define ADT7462_PIN1_CONF 0x10 /* 0 = GPIO1, 1 = Tach1 */ +#define ADT7462_DIODE3_CONF 0x20 /* 0 = volt/SCSI, 1 = D3+/D3- */ +#define ADT7462_DIODE1_CONF 0x40 /* 0 = volt/SCSI, 1 = D1+/D1- */ +#define ADT7462_VID_EN 0x80 /* 1 = En. VID's pins 1-4 28 31 32 */ + +#define ADT7462_PIN1_TACH(val, x) (x > 3 || \ + (!(val & ADT7462_VID_EN) && (val & (1 << x)) != 0)) +#define ADT7462_PIN1_TEMP(val, x) (x== 0 || x == 2 || \ + (x == 3 && (val & ADT7462_DIODE3_CONF)) || \ + (x == 1 && (val & ADT7462_DIODE1_CONF))) +#define ADT7462_PIN1_PIN19_V (!(val & ADT7462_DIODE3_CONF)) +#define ADT7462_PIN1_PIN20_V ADT7462_PIN1_PIN19V +#define ADT7462_PIN1_PIN15_V (!(val & ADT7462_DIODE1_CONF)) +#define ADT7462_PIN1_PIN16_V ADT7462_PIN1_PIN15V + +/* 0x11: Pin configuration register 2 */ +#define ADT7462_PIN23_CONF 0x03 /* 00 = Vcc, 2.5v, 1.8v, 11 = 1.5v */ +#define ADT7462_PIN22_CONF 0x04 /* 0 = 12v3, 1 = Tach8 */ +#define ADT7462_PIN21_CONF 0x08 /* 0 = 5v, 1 = Tach7 */ +#define ADT7462_PIN19_CONF 0x10 /* 0 = 1.25v, 1 = 0.9v */ +#define ADT7462_PIN15_CONF 0x20 /* 0 = 2.5v, 1 = 1.8v */ +#define ADT7462_PIN13_CONF 0x40 /* 0 = 3.3v, 1 = PWM4 */ +#define ADT7462_PIN8_CONF 0x80 /* 0 = 12v2, 1 = Tach6 */ + +#define ADT7462_PIN2_P23_VCCP(val) ((val & ADT7462_PIN23_CONF) == 0x00) +#define ADT7462_PIN2_P23_25V(val) ((val & ADT7462_PIN23_CONF) == 0x01) +#define ADT7462_PIN2_P23_18V(val) ((val & ADT7462_PIN23_CONF) == 0x10) +#define ADT7462_PIN2_P23_15V(val) ((val & ADT7462_PIN23_CONF) == 0x11) +#define ADT7462_PIN2_P22_12V3(val) ((val & ADT7462_PIN22_CONF) == 0x00) +#define ADT7462_PIN2_P22_TACH8(val) ((val & ADT7462_PIN22_CONF) == 0x04) +#define ADT7462_PIN2_P21_5V(val) ((val & ADT7462_PIN21_CONF) == 0x00) +#define ADT7462_PIN2_P21_TACH7(val) ((val & ADT7462_PIN21_CONF) == 0x08) +#define ADT7462_PIN2_P19_125V(val) ((val & ADT7462_PIN19_CONF) == 0x00) +#define ADT7462_PIN2_P19_09V(val) ((val & ADT7462_PIN19_CONF) == 0x10) +#define ADT7462_PIN2_P15_25V(val) ((val & ADT7462_PIN15_CONF) == 0x00) +#define ADT7462_PIN2_P15_18V(val) ((val & ADT7462_PIN15_CONF) == 0x20) +#define ADT7462_PIN2_P13_33V(val) ((val & ADT7462_PIN13_CONF) == 0x00) +#define ADT7462_PIN2_P13_PWM4(val) ((val & ADT7462_PIN13_CONF) == 0x40) +#define ADT7462_PIN2_P8_12V2(val) ((val & ADT7462_PIN8_CONF) == 0x00) +#define ADT7462_PIN2_P8_TACH6(val) ((val & ADT7462_PIN8_CONF) == 0x80) +#define ADT7462_PIN2_TACH(val, x) (x < 5 || \ + (x == 5 && ADT7462_PIN2_P8_TACH6(val)) || \ + (x == 6 && ADT7462_PIN2_P21_TACH7(val)) || \ + (x == 7 && ADT7462_PIN2_P22_TACH8(val))) + +/* 0x12: Pin configuration register 3 */ +#define ADT7462_PIN27_CONF 0x02 /* 0 = fan2max, 1 = chassis intrus */ +#define ADT7462_PIN26_CONF 0x0c /* 00 = Vbatt, 1.2v2, 10/11=vr_hot2 */ +#define ADT7462_PIN25_CONF 0x30 /* 00 = 3.3v, 1.2v1, 10/11=vr_hot1 */ +#define ADT7462_PIN24_CONF 0xc0 /* 00 = Vccp, 2.5v, 1.8v, 11 = 1.5v */ + +/* 0x13: Pin configuration register 4 */ +#define ADT7462_PIN32_CONF 0x04 /* 0 = GPIO6, 1 = PWM2 */ +#define ADT7462_PIN31_CONF 0x08 /* 0 = GPIO5, 1 = PWM1 */ +#define ADT7462_PIN29_CONF 0x30 /* 00 = GPIO8, 1.5v, 10/11 = therm2 */ +#define ADT7462_PIN28_CONF 0xc0 /* 00 = GPIO7, 1.5v, 10/11 = therm1 */ + +/* 0x14: Easy configuration options */ +#define ADT7462_EASY1 0x01 /* Enable easy option 1 */ +#define ADT7462_EASY2 0x02 /* Enable easy option 2 */ +#define ADT7462_EASY3 0x04 /* Enable easy option 3 */ +#define ADT7462_EASY4 0x08 /* Enable easy option 4 */ + +/* 0x16: EDO/Single channel enable */ +#define ADT7462_EDO_EN1 0x01 /* Enable EDO on GPIO5 */ +#define ADT7462_EDO_EN2 0x02 /* Enable EDO on GPIO6 */ +#define ADT7462_1CHAN_MODE 0x04 /* Select single-channel mode */ +#define ADT7462_CHAN_SEL 0xf8 /* Select channel mask */ + +/* 0x18: Voltage attenuator configuration 1 */ +#define ADT7462_ATT_PIN7 0x02 /* Enable attenuator on pin7 */ +#define ADT7462_ATT_PIN8 0x04 /* Enable attenuator on pin8 */ +#define ADT7462_ATT_PIN13 0x08 /* Enable attenuator on pin13 */ +#define ADT7462_ATT_PIN15 0x10 /* Enable attenuator on pin15 */ +#define ADT7462_ATT_PIN19 0x20 /* Enable attenuator on pin19 */ +#define ADT7462_ATT_PIN21 0x40 /* Enable attenuator on pin21 */ +#define ADT7462_ATT_PIN22 0x80 /* Enable attenuator on pin22 */ + +/* 0x19: Voltage attenuator configuration 2 */ +#define ADT7462_ATT_PIN23 0x01 /* Enable attenuator on pin23 */ +#define ADT7462_ATT_PIN24 0x02 /* Enable attenuator on pin24 */ +#define ADT7462_ATT_PIN25 0x04 /* Enable attenuator on pin25 */ +#define ADT7462_ATT_PIN28 0x10 /* Enable attenuator on pin28 */ +#define ADT7462_ATT_PIN29 0x20 /* Enable attenuator on pin29 */ + +/* 0x1a: Enhanced acoustics register 1 */ +#define ADT7462_AC_EN1 0x01 /* En. enhanced acoustics for PWM1 */ +#define ADT7462_AC_EN2 0x02 /* En. enhanced acoustics for PWM2 */ +#define ADT7462_AC_RATE1 0x1c /* Ramp rate for PWM1 */ + +/* 0x1b: Enhanced acoustics register 2 */ +#define ADT7462_AC_EN3 0x01 /* En. enhanced acoustics for PWM3 */ +#define ADT7462_AC_EN4 0x02 /* En. enhanced acoustics for PWM4 */ +#define ADT7462_AC_RATE3 0x1c /* Ramp rate for PWM3 */ +#define ADT7462_AC_RATE4 0xe0 /* Ramp rate for PWM4 */ + +/* 0x1c: Fan freewheeling test */ +#define ADT7462_FAN_FREE1_END 0x01 /* Freewheeling for fan1 complete */ +#define ADT7462_FAN_FREE2_END 0x02 /* Freewheeling for fan2 complete */ +#define ADT7462_FAN_FREE3_END 0x04 /* Freewheeling for fan3 complete */ +#define ADT7462_FAN_FREE4_END 0x08 /* Freewheeling for fan4 complete */ +#define ADT7462_FAN_FREE5_END 0x10 /* Freewheeling for fan5 complete */ +#define ADT7462_FAN_FREE6_END 0x20 /* Freewheeling for fan6 complete */ +#define ADT7462_FAN_FREE7_END 0x40 /* Freewheeling for fan7 complete */ +#define ADT7462_FAN_FREE8_END 0x80 /* Freewheeling for fan8 complete */ + +/* 0x1d: Fans present */ +#define ADT7462_FAN1_PRESENT 0x01 /* Fan1 present */ +#define ADT7462_FAN2_PRESENT 0x02 /* Fan2 present */ +#define ADT7462_FAN3_PRESENT 0x04 /* Fan3 present */ +#define ADT7462_FAN4_PRESENT 0x08 /* Fan4 present */ +#define ADT7462_FAN5_PRESENT 0x10 /* Fan5 present */ +#define ADT7462_FAN6_PRESENT 0x20 /* Fan6 present */ +#define ADT7462_FAN7_PRESENT 0x40 /* Fan7 present */ +#define ADT7462_FAN8_PRESENT 0x80 /* Fan8 present */ + +/* 0x1e: Fan freewheeling test */ +#define ADT7462_FAN_FREE1 0x01 /* Start freewheeling test for fan1 */ +#define ADT7462_FAN_FREE2 0x02 /* Start freewheeling test for fan2 */ +#define ADT7462_FAN_FREE3 0x04 /* Start freewheeling test for fan3 */ +#define ADT7462_FAN_FREE4 0x08 /* Start freewheeling test for fan4 */ +#define ADT7462_FAN_FREE5 0x10 /* Start freewheeling test for fan5 */ +#define ADT7462_FAN_FREE6 0x20 /* Start freewheeling test for fan6 */ +#define ADT7462_FAN_FREE7 0x40 /* Start freewheeling test for fan7 */ +#define ADT7462_FAN_FREE8 0x80 /* Start freewheeling test for fan8 */ + +/* 0x21: PWM1 configuration register */ +#define ADT7462_PWM1_SPIN_TIMEO 0x07 /* Fan startup and test timeout */ +#define ADT7462_PWM1_SLOW 0x08 /* Slow en. acoustics mode start*/ +#define ADT7462_PWM1_INV 0x10 /* PWM output 0 = low, 1 = high */ +#define ADT7462_PWM1_BHVR 0xe0 /* PWM control temp. mask */ + +/* 0x22: PWM2 configuration register */ +#define ADT7462_PWM2_SPIN_TIMEO 0x07 /* Fan startup and test timeout */ +#define ADT7462_PWM2_SLOW 0x08 /* Slow en. acoustics mode start*/ +#define ADT7462_PWM2_INV 0x10 /* PWM output 0 = low, 1 = high */ +#define ADT7462_PWM2_BHVR 0xe0 /* PWM control temp. mask */ + +/* 0x23: PWM3 configuration register */ +#define ADT7462_PWM3_SPIN_TIMEO 0x07 /* Fan startup and test timeout */ +#define ADT7462_PWM3_SLOW 0x08 /* Slow en. acoustics mode start*/ +#define ADT7462_PWM3_INV 0x10 /* PWM output 0 = low, 1 = high */ +#define ADT7462_PWM3_BHVR 0xe0 /* PWM control temp. mask */ + +/* 0x24: PWM1 configuration register */ +#define ADT7462_PWM4_SPIN_TIMEO 0x07 /* Fan startup and test timeout */ +#define ADT7462_PWM4_SLOW 0x08 /* Slow en. acoustics mode start*/ +#define ADT7462_PWM4_INV 0x10 /* PWM output 0 = low, 1 = high */ +#define ADT7462_PWM4_BHVR 0xe0 /* PWM control temp. mask */ + +/* 0x25: PWM1, PWM2 frequency */ +#define ADT7462_PWM1_TMIN 0x01 /* PWM1 is off or min at Tmin */ +#define ADT7462_PWM2_TMIN 0x02 /* PWM2 is off or min at Tmin */ +#define ADT7462_PWM1_LFREQ 0x1c /* PWM1 low frequency mask */ +#define ADT7462_PWM2_LFREQ 0xe0 /* PWM2 low frequency mask */ + +/* 0x26: PWM3, PWM4 frequency */ +#define ADT7462_PWM3_TMIN 0x01 /* PWM3 is off or min at Tmin */ +#define ADT7462_PWM4_TMIN 0x02 /* PWM4 is off or min at Tmin */ +#define ADT7462_PWM3_LFREQ 0x1c /* PWM3 low frequency mask */ +#define ADT7462_PWM4_LFREQ 0xe0 /* PWM4 low frequency mask */ + +/* 0x30: Thermal mask register 1 */ +#define ADT7462_MASK_LOCAL 0x02 /* Mask local temp. out of limit */ +#define ADT7462_MASK_REMOTE1 0x04 /* Mask remote1 temp. out of limit */ +#define ADT7462_MASK_REMOTE2 0x08 /* Mask remote2 temp. out of limit */ +#define ADT7462_MASK_REMOTE3 0x10 /* Mask remote3 temp. out of limit */ +#define ADT7462_MASK_DIODE1 0x20 /* Mask remote1 temp. error */ +#define ADT7462_MASK_DIODE2 0x40 /* Mask remote2 temp. error */ +#define ADT7462_MASK_DIODE3 0x80 /* Mask remote3 temp. error */ + +/* 0x31: Thermal mask register 2 */ +#define ADT7462_MASK_TH1_PCT 0x01 /* Mask therm1 percent alert */ +#define ADT7462_MASK_TH1_ASRT 0x02 /* Mask therm1 assert */ +#define ADT7462_MASK_TH1_STAT 0x04 /* Mask therm1 state alert */ +#define ADT7462_MASK_TH2_PCT 0x08 /* Mask therm2 percent alert */ +#define ADT7462_MASK_TH2_ASRT 0x10 /* Mask therm2 assert */ +#define ADT7462_MASK_TH2_STAT 0x20 /* Mask therm2 state alert */ +#define ADT7462_MASK_VRD1 0x40 /* Mask VRD1 assert */ +#define ADT7462_MASK_VRD2 0x80 /* Mask VRD2 assert */ + +/* 0x32: Voltage mask register 1 */ +#define ADT7462_MASK_12V1 0x01 /* Mask 12V1 alert */ +#define ADT7462_MASK_12V2 0x02 /* Mask 12V2 alert */ +#define ADT7462_MASK_12V3 0x04 /* Mask 12V3 alert */ +#define ADT7462_MASK_33V 0x08 /* Mask 3.3V alert */ +#define ADT7462_MASK_PIN15V 0x10 /* Mask pin15v alert */ +#define ADT7462_MASK_PIN19V 0x20 /* Mask pin19v alert */ +#define ADT7462_MASK_5V 0x40 /* Mask 5V alert */ +#define ADT7462_MASK_PIN23V 0x80 /* Mask pin23v alert */ + +/* 0x33: Voltage mask register 2 */ +#define ADT7462_MASK_PIN24V 0x08 /* Mask pin24v alert */ +#define ADT7462_MASK_PIN25V 0x10 /* Mask pin25v alert */ +#define ADT7462_MASK_PIN26V 0x20 /* Mask pin26v alert */ +#define ADT7462_MASK_15V2 0x40 /* Mask 1.5V2 alert */ +#define ADT7462_MASK_15V1 0x80 /* Mask 1.5V1 alert */ + +/* 0x34: Fan mask register */ +#define ADT7462_MASK_FAN1_FAULT 0x01 /* Mask fan1 fault */ +#define ADT7462_MASK_FAN2_FAULT 0x02 /* Mask fan2 fault */ +#define ADT7462_MASK_FAN3_FAULT 0x04 /* Mask fan3 fault */ +#define ADT7462_MASK_FAN4_FAULT 0x08 /* Mask fan4 fault */ +#define ADT7462_MASK_FAN5_FAULT 0x10 /* Mask fan5 fault */ +#define ADT7462_MASK_FAN6_FAULT 0x20 /* Mask fan6 fault */ +#define ADT7462_MASK_FAN7_FAULT 0x40 /* Mask fan7 fault */ +#define ADT7462_MASK_FAN8_FAULT 0x80 /* Mask fan8 fault */ + +/* 0x35: Digital mask register */ +#define ADT7462_MASK_FAN2MAX 0x08 /* Mask fan2max alert */ +#define ADT7462_MASK_SCSI1 0x10 /* Mask SCSI1 alert */ +#define ADT7462_MASK_SCSI2 0x20 /* Mask SCSI2 alert */ +#define ADT7462_MASK_VID 0x40 /* Mask VID comparison alert */ +#define ADT7462_MASK_CHASSIS 0x80 /* Mask chassis intrusion alert */ + +/* 0x36: GPIO mask register */ +#define ADT7462_MASK_GPIO1_FLT 0x01 /* Mask GPIO1 fault */ +#define ADT7462_MASK_GPIO2_FLT 0x02 /* Mask GPIO2 fault */ +#define ADT7462_MASK_GPIO3_FLT 0x04 /* Mask GPIO3 fault */ +#define ADT7462_MASK_GPIO4_FLT 0x08 /* Mask GPIO4 fault */ +#define ADT7462_MASK_GPIO5_FLT 0x10 /* Mask GPIO5 fault */ +#define ADT7462_MASK_GPIO6_FLT 0x20 /* Mask GPIO6 fault */ +#define ADT7462_MASK_GPIO7_FLT 0x40 /* Mask GPIO7 fault */ +#define ADT7462_MASK_GPIO8_FLT 0x80 /* Mask GPIO8 fault */ + +/* 0x36: EDO mask register 1 */ +#define ADT7462_EDO1_GPIO1 0x01 /* Mask GPIO1 for EDO1 */ +#define ADT7462_EDO1_GPIO2 0x02 /* Mask GPIO2 for EDO1 */ +#define ADT7462_EDO1_GPIO3 0x04 /* Mask GPIO3 for EDO1 */ +#define ADT7462_EDO1_GPIO4 0x08 /* Mask GPIO4 for EDO1 */ +#define ADT7462_EDO1_FAN 0x20 /* Mask fan fault for EDO1 */ +#define ADT7462_EDO1_TEMP 0x40 /* Mask therm for EDO1 */ +#define ADT7462_EDO2_VOLT 0x80 /* Mask volt limit for EDO1 */ + +/* 0x36: EDO mask register 2 */ +#define ADT7462_EDO2_GPIO1 0x01 /* Mask GPIO1 for EDO2 */ +#define ADT7462_EDO2_GPIO2 0x02 /* Mask GPIO2 for EDO2 */ +#define ADT7462_EDO2_GPIO3 0x04 /* Mask GPIO3 for EDO2 */ +#define ADT7462_EDO2_GPIO4 0x08 /* Mask GPIO4 for EDO2 */ +#define ADT7462_EDO2_FAN 0x20 /* Mask fan fault for EDO2 */ +#define ADT7462_EDO2_TEMP 0x40 /* Mask therm for EDO2 */ +#define ADT7462_EDO2_VOLT 0x80 /* Mask volt limit for EDO2 */ + +/* 0x3d: Device ID register */ +#define ADT7462_DEV_ID_VAL 0x62 /* ADT7462 device ID */ + +/* 0x3e: Company ID register */ +#define ADT7462_COMP_ID_VAL 0x41 /* ADT7462 company ID */ + +/* 0x3f: Revision ID register */ +#define ADT7462_REV_ID_VAL 0x04 /* ADT7462 revision ID */ + +/* 0x44 - 0x53: Temperature limit registers: -64'C + value */ +#define ADT7462_TEMP_BASE 209150000 + +/* 0x54 - Local/remote 1 temperature hysteresis */ +#define ADT7462_REMOTE1_TH_HYST 0x0f /* Remote 1 therm hyst. (0 - 15) */ +#define ADT7462_LOCAL_TH_HYST 0xf0 /* Local therm hyst. (0 - 15) */ + +/* 0x55 - Remote 2/remote 3 temperature hysteresis */ +#define ADT7462_REMOTE3_TH_HYST 0x0f /* Remote 3 therm hyst. (0 - 15) */ +#define ADT7462_REMOTE2_TH_HYST 0xf0 /* Remote 2 therm hyst. (0 - 15) */ + +/* 0x56 - 0x59: Offset registers: resolution = 0.5'C */ + +/* 0x5a - 0x5b: Operation point registers */ + +/* 0x5c - 0x5f: Timing registers */ + +/* 0x60 - 0x63: Trange/Hysteresis registers */ +#define ADT7462_LOCAL_FN_HYST 0x0f /* Local auto fan loop hyst. (0-15) */ +#define ADT7462_LOCAL_FN_RNGE 0xf0 /* Local Trange value (2 - 80) */ +#define ADT7462_REMOTE1_FN_HYST ADT7462_LOCAL_FN_HYST +#define ADT7462_REMOTE1_FN_RNGE ADT7462_LOCAL_FN_RNGE +#define ADT7462_REMOTE2_FN_HYST ADT7462_LOCAL_FN_HYST +#define ADT7462_REMOTE2_FN_RNGE ADT7462_LOCAL_FN_RNGE +#define ADT7462_REMOTE3_FN_HYST ADT7462_LOCAL_FN_HYST +#define ADT7462_REMOTE3_FN_RNGE ADT7462_LOCAL_FN_RNGE + +/* 0x64: Operating point hysteresis */ +#define ADT7462_OPT_HYST 0xf0 /* Tmin loop hyst. (0 - 15) */ + +/* 0x68 - 0x77: Voltage limit registers */ + +/* 0x78 - 0x7f: Tach limit registers */ +#define ADT7462_TACH_LIMIT(x) (0x78 + x) + +/* 0x80 - 0x81: Therm timer limit registers */ + +/* 0x88 - 0x8f: Temperature value registers */ +#define ADT7462_TEMP_VAL_LSB(x) (0x88 + 2 * x) +#define ADT7462_TEMP_VAL_MSB(x) (0x89 + 2 * x) + +/* 0x90 - 0x96: Voltage value registers */ + +/* 0x97: VID value register */ + +/* 0x98 - 0x9f, 0xa2 - 0xa9: Tach value registers */ +#define ADT7462_TACH_VAL_LSB(x) \ + (x < 4 ? 0x98 + 2 * x : 0x9a + 2 * x) +#define ADT7462_TACH_VAL_MSB(x) \ + (x < 4 ? 0x99 + 2 * x : 0x9b + 2 * x) +#define ADT7462_TACH_PERIOD (90000 * 60) + +/* 0xaa - 0xad: PWM current duty cycle registers */ + +/* 0xae - 0xaf: Therm time value registers */ + +/* 0xb8, 0xc0: Host/BMC thermal status register 1 */ +#define ADT7462_LOCAL_TRIP 0x02 +#define ADT7462_REMOTE1_TRIP 0x04 +#define ADT7462_REMOTE2_TRIP 0x08 +#define ADT7462_REMOTE3_TRIP 0x10 +#define ADT7462_DIODE1_ERR 0x20 +#define ADT7462_DIODE2_ERR 0x40 +#define ADT7462_DIODE3_ERR 0x80 + +/* 0xb9, 0xc1: Host/BMC thermal status register 2 */ +#define ADT7462_THERM1_PCT 0x01 +#define ADT7462_THERM1_ASSERT 0x02 +#define ADT7462_THERM1_STATE 0x04 +#define ADT7462_THERM2_PCT 0x08 +#define ADT7462_THERM2_ASSERT 0x10 +#define ADT7462_THERM2_STATE 0x20 +#define ADT7462_VRD1_ASSERT 0x40 +#define ADT7462_VRD2_ASSERT 0x80 + +/* 0xba: Host thermal status register 3 */ +#define ADT7462_THERM1_L_EXCD 0x01 +#define ADT7462_THERM1_R1_EXCD 0x02 +#define ADT7462_THERM1_R2_EXCD 0x04 +#define ADT7462_THERM1_R3_EXCD 0x08 +#define ADT7462_THERM2_L_EXCD 0x10 +#define ADT7462_THERM2_R1_EXCD 0x20 +#define ADT7462_THERM2_R2_EXCD 0x40 +#define ADT7462_THERM2_R3_EXCD 0x80 + +/* 0xbb, 0xc3: Host/BMC voltage status register 1 */ +#define ADT7462_12V1_TRIP 0x01 +#define ADT7462_12V2_TRIP 0x02 +#define ADT7462_12V3_TRIP 0x04 +#define ADT7462_33V_TRIP 0x08 +#define ADT7462_PIN15V_TRIP 0x10 +#define ADT7462_PIN19V_TRIP 0x20 +#define ADT7462_5V_TRIP 0x40 +#define ADT7462_PIN23V_TRIP 0x80 + +/* 0xbc, 0xc4: Host/BMC voltage status register 2 */ +#define ADT7462_PIN24V_TRIP 0x04 +#define ADT7462_PIN25V_TRIP 0x10 +#define ADT7462_PIN26V_TRIP 0x20 +#define ADT7462_15V2_TRIP 0x40 +#define ADT7462_15V1_TRIP 0x80 + +/* 0xbd, 0xc5: Host/BMC fan status register */ +#define ADT7462_FAN1_FAULT 0x01 +#define ADT7462_FAN2_FAULT 0x02 +#define ADT7462_FAN3_FAULT 0x04 +#define ADT7462_FAN4_FAULT 0x08 +#define ADT7462_FAN5_FAULT 0x10 +#define ADT7462_FAN6_FAULT 0x20 +#define ADT7462_FAN7_FAULT 0x40 +#define ADT7462_FAN8_FAULT 0x80 + +/* 0xbe, 0xc6: Host/BMC digital status register */ +#define ADT7462_FAN2MAX_ASSERT 0x08 +#define ADT7462_SCSI1_ASSERT 0x10 +#define ADT7462_SCSI2_ASSERT 0x20 +#define ADT7462_VID_COMP_FLT 0x40 +#define ADT7462_CHASSIS_ASSERT 0x80 + +/* 0xbe, 0xc6: Host/BMC GPIO status register */ +#define ADT7462_GPIO1_ASSERT 0x01 +#define ADT7462_GPIO2_ASSERT 0x02 +#define ADT7462_GPIO3_ASSERT 0x04 +#define ADT7462_GPIO4_ASSERT 0x08 +#define ADT7462_GPIO5_ASSERT 0x10 +#define ADT7462_GPIO6_ASSERT 0x20 +#define ADT7462_GPIO7_ASSERT 0x40 +#define ADT7462_GPIO8_ASSERT 0x80 + +#endif /* _DEV_I2C_ADT7462REG_H_ */ --- src/sys/dev/i2c/files.i2c.dist 2026-02-10 22:19:26.249986431 +0100 +++ src/sys/dev/i2c/files.i2c 2026-02-10 22:18:55.239986449 +0100 @@ -464,3 +464,18 @@ # NXP SC16IS7xx UART bridge attach sc16is7xx at iic with sc16is7xxi2c file dev/i2c/sc16is7xxi2c.c sc16is7xxi2c + +# Texas Instruments LM95221 temperature sensor +device lm95221ts: sysmon_envsys +attach lm95221ts at iic +file dev/i2c/lm95221.c lm95221ts + +# NXP LM75A temperature sensor +device lm75a: sysmon_envsys +attach lm75a at iic +file dev/i2c/nxp_lm75a.c lm75a + +# ADM1026 system monitor +device adt7462sm: sysmon_envsys +attach adt7462sm at iic +file dev/i2c/adt7462.c adt7462sm --- src/sys/dev/i2c/lm95221.c.dist 2026-02-10 22:20:01.329986410 +0100 +++ src/sys/dev/i2c/lm95221.c 2026-02-03 09:37:15.579987938 +0100 @@ -0,0 +1,280 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define LM95221_LOCAL 0 +#define LM95221_REMOTE1 1 +#define LM95221_REMOTE2 2 +#define LM95221_MISSING 0x01 +#define LM95221_SIGNED 0x02 + +struct lm95221ts_softc { + device_t sc_dev; + i2c_tag_t sc_tag; + int sc_address; + + struct sysmon_envsys *sc_sme; + envsys_data_t sc_sensor[LM95221_NSENSORS]; + int sc_map[LM95221_NSENSORS]; /* Sysmon numbers to sensors */ + uint8_t sc_reg_m[LM95221_NSENSORS]; /* MSB register */ + uint8_t sc_reg_l[LM95221_NSENSORS]; /* LSB register */ + uint8_t sc_flags[LM95221_NSENSORS]; /* Flags for remotes */ +}; + +static int lm95221ts_match(device_t, cfdata_t, void *); +static void lm95221ts_attach(device_t, device_t, void *); +static int lm95221ts_detach(device_t, int); +void lm95221ts_refresh(struct sysmon_envsys *, envsys_data_t *); +static int lm95221ts_read_temp(struct lm95221ts_softc *, uint8_t, uint8_t, + uint8_t, uint32_t *); + +CFATTACH_DECL_NEW(lm95221ts, sizeof(struct lm95221ts_softc), + lm95221ts_match, lm95221ts_attach, lm95221ts_detach, NULL); + + +static const struct device_compatible_entry compat_data[] = { + { .compat = "i2c-lm95221" }, + DEVICE_COMPAT_EOL +}; + +static int +lm95221ts_match(device_t parent, cfdata_t cf, void *aux) +{ + struct i2c_attach_args *ia = aux; + int match_result; + + if (iic_use_direct_match(ia, cf, compat_data, &match_result)) + return match_result; + + /* + * Indirect config - assume config is correct! + */ + if (ia->ia_addr == LM95221_ADDR) + return I2C_MATCH_ADDRESS_ONLY; + + return 0; +} + +static void +lm95221ts_attach(device_t parent, device_t self, void *aux) +{ + struct lm95221ts_softc *sc = device_private(self); + struct i2c_attach_args *ia = aux; + uint8_t reg, val; + int i, map; + + sc->sc_tag = ia->ia_tag; + sc->sc_address = ia->ia_addr; + sc->sc_dev = self; + + aprint_normal(": LM95221 temperature sensor\n"); + + /* Read status, start conversion */ + for (i = 0; i < LM95221_NSENSORS; i++) + sc->sc_flags[i] = 0; + + if (iic_acquire_bus(sc->sc_tag, 0)) { + aprint_error_dev(sc->sc_dev, + "unable to acquire i2c bus\n"); + return; + } + + reg = LM95221_STATUS; + if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_address, ®, 1, &val, 1, 0)) { + aprint_error_dev(sc->sc_dev, + "unable to read status register\n"); + return; + } + if (val & LM95221_STAT_NO_REMOTE1) + sc->sc_flags[LM95221_REMOTE1] |= LM95221_MISSING; + if (val & LM95221_STAT_NO_REMOTE2) + sc->sc_flags[LM95221_REMOTE2] |= LM95221_MISSING; + + reg = LM95221_CONFIG; + if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_address, ®, 1, &val, 1, 0)) { + iic_release_bus(sc->sc_tag, 0); + aprint_error_dev(sc->sc_dev, + "unable to read config register\n"); + return; + } + if (val & LM95221_CONF_REMOTE1) + sc->sc_flags[LM95221_REMOTE1] |= LM95221_SIGNED; + if (val & LM95221_CONF_REMOTE2) + sc->sc_flags[LM95221_REMOTE2] |= LM95221_SIGNED; + + if (val & LM95221_CONF_STANDBY) { + val &= ~(LM95221_CONF_STANDBY); + if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, + sc->sc_address, ®, 1, &val, 1, 0)) { + aprint_error_dev(sc->sc_dev, + "unable to write config register\n"); + iic_release_bus(sc->sc_tag, 0); + return; + } + } + + iic_release_bus(sc->sc_tag, 0); + + sc->sc_sme = sysmon_envsys_create(); + sc->sc_sme->sme_name = device_xname(self); + sc->sc_sme->sme_cookie = sc; + sc->sc_sme->sme_refresh = lm95221ts_refresh; + + strlcpy(sc->sc_sensor[LM95221_LOCAL].desc, "local", + sizeof(sc->sc_sensor[LM95221_LOCAL].desc)); + sc->sc_reg_m[LM95221_LOCAL] = LM95221_LOCAL_MSB; + sc->sc_reg_l[LM95221_LOCAL] = LM95221_LOCAL_LSB; + + strlcpy(sc->sc_sensor[LM95221_REMOTE1].desc, "remote 1", + sizeof(sc->sc_sensor[LM95221_REMOTE1].desc)); + sc->sc_reg_m[LM95221_REMOTE1] = LM95221_REMOTE1_MSB; + sc->sc_reg_l[LM95221_REMOTE1] = LM95221_REMOTE1_LSB; + + strlcpy(sc->sc_sensor[LM95221_REMOTE2].desc, "remote 2", + sizeof(sc->sc_sensor[LM95221_REMOTE2].desc)); + sc->sc_reg_m[LM95221_REMOTE2] = LM95221_REMOTE2_MSB; + sc->sc_reg_l[LM95221_REMOTE2] = LM95221_REMOTE2_LSB; + + for (i = 0; i < LM95221_NSENSORS; i++) { + if (i == LM95221_REMOTE1 && + (sc->sc_flags[LM95221_REMOTE1] & LM95221_MISSING)) + continue; + if (i == LM95221_REMOTE2 && + (sc->sc_flags[LM95221_REMOTE2] & LM95221_MISSING)) + continue; + + sc->sc_sensor[i].units = ENVSYS_STEMP; + sc->sc_sensor[i].state = ENVSYS_SINVALID; + sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY; + + if (sysmon_envsys_sensor_attach(sc->sc_sme, + &sc->sc_sensor[i])) { + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; + aprint_error_dev(sc->sc_dev, + "unable to attach sensor %d to sysmon\n", i); + return; + } + map = sc->sc_sensor[i].sensor; + sc->sc_map[map] = i; + } + + if (sysmon_envsys_register(sc->sc_sme)) { + aprint_error_dev(self, "unable to register with sysmon\n"); + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; + return; + } +} + +static int +lm95221ts_detach(device_t self, int flags) +{ + struct lm95221ts_softc *sc = device_private(self); + + if (sc->sc_sme != NULL) { + sysmon_envsys_unregister(sc->sc_sme); + sc->sc_sme = NULL; + } + + return 0; +} + +void +lm95221ts_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) +{ + struct lm95221ts_softc *sc = sme->sme_cookie; + uint32_t val; + uint8_t reg_m, reg_l, flags; + int map; + + map = sc->sc_map[edata->sensor]; + reg_m = sc->sc_reg_m[map]; + reg_l = sc->sc_reg_l[map]; + flags = sc->sc_flags[map]; + + if (iic_acquire_bus(sc->sc_tag, 0)) + return; + + if (lm95221ts_read_temp(sc, reg_m, reg_l, flags, &val) == 0) { + edata->value_cur = val; + edata->state = ENVSYS_SVALID; + } else { + edata->state = ENVSYS_SINVALID; + } + + iic_release_bus(sc->sc_tag, 0); +} + +static int +lm95221ts_read_temp(struct lm95221ts_softc *sc, uint8_t reg_m, uint8_t reg_l, + uint8_t flags, uint32_t *valp) +{ + uint8_t buf_m, buf_l; + int err; + + err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_address, ®_m, 1, &buf_m, 1, 0); + if (err) + return err; + err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_address, ®_l, 1, &buf_l, 1, 0); + if (err) + return err; + + /* + * Convert the 11 high bits from the 2 bytes to a temperature in uK. + * Maybe sign-extend the MSB, and add in the LSB + */ + if (flags & LM95221_SIGNED) + *valp = buf_m; + else + *valp = (int8_t) buf_m; + *valp = (*valp << 3) + ((buf_l >> 5) & 0x07); + *valp = *valp * 125000 + 273150000; + + return 0; +} --- src/sys/dev/i2c/lm95221reg.h.dist 2026-02-10 22:20:01.329986410 +0100 +++ src/sys/dev/i2c/lm95221reg.h 2026-02-03 07:36:19.779992263 +0100 @@ -0,0 +1,102 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2026 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_I2C_LM95221REG_H_ +#define _DEV_I2C_LM95221REG_H_ + +/* LM95221 temperature sensor register definitions */ + +#define LM95221_ADDR 0x2b + +/* + * Temperature on the LM95221 is represented by a 10-bit (local) and 11-bit + * (remote) 2's complement word or 11-bit (remote) unsigned binary word + * with an LSB of 0.125C. The data is left-justified in 2 * 8-bit registers. + * + * 11-bit 2's complement: + * + * +125C 0111 1101 0000 0000 0x7d00 + * +25C 0001 1001 0000 0000 0x1900 + * +1C 0000 0001 0000 0000 0x0100 + * +0.125C 0000 0000 0010 0000 0x0020 + * 0C 0000 0000 0000 0000 0x0000 + * -0.125C 1111 1111 1110 0000 0xffe0 + * -1C 1111 1111 0000 0000 0xff00 + * -25C 1110 0111 0000 0000 0xe700 + * -55C 1100 1001 0000 0000 0xc900 + * + * 11-bit unsigned binary: + * + * +255.875C 1111 1111 1110 0000 0xffe0 + * +255C 1111 1111 0000 0000 0xff90 + * +201C 1100 1001 0000 0000 0xc900 + * +125C 0111 1101 0000 0000 0x7d00 + * +25C 0001 1001 0000 0000 0x1900 + * +1C 0000 0001 0000 0000 0x0100 + * +0.125C 0000 0000 0010 0000 0x0020 + * 0C 0000 0000 0000 0000 0x0000 + * + * 10-bit 2's complement is similar to 11-bit 2's complement + * (but with 0.25C resolution) + */ + +/* Command bits 0-2 select the register */ +#define LM95221_STATUS 0x02 /* Status */ +#define LM95221_CONFIG 0x03 /* Configuration */ +#define LM95221_1SHOT 0x0f /* One conversion when in standby */ +#define LM95221_LOCAL_MSB 0x10 /* Reading MSB locks LSB 0x20 */ +#define LM95221_REMOTE1_MSB 0x11 /* Reading MSB locks LSB 0x21 */ +#define LM95221_REMOTE2_MSB 0x12 /* Reading MSB locks LSB 0x22 */ +#define LM95221_LOCAL_LSB 0x20 /* Reading LSB unlocks it */ +#define LM95221_REMOTE1_LSB 0x21 /* Reading LSB unlocks it */ +#define LM95221_REMOTE2_LSB 0x22 /* Reading LSB unlocks it */ +#define LM95221_MANUF 0xfe /* Manufacturer ID (0x01) */ +#define LM95221_REVISION 0xff /* Manufacturer ID (0x61) */ + +/* Status register */ +#define LM95221_STAT_NO_REMOTE1 0x01 /* Remote 1 diode missing */ +#define LM95221_STAT_NO_REMOTE2 0x02 /* Remote 1 diode missing */ +#define LM95221_STAT_BUSY 0x80 /* Currently converting */ + +/* Configuration register */ +#define LM95221_CONF_REMOTE1 0x02 /* Remote 1 signed format */ +#define LM95221_CONF_REMOTE2 0x04 /* Remote 2 signed format */ +#define LM95221_CONF_CONVRATE 0x30 /* Conversion rate */ +#define LM95221_CONF_STANDBY 0x40 /* Disable conversions */ +#define LM95221_CONVRATE_CONT 0x00 /* Continuous every 66ms */ +#define LM95221_CONVRATE_200 0x10 /* Every 200ms */ +#define LM95221_CONVRATE_1000 0x20 /* Every 1s */ +#define LM95221_CONVRATE_3000 0x30 /* Every 3s */ + +/* Temperature registers */ +#define LM95221_NSENSORS 3 + +#endif /* _DEV_I2C_LM95221REG_H_ */ --- src/sys/dev/i2c/nxp_lm75a.c.dist 2026-02-10 22:20:01.319986410 +0100 +++ src/sys/dev/i2c/nxp_lm75a.c 2026-02-04 16:41:22.839998521 +0100 @@ -0,0 +1,305 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2026 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct lm75a_softc { + device_t sc_dev; + i2c_tag_t sc_tag; + int sc_address; + + struct sysmon_envsys *sc_sme; + envsys_data_t sc_sensor; + uint32_t sc_otemp, sc_hyst; +}; + +static int lm75a_match(device_t, cfdata_t, void *); +static void lm75a_attach(device_t, device_t, void *); +static int lm75a_detach(device_t, int); +void lm75a_refresh(struct sysmon_envsys *, envsys_data_t *); +void lm75a_get_limits(struct sysmon_envsys *, envsys_data_t *, + sysmon_envsys_lim_t *, uint32_t *); +void lm75a_set_limits(struct sysmon_envsys *, envsys_data_t *, + sysmon_envsys_lim_t *, uint32_t *); +static int lm75a_read_temp(struct lm75a_softc *, uint8_t, uint32_t *); +static int lm75a_write_temp(struct lm75a_softc *, uint8_t, uint32_t); + +CFATTACH_DECL_NEW(lm75a, sizeof(struct lm75a_softc), + lm75a_match, lm75a_attach, lm75a_detach, NULL); + + +static const struct device_compatible_entry compat_data[] = { + { .compat = "i2c-lm75a" }, + DEVICE_COMPAT_EOL +}; + +static int +lm75a_match(device_t parent, cfdata_t cf, void *aux) +{ + struct i2c_attach_args *ia = aux; + int match_result; + + if (iic_use_direct_match(ia, cf, compat_data, &match_result)) + return match_result; + + /* + * Indirect config - assume config is correct! + */ + if ((ia->ia_addr & LM75A_ADDRMASK) == LM75A_ADDR) + return I2C_MATCH_ADDRESS_ONLY; + + return 0; +} + +static void +lm75a_attach(device_t parent, device_t self, void *aux) +{ + struct lm75a_softc *sc = device_private(self); + struct i2c_attach_args *ia = aux; + uint8_t reg, val; + uint32_t tval; + + sc->sc_tag = ia->ia_tag; + sc->sc_address = ia->ia_addr; + sc->sc_dev = self; + + aprint_normal(": LM75A temperature sensor\n"); + + if (iic_acquire_bus(sc->sc_tag, 0)) { + aprint_error_dev(self, + "unable to acquire I2C bus\n"); + return; + } + + /* Read and save overtemp (critical limit) and hysteresis */ + reg = LM75A_OVERTEMP; + if (lm75a_read_temp(sc, reg, &tval)) { + iic_release_bus(sc->sc_tag, 0); + aprint_error_dev(sc->sc_dev, + "unable to read overtemp register\n"); + return; + } + sc->sc_otemp = tval; + reg = LM75A_THYST; + if (lm75a_read_temp(sc, reg, &tval)) { + iic_release_bus(sc->sc_tag, 0); + aprint_error_dev(sc->sc_dev, + "unable to read hysteresis register\n"); + return; + } + sc->sc_hyst = tval; + + /* Read config, start conversion */ + reg = LM75A_CONFIG; + if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_address, ®, 1, &val, 1, 0)) { + iic_release_bus(sc->sc_tag, 0); + aprint_error_dev(sc->sc_dev, + "unable to read config register\n"); + return; + } + + if (val & LM75A_CONF_SHUTDOWN) { + val &= ~(LM75A_CONF_SHUTDOWN); + if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, + sc->sc_address, ®, 1, &val, 1, 0)) { + iic_release_bus(sc->sc_tag, 0); + aprint_error_dev(sc->sc_dev, + "unable to write config register\n"); + return; + } + } + + iic_release_bus(sc->sc_tag, 0); + + sc->sc_sme = sysmon_envsys_create(); + sc->sc_sme->sme_name = device_xname(self); + sc->sc_sme->sme_cookie = sc; + sc->sc_sme->sme_refresh = lm75a_refresh; + sc->sc_sme->sme_get_limits = lm75a_get_limits; + sc->sc_sme->sme_set_limits = lm75a_set_limits; + + strlcpy(sc->sc_sensor.desc, "temperature", sizeof(sc->sc_sensor.desc)); + sc->sc_sensor.units = ENVSYS_STEMP; + sc->sc_sensor.state = ENVSYS_SINVALID; + sc->sc_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY; + + if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; + aprint_error_dev(sc->sc_dev, + "unable to attach sensor to sysmon\n"); + return; + } + + if (sysmon_envsys_register(sc->sc_sme)) { + aprint_error_dev(self, "unable to register with sysmon\n"); + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; + return; + } +} + +static int +lm75a_detach(device_t self, int flags) +{ + struct lm75a_softc *sc = device_private(self); + + if (sc->sc_sme != NULL) { + sysmon_envsys_unregister(sc->sc_sme); + sc->sc_sme = NULL; + } + + return 0; +} + +void +lm75a_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) +{ + struct lm75a_softc *sc = sme->sme_cookie; + uint8_t reg; + uint32_t val; + + if (iic_acquire_bus(sc->sc_tag, 0)) + return; + + reg = LM75A_TEMP; + if (lm75a_read_temp(sc, reg, &val) == 0) { + edata->value_cur = val; + edata->state = ENVSYS_SVALID; + } else { + edata->state = ENVSYS_SINVALID; + } + + iic_release_bus(sc->sc_tag, 0); +} + +void +lm75a_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, + sysmon_envsys_lim_t *limits, uint32_t *props) +{ + struct lm75a_softc *sc = sme->sme_cookie; + uint8_t reg; + uint32_t val; + + *props &= ~(PROP_CRITMAX | PROP_WARNMAX | PROP_WARNMIN); + + if (iic_acquire_bus(sc->sc_tag, 0)) + return; + + reg = LM75A_OVERTEMP; + if (lm75a_read_temp(sc, reg, &val) == 0) { + limits->sel_critmax = val; + *props |= PROP_CRITMAX; + } + + iic_release_bus(sc->sc_tag, 0); +} + +void +lm75a_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata, + sysmon_envsys_lim_t *limits, uint32_t *props) +{ + struct lm75a_softc *sc = sme->sme_cookie; + uint8_t reg; + uint32_t otemp, hyst; + + if (iic_acquire_bus(sc->sc_tag, 0)) + return; + + if (*props & PROP_CRITMAX) { + if (limits == NULL) { /* Restore defaults */ + otemp = sc->sc_otemp; + hyst = sc->sc_hyst; + } else { + otemp = limits->sel_critmax; + /* Make sure that hyst is less than overtemp */ + hyst = otemp - (sc->sc_otemp - sc->sc_hyst); + } + reg = LM75A_OVERTEMP; + if (lm75a_write_temp(sc, reg, otemp) == 0) { + reg = LM75A_THYST; + lm75a_write_temp(sc, reg, hyst); + } + } + + iic_release_bus(sc->sc_tag, 0); +} + +static int +lm75a_read_temp(struct lm75a_softc *sc, uint8_t reg, uint32_t *valp) +{ + uint8_t buf[LM75A_TEMP_LEN]; + int err; + + err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_address, ®, 1, buf, LM75A_TEMP_LEN, 0); + if (err) + return err; + + /* + * Convert the 11 high bits from the 2 bytes to a temperature in uK. + * Sign-extend the MSB and add in the LSB + */ + *valp = (int8_t) buf[0]; + *valp = (*valp << 3) + ((buf[1] >> 5) & 0x07); + *valp = *valp * 125000 + 273150000; + + return 0; +} + +static int +lm75a_write_temp(struct lm75a_softc *sc, uint8_t reg, uint32_t val) +{ + uint8_t buf[LM75A_TEMP_LEN]; + uint32_t temp; + + /* Convert the temperature in uK to 11 high bits in 2 bytes.*/ + temp = (int) (val - 273150000) / 125000 ; + buf[0] = (temp >> 3) & 0xff; + buf[1] = (temp << 5) & 0xe0; + + return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, + sc->sc_address, ®, 1, buf, LM75A_TEMP_LEN, 0); +} + --- src/sys/dev/i2c/nxp_lm75areg.h.dist 2026-02-10 22:20:01.329986410 +0100 +++ src/sys/dev/i2c/nxp_lm75areg.h 2026-02-03 07:33:36.719992360 +0100 @@ -0,0 +1,82 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2026 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_I2C_LM75AREG_H_ +#define _DEV_I2C_LM75AREG_H_ + +/* + * *NXP* LM75A temperature sensor register definitions. + * Pin compatible but not the same as the *TI* LM75A. + */ + +#define LM75A_ADDRMASK 0x3f8 +#define LM75A_ADDR 0x48 + +/* + * Temperature on the NXP LM75A is represented by a 11-bit two's complement + * word with the LSB equal to 0.125C. From the data sheet: + * + * +127C 011 1111 1000 0x3f8 + * +126.875C 011 1111 0111 0x3f7 + * +126.125C 011 1111 0001 0x3f1 + * +125C 011 1110 1000 0x3e8 + * +25C 000 1100 1000 0x9c8 + * +0.125C 000 0000 0001 0x001 + * 0C 000 0000 0000 0x000 + * -0.125C 111 1111 1111 0x7ff + * -25C 111 0011 1000 0x738 + * -54.875C 110 0100 1001 0x649 + * -55C 110 0100 1000 0x648 + * + * The bits are left-shifted to take the high 11 bits of the 2 registers. + */ + +/* Command bits 0-2 select the register */ +#define LM75A_TEMP 0x00 +#define LM75A_CONFIG 0x01 +#define LM75A_THYST 0x02 +#define LM75A_OVERTEMP 0x03 + +/* Configuration register */ +#define LM75A_CONF_SHUTDOWN (1 << 0) /* Shutdown mode */ +#define LM75A_CONF_OS_MODE_INT (1 << 1) /* OS interrupt mode*/ +#define LM75A_CONF_OS_MODE_COMP (1 << 0) /* OS comparator mode*/ +#define LM75A_CONF_OS_POL_HIGH (2 << 1) /* OS polarity high */ +#define LM75A_CONF_OS_POL_LOW (2 << 0) /* OS polarity low */ +#define LM75A_CONF_OS_FLTQ_1 (0 << 3) /* OS fault queue len 1 */ +#define LM75A_CONF_OS_FLTQ_2 (1 << 3) /* OS fault queue len 2 */ +#define LM75A_CONF_OS_FLTQ_4 (2 << 3) /* OS fault queue len 4 */ +#define LM75A_CONF_OS_FLTQ_6 (3 << 3) /* OS fault queue len 6 */ + +#define LM75A_TEMP_LEN 2 /* 2 bytes for temperatures */ +#define LM75A_CONF_LEN 1 /* 1 byte for configuration */ + +#endif /* _DEV_I2C_LM75AREG_H_ */ --- src/sys/arch/sparc64/conf/GENERIC.dist 2026-02-10 22:21:13.119986367 +0100 +++ src/sys/arch/sparc64/conf/GENERIC 2026-02-10 22:21:49.449986346 +0100 @@ -830,9 +830,13 @@ lmtemp* at iic? addr? tda* at iic? addr? # fan control on SB1000/2000 dbcool* at iic? addr? # SB25000 +lm75a* at iic? addr? # U45 +lm95221ts* at iic? addr? # U45 +adt7462sm* at iic? addr? # U45 seeprom* at iic? addr? # i2c-at24c64 fru's pcagpio* at iic? addr? # V210/V240 GPIO's pcf8574io* at iic? addr? # E250 GPIO's +dsrtc* at iic? addr? # RSC clock found on V210 and V240. ### Other pseudo-devices --- src/sys/arch/sparc64/sparc64/autoconf.c.dist 2026-02-06 17:35:30.000000000 +0100 +++ src/sys/arch/sparc64/sparc64/autoconf.c 2026-02-04 20:14:36.879990895 +0100 @@ -1,4 +1,4 @@ -/* $NetBSD: autoconf.c,v 1.247 2026/02/06 16:35:30 jdc Exp $ */ +/* $NetBSD: autoconf.c,v 1.246 2025/10/13 04:06:13 thorpej Exp $ */ /* * Copyright (c) 1996 @@ -48,7 +48,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.247 2026/02/06 16:35:30 jdc Exp $"); +__KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.246 2025/10/13 04:06:13 thorpej Exp $"); #include "opt_ddb.h" #include "opt_kgdb.h" @@ -775,8 +775,42 @@ portid = -1; ma.ma_upaid = portid; - if (prom_getprop(node, "reg", sizeof(*ma.ma_reg), - &ma.ma_nreg, &ma.ma_reg) != 0) +#define NREG32 3 + if (prom_getproplen(node, "reg") == NREG32 * 4) { + /* + * Fix up PROM's where reg is encoded as a 64-bit and + * a 32-bit value (e.g. 00000400 0fc62020 00000010), + * but we want 2 x 64-bit values. + */ + int n; + int32_t *reg32p = NULL; + + if (prom_getprop(node, "reg", sizeof(int32_t), + &n, ®32p) != 0) + continue; + if (n != NREG32) { + free(reg32p, M_DEVBUF); + continue; + } + ma.ma_reg = malloc(sizeof(*ma.ma_reg), M_DEVBUF, + M_NOWAIT); + if (ma.ma_reg == NULL) + continue; + ma.ma_reg->ur_paddr = reg32p[0]; + ma.ma_reg->ur_paddr = ma.ma_reg->ur_paddr << 32; + ma.ma_reg->ur_paddr |= reg32p[1]; + ma.ma_reg->ur_len = reg32p[2]; + ma.ma_nreg = 1; + free(reg32p, M_DEVBUF); +#ifdef DEBUG + if (autoconf_debug & ACDB_PROBE) { + printf(" fixed up 64/32 reg property\n"); + } +#endif + } else + /* reg is encoded as we expect */ + if (prom_getprop(node, "reg", sizeof(*ma.ma_reg), + &ma.ma_nreg, &ma.ma_reg) != 0) continue; #ifdef DEBUG if (autoconf_debug & ACDB_PROBE) { --- src/sys/arch/sparc64/sparc64/ofw_patch.c.dist 2026-02-06 17:35:30.000000000 +0100 +++ src/sys/arch/sparc64/sparc64/ofw_patch.c 2026-02-11 14:08:31.969978237 +0100 @@ -1,4 +1,4 @@ -/* $NetBSD: ofw_patch.c,v 1.10 2026/02/06 16:35:30 jdc Exp $ */ +/* $NetBSD: ofw_patch.c,v 1.9 2025/09/19 13:19:25 thorpej Exp $ */ /*- * Copyright (c) 2020 The NetBSD Foundation, Inc. @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -__KERNEL_RCSID(0, "$NetBSD: ofw_patch.c,v 1.10 2026/02/06 16:35:30 jdc Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ofw_patch.c,v 1.9 2025/09/19 13:19:25 thorpej Exp $"); #include @@ -290,6 +290,24 @@ add_i2c_device(cfg, "temperature-sensor", "i2c-lm75", 0x4e, 0); } +/* + * Add U45 environmental sensors that are not in the OFW tree. + */ +static void +add_env_sensors_u45(device_t busdev) +{ + prop_array_t cfg; + + DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", machine_model)); + cfg = create_i2c_dict(busdev); + + /* LM95221 at 0x2b */ + add_i2c_device(cfg, "temperature-sensor", "i2c-lm95221", 0x2b, 0); + + /* NXP LM75A at 0x4f */ + add_i2c_device(cfg, "temperature-sensor", "i2c-lm75a", 0x4f, 0); +} + /* Sensors and GPIO's for E450 and E250 */ static void add_i2c_props_e450(device_t busdev, uint64_t node) @@ -347,6 +365,36 @@ prop_object_release(cfg); } +/* + * Fix-up U45 incorrect properties in the OFW tree. + */ +static void +fix_properties_u45(device_t busdev) +{ + prop_dictionary_t props = device_properties(busdev); + prop_array_t cfg; + prop_object_iterator_t iter; + prop_object_t dev; + uint8_t addr; + + cfg = prop_dictionary_get(props, "i2c-child-devices"); + if (!cfg) + return; + + iter = prop_array_iterator(cfg); + while ((dev = prop_object_iterator_next(iter)) != NULL) { + if (prop_object_type(dev) != PROP_TYPE_DICTIONARY) + continue; + if (prop_dictionary_get_uint8(dev, "addr", &addr) == 0) + continue; + if (addr != 0x57) + continue; + prop_dictionary_set_data(dev, "compatible", "i2c-at24c32", + strlen("i2c-at24c32") + 1); + } + prop_object_iterator_release(iter); +} + /* Hardware specific i2c bus properties */ void set_i2c_bus_props(device_t busdev, uint64_t busnode) @@ -360,6 +408,11 @@ !strcmp(machine_model, "SUNW,Sun-Fire-V210")) add_env_sensors_v210(busdev); + if (!strcmp(machine_model, "SUNW,A70")) { + add_env_sensors_u45(busdev); + fix_properties_u45(busdev); + } + /* E450 SUNW,envctrl */ if (!strcmp(machine_model, "SUNW,Ultra-4")) add_i2c_props_e450(busdev, busnode); @@ -389,12 +442,24 @@ prop_dictionary_set_uint8(props, "fan_mask", 0x08); } + + if (device_is_a(dev, "dsrtc")) { + prop_dictionary_t props = device_properties(dev); + prop_dictionary_set_uint(props, "start-year", 2000); + } + } + + if (!strcmp(machine_model, "SUNW,A70")) { + if (device_is_a(dev, "adt7462sm")){ + /* U45 has 5 fans */ + prop_dictionary_t props = device_properties(dev); + prop_dictionary_set_uint8(props, "fan_conf", 0x1f); + } } if (!strcmp(machine_model, "SUNW,Ultra-250")) if (device_is_a(dev, "pcf8574io")) add_gpio_props_e250(dev, aux); - } /* Static EDID definitions */ --- src/sys/kern/kern_todr.c.dist 2026-01-23 16:30:48.189998898 +0100 +++ src/sys/kern/kern_todr.c 2026-02-11 14:58:11.409976461 +0100 @@ -78,6 +78,7 @@ #include #include #include +#include #include #include #include @@ -86,11 +87,17 @@ #include /* hmm.. this should probably move to sys */ +struct todr_list { + todr_chip_handle_t todr_handle; + struct todr_list *todr_next; +}; + static int todr_gettime(todr_chip_handle_t, struct timeval *); static int todr_settime(todr_chip_handle_t, struct timeval *); static kmutex_t todr_mutex; -static todr_chip_handle_t todr_handle; +static struct todr_list todr_head = + { .todr_handle = NULL, .todr_next = NULL }; static bool todr_initialized; /* The minimum reasonable RTC date before preposterousness */ @@ -179,6 +186,7 @@ void todr_attach(todr_chip_handle_t todr) { + struct todr_list *todr_newp, *todr_iterp; /* * todr_init() is called very early in main(), but this is @@ -191,13 +199,22 @@ return; } - todr_lock(); - if (todr_handle) { - todr_unlock(); - printf("todr_attach: TOD already configured\n"); - return; + /* Initial entry is static, others allocated. */ + if (todr_head.todr_handle != NULL) { + todr_newp = kmem_alloc(sizeof(struct todr_list), + KM_SLEEP); + todr_newp->todr_next = NULL; + todr_lock(); + todr_newp->todr_handle = todr; + todr_iterp = &todr_head; + while (todr_iterp->todr_next != NULL) + todr_iterp = todr_iterp->todr_next; + todr_iterp->todr_next = todr_newp; + printf("(secondary clock)\n"); + } else { + todr_lock(); + todr_head.todr_handle = todr; } - todr_handle = todr; todr_unlock(); } @@ -246,16 +263,17 @@ /* * Some ports need to be supplied base in order to fabricate a time_t. */ - if (todr_handle) - todr_handle->todr_base_time = base; + if (todr_head.todr_handle != NULL) + todr_head.todr_handle->todr_base_time = base; memset(&tv, 0, sizeof(tv)); - if ((todr_handle == NULL) || - (todr_gettime(todr_handle, &tv) != 0) || + /* Note, that we only read time from the first RTC. */ + if ((todr_head.todr_handle == NULL) || + (todr_gettime(todr_head.todr_handle, &tv) != 0) || (tv.tv_sec < (PREPOSTEROUS_YEARS * SECS_PER_COMMON_YEAR))) { - if (todr_handle != NULL) + if (todr_head.todr_handle != NULL) printf("WARNING: preposterous TOD clock time\n"); else printf("WARNING: no TOD clock present\n"); @@ -322,6 +340,7 @@ todr_save_systime(void) { struct timeval tv; + struct todr_list *todr_iterp; KASSERT(todr_lock_owned()); @@ -338,9 +357,13 @@ if (tv.tv_sec == 0) return; - if (todr_handle) - if (todr_settime(todr_handle, &tv) != 0) - printf("Cannot set TOD clock time\n"); + todr_iterp = &todr_head; + do { + if (todr_iterp->todr_handle != NULL) + if (todr_settime(todr_iterp->todr_handle, &tv) != 0) + printf("Cannot set TOD clock time\n"); + todr_iterp = todr_iterp->todr_next; + } while (todr_iterp != NULL); } /*