Each part exposes a different number of temperature channels; register only the temperature properties that exist and gate the temperature register reads on both channel presence and the configuration enable bits. A disabled or absent channel now reads back zero instead of stale data, resolving the long-standing FIXME.
Signed-off-by: Emmanuel Blot <[email protected]> --- hw/sensor/tmp421.c | 100 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/hw/sensor/tmp421.c b/hw/sensor/tmp421.c index 7f0e3b3214..8bacd7d05e 100644 --- a/hw/sensor/tmp421.c +++ b/hw/sensor/tmp421.c @@ -59,6 +59,7 @@ struct TMP421Class { I2CSlaveClass parent_class; uint8_t device_id; + uint8_t nchannels; /* local + remote */ uint8_t config2_reset; }; @@ -159,12 +160,50 @@ static void tmp421_set_temperature(Object *obj, Visitor *v, const char *name, s->temperature[tempid] = (int16_t) ((temp * 256 - 128) / 1000) + offset; } +static bool tmp421_channel_enabled(TMP421State *s, unsigned channel) +{ + TMP421Class *sc = TMP421_GET_CLASS(s); + + return channel < sc->nchannels && + (s->config[1] & (1 << (3 + channel))); +} + +/* + * A disabled or absent channel reads back zero. The low byte carries the + * temperature fraction in its upper nibble. + */ +static uint8_t tmp421_temp_high(TMP421State *s, unsigned channel) +{ + uint16_t reg = s->temperature[channel]; + + return tmp421_channel_enabled(s, channel) ? reg >> 8 : 0; +} + +static uint8_t tmp421_temp_low(TMP421State *s, unsigned channel) +{ + uint16_t reg = s->temperature[channel]; + + return tmp421_channel_enabled(s, channel) ? reg & 0xf0 : 0; +} + static void tmp421_read(TMP421State *s) { TMP421Class *sc = TMP421_GET_CLASS(s); s->len = 0; + if (s->pointer <= TMP421_TEMP_MSB3) { + /* high byte read latches the paired low byte from one conversion */ + unsigned channel = s->pointer - TMP421_TEMP_MSB0; + s->buf[s->len++] = tmp421_temp_high(s, channel); + s->buf[s->len++] = tmp421_temp_low(s, channel); + return; + } + if (s->pointer >= TMP421_TEMP_LSB0 && s->pointer <= TMP421_TEMP_LSB3) { + s->buf[s->len++] = tmp421_temp_low(s, s->pointer - TMP421_TEMP_LSB0); + return; + } + switch (s->pointer) { case TMP421_MANUFACTURER_ID_REG: s->buf[s->len++] = TMP421_MANUFACTURER_ID; @@ -184,35 +223,8 @@ static void tmp421_read(TMP421State *s) case TMP421_STATUS_REG: s->buf[s->len++] = s->status; break; - - /* FIXME: check for channel enablement in config registers */ - case TMP421_TEMP_MSB0: - s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 8); - s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 0) & 0xf0; - break; - case TMP421_TEMP_MSB1: - s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 8); - s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 0) & 0xf0; - break; - case TMP421_TEMP_MSB2: - s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 8); - s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 0) & 0xf0; - break; - case TMP421_TEMP_MSB3: - s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 8); - s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 0) & 0xf0; - break; - case TMP421_TEMP_LSB0: - s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 0) & 0xf0; - break; - case TMP421_TEMP_LSB1: - s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 0) & 0xf0; - break; - case TMP421_TEMP_LSB2: - s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 0) & 0xf0; - break; - case TMP421_TEMP_LSB3: - s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 0) & 0xf0; + default: + s->buf[s->len++] = 0; break; } } @@ -316,6 +328,19 @@ static void tmp421_reset_hold(Object *obj, ResetType type) tmp421_reset(TMP421(obj)); } +static void tmp421_initfn(Object *obj) +{ + TMP421Class *sc = TMP421_GET_CLASS(obj); + + for (unsigned channel = 0; channel < sc->nchannels; channel++) { + g_autofree char *name = g_strdup_printf("temperature%u", channel); + + object_property_add(obj, name, "int", + tmp421_get_temperature, + tmp421_set_temperature, NULL, NULL); + } +} + static void tmp421_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -331,19 +356,7 @@ static void tmp421_class_init(ObjectClass *klass, const void *data) sc->device_id = TMP421_DEVICE_ID; sc->config2_reset = 0x1c; - - object_class_property_add(klass, "temperature0", "int", - tmp421_get_temperature, - tmp421_set_temperature, NULL, NULL); - object_class_property_add(klass, "temperature1", "int", - tmp421_get_temperature, - tmp421_set_temperature, NULL, NULL); - object_class_property_add(klass, "temperature2", "int", - tmp421_get_temperature, - tmp421_set_temperature, NULL, NULL); - object_class_property_add(klass, "temperature3", "int", - tmp421_get_temperature, - tmp421_set_temperature, NULL, NULL); + sc->nchannels = 2; } static void tmp422_class_init(ObjectClass *klass, const void *data) @@ -352,6 +365,7 @@ static void tmp422_class_init(ObjectClass *klass, const void *data) sc->device_id = TMP422_DEVICE_ID; sc->config2_reset = 0x3c; + sc->nchannels = 3; } static void tmp423_class_init(ObjectClass *klass, const void *data) @@ -360,6 +374,7 @@ static void tmp423_class_init(ObjectClass *klass, const void *data) sc->device_id = TMP423_DEVICE_ID; sc->config2_reset = 0x7c; + sc->nchannels = 4; } static const TypeInfo tmp421_types[] = { @@ -367,6 +382,7 @@ static const TypeInfo tmp421_types[] = { .name = TYPE_TMP421, .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(TMP421State), + .instance_init = tmp421_initfn, .class_size = sizeof(TMP421Class), .class_init = tmp421_class_init, }, -- 2.53.0-Meta
