diff --git a/qemu/hw/mc146818rtc.c b/qemu/hw/mc146818rtc.c index e1e6427..fd00f3a 100644 --- a/qemu/hw/mc146818rtc.c +++ b/qemu/hw/mc146818rtc.c @@ -72,6 +72,7 @@ struct RTCState { static void rtc_set_time(RTCState *s); static void rtc_copy_date(RTCState *s); +static void rtc_catchup(RTCState *s); static void rtc_timer_update(RTCState *s, int64_t current_time) { @@ -108,6 +109,8 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) { RTCState *s = opaque; + rtc_catchup(s); + if ((addr & 1) == 0) { s->cmos_index = data & 0x7f; } else { @@ -334,13 +337,48 @@ static void rtc_update_second2(void *opaque) s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; s->next_second_time += ticks_per_sec; - qemu_mod_timer(s->second_timer, s->next_second_time); + + /* Keep going only if interrupts are needed */ + if (s->cmos_data[RTC_REG_B] & (REG_B_AIE | REG_B_UIE)) { + qemu_mod_timer(s->second_timer, s->next_second_time); + } +} + +static void rtc_catchup(RTCState *s) +{ + int64_t now; + + if (!qemu_timer_pending(s->second_timer) && + !qemu_timer_pending(s->second_timer2)) { + + /* Count the seconds passed since last access */ + now = qemu_get_clock(vm_clock); + + while (s->next_second_time <= now) { + rtc_next_second(&s->current_tm); + s->next_second_time += ticks_per_sec; + } + + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + rtc_copy_date(s); + } + + /* Restart the right timer */ + if ((s->next_second_time - now) < ticks_per_sec/100) { + rtc_update_second(s); + } else { + qemu_mod_timer(s->second_timer, s->next_second_time); + } + } } static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) { RTCState *s = opaque; int ret; + + rtc_catchup(s); + if ((addr & 1) == 0) { return 0xff; } else {