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 <stdlib.h>
17#include <stdint.h>
18
19#include <utils/util.h>
20
21#include <platsupport/mach/epit.h>
22#include <platsupport/plat/timer.h>
23
24#define CLEANUP_FAIL_TEXT "Failed to cleanup the EPIT after failing to initialise it"
25
26/* EPIT CONTROL REGISTER BITS */
27typedef enum {
28    /*
29     * This bit enables the EPIT.
30     */
31    EN = 0,
32
33    /*
34     * By setting this bit, then when EPIT is disabled (EN=0), then
35     * both Main Counter and Prescaler Counter freeze their count at
36     * current count values.
37     */
38    ENMOD = 1,
39
40    /*
41     *  This bit enables the generation of interrupt when a compare
42     *  event occurs
43     */
44    OCIEN = 2,
45
46    /*
47     * This bit is cleared by hardware reset. It controls whether the
48     * counter runs in free running mode OR set and forget mode.
49     */
50    RLD = 3,
51
52    /*
53     * Bits 4 - 15 determine the prescaler value by which the clock is divided
54     * before it goes into the counter.
55     *
56     * The prescaler used is the value in these bits + 1. ie:
57     *
58     * 0x00 divide by 1
59     * 0x01 divide by 2
60     * 0x10 divide by 3
61     *  .
62     *  .
63     *  .
64     * 0xFFF divide by 4096
65     *
66     */
67    PRESCALER = 4,
68
69    /*
70     * This bit controls the counter data when the modulus register is
71     * written. When this bit is set, all writes to the load register
72     * will overwrite the counter contents and the counter will
73     * subsequently start counting down from the programmed value.
74     */
75    IOVW = 17,
76
77    /*
78     * These bits select the clock input used to run the counter. After
79     * reset, the system functional clock is selected. The input clock
80     * can also be turned off if these bits are set to 00. This field
81     * value should only be changed when the EPIT is disabled.
82     */
83    CLKSRC = 24
84} epit_control_reg;
85
86enum IPGConstants {
87    IPG_CLK = 1, IPG_CLK_HIGHFREQ = 2, IPG_CLK_32K = 3
88};
89
90/* Memory map for EPIT (Enhanced Periodic Interrupt Timer). */
91struct epit_map {
92    /* epit control register */
93    uint32_t epitcr;
94    /* epit status register */
95    uint32_t epitsr;
96    /* epit load register */
97    uint32_t epitlr;
98    /* epit compare register */
99    uint32_t epitcmpr;
100    /* epit counter register */
101    uint32_t epitcnt;
102};
103
104int epit_stop(epit_t *epit)
105{
106    /* Disable timer irq. */
107    epit->epit_map->epitcr &= ~(BIT(EN));
108    return 0;
109}
110
111int epit_set_timeout_ticks(epit_t *epit, uint64_t counterValue, bool periodic)
112{
113    ZF_LOGF_IF(epit == NULL, "invalid epit provided");
114    ZF_LOGF_IF(epit->epit_map == NULL, "uninitialised epit provided");
115
116    if (counterValue >= (1ULL << 32)) {
117        /* Counter too large to be stored in 32 bits. */
118        ZF_LOGW("counterValue too high, going to be capping it\n");
119        counterValue = UINT32_MAX;
120    }
121
122    /* configure it and turn it on */
123    uint32_t reload_val = periodic ? BIT(RLD) : 0;
124    epit->epit_map->epitcr = reload_val | (IPG_CLK << CLKSRC) | /* Clock source = IPG */
125                             (epit->prescaler << PRESCALER) | /* Set the prescaler */
126                             BIT(IOVW) | /* Overwrite counter immediately on write */
127                             BIT(OCIEN) | /* Enable interrupt on comparison event */
128                             BIT(ENMOD) | /* Count from modulus on restart */
129                             BIT(EN);
130
131    /* hardware has a race condition where the epitlr won't be set properly
132     * - keep trying until it works
133     */
134    epit->epit_map->epitlr = counterValue;
135    while (epit->epit_map->epitlr != counterValue) {
136        epit->epit_map->epitlr = counterValue;
137    }
138
139    return 0;
140}
141
142int epit_set_timeout(epit_t *epit, uint64_t ns, bool periodic)
143{
144    if (epit->is_timestamp) {
145        ZF_LOGW("Using the EPIT as a timeout timer when it is configured as a timestamp timer");
146    }
147    ZF_LOGF_IF(epit == NULL, "invalid epit provided");
148    ZF_LOGF_IF(epit->epit_map == NULL, "uninitialised epit provided");
149    /* Set counter modulus - this effectively sets the timeouts to us but doesn't
150     * overflow as fast. */
151    uint64_t counterValue = (uint64_t)(IPG_FREQ / (epit->prescaler + 1)) * (ns / 1000ULL);
152
153    return epit_set_timeout_ticks(epit, counterValue, periodic);
154}
155
156uint64_t epit_get_time(epit_t *epit)
157{
158    if (!epit->is_timestamp) {
159        ZF_LOGW("Using the EPIT as a timestamp timer when it is configured as a timeout timer");
160    }
161    ZF_LOGF_IF(epit == NULL, "invalid epit provided");
162    ZF_LOGF_IF(epit->epit_map == NULL, "uninitialised epit provided");
163    uint64_t ticks = (epit->high_bits + !!epit->epit_map->epitsr) << 32llu;
164    ticks += (UINT32_MAX - epit->epit_map->epitcnt);
165    return (ticks * 1000llu) / (IPG_FREQ / (epit->prescaler + 1));
166}
167
168static void epit_handle_irq(void *data, ps_irq_acknowledge_fn_t acknowledge_fn, void *ack_data)
169{
170    assert(data != NULL);
171    epit_t *epit = data;
172    if (epit->epit_map->epitsr) {
173        /* ack the irq */
174        epit->epit_map->epitsr = 1;
175        if (epit->is_timestamp) {
176            epit->high_bits++;
177        }
178    }
179    /* acknowledge the interrupt and call the user callback if any */
180    ZF_LOGF_IF(acknowledge_fn(ack_data), "Failed to acknowledge the interrupt from the EPIT");
181    if (epit->user_callback) {
182        if (epit->is_timestamp) {
183            epit->user_callback(epit->user_callback_token, LTIMER_OVERFLOW_EVENT);
184        } else {
185            epit->user_callback(epit->user_callback_token, LTIMER_TIMEOUT_EVENT);
186        }
187    }
188}
189
190static int allocate_register_callback(pmem_region_t pmem, unsigned curr_num, size_t num_regs, void *token)
191{
192    assert(token != NULL);
193    /* Should only be called once. I.e. only one register field */
194    assert(curr_num == 0);
195    epit_t *epit = token;
196    epit->epit_map = (volatile struct epit_map *) ps_pmem_map(&epit->io_ops, pmem, false, PS_MEM_NORMAL);
197    if (!epit->epit_map) {
198        ZF_LOGE("Failed to map in registers for the EPIT");
199        return EIO;
200    }
201    epit->timer_pmem = pmem;
202    return 0;
203}
204
205static int allocate_irq_callback(ps_irq_t irq, unsigned curr_num, size_t num_irqs, void *token)
206{
207    assert(token != NULL);
208    /* Should only be called once. I.e. only one interrupt field */
209    assert(curr_num == 0);
210    epit_t *epit = token;
211    epit->irq_id = ps_irq_register(&epit->io_ops.irq_ops, irq, epit_handle_irq, epit);
212    if (epit->irq_id < 0) {
213        ZF_LOGE("Failed to register the EPIT interrupt with the IRQ interface");
214        return EIO;
215    }
216    return 0;
217}
218
219int epit_init(epit_t *epit, epit_config_t config)
220{
221    if (epit == NULL) {
222        ZF_LOGE("Epit cannot be NULL, must be preallocated");
223        return EINVAL;
224    }
225
226    /* Initialise the structure */
227    epit->io_ops = config.io_ops;
228    epit->user_callback = config.user_callback;
229    epit->user_callback_token = config.user_callback_token;
230    epit->irq_id = PS_INVALID_IRQ_ID;
231    epit->prescaler = config.prescaler;
232    epit->is_timestamp = config.is_timestamp;
233
234    /* Read the timer's path in the DTB */
235    ps_fdt_cookie_t *cookie = NULL;
236    int error = ps_fdt_read_path(&epit->io_ops.io_fdt, &epit->io_ops.malloc_ops, config.device_path, &cookie);
237    if (error) {
238        ZF_LOGF_IF(ps_fdt_cleanup_cookie(&epit->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT);
239        ZF_LOGF_IF(epit_destroy(epit), CLEANUP_FAIL_TEXT);
240        return ENODEV;
241    }
242
243    /* Walk the registers and allocate them */
244    error = ps_fdt_walk_registers(&epit->io_ops.io_fdt, cookie, allocate_register_callback, epit);
245    if (error) {
246        ZF_LOGF_IF(ps_fdt_cleanup_cookie(&epit->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT);
247        ZF_LOGF_IF(epit_destroy(epit), CLEANUP_FAIL_TEXT);
248        return ENODEV;
249    }
250
251    /* Walk the interrupts and allocate the first */
252    error = ps_fdt_walk_irqs(&epit->io_ops.io_fdt, cookie, allocate_irq_callback, epit);
253    if (error) {
254        ZF_LOGF_IF(ps_fdt_cleanup_cookie(&epit->io_ops.malloc_ops, cookie), CLEANUP_FAIL_TEXT);
255        ZF_LOGF_IF(epit_destroy(epit), CLEANUP_FAIL_TEXT);
256        return ENODEV;
257    }
258
259    ZF_LOGF_IF(ps_fdt_cleanup_cookie(&epit->io_ops.malloc_ops, cookie),
260               "Failed to cleanup the FDT cookie after initialising the GPT");
261
262    /* Disable EPIT. */
263    epit->epit_map->epitcr = 0;
264
265    /* Interrupt when compare with 0. */
266    epit->epit_map->epitcmpr = 0;
267
268    return 0;
269}
270
271int epit_destroy(epit_t *epit)
272{
273    if (epit->epit_map) {
274        ZF_LOGF_IF(epit_stop(epit), "Failed to stop the GPT before de-allocating it");
275        ps_io_unmap(&epit->io_ops.io_mapper, (void *) epit->epit_map, (size_t) epit->timer_pmem.length);
276    }
277
278    if (epit->irq_id != PS_INVALID_IRQ_ID) {
279        ZF_LOGF_IF(ps_irq_unregister(&epit->io_ops.irq_ops, epit->irq_id), "Failed to unregister IRQ");
280    }
281
282    return 0;
283}
284