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 >