diff options
author | Jan Kiszka <jan.kiszka@siemens.com> | 2010-06-13 14:15:42 +0200 |
---|---|---|
committer | Blue Swirl <blauwirbel@gmail.com> | 2010-06-13 15:33:00 +0300 |
commit | 22a9fe38a37efc2feb67fac533309ea219242cb5 (patch) | |
tree | 399fc2c806f727dda19547df2d3f9db54dfce7af | |
parent | b7eaa6c77c67c2f2e821a51c6b2758aa482b2d71 (diff) |
hpet: Add support for level-triggered interrupts
By implementing this feature we can also remove a nasty way to kill qemu
(by trying to enable level-triggered hpet interrupts).
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
-rw-r--r-- | hw/hpet.c | 32 |
1 files changed, 22 insertions, 10 deletions
@@ -159,8 +159,10 @@ static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current) } } -static void update_irq(struct HPETTimer *timer) +static void update_irq(struct HPETTimer *timer, int set) { + uint64_t mask; + HPETState *s; int route; if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) { @@ -172,10 +174,18 @@ static void update_irq(struct HPETTimer *timer) } else { route = timer_int_route(timer); } - if (!timer_enabled(timer) || !hpet_enabled(timer->state)) { - return; + s = timer->state; + mask = 1 << timer->tn; + if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) { + s->isr &= ~mask; + qemu_irq_lower(s->irqs[route]); + } else if (timer->config & HPET_TN_TYPE_LEVEL) { + s->isr |= mask; + qemu_irq_raise(s->irqs[route]); + } else { + s->isr &= ~mask; + qemu_irq_pulse(s->irqs[route]); } - qemu_irq_pulse(timer->state->irqs[route]); } static void hpet_pre_save(void *opaque) @@ -261,7 +271,7 @@ static void hpet_timer(void *opaque) t->wrap_flag = 0; } } - update_irq(t); + update_irq(t, 1); } static void hpet_set_timer(HPETTimer *t) @@ -291,6 +301,7 @@ static void hpet_set_timer(HPETTimer *t) static void hpet_del_timer(HPETTimer *t) { qemu_del_timer(t->qemu_timer); + update_irq(t, 0); } #ifdef HPET_DEBUG @@ -423,10 +434,6 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, timer->cmp = (uint32_t)timer->cmp; timer->period = (uint32_t)timer->period; } - if (new_val & HPET_TN_TYPE_LEVEL) { - printf("qemu: level-triggered hpet not supported\n"); - exit (-1); - } if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) { hpet_set_timer(timer); } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) { @@ -522,7 +529,12 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, DPRINTF("qemu: invalid HPET_CFG+4 write \n"); break; case HPET_STATUS: - /* FIXME: need to handle level-triggered interrupts */ + val = new_val & s->isr; + for (i = 0; i < HPET_NUM_TIMERS; i++) { + if (val & (1 << i)) { + update_irq(&s->timer[i], 0); + } + } break; case HPET_COUNTER: if (hpet_enabled(s)) { |