1 // SPDX-License-Identifier: GPL-2.0+
3 * Based on clocksource code. See commit 74d23cc704d1
5 #include <linux/export.h>
6 #include <linux/timecounter.h>
8 void timecounter_init(struct timecounter
*tc
,
9 const struct cyclecounter
*cc
,
13 tc
->cycle_last
= cc
->read(cc
);
14 tc
->nsec
= start_tstamp
;
15 tc
->mask
= (1ULL << cc
->shift
) - 1;
18 EXPORT_SYMBOL_GPL(timecounter_init
);
21 * timecounter_read_delta - get nanoseconds since last call of this function
22 * @tc: Pointer to time counter
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
28 * The first call to this function for a new time counter initializes
29 * the time tracking and returns an undefined result.
31 static u64
timecounter_read_delta(struct timecounter
*tc
)
33 u64 cycle_now
, cycle_delta
;
36 /* read cycle counter: */
37 cycle_now
= tc
->cc
->read(tc
->cc
);
39 /* calculate the delta since the last timecounter_read_delta(): */
40 cycle_delta
= (cycle_now
- tc
->cycle_last
) & tc
->cc
->mask
;
42 /* convert to nanoseconds: */
43 ns_offset
= cyclecounter_cyc2ns(tc
->cc
, cycle_delta
,
46 /* update time stamp of timecounter_read_delta() call: */
47 tc
->cycle_last
= cycle_now
;
52 u64
timecounter_read(struct timecounter
*tc
)
56 /* increment time by nanoseconds since last call */
57 nsec
= timecounter_read_delta(tc
);
63 EXPORT_SYMBOL_GPL(timecounter_read
);
66 * This is like cyclecounter_cyc2ns(), but it is used for computing a
67 * time previous to the time stored in the cycle counter.
69 static u64
cc_cyc2ns_backwards(const struct cyclecounter
*cc
,
70 u64 cycles
, u64 mask
, u64 frac
)
72 u64 ns
= (u64
) cycles
;
74 ns
= ((ns
* cc
->mult
) - frac
) >> cc
->shift
;
79 u64
timecounter_cyc2time(struct timecounter
*tc
,
82 u64 delta
= (cycle_tstamp
- tc
->cycle_last
) & tc
->cc
->mask
;
83 u64 nsec
= tc
->nsec
, frac
= tc
->frac
;
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.
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
);
94 nsec
+= cyclecounter_cyc2ns(tc
->cc
, delta
, tc
->mask
, &frac
);
99 EXPORT_SYMBOL_GPL(timecounter_cyc2time
);