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#include <stdio.h>
14#include <assert.h>
15#include <errno.h>
16#include <utils/util.h>
17#include <platsupport/timer.h>
18#include <platsupport/fdt.h>
19#include <platsupport/plat/timer.h>
20#include <utils/frequency.h>
21
22#include "../../ltimer.h"
23
24#define USER_MODE BIT(1)
25#define UNMASKED_INT BIT(2)
26#define TCLR_STARTTIMER BIT(0)
27#define TISR_IRQ_CLEAR BIT(0)
28
29//debug method
30static void print_regs(rk_t *rk)
31{
32    printf("load_count0          >> 0x%08x\n", rk->hw->load_count0);
33    printf("load_count1          >> 0x%08x\n", rk->hw->load_count1);
34    printf("current_cnt_lowbits  >> 0x%08x\n", rk->hw->current_value0);
35    printf("current_cnt_highbits >> 0x%08x\n", rk->hw->current_value1);
36    printf("load_count2          >> 0x%08x\n", rk->hw->load_count2);
37    printf("load_count3          >> 0x%08x\n", rk->hw->load_count3);
38    printf("interrupt_status     >> 0x%08x\n", rk->hw->interrupt_status);
39    printf("control_register     >> 0x%08x\n", rk->hw->control_register);
40}
41
42int rk_stop(rk_t *rk)
43{
44    if (rk == NULL) {
45        return EINVAL;
46    }
47
48    rk->hw->control_register = 0;
49    return 0;
50}
51
52uint64_t rk_get_time(rk_t *rk)
53{
54    if (rk == NULL) {
55        return EINVAL;
56    }
57    uint32_t val1 = rk->hw->current_value1;
58    uint32_t val2 = rk->hw->current_value0;
59    if (val1 != rk->hw->current_value1) {
60        val1 = rk->hw->current_value1;
61        val2 = rk->hw->current_value0;
62    }
63
64    uint64_t time = 0;
65    time = val1;
66    time <<= 32;
67    time |= val2;
68    return ((uint64_t)((time) * NS_IN_S) / 24000000ull);
69}
70
71int rk_start_timestamp_timer(rk_t *rk)
72{
73    assert(rk != NULL);
74    assert(rk->user_cb_event == LTIMER_OVERFLOW_EVENT);
75
76    rk->hw->control_register = 0;
77
78    //set timer to count up monotonically
79    rk->hw->load_count0  = 0xffffffff;
80    rk->hw->load_count1  = 0xffffffff;
81
82    rk->hw->control_register |= UNMASKED_INT | TCLR_STARTTIMER;
83    return 0;
84}
85
86int rk_set_timeout(rk_t *rk, uint64_t ns, bool periodic)
87{
88    if (rk == NULL) {
89        return EINVAL;
90    }
91    /* disable timer */
92    rk->hw->control_register = 0;
93
94    /* timer mode */
95    uint32_t tclrFlags = periodic ? 0 : USER_MODE;
96
97    /* load timer count */
98    uint64_t ticks = freq_ns_and_hz_to_cycles(ns, 24000000ull);
99    rk->hw->load_count0  = (uint32_t)(ticks & 0xffffffff);
100    rk->hw->load_count1  = (ticks >> 32);
101
102    /* enable timer with configs */
103    rk->hw->control_register |= TCLR_STARTTIMER | UNMASKED_INT | tclrFlags;
104    return 0;
105}
106
107static void rk_handle_irq(void *data, ps_irq_acknowledge_fn_t acknowledge_fn, void *ack_data)
108{
109    assert(data != NULL);
110    rk_t *rk = data;
111
112    /* ack any pending irqs */
113    rk->hw->interrupt_status = 1;
114
115    ZF_LOGF_IF(acknowledge_fn(ack_data), "Failed to acknowledge the timer's interrupts");
116    if (rk->user_cb_fn) {
117        rk->user_cb_fn(rk->user_cb_token, rk->user_cb_event);
118    }
119}
120
121bool rk_pending_match(rk_t *rk)
122{
123    return rk->hw->interrupt_status & TISR_IRQ_CLEAR;
124}
125
126void rk_destroy(rk_t *rk)
127{
128    int error;
129    if (rk->irq_id != PS_INVALID_IRQ_ID) {
130        error = ps_irq_unregister(&rk->ops.irq_ops, rk->irq_id);
131        ZF_LOGF_IF(error, "Failed to unregister IRQ");
132    }
133    if (rk->hw != NULL) {
134        rk_stop(rk);
135    }
136    if (rk->rk_map_base != NULL) {
137        /* use base because rk_map is adjusted based on whether secondary */
138        ps_pmem_unmap(&rk->ops, rk->pmem, (void *) rk->rk_map_base);
139    }
140}
141
142static int irq_index_walker(ps_irq_t irq, unsigned curr_num, size_t num_irqs, void *token)
143{
144    rk_t *rk = token;
145
146    if (RK_IRQ_CHOICE == curr_num) {
147        irq_id_t registered_id = ps_irq_register(&rk->ops.irq_ops, irq, rk_handle_irq, rk);
148        if (registered_id >= 0) {
149            rk->irq_id = registered_id;
150            rk->irq = irq;
151        } else {
152            /* Bail on error */
153            return registered_id;
154        }
155    }
156
157    return 0;
158}
159
160int rk_init(rk_t *rk, ps_io_ops_t ops, rk_config_t config)
161{
162    int error;
163
164    if (rk == NULL) {
165        ZF_LOGE("rk cannot be null");
166        return EINVAL;
167    }
168
169    rk->ops = ops;
170    rk->user_cb_fn = config.user_cb_fn;
171    rk->user_cb_token = config.user_cb_token;
172    rk->user_cb_event = config.user_cb_event;
173
174    ps_fdt_cookie_t *cookie = NULL;
175    error = ps_fdt_read_path(&ops.io_fdt, &ops.malloc_ops, config.fdt_path, &cookie);
176    if (error) {
177        ZF_LOGE("rockpro64 timer failed to read path (%d, %s)", error, config.fdt_path);
178        return error;
179    }
180
181    rk->rk_map_base = ps_fdt_index_map_register(&ops, cookie, RK_REG_CHOICE, &rk->pmem);
182    if (rk->rk_map_base == NULL) {
183        ZF_LOGE("rockpro64 timer failed to map registers");
184        return ENODEV;
185    }
186
187    error = ps_fdt_walk_irqs(&ops.io_fdt, cookie, irq_index_walker, rk);
188    if (error) {
189        ZF_LOGE("rockpro64 timer failed to register irqs (%d)", error);
190        return error;
191    }
192
193    rk->irq_id = ps_fdt_cleanup_cookie(&ops.malloc_ops, cookie);
194    if (rk->irq_id) {
195        ZF_LOGE("rockpro64 timer to clean up cookie (%d)", error);
196        return rk->irq_id;
197    }
198
199    rk->hw = rk->rk_map_base;
200
201    return 0;
202}
203
204/* initialise rk using the base address of rkp, so that we do not attempt to map
205 * that base address again but instead re-use it. */
206int rk_init_secondary(rk_t *rk, rk_t *rkp, ps_io_ops_t ops, rk_config_t config)
207{
208    int error;
209
210    if (rk == NULL || rkp == NULL) {
211        ZF_LOGE("rk or rkp cannot be null");
212        return EINVAL;
213    }
214
215    rk->ops = ops;
216    rk->user_cb_fn = config.user_cb_fn;
217    rk->user_cb_token = config.user_cb_token;
218    rk->user_cb_event = config.user_cb_event;
219
220    /* so that destroy does not try to unmap twice */
221    rk->rk_map_base = NULL;
222    /* just like dmt, rockpro64 has another timer in the same page at 0x20 offset */
223    rk->hw = (void *)((uintptr_t) rkp->rk_map_base) + 0x20;
224    /* similarly, the IRQ for this secondary timer is offset by 1 */
225    rk->irq_id = PS_INVALID_IRQ_ID;
226    ps_irq_t irq2 = { .type = PS_INTERRUPT, .irq.number = rkp->irq.irq.number + 1 };
227    irq_id_t irq2_id = ps_irq_register(&ops.irq_ops, irq2, rk_handle_irq, rk);
228    if (irq2_id < 0) {
229        ZF_LOGE("Failed to register secondary irq for rk timer");
230        return irq2_id;
231    }
232    rk->irq_id = irq2_id;
233
234    return 0;
235}
236