diff options
author | Pavel Kubelun <be.dissent@gmail.com> | 2016-11-04 02:12:32 +0300 |
---|---|---|
committer | John Crispin <john@phrozen.org> | 2016-11-16 10:59:30 +0100 |
commit | 793d448a51b53d81e2dbd58a5865a204de92ad34 (patch) | |
tree | c0bc1ee32bdff6336dfa73288b5d7f2ac8564a7b /target/linux | |
parent | 4a6f9fc6333d5043d4ddfd25d87be3ae17f9e794 (diff) | |
download | upstream-793d448a51b53d81e2dbd58a5865a204de92ad34.tar.gz upstream-793d448a51b53d81e2dbd58a5865a204de92ad34.tar.bz2 upstream-793d448a51b53d81e2dbd58a5865a204de92ad34.zip |
ipq806x: backport upstream wdt driver
Signed-off-by: Pavel Kubelun <be.dissent@gmail.com>
Diffstat (limited to 'target/linux')
16 files changed, 2567 insertions, 38 deletions
diff --git a/target/linux/ipq806x/config-4.4 b/target/linux/ipq806x/config-4.4 index e67d0f17d7..252e53a140 100644 --- a/target/linux/ipq806x/config-4.4 +++ b/target/linux/ipq806x/config-4.4 @@ -412,6 +412,7 @@ CONFIG_VECTORS_BASE=0xffff0000 CONFIG_VFP=y CONFIG_VFPv3=y CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_SYSFS is not set # CONFIG_WL_TI is not set CONFIG_XPS=y CONFIG_XZ_DEC_ARM=y diff --git a/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch b/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch new file mode 100644 index 0000000000..0534e78a27 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-1-watchdog-core-add-restart-handler-support.patch @@ -0,0 +1,205 @@ +From 2165bf524da5f5e496d1cdb8c5afae1345ecce1e Mon Sep 17 00:00:00 2001 +From: Damien Riegel <damien.riegel@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 index 0000000000..2afa4e566b --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-2-watchdog-core-add-reboot-notifier-support.patch @@ -0,0 +1,160 @@ +From e131319669e0ef5e6fcd75174daeffa40492135c Mon Sep 17 00:00:00 2001 +From: Damien Riegel <damien.riegel@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 index 0000000000..1906a1f4e5 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-3-watchdog-Use-static-struct-class-watchdog_class-instead-of-pointer.patch @@ -0,0 +1,111 @@ +From 906d7a5cfeda508e7361f021605579a00cd82815 Mon Sep 17 00:00:00 2001 +From: Pratyush Anand <panand@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 index 0000000000..fd4b38a9fb --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-4-watchdog-Read-device-status-through-sysfs-attributes.patch @@ -0,0 +1,260 @@ +From 33b711269ade3f6bc9d9d15e4343e6fa922d999b Mon Sep 17 00:00:00 2001 +From: Pratyush Anand <panand@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 index 0000000000..059ebdd823 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-5-watchdog-Create-watchdog-device-in-watchdog_dev-c.patch @@ -0,0 +1,261 @@ +From 32ecc6392654a0db34b310e8924b5b2c3b8bf503 Mon Sep 17 00:00:00 2001 +From: Guenter Roeck <linux@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 index 0000000000..214dabfb9a --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch @@ -0,0 +1,969 @@ +From b4ffb1909843b28f3b1b60197d517b123b7a9b66 Mon Sep 17 00:00:00 2001 +From: Guenter Roeck <linux@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 index 0000000000..9640cd4757 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-7-watchdog-Drop-pointer-to-watchdog-device-from-struct-watchdog_device.patch @@ -0,0 +1,117 @@ +From 0254e953537c92df3e7d0176f401a211e944fd61 Mon Sep 17 00:00:00 2001 +From: Guenter Roeck <linux@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 index 0000000000..ba56a86538 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/009-8-watchdog-kill-unref-ref-ops.patch @@ -0,0 +1,26 @@ +From 62cd1c40ce1c7c16835b599751c7a002eb5bbdf5 Mon Sep 17 00:00:00 2001 +From: Tomas Winkler <tomas.winkler@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 index 0000000000..d36dedcd1c --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-1-qcom-wdt-use-core-restart-handler.patch @@ -0,0 +1,113 @@ +From 80969a68ffed12f82e2a29908306ff43a6861a61 Mon Sep 17 00:00:00 2001 +From: Damien Riegel <damien.riegel@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 index 0000000000..31371c2c81 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-2-qcom-wdt-Do-not-set-dev-in-struct-watchdog_device.patch @@ -0,0 +1,25 @@ +From 0933b453f1c7104d873aacf8524f8ac380a7ed08 Mon Sep 17 00:00:00 2001 +From: Guenter Roeck <linux@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 index 0000000000..7ceff32caa --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-3-watchdog-Add-action-and-data-parameters-to-restart-handler-callback.patch @@ -0,0 +1,51 @@ +rom 4d8b229d5ea610affe672e919021e9d02cd877da Mon Sep 17 00:00:00 2001 +From: Guenter Roeck <linux@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 index 0000000000..f7fcaeeda2 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-4-watchdog-qcom-Report-reboot-reason.patch @@ -0,0 +1,46 @@ +From b6ef36d2c1e391adc1fe1b2dd2a0f887a9f3052b Mon Sep 17 00:00:00 2001 +From: Guenter Roeck <groeck@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 index 0000000000..82dd875434 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch @@ -0,0 +1,162 @@ +From f0d9d0f4b44ae5503ea368e7f066b20f12ca1d37 Mon Sep 17 00:00:00 2001 +From: Matthew McClintock <mmcclint@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 index 0000000000..bdc3f99897 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-6-watchdog-qcom-configure-BARK-time-in-addition-to-BITE-time.patch @@ -0,0 +1,60 @@ +From 10073a205df269abcbd9c3fbc690a813827107ef Mon Sep 17 00:00:00 2001 +From: Matthew McClintock <mmcclint@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 index dde5822f16..0000000000 --- a/target/linux/ipq806x/patches-4.4/710-watchdog-qcom-set-WDT_BARK_TIME-register-offset-to-o.patch +++ /dev/null @@ -1,38 +0,0 @@ -From abc9f55079169806bcc31f29ec27f7df11c6184c Mon Sep 17 00:00:00 2001 -From: Ram Chandra Jangir <rjangi@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; |