Patchwork [v5,4/7] RTC: Set internal millisecond register to 500ms when reset divider

login
register
mail settings
Submitter Zhang, Yang Z
Date May 9, 2012, 7:22 a.m.
Message ID <A9667DDFB95DB7438FA9D7D576C3D87E12C82F@SHSMSX101.ccr.corp.intel.com>
Download mbox | patch
Permalink /patch/157881/
State New
Headers show

Comments

Zhang, Yang Z - May 9, 2012, 7:22 a.m.
The first update cycle begins one - half seconds later when divider
reset is removing.

Signed-off-by: Yang Zhang <yang.z.zhang@Intel.com>
---
 hw/mc146818rtc.c |   61 +++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 51 insertions(+), 10 deletions(-)

--
1.7.1

Patch

diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index b70422a..c9b5232 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -113,6 +113,14 @@  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 inline bool rtc_running(RTCState *s)
+{
+    return (!(s->cmos_data[RTC_REG_B] & REG_B_SET) &&
+            (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20);
+}
+
 static uint64_t get_guest_rtc_us(RTCState *s)
 {
     int64_t host_usec, offset_usec, guest_usec;
@@ -223,16 +231,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(rtc_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;
 }
@@ -261,12 +277,29 @@  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
         case RTC_YEAR:
             s->cmos_data[s->cmos_index] = data;
             /* if in set mode, do not update the time */
-            if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+            if (rtc_running(s)) {
                 rtc_set_time(s);
-                rtc_set_offset(s);
+                rtc_set_offset(s, 0);
             }
             break;
         case RTC_REG_A:
+            if (((data & 0x60) == 0x60) &&
+                (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) {
+                rtc_calibrate_time(s);
+                rtc_set_cmos(s);
+                s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+            } else if (((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) &&
+                    (data & 0x70)  <= 0x20) {
+                /* when the divider reset is removed, the first update cycle
+                 * begins one-half second later*/
+                divider_reset = 1;
+                if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+                    rtc_set_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);
@@ -275,7 +308,7 @@  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
         case RTC_REG_B:
             if (data & REG_B_SET) {
                 /* update cmos to when the rtc was stopping */
-                if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+                if (rtc_running(s)) {
                     rtc_calibrate_time(s);
                     rtc_set_cmos(s);
                 }
@@ -284,9 +317,16 @@  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
                 data &= ~REG_B_UIE;
             } else {
                 /* if disabling set mode, update the time */
-                if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+                if ((s->cmos_data[RTC_REG_B] & REG_B_SET) &&
+                        (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) {
                     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;
@@ -384,7 +424,8 @@  static int update_in_progress(RTCState *s)
 {
     int64_t guest_usec;

-    if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+    if ((s->cmos_data[RTC_REG_B] & REG_B_SET) ||
+            ((s->cmos_data[RTC_REG_A] & 0x60) == 0x60)) {
         return 0;
     }
     guest_usec = get_guest_rtc_us(s);
@@ -412,7 +453,7 @@  static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
         case RTC_YEAR:
             /* if not in set mode, calibrate cmos before
              * reading*/
-            if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+            if (rtc_running(s)) {
                 rtc_calibrate_time(s);
                 rtc_set_cmos(s);
             }