diff options
Diffstat (limited to 'kernel/time/timecounter.c')
-rw-r--r-- | kernel/time/timecounter.c | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/kernel/time/timecounter.c b/kernel/time/timecounter.c new file mode 100644 index 000000000000..59a1ec3a57cb --- /dev/null +++ b/kernel/time/timecounter.c | |||
@@ -0,0 +1,95 @@ | |||
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); | ||