unsigned int timeout;
        unsigned int min_timeout;
        unsigned int max_timeout;
+       unsigned int max_hw_heartbeat_ms;
        struct notifier_block reboot_nb;
        struct notifier_block restart_nb;
        void *driver_data;
   additional information about the watchdog timer itself. (Like it's unique name)
 * ops: a pointer to the list of watchdog operations that the watchdog supports.
 * timeout: the watchdog timer's timeout value (in seconds).
+  This is the time after which the system will reboot if user space does
+  not send a heartbeat request if WDOG_ACTIVE is set.
 * min_timeout: the watchdog timer's minimum timeout value (in seconds).
-* max_timeout: the watchdog timer's maximum timeout value (in seconds).
+  If set, the minimum configurable value for 'timeout'.
+* max_timeout: the watchdog timer's maximum timeout value (in seconds),
+  as seen from userspace. If set, the maximum configurable value for
+  'timeout'. Not used if max_hw_heartbeat_ms is non-zero.
+* max_hw_heartbeat_ms: Maximum hardware heartbeat, in milli-seconds.
+  If set, the infrastructure will send heartbeats to the watchdog driver
+  if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE
+  is set and userspace failed to send a heartbeat for at least 'timeout'
+  seconds.
 * reboot_nb: notifier block that is registered for reboot notifications, for
   internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core
   will stop the watchdog on such notifications.
   and -EIO for "could not write value to the watchdog". On success this
   routine should set the timeout value of the watchdog_device to the
   achieved timeout value (which may be different from the requested one
-  because the watchdog does not necessarily has a 1 second resolution).
+  because the watchdog does not necessarily have a 1 second resolution).
+  Drivers implementing max_hw_heartbeat_ms set the hardware watchdog heartbeat
+  to the minimum of timeout and max_hw_heartbeat_ms. Those drivers set the
+  timeout value of the watchdog_device either to the requested timeout value
+  (if it is larger than max_hw_heartbeat_ms), or to the achieved timeout value.
   (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the
   watchdog's info structure).
   If the watchdog driver does not have to perform any action but setting the
 
 #include <linux/errno.h>       /* For the -ENODEV/... values */
 #include <linux/fs.h>          /* For file operations */
 #include <linux/init.h>                /* For __init/__exit/... */
+#include <linux/jiffies.h>     /* For timeout functions */
 #include <linux/kernel.h>      /* For printk/panic/... */
 #include <linux/kref.h>                /* For data references */
 #include <linux/miscdevice.h>  /* For handling misc devices */
 #include <linux/slab.h>                /* For memory functions */
 #include <linux/types.h>       /* For standard types (like size_t) */
 #include <linux/watchdog.h>    /* For watchdog specific items */
+#include <linux/workqueue.h>   /* For workqueue */
 #include <linux/uaccess.h>     /* For copy_to_user/put_user/... */
 
 #include "watchdog_core.h"
        struct cdev cdev;
        struct watchdog_device *wdd;
        struct mutex lock;
+       unsigned long last_keepalive;
+       struct delayed_work work;
        unsigned long status;           /* Internal status bits */
 #define _WDOG_DEV_OPEN         0       /* Opened ? */
 #define _WDOG_ALLOW_RELEASE    1       /* Did we receive the magic char ? */
 /* Reference to watchdog device behind /dev/watchdog */
 static struct watchdog_core_data *old_wd_data;
 
+static struct workqueue_struct *watchdog_wq;
+
+static inline bool watchdog_need_worker(struct watchdog_device *wdd)
+{
+       /* All variables in milli-seconds */
+       unsigned int hm = wdd->max_hw_heartbeat_ms;
+       unsigned int t = wdd->timeout * 1000;
+
+       /*
+        * A worker to generate heartbeat requests is needed if all of the
+        * following conditions are true.
+        * - Userspace activated the watchdog.
+        * - The driver provided a value for the maximum hardware timeout, and
+        *   thus is aware that the framework supports generating heartbeat
+        *   requests.
+        * - Userspace requests a longer timeout than the hardware can handle.
+        */
+       return watchdog_active(wdd) && hm && t > hm;
+}
+
+static long watchdog_next_keepalive(struct watchdog_device *wdd)
+{
+       struct watchdog_core_data *wd_data = wdd->wd_data;
+       unsigned int timeout_ms = wdd->timeout * 1000;
+       unsigned long keepalive_interval;
+       unsigned long last_heartbeat;
+       unsigned long virt_timeout;
+       unsigned int hw_heartbeat_ms;
+
+       virt_timeout = wd_data->last_keepalive + msecs_to_jiffies(timeout_ms);
+       hw_heartbeat_ms = min(timeout_ms, wdd->max_hw_heartbeat_ms);
+       keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2);
+
+       /*
+        * To ensure that the watchdog times out wdd->timeout seconds
+        * after the most recent ping from userspace, the last
+        * worker ping has to come in hw_heartbeat_ms before this timeout.
+        */
+       last_heartbeat = virt_timeout - msecs_to_jiffies(hw_heartbeat_ms);
+       return min_t(long, last_heartbeat - jiffies, keepalive_interval);
+}
+
+static inline void watchdog_update_worker(struct watchdog_device *wdd)
+{
+       struct watchdog_core_data *wd_data = wdd->wd_data;
+
+       if (watchdog_need_worker(wdd)) {
+               long t = watchdog_next_keepalive(wdd);
+
+               if (t > 0)
+                       mod_delayed_work(watchdog_wq, &wd_data->work, t);
+       } else {
+               cancel_delayed_work(&wd_data->work);
+       }
+}
+
+static int __watchdog_ping(struct watchdog_device *wdd)
+{
+       int err;
+
+       if (wdd->ops->ping)
+               err = wdd->ops->ping(wdd);  /* ping the watchdog */
+       else
+               err = wdd->ops->start(wdd); /* restart watchdog */
+
+       watchdog_update_worker(wdd);
+
+       return err;
+}
+
 /*
  *     watchdog_ping: ping the watchdog.
  *     @wdd: the watchdog device to ping
 
 static int watchdog_ping(struct watchdog_device *wdd)
 {
-       int err;
+       struct watchdog_core_data *wd_data = wdd->wd_data;
 
        if (!watchdog_active(wdd))
                return 0;
 
-       if (wdd->ops->ping)
-               err = wdd->ops->ping(wdd);      /* ping the watchdog */
-       else
-               err = wdd->ops->start(wdd);     /* restart watchdog */
+       wd_data->last_keepalive = jiffies;
+       return __watchdog_ping(wdd);
+}
 
-       return err;
+static void watchdog_ping_work(struct work_struct *work)
+{
+       struct watchdog_core_data *wd_data;
+       struct watchdog_device *wdd;
+
+       wd_data = container_of(to_delayed_work(work), struct watchdog_core_data,
+                              work);
+
+       mutex_lock(&wd_data->lock);
+       wdd = wd_data->wdd;
+       if (wdd && watchdog_active(wdd))
+               __watchdog_ping(wdd);
+       mutex_unlock(&wd_data->lock);
 }
 
 /*
 
 static int watchdog_start(struct watchdog_device *wdd)
 {
+       struct watchdog_core_data *wd_data = wdd->wd_data;
+       unsigned long started_at;
        int err;
 
        if (watchdog_active(wdd))
                return 0;
 
+       started_at = jiffies;
        err = wdd->ops->start(wdd);
-       if (err == 0)
+       if (err == 0) {
                set_bit(WDOG_ACTIVE, &wdd->status);
+               wd_data->last_keepalive = started_at;
+               watchdog_update_worker(wdd);
+       }
 
        return err;
 }
 
 static int watchdog_stop(struct watchdog_device *wdd)
 {
+       struct watchdog_core_data *wd_data = wdd->wd_data;
        int err;
 
        if (!watchdog_active(wdd))
        }
 
        err = wdd->ops->stop(wdd);
-       if (err == 0)
+       if (err == 0) {
                clear_bit(WDOG_ACTIVE, &wdd->status);
+               cancel_delayed_work(&wd_data->work);
+       }
 
        return err;
 }
        else
                wdd->timeout = timeout;
 
+       watchdog_update_worker(wdd);
+
        return err;
 }
 
                watchdog_ping(wdd);
        }
 
+       cancel_delayed_work_sync(&wd_data->work);
+
        /* make sure that /dev/watchdog can be re-opened */
        clear_bit(_WDOG_DEV_OPEN, &wd_data->status);
 
        wd_data->wdd = wdd;
        wdd->wd_data = wd_data;
 
+       if (!watchdog_wq)
+               return -ENODEV;
+
+       INIT_DELAYED_WORK(&wd_data->work, watchdog_ping_work);
+
        if (wdd->id == 0) {
                old_wd_data = wd_data;
                watchdog_miscdev.parent = wdd->parent;
        wdd->wd_data = NULL;
        mutex_unlock(&wd_data->lock);
 
+       cancel_delayed_work_sync(&wd_data->work);
+
        kref_put(&wd_data->kref, watchdog_core_data_release);
 }
 
 {
        int err;
 
+       watchdog_wq = alloc_workqueue("watchdogd",
+                                     WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
+       if (!watchdog_wq) {
+               pr_err("Failed to create watchdog workqueue\n");
+               return -ENOMEM;
+       }
+
        err = class_register(&watchdog_class);
        if (err < 0) {
                pr_err("couldn't register class\n");
 {
        unregister_chrdev_region(watchdog_devt, MAX_DOGS);
        class_unregister(&watchdog_class);
+       destroy_workqueue(watchdog_wq);
 }
 
 
 
 #include <linux/bitops.h>
-#include <linux/device.h>
 #include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
 #include <linux/notifier.h>
 #include <uapi/linux/watchdog.h>
 
  * @bootstatus:        Status of the watchdog device at boot.
  * @timeout:   The watchdog devices timeout value (in seconds).
  * @min_timeout:The watchdog devices minimum timeout value (in seconds).
- * @max_timeout:The watchdog devices maximum timeout value (in seconds).
+ * @max_timeout:The watchdog devices maximum timeout value (in seconds)
+ *             as configurable from user space. Only relevant if
+ *             max_hw_heartbeat_ms is not provided.
+ * @max_hw_heartbeat_ms:
+ *             Hardware limit for maximum timeout, in milli-seconds.
+ *             Replaces max_timeout if specified.
  * @reboot_nb: The notifier block to stop watchdog on reboot.
  * @restart_nb:        The notifier block to register a restart function.
  * @driver_data:Pointer to the drivers private data.
  * @wd_data:   Pointer to watchdog core internal data.
  * @status:    Field that contains the devices internal status bits.
- * @deferred: entry in wtd_deferred_reg_list which is used to
- *                        register early initialized watchdogs.
+ * @deferred:  Entry in wtd_deferred_reg_list which is used to
+ *             register early initialized watchdogs.
  *
  * The watchdog_device structure contains all information about a
  * watchdog timer device.
        unsigned int timeout;
        unsigned int min_timeout;
        unsigned int max_timeout;
+       unsigned int max_hw_heartbeat_ms;
        struct notifier_block reboot_nb;
        struct notifier_block restart_nb;
        void *driver_data;
 {
        /*
         * The timeout is invalid if
+        * - the requested value is larger than UINT_MAX / 1000
+        *   (since internal calculations are done in milli-seconds),
+        * or
         * - the requested value is smaller than the configured minimum timeout,
         * or
-        * - a maximum timeout is configured, and the requested value is larger
-        *   than the maximum timeout.
+        * - a maximum hardware timeout is not configured, a maximum timeout
+        *   is configured, and the requested value is larger than the
+        *   configured maximum timeout.
         */
-       return t < wdd->min_timeout ||
-               (wdd->max_timeout && t > wdd->max_timeout);
+       return t > UINT_MAX / 1000 || t < wdd->min_timeout ||
+               (!wdd->max_hw_heartbeat_ms && wdd->max_timeout &&
+                t > wdd->max_timeout);
 }
 
 /* Use the following functions to manipulate watchdog driver specific data */