summary refs log tree commit diff stats
path: root/hw/mc146818rtc.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/mc146818rtc.c')
-rw-r--r--hw/mc146818rtc.c57
1 files changed, 57 insertions, 0 deletions
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index f57a9a6783..cd57bf3760 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -67,6 +67,10 @@ struct RTCState {
     int64_t next_periodic_time;
     /* second update */
     int64_t next_second_time;
+#ifdef TARGET_I386
+    uint32_t irq_coalesced;
+    uint32_t period;
+#endif
     QEMUTimer *second_timer;
     QEMUTimer *second_timer2;
 };
@@ -104,12 +108,20 @@ static void rtc_timer_update(RTCState *s, int64_t current_time)
             period_code += 7;
         /* period in 32 Khz cycles */
         period = 1 << (period_code - 1);
+#ifdef TARGET_I386
+        if(period != s->period)
+            s->irq_coalesced = (s->irq_coalesced * s->period) / period;
+        s->period = period;
+#endif
         /* compute 32 khz clock */
         cur_clock = muldiv64(current_time, 32768, ticks_per_sec);
         next_irq_clock = (cur_clock & ~(period - 1)) + period;
         s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1;
         qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
     } else {
+#ifdef TARGET_I386
+        s->irq_coalesced = 0;
+#endif
         qemu_del_timer(s->periodic_timer);
     }
 }
@@ -119,6 +131,12 @@ static void rtc_periodic_timer(void *opaque)
     RTCState *s = opaque;
 
     rtc_timer_update(s, s->next_periodic_time);
+#ifdef TARGET_I386
+    if ((s->cmos_data[RTC_REG_C] & 0xc0) && rtc_td_hack) {
+        s->irq_coalesced++;
+        return;
+    }
+#endif
     s->cmos_data[RTC_REG_C] |= 0xc0;
     rtc_irq_raise(s->irq);
 }
@@ -379,6 +397,15 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
         case RTC_REG_C:
             ret = s->cmos_data[s->cmos_index];
             qemu_irq_lower(s->irq);
+#ifdef TARGET_I386
+            if(s->irq_coalesced) {
+                apic_reset_irq_delivered();
+                qemu_irq_raise(s->irq);
+                if (apic_get_irq_delivered())
+                    s->irq_coalesced--;
+                break;
+            }
+#endif
             s->cmos_data[RTC_REG_C] = 0x00;
             break;
         default:
@@ -473,6 +500,28 @@ static int rtc_load(QEMUFile *f, void *opaque, int version_id)
     return 0;
 }
 
+#ifdef TARGET_I386
+static void rtc_save_td(QEMUFile *f, void *opaque)
+{
+    RTCState *s = opaque;
+
+    qemu_put_be32(f, s->irq_coalesced);
+    qemu_put_be32(f, s->period);
+}
+
+static int rtc_load_td(QEMUFile *f, void *opaque, int version_id)
+{
+    RTCState *s = opaque;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    s->irq_coalesced = qemu_get_be32(f);
+    s->period = qemu_get_be32(f);
+    return 0;
+}
+#endif
+
 RTCState *rtc_init(int base, qemu_irq irq)
 {
     RTCState *s;
@@ -503,6 +552,10 @@ RTCState *rtc_init(int base, qemu_irq irq)
     register_ioport_read(base, 2, 1, cmos_ioport_read, s);
 
     register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
+#ifdef TARGET_I386
+    if (rtc_td_hack)
+        register_savevm("mc146818rtc-td", base, 1, rtc_save_td, rtc_load_td, s);
+#endif
     return s;
 }
 
@@ -609,5 +662,9 @@ RTCState *rtc_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq)
     cpu_register_physical_memory(base, 2 << it_shift, io_memory);
 
     register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
+#ifdef TARGET_I386
+    if (rtc_td_hack)
+        register_savevm("mc146818rtc-td", base, 1, rtc_save_td, rtc_load_td, s);
+#endif
     return s;
 }