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,
},
};