12 #include <linux/module.h>
17 #include <linux/sched.h>
21 #include <linux/time.h>
37 static inline void tk_normalize_xtime(
struct timekeeper *tk)
55 tk_normalize_xtime(tk);
99 old_clock = tk->
clock;
105 tmp <<= clock->
shift;
107 tmp += clock->
mult/2;
123 int shift_change = clock->
shift - old_clock->
shift;
124 if (shift_change < 0)
145 cycle_t cycle_now, cycle_delta;
151 cycle_now = clock->
read(clock);
163 static inline s64 timekeeping_get_ns_raw(
struct timekeeper *tk)
165 cycle_t cycle_now, cycle_delta;
171 cycle_now = clock->
read(clock);
177 nsec = clocksource_cyc2ns(cycle_delta, clock->
mult, clock->
shift);
184 static void timekeeping_update(
struct timekeeper *tk,
bool clearntp)
200 static void timekeeping_forward_now(
struct timekeeper *tk)
202 cycle_t cycle_now, cycle_delta;
207 cycle_now = clock->
read(clock);
216 tk_normalize_xtime(tk);
218 nsec = clocksource_cyc2ns(cycle_delta, clock->
mult, clock->
shift);
219 timespec_add_ns(&tk->
raw_time, nsec);
237 seq = read_seqbegin(&tk->
lock);
240 nsecs = timekeeping_get_ns(tk);
242 }
while (read_seqretry(&tk->
lock, seq));
245 timespec_add_ns(ts, nsecs);
258 seq = read_seqbegin(&tk->
lock);
262 }
while (read_seqretry(&tk->
lock, seq));
289 seq = read_seqbegin(&tk->
lock);
291 nsec = timekeeping_get_ns(tk);
294 }
while (read_seqretry(&tk->
lock, seq));
298 timespec_add_ns(ts, nsec + tomono.
tv_nsec);
302 #ifdef CONFIG_NTP_PPS
317 s64 nsecs_raw, nsecs_real;
322 seq = read_seqbegin(&tk->
lock);
328 nsecs_raw = timekeeping_get_ns_raw(tk);
329 nsecs_real = timekeeping_get_ns(tk);
331 }
while (read_seqretry(&tk->
lock, seq));
333 timespec_add_ns(ts_raw, nsecs_raw);
334 timespec_add_ns(ts_real, nsecs_real);
368 if (!timespec_valid_strict(tv))
373 timekeeping_forward_now(tk);
381 tk_set_xtime(tk, tv);
383 timekeeping_update(tk,
true);
412 timekeeping_forward_now(tk);
415 tmp = timespec_add(tk_xtime(tk), *ts);
416 if (!timespec_valid_strict(&tmp)) {
421 tk_xtime_add(tk, ts);
425 timekeeping_update(tk,
true);
441 static int change_clocksource(
void *
data)
451 timekeeping_forward_now(tk);
452 if (!new->enable || new->enable(
new) == 0) {
454 tk_setup_internals(tk,
new);
458 timekeeping_update(tk,
true);
476 if (tk->
clock == clock)
478 stop_machine(change_clocksource, clock,
NULL);
493 return timespec_to_ktime(now);
510 seq = read_seqbegin(&tk->
lock);
511 nsecs = timekeeping_get_ns_raw(tk);
514 }
while (read_seqretry(&tk->
lock, seq));
516 timespec_add_ns(ts, nsecs);
530 seq = read_seqbegin(&tk->
lock);
534 }
while (read_seqretry(&tk->
lock, seq));
549 seq = read_seqbegin(&tk->
lock);
551 ret = tk->
clock->max_idle_ns;
553 }
while (read_seqretry(&tk->
lock, seq));
599 if (!timespec_valid_strict(&now)) {
600 pr_warn(
"WARNING: Persistent clock returned invalid value!\n"
601 " Check your CMOS/BIOS settings.\n");
607 if (!timespec_valid_strict(&boot)) {
608 pr_warn(
"WARNING: Boot clock returned invalid value!\n"
609 " Check your CMOS/BIOS settings.\n");
622 tk_setup_internals(tk, clock);
624 tk_set_xtime(tk, &now);
631 tk_set_wall_to_mono(tk, tmp);
635 tk_set_sleep_time(tk, tmp);
641 static struct timespec timekeeping_suspend_time;
650 static void __timekeeping_inject_sleeptime(
struct timekeeper *tk,
653 if (!timespec_valid_strict(delta)) {
655 "sleep delta value!\n");
658 tk_xtime_add(tk, delta);
686 timekeeping_forward_now(tk);
688 __timekeeping_inject_sleeptime(tk, delta);
690 timekeeping_update(tk,
true);
705 static void timekeeping_resume(
void)
718 if (timespec_compare(&ts, &timekeeping_suspend_time) > 0) {
719 ts = timespec_sub(ts, timekeeping_suspend_time);
720 __timekeeping_inject_sleeptime(tk, &ts);
726 timekeeping_update(tk,
false);
737 static int timekeeping_suspend(
void)
747 timekeeping_forward_now(tk);
756 delta = timespec_sub(tk_xtime(tk), timekeeping_suspend_time);
757 delta_delta = timespec_sub(delta, old_delta);
758 if (
abs(delta_delta.tv_sec) >= 2) {
766 timekeeping_suspend_time =
767 timespec_add(timekeeping_suspend_time, delta_delta);
779 static struct syscore_ops timekeeping_syscore_ops = {
780 .resume = timekeeping_resume,
781 .suspend = timekeeping_suspend,
784 static int __init timekeeping_init_ops(
void)
814 error2 =
abs(error2);
815 for (look_ahead = 0; error2 > 0; look_ahead++)
824 error = ((error - tick_error) >> look_ahead) + tick_error;
835 for (adj = 0; error >
i; adj++)
848 static void timekeeping_adjust(
struct timekeeper *tk,
s64 offset)
867 if (error > interval) {
885 if (
likely(error <= interval))
888 adj = timekeeping_bigadjust(tk, error, &interval, &offset);
890 if (error < -interval) {
893 if (
likely(error >= -interval)) {
898 adj = timekeeping_bigadjust(tk, error, &interval, &offset);
908 "Adjusting %s more than 11%% (%ld vs %ld)\n",
997 static inline void accumulate_nsecs_to_secs(
struct timekeeper *tk)
1016 tk_set_wall_to_mono(tk,
1019 clock_was_set_delayed();
1047 accumulate_nsecs_to_secs(tk);
1053 u64 raw_secs = raw_nsecs;
1067 #ifdef CONFIG_GENERIC_TIME_VSYSCALL_OLD
1089 #define old_vsyscall_fixup(tk)
1098 static void update_wall_time(
void)
1103 int shift = 0, maxshift;
1104 unsigned long flags;
1114 #ifdef CONFIG_ARCH_USES_GETTIMEOFFSET
1133 shift =
max(0, shift);
1136 shift =
min(shift, maxshift);
1138 offset = logarithmic_accumulation(tk, offset, shift);
1144 timekeeping_adjust(tk, offset);
1156 accumulate_nsecs_to_secs(tk);
1158 timekeeping_update(tk,
false);
1209 seq = read_seqbegin(&tk->
lock);
1211 nsec = timekeeping_get_ns(tk);
1215 }
while (read_seqretry(&tk->
lock, seq));
1236 return timespec_to_ktime(ts);
1264 return tk_xtime(tk);
1274 seq = read_seqbegin(&tk->
lock);
1277 }
while (read_seqretry(&tk->
lock, seq));
1290 seq = read_seqbegin(&tk->
lock);
1294 }
while (read_seqretry(&tk->
lock, seq));
1327 seq = read_seqbegin(&tk->
lock);
1328 *xtim = tk_xtime(tk);
1331 }
while (read_seqretry(&tk->
lock, seq));
1334 #ifdef CONFIG_HIGH_RES_TIMERS
1351 seq = read_seqbegin(&tk->
lock);
1354 nsecs = timekeeping_get_ns(tk);
1358 }
while (read_seqretry(&tk->
lock, seq));
1361 now = ktime_sub(now, *offs_real);
1376 seq = read_seqbegin(&tk->
lock);
1378 }
while (read_seqretry(&tk->
lock, seq));
1380 return timespec_to_ktime(wtom);