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 --- drivers/irqchip/irq-gic.c | 59 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip') 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); } -- cgit