1 /***************************************************************************//**
2 * @file
3 * @brief Watchdog (WDOG) peripheral API
4 *******************************************************************************
5 * # License
6 * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
7 *******************************************************************************
8 *
9 * SPDX-License-Identifier: Zlib
10 *
11 * The licensor of this software is Silicon Laboratories Inc.
12 *
13 * This software is provided 'as-is', without any express or implied
14 * warranty. In no event will the authors be held liable for any damages
15 * arising from the use of this software.
16 *
17 * Permission is granted to anyone to use this software for any purpose,
18 * including commercial applications, and to alter it and redistribute it
19 * freely, subject to the following restrictions:
20 *
21 * 1. The origin of this software must not be misrepresented; you must not
22 * claim that you wrote the original software. If you use this software
23 * in a product, an acknowledgment in the product documentation would be
24 * appreciated but is not required.
25 * 2. Altered source versions must be plainly marked as such, and must not be
26 * misrepresented as being the original software.
27 * 3. This notice may not be removed or altered from any source distribution.
28 *
29 ******************************************************************************/
30
31 #include "em_wdog.h"
32 #if defined(WDOG_COUNT) && (WDOG_COUNT > 0)
33
34 #include "em_bus.h"
35 #include "em_core.h"
36
37 /***************************************************************************//**
38 * @addtogroup wdog WDOG - Watchdog
39 * @brief Watchdog (WDOG) Peripheral API
40 * @details
41 * This module contains functions to control the WDOG peripheral of Silicon
42 * Labs 32-bit MCUs and SoCs. The WDOG resets the system in case of a fault
43 * condition.
44 * @{
45 ******************************************************************************/
46
47 /** In some scenarioes when the watchdog is disabled the synchronization
48 * register might be set and not be cleared until the watchdog is enabled
49 * again. This will happen when for instance some watchdog register is modified
50 * while the watchdog clock is disabled. In these scenarioes we need to make
51 * sure that the software does not wait forever. */
52 #define WDOG_SYNC_TIMEOUT 30000
53
54 /*******************************************************************************
55 ************************** GLOBAL FUNCTIONS *******************************
56 ******************************************************************************/
57
58 /***************************************************************************//**
59 * @brief
60 * Enable/disable the watchdog timer.
61 *
62 * @note
63 * This function modifies the WDOG CTRL register which requires
64 * synchronization into the low-frequency domain. If this register is modified
65 * before a previous update to the same register has completed, this function
66 * will stall until the previous synchronization has completed.
67 *
68 * @param[in] wdog
69 * A pointer to the WDOG peripheral register block.
70 *
71 * @param[in] enable
72 * True to enable Watchdog, false to disable. Watchdog cannot be disabled if
73 * it's been locked.
74 ******************************************************************************/
WDOGn_Enable(WDOG_TypeDef * wdog,bool enable)75 void WDOGn_Enable(WDOG_TypeDef *wdog, bool enable)
76 {
77 // SYNCBUSY may stall when locked.
78 #if defined(_WDOG_STATUS_MASK)
79 if ((wdog->STATUS & _WDOG_STATUS_LOCK_MASK) == WDOG_STATUS_LOCK_LOCKED) {
80 return;
81 }
82 #else
83 if (wdog->CTRL & WDOG_CTRL_LOCK) {
84 return;
85 }
86 #endif
87
88 #if defined(_WDOG_EN_MASK)
89 if (!enable) {
90 while (wdog->SYNCBUSY & WDOG_SYNCBUSY_CMD) {
91 }
92 wdog->EN_CLR = WDOG_EN_EN;
93 #if defined(_WDOG_EN_DISABLING_MASK)
94 while (wdog->EN & _WDOG_EN_DISABLING_MASK) {
95 }
96 #endif
97 } else {
98 wdog->EN_SET = WDOG_EN_EN;
99 }
100 #else
101 // Wait for previous operations/modifications to complete
102 int i = 0;
103 while (((wdog->SYNCBUSY & WDOG_SYNCBUSY_CTRL) != 0U)
104 && (i < WDOG_SYNC_TIMEOUT)) {
105 i++;
106 }
107
108 bool wdogState = ((wdog->CTRL & _WDOG_CTRL_EN_MASK) != 0U);
109
110 // Make sure to only write to the CTRL register if we are changing mode
111 if (wdogState != enable) {
112 BUS_RegBitWrite(&wdog->CTRL, _WDOG_CTRL_EN_SHIFT, enable);
113 }
114 #endif
115 }
116
117 /***************************************************************************//**
118 * @brief
119 * Feed WDOG.
120 *
121 * @details
122 * When WDOG is activated, it must be fed (i.e., clearing the counter)
123 * before it reaches the defined timeout period. Otherwise, WDOG
124 * will generate a reset.
125 *
126 * @note
127 * Note that WDOG is an asynchronous peripheral and when calling the
128 * WDOGn_Feed() function the hardware starts the process of clearing the
129 * counter. This process takes some time before it completes depending on the
130 * selected oscillator (up to 4 peripheral clock cycles). When using the
131 * ULFRCO for instance as the oscillator the watchdog runs on a 1 kHz clock
132 * and a watchdog clear operation might take up to 4 ms.
133 *
134 * If the device enters EM2 or EM3 while a command is in progress then that
135 * command will be aborted. An application can use @ref WDOGn_SyncWait()
136 * to wait for a command to complete.
137 *
138 * @param[in] wdog
139 * A pointer to the WDOG peripheral register block.
140 ******************************************************************************/
WDOGn_Feed(WDOG_TypeDef * wdog)141 void WDOGn_Feed(WDOG_TypeDef *wdog)
142 {
143 #if (_SILICON_LABS_32B_SERIES < 2)
144
145 // WDOG should not be fed while it is disabled.
146 if (!(wdog->CTRL & WDOG_CTRL_EN)) {
147 return;
148 }
149
150 // If a previous clearing is synchronized to the LF domain, there
151 // is no point in waiting for it to complete before clearing over again.
152 // This avoids stalling the core in the typical use case where some idle loop
153 // keeps clearing WDOG.
154 if (wdog->SYNCBUSY & WDOG_SYNCBUSY_CMD) {
155 return;
156 }
157 // Before writing to the WDOG_CMD register, make sure that
158 // any previous write to the WDOG_CTRL is complete.
159 while ( (wdog->SYNCBUSY & WDOG_SYNCBUSY_CTRL) != 0U ) {
160 }
161
162 wdog->CMD = WDOG_CMD_CLEAR;
163
164 #else // Series 2 devices
165
166 CORE_DECLARE_IRQ_STATE;
167
168 // WDOG should not be fed while it is disabled.
169 if ((wdog->EN & WDOG_EN_EN) == 0U) {
170 return;
171 }
172
173 // We need an atomic section around the check for sync and the clear command
174 // because sending a clear command while a previous command is being synchronized
175 // will cause a BusFault.
176 CORE_ENTER_ATOMIC();
177 if ((wdog->SYNCBUSY & WDOG_SYNCBUSY_CMD) == 0U) {
178 wdog->CMD = WDOG_CMD_CLEAR;
179 }
180 CORE_EXIT_ATOMIC();
181
182 #endif
183 }
184
185 /***************************************************************************//**
186 * @brief
187 * Initialize WDOG (assuming the WDOG configuration has not been
188 * locked).
189 *
190 * @note
191 * This function modifies the WDOG CTRL register which requires
192 * synchronization into the low-frequency domain. If this register is modified
193 * before a previous update to the same register has completed, this function
194 * will stall until the previous synchronization has completed.
195 *
196 * @param[in] wdog
197 * Pointer to the WDOG peripheral register block.
198 *
199 * @param[in] init
200 * The structure holding the WDOG configuration. A default setting
201 * #WDOG_INIT_DEFAULT is available for initialization.
202 ******************************************************************************/
WDOGn_Init(WDOG_TypeDef * wdog,const WDOG_Init_TypeDef * init)203 void WDOGn_Init(WDOG_TypeDef *wdog, const WDOG_Init_TypeDef *init)
204 {
205 #if defined(_WDOG_CFG_MASK)
206 // Handle series-2 devices
207
208 if (wdog->EN != 0U) {
209 while (wdog->SYNCBUSY != 0U) {
210 // Wait for any potential synchronization to finish
211 }
212 wdog->EN_CLR = WDOG_EN_EN;
213 #if defined(_WDOG_EN_DISABLING_MASK)
214 while (wdog->EN & _WDOG_EN_DISABLING_MASK) {
215 /* Wait for disabling to finish */
216 }
217 #endif
218 }
219
220 wdog->CFG = (init->debugRun ? WDOG_CFG_DEBUGRUN : 0U)
221 | (init->clrSrc ? WDOG_CFG_CLRSRC : 0U)
222 #if defined(_WDOG_CFG_EM1RUN_MASK)
223 | (init->em1Run ? WDOG_CFG_EM1RUN : 0U)
224 #endif
225 | (init->em2Run ? WDOG_CFG_EM2RUN : 0U)
226 | (init->em3Run ? WDOG_CFG_EM3RUN : 0U)
227 | (init->em4Block ? WDOG_CFG_EM4BLOCK : 0U)
228 | (init->prs0MissRstEn ? WDOG_CFG_PRS0MISSRSTEN : 0U)
229 | (init->prs1MissRstEn ? WDOG_CFG_PRS1MISSRSTEN : 0U)
230 | (init->resetDisable ? WDOG_CFG_WDOGRSTDIS : 0U)
231 | ((uint32_t)(init->warnSel) << _WDOG_CFG_WARNSEL_SHIFT)
232 | ((uint32_t)(init->winSel) << _WDOG_CFG_WINSEL_SHIFT)
233 | ((uint32_t)(init->perSel) << _WDOG_CFG_PERSEL_SHIFT);
234
235 WDOGn_Enable(wdog, init->enable);
236
237 if (init->lock) {
238 WDOGn_Lock(wdog);
239 }
240 #else
241 // Handle series-0 and series-1 devices
242 uint32_t setting;
243
244 setting = (init->enable ? WDOG_CTRL_EN : 0U)
245 | (init->debugRun ? WDOG_CTRL_DEBUGRUN : 0U)
246 #if defined(_WDOG_CTRL_CLRSRC_MASK)
247 | (init->clrSrc ? WDOG_CTRL_CLRSRC : 0U)
248 #endif
249 | (init->em2Run ? WDOG_CTRL_EM2RUN : 0U)
250 | (init->em3Run ? WDOG_CTRL_EM3RUN : 0U)
251 | (init->em4Block ? WDOG_CTRL_EM4BLOCK : 0U)
252 | (init->swoscBlock ? WDOG_CTRL_SWOSCBLOCK : 0U)
253 | (init->lock ? WDOG_CTRL_LOCK : 0U)
254 | ((uint32_t)(init->clkSel) << _WDOG_CTRL_CLKSEL_SHIFT)
255 | ((uint32_t)(init->perSel) << _WDOG_CTRL_PERSEL_SHIFT);
256
257 #if defined(_WDOG_CTRL_WDOGRSTDIS_MASK)
258 setting |= (init->resetDisable ? WDOG_CTRL_WDOGRSTDIS : 0U);
259 #endif
260 #if defined(_WDOG_CTRL_WARNSEL_MASK)
261 setting |= ((uint32_t)(init->warnSel) << _WDOG_CTRL_WARNSEL_SHIFT);
262 #endif
263 #if defined(_WDOG_CTRL_WINSEL_MASK)
264 setting |= ((uint32_t)(init->winSel) << _WDOG_CTRL_WINSEL_SHIFT);
265 #endif
266
267 // Wait for previous operations/modifications to complete
268 int i = 0;
269 while (((wdog->SYNCBUSY & WDOG_SYNCBUSY_CTRL) != 0U)
270 && (i < WDOG_SYNC_TIMEOUT)) {
271 i++;
272 }
273 wdog->CTRL = setting;
274 #endif
275 }
276
277 /***************************************************************************//**
278 * @brief
279 * Lock the WDOG configuration.
280 *
281 * @details
282 * This prevents errors from overwriting the WDOG configuration, possibly
283 * disabling it. Only a reset can unlock the WDOG configuration once locked.
284 *
285 * If the LFRCO or LFXO clocks are used to clock WDOG,
286 * consider using the option of inhibiting those clocks to be disabled.
287 * See the WDOG_Enable() initialization structure.
288 *
289 * @note
290 * This function modifies the WDOG CTRL register which requires
291 * synchronization into the low-frequency domain. If this register is modified
292 * before a previous update to the same register has completed, this function
293 * will stall until the previous synchronization has completed.
294 *
295 * @param[in] wdog
296 * A pointer to WDOG peripheral register block.
297 ******************************************************************************/
WDOGn_Lock(WDOG_TypeDef * wdog)298 void WDOGn_Lock(WDOG_TypeDef *wdog)
299 {
300 #if defined(_WDOG_LOCK_MASK)
301 wdog->LOCK = _WDOG_LOCK_LOCKKEY_LOCK;
302 #else
303 // Wait for any pending previous write operation to have been completed in
304 // the low-frequency domain.
305 while ( (wdog->SYNCBUSY & WDOG_SYNCBUSY_CTRL) != 0U ) {
306 }
307
308 // Disable writing to the control register.
309 BUS_RegBitWrite(&wdog->CTRL, _WDOG_CTRL_LOCK_SHIFT, 1);
310 #endif
311 }
312
313 /***************************************************************************//**
314 * @brief
315 * Wait for the WDOG to complete all synchronization of register changes
316 * and commands.
317 *
318 * @param[in] wdog
319 * A pointer to WDOG peripheral register block.
320 ******************************************************************************/
WDOGn_SyncWait(WDOG_TypeDef * wdog)321 void WDOGn_SyncWait(WDOG_TypeDef *wdog)
322 {
323 #if defined(_SILICON_LABS_32B_SERIES_2)
324 while ((wdog->EN != 0U) && (wdog->SYNCBUSY != 0U)) {
325 // Wait for synchronization to finish
326 }
327 #else
328 while (wdog->SYNCBUSY != 0U) {
329 // Wait for synchronization to finish
330 }
331 #endif
332 }
333
334 /***************************************************************************//**
335 * @brief
336 * Unlock the WDOG configuration.
337 *
338 * @details
339 * Note that this function will have no effect on devices where a reset is
340 * the only way to unlock the watchdog.
341 *
342 * @param[in] wdog
343 * A pointer to WDOG peripheral register block.
344 ******************************************************************************/
WDOGn_Unlock(WDOG_TypeDef * wdog)345 void WDOGn_Unlock(WDOG_TypeDef *wdog)
346 {
347 #if defined(_WDOG_LOCK_MASK)
348 wdog->LOCK = _WDOG_LOCK_LOCKKEY_UNLOCK;
349 #else
350 (void) wdog;
351 #endif
352 }
353
354 /** @} (end addtogroup wdog) */
355 #endif /* defined(WDOG_COUNT) && (WDOG_COUNT > 0) */
356