* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
- * This code is licenced under the GPL.
+ * This code is licensed under the GPL.
*/
/* This file contains implementation code for the RealView EB interrupt
typedef struct gic_irq_state
{
- /* ??? The documentation seems to imply the enable bits are global, even
- for per-cpu interrupts. This seems strange. */
- unsigned enabled:1;
+ /* The enable bits are only banked for per-cpu interrupts. */
+ unsigned enabled:NCPU;
unsigned pending:NCPU;
unsigned active:NCPU;
unsigned level:NCPU;
#define NUM_CPU(s) 1
#endif
-#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
-#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
-#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
+#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm)
+#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm)
+#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0)
#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
int num_cpu;
#endif
- int iomemtype;
+ MemoryRegion iomem;
} gic_state;
/* TODO: Many places that call this routine could be optimized. */
best_prio = 0x100;
best_irq = 1023;
for (irq = 0; irq < GIC_NIRQ; irq++) {
- if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) {
+ if (GIC_TEST_ENABLED(irq, cm) && GIC_TEST_PENDING(irq, cm)) {
if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
best_prio = GIC_GET_PRIORITY(irq, cpu);
best_irq = irq;
if (level) {
GIC_SET_LEVEL(irq, ALL_CPU_MASK);
- if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
+ 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));
}
int update = 0;
int cm = 1 << cpu;
DPRINTF("EOI %d\n", irq);
+ if (irq >= GIC_NIRQ) {
+ /* This handles two cases:
+ * 1. If software writes the ID of a spurious interrupt [ie 1023]
+ * to the GICC_EOIR, the GIC ignores that write.
+ * 2. If software writes the number of a non-existent interrupt
+ * this must be a subcase of "value written does not match the last
+ * valid interrupt value read from the Interrupt Acknowledge
+ * register" and so this is UNPREDICTABLE. We choose to ignore it.
+ */
+ return;
+ }
if (s->running_irq[cpu] == 1023)
return; /* No active IRQ. */
- if (irq != 1023) {
- /* Mark level triggered interrupts as pending if they are still
- raised. */
- if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
- && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
- DPRINTF("Set %d pending mask %x\n", irq, cm);
- GIC_SET_PENDING(irq, cm);
- update = 1;
- }
+ /* Mark level triggered interrupts as pending if they are still
+ raised. */
+ if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
+ && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
+ DPRINTF("Set %d pending mask %x\n", irq, cm);
+ GIC_SET_PENDING(irq, cm);
+ update = 1;
}
if (irq != s->running_irq[cpu]) {
/* Complete an IRQ that is not currently running. */
goto bad_reg;
res = 0;
for (i = 0; i < 8; i++) {
- if (GIC_TEST_ENABLED(irq + i)) {
+ if (GIC_TEST_ENABLED(irq + i, cm)) {
res |= (1 << i);
}
}
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
- if (!GIC_TEST_ENABLED(irq + i))
+ int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK;
+
+ if (!GIC_TEST_ENABLED(irq + i, cm)) {
DPRINTF("Enabled IRQ %d\n", irq + i);
- GIC_SET_ENABLED(irq + i);
+ }
+ GIC_SET_ENABLED(irq + i, cm);
/* If a raised level triggered IRQ enabled then mark
is as pending. */
if (GIC_TEST_LEVEL(irq + i, mask)
value = 0;
for (i = 0; i < 8; i++) {
if (value & (1 << i)) {
- if (GIC_TEST_ENABLED(irq + i))
+ int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK;
+
+ if (GIC_TEST_ENABLED(irq + i, cm)) {
DPRINTF("Disabled IRQ %d\n", irq + i);
- GIC_CLEAR_ENABLED(irq + i);
+ }
+ GIC_CLEAR_ENABLED(irq + i, cm);
}
}
} else if (offset < 0x280) {
mask = (value >> 16) & ALL_CPU_MASK;
break;
case 1:
- mask = 1 << cpu;
+ mask = ALL_CPU_MASK ^ (1 << cpu);
break;
case 2:
- mask = ALL_CPU_MASK ^ (1 << cpu);
+ mask = 1 << cpu;
break;
default:
DPRINTF("Bad Soft Int target filter\n");
gic_dist_writew(opaque, offset + 2, value >> 16);
}
-static CPUReadMemoryFunc * const gic_dist_readfn[] = {
- gic_dist_readb,
- gic_dist_readw,
- gic_dist_readl
-};
-
-static CPUWriteMemoryFunc * const gic_dist_writefn[] = {
- gic_dist_writeb,
- gic_dist_writew,
- gic_dist_writel
+static const MemoryRegionOps gic_dist_ops = {
+ .old_mmio = {
+ .read = { gic_dist_readb, gic_dist_readw, gic_dist_readl, },
+ .write = { gic_dist_writeb, gic_dist_writew, gic_dist_writel, },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
};
#ifndef NVIC
return 0;
case 0x0c: /* Acknowledge */
return gic_acknowledge_irq(s, cpu);
- case 0x14: /* Runing Priority */
+ case 0x14: /* Running Priority */
return s->running_priority[cpu];
case 0x18: /* Highest Pending Interrupt */
return s->current_pending[cpu];
#endif
}
for (i = 0; i < 16; i++) {
- GIC_SET_ENABLED(i);
+ GIC_SET_ENABLED(i, ALL_CPU_MASK);
GIC_SET_TRIGGER(i);
}
#ifdef NVIC
qemu_put_be32(f, s->enabled);
for (i = 0; i < NUM_CPU(s); i++) {
qemu_put_be32(f, s->cpu_enabled[i]);
-#ifndef NVIC
- qemu_put_be32(f, s->irq_target[i]);
-#endif
for (j = 0; j < 32; j++)
qemu_put_be32(f, s->priority1[j][i]);
for (j = 0; j < GIC_NIRQ; j++)
qemu_put_be32(f, s->priority2[i]);
}
for (i = 0; i < GIC_NIRQ; i++) {
+#ifndef NVIC
+ qemu_put_be32(f, s->irq_target[i]);
+#endif
qemu_put_byte(f, s->irq_state[i].enabled);
qemu_put_byte(f, s->irq_state[i].pending);
qemu_put_byte(f, s->irq_state[i].active);
int i;
int j;
- if (version_id != 1)
+ if (version_id != 2)
return -EINVAL;
s->enabled = qemu_get_be32(f);
for (i = 0; i < NUM_CPU(s); i++) {
s->cpu_enabled[i] = qemu_get_be32(f);
-#ifndef NVIC
- s->irq_target[i] = qemu_get_be32(f);
-#endif
for (j = 0; j < 32; j++)
s->priority1[j][i] = qemu_get_be32(f);
for (j = 0; j < GIC_NIRQ; j++)
s->priority2[i] = qemu_get_be32(f);
}
for (i = 0; i < GIC_NIRQ; i++) {
+#ifndef NVIC
+ s->irq_target[i] = qemu_get_be32(f);
+#endif
s->irq_state[i].enabled = qemu_get_byte(f);
s->irq_state[i].pending = qemu_get_byte(f);
s->irq_state[i].active = qemu_get_byte(f);
for (i = 0; i < NUM_CPU(s); i++) {
sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
}
- s->iomemtype = cpu_register_io_memory(gic_dist_readfn,
- gic_dist_writefn, s,
- DEVICE_NATIVE_ENDIAN);
+ memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000);
gic_reset(s);
- register_savevm(NULL, "arm_gic", -1, 1, gic_save, gic_load, s);
+ register_savevm(NULL, "arm_gic", -1, 2, gic_save, gic_load, s);
}