arm: add support for GIC FIQ WAR
Xin Xie [Wed, 25 Sep 2013 00:51:20 +0000 (17:51 -0700)]
The FIQ WAR requires all the IRQs using the GIC group1, and let the FIQ
use GIC group0 only. This patch will set all the normal IRQs to group1
if GIC WAR is enabled.

bug 1277869
bug 1357562

Change-Id: I8993445850d2221f93b59d18e45d77d81903a616
Signed-off-by: Xin Xie <xxie@nvidia.com>
Reviewed-on: http://git-master/r/278583
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Bo Yan <byan@nvidia.com>

arch/arm/common/gic.c
arch/arm/include/asm/hardware/gic.h

index 4824d57..ec0cf1c 100644 (file)
@@ -2,6 +2,7 @@
  *  linux/arch/arm/common/gic.c
  *
  *  Copyright (C) 2002 ARM Limited, All Rights Reserved.
+ *  Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -394,7 +395,16 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
        for (i = 32; i < gic_irqs; i += 32)
                writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
 
-       writel_relaxed(1, base + GIC_DIST_CTRL);
+       if (has_fiq_gic_war()) {
+               /* Set PPI and SGI in group 1 */
+               for (i = 0; i < gic_irqs; i += 32)
+                       writel_relaxed(~0UL, base + GIC_DIST_IGROUP +
+                                                       i * 4 / 32);
+               writel_relaxed(GIC_DIST_CTRL_EN_GRP0 | GIC_DIST_CTRL_EN_GRP1,
+                               base + GIC_DIST_CTRL);
+       } else {
+               writel_relaxed(GIC_DIST_CTRL_EN_GRP0, base + GIC_DIST_CTRL);
+       }
 }
 
 static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
@@ -417,7 +427,8 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
                writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);
 
        writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
-       writel_relaxed(1, base + GIC_CPU_CTRL);
+
+       writel_relaxed(gic_get_cpu_ctrl_val(), base + GIC_CPU_CTRL);
 }
 
 #ifdef CONFIG_CPU_PM
@@ -495,7 +506,17 @@ static void gic_dist_restore(unsigned int gic_nr)
                writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
                        dist_base + GIC_DIST_ENABLE_SET + i * 4);
 
-       writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+       if (has_fiq_gic_war()) {
+               /* Set PPI and SGI in group 1 */
+               for (i = 0; i < gic_irqs; i += 32)
+                       writel_relaxed(~0UL, dist_base +
+                                       GIC_DIST_IGROUP + i * 4 / 32);
+               writel_relaxed(GIC_DIST_CTRL_EN_GRP0 | GIC_DIST_CTRL_EN_GRP1,
+                               dist_base + GIC_DIST_CTRL);
+       } else {
+               writel_relaxed(GIC_DIST_CTRL_EN_GRP0,
+                               dist_base + GIC_DIST_CTRL);
+       }
 }
 
 static void gic_cpu_save(unsigned int gic_nr)
@@ -551,8 +572,13 @@ static void gic_cpu_restore(unsigned int gic_nr)
        for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
                writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4);
 
+       if (has_fiq_gic_war())
+               writel_relaxed(~0UL, dist_base + GIC_DIST_IGROUP);
+
        writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
-       writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
+
+       writel_relaxed(gic_get_cpu_ctrl_val(), cpu_base + GIC_CPU_CTRL);
+
 }
 
 static int gic_notifier(struct notifier_block *self, unsigned long cmd,        void *v)
@@ -736,7 +762,17 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
 
 void __cpuinit gic_secondary_init(unsigned int gic_nr)
 {
+       void __iomem *dist_base;
+
        BUG_ON(gic_nr >= MAX_GIC_NR);
+       dist_base = gic_data_dist_base(&gic_data[gic_nr]);
+
+       if (has_fiq_gic_war())
+               /*
+                * First 32 IRQs is private per-CPU mapping, we need set it to
+                * use GIC group1 whenever a secondary CPU come online.
+                */
+               writel_relaxed(~0UL, dist_base + GIC_DIST_IGROUP);
 
        gic_cpu_init(&gic_data[gic_nr]);
 }
@@ -757,8 +793,15 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
         */
        dsb();
 
-       /* this always happens on GIC0 */
-       writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
+       writel_relaxed(map << 16 | irq |
+                       /*
+                        * If use GIC WAR and kernel has secure access, we use
+                        * GIC group 1 to send SGI. For kernel don't have
+                        * secure access (as under SecureOS build), SGI will
+                        * send through the GIC group 1 regardless.
+                        */
+                       (has_fiq_gic_war() ? GIC_DIST_SOFTINT_NSATT : 0),
+                       gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
 }
 #endif
 
index 4b1ce6c..7d3ddf7 100644 (file)
@@ -2,6 +2,7 @@
  *  arch/arm/include/asm/hardware/gic.h
  *
  *  Copyright (C) 2002 ARM Limited, All Rights Reserved.
+ *  Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include <linux/compiler.h>
 
 #define GIC_CPU_CTRL                   0x00
+#define GIC_CPU_CTRL_EOI_MODE_NS       (1 << 10)
+#define GIC_CPU_CTRL_EOI_MODE_S                (1 << 9)
+#define GIC_CPU_CTRL_IRQ_BYP_DIS_GRP_1 (1 << 8)
+#define GIC_CPU_CTRL_FIQ_BYP_DIS_GRP_1 (1 << 7)
+#define GIC_CPU_CTRL_IRQ_BYP_DIS_GRP_0 (1 << 6)
+#define GIC_CPU_CTRL_FIQ_BYP_DIS_GRP_0 (1 << 5)
+#define GIC_CPU_CTRL_CBPR              (1 << 4)
+#define GIC_CPU_CTRL_FIQ_EN            (1 << 3)
+#define GIC_CPU_CTRL_ACK_CTL           (1 << 2)
+#define GIC_CPU_CTRL_ENABLE_GRP1       (1 << 1)
+#define GIC_CPU_CTRL_ENABLE_GRP0       (1 << 0)
 #define GIC_CPU_PRIMASK                        0x04
 #define GIC_CPU_BINPOINT               0x08
 #define GIC_CPU_INTACK                 0x0c
 #define GIC_CPU_HIGHPRI                        0x18
 
 #define GIC_DIST_CTRL                  0x000
+#define GIC_DIST_CTRL_EN_GRP1          (1 << 1)
+#define GIC_DIST_CTRL_EN_GRP0          (1 << 0)
 #define GIC_DIST_CTR                   0x004
+#define GIC_DIST_IGROUP                        0x080
 #define GIC_DIST_ENABLE_SET            0x100
 #define GIC_DIST_ENABLE_CLEAR          0x180
 #define GIC_DIST_PENDING_SET           0x200
@@ -31,6 +46,7 @@
 #define GIC_DIST_TARGET                        0x800
 #define GIC_DIST_CONFIG                        0xc00
 #define GIC_DIST_SOFTINT               0xf00
+#define GIC_DIST_SOFTINT_NSATT         (1 << 15)
 
 #ifndef __ASSEMBLY__
 #include <linux/irqdomain.h>
@@ -52,6 +68,34 @@ static inline void gic_init(unsigned int nr, int start,
        gic_init_bases(nr, start, dist, cpu, 0, NULL);
 }
 
+static inline bool has_fiq_gic_war(void)
+{
+#if defined(CONFIG_TEGRA_WDT_FIQ_WAR)
+       /*
+        * Tegra 11x device can only pass the FIQ using the GIC group 0 if
+        * non-secure OS is enabled.
+        */
+       return true;
+#else
+       return false;
+#endif
+}
+
+static inline unsigned int gic_get_cpu_ctrl_val(void)
+{
+       unsigned int reg;
+
+       if (has_fiq_gic_war())
+               /*
+                * We use Group 0 for the FIQ and Group 1 for IRQ. ACK_CTL bit
+                * is set so we can read back IRQ # correctly for group1 IRQ.
+                */
+               reg = GIC_CPU_CTRL_ENABLE_GRP0 | GIC_CPU_CTRL_ENABLE_GRP1 |
+                     GIC_CPU_CTRL_ACK_CTL | GIC_CPU_CTRL_FIQ_EN;
+       else
+               reg = GIC_CPU_CTRL_ENABLE_GRP0;
+       return reg;
+}
 #endif
 
 #endif