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