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