summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2012-10-12 11:54:38 +0100
committerPeter Maydell <peter.maydell@linaro.org>2012-10-12 11:54:38 +0100
commit35b87a861394ed088c4db3b7ba9a67d8bb2b289e (patch)
tree67386b7048735e29b794996bb2d76f86c7610423
parentba4906a9b64e165a958e12f6208ca834dc7a36dc (diff)
downloadqemu-35b87a861394ed088c4db3b7ba9a67d8bb2b289e.tar.gz
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>
-rw-r--r--hw/ds1338.c59
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;
}