arm: tegra: add FIQ WAR for t11x non-secure
Xin Xie [Wed, 25 Sep 2013 01:04:29 +0000 (18:04 -0700)]
On Tegra T11x WDT FIQ is not routed to the GIC, so we cannot use Tegra
WDT for soft-hang debugging.

This WAR is to configure GIC group0 to generate the FIQ to CPU, and only
let WDT routed to GIC group0. All other normal IRQs will use GIC group1.
This WAR is only applicable to non-secure OS build; secure OS build will
treat FIQ as secure interrupt for enterring monitor.

This patch is based on previous work of Lucas Dai and Bo Yan.

bug 1277869
bug 1357562

Change-Id: Ie9a05f9f2974d2b9fb2d09f7847639e553917cb7
Signed-off-by: Xin Xie <xxie@nvidia.com>
Reviewed-on: http://git-master/r/268797
Reviewed-by: Mrutyunjay Sawant <msawant@nvidia.com>
Tested-by: Mrutyunjay Sawant <msawant@nvidia.com>

arch/arm/mach-tegra/Kconfig
arch/arm/mach-tegra/fiq.c
arch/arm/mach-tegra/gic.c
arch/arm/mach-tegra/gic.h
arch/arm/mach-tegra/include/mach/fiq.h

index f57be85..92c006a 100644 (file)
@@ -133,6 +133,7 @@ config ARCH_TEGRA_11x_SOC
        select TEGRA_THERMAL_THROTTLE_EXACT_FREQ
        select ARM_ERRATA_799270
        select ARM_ERRATA_798181
+       select TEGRA_WDT_FIQ_WAR if !TRUSTED_FOUNDATIONS && TEGRA_FIQ_DEBUGGER
        help
          Support for NVIDIA Tegra 11x family of SoCs, based upon the
          ARM Cortex-A15MP CPU
@@ -423,6 +424,16 @@ config TEGRA_FIQ_DEBUGGER
        help
          Enables the FIQ serial debugger on Tegra
 
+config TEGRA_WDT_FIQ_WAR
+       bool "Enable FIQ WAR on Tegra"
+       depends on TEGRA_FIQ_DEBUGGER
+       help
+         Tegra chip T114 and T124 don't have FIQ connected to GIC. This
+         option configure GIC group0 generating FIQ only, and let GIC
+         group1 handle normal IRQs. Note this option can be only enabled
+         for system running no secure OS, because secure OS use group0
+         specifically as monitor trap.
+
 config TEGRA_PTM
        bool "Enable PTM debugger on Tegra"
        default n
index 19f9c05..5dab815 100644 (file)
@@ -1,9 +1,11 @@
 /*
  * Copyright (C) 2010 Google, Inc.
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
  *
  * Author:
  *     Brian Swetland <swetland@google.com>
  *     Iliyan Malchev <malchev@google.com>
+ *     Lucas Dai <lucasd@nvidia.com>
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -28,6 +30,7 @@
 #include <mach/iomap.h>
 #include <mach/fiq.h>
 
+#include "gic.h"
 #include "board.h"
 
 #define ICTLR_CPU_IER          0x20
 #define ICTLR_CPU_IER_CLR      0x28
 #define ICTLR_CPU_IEP_CLASS    0x2C
 
+#define ICTLR_COP_IER          0x30
+#define ICTLR_COP_IER_SET      0x34
+#define ICTLR_COP_IER_CLR      0x38
+#define ICTLR_COP_IEP_CLASS    0x3c
+
+
 #define FIRST_LEGACY_IRQ       32
 
 static void __iomem *ictlr_reg_base[] = {
@@ -44,6 +53,18 @@ static void __iomem *ictlr_reg_base[] = {
        IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE),
 };
 
+static bool tegra_check_cop_irq_enabled(unsigned int irq)
+{
+       void __iomem *base;
+       pr_debug("%s: %d\n", __func__, irq);
+
+       irq -= FIRST_LEGACY_IRQ;
+       base = ictlr_reg_base[irq >> 5];
+       if ((1 << (irq & 31)) & readl(base + ICTLR_COP_IER))
+               return true;
+       return false;
+}
+
 static void tegra_legacy_select_fiq(unsigned int irq, bool fiq)
 {
        void __iomem *base;
@@ -82,18 +103,53 @@ static void tegra_fiq_unmask(struct irq_data *d)
 
 void tegra_fiq_enable(int irq)
 {
-       void __iomem *base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100);
-       /* enable FIQ */
-       u32 val = readl(base + GIC_CPU_CTRL);
-       val &= ~8; /* pass FIQs through */
-       val |= 2; /* enableNS */
-       writel(val, base + GIC_CPU_CTRL);
-       tegra_legacy_select_fiq(irq, true);
-       tegra_fiq_unmask(irq_get_irq_data(irq));
+       u32 val;
+       void __iomem *base;
+
+       base = tegra_get_gic_cpu_base();
+       if (!has_fiq_gic_war()) {
+               /* enable FIQ */
+               val = readl(base + GIC_CPU_CTRL);
+               val &= ~GIC_CPU_CTRL_FIQ_EN; /* pass FIQs through */
+               val |= GIC_CPU_CTRL_ENABLE_GRP1; /* enableNS */
+               writel(val, base + GIC_CPU_CTRL);
+               tegra_legacy_select_fiq(irq, true);
+               tegra_fiq_unmask(irq_get_irq_data(irq));
+       } else {
+               /*
+                * For Tegra FIQ WAR, we cannot use FIQ to CPU route directly.
+                * We are relying on GIC Group 0 (which this IRQ will be
+                * routed to) to generate FIQ, we need check if AVP also using
+                * this IRQ.
+                */
+               BUG_ON(tegra_check_cop_irq_enabled(irq));
+               enable_irq(irq);
+       }
+}
+
+void tegra_fiq_ack(unsigned int fiq)
+{
+       if (has_fiq_gic_war())
+               /*
+                * We will continuously get into the FIQ handler if we don't
+                * do ACK; if we do ACK we will hit into FIQ handler once per
+                * one FIQ:
+                *  * For UART FIQ user interactive debugger, we might want make
+                *    FIQ handler continuously running to check the UART input,
+                *    so we don't need do ACK in this case;
+                *  * If we just want use FIQ debugger dump the stack once, we
+                *    need do the FIQ ACK by this function.
+                */
+               readl_relaxed(tegra_get_gic_cpu_base() + GIC_CPU_INTACK);
 }
 
 void tegra_fiq_disable(int irq)
 {
-       tegra_fiq_mask(irq_get_irq_data(irq));
-       tegra_legacy_select_fiq(irq, false);
+       if (!has_fiq_gic_war()) {
+               tegra_fiq_mask(irq_get_irq_data(irq));
+               tegra_legacy_select_fiq(irq, false);
+       } else {
+               disable_irq(irq);
+       }
 }
+
index 44e017c..6c5a8ef 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2012, NVIDIA Corporation
+ * Copyright (C) 2010-2013, NVIDIA CORPORATION.  All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -22,6 +22,7 @@
 
 #include <mach/iomap.h>
 #include <mach/irqs.h>
+#include <mach/fiq.h>
 
 #include "gic.h"
 #include "pm.h"
@@ -30,6 +31,7 @@
 
 void __iomem *tegra_gic_cpu_base;
 static u32 gic_version;
+static void __iomem *gic_dist_base = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE);
 
 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
 
@@ -50,11 +52,37 @@ void tegra_gic_cpu_disable(bool disable_pass_through)
 
 void tegra_gic_cpu_enable(void)
 {
-       writel(1, tegra_gic_cpu_base + GIC_CPU_CTRL);
+       writel(gic_get_cpu_ctrl_val(), tegra_gic_cpu_base + GIC_CPU_CTRL);
 }
 
 #endif
 
+/*
+ * if we use Tegra FIQ WAR, we use this function to enable the IRQ on GIC
+ * group 0. Using the WAR, group0 will generate the FIQ only to CPUs. We also
+ * need set the group0 IRQ to highest priority  higher than GIC group1's IRQ's
+ * priority.
+ */
+static void tegra_gic_set_group0_fiq(int fiq)
+{
+       u32 val = 0;
+       int group_nr;
+       int fiq_offset;
+
+       if (!has_fiq_gic_war())
+               return;
+
+       group_nr = (fiq / 32) * 4;
+       val = readl_relaxed(gic_dist_base + GIC_DIST_IGROUP + group_nr);
+       val &= ~(1 << (fiq % 32));
+       writel_relaxed(val, gic_dist_base + GIC_DIST_IGROUP + group_nr);
+
+       fiq_offset = fiq / 4 * 4;
+       val = readl_relaxed(gic_dist_base + GIC_DIST_PRI + fiq_offset);
+       val &= ~(0xff << ((fiq % 4) * 8));
+       writel_relaxed(val, gic_dist_base + GIC_DIST_PRI + fiq_offset);
+}
+
 #if defined(CONFIG_PM_SLEEP)
 
 int tegra_gic_pending_interrupt(void)
@@ -67,7 +95,6 @@ int tegra_gic_pending_interrupt(void)
 
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
 
-static void __iomem *gic_dist_base = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE);
 static u32 gic_affinity[INT_GIC_NR/4];
 
 void tegra_gic_dist_disable(void)
@@ -77,7 +104,10 @@ void tegra_gic_dist_disable(void)
 
 void tegra_gic_dist_enable(void)
 {
-       writel(1, gic_dist_base + GIC_DIST_CTRL);
+       writel(has_fiq_gic_war() ?
+               GIC_DIST_CTRL_EN_GRP0 | GIC_DIST_CTRL_EN_GRP1 :
+               GIC_DIST_CTRL_EN_GRP0,
+               gic_dist_base + GIC_DIST_CTRL);
 }
 
 void tegra_gic_disable_affinity(void)
@@ -138,6 +168,11 @@ static int tegra_gic_notifier(struct notifier_block *self, unsigned long cmd, vo
        case CPU_PM_ENTER:
                writel(0x1E0, tegra_gic_cpu_base + GIC_CPU_CTRL);
                break;
+       case CPU_CLUSTER_PM_ENTER_FAILED:
+       case CPU_CLUSTER_PM_EXIT:
+               if (has_fiq_gic_war())
+                       tegra_gic_set_group0_fiq(TEGRA_FIQ_WAR_FIQ_NR);
+               break;
        }
 
        return NOTIFY_OK;
@@ -153,7 +188,7 @@ u32 tegra_gic_version(void)
        return gic_version;
 }
 
-void __init tegra_gic_init(bool is_dt)
+void __iomem *tegra_get_gic_cpu_base(void)
 {
        u32 midr;
 
@@ -162,9 +197,14 @@ void __init tegra_gic_init(bool is_dt)
        midr = (midr & 0x0000FFF0) >> 4;
 
        if (midr == ARM_VERSION_CORTEX_A15)
-               tegra_gic_cpu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x2000);
+               return IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x2000);
        else
-               tegra_gic_cpu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100);
+               return IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100);
+}
+
+void __init tegra_gic_init(bool is_dt)
+{
+       tegra_gic_cpu_base = tegra_get_gic_cpu_base();
 
        if (!is_dt)
                gic_init(0, 29, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE),
@@ -177,4 +217,6 @@ void __init tegra_gic_init(bool is_dt)
        if (gic_version == GIC_V2)
                cpu_pm_register_notifier(&tegra_gic_notifier_block);
 #endif
+       if (has_fiq_gic_war())
+               tegra_gic_set_group0_fiq(TEGRA_FIQ_WAR_FIQ_NR);
 }
index d5ea028..fe746e2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/include/mach/gic.h
  *
- * Copyright (C) 2010-2012 NVIDIA Corporation
+ * Copyright (C) 2010-2013 NVIDIA CORPORATION.  All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -43,6 +43,7 @@ void tegra_gic_affinity_to_cpu0(void);
 #endif
 #endif
 
+void __iomem *tegra_get_gic_cpu_base(void);
 u32 tegra_gic_version(void);
 void __init tegra_gic_init(bool is_dt);
 
index 17625fa..6ea418b 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2010 Google, Inc.
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
  *
  * Author:
  *     Iliyan Malchev <malchev@google.com>
@@ -22,4 +23,6 @@
 void tegra_fiq_enable(int n);
 void tegra_fiq_disable(int n);
 
+#define TEGRA_FIQ_WAR_FIQ_NR INT_WDT_AVP
+
 #endif