Merge tag 'linux-watchdog-5.9-rc1' of git://www.linux-watchdog.org/linux-watchdog
[linux-2.6-microblaze.git] / drivers / watchdog / watchdog_dev.c
index b9dc2c3..6798add 100644 (file)
@@ -275,15 +275,18 @@ static int watchdog_start(struct watchdog_device *wdd)
        set_bit(_WDOG_KEEPALIVE, &wd_data->status);
 
        started_at = ktime_get();
-       if (watchdog_hw_running(wdd) && wdd->ops->ping)
-               err = wdd->ops->ping(wdd);
-       else
+       if (watchdog_hw_running(wdd) && wdd->ops->ping) {
+               err = __watchdog_ping(wdd);
+               if (err == 0)
+                       set_bit(WDOG_ACTIVE, &wdd->status);
+       } else {
                err = wdd->ops->start(wdd);
-       if (err == 0) {
-               set_bit(WDOG_ACTIVE, &wdd->status);
-               wd_data->last_keepalive = started_at;
-               wd_data->last_hw_keepalive = started_at;
-               watchdog_update_worker(wdd);
+               if (err == 0) {
+                       set_bit(WDOG_ACTIVE, &wdd->status);
+                       wd_data->last_keepalive = started_at;
+                       wd_data->last_hw_keepalive = started_at;
+                       watchdog_update_worker(wdd);
+               }
        }
 
        return err;
@@ -587,7 +590,7 @@ static DEVICE_ATTR_RW(pretimeout_governor);
 static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
                                int n)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct watchdog_device *wdd = dev_get_drvdata(dev);
        umode_t mode = attr->mode;
 
@@ -776,7 +779,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
                err = watchdog_ping(wdd);
                if (err < 0)
                        break;
-               /* fall through */
+               fallthrough;
        case WDIOC_GETTIMEOUT:
                /* timeout == 0 means that we don't know the timeout */
                if (wdd->timeout == 0) {
@@ -916,7 +919,7 @@ static int watchdog_release(struct inode *inode, struct file *file)
         * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then
         * watchdog_stop will fail.
         */
-       if (!test_bit(WDOG_ACTIVE, &wdd->status))
+       if (!watchdog_active(wdd))
                err = 0;
        else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) ||
                 !(wdd->info->options & WDIOF_MAGICCLOSE))
@@ -994,6 +997,15 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
        if (IS_ERR_OR_NULL(watchdog_kworker))
                return -ENODEV;
 
+       device_initialize(&wd_data->dev);
+       wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id);
+       wd_data->dev.class = &watchdog_class;
+       wd_data->dev.parent = wdd->parent;
+       wd_data->dev.groups = wdd->groups;
+       wd_data->dev.release = watchdog_core_data_release;
+       dev_set_drvdata(&wd_data->dev, wdd);
+       dev_set_name(&wd_data->dev, "watchdog%d", wdd->id);
+
        kthread_init_work(&wd_data->work, watchdog_ping_work);
        hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
        wd_data->timer.function = watchdog_timer_expired;
@@ -1014,15 +1026,6 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
                }
        }
 
-       device_initialize(&wd_data->dev);
-       wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id);
-       wd_data->dev.class = &watchdog_class;
-       wd_data->dev.parent = wdd->parent;
-       wd_data->dev.groups = wdd->groups;
-       wd_data->dev.release = watchdog_core_data_release;
-       dev_set_drvdata(&wd_data->dev, wdd);
-       dev_set_name(&wd_data->dev, "watchdog%d", wdd->id);
-
        /* Fill in the data structures */
        cdev_init(&wd_data->cdev, &watchdog_fops);
 
@@ -1135,6 +1138,36 @@ void watchdog_dev_unregister(struct watchdog_device *wdd)
        watchdog_cdev_unregister(wdd);
 }
 
+/*
+ *     watchdog_set_last_hw_keepalive: set last HW keepalive time for watchdog
+ *     @wdd: watchdog device
+ *     @last_ping_ms: time since last HW heartbeat
+ *
+ *     Adjusts the last known HW keepalive time for a watchdog timer.
+ *     This is needed if the watchdog is already running when the probe
+ *     function is called, and it can't be pinged immediately. This
+ *     function must be called immediately after watchdog registration,
+ *     and min_hw_heartbeat_ms must be set for this to be useful.
+ */
+int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd,
+                                  unsigned int last_ping_ms)
+{
+       struct watchdog_core_data *wd_data;
+       ktime_t now;
+
+       if (!wdd)
+               return -EINVAL;
+
+       wd_data = wdd->wd_data;
+
+       now = ktime_get();
+
+       wd_data->last_hw_keepalive = ktime_sub(now, ms_to_ktime(last_ping_ms));
+
+       return __watchdog_ping(wdd);
+}
+EXPORT_SYMBOL_GPL(watchdog_set_last_hw_keepalive);
+
 /*
  *     watchdog_dev_init: init dev part of watchdog core
  *