summaryrefslogtreecommitdiff
path: root/hw/arm_gic.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2012-04-13 11:39:07 +0000
committerPeter Maydell <peter.maydell@linaro.org>2012-04-13 11:39:07 +0000
commit544d1afa7013fce155f5afbbc24737f2fc0c0f26 (patch)
tree28750d0864852d6bd7023f5f36586a1bf6581188 /hw/arm_gic.c
parent926c4aff6ecf2b26b3508773196314a774bf5c4c (diff)
hw/arm_gic: Expose PPI inputs as gpio inputs
Expose the Private Peripheral Interrupt inputs as GPIO inputs. The layout of the GPIO array is thus: [0..N-1] SPIs [N..N+31] PPIs for CPU 0 [N+32..N+63] PPIs for CPU 1 ... Treating PPIs as being another kind of input line is in line with the GIC architecture specification, where they are clearly described that way. The 11MPCore TRM is a bit more ambiguous, but there is no practical difference between "set PPI X as pending" and "0->1 transition on a PPI input line configured as edge triggered", and PPIs are always edge triggered, so this change won't affect behaviour. Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/arm_gic.c')
-rw-r--r--hw/arm_gic.c49
1 files changed, 40 insertions, 9 deletions
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
index df1a34b8df..fabbcc5d63 100644
--- a/hw/arm_gic.c
+++ b/hw/arm_gic.c
@@ -192,20 +192,40 @@ gic_set_pending_private(gic_state *s, int cpu, int irq)
/* Process a change in an external IRQ input. */
static void gic_set_irq(void *opaque, int irq, int level)
{
+ /* Meaning of the 'irq' parameter:
+ * [0..N-1] : external interrupts
+ * [N..N+31] : PPI (internal) interrupts for CPU 0
+ * [N+32..N+63] : PPI (internal interrupts for CPU 1
+ * ...
+ */
gic_state *s = (gic_state *)opaque;
- /* The first external input line is internal interrupt 32. */
- irq += GIC_INTERNAL;
- if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
+ int cm, target;
+ if (irq < (s->num_irq - GIC_INTERNAL)) {
+ /* The first external input line is internal interrupt 32. */
+ cm = ALL_CPU_MASK;
+ irq += GIC_INTERNAL;
+ target = GIC_TARGET(irq);
+ } else {
+ int cpu;
+ irq -= (s->num_irq - GIC_INTERNAL);
+ cpu = irq / GIC_INTERNAL;
+ irq %= GIC_INTERNAL;
+ cm = 1 << cpu;
+ target = cm;
+ }
+
+ if (level == GIC_TEST_LEVEL(irq, cm)) {
return;
+ }
if (level) {
- GIC_SET_LEVEL(irq, ALL_CPU_MASK);
- if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq, ALL_CPU_MASK)) {
- DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
- GIC_SET_PENDING(irq, GIC_TARGET(irq));
+ GIC_SET_LEVEL(irq, cm);
+ if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
+ DPRINTF("Set %d pending mask %x\n", irq, target);
+ GIC_SET_PENDING(irq, target);
}
} else {
- GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
+ GIC_CLEAR_LEVEL(irq, cm);
}
gic_update(s);
}
@@ -849,7 +869,18 @@ static void gic_init(gic_state *s, int num_irq)
num_irq);
}
- qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL);
+ i = s->num_irq - GIC_INTERNAL;
+#ifndef NVIC
+ /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
+ * GPIO array layout is thus:
+ * [0..N-1] SPIs
+ * [N..N+31] PPIs for CPU 0
+ * [N+32..N+63] PPIs for CPU 1
+ * ...
+ */
+ i += (GIC_INTERNAL * num_cpu);
+#endif
+ qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, i);
for (i = 0; i < NUM_CPU(s); i++) {
sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
}