diff options
Diffstat (limited to 'hw/mc146818rtc.c')
-rw-r--r-- | hw/mc146818rtc.c | 62 |
1 files changed, 47 insertions, 15 deletions
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 9640afe7b3..888b85a9d6 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -73,6 +73,7 @@ struct RTCState { #ifdef TARGET_I386 uint32_t irq_coalesced; uint32_t period; + QEMUTimer *coalesced_timer; #endif QEMUTimer *second_timer; QEMUTimer *second_timer2; @@ -93,6 +94,37 @@ static void rtc_irq_raise(qemu_irq irq) { static void rtc_set_time(RTCState *s); static void rtc_copy_date(RTCState *s); +#ifdef TARGET_I386 +static void rtc_coalesced_timer_update(RTCState *s) +{ + if (s->irq_coalesced == 0) { + qemu_del_timer(s->coalesced_timer); + } else { + /* divide each RTC interval to 2 - 8 smaller intervals */ + int c = MIN(s->irq_coalesced, 7) + 1; + int64_t next_clock = qemu_get_clock(vm_clock) + + muldiv64(s->period / c, ticks_per_sec, 32768); + qemu_mod_timer(s->coalesced_timer, next_clock); + } +} + +static void rtc_coalesced_timer(void *opaque) +{ + RTCState *s = opaque; + + if (s->irq_coalesced != 0) { + apic_reset_irq_delivered(); + s->cmos_data[RTC_REG_C] |= 0xc0; + rtc_irq_raise(s->irq); + if (apic_get_irq_delivered()) { + s->irq_coalesced--; + } + } + + rtc_coalesced_timer_update(s); +} +#endif + static void rtc_timer_update(RTCState *s, int64_t current_time) { int period_code, period; @@ -138,14 +170,18 @@ static void rtc_periodic_timer(void *opaque) RTCState *s = opaque; rtc_timer_update(s, s->next_periodic_time); -#ifdef TARGET_I386 - if ((s->cmos_data[RTC_REG_C] & 0xc0) && rtc_td_hack) { - s->irq_coalesced++; - return; - } -#endif if (s->cmos_data[RTC_REG_B] & REG_B_PIE) { s->cmos_data[RTC_REG_C] |= 0xc0; +#ifdef TARGET_I386 + if(rtc_td_hack) { + apic_reset_irq_delivered(); + rtc_irq_raise(s->irq); + if (!apic_get_irq_delivered()) { + s->irq_coalesced++; + rtc_coalesced_timer_update(s); + } + } else +#endif rtc_irq_raise(s->irq); } if (s->cmos_data[RTC_REG_B] & REG_B_SQWE) { @@ -415,15 +451,6 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) case RTC_REG_C: ret = s->cmos_data[s->cmos_index]; qemu_irq_lower(s->irq); -#ifdef TARGET_I386 - if(s->irq_coalesced) { - apic_reset_irq_delivered(); - qemu_irq_raise(s->irq); - if (apic_get_irq_delivered()) - s->irq_coalesced--; - break; - } -#endif s->cmos_data[RTC_REG_C] = 0x00; break; default: @@ -536,6 +563,7 @@ static int rtc_load_td(QEMUFile *f, void *opaque, int version_id) s->irq_coalesced = qemu_get_be32(f); s->period = qemu_get_be32(f); + rtc_coalesced_timer_update(s); return 0; } #endif @@ -558,6 +586,10 @@ RTCState *rtc_init_sqw(int base, qemu_irq irq, qemu_irq sqw_irq, int base_year) s->periodic_timer = qemu_new_timer(vm_clock, rtc_periodic_timer, s); +#ifdef TARGET_I386 + if (rtc_td_hack) + s->coalesced_timer = qemu_new_timer(vm_clock, rtc_coalesced_timer, s); +#endif s->second_timer = qemu_new_timer(vm_clock, rtc_update_second, s); s->second_timer2 = qemu_new_timer(vm_clock, |