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


Reply via email to