diff options
author | Guanchao Liang <liang.guanchao@linaro.org> | 2016-11-30 06:19:29 +0800 |
---|---|---|
committer | Jerome Forissier <jerome.forissier@linaro.org> | 2016-11-29 17:11:37 +0100 |
commit | 26ed70ec3afa4776dff3df058723e794e5263a6b (patch) | |
tree | e27cbb04139b995b205393dfcccafb14390f8f51 /core/drivers/gic.c | |
parent | ab046bb510e8ec746da9e643ebf241feacecc53e (diff) |
core: add code for the interrupt framework
With this commit, we add three more GIC APIs for the kernel of OPTEE-OS:
itr_raise_sgi : can raise software generate interrupt(SGI) from secure
world to no-secure world, or secure world to secure world. It's a quick
communication between different worlds and different cores. Because SGI
is using the GIC N-N model, so with this API, every core can receive
the interrupt if want.
itr_raise_pi : can trigger the peripheral interrupt with the corresponding
interrupt number. When sending it to N cores, just one core can receive
the effective interrupt.
itr_set_affinity : can target the peripheral interrupt to the core you
want, it means that one can bind the interrupt to the corresponding core
use this API.
The usage may as follow:
itr_raise_sgi(11, 0x1 << 1)
it will raise SGI11 to core 1, and if you want not only core 1 can receive
SGI11 but also core 2, then you can change the code to
itr_raise_sgi(11, 0x1 << 1 || 0x1 << 2).
itr_set_affinity(61, 0x1 << 1)
itr_raise_pi(61)
These two APIs may use together, the operation set_affinity set the PI61
can just sent to core 1, then raise_pi, core 1 will receive the peripheral
interrupt 61.
Signed-off-by: Guanchao Liang <liang.guanchao@linaro.org>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
Reviewed-by: Etienne Carriere <etienne.carriere@linaro.org>
Reviewed-by: Joakim Bech <joakim.bech@linaro.org>
Tested-by: Joakim Bech <joakim.bech@linaro.org> (QEMU)
[Update commit author to be same as S-o-b: above]
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
Diffstat (limited to 'core/drivers/gic.c')
-rw-r--r-- | core/drivers/gic.c | 89 |
1 files changed, 87 insertions, 2 deletions
diff --git a/core/drivers/gic.c b/core/drivers/gic.c index c767dec1..93979a0a 100644 --- a/core/drivers/gic.c +++ b/core/drivers/gic.c @@ -50,9 +50,11 @@ #define GICD_IGROUPR(n) (0x080 + (n) * 4) #define GICD_ISENABLER(n) (0x100 + (n) * 4) #define GICD_ICENABLER(n) (0x180 + (n) * 4) +#define GICD_ISPENDR(n) (0x200 + (n) * 4) #define GICD_ICPENDR(n) (0x280 + (n) * 4) #define GICD_IPRIORITYR(n) (0x400 + (n) * 4) #define GICD_ITARGETSR(n) (0x800 + (n) * 4) +#define GICD_SGIR (0xF00) #define GICD_CTLR_ENABLEGRP0 (1 << 0) #define GICD_CTLR_ENABLEGRP1 (1 << 1) @@ -60,6 +62,12 @@ /* Number of Private Peripheral Interrupt */ #define NUM_PPI 32 +/* Number of Software Generated Interrupt */ +#define NUM_SGI 16 + +/* Number of Non-secure Software Generated Interrupt */ +#define NUM_NS_SGI 8 + /* Number of interrupts in one register */ #define NUM_INTS_PER_REG 32 @@ -82,11 +90,19 @@ 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 void gic_op_raise_pi(struct itr_chip *chip, size_t it); +static void gic_op_raise_sgi(struct itr_chip *chip, size_t it, + uint8_t cpu_mask); +static void gic_op_set_affinity(struct itr_chip *chip, size_t it, + uint8_t cpu_mask); static const struct itr_ops gic_ops = { .add = gic_op_add, .enable = gic_op_enable, .disable = gic_op_disable, + .raise_pi = gic_op_raise_pi, + .raise_sgi = gic_op_raise_sgi, + .set_affinity = gic_op_set_affinity, }; static size_t probe_max_it(vaddr_t gicc_base, vaddr_t gicd_base) @@ -250,8 +266,13 @@ static void gic_it_enable(struct gic_data *gd, size_t it) /* Assigned to group0 */ assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask)); - /* Not enabled yet */ - assert(!(read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask)); + if (it >= NUM_SGI) { + /* + * Not enabled yet, except Software Generated Interrupt + * which is implementation defined + */ + assert(!(read32(gd->gicd_base + GICD_ISENABLER(idx)) & mask)); + } /* Enable the interrupt */ write32(mask, gd->gicd_base + GICD_ISENABLER(idx)); @@ -269,6 +290,36 @@ static void gic_it_disable(struct gic_data *gd, size_t it) write32(mask, gd->gicd_base + GICD_ICENABLER(idx)); } +static void gic_it_set_pending(struct gic_data *gd, size_t it) +{ + size_t idx = it / NUM_INTS_PER_REG; + uint32_t mask = BIT32(it % NUM_INTS_PER_REG); + + /* Should be Peripheral Interrupt */ + assert(it >= NUM_SGI); + /* Assigned to group0 */ + assert(!(read32(gd->gicd_base + GICD_IGROUPR(idx)) & mask)); + + /* Raise the interrupt */ + write32(mask, gd->gicd_base + GICD_ISPENDR(idx)); +} + +static void gic_it_raise_sgi(struct gic_data *gd, size_t it, + uint8_t cpu_mask, uint8_t group) +{ + uint32_t mask_id = it & 0xf; + uint32_t mask_group = group & 0x1; + uint32_t mask_cpu = cpu_mask & 0xff; + uint32_t mask = (mask_id | SHIFT_U32(mask_group, 15) | + SHIFT_U32(mask_cpu, 16)); + + /* Should be Software Generated Interrupt */ + assert(it < NUM_SGI); + + /* Raise the interrupt */ + write32(mask, gd->gicd_base + GICD_SGIR); +} + static uint32_t gic_read_iar(struct gic_data *gd) { return read32(gd->gicc_base + GICC_IAR); @@ -370,3 +421,37 @@ static void gic_op_disable(struct itr_chip *chip, size_t it) gic_it_disable(gd, it); } + +static void gic_op_raise_pi(struct itr_chip *chip, size_t it) +{ + struct gic_data *gd = container_of(chip, struct gic_data, chip); + + if (it >= gd->max_it) + panic(); + + gic_it_set_pending(gd, it); +} + +static void gic_op_raise_sgi(struct itr_chip *chip, size_t it, + uint8_t cpu_mask) +{ + struct gic_data *gd = container_of(chip, struct gic_data, chip); + + if (it >= gd->max_it) + panic(); + + if (it < NUM_NS_SGI) + gic_it_raise_sgi(gd, it, cpu_mask, 1); + else + gic_it_raise_sgi(gd, it, cpu_mask, 0); +} +static void gic_op_set_affinity(struct itr_chip *chip, size_t it, + uint8_t cpu_mask) +{ + struct gic_data *gd = container_of(chip, struct gic_data, chip); + + if (it >= gd->max_it) + panic(); + + gic_it_set_cpu_mask(gd, it, cpu_mask); +} |