diff options
author | Fabian Aggeler <aggelerf@ethz.ch> | 2015-05-12 11:57:17 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2015-05-12 11:57:17 +0100 |
commit | 32951860834f09d1c1a0b81d8d7d5529e2d0e074 (patch) | |
tree | 2056b5bdb2eb8a02e338dbb5ddc3f47267964725 /hw/intc/arm_gic.c | |
parent | 822e9cc310484f77e0b1c16fbef763a5d0eec80a (diff) |
hw/intc/arm_gic: Make ICCICR/GICC_CTLR banked
ICCICR/GICC_CTLR is banked in GICv1 implementations with Security
Extensions or in GICv2 in independent from Security Extensions.
This makes it possible to enable forwarding of interrupts from
the CPU interfaces to the connected processors for Group0 and Group1.
We also allow to set additional bits like AckCtl and FIQEn by changing
the type from bool to uint32. Since the field does not only store the
enable bit anymore and since we are touching the vmstate, we use the
opportunity to rename the field to cpu_ctlr.
Signed-off-by: Fabian Aggeler <aggelerf@ethz.ch>
Signed-off-by: Greg Bellows <greg.bellows@linaro.org>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1430502643-25909-9-git-send-email-peter.maydell@linaro.org
Message-id: 1429113742-8371-9-git-send-email-greg.bellows@linaro.org
[PMM: rewrote to store state in a single uint32_t rather than
keeping the NS and S banked variants separate; this considerably
simplifies the get/set functions]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/intc/arm_gic.c')
-rw-r--r-- | hw/intc/arm_gic.c | 51 |
1 files changed, 47 insertions, 4 deletions
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index e6ad8dea72..4aaaac2de3 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -68,7 +68,7 @@ void gic_update(GICState *s) cm = 1 << cpu; s->current_pending[cpu] = 1023; if (!(s->ctlr & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1)) - || !s->cpu_enabled[cpu]) { + || !(s->cpu_ctlr[cpu] & (GICC_CTLR_EN_GRP0 | GICC_CTLR_EN_GRP1))) { qemu_irq_lower(s->parent_irq[cpu]); return; } @@ -242,6 +242,50 @@ void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val) } } +static uint32_t gic_get_cpu_control(GICState *s, int cpu, MemTxAttrs attrs) +{ + uint32_t ret = s->cpu_ctlr[cpu]; + + if (s->security_extn && !attrs.secure) { + /* Construct the NS banked view of GICC_CTLR from the correct + * bits of the S banked view. We don't need to move the bypass + * control bits because we don't implement that (IMPDEF) part + * of the GIC architecture. + */ + ret = (ret & (GICC_CTLR_EN_GRP1 | GICC_CTLR_EOIMODE_NS)) >> 1; + } + return ret; +} + +static void gic_set_cpu_control(GICState *s, int cpu, uint32_t value, + MemTxAttrs attrs) +{ + uint32_t mask; + + if (s->security_extn && !attrs.secure) { + /* The NS view can only write certain bits in the register; + * the rest are unchanged + */ + mask = GICC_CTLR_EN_GRP1; + if (s->revision == 2) { + mask |= GICC_CTLR_EOIMODE_NS; + } + s->cpu_ctlr[cpu] &= ~mask; + s->cpu_ctlr[cpu] |= (value << 1) & mask; + } else { + if (s->revision == 2) { + mask = s->security_extn ? GICC_CTLR_V2_S_MASK : GICC_CTLR_V2_MASK; + } else { + mask = s->security_extn ? GICC_CTLR_V1_S_MASK : GICC_CTLR_V1_MASK; + } + s->cpu_ctlr[cpu] = value & mask; + } + DPRINTF("CPU Interface %d: Group0 Interrupts %sabled, " + "Group1 Interrupts %sabled\n", cpu, + (s->cpu_ctlr[cpu] & GICC_CTLR_EN_GRP0) ? "En" : "Dis", + (s->cpu_ctlr[cpu] & GICC_CTLR_EN_GRP1) ? "En" : "Dis"); +} + void gic_complete_irq(GICState *s, int cpu, int irq) { int update = 0; @@ -756,7 +800,7 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, { switch (offset) { case 0x00: /* Control */ - *data = s->cpu_enabled[cpu]; + *data = gic_get_cpu_control(s, cpu, attrs); break; case 0x04: /* Priority mask */ *data = s->priority_mask[cpu]; @@ -806,8 +850,7 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, { switch (offset) { case 0x00: /* Control */ - s->cpu_enabled[cpu] = (value & 1); - DPRINTF("CPU %d %sabled\n", cpu, s->cpu_enabled[cpu] ? "En" : "Dis"); + gic_set_cpu_control(s, cpu, value, attrs); break; case 0x04: /* Priority mask */ s->priority_mask[cpu] = (value & 0xff); |