[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH] hw/ds1338.c: implement clock enable/disable (CH bit
From: |
Antoine Mathys |
Subject: |
[Qemu-devel] [PATCH] hw/ds1338.c: implement clock enable/disable (CH bit) |
Date: |
Sat, 16 Feb 2013 17:03:37 +0100 |
Signed-off-by: Antoine Mathys <address@hidden>
---
hw/ds1338.c | 156 ++++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 95 insertions(+), 61 deletions(-)
diff --git a/hw/ds1338.c b/hw/ds1338.c
index 1da0f96..5a93fb6 100644
--- a/hw/ds1338.c
+++ b/hw/ds1338.c
@@ -48,17 +48,32 @@ static const VMStateDescription vmstate_ds1338 = {
}
};
-static void capture_current_time(DS1338State *s)
+/* This mask is used to clear the read as zero bits in the RTC registers */
+static const uint8_t nvram_mask[8] = {
+ 0xff, 0x7f, 0x7f, 0x7, 0x3f, 0x1f, 0xff, 0xb3
+};
+
+
+static int compute_wday(int y, int m, int d)
{
- /* Capture the current time into the secondary registers
- * which will be actually read by the data transfer operation.
- */
- struct tm now;
- qemu_get_timedate(&now, s->offset);
- s->nvram[0] = to_bcd(now.tm_sec);
- s->nvram[1] = to_bcd(now.tm_min);
+ static int t[12] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
+
+ if (m < 2) {
+ y--;
+ }
+ return (y + y/4 - y/100 + y/400 + t[m] + d) % 7;
+}
+
+/* Write TM to the RTC registers. */
+static void write_time(DS1338State *s, const struct tm *tm)
+{
+ /* Preserve the CH flag. */
+ s->nvram[0] &= SECONDS_CH;
+ s->nvram[0] |= to_bcd(tm->tm_sec);
+
+ s->nvram[1] = to_bcd(tm->tm_min);
if (s->nvram[2] & HOURS_12) {
- int tmp = now.tm_hour;
+ int tmp = tm->tm_hour;
if (tmp % 12 == 0) {
tmp += 12;
}
@@ -68,12 +83,50 @@ static void capture_current_time(DS1338State *s)
s->nvram[2] = HOURS_12 | HOURS_PM | to_bcd(tmp - 12);
}
} else {
- s->nvram[2] = to_bcd(now.tm_hour);
+ s->nvram[2] = to_bcd(tm->tm_hour);
+ }
+ s->nvram[3] = (tm->tm_wday + s->wday_offset) % 7 + 1;
+ s->nvram[4] = to_bcd(tm->tm_mday);
+ s->nvram[5] = to_bcd(tm->tm_mon + 1);
+ s->nvram[6] = to_bcd(tm->tm_year - 100);
+}
+
+/* Read TM from the RTC registers. */
+static void read_time(DS1338State *s, struct tm *tm)
+{
+ tm->tm_sec = from_bcd(s->nvram[0] & 0x7f);
+ tm->tm_min = from_bcd(s->nvram[1] & 0x7f);
+ if (s->nvram[2] & HOURS_12) {
+ int tmp = from_bcd(s->nvram[2] & (HOURS_PM - 1));
+ if (s->nvram[2] & HOURS_PM) {
+ tmp += 12;
+ }
+ if (tmp % 12 == 0) {
+ tmp -= 12;
+ }
+ tm->tm_hour = tmp;
+ } else {
+ tm->tm_hour = from_bcd(s->nvram[2] & (HOURS_12 - 1));
+ }
+ tm->tm_mday = from_bcd(s->nvram[4] & 0x3f);
+ tm->tm_mon = from_bcd(s->nvram[5] & 0x1f) - 1;
+ tm->tm_year = from_bcd(s->nvram[6]) + 100;
+ tm->tm_wday = compute_wday(tm->tm_year + 1900, tm->tm_mon, tm->tm_mday);
+}
+
+static bool clock_running(DS1338State *s)
+{
+ return !(s->nvram[0] & SECONDS_CH);
+}
+
+static void capture_current_time(DS1338State *s)
+{
+ if (clock_running(s)) {
+ /* Write current time. */
+ struct tm tmp;
+ qemu_get_timedate(&tmp, s->offset);
+ write_time(s, &tmp);
}
- 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);
}
static void inc_regptr(DS1338State *s)
@@ -129,65 +182,46 @@ static int ds1338_send(I2CSlave *i2c, uint8_t data)
}
if (s->ptr < 7) {
/* Time register. */
- struct tm now;
- qemu_get_timedate(&now, s->offset);
- switch(s->ptr) {
- case 0:
- /* TODO: Implement CH (stop) bit. */
- now.tm_sec = from_bcd(data & 0x7f);
- break;
- case 1:
- now.tm_min = from_bcd(data & 0x7f);
- break;
- case 2:
- if (data & HOURS_12) {
- int tmp = from_bcd(data & (HOURS_PM - 1));
- if (data & HOURS_PM) {
- tmp += 12;
- }
- if (tmp % 12 == 0) {
- tmp -= 12;
- }
- now.tm_hour = tmp;
- } else {
- now.tm_hour = from_bcd(data & (HOURS_12 - 1));
- }
- break;
- case 3:
- {
- /* The day field is supposed to contain a value in
- the range 1-7. Otherwise behavior is undefined.
- */
- int user_wday = (data & 7) - 1;
- s->wday_offset = (user_wday - now.tm_wday + 7) % 7;
+ bool was_running = clock_running(s);
+
+ capture_current_time(s);
+
+ s->nvram[s->ptr] = data & nvram_mask[s->ptr];
+
+ if (clock_running(s)) {
+ /* Read the new time */
+ struct tm tmp;
+ int user_wday;
+
+ read_time(s, &tmp);
+ s->offset = qemu_timedate_diff(&tmp);
+
+ /* The day field is supposed to contain a value in
+ the range 1-7. Otherwise behavior is undefined.
+ */
+ user_wday = (s->nvram[3] & 7) - 1;
+ s->wday_offset = (user_wday - tmp.tm_wday + 7) % 7;
+ } else {
+ /* If the clock is transitioning from on to off, set the OSF
+ flag. */
+ if (was_running) {
+ s->nvram[7] |= CTRL_OSF;
}
- break;
- case 4:
- now.tm_mday = from_bcd(data & 0x3f);
- break;
- case 5:
- now.tm_mon = from_bcd(data & 0x1f) - 1;
- break;
- case 6:
- now.tm_year = from_bcd(data) + 100;
- 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;
+ s->nvram[s->ptr] = data & nvram_mask[s->ptr];
} else {
s->nvram[s->ptr] = data;
}
- inc_regptr(s);
+ /* Note: we don't need to reload the rtc registers on wraparound
+ when writing */
+ s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
return 0;
}
--
1.7.10.4
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] [PATCH] hw/ds1338.c: implement clock enable/disable (CH bit),
Antoine Mathys <=