1 /*
2 * Copyright (c) 2021 Bestechnic (Shanghai) Co., Ltd. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "plat_types.h"
16 #include "plat_addr_map.h"
17 #include "cmsis.h"
18 #include "cmsis_nvic.h"
19 #include "hal_timer.h"
20 #include "hal_trace.h"
21 #include "hal_uart.h"
22 #include "hal_wdt.h"
23
24 #if defined(CHIP_BEST3001) || defined(CHIP_BEST3003) || defined(CHIP_BEST3005) || defined(CHIP_BEST1400) || defined(CHIP_BEST1402)
25 #define CLOCK_SYNC_WORKAROUND
26 #endif
27
28 #define SLOW_TIMER_VAL_DELTA 1
29 #define FAST_TIMER_VAL_DELTA 20
30
31 /* wdt controller */
32 /* reg address */
33 /* default timeout in seconds */
34 #define DEFAULT_TIMEOUT 60
35
36 #define WDT_RATE CONFIG_SYSTICK_HZ
37
38 /* watchdog register offsets and masks */
39 #define WDTLOAD_REG_OFFSET 0x000
40 #define WDTLOAD_LOAD_MIN 0x00000001
41 #define WDTLOAD_LOAD_MAX 0xFFFFFFFF
42
43 #define WDTVALUE_REG_OFFSET 0x004
44
45 #define WDTCONTROL_REG_OFFSET 0x008
46 #define WDTCONTROL_REG_INT_ENABLE (1 << 0)
47 #define WDTCONTROL_REG_RESET_ENABLE (1 << 1)
48
49 #define WDTINTCLR_REG_OFFSET 0x00C
50
51 #define WDTRIS_REG_OFFSET 0x010
52 #define WDTRIS_REG_INT_MASK (1 << 0)
53
54 #define WDTMIS_REG_OFFSET 0x014
55 #define WDTMIS_REG_INT_MASK (1 << 0)
56
57 #define WDTLOCK_REG_OFFSET 0xC00
58 #define WDTLOCK_REG_UNLOCK 0x1ACCE551
59 #define WDTLOCK_REG_LOCK 0x00000001
60
61 /* read write */
62 #define wdtip_write32(v,b,a) \
63 (*((volatile unsigned int *)(b+a)) = v)
64 #define wdtip_read32(b,a) \
65 (*((volatile unsigned int *)(b+a)))
66
67 #if 1
68 typedef void (*HAL_WDT_IRQ_HANDLER)(void);
69
70 struct HAL_WDT_CTX {
71 unsigned int load_val;
72 };
73
74 struct HAL_WDT_CTX hal_wdt_ctx[HAL_WDT_ID_NUM];
75 static void _wdt1_irq_handler(void);
76 HAL_WDT_IRQ_HANDLER hal_wdt_irq_handler[HAL_WDT_ID_NUM] =
77 {
78 _wdt1_irq_handler,
79 };
80 HAL_WDT_IRQ_CALLBACK hal_wdt_irq_callback[HAL_WDT_ID_NUM];
81
_wdt1_irq_handler(void)82 static void _wdt1_irq_handler(void)
83 {
84 if (hal_wdt_irq_callback[HAL_WDT_ID_0] != 0) {
85 hal_wdt_irq_callback[HAL_WDT_ID_0](HAL_WDT_ID_0, HAL_WDT_EVENT_FIRE);
86 }
87 }
88
_wdt_get_base(enum HAL_WDT_ID_T id)89 static unsigned int _wdt_get_base(enum HAL_WDT_ID_T id)
90 {
91 switch(id) {
92 default:
93 case HAL_WDT_ID_0:
94 return WDT_BASE;
95 break;
96 }
97
98 return 0;
99 }
100
_wdt_load(enum HAL_WDT_ID_T id)101 static void _wdt_load(enum HAL_WDT_ID_T id)
102 {
103 unsigned int reg_base = 0;
104 struct HAL_WDT_CTX *wdt = &hal_wdt_ctx[id];
105 uint32_t lock;
106
107 reg_base = _wdt_get_base(id);
108
109 lock = int_lock();
110
111 wdtip_write32(WDTLOCK_REG_UNLOCK, reg_base, WDTLOCK_REG_OFFSET);
112
113 #ifdef CLOCK_SYNC_WORKAROUND
114 uint32_t val;
115
116 do {
117 wdtip_write32(wdt->load_val, reg_base, WDTLOAD_REG_OFFSET);
118 val = wdtip_read32(reg_base, WDTVALUE_REG_OFFSET);
119 } while ((wdt->load_val < val) || (wdt->load_val > val + SLOW_TIMER_VAL_DELTA));
120 #else
121 wdtip_write32(wdt->load_val, reg_base, WDTLOAD_REG_OFFSET);
122 #endif
123
124 wdtip_write32(WDTLOCK_REG_LOCK, reg_base, WDTLOCK_REG_OFFSET);
125
126 int_unlock(lock);
127
128 /* Flush posted writes. */
129 wdtip_read32(reg_base, WDTLOCK_REG_OFFSET);
130 }
131
_wdt_config(enum HAL_WDT_ID_T id)132 static void _wdt_config(enum HAL_WDT_ID_T id)
133 {
134 unsigned int reg_base = 0;
135 uint32_t lock;
136
137 reg_base = _wdt_get_base(id);
138
139 lock = int_lock();
140 wdtip_write32(WDTLOCK_REG_UNLOCK, reg_base, WDTLOCK_REG_OFFSET);
141 wdtip_write32(WDTRIS_REG_INT_MASK, reg_base, WDTINTCLR_REG_OFFSET);
142 wdtip_write32(WDTCONTROL_REG_INT_ENABLE | WDTCONTROL_REG_RESET_ENABLE, reg_base, WDTCONTROL_REG_OFFSET);
143 wdtip_write32(WDTLOCK_REG_LOCK, reg_base, WDTLOCK_REG_OFFSET);
144 int_unlock(lock);
145
146 /* Flush posted writes. */
147 wdtip_read32(reg_base, WDTLOCK_REG_OFFSET);
148 }
149
150 static int wdt_start;
151
152 /* mandatory operations */
hal_wdt_start(enum HAL_WDT_ID_T id)153 int hal_wdt_start(enum HAL_WDT_ID_T id)
154 {
155 _wdt_load(id);
156 _wdt_config(id);
157 wdt_start = 1;
158 return 0;
159 }
hal_wdt_stop(enum HAL_WDT_ID_T id)160 int hal_wdt_stop(enum HAL_WDT_ID_T id)
161 {
162 unsigned int reg_base = 0;
163 uint32_t lock;
164
165 reg_base = _wdt_get_base(id);
166
167 lock = int_lock();
168 wdtip_write32(WDTLOCK_REG_UNLOCK, reg_base, WDTLOCK_REG_OFFSET);
169 wdtip_write32(0, reg_base, WDTCONTROL_REG_OFFSET);
170 wdtip_write32(WDTLOCK_REG_LOCK, reg_base, WDTLOCK_REG_OFFSET);
171 int_unlock(lock);
172
173 /* Flush posted writes. */
174 wdtip_read32(reg_base, WDTLOCK_REG_OFFSET);
175
176 wdt_start = 0;
177 return 0;
178 }
179
180 /* optional operations */
hal_wdt_ping(enum HAL_WDT_ID_T id)181 int hal_wdt_ping(enum HAL_WDT_ID_T id)
182 {
183 if (wdt_start) {
184 _wdt_load(id);
185 }
186
187 return 0;
188 }
hal_wdt_set_timeout(enum HAL_WDT_ID_T id,unsigned int timeout)189 int hal_wdt_set_timeout(enum HAL_WDT_ID_T id, unsigned int timeout)
190 {
191 uint64_t load;
192 struct HAL_WDT_CTX *wdt = &hal_wdt_ctx[id];
193
194 /*
195 * sp805 runs counter with given value twice, after the end of first
196 * counter it gives an interrupt and then starts counter again. If
197 * interrupt already occurred then it resets the system. This is why
198 * load is half of what should be required.
199 */
200 load = WDT_RATE * timeout - 1;
201
202 load = (load > WDTLOAD_LOAD_MAX) ? WDTLOAD_LOAD_MAX: load;
203 load = (load < WDTLOAD_LOAD_MIN) ? WDTLOAD_LOAD_MIN: load;
204
205 wdt->load_val = load;
206
207 return 0;
208 }
hal_wdt_get_timeleft(enum HAL_WDT_ID_T id)209 unsigned int hal_wdt_get_timeleft(enum HAL_WDT_ID_T id)
210 {
211 uint64_t load;
212 unsigned int reg_base = 0;
213 struct HAL_WDT_CTX *wdt = &hal_wdt_ctx[id];
214
215 reg_base = _wdt_get_base(id);
216
217 load = wdtip_read32(reg_base, WDTVALUE_REG_OFFSET);
218
219 /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */
220 if (!(wdtip_read32(reg_base, WDTRIS_REG_OFFSET) & WDTRIS_REG_INT_MASK))
221 load += wdt->load_val + 1;
222
223 return load/WDT_RATE;
224 }
225
hal_wdt_set_irq_callback(enum HAL_WDT_ID_T id,HAL_WDT_IRQ_CALLBACK cb)226 void hal_wdt_set_irq_callback(enum HAL_WDT_ID_T id, HAL_WDT_IRQ_CALLBACK cb)
227 {
228 switch(id) {
229 default:
230 case HAL_WDT_ID_0:
231 NVIC_SetVector(WDT_IRQn, (uint32_t)hal_wdt_irq_handler[id]);
232 NVIC_SetPriority(WDT_IRQn, IRQ_PRIORITY_REALTIME);
233 NVIC_ClearPendingIRQ(WDT_IRQn);
234 NVIC_EnableIRQ(WDT_IRQn);
235 break;
236 }
237
238 hal_wdt_irq_callback[id] = cb;
239 }
240
241 #endif
242