The first update cycle begins one - half seconds later when divider
reset is removing.

Signed-off-by: Yang Zhang <yang.z.zh...@intel.com>
---
 hw/mc146818rtc.c |   38 +++++++++++++++++++++++++++++++++-----
 1 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 6ebb8f6..5e7fbb5 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -110,6 +110,8 @@ static void rtc_set_time(RTCState *s);
 static void rtc_calibrate_time(RTCState *s);
 static void rtc_set_cmos(RTCState *s);

+static int32_t divider_reset;
+
 static uint64_t get_guest_rtc_us(RTCState *s)
 {
     int64_t host_usec, offset_usec, guest_usec;
@@ -220,16 +222,24 @@ static void rtc_periodic_timer(void *opaque)
     }
 }

-static void rtc_set_offset(RTCState *s)
+static void rtc_set_offset(RTCState *s, int32_t start_usec)
 {
     struct tm *tm = &s->current_tm;
-    int64_t host_usec, guest_sec, guest_usec;
+    int64_t host_usec, guest_sec, guest_usec, offset_usec, old_guest_usec;

     host_usec = qemu_get_clock_ns(host_clock) / NS_PER_USEC;
+    offset_usec = s->offset_sec * USEC_PER_SEC + s->offset_usec;
+    old_guest_usec = (host_usec + offset_usec) % USEC_PER_SEC;

     guest_sec = mktimegm(tm);
-    guest_usec = guest_sec * USEC_PER_SEC;

+    /* start_usec equal 0 means rtc internal millisecond is
+     * same with before */
+    if (start_usec == 0) {
+        guest_usec = guest_sec * USEC_PER_SEC + old_guest_usec;
+    } else {
+        guest_usec = guest_sec * USEC_PER_SEC + start_usec;
+    }
     s->offset_sec = (guest_usec - host_usec) / USEC_PER_SEC;
     s->offset_usec = (guest_usec - host_usec) % USEC_PER_SEC;
 }
@@ -260,10 +270,22 @@ static void cmos_ioport_write(void *opaque, uint32_t 
addr, uint32_t data)
             /* if in set mode, do not update the time */
             if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
                 rtc_set_time(s);
-                rtc_set_offset(s);
+                rtc_set_offset(s, 0);
             }
             break;
         case RTC_REG_A:
+            /* when the divider reset is removed, the first update cycle
+             * begins one-half second later*/
+            if (((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) &&
+                                    ((data & 0x70) >> 4) <= 2) {
+                divider_reset = 1;
+                if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+                    rtc_calibrate_time(s);
+                    rtc_set_offset(s, 500000);
+                    s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+                    divider_reset = 0;
+                }
+            }
             /* UIP bit is read only */
             s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
                 (s->cmos_data[RTC_REG_A] & REG_A_UIP);
@@ -283,7 +305,13 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, 
uint32_t data)
                 /* if disabling set mode, update the time */
                 if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
                     rtc_set_time(s);
-                    rtc_set_offset(s);
+                    if (divider_reset == 1) {
+                        rtc_set_offset(s, 500000);
+                        s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+                        divider_reset = 0;
+                    } else {
+                        rtc_set_offset(s, 0);
+                    }
                 }
             }
             s->cmos_data[RTC_REG_B] = data;
--
1.7.1

Reply via email to