1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#pragma once 14 15#include <platsupport/io.h> 16#include <platsupport/pmem.h> 17#include <platsupport/irq.h> 18/** 19 * This file provides the interface for an OS independant consisent timer interface. 20 * 21 * Implementations of this interface vary per platform - for some platforms, 22 * a single timer driver will back the implementation, for others, there may be many. 23 */ 24 25typedef enum { 26 TIMEOUT_PERIODIC, 27 TIMEOUT_ABSOLUTE, 28 TIMEOUT_RELATIVE 29} timeout_type_t; 30 31typedef enum { 32 LTIMER_TIMEOUT_EVENT, 33 LTIMER_OVERFLOW_EVENT 34} ltimer_event_t; 35 36/* 37 * Type signature of the callback function that can be accepted by the ltimer. 38 * The callbacks are invoked when an event occurs. These events are described 39 * by the ltimer_event_t type. 40 * 41 * The callbacks are expected to be reentrant with regards to the ltimer state. 42 * More specifically, the ltimer interface cannot guarantee that the internal 43 * state will be race-safe if two different callbacks invoke the ltimer API 44 * functions concurrently. 45 */ 46typedef void (*ltimer_callback_fn_t)(void *token, ltimer_event_t event); 47 48/* logical timers are the interface used by the timer manager to multiplex 49 * timer requests from clients - only one timeout can be registered at a time. 50 * logical timers may be backed by several timer drivers to implement the 51 * functionality 52 */ 53typedef struct ltimer { 54 /* 55 * Get the number of irqs this ltimer requires to be handled to function. 56 * 57 * @param data for the logical timer to use 58 * @return the number of irqs this timer needs. 59 */ 60 size_t (*get_num_irqs)(void *data); 61 62 /* 63 * Get the nth irq number. 64 * 65 * @param data for the logical timer to use 66 * @param n index of the irq, < get_num_irqs 67 * @param[out] irq variable to read the irq number into 68 * @return 0 on success, errno on error. 69 */ 70 int (*get_nth_irq)(void *data, size_t n, ps_irq_t *irq); 71 72 /* Get the number of phyiscal memory regions needed by this ltimer 73 * 74 * @param data for the logical timer to use 75 * @return the number of pmem regions this timer needs. 76 */ 77 size_t (*get_num_pmems)(void *data); 78 79 /* 80 * Populate a region structure with details of the nth pmem region this timer requires. 81 * 82 * @param data for the logical timer to use 83 * @param n index of the pmem_region, < get_num_pmems 84 * @return 0 on success, errno on error. 85 */ 86 int (*get_nth_pmem)(void *data, size_t n, pmem_region_t *region); 87 88 /* 89 * Read the current time in nanoseconds. Precision depends on the implementation, but 90 * the value is guaranteed to be monotonically increasing and at least millisecond accurate. 91 * 92 * @param data for the logical timer to use 93 * @param[out] time variable to read the time value into 94 * @return 0 on success, errno on error. 95 */ 96 int (*get_time)(void *data, uint64_t *time); 97 98 /* 99 * Get the precision of this time returned by get_time. i.e if the timer is 100 * millisecond precise return NS_IN_US. 101 * 102 * @param data for the logical timer to use 103 * @param[out] resolution variable to read the resoltion value into 104 * @return 0 on success, errno on error. 105 */ 106 int (*get_resolution)(void *data, uint64_t *resolution); 107 108 /* 109 * Set an irq to come in at a specific time. 110 * 111 * IRQs may come in earlier than requested due to implementation details. 112 * 113 * @param data for the logical timer to use 114 * @param ns ns value (depends on timer type) 115 * @param type type of timeout 116 * @return 0 on success, errno on error. 117 */ 118 int (*set_timeout)(void *data, uint64_t ns, timeout_type_t type); 119 120 /* 121 * Reset the timer into a state similar to what it was when first initialized. This 122 * should cancel any outstanding timeouts etc. It may or may not cause the monotonic 123 * upcounter to change (by resetting to 0 or otherwise) 124 * 125 * @param data for the logical timer to use 126 * @return 0 on success, errno on error. 127 */ 128 int (*reset)(void *data); 129 130 /* Destroy an ltimer, freeing any resources and turning off devices */ 131 void (*destroy)(void *data); 132 133 /* data for the implementation to use */ 134 void *data; 135} ltimer_t; 136 137/* Logical timer helper functions */ 138static inline int ltimer_get_resolution(ltimer_t *timer, uint64_t *resolution) 139{ 140 if (!timer) { 141 ZF_LOGE("Logical timer invalid!"); 142 return EINVAL; 143 } 144 145 if (!resolution) { 146 ZF_LOGE("time argument cannot be NULL"); 147 return EINVAL; 148 } 149 150 if (timer->get_resolution == NULL) { 151 ZF_LOGE("not implemented"); 152 return ENOSYS; 153 } 154 155 return timer->get_resolution(timer->data, resolution); 156} 157 158static inline int ltimer_set_timeout(ltimer_t *timer, uint64_t nanoseconds, timeout_type_t type) 159{ 160 if (!timer) { 161 ZF_LOGE("Logical timer invalid!"); 162 return EINVAL; 163 } 164 165 if (timer->set_timeout == NULL) { 166 ZF_LOGE("not implemented"); 167 return ENOSYS; 168 } 169 170 switch (type) { 171 case TIMEOUT_ABSOLUTE: 172 case TIMEOUT_PERIODIC: 173 case TIMEOUT_RELATIVE: 174 break; 175 default: 176 ZF_LOGE("Invalid timer type"); 177 return EINVAL; 178 } 179 180 return timer->set_timeout(timer->data, nanoseconds, type); 181} 182 183static inline size_t ltimer_get_num_irqs(ltimer_t *timer) 184{ 185 if (timer->get_num_irqs == NULL) { 186 /* assume no irqs for this timer */ 187 return 0; 188 } 189 190 return timer->get_num_irqs(timer->data); 191} 192 193static inline int ltimer_get_nth_irq(ltimer_t *timer, size_t n, ps_irq_t *irq) 194{ 195 if (!timer || !irq) { 196 ZF_LOGE("Arguments cannot be null"); 197 return EINVAL; 198 } 199 200 if (!timer->get_nth_irq || !timer->get_num_irqs) { 201 ZF_LOGE("not implemented"); 202 return ENOSYS; 203 } 204 205 size_t nirqs = ltimer_get_num_irqs(timer); 206 if (n >= nirqs) { 207 ZF_LOGD("n invalid"); 208 return EINVAL; 209 } 210 211 return timer->get_nth_irq(timer->data, n, irq); 212} 213 214static inline size_t ltimer_get_num_pmems(ltimer_t *timer) 215{ 216 if (timer->get_num_pmems == NULL) { 217 /* assume no physical memory required for this ltimer */ 218 return 0; 219 } 220 221 return timer->get_num_pmems(timer->data); 222} 223 224static inline int ltimer_get_nth_pmem(ltimer_t *timer, size_t n, pmem_region_t *pmem) 225{ 226 if (!timer || !pmem) { 227 ZF_LOGE("Arguments cannot be null"); 228 return EINVAL; 229 } 230 231 if (!timer->get_nth_pmem || !timer->get_num_pmems) { 232 ZF_LOGE("not implemented"); 233 return ENOSYS; 234 } 235 236 size_t npmems = ltimer_get_num_pmems(timer); 237 if (n >= npmems) { 238 ZF_LOGD("n invalid"); 239 return EINVAL; 240 } 241 242 return timer->get_nth_pmem(timer->data, n, pmem); 243} 244 245static inline int ltimer_get_time(ltimer_t *timer, uint64_t *time) 246{ 247 if (!timer) { 248 ZF_LOGE("Logical timer invalid!"); 249 return EINVAL; 250 } 251 252 if (!time) { 253 ZF_LOGE("time argument cannot be NULL"); 254 return EINVAL; 255 } 256 257 if (timer->get_time == NULL) { 258 ZF_LOGE("get_time not implemented"); 259 return ENOSYS; 260 } 261 262 return timer->get_time(timer->data, time); 263} 264 265static inline int ltimer_reset(ltimer_t *timer) 266{ 267 if (!timer) { 268 ZF_LOGE("Logical timer invalid!"); 269 return EINVAL; 270 } 271 272 if (timer->reset == NULL) { 273 ZF_LOGE("not implemented"); 274 return ENOSYS; 275 } 276 277 return timer->reset(timer->data); 278} 279 280static inline void ltimer_destroy(ltimer_t *timer) 281{ 282 if (!timer || !timer->destroy) { 283 ZF_LOGW("nothing to destroy"); 284 return; 285 } 286 287 return timer->destroy(timer->data); 288} 289 290/* Spinning delay functions */ 291static inline void ltimer_ns_delay(ltimer_t *timer, uint64_t nanoseconds) { 292 uint64_t start, end; 293 294 int error = ltimer_get_time(timer, &start); 295 /* spin */ 296 for (int i = 0; !error; i++) { 297 error = ltimer_get_time(timer, &end); 298 if (end - start >= nanoseconds) { 299 break; 300 } else if (i % 1000 == 0 && start == end) { 301 ZF_LOGD("Time doesn't appear to be changing"); 302 } 303 } 304} 305 306static inline void ltimer_s_delay(ltimer_t *timer, uint64_t seconds) { 307 ltimer_ns_delay(timer, seconds * NS_IN_S); 308} 309 310static inline void ltimer_ms_delay(ltimer_t *timer, uint64_t milliseconds) { 311 ltimer_ns_delay(timer, milliseconds * NS_IN_MS); 312} 313 314static inline void ltimer_us_delay(ltimer_t *timer, uint64_t microseconds) { 315 ltimer_ns_delay(timer, microseconds * NS_IN_US); 316} 317 318/* 319 * default init function -> platforms may provide multiple ltimers, but each 320 * must have a default 321 * 322 * The callback functions will be invoked every single time an event described 323 * by the 'ltimer_event_t' type occurs. A reminder that care should be taken 324 * with calling the ltimer functions inside the callback, the ltimer interface 325 * functions are not reentrant. 326 */ 327int ltimer_default_init(ltimer_t *timer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token); 328/* initialise the subset of functions required to get 329 * the resources this ltimer needs without initialising the actual timer 330 * drivers*/ 331int ltimer_default_describe(ltimer_t *timer, ps_io_ops_t ops); 332