@@ -93,9 +93,14 @@ typedef struct RTCState {
qemu_irq irq;
qemu_irq sqw_irq;
int it_shift;
+
/* periodic timer */
QEMUTimer *periodic_timer;
int64_t next_periodic_time;
+
+ /* update-ended timer */
+ QEMUTimer *update_timer;
+
uint16_t irq_reinject_on_ack_count;
uint32_t irq_coalesced;
uint32_t period;
@@ -140,7 +145,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;
@@ -178,7 +184,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;
@@ -205,6 +211,40 @@ static void rtc_periodic_timer(void *opaque)
}
}
+/* handle update-ended timer */
+static void update_ended_timer_update(RTCState *s)
+{
+ struct timeval tv_now;
+ uint64_t next_update_time;
+ uint64_t expire_time;
+
+ if ((s->cmos_data[RTC_REG_B] & REG_B_UIE) &&
+ !(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ gettimeofday(&tv_now, NULL);
+ next_update_time = USEC_PER_SEC -
+ (tv_now.tv_usec + s->offset_usec) % 1000000;
+ expire_time = (get_ticks_per_sec() / USEC_PER_SEC) * next_update_time +
+ qemu_get_clock_ns(rtc_clock);
+ qemu_mod_timer(s->update_timer, expire_time);
+ } else {
+ qemu_del_timer(s->update_timer);
+ }
+}
+
+static void rtc_update_timer(void *opaque)
+{
+ RTCState *s = opaque;
+
+ update_ended_timer_update(s);
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ 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);
+ }
+ }
+}
+
static void rtc_set_offset(RTCState *s)
{
struct tm *tm = &s->current_tm;
@@ -256,7 +296,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
/* 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) {
@@ -279,7 +319,8 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
} else {
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));
+ update_ended_timer_update(s);
break;
case RTC_REG_C:
case RTC_REG_D:
@@ -493,6 +534,7 @@ 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_UINT32_V(irq_coalesced, RTCState, 2),
VMSTATE_UINT32_V(period, RTCState, 2),
VMSTATE_END_OF_LIST()
@@ -505,7 +547,7 @@ 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);
#ifdef TARGET_I386
if (s->lost_tick_policy == LOST_TICK_SLEW) {
rtc_coalesced_timer_update(s);
@@ -589,6 +631,7 @@ 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->clock_reset_notifier.notify = rtc_notify_clock_reset;
qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier);
Use timer to emulate RTC update-ended interrupt. The timer is enabled only when UIE is setting. Signed-off-by: Yang Zhang <yang.z.zhang@intel.com> --- hw/mc146818rtc.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 48 insertions(+), 5 deletions(-) -- 1.7.1