From 83fbae5a148cc1cd53e5be1a28edb3b6701b7af2 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 27 May 2016 17:33:54 +0200 Subject: [PATCH 01/50] watchdog: Add a device managed API for watchdog_register_device() This helps in reducing code in .remove callbacks and sometimes dropping .remove callbacks entirely. Signed-off-by: Neil Armstrong Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/driver-model/devres.txt | 3 +++ drivers/watchdog/watchdog_core.c | 37 +++++++++++++++++++++++++++ include/linux/watchdog.h | 3 +++ 3 files changed, 43 insertions(+) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index c63eea0c1c8c..589296bdc133 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -357,3 +357,6 @@ SLAVE DMA ENGINE SPI devm_spi_register_master() + +WATCHDOG + devm_watchdog_register_device() diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 7c3ba58ae1be..f4f02d2763ec 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -329,6 +329,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd) EXPORT_SYMBOL_GPL(watchdog_unregister_device); +static void devm_watchdog_unregister_device(struct device *dev, void *res) +{ + watchdog_unregister_device(*(struct watchdog_device **)res); +} + +/** + * devm_watchdog_register_device() - resource managed watchdog_register_device() + * @dev: device that is registering this watchdog device + * @wdd: watchdog device + * + * Managed watchdog_register_device(). For watchdog device registered by this + * function, watchdog_unregister_device() is automatically called on driver + * detach. See watchdog_register_device() for more information. + */ +int devm_watchdog_register_device(struct device *dev, + struct watchdog_device *wdd) +{ + struct watchdog_device **rcwdd; + int ret; + + rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*wdd), + GFP_KERNEL); + if (!rcwdd) + return -ENOMEM; + + ret = watchdog_register_device(wdd); + if (!ret) { + *rcwdd = wdd; + devres_add(dev, rcwdd); + } else { + devres_free(rcwdd); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_watchdog_register_device); + static int __init watchdog_deferred_registration(void) { mutex_lock(&wtd_deferred_reg_mutex); diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 51732d6c9555..6b75e38b683f 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -180,4 +180,7 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd, extern int watchdog_register_device(struct watchdog_device *); extern void watchdog_unregister_device(struct watchdog_device *); +/* devres register variant */ +int devm_watchdog_register_device(struct device *dev, struct watchdog_device *); + #endif /* ifndef _LINUX_WATCHDOG_H */ From 0215efc02e32d0a806ad13be7e43c6784428b334 Mon Sep 17 00:00:00 2001 From: Brian Boylston Date: Wed, 25 May 2016 11:20:15 -0500 Subject: [PATCH 02/50] watchdog: hpwdt: remove email address from doc Remove Tom's email address from the documentation Signed-off-by: Brian Boylston Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/hpwdt.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Documentation/watchdog/hpwdt.txt b/Documentation/watchdog/hpwdt.txt index a40398cce9d1..7a9f635d0258 100644 --- a/Documentation/watchdog/hpwdt.txt +++ b/Documentation/watchdog/hpwdt.txt @@ -1,9 +1,9 @@ -Last reviewed: 04/04/2016 +Last reviewed: 05/20/2016 HPE iLO NMI Watchdog Driver NMI sourcing for iLO based ProLiant Servers Documentation and Driver by - Thomas Mingarelli + Thomas Mingarelli The HPE iLO NMI Watchdog driver is a kernel module that provides basic watchdog functionality and the added benefit of NMI sourcing. Both the @@ -95,4 +95,3 @@ Last reviewed: 04/04/2016 -- Tom Mingarelli - (thomas.mingarelli@hpe.com) From 214de83ec2be9309667baf181b14c603aeb962be Mon Sep 17 00:00:00 2001 From: Brian Boylston Date: Wed, 25 May 2016 11:19:54 -0500 Subject: [PATCH 03/50] MAINTAINERS: add entry for HPE watchdog driver Add a maintainer for the HPE watchdog driver Signed-off-by: Brian Boylston Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1d748377595f..139a5329ce9d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5304,6 +5304,12 @@ T: git git://linuxtv.org/anttip/media_tree.git S: Maintained F: drivers/media/dvb-frontends/hd29l2* +HEWLETT PACKARD ENTERPRISE ILO NMI WATCHDOG DRIVER +M: Brian Boylston +S: Supported +F: Documentation/watchdog/hpwdt.txt +F: drivers/watchdog/hpwdt.c + HEWLETT-PACKARD SMART ARRAY RAID DRIVER (hpsa) M: Don Brace L: iss_storagedev@hp.com From 9c963ba0e0bacfb13c41d3334630ff67044ac159 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2016 08:37:43 +0200 Subject: [PATCH 04/50] watchdog: softdog: remove obsolete comments The history is obsolete, especially since we switched to watchdog framework. The section markers also don't make sense anymore given the small size of the driver. Signed-off-by: Wolfram Sang Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/softdog.c | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index 99a06f9e3930..8bc0b164afc9 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -17,23 +17,6 @@ * * Software only watchdog driver. Unlike its big brother the WDT501P * driver this won't always recover a failed machine. - * - * 03/96: Angelo Haritsis : - * Modularised. - * Added soft_margin; use upon insmod to change the timer delay. - * NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate - * minors. - * - * 19980911 Alan Cox - * Made SMP safe for 2.3.x - * - * 20011127 Joel Becker (jlbec@evilplan.org> - * Added soft_noboot; Allows testing the softdog trigger without - * requiring a recompile. - * Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT. - * - * 20020530 Joel Becker - * Added Matt Domsch's nowayout module option. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -71,19 +54,11 @@ module_param(soft_panic, int, 0); MODULE_PARM_DESC(soft_panic, "Softdog action, set to 1 to panic, 0 to reboot (default=0)"); -/* - * Our timer - */ - static void watchdog_fire(unsigned long); static struct timer_list watchdog_ticktock = TIMER_INITIALIZER(watchdog_fire, 0, 0); -/* - * If the timer expires.. - */ - static void watchdog_fire(unsigned long data) { module_put(THIS_MODULE); @@ -99,10 +74,6 @@ static void watchdog_fire(unsigned long data) } } -/* - * Softdog operations - */ - static int softdog_ping(struct watchdog_device *w) { if (!mod_timer(&watchdog_ticktock, jiffies+(w->timeout*HZ))) @@ -124,10 +95,6 @@ static int softdog_set_timeout(struct watchdog_device *w, unsigned int t) return 0; } -/* - * Kernel Interfaces - */ - static struct watchdog_info softdog_info = { .identity = "Software Watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, From e8cf96abba977b6d66bfc2bbc5248a924c9edaec Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2016 08:37:44 +0200 Subject: [PATCH 05/50] watchdog: softdog: use watchdog core to init timeout value Error string and comment say we fall back to a default, but in reality we bailed out. Refactor the code to use the core helper which then matches the described behaviour. While updating the init message anyhow, shorten it while we are here; no need for versioning there as well and the name is already given via pr_fmt. Signed-off-by: Wolfram Sang Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/softdog.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index 8bc0b164afc9..a9ad27dd4650 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -111,22 +111,15 @@ static struct watchdog_device softdog_dev = { .info = &softdog_info, .ops = &softdog_ops, .min_timeout = 1, - .max_timeout = 0xFFFF + .max_timeout = 65535, + .timeout = TIMER_MARGIN, }; static int __init watchdog_init(void) { int ret; - /* Check that the soft_margin value is within it's range; - if not reset to the default */ - if (soft_margin < 1 || soft_margin > 65535) { - pr_info("soft_margin must be 0 < soft_margin < 65536, using %d\n", - TIMER_MARGIN); - return -EINVAL; - } - softdog_dev.timeout = soft_margin; - + watchdog_init_timeout(&softdog_dev, soft_margin, NULL); watchdog_set_nowayout(&softdog_dev, nowayout); watchdog_stop_on_reboot(&softdog_dev); @@ -134,8 +127,8 @@ static int __init watchdog_init(void) if (ret) return ret; - pr_info("Software Watchdog Timer: 0.08 initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n", - soft_noboot, soft_margin, soft_panic, nowayout); + pr_info("initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n", + soft_noboot, softdog_dev.timeout, soft_panic, nowayout); return 0; } From 0efc70b8238dc6d26289b501f126798b9d0da601 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2016 08:37:45 +0200 Subject: [PATCH 06/50] watchdog: softdog: consistently use softdog_ prefix And move module_init/exit to the proper place while here. Signed-off-by: Wolfram Sang Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/softdog.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index a9ad27dd4650..0a29f5a08337 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -54,12 +54,12 @@ module_param(soft_panic, int, 0); MODULE_PARM_DESC(soft_panic, "Softdog action, set to 1 to panic, 0 to reboot (default=0)"); -static void watchdog_fire(unsigned long); +static void softdog_fire(unsigned long); -static struct timer_list watchdog_ticktock = - TIMER_INITIALIZER(watchdog_fire, 0, 0); +static struct timer_list softdog_ticktock = + TIMER_INITIALIZER(softdog_fire, 0, 0); -static void watchdog_fire(unsigned long data) +static void softdog_fire(unsigned long data) { module_put(THIS_MODULE); if (soft_noboot) @@ -76,14 +76,14 @@ static void watchdog_fire(unsigned long data) static int softdog_ping(struct watchdog_device *w) { - if (!mod_timer(&watchdog_ticktock, jiffies+(w->timeout*HZ))) + if (!mod_timer(&softdog_ticktock, jiffies+(w->timeout*HZ))) __module_get(THIS_MODULE); return 0; } static int softdog_stop(struct watchdog_device *w) { - if (del_timer(&watchdog_ticktock)) + if (del_timer(&softdog_ticktock)) module_put(THIS_MODULE); return 0; @@ -115,7 +115,7 @@ static struct watchdog_device softdog_dev = { .timeout = TIMER_MARGIN, }; -static int __init watchdog_init(void) +static int __init softdog_init(void) { int ret; @@ -132,14 +132,13 @@ static int __init watchdog_init(void) return 0; } +module_init(softdog_init); -static void __exit watchdog_exit(void) +static void __exit softdog_exit(void) { watchdog_unregister_device(&softdog_dev); } - -module_init(watchdog_init); -module_exit(watchdog_exit); +module_exit(softdog_exit); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("Software Watchdog Device Driver"); From 44ba0f04af44d7f3a157ee655bdfc53660d8faf9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2016 08:37:46 +0200 Subject: [PATCH 07/50] watchdog: softdog: remove forward declaration Signed-off-by: Wolfram Sang Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/softdog.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index 0a29f5a08337..42faa3d424d5 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -54,11 +54,6 @@ module_param(soft_panic, int, 0); MODULE_PARM_DESC(soft_panic, "Softdog action, set to 1 to panic, 0 to reboot (default=0)"); -static void softdog_fire(unsigned long); - -static struct timer_list softdog_ticktock = - TIMER_INITIALIZER(softdog_fire, 0, 0); - static void softdog_fire(unsigned long data) { module_put(THIS_MODULE); @@ -74,6 +69,9 @@ static void softdog_fire(unsigned long data) } } +static struct timer_list softdog_ticktock = + TIMER_INITIALIZER(softdog_fire, 0, 0); + static int softdog_ping(struct watchdog_device *w) { if (!mod_timer(&softdog_ticktock, jiffies+(w->timeout*HZ))) From e65c5825c6ef850a26cd449157024bad6e2eec9c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2016 08:37:47 +0200 Subject: [PATCH 08/50] watchdog: softdog: sort includes to avoid duplicates Signed-off-by: Wolfram Sang Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/softdog.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index 42faa3d424d5..ab0e02fc81a2 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -21,15 +21,15 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include #define TIMER_MARGIN 60 /* Default is 60 seconds */ static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */ From 61a21274c9eee5a3adb85441a91a071de4260863 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2016 08:37:48 +0200 Subject: [PATCH 09/50] watchdog: softdog: drop superfluous set_timeout callback If we leave set_timeout empty, the core will do exactly what is implemented here anyway. Signed-off-by: Wolfram Sang Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/softdog.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index ab0e02fc81a2..5e3a30b99d44 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -87,12 +87,6 @@ static int softdog_stop(struct watchdog_device *w) return 0; } -static int softdog_set_timeout(struct watchdog_device *w, unsigned int t) -{ - w->timeout = t; - return 0; -} - static struct watchdog_info softdog_info = { .identity = "Software Watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, @@ -102,7 +96,6 @@ static struct watchdog_ops softdog_ops = { .owner = THIS_MODULE, .start = softdog_ping, .stop = softdog_stop, - .set_timeout = softdog_set_timeout, }; static struct watchdog_device softdog_dev = { From 4a23e2bf032fdceecef1ab314c7f84d9c2050490 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 25 May 2016 08:37:49 +0200 Subject: [PATCH 10/50] watchdog: softdog: improve coding style Signed-off-by: Wolfram Sang Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/softdog.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index 5e3a30b99d44..b067edf246df 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -57,9 +57,9 @@ MODULE_PARM_DESC(soft_panic, static void softdog_fire(unsigned long data) { module_put(THIS_MODULE); - if (soft_noboot) + if (soft_noboot) { pr_crit("Triggered - Reboot ignored\n"); - else if (soft_panic) { + } else if (soft_panic) { pr_crit("Initiating panic\n"); panic("Software Watchdog Timer expired"); } else { @@ -74,7 +74,7 @@ static struct timer_list softdog_ticktock = static int softdog_ping(struct watchdog_device *w) { - if (!mod_timer(&softdog_ticktock, jiffies+(w->timeout*HZ))) + if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ))) __module_get(THIS_MODULE); return 0; } From 0c9444cc9e031541694268e58d4b999a6ee258e3 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 18 May 2016 17:50:59 +0930 Subject: [PATCH 11/50] dt-bindings: watchdog: Add Aspeed watchdog timer bindings Signed-off-by: Joel Stanley Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../devicetree/bindings/watchdog/aspeed-wdt.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt diff --git a/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt b/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt new file mode 100644 index 000000000000..c5e74d7b4406 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt @@ -0,0 +1,16 @@ +Aspeed Watchdog Timer + +Required properties: + - compatible: must be one of: + - "aspeed,ast2400-wdt" + - "aspeed,ast2500-wdt" + + - reg: physical base address of the controller and length of memory mapped + region + +Example: + + wdt1: watchdog@1e785000 { + compatible = "aspeed,ast2400-wdt"; + reg = <0x1e785000 0x1c>; + }; From efa859f7d7860f73396c1ff28017e55b5b403e82 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 18 May 2016 17:51:00 +0930 Subject: [PATCH 12/50] watchdog: Add Aspeed watchdog driver Provides generic watchdog features as well as reboot support for the Aspeed SoCs. Signed-off-by: Joel Stanley Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 13 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/aspeed_wdt.c | 212 ++++++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 drivers/watchdog/aspeed_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b4b3e256491b..a12bf540c0b1 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -669,6 +669,19 @@ config RENESAS_WDT This driver adds watchdog support for the integrated watchdogs in the Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT). +config ASPEED_WATCHDOG + tristate "Aspeed 2400 watchdog support" + depends on ARCH_ASPEED || COMPILE_TEST + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + in Apseed BMC SoCs. + + This driver is required to reboot the SoC. + + To compile this driver as a module, choose M here: the + module will be called aspeed_wdt. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index a46e7c1380ac..4990c50b04e5 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o +obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c new file mode 100644 index 000000000000..f5ad8023c2e6 --- /dev/null +++ b/drivers/watchdog/aspeed_wdt.c @@ -0,0 +1,212 @@ +/* + * Copyright 2016 IBM Corporation + * + * Joel Stanley + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct aspeed_wdt { + struct watchdog_device wdd; + void __iomem *base; + u32 ctrl; +}; + +static const struct of_device_id aspeed_wdt_of_table[] = { + { .compatible = "aspeed,ast2400-wdt" }, + { .compatible = "aspeed,ast2500-wdt" }, + { }, +}; +MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table); + +#define WDT_STATUS 0x00 +#define WDT_RELOAD_VALUE 0x04 +#define WDT_RESTART 0x08 +#define WDT_CTRL 0x0C +#define WDT_CTRL_RESET_MODE_SOC (0x00 << 5) +#define WDT_CTRL_RESET_MODE_FULL_CHIP (0x01 << 5) +#define WDT_CTRL_1MHZ_CLK BIT(4) +#define WDT_CTRL_WDT_EXT BIT(3) +#define WDT_CTRL_WDT_INTR BIT(2) +#define WDT_CTRL_RESET_SYSTEM BIT(1) +#define WDT_CTRL_ENABLE BIT(0) + +#define WDT_RESTART_MAGIC 0x4755 + +/* 32 bits at 1MHz, in milliseconds */ +#define WDT_MAX_TIMEOUT_MS 4294967 +#define WDT_DEFAULT_TIMEOUT 30 +#define WDT_RATE_1MHZ 1000000 + +static struct aspeed_wdt *to_aspeed_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct aspeed_wdt, wdd); +} + +static void aspeed_wdt_enable(struct aspeed_wdt *wdt, int count) +{ + wdt->ctrl |= WDT_CTRL_ENABLE; + + writel(0, wdt->base + WDT_CTRL); + writel(count, wdt->base + WDT_RELOAD_VALUE); + writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART); + writel(wdt->ctrl, wdt->base + WDT_CTRL); +} + +static int aspeed_wdt_start(struct watchdog_device *wdd) +{ + struct aspeed_wdt *wdt = to_aspeed_wdt(wdd); + + aspeed_wdt_enable(wdt, wdd->timeout * WDT_RATE_1MHZ); + + return 0; +} + +static int aspeed_wdt_stop(struct watchdog_device *wdd) +{ + struct aspeed_wdt *wdt = to_aspeed_wdt(wdd); + + wdt->ctrl &= ~WDT_CTRL_ENABLE; + writel(wdt->ctrl, wdt->base + WDT_CTRL); + + return 0; +} + +static int aspeed_wdt_ping(struct watchdog_device *wdd) +{ + struct aspeed_wdt *wdt = to_aspeed_wdt(wdd); + + writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART); + + return 0; +} + +static int aspeed_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct aspeed_wdt *wdt = to_aspeed_wdt(wdd); + u32 actual; + + wdd->timeout = timeout; + + actual = min(timeout, wdd->max_hw_heartbeat_ms * 1000); + + writel(actual * WDT_RATE_1MHZ, wdt->base + WDT_RELOAD_VALUE); + writel(WDT_RESTART_MAGIC, wdt->base + WDT_RESTART); + + return 0; +} + +static int aspeed_wdt_restart(struct watchdog_device *wdd, + unsigned long action, void *data) +{ + struct aspeed_wdt *wdt = to_aspeed_wdt(wdd); + + aspeed_wdt_enable(wdt, 128 * WDT_RATE_1MHZ / 1000); + + mdelay(1000); + + return 0; +} + +static const struct watchdog_ops aspeed_wdt_ops = { + .start = aspeed_wdt_start, + .stop = aspeed_wdt_stop, + .ping = aspeed_wdt_ping, + .set_timeout = aspeed_wdt_set_timeout, + .restart = aspeed_wdt_restart, + .owner = THIS_MODULE, +}; + +static const struct watchdog_info aspeed_wdt_info = { + .options = WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE + | WDIOF_SETTIMEOUT, + .identity = KBUILD_MODNAME, +}; + +static int aspeed_wdt_remove(struct platform_device *pdev) +{ + struct aspeed_wdt *wdt = platform_get_drvdata(pdev); + + watchdog_unregister_device(&wdt->wdd); + + return 0; +} + +static int aspeed_wdt_probe(struct platform_device *pdev) +{ + struct aspeed_wdt *wdt; + struct resource *res; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); + + /* + * The ast2400 wdt can run at PCLK, or 1MHz. The ast2500 only + * runs at 1MHz. We chose to always run at 1MHz, as there's no + * good reason to have a faster watchdog counter. + */ + wdt->wdd.info = &aspeed_wdt_info; + wdt->wdd.ops = &aspeed_wdt_ops; + wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS; + wdt->wdd.parent = &pdev->dev; + + wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT; + watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); + + /* + * Control reset on a per-device basis to ensure the + * host is not affected by a BMC reboot, so only reset + * the SOC and not the full chip + */ + wdt->ctrl = WDT_CTRL_RESET_MODE_SOC | + WDT_CTRL_1MHZ_CLK | + WDT_CTRL_RESET_SYSTEM; + + if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE) { + aspeed_wdt_start(&wdt->wdd); + set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); + } + + ret = watchdog_register_device(&wdt->wdd); + if (ret) { + dev_err(&pdev->dev, "failed to register\n"); + return ret; + } + + platform_set_drvdata(pdev, wdt); + + return 0; +} + +static struct platform_driver aspeed_watchdog_driver = { + .probe = aspeed_wdt_probe, + .remove = aspeed_wdt_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = of_match_ptr(aspeed_wdt_of_table), + }, +}; +module_platform_driver(aspeed_watchdog_driver); + +MODULE_DESCRIPTION("Aspeed Watchdog Driver"); +MODULE_LICENSE("GPL"); From b7a8c420f31eaf521f66f093a1a00e92718dd193 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 1 Jun 2016 09:05:48 -0400 Subject: [PATCH 13/50] watchdog: pcwd: Utilize the module_isa_driver macro This driver does not do anything special in module init/exit. This patch eliminates the module init/exit boilerplate code by utilizing the module_isa_driver macro. Signed-off-by: William Breathitt Gray Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pcwd.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c index e936f15dc7c7..3ad5206d7935 100644 --- a/drivers/watchdog/pcwd.c +++ b/drivers/watchdog/pcwd.c @@ -992,19 +992,7 @@ static struct isa_driver pcwd_isa_driver = { }, }; -static int __init pcwd_init_module(void) -{ - return isa_register_driver(&pcwd_isa_driver, PCWD_ISA_NR_CARDS); -} - -static void __exit pcwd_cleanup_module(void) -{ - isa_unregister_driver(&pcwd_isa_driver); - pr_info("Watchdog Module Unloaded\n"); -} - -module_init(pcwd_init_module); -module_exit(pcwd_cleanup_module); +module_isa_driver(pcwd_isa_driver, PCWD_ISA_NR_CARDS); MODULE_AUTHOR("Ken Hollis , " "Wim Van Sebroeck "); From 1894cad9bf2c10359b2b7a0c00e564698f712751 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Tue, 31 May 2016 14:08:08 +0800 Subject: [PATCH 14/50] watchdog: skip min and max timeout validity check when max_hw_heartbeat_ms is defined When max_hw_heartbeat_ms has a none zero value, max_timeout is not used. So it's value can be 0. In such case if a driver uses min_timeout functionality, then check will always fail. This patch fixes above issue. Signed-off-by: Pratyush Anand Signed-off-by: Fu Wei Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index f4f02d2763ec..6abb83cd7681 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -88,7 +88,7 @@ static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) * Check that we have valid min and max timeout values, if * not reset them both to 0 (=not used or unknown) */ - if (wdd->min_timeout > wdd->max_timeout) { + if (!wdd->max_hw_heartbeat_ms && wdd->min_timeout > wdd->max_timeout) { pr_info("Invalid min and max timeout values, resetting to 0!\n"); wdd->min_timeout = 0; wdd->max_timeout = 0; From e05e80eb58cc9679d0ff8de5af095a66ba855e10 Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Tue, 31 May 2016 14:08:09 +0800 Subject: [PATCH 15/50] watchdog: sbsa: Use max_hw_heartbeat_ms instead of max_timeout Using max_hw_heartbeat_ms instead of max_timeout gives the flexibility to achieve higher user "timeout". Therefore, use this new infrastructure. Signed-off-by: Pratyush Anand Signed-off-by: Fu Wei Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sbsa_gwdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index ad383f6f15fc..6af71e07f309 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -273,7 +273,7 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) wdd->info = &sbsa_gwdt_info; wdd->ops = &sbsa_gwdt_ops; wdd->min_timeout = 1; - wdd->max_timeout = U32_MAX / gwdt->clk; + wdd->max_hw_heartbeat_ms = U32_MAX / gwdt->clk * 1000; wdd->timeout = DEFAULT_TIMEOUT; watchdog_set_drvdata(wdd, gwdt); watchdog_set_nowayout(wdd, nowayout); @@ -310,7 +310,7 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) * the timeout is (WOR * 2), so the maximum timeout should be doubled. */ if (!action) - wdd->max_timeout *= 2; + wdd->max_hw_heartbeat_ms *= 2; watchdog_init_timeout(wdd, timeout, dev); /* From c3c1e29c8ad11dec67d01203759272da0d0df3aa Mon Sep 17 00:00:00 2001 From: Pratyush Anand Date: Tue, 31 May 2016 14:08:10 +0800 Subject: [PATCH 16/50] watchdog: sbsa: Set WDOG_HW_RUNNING, when watchdog is already running. This patch uses the new flag WDOG_HW_RUNNING in driver. According to the definition of this flag, it should be set, if watchdog is running after booting, before it's opened. Signed-off-by: Pratyush Anand Signed-off-by: Fu Wei Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sbsa_gwdt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index 6af71e07f309..cc885f1c1850 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -283,6 +283,8 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) dev_warn(dev, "System reset by WDT.\n"); wdd->bootstatus |= WDIOF_CARDRESET; } + if (status & SBSA_GWDT_WCS_EN) + set_bit(WDOG_HW_RUNNING, &wdd->status); if (action) { irq = platform_get_irq(pdev, 0); From f9f535c1b76128f94dae7a4bb41464aacd4c10ec Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 27 May 2016 14:19:06 -0700 Subject: [PATCH 17/50] watchdog: Improve description of min_hw_heartbeat_ms The description of min_hw_heartbeat_ms is misleading and needs some improvements. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/watchdog-kernel-api.txt | 5 +++-- include/linux/watchdog.h | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 917eeeabfa5e..92700d229bf9 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -82,8 +82,9 @@ It contains following fields: * max_timeout: the watchdog timer's maximum timeout value (in seconds), as seen from userspace. If set, the maximum configurable value for 'timeout'. Not used if max_hw_heartbeat_ms is non-zero. -* min_hw_heartbeat_ms: Minimum time between heartbeats sent to the chip, - in milli-seconds. +* min_hw_heartbeat_ms: Hardware limit for minimum time between heartbeats, + in milli-seconds. This value is normally 0; it should only be provided + if the hardware can not tolerate lower intervals between heartbeats. * max_hw_heartbeat_ms: Maximum hardware heartbeat, in milli-seconds. If set, the infrastructure will send heartbeats to the watchdog driver if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 6b75e38b683f..7047bc7f8106 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -66,7 +66,8 @@ struct watchdog_ops { * as configurable from user space. Only relevant if * max_hw_heartbeat_ms is not provided. * @min_hw_heartbeat_ms: - * Minimum time between heartbeats, in milli-seconds. + * Hardware limit for minimum time between heartbeats, + * in milli-seconds. * @max_hw_heartbeat_ms: * Hardware limit for maximum timeout, in milli-seconds. * Replaces max_timeout if specified. From a3e376d26ace6d823f7aa4c63476e7089a6196df Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 27 May 2016 15:28:00 -0700 Subject: [PATCH 18/50] watchdog: tangox: Mark running watchdog correctly A running watchdog is marked with WDOG_HW_RUNNING, not with WDOG_ACTIVE. WDOG_ACTIVE indicates that the watchdog device has been opened from user space. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/tangox_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c index cfbed7e051b6..e7a5d0fc81d4 100644 --- a/drivers/watchdog/tangox_wdt.c +++ b/drivers/watchdog/tangox_wdt.c @@ -170,7 +170,7 @@ static int tangox_wdt_probe(struct platform_device *pdev) * already running. */ if (readl(dev->base + WD_COUNTER)) { - set_bit(WDOG_ACTIVE, &dev->wdt.status); + set_bit(WDOG_HW_RUNNING, &dev->wdt.status); tangox_wdt_start(&dev->wdt); } From b99c87740541f04cec056978452d7913da6ac9a5 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 7 Jun 2016 12:53:07 +0100 Subject: [PATCH 19/50] watchdog: sirf: fix __iomem * warnings Fix the following warnings from sparse due to casting to/from an __iomem annotated variable: drivers/watchdog/sirfsoc_wdt.c:48:18: warning: incorrect type in assignment (different address spaces) drivers/watchdog/sirfsoc_wdt.c:48:18: expected void [noderef] *wdt_base drivers/watchdog/sirfsoc_wdt.c:48:18: got void * drivers/watchdog/sirfsoc_wdt.c:64:18: warning: incorrect type in assignment (different address spaces) drivers/watchdog/sirfsoc_wdt.c:64:18: expected void [noderef] *wdt_base drivers/watchdog/sirfsoc_wdt.c:64:18: got void * drivers/watchdog/sirfsoc_wdt.c:82:54: warning: incorrect type in initializer (different address spaces) drivers/watchdog/sirfsoc_wdt.c:82:54: expected void [noderef] *wdt_base drivers/watchdog/sirfsoc_wdt.c:82:54: got void * drivers/watchdog/sirfsoc_wdt.c:99:54: warning: incorrect type in initializer (different address spaces) drivers/watchdog/sirfsoc_wdt.c:99:54: expected void [noderef] *wdt_base drivers/watchdog/sirfsoc_wdt.c:99:54: got void * drivers/watchdog/sirfsoc_wdt.c:153:44: warning: incorrect type in argument 2 (different address spaces) drivers/watchdog/sirfsoc_wdt.c:153:44: expected void *data drivers/watchdog/sirfsoc_wdt.c:153:44: got void [noderef] *[assigned] base Signed-off-by: Ben Dooks Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sirfsoc_wdt.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c index d0578ab2e636..3050a0031479 100644 --- a/drivers/watchdog/sirfsoc_wdt.c +++ b/drivers/watchdog/sirfsoc_wdt.c @@ -39,13 +39,18 @@ MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)"); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +static void __iomem *sirfsoc_wdt_base(struct watchdog_device *wdd) +{ + return (void __iomem __force *)watchdog_get_drvdata(wdd); +} + static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd) { u32 counter, match; void __iomem *wdt_base; int time_left; - wdt_base = watchdog_get_drvdata(wdd); + wdt_base = sirfsoc_wdt_base(wdd); counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO); match = readl(wdt_base + SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2)); @@ -61,7 +66,7 @@ static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd) void __iomem *wdt_base; timeout_ticks = wdd->timeout * CLOCK_FREQ; - wdt_base = watchdog_get_drvdata(wdd); + wdt_base = sirfsoc_wdt_base(wdd); /* Enable the latch before reading the LATCH_LO register */ writel(1, wdt_base + SIRFSOC_TIMER_LATCH); @@ -79,7 +84,7 @@ static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd) static int sirfsoc_wdt_enable(struct watchdog_device *wdd) { - void __iomem *wdt_base = watchdog_get_drvdata(wdd); + void __iomem *wdt_base = sirfsoc_wdt_base(wdd); sirfsoc_wdt_updatetimeout(wdd); /* @@ -96,7 +101,7 @@ static int sirfsoc_wdt_enable(struct watchdog_device *wdd) static int sirfsoc_wdt_disable(struct watchdog_device *wdd) { - void __iomem *wdt_base = watchdog_get_drvdata(wdd); + void __iomem *wdt_base = sirfsoc_wdt_base(wdd); writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN); writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN) @@ -150,7 +155,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - watchdog_set_drvdata(&sirfsoc_wdd, base); + watchdog_set_drvdata(&sirfsoc_wdd, (__force void *)base); watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev); watchdog_set_nowayout(&sirfsoc_wdd, nowayout); From 14b24a88a36602c2c4e3bbb955be1aa9378c3be2 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Wed, 8 Jun 2016 14:57:50 +0800 Subject: [PATCH 20/50] watchdog: f71808e_wdt: Add F81866 support Adds watchdog enable support for Fintek F81866 Super-IO chip to Fintek wdt driver (f71808e_wdt) Tested and verified on iBASE MI802 Industrial PC Datasheet references: http://www.alldatasheet.com/datasheet-pdf/pdf/459085/FINTEK/F81866AD-I.html Suggested-by: Guenter Roeck Signed-off-by: Ji-Ze Hong (Peter Hong) Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index d4ba262da7ba..1b7e9169072f 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -45,9 +45,11 @@ #define SIO_REG_DEVREV 0x22 /* Device revision */ #define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */ #define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */ +#define SIO_F81866_REG_PORT_SEL 0x27 /* F81866 Multi-Function Register */ #define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */ #define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */ #define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */ +#define SIO_F81866_REG_GPIO1 0x2c /* F81866 GPIO1 Enable Register */ #define SIO_REG_ENABLE 0x30 /* Logical device enable */ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ @@ -60,6 +62,7 @@ #define SIO_F71882_ID 0x0541 /* Chipset ID */ #define SIO_F71889_ID 0x0723 /* Chipset ID */ #define SIO_F81865_ID 0x0704 /* Chipset ID */ +#define SIO_F81866_ID 0x1010 /* Chipset ID */ #define F71808FG_REG_WDO_CONF 0xf0 #define F71808FG_REG_WDT_CONF 0xf5 @@ -116,7 +119,8 @@ module_param(start_withtimeout, uint, 0); MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with" " given initial timeout. Zero (default) disables this feature."); -enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg, f81865 }; +enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg, f81865, + f81866}; static const char *f71808e_names[] = { "f71808fg", @@ -126,6 +130,7 @@ static const char *f71808e_names[] = { "f71882fg", "f71889fg", "f81865", + "f81866", }; /* Super-I/O Function prototypes */ @@ -370,6 +375,22 @@ static int watchdog_start(void) superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5); break; + case f81866: + /* Set pin 70 to WDTRST# */ + superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, + BIT(3) | BIT(0)); + superio_set_bit(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, + BIT(2)); + /* + * GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0. + * The PIN 70(GPIO15/WDTRST) is controlled by 2Ch: + * BIT5: 0 -> WDTRST# + * 1 -> GPIO15 + */ + superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_GPIO1, + BIT(5)); + break; + default: /* * 'default' label to shut up the compiler and catch @@ -382,7 +403,7 @@ static int watchdog_start(void) superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0); - if (watchdog.type == f81865) + if (watchdog.type == f81865 || watchdog.type == f81866) superio_set_bit(watchdog.sioaddr, F81865_REG_WDO_CONF, F81865_FLAG_WDOUT_EN); else @@ -788,6 +809,9 @@ static int __init f71808e_find(int sioaddr) case SIO_F81865_ID: watchdog.type = f81865; break; + case SIO_F81866_ID: + watchdog.type = f81866; + break; default: pr_info("Unrecognized Fintek device: %04x\n", (unsigned int)devid); From ff3bb2f5c3cd70f4dd642525916f300bffaff0ae Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 9 Jun 2016 18:59:05 +0530 Subject: [PATCH 21/50] watchdog: max77620: Add support for watchdog timer Maxim PMIC MAX77620 is Power management IC which have multiple sub blocks like regulators (DCDC/LDOs), GPIO, RTC, Clock, Watchdog timer etc. Add the driver for watchdog timer under watchdog framework. The driver implements the watchdog callbacks to start, stop, ping and set timeout for watchodg framework. Signed-off-by: Laxman Dewangan Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 9 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/max77620_wdt.c | 227 ++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 drivers/watchdog/max77620_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index a12bf540c0b1..65058d659d52 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -516,6 +516,15 @@ config MAX63XX_WATCHDOG help Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. +config MAX77620_WATCHDOG + tristate "Maxim Max77620 Watchdog Timer" + depends on MFD_MAX77620 + help + This is the driver for the Max77620 watchdog timer. + Say 'Y' here to enable the watchdog timer support for + MAX77620 chips. To compile this driver as a module, + choose M here: the module will be called max77620_wdt. + config IMX2_WDT tristate "IMX2+ Watchdog" depends on ARCH_MXC || ARCH_LAYERSCAPE diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 4990c50b04e5..3c0ed73b2cab 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -204,6 +204,7 @@ obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o +obj-$(CONFIG_MAX77620_WATCHDOG) += max77620_wdt.o obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o diff --git a/drivers/watchdog/max77620_wdt.c b/drivers/watchdog/max77620_wdt.c new file mode 100644 index 000000000000..48b84df2afda --- /dev/null +++ b/drivers/watchdog/max77620_wdt.c @@ -0,0 +1,227 @@ +/* + * Maxim MAX77620 Watchdog Driver + * + * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool nowayout = WATCHDOG_NOWAYOUT; + +struct max77620_wdt { + struct device *dev; + struct regmap *rmap; + struct watchdog_device wdt_dev; +}; + +static int max77620_wdt_start(struct watchdog_device *wdt_dev) +{ + struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); + + return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, + MAX77620_WDTEN, MAX77620_WDTEN); +} + +static int max77620_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); + + return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, + MAX77620_WDTEN, 0); +} + +static int max77620_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); + + return regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3, + MAX77620_WDTC_MASK, 0x1); +} + +static int max77620_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct max77620_wdt *wdt = watchdog_get_drvdata(wdt_dev); + unsigned int wdt_timeout; + u8 regval; + int ret; + + switch (timeout) { + case 0 ... 2: + regval = MAX77620_TWD_2s; + wdt_timeout = 2; + break; + + case 3 ... 16: + regval = MAX77620_TWD_16s; + wdt_timeout = 16; + break; + + case 17 ... 64: + regval = MAX77620_TWD_64s; + wdt_timeout = 64; + break; + + default: + regval = MAX77620_TWD_128s; + wdt_timeout = 128; + break; + } + + ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL3, + MAX77620_WDTC_MASK, 0x1); + if (ret < 0) + return ret; + + ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, + MAX77620_TWD_MASK, regval); + if (ret < 0) + return ret; + + wdt_dev->timeout = wdt_timeout; + + return 0; +} + +static const struct watchdog_info max77620_wdt_info = { + .identity = "max77620-watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, +}; + +static const struct watchdog_ops max77620_wdt_ops = { + .start = max77620_wdt_start, + .stop = max77620_wdt_stop, + .ping = max77620_wdt_ping, + .set_timeout = max77620_wdt_set_timeout, +}; + +static int max77620_wdt_probe(struct platform_device *pdev) +{ + struct max77620_wdt *wdt; + struct watchdog_device *wdt_dev; + unsigned int regval; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->dev = &pdev->dev; + wdt->rmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!wdt->rmap) { + dev_err(wdt->dev, "Failed to get parent regmap\n"); + return -ENODEV; + } + + wdt_dev = &wdt->wdt_dev; + wdt_dev->info = &max77620_wdt_info; + wdt_dev->ops = &max77620_wdt_ops; + wdt_dev->min_timeout = 2; + wdt_dev->max_timeout = 128; + wdt_dev->max_hw_heartbeat_ms = 128 * 1000; + + platform_set_drvdata(pdev, wdt); + + /* Enable WD_RST_WK - WDT expire results in a restart */ + ret = regmap_update_bits(wdt->rmap, MAX77620_REG_ONOFFCNFG2, + MAX77620_ONOFFCNFG2_WD_RST_WK, + MAX77620_ONOFFCNFG2_WD_RST_WK); + if (ret < 0) { + dev_err(wdt->dev, "Failed to set WD_RST_WK: %d\n", ret); + return ret; + } + + /* Set WDT clear in OFF and sleep mode */ + ret = regmap_update_bits(wdt->rmap, MAX77620_REG_CNFGGLBL2, + MAX77620_WDTOFFC | MAX77620_WDTSLPC, + MAX77620_WDTOFFC | MAX77620_WDTSLPC); + if (ret < 0) { + dev_err(wdt->dev, "Failed to set WDT OFF mode: %d\n", ret); + return ret; + } + + /* Check if WDT running and if yes then set flags properly */ + ret = regmap_read(wdt->rmap, MAX77620_REG_CNFGGLBL2, ®val); + if (ret < 0) { + dev_err(wdt->dev, "Failed to read WDT CFG register: %d\n", ret); + return ret; + } + + switch (regval & MAX77620_TWD_MASK) { + case MAX77620_TWD_2s: + wdt_dev->timeout = 2; + break; + case MAX77620_TWD_16s: + wdt_dev->timeout = 16; + break; + case MAX77620_TWD_64s: + wdt_dev->timeout = 64; + break; + default: + wdt_dev->timeout = 128; + break; + } + + if (regval & MAX77620_WDTEN) + set_bit(WDOG_HW_RUNNING, &wdt_dev->status); + + watchdog_set_nowayout(wdt_dev, nowayout); + watchdog_set_drvdata(wdt_dev, wdt); + + ret = watchdog_register_device(wdt_dev); + if (ret < 0) { + dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int max77620_wdt_remove(struct platform_device *pdev) +{ + struct max77620_wdt *wdt = platform_get_drvdata(pdev); + + max77620_wdt_stop(&wdt->wdt_dev); + watchdog_unregister_device(&wdt->wdt_dev); + + return 0; +} + +static struct platform_device_id max77620_wdt_devtype[] = { + { .name = "max77620-watchdog", }, + { }, +}; + +static struct platform_driver max77620_wdt_driver = { + .driver = { + .name = "max77620-watchdog", + }, + .probe = max77620_wdt_probe, + .remove = max77620_wdt_remove, + .id_table = max77620_wdt_devtype, +}; + +module_platform_driver(max77620_wdt_driver); + +MODULE_DESCRIPTION("Max77620 watchdog timer driver"); + +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " + "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); From ff9cc8384a3efba7e3daedf66dfd58b0a0fff069 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Wed, 8 Jun 2016 18:16:15 +0300 Subject: [PATCH 22/50] watchdog: drop redundant 'default n' option for WATCHDOG_SYSFS The option 'default n' and its absence are equal for kbuild, which makes explicit 'default n' redundant. Signed-off-by: Vladimir Zapolskiy Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 65058d659d52..c19b11a91cde 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -48,7 +48,6 @@ config WATCHDOG_NOWAYOUT 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. From 22daf7a71683185db6bcb089e006be5f99dddf9c Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Sat, 9 Jul 2016 11:43:19 +0200 Subject: [PATCH 23/50] watchdog: ziirave_wdt: Correct I2C device id to fix module autoloading. The I2C core removes the manufacturer prefix from the compatible field so it reports to user-space the uevent i2c:rave-wdt, but this doesn't match with the i2c_device_id (i2c:ziirave-wdt) array so the module is not autoloaded. Correct the I2C device id to match with the reported uevent and fix the module autoloading functionality. Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/ziirave_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index cbe373de3659..fa1efef3c96e 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c @@ -339,7 +339,7 @@ static int ziirave_wdt_remove(struct i2c_client *client) } static struct i2c_device_id ziirave_wdt_id[] = { - { "ziirave-wdt", 0 }, + { "rave-wdt", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id); From db6d2d0e6d24176f524359bff2f209f0c8425496 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 17 Jun 2016 17:11:35 +0000 Subject: [PATCH 24/50] watchdog: pic32-wdt: Fix return value check in pic32_wdt_drv_probe() In case of error, the function devm_kzalloc() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pic32-wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/pic32-wdt.c b/drivers/watchdog/pic32-wdt.c index 6047aa89a4d3..2b7a2b22fcb2 100644 --- a/drivers/watchdog/pic32-wdt.c +++ b/drivers/watchdog/pic32-wdt.c @@ -174,8 +174,8 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev) struct resource *mem; wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); - if (IS_ERR(wdt)) - return PTR_ERR(wdt); + if (!wdt) + return -ENOMEM; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); wdt->regs = devm_ioremap_resource(&pdev->dev, mem); From fafdbabaf5c6a949168b384e159152f6e37427ba Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 17 Jun 2016 17:12:05 +0000 Subject: [PATCH 25/50] watchdog: pic32-dmt: Fix return value check in pic32_dmt_probe() In case of error, the function devm_kzalloc() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pic32-dmt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/pic32-dmt.c b/drivers/watchdog/pic32-dmt.c index 962f58c03353..5e8890eaa49f 100644 --- a/drivers/watchdog/pic32-dmt.c +++ b/drivers/watchdog/pic32-dmt.c @@ -176,8 +176,8 @@ static int pic32_dmt_probe(struct platform_device *pdev) struct watchdog_device *wdd = &pic32_dmt_wdd; dmt = devm_kzalloc(&pdev->dev, sizeof(*dmt), GFP_KERNEL); - if (IS_ERR(dmt)) - return PTR_ERR(dmt); + if (!dmt) + return -ENOMEM; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); dmt->regs = devm_ioremap_resource(&pdev->dev, mem); From 3b3a1c8fbb36f8e077b6ac09bcac03024d5bfa5c Mon Sep 17 00:00:00 2001 From: "Yong, Jonathan" Date: Fri, 17 Jun 2016 00:33:31 +0000 Subject: [PATCH 26/50] watchdog: iTCO-wdt: handle 5th variation for Apollo Lake The Apollo Lake Watchdog has the no_reboot flag in the 4th bit. Signed-off-by: Yong, Jonathan Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/iTCO_wdt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 0acc6c5f729d..54cab189a763 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -150,6 +150,7 @@ static inline u32 no_reboot_bit(void) u32 enable_bit; switch (iTCO_wdt_private.iTCO_version) { + case 5: case 3: enable_bit = 0x00000010; break; @@ -512,6 +513,7 @@ static int iTCO_wdt_probe(struct platform_device *dev) /* Clear out the (probably old) status */ switch (iTCO_wdt_private.iTCO_version) { + case 5: case 4: outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ From 334da2d6753ba548851c6329c4b6e74eb731368a Mon Sep 17 00:00:00 2001 From: "Yong, Jonathan" Date: Fri, 17 Jun 2016 00:33:32 +0000 Subject: [PATCH 27/50] x86: Fix Apollo Lake Watchdog address in PMC driver The TCO I/O base is 40h rather than the usual 30h, and the re_reboot bit is at ACPIBASE+8. Signed-off-by: Yong, Jonathan Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/platform/x86/intel_pmc_ipc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index 6f497e80c9df..b86e1bcaa055 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -85,7 +85,7 @@ * platform device and to export resources for those functions. */ #define TCO_DEVICE_NAME "iTCO_wdt" -#define SMI_EN_OFFSET 0x30 +#define SMI_EN_OFFSET 0x40 #define SMI_EN_SIZE 4 #define TCO_BASE_OFFSET 0x60 #define TCO_REGS_SIZE 16 @@ -94,6 +94,8 @@ #define TELEM_SSRAM_SIZE 240 #define TELEM_PMC_SSRAM_OFFSET 0x1B00 #define TELEM_PUNIT_SSRAM_OFFSET 0x1A00 +#define TCO_PMC_OFFSET 0x8 +#define TCO_PMC_SIZE 0x4 static const int iTCO_version = 3; @@ -502,7 +504,7 @@ static struct resource tco_res[] = { static struct itco_wdt_platform_data tco_info = { .name = "Apollo Lake SoC", - .version = 3, + .version = 5, }; #define TELEMETRY_RESOURCE_PUNIT_SSRAM 0 @@ -572,8 +574,8 @@ static int ipc_create_tco_device(void) res->end = res->start + SMI_EN_SIZE - 1; res = tco_res + TCO_RESOURCE_GCR_MEM; - res->start = ipcdev.gcr_base; - res->end = res->start + ipcdev.gcr_size - 1; + res->start = ipcdev.gcr_base + TCO_PMC_OFFSET; + res->end = res->start + TCO_PMC_SIZE - 1; ret = platform_device_add_resources(pdev, tco_res, ARRAY_SIZE(tco_res)); if (ret) { From ee279c2734b07fb74031b1626b0f097cee9de5c8 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 21 Jun 2016 18:00:14 -0500 Subject: [PATCH 28/50] Documentation/watchdog: use stdout instead of stderr in watchdog-test The watchdog-test utility outputs all messages to stderr, even those that are not error messages. Output to stdout instead. Instead of flushing the output after every write, just disabled the output buffer. Also display a dot for every ping of the watchdog, so that the user knows that it's working. Signed-off-by: Timur Tabi Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/src/watchdog-test.c | 28 ++++++++++------------ 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/Documentation/watchdog/src/watchdog-test.c b/Documentation/watchdog/src/watchdog-test.c index fcdde8fc98be..b3cc7aa12a72 100644 --- a/Documentation/watchdog/src/watchdog-test.c +++ b/Documentation/watchdog/src/watchdog-test.c @@ -23,6 +23,7 @@ static void keep_alive(void) { int dummy; + printf("."); ioctl(fd, WDIOC_KEEPALIVE, &dummy); } @@ -34,7 +35,7 @@ static void keep_alive(void) static void term(int sig) { close(fd); - fprintf(stderr, "Stopping watchdog ticks...\n"); + printf("\nStopping watchdog ticks...\n"); exit(0); } @@ -43,11 +44,12 @@ int main(int argc, char *argv[]) int flags; unsigned int ping_rate = 1; + setbuf(stdout, NULL); + fd = open("/dev/watchdog", O_WRONLY); if (fd == -1) { - fprintf(stderr, "Watchdog device not enabled.\n"); - fflush(stderr); + printf("Watchdog device not enabled.\n"); exit(-1); } @@ -55,36 +57,30 @@ int main(int argc, char *argv[]) if (!strncasecmp(argv[1], "-d", 2)) { flags = WDIOS_DISABLECARD; ioctl(fd, WDIOC_SETOPTIONS, &flags); - fprintf(stderr, "Watchdog card disabled.\n"); - fflush(stderr); + printf("Watchdog card disabled.\n"); goto end; } else if (!strncasecmp(argv[1], "-e", 2)) { flags = WDIOS_ENABLECARD; ioctl(fd, WDIOC_SETOPTIONS, &flags); - fprintf(stderr, "Watchdog card enabled.\n"); - fflush(stderr); + printf("Watchdog card enabled.\n"); goto end; } else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) { flags = atoi(argv[2]); ioctl(fd, WDIOC_SETTIMEOUT, &flags); - fprintf(stderr, "Watchdog timeout set to %u seconds.\n", flags); - fflush(stderr); + printf("Watchdog timeout set to %u seconds.\n", flags); goto end; } else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) { ping_rate = strtoul(argv[2], NULL, 0); - fprintf(stderr, "Watchdog ping rate set to %u seconds.\n", ping_rate); - fflush(stderr); + printf("Watchdog ping rate set to %u seconds.\n", ping_rate); } else { - fprintf(stderr, "-d to disable, -e to enable, -t to set " \ + printf("-d to disable, -e to enable, -t to set " \ "the timeout,\n-p to set the ping rate, and \n"); - fprintf(stderr, "run by itself to tick the card.\n"); - fflush(stderr); + printf("run by itself to tick the card.\n"); goto end; } } - fprintf(stderr, "Watchdog Ticking Away!\n"); - fflush(stderr); + printf("Watchdog Ticking Away!\n"); signal(SIGINT, term); From 5a2d3de1960260f34da84ac27bdc1345b541f2e8 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 21 Jun 2016 18:00:15 -0500 Subject: [PATCH 29/50] Documentation/watchdog: add support for magic close to watchdog-test Some drivers have the WDIOF_MAGICCLOSE set, which means that applications need to write 'V' to the watchdog device before closing, otherwise the driver won't stop the watchdog timer. Signed-off-by: Timur Tabi Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/src/watchdog-test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/watchdog/src/watchdog-test.c b/Documentation/watchdog/src/watchdog-test.c index b3cc7aa12a72..c69153913722 100644 --- a/Documentation/watchdog/src/watchdog-test.c +++ b/Documentation/watchdog/src/watchdog-test.c @@ -13,6 +13,7 @@ #include int fd; +const char v = 'V'; /* * This function simply sends an IOCTL to the driver, which in turn ticks @@ -34,6 +35,7 @@ static void keep_alive(void) static void term(int sig) { + write(fd, &v, 1); close(fd); printf("\nStopping watchdog ticks...\n"); exit(0); @@ -89,6 +91,7 @@ int main(int argc, char *argv[]) sleep(ping_rate); } end: + write(fd, &v, 1); close(fd); return 0; } From a74cab40b5dc51b5ed042add1c5d2f5e9a1964bc Mon Sep 17 00:00:00 2001 From: Stefan Christ Date: Wed, 6 Jul 2016 10:40:11 +0200 Subject: [PATCH 30/50] watchdog: da9063_wdt: don't trigger watchdog too fast Triggering the watchdog faster than T_WDMIN=256ms leads to resets of the DA9063 chip. The datasheet says that the watchdog must only be triggered in the timeframe T_WDMIN to T_WDMAX. The T_WDMAX is configured in the driver. Signed-off-by: Stefan Christ Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/da9063_wdt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index a100f648880d..5d6b4e5f7989 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c @@ -34,6 +34,7 @@ static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; #define DA9063_WDT_MIN_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MIN] #define DA9063_WDT_MAX_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MAX] #define DA9063_WDG_TIMEOUT wdt_timeout[3] +#define DA9063_RESET_PROTECTION_MS 256 struct da9063_watchdog { struct da9063 *da9063; @@ -171,6 +172,7 @@ static int da9063_wdt_probe(struct platform_device *pdev) wdt->wdtdev.ops = &da9063_watchdog_ops; wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT; wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT; + wdt->wdtdev.min_hw_heartbeat_ms = DA9063_RESET_PROTECTION_MS; wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT; wdt->wdtdev.parent = &pdev->dev; From 6e062696d7ffa4e574f74514906e04508917c876 Mon Sep 17 00:00:00 2001 From: Matthew McClintock Date: Wed, 29 Jun 2016 10:50:00 -0700 Subject: [PATCH 31/50] watchdog: qcom: update device tree bindings Update the compatible string to align with driver and also add SoC specific string to DTS. CC: linux-watchdog@vger.kernel.org Signed-off-by: Matthew McClintock Signed-off-by: Thomas Pedersen Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/qcom-wdt.txt | 2 ++ arch/arm/boot/dts/qcom-apq8064.dtsi | 3 ++- arch/arm/boot/dts/qcom-ipq8064.dtsi | 3 ++- arch/arm/boot/dts/qcom-msm8960.dtsi | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt b/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt index 4726924d034e..6b7b5cebcc53 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt @@ -7,6 +7,8 @@ Required properties : "qcom,kpss-wdt-msm8960" "qcom,kpss-wdt-apq8064" "qcom,kpss-wdt-ipq8064" + "qcom,kpss-timer" + "qcom,scss-timer" - reg : shall contain base register location and length - clocks : shall contain the input clock diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi index df96ccdc9bb4..e318d04319a0 100644 --- a/arch/arm/boot/dts/qcom-apq8064.dtsi +++ b/arch/arm/boot/dts/qcom-apq8064.dtsi @@ -247,7 +247,8 @@ }; timer@200a000 { - compatible = "qcom,kpss-timer", "qcom,msm-timer"; + compatible = "qcom,kpss-timer", + "qcom,kpss-wdt-apq8064", "qcom,msm-timer"; interrupts = <1 1 0x301>, <1 2 0x301>, <1 3 0x301>; diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi index 2601a907947b..2e375576ffd0 100644 --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi @@ -122,7 +122,8 @@ }; timer@200a000 { - compatible = "qcom,kpss-timer", "qcom,msm-timer"; + compatible = "qcom,kpss-timer", + "qcom,kpss-wdt-ipq8064", "qcom,msm-timer"; interrupts = <1 1 0x301>, <1 2 0x301>, <1 3 0x301>, diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi index da05e28a81a7..288f56e0ccf5 100644 --- a/arch/arm/boot/dts/qcom-msm8960.dtsi +++ b/arch/arm/boot/dts/qcom-msm8960.dtsi @@ -87,7 +87,8 @@ }; timer@200a000 { - compatible = "qcom,kpss-timer", "qcom,msm-timer"; + compatible = "qcom,kpss-timer", + "qcom,kpss-wdt-msm8960", "qcom,msm-timer"; interrupts = <1 1 0x301>, <1 2 0x301>, <1 3 0x301>; From f0d9d0f4b44ae5503ea368e7f066b20f12ca1d37 Mon Sep 17 00:00:00 2001 From: Matthew McClintock Date: Wed, 29 Jun 2016 10:50:01 -0700 Subject: [PATCH 32/50] 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 Signed-off-by: Thomas Pedersen Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../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(-) diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt b/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt index 6b7b5cebcc53..41aeaa2ff0f8 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt @@ -7,8 +7,10 @@ Required properties : "qcom,kpss-wdt-msm8960" "qcom,kpss-wdt-apq8064" "qcom,kpss-wdt-ipq8064" + "qcom,kpss-wdt-ipq4019" "qcom,kpss-timer" "qcom,scss-timer" + "qcom,kpss-wdt" - reg : shall contain base register location and length - clocks : shall contain the input clock diff --git a/arch/arm/boot/dts/qcom-ipq4019.dtsi b/arch/arm/boot/dts/qcom-ipq4019.dtsi index 5c08d19066c2..e625656a608a 100644 --- a/arch/arm/boot/dts/qcom-ipq4019.dtsi +++ b/arch/arm/boot/dts/qcom-ipq4019.dtsi @@ -252,7 +252,7 @@ }; watchdog@b017000 { - compatible = "qcom,kpss-standalone"; + compatible = "qcom,kpss-wdt", "qcom,kpss-wdt-ipq4019"; reg = <0xb017000 0x40>; clocks = <&sleep_clk>; timeout-sec = <10>; diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index a043fa4f60e5..111e8a7d2645 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -18,19 +18,42 @@ #include #include #include +#include -#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 watchdog_device *wdd) { 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_device *wdd) { 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_device *wdd) { 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 watchdog_device *wdd, unsigned long action, */ 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 platform_device *pdev) 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 platform_device *pdev) 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 platform_device *pdev) } 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); From 10073a205df269abcbd9c3fbc690a813827107ef Mon Sep 17 00:00:00 2001 From: Matthew McClintock Date: Tue, 28 Jun 2016 11:35:21 -0700 Subject: [PATCH 33/50] 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 Reviewed-by: Guenter Roeck Signed-off-by: Thomas Pedersen Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/qcom-wdt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 111e8a7d2645..5796b5d1b3f2 100644 --- 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_tmr[] = { [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 watchdog_device *wdd) 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 watchdog_device *wdd, unsigned long action, 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)); From 683fa50f0e18cb12da41a7df41cc1fa8b002e071 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Sun, 10 Jul 2016 11:11:04 +0200 Subject: [PATCH 34/50] watchdog: Add Meson GXBB Watchdog Driver Add watchdog specific driver for Amlogic Meson GXBB SoC. Signed-off-by: Neil Armstrong Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 10 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/meson_gxbb_wdt.c | 270 ++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 drivers/watchdog/meson_gxbb_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index c19b11a91cde..1bffe006ca9a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -617,6 +617,16 @@ config QCOM_WDT To compile this driver as a module, choose M here: the module will be called qcom_wdt. +config MESON_GXBB_WATCHDOG + tristate "Amlogic Meson GXBB SoCs watchdog support" + depends on ARCH_MESON + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + in Amlogic Meson GXBB SoCs. + To compile this driver as a module, choose M here: the + module will be called meson_gxbb_wdt. + config MESON_WATCHDOG tristate "Amlogic Meson SoCs watchdog support" depends on ARCH_MESON diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 3c0ed73b2cab..c22ad3ea3539 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_ST_LPC_WATCHDOG) += st_lpc_wdt.o obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o +obj-$(CONFIG_MESON_GXBB_WATCHDOG) += meson_gxbb_wdt.o obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c new file mode 100644 index 000000000000..44d180a2c5e5 --- /dev/null +++ b/drivers/watchdog/meson_gxbb_wdt.c @@ -0,0 +1,270 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * BSD LICENSE + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_TIMEOUT 30 /* seconds */ + +#define GXBB_WDT_CTRL_REG 0x0 +#define GXBB_WDT_TCNT_REG 0x8 +#define GXBB_WDT_RSET_REG 0xc + +#define GXBB_WDT_CTRL_CLKDIV_EN BIT(25) +#define GXBB_WDT_CTRL_CLK_EN BIT(24) +#define GXBB_WDT_CTRL_EE_RESET BIT(21) +#define GXBB_WDT_CTRL_EN BIT(18) +#define GXBB_WDT_CTRL_DIV_MASK (BIT(18) - 1) + +#define GXBB_WDT_TCNT_SETUP_MASK (BIT(16) - 1) +#define GXBB_WDT_TCNT_CNT_SHIFT 16 + +struct meson_gxbb_wdt { + void __iomem *reg_base; + struct watchdog_device wdt_dev; + struct clk *clk; +}; + +static int meson_gxbb_wdt_start(struct watchdog_device *wdt_dev) +{ + struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev); + + writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) | GXBB_WDT_CTRL_EN, + data->reg_base + GXBB_WDT_CTRL_REG); + + return 0; +} + +static int meson_gxbb_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev); + + writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) & ~GXBB_WDT_CTRL_EN, + data->reg_base + GXBB_WDT_CTRL_REG); + + return 0; +} + +static int meson_gxbb_wdt_ping(struct watchdog_device *wdt_dev) +{ + struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev); + + writel(0, data->reg_base + GXBB_WDT_RSET_REG); + + return 0; +} + +static int meson_gxbb_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev); + unsigned long tcnt = timeout * 1000; + + if (tcnt > GXBB_WDT_TCNT_SETUP_MASK) + tcnt = GXBB_WDT_TCNT_SETUP_MASK; + + wdt_dev->timeout = timeout; + + meson_gxbb_wdt_ping(wdt_dev); + + writel(tcnt, data->reg_base + GXBB_WDT_TCNT_REG); + + return 0; +} + +static unsigned int meson_gxbb_wdt_get_timeleft(struct watchdog_device *wdt_dev) +{ + struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev); + unsigned long reg; + + reg = readl(data->reg_base + GXBB_WDT_TCNT_REG); + + return ((reg >> GXBB_WDT_TCNT_CNT_SHIFT) - + (reg & GXBB_WDT_TCNT_SETUP_MASK)) / 1000; +} + +static const struct watchdog_ops meson_gxbb_wdt_ops = { + .start = meson_gxbb_wdt_start, + .stop = meson_gxbb_wdt_stop, + .ping = meson_gxbb_wdt_ping, + .set_timeout = meson_gxbb_wdt_set_timeout, + .get_timeleft = meson_gxbb_wdt_get_timeleft, +}; + +static const struct watchdog_info meson_gxbb_wdt_info = { + .identity = "Meson GXBB Watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, +}; + +static int __maybe_unused meson_gxbb_wdt_resume(struct device *dev) +{ + struct meson_gxbb_wdt *data = dev_get_drvdata(dev); + + if (watchdog_active(&data->wdt_dev)) + meson_gxbb_wdt_start(&data->wdt_dev); + + return 0; +} + +static int __maybe_unused meson_gxbb_wdt_suspend(struct device *dev) +{ + struct meson_gxbb_wdt *data = dev_get_drvdata(dev); + + if (watchdog_active(&data->wdt_dev)) + meson_gxbb_wdt_stop(&data->wdt_dev); + + return 0; +} + +static const struct dev_pm_ops meson_gxbb_wdt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(meson_gxbb_wdt_suspend, meson_gxbb_wdt_resume) +}; + +static const struct of_device_id meson_gxbb_wdt_dt_ids[] = { + { .compatible = "amlogic,meson-gxbb-wdt", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, meson_gxbb_wdt_dt_ids); + +static int meson_gxbb_wdt_probe(struct platform_device *pdev) +{ + struct meson_gxbb_wdt *data; + struct resource *res; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->reg_base)) + return PTR_ERR(data->reg_base); + + data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(data->clk)) + return PTR_ERR(data->clk); + + clk_prepare_enable(data->clk); + + platform_set_drvdata(pdev, data); + + data->wdt_dev.parent = &pdev->dev; + data->wdt_dev.info = &meson_gxbb_wdt_info; + data->wdt_dev.ops = &meson_gxbb_wdt_ops; + data->wdt_dev.max_hw_heartbeat_ms = GXBB_WDT_TCNT_SETUP_MASK; + data->wdt_dev.min_timeout = 1; + data->wdt_dev.timeout = DEFAULT_TIMEOUT; + watchdog_set_drvdata(&data->wdt_dev, data); + + /* Setup with 1ms timebase */ + writel(((clk_get_rate(data->clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) | + GXBB_WDT_CTRL_EE_RESET | + GXBB_WDT_CTRL_CLK_EN | + GXBB_WDT_CTRL_CLKDIV_EN, + data->reg_base + GXBB_WDT_CTRL_REG); + + meson_gxbb_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout); + + ret = watchdog_register_device(&data->wdt_dev); + if (ret) { + clk_disable_unprepare(data->clk); + return ret; + } + + return 0; +} + +static int meson_gxbb_wdt_remove(struct platform_device *pdev) +{ + struct meson_gxbb_wdt *data = platform_get_drvdata(pdev); + + watchdog_unregister_device(&data->wdt_dev); + + clk_disable_unprepare(data->clk); + + return 0; +} + +static void meson_gxbb_wdt_shutdown(struct platform_device *pdev) +{ + struct meson_gxbb_wdt *data = platform_get_drvdata(pdev); + + meson_gxbb_wdt_stop(&data->wdt_dev); +} + +static struct platform_driver meson_gxbb_wdt_driver = { + .probe = meson_gxbb_wdt_probe, + .remove = meson_gxbb_wdt_remove, + .shutdown = meson_gxbb_wdt_shutdown, + .driver = { + .name = "meson-gxbb-wdt", + .pm = &meson_gxbb_wdt_pm_ops, + .of_match_table = meson_gxbb_wdt_dt_ids, + }, +}; + +module_platform_driver(meson_gxbb_wdt_driver); + +MODULE_ALIAS("platform:meson-gxbb-wdt"); +MODULE_AUTHOR("Neil Armstrong "); +MODULE_DESCRIPTION("Amlogic Meson GXBB Watchdog timer driver"); +MODULE_LICENSE("Dual BSD/GPL"); From 80cdd91d3faf8d25977bcbf74613761eef912aab Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Sun, 10 Jul 2016 11:11:05 +0200 Subject: [PATCH 35/50] dt-bindings: watchdog: Add Meson GXBB Watchdog bindings Acked-by: Rob Herring Signed-off-by: Neil Armstrong Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../bindings/watchdog/meson-gxbb-wdt.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/meson-gxbb-wdt.txt diff --git a/Documentation/devicetree/bindings/watchdog/meson-gxbb-wdt.txt b/Documentation/devicetree/bindings/watchdog/meson-gxbb-wdt.txt new file mode 100644 index 000000000000..c7fe36fa739c --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/meson-gxbb-wdt.txt @@ -0,0 +1,16 @@ +Meson GXBB SoCs Watchdog timer + +Required properties: + +- compatible : should be "amlogic,meson-gxbb-wdt" +- reg : Specifies base physical address and size of the registers. +- clocks : Should be a phandle to the Watchdog clock source, for GXBB the xtal + is the default clock source. + +Example: + +wdt: watchdog@98d0 { + compatible = "amlogic,meson-gxbb-wdt"; + reg = <0 0x98d0 0x0 0x10>; + clocks = <&xtal>; +}; From aea4b477b96a1e098bdd39a7503eb4163cf65dbb Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 15 Jul 2016 10:15:21 +0200 Subject: [PATCH 36/50] watchdog: bcm2835_wdt: constify _ops and _info structures These are never modified, so might as well be const. Signed-off-by: Rasmus Villemoes Reviewed-by: Guenter Roeck Acked-by: Eric Anholt Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/bcm2835_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index 2e6164c4abc0..733e402e80d7 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -96,7 +96,7 @@ static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog) return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET); } -static struct watchdog_ops bcm2835_wdt_ops = { +static const struct watchdog_ops bcm2835_wdt_ops = { .owner = THIS_MODULE, .start = bcm2835_wdt_start, .stop = bcm2835_wdt_stop, @@ -104,7 +104,7 @@ static struct watchdog_ops bcm2835_wdt_ops = { .get_timeleft = bcm2835_wdt_get_timeleft, }; -static struct watchdog_info bcm2835_wdt_info = { +static const struct watchdog_info bcm2835_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "Broadcom BCM2835 Watchdog timer", From 960ae4ea776e160b3c47dc4b461e6f42ef2f659a Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 15 Jul 2016 10:15:22 +0200 Subject: [PATCH 37/50] watchdog: bcm2835_wdt: remove redundant ->set_timeout callback bcm2835_wdt_set_timeout does exactly what the watchdog framework does in the absence of a ->set_timeout callback (see watchdog_set_timeout in watchdog_dev.c), so remove it. Signed-off-by: Rasmus Villemoes Reviewed-by: Guenter Roeck Acked-by: Eric Anholt Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/bcm2835_wdt.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index 733e402e80d7..4dddd8298a22 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -82,12 +82,6 @@ static int bcm2835_wdt_stop(struct watchdog_device *wdog) return 0; } -static int bcm2835_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t) -{ - wdog->timeout = t; - return 0; -} - static unsigned int bcm2835_wdt_get_timeleft(struct watchdog_device *wdog) { struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog); @@ -100,7 +94,6 @@ static const struct watchdog_ops bcm2835_wdt_ops = { .owner = THIS_MODULE, .start = bcm2835_wdt_start, .stop = bcm2835_wdt_stop, - .set_timeout = bcm2835_wdt_set_timeout, .get_timeleft = bcm2835_wdt_get_timeleft, }; From 65b5b5e6480718e8cb5b6a1d32a7cb4efb9d93a9 Mon Sep 17 00:00:00 2001 From: Alexey Kunitskiy Date: Sun, 17 Jul 2016 19:34:24 +0300 Subject: [PATCH 38/50] watchdog: add support for MCP78S chipset in nv_tco Add support for MCP78S chipset in nv_tco watchdog driver Signed-off-by: Aleksey Kunitskiy Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/nv_tco.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c index bd917bb757b8..a0fabf6f92b0 100644 --- a/drivers/watchdog/nv_tco.c +++ b/drivers/watchdog/nv_tco.c @@ -294,6 +294,8 @@ static const struct pci_device_id tco_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS, + PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS, PCI_ANY_ID, PCI_ANY_ID, }, { 0, }, /* End of list */ From 3fbfe9264756d3fd99a9210345016c94ec4ada73 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 14 Jul 2016 11:16:26 +0200 Subject: [PATCH 39/50] watchdog: change watchdog_need_worker logic If the driver indicates that the watchdog is running, the framework should feed it until userspace opens the device, regardless of whether the driver has set max_hw_heartbeat_ms. This patch only affects the case where wdd->max_hw_heartbeat_ms is zero, wdd->timeout is non-zero, the watchdog is not active and the hardware device is running (*): - If wdd->timeout is zero, watchdog_need_worker() returns false both before and after this patch, and watchdog_next_keepalive() is not called. - If watchdog_active(wdd), the return value from watchdog_need_worker is also the same as before (namely, hm && t > hm). Hence in that case, watchdog_next_keepalive() is only called if hm == max_hw_heartbeat_ms is non-zero, so the change to min_not_zero there is a no-op. - If the watchdog is not active and the device is not running, we return false from watchdog_need_worker just as before. That leaves the watchdog_hw_running(wdd) && !watchdog_active(wdd) && wdd->timeout case. Again, it's easy to see that if wdd->max_hw_heartbeat_ms is non-zero, we return true from watchdog_need_worker with and without this patch, and the logic in watchdog_next_keepalive is unchanged. Finally, if wdd->max_hw_heartbeat_ms is 0, we used to end up in the cancel_delayed_work branch, whereas with this patch we end up scheduling a ping timeout_ms/2 from now. (*) This should imply that no current kernel drivers are affected, since the only drivers which explicitly set WDOG_HW_RUNNING are imx2_wdt.c and dw_wdt.c, both of which also provide a non-zero value for max_hw_heartbeat_ms. The watchdog core also sets WDOG_HW_RUNNING, but only when the driver doesn't provide ->stop, in which case it must, according to Documentation/watchdog/watchdog-kernel-api.txt, set max_hw_heartbeat_ms. Signed-off-by: Rasmus Villemoes Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 3595cffa24ea..14f8a92fca12 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -92,9 +92,13 @@ static inline bool watchdog_need_worker(struct watchdog_device *wdd) * thus is aware that the framework supports generating heartbeat * requests. * - Userspace requests a longer timeout than the hardware can handle. + * + * Alternatively, if userspace has not opened the watchdog + * device, we take care of feeding the watchdog if it is + * running. */ - return hm && ((watchdog_active(wdd) && t > hm) || - (t && !watchdog_active(wdd) && watchdog_hw_running(wdd))); + return (hm && watchdog_active(wdd) && t > hm) || + (t && !watchdog_active(wdd) && watchdog_hw_running(wdd)); } static long watchdog_next_keepalive(struct watchdog_device *wdd) @@ -107,7 +111,7 @@ static long watchdog_next_keepalive(struct watchdog_device *wdd) unsigned int hw_heartbeat_ms; virt_timeout = wd_data->last_keepalive + msecs_to_jiffies(timeout_ms); - hw_heartbeat_ms = min(timeout_ms, wdd->max_hw_heartbeat_ms); + hw_heartbeat_ms = min_not_zero(timeout_ms, wdd->max_hw_heartbeat_ms); keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2); if (!watchdog_active(wdd)) From c7ef68c32265a396a4d977f896a11a4cebb0ace4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 17 Jul 2016 13:47:47 -0700 Subject: [PATCH 40/50] watchdog: tangox: Set max_hw_heartbeat_ms instead of max_timeout Setting max_hw_heartbeat_ms lets the watchdog core provide a virtual timeout if the timeout requested by user space is larger than the maximum hardware timeout. Also, it helps the watchdog core to provide heartbeats if the hardware watchdog is running while closed. Fixes: a3e376d26ace ("watchdog: tangox: Mark running watchdog correctly") Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/tangox_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c index e7a5d0fc81d4..202c4b9cc921 100644 --- a/drivers/watchdog/tangox_wdt.c +++ b/drivers/watchdog/tangox_wdt.c @@ -149,7 +149,7 @@ static int tangox_wdt_probe(struct platform_device *pdev) dev->wdt.ops = &tangox_wdt_ops; dev->wdt.timeout = DEFAULT_TIMEOUT; dev->wdt.min_timeout = 1; - dev->wdt.max_timeout = (U32_MAX - 1) / dev->clk_rate; + dev->wdt.max_hw_heartbeat_ms = (U32_MAX - 1) / dev->clk_rate; watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev); watchdog_set_nowayout(&dev->wdt, nowayout); From 90b826f17a4e1c4ff2b3ef69512a6409e94f4d64 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 17 Jul 2016 15:04:11 -0700 Subject: [PATCH 41/50] watchdog: Implement status function in watchdog core Up to now, the watchdog status function called a driver function, which was supposed to return the watchdog status. All but one driver using the watchdog core did not implement this function, and the driver implementing it did not implement it correctly (the function is supposed to return WDIOF_ flags). At the same time, at least some of the status information can be provided by the watchdog core. Provide the available status bits directly from the watchdog driver core. Call the driver status function if it exists to get the boot status, but always provide WDIOF_MAGICCLOSE and WDIOF_KEEPALIVEPING internally. This patch makes the 'status' sysfs attribute always available. This attribute is now displayed as hex number with 0x prepended to be easier to decode. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../watchdog/watchdog-kernel-api.txt | 4 +++ drivers/watchdog/watchdog_dev.c | 34 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 92700d229bf9..7f31125c123e 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -167,6 +167,10 @@ they are supported. These optional routines/operations are: info structure). * status: this routine checks the status of the watchdog timer device. The status of the device is reported with watchdog WDIOF_* status flags/bits. + WDIOF_MAGICCLOSE and WDIOF_KEEPALIVEPING are reported by the watchdog core; + it is not necessary to report those bits from the driver. Also, if no status + function is provided by the driver, the watchdog core reports the status bits + provided in the bootstatus variable of struct watchdog_device. * set_timeout: this routine checks and changes the timeout of the watchdog timer device. It returns 0 on success, -EINVAL for "parameter out of range" and -EIO for "could not write value to the watchdog". On success this diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 14f8a92fca12..270f4bf291e3 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -69,6 +69,7 @@ struct watchdog_core_data { unsigned long status; /* Internal status bits */ #define _WDOG_DEV_OPEN 0 /* Opened ? */ #define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ +#define _WDOG_KEEPALIVE 2 /* Did we receive a keepalive ? */ }; /* the dev_t structure to store the dynamically allocated watchdog devices */ @@ -184,6 +185,8 @@ static int watchdog_ping(struct watchdog_device *wdd) if (!watchdog_active(wdd) && !watchdog_hw_running(wdd)) return 0; + set_bit(_WDOG_KEEPALIVE, &wd_data->status); + wd_data->last_keepalive = jiffies; return __watchdog_ping(wdd); } @@ -223,6 +226,8 @@ static int watchdog_start(struct watchdog_device *wdd) if (watchdog_active(wdd)) return 0; + set_bit(_WDOG_KEEPALIVE, &wd_data->status); + started_at = jiffies; if (watchdog_hw_running(wdd) && wdd->ops->ping) err = wdd->ops->ping(wdd); @@ -286,10 +291,27 @@ static int watchdog_stop(struct watchdog_device *wdd) static unsigned int watchdog_get_status(struct watchdog_device *wdd) { - if (!wdd->ops->status) - return 0; + struct watchdog_core_data *wd_data = wdd->wd_data; + unsigned int status; - return wdd->ops->status(wdd); + if (wdd->ops->status) + status = wdd->ops->status(wdd); + else + status = wdd->bootstatus & (WDIOF_CARDRESET | + WDIOF_OVERHEAT | + WDIOF_FANFAULT | + WDIOF_EXTERN1 | + WDIOF_EXTERN2 | + WDIOF_POWERUNDER | + WDIOF_POWEROVER); + + if (test_bit(_WDOG_ALLOW_RELEASE, &wd_data->status)) + status |= WDIOF_MAGICCLOSE; + + if (test_and_clear_bit(_WDOG_KEEPALIVE, &wd_data->status)) + status |= WDIOF_KEEPALIVEPING; + + return status; } /* @@ -365,7 +387,7 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr, status = watchdog_get_status(wdd); mutex_unlock(&wd_data->lock); - return sprintf(buf, "%u\n", status); + return sprintf(buf, "0x%x\n", status); } static DEVICE_ATTR_RO(status); @@ -433,9 +455,7 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, 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) + if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft) mode = 0; return mode; From e035d8f787b1de664fcac5eb84643f8c374284c4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 17 Jul 2016 15:04:12 -0700 Subject: [PATCH 42/50] watchdog: sbsa: Drop status function The watchdog status function is supposed to return WDIOF_ flags, not internal status flags. The available WDIOF_ flags are now returned by the watchdog core, so the status function in this driver is unnecessary. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sbsa_gwdt.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index cc885f1c1850..ce0c38bd0f00 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -180,15 +180,6 @@ static int sbsa_gwdt_keepalive(struct watchdog_device *wdd) return 0; } -static unsigned int sbsa_gwdt_status(struct watchdog_device *wdd) -{ - struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); - u32 status = readl(gwdt->control_base + SBSA_GWDT_WCS); - - /* is the watchdog timer running? */ - return (status & SBSA_GWDT_WCS_EN) << WDOG_ACTIVE; -} - static int sbsa_gwdt_start(struct watchdog_device *wdd) { struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); @@ -228,7 +219,6 @@ static struct watchdog_ops sbsa_gwdt_ops = { .owner = THIS_MODULE, .start = sbsa_gwdt_start, .stop = sbsa_gwdt_stop, - .status = sbsa_gwdt_status, .ping = sbsa_gwdt_keepalive, .set_timeout = sbsa_gwdt_set_timeout, .get_timeleft = sbsa_gwdt_get_timeleft, From 9dd8d5f870f44597c67d0e5f72753b9d02810308 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 19 Jul 2016 17:41:22 +0200 Subject: [PATCH 43/50] Documentation/watchdog: check return value for magic close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A recent commit added a write to the watchdog test code for doing the "magic close", but that caused a compile-time warning: Documentation/watchdog/src/watchdog-test.c: In function ‘main’: Documentation/watchdog/src/watchdog-test.c:94:5: warning: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Wunused-result] This changes the code to print a runtime warning if the write fails. Fixes: 5a2d3de19602 ("Documentation/watchdog: add support for magic close to watchdog-test") Signed-off-by: Arnd Bergmann Acked-by: Timur Tabi Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/src/watchdog-test.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Documentation/watchdog/src/watchdog-test.c b/Documentation/watchdog/src/watchdog-test.c index c69153913722..6983d05097e2 100644 --- a/Documentation/watchdog/src/watchdog-test.c +++ b/Documentation/watchdog/src/watchdog-test.c @@ -2,6 +2,7 @@ * Watchdog Driver Test Program */ +#include #include #include #include @@ -35,9 +36,13 @@ static void keep_alive(void) static void term(int sig) { - write(fd, &v, 1); + int ret = write(fd, &v, 1); + close(fd); - printf("\nStopping watchdog ticks...\n"); + if (ret < 0) + printf("\nStopping watchdog ticks failed (%d)...\n", errno); + else + printf("\nStopping watchdog ticks...\n"); exit(0); } @@ -45,6 +50,7 @@ int main(int argc, char *argv[]) { int flags; unsigned int ping_rate = 1; + int ret; setbuf(stdout, NULL); @@ -91,7 +97,9 @@ int main(int argc, char *argv[]) sleep(ping_rate); } end: - write(fd, &v, 1); + ret = write(fd, &v, 1); + if (ret < 0) + printf("Stopping watchdog ticks failed (%d)...\n", errno); close(fd); return 0; } From c682b5cb7ee0d5430e952956603888d7ffebc27a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 19 Jul 2016 13:46:54 +0200 Subject: [PATCH 44/50] watchdog: renesas-wdt: Add support for the r8a7796 wdt Document support for the Watchdog Timer (WDT) Controller in the Renesas R-Car M3-W (r8a7796) SoC. No driver update is needed. Signed-off-by: Geert Uytterhoeven Acked-by: Wolfram Sang Acked-by: Rob Herring Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/renesas-wdt.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt b/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt index b9512f1eb80a..da24e3133417 100644 --- a/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/renesas-wdt.txt @@ -1,7 +1,11 @@ Renesas Watchdog Timer (WDT) Controller Required properties: -- compatible : Should be "renesas,r8a7795-wdt", or "renesas,rcar-gen3-wdt" +- compatible : Should be "renesas,-wdt", and + "renesas,rcar-gen3-wdt" as fallback. + Examples with soctypes are: + - "renesas,r8a7795-wdt" (R-Car H3) + - "renesas,r8a7796-wdt" (R-Car M3-W) When compatible with the generic version, nodes must list the SoC-specific version corresponding to the platform first, followed by the generic From aa4614d15990fcbe93969dee600361d47d28885f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 19 Jul 2016 11:35:19 +0000 Subject: [PATCH 45/50] watchdog: pic32-wdt: Remove .owner field for driver Remove .owner field if calls are used which set it automatically. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Wei Yongjun Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pic32-wdt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/watchdog/pic32-wdt.c b/drivers/watchdog/pic32-wdt.c index 2b7a2b22fcb2..18174fef5113 100644 --- a/drivers/watchdog/pic32-wdt.c +++ b/drivers/watchdog/pic32-wdt.c @@ -251,7 +251,6 @@ static struct platform_driver pic32_wdt_driver = { .remove = pic32_wdt_drv_remove, .driver = { .name = "pic32-wdt", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(pic32_wdt_dt_ids), } }; From 919edd5119796f1cc3b47f23e7e15cc0f46942ba Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 19 Jul 2016 11:35:11 +0000 Subject: [PATCH 46/50] watchdog: pic32-dmt: Remove .owner field for driver Remove .owner field if calls are used which set it automatically. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Wei Yongjun Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pic32-dmt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/watchdog/pic32-dmt.c b/drivers/watchdog/pic32-dmt.c index 5e8890eaa49f..c797305f8338 100644 --- a/drivers/watchdog/pic32-dmt.c +++ b/drivers/watchdog/pic32-dmt.c @@ -245,7 +245,6 @@ static struct platform_driver pic32_dmt_driver = { .remove = pic32_dmt_remove, .driver = { .name = "pic32-dmt", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(pic32_dmt_of_ids), } }; From cddd74db8d42b953a0612810f07c99e73deab185 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 19 Jul 2016 11:21:56 +0000 Subject: [PATCH 47/50] watchdog: pic32-wdt: Fix return value check in pic32_wdt_drv_probe() In case of error, the function devm_ioremap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pic32-wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/pic32-wdt.c b/drivers/watchdog/pic32-wdt.c index 18174fef5113..e2761068dc6f 100644 --- a/drivers/watchdog/pic32-wdt.c +++ b/drivers/watchdog/pic32-wdt.c @@ -183,8 +183,8 @@ static int pic32_wdt_drv_probe(struct platform_device *pdev) return PTR_ERR(wdt->regs); wdt->rst_base = devm_ioremap(&pdev->dev, PIC32_BASE_RESET, 0x10); - if (IS_ERR(wdt->rst_base)) - return PTR_ERR(wdt->rst_base); + if (!wdt->rst_base) + return -ENOMEM; wdt->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(wdt->clk)) { From 138913cb632be12344982e54ccd12f6f15971bf7 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 19 Jul 2016 11:22:34 +0000 Subject: [PATCH 48/50] watchdog: core: Fix error handling of watchdog_dev_init() Fix the error handling paths of watchdog_dev_init(). Signed-off-by: Wei Yongjun Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 270f4bf291e3..f128efc36135 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -972,17 +972,22 @@ int __init watchdog_dev_init(void) err = class_register(&watchdog_class); if (err < 0) { pr_err("couldn't register class\n"); - return err; + goto err_register; } 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; + goto err_alloc; } return 0; + +err_alloc: + class_unregister(&watchdog_class); +err_register: + destroy_workqueue(watchdog_wq); + return err; } /* From 3c10bbde10fe4dca52726e246cefa6b0a1dfbd3e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 21 Jul 2016 14:21:56 -0700 Subject: [PATCH 49/50] watchdog: core: Clear WDOG_HW_RUNNING before calling the stop function WDOG_HW_RUNNING indicates that the hardware watchdog is running while the watchdog device is closed. The flag may be set by the driver when it is instantiated to indicate that the watchdog is running, and that the watchdog core needs to send heartbeat requests to the driver until the watchdog device is opened. When the watchdog device is closed, the flag can be used by the driver's stop function to indicate to the watchdog core that it was unable to stop the watchdog, and that the watchdog core needs to send heartbeat requests. This only works if the flag is actually cleared when the watchdog is stopped. To avoid having to clear the flag in each driver's stop function, clear it in the watchdog core before calling the stop function. Reported-by: Rasmus Villemoes Fixes: ee142889e32f ("watchdog: Introduce WDOG_HW_RUNNING flag") Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index f128efc36135..040bf8382f46 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -267,10 +267,12 @@ static int watchdog_stop(struct watchdog_device *wdd) return -EBUSY; } - if (wdd->ops->stop) + if (wdd->ops->stop) { + clear_bit(WDOG_HW_RUNNING, &wdd->status); err = wdd->ops->stop(wdd); - else + } else { set_bit(WDOG_HW_RUNNING, &wdd->status); + } if (err == 0) { clear_bit(WDOG_ACTIVE, &wdd->status); From 1ac06563434e5f3302259608d3589bf7002431fe Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 26 Jul 2016 14:50:14 +0000 Subject: [PATCH 50/50] watchdog: gpio_wdt: Fix missing platform_set_drvdata() in gpio_wdt_probe() Add missing platform_set_drvdata() in gpio_wdt_probe(), otherwise calling platform_get_drvdata() in remove returns NULL. This is detected by Coccinelle semantic patch. Signed-off-by: Wei Yongjun Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/gpio_wdt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index ba066e4a707b..93457cabc178 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -151,6 +151,8 @@ static int gpio_wdt_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + platform_set_drvdata(pdev, priv); + priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags); if (!gpio_is_valid(priv->gpio)) return priv->gpio;