From patchwork Mon Feb 20 00:25:38 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: 142091 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id A4887B6FA5 for ; Mon, 20 Feb 2012 11:25:59 +1100 (EST) Received: from localhost ([::1]:58391 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RzH4m-000104-OD for incoming@patchwork.ozlabs.org; Sun, 19 Feb 2012 19:25:56 -0500 Received: from eggs.gnu.org ([140.186.70.92]:47284) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RzH4c-0000z5-4t for qemu-devel@nongnu.org; Sun, 19 Feb 2012 19:25:49 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RzH4a-0007Pd-MT for qemu-devel@nongnu.org; Sun, 19 Feb 2012 19:25:46 -0500 Received: from mga03.intel.com ([143.182.124.21]:65276) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RzH4a-0007PX-7o for qemu-devel@nongnu.org; Sun, 19 Feb 2012 19:25:44 -0500 Received: from azsmga002.ch.intel.com ([10.2.17.35]) by azsmga101.ch.intel.com with ESMTP; 19 Feb 2012 16:25:43 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.71,315,1320652800"; d="scan'208";a="68193947" Received: from pgsmsx509.gar.corp.intel.com ([172.30.13.17]) by AZSMGA002.ch.intel.com with ESMTP; 19 Feb 2012 16:25:41 -0800 Received: from pgsmsx152.gar.corp.intel.com (172.30.236.43) by PGSMSX509.gar.corp.intel.com (172.30.13.17) with Microsoft SMTP Server (TLS) id 8.2.255.0; Mon, 20 Feb 2012 08:25:41 +0800 Received: from shsmsx151.ccr.corp.intel.com (10.239.6.50) by PGSMSX152.gar.corp.intel.com (172.30.236.43) with Microsoft SMTP Server (TLS) id 14.1.355.2; Mon, 20 Feb 2012 08:25:40 +0800 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, 20 Feb 2012 08:25:39 +0800 From: "Zhang, Yang Z" To: "qemu-devel@nongnu.org" Thread-Topic: [PATCH v2 3/4] RTC:Add alarm support Thread-Index: AczvZinysfkezj0CTs2xJilVVajq6w== Date: Mon, 20 Feb 2012 00:25:38 +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" , Marcelo Tosatti , Jan Kiszka , "kvm@vger.kernel.org" Subject: [Qemu-devel] [PATCH v2 3/4] RTC:Add alarm 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 timer to emulate alarm. The timer is enabled when AIE is setting Signed-off-by: Yang Zhang --- hw/mc146818rtc.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 187 insertions(+), 0 deletions(-) -- 1.7.1 diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index bb1873b..2445c6b 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -45,6 +45,10 @@ #endif #define USEC_PER_SEC 1000000 +#define SEC_PER_MIN 60 +#define SEC_PER_HOUR 3600 +#define MIN_PER_HOUR 60 +#define HOUR_PER_DAY 24 #define RTC_REINJECT_ON_ACK_COUNT 20 @@ -101,6 +105,9 @@ typedef struct RTCState { /* update-ended timer */ QEMUTimer *update_timer; + /* alarm timer */ + QEMUTimer *alarm_timer; + uint16_t irq_reinject_on_ack_count; uint32_t irq_coalesced; uint32_t period; @@ -110,6 +117,9 @@ typedef struct RTCState { } RTCState; static void rtc_set_time(RTCState *s); +static void rtc_set_cmos(RTCState *s); +static void rtc_calibrate_time(RTCState *s); +static inline int rtc_from_bcd(RTCState *s, int a); #ifdef TARGET_I386 static void rtc_coalesced_timer_update(RTCState *s) @@ -245,6 +255,180 @@ static void rtc_update_timer(void *opaque) } } +/* handle alarm timer */ +static void alarm_timer_update(RTCState *s) +{ + struct timeval tv_now; + uint64_t next_update_usec, next_update_time, next_alarm_sec; + uint64_t expire_time; + int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec; + int32_t hour, min; + + if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { + rtc_calibrate_time(s); + rtc_set_cmos(s); + + alarm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]); + alarm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]); + alarm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]); + if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { + alarm_hour %= 12; + if (s->cmos_data[RTC_HOURS] & 0x80) { + alarm_hour += 12; + } + } + cur_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); + cur_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); + cur_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS]); + if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { + cur_hour %= 12; + if (s->cmos_data[RTC_HOURS] & 0x80) { + cur_hour += 12; + } + } + + gettimeofday(&tv_now, NULL); + next_update_usec = + USEC_PER_SEC - (tv_now.tv_usec + s->offset_usec) % 1000000; + next_update_time = (get_ticks_per_sec() / USEC_PER_SEC) * + next_update_usec + qemu_get_clock_ns(rtc_clock); + + if ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0) { + if ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec = 1; + } else if (cur_sec < alarm_sec) { + next_alarm_sec = alarm_sec - cur_sec; + } else { + next_alarm_sec = alarm_sec + SEC_PER_MIN - cur_sec; + } + } else { + if (cur_min < alarm_min) { + min = alarm_min - cur_min; + next_alarm_sec = min * SEC_PER_MIN - cur_sec; + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec += 0; + } else { + next_alarm_sec += alarm_sec; + } + } else if (cur_min == alarm_min) { + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec += 1; + } else if (cur_sec < alarm_sec) { + next_alarm_sec = alarm_sec - cur_sec; + } else { + min = alarm_min + MIN_PER_HOUR - cur_min; + next_alarm_sec = + alarm_sec + min * SEC_PER_MIN - cur_sec; + } + } else { + min = alarm_min + MIN_PER_HOUR - cur_min; + next_alarm_sec = min * SEC_PER_MIN - cur_sec; + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec += 0; + } else { + next_alarm_sec += alarm_sec; + } + } + } + } else { + if (cur_hour < alarm_hour) { + hour = alarm_hour - cur_hour; + next_alarm_sec = hour * SEC_PER_HOUR - + cur_min * SEC_PER_MIN - cur_sec; + if ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec += 0; + } else { + next_alarm_sec += alarm_sec; + } + } else { + next_alarm_sec += alarm_min * SEC_PER_MIN; + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec += 0; + } else { + next_alarm_sec += alarm_sec; + } + } + } else if (cur_hour == alarm_hour) { + if ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec = 1; + } else if (cur_sec < alarm_sec) { + next_alarm_sec = alarm_sec - cur_sec; + } else { + next_alarm_sec = alarm_sec + SEC_PER_MIN - cur_sec; + } + } else if (cur_min < alarm_min) { + min = alarm_min - cur_min; + next_alarm_sec = min * SEC_PER_MIN - cur_sec; + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec += 0; + } else { + next_alarm_sec += alarm_sec; + } + } else if (cur_min == alarm_min) { + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec = 1; + } else if (cur_sec < alarm_sec) { + next_alarm_sec = alarm_sec - cur_sec; + } else { + hour = alarm_hour - cur_hour; + next_alarm_sec = hour * SEC_PER_HOUR - + cur_min * SEC_PER_MIN - cur_sec; + next_alarm_sec += alarm_min * SEC_PER_MIN + alarm_sec; + } + } else { + hour = alarm_hour - cur_hour; + next_alarm_sec = hour * SEC_PER_HOUR - + cur_min * SEC_PER_MIN - cur_sec; + next_alarm_sec += alarm_min * SEC_PER_MIN; + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec += 0; + } else { + next_alarm_sec += alarm_sec; + } + } + } else { + hour = alarm_hour + HOUR_PER_DAY - cur_hour; + next_alarm_sec = hour * SEC_PER_HOUR - + cur_min * SEC_PER_MIN - cur_sec; + if ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec += 0; + } else { + next_alarm_sec += alarm_sec; + } + } else { + next_alarm_sec += alarm_min * SEC_PER_MIN; + if ((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) { + next_alarm_sec += 0; + } else { + next_alarm_sec += alarm_sec; + } + } + } + } + expire_time = (next_alarm_sec - 1) * get_ticks_per_sec() + + next_update_time; + qemu_mod_timer(s->alarm_timer, expire_time); + } else { + qemu_del_timer(s->alarm_timer); + } +} + +static void rtc_alarm_timer(void *opaque) +{ + RTCState *s = opaque; + + /* alarm interrupt */ + s->cmos_data[RTC_REG_C] |= REG_C_AF; + if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { + s->cmos_data[RTC_REG_C] |= REG_C_IRQF; + qemu_irq_raise(s->irq); + } +} + static void rtc_set_offset(RTCState *s) { struct tm *tm = &s->current_tm; @@ -321,6 +505,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) } periodic_timer_update(s, qemu_get_clock_ns(rtc_clock)); update_ended_timer_update(s); + alarm_timer_update(s); break; case RTC_REG_C: case RTC_REG_D: @@ -535,6 +720,7 @@ static const VMStateDescription vmstate_rtc = { VMSTATE_TIMER(periodic_timer, RTCState), VMSTATE_INT64(next_periodic_time, RTCState), VMSTATE_TIMER(update_timer, RTCState), + VMSTATE_TIMER(alarm_timer, RTCState), VMSTATE_UINT32_V(irq_coalesced, RTCState, 2), VMSTATE_UINT32_V(period, RTCState, 2), VMSTATE_END_OF_LIST() @@ -632,6 +818,7 @@ static int rtc_initfn(ISADevice *dev) 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->alarm_timer = qemu_new_timer_ns(rtc_clock, rtc_alarm_timer, s); s->clock_reset_notifier.notify = rtc_notify_clock_reset; qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier);