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#include <autoconf.h>
14
15#include <assert.h>
16#include <stdio.h>
17#include <stdbool.h>
18#include <stdio.h>
19#include <errno.h>
20#include <sel4/sel4.h>
21#include <sel4/arch/constants.h>
22#include <camkes.h>
23#include <camkes/irq.h>
24#include <platsupport/time_manager.h>
25#include <platsupport/local_time_manager.h>
26#include <platsupport/irq.h>
27#include <utils/util.h>
28#include <sel4utils/sel4_zf_logif.h>
29#include <simple/simple.h>
30#include <camkes/io.h>
31
32#include "plat.h"
33
34/* ltimer for accessing timer devices */
35static ltimer_t ltimer;
36/* time manager for timeout multiplexing */
37static time_manager_t time_manager;
38
39/* declare the memory needed for the clients
40 * this field tracks which timeouts have triggered
41 * for a specific client */
42uint32_t *client_state = NULL;
43
44/* Prototype for this function is not generated by the camkes templates yet */
45seL4_Word the_timer_get_sender_id();
46void the_timer_emit(unsigned int);
47int the_timer_largest_badge(void);
48
49static ps_io_ops_t io_ops;
50
51static inline uint64_t current_time_ns()
52{
53    uint64_t time;
54    int error = ltimer_get_time(&ltimer, &time);
55    ZF_LOGF_IF(error, "Failed to get time");
56    return time;
57}
58
59static inline unsigned int get_time_token(int cid, int tid)
60{
61    return (unsigned int) cid * timers_per_client + tid;
62}
63
64static int signal_client(uintptr_t token)
65{
66
67    int cid = ((int) token) / timers_per_client;
68    int tid = ((int) token) % timers_per_client;
69
70    assert(client_state != NULL);
71
72    client_state[cid] |= BIT(tid);
73    the_timer_emit(cid + 1);
74
75    return 0;
76}
77
78void time_server_ltimer_handle(UNUSED void *empty_token, ltimer_event_t ltimer_event)
79{
80    int error = time_server_lock();
81    ZF_LOGF_IF(error, "Failed to lock time server");
82
83    error = tm_update(&time_manager);
84    ZF_LOGF_IF(error, "Failed to update time manager");
85
86    error = time_server_unlock();
87    ZF_LOGF_IF(error, "Failed to unlock time server");
88}
89
90static int _oneshot_relative(int cid, int tid, uint64_t ns)
91{
92    if (tid >= timers_per_client || tid < 0) {
93        ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client);
94        return -1;
95    }
96
97    int error = time_server_lock();
98    ZF_LOGF_IF(error, "Failed to lock time server");
99
100    unsigned int id = get_time_token(cid, tid);
101    error = tm_register_rel_cb(&time_manager, ns, id, signal_client, (uintptr_t) id);
102    ZF_LOGF_IF(error, "Failed to set timeout");
103
104    error = time_server_unlock();
105    ZF_LOGF_IF(error, "Failed to unlock time server");
106    return 0;
107}
108
109static int _oneshot_absolute(int cid, int tid, uint64_t ns)
110{
111    if (tid >= timers_per_client || tid < 0) {
112        ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client);
113        return -1;
114    }
115
116    int error = time_server_lock();
117    ZF_LOGF_IF(error, "Failed to lock time server");
118
119    unsigned int token = get_time_token(cid, tid);
120
121    error = tm_register_abs_cb(&time_manager, ns, token, signal_client, (uintptr_t) token);
122    if (error == ETIME) {
123        signal_client(token);
124        error = 0;
125    }
126    ZF_LOGF_IF(error, "Failed to set timeout");
127
128    error = time_server_unlock();
129    ZF_LOGF_IF(error, "Failed to unlock time server");
130    return 0;
131}
132
133static int _periodic(int cid, int tid, uint64_t ns)
134{
135    if (tid >= timers_per_client || tid < 0) {
136        ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client);
137        return -1;
138    }
139
140    int error = time_server_lock();
141    ZF_LOGF_IF(error, "Failed to lock time server");
142
143    unsigned int token = get_time_token(cid, tid);
144    error = tm_register_periodic_cb(&time_manager, ns, 0, token, signal_client, (uintptr_t) token);
145    ZF_LOGF_IF(error, "Failed to set timeout");
146
147    error = time_server_unlock();
148    ZF_LOGF_IF(error, "Failed to unlock time server");
149    return 0;
150}
151
152static int _stop(int cid, int tid)
153{
154    if (tid >= timers_per_client || tid < 0) {
155        ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client);
156        return -1;
157    }
158    int error = time_server_lock();
159    ZF_LOGF_IF(error, "Failed to lock time server");
160
161    error = tm_deregister_cb(&time_manager, get_time_token(cid, tid));
162    ZF_LOGF_IF(error, "Failed to deregister callback");
163
164    error = time_server_unlock();
165    ZF_LOGF_IF(error, "Failed to unlock time server");
166    return 0;
167}
168
169static unsigned int _completed(int cid)
170{
171    int error = time_server_lock();
172    ZF_LOGF_IF(error, "Failed to lock time server");
173
174    assert(client_state != NULL);
175    unsigned int ret = client_state[cid];
176    client_state[cid] = 0;
177
178    error = time_server_unlock();
179    ZF_LOGF_IF(error, "Failed to unlock time server");
180
181    return ret;
182}
183
184static uint64_t _time(int cid)
185{
186    return current_time_ns();
187}
188
189/* substract 1 from the badge as we started counting badges at 1
190 * to avoid using the 0 badge */
191int the_timer_oneshot_relative(int id, uint64_t ns)
192{
193    return _oneshot_relative(the_timer_get_sender_id() - 1, id, ns);
194}
195
196int the_timer_oneshot_absolute(int id, uint64_t ns)
197{
198    return _oneshot_absolute(the_timer_get_sender_id() - 1, id, ns);
199}
200
201int the_timer_periodic(int id, uint64_t ns)
202{
203    return _periodic(the_timer_get_sender_id() - 1, id, ns);
204}
205
206int the_timer_stop(int id)
207{
208    return _stop(the_timer_get_sender_id() - 1, id);
209}
210
211unsigned int the_timer_completed()
212{
213    return _completed(the_timer_get_sender_id() - 1);
214}
215
216uint64_t the_timer_time()
217{
218    return _time(the_timer_get_sender_id() - 1);
219}
220
221void post_init()
222{
223    int error = time_server_lock();
224    ZF_LOGF_IF(error, "Failed to lock timer server");
225
226    error = camkes_io_ops(&io_ops);
227    ZF_LOGF_IF(error, "Failed to get camkes_io_ops");
228
229    error = ps_calloc(&(io_ops.malloc_ops), the_timer_largest_badge(), sizeof(*client_state), (void **) &client_state);
230    ZF_LOGF_IF(error, "Failed to allocate client state");
231
232    if (plat_pre_init) {
233        plat_pre_init();
234    }
235
236    error = ltimer_default_init(&ltimer, io_ops, time_server_ltimer_handle, NULL);
237    ZF_LOGF_IF(error, "Failed to init timer");
238
239    if (plat_post_init) {
240        plat_post_init(&ltimer);
241    }
242
243    int num_timers = timers_per_client * the_timer_largest_badge();
244    tm_init(&time_manager, &ltimer, &io_ops, num_timers);
245    for (unsigned int i = 0; i < num_timers; i++) {
246        error = tm_alloc_id_at(&time_manager, i);
247        ZF_LOGF_IF(error, "Failed to alloc id at %u\n", i);
248    }
249
250    error = time_server_unlock();
251    ZF_LOGF_IF(error, "Failed to unlock timer server");
252
253    set_putchar(putchar_putchar);
254}
255