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