ipq806x: backport upstream wdt driver
authorPavel Kubelun <be.dissent@gmail.com>
Thu, 3 Nov 2016 23:12:32 +0000 (02:12 +0300)
committerJohn Crispin <john@phrozen.org>
Wed, 16 Nov 2016 09:59:30 +0000 (10:59 +0100)
Signed-off-by: Pavel Kubelun <be.dissent@gmail.com>
16 files changed:
target/linux/ipq806x/config-4.4
target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch [new file with mode: 0644]
target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch [deleted file]

index e67d0f17d70ec1114ea2a527ea8597c72542e459..252e53a1409d64c53cbc34494e33e6bbfc7c3743 100644 (file)
@@ -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 (file)
index 0000000..0534e78
--- /dev/null
@@ -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 *);
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 (file)
index 0000000..2afa4e5
--- /dev/null
@@ -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)
+ {
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 (file)
index 0000000..1906a1f
--- /dev/null
@@ -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);
+ }
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 (file)
index 0000000..fd4b38a
--- /dev/null
@@ -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,
+ };
+ /*
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 (file)
index 0000000..059ebdd
--- /dev/null
@@ -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;
+ }
+ /*
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 (file)
index 0000000..214dabf
--- /dev/null
@@ -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;
+ };
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 (file)
index 0000000..9640cd4
--- /dev/null
@@ -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;
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 (file)
index 0000000..ba56a86
--- /dev/null
@@ -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);
+ };
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 (file)
index 0000000..d36dedc
--- /dev/null
@@ -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;
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 (file)
index 0000000..31371c2
--- /dev/null
@@ -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;
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 (file)
index 0000000..7ceff32
--- /dev/null
@@ -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);
+ };
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 (file)
index 0000000..f7fcaee
--- /dev/null
@@ -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
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 (file)
index 0000000..82dd875
--- /dev/null
@@ -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);
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 (file)
index 0000000..bdc3f99
--- /dev/null
@@ -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));
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 (file)
index dde5822..0000000
+++ /dev/null
@@ -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;