On 10/12/26 16:32, Emmanuel Blot wrote:
In preparation for a register-compatible variant, refactor DS1338
register map. Introduce DS1338Class, carrying the register-pointer wrap
boundary and the control/OSF register location and masks, and drive
inc_regptr() and the control-register handling.

The control-register power-on value is parameterised along with them,
so the part comes up holding the contents its datasheet specifies
rather than zero, and its oscillator-stop flag now reads set at
power-on.

Encode the year register modulo 100 (tm_year % 100) instead of
tm_year - 100.

Signed-off-by: Emmanuel Blot <[email protected]>
---
  hw/rtc/ds1338.c | 71 ++++++++++++++++++++++++++++++++++++++++++---------------
  1 file changed, 53 insertions(+), 18 deletions(-)

diff --git a/hw/rtc/ds1338.c b/hw/rtc/ds1338.c
index 1755a3a37c..7eaa663113 100644
--- a/hw/rtc/ds1338.c
+++ b/hw/rtc/ds1338.c
@@ -30,7 +30,14 @@
  #define HOURS_PM   0x20
  #define CTRL_OSF   0x20
-OBJECT_DECLARE_SIMPLE_TYPE(DS1338State, DS1338)
+/* DS1338 register map */
+#define DS1338_NUM_REGS    NVRAM_SIZE   /* 0x00..0x3f, then wraps */
+#define DS1338_CONTROL     0x07
+#define DS1338_CTRL_MASK   0xb3         /* bits 2, 3 and 6 read back as zero */
+/* POR: OUT, OSF, SQWE and both rate-select bits come up set. */
+#define DS1338_CTRL_RESET  0xb3
+
+OBJECT_DECLARE_TYPE(DS1338State, DS1338Class, DS1338)
struct DS1338State {
      I2CSlave parent_obj;
@@ -42,6 +49,17 @@ struct DS1338State {
      bool addr_byte;
  };
+struct DS1338Class {
+    I2CSlaveClass parent_class;
+
+    uint8_t num_regs;       /* register-pointer wrap boundary */
+    uint8_t ctrl_addr;      /* control register address */
+    uint8_t ctrl_mask;      /* writable/read-back bits of the control reg */
+    uint8_t osf_addr;       /* register holding the OSF flag */
+    uint8_t osf_mask;       /* OSF bit within osf_addr */
+    uint8_t ctrl_reset;     /* control-register power-on value */
+};
+
  static const VMStateDescription vmstate_ds1338 = {
      .name = "ds1338",
      .version_id = 2,
@@ -82,16 +100,19 @@ static void ds1338_capture_current_time(DS1338State *s)
      s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1;
      s->nvram[4] = to_bcd(now.tm_mday);
      s->nvram[5] = to_bcd(now.tm_mon + 1);
-    s->nvram[6] = to_bcd(now.tm_year - 100);
+    s->nvram[6] = to_bcd(now.tm_year % 100);
  }
static void ds1338_inc_regptr(DS1338State *s)
  {
-    /* The register pointer wraps around after 0x3F; wraparound
-     * causes the current time/date to be retransferred into
-     * the secondary registers.
+    DS1338Class *k = DS1338_GET_CLASS(s);
+
+    /*
+     * The register pointer wraps around after the last register; wraparound
+     * causes the current time/date to be retransferred into the secondary
+     * registers.
       */
-    s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
+    s->ptr = (s->ptr + 1) % k->num_regs;
      if (!s->ptr) {
          ds1338_capture_current_time(s);
      }
@@ -136,11 +157,12 @@ static uint8_t ds1338_recv(I2CSlave *i2c)
  static int ds1338_send(I2CSlave *i2c, uint8_t data)
  {
      DS1338State *s = DS1338(i2c);
+    DS1338Class *k = DS1338_GET_CLASS(i2c);
trace_ds1338_send(s->ptr, data); if (s->addr_byte) {
-        s->ptr = data & (NVRAM_SIZE - 1);
+        s->ptr = data % k->num_regs;
          s->addr_byte = false;
          return 0;
      }
@@ -190,18 +212,19 @@ static int ds1338_send(I2CSlave *i2c, uint8_t data)
              break;
          }
          s->offset = qemu_timedate_diff(&now);
-    } else if (s->ptr == 7) {
-        /* Control register. */
-
-        /* Ensure bits 2, 3 and 6 will read back as zero. */
-        data &= 0xB3;
-
-        /* Attempting to write the OSF flag to logic 1 leaves the
-           value unchanged. */
-        data = (data & ~CTRL_OSF) | (data & s->nvram[s->ptr] & CTRL_OSF);
-
-        s->nvram[s->ptr] = data;
      } else {
+        if (s->ptr == k->ctrl_addr) {
+            /* Control register: reserved bits read back as zero. */
+            data &= k->ctrl_mask;
+        }
+        if (s->ptr == k->osf_addr) {
+            /*
+             * Attempting to write the OSF flag to logic 1 leaves the
+             * value unchanged.
+             */
+            data = (data & ~k->osf_mask) |
+                   (data & s->nvram[s->ptr] & k->osf_mask);
+        }
          s->nvram[s->ptr] = data;
      }
      ds1338_inc_regptr(s);
@@ -211,11 +234,13 @@ static int ds1338_send(I2CSlave *i2c, uint8_t data)
  static void ds1338_reset_hold(Object *obj, ResetType type)
  {
      DS1338State *s = DS1338(obj);
+    DS1338Class *k = DS1338_GET_CLASS(s);
/* The clock is running and synchronized with the host */
      s->offset = 0;
      s->wday_offset = 0;
      memset(s->nvram, 0, NVRAM_SIZE);
+    s->nvram[k->ctrl_addr] = k->ctrl_reset;
      s->ptr = 0;
      s->addr_byte = false;
  }
@@ -225,12 +250,21 @@ static void ds1338_class_init(ObjectClass *klass, const 
void *data)
      DeviceClass *dc = DEVICE_CLASS(klass);
      I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
      ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DS1338Class *dsc = DS1338_CLASS(klass);
k->event = ds1338_event;
      k->recv = ds1338_recv;
      k->send = ds1338_send;
      rc->phases.hold = ds1338_reset_hold;
+    dc->desc = "DS1338 I2C RTC with 56-byte NV RAM";
      dc->vmsd = &vmstate_ds1338;
+
+    dsc->num_regs   = DS1338_NUM_REGS;
+    dsc->ctrl_addr  = DS1338_CONTROL;
+    dsc->ctrl_mask  = DS1338_CTRL_MASK;
+    dsc->osf_addr   = DS1338_CONTROL;
+    dsc->osf_mask   = CTRL_OSF;
+    dsc->ctrl_reset = DS1338_CTRL_RESET;
  }
static const TypeInfo ds1338_types[] = {
@@ -238,6 +272,7 @@ static const TypeInfo ds1338_types[] = {
          .name          = TYPE_DS1338,
          .parent        = TYPE_I2C_SLAVE,
          .instance_size = sizeof(DS1338State),
+        .class_size    = sizeof(DS1338Class),
          .class_init    = ds1338_class_init,
      },
  };


Reviewed-by: Cédric Le Goater <[email protected]>

Thanks,

C.


Reply via email to