summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2012-01-31 22:48:34 -0800
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-02-17 07:14:44 -0800
commit676518dbd35c737a59205e9611a92e32146d461b (patch)
tree9f762e0f90bdd1372713f62900e9a91a1435e44e
parent39af9f4c2c3cf0d9b8026986db5d73b9ad3ffe11 (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/Kconfig4
-rw-r--r--arch/arm/mach-tegra/cpuidle-t3.c22
-rw-r--r--arch/arm/mach-tegra/pm.h10
-rw-r--r--arch/arm/mach-tegra/timer-t3.c9
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)