ARM: gic: Add spinlocks for SGIR/AIR/EOI for 8625
On 8625 due to bug in AHB MUX on hready, back to back write followed
by read (from any CPU) on QGIC2 registers (SGIR(WO) ,IAR(RO) and
write on EOI(WO)) cause the read data to get corrupted on AHB bus.
Due to this whenever a valid irq has occurs, and dispatched to the cpu
interface but still cpu reads the IAR as 0x0 and that particular IRQ
becomes active.
But due to incorrect irq id (read as 0x0), IRQ handler will not do
proper EOI for that particular interrupt and thus it gets trapped
in the active state.
Below is the qgic register dump from CPU-1, in this particular case
we see that SGI-3 is not getting clear as cpu reads this as 0x0.
AZSD:C0000200| 00240008 00000000 00008000 00000000 00000000 00000000
00000000 00000000 ..$.............................
AZSD:C0000280| 00240008 00000000 00008000 00000000 00000000 00000000
00000000 00000000 ..$.............................
AZSD:C0000300| 00040008 00000000 00000000 00000000 00000000 00000000
00000000 00000000 ................................
AZSD:C0000380| 00040008 00000000 00000000 00000000 00000000 00000000
00000000 00000000 ................................
As the interrupt gets trapped, no other interrupt received on the core is
services any more causing the system hang.
CRs-Fixed: 349219
Change-Id: Icad2c65114377a08984b1032566cfba811bb4ca8
Signed-off-by: Taniya Das <tdas@codeaurora.org>
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c
index 4e43cb2..1107412 100644
--- a/arch/arm/common/gic.c
+++ b/arch/arm/common/gic.c
@@ -214,6 +214,9 @@
void __iomem *base = gic_data_dist_base(gic);
for (i = 0; i * 32 < gic->max_irq; i++) {
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_lock(&irq_controller_lock);
+#endif
gic->enabled_irqs[i]
= readl_relaxed(base + GIC_DIST_ENABLE_SET + i * 4);
/* disable all of them */
@@ -221,6 +224,9 @@
/* enable the wakeup set */
writel_relaxed(gic->wakeup_irqs[i],
base + GIC_DIST_ENABLE_SET + i * 4);
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_unlock(&irq_controller_lock);
+#endif
}
mb();
return 0;
@@ -269,11 +275,17 @@
gic_show_resume_irq(gic);
for (i = 0; i * 32 < gic->max_irq; i++) {
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_lock(&irq_controller_lock);
+#endif
/* disable all of them */
writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4);
/* enable the enabled set */
writel_relaxed(gic->enabled_irqs[i],
base + GIC_DIST_ENABLE_SET + i * 4);
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_unlock(&irq_controller_lock);
+#endif
}
mb();
}
@@ -306,8 +318,13 @@
gic_arch_extn.irq_eoi(d);
raw_spin_unlock(&irq_controller_lock);
}
-
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_lock(&irq_controller_lock);
+#endif
writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI);
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_unlock(&irq_controller_lock);
+#endif
}
static int gic_set_type(struct irq_data *d, unsigned int type)
@@ -430,7 +447,13 @@
void __iomem *cpu_base = gic_data_cpu_base(gic);
do {
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_lock(&irq_controller_lock);
+#endif
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_unlock(&irq_controller_lock);
+#endif
irqnr = irqstat & ~0x1c00;
if (likely(irqnr > 15 && irqnr < 1021)) {
@@ -439,7 +462,13 @@
continue;
}
if (irqnr < 16) {
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_lock(&irq_controller_lock);
+#endif
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_unlock(&irq_controller_lock);
+#endif
#ifdef CONFIG_SMP
handle_IPI(irqnr, regs);
#endif
@@ -588,6 +617,9 @@
* Deal with the banked PPI and SGI interrupts - disable all
* PPI interrupts, ensure all SGI interrupts are enabled.
*/
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_lock(&irq_controller_lock);
+#endif
writel_relaxed(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR);
writel_relaxed(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET);
@@ -607,6 +639,9 @@
writel_relaxed(0xF, base + GIC_CPU_CTRL);
else
writel_relaxed(1, base + GIC_CPU_CTRL);
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_unlock(&irq_controller_lock);
+#endif
mb();
}
@@ -944,6 +979,9 @@
int cpu;
unsigned long sgir;
unsigned long map = 0;
+#ifdef CONFIG_ARCH_MSM8625
+ unsigned long flags;
+#endif
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
@@ -959,9 +997,15 @@
*/
dsb();
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_lock_irqsave(&irq_controller_lock, flags);
+#endif
/* this always happens on GIC0 */
writel_relaxed(sgir,
gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
+#ifdef CONFIG_ARCH_MSM8625
+ raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
+#endif
mb();
}
#endif