Thomas Gleixner | 35728b8 | 2018-10-31 19:21:09 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
Richard Cochran | 74d23cc | 2014-12-21 19:46:56 +0100 | [diff] [blame] | 2 | /* |
Thomas Gleixner | 58c5fc2 | 2018-10-31 19:21:08 +0100 | [diff] [blame] | 3 | * Based on clocksource code. See commit 74d23cc704d1 |
Richard Cochran | 74d23cc | 2014-12-21 19:46:56 +0100 | [diff] [blame] | 4 | */ |
Richard Cochran | 74d23cc | 2014-12-21 19:46:56 +0100 | [diff] [blame] | 5 | #include <linux/export.h> |
| 6 | #include <linux/timecounter.h> |
| 7 | |
| 8 | void timecounter_init(struct timecounter *tc, |
| 9 | const struct cyclecounter *cc, |
| 10 | u64 start_tstamp) |
| 11 | { |
| 12 | tc->cc = cc; |
| 13 | tc->cycle_last = cc->read(cc); |
| 14 | tc->nsec = start_tstamp; |
Richard Cochran | 2eebdde | 2014-12-21 19:47:06 +0100 | [diff] [blame] | 15 | tc->mask = (1ULL << cc->shift) - 1; |
| 16 | tc->frac = 0; |
Richard Cochran | 74d23cc | 2014-12-21 19:46:56 +0100 | [diff] [blame] | 17 | } |
| 18 | EXPORT_SYMBOL_GPL(timecounter_init); |
| 19 | |
| 20 | /** |
| 21 | * timecounter_read_delta - get nanoseconds since last call of this function |
| 22 | * @tc: Pointer to time counter |
| 23 | * |
| 24 | * When the underlying cycle counter runs over, this will be handled |
| 25 | * correctly as long as it does not run over more than once between |
| 26 | * calls. |
| 27 | * |
| 28 | * The first call to this function for a new time counter initializes |
| 29 | * the time tracking and returns an undefined result. |
| 30 | */ |
| 31 | static u64 timecounter_read_delta(struct timecounter *tc) |
| 32 | { |
Thomas Gleixner | a5a1d1c | 2016-12-21 20:32:01 +0100 | [diff] [blame] | 33 | u64 cycle_now, cycle_delta; |
Richard Cochran | 74d23cc | 2014-12-21 19:46:56 +0100 | [diff] [blame] | 34 | u64 ns_offset; |
| 35 | |
| 36 | /* read cycle counter: */ |
| 37 | cycle_now = tc->cc->read(tc->cc); |
| 38 | |
| 39 | /* calculate the delta since the last timecounter_read_delta(): */ |
| 40 | cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask; |
| 41 | |
| 42 | /* convert to nanoseconds: */ |
Richard Cochran | 2eebdde | 2014-12-21 19:47:06 +0100 | [diff] [blame] | 43 | ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta, |
| 44 | tc->mask, &tc->frac); |
Richard Cochran | 74d23cc | 2014-12-21 19:46:56 +0100 | [diff] [blame] | 45 | |
| 46 | /* update time stamp of timecounter_read_delta() call: */ |
| 47 | tc->cycle_last = cycle_now; |
| 48 | |
| 49 | return ns_offset; |
| 50 | } |
| 51 | |
| 52 | u64 timecounter_read(struct timecounter *tc) |
| 53 | { |
| 54 | u64 nsec; |
| 55 | |
| 56 | /* increment time by nanoseconds since last call */ |
| 57 | nsec = timecounter_read_delta(tc); |
| 58 | nsec += tc->nsec; |
| 59 | tc->nsec = nsec; |
| 60 | |
| 61 | return nsec; |
| 62 | } |
| 63 | EXPORT_SYMBOL_GPL(timecounter_read); |
| 64 | |
Richard Cochran | 2eebdde | 2014-12-21 19:47:06 +0100 | [diff] [blame] | 65 | /* |
| 66 | * This is like cyclecounter_cyc2ns(), but it is used for computing a |
| 67 | * time previous to the time stored in the cycle counter. |
| 68 | */ |
| 69 | static u64 cc_cyc2ns_backwards(const struct cyclecounter *cc, |
Thomas Gleixner | a5a1d1c | 2016-12-21 20:32:01 +0100 | [diff] [blame] | 70 | u64 cycles, u64 mask, u64 frac) |
Richard Cochran | 2eebdde | 2014-12-21 19:47:06 +0100 | [diff] [blame] | 71 | { |
| 72 | u64 ns = (u64) cycles; |
| 73 | |
| 74 | ns = ((ns * cc->mult) - frac) >> cc->shift; |
| 75 | |
| 76 | return ns; |
| 77 | } |
| 78 | |
Marc Kleine-Budde | 07ff4ae | 2021-03-03 11:35:44 +0100 | [diff] [blame] | 79 | u64 timecounter_cyc2time(const struct timecounter *tc, |
Thomas Gleixner | a5a1d1c | 2016-12-21 20:32:01 +0100 | [diff] [blame] | 80 | u64 cycle_tstamp) |
Richard Cochran | 74d23cc | 2014-12-21 19:46:56 +0100 | [diff] [blame] | 81 | { |
Richard Cochran | 2eebdde | 2014-12-21 19:47:06 +0100 | [diff] [blame] | 82 | u64 delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; |
| 83 | u64 nsec = tc->nsec, frac = tc->frac; |
Richard Cochran | 74d23cc | 2014-12-21 19:46:56 +0100 | [diff] [blame] | 84 | |
| 85 | /* |
| 86 | * Instead of always treating cycle_tstamp as more recent |
| 87 | * than tc->cycle_last, detect when it is too far in the |
| 88 | * future and treat it as old time stamp instead. |
| 89 | */ |
Richard Cochran | 2eebdde | 2014-12-21 19:47:06 +0100 | [diff] [blame] | 90 | if (delta > tc->cc->mask / 2) { |
| 91 | delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; |
| 92 | nsec -= cc_cyc2ns_backwards(tc->cc, delta, tc->mask, frac); |
Richard Cochran | 74d23cc | 2014-12-21 19:46:56 +0100 | [diff] [blame] | 93 | } else { |
Richard Cochran | 2eebdde | 2014-12-21 19:47:06 +0100 | [diff] [blame] | 94 | nsec += cyclecounter_cyc2ns(tc->cc, delta, tc->mask, &frac); |
Richard Cochran | 74d23cc | 2014-12-21 19:46:56 +0100 | [diff] [blame] | 95 | } |
| 96 | |
| 97 | return nsec; |
| 98 | } |
| 99 | EXPORT_SYMBOL_GPL(timecounter_cyc2time); |