aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorFeng Tang <feng.tang@intel.com>2013-03-11 23:56:48 -0400
committerJohn Stultz <john.stultz@linaro.org>2013-03-15 19:51:29 -0400
commite445cf1c4257cc0238d72e4129eb4739f46fd3de (patch)
tree5ff9c120a867939996d83dae6e885e81177c1819 /kernel
parent82f9c080b22a5b859ae2b50822dfb6b812898fdb (diff)
timekeeping: utilize the suspend-nonstop clocksource to count suspended time
There are some new processors whose TSC clocksource won't stop during suspend. Currently, after system resumes, kernel will use persistent clock or RTC to compensate the sleep time, but with these nonstop clocksources, we could skip the special compensation from external sources, and just use current clocksource for time recounting. This can solve some time drift bugs caused by some not-so-accurate or error-prone RTC devices. The current way to count suspended time is first try to use the persistent clock, and then try the RTC if persistent clock can't be used. This patch will change the trying order to: suspend-nonstop clocksource -> persistent clock -> RTC When counting the sleep time with nonstop clocksource, use an accurate way suggested by Jason Gunthorpe to cover very large delta cycles. Signed-off-by: Feng Tang <feng.tang@intel.com> [jstultz: Small optimization, avoiding re-reading the clocksource] Signed-off-by: John Stultz <john.stultz@linaro.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/time/timekeeping.c58
1 files changed, 51 insertions, 7 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 9a0bc98fbe1d..0355f125d585 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -788,22 +788,66 @@ void timekeeping_inject_sleeptime(struct timespec *delta)
788static void timekeeping_resume(void) 788static void timekeeping_resume(void)
789{ 789{
790 struct timekeeper *tk = &timekeeper; 790 struct timekeeper *tk = &timekeeper;
791 struct clocksource *clock = tk->clock;
791 unsigned long flags; 792 unsigned long flags;
792 struct timespec ts; 793 struct timespec ts_new, ts_delta;
794 cycle_t cycle_now, cycle_delta;
795 bool suspendtime_found = false;
793 796
794 read_persistent_clock(&ts); 797 read_persistent_clock(&ts_new);
795 798
796 clockevents_resume(); 799 clockevents_resume();
797 clocksource_resume(); 800 clocksource_resume();
798 801
799 write_seqlock_irqsave(&tk->lock, flags); 802 write_seqlock_irqsave(&tk->lock, flags);
800 803
801 if (timespec_compare(&ts, &timekeeping_suspend_time) > 0) { 804 /*
802 ts = timespec_sub(ts, timekeeping_suspend_time); 805 * After system resumes, we need to calculate the suspended time and
803 __timekeeping_inject_sleeptime(tk, &ts); 806 * compensate it for the OS time. There are 3 sources that could be
807 * used: Nonstop clocksource during suspend, persistent clock and rtc
808 * device.
809 *
810 * One specific platform may have 1 or 2 or all of them, and the
811 * preference will be:
812 * suspend-nonstop clocksource -> persistent clock -> rtc
813 * The less preferred source will only be tried if there is no better
814 * usable source. The rtc part is handled separately in rtc core code.
815 */
816 cycle_now = clock->read(clock);
817 if ((clock->flags & CLOCK_SOURCE_SUSPEND_NONSTOP) &&
818 cycle_now > clock->cycle_last) {
819 u64 num, max = ULLONG_MAX;
820 u32 mult = clock->mult;
821 u32 shift = clock->shift;
822 s64 nsec = 0;
823
824 cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
825
826 /*
827 * "cycle_delta * mutl" may cause 64 bits overflow, if the
828 * suspended time is too long. In that case we need do the
829 * 64 bits math carefully
830 */
831 do_div(max, mult);
832 if (cycle_delta > max) {
833 num = div64_u64(cycle_delta, max);
834 nsec = (((u64) max * mult) >> shift) * num;
835 cycle_delta -= num * max;
836 }
837 nsec += ((u64) cycle_delta * mult) >> shift;
838
839 ts_delta = ns_to_timespec(nsec);
840 suspendtime_found = true;
841 } else if (timespec_compare(&ts_new, &timekeeping_suspend_time) > 0) {
842 ts_delta = timespec_sub(ts_new, timekeeping_suspend_time);
843 suspendtime_found = true;
804 } 844 }
805 /* re-base the last cycle value */ 845
806 tk->clock->cycle_last = tk->clock->read(tk->clock); 846 if (suspendtime_found)
847 __timekeeping_inject_sleeptime(tk, &ts_delta);
848
849 /* Re-base the last cycle value */
850 clock->cycle_last = cycle_now;
807 tk->ntp_error = 0; 851 tk->ntp_error = 0;
808 timekeeping_suspended = 0; 852 timekeeping_suspended = 0;
809 timekeeping_update(tk, false); 853 timekeeping_update(tk, false);