1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <config.h>
8#include <types.h>
9#include <machine/io.h>
10#include <kernel/vspace.h>
11#include <arch/machine.h>
12#include <arch/kernel/vspace.h>
13#include <plat/machine.h>
14#include <linker.h>
15#include <plat/machine/devices_gen.h>
16#include <plat/machine/hardware.h>
17
18#define TIOCP_CFG_SOFTRESET BIT(0)
19
20#define TIER_MATCH_ENABLE BIT(0)
21#define TIER_OVERFLOW_ENABLE BIT(1)
22#define TIER_COMPARE_ENABLE BIT(2)
23
24#define TCLR_STARTTIMER BIT(0)
25#define TCLR_AUTORELOAD BIT(1)
26#define TCLR_PRESCALE_ENABLE BIT(5)
27#define TCLR_COMPAREENABLE BIT(6)
28
29timer_t *timer = (timer_t *) TIMER_PPTR;
30
31#define WDT_REG(base, off) ((volatile uint32_t *)((base) + (off)))
32#define WDT_REG_WWPS 0x34
33#define WDT_REG_WSPR 0x48
34#define WDT_WWPS_PEND_WSPR BIT(4)
35
36#define SET_REGISTER(reg, mask) \
37        do {                    \
38            reg |= mask;        \
39        } while((reg & mask) != mask);
40
41static BOOT_CODE void disableWatchdog(void)
42{
43    uint32_t wdt = WDT1_PPTR;
44
45    // am335x ref man, sec 20.4.3.8
46    *WDT_REG(wdt, WDT_REG_WSPR) = 0xaaaa;
47    while ((*WDT_REG(wdt, WDT_REG_WWPS) & WDT_WWPS_PEND_WSPR)) {
48        continue;
49    }
50    *WDT_REG(wdt, WDT_REG_WSPR) = 0x5555;
51    while ((*WDT_REG(wdt, WDT_REG_WWPS) & WDT_WWPS_PEND_WSPR)) {
52        continue;
53    }
54}
55
56/*
57 * Enable DMTIMER clocks, otherwise their registers wont be accessible.
58 * This could be moved out of kernel.
59 */
60static BOOT_CODE void enableTimers(void)
61{
62    uint32_t cmper = CMPER_PPTR;
63
64    /* XXX repeat this for DMTIMER4..7 */
65    /* select clock - Timer 3 */
66    *CMPER_REG(cmper, CMPER_CLKSEL_TIMER3) = CMPER_CKLSEL_MOSC;
67    while ((*CMPER_REG(cmper, CMPER_CLKSEL_TIMER3) & RESERVED) != CMPER_CKLSEL_MOSC) {
68        continue;
69    }
70
71    /* enable clock */
72    *CMPER_REG(cmper, CMPER_TIMER3_CLKCTRL) = CMPER_CLKCTRL_ENABLE;
73    while ((*CMPER_REG(cmper, CMPER_TIMER3_CLKCTRL) & RESERVED) != CMPER_CLKCTRL_ENABLE) {
74        continue;
75    }
76
77    /* select clock - Timer 4 */
78    *CMPER_REG(cmper, CMPER_CLKSEL_TIMER4) = CMPER_CKLSEL_MOSC;
79    while ((*CMPER_REG(cmper, CMPER_CLKSEL_TIMER4) & RESERVED) != CMPER_CKLSEL_MOSC) {
80        continue;
81    }
82
83    /* enable clock */
84    *CMPER_REG(cmper, CMPER_TIMER4_CLKCTRL) = CMPER_CLKCTRL_ENABLE;
85    while ((*CMPER_REG(cmper, CMPER_TIMER4_CLKCTRL) & RESERVED) != CMPER_CLKCTRL_ENABLE) {
86        continue;
87    }
88}
89#ifdef CONFIG_KERNEL_MCS
90
91/* The idea here is to use the functionality of overflow interrupts to calculate time
92   and use match interrupts for setting a deadline. */
93
94uint32_t high_bits = 0;
95BOOT_CODE void initTimer(void)
96{
97    int timeout;
98    disableWatchdog();
99    enableTimers();
100
101    /* Configure dmtimer0 as kernel timer */
102    SET_REGISTER(timer->cfg, TIOCP_CFG_SOFTRESET);
103
104    /* disable */
105    SET_REGISTER(timer->tclr, 0u);
106
107    /* wait for reset */
108    for (timeout = 10000; (timer->cfg & TIOCP_CFG_SOFTRESET) && timeout > 0; timeout--);
109
110    if (!timeout) {
111        printf("init timer failed\n");
112        return;
113    }
114
115    maskInterrupt(/*disable*/ true, KERNEL_TIMER_IRQ);
116
117    /* Set the reload value */
118    SET_REGISTER(timer->tldr, 0u);
119
120    /* Enables interrupt on overflow and match */
121    SET_REGISTER(timer->tier, (TIER_OVERFLOW_ENABLE | TIER_MATCH_ENABLE));
122
123    /* Clear the read register */
124    SET_REGISTER(timer->tcrr, 0u);
125
126    /* start the timer */
127    SET_REGISTER(timer->tclr, (TCLR_AUTORELOAD | TCLR_STARTTIMER | TCLR_COMPAREENABLE));
128}
129
130#else /* CONFIG_KERNEL_MCS */
131
132
133/* Configure dmtimer0 as kernel preemption timer */
134BOOT_CODE void initTimer(void)
135{
136    int timeout;
137
138    disableWatchdog();
139    enableTimers();
140
141    timer->cfg = TIOCP_CFG_SOFTRESET;
142
143    for (timeout = 10000; (timer->cfg & TIOCP_CFG_SOFTRESET) && timeout > 0; timeout--)
144        ;
145    if (!timeout) {
146        printf("init timer failed\n");
147        return;
148    }
149
150    maskInterrupt(/*disable*/ true, KERNEL_TIMER_IRQ);
151
152    /* Set the reload value */
153    timer->tldr = 0xFFFFFFFFUL - TIMER_RELOAD;
154
155    /* Enables interrupt on overflow */
156    timer->tier = TIER_OVERFLOW_ENABLE;
157
158    /* Clear the read register */
159    timer->tcrr = 0xFFFFFFFFUL - TIMER_RELOAD;
160
161    /* Set autoreload and start the timer */
162    timer->tclr = TCLR_AUTORELOAD | TCLR_STARTTIMER;
163}
164#endif /* CONFIG_KERNEL_MCS */
165