summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/timer-t3.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2012-02-02 16:21:02 -0800
committerSimone Willett <swillett@nvidia.com>2012-02-23 09:32:13 -0800
commitc8f33bd25385f4dc0f1a69e0e9faa04b1941b34e (patch)
treeb33446710df4ff3bc8b51aa0955315c01bfb80c3 /arch/arm/mach-tegra/timer-t3.c
parentd9ecb9be27bb10535159ebb43ca3fa515edfc13b (diff)
ARM: tegra: power: Power off multiple CPUs on-line
Currently on Tegra3 cpu complex is powered off in idle (enters CPU0 LP2 state) only if all secondary CPUs are off-line. This commit adds an option for CPU0 to enter LP2 while secondary CPUs are still on-line but have been power gated and entered LP2 state by themselves. The critical race: secondary CPU is waking up from LP2, while CPU0 is turning common CPU rail off, is addressed as follows. 1. When entering LP2 state on CPU0: a) disable GIC distributor b) check that CPU1-3 are all power-gated (i.e., either off-lined or have entered LP2) c) if (b) passes - set all interrupts affinity to CPU0, then re-enable distributor and continue with CPU complex powering off d) if (b) fails - re-enable distributor and enter clock-gated (LP3) state on CPU0 This procedure prevents waking secondary CPUs by GIC SPIs. 2. We still need to make sure that no CPU1-3 PPIs from legacy IRQ/FIQ or private timers would happen. This is achieved by disabling timers and legacy interrupts if CPU1-3 enters LP2 state with external timers selected as wake sources. Respectively, establish dependency between turning rail off and LP2 wake timers configuration options. 3. Finally, no IPIs is sent by CPU0 entering LP2. There are no special changes in wake up procedures - whenever CPU0 is awaken by external interrupt or wake timer, cpu complex is powered on by h/w, and secondary CPUs that were in LP2 state are ungated by the same interrupt (off-line CPUs are kept power gated). Hence, there is no need for CPU1-3 external wake timers to run while the rail is off, and these timers are stopped. To make sure that none of secondary CPUs over-sleeps its LP2 time, CPU0 wake timer is set to minimum sleep interval of all CPUs. By default configuration option for powering off multiple on-line CPUs is disabled on Tegra3. Change-Id: I4920d0df375536b2b8ebd9e6738c5fe4f92b92a0 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/83547 Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/timer-t3.c')
-rw-r--r--arch/arm/mach-tegra/timer-t3.c17
1 files changed, 17 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/timer-t3.c b/arch/arm/mach-tegra/timer-t3.c
index 15c607dfbea7..7b24e7cafcbb 100644
--- a/arch/arm/mach-tegra/timer-t3.c
+++ b/arch/arm/mach-tegra/timer-t3.c
@@ -70,6 +70,7 @@
static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE);
static cpumask_t wake_timer_ready;
+static cpumask_t wake_timer_canceled;
#define timer_writel(value, reg) \
__raw_writel(value, (u32)timer_reg_base + (reg))
@@ -217,6 +218,9 @@ unsigned long tegra3_lp2_timer_remain(void)
{
int cpu = cpu_number();
+ if (cpumask_test_and_clear_cpu(cpu, &wake_timer_canceled))
+ return -ETIME;
+
return timer_readl(lp2_wake_timers[cpu] + TIMER_PCR) & 0x1ffffffful;
}
@@ -224,6 +228,19 @@ int tegra3_is_lp2_timer_ready(unsigned int cpu)
{
return cpumask_test_cpu(cpu, &wake_timer_ready);
}
+
+void tegra3_lp2_timer_cancel_secondary(void)
+{
+ int cpu;
+ int base;
+
+ for (cpu = 1; cpu < ARRAY_SIZE(lp2_wake_timers); cpu++) {
+ base = lp2_wake_timers[cpu];
+ cpumask_set_cpu(cpu, &wake_timer_canceled);
+ timer_writel(0, base + TIMER_PTV);
+ timer_writel(1<<30, base + TIMER_PCR);
+ }
+}
#endif
void __init tegra3_init_timer(u32 *offset, int *irq)