diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2012-10-12 11:54:38 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2012-10-12 11:54:38 +0100 |
commit | 35b87a861394ed088c4db3b7ba9a67d8bb2b289e (patch) | |
tree | 67386b7048735e29b794996bb2d76f86c7610423 /hw/ds1338.c | |
parent | ba4906a9b64e165a958e12f6208ca834dc7a36dc (diff) |
hw/ds1338: Recapture current time when register pointer wraps around
The DS1338 datasheet documents that the current time is captured into
the secondary registers when the register pointer wraps round to zero
as well as at a START condition. Implement this.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/ds1338.c')
-rw-r--r-- | hw/ds1338.c | 59 |
1 files changed, 42 insertions, 17 deletions
diff --git a/hw/ds1338.c b/hw/ds1338.c index be68140ae2..842d2de14d 100644 --- a/hw/ds1338.c +++ b/hw/ds1338.c @@ -26,27 +26,52 @@ typedef struct { int addr_byte; } DS1338State; +static void capture_current_time(DS1338State *s) +{ + /* Capture the current time into the secondary registers + * which will be actually read by the data transfer operation. + */ + qemu_get_timedate(&s->now, s->offset); + s->nvram[0] = to_bcd(s->now.tm_sec); + s->nvram[1] = to_bcd(s->now.tm_min); + if (s->nvram[2] & 0x40) { + s->nvram[2] = (to_bcd((s->now.tm_hour % 12)) + 1) | 0x40; + if (s->now.tm_hour >= 12) { + s->nvram[2] |= 0x20; + } + } else { + s->nvram[2] = to_bcd(s->now.tm_hour); + } + s->nvram[3] = to_bcd(s->now.tm_wday) + 1; + s->nvram[4] = to_bcd(s->now.tm_mday); + s->nvram[5] = to_bcd(s->now.tm_mon) + 1; + s->nvram[6] = to_bcd(s->now.tm_year - 100); +} + +static void inc_regptr(DS1338State *s) +{ + /* The register pointer wraps around after 0x3F; wraparound + * causes the current time/date to be retransferred into + * the secondary registers. + */ + s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1); + if (!s->ptr) { + capture_current_time(s); + } +} + static void ds1338_event(I2CSlave *i2c, enum i2c_event event) { DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c); switch (event) { case I2C_START_RECV: - qemu_get_timedate(&s->now, s->offset); - s->nvram[0] = to_bcd(s->now.tm_sec); - s->nvram[1] = to_bcd(s->now.tm_min); - if (s->nvram[2] & 0x40) { - s->nvram[2] = (to_bcd((s->now.tm_hour % 12)) + 1) | 0x40; - if (s->now.tm_hour >= 12) { - s->nvram[2] |= 0x20; - } - } else { - s->nvram[2] = to_bcd(s->now.tm_hour); - } - s->nvram[3] = to_bcd(s->now.tm_wday) + 1; - s->nvram[4] = to_bcd(s->now.tm_mday); - s->nvram[5] = to_bcd(s->now.tm_mon) + 1; - s->nvram[6] = to_bcd(s->now.tm_year - 100); + /* In h/w, capture happens on any START condition, not just a + * START_RECV, but there is no need to actually capture on + * START_SEND, because the guest can't get at that data + * without going through a START_RECV which would overwrite it. + */ + capture_current_time(s); break; case I2C_START_SEND: s->addr_byte = 1; @@ -62,7 +87,7 @@ static int ds1338_recv(I2CSlave *i2c) uint8_t res; res = s->nvram[s->ptr]; - s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1); + inc_regptr(s); return res; } @@ -116,7 +141,7 @@ static int ds1338_send(I2CSlave *i2c, uint8_t data) } else { s->nvram[s->ptr] = data; } - s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1); + inc_regptr(s); return 0; } |