diff options
author | Jens Wiklander <jens.wiklander@linaro.org> | 2016-04-21 21:51:34 +0200 |
---|---|---|
committer | Jens Wiklander <jens.wiklander@linaro.org> | 2016-04-29 09:59:55 +0200 |
commit | 7315b7b47303ec73b8f5a994c37bb6bed0d00e37 (patch) | |
tree | 690d65ce7be5a08cac4d3b61a572d29887d31be8 /core/drivers/gic.c | |
parent | 514930456ddbff1d42984e8311964272cdb91ba5 (diff) |
core: add interrupt framework
Adds interrupt frameworks and adjusts gic driver to fit in.
Update plat-vexpress and sunxi platforms to initialize gic with slightly
modified interface.
Reviewed-by: Pascal Brand <pascal.brand@linaro.org>
Tested-by: Jens Wiklander <jens.wiklander@linaro.org> (QEMU, FVP)
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
Diffstat (limited to 'core/drivers/gic.c')
-rw-r--r-- | core/drivers/gic.c | 216 |
1 files changed, 139 insertions, 77 deletions
diff --git a/core/drivers/gic.c b/core/drivers/gic.c index b1fa8f9a..ba0d203b 100644 --- a/core/drivers/gic.c +++ b/core/drivers/gic.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016, Linaro Limited * Copyright (c) 2014, STMicroelectronics International N.V. * All rights reserved. * @@ -26,6 +27,8 @@ */ #include <drivers/gic.h> +#include <kernel/interrupt.h> +#include <util.h> #include <io.h> #include <trace.h> @@ -70,14 +73,23 @@ /* Maximum number of interrups a GIC can support */ #define GIC_MAX_INTS 1020 +#define GIC_SPURIOUS_ID 1023 -static struct { - vaddr_t gicc_base; - vaddr_t gicd_base; - size_t max_it; -} gic; +#define GICC_IAR_IT_ID_MASK 0x3ff +#define GICC_IAR_CPU_ID_MASK 0x7 +#define GICC_IAR_CPU_ID_SHIFT 10 -static size_t probe_max_it(void) +static void gic_op_add(struct itr_chip *chip, size_t it, uint32_t flags); +static void gic_op_enable(struct itr_chip *chip, size_t it); +static void gic_op_disable(struct itr_chip *chip, size_t it); + +static const struct itr_ops gic_ops = { + .add = gic_op_add, + .enable = gic_op_enable, + .disable = gic_op_disable, +}; + +static size_t probe_max_it(vaddr_t gicc_base, vaddr_t gicd_base) { int i; uint32_t old_ctlr; @@ -88,17 +100,17 @@ static size_t probe_max_it(void) /* * Probe which interrupt number is the largest. */ - old_ctlr = read32(gic.gicc_base + GICC_CTLR); - write32(0, gic.gicc_base + GICC_CTLR); + old_ctlr = read32(gicc_base + GICC_CTLR); + write32(0, gicc_base + GICC_CTLR); for (i = max_regs; i >= 0; i--) { uint32_t old_reg; uint32_t reg; int b; - old_reg = read32(gic.gicd_base + GICD_ISENABLER(i)); - write32(0xffffffff, gic.gicd_base + GICD_ISENABLER(i)); - reg = read32(gic.gicd_base + GICD_ISENABLER(i)); - write32(old_reg, gic.gicd_base + GICD_ICENABLER(i)); + old_reg = read32(gicd_base + GICD_ISENABLER(i)); + write32(0xffffffff, gicd_base + GICD_ISENABLER(i)); + reg = read32(gicd_base + GICD_ISENABLER(i)); + write32(old_reg, gicd_base + GICD_ICENABLER(i)); for (b = NUM_INTS_PER_REG - 1; b >= 0; b--) { if ((1 << b) & reg) { ret = i * NUM_INTS_PER_REG + b; @@ -107,43 +119,41 @@ static size_t probe_max_it(void) } } out: - write32(old_ctlr, gic.gicc_base + GICC_CTLR); + write32(old_ctlr, gicc_base + GICC_CTLR); return ret; } -void gic_cpu_init(void) +void gic_cpu_init(struct gic_data *gd) { /* per-CPU interrupts config: * ID0-ID7(SGI) for Non-secure interrupts * ID8-ID15(SGI) for Secure interrupts. * All PPI config as Non-secure interrupts. */ - write32(0xffff00ff, gic.gicd_base + GICD_IGROUPR(0)); + write32(0xffff00ff, gd->gicd_base + GICD_IGROUPR(0)); /* Set the priority mask to permit Non-secure interrupts, and to * allow the Non-secure world to adjust the priority mask itself */ - write32(0x80, gic.gicc_base + GICC_PMR); + write32(0x80, gd->gicc_base + GICC_PMR); /* Enable GIC */ write32(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 | GICC_CTLR_FIQEN, - gic.gicc_base + GICC_CTLR); + gd->gicc_base + GICC_CTLR); } -void gic_init(vaddr_t gicc_base, vaddr_t gicd_base) +void gic_init(struct gic_data *gd, vaddr_t gicc_base, vaddr_t gicd_base) { size_t n; - gic.gicc_base = gicc_base; - gic.gicd_base = gicd_base; - gic.max_it = probe_max_it(); + gic_init_base_addr(gd, gicc_base, gicd_base); - for (n = 0; n <= gic.max_it / NUM_INTS_PER_REG; n++) { + for (n = 0; n <= gd->max_it / NUM_INTS_PER_REG; n++) { /* Disable interrupts */ - write32(0xffffffff, gic.gicd_base + GICD_ICENABLER(n)); + write32(0xffffffff, gd->gicd_base + GICD_ICENABLER(n)); /* Make interrupts non-pending */ - write32(0xffffffff, gic.gicd_base + GICD_ICPENDR(n)); + write32(0xffffffff, gd->gicd_base + GICD_ICPENDR(n)); /* Mark interrupts non-secure */ if (n == 0) { @@ -152,154 +162,206 @@ void gic_init(vaddr_t gicc_base, vaddr_t gicd_base) * ID8-ID15(SGI) for Secure interrupts. * All PPI config as Non-secure interrupts. */ - write32(0xffff00ff, gic.gicd_base + GICD_IGROUPR(n)); + write32(0xffff00ff, gd->gicd_base + GICD_IGROUPR(n)); } else { - write32(0xffffffff, gic.gicd_base + GICD_IGROUPR(n)); + write32(0xffffffff, gd->gicd_base + GICD_IGROUPR(n)); } } /* Set the priority mask to permit Non-secure interrupts, and to * allow the Non-secure world to adjust the priority mask itself */ - write32(0x80, gic.gicc_base + GICC_PMR); + write32(0x80, gd->gicc_base + GICC_PMR); /* Enable GIC */ write32(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 | GICC_CTLR_FIQEN, - gic.gicc_base + GICC_CTLR); + gd->gicc_base + GICC_CTLR); write32(GICD_CTLR_ENABLEGRP0 | GICD_CTLR_ENABLEGRP1, - gic.gicd_base + GICD_CTLR); + gd->gicd_base + GICD_CTLR); } -void gic_init_base_addr(vaddr_t gicc_base, vaddr_t gicd_base) +void gic_init_base_addr(struct gic_data *gd, vaddr_t gicc_base, + vaddr_t gicd_base) { - gic.gicc_base = gicc_base; - gic.gicd_base = gicd_base; - gic.max_it = probe_max_it(); + gd->gicc_base = gicc_base; + gd->gicd_base = gicd_base; + gd->max_it = probe_max_it(gicc_base, gicd_base); + gd->chip.ops = &gic_ops; } -void gic_it_add(size_t it) +static void gic_it_add(struct gic_data *gd, size_t it) { size_t idx = it / NUM_INTS_PER_REG; uint32_t mask = 1 << (it % NUM_INTS_PER_REG); - assert(it <= gic.max_it); /* Not too large */ + assert(it <= gd->max_it); /* Not too large */ /* Disable the interrupt */ - write32(mask, gic.gicd_base + GICD_ICENABLER(idx)); + write32(mask, gd->gicd_base + GICD_ICENABLER(idx)); /* Make it non-pending */ - write32(mask, gic.gicd_base + GICD_ICPENDR(idx)); + write32(mask, gd->gicd_base + GICD_ICPENDR(idx)); /* Assign it to group0 */ - write32(read32(gic.gicd_base + GICD_IGROUPR(idx)) & ~mask, - gic.gicd_base + GICD_IGROUPR(idx)); + write32(read32(gd->gicd_base + GICD_IGROUPR(idx)) & ~mask, + gd->gicd_base + GICD_IGROUPR(idx)); } -void gic_it_set_cpu_mask(size_t it, uint8_t cpu_mask) +static void gic_it_set_cpu_mask(struct gic_data *gd, size_t it, + uint8_t cpu_mask) { size_t idx = it / NUM_INTS_PER_REG; uint32_t mask = 1 << (it % NUM_INTS_PER_REG); uint32_t target, target_shift; - assert(it <= gic.max_it); /* Not too large */ + assert(it <= gd->max_it); /* Not too large */ /* Assigned to group0 */ - assert(!(read32(gic.gicd_base + GICD_IGROUPR(idx)) & mask)); + assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask)); /* Route it to selected CPUs */ - target = read32(gic.gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG)); + target = read32(gd->gicd_base + + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG)); target_shift = (it % NUM_TARGETS_PER_REG) * ITARGETSR_FIELD_BITS; target &= ~(ITARGETSR_FIELD_MASK << target_shift); target |= cpu_mask << target_shift; DMSG("cpu_mask: writing 0x%x to 0x%" PRIxVA, - target, gic.gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG)); - write32(target, gic.gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG)); + target, gd->gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG)); + write32(target, + gd->gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG)); DMSG("cpu_mask: 0x%x\n", - read32(gic.gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG))); + read32(gd->gicd_base + GICD_ITARGETSR(it / NUM_TARGETS_PER_REG))); } -void gic_it_set_prio(size_t it, uint8_t prio) +static void gic_it_set_prio(struct gic_data *gd, size_t it, uint8_t prio) { size_t idx = it / NUM_INTS_PER_REG; uint32_t mask = 1 << (it % NUM_INTS_PER_REG); - assert(it <= gic.max_it); /* Not too large */ + assert(it <= gd->max_it); /* Not too large */ /* Assigned to group0 */ - assert(!(read32(gic.gicd_base + GICD_IGROUPR(idx)) & mask)); + assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask)); /* Set prio it to selected CPUs */ DMSG("prio: writing 0x%x to 0x%" PRIxVA, - prio, gic.gicd_base + GICD_IPRIORITYR(0) + it); - write8(prio, gic.gicd_base + GICD_IPRIORITYR(0) + it); + prio, gd->gicd_base + GICD_IPRIORITYR(0) + it); + write8(prio, gd->gicd_base + GICD_IPRIORITYR(0) + it); } -void gic_it_enable(size_t it) +static void gic_it_enable(struct gic_data *gd, size_t it) { size_t idx = it / NUM_INTS_PER_REG; uint32_t mask = 1 << (it % NUM_INTS_PER_REG); - assert(it <= gic.max_it); /* Not too large */ + assert(it <= gd->max_it); /* Not too large */ /* Assigned to group0 */ - assert(!(read32(gic.gicd_base + GICD_IGROUPR(idx)) & mask)); + assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask)); /* Not enabled yet */ - assert(!(read32(gic.gicd_base + GICD_ISENABLER(idx)) & mask)); + assert(!(read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask)); /* Enable the interrupt */ - write32(mask, gic.gicd_base + GICD_ISENABLER(idx)); + write32(mask, gd->gicd_base + GICD_ISENABLER(idx)); } -void gic_it_disable(size_t it) +static void gic_it_disable(struct gic_data *gd, size_t it) { size_t idx = it / NUM_INTS_PER_REG; uint32_t mask = 1 << (it % NUM_INTS_PER_REG); - assert(it <= gic.max_it); /* Not too large */ + assert(it <= gd->max_it); /* Not too large */ /* Assigned to group0 */ - assert(!(read32(gic.gicd_base + GICD_IGROUPR(idx)) & mask)); + assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask)); /* Disable the interrupt */ - write32(mask, gic.gicd_base + GICD_ICENABLER(idx)); + write32(mask, gd->gicd_base + GICD_ICENABLER(idx)); } -uint32_t gic_read_iar(void) +static uint32_t gic_read_iar(struct gic_data *gd) { - return read32(gic.gicc_base + GICC_IAR); + return read32(gd->gicc_base + GICC_IAR); } -void gic_write_eoir(uint32_t eoir) +static void gic_write_eoir(struct gic_data *gd, uint32_t eoir) { - write32(eoir, gic.gicc_base + GICC_EOIR); + write32(eoir, gd->gicc_base + GICC_EOIR); } -bool gic_it_is_enabled(size_t it) { +static bool gic_it_is_enabled(struct gic_data *gd, size_t it) +{ size_t idx = it / NUM_INTS_PER_REG; uint32_t mask = 1 << (it % NUM_INTS_PER_REG); - return !!(read32(gic.gicd_base + GICD_ISENABLER(idx)) & mask); + return !!(read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask); } -bool gic_it_get_group(size_t it) { +static bool __maybe_unused gic_it_get_group(struct gic_data *gd, size_t it) +{ size_t idx = it / NUM_INTS_PER_REG; uint32_t mask = 1 << (it % NUM_INTS_PER_REG); - return !!(read32(gic.gicd_base + GICD_IGROUPR(idx)) & mask); + return !!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask); } -uint32_t gic_it_get_target(size_t it) { +static uint32_t __maybe_unused gic_it_get_target(struct gic_data *gd, size_t it) +{ size_t reg_idx = it / NUM_TARGETS_PER_REG; - uint32_t target_shift = (it % NUM_TARGETS_PER_REG) * ITARGETSR_FIELD_BITS; + uint32_t target_shift = (it % NUM_TARGETS_PER_REG) * + ITARGETSR_FIELD_BITS; uint32_t target_mask = ITARGETSR_FIELD_MASK << target_shift; uint32_t target = - read32(gic.gicd_base + GICD_ITARGETSR(reg_idx)) & target_mask; + read32(gd->gicd_base + GICD_ITARGETSR(reg_idx)) & target_mask; + target = target >> target_shift; return target; } -void gic_dump_state(void) +void gic_dump_state(struct gic_data *gd) { int i; - DMSG("GICC_CTLR: 0x%x", read32(gic.gicc_base + GICC_CTLR)); - DMSG("GICD_CTLR: 0x%x", read32(gic.gicd_base + GICD_CTLR)); - for (i = 0; i < (int)gic.max_it; i++) { - if (gic_it_is_enabled(i)) { + DMSG("GICC_CTLR: 0x%x", read32(gd->gicc_base + GICC_CTLR)); + DMSG("GICD_CTLR: 0x%x", read32(gd->gicd_base + GICD_CTLR)); + + for (i = 0; i < (int)gd->max_it; i++) { + if (gic_it_is_enabled(gd, i)) { DMSG("irq%d: enabled, group:%d, target:%x", i, - gic_it_get_group(i), gic_it_get_target(i)); + gic_it_get_group(gd, i), gic_it_get_target(gd, i)); } } } + +void gic_it_handle(struct gic_data *gd) +{ + uint32_t iar; + uint32_t id; + + iar = gic_read_iar(gd); + id = iar & GICC_IAR_IT_ID_MASK; + + if (id == GIC_SPURIOUS_ID) + DMSG("ignoring spurious interrupt"); + else + itr_handle(id); + + gic_write_eoir(gd, iar); +} + +static void gic_op_add(struct itr_chip *chip, size_t it, + uint32_t flags __unused) +{ + struct gic_data *gd = container_of(chip, struct gic_data, chip); + + gic_it_add(gd, it); + /* Set the CPU mask to deliver interrupts to any online core */ + gic_it_set_cpu_mask(gd, it, 0xff); + gic_it_set_prio(gd, it, 0x1); +} + +static void gic_op_enable(struct itr_chip *chip, size_t it) +{ + struct gic_data *gd = container_of(chip, struct gic_data, chip); + + gic_it_enable(gd, it); +} + +static void gic_op_disable(struct itr_chip *chip, size_t it) +{ + struct gic_data *gd = container_of(chip, struct gic_data, chip); + + gic_it_disable(gd, it); +} |