timer.h
1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 #ifndef TIMER_H 3 #define TIMER_H 4 5 #include <types.h> 6 7 #define NSECS_PER_SEC 1000000000 8 #define USECS_PER_SEC 1000000 9 #define MSECS_PER_SEC 1000 10 #define USECS_PER_MSEC (USECS_PER_SEC / MSECS_PER_SEC) 11 12 /* The time structures are defined to be a representation of the time since 13 * coreboot started executing one of its stages. The reason for using structures 14 * is to allow for changes in the future. The structures' details are exposed 15 * so that the compiler can allocate space on the stack and use in other 16 * structures. In other words, accessing any field within this structure 17 * outside of the core timer code is not supported. */ 18 19 struct mono_time { 20 uint64_t microseconds; 21 }; 22 23 /* A timeout_callback structure is used for the book keeping for scheduling 24 * work in the future. When a callback is called the structure can be 25 * re-used for scheduling as it is not being tracked by the core timer 26 * library any more. */ 27 struct timeout_callback { 28 void *priv; 29 void (*callback)(struct timeout_callback *tocb); 30 /* Not for public use. The timer library uses the fields below. */ 31 struct mono_time expiration; 32 }; 33 34 /* Obtain the current monotonic time. The assumption is that the time counts 35 * up from the value 0 with value 0 being the point when the timer was 36 * initialized. Additionally, the timer is assumed to only be valid for the 37 * duration of the boot. 38 * 39 * Note that any implementations of timer_monotonic_get() 40 * need to ensure its timesource does not roll over within 10 secs. The reason 41 * is that the time between calls to timer_monotonic_get() may be on order 42 * of 10 seconds. */ 43 void timer_monotonic_get(struct mono_time *mt); 44 45 /* Returns 1 if callbacks still present in the queue. 0 if no timers left. */ 46 int timers_run(void); 47 48 /* Schedule a callback to be ran microseconds from time of invocation. 49 * 0 returned on success, < 0 on error. */ 50 int timer_sched_callback(struct timeout_callback *tocb, uint64_t us); 51 52 /* Set an absolute time to a number of microseconds. */ 53 static inline void mono_time_set_usecs(struct mono_time *mt, uint64_t us) 54 { 55 mt->microseconds = us; 56 } 57 58 /* Set an absolute time to a number of milliseconds. */ 59 static inline void mono_time_set_msecs(struct mono_time *mt, uint64_t ms) 60 { 61 mt->microseconds = ms * USECS_PER_MSEC; 62 } 63 64 /* Add microseconds to an absolute time. */ 65 static inline void mono_time_add_usecs(struct mono_time *mt, int64_t us) 66 { 67 mt->microseconds += us; 68 } 69 70 /* Add milliseconds to an absolute time. */ 71 static inline void mono_time_add_msecs(struct mono_time *mt, int64_t ms) 72 { 73 mono_time_add_usecs(mt, ms * USECS_PER_MSEC); 74 } 75 76 /* Compare two absolute times: Return -1, 0, or 1 if t1 is <, =, or > t2, 77 * respectively. */ 78 static inline int mono_time_cmp(const struct mono_time *t1, 79 const struct mono_time *t2) 80 { 81 if (t1->microseconds == t2->microseconds) 82 return 0; 83 84 if (t1->microseconds < t2->microseconds) 85 return -1; 86 87 return 1; 88 } 89 90 /* Return true if t1 after t2 */ 91 static inline int mono_time_after(const struct mono_time *t1, 92 const struct mono_time *t2) 93 { 94 return mono_time_cmp(t1, t2) > 0; 95 } 96 97 /* Return true if t1 before t2. */ 98 static inline int mono_time_before(const struct mono_time *t1, 99 const struct mono_time *t2) 100 { 101 return mono_time_cmp(t1, t2) < 0; 102 } 103 104 /* Return time difference between t1 and t2. i.e. t2 - t1. */ 105 static inline int64_t mono_time_diff_microseconds(const struct mono_time *t1, 106 const struct mono_time *t2) 107 { 108 return t2->microseconds - t1->microseconds; 109 } 110 111 struct stopwatch { 112 struct mono_time start; 113 struct mono_time current; 114 struct mono_time expires; 115 }; 116 117 static inline void stopwatch_init(struct stopwatch *sw) 118 { 119 if (CONFIG(HAVE_MONOTONIC_TIMER)) 120 timer_monotonic_get(&sw->start); 121 else 122 sw->start.microseconds = 0; 123 124 sw->current = sw->expires = sw->start; 125 } 126 127 static inline void stopwatch_init_usecs_expire(struct stopwatch *sw, uint64_t us) 128 { 129 stopwatch_init(sw); 130 mono_time_add_usecs(&sw->expires, us); 131 } 132 133 static inline void stopwatch_init_msecs_expire(struct stopwatch *sw, uint64_t ms) 134 { 135 stopwatch_init_usecs_expire(sw, USECS_PER_MSEC * ms); 136 } 137 138 /* 139 * Tick the stopwatch to collect the current time. 140 */ 141 static inline void stopwatch_tick(struct stopwatch *sw) 142 { 143 if (CONFIG(HAVE_MONOTONIC_TIMER)) 144 timer_monotonic_get(&sw->current); 145 else 146 sw->current.microseconds = 0; 147 } 148 149 /* 150 * Tick and check the stopwatch for expiration. Returns non-zero on expiration. 151 */ 152 static inline int stopwatch_expired(struct stopwatch *sw) 153 { 154 stopwatch_tick(sw); 155 return !mono_time_before(&sw->current, &sw->expires); 156 } 157 158 /* 159 * Tick and check the stopwatch as long as it has not expired. 160 */ 161 static inline void stopwatch_wait_until_expired(struct stopwatch *sw) 162 { 163 while (!stopwatch_expired(sw)) 164 ; 165 } 166 167 /* 168 * Return number of microseconds since starting the stopwatch. 169 */ 170 static inline int64_t stopwatch_duration_usecs(struct stopwatch *sw) 171 { 172 /* 173 * If the stopwatch hasn't been ticked (current == start) tick 174 * the stopwatch to gather the accumulated time. 175 */ 176 if (!mono_time_cmp(&sw->start, &sw->current)) 177 stopwatch_tick(sw); 178 179 return mono_time_diff_microseconds(&sw->start, &sw->current); 180 } 181 182 static inline int64_t stopwatch_duration_msecs(struct stopwatch *sw) 183 { 184 return stopwatch_duration_usecs(sw) / USECS_PER_MSEC; 185 } 186 187 /* 188 * Helper macro to wait until a condition becomes true or a timeout elapses. 189 * 190 * condition: a C expression to wait for 191 * timeout: timeout, in microseconds 192 * 193 * Returns: 194 * 0 if the condition still evaluates to false after the timeout elapsed, 195 * >0 if the condition evaluates to true. The return value is the amount of 196 * microseconds waited (at least 1). 197 */ 198 #define wait_us(timeout_us, condition) \ 199 ({ \ 200 int64_t __ret = 0; \ 201 struct stopwatch __sw; \ 202 stopwatch_init_usecs_expire(&__sw, timeout_us); \ 203 do { \ 204 if (condition) { \ 205 stopwatch_tick(&__sw); \ 206 __ret = stopwatch_duration_usecs(&__sw); \ 207 if (!__ret) /* make sure it evaluates to true */\ 208 __ret = 1; \ 209 break; \ 210 } \ 211 } while (!stopwatch_expired(&__sw)); \ 212 __ret; \ 213 }) 214 215 #define wait_ms(timeout_ms, condition) \ 216 DIV_ROUND_UP(wait_us((timeout_ms) * USECS_PER_MSEC, condition), \ 217 USECS_PER_MSEC) 218 219 #endif /* TIMER_H */