[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 5/5] RTC: Add alarm support



Use a timer to emulate update cycle. The timer runs only when AIE is set.

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

diff -r 3215fd33b9b1 -r 62e9edb98cd3 xen/arch/x86/hvm/rtc.c
--- a/xen/arch/x86/hvm/rtc.c    Mon Mar 05 14:49:04 2012 +0800
+++ b/xen/arch/x86/hvm/rtc.c    Mon Mar 05 15:32:18 2012 +0800
@@ -30,6 +30,12 @@

 #define USEC_PER_SEC    1000000UL
 #define NS_PER_USEC     1000UL
+#define NS_PER_SEC      1000000000ULL
+
+#define SEC_PER_MIN     60
+#define SEC_PER_HOUR    3600
+#define MIN_PER_HOUR    60
+#define HOUR_PER_DAY    24

 #define domain_vrtc(x) (&(x)->arch.hvm_domain.pl_time.vrtc)
 #define vcpu_vrtc(x)   (domain_vrtc((x)->domain))
@@ -40,6 +46,9 @@
 #define get_year(x)    (x + epoch_year)

 static void rtc_copy_date(RTCState *s);
+static void rtc_set_time(RTCState *s);
+static inline int from_bcd(RTCState *s, int a);
+static inline int convert_hour(RTCState *s, int hour);

 static void rtc_periodic_cb(struct vcpu *v, void *opaque)
 {
@@ -153,7 +162,204 @@ static void rtc_update_timer2(void *opaq
     spin_unlock(&s->lock);
 }

-static void rtc_set_time(RTCState *s);
+/* handle alarm timer */
+static void alarm_timer_update(RTCState *s)
+{
+    uint64_t next_update_time, next_alarm_sec;
+    uint64_t expire_time;
+    int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec;
+    int32_t hour, min;
+    struct domain *d = vrtc_domain(s);
+
+    ASSERT(spin_is_locked(&s->lock));
+
+    stop_timer(&s->alarm_timer);
+
+    if ((s->hw.cmos_data[RTC_REG_B] & RTC_AIE) &&
+            !(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
+    {
+        s->current_tm = gmtime(get_localtime(d));
+        rtc_copy_date(s);
+
+        alarm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS_ALARM]);
+        alarm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES_ALARM]);
+        alarm_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS_ALARM]);
+        alarm_hour = convert_hour(s, alarm_hour);
+
+        cur_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]);
+        cur_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]);
+        cur_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS]);
+        cur_hour = convert_hour(s, cur_hour);
+
+        next_update_time = USEC_PER_SEC - (get_localtime_us(d) % USEC_PER_SEC);
+        next_update_time = next_update_time * NS_PER_USEC + NOW();
+
+        if ((s->hw.cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0)
+        {
+            if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0)
+            {
+                if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                    next_alarm_sec = 1;
+                else if (cur_sec < alarm_sec)
+                    next_alarm_sec = alarm_sec - cur_sec;
+                else
+                    next_alarm_sec = alarm_sec + SEC_PER_MIN - cur_sec;
+            }
+            else
+            {
+                if (cur_min < alarm_min)
+                {
+                    min = alarm_min - cur_min;
+                    next_alarm_sec = min * SEC_PER_MIN - cur_sec;
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec += 0;
+                    else
+                        next_alarm_sec += alarm_sec;
+                }
+                else if (cur_min == alarm_min)
+                {
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec = 1;
+                    else if (cur_sec < alarm_sec)
+                        next_alarm_sec = alarm_sec - cur_sec;
+                    else
+                    {
+                        min = alarm_min + MIN_PER_HOUR - cur_min;
+                        next_alarm_sec =
+                            alarm_sec + min * SEC_PER_MIN - cur_sec;
+                    }
+                }
+                else
+                {
+                    min = alarm_min + MIN_PER_HOUR - cur_min;
+                    next_alarm_sec = min * SEC_PER_MIN - cur_sec;
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec += 0;
+                    else
+                        next_alarm_sec += alarm_sec;
+                }
+            }
+        }
+        else
+        {
+            if (cur_hour < alarm_hour)
+            {
+                hour = alarm_hour - cur_hour;
+                next_alarm_sec = hour * SEC_PER_HOUR -
+                    cur_min * SEC_PER_MIN - cur_sec;
+                if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0)
+                {
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec += 0;
+                    else
+                        next_alarm_sec += alarm_sec;
+                }
+                else
+                {
+                    next_alarm_sec += alarm_min * SEC_PER_MIN;
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec += 0;
+                    else
+                        next_alarm_sec += alarm_sec;
+                }
+            }
+            else if (cur_hour == alarm_hour)
+            {
+                if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0)
+                {
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec = 1;
+                    else if (cur_sec < alarm_sec)
+                        next_alarm_sec = alarm_sec - cur_sec;
+                    else
+                        next_alarm_sec = alarm_sec + SEC_PER_MIN - cur_sec;
+                }
+                else if (cur_min < alarm_min)
+                {
+                    min = alarm_min - cur_min;
+                    next_alarm_sec = min * SEC_PER_MIN - cur_sec;
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec += 0;
+                    else
+                        next_alarm_sec += alarm_sec;
+                }
+                else if (cur_min == alarm_min)
+                {
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec = 1;
+                    else if (cur_sec < alarm_sec)
+                        next_alarm_sec = alarm_sec - cur_sec;
+                    else
+                    {
+                        hour = alarm_hour + HOUR_PER_DAY - cur_hour;
+                        next_alarm_sec = hour * SEC_PER_HOUR -
+                            cur_min * SEC_PER_MIN - cur_sec;
+                        next_alarm_sec += alarm_min * SEC_PER_MIN + alarm_sec;
+                    }
+                }
+                else
+                {
+                    hour = alarm_hour + HOUR_PER_DAY - cur_hour;
+                    next_alarm_sec = hour * SEC_PER_HOUR -
+                        cur_min * SEC_PER_MIN - cur_sec;
+                    next_alarm_sec += alarm_min * SEC_PER_MIN;
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec += 0;
+                    else
+                        next_alarm_sec += alarm_sec;
+                }
+            }
+            else
+            {
+                hour = alarm_hour + HOUR_PER_DAY - cur_hour;
+                next_alarm_sec = hour * SEC_PER_HOUR -
+                    cur_min * SEC_PER_MIN - cur_sec;
+                if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0)
+                {
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec += 0;
+                    else
+                        next_alarm_sec += alarm_sec;
+                }
+                else
+                {
+                    next_alarm_sec += alarm_min * SEC_PER_MIN;
+                    if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0)
+                        next_alarm_sec += 0;
+                    else
+                        next_alarm_sec += alarm_sec;
+                }
+            }
+        }
+        expire_time = (next_alarm_sec - 1) * NS_PER_SEC + next_update_time;
+        /* release lock before set timer */
+        spin_unlock(&s->lock);
+        set_timer(&s->alarm_timer, expire_time);
+        /* fetch lock again */
+        spin_lock(&s->lock);
+    }
+}
+
+static void rtc_alarm_cb(void *opaque)
+{
+    RTCState *s = opaque;
+    struct domain *d = vrtc_domain(s);
+
+    spin_lock(&s->lock);
+    if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
+    {
+        s->hw.cmos_data[RTC_REG_C] |= RTC_AF;
+        /* alarm interrupt */
+        if (s->hw.cmos_data[RTC_REG_B] & RTC_AIE)
+        {
+            s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF;
+            hvm_isa_irq_deassert(d, RTC_IRQ);
+            hvm_isa_irq_assert(d, RTC_IRQ);
+        }
+        alarm_timer_update(s);
+    }
+    spin_unlock(&s->lock);
+}

 static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data)
 {
@@ -182,6 +388,7 @@ static int rtc_ioport_write(void *opaque
     case RTC_MINUTES_ALARM:
     case RTC_HOURS_ALARM:
         s->hw.cmos_data[s->hw.cmos_index] = data;
+        alarm_timer_update(s);
         break;
     case RTC_SECONDS:
     case RTC_MINUTES:
@@ -194,6 +401,7 @@ static int rtc_ioport_write(void *opaque
         /* if in set mode, do not update the time */
         if ( !(s->hw.cmos_data[RTC_REG_B] & RTC_SET) )
             rtc_set_time(s);
+        alarm_timer_update(s);
         break;
     case RTC_REG_A:
         /* UIP bit is read only */
@@ -230,6 +438,7 @@ static int rtc_ioport_write(void *opaque
         s->hw.cmos_data[RTC_REG_B] = data;
         rtc_timer_update(s);
         check_update_timer(s);
+        alarm_timer_update(s);
         break;
     case RTC_REG_C:
     case RTC_REG_D:
@@ -426,6 +635,7 @@ void rtc_migrate_timers(struct vcpu *v)
     {
         migrate_timer(&s->update_timer, v->processor);;
         migrate_timer(&s->update_timer2, v->processor);;
+        migrate_timer(&s->alarm_timer, v->processor);;
     }
 }

@@ -462,6 +672,7 @@ static int rtc_load(struct domain *d, hv
     /* Reset the periodic interrupt timer based on the registers */
     rtc_timer_update(s);
     check_update_timer(s);
+    alarm_timer_update(s);

     spin_unlock(&s->lock);

@@ -486,12 +697,12 @@ void rtc_init(struct domain *d)

     init_timer(&s->update_timer, rtc_update_timer, s, smp_processor_id());
     init_timer(&s->update_timer2, rtc_update_timer2, s, smp_processor_id());
+    init_timer(&s->alarm_timer, rtc_alarm_cb, s, smp_processor_id());

     register_portio_handler(d, RTC_PORT(0), 2, handle_rtc_io);

     rtc_reset(d);

-
     spin_lock(&s->lock);

     s->hw.cmos_data[RTC_REG_A] = RTC_REF_CLCK_32KHZ | 6; /* ~1kHz */
@@ -516,6 +727,7 @@ void rtc_deinit(struct domain *d)
     destroy_periodic_time(&s->pt);
     kill_timer(&s->update_timer);
     kill_timer(&s->update_timer2);
+    kill_timer(&s->alarm_timer);
 }

 void rtc_update_clock(struct domain *d)
diff -r 3215fd33b9b1 -r 62e9edb98cd3 xen/include/asm-x86/hvm/vpt.h
--- a/xen/include/asm-x86/hvm/vpt.h     Mon Mar 05 14:49:04 2012 +0800
+++ b/xen/include/asm-x86/hvm/vpt.h     Mon Mar 05 15:32:18 2012 +0800
@@ -110,6 +110,8 @@ typedef struct RTCState {
     /* update-ended timer */
     struct timer update_timer;
     struct timer update_timer2;
+    /* alarm timer */
+    struct timer alarm_timer;
     uint64_t next_update_time;
     uint32_t use_timer;
     spinlock_t lock;

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.