All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature
@ 2015-04-22 11:11 ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni
  Cc: Wenyou.Yang, Timo Kokkonen

The watchdog kernel API is quite limited. It has support for providing
generic device handling, but it doesn't really know anything about the
watchdog hardware or its constraints. The watchdog drivers come with a
lot of diversity and their own set of quirks and constraints. Some of
their limitations are not nice for the user space, so the drivers work
around them with all sorts of ad hoc implementations.

One common pattern is to use kernel timers or work queues to allow
longer timeout parameters than the actual hardware supports. To solve
this problem, this patch set extends the kernel watchdog API with a few
parameters that let the core know more about the watchdog HW and take
care about the timeout extending.

The patch set also implements "early_timeout_sec" feature that is very
common on many production systems where early kernel or user space
crashes must lead to a device reset. Traditional watchdog handling
does not allow this as the watchdog is stopped (fully or emulating
stopped state with kernel timers) before user space opens it for the
first time.

As a consequence of the core being able to extend the HW timeout, the
maximum timeout parameter is no longer used with the drivers that use
the new API extension. Instead, arbitrary timeout parameters are
supported, as long as they are longer than the minimum supported by
the driver.

The changes in this series are designed to be taken in use one driver
at time. Old drivers continue to work exactly the same as before.

This patch set converts at91sam9_wdt, imx2_wdt and omap_wdt to use the
new API. The patches have been tested on three ARM boards: ADG42 (at91
sama5d3), beaglebone and wandboard dual.

Please review and give feedback.

Patch revision history:

-v7: Convert also omap_wdt and imx2_wdt drivers in addition to
  at91sam9_wdt to use the new core API. This allowed me to test a lot
  more use cases and find many bugs I had left in previous
  version. The max_timeout parameter is no longer needed for new
  drivers as watchdog core can extend the timeout and support
  unlimited timeout values on behalf of the HW.

-v6: Fixed some issued based on feedback from Wenyou Yang. The logic
  in watchdog_worker() function is now significantly easier to
  read. Several errors with stopping and starting the worker are also
  now fixed.

-v5: Re-think the approach to be fully generic. The early_timeout_sec
  handling is no longer in the driver but in the watchdog core. As a
  result the core needed to gain knowledge about the watchdog
  hardware. Appropriate handling is added in the core. The side effect
  for this is that drivers using the new extensions can be simplified
  a lot and different kinds of watchdog hardware can be made to
  behave the same for the user space.

-v4: Binding documentation is now separated completely from the driver
  patch. The documentation no longer makes any assumptions about how
  the actual implementation is made, it just describes the actual
  behavior the driver should implement in order to satisfy the
  requirement.

- v3: Rename the property to "early-timeout-sec" and use it as a
  timeout value that stops the timer in the atmel driver after the
  timeout expires. A watchdog.txt is also introduced for documenting
  the common watchdog properties, including now this one and
  "timeout-sec" property.

- v2: Rename the property to "enable-early-reset" as the behavior
  itself is not atmel specific. This way other drivers are free to
  implement same behavior with the same property name.

- v1: Propose property name "atmle,no-early-timer" for disabling the
  timer that keeps the atmel watchdog running until user space opens
  the device.


Timo Kokkonen (8):
  watchdog: Extend kernel API to know about HW limitations
  watchdog: Allow watchdog to reset device at early boot
  devicetree: Document generic watchdog properties
  Documentation/watchdog: watchdog-test.c: Add support for changing
    timeout
  watchdog: at91sam9_wdt: Convert to use new watchdog core extensions
  watchdog: imx2_wdt: Convert to use new core extensions
  watchdog: omap_wdt: Fix memory leak on probe fail
  watchdog: omap_wdt: Convert to use new core extensions

 .../devicetree/bindings/watchdog/watchdog.txt      |  20 ++++
 Documentation/watchdog/src/watchdog-test.c         |   6 +
 drivers/watchdog/at91sam9_wdt.c                    |  62 +++-------
 drivers/watchdog/imx2_wdt.c                        |  43 ++-----
 drivers/watchdog/omap_wdt.c                        |  48 ++++++--
 drivers/watchdog/watchdog_core.c                   | 127 ++++++++++++++++++++-
 drivers/watchdog/watchdog_dev.c                    |  79 +++++++++++--
 include/linux/watchdog.h                           |  24 ++++
 8 files changed, 313 insertions(+), 96 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/watchdog/watchdog.txt

-- 
2.1.0


^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature
@ 2015-04-22 11:11 ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

The watchdog kernel API is quite limited. It has support for providing
generic device handling, but it doesn't really know anything about the
watchdog hardware or its constraints. The watchdog drivers come with a
lot of diversity and their own set of quirks and constraints. Some of
their limitations are not nice for the user space, so the drivers work
around them with all sorts of ad hoc implementations.

One common pattern is to use kernel timers or work queues to allow
longer timeout parameters than the actual hardware supports. To solve
this problem, this patch set extends the kernel watchdog API with a few
parameters that let the core know more about the watchdog HW and take
care about the timeout extending.

The patch set also implements "early_timeout_sec" feature that is very
common on many production systems where early kernel or user space
crashes must lead to a device reset. Traditional watchdog handling
does not allow this as the watchdog is stopped (fully or emulating
stopped state with kernel timers) before user space opens it for the
first time.

As a consequence of the core being able to extend the HW timeout, the
maximum timeout parameter is no longer used with the drivers that use
the new API extension. Instead, arbitrary timeout parameters are
supported, as long as they are longer than the minimum supported by
the driver.

The changes in this series are designed to be taken in use one driver
at time. Old drivers continue to work exactly the same as before.

This patch set converts at91sam9_wdt, imx2_wdt and omap_wdt to use the
new API. The patches have been tested on three ARM boards: ADG42 (at91
sama5d3), beaglebone and wandboard dual.

Please review and give feedback.

Patch revision history:

-v7: Convert also omap_wdt and imx2_wdt drivers in addition to
  at91sam9_wdt to use the new core API. This allowed me to test a lot
  more use cases and find many bugs I had left in previous
  version. The max_timeout parameter is no longer needed for new
  drivers as watchdog core can extend the timeout and support
  unlimited timeout values on behalf of the HW.

-v6: Fixed some issued based on feedback from Wenyou Yang. The logic
  in watchdog_worker() function is now significantly easier to
  read. Several errors with stopping and starting the worker are also
  now fixed.

-v5: Re-think the approach to be fully generic. The early_timeout_sec
  handling is no longer in the driver but in the watchdog core. As a
  result the core needed to gain knowledge about the watchdog
  hardware. Appropriate handling is added in the core. The side effect
  for this is that drivers using the new extensions can be simplified
  a lot and different kinds of watchdog hardware can be made to
  behave the same for the user space.

-v4: Binding documentation is now separated completely from the driver
  patch. The documentation no longer makes any assumptions about how
  the actual implementation is made, it just describes the actual
  behavior the driver should implement in order to satisfy the
  requirement.

- v3: Rename the property to "early-timeout-sec" and use it as a
  timeout value that stops the timer in the atmel driver after the
  timeout expires. A watchdog.txt is also introduced for documenting
  the common watchdog properties, including now this one and
  "timeout-sec" property.

- v2: Rename the property to "enable-early-reset" as the behavior
  itself is not atmel specific. This way other drivers are free to
  implement same behavior with the same property name.

- v1: Propose property name "atmle,no-early-timer" for disabling the
  timer that keeps the atmel watchdog running until user space opens
  the device.


Timo Kokkonen (8):
  watchdog: Extend kernel API to know about HW limitations
  watchdog: Allow watchdog to reset device at early boot
  devicetree: Document generic watchdog properties
  Documentation/watchdog: watchdog-test.c: Add support for changing
    timeout
  watchdog: at91sam9_wdt: Convert to use new watchdog core extensions
  watchdog: imx2_wdt: Convert to use new core extensions
  watchdog: omap_wdt: Fix memory leak on probe fail
  watchdog: omap_wdt: Convert to use new core extensions

 .../devicetree/bindings/watchdog/watchdog.txt      |  20 ++++
 Documentation/watchdog/src/watchdog-test.c         |   6 +
 drivers/watchdog/at91sam9_wdt.c                    |  62 +++-------
 drivers/watchdog/imx2_wdt.c                        |  43 ++-----
 drivers/watchdog/omap_wdt.c                        |  48 ++++++--
 drivers/watchdog/watchdog_core.c                   | 127 ++++++++++++++++++++-
 drivers/watchdog/watchdog_dev.c                    |  79 +++++++++++--
 include/linux/watchdog.h                           |  24 ++++
 8 files changed, 313 insertions(+), 96 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/watchdog/watchdog.txt

-- 
2.1.0

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
  2015-04-22 11:11 ` Timo Kokkonen
@ 2015-04-22 11:11   ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni
  Cc: Wenyou.Yang, Timo Kokkonen

There is a great deal of diversity in the watchdog hardware found on
different devices. Differen hardware have different contstraints on
them, many of the constraints that are excessively difficult for the
user space to satisfy.

One such constraint is the length of the timeout value, which in many
cases can be just a few seconds. Drivers are creating ad hoc solutions
with timers and workqueues to extend the timeout in order to give user
space more time between updates. Looking at the drivers it is clear
that this has resulted to a lot of duplicate code.

Add an extension to the watchdog kernel API that allows the driver to
describe tis HW constraints to the watchdog code. A kernel worker in
the core is then used to extend the watchdog timeout on behalf of the
user space. This allows the drivers to be simplified as core takes
over the timer extending.

Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
 drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
 include/linux/watchdog.h         |  23 +++++++++
 3 files changed, 189 insertions(+), 10 deletions(-)

diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index cec9b55..fd12489 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
 EXPORT_SYMBOL_GPL(watchdog_init_timeout);
 
 /**
+ * watchdog_init_parms() - initialize generic watchdog parameters
+ * @wdd: Watchdog device to operate
+ * @dev: Device that stores the device tree properties
+ *
+ * Initialize the generic timeout parameters. The driver needs to set
+ * hw_features bitmask from @wdd prior calling this function in order
+ * to ensure the core knows how to handle the HW.
+ *
+ * A zero is returned on success and -EINVAL for failure.
+ */
+int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
+{
+	int ret = 0;
+
+	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Max HW timeout needs to be set so that core knows when to
+	 * use a kernel worker to support longer watchdog timeouts
+	 */
+	if (!wdd->hw_max_timeout)
+		return -EINVAL;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(watchdog_init_params);
+
+static void watchdog_worker(struct work_struct *work)
+{
+	struct watchdog_device *wdd = container_of(to_delayed_work(work),
+						struct watchdog_device, work);
+	bool boot_keepalive;
+	bool active_keepalive;
+
+	mutex_lock(&wdd->lock);
+
+	boot_keepalive = !watchdog_active(wdd) &&
+		!watchdog_is_stoppable(wdd);
+
+	active_keepalive = watchdog_active(wdd) &&
+		wdd->hw_max_timeout < wdd->timeout * HZ;
+
+	if (time_after(jiffies, wdd->expires) && watchdog_active(wdd)) {
+		pr_crit("I will reset your machine !\n");
+		mutex_unlock(&wdd->lock);
+		return;
+	}
+
+	if (boot_keepalive || active_keepalive) {
+		if (wdd->ops->ping)
+			wdd->ops->ping(wdd);
+		else
+			wdd->ops->start(wdd);
+
+		schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
+	}
+
+	mutex_unlock(&wdd->lock);
+}
+
+static int prepare_watchdog(struct watchdog_device *wdd)
+{
+	int err = 0;
+
+	/* Stop the watchdog now before user space opens the device */
+	if (watchdog_is_stoppable(wdd) &&
+		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
+		err = wdd->ops->stop(wdd);
+
+	} else if (!watchdog_is_stoppable(wdd) &&
+		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
+		/*
+		 * Can't stop it, use a delayed worker to tick it
+		 * until it's open by user space
+		 */
+		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
+	}
+	return err;
+}
+
+/**
  * watchdog_register_device() - register a watchdog device
  * @wdd: watchdog device
  *
@@ -156,13 +239,24 @@ int watchdog_register_device(struct watchdog_device *wdd)
 	wdd->dev = device_create(watchdog_class, wdd->parent, devno,
 					NULL, "watchdog%d", wdd->id);
 	if (IS_ERR(wdd->dev)) {
-		watchdog_dev_unregister(wdd);
-		ida_simple_remove(&watchdog_ida, id);
 		ret = PTR_ERR(wdd->dev);
-		return ret;
+		goto dev_create_fail;
+	}
+
+	INIT_DELAYED_WORK(&wdd->work, watchdog_worker);
+
+	if (wdd->hw_max_timeout) {
+		ret = prepare_watchdog(wdd);
+		if (ret)
+			goto dev_create_fail;
 	}
 
 	return 0;
+
+dev_create_fail:
+	watchdog_dev_unregister(wdd);
+	ida_simple_remove(&watchdog_ida, id);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(watchdog_register_device);
 
@@ -181,6 +275,7 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
 	if (wdd == NULL)
 		return;
 
+	cancel_delayed_work_sync(&wdd->work);
 	devno = wdd->cdev.dev;
 	ret = watchdog_dev_unregister(wdd);
 	if (ret)
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 6aaefba..04ac68c 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -49,6 +49,14 @@ static dev_t watchdog_devt;
 /* the watchdog device behind /dev/watchdog */
 static struct watchdog_device *old_wdd;
 
+static int _watchdog_ping(struct watchdog_device *wddev)
+{
+	if (wddev->ops->ping)
+		return wddev->ops->ping(wddev);  /* ping the watchdog */
+	else
+		return wddev->ops->start(wddev); /* restart watchdog */
+}
+
 /*
  *	watchdog_ping: ping the watchdog.
  *	@wddev: the watchdog device to ping
@@ -73,10 +81,13 @@ static int watchdog_ping(struct watchdog_device *wddev)
 	if (!watchdog_active(wddev))
 		goto out_ping;
 
-	if (wddev->ops->ping)
-		err = wddev->ops->ping(wddev);  /* ping the watchdog */
-	else
-		err = wddev->ops->start(wddev); /* restart watchdog */
+	err = _watchdog_ping(wddev);
+
+	if (wddev->hw_max_timeout &&
+		wddev->timeout * HZ > wddev->hw_max_timeout) {
+		wddev->expires = jiffies + wddev->timeout * HZ;
+		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
+	}
 
 out_ping:
 	mutex_unlock(&wddev->lock);
@@ -110,6 +121,13 @@ static int watchdog_start(struct watchdog_device *wddev)
 	if (err == 0)
 		set_bit(WDOG_ACTIVE, &wddev->status);
 
+	if (wddev->hw_max_timeout &&
+		wddev->timeout * HZ > wddev->hw_max_timeout) {
+		wddev->expires = jiffies + wddev->timeout * HZ;
+		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
+	} else
+		cancel_delayed_work(&wddev->work);
+
 out_start:
 	mutex_unlock(&wddev->lock);
 	return err;
@@ -145,9 +163,21 @@ static int watchdog_stop(struct watchdog_device *wddev)
 		goto out_stop;
 	}
 
-	err = wddev->ops->stop(wddev);
-	if (err == 0)
+	if (!wddev->hw_max_timeout || watchdog_is_stoppable(wddev)) {
+		cancel_delayed_work(&wddev->work);
+		err = wddev->ops->stop(wddev);
+		if (err == 0)
+			clear_bit(WDOG_ACTIVE, &wddev->status);
+	} else {
+		/* Unstoppable watchdogs need the worker to keep them alive */
 		clear_bit(WDOG_ACTIVE, &wddev->status);
+		/*
+		 * Ping it once as we don't know how much time there
+		 * is left in the watchdog timer.
+		 */
+		err = _watchdog_ping(wddev);
+		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
+	}
 
 out_stop:
 	mutex_unlock(&wddev->lock);
@@ -194,7 +224,38 @@ out_status:
 static int watchdog_set_timeout(struct watchdog_device *wddev,
 							unsigned int timeout)
 {
-	int err;
+	int err = 0;
+
+	if (wddev->hw_max_timeout) {
+		int hw_timeout;
+		/*
+		 * We can't support too short timeout values. There is
+		 * really no maximu however, anything longer than HW
+		 * maximum will be supported by the watchdog core on
+		 * behalf of the actual HW.
+		 */
+		if (timeout < wddev->min_timeout)
+			return -EINVAL;
+
+		mutex_lock(&wddev->lock);
+		if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+			err = -ENODEV;
+			goto out_timeout;
+		}
+
+		if (timeout * HZ > wddev->hw_max_timeout)
+			schedule_delayed_work(&wddev->work,
+					wddev->hw_heartbeat);
+
+		hw_timeout = min(timeout, wddev->hw_max_timeout / HZ);
+		if (wddev->info->options & WDIOF_SETTIMEOUT)
+			err = wddev->ops->set_timeout(wddev, hw_timeout);
+
+		if (hw_timeout < timeout)
+			wddev->timeout = timeout;
+
+		goto out_timeout;
+	}
 
 	if ((wddev->ops->set_timeout == NULL) ||
 	    !(wddev->info->options & WDIOF_SETTIMEOUT))
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index 395b70e..027c99d 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -12,6 +12,7 @@
 #include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/cdev.h>
+#include <linux/workqueue.h>
 #include <uapi/linux/watchdog.h>
 
 struct watchdog_ops;
@@ -62,9 +63,13 @@ struct watchdog_ops {
  * @timeout:	The watchdog devices timeout value.
  * @min_timeout:The watchdog devices minimum timeout value.
  * @max_timeout:The watchdog devices maximum timeout value.
+ * @hw_max_timeout:The watchdog hardware maximum timeout value.
+ * @hw_heartbeat:Time interval in HW between timer pings.
  * @driver-data:Pointer to the drivers private data.
  * @lock:	Lock for watchdog core internal use only.
+ * @work:	Worker used to provide longer timeouts than hardware supports.
  * @status:	Field that contains the devices internal status bits.
+ * @hw_features:Feature bits describing how the watchdog HW works.
  *
  * The watchdog_device structure contains all information about a
  * watchdog timer device.
@@ -86,8 +91,12 @@ struct watchdog_device {
 	unsigned int timeout;
 	unsigned int min_timeout;
 	unsigned int max_timeout;
+	unsigned int hw_max_timeout; /* in jiffies */
+	unsigned int hw_heartbeat; /* in jiffies */
+	unsigned long int expires; /* for keepalive worker */
 	void *driver_data;
 	struct mutex lock;
+	struct delayed_work work;
 	unsigned long status;
 /* Bit numbers for status flags */
 #define WDOG_ACTIVE		0	/* Is the watchdog running/active */
@@ -95,6 +104,14 @@ struct watchdog_device {
 #define WDOG_ALLOW_RELEASE	2	/* Did we receive the magic char ? */
 #define WDOG_NO_WAY_OUT		3	/* Is 'nowayout' feature set ? */
 #define WDOG_UNREGISTERED	4	/* Has the device been unregistered */
+
+/* Bits describing features supported by the HW */
+	unsigned long hw_features;
+
+/* Can the watchdog be stopped and started */
+#define WDOG_HW_IS_STOPPABLE		BIT(0)
+/* Is the watchdog already running when the driver starts up */
+#define WDOG_HW_RUNNING_AT_BOOT		BIT(1)
 };
 
 #define WATCHDOG_NOWAYOUT		IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
@@ -120,6 +137,11 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
 		(t < wdd->min_timeout || t > wdd->max_timeout));
 }
 
+static inline bool watchdog_is_stoppable(struct watchdog_device *wdd)
+{
+	return wdd->hw_features & WDOG_HW_IS_STOPPABLE;
+}
+
 /* Use the following functions to manipulate watchdog driver specific data */
 static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
 {
@@ -134,6 +156,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
 /* drivers/watchdog/watchdog_core.c */
 extern int watchdog_init_timeout(struct watchdog_device *wdd,
 				  unsigned int timeout_parm, struct device *dev);
+int watchdog_init_params(struct watchdog_device *wdd, struct device *dev);
 extern int watchdog_register_device(struct watchdog_device *);
 extern void watchdog_unregister_device(struct watchdog_device *);
 
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
@ 2015-04-22 11:11   ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

There is a great deal of diversity in the watchdog hardware found on
different devices. Differen hardware have different contstraints on
them, many of the constraints that are excessively difficult for the
user space to satisfy.

One such constraint is the length of the timeout value, which in many
cases can be just a few seconds. Drivers are creating ad hoc solutions
with timers and workqueues to extend the timeout in order to give user
space more time between updates. Looking at the drivers it is clear
that this has resulted to a lot of duplicate code.

Add an extension to the watchdog kernel API that allows the driver to
describe tis HW constraints to the watchdog code. A kernel worker in
the core is then used to extend the watchdog timeout on behalf of the
user space. This allows the drivers to be simplified as core takes
over the timer extending.

Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
 drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
 include/linux/watchdog.h         |  23 +++++++++
 3 files changed, 189 insertions(+), 10 deletions(-)

diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index cec9b55..fd12489 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
 EXPORT_SYMBOL_GPL(watchdog_init_timeout);
 
 /**
+ * watchdog_init_parms() - initialize generic watchdog parameters
+ * @wdd: Watchdog device to operate
+ * @dev: Device that stores the device tree properties
+ *
+ * Initialize the generic timeout parameters. The driver needs to set
+ * hw_features bitmask from @wdd prior calling this function in order
+ * to ensure the core knows how to handle the HW.
+ *
+ * A zero is returned on success and -EINVAL for failure.
+ */
+int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
+{
+	int ret = 0;
+
+	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Max HW timeout needs to be set so that core knows when to
+	 * use a kernel worker to support longer watchdog timeouts
+	 */
+	if (!wdd->hw_max_timeout)
+		return -EINVAL;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(watchdog_init_params);
+
+static void watchdog_worker(struct work_struct *work)
+{
+	struct watchdog_device *wdd = container_of(to_delayed_work(work),
+						struct watchdog_device, work);
+	bool boot_keepalive;
+	bool active_keepalive;
+
+	mutex_lock(&wdd->lock);
+
+	boot_keepalive = !watchdog_active(wdd) &&
+		!watchdog_is_stoppable(wdd);
+
+	active_keepalive = watchdog_active(wdd) &&
+		wdd->hw_max_timeout < wdd->timeout * HZ;
+
+	if (time_after(jiffies, wdd->expires) && watchdog_active(wdd)) {
+		pr_crit("I will reset your machine !\n");
+		mutex_unlock(&wdd->lock);
+		return;
+	}
+
+	if (boot_keepalive || active_keepalive) {
+		if (wdd->ops->ping)
+			wdd->ops->ping(wdd);
+		else
+			wdd->ops->start(wdd);
+
+		schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
+	}
+
+	mutex_unlock(&wdd->lock);
+}
+
+static int prepare_watchdog(struct watchdog_device *wdd)
+{
+	int err = 0;
+
+	/* Stop the watchdog now before user space opens the device */
+	if (watchdog_is_stoppable(wdd) &&
+		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
+		err = wdd->ops->stop(wdd);
+
+	} else if (!watchdog_is_stoppable(wdd) &&
+		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
+		/*
+		 * Can't stop it, use a delayed worker to tick it
+		 * until it's open by user space
+		 */
+		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
+	}
+	return err;
+}
+
+/**
  * watchdog_register_device() - register a watchdog device
  * @wdd: watchdog device
  *
@@ -156,13 +239,24 @@ int watchdog_register_device(struct watchdog_device *wdd)
 	wdd->dev = device_create(watchdog_class, wdd->parent, devno,
 					NULL, "watchdog%d", wdd->id);
 	if (IS_ERR(wdd->dev)) {
-		watchdog_dev_unregister(wdd);
-		ida_simple_remove(&watchdog_ida, id);
 		ret = PTR_ERR(wdd->dev);
-		return ret;
+		goto dev_create_fail;
+	}
+
+	INIT_DELAYED_WORK(&wdd->work, watchdog_worker);
+
+	if (wdd->hw_max_timeout) {
+		ret = prepare_watchdog(wdd);
+		if (ret)
+			goto dev_create_fail;
 	}
 
 	return 0;
+
+dev_create_fail:
+	watchdog_dev_unregister(wdd);
+	ida_simple_remove(&watchdog_ida, id);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(watchdog_register_device);
 
@@ -181,6 +275,7 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
 	if (wdd == NULL)
 		return;
 
+	cancel_delayed_work_sync(&wdd->work);
 	devno = wdd->cdev.dev;
 	ret = watchdog_dev_unregister(wdd);
 	if (ret)
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 6aaefba..04ac68c 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -49,6 +49,14 @@ static dev_t watchdog_devt;
 /* the watchdog device behind /dev/watchdog */
 static struct watchdog_device *old_wdd;
 
+static int _watchdog_ping(struct watchdog_device *wddev)
+{
+	if (wddev->ops->ping)
+		return wddev->ops->ping(wddev);  /* ping the watchdog */
+	else
+		return wddev->ops->start(wddev); /* restart watchdog */
+}
+
 /*
  *	watchdog_ping: ping the watchdog.
  *	@wddev: the watchdog device to ping
@@ -73,10 +81,13 @@ static int watchdog_ping(struct watchdog_device *wddev)
 	if (!watchdog_active(wddev))
 		goto out_ping;
 
-	if (wddev->ops->ping)
-		err = wddev->ops->ping(wddev);  /* ping the watchdog */
-	else
-		err = wddev->ops->start(wddev); /* restart watchdog */
+	err = _watchdog_ping(wddev);
+
+	if (wddev->hw_max_timeout &&
+		wddev->timeout * HZ > wddev->hw_max_timeout) {
+		wddev->expires = jiffies + wddev->timeout * HZ;
+		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
+	}
 
 out_ping:
 	mutex_unlock(&wddev->lock);
@@ -110,6 +121,13 @@ static int watchdog_start(struct watchdog_device *wddev)
 	if (err == 0)
 		set_bit(WDOG_ACTIVE, &wddev->status);
 
+	if (wddev->hw_max_timeout &&
+		wddev->timeout * HZ > wddev->hw_max_timeout) {
+		wddev->expires = jiffies + wddev->timeout * HZ;
+		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
+	} else
+		cancel_delayed_work(&wddev->work);
+
 out_start:
 	mutex_unlock(&wddev->lock);
 	return err;
@@ -145,9 +163,21 @@ static int watchdog_stop(struct watchdog_device *wddev)
 		goto out_stop;
 	}
 
-	err = wddev->ops->stop(wddev);
-	if (err == 0)
+	if (!wddev->hw_max_timeout || watchdog_is_stoppable(wddev)) {
+		cancel_delayed_work(&wddev->work);
+		err = wddev->ops->stop(wddev);
+		if (err == 0)
+			clear_bit(WDOG_ACTIVE, &wddev->status);
+	} else {
+		/* Unstoppable watchdogs need the worker to keep them alive */
 		clear_bit(WDOG_ACTIVE, &wddev->status);
+		/*
+		 * Ping it once as we don't know how much time there
+		 * is left in the watchdog timer.
+		 */
+		err = _watchdog_ping(wddev);
+		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
+	}
 
 out_stop:
 	mutex_unlock(&wddev->lock);
@@ -194,7 +224,38 @@ out_status:
 static int watchdog_set_timeout(struct watchdog_device *wddev,
 							unsigned int timeout)
 {
-	int err;
+	int err = 0;
+
+	if (wddev->hw_max_timeout) {
+		int hw_timeout;
+		/*
+		 * We can't support too short timeout values. There is
+		 * really no maximu however, anything longer than HW
+		 * maximum will be supported by the watchdog core on
+		 * behalf of the actual HW.
+		 */
+		if (timeout < wddev->min_timeout)
+			return -EINVAL;
+
+		mutex_lock(&wddev->lock);
+		if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
+			err = -ENODEV;
+			goto out_timeout;
+		}
+
+		if (timeout * HZ > wddev->hw_max_timeout)
+			schedule_delayed_work(&wddev->work,
+					wddev->hw_heartbeat);
+
+		hw_timeout = min(timeout, wddev->hw_max_timeout / HZ);
+		if (wddev->info->options & WDIOF_SETTIMEOUT)
+			err = wddev->ops->set_timeout(wddev, hw_timeout);
+
+		if (hw_timeout < timeout)
+			wddev->timeout = timeout;
+
+		goto out_timeout;
+	}
 
 	if ((wddev->ops->set_timeout == NULL) ||
 	    !(wddev->info->options & WDIOF_SETTIMEOUT))
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index 395b70e..027c99d 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -12,6 +12,7 @@
 #include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/cdev.h>
+#include <linux/workqueue.h>
 #include <uapi/linux/watchdog.h>
 
 struct watchdog_ops;
@@ -62,9 +63,13 @@ struct watchdog_ops {
  * @timeout:	The watchdog devices timeout value.
  * @min_timeout:The watchdog devices minimum timeout value.
  * @max_timeout:The watchdog devices maximum timeout value.
+ * @hw_max_timeout:The watchdog hardware maximum timeout value.
+ * @hw_heartbeat:Time interval in HW between timer pings.
  * @driver-data:Pointer to the drivers private data.
  * @lock:	Lock for watchdog core internal use only.
+ * @work:	Worker used to provide longer timeouts than hardware supports.
  * @status:	Field that contains the devices internal status bits.
+ * @hw_features:Feature bits describing how the watchdog HW works.
  *
  * The watchdog_device structure contains all information about a
  * watchdog timer device.
@@ -86,8 +91,12 @@ struct watchdog_device {
 	unsigned int timeout;
 	unsigned int min_timeout;
 	unsigned int max_timeout;
+	unsigned int hw_max_timeout; /* in jiffies */
+	unsigned int hw_heartbeat; /* in jiffies */
+	unsigned long int expires; /* for keepalive worker */
 	void *driver_data;
 	struct mutex lock;
+	struct delayed_work work;
 	unsigned long status;
 /* Bit numbers for status flags */
 #define WDOG_ACTIVE		0	/* Is the watchdog running/active */
@@ -95,6 +104,14 @@ struct watchdog_device {
 #define WDOG_ALLOW_RELEASE	2	/* Did we receive the magic char ? */
 #define WDOG_NO_WAY_OUT		3	/* Is 'nowayout' feature set ? */
 #define WDOG_UNREGISTERED	4	/* Has the device been unregistered */
+
+/* Bits describing features supported by the HW */
+	unsigned long hw_features;
+
+/* Can the watchdog be stopped and started */
+#define WDOG_HW_IS_STOPPABLE		BIT(0)
+/* Is the watchdog already running when the driver starts up */
+#define WDOG_HW_RUNNING_AT_BOOT		BIT(1)
 };
 
 #define WATCHDOG_NOWAYOUT		IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
@@ -120,6 +137,11 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
 		(t < wdd->min_timeout || t > wdd->max_timeout));
 }
 
+static inline bool watchdog_is_stoppable(struct watchdog_device *wdd)
+{
+	return wdd->hw_features & WDOG_HW_IS_STOPPABLE;
+}
+
 /* Use the following functions to manipulate watchdog driver specific data */
 static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
 {
@@ -134,6 +156,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
 /* drivers/watchdog/watchdog_core.c */
 extern int watchdog_init_timeout(struct watchdog_device *wdd,
 				  unsigned int timeout_parm, struct device *dev);
+int watchdog_init_params(struct watchdog_device *wdd, struct device *dev);
 extern int watchdog_register_device(struct watchdog_device *);
 extern void watchdog_unregister_device(struct watchdog_device *);
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 2/8] watchdog: Allow watchdog to reset device at early boot
  2015-04-22 11:11 ` Timo Kokkonen
@ 2015-04-22 11:11   ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni
  Cc: Wenyou.Yang, Timo Kokkonen

Historically the watchdogs have always been stopped before user space
opens and takes over the device. This is not good on many production
systems where any crash, in kernel or user space, must always result
in a device reset.

Add a new early_timeout_sec parameter to the watchdog that gives user
space certain amount of time to set up itself and take over the
watchdog. Until this timeout has been reached the watchdog core takes
care of petting the watchdog HW. If there is any crash in kernel or
user space, reboot is guaranteed as watchdog hardware is never
stopped.

There is also mode of supplying zero seconds for the early_timeout_sec
parameter. In this mode the worker is not scheduled, so the watchdog
timer is not touched nor is the HW petted until user space takes over
it.

Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/watchdog_core.c | 50 ++++++++++++++++++++++++++++++----------
 drivers/watchdog/watchdog_dev.c  |  4 ++++
 include/linux/watchdog.h         |  1 +
 3 files changed, 43 insertions(+), 12 deletions(-)

diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index fd12489..c18b517 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -111,12 +111,18 @@ EXPORT_SYMBOL_GPL(watchdog_init_timeout);
  */
 int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
 {
+	unsigned int t = 0;
 	int ret = 0;
 
 	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
 	if (ret < 0)
 		return ret;
 
+	if (!of_property_read_u32(dev->of_node, "early-timeout-sec", &t))
+		wdd->early_timeout_sec = t;
+	else
+		wdd->early_timeout_sec = -1;
+
 	/*
 	 * Max HW timeout needs to be set so that core knows when to
 	 * use a kernel worker to support longer watchdog timeouts
@@ -134,11 +140,16 @@ static void watchdog_worker(struct work_struct *work)
 						struct watchdog_device, work);
 	bool boot_keepalive;
 	bool active_keepalive;
+	bool early_timeout_expired;
 
 	mutex_lock(&wdd->lock);
 
-	boot_keepalive = !watchdog_active(wdd) &&
-		!watchdog_is_stoppable(wdd);
+	early_timeout_expired  = !watchdog_active(wdd) &&
+		wdd->early_timeout_sec >= 0 &&
+		time_after(jiffies, wdd->expires);
+
+	boot_keepalive = (!watchdog_active(wdd) &&
+			!watchdog_is_stoppable(wdd)) || !early_timeout_expired;
 
 	active_keepalive = watchdog_active(wdd) &&
 		wdd->hw_max_timeout < wdd->timeout * HZ;
@@ -165,18 +176,33 @@ static int prepare_watchdog(struct watchdog_device *wdd)
 {
 	int err = 0;
 
-	/* Stop the watchdog now before user space opens the device */
-	if (watchdog_is_stoppable(wdd) &&
-		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
-		err = wdd->ops->stop(wdd);
-
-	} else if (!watchdog_is_stoppable(wdd) &&
-		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
+	if (wdd->early_timeout_sec >= 0) {
 		/*
-		 * Can't stop it, use a delayed worker to tick it
-		 * until it's open by user space
+		 * early timeout, if set, ensures that watchdog will
+		 * reset the device unless user space opens the
+		 * watchdog device within the given interval.
 		 */
-		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
+		if (!(wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT))
+			wdd->ops->start(wdd);
+
+		if (wdd->early_timeout_sec > 0) {
+			wdd->expires = jiffies + wdd->early_timeout_sec * HZ;
+			schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
+		}
+	} else {
+		/* Stop the watchdog now before user space opens the device */
+		if (watchdog_is_stoppable(wdd) &&
+			wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
+			err = wdd->ops->stop(wdd);
+
+		} else if (!watchdog_is_stoppable(wdd) &&
+			wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
+			/*
+			 * Can't stop it, use a delayed worker to tick it
+			 * until it's open by user space
+			 */
+			schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
+		}
 	}
 	return err;
 }
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 04ac68c..2f623b4 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -128,6 +128,10 @@ static int watchdog_start(struct watchdog_device *wddev)
 	} else
 		cancel_delayed_work(&wddev->work);
 
+	/* Once we open the device, early timeout can be disabled */
+	if (wddev->early_timeout_sec >= 0)
+		wddev->early_timeout_sec = -1;
+
 out_start:
 	mutex_unlock(&wddev->lock);
 	return err;
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index 027c99d..a9d2598 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -94,6 +94,7 @@ struct watchdog_device {
 	unsigned int hw_max_timeout; /* in jiffies */
 	unsigned int hw_heartbeat; /* in jiffies */
 	unsigned long int expires; /* for keepalive worker */
+	int early_timeout_sec;
 	void *driver_data;
 	struct mutex lock;
 	struct delayed_work work;
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 2/8] watchdog: Allow watchdog to reset device at early boot
@ 2015-04-22 11:11   ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

Historically the watchdogs have always been stopped before user space
opens and takes over the device. This is not good on many production
systems where any crash, in kernel or user space, must always result
in a device reset.

Add a new early_timeout_sec parameter to the watchdog that gives user
space certain amount of time to set up itself and take over the
watchdog. Until this timeout has been reached the watchdog core takes
care of petting the watchdog HW. If there is any crash in kernel or
user space, reboot is guaranteed as watchdog hardware is never
stopped.

There is also mode of supplying zero seconds for the early_timeout_sec
parameter. In this mode the worker is not scheduled, so the watchdog
timer is not touched nor is the HW petted until user space takes over
it.

Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/watchdog_core.c | 50 ++++++++++++++++++++++++++++++----------
 drivers/watchdog/watchdog_dev.c  |  4 ++++
 include/linux/watchdog.h         |  1 +
 3 files changed, 43 insertions(+), 12 deletions(-)

diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index fd12489..c18b517 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -111,12 +111,18 @@ EXPORT_SYMBOL_GPL(watchdog_init_timeout);
  */
 int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
 {
+	unsigned int t = 0;
 	int ret = 0;
 
 	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
 	if (ret < 0)
 		return ret;
 
+	if (!of_property_read_u32(dev->of_node, "early-timeout-sec", &t))
+		wdd->early_timeout_sec = t;
+	else
+		wdd->early_timeout_sec = -1;
+
 	/*
 	 * Max HW timeout needs to be set so that core knows when to
 	 * use a kernel worker to support longer watchdog timeouts
@@ -134,11 +140,16 @@ static void watchdog_worker(struct work_struct *work)
 						struct watchdog_device, work);
 	bool boot_keepalive;
 	bool active_keepalive;
+	bool early_timeout_expired;
 
 	mutex_lock(&wdd->lock);
 
-	boot_keepalive = !watchdog_active(wdd) &&
-		!watchdog_is_stoppable(wdd);
+	early_timeout_expired  = !watchdog_active(wdd) &&
+		wdd->early_timeout_sec >= 0 &&
+		time_after(jiffies, wdd->expires);
+
+	boot_keepalive = (!watchdog_active(wdd) &&
+			!watchdog_is_stoppable(wdd)) || !early_timeout_expired;
 
 	active_keepalive = watchdog_active(wdd) &&
 		wdd->hw_max_timeout < wdd->timeout * HZ;
@@ -165,18 +176,33 @@ static int prepare_watchdog(struct watchdog_device *wdd)
 {
 	int err = 0;
 
-	/* Stop the watchdog now before user space opens the device */
-	if (watchdog_is_stoppable(wdd) &&
-		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
-		err = wdd->ops->stop(wdd);
-
-	} else if (!watchdog_is_stoppable(wdd) &&
-		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
+	if (wdd->early_timeout_sec >= 0) {
 		/*
-		 * Can't stop it, use a delayed worker to tick it
-		 * until it's open by user space
+		 * early timeout, if set, ensures that watchdog will
+		 * reset the device unless user space opens the
+		 * watchdog device within the given interval.
 		 */
-		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
+		if (!(wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT))
+			wdd->ops->start(wdd);
+
+		if (wdd->early_timeout_sec > 0) {
+			wdd->expires = jiffies + wdd->early_timeout_sec * HZ;
+			schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
+		}
+	} else {
+		/* Stop the watchdog now before user space opens the device */
+		if (watchdog_is_stoppable(wdd) &&
+			wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
+			err = wdd->ops->stop(wdd);
+
+		} else if (!watchdog_is_stoppable(wdd) &&
+			wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
+			/*
+			 * Can't stop it, use a delayed worker to tick it
+			 * until it's open by user space
+			 */
+			schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
+		}
 	}
 	return err;
 }
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 04ac68c..2f623b4 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -128,6 +128,10 @@ static int watchdog_start(struct watchdog_device *wddev)
 	} else
 		cancel_delayed_work(&wddev->work);
 
+	/* Once we open the device, early timeout can be disabled */
+	if (wddev->early_timeout_sec >= 0)
+		wddev->early_timeout_sec = -1;
+
 out_start:
 	mutex_unlock(&wddev->lock);
 	return err;
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index 027c99d..a9d2598 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -94,6 +94,7 @@ struct watchdog_device {
 	unsigned int hw_max_timeout; /* in jiffies */
 	unsigned int hw_heartbeat; /* in jiffies */
 	unsigned long int expires; /* for keepalive worker */
+	int early_timeout_sec;
 	void *driver_data;
 	struct mutex lock;
 	struct delayed_work work;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 3/8] devicetree: Document generic watchdog properties
  2015-04-22 11:11 ` Timo Kokkonen
@ 2015-04-22 11:11   ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni
  Cc: Wenyou.Yang, Timo Kokkonen

There is no documentation for the watchdog properties that are common
among most of the watchdog drivers. Add document where these generic
properties can be described and told how they should be used in
drivers.

Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 .../devicetree/bindings/watchdog/watchdog.txt        | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/watchdog.txt

diff --git a/Documentation/devicetree/bindings/watchdog/watchdog.txt b/Documentation/devicetree/bindings/watchdog/watchdog.txt
new file mode 100644
index 0000000..3781406
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/watchdog.txt
@@ -0,0 +1,20 @@
+These properties are common among most watchdog drivers. Any driver
+that requires the functionality listed below should implement them
+using these definitions.
+
+Optional properties:
+- timeout-sec: Contains the watchdog timeout in seconds.
+- early-timeout-sec: If present, specify the timeout in seconds for
+  how long it can take for the watchdog daemon to take over the
+  watchdog device. If driver supports this property it must ensure the
+  watchdog hardware is running during this period and a watchdog reset
+  must occur if user space fails to open the device in time. If left
+  zero, the driver only needs to guarantee the watchdog is not
+  stopped or is started during driver init.
+
+Example:
+
+watchdog {
+	 timeout-sec = <60>;
+	 early-timeout-sec = <120>;
+};
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 3/8] devicetree: Document generic watchdog properties
@ 2015-04-22 11:11   ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

There is no documentation for the watchdog properties that are common
among most of the watchdog drivers. Add document where these generic
properties can be described and told how they should be used in
drivers.

Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 .../devicetree/bindings/watchdog/watchdog.txt        | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/watchdog.txt

diff --git a/Documentation/devicetree/bindings/watchdog/watchdog.txt b/Documentation/devicetree/bindings/watchdog/watchdog.txt
new file mode 100644
index 0000000..3781406
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/watchdog.txt
@@ -0,0 +1,20 @@
+These properties are common among most watchdog drivers. Any driver
+that requires the functionality listed below should implement them
+using these definitions.
+
+Optional properties:
+- timeout-sec: Contains the watchdog timeout in seconds.
+- early-timeout-sec: If present, specify the timeout in seconds for
+  how long it can take for the watchdog daemon to take over the
+  watchdog device. If driver supports this property it must ensure the
+  watchdog hardware is running during this period and a watchdog reset
+  must occur if user space fails to open the device in time. If left
+  zero, the driver only needs to guarantee the watchdog is not
+  stopped or is started during driver init.
+
+Example:
+
+watchdog {
+	 timeout-sec = <60>;
+	 early-timeout-sec = <120>;
+};
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 4/8] Documentation/watchdog: watchdog-test.c: Add support for changing timeout
  2015-04-22 11:11 ` Timo Kokkonen
@ 2015-04-22 11:11   ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni
  Cc: Wenyou.Yang, Timo Kokkonen

It might be useful for the random watchdog developer to also test
changing the watchdog timeout. Therefore, change the test application
to also support changing timeout.

Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 Documentation/watchdog/src/watchdog-test.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/watchdog/src/watchdog-test.c b/Documentation/watchdog/src/watchdog-test.c
index 3da8229..fb20d70 100644
--- a/Documentation/watchdog/src/watchdog-test.c
+++ b/Documentation/watchdog/src/watchdog-test.c
@@ -63,6 +63,12 @@ int main(int argc, char *argv[])
 	    fprintf(stderr, "Watchdog card enabled.\n");
 	    fflush(stderr);
 	    goto end;
+	} else if (!strncasecmp(argv[1], "-t", 2)) {
+	    flags = atoi(argv[2]);
+	    ioctl(fd, WDIOC_SETTIMEOUT, &flags);
+	    fprintf(stderr, "Watchdog timeout set to %d seconds.\n", flags);
+	    fflush(stderr);
+	    goto end;
 	} else {
 	    fprintf(stderr, "-d to disable, -e to enable.\n");
 	    fprintf(stderr, "run by itself to tick the card.\n");
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 4/8] Documentation/watchdog: watchdog-test.c: Add support for changing timeout
@ 2015-04-22 11:11   ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

It might be useful for the random watchdog developer to also test
changing the watchdog timeout. Therefore, change the test application
to also support changing timeout.

Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 Documentation/watchdog/src/watchdog-test.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/watchdog/src/watchdog-test.c b/Documentation/watchdog/src/watchdog-test.c
index 3da8229..fb20d70 100644
--- a/Documentation/watchdog/src/watchdog-test.c
+++ b/Documentation/watchdog/src/watchdog-test.c
@@ -63,6 +63,12 @@ int main(int argc, char *argv[])
 	    fprintf(stderr, "Watchdog card enabled.\n");
 	    fflush(stderr);
 	    goto end;
+	} else if (!strncasecmp(argv[1], "-t", 2)) {
+	    flags = atoi(argv[2]);
+	    ioctl(fd, WDIOC_SETTIMEOUT, &flags);
+	    fprintf(stderr, "Watchdog timeout set to %d seconds.\n", flags);
+	    fflush(stderr);
+	    goto end;
 	} else {
 	    fprintf(stderr, "-d to disable, -e to enable.\n");
 	    fprintf(stderr, "run by itself to tick the card.\n");
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 5/8] watchdog: at91sam9_wdt: Convert to use new watchdog core extensions
  2015-04-22 11:11 ` Timo Kokkonen
@ 2015-04-22 11:11   ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni
  Cc: Wenyou.Yang, Timo Kokkonen

Fill in the new watchdog core parameters and call
watchdog_init_params() to let core know we are ready to support the
new core API extensions. This allows the ad hoc timer code to be
removed from the driver as the watchdog core takes care of petting the
driver as needed.

Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/at91sam9_wdt.c | 62 +++++++++++------------------------------
 1 file changed, 16 insertions(+), 46 deletions(-)

diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index 1443b3c..053b5b6 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -29,7 +29,6 @@
 #include <linux/types.h>
 #include <linux/watchdog.h>
 #include <linux/jiffies.h>
-#include <linux/timer.h>
 #include <linux/bitops.h>
 #include <linux/uaccess.h>
 #include <linux/of.h>
@@ -83,11 +82,8 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
 struct at91wdt {
 	struct watchdog_device wdd;
 	void __iomem *base;
-	unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
-	struct timer_list timer;	/* The timer that pings the watchdog */
 	u32 mr;
 	u32 mr_mask;
-	unsigned long heartbeat;	/* WDT heartbeat in jiffies */
 	bool nowayout;
 	unsigned int irq;
 };
@@ -115,26 +111,11 @@ static inline void at91_wdt_reset(struct at91wdt *wdt)
 	wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
 }
 
-/*
- * Timer tick
- */
-static void at91_ping(unsigned long data)
-{
-	struct at91wdt *wdt = (struct at91wdt *)data;
-	if (time_before(jiffies, wdt->next_heartbeat) ||
-	    !watchdog_active(&wdt->wdd)) {
-		at91_wdt_reset(wdt);
-		mod_timer(&wdt->timer, jiffies + wdt->heartbeat);
-	} else {
-		pr_crit("I will reset your machine !\n");
-	}
-}
-
 static int at91_wdt_start(struct watchdog_device *wdd)
 {
 	struct at91wdt *wdt = to_wdt(wdd);
-	/* calculate when the next userspace timeout will be */
-	wdt->next_heartbeat = jiffies + wdd->timeout * HZ;
+
+	at91_wdt_reset(wdt);
 	return 0;
 }
 
@@ -146,7 +127,11 @@ static int at91_wdt_stop(struct watchdog_device *wdd)
 
 static int at91_wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout)
 {
-	wdd->timeout = new_timeout;
+	/*
+	 * The watchdog hardware can't be reconfigured, only one
+	 * timeout is supported by the HW.
+	 */
+	wdd->timeout = wdd->hw_max_timeout / HZ;
 	return at91_wdt_start(wdd);
 }
 
@@ -196,11 +181,11 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt)
 	 * it at the min_heartbeat period.
 	 */
 	if ((max_heartbeat / 4) >= min_heartbeat)
-		wdt->heartbeat = max_heartbeat / 4;
+		wdt->wdd.hw_heartbeat = max_heartbeat / 4;
 	else if ((max_heartbeat / 2) >= min_heartbeat)
-		wdt->heartbeat = max_heartbeat / 2;
+		wdt->wdd.hw_heartbeat = max_heartbeat / 2;
 	else
-		wdt->heartbeat = min_heartbeat;
+		wdt->wdd.hw_heartbeat = min_heartbeat;
 
 	if (max_heartbeat < min_heartbeat + 4)
 		dev_warn(dev,
@@ -220,32 +205,15 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt)
 			 "watchdog already configured differently (mr = %x expecting %x)\n",
 			 tmp & wdt->mr_mask, wdt->mr & wdt->mr_mask);
 
-	setup_timer(&wdt->timer, at91_ping, (unsigned long)wdt);
-
-	/*
-	 * Use min_heartbeat the first time to avoid spurious watchdog reset:
-	 * we don't know for how long the watchdog counter is running, and
-	 *  - resetting it right now might trigger a watchdog fault reset
-	 *  - waiting for heartbeat time might lead to a watchdog timeout
-	 *    reset
-	 */
-	mod_timer(&wdt->timer, jiffies + min_heartbeat);
+	wdt->wdd.hw_features = WDOG_HW_RUNNING_AT_BOOT;
+	watchdog_init_params(&wdt->wdd, dev);
 
-	/* Try to set timeout from device tree first */
-	if (watchdog_init_timeout(&wdt->wdd, 0, dev))
-		watchdog_init_timeout(&wdt->wdd, heartbeat, dev);
 	watchdog_set_nowayout(&wdt->wdd, wdt->nowayout);
 	err = watchdog_register_device(&wdt->wdd);
 	if (err)
-		goto out_stop_timer;
-
-	wdt->next_heartbeat = jiffies + wdt->wdd.timeout * HZ;
+		return err;
 
 	return 0;
-
-out_stop_timer:
-	del_timer(&wdt->timer);
-	return err;
 }
 
 /* ......................................................................... */
@@ -287,6 +255,8 @@ static int of_at91wdt_init(struct device_node *np, struct at91wdt *wdt)
 		}
 	}
 
+	wdt->wdd.hw_max_timeout = max * HZ;
+
 	min = secs_to_ticks(min);
 	max = secs_to_ticks(max);
 
@@ -346,6 +316,7 @@ static int __init at91wdt_probe(struct platform_device *pdev)
 	wdt->wdd.timeout = WDT_HEARTBEAT;
 	wdt->wdd.min_timeout = 1;
 	wdt->wdd.max_timeout = 0xFFFF;
+	wdt->wdd.hw_max_timeout = WDT_COUNTER_MAX_SECS * HZ;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	wdt->base = devm_ioremap_resource(&pdev->dev, r);
@@ -376,7 +347,6 @@ static int __exit at91wdt_remove(struct platform_device *pdev)
 	watchdog_unregister_device(&wdt->wdd);
 
 	pr_warn("I quit now, hardware will probably reboot!\n");
-	del_timer(&wdt->timer);
 
 	return 0;
 }
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 5/8] watchdog: at91sam9_wdt: Convert to use new watchdog core extensions
@ 2015-04-22 11:11   ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

Fill in the new watchdog core parameters and call
watchdog_init_params() to let core know we are ready to support the
new core API extensions. This allows the ad hoc timer code to be
removed from the driver as the watchdog core takes care of petting the
driver as needed.

Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/at91sam9_wdt.c | 62 +++++++++++------------------------------
 1 file changed, 16 insertions(+), 46 deletions(-)

diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
index 1443b3c..053b5b6 100644
--- a/drivers/watchdog/at91sam9_wdt.c
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -29,7 +29,6 @@
 #include <linux/types.h>
 #include <linux/watchdog.h>
 #include <linux/jiffies.h>
-#include <linux/timer.h>
 #include <linux/bitops.h>
 #include <linux/uaccess.h>
 #include <linux/of.h>
@@ -83,11 +82,8 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
 struct at91wdt {
 	struct watchdog_device wdd;
 	void __iomem *base;
-	unsigned long next_heartbeat;	/* the next_heartbeat for the timer */
-	struct timer_list timer;	/* The timer that pings the watchdog */
 	u32 mr;
 	u32 mr_mask;
-	unsigned long heartbeat;	/* WDT heartbeat in jiffies */
 	bool nowayout;
 	unsigned int irq;
 };
@@ -115,26 +111,11 @@ static inline void at91_wdt_reset(struct at91wdt *wdt)
 	wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
 }
 
-/*
- * Timer tick
- */
-static void at91_ping(unsigned long data)
-{
-	struct at91wdt *wdt = (struct at91wdt *)data;
-	if (time_before(jiffies, wdt->next_heartbeat) ||
-	    !watchdog_active(&wdt->wdd)) {
-		at91_wdt_reset(wdt);
-		mod_timer(&wdt->timer, jiffies + wdt->heartbeat);
-	} else {
-		pr_crit("I will reset your machine !\n");
-	}
-}
-
 static int at91_wdt_start(struct watchdog_device *wdd)
 {
 	struct at91wdt *wdt = to_wdt(wdd);
-	/* calculate when the next userspace timeout will be */
-	wdt->next_heartbeat = jiffies + wdd->timeout * HZ;
+
+	at91_wdt_reset(wdt);
 	return 0;
 }
 
@@ -146,7 +127,11 @@ static int at91_wdt_stop(struct watchdog_device *wdd)
 
 static int at91_wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout)
 {
-	wdd->timeout = new_timeout;
+	/*
+	 * The watchdog hardware can't be reconfigured, only one
+	 * timeout is supported by the HW.
+	 */
+	wdd->timeout = wdd->hw_max_timeout / HZ;
 	return at91_wdt_start(wdd);
 }
 
@@ -196,11 +181,11 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt)
 	 * it at the min_heartbeat period.
 	 */
 	if ((max_heartbeat / 4) >= min_heartbeat)
-		wdt->heartbeat = max_heartbeat / 4;
+		wdt->wdd.hw_heartbeat = max_heartbeat / 4;
 	else if ((max_heartbeat / 2) >= min_heartbeat)
-		wdt->heartbeat = max_heartbeat / 2;
+		wdt->wdd.hw_heartbeat = max_heartbeat / 2;
 	else
-		wdt->heartbeat = min_heartbeat;
+		wdt->wdd.hw_heartbeat = min_heartbeat;
 
 	if (max_heartbeat < min_heartbeat + 4)
 		dev_warn(dev,
@@ -220,32 +205,15 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt)
 			 "watchdog already configured differently (mr = %x expecting %x)\n",
 			 tmp & wdt->mr_mask, wdt->mr & wdt->mr_mask);
 
-	setup_timer(&wdt->timer, at91_ping, (unsigned long)wdt);
-
-	/*
-	 * Use min_heartbeat the first time to avoid spurious watchdog reset:
-	 * we don't know for how long the watchdog counter is running, and
-	 *  - resetting it right now might trigger a watchdog fault reset
-	 *  - waiting for heartbeat time might lead to a watchdog timeout
-	 *    reset
-	 */
-	mod_timer(&wdt->timer, jiffies + min_heartbeat);
+	wdt->wdd.hw_features = WDOG_HW_RUNNING_AT_BOOT;
+	watchdog_init_params(&wdt->wdd, dev);
 
-	/* Try to set timeout from device tree first */
-	if (watchdog_init_timeout(&wdt->wdd, 0, dev))
-		watchdog_init_timeout(&wdt->wdd, heartbeat, dev);
 	watchdog_set_nowayout(&wdt->wdd, wdt->nowayout);
 	err = watchdog_register_device(&wdt->wdd);
 	if (err)
-		goto out_stop_timer;
-
-	wdt->next_heartbeat = jiffies + wdt->wdd.timeout * HZ;
+		return err;
 
 	return 0;
-
-out_stop_timer:
-	del_timer(&wdt->timer);
-	return err;
 }
 
 /* ......................................................................... */
@@ -287,6 +255,8 @@ static int of_at91wdt_init(struct device_node *np, struct at91wdt *wdt)
 		}
 	}
 
+	wdt->wdd.hw_max_timeout = max * HZ;
+
 	min = secs_to_ticks(min);
 	max = secs_to_ticks(max);
 
@@ -346,6 +316,7 @@ static int __init at91wdt_probe(struct platform_device *pdev)
 	wdt->wdd.timeout = WDT_HEARTBEAT;
 	wdt->wdd.min_timeout = 1;
 	wdt->wdd.max_timeout = 0xFFFF;
+	wdt->wdd.hw_max_timeout = WDT_COUNTER_MAX_SECS * HZ;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	wdt->base = devm_ioremap_resource(&pdev->dev, r);
@@ -376,7 +347,6 @@ static int __exit at91wdt_remove(struct platform_device *pdev)
 	watchdog_unregister_device(&wdt->wdd);
 
 	pr_warn("I quit now, hardware will probably reboot!\n");
-	del_timer(&wdt->timer);
 
 	return 0;
 }
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 6/8] watchdog: imx2_wdt: Convert to use new core extensions
  2015-04-22 11:11 ` Timo Kokkonen
@ 2015-04-22 11:11   ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni
  Cc: Wenyou.Yang, Timo Kokkonen

Fill in the HW capabilities in watchdog_device structure and call
watchdgog_init_params() to let watchdog core to init itself
properly. The watchdog core can then ping stopped watchdog and the
timer code in the driver can be removed.

Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/imx2_wdt.c | 43 ++++++++++---------------------------------
 1 file changed, 10 insertions(+), 33 deletions(-)

diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index 5e6d808..8be8006 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -62,7 +62,6 @@
 struct imx2_wdt_device {
 	struct clk *clk;
 	struct regmap *regmap;
-	struct timer_list timer;	/* Pings the watchdog when closed */
 	struct watchdog_device wdog;
 	struct notifier_block restart_handler;
 };
@@ -151,21 +150,13 @@ static int imx2_wdt_ping(struct watchdog_device *wdog)
 	return 0;
 }
 
-static void imx2_wdt_timer_ping(unsigned long arg)
-{
-	struct watchdog_device *wdog = (struct watchdog_device *)arg;
-	struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
-
-	/* ping it every wdog->timeout / 2 seconds to prevent reboot */
-	imx2_wdt_ping(wdog);
-	mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2);
-}
-
 static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
 				unsigned int new_timeout)
 {
 	struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
 
+	wdog->hw_heartbeat = new_timeout * HZ / 2;
+	wdog->timeout = new_timeout;
 	regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
 			   WDOG_SEC_TO_COUNT(new_timeout));
 	return 0;
@@ -176,8 +167,6 @@ static int imx2_wdt_start(struct watchdog_device *wdog)
 	struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
 
 	if (imx2_wdt_is_running(wdev)) {
-		/* delete the timer that pings the watchdog after close */
-		del_timer_sync(&wdev->timer);
 		imx2_wdt_set_timeout(wdog, wdog->timeout);
 	} else
 		imx2_wdt_setup(wdog);
@@ -191,7 +180,7 @@ static int imx2_wdt_stop(struct watchdog_device *wdog)
 	 * We don't need a clk_disable, it cannot be disabled once started.
 	 * We use a timer to ping the watchdog while /dev/watchdog is closed
 	 */
-	imx2_wdt_timer_ping((unsigned long)wdog);
+	imx2_wdt_ping(wdog);
 	return 0;
 }
 
@@ -201,7 +190,7 @@ static inline void imx2_wdt_ping_if_active(struct watchdog_device *wdog)
 
 	if (imx2_wdt_is_running(wdev)) {
 		imx2_wdt_set_timeout(wdog, wdog->timeout);
-		imx2_wdt_timer_ping((unsigned long)wdog);
+		imx2_wdt_ping(wdog);
 	}
 }
 
@@ -256,6 +245,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
 	wdog->ops		= &imx2_wdt_ops;
 	wdog->min_timeout	= 1;
 	wdog->max_timeout	= IMX2_WDT_MAX_TIME;
+	wdog->hw_max_timeout	= wdog->max_timeout * HZ;
 
 	clk_prepare_enable(wdev->clk);
 
@@ -267,12 +257,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
 		dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n",
 			 timeout, wdog->timeout);
 
+	wdog->timeout = timeout;
+	wdog->hw_heartbeat = timeout * HZ / 2;
 	platform_set_drvdata(pdev, wdog);
 	watchdog_set_drvdata(wdog, wdev);
 	watchdog_set_nowayout(wdog, nowayout);
-	watchdog_init_timeout(wdog, timeout, &pdev->dev);
-
-	setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog);
+	watchdog_init_params(wdog, &pdev->dev);
 
 	imx2_wdt_ping_if_active(wdog);
 
@@ -311,7 +301,6 @@ static int __exit imx2_wdt_remove(struct platform_device *pdev)
 	watchdog_unregister_device(wdog);
 
 	if (imx2_wdt_is_running(wdev)) {
-		del_timer_sync(&wdev->timer);
 		imx2_wdt_ping(wdog);
 		dev_crit(&pdev->dev, "Device removed: Expect reboot!\n");
 	}
@@ -325,10 +314,9 @@ static void imx2_wdt_shutdown(struct platform_device *pdev)
 
 	if (imx2_wdt_is_running(wdev)) {
 		/*
-		 * We are running, we need to delete the timer but will
-		 * give max timeout before reboot will take place
+		 * We are running, give max timeout before reboot will
+		 * take place
 		 */
-		del_timer_sync(&wdev->timer);
 		imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
 		imx2_wdt_ping(wdog);
 		dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n");
@@ -346,10 +334,6 @@ static int imx2_wdt_suspend(struct device *dev)
 	if (imx2_wdt_is_running(wdev)) {
 		imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
 		imx2_wdt_ping(wdog);
-
-		/* The watchdog is not active */
-		if (!watchdog_active(wdog))
-			del_timer_sync(&wdev->timer);
 	}
 
 	clk_disable_unprepare(wdev->clk);
@@ -378,13 +362,6 @@ static int imx2_wdt_resume(struct device *dev)
 		/* Resuming from non-deep sleep state. */
 		imx2_wdt_set_timeout(wdog, wdog->timeout);
 		imx2_wdt_ping(wdog);
-		/*
-		 * But the watchdog is not active, then start
-		 * the timer again.
-		 */
-		if (!watchdog_active(wdog))
-			mod_timer(&wdev->timer,
-				  jiffies + wdog->timeout * HZ / 2);
 	}
 
 	return 0;
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 6/8] watchdog: imx2_wdt: Convert to use new core extensions
@ 2015-04-22 11:11   ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

Fill in the HW capabilities in watchdog_device structure and call
watchdgog_init_params() to let watchdog core to init itself
properly. The watchdog core can then ping stopped watchdog and the
timer code in the driver can be removed.

Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/imx2_wdt.c | 43 ++++++++++---------------------------------
 1 file changed, 10 insertions(+), 33 deletions(-)

diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
index 5e6d808..8be8006 100644
--- a/drivers/watchdog/imx2_wdt.c
+++ b/drivers/watchdog/imx2_wdt.c
@@ -62,7 +62,6 @@
 struct imx2_wdt_device {
 	struct clk *clk;
 	struct regmap *regmap;
-	struct timer_list timer;	/* Pings the watchdog when closed */
 	struct watchdog_device wdog;
 	struct notifier_block restart_handler;
 };
@@ -151,21 +150,13 @@ static int imx2_wdt_ping(struct watchdog_device *wdog)
 	return 0;
 }
 
-static void imx2_wdt_timer_ping(unsigned long arg)
-{
-	struct watchdog_device *wdog = (struct watchdog_device *)arg;
-	struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
-
-	/* ping it every wdog->timeout / 2 seconds to prevent reboot */
-	imx2_wdt_ping(wdog);
-	mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2);
-}
-
 static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
 				unsigned int new_timeout)
 {
 	struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
 
+	wdog->hw_heartbeat = new_timeout * HZ / 2;
+	wdog->timeout = new_timeout;
 	regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
 			   WDOG_SEC_TO_COUNT(new_timeout));
 	return 0;
@@ -176,8 +167,6 @@ static int imx2_wdt_start(struct watchdog_device *wdog)
 	struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
 
 	if (imx2_wdt_is_running(wdev)) {
-		/* delete the timer that pings the watchdog after close */
-		del_timer_sync(&wdev->timer);
 		imx2_wdt_set_timeout(wdog, wdog->timeout);
 	} else
 		imx2_wdt_setup(wdog);
@@ -191,7 +180,7 @@ static int imx2_wdt_stop(struct watchdog_device *wdog)
 	 * We don't need a clk_disable, it cannot be disabled once started.
 	 * We use a timer to ping the watchdog while /dev/watchdog is closed
 	 */
-	imx2_wdt_timer_ping((unsigned long)wdog);
+	imx2_wdt_ping(wdog);
 	return 0;
 }
 
@@ -201,7 +190,7 @@ static inline void imx2_wdt_ping_if_active(struct watchdog_device *wdog)
 
 	if (imx2_wdt_is_running(wdev)) {
 		imx2_wdt_set_timeout(wdog, wdog->timeout);
-		imx2_wdt_timer_ping((unsigned long)wdog);
+		imx2_wdt_ping(wdog);
 	}
 }
 
@@ -256,6 +245,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
 	wdog->ops		= &imx2_wdt_ops;
 	wdog->min_timeout	= 1;
 	wdog->max_timeout	= IMX2_WDT_MAX_TIME;
+	wdog->hw_max_timeout	= wdog->max_timeout * HZ;
 
 	clk_prepare_enable(wdev->clk);
 
@@ -267,12 +257,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
 		dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n",
 			 timeout, wdog->timeout);
 
+	wdog->timeout = timeout;
+	wdog->hw_heartbeat = timeout * HZ / 2;
 	platform_set_drvdata(pdev, wdog);
 	watchdog_set_drvdata(wdog, wdev);
 	watchdog_set_nowayout(wdog, nowayout);
-	watchdog_init_timeout(wdog, timeout, &pdev->dev);
-
-	setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog);
+	watchdog_init_params(wdog, &pdev->dev);
 
 	imx2_wdt_ping_if_active(wdog);
 
@@ -311,7 +301,6 @@ static int __exit imx2_wdt_remove(struct platform_device *pdev)
 	watchdog_unregister_device(wdog);
 
 	if (imx2_wdt_is_running(wdev)) {
-		del_timer_sync(&wdev->timer);
 		imx2_wdt_ping(wdog);
 		dev_crit(&pdev->dev, "Device removed: Expect reboot!\n");
 	}
@@ -325,10 +314,9 @@ static void imx2_wdt_shutdown(struct platform_device *pdev)
 
 	if (imx2_wdt_is_running(wdev)) {
 		/*
-		 * We are running, we need to delete the timer but will
-		 * give max timeout before reboot will take place
+		 * We are running, give max timeout before reboot will
+		 * take place
 		 */
-		del_timer_sync(&wdev->timer);
 		imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
 		imx2_wdt_ping(wdog);
 		dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n");
@@ -346,10 +334,6 @@ static int imx2_wdt_suspend(struct device *dev)
 	if (imx2_wdt_is_running(wdev)) {
 		imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
 		imx2_wdt_ping(wdog);
-
-		/* The watchdog is not active */
-		if (!watchdog_active(wdog))
-			del_timer_sync(&wdev->timer);
 	}
 
 	clk_disable_unprepare(wdev->clk);
@@ -378,13 +362,6 @@ static int imx2_wdt_resume(struct device *dev)
 		/* Resuming from non-deep sleep state. */
 		imx2_wdt_set_timeout(wdog, wdog->timeout);
 		imx2_wdt_ping(wdog);
-		/*
-		 * But the watchdog is not active, then start
-		 * the timer again.
-		 */
-		if (!watchdog_active(wdog))
-			mod_timer(&wdev->timer,
-				  jiffies + wdog->timeout * HZ / 2);
 	}
 
 	return 0;
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 7/8] watchdog: omap_wdt: Fix memory leak on probe fail
  2015-04-22 11:11 ` Timo Kokkonen
@ 2015-04-22 11:11   ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni
  Cc: Wenyou.Yang, Timo Kokkonen

Structures allocated on the beginning of the probe function must be
freed in case of failure.

Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/omap_wdt.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 1e6be9e..bbaf39a 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -226,8 +226,10 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	/* reserve static register mappings */
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	wdev->base = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(wdev->base))
-		return PTR_ERR(wdev->base);
+	if (IS_ERR(wdev->base)) {
+		ret = PTR_ERR(wdev->base);
+		goto err_ioremap;
+	}
 
 	omap_wdt->info	      = &omap_wdt_info;
 	omap_wdt->ops	      = &omap_wdt_ops;
@@ -258,10 +260,8 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	omap_wdt_disable(wdev);
 
 	ret = watchdog_register_device(omap_wdt);
-	if (ret) {
-		pm_runtime_disable(wdev->dev);
-		return ret;
-	}
+	if (ret)
+		goto err_register_device;
 
 	pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
 		readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
@@ -270,6 +270,14 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	pm_runtime_put_sync(wdev->dev);
 
 	return 0;
+
+err_register_device:
+	pm_runtime_disable(wdev->dev);
+err_ioremap:
+	kfree(wdev);
+	kfree(omap_wdt);
+
+	return ret;
 }
 
 static void omap_wdt_shutdown(struct platform_device *pdev)
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 7/8] watchdog: omap_wdt: Fix memory leak on probe fail
@ 2015-04-22 11:11   ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

Structures allocated on the beginning of the probe function must be
freed in case of failure.

Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/omap_wdt.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 1e6be9e..bbaf39a 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -226,8 +226,10 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	/* reserve static register mappings */
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	wdev->base = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(wdev->base))
-		return PTR_ERR(wdev->base);
+	if (IS_ERR(wdev->base)) {
+		ret = PTR_ERR(wdev->base);
+		goto err_ioremap;
+	}
 
 	omap_wdt->info	      = &omap_wdt_info;
 	omap_wdt->ops	      = &omap_wdt_ops;
@@ -258,10 +260,8 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	omap_wdt_disable(wdev);
 
 	ret = watchdog_register_device(omap_wdt);
-	if (ret) {
-		pm_runtime_disable(wdev->dev);
-		return ret;
-	}
+	if (ret)
+		goto err_register_device;
 
 	pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
 		readl_relaxed(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
@@ -270,6 +270,14 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	pm_runtime_put_sync(wdev->dev);
 
 	return 0;
+
+err_register_device:
+	pm_runtime_disable(wdev->dev);
+err_ioremap:
+	kfree(wdev);
+	kfree(omap_wdt);
+
+	return ret;
 }
 
 static void omap_wdt_shutdown(struct platform_device *pdev)
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
  2015-04-22 11:11 ` Timo Kokkonen
@ 2015-04-22 11:11   ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni
  Cc: Wenyou.Yang, Timo Kokkonen

Use the new watchdog core extensions to let watchdog core take over
boot time watchdog behavior. The difference is that early-timeout-sec
device tree property becomes available for this driver and a running
watchdog is not stopped unless the core decides to stop it.

Omap watchdog is running by default in the boot up but bootloader
might have stopped it. Therefore we fill the WDOG_HW_RUNNING_AT_BOOT
bit depending on the actual watchdog state so that the watchdog core
can act properly.

Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/omap_wdt.c | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index bbaf39a..7164f2e 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -78,6 +78,13 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
 	/* reloaded WCRR from WLDR */
 }
 
+static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
+{
+	void __iomem *base = wdev->base;
+
+	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
+}
+
 static void omap_wdt_enable(struct omap_wdt_dev *wdev)
 {
 	void __iomem *base = wdev->base;
@@ -183,6 +190,7 @@ static int omap_wdt_set_timeout(struct watchdog_device *wdog,
 	omap_wdt_enable(wdev);
 	omap_wdt_reload(wdev);
 	wdog->timeout = timeout;
+	wdog->hw_heartbeat = timeout * HZ / 2;
 	mutex_unlock(&wdev->lock);
 
 	return 0;
@@ -235,12 +243,15 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	omap_wdt->ops	      = &omap_wdt_ops;
 	omap_wdt->min_timeout = TIMER_MARGIN_MIN;
 	omap_wdt->max_timeout = TIMER_MARGIN_MAX;
+	omap_wdt->hw_max_timeout = TIMER_MARGIN_MAX * HZ;
+	omap_wdt->hw_features = WDOG_HW_IS_STOPPABLE;
 
 	if (timer_margin >= TIMER_MARGIN_MIN &&
 	    timer_margin <= TIMER_MARGIN_MAX)
 		omap_wdt->timeout = timer_margin;
 	else
 		omap_wdt->timeout = TIMER_MARGIN_DEFAULT;
+	omap_wdt->hw_heartbeat = omap_wdt->timeout * HZ / 2;
 
 	watchdog_set_drvdata(omap_wdt, wdev);
 	watchdog_set_nowayout(omap_wdt, nowayout);
@@ -250,6 +261,18 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	pm_runtime_enable(wdev->dev);
 	pm_runtime_get_sync(wdev->dev);
 
+	if (omap_wdt_is_running(wdev))
+		omap_wdt->hw_features |= WDOG_HW_RUNNING_AT_BOOT;
+	else
+		/*
+		 * The watchdog should be stopped already by
+		 * bootloader. But unless we call disable here, the
+		 * timeout might not be set correctly on the first
+		 * start. So call disable anyway to make sure the
+		 * watchdog really is stopped properly.
+		 */
+		omap_wdt_disable(wdev);
+
 	if (pdata && pdata->read_reset_sources)
 		rs = pdata->read_reset_sources();
 	else
@@ -257,7 +280,9 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ?
 				WDIOF_CARDRESET : 0;
 
-	omap_wdt_disable(wdev);
+	ret = watchdog_init_params(omap_wdt, &pdev->dev);
+	if (ret)
+		goto err_init_params;
 
 	ret = watchdog_register_device(omap_wdt);
 	if (ret)
@@ -271,6 +296,7 @@ static int omap_wdt_probe(struct platform_device *pdev)
 
 	return 0;
 
+err_init_params:
 err_register_device:
 	pm_runtime_disable(wdev->dev);
 err_ioremap:
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
@ 2015-04-22 11:11   ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-22 11:11 UTC (permalink / raw)
  To: linux-arm-kernel

Use the new watchdog core extensions to let watchdog core take over
boot time watchdog behavior. The difference is that early-timeout-sec
device tree property becomes available for this driver and a running
watchdog is not stopped unless the core decides to stop it.

Omap watchdog is running by default in the boot up but bootloader
might have stopped it. Therefore we fill the WDOG_HW_RUNNING_AT_BOOT
bit depending on the actual watchdog state so that the watchdog core
can act properly.

Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
---
 drivers/watchdog/omap_wdt.c | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index bbaf39a..7164f2e 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -78,6 +78,13 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
 	/* reloaded WCRR from WLDR */
 }
 
+static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
+{
+	void __iomem *base = wdev->base;
+
+	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
+}
+
 static void omap_wdt_enable(struct omap_wdt_dev *wdev)
 {
 	void __iomem *base = wdev->base;
@@ -183,6 +190,7 @@ static int omap_wdt_set_timeout(struct watchdog_device *wdog,
 	omap_wdt_enable(wdev);
 	omap_wdt_reload(wdev);
 	wdog->timeout = timeout;
+	wdog->hw_heartbeat = timeout * HZ / 2;
 	mutex_unlock(&wdev->lock);
 
 	return 0;
@@ -235,12 +243,15 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	omap_wdt->ops	      = &omap_wdt_ops;
 	omap_wdt->min_timeout = TIMER_MARGIN_MIN;
 	omap_wdt->max_timeout = TIMER_MARGIN_MAX;
+	omap_wdt->hw_max_timeout = TIMER_MARGIN_MAX * HZ;
+	omap_wdt->hw_features = WDOG_HW_IS_STOPPABLE;
 
 	if (timer_margin >= TIMER_MARGIN_MIN &&
 	    timer_margin <= TIMER_MARGIN_MAX)
 		omap_wdt->timeout = timer_margin;
 	else
 		omap_wdt->timeout = TIMER_MARGIN_DEFAULT;
+	omap_wdt->hw_heartbeat = omap_wdt->timeout * HZ / 2;
 
 	watchdog_set_drvdata(omap_wdt, wdev);
 	watchdog_set_nowayout(omap_wdt, nowayout);
@@ -250,6 +261,18 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	pm_runtime_enable(wdev->dev);
 	pm_runtime_get_sync(wdev->dev);
 
+	if (omap_wdt_is_running(wdev))
+		omap_wdt->hw_features |= WDOG_HW_RUNNING_AT_BOOT;
+	else
+		/*
+		 * The watchdog should be stopped already by
+		 * bootloader. But unless we call disable here, the
+		 * timeout might not be set correctly on the first
+		 * start. So call disable anyway to make sure the
+		 * watchdog really is stopped properly.
+		 */
+		omap_wdt_disable(wdev);
+
 	if (pdata && pdata->read_reset_sources)
 		rs = pdata->read_reset_sources();
 	else
@@ -257,7 +280,9 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ?
 				WDIOF_CARDRESET : 0;
 
-	omap_wdt_disable(wdev);
+	ret = watchdog_init_params(omap_wdt, &pdev->dev);
+	if (ret)
+		goto err_init_params;
 
 	ret = watchdog_register_device(omap_wdt);
 	if (ret)
@@ -271,6 +296,7 @@ static int omap_wdt_probe(struct platform_device *pdev)
 
 	return 0;
 
+err_init_params:
 err_register_device:
 	pm_runtime_disable(wdev->dev);
 err_ioremap:
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
  2015-04-22 11:11   ` Timo Kokkonen
@ 2015-04-24 17:08     ` Guenter Roeck
  -1 siblings, 0 replies; 66+ messages in thread
From: Guenter Roeck @ 2015-04-24 17:08 UTC (permalink / raw)
  To: Timo Kokkonen
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
> There is a great deal of diversity in the watchdog hardware found on
> different devices. Differen hardware have different contstraints on
> them, many of the constraints that are excessively difficult for the
> user space to satisfy.
> 
> One such constraint is the length of the timeout value, which in many
> cases can be just a few seconds. Drivers are creating ad hoc solutions
> with timers and workqueues to extend the timeout in order to give user
> space more time between updates. Looking at the drivers it is clear
> that this has resulted to a lot of duplicate code.
> 
> Add an extension to the watchdog kernel API that allows the driver to
> describe tis HW constraints to the watchdog code. A kernel worker in
> the core is then used to extend the watchdog timeout on behalf of the
> user space. This allows the drivers to be simplified as core takes
> over the timer extending.
> 
> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>

I started twice to review this series, and each time there is a new version
before I can finish the review. Guess I'll wait until it settles down a bit
before trying again :-(. Just a quick comment below.

> ---
>  drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>  drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>  include/linux/watchdog.h         |  23 +++++++++
>  3 files changed, 189 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
> index cec9b55..fd12489 100644
> --- a/drivers/watchdog/watchdog_core.c
> +++ b/drivers/watchdog/watchdog_core.c
> @@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
>  EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>  
>  /**
> + * watchdog_init_parms() - initialize generic watchdog parameters
> + * @wdd: Watchdog device to operate
> + * @dev: Device that stores the device tree properties
> + *
> + * Initialize the generic timeout parameters. The driver needs to set
> + * hw_features bitmask from @wdd prior calling this function in order
> + * to ensure the core knows how to handle the HW.
> + *
> + * A zero is returned on success and -EINVAL for failure.
> + */
> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
> +{
> +	int ret = 0;
> +
> +	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);

You are changing the semantics of watchdog_init_timeout here;
for all practical purposes it no longer accepts the timeout passed
as parameter, but expects the timeout to be configured in wdd->timeout
instead. Please don't do that.

Guenter

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
@ 2015-04-24 17:08     ` Guenter Roeck
  0 siblings, 0 replies; 66+ messages in thread
From: Guenter Roeck @ 2015-04-24 17:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
> There is a great deal of diversity in the watchdog hardware found on
> different devices. Differen hardware have different contstraints on
> them, many of the constraints that are excessively difficult for the
> user space to satisfy.
> 
> One such constraint is the length of the timeout value, which in many
> cases can be just a few seconds. Drivers are creating ad hoc solutions
> with timers and workqueues to extend the timeout in order to give user
> space more time between updates. Looking at the drivers it is clear
> that this has resulted to a lot of duplicate code.
> 
> Add an extension to the watchdog kernel API that allows the driver to
> describe tis HW constraints to the watchdog code. A kernel worker in
> the core is then used to extend the watchdog timeout on behalf of the
> user space. This allows the drivers to be simplified as core takes
> over the timer extending.
> 
> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>

I started twice to review this series, and each time there is a new version
before I can finish the review. Guess I'll wait until it settles down a bit
before trying again :-(. Just a quick comment below.

> ---
>  drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>  drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>  include/linux/watchdog.h         |  23 +++++++++
>  3 files changed, 189 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
> index cec9b55..fd12489 100644
> --- a/drivers/watchdog/watchdog_core.c
> +++ b/drivers/watchdog/watchdog_core.c
> @@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
>  EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>  
>  /**
> + * watchdog_init_parms() - initialize generic watchdog parameters
> + * @wdd: Watchdog device to operate
> + * @dev: Device that stores the device tree properties
> + *
> + * Initialize the generic timeout parameters. The driver needs to set
> + * hw_features bitmask from @wdd prior calling this function in order
> + * to ensure the core knows how to handle the HW.
> + *
> + * A zero is returned on success and -EINVAL for failure.
> + */
> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
> +{
> +	int ret = 0;
> +
> +	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);

You are changing the semantics of watchdog_init_timeout here;
for all practical purposes it no longer accepts the timeout passed
as parameter, but expects the timeout to be configured in wdd->timeout
instead. Please don't do that.

Guenter

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 7/8] watchdog: omap_wdt: Fix memory leak on probe fail
  2015-04-22 11:11   ` Timo Kokkonen
@ 2015-04-26 15:32     ` Guenter Roeck
  -1 siblings, 0 replies; 66+ messages in thread
From: Guenter Roeck @ 2015-04-26 15:32 UTC (permalink / raw)
  To: Timo Kokkonen, linux-arm-kernel, linux-watchdog, boris.brezillon,
	nicolas.ferre, alexandre.belloni
  Cc: Wenyou.Yang

On 04/22/2015 04:11 AM, Timo Kokkonen wrote:
> Structures allocated on the beginning of the probe function must be
> freed in case of failure.

Really ? The functions are allocated with devm_kzalloc.
It might be useful to introduce an error on purpose, have the code
jump to your new label, and observe what happens.

Guenter


^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 7/8] watchdog: omap_wdt: Fix memory leak on probe fail
@ 2015-04-26 15:32     ` Guenter Roeck
  0 siblings, 0 replies; 66+ messages in thread
From: Guenter Roeck @ 2015-04-26 15:32 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/22/2015 04:11 AM, Timo Kokkonen wrote:
> Structures allocated on the beginning of the probe function must be
> freed in case of failure.

Really ? The functions are allocated with devm_kzalloc.
It might be useful to introduce an error on purpose, have the code
jump to your new label, and observe what happens.

Guenter

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
  2015-04-24 17:08     ` Guenter Roeck
@ 2015-04-27  5:41       ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-27  5:41 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

On 24.04.2015 20:08, Guenter Roeck wrote:
> On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
>> There is a great deal of diversity in the watchdog hardware found on
>> different devices. Differen hardware have different contstraints on
>> them, many of the constraints that are excessively difficult for the
>> user space to satisfy.
>>
>> One such constraint is the length of the timeout value, which in many
>> cases can be just a few seconds. Drivers are creating ad hoc solutions
>> with timers and workqueues to extend the timeout in order to give user
>> space more time between updates. Looking at the drivers it is clear
>> that this has resulted to a lot of duplicate code.
>>
>> Add an extension to the watchdog kernel API that allows the driver to
>> describe tis HW constraints to the watchdog code. A kernel worker in
>> the core is then used to extend the watchdog timeout on behalf of the
>> user space. This allows the drivers to be simplified as core takes
>> over the timer extending.
>>
>> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
>
> I started twice to review this series, and each time there is a new version
> before I can finish the review. Guess I'll wait until it settles down a bit
> before trying again :-(. Just a quick comment below.
>

Yeah, I didn't quite know how much time I had available to work with 
these patches, so I kept on sending fixed series out in a hope I get 
some feedback before making too big steps in any false direction.. And I 
ended up doing a  bit more than I thought at first. But I'm not going to 
make a new one until I have got enough feedback from this version. So 
please go ahead and review this version when you have time.

>> ---
>>   drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>>   drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>>   include/linux/watchdog.h         |  23 +++++++++
>>   3 files changed, 189 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
>> index cec9b55..fd12489 100644
>> --- a/drivers/watchdog/watchdog_core.c
>> +++ b/drivers/watchdog/watchdog_core.c
>> @@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
>>   EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>>
>>   /**
>> + * watchdog_init_parms() - initialize generic watchdog parameters
>> + * @wdd: Watchdog device to operate
>> + * @dev: Device that stores the device tree properties
>> + *
>> + * Initialize the generic timeout parameters. The driver needs to set
>> + * hw_features bitmask from @wdd prior calling this function in order
>> + * to ensure the core knows how to handle the HW.
>> + *
>> + * A zero is returned on success and -EINVAL for failure.
>> + */
>> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
>> +{
>> +	int ret = 0;
>> +
>> +	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
>
> You are changing the semantics of watchdog_init_timeout here;
> for all practical purposes it no longer accepts the timeout passed
> as parameter, but expects the timeout to be configured in wdd->timeout
> instead. Please don't do that.

hmm.. Yes, this isn't quite right. What I thought it should be is that 
the driver initializes  the values with sane value (either from module 
parameter or some default) and then if that's not set (wdd->timeout is 
zero), watchdog_init_params() gets the value from device tree. If we 
still don't get any preference for the timeout, then use some reasonable 
default, such as 60 seconds. How's that?

Right now the module parameter basically gets ignored altogether, which 
is wrong.

-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
@ 2015-04-27  5:41       ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-27  5:41 UTC (permalink / raw)
  To: linux-arm-kernel

On 24.04.2015 20:08, Guenter Roeck wrote:
> On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
>> There is a great deal of diversity in the watchdog hardware found on
>> different devices. Differen hardware have different contstraints on
>> them, many of the constraints that are excessively difficult for the
>> user space to satisfy.
>>
>> One such constraint is the length of the timeout value, which in many
>> cases can be just a few seconds. Drivers are creating ad hoc solutions
>> with timers and workqueues to extend the timeout in order to give user
>> space more time between updates. Looking at the drivers it is clear
>> that this has resulted to a lot of duplicate code.
>>
>> Add an extension to the watchdog kernel API that allows the driver to
>> describe tis HW constraints to the watchdog code. A kernel worker in
>> the core is then used to extend the watchdog timeout on behalf of the
>> user space. This allows the drivers to be simplified as core takes
>> over the timer extending.
>>
>> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
>
> I started twice to review this series, and each time there is a new version
> before I can finish the review. Guess I'll wait until it settles down a bit
> before trying again :-(. Just a quick comment below.
>

Yeah, I didn't quite know how much time I had available to work with 
these patches, so I kept on sending fixed series out in a hope I get 
some feedback before making too big steps in any false direction.. And I 
ended up doing a  bit more than I thought at first. But I'm not going to 
make a new one until I have got enough feedback from this version. So 
please go ahead and review this version when you have time.

>> ---
>>   drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>>   drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>>   include/linux/watchdog.h         |  23 +++++++++
>>   3 files changed, 189 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
>> index cec9b55..fd12489 100644
>> --- a/drivers/watchdog/watchdog_core.c
>> +++ b/drivers/watchdog/watchdog_core.c
>> @@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
>>   EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>>
>>   /**
>> + * watchdog_init_parms() - initialize generic watchdog parameters
>> + * @wdd: Watchdog device to operate
>> + * @dev: Device that stores the device tree properties
>> + *
>> + * Initialize the generic timeout parameters. The driver needs to set
>> + * hw_features bitmask from @wdd prior calling this function in order
>> + * to ensure the core knows how to handle the HW.
>> + *
>> + * A zero is returned on success and -EINVAL for failure.
>> + */
>> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
>> +{
>> +	int ret = 0;
>> +
>> +	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
>
> You are changing the semantics of watchdog_init_timeout here;
> for all practical purposes it no longer accepts the timeout passed
> as parameter, but expects the timeout to be configured in wdd->timeout
> instead. Please don't do that.

hmm.. Yes, this isn't quite right. What I thought it should be is that 
the driver initializes  the values with sane value (either from module 
parameter or some default) and then if that's not set (wdd->timeout is 
zero), watchdog_init_params() gets the value from device tree. If we 
still don't get any preference for the timeout, then use some reasonable 
default, such as 60 seconds. How's that?

Right now the module parameter basically gets ignored altogether, which 
is wrong.

-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 7/8] watchdog: omap_wdt: Fix memory leak on probe fail
  2015-04-26 15:32     ` Guenter Roeck
@ 2015-04-27  5:50       ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-27  5:50 UTC (permalink / raw)
  To: Guenter Roeck, linux-arm-kernel, linux-watchdog, boris.brezillon,
	nicolas.ferre, alexandre.belloni
  Cc: Wenyou.Yang

On 26.04.2015 18:32, Guenter Roeck wrote:
> On 04/22/2015 04:11 AM, Timo Kokkonen wrote:
>> Structures allocated on the beginning of the probe function must be
>> freed in case of failure.
>
> Really ? The functions are allocated with devm_kzalloc.
> It might be useful to introduce an error on purpose, have the code
> jump to your new label, and observe what happens.

Interesting. I wasn't aware of devm_kzalloc. I will have to remove this 
patch from the series and rework the another omap patch to apply without 
this. Thanks!

-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 7/8] watchdog: omap_wdt: Fix memory leak on probe fail
@ 2015-04-27  5:50       ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-04-27  5:50 UTC (permalink / raw)
  To: linux-arm-kernel

On 26.04.2015 18:32, Guenter Roeck wrote:
> On 04/22/2015 04:11 AM, Timo Kokkonen wrote:
>> Structures allocated on the beginning of the probe function must be
>> freed in case of failure.
>
> Really ? The functions are allocated with devm_kzalloc.
> It might be useful to introduce an error on purpose, have the code
> jump to your new label, and observe what happens.

Interesting. I wasn't aware of devm_kzalloc. I will have to remove this 
patch from the series and rework the another omap patch to apply without 
this. Thanks!

-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
  2015-04-22 11:11   ` Timo Kokkonen
@ 2015-05-03 18:56     ` Uwe Kleine-König
  -1 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-03 18:56 UTC (permalink / raw)
  To: Timo Kokkonen
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

Hello,

On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
> Use the new watchdog core extensions to let watchdog core take over
> boot time watchdog behavior. The difference is that early-timeout-sec
> device tree property becomes available for this driver and a running
> watchdog is not stopped unless the core decides to stop it.
> 
> Omap watchdog is running by default in the boot up but bootloader
> might have stopped it. Therefore we fill the WDOG_HW_RUNNING_AT_BOOT
> bit depending on the actual watchdog state so that the watchdog core
> can act properly.
> 
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
> ---
>  drivers/watchdog/omap_wdt.c | 28 +++++++++++++++++++++++++++-
>  1 file changed, 27 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
> index bbaf39a..7164f2e 100644
> --- a/drivers/watchdog/omap_wdt.c
> +++ b/drivers/watchdog/omap_wdt.c
> @@ -78,6 +78,13 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
>  	/* reloaded WCRR from WLDR */
>  }
>  
> +static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
> +{
> +	void __iomem *base = wdev->base;
> +
> +	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
> +}
This isn't reliable. The sequence needed to enable the watchdog is
	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
	writel(0x4444, base + OMAP_WATCHDOG_SPR);

The sequence to stop is:
	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
	writel(0x5555, base + OMAP_WATCHDOG_SPR);

But:

barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
44e35048: 00005555                                           UU..
barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
44e35048: 00004444                                           DD..

So the register contains 0x4444 but the timer doesn't run. So at best
testing for 0x4444 is a good heuristic.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
@ 2015-05-03 18:56     ` Uwe Kleine-König
  0 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-03 18:56 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
> Use the new watchdog core extensions to let watchdog core take over
> boot time watchdog behavior. The difference is that early-timeout-sec
> device tree property becomes available for this driver and a running
> watchdog is not stopped unless the core decides to stop it.
> 
> Omap watchdog is running by default in the boot up but bootloader
> might have stopped it. Therefore we fill the WDOG_HW_RUNNING_AT_BOOT
> bit depending on the actual watchdog state so that the watchdog core
> can act properly.
> 
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
> ---
>  drivers/watchdog/omap_wdt.c | 28 +++++++++++++++++++++++++++-
>  1 file changed, 27 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
> index bbaf39a..7164f2e 100644
> --- a/drivers/watchdog/omap_wdt.c
> +++ b/drivers/watchdog/omap_wdt.c
> @@ -78,6 +78,13 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
>  	/* reloaded WCRR from WLDR */
>  }
>  
> +static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
> +{
> +	void __iomem *base = wdev->base;
> +
> +	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
> +}
This isn't reliable. The sequence needed to enable the watchdog is
	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
	writel(0x4444, base + OMAP_WATCHDOG_SPR);

The sequence to stop is:
	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
	writel(0x5555, base + OMAP_WATCHDOG_SPR);

But:

barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
44e35048: 00005555                                           UU..
barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
44e35048: 00004444                                           DD..

So the register contains 0x4444 but the timer doesn't run. So at best
testing for 0x4444 is a good heuristic.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
  2015-05-03 18:56     ` Uwe Kleine-König
@ 2015-05-04  5:59       ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-04  5:59 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

Hi, 03.05.2015 21:56, Uwe Kleine-König wrote:
> Hello,
>
> On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
>> Use the new watchdog core extensions to let watchdog core take over
>> boot time watchdog behavior. The difference is that early-timeout-sec
>> device tree property becomes available for this driver and a running
>> watchdog is not stopped unless the core decides to stop it.
>>
>> Omap watchdog is running by default in the boot up but bootloader
>> might have stopped it. Therefore we fill the WDOG_HW_RUNNING_AT_BOOT
>> bit depending on the actual watchdog state so that the watchdog core
>> can act properly.
>>
>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
>> ---
>>   drivers/watchdog/omap_wdt.c | 28 +++++++++++++++++++++++++++-
>>   1 file changed, 27 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
>> index bbaf39a..7164f2e 100644
>> --- a/drivers/watchdog/omap_wdt.c
>> +++ b/drivers/watchdog/omap_wdt.c
>> @@ -78,6 +78,13 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
>>   	/* reloaded WCRR from WLDR */
>>   }
>>
>> +static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
>> +{
>> +	void __iomem *base = wdev->base;
>> +
>> +	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
>> +}
> This isn't reliable. The sequence needed to enable the watchdog is
> 	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
> 	writel(0x4444, base + OMAP_WATCHDOG_SPR);
>
> The sequence to stop is:
> 	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
> 	writel(0x5555, base + OMAP_WATCHDOG_SPR);
>
> But:
>
> barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
> 44e35048: 00005555                                           UU..
> barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
> barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
> 44e35048: 00004444                                           DD..
>
> So the register contains 0x4444 but the timer doesn't run. So at best
> testing for 0x4444 is a good heuristic.

Yeah.. I don't think we can get any better than that. Unless we start 
checking the counter register and see whether it really counts or not, 
and I think that's a bit overkill.. So I'd say we should be safe when 
assuming bootloader is doing things correctly. Although, we could add a 
comment to the code that the test may not be 100% reliable in case the 
start sequence have not been issued properly.

Thanks for pointing this out!

-Timo


^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
@ 2015-05-04  5:59       ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-04  5:59 UTC (permalink / raw)
  To: linux-arm-kernel

Hi, 03.05.2015 21:56, Uwe Kleine-K?nig wrote:
> Hello,
>
> On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
>> Use the new watchdog core extensions to let watchdog core take over
>> boot time watchdog behavior. The difference is that early-timeout-sec
>> device tree property becomes available for this driver and a running
>> watchdog is not stopped unless the core decides to stop it.
>>
>> Omap watchdog is running by default in the boot up but bootloader
>> might have stopped it. Therefore we fill the WDOG_HW_RUNNING_AT_BOOT
>> bit depending on the actual watchdog state so that the watchdog core
>> can act properly.
>>
>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
>> ---
>>   drivers/watchdog/omap_wdt.c | 28 +++++++++++++++++++++++++++-
>>   1 file changed, 27 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
>> index bbaf39a..7164f2e 100644
>> --- a/drivers/watchdog/omap_wdt.c
>> +++ b/drivers/watchdog/omap_wdt.c
>> @@ -78,6 +78,13 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
>>   	/* reloaded WCRR from WLDR */
>>   }
>>
>> +static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
>> +{
>> +	void __iomem *base = wdev->base;
>> +
>> +	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
>> +}
> This isn't reliable. The sequence needed to enable the watchdog is
> 	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
> 	writel(0x4444, base + OMAP_WATCHDOG_SPR);
>
> The sequence to stop is:
> 	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
> 	writel(0x5555, base + OMAP_WATCHDOG_SPR);
>
> But:
>
> barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
> 44e35048: 00005555                                           UU..
> barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
> barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
> 44e35048: 00004444                                           DD..
>
> So the register contains 0x4444 but the timer doesn't run. So at best
> testing for 0x4444 is a good heuristic.

Yeah.. I don't think we can get any better than that. Unless we start 
checking the counter register and see whether it really counts or not, 
and I think that's a bit overkill.. So I'd say we should be safe when 
assuming bootloader is doing things correctly. Although, we could add a 
comment to the code that the test may not be 100% reliable in case the 
start sequence have not been issued properly.

Thanks for pointing this out!

-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
  2015-05-04  5:59       ` Timo Kokkonen
@ 2015-05-04  7:04         ` Uwe Kleine-König
  -1 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-04  7:04 UTC (permalink / raw)
  To: Timo Kokkonen
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

Hello Timo,

On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
> Hi, 03.05.2015 21:56, Uwe Kleine-König wrote:
> >On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
> >>+static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
> >>+{
> >>+	void __iomem *base = wdev->base;
> >>+
> >>+	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
> >>+}
> >This isn't reliable. The sequence needed to enable the watchdog is
> >	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
> >	writel(0x4444, base + OMAP_WATCHDOG_SPR);
> >
> >The sequence to stop is:
> >	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
> >	writel(0x5555, base + OMAP_WATCHDOG_SPR);
> >
> >But:
> >
> >barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
> >44e35048: 00005555                                           UU..
> >barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
> >barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
> >44e35048: 00004444                                           DD..
> >
> >So the register contains 0x4444 but the timer doesn't run. So at best
> >testing for 0x4444 is a good heuristic.
> 
> Yeah.. I don't think we can get any better than that. Unless we
> start checking the counter register and see whether it really counts
> or not, and I think that's a bit overkill.. So I'd say we should be
> safe when assuming bootloader is doing things correctly. Although,
> we could add a comment to the code that the test may not be 100%
> reliable in case the start sequence have not been issued properly.
> 
> Thanks for pointing this out!
It doesn't seem to much overhead to do:

	/*
	 * There is no register that tells us if the timer is running,
	 * so we have to resort to sample twice. The minimal frequency
	 * is 256 Hz (32768 Hz prescaled with 2**7).
	 */
	counter1 = readl(base + OMAP_WATCHDOG_CCR);
	mdelay(4);
	counter2 = readl(base + OMAP_WATCHDOG_CCR);
	return counter1 != counter2;

I'd say it's even worth to do:

	cntrl = readl(base + OMAP_WATCHDOG_CNTRL);
	if (cntrl & (1 << 5))
		shift = (cntrl >> 2) & 0x7;
	else
		shift = 0;
	counter1 = readl(base + OMAP_WATCHDOG_CCR);
	udelay(31 << shift);
	counter2 = readl(base + OMAP_WATCHDOG_CCR);
	return counter1 != counter2;

For some bonus points add some defines for the magic constants.

This is save as the OMAP_WATCHDOG_CNTRL doesn't seem to accept reads
while the counter is running. Maybe even this could be used to detect a
running timer?:

 - enable timer:
	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0xbbbb
	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444

 - read out WCLR
	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4      
	44e35024: 00000020                                            ...

 - write to WCLR
	barebox@TI AM335x BeagleBone black:/ mw 0x44e35024 0x0

 - check result; didn't work
	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4 
	44e35024: 00000020                                            ...

 - stop timer
	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0xaaaa
	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x5555

 - recheck WCLR
	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
	44e35024: 00000020                                            ...

 - write to WCLR
	barebox@TI AM335x BeagleBone black:/ mw 0x44e35024 0x0

 - check result; write succeeded
	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
	44e35024: 00000000                                           ....

(This is was tested on an AM335x.)

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
@ 2015-05-04  7:04         ` Uwe Kleine-König
  0 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-04  7:04 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Timo,

On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
> Hi, 03.05.2015 21:56, Uwe Kleine-K?nig wrote:
> >On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
> >>+static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
> >>+{
> >>+	void __iomem *base = wdev->base;
> >>+
> >>+	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
> >>+}
> >This isn't reliable. The sequence needed to enable the watchdog is
> >	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
> >	writel(0x4444, base + OMAP_WATCHDOG_SPR);
> >
> >The sequence to stop is:
> >	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
> >	writel(0x5555, base + OMAP_WATCHDOG_SPR);
> >
> >But:
> >
> >barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
> >44e35048: 00005555                                           UU..
> >barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
> >barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
> >44e35048: 00004444                                           DD..
> >
> >So the register contains 0x4444 but the timer doesn't run. So at best
> >testing for 0x4444 is a good heuristic.
> 
> Yeah.. I don't think we can get any better than that. Unless we
> start checking the counter register and see whether it really counts
> or not, and I think that's a bit overkill.. So I'd say we should be
> safe when assuming bootloader is doing things correctly. Although,
> we could add a comment to the code that the test may not be 100%
> reliable in case the start sequence have not been issued properly.
> 
> Thanks for pointing this out!
It doesn't seem to much overhead to do:

	/*
	 * There is no register that tells us if the timer is running,
	 * so we have to resort to sample twice. The minimal frequency
	 * is 256 Hz (32768 Hz prescaled with 2**7).
	 */
	counter1 = readl(base + OMAP_WATCHDOG_CCR);
	mdelay(4);
	counter2 = readl(base + OMAP_WATCHDOG_CCR);
	return counter1 != counter2;

I'd say it's even worth to do:

	cntrl = readl(base + OMAP_WATCHDOG_CNTRL);
	if (cntrl & (1 << 5))
		shift = (cntrl >> 2) & 0x7;
	else
		shift = 0;
	counter1 = readl(base + OMAP_WATCHDOG_CCR);
	udelay(31 << shift);
	counter2 = readl(base + OMAP_WATCHDOG_CCR);
	return counter1 != counter2;

For some bonus points add some defines for the magic constants.

This is save as the OMAP_WATCHDOG_CNTRL doesn't seem to accept reads
while the counter is running. Maybe even this could be used to detect a
running timer?:

 - enable timer:
	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0xbbbb
	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444

 - read out WCLR
	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4      
	44e35024: 00000020                                            ...

 - write to WCLR
	barebox at TI AM335x BeagleBone black:/ mw 0x44e35024 0x0

 - check result; didn't work
	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4 
	44e35024: 00000020                                            ...

 - stop timer
	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0xaaaa
	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x5555

 - recheck WCLR
	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4
	44e35024: 00000020                                            ...

 - write to WCLR
	barebox at TI AM335x BeagleBone black:/ mw 0x44e35024 0x0

 - check result; write succeeded
	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
	44e35024: 00000000                                           ....

(This is was tested on an AM335x.)

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
  2015-04-22 11:11   ` Timo Kokkonen
@ 2015-05-04  7:58     ` Uwe Kleine-König
  -1 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-04  7:58 UTC (permalink / raw)
  To: Timo Kokkonen
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

Hello Timo,

On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
> There is a great deal of diversity in the watchdog hardware found on
> different devices. Differen hardware have different contstraints on
s/Differen/Different/; s/contstraints/constraints/

> them, many of the constraints that are excessively difficult for the
> user space to satisfy.
> 
> One such constraint is the length of the timeout value, which in many
> cases can be just a few seconds. Drivers are creating ad hoc solutions
> with timers and workqueues to extend the timeout in order to give user
> space more time between updates. Looking at the drivers it is clear
> that this has resulted to a lot of duplicate code.
> 
> Add an extension to the watchdog kernel API that allows the driver to
> describe tis HW constraints to the watchdog code. A kernel worker in
s/tis/its/

> the core is then used to extend the watchdog timeout on behalf of the
> user space. This allows the drivers to be simplified as core takes
> over the timer extending.
In general a nice idea.

> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
> ---
>  drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>  drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>  include/linux/watchdog.h         |  23 +++++++++
>  3 files changed, 189 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
> index cec9b55..fd12489 100644
> --- a/drivers/watchdog/watchdog_core.c
> +++ b/drivers/watchdog/watchdog_core.c
> @@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
>  EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>  
>  /**
> + * watchdog_init_parms() - initialize generic watchdog parameters
> + * @wdd: Watchdog device to operate
> + * @dev: Device that stores the device tree properties
> + *
> + * Initialize the generic timeout parameters. The driver needs to set
> + * hw_features bitmask from @wdd prior calling this function in order
> + * to ensure the core knows how to handle the HW.
> + *
> + * A zero is returned on success and -EINVAL for failure.
> + */
> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
> +{
> +	int ret = 0;
> +
> +	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
> +	if (ret < 0)
> +		return ret;
As Guenter pointed out, using wdd->timeout is unfortunate here.

> +
> +	/*
> +	 * Max HW timeout needs to be set so that core knows when to
> +	 * use a kernel worker to support longer watchdog timeouts
> +	 */
> +	if (!wdd->hw_max_timeout)
> +		return -EINVAL;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(watchdog_init_params);
> +
> +static void watchdog_worker(struct work_struct *work)
> +{
> +	struct watchdog_device *wdd = container_of(to_delayed_work(work),
> +						struct watchdog_device, work);
please align the follow-up line to the opening (.

> +	bool boot_keepalive;
> +	bool active_keepalive;
> +
> +	mutex_lock(&wdd->lock);
> +
> +	boot_keepalive = !watchdog_active(wdd) &&
I'm not sure about the semantic of watchdog_active here. Would be nice
to clearify this. Does it mean /dev/watchdog is open, or the watchdog is
running? Maybe introduce watchdog_running(wdd) and watchdog_opened(wdd)?
Is this related to the topic of this patch? You didn't mention anything
in the commit log about the time until userspace comes up.

> +		!watchdog_is_stoppable(wdd);
> +
> +	active_keepalive = watchdog_active(wdd) &&
> +		wdd->hw_max_timeout < wdd->timeout * HZ;
> +
> +	if (time_after(jiffies, wdd->expires) && watchdog_active(wdd)) {
> +		pr_crit("I will reset your machine !\n");
> +		mutex_unlock(&wdd->lock);
> +		return;
> +	}
I'd replace the pr_crit by a code comment. Also "I will reset your
machine!" is wrong, the core just stops petting the watchdog. If
userspace comes in in time there is no harm, right?

> +	if (boot_keepalive || active_keepalive) {
> +		if (wdd->ops->ping)
> +			wdd->ops->ping(wdd);
> +		else
> +			wdd->ops->start(wdd);
if (boot_keepalive || active_keepalive)
	_watchdog_ping(wdd);

> +
> +		schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
s/  / /; Would it be sensible to assume hw_heartbeat to be timeout / 2
always?

> +	}
> +
> +	mutex_unlock(&wdd->lock);
> +}
> +
> +static int prepare_watchdog(struct watchdog_device *wdd)
> +{
> +	int err = 0;
> +
> +	/* Stop the watchdog now before user space opens the device */
> +	if (watchdog_is_stoppable(wdd) &&
> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
> +		err = wdd->ops->stop(wdd);
> +
> +	} else if (!watchdog_is_stoppable(wdd) &&
> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
> +		/*
> +		 * Can't stop it, use a delayed worker to tick it
> +		 * until it's open by user space
> +		 */
I'd add a ping here because if the driver comes up a big part of the
initial timeout setup by the bootloader might already be over.

Note that at least for the omap watchdog driver the assignment to
timeout doesn't relate in any way to the timeout actually setup in
hardware.

> +		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
> +	}
> +	return err;
> +}
> +
> +/**
>   * watchdog_register_device() - register a watchdog device
>   * @wdd: watchdog device
>   *
> @@ -156,13 +239,24 @@ int watchdog_register_device(struct watchdog_device *wdd)
>  	wdd->dev = device_create(watchdog_class, wdd->parent, devno,
>  					NULL, "watchdog%d", wdd->id);
>  	if (IS_ERR(wdd->dev)) {
> -		watchdog_dev_unregister(wdd);
> -		ida_simple_remove(&watchdog_ida, id);
>  		ret = PTR_ERR(wdd->dev);
> -		return ret;
> +		goto dev_create_fail;
> +	}
> +
> +	INIT_DELAYED_WORK(&wdd->work, watchdog_worker);
> +
> +	if (wdd->hw_max_timeout) {
> +		ret = prepare_watchdog(wdd);
> +		if (ret)
> +			goto dev_create_fail;
	} else {
		dev_warn("Incomplete watchdog driver implementation, please report or fix\n")
>  	}
>  
>  	return 0;
> +
> +dev_create_fail:
> +	watchdog_dev_unregister(wdd);
> +	ida_simple_remove(&watchdog_ida, id);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(watchdog_register_device);
>  
> @@ -181,6 +275,7 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
>  	if (wdd == NULL)
>  		return;
>  
> +	cancel_delayed_work_sync(&wdd->work);
>  	devno = wdd->cdev.dev;
>  	ret = watchdog_dev_unregister(wdd);
>  	if (ret)
> diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
> index 6aaefba..04ac68c 100644
> --- a/drivers/watchdog/watchdog_dev.c
> +++ b/drivers/watchdog/watchdog_dev.c
> @@ -49,6 +49,14 @@ static dev_t watchdog_devt;
>  /* the watchdog device behind /dev/watchdog */
>  static struct watchdog_device *old_wdd;
>  
> +static int _watchdog_ping(struct watchdog_device *wddev)
> +{
> +	if (wddev->ops->ping)
> +		return wddev->ops->ping(wddev);  /* ping the watchdog */
> +	else
> +		return wddev->ops->start(wddev); /* restart watchdog */
> +}
> +
>  /*
>   *	watchdog_ping: ping the watchdog.
>   *	@wddev: the watchdog device to ping
> @@ -73,10 +81,13 @@ static int watchdog_ping(struct watchdog_device *wddev)
>  	if (!watchdog_active(wddev))
>  		goto out_ping;
>  
> -	if (wddev->ops->ping)
> -		err = wddev->ops->ping(wddev);  /* ping the watchdog */
> -	else
> -		err = wddev->ops->start(wddev); /* restart watchdog */
> +	err = _watchdog_ping(wddev);
> +
> +	if (wddev->hw_max_timeout &&
> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
> +		wddev->expires = jiffies + wddev->timeout * HZ;
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	}
>  
>  out_ping:
>  	mutex_unlock(&wddev->lock);
> @@ -110,6 +121,13 @@ static int watchdog_start(struct watchdog_device *wddev)
>  	if (err == 0)
>  		set_bit(WDOG_ACTIVE, &wddev->status);
>  
> +	if (wddev->hw_max_timeout &&
> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
> +		wddev->expires = jiffies + wddev->timeout * HZ;
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	} else
> +		cancel_delayed_work(&wddev->work);
> +
>  out_start:
>  	mutex_unlock(&wddev->lock);
>  	return err;
> @@ -145,9 +163,21 @@ static int watchdog_stop(struct watchdog_device *wddev)
>  		goto out_stop;
>  	}
>  
> -	err = wddev->ops->stop(wddev);
> -	if (err == 0)
> +	if (!wddev->hw_max_timeout || watchdog_is_stoppable(wddev)) {
> +		cancel_delayed_work(&wddev->work);
> +		err = wddev->ops->stop(wddev);
> +		if (err == 0)
> +			clear_bit(WDOG_ACTIVE, &wddev->status);
> +	} else {
> +		/* Unstoppable watchdogs need the worker to keep them alive */
>  		clear_bit(WDOG_ACTIVE, &wddev->status);
> +		/*
> +		 * Ping it once as we don't know how much time there
> +		 * is left in the watchdog timer.
> +		 */
> +		err = _watchdog_ping(wddev);
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	}
Why not do:

	err = wddev->ops->stop(wddev);
	clear_bit(WDOG_ACTIVE, &wddev->status);
	if (err != 0) {
		err = _watchdog_ping(wddev);
		if (err) {
			dev_crit(...);
		schedule_delayed_work(...);
	}

This way you (maybe?) don't need to expand all drivers to tell you if
the watchdog timer is stoppable, just assert that the stop callback
fails which IMHO is easier and more natural.

>  
>  out_stop:
>  	mutex_unlock(&wddev->lock);
> @@ -194,7 +224,38 @@ out_status:
>  static int watchdog_set_timeout(struct watchdog_device *wddev,
>  							unsigned int timeout)
>  {
> -	int err;
> +	int err = 0;
> +
> +	if (wddev->hw_max_timeout) {
> +		int hw_timeout;
> +		/*
> +		 * We can't support too short timeout values. There is
> +		 * really no maximu however, anything longer than HW
s/maximu/maximum/

> +		 * maximum will be supported by the watchdog core on
> +		 * behalf of the actual HW.
> +		 */
> +		if (timeout < wddev->min_timeout)
> +			return -EINVAL;
Is it save to read min_timout without holding the lock?
> +
> +		mutex_lock(&wddev->lock);
> +		if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
> +			err = -ENODEV;
> +			goto out_timeout;
> +		}
> +
> +		if (timeout * HZ > wddev->hw_max_timeout)
> +			schedule_delayed_work(&wddev->work,
> +					wddev->hw_heartbeat);
> +
> +		hw_timeout = min(timeout, wddev->hw_max_timeout / HZ);
If you'd keep hw_max_timeout in seconds (i.e. what is used for timeout)
you wouldn't need to divide here. If my watchdog has hw_max_timeout =
0.5s then you do wddev->ops->set_timeout(0) here.

> +		if (wddev->info->options & WDIOF_SETTIMEOUT)
> +			err = wddev->ops->set_timeout(wddev, hw_timeout);
The case of not having WDIOF_SETTIMEOUT isn't handled here, right? Other
places check for WDIOF_SETTIMEOUT and wddev->ops->set_timeout?!

> +
> +		if (hw_timeout < timeout)
> +			wddev->timeout = timeout;
What is the semantic of wddev->timeout? Does it hold the value
set by userspace or the value the hardware is set to? You'd need to
clarify that by adding more comments. Is this if correct?

> +
> +		goto out_timeout;
> +	}
>  
>  	if ((wddev->ops->set_timeout == NULL) ||
>  	    !(wddev->info->options & WDIOF_SETTIMEOUT))
> diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
> index 395b70e..027c99d 100644
> --- a/include/linux/watchdog.h
> +++ b/include/linux/watchdog.h
> @@ -12,6 +12,7 @@
>  #include <linux/bitops.h>
>  #include <linux/device.h>
>  #include <linux/cdev.h>
> +#include <linux/workqueue.h>
>  #include <uapi/linux/watchdog.h>
>  
>  struct watchdog_ops;
> @@ -62,9 +63,13 @@ struct watchdog_ops {
>   * @timeout:	The watchdog devices timeout value.
>   * @min_timeout:The watchdog devices minimum timeout value.
>   * @max_timeout:The watchdog devices maximum timeout value.
> + * @hw_max_timeout:The watchdog hardware maximum timeout value.
> + * @hw_heartbeat:Time interval in HW between timer pings.
>   * @driver-data:Pointer to the drivers private data.
>   * @lock:	Lock for watchdog core internal use only.
> + * @work:	Worker used to provide longer timeouts than hardware supports.
>   * @status:	Field that contains the devices internal status bits.
> + * @hw_features:Feature bits describing how the watchdog HW works.
>   *
>   * The watchdog_device structure contains all information about a
>   * watchdog timer device.
> @@ -86,8 +91,12 @@ struct watchdog_device {
>  	unsigned int timeout;
>  	unsigned int min_timeout;
>  	unsigned int max_timeout;
> +	unsigned int hw_max_timeout; /* in jiffies */
> +	unsigned int hw_heartbeat; /* in jiffies */
> +	unsigned long int expires; /* for keepalive worker */
>  	void *driver_data;
>  	struct mutex lock;
> +	struct delayed_work work;
I'd group things together here that belong together.

>  	unsigned long status;
>  /* Bit numbers for status flags */
>  #define WDOG_ACTIVE		0	/* Is the watchdog running/active */
> @@ -95,6 +104,14 @@ struct watchdog_device {
>  #define WDOG_ALLOW_RELEASE	2	/* Did we receive the magic char ? */
>  #define WDOG_NO_WAY_OUT		3	/* Is 'nowayout' feature set ? */
>  #define WDOG_UNREGISTERED	4	/* Has the device been unregistered */
> +
> +/* Bits describing features supported by the HW */
> +	unsigned long hw_features;
> +
> +/* Can the watchdog be stopped and started */
> +#define WDOG_HW_IS_STOPPABLE		BIT(0)
> +/* Is the watchdog already running when the driver starts up */
> +#define WDOG_HW_RUNNING_AT_BOOT		BIT(1)
>  };
>  
>  #define WATCHDOG_NOWAYOUT		IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
> @@ -120,6 +137,11 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
>  		(t < wdd->min_timeout || t > wdd->max_timeout));
>  }
>  
> +static inline bool watchdog_is_stoppable(struct watchdog_device *wdd)
> +{
> +	return wdd->hw_features & WDOG_HW_IS_STOPPABLE;
> +}
> +
>  /* Use the following functions to manipulate watchdog driver specific data */
>  static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
>  {
> @@ -134,6 +156,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
>  /* drivers/watchdog/watchdog_core.c */
>  extern int watchdog_init_timeout(struct watchdog_device *wdd,
>  				  unsigned int timeout_parm, struct device *dev);
> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev);
This needs commenting and probably embrace watchdog_init_timeout
properly. i.e. remove watchdog_init_timeout.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
@ 2015-05-04  7:58     ` Uwe Kleine-König
  0 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-04  7:58 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Timo,

On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
> There is a great deal of diversity in the watchdog hardware found on
> different devices. Differen hardware have different contstraints on
s/Differen/Different/; s/contstraints/constraints/

> them, many of the constraints that are excessively difficult for the
> user space to satisfy.
> 
> One such constraint is the length of the timeout value, which in many
> cases can be just a few seconds. Drivers are creating ad hoc solutions
> with timers and workqueues to extend the timeout in order to give user
> space more time between updates. Looking at the drivers it is clear
> that this has resulted to a lot of duplicate code.
> 
> Add an extension to the watchdog kernel API that allows the driver to
> describe tis HW constraints to the watchdog code. A kernel worker in
s/tis/its/

> the core is then used to extend the watchdog timeout on behalf of the
> user space. This allows the drivers to be simplified as core takes
> over the timer extending.
In general a nice idea.

> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
> ---
>  drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>  drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>  include/linux/watchdog.h         |  23 +++++++++
>  3 files changed, 189 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
> index cec9b55..fd12489 100644
> --- a/drivers/watchdog/watchdog_core.c
> +++ b/drivers/watchdog/watchdog_core.c
> @@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
>  EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>  
>  /**
> + * watchdog_init_parms() - initialize generic watchdog parameters
> + * @wdd: Watchdog device to operate
> + * @dev: Device that stores the device tree properties
> + *
> + * Initialize the generic timeout parameters. The driver needs to set
> + * hw_features bitmask from @wdd prior calling this function in order
> + * to ensure the core knows how to handle the HW.
> + *
> + * A zero is returned on success and -EINVAL for failure.
> + */
> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
> +{
> +	int ret = 0;
> +
> +	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
> +	if (ret < 0)
> +		return ret;
As Guenter pointed out, using wdd->timeout is unfortunate here.

> +
> +	/*
> +	 * Max HW timeout needs to be set so that core knows when to
> +	 * use a kernel worker to support longer watchdog timeouts
> +	 */
> +	if (!wdd->hw_max_timeout)
> +		return -EINVAL;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(watchdog_init_params);
> +
> +static void watchdog_worker(struct work_struct *work)
> +{
> +	struct watchdog_device *wdd = container_of(to_delayed_work(work),
> +						struct watchdog_device, work);
please align the follow-up line to the opening (.

> +	bool boot_keepalive;
> +	bool active_keepalive;
> +
> +	mutex_lock(&wdd->lock);
> +
> +	boot_keepalive = !watchdog_active(wdd) &&
I'm not sure about the semantic of watchdog_active here. Would be nice
to clearify this. Does it mean /dev/watchdog is open, or the watchdog is
running? Maybe introduce watchdog_running(wdd) and watchdog_opened(wdd)?
Is this related to the topic of this patch? You didn't mention anything
in the commit log about the time until userspace comes up.

> +		!watchdog_is_stoppable(wdd);
> +
> +	active_keepalive = watchdog_active(wdd) &&
> +		wdd->hw_max_timeout < wdd->timeout * HZ;
> +
> +	if (time_after(jiffies, wdd->expires) && watchdog_active(wdd)) {
> +		pr_crit("I will reset your machine !\n");
> +		mutex_unlock(&wdd->lock);
> +		return;
> +	}
I'd replace the pr_crit by a code comment. Also "I will reset your
machine!" is wrong, the core just stops petting the watchdog. If
userspace comes in in time there is no harm, right?

> +	if (boot_keepalive || active_keepalive) {
> +		if (wdd->ops->ping)
> +			wdd->ops->ping(wdd);
> +		else
> +			wdd->ops->start(wdd);
if (boot_keepalive || active_keepalive)
	_watchdog_ping(wdd);

> +
> +		schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
s/  / /; Would it be sensible to assume hw_heartbeat to be timeout / 2
always?

> +	}
> +
> +	mutex_unlock(&wdd->lock);
> +}
> +
> +static int prepare_watchdog(struct watchdog_device *wdd)
> +{
> +	int err = 0;
> +
> +	/* Stop the watchdog now before user space opens the device */
> +	if (watchdog_is_stoppable(wdd) &&
> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
> +		err = wdd->ops->stop(wdd);
> +
> +	} else if (!watchdog_is_stoppable(wdd) &&
> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
> +		/*
> +		 * Can't stop it, use a delayed worker to tick it
> +		 * until it's open by user space
> +		 */
I'd add a ping here because if the driver comes up a big part of the
initial timeout setup by the bootloader might already be over.

Note that at least for the omap watchdog driver the assignment to
timeout doesn't relate in any way to the timeout actually setup in
hardware.

> +		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
> +	}
> +	return err;
> +}
> +
> +/**
>   * watchdog_register_device() - register a watchdog device
>   * @wdd: watchdog device
>   *
> @@ -156,13 +239,24 @@ int watchdog_register_device(struct watchdog_device *wdd)
>  	wdd->dev = device_create(watchdog_class, wdd->parent, devno,
>  					NULL, "watchdog%d", wdd->id);
>  	if (IS_ERR(wdd->dev)) {
> -		watchdog_dev_unregister(wdd);
> -		ida_simple_remove(&watchdog_ida, id);
>  		ret = PTR_ERR(wdd->dev);
> -		return ret;
> +		goto dev_create_fail;
> +	}
> +
> +	INIT_DELAYED_WORK(&wdd->work, watchdog_worker);
> +
> +	if (wdd->hw_max_timeout) {
> +		ret = prepare_watchdog(wdd);
> +		if (ret)
> +			goto dev_create_fail;
	} else {
		dev_warn("Incomplete watchdog driver implementation, please report or fix\n")
>  	}
>  
>  	return 0;
> +
> +dev_create_fail:
> +	watchdog_dev_unregister(wdd);
> +	ida_simple_remove(&watchdog_ida, id);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(watchdog_register_device);
>  
> @@ -181,6 +275,7 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
>  	if (wdd == NULL)
>  		return;
>  
> +	cancel_delayed_work_sync(&wdd->work);
>  	devno = wdd->cdev.dev;
>  	ret = watchdog_dev_unregister(wdd);
>  	if (ret)
> diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
> index 6aaefba..04ac68c 100644
> --- a/drivers/watchdog/watchdog_dev.c
> +++ b/drivers/watchdog/watchdog_dev.c
> @@ -49,6 +49,14 @@ static dev_t watchdog_devt;
>  /* the watchdog device behind /dev/watchdog */
>  static struct watchdog_device *old_wdd;
>  
> +static int _watchdog_ping(struct watchdog_device *wddev)
> +{
> +	if (wddev->ops->ping)
> +		return wddev->ops->ping(wddev);  /* ping the watchdog */
> +	else
> +		return wddev->ops->start(wddev); /* restart watchdog */
> +}
> +
>  /*
>   *	watchdog_ping: ping the watchdog.
>   *	@wddev: the watchdog device to ping
> @@ -73,10 +81,13 @@ static int watchdog_ping(struct watchdog_device *wddev)
>  	if (!watchdog_active(wddev))
>  		goto out_ping;
>  
> -	if (wddev->ops->ping)
> -		err = wddev->ops->ping(wddev);  /* ping the watchdog */
> -	else
> -		err = wddev->ops->start(wddev); /* restart watchdog */
> +	err = _watchdog_ping(wddev);
> +
> +	if (wddev->hw_max_timeout &&
> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
> +		wddev->expires = jiffies + wddev->timeout * HZ;
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	}
>  
>  out_ping:
>  	mutex_unlock(&wddev->lock);
> @@ -110,6 +121,13 @@ static int watchdog_start(struct watchdog_device *wddev)
>  	if (err == 0)
>  		set_bit(WDOG_ACTIVE, &wddev->status);
>  
> +	if (wddev->hw_max_timeout &&
> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
> +		wddev->expires = jiffies + wddev->timeout * HZ;
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	} else
> +		cancel_delayed_work(&wddev->work);
> +
>  out_start:
>  	mutex_unlock(&wddev->lock);
>  	return err;
> @@ -145,9 +163,21 @@ static int watchdog_stop(struct watchdog_device *wddev)
>  		goto out_stop;
>  	}
>  
> -	err = wddev->ops->stop(wddev);
> -	if (err == 0)
> +	if (!wddev->hw_max_timeout || watchdog_is_stoppable(wddev)) {
> +		cancel_delayed_work(&wddev->work);
> +		err = wddev->ops->stop(wddev);
> +		if (err == 0)
> +			clear_bit(WDOG_ACTIVE, &wddev->status);
> +	} else {
> +		/* Unstoppable watchdogs need the worker to keep them alive */
>  		clear_bit(WDOG_ACTIVE, &wddev->status);
> +		/*
> +		 * Ping it once as we don't know how much time there
> +		 * is left in the watchdog timer.
> +		 */
> +		err = _watchdog_ping(wddev);
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	}
Why not do:

	err = wddev->ops->stop(wddev);
	clear_bit(WDOG_ACTIVE, &wddev->status);
	if (err != 0) {
		err = _watchdog_ping(wddev);
		if (err) {
			dev_crit(...);
		schedule_delayed_work(...);
	}

This way you (maybe?) don't need to expand all drivers to tell you if
the watchdog timer is stoppable, just assert that the stop callback
fails which IMHO is easier and more natural.

>  
>  out_stop:
>  	mutex_unlock(&wddev->lock);
> @@ -194,7 +224,38 @@ out_status:
>  static int watchdog_set_timeout(struct watchdog_device *wddev,
>  							unsigned int timeout)
>  {
> -	int err;
> +	int err = 0;
> +
> +	if (wddev->hw_max_timeout) {
> +		int hw_timeout;
> +		/*
> +		 * We can't support too short timeout values. There is
> +		 * really no maximu however, anything longer than HW
s/maximu/maximum/

> +		 * maximum will be supported by the watchdog core on
> +		 * behalf of the actual HW.
> +		 */
> +		if (timeout < wddev->min_timeout)
> +			return -EINVAL;
Is it save to read min_timout without holding the lock?
> +
> +		mutex_lock(&wddev->lock);
> +		if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
> +			err = -ENODEV;
> +			goto out_timeout;
> +		}
> +
> +		if (timeout * HZ > wddev->hw_max_timeout)
> +			schedule_delayed_work(&wddev->work,
> +					wddev->hw_heartbeat);
> +
> +		hw_timeout = min(timeout, wddev->hw_max_timeout / HZ);
If you'd keep hw_max_timeout in seconds (i.e. what is used for timeout)
you wouldn't need to divide here. If my watchdog has hw_max_timeout =
0.5s then you do wddev->ops->set_timeout(0) here.

> +		if (wddev->info->options & WDIOF_SETTIMEOUT)
> +			err = wddev->ops->set_timeout(wddev, hw_timeout);
The case of not having WDIOF_SETTIMEOUT isn't handled here, right? Other
places check for WDIOF_SETTIMEOUT and wddev->ops->set_timeout?!

> +
> +		if (hw_timeout < timeout)
> +			wddev->timeout = timeout;
What is the semantic of wddev->timeout? Does it hold the value
set by userspace or the value the hardware is set to? You'd need to
clarify that by adding more comments. Is this if correct?

> +
> +		goto out_timeout;
> +	}
>  
>  	if ((wddev->ops->set_timeout == NULL) ||
>  	    !(wddev->info->options & WDIOF_SETTIMEOUT))
> diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
> index 395b70e..027c99d 100644
> --- a/include/linux/watchdog.h
> +++ b/include/linux/watchdog.h
> @@ -12,6 +12,7 @@
>  #include <linux/bitops.h>
>  #include <linux/device.h>
>  #include <linux/cdev.h>
> +#include <linux/workqueue.h>
>  #include <uapi/linux/watchdog.h>
>  
>  struct watchdog_ops;
> @@ -62,9 +63,13 @@ struct watchdog_ops {
>   * @timeout:	The watchdog devices timeout value.
>   * @min_timeout:The watchdog devices minimum timeout value.
>   * @max_timeout:The watchdog devices maximum timeout value.
> + * @hw_max_timeout:The watchdog hardware maximum timeout value.
> + * @hw_heartbeat:Time interval in HW between timer pings.
>   * @driver-data:Pointer to the drivers private data.
>   * @lock:	Lock for watchdog core internal use only.
> + * @work:	Worker used to provide longer timeouts than hardware supports.
>   * @status:	Field that contains the devices internal status bits.
> + * @hw_features:Feature bits describing how the watchdog HW works.
>   *
>   * The watchdog_device structure contains all information about a
>   * watchdog timer device.
> @@ -86,8 +91,12 @@ struct watchdog_device {
>  	unsigned int timeout;
>  	unsigned int min_timeout;
>  	unsigned int max_timeout;
> +	unsigned int hw_max_timeout; /* in jiffies */
> +	unsigned int hw_heartbeat; /* in jiffies */
> +	unsigned long int expires; /* for keepalive worker */
>  	void *driver_data;
>  	struct mutex lock;
> +	struct delayed_work work;
I'd group things together here that belong together.

>  	unsigned long status;
>  /* Bit numbers for status flags */
>  #define WDOG_ACTIVE		0	/* Is the watchdog running/active */
> @@ -95,6 +104,14 @@ struct watchdog_device {
>  #define WDOG_ALLOW_RELEASE	2	/* Did we receive the magic char ? */
>  #define WDOG_NO_WAY_OUT		3	/* Is 'nowayout' feature set ? */
>  #define WDOG_UNREGISTERED	4	/* Has the device been unregistered */
> +
> +/* Bits describing features supported by the HW */
> +	unsigned long hw_features;
> +
> +/* Can the watchdog be stopped and started */
> +#define WDOG_HW_IS_STOPPABLE		BIT(0)
> +/* Is the watchdog already running when the driver starts up */
> +#define WDOG_HW_RUNNING_AT_BOOT		BIT(1)
>  };
>  
>  #define WATCHDOG_NOWAYOUT		IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
> @@ -120,6 +137,11 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
>  		(t < wdd->min_timeout || t > wdd->max_timeout));
>  }
>  
> +static inline bool watchdog_is_stoppable(struct watchdog_device *wdd)
> +{
> +	return wdd->hw_features & WDOG_HW_IS_STOPPABLE;
> +}
> +
>  /* Use the following functions to manipulate watchdog driver specific data */
>  static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
>  {
> @@ -134,6 +156,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
>  /* drivers/watchdog/watchdog_core.c */
>  extern int watchdog_init_timeout(struct watchdog_device *wdd,
>  				  unsigned int timeout_parm, struct device *dev);
> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev);
This needs commenting and probably embrace watchdog_init_timeout
properly. i.e. remove watchdog_init_timeout.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
  2015-05-04  7:58     ` Uwe Kleine-König
@ 2015-05-04  9:40       ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-04  9:40 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

Hi Uwe

On 04.05.2015 10:58, Uwe Kleine-König wrote:
> Hello Timo,
>
> On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
>> There is a great deal of diversity in the watchdog hardware found on
>> different devices. Differen hardware have different contstraints on
> s/Differen/Different/; s/contstraints/constraints/
>
>> them, many of the constraints that are excessively difficult for the
>> user space to satisfy.
>>
>> One such constraint is the length of the timeout value, which in many
>> cases can be just a few seconds. Drivers are creating ad hoc solutions
>> with timers and workqueues to extend the timeout in order to give user
>> space more time between updates. Looking at the drivers it is clear
>> that this has resulted to a lot of duplicate code.
>>
>> Add an extension to the watchdog kernel API that allows the driver to
>> describe tis HW constraints to the watchdog code. A kernel worker in
> s/tis/its/
>

I thought that I ran ispell at least on the commit message, but maybe 
not on all commits.. I'll try to collect all these fixes to the next 
version.

>> the core is then used to extend the watchdog timeout on behalf of the
>> user space. This allows the drivers to be simplified as core takes
>> over the timer extending.
> In general a nice idea.
>
>> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
>> ---
>>   drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>>   drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>>   include/linux/watchdog.h         |  23 +++++++++
>>   3 files changed, 189 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
>> index cec9b55..fd12489 100644
>> --- a/drivers/watchdog/watchdog_core.c
>> +++ b/drivers/watchdog/watchdog_core.c
>> @@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
>>   EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>>
>>   /**
>> + * watchdog_init_parms() - initialize generic watchdog parameters
>> + * @wdd: Watchdog device to operate
>> + * @dev: Device that stores the device tree properties
>> + *
>> + * Initialize the generic timeout parameters. The driver needs to set
>> + * hw_features bitmask from @wdd prior calling this function in order
>> + * to ensure the core knows how to handle the HW.
>> + *
>> + * A zero is returned on success and -EINVAL for failure.
>> + */
>> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
>> +{
>> +	int ret = 0;
>> +
>> +	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
>> +	if (ret < 0)
>> +		return ret;
> As Guenter pointed out, using wdd->timeout is unfortunate here.
>

Yeah, I'll come up with something better for the next version.

>> +
>> +	/*
>> +	 * Max HW timeout needs to be set so that core knows when to
>> +	 * use a kernel worker to support longer watchdog timeouts
>> +	 */
>> +	if (!wdd->hw_max_timeout)
>> +		return -EINVAL;
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(watchdog_init_params);
>> +
>> +static void watchdog_worker(struct work_struct *work)
>> +{
>> +	struct watchdog_device *wdd = container_of(to_delayed_work(work),
>> +						struct watchdog_device, work);
> please align the follow-up line to the opening (.
>

ok

>> +	bool boot_keepalive;
>> +	bool active_keepalive;
>> +
>> +	mutex_lock(&wdd->lock);
>> +
>> +	boot_keepalive = !watchdog_active(wdd) &&
> I'm not sure about the semantic of watchdog_active here. Would be nice
> to clearify this. Does it mean /dev/watchdog is open, or the watchdog is
> running? Maybe introduce watchdog_running(wdd) and watchdog_opened(wdd)?

watchdog_active() simply means the device is open by user and we expect 
the HW to be running. What is a bit confusing here is that if watchdog 
is closed by the user, according the variable names here, it goes back 
to "boot_keepalive" mode. Maybe it would be better to rename the 
variable as inactive_keepalive instead.

 > Is this related to the topic of this patch? You didn't mention anything
 > in the commit log about the time until userspace comes up.
 >

It's the next patch in this series that introduces the early_timeout_sec 
logic. I deliberately splitted that functionality out from this patch to 
not confuse the new feature with the change of the existing API.

>> +		!watchdog_is_stoppable(wdd);
>> +
>> +	active_keepalive = watchdog_active(wdd) &&
>> +		wdd->hw_max_timeout < wdd->timeout * HZ;
>> +
>> +	if (time_after(jiffies, wdd->expires) && watchdog_active(wdd)) {
>> +		pr_crit("I will reset your machine !\n");
>> +		mutex_unlock(&wdd->lock);
>> +		return;
>> +	}
> I'd replace the pr_crit by a code comment. Also "I will reset your
> machine!" is wrong, the core just stops petting the watchdog.

Yeah, this is something that kept repeating with some of the drivers 
that had custom keepalive code with them. I'm fine with replacing it 
with a commect.

> If userspace comes in in time there is no harm, right?

Yep, indeed.

>
>> +	if (boot_keepalive || active_keepalive) {
>> +		if (wdd->ops->ping)
>> +			wdd->ops->ping(wdd);
>> +		else
>> +			wdd->ops->start(wdd);
> if (boot_keepalive || active_keepalive)
> 	_watchdog_ping(wdd);
>

ack.

>> +
>> +		schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
> s/  / /; Would it be sensible to assume hw_heartbeat to be timeout / 2
> always?
>

Timeout variable stores the user space timeout value, which might be 
different to what hardware is capable of. I'd better document that 
change in the headers at least.

>> +	}
>> +
>> +	mutex_unlock(&wdd->lock);
>> +}
>> +
>> +static int prepare_watchdog(struct watchdog_device *wdd)
>> +{
>> +	int err = 0;
>> +
>> +	/* Stop the watchdog now before user space opens the device */
>> +	if (watchdog_is_stoppable(wdd) &&
>> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
>> +		err = wdd->ops->stop(wdd);
>> +
>> +	} else if (!watchdog_is_stoppable(wdd) &&
>> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
>> +		/*
>> +		 * Can't stop it, use a delayed worker to tick it
>> +		 * until it's open by user space
>> +		 */
> I'd add a ping here because if the driver comes up a big part of the
> initial timeout setup by the bootloader might already be over.

Agreed.

> Note that at least for the omap watchdog driver the assignment to
> timeout doesn't relate in any way to the timeout actually setup in
> hardware.

The timeout value should match what is setup in hardware, unless user is 
requesting something beyond hardware limits, in which case we use the 
worker to extend the limit. I'm not sure how that is relevant at here 
though..

>> +		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
>> +	}
>> +	return err;
>> +}
>> +
>> +/**
>>    * watchdog_register_device() - register a watchdog device
>>    * @wdd: watchdog device
>>    *
>> @@ -156,13 +239,24 @@ int watchdog_register_device(struct watchdog_device *wdd)
>>   	wdd->dev = device_create(watchdog_class, wdd->parent, devno,
>>   					NULL, "watchdog%d", wdd->id);
>>   	if (IS_ERR(wdd->dev)) {
>> -		watchdog_dev_unregister(wdd);
>> -		ida_simple_remove(&watchdog_ida, id);
>>   		ret = PTR_ERR(wdd->dev);
>> -		return ret;
>> +		goto dev_create_fail;
>> +	}
>> +
>> +	INIT_DELAYED_WORK(&wdd->work, watchdog_worker);
>> +
>> +	if (wdd->hw_max_timeout) {
>> +		ret = prepare_watchdog(wdd);
>> +		if (ret)
>> +			goto dev_create_fail;
> 	} else {
> 		dev_warn("Incomplete watchdog driver implementation, please report or fix\n")

Yes, this would probably speed up the rate drivers are converting to use 
the new API extensions. I'll add it.

>>   	}
>>
>>   	return 0;
>> +
>> +dev_create_fail:
>> +	watchdog_dev_unregister(wdd);
>> +	ida_simple_remove(&watchdog_ida, id);
>> +	return ret;
>>   }
>>   EXPORT_SYMBOL_GPL(watchdog_register_device);
>>
>> @@ -181,6 +275,7 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
>>   	if (wdd == NULL)
>>   		return;
>>
>> +	cancel_delayed_work_sync(&wdd->work);
>>   	devno = wdd->cdev.dev;
>>   	ret = watchdog_dev_unregister(wdd);
>>   	if (ret)
>> diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
>> index 6aaefba..04ac68c 100644
>> --- a/drivers/watchdog/watchdog_dev.c
>> +++ b/drivers/watchdog/watchdog_dev.c
>> @@ -49,6 +49,14 @@ static dev_t watchdog_devt;
>>   /* the watchdog device behind /dev/watchdog */
>>   static struct watchdog_device *old_wdd;
>>
>> +static int _watchdog_ping(struct watchdog_device *wddev)
>> +{
>> +	if (wddev->ops->ping)
>> +		return wddev->ops->ping(wddev);  /* ping the watchdog */
>> +	else
>> +		return wddev->ops->start(wddev); /* restart watchdog */
>> +}
>> +
>>   /*
>>    *	watchdog_ping: ping the watchdog.
>>    *	@wddev: the watchdog device to ping
>> @@ -73,10 +81,13 @@ static int watchdog_ping(struct watchdog_device *wddev)
>>   	if (!watchdog_active(wddev))
>>   		goto out_ping;
>>
>> -	if (wddev->ops->ping)
>> -		err = wddev->ops->ping(wddev);  /* ping the watchdog */
>> -	else
>> -		err = wddev->ops->start(wddev); /* restart watchdog */
>> +	err = _watchdog_ping(wddev);
>> +
>> +	if (wddev->hw_max_timeout &&
>> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
>> +		wddev->expires = jiffies + wddev->timeout * HZ;
>> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
>> +	}
>>
>>   out_ping:
>>   	mutex_unlock(&wddev->lock);
>> @@ -110,6 +121,13 @@ static int watchdog_start(struct watchdog_device *wddev)
>>   	if (err == 0)
>>   		set_bit(WDOG_ACTIVE, &wddev->status);
>>
>> +	if (wddev->hw_max_timeout &&
>> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
>> +		wddev->expires = jiffies + wddev->timeout * HZ;
>> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
>> +	} else
>> +		cancel_delayed_work(&wddev->work);
>> +
>>   out_start:
>>   	mutex_unlock(&wddev->lock);
>>   	return err;
>> @@ -145,9 +163,21 @@ static int watchdog_stop(struct watchdog_device *wddev)
>>   		goto out_stop;
>>   	}
>>
>> -	err = wddev->ops->stop(wddev);
>> -	if (err == 0)
>> +	if (!wddev->hw_max_timeout || watchdog_is_stoppable(wddev)) {
>> +		cancel_delayed_work(&wddev->work);
>> +		err = wddev->ops->stop(wddev);
>> +		if (err == 0)
>> +			clear_bit(WDOG_ACTIVE, &wddev->status);
>> +	} else {
>> +		/* Unstoppable watchdogs need the worker to keep them alive */
>>   		clear_bit(WDOG_ACTIVE, &wddev->status);
>> +		/*
>> +		 * Ping it once as we don't know how much time there
>> +		 * is left in the watchdog timer.
>> +		 */
>> +		err = _watchdog_ping(wddev);
>> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
>> +	}
> Why not do:
>
> 	err = wddev->ops->stop(wddev);
> 	clear_bit(WDOG_ACTIVE, &wddev->status);
> 	if (err != 0) {
> 		err = _watchdog_ping(wddev);
> 		if (err) {
> 			dev_crit(...);
> 		schedule_delayed_work(...);
> 	}
>
> This way you (maybe?) don't need to expand all drivers to tell you if
> the watchdog timer is stoppable, just assert that the stop callback
> fails which IMHO is easier and more natural.
>

Currently the watchdog API mandates that stop() function must be 
implemented and the drivers that can't stop the actual HW just set up 
their own tricks to emulate stopped hardware. What we could do is to 
require that drivers using the new API return error on their stop 
function in case they can't stop the hardware. I never thought it this 
way. This would indeed reduce 50% of the proposed HW feature bits.

I'll look into this. The idea is good.

>>
>>   out_stop:
>>   	mutex_unlock(&wddev->lock);
>> @@ -194,7 +224,38 @@ out_status:
>>   static int watchdog_set_timeout(struct watchdog_device *wddev,
>>   							unsigned int timeout)
>>   {
>> -	int err;
>> +	int err = 0;
>> +
>> +	if (wddev->hw_max_timeout) {
>> +		int hw_timeout;
>> +		/*
>> +		 * We can't support too short timeout values. There is
>> +		 * really no maximu however, anything longer than HW
> s/maximu/maximum/
>
>> +		 * maximum will be supported by the watchdog core on
>> +		 * behalf of the actual HW.
>> +		 */
>> +		if (timeout < wddev->min_timeout)
>> +			return -EINVAL;
> Is it save to read min_timout without holding the lock?

No, I'd say it is a bug, even though I don't think we are likely to race 
here. I see no reason why this shouldn't be covered with the lock.

>> +
>> +		mutex_lock(&wddev->lock);
>> +		if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
>> +			err = -ENODEV;
>> +			goto out_timeout;
>> +		}
>> +
>> +		if (timeout * HZ > wddev->hw_max_timeout)
>> +			schedule_delayed_work(&wddev->work,
>> +					wddev->hw_heartbeat);
>> +
>> +		hw_timeout = min(timeout, wddev->hw_max_timeout / HZ);
> If you'd keep hw_max_timeout in seconds (i.e. what is used for timeout)
> you wouldn't need to divide here. If my watchdog has hw_max_timeout =
> 0.5s then you do wddev->ops->set_timeout(0) here.
>

If the maximum supported timeout is less than a second, there is no 
really point in having WDIOF_SETTIMEOUT set in the driver at all. There 
is no way user can set up the timeout through the current API. The 
hw_max_timeout needs to be in jiffies in case the HW maximum is less 
than a second.

>> +		if (wddev->info->options & WDIOF_SETTIMEOUT)
>> +			err = wddev->ops->set_timeout(wddev, hw_timeout);
> The case of not having WDIOF_SETTIMEOUT isn't handled here, right? Other
> places check for WDIOF_SETTIMEOUT and wddev->ops->set_timeout?!
>

Good point. I didn't actually test this with any drivers that didn't 
have WDIOF_SETTIMEOUT set. For example with at91sam9_wdt 
WDIOF_SETTIMEOUT is set but only because the driver implemented its own 
fake timeouts as the hardware cannot be set. So I need to remove 
WDIOF_SETTIMEOUT bit from the driver and then handle the timeout 
handling here properly. And in that case, what we can do here is to 
either have a timeout that is exactly same what is supported by the 
hardware or use the worker to extend the timeout. Can't do anything else.

Good catch!

>> +
>> +		if (hw_timeout < timeout)
>> +			wddev->timeout = timeout;
> What is the semantic of wddev->timeout? Does it hold the value
> set by userspace or the value the hardware is set to? You'd need to
> clarify that by adding more comments. Is this if correct?
>

It stores the timeout interval we are expecting from the user space. 
Hardware timeout can be shorter. I'll document this better to the next 
patch version.

>> +
>> +		goto out_timeout;
>> +	}
>>
>>   	if ((wddev->ops->set_timeout == NULL) ||
>>   	    !(wddev->info->options & WDIOF_SETTIMEOUT))
>> diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
>> index 395b70e..027c99d 100644
>> --- a/include/linux/watchdog.h
>> +++ b/include/linux/watchdog.h
>> @@ -12,6 +12,7 @@
>>   #include <linux/bitops.h>
>>   #include <linux/device.h>
>>   #include <linux/cdev.h>
>> +#include <linux/workqueue.h>
>>   #include <uapi/linux/watchdog.h>
>>
>>   struct watchdog_ops;
>> @@ -62,9 +63,13 @@ struct watchdog_ops {
>>    * @timeout:	The watchdog devices timeout value.
>>    * @min_timeout:The watchdog devices minimum timeout value.
>>    * @max_timeout:The watchdog devices maximum timeout value.
>> + * @hw_max_timeout:The watchdog hardware maximum timeout value.
>> + * @hw_heartbeat:Time interval in HW between timer pings.
>>    * @driver-data:Pointer to the drivers private data.
>>    * @lock:	Lock for watchdog core internal use only.
>> + * @work:	Worker used to provide longer timeouts than hardware supports.
>>    * @status:	Field that contains the devices internal status bits.
>> + * @hw_features:Feature bits describing how the watchdog HW works.
>>    *
>>    * The watchdog_device structure contains all information about a
>>    * watchdog timer device.
>> @@ -86,8 +91,12 @@ struct watchdog_device {
>>   	unsigned int timeout;
>>   	unsigned int min_timeout;
>>   	unsigned int max_timeout;
>> +	unsigned int hw_max_timeout; /* in jiffies */
>> +	unsigned int hw_heartbeat; /* in jiffies */
>> +	unsigned long int expires; /* for keepalive worker */
>>   	void *driver_data;
>>   	struct mutex lock;
>> +	struct delayed_work work;
> I'd group things together here that belong together.
>

How would you like to have the "belong together" criteria to be defined? 
I now have timeout values together and then other structures in another 
group as I found that logical. I'm fine with other criteria too, if you 
specify one to me.

>>   	unsigned long status;
>>   /* Bit numbers for status flags */
>>   #define WDOG_ACTIVE		0	/* Is the watchdog running/active */
>> @@ -95,6 +104,14 @@ struct watchdog_device {
>>   #define WDOG_ALLOW_RELEASE	2	/* Did we receive the magic char ? */
>>   #define WDOG_NO_WAY_OUT		3	/* Is 'nowayout' feature set ? */
>>   #define WDOG_UNREGISTERED	4	/* Has the device been unregistered */
>> +
>> +/* Bits describing features supported by the HW */
>> +	unsigned long hw_features;
>> +
>> +/* Can the watchdog be stopped and started */
>> +#define WDOG_HW_IS_STOPPABLE		BIT(0)
>> +/* Is the watchdog already running when the driver starts up */
>> +#define WDOG_HW_RUNNING_AT_BOOT		BIT(1)
>>   };
>>
>>   #define WATCHDOG_NOWAYOUT		IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
>> @@ -120,6 +137,11 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
>>   		(t < wdd->min_timeout || t > wdd->max_timeout));
>>   }
>>
>> +static inline bool watchdog_is_stoppable(struct watchdog_device *wdd)
>> +{
>> +	return wdd->hw_features & WDOG_HW_IS_STOPPABLE;
>> +}
>> +
>>   /* Use the following functions to manipulate watchdog driver specific data */
>>   static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
>>   {
>> @@ -134,6 +156,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
>>   /* drivers/watchdog/watchdog_core.c */
>>   extern int watchdog_init_timeout(struct watchdog_device *wdd,
>>   				  unsigned int timeout_parm, struct device *dev);
>> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev);
> This needs commenting and probably embrace watchdog_init_timeout
> properly. i.e. remove watchdog_init_timeout.

Yes, the comments are now mostly above the function implementation. I 
can add more there if you feel its inadequate.

watchdog_init_timeout() can be removed once all drivers are converted to 
use init_watchdog_params()


Thank you for your valuable review.

-Timo

>
> Best regards
> Uwe
>


^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
@ 2015-05-04  9:40       ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-04  9:40 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Uwe

On 04.05.2015 10:58, Uwe Kleine-K?nig wrote:
> Hello Timo,
>
> On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
>> There is a great deal of diversity in the watchdog hardware found on
>> different devices. Differen hardware have different contstraints on
> s/Differen/Different/; s/contstraints/constraints/
>
>> them, many of the constraints that are excessively difficult for the
>> user space to satisfy.
>>
>> One such constraint is the length of the timeout value, which in many
>> cases can be just a few seconds. Drivers are creating ad hoc solutions
>> with timers and workqueues to extend the timeout in order to give user
>> space more time between updates. Looking at the drivers it is clear
>> that this has resulted to a lot of duplicate code.
>>
>> Add an extension to the watchdog kernel API that allows the driver to
>> describe tis HW constraints to the watchdog code. A kernel worker in
> s/tis/its/
>

I thought that I ran ispell at least on the commit message, but maybe 
not on all commits.. I'll try to collect all these fixes to the next 
version.

>> the core is then used to extend the watchdog timeout on behalf of the
>> user space. This allows the drivers to be simplified as core takes
>> over the timer extending.
> In general a nice idea.
>
>> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
>> ---
>>   drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>>   drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>>   include/linux/watchdog.h         |  23 +++++++++
>>   3 files changed, 189 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
>> index cec9b55..fd12489 100644
>> --- a/drivers/watchdog/watchdog_core.c
>> +++ b/drivers/watchdog/watchdog_core.c
>> @@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
>>   EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>>
>>   /**
>> + * watchdog_init_parms() - initialize generic watchdog parameters
>> + * @wdd: Watchdog device to operate
>> + * @dev: Device that stores the device tree properties
>> + *
>> + * Initialize the generic timeout parameters. The driver needs to set
>> + * hw_features bitmask from @wdd prior calling this function in order
>> + * to ensure the core knows how to handle the HW.
>> + *
>> + * A zero is returned on success and -EINVAL for failure.
>> + */
>> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
>> +{
>> +	int ret = 0;
>> +
>> +	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
>> +	if (ret < 0)
>> +		return ret;
> As Guenter pointed out, using wdd->timeout is unfortunate here.
>

Yeah, I'll come up with something better for the next version.

>> +
>> +	/*
>> +	 * Max HW timeout needs to be set so that core knows when to
>> +	 * use a kernel worker to support longer watchdog timeouts
>> +	 */
>> +	if (!wdd->hw_max_timeout)
>> +		return -EINVAL;
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(watchdog_init_params);
>> +
>> +static void watchdog_worker(struct work_struct *work)
>> +{
>> +	struct watchdog_device *wdd = container_of(to_delayed_work(work),
>> +						struct watchdog_device, work);
> please align the follow-up line to the opening (.
>

ok

>> +	bool boot_keepalive;
>> +	bool active_keepalive;
>> +
>> +	mutex_lock(&wdd->lock);
>> +
>> +	boot_keepalive = !watchdog_active(wdd) &&
> I'm not sure about the semantic of watchdog_active here. Would be nice
> to clearify this. Does it mean /dev/watchdog is open, or the watchdog is
> running? Maybe introduce watchdog_running(wdd) and watchdog_opened(wdd)?

watchdog_active() simply means the device is open by user and we expect 
the HW to be running. What is a bit confusing here is that if watchdog 
is closed by the user, according the variable names here, it goes back 
to "boot_keepalive" mode. Maybe it would be better to rename the 
variable as inactive_keepalive instead.

 > Is this related to the topic of this patch? You didn't mention anything
 > in the commit log about the time until userspace comes up.
 >

It's the next patch in this series that introduces the early_timeout_sec 
logic. I deliberately splitted that functionality out from this patch to 
not confuse the new feature with the change of the existing API.

>> +		!watchdog_is_stoppable(wdd);
>> +
>> +	active_keepalive = watchdog_active(wdd) &&
>> +		wdd->hw_max_timeout < wdd->timeout * HZ;
>> +
>> +	if (time_after(jiffies, wdd->expires) && watchdog_active(wdd)) {
>> +		pr_crit("I will reset your machine !\n");
>> +		mutex_unlock(&wdd->lock);
>> +		return;
>> +	}
> I'd replace the pr_crit by a code comment. Also "I will reset your
> machine!" is wrong, the core just stops petting the watchdog.

Yeah, this is something that kept repeating with some of the drivers 
that had custom keepalive code with them. I'm fine with replacing it 
with a commect.

> If userspace comes in in time there is no harm, right?

Yep, indeed.

>
>> +	if (boot_keepalive || active_keepalive) {
>> +		if (wdd->ops->ping)
>> +			wdd->ops->ping(wdd);
>> +		else
>> +			wdd->ops->start(wdd);
> if (boot_keepalive || active_keepalive)
> 	_watchdog_ping(wdd);
>

ack.

>> +
>> +		schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
> s/  / /; Would it be sensible to assume hw_heartbeat to be timeout / 2
> always?
>

Timeout variable stores the user space timeout value, which might be 
different to what hardware is capable of. I'd better document that 
change in the headers at least.

>> +	}
>> +
>> +	mutex_unlock(&wdd->lock);
>> +}
>> +
>> +static int prepare_watchdog(struct watchdog_device *wdd)
>> +{
>> +	int err = 0;
>> +
>> +	/* Stop the watchdog now before user space opens the device */
>> +	if (watchdog_is_stoppable(wdd) &&
>> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
>> +		err = wdd->ops->stop(wdd);
>> +
>> +	} else if (!watchdog_is_stoppable(wdd) &&
>> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
>> +		/*
>> +		 * Can't stop it, use a delayed worker to tick it
>> +		 * until it's open by user space
>> +		 */
> I'd add a ping here because if the driver comes up a big part of the
> initial timeout setup by the bootloader might already be over.

Agreed.

> Note that at least for the omap watchdog driver the assignment to
> timeout doesn't relate in any way to the timeout actually setup in
> hardware.

The timeout value should match what is setup in hardware, unless user is 
requesting something beyond hardware limits, in which case we use the 
worker to extend the limit. I'm not sure how that is relevant at here 
though..

>> +		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
>> +	}
>> +	return err;
>> +}
>> +
>> +/**
>>    * watchdog_register_device() - register a watchdog device
>>    * @wdd: watchdog device
>>    *
>> @@ -156,13 +239,24 @@ int watchdog_register_device(struct watchdog_device *wdd)
>>   	wdd->dev = device_create(watchdog_class, wdd->parent, devno,
>>   					NULL, "watchdog%d", wdd->id);
>>   	if (IS_ERR(wdd->dev)) {
>> -		watchdog_dev_unregister(wdd);
>> -		ida_simple_remove(&watchdog_ida, id);
>>   		ret = PTR_ERR(wdd->dev);
>> -		return ret;
>> +		goto dev_create_fail;
>> +	}
>> +
>> +	INIT_DELAYED_WORK(&wdd->work, watchdog_worker);
>> +
>> +	if (wdd->hw_max_timeout) {
>> +		ret = prepare_watchdog(wdd);
>> +		if (ret)
>> +			goto dev_create_fail;
> 	} else {
> 		dev_warn("Incomplete watchdog driver implementation, please report or fix\n")

Yes, this would probably speed up the rate drivers are converting to use 
the new API extensions. I'll add it.

>>   	}
>>
>>   	return 0;
>> +
>> +dev_create_fail:
>> +	watchdog_dev_unregister(wdd);
>> +	ida_simple_remove(&watchdog_ida, id);
>> +	return ret;
>>   }
>>   EXPORT_SYMBOL_GPL(watchdog_register_device);
>>
>> @@ -181,6 +275,7 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
>>   	if (wdd == NULL)
>>   		return;
>>
>> +	cancel_delayed_work_sync(&wdd->work);
>>   	devno = wdd->cdev.dev;
>>   	ret = watchdog_dev_unregister(wdd);
>>   	if (ret)
>> diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
>> index 6aaefba..04ac68c 100644
>> --- a/drivers/watchdog/watchdog_dev.c
>> +++ b/drivers/watchdog/watchdog_dev.c
>> @@ -49,6 +49,14 @@ static dev_t watchdog_devt;
>>   /* the watchdog device behind /dev/watchdog */
>>   static struct watchdog_device *old_wdd;
>>
>> +static int _watchdog_ping(struct watchdog_device *wddev)
>> +{
>> +	if (wddev->ops->ping)
>> +		return wddev->ops->ping(wddev);  /* ping the watchdog */
>> +	else
>> +		return wddev->ops->start(wddev); /* restart watchdog */
>> +}
>> +
>>   /*
>>    *	watchdog_ping: ping the watchdog.
>>    *	@wddev: the watchdog device to ping
>> @@ -73,10 +81,13 @@ static int watchdog_ping(struct watchdog_device *wddev)
>>   	if (!watchdog_active(wddev))
>>   		goto out_ping;
>>
>> -	if (wddev->ops->ping)
>> -		err = wddev->ops->ping(wddev);  /* ping the watchdog */
>> -	else
>> -		err = wddev->ops->start(wddev); /* restart watchdog */
>> +	err = _watchdog_ping(wddev);
>> +
>> +	if (wddev->hw_max_timeout &&
>> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
>> +		wddev->expires = jiffies + wddev->timeout * HZ;
>> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
>> +	}
>>
>>   out_ping:
>>   	mutex_unlock(&wddev->lock);
>> @@ -110,6 +121,13 @@ static int watchdog_start(struct watchdog_device *wddev)
>>   	if (err == 0)
>>   		set_bit(WDOG_ACTIVE, &wddev->status);
>>
>> +	if (wddev->hw_max_timeout &&
>> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
>> +		wddev->expires = jiffies + wddev->timeout * HZ;
>> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
>> +	} else
>> +		cancel_delayed_work(&wddev->work);
>> +
>>   out_start:
>>   	mutex_unlock(&wddev->lock);
>>   	return err;
>> @@ -145,9 +163,21 @@ static int watchdog_stop(struct watchdog_device *wddev)
>>   		goto out_stop;
>>   	}
>>
>> -	err = wddev->ops->stop(wddev);
>> -	if (err == 0)
>> +	if (!wddev->hw_max_timeout || watchdog_is_stoppable(wddev)) {
>> +		cancel_delayed_work(&wddev->work);
>> +		err = wddev->ops->stop(wddev);
>> +		if (err == 0)
>> +			clear_bit(WDOG_ACTIVE, &wddev->status);
>> +	} else {
>> +		/* Unstoppable watchdogs need the worker to keep them alive */
>>   		clear_bit(WDOG_ACTIVE, &wddev->status);
>> +		/*
>> +		 * Ping it once as we don't know how much time there
>> +		 * is left in the watchdog timer.
>> +		 */
>> +		err = _watchdog_ping(wddev);
>> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
>> +	}
> Why not do:
>
> 	err = wddev->ops->stop(wddev);
> 	clear_bit(WDOG_ACTIVE, &wddev->status);
> 	if (err != 0) {
> 		err = _watchdog_ping(wddev);
> 		if (err) {
> 			dev_crit(...);
> 		schedule_delayed_work(...);
> 	}
>
> This way you (maybe?) don't need to expand all drivers to tell you if
> the watchdog timer is stoppable, just assert that the stop callback
> fails which IMHO is easier and more natural.
>

Currently the watchdog API mandates that stop() function must be 
implemented and the drivers that can't stop the actual HW just set up 
their own tricks to emulate stopped hardware. What we could do is to 
require that drivers using the new API return error on their stop 
function in case they can't stop the hardware. I never thought it this 
way. This would indeed reduce 50% of the proposed HW feature bits.

I'll look into this. The idea is good.

>>
>>   out_stop:
>>   	mutex_unlock(&wddev->lock);
>> @@ -194,7 +224,38 @@ out_status:
>>   static int watchdog_set_timeout(struct watchdog_device *wddev,
>>   							unsigned int timeout)
>>   {
>> -	int err;
>> +	int err = 0;
>> +
>> +	if (wddev->hw_max_timeout) {
>> +		int hw_timeout;
>> +		/*
>> +		 * We can't support too short timeout values. There is
>> +		 * really no maximu however, anything longer than HW
> s/maximu/maximum/
>
>> +		 * maximum will be supported by the watchdog core on
>> +		 * behalf of the actual HW.
>> +		 */
>> +		if (timeout < wddev->min_timeout)
>> +			return -EINVAL;
> Is it save to read min_timout without holding the lock?

No, I'd say it is a bug, even though I don't think we are likely to race 
here. I see no reason why this shouldn't be covered with the lock.

>> +
>> +		mutex_lock(&wddev->lock);
>> +		if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
>> +			err = -ENODEV;
>> +			goto out_timeout;
>> +		}
>> +
>> +		if (timeout * HZ > wddev->hw_max_timeout)
>> +			schedule_delayed_work(&wddev->work,
>> +					wddev->hw_heartbeat);
>> +
>> +		hw_timeout = min(timeout, wddev->hw_max_timeout / HZ);
> If you'd keep hw_max_timeout in seconds (i.e. what is used for timeout)
> you wouldn't need to divide here. If my watchdog has hw_max_timeout =
> 0.5s then you do wddev->ops->set_timeout(0) here.
>

If the maximum supported timeout is less than a second, there is no 
really point in having WDIOF_SETTIMEOUT set in the driver at all. There 
is no way user can set up the timeout through the current API. The 
hw_max_timeout needs to be in jiffies in case the HW maximum is less 
than a second.

>> +		if (wddev->info->options & WDIOF_SETTIMEOUT)
>> +			err = wddev->ops->set_timeout(wddev, hw_timeout);
> The case of not having WDIOF_SETTIMEOUT isn't handled here, right? Other
> places check for WDIOF_SETTIMEOUT and wddev->ops->set_timeout?!
>

Good point. I didn't actually test this with any drivers that didn't 
have WDIOF_SETTIMEOUT set. For example with at91sam9_wdt 
WDIOF_SETTIMEOUT is set but only because the driver implemented its own 
fake timeouts as the hardware cannot be set. So I need to remove 
WDIOF_SETTIMEOUT bit from the driver and then handle the timeout 
handling here properly. And in that case, what we can do here is to 
either have a timeout that is exactly same what is supported by the 
hardware or use the worker to extend the timeout. Can't do anything else.

Good catch!

>> +
>> +		if (hw_timeout < timeout)
>> +			wddev->timeout = timeout;
> What is the semantic of wddev->timeout? Does it hold the value
> set by userspace or the value the hardware is set to? You'd need to
> clarify that by adding more comments. Is this if correct?
>

It stores the timeout interval we are expecting from the user space. 
Hardware timeout can be shorter. I'll document this better to the next 
patch version.

>> +
>> +		goto out_timeout;
>> +	}
>>
>>   	if ((wddev->ops->set_timeout == NULL) ||
>>   	    !(wddev->info->options & WDIOF_SETTIMEOUT))
>> diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
>> index 395b70e..027c99d 100644
>> --- a/include/linux/watchdog.h
>> +++ b/include/linux/watchdog.h
>> @@ -12,6 +12,7 @@
>>   #include <linux/bitops.h>
>>   #include <linux/device.h>
>>   #include <linux/cdev.h>
>> +#include <linux/workqueue.h>
>>   #include <uapi/linux/watchdog.h>
>>
>>   struct watchdog_ops;
>> @@ -62,9 +63,13 @@ struct watchdog_ops {
>>    * @timeout:	The watchdog devices timeout value.
>>    * @min_timeout:The watchdog devices minimum timeout value.
>>    * @max_timeout:The watchdog devices maximum timeout value.
>> + * @hw_max_timeout:The watchdog hardware maximum timeout value.
>> + * @hw_heartbeat:Time interval in HW between timer pings.
>>    * @driver-data:Pointer to the drivers private data.
>>    * @lock:	Lock for watchdog core internal use only.
>> + * @work:	Worker used to provide longer timeouts than hardware supports.
>>    * @status:	Field that contains the devices internal status bits.
>> + * @hw_features:Feature bits describing how the watchdog HW works.
>>    *
>>    * The watchdog_device structure contains all information about a
>>    * watchdog timer device.
>> @@ -86,8 +91,12 @@ struct watchdog_device {
>>   	unsigned int timeout;
>>   	unsigned int min_timeout;
>>   	unsigned int max_timeout;
>> +	unsigned int hw_max_timeout; /* in jiffies */
>> +	unsigned int hw_heartbeat; /* in jiffies */
>> +	unsigned long int expires; /* for keepalive worker */
>>   	void *driver_data;
>>   	struct mutex lock;
>> +	struct delayed_work work;
> I'd group things together here that belong together.
>

How would you like to have the "belong together" criteria to be defined? 
I now have timeout values together and then other structures in another 
group as I found that logical. I'm fine with other criteria too, if you 
specify one to me.

>>   	unsigned long status;
>>   /* Bit numbers for status flags */
>>   #define WDOG_ACTIVE		0	/* Is the watchdog running/active */
>> @@ -95,6 +104,14 @@ struct watchdog_device {
>>   #define WDOG_ALLOW_RELEASE	2	/* Did we receive the magic char ? */
>>   #define WDOG_NO_WAY_OUT		3	/* Is 'nowayout' feature set ? */
>>   #define WDOG_UNREGISTERED	4	/* Has the device been unregistered */
>> +
>> +/* Bits describing features supported by the HW */
>> +	unsigned long hw_features;
>> +
>> +/* Can the watchdog be stopped and started */
>> +#define WDOG_HW_IS_STOPPABLE		BIT(0)
>> +/* Is the watchdog already running when the driver starts up */
>> +#define WDOG_HW_RUNNING_AT_BOOT		BIT(1)
>>   };
>>
>>   #define WATCHDOG_NOWAYOUT		IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
>> @@ -120,6 +137,11 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
>>   		(t < wdd->min_timeout || t > wdd->max_timeout));
>>   }
>>
>> +static inline bool watchdog_is_stoppable(struct watchdog_device *wdd)
>> +{
>> +	return wdd->hw_features & WDOG_HW_IS_STOPPABLE;
>> +}
>> +
>>   /* Use the following functions to manipulate watchdog driver specific data */
>>   static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
>>   {
>> @@ -134,6 +156,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
>>   /* drivers/watchdog/watchdog_core.c */
>>   extern int watchdog_init_timeout(struct watchdog_device *wdd,
>>   				  unsigned int timeout_parm, struct device *dev);
>> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev);
> This needs commenting and probably embrace watchdog_init_timeout
> properly. i.e. remove watchdog_init_timeout.

Yes, the comments are now mostly above the function implementation. I 
can add more there if you feel its inadequate.

watchdog_init_timeout() can be removed once all drivers are converted to 
use init_watchdog_params()


Thank you for your valuable review.

-Timo

>
> Best regards
> Uwe
>

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
  2015-05-04  7:04         ` Uwe Kleine-König
@ 2015-05-04 10:06           ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-04 10:06 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

On 04.05.2015 10:04, Uwe Kleine-König wrote:
> Hello Timo,
>
> On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
>> Hi, 03.05.2015 21:56, Uwe Kleine-König wrote:
>>> On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
>>>> +static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
>>>> +{
>>>> +	void __iomem *base = wdev->base;
>>>> +
>>>> +	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
>>>> +}
>>> This isn't reliable. The sequence needed to enable the watchdog is
>>> 	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
>>> 	writel(0x4444, base + OMAP_WATCHDOG_SPR);
>>>
>>> The sequence to stop is:
>>> 	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
>>> 	writel(0x5555, base + OMAP_WATCHDOG_SPR);
>>>
>>> But:
>>>
>>> barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
>>> 44e35048: 00005555                                           UU..
>>> barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
>>> barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
>>> 44e35048: 00004444                                           DD..
>>>
>>> So the register contains 0x4444 but the timer doesn't run. So at best
>>> testing for 0x4444 is a good heuristic.
>>
>> Yeah.. I don't think we can get any better than that. Unless we
>> start checking the counter register and see whether it really counts
>> or not, and I think that's a bit overkill.. So I'd say we should be
>> safe when assuming bootloader is doing things correctly. Although,
>> we could add a comment to the code that the test may not be 100%
>> reliable in case the start sequence have not been issued properly.
>>
>> Thanks for pointing this out!
> It doesn't seem to much overhead to do:
>
> 	/*
> 	 * There is no register that tells us if the timer is running,
> 	 * so we have to resort to sample twice. The minimal frequency
> 	 * is 256 Hz (32768 Hz prescaled with 2**7).
> 	 */
> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> 	mdelay(4);
> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> 	return counter1 != counter2;
>
> I'd say it's even worth to do:
>
> 	cntrl = readl(base + OMAP_WATCHDOG_CNTRL);
> 	if (cntrl & (1 << 5))
> 		shift = (cntrl >> 2) & 0x7;
> 	else
> 		shift = 0;
> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> 	udelay(31 << shift);
> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> 	return counter1 != counter2;
>

I think it is very rare that the watchdog is running in any other than 
32kHz clock, so the above should be sufficient in any case. The worst 
case delay of 3968us is a bit long, but even that is happening only 
during probe. And vast majority of user are going to have the 31us 
delay, so that should be fine.

Thanks,
-Timo

> For some bonus points add some defines for the magic constants.
>
> This is save as the OMAP_WATCHDOG_CNTRL doesn't seem to accept reads
> while the counter is running. Maybe even this could be used to detect a
> running timer?:
>
>   - enable timer:
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0xbbbb
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
>
>   - read out WCLR
> 	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - write to WCLR
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35024 0x0
>
>   - check result; didn't work
> 	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - stop timer
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0xaaaa
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x5555
>
>   - recheck WCLR
> 	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - write to WCLR
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35024 0x0
>
>   - check result; write succeeded
> 	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000000                                           ....
>
> (This is was tested on an AM335x.)
>
> Best regards
> Uwe
>


^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
@ 2015-05-04 10:06           ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-04 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

On 04.05.2015 10:04, Uwe Kleine-K?nig wrote:
> Hello Timo,
>
> On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
>> Hi, 03.05.2015 21:56, Uwe Kleine-K?nig wrote:
>>> On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
>>>> +static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
>>>> +{
>>>> +	void __iomem *base = wdev->base;
>>>> +
>>>> +	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
>>>> +}
>>> This isn't reliable. The sequence needed to enable the watchdog is
>>> 	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
>>> 	writel(0x4444, base + OMAP_WATCHDOG_SPR);
>>>
>>> The sequence to stop is:
>>> 	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
>>> 	writel(0x5555, base + OMAP_WATCHDOG_SPR);
>>>
>>> But:
>>>
>>> barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
>>> 44e35048: 00005555                                           UU..
>>> barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
>>> barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
>>> 44e35048: 00004444                                           DD..
>>>
>>> So the register contains 0x4444 but the timer doesn't run. So at best
>>> testing for 0x4444 is a good heuristic.
>>
>> Yeah.. I don't think we can get any better than that. Unless we
>> start checking the counter register and see whether it really counts
>> or not, and I think that's a bit overkill.. So I'd say we should be
>> safe when assuming bootloader is doing things correctly. Although,
>> we could add a comment to the code that the test may not be 100%
>> reliable in case the start sequence have not been issued properly.
>>
>> Thanks for pointing this out!
> It doesn't seem to much overhead to do:
>
> 	/*
> 	 * There is no register that tells us if the timer is running,
> 	 * so we have to resort to sample twice. The minimal frequency
> 	 * is 256 Hz (32768 Hz prescaled with 2**7).
> 	 */
> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> 	mdelay(4);
> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> 	return counter1 != counter2;
>
> I'd say it's even worth to do:
>
> 	cntrl = readl(base + OMAP_WATCHDOG_CNTRL);
> 	if (cntrl & (1 << 5))
> 		shift = (cntrl >> 2) & 0x7;
> 	else
> 		shift = 0;
> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> 	udelay(31 << shift);
> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> 	return counter1 != counter2;
>

I think it is very rare that the watchdog is running in any other than 
32kHz clock, so the above should be sufficient in any case. The worst 
case delay of 3968us is a bit long, but even that is happening only 
during probe. And vast majority of user are going to have the 31us 
delay, so that should be fine.

Thanks,
-Timo

> For some bonus points add some defines for the magic constants.
>
> This is save as the OMAP_WATCHDOG_CNTRL doesn't seem to accept reads
> while the counter is running. Maybe even this could be used to detect a
> running timer?:
>
>   - enable timer:
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0xbbbb
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
>
>   - read out WCLR
> 	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - write to WCLR
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35024 0x0
>
>   - check result; didn't work
> 	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - stop timer
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0xaaaa
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x5555
>
>   - recheck WCLR
> 	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - write to WCLR
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35024 0x0
>
>   - check result; write succeeded
> 	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000000                                           ....
>
> (This is was tested on an AM335x.)
>
> Best regards
> Uwe
>

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
  2015-04-22 11:11   ` Timo Kokkonen
@ 2015-05-04 15:43     ` Guenter Roeck
  -1 siblings, 0 replies; 66+ messages in thread
From: Guenter Roeck @ 2015-05-04 15:43 UTC (permalink / raw)
  To: Timo Kokkonen
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
> There is a great deal of diversity in the watchdog hardware found on
> different devices. Differen hardware have different contstraints on
> them, many of the constraints that are excessively difficult for the
> user space to satisfy.
> 
> One such constraint is the length of the timeout value, which in many
> cases can be just a few seconds. Drivers are creating ad hoc solutions
> with timers and workqueues to extend the timeout in order to give user
> space more time between updates. Looking at the drivers it is clear
> that this has resulted to a lot of duplicate code.
> 
> Add an extension to the watchdog kernel API that allows the driver to
> describe tis HW constraints to the watchdog code. A kernel worker in
> the core is then used to extend the watchdog timeout on behalf of the
> user space. This allows the drivers to be simplified as core takes
> over the timer extending.
> 
> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>

Hi Timo,

Time to add my thoughts before you create the next version.
It isn't complete, so I won't try a line by line review.

The init_params function isn't the best choice for a name. Ultimately
we have other concepts to add - such as generic pretimeout support -
which makes this more and more difficult to handle. A separate function to
initialize the early timeout separately might make more sense. Either case,
any new API must not provide less functionality than the old one.

The current semantics of timeout and max_timeout is that those _are_ the
hardware limits. We can not just change that. Also, the new functionality
should, as much as possible, be automatic and should not require changes in
existing drivers to be useful for them.

I understand we need something line hw_timeout and hw_max_timeout, but that
doesn't mean that those _have_ to be specified to be used. For a generic use
case, it is sufficient to assume that timeout == hw_timeout and max_timeout ==
hw_max_timeout, and the hw_ values can be set automatically if not specified.
The code should be more permissive to not bail out if , for example,
hw_max_timeout is not specified (then hw_max_timeout == max_timeout).

Similar, it should not be necessary to have to specify hw_timeout and
hw_max_timeout in the first place for those to be useful. We can instead do
something like

	#define MIN_TIMEOUT	(60 * 10)	/* in seconds */

	if (!hw_max_timeout && max_timeout < MIN_TIMEOUT) {
		hw_max_timeout = max_timeout;
		max_timeout = MIN_TIMEOUT;
	}

with similar adjustments for hw_timeout and timeout. I am not sure if we should
keep the current meaning of timeout and max_timeout (ie it means hw limits) and
introduce sw_max_timeout and sw_timeout, or extend the meaning of max_timeout
and timeout to mean both and introduce the hw_variables. The latter would let us
specify more granularity for the hw_ values, so maybe the latter approach is
better.

Limits should not be in jiffies unless only used internally by the watchdog
subsystem. If more granularity is needed, use milliseconds and convert to
jiffies where needed.

Consider adding helper functions such as _watchdog_ping as inline in
watchdog_core.h.

hw_heartbeat should be set from the watchdog core; I don't think it needs to
be provided by the driver. It can be calculated from hw_max_timeout and timeout
as needed (to something like min(hw_max_timeout, timeout) / 2).

You don't need boot_kepalive and active_keepalive as separate flags in
watchdog_worker.

hw_features is unfortunate, and I am not sure that it is needed. That the
watchdog is running at boot time is not really a hardware feature, but a state.
Either add a bit to 'status' in watchdog_device, or add a callback function. 
WDOG_HW_IS_STOPPABLE can be added to 'status' as well. It should probably be
reversed, though, to something like WDOG_NOT_STOPPABLE, so that it does not have
to be set to existing drivers.

Guenter

> ---
>  drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>  drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>  include/linux/watchdog.h         |  23 +++++++++
>  3 files changed, 189 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
> index cec9b55..fd12489 100644
> --- a/drivers/watchdog/watchdog_core.c
> +++ b/drivers/watchdog/watchdog_core.c
> @@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
>  EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>  
>  /**
> + * watchdog_init_parms() - initialize generic watchdog parameters
> + * @wdd: Watchdog device to operate
> + * @dev: Device that stores the device tree properties
> + *
> + * Initialize the generic timeout parameters. The driver needs to set
> + * hw_features bitmask from @wdd prior calling this function in order
> + * to ensure the core knows how to handle the HW.
> + *
> + * A zero is returned on success and -EINVAL for failure.
> + */
> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
> +{
> +	int ret = 0;
> +
> +	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * Max HW timeout needs to be set so that core knows when to
> +	 * use a kernel worker to support longer watchdog timeouts
> +	 */
> +	if (!wdd->hw_max_timeout)
> +		return -EINVAL;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(watchdog_init_params);
> +
> +static void watchdog_worker(struct work_struct *work)
> +{
> +	struct watchdog_device *wdd = container_of(to_delayed_work(work),
> +						struct watchdog_device, work);
> +	bool boot_keepalive;
> +	bool active_keepalive;
> +
> +	mutex_lock(&wdd->lock);
> +
> +	boot_keepalive = !watchdog_active(wdd) &&
> +		!watchdog_is_stoppable(wdd);
> +
> +	active_keepalive = watchdog_active(wdd) &&
> +		wdd->hw_max_timeout < wdd->timeout * HZ;
> +
> +	if (time_after(jiffies, wdd->expires) && watchdog_active(wdd)) {
> +		pr_crit("I will reset your machine !\n");
> +		mutex_unlock(&wdd->lock);
> +		return;
> +	}
> +
> +	if (boot_keepalive || active_keepalive) {
> +		if (wdd->ops->ping)
> +			wdd->ops->ping(wdd);
> +		else
> +			wdd->ops->start(wdd);
> +
> +		schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
> +	}
> +
> +	mutex_unlock(&wdd->lock);
> +}
> +
> +static int prepare_watchdog(struct watchdog_device *wdd)
> +{
> +	int err = 0;
> +
> +	/* Stop the watchdog now before user space opens the device */
> +	if (watchdog_is_stoppable(wdd) &&
> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
> +		err = wdd->ops->stop(wdd);
> +
> +	} else if (!watchdog_is_stoppable(wdd) &&
> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
> +		/*
> +		 * Can't stop it, use a delayed worker to tick it
> +		 * until it's open by user space
> +		 */
> +		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
> +	}
> +	return err;
> +}
> +
> +/**
>   * watchdog_register_device() - register a watchdog device
>   * @wdd: watchdog device
>   *
> @@ -156,13 +239,24 @@ int watchdog_register_device(struct watchdog_device *wdd)
>  	wdd->dev = device_create(watchdog_class, wdd->parent, devno,
>  					NULL, "watchdog%d", wdd->id);
>  	if (IS_ERR(wdd->dev)) {
> -		watchdog_dev_unregister(wdd);
> -		ida_simple_remove(&watchdog_ida, id);
>  		ret = PTR_ERR(wdd->dev);
> -		return ret;
> +		goto dev_create_fail;
> +	}
> +
> +	INIT_DELAYED_WORK(&wdd->work, watchdog_worker);
> +
> +	if (wdd->hw_max_timeout) {
> +		ret = prepare_watchdog(wdd);
> +		if (ret)
> +			goto dev_create_fail;
>  	}
>  
>  	return 0;
> +
> +dev_create_fail:
> +	watchdog_dev_unregister(wdd);
> +	ida_simple_remove(&watchdog_ida, id);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(watchdog_register_device);
>  
> @@ -181,6 +275,7 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
>  	if (wdd == NULL)
>  		return;
>  
> +	cancel_delayed_work_sync(&wdd->work);
>  	devno = wdd->cdev.dev;
>  	ret = watchdog_dev_unregister(wdd);
>  	if (ret)
> diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
> index 6aaefba..04ac68c 100644
> --- a/drivers/watchdog/watchdog_dev.c
> +++ b/drivers/watchdog/watchdog_dev.c
> @@ -49,6 +49,14 @@ static dev_t watchdog_devt;
>  /* the watchdog device behind /dev/watchdog */
>  static struct watchdog_device *old_wdd;
>  
> +static int _watchdog_ping(struct watchdog_device *wddev)
> +{
> +	if (wddev->ops->ping)
> +		return wddev->ops->ping(wddev);  /* ping the watchdog */
> +	else
> +		return wddev->ops->start(wddev); /* restart watchdog */
> +}
> +
>  /*
>   *	watchdog_ping: ping the watchdog.
>   *	@wddev: the watchdog device to ping
> @@ -73,10 +81,13 @@ static int watchdog_ping(struct watchdog_device *wddev)
>  	if (!watchdog_active(wddev))
>  		goto out_ping;
>  
> -	if (wddev->ops->ping)
> -		err = wddev->ops->ping(wddev);  /* ping the watchdog */
> -	else
> -		err = wddev->ops->start(wddev); /* restart watchdog */
> +	err = _watchdog_ping(wddev);
> +
> +	if (wddev->hw_max_timeout &&
> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
> +		wddev->expires = jiffies + wddev->timeout * HZ;
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	}
>  
>  out_ping:
>  	mutex_unlock(&wddev->lock);
> @@ -110,6 +121,13 @@ static int watchdog_start(struct watchdog_device *wddev)
>  	if (err == 0)
>  		set_bit(WDOG_ACTIVE, &wddev->status);
>  
> +	if (wddev->hw_max_timeout &&
> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
> +		wddev->expires = jiffies + wddev->timeout * HZ;
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	} else
> +		cancel_delayed_work(&wddev->work);
> +
>  out_start:
>  	mutex_unlock(&wddev->lock);
>  	return err;
> @@ -145,9 +163,21 @@ static int watchdog_stop(struct watchdog_device *wddev)
>  		goto out_stop;
>  	}
>  
> -	err = wddev->ops->stop(wddev);
> -	if (err == 0)
> +	if (!wddev->hw_max_timeout || watchdog_is_stoppable(wddev)) {
> +		cancel_delayed_work(&wddev->work);
> +		err = wddev->ops->stop(wddev);
> +		if (err == 0)
> +			clear_bit(WDOG_ACTIVE, &wddev->status);
> +	} else {
> +		/* Unstoppable watchdogs need the worker to keep them alive */
>  		clear_bit(WDOG_ACTIVE, &wddev->status);
> +		/*
> +		 * Ping it once as we don't know how much time there
> +		 * is left in the watchdog timer.
> +		 */
> +		err = _watchdog_ping(wddev);
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	}
>  
>  out_stop:
>  	mutex_unlock(&wddev->lock);
> @@ -194,7 +224,38 @@ out_status:
>  static int watchdog_set_timeout(struct watchdog_device *wddev,
>  							unsigned int timeout)
>  {
> -	int err;
> +	int err = 0;
> +
> +	if (wddev->hw_max_timeout) {
> +		int hw_timeout;
> +		/*
> +		 * We can't support too short timeout values. There is
> +		 * really no maximu however, anything longer than HW
> +		 * maximum will be supported by the watchdog core on
> +		 * behalf of the actual HW.
> +		 */
> +		if (timeout < wddev->min_timeout)
> +			return -EINVAL;
> +
> +		mutex_lock(&wddev->lock);
> +		if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
> +			err = -ENODEV;
> +			goto out_timeout;
> +		}
> +
> +		if (timeout * HZ > wddev->hw_max_timeout)
> +			schedule_delayed_work(&wddev->work,
> +					wddev->hw_heartbeat);
> +
> +		hw_timeout = min(timeout, wddev->hw_max_timeout / HZ);
> +		if (wddev->info->options & WDIOF_SETTIMEOUT)
> +			err = wddev->ops->set_timeout(wddev, hw_timeout);
> +
> +		if (hw_timeout < timeout)
> +			wddev->timeout = timeout;
> +
> +		goto out_timeout;
> +	}
>  
>  	if ((wddev->ops->set_timeout == NULL) ||
>  	    !(wddev->info->options & WDIOF_SETTIMEOUT))
> diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
> index 395b70e..027c99d 100644
> --- a/include/linux/watchdog.h
> +++ b/include/linux/watchdog.h
> @@ -12,6 +12,7 @@
>  #include <linux/bitops.h>
>  #include <linux/device.h>
>  #include <linux/cdev.h>
> +#include <linux/workqueue.h>
>  #include <uapi/linux/watchdog.h>
>  
>  struct watchdog_ops;
> @@ -62,9 +63,13 @@ struct watchdog_ops {
>   * @timeout:	The watchdog devices timeout value.
>   * @min_timeout:The watchdog devices minimum timeout value.
>   * @max_timeout:The watchdog devices maximum timeout value.
> + * @hw_max_timeout:The watchdog hardware maximum timeout value.
> + * @hw_heartbeat:Time interval in HW between timer pings.
>   * @driver-data:Pointer to the drivers private data.
>   * @lock:	Lock for watchdog core internal use only.
> + * @work:	Worker used to provide longer timeouts than hardware supports.
>   * @status:	Field that contains the devices internal status bits.
> + * @hw_features:Feature bits describing how the watchdog HW works.
>   *
>   * The watchdog_device structure contains all information about a
>   * watchdog timer device.
> @@ -86,8 +91,12 @@ struct watchdog_device {
>  	unsigned int timeout;
>  	unsigned int min_timeout;
>  	unsigned int max_timeout;
> +	unsigned int hw_max_timeout; /* in jiffies */
> +	unsigned int hw_heartbeat; /* in jiffies */
> +	unsigned long int expires; /* for keepalive worker */
>  	void *driver_data;
>  	struct mutex lock;
> +	struct delayed_work work;
>  	unsigned long status;
>  /* Bit numbers for status flags */
>  #define WDOG_ACTIVE		0	/* Is the watchdog running/active */
> @@ -95,6 +104,14 @@ struct watchdog_device {
>  #define WDOG_ALLOW_RELEASE	2	/* Did we receive the magic char ? */
>  #define WDOG_NO_WAY_OUT		3	/* Is 'nowayout' feature set ? */
>  #define WDOG_UNREGISTERED	4	/* Has the device been unregistered */
> +
> +/* Bits describing features supported by the HW */
> +	unsigned long hw_features;
> +
> +/* Can the watchdog be stopped and started */
> +#define WDOG_HW_IS_STOPPABLE		BIT(0)
> +/* Is the watchdog already running when the driver starts up */
> +#define WDOG_HW_RUNNING_AT_BOOT		BIT(1)
>  };
>  
>  #define WATCHDOG_NOWAYOUT		IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
> @@ -120,6 +137,11 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
>  		(t < wdd->min_timeout || t > wdd->max_timeout));
>  }
>  
> +static inline bool watchdog_is_stoppable(struct watchdog_device *wdd)
> +{
> +	return wdd->hw_features & WDOG_HW_IS_STOPPABLE;
> +}
> +
>  /* Use the following functions to manipulate watchdog driver specific data */
>  static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
>  {
> @@ -134,6 +156,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
>  /* drivers/watchdog/watchdog_core.c */
>  extern int watchdog_init_timeout(struct watchdog_device *wdd,
>  				  unsigned int timeout_parm, struct device *dev);
> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev);
>  extern int watchdog_register_device(struct watchdog_device *);
>  extern void watchdog_unregister_device(struct watchdog_device *);
>  
> -- 
> 2.1.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
@ 2015-05-04 15:43     ` Guenter Roeck
  0 siblings, 0 replies; 66+ messages in thread
From: Guenter Roeck @ 2015-05-04 15:43 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
> There is a great deal of diversity in the watchdog hardware found on
> different devices. Differen hardware have different contstraints on
> them, many of the constraints that are excessively difficult for the
> user space to satisfy.
> 
> One such constraint is the length of the timeout value, which in many
> cases can be just a few seconds. Drivers are creating ad hoc solutions
> with timers and workqueues to extend the timeout in order to give user
> space more time between updates. Looking at the drivers it is clear
> that this has resulted to a lot of duplicate code.
> 
> Add an extension to the watchdog kernel API that allows the driver to
> describe tis HW constraints to the watchdog code. A kernel worker in
> the core is then used to extend the watchdog timeout on behalf of the
> user space. This allows the drivers to be simplified as core takes
> over the timer extending.
> 
> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>

Hi Timo,

Time to add my thoughts before you create the next version.
It isn't complete, so I won't try a line by line review.

The init_params function isn't the best choice for a name. Ultimately
we have other concepts to add - such as generic pretimeout support -
which makes this more and more difficult to handle. A separate function to
initialize the early timeout separately might make more sense. Either case,
any new API must not provide less functionality than the old one.

The current semantics of timeout and max_timeout is that those _are_ the
hardware limits. We can not just change that. Also, the new functionality
should, as much as possible, be automatic and should not require changes in
existing drivers to be useful for them.

I understand we need something line hw_timeout and hw_max_timeout, but that
doesn't mean that those _have_ to be specified to be used. For a generic use
case, it is sufficient to assume that timeout == hw_timeout and max_timeout ==
hw_max_timeout, and the hw_ values can be set automatically if not specified.
The code should be more permissive to not bail out if , for example,
hw_max_timeout is not specified (then hw_max_timeout == max_timeout).

Similar, it should not be necessary to have to specify hw_timeout and
hw_max_timeout in the first place for those to be useful. We can instead do
something like

	#define MIN_TIMEOUT	(60 * 10)	/* in seconds */

	if (!hw_max_timeout && max_timeout < MIN_TIMEOUT) {
		hw_max_timeout = max_timeout;
		max_timeout = MIN_TIMEOUT;
	}

with similar adjustments for hw_timeout and timeout. I am not sure if we should
keep the current meaning of timeout and max_timeout (ie it means hw limits) and
introduce sw_max_timeout and sw_timeout, or extend the meaning of max_timeout
and timeout to mean both and introduce the hw_variables. The latter would let us
specify more granularity for the hw_ values, so maybe the latter approach is
better.

Limits should not be in jiffies unless only used internally by the watchdog
subsystem. If more granularity is needed, use milliseconds and convert to
jiffies where needed.

Consider adding helper functions such as _watchdog_ping as inline in
watchdog_core.h.

hw_heartbeat should be set from the watchdog core; I don't think it needs to
be provided by the driver. It can be calculated from hw_max_timeout and timeout
as needed (to something like min(hw_max_timeout, timeout) / 2).

You don't need boot_kepalive and active_keepalive as separate flags in
watchdog_worker.

hw_features is unfortunate, and I am not sure that it is needed. That the
watchdog is running@boot time is not really a hardware feature, but a state.
Either add a bit to 'status' in watchdog_device, or add a callback function. 
WDOG_HW_IS_STOPPABLE can be added to 'status' as well. It should probably be
reversed, though, to something like WDOG_NOT_STOPPABLE, so that it does not have
to be set to existing drivers.

Guenter

> ---
>  drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>  drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>  include/linux/watchdog.h         |  23 +++++++++
>  3 files changed, 189 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
> index cec9b55..fd12489 100644
> --- a/drivers/watchdog/watchdog_core.c
> +++ b/drivers/watchdog/watchdog_core.c
> @@ -99,6 +99,89 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
>  EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>  
>  /**
> + * watchdog_init_parms() - initialize generic watchdog parameters
> + * @wdd: Watchdog device to operate
> + * @dev: Device that stores the device tree properties
> + *
> + * Initialize the generic timeout parameters. The driver needs to set
> + * hw_features bitmask from @wdd prior calling this function in order
> + * to ensure the core knows how to handle the HW.
> + *
> + * A zero is returned on success and -EINVAL for failure.
> + */
> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
> +{
> +	int ret = 0;
> +
> +	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * Max HW timeout needs to be set so that core knows when to
> +	 * use a kernel worker to support longer watchdog timeouts
> +	 */
> +	if (!wdd->hw_max_timeout)
> +		return -EINVAL;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(watchdog_init_params);
> +
> +static void watchdog_worker(struct work_struct *work)
> +{
> +	struct watchdog_device *wdd = container_of(to_delayed_work(work),
> +						struct watchdog_device, work);
> +	bool boot_keepalive;
> +	bool active_keepalive;
> +
> +	mutex_lock(&wdd->lock);
> +
> +	boot_keepalive = !watchdog_active(wdd) &&
> +		!watchdog_is_stoppable(wdd);
> +
> +	active_keepalive = watchdog_active(wdd) &&
> +		wdd->hw_max_timeout < wdd->timeout * HZ;
> +
> +	if (time_after(jiffies, wdd->expires) && watchdog_active(wdd)) {
> +		pr_crit("I will reset your machine !\n");
> +		mutex_unlock(&wdd->lock);
> +		return;
> +	}
> +
> +	if (boot_keepalive || active_keepalive) {
> +		if (wdd->ops->ping)
> +			wdd->ops->ping(wdd);
> +		else
> +			wdd->ops->start(wdd);
> +
> +		schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
> +	}
> +
> +	mutex_unlock(&wdd->lock);
> +}
> +
> +static int prepare_watchdog(struct watchdog_device *wdd)
> +{
> +	int err = 0;
> +
> +	/* Stop the watchdog now before user space opens the device */
> +	if (watchdog_is_stoppable(wdd) &&
> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
> +		err = wdd->ops->stop(wdd);
> +
> +	} else if (!watchdog_is_stoppable(wdd) &&
> +		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
> +		/*
> +		 * Can't stop it, use a delayed worker to tick it
> +		 * until it's open by user space
> +		 */
> +		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
> +	}
> +	return err;
> +}
> +
> +/**
>   * watchdog_register_device() - register a watchdog device
>   * @wdd: watchdog device
>   *
> @@ -156,13 +239,24 @@ int watchdog_register_device(struct watchdog_device *wdd)
>  	wdd->dev = device_create(watchdog_class, wdd->parent, devno,
>  					NULL, "watchdog%d", wdd->id);
>  	if (IS_ERR(wdd->dev)) {
> -		watchdog_dev_unregister(wdd);
> -		ida_simple_remove(&watchdog_ida, id);
>  		ret = PTR_ERR(wdd->dev);
> -		return ret;
> +		goto dev_create_fail;
> +	}
> +
> +	INIT_DELAYED_WORK(&wdd->work, watchdog_worker);
> +
> +	if (wdd->hw_max_timeout) {
> +		ret = prepare_watchdog(wdd);
> +		if (ret)
> +			goto dev_create_fail;
>  	}
>  
>  	return 0;
> +
> +dev_create_fail:
> +	watchdog_dev_unregister(wdd);
> +	ida_simple_remove(&watchdog_ida, id);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(watchdog_register_device);
>  
> @@ -181,6 +275,7 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
>  	if (wdd == NULL)
>  		return;
>  
> +	cancel_delayed_work_sync(&wdd->work);
>  	devno = wdd->cdev.dev;
>  	ret = watchdog_dev_unregister(wdd);
>  	if (ret)
> diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
> index 6aaefba..04ac68c 100644
> --- a/drivers/watchdog/watchdog_dev.c
> +++ b/drivers/watchdog/watchdog_dev.c
> @@ -49,6 +49,14 @@ static dev_t watchdog_devt;
>  /* the watchdog device behind /dev/watchdog */
>  static struct watchdog_device *old_wdd;
>  
> +static int _watchdog_ping(struct watchdog_device *wddev)
> +{
> +	if (wddev->ops->ping)
> +		return wddev->ops->ping(wddev);  /* ping the watchdog */
> +	else
> +		return wddev->ops->start(wddev); /* restart watchdog */
> +}
> +
>  /*
>   *	watchdog_ping: ping the watchdog.
>   *	@wddev: the watchdog device to ping
> @@ -73,10 +81,13 @@ static int watchdog_ping(struct watchdog_device *wddev)
>  	if (!watchdog_active(wddev))
>  		goto out_ping;
>  
> -	if (wddev->ops->ping)
> -		err = wddev->ops->ping(wddev);  /* ping the watchdog */
> -	else
> -		err = wddev->ops->start(wddev); /* restart watchdog */
> +	err = _watchdog_ping(wddev);
> +
> +	if (wddev->hw_max_timeout &&
> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
> +		wddev->expires = jiffies + wddev->timeout * HZ;
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	}
>  
>  out_ping:
>  	mutex_unlock(&wddev->lock);
> @@ -110,6 +121,13 @@ static int watchdog_start(struct watchdog_device *wddev)
>  	if (err == 0)
>  		set_bit(WDOG_ACTIVE, &wddev->status);
>  
> +	if (wddev->hw_max_timeout &&
> +		wddev->timeout * HZ > wddev->hw_max_timeout) {
> +		wddev->expires = jiffies + wddev->timeout * HZ;
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	} else
> +		cancel_delayed_work(&wddev->work);
> +
>  out_start:
>  	mutex_unlock(&wddev->lock);
>  	return err;
> @@ -145,9 +163,21 @@ static int watchdog_stop(struct watchdog_device *wddev)
>  		goto out_stop;
>  	}
>  
> -	err = wddev->ops->stop(wddev);
> -	if (err == 0)
> +	if (!wddev->hw_max_timeout || watchdog_is_stoppable(wddev)) {
> +		cancel_delayed_work(&wddev->work);
> +		err = wddev->ops->stop(wddev);
> +		if (err == 0)
> +			clear_bit(WDOG_ACTIVE, &wddev->status);
> +	} else {
> +		/* Unstoppable watchdogs need the worker to keep them alive */
>  		clear_bit(WDOG_ACTIVE, &wddev->status);
> +		/*
> +		 * Ping it once as we don't know how much time there
> +		 * is left in the watchdog timer.
> +		 */
> +		err = _watchdog_ping(wddev);
> +		schedule_delayed_work(&wddev->work, wddev->hw_heartbeat);
> +	}
>  
>  out_stop:
>  	mutex_unlock(&wddev->lock);
> @@ -194,7 +224,38 @@ out_status:
>  static int watchdog_set_timeout(struct watchdog_device *wddev,
>  							unsigned int timeout)
>  {
> -	int err;
> +	int err = 0;
> +
> +	if (wddev->hw_max_timeout) {
> +		int hw_timeout;
> +		/*
> +		 * We can't support too short timeout values. There is
> +		 * really no maximu however, anything longer than HW
> +		 * maximum will be supported by the watchdog core on
> +		 * behalf of the actual HW.
> +		 */
> +		if (timeout < wddev->min_timeout)
> +			return -EINVAL;
> +
> +		mutex_lock(&wddev->lock);
> +		if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
> +			err = -ENODEV;
> +			goto out_timeout;
> +		}
> +
> +		if (timeout * HZ > wddev->hw_max_timeout)
> +			schedule_delayed_work(&wddev->work,
> +					wddev->hw_heartbeat);
> +
> +		hw_timeout = min(timeout, wddev->hw_max_timeout / HZ);
> +		if (wddev->info->options & WDIOF_SETTIMEOUT)
> +			err = wddev->ops->set_timeout(wddev, hw_timeout);
> +
> +		if (hw_timeout < timeout)
> +			wddev->timeout = timeout;
> +
> +		goto out_timeout;
> +	}
>  
>  	if ((wddev->ops->set_timeout == NULL) ||
>  	    !(wddev->info->options & WDIOF_SETTIMEOUT))
> diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
> index 395b70e..027c99d 100644
> --- a/include/linux/watchdog.h
> +++ b/include/linux/watchdog.h
> @@ -12,6 +12,7 @@
>  #include <linux/bitops.h>
>  #include <linux/device.h>
>  #include <linux/cdev.h>
> +#include <linux/workqueue.h>
>  #include <uapi/linux/watchdog.h>
>  
>  struct watchdog_ops;
> @@ -62,9 +63,13 @@ struct watchdog_ops {
>   * @timeout:	The watchdog devices timeout value.
>   * @min_timeout:The watchdog devices minimum timeout value.
>   * @max_timeout:The watchdog devices maximum timeout value.
> + * @hw_max_timeout:The watchdog hardware maximum timeout value.
> + * @hw_heartbeat:Time interval in HW between timer pings.
>   * @driver-data:Pointer to the drivers private data.
>   * @lock:	Lock for watchdog core internal use only.
> + * @work:	Worker used to provide longer timeouts than hardware supports.
>   * @status:	Field that contains the devices internal status bits.
> + * @hw_features:Feature bits describing how the watchdog HW works.
>   *
>   * The watchdog_device structure contains all information about a
>   * watchdog timer device.
> @@ -86,8 +91,12 @@ struct watchdog_device {
>  	unsigned int timeout;
>  	unsigned int min_timeout;
>  	unsigned int max_timeout;
> +	unsigned int hw_max_timeout; /* in jiffies */
> +	unsigned int hw_heartbeat; /* in jiffies */
> +	unsigned long int expires; /* for keepalive worker */
>  	void *driver_data;
>  	struct mutex lock;
> +	struct delayed_work work;
>  	unsigned long status;
>  /* Bit numbers for status flags */
>  #define WDOG_ACTIVE		0	/* Is the watchdog running/active */
> @@ -95,6 +104,14 @@ struct watchdog_device {
>  #define WDOG_ALLOW_RELEASE	2	/* Did we receive the magic char ? */
>  #define WDOG_NO_WAY_OUT		3	/* Is 'nowayout' feature set ? */
>  #define WDOG_UNREGISTERED	4	/* Has the device been unregistered */
> +
> +/* Bits describing features supported by the HW */
> +	unsigned long hw_features;
> +
> +/* Can the watchdog be stopped and started */
> +#define WDOG_HW_IS_STOPPABLE		BIT(0)
> +/* Is the watchdog already running when the driver starts up */
> +#define WDOG_HW_RUNNING_AT_BOOT		BIT(1)
>  };
>  
>  #define WATCHDOG_NOWAYOUT		IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
> @@ -120,6 +137,11 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
>  		(t < wdd->min_timeout || t > wdd->max_timeout));
>  }
>  
> +static inline bool watchdog_is_stoppable(struct watchdog_device *wdd)
> +{
> +	return wdd->hw_features & WDOG_HW_IS_STOPPABLE;
> +}
> +
>  /* Use the following functions to manipulate watchdog driver specific data */
>  static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
>  {
> @@ -134,6 +156,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
>  /* drivers/watchdog/watchdog_core.c */
>  extern int watchdog_init_timeout(struct watchdog_device *wdd,
>  				  unsigned int timeout_parm, struct device *dev);
> +int watchdog_init_params(struct watchdog_device *wdd, struct device *dev);
>  extern int watchdog_register_device(struct watchdog_device *);
>  extern void watchdog_unregister_device(struct watchdog_device *);
>  
> -- 
> 2.1.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
  2015-05-04  5:59       ` Timo Kokkonen
@ 2015-05-04 16:08         ` Guenter Roeck
  -1 siblings, 0 replies; 66+ messages in thread
From: Guenter Roeck @ 2015-05-04 16:08 UTC (permalink / raw)
  To: Timo Kokkonen
  Cc: Uwe Kleine-König, linux-arm-kernel, linux-watchdog,
	boris.brezillon, nicolas.ferre, alexandre.belloni, Wenyou.Yang

On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
> Hi, 03.05.2015 21:56, Uwe Kleine-König wrote:
> >Hello,
> >
> >On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
> >>Use the new watchdog core extensions to let watchdog core take over
> >>boot time watchdog behavior. The difference is that early-timeout-sec
> >>device tree property becomes available for this driver and a running
> >>watchdog is not stopped unless the core decides to stop it.
> >>
> >>Omap watchdog is running by default in the boot up but bootloader
> >>might have stopped it. Therefore we fill the WDOG_HW_RUNNING_AT_BOOT
> >>bit depending on the actual watchdog state so that the watchdog core
> >>can act properly.
> >>
> >>Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
> >>---
> >>  drivers/watchdog/omap_wdt.c | 28 +++++++++++++++++++++++++++-
> >>  1 file changed, 27 insertions(+), 1 deletion(-)
> >>
> >>diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
> >>index bbaf39a..7164f2e 100644
> >>--- a/drivers/watchdog/omap_wdt.c
> >>+++ b/drivers/watchdog/omap_wdt.c
> >>@@ -78,6 +78,13 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
> >>  	/* reloaded WCRR from WLDR */
> >>  }
> >>
> >>+static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
> >>+{
> >>+	void __iomem *base = wdev->base;
> >>+
> >>+	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
> >>+}
> >This isn't reliable. The sequence needed to enable the watchdog is
> >	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
> >	writel(0x4444, base + OMAP_WATCHDOG_SPR);
> >
> >The sequence to stop is:
> >	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
> >	writel(0x5555, base + OMAP_WATCHDOG_SPR);
> >
> >But:
> >
> >barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
> >44e35048: 00005555                                           UU..
> >barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
> >barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
> >44e35048: 00004444                                           DD..
> >
> >So the register contains 0x4444 but the timer doesn't run. So at best
> >testing for 0x4444 is a good heuristic.
> 
> Yeah.. I don't think we can get any better than that. Unless we start
> checking the counter register and see whether it really counts or not, and I
> think that's a bit overkill.. So I'd say we should be safe when assuming

You sure ? I would prefer Uwe's suggestion to check the counter

> bootloader is doing things correctly. Although, we could add a comment to

... because I don't really trust the bootloader that much. If people are not
happy with the resulting 4ms added boot time, they should fix the hardware.

Otherwise we'll have to deal with people submitting bug reports because their
system experiences "unexpected" resets, and I don't really want to have to deal
with this.

Guenter

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
@ 2015-05-04 16:08         ` Guenter Roeck
  0 siblings, 0 replies; 66+ messages in thread
From: Guenter Roeck @ 2015-05-04 16:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
> Hi, 03.05.2015 21:56, Uwe Kleine-K?nig wrote:
> >Hello,
> >
> >On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
> >>Use the new watchdog core extensions to let watchdog core take over
> >>boot time watchdog behavior. The difference is that early-timeout-sec
> >>device tree property becomes available for this driver and a running
> >>watchdog is not stopped unless the core decides to stop it.
> >>
> >>Omap watchdog is running by default in the boot up but bootloader
> >>might have stopped it. Therefore we fill the WDOG_HW_RUNNING_AT_BOOT
> >>bit depending on the actual watchdog state so that the watchdog core
> >>can act properly.
> >>
> >>Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
> >>---
> >>  drivers/watchdog/omap_wdt.c | 28 +++++++++++++++++++++++++++-
> >>  1 file changed, 27 insertions(+), 1 deletion(-)
> >>
> >>diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
> >>index bbaf39a..7164f2e 100644
> >>--- a/drivers/watchdog/omap_wdt.c
> >>+++ b/drivers/watchdog/omap_wdt.c
> >>@@ -78,6 +78,13 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
> >>  	/* reloaded WCRR from WLDR */
> >>  }
> >>
> >>+static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
> >>+{
> >>+	void __iomem *base = wdev->base;
> >>+
> >>+	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
> >>+}
> >This isn't reliable. The sequence needed to enable the watchdog is
> >	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
> >	writel(0x4444, base + OMAP_WATCHDOG_SPR);
> >
> >The sequence to stop is:
> >	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
> >	writel(0x5555, base + OMAP_WATCHDOG_SPR);
> >
> >But:
> >
> >barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
> >44e35048: 00005555                                           UU..
> >barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
> >barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
> >44e35048: 00004444                                           DD..
> >
> >So the register contains 0x4444 but the timer doesn't run. So at best
> >testing for 0x4444 is a good heuristic.
> 
> Yeah.. I don't think we can get any better than that. Unless we start
> checking the counter register and see whether it really counts or not, and I
> think that's a bit overkill.. So I'd say we should be safe when assuming

You sure ? I would prefer Uwe's suggestion to check the counter

> bootloader is doing things correctly. Although, we could add a comment to

... because I don't really trust the bootloader that much. If people are not
happy with the resulting 4ms added boot time, they should fix the hardware.

Otherwise we'll have to deal with people submitting bug reports because their
system experiences "unexpected" resets, and I don't really want to have to deal
with this.

Guenter

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
  2015-04-22 11:11   ` Timo Kokkonen
@ 2015-05-04 21:17     ` Marc Kleine-Budde
  -1 siblings, 0 replies; 66+ messages in thread
From: Marc Kleine-Budde @ 2015-05-04 21:17 UTC (permalink / raw)
  To: Timo Kokkonen, linux-arm-kernel, linux-watchdog, boris.brezillon,
	nicolas.ferre, alexandre.belloni
  Cc: Wenyou.Yang

[-- Attachment #1: Type: text/plain, Size: 2842 bytes --]

On 04/22/2015 01:11 PM, Timo Kokkonen wrote:
> There is a great deal of diversity in the watchdog hardware found on
> different devices. Differen hardware have different contstraints on
> them, many of the constraints that are excessively difficult for the
> user space to satisfy.
> 
> One such constraint is the length of the timeout value, which in many
> cases can be just a few seconds. Drivers are creating ad hoc solutions
> with timers and workqueues to extend the timeout in order to give user
> space more time between updates. Looking at the drivers it is clear
> that this has resulted to a lot of duplicate code.
> 
> Add an extension to the watchdog kernel API that allows the driver to
> describe tis HW constraints to the watchdog code. A kernel worker in
> the core is then used to extend the watchdog timeout on behalf of the
> user space. This allows the drivers to be simplified as core takes
> over the timer extending.
> 
> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
> ---
>  drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>  drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>  include/linux/watchdog.h         |  23 +++++++++
>  3 files changed, 189 insertions(+), 10 deletions(-)
> 
[...]

>  static int watchdog_set_timeout(struct watchdog_device *wddev,
>  							unsigned int timeout)
>  {
> -	int err;
> +	int err = 0;
> +
> +	if (wddev->hw_max_timeout) {
> +		int hw_timeout;
> +		/*
> +		 * We can't support too short timeout values. There is
> +		 * really no maximu however, anything longer than HW
> +		 * maximum will be supported by the watchdog core on
> +		 * behalf of the actual HW.
> +		 */
> +		if (timeout < wddev->min_timeout)
> +			return -EINVAL;
> +
> +		mutex_lock(&wddev->lock);

The locking in this function looks weird.

> +		if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
> +			err = -ENODEV;
> +			goto out_timeout;
> +		}
> +
> +		if (timeout * HZ > wddev->hw_max_timeout)
> +			schedule_delayed_work(&wddev->work,
> +					wddev->hw_heartbeat);
> +
> +		hw_timeout = min(timeout, wddev->hw_max_timeout / HZ);
> +		if (wddev->info->options & WDIOF_SETTIMEOUT)
> +			err = wddev->ops->set_timeout(wddev, hw_timeout);
> +
> +		if (hw_timeout < timeout)
> +			wddev->timeout = timeout;
> +
> +		goto out_timeout;
> +	}
>  
>  	if ((wddev->ops->set_timeout == NULL) ||
>  	    !(wddev->info->options & WDIOF_SETTIMEOUT))

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
@ 2015-05-04 21:17     ` Marc Kleine-Budde
  0 siblings, 0 replies; 66+ messages in thread
From: Marc Kleine-Budde @ 2015-05-04 21:17 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/22/2015 01:11 PM, Timo Kokkonen wrote:
> There is a great deal of diversity in the watchdog hardware found on
> different devices. Differen hardware have different contstraints on
> them, many of the constraints that are excessively difficult for the
> user space to satisfy.
> 
> One such constraint is the length of the timeout value, which in many
> cases can be just a few seconds. Drivers are creating ad hoc solutions
> with timers and workqueues to extend the timeout in order to give user
> space more time between updates. Looking at the drivers it is clear
> that this has resulted to a lot of duplicate code.
> 
> Add an extension to the watchdog kernel API that allows the driver to
> describe tis HW constraints to the watchdog code. A kernel worker in
> the core is then used to extend the watchdog timeout on behalf of the
> user space. This allows the drivers to be simplified as core takes
> over the timer extending.
> 
> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
> ---
>  drivers/watchdog/watchdog_core.c | 101 +++++++++++++++++++++++++++++++++++++--
>  drivers/watchdog/watchdog_dev.c  |  75 ++++++++++++++++++++++++++---
>  include/linux/watchdog.h         |  23 +++++++++
>  3 files changed, 189 insertions(+), 10 deletions(-)
> 
[...]

>  static int watchdog_set_timeout(struct watchdog_device *wddev,
>  							unsigned int timeout)
>  {
> -	int err;
> +	int err = 0;
> +
> +	if (wddev->hw_max_timeout) {
> +		int hw_timeout;
> +		/*
> +		 * We can't support too short timeout values. There is
> +		 * really no maximu however, anything longer than HW
> +		 * maximum will be supported by the watchdog core on
> +		 * behalf of the actual HW.
> +		 */
> +		if (timeout < wddev->min_timeout)
> +			return -EINVAL;
> +
> +		mutex_lock(&wddev->lock);

The locking in this function looks weird.

> +		if (test_bit(WDOG_UNREGISTERED, &wddev->status)) {
> +			err = -ENODEV;
> +			goto out_timeout;
> +		}
> +
> +		if (timeout * HZ > wddev->hw_max_timeout)
> +			schedule_delayed_work(&wddev->work,
> +					wddev->hw_heartbeat);
> +
> +		hw_timeout = min(timeout, wddev->hw_max_timeout / HZ);
> +		if (wddev->info->options & WDIOF_SETTIMEOUT)
> +			err = wddev->ops->set_timeout(wddev, hw_timeout);
> +
> +		if (hw_timeout < timeout)
> +			wddev->timeout = timeout;
> +
> +		goto out_timeout;
> +	}
>  
>  	if ((wddev->ops->set_timeout == NULL) ||
>  	    !(wddev->info->options & WDIOF_SETTIMEOUT))

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150504/5d0f92c6/attachment.sig>

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
  2015-05-04 15:43     ` Guenter Roeck
@ 2015-05-05  6:26       ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-05  6:26 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

Hi Guenter,

On 04.05.2015 18:43, Guenter Roeck wrote:
> On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
>> There is a great deal of diversity in the watchdog hardware found on
>> different devices. Differen hardware have different contstraints on
>> them, many of the constraints that are excessively difficult for the
>> user space to satisfy.
>>
>> One such constraint is the length of the timeout value, which in many
>> cases can be just a few seconds. Drivers are creating ad hoc solutions
>> with timers and workqueues to extend the timeout in order to give user
>> space more time between updates. Looking at the drivers it is clear
>> that this has resulted to a lot of duplicate code.
>>
>> Add an extension to the watchdog kernel API that allows the driver to
>> describe tis HW constraints to the watchdog code. A kernel worker in
>> the core is then used to extend the watchdog timeout on behalf of the
>> user space. This allows the drivers to be simplified as core takes
>> over the timer extending.
>>
>> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
>
> Hi Timo,
>
> Time to add my thoughts before you create the next version.
> It isn't complete, so I won't try a line by line review.
>
> The init_params function isn't the best choice for a name. Ultimately
> we have other concepts to add - such as generic pretimeout support -
> which makes this more and more difficult to handle. A separate function to
> initialize the early timeout separately might make more sense. Either case,
> any new API must not provide less functionality than the old one.

Did you have something specific on your mind, something else besides of 
the early-timeout-sec parameter I'm adding with this series? I would 
like to learn more about it so that I can ensure the core API is as 
generic as possible.

> The current semantics of timeout and max_timeout is that those _are_ the
> hardware limits. We can not just change that. Also, the new functionality
> should, as much as possible, be automatic and should not require changes in
> existing drivers to be useful for them.
>
> I understand we need something line hw_timeout and hw_max_timeout, but that
> doesn't mean that those _have_ to be specified to be used. For a generic use
> case, it is sufficient to assume that timeout == hw_timeout and max_timeout ==
> hw_max_timeout, and the hw_ values can be set automatically if not specified.
> The code should be more permissive to not bail out if , for example,
> hw_max_timeout is not specified (then hw_max_timeout == max_timeout).

There are two requirements here that made me add the new hw_timeout 
variables:

1) Some of the hardware need higher precision in the timeout parameters 
as their limits are very short
2) There are too many watchdog drivers for me to change all at once, so 
old drivers need to remain functional while they are converted to the 
new core API

Also once the drivers are using the new API, there are actually two 
timeout values, one that is limited by the HW and one that is requested 
by the user space. The core code then intermediates between these two so 
that user space doesn't need to know about the HW limitations.

The max_timeout actually becomes irrelevant from user space perspective 
as the core code is capable of extending the timeout indefinitely, if 
that is what user space wants. We could add some logical maximum here, 
such as one day, what is already used by some of the drivers..

> Similar, it should not be necessary to have to specify hw_timeout and
> hw_max_timeout in the first place for those to be useful. We can instead do
> something like
>
> 	#define MIN_TIMEOUT	(60 * 10)	/* in seconds */
>
> 	if (!hw_max_timeout && max_timeout < MIN_TIMEOUT) {
> 		hw_max_timeout = max_timeout;
> 		max_timeout = MIN_TIMEOUT;
> 	}
>
> with similar adjustments for hw_timeout and timeout. I am not sure if we should
> keep the current meaning of timeout and max_timeout (ie it means hw limits) and
> introduce sw_max_timeout and sw_timeout, or extend the meaning of max_timeout
> and timeout to mean both and introduce the hw_variables. The latter would let us
> specify more granularity for the hw_ values, so maybe the latter approach is
> better.

I would like to be clear with the variable naming that others are for SW 
and the other for HW, but as we can't change all drivers at once, I'm 
trying to make it obvious that the new set of limits are purely for 
hardware. Once all drivers using the core API are filling the hw limits, 
the core is the only place that is taking care of the sw limits.

> Limits should not be in jiffies unless only used internally by the watchdog
> subsystem. If more granularity is needed, use milliseconds and convert to
> jiffies where needed.

I can change the limits in milliseconds.

> Consider adding helper functions such as _watchdog_ping as inline in
> watchdog_core.h.

I was wondering whether it should be in there instead of in 
watchdog_dev.c, so I'll move it there in the next version.

> hw_heartbeat should be set from the watchdog core; I don't think it needs to
> be provided by the driver. It can be calculated from hw_max_timeout and timeout
> as needed (to something like min(hw_max_timeout, timeout) / 2).

The HW might have limitations on what is the proper or ideal heartbeat 
value, which is why I thought driver should probably decide about how it 
should be set. But we could let core make a guess of its own if 
hw_heartbeat is left zero. Now I'm assuming the driver is changing the 
hw_heartbeat when timeout is changed.

> You don't need boot_kepalive and active_keepalive as separate flags in
> watchdog_worker.

You are right, I'll fix it.

> hw_features is unfortunate, and I am not sure that it is needed. That the
> watchdog is running at boot time is not really a hardware feature, but a state.
> Either add a bit to 'status' in watchdog_device, or add a callback function.
> WDOG_HW_IS_STOPPABLE can be added to 'status' as well. It should probably be
> reversed, though, to something like WDOG_NOT_STOPPABLE, so that it does not have
> to be set to existing drivers.

We can avoid WDOG_HW_IS_STOPPABLE entirely by mandating that driver 
stop() returns failure if hardware can't be stopped. This way core can 
start the worker whenever the HW can't be stopped.

Then we only have the boot time status of the watchdog left. For some HW 
that's a feature (eg. at91sam9_wdt) and some other it's a state (eg. 
omap_wdt). But I can add a new status bit for it, that's a better 
approach than having an entirely new set of HW flags.

Thanks for your review.

-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations
@ 2015-05-05  6:26       ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-05  6:26 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Guenter,

On 04.05.2015 18:43, Guenter Roeck wrote:
> On Wed, Apr 22, 2015 at 02:11:35PM +0300, Timo Kokkonen wrote:
>> There is a great deal of diversity in the watchdog hardware found on
>> different devices. Differen hardware have different contstraints on
>> them, many of the constraints that are excessively difficult for the
>> user space to satisfy.
>>
>> One such constraint is the length of the timeout value, which in many
>> cases can be just a few seconds. Drivers are creating ad hoc solutions
>> with timers and workqueues to extend the timeout in order to give user
>> space more time between updates. Looking at the drivers it is clear
>> that this has resulted to a lot of duplicate code.
>>
>> Add an extension to the watchdog kernel API that allows the driver to
>> describe tis HW constraints to the watchdog code. A kernel worker in
>> the core is then used to extend the watchdog timeout on behalf of the
>> user space. This allows the drivers to be simplified as core takes
>> over the timer extending.
>>
>> Tested-by: Wenyou Yang <wenyou.yang@atmel.com>
>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
>
> Hi Timo,
>
> Time to add my thoughts before you create the next version.
> It isn't complete, so I won't try a line by line review.
>
> The init_params function isn't the best choice for a name. Ultimately
> we have other concepts to add - such as generic pretimeout support -
> which makes this more and more difficult to handle. A separate function to
> initialize the early timeout separately might make more sense. Either case,
> any new API must not provide less functionality than the old one.

Did you have something specific on your mind, something else besides of 
the early-timeout-sec parameter I'm adding with this series? I would 
like to learn more about it so that I can ensure the core API is as 
generic as possible.

> The current semantics of timeout and max_timeout is that those _are_ the
> hardware limits. We can not just change that. Also, the new functionality
> should, as much as possible, be automatic and should not require changes in
> existing drivers to be useful for them.
>
> I understand we need something line hw_timeout and hw_max_timeout, but that
> doesn't mean that those _have_ to be specified to be used. For a generic use
> case, it is sufficient to assume that timeout == hw_timeout and max_timeout ==
> hw_max_timeout, and the hw_ values can be set automatically if not specified.
> The code should be more permissive to not bail out if , for example,
> hw_max_timeout is not specified (then hw_max_timeout == max_timeout).

There are two requirements here that made me add the new hw_timeout 
variables:

1) Some of the hardware need higher precision in the timeout parameters 
as their limits are very short
2) There are too many watchdog drivers for me to change all at once, so 
old drivers need to remain functional while they are converted to the 
new core API

Also once the drivers are using the new API, there are actually two 
timeout values, one that is limited by the HW and one that is requested 
by the user space. The core code then intermediates between these two so 
that user space doesn't need to know about the HW limitations.

The max_timeout actually becomes irrelevant from user space perspective 
as the core code is capable of extending the timeout indefinitely, if 
that is what user space wants. We could add some logical maximum here, 
such as one day, what is already used by some of the drivers..

> Similar, it should not be necessary to have to specify hw_timeout and
> hw_max_timeout in the first place for those to be useful. We can instead do
> something like
>
> 	#define MIN_TIMEOUT	(60 * 10)	/* in seconds */
>
> 	if (!hw_max_timeout && max_timeout < MIN_TIMEOUT) {
> 		hw_max_timeout = max_timeout;
> 		max_timeout = MIN_TIMEOUT;
> 	}
>
> with similar adjustments for hw_timeout and timeout. I am not sure if we should
> keep the current meaning of timeout and max_timeout (ie it means hw limits) and
> introduce sw_max_timeout and sw_timeout, or extend the meaning of max_timeout
> and timeout to mean both and introduce the hw_variables. The latter would let us
> specify more granularity for the hw_ values, so maybe the latter approach is
> better.

I would like to be clear with the variable naming that others are for SW 
and the other for HW, but as we can't change all drivers at once, I'm 
trying to make it obvious that the new set of limits are purely for 
hardware. Once all drivers using the core API are filling the hw limits, 
the core is the only place that is taking care of the sw limits.

> Limits should not be in jiffies unless only used internally by the watchdog
> subsystem. If more granularity is needed, use milliseconds and convert to
> jiffies where needed.

I can change the limits in milliseconds.

> Consider adding helper functions such as _watchdog_ping as inline in
> watchdog_core.h.

I was wondering whether it should be in there instead of in 
watchdog_dev.c, so I'll move it there in the next version.

> hw_heartbeat should be set from the watchdog core; I don't think it needs to
> be provided by the driver. It can be calculated from hw_max_timeout and timeout
> as needed (to something like min(hw_max_timeout, timeout) / 2).

The HW might have limitations on what is the proper or ideal heartbeat 
value, which is why I thought driver should probably decide about how it 
should be set. But we could let core make a guess of its own if 
hw_heartbeat is left zero. Now I'm assuming the driver is changing the 
hw_heartbeat when timeout is changed.

> You don't need boot_kepalive and active_keepalive as separate flags in
> watchdog_worker.

You are right, I'll fix it.

> hw_features is unfortunate, and I am not sure that it is needed. That the
> watchdog is running at boot time is not really a hardware feature, but a state.
> Either add a bit to 'status' in watchdog_device, or add a callback function.
> WDOG_HW_IS_STOPPABLE can be added to 'status' as well. It should probably be
> reversed, though, to something like WDOG_NOT_STOPPABLE, so that it does not have
> to be set to existing drivers.

We can avoid WDOG_HW_IS_STOPPABLE entirely by mandating that driver 
stop() returns failure if hardware can't be stopped. This way core can 
start the worker whenever the HW can't be stopped.

Then we only have the boot time status of the watchdog left. For some HW 
that's a feature (eg. at91sam9_wdt) and some other it's a state (eg. 
omap_wdt). But I can add a new status bit for it, that's a better 
approach than having an entirely new set of HW flags.

Thanks for your review.

-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 6/8] watchdog: imx2_wdt: Convert to use new core extensions
  2015-04-22 11:11   ` Timo Kokkonen
@ 2015-05-05  8:11     ` Marc Kleine-Budde
  -1 siblings, 0 replies; 66+ messages in thread
From: Marc Kleine-Budde @ 2015-05-05  8:11 UTC (permalink / raw)
  To: Timo Kokkonen, linux-arm-kernel, linux-watchdog, boris.brezillon,
	nicolas.ferre, alexandre.belloni
  Cc: Wenyou.Yang

[-- Attachment #1: Type: text/plain, Size: 1118 bytes --]

On 04/22/2015 01:11 PM, Timo Kokkonen wrote:
> Fill in the HW capabilities in watchdog_device structure and call
> watchdgog_init_params() to let watchdog core to init itself
> properly. The watchdog core can then ping stopped watchdog and the
> timer code in the driver can be removed.
> 
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>

This patch changes the default behaviour of the imx watchdog. Without
the patch: If the system boot with watchdog enabled (by the bootloader),
the driver sets up a threads to pet the watchdog. With the patch,
imx2_wdt_ping() is called once during probe but the thread is not
started. There are no timeout or early_timeout paremters in the DT or in
the kernel command line.

I don't like the old behaviour, but I think there are some setups that
rely on this feature.

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 6/8] watchdog: imx2_wdt: Convert to use new core extensions
@ 2015-05-05  8:11     ` Marc Kleine-Budde
  0 siblings, 0 replies; 66+ messages in thread
From: Marc Kleine-Budde @ 2015-05-05  8:11 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/22/2015 01:11 PM, Timo Kokkonen wrote:
> Fill in the HW capabilities in watchdog_device structure and call
> watchdgog_init_params() to let watchdog core to init itself
> properly. The watchdog core can then ping stopped watchdog and the
> timer code in the driver can be removed.
> 
> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>

This patch changes the default behaviour of the imx watchdog. Without
the patch: If the system boot with watchdog enabled (by the bootloader),
the driver sets up a threads to pet the watchdog. With the patch,
imx2_wdt_ping() is called once during probe but the thread is not
started. There are no timeout or early_timeout paremters in the DT or in
the kernel command line.

I don't like the old behaviour, but I think there are some setups that
rely on this feature.

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150505/168ea742/attachment.sig>

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 6/8] watchdog: imx2_wdt: Convert to use new core extensions
  2015-05-05  8:11     ` Marc Kleine-Budde
@ 2015-05-05  8:31       ` Marc Kleine-Budde
  -1 siblings, 0 replies; 66+ messages in thread
From: Marc Kleine-Budde @ 2015-05-05  8:31 UTC (permalink / raw)
  To: Timo Kokkonen, linux-arm-kernel, linux-watchdog, boris.brezillon,
	nicolas.ferre, alexandre.belloni
  Cc: Wenyou.Yang

[-- Attachment #1: Type: text/plain, Size: 1327 bytes --]

On 05/05/2015 10:11 AM, Marc Kleine-Budde wrote:
> On 04/22/2015 01:11 PM, Timo Kokkonen wrote:
>> Fill in the HW capabilities in watchdog_device structure and call
>> watchdgog_init_params() to let watchdog core to init itself
>> properly. The watchdog core can then ping stopped watchdog and the
>> timer code in the driver can be removed.
>>
>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
> 
> This patch changes the default behaviour of the imx watchdog. Without
> the patch: If the system boot with watchdog enabled (by the bootloader),
> the driver sets up a threads to pet the watchdog. With the patch,
> imx2_wdt_ping() is called once during probe but the thread is not
> started. There are no timeout or early_timeout paremters in the DT or in
> the kernel command line.
> 
> I don't like the old behaviour, but I think there are some setups that
> rely on this feature.

BTW: the start-thread-if-wd-is-active feature was added in:

    faad5de0b104 watchdog: imx2_wdt: convert to watchdog core api

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 6/8] watchdog: imx2_wdt: Convert to use new core extensions
@ 2015-05-05  8:31       ` Marc Kleine-Budde
  0 siblings, 0 replies; 66+ messages in thread
From: Marc Kleine-Budde @ 2015-05-05  8:31 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/05/2015 10:11 AM, Marc Kleine-Budde wrote:
> On 04/22/2015 01:11 PM, Timo Kokkonen wrote:
>> Fill in the HW capabilities in watchdog_device structure and call
>> watchdgog_init_params() to let watchdog core to init itself
>> properly. The watchdog core can then ping stopped watchdog and the
>> timer code in the driver can be removed.
>>
>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
> 
> This patch changes the default behaviour of the imx watchdog. Without
> the patch: If the system boot with watchdog enabled (by the bootloader),
> the driver sets up a threads to pet the watchdog. With the patch,
> imx2_wdt_ping() is called once during probe but the thread is not
> started. There are no timeout or early_timeout paremters in the DT or in
> the kernel command line.
> 
> I don't like the old behaviour, but I think there are some setups that
> rely on this feature.

BTW: the start-thread-if-wd-is-active feature was added in:

    faad5de0b104 watchdog: imx2_wdt: convert to watchdog core api

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150505/394b4816/attachment.sig>

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 6/8] watchdog: imx2_wdt: Convert to use new core extensions
  2015-05-05  8:31       ` Marc Kleine-Budde
@ 2015-05-05  9:07         ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-05  9:07 UTC (permalink / raw)
  To: Marc Kleine-Budde, linux-arm-kernel, linux-watchdog,
	boris.brezillon, nicolas.ferre, alexandre.belloni
  Cc: Wenyou.Yang

Hi Marc,

On 05.05.2015 11:31, Marc Kleine-Budde wrote:
> On 05/05/2015 10:11 AM, Marc Kleine-Budde wrote:
>> On 04/22/2015 01:11 PM, Timo Kokkonen wrote:
>>> Fill in the HW capabilities in watchdog_device structure and call
>>> watchdgog_init_params() to let watchdog core to init itself
>>> properly. The watchdog core can then ping stopped watchdog and the
>>> timer code in the driver can be removed.
>>>
>>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
>>
>> This patch changes the default behaviour of the imx watchdog. Without
>> the patch: If the system boot with watchdog enabled (by the bootloader),
>> the driver sets up a threads to pet the watchdog. With the patch,
>> imx2_wdt_ping() is called once during probe but the thread is not
>> started. There are no timeout or early_timeout paremters in the DT or in
>> the kernel command line.
>>
>> I don't like the old behaviour, but I think there are some setups that
>> rely on this feature.
>
> BTW: the start-thread-if-wd-is-active feature was added in:
>
>      faad5de0b104 watchdog: imx2_wdt: convert to watchdog core api
>

Yes, thanks for pointing this out. I didn't realize this was in place. 
So what we need to do is to tell the watchdog core the actual status of 
the watchdog HW, whether it is running or not. The core can then start 
the worker thread to ping the HW if needed.

I will fix this for the next patch version.

Thanks!
-Timo


^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 6/8] watchdog: imx2_wdt: Convert to use new core extensions
@ 2015-05-05  9:07         ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-05  9:07 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marc,

On 05.05.2015 11:31, Marc Kleine-Budde wrote:
> On 05/05/2015 10:11 AM, Marc Kleine-Budde wrote:
>> On 04/22/2015 01:11 PM, Timo Kokkonen wrote:
>>> Fill in the HW capabilities in watchdog_device structure and call
>>> watchdgog_init_params() to let watchdog core to init itself
>>> properly. The watchdog core can then ping stopped watchdog and the
>>> timer code in the driver can be removed.
>>>
>>> Signed-off-by: Timo Kokkonen <timo.kokkonen@offcode.fi>
>>
>> This patch changes the default behaviour of the imx watchdog. Without
>> the patch: If the system boot with watchdog enabled (by the bootloader),
>> the driver sets up a threads to pet the watchdog. With the patch,
>> imx2_wdt_ping() is called once during probe but the thread is not
>> started. There are no timeout or early_timeout paremters in the DT or in
>> the kernel command line.
>>
>> I don't like the old behaviour, but I think there are some setups that
>> rely on this feature.
>
> BTW: the start-thread-if-wd-is-active feature was added in:
>
>      faad5de0b104 watchdog: imx2_wdt: convert to watchdog core api
>

Yes, thanks for pointing this out. I didn't realize this was in place. 
So what we need to do is to tell the watchdog core the actual status of 
the watchdog HW, whether it is running or not. The core can then start 
the worker thread to ping the HW if needed.

I will fix this for the next patch version.

Thanks!
-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature
  2015-04-22 11:11 ` Timo Kokkonen
@ 2015-05-05 13:50   ` Uwe Kleine-König
  -1 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-05 13:50 UTC (permalink / raw)
  To: Timo Kokkonen
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang, Marc Kleine-Budde

Hello,

I talked to Marc Kleine-Budde about your approach, thought a bit more
about it and want to share a few thoughts with you.

Actually your series addresses three different problems

 a) some watchdog hardware isn't stoppable;
 b) some watchdog hardware has short maximal timeout;
 c) what to do with a watchdog that is already running at probe time?

The common solution is to add a mid-layer between userspace and the
driver that bridges the possible hardware limitations when userspace
wants to stop the watchdog or set a big timeout value. c) is a bit
different but could make use of the infrastructure that is introduced
while fixing a+b). The main difference between a+b) and c) is that for
c) you have to introduce some policy. If the series were mine I'd first
do three commits that address a), b) and c) each. Then convert drivers
to it.

Guenter and I already said something similar, but I will eventually
repeat it here more explicitly: When introducing a midlayer that
abstracts between hardware and it's users the IMHO most important thing
to get right is to be explicit about which side of a midlayer you're
currently working at. That is, be explicit about watchdog_is_running:
Does it mean the hardware is running, or does userspace believe the
watchdog to be active? Same for timeout, stoppable etc.pp.

When you consider changing the unit the watchdog core is using, why not
change to nano seconds and 64 bit variables? You might be able to copy
some algorithms and ideas from the timer core that uses these.

For c), I'd want to have a compile time setting that specifies the
default value for the policy:
 - disable at probe
 - don't touch a running timer
 - start at probe
This is friendly to distributions that might want to set "disable at
probe"-default to be on the safe side. Still make this overridable by a
kernel parameter. I'm not sure that "start at probe" is a sensible
feature. (Either you want to start the watchdog early, in this case even
before Linux starts; or you're not that strict then it doesn't matter
much if it takes yet another little while until your application that
pets the watchdog is up.)

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature
@ 2015-05-05 13:50   ` Uwe Kleine-König
  0 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-05 13:50 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

I talked to Marc Kleine-Budde about your approach, thought a bit more
about it and want to share a few thoughts with you.

Actually your series addresses three different problems

 a) some watchdog hardware isn't stoppable;
 b) some watchdog hardware has short maximal timeout;
 c) what to do with a watchdog that is already running at probe time?

The common solution is to add a mid-layer between userspace and the
driver that bridges the possible hardware limitations when userspace
wants to stop the watchdog or set a big timeout value. c) is a bit
different but could make use of the infrastructure that is introduced
while fixing a+b). The main difference between a+b) and c) is that for
c) you have to introduce some policy. If the series were mine I'd first
do three commits that address a), b) and c) each. Then convert drivers
to it.

Guenter and I already said something similar, but I will eventually
repeat it here more explicitly: When introducing a midlayer that
abstracts between hardware and it's users the IMHO most important thing
to get right is to be explicit about which side of a midlayer you're
currently working at. That is, be explicit about watchdog_is_running:
Does it mean the hardware is running, or does userspace believe the
watchdog to be active? Same for timeout, stoppable etc.pp.

When you consider changing the unit the watchdog core is using, why not
change to nano seconds and 64 bit variables? You might be able to copy
some algorithms and ideas from the timer core that uses these.

For c), I'd want to have a compile time setting that specifies the
default value for the policy:
 - disable at probe
 - don't touch a running timer
 - start at probe
This is friendly to distributions that might want to set "disable at
probe"-default to be on the safe side. Still make this overridable by a
kernel parameter. I'm not sure that "start at probe" is a sensible
feature. (Either you want to start the watchdog early, in this case even
before Linux starts; or you're not that strict then it doesn't matter
much if it takes yet another little while until your application that
pets the watchdog is up.)

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature
  2015-05-05 13:50   ` Uwe Kleine-König
@ 2015-05-06  7:26     ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-06  7:26 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang, Marc Kleine-Budde

Hi Uwe,

On 05.05.2015 16:50, Uwe Kleine-König wrote:
> Hello,
>
> I talked to Marc Kleine-Budde about your approach, thought a bit more
> about it and want to share a few thoughts with you.
>
> Actually your series addresses three different problems
>
>   a) some watchdog hardware isn't stoppable;
>   b) some watchdog hardware has short maximal timeout;
>   c) what to do with a watchdog that is already running at probe time?
>
> The common solution is to add a mid-layer between userspace and the
> driver that bridges the possible hardware limitations when userspace
> wants to stop the watchdog or set a big timeout value. c) is a bit
> different but could make use of the infrastructure that is introduced
> while fixing a+b). The main difference between a+b) and c) is that for
> c) you have to introduce some policy. If the series were mine I'd first
> do three commits that address a), b) and c) each. Then convert drivers
> to it.

The infrastructure needed for both a and b is the same, the actual code 
to implement either a or b is also touching the same functions and quite 
slim, so I think it may not be feasible to separate those in their own 
patches. But I'll think about it and see if logical separation makes sense.

> Guenter and I already said something similar, but I will eventually
> repeat it here more explicitly: When introducing a midlayer that
> abstracts between hardware and it's users the IMHO most important thing
> to get right is to be explicit about which side of a midlayer you're
> currently working at. That is, be explicit about watchdog_is_running:
> Does it mean the hardware is running, or does userspace believe the
> watchdog to be active? Same for timeout, stoppable etc.pp.

Yes, I agree. Thanks for clarifying this. I will keep this in mind and 
try to be explicit when naming functions and variables. The best way 
would be to also rename the existing variables too, but that would also 
touch all the existing drivers. Keeping the current naming scheme in 
place no doubt is less explicit about which side of the new midlayer 
they are working on. But luckily the logic of the existing driver 
doesn't change, they are all exposing things directly to userland and 
they can be documented as is. The new variables and functions add 
interfaces only between the new midlayer and HW, so they are explicit as 
well. So I think it is a fair compromise to leave all existing interface 
towards the user space as is.

> When you consider changing the unit the watchdog core is using, why not
> change to nano seconds and 64 bit variables? You might be able to copy
> some algorithms and ideas from the timer core that uses these.

Yes, sounds like a good idea. I will look into it.

> For c), I'd want to have a compile time setting that specifies the
> default value for the policy:
>   - disable at probe
>   - don't touch a running timer
>   - start at probe
> This is friendly to distributions that might want to set "disable at
> probe"-default to be on the safe side. Still make this overridable by a
> kernel parameter. I'm not sure that "start at probe" is a sensible
> feature. (Either you want to start the watchdog early, in this case even
> before Linux starts; or you're not that strict then it doesn't matter
> much if it takes yet another little while until your application that
> pets the watchdog is up.)

Yes, this is very simple and sounds like a reasonable thing to have. I 
will add a Kconfig entry for this kind of build time default.

I will start working on the next version soon before I start forgetting 
all the good feedback I have received...

Thanks!
-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature
@ 2015-05-06  7:26     ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-06  7:26 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Uwe,

On 05.05.2015 16:50, Uwe Kleine-K?nig wrote:
> Hello,
>
> I talked to Marc Kleine-Budde about your approach, thought a bit more
> about it and want to share a few thoughts with you.
>
> Actually your series addresses three different problems
>
>   a) some watchdog hardware isn't stoppable;
>   b) some watchdog hardware has short maximal timeout;
>   c) what to do with a watchdog that is already running at probe time?
>
> The common solution is to add a mid-layer between userspace and the
> driver that bridges the possible hardware limitations when userspace
> wants to stop the watchdog or set a big timeout value. c) is a bit
> different but could make use of the infrastructure that is introduced
> while fixing a+b). The main difference between a+b) and c) is that for
> c) you have to introduce some policy. If the series were mine I'd first
> do three commits that address a), b) and c) each. Then convert drivers
> to it.

The infrastructure needed for both a and b is the same, the actual code 
to implement either a or b is also touching the same functions and quite 
slim, so I think it may not be feasible to separate those in their own 
patches. But I'll think about it and see if logical separation makes sense.

> Guenter and I already said something similar, but I will eventually
> repeat it here more explicitly: When introducing a midlayer that
> abstracts between hardware and it's users the IMHO most important thing
> to get right is to be explicit about which side of a midlayer you're
> currently working at. That is, be explicit about watchdog_is_running:
> Does it mean the hardware is running, or does userspace believe the
> watchdog to be active? Same for timeout, stoppable etc.pp.

Yes, I agree. Thanks for clarifying this. I will keep this in mind and 
try to be explicit when naming functions and variables. The best way 
would be to also rename the existing variables too, but that would also 
touch all the existing drivers. Keeping the current naming scheme in 
place no doubt is less explicit about which side of the new midlayer 
they are working on. But luckily the logic of the existing driver 
doesn't change, they are all exposing things directly to userland and 
they can be documented as is. The new variables and functions add 
interfaces only between the new midlayer and HW, so they are explicit as 
well. So I think it is a fair compromise to leave all existing interface 
towards the user space as is.

> When you consider changing the unit the watchdog core is using, why not
> change to nano seconds and 64 bit variables? You might be able to copy
> some algorithms and ideas from the timer core that uses these.

Yes, sounds like a good idea. I will look into it.

> For c), I'd want to have a compile time setting that specifies the
> default value for the policy:
>   - disable at probe
>   - don't touch a running timer
>   - start at probe
> This is friendly to distributions that might want to set "disable at
> probe"-default to be on the safe side. Still make this overridable by a
> kernel parameter. I'm not sure that "start at probe" is a sensible
> feature. (Either you want to start the watchdog early, in this case even
> before Linux starts; or you're not that strict then it doesn't matter
> much if it takes yet another little while until your application that
> pets the watchdog is up.)

Yes, this is very simple and sounds like a reasonable thing to have. I 
will add a Kconfig entry for this kind of build time default.

I will start working on the next version soon before I start forgetting 
all the good feedback I have received...

Thanks!
-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature
  2015-05-06  7:26     ` Timo Kokkonen
@ 2015-05-06  7:48       ` Uwe Kleine-König
  -1 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-06  7:48 UTC (permalink / raw)
  To: Timo Kokkonen
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang, Marc Kleine-Budde

Hello,

On Wed, May 06, 2015 at 10:26:07AM +0300, Timo Kokkonen wrote:
> On 05.05.2015 16:50, Uwe Kleine-König wrote:
> >Hello,
> >
> >I talked to Marc Kleine-Budde about your approach, thought a bit more
> >about it and want to share a few thoughts with you.
> >
> >Actually your series addresses three different problems
> >
> >  a) some watchdog hardware isn't stoppable;
> >  b) some watchdog hardware has short maximal timeout;
> >  c) what to do with a watchdog that is already running at probe time?
> >
> >The common solution is to add a mid-layer between userspace and the
> >driver that bridges the possible hardware limitations when userspace
> >wants to stop the watchdog or set a big timeout value. c) is a bit
> >different but could make use of the infrastructure that is introduced
> >while fixing a+b). The main difference between a+b) and c) is that for
> >c) you have to introduce some policy. If the series were mine I'd first
> >do three commits that address a), b) and c) each. Then convert drivers
> >to it.
> 
> The infrastructure needed for both a and b is the same, the actual
> code to implement either a or b is also touching the same functions
> and quite slim, so I think it may not be feasible to separate those
> in their own patches. But I'll think about it and see if logical
> separation makes sense.
Yeah, probably you have to actually try it to see if it's sensible.

> >Guenter and I already said something similar, but I will eventually
> >repeat it here more explicitly: When introducing a midlayer that
> >abstracts between hardware and it's users the IMHO most important thing
> >to get right is to be explicit about which side of a midlayer you're
> >currently working at. That is, be explicit about watchdog_is_running:
> >Does it mean the hardware is running, or does userspace believe the
> >watchdog to be active? Same for timeout, stoppable etc.pp.
> 
> Yes, I agree. Thanks for clarifying this. I will keep this in mind
> and try to be explicit when naming functions and variables. The best
> way would be to also rename the existing variables too, but that
> would also touch all the existing drivers. Keeping the current
> naming scheme in place no doubt is less explicit about which side of
> the new midlayer they are working on. But luckily the logic of the
> existing driver doesn't change, they are all exposing things
> directly to userland and they can be documented as is. The new
> variables and functions add interfaces only between the new midlayer
> and HW, so they are explicit as well. So I think it is a fair
> compromise to leave all existing interface towards the user space as
> is.
Maybe a good first step before addressing a+b+c) is to cleanup the
naming? Not sure how well this works though.

> >When you consider changing the unit the watchdog core is using, why not
> >change to nano seconds and 64 bit variables? You might be able to copy
> >some algorithms and ideas from the timer core that uses these.
> 
> Yes, sounds like a good idea. I will look into it.
I wouldn't be surprised if you had to build a parallel watchdog device
model for that and assert that both work until all drivers are converted
to the new model. Sounds like fun :-)

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature
@ 2015-05-06  7:48       ` Uwe Kleine-König
  0 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-06  7:48 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

On Wed, May 06, 2015 at 10:26:07AM +0300, Timo Kokkonen wrote:
> On 05.05.2015 16:50, Uwe Kleine-K?nig wrote:
> >Hello,
> >
> >I talked to Marc Kleine-Budde about your approach, thought a bit more
> >about it and want to share a few thoughts with you.
> >
> >Actually your series addresses three different problems
> >
> >  a) some watchdog hardware isn't stoppable;
> >  b) some watchdog hardware has short maximal timeout;
> >  c) what to do with a watchdog that is already running at probe time?
> >
> >The common solution is to add a mid-layer between userspace and the
> >driver that bridges the possible hardware limitations when userspace
> >wants to stop the watchdog or set a big timeout value. c) is a bit
> >different but could make use of the infrastructure that is introduced
> >while fixing a+b). The main difference between a+b) and c) is that for
> >c) you have to introduce some policy. If the series were mine I'd first
> >do three commits that address a), b) and c) each. Then convert drivers
> >to it.
> 
> The infrastructure needed for both a and b is the same, the actual
> code to implement either a or b is also touching the same functions
> and quite slim, so I think it may not be feasible to separate those
> in their own patches. But I'll think about it and see if logical
> separation makes sense.
Yeah, probably you have to actually try it to see if it's sensible.

> >Guenter and I already said something similar, but I will eventually
> >repeat it here more explicitly: When introducing a midlayer that
> >abstracts between hardware and it's users the IMHO most important thing
> >to get right is to be explicit about which side of a midlayer you're
> >currently working at. That is, be explicit about watchdog_is_running:
> >Does it mean the hardware is running, or does userspace believe the
> >watchdog to be active? Same for timeout, stoppable etc.pp.
> 
> Yes, I agree. Thanks for clarifying this. I will keep this in mind
> and try to be explicit when naming functions and variables. The best
> way would be to also rename the existing variables too, but that
> would also touch all the existing drivers. Keeping the current
> naming scheme in place no doubt is less explicit about which side of
> the new midlayer they are working on. But luckily the logic of the
> existing driver doesn't change, they are all exposing things
> directly to userland and they can be documented as is. The new
> variables and functions add interfaces only between the new midlayer
> and HW, so they are explicit as well. So I think it is a fair
> compromise to leave all existing interface towards the user space as
> is.
Maybe a good first step before addressing a+b+c) is to cleanup the
naming? Not sure how well this works though.

> >When you consider changing the unit the watchdog core is using, why not
> >change to nano seconds and 64 bit variables? You might be able to copy
> >some algorithms and ideas from the timer core that uses these.
> 
> Yes, sounds like a good idea. I will look into it.
I wouldn't be surprised if you had to build a parallel watchdog device
model for that and assert that both work until all drivers are converted
to the new model. Sounds like fun :-)

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature
  2015-05-06  7:48       ` Uwe Kleine-König
@ 2015-05-06  8:23         ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-06  8:23 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang, Marc Kleine-Budde

On 06.05.2015 10:48, Uwe Kleine-König wrote:
> Hello,
>
> On Wed, May 06, 2015 at 10:26:07AM +0300, Timo Kokkonen wrote:
>> On 05.05.2015 16:50, Uwe Kleine-König wrote:
>>> Hello,
>>>
>>> I talked to Marc Kleine-Budde about your approach, thought a bit more
>>> about it and want to share a few thoughts with you.
>>>
>>> Actually your series addresses three different problems
>>>
>>>   a) some watchdog hardware isn't stoppable;
>>>   b) some watchdog hardware has short maximal timeout;
>>>   c) what to do with a watchdog that is already running at probe time?
>>>
>>> The common solution is to add a mid-layer between userspace and the
>>> driver that bridges the possible hardware limitations when userspace
>>> wants to stop the watchdog or set a big timeout value. c) is a bit
>>> different but could make use of the infrastructure that is introduced
>>> while fixing a+b). The main difference between a+b) and c) is that for
>>> c) you have to introduce some policy. If the series were mine I'd first
>>> do three commits that address a), b) and c) each. Then convert drivers
>>> to it.
>>
>> The infrastructure needed for both a and b is the same, the actual
>> code to implement either a or b is also touching the same functions
>> and quite slim, so I think it may not be feasible to separate those
>> in their own patches. But I'll think about it and see if logical
>> separation makes sense.
> Yeah, probably you have to actually try it to see if it's sensible.
>
>>> Guenter and I already said something similar, but I will eventually
>>> repeat it here more explicitly: When introducing a midlayer that
>>> abstracts between hardware and it's users the IMHO most important thing
>>> to get right is to be explicit about which side of a midlayer you're
>>> currently working at. That is, be explicit about watchdog_is_running:
>>> Does it mean the hardware is running, or does userspace believe the
>>> watchdog to be active? Same for timeout, stoppable etc.pp.
>>
>> Yes, I agree. Thanks for clarifying this. I will keep this in mind
>> and try to be explicit when naming functions and variables. The best
>> way would be to also rename the existing variables too, but that
>> would also touch all the existing drivers. Keeping the current
>> naming scheme in place no doubt is less explicit about which side of
>> the new midlayer they are working on. But luckily the logic of the
>> existing driver doesn't change, they are all exposing things
>> directly to userland and they can be documented as is. The new
>> variables and functions add interfaces only between the new midlayer
>> and HW, so they are explicit as well. So I think it is a fair
>> compromise to leave all existing interface towards the user space as
>> is.
> Maybe a good first step before addressing a+b+c) is to cleanup the
> naming? Not sure how well this works though.
>

I'm sure it can be done. I'll probably should use coccinelle to get it 
right as I don't really trust sed alone is good enough.. And I may not 
be able to compile test all drivers, let alone run any other testing. 
There are 80+ drivers that I'd probably need to touch.. It is a lot of 
code churn but still a trivial variable rename. If you are willing to 
accept such a patch, then I will see how it can be done.

Although, are there any other variables besides timeout and min_timeout 
that we need to change? The timeout would become sw_timeout and 
min_timeout would be hw_min_timeout. Maybe watchdog_active() could be 
renamed to watchdog_device_open() and then add new watchdog_hw_running() 
to check whether the watchdog HW really is running or not.

So, two existing variable renames and one existing function rename. Or 
if no rename, then just document thoroughly how they work now and avoid 
excess code churn.

>>> When you consider changing the unit the watchdog core is using, why not
>>> change to nano seconds and 64 bit variables? You might be able to copy
>>> some algorithms and ideas from the timer core that uses these.
>>
>> Yes, sounds like a good idea. I will look into it.
> I wouldn't be surprised if you had to build a parallel watchdog device
> model for that and assert that both work until all drivers are converted
> to the new model. Sounds like fun :-)

I just realized that we were talking about changing the time stamp model 
for the core entirely, not just the new model... Which is much more 
complicated. Damn.

Well I will think about it. There is not much new that I'm adding here 
now. I'm hoping it could be done so that we just need to fill couple of 
new variables in the driver and call a new init function to set up the 
rest of the parameters and then clean up unneeded functionality. And the 
core changes are really not that big either.

What I probably should do at least is to mark clearly or maybe factor 
out the places where we deal with the old drivers and the deprecated 
variables. Then this code could be removed easily once there no longer 
is need for it. But I will keep on thinking it as I rework the series..

Thanks,
-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature
@ 2015-05-06  8:23         ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-06  8:23 UTC (permalink / raw)
  To: linux-arm-kernel

On 06.05.2015 10:48, Uwe Kleine-K?nig wrote:
> Hello,
>
> On Wed, May 06, 2015 at 10:26:07AM +0300, Timo Kokkonen wrote:
>> On 05.05.2015 16:50, Uwe Kleine-K?nig wrote:
>>> Hello,
>>>
>>> I talked to Marc Kleine-Budde about your approach, thought a bit more
>>> about it and want to share a few thoughts with you.
>>>
>>> Actually your series addresses three different problems
>>>
>>>   a) some watchdog hardware isn't stoppable;
>>>   b) some watchdog hardware has short maximal timeout;
>>>   c) what to do with a watchdog that is already running at probe time?
>>>
>>> The common solution is to add a mid-layer between userspace and the
>>> driver that bridges the possible hardware limitations when userspace
>>> wants to stop the watchdog or set a big timeout value. c) is a bit
>>> different but could make use of the infrastructure that is introduced
>>> while fixing a+b). The main difference between a+b) and c) is that for
>>> c) you have to introduce some policy. If the series were mine I'd first
>>> do three commits that address a), b) and c) each. Then convert drivers
>>> to it.
>>
>> The infrastructure needed for both a and b is the same, the actual
>> code to implement either a or b is also touching the same functions
>> and quite slim, so I think it may not be feasible to separate those
>> in their own patches. But I'll think about it and see if logical
>> separation makes sense.
> Yeah, probably you have to actually try it to see if it's sensible.
>
>>> Guenter and I already said something similar, but I will eventually
>>> repeat it here more explicitly: When introducing a midlayer that
>>> abstracts between hardware and it's users the IMHO most important thing
>>> to get right is to be explicit about which side of a midlayer you're
>>> currently working at. That is, be explicit about watchdog_is_running:
>>> Does it mean the hardware is running, or does userspace believe the
>>> watchdog to be active? Same for timeout, stoppable etc.pp.
>>
>> Yes, I agree. Thanks for clarifying this. I will keep this in mind
>> and try to be explicit when naming functions and variables. The best
>> way would be to also rename the existing variables too, but that
>> would also touch all the existing drivers. Keeping the current
>> naming scheme in place no doubt is less explicit about which side of
>> the new midlayer they are working on. But luckily the logic of the
>> existing driver doesn't change, they are all exposing things
>> directly to userland and they can be documented as is. The new
>> variables and functions add interfaces only between the new midlayer
>> and HW, so they are explicit as well. So I think it is a fair
>> compromise to leave all existing interface towards the user space as
>> is.
> Maybe a good first step before addressing a+b+c) is to cleanup the
> naming? Not sure how well this works though.
>

I'm sure it can be done. I'll probably should use coccinelle to get it 
right as I don't really trust sed alone is good enough.. And I may not 
be able to compile test all drivers, let alone run any other testing. 
There are 80+ drivers that I'd probably need to touch.. It is a lot of 
code churn but still a trivial variable rename. If you are willing to 
accept such a patch, then I will see how it can be done.

Although, are there any other variables besides timeout and min_timeout 
that we need to change? The timeout would become sw_timeout and 
min_timeout would be hw_min_timeout. Maybe watchdog_active() could be 
renamed to watchdog_device_open() and then add new watchdog_hw_running() 
to check whether the watchdog HW really is running or not.

So, two existing variable renames and one existing function rename. Or 
if no rename, then just document thoroughly how they work now and avoid 
excess code churn.

>>> When you consider changing the unit the watchdog core is using, why not
>>> change to nano seconds and 64 bit variables? You might be able to copy
>>> some algorithms and ideas from the timer core that uses these.
>>
>> Yes, sounds like a good idea. I will look into it.
> I wouldn't be surprised if you had to build a parallel watchdog device
> model for that and assert that both work until all drivers are converted
> to the new model. Sounds like fun :-)

I just realized that we were talking about changing the time stamp model 
for the core entirely, not just the new model... Which is much more 
complicated. Damn.

Well I will think about it. There is not much new that I'm adding here 
now. I'm hoping it could be done so that we just need to fill couple of 
new variables in the driver and call a new init function to set up the 
rest of the parameters and then clean up unneeded functionality. And the 
core changes are really not that big either.

What I probably should do at least is to mark clearly or maybe factor 
out the places where we deal with the old drivers and the deprecated 
variables. Then this code could be removed easily once there no longer 
is need for it. But I will keep on thinking it as I rework the series..

Thanks,
-Timo

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
  2015-05-04  7:04         ` Uwe Kleine-König
@ 2015-05-07  6:42           ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-07  6:42 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

On 04.05.2015 10:04, Uwe Kleine-König wrote:
> Hello Timo,
>
> On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
>> Hi, 03.05.2015 21:56, Uwe Kleine-König wrote:
>>> On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
>>>> +static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
>>>> +{
>>>> +	void __iomem *base = wdev->base;
>>>> +
>>>> +	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
>>>> +}
>>> This isn't reliable. The sequence needed to enable the watchdog is
>>> 	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
>>> 	writel(0x4444, base + OMAP_WATCHDOG_SPR);
>>>
>>> The sequence to stop is:
>>> 	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
>>> 	writel(0x5555, base + OMAP_WATCHDOG_SPR);
>>>
>>> But:
>>>
>>> barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
>>> 44e35048: 00005555                                           UU..
>>> barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
>>> barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
>>> 44e35048: 00004444                                           DD..
>>>
>>> So the register contains 0x4444 but the timer doesn't run. So at best
>>> testing for 0x4444 is a good heuristic.
>>
>> Yeah.. I don't think we can get any better than that. Unless we
>> start checking the counter register and see whether it really counts
>> or not, and I think that's a bit overkill.. So I'd say we should be
>> safe when assuming bootloader is doing things correctly. Although,
>> we could add a comment to the code that the test may not be 100%
>> reliable in case the start sequence have not been issued properly.
>>
>> Thanks for pointing this out!
> It doesn't seem to much overhead to do:
>
> 	/*
> 	 * There is no register that tells us if the timer is running,
> 	 * so we have to resort to sample twice. The minimal frequency
> 	 * is 256 Hz (32768 Hz prescaled with 2**7).
> 	 */
> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> 	mdelay(4);
> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> 	return counter1 != counter2;
>
> I'd say it's even worth to do:
>
> 	cntrl = readl(base + OMAP_WATCHDOG_CNTRL);
> 	if (cntrl & (1 << 5))
> 		shift = (cntrl >> 2) & 0x7;
> 	else
> 		shift = 0;
> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> 	udelay(31 << shift);
> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> 	return counter1 != counter2;
>
> For some bonus points add some defines for the magic constants.
>
> This is save as the OMAP_WATCHDOG_CNTRL doesn't seem to accept reads
> while the counter is running. Maybe even this could be used to detect a
> running timer?:

Maybe, but you will get a data abort when the HW doesn't accept a read. 
How do you recover from that?

Also, it seems that for some reason the watchdog HW is very picky about 
which registers can be read in what condition. If I add the code to read 
watchdog counter register, I will get a data abort later at the end of 
the probe when the watchdog revision is read. I don't understand why 
that happens, this does not seem logica. Maybe it has got something to 
do with the fact that the watchdog is stopped? If I can't read the 
counter register, I can't really implement the above heuristic.. Any ideas?

-Timo

>
>   - enable timer:
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0xbbbb
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
>
>   - read out WCLR
> 	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - write to WCLR
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35024 0x0
>
>   - check result; didn't work
> 	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - stop timer
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0xaaaa
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x5555
>
>   - recheck WCLR
> 	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - write to WCLR
> 	barebox@TI AM335x BeagleBone black:/ mw 0x44e35024 0x0
>
>   - check result; write succeeded
> 	barebox@TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000000                                           ....
>
> (This is was tested on an AM335x.)
>
> Best regards
> Uwe
>


^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
@ 2015-05-07  6:42           ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-07  6:42 UTC (permalink / raw)
  To: linux-arm-kernel

On 04.05.2015 10:04, Uwe Kleine-K?nig wrote:
> Hello Timo,
>
> On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
>> Hi, 03.05.2015 21:56, Uwe Kleine-K?nig wrote:
>>> On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
>>>> +static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
>>>> +{
>>>> +	void __iomem *base = wdev->base;
>>>> +
>>>> +	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
>>>> +}
>>> This isn't reliable. The sequence needed to enable the watchdog is
>>> 	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
>>> 	writel(0x4444, base + OMAP_WATCHDOG_SPR);
>>>
>>> The sequence to stop is:
>>> 	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
>>> 	writel(0x5555, base + OMAP_WATCHDOG_SPR);
>>>
>>> But:
>>>
>>> barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
>>> 44e35048: 00005555                                           UU..
>>> barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
>>> barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
>>> 44e35048: 00004444                                           DD..
>>>
>>> So the register contains 0x4444 but the timer doesn't run. So at best
>>> testing for 0x4444 is a good heuristic.
>>
>> Yeah.. I don't think we can get any better than that. Unless we
>> start checking the counter register and see whether it really counts
>> or not, and I think that's a bit overkill.. So I'd say we should be
>> safe when assuming bootloader is doing things correctly. Although,
>> we could add a comment to the code that the test may not be 100%
>> reliable in case the start sequence have not been issued properly.
>>
>> Thanks for pointing this out!
> It doesn't seem to much overhead to do:
>
> 	/*
> 	 * There is no register that tells us if the timer is running,
> 	 * so we have to resort to sample twice. The minimal frequency
> 	 * is 256 Hz (32768 Hz prescaled with 2**7).
> 	 */
> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> 	mdelay(4);
> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> 	return counter1 != counter2;
>
> I'd say it's even worth to do:
>
> 	cntrl = readl(base + OMAP_WATCHDOG_CNTRL);
> 	if (cntrl & (1 << 5))
> 		shift = (cntrl >> 2) & 0x7;
> 	else
> 		shift = 0;
> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> 	udelay(31 << shift);
> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> 	return counter1 != counter2;
>
> For some bonus points add some defines for the magic constants.
>
> This is save as the OMAP_WATCHDOG_CNTRL doesn't seem to accept reads
> while the counter is running. Maybe even this could be used to detect a
> running timer?:

Maybe, but you will get a data abort when the HW doesn't accept a read. 
How do you recover from that?

Also, it seems that for some reason the watchdog HW is very picky about 
which registers can be read in what condition. If I add the code to read 
watchdog counter register, I will get a data abort later at the end of 
the probe when the watchdog revision is read. I don't understand why 
that happens, this does not seem logica. Maybe it has got something to 
do with the fact that the watchdog is stopped? If I can't read the 
counter register, I can't really implement the above heuristic.. Any ideas?

-Timo

>
>   - enable timer:
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0xbbbb
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
>
>   - read out WCLR
> 	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - write to WCLR
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35024 0x0
>
>   - check result; didn't work
> 	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - stop timer
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0xaaaa
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x5555
>
>   - recheck WCLR
> 	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000020                                            ...
>
>   - write to WCLR
> 	barebox at TI AM335x BeagleBone black:/ mw 0x44e35024 0x0
>
>   - check result; write succeeded
> 	barebox at TI AM335x BeagleBone black:/ md 0x44e35024+4
> 	44e35024: 00000000                                           ....
>
> (This is was tested on an AM335x.)
>
> Best regards
> Uwe
>

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
  2015-05-07  6:42           ` Timo Kokkonen
@ 2015-05-07  7:30             ` Uwe Kleine-König
  -1 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-07  7:30 UTC (permalink / raw)
  To: Timo Kokkonen
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

Hello Timo,

On Thu, May 07, 2015 at 09:42:44AM +0300, Timo Kokkonen wrote:
> On 04.05.2015 10:04, Uwe Kleine-König wrote:
> >On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
> >>Hi, 03.05.2015 21:56, Uwe Kleine-König wrote:
> >>>On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
> >>>>+static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
> >>>>+{
> >>>>+	void __iomem *base = wdev->base;
> >>>>+
> >>>>+	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
> >>>>+}
> >>>This isn't reliable. The sequence needed to enable the watchdog is
> >>>	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
> >>>	writel(0x4444, base + OMAP_WATCHDOG_SPR);
> >>>
> >>>The sequence to stop is:
> >>>	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
> >>>	writel(0x5555, base + OMAP_WATCHDOG_SPR);
> >>>
> >>>But:
> >>>
> >>>barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
> >>>44e35048: 00005555                                           UU..
> >>>barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
> >>>barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
> >>>44e35048: 00004444                                           DD..
> >>>
> >>>So the register contains 0x4444 but the timer doesn't run. So at best
> >>>testing for 0x4444 is a good heuristic.
> >>
> >>Yeah.. I don't think we can get any better than that. Unless we
> >>start checking the counter register and see whether it really counts
> >>or not, and I think that's a bit overkill.. So I'd say we should be
> >>safe when assuming bootloader is doing things correctly. Although,
> >>we could add a comment to the code that the test may not be 100%
> >>reliable in case the start sequence have not been issued properly.
> >>
> >>Thanks for pointing this out!
> >It doesn't seem to much overhead to do:
> >
> >	/*
> >	 * There is no register that tells us if the timer is running,
> >	 * so we have to resort to sample twice. The minimal frequency
> >	 * is 256 Hz (32768 Hz prescaled with 2**7).
> >	 */
> >	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> >	mdelay(4);
> >	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> >	return counter1 != counter2;
> >
> >I'd say it's even worth to do:
> >
> >	cntrl = readl(base + OMAP_WATCHDOG_CNTRL);
> >	if (cntrl & (1 << 5))
> >		shift = (cntrl >> 2) & 0x7;
> >	else
> >		shift = 0;
> >	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> >	udelay(31 << shift);
> >	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> >	return counter1 != counter2;
> >
> >For some bonus points add some defines for the magic constants.
> >
> >This is save as the OMAP_WATCHDOG_CNTRL doesn't seem to accept reads
s/reads/writes/ in the above line.

> >while the counter is running. Maybe even this could be used to detect a
> >running timer?:
> 
> Maybe, but you will get a data abort when the HW doesn't accept a
> read. How do you recover from that?
> 
> Also, it seems that for some reason the watchdog HW is very picky
> about which registers can be read in what condition. If I add the
> code to read watchdog counter register, I will get a data abort
> later at the end of the probe when the watchdog revision is read. I
> don't understand why that happens, this does not seem logica. Maybe
> it has got something to do with the fact that the watchdog is
> stopped? If I can't read the counter register, I can't really
> implement the above heuristic.. Any ideas?
Which SoC do you use for your tests? Sounds strange. If you provide me
your wip patch I can take a look.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
@ 2015-05-07  7:30             ` Uwe Kleine-König
  0 siblings, 0 replies; 66+ messages in thread
From: Uwe Kleine-König @ 2015-05-07  7:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Timo,

On Thu, May 07, 2015 at 09:42:44AM +0300, Timo Kokkonen wrote:
> On 04.05.2015 10:04, Uwe Kleine-K?nig wrote:
> >On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
> >>Hi, 03.05.2015 21:56, Uwe Kleine-K?nig wrote:
> >>>On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
> >>>>+static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
> >>>>+{
> >>>>+	void __iomem *base = wdev->base;
> >>>>+
> >>>>+	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
> >>>>+}
> >>>This isn't reliable. The sequence needed to enable the watchdog is
> >>>	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
> >>>	writel(0x4444, base + OMAP_WATCHDOG_SPR);
> >>>
> >>>The sequence to stop is:
> >>>	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
> >>>	writel(0x5555, base + OMAP_WATCHDOG_SPR);
> >>>
> >>>But:
> >>>
> >>>barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
> >>>44e35048: 00005555                                           UU..
> >>>barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
> >>>barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
> >>>44e35048: 00004444                                           DD..
> >>>
> >>>So the register contains 0x4444 but the timer doesn't run. So at best
> >>>testing for 0x4444 is a good heuristic.
> >>
> >>Yeah.. I don't think we can get any better than that. Unless we
> >>start checking the counter register and see whether it really counts
> >>or not, and I think that's a bit overkill.. So I'd say we should be
> >>safe when assuming bootloader is doing things correctly. Although,
> >>we could add a comment to the code that the test may not be 100%
> >>reliable in case the start sequence have not been issued properly.
> >>
> >>Thanks for pointing this out!
> >It doesn't seem to much overhead to do:
> >
> >	/*
> >	 * There is no register that tells us if the timer is running,
> >	 * so we have to resort to sample twice. The minimal frequency
> >	 * is 256 Hz (32768 Hz prescaled with 2**7).
> >	 */
> >	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> >	mdelay(4);
> >	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> >	return counter1 != counter2;
> >
> >I'd say it's even worth to do:
> >
> >	cntrl = readl(base + OMAP_WATCHDOG_CNTRL);
> >	if (cntrl & (1 << 5))
> >		shift = (cntrl >> 2) & 0x7;
> >	else
> >		shift = 0;
> >	counter1 = readl(base + OMAP_WATCHDOG_CCR);
> >	udelay(31 << shift);
> >	counter2 = readl(base + OMAP_WATCHDOG_CCR);
> >	return counter1 != counter2;
> >
> >For some bonus points add some defines for the magic constants.
> >
> >This is save as the OMAP_WATCHDOG_CNTRL doesn't seem to accept reads
s/reads/writes/ in the above line.

> >while the counter is running. Maybe even this could be used to detect a
> >running timer?:
> 
> Maybe, but you will get a data abort when the HW doesn't accept a
> read. How do you recover from that?
> 
> Also, it seems that for some reason the watchdog HW is very picky
> about which registers can be read in what condition. If I add the
> code to read watchdog counter register, I will get a data abort
> later at the end of the probe when the watchdog revision is read. I
> don't understand why that happens, this does not seem logica. Maybe
> it has got something to do with the fact that the watchdog is
> stopped? If I can't read the counter register, I can't really
> implement the above heuristic.. Any ideas?
Which SoC do you use for your tests? Sounds strange. If you provide me
your wip patch I can take a look.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply	[flat|nested] 66+ messages in thread

* Re: [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
  2015-05-07  7:30             ` Uwe Kleine-König
@ 2015-05-07  7:39               ` Timo Kokkonen
  -1 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-07  7:39 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-arm-kernel, linux-watchdog, boris.brezillon, nicolas.ferre,
	alexandre.belloni, Wenyou.Yang

On 07.05.2015 10:30, Uwe Kleine-König wrote:
> Hello Timo,
>
> On Thu, May 07, 2015 at 09:42:44AM +0300, Timo Kokkonen wrote:
>> On 04.05.2015 10:04, Uwe Kleine-König wrote:
>>> On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
>>>> Hi, 03.05.2015 21:56, Uwe Kleine-König wrote:
>>>>> On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
>>>>>> +static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
>>>>>> +{
>>>>>> +	void __iomem *base = wdev->base;
>>>>>> +
>>>>>> +	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
>>>>>> +}
>>>>> This isn't reliable. The sequence needed to enable the watchdog is
>>>>> 	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
>>>>> 	writel(0x4444, base + OMAP_WATCHDOG_SPR);
>>>>>
>>>>> The sequence to stop is:
>>>>> 	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
>>>>> 	writel(0x5555, base + OMAP_WATCHDOG_SPR);
>>>>>
>>>>> But:
>>>>>
>>>>> barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
>>>>> 44e35048: 00005555                                           UU..
>>>>> barebox@TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
>>>>> barebox@TI AM335x BeagleBone black:/ md 0x44e35048+4
>>>>> 44e35048: 00004444                                           DD..
>>>>>
>>>>> So the register contains 0x4444 but the timer doesn't run. So at best
>>>>> testing for 0x4444 is a good heuristic.
>>>>
>>>> Yeah.. I don't think we can get any better than that. Unless we
>>>> start checking the counter register and see whether it really counts
>>>> or not, and I think that's a bit overkill.. So I'd say we should be
>>>> safe when assuming bootloader is doing things correctly. Although,
>>>> we could add a comment to the code that the test may not be 100%
>>>> reliable in case the start sequence have not been issued properly.
>>>>
>>>> Thanks for pointing this out!
>>> It doesn't seem to much overhead to do:
>>>
>>> 	/*
>>> 	 * There is no register that tells us if the timer is running,
>>> 	 * so we have to resort to sample twice. The minimal frequency
>>> 	 * is 256 Hz (32768 Hz prescaled with 2**7).
>>> 	 */
>>> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
>>> 	mdelay(4);
>>> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
>>> 	return counter1 != counter2;
>>>
>>> I'd say it's even worth to do:
>>>
>>> 	cntrl = readl(base + OMAP_WATCHDOG_CNTRL);
>>> 	if (cntrl & (1 << 5))
>>> 		shift = (cntrl >> 2) & 0x7;
>>> 	else
>>> 		shift = 0;
>>> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
>>> 	udelay(31 << shift);
>>> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
>>> 	return counter1 != counter2;
>>>
>>> For some bonus points add some defines for the magic constants.
>>>
>>> This is save as the OMAP_WATCHDOG_CNTRL doesn't seem to accept reads
> s/reads/writes/ in the above line.
>
>>> while the counter is running. Maybe even this could be used to detect a
>>> running timer?:
>>
>> Maybe, but you will get a data abort when the HW doesn't accept a
>> read. How do you recover from that?
>>
>> Also, it seems that for some reason the watchdog HW is very picky
>> about which registers can be read in what condition. If I add the
>> code to read watchdog counter register, I will get a data abort
>> later at the end of the probe when the watchdog revision is read. I
>> don't understand why that happens, this does not seem logica. Maybe
>> it has got something to do with the fact that the watchdog is
>> stopped? If I can't read the counter register, I can't really
>> implement the above heuristic.. Any ideas?
> Which SoC do you use for your tests? Sounds strange. If you provide me
> your wip patch I can take a look.
>

I tried it on BeagleBone which has am35xx

I tried both approaches and neither were working:

diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index f86775e..b929c83 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -41,6 +41,7 @@
  #include <linux/slab.h>
  #include <linux/pm_runtime.h>
  #include <linux/platform_data/omap-wd-timer.h>
+#include <linux/delay.h>

  #include "omap_wdt.h"

@@ -81,6 +82,31 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
  static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
  {
  	void __iomem *base = wdev->base;
+	unsigned long cntrl, counter1, counter2, shift;
+       /*
+	* There is no register that tells us if the timer is running,
+	* so we have to resort to sample twice. Use shortest delay
+	* depending on the actual prescaling value.
+	*
+	* Note! If bootloader configured a very large prescaler
+	* value, we might delay up to 4ms here. If that happens, you
+	* are better to fix your bootloader anyway!
+	*/
+
+	/*
+	cntrl = readl_relaxed(base + OMAP_WATCHDOG_CNTRL);
+	if (cntrl & (1 << 5))
+		shift = (cntrl >> 2) & 0x7;
+	else
+		shift = 0;
+	*/
+
+	counter1 = readl_relaxed(base + OMAP_WATCHDOG_CRR);
+	/*udelay(31 << shift);*/
+	msleep(4);
+	counter2 = readl_relaxed(base + OMAP_WATCHDOG_CRR);
+
+	return counter1 != counter2;

  	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
  }



--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 66+ messages in thread

* [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions
@ 2015-05-07  7:39               ` Timo Kokkonen
  0 siblings, 0 replies; 66+ messages in thread
From: Timo Kokkonen @ 2015-05-07  7:39 UTC (permalink / raw)
  To: linux-arm-kernel

On 07.05.2015 10:30, Uwe Kleine-K?nig wrote:
> Hello Timo,
>
> On Thu, May 07, 2015 at 09:42:44AM +0300, Timo Kokkonen wrote:
>> On 04.05.2015 10:04, Uwe Kleine-K?nig wrote:
>>> On Mon, May 04, 2015 at 08:59:03AM +0300, Timo Kokkonen wrote:
>>>> Hi, 03.05.2015 21:56, Uwe Kleine-K?nig wrote:
>>>>> On Wed, Apr 22, 2015 at 02:11:42PM +0300, Timo Kokkonen wrote:
>>>>>> +static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
>>>>>> +{
>>>>>> +	void __iomem *base = wdev->base;
>>>>>> +
>>>>>> +	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
>>>>>> +}
>>>>> This isn't reliable. The sequence needed to enable the watchdog is
>>>>> 	writel(0xbbbb, base + OMAP_WATCHDOG_SPR);
>>>>> 	writel(0x4444, base + OMAP_WATCHDOG_SPR);
>>>>>
>>>>> The sequence to stop is:
>>>>> 	writel(0xaaaa, base + OMAP_WATCHDOG_SPR);
>>>>> 	writel(0x5555, base + OMAP_WATCHDOG_SPR);
>>>>>
>>>>> But:
>>>>>
>>>>> barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
>>>>> 44e35048: 00005555                                           UU..
>>>>> barebox at TI AM335x BeagleBone black:/ mw 0x44e35048 0x4444
>>>>> barebox at TI AM335x BeagleBone black:/ md 0x44e35048+4
>>>>> 44e35048: 00004444                                           DD..
>>>>>
>>>>> So the register contains 0x4444 but the timer doesn't run. So at best
>>>>> testing for 0x4444 is a good heuristic.
>>>>
>>>> Yeah.. I don't think we can get any better than that. Unless we
>>>> start checking the counter register and see whether it really counts
>>>> or not, and I think that's a bit overkill.. So I'd say we should be
>>>> safe when assuming bootloader is doing things correctly. Although,
>>>> we could add a comment to the code that the test may not be 100%
>>>> reliable in case the start sequence have not been issued properly.
>>>>
>>>> Thanks for pointing this out!
>>> It doesn't seem to much overhead to do:
>>>
>>> 	/*
>>> 	 * There is no register that tells us if the timer is running,
>>> 	 * so we have to resort to sample twice. The minimal frequency
>>> 	 * is 256 Hz (32768 Hz prescaled with 2**7).
>>> 	 */
>>> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
>>> 	mdelay(4);
>>> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
>>> 	return counter1 != counter2;
>>>
>>> I'd say it's even worth to do:
>>>
>>> 	cntrl = readl(base + OMAP_WATCHDOG_CNTRL);
>>> 	if (cntrl & (1 << 5))
>>> 		shift = (cntrl >> 2) & 0x7;
>>> 	else
>>> 		shift = 0;
>>> 	counter1 = readl(base + OMAP_WATCHDOG_CCR);
>>> 	udelay(31 << shift);
>>> 	counter2 = readl(base + OMAP_WATCHDOG_CCR);
>>> 	return counter1 != counter2;
>>>
>>> For some bonus points add some defines for the magic constants.
>>>
>>> This is save as the OMAP_WATCHDOG_CNTRL doesn't seem to accept reads
> s/reads/writes/ in the above line.
>
>>> while the counter is running. Maybe even this could be used to detect a
>>> running timer?:
>>
>> Maybe, but you will get a data abort when the HW doesn't accept a
>> read. How do you recover from that?
>>
>> Also, it seems that for some reason the watchdog HW is very picky
>> about which registers can be read in what condition. If I add the
>> code to read watchdog counter register, I will get a data abort
>> later at the end of the probe when the watchdog revision is read. I
>> don't understand why that happens, this does not seem logica. Maybe
>> it has got something to do with the fact that the watchdog is
>> stopped? If I can't read the counter register, I can't really
>> implement the above heuristic.. Any ideas?
> Which SoC do you use for your tests? Sounds strange. If you provide me
> your wip patch I can take a look.
>

I tried it on BeagleBone which has am35xx

I tried both approaches and neither were working:

diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index f86775e..b929c83 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -41,6 +41,7 @@
  #include <linux/slab.h>
  #include <linux/pm_runtime.h>
  #include <linux/platform_data/omap-wd-timer.h>
+#include <linux/delay.h>

  #include "omap_wdt.h"

@@ -81,6 +82,31 @@ static void omap_wdt_reload(struct omap_wdt_dev *wdev)
  static int omap_wdt_is_running(struct omap_wdt_dev *wdev)
  {
  	void __iomem *base = wdev->base;
+	unsigned long cntrl, counter1, counter2, shift;
+       /*
+	* There is no register that tells us if the timer is running,
+	* so we have to resort to sample twice. Use shortest delay
+	* depending on the actual prescaling value.
+	*
+	* Note! If bootloader configured a very large prescaler
+	* value, we might delay up to 4ms here. If that happens, you
+	* are better to fix your bootloader anyway!
+	*/
+
+	/*
+	cntrl = readl_relaxed(base + OMAP_WATCHDOG_CNTRL);
+	if (cntrl & (1 << 5))
+		shift = (cntrl >> 2) & 0x7;
+	else
+		shift = 0;
+	*/
+
+	counter1 = readl_relaxed(base + OMAP_WATCHDOG_CRR);
+	/*udelay(31 << shift);*/
+	msleep(4);
+	counter2 = readl_relaxed(base + OMAP_WATCHDOG_CRR);
+
+	return counter1 != counter2;

  	return readl_relaxed(base + OMAP_WATCHDOG_SPR) == 0x4444;
  }

^ permalink raw reply related	[flat|nested] 66+ messages in thread

end of thread, other threads:[~2015-05-07  7:39 UTC | newest]

Thread overview: 66+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-22 11:11 [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature Timo Kokkonen
2015-04-22 11:11 ` Timo Kokkonen
2015-04-22 11:11 ` [PATCHv7 1/8] watchdog: Extend kernel API to know about HW limitations Timo Kokkonen
2015-04-22 11:11   ` Timo Kokkonen
2015-04-24 17:08   ` Guenter Roeck
2015-04-24 17:08     ` Guenter Roeck
2015-04-27  5:41     ` Timo Kokkonen
2015-04-27  5:41       ` Timo Kokkonen
2015-05-04  7:58   ` Uwe Kleine-König
2015-05-04  7:58     ` Uwe Kleine-König
2015-05-04  9:40     ` Timo Kokkonen
2015-05-04  9:40       ` Timo Kokkonen
2015-05-04 15:43   ` Guenter Roeck
2015-05-04 15:43     ` Guenter Roeck
2015-05-05  6:26     ` Timo Kokkonen
2015-05-05  6:26       ` Timo Kokkonen
2015-05-04 21:17   ` Marc Kleine-Budde
2015-05-04 21:17     ` Marc Kleine-Budde
2015-04-22 11:11 ` [PATCHv7 2/8] watchdog: Allow watchdog to reset device at early boot Timo Kokkonen
2015-04-22 11:11   ` Timo Kokkonen
2015-04-22 11:11 ` [PATCHv7 3/8] devicetree: Document generic watchdog properties Timo Kokkonen
2015-04-22 11:11   ` Timo Kokkonen
2015-04-22 11:11 ` [PATCHv7 4/8] Documentation/watchdog: watchdog-test.c: Add support for changing timeout Timo Kokkonen
2015-04-22 11:11   ` Timo Kokkonen
2015-04-22 11:11 ` [PATCHv7 5/8] watchdog: at91sam9_wdt: Convert to use new watchdog core extensions Timo Kokkonen
2015-04-22 11:11   ` Timo Kokkonen
2015-04-22 11:11 ` [PATCHv7 6/8] watchdog: imx2_wdt: Convert to use new " Timo Kokkonen
2015-04-22 11:11   ` Timo Kokkonen
2015-05-05  8:11   ` Marc Kleine-Budde
2015-05-05  8:11     ` Marc Kleine-Budde
2015-05-05  8:31     ` Marc Kleine-Budde
2015-05-05  8:31       ` Marc Kleine-Budde
2015-05-05  9:07       ` Timo Kokkonen
2015-05-05  9:07         ` Timo Kokkonen
2015-04-22 11:11 ` [PATCHv7 7/8] watchdog: omap_wdt: Fix memory leak on probe fail Timo Kokkonen
2015-04-22 11:11   ` Timo Kokkonen
2015-04-26 15:32   ` Guenter Roeck
2015-04-26 15:32     ` Guenter Roeck
2015-04-27  5:50     ` Timo Kokkonen
2015-04-27  5:50       ` Timo Kokkonen
2015-04-22 11:11 ` [PATCHv7 8/8] watchdog: omap_wdt: Convert to use new core extensions Timo Kokkonen
2015-04-22 11:11   ` Timo Kokkonen
2015-05-03 18:56   ` Uwe Kleine-König
2015-05-03 18:56     ` Uwe Kleine-König
2015-05-04  5:59     ` Timo Kokkonen
2015-05-04  5:59       ` Timo Kokkonen
2015-05-04  7:04       ` Uwe Kleine-König
2015-05-04  7:04         ` Uwe Kleine-König
2015-05-04 10:06         ` Timo Kokkonen
2015-05-04 10:06           ` Timo Kokkonen
2015-05-07  6:42         ` Timo Kokkonen
2015-05-07  6:42           ` Timo Kokkonen
2015-05-07  7:30           ` Uwe Kleine-König
2015-05-07  7:30             ` Uwe Kleine-König
2015-05-07  7:39             ` Timo Kokkonen
2015-05-07  7:39               ` Timo Kokkonen
2015-05-04 16:08       ` Guenter Roeck
2015-05-04 16:08         ` Guenter Roeck
2015-05-05 13:50 ` [PATCHv7 0/8] watchdog: Extend kernel API and add early_timeout_sec feature Uwe Kleine-König
2015-05-05 13:50   ` Uwe Kleine-König
2015-05-06  7:26   ` Timo Kokkonen
2015-05-06  7:26     ` Timo Kokkonen
2015-05-06  7:48     ` Uwe Kleine-König
2015-05-06  7:48       ` Uwe Kleine-König
2015-05-06  8:23       ` Timo Kokkonen
2015-05-06  8:23         ` Timo Kokkonen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.