driver: watchdog: npcx: support longer watchdog timeout

In the current driver, the longest time of the watchdog timeout is ~8
seconds because the pre-scalar is fixed at 32 (WDCP=5). This commit
removes this limitation by dynamically calculating the pre-scalar
according to the watchdog timeout setting from the API.

Signed-off-by: Jun Lin <CHLin56@nuvoton.com>
Signed-off-by: Tom Chang <CHChang19@nuvoton.com>
This commit is contained in:
Jun Lin 2024-05-09 16:52:00 +08:00 committed by Fabio Baltieri
commit c864024c32
2 changed files with 36 additions and 14 deletions

View file

@ -12,12 +12,11 @@ config WDT_NPCX
processors.
Say y if you wish to use watchdog on NPCX MCU.
config WDT_NPCX_DELAY_CYCLES
int "Number of delay cycles before generating watchdog event/signal"
config WDT_NPCX_WARNING_LEADING_TIME_MS
int "Milliseconds before generating watchdog event/signal"
depends on WDT_NPCX
range 1 255
default 10
default 500
help
This option defines the window in which a watchdog event must be
handled, in units of 31ms. After this time window, the watchdog reset
triggers immediately.
handled. After this time window, the watchdog reset triggers
immediately.

View file

@ -46,10 +46,13 @@ LOG_MODULE_REGISTER(wdt_npcx, CONFIG_WDT_LOG_LEVEL);
#define NPCX_WDT_CLK LFCLK
/*
* Maximum watchdog window time. Since the watchdog counter is 8-bits, maximum
* time supported by npcx watchdog is 256 * (32 * 32) / 32768 = 8 sec.
* Maximum watchdog window time. Keep the timer and watchdog clock prescaler
* (TWCP) to 0x5. Since the watchdog counter is 8-bits, maximum time supported
* by npcx watchdog is 256 * (32 * 32768) / 32768 = 8192 sec.
* The maximum time supported of T0OUT is 65536 * 32 / 32768 = 64 sec.
* Thus, the maximum time of watchdog set here is 64 sec.
*/
#define NPCX_WDT_MAX_WND_TIME 8000UL
#define NPCX_WDT_MAX_WND_TIME 64000UL
/*
* Minimum watchdog window time. Ensure we have waited at least 3 watchdog
@ -139,7 +142,7 @@ static void wdt_t0out_isr(const struct device *dev, struct npcx_wui *wui)
ARG_UNUSED(wui);
LOG_DBG("WDT reset will issue after %d delay cycle! WUI(%d %d %d)",
CONFIG_WDT_NPCX_DELAY_CYCLES, wui->table, wui->group, wui->bit);
CONFIG_WDT_NPCX_WARNING_LEADING_TIME_MS, wui->table, wui->group, wui->bit);
/* Handle watchdog event here. */
if (data->cb) {
@ -208,6 +211,9 @@ static int wdt_npcx_setup(const struct device *dev, uint8_t options)
struct twd_reg *const inst = HAL_INSTANCE(dev);
const struct wdt_npcx_config *const config = dev->config;
struct wdt_npcx_data *const data = dev->data;
uint32_t wd_cnt, pre_scal;
uint8_t wdcp;
int rv;
/* Disable irq of t0-out expired event first */
@ -242,9 +248,24 @@ static int wdt_npcx_setup(const struct device *dev, uint8_t options)
inst->TWDT0 = MAX(DIV_ROUND_UP(data->timeout * NPCX_WDT_CLK,
32 * 1000) - 1, 1);
/* Configure 8-bit watchdog counter */
inst->WDCNT = MIN(DIV_ROUND_UP(data->timeout, 32) +
CONFIG_WDT_NPCX_DELAY_CYCLES, 0xff);
/* Configure 8-bit watchdog counter
* Change the prescaler of watchdog clock for larger timeout
*/
wd_cnt = DIV_ROUND_UP((data->timeout + CONFIG_WDT_NPCX_WARNING_LEADING_TIME_MS) *
NPCX_WDT_CLK,
32 * 1000);
pre_scal = DIV_ROUND_UP(wd_cnt, 255);
/*
* Find the smallest power of 2 greater than or equal to the
* prescaler
*/
wdcp = LOG2(pre_scal - 1) + 1;
pre_scal = 1 << wdcp;
inst->WDCP = wdcp;
inst->WDCNT = wd_cnt / pre_scal;
LOG_DBG("WDT setup: TWDT0, WDCNT are %d, %d", inst->TWDT0, inst->WDCNT);
@ -265,12 +286,14 @@ static int wdt_npcx_disable(const struct device *dev)
const struct wdt_npcx_config *const config = dev->config;
struct wdt_npcx_data *const data = dev->data;
struct twd_reg *const inst = HAL_INSTANCE(dev);
uint16_t min_wnd_t;
/*
* Ensure we have waited at least 3 watchdog ticks before
* stopping watchdog
*/
while (k_uptime_get() - data->last_watchdog_touch < NPCX_WDT_MIN_WND_TIME) {
min_wnd_t = DIV_ROUND_UP(3 * NPCX_WDT_CLK, 32 * (1 << inst->WDCP));
while (k_uptime_get() - data->last_watchdog_touch < min_wnd_t) {
continue;
}