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 <stdio.h>
14#include <assert.h>
15#include <errno.h>
16#include <stdlib.h>
17#include <utils/util.h>
18#include <inttypes.h>
19
20#include <platsupport/timer.h>
21#include <platsupport/plat/timer.h>
22
23/* enable bit */
24#define PVT_E_BIT           31
25
26/* enable auto-reload for periodic mode */
27#define PVT_PERIODIC_E_BIT  30
28
29/* 0-28 bits are for value, n + 1 trigger mode */
30#define PVT_VAL_MASK        0x1fffffff
31#define PVT_VAL_BITS        29
32
33/* write 1 to clear interruts */
34#define PCR_INTR_CLR_BIT    30
35
36/* counter value: decrements from PVT */
37#define PCR_VAL_MASK        0x1fffffff
38/* counter width is 29 bits */
39
40/* The NV-TMR timers are based on a 1us upcounter. This 1us upcounter literally
41 * counts up once every 1us, so the frequency here can only be 1us.
42 *
43 * You are required to initialize the upcounter with a divisor that will cause
44 * it to count up at 1us, and no other upcount value is supported.
45 *
46 * We set the divisor below in nv_get_timer(). The divisor divides the
47 * "clk_m" input clock (which operates at 12MHz) down to 1us.
48 */
49#define CLK_FREQ_MHZ        1
50#define CLK_FREQ_HZ         CLK_FREQ_MHZ * US_IN_S
51
52int nv_tmr_start(nv_tmr_t *tmr)
53{
54    tmr->tmr_map->pcr |= BIT(PCR_INTR_CLR_BIT);
55    tmr->tmr_map->pvt |= BIT(PVT_E_BIT);
56    return 0;
57}
58
59int nv_tmr_stop(nv_tmr_t *tmr)
60{
61    tmr->tmr_map->pvt &= ~(BIT(PVT_E_BIT));
62    return 0;
63}
64
65void nv_tmr_destroy(nv_tmr_t *tmr)
66{
67    if (tmr->reg_base) {
68        ps_pmem_unmap(&tmr->ops, tmr->timer_pmem, (void *) tmr->reg_base);
69    }
70
71    if (tmr->irq_id > PS_INVALID_IRQ_ID) {
72        int error = ps_irq_unregister(&tmr->ops.irq_ops, tmr->irq_id);
73        ZF_LOGF_IF(error, "Failed to unregister an IRQ");
74    }
75}
76
77#define INVALID_PVT_VAL 0x80000000
78
79static uint32_t get_ticks(uint64_t ns)
80{
81    uint64_t microsecond = ns / 1000ull;
82    uint64_t ticks = microsecond * CLK_FREQ_MHZ;
83    if (ticks >= BIT(PVT_VAL_BITS)) {
84        ZF_LOGE("ns too high %"PRIu64"\n", ns);
85        return INVALID_PVT_VAL;
86    }
87    return (uint32_t)ticks;
88}
89
90int nv_tmr_set_timeout(nv_tmr_t *tmr, bool periodic, uint64_t ns)
91{
92    uint32_t ticks = get_ticks(ns);
93    if (ticks == INVALID_PVT_VAL) {
94        ZF_LOGE("Invalid PVT val");
95        return EINVAL;
96    }
97    /* ack any pending irqs */
98    tmr->tmr_map->pcr |= BIT(PCR_INTR_CLR_BIT);
99    tmr->tmr_map->pvt = BIT(PVT_E_BIT) | ticks
100                        | ((periodic) ? BIT(PVT_PERIODIC_E_BIT) : 0);
101    return 0;
102}
103
104void nv_tmr_handle_irq(void *data, ps_irq_acknowledge_fn_t acknowledge_fn, void *ack_data)
105{
106    nv_tmr_t *tmr = data;
107    tmr->tmr_map->pcr |= BIT(PCR_INTR_CLR_BIT);
108    if (!(tmr->tmr_map->pvt & BIT(PVT_PERIODIC_E_BIT))) {
109        tmr->tmr_map->pvt &= ~(BIT(PVT_E_BIT));
110    }
111    ZF_LOGF_IF(acknowledge_fn(ack_data), "Failed to acknowledge the timer's interrupts");
112    if (tmr->user_callback) {
113        /* Call the ltimer's user callback */
114        tmr->user_callback(tmr->user_callback_token, LTIMER_TIMEOUT_EVENT);
115    }
116}
117
118uint64_t nv_tmr_get_time(nv_tmr_t *tmr)
119{
120    return (uint64_t)tmr->tmrus_map->cntr_1us * NS_IN_US;
121}
122
123#define TMRUS_USEC_CFG_DEFAULT   11
124
125static int allocate_register_callback(pmem_region_t pmem, unsigned curr_num, size_t num_regs, void *token)
126{
127    assert(token != NULL);
128    nv_tmr_t *tmr = token;
129    /* There's only one register region to map, map it in */
130    assert(num_regs == 1 && curr_num == 0);
131    tmr->reg_base = (uintptr_t) ps_pmem_map(&tmr->ops, pmem, false, PS_MEM_NORMAL);
132    if (tmr->reg_base == 0) {
133        return EIO;
134    }
135    tmr->timer_pmem = pmem;
136    return 0;
137}
138
139static int allocate_irq_callback(ps_irq_t irq, unsigned curr_num, size_t num_irqs, void *token)
140{
141    assert(token != NULL);
142    nv_tmr_t *tmr = token;
143    /* Skip all interrupts except the first */
144    if (curr_num != 0) {
145        return 0;
146    }
147
148    tmr->irq_id = ps_irq_register(&tmr->ops.irq_ops, irq, nv_tmr_handle_irq, tmr);
149    if (tmr->irq_id < 0) {
150        return EIO;
151    }
152
153    return 0;
154}
155
156int nv_tmr_init(nv_tmr_t *tmr, ps_io_ops_t ops, char *device_path, ltimer_callback_fn_t user_callback,
157                void *user_callback_token)
158{
159    if (!tmr || !device_path) {
160        return EINVAL;
161    }
162
163    /* setup the private structure */
164    tmr->ops = ops;
165    tmr->user_callback = user_callback;
166    tmr->user_callback_token = user_callback_token;
167    tmr->irq_id = PS_INVALID_IRQ_ID;
168
169    /* read the timer's path in the DTB */
170    ps_fdt_cookie_t *cookie = NULL;
171    int error = ps_fdt_read_path(&ops.io_fdt, &ops.malloc_ops, device_path, &cookie);
172    if (error) {
173        nv_tmr_destroy(tmr);
174        return ENODEV;
175    }
176
177    /* walk the registers and allocate them */
178    error = ps_fdt_walk_registers(&ops.io_fdt, cookie, allocate_register_callback, tmr);
179    if (error) {
180        nv_tmr_destroy(tmr);
181        return ENODEV;
182    }
183
184    /* walk the interrupts and allocate the first */
185    error = ps_fdt_walk_irqs(&ops.io_fdt, cookie, allocate_irq_callback, tmr);
186    if (error) {
187        nv_tmr_destroy(tmr);
188        return ENODEV;
189    }
190
191    error = ps_fdt_cleanup_cookie(&ops.malloc_ops, cookie);
192    if (error) {
193        nv_tmr_destroy(tmr);
194        return ENODEV;
195    }
196
197    tmr->tmr_map = (void *)(tmr->reg_base + NV_TMR_ID_OFFSET);
198    tmr->tmrus_map = (void *)(tmr->reg_base + TMRUS_OFFSET);
199    tmr->tmr_shared_map = (void *) tmr->reg_base + TMR_SHARED_OFFSET;
200
201    tmr->tmr_map->pvt = 0;
202    tmr->tmr_map->pcr = BIT(PCR_INTR_CLR_BIT);
203
204    /* The following #ifdef is for some platform specific init for tk1, tx1, tx2
205     * currently this custom init is only one line each hence using the ifdef.
206     * If the platform specific code becomes any larger, then it should be considered
207     * moving into a per platform nv_timer_plat_init function
208     */
209#ifdef CONFIG_PLAT_TX2
210    /* Route the interrupt to the correct shared interrupt number. */
211    tmr->tmr_shared_map->TKEIE[NV_TMR_ID] = BIT(NV_TMR_ID);
212#else
213    /* Just unconditionally set the divisor as if "clk_m" is always 12MHz,
214     * because it actually is always 12MHz on TK1 or TX1.
215     *
216     * Nvidia manual, section 5.2.2, Table 14:
217     * "clk_m: This clock (with DFT control) runs at 12 MHz, 13 MHz, 16.8 MHz,
218     * 19.2 MHz, 26 MHz, 38.4 MHz, or 48 MHz. Only 12 MHz is currently
219     * supported."
220     */
221    tmr->tmrus_map->usec_cfg = TMRUS_USEC_CFG_DEFAULT;
222#endif
223
224    return 0;
225}
226