From 39bd56df7b2dae80d4099c64ad776775a3876ed5 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 2 Mar 2017 18:31:13 +0100 Subject: watchodg: sama5d4: simplify probe Because the only way to use the driver is to have a device tree enabling it, pdev->dev.of_node will never be NULL. Remove the unnecessary check. Signed-off-by: Alexandre Belloni Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sama5d4_wdt.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index 362fd229786d..d710014f3b7d 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -228,15 +228,13 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) wdt->reg_base = regs; - if (pdev->dev.of_node) { - irq = irq_of_parse_and_map(pdev->dev.of_node, 0); - if (!irq) - dev_warn(&pdev->dev, "failed to get IRQ from DT\n"); + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!irq) + dev_warn(&pdev->dev, "failed to get IRQ from DT\n"); - ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt); - if (ret) - return ret; - } + ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt); + if (ret) + return ret; if ((wdt->mr & AT91_WDT_WDFIEN) && irq) { ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler, -- cgit v1.2.3 From 5dca80f63eaf18eca2ba3ebf61056feb66103951 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 2 Mar 2017 18:31:14 +0100 Subject: watchdog: sama5d4: Add comment explaining what happens on resume Because suspending to RAM may lose the register values, they are restored on resume. This is currently done unconditionally because there is currently no way to know (from the driver) whether they have really been lost or are still valid. Writing MR also pings the watchdog and this may not be what is expected so add a comment explaining why it happens. Signed-off-by: Alexandre Belloni Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sama5d4_wdt.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index d710014f3b7d..0ae947c3d7bc 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -300,6 +300,11 @@ static int sama5d4_wdt_resume(struct device *dev) { struct sama5d4_wdt *wdt = dev_get_drvdata(dev); + /* + * FIXME: writing MR also pings the watchdog which may not be desired. + * This should only be done when the registers are lost on suspend but + * there is no way to get this information right now. + */ sama5d4_wdt_init(wdt); return 0; -- cgit v1.2.3 From aea24187f65e8adb00b2be949cd809fcb2aa241c Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Sat, 4 Mar 2017 17:37:35 -0500 Subject: watchdog: add rza_wdt driver Adds a watchdog timer driver for the Renesas RZ/A Series SoCs. A reset handler is also included since a WDT overflow is the only method for restarting an RZ/A SoC. Signed-off-by: Chris Brandt Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 8 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/rza_wdt.c | 199 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 drivers/watchdog/rza_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 8b9049dac094..1fdb5a365413 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -721,6 +721,14 @@ 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 RENESAS_RZAWDT + tristate "Renesas RZ/A WDT Watchdog" + depends on ARCH_RENESAS || COMPILE_TEST + select WATCHDOG_CORE + help + This driver adds watchdog support for the integrated watchdogs in the + Renesas RZ/A SoCs. These watchdogs can be used to reset a system. + config ASPEED_WATCHDOG tristate "Aspeed 2400 watchdog support" depends on ARCH_ASPEED || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index a2126e2a99ae..e23fb4b51878 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -82,6 +82,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_RENESAS_RZAWDT) += rza_wdt.o obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c new file mode 100644 index 000000000000..e618218d2374 --- /dev/null +++ b/drivers/watchdog/rza_wdt.c @@ -0,0 +1,199 @@ +/* + * Renesas RZ/A Series WDT Driver + * + * Copyright (C) 2017 Renesas Electronics America, Inc. + * Copyright (C) 2017 Chris Brandt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_TIMEOUT 30 + +/* Watchdog Timer Registers */ +#define WTCSR 0 +#define WTCSR_MAGIC 0xA500 +#define WTSCR_WT BIT(6) +#define WTSCR_TME BIT(5) +#define WTSCR_CKS(i) (i) + +#define WTCNT 2 +#define WTCNT_MAGIC 0x5A00 + +#define WRCSR 4 +#define WRCSR_MAGIC 0x5A00 +#define WRCSR_RSTE BIT(6) +#define WRCSR_CLEAR_WOVF 0xA500 /* special value */ + +struct rza_wdt { + struct watchdog_device wdev; + void __iomem *base; + struct clk *clk; +}; + +static int rza_wdt_start(struct watchdog_device *wdev) +{ + struct rza_wdt *priv = watchdog_get_drvdata(wdev); + + /* Stop timer */ + writew(WTCSR_MAGIC | 0, priv->base + WTCSR); + + /* Must dummy read WRCSR:WOVF at least once before clearing */ + readb(priv->base + WRCSR); + writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR); + + /* + * Start timer with slowest clock source and reset option enabled. + */ + writew(WRCSR_MAGIC | WRCSR_RSTE, priv->base + WRCSR); + writew(WTCNT_MAGIC | 0, priv->base + WTCNT); + writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME | WTSCR_CKS(7), + priv->base + WTCSR); + + return 0; +} + +static int rza_wdt_stop(struct watchdog_device *wdev) +{ + struct rza_wdt *priv = watchdog_get_drvdata(wdev); + + writew(WTCSR_MAGIC | 0, priv->base + WTCSR); + + return 0; +} + +static int rza_wdt_ping(struct watchdog_device *wdev) +{ + struct rza_wdt *priv = watchdog_get_drvdata(wdev); + + writew(WTCNT_MAGIC | 0, priv->base + WTCNT); + + return 0; +} + +static int rza_wdt_restart(struct watchdog_device *wdev, unsigned long action, + void *data) +{ + struct rza_wdt *priv = watchdog_get_drvdata(wdev); + + /* Stop timer */ + writew(WTCSR_MAGIC | 0, priv->base + WTCSR); + + /* Must dummy read WRCSR:WOVF at least once before clearing */ + readb(priv->base + WRCSR); + writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR); + + /* + * Start timer with fastest clock source and only 1 clock left before + * overflow with reset option enabled. + */ + writew(WRCSR_MAGIC | WRCSR_RSTE, priv->base + WRCSR); + writew(WTCNT_MAGIC | 255, priv->base + WTCNT); + writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME, priv->base + WTCSR); + + /* + * Actually make sure the above sequence hits hardware before sleeping. + */ + wmb(); + + /* Wait for WDT overflow (reset) */ + udelay(20); + + return 0; +} + +static const struct watchdog_info rza_wdt_ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + .identity = "Renesas RZ/A WDT Watchdog", +}; + +static const struct watchdog_ops rza_wdt_ops = { + .owner = THIS_MODULE, + .start = rza_wdt_start, + .stop = rza_wdt_stop, + .ping = rza_wdt_ping, + .restart = rza_wdt_restart, +}; + +static int rza_wdt_probe(struct platform_device *pdev) +{ + struct rza_wdt *priv; + struct resource *res; + unsigned long rate; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + rate = clk_get_rate(priv->clk); + if (rate < 16384) { + dev_err(&pdev->dev, "invalid clock rate (%ld)\n", rate); + return -ENOENT; + } + + /* Assume slowest clock rate possible (CKS=7) */ + rate /= 16384; + + priv->wdev.info = &rza_wdt_ident, + priv->wdev.ops = &rza_wdt_ops, + priv->wdev.parent = &pdev->dev; + + /* + * Since the max possible timeout of our 8-bit count register is less + * than a second, we must use max_hw_heartbeat_ms. + */ + priv->wdev.max_hw_heartbeat_ms = (1000 * U8_MAX) / rate; + dev_dbg(&pdev->dev, "max hw timeout of %dms\n", + priv->wdev.max_hw_heartbeat_ms); + + priv->wdev.min_timeout = 1; + priv->wdev.timeout = DEFAULT_TIMEOUT; + + watchdog_init_timeout(&priv->wdev, 0, &pdev->dev); + watchdog_set_drvdata(&priv->wdev, priv); + + ret = devm_watchdog_register_device(&pdev->dev, &priv->wdev); + if (ret) + dev_err(&pdev->dev, "Cannot register watchdog device\n"); + + return ret; +} + +static const struct of_device_id rza_wdt_of_match[] = { + { .compatible = "renesas,rza-wdt", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rza_wdt_of_match); + +static struct platform_driver rza_wdt_driver = { + .probe = rza_wdt_probe, + .driver = { + .name = "rza_wdt", + .of_match_table = rza_wdt_of_match, + }, +}; + +module_platform_driver(rza_wdt_driver); + +MODULE_DESCRIPTION("Renesas RZ/A WDT Driver"); +MODULE_AUTHOR("Chris Brandt "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 954351e8707bcdf6091cc55dab4e9e2453de6655 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 11 Mar 2017 00:22:17 +0200 Subject: watchdog: intel-mid_wdt: Keep watchdog running Firmware followed by bootloader leaves watchdog running. Keep it running in the driver. User will not need any additional options to reboot in case of panic during boot. Signed-off-by: Andy Shevchenko Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/intel-mid_wdt.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 45e4d02221b5..72c108a12c19 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -147,8 +147,21 @@ static int mid_wdt_probe(struct platform_device *pdev) return ret; } - /* Make sure the watchdog is not running */ - wdt_stop(wdt_dev); + /* + * The firmware followed by U-Boot leaves the watchdog running + * with the default threshold which may vary. When we get here + * we should make a decision to prevent any side effects before + * user space daemon will take care of it. The best option, + * taking into consideration that there is no way to read values + * back from hardware, is to enforce watchdog being run with + * deterministic values. + */ + ret = wdt_start(wdt_dev); + if (ret) + return ret; + + /* Make sure the watchdog is serviced */ + set_bit(WDOG_HW_RUNNING, &wdt_dev->status); ret = devm_watchdog_register_device(&pdev->dev, wdt_dev); if (ret) { -- cgit v1.2.3 From 58415efe96670b858eb7b5ab066881ae3f1dad25 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Mar 2017 21:07:24 +0200 Subject: watchdog: s3c2410: Constify local structures Structures watchdog_device, watchdog_ops and s3c2410_wdt_variant are not modified so they can be made const to increase code safeness. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/s3c2410_wdt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 6ed97596ca80..52b66bcdd1ef 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -131,7 +131,7 @@ struct s3c2410_wdt { unsigned long wtdat_save; struct watchdog_device wdt_device; struct notifier_block freq_transition; - struct s3c2410_wdt_variant *drv_data; + const struct s3c2410_wdt_variant *drv_data; struct regmap *pmureg; }; @@ -401,7 +401,7 @@ static const struct watchdog_ops s3c2410wdt_ops = { .restart = s3c2410wdt_restart, }; -static struct watchdog_device s3c2410_wdd = { +static const struct watchdog_device s3c2410_wdd = { .info = &s3c2410_wdt_ident, .ops = &s3c2410wdt_ops, .timeout = S3C2410_WATCHDOG_DEFAULT_TIME, @@ -507,7 +507,7 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) return 0; } -static inline struct s3c2410_wdt_variant * +static inline const struct s3c2410_wdt_variant * s3c2410_get_wdt_drv_data(struct platform_device *pdev) { if (pdev->dev.of_node) { -- cgit v1.2.3 From a9a02c46624ad112eb07ca26b6a025c1358ed3ad Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Mar 2017 21:07:25 +0200 Subject: watchdog: s3c2410: Simplify getting driver data Simplify the flow in helper function for getting the driver data by using of_device_get_match_data() and only one if() branch. The code should be equivalent. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/s3c2410_wdt.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 52b66bcdd1ef..0532dc93e600 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -510,14 +511,16 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) static inline const struct s3c2410_wdt_variant * s3c2410_get_wdt_drv_data(struct platform_device *pdev) { - if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(s3c2410_wdt_match, pdev->dev.of_node); - return (struct s3c2410_wdt_variant *)match->data; - } else { - return (struct s3c2410_wdt_variant *) - platform_get_device_id(pdev)->driver_data; + const struct s3c2410_wdt_variant *variant; + + variant = of_device_get_match_data(&pdev->dev); + if (!variant) { + /* Device matched by platform_device_id */ + variant = (struct s3c2410_wdt_variant *) + platform_get_device_id(pdev)->driver_data; } + + return variant; } static int s3c2410wdt_probe(struct platform_device *pdev) -- cgit v1.2.3 From 08497f22b15affcf08fb2d1173caa73e7ad54df7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Mar 2017 21:07:26 +0200 Subject: watchdog: s3c2410: Minor code cleanup Cleanup the code from minor readability issues (no functional changes): 1. Fix checkpatch: ERROR: Do not include the paragraph about writing to the Free Software Foundation's mailing address. 2. Fix checkpatch: WARNING: quoted string split across lines 3. Fix chechpatch: WARNING: Prefer 'unsigned int' to bare use of 'unsigned' 4. Use 'dev' consistently in probe function instead of mixing dev with &pdev->dev. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/s3c2410_wdt.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 0532dc93e600..adaa43543f0a 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -1,5 +1,4 @@ -/* linux/drivers/char/watchdog/s3c2410_wdt.c - * +/* * Copyright (c) 2004 Simtec Electronics * Ben Dooks * @@ -17,11 +16,7 @@ * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ + */ #include #include @@ -95,8 +90,7 @@ MODULE_PARM_DESC(tmr_atboot, __MODULE_STRING(S3C2410_WATCHDOG_ATBOOT)); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, " - "0 to reboot (default 0)"); +MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)"); /** * struct s3c2410_wdt_variant - Per-variant config data @@ -311,7 +305,8 @@ static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt) return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; } -static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout) +static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, + unsigned int timeout) { struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); unsigned long freq = clk_get_rate(wdt->clock); @@ -525,7 +520,7 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev) static int s3c2410wdt_probe(struct platform_device *pdev) { - struct device *dev; + struct device *dev = &pdev->dev; struct s3c2410_wdt *wdt; struct resource *wdt_mem; struct resource *wdt_irq; @@ -533,13 +528,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev) int started = 0; int ret; - dev = &pdev->dev; - wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) return -ENOMEM; - wdt->dev = &pdev->dev; + wdt->dev = dev; spin_lock_init(&wdt->lock); wdt->wdt_device = s3c2410_wdd; @@ -595,7 +588,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) /* see if we can actually set the requested timer margin, and if * not, try the default value */ - watchdog_init_timeout(&wdt->wdt_device, tmr_margin, &pdev->dev); + watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev); ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, wdt->wdt_device.timeout); if (ret) { @@ -604,11 +597,10 @@ static int s3c2410wdt_probe(struct platform_device *pdev) if (started == 0) dev_info(dev, - "tmr_margin value out of range, default %d used\n", - S3C2410_WATCHDOG_DEFAULT_TIME); + "tmr_margin value out of range, default %d used\n", + S3C2410_WATCHDOG_DEFAULT_TIME); else - dev_info(dev, "default timer value is out of range, " - "cannot start\n"); + dev_info(dev, "default timer value is out of range, cannot start\n"); } ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0, @@ -622,7 +614,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev) watchdog_set_restart_priority(&wdt->wdt_device, 128); wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); - wdt->wdt_device.parent = &pdev->dev; + wdt->wdt_device.parent = dev; ret = watchdog_register_device(&wdt->wdt_device); if (ret) { @@ -757,7 +749,6 @@ static struct platform_driver s3c2410wdt_driver = { module_platform_driver(s3c2410wdt_driver); -MODULE_AUTHOR("Ben Dooks , " - "Dimitry Andric "); +MODULE_AUTHOR("Ben Dooks , Dimitry Andric "); MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ecd94a41debfd21961c9f59f639dce26f2c779f0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 29 Mar 2017 17:34:24 +0200 Subject: watchdog: orion: make license info match the file header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header says: This file is licensed under the terms of the GNU General Public License version 2. The right identifier for MODULE_LICENSE is "GPL v2" then. Signed-off-by: Uwe Kleine-König Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/orion_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 39be4dd8035e..83af7d6cc37c 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -651,5 +651,5 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:orion_wdt"); -- cgit v1.2.3 From 03bca15833f2865b11835b7f5bfa594d1aaacecc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 28 Feb 2016 13:12:23 -0800 Subject: watchdog: gpio: Convert to use infrastructure triggered keepalives The watchdog infrastructure now supports handling watchdog keepalive if the watchdog is running while the watchdog device is closed. The infrastructure now also supports generating additional heartbeats if the maximum hardware timeout is smaller than or close to the configured timeout. Convert the driver to use this infrastructure. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/gpio_wdt.c | 73 ++++++++------------------------------------- 1 file changed, 13 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index 93457cabc178..cb66c2f99ff1 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -18,7 +18,6 @@ #define SOFT_TIMEOUT_MIN 1 #define SOFT_TIMEOUT_DEF 60 -#define SOFT_TIMEOUT_MAX 0xffff enum { HW_ALGO_TOGGLE, @@ -30,11 +29,7 @@ struct gpio_wdt_priv { bool active_low; bool state; bool always_running; - bool armed; unsigned int hw_algo; - unsigned int hw_margin; - unsigned long last_jiffies; - struct timer_list timer; struct watchdog_device wdd; }; @@ -47,21 +42,10 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv) gpio_direction_input(priv->gpio); } -static void gpio_wdt_hwping(unsigned long data) +static int gpio_wdt_ping(struct watchdog_device *wdd) { - struct watchdog_device *wdd = (struct watchdog_device *)data; struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); - if (priv->armed && time_after(jiffies, priv->last_jiffies + - msecs_to_jiffies(wdd->timeout * 1000))) { - dev_crit(wdd->parent, - "Timer expired. System will reboot soon!\n"); - return; - } - - /* Restart timer */ - mod_timer(&priv->timer, jiffies + priv->hw_margin); - switch (priv->hw_algo) { case HW_ALGO_TOGGLE: /* Toggle output pin */ @@ -75,55 +59,33 @@ static void gpio_wdt_hwping(unsigned long data) gpio_set_value_cansleep(priv->gpio, priv->active_low); break; } -} - -static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv) -{ - priv->state = priv->active_low; - gpio_direction_output(priv->gpio, priv->state); - priv->last_jiffies = jiffies; - gpio_wdt_hwping((unsigned long)&priv->wdd); + return 0; } static int gpio_wdt_start(struct watchdog_device *wdd) { struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); - gpio_wdt_start_impl(priv); - priv->armed = true; + priv->state = priv->active_low; + gpio_direction_output(priv->gpio, priv->state); - return 0; + set_bit(WDOG_HW_RUNNING, &wdd->status); + + return gpio_wdt_ping(wdd); } static int gpio_wdt_stop(struct watchdog_device *wdd) { struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); - priv->armed = false; if (!priv->always_running) { - mod_timer(&priv->timer, 0); gpio_wdt_disable(priv); + clear_bit(WDOG_HW_RUNNING, &wdd->status); } return 0; } -static int gpio_wdt_ping(struct watchdog_device *wdd) -{ - struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); - - priv->last_jiffies = jiffies; - - return 0; -} - -static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) -{ - wdd->timeout = t; - - return gpio_wdt_ping(wdd); -} - static const struct watchdog_info gpio_wdt_ident = { .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, @@ -135,7 +97,6 @@ static const struct watchdog_ops gpio_wdt_ops = { .start = gpio_wdt_start, .stop = gpio_wdt_stop, .ping = gpio_wdt_ping, - .set_timeout = gpio_wdt_set_timeout, }; static int gpio_wdt_probe(struct platform_device *pdev) @@ -185,9 +146,6 @@ static int gpio_wdt_probe(struct platform_device *pdev) if (hw_margin < 2 || hw_margin > 65535) return -EINVAL; - /* Use safe value (1/2 of real timeout) */ - priv->hw_margin = msecs_to_jiffies(hw_margin / 2); - priv->always_running = of_property_read_bool(pdev->dev.of_node, "always-running"); @@ -196,31 +154,26 @@ static int gpio_wdt_probe(struct platform_device *pdev) priv->wdd.info = &gpio_wdt_ident; priv->wdd.ops = &gpio_wdt_ops; priv->wdd.min_timeout = SOFT_TIMEOUT_MIN; - priv->wdd.max_timeout = SOFT_TIMEOUT_MAX; + priv->wdd.max_hw_heartbeat_ms = hw_margin; priv->wdd.parent = &pdev->dev; if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0) priv->wdd.timeout = SOFT_TIMEOUT_DEF; - setup_timer(&priv->timer, gpio_wdt_hwping, (unsigned long)&priv->wdd); - watchdog_stop_on_reboot(&priv->wdd); - ret = watchdog_register_device(&priv->wdd); - if (ret) - return ret; - if (priv->always_running) - gpio_wdt_start_impl(priv); + gpio_wdt_start(&priv->wdd); - return 0; + ret = watchdog_register_device(&priv->wdd); + + return ret; } static int gpio_wdt_remove(struct platform_device *pdev) { struct gpio_wdt_priv *priv = platform_get_drvdata(pdev); - del_timer_sync(&priv->timer); watchdog_unregister_device(&priv->wdd); return 0; -- cgit v1.2.3 From 4332d113c66a6ecb3702cb8f265adbbe654f9d5f Mon Sep 17 00:00:00 2001 From: Yannick Fertre Date: Thu, 6 Apr 2017 14:19:25 +0200 Subject: watchdog: Add STM32 IWDG driver This patch adds IWDG (Independent WatchDoG) support for STM32 platform. Signed-off-by: Yannick FERTRE Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 12 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/stm32_iwdg.c | 253 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 drivers/watchdog/stm32_iwdg.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1fdb5a365413..124e9b87ab6b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -752,6 +752,18 @@ config ZX2967_WATCHDOG To compile this driver as a module, choose M here: the module will be called zx2967_wdt. +config STM32_WATCHDOG + tristate "STM32 Independent WatchDoG (IWDG) support" + depends on ARCH_STM32 + select WATCHDOG_CORE + default y + help + Say Y here to include support for the watchdog timer + in stm32 SoCs. + + To compile this driver as a module, choose M here: the + module will be called stm32_iwdg. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index e23fb4b51878..3aafd997917a 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o +obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c new file mode 100644 index 000000000000..6c501b7dba29 --- /dev/null +++ b/drivers/watchdog/stm32_iwdg.c @@ -0,0 +1,253 @@ +/* + * Driver for STM32 Independent Watchdog + * + * Copyright (C) Yannick Fertre 2017 + * Author: Yannick Fertre + * + * This driver is based on tegra_wdt.c + * + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* IWDG registers */ +#define IWDG_KR 0x00 /* Key register */ +#define IWDG_PR 0x04 /* Prescaler Register */ +#define IWDG_RLR 0x08 /* ReLoad Register */ +#define IWDG_SR 0x0C /* Status Register */ +#define IWDG_WINR 0x10 /* Windows Register */ + +/* IWDG_KR register bit mask */ +#define KR_KEY_RELOAD 0xAAAA /* reload counter enable */ +#define KR_KEY_ENABLE 0xCCCC /* peripheral enable */ +#define KR_KEY_EWA 0x5555 /* write access enable */ +#define KR_KEY_DWA 0x0000 /* write access disable */ + +/* IWDG_PR register bit values */ +#define PR_4 0x00 /* prescaler set to 4 */ +#define PR_8 0x01 /* prescaler set to 8 */ +#define PR_16 0x02 /* prescaler set to 16 */ +#define PR_32 0x03 /* prescaler set to 32 */ +#define PR_64 0x04 /* prescaler set to 64 */ +#define PR_128 0x05 /* prescaler set to 128 */ +#define PR_256 0x06 /* prescaler set to 256 */ + +/* IWDG_RLR register values */ +#define RLR_MIN 0x07C /* min value supported by reload register */ +#define RLR_MAX 0xFFF /* max value supported by reload register */ + +/* IWDG_SR register bit mask */ +#define FLAG_PVU BIT(0) /* Watchdog prescaler value update */ +#define FLAG_RVU BIT(1) /* Watchdog counter reload value update */ + +/* set timeout to 100000 us */ +#define TIMEOUT_US 100000 +#define SLEEP_US 1000 + +struct stm32_iwdg { + struct watchdog_device wdd; + void __iomem *regs; + struct clk *clk; + unsigned int rate; +}; + +static inline u32 reg_read(void __iomem *base, u32 reg) +{ + return readl_relaxed(base + reg); +} + +static inline void reg_write(void __iomem *base, u32 reg, u32 val) +{ + writel_relaxed(val, base + reg); +} + +static int stm32_iwdg_start(struct watchdog_device *wdd) +{ + struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd); + u32 val = FLAG_PVU | FLAG_RVU; + u32 reload; + int ret; + + dev_dbg(wdd->parent, "%s\n", __func__); + + /* prescaler fixed to 256 */ + reload = clamp_t(unsigned int, ((wdd->timeout * wdt->rate) / 256) - 1, + RLR_MIN, RLR_MAX); + + /* enable write access */ + reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA); + + /* set prescaler & reload registers */ + reg_write(wdt->regs, IWDG_PR, PR_256); /* prescaler fix to 256 */ + reg_write(wdt->regs, IWDG_RLR, reload); + reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE); + + /* wait for the registers to be updated (max 100ms) */ + ret = readl_relaxed_poll_timeout(wdt->regs + IWDG_SR, val, + !(val & (FLAG_PVU | FLAG_RVU)), + SLEEP_US, TIMEOUT_US); + if (ret) { + dev_err(wdd->parent, + "Fail to set prescaler or reload registers\n"); + return ret; + } + + /* reload watchdog */ + reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD); + + return 0; +} + +static int stm32_iwdg_ping(struct watchdog_device *wdd) +{ + struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd); + + dev_dbg(wdd->parent, "%s\n", __func__); + + /* reload watchdog */ + reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD); + + return 0; +} + +static int stm32_iwdg_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + dev_dbg(wdd->parent, "%s timeout: %d sec\n", __func__, timeout); + + wdd->timeout = timeout; + + if (watchdog_active(wdd)) + return stm32_iwdg_start(wdd); + + return 0; +} + +static const struct watchdog_info stm32_iwdg_info = { + .options = WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .identity = "STM32 Independent Watchdog", +}; + +static struct watchdog_ops stm32_iwdg_ops = { + .owner = THIS_MODULE, + .start = stm32_iwdg_start, + .ping = stm32_iwdg_ping, + .set_timeout = stm32_iwdg_set_timeout, +}; + +static int stm32_iwdg_probe(struct platform_device *pdev) +{ + struct watchdog_device *wdd; + struct stm32_iwdg *wdt; + struct resource *res; + void __iomem *regs; + struct clk *clk; + int ret; + + /* This is the timer base. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) { + dev_err(&pdev->dev, "Could not get resource\n"); + return PTR_ERR(regs); + } + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Unable to get clock\n"); + return PTR_ERR(clk); + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(&pdev->dev, "Unable to prepare clock %p\n", clk); + return ret; + } + + /* + * Allocate our watchdog driver data, which has the + * struct watchdog_device nested within it. + */ + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) { + ret = -ENOMEM; + goto err; + } + + /* Initialize struct stm32_iwdg. */ + wdt->regs = regs; + wdt->clk = clk; + wdt->rate = clk_get_rate(clk); + + /* Initialize struct watchdog_device. */ + wdd = &wdt->wdd; + wdd->info = &stm32_iwdg_info; + wdd->ops = &stm32_iwdg_ops; + wdd->min_timeout = ((RLR_MIN + 1) * 256) / wdt->rate; + wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * 256 * 1000) / wdt->rate; + wdd->parent = &pdev->dev; + + watchdog_set_drvdata(wdd, wdt); + watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); + + ret = watchdog_init_timeout(wdd, 0, &pdev->dev); + if (ret) + dev_warn(&pdev->dev, + "unable to set timeout value, using default\n"); + + ret = watchdog_register_device(wdd); + if (ret) { + dev_err(&pdev->dev, "failed to register watchdog device\n"); + goto err; + } + + platform_set_drvdata(pdev, wdt); + + return 0; +err: + clk_disable_unprepare(clk); + + return ret; +} + +static int stm32_iwdg_remove(struct platform_device *pdev) +{ + struct stm32_iwdg *wdt = platform_get_drvdata(pdev); + + watchdog_unregister_device(&wdt->wdd); + clk_disable_unprepare(wdt->clk); + + return 0; +} + +static const struct of_device_id stm32_iwdg_of_match[] = { + { .compatible = "st,stm32-iwdg" }, + { /* end node */ } +}; +MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match); + +static struct platform_driver stm32_iwdg_driver = { + .probe = stm32_iwdg_probe, + .remove = stm32_iwdg_remove, + .driver = { + .name = "iwdg", + .of_match_table = stm32_iwdg_of_match, + }, +}; +module_platform_driver(stm32_iwdg_driver); + +MODULE_AUTHOR("Yannick Fertre "); +MODULE_DESCRIPTION("STMicroelectronics STM32 Independent Watchdog Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 166fbcf88fdafa02f784ec25ac64745c716b2de0 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Mon, 17 Apr 2017 22:37:05 +0200 Subject: watchdog: f71808e_wdt: Add F71868 support This adds support for watchdog part of Fintek F71868 Super I/O chip to f71808e_wdt driver. The F71868 chip is, in general, very similar to a F71869, however it has slightly different set of available reset pulse widths. Tested on MSI A55M-P33 motherboard. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 7 ++++--- drivers/watchdog/f71808e_wdt.c | 27 ++++++++++++++++++++------- 2 files changed, 24 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 124e9b87ab6b..2696493c808b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -849,11 +849,12 @@ config EBC_C384_WDT the timeout module parameter. config F71808E_WDT - tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog" + tristate "Fintek F718xx, F818xx Super I/O Watchdog" depends on X86 help - This is the driver for the hardware watchdog on the Fintek - F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers. + This is the driver for the hardware watchdog on the Fintek F71808E, + F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866 + Super I/O controllers. You can compile this driver directly into the kernel, or use it as a module. The module will be called f71808e_wdt. diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 1b7e9169072f..8658dba21768 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -57,6 +57,7 @@ #define SIO_F71808_ID 0x0901 /* Chipset ID */ #define SIO_F71858_ID 0x0507 /* Chipset ID */ #define SIO_F71862_ID 0x0601 /* Chipset ID */ +#define SIO_F71868_ID 0x1106 /* Chipset ID */ #define SIO_F71869_ID 0x0814 /* Chipset ID */ #define SIO_F71869A_ID 0x1007 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ @@ -101,7 +102,7 @@ MODULE_PARM_DESC(timeout, static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH; module_param(pulse_width, uint, 0); MODULE_PARM_DESC(pulse_width, - "Watchdog signal pulse width. 0(=level), 1 ms, 25 ms, 125 ms or 5000 ms" + "Watchdog signal pulse width. 0(=level), 1, 25, 30, 125, 150, 5000 or 6000 ms" " (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")"); static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN; @@ -119,13 +120,14 @@ 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, - f81866}; +enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg, + f81865, f81866}; static const char *f71808e_names[] = { "f71808fg", "f71858fg", "f71862fg", + "f71868", "f71869", "f71882fg", "f71889fg", @@ -252,16 +254,23 @@ static int watchdog_set_timeout(int timeout) static int watchdog_set_pulse_width(unsigned int pw) { int err = 0; + unsigned int t1 = 25, t2 = 125, t3 = 5000; + + if (watchdog.type == f71868) { + t1 = 30; + t2 = 150; + t3 = 6000; + } mutex_lock(&watchdog.lock); - if (pw <= 1) { + if (pw <= 1) { watchdog.pulse_val = 0; - } else if (pw <= 25) { + } else if (pw <= t1) { watchdog.pulse_val = 1; - } else if (pw <= 125) { + } else if (pw <= t2) { watchdog.pulse_val = 2; - } else if (pw <= 5000) { + } else if (pw <= t3) { watchdog.pulse_val = 3; } else { pr_err("pulse width out of range\n"); @@ -354,6 +363,7 @@ static int watchdog_start(void) goto exit_superio; break; + case f71868: case f71869: /* GPIO14 --> WDTRST# */ superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4); @@ -792,6 +802,9 @@ static int __init f71808e_find(int sioaddr) watchdog.type = f71862fg; err = f71862fg_pin_configure(0); /* validate module parameter */ break; + case SIO_F71868_ID: + watchdog.type = f71868; + break; case SIO_F71869_ID: case SIO_F71869A_ID: watchdog.type = f71869; -- cgit v1.2.3 From 2501b015313fe2fa40ed11fa4dd1748e09b7c773 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 12 May 2017 14:05:32 +0200 Subject: watchdog: core: add option to avoid early handling of watchdog On some systems its desirable to have watchdog reboot the system when it does not come up fast enough. This adds a kernel parameter to disable the auto-update of watchdog before userspace takes over and a kernel option to set the default. The info messages were added to shorten error searching on misconfigured systems. Signed-off-by: Sebastian Reichel Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 11 +++++++++++ drivers/watchdog/watchdog_dev.c | 19 ++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 2696493c808b..e9da19627914 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -46,6 +46,17 @@ config WATCHDOG_NOWAYOUT get killed. If you say Y here, the watchdog cannot be stopped once it has been started. +config WATCHDOG_HANDLE_BOOT_ENABLED + bool "Update boot-enabled watchdog until userspace takes over" + default y + help + The default watchdog behaviour (which you get if you say Y here) is + to ping watchdog devices that were enabled before the driver has + been loaded until control is taken over from userspace using the + /dev/watchdog file. If you say N here, the kernel will not update + the watchdog on its own. Thus if your userspace does not start fast + enough your device will reboot. + config WATCHDOG_SYSFS bool "Read different watchdog information through sysfs" help diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index d5d2bbd8f428..4bc7ab60b12c 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -80,6 +80,9 @@ static struct watchdog_core_data *old_wd_data; static struct workqueue_struct *watchdog_wq; +static bool handle_boot_enabled = + IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED); + static inline bool watchdog_need_worker(struct watchdog_device *wdd) { /* All variables in milli-seconds */ @@ -956,9 +959,14 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) * and schedule an immediate ping. */ if (watchdog_hw_running(wdd)) { - __module_get(wdd->ops->owner); - kref_get(&wd_data->kref); - queue_delayed_work(watchdog_wq, &wd_data->work, 0); + if (handle_boot_enabled) { + __module_get(wdd->ops->owner); + kref_get(&wd_data->kref); + queue_delayed_work(watchdog_wq, &wd_data->work, 0); + } else { + pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n", + wdd->id); + } } return 0; @@ -1106,3 +1114,8 @@ void __exit watchdog_dev_exit(void) class_unregister(&watchdog_class); destroy_workqueue(watchdog_wq); } + +module_param(handle_boot_enabled, bool, 0444); +MODULE_PARM_DESC(handle_boot_enabled, + "Watchdog core auto-updates boot enabled watchdogs before userspace takes over (default=" + __MODULE_STRING(IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED)) ")"); -- cgit v1.2.3 From 3a9aedb282acf1499a31834c457335e4535e4a1d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 29 May 2017 16:21:31 -0700 Subject: watchdog: w83627hf: Add support for NCT6793D and NCT6795D Both NCT6793D and NCT6795D are compatible to NCT6792D. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/w83627hf_wdt.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 98fd186c6878..d9ba0496713c 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -49,7 +49,8 @@ static int cr_wdt_csr; /* WDT control & status register */ enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, - w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6102 }; + w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, + nct6795, nct6102 }; static int timeout; /* in seconds */ module_param(timeout, int, 0); @@ -97,6 +98,8 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); #define NCT6779_ID 0xc5 #define NCT6791_ID 0xc8 #define NCT6792_ID 0xc9 +#define NCT6793_ID 0xd1 +#define NCT6795_ID 0xd3 #define W83627HF_WDT_TIMEOUT 0xf6 #define W83697HF_WDT_TIMEOUT 0xf4 @@ -204,6 +207,8 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) case nct6779: case nct6791: case nct6792: + case nct6793: + case nct6795: case nct6102: /* * These chips have a fixed WDTO# output pin (W83627UHG), @@ -396,6 +401,12 @@ static int wdt_find(int addr) case NCT6792_ID: ret = nct6792; break; + case NCT6793_ID: + ret = nct6793; + break; + case NCT6795_ID: + ret = nct6795; + break; case NCT6102_ID: ret = nct6102; cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; @@ -437,6 +448,8 @@ static int __init wdt_init(void) "NCT6779", "NCT6791", "NCT6792", + "NCT6793", + "NCT6795", "NCT6102", }; -- cgit v1.2.3 From 65a3b6935d920a37820226864eb607467e49ba50 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Mon, 22 May 2017 10:51:39 +0200 Subject: watchdog: dw_wdt: get reset lines from dt The dw_wdt has an external reset line, that can keep the device in reset and therefore rendering it useless and also is the only way of stopping the watchdog once it was started. Get the reset lines for this core from the devicetree. As these lines are optional, use devm_reset_control_get_optional_shared. If the reset line is not specified in the devicetree, the reset framework will just skip deasserting and continue. This way all users of the driver will continue to function without any harm, even if the reset line is not specified in the devicetree. Signed-off-by: Steffen Trumtrar Cc: linux-watchdog@vger.kernel.org Reviewed-by: Philipp Zabel Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 914da3a4d334..36be987ff9ef 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #define WDOG_CONTROL_REG_OFFSET 0x00 @@ -54,6 +55,7 @@ struct dw_wdt { struct clk *clk; unsigned long rate; struct watchdog_device wdd; + struct reset_control *rst; }; #define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd) @@ -234,6 +236,14 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) goto out_disable_clk; } + dw_wdt->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); + if (IS_ERR(dw_wdt->rst)) { + ret = PTR_ERR(dw_wdt->rst); + goto out_disable_clk; + } + + reset_control_deassert(dw_wdt->rst); + wdd = &dw_wdt->wdd; wdd->info = &dw_wdt_ident; wdd->ops = &dw_wdt_ops; @@ -279,6 +289,7 @@ static int dw_wdt_drv_remove(struct platform_device *pdev) struct dw_wdt *dw_wdt = platform_get_drvdata(pdev); watchdog_unregister_device(&dw_wdt->wdd); + reset_control_assert(dw_wdt->rst); clk_disable_unprepare(dw_wdt->clk); return 0; -- cgit v1.2.3 From 733403017399f18c1e31db2470b724e2605618b9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 10 Jun 2017 21:04:32 -0700 Subject: watchdog: it87: Drop FSF mailing address The FSF mailing address may change. Drop it. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/it87_wdt.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index b9878c41598f..8c847f8c2470 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -24,10 +24,6 @@ * 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, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -- cgit v1.2.3 From 1d7b80394c48a56c705733cb6c044199ffbf1dfd Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 10 Jun 2017 21:04:33 -0700 Subject: watchdog: it87: Convert to use watchdog core infrastructure Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 1 + drivers/watchdog/it87_wdt.c | 375 +++++++++----------------------------------- 2 files changed, 72 insertions(+), 304 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e9da19627914..d347bb25ac2e 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1069,6 +1069,7 @@ config IT8712F_WDT config IT87_WDT tristate "IT87 Watchdog Timer" depends on X86 + select WATCHDOG_CORE ---help--- This is the driver for the hardware watchdog on the ITE IT8620, IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728 diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index 8c847f8c2470..9a6523e70ffc 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -32,26 +32,18 @@ #include #include #include -#include -#include #include #include #include #include #include -#include #include - -#define WATCHDOG_VERSION "1.14" #define WATCHDOG_NAME "IT87 WDT" -#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" -#define WD_MAGIC 'V' /* Defaults for Module Parameter */ #define DEFAULT_NOGAMEPORT 0 #define DEFAULT_NOCIR 0 -#define DEFAULT_EXCLUSIVE 1 #define DEFAULT_TIMEOUT 60 #define DEFAULT_TESTMODE 0 #define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT @@ -129,23 +121,17 @@ #define GP_BASE_DEFAULT 0x0201 /* wdt_status */ -#define WDTS_TIMER_RUN 0 -#define WDTS_DEV_OPEN 1 -#define WDTS_KEEPALIVE 2 -#define WDTS_LOCKED 3 -#define WDTS_USE_GP 4 -#define WDTS_EXPECTED 5 -#define WDTS_USE_CIR 6 - -static unsigned int base, gpact, ciract, max_units, chip_type; -static unsigned long wdt_status; - -static int nogameport = DEFAULT_NOGAMEPORT; -static int nocir = DEFAULT_NOCIR; -static int exclusive = DEFAULT_EXCLUSIVE; -static int timeout = DEFAULT_TIMEOUT; -static int testmode = DEFAULT_TESTMODE; -static bool nowayout = DEFAULT_NOWAYOUT; +#define WDTS_USE_GP 0 +#define WDTS_USE_CIR 1 + +static unsigned int base, gpact, ciract, max_units, chip_type; +static unsigned long wdt_status; + +static int nogameport = DEFAULT_NOGAMEPORT; +static int nocir = DEFAULT_NOCIR; +static unsigned int timeout = DEFAULT_TIMEOUT; +static int testmode = DEFAULT_TESTMODE; +static bool nowayout = DEFAULT_NOWAYOUT; module_param(nogameport, int, 0); MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default=" @@ -153,9 +139,6 @@ MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default=" module_param(nocir, int, 0); MODULE_PARM_DESC(nocir, "Forbid the use of Consumer IR interrupts to reset timer, default=" __MODULE_STRING(DEFAULT_NOCIR)); -module_param(exclusive, int, 0); -MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default=" - __MODULE_STRING(DEFAULT_EXCLUSIVE)); module_param(timeout, int, 0); MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default=" __MODULE_STRING(DEFAULT_TIMEOUT)); @@ -227,7 +210,7 @@ static inline void superio_outw(int val, int reg) } /* Internal function, should be called after superio_select(GPIO) */ -static void wdt_update_timeout(void) +static void _wdt_update_timeout(void) { unsigned char cfg = WDT_KRST; int tm = timeout; @@ -249,6 +232,21 @@ static void wdt_update_timeout(void) superio_outb(tm>>8, WDTVALMSB); } +static int wdt_update_timeout(void) +{ + int ret; + + ret = superio_enter(); + if (ret) + return ret; + + superio_select(GPIO); + _wdt_update_timeout(); + superio_exit(); + + return 0; +} + static int wdt_round_time(int t) { t += 59; @@ -258,25 +256,22 @@ static int wdt_round_time(int t) /* watchdog timer handling */ -static void wdt_keepalive(void) +static int wdt_keepalive(struct watchdog_device *wdd) { + int ret = 0; + if (test_bit(WDTS_USE_GP, &wdt_status)) inb(base); else if (test_bit(WDTS_USE_CIR, &wdt_status)) /* The timer reloads with around 5 msec delay */ outb(0x55, CIR_DR(base)); - else { - if (superio_enter()) - return; + else + ret = wdt_update_timeout(); - superio_select(GPIO); - wdt_update_timeout(); - superio_exit(); - } - set_bit(WDTS_KEEPALIVE, &wdt_status); + return ret; } -static int wdt_start(void) +static int wdt_start(struct watchdog_device *wdd) { int ret = superio_enter(); if (ret) @@ -287,14 +282,15 @@ static int wdt_start(void) superio_outb(WDT_GAMEPORT, WDTCTRL); else if (test_bit(WDTS_USE_CIR, &wdt_status)) superio_outb(WDT_CIRINT, WDTCTRL); - wdt_update_timeout(); + + _wdt_update_timeout(); superio_exit(); return 0; } -static int wdt_stop(void) +static int wdt_stop(struct watchdog_device *wdd) { int ret = superio_enter(); if (ret) @@ -321,280 +317,49 @@ static int wdt_stop(void) * Used within WDIOC_SETTIMEOUT watchdog device ioctl. */ -static int wdt_set_timeout(int t) +static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) { - if (t < 1 || t > max_units * 60) - return -EINVAL; + int ret = 0; if (t > max_units) - timeout = wdt_round_time(t); - else - timeout = t; - - if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { - int ret = superio_enter(); - if (ret) - return ret; - - superio_select(GPIO); - wdt_update_timeout(); - superio_exit(); - } - return 0; -} - -/** - * wdt_get_status - determines the status supported by watchdog ioctl - * @status: status returned to user space - * - * The status bit of the device does not allow to distinguish - * between a regular system reset and a watchdog forced reset. - * But, in test mode it is useful, so it is supported through - * WDIOC_GETSTATUS watchdog ioctl. Additionally the driver - * reports the keepalive signal and the acception of the magic. - * - * Used within WDIOC_GETSTATUS watchdog device ioctl. - */ - -static int wdt_get_status(int *status) -{ - *status = 0; - if (testmode) { - int ret = superio_enter(); - if (ret) - return ret; - - superio_select(GPIO); - if (superio_inb(WDTCTRL) & WDT_ZERO) { - superio_outb(0x00, WDTCTRL); - clear_bit(WDTS_TIMER_RUN, &wdt_status); - *status |= WDIOF_CARDRESET; - } - - superio_exit(); - } - if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status)) - *status |= WDIOF_KEEPALIVEPING; - if (test_bit(WDTS_EXPECTED, &wdt_status)) - *status |= WDIOF_MAGICCLOSE; - return 0; -} - -/* /dev/watchdog handling */ - -/** - * wdt_open - watchdog file_operations .open - * @inode: inode of the device - * @file: file handle to the device - * - * The watchdog timer starts by opening the device. - * - * Used within the file operation of the watchdog device. - */ - -static int wdt_open(struct inode *inode, struct file *file) -{ - if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status)) - return -EBUSY; - if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { - int ret; - if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status)) - __module_get(THIS_MODULE); - - ret = wdt_start(); - if (ret) { - clear_bit(WDTS_LOCKED, &wdt_status); - clear_bit(WDTS_TIMER_RUN, &wdt_status); - clear_bit(WDTS_DEV_OPEN, &wdt_status); - return ret; - } - } - return nonseekable_open(inode, file); -} + t = wdt_round_time(t); -/** - * wdt_release - watchdog file_operations .release - * @inode: inode of the device - * @file: file handle to the device - * - * Closing the watchdog device either stops the watchdog timer - * or in the case, that nowayout is set or the magic character - * wasn't written, a critical warning about an running watchdog - * timer is given. - * - * Used within the file operation of the watchdog device. - */ + wdd->timeout = t; -static int wdt_release(struct inode *inode, struct file *file) -{ - if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { - if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) { - int ret = wdt_stop(); - if (ret) { - /* - * Stop failed. Just keep the watchdog alive - * and hope nothing bad happens. - */ - set_bit(WDTS_EXPECTED, &wdt_status); - wdt_keepalive(); - return ret; - } - clear_bit(WDTS_TIMER_RUN, &wdt_status); - } else { - wdt_keepalive(); - pr_crit("unexpected close, not stopping watchdog!\n"); - } - } - clear_bit(WDTS_DEV_OPEN, &wdt_status); - return 0; -} - -/** - * wdt_write - watchdog file_operations .write - * @file: file handle to the watchdog - * @buf: buffer to write - * @count: count of bytes - * @ppos: pointer to the position to write. No seeks allowed - * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we don't define content meaning. - * - * Used within the file operation of the watchdog device. - */ + if (watchdog_hw_running(wdd)) + ret = wdt_update_timeout(); -static ssize_t wdt_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - if (count) { - clear_bit(WDTS_EXPECTED, &wdt_status); - wdt_keepalive(); - } - if (!nowayout) { - size_t ofs; - - /* note: just in case someone wrote the magic character long ago */ - for (ofs = 0; ofs != count; ofs++) { - char c; - if (get_user(c, buf + ofs)) - return -EFAULT; - if (c == WD_MAGIC) - set_bit(WDTS_EXPECTED, &wdt_status); - } - } - return count; + return ret; } static const struct watchdog_info ident = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, - .firmware_version = 1, + .firmware_version = 1, .identity = WATCHDOG_NAME, }; -/** - * wdt_ioctl - watchdog file_operations .unlocked_ioctl - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer - * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. - * - * Used within the file operation of the watchdog device. - */ - -static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int rc = 0, status, new_options, new_timeout; - union { - struct watchdog_info __user *ident; - int __user *i; - } uarg; - - uarg.i = (int __user *)arg; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(uarg.ident, - &ident, sizeof(ident)) ? -EFAULT : 0; - - case WDIOC_GETSTATUS: - rc = wdt_get_status(&status); - if (rc) - return rc; - return put_user(status, uarg.i); - - case WDIOC_GETBOOTSTATUS: - return put_user(0, uarg.i); - - case WDIOC_KEEPALIVE: - wdt_keepalive(); - return 0; - - case WDIOC_SETOPTIONS: - if (get_user(new_options, uarg.i)) - return -EFAULT; - - switch (new_options) { - case WDIOS_DISABLECARD: - if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { - rc = wdt_stop(); - if (rc) - return rc; - } - clear_bit(WDTS_TIMER_RUN, &wdt_status); - return 0; - - case WDIOS_ENABLECARD: - if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { - rc = wdt_start(); - if (rc) { - clear_bit(WDTS_TIMER_RUN, &wdt_status); - return rc; - } - } - return 0; - - default: - return -EFAULT; - } - - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, uarg.i)) - return -EFAULT; - rc = wdt_set_timeout(new_timeout); - case WDIOC_GETTIMEOUT: - if (put_user(timeout, uarg.i)) - return -EFAULT; - return rc; +static struct watchdog_ops wdt_ops = { + .owner = THIS_MODULE, + .start = wdt_start, + .stop = wdt_stop, + .ping = wdt_keepalive, + .set_timeout = wdt_set_timeout, +}; - default: - return -ENOTTY; - } -} +static struct watchdog_device wdt_dev = { + .info = &ident, + .ops = &wdt_ops, + .min_timeout = 1, +}; static int wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) + void *unused) { if (code == SYS_DOWN || code == SYS_HALT) - wdt_stop(); + wdt_stop(&wdt_dev); return NOTIFY_DONE; } -static const struct file_operations wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = wdt_write, - .unlocked_ioctl = wdt_ioctl, - .open = wdt_open, - .release = wdt_release, -}; - -static struct miscdevice wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &wdt_fops, -}; - static struct notifier_block wdt_notifier = { .notifier_call = wdt_notify_sys, }; @@ -708,19 +473,15 @@ static int __init it87_wdt_init(void) if (timeout > max_units) timeout = wdt_round_time(timeout); + wdt_dev.timeout = timeout; + wdt_dev.max_timeout = max_units * 60; + rc = register_reboot_notifier(&wdt_notifier); if (rc) { pr_err("Cannot register reboot notifier (err=%d)\n", rc); goto err_out_region; } - rc = misc_register(&wdt_miscdev); - if (rc) { - pr_err("Cannot register miscdev on minor=%d (err=%d)\n", - wdt_miscdev.minor, rc); - goto err_out_reboot; - } - /* Initialize CIR to use it as keepalive source */ if (test_bit(WDTS_USE_CIR, &wdt_status)) { outb(0x00, CIR_RCR(base)); @@ -732,9 +493,15 @@ static int __init it87_wdt_init(void) outb(0x09, CIR_IER(base)); } - pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d nocir=%d)\n", + rc = watchdog_register_device(&wdt_dev); + if (rc) { + pr_err("Cannot register watchdog device (err=%d)\n", rc); + goto err_out_reboot; + } + + pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d nogameport=%d nocir=%d)\n", chip_type, chip_rev, timeout, - nowayout, testmode, exclusive, nogameport, nocir); + nowayout, testmode, nogameport, nocir); superio_exit(); return 0; @@ -778,7 +545,7 @@ static void __exit it87_wdt_exit(void) superio_exit(); } - misc_deregister(&wdt_miscdev); + watchdog_unregister_device(&wdt_dev); unregister_reboot_notifier(&wdt_notifier); if (test_bit(WDTS_USE_GP, &wdt_status)) -- cgit v1.2.3 From 893dc8b5c9785e9ecf5ddfebbd78da6eddcdbac6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 10 Jun 2017 21:04:34 -0700 Subject: watchdog: it87: Drop support for resetting watchdog though CIR and Game port Resetting the watchdog timer on CIR interrupts or on game port interrupts is not not supported on recent chips, and doesn't really tell if the system is stable. On top of that, at least the bit to enable resetting the watchdog through the game port is used differently on recent chips. Drop resetting the watchdog on CIR or game port interrupts to simplify the code. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 3 - drivers/watchdog/it87_wdt.c | 226 ++++---------------------------------------- 2 files changed, 19 insertions(+), 210 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d347bb25ac2e..e5749515d3ce 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1075,9 +1075,6 @@ config IT87_WDT IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728 Super I/O chips. - If the driver does not work, then make sure that the game port in - the BIOS is enabled. - This watchdog simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain amount of time. diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index 9a6523e70ffc..29aea28628c8 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -42,8 +41,6 @@ #define WATCHDOG_NAME "IT87 WDT" /* Defaults for Module Parameter */ -#define DEFAULT_NOGAMEPORT 0 -#define DEFAULT_NOCIR 0 #define DEFAULT_TIMEOUT 60 #define DEFAULT_TESTMODE 0 #define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT @@ -54,15 +51,11 @@ /* Logical device Numbers LDN */ #define GPIO 0x07 -#define GAMEPORT 0x09 -#define CIR 0x0a /* Configuration Registers and Functions */ #define LDNREG 0x07 #define CHIPID 0x20 #define CHIPREV 0x22 -#define ACTREG 0x30 -#define BASEREG 0x60 /* Chip Id numbers */ #define NO_DEV_ID 0xffff @@ -84,14 +77,6 @@ #define WDTVALLSB 0x73 #define WDTVALMSB 0x74 -/* GPIO Bits WDTCTRL */ -#define WDT_CIRINT 0x80 -#define WDT_MOUSEINT 0x40 -#define WDT_KYBINT 0x20 -#define WDT_GAMEPORT 0x10 /* not in it8718, it8720, it8721, it8728 */ -#define WDT_FORCE 0x02 -#define WDT_ZERO 0x01 - /* GPIO Bits WDTCFG */ #define WDT_TOV1 0x80 #define WDT_KRST 0x40 @@ -99,46 +84,12 @@ #define WDT_PWROK 0x10 /* not in it8721 */ #define WDT_INT_MASK 0x0f -/* CIR Configuration Register LDN=0x0a */ -#define CIR_ILS 0x70 - -/* The default Base address is not always available, we use this */ -#define CIR_BASE 0x0208 - -/* CIR Controller */ -#define CIR_DR(b) (b) -#define CIR_IER(b) (b + 1) -#define CIR_RCR(b) (b + 2) -#define CIR_TCR1(b) (b + 3) -#define CIR_TCR2(b) (b + 4) -#define CIR_TSR(b) (b + 5) -#define CIR_RSR(b) (b + 6) -#define CIR_BDLR(b) (b + 5) -#define CIR_BDHR(b) (b + 6) -#define CIR_IIR(b) (b + 7) - -/* Default Base address of Game port */ -#define GP_BASE_DEFAULT 0x0201 +static unsigned int max_units, chip_type; -/* wdt_status */ -#define WDTS_USE_GP 0 -#define WDTS_USE_CIR 1 - -static unsigned int base, gpact, ciract, max_units, chip_type; -static unsigned long wdt_status; - -static int nogameport = DEFAULT_NOGAMEPORT; -static int nocir = DEFAULT_NOCIR; static unsigned int timeout = DEFAULT_TIMEOUT; -static int testmode = DEFAULT_TESTMODE; -static bool nowayout = DEFAULT_NOWAYOUT; - -module_param(nogameport, int, 0); -MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default=" - __MODULE_STRING(DEFAULT_NOGAMEPORT)); -module_param(nocir, int, 0); -MODULE_PARM_DESC(nocir, "Forbid the use of Consumer IR interrupts to reset timer, default=" - __MODULE_STRING(DEFAULT_NOCIR)); +static int testmode = DEFAULT_TESTMODE; +static bool nowayout = DEFAULT_NOWAYOUT; + module_param(timeout, int, 0); MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default=" __MODULE_STRING(DEFAULT_TIMEOUT)); @@ -210,29 +161,28 @@ static inline void superio_outw(int val, int reg) } /* Internal function, should be called after superio_select(GPIO) */ -static void _wdt_update_timeout(void) +static void _wdt_update_timeout(unsigned int t) { unsigned char cfg = WDT_KRST; - int tm = timeout; if (testmode) cfg = 0; - if (tm <= max_units) + if (t <= max_units) cfg |= WDT_TOV1; else - tm /= 60; + t /= 60; if (chip_type != IT8721_ID) cfg |= WDT_PWROK; superio_outb(cfg, WDTCFG); - superio_outb(tm, WDTVALLSB); + superio_outb(t, WDTVALLSB); if (max_units > 255) - superio_outb(tm>>8, WDTVALMSB); + superio_outb(t >> 8, WDTVALMSB); } -static int wdt_update_timeout(void) +static int wdt_update_timeout(unsigned int t) { int ret; @@ -241,7 +191,7 @@ static int wdt_update_timeout(void) return ret; superio_select(GPIO); - _wdt_update_timeout(); + _wdt_update_timeout(t); superio_exit(); return 0; @@ -256,55 +206,14 @@ static int wdt_round_time(int t) /* watchdog timer handling */ -static int wdt_keepalive(struct watchdog_device *wdd) -{ - int ret = 0; - - if (test_bit(WDTS_USE_GP, &wdt_status)) - inb(base); - else if (test_bit(WDTS_USE_CIR, &wdt_status)) - /* The timer reloads with around 5 msec delay */ - outb(0x55, CIR_DR(base)); - else - ret = wdt_update_timeout(); - - return ret; -} - static int wdt_start(struct watchdog_device *wdd) { - int ret = superio_enter(); - if (ret) - return ret; - - superio_select(GPIO); - if (test_bit(WDTS_USE_GP, &wdt_status)) - superio_outb(WDT_GAMEPORT, WDTCTRL); - else if (test_bit(WDTS_USE_CIR, &wdt_status)) - superio_outb(WDT_CIRINT, WDTCTRL); - - _wdt_update_timeout(); - - superio_exit(); - - return 0; + return wdt_update_timeout(wdd->timeout); } static int wdt_stop(struct watchdog_device *wdd) { - int ret = superio_enter(); - if (ret) - return ret; - - superio_select(GPIO); - superio_outb(0x00, WDTCTRL); - superio_outb(WDT_TOV1, WDTCFG); - superio_outb(0x00, WDTVALLSB); - if (max_units > 255) - superio_outb(0x00, WDTVALMSB); - - superio_exit(); - return 0; + return wdt_update_timeout(0); } /** @@ -327,7 +236,7 @@ static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) wdd->timeout = t; if (watchdog_hw_running(wdd)) - ret = wdt_update_timeout(); + ret = wdt_update_timeout(t); return ret; } @@ -342,7 +251,6 @@ static struct watchdog_ops wdt_ops = { .owner = THIS_MODULE, .start = wdt_start, .stop = wdt_stop, - .ping = wdt_keepalive, .set_timeout = wdt_set_timeout, }; @@ -366,12 +274,8 @@ static struct notifier_block wdt_notifier = { static int __init it87_wdt_init(void) { - int rc = 0; - int try_gameport = !nogameport; u8 chip_rev; - int gp_rreq_fail = 0; - - wdt_status = 0; + int rc; rc = superio_enter(); if (rc) @@ -399,7 +303,6 @@ static int __init it87_wdt_init(void) case IT8728_ID: case IT8783_ID: max_units = 65535; - try_gameport = 0; break; case IT8705_ID: pr_err("Unsupported Chip found, Chip %04x Revision %02x\n", @@ -421,48 +324,7 @@ static int __init it87_wdt_init(void) superio_select(GPIO); superio_outb(WDT_TOV1, WDTCFG); superio_outb(0x00, WDTCTRL); - - /* First try to get Gameport support */ - if (try_gameport) { - superio_select(GAMEPORT); - base = superio_inw(BASEREG); - if (!base) { - base = GP_BASE_DEFAULT; - superio_outw(base, BASEREG); - } - gpact = superio_inb(ACTREG); - superio_outb(0x01, ACTREG); - if (request_region(base, 1, WATCHDOG_NAME)) - set_bit(WDTS_USE_GP, &wdt_status); - else - gp_rreq_fail = 1; - } - - /* If we haven't Gameport support, try to get CIR support */ - if (!nocir && !test_bit(WDTS_USE_GP, &wdt_status)) { - if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { - if (gp_rreq_fail) - pr_err("I/O Address 0x%04x and 0x%04x already in use\n", - base, CIR_BASE); - else - pr_err("I/O Address 0x%04x already in use\n", - CIR_BASE); - rc = -EIO; - goto err_out; - } - base = CIR_BASE; - - superio_select(CIR); - superio_outw(base, BASEREG); - superio_outb(0x00, CIR_ILS); - ciract = superio_inb(ACTREG); - superio_outb(0x01, ACTREG); - if (gp_rreq_fail) { - superio_select(GAMEPORT); - superio_outb(gpact, ACTREG); - } - set_bit(WDTS_USE_CIR, &wdt_status); - } + superio_exit(); if (timeout < 1 || timeout > max_units * 60) { timeout = DEFAULT_TIMEOUT; @@ -479,18 +341,7 @@ static int __init it87_wdt_init(void) rc = register_reboot_notifier(&wdt_notifier); if (rc) { pr_err("Cannot register reboot notifier (err=%d)\n", rc); - goto err_out_region; - } - - /* Initialize CIR to use it as keepalive source */ - if (test_bit(WDTS_USE_CIR, &wdt_status)) { - outb(0x00, CIR_RCR(base)); - outb(0xc0, CIR_TCR1(base)); - outb(0x5c, CIR_TCR2(base)); - outb(0x10, CIR_IER(base)); - outb(0x00, CIR_BDHR(base)); - outb(0x01, CIR_BDLR(base)); - outb(0x09, CIR_IER(base)); + return rc; } rc = watchdog_register_device(&wdt_dev); @@ -499,59 +350,20 @@ static int __init it87_wdt_init(void) goto err_out_reboot; } - pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d nogameport=%d nocir=%d)\n", - chip_type, chip_rev, timeout, - nowayout, testmode, nogameport, nocir); + pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n", + chip_type, chip_rev, timeout, nowayout, testmode); - superio_exit(); return 0; err_out_reboot: unregister_reboot_notifier(&wdt_notifier); -err_out_region: - if (test_bit(WDTS_USE_GP, &wdt_status)) - release_region(base, 1); - else if (test_bit(WDTS_USE_CIR, &wdt_status)) { - release_region(base, 8); - superio_select(CIR); - superio_outb(ciract, ACTREG); - } -err_out: - if (try_gameport) { - superio_select(GAMEPORT); - superio_outb(gpact, ACTREG); - } - - superio_exit(); return rc; } static void __exit it87_wdt_exit(void) { - if (superio_enter() == 0) { - superio_select(GPIO); - superio_outb(0x00, WDTCTRL); - superio_outb(0x00, WDTCFG); - superio_outb(0x00, WDTVALLSB); - if (max_units > 255) - superio_outb(0x00, WDTVALMSB); - if (test_bit(WDTS_USE_GP, &wdt_status)) { - superio_select(GAMEPORT); - superio_outb(gpact, ACTREG); - } else if (test_bit(WDTS_USE_CIR, &wdt_status)) { - superio_select(CIR); - superio_outb(ciract, ACTREG); - } - superio_exit(); - } - watchdog_unregister_device(&wdt_dev); unregister_reboot_notifier(&wdt_notifier); - - if (test_bit(WDTS_USE_GP, &wdt_status)) - release_region(base, 1); - else if (test_bit(WDTS_USE_CIR, &wdt_status)) - release_region(base, 8); } module_init(it87_wdt_init); -- cgit v1.2.3 From 1123c514b11201758674e2b34f297b94509d81db Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 10 Jun 2017 21:04:35 -0700 Subject: watchdog: it87: Use infrastructure to stop watchdog on reboot Use watchdog_stop_on_reboot() to stop the watchdog on reboot instead of registering a driver-specific notifier. While at it, reorder remaining include files alphabetically. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/it87_wdt.c | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index 29aea28628c8..07f4727eb7f4 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -28,15 +28,13 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include #include #include #include -#include -#include #include -#include -#include -#include #define WATCHDOG_NAME "IT87 WDT" @@ -260,18 +258,6 @@ static struct watchdog_device wdt_dev = { .min_timeout = 1, }; -static int wdt_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - wdt_stop(&wdt_dev); - return NOTIFY_DONE; -} - -static struct notifier_block wdt_notifier = { - .notifier_call = wdt_notify_sys, -}; - static int __init it87_wdt_init(void) { u8 chip_rev; @@ -338,32 +324,22 @@ static int __init it87_wdt_init(void) wdt_dev.timeout = timeout; wdt_dev.max_timeout = max_units * 60; - rc = register_reboot_notifier(&wdt_notifier); - if (rc) { - pr_err("Cannot register reboot notifier (err=%d)\n", rc); - return rc; - } - + watchdog_stop_on_reboot(&wdt_dev); rc = watchdog_register_device(&wdt_dev); if (rc) { pr_err("Cannot register watchdog device (err=%d)\n", rc); - goto err_out_reboot; + return rc; } pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n", chip_type, chip_rev, timeout, nowayout, testmode); return 0; - -err_out_reboot: - unregister_reboot_notifier(&wdt_notifier); - return rc; } static void __exit it87_wdt_exit(void) { watchdog_unregister_device(&wdt_dev); - unregister_reboot_notifier(&wdt_notifier); } module_init(it87_wdt_init); -- cgit v1.2.3 From cddda07c7b31828f08c18f5898df0e457d280ada Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 10 Jun 2017 21:04:36 -0700 Subject: watchdog: it87: Add support for various Super-IO chips Add support for IT8607, IT8622, IT8625, IT8628, IT8655, IT8665, and IT8686. Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 7 ++++--- drivers/watchdog/it87_wdt.c | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e5749515d3ce..5ca9fb7c1180 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1071,9 +1071,10 @@ config IT87_WDT depends on X86 select WATCHDOG_CORE ---help--- - This is the driver for the hardware watchdog on the ITE IT8620, - IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728 - Super I/O chips. + This is the driver for the hardware watchdog on the ITE IT8607, + IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686, IT8702, + IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728, and + IT8783 Super I/O chips. This watchdog simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index 07f4727eb7f4..dd1e7eaef50f 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -12,8 +12,9 @@ * http://www.ite.com.tw/ * * Support of the watchdog timers, which are available on - * IT8620, IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, - * IT8728 and IT8783. + * IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686, + * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728, + * and IT8783. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -57,7 +58,14 @@ /* Chip Id numbers */ #define NO_DEV_ID 0xffff +#define IT8607_ID 0x8607 #define IT8620_ID 0x8620 +#define IT8622_ID 0x8622 +#define IT8625_ID 0x8625 +#define IT8628_ID 0x8628 +#define IT8655_ID 0x8655 +#define IT8665_ID 0x8665 +#define IT8686_ID 0x8686 #define IT8702_ID 0x8702 #define IT8705_ID 0x8705 #define IT8712_ID 0x8712 @@ -282,7 +290,14 @@ static int __init it87_wdt_init(void) case IT8726_ID: max_units = 65535; break; + case IT8607_ID: case IT8620_ID: + case IT8622_ID: + case IT8625_ID: + case IT8628_ID: + case IT8655_ID: + case IT8665_ID: + case IT8686_ID: case IT8718_ID: case IT8720_ID: case IT8721_ID: -- cgit v1.2.3 From 65360944c19814a6d3c0a58c5a983f7198d29c51 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 7 Jun 2017 15:04:15 +0530 Subject: watchdog: meson: Handle return value of clk_prepare_enable clk_prepare_enable() can fail here and we must check its return value. Signed-off-by: Arvind Yadav Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/meson_gxbb_wdt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c index 45d47664a00a..69a5a57f1446 100644 --- a/drivers/watchdog/meson_gxbb_wdt.c +++ b/drivers/watchdog/meson_gxbb_wdt.c @@ -203,7 +203,9 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev) if (IS_ERR(data->clk)) return PTR_ERR(data->clk); - clk_prepare_enable(data->clk); + ret = clk_prepare_enable(data->clk); + if (ret) + return ret; platform_set_drvdata(pdev, data); -- cgit v1.2.3 From 8f11eb58ad5747bab622910cc2efaa45e6a21c1f Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 6 Jun 2017 15:47:53 +0530 Subject: watchdog: davinci: Handle return value of clk_prepare_enable clk_prepare_enable() can fail here and we must check its return value. Signed-off-by: Arvind Yadav Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/davinci_wdt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 0e731d797a2a..1ba9ead7d75a 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -173,7 +173,11 @@ static int davinci_wdt_probe(struct platform_device *pdev) return PTR_ERR(davinci_wdt->clk); } - clk_prepare_enable(davinci_wdt->clk); + ret = clk_prepare_enable(davinci_wdt->clk); + if (ret) { + dev_err(&pdev->dev, "failed to prepare clock\n"); + return ret; + } platform_set_drvdata(pdev, davinci_wdt); -- cgit v1.2.3 From 737bcff5faaeea121ccf7c5885507d438c60f06b Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 6 Jun 2017 16:08:31 +0530 Subject: watchdog: davinci: Add missing clk_disable_unprepare(). davinci_wdt_probe() can fail here and we must disable clock. Signed-off-by: Arvind Yadav Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/davinci_wdt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 1ba9ead7d75a..2f46487af86d 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -202,8 +202,10 @@ static int davinci_wdt_probe(struct platform_device *pdev) return PTR_ERR(davinci_wdt->base); ret = watchdog_register_device(wdd); - if (ret < 0) + if (ret < 0) { + clk_disable_unprepare(davinci_wdt->clk); dev_err(dev, "cannot register watchdog device\n"); + } return ret; } -- cgit v1.2.3 From d34f1f4800439c8330f05e9f1133317f3b1f8c38 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 12 Jun 2017 11:50:26 +0530 Subject: watchdog: bcm47xx_wdt: constify bcm47xx_wdt_hard_ops and bcm47xx_wdt_soft_ops File size before: text data bss dec hex filename 1282 388 1 1671 687 drivers/watchdog/bcm47xx_wdt.o File size After adding 'const': text data bss dec hex filename 1474 196 1 1671 687 drivers/watchdog/bcm47xx_wdt.o Signed-off-by: Arvind Yadav Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/bcm47xx_wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index 35725e21b18a..236582809336 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -97,7 +97,7 @@ static int bcm47xx_wdt_restart(struct watchdog_device *wdd, return 0; } -static struct watchdog_ops bcm47xx_wdt_hard_ops = { +static const struct watchdog_ops bcm47xx_wdt_hard_ops = { .owner = THIS_MODULE, .start = bcm47xx_wdt_hard_start, .stop = bcm47xx_wdt_hard_stop, @@ -168,7 +168,7 @@ static const struct watchdog_info bcm47xx_wdt_info = { WDIOF_MAGICCLOSE, }; -static struct watchdog_ops bcm47xx_wdt_soft_ops = { +static const struct watchdog_ops bcm47xx_wdt_soft_ops = { .owner = THIS_MODULE, .start = bcm47xx_wdt_soft_start, .stop = bcm47xx_wdt_soft_stop, -- cgit v1.2.3 From 935988c67406f35e63873ac63e2962664ee99bdc Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 12 Jun 2017 12:02:39 +0530 Subject: watchdog: zx2967: constify zx2967_wdt_ops. File size before: text data bss dec hex filename 988 288 0 1276 4fc drivers/watchdog/zx2967_wdt.o File size After adding 'const': text data bss dec hex filename 1084 192 0 1276 4fc drivers/watchdog/zx2967_wdt.o Signed-off-by: Arvind Yadav Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/zx2967_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/zx2967_wdt.c b/drivers/watchdog/zx2967_wdt.c index c98252733c30..69ec5855584b 100644 --- a/drivers/watchdog/zx2967_wdt.c +++ b/drivers/watchdog/zx2967_wdt.c @@ -154,7 +154,7 @@ static const struct watchdog_info zx2967_wdt_ident = { .identity = "zx2967 watchdog", }; -static struct watchdog_ops zx2967_wdt_ops = { +static const struct watchdog_ops zx2967_wdt_ops = { .owner = THIS_MODULE, .start = zx2967_wdt_start, .stop = zx2967_wdt_stop, -- cgit v1.2.3 From 011e29e7d93d80c2529ba17109bc4e5d031ea2b1 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 20 Jun 2017 11:04:26 +0530 Subject: watchdog: cadence_wdt: make of_device_ids const. of_device_ids are not supposed to change at runtime. All functions working with of_device_ids provided by work with const of_device_ids. So mark the non-const structs as const. File size before: text data bss dec hex filename 1962 612 4 2578 a12 drivers/watchdog/cadence_wdt.o File size after constify cdns_wdt_of_match: text data bss dec hex filename 2378 196 4 2578 a12 drivers/watchdog/cadence_wdt.o Signed-off-by: Arvind Yadav Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/cadence_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index 86e0b5d2e761..05c000081e9d 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -458,7 +458,7 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume); -static struct of_device_id cdns_wdt_of_match[] = { +static const struct of_device_id cdns_wdt_of_match[] = { { .compatible = "cdns,wdt-r1p2", }, { /* end of table */ } }; -- cgit v1.2.3 From e7bf02895f06c0603af800c4bfce3ca4ede9147d Mon Sep 17 00:00:00 2001 From: Keiji Hayashibara Date: Wed, 14 Jun 2017 16:53:44 +0900 Subject: watchdog: uniphier: add UniPhier watchdog driver Add a watchdog driver for Socionext UniPhier series SoC. Note that the timeout value for this device must be a power of 2 because of the specification. Signed-off-by: Keiji Hayashibara Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/watchdog-parameters.txt | 6 + drivers/watchdog/Kconfig | 12 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/uniphier_wdt.c | 268 +++++++++++++++++++++++++ 4 files changed, 287 insertions(+) create mode 100644 drivers/watchdog/uniphier_wdt.c (limited to 'drivers') diff --git a/Documentation/watchdog/watchdog-parameters.txt b/Documentation/watchdog/watchdog-parameters.txt index 914518aeb972..b3526365ea8e 100644 --- a/Documentation/watchdog/watchdog-parameters.txt +++ b/Documentation/watchdog/watchdog-parameters.txt @@ -369,6 +369,12 @@ timeout: Watchdog timeout in seconds. (0 +#include +#include +#include +#include +#include +#include + +/* WDT timer setting register */ +#define WDTTIMSET 0x3004 +#define WDTTIMSET_PERIOD_MASK (0xf << 0) +#define WDTTIMSET_PERIOD_1_SEC (0x3 << 0) + +/* WDT reset selection register */ +#define WDTRSTSEL 0x3008 +#define WDTRSTSEL_RSTSEL_MASK (0x3 << 0) +#define WDTRSTSEL_RSTSEL_BOTH (0x0 << 0) +#define WDTRSTSEL_RSTSEL_IRQ_ONLY (0x2 << 0) + +/* WDT control register */ +#define WDTCTRL 0x300c +#define WDTCTRL_STATUS BIT(8) +#define WDTCTRL_CLEAR BIT(1) +#define WDTCTRL_ENABLE BIT(0) + +#define SEC_TO_WDTTIMSET_PRD(sec) \ + (ilog2(sec) + WDTTIMSET_PERIOD_1_SEC) + +#define WDTST_TIMEOUT 1000 /* usec */ + +#define WDT_DEFAULT_TIMEOUT 64 /* Default is 64 seconds */ +#define WDT_PERIOD_MIN 1 +#define WDT_PERIOD_MAX 128 + +static unsigned int timeout = 0; +static bool nowayout = WATCHDOG_NOWAYOUT; + +struct uniphier_wdt_dev { + struct watchdog_device wdt_dev; + struct regmap *regmap; +}; + +/* + * UniPhier Watchdog operations + */ +static int uniphier_watchdog_ping(struct watchdog_device *w) +{ + struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w); + unsigned int val; + int ret; + + /* Clear counter */ + ret = regmap_write_bits(wdev->regmap, WDTCTRL, + WDTCTRL_CLEAR, WDTCTRL_CLEAR); + if (!ret) + /* + * As SoC specification, after clear counter, + * it needs to wait until counter status is 1. + */ + ret = regmap_read_poll_timeout(wdev->regmap, WDTCTRL, val, + (val & WDTCTRL_STATUS), + 0, WDTST_TIMEOUT); + + return ret; +} + +static int __uniphier_watchdog_start(struct regmap *regmap, unsigned int sec) +{ + unsigned int val; + int ret; + + ret = regmap_read_poll_timeout(regmap, WDTCTRL, val, + !(val & WDTCTRL_STATUS), + 0, WDTST_TIMEOUT); + if (ret) + return ret; + + /* Setup period */ + ret = regmap_write(regmap, WDTTIMSET, + SEC_TO_WDTTIMSET_PRD(sec)); + if (ret) + return ret; + + /* Enable and clear watchdog */ + ret = regmap_write(regmap, WDTCTRL, WDTCTRL_ENABLE | WDTCTRL_CLEAR); + if (!ret) + /* + * As SoC specification, after clear counter, + * it needs to wait until counter status is 1. + */ + ret = regmap_read_poll_timeout(regmap, WDTCTRL, val, + (val & WDTCTRL_STATUS), + 0, WDTST_TIMEOUT); + + return ret; +} + +static int __uniphier_watchdog_stop(struct regmap *regmap) +{ + /* Disable and stop watchdog */ + return regmap_write_bits(regmap, WDTCTRL, WDTCTRL_ENABLE, 0); +} + +static int __uniphier_watchdog_restart(struct regmap *regmap, unsigned int sec) +{ + int ret; + + ret = __uniphier_watchdog_stop(regmap); + if (ret) + return ret; + + return __uniphier_watchdog_start(regmap, sec); +} + +static int uniphier_watchdog_start(struct watchdog_device *w) +{ + struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w); + unsigned int tmp_timeout; + + tmp_timeout = roundup_pow_of_two(w->timeout); + + return __uniphier_watchdog_start(wdev->regmap, tmp_timeout); +} + +static int uniphier_watchdog_stop(struct watchdog_device *w) +{ + struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w); + + return __uniphier_watchdog_stop(wdev->regmap); +} + +static int uniphier_watchdog_set_timeout(struct watchdog_device *w, + unsigned int t) +{ + struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w); + unsigned int tmp_timeout; + int ret; + + tmp_timeout = roundup_pow_of_two(t); + if (tmp_timeout == w->timeout) + return 0; + + if (watchdog_active(w)) { + ret = __uniphier_watchdog_restart(wdev->regmap, tmp_timeout); + if (ret) + return ret; + } + + w->timeout = tmp_timeout; + + return 0; +} + +/* + * Kernel Interfaces + */ +static const struct watchdog_info uniphier_wdt_info = { + .identity = "uniphier-wdt", + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE | + WDIOF_OVERHEAT, +}; + +static const struct watchdog_ops uniphier_wdt_ops = { + .owner = THIS_MODULE, + .start = uniphier_watchdog_start, + .stop = uniphier_watchdog_stop, + .ping = uniphier_watchdog_ping, + .set_timeout = uniphier_watchdog_set_timeout, +}; + +static int uniphier_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct uniphier_wdt_dev *wdev; + struct regmap *regmap; + struct device_node *parent; + int ret; + + wdev = devm_kzalloc(dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) + return -ENOMEM; + + platform_set_drvdata(pdev, wdev); + + parent = of_get_parent(dev->of_node); /* parent should be syscon node */ + regmap = syscon_node_to_regmap(parent); + of_node_put(parent); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + wdev->regmap = regmap; + wdev->wdt_dev.info = &uniphier_wdt_info; + wdev->wdt_dev.ops = &uniphier_wdt_ops; + wdev->wdt_dev.max_timeout = WDT_PERIOD_MAX; + wdev->wdt_dev.min_timeout = WDT_PERIOD_MIN; + wdev->wdt_dev.parent = dev; + + if (watchdog_init_timeout(&wdev->wdt_dev, timeout, dev) < 0) { + wdev->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT; + } + watchdog_set_nowayout(&wdev->wdt_dev, nowayout); + watchdog_stop_on_reboot(&wdev->wdt_dev); + + watchdog_set_drvdata(&wdev->wdt_dev, wdev); + + uniphier_watchdog_stop(&wdev->wdt_dev); + ret = regmap_write(wdev->regmap, WDTRSTSEL, WDTRSTSEL_RSTSEL_BOTH); + if (ret) + return ret; + + ret = devm_watchdog_register_device(dev, &wdev->wdt_dev); + if (ret) + return ret; + + dev_info(dev, "watchdog driver (timeout=%d sec, nowayout=%d)\n", + wdev->wdt_dev.timeout, nowayout); + + return 0; +} + +static const struct of_device_id uniphier_wdt_dt_ids[] = { + { .compatible = "socionext,uniphier-wdt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, uniphier_wdt_dt_ids); + +static struct platform_driver uniphier_wdt_driver = { + .probe = uniphier_wdt_probe, + .driver = { + .name = "uniphier-wdt", + .of_match_table = uniphier_wdt_dt_ids, + }, +}; + +module_platform_driver(uniphier_wdt_driver); + +module_param(timeout, uint, 0000); +MODULE_PARM_DESC(timeout, + "Watchdog timeout seconds in power of 2. (0 < timeout < 128, default=" + __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")"); + +module_param(nowayout, bool, 0000); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_AUTHOR("Keiji Hayashibara "); +MODULE_DESCRIPTION("UniPhier Watchdog Device Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From c013b65ad8a1e132f733404809afc72f7d00e768 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 30 May 2017 10:56:45 +0200 Subject: watchdog: introduce watchdog_worker_should_ping helper This will be useful when the condition becomes slightly more complicated in the next patch. Signed-off-by: Rasmus Villemoes Reviewed-by: Esben Haabendal Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 4bc7ab60b12c..0826e663bd5a 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -195,18 +195,23 @@ static int watchdog_ping(struct watchdog_device *wdd) return __watchdog_ping(wdd); } +static bool watchdog_worker_should_ping(struct watchdog_core_data *wd_data) +{ + struct watchdog_device *wdd = wd_data->wdd; + + return wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd)); +} + static void watchdog_ping_work(struct work_struct *work) { struct watchdog_core_data *wd_data; - struct watchdog_device *wdd; wd_data = container_of(to_delayed_work(work), struct watchdog_core_data, work); mutex_lock(&wd_data->lock); - wdd = wd_data->wdd; - if (wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd))) - __watchdog_ping(wdd); + if (watchdog_worker_should_ping(wd_data)) + __watchdog_ping(wd_data->wdd); mutex_unlock(&wd_data->lock); } -- cgit v1.2.3