diff options
author | Scott Williams <scwilliams@nvidia.com> | 2011-07-21 18:24:34 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:46:57 -0800 |
commit | 3b3b17486113ac2e5af1f6d2d555f1315cb40434 (patch) | |
tree | a5943868a0be1de16024cba4c9e310b934847171 /arch | |
parent | edd6779f31582fb6acdb59f58dfb4471fa4078d6 (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')
-rw-r--r-- | arch/arm/mach-tegra/cpuidle.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pm.c | 26 |
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(); |