diff options
author | John Stultz <johnstul@us.ibm.com> | 2007-02-16 01:27:30 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-16 08:13:57 -0800 |
commit | 411187fb05cd11676b0979d9fbf3291db69dbce2 (patch) | |
tree | 6202ca36868ab47edf737de81c7b7c4841228ab3 | |
parent | 9f907c0144496e464bd5ed5a99a51227d63a9c0b (diff) |
[PATCH] GTOD: persistent clock support
Persistent clock support: do proper timekeeping across suspend/resume.
[bunk@stusta.de: cleanup]
Signed-off-by: John Stultz <johnstul@us.ibm.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Cc: Roman Zippel <zippel@linux-m68k.org>
Cc: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/hrtimer.h | 3 | ||||
-rw-r--r-- | include/linux/time.h | 1 | ||||
-rw-r--r-- | kernel/hrtimer.c | 8 | ||||
-rw-r--r-- | kernel/timer.c | 39 |
4 files changed, 50 insertions, 1 deletions
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index fca93025ab5..660d91dea78 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -146,6 +146,9 @@ extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, /* Soft interrupt function to run the hrtimer queues: */ extern void hrtimer_run_queues(void); +/* Resume notification */ +void hrtimer_notify_resume(void); + /* Bootup initialization: */ extern void __init hrtimers_init(void); diff --git a/include/linux/time.h b/include/linux/time.h index eceb1a59b07..8ea8dea713c 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -92,6 +92,7 @@ extern struct timespec xtime; extern struct timespec wall_to_monotonic; extern seqlock_t xtime_lock __attribute__((weak)); +extern unsigned long read_persistent_clock(void); void timekeeping_init(void); static inline unsigned long get_seconds(void) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 80666f6cd4f..c0fdb9b6d29 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -292,6 +292,14 @@ static unsigned long ktime_divns(const ktime_t kt, s64 div) #endif /* BITS_PER_LONG >= 64 */ /* + * Timekeeping resumed notification + */ +void hrtimer_notify_resume(void) +{ + clock_was_set(); +} + +/* * Counterpart to lock_timer_base above: */ static inline diff --git a/kernel/timer.c b/kernel/timer.c index 4f9cc2a48be..8f4bf1e6a3e 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -878,12 +878,27 @@ int timekeeping_is_continuous(void) return ret; } +/** + * read_persistent_clock - Return time in seconds from the persistent clock. + * + * Weak dummy function for arches that do not yet support it. + * Returns seconds from epoch using the battery backed persistent clock. + * Returns zero if unsupported. + * + * XXX - Do be sure to remove it once all arches implement it. + */ +unsigned long __attribute__((weak)) read_persistent_clock(void) +{ + return 0; +} + /* * timekeeping_init - Initializes the clocksource and common timekeeping values */ void __init timekeeping_init(void) { unsigned long flags; + unsigned long sec = read_persistent_clock(); write_seqlock_irqsave(&xtime_lock, flags); @@ -893,11 +908,20 @@ void __init timekeeping_init(void) clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH); clock->cycle_last = clocksource_read(clock); + xtime.tv_sec = sec; + xtime.tv_nsec = 0; + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); + write_sequnlock_irqrestore(&xtime_lock, flags); } +/* flag for if timekeeping is suspended */ static int timekeeping_suspended; +/* time in seconds when suspend began */ +static unsigned long timekeeping_suspend_time; + /** * timekeeping_resume - Resumes the generic timekeeping subsystem. * @dev: unused @@ -909,13 +933,25 @@ static int timekeeping_suspended; static int timekeeping_resume(struct sys_device *dev) { unsigned long flags; + unsigned long now = read_persistent_clock(); write_seqlock_irqsave(&xtime_lock, flags); - /* restart the last cycle value */ + + if (now && (now > timekeeping_suspend_time)) { + unsigned long sleep_length = now - timekeeping_suspend_time; + + xtime.tv_sec += sleep_length; + wall_to_monotonic.tv_sec -= sleep_length; + } + /* re-base the last cycle value */ clock->cycle_last = clocksource_read(clock); clock->error = 0; timekeeping_suspended = 0; write_sequnlock_irqrestore(&xtime_lock, flags); + + touch_softlockup_watchdog(); + hrtimer_notify_resume(); + return 0; } @@ -925,6 +961,7 @@ static int timekeeping_suspend(struct sys_device *dev, pm_message_t state) write_seqlock_irqsave(&xtime_lock, flags); timekeeping_suspended = 1; + timekeeping_suspend_time = read_persistent_clock(); write_sequnlock_irqrestore(&xtime_lock, flags); return 0; } |