From patchwork Mon Mar 19 06:14:23 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: 147558 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 442CCB6FBE for ; Tue, 20 Mar 2012 02:41:01 +1100 (EST) Received: from localhost ([::1]:54454 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S9czo-00071O-Fq for incoming@patchwork.ozlabs.org; Mon, 19 Mar 2012 09:51:36 -0400 Received: from eggs.gnu.org ([208.118.235.92]:54664) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S9VrS-0005MT-JF for qemu-devel@nongnu.org; Mon, 19 Mar 2012 02:14:32 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1S9VrQ-0001WH-8H for qemu-devel@nongnu.org; Mon, 19 Mar 2012 02:14:30 -0400 Received: from mga14.intel.com ([143.182.124.37]:16754) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1S9VrP-0001Vs-NK for qemu-devel@nongnu.org; Mon, 19 Mar 2012 02:14:28 -0400 Received: from azsmga001.ch.intel.com ([10.2.17.19]) by azsmga102.ch.intel.com with ESMTP; 18 Mar 2012 23:14:25 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.71,315,1320652800"; d="scan'208";a="120496826" Received: from azsmsx603.amr.corp.intel.com ([10.2.161.23]) by azsmga001.ch.intel.com with ESMTP; 18 Mar 2012 23:14:25 -0700 Received: from shsmsx151.ccr.corp.intel.com (10.239.6.50) by azsmsx603.amr.corp.intel.com (10.2.161.23) with Microsoft SMTP Server (TLS) id 8.2.255.0; Sun, 18 Mar 2012 23:14:24 -0700 Received: from shsmsx101.ccr.corp.intel.com ([169.254.1.142]) by SHSMSX151.ccr.corp.intel.com ([169.254.3.91]) with mapi id 14.01.0355.002; Mon, 19 Mar 2012 14:14:23 +0800 From: "Zhang, Yang Z" To: "qemu-devel@nongnu.org" Thread-Topic: [PATCH v4 5/7] RTC:Add RTC update-ended interrupt support Thread-Index: Ac0Fl4Z7fnB8ymE0RoCP1PlFQJmE4w== Date: Mon, 19 Mar 2012 06:14:23 +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.37 X-Mailman-Approved-At: Mon, 19 Mar 2012 09:50:26 -0400 Cc: Paolo Bonzini , "aliguori@us.ibm.com" , "kvm@vger.kernel.org" Subject: [Qemu-devel] [PATCH v4 5/7] RTC:Add RTC update-ended interrupt support 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 a timer to emulate update cycle. When update cycle ended and UIE is setting, then raise an interrupt. The timer runs only when UF or AF is cleared. Signed-off-by: Yang Zhang --- hw/mc146818rtc.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 80 insertions(+), 6 deletions(-) -- 1.7.1 diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 5e7fbb5..fae049e 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -97,6 +97,11 @@ typedef struct RTCState { /* periodic timer */ QEMUTimer *periodic_timer; int64_t next_periodic_time; + /* update-ended timer */ + QEMUTimer *update_timer; + QEMUTimer *update_timer2; + uint64_t next_update_time; + uint32_t use_timer; uint16_t irq_reinject_on_ack_count; uint32_t irq_coalesced; uint32_t period; @@ -157,7 +162,8 @@ static void rtc_coalesced_timer(void *opaque) } #endif -static void rtc_timer_update(RTCState *s, int64_t current_time) +/* handle periodic timer */ +static void periodic_timer_update(RTCState *s, int64_t current_time) { int period_code, period; int64_t cur_clock, next_irq_clock; @@ -195,7 +201,7 @@ static void rtc_periodic_timer(void *opaque) { RTCState *s = opaque; - rtc_timer_update(s, s->next_periodic_time); + periodic_timer_update(s, s->next_periodic_time); s->cmos_data[RTC_REG_C] |= REG_C_PF; if (s->cmos_data[RTC_REG_B] & REG_B_PIE) { s->cmos_data[RTC_REG_C] |= REG_C_IRQF; @@ -222,6 +228,58 @@ static void rtc_periodic_timer(void *opaque) } } +/* handle update-ended timer */ +static void check_update_timer(RTCState *s) +{ + uint64_t next_update_time, expire_time; + uint64_t guest_usec; + qemu_del_timer(s->update_timer); + qemu_del_timer(s->update_timer2); + + if (!((s->cmos_data[RTC_REG_C] & (REG_C_UF | REG_C_AF)) == + (REG_C_UF | REG_C_AF)) && !(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + s->use_timer = 1; + guest_usec = get_guest_rtc_us(s) % USEC_PER_SEC; + if (guest_usec >= (USEC_PER_SEC - 244)) { + /* RTC is in update cycle when enabling UIE */ + s->cmos_data[RTC_REG_A] |= REG_A_UIP; + next_update_time = (USEC_PER_SEC - guest_usec) * NS_PER_USEC; + expire_time = qemu_get_clock_ns(rtc_clock) + next_update_time; + qemu_mod_timer(s->update_timer2, expire_time); + } else { + next_update_time = (USEC_PER_SEC - guest_usec - 244) * NS_PER_USEC; + expire_time = qemu_get_clock_ns(rtc_clock) + next_update_time; + s->next_update_time = expire_time; + qemu_mod_timer(s->update_timer, expire_time); + } + } else { + s->use_timer = 0; + } +} + +static void rtc_update_timer(void *opaque) +{ + RTCState *s = opaque; + + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + s->cmos_data[RTC_REG_A] |= REG_A_UIP; + qemu_mod_timer(s->update_timer2, s->next_update_time + 244000UL); + } +} + +static void rtc_update_timer2(void *opaque) +{ + RTCState *s = opaque; + + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + s->cmos_data[RTC_REG_C] |= REG_C_UF; + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; + s->cmos_data[RTC_REG_C] |= REG_C_IRQF; + qemu_irq_raise(s->irq); + } + check_update_timer(s); +} + static void rtc_set_offset(RTCState *s, int32_t start_usec) { struct tm *tm = &s->current_tm; @@ -283,13 +341,14 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) rtc_calibrate_time(s); rtc_set_offset(s, 500000); s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; + check_update_timer(s); divider_reset = 0; } } /* UIP bit is read only */ s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) | (s->cmos_data[RTC_REG_A] & REG_A_UIP); - rtc_timer_update(s, qemu_get_clock_ns(rtc_clock)); + periodic_timer_update(s, qemu_get_clock_ns(rtc_clock)); break; case RTC_REG_B: if (data & REG_B_SET) { @@ -315,7 +374,8 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) } } s->cmos_data[RTC_REG_B] = data; - rtc_timer_update(s, qemu_get_clock_ns(rtc_clock)); + periodic_timer_update(s, qemu_get_clock_ns(rtc_clock)); + check_update_timer(s); break; case RTC_REG_C: case RTC_REG_D: @@ -445,7 +505,7 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) break; case RTC_REG_A: ret = s->cmos_data[s->cmos_index]; - if (update_in_progress(s)) { + if ((s->use_timer == 0) && update_in_progress(s)) { ret |= REG_A_UIP; } break; @@ -453,6 +513,12 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) ret = s->cmos_data[s->cmos_index]; qemu_irq_lower(s->irq); s->cmos_data[RTC_REG_C] = 0x00; + if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { + rtc_calibrate_time(s); + } + if (ret & (REG_C_UF | REG_C_AF)) { + check_update_timer(s); + } #ifdef TARGET_I386 if(s->irq_coalesced && (s->cmos_data[RTC_REG_B] & REG_B_PIE) && @@ -545,6 +611,9 @@ static const VMStateDescription vmstate_rtc = { VMSTATE_INT32(offset_usec, RTCState), VMSTATE_TIMER(periodic_timer, RTCState), VMSTATE_INT64(next_periodic_time, RTCState), + VMSTATE_TIMER(update_timer, RTCState), + VMSTATE_TIMER(update_timer2, RTCState), + VMSTATE_UINT64(next_update_time, RTCState), VMSTATE_UINT32_V(irq_coalesced, RTCState, 2), VMSTATE_UINT32_V(period, RTCState, 2), VMSTATE_END_OF_LIST() @@ -557,7 +626,8 @@ static void rtc_notify_clock_reset(Notifier *notifier, void *data) int64_t now = *(int64_t *)data; rtc_set_date_from_host(&s->dev); - rtc_timer_update(s, now); + periodic_timer_update(s, now); + check_update_timer(s); #ifdef TARGET_I386 if (s->lost_tick_policy == LOST_TICK_SLEW) { rtc_coalesced_timer_update(s); @@ -579,6 +649,7 @@ static void rtc_reset(void *opaque) s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE); s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF); + check_update_timer(s); qemu_irq_lower(s->irq); @@ -649,6 +720,9 @@ static int rtc_initfn(ISADevice *dev) #endif s->periodic_timer = qemu_new_timer_ns(rtc_clock, rtc_periodic_timer, s); + s->update_timer = qemu_new_timer_ns(rtc_clock, rtc_update_timer, s); + s->update_timer2 = qemu_new_timer_ns(rtc_clock, rtc_update_timer2, s); + check_update_timer(s); s->clock_reset_notifier.notify = rtc_notify_clock_reset; qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier);