summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Crosthwaite <peter.crosthwaite@xilinx.com>2013-06-11 10:59:09 +1000
committerEdgar E. Iglesias <edgar.iglesias@gmail.com>2013-06-18 09:45:00 +0200
commit45fdd3bf5a00466cb0f762c638291a5446773dc9 (patch)
tree8b5dd0a7da6dad03b3ce24fe9659a00d96a0304e
parent6327c221fff955ee979559ec85c148963e06d78f (diff)
downloadqemu-45fdd3bf5a00466cb0f762c638291a5446773dc9.tar.gz
qemu-45fdd3bf5a00466cb0f762c638291a5446773dc9.tar.bz2
qemu-45fdd3bf5a00466cb0f762c638291a5446773dc9.zip
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>
-rw-r--r--hw/intc/xilinx_intc.c16
1 files changed, 15 insertions, 1 deletions
diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c
index d243a0015f..09b445320b 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);
}