Commit 411187fb authored by John Stultz's avatar John Stultz Committed by Linus Torvalds

[PATCH] GTOD: persistent clock support

Persistent clock support: do proper timekeeping across suspend/resume.

[bunk@stusta.de: cleanup]
Signed-off-by: default avatarJohn Stultz <johnstul@us.ibm.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Cc: Roman Zippel <zippel@linux-m68k.org>
Cc: Adrian Bunk <bunk@stusta.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 9f907c01
...@@ -146,6 +146,9 @@ extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, ...@@ -146,6 +146,9 @@ extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
/* Soft interrupt function to run the hrtimer queues: */ /* Soft interrupt function to run the hrtimer queues: */
extern void hrtimer_run_queues(void); extern void hrtimer_run_queues(void);
/* Resume notification */
void hrtimer_notify_resume(void);
/* Bootup initialization: */ /* Bootup initialization: */
extern void __init hrtimers_init(void); extern void __init hrtimers_init(void);
......
...@@ -92,6 +92,7 @@ extern struct timespec xtime; ...@@ -92,6 +92,7 @@ extern struct timespec xtime;
extern struct timespec wall_to_monotonic; extern struct timespec wall_to_monotonic;
extern seqlock_t xtime_lock __attribute__((weak)); extern seqlock_t xtime_lock __attribute__((weak));
extern unsigned long read_persistent_clock(void);
void timekeeping_init(void); void timekeeping_init(void);
static inline unsigned long get_seconds(void) static inline unsigned long get_seconds(void)
......
...@@ -291,6 +291,14 @@ static unsigned long ktime_divns(const ktime_t kt, s64 div) ...@@ -291,6 +291,14 @@ static unsigned long ktime_divns(const ktime_t kt, s64 div)
# define ktime_divns(kt, div) (unsigned long)((kt).tv64 / (div)) # define ktime_divns(kt, div) (unsigned long)((kt).tv64 / (div))
#endif /* BITS_PER_LONG >= 64 */ #endif /* BITS_PER_LONG >= 64 */
/*
* Timekeeping resumed notification
*/
void hrtimer_notify_resume(void)
{
clock_was_set();
}
/* /*
* Counterpart to lock_timer_base above: * Counterpart to lock_timer_base above:
*/ */
......
...@@ -878,12 +878,27 @@ int timekeeping_is_continuous(void) ...@@ -878,12 +878,27 @@ int timekeeping_is_continuous(void)
return ret; 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 * timekeeping_init - Initializes the clocksource and common timekeeping values
*/ */
void __init timekeeping_init(void) void __init timekeeping_init(void)
{ {
unsigned long flags; unsigned long flags;
unsigned long sec = read_persistent_clock();
write_seqlock_irqsave(&xtime_lock, flags); write_seqlock_irqsave(&xtime_lock, flags);
...@@ -893,11 +908,20 @@ void __init timekeeping_init(void) ...@@ -893,11 +908,20 @@ void __init timekeeping_init(void)
clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH); clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH);
clock->cycle_last = clocksource_read(clock); 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); write_sequnlock_irqrestore(&xtime_lock, flags);
} }
/* flag for if timekeeping is suspended */
static int timekeeping_suspended; static int timekeeping_suspended;
/* time in seconds when suspend began */
static unsigned long timekeeping_suspend_time;
/** /**
* timekeeping_resume - Resumes the generic timekeeping subsystem. * timekeeping_resume - Resumes the generic timekeeping subsystem.
* @dev: unused * @dev: unused
...@@ -909,13 +933,25 @@ static int timekeeping_suspended; ...@@ -909,13 +933,25 @@ static int timekeeping_suspended;
static int timekeeping_resume(struct sys_device *dev) static int timekeeping_resume(struct sys_device *dev)
{ {
unsigned long flags; unsigned long flags;
unsigned long now = read_persistent_clock();
write_seqlock_irqsave(&xtime_lock, flags); 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->cycle_last = clocksource_read(clock);
clock->error = 0; clock->error = 0;
timekeeping_suspended = 0; timekeeping_suspended = 0;
write_sequnlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
touch_softlockup_watchdog();
hrtimer_notify_resume();
return 0; return 0;
} }
...@@ -925,6 +961,7 @@ static int timekeeping_suspend(struct sys_device *dev, pm_message_t state) ...@@ -925,6 +961,7 @@ static int timekeeping_suspend(struct sys_device *dev, pm_message_t state)
write_seqlock_irqsave(&xtime_lock, flags); write_seqlock_irqsave(&xtime_lock, flags);
timekeeping_suspended = 1; timekeeping_suspended = 1;
timekeeping_suspend_time = read_persistent_clock();
write_sequnlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment