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->em2Run ? WDOG_CFG_EM2RUN : 0U)
222 | (init->em3Run ? WDOG_CFG_EM3RUN : 0U)
223 | (init->em4Block ? WDOG_CFG_EM4BLOCK : 0U)
224 | (init->resetDisable ? WDOG_CFG_WDOGRSTDIS : 0U)
225 | ((uint32_t)(init->warnSel) << _WDOG_CFG_WARNSEL_SHIFT)
226 | ((uint32_t)(init->winSel) << _WDOG_CFG_WINSEL_SHIFT)
227 | ((uint32_t)(init->perSel) << _WDOG_CFG_PERSEL_SHIFT);
228
229 WDOGn_Enable(wdog, init->enable);
230
231 if (init->lock) {
232 WDOGn_Lock(wdog);
233 }
234 #else
235 // Handle series-0 and series-1 devices
236 uint32_t setting;
237
238 setting = (init->enable ? WDOG_CTRL_EN : 0U)
239 | (init->debugRun ? WDOG_CTRL_DEBUGRUN : 0U)
240 | (init->em2Run ? WDOG_CTRL_EM2RUN : 0U)
241 | (init->em3Run ? WDOG_CTRL_EM3RUN : 0U)
242 | (init->em4Block ? WDOG_CTRL_EM4BLOCK : 0U)
243 | (init->swoscBlock ? WDOG_CTRL_SWOSCBLOCK : 0U)
244 | (init->lock ? WDOG_CTRL_LOCK : 0U)
245 | ((uint32_t)(init->clkSel) << _WDOG_CTRL_CLKSEL_SHIFT)
246 | ((uint32_t)(init->perSel) << _WDOG_CTRL_PERSEL_SHIFT);
247
248 #if defined(_WDOG_CTRL_WDOGRSTDIS_MASK)
249 setting |= (init->resetDisable ? WDOG_CTRL_WDOGRSTDIS : 0U);
250 #endif
251 #if defined(_WDOG_CTRL_WARNSEL_MASK)
252 setting |= ((uint32_t)(init->warnSel) << _WDOG_CTRL_WARNSEL_SHIFT);
253 #endif
254 #if defined(_WDOG_CTRL_WINSEL_MASK)
255 setting |= ((uint32_t)(init->winSel) << _WDOG_CTRL_WINSEL_SHIFT);
256 #endif
257
258 // Wait for previous operations/modifications to complete
259 int i = 0;
260 while (((wdog->SYNCBUSY & WDOG_SYNCBUSY_CTRL) != 0U)
261 && (i < WDOG_SYNC_TIMEOUT)) {
262 i++;
263 }
264 wdog->CTRL = setting;
265 #endif
266 }
267
268 /***************************************************************************//**
269 * @brief
270 * Lock the WDOG configuration.
271 *
272 * @details
273 * This prevents errors from overwriting the WDOG configuration, possibly
274 * disabling it. Only a reset can unlock the WDOG configuration once locked.
275 *
276 * If the LFRCO or LFXO clocks are used to clock WDOG,
277 * consider using the option of inhibiting those clocks to be disabled.
278 * See the WDOG_Enable() initialization structure.
279 *
280 * @note
281 * This function modifies the WDOG CTRL register which requires
282 * synchronization into the low-frequency domain. If this register is modified
283 * before a previous update to the same register has completed, this function
284 * will stall until the previous synchronization has completed.
285 *
286 * @param[in] wdog
287 * A pointer to WDOG peripheral register block.
288 ******************************************************************************/
WDOGn_Lock(WDOG_TypeDef * wdog)289 void WDOGn_Lock(WDOG_TypeDef *wdog)
290 {
291 #if defined(_WDOG_LOCK_MASK)
292 wdog->LOCK = _WDOG_LOCK_LOCKKEY_LOCK;
293 #else
294 // Wait for any pending previous write operation to have been completed in
295 // the low-frequency domain.
296 while ( (wdog->SYNCBUSY & WDOG_SYNCBUSY_CTRL) != 0U ) {
297 }
298
299 // Disable writing to the control register.
300 BUS_RegBitWrite(&wdog->CTRL, _WDOG_CTRL_LOCK_SHIFT, 1);
301 #endif
302 }
303
304 /***************************************************************************//**
305 * @brief
306 * Wait for the WDOG to complete all synchronization of register changes
307 * and commands.
308 *
309 * @param[in] wdog
310 * A pointer to WDOG peripheral register block.
311 ******************************************************************************/
WDOGn_SyncWait(WDOG_TypeDef * wdog)312 void WDOGn_SyncWait(WDOG_TypeDef *wdog)
313 {
314 #if defined(_SILICON_LABS_32B_SERIES_2)
315 while ((wdog->EN != 0U) && (wdog->SYNCBUSY != 0U)) {
316 // Wait for synchronization to finish
317 }
318 #else
319 while (wdog->SYNCBUSY != 0U) {
320 // Wait for synchronization to finish
321 }
322 #endif
323 }
324
325 /***************************************************************************//**
326 * @brief
327 * Unlock the WDOG configuration.
328 *
329 * @details
330 * Note that this function will have no effect on devices where a reset is
331 * the only way to unlock the watchdog.
332 *
333 * @param[in] wdog
334 * A pointer to WDOG peripheral register block.
335 ******************************************************************************/
WDOGn_Unlock(WDOG_TypeDef * wdog)336 void WDOGn_Unlock(WDOG_TypeDef *wdog)
337 {
338 #if defined(_WDOG_LOCK_MASK)
339 wdog->LOCK = _WDOG_LOCK_LOCKKEY_UNLOCK;
340 #else
341 (void) wdog;
342 #endif
343 }
344
345 /** @} (end addtogroup wdog) */
346 #endif /* defined(WDOG_COUNT) && (WDOG_COUNT > 0) */
347