diff options
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 1 | ||||
-rw-r--r-- | drivers/watchdog/asm9260_wdt.c | 8 | ||||
-rw-r--r-- | drivers/watchdog/coh901327_wdt.c | 18 | ||||
-rw-r--r-- | drivers/watchdog/gpio_wdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/hpwdt.c | 312 | ||||
-rw-r--r-- | drivers/watchdog/imx2_wdt.c | 8 | ||||
-rw-r--r-- | drivers/watchdog/lpc18xx_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/meson_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/mtk_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/omap_wdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/pnx4008_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/renesas_wdt.c | 87 | ||||
-rw-r--r-- | drivers/watchdog/sama5d4_wdt.c | 6 | ||||
-rw-r--r-- | drivers/watchdog/sirfsoc_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/sunxi_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/uniphier_wdt.c | 5 |
16 files changed, 236 insertions, 229 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 37460cd6cabb..e873522fca2d 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1118,6 +1118,7 @@ config IT87_WDT config HP_WATCHDOG tristate "HP ProLiant iLO2+ Hardware Watchdog Timer" + select WATCHDOG_CORE depends on X86 && PCI help A software monitoring watchdog and NMI sourcing driver. This driver diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c index 7dd0da644a7f..2cf56b459d84 100644 --- a/drivers/watchdog/asm9260_wdt.c +++ b/drivers/watchdog/asm9260_wdt.c @@ -292,14 +292,14 @@ static int asm9260_wdt_probe(struct platform_device *pdev) if (IS_ERR(priv->iobase)) return PTR_ERR(priv->iobase); - ret = asm9260_wdt_get_dt_clks(priv); - if (ret) - return ret; - priv->rst = devm_reset_control_get_exclusive(&pdev->dev, "wdt_rst"); if (IS_ERR(priv->rst)) return PTR_ERR(priv->rst); + ret = asm9260_wdt_get_dt_clks(priv); + if (ret) + return ret; + wdd = &priv->wdd; wdd->info = &asm9260_wdt_ident; wdd->ops = &asm9260_wdt_ops; diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c index 4410337f4f7f..e3a78f927f83 100644 --- a/drivers/watchdog/coh901327_wdt.c +++ b/drivers/watchdog/coh901327_wdt.c @@ -1,8 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * coh901327_wdt.c * * Copyright (C) 2008-2009 ST-Ericsson AB - * License terms: GNU General Public License (GPL) version 2 * Watchdog driver for the ST-Ericsson AB COH 901 327 IP core * Author: Linus Walleij <linus.walleij@stericsson.com> */ @@ -67,7 +67,9 @@ #define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U /* Default timeout in seconds = 1 minute */ -static unsigned int margin = 60; +#define U300_WDOG_DEFAULT_TIMEOUT 60 + +static unsigned int margin; static int irq; static void __iomem *virtbase; static struct device *parent; @@ -235,8 +237,9 @@ static struct watchdog_device coh901327_wdt = { * timeout register is max * 0x7FFF = 327670ms ~= 327s. */ - .min_timeout = 0, + .min_timeout = 1, .max_timeout = 327, + .timeout = U300_WDOG_DEFAULT_TIMEOUT, }; static int __exit coh901327_remove(struct platform_device *pdev) @@ -315,16 +318,15 @@ static int __init coh901327_probe(struct platform_device *pdev) goto out_no_irq; } - ret = watchdog_init_timeout(&coh901327_wdt, margin, dev); - if (ret < 0) - coh901327_wdt.timeout = 60; + watchdog_init_timeout(&coh901327_wdt, margin, dev); coh901327_wdt.parent = dev; ret = watchdog_register_device(&coh901327_wdt); if (ret) goto out_no_wdog; - dev_info(dev, "initialized. timer margin=%d sec\n", margin); + dev_info(dev, "initialized. (timeout=%d sec)\n", + coh901327_wdt.timeout); return 0; out_no_wdog: @@ -419,5 +421,5 @@ MODULE_DESCRIPTION("COH 901 327 Watchdog"); module_param(margin, uint, 0); MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:coh901327-watchdog"); diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index 3ade28190341..ea77cae03c9d 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -152,9 +152,9 @@ static int gpio_wdt_probe(struct platform_device *pdev) priv->wdd.min_timeout = SOFT_TIMEOUT_MIN; priv->wdd.max_hw_heartbeat_ms = hw_margin; priv->wdd.parent = dev; + priv->wdd.timeout = SOFT_TIMEOUT_DEF; - if (watchdog_init_timeout(&priv->wdd, 0, dev) < 0) - priv->wdd.timeout = SOFT_TIMEOUT_DEF; + watchdog_init_timeout(&priv->wdd, 0, &pdev->dev); watchdog_stop_on_reboot(&priv->wdd); diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index b0a158073abd..a43ab2cecca2 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -4,7 +4,7 @@ * * SoftDog 0.05: A Software Watchdog Device * - * (c) Copyright 2015 Hewlett Packard Enterprise Development LP + * (c) Copyright 2018 Hewlett Packard Enterprise Development LP * Thomas Mingarelli <thomas.mingarelli@hpe.com> * * This program is free software; you can redistribute it and/or @@ -16,34 +16,27 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/device.h> -#include <linux/fs.h> #include <linux/io.h> -#include <linux/bitops.h> #include <linux/kernel.h> -#include <linux/miscdevice.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/types.h> -#include <linux/uaccess.h> #include <linux/watchdog.h> #include <asm/nmi.h> -#define HPWDT_VERSION "1.4.0" +#define HPWDT_VERSION "2.0.0" #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) #define DEFAULT_MARGIN 30 +#define PRETIMEOUT_SEC 9 +static bool ilo5; static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */ -static unsigned int reload; /* the computed soft_margin */ static bool nowayout = WATCHDOG_NOWAYOUT; -#ifdef CONFIG_HPWDT_NMI_DECODING -static unsigned int allow_kdump = 1; -#endif -static char expect_release; -static unsigned long hpwdt_is_open; +static bool pretimeout = IS_ENABLED(CONFIG_HPWDT_NMI_DECODING); static void __iomem *pci_mem_addr; /* the PCI-memory address */ static unsigned long __iomem *hpwdt_nmistat; @@ -61,48 +54,92 @@ MODULE_DEVICE_TABLE(pci, hpwdt_devices); /* * Watchdog operations */ -static void hpwdt_start(void) +static int hpwdt_start(struct watchdog_device *wdd) { - reload = SECS_TO_TICKS(soft_margin); + int control = 0x81 | (pretimeout ? 0x4 : 0); + int reload = SECS_TO_TICKS(wdd->timeout); + + dev_dbg(wdd->parent, "start watchdog 0x%08x:0x%02x\n", reload, control); iowrite16(reload, hpwdt_timer_reg); - iowrite8(0x85, hpwdt_timer_con); + iowrite8(control, hpwdt_timer_con); + + return 0; } static void hpwdt_stop(void) { unsigned long data; + pr_debug("stop watchdog\n"); + data = ioread8(hpwdt_timer_con); data &= 0xFE; iowrite8(data, hpwdt_timer_con); } -static void hpwdt_ping(void) +static int hpwdt_stop_core(struct watchdog_device *wdd) { - iowrite16(reload, hpwdt_timer_reg); + hpwdt_stop(); + + return 0; } -static int hpwdt_change_timer(int new_margin) +static int hpwdt_ping(struct watchdog_device *wdd) { - if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) { - pr_warn("New value passed in is invalid: %d seconds\n", - new_margin); - return -EINVAL; - } + int reload = SECS_TO_TICKS(wdd->timeout); - soft_margin = new_margin; - pr_debug("New timer passed in is %d seconds\n", new_margin); - reload = SECS_TO_TICKS(soft_margin); + dev_dbg(wdd->parent, "ping watchdog 0x%08x\n", reload); + iowrite16(reload, hpwdt_timer_reg); return 0; } -static int hpwdt_time_left(void) +static unsigned int hpwdt_gettimeleft(struct watchdog_device *wdd) { return TICKS_TO_SECS(ioread16(hpwdt_timer_reg)); } +static int hpwdt_settimeout(struct watchdog_device *wdd, unsigned int val) +{ + dev_dbg(wdd->parent, "set_timeout = %d\n", val); + + wdd->timeout = val; + if (val <= wdd->pretimeout) { + dev_dbg(wdd->parent, "pretimeout < timeout. Setting to zero\n"); + wdd->pretimeout = 0; + pretimeout = 0; + if (watchdog_active(wdd)) + hpwdt_start(wdd); + } + hpwdt_ping(wdd); + + return 0; +} + #ifdef CONFIG_HPWDT_NMI_DECODING +static int hpwdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req) +{ + unsigned int val = 0; + + dev_dbg(wdd->parent, "set_pretimeout = %d\n", req); + if (req) { + val = PRETIMEOUT_SEC; + if (val >= wdd->timeout) + return -EINVAL; + } + + if (val != req) + dev_dbg(wdd->parent, "Rounding pretimeout to: %d\n", val); + + wdd->pretimeout = val; + pretimeout = !!val; + + if (watchdog_active(wdd)) + hpwdt_start(wdd); + + return 0; +} + static int hpwdt_my_nmi(void) { return ioread8(hpwdt_nmistat) & 0x6; @@ -113,178 +150,71 @@ static int hpwdt_my_nmi(void) */ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) { - if ((ulReason == NMI_UNKNOWN) && !hpwdt_my_nmi()) - return NMI_DONE; - - if (allow_kdump) - hpwdt_stop(); - - nmi_panic(regs, "An NMI occurred. Depending on your system the reason " - "for the NMI is logged in any one of the following " - "resources:\n" + unsigned int mynmi = hpwdt_my_nmi(); + static char panic_msg[] = + "00: An NMI occurred. Depending on your system the reason " + "for the NMI is logged in any one of the following resources:\n" "1. Integrated Management Log (IML)\n" "2. OA Syslog\n" "3. OA Forward Progress Log\n" - "4. iLO Event Log"); - - return NMI_HANDLED; -} -#endif /* CONFIG_HPWDT_NMI_DECODING */ - -/* - * /dev/watchdog handling - */ -static int hpwdt_open(struct inode *inode, struct file *file) -{ - /* /dev/watchdog can only be opened once */ - if (test_and_set_bit(0, &hpwdt_is_open)) - return -EBUSY; + "4. iLO Event Log"; - /* Start the watchdog */ - hpwdt_start(); - hpwdt_ping(); - - return nonseekable_open(inode, file); -} + if (ilo5 && ulReason == NMI_UNKNOWN && mynmi) + return NMI_DONE; -static int hpwdt_release(struct inode *inode, struct file *file) -{ - /* Stop the watchdog */ - if (expect_release == 42) { - hpwdt_stop(); - } else { - pr_crit("Unexpected close, not stopping watchdog!\n"); - hpwdt_ping(); - } + if (ilo5 && !pretimeout) + return NMI_DONE; - expect_release = 0; + hpwdt_stop(); - /* /dev/watchdog is being closed, make sure it can be re-opened */ - clear_bit(0, &hpwdt_is_open); + hex_byte_pack(panic_msg, mynmi); + nmi_panic(regs, panic_msg); - return 0; + return NMI_HANDLED; } +#endif /* CONFIG_HPWDT_NMI_DECODING */ -static ssize_t hpwdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - /* See if we got the magic character 'V' and reload the timer */ - if (len) { - if (!nowayout) { - size_t i; - - /* note: just in case someone wrote the magic character - * five months ago... */ - expect_release = 0; - - /* scan to see whether or not we got the magic char. */ - for (i = 0; i != len; i++) { - char c; - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - expect_release = 42; - } - } - - /* someone wrote to us, we should reload the timer */ - hpwdt_ping(); - } - - return len; -} static const struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | + .options = WDIOF_PRETIMEOUT | + WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .identity = "HPE iLO2+ HW Watchdog Timer", }; -static long hpwdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - int new_margin, options; - int ret = -ENOTTY; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = 0; - if (copy_to_user(argp, &ident, sizeof(ident))) - ret = -EFAULT; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - ret = put_user(0, p); - break; - - case WDIOC_KEEPALIVE: - hpwdt_ping(); - ret = 0; - break; - - case WDIOC_SETOPTIONS: - ret = get_user(options, p); - if (ret) - break; - - if (options & WDIOS_DISABLECARD) - hpwdt_stop(); - - if (options & WDIOS_ENABLECARD) { - hpwdt_start(); - hpwdt_ping(); - } - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(new_margin, p); - if (ret) - break; - - ret = hpwdt_change_timer(new_margin); - if (ret) - break; - - hpwdt_ping(); - /* Fall */ - case WDIOC_GETTIMEOUT: - ret = put_user(soft_margin, p); - break; - - case WDIOC_GETTIMELEFT: - ret = put_user(hpwdt_time_left(), p); - break; - } - return ret; -} - /* * Kernel interfaces */ -static const struct file_operations hpwdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = hpwdt_write, - .unlocked_ioctl = hpwdt_ioctl, - .open = hpwdt_open, - .release = hpwdt_release, + +static const struct watchdog_ops hpwdt_ops = { + .owner = THIS_MODULE, + .start = hpwdt_start, + .stop = hpwdt_stop_core, + .ping = hpwdt_ping, + .set_timeout = hpwdt_settimeout, + .get_timeleft = hpwdt_gettimeleft, +#ifdef CONFIG_HPWDT_NMI_DECODING + .set_pretimeout = hpwdt_set_pretimeout, +#endif }; -static struct miscdevice hpwdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &hpwdt_fops, +static struct watchdog_device hpwdt_dev = { + .info = &ident, + .ops = &hpwdt_ops, + .min_timeout = 1, + .max_timeout = HPWDT_MAX_TIMER, + .timeout = DEFAULT_MARGIN, +#ifdef CONFIG_HPWDT_NMI_DECODING + .pretimeout = PRETIMEOUT_SEC, +#endif }; + /* * Init & Exit */ - static int hpwdt_init_nmi_decoding(struct pci_dev *dev) { #ifdef CONFIG_HPWDT_NMI_DECODING @@ -303,9 +233,8 @@ static int hpwdt_init_nmi_decoding(struct pci_dev *dev) goto error2; dev_info(&dev->dev, - "HPE Watchdog Timer Driver: NMI decoding initialized" - ", allow kernel dump: %s (default = 1/ON)\n", - (allow_kdump == 0) ? "OFF" : "ON"); + "HPE Watchdog Timer Driver: NMI decoding initialized\n"); + return 0; error2: @@ -375,29 +304,32 @@ static int hpwdt_init_one(struct pci_dev *dev, /* Make sure that timer is disabled until /dev/watchdog is opened */ hpwdt_stop(); - /* Make sure that we have a valid soft_margin */ - if (hpwdt_change_timer(soft_margin)) - hpwdt_change_timer(DEFAULT_MARGIN); - /* Initialize NMI Decoding functionality */ retval = hpwdt_init_nmi_decoding(dev); if (retval != 0) goto error_init_nmi_decoding; - retval = misc_register(&hpwdt_miscdev); + watchdog_set_nowayout(&hpwdt_dev, nowayout); + if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL)) + dev_warn(&dev->dev, "Invalid soft_margin: %d.\n", soft_margin); + + hpwdt_dev.parent = &dev->dev; + retval = watchdog_register_device(&hpwdt_dev); if (retval < 0) { - dev_warn(&dev->dev, - "Unable to register miscdev on minor=%d (err=%d).\n", - WATCHDOG_MINOR, retval); - goto error_misc_register; + dev_err(&dev->dev, "watchdog register failed: %d.\n", retval); + goto error_wd_register; } dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s" ", timer margin: %d seconds (nowayout=%d).\n", - HPWDT_VERSION, soft_margin, nowayout); + HPWDT_VERSION, hpwdt_dev.timeout, nowayout); + + if (dev->subsystem_vendor == PCI_VENDOR_ID_HP_3PAR) + ilo5 = true; + return 0; -error_misc_register: +error_wd_register: hpwdt_exit_nmi_decoding(); error_init_nmi_decoding: pci_iounmap(dev, pci_mem_addr); @@ -411,7 +343,7 @@ static void hpwdt_exit(struct pci_dev *dev) if (!nowayout) hpwdt_stop(); - misc_deregister(&hpwdt_miscdev); + watchdog_unregister_device(&hpwdt_dev); hpwdt_exit_nmi_decoding(); pci_iounmap(dev, pci_mem_addr); pci_disable_device(dev); @@ -425,7 +357,7 @@ static struct pci_driver hpwdt_driver = { }; MODULE_AUTHOR("Tom Mingarelli"); -MODULE_DESCRIPTION("hp watchdog driver"); +MODULE_DESCRIPTION("hpe watchdog driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(HPWDT_VERSION); @@ -437,8 +369,8 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); #ifdef CONFIG_HPWDT_NMI_DECODING -module_param(allow_kdump, int, 0); -MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); -#endif /* CONFIG_HPWDT_NMI_DECODING */ +module_param(pretimeout, bool, 0); +MODULE_PARM_DESC(pretimeout, "Watchdog pretimeout enabled"); +#endif module_pci_driver(hpwdt_driver); diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 518dfa1047cb..f07850d2c977 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -76,7 +76,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -static unsigned timeout = IMX2_WDT_DEFAULT_TIME; +static unsigned timeout; module_param(timeout, uint, 0); MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" __MODULE_STRING(IMX2_WDT_DEFAULT_TIME) ")"); @@ -281,6 +281,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdog->info = &imx2_wdt_info; wdog->ops = &imx2_wdt_ops; wdog->min_timeout = 1; + wdog->timeout = IMX2_WDT_DEFAULT_TIME; wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000; wdog->parent = &pdev->dev; @@ -299,11 +300,6 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdev->ext_reset = of_property_read_bool(pdev->dev.of_node, "fsl,ext-reset-output"); - wdog->timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME); - if (wdog->timeout != timeout) - dev_warn(&pdev->dev, "Initial timeout out of range! Clamped from %u to %u\n", - timeout, wdog->timeout); - platform_set_drvdata(pdev, wdog); watchdog_set_drvdata(wdog, wdev); watchdog_set_nowayout(wdog, nowayout); diff --git a/drivers/watchdog/lpc18xx_wdt.c b/drivers/watchdog/lpc18xx_wdt.c index b4221f43cd94..331cadb459ac 100644 --- a/drivers/watchdog/lpc18xx_wdt.c +++ b/drivers/watchdog/lpc18xx_wdt.c @@ -265,7 +265,7 @@ static int lpc18xx_wdt_probe(struct platform_device *pdev) lpc18xx_wdt->wdt_dev.parent = dev; watchdog_set_drvdata(&lpc18xx_wdt->wdt_dev, lpc18xx_wdt); - ret = watchdog_init_timeout(&lpc18xx_wdt->wdt_dev, heartbeat, dev); + watchdog_init_timeout(&lpc18xx_wdt->wdt_dev, heartbeat, dev); __lpc18xx_wdt_set_timeout(lpc18xx_wdt); diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c index 304274c67735..cd0275a6cdac 100644 --- a/drivers/watchdog/meson_wdt.c +++ b/drivers/watchdog/meson_wdt.c @@ -36,7 +36,7 @@ #define MESON_SEC_TO_TC(s, c) ((s) * (c)) static bool nowayout = WATCHDOG_NOWAYOUT; -static unsigned int timeout = MESON_WDT_TIMEOUT; +static unsigned int timeout; struct meson_wdt_data { unsigned int enable; diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index 7ed417a765c7..fcdc10ec28a3 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -57,7 +57,7 @@ #define DRV_VERSION "1.0" static bool nowayout = WATCHDOG_NOWAYOUT; -static unsigned int timeout = WDT_MAX_TIMEOUT; +static unsigned int timeout; struct mtk_wdt_dev { struct watchdog_device wdt_dev; diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 1b02bfa81b29..ae77112ce97f 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -253,10 +253,10 @@ static int omap_wdt_probe(struct platform_device *pdev) wdev->wdog.ops = &omap_wdt_ops; wdev->wdog.min_timeout = TIMER_MARGIN_MIN; wdev->wdog.max_timeout = TIMER_MARGIN_MAX; + wdev->wdog.timeout = TIMER_MARGIN_DEFAULT; wdev->wdog.parent = &pdev->dev; - if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0) - wdev->wdog.timeout = TIMER_MARGIN_DEFAULT; + watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev); watchdog_set_nowayout(&wdev->wdog, nowayout); diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c index 0529aed158a4..8e261799c84e 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c @@ -78,7 +78,7 @@ #define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */ static bool nowayout = WATCHDOG_NOWAYOUT; -static unsigned int heartbeat = DEFAULT_HEARTBEAT; +static unsigned int heartbeat; static DEFINE_SPINLOCK(io_lock); static void __iomem *wdt_base; diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c index 831ef83f6de1..6b8c6ddfe30b 100644 --- a/drivers/watchdog/renesas_wdt.c +++ b/drivers/watchdog/renesas_wdt.c @@ -16,6 +16,8 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/smp.h> +#include <linux/sys_soc.h> #include <linux/watchdog.h> #define RWTCNT 0 @@ -49,6 +51,7 @@ struct rwdt_priv { void __iomem *base; struct watchdog_device wdev; unsigned long clk_rate; + u16 time_left; u8 cks; }; @@ -107,6 +110,16 @@ static unsigned int rwdt_get_timeleft(struct watchdog_device *wdev) return DIV_BY_CLKS_PER_SEC(priv, 65536 - val); } +static int rwdt_restart(struct watchdog_device *wdev, unsigned long action, + void *data) +{ + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); + + rwdt_start(wdev); + rwdt_write(priv, 0xffff, RWTCNT); + return 0; +} + static const struct watchdog_info rwdt_ident = { .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, .identity = "Renesas WDT Watchdog", @@ -118,8 +131,47 @@ static const struct watchdog_ops rwdt_ops = { .stop = rwdt_stop, .ping = rwdt_init_timeout, .get_timeleft = rwdt_get_timeleft, + .restart = rwdt_restart, }; +#if defined(CONFIG_ARCH_RCAR_GEN2) && defined(CONFIG_SMP) +/* + * Watchdog-reset integration is broken on early revisions of R-Car Gen2 SoCs + */ +static const struct soc_device_attribute rwdt_quirks_match[] = { + { + .soc_id = "r8a7790", + .revision = "ES1.*", + .data = (void *)1, /* needs single CPU */ + }, { + .soc_id = "r8a7791", + .revision = "ES[12].*", + .data = (void *)1, /* needs single CPU */ + }, { + .soc_id = "r8a7792", + .revision = "*", + .data = (void *)0, /* needs SMP disabled */ + }, + { /* sentinel */ } +}; + +static bool rwdt_blacklisted(struct device *dev) +{ + const struct soc_device_attribute *attr; + + attr = soc_device_match(rwdt_quirks_match); + if (attr && setup_max_cpus > (uintptr_t)attr->data) { + dev_info(dev, "Watchdog blacklisted on %s %s\n", attr->soc_id, + attr->revision); + return true; + } + + return false; +} +#else /* !CONFIG_ARCH_RCAR_GEN2 || !CONFIG_SMP */ +static inline bool rwdt_blacklisted(struct device *dev) { return false; } +#endif /* !CONFIG_ARCH_RCAR_GEN2 || !CONFIG_SMP */ + static int rwdt_probe(struct platform_device *pdev) { struct rwdt_priv *priv; @@ -128,6 +180,9 @@ static int rwdt_probe(struct platform_device *pdev) unsigned long clks_per_sec; int ret, i; + if (rwdt_blacklisted(&pdev->dev)) + return -ENODEV; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -176,6 +231,7 @@ static int rwdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); watchdog_set_drvdata(&priv->wdev, priv); watchdog_set_nowayout(&priv->wdev, nowayout); + watchdog_set_restart_priority(&priv->wdev, 0); /* This overrides the default timeout only if DT configuration was found */ ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev); @@ -203,12 +259,32 @@ static int rwdt_remove(struct platform_device *pdev) return 0; } -/* - * This driver does also fit for R-Car Gen2 (r8a779[0-4]) WDT. However, for SMP - * to work there, one also needs a RESET (RST) driver which does not exist yet - * due to HW issues. This needs to be solved before adding compatibles here. - */ +static int __maybe_unused rwdt_suspend(struct device *dev) +{ + struct rwdt_priv *priv = dev_get_drvdata(dev); + + if (watchdog_active(&priv->wdev)) { + priv->time_left = readw(priv->base + RWTCNT); + rwdt_stop(&priv->wdev); + } + return 0; +} + +static int __maybe_unused rwdt_resume(struct device *dev) +{ + struct rwdt_priv *priv = dev_get_drvdata(dev); + + if (watchdog_active(&priv->wdev)) { + rwdt_start(&priv->wdev); + rwdt_write(priv, priv->time_left, RWTCNT); + } + return 0; +} + +static SIMPLE_DEV_PM_OPS(rwdt_pm_ops, rwdt_suspend, rwdt_resume); + static const struct of_device_id rwdt_ids[] = { + { .compatible = "renesas,rcar-gen2-wdt", }, { .compatible = "renesas,rcar-gen3-wdt", }, { /* sentinel */ } }; @@ -218,6 +294,7 @@ static struct platform_driver rwdt_driver = { .driver = { .name = "renesas_wdt", .of_match_table = rwdt_ids, + .pm = &rwdt_pm_ops, }, .probe = rwdt_probe, .remove = rwdt_remove, diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index 0ae947c3d7bc..255169916dbb 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -33,7 +33,7 @@ struct sama5d4_wdt { unsigned long last_ping; }; -static int wdt_timeout = WDT_DEFAULT_TIMEOUT; +static int wdt_timeout; static bool nowayout = WATCHDOG_NOWAYOUT; module_param(wdt_timeout, int, 0); @@ -212,7 +212,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) return -ENOMEM; wdd = &wdt->wdd; - wdd->timeout = wdt_timeout; + wdd->timeout = WDT_DEFAULT_TIMEOUT; wdd->info = &sama5d4_wdt_info; wdd->ops = &sama5d4_wdt_ops; wdd->min_timeout = MIN_WDT_TIMEOUT; @@ -273,7 +273,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wdt); dev_info(&pdev->dev, "initialized (timeout = %d sec, nowayout = %d)\n", - wdt_timeout, nowayout); + wdd->timeout, nowayout); return 0; } diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c index 4eea351e09b0..ac0c9d2c4aee 100644 --- a/drivers/watchdog/sirfsoc_wdt.c +++ b/drivers/watchdog/sirfsoc_wdt.c @@ -29,7 +29,7 @@ #define SIRFSOC_WDT_MAX_TIMEOUT (10 * 60) /* 10 mins */ #define SIRFSOC_WDT_DEFAULT_TIMEOUT 30 /* 30 secs */ -static unsigned int timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT; +static unsigned int timeout; static bool nowayout = WATCHDOG_NOWAYOUT; module_param(timeout, uint, 0); diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 802e31b1416d..c6c73656997e 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -39,7 +39,7 @@ #define DRV_VERSION "1.0" static bool nowayout = WATCHDOG_NOWAYOUT; -static unsigned int timeout = WDT_MAX_TIMEOUT; +static unsigned int timeout; /* * This structure stores the register offsets for different variants diff --git a/drivers/watchdog/uniphier_wdt.c b/drivers/watchdog/uniphier_wdt.c index 0ea2339d9702..0e4f8d53ce3c 100644 --- a/drivers/watchdog/uniphier_wdt.c +++ b/drivers/watchdog/uniphier_wdt.c @@ -212,11 +212,10 @@ static int uniphier_wdt_probe(struct platform_device *pdev) 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.timeout = WDT_DEFAULT_TIMEOUT; wdev->wdt_dev.parent = dev; - if (watchdog_init_timeout(&wdev->wdt_dev, timeout, dev) < 0) { - wdev->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT; - } + watchdog_init_timeout(&wdev->wdt_dev, timeout, dev); watchdog_set_nowayout(&wdev->wdt_dev, nowayout); watchdog_stop_on_reboot(&wdev->wdt_dev); |