summaryrefslogtreecommitdiff
path: root/drivers/irqchip/irq-gic.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/irqchip/irq-gic.c')
-rw-r--r--drivers/irqchip/irq-gic.c59
1 files changed, 55 insertions, 4 deletions
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index b1d9c22caf2e..fc130841d9fc 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -446,6 +446,7 @@ static bool gic_check_gicv2(void __iomem *base)
static void gic_cpu_if_up(struct gic_chip_data *gic)
{
void __iomem *cpu_base = gic_data_cpu_base(gic);
+ void __iomem *dist_base = gic_data_dist_base(gic);
u32 bypass = 0;
u32 mode = 0;
int i;
@@ -463,6 +464,9 @@ static void gic_cpu_if_up(struct gic_chip_data *gic)
bypass = readl(cpu_base + GIC_CPU_CTRL);
bypass &= GICC_DIS_BYPASS_MASK;
+ if (readl_relaxed(dist_base + GIC_DIST_CTRL) & GICD_ENABLE_GRP1)
+ bypass |= 0x1e;
+
writel_relaxed(bypass | mode | GICC_ENABLE, cpu_base + GIC_CPU_CTRL);
}
@@ -485,9 +489,19 @@ static void gic_dist_init(struct gic_chip_data *gic)
for (i = 32; i < gic_irqs; i += 4)
writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);
+ writel_relaxed(GICD_ENABLE_GRP1, base + GIC_DIST_CTRL);
+
+ /*
+ * Optionally set all global interrupts to be group 1.
+ */
+ if (readl_relaxed(base + GIC_DIST_CTRL) & GICD_ENABLE_GRP1) {
+ for (i = 32; i < gic_irqs; i += 32)
+ writel_relaxed(0xffffffff, base + GIC_DIST_IGROUP + i * 4 / 32);
+ }
+
gic_dist_config(base, gic_irqs, NULL);
- writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL);
+ writel_relaxed(GICD_ENABLE | GICD_ENABLE_GRP1, base + GIC_DIST_CTRL);
}
static int gic_cpu_init(struct gic_chip_data *gic)
@@ -524,6 +538,17 @@ static int gic_cpu_init(struct gic_chip_data *gic)
gic_cpu_config(dist_base, 32, NULL);
+ /*
+ * Set all PPI and SGI interrupts to be group 1.
+ *
+ * If grouping is not available (not implemented or prohibited by
+ * security mode) these registers are read-as-zero/write-ignored.
+ */
+ if (readl_relaxed(dist_base + GIC_DIST_CTRL) & GICD_ENABLE_GRP1) {
+ writel_relaxed(0xffff7fff, dist_base + GIC_DIST_IGROUP + 0);
+ writel_relaxed(0x00a0a0a0, dist_base + GIC_DIST_PRI + 12);
+ }
+
writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK);
gic_cpu_if_up(gic);
@@ -635,7 +660,7 @@ void gic_dist_restore(struct gic_chip_data *gic)
dist_base + GIC_DIST_ACTIVE_SET + i * 4);
}
- writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL);
+ writel_relaxed(GICD_ENABLE | GICD_ENABLE_GRP1, dist_base + GIC_DIST_CTRL);
}
void gic_cpu_save(struct gic_chip_data *gic)
@@ -706,6 +731,17 @@ void gic_cpu_restore(struct gic_chip_data *gic)
writel_relaxed(GICD_INT_DEF_PRI_X4,
dist_base + GIC_DIST_PRI + i * 4);
+ /*
+ * Set all PPI and SGI interrupts to be group 1.
+ *
+ * If grouping is not available (not implemented or prohibited by
+ * security mode) these registers are read-as-zero/write-ignored.
+ */
+ if (readl_relaxed(dist_base + GIC_DIST_CTRL) & GICD_ENABLE_GRP1) {
+ writel_relaxed(0xffff7fff, dist_base + GIC_DIST_IGROUP + 0);
+ writel_relaxed(0x00a0a0a0, dist_base + GIC_DIST_PRI + 12);
+ }
+
writel_relaxed(GICC_INT_PRI_THRESHOLD, cpu_base + GIC_CPU_PRIMASK);
gic_cpu_if_up(gic);
}
@@ -797,10 +833,19 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
return IRQ_SET_MASK_OK_DONE;
}
+static bool sgi_is_nonsecure(int irq, struct gic_chip_data *gic)
+{
+ void __iomem *dist_base = gic_data_dist_base(gic);
+ /* FIXME: this should be done in a more generic way */
+ return irq != 15 && readl_relaxed(dist_base + GIC_DIST_CTRL) & GICD_ENABLE_GRP1;
+}
+
static void gic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
{
- int cpu;
+ struct gic_chip_data *gic = &gic_data[0];
unsigned long flags, map = 0;
+ unsigned int softirq;
+ int cpu;
if (unlikely(nr_cpu_ids == 1)) {
/* Only one CPU? let's do a self-IPI... */
@@ -821,8 +866,14 @@ static void gic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
*/
dmb(ishst);
+ softirq = map << 16 | d->hwirq;
+
+ /* SATT only has effect if we are running in the secure world */
+ if (sgi_is_nonsecure(d->hwirq, gic))
+ softirq |= 0x8000;
+
/* this always happens on GIC0 */
- writel_relaxed(map << 16 | d->hwirq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
+ writel_relaxed(softirq, gic_data_dist_base(gic) + GIC_DIST_SOFTINT);
gic_unlock_irqrestore(flags);
}