[Top][All Lists]
[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.
- [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Zhang, Yang Z, 2012/01/06
- Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Jan Kiszka, 2012/01/06
- Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Paolo Bonzini, 2012/01/09
- Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Zhang, Yang Z, 2012/01/10
- Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Paolo Bonzini, 2012/01/11
- Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Zhang, Yang Z, 2012/01/10
- Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Philipp Hahn, 2012/01/11
- Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Paolo Bonzini, 2012/01/11
- Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Zhang, Yang Z, 2012/01/11
Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer,
Marcelo Tosatti <=
Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Marcelo Tosatti, 2012/01/12
Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Marcelo Tosatti, 2012/01/12
Re: [Qemu-devel] [PATCH 3/3] stop the periodic RTC update timer, Zhang, Yang Z, 2012/01/12