diff options
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 6 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/apple_wdt.c | 25 | ||||
-rw-r--r-- | drivers/watchdog/ar7_wdt.c | 315 | ||||
-rw-r--r-- | drivers/watchdog/aspeed_wdt.c | 11 | ||||
-rw-r--r-- | drivers/watchdog/at91sam9_wdt.c | 20 | ||||
-rw-r--r-- | drivers/watchdog/ath79_wdt.c | 19 | ||||
-rw-r--r-- | drivers/watchdog/gpio_wdt.c | 16 | ||||
-rw-r--r-- | drivers/watchdog/imx7ulp_wdt.c | 8 | ||||
-rw-r--r-- | drivers/watchdog/imx_sc_wdt.c | 5 | ||||
-rw-r--r-- | drivers/watchdog/it87_wdt.c | 8 | ||||
-rw-r--r-- | drivers/watchdog/ixp4xx_wdt.c | 28 | ||||
-rw-r--r-- | drivers/watchdog/marvell_gti_wdt.c | 14 | ||||
-rw-r--r-- | drivers/watchdog/mlx_wdt.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/of_xilinx_wdt.c | 13 | ||||
-rw-r--r-- | drivers/watchdog/sbsa_gwdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/st_lpc_wdt.c | 11 | ||||
-rw-r--r-- | drivers/watchdog/sunplus_wdt.c | 17 | ||||
-rw-r--r-- | drivers/watchdog/wdat_wdt.c | 2 |
19 files changed, 123 insertions, 401 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 8cb6fa45d599..7d22051b15a2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1768,12 +1768,6 @@ config SIBYTE_WDOG To compile this driver as a loadable module, choose M here. The module will be called sb_wdog. -config AR7_WDT - tristate "TI AR7 Watchdog Timer" - depends on AR7 || (MIPS && 32BIT && COMPILE_TEST) - help - Hardware driver for the TI AR7 Watchdog Timer. - config TXX9_WDT tristate "Toshiba TXx9 Watchdog Timer" depends on CPU_TX49XX || (MIPS && COMPILE_TEST) diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 7eab9de311cb..7cbc34514ec1 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -168,7 +168,6 @@ obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_JZ4740_WDT) += jz4740_wdt.o obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o -obj-$(CONFIG_AR7_WDT) += ar7_wdt.o obj-$(CONFIG_TXX9_WDT) += txx9wdt.o obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c index eddeb0fede89..d4f739932f0b 100644 --- a/drivers/watchdog/apple_wdt.c +++ b/drivers/watchdog/apple_wdt.c @@ -173,6 +173,8 @@ static int apple_wdt_probe(struct platform_device *pdev) if (!wdt->clk_rate) return -EINVAL; + platform_set_drvdata(pdev, wdt); + wdt->wdd.ops = &apple_wdt_ops; wdt->wdd.info = &apple_wdt_info; wdt->wdd.max_timeout = U32_MAX / wdt->clk_rate; @@ -190,6 +192,28 @@ static int apple_wdt_probe(struct platform_device *pdev) return devm_watchdog_register_device(dev, &wdt->wdd); } +static int apple_wdt_resume(struct device *dev) +{ + struct apple_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd) || watchdog_hw_running(&wdt->wdd)) + apple_wdt_start(&wdt->wdd); + + return 0; +} + +static int apple_wdt_suspend(struct device *dev) +{ + struct apple_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd) || watchdog_hw_running(&wdt->wdd)) + apple_wdt_stop(&wdt->wdd); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(apple_wdt_pm_ops, apple_wdt_suspend, apple_wdt_resume); + static const struct of_device_id apple_wdt_of_match[] = { { .compatible = "apple,wdt" }, {}, @@ -200,6 +224,7 @@ static struct platform_driver apple_wdt_driver = { .driver = { .name = "apple-watchdog", .of_match_table = apple_wdt_of_match, + .pm = pm_sleep_ptr(&apple_wdt_pm_ops), }, .probe = apple_wdt_probe, }; diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c deleted file mode 100644 index cdcaeb0961ac..000000000000 --- a/drivers/watchdog/ar7_wdt.c +++ /dev/null @@ -1,315 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * drivers/watchdog/ar7_wdt.c - * - * Copyright (C) 2007 Nicolas Thill <nico@openwrt.org> - * Copyright (c) 2005 Enrik Berkhan <Enrik.Berkhan@akk.org> - * - * Some code taken from: - * National Semiconductor SCx200 Watchdog support - * Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/errno.h> -#include <linux/miscdevice.h> -#include <linux/platform_device.h> -#include <linux/watchdog.h> -#include <linux/fs.h> -#include <linux/ioport.h> -#include <linux/io.h> -#include <linux/uaccess.h> -#include <linux/clk.h> - -#include <asm/addrspace.h> -#include <asm/mach-ar7/ar7.h> - -#define LONGNAME "TI AR7 Watchdog Timer" - -MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>"); -MODULE_DESCRIPTION(LONGNAME); -MODULE_LICENSE("GPL"); - -static int margin = 60; -module_param(margin, int, 0); -MODULE_PARM_DESC(margin, "Watchdog margin in seconds"); - -static bool nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); - -#define READ_REG(x) readl((void __iomem *)&(x)) -#define WRITE_REG(x, v) writel((v), (void __iomem *)&(x)) - -struct ar7_wdt { - u32 kick_lock; - u32 kick; - u32 change_lock; - u32 change; - u32 disable_lock; - u32 disable; - u32 prescale_lock; - u32 prescale; -}; - -static unsigned long wdt_is_open; -static unsigned expect_close; -static DEFINE_SPINLOCK(wdt_lock); - -/* XXX currently fixed, allows max margin ~68.72 secs */ -#define prescale_value 0xffff - -/* Pointer to the remapped WDT IO space */ -static struct ar7_wdt *ar7_wdt; - -static struct clk *vbus_clk; - -static void ar7_wdt_kick(u32 value) -{ - WRITE_REG(ar7_wdt->kick_lock, 0x5555); - if ((READ_REG(ar7_wdt->kick_lock) & 3) == 1) { - WRITE_REG(ar7_wdt->kick_lock, 0xaaaa); - if ((READ_REG(ar7_wdt->kick_lock) & 3) == 3) { - WRITE_REG(ar7_wdt->kick, value); - return; - } - } - pr_err("failed to unlock WDT kick reg\n"); -} - -static void ar7_wdt_prescale(u32 value) -{ - WRITE_REG(ar7_wdt->prescale_lock, 0x5a5a); - if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 1) { - WRITE_REG(ar7_wdt->prescale_lock, 0xa5a5); - if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 3) { - WRITE_REG(ar7_wdt->prescale, value); - return; - } - } - pr_err("failed to unlock WDT prescale reg\n"); -} - -static void ar7_wdt_change(u32 value) -{ - WRITE_REG(ar7_wdt->change_lock, 0x6666); - if ((READ_REG(ar7_wdt->change_lock) & 3) == 1) { - WRITE_REG(ar7_wdt->change_lock, 0xbbbb); - if ((READ_REG(ar7_wdt->change_lock) & 3) == 3) { - WRITE_REG(ar7_wdt->change, value); - return; - } - } - pr_err("failed to unlock WDT change reg\n"); -} - -static void ar7_wdt_disable(u32 value) -{ - WRITE_REG(ar7_wdt->disable_lock, 0x7777); - if ((READ_REG(ar7_wdt->disable_lock) & 3) == 1) { - WRITE_REG(ar7_wdt->disable_lock, 0xcccc); - if ((READ_REG(ar7_wdt->disable_lock) & 3) == 2) { - WRITE_REG(ar7_wdt->disable_lock, 0xdddd); - if ((READ_REG(ar7_wdt->disable_lock) & 3) == 3) { - WRITE_REG(ar7_wdt->disable, value); - return; - } - } - } - pr_err("failed to unlock WDT disable reg\n"); -} - -static void ar7_wdt_update_margin(int new_margin) -{ - u32 change; - u32 vbus_rate; - - vbus_rate = clk_get_rate(vbus_clk); - change = new_margin * (vbus_rate / prescale_value); - if (change < 1) - change = 1; - if (change > 0xffff) - change = 0xffff; - ar7_wdt_change(change); - margin = change * prescale_value / vbus_rate; - pr_info("timer margin %d seconds (prescale %d, change %d, freq %d)\n", - margin, prescale_value, change, vbus_rate); -} - -static void ar7_wdt_enable_wdt(void) -{ - pr_debug("enabling watchdog timer\n"); - ar7_wdt_disable(1); - ar7_wdt_kick(1); -} - -static void ar7_wdt_disable_wdt(void) -{ - pr_debug("disabling watchdog timer\n"); - ar7_wdt_disable(0); -} - -static int ar7_wdt_open(struct inode *inode, struct file *file) -{ - /* only allow one at a time */ - if (test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - ar7_wdt_enable_wdt(); - expect_close = 0; - - return stream_open(inode, file); -} - -static int ar7_wdt_release(struct inode *inode, struct file *file) -{ - if (!expect_close) - pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n"); - else if (!nowayout) - ar7_wdt_disable_wdt(); - clear_bit(0, &wdt_is_open); - return 0; -} - -static ssize_t ar7_wdt_write(struct file *file, const char *data, - size_t len, loff_t *ppos) -{ - /* check for a magic close character */ - if (len) { - size_t i; - - spin_lock(&wdt_lock); - ar7_wdt_kick(1); - spin_unlock(&wdt_lock); - - expect_close = 0; - for (i = 0; i < len; ++i) { - char c; - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - expect_close = 1; - } - - } - return len; -} - -static long ar7_wdt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - static const struct watchdog_info ident = { - .identity = LONGNAME, - .firmware_version = 1, - .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE), - }; - int new_margin; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user((struct watchdog_info *)arg, &ident, - sizeof(ident))) - return -EFAULT; - return 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - if (put_user(0, (int *)arg)) - return -EFAULT; - return 0; - case WDIOC_KEEPALIVE: - ar7_wdt_kick(1); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_margin, (int *)arg)) - return -EFAULT; - if (new_margin < 1) - return -EINVAL; - - spin_lock(&wdt_lock); - ar7_wdt_update_margin(new_margin); - ar7_wdt_kick(1); - spin_unlock(&wdt_lock); - fallthrough; - case WDIOC_GETTIMEOUT: - if (put_user(margin, (int *)arg)) - return -EFAULT; - return 0; - default: - return -ENOTTY; - } -} - -static const struct file_operations ar7_wdt_fops = { - .owner = THIS_MODULE, - .write = ar7_wdt_write, - .unlocked_ioctl = ar7_wdt_ioctl, - .compat_ioctl = compat_ptr_ioctl, - .open = ar7_wdt_open, - .release = ar7_wdt_release, - .llseek = no_llseek, -}; - -static struct miscdevice ar7_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ar7_wdt_fops, -}; - -static int ar7_wdt_probe(struct platform_device *pdev) -{ - int rc; - - ar7_wdt = devm_platform_ioremap_resource_byname(pdev, "regs"); - if (IS_ERR(ar7_wdt)) - return PTR_ERR(ar7_wdt); - - vbus_clk = clk_get(NULL, "vbus"); - if (IS_ERR(vbus_clk)) { - pr_err("could not get vbus clock\n"); - return PTR_ERR(vbus_clk); - } - - ar7_wdt_disable_wdt(); - ar7_wdt_prescale(prescale_value); - ar7_wdt_update_margin(margin); - - rc = misc_register(&ar7_wdt_miscdev); - if (rc) { - pr_err("unable to register misc device\n"); - goto out; - } - return 0; - -out: - clk_put(vbus_clk); - vbus_clk = NULL; - return rc; -} - -static void ar7_wdt_remove(struct platform_device *pdev) -{ - misc_deregister(&ar7_wdt_miscdev); - clk_put(vbus_clk); - vbus_clk = NULL; -} - -static void ar7_wdt_shutdown(struct platform_device *pdev) -{ - if (!nowayout) - ar7_wdt_disable_wdt(); -} - -static struct platform_driver ar7_wdt_driver = { - .probe = ar7_wdt_probe, - .remove_new = ar7_wdt_remove, - .shutdown = ar7_wdt_shutdown, - .driver = { - .name = "ar7_wdt", - }, -}; - -module_platform_driver(ar7_wdt_driver); diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index b72a858bbac7..b4773a6aaf8c 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -79,6 +79,8 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table); #define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1) #define WDT_CLEAR_TIMEOUT_STATUS 0x14 #define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0) +#define WDT_RESET_MASK1 0x1c +#define WDT_RESET_MASK2 0x20 /* * WDT_RESET_WIDTH controls the characteristics of the external pulse (if @@ -402,6 +404,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev) if ((of_device_is_compatible(np, "aspeed,ast2500-wdt")) || (of_device_is_compatible(np, "aspeed,ast2600-wdt"))) { + u32 reset_mask[2]; + size_t nrstmask = of_device_is_compatible(np, "aspeed,ast2600-wdt") ? 2 : 1; u32 reg = readl(wdt->base + WDT_RESET_WIDTH); reg &= wdt->cfg->ext_pulse_width_mask; @@ -419,6 +423,13 @@ static int aspeed_wdt_probe(struct platform_device *pdev) reg |= WDT_OPEN_DRAIN_MAGIC; writel(reg, wdt->base + WDT_RESET_WIDTH); + + ret = of_property_read_u32_array(np, "aspeed,reset-mask", reset_mask, nrstmask); + if (!ret) { + writel(reset_mask[0], wdt->base + WDT_RESET_MASK1); + if (nrstmask > 1) + writel(reset_mask[1], wdt->base + WDT_RESET_MASK2); + } } if (!of_property_read_u32(np, "aspeed,ext-pulse-duration", &duration)) { diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index fed7be246442..b111b28acb94 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -348,25 +348,21 @@ static int __init at91wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); - wdt->sclk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(wdt->sclk)) - return PTR_ERR(wdt->sclk); - - err = clk_prepare_enable(wdt->sclk); - if (err) { + wdt->sclk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(wdt->sclk)) { dev_err(&pdev->dev, "Could not enable slow clock\n"); - return err; + return PTR_ERR(wdt->sclk); } if (pdev->dev.of_node) { err = of_at91wdt_init(pdev->dev.of_node, wdt); if (err) - goto err_clk; + return err; } err = at91_wdt_init(pdev, wdt); if (err) - goto err_clk; + return err; platform_set_drvdata(pdev, wdt); @@ -374,11 +370,6 @@ static int __init at91wdt_probe(struct platform_device *pdev) wdt->wdd.timeout, wdt->nowayout); return 0; - -err_clk: - clk_disable_unprepare(wdt->sclk); - - return err; } static int __exit at91wdt_remove(struct platform_device *pdev) @@ -388,7 +379,6 @@ static int __exit at91wdt_remove(struct platform_device *pdev) pr_warn("I quit now, hardware will probably reboot!\n"); del_timer(&wdt->timer); - clk_disable_unprepare(wdt->sclk); return 0; } diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c index b7b705060438..e5cc30622b12 100644 --- a/drivers/watchdog/ath79_wdt.c +++ b/drivers/watchdog/ath79_wdt.c @@ -257,19 +257,13 @@ static int ath79_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt_base)) return PTR_ERR(wdt_base); - wdt_clk = devm_clk_get(&pdev->dev, "wdt"); + wdt_clk = devm_clk_get_enabled(&pdev->dev, "wdt"); if (IS_ERR(wdt_clk)) return PTR_ERR(wdt_clk); - err = clk_prepare_enable(wdt_clk); - if (err) - return err; - wdt_freq = clk_get_rate(wdt_clk); - if (!wdt_freq) { - err = -EINVAL; - goto err_clk_disable; - } + if (!wdt_freq) + return -EINVAL; max_timeout = (0xfffffffful / wdt_freq); if (timeout < 1 || timeout > max_timeout) { @@ -286,20 +280,15 @@ static int ath79_wdt_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "unable to register misc device, err=%d\n", err); - goto err_clk_disable; + return err; } return 0; - -err_clk_disable: - clk_disable_unprepare(wdt_clk); - return err; } static void ath79_wdt_remove(struct platform_device *pdev) { misc_deregister(&ath79_wdt_miscdev); - clk_disable_unprepare(wdt_clk); } static void ath79_wdt_shutdown(struct platform_device *pdev) diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index 0923201ce874..a7b814ea740b 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -5,12 +5,13 @@ * Author: 2013, Alexander Shiyan <shc_work@mail.ru> */ -#include <linux/err.h> #include <linux/delay.h> -#include <linux/module.h> +#include <linux/err.h> #include <linux/gpio/consumer.h> -#include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/watchdog.h> static bool nowayout = WATCHDOG_NOWAYOUT; @@ -106,7 +107,6 @@ static const struct watchdog_ops gpio_wdt_ops = { static int gpio_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; struct gpio_wdt_priv *priv; enum gpiod_flags gflags; unsigned int hw_margin; @@ -119,7 +119,7 @@ static int gpio_wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - ret = of_property_read_string(np, "hw_algo", &algo); + ret = device_property_read_string(dev, "hw_algo", &algo); if (ret) return ret; if (!strcmp(algo, "toggle")) { @@ -136,16 +136,14 @@ static int gpio_wdt_probe(struct platform_device *pdev) if (IS_ERR(priv->gpiod)) return PTR_ERR(priv->gpiod); - ret = of_property_read_u32(np, - "hw_margin_ms", &hw_margin); + ret = device_property_read_u32(dev, "hw_margin_ms", &hw_margin); if (ret) return ret; /* Disallow values lower than 2 and higher than 65535 ms */ if (hw_margin < 2 || hw_margin > 65535) return -EINVAL; - priv->always_running = of_property_read_bool(np, - "always-running"); + priv->always_running = device_property_read_bool(dev, "always-running"); watchdog_set_drvdata(&priv->wdd, priv); diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c index c703586c6e5f..b21d7a74a42d 100644 --- a/drivers/watchdog/imx7ulp_wdt.c +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -23,6 +23,7 @@ #define LPO_CLK_SHIFT 8 #define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT) #define WDOG_CS_EN BIT(7) +#define WDOG_CS_INT_EN BIT(6) #define WDOG_CS_UPDATE BIT(5) #define WDOG_CS_WAIT BIT(1) #define WDOG_CS_STOP BIT(0) @@ -62,6 +63,7 @@ struct imx7ulp_wdt_device { void __iomem *base; struct clk *clk; bool post_rcs_wait; + bool ext_reset; const struct imx_wdt_hw_feature *hw; }; @@ -285,6 +287,9 @@ static int imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout if (wdt->hw->prescaler_enable) val |= WDOG_CS_PRES; + if (wdt->ext_reset) + val |= WDOG_CS_INT_EN; + do { ret = _imx7ulp_wdt_init(wdt, timeout, val); toval = readl(wdt->base + WDOG_TOVAL); @@ -321,6 +326,9 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev) return PTR_ERR(imx7ulp_wdt->clk); } + /* The WDOG may need to do external reset through dedicated pin */ + imx7ulp_wdt->ext_reset = of_property_read_bool(dev->of_node, "fsl,ext-reset-output"); + imx7ulp_wdt->post_rcs_wait = true; if (of_device_is_compatible(dev->of_node, "fsl,imx8ulp-wdt")) { diff --git a/drivers/watchdog/imx_sc_wdt.c b/drivers/watchdog/imx_sc_wdt.c index 8ac021748d16..e51fe1b78518 100644 --- a/drivers/watchdog/imx_sc_wdt.c +++ b/drivers/watchdog/imx_sc_wdt.c @@ -34,6 +34,7 @@ #define SC_IRQ_WDOG 1 #define SC_IRQ_GROUP_WDOG 1 +#define SC_TIMER_ERR_BUSY 10 static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0000); @@ -61,7 +62,9 @@ static int imx_sc_wdt_start(struct watchdog_device *wdog) arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_START_WDOG, 0, 0, 0, 0, 0, 0, &res); - if (res.a0) + + /* Ignore if already enabled(SC_TIMER_ERR_BUSY) */ + if (res.a0 && res.a0 != SC_TIMER_ERR_BUSY) return -EACCES; arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_WDOG_ACT, diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index bb1122909396..e888b1bdd1f2 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -13,9 +13,9 @@ * http://www.ite.com.tw/ * * Support of the watchdog timers, which are available on - * IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686, - * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728, - * IT8772, IT8783 and IT8784. + * IT8607, IT8613, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, + * IT8686, IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, + * IT8728, IT8772, IT8783 and IT8784. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -50,6 +50,7 @@ /* Chip Id numbers */ #define NO_DEV_ID 0xffff #define IT8607_ID 0x8607 +#define IT8613_ID 0x8613 #define IT8620_ID 0x8620 #define IT8622_ID 0x8622 #define IT8625_ID 0x8625 @@ -277,6 +278,7 @@ static int __init it87_wdt_init(void) max_units = 65535; break; case IT8607_ID: + case IT8613_ID: case IT8620_ID: case IT8622_ID: case IT8625_ID: diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c index 607ce4b8df57..ec0c08652ec2 100644 --- a/drivers/watchdog/ixp4xx_wdt.c +++ b/drivers/watchdog/ixp4xx_wdt.c @@ -105,6 +105,25 @@ static const struct watchdog_ops ixp4xx_wdt_ops = { .owner = THIS_MODULE, }; +/* + * The A0 version of the IXP422 had a bug in the watchdog making + * is useless, but we still need to use it to restart the system + * as it is the only way, so in this special case we register a + * "dummy" watchdog that doesn't really work, but will support + * the restart operation. + */ +static int ixp4xx_wdt_dummy(struct watchdog_device *wdd) +{ + return 0; +} + +static const struct watchdog_ops ixp4xx_wdt_restart_only_ops = { + .start = ixp4xx_wdt_dummy, + .stop = ixp4xx_wdt_dummy, + .restart = ixp4xx_wdt_restart, + .owner = THIS_MODULE, +}; + static const struct watchdog_info ixp4xx_wdt_info = { .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE @@ -114,14 +133,17 @@ static const struct watchdog_info ixp4xx_wdt_info = { static int ixp4xx_wdt_probe(struct platform_device *pdev) { + static const struct watchdog_ops *iwdt_ops; struct device *dev = &pdev->dev; struct ixp4xx_wdt *iwdt; struct clk *clk; int ret; if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) { - dev_err(dev, "Rev. A0 IXP42x CPU detected - watchdog disabled\n"); - return -ENODEV; + dev_info(dev, "Rev. A0 IXP42x CPU detected - only restart supported\n"); + iwdt_ops = &ixp4xx_wdt_restart_only_ops; + } else { + iwdt_ops = &ixp4xx_wdt_ops; } iwdt = devm_kzalloc(dev, sizeof(*iwdt), GFP_KERNEL); @@ -141,7 +163,7 @@ static int ixp4xx_wdt_probe(struct platform_device *pdev) iwdt->rate = IXP4XX_TIMER_FREQ; iwdt->wdd.info = &ixp4xx_wdt_info; - iwdt->wdd.ops = &ixp4xx_wdt_ops; + iwdt->wdd.ops = iwdt_ops; iwdt->wdd.min_timeout = 1; iwdt->wdd.max_timeout = U32_MAX / iwdt->rate; iwdt->wdd.parent = dev; diff --git a/drivers/watchdog/marvell_gti_wdt.c b/drivers/watchdog/marvell_gti_wdt.c index d7eb8286e11e..098bb141a521 100644 --- a/drivers/watchdog/marvell_gti_wdt.c +++ b/drivers/watchdog/marvell_gti_wdt.c @@ -8,8 +8,8 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/of.h> #include <linux/watchdog.h> /* @@ -190,6 +190,13 @@ static int gti_wdt_set_pretimeout(struct watchdog_device *wdev, struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev); struct watchdog_device *wdog_dev = &priv->wdev; + if (!timeout) { + /* Disable Interrupt */ + writeq(GTI_CWD_INT_ENA_CLR_VAL(priv->wdt_timer_idx), + priv->base + GTI_CWD_INT_ENA_CLR); + return 0; + } + /* pretimeout should 1/3 of max_timeout */ if (timeout * 3 <= wdog_dev->max_timeout) return gti_wdt_settimeout(wdev, timeout * 3); @@ -271,7 +278,7 @@ static int gti_wdt_probe(struct platform_device *pdev) &wdt_idx); if (!err) { if (wdt_idx >= priv->data->gti_num_timers) - return dev_err_probe(&pdev->dev, err, + return dev_err_probe(&pdev->dev, -EINVAL, "GTI wdog timer index not valid"); priv->wdt_timer_idx = wdt_idx; @@ -292,6 +299,7 @@ static int gti_wdt_probe(struct platform_device *pdev) /* Maximum timeout is 3 times the pretimeout */ wdog_dev->max_timeout = max_pretimeout * 3; + wdog_dev->max_hw_heartbeat_ms = max_pretimeout * 1000; /* Minimum first timeout (pretimeout) is 1, so min_timeout as 3 */ wdog_dev->min_timeout = 3; wdog_dev->timeout = wdog_dev->pretimeout; @@ -308,7 +316,7 @@ static int gti_wdt_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return dev_err_probe(&pdev->dev, irq, "IRQ resource not found\n"); + return irq; err = devm_request_irq(dev, irq, gti_wdt_interrupt, 0, pdev->name, &priv->wdev); diff --git a/drivers/watchdog/mlx_wdt.c b/drivers/watchdog/mlx_wdt.c index 9c5b6616fc87..667e2c5b3431 100644 --- a/drivers/watchdog/mlx_wdt.c +++ b/drivers/watchdog/mlx_wdt.c @@ -39,6 +39,7 @@ * @tleft_idx: index for direct access to time left register; * @ping_idx: index for direct access to ping register; * @reset_idx: index for direct access to reset cause register; + * @regmap_val_sz: size of value in register map; * @wd_type: watchdog HW type; */ struct mlxreg_wdt { diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c index 05657dc1d36a..352853e6fe71 100644 --- a/drivers/watchdog/of_xilinx_wdt.c +++ b/drivers/watchdog/of_xilinx_wdt.c @@ -187,7 +187,7 @@ static int xwdt_probe(struct platform_device *pdev) watchdog_set_nowayout(xilinx_wdt_wdd, enable_once); - xdev->clk = devm_clk_get_enabled(dev, NULL); + xdev->clk = devm_clk_get_prepared(dev, NULL); if (IS_ERR(xdev->clk)) { if (PTR_ERR(xdev->clk) != -ENOENT) return PTR_ERR(xdev->clk); @@ -218,18 +218,25 @@ static int xwdt_probe(struct platform_device *pdev) spin_lock_init(&xdev->spinlock); watchdog_set_drvdata(xilinx_wdt_wdd, xdev); + rc = clk_enable(xdev->clk); + if (rc) { + dev_err(dev, "unable to enable clock\n"); + return rc; + } + rc = xwdt_selftest(xdev); if (rc == XWT_TIMER_FAILED) { dev_err(dev, "SelfTest routine error\n"); + clk_disable(xdev->clk); return rc; } + clk_disable(xdev->clk); + rc = devm_watchdog_register_device(dev, xilinx_wdt_wdd); if (rc) return rc; - clk_disable(xdev->clk); - dev_info(dev, "Xilinx Watchdog Timer with timeout %ds\n", xilinx_wdt_wdd->timeout); diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index 421ebcda62e6..5f23913ce3b4 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -152,14 +152,14 @@ static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd, timeout = clamp_t(unsigned int, timeout, 1, wdd->max_hw_heartbeat_ms / 1000); if (action) - sbsa_gwdt_reg_write(gwdt->clk * timeout, gwdt); + sbsa_gwdt_reg_write((u64)gwdt->clk * timeout, gwdt); else /* * In the single stage mode, The first signal (WS0) is ignored, * the timeout is (WOR * 2), so the WOR should be configured * to half value of timeout. */ - sbsa_gwdt_reg_write(gwdt->clk / 2 * timeout, gwdt); + sbsa_gwdt_reg_write(((u64)gwdt->clk / 2) * timeout, gwdt); return 0; } diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c index d2aa43c00221..4c5b8d98a4f3 100644 --- a/drivers/watchdog/st_lpc_wdt.c +++ b/drivers/watchdog/st_lpc_wdt.c @@ -15,7 +15,6 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/watchdog.h> @@ -42,7 +41,7 @@ struct st_wdog { void __iomem *base; struct device *dev; struct regmap *regmap; - struct st_wdog_syscfg *syscfg; + const struct st_wdog_syscfg *syscfg; struct clk *clk; unsigned long clkrate; bool warm_reset; @@ -150,7 +149,6 @@ static void st_clk_disable_unprepare(void *data) static int st_wdog_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *match; struct device_node *np = dev->of_node; struct st_wdog *st_wdog; struct regmap *regmap; @@ -173,12 +171,7 @@ static int st_wdog_probe(struct platform_device *pdev) if (!st_wdog) return -ENOMEM; - match = of_match_device(st_wdog_match, dev); - if (!match) { - dev_err(dev, "Couldn't match device\n"); - return -ENODEV; - } - st_wdog->syscfg = (struct st_wdog_syscfg *)match->data; + st_wdog->syscfg = (struct st_wdog_syscfg *)device_get_match_data(dev); base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) diff --git a/drivers/watchdog/sunplus_wdt.c b/drivers/watchdog/sunplus_wdt.c index e2d8c532bcb1..9d3ca848e8b6 100644 --- a/drivers/watchdog/sunplus_wdt.c +++ b/drivers/watchdog/sunplus_wdt.c @@ -136,11 +136,6 @@ static const struct watchdog_ops sp_wdt_ops = { .restart = sp_wdt_restart, }; -static void sp_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - static void sp_reset_control_assert(void *data) { reset_control_assert(data); @@ -156,17 +151,9 @@ static int sp_wdt_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->clk = devm_clk_get(dev, NULL); + priv->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(priv->clk)) - return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clock\n"); - - ret = clk_prepare_enable(priv->clk); - if (ret) - return dev_err_probe(dev, ret, "Failed to enable clock\n"); - - ret = devm_add_action_or_reset(dev, sp_clk_disable_unprepare, priv->clk); - if (ret) - return ret; + return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to enable clock\n"); /* The timer and watchdog shared the STC reset */ priv->rstc = devm_reset_control_get_shared(dev, NULL); diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c index 0ba99bed59fc..650fdc7996e1 100644 --- a/drivers/watchdog/wdat_wdt.c +++ b/drivers/watchdog/wdat_wdt.c @@ -269,7 +269,7 @@ static int wdat_wdt_stop(struct watchdog_device *wdd) static int wdat_wdt_ping(struct watchdog_device *wdd) { - return wdat_wdt_run_action(to_wdat_wdt(wdd), ACPI_WDAT_RESET, 0, NULL); + return wdat_wdt_run_action(to_wdat_wdt(wdd), ACPI_WDAT_RESET, wdd->timeout, NULL); } static int wdat_wdt_set_timeout(struct watchdog_device *wdd, |