Commit | Line | Data |
---|---|---|
74d23cc7 RC |
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 | } | |
29 | EXPORT_SYMBOL_GPL(timecounter_init); | |
30 | ||
31 | /** | |
32 | * timecounter_read_delta - get nanoseconds since last call of this function | |
33 | * @tc: Pointer to time counter | |
34 | * | |
35 | * When the underlying cycle counter runs over, this will be handled | |
36 | * correctly as long as it does not run over more than once between | |
37 | * calls. | |
38 | * | |
39 | * The first call to this function for a new time counter initializes | |
40 | * the time tracking and returns an undefined result. | |
41 | */ | |
42 | static u64 timecounter_read_delta(struct timecounter *tc) | |
43 | { | |
44 | cycle_t cycle_now, cycle_delta; | |
45 | u64 ns_offset; | |
46 | ||
47 | /* read cycle counter: */ | |
48 | cycle_now = tc->cc->read(tc->cc); | |
49 | ||
50 | /* calculate the delta since the last timecounter_read_delta(): */ | |
51 | cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask; | |
52 | ||
53 | /* convert to nanoseconds: */ | |
54 | ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta); | |
55 | ||
56 | /* update time stamp of timecounter_read_delta() call: */ | |
57 | tc->cycle_last = cycle_now; | |
58 | ||
59 | return ns_offset; | |
60 | } | |
61 | ||
62 | u64 timecounter_read(struct timecounter *tc) | |
63 | { | |
64 | u64 nsec; | |
65 | ||
66 | /* increment time by nanoseconds since last call */ | |
67 | nsec = timecounter_read_delta(tc); | |
68 | nsec += tc->nsec; | |
69 | tc->nsec = nsec; | |
70 | ||
71 | return nsec; | |
72 | } | |
73 | EXPORT_SYMBOL_GPL(timecounter_read); | |
74 | ||
75 | u64 timecounter_cyc2time(struct timecounter *tc, | |
76 | cycle_t cycle_tstamp) | |
77 | { | |
78 | u64 cycle_delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; | |
79 | u64 nsec; | |
80 | ||
81 | /* | |
82 | * Instead of always treating cycle_tstamp as more recent | |
83 | * than tc->cycle_last, detect when it is too far in the | |
84 | * future and treat it as old time stamp instead. | |
85 | */ | |
86 | if (cycle_delta > tc->cc->mask / 2) { | |
87 | cycle_delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; | |
88 | nsec = tc->nsec - cyclecounter_cyc2ns(tc->cc, cycle_delta); | |
89 | } else { | |
90 | nsec = cyclecounter_cyc2ns(tc->cc, cycle_delta) + tc->nsec; | |
91 | } | |
92 | ||
93 | return nsec; | |
94 | } | |
95 | EXPORT_SYMBOL_GPL(timecounter_cyc2time); |