1/*
2 * Copyright 2020, 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/* Minimal implementation of a logical timer for zynq
14 *
15 * Does not implement some functions yet.
16 */
17#include <platsupport/timer.h>
18#include <platsupport/ltimer.h>
19#include <platsupport/plat/timer.h>
20
21#include <utils/util.h>
22
23#include "../../ltimer.h"
24
25/* Use ttc0_timer1 for timeouts/sleep */
26#define TTC_TIMEOUT   TTC0_TIMER1
27/* Use ttc1_timer1 to keep running for timestamp/gettime */
28#define TTC_TIMESTAMP TTC1_TIMER1
29
30#define N_TTCS 2
31#define TIMEOUT_IDX 0
32#define TIMESTAMP_IDX 1
33
34typedef struct {
35    ttc_t ttcs[N_TTCS];
36    ps_io_ops_t ops;
37    bool timeout_initialised;
38    bool timestamp_initialised;
39} ttc_ltimer_t;
40
41static int get_time(void *data, uint64_t *time)
42{
43    assert(data != NULL);
44    assert(time != NULL);
45    ttc_ltimer_t *ttc_ltimer = data;
46    *time = ttc_get_time(&ttc_ltimer->ttcs[TIMESTAMP_IDX]);
47    return 0;
48}
49
50static int get_resolution(void *data, uint64_t *resolution)
51{
52    return ENOSYS;
53}
54
55static int set_timeout(void *data, uint64_t ns, timeout_type_t type)
56{
57    assert(data != NULL);
58    ttc_ltimer_t *ttc_ltimer = data;
59
60    if (type == TIMEOUT_ABSOLUTE) {
61        uint64_t time = 0;
62        get_time(data, &time);
63        if (ns <= time) {
64            return ETIME;
65        } else {
66            ns -= time;
67        }
68    }
69
70    return ttc_set_timeout(&ttc_ltimer->ttcs[TIMEOUT_IDX], ns, type == TIMEOUT_PERIODIC);
71}
72
73static int reset(void *data)
74{
75    assert(data != NULL);
76    ttc_ltimer_t *ttc_ltimer = data;
77
78    /* reset the timers */
79    ttc_stop(&ttc_ltimer->ttcs[TIMEOUT_IDX]);
80    ttc_start(&ttc_ltimer->ttcs[TIMEOUT_IDX]);
81    ttc_stop(&ttc_ltimer->ttcs[TIMESTAMP_IDX]);
82    ttc_start(&ttc_ltimer->ttcs[TIMESTAMP_IDX]);
83
84    return 0;
85}
86
87static void destroy(void *data)
88{
89    assert(data);
90
91    ttc_ltimer_t *ttc_ltimer = data;
92
93    int error = 0;
94
95    if (ttc_ltimer->timeout_initialised) {
96        error = ttc_destroy(&ttc_ltimer->ttcs[TIMEOUT_IDX]);
97        ZF_LOGF_IF(error, "Failed to de-allocate the timeout timer");
98    }
99
100    if (ttc_ltimer->timestamp_initialised) {
101        error = ttc_destroy(&ttc_ltimer->ttcs[TIMESTAMP_IDX]);
102        ZF_LOGF_IF(error, "Failed to de-allocate the timestamp timer");
103    }
104
105    ps_free(&ttc_ltimer->ops.malloc_ops, sizeof(ttc_ltimer), ttc_ltimer);
106}
107
108static int create_ltimer(ltimer_t *ltimer, ps_io_ops_t ops)
109{
110    ltimer->get_time = get_time;
111    ltimer->get_resolution = get_resolution;
112    ltimer->set_timeout = set_timeout;
113    ltimer->reset = reset;
114    ltimer->destroy = destroy;
115
116    int error = ps_calloc(&ops.malloc_ops, 1, sizeof(ttc_ltimer_t), &ltimer->data);
117    if (error) {
118        return error;
119    }
120    assert(ltimer->data != NULL);
121
122    return 0;
123}
124
125int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token)
126{
127    int error = create_ltimer(ltimer, ops);
128    if (error) {
129        return error;
130    }
131
132    ttc_ltimer_t *ttc_ltimer = ltimer->data;
133    ttc_ltimer->ops = ops;
134
135    ttc_config_t config = {
136        .io_ops = ops,
137        .user_callback = callback,
138        .user_callback_token = callback_token,
139        .id = TTC_TIMEOUT,
140    };
141
142    ttc_config_t config_timestamp = {
143        .io_ops = ops,
144        .user_callback = callback,
145        .user_callback_token = callback_token,
146        .is_timestamp = true,
147        .id = TTC_TIMESTAMP,
148    };
149
150    error = ttc_init(&ttc_ltimer->ttcs[TIMEOUT_IDX], config);
151    if (error) {
152        ZF_LOGE("Failed to init the timeout timer");
153        ltimer_destroy(ltimer);
154        return error;
155    }
156
157    ttc_ltimer->timeout_initialised = true;
158
159    error = ttc_start(&ttc_ltimer->ttcs[TIMEOUT_IDX]);
160    if (error) {
161        ZF_LOGE("Failed to start the timeout timer");
162        ltimer_destroy(ltimer);
163        return error;
164    }
165
166    /* set the second ttc to be a timestamp counter */
167    error = ttc_init(&ttc_ltimer->ttcs[TIMESTAMP_IDX], config_timestamp);
168    if (error) {
169        ZF_LOGE("Failed to init the timestamp timer");
170        ltimer_destroy(ltimer);
171        return error;
172    }
173
174    ttc_ltimer->timestamp_initialised = true;
175
176    ttc_freerun(&ttc_ltimer->ttcs[TIMESTAMP_IDX]);
177    error = ttc_start(&ttc_ltimer->ttcs[TIMESTAMP_IDX]);
178    if (error) {
179        ZF_LOGE("Failed to start the timestamp timer");
180        ltimer_destroy(ltimer);
181        return error;
182    }
183
184    return 0;
185}
186
187/* This function is intended to be deleted,
188 * this is just left here for now so that stuff can compile */
189int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops)
190{
191    ZF_LOGE("get_(nth/num)_(irqs/pmems) are not valid");
192    return EINVAL;
193}
194