diff options
author | Alex Frid <afrid@nvidia.com> | 2012-01-31 22:48:34 -0800 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-02-17 07:14:44 -0800 |
commit | 676518dbd35c737a59205e9611a92e32146d461b (patch) | |
tree | 9f762e0f90bdd1372713f62900e9a91a1435e44e | |
parent | 39af9f4c2c3cf0d9b8026986db5d73b9ad3ffe11 (diff) |
ARM: tegra: power: Add external LP2 wake timers on secondary CPUs
Add an option to use external timer as Tegra3 secondary CPU wake
source from lp2 (power gated) state. This is a follow up to commit
51e6be9ce103fbeb2b73fa2a9d2b6528a6941e81 that disabled wake from
external timer, since its interrupt is registered too late - after
secondary CPU is brought on-line, and already had a chance to enter
lp2. With this commit, secondary CPU is not allowed to enter lp2 in
idle until wake timer is registered (clock-gated lp3 state is entered
instead).
External timer wake up mechanism is enabled on Tegra3 only if option
HAVE_ARM_TWD is not selected. Otherwise, continue to use local CPU
timers as lp2 wake sources.
Change-Id: Ic8c33f55e77174717bfa6525041e1263d3232dd5
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/83546
Reviewed-by: Rohan Somvanshi <rsomvanshi@nvidia.com>
Tested-by: Rohan Somvanshi <rsomvanshi@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/Kconfig | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/cpuidle-t3.c | 22 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pm.h | 10 | ||||
-rw-r--r-- | arch/arm/mach-tegra/timer-t3.c | 9 |
4 files changed, 41 insertions, 4 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index f3d49bfc89b3..0a5d4d930790 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -41,6 +41,7 @@ config ARCH_TEGRA_3x_SOC select ARCH_SUPPORTS_MSI if TEGRA_PCI select PCI_MSI if TEGRA_PCI select ARM_ERRATA_754322 + select TEGRA_LP2_ARM_TWD if HAVE_ARM_TWD help Support for NVIDIA Tegra 3 family of SoCs, based upon the ARM CortexA9MP CPU and the ARM PL310 L2 cache controller @@ -471,6 +472,9 @@ config TEGRA_WDT_RECOVERY help Enables watchdog recovery mechanism to protect against suspend/resume hangs. + +config TEGRA_LP2_ARM_TWD + bool endif config TEGRA_SLOW_CSITE diff --git a/arch/arm/mach-tegra/cpuidle-t3.c b/arch/arm/mach-tegra/cpuidle-t3.c index e75ded720198..4cdfbf2abbc0 100644 --- a/arch/arm/mach-tegra/cpuidle-t3.c +++ b/arch/arm/mach-tegra/cpuidle-t3.c @@ -314,23 +314,32 @@ static void tegra3_idle_enter_lp2_cpu_n(struct cpuidle_device *dev, (twd_context.twd_ctrl & TWD_TIMER_CONTROL_IT_ENABLE)) { request = div_u64((u64)twd_context.twd_cnt * 1000000, twd_rate); +#ifdef CONFIG_TEGRA_LP2_ARM_TWD if (request >= state->target_residency) { twd_context.twd_cnt -= state->exit_latency * (twd_rate / 1000000); writel(twd_context.twd_cnt, twd_base + TWD_TIMER_COUNTER); } +#endif } } - if (request < state->target_residency) { + if (!tegra_is_lp2_timer_ready(dev->cpu) || + (request < state->target_residency)) { /* - * Not enough time left to enter LP2 + * Not enough time left to enter LP2, or wake timer not ready */ tegra3_lp3_fall_back(dev); return; } +#ifndef CONFIG_TEGRA_LP2_ARM_TWD + sleep_time = request - state->exit_latency; + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); + tegra_twd_suspend(&twd_context); + tegra_lp2_set_trigger(sleep_time); +#endif idle_stats.tear_down_count[cpu_number(dev->cpu)]++; trace_power_start(POWER_CSTATE, 2, dev->cpu); @@ -344,9 +353,16 @@ static void tegra3_idle_enter_lp2_cpu_n(struct cpuidle_device *dev, tegra3_sleep_cpu_secondary(PLAT_PHYS_OFFSET - PAGE_OFFSET); tegra_cpu_wake_by_time[dev->cpu] = LLONG_MAX; + +#ifdef CONFIG_TEGRA_LP2_ARM_TWD if (!tegra_twd_get_state(&twd_context)) sleep_completed = (twd_context.twd_cnt == 0); - +#else + sleep_completed = !tegra_lp2_timer_remain(); + tegra_lp2_set_trigger(0); + tegra_twd_resume(&twd_context); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); +#endif sleep_time = ktime_to_us(ktime_sub(ktime_get(), entry_time)); idle_stats.in_lp2_time[cpu_number(dev->cpu)] += sleep_time; if (sleep_completed) { diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h index 0ad1f24612cc..44320c891495 100644 --- a/arch/arm/mach-tegra/pm.h +++ b/arch/arm/mach-tegra/pm.h @@ -152,6 +152,7 @@ unsigned long tegra2_lp2_timer_remain(void); #ifdef CONFIG_ARCH_TEGRA_3x_SOC void tegra3_lp2_set_trigger(unsigned long cycles); unsigned long tegra3_lp2_timer_remain(void); +int tegra3_is_lp2_timer_ready(unsigned int cpu); #endif static inline void tegra_lp0_suspend_init(void) @@ -181,6 +182,15 @@ static inline unsigned long tegra_lp2_timer_remain(void) #endif } +static inline int tegra_is_lp2_timer_ready(unsigned int cpu) +{ +#if defined(CONFIG_TEGRA_LP2_ARM_TWD) || defined(CONFIG_ARCH_TEGRA_2x_SOC) + return 1; +#else + return tegra3_is_lp2_timer_ready(cpu); +#endif +} + #if DEBUG_CLUSTER_SWITCH && 0 /* !!!FIXME!!! THIS IS BROKEN */ extern unsigned int tegra_cluster_debug; #define DEBUG_CLUSTER(x) do { if (tegra_cluster_debug) printk x; } while (0) diff --git a/arch/arm/mach-tegra/timer-t3.c b/arch/arm/mach-tegra/timer-t3.c index 0f6afc831dd5..15c607dfbea7 100644 --- a/arch/arm/mach-tegra/timer-t3.c +++ b/arch/arm/mach-tegra/timer-t3.c @@ -69,6 +69,7 @@ #define TIMER6_OFFSET (TEGRA_TMR6_BASE-TEGRA_TMR1_BASE) static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE); +static cpumask_t wake_timer_ready; #define timer_writel(value, reg) \ __raw_writel(value, (u32)timer_reg_base + (reg)) @@ -177,8 +178,8 @@ static void tegra3_register_wake_timer(unsigned int cpu) goto fail; } #endif - test_lp2_wake_timer(cpu); + cpumask_set_cpu(cpu, &wake_timer_ready); return; fail: tegra_lp2_in_idle(false); @@ -187,6 +188,7 @@ fail: #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_HOTPLUG_CPU) static void tegra3_unregister_wake_timer(unsigned int cpu) { + cpumask_clear_cpu(cpu, &wake_timer_ready); #ifdef CONFIG_SMP /* Reassign the affinity of the wake IRQ to CPU 0. */ (void)irq_set_affinity(tegra_lp2wake_irq[cpu].irq, cpumask_of(0)); @@ -217,6 +219,11 @@ unsigned long tegra3_lp2_timer_remain(void) return timer_readl(lp2_wake_timers[cpu] + TIMER_PCR) & 0x1ffffffful; } + +int tegra3_is_lp2_timer_ready(unsigned int cpu) +{ + return cpumask_test_cpu(cpu, &wake_timer_ready); +} #endif void __init tegra3_init_timer(u32 *offset, int *irq) |