diff --git a/drivers/timers/Kconfig b/drivers/timers/Kconfig index cb34cc4965d..734a9b787d7 100644 --- a/drivers/timers/Kconfig +++ b/drivers/timers/Kconfig @@ -255,6 +255,40 @@ config WATCHDOG_DEVPATH string "Watchdog Device Path" default "/dev/watchdog0" +config WATCHDOG_AUTOMONITOR + bool "Auto-monitor" + ---help--- + The auto-monitor provides an OS-internal mechanism for automatically + start and repeatly reset the counting after the watchdog is register. + +if WATCHDOG_AUTOMONITOR + +config WATCHDOG_AUTOMONITOR_TIMEOUT + int "Auto-monitor reset timeout(second)" + default 60 + +choice + prompt "Auto-monitor keepalive by" + default WATCHDOG_AUTOMONITOR_BY_TIMER + +config WATCHDOG_AUTOMONITOR_BY_CAPTURE + bool "Capture callback" + +config WATCHDOG_AUTOMONITOR_BY_TIMER + bool "Timer callback" + +config WATCHDOG_AUTOMONITOR_BY_WORKER + bool "Worker callback" + depends on SCHED_WORKQUEUE + +config WATCHDOG_AUTOMONITOR_BY_IDLE + bool "Idle callback" + depends on PM + +endchoice + +endif # WATCHDOG_AUTOMONITOR + endif # WATCHDOG config TIMERS_CS2100CP diff --git a/drivers/timers/watchdog.c b/drivers/timers/watchdog.c index a1a29fbaf4b..7433e5ef56f 100644 --- a/drivers/timers/watchdog.c +++ b/drivers/timers/watchdog.c @@ -52,10 +52,22 @@ #include #include #include +#include +#include +#include #include #ifdef CONFIG_WATCHDOG +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define WATCHDOG_AUTOMONITOR_TIMEOUT_MSEC \ + (1000 * CONFIG_WATCHDOG_AUTOMONITOR_TIMEOUT) +#define WATCHDOG_AUTOMONITOR_TIMEOUT_TICK \ + SEC2TICK(CONFIG_WATCHDOG_AUTOMONITOR_TIMEOUT) + /**************************************************************************** * Private Type Definitions ****************************************************************************/ @@ -64,6 +76,17 @@ struct watchdog_upperhalf_s { +#ifdef CONFIG_WATCHDOG_AUTOMONITOR +#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER) + WDOG_ID wdog; +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER) + struct work_s work; +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE) + struct pm_callback_s idle; +#endif + bool monitor; +#endif + uint8_t crefs; /* The number of times the device has been opened */ sem_t exclsem; /* Supports mutual exclusion */ FAR char *path; /* Registration path */ @@ -107,13 +130,118 @@ static const struct file_operations g_wdogops = * Private Functions ****************************************************************************/ -/************************************************************************************ +#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE) +static int watchdog_automonitor_capture(int irq, FAR void *context, + FAR void *arg) +{ + FAR struct watchdog_upperhalf_s *upper = arg; + FAR struct watchdog_lowerhalf_s *lower = upper->lower; + + if (upper->monitor) + { + lower->ops->keepalive(lower); + } + + return 0; +} +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER) +static void watchdog_automonitor_timer(int argc, wdparm_t arg1, ...) +{ + FAR struct watchdog_upperhalf_s *upper = (FAR void *)arg1; + FAR struct watchdog_lowerhalf_s *lower = upper->lower; + + if (upper->monitor) + { + lower->ops->keepalive(lower); + wd_start(upper->wdog, WATCHDOG_AUTOMONITOR_TIMEOUT_TICK / 2, + watchdog_automonitor_timer, 1, upper); + } +} +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER) +static void watchdog_automonitor_worker(FAR void *arg) +{ + FAR struct watchdog_upperhalf_s *upper = arg; + FAR struct watchdog_lowerhalf_s *lower = upper->lower; + + if (upper->monitor) + { + lower->ops->keepalive(lower); + work_queue(LPWORK, &upper->work, watchdog_automonitor_worker, + upper, WATCHDOG_AUTOMONITOR_TIMEOUT_TICK / 2); + } +} +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE) +static void watchdog_automonitor_idle(FAR struct pm_callback_s *cb, + int domain, enum pm_state_e pmstate) +{ + FAR struct watchdog_upperhalf_s *upper = (FAR void *)cb; + FAR struct watchdog_lowerhalf_s *lower = upper->lower; + + if (upper->monitor) + { + lower->ops->keepalive(lower); + } +} +#endif + +#ifdef CONFIG_WATCHDOG_AUTOMONITOR +static void watchdog_automonitor_start(FAR struct watchdog_upperhalf_s *upper) +{ + FAR struct watchdog_lowerhalf_s *lower = upper->lower; + + if (!upper->monitor) + { + upper->monitor = true; +#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE) + lower->ops->capture(lower, watchdog_automonitor_capture); +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER) + upper->wdog = wd_create(); + wd_start(upper->wdog, WATCHDOG_AUTOMONITOR_TIMEOUT_TICK / 2, + watchdog_automonitor_timer, 1, upper); +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER) + work_queue(LPWORK, &upper->work, watchdog_automonitor_worker, + upper, WATCHDOG_AUTOMONITOR_TIMEOUT_TICK / 2); +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE) + upper->idle.notify = watchdog_automonitor_idle; + pm_register(&upper->idle); +#endif + if (lower->ops->settimeout) + { + lower->ops->settimeout(lower, WATCHDOG_AUTOMONITOR_TIMEOUT_MSEC); + } + + lower->ops->start(lower); + } +} + +static void watchdog_automonitor_stop(FAR struct watchdog_upperhalf_s *upper) +{ + FAR struct watchdog_lowerhalf_s *lower = upper->lower; + + if (upper->monitor) + { + upper->monitor = false; + lower->ops->stop(lower); +#if defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_CAPTURE) + lower->ops->capture(lower, NULL); +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_TIMER) + wd_delete(upper->wdog); +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_WORKER) + work_cancel(LPWORK, &upper->work); +#elif defined(CONFIG_WATCHDOG_AUTOMONITOR_BY_IDLE) + pm_unregister(&upper->idle); +#endif + } +} +#endif + +/**************************************************************************** * Name: wdog_open * * Description: * This function is called whenever the watchdog timer device is opened. * - ************************************************************************************/ + ****************************************************************************/ static int wdog_open(FAR struct file *filep) { @@ -158,13 +286,13 @@ errout: return ret; } -/************************************************************************************ +/**************************************************************************** * Name: wdog_close * * Description: * This function is called when the watchdog timer device is closed. * - ************************************************************************************/ + ****************************************************************************/ static int wdog_close(FAR struct file *filep) { @@ -198,42 +326,44 @@ errout: return ret; } -/************************************************************************************ +/**************************************************************************** * Name: wdog_read * * Description: * A dummy read method. This is provided only to satisfy the VFS layer. * - ************************************************************************************/ + ****************************************************************************/ -static ssize_t wdog_read(FAR struct file *filep, FAR char *buffer, size_t buflen) +static ssize_t wdog_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) { /* Return zero -- usually meaning end-of-file */ return 0; } -/************************************************************************************ +/**************************************************************************** * Name: wdog_write * * Description: * A dummy write method. This is provided only to satisfy the VFS layer. * - ************************************************************************************/ + ****************************************************************************/ -static ssize_t wdog_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) +static ssize_t wdog_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) { return 0; } -/************************************************************************************ +/**************************************************************************** * Name: wdog_ioctl * * Description: - * The standard ioctl method. This is where ALL of the watchdog timer work is - * done. + * The standard ioctl method. This is where ALL of the watchdog timer + * work is done. * - ************************************************************************************/ + ****************************************************************************/ static int wdog_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { @@ -267,7 +397,13 @@ static int wdog_ioctl(FAR struct file *filep, int cmd, unsigned long arg) case WDIOC_START: { - /* Start the watchdog timer, resetting the time to the current timeout */ +#ifdef CONFIG_WATCHDOG_AUTOMONITOR + watchdog_automonitor_stop(upper); +#endif + + /* Start the watchdog timer, resetting the time to the current + * timeout + */ DEBUGASSERT(lower->ops->start); /* Required */ ret = lower->ops->start(lower); @@ -380,8 +516,8 @@ static int wdog_ioctl(FAR struct file *filep, int cmd, unsigned long arg) case WDIOC_KEEPALIVE: { - /* Reset the watchdog timer to the current timeout value, prevent any - * imminent watchdog timeouts. This is sometimes referred as + /* Reset the watchdog timer to the current timeout value, prevent + * any imminent watchdog timeouts. This is sometimes referred as * "pinging" the watchdog timer or "petting the dog". */ @@ -396,8 +532,9 @@ static int wdog_ioctl(FAR struct file *filep, int cmd, unsigned long arg) } break; - - /* Any unrecognized IOCTL commands might be platform-specific ioctl commands */ + /* Any unrecognized IOCTL commands might be platform-specific ioctl + * commands + */ default: { @@ -432,18 +569,19 @@ static int wdog_ioctl(FAR struct file *filep, int cmd, unsigned long arg) * Name: watchdog_register * * Description: - * This function binds an instance of a "lower half" watchdog driver with the - * "upper half" watchdog device and registers that device so that can be used - * by application code. + * This function binds an instance of a "lower half" watchdog driver with + * the "upper half" watchdog device and registers that device so that can + * be usedby application code. * * When this function is called, the "lower half" driver should be in the * disabled state (as if the stop() method had already been called). * * Input Parameters: * dev path - The full path to the driver to be registers in the NuttX - * pseudo-filesystem. The recommended convention is to name all watchdog - * drivers as "/dev/watchdog0", "/dev/watchdog1", etc. where the driver - * path differs only in the "minor" number at the end of the device name. + * pseudo-filesystem. The recommended convention is to name all + * watchdogdrivers as "/dev/watchdog0", "/dev/watchdog1", etc. where + * the driverpath differs only in the "minor" number at the end of the + * device name. * lower - A pointer to an instance of lower half watchdog driver. This * instance is bound to the watchdog driver and must persists as long as * the driver persists. @@ -498,6 +636,10 @@ FAR void *watchdog_register(FAR const char *path, goto errout_with_path; } +#ifdef CONFIG_WATCHDOG_AUTOMONITOR + watchdog_automonitor_start(upper); +#endif + return (FAR void *)upper; errout_with_path: @@ -540,6 +682,10 @@ void watchdog_unregister(FAR void *handle) wdinfo("Unregistering: %s\n", upper->path); +#ifdef CONFIG_WATCHDOG_AUTOMONITOR + watchdog_automonitor_stop(upper); +#endif + /* Disable the watchdog timer */ DEBUGASSERT(lower->ops->stop); /* Required */