Patchwork [4/7] RTC: Update the RTC clock only when reading it

login
register
mail settings
Submitter Paolo Bonzini
Date July 20, 2012, 10:53 a.m.
Message ID <1342781633-7288-5-git-send-email-pbonzini@redhat.com>
Download mbox | patch
Permalink /patch/172205/
State New
Headers show

Comments

Paolo Bonzini - July 20, 2012, 10:53 a.m.
From: "Zhang, Yang Z" <yang.z.zhang@intel.com>

Calculate guest RTC based on the time of the last update, instead of
using timers.  The formula is

    (base_rtc + guest_time_now - guest_time_last_update + offset)

Base_rtc is the RTC value when the RTC was last updated.
Guest_time_now is the guest time when the access happens.
Guest_time_last_update was the guest time when the RTC was last updated.
Offset is used when divider reset happens or the set bit is toggled.

The timer is kept in order to signal interrupts, but it only needs to
run when either UF or AF is cleared.  When the bits are both set, the
timer does not run.

UIP is now synthesized when reading register A.  If the timer is not set,
or if there is more than one second before it (as is the case at the
end of this series), the leading edge of UIP is computed and the rising
edge occurs 220us later.  If the update timer occurs within one second,
however, the rising edge of the AF and UF bits should coincide withe
the falling edge of UIP.  We do not know exactly when this will happen
because there could be delays in the servicing of the timer.  Hence, in
this case reading register A only computes for the rising edge of UIP,
and latches the bit until the timer is fired and clears it.

Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/mc146818rtc.c |  315 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 183 insertions(+), 132 deletions(-)
Juan Quintela - July 23, 2012, 5:17 a.m.
Paolo Bonzini <pbonzini@redhat.com> wrote:
> From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
>
> Calculate guest RTC based on the time of the last update, instead of
> using timers.  The formula is
>
>     (base_rtc + guest_time_now - guest_time_last_update + offset)
>
> Base_rtc is the RTC value when the RTC was last updated.
> Guest_time_now is the guest time when the access happens.
> Guest_time_last_update was the guest time when the RTC was last updated.
> Offset is used when divider reset happens or the set bit is toggled.
>
> The timer is kept in order to signal interrupts, but it only needs to
> run when either UF or AF is cleared.  When the bits are both set, the
> timer does not run.
>
> UIP is now synthesized when reading register A.  If the timer is not set,
> or if there is more than one second before it (as is the case at the
> end of this series), the leading edge of UIP is computed and the rising
> edge occurs 220us later.  If the update timer occurs within one second,
> however, the rising edge of the AF and UF bits should coincide withe
> the falling edge of UIP.  We do not know exactly when this will happen
> because there could be delays in the servicing of the timer.  Hence, in
> this case reading register A only computes for the rising edge of UIP,
> and latches the bit until the timer is fired and clears it.
>
> Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> @@ -540,11 +593,12 @@ static const VMStateDescription vmstate_rtc = {
>          VMSTATE_INT32(current_tm.tm_mday, RTCState),
>          VMSTATE_INT32(current_tm.tm_mon, RTCState),
>          VMSTATE_INT32(current_tm.tm_year, RTCState),
> +        VMSTATE_UINT64(base_rtc, RTCState),
> +        VMSTATE_UINT64(last_update, RTCState),
> +        VMSTATE_INT64(offset, RTCState),
           VMSTATE_UINT64_V(base_rtc, RTCState, 3)
same ofr the others.

Normally, new fields are added at the end of the structure.


>          VMSTATE_TIMER(periodic_timer, RTCState),
>          VMSTATE_INT64(next_periodic_time, RTCState),
> -        VMSTATE_INT64(next_second_time, RTCState),
> -        VMSTATE_TIMER(second_timer, RTCState),
> -        VMSTATE_TIMER(second_timer2, RTCState),
> +        VMSTATE_TIMER(update_timer, RTCState),

I have to read the rest of the patch to know what is the relation of
this 4 fields, to see if there is any way to create this in any sane
way that is compatible.

The new fields can go in a different subsection.  The three ones that
are removed, I don't know if that values can be calculated.


>          VMSTATE_UINT32_V(irq_coalesced, RTCState, 2),
>          VMSTATE_UINT32_V(period, RTCState, 2),
>          VMSTATE_END_OF_LIST()

Later, Juan.
Paolo Bonzini - July 23, 2012, 7:19 a.m.
Il 23/07/2012 07:17, Juan Quintela ha scritto:
> Paolo Bonzini <pbonzini@redhat.com> wrote:
>> From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
>>
>> Calculate guest RTC based on the time of the last update, instead of
>> using timers.  The formula is
>>
>>     (base_rtc + guest_time_now - guest_time_last_update + offset)
>>
>> Base_rtc is the RTC value when the RTC was last updated.
>> Guest_time_now is the guest time when the access happens.
>> Guest_time_last_update was the guest time when the RTC was last updated.
>> Offset is used when divider reset happens or the set bit is toggled.
>>
>> The timer is kept in order to signal interrupts, but it only needs to
>> run when either UF or AF is cleared.  When the bits are both set, the
>> timer does not run.
>>
>> UIP is now synthesized when reading register A.  If the timer is not set,
>> or if there is more than one second before it (as is the case at the
>> end of this series), the leading edge of UIP is computed and the rising
>> edge occurs 220us later.  If the update timer occurs within one second,
>> however, the rising edge of the AF and UF bits should coincide withe
>> the falling edge of UIP.  We do not know exactly when this will happen
>> because there could be delays in the servicing of the timer.  Hence, in
>> this case reading register A only computes for the rising edge of UIP,
>> and latches the bit until the timer is fired and clears it.
>>
>> Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> @@ -540,11 +593,12 @@ static const VMStateDescription vmstate_rtc = {
>>          VMSTATE_INT32(current_tm.tm_mday, RTCState),
>>          VMSTATE_INT32(current_tm.tm_mon, RTCState),
>>          VMSTATE_INT32(current_tm.tm_year, RTCState),
>> +        VMSTATE_UINT64(base_rtc, RTCState),
>> +        VMSTATE_UINT64(last_update, RTCState),
>> +        VMSTATE_INT64(offset, RTCState),
>            VMSTATE_UINT64_V(base_rtc, RTCState, 3)
> same ofr the others.
> 
> Normally, new fields are added at the end of the structure.

Doesn't really matter if you bump the minimum version but you're right
that it is more correct.

> 
>>          VMSTATE_TIMER(periodic_timer, RTCState),
>>          VMSTATE_INT64(next_periodic_time, RTCState),
>> -        VMSTATE_INT64(next_second_time, RTCState),
>> -        VMSTATE_TIMER(second_timer, RTCState),
>> -        VMSTATE_TIMER(second_timer2, RTCState),
>> +        VMSTATE_TIMER(update_timer, RTCState),
> 
> I have to read the rest of the patch to know what is the relation of
> this 4 fields, to see if there is any way to create this in any sane
> way that is compatible.

next_second_time is computed like this (see check_update_timer):

    guest_nsec = get_guest_rtc_ns(s) % NSEC_PER_SEC;
    next_update_time = qemu_get_clock_ns(rtc_clock)
        + NSEC_PER_SEC - guest_nsec;

One of second_timer and second_timer2 is unset, depending on whether
UIP=1 or UIP=0 respectively.

second_timer if set is next_second_time.

second_timer2 if set is next_second_time + get_ticks_per_sec/100.

> The new fields can go in a different subsection.

But then you would transmit the subsection always, and old QEMU doesn't
know how to skip it.  So it doesn't really help.

This is because base_rtc, last_update and offset need to be transmitted
always.  next_alarm_time only if no alarm fired already, but the common
case is yes.  Only for update_timer we can hack and transmit it in the
slot that was used for next_second_time.  Unless you want to transmit
the subsection only for the new machine version, which is never done in
the QEMU tree.

Paolo

Patch

diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 46f0c10..3a99605 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -45,6 +45,8 @@ 
 # define DPRINTF_C(format, ...)      do { } while (0)
 #endif
 
+#define NSEC_PER_SEC    1000000000LL
+
 #define RTC_REINJECT_ON_ACK_COUNT 20
 
 typedef struct RTCState {
@@ -54,27 +56,40 @@  typedef struct RTCState {
     uint8_t cmos_index;
     struct tm current_tm;
     int32_t base_year;
+    uint64_t base_rtc;
+    uint64_t last_update;
+    int64_t offset;
     qemu_irq irq;
     qemu_irq sqw_irq;
     int it_shift;
     /* periodic timer */
     QEMUTimer *periodic_timer;
     int64_t next_periodic_time;
-    /* second update */
-    int64_t next_second_time;
+    /* update-ended timer */
+    QEMUTimer *update_timer;
     uint16_t irq_reinject_on_ack_count;
     uint32_t irq_coalesced;
     uint32_t period;
     QEMUTimer *coalesced_timer;
-    QEMUTimer *second_timer;
-    QEMUTimer *second_timer2;
     Notifier clock_reset_notifier;
     LostTickPolicy lost_tick_policy;
     Notifier suspend_notifier;
 } RTCState;
 
 static void rtc_set_time(RTCState *s);
-static void rtc_copy_date(RTCState *s);
+static void rtc_update_time(RTCState *s);
+static void rtc_set_cmos(RTCState *s);
+static inline int rtc_from_bcd(RTCState *s, int a);
+
+static uint64_t get_guest_rtc_ns(RTCState *s)
+{
+    uint64_t guest_rtc;
+    uint64_t guest_clock = qemu_get_clock_ns(rtc_clock);
+
+    guest_rtc = s->base_rtc * NSEC_PER_SEC
+                 + guest_clock - s->last_update + s->offset;
+    return guest_rtc;
+}
 
 #ifdef TARGET_I386
 static void rtc_coalesced_timer_update(RTCState *s)
@@ -110,6 +125,7 @@  static void rtc_coalesced_timer(void *opaque)
 }
 #endif
 
+/* handle periodic timer */
 static void periodic_timer_update(RTCState *s, int64_t current_time)
 {
     int period_code, period;
@@ -175,6 +191,100 @@  static void rtc_periodic_timer(void *opaque)
     }
 }
 
+/* handle update-ended timer */
+static void check_update_timer(RTCState *s)
+{
+    uint64_t next_update_time;
+    uint64_t guest_nsec;
+
+    /* From the data sheet: setting the SET bit does not prevent
+     * interrupts from occurring!  However, it will prevent an
+     * alarm interrupt from occurring, because the time of day is
+     * not updated.
+     */
+    if ((s->cmos_data[RTC_REG_C] & REG_C_UF) &&
+        (s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+        qemu_del_timer(s->update_timer);
+        return;
+    }
+    if ((s->cmos_data[RTC_REG_C] & REG_C_UF) &&
+        (s->cmos_data[RTC_REG_C] & REG_C_AF)) {
+        qemu_del_timer(s->update_timer);
+        return;
+    }
+
+    guest_nsec = get_guest_rtc_ns(s) % NSEC_PER_SEC;
+    /* reprogram to next second */
+    next_update_time = qemu_get_clock_ns(rtc_clock)
+        + NSEC_PER_SEC - guest_nsec;
+    if (next_update_time != qemu_timer_expire_time_ns(s->update_timer)) {
+        qemu_mod_timer(s->update_timer, next_update_time);
+    }
+}
+
+static inline uint8_t convert_hour(RTCState *s, uint8_t hour)
+{
+    if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) {
+        hour %= 12;
+        if (s->cmos_data[RTC_HOURS] & 0x80) {
+            hour += 12;
+        }
+    }
+    return hour;
+}
+
+static uint32_t check_alarm(RTCState *s)
+{
+    uint8_t alarm_hour, alarm_min, alarm_sec;
+    uint8_t cur_hour, cur_min, cur_sec;
+
+    alarm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]);
+    alarm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]);
+    alarm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]);
+    alarm_hour = convert_hour(s, alarm_hour);
+
+    cur_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]);
+    cur_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]);
+    cur_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS]);
+    cur_hour = convert_hour(s, cur_hour);
+
+    if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0
+                || alarm_sec == cur_sec) &&
+            ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0
+             || alarm_min == cur_min) &&
+            ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0
+             || alarm_hour == cur_hour)) {
+        return 1;
+    }
+    return 0;
+
+}
+
+static void rtc_update_timer(void *opaque)
+{
+    RTCState *s = opaque;
+    int32_t irqs = REG_C_UF;
+    int32_t new_irqs;
+
+    /* UIP might have been latched, update time and clear it.  */
+    rtc_update_time(s);
+    s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+
+    if (check_alarm(s)) {
+        irqs |= REG_C_AF;
+        if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
+            qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC);
+        }
+    }
+    new_irqs = irqs & ~s->cmos_data[RTC_REG_C];
+    s->cmos_data[RTC_REG_C] |= irqs;
+    if ((new_irqs & s->cmos_data[RTC_REG_B]) != 0) {
+        s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
+        qemu_irq_raise(s->irq);
+    }
+    check_update_timer(s);
+}
+
 static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
 {
     RTCState *s = opaque;
@@ -189,6 +299,7 @@  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
         case RTC_MINUTES_ALARM:
         case RTC_HOURS_ALARM:
             s->cmos_data[s->cmos_index] = data;
+            check_update_timer(s);
             break;
         case RTC_SECONDS:
         case RTC_MINUTES:
@@ -201,6 +312,7 @@  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);
+                check_update_timer(s);
             }
             break;
         case RTC_REG_A:
@@ -208,15 +320,21 @@  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
             s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
                 (s->cmos_data[RTC_REG_A] & REG_A_UIP);
             periodic_timer_update(s, qemu_get_clock_ns(rtc_clock));
+            check_update_timer(s);
             break;
         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)) {
+                    rtc_update_time(s);
+                }
                 /* set mode: reset UIP mode */
                 s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
                 data &= ~REG_B_UIE;
             } else {
                 /* if disabling set mode, update the time */
                 if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+                    s->offset = get_guest_rtc_ns(s) % NSEC_PER_SEC;
                     rtc_set_time(s);
                 }
             }
@@ -231,6 +349,7 @@  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
             }
             s->cmos_data[RTC_REG_B] = data;
             periodic_timer_update(s, qemu_get_clock_ns(rtc_clock));
+            check_update_timer(s);
             break;
         case RTC_REG_C:
         case RTC_REG_D:
@@ -279,10 +398,13 @@  static void rtc_set_time(RTCState *s)
     tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
     tm->tm_year = rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year - 1900;
 
+    s->base_rtc = mktimegm(tm);
+    s->last_update = qemu_get_clock_ns(rtc_clock);
+
     rtc_change_mon_event(tm);
 }
 
-static void rtc_copy_date(RTCState *s)
+static void rtc_set_cmos(RTCState *s)
 {
     const struct tm *tm = &s->current_tm;
     int year;
@@ -308,122 +430,41 @@  static void rtc_copy_date(RTCState *s)
     s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year);
 }
 
-/* month is between 0 and 11. */
-static int get_days_in_month(int month, int year)
+static void rtc_update_time(RTCState *s)
 {
-    static const int days_tab[12] = {
-        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-    };
-    int d;
-    if ((unsigned )month >= 12)
-        return 31;
-    d = days_tab[month];
-    if (month == 1) {
-        if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
-            d++;
-    }
-    return d;
+    struct tm *ret;
+    time_t guest_sec;
+    int64_t guest_nsec;
+
+    guest_nsec = get_guest_rtc_ns(s);
+    guest_sec = guest_nsec / NSEC_PER_SEC;
+    ret = gmtime(&guest_sec);
+    s->current_tm = *ret;
+    rtc_set_cmos(s);
 }
 
-/* update 'tm' to the next second */
-static void rtc_next_second(struct tm *tm)
+static int update_in_progress(RTCState *s)
 {
-    int days_in_month;
-
-    tm->tm_sec++;
-    if ((unsigned)tm->tm_sec >= 60) {
-        tm->tm_sec = 0;
-        tm->tm_min++;
-        if ((unsigned)tm->tm_min >= 60) {
-            tm->tm_min = 0;
-            tm->tm_hour++;
-            if ((unsigned)tm->tm_hour >= 24) {
-                tm->tm_hour = 0;
-                /* next day */
-                tm->tm_wday++;
-                if ((unsigned)tm->tm_wday >= 7)
-                    tm->tm_wday = 0;
-                days_in_month = get_days_in_month(tm->tm_mon,
-                                                  tm->tm_year + 1900);
-                tm->tm_mday++;
-                if (tm->tm_mday < 1) {
-                    tm->tm_mday = 1;
-                } else if (tm->tm_mday > days_in_month) {
-                    tm->tm_mday = 1;
-                    tm->tm_mon++;
-                    if (tm->tm_mon >= 12) {
-                        tm->tm_mon = 0;
-                        tm->tm_year++;
-                    }
-                }
-            }
-        }
-    }
-}
-
-
-static void rtc_update_second(void *opaque)
-{
-    RTCState *s = opaque;
-    int64_t delay;
-
-    /* if the oscillator is not in normal operation, we do not update */
-    if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
-        s->next_second_time += get_ticks_per_sec();
-        qemu_mod_timer(s->second_timer, s->next_second_time);
-    } else {
-        rtc_next_second(&s->current_tm);
-
-        if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
-            /* update in progress bit */
-            s->cmos_data[RTC_REG_A] |= REG_A_UIP;
-        }
-        /* should be 244 us = 8 / 32768 seconds, but currently the
-           timers do not have the necessary resolution. */
-        delay = (get_ticks_per_sec() * 1) / 100;
-        if (delay < 1)
-            delay = 1;
-        qemu_mod_timer(s->second_timer2,
-                       s->next_second_time + delay);
-    }
-}
-
-static void rtc_update_second2(void *opaque)
-{
-    RTCState *s = opaque;
+    int64_t guest_nsec;
 
-    if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
-        rtc_copy_date(s);
+    if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+        return 0;
     }
-
-    /* check alarm */
-    if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
-         rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) &&
-        ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
-         rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) &&
-        ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
-         rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) {
-
-        s->cmos_data[RTC_REG_C] |= REG_C_AF;
-        if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
-            qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC);
-            qemu_irq_raise(s->irq);
-            s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
+    if (qemu_timer_pending(s->update_timer)) {
+        int64_t next_update_time = qemu_timer_expire_time_ns(s->update_timer);
+        /* Latch UIP until the timer expires.  */
+        if (qemu_get_clock_ns(rtc_clock) >= (next_update_time - 244000)) {
+            s->cmos_data[RTC_REG_A] |= REG_A_UIP;
+            return 1;
         }
     }
 
-    /* update ended interrupt */
-    s->cmos_data[RTC_REG_C] |= REG_C_UF;
-    if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
-        s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
-        qemu_irq_raise(s->irq);
+    guest_nsec = get_guest_rtc_ns(s);
+    /* UIP bit will be set at last 244us of every second. */
+    if ((guest_nsec % NSEC_PER_SEC) >= (NSEC_PER_SEC - 244000)) {
+        return 1;
     }
-
-    /* clear update in progress bit */
-    s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
-
-    s->next_second_time += get_ticks_per_sec();
-    qemu_mod_timer(s->second_timer, s->next_second_time);
+    return 0;
 }
 
 static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
@@ -441,15 +482,28 @@  static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
         case RTC_DAY_OF_MONTH:
         case RTC_MONTH:
         case RTC_YEAR:
+            /* if not in set mode, calibrate cmos before
+             * reading*/
+            if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+                rtc_update_time(s);
+            }
             ret = s->cmos_data[s->cmos_index];
             break;
         case RTC_REG_A:
+            if (update_in_progress(s)) {
+                s->cmos_data[s->cmos_index] |= REG_A_UIP;
+            } else {
+                s->cmos_data[s->cmos_index] &= ~REG_A_UIP;
+            }
             ret = s->cmos_data[s->cmos_index];
             break;
         case RTC_REG_C:
             ret = s->cmos_data[s->cmos_index];
             qemu_irq_lower(s->irq);
             s->cmos_data[RTC_REG_C] = 0x00;
+            if (ret & (REG_C_UF | REG_C_AF)) {
+                check_update_timer(s);
+            }
 #ifdef TARGET_I386
             if(s->irq_coalesced &&
                     (s->cmos_data[RTC_REG_B] & REG_B_PIE) &&
@@ -484,13 +538,6 @@  void rtc_set_memory(ISADevice *dev, int addr, int val)
         s->cmos_data[addr] = val;
 }
 
-void rtc_set_date(ISADevice *dev, const struct tm *tm)
-{
-    RTCState *s = DO_UPCAST(RTCState, dev, dev);
-    s->current_tm = *tm;
-    rtc_copy_date(s);
-}
-
 /* PC cmos mappings */
 #define REG_IBM_CENTURY_BYTE        0x32
 #define REG_IBM_PS2_CENTURY_BYTE    0x37
@@ -501,9 +548,15 @@  static void rtc_set_date_from_host(ISADevice *dev)
     struct tm tm;
     int val;
 
-    /* set the CMOS date */
     qemu_get_timedate(&tm, 0);
-    rtc_set_date(dev, &tm);
+
+    s->base_rtc = mktimegm(&tm);
+    s->last_update = qemu_get_clock_ns(rtc_clock);
+    s->offset = 0;
+
+    /* set the CMOS date */
+    s->current_tm = tm;
+    rtc_set_cmos(s);
 
     val = rtc_to_bcd(s, (tm.tm_year / 100) + 19);
     rtc_set_memory(dev, REG_IBM_CENTURY_BYTE, val);
@@ -540,11 +593,12 @@  static const VMStateDescription vmstate_rtc = {
         VMSTATE_INT32(current_tm.tm_mday, RTCState),
         VMSTATE_INT32(current_tm.tm_mon, RTCState),
         VMSTATE_INT32(current_tm.tm_year, RTCState),
+        VMSTATE_UINT64(base_rtc, RTCState),
+        VMSTATE_UINT64(last_update, RTCState),
+        VMSTATE_INT64(offset, RTCState),
         VMSTATE_TIMER(periodic_timer, RTCState),
         VMSTATE_INT64(next_periodic_time, RTCState),
-        VMSTATE_INT64(next_second_time, RTCState),
-        VMSTATE_TIMER(second_timer, RTCState),
-        VMSTATE_TIMER(second_timer2, RTCState),
+        VMSTATE_TIMER(update_timer, RTCState),
         VMSTATE_UINT32_V(irq_coalesced, RTCState, 2),
         VMSTATE_UINT32_V(period, RTCState, 2),
         VMSTATE_END_OF_LIST()
@@ -557,9 +611,8 @@  static void rtc_notify_clock_reset(Notifier *notifier, void *data)
     int64_t now = *(int64_t *)data;
 
     rtc_set_date_from_host(&s->dev);
-    s->next_second_time = now + (get_ticks_per_sec() * 99) / 100;
-    qemu_mod_timer(s->second_timer2, s->next_second_time);
     periodic_timer_update(s, now);
+    check_update_timer(s);
 #ifdef TARGET_I386
     if (s->lost_tick_policy == LOST_TICK_SLEW) {
         rtc_coalesced_timer_update(s);
@@ -581,6 +634,7 @@  static void rtc_reset(void *opaque)
 
     s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE);
     s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF);
+    check_update_timer(s);
 
     qemu_irq_lower(s->irq);
 
@@ -613,6 +667,7 @@  static void rtc_get_date(Object *obj, Visitor *v, void *opaque,
     ISADevice *isa = ISA_DEVICE(obj);
     RTCState *s = DO_UPCAST(RTCState, dev, isa);
 
+    rtc_update_time(s);
     visit_start_struct(v, NULL, "struct tm", name, 0, errp);
     visit_type_int32(v, &s->current_tm.tm_year, "tm_year", errp);
     visit_type_int32(v, &s->current_tm.tm_mon, "tm_mon", errp);
@@ -649,8 +704,8 @@  static int rtc_initfn(ISADevice *dev)
 #endif
 
     s->periodic_timer = qemu_new_timer_ns(rtc_clock, rtc_periodic_timer, s);
-    s->second_timer = qemu_new_timer_ns(rtc_clock, rtc_update_second, s);
-    s->second_timer2 = qemu_new_timer_ns(rtc_clock, rtc_update_second2, s);
+    s->update_timer = qemu_new_timer_ns(rtc_clock, rtc_update_timer, s);
+    check_update_timer(s);
 
     s->clock_reset_notifier.notify = rtc_notify_clock_reset;
     qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier);
@@ -658,10 +713,6 @@  static int rtc_initfn(ISADevice *dev)
     s->suspend_notifier.notify = rtc_notify_suspend;
     qemu_register_suspend_notifier(&s->suspend_notifier);
 
-    s->next_second_time =
-        qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100;
-    qemu_mod_timer(s->second_timer2, s->next_second_time);
-
     memory_region_init_io(&s->io, &cmos_ops, s, "rtc", 2);
     isa_register_ioport(dev, &s->io, base);