diff options
author | Avi Kivity <avi@redhat.com> | 2012-02-29 11:26:57 +0200 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2012-02-29 11:26:57 +0200 |
commit | fb83307d04dda3d84b032c05fad6c417d0041fca (patch) | |
tree | d9408eaf4bcbb2174fc83d8726ddae6d0b810cf6 | |
parent | f91d8a2904e491373429d107c2c6d69ab3a5c0a7 (diff) | |
parent | ce967e2f33861b0e17753f97fa4527b5943c94b6 (diff) |
Merge commit 'ce967e2f33861b0e17753f97fa4527b5943c94b6' into upstream-mergeupstream-merge
* commit 'ce967e2f33861b0e17753f97fa4527b5943c94b6':
i8254: Rework & fix interaction with HPET in legacy mode
Conflicts:
hw/i8254.c
Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r-- | hw/hpet.c | 42 | ||||
-rw-r--r-- | hw/hpet_emul.h | 3 | ||||
-rw-r--r-- | hw/i8254-kvm.c | 4 | ||||
-rw-r--r-- | hw/i8254.c | 75 | ||||
-rw-r--r-- | hw/i8254.h | 5 | ||||
-rw-r--r-- | hw/pc.c | 15 |
6 files changed, 61 insertions, 83 deletions
@@ -65,6 +65,7 @@ typedef struct HPETState { qemu_irq irqs[HPET_NUM_IRQ_ROUTES]; uint32_t flags; uint8_t rtc_irq_level; + qemu_irq pit_enabled; uint8_t num_timers; HPETTimer timer[HPET_MAX_TIMERS]; @@ -239,10 +240,6 @@ static int hpet_post_load(void *opaque, int version_id) s->flags |= 1 << HPET_MSI_SUPPORT; } - if (hpet_in_legacy_mode(s)) { - hpet_pit_disable(); - } - return 0; } @@ -578,12 +575,15 @@ static void hpet_ram_write(void *opaque, target_phys_addr_t addr, hpet_del_timer(&s->timer[i]); } } - /* i8254 and RTC are disabled when HPET is in legacy mode */ + /* i8254 and RTC output pins are disabled + * when HPET is in legacy mode */ if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - hpet_pit_disable(); + qemu_set_irq(s->pit_enabled, 0); + qemu_irq_lower(s->irqs[0]); qemu_irq_lower(s->irqs[RTC_ISA_IRQ]); } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - hpet_pit_enable(); + qemu_irq_lower(s->irqs[0]); + qemu_set_irq(s->pit_enabled, 1); qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level); } break; @@ -637,7 +637,6 @@ static void hpet_reset(DeviceState *d) { HPETState *s = FROM_SYSBUS(HPETState, sysbus_from_qdev(d)); int i; - static int count = 0; for (i = 0; i < s->num_timers; i++) { HPETTimer *timer = &s->timer[i]; @@ -654,32 +653,30 @@ static void hpet_reset(DeviceState *d) timer->wrap_flag = 0; } + qemu_set_irq(s->pit_enabled, 1); s->hpet_counter = 0ULL; s->hpet_offset = 0ULL; s->config = 0ULL; - if (count > 0) { - /* we don't enable pit when hpet_reset is first called (by hpet_init) - * because hpet is taking over for pit here. On subsequent invocations, - * hpet_reset is called due to system reset. At this point control must - * be returned to pit until SW reenables hpet. - */ - hpet_pit_enable(); - } hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; hpet_cfg.hpet[s->hpet_id].address = sysbus_from_qdev(d)->mmio[0].addr; - count = 1; /* to document that the RTC lowers its output on reset as well */ s->rtc_irq_level = 0; } -static void hpet_handle_rtc_irq(void *opaque, int n, int level) +static void hpet_handle_legacy_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); + if (n == HPET_LEGACY_PIT_INT) { + if (!hpet_in_legacy_mode(s)) { + qemu_set_irq(s->irqs[0], level); + } + } else { + s->rtc_irq_level = level; + if (!hpet_in_legacy_mode(s)) { + qemu_set_irq(s->irqs[RTC_ISA_IRQ], level); + } } } @@ -722,7 +719,8 @@ static int hpet_init(SysBusDevice *dev) s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; s->capability |= ((HPET_CLK_PERIOD) << 32); - qdev_init_gpio_in(&dev->qdev, hpet_handle_rtc_irq, 1); + qdev_init_gpio_in(&dev->qdev, hpet_handle_legacy_irq, 2); + qdev_init_gpio_out(&dev->qdev, &s->pit_enabled, 1); /* HPET Area */ memory_region_init_io(&s->iomem, &hpet_ram_ops, s, "hpet", 0x400); diff --git a/hw/hpet_emul.h b/hw/hpet_emul.h index 612870253..757f79fdd 100644 --- a/hw/hpet_emul.h +++ b/hw/hpet_emul.h @@ -22,6 +22,9 @@ #define HPET_NUM_IRQ_ROUTES 32 +#define HPET_LEGACY_PIT_INT 0 +#define HPET_LEGACY_RTC_INT 1 + #define HPET_CFG_ENABLE 0x001 #define HPET_CFG_LEGACY 0x002 diff --git a/hw/i8254-kvm.c b/hw/i8254-kvm.c index 8b494d0d9..ba3b5c9af 100644 --- a/hw/i8254-kvm.c +++ b/hw/i8254-kvm.c @@ -40,7 +40,7 @@ static void kvm_pit_pre_save(void *opaque) if (kvm_has_pit_state2()) { kvm_get_pit2(kvm_state, &pit2); - s->flags = pit2.flags; + s->channels[0].irq_disabled = pit2.flags; } else { /* pit2 is superset of pit struct so just cast it and use it */ kvm_get_pit(kvm_state, (struct kvm_pit_state *)&pit2); @@ -72,7 +72,7 @@ static int kvm_pit_post_load(void *opaque, int version_id) struct PITChannelState *sc; int i; - pit2.flags = s->flags; + pit2.flags = s->channels[0].irq_disabled; for (i = 0; i < 3; i++) { c = &pit2.channels[i]; sc = &s->channels[i]; diff --git a/hw/i8254.c b/hw/i8254.c index 430077baa..3688ab8ad 100644 --- a/hw/i8254.c +++ b/hw/i8254.c @@ -30,8 +30,6 @@ //#define DEBUG_PIT -static PITState pit_state; - static void pit_irq_timer_update(PITChannelState *s, int64_t current_time); static int pit_get_count(PITChannelState *s) @@ -213,7 +211,7 @@ static inline void pit_load_count(PITState *s, int val, int chan) s->channels[chan].count_load_time = qemu_get_clock_ns(vm_clock); s->channels[chan].count = val; #ifdef TARGET_I386 - if (chan == 0 && pit_state.flags & PIT_FLAGS_HPET_LEGACY) { + if (chan == 0 && s->channels[0].irq_disabled) { return; } #endif @@ -352,8 +350,9 @@ static void pit_irq_timer_update(PITChannelState *s, int64_t current_time) int64_t expire_time; int irq_level; - if (!s->irq_timer) + if (!s->irq_timer || s->irq_disabled) { return; + } expire_time = pit_get_next_transition_time(s, current_time); irq_level = pit_get_out1(s, current_time); qemu_set_irq(s->irq, irq_level); @@ -409,7 +408,7 @@ static int pit_load_old(QEMUFile *f, void *opaque, int version_id) if (version_id != PIT_SAVEVM_VERSION) return -EINVAL; - pit->flags = qemu_get_be32(f); + (void)qemu_get_be32(f); for(i = 0; i < 3; i++) { s = &pit->channels[i]; s->count=qemu_get_be32(f); @@ -425,6 +424,7 @@ static int pit_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_8s(f, &s->bcd); qemu_get_8s(f, &s->gate); s->count_load_time=qemu_get_be64(f); + s->irq_disabled = 0; if (s->irq_timer) { s->next_transition_time=qemu_get_be64(f); qemu_get_timer(f, s->irq_timer); @@ -436,12 +436,12 @@ static int pit_load_old(QEMUFile *f, void *opaque, int version_id) VMStateDescription vmstate_pit = { .name = "i8254", - .version_id = 2, + .version_id = 3, .minimum_version_id = 2, .minimum_version_id_old = 1, .load_state_old = pit_load_old, .fields = (VMStateField []) { - VMSTATE_UINT32(flags, PITState), + VMSTATE_UINT32_V(channels[0].irq_disabled, PITState, 3), VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState), VMSTATE_TIMER(channels[0].irq_timer, PITState), VMSTATE_END_OF_LIST() @@ -455,7 +455,7 @@ static void pit_reset(DeviceState *dev) int i; #ifdef TARGET_I386 - pit->flags &= ~PIT_FLAGS_HPET_LEGACY; + pit->channels[0].irq_disabled = 0; #endif for(i = 0;i < 3; i++) { s = &pit->channels[i]; @@ -463,7 +463,7 @@ static void pit_reset(DeviceState *dev) s->gate = (i != 2); s->count_load_time = qemu_get_clock_ns(vm_clock); s->count = 0x10000; - if (i == 0) { + if (i == 0 && !s->irq_disabled) { s->next_transition_time = pit_get_next_transition_time(s, s->count_load_time); qemu_mod_timer(s->irq_timer, s->next_transition_time); @@ -474,50 +474,21 @@ static void pit_reset(DeviceState *dev) } } -#ifdef TARGET_I386 -/* When HPET is operating in legacy mode, i8254 timer0 is disabled */ - -void hpet_pit_disable(void) -{ - PITChannelState *s = &pit_state.channels[0]; - - if (kvm_enabled() && kvm_irqchip_in_kernel()) { - if (kvm_has_pit_state2()) { - kvm_hpet_disable_kpit(); - } else { - fprintf(stderr, "%s: kvm does not support pit_state2!\n", __FUNCTION__); - exit(1); - } - } else { - pit_state.flags |= PIT_FLAGS_HPET_LEGACY; - if (s->irq_timer) { - qemu_del_timer(s->irq_timer); - } - } -} - -/* When HPET is reset or leaving legacy mode, it must reenable i8254 - * timer 0 - */ - -void hpet_pit_enable(void) +/* When HPET is operating in legacy mode, suppress the ignored timer IRQ, + * reenable it when legacy mode is left again. */ +static void pit_irq_control(void *opaque, int n, int enable) { - PITState *pit = &pit_state; + PITState *pit = opaque; PITChannelState *s = &pit->channels[0]; - if (kvm_enabled() && kvm_irqchip_in_kernel()) { - if (kvm_has_pit_state2()) { - kvm_hpet_enable_kpit(); - } else { - fprintf(stderr, "%s: kvm does not support pit_state2!\n", __FUNCTION__); - exit(1); - } + if (enable) { + s->irq_disabled = 0; + pit_irq_timer_update(s, qemu_get_clock_ns(vm_clock)); } else { - pit_state.flags &= ~PIT_FLAGS_HPET_LEGACY; - pit_load_count(pit, s->count, 0); + s->irq_disabled = 1; + qemu_del_timer(s->irq_timer); } } -#endif static const MemoryRegionPortio pit_portio[] = { { 0, 4, 1, .write = pit_ioport_write }, @@ -535,9 +506,10 @@ static int pit_initfn(ISADevice *dev) PITChannelState *s; #ifdef CONFIG_KVM_PIT - if (kvm_enabled() && kvm_irqchip_in_kernel()) + if (kvm_enabled() && kvm_irqchip_in_kernel()) { kvm_pit_init(pit); - else { + return 0; + } #endif s = &pit->channels[0]; @@ -548,9 +520,8 @@ static int pit_initfn(ISADevice *dev) memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4); isa_register_ioport(dev, &pit->ioports, pit->iobase); -#ifdef CONFIG_KVM_PIT - } -#endif + qdev_init_gpio_in(&dev->qdev, pit_irq_control, 1); + qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2); return 0; diff --git a/hw/i8254.h b/hw/i8254.h index 3b114bc56..51b2d3e22 100644 --- a/hw/i8254.h +++ b/hw/i8254.h @@ -53,6 +53,7 @@ typedef struct PITChannelState { int64_t next_transition_time; QEMUTimer *irq_timer; qemu_irq irq; + uint32_t irq_disabled; } PITChannelState; struct PITState { @@ -60,7 +61,6 @@ struct PITState { MemoryRegion ioports; uint32_t iobase; PITChannelState channels[3]; - uint32_t flags; }; void pit_save(QEMUFile *f, void *opaque); @@ -94,7 +94,4 @@ int pit_get_initial_count(ISADevice *dev, int channel); int pit_get_mode(ISADevice *dev, int channel); int pit_get_out(ISADevice *dev, int channel, int64_t current_time); -void hpet_pit_disable(void); -void hpet_pit_enable(void); - #endif /* !HW_I8254_H */ @@ -1151,6 +1151,9 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, { int i; DriveInfo *fd[MAX_FD]; + DeviceState *hpet = NULL; + int pit_isa_irq = 0; + qemu_irq pit_alt_irq = NULL; qemu_irq rtc_irq = NULL; qemu_irq *a20_line; ISADevice *i8042, *port92, *vmmouse, *pit; @@ -1161,20 +1164,26 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL); if (!no_hpet) { - DeviceState *hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL); + hpet = sysbus_try_create_simple("hpet", HPET_BASE, NULL); if (hpet) { for (i = 0; i < GSI_NUM_PINS; i++) { sysbus_connect_irq(sysbus_from_qdev(hpet), i, gsi[i]); } - rtc_irq = qdev_get_gpio_in(hpet, 0); + pit_isa_irq = -1; + pit_alt_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_PIT_INT); + rtc_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_RTC_INT); } } *rtc_state = rtc_init(isa_bus, 2000, rtc_irq); qemu_register_boot_set(pc_boot_set, *rtc_state); - pit = pit_init(isa_bus, 0x40, 0, NULL); + pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq); + if (hpet) { + /* connect PIT to output control line of the HPET */ + qdev_connect_gpio_out(hpet, 0, qdev_get_gpio_in(&pit->qdev, 0)); + } pcspk_init(pit); for(i = 0; i < MAX_SERIAL_PORTS; i++) { |