qemu-devel
[Top][All Lists]
Advanced

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

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


From: Marcelo Tosatti
Subject: Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer
Date: Wed, 11 Jan 2012 11:10:04 -0200
User-agent: Mutt/1.5.21 (2010-09-15)

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 <address@hidden>
> 
> 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.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]