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#include <stdio.h>
13#include <assert.h>
14
15#include <utils/util.h>
16#include <utils/time.h>
17
18#include <platsupport/ltimer.h>
19#include <platsupport/plat/sp804.h>
20#include <platsupport/io.h>
21
22#include "../../ltimer.h"
23
24/*
25 * We use two sp804 timers: one to keep track of an absolute time, the other for timeouts.
26 */
27
28typedef struct {
29    /* fvp sp804 have 2 timers per frame, we just use one per each */
30    sp804_t sp804_timeout;
31    sp804_t sp804_timestamp;
32    ps_io_ops_t ops;
33} fvp_ltimer_t;
34
35static int get_time(void *data, uint64_t *time)
36{
37    fvp_ltimer_t *fvp_ltimer = data;
38    assert(data != NULL);
39    assert(time != NULL);
40
41    *time = sp804_get_time(&fvp_ltimer->sp804_timestamp);
42    return 0;
43}
44
45int set_timeout(void *data, uint64_t ns, timeout_type_t type)
46{
47    if (type == TIMEOUT_ABSOLUTE) {
48        uint64_t time;
49        int error = get_time(data, &time);
50        if (error) {
51            return error;
52        }
53        if (time > ns) {
54            return ETIME;
55        }
56        ns -= time;
57    }
58
59    fvp_ltimer_t *fvp_ltimer = data;
60    return sp804_set_timeout(&fvp_ltimer->sp804_timeout, ns, type == TIMEOUT_PERIODIC, true);
61}
62
63static int reset(void *data)
64{
65    fvp_ltimer_t *fvp_ltimer = data;
66    /* restart the rtc */
67    sp804_stop(&fvp_ltimer->sp804_timeout);
68    sp804_start(&fvp_ltimer->sp804_timeout);
69    return 0;
70}
71
72static void destroy(void *data)
73{
74    assert(data != NULL);
75    fvp_ltimer_t *fvp_ltimer = data;
76    sp804_destroy(&fvp_ltimer->sp804_timeout);
77    sp804_destroy(&fvp_ltimer->sp804_timestamp);
78    ps_free(&fvp_ltimer->ops.malloc_ops, sizeof(fvp_ltimer_t), fvp_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    int error;
84
85    if (ltimer == NULL) {
86        ZF_LOGE("ltimer cannot be NULL");
87        return EINVAL;
88    }
89
90    error = create_ltimer_simple(
91                ltimer, ops, sizeof(fvp_ltimer_t),
92                get_time, set_timeout, reset, destroy
93            );
94    if (error) {
95        ZF_LOGE("Failed to create ltimer simple");
96        return error;
97    }
98
99    fvp_ltimer_t *fvp_ltimer = ltimer->data;
100    fvp_ltimer->ops = ops;
101
102    /* set up an SP804 for timeouts */
103    sp804_config_t sp804_config = {
104        .fdt_path = SP804_TIMER1_PATH,
105        .user_cb_fn = callback,
106        .user_cb_token = callback_token,
107        .user_cb_event = LTIMER_TIMEOUT_EVENT
108    };
109
110    error = sp804_init(&fvp_ltimer->sp804_timeout, ops, sp804_config);
111    if (error) {
112        ZF_LOGE("Failed to init timeout timer");
113        destroy(&fvp_ltimer);
114        return error;
115    }
116
117    error = sp804_start(&fvp_ltimer->sp804_timeout);
118    if (error) {
119        ZF_LOGE("Failed to start timeout timer");
120        destroy(&fvp_ltimer);
121        return error;
122    }
123
124    /* another for timestamps */
125    sp804_config.fdt_path = SP804_TIMER2_PATH;
126    sp804_config.user_cb_event = LTIMER_OVERFLOW_EVENT;
127
128    error = sp804_init(&fvp_ltimer->sp804_timestamp, ops, sp804_config);
129    if (error) {
130        ZF_LOGE("Failed to init timestamp timer");
131        destroy(&fvp_ltimer);
132        return error;
133    }
134
135    error = sp804_start(&fvp_ltimer->sp804_timestamp);
136    if (error) {
137        ZF_LOGE("Failed to start timestamp timer");
138        destroy(&fvp_ltimer);
139        return error;
140    }
141
142    error = sp804_set_timeout_ticks(&fvp_ltimer->sp804_timestamp, UINT32_MAX, true, true);
143    if (error) {
144        ZF_LOGE("Failed to set timeout ticks for timer");
145        destroy(&fvp_ltimer);
146        return error;
147    }
148
149    return 0;
150}
151
152/* This function is intended to be deleted,
153 * this is just left here for now so that stuff can compile */
154int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops)
155{
156    ZF_LOGE("get_(nth/num)_(irqs/pmems) are not valid");
157    return EINVAL;
158}
159