driver: wdt: npcx: replace critical sections with timeout mechanism.

TWDT0 is loaded with a new value and the counter restarts counting with
it by written RST bit in Timer Control register (T0CSR) to 1. Then, the
RST bit in T0CSR register is cleared automatically on the 2nd rising
edge of T0IN clock. Since TWCP is set to 1:32, the maximum time that RST
bit is unset is 32 * (1 / 32768) ~= 980us.

Polling this bit within a critical section in current npcx watchdog
driver isn't a good approach since it might block the other interrupts
need to service them in time. This CL introduces a timeout mechanism and
removes the critical section to improve this disadvantage. Consider the
clock tolerance, 2 ms is a suitable timeout value for RST bit. We also
remove polling for WD_RUN bit in T0CSR. Npcx watchdog needs serval LFCLK
(32k Hz) clocks to stop watchdog. 1 ms is long enough for the timeout
mechanism.

Signed-off-by: Mulin Chao <mlchao@nuvoton.com>
This commit is contained in:
Mulin Chao 2021-04-20 23:00:36 -07:00 committed by Maureen Helm
commit 1a2e59b2e9

View file

@ -53,6 +53,12 @@ LOG_MODULE_REGISTER(wdt_npcx, CONFIG_WDT_LOG_LEVEL);
*/
#define NPCX_WDT_MIN_WND_TIME 100UL
/* Timeout for reloading and restarting Timer 0. (Unit:ms) */
#define NPCX_T0CSR_RST_TIMEOUT 2
/* Timeout for stopping watchdog. (Unit:ms) */
#define NPCX_WATCHDOG_STOP_TIMEOUT 1
/* Device config */
struct wdt_npcx_config {
/* wdt controller base address */
@ -81,31 +87,47 @@ struct miwu_dev_callback miwu_cb;
#define HAL_INSTANCE(dev) (struct twd_reg *)(DRV_CONFIG(dev)->base)
/* WDT local inline functions */
static inline void wdt_t0out_reload(const struct device *dev)
static inline int wdt_t0out_reload(const struct device *dev)
{
struct twd_reg *const inst = HAL_INSTANCE(dev);
unsigned int key;
uint64_t st;
key = irq_lock();
/* Reload and restart T0 timer */
inst->T0CSR = (inst->T0CSR & ~BIT(NPCX_T0CSR_WDRST_STS)) |
BIT(NPCX_T0CSR_RST);
/* Wait for timer is loaded and restart */
while (IS_BIT_SET(inst->T0CSR, NPCX_T0CSR_RST))
;
irq_unlock(key);
st = k_uptime_get();
while (IS_BIT_SET(inst->T0CSR, NPCX_T0CSR_RST)) {
if (k_uptime_get() - st > NPCX_T0CSR_RST_TIMEOUT) {
/* RST bit is still set? */
if (IS_BIT_SET(inst->T0CSR, NPCX_T0CSR_RST)) {
LOG_ERR("Timeout: reload T0 timer!");
return -ETIMEDOUT;
}
}
}
return 0;
}
static inline void wdt_wait_stopped(const struct device *dev)
static inline int wdt_wait_stopped(const struct device *dev)
{
struct twd_reg *const inst = HAL_INSTANCE(dev);
unsigned int key;
uint64_t st;
key = irq_lock();
st = k_uptime_get();
/* If watchdog is still running? */
while (IS_BIT_SET(inst->T0CSR, NPCX_T0CSR_WD_RUN))
;
irq_unlock(key);
while (IS_BIT_SET(inst->T0CSR, NPCX_T0CSR_WD_RUN)) {
if (k_uptime_get() - st > NPCX_WATCHDOG_STOP_TIMEOUT) {
/* WD_RUN bit is still set? */
if (IS_BIT_SET(inst->T0CSR, NPCX_T0CSR_WD_RUN)) {
LOG_ERR("Timeout: stop watchdog timer!");
return -ETIMEDOUT;
}
}
}
return 0;
}
/* WDT local functions */
@ -184,6 +206,7 @@ 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 = DRV_CONFIG(dev);
struct wdt_npcx_data *const data = DRV_DATA(dev);
int rv;
/* Disable irq of t0-out expired event first */
npcx_miwu_irq_disable(&config->t0out);
@ -222,7 +245,7 @@ static int wdt_npcx_setup(const struct device *dev, uint8_t options)
LOG_DBG("WDT setup: TWDT0, WDCNT are %d, %d", inst->TWDT0, inst->WDCNT);
/* Reload and restart T0 timer */
wdt_t0out_reload(dev);
rv = wdt_t0out_reload(dev);
/* Configure t0 timer interrupt and its isr. */
wdt_config_t0out_interrupt(dev);
@ -230,7 +253,7 @@ static int wdt_npcx_setup(const struct device *dev, uint8_t options)
/* Enable irq of t0-out expired event */
npcx_miwu_irq_enable(&config->t0out);
return 0;
return rv;
}
static int wdt_npcx_disable(const struct device *dev)
@ -260,9 +283,7 @@ static int wdt_npcx_disable(const struct device *dev)
data->timeout_installed = false;
/* Wait for watchdof is stopped. */
wdt_wait_stopped(dev);
return 0;
return wdt_wait_stopped(dev);
}
static int wdt_npcx_feed(const struct device *dev, int channel_id)
@ -276,9 +297,7 @@ static int wdt_npcx_feed(const struct device *dev, int channel_id)
data->last_watchdog_touch = k_uptime_get();
/* Reload and restart T0 timer */
wdt_t0out_reload(dev);
return 0;
return wdt_t0out_reload(dev);
}
/* WDT driver registration */