ARM: tegra11x: more accurate sleep time
Bo Yan [Wed, 23 Jan 2013 19:02:16 +0000 (11:02 -0800)]
The sleep length in tick data structure does not reflect how long
timer has been running, thus the expected wake up trigger may be
set to a value which is too late.

Directly accessing timer register to get the next timer event,
which is then used to calculate the expected wake up time.

This implies we are sleeping shorter than before in case of cluster
power down, but will make sure we don't oversleep.

Change-Id: I84598db30b6a739103026d090b130f3adb63147b
Signed-off-by: Bo Yan <byan@nvidia.com>
Reviewed-on: http://git-master/r/193483
(cherry picked from commit f51efbce4b67bfb79cdb68f2613e58b080634b37)
Reviewed-on: http://git-master/r/196111
Reviewed-by: Mrutyunjay Sawant <msawant@nvidia.com>
Tested-by: Mrutyunjay Sawant <msawant@nvidia.com>

arch/arm/mach-tegra/cpuidle-t11x.c
arch/arm/mach-tegra/timer.c
arch/arm/mach-tegra/timer.h

index 9f2d94e..cb12275 100644 (file)
@@ -165,7 +165,9 @@ bool tegra11x_pd_is_allowed(struct cpuidle_device *dev,
                                to_cpumask(&cpu_power_gating_in_idle)))
                return false;
 
-       request = ktime_to_us(tick_nohz_get_sleep_length());
+       if (tegra_cpu_timer_get_remain(&request))
+               return false;
+
        if (state->exit_latency != pd_exit_latencies[cpu_number(dev->cpu)]) {
                /* possible on the 1st entry after cluster switch*/
                state->exit_latency = pd_exit_latencies[cpu_number(dev->cpu)];
@@ -379,7 +381,7 @@ static bool tegra_cpu_core_power_down(struct cpuidle_device *dev,
 
        if (!arch_timer_get_state(&timer_context)) {
                if ((timer_context.cntp_ctl & ARCH_TIMER_CTRL_ENABLE) &&
-                   ~(timer_context.cntp_ctl & ARCH_TIMER_CTRL_IT_MASK)) {
+                   !(timer_context.cntp_ctl & ARCH_TIMER_CTRL_IT_MASK)) {
                        if (timer_context.cntp_tval <= 0) {
                                cpu_do_idle();
                                return false;
@@ -472,7 +474,12 @@ bool tegra11x_idle_power_down(struct cpuidle_device *dev,
        bool power_gating_cpu_only = true;
        int status = -1;
        unsigned long rate = ULONG_MAX;
-       s64 request = ktime_to_us(tick_nohz_get_sleep_length());
+       s64 request;
+
+       if (tegra_cpu_timer_get_remain(&request)) {
+               cpu_do_idle();
+               return false;
+       }
 
        tegra_set_cpu_in_pd(dev->cpu);
        cpu_gating_only = (((fast_cluster_power_down_mode
index def4ce0..20bbdea 100644 (file)
@@ -6,7 +6,7 @@
  * Author:
  *     Colin Cross <ccross@google.com>
  *
- * Copyright (C) 2010-2012 NVIDIA Corporation.
+ * Copyright (C) 2010-2013 NVIDIA Corporation.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -55,7 +55,8 @@ static void __iomem *rtc_base = IO_ADDRESS(TEGRA_RTC_BASE);
 
 static struct timespec persistent_ts;
 #ifdef CONFIG_ARM_ARCH_TIMER
-static u32 arch_timer_mult, arch_timer_shift;
+static u32 arch_timer_ns_mult, arch_timer_ns_shift;
+static u32 arch_timer_us_mult, arch_timer_us_shift;
 static u64 persistent_cycles, last_persistent_cycles;
 #else
 static u64 persistent_ms, last_persistent_ms;
@@ -164,7 +165,7 @@ void tegra_read_persistent_clock(struct timespec *ts)
        last_persistent_cycles = persistent_cycles;
        persistent_cycles = ((u64)cvalh << 32) | cvall;
        delta = persistent_cycles - last_persistent_cycles;
-       delta = (delta * arch_timer_mult) >> arch_timer_shift;
+       delta = (delta * arch_timer_ns_mult) >> arch_timer_ns_shift;
        timespec_add_ns(tsp, delta);
        *ts = *tsp;
 }
@@ -328,8 +329,21 @@ int arch_timer_get_state(struct arch_timer_context *context)
        return 0;
 }
 
-#else
-#define arch_timer_get_state do {} while(0)
+int tegra_cpu_timer_get_remain(s64 *time)
+{
+       s32 cntp_tval;
+       int ret = 0;
+
+       asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (cntp_tval));
+
+       if (cntp_tval <= 0)
+               ret = -ETIME;
+       else
+               *time = (s64)((s64)cntp_tval * arch_timer_us_mult)
+                       >> arch_timer_us_shift;
+
+       return 0;
+}
 #endif
 
 #ifdef CONFIG_ARM_ARCH_TIMER
@@ -402,8 +416,10 @@ void __init tegra_cpu_timer_init(void)
 #endif
        sec = CLOCKSOURCE_MASK(56);
        do_div(sec, tsc_ref_freq);
-       clocks_calc_mult_shift(&arch_timer_mult, &arch_timer_shift,
+       clocks_calc_mult_shift(&arch_timer_ns_mult, &arch_timer_ns_shift,
                                tsc_ref_freq, NSEC_PER_SEC, sec);
+       clocks_calc_mult_shift(&arch_timer_us_mult, &arch_timer_us_shift,
+                               tsc_ref_freq, USEC_PER_SEC, sec);
        return;
 }
 
index 6bd4aca..6efee0c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/timer.h
  *
- * Copyright (C) 2010-2012 NVIDIA Corporation
+ * Copyright (C) 2010-2013 NVIDIA Corporation
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -67,8 +67,7 @@ struct arch_timer_context {
 };
 
 int arch_timer_get_state(struct arch_timer_context *);
-void arch_timer_suspend(struct arch_timer_context *);
-void arch_timer_resume(struct arch_timer_context *);
+int tegra_cpu_timer_get_remain(s64 *time);
 #endif
 
 #if defined(CONFIG_ARM_ARCH_TIMER) && defined(CONFIG_PM_SLEEP)