aboutsummaryrefslogtreecommitdiff
path: root/core/drivers/gic.c
diff options
context:
space:
mode:
authorGuanchao Liang <liang.guanchao@linaro.org>2016-11-30 06:19:29 +0800
committerJerome Forissier <jerome.forissier@linaro.org>2016-11-29 17:11:37 +0100
commit26ed70ec3afa4776dff3df058723e794e5263a6b (patch)
treee27cbb04139b995b205393dfcccafb14390f8f51 /core/drivers/gic.c
parentab046bb510e8ec746da9e643ebf241feacecc53e (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.c89
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);
+}