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
29 timer_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
disableWatchdog(void)41 static 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 */
enableTimers(void)60 static 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
94 uint32_t high_bits = 0;
initTimer(void)95 BOOT_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 */
initTimer(void)134 BOOT_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