summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Williams <scwilliams@nvidia.com>2011-07-12 18:05:12 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:46:54 -0800
commit108c397e52eed8e8f6fa510cdba20d0c7665de21 (patch)
tree813f43fda7ad5859e920f6d89eb6f0d71bb1f3bb
parent27a435eb62ef122692e904cbd2959ac191bc43cc (diff)
ARM: tegra: Add Tegra GIC extensions
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
-rw-r--r--arch/arm/mach-tegra/Makefile1
-rw-r--r--arch/arm/mach-tegra/cpuidle-t2.c7
-rw-r--r--arch/arm/mach-tegra/cpuidle.h9
-rw-r--r--arch/arm/mach-tegra/gic.c129
-rw-r--r--arch/arm/mach-tegra/gic.h49
-rw-r--r--arch/arm/mach-tegra/include/mach/irqs.h8
-rw-r--r--arch/arm/mach-tegra/pm.h2
7 files changed, 192 insertions, 13 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 9e909b6dbd8e..baac685f590a 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -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
diff --git a/arch/arm/mach-tegra/cpuidle-t2.c b/arch/arm/mach-tegra/cpuidle-t2.c
index 326d4e8ec215..c4f648e41eb2 100644
--- a/arch/arm/mach-tegra/cpuidle-t2.c
+++ b/arch/arm/mach-tegra/cpuidle-t2.c
@@ -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) {
diff --git a/arch/arm/mach-tegra/cpuidle.h b/arch/arm/mach-tegra/cpuidle.h
index ae3386139d38..5d0c3d695342 100644
--- a/arch/arm/mach-tegra/cpuidle.h
+++ b/arch/arm/mach-tegra/cpuidle.h
@@ -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
index 000000000000..50cecc4eed64
--- /dev/null
+++ b/arch/arm/mach-tegra/gic.c
@@ -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
index 000000000000..94dab6e581af
--- /dev/null
+++ b/arch/arm/mach-tegra/gic.h
@@ -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_ */
diff --git a/arch/arm/mach-tegra/include/mach/irqs.h b/arch/arm/mach-tegra/include/mach/irqs.h
index e7c1ad3b7991..d25a0aa28862 100644
--- a/arch/arm/mach-tegra/include/mach/irqs.h
+++ b/arch/arm/mach-tegra/include/mach/irqs.h
@@ -166,7 +166,9 @@
#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
@@ -352,7 +354,9 @@
#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
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index ff8480bc76ca..9a78f620f571 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -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,