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#include <errno.h>
13#include <stdlib.h>
14#include <utils/util.h>
15#include "../../arch/arm/clock.h"
16#include <platsupport/timer.h>
17#include <platsupport/plat/system_timer.h>
18
19/*
20 * The system timer on the BCM283[5-7] is fairly simple in its nature.
21 * It has a 64-bit free-running counter and 4 compare registers. When
22 * the lower 32 bits of the free-running counter match one of the
23 * compare registers, an associated IRQ is generated and an associated
24 * control bit is set in the control register.
25 */
26
27int system_timer_init(system_timer_t *timer, system_timer_config_t config) {
28    if (timer == NULL || config.vaddr == NULL) {
29        return EINVAL;
30    }
31
32    timer->regs = config.vaddr;
33
34    return 0;
35}
36
37uint64_t system_timer_get_time(system_timer_t *timer) {
38    if (timer == NULL) {
39        return EINVAL;
40    }
41
42    uint64_t initial_high = timer->regs->counter_high;
43    uint64_t low = timer->regs->counter_low;
44    uint64_t high = timer->regs->counter_high;
45    if (high != initial_high) {
46        /* get low again if high has ticked over. */
47        low = timer->regs->counter_low;
48    }
49
50    uint64_t ticks = (high << 32) | low;
51    uint64_t time = ticks * SYSTEM_TIMER_NS_PER_TICK;
52
53    return time;
54}
55
56int system_timer_set_timeout(system_timer_t *timer, uint64_t ns) {
57    if (timer == NULL) {
58        return EINVAL;
59    }
60
61    /* Can only set a timeout within the next 2^32 microseconds. */
62    uint64_t time = system_timer_get_time(timer);
63    uint64_t ticks = time / SYSTEM_TIMER_NS_PER_TICK;
64    uint64_t timeout_ticks = ns / SYSTEM_TIMER_NS_PER_TICK;
65    if (timeout_ticks < ticks) {
66        ZF_LOGE("Timeout in the past\n");
67        return ETIME;
68    } else if ((timeout_ticks - ticks) > UINT32_MAX) {
69        ZF_LOGE("Timeout too far in the future\n");
70        return ETIME;
71    }
72
73    /* Clear any existing interrupt. */
74    timer->regs->ctrl = BIT(SYSTEM_TIMER_MATCH);
75
76    uint32_t timeout = timeout_ticks & MASK(32);
77    timer->regs->compare[SYSTEM_TIMER_MATCH] = timeout;
78
79    time = system_timer_get_time(timer);
80    if (time >= ns && !(timer->regs->ctrl & BIT(SYSTEM_TIMER_MATCH))) {
81        timer->regs->ctrl = BIT(SYSTEM_TIMER_MATCH);
82        ZF_LOGE("Timeout missed\n");
83        return ETIME;
84    }
85
86    return 0;
87}
88
89int system_timer_handle_irq(system_timer_t *timer) {
90    if (timer == NULL) {
91        return EINVAL;
92    }
93
94    timer->regs->ctrl = BIT(SYSTEM_TIMER_MATCH);
95
96    return 0;
97}
98
99int system_timer_reset(system_timer_t *timer) {
100    if (timer == NULL) {
101        return EINVAL;
102    }
103
104    /* Just clear the one timer that is used. */
105    timer->regs->ctrl = BIT(SYSTEM_TIMER_MATCH);
106
107    return 0;
108}
109