diff options
author | Peter Crosthwaite <peter.crosthwaite@xilinx.com> | 2013-06-11 10:59:09 +1000 |
---|---|---|
committer | Edgar E. Iglesias <edgar.iglesias@gmail.com> | 2013-06-18 09:45:00 +0200 |
commit | 45fdd3bf5a00466cb0f762c638291a5446773dc9 (patch) | |
tree | 8b5dd0a7da6dad03b3ce24fe9659a00d96a0304e /hw/intc | |
parent | 6327c221fff955ee979559ec85c148963e06d78f (diff) | |
download | hqemu-45fdd3bf5a00466cb0f762c638291a5446773dc9.zip hqemu-45fdd3bf5a00466cb0f762c638291a5446773dc9.tar.gz |
intc/xilinx_intc: Handle level interrupt retriggering
Acking a level sensitive interrupt should have no effect if the
interrupt pin is still asserted. The current implementation requires
and edge condition to occur for setting a level sensitive IRQ, which
means an ACK can clear a level sensitive interrupt, until the original
source strobes the interrupt again.
Fix by keeping track of the interrupt pin state and setting ISR based
on this every time update_irq() is called.
Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
Diffstat (limited to 'hw/intc')
-rw-r--r-- | hw/intc/xilinx_intc.c | 16 |
1 files changed, 15 insertions, 1 deletions
diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c index d243a00..09b4453 100644 --- a/hw/intc/xilinx_intc.c +++ b/hw/intc/xilinx_intc.c @@ -49,11 +49,19 @@ struct xlx_pic /* Runtime control registers. */ uint32_t regs[R_MAX]; + /* state of the interrupt input pins */ + uint32_t irq_pin_state; }; static void update_irq(struct xlx_pic *p) { uint32_t i; + + /* level triggered interrupt */ + if (p->regs[R_MER] & 2) { + p->regs[R_ISR] |= p->irq_pin_state & ~p->c_kind_of_intr; + } + /* Update the pending register. */ p->regs[R_IPR] = p->regs[R_ISR] & p->regs[R_IER]; @@ -135,7 +143,13 @@ static void irq_handler(void *opaque, int irq, int level) return; } - p->regs[R_ISR] |= (level << irq); + /* edge triggered interrupt */ + if (p->c_kind_of_intr & (1 << irq) && p->regs[R_MER] & 2) { + p->regs[R_ISR] |= (level << irq); + } + + p->irq_pin_state &= ~(1 << irq); + p->irq_pin_state |= level << irq; update_irq(p); } |