From a4e199454b3897502a64eb2c05427d636e2e9282 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 18 Aug 2014 10:28:51 +0100 Subject: ARM: cobble together FIQ backtracing Cobble the FIQ backtracing together, some of this patch is based on some of Daniel Thompson's work. Experimental, and not for submission yet. Signed-off-by: Russell King --- arch/arm/kernel/traps.c | 4 +++ drivers/irqchip/irq-gic.c | 59 ++++++++++++++++++++++++++++++++++++++--- include/linux/irqchip/arm-gic.h | 2 ++ 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 #include #include +#include #include #include @@ -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 -- cgit