Mask out reserved and absent-channel bits on writes to the two configuration registers and the conversion rate, accept the one-shot trigger, and back the per-remote-channel N-factor correction registers with real storage. The N-factor state migrates through an optional subsection.
Signed-off-by: Emmanuel Blot <[email protected]> --- hw/sensor/tmp421.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/hw/sensor/tmp421.c b/hw/sensor/tmp421.c index 8bacd7d05e..9e1249afb8 100644 --- a/hw/sensor/tmp421.c +++ b/hw/sensor/tmp421.c @@ -22,6 +22,16 @@ * * 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: + * - Conversions are instantaneous. The conversion-rate register and the + * shutdown bit are stored but do not decide when a measurement appears, + * and a one-shot write is accepted without scheduling anything: a read + * always returns a fresh value. + * - The N-factor correction registers are stored and never applied. The model + * has no ideality factor to correct for. + * - The sensor-fault OPEN bit and the PVLD bit of the temperature low byte are + * never set, so neither an open circuit nor a low supply is reported. */ #include "qemu/osdep.h" @@ -44,6 +54,7 @@ struct TMP421State { /*< public >*/ int16_t temperature[4]; + uint8_t n_correct[3]; /* remote channels only */ uint8_t status; uint8_t config[2]; @@ -85,6 +96,8 @@ OBJECT_DECLARE_TYPE(TMP421State, TMP421Class, TMP421) #define TMP421_CONVERSION_RATE_REG 0x0B #define TMP421_ONE_SHOT 0x0F +#define TMP421_N_CORRECT 0x21 + #define TMP421_RESET 0xFC #define TMP421_MANUFACTURER_ID_REG 0xFE #define TMP421_DEVICE_ID_REG 0xFF @@ -203,6 +216,11 @@ static void tmp421_read(TMP421State *s) s->buf[s->len++] = tmp421_temp_low(s, s->pointer - TMP421_TEMP_LSB0); return; } + if (s->pointer >= TMP421_N_CORRECT && s->pointer < TMP421_N_CORRECT + 3) { + unsigned idx = s->pointer - TMP421_N_CORRECT; + s->buf[s->len++] = idx + 1 < sc->nchannels ? s->n_correct[idx] : 0; + return; + } switch (s->pointer) { case TMP421_MANUFACTURER_ID_REG: @@ -233,15 +251,29 @@ static void tmp421_reset(TMP421State *s); static void tmp421_write(TMP421State *s) { + TMP421Class *sc = TMP421_GET_CLASS(s); + + if (s->pointer >= TMP421_N_CORRECT && s->pointer < TMP421_N_CORRECT + 3) { + unsigned idx = s->pointer - TMP421_N_CORRECT; + if (idx + 1 < sc->nchannels) { + s->n_correct[idx] = s->buf[0]; + } + return; + } + switch (s->pointer) { - case TMP421_CONVERSION_RATE_REG: - s->rate = s->buf[0]; - break; case TMP421_CONFIG_REG_1: - s->config[0] = s->buf[0]; + s->config[0] = s->buf[0] & + (TMP421_CONFIG_RANGE | TMP421_CONFIG_SHUTDOWN); break; case TMP421_CONFIG_REG_2: - s->config[1] = s->buf[0]; + s->config[1] = s->buf[0] & sc->config2_reset; + break; + case TMP421_CONVERSION_RATE_REG: + s->rate = s->buf[0] & 0x07; + break; + case TMP421_ONE_SHOT: + /* starts a conversion in shutdown; conversions are immediate here */ break; case TMP421_RESET: tmp421_reset(s); @@ -291,6 +323,24 @@ static int tmp421_event(I2CSlave *i2c, enum i2c_event event) return 0; } +static bool tmp421_n_correct_needed(void *opaque) +{ + TMP421State *s = opaque; + + return s->n_correct[0] || s->n_correct[1] || s->n_correct[2]; +} + +static const VMStateDescription vmstate_tmp421_n_correct = { + .name = "TMP421/n-correct", + .version_id = 1, + .minimum_version_id = 1, + .needed = tmp421_n_correct_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(n_correct, TMP421State, 3), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_tmp421 = { .name = "TMP421", .version_id = 0, @@ -305,6 +355,10 @@ static const VMStateDescription vmstate_tmp421 = { VMSTATE_INT16_ARRAY(temperature, TMP421State, 4), VMSTATE_I2C_SLAVE(parent_obj, TMP421State), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_tmp421_n_correct, + NULL } }; @@ -316,6 +370,7 @@ static void tmp421_reset(TMP421State *s) * The measured temperatures are not cleared: they model the physical * input the ADC re-samples after reset, and boards may preset them. */ + memset(s->n_correct, 0, sizeof(s->n_correct)); s->pointer = 0; s->config[0] = 0; s->config[1] = sc->config2_reset; -- 2.53.0-Meta
