qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Marcelo Tosatti <mtosatti@redhat.com>
To: "Zhang, Yang Z" <yang.z.zhang@intel.com>
Cc: "aliguori@us.ibm.com" <aliguori@us.ibm.com>,
	"kvm@vger.kernel.org" <kvm@vger.kernel.org>,
	"Shan, Haitao" <haitao.shan@intel.com>,
	"qemu-devel@nongnu.org" <qemu-devel@nongnu.org>,
	"avi@redhat.com" <avi@redhat.com>
Subject: Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer
Date: Wed, 11 Jan 2012 11:10:04 -0200	[thread overview]
Message-ID: <20120111131004.GA18178@amt.cnet> (raw)
In-Reply-To: <A9667DDFB95DB7438FA9D7D576C3D87E018CDB@SHSMSX101.ccr.corp.intel.com>

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@intel.com>
> 
> 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.

  parent reply	other threads:[~2012-01-11 13:11 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-01-06  7:37 [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer Zhang, Yang Z
2012-01-06 17:26 ` Jan Kiszka
2012-01-09  7:10   ` Zhang, Yang Z
2012-01-09  8:19 ` Paolo Bonzini
2012-01-10  6:37   ` Zhang, Yang Z
2012-01-10  9:24     ` Paolo Bonzini
2012-01-11  0:56       ` Zhang, Yang Z
2012-01-11  7:24         ` Paolo Bonzini
2012-01-12  0:51           ` Zhang, Yang Z
2012-01-11  7:25         ` Philipp Hahn
2012-01-11 13:10 ` Marcelo Tosatti [this message]
2012-01-12  0:00   ` Zhang, Yang Z
2012-01-12  9:26     ` Paolo Bonzini
2012-01-12 10:20       ` Marcelo Tosatti
2012-01-12 10:43         ` Paolo Bonzini
2012-01-12  9:59     ` Marcelo Tosatti
2012-01-12 10:03       ` Marcelo Tosatti
2012-01-12 10:12         ` Zhang, Yang Z

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120111131004.GA18178@amt.cnet \
    --to=mtosatti@redhat.com \
    --cc=aliguori@us.ibm.com \
    --cc=avi@redhat.com \
    --cc=haitao.shan@intel.com \
    --cc=kvm@vger.kernel.org \
    --cc=qemu-devel@nongnu.org \
    --cc=yang.z.zhang@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).