summaryrefslogtreecommitdiff
path: root/drivers/watchdog/watchdog_dev.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2020-08-26 01:36:07 +0200
committerThomas Gleixner <tglx@linutronix.de>2020-08-26 01:36:07 +0200
commitceb2465c51195967f11f6507538579816ac67cb8 (patch)
treeeab534b1062608e36a7b54ccc7be693625f235b8 /drivers/watchdog/watchdog_dev.c
parentf107cee94ba4d2c7357fde59a1d84346c73d4958 (diff)
parent821fc9e261f3af235752f46e59084467cfd440c4 (diff)
Merge tag 'irqchip-fixes-5.9-2' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/urgentirq-urgent-2020-08-30
Pull irqchip fixes from Marc Zyngier: - Revert the wholesale conversion to platform drivers of the pdc, sysirq and cirq drivers, as it breaks a number of platforms even when the driver is built-in (probe ordering bites you). - Prevent interrupt from being lost with the STM32 exti driver - Fix wake-up interrupts for the MIPS Ingenic driver - Fix an embarassing typo in the new module helpers, leading to the probe failing most of the time - The promised TI firmware rework that couldn't make it into the merge window due to a very badly managed set of dependencies
Diffstat (limited to 'drivers/watchdog/watchdog_dev.c')
-rw-r--r--drivers/watchdog/watchdog_dev.c73
1 files changed, 53 insertions, 20 deletions
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index b9dc2c352151..6798addabd5a 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -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);
@@ -1136,6 +1139,36 @@ void watchdog_dev_unregister(struct watchdog_device *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
*
* Allocate a range of chardev nodes to use for watchdog devices