ARM: tegra: power: Add external LP2 wake timers on secondary CPUs
Alex Frid [Wed, 1 Feb 2012 06:48:34 +0000 (22:48 -0800)]
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>

Rebase-Id: R0b524f59fa8d1f461780c3ba1d78ade794b1d63a

arch/arm/mach-tegra/Kconfig
arch/arm/mach-tegra/cpuidle-t3.c
arch/arm/mach-tegra/pm.h
arch/arm/mach-tegra/timer-t3.c

index 51c63e5..0d752e2 100644 (file)
@@ -50,6 +50,7 @@ config ARCH_TEGRA_3x_SOC
        select PINCTRL_TEGRA30
        select PL310_ERRATA_769419 if CACHE_L2X0
        select REPORT_PRESENT_CPUS if TEGRA_AUTO_HOTPLUG
+       select TEGRA_LP2_ARM_TWD if HAVE_ARM_TWD
        select USB_ARCH_HAS_EHCI if USB_SUPPORT
        select USB_ULPI if USB_PHY
        select USB_ULPI_VIEWPORT if USB_PHY
@@ -245,6 +246,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
index ca9d86a..665e102 100644 (file)
@@ -316,23 +316,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);
@@ -346,9 +355,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) {
index 1356f92..982d2ed 100644 (file)
@@ -150,6 +150,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)
@@ -179,6 +180,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)
index 5bde771..b82a1be 100644 (file)
@@ -70,6 +70,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, timer_reg_base + (reg))
@@ -178,8 +179,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);
@@ -188,6 +189,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));
@@ -218,6 +220,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, unsigned long rate)