summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Thompson <daniel.thompson@linaro.org>2014-04-11 17:14:58 +0100
committerDaniel Thompson <daniel.thompson@linaro.org>2014-04-11 17:14:58 +0100
commit89bbb19a8429d5abb3e028db398394cc37802014 (patch)
tree8be67f05444b1f8adb10fa45804371065bb97239
parentefcc87d9aedb590b8506cd1a7c8abe557c760f9e (diff)
WIP: Initial attempt at GICv2 (without security extensions).dev/arm-gicv2
Change-Id: I9767d7a9aad43e8762a7b615c6f9dd1a00799f18 Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
-rw-r--r--hw/arm/vexpress.c2
-rw-r--r--hw/intc/arm_gic.c93
-rw-r--r--hw/intc/arm_gic_common.c9
-rw-r--r--hw/intc/armv7m_nvic.c2
-rw-r--r--hw/intc/gic_internal.h3
-rw-r--r--include/hw/intc/arm_gic_common.h6
6 files changed, 98 insertions, 17 deletions
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index 169eb061a..66063d5f1 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -228,6 +228,8 @@ static void init_cpus(const char *cpu_model, const char *privdev,
DeviceState *cpudev = DEVICE(qemu_get_cpu(n));
sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+ sysbus_connect_irq(busdev, n + smp_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
}
}
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 955b8d494..c37a857cb 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -22,7 +22,7 @@
#include "gic_internal.h"
#include "qom/cpu.h"
-//#define DEBUG_GIC
+#define DEBUG_GIC
#ifdef DEBUG_GIC
#define DPRINTF(fmt, ...) \
@@ -52,14 +52,15 @@ void gic_update(GICState *s)
int best_irq;
int best_prio;
int irq;
- int level;
+ int irq_level;
+ int fiq_level;
int cpu;
int cm;
for (cpu = 0; cpu < NUM_CPU(s); cpu++) {
cm = 1 << cpu;
s->current_pending[cpu] = 1023;
- if (!s->enabled || !s->cpu_enabled[cpu]) {
+ if (!s->enabled_grp0 || !s->cpu_enabled[cpu]) {
qemu_irq_lower(s->parent_irq[cpu]);
return;
}
@@ -73,15 +74,25 @@ void gic_update(GICState *s)
}
}
}
- level = 0;
+ irq_level = 0;
if (best_prio < s->priority_mask[cpu]) {
s->current_pending[cpu] = best_irq;
if (best_prio < s->running_priority[cpu]) {
DPRINTF("Raised pending IRQ %d (cpu %d)\n", best_irq, cpu);
- level = 1;
+ irq_level = 1;
}
}
- qemu_set_irq(s->parent_irq[cpu], level);
+
+ fiq_level = 0;
+ if (s->revision >= 2) {
+ if (s->enabled_fiq && !GIC_TEST_GROUP(best_irq)) {
+ fiq_level = irq_level;
+ irq_level = 0;
+ }
+ }
+
+ qemu_set_irq(s->parent_irq[cpu], irq_level);
+ qemu_set_irq(s->parent_fiq[cpu], fiq_level);
}
}
@@ -294,15 +305,35 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
cpu = gic_get_current_cpu(s);
cm = 1 << cpu;
if (offset < 0x100) {
- if (offset == 0)
- return s->enabled;
+ if (offset == 0) {
+ if (s->revision < 2) {
+ return s->enabled_grp0;
+ } else {
+ return (s->enabled_grp0) | (s->enabled_grp1 << 1) |
+ (s->enabled_fiq << 3);
+ }
+ }
if (offset == 4)
return ((s->num_irq / 32) - 1) | ((NUM_CPU(s) - 1) << 5);
if (offset < 0x08)
return 0;
if (offset >= 0x80) {
- /* Interrupt Security , RAZ/WI */
- return 0;
+ /* Interrupt Group. */
+ if (s->revision < 2) {
+ /* GICv1 (or security extensions), RAZ/WI */
+ res = 0;
+ } else {
+ /* GICv2+ without security extensions */
+ irq = (offset - 0x80) * 8 + GIC_BASE_IRQ;
+ if (irq >= s->num_irq)
+ goto bad_reg;
+ res = 0;
+ for (i = 0; i < 8; i++) {
+ if (GIC_TEST_GROUP(irq + i)) {
+ res |= (1 << i);
+ }
+ }
+ }
}
goto bad_reg;
} else if (offset < 0x200) {
@@ -441,12 +472,45 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
cpu = gic_get_current_cpu(s);
if (offset < 0x100) {
if (offset == 0) {
- s->enabled = (value & 1);
- DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
+ s->enabled_grp0 = (value & (1 << 0));
+ if (s->revision < 2) {
+ DPRINTF("Distribution %sabled\n",
+ s->enabled_grp0 ? "En" : "Dis");
+ } else {
+ s->enabled_grp1 = (value & (1 << 1));
+ s->enabled_fiq = (value & (1 << 3));
+ DPRINTF("Interrupt distribution: "
+ "group0 %sabled; group1 %sabled; fiq %sabled\n",
+ s->enabled_grp0 ? "En" : "Dis",
+ s->enabled_grp1 ? "En" : "Dis",
+ s->enabled_fiq ? "En" : "Dis");
+ }
} else if (offset < 4) {
/* ignored. */
} else if (offset >= 0x80) {
- /* Interrupt Security Registers, RAZ/WI */
+ /* Interrupt Group. */
+ if (s->revision < 2) {
+ /* GICv1 (or security extensions), RAZ/WI */
+ } else {
+ /* GICv2+ without security extensions */
+ irq = (offset - 0x80) * 8 + GIC_BASE_IRQ;
+ irq += GIC_BASE_IRQ;
+ if (irq >= s->num_irq)
+ goto bad_reg;
+ for (i = 0; i < 8; i++) {
+ if (value & (1 << i)) {
+ if (!GIC_TEST_GROUP(irq + i)) {
+ DPRINTF("Set IRQ %d to group1\n", irq + i);
+ }
+ GIC_SET_GROUP(irq + i);
+ } else {
+ if (GIC_TEST_GROUP(irq + i)) {
+ DPRINTF("Set IRQ %d to group0\n", irq + i);
+ }
+ GIC_CLEAR_GROUP(irq + i);
+ }
+ }
+ }
} else {
goto bad_reg;
}
@@ -786,6 +850,9 @@ void gic_init_irqs_and_distributor(GICState *s, int num_irq)
for (i = 0; i < NUM_CPU(s); i++) {
sysbus_init_irq(sbd, &s->parent_irq[i]);
}
+ for (i = 0; i < NUM_CPU(s); i++) {
+ sysbus_init_irq(sbd, &s->parent_fiq[i]);
+ }
memory_region_init_io(&s->iomem, OBJECT(s), &gic_dist_ops, s,
"gic_dist", 0x1000);
}
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index 6d884eca3..6935331b4 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -52,6 +52,7 @@ static const VMStateDescription vmstate_gic_irq_state = {
VMSTATE_UINT8(level, gic_irq_state),
VMSTATE_BOOL(model, gic_irq_state),
VMSTATE_BOOL(edge_trigger, gic_irq_state),
+ VMSTATE_BOOL(group, gic_irq_state),
VMSTATE_END_OF_LIST()
}
};
@@ -63,7 +64,9 @@ static const VMStateDescription vmstate_gic = {
.pre_save = gic_pre_save,
.post_load = gic_post_load,
.fields = (VMStateField[]) {
- VMSTATE_BOOL(enabled, GICState),
+ VMSTATE_BOOL(enabled_grp0, GICState),
+ VMSTATE_BOOL(enabled_grp1, GICState),
+ VMSTATE_BOOL(enabled_fiq, GICState),
VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, GIC_NCPU),
VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1,
vmstate_gic_irq_state, gic_irq_state),
@@ -138,7 +141,9 @@ static void arm_gic_common_reset(DeviceState *dev)
s->irq_target[i] = 1;
}
}
- s->enabled = false;
+ s->enabled_grp0 = false;
+ s->enabled_grp1 = false;
+ s->enabled_fiq = false;
}
static Property arm_gic_common_properties[] = {
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 6066fa683..f3d1f4347 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -466,7 +466,7 @@ static void armv7m_nvic_reset(DeviceState *dev)
s->gic.cpu_enabled[0] = true;
s->gic.priority_mask[0] = 0x100;
/* The NVIC as a whole is always enabled. */
- s->gic.enabled = true;
+ s->gic.enabled_grp0 = true;
systick_reset(s);
}
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
index 48a58d789..017b0174f 100644
--- a/hw/intc/gic_internal.h
+++ b/hw/intc/gic_internal.h
@@ -29,6 +29,9 @@
through the normal GIC interface. */
#define GIC_BASE_IRQ ((s->revision == REV_NVIC) ? 32 : 0)
+#define GIC_SET_GROUP(irq) s->irq_state[irq].group = true
+#define GIC_CLEAR_GROUP(irq) s->irq_state[irq].group = false
+#define GIC_TEST_GROUP(irq) (s->irq_state[irq].group)
#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)
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index f6887ed92..7bd1fc01a 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -42,6 +42,7 @@ typedef struct gic_irq_state {
uint8_t level;
bool model; /* 0 = N:N, 1 = 1:N */
bool edge_trigger; /* true: edge-triggered, false: level-triggered */
+ bool group; /* true: group1, false: group0 */
} gic_irq_state;
typedef struct GICState {
@@ -50,7 +51,10 @@ typedef struct GICState {
/*< public >*/
qemu_irq parent_irq[GIC_NCPU];
- bool enabled;
+ qemu_irq parent_fiq[GIC_NCPU];
+ bool enabled_grp0;
+ bool enabled_grp1;
+ bool enabled_fiq;
bool cpu_enabled[GIC_NCPU];
gic_irq_state irq_state[GIC_MAXIRQ];