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 beagle bone
14 *
15 * We use the 1 DMT for timeouts and 1 for a millisecond tick.
16 */
17#include <platsupport/timer.h>
18#include <platsupport/ltimer.h>
19#include <platsupport/plat/timer.h>
20#include <utils/frequency.h>
21
22#include <utils/util.h>
23
24#include "../../ltimer.h"
25
26typedef struct {
27    dmt_t dmt_timeout;
28    dmt_t dmt_timestamp;
29    ps_io_ops_t ops;
30} dmt_ltimer_t;
31
32static int get_time(void *data, uint64_t *time)
33{
34    assert(data != NULL);
35    assert(time != NULL);
36    dmt_ltimer_t *dmt_ltimer = data;
37
38    *time = dmt_get_time(&dmt_ltimer->dmt_timestamp);
39    return 0;
40}
41
42static int get_resolution(void *data, uint64_t *resolution)
43{
44    *resolution = NS_IN_MS;
45    return 0;
46}
47
48static int set_timeout(void *data, uint64_t ns, timeout_type_t type)
49{
50    assert(data != NULL);
51    dmt_ltimer_t *dmt_ltimer = data;
52
53    if (type == TIMEOUT_ABSOLUTE) {
54        uint64_t current_time = 0;
55        int error = get_time(data, &current_time);
56        assert(error == 0);
57        if (ns < current_time) {
58            return ETIME;
59        }
60        ns -= current_time;
61    }
62
63    return dmt_set_timeout(&dmt_ltimer->dmt_timeout, ns, type == TIMEOUT_PERIODIC);
64}
65
66static int reset(void *data)
67{
68    assert(data != NULL);
69    dmt_ltimer_t *dmt_ltimer = data;
70    dmt_stop(&dmt_ltimer->dmt_timeout);
71    dmt_stop(&dmt_ltimer->dmt_timestamp);
72    dmt_start(&dmt_ltimer->dmt_timeout);
73    dmt_start(&dmt_ltimer->dmt_timestamp);
74    return 0;
75}
76
77static void destroy(void *data)
78{
79    assert(data != NULL);
80    dmt_ltimer_t *dmt_ltimer = data;
81    dmt_destroy(&dmt_ltimer->dmt_timeout);
82    dmt_destroy(&dmt_ltimer->dmt_timestamp);
83    ps_free(&dmt_ltimer->ops.malloc_ops, sizeof(dmt_ltimer_t), dmt_ltimer);
84}
85
86int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token)
87{
88    int error;
89
90    error = create_ltimer_simple(
91                ltimer, ops, sizeof(dmt_ltimer_t),
92                get_time, set_timeout, reset, destroy
93            );
94    if (error) {
95        ZF_LOGE("Failed to create ltimer");
96        return error;
97    }
98    ltimer->get_resolution = get_resolution;
99
100    dmt_ltimer_t *dmt_ltimer = ltimer->data;
101    dmt_ltimer->ops = ops;
102
103    dmt_config_t dmt_config = {
104        .user_cb_fn = callback,
105        .user_cb_token = callback_token,
106        .user_cb_event = LTIMER_OVERFLOW_EVENT,
107        .fdt_path = DMTIMER2_PATH,
108    };
109
110    error = dmt_init(&dmt_ltimer->dmt_timestamp, ops, dmt_config);
111    if (error) {
112        ZF_LOGE("Failed to init dmt (TIMEKEEPING)");
113        destroy(dmt_ltimer);
114        return error;
115    }
116
117    error = dmt_start_ticking_timer(&dmt_ltimer->dmt_timestamp);
118    if (error) {
119        ZF_LOGE("Failed to start dmt (TIMEKEEPING)");
120        destroy(dmt_ltimer);
121        return error;
122    }
123
124    dmt_config.fdt_path = DMTIMER3_PATH;
125    dmt_config.user_cb_event = LTIMER_TIMEOUT_EVENT;
126
127    error = dmt_init(&dmt_ltimer->dmt_timeout, ops, dmt_config);
128    if (error) {
129        ZF_LOGE("Failed to init dmt (TIMEOUT)");
130        destroy(dmt_ltimer);
131        return error;
132    }
133
134    error = dmt_start(&dmt_ltimer->dmt_timeout);
135    if (error) {
136        ZF_LOGE("Failed to start dmt (TIMEOUT)");
137        destroy(dmt_ltimer);
138        return error;
139    }
140
141    return 0;
142}
143
144/* This function is intended to be deleted,
145 * this is just left here for now so that stuff can compile */
146int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops)
147{
148    ZF_LOGE("get_(nth/num)_(irqs/pmems) are not valid");
149    return EINVAL;
150}
151