From 7d932dfdc5ebc07a7bfed9c01e587c9c7e9b6e26 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 13 Jun 2010 14:15:40 +0200 Subject: hpet/rtc: Rework RTC IRQ replacement by HPET Allow the intercept the RTC IRQ for the HPET legacy mode. Then push routing to IRQ8 completely into the HPET. This allows to turn hpet_in_legacy_mode() into a private function. Furthermore, this stops the RTC from clearing IRQ8 even if the HPET is in control. This patch comes with a side effect: The RTC timers will no longer be stoppend when there is no IRQ consumer, possibly causing a minor performance degration. But as the guest may want to redirect the RTC to the SCI in that mode, it should normally disable unused IRQ source anyway. Signed-off-by: Jan Kiszka Signed-off-by: Blue Swirl --- hw/hpet.c | 28 +++++++++++++++++++++------- hw/hpet_emul.h | 4 ---- hw/mc146818rtc.c | 49 +++++++++++++++---------------------------------- hw/mc146818rtc.h | 4 +++- hw/mips_jazz.c | 2 +- hw/mips_malta.c | 2 +- hw/mips_r4k.c | 2 +- hw/pc.c | 14 ++++++++------ hw/ppc_prep.c | 2 +- 9 files changed, 51 insertions(+), 56 deletions(-) (limited to 'hw') diff --git a/hw/hpet.c b/hw/hpet.c index 041dd84..edfe02e 100644 --- a/hw/hpet.c +++ b/hw/hpet.c @@ -30,6 +30,7 @@ #include "qemu-timer.h" #include "hpet_emul.h" #include "sysbus.h" +#include "mc146818rtc.h" //#define HPET_DEBUG #ifdef HPET_DEBUG @@ -58,6 +59,7 @@ typedef struct HPETState { SysBusDevice busdev; uint64_t hpet_offset; qemu_irq irqs[HPET_NUM_IRQ_ROUTES]; + uint8_t rtc_irq_level; HPETTimer timer[HPET_NUM_TIMERS]; /* Memory-mapped, software visible registers */ @@ -69,12 +71,9 @@ typedef struct HPETState { static HPETState *hpet_statep; -uint32_t hpet_in_legacy_mode(void) +static uint32_t hpet_in_legacy_mode(HPETState *s) { - if (!hpet_statep) { - return 0; - } - return hpet_statep->config & HPET_CFG_LEGACY; + return s->config & HPET_CFG_LEGACY; } static uint32_t timer_int_route(struct HPETTimer *timer) @@ -166,12 +165,12 @@ static void update_irq(struct HPETTimer *timer) { int route; - if (timer->tn <= 1 && hpet_in_legacy_mode()) { + if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) { /* if LegacyReplacementRoute bit is set, HPET specification requires * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. */ - route = (timer->tn == 0) ? 0 : 8; + route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ; } else { route = timer_int_route(timer); } @@ -515,8 +514,10 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, /* i8254 and RTC are disabled when HPET is in legacy mode */ if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { hpet_pit_disable(); + qemu_irq_lower(s->irqs[RTC_ISA_IRQ]); } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { hpet_pit_enable(); + qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level); } break; case HPET_CFG + 4: @@ -607,6 +608,16 @@ static void hpet_reset(DeviceState *d) count = 1; } +static void hpet_handle_rtc_irq(void *opaque, int n, int level) +{ + HPETState *s = FROM_SYSBUS(HPETState, opaque); + + s->rtc_irq_level = level; + if (!hpet_in_legacy_mode(s)) { + qemu_set_irq(s->irqs[RTC_ISA_IRQ], level); + } +} + static int hpet_init(SysBusDevice *dev) { HPETState *s = FROM_SYSBUS(HPETState, dev); @@ -625,6 +636,9 @@ static int hpet_init(SysBusDevice *dev) timer->state = s; } + isa_reserve_irq(RTC_ISA_IRQ); + qdev_init_gpio_in(&dev->qdev, hpet_handle_rtc_irq, 1); + /* HPET Area */ iomemtype = cpu_register_io_memory(hpet_ram_read, hpet_ram_write, s); diff --git a/hw/hpet_emul.h b/hw/hpet_emul.h index 785f850..9c268cc 100644 --- a/hw/hpet_emul.h +++ b/hw/hpet_emul.h @@ -47,8 +47,4 @@ #define HPET_TN_INT_ROUTE_CAP_SHIFT 32 #define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U -#if defined TARGET_I386 -extern uint32_t hpet_in_legacy_mode(void); -#endif - #endif diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index c3e6a70..c3459bf 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -27,7 +27,6 @@ #include "pc.h" #include "apic.h" #include "isa.h" -#include "hpet_emul.h" #include "mc146818rtc.h" //#define DEBUG_CMOS @@ -101,19 +100,6 @@ typedef struct RTCState { QEMUTimer *second_timer2; } RTCState; -static void rtc_irq_raise(qemu_irq irq) -{ - /* When HPET is operating in legacy mode, RTC interrupts are disabled - * We block qemu_irq_raise, but not qemu_irq_lower, in case legacy - * mode is established while interrupt is raised. We want it to - * be lowered in any case - */ -#if defined TARGET_I386 - if (!hpet_in_legacy_mode()) -#endif - qemu_irq_raise(irq); -} - static void rtc_set_time(RTCState *s); static void rtc_copy_date(RTCState *s); @@ -139,7 +125,7 @@ static void rtc_coalesced_timer(void *opaque) apic_reset_irq_delivered(); s->cmos_data[RTC_REG_C] |= 0xc0; DPRINTF_C("cmos: injecting from timer\n"); - rtc_irq_raise(s->irq); + qemu_irq_raise(s->irq); if (apic_get_irq_delivered()) { s->irq_coalesced--; DPRINTF_C("cmos: coalesced irqs decreased to %d\n", @@ -155,19 +141,10 @@ static void rtc_timer_update(RTCState *s, int64_t current_time) { int period_code, period; int64_t cur_clock, next_irq_clock; - int enable_pie; period_code = s->cmos_data[RTC_REG_A] & 0x0f; -#if defined TARGET_I386 - /* disable periodic timer if hpet is in legacy mode, since interrupts are - * disabled anyway. - */ - enable_pie = !hpet_in_legacy_mode(); -#else - enable_pie = 1; -#endif if (period_code != 0 - && (((s->cmos_data[RTC_REG_B] & REG_B_PIE) && enable_pie) + && ((s->cmos_data[RTC_REG_B] & REG_B_PIE) || ((s->cmos_data[RTC_REG_B] & REG_B_SQWE) && s->sqw_irq))) { if (period_code <= 2) period_code += 7; @@ -206,7 +183,7 @@ static void rtc_periodic_timer(void *opaque) if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT) s->irq_reinject_on_ack_count = 0; apic_reset_irq_delivered(); - rtc_irq_raise(s->irq); + qemu_irq_raise(s->irq); if (!apic_get_irq_delivered()) { s->irq_coalesced++; rtc_coalesced_timer_update(s); @@ -215,7 +192,7 @@ static void rtc_periodic_timer(void *opaque) } } else #endif - rtc_irq_raise(s->irq); + qemu_irq_raise(s->irq); } if (s->cmos_data[RTC_REG_B] & REG_B_SQWE) { /* Not square wave at all but we don't want 2048Hz interrupts! @@ -444,15 +421,15 @@ static void rtc_update_second2(void *opaque) s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) { s->cmos_data[RTC_REG_C] |= 0xa0; - rtc_irq_raise(s->irq); + qemu_irq_raise(s->irq); } } /* 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; - rtc_irq_raise(s->irq); + s->cmos_data[RTC_REG_C] |= REG_C_IRQF; + qemu_irq_raise(s->irq); } /* clear update in progress bit */ @@ -606,9 +583,6 @@ static int rtc_initfn(ISADevice *dev) { RTCState *s = DO_UPCAST(RTCState, dev, dev); int base = 0x70; - int isairq = 8; - - isa_init_irq(dev, &s->irq, isairq); s->cmos_data[RTC_REG_A] = 0x26; s->cmos_data[RTC_REG_B] = 0x02; @@ -638,13 +612,20 @@ static int rtc_initfn(ISADevice *dev) return 0; } -ISADevice *rtc_init(int base_year) +ISADevice *rtc_init(int base_year, qemu_irq intercept_irq) { ISADevice *dev; + RTCState *s; dev = isa_create("mc146818rtc"); + s = DO_UPCAST(RTCState, dev, dev); qdev_prop_set_int32(&dev->qdev, "base_year", base_year); qdev_init_nofail(&dev->qdev); + if (intercept_irq) { + s->irq = intercept_irq; + } else { + isa_init_irq(dev, &s->irq, RTC_ISA_IRQ); + } return dev; } diff --git a/hw/mc146818rtc.h b/hw/mc146818rtc.h index 6f46a68..575968c 100644 --- a/hw/mc146818rtc.h +++ b/hw/mc146818rtc.h @@ -3,7 +3,9 @@ #include "isa.h" -ISADevice *rtc_init(int base_year); +#define RTC_ISA_IRQ 8 + +ISADevice *rtc_init(int base_year, qemu_irq intercept_irq); void rtc_set_memory(ISADevice *dev, int addr, int val); void rtc_set_date(ISADevice *dev, const struct tm *tm); diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c index ead3a00..22db7a2 100644 --- a/hw/mips_jazz.c +++ b/hw/mips_jazz.c @@ -259,7 +259,7 @@ void mips_jazz_init (ram_addr_t ram_size, fdctrl_init_sysbus(rc4030[1], 0, 0x80003000, fds); /* Real time clock */ - rtc_init(1980); + rtc_init(1980, NULL); s_rtc = cpu_register_io_memory(rtc_read, rtc_write, NULL); cpu_register_physical_memory(0x80004000, 0x00001000, s_rtc); diff --git a/hw/mips_malta.c b/hw/mips_malta.c index a8f9d15..23de7f0 100644 --- a/hw/mips_malta.c +++ b/hw/mips_malta.c @@ -959,7 +959,7 @@ void mips_malta_init (ram_addr_t ram_size, /* Super I/O */ isa_dev = isa_create_simple("i8042"); - rtc_state = rtc_init(2000); + rtc_state = rtc_init(2000, NULL); serial_isa_init(0, serial_hds[0]); serial_isa_init(1, serial_hds[1]); if (parallel_hds[0]) diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c index f1fcfcd..5a96dea 100644 --- a/hw/mips_r4k.c +++ b/hw/mips_r4k.c @@ -267,7 +267,7 @@ void mips_r4k_init (ram_addr_t ram_size, isa_bus_new(NULL); isa_bus_irqs(i8259); - rtc_state = rtc_init(2000); + rtc_state = rtc_init(2000, NULL); /* Register 64 KB of ISA IO space at 0x14000000 */ #ifdef TARGET_WORDS_BIGENDIAN diff --git a/hw/pc.c b/hw/pc.c index ae31e2e..1491129 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -943,6 +943,7 @@ void pc_basic_device_init(qemu_irq *isa_irq, int i; DriveInfo *fd[MAX_FD]; PITState *pit; + qemu_irq rtc_irq = NULL; qemu_irq *a20_line; ISADevice *i8042; qemu_irq *cpu_exit_irq; @@ -951,19 +952,20 @@ void pc_basic_device_init(qemu_irq *isa_irq, register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL); - *rtc_state = rtc_init(2000); - - qemu_register_boot_set(pc_boot_set, *rtc_state); - - pit = pit_init(0x40, isa_reserve_irq(0)); - pcspk_init(pit); if (!no_hpet) { DeviceState *hpet = sysbus_create_simple("hpet", HPET_BASE, NULL); for (i = 0; i < 24; i++) { sysbus_connect_irq(sysbus_from_qdev(hpet), i, isa_irq[i]); } + rtc_irq = qdev_get_gpio_in(hpet, 0); } + *rtc_state = rtc_init(2000, rtc_irq); + + qemu_register_boot_set(pc_boot_set, *rtc_state); + + pit = pit_init(0x40, isa_reserve_irq(0)); + pcspk_init(pit); for(i = 0; i < MAX_SERIAL_PORTS; i++) { if (serial_hds[i]) { diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index 16c9950..bb9e15f 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -696,7 +696,7 @@ static void ppc_prep_init (ram_addr_t ram_size, pci_vga_init(pci_bus, 0, 0); // openpic = openpic_init(0x00000000, 0xF0000000, 1); // pit = pit_init(0x40, i8259[0]); - rtc_init(2000); + rtc_init(2000, NULL); if (serial_hds[0]) serial_isa_init(0, serial_hds[0]); -- cgit v1.1