@@ -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;
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 | 38 +++++++++++++++++++++++++++++++++----- 1 files changed, 33 insertions(+), 5 deletions(-) -- 1.7.1