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#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/rtc.h>
20#include <platsupport/plat/dmt.h>
21#include <platsupport/io.h>
22
23#include "../../ltimer.h"
24
25/*
26 * We use two dm timers: one to keep track of an absolute time, the other for timeouts.
27 */
28/* hikey dualtimers have 2 timers per frame, we just use one from each */
29typedef struct {
30    dmt_t dmt_timeout;
31    dmt_t dmt_timestamp;
32    ps_io_ops_t ops;
33} hikey_ltimer_t;
34
35static int get_time(void *data, uint64_t *time)
36{
37    hikey_ltimer_t *hikey_ltimer = data;
38    assert(data != NULL);
39    assert(time != NULL);
40
41    *time = dmt_get_time(&hikey_ltimer->dmt_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    hikey_ltimer_t *hikey_ltimer = data;
60    return dmt_set_timeout(&hikey_ltimer->dmt_timeout, ns, type == TIMEOUT_PERIODIC, true);
61}
62
63static int reset(void *data)
64{
65    hikey_ltimer_t *hikey_ltimer = data;
66    /* restart the rtc */
67    dmt_stop(&hikey_ltimer->dmt_timeout);
68    dmt_start(&hikey_ltimer->dmt_timeout);
69    return 0;
70}
71
72static void destroy(void *data)
73{
74    assert(data != NULL);
75    hikey_ltimer_t *hikey_ltimer = data;
76    /* NOTE: Note that timeout is primary, timestamp is secondary.
77     *  this means that timeout holds the region mapping.
78     *  We must first destroy timestamp before destroying timeout
79     *  so that the region is freed last. */
80    dmt_destroy(&hikey_ltimer->dmt_timestamp);
81    dmt_destroy(&hikey_ltimer->dmt_timeout);
82    ps_free(&hikey_ltimer->ops.malloc_ops, sizeof(hikey_ltimer_t), hikey_ltimer);
83}
84
85int ltimer_default_init(ltimer_t *ltimer, ps_io_ops_t ops, ltimer_callback_fn_t callback, void *callback_token)
86{
87    int error;
88
89    if (ltimer == NULL) {
90        ZF_LOGE("ltimer cannot be NULL");
91        return EINVAL;
92    }
93
94    error = create_ltimer_simple(
95                ltimer, ops, sizeof(hikey_ltimer_t),
96                get_time, set_timeout, reset, destroy
97            );
98    if (error) {
99        ZF_LOGE("Failed to create ltimer for hikey");
100        return error;
101    }
102
103    hikey_ltimer_t *hikey_ltimer = ltimer->data;
104    hikey_ltimer->ops = ops;
105
106    /* set up a DMT for timeouts */
107    dmt_config_t dmt_config = {
108        .fdt_path = DMT_PATH,
109        .user_cb_fn = callback,
110        .user_cb_token = callback_token,
111        .user_cb_event = LTIMER_TIMEOUT_EVENT
112    };
113
114    error = dmt_init(&hikey_ltimer->dmt_timeout, ops, dmt_config);
115    if (error) {
116        ZF_LOGE("Failed to init dmt timeout timer");
117        destroy(hikey_ltimer);
118        return error;
119    }
120
121    error = dmt_start(&hikey_ltimer->dmt_timeout);
122    if (error) {
123        ZF_LOGE("Failed to start dmt timeout timer");
124        destroy(hikey_ltimer);
125        return error;
126    }
127
128    /* set up a DMT for timestamps */
129    dmt_config.user_cb_event = LTIMER_OVERFLOW_EVENT;
130
131    error = dmt_init_secondary(&hikey_ltimer->dmt_timestamp, &hikey_ltimer->dmt_timeout, ops, dmt_config);
132    if (error) {
133        ZF_LOGE("Failed to init dmt secondary for timestamps");
134        destroy(hikey_ltimer);
135        return error;
136    }
137
138    error = dmt_start(&hikey_ltimer->dmt_timestamp);
139    if (error) {
140        ZF_LOGE("Failed to start dmt timestamp timer");
141        destroy(hikey_ltimer);
142        return error;
143    }
144
145    error = dmt_set_timeout_ticks(&hikey_ltimer->dmt_timestamp, UINT32_MAX, true, true);
146    if (error) {
147        ZF_LOGE("Failed to set ticks for dmt timestamp timer");
148        destroy(hikey_ltimer);
149        return error;
150    }
151
152    return 0;
153}
154
155/* This function is intended to be deleted,
156 * this is just left here for now so that stuff can compile */
157int ltimer_default_describe(ltimer_t *ltimer, ps_io_ops_t ops)
158{
159    ZF_LOGE("get_(nth/num)_(irqs/pmems) are not valid");
160    return EINVAL;
161}
162