@@ -79,6 +79,7 @@ typedef struct RTCState {
/* periodic timer */
QEMUTimer *periodic_timer;
int64_t next_periodic_time;
+ uint64_t last_periodic_time;
/* update-ended timer */
QEMUTimer *update_timer;
uint64_t next_alarm_time;
@@ -152,7 +153,8 @@ static void rtc_coalesced_timer(void *opaque)
static void periodic_timer_update(RTCState *s, int64_t current_time)
{
int period_code, period;
- int64_t cur_clock, next_irq_clock;
+ int64_t cur_clock, next_irq_clock, pre_irq_clock;
+ bool change = false;
period_code = s->cmos_data[RTC_REG_A] & 0x0f;
if (period_code != 0
@@ -165,14 +167,28 @@ static void periodic_timer_update(RTCState *s, int64_t current_time)
if (period != s->period) {
s->irq_coalesced = (s->irq_coalesced * s->period) / period;
DPRINTF_C("cmos: coalesced irqs scaled to %d\n", s->irq_coalesced);
+ if (s->period && period) {
+ change = true;
+ }
}
s->period = period;
#endif
/* compute 32 khz clock */
cur_clock =
muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND);
+ if (change) {
+ int offset = 0;
- next_irq_clock = (cur_clock & ~(period - 1)) + period;
+ pre_irq_clock = muldiv64(s->last_periodic_time, RTC_CLOCK_RATE,
+ NANOSECONDS_PER_SECOND);
+ if ((cur_clock - pre_irq_clock) > period) {
+ offset = (cur_clock - pre_irq_clock) / period;
+ }
+ s->irq_coalesced += offset;
+ next_irq_clock = pre_irq_clock + (offset + 1) * period;
+ } else {
+ next_irq_clock = (cur_clock & ~(period - 1)) + period;
+ }
s->next_periodic_time = muldiv64(next_irq_clock, NANOSECONDS_PER_SECOND,
RTC_CLOCK_RATE) + 1;
timer_mod(s->periodic_timer, s->next_periodic_time);
@@ -187,7 +203,9 @@ static void periodic_timer_update(RTCState *s, int64_t current_time)
static void rtc_periodic_timer(void *opaque)
{
RTCState *s = opaque;
-
+ int64_t next_periodic_time;
+
+ next_periodic_time = s->next_periodic_time;
periodic_timer_update(s, s->next_periodic_time);
s->cmos_data[RTC_REG_C] |= REG_C_PF;
if (s->cmos_data[RTC_REG_B] & REG_B_PIE) {
@@ -204,6 +222,7 @@ static void rtc_periodic_timer(void *opaque)
DPRINTF_C("cmos: coalesced irqs increased to %d\n",
s->irq_coalesced);
}
+ s->last_periodic_time = next_periodic_time;
} else
#endif
qemu_irq_raise(s->irq);