[source] ipq806x: backport upstream wdt driver
LEDE Commits
lede-commits at lists.infradead.org
Wed Nov 16 02:11:04 PST 2016
blogic pushed a commit to source.git, branch master:
https://git.lede-project.org/793d448a51b53d81e2dbd58a5865a204de92ad34
commit 793d448a51b53d81e2dbd58a5865a204de92ad34
Author: Pavel Kubelun <be.dissent at gmail.com>
AuthorDate: Fri Nov 4 02:12:32 2016 +0300
ipq806x: backport upstream wdt driver
Signed-off-by: Pavel Kubelun <be.dissent at gmail.com>
---
target/linux/ipq806x/config-4.4 | 1 +
...watchdog-core-add-restart-handler-support.patch | 205 +++++
...watchdog-core-add-reboot-notifier-support.patch | 160 ++++
...t-class-watchdog_class-instead-of-pointer.patch | 111 +++
...ad-device-status-through-sysfs-attributes.patch | 260 ++++++
...-Create-watchdog-device-in-watchdog_dev-c.patch | 261 ++++++
...tain-variables-based-on-variable-lifetime.patch | 969 +++++++++++++++++++++
...tchdog-device-from-struct-watchdog_device.patch | 117 +++
.../009-8-watchdog-kill-unref-ref-ops.patch | 26 +
.../010-1-qcom-wdt-use-core-restart-handler.patch | 113 +++
...-Do-not-set-dev-in-struct-watchdog_device.patch | 25 +
...ta-parameters-to-restart-handler-callback.patch | 51 ++
.../010-4-watchdog-qcom-Report-reboot-reason.patch | 46 +
...or-standalone-watchdog-not-in-timer-block.patch | 162 ++++
...figure-BARK-time-in-addition-to-BITE-time.patch | 60 ++
...om-set-WDT_BARK_TIME-register-offset-to-o.patch | 38 -
16 files changed, 2567 insertions(+), 38 deletions(-)
diff --git a/target/linux/ipq806x/config-4.4 b/target/linux/ipq806x/config-4.4
index e67d0f1..252e53a 100644
--- a/target/linux/ipq806x/config-4.4
+++ b/target/linux/ipq806x/config-4.4
@@ -412,6 +412,7 @@ CONFIG_VECTORS_BASE=0xffff0000
CONFIG_VFP=y
CONFIG_VFPv3=y
CONFIG_WATCHDOG_CORE=y
+# CONFIG_WATCHDOG_SYSFS is not set
# CONFIG_WL_TI is not set
CONFIG_XPS=y
CONFIG_XZ_DEC_ARM=y
diff --git a/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch b/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch
new file mode 100644
index 0000000..0534e78
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch
@@ -0,0 +1,205 @@
+From 2165bf524da5f5e496d1cdb8c5afae1345ecce1e Mon Sep 17 00:00:00 2001
+From: Damien Riegel <damien.riegel at 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 at savoirfairelinux.com>
+Signed-off-by: Damien Riegel <damien.riegel at savoirfairelinux.com>
+Reviewed-by: Guenter Roeck <linux at roeck-us.net>
+Reviewed-by: Vivien Didelot <vivien.didelot at savoirfairelinux.com>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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 *);
diff --git a/target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch b/target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch
new file mode 100644
index 0000000..2afa4e5
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch
@@ -0,0 +1,160 @@
+From e131319669e0ef5e6fcd75174daeffa40492135c Mon Sep 17 00:00:00 2001
+From: Damien Riegel <damien.riegel at 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 at savoirfairelinux.com>
+Signed-off-by: Damien Riegel <damien.riegel at savoirfairelinux.com>
+Reviewed-by: Vivien Didelot <vivien.didelot at savoirfairelinux.com>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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)
+ {
diff --git a/target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch b/target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch
new file mode 100644
index 0000000..1906a1f
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch
@@ -0,0 +1,111 @@
+From 906d7a5cfeda508e7361f021605579a00cd82815 Mon Sep 17 00:00:00 2001
+From: Pratyush Anand <panand at 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 at redhat.com>
+Suggested-by: Guenter Roeck <linux at roeck-us.net>
+Reviewed-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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);
+ }
diff --git a/target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch b/target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch
new file mode 100644
index 0000000..fd4b38a
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch
@@ -0,0 +1,260 @@
+From 33b711269ade3f6bc9d9d15e4343e6fa922d999b Mon Sep 17 00:00:00 2001
+From: Pratyush Anand <panand at 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 at redhat.com>
+Reviewed-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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,
+ };
+
+ /*
diff --git a/target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch b/target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch
new file mode 100644
index 0000000..059ebdd
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch
@@ -0,0 +1,261 @@
+From 32ecc6392654a0db34b310e8924b5b2c3b8bf503 Mon Sep 17 00:00:00 2001
+From: Guenter Roeck <linux at 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 at savoirfairelinux.com>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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;
+ }
+
+ /*
diff --git a/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch b/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch
new file mode 100644
index 0000000..214dabf
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch
@@ -0,0 +1,969 @@
+From b4ffb1909843b28f3b1b60197d517b123b7a9b66 Mon Sep 17 00:00:00 2001
+From: Guenter Roeck <linux at 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 at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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;
+ };
+
diff --git a/target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch b/target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch
new file mode 100644
index 0000000..9640cd4
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch
@@ -0,0 +1,117 @@
+From 0254e953537c92df3e7d0176f401a211e944fd61 Mon Sep 17 00:00:00 2001
+From: Guenter Roeck <linux at 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 at roeck-us.net>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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;
diff --git a/target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch b/target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch
new file mode 100644
index 0000000..ba56a86
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch
@@ -0,0 +1,26 @@
+From 62cd1c40ce1c7c16835b599751c7a002eb5bbdf5 Mon Sep 17 00:00:00 2001
+From: Tomas Winkler <tomas.winkler at 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 at intel.com>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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);
+ };
+
diff --git a/target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch b/target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch
new file mode 100644
index 0000000..d36dedc
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch
@@ -0,0 +1,113 @@
+From 80969a68ffed12f82e2a29908306ff43a6861a61 Mon Sep 17 00:00:00 2001
+From: Damien Riegel <damien.riegel at 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 at savoirfairelinux.com>
+Reviewed-by: Guenter Roeck <linux at roeck-us.net>
+Reviewed-by: Vivien Didelot <vivien.didelot at savoirfairelinux.com>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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;
diff --git a/target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch b/target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch
new file mode 100644
index 0000000..31371c2
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch
@@ -0,0 +1,25 @@
+From 0933b453f1c7104d873aacf8524f8ac380a7ed08 Mon Sep 17 00:00:00 2001
+From: Guenter Roeck <linux at 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 at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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;
diff --git a/target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch b/target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch
new file mode 100644
index 0000000..7ceff32
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch
@@ -0,0 +1,51 @@
+rom 4d8b229d5ea610affe672e919021e9d02cd877da Mon Sep 17 00:00:00 2001
+From: Guenter Roeck <linux at 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 at tycoint.com>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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);
+ };
+
diff --git a/target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch b/target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch
new file mode 100644
index 0000000..f7fcaee
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch
@@ -0,0 +1,46 @@
+From b6ef36d2c1e391adc1fe1b2dd2a0f887a9f3052b Mon Sep 17 00:00:00 2001
+From: Guenter Roeck <groeck at 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 at chromium.org>
+Tested-by: Grant Grundler <grundler at chromium.org>
+Signed-off-by: Guenter Roeck <groeck at chromium.org>
+Signed-off-by: Wim Van Sebroeck <wim at 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
diff --git a/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch b/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch
new file mode 100644
index 0000000..82dd875
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch
@@ -0,0 +1,162 @@
+From f0d9d0f4b44ae5503ea368e7f066b20f12ca1d37 Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at 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 at codeaurora.org>
+Signed-off-by: Thomas Pedersen <twp at codeaurora.org>
+Reviewed-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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);
diff --git a/target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch b/target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch
new file mode 100644
index 0000000..bdc3f99
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch
@@ -0,0 +1,60 @@
+From 10073a205df269abcbd9c3fbc690a813827107ef Mon Sep 17 00:00:00 2001
+From: Matthew McClintock <mmcclint at 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 at codeaurora.org>
+Reviewed-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Thomas Pedersen <twp at codeaurora.org>
+Signed-off-by: Guenter Roeck <linux at roeck-us.net>
+Signed-off-by: Wim Van Sebroeck <wim at 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));
+
diff --git a/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch b/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch
deleted file mode 100644
index dde5822..0000000
--- a/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From abc9f55079169806bcc31f29ec27f7df11c6184c Mon Sep 17 00:00:00 2001
-From: Ram Chandra Jangir <rjangi at 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 at 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;
More information about the lede-commits
mailing list