diff mbox

[v2,2/4] RTC:Add RTC update-ended interrupt logic

Message ID A9667DDFB95DB7438FA9D7D576C3D87E071FF3@SHSMSX101.ccr.corp.intel.com
State New
Headers show

Commit Message

Zhang, Yang Z Feb. 20, 2012, 12:25 a.m. UTC
Use timer to emulate RTC update-ended interrupt. The timer is enabled
only when UIE is setting.

Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>

---
 hw/mc146818rtc.c |   53 ++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 48 insertions(+), 5 deletions(-)

--
1.7.1

Comments

Paolo Bonzini Feb. 20, 2012, 7:38 a.m. UTC | #1
On 02/20/2012 01:25 AM, Zhang, Yang Z wrote:
> Use timer to emulate RTC update-ended interrupt. The timer is enabled
> only when UIE is setting.

The timer needs to be enabled when UF is clear, not when UIE is set.  If
UIE is set but the update interrupt is masked you do not need to do
anything; on the other hand you need to update UF in case the guest is
observing interrupts with polling.

Paolo
Zhang, Yang Z Feb. 20, 2012, 11:55 p.m. UTC | #2
> -----Original Message-----
> From: Paolo Bonzini [mailto:paolo.bonzini@gmail.com] On Behalf Of Paolo
> Bonzini
> Sent: Monday, February 20, 2012 3:38 PM
> To: Zhang, Yang Z
> Cc: qemu-devel@nongnu.org; Jan Kiszka; kvm@vger.kernel.org;
> aliguori@us.ibm.com; Marcelo Tosatti
> Subject: Re: [PATCH v2 2/4] RTC:Add RTC update-ended interrupt logic
> 
> On 02/20/2012 01:25 AM, Zhang, Yang Z wrote:
> > Use timer to emulate RTC update-ended interrupt. The timer is enabled
> > only when UIE is setting.
> 
> The timer needs to be enabled when UF is clear, not when UIE is set.  If UIE is
> set but the update interrupt is masked you do not need to do anything; on the
> other hand you need to update UF in case the guest is observing interrupts with
> polling.
Good point.

best regards
yang
diff mbox

Patch

diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index b6655ae..bb1873b 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -93,9 +93,14 @@  typedef struct RTCState {
     qemu_irq irq;
     qemu_irq sqw_irq;
     int it_shift;
+
     /* periodic timer */
     QEMUTimer *periodic_timer;
     int64_t next_periodic_time;
+
+    /* update-ended timer */
+    QEMUTimer *update_timer;
+
     uint16_t irq_reinject_on_ack_count;
     uint32_t irq_coalesced;
     uint32_t period;
@@ -140,7 +145,8 @@  static void rtc_coalesced_timer(void *opaque)
 }
 #endif

-static void rtc_timer_update(RTCState *s, int64_t current_time)
+/* handle periodic timer */
+static void periodic_timer_update(RTCState *s, int64_t current_time)
 {
     int period_code, period;
     int64_t cur_clock, next_irq_clock;
@@ -178,7 +184,7 @@  static void rtc_periodic_timer(void *opaque)
 {
     RTCState *s = opaque;

-    rtc_timer_update(s, 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) {
         s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
@@ -205,6 +211,40 @@  static void rtc_periodic_timer(void *opaque)
     }
 }

+/* handle update-ended timer */
+static void update_ended_timer_update(RTCState *s)
+{
+    struct timeval tv_now;
+    uint64_t next_update_time;
+    uint64_t expire_time;
+
+    if ((s->cmos_data[RTC_REG_B] & REG_B_UIE) &&
+            !(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+        gettimeofday(&tv_now, NULL);
+        next_update_time = USEC_PER_SEC -
+                           (tv_now.tv_usec + s->offset_usec) % 1000000;
+        expire_time = (get_ticks_per_sec() / USEC_PER_SEC) * next_update_time +
+                       qemu_get_clock_ns(rtc_clock);
+        qemu_mod_timer(s->update_timer, expire_time);
+    } else {
+        qemu_del_timer(s->update_timer);
+    }
+}
+
+static void rtc_update_timer(void *opaque)
+{
+    RTCState *s = opaque;
+
+    update_ended_timer_update(s);
+    if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+        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);
+        }
+    }
+}
+
 static void rtc_set_offset(RTCState *s)
 {
     struct tm *tm = &s->current_tm;
@@ -256,7 +296,7 @@  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
             /* UIP bit is read only */
             s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
                 (s->cmos_data[RTC_REG_A] & REG_A_UIP);
-            rtc_timer_update(s, qemu_get_clock_ns(rtc_clock));
+            periodic_timer_update(s, qemu_get_clock_ns(rtc_clock));
             break;
         case RTC_REG_B:
             if (data & REG_B_SET) {
@@ -279,7 +319,8 @@  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
             } else {
                 s->cmos_data[RTC_REG_B] = data;
             }
-            rtc_timer_update(s, qemu_get_clock_ns(rtc_clock));
+            periodic_timer_update(s, qemu_get_clock_ns(rtc_clock));
+            update_ended_timer_update(s);
             break;
         case RTC_REG_C:
         case RTC_REG_D:
@@ -493,6 +534,7 @@  static const VMStateDescription vmstate_rtc = {
         VMSTATE_INT32(offset_usec, RTCState),
         VMSTATE_TIMER(periodic_timer, RTCState),
         VMSTATE_INT64(next_periodic_time, RTCState),
+        VMSTATE_TIMER(update_timer, RTCState),
         VMSTATE_UINT32_V(irq_coalesced, RTCState, 2),
         VMSTATE_UINT32_V(period, RTCState, 2),
         VMSTATE_END_OF_LIST()
@@ -505,7 +547,7 @@  static void rtc_notify_clock_reset(Notifier *notifier, void *data)
     int64_t now = *(int64_t *)data;

     rtc_set_date_from_host(&s->dev);
-    rtc_timer_update(s, now);
+    periodic_timer_update(s, now);
 #ifdef TARGET_I386
     if (s->lost_tick_policy == LOST_TICK_SLEW) {
         rtc_coalesced_timer_update(s);
@@ -589,6 +631,7 @@  static int rtc_initfn(ISADevice *dev)
 #endif

     s->periodic_timer = qemu_new_timer_ns(rtc_clock, rtc_periodic_timer, s);
+    s->update_timer = qemu_new_timer_ns(rtc_clock, rtc_update_timer, s);

     s->clock_reset_notifier.notify = rtc_notify_clock_reset;
     qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier);