Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 257204
b: refs/heads/master
c: 3dcad5f
h: refs/heads/master
v: v3
  • Loading branch information
John Stultz committed Jun 21, 2011
1 parent 44c4b70 commit ac9b70b
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 18 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: cb33217b1b2523895eb328a0b13fb3b1c4000969
refs/heads/master: 3dcad5ff08f65ae30832220a0e0ee2eac3502a1a
65 changes: 48 additions & 17 deletions trunk/drivers/rtc/class.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,41 @@ static void rtc_device_release(struct device *dev)
* system's wall clock; restore it on resume().
*/

static time_t oldtime;
static struct timespec oldts;
static struct timespec old_rtc, old_system, old_delta;


static int rtc_suspend(struct device *dev, pm_message_t mesg)
{
struct rtc_device *rtc = to_rtc_device(dev);
struct rtc_time tm;

struct timespec delta, delta_delta;
if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
return 0;

/* snapshot the current RTC and system time at suspend*/
rtc_read_time(rtc, &tm);
ktime_get_ts(&oldts);
rtc_tm_to_time(&tm, &oldtime);
getnstimeofday(&old_system);
rtc_tm_to_time(&tm, &old_rtc.tv_sec);


/*
* To avoid drift caused by repeated suspend/resumes,
* which each can add ~1 second drift error,
* try to compensate so the difference in system time
* and rtc time stays close to constant.
*/
delta = timespec_sub(old_system, old_rtc);
delta_delta = timespec_sub(delta, old_delta);
if (abs(delta_delta.tv_sec) >= 2) {
/*
* if delta_delta is too large, assume time correction
* has occured and set old_delta to the current delta.
*/
old_delta = delta;
} else {
/* Otherwise try to adjust old_system to compensate */
old_system = timespec_sub(old_system, delta_delta);
}

return 0;
}
Expand All @@ -63,32 +84,42 @@ static int rtc_resume(struct device *dev)
{
struct rtc_device *rtc = to_rtc_device(dev);
struct rtc_time tm;
time_t newtime;
struct timespec time;
struct timespec newts;
struct timespec new_system, new_rtc;
struct timespec sleep_time;

if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
return 0;

ktime_get_ts(&newts);
/* snapshot the current rtc and system time at resume */
getnstimeofday(&new_system);
rtc_read_time(rtc, &tm);
if (rtc_valid_tm(&tm) != 0) {
pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev));
return 0;
}
rtc_tm_to_time(&tm, &newtime);
if (newtime <= oldtime) {
if (newtime < oldtime)
rtc_tm_to_time(&tm, &new_rtc.tv_sec);
new_rtc.tv_nsec = 0;

if (new_rtc.tv_sec <= old_rtc.tv_sec) {
if (new_rtc.tv_sec < old_rtc.tv_sec)
pr_debug("%s: time travel!\n", dev_name(&rtc->dev));
return 0;
}
/* calculate the RTC time delta */
set_normalized_timespec(&time, newtime - oldtime, 0);

/* subtract kernel time between rtc_suspend to rtc_resume */
time = timespec_sub(time, timespec_sub(newts, oldts));
/* calculate the RTC time delta (sleep time)*/
sleep_time = timespec_sub(new_rtc, old_rtc);

/*
* Since these RTC suspend/resume handlers are not called
* at the very end of suspend or the start of resume,
* some run-time may pass on either sides of the sleep time
* so subtract kernel run-time between rtc_suspend to rtc_resume
* to keep things accurate.
*/
sleep_time = timespec_sub(sleep_time,
timespec_sub(new_system, old_system));

timekeeping_inject_sleeptime(&time);
timekeeping_inject_sleeptime(&sleep_time);
return 0;
}

Expand Down

0 comments on commit ac9b70b

Please sign in to comment.