ARM: tegra: Add Tegra GIC extensions
Scott Williams [Wed, 13 Jul 2011 01:05:12 +0000 (18:05 -0700)]
Implement extensions to the standard ARM GIC API for Tegra3 power management.

Change-Id: If8b2ce2b366e48bb5ca82d3de2acab1fd0a81bb9
Signed-off-by: Scott Williams <scwilliams@nvidia.com>
Signed-off-by: Dan Willemsen <dwillemsen@nvidia.com>

Rebase-Id: Rd7527cd57edf054c871f5d04d7e9185643f79843

arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/cpuidle-t2.c
arch/arm/mach-tegra/cpuidle.h
arch/arm/mach-tegra/gic.c [new file with mode: 0644]
arch/arm/mach-tegra/gic.h [new file with mode: 0644]
arch/arm/mach-tegra/include/mach/irqs.h
arch/arm/mach-tegra/pm.h

index 9e909b6..baac685 100644 (file)
@@ -15,6 +15,7 @@ obj-y                                   += delay.o
 obj-y                                   += powergate.o
 obj-y                                   += pm.o
 obj-$(CONFIG_PM_SLEEP)                  += pm-irq.o
+obj-y                                   += gic.o
 obj-y                                   += sleep.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += sleep-t2.o
 obj-y                                   += fuse.o
index 326d4e8..c4f648e 100644 (file)
@@ -45,6 +45,7 @@
 #include <mach/irqs.h>
 
 #include "cpuidle.h"
+#include "gic.h"
 #include "pm.h"
 #include "sleep.h"
 
@@ -187,8 +188,10 @@ static int tegra2_idle_lp2_last(struct cpuidle_device *dev,
 
                if (tegra_idle_lp2_last(sleep_time, 0) == 0)
                        sleep_completed = true;
-               else
-                       idle_stats.lp2_int_count[tegra_pending_interrupt()]++;
+               else {
+                       int irq = tegra_gic_pending_interrupt();
+                       idle_stats.lp2_int_count[irq]++;
+               }
        }
 
        for_each_online_cpu(i) {
index ae33861..5d0c3d6 100644 (file)
@@ -75,15 +75,6 @@ static inline int tegra_lp2_debug_show(struct seq_file *s, void *data)
 }
 #endif
 
-static inline int tegra_pending_interrupt(void)
-{
-       void __iomem *gic_cpu = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100);
-       u32 reg = readl(gic_cpu + 0x18);
-       reg &= 0x3FF;
-
-       return reg;
-}
-
 #ifdef CONFIG_CPU_IDLE
 void tegra_lp2_in_idle(bool enable);
 #else
diff --git a/arch/arm/mach-tegra/gic.c b/arch/arm/mach-tegra/gic.c
new file mode 100644 (file)
index 0000000..50cecc4
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010-2011, NVIDIA Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpumask.h>     /* Required by asm/hardware/gic.h */
+#include <linux/io.h>
+#include <linux/irqnr.h>
+
+#include <asm/hardware/gic.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include "gic.h"
+#include "pm.h"
+
+#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
+static void __iomem *gic_cpu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100);
+
+void tegra_gic_cpu_disable(void)
+{
+       writel(0, gic_cpu_base + GIC_CPU_CTRL);
+}
+
+void tegra_gic_cpu_enable(void)
+{
+       writel(1, gic_cpu_base + GIC_CPU_CTRL);
+}
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+void tegra_gic_pass_through_disable(void)
+{
+       u32 val = readl(gic_cpu_base + GIC_CPU_CTRL);
+       val |= 2; /* enableNS = disable GIC pass through */
+       writel(val, gic_cpu_base + GIC_CPU_CTRL);
+}
+
+#endif
+#endif
+
+#if defined(CONFIG_PM_SLEEP)
+
+int tegra_gic_pending_interrupt(void)
+{
+       u32 irq = readl(gic_cpu_base + GIC_CPU_HIGHPRI);
+       irq &= 0x3FF;
+
+       return irq;
+}
+
+#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)
+{
+       writel(0, gic_dist_base + GIC_DIST_CTRL);
+}
+
+void tegra_gic_dist_enable(void)
+{
+       writel(1, gic_dist_base + GIC_DIST_CTRL);
+}
+
+void tegra_gic_disable_affinity(void)
+{
+       unsigned int i;
+
+       BUG_ON(is_lp_cluster());
+
+       /* The GIC distributor TARGET register is one byte per IRQ. */
+       for (i = 32; i < INT_GIC_NR; i += 4) {
+               /* Save the affinity. */
+               gic_affinity[i/4] = __raw_readl(gic_dist_base +
+                                               GIC_DIST_TARGET + i);
+
+               /* Force this interrupt to CPU0. */
+               __raw_writel(0x01010101, gic_dist_base + GIC_DIST_TARGET + i);
+       }
+
+       wmb();
+}
+
+void tegra_gic_restore_affinity(void)
+{
+       unsigned int i;
+
+       BUG_ON(is_lp_cluster());
+
+       /* The GIC distributor TARGET register is one byte per IRQ. */
+       for (i = 32; i < INT_GIC_NR; i += 4) {
+#ifdef CONFIG_BUG
+               u32 reg = __raw_readl(gic_dist_base + GIC_DIST_TARGET + i);
+               if (reg & 0xFEFEFEFE)
+                       panic("GIC affinity changed!");
+#endif
+               /* Restore this interrupt's affinity. */
+               __raw_writel(gic_affinity[i/4], gic_dist_base +
+                            GIC_DIST_TARGET + i);
+       }
+
+       wmb();
+}
+
+void tegra_gic_affinity_to_cpu0(void)
+{
+       unsigned int i;
+
+       BUG_ON(is_lp_cluster());
+
+       for (i = 32; i < INT_GIC_NR; i += 4)
+               __raw_writel(0x01010101, gic_dist_base + GIC_DIST_TARGET + i);
+       wmb();
+}
+#endif
+#endif
diff --git a/arch/arm/mach-tegra/gic.h b/arch/arm/mach-tegra/gic.h
new file mode 100644 (file)
index 0000000..94dab6e
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * arch/arm/mach-tegra/include/mach/gic.h
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MACH_TEGRA_GIC_H_
+#define _MACH_TEGRA_GIC_H_
+
+#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
+
+void tegra_gic_cpu_disable(void);
+void tegra_gic_cpu_enable(void);
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+void tegra_gic_pass_through_disable(void);
+
+#endif
+#endif
+
+
+#if defined(CONFIG_PM_SLEEP)
+
+int tegra_gic_pending_interrupt(void);
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+
+void tegra_gic_dist_disable(void);
+void tegra_gic_dist_enable(void);
+
+void tegra_gic_disable_affinity(void);
+void tegra_gic_restore_affinity(void);
+void tegra_gic_affinity_to_cpu0(void);
+
+#endif
+#endif
+
+#endif /* _MACH_TEGRA_GIC_H_ */
index e7c1ad3..d25a0aa 100644 (file)
 #define INT_QUAD_RES_30                        (INT_QUAD_BASE + 30)
 #define INT_QUAD_RES_31                        (INT_QUAD_BASE + 31)
 
-#define INT_MAIN_NR                    (INT_QUAD_BASE + 32 - INT_PRI_BASE)
+#define INT_GIC_NR                     (INT_QUAD_BASE + 32)
+
+#define INT_MAIN_NR                    (INT_GIC_NR - INT_PRI_BASE)
 
 #define INT_SYNCPT_THRESH_BASE         (INT_QUAD_BASE + 32)
 #define INT_SYNCPT_THRESH_NR           32
 #define INT_QUINT_RES_30               (INT_QUINT_BASE + 30)
 #define INT_QUINT_RES_31               (INT_QUINT_BASE + 31)
 
-#define INT_MAIN_NR                    (INT_QUINT_BASE + 32 - INT_PRI_BASE)
+#define INT_GIC_NR                     (INT_QUINT_BASE + 32)
+
+#define INT_MAIN_NR                    (INT_GIC_NR - INT_PRI_BASE)
 
 #define INT_SYNCPT_THRESH_BASE         (INT_QUINT_BASE + 32)
 #define INT_SYNCPT_THRESH_NR           32
index ff8480b..9a78f62 100644 (file)
@@ -23,6 +23,8 @@
 #define _MACH_TEGRA_PM_H_
 
 #include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/errno.h>
 
 enum tegra_suspend_mode {
        TEGRA_SUSPEND_NONE = 0,