Skip to content

Commit 3c622f6

Browse files
authored
Merge pull request #14743 from OpenNuvoton/nuvoton_m251_m261_wdt
Nuvoton: Support watchdog on M251/M261 series
2 parents 530d4be + de00a5a commit 3c622f6

File tree

5 files changed

+473
-1
lines changed

5 files changed

+473
-1
lines changed

targets/TARGET_NUVOTON/TARGET_M251/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ target_sources(mbed-m251
6060
sleep.c
6161
spi_api.c
6262
us_ticker.c
63+
watchdog_api.c
6364
)
6465

6566
target_include_directories(mbed-m251
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
/*
2+
* Copyright (c) 2019-2020 Nuvoton Technology Corporation
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include "watchdog_api.h"
19+
20+
#if DEVICE_WATCHDOG
21+
22+
#include "cmsis.h"
23+
24+
/* Watchdog clock per second */
25+
#if MBED_CONF_TARGET_LXT_PRESENT
26+
#define NU_WDTCLK_PER_SEC (__LXT)
27+
#define NU_WDTCLK_PER_SEC_MAX (__LXT)
28+
#define NU_WDTCLK_PER_SEC_MIN (__LXT)
29+
#else
30+
#define NU_WDTCLK_PER_SEC (__LIRC)
31+
#define NU_WDTCLK_PER_SEC_MAX ((uint32_t) ((__LIRC) * 1.15f))
32+
#define NU_WDTCLK_PER_SEC_MIN ((uint32_t) ((__LIRC) * 0.85f))
33+
#endif
34+
35+
/* Convert watchdog clock to nearest ms */
36+
#define NU_WDTCLK2MS(WDTCLK) (((WDTCLK) * 1000 + ((NU_WDTCLK_PER_SEC) / 2)) / (NU_WDTCLK_PER_SEC))
37+
38+
/* Convert ms to nearest watchdog clock */
39+
#define NU_MS2WDTCLK(MS) (((MS) * (NU_WDTCLK_PER_SEC) + 500) / 1000)
40+
41+
/* List of hardware-supported watchdog timeout in clocks */
42+
#define NU_WDT_16CLK 16
43+
#define NU_WDT_64CLK 64
44+
#define NU_WDT_256CLK 256
45+
#define NU_WDT_1024CLK 1024
46+
#define NU_WDT_4096CLK 4096
47+
#define NU_WDT_16384CLK 16384
48+
#define NU_WDT_65536CLK 65536
49+
#define NU_WDT_262144CLK 262144
50+
51+
/* Watchdog reset delay
52+
*
53+
* 1. Cannot be too small. This is to avoid premature WDT reset in pieces of timeout cascading.
54+
* 2. Cannot be too large. This is to pass Greentea reset_reason/watchdog_reset tests, which have e.g. 50~100 reset delay tolerance.
55+
*/
56+
#define NU_WDT_RESET_DELAY_RSTDSEL WDT_RESET_DELAY_130CLK
57+
58+
/* Support watchdog timeout values beyond H/W
59+
*
60+
* Watchdog Timer H/W just supports timeout values of 2^4, 2^6, ..., 2^18 clocks.
61+
* To extend the support range to 1 and UINT32_MAX, we cascade multiple small timeouts to
62+
* reach one large timeout specified in hal_watchdog_init.
63+
*/
64+
65+
/* Track if WDT H/W has been initialized */
66+
static bool wdt_hw_inited = 0;
67+
/* Hold initially-configured timeout in hal_watchdog_init */
68+
static uint32_t wdt_timeout_reload_ms = 0;
69+
/* Track remaining timeout for cascading */
70+
static uint32_t wdt_timeout_rmn_clk = 0;
71+
72+
static void watchdog_setup_cascade_timeout(void);
73+
static void WDT_IRQHandler(void);
74+
75+
watchdog_status_t hal_watchdog_init(const watchdog_config_t *config)
76+
{
77+
/* Check validity of arguments */
78+
if (! config || ! config->timeout_ms) {
79+
return WATCHDOG_STATUS_INVALID_ARGUMENT;
80+
}
81+
82+
wdt_timeout_reload_ms = config->timeout_ms;
83+
wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms);
84+
85+
if (! wdt_hw_inited) {
86+
wdt_hw_inited = 1;
87+
88+
SYS_UnlockReg();
89+
90+
/* Enable IP module clock */
91+
CLK_EnableModuleClock(WDT_MODULE);
92+
93+
/* Select IP clock source */
94+
#if MBED_CONF_TARGET_LXT_PRESENT
95+
CLK_SetModuleClock(WDT_MODULE, CLK_CLKSEL1_WDTSEL_LXT, 0);
96+
#else
97+
CLK_SetModuleClock(WDT_MODULE, CLK_CLKSEL1_WDTSEL_LIRC, 0);
98+
#endif
99+
100+
SYS_LockReg();
101+
102+
/* Set up IP interrupt */
103+
NVIC_SetVector(WDT_IRQn, (uint32_t) WDT_IRQHandler);
104+
NVIC_EnableIRQ(WDT_IRQn);
105+
}
106+
107+
watchdog_setup_cascade_timeout();
108+
109+
return WATCHDOG_STATUS_OK;
110+
}
111+
112+
void hal_watchdog_kick(void)
113+
{
114+
/* If a watchdog is not running, this function does nothing */
115+
if (!(WDT->CTL & WDT_CTL_WDTEN_Msk)) {
116+
return;
117+
}
118+
119+
wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms);
120+
watchdog_setup_cascade_timeout();
121+
}
122+
123+
watchdog_status_t hal_watchdog_stop(void)
124+
{
125+
SYS_UnlockReg();
126+
127+
/* Clear all flags & Disable interrupt & Disable WDT */
128+
WDT->CTL = (WDT->CTL & ~(WDT_CTL_WDTEN_Msk | WDT_CTL_INTEN_Msk)) | (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk);
129+
130+
SYS_LockReg();
131+
132+
return WATCHDOG_STATUS_OK;
133+
}
134+
135+
uint32_t hal_watchdog_get_reload_value(void)
136+
{
137+
return wdt_timeout_reload_ms;
138+
}
139+
140+
watchdog_features_t hal_watchdog_get_platform_features(void)
141+
{
142+
watchdog_features_t wdt_feat;
143+
144+
/* We can support timeout values between 1 and UINT32_MAX by cascading. */
145+
wdt_feat.max_timeout = UINT32_MAX;
146+
/* Support re-configuring watchdog timer */
147+
wdt_feat.update_config = 1;
148+
/* Support stopping watchdog timer */
149+
wdt_feat.disable_watchdog = 1;
150+
/* Typical frequency of not calibrated watchdog clock in Hz */
151+
wdt_feat.clock_typical_frequency = NU_WDTCLK_PER_SEC;
152+
/* Maximum frequency of not calibrated watchdog clock in Hz */
153+
wdt_feat.clock_max_frequency = NU_WDTCLK_PER_SEC_MAX;
154+
155+
return wdt_feat;
156+
}
157+
158+
static void watchdog_setup_cascade_timeout(void)
159+
{
160+
uint32_t wdt_timeout_clk_toutsel;
161+
162+
if (wdt_timeout_rmn_clk >= NU_WDT_262144CLK) {
163+
wdt_timeout_rmn_clk -= NU_WDT_262144CLK;
164+
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW18;
165+
} else if (wdt_timeout_rmn_clk >= NU_WDT_65536CLK) {
166+
wdt_timeout_rmn_clk -= NU_WDT_65536CLK;
167+
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW16;
168+
} else if (wdt_timeout_rmn_clk >= NU_WDT_16384CLK) {
169+
wdt_timeout_rmn_clk -= NU_WDT_16384CLK;
170+
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW14;
171+
} else if (wdt_timeout_rmn_clk >= NU_WDT_4096CLK) {
172+
wdt_timeout_rmn_clk -= NU_WDT_4096CLK;
173+
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW12;
174+
} else if (wdt_timeout_rmn_clk >= NU_WDT_1024CLK) {
175+
wdt_timeout_rmn_clk -= NU_WDT_1024CLK;
176+
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW10;
177+
} else if (wdt_timeout_rmn_clk >= NU_WDT_256CLK) {
178+
wdt_timeout_rmn_clk -= NU_WDT_256CLK;
179+
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW8;
180+
} else if (wdt_timeout_rmn_clk >= NU_WDT_64CLK) {
181+
wdt_timeout_rmn_clk -= NU_WDT_64CLK;
182+
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW6;
183+
} else if (wdt_timeout_rmn_clk >= NU_WDT_16CLK) {
184+
wdt_timeout_rmn_clk -= NU_WDT_16CLK;
185+
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4;
186+
} else if (wdt_timeout_rmn_clk) {
187+
wdt_timeout_rmn_clk = 0;
188+
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4;
189+
} else {
190+
/* WDT has timed-out and will restart system soon. We just disable interrupt to escape
191+
* getting stuck in WDT ISR. */
192+
SYS_UnlockReg();
193+
194+
/* Clear all flags & Disable interrupt */
195+
WDT->CTL = (WDT->CTL & ~WDT_CTL_INTEN_Msk) | (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk);
196+
197+
SYS_LockReg();
198+
return;
199+
}
200+
201+
SYS_UnlockReg();
202+
203+
/* Configure reset delay on timeout */
204+
WDT->ALTCTL = NU_WDT_RESET_DELAY_RSTDSEL;
205+
206+
/* Reset watchdog timer */
207+
WDT_RESET_COUNTER();
208+
209+
/* Configure another piece of cascaded WDT timeout */
210+
WDT->CTL = wdt_timeout_clk_toutsel | // Timeout interval
211+
WDT_CTL_WDTEN_Msk | // Enable watchdog timer
212+
WDT_CTL_INTEN_Msk | // Enable interrupt
213+
WDT_CTL_WKF_Msk | // Clear wake-up flag
214+
WDT_CTL_WKEN_Msk | // Enable wake-up on timeout
215+
WDT_CTL_IF_Msk | // Clear interrupt flag
216+
WDT_CTL_RSTF_Msk | // Clear reset flag
217+
WDT_CTL_RSTEN_Msk; // Enable reset always to address cascaded timeout failure in interrupt disabled scenario e.g. Hard Fault
218+
219+
SYS_LockReg();
220+
}
221+
222+
void WDT_IRQHandler(void)
223+
{
224+
/* Check WDT interrupt flag */
225+
if (WDT_GET_TIMEOUT_INT_FLAG()) {
226+
/* Continue another piece of cascaded WDT timeout */
227+
watchdog_setup_cascade_timeout();
228+
} else {
229+
/* Clear all flags */
230+
WDT->CTL |= (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk);
231+
}
232+
}
233+
234+
#endif

targets/TARGET_NUVOTON/TARGET_M261/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ target_sources(mbed-m261
6868
spi_api.c
6969
trng_api.cpp
7070
us_ticker.c
71+
watchdog_api.c
7172

7273
crypto/crypto-misc.cpp
7374
)

0 commit comments

Comments
 (0)