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 "../../arch/arm/clock.h"
19#include <platsupport/timer.h>
20#include <platsupport/plat/spt.h>
21
22/* The arm timer on the BCM2837 is based on a SP804 timer with some modifications
23
24    - Only one timer
25    - It only runs in continuous mode
26    - Can be set to either stop or continue in ARM debug halt mode
27    - Pre-scale options are only 1, 16 and 256
28    - CLK source is 250 MHz before prescale.
29    - Has an additional up counter register that will count up on each timer tick and
30        roll over without generating interrupts.  It has its own prescalar.
31
32    Functions by setting a value in the load register which the timer will move to the value
33    register and when the value counts down to zero, an interrupt will occur if interrupts are
34    enabled and the value from the load register will be copied back into the value register.
35
36 */
37
38enum {
39    /*
40        Width of the counter.
41            0: 16-bit counters
42            1: 32-bit counters
43    */
44        COUNTER_WIDTH_BIT = 1,
45    /*
46        The prescale bits select the prescale for the down counter.
47        Note: There are other pre-scale bits in this register (16:23) for
48                the separate free running up-counting counter.
49        00 : pre-scale is clock / 1 (No pre-scale)
50        01 : pre-scale is clock / 16
51        10 : pre-scale is clock / 256
52        11 : pre-scale is clock / 1
53    */
54        PRESCALE_BIT = 2,
55
56    /*
57        Interrupt enable bit.
58            0: Timer interrupt disabled
59            1: Timer interrupt enabled
60    */
61        TIMER_INTERRUPT_ENABLE = 5,
62
63    /*
64        Timer enable.
65            0: Disabled
66            1: Enabled
67    */
68        TIMER_ENABLE = 7,
69
70    /*
71        Timer behaviour when ARM is in debug halt mode:
72            0: Timers keep running
73            1: Timers halted.
74    */
75        ARM_DEBUG_HALT = 8,
76
77    /*
78        Whether the free-run counter is enabled.
79            0: Enabled
80            1: Disabled
81     */
82        FREE_RUN_ENABLE = 9,
83
84    /*
85        Free running scalar.  8 bits wide.  Reset value is 0x3E
86        Freq = clk/(prescale + 1)
87     */
88        FREE_RUN_PRESCALE = 16
89} control_reg;
90
91#define LOAD_WIDTH 32
92#define VALUE_WIDTH 32
93#define CTRL_WIDTH 24
94#define IRQ_CLEAR_WIDTH 32
95#define RAW_IRQ_WIDTH 1
96#define MASKED_IRQ_WIDTH 1
97#define PRE_DIVIDER_WIDTH 10
98
99#define TIMER_BASE_OFFSET 0x400
100
101int spt_start(spt_t *spt)
102{
103    if (spt == NULL) {
104        return EINVAL;
105    }
106    /* Enable timer */
107    spt->regs->ctrl = BIT(FREE_RUN_ENABLE) | BIT(COUNTER_WIDTH_BIT);
108    return 0;
109}
110
111int spt_stop(spt_t *spt)
112{
113    if (!spt) {
114        return EINVAL;
115    }
116    /* Disable timer */
117    spt->regs->ctrl = 0;
118    return 0;
119}
120
121/* Set up the timer to fire an interrupt every ns nanoseconds.
122 * The first such interrupt may arrive before ns nanoseconds
123 * have passed since calling. */
124int spt_set_timeout(spt_t *spt, uint64_t ns)
125{
126    if (!spt) {
127        return EINVAL;
128    }
129    uint64_t ticks = ns / (NS_IN_US / (spt->freq / MHZ));
130    uint32_t prescale_bits = 0;
131    spt->counter_start = ns;
132    if (ticks == 0) {
133        ZF_LOGE("ns too low: %llu\n", ns);
134        return EINVAL;
135    }
136
137    /* Prescale only has 3 values, so we need to calculate them here */
138    if (ticks >= (UINT32_MAX + 1)) {
139        ticks /= 16;
140        if (ticks >= (1ULL << 32)) {
141            ticks /= 16;
142            if (ticks >= (1ULL << 32)) {
143                ZF_LOGE("ns too high: %llu\n", ns);
144                return EINVAL;
145            } else {
146                prescale_bits = 2;
147            }
148        } else {
149            prescale_bits = 1;
150        }
151    }
152
153    /* Configure timer */
154    spt->regs->load = ticks;
155    spt->regs->pre_divider = 0;
156    spt->regs->irq_clear = 1;
157    spt->regs->ctrl = BIT(COUNTER_WIDTH_BIT) | (prescale_bits << PRESCALE_BIT) |
158                        BIT(TIMER_INTERRUPT_ENABLE) | BIT(FREE_RUN_ENABLE) | BIT(TIMER_ENABLE);
159
160    return 0;
161}
162
163int spt_handle_irq(spt_t *spt)
164{
165    if (!spt) {
166        return EINVAL;
167    }
168    if (spt->regs->masked_irq) {
169        spt->regs->irq_clear = 1;
170        spt->regs->ctrl &= ~(BIT(TIMER_ENABLE));
171    } else {
172        ZF_LOGW("handle irq called when no interrupt pending\n");
173        return EINVAL;
174    }
175    return 0;
176}
177
178uint64_t spt_get_time(spt_t *spt)
179{
180    uint64_t value = spt->regs->free_run_count;
181    /* convert raw count to ns. As we never change the free run prescaler we do not need to
182     * scale by it. We multiplly by mhz as the free run count is a 32-bit value so we will
183     * not overflow a 64-bit value at that point */
184    uint64_t ns = (value * MHZ / spt->freq) * NS_IN_US;
185    return ns;
186}
187
188int spt_init(spt_t *spt, spt_config_t config)
189{
190    clk_t *clk;
191
192    if (!spt || !config.vaddr) {
193        return EINVAL;
194    }
195
196    /* Save the mmio address */
197    spt->regs = (void *) ((uintptr_t) config.vaddr) + TIMER_BASE_OFFSET;
198    clock_sys_t clk_sys;
199    clock_sys_init_default(&clk_sys);
200    clk = clk_get_clock(&clk_sys, CLK_SP804);
201    spt->freq = clk_get_freq(clk);
202
203    /* handle any pending irqs */
204    spt_handle_irq(spt);
205    return 0;
206}
207