From patchwork Thu May 17 02:28:49 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Zhang, Yang Z" X-Patchwork-Id: 159798 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DD2ADB6FB9 for ; Thu, 17 May 2012 12:29:14 +1000 (EST) Received: from localhost ([::1]:43462 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SUqSm-0003iD-HJ for incoming@patchwork.ozlabs.org; Wed, 16 May 2012 22:29:12 -0400 Received: from eggs.gnu.org ([208.118.235.92]:47672) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SUqSX-0003Wt-Ob for qemu-devel@nongnu.org; Wed, 16 May 2012 22:28:59 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SUqSV-0007ei-5k for qemu-devel@nongnu.org; Wed, 16 May 2012 22:28:57 -0400 Received: from mga03.intel.com ([143.182.124.21]:42321) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SUqSU-0007eQ-Li for qemu-devel@nongnu.org; Wed, 16 May 2012 22:28:55 -0400 Received: from azsmga001.ch.intel.com ([10.2.17.19]) by azsmga101.ch.intel.com with ESMTP; 16 May 2012 19:28:52 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.71,315,1320652800"; d="scan'208";a="144183435" Received: from azsmsx601.amr.corp.intel.com ([10.2.121.193]) by azsmga001.ch.intel.com with ESMTP; 16 May 2012 19:28:52 -0700 Received: from shsmsx102.ccr.corp.intel.com (10.239.4.154) by azsmsx601.amr.corp.intel.com (10.2.121.193) with Microsoft SMTP Server (TLS) id 8.2.255.0; Wed, 16 May 2012 19:28:51 -0700 Received: from shsmsx101.ccr.corp.intel.com ([169.254.1.6]) by SHSMSX102.ccr.corp.intel.com ([169.254.2.133]) with mapi id 14.01.0355.002; Thu, 17 May 2012 10:28:49 +0800 From: "Zhang, Yang Z" To: "'qemu-devel@nongnu.org'" Thread-Topic: [PATCH v6 2/7] RTC: Update the RTC clock only when reading it Thread-Index: Ac0z1MqXOYQbu0U5T+au9Obv4NiVhg== Date: Thu, 17 May 2012 02:28:49 +0000 Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [10.239.127.40] MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 143.182.124.21 Cc: 'Paolo Bonzini' , "'aliguori@us.ibm.com'" , "'qemu-devel@nongnu.org'" Subject: [Qemu-devel] [PATCH v6 2/7] RTC: Update the RTC clock only when reading it X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Use offset instead of timer to calculate guest rtc. Guest rtc is calculated by (base_rtc + guest_time_now - guest_time_last_update_rtc + offset). Base_rtc means the rtc value of last update. Guest_time_now means the guest time that access happens. Guest_time_last_update means the guest time of last update rtc. Offset is used when divider reset happened or set bit is changed. Signed-off-by: Yang Zhang --- hw/mc146818rtc.c | 209 ++++++++++++++++++------------------------------------ qemu-timer.h | 5 ++ 2 files changed, 73 insertions(+), 141 deletions(-) -- 1.7.1 diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 1ccfb50..24db5cf 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -45,6 +45,9 @@ # define DPRINTF_C(format, ...) do { } while (0) #endif +#define USEC_PER_SEC 1000000L +#define NS_PER_USEC 1000L + #define RTC_REINJECT_ON_ACK_COUNT 20 typedef struct RTCState { @@ -54,27 +57,38 @@ typedef struct RTCState { uint8_t cmos_index; struct tm current_tm; int32_t base_year; + uint64_t base_rtc; + uint64_t last_update; + int64_t offset; + uint64_t old_guest_usec; qemu_irq irq; qemu_irq sqw_irq; int it_shift; /* periodic timer */ QEMUTimer *periodic_timer; int64_t next_periodic_time; - /* second update */ - int64_t next_second_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; LostTickPolicy lost_tick_policy; Notifier suspend_notifier; } RTCState; static void rtc_set_time(RTCState *s); -static void rtc_copy_date(RTCState *s); +static void rtc_calibrate_time(RTCState *s); +static void rtc_set_cmos(RTCState *s); + +static uint64_t get_guest_rtc_us(RTCState *s) +{ + uint64_t guest_rtc; + uint64_t guest_clock = qemu_get_clock_us(rtc_clock); + + guest_rtc = s->base_rtc * USEC_PER_SEC + + guest_clock - s->last_update + s->offset; + return guest_rtc; +} #ifdef TARGET_I386 static void rtc_coalesced_timer_update(RTCState *s) @@ -175,6 +189,14 @@ static void rtc_periodic_timer(void *opaque) } } +static void rtc_set_offset(RTCState *s, bool running, bool div_reset) +{ + if (div_reset) + s->offset = 500000; + else if (!running) + s->offset = s->old_guest_usec % USEC_PER_SEC; +} + static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) { RTCState *s = opaque; @@ -201,6 +223,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) /* if in set mode, do not update the time */ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { rtc_set_time(s); + rtc_set_offset(s, 1, 0); } break; case RTC_REG_A: @@ -211,6 +234,12 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) break; case RTC_REG_B: if (data & REG_B_SET) { + /* update cmos to when the rtc was stopping */ + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + rtc_calibrate_time(s); + rtc_set_cmos(s); + s->old_guest_usec = get_guest_rtc_us(s); + } /* set mode: reset UIP mode */ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; data &= ~REG_B_UIE; @@ -218,6 +247,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) /* if disabling set mode, update the time */ if (s->cmos_data[RTC_REG_B] & REG_B_SET) { rtc_set_time(s); + rtc_set_offset(s, 0, 0); } } s->cmos_data[RTC_REG_B] = data; @@ -270,10 +300,13 @@ static void rtc_set_time(RTCState *s) 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->base_rtc = mktimegm(tm); + s->last_update = qemu_get_clock_us(rtc_clock); + rtc_change_mon_event(tm); } -static void rtc_copy_date(RTCState *s) +static void rtc_set_cmos(RTCState *s) { const struct tm *tm = &s->current_tm; int year; @@ -299,122 +332,16 @@ static void rtc_copy_date(RTCState *s) s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year); } -/* month is between 0 and 11. */ -static int get_days_in_month(int month, int year) -{ - static const int days_tab[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - }; - int d; - if ((unsigned )month >= 12) - return 31; - d = days_tab[month]; - if (month == 1) { - if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) - d++; - } - return d; -} - -/* update 'tm' to the next second */ -static void rtc_next_second(struct tm *tm) +static void rtc_calibrate_time(RTCState *s) { - int days_in_month; - - tm->tm_sec++; - if ((unsigned)tm->tm_sec >= 60) { - tm->tm_sec = 0; - tm->tm_min++; - if ((unsigned)tm->tm_min >= 60) { - tm->tm_min = 0; - tm->tm_hour++; - if ((unsigned)tm->tm_hour >= 24) { - tm->tm_hour = 0; - /* next day */ - tm->tm_wday++; - if ((unsigned)tm->tm_wday >= 7) - tm->tm_wday = 0; - days_in_month = get_days_in_month(tm->tm_mon, - tm->tm_year + 1900); - tm->tm_mday++; - if (tm->tm_mday < 1) { - tm->tm_mday = 1; - } else if (tm->tm_mday > days_in_month) { - tm->tm_mday = 1; - tm->tm_mon++; - if (tm->tm_mon >= 12) { - tm->tm_mon = 0; - tm->tm_year++; - } - } - } - } - } -} - - -static void rtc_update_second(void *opaque) -{ - RTCState *s = opaque; - int64_t delay; - - /* if the oscillator is not in normal operation, we do not update */ - if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) { - s->next_second_time += get_ticks_per_sec(); - qemu_mod_timer(s->second_timer, s->next_second_time); - } else { - rtc_next_second(&s->current_tm); - - if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { - /* update in progress bit */ - s->cmos_data[RTC_REG_A] |= REG_A_UIP; - } - /* should be 244 us = 8 / 32768 seconds, but currently the - timers do not have the necessary resolution. */ - delay = (get_ticks_per_sec() * 1) / 100; - if (delay < 1) - delay = 1; - qemu_mod_timer(s->second_timer2, - s->next_second_time + delay); - } -} - -static void rtc_update_second2(void *opaque) -{ - RTCState *s = opaque; - - if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { - rtc_copy_date(s); - } - - /* check alarm */ - if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 || - rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) && - ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 || - rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) && - ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 || - rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) { - - s->cmos_data[RTC_REG_C] |= REG_C_AF; - if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC); - qemu_irq_raise(s->irq); - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - } - } - - /* 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); - } - - /* clear update in progress bit */ - s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - - s->next_second_time += get_ticks_per_sec(); - qemu_mod_timer(s->second_timer, s->next_second_time); + struct tm *ret; + time_t guest_sec; + int64_t guest_usec; + + guest_usec = get_guest_rtc_us(s); + guest_sec = guest_usec / USEC_PER_SEC; + ret = gmtime(&guest_sec); + s->current_tm = *ret; } static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) @@ -432,6 +359,12 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) case RTC_DAY_OF_MONTH: case RTC_MONTH: case RTC_YEAR: + /* if not in set mode, calibrate cmos before + * reading*/ + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + rtc_calibrate_time(s); + rtc_set_cmos(s); + } ret = s->cmos_data[s->cmos_index]; break; case RTC_REG_A: @@ -475,13 +408,6 @@ void rtc_set_memory(ISADevice *dev, int addr, int val) s->cmos_data[addr] = val; } -void rtc_set_date(ISADevice *dev, const struct tm *tm) -{ - RTCState *s = DO_UPCAST(RTCState, dev, dev); - s->current_tm = *tm; - rtc_copy_date(s); -} - /* PC cmos mappings */ #define REG_IBM_CENTURY_BYTE 0x32 #define REG_IBM_PS2_CENTURY_BYTE 0x37 @@ -492,9 +418,15 @@ static void rtc_set_date_from_host(ISADevice *dev) struct tm tm; int val; - /* set the CMOS date */ qemu_get_timedate(&tm, 0); - rtc_set_date(dev, &tm); + + s->base_rtc = mktimegm(&tm); + s->last_update = qemu_get_clock_us(rtc_clock); + s->offset = 0; + + /* set the CMOS date */ + s->current_tm = tm; + rtc_set_cmos(s); val = rtc_to_bcd(s, (tm.tm_year / 100) + 19); rtc_set_memory(dev, REG_IBM_CENTURY_BYTE, val); @@ -531,11 +463,12 @@ static const VMStateDescription vmstate_rtc = { VMSTATE_INT32(current_tm.tm_mday, RTCState), VMSTATE_INT32(current_tm.tm_mon, RTCState), VMSTATE_INT32(current_tm.tm_year, RTCState), + VMSTATE_UINT64(base_rtc, RTCState), + VMSTATE_UINT64(last_update, RTCState), + VMSTATE_INT64(offset, RTCState), + VMSTATE_UINT64(old_guest_usec, RTCState), VMSTATE_TIMER(periodic_timer, RTCState), VMSTATE_INT64(next_periodic_time, RTCState), - VMSTATE_INT64(next_second_time, RTCState), - VMSTATE_TIMER(second_timer, RTCState), - VMSTATE_TIMER(second_timer2, RTCState), VMSTATE_UINT32_V(irq_coalesced, RTCState, 2), VMSTATE_UINT32_V(period, RTCState, 2), VMSTATE_END_OF_LIST() @@ -548,8 +481,6 @@ static void rtc_notify_clock_reset(Notifier *notifier, void *data) int64_t now = *(int64_t *)data; rtc_set_date_from_host(&s->dev); - s->next_second_time = now + (get_ticks_per_sec() * 99) / 100; - qemu_mod_timer(s->second_timer2, s->next_second_time); rtc_timer_update(s, now); #ifdef TARGET_I386 if (s->lost_tick_policy == LOST_TICK_SLEW) { @@ -604,6 +535,8 @@ static void rtc_get_date(Object *obj, Visitor *v, void *opaque, ISADevice *isa = ISA_DEVICE(obj); RTCState *s = DO_UPCAST(RTCState, dev, isa); + rtc_calibrate_time(s); + rtc_set_cmos(s); visit_start_struct(v, NULL, "struct tm", name, 0, errp); visit_type_int32(v, &s->current_tm.tm_year, "tm_year", errp); visit_type_int32(v, &s->current_tm.tm_mon, "tm_mon", errp); @@ -640,8 +573,6 @@ static int rtc_initfn(ISADevice *dev) #endif s->periodic_timer = qemu_new_timer_ns(rtc_clock, rtc_periodic_timer, s); - s->second_timer = qemu_new_timer_ns(rtc_clock, rtc_update_second, s); - s->second_timer2 = qemu_new_timer_ns(rtc_clock, rtc_update_second2, s); s->clock_reset_notifier.notify = rtc_notify_clock_reset; qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier); @@ -649,10 +580,6 @@ static int rtc_initfn(ISADevice *dev) s->suspend_notifier.notify = rtc_notify_suspend; qemu_register_suspend_notifier(&s->suspend_notifier); - s->next_second_time = - qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100; - qemu_mod_timer(s->second_timer2, s->next_second_time); - memory_region_init_io(&s->io, &cmos_ops, s, "rtc", 2); isa_register_ioport(dev, &s->io, base); diff --git a/qemu-timer.h b/qemu-timer.h index f8af595..9310f90 100644 --- a/qemu-timer.h +++ b/qemu-timer.h @@ -84,6 +84,11 @@ static inline int64_t qemu_get_clock_ms(QEMUClock *clock) return qemu_get_clock_ns(clock) / SCALE_MS; } +static inline int64_t qemu_get_clock_us(QEMUClock *clock) +{ + return qemu_get_clock_ns(clock) / SCALE_US; +} + static inline int64_t get_ticks_per_sec(void) { return 1000000000LL;