Skip to content

Commit

Permalink
Merge pull request #14743 from OpenNuvoton/nuvoton_m251_m261_wdt
Browse files Browse the repository at this point in the history
Nuvoton: Support watchdog on M251/M261 series
  • Loading branch information
0xc0170 authored Jun 11, 2021
2 parents 530d4be + de00a5a commit 3c622f6
Show file tree
Hide file tree
Showing 5 changed files with 473 additions and 1 deletion.
1 change: 1 addition & 0 deletions targets/TARGET_NUVOTON/TARGET_M251/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ target_sources(mbed-m251
sleep.c
spi_api.c
us_ticker.c
watchdog_api.c
)

target_include_directories(mbed-m251
Expand Down
234 changes: 234 additions & 0 deletions targets/TARGET_NUVOTON/TARGET_M251/watchdog_api.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
/*
* Copyright (c) 2019-2020 Nuvoton Technology Corporation
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "watchdog_api.h"

#if DEVICE_WATCHDOG

#include "cmsis.h"

/* Watchdog clock per second */
#if MBED_CONF_TARGET_LXT_PRESENT
#define NU_WDTCLK_PER_SEC (__LXT)
#define NU_WDTCLK_PER_SEC_MAX (__LXT)
#define NU_WDTCLK_PER_SEC_MIN (__LXT)
#else
#define NU_WDTCLK_PER_SEC (__LIRC)
#define NU_WDTCLK_PER_SEC_MAX ((uint32_t) ((__LIRC) * 1.15f))
#define NU_WDTCLK_PER_SEC_MIN ((uint32_t) ((__LIRC) * 0.85f))
#endif

/* Convert watchdog clock to nearest ms */
#define NU_WDTCLK2MS(WDTCLK) (((WDTCLK) * 1000 + ((NU_WDTCLK_PER_SEC) / 2)) / (NU_WDTCLK_PER_SEC))

/* Convert ms to nearest watchdog clock */
#define NU_MS2WDTCLK(MS) (((MS) * (NU_WDTCLK_PER_SEC) + 500) / 1000)

/* List of hardware-supported watchdog timeout in clocks */
#define NU_WDT_16CLK 16
#define NU_WDT_64CLK 64
#define NU_WDT_256CLK 256
#define NU_WDT_1024CLK 1024
#define NU_WDT_4096CLK 4096
#define NU_WDT_16384CLK 16384
#define NU_WDT_65536CLK 65536
#define NU_WDT_262144CLK 262144

/* Watchdog reset delay
*
* 1. Cannot be too small. This is to avoid premature WDT reset in pieces of timeout cascading.
* 2. Cannot be too large. This is to pass Greentea reset_reason/watchdog_reset tests, which have e.g. 50~100 reset delay tolerance.
*/
#define NU_WDT_RESET_DELAY_RSTDSEL WDT_RESET_DELAY_130CLK

/* Support watchdog timeout values beyond H/W
*
* Watchdog Timer H/W just supports timeout values of 2^4, 2^6, ..., 2^18 clocks.
* To extend the support range to 1 and UINT32_MAX, we cascade multiple small timeouts to
* reach one large timeout specified in hal_watchdog_init.
*/

/* Track if WDT H/W has been initialized */
static bool wdt_hw_inited = 0;
/* Hold initially-configured timeout in hal_watchdog_init */
static uint32_t wdt_timeout_reload_ms = 0;
/* Track remaining timeout for cascading */
static uint32_t wdt_timeout_rmn_clk = 0;

static void watchdog_setup_cascade_timeout(void);
static void WDT_IRQHandler(void);

watchdog_status_t hal_watchdog_init(const watchdog_config_t *config)
{
/* Check validity of arguments */
if (! config || ! config->timeout_ms) {
return WATCHDOG_STATUS_INVALID_ARGUMENT;
}

wdt_timeout_reload_ms = config->timeout_ms;
wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms);

if (! wdt_hw_inited) {
wdt_hw_inited = 1;

SYS_UnlockReg();

/* Enable IP module clock */
CLK_EnableModuleClock(WDT_MODULE);

/* Select IP clock source */
#if MBED_CONF_TARGET_LXT_PRESENT
CLK_SetModuleClock(WDT_MODULE, CLK_CLKSEL1_WDTSEL_LXT, 0);
#else
CLK_SetModuleClock(WDT_MODULE, CLK_CLKSEL1_WDTSEL_LIRC, 0);
#endif

SYS_LockReg();

/* Set up IP interrupt */
NVIC_SetVector(WDT_IRQn, (uint32_t) WDT_IRQHandler);
NVIC_EnableIRQ(WDT_IRQn);
}

watchdog_setup_cascade_timeout();

return WATCHDOG_STATUS_OK;
}

void hal_watchdog_kick(void)
{
/* If a watchdog is not running, this function does nothing */
if (!(WDT->CTL & WDT_CTL_WDTEN_Msk)) {
return;
}

wdt_timeout_rmn_clk = NU_MS2WDTCLK(wdt_timeout_reload_ms);
watchdog_setup_cascade_timeout();
}

watchdog_status_t hal_watchdog_stop(void)
{
SYS_UnlockReg();

/* Clear all flags & Disable interrupt & Disable WDT */
WDT->CTL = (WDT->CTL & ~(WDT_CTL_WDTEN_Msk | WDT_CTL_INTEN_Msk)) | (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk);

SYS_LockReg();

return WATCHDOG_STATUS_OK;
}

uint32_t hal_watchdog_get_reload_value(void)
{
return wdt_timeout_reload_ms;
}

watchdog_features_t hal_watchdog_get_platform_features(void)
{
watchdog_features_t wdt_feat;

/* We can support timeout values between 1 and UINT32_MAX by cascading. */
wdt_feat.max_timeout = UINT32_MAX;
/* Support re-configuring watchdog timer */
wdt_feat.update_config = 1;
/* Support stopping watchdog timer */
wdt_feat.disable_watchdog = 1;
/* Typical frequency of not calibrated watchdog clock in Hz */
wdt_feat.clock_typical_frequency = NU_WDTCLK_PER_SEC;
/* Maximum frequency of not calibrated watchdog clock in Hz */
wdt_feat.clock_max_frequency = NU_WDTCLK_PER_SEC_MAX;

return wdt_feat;
}

static void watchdog_setup_cascade_timeout(void)
{
uint32_t wdt_timeout_clk_toutsel;

if (wdt_timeout_rmn_clk >= NU_WDT_262144CLK) {
wdt_timeout_rmn_clk -= NU_WDT_262144CLK;
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW18;
} else if (wdt_timeout_rmn_clk >= NU_WDT_65536CLK) {
wdt_timeout_rmn_clk -= NU_WDT_65536CLK;
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW16;
} else if (wdt_timeout_rmn_clk >= NU_WDT_16384CLK) {
wdt_timeout_rmn_clk -= NU_WDT_16384CLK;
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW14;
} else if (wdt_timeout_rmn_clk >= NU_WDT_4096CLK) {
wdt_timeout_rmn_clk -= NU_WDT_4096CLK;
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW12;
} else if (wdt_timeout_rmn_clk >= NU_WDT_1024CLK) {
wdt_timeout_rmn_clk -= NU_WDT_1024CLK;
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW10;
} else if (wdt_timeout_rmn_clk >= NU_WDT_256CLK) {
wdt_timeout_rmn_clk -= NU_WDT_256CLK;
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW8;
} else if (wdt_timeout_rmn_clk >= NU_WDT_64CLK) {
wdt_timeout_rmn_clk -= NU_WDT_64CLK;
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW6;
} else if (wdt_timeout_rmn_clk >= NU_WDT_16CLK) {
wdt_timeout_rmn_clk -= NU_WDT_16CLK;
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4;
} else if (wdt_timeout_rmn_clk) {
wdt_timeout_rmn_clk = 0;
wdt_timeout_clk_toutsel = WDT_TIMEOUT_2POW4;
} else {
/* WDT has timed-out and will restart system soon. We just disable interrupt to escape
* getting stuck in WDT ISR. */
SYS_UnlockReg();

/* Clear all flags & Disable interrupt */
WDT->CTL = (WDT->CTL & ~WDT_CTL_INTEN_Msk) | (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk);

SYS_LockReg();
return;
}

SYS_UnlockReg();

/* Configure reset delay on timeout */
WDT->ALTCTL = NU_WDT_RESET_DELAY_RSTDSEL;

/* Reset watchdog timer */
WDT_RESET_COUNTER();

/* Configure another piece of cascaded WDT timeout */
WDT->CTL = wdt_timeout_clk_toutsel | // Timeout interval
WDT_CTL_WDTEN_Msk | // Enable watchdog timer
WDT_CTL_INTEN_Msk | // Enable interrupt
WDT_CTL_WKF_Msk | // Clear wake-up flag
WDT_CTL_WKEN_Msk | // Enable wake-up on timeout
WDT_CTL_IF_Msk | // Clear interrupt flag
WDT_CTL_RSTF_Msk | // Clear reset flag
WDT_CTL_RSTEN_Msk; // Enable reset always to address cascaded timeout failure in interrupt disabled scenario e.g. Hard Fault

SYS_LockReg();
}

void WDT_IRQHandler(void)
{
/* Check WDT interrupt flag */
if (WDT_GET_TIMEOUT_INT_FLAG()) {
/* Continue another piece of cascaded WDT timeout */
watchdog_setup_cascade_timeout();
} else {
/* Clear all flags */
WDT->CTL |= (WDT_CTL_WKF_Msk | WDT_CTL_IF_Msk | WDT_CTL_RSTF_Msk);
}
}

#endif
1 change: 1 addition & 0 deletions targets/TARGET_NUVOTON/TARGET_M261/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ target_sources(mbed-m261
spi_api.c
trng_api.cpp
us_ticker.c
watchdog_api.c

crypto/crypto-misc.cpp
)
Expand Down
Loading

0 comments on commit 3c622f6

Please sign in to comment.