summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/kernel/traps.c4
-rw-r--r--drivers/irqchip/irq-gic.c59
-rw-r--r--include/linux/irqchip/arm-gic.h2
3 files changed, 61 insertions, 4 deletions
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 17d5a785df28..798b30942a24 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -26,6 +26,7 @@
#include <linux/sched/debug.h>
#include <linux/sched/task_stack.h>
#include <linux/irq.h>
+#include <linux/nmi.h>
#include <linux/atomic.h>
#include <asm/cacheflush.h>
@@ -514,6 +515,9 @@ asmlinkage void __exception_irq_entry handle_fiq_as_nmi(struct pt_regs *regs)
nmi_enter();
/* nop. FIQ handlers for special arch/arm features can be added here. */
+#ifdef CONFIG_SMP
+ nmi_cpu_backtrace(regs);
+#endif
nmi_exit();
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);
}
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index 5686711b0f40..e7dd6e89b493 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -57,6 +57,8 @@
#define GIC_DIST_SGI_PENDING_SET 0xf20
#define GICD_ENABLE 0x1
+#define GICD_ENABLE_GRP1 BIT(1)
+
#define GICD_DISABLE 0x0
#define GICD_INT_ACTLOW_LVLTRIG 0x0
#define GICD_INT_EN_CLR_X32 0xffffffff