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 | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 40 insertions(+), 6 deletions(-) diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index b03c420..3dd71ac 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -80,6 +80,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) { uint64_t guest_rtc; @@ -221,12 +229,30 @@ 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, 1, 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; + s->old_guest_usec = get_guest_rtc_us(s); + } 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, 0, 1); + 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); @@ -235,7 +261,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); s->old_guest_usec = get_guest_rtc_us(s); @@ -245,9 +271,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, 0, 0); + if (divider_reset == 1) { + rtc_set_offset(s, 0, 1); + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; + divider_reset = 0; + } else { + rtc_set_offset(s, 0, 0); + } } } s->cmos_data[RTC_REG_B] = data; @@ -348,7 +381,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); @@ -376,7 +410,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); } -- 1.7.1