aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-07-22 19:52:18 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-22 19:52:18 -0400
commit112ec469663e09ffc815761254b52f3ca787ce83 (patch)
tree18a7d2300dc10b7c2c994107681dffc927589701 /drivers/rtc
parenta99a7d1436f9375662f35ccac8f1a1e1b0302a11 (diff)
parentcbaa51524b3224813814607177a00c350ee35d12 (diff)
Merge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: time: Fix stupid KERN_WARN compile issue rtc: Avoid accumulating time drift in suspend/resume time: Avoid accumulating time drift in suspend/resume time: Catch invalid timespec sleep values in __timekeeping_inject_sleeptime
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/class.c65
1 files changed, 48 insertions, 17 deletions
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 4194e59e14cd..01a7df5317c1 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -41,20 +41,41 @@ static void rtc_device_release(struct device *dev)
41 * system's wall clock; restore it on resume(). 41 * system's wall clock; restore it on resume().
42 */ 42 */
43 43
44static time_t oldtime; 44static struct timespec old_rtc, old_system, old_delta;
45static struct timespec oldts; 45
46 46
47static int rtc_suspend(struct device *dev, pm_message_t mesg) 47static int rtc_suspend(struct device *dev, pm_message_t mesg)
48{ 48{
49 struct rtc_device *rtc = to_rtc_device(dev); 49 struct rtc_device *rtc = to_rtc_device(dev);
50 struct rtc_time tm; 50 struct rtc_time tm;
51 51 struct timespec delta, delta_delta;
52 if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) 52 if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
53 return 0; 53 return 0;
54 54
55 /* snapshot the current RTC and system time at suspend*/
55 rtc_read_time(rtc, &tm); 56 rtc_read_time(rtc, &tm);
56 ktime_get_ts(&oldts); 57 getnstimeofday(&old_system);
57 rtc_tm_to_time(&tm, &oldtime); 58 rtc_tm_to_time(&tm, &old_rtc.tv_sec);
59
60
61 /*
62 * To avoid drift caused by repeated suspend/resumes,
63 * which each can add ~1 second drift error,
64 * try to compensate so the difference in system time
65 * and rtc time stays close to constant.
66 */
67 delta = timespec_sub(old_system, old_rtc);
68 delta_delta = timespec_sub(delta, old_delta);
69 if (abs(delta_delta.tv_sec) >= 2) {
70 /*
71 * if delta_delta is too large, assume time correction
72 * has occured and set old_delta to the current delta.
73 */
74 old_delta = delta;
75 } else {
76 /* Otherwise try to adjust old_system to compensate */
77 old_system = timespec_sub(old_system, delta_delta);
78 }
58 79
59 return 0; 80 return 0;
60} 81}
@@ -63,32 +84,42 @@ static int rtc_resume(struct device *dev)
63{ 84{
64 struct rtc_device *rtc = to_rtc_device(dev); 85 struct rtc_device *rtc = to_rtc_device(dev);
65 struct rtc_time tm; 86 struct rtc_time tm;
66 time_t newtime; 87 struct timespec new_system, new_rtc;
67 struct timespec time; 88 struct timespec sleep_time;
68 struct timespec newts;
69 89
70 if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) 90 if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
71 return 0; 91 return 0;
72 92
73 ktime_get_ts(&newts); 93 /* snapshot the current rtc and system time at resume */
94 getnstimeofday(&new_system);
74 rtc_read_time(rtc, &tm); 95 rtc_read_time(rtc, &tm);
75 if (rtc_valid_tm(&tm) != 0) { 96 if (rtc_valid_tm(&tm) != 0) {
76 pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev)); 97 pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev));
77 return 0; 98 return 0;
78 } 99 }
79 rtc_tm_to_time(&tm, &newtime); 100 rtc_tm_to_time(&tm, &new_rtc.tv_sec);
80 if (newtime <= oldtime) { 101 new_rtc.tv_nsec = 0;
81 if (newtime < oldtime) 102
103 if (new_rtc.tv_sec <= old_rtc.tv_sec) {
104 if (new_rtc.tv_sec < old_rtc.tv_sec)
82 pr_debug("%s: time travel!\n", dev_name(&rtc->dev)); 105 pr_debug("%s: time travel!\n", dev_name(&rtc->dev));
83 return 0; 106 return 0;
84 } 107 }
85 /* calculate the RTC time delta */
86 set_normalized_timespec(&time, newtime - oldtime, 0);
87 108
88 /* subtract kernel time between rtc_suspend to rtc_resume */ 109 /* calculate the RTC time delta (sleep time)*/
89 time = timespec_sub(time, timespec_sub(newts, oldts)); 110 sleep_time = timespec_sub(new_rtc, old_rtc);
111
112 /*
113 * Since these RTC suspend/resume handlers are not called
114 * at the very end of suspend or the start of resume,
115 * some run-time may pass on either sides of the sleep time
116 * so subtract kernel run-time between rtc_suspend to rtc_resume
117 * to keep things accurate.
118 */
119 sleep_time = timespec_sub(sleep_time,
120 timespec_sub(new_system, old_system));
90 121
91 timekeeping_inject_sleeptime(&time); 122 timekeeping_inject_sleeptime(&sleep_time);
92 return 0; 123 return 0;
93} 124}
94 125