135728b82SThomas Gleixner // SPDX-License-Identifier: GPL-2.0+
274d23cc7SRichard Cochran /*
358c5fc2bSThomas Gleixner * Based on clocksource code. See commit 74d23cc704d1
474d23cc7SRichard Cochran */
574d23cc7SRichard Cochran #include <linux/export.h>
674d23cc7SRichard Cochran #include <linux/timecounter.h>
774d23cc7SRichard Cochran
timecounter_init(struct timecounter * tc,const struct cyclecounter * cc,u64 start_tstamp)874d23cc7SRichard Cochran void timecounter_init(struct timecounter *tc,
974d23cc7SRichard Cochran const struct cyclecounter *cc,
1074d23cc7SRichard Cochran u64 start_tstamp)
1174d23cc7SRichard Cochran {
1274d23cc7SRichard Cochran tc->cc = cc;
1374d23cc7SRichard Cochran tc->cycle_last = cc->read(cc);
1474d23cc7SRichard Cochran tc->nsec = start_tstamp;
152eebdde6SRichard Cochran tc->mask = (1ULL << cc->shift) - 1;
162eebdde6SRichard Cochran tc->frac = 0;
1774d23cc7SRichard Cochran }
1874d23cc7SRichard Cochran EXPORT_SYMBOL_GPL(timecounter_init);
1974d23cc7SRichard Cochran
2074d23cc7SRichard Cochran /**
2174d23cc7SRichard Cochran * timecounter_read_delta - get nanoseconds since last call of this function
2274d23cc7SRichard Cochran * @tc: Pointer to time counter
2374d23cc7SRichard Cochran *
2474d23cc7SRichard Cochran * When the underlying cycle counter runs over, this will be handled
2574d23cc7SRichard Cochran * correctly as long as it does not run over more than once between
2674d23cc7SRichard Cochran * calls.
2774d23cc7SRichard Cochran *
2874d23cc7SRichard Cochran * The first call to this function for a new time counter initializes
2974d23cc7SRichard Cochran * the time tracking and returns an undefined result.
3074d23cc7SRichard Cochran */
timecounter_read_delta(struct timecounter * tc)3174d23cc7SRichard Cochran static u64 timecounter_read_delta(struct timecounter *tc)
3274d23cc7SRichard Cochran {
33a5a1d1c2SThomas Gleixner u64 cycle_now, cycle_delta;
3474d23cc7SRichard Cochran u64 ns_offset;
3574d23cc7SRichard Cochran
3674d23cc7SRichard Cochran /* read cycle counter: */
3774d23cc7SRichard Cochran cycle_now = tc->cc->read(tc->cc);
3874d23cc7SRichard Cochran
3974d23cc7SRichard Cochran /* calculate the delta since the last timecounter_read_delta(): */
4074d23cc7SRichard Cochran cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask;
4174d23cc7SRichard Cochran
4274d23cc7SRichard Cochran /* convert to nanoseconds: */
432eebdde6SRichard Cochran ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta,
442eebdde6SRichard Cochran tc->mask, &tc->frac);
4574d23cc7SRichard Cochran
4674d23cc7SRichard Cochran /* update time stamp of timecounter_read_delta() call: */
4774d23cc7SRichard Cochran tc->cycle_last = cycle_now;
4874d23cc7SRichard Cochran
4974d23cc7SRichard Cochran return ns_offset;
5074d23cc7SRichard Cochran }
5174d23cc7SRichard Cochran
timecounter_read(struct timecounter * tc)5274d23cc7SRichard Cochran u64 timecounter_read(struct timecounter *tc)
5374d23cc7SRichard Cochran {
5474d23cc7SRichard Cochran u64 nsec;
5574d23cc7SRichard Cochran
5674d23cc7SRichard Cochran /* increment time by nanoseconds since last call */
5774d23cc7SRichard Cochran nsec = timecounter_read_delta(tc);
5874d23cc7SRichard Cochran nsec += tc->nsec;
5974d23cc7SRichard Cochran tc->nsec = nsec;
6074d23cc7SRichard Cochran
6174d23cc7SRichard Cochran return nsec;
6274d23cc7SRichard Cochran }
6374d23cc7SRichard Cochran EXPORT_SYMBOL_GPL(timecounter_read);
6474d23cc7SRichard Cochran
652eebdde6SRichard Cochran /*
662eebdde6SRichard Cochran * This is like cyclecounter_cyc2ns(), but it is used for computing a
672eebdde6SRichard Cochran * time previous to the time stored in the cycle counter.
682eebdde6SRichard Cochran */
cc_cyc2ns_backwards(const struct cyclecounter * cc,u64 cycles,u64 mask,u64 frac)692eebdde6SRichard Cochran static u64 cc_cyc2ns_backwards(const struct cyclecounter *cc,
70a5a1d1c2SThomas Gleixner u64 cycles, u64 mask, u64 frac)
712eebdde6SRichard Cochran {
722eebdde6SRichard Cochran u64 ns = (u64) cycles;
732eebdde6SRichard Cochran
742eebdde6SRichard Cochran ns = ((ns * cc->mult) - frac) >> cc->shift;
752eebdde6SRichard Cochran
762eebdde6SRichard Cochran return ns;
772eebdde6SRichard Cochran }
782eebdde6SRichard Cochran
timecounter_cyc2time(const struct timecounter * tc,u64 cycle_tstamp)79*07ff4aedSMarc Kleine-Budde u64 timecounter_cyc2time(const struct timecounter *tc,
80a5a1d1c2SThomas Gleixner u64 cycle_tstamp)
8174d23cc7SRichard Cochran {
822eebdde6SRichard Cochran u64 delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask;
832eebdde6SRichard Cochran u64 nsec = tc->nsec, frac = tc->frac;
8474d23cc7SRichard Cochran
8574d23cc7SRichard Cochran /*
8674d23cc7SRichard Cochran * Instead of always treating cycle_tstamp as more recent
8774d23cc7SRichard Cochran * than tc->cycle_last, detect when it is too far in the
8874d23cc7SRichard Cochran * future and treat it as old time stamp instead.
8974d23cc7SRichard Cochran */
902eebdde6SRichard Cochran if (delta > tc->cc->mask / 2) {
912eebdde6SRichard Cochran delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask;
922eebdde6SRichard Cochran nsec -= cc_cyc2ns_backwards(tc->cc, delta, tc->mask, frac);
9374d23cc7SRichard Cochran } else {
942eebdde6SRichard Cochran nsec += cyclecounter_cyc2ns(tc->cc, delta, tc->mask, &frac);
9574d23cc7SRichard Cochran }
9674d23cc7SRichard Cochran
9774d23cc7SRichard Cochran return nsec;
9874d23cc7SRichard Cochran }
9974d23cc7SRichard Cochran EXPORT_SYMBOL_GPL(timecounter_cyc2time);
100