Re: [PATCH 07/25] hw/sensor: tmp105: add TMP75, TMP175 and LM75B variants

Cédric Le Goater <[email protected]> Wed, 29 Jul 2026 19:44:17 +0200
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
On 7/29/26 17:12, Emmanuel Blot wrote:
> The TI TMP75/TMP175 and the NXP LM75B share the TMP105 register map and
> control semantics, differing only in a few details. Model them as
> variants of the TMP105, parameterised by a small per-type class
> descriptor covering the fault-queue depths, the writable config bits,
> the converter resolution, the set-point masks and the TMP75's
> alert-clear on thermostat-mode change.
> 
> The variant is class data, not migrated state, so the wire format is
> unchanged and all variants share the existing vmstate.
> 
> While here, align the shared shutdown handling with the hardware:
> entering shutdown now clears the ALERT/OS output in interrupt mode,
> correcting the base TMP105 as well.
> 
> Signed-off-by: Emmanuel Blot <[email protected]>
> ---
>   hw/sensor/tmp105.c         | 171 +++++++++++++++++++++++++++++++++++++--------
>   include/hw/sensor/tmp105.h |   6 +-
>   2 files changed, 147 insertions(+), 30 deletions(-)
> 
> diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c
> index fefa661711..69ade2013d 100644
> --- a/hw/sensor/tmp105.c
> +++ b/hw/sensor/tmp105.c
> @@ -1,5 +1,5 @@
>   /*
> - * Texas Instruments TMP105 temperature sensor.
> + * Texas Instruments TMP105/TMP75/TMP175/LM75B temperature sensor.
>    *
>    * Copyright (C) 2008 Nokia Corporation
>    * Written by Andrzej Zaborowski <[email protected]>
> @@ -16,6 +16,13 @@
>    *
>    * You should have received a copy of the GNU General Public License along
>    * with this program; if not, see <http://www.gnu.org/licenses/>.
> + *
> + * Limitations:
> + * - The SMBus Alert Response Address protocol and the general-call commands
> + *   are not implemented.
> + * - The over-limit comparison uses the full 8.8 fixed-point temperature and a
> + *   ">=" boundary for every variant. The LM75B's 9-bit comparison quantisation
> + *   and strict-exceed boundary are therefore only approximated.
>    */
>   
>   #include "qemu/osdep.h"
> @@ -31,15 +38,29 @@
>   #include "migration/vmstate.h"
>   #include "trace.h"
>   
> -OBJECT_DECLARE_SIMPLE_TYPE(TMP105State, TMP105)
> +OBJECT_DECLARE_TYPE(TMP105State, TMP105Class, TMP105)
>   
>   /**
> - * TMP105State:
> - * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the
> - * temperature. See Table 8 in the data sheet.
> + * TMP105Variant:
> + * Per-device-model parameters. The TMP105, TMP75, TMP175 and LM75B share the
> + * same register map and control semantics; they differ only in a handful of
> + * details captured here.
>    *
> - * @see_also: http://www.ti.com/lit/gpn/tmp105
> + * @faultq: fault-queue length table selected by Config bits
> + * @config_wmask: writable Config bits. The LM75B has no resolution (R1:R0) or
> + * one-shot (OS) bits.
> + * @fixed_res: converter resolution field pinned by the device, or -1 when it is
> + * software-selectable.
> + * @limit_lsb_mask: low-byte mask applied to the T_LOW/T_HIGH limit registers.
>    */
> +typedef struct TMP105Variant {
> +    const uint8_t *faultq;
> +    uint8_t config_wmask;
> +    int8_t fixed_res;
> +    uint8_t limit_lsb_mask;
> +    bool tm_change_clears_alert;
> +} TMP105Variant;

There are class attributes which belong to the TMP105Class below.

Thanks,

C.

>   struct TMP105State {
>       /*< private >*/
>       I2CSlave parent_obj;
> @@ -53,7 +74,7 @@ struct TMP105State {
>       uint8_t config;
>       int16_t temperature;
>       int16_t limit[2];
> -    int faults;
> +    uint8_t faults;
>       uint8_t fault_count;
>       uint8_t alarm;
>       /*
> @@ -65,6 +86,11 @@ struct TMP105State {
>       bool detect_falling;
>   };
>   
> +struct TMP105Class {
> +    I2CSlaveClass parent_class;
> +    const TMP105Variant *var;
> +};
> +
>   FIELD(CONFIG, SHUTDOWN_MODE,        0, 1)
>   FIELD(CONFIG, THERMOSTAT_MODE,      1, 1)
>   FIELD(CONFIG, POLARITY,             2, 1)
> @@ -152,10 +178,11 @@ static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name,
>       tmp105_alarm_update(s, false);
>   }
>   
> -static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
> -
>   static void tmp105_read(TMP105State *s)
>   {
> +    const TMP105Variant *var = TMP105_GET_CLASS(s)->var;
> +    int res;
> +
>       s->len = 0;
>   
>       if (FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE)) {
> @@ -165,9 +192,11 @@ static void tmp105_read(TMP105State *s)
>   
>       switch (s->pointer & 3) {
>       case TMP105_REG_TEMPERATURE:
> +        res = var->fixed_res >= 0 ? var->fixed_res :
> +              FIELD_EX8(s->config, CONFIG, CONVERTER_RESOLUTION);
>           s->buf[s->len++] = (((uint16_t) s->temperature) >> 8);
>           s->buf[s->len++] = (((uint16_t) s->temperature) >> 0) &
> -                (0xf0 << (FIELD_EX8(~s->config, CONFIG, CONVERTER_RESOLUTION)));
> +                (0xf0 << (3 - res));
>           break;
>   
>       case TMP105_REG_CONFIG:
> @@ -190,6 +219,10 @@ static void tmp105_read(TMP105State *s)
>   
>   static void tmp105_write(TMP105State *s)
>   {
> +    const TMP105Variant *var = TMP105_GET_CLASS(s)->var;
> +    uint8_t config, one_shot;
> +    bool waking;
> +
>       trace_tmp105_write(s->parent_obj.address, s->pointer);
>   
>       switch (s->pointer & 3) {
> @@ -197,14 +230,27 @@ static void tmp105_write(TMP105State *s)
>           break;
>   
>       case TMP105_REG_CONFIG:
> -        if (FIELD_EX8(s->buf[0] & ~s->config, CONFIG, SHUTDOWN_MODE)) {
> +        config = s->buf[0] & var->config_wmask;
> +        if (FIELD_EX8(config & ~s->config, CONFIG, SHUTDOWN_MODE)) {
>               trace_tmp105_write_shutdown(s->parent_obj.address);
> +            if (FIELD_EX8(config, CONFIG, THERMOSTAT_MODE)) {
> +                s->alarm = 0;
> +            }
>           }
> -        s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0);
> -        s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> -        if (FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT) &&
> -            FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
> +        if (var->tm_change_clears_alert &&
> +            FIELD_EX8(config ^ s->config, CONFIG, THERMOSTAT_MODE)) {
> +            s->alarm = 0;
> +            s->fault_count = 0;
> +            s->detect_falling = false;
> +        }
> +        waking = FIELD_EX8(s->config & ~config, CONFIG, SHUTDOWN_MODE);
> +        one_shot = FIELD_EX8(config, CONFIG, ONE_SHOT);
> +        s->config = FIELD_DP8(config, CONFIG, ONE_SHOT, 0);
> +        s->faults = var->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> +        if (one_shot && FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE)) {
>               tmp105_alarm_update(s, true);
> +        } else if (waking) {
> +            tmp105_alarm_update(s, false);
>           } else {
>               tmp105_interrupt_update(s);
>           }
> @@ -214,7 +260,8 @@ static void tmp105_write(TMP105State *s)
>       case TMP105_REG_T_HIGH:
>           if (s->len >= 3) {
>               s->limit[s->pointer & 1] = (int16_t)
> -                    ((((uint16_t) s->buf[0]) << 8) | (s->buf[1] & 0xf0));
> +                    ((((uint16_t) s->buf[0]) << 8) |
> +                     (s->buf[1] & var->limit_lsb_mask));
>           }
>           tmp105_interrupt_update(s);
>           break;
> @@ -265,8 +312,9 @@ static int tmp105_event(I2CSlave *i2c, enum i2c_event event)
>   static int tmp105_post_load(void *opaque, int version_id)
>   {
>       TMP105State *s = opaque;
> +    const TMP105Variant *var = TMP105_GET_CLASS(s)->var;
>   
> -    s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> +    s->faults = var->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
>   
>       tmp105_interrupt_update(s);
>       return 0;
> @@ -339,11 +387,12 @@ static const VMStateDescription vmstate_tmp105 = {
>   static void tmp105_reset_hold(Object *obj, ResetType type)
>   {
>       TMP105State *s = TMP105(obj);
> +    const TMP105Variant *var = TMP105_GET_CLASS(s)->var;
>   
>       s->temperature = 0;
>       s->pointer = 0;
>       s->config = 0;
> -    s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
> +    s->faults = var->faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)];
>       s->fault_count = 0;
>       s->alarm = 0;
>       s->detect_falling = false;
> @@ -376,6 +425,7 @@ static void tmp105_class_init(ObjectClass *klass, const void *data)
>       DeviceClass *dc = DEVICE_CLASS(klass);
>       I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
>       ResettableClass *rc = RESETTABLE_CLASS(klass);
> +    TMP105Class *tc = TMP105_CLASS(klass);
>   
>       dc->realize = tmp105_realize;
>       k->event = tmp105_event;
> @@ -383,19 +433,82 @@ static void tmp105_class_init(ObjectClass *klass, const void *data)
>       k->send = tmp105_tx;
>       rc->phases.hold = tmp105_reset_hold;
>       dc->vmsd = &vmstate_tmp105;
> +    tc->var = data;
>   }
>   
> -static const TypeInfo tmp105_info = {
> -    .name          = TYPE_TMP105,
> -    .parent        = TYPE_I2C_SLAVE,
> -    .instance_size = sizeof(TMP105State),
> -    .instance_init = tmp105_initfn,
> -    .class_init    = tmp105_class_init,
> +/*
> + * Fault-queue length selected by Config F1:F0. Row 0 (1/2/4/6) is used by the
> + * TMP105, TMP175 and LM75B; row 1 (1/2/3/4) by the TMP75.
> + */
> +static const uint8_t tmp105_faultq[][4] = {
> +    { 1, 2, 4, 6 },
> +    { 1, 2, 3, 4 },
>   };
>   
> -static void tmp105_register_types(void)
> -{
> -    type_register_static(&tmp105_info);
> -}
> +/* The F1:F0 field must never index past a fault-queue table row. */
> +QEMU_BUILD_BUG_ON((1 << R_CONFIG_FAULT_QUEUE_LENGTH) - 1 >=
> +                  ARRAY_SIZE(tmp105_faultq[0]));
> +
> +static const TMP105Variant tmp105_variant = {
> +    .faultq = tmp105_faultq[0],
> +    .config_wmask = 0xff,
> +    .fixed_res = -1,
> +    .limit_lsb_mask = 0xf0,
> +    .tm_change_clears_alert = false,
> +};
> +
> +static const TMP105Variant tmp175_variant = {
> +    .faultq = tmp105_faultq[0],
> +    .config_wmask = 0xff,
> +    .fixed_res = -1,
> +    .limit_lsb_mask = 0xf0,
> +    .tm_change_clears_alert = false,
> +};
> +
> +static const TMP105Variant tmp75_variant = {
> +    .faultq = tmp105_faultq[1],
> +    .config_wmask = 0xff,
> +    .fixed_res = -1,
> +    .limit_lsb_mask = 0xf0,
> +    .tm_change_clears_alert = true,
> +};
> +
> +static const TMP105Variant lm75b_variant = {
> +    .faultq = tmp105_faultq[0],
> +    .config_wmask = 0x1f,    /* R1:R0 and OS are reserved */
> +    .fixed_res = 2,          /* fixed 11-bit resolution, 0.125 C */
> +    .limit_lsb_mask = 0x80,  /* 9-bit limits, 0.5 C */
> +    .tm_change_clears_alert = false,
> +};
> +
> +static const TypeInfo tmp105_types[] = {
> +    {
> +        .name          = TYPE_TMP105,
> +        .parent        = TYPE_I2C_SLAVE,
> +        .instance_size = sizeof(TMP105State),
> +        .class_size    = sizeof(TMP105Class),
> +        .instance_init = tmp105_initfn,
> +        .class_init    = tmp105_class_init,
> +        .class_data    = &tmp105_variant,
> +    },
> +    {
> +        .name          = TYPE_TMP175,
> +        .parent        = TYPE_TMP105,
> +        .class_init    = tmp105_class_init,
> +        .class_data    = &tmp175_variant,
> +    },
> +    {
> +        .name          = TYPE_TMP75,
> +        .parent        = TYPE_TMP105,
> +        .class_init    = tmp105_class_init,
> +        .class_data    = &tmp75_variant,
> +    },
> +    {
> +        .name          = TYPE_LM75B,
> +        .parent        = TYPE_TMP105,
> +        .class_init    = tmp105_class_init,
> +        .class_data    = &lm75b_variant,
> +    },
> +};
>   
> -type_init(tmp105_register_types)
> +DEFINE_TYPES(tmp105_types)
> diff --git a/include/hw/sensor/tmp105.h b/include/hw/sensor/tmp105.h
> index daece592e9..0698aeead7 100644
> --- a/include/hw/sensor/tmp105.h
> +++ b/include/hw/sensor/tmp105.h
> @@ -1,5 +1,5 @@
>   /*
> - * Texas Instruments TMP105 Temperature Sensor
> + * Texas Instruments TMP105/TMP75/TMP175/LM75B Temperature Sensor
>    *
>    * Browse the data sheet:
>    *
> @@ -14,6 +14,10 @@
>   #ifndef HW_SENSOR_TMP105_H
>   #define HW_SENSOR_TMP105_H
>   
> +/* TMP75, TMP175 and NXP LM75B are register-compatible with TMP105. */
>   #define TYPE_TMP105 "tmp105"
> +#define TYPE_TMP175 "tmp175"
> +#define TYPE_TMP75  "tmp75"
> +#define TYPE_LM75B  "lm75b"
>   
>   #endif
>