1/*
2 * Copyright 2019, 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/* Implementation of a logical timer for rockpro64
14 *
15 * We use the 1 rk for timeouts and 1 for a millisecond tick.
16 *
17 * The configuration of timers that we use allows us to map only one page in.
18 */
19#include <platsupport/timer.h>
20#include <platsupport/ltimer.h>
21#include <platsupport/plat/timer.h>
22#include <utils/util.h>
23
24#include "../../ltimer.h"
25
26typedef struct {
27    rk_t rk_timeout;
28    rk_t rk_timestamp;
29    ps_io_ops_t ops;
30} rk_ltimer_t;
31
32static int get_time(void *data, uint64_t *time)
33{
34    assert(data != NULL);
35    assert(time != NULL);
36
37    rk_ltimer_t *rk_ltimer = data;
38    *time = rk_get_time(&rk_ltimer->rk_timestamp);
39    return 0;
40}
41
42static int set_timeout(void *data, uint64_t ns, timeout_type_t type)
43{
44    assert(data != NULL);
45    rk_ltimer_t *rk_ltimer = data;
46
47    if (type == TIMEOUT_ABSOLUTE) {
48        uint64_t current_time = 0;
49        int error = get_time(data, &current_time);
50        assert(error == 0);
51        ns -= current_time;
52    }
53
54    return rk_set_timeout(&rk_ltimer->rk_timeout, ns, type == TIMEOUT_PERIODIC);
55}
56
57static int reset(void *data)
58{
59    assert(data != NULL);
60    rk_ltimer_t *rk_ltimer = data;
61    /* just reset the timeout timer */
62    rk_stop(&rk_ltimer->rk_timeout);
63    /* no need to start it again, that is done by
64     * set_timeout automatically */
65    return 0;
66}
67
68static void destroy(void *data)
69{
70    assert(data != NULL);
71    rk_ltimer_t *rk_ltimer = data;
72    /* NOTE: Note that timeout is primary, timestamp is secondary.
73     *  this means that timeout holds the region mapping.
74     *  We must first destroy timestamp before destroying timeout
75     *  so that the region is freed last. */
76    rk_destroy(&rk_ltimer->rk_timestamp);
77    rk_destroy(&rk_ltimer->rk_timeout);
78    ps_free(&rk_ltimer->ops.malloc_ops, sizeof(rk_ltimer_t), rk_ltimer);
79}
80
81int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token)
82{
83    /* mostly copied from dmt.c */
84    int error;
85
86    if (ltimer == NULL) {
87        ZF_LOGE("ltimer cannot be NULL");
88        return EINVAL;
89    }
90
91    error = create_ltimer_simple(
92                ltimer, ops, sizeof(rk_ltimer_t),
93                get_time, set_timeout, reset, destroy
94            );
95    if (error) {
96        ZF_LOGE("Failed to create ltimer for rk");
97        return error;
98    }
99
100    rk_ltimer_t *rk_ltimer = ltimer->data;
101    rk_ltimer->ops = ops;
102
103    /* set up a timer for timeouts */
104    rk_config_t rk_config = {
105        .fdt_path = RK_TIMER_PATH,
106        .user_cb_fn = callback,
107        .user_cb_token = callback_token,
108        .user_cb_event = LTIMER_TIMEOUT_EVENT
109    };
110
111    error = rk_init(&rk_ltimer->rk_timeout, ops, rk_config);
112    if (error) {
113        ZF_LOGE("Failed to initialise timer (timeout)");
114        destroy(rk_ltimer);
115        return error;
116    }
117
118    /* no start for timeout timer */
119
120    /* set up a timer for timestamps */
121    rk_config.user_cb_event = LTIMER_OVERFLOW_EVENT;
122
123    error = rk_init_secondary(&rk_ltimer->rk_timestamp, &rk_ltimer->rk_timeout, ops, rk_config);
124    if (error) {
125        ZF_LOGE("Failed to initialise timer (timestamp)");
126        destroy(rk_ltimer);
127        return error;
128    }
129
130    error = rk_start_timestamp_timer(&rk_ltimer->rk_timestamp);
131    if (error) {
132        ZF_LOGE("Failed to start timestamp timer");
133        destroy(rk_ltimer);
134        return error;
135    }
136
137    return 0;
138}
139
140/* This function is intended to be deleted,
141 * this is just left here for now so that stuff can compile */
142int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops)
143{
144    ZF_LOGE("get_(nth/num)_(irqs/pmems) are not valid");
145    return EINVAL;
146}
147