Kyle Swenson | 8d8f654 | 2021-03-15 11:02:55 -0600 | [diff] [blame] | 1 | /* |
| 2 | * linux/kernel/time/timecounter.c |
| 3 | * |
| 4 | * based on code that migrated away from |
| 5 | * linux/kernel/time/clocksource.c |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; either version 2 of the License, or |
| 10 | * (at your option) any later version. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | */ |
| 17 | |
| 18 | #include <linux/export.h> |
| 19 | #include <linux/timecounter.h> |
| 20 | |
| 21 | void timecounter_init(struct timecounter *tc, |
| 22 | const struct cyclecounter *cc, |
| 23 | u64 start_tstamp) |
| 24 | { |
| 25 | tc->cc = cc; |
| 26 | tc->cycle_last = cc->read(cc); |
| 27 | tc->nsec = start_tstamp; |
| 28 | tc->mask = (1ULL << cc->shift) - 1; |
| 29 | tc->frac = 0; |
| 30 | } |
| 31 | EXPORT_SYMBOL_GPL(timecounter_init); |
| 32 | |
| 33 | /** |
| 34 | * timecounter_read_delta - get nanoseconds since last call of this function |
| 35 | * @tc: Pointer to time counter |
| 36 | * |
| 37 | * When the underlying cycle counter runs over, this will be handled |
| 38 | * correctly as long as it does not run over more than once between |
| 39 | * calls. |
| 40 | * |
| 41 | * The first call to this function for a new time counter initializes |
| 42 | * the time tracking and returns an undefined result. |
| 43 | */ |
| 44 | static u64 timecounter_read_delta(struct timecounter *tc) |
| 45 | { |
| 46 | cycle_t cycle_now, cycle_delta; |
| 47 | u64 ns_offset; |
| 48 | |
| 49 | /* read cycle counter: */ |
| 50 | cycle_now = tc->cc->read(tc->cc); |
| 51 | |
| 52 | /* calculate the delta since the last timecounter_read_delta(): */ |
| 53 | cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask; |
| 54 | |
| 55 | /* convert to nanoseconds: */ |
| 56 | ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta, |
| 57 | tc->mask, &tc->frac); |
| 58 | |
| 59 | /* update time stamp of timecounter_read_delta() call: */ |
| 60 | tc->cycle_last = cycle_now; |
| 61 | |
| 62 | return ns_offset; |
| 63 | } |
| 64 | |
| 65 | u64 timecounter_read(struct timecounter *tc) |
| 66 | { |
| 67 | u64 nsec; |
| 68 | |
| 69 | /* increment time by nanoseconds since last call */ |
| 70 | nsec = timecounter_read_delta(tc); |
| 71 | nsec += tc->nsec; |
| 72 | tc->nsec = nsec; |
| 73 | |
| 74 | return nsec; |
| 75 | } |
| 76 | EXPORT_SYMBOL_GPL(timecounter_read); |
| 77 | |
| 78 | /* |
| 79 | * This is like cyclecounter_cyc2ns(), but it is used for computing a |
| 80 | * time previous to the time stored in the cycle counter. |
| 81 | */ |
| 82 | static u64 cc_cyc2ns_backwards(const struct cyclecounter *cc, |
| 83 | cycle_t cycles, u64 mask, u64 frac) |
| 84 | { |
| 85 | u64 ns = (u64) cycles; |
| 86 | |
| 87 | ns = ((ns * cc->mult) - frac) >> cc->shift; |
| 88 | |
| 89 | return ns; |
| 90 | } |
| 91 | |
| 92 | u64 timecounter_cyc2time(struct timecounter *tc, |
| 93 | cycle_t cycle_tstamp) |
| 94 | { |
| 95 | u64 delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; |
| 96 | u64 nsec = tc->nsec, frac = tc->frac; |
| 97 | |
| 98 | /* |
| 99 | * Instead of always treating cycle_tstamp as more recent |
| 100 | * than tc->cycle_last, detect when it is too far in the |
| 101 | * future and treat it as old time stamp instead. |
| 102 | */ |
| 103 | if (delta > tc->cc->mask / 2) { |
| 104 | delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; |
| 105 | nsec -= cc_cyc2ns_backwards(tc->cc, delta, tc->mask, frac); |
| 106 | } else { |
| 107 | nsec += cyclecounter_cyc2ns(tc->cc, delta, tc->mask, &frac); |
| 108 | } |
| 109 | |
| 110 | return nsec; |
| 111 | } |
| 112 | EXPORT_SYMBOL_GPL(timecounter_cyc2time); |