Re: [PATCH 3/3] stop the periodic RTC update timer

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

 



On Fri, Jan 06, 2012 at 07:37:31AM +0000, Zhang, Yang Z wrote:
> change the RTC update logic to use host time with offset to calculate RTC clock.
> 	There have no need to use two periodic timers to maintain an internal timer for RTC clock update and alarm check. Instead, we calculate the real RTC time by the host time with an offset. For alarm and updated-end interrupt, if guest enabled it, then we setup a timer, or else, stop it.
> 
> Signed-off-by: Yang Zhang <yang.z.zhang@xxxxxxxxx>
> 
> diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
> index 9cbd052..ac1854e 100644
> --- a/hw/mc146818rtc.c
> +++ b/hw/mc146818rtc.c
> @@ -84,7 +84,7 @@ typedef struct RTCState {
>      MemoryRegion io;
>      uint8_t cmos_data[128];
>      uint8_t cmos_index;
> -    struct tm current_tm;
> +    int64_t offset;
>      int32_t base_year;
>      qemu_irq irq;
>      qemu_irq sqw_irq;
> @@ -93,19 +93,18 @@ typedef struct RTCState {
>      QEMUTimer *periodic_timer;
>      int64_t next_periodic_time;
>      /* second update */
> -    int64_t next_second_time;
> +    QEMUTimer *update_timer;
> +    int64_t next_update_time;
> +    /* alarm  */
> +    QEMUTimer *alarm_timer;
> +    int64_t next_alarm_time;
>      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;
>  } RTCState;
> 
> -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)
>  {
> @@ -140,6 +139,72 @@ static void rtc_coalesced_timer(void *opaque)
>  }
>  #endif
> 
> +static inline int rtc_to_bcd(RTCState *s, int a)
> +{
> +    if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
> +        return a;
> +    } else {
> +        return ((a / 10) << 4) | (a % 10);
> +    }
> +}
> +
> +static inline int rtc_from_bcd(RTCState *s, int a)
> +{
> +    if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
> +        return a;
> +    } else {
> +        return ((a >> 4) * 10) + (a & 0x0f);
> +    }
> +}
> +
> +static void rtc_set_time(RTCState *s)
> +{
> +    struct tm tm ;
> +
> +    tm.tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]);
> +    tm.tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]);
> +    tm.tm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
> +    if (!(s->cmos_data[RTC_REG_B] & REG_B_24H) &&
> +        (s->cmos_data[RTC_HOURS] & 0x80)) {
> +        tm.tm_hour += 12;
> +    }
> +    tm.tm_wday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]) - 1;
> +    tm.tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
> +    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->offset = qemu_timedate_diff(&tm);
> +
> +    rtc_change_mon_event(&tm);
> +}
> +
> +static void rtc_update_time(RTCState *s)
> +{
> +    struct tm tm;
> +    int year;
> +
> +    qemu_get_timedate(&tm, s->offset);
> +
> +    s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm.tm_sec);
> +    s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm.tm_min);
> +    if (s->cmos_data[RTC_REG_B] & REG_B_24H) {
> +        /* 24 hour format */
> +        s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm.tm_hour);
> +    } else {
> +        /* 12 hour format */
> +        s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm.tm_hour % 12);
> +        if (tm.tm_hour >= 12)
> +            s->cmos_data[RTC_HOURS] |= 0x80;
> +    }
> +    s->cmos_data[RTC_DAY_OF_WEEK] = rtc_to_bcd(s, tm.tm_wday + 1);
> +    s->cmos_data[RTC_DAY_OF_MONTH] = rtc_to_bcd(s, tm.tm_mday);
> +    s->cmos_data[RTC_MONTH] = rtc_to_bcd(s, tm.tm_mon + 1);
> +    year = (tm.tm_year - s->base_year) % 100;
> +    if (year < 0)
> +        year += 100;
> +    s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year);
> +}
> +

Please have this code move in a separate, earlier patch.

>  static void rtc_timer_update(RTCState *s, int64_t current_time)
>  {
>      int period_code, period;
> @@ -174,7 +239,7 @@ static void rtc_timer_update(RTCState *s, int64_t current_time)
>      }
>  }
> 
> -static void rtc_periodic_timer(void *opaque)
> +static void rtc_periodic_interrupt(void *opaque)
>  {
>      RTCState *s = opaque;
> 
> @@ -204,6 +269,92 @@ static void rtc_periodic_timer(void *opaque)
>      }
>  }
> 
> +static void rtc_enable_update_interrupt(void *opaque)
> +{
> +    RTCState *s = opaque;
> +
> +    s->next_update_time = qemu_get_clock_ns(rtc_clock) + get_ticks_per_sec();
> +    qemu_mod_timer(s->update_timer, s->next_update_time);
> +}
> +
> +static void rtc_disable_update_interrupt(void *opaque)
> +{
> +    RTCState *s = opaque;
> +
> +    qemu_del_timer(s->update_timer);
> +}
> +
> +static void rtc_update_interrupt(void *opaque)
> +{
> +    RTCState *s = opaque;
> +
> +    /* 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);
> +
> +        s->next_update_time += get_ticks_per_sec();
> +       qemu_mod_timer(s->update_timer, s->next_update_time);
> +    } else
> +       rtc_disable_update_interrupt(s);
> +}
> +
> +static void rtc_enable_alarm(void *opaque)
> +{
> +    RTCState *s = opaque;
> +
> +    s->next_alarm_time = qemu_get_clock_ns(rtc_clock) + get_ticks_per_sec();
> +    s->next_alarm_time /= get_ticks_per_sec();
> +    s->next_alarm_time *= get_ticks_per_sec();
> +
> +    qemu_mod_timer(s->alarm_timer, s->next_alarm_time);
> +}
> +
> +static void rtc_disable_alarm(void *opaque)
> +{
> +    RTCState *s = opaque;
> +
> +    qemu_del_timer(s->alarm_timer);
> +}
> +
> +static void rtc_alarm_interrupt(void *opaque)
> +{
> +    RTCState *s = opaque;
> +    struct tm tm;
> +    int hour = 0;
> +
> +    qemu_get_timedate(&tm, s->offset);
> +
> +    if ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) != 0xc0) {
> +       hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM] & 0x7f);
> +       if (!(s->cmos_data[RTC_REG_B] & REG_B_24H) &&
> +            (s->cmos_data[RTC_HOURS_ALARM] & 0x80)) {
> +                   hour += 12;
> +       }
> +    }
> +
> +    /* check alarm */
> +    if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
> +        if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
> +             rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == tm.tm_sec) &&
> +            ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
> +             rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == tm.tm_min) &&
> +            ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
> +             hour == tm.tm_hour)) {
> +
> +           printf("raise irq\n");

printf.

> +            s->cmos_data[RTC_REG_C] |= 0xa0;
> +            qemu_irq_raise(s->irq);
> +        }
> +
> +       s->next_alarm_time += get_ticks_per_sec();
> +       qemu_mod_timer(s->alarm_timer, s->next_alarm_time);
> +    } else
> +       rtc_disable_alarm(s);
> +
> +}
> +
>  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
>  {
>      RTCState *s = opaque;
> @@ -239,26 +390,37 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
>              rtc_timer_update(s, qemu_get_clock_ns(rtc_clock));
>              break;
>          case RTC_REG_B:
> -            if (data & REG_B_SET) {
> -                /* set mode: reset UIP mode */
> -                s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
> -                data &= ~REG_B_UIE;
> -            } else {
> +            if (data & REG_B_SET)
> +                rtc_update_time(s);
> +            else {
>                  /* if disabling set mode, update the time */
>                  if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
>                      rtc_set_time(s);
>                  }
>              }
> +
>              if (((s->cmos_data[RTC_REG_B] ^ data) & (REG_B_DM | REG_B_24H)) &&
>                  !(data & REG_B_SET)) {
>                  /* If the time format has changed and not in set mode,
>                     update the registers immediately. */
>                  s->cmos_data[RTC_REG_B] = data;
> -                rtc_copy_date(s);
> -            } else {
> +                rtc_update_time(s);
> +            } else
>                  s->cmos_data[RTC_REG_B] = data;
> -            }
 +
> +           /* check alarm interrupt */
> +           if ((s->cmos_data[RTC_REG_B] & REG_B_AIE) &&
> +               !(s->cmos_data[RTC_REG_B] & REG_B_SET) )
> +               rtc_enable_alarm(s);
> +
> +           /* check update-ended interrupt */
> +           if ((s->cmos_data[RTC_REG_B] & REG_B_UIE) &&
> +               !(s->cmos_data[RTC_REG_B] & REG_B_SET))
> +               rtc_enable_update_interrupt(s);
> +
> +           /* check periodic interrupt or square-wave */
>              rtc_timer_update(s, qemu_get_clock_ns(rtc_clock));
> +

Regarding the UIP bit, a guest could read it in a loop and wait for the
value to change. But you can emulate it in cmos_ioport_read by reading
the host time, that is, return 1 during 244us, 0 for remaining of the
second, and have that in sync with update-cycle-ended interrupt if its
enabled.

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux