mirror of https://github.com/hak5/openwrt.git
ipq806x: backport upstream wdt driver
Signed-off-by: Pavel Kubelun <be.dissent@gmail.com>lede-17.01
parent
4a6f9fc633
commit
793d448a51
|
@ -412,6 +412,7 @@ CONFIG_VECTORS_BASE=0xffff0000
|
||||||
CONFIG_VFP=y
|
CONFIG_VFP=y
|
||||||
CONFIG_VFPv3=y
|
CONFIG_VFPv3=y
|
||||||
CONFIG_WATCHDOG_CORE=y
|
CONFIG_WATCHDOG_CORE=y
|
||||||
|
# CONFIG_WATCHDOG_SYSFS is not set
|
||||||
# CONFIG_WL_TI is not set
|
# CONFIG_WL_TI is not set
|
||||||
CONFIG_XPS=y
|
CONFIG_XPS=y
|
||||||
CONFIG_XZ_DEC_ARM=y
|
CONFIG_XZ_DEC_ARM=y
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
From 2165bf524da5f5e496d1cdb8c5afae1345ecce1e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Damien Riegel <damien.riegel@savoirfairelinux.com>
|
||||||
|
Date: Mon, 16 Nov 2015 12:27:59 -0500
|
||||||
|
Subject: watchdog: core: add restart handler support
|
||||||
|
|
||||||
|
Many watchdog drivers implement the same code to register a restart
|
||||||
|
handler. This patch provides a generic way to set such a function.
|
||||||
|
|
||||||
|
The patch adds a new restart watchdog operation. If a restart priority
|
||||||
|
greater than 0 is needed, the driver can call
|
||||||
|
watchdog_set_restart_priority to set it.
|
||||||
|
|
||||||
|
Suggested-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||||
|
Signed-off-by: Damien Riegel <damien.riegel@savoirfairelinux.com>
|
||||||
|
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
Documentation/watchdog/watchdog-kernel-api.txt | 19 ++++++++++
|
||||||
|
drivers/watchdog/watchdog_core.c | 48 ++++++++++++++++++++++++++
|
||||||
|
include/linux/watchdog.h | 6 ++++
|
||||||
|
3 files changed, 73 insertions(+)
|
||||||
|
|
||||||
|
--- a/Documentation/watchdog/watchdog-kernel-api.txt
|
||||||
|
+++ b/Documentation/watchdog/watchdog-kernel-api.txt
|
||||||
|
@@ -53,6 +53,7 @@ struct watchdog_device {
|
||||||
|
unsigned int timeout;
|
||||||
|
unsigned int min_timeout;
|
||||||
|
unsigned int max_timeout;
|
||||||
|
+ struct notifier_block restart_nb;
|
||||||
|
void *driver_data;
|
||||||
|
struct mutex lock;
|
||||||
|
unsigned long status;
|
||||||
|
@@ -75,6 +76,10 @@ It contains following fields:
|
||||||
|
* timeout: the watchdog timer's timeout value (in seconds).
|
||||||
|
* min_timeout: the watchdog timer's minimum timeout value (in seconds).
|
||||||
|
* max_timeout: the watchdog timer's maximum timeout value (in seconds).
|
||||||
|
+* restart_nb: notifier block that is registered for machine restart, for
|
||||||
|
+ internal use only. If a watchdog is capable of restarting the machine, it
|
||||||
|
+ should define ops->restart. Priority can be changed through
|
||||||
|
+ watchdog_set_restart_priority.
|
||||||
|
* bootstatus: status of the device after booting (reported with watchdog
|
||||||
|
WDIOF_* status bits).
|
||||||
|
* driver_data: a pointer to the drivers private data of a watchdog device.
|
||||||
|
@@ -100,6 +105,7 @@ struct watchdog_ops {
|
||||||
|
unsigned int (*status)(struct watchdog_device *);
|
||||||
|
int (*set_timeout)(struct watchdog_device *, unsigned int);
|
||||||
|
unsigned int (*get_timeleft)(struct watchdog_device *);
|
||||||
|
+ int (*restart)(struct watchdog_device *);
|
||||||
|
void (*ref)(struct watchdog_device *);
|
||||||
|
void (*unref)(struct watchdog_device *);
|
||||||
|
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
|
||||||
|
@@ -164,6 +170,8 @@ they are supported. These optional routi
|
||||||
|
(Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the
|
||||||
|
watchdog's info structure).
|
||||||
|
* get_timeleft: this routines returns the time that's left before a reset.
|
||||||
|
+* restart: this routine restarts the machine. It returns 0 on success or a
|
||||||
|
+ negative errno code for failure.
|
||||||
|
* ref: the operation that calls kref_get on the kref of a dynamically
|
||||||
|
allocated watchdog_device struct.
|
||||||
|
* unref: the operation that calls kref_put on the kref of a dynamically
|
||||||
|
@@ -231,3 +239,14 @@ the device tree (if the module timeout p
|
||||||
|
to set the default timeout value as timeout value in the watchdog_device and
|
||||||
|
then use this function to set the user "preferred" timeout value.
|
||||||
|
This routine returns zero on success and a negative errno code for failure.
|
||||||
|
+
|
||||||
|
+To change the priority of the restart handler the following helper should be
|
||||||
|
+used:
|
||||||
|
+
|
||||||
|
+void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
|
||||||
|
+
|
||||||
|
+User should follow the following guidelines for setting the priority:
|
||||||
|
+* 0: should be called in last resort, has limited restart capabilities
|
||||||
|
+* 128: default restart handler, use if no other handler is expected to be
|
||||||
|
+ available, and/or if restart is sufficient to restart the entire system
|
||||||
|
+* 255: highest priority, will preempt all other restart handlers
|
||||||
|
--- a/drivers/watchdog/watchdog_core.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_core.c
|
||||||
|
@@ -32,6 +32,7 @@
|
||||||
|
#include <linux/types.h> /* For standard types */
|
||||||
|
#include <linux/errno.h> /* For the -ENODEV/... values */
|
||||||
|
#include <linux/kernel.h> /* For printk/panic/... */
|
||||||
|
+#include <linux/reboot.h> /* For restart handler */
|
||||||
|
#include <linux/watchdog.h> /* For watchdog specific items */
|
||||||
|
#include <linux/init.h> /* For __init/__exit/... */
|
||||||
|
#include <linux/idr.h> /* For ida_* macros */
|
||||||
|
@@ -137,6 +138,41 @@ int watchdog_init_timeout(struct watchdo
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(watchdog_init_timeout);
|
||||||
|
|
||||||
|
+static int watchdog_restart_notifier(struct notifier_block *nb,
|
||||||
|
+ unsigned long action, void *data)
|
||||||
|
+{
|
||||||
|
+ struct watchdog_device *wdd = container_of(nb, struct watchdog_device,
|
||||||
|
+ restart_nb);
|
||||||
|
+
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ ret = wdd->ops->restart(wdd);
|
||||||
|
+ if (ret)
|
||||||
|
+ return NOTIFY_BAD;
|
||||||
|
+
|
||||||
|
+ return NOTIFY_DONE;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * watchdog_set_restart_priority - Change priority of restart handler
|
||||||
|
+ * @wdd: watchdog device
|
||||||
|
+ * @priority: priority of the restart handler, should follow these guidelines:
|
||||||
|
+ * 0: use watchdog's restart function as last resort, has limited restart
|
||||||
|
+ * capabilies
|
||||||
|
+ * 128: default restart handler, use if no other handler is expected to be
|
||||||
|
+ * available and/or if restart is sufficient to restart the entire system
|
||||||
|
+ * 255: preempt all other handlers
|
||||||
|
+ *
|
||||||
|
+ * If a wdd->ops->restart function is provided when watchdog_register_device is
|
||||||
|
+ * called, it will be registered as a restart handler with the priority given
|
||||||
|
+ * here.
|
||||||
|
+ */
|
||||||
|
+void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority)
|
||||||
|
+{
|
||||||
|
+ wdd->restart_nb.priority = priority;
|
||||||
|
+}
|
||||||
|
+EXPORT_SYMBOL_GPL(watchdog_set_restart_priority);
|
||||||
|
+
|
||||||
|
static int __watchdog_register_device(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
int ret, id = -1, devno;
|
||||||
|
@@ -202,6 +238,15 @@ static int __watchdog_register_device(st
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (wdd->ops->restart) {
|
||||||
|
+ wdd->restart_nb.notifier_call = watchdog_restart_notifier;
|
||||||
|
+
|
||||||
|
+ ret = register_restart_handler(&wdd->restart_nb);
|
||||||
|
+ if (ret)
|
||||||
|
+ dev_warn(wdd->dev, "Cannot register restart handler (%d)\n",
|
||||||
|
+ ret);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -238,6 +283,9 @@ static void __watchdog_unregister_device
|
||||||
|
if (wdd == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
+ if (wdd->ops->restart)
|
||||||
|
+ unregister_restart_handler(&wdd->restart_nb);
|
||||||
|
+
|
||||||
|
devno = wdd->cdev.dev;
|
||||||
|
ret = watchdog_dev_unregister(wdd);
|
||||||
|
if (ret)
|
||||||
|
--- 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/notifier.h>
|
||||||
|
#include <uapi/linux/watchdog.h>
|
||||||
|
|
||||||
|
struct watchdog_ops;
|
||||||
|
@@ -26,6 +27,7 @@ struct watchdog_device;
|
||||||
|
* @status: The routine that shows the status of the watchdog device.
|
||||||
|
* @set_timeout:The routine for setting the watchdog devices timeout value (in seconds).
|
||||||
|
* @get_timeleft:The routine that gets the time left before a reset (in seconds).
|
||||||
|
+ * @restart: The routine for restarting the machine.
|
||||||
|
* @ref: The ref operation for dyn. allocated watchdog_device structs
|
||||||
|
* @unref: The unref operation for dyn. allocated watchdog_device structs
|
||||||
|
* @ioctl: The routines that handles extra ioctl calls.
|
||||||
|
@@ -45,6 +47,7 @@ struct watchdog_ops {
|
||||||
|
unsigned int (*status)(struct watchdog_device *);
|
||||||
|
int (*set_timeout)(struct watchdog_device *, unsigned int);
|
||||||
|
unsigned int (*get_timeleft)(struct watchdog_device *);
|
||||||
|
+ int (*restart)(struct watchdog_device *);
|
||||||
|
void (*ref)(struct watchdog_device *);
|
||||||
|
void (*unref)(struct watchdog_device *);
|
||||||
|
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
|
||||||
|
@@ -62,6 +65,7 @@ struct watchdog_ops {
|
||||||
|
* @timeout: The watchdog devices timeout value (in seconds).
|
||||||
|
* @min_timeout:The watchdog devices minimum timeout value (in seconds).
|
||||||
|
* @max_timeout:The watchdog devices maximum timeout value (in seconds).
|
||||||
|
+ * @restart_nb: The notifier block to register a restart function.
|
||||||
|
* @driver-data:Pointer to the drivers private data.
|
||||||
|
* @lock: Lock for watchdog core internal use only.
|
||||||
|
* @status: Field that contains the devices internal status bits.
|
||||||
|
@@ -88,6 +92,7 @@ struct watchdog_device {
|
||||||
|
unsigned int timeout;
|
||||||
|
unsigned int min_timeout;
|
||||||
|
unsigned int max_timeout;
|
||||||
|
+ struct notifier_block restart_nb;
|
||||||
|
void *driver_data;
|
||||||
|
struct mutex lock;
|
||||||
|
unsigned long status;
|
||||||
|
@@ -142,6 +147,7 @@ static inline void *watchdog_get_drvdata
|
||||||
|
}
|
||||||
|
|
||||||
|
/* drivers/watchdog/watchdog_core.c */
|
||||||
|
+void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
|
||||||
|
extern int watchdog_init_timeout(struct watchdog_device *wdd,
|
||||||
|
unsigned int timeout_parm, struct device *dev);
|
||||||
|
extern int watchdog_register_device(struct watchdog_device *);
|
|
@ -0,0 +1,160 @@
|
||||||
|
From e131319669e0ef5e6fcd75174daeffa40492135c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Damien Riegel <damien.riegel@savoirfairelinux.com>
|
||||||
|
Date: Fri, 20 Nov 2015 16:54:51 -0500
|
||||||
|
Subject: watchdog: core: add reboot notifier support
|
||||||
|
|
||||||
|
Many watchdog drivers register a reboot notifier in order to stop the
|
||||||
|
watchdog on system reboot. Thus we can factorize this code in the
|
||||||
|
watchdog core.
|
||||||
|
|
||||||
|
For that purpose, a new notifier block is added in watchdog_device for
|
||||||
|
internal use only, as well as a new watchdog_stop_on_reboot helper
|
||||||
|
function.
|
||||||
|
|
||||||
|
If this helper is called, watchdog core registers the related notifier
|
||||||
|
block and will stop the watchdog when SYS_HALT or SYS_DOWN is received.
|
||||||
|
|
||||||
|
Since this operation can be critical on some platforms, abort the device
|
||||||
|
registration if the reboot notifier registration fails.
|
||||||
|
|
||||||
|
Suggested-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||||
|
Signed-off-by: Damien Riegel <damien.riegel@savoirfairelinux.com>
|
||||||
|
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
Documentation/watchdog/watchdog-kernel-api.txt | 8 ++++++
|
||||||
|
drivers/watchdog/watchdog_core.c | 37 ++++++++++++++++++++++++++
|
||||||
|
include/linux/watchdog.h | 9 +++++++
|
||||||
|
3 files changed, 54 insertions(+)
|
||||||
|
|
||||||
|
--- a/Documentation/watchdog/watchdog-kernel-api.txt
|
||||||
|
+++ b/Documentation/watchdog/watchdog-kernel-api.txt
|
||||||
|
@@ -53,6 +53,7 @@ struct watchdog_device {
|
||||||
|
unsigned int timeout;
|
||||||
|
unsigned int min_timeout;
|
||||||
|
unsigned int max_timeout;
|
||||||
|
+ struct notifier_block reboot_nb;
|
||||||
|
struct notifier_block restart_nb;
|
||||||
|
void *driver_data;
|
||||||
|
struct mutex lock;
|
||||||
|
@@ -76,6 +77,9 @@ It contains following fields:
|
||||||
|
* timeout: the watchdog timer's timeout value (in seconds).
|
||||||
|
* min_timeout: the watchdog timer's minimum timeout value (in seconds).
|
||||||
|
* max_timeout: the watchdog timer's maximum timeout value (in seconds).
|
||||||
|
+* reboot_nb: notifier block that is registered for reboot notifications, for
|
||||||
|
+ internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core
|
||||||
|
+ will stop the watchdog on such notifications.
|
||||||
|
* restart_nb: notifier block that is registered for machine restart, for
|
||||||
|
internal use only. If a watchdog is capable of restarting the machine, it
|
||||||
|
should define ops->restart. Priority can be changed through
|
||||||
|
@@ -240,6 +244,10 @@ to set the default timeout value as time
|
||||||
|
then use this function to set the user "preferred" timeout value.
|
||||||
|
This routine returns zero on success and a negative errno code for failure.
|
||||||
|
|
||||||
|
+To disable the watchdog on reboot, the user must call the following helper:
|
||||||
|
+
|
||||||
|
+static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd);
|
||||||
|
+
|
||||||
|
To change the priority of the restart handler the following helper should be
|
||||||
|
used:
|
||||||
|
|
||||||
|
--- a/drivers/watchdog/watchdog_core.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_core.c
|
||||||
|
@@ -138,6 +138,25 @@ int watchdog_init_timeout(struct watchdo
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(watchdog_init_timeout);
|
||||||
|
|
||||||
|
+static int watchdog_reboot_notifier(struct notifier_block *nb,
|
||||||
|
+ unsigned long code, void *data)
|
||||||
|
+{
|
||||||
|
+ struct watchdog_device *wdd = container_of(nb, struct watchdog_device,
|
||||||
|
+ reboot_nb);
|
||||||
|
+
|
||||||
|
+ if (code == SYS_DOWN || code == SYS_HALT) {
|
||||||
|
+ if (watchdog_active(wdd)) {
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ ret = wdd->ops->stop(wdd);
|
||||||
|
+ if (ret)
|
||||||
|
+ return NOTIFY_BAD;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return NOTIFY_DONE;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int watchdog_restart_notifier(struct notifier_block *nb,
|
||||||
|
unsigned long action, void *data)
|
||||||
|
{
|
||||||
|
@@ -238,6 +257,21 @@ static int __watchdog_register_device(st
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) {
|
||||||
|
+ wdd->reboot_nb.notifier_call = watchdog_reboot_notifier;
|
||||||
|
+
|
||||||
|
+ ret = register_reboot_notifier(&wdd->reboot_nb);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(wdd->dev, "Cannot register reboot notifier (%d)\n",
|
||||||
|
+ ret);
|
||||||
|
+ watchdog_dev_unregister(wdd);
|
||||||
|
+ device_destroy(watchdog_class, devno);
|
||||||
|
+ ida_simple_remove(&watchdog_ida, wdd->id);
|
||||||
|
+ wdd->dev = NULL;
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (wdd->ops->restart) {
|
||||||
|
wdd->restart_nb.notifier_call = watchdog_restart_notifier;
|
||||||
|
|
||||||
|
@@ -286,6 +320,9 @@ static void __watchdog_unregister_device
|
||||||
|
if (wdd->ops->restart)
|
||||||
|
unregister_restart_handler(&wdd->restart_nb);
|
||||||
|
|
||||||
|
+ if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status))
|
||||||
|
+ unregister_reboot_notifier(&wdd->reboot_nb);
|
||||||
|
+
|
||||||
|
devno = wdd->cdev.dev;
|
||||||
|
ret = watchdog_dev_unregister(wdd);
|
||||||
|
if (ret)
|
||||||
|
--- a/include/linux/watchdog.h
|
||||||
|
+++ b/include/linux/watchdog.h
|
||||||
|
@@ -65,6 +65,7 @@ struct watchdog_ops {
|
||||||
|
* @timeout: The watchdog devices timeout value (in seconds).
|
||||||
|
* @min_timeout:The watchdog devices minimum timeout value (in seconds).
|
||||||
|
* @max_timeout:The watchdog devices maximum timeout value (in seconds).
|
||||||
|
+ * @reboot_nb: The notifier block to stop watchdog on reboot.
|
||||||
|
* @restart_nb: The notifier block to register a restart function.
|
||||||
|
* @driver-data:Pointer to the drivers private data.
|
||||||
|
* @lock: Lock for watchdog core internal use only.
|
||||||
|
@@ -92,6 +93,7 @@ struct watchdog_device {
|
||||||
|
unsigned int timeout;
|
||||||
|
unsigned int min_timeout;
|
||||||
|
unsigned int max_timeout;
|
||||||
|
+ struct notifier_block reboot_nb;
|
||||||
|
struct notifier_block restart_nb;
|
||||||
|
void *driver_data;
|
||||||
|
struct mutex lock;
|
||||||
|
@@ -102,6 +104,7 @@ 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 */
|
||||||
|
+#define WDOG_STOP_ON_REBOOT 5 /* Should be stopped on reboot */
|
||||||
|
struct list_head deferred;
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -121,6 +124,12 @@ static inline void watchdog_set_nowayout
|
||||||
|
set_bit(WDOG_NO_WAY_OUT, &wdd->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
+/* Use the following function to stop the watchdog on reboot */
|
||||||
|
+static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd)
|
||||||
|
+{
|
||||||
|
+ set_bit(WDOG_STOP_ON_REBOOT, &wdd->status);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/* Use the following function to check if a timeout value is invalid */
|
||||||
|
static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t)
|
||||||
|
{
|
|
@ -0,0 +1,111 @@
|
||||||
|
From 906d7a5cfeda508e7361f021605579a00cd82815 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Pratyush Anand <panand@redhat.com>
|
||||||
|
Date: Thu, 17 Dec 2015 17:53:58 +0530
|
||||||
|
Subject: watchdog: Use static struct class watchdog_class in stead of pointer
|
||||||
|
|
||||||
|
We need few sysfs attributes to know different status of a watchdog device.
|
||||||
|
To do that, we need to associate .dev_groups with watchdog_class. So
|
||||||
|
convert it from pointer to static.
|
||||||
|
Putting this static struct in watchdog_dev.c, so that static device
|
||||||
|
attributes defined in that file can be attached to it.
|
||||||
|
|
||||||
|
Signed-off-by: Pratyush Anand <panand@redhat.com>
|
||||||
|
Suggested-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
drivers/watchdog/watchdog_core.c | 15 ++-------------
|
||||||
|
drivers/watchdog/watchdog_core.h | 2 +-
|
||||||
|
drivers/watchdog/watchdog_dev.c | 26 ++++++++++++++++++++++----
|
||||||
|
3 files changed, 25 insertions(+), 18 deletions(-)
|
||||||
|
|
||||||
|
--- a/drivers/watchdog/watchdog_core.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_core.c
|
||||||
|
@@ -370,19 +370,9 @@ static int __init watchdog_deferred_regi
|
||||||
|
|
||||||
|
static int __init watchdog_init(void)
|
||||||
|
{
|
||||||
|
- int err;
|
||||||
|
-
|
||||||
|
- watchdog_class = class_create(THIS_MODULE, "watchdog");
|
||||||
|
- if (IS_ERR(watchdog_class)) {
|
||||||
|
- pr_err("couldn't create class\n");
|
||||||
|
+ watchdog_class = watchdog_dev_init();
|
||||||
|
+ if (IS_ERR(watchdog_class))
|
||||||
|
return PTR_ERR(watchdog_class);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- err = watchdog_dev_init();
|
||||||
|
- if (err < 0) {
|
||||||
|
- class_destroy(watchdog_class);
|
||||||
|
- return err;
|
||||||
|
- }
|
||||||
|
|
||||||
|
watchdog_deferred_registration();
|
||||||
|
return 0;
|
||||||
|
@@ -391,7 +381,6 @@ static int __init watchdog_init(void)
|
||||||
|
static void __exit watchdog_exit(void)
|
||||||
|
{
|
||||||
|
watchdog_dev_exit();
|
||||||
|
- class_destroy(watchdog_class);
|
||||||
|
ida_destroy(&watchdog_ida);
|
||||||
|
}
|
||||||
|
|
||||||
|
--- a/drivers/watchdog/watchdog_core.h
|
||||||
|
+++ b/drivers/watchdog/watchdog_core.h
|
||||||
|
@@ -33,5 +33,5 @@
|
||||||
|
*/
|
||||||
|
extern int watchdog_dev_register(struct watchdog_device *);
|
||||||
|
extern int watchdog_dev_unregister(struct watchdog_device *);
|
||||||
|
-extern int __init watchdog_dev_init(void);
|
||||||
|
+extern struct class * __init watchdog_dev_init(void);
|
||||||
|
extern void __exit watchdog_dev_exit(void);
|
||||||
|
--- a/drivers/watchdog/watchdog_dev.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_dev.c
|
||||||
|
@@ -581,18 +581,35 @@ int watchdog_dev_unregister(struct watch
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static struct class watchdog_class = {
|
||||||
|
+ .name = "watchdog",
|
||||||
|
+ .owner = THIS_MODULE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* watchdog_dev_init: init dev part of watchdog core
|
||||||
|
*
|
||||||
|
* Allocate a range of chardev nodes to use for watchdog devices
|
||||||
|
*/
|
||||||
|
|
||||||
|
-int __init watchdog_dev_init(void)
|
||||||
|
+struct class * __init watchdog_dev_init(void)
|
||||||
|
{
|
||||||
|
- int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog");
|
||||||
|
- if (err < 0)
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ err = class_register(&watchdog_class);
|
||||||
|
+ if (err < 0) {
|
||||||
|
+ pr_err("couldn't register class\n");
|
||||||
|
+ return ERR_PTR(err);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog");
|
||||||
|
+ if (err < 0) {
|
||||||
|
pr_err("watchdog: unable to allocate char dev region\n");
|
||||||
|
- return err;
|
||||||
|
+ class_unregister(&watchdog_class);
|
||||||
|
+ return ERR_PTR(err);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return &watchdog_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -604,4 +621,5 @@ int __init watchdog_dev_init(void)
|
||||||
|
void __exit watchdog_dev_exit(void)
|
||||||
|
{
|
||||||
|
unregister_chrdev_region(watchdog_devt, MAX_DOGS);
|
||||||
|
+ class_unregister(&watchdog_class);
|
||||||
|
}
|
|
@ -0,0 +1,260 @@
|
||||||
|
From 33b711269ade3f6bc9d9d15e4343e6fa922d999b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Pratyush Anand <panand@redhat.com>
|
||||||
|
Date: Thu, 17 Dec 2015 17:53:59 +0530
|
||||||
|
Subject: watchdog: Read device status through sysfs attributes
|
||||||
|
|
||||||
|
This patch adds following attributes to watchdog device's sysfs interface
|
||||||
|
to read its different status.
|
||||||
|
|
||||||
|
* state - reads whether device is active or not
|
||||||
|
* identity - reads Watchdog device's identity string.
|
||||||
|
* timeout - reads current timeout.
|
||||||
|
* timeleft - reads timeleft before watchdog generates a reset
|
||||||
|
* bootstatus - reads status of the watchdog device at boot
|
||||||
|
* status - reads watchdog device's internal status bits
|
||||||
|
* nowayout - reads whether nowayout feature was set or not
|
||||||
|
|
||||||
|
Testing with iTCO_wdt:
|
||||||
|
# cd /sys/class/watchdog/watchdog1/
|
||||||
|
# ls
|
||||||
|
bootstatus dev device identity nowayout power state
|
||||||
|
subsystem timeleft timeout uevent
|
||||||
|
# cat identity
|
||||||
|
iTCO_wdt
|
||||||
|
# cat timeout
|
||||||
|
30
|
||||||
|
# cat state
|
||||||
|
inactive
|
||||||
|
# echo > /dev/watchdog1
|
||||||
|
# cat timeleft
|
||||||
|
26
|
||||||
|
# cat state
|
||||||
|
active
|
||||||
|
# cat bootstatus
|
||||||
|
0
|
||||||
|
# cat nowayout
|
||||||
|
0
|
||||||
|
|
||||||
|
Signed-off-by: Pratyush Anand <panand@redhat.com>
|
||||||
|
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
Documentation/ABI/testing/sysfs-class-watchdog | 51 +++++++++++
|
||||||
|
drivers/watchdog/Kconfig | 7 ++
|
||||||
|
drivers/watchdog/watchdog_core.c | 2 +-
|
||||||
|
drivers/watchdog/watchdog_dev.c | 114 +++++++++++++++++++++++++
|
||||||
|
4 files changed, 173 insertions(+), 1 deletion(-)
|
||||||
|
create mode 100644 Documentation/ABI/testing/sysfs-class-watchdog
|
||||||
|
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Documentation/ABI/testing/sysfs-class-watchdog
|
||||||
|
@@ -0,0 +1,51 @@
|
||||||
|
+What: /sys/class/watchdog/watchdogn/bootstatus
|
||||||
|
+Date: August 2015
|
||||||
|
+Contact: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
+Description:
|
||||||
|
+ It is a read only file. It contains status of the watchdog
|
||||||
|
+ device at boot. It is equivalent to WDIOC_GETBOOTSTATUS of
|
||||||
|
+ ioctl interface.
|
||||||
|
+
|
||||||
|
+What: /sys/class/watchdog/watchdogn/identity
|
||||||
|
+Date: August 2015
|
||||||
|
+Contact: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
+Description:
|
||||||
|
+ It is a read only file. It contains identity string of
|
||||||
|
+ watchdog device.
|
||||||
|
+
|
||||||
|
+What: /sys/class/watchdog/watchdogn/nowayout
|
||||||
|
+Date: August 2015
|
||||||
|
+Contact: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
+Description:
|
||||||
|
+ It is a read only file. While reading, it gives '1' if that
|
||||||
|
+ device supports nowayout feature else, it gives '0'.
|
||||||
|
+
|
||||||
|
+What: /sys/class/watchdog/watchdogn/state
|
||||||
|
+Date: August 2015
|
||||||
|
+Contact: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
+Description:
|
||||||
|
+ It is a read only file. It gives active/inactive status of
|
||||||
|
+ watchdog device.
|
||||||
|
+
|
||||||
|
+What: /sys/class/watchdog/watchdogn/status
|
||||||
|
+Date: August 2015
|
||||||
|
+Contact: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
+Description:
|
||||||
|
+ It is a read only file. It contains watchdog device's
|
||||||
|
+ internal status bits. It is equivalent to WDIOC_GETSTATUS
|
||||||
|
+ of ioctl interface.
|
||||||
|
+
|
||||||
|
+What: /sys/class/watchdog/watchdogn/timeleft
|
||||||
|
+Date: August 2015
|
||||||
|
+Contact: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
+Description:
|
||||||
|
+ It is a read only file. It contains value of time left for
|
||||||
|
+ reset generation. It is equivalent to WDIOC_GETTIMELEFT of
|
||||||
|
+ ioctl interface.
|
||||||
|
+
|
||||||
|
+What: /sys/class/watchdog/watchdogn/timeout
|
||||||
|
+Date: August 2015
|
||||||
|
+Contact: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
+Description:
|
||||||
|
+ It is a read only file. It is read to know about current
|
||||||
|
+ value of timeout programmed.
|
||||||
|
--- a/drivers/watchdog/Kconfig
|
||||||
|
+++ b/drivers/watchdog/Kconfig
|
||||||
|
@@ -46,6 +46,13 @@ config WATCHDOG_NOWAYOUT
|
||||||
|
get killed. If you say Y here, the watchdog cannot be stopped once
|
||||||
|
it has been started.
|
||||||
|
|
||||||
|
+config WATCHDOG_SYSFS
|
||||||
|
+ bool "Read different watchdog information through sysfs"
|
||||||
|
+ default n
|
||||||
|
+ help
|
||||||
|
+ Say Y here if you want to enable watchdog device status read through
|
||||||
|
+ sysfs attributes.
|
||||||
|
+
|
||||||
|
#
|
||||||
|
# General Watchdog drivers
|
||||||
|
#
|
||||||
|
--- a/drivers/watchdog/watchdog_core.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_core.c
|
||||||
|
@@ -249,7 +249,7 @@ static int __watchdog_register_device(st
|
||||||
|
|
||||||
|
devno = wdd->cdev.dev;
|
||||||
|
wdd->dev = device_create(watchdog_class, wdd->parent, devno,
|
||||||
|
- NULL, "watchdog%d", wdd->id);
|
||||||
|
+ wdd, "watchdog%d", wdd->id);
|
||||||
|
if (IS_ERR(wdd->dev)) {
|
||||||
|
watchdog_dev_unregister(wdd);
|
||||||
|
ida_simple_remove(&watchdog_ida, id);
|
||||||
|
--- a/drivers/watchdog/watchdog_dev.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_dev.c
|
||||||
|
@@ -247,6 +247,119 @@ out_timeleft:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
+#ifdef CONFIG_WATCHDOG_SYSFS
|
||||||
|
+static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
+ char *buf)
|
||||||
|
+{
|
||||||
|
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
+
|
||||||
|
+ return sprintf(buf, "%d\n", !!test_bit(WDOG_NO_WAY_OUT, &wdd->status));
|
||||||
|
+}
|
||||||
|
+static DEVICE_ATTR_RO(nowayout);
|
||||||
|
+
|
||||||
|
+static ssize_t status_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
+ char *buf)
|
||||||
|
+{
|
||||||
|
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
+ ssize_t status;
|
||||||
|
+ unsigned int val;
|
||||||
|
+
|
||||||
|
+ status = watchdog_get_status(wdd, &val);
|
||||||
|
+ if (!status)
|
||||||
|
+ status = sprintf(buf, "%u\n", val);
|
||||||
|
+
|
||||||
|
+ return status;
|
||||||
|
+}
|
||||||
|
+static DEVICE_ATTR_RO(status);
|
||||||
|
+
|
||||||
|
+static ssize_t bootstatus_show(struct device *dev,
|
||||||
|
+ struct device_attribute *attr, char *buf)
|
||||||
|
+{
|
||||||
|
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
+
|
||||||
|
+ return sprintf(buf, "%u\n", wdd->bootstatus);
|
||||||
|
+}
|
||||||
|
+static DEVICE_ATTR_RO(bootstatus);
|
||||||
|
+
|
||||||
|
+static ssize_t timeleft_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
+ char *buf)
|
||||||
|
+{
|
||||||
|
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
+ ssize_t status;
|
||||||
|
+ unsigned int val;
|
||||||
|
+
|
||||||
|
+ status = watchdog_get_timeleft(wdd, &val);
|
||||||
|
+ if (!status)
|
||||||
|
+ status = sprintf(buf, "%u\n", val);
|
||||||
|
+
|
||||||
|
+ return status;
|
||||||
|
+}
|
||||||
|
+static DEVICE_ATTR_RO(timeleft);
|
||||||
|
+
|
||||||
|
+static ssize_t timeout_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
+ char *buf)
|
||||||
|
+{
|
||||||
|
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
+
|
||||||
|
+ return sprintf(buf, "%u\n", wdd->timeout);
|
||||||
|
+}
|
||||||
|
+static DEVICE_ATTR_RO(timeout);
|
||||||
|
+
|
||||||
|
+static ssize_t identity_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
+ char *buf)
|
||||||
|
+{
|
||||||
|
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
+
|
||||||
|
+ return sprintf(buf, "%s\n", wdd->info->identity);
|
||||||
|
+}
|
||||||
|
+static DEVICE_ATTR_RO(identity);
|
||||||
|
+
|
||||||
|
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
+ char *buf)
|
||||||
|
+{
|
||||||
|
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
+
|
||||||
|
+ if (watchdog_active(wdd))
|
||||||
|
+ return sprintf(buf, "active\n");
|
||||||
|
+
|
||||||
|
+ return sprintf(buf, "inactive\n");
|
||||||
|
+}
|
||||||
|
+static DEVICE_ATTR_RO(state);
|
||||||
|
+
|
||||||
|
+static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
|
||||||
|
+ int n)
|
||||||
|
+{
|
||||||
|
+ struct device *dev = container_of(kobj, struct device, kobj);
|
||||||
|
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
+ umode_t mode = attr->mode;
|
||||||
|
+
|
||||||
|
+ if (attr == &dev_attr_status.attr && !wdd->ops->status)
|
||||||
|
+ mode = 0;
|
||||||
|
+ else if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
|
||||||
|
+ mode = 0;
|
||||||
|
+
|
||||||
|
+ return mode;
|
||||||
|
+}
|
||||||
|
+static struct attribute *wdt_attrs[] = {
|
||||||
|
+ &dev_attr_state.attr,
|
||||||
|
+ &dev_attr_identity.attr,
|
||||||
|
+ &dev_attr_timeout.attr,
|
||||||
|
+ &dev_attr_timeleft.attr,
|
||||||
|
+ &dev_attr_bootstatus.attr,
|
||||||
|
+ &dev_attr_status.attr,
|
||||||
|
+ &dev_attr_nowayout.attr,
|
||||||
|
+ NULL,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct attribute_group wdt_group = {
|
||||||
|
+ .attrs = wdt_attrs,
|
||||||
|
+ .is_visible = wdt_is_visible,
|
||||||
|
+};
|
||||||
|
+__ATTRIBUTE_GROUPS(wdt);
|
||||||
|
+#else
|
||||||
|
+#define wdt_groups NULL
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* watchdog_ioctl_op: call the watchdog drivers ioctl op if defined
|
||||||
|
* @wdd: the watchdog device to do the ioctl on
|
||||||
|
@@ -584,6 +697,7 @@ int watchdog_dev_unregister(struct watch
|
||||||
|
static struct class watchdog_class = {
|
||||||
|
.name = "watchdog",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
+ .dev_groups = wdt_groups,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
|
@ -0,0 +1,261 @@
|
||||||
|
From 32ecc6392654a0db34b310e8924b5b2c3b8bf503 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Date: Fri, 25 Dec 2015 16:01:40 -0800
|
||||||
|
Subject: watchdog: Create watchdog device in watchdog_dev.c
|
||||||
|
|
||||||
|
The watchdog character device is currently created in watchdog_dev.c,
|
||||||
|
and the watchdog device in watchdog_core.c. This results in
|
||||||
|
cross-dependencies, since device creation needs to know the watchdog
|
||||||
|
character device number as well as the watchdog class, both of which
|
||||||
|
reside in watchdog_dev.c.
|
||||||
|
|
||||||
|
Create the watchdog device in watchdog_dev.c to simplify the code.
|
||||||
|
|
||||||
|
Inspired by earlier patch set from Damien Riegel.
|
||||||
|
|
||||||
|
Cc: Damien Riegel <damien.riegel@savoirfairelinux.com>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
drivers/watchdog/watchdog_core.c | 33 ++++--------------
|
||||||
|
drivers/watchdog/watchdog_core.h | 4 +--
|
||||||
|
drivers/watchdog/watchdog_dev.c | 73 +++++++++++++++++++++++++++++++++-------
|
||||||
|
3 files changed, 69 insertions(+), 41 deletions(-)
|
||||||
|
|
||||||
|
--- a/drivers/watchdog/watchdog_core.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_core.c
|
||||||
|
@@ -42,7 +42,6 @@
|
||||||
|
#include "watchdog_core.h" /* For watchdog_dev_register/... */
|
||||||
|
|
||||||
|
static DEFINE_IDA(watchdog_ida);
|
||||||
|
-static struct class *watchdog_class;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deferred Registration infrastructure.
|
||||||
|
@@ -194,7 +193,7 @@ EXPORT_SYMBOL_GPL(watchdog_set_restart_p
|
||||||
|
|
||||||
|
static int __watchdog_register_device(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
- int ret, id = -1, devno;
|
||||||
|
+ int ret, id = -1;
|
||||||
|
|
||||||
|
if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
|
||||||
|
return -EINVAL;
|
||||||
|
@@ -247,16 +246,6 @@ static int __watchdog_register_device(st
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- devno = wdd->cdev.dev;
|
||||||
|
- wdd->dev = device_create(watchdog_class, wdd->parent, devno,
|
||||||
|
- wdd, "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;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) {
|
||||||
|
wdd->reboot_nb.notifier_call = watchdog_reboot_notifier;
|
||||||
|
|
||||||
|
@@ -265,9 +254,7 @@ static int __watchdog_register_device(st
|
||||||
|
dev_err(wdd->dev, "Cannot register reboot notifier (%d)\n",
|
||||||
|
ret);
|
||||||
|
watchdog_dev_unregister(wdd);
|
||||||
|
- device_destroy(watchdog_class, devno);
|
||||||
|
ida_simple_remove(&watchdog_ida, wdd->id);
|
||||||
|
- wdd->dev = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -311,9 +298,6 @@ EXPORT_SYMBOL_GPL(watchdog_register_devi
|
||||||
|
|
||||||
|
static void __watchdog_unregister_device(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
- int ret;
|
||||||
|
- int devno;
|
||||||
|
-
|
||||||
|
if (wdd == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
@@ -323,13 +307,8 @@ static void __watchdog_unregister_device
|
||||||
|
if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status))
|
||||||
|
unregister_reboot_notifier(&wdd->reboot_nb);
|
||||||
|
|
||||||
|
- devno = wdd->cdev.dev;
|
||||||
|
- ret = watchdog_dev_unregister(wdd);
|
||||||
|
- if (ret)
|
||||||
|
- pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
|
||||||
|
- device_destroy(watchdog_class, devno);
|
||||||
|
+ watchdog_dev_unregister(wdd);
|
||||||
|
ida_simple_remove(&watchdog_ida, wdd->id);
|
||||||
|
- wdd->dev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@@ -370,9 +349,11 @@ static int __init watchdog_deferred_regi
|
||||||
|
|
||||||
|
static int __init watchdog_init(void)
|
||||||
|
{
|
||||||
|
- watchdog_class = watchdog_dev_init();
|
||||||
|
- if (IS_ERR(watchdog_class))
|
||||||
|
- return PTR_ERR(watchdog_class);
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ err = watchdog_dev_init();
|
||||||
|
+ if (err < 0)
|
||||||
|
+ return err;
|
||||||
|
|
||||||
|
watchdog_deferred_registration();
|
||||||
|
return 0;
|
||||||
|
--- a/drivers/watchdog/watchdog_core.h
|
||||||
|
+++ b/drivers/watchdog/watchdog_core.h
|
||||||
|
@@ -32,6 +32,6 @@
|
||||||
|
* Functions/procedures to be called by the core
|
||||||
|
*/
|
||||||
|
extern int watchdog_dev_register(struct watchdog_device *);
|
||||||
|
-extern int watchdog_dev_unregister(struct watchdog_device *);
|
||||||
|
-extern struct class * __init watchdog_dev_init(void);
|
||||||
|
+extern void watchdog_dev_unregister(struct watchdog_device *);
|
||||||
|
+extern int __init watchdog_dev_init(void);
|
||||||
|
extern void __exit watchdog_dev_exit(void);
|
||||||
|
--- a/drivers/watchdog/watchdog_dev.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_dev.c
|
||||||
|
@@ -628,17 +628,18 @@ static struct miscdevice watchdog_miscde
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
- * watchdog_dev_register: register a watchdog device
|
||||||
|
+ * watchdog_cdev_register: register watchdog character device
|
||||||
|
* @wdd: watchdog device
|
||||||
|
+ * @devno: character device number
|
||||||
|
*
|
||||||
|
- * Register a watchdog device including handling the legacy
|
||||||
|
+ * Register a watchdog character device including handling the legacy
|
||||||
|
* /dev/watchdog node. /dev/watchdog is actually a miscdevice and
|
||||||
|
* thus we set it up like that.
|
||||||
|
*/
|
||||||
|
|
||||||
|
-int watchdog_dev_register(struct watchdog_device *wdd)
|
||||||
|
+static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
|
||||||
|
{
|
||||||
|
- int err, devno;
|
||||||
|
+ int err;
|
||||||
|
|
||||||
|
if (wdd->id == 0) {
|
||||||
|
old_wdd = wdd;
|
||||||
|
@@ -656,7 +657,6 @@ int watchdog_dev_register(struct watchdo
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in the data structures */
|
||||||
|
- devno = MKDEV(MAJOR(watchdog_devt), wdd->id);
|
||||||
|
cdev_init(&wdd->cdev, &watchdog_fops);
|
||||||
|
wdd->cdev.owner = wdd->ops->owner;
|
||||||
|
|
||||||
|
@@ -674,13 +674,14 @@ int watchdog_dev_register(struct watchdo
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
- * watchdog_dev_unregister: unregister a watchdog device
|
||||||
|
+ * watchdog_cdev_unregister: unregister watchdog character device
|
||||||
|
* @watchdog: watchdog device
|
||||||
|
*
|
||||||
|
- * Unregister the watchdog and if needed the legacy /dev/watchdog device.
|
||||||
|
+ * Unregister watchdog character device and if needed the legacy
|
||||||
|
+ * /dev/watchdog device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
-int watchdog_dev_unregister(struct watchdog_device *wdd)
|
||||||
|
+static void watchdog_cdev_unregister(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
mutex_lock(&wdd->lock);
|
||||||
|
set_bit(WDOG_UNREGISTERED, &wdd->status);
|
||||||
|
@@ -691,7 +692,6 @@ int watchdog_dev_unregister(struct watch
|
||||||
|
misc_deregister(&watchdog_miscdev);
|
||||||
|
old_wdd = NULL;
|
||||||
|
}
|
||||||
|
- return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct class watchdog_class = {
|
||||||
|
@@ -701,29 +701,76 @@ static struct class watchdog_class = {
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
+ * watchdog_dev_register: register a watchdog device
|
||||||
|
+ * @wdd: watchdog device
|
||||||
|
+ *
|
||||||
|
+ * Register a watchdog device including handling the legacy
|
||||||
|
+ * /dev/watchdog node. /dev/watchdog is actually a miscdevice and
|
||||||
|
+ * thus we set it up like that.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+int watchdog_dev_register(struct watchdog_device *wdd)
|
||||||
|
+{
|
||||||
|
+ struct device *dev;
|
||||||
|
+ dev_t devno;
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ devno = MKDEV(MAJOR(watchdog_devt), wdd->id);
|
||||||
|
+
|
||||||
|
+ ret = watchdog_cdev_register(wdd, devno);
|
||||||
|
+ if (ret)
|
||||||
|
+ return ret;
|
||||||
|
+
|
||||||
|
+ dev = device_create(&watchdog_class, wdd->parent, devno, wdd,
|
||||||
|
+ "watchdog%d", wdd->id);
|
||||||
|
+ if (IS_ERR(dev)) {
|
||||||
|
+ watchdog_cdev_unregister(wdd);
|
||||||
|
+ return PTR_ERR(dev);
|
||||||
|
+ }
|
||||||
|
+ wdd->dev = dev;
|
||||||
|
+
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * watchdog_dev_unregister: unregister a watchdog device
|
||||||
|
+ * @watchdog: watchdog device
|
||||||
|
+ *
|
||||||
|
+ * Unregister watchdog device and if needed the legacy
|
||||||
|
+ * /dev/watchdog device.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+void watchdog_dev_unregister(struct watchdog_device *wdd)
|
||||||
|
+{
|
||||||
|
+ watchdog_cdev_unregister(wdd);
|
||||||
|
+ device_destroy(&watchdog_class, wdd->dev->devt);
|
||||||
|
+ wdd->dev = NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
* watchdog_dev_init: init dev part of watchdog core
|
||||||
|
*
|
||||||
|
* Allocate a range of chardev nodes to use for watchdog devices
|
||||||
|
*/
|
||||||
|
|
||||||
|
-struct class * __init watchdog_dev_init(void)
|
||||||
|
+int __init watchdog_dev_init(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = class_register(&watchdog_class);
|
||||||
|
if (err < 0) {
|
||||||
|
pr_err("couldn't register class\n");
|
||||||
|
- return ERR_PTR(err);
|
||||||
|
+ return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog");
|
||||||
|
if (err < 0) {
|
||||||
|
pr_err("watchdog: unable to allocate char dev region\n");
|
||||||
|
class_unregister(&watchdog_class);
|
||||||
|
- return ERR_PTR(err);
|
||||||
|
+ return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
- return &watchdog_class;
|
||||||
|
+ return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
|
@ -0,0 +1,969 @@
|
||||||
|
From b4ffb1909843b28f3b1b60197d517b123b7a9b66 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Date: Fri, 25 Dec 2015 16:01:42 -0800
|
||||||
|
Subject: watchdog: Separate and maintain variables based on variable lifetime
|
||||||
|
|
||||||
|
All variables required by the watchdog core to manage a watchdog are
|
||||||
|
currently stored in struct watchdog_device. The lifetime of those
|
||||||
|
variables is determined by the watchdog driver. However, the lifetime
|
||||||
|
of variables used by the watchdog core differs from the lifetime of
|
||||||
|
struct watchdog_device. To remedy this situation, watchdog drivers
|
||||||
|
can implement ref and unref callbacks, to be used by the watchdog
|
||||||
|
core to lock struct watchdog_device in memory.
|
||||||
|
|
||||||
|
While this solves the immediate problem, it depends on watchdog drivers
|
||||||
|
to actually implement the ref/unref callbacks. This is error prone,
|
||||||
|
often not implemented in the first place, or not implemented correctly.
|
||||||
|
|
||||||
|
To solve the problem without requiring driver support, split the variables
|
||||||
|
in struct watchdog_device into two data structures - one for variables
|
||||||
|
associated with the watchdog driver, one for variables associated with
|
||||||
|
the watchdog core. With this approach, the watchdog core can keep track
|
||||||
|
of its variable lifetime and no longer depends on ref/unref callbacks
|
||||||
|
in the driver. As a side effect, some of the variables originally in
|
||||||
|
struct watchdog_driver are now private to the watchdog core and no longer
|
||||||
|
visible in watchdog drivers.
|
||||||
|
|
||||||
|
As a side effect of the changes made, an ioctl will now always fail
|
||||||
|
with -ENODEV after a watchdog device was unregistered with the character
|
||||||
|
device still open. Previously, it would only fail with -ENODEV in some
|
||||||
|
situations. Also, ioctl operations are now atomic from driver perspective.
|
||||||
|
With this change, it is now guaranteed that the driver will not unregister
|
||||||
|
a watchdog between a timeout change and the subsequent ping.
|
||||||
|
|
||||||
|
The 'ref' and 'unref' callbacks in struct watchdog_driver are no longer
|
||||||
|
used and marked as deprecated.
|
||||||
|
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
Documentation/watchdog/watchdog-kernel-api.txt | 45 +--
|
||||||
|
drivers/watchdog/watchdog_core.c | 2 -
|
||||||
|
drivers/watchdog/watchdog_dev.c | 383 +++++++++++++------------
|
||||||
|
include/linux/watchdog.h | 22 +-
|
||||||
|
4 files changed, 218 insertions(+), 234 deletions(-)
|
||||||
|
|
||||||
|
--- a/Documentation/watchdog/watchdog-kernel-api.txt
|
||||||
|
+++ b/Documentation/watchdog/watchdog-kernel-api.txt
|
||||||
|
@@ -44,7 +44,6 @@ The watchdog device structure looks like
|
||||||
|
|
||||||
|
struct watchdog_device {
|
||||||
|
int id;
|
||||||
|
- struct cdev cdev;
|
||||||
|
struct device *dev;
|
||||||
|
struct device *parent;
|
||||||
|
const struct watchdog_info *info;
|
||||||
|
@@ -56,7 +55,7 @@ struct watchdog_device {
|
||||||
|
struct notifier_block reboot_nb;
|
||||||
|
struct notifier_block restart_nb;
|
||||||
|
void *driver_data;
|
||||||
|
- struct mutex lock;
|
||||||
|
+ struct watchdog_core_data *wd_data;
|
||||||
|
unsigned long status;
|
||||||
|
struct list_head deferred;
|
||||||
|
};
|
||||||
|
@@ -66,8 +65,6 @@ It contains following fields:
|
||||||
|
/dev/watchdog0 cdev (dynamic major, minor 0) as well as the old
|
||||||
|
/dev/watchdog miscdev. The id is set automatically when calling
|
||||||
|
watchdog_register_device.
|
||||||
|
-* cdev: cdev for the dynamic /dev/watchdog<id> device nodes. This
|
||||||
|
- field is also populated by watchdog_register_device.
|
||||||
|
* dev: device under the watchdog class (created by watchdog_register_device).
|
||||||
|
* parent: set this to the parent device (or NULL) before calling
|
||||||
|
watchdog_register_device.
|
||||||
|
@@ -89,11 +86,10 @@ It contains following fields:
|
||||||
|
* driver_data: a pointer to the drivers private data of a watchdog device.
|
||||||
|
This data should only be accessed via the watchdog_set_drvdata and
|
||||||
|
watchdog_get_drvdata routines.
|
||||||
|
-* lock: Mutex for WatchDog Timer Driver Core internal use only.
|
||||||
|
+* wd_data: a pointer to watchdog core internal data.
|
||||||
|
* status: this field contains a number of status bits that give extra
|
||||||
|
information about the status of the device (Like: is the watchdog timer
|
||||||
|
- running/active, is the nowayout bit set, is the device opened via
|
||||||
|
- the /dev/watchdog interface or not, ...).
|
||||||
|
+ running/active, or is the nowayout bit set).
|
||||||
|
* deferred: entry in wtd_deferred_reg_list which is used to
|
||||||
|
register early initialized watchdogs.
|
||||||
|
|
||||||
|
@@ -110,8 +106,8 @@ struct watchdog_ops {
|
||||||
|
int (*set_timeout)(struct watchdog_device *, unsigned int);
|
||||||
|
unsigned int (*get_timeleft)(struct watchdog_device *);
|
||||||
|
int (*restart)(struct watchdog_device *);
|
||||||
|
- void (*ref)(struct watchdog_device *);
|
||||||
|
- void (*unref)(struct watchdog_device *);
|
||||||
|
+ void (*ref)(struct watchdog_device *) __deprecated;
|
||||||
|
+ void (*unref)(struct watchdog_device *) __deprecated;
|
||||||
|
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -120,20 +116,6 @@ driver's operations. This module owner w
|
||||||
|
the watchdog is active. (This to avoid a system crash when you unload the
|
||||||
|
module and /dev/watchdog is still open).
|
||||||
|
|
||||||
|
-If the watchdog_device struct is dynamically allocated, just locking the module
|
||||||
|
-is not enough and a driver also needs to define the ref and unref operations to
|
||||||
|
-ensure the structure holding the watchdog_device does not go away.
|
||||||
|
-
|
||||||
|
-The simplest (and usually sufficient) implementation of this is to:
|
||||||
|
-1) Add a kref struct to the same structure which is holding the watchdog_device
|
||||||
|
-2) Define a release callback for the kref which frees the struct holding both
|
||||||
|
-3) Call kref_init on this kref *before* calling watchdog_register_device()
|
||||||
|
-4) Define a ref operation calling kref_get on this kref
|
||||||
|
-5) Define a unref operation calling kref_put on this kref
|
||||||
|
-6) When it is time to cleanup:
|
||||||
|
- * Do not kfree() the struct holding both, the last kref_put will do this!
|
||||||
|
- * *After* calling watchdog_unregister_device() call kref_put on the kref
|
||||||
|
-
|
||||||
|
Some operations are mandatory and some are optional. The mandatory operations
|
||||||
|
are:
|
||||||
|
* start: this is a pointer to the routine that starts the watchdog timer
|
||||||
|
@@ -176,34 +158,21 @@ they are supported. These optional routi
|
||||||
|
* get_timeleft: this routines returns the time that's left before a reset.
|
||||||
|
* restart: this routine restarts the machine. It returns 0 on success or a
|
||||||
|
negative errno code for failure.
|
||||||
|
-* ref: the operation that calls kref_get on the kref of a dynamically
|
||||||
|
- allocated watchdog_device struct.
|
||||||
|
-* unref: the operation that calls kref_put on the kref of a dynamically
|
||||||
|
- allocated watchdog_device struct.
|
||||||
|
* ioctl: if this routine is present then it will be called first before we do
|
||||||
|
our own internal ioctl call handling. This routine should return -ENOIOCTLCMD
|
||||||
|
if a command is not supported. The parameters that are passed to the ioctl
|
||||||
|
call are: watchdog_device, cmd and arg.
|
||||||
|
|
||||||
|
+The 'ref' and 'unref' operations are no longer used and deprecated.
|
||||||
|
+
|
||||||
|
The status bits should (preferably) be set with the set_bit and clear_bit alike
|
||||||
|
bit-operations. The status bits that are defined are:
|
||||||
|
* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device
|
||||||
|
is active or not. When the watchdog is active after booting, then you should
|
||||||
|
set this status bit (Note: when you register the watchdog timer device with
|
||||||
|
this bit set, then opening /dev/watchdog will skip the start operation)
|
||||||
|
-* WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device
|
||||||
|
- was opened via /dev/watchdog.
|
||||||
|
- (This bit should only be used by the WatchDog Timer Driver Core).
|
||||||
|
-* WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character
|
||||||
|
- has been sent (so that we can support the magic close feature).
|
||||||
|
- (This bit should only be used by the WatchDog Timer Driver Core).
|
||||||
|
* WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog.
|
||||||
|
If this bit is set then the watchdog timer will not be able to stop.
|
||||||
|
-* WDOG_UNREGISTERED: this bit gets set by the WatchDog Timer Driver Core
|
||||||
|
- after calling watchdog_unregister_device, and then checked before calling
|
||||||
|
- any watchdog_ops, so that you can be sure that no operations (other then
|
||||||
|
- unref) will get called after unregister, even if userspace still holds a
|
||||||
|
- reference to /dev/watchdog
|
||||||
|
|
||||||
|
To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog
|
||||||
|
timer device) you can either:
|
||||||
|
--- a/drivers/watchdog/watchdog_core.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_core.c
|
||||||
|
@@ -210,8 +210,6 @@ static int __watchdog_register_device(st
|
||||||
|
* corrupted in a later stage then we expect a kernel panic!
|
||||||
|
*/
|
||||||
|
|
||||||
|
- mutex_init(&wdd->lock);
|
||||||
|
-
|
||||||
|
/* Use alias for watchdog id if possible */
|
||||||
|
if (wdd->parent) {
|
||||||
|
ret = of_alias_get_id(wdd->parent->of_node, "watchdog");
|
||||||
|
--- a/drivers/watchdog/watchdog_dev.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_dev.c
|
||||||
|
@@ -32,27 +32,51 @@
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
-#include <linux/module.h> /* For module stuff/... */
|
||||||
|
-#include <linux/types.h> /* For standard types (like size_t) */
|
||||||
|
+#include <linux/cdev.h> /* For character device */
|
||||||
|
#include <linux/errno.h> /* For the -ENODEV/... values */
|
||||||
|
-#include <linux/kernel.h> /* For printk/panic/... */
|
||||||
|
#include <linux/fs.h> /* For file operations */
|
||||||
|
-#include <linux/watchdog.h> /* For watchdog specific items */
|
||||||
|
-#include <linux/miscdevice.h> /* For handling misc devices */
|
||||||
|
#include <linux/init.h> /* For __init/__exit/... */
|
||||||
|
+#include <linux/kernel.h> /* For printk/panic/... */
|
||||||
|
+#include <linux/kref.h> /* For data references */
|
||||||
|
+#include <linux/miscdevice.h> /* For handling misc devices */
|
||||||
|
+#include <linux/module.h> /* For module stuff/... */
|
||||||
|
+#include <linux/mutex.h> /* For mutexes */
|
||||||
|
+#include <linux/slab.h> /* For memory functions */
|
||||||
|
+#include <linux/types.h> /* For standard types (like size_t) */
|
||||||
|
+#include <linux/watchdog.h> /* For watchdog specific items */
|
||||||
|
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
|
||||||
|
|
||||||
|
#include "watchdog_core.h"
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * struct watchdog_core_data - watchdog core internal data
|
||||||
|
+ * @kref: Reference count.
|
||||||
|
+ * @cdev: The watchdog's Character device.
|
||||||
|
+ * @wdd: Pointer to watchdog device.
|
||||||
|
+ * @lock: Lock for watchdog core.
|
||||||
|
+ * @status: Watchdog core internal status bits.
|
||||||
|
+ */
|
||||||
|
+struct watchdog_core_data {
|
||||||
|
+ struct kref kref;
|
||||||
|
+ struct cdev cdev;
|
||||||
|
+ struct watchdog_device *wdd;
|
||||||
|
+ struct mutex lock;
|
||||||
|
+ unsigned long status; /* Internal status bits */
|
||||||
|
+#define _WDOG_DEV_OPEN 0 /* Opened ? */
|
||||||
|
+#define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
/* the dev_t structure to store the dynamically allocated watchdog devices */
|
||||||
|
static dev_t watchdog_devt;
|
||||||
|
-/* the watchdog device behind /dev/watchdog */
|
||||||
|
-static struct watchdog_device *old_wdd;
|
||||||
|
+/* Reference to watchdog device behind /dev/watchdog */
|
||||||
|
+static struct watchdog_core_data *old_wd_data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* watchdog_ping: ping the watchdog.
|
||||||
|
* @wdd: the watchdog device to ping
|
||||||
|
*
|
||||||
|
+ * The caller must hold wd_data->lock.
|
||||||
|
+ *
|
||||||
|
* If the watchdog has no own ping operation then it needs to be
|
||||||
|
* restarted via the start operation. This wrapper function does
|
||||||
|
* exactly that.
|
||||||
|
@@ -61,25 +85,16 @@ static struct watchdog_device *old_wdd;
|
||||||
|
|
||||||
|
static int watchdog_ping(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
- int err = 0;
|
||||||
|
-
|
||||||
|
- mutex_lock(&wdd->lock);
|
||||||
|
-
|
||||||
|
- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) {
|
||||||
|
- err = -ENODEV;
|
||||||
|
- goto out_ping;
|
||||||
|
- }
|
||||||
|
+ int err;
|
||||||
|
|
||||||
|
if (!watchdog_active(wdd))
|
||||||
|
- goto out_ping;
|
||||||
|
+ return 0;
|
||||||
|
|
||||||
|
if (wdd->ops->ping)
|
||||||
|
err = wdd->ops->ping(wdd); /* ping the watchdog */
|
||||||
|
else
|
||||||
|
err = wdd->ops->start(wdd); /* restart watchdog */
|
||||||
|
|
||||||
|
-out_ping:
|
||||||
|
- mutex_unlock(&wdd->lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -87,6 +102,8 @@ out_ping:
|
||||||
|
* watchdog_start: wrapper to start the watchdog.
|
||||||
|
* @wdd: the watchdog device to start
|
||||||
|
*
|
||||||
|
+ * The caller must hold wd_data->lock.
|
||||||
|
+ *
|
||||||
|
* Start the watchdog if it is not active and mark it active.
|
||||||
|
* This function returns zero on success or a negative errno code for
|
||||||
|
* failure.
|
||||||
|
@@ -94,24 +111,15 @@ out_ping:
|
||||||
|
|
||||||
|
static int watchdog_start(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
- int err = 0;
|
||||||
|
-
|
||||||
|
- mutex_lock(&wdd->lock);
|
||||||
|
-
|
||||||
|
- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) {
|
||||||
|
- err = -ENODEV;
|
||||||
|
- goto out_start;
|
||||||
|
- }
|
||||||
|
+ int err;
|
||||||
|
|
||||||
|
if (watchdog_active(wdd))
|
||||||
|
- goto out_start;
|
||||||
|
+ return 0;
|
||||||
|
|
||||||
|
err = wdd->ops->start(wdd);
|
||||||
|
if (err == 0)
|
||||||
|
set_bit(WDOG_ACTIVE, &wdd->status);
|
||||||
|
|
||||||
|
-out_start:
|
||||||
|
- mutex_unlock(&wdd->lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -119,6 +127,8 @@ out_start:
|
||||||
|
* watchdog_stop: wrapper to stop the watchdog.
|
||||||
|
* @wdd: the watchdog device to stop
|
||||||
|
*
|
||||||
|
+ * The caller must hold wd_data->lock.
|
||||||
|
+ *
|
||||||
|
* Stop the watchdog if it is still active and unmark it active.
|
||||||
|
* This function returns zero on success or a negative errno code for
|
||||||
|
* failure.
|
||||||
|
@@ -127,93 +137,58 @@ out_start:
|
||||||
|
|
||||||
|
static int watchdog_stop(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
- int err = 0;
|
||||||
|
-
|
||||||
|
- mutex_lock(&wdd->lock);
|
||||||
|
-
|
||||||
|
- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) {
|
||||||
|
- err = -ENODEV;
|
||||||
|
- goto out_stop;
|
||||||
|
- }
|
||||||
|
+ int err;
|
||||||
|
|
||||||
|
if (!watchdog_active(wdd))
|
||||||
|
- goto out_stop;
|
||||||
|
+ return 0;
|
||||||
|
|
||||||
|
if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) {
|
||||||
|
dev_info(wdd->dev, "nowayout prevents watchdog being stopped!\n");
|
||||||
|
- err = -EBUSY;
|
||||||
|
- goto out_stop;
|
||||||
|
+ return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = wdd->ops->stop(wdd);
|
||||||
|
if (err == 0)
|
||||||
|
clear_bit(WDOG_ACTIVE, &wdd->status);
|
||||||
|
|
||||||
|
-out_stop:
|
||||||
|
- mutex_unlock(&wdd->lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* watchdog_get_status: wrapper to get the watchdog status
|
||||||
|
* @wdd: the watchdog device to get the status from
|
||||||
|
- * @status: the status of the watchdog device
|
||||||
|
+ *
|
||||||
|
+ * The caller must hold wd_data->lock.
|
||||||
|
*
|
||||||
|
* Get the watchdog's status flags.
|
||||||
|
*/
|
||||||
|
|
||||||
|
-static int watchdog_get_status(struct watchdog_device *wdd,
|
||||||
|
- unsigned int *status)
|
||||||
|
+static unsigned int watchdog_get_status(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
- int err = 0;
|
||||||
|
-
|
||||||
|
- *status = 0;
|
||||||
|
if (!wdd->ops->status)
|
||||||
|
- return -EOPNOTSUPP;
|
||||||
|
-
|
||||||
|
- mutex_lock(&wdd->lock);
|
||||||
|
-
|
||||||
|
- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) {
|
||||||
|
- err = -ENODEV;
|
||||||
|
- goto out_status;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- *status = wdd->ops->status(wdd);
|
||||||
|
+ return 0;
|
||||||
|
|
||||||
|
-out_status:
|
||||||
|
- mutex_unlock(&wdd->lock);
|
||||||
|
- return err;
|
||||||
|
+ return wdd->ops->status(wdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* watchdog_set_timeout: set the watchdog timer timeout
|
||||||
|
* @wdd: the watchdog device to set the timeout for
|
||||||
|
* @timeout: timeout to set in seconds
|
||||||
|
+ *
|
||||||
|
+ * The caller must hold wd_data->lock.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int watchdog_set_timeout(struct watchdog_device *wdd,
|
||||||
|
unsigned int timeout)
|
||||||
|
{
|
||||||
|
- int err;
|
||||||
|
-
|
||||||
|
if (!wdd->ops->set_timeout || !(wdd->info->options & WDIOF_SETTIMEOUT))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (watchdog_timeout_invalid(wdd, timeout))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
- mutex_lock(&wdd->lock);
|
||||||
|
-
|
||||||
|
- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) {
|
||||||
|
- err = -ENODEV;
|
||||||
|
- goto out_timeout;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- err = wdd->ops->set_timeout(wdd, timeout);
|
||||||
|
-
|
||||||
|
-out_timeout:
|
||||||
|
- mutex_unlock(&wdd->lock);
|
||||||
|
- return err;
|
||||||
|
+ return wdd->ops->set_timeout(wdd, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -221,30 +196,22 @@ out_timeout:
|
||||||
|
* @wdd: the watchdog device to get the remaining time from
|
||||||
|
* @timeleft: the time that's left
|
||||||
|
*
|
||||||
|
+ * The caller must hold wd_data->lock.
|
||||||
|
+ *
|
||||||
|
* Get the time before a watchdog will reboot (if not pinged).
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int watchdog_get_timeleft(struct watchdog_device *wdd,
|
||||||
|
unsigned int *timeleft)
|
||||||
|
{
|
||||||
|
- int err = 0;
|
||||||
|
-
|
||||||
|
*timeleft = 0;
|
||||||
|
+
|
||||||
|
if (!wdd->ops->get_timeleft)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
- mutex_lock(&wdd->lock);
|
||||||
|
-
|
||||||
|
- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) {
|
||||||
|
- err = -ENODEV;
|
||||||
|
- goto out_timeleft;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
*timeleft = wdd->ops->get_timeleft(wdd);
|
||||||
|
|
||||||
|
-out_timeleft:
|
||||||
|
- mutex_unlock(&wdd->lock);
|
||||||
|
- return err;
|
||||||
|
+ return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_WATCHDOG_SYSFS
|
||||||
|
@@ -261,14 +228,14 @@ static ssize_t status_show(struct device
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
- ssize_t status;
|
||||||
|
- unsigned int val;
|
||||||
|
+ struct watchdog_core_data *wd_data = wdd->wd_data;
|
||||||
|
+ unsigned int status;
|
||||||
|
|
||||||
|
- status = watchdog_get_status(wdd, &val);
|
||||||
|
- if (!status)
|
||||||
|
- status = sprintf(buf, "%u\n", val);
|
||||||
|
+ mutex_lock(&wd_data->lock);
|
||||||
|
+ status = watchdog_get_status(wdd);
|
||||||
|
+ mutex_unlock(&wd_data->lock);
|
||||||
|
|
||||||
|
- return status;
|
||||||
|
+ return sprintf(buf, "%u\n", status);
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR_RO(status);
|
||||||
|
|
||||||
|
@@ -285,10 +252,13 @@ static ssize_t timeleft_show(struct devi
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||||
|
+ struct watchdog_core_data *wd_data = wdd->wd_data;
|
||||||
|
ssize_t status;
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
+ mutex_lock(&wd_data->lock);
|
||||||
|
status = watchdog_get_timeleft(wdd, &val);
|
||||||
|
+ mutex_unlock(&wd_data->lock);
|
||||||
|
if (!status)
|
||||||
|
status = sprintf(buf, "%u\n", val);
|
||||||
|
|
||||||
|
@@ -365,28 +335,17 @@ __ATTRIBUTE_GROUPS(wdt);
|
||||||
|
* @wdd: the watchdog device to do the ioctl on
|
||||||
|
* @cmd: watchdog command
|
||||||
|
* @arg: argument pointer
|
||||||
|
+ *
|
||||||
|
+ * The caller must hold wd_data->lock.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd,
|
||||||
|
unsigned long arg)
|
||||||
|
{
|
||||||
|
- int err;
|
||||||
|
-
|
||||||
|
if (!wdd->ops->ioctl)
|
||||||
|
return -ENOIOCTLCMD;
|
||||||
|
|
||||||
|
- mutex_lock(&wdd->lock);
|
||||||
|
-
|
||||||
|
- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) {
|
||||||
|
- err = -ENODEV;
|
||||||
|
- goto out_ioctl;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- err = wdd->ops->ioctl(wdd, cmd, arg);
|
||||||
|
-
|
||||||
|
-out_ioctl:
|
||||||
|
- mutex_unlock(&wdd->lock);
|
||||||
|
- return err;
|
||||||
|
+ return wdd->ops->ioctl(wdd, cmd, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -404,10 +363,11 @@ out_ioctl:
|
||||||
|
static ssize_t watchdog_write(struct file *file, const char __user *data,
|
||||||
|
size_t len, loff_t *ppos)
|
||||||
|
{
|
||||||
|
- struct watchdog_device *wdd = file->private_data;
|
||||||
|
+ struct watchdog_core_data *wd_data = file->private_data;
|
||||||
|
+ struct watchdog_device *wdd;
|
||||||
|
+ int err;
|
||||||
|
size_t i;
|
||||||
|
char c;
|
||||||
|
- int err;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return 0;
|
||||||
|
@@ -416,18 +376,25 @@ static ssize_t watchdog_write(struct fil
|
||||||
|
* Note: just in case someone wrote the magic character
|
||||||
|
* five months ago...
|
||||||
|
*/
|
||||||
|
- clear_bit(WDOG_ALLOW_RELEASE, &wdd->status);
|
||||||
|
+ clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status);
|
||||||
|
|
||||||
|
/* scan to see whether or not we got the magic character */
|
||||||
|
for (i = 0; i != len; i++) {
|
||||||
|
if (get_user(c, data + i))
|
||||||
|
return -EFAULT;
|
||||||
|
if (c == 'V')
|
||||||
|
- set_bit(WDOG_ALLOW_RELEASE, &wdd->status);
|
||||||
|
+ set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* someone wrote to us, so we send the watchdog a keepalive ping */
|
||||||
|
- err = watchdog_ping(wdd);
|
||||||
|
+
|
||||||
|
+ err = -ENODEV;
|
||||||
|
+ mutex_lock(&wd_data->lock);
|
||||||
|
+ wdd = wd_data->wdd;
|
||||||
|
+ if (wdd)
|
||||||
|
+ err = watchdog_ping(wdd);
|
||||||
|
+ mutex_unlock(&wd_data->lock);
|
||||||
|
+
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
@@ -447,71 +414,94 @@ static ssize_t watchdog_write(struct fil
|
||||||
|
static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
||||||
|
unsigned long arg)
|
||||||
|
{
|
||||||
|
- struct watchdog_device *wdd = file->private_data;
|
||||||
|
+ struct watchdog_core_data *wd_data = file->private_data;
|
||||||
|
void __user *argp = (void __user *)arg;
|
||||||
|
+ struct watchdog_device *wdd;
|
||||||
|
int __user *p = argp;
|
||||||
|
unsigned int val;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
+ mutex_lock(&wd_data->lock);
|
||||||
|
+
|
||||||
|
+ wdd = wd_data->wdd;
|
||||||
|
+ if (!wdd) {
|
||||||
|
+ err = -ENODEV;
|
||||||
|
+ goto out_ioctl;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
err = watchdog_ioctl_op(wdd, cmd, arg);
|
||||||
|
if (err != -ENOIOCTLCMD)
|
||||||
|
- return err;
|
||||||
|
+ goto out_ioctl;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case WDIOC_GETSUPPORT:
|
||||||
|
- return copy_to_user(argp, wdd->info,
|
||||||
|
+ err = copy_to_user(argp, wdd->info,
|
||||||
|
sizeof(struct watchdog_info)) ? -EFAULT : 0;
|
||||||
|
+ break;
|
||||||
|
case WDIOC_GETSTATUS:
|
||||||
|
- err = watchdog_get_status(wdd, &val);
|
||||||
|
- if (err == -ENODEV)
|
||||||
|
- return err;
|
||||||
|
- return put_user(val, p);
|
||||||
|
+ val = watchdog_get_status(wdd);
|
||||||
|
+ err = put_user(val, p);
|
||||||
|
+ break;
|
||||||
|
case WDIOC_GETBOOTSTATUS:
|
||||||
|
- return put_user(wdd->bootstatus, p);
|
||||||
|
+ err = put_user(wdd->bootstatus, p);
|
||||||
|
+ break;
|
||||||
|
case WDIOC_SETOPTIONS:
|
||||||
|
- if (get_user(val, p))
|
||||||
|
- return -EFAULT;
|
||||||
|
+ if (get_user(val, p)) {
|
||||||
|
+ err = -EFAULT;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
if (val & WDIOS_DISABLECARD) {
|
||||||
|
err = watchdog_stop(wdd);
|
||||||
|
if (err < 0)
|
||||||
|
- return err;
|
||||||
|
+ break;
|
||||||
|
}
|
||||||
|
- if (val & WDIOS_ENABLECARD) {
|
||||||
|
+ if (val & WDIOS_ENABLECARD)
|
||||||
|
err = watchdog_start(wdd);
|
||||||
|
- if (err < 0)
|
||||||
|
- return err;
|
||||||
|
- }
|
||||||
|
- return 0;
|
||||||
|
+ break;
|
||||||
|
case WDIOC_KEEPALIVE:
|
||||||
|
- if (!(wdd->info->options & WDIOF_KEEPALIVEPING))
|
||||||
|
- return -EOPNOTSUPP;
|
||||||
|
- return watchdog_ping(wdd);
|
||||||
|
+ if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) {
|
||||||
|
+ err = -EOPNOTSUPP;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ err = watchdog_ping(wdd);
|
||||||
|
+ break;
|
||||||
|
case WDIOC_SETTIMEOUT:
|
||||||
|
- if (get_user(val, p))
|
||||||
|
- return -EFAULT;
|
||||||
|
+ if (get_user(val, p)) {
|
||||||
|
+ err = -EFAULT;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
err = watchdog_set_timeout(wdd, val);
|
||||||
|
if (err < 0)
|
||||||
|
- return err;
|
||||||
|
+ break;
|
||||||
|
/* If the watchdog is active then we send a keepalive ping
|
||||||
|
* to make sure that the watchdog keep's running (and if
|
||||||
|
* possible that it takes the new timeout) */
|
||||||
|
err = watchdog_ping(wdd);
|
||||||
|
if (err < 0)
|
||||||
|
- return err;
|
||||||
|
+ break;
|
||||||
|
/* Fall */
|
||||||
|
case WDIOC_GETTIMEOUT:
|
||||||
|
/* timeout == 0 means that we don't know the timeout */
|
||||||
|
- if (wdd->timeout == 0)
|
||||||
|
- return -EOPNOTSUPP;
|
||||||
|
- return put_user(wdd->timeout, p);
|
||||||
|
+ if (wdd->timeout == 0) {
|
||||||
|
+ err = -EOPNOTSUPP;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ err = put_user(wdd->timeout, p);
|
||||||
|
+ break;
|
||||||
|
case WDIOC_GETTIMELEFT:
|
||||||
|
err = watchdog_get_timeleft(wdd, &val);
|
||||||
|
- if (err)
|
||||||
|
- return err;
|
||||||
|
- return put_user(val, p);
|
||||||
|
+ if (err < 0)
|
||||||
|
+ break;
|
||||||
|
+ err = put_user(val, p);
|
||||||
|
+ break;
|
||||||
|
default:
|
||||||
|
- return -ENOTTY;
|
||||||
|
+ err = -ENOTTY;
|
||||||
|
+ break;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+out_ioctl:
|
||||||
|
+ mutex_unlock(&wd_data->lock);
|
||||||
|
+ return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -526,45 +516,59 @@ static long watchdog_ioctl(struct file *
|
||||||
|
|
||||||
|
static int watchdog_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
- int err = -EBUSY;
|
||||||
|
+ struct watchdog_core_data *wd_data;
|
||||||
|
struct watchdog_device *wdd;
|
||||||
|
+ int err;
|
||||||
|
|
||||||
|
/* Get the corresponding watchdog device */
|
||||||
|
if (imajor(inode) == MISC_MAJOR)
|
||||||
|
- wdd = old_wdd;
|
||||||
|
+ wd_data = old_wd_data;
|
||||||
|
else
|
||||||
|
- wdd = container_of(inode->i_cdev, struct watchdog_device, cdev);
|
||||||
|
+ wd_data = container_of(inode->i_cdev, struct watchdog_core_data,
|
||||||
|
+ cdev);
|
||||||
|
|
||||||
|
/* the watchdog is single open! */
|
||||||
|
- if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status))
|
||||||
|
+ if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
+ wdd = wd_data->wdd;
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* If the /dev/watchdog device is open, we don't want the module
|
||||||
|
* to be unloaded.
|
||||||
|
*/
|
||||||
|
- if (!try_module_get(wdd->ops->owner))
|
||||||
|
- goto out;
|
||||||
|
+ if (!try_module_get(wdd->ops->owner)) {
|
||||||
|
+ err = -EBUSY;
|
||||||
|
+ goto out_clear;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
err = watchdog_start(wdd);
|
||||||
|
if (err < 0)
|
||||||
|
goto out_mod;
|
||||||
|
|
||||||
|
- file->private_data = wdd;
|
||||||
|
+ file->private_data = wd_data;
|
||||||
|
|
||||||
|
- if (wdd->ops->ref)
|
||||||
|
- wdd->ops->ref(wdd);
|
||||||
|
+ kref_get(&wd_data->kref);
|
||||||
|
|
||||||
|
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
|
||||||
|
return nonseekable_open(inode, file);
|
||||||
|
|
||||||
|
out_mod:
|
||||||
|
- module_put(wdd->ops->owner);
|
||||||
|
-out:
|
||||||
|
- clear_bit(WDOG_DEV_OPEN, &wdd->status);
|
||||||
|
+ module_put(wd_data->wdd->ops->owner);
|
||||||
|
+out_clear:
|
||||||
|
+ clear_bit(_WDOG_DEV_OPEN, &wd_data->status);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static void watchdog_core_data_release(struct kref *kref)
|
||||||
|
+{
|
||||||
|
+ struct watchdog_core_data *wd_data;
|
||||||
|
+
|
||||||
|
+ wd_data = container_of(kref, struct watchdog_core_data, kref);
|
||||||
|
+
|
||||||
|
+ kfree(wd_data);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* watchdog_release: release the watchdog device.
|
||||||
|
* @inode: inode of device
|
||||||
|
@@ -577,9 +581,16 @@ out:
|
||||||
|
|
||||||
|
static int watchdog_release(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
- struct watchdog_device *wdd = file->private_data;
|
||||||
|
+ struct watchdog_core_data *wd_data = file->private_data;
|
||||||
|
+ struct watchdog_device *wdd;
|
||||||
|
int err = -EBUSY;
|
||||||
|
|
||||||
|
+ mutex_lock(&wd_data->lock);
|
||||||
|
+
|
||||||
|
+ wdd = wd_data->wdd;
|
||||||
|
+ if (!wdd)
|
||||||
|
+ goto done;
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* We only stop the watchdog if we received the magic character
|
||||||
|
* or if WDIOF_MAGICCLOSE is not set. If nowayout was set then
|
||||||
|
@@ -587,29 +598,24 @@ static int watchdog_release(struct inode
|
||||||
|
*/
|
||||||
|
if (!test_bit(WDOG_ACTIVE, &wdd->status))
|
||||||
|
err = 0;
|
||||||
|
- else if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) ||
|
||||||
|
+ else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) ||
|
||||||
|
!(wdd->info->options & WDIOF_MAGICCLOSE))
|
||||||
|
err = watchdog_stop(wdd);
|
||||||
|
|
||||||
|
/* If the watchdog was not stopped, send a keepalive ping */
|
||||||
|
if (err < 0) {
|
||||||
|
- mutex_lock(&wdd->lock);
|
||||||
|
- if (!test_bit(WDOG_UNREGISTERED, &wdd->status))
|
||||||
|
- dev_crit(wdd->dev, "watchdog did not stop!\n");
|
||||||
|
- mutex_unlock(&wdd->lock);
|
||||||
|
+ dev_crit(wdd->dev, "watchdog did not stop!\n");
|
||||||
|
watchdog_ping(wdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* Allow the owner module to be unloaded again */
|
||||||
|
- module_put(wdd->ops->owner);
|
||||||
|
-
|
||||||
|
/* make sure that /dev/watchdog can be re-opened */
|
||||||
|
- clear_bit(WDOG_DEV_OPEN, &wdd->status);
|
||||||
|
-
|
||||||
|
- /* Note wdd may be gone after this, do not use after this! */
|
||||||
|
- if (wdd->ops->unref)
|
||||||
|
- wdd->ops->unref(wdd);
|
||||||
|
+ clear_bit(_WDOG_DEV_OPEN, &wd_data->status);
|
||||||
|
|
||||||
|
+done:
|
||||||
|
+ mutex_unlock(&wd_data->lock);
|
||||||
|
+ /* Allow the owner module to be unloaded again */
|
||||||
|
+ module_put(wd_data->cdev.owner);
|
||||||
|
+ kref_put(&wd_data->kref, watchdog_core_data_release);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -639,10 +645,20 @@ static struct miscdevice watchdog_miscde
|
||||||
|
|
||||||
|
static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
|
||||||
|
{
|
||||||
|
+ struct watchdog_core_data *wd_data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
+ wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL);
|
||||||
|
+ if (!wd_data)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ kref_init(&wd_data->kref);
|
||||||
|
+ mutex_init(&wd_data->lock);
|
||||||
|
+
|
||||||
|
+ wd_data->wdd = wdd;
|
||||||
|
+ wdd->wd_data = wd_data;
|
||||||
|
+
|
||||||
|
if (wdd->id == 0) {
|
||||||
|
- old_wdd = wdd;
|
||||||
|
+ old_wd_data = wd_data;
|
||||||
|
watchdog_miscdev.parent = wdd->parent;
|
||||||
|
err = misc_register(&watchdog_miscdev);
|
||||||
|
if (err != 0) {
|
||||||
|
@@ -651,23 +667,25 @@ static int watchdog_cdev_register(struct
|
||||||
|
if (err == -EBUSY)
|
||||||
|
pr_err("%s: a legacy watchdog module is probably present.\n",
|
||||||
|
wdd->info->identity);
|
||||||
|
- old_wdd = NULL;
|
||||||
|
+ old_wd_data = NULL;
|
||||||
|
+ kfree(wd_data);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in the data structures */
|
||||||
|
- cdev_init(&wdd->cdev, &watchdog_fops);
|
||||||
|
- wdd->cdev.owner = wdd->ops->owner;
|
||||||
|
+ cdev_init(&wd_data->cdev, &watchdog_fops);
|
||||||
|
+ wd_data->cdev.owner = wdd->ops->owner;
|
||||||
|
|
||||||
|
/* Add the device */
|
||||||
|
- err = cdev_add(&wdd->cdev, devno, 1);
|
||||||
|
+ err = cdev_add(&wd_data->cdev, devno, 1);
|
||||||
|
if (err) {
|
||||||
|
pr_err("watchdog%d unable to add device %d:%d\n",
|
||||||
|
wdd->id, MAJOR(watchdog_devt), wdd->id);
|
||||||
|
if (wdd->id == 0) {
|
||||||
|
misc_deregister(&watchdog_miscdev);
|
||||||
|
- old_wdd = NULL;
|
||||||
|
+ old_wd_data = NULL;
|
||||||
|
+ kref_put(&wd_data->kref, watchdog_core_data_release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
@@ -683,15 +701,20 @@ static int watchdog_cdev_register(struct
|
||||||
|
|
||||||
|
static void watchdog_cdev_unregister(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
- mutex_lock(&wdd->lock);
|
||||||
|
- set_bit(WDOG_UNREGISTERED, &wdd->status);
|
||||||
|
- mutex_unlock(&wdd->lock);
|
||||||
|
+ struct watchdog_core_data *wd_data = wdd->wd_data;
|
||||||
|
|
||||||
|
- cdev_del(&wdd->cdev);
|
||||||
|
+ cdev_del(&wd_data->cdev);
|
||||||
|
if (wdd->id == 0) {
|
||||||
|
misc_deregister(&watchdog_miscdev);
|
||||||
|
- old_wdd = NULL;
|
||||||
|
+ old_wd_data = NULL;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ mutex_lock(&wd_data->lock);
|
||||||
|
+ wd_data->wdd = NULL;
|
||||||
|
+ wdd->wd_data = NULL;
|
||||||
|
+ mutex_unlock(&wd_data->lock);
|
||||||
|
+
|
||||||
|
+ kref_put(&wd_data->kref, watchdog_core_data_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct class watchdog_class = {
|
||||||
|
@@ -742,9 +765,9 @@ int watchdog_dev_register(struct watchdo
|
||||||
|
|
||||||
|
void watchdog_dev_unregister(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
- watchdog_cdev_unregister(wdd);
|
||||||
|
device_destroy(&watchdog_class, wdd->dev->devt);
|
||||||
|
wdd->dev = NULL;
|
||||||
|
+ watchdog_cdev_unregister(wdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- a/include/linux/watchdog.h
|
||||||
|
+++ b/include/linux/watchdog.h
|
||||||
|
@@ -17,6 +17,7 @@
|
||||||
|
|
||||||
|
struct watchdog_ops;
|
||||||
|
struct watchdog_device;
|
||||||
|
+struct watchdog_core_data;
|
||||||
|
|
||||||
|
/** struct watchdog_ops - The watchdog-devices operations
|
||||||
|
*
|
||||||
|
@@ -28,8 +29,6 @@ struct watchdog_device;
|
||||||
|
* @set_timeout:The routine for setting the watchdog devices timeout value (in seconds).
|
||||||
|
* @get_timeleft:The routine that gets the time left before a reset (in seconds).
|
||||||
|
* @restart: The routine for restarting the machine.
|
||||||
|
- * @ref: The ref operation for dyn. allocated watchdog_device structs
|
||||||
|
- * @unref: The unref operation for dyn. allocated watchdog_device structs
|
||||||
|
* @ioctl: The routines that handles extra ioctl calls.
|
||||||
|
*
|
||||||
|
* The watchdog_ops structure contains a list of low-level operations
|
||||||
|
@@ -48,15 +47,14 @@ struct watchdog_ops {
|
||||||
|
int (*set_timeout)(struct watchdog_device *, unsigned int);
|
||||||
|
unsigned int (*get_timeleft)(struct watchdog_device *);
|
||||||
|
int (*restart)(struct watchdog_device *);
|
||||||
|
- void (*ref)(struct watchdog_device *);
|
||||||
|
- void (*unref)(struct watchdog_device *);
|
||||||
|
+ void (*ref)(struct watchdog_device *) __deprecated;
|
||||||
|
+ void (*unref)(struct watchdog_device *) __deprecated;
|
||||||
|
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** struct watchdog_device - The structure that defines a watchdog device
|
||||||
|
*
|
||||||
|
* @id: The watchdog's ID. (Allocated by watchdog_register_device)
|
||||||
|
- * @cdev: The watchdog's Character device.
|
||||||
|
* @dev: The device for our watchdog
|
||||||
|
* @parent: The parent bus device
|
||||||
|
* @info: Pointer to a watchdog_info structure.
|
||||||
|
@@ -67,8 +65,8 @@ struct watchdog_ops {
|
||||||
|
* @max_timeout:The watchdog devices maximum timeout value (in seconds).
|
||||||
|
* @reboot_nb: The notifier block to stop watchdog on reboot.
|
||||||
|
* @restart_nb: The notifier block to register a restart function.
|
||||||
|
- * @driver-data:Pointer to the drivers private data.
|
||||||
|
- * @lock: Lock for watchdog core internal use only.
|
||||||
|
+ * @driver_data:Pointer to the drivers private data.
|
||||||
|
+ * @wd_data: Pointer to watchdog core internal data.
|
||||||
|
* @status: Field that contains the devices internal status bits.
|
||||||
|
* @deferred: entry in wtd_deferred_reg_list which is used to
|
||||||
|
* register early initialized watchdogs.
|
||||||
|
@@ -84,7 +82,6 @@ struct watchdog_ops {
|
||||||
|
*/
|
||||||
|
struct watchdog_device {
|
||||||
|
int id;
|
||||||
|
- struct cdev cdev;
|
||||||
|
struct device *dev;
|
||||||
|
struct device *parent;
|
||||||
|
const struct watchdog_info *info;
|
||||||
|
@@ -96,15 +93,12 @@ struct watchdog_device {
|
||||||
|
struct notifier_block reboot_nb;
|
||||||
|
struct notifier_block restart_nb;
|
||||||
|
void *driver_data;
|
||||||
|
- struct mutex lock;
|
||||||
|
+ struct watchdog_core_data *wd_data;
|
||||||
|
unsigned long status;
|
||||||
|
/* Bit numbers for status flags */
|
||||||
|
#define WDOG_ACTIVE 0 /* Is the watchdog running/active */
|
||||||
|
-#define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */
|
||||||
|
-#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 */
|
||||||
|
-#define WDOG_STOP_ON_REBOOT 5 /* Should be stopped on reboot */
|
||||||
|
+#define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */
|
||||||
|
+#define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */
|
||||||
|
struct list_head deferred;
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
From 0254e953537c92df3e7d0176f401a211e944fd61 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Date: Sun, 3 Jan 2016 15:11:58 -0800
|
||||||
|
Subject: watchdog: Drop pointer to watchdog device from struct watchdog_device
|
||||||
|
|
||||||
|
The lifetime of the watchdog device pointer is different from the lifetime
|
||||||
|
of its character device. Remove it entirely to avoid race conditions.
|
||||||
|
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
Documentation/watchdog/watchdog-kernel-api.txt | 2 --
|
||||||
|
drivers/watchdog/watchdog_core.c | 8 ++++----
|
||||||
|
drivers/watchdog/watchdog_dev.c | 9 ++++-----
|
||||||
|
include/linux/watchdog.h | 2 --
|
||||||
|
4 files changed, 8 insertions(+), 13 deletions(-)
|
||||||
|
|
||||||
|
--- a/Documentation/watchdog/watchdog-kernel-api.txt
|
||||||
|
+++ b/Documentation/watchdog/watchdog-kernel-api.txt
|
||||||
|
@@ -44,7 +44,6 @@ The watchdog device structure looks like
|
||||||
|
|
||||||
|
struct watchdog_device {
|
||||||
|
int id;
|
||||||
|
- struct device *dev;
|
||||||
|
struct device *parent;
|
||||||
|
const struct watchdog_info *info;
|
||||||
|
const struct watchdog_ops *ops;
|
||||||
|
@@ -65,7 +64,6 @@ It contains following fields:
|
||||||
|
/dev/watchdog0 cdev (dynamic major, minor 0) as well as the old
|
||||||
|
/dev/watchdog miscdev. The id is set automatically when calling
|
||||||
|
watchdog_register_device.
|
||||||
|
-* dev: device under the watchdog class (created by watchdog_register_device).
|
||||||
|
* parent: set this to the parent device (or NULL) before calling
|
||||||
|
watchdog_register_device.
|
||||||
|
* info: a pointer to a watchdog_info structure. This structure gives some
|
||||||
|
--- a/drivers/watchdog/watchdog_core.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_core.c
|
||||||
|
@@ -249,8 +249,8 @@ static int __watchdog_register_device(st
|
||||||
|
|
||||||
|
ret = register_reboot_notifier(&wdd->reboot_nb);
|
||||||
|
if (ret) {
|
||||||
|
- dev_err(wdd->dev, "Cannot register reboot notifier (%d)\n",
|
||||||
|
- ret);
|
||||||
|
+ pr_err("watchdog%d: Cannot register reboot notifier (%d)\n",
|
||||||
|
+ wdd->id, ret);
|
||||||
|
watchdog_dev_unregister(wdd);
|
||||||
|
ida_simple_remove(&watchdog_ida, wdd->id);
|
||||||
|
return ret;
|
||||||
|
@@ -262,8 +262,8 @@ static int __watchdog_register_device(st
|
||||||
|
|
||||||
|
ret = register_restart_handler(&wdd->restart_nb);
|
||||||
|
if (ret)
|
||||||
|
- dev_warn(wdd->dev, "Cannot register restart handler (%d)\n",
|
||||||
|
- ret);
|
||||||
|
+ pr_warn("watchog%d: Cannot register restart handler (%d)\n",
|
||||||
|
+ wdd->id, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
--- a/drivers/watchdog/watchdog_dev.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_dev.c
|
||||||
|
@@ -143,7 +143,8 @@ static int watchdog_stop(struct watchdog
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) {
|
||||||
|
- dev_info(wdd->dev, "nowayout prevents watchdog being stopped!\n");
|
||||||
|
+ pr_info("watchdog%d: nowayout prevents watchdog being stopped!\n",
|
||||||
|
+ wdd->id);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -604,7 +605,7 @@ static int watchdog_release(struct inode
|
||||||
|
|
||||||
|
/* If the watchdog was not stopped, send a keepalive ping */
|
||||||
|
if (err < 0) {
|
||||||
|
- dev_crit(wdd->dev, "watchdog did not stop!\n");
|
||||||
|
+ pr_crit("watchdog%d: watchdog did not stop!\n", wdd->id);
|
||||||
|
watchdog_ping(wdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -750,7 +751,6 @@ int watchdog_dev_register(struct watchdo
|
||||||
|
watchdog_cdev_unregister(wdd);
|
||||||
|
return PTR_ERR(dev);
|
||||||
|
}
|
||||||
|
- wdd->dev = dev;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@@ -765,8 +765,7 @@ int watchdog_dev_register(struct watchdo
|
||||||
|
|
||||||
|
void watchdog_dev_unregister(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
- device_destroy(&watchdog_class, wdd->dev->devt);
|
||||||
|
- wdd->dev = NULL;
|
||||||
|
+ device_destroy(&watchdog_class, wdd->wd_data->cdev.dev);
|
||||||
|
watchdog_cdev_unregister(wdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
--- a/include/linux/watchdog.h
|
||||||
|
+++ b/include/linux/watchdog.h
|
||||||
|
@@ -55,7 +55,6 @@ struct watchdog_ops {
|
||||||
|
/** struct watchdog_device - The structure that defines a watchdog device
|
||||||
|
*
|
||||||
|
* @id: The watchdog's ID. (Allocated by watchdog_register_device)
|
||||||
|
- * @dev: The device for our watchdog
|
||||||
|
* @parent: The parent bus device
|
||||||
|
* @info: Pointer to a watchdog_info structure.
|
||||||
|
* @ops: Pointer to the list of watchdog operations.
|
||||||
|
@@ -82,7 +81,6 @@ struct watchdog_ops {
|
||||||
|
*/
|
||||||
|
struct watchdog_device {
|
||||||
|
int id;
|
||||||
|
- struct device *dev;
|
||||||
|
struct device *parent;
|
||||||
|
const struct watchdog_info *info;
|
||||||
|
const struct watchdog_ops *ops;
|
|
@ -0,0 +1,26 @@
|
||||||
|
From 62cd1c40ce1c7c16835b599751c7a002eb5bbdf5 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Tomas Winkler <tomas.winkler@intel.com>
|
||||||
|
Date: Sun, 3 Jan 2016 13:32:37 +0200
|
||||||
|
Subject: watchdog: kill unref/ref ops
|
||||||
|
|
||||||
|
ref/unref ops are not called at all so even marked them as deprecated
|
||||||
|
is misleading, we need to just drop the API.
|
||||||
|
|
||||||
|
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
include/linux/watchdog.h | 2 --
|
||||||
|
1 file changed, 2 deletions(-)
|
||||||
|
|
||||||
|
--- a/include/linux/watchdog.h
|
||||||
|
+++ b/include/linux/watchdog.h
|
||||||
|
@@ -47,8 +47,6 @@ struct watchdog_ops {
|
||||||
|
int (*set_timeout)(struct watchdog_device *, unsigned int);
|
||||||
|
unsigned int (*get_timeleft)(struct watchdog_device *);
|
||||||
|
int (*restart)(struct watchdog_device *);
|
||||||
|
- void (*ref)(struct watchdog_device *) __deprecated;
|
||||||
|
- void (*unref)(struct watchdog_device *) __deprecated;
|
||||||
|
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
From 80969a68ffed12f82e2a29908306ff43a6861a61 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Damien Riegel <damien.riegel@savoirfairelinux.com>
|
||||||
|
Date: Mon, 16 Nov 2015 12:28:09 -0500
|
||||||
|
Subject: watchdog: qcom-wdt: use core restart handler
|
||||||
|
|
||||||
|
Get rid of the custom restart handler by using the one provided by the
|
||||||
|
watchdog core.
|
||||||
|
|
||||||
|
Signed-off-by: Damien Riegel <damien.riegel@savoirfairelinux.com>
|
||||||
|
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
drivers/watchdog/qcom-wdt.c | 49 ++++++++++++++++++---------------------------
|
||||||
|
1 file changed, 19 insertions(+), 30 deletions(-)
|
||||||
|
|
||||||
|
--- a/drivers/watchdog/qcom-wdt.c
|
||||||
|
+++ b/drivers/watchdog/qcom-wdt.c
|
||||||
|
@@ -17,7 +17,6 @@
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
-#include <linux/reboot.h>
|
||||||
|
#include <linux/watchdog.h>
|
||||||
|
|
||||||
|
#define WDT_RST 0x38
|
||||||
|
@@ -28,7 +27,6 @@ struct qcom_wdt {
|
||||||
|
struct watchdog_device wdd;
|
||||||
|
struct clk *clk;
|
||||||
|
unsigned long rate;
|
||||||
|
- struct notifier_block restart_nb;
|
||||||
|
void __iomem *base;
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -72,25 +70,9 @@ static int qcom_wdt_set_timeout(struct w
|
||||||
|
return qcom_wdt_start(wdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static const struct watchdog_ops qcom_wdt_ops = {
|
||||||
|
- .start = qcom_wdt_start,
|
||||||
|
- .stop = qcom_wdt_stop,
|
||||||
|
- .ping = qcom_wdt_ping,
|
||||||
|
- .set_timeout = qcom_wdt_set_timeout,
|
||||||
|
- .owner = THIS_MODULE,
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
-static const struct watchdog_info qcom_wdt_info = {
|
||||||
|
- .options = WDIOF_KEEPALIVEPING
|
||||||
|
- | WDIOF_MAGICCLOSE
|
||||||
|
- | WDIOF_SETTIMEOUT,
|
||||||
|
- .identity = KBUILD_MODNAME,
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
-static int qcom_wdt_restart(struct notifier_block *nb, unsigned long action,
|
||||||
|
- void *data)
|
||||||
|
+static int qcom_wdt_restart(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
- struct qcom_wdt *wdt = container_of(nb, struct qcom_wdt, restart_nb);
|
||||||
|
+ struct qcom_wdt *wdt = to_qcom_wdt(wdd);
|
||||||
|
u32 timeout;
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -110,9 +92,25 @@ static int qcom_wdt_restart(struct notif
|
||||||
|
wmb();
|
||||||
|
|
||||||
|
msleep(150);
|
||||||
|
- return NOTIFY_DONE;
|
||||||
|
+ return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static const struct watchdog_ops qcom_wdt_ops = {
|
||||||
|
+ .start = qcom_wdt_start,
|
||||||
|
+ .stop = qcom_wdt_stop,
|
||||||
|
+ .ping = qcom_wdt_ping,
|
||||||
|
+ .set_timeout = qcom_wdt_set_timeout,
|
||||||
|
+ .restart = qcom_wdt_restart,
|
||||||
|
+ .owner = THIS_MODULE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct watchdog_info qcom_wdt_info = {
|
||||||
|
+ .options = WDIOF_KEEPALIVEPING
|
||||||
|
+ | WDIOF_MAGICCLOSE
|
||||||
|
+ | WDIOF_SETTIMEOUT,
|
||||||
|
+ .identity = KBUILD_MODNAME,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
static int qcom_wdt_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct qcom_wdt *wdt;
|
||||||
|
@@ -187,14 +185,6 @@ static int qcom_wdt_probe(struct platfor
|
||||||
|
goto err_clk_unprepare;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /*
|
||||||
|
- * WDT restart notifier has priority 0 (use as a last resort)
|
||||||
|
- */
|
||||||
|
- wdt->restart_nb.notifier_call = qcom_wdt_restart;
|
||||||
|
- ret = register_restart_handler(&wdt->restart_nb);
|
||||||
|
- if (ret)
|
||||||
|
- dev_err(&pdev->dev, "failed to setup restart handler\n");
|
||||||
|
-
|
||||||
|
platform_set_drvdata(pdev, wdt);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
@@ -207,7 +197,6 @@ static int qcom_wdt_remove(struct platfo
|
||||||
|
{
|
||||||
|
struct qcom_wdt *wdt = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
- unregister_restart_handler(&wdt->restart_nb);
|
||||||
|
watchdog_unregister_device(&wdt->wdd);
|
||||||
|
clk_disable_unprepare(wdt->clk);
|
||||||
|
return 0;
|
|
@ -0,0 +1,25 @@
|
||||||
|
From 0933b453f1c7104d873aacf8524f8ac380a7ed08 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Date: Thu, 24 Dec 2015 14:22:04 -0800
|
||||||
|
Subject: watchdog: qcom-wdt: Do not set 'dev' in struct watchdog_device
|
||||||
|
|
||||||
|
The 'dev' pointer in struct watchdog_device is set by the watchdog core
|
||||||
|
when registering the watchdog device and not by the driver. It points to
|
||||||
|
the watchdog device, not its parent.
|
||||||
|
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
drivers/watchdog/qcom-wdt.c | 1 -
|
||||||
|
1 file changed, 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/drivers/watchdog/qcom-wdt.c
|
||||||
|
+++ b/drivers/watchdog/qcom-wdt.c
|
||||||
|
@@ -164,7 +164,6 @@ static int qcom_wdt_probe(struct platfor
|
||||||
|
goto err_clk_unprepare;
|
||||||
|
}
|
||||||
|
|
||||||
|
- wdt->wdd.dev = &pdev->dev;
|
||||||
|
wdt->wdd.info = &qcom_wdt_info;
|
||||||
|
wdt->wdd.ops = &qcom_wdt_ops;
|
||||||
|
wdt->wdd.min_timeout = 1;
|
|
@ -0,0 +1,51 @@
|
||||||
|
rom 4d8b229d5ea610affe672e919021e9d02cd877da Mon Sep 17 00:00:00 2001
|
||||||
|
From: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Date: Fri, 26 Feb 2016 17:32:49 -0800
|
||||||
|
Subject: watchdog: Add 'action' and 'data' parameters to restart handler
|
||||||
|
callback
|
||||||
|
|
||||||
|
The 'action' (or restart mode) and data parameters may be used by restart
|
||||||
|
handlers, so they should be passed to the restart callback functions.
|
||||||
|
|
||||||
|
Cc: Sylvain Lemieux <slemieux@tycoint.com>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
drivers/watchdog/qcom-wdt.c | 3 ++-
|
||||||
|
drivers/watchdog/watchdog_core.c | 2 +-
|
||||||
|
include/linux/watchdog.h | 2 +-
|
||||||
|
|
||||||
|
--- a/drivers/watchdog/qcom-wdt.c
|
||||||
|
+++ b/drivers/watchdog/qcom-wdt.c
|
||||||
|
@@ -70,7 +70,8 @@ static int qcom_wdt_set_timeout(struct w
|
||||||
|
return qcom_wdt_start(wdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int qcom_wdt_restart(struct watchdog_device *wdd)
|
||||||
|
+static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
|
||||||
|
+ void *data)
|
||||||
|
{
|
||||||
|
struct qcom_wdt *wdt = to_qcom_wdt(wdd);
|
||||||
|
u32 timeout;
|
||||||
|
--- a/drivers/watchdog/watchdog_core.c
|
||||||
|
+++ b/drivers/watchdog/watchdog_core.c
|
||||||
|
@@ -164,7 +164,7 @@ static int watchdog_restart_notifier(str
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
- ret = wdd->ops->restart(wdd);
|
||||||
|
+ ret = wdd->ops->restart(wdd, action, data);
|
||||||
|
if (ret)
|
||||||
|
return NOTIFY_BAD;
|
||||||
|
|
||||||
|
--- a/include/linux/watchdog.h
|
||||||
|
+++ b/include/linux/watchdog.h
|
||||||
|
@@ -46,7 +46,7 @@ struct watchdog_ops {
|
||||||
|
unsigned int (*status)(struct watchdog_device *);
|
||||||
|
int (*set_timeout)(struct watchdog_device *, unsigned int);
|
||||||
|
unsigned int (*get_timeleft)(struct watchdog_device *);
|
||||||
|
- int (*restart)(struct watchdog_device *);
|
||||||
|
+ int (*restart)(struct watchdog_device *, unsigned long, void *);
|
||||||
|
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
From b6ef36d2c1e391adc1fe1b2dd2a0f887a9f3052b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Guenter Roeck <groeck@chromium.org>
|
||||||
|
Date: Mon, 4 Apr 2016 17:37:46 -0700
|
||||||
|
Subject: watchdog: qcom: Report reboot reason
|
||||||
|
|
||||||
|
The Qualcom watchdog timer block reports if the system was reset by the
|
||||||
|
watchdog. Pass the information to user space.
|
||||||
|
|
||||||
|
Reviewed-by: Grant Grundler <grundler@chromium.org>
|
||||||
|
Tested-by: Grant Grundler <grundler@chromium.org>
|
||||||
|
Signed-off-by: Guenter Roeck <groeck@chromium.org>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
drivers/watchdog/qcom-wdt.c | 7 ++++++-
|
||||||
|
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/drivers/watchdog/qcom-wdt.c
|
||||||
|
+++ b/drivers/watchdog/qcom-wdt.c
|
||||||
|
@@ -21,6 +21,7 @@
|
||||||
|
|
||||||
|
#define WDT_RST 0x38
|
||||||
|
#define WDT_EN 0x40
|
||||||
|
+#define WDT_STS 0x44
|
||||||
|
#define WDT_BITE_TIME 0x5C
|
||||||
|
|
||||||
|
struct qcom_wdt {
|
||||||
|
@@ -108,7 +109,8 @@ static const struct watchdog_ops qcom_wd
|
||||||
|
static const struct watchdog_info qcom_wdt_info = {
|
||||||
|
.options = WDIOF_KEEPALIVEPING
|
||||||
|
| WDIOF_MAGICCLOSE
|
||||||
|
- | WDIOF_SETTIMEOUT,
|
||||||
|
+ | WDIOF_SETTIMEOUT
|
||||||
|
+ | WDIOF_CARDRESET,
|
||||||
|
.identity = KBUILD_MODNAME,
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -171,6 +173,9 @@ static int qcom_wdt_probe(struct platfor
|
||||||
|
wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
|
||||||
|
wdt->wdd.parent = &pdev->dev;
|
||||||
|
|
||||||
|
+ if (readl(wdt->base + WDT_STS) & 1)
|
||||||
|
+ wdt->wdd.bootstatus = WDIOF_CARDRESET;
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* If 'timeout-sec' unspecified in devicetree, assume a 30 second
|
||||||
|
* default, unless the max timeout is less than 30 seconds, then use
|
|
@ -0,0 +1,162 @@
|
||||||
|
From f0d9d0f4b44ae5503ea368e7f066b20f12ca1d37 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||||
|
Date: Wed, 29 Jun 2016 10:50:01 -0700
|
||||||
|
Subject: watchdog: qcom: add option for standalone watchdog not in timer block
|
||||||
|
|
||||||
|
Commit 0dfd582e026a ("watchdog: qcom: use timer devicetree
|
||||||
|
binding") moved to use the watchdog as a subset timer
|
||||||
|
register block. Some devices have the watchdog completely
|
||||||
|
standalone with slightly different register offsets as
|
||||||
|
well so let's account for the differences here.
|
||||||
|
|
||||||
|
The existing "kpss-standalone" compatible string doesn't
|
||||||
|
make it entirely clear exactly what the device is so
|
||||||
|
rename to "kpss-wdt" to reflect watchdog timer
|
||||||
|
functionality. Also update ipq4019 DTS with an SoC
|
||||||
|
specific compatible.
|
||||||
|
|
||||||
|
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||||
|
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||||
|
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
.../devicetree/bindings/watchdog/qcom-wdt.txt | 2 +
|
||||||
|
arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 +-
|
||||||
|
drivers/watchdog/qcom-wdt.c | 64 ++++++++++++++++------
|
||||||
|
3 files changed, 51 insertions(+), 17 deletions(-)
|
||||||
|
|
||||||
|
--- a/drivers/watchdog/qcom-wdt.c
|
||||||
|
+++ b/drivers/watchdog/qcom-wdt.c
|
||||||
|
@@ -18,19 +18,42 @@
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/watchdog.h>
|
||||||
|
+#include <linux/of_device.h>
|
||||||
|
|
||||||
|
-#define WDT_RST 0x38
|
||||||
|
-#define WDT_EN 0x40
|
||||||
|
-#define WDT_STS 0x44
|
||||||
|
-#define WDT_BITE_TIME 0x5C
|
||||||
|
+enum wdt_reg {
|
||||||
|
+ WDT_RST,
|
||||||
|
+ WDT_EN,
|
||||||
|
+ WDT_STS,
|
||||||
|
+ WDT_BITE_TIME,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const u32 reg_offset_data_apcs_tmr[] = {
|
||||||
|
+ [WDT_RST] = 0x38,
|
||||||
|
+ [WDT_EN] = 0x40,
|
||||||
|
+ [WDT_STS] = 0x44,
|
||||||
|
+ [WDT_BITE_TIME] = 0x5C,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const u32 reg_offset_data_kpss[] = {
|
||||||
|
+ [WDT_RST] = 0x4,
|
||||||
|
+ [WDT_EN] = 0x8,
|
||||||
|
+ [WDT_STS] = 0xC,
|
||||||
|
+ [WDT_BITE_TIME] = 0x14,
|
||||||
|
+};
|
||||||
|
|
||||||
|
struct qcom_wdt {
|
||||||
|
struct watchdog_device wdd;
|
||||||
|
struct clk *clk;
|
||||||
|
unsigned long rate;
|
||||||
|
void __iomem *base;
|
||||||
|
+ const u32 *layout;
|
||||||
|
};
|
||||||
|
|
||||||
|
+static void __iomem *wdt_addr(struct qcom_wdt *wdt, enum wdt_reg reg)
|
||||||
|
+{
|
||||||
|
+ return wdt->base + wdt->layout[reg];
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static inline
|
||||||
|
struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd)
|
||||||
|
{
|
||||||
|
@@ -41,10 +64,10 @@ static int qcom_wdt_start(struct watchdo
|
||||||
|
{
|
||||||
|
struct qcom_wdt *wdt = to_qcom_wdt(wdd);
|
||||||
|
|
||||||
|
- writel(0, wdt->base + WDT_EN);
|
||||||
|
- writel(1, wdt->base + WDT_RST);
|
||||||
|
- writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME);
|
||||||
|
- writel(1, wdt->base + WDT_EN);
|
||||||
|
+ writel(0, wdt_addr(wdt, WDT_EN));
|
||||||
|
+ writel(1, wdt_addr(wdt, WDT_RST));
|
||||||
|
+ writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME));
|
||||||
|
+ writel(1, wdt_addr(wdt, WDT_EN));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -52,7 +75,7 @@ static int qcom_wdt_stop(struct watchdog
|
||||||
|
{
|
||||||
|
struct qcom_wdt *wdt = to_qcom_wdt(wdd);
|
||||||
|
|
||||||
|
- writel(0, wdt->base + WDT_EN);
|
||||||
|
+ writel(0, wdt_addr(wdt, WDT_EN));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -60,7 +83,7 @@ static int qcom_wdt_ping(struct watchdog
|
||||||
|
{
|
||||||
|
struct qcom_wdt *wdt = to_qcom_wdt(wdd);
|
||||||
|
|
||||||
|
- writel(1, wdt->base + WDT_RST);
|
||||||
|
+ writel(1, wdt_addr(wdt, WDT_RST));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -83,10 +106,10 @@ static int qcom_wdt_restart(struct watch
|
||||||
|
*/
|
||||||
|
timeout = 128 * wdt->rate / 1000;
|
||||||
|
|
||||||
|
- writel(0, wdt->base + WDT_EN);
|
||||||
|
- writel(1, wdt->base + WDT_RST);
|
||||||
|
- writel(timeout, wdt->base + WDT_BITE_TIME);
|
||||||
|
- writel(1, wdt->base + WDT_EN);
|
||||||
|
+ writel(0, wdt_addr(wdt, WDT_EN));
|
||||||
|
+ writel(1, wdt_addr(wdt, WDT_RST));
|
||||||
|
+ writel(timeout, wdt_addr(wdt, WDT_BITE_TIME));
|
||||||
|
+ writel(1, wdt_addr(wdt, WDT_EN));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Actually make sure the above sequence hits hardware before sleeping.
|
||||||
|
@@ -119,9 +142,16 @@ static int qcom_wdt_probe(struct platfor
|
||||||
|
struct qcom_wdt *wdt;
|
||||||
|
struct resource *res;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
+ const u32 *regs;
|
||||||
|
u32 percpu_offset;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
+ regs = of_device_get_match_data(&pdev->dev);
|
||||||
|
+ if (!regs) {
|
||||||
|
+ dev_err(&pdev->dev, "Unsupported QCOM WDT module\n");
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
|
||||||
|
if (!wdt)
|
||||||
|
return -ENOMEM;
|
||||||
|
@@ -172,6 +202,7 @@ static int qcom_wdt_probe(struct platfor
|
||||||
|
wdt->wdd.min_timeout = 1;
|
||||||
|
wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
|
||||||
|
wdt->wdd.parent = &pdev->dev;
|
||||||
|
+ wdt->layout = regs;
|
||||||
|
|
||||||
|
if (readl(wdt->base + WDT_STS) & 1)
|
||||||
|
wdt->wdd.bootstatus = WDIOF_CARDRESET;
|
||||||
|
@@ -208,8 +239,9 @@ static int qcom_wdt_remove(struct platfo
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id qcom_wdt_of_table[] = {
|
||||||
|
- { .compatible = "qcom,kpss-timer" },
|
||||||
|
- { .compatible = "qcom,scss-timer" },
|
||||||
|
+ { .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr },
|
||||||
|
+ { .compatible = "qcom,scss-timer", .data = reg_offset_data_apcs_tmr },
|
||||||
|
+ { .compatible = "qcom,kpss-wdt", .data = reg_offset_data_kpss },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
|
|
@ -0,0 +1,60 @@
|
||||||
|
From 10073a205df269abcbd9c3fbc690a813827107ef Mon Sep 17 00:00:00 2001
|
||||||
|
From: Matthew McClintock <mmcclint@codeaurora.org>
|
||||||
|
Date: Tue, 28 Jun 2016 11:35:21 -0700
|
||||||
|
Subject: watchdog: qcom: configure BARK time in addition to BITE time
|
||||||
|
|
||||||
|
For certain parts and some versions of TZ, TZ will reset the chip
|
||||||
|
when a BARK is triggered even though it was not configured here. So
|
||||||
|
by default let's configure this BARK time as well.
|
||||||
|
|
||||||
|
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
|
||||||
|
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Thomas Pedersen <twp@codeaurora.org>
|
||||||
|
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
|
||||||
|
---
|
||||||
|
drivers/watchdog/qcom-wdt.c | 5 +++++
|
||||||
|
1 file changed, 5 insertions(+)
|
||||||
|
|
||||||
|
--- a/drivers/watchdog/qcom-wdt.c
|
||||||
|
+++ b/drivers/watchdog/qcom-wdt.c
|
||||||
|
@@ -24,6 +24,7 @@ enum wdt_reg {
|
||||||
|
WDT_RST,
|
||||||
|
WDT_EN,
|
||||||
|
WDT_STS,
|
||||||
|
+ WDT_BARK_TIME,
|
||||||
|
WDT_BITE_TIME,
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -31,6 +32,7 @@ static const u32 reg_offset_data_apcs_tm
|
||||||
|
[WDT_RST] = 0x38,
|
||||||
|
[WDT_EN] = 0x40,
|
||||||
|
[WDT_STS] = 0x44,
|
||||||
|
+ [WDT_BARK_TIME] = 0x4C,
|
||||||
|
[WDT_BITE_TIME] = 0x5C,
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -38,6 +40,7 @@ static const u32 reg_offset_data_kpss[]
|
||||||
|
[WDT_RST] = 0x4,
|
||||||
|
[WDT_EN] = 0x8,
|
||||||
|
[WDT_STS] = 0xC,
|
||||||
|
+ [WDT_BARK_TIME] = 0x10,
|
||||||
|
[WDT_BITE_TIME] = 0x14,
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -66,6 +69,7 @@ static int qcom_wdt_start(struct watchdo
|
||||||
|
|
||||||
|
writel(0, wdt_addr(wdt, WDT_EN));
|
||||||
|
writel(1, wdt_addr(wdt, WDT_RST));
|
||||||
|
+ writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
|
||||||
|
writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME));
|
||||||
|
writel(1, wdt_addr(wdt, WDT_EN));
|
||||||
|
return 0;
|
||||||
|
@@ -108,6 +112,7 @@ static int qcom_wdt_restart(struct watch
|
||||||
|
|
||||||
|
writel(0, wdt_addr(wdt, WDT_EN));
|
||||||
|
writel(1, wdt_addr(wdt, WDT_RST));
|
||||||
|
+ writel(timeout, wdt_addr(wdt, WDT_BARK_TIME));
|
||||||
|
writel(timeout, wdt_addr(wdt, WDT_BITE_TIME));
|
||||||
|
writel(1, wdt_addr(wdt, WDT_EN));
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
From abc9f55079169806bcc31f29ec27f7df11c6184c Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ram Chandra Jangir <rjangi@codeaurora.org>
|
|
||||||
Date: Thu, 4 Feb 2016 12:41:56 +0530
|
|
||||||
Subject: [PATCH 2/2] watchdog: qcom: set WDT_BARK_TIME register offset to one
|
|
||||||
second less of bite time
|
|
||||||
|
|
||||||
Currently WDT_BARK_TIME register offset is not configured with bark
|
|
||||||
timeout during wdt_start,and it is taking bark timeout's default value.
|
|
||||||
For some versions of TZ (secure mode) will consider a BARK the same
|
|
||||||
as BITE and reset the board.
|
|
||||||
|
|
||||||
So instead let's just configure the BARK time to be less than a second
|
|
||||||
of the bite timeout so the board does not reset in this scenario
|
|
||||||
|
|
||||||
Change-Id: Ie09850ad7e0470ed721e6924911ca2a81fd9ff8a
|
|
||||||
Signed-off-by: Ram Chandra Jangir <rjangi@codeaurora.org>
|
|
||||||
---
|
|
||||||
drivers/watchdog/qcom-wdt.c | 2 ++
|
|
||||||
1 file changed, 2 insertions(+)
|
|
||||||
|
|
||||||
--- a/drivers/watchdog/qcom-wdt.c
|
|
||||||
+++ b/drivers/watchdog/qcom-wdt.c
|
|
||||||
@@ -22,6 +22,7 @@
|
|
||||||
|
|
||||||
#define WDT_RST 0x38
|
|
||||||
#define WDT_EN 0x40
|
|
||||||
+#define WDT_BARK_TIME 0x4C
|
|
||||||
#define WDT_BITE_TIME 0x5C
|
|
||||||
|
|
||||||
struct qcom_wdt {
|
|
||||||
@@ -44,6 +45,7 @@ static int qcom_wdt_start(struct watchdo
|
|
||||||
|
|
||||||
writel(0, wdt->base + WDT_EN);
|
|
||||||
writel(1, wdt->base + WDT_RST);
|
|
||||||
+ writel((wdd->timeout - 1) * wdt->rate, wdt->base + WDT_BARK_TIME);
|
|
||||||
writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME);
|
|
||||||
writel(1, wdt->base + WDT_EN);
|
|
||||||
return 0;
|
|
Loading…
Reference in New Issue