summaryrefslogtreecommitdiff
path: root/arch/arm
diff options
context:
space:
mode:
authorScott Williams <scwilliams@nvidia.com>2011-07-21 18:24:34 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:46:57 -0800
commit3b3b17486113ac2e5af1f6d2d555f1315cb40434 (patch)
treea5943868a0be1de16024cba4c9e310b934847171 /arch/arm
parentedd6779f31582fb6acdb59f58dfb4471fa4078d6 (diff)
ARM: tegra: power: Save TWD registers on cluster transitions
The ARM timer/watchdog (TWD) registers do not need saving on LP2 transitions resulting from real idle events. They do still need saving/restoring on transitions resulting from cluster control operations. Change-Id: I459b25b98c256a52a2e9e68fb63dbf2681e90b07 Signed-off-by: Scott Williams <scwilliams@nvidia.com> Signed-off-by: Dan Willemsen <dwillemsen@nvidia.com> Rebase-Id: R3c7c0cae8b847af6355fa1fa0b8bf5bf1e1efef5
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-tegra/cpuidle.c3
-rw-r--r--arch/arm/mach-tegra/pm.c26
2 files changed, 23 insertions, 6 deletions
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
index 820eba972a1b..73ed6d4e3a2f 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -135,8 +135,11 @@ static int tegra_idle_enter_lp2(struct cpuidle_device *dev,
tegra_cpu_idle_stats_lp2_ready(dev->cpu);
+ /* Shut down the CPU local timer and switch timekeeping to the
+ global system timer. */
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
tegra_idle_lp2(dev, state);
+ /* Switch timekeeping back to the CPU local timer. */
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
exit = ktime_sub(ktime_get(), enter);
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 659db1d5d726..273aa178bf1d 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -82,6 +82,8 @@ struct suspend_context {
u32 mc[3];
u8 uart[5];
+
+ struct tegra_twd_context twd;
};
#ifdef CONFIG_PM_SLEEP
@@ -277,7 +279,7 @@ static void set_power_timers(unsigned long us_on, unsigned long us_off,
*
* Always called on CPU 0.
*/
-static void restore_cpu_complex(void)
+static void restore_cpu_complex(u32 mode)
{
int cpu = smp_processor_id();
unsigned int reg;
@@ -332,6 +334,11 @@ static void restore_cpu_complex(void)
reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */
reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */
flowctrl_writel(reg, FLOW_CTRL_CPU_CSR(cpu));
+
+ /* If an immedidate cluster switch is being perfomed, restore the
+ local timer registers. See save_cpu_complex() for the details. */
+ if (mode & (TEGRA_POWER_CLUSTER_MASK | TEGRA_POWER_CLUSTER_IMMEDIATE))
+ tegra_twd_resume(&tegra_sctx.twd);
}
/*
@@ -342,7 +349,7 @@ static void restore_cpu_complex(void)
*
* Must always be called on cpu 0.
*/
-static void suspend_cpu_complex(void)
+static void suspend_cpu_complex(u32 mode)
{
int cpu = smp_processor_id();
unsigned int reg;
@@ -363,6 +370,13 @@ static void suspend_cpu_complex(void)
tegra_sctx.pllp_misc = readl(clk_rst + CLK_RESET_PLLP_MISC);
tegra_sctx.cclk_divider = readl(clk_rst + CLK_RESET_CCLK_DIVIDER);
+ /* If an immedidate cluster switch is being perfomed, save the
+ local timer registers. For calls resulting from CPU LP2 in
+ idle or system suspend, the local timer is shut down and
+ timekeeping switches over to the global system timer. */
+ if (mode & (TEGRA_POWER_CLUSTER_MASK | TEGRA_POWER_CLUSTER_IMMEDIATE))
+ tegra_twd_suspend(&tegra_sctx.twd);
+
reg = readl(FLOW_CTRL_CPU_CSR(cpu));
reg &= ~FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
reg &= ~FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
@@ -461,7 +475,7 @@ unsigned int tegra_idle_lp2_last(unsigned int sleep_time, unsigned int flags)
tegra_lp2_set_trigger(sleep_time);
cpu_complex_pm_enter();
- suspend_cpu_complex();
+ suspend_cpu_complex(mode);
tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_prolog);
flush_cache_all();
outer_flush_all();
@@ -473,7 +487,7 @@ unsigned int tegra_idle_lp2_last(unsigned int sleep_time, unsigned int flags)
l2x0_enable();
#endif
tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_switch);
- restore_cpu_complex();
+ restore_cpu_complex(mode);
cpu_complex_pm_exit();
remain = tegra_lp2_timer_remain();
@@ -668,7 +682,7 @@ int tegra_suspend_dram(enum tegra_suspend_mode mode)
if (mode == TEGRA_SUSPEND_LP0)
tegra_lp0_suspend_mc();
- suspend_cpu_complex();
+ suspend_cpu_complex(0);
flush_cache_all();
outer_flush_all();
outer_disable();
@@ -683,7 +697,7 @@ int tegra_suspend_dram(enum tegra_suspend_mode mode)
if (mode == TEGRA_SUSPEND_LP0)
tegra_lp0_resume_mc();
- restore_cpu_complex();
+ restore_cpu_complex(0);
cpu_complex_pm_exit();
cpu_pm_exit();