diff options
author | John Stultz <johnstul@us.ibm.com> | 2007-02-16 04:27:30 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-16 11:13:57 -0500 |
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 fca93025ab51..660d91dea78c 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h | |||
@@ -146,6 +146,9 @@ extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, | |||
146 | /* Soft interrupt function to run the hrtimer queues: */ | 146 | /* Soft interrupt function to run the hrtimer queues: */ |
147 | extern void hrtimer_run_queues(void); | 147 | extern void hrtimer_run_queues(void); |
148 | 148 | ||
149 | /* Resume notification */ | ||
150 | void hrtimer_notify_resume(void); | ||
151 | |||
149 | /* Bootup initialization: */ | 152 | /* Bootup initialization: */ |
150 | extern void __init hrtimers_init(void); | 153 | extern void __init hrtimers_init(void); |
151 | 154 | ||
diff --git a/include/linux/time.h b/include/linux/time.h index eceb1a59b078..8ea8dea713c7 100644 --- a/include/linux/time.h +++ b/include/linux/time.h | |||
@@ -92,6 +92,7 @@ extern struct timespec xtime; | |||
92 | extern struct timespec wall_to_monotonic; | 92 | extern struct timespec wall_to_monotonic; |
93 | extern seqlock_t xtime_lock __attribute__((weak)); | 93 | extern seqlock_t xtime_lock __attribute__((weak)); |
94 | 94 | ||
95 | extern unsigned long read_persistent_clock(void); | ||
95 | void timekeeping_init(void); | 96 | void timekeeping_init(void); |
96 | 97 | ||
97 | static inline unsigned long get_seconds(void) | 98 | static inline unsigned long get_seconds(void) |
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 80666f6cd4f9..c0fdb9b6d296 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c | |||
@@ -292,6 +292,14 @@ static unsigned long ktime_divns(const ktime_t kt, s64 div) | |||
292 | #endif /* BITS_PER_LONG >= 64 */ | 292 | #endif /* BITS_PER_LONG >= 64 */ |
293 | 293 | ||
294 | /* | 294 | /* |
295 | * Timekeeping resumed notification | ||
296 | */ | ||
297 | void hrtimer_notify_resume(void) | ||
298 | { | ||
299 | clock_was_set(); | ||
300 | } | ||
301 | |||
302 | /* | ||
295 | * Counterpart to lock_timer_base above: | 303 | * Counterpart to lock_timer_base above: |
296 | */ | 304 | */ |
297 | static inline | 305 | static inline |
diff --git a/kernel/timer.c b/kernel/timer.c index 4f9cc2a48beb..8f4bf1e6a3ed 100644 --- a/kernel/timer.c +++ b/kernel/timer.c | |||
@@ -878,12 +878,27 @@ int timekeeping_is_continuous(void) | |||
878 | return ret; | 878 | return ret; |
879 | } | 879 | } |
880 | 880 | ||
881 | /** | ||
882 | * read_persistent_clock - Return time in seconds from the persistent clock. | ||
883 | * | ||
884 | * Weak dummy function for arches that do not yet support it. | ||
885 | * Returns seconds from epoch using the battery backed persistent clock. | ||
886 | * Returns zero if unsupported. | ||
887 | * | ||
888 | * XXX - Do be sure to remove it once all arches implement it. | ||
889 | */ | ||
890 | unsigned long __attribute__((weak)) read_persistent_clock(void) | ||
891 | { | ||
892 | return 0; | ||
893 | } | ||
894 | |||
881 | /* | 895 | /* |
882 | * timekeeping_init - Initializes the clocksource and common timekeeping values | 896 | * timekeeping_init - Initializes the clocksource and common timekeeping values |
883 | */ | 897 | */ |
884 | void __init timekeeping_init(void) | 898 | void __init timekeeping_init(void) |
885 | { | 899 | { |
886 | unsigned long flags; | 900 | unsigned long flags; |
901 | unsigned long sec = read_persistent_clock(); | ||
887 | 902 | ||
888 | write_seqlock_irqsave(&xtime_lock, flags); | 903 | write_seqlock_irqsave(&xtime_lock, flags); |
889 | 904 | ||
@@ -893,11 +908,20 @@ void __init timekeeping_init(void) | |||
893 | clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH); | 908 | clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH); |
894 | clock->cycle_last = clocksource_read(clock); | 909 | clock->cycle_last = clocksource_read(clock); |
895 | 910 | ||
911 | xtime.tv_sec = sec; | ||
912 | xtime.tv_nsec = 0; | ||
913 | set_normalized_timespec(&wall_to_monotonic, | ||
914 | -xtime.tv_sec, -xtime.tv_nsec); | ||
915 | |||
896 | write_sequnlock_irqrestore(&xtime_lock, flags); | 916 | write_sequnlock_irqrestore(&xtime_lock, flags); |
897 | } | 917 | } |
898 | 918 | ||
899 | 919 | ||
920 | /* flag for if timekeeping is suspended */ | ||
900 | static int timekeeping_suspended; | 921 | static int timekeeping_suspended; |
922 | /* time in seconds when suspend began */ | ||
923 | static unsigned long timekeeping_suspend_time; | ||
924 | |||
901 | /** | 925 | /** |
902 | * timekeeping_resume - Resumes the generic timekeeping subsystem. | 926 | * timekeeping_resume - Resumes the generic timekeeping subsystem. |
903 | * @dev: unused | 927 | * @dev: unused |
@@ -909,13 +933,25 @@ static int timekeeping_suspended; | |||
909 | static int timekeeping_resume(struct sys_device *dev) | 933 | static int timekeeping_resume(struct sys_device *dev) |
910 | { | 934 | { |
911 | unsigned long flags; | 935 | unsigned long flags; |
936 | unsigned long now = read_persistent_clock(); | ||
912 | 937 | ||
913 | write_seqlock_irqsave(&xtime_lock, flags); | 938 | write_seqlock_irqsave(&xtime_lock, flags); |
914 | /* restart the last cycle value */ | 939 | |
940 | if (now && (now > timekeeping_suspend_time)) { | ||
941 | unsigned long sleep_length = now - timekeeping_suspend_time; | ||
942 | |||
943 | xtime.tv_sec += sleep_length; | ||
944 | wall_to_monotonic.tv_sec -= sleep_length; | ||
945 | } | ||
946 | /* re-base the last cycle value */ | ||
915 | clock->cycle_last = clocksource_read(clock); | 947 | clock->cycle_last = clocksource_read(clock); |
916 | clock->error = 0; | 948 | clock->error = 0; |
917 | timekeeping_suspended = 0; | 949 | timekeeping_suspended = 0; |
918 | write_sequnlock_irqrestore(&xtime_lock, flags); | 950 | write_sequnlock_irqrestore(&xtime_lock, flags); |
951 | |||
952 | touch_softlockup_watchdog(); | ||
953 | hrtimer_notify_resume(); | ||
954 | |||
919 | return 0; | 955 | return 0; |
920 | } | 956 | } |
921 | 957 | ||
@@ -925,6 +961,7 @@ static int timekeeping_suspend(struct sys_device *dev, pm_message_t state) | |||
925 | 961 | ||
926 | write_seqlock_irqsave(&xtime_lock, flags); | 962 | write_seqlock_irqsave(&xtime_lock, flags); |
927 | timekeeping_suspended = 1; | 963 | timekeeping_suspended = 1; |
964 | timekeeping_suspend_time = read_persistent_clock(); | ||
928 | write_sequnlock_irqrestore(&xtime_lock, flags); | 965 | write_sequnlock_irqrestore(&xtime_lock, flags); |
929 | return 0; | 966 | return 0; |
930 | } | 967 | } |