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