parent
4a6f9fc633
commit
793d448a51
@ -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