diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-27 10:25:22 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-27 10:25:22 -0700 |
commit | 5ecf3e110c32c5756351eed067cdf6a91c308e62 (patch) | |
tree | ca93d93245620eda168fb70c141488918f2d8250 /drivers | |
parent | ed3f4e239834317934cc73a187e27e44b217056b (diff) | |
parent | cd6ba41c192deca20d9add5554bc8c51d0f07de2 (diff) | |
download | linux-stable-5ecf3e110c32c5756351eed067cdf6a91c308e62.tar.gz linux-stable-5ecf3e110c32c5756351eed067cdf6a91c308e62.tar.bz2 linux-stable-5ecf3e110c32c5756351eed067cdf6a91c308e62.zip |
Merge tag 'linux-watchdog-4.20-rc1' of git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
- Add Armada 37xx CPU watchdog
- w83627hf_wdt: Add Support for NCT6796D, NCT6797D, NCT6798D
- hpwdt: several improvements
- renesas_wdt: SPDX identifiers, stop when unregistering, support for
R7S9210
- rza_wdt: SPDX identifiers, support longer timeouts
- core: fix null pointer dereference when releasing cdev
- iTCO_wdt: Drop option vendorsupport=2
- sama5d4: fix timeout-sec usage
- lantiq_wdt: convert to watchdog framework
- several small fixes
* tag 'linux-watchdog-4.20-rc1' of git://www.linux-watchdog.org/linux-watchdog: (30 commits)
watchdog: ts4800: release syscon device node in ts4800_wdt_probe()
watchdog: armada_37xx_wdt: use do_div for u64 division
documentation: watchdog: add documentation for armada-37xx-wdt
dt-bindings: watchdog: Document armada-37xx-wdt binding
watchdog: Add support for Armada 37xx CPU watchdog
dt-bindings: watchdog: add mpc8xxx-wdt support
watchdog: mpc8xxx: provide boot status
MAINTAINERS: Fix file pattern for MEN Z069 watchdog driver
dt-bindings: watchdog: renesas-wdt: Add support for R7S9210
watchdog: rza_wdt: Support longer timeouts
watchdog: hpwdt: Disable PreTimeout when Timeout is smaller
watchdog: w83627hf_wdt: Support NCT6796D, NCT6797D, NCT6798D
watchdog: mpc8xxx: use dev_xxxx() instead of pr_xxxx()
watchdog: lantiq: add get_timeleft callback
watchdog: lantiq: Convert to watchdog_device
watchdog: lantiq: update register names to better match spec
watchdog: sama5d4: fix timeout-sec usage
watchdog: fix a small number of "watchog" typos in comments
watchdog: rza_wdt: convert to SPDX identifiers
watchdog: iTCO_wdt: Remove unused hooks
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/watchdog/Kconfig | 12 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/armada_37xx_wdt.c | 388 | ||||
-rw-r--r-- | drivers/watchdog/booke_wdt.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/hpwdt.c | 24 | ||||
-rw-r--r-- | drivers/watchdog/iTCO_vendor.h | 4 | ||||
-rw-r--r-- | drivers/watchdog/iTCO_vendor_support.c | 168 | ||||
-rw-r--r-- | drivers/watchdog/iTCO_wdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/lantiq_wdt.c | 311 | ||||
-rw-r--r-- | drivers/watchdog/mpc8xxx_wdt.c | 46 | ||||
-rw-r--r-- | drivers/watchdog/renesas_wdt.c | 6 | ||||
-rw-r--r-- | drivers/watchdog/rza_wdt.c | 93 | ||||
-rw-r--r-- | drivers/watchdog/sama5d4_wdt.c | 6 | ||||
-rw-r--r-- | drivers/watchdog/ts4800_wdt.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/via_wdt.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/w83627hf_wdt.c | 8 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 10 |
17 files changed, 697 insertions, 391 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 5ea8909a41f9..2d64333f4782 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -273,6 +273,17 @@ config ARM_SBSA_WATCHDOG To compile this driver as module, choose M here: The module will be called sbsa_gwdt. +config ARMADA_37XX_WATCHDOG + tristate "Armada 37xx watchdog" + depends on ARCH_MVEBU || COMPILE_TEST + select MFD_SYSCON + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer found on + Marvell Armada 37xx SoCs. + To compile this driver as a module, choose M here: the + module will be called armada_37xx_wdt. + config ASM9260_WATCHDOG tristate "Alphascale ASM9260 watchdog" depends on MACH_ASM9260 || COMPILE_TEST @@ -1621,6 +1632,7 @@ config IMGPDC_WDT config LANTIQ_WDT tristate "Lantiq SoC watchdog" depends on LANTIQ + select WATCHDOG_CORE help Hardware driver for the Lantiq SoC Watchdog Timer. diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index bf92e7bf9ce0..f69cdff5ad7f 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o # ARM Architecture obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o +obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o diff --git a/drivers/watchdog/armada_37xx_wdt.c b/drivers/watchdog/armada_37xx_wdt.c new file mode 100644 index 000000000000..4b4054f54df9 --- /dev/null +++ b/drivers/watchdog/armada_37xx_wdt.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Watchdog driver for Marvell Armada 37xx SoCs + * + * Author: Marek Behun <marek.behun@nic.cz> + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/types.h> +#include <linux/watchdog.h> + +/* + * There are four counters that can be used for watchdog on Armada 37xx. + * The addresses for counter control registers are register base plus ID*0x10, + * where ID is 0, 1, 2 or 3. + * + * In this driver we use IDs 0 and 1. Counter ID 1 is used as watchdog counter, + * while counter ID 0 is used to implement pinging the watchdog: counter ID 1 is + * set to restart counting from initial value on counter ID 0 end count event. + * Pinging is done by forcing immediate end count event on counter ID 0. + * If only one counter was used, pinging would have to be implemented by + * disabling and enabling the counter, leaving the system in a vulnerable state + * for a (really) short period of time. + * + * Counters ID 2 and 3 are enabled by default even before U-Boot loads, + * therefore this driver does not provide a way to use them, eg. by setting a + * property in device tree. + */ + +#define CNTR_ID_RETRIGGER 0 +#define CNTR_ID_WDOG 1 + +/* relative to cpu_misc */ +#define WDT_TIMER_SELECT 0x64 +#define WDT_TIMER_SELECT_MASK 0xf +#define WDT_TIMER_SELECT_VAL BIT(CNTR_ID_WDOG) + +/* relative to reg */ +#define CNTR_CTRL(id) ((id) * 0x10) +#define CNTR_CTRL_ENABLE 0x0001 +#define CNTR_CTRL_ACTIVE 0x0002 +#define CNTR_CTRL_MODE_MASK 0x000c +#define CNTR_CTRL_MODE_ONESHOT 0x0000 +#define CNTR_CTRL_MODE_HWSIG 0x000c +#define CNTR_CTRL_TRIG_SRC_MASK 0x00f0 +#define CNTR_CTRL_TRIG_SRC_PREV_CNTR 0x0050 +#define CNTR_CTRL_PRESCALE_MASK 0xff00 +#define CNTR_CTRL_PRESCALE_MIN 2 +#define CNTR_CTRL_PRESCALE_SHIFT 8 + +#define CNTR_COUNT_LOW(id) (CNTR_CTRL(id) + 0x4) +#define CNTR_COUNT_HIGH(id) (CNTR_CTRL(id) + 0x8) + +#define WATCHDOG_TIMEOUT 120 + +static unsigned int timeout; +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct armada_37xx_watchdog { + struct watchdog_device wdt; + struct regmap *cpu_misc; + void __iomem *reg; + u64 timeout; /* in clock ticks */ + unsigned long clk_rate; + struct clk *clk; +}; + +static u64 get_counter_value(struct armada_37xx_watchdog *dev, int id) +{ + u64 val; + + /* + * when low is read, high is latched into flip-flops so that it can be + * read consistently without using software debouncing + */ + val = readl(dev->reg + CNTR_COUNT_LOW(id)); + val |= ((u64)readl(dev->reg + CNTR_COUNT_HIGH(id))) << 32; + + return val; +} + +static void set_counter_value(struct armada_37xx_watchdog *dev, int id, u64 val) +{ + writel(val & 0xffffffff, dev->reg + CNTR_COUNT_LOW(id)); + writel(val >> 32, dev->reg + CNTR_COUNT_HIGH(id)); +} + +static void counter_enable(struct armada_37xx_watchdog *dev, int id) +{ + u32 reg; + + reg = readl(dev->reg + CNTR_CTRL(id)); + reg |= CNTR_CTRL_ENABLE; + writel(reg, dev->reg + CNTR_CTRL(id)); +} + +static void counter_disable(struct armada_37xx_watchdog *dev, int id) +{ + u32 reg; + + reg = readl(dev->reg + CNTR_CTRL(id)); + reg &= ~CNTR_CTRL_ENABLE; + writel(reg, dev->reg + CNTR_CTRL(id)); +} + +static void init_counter(struct armada_37xx_watchdog *dev, int id, u32 mode, + u32 trig_src) +{ + u32 reg; + + reg = readl(dev->reg + CNTR_CTRL(id)); + + reg &= ~(CNTR_CTRL_MODE_MASK | CNTR_CTRL_PRESCALE_MASK | + CNTR_CTRL_TRIG_SRC_MASK); + + /* set mode */ + reg |= mode & CNTR_CTRL_MODE_MASK; + + /* set prescaler to the min value */ + reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT; + + /* set trigger source */ + reg |= trig_src & CNTR_CTRL_TRIG_SRC_MASK; + + writel(reg, dev->reg + CNTR_CTRL(id)); +} + +static int armada_37xx_wdt_ping(struct watchdog_device *wdt) +{ + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + + /* counter 1 is retriggered by forcing end count on counter 0 */ + counter_disable(dev, CNTR_ID_RETRIGGER); + counter_enable(dev, CNTR_ID_RETRIGGER); + + return 0; +} + +static unsigned int armada_37xx_wdt_get_timeleft(struct watchdog_device *wdt) +{ + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + u64 res; + + res = get_counter_value(dev, CNTR_ID_WDOG) * CNTR_CTRL_PRESCALE_MIN; + do_div(res, dev->clk_rate); + + return res; +} + +static int armada_37xx_wdt_set_timeout(struct watchdog_device *wdt, + unsigned int timeout) +{ + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + + wdt->timeout = timeout; + + /* + * Compute the timeout in clock rate. We use smallest possible + * prescaler, which divides the clock rate by 2 + * (CNTR_CTRL_PRESCALE_MIN). + */ + dev->timeout = (u64)dev->clk_rate * timeout; + do_div(dev->timeout, CNTR_CTRL_PRESCALE_MIN); + + return 0; +} + +static bool armada_37xx_wdt_is_running(struct armada_37xx_watchdog *dev) +{ + u32 reg; + + regmap_read(dev->cpu_misc, WDT_TIMER_SELECT, ®); + if ((reg & WDT_TIMER_SELECT_MASK) != WDT_TIMER_SELECT_VAL) + return false; + + reg = readl(dev->reg + CNTR_CTRL(CNTR_ID_WDOG)); + return !!(reg & CNTR_CTRL_ACTIVE); +} + +static int armada_37xx_wdt_start(struct watchdog_device *wdt) +{ + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + + /* select counter 1 as watchdog counter */ + regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, WDT_TIMER_SELECT_VAL); + + /* init counter 0 as retrigger counter for counter 1 */ + init_counter(dev, CNTR_ID_RETRIGGER, CNTR_CTRL_MODE_ONESHOT, 0); + set_counter_value(dev, CNTR_ID_RETRIGGER, 0); + + /* init counter 1 to be retriggerable by counter 0 end count */ + init_counter(dev, CNTR_ID_WDOG, CNTR_CTRL_MODE_HWSIG, + CNTR_CTRL_TRIG_SRC_PREV_CNTR); + set_counter_value(dev, CNTR_ID_WDOG, dev->timeout); + + /* enable counter 1 */ + counter_enable(dev, CNTR_ID_WDOG); + + /* start counter 1 by forcing immediate end count on counter 0 */ + counter_enable(dev, CNTR_ID_RETRIGGER); + + return 0; +} + +static int armada_37xx_wdt_stop(struct watchdog_device *wdt) +{ + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + + counter_disable(dev, CNTR_ID_WDOG); + counter_disable(dev, CNTR_ID_RETRIGGER); + regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, 0); + + return 0; +} + +static const struct watchdog_info armada_37xx_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Armada 37xx Watchdog", +}; + +static const struct watchdog_ops armada_37xx_wdt_ops = { + .owner = THIS_MODULE, + .start = armada_37xx_wdt_start, + .stop = armada_37xx_wdt_stop, + .ping = armada_37xx_wdt_ping, + .set_timeout = armada_37xx_wdt_set_timeout, + .get_timeleft = armada_37xx_wdt_get_timeleft, +}; + +static int armada_37xx_wdt_probe(struct platform_device *pdev) +{ + struct armada_37xx_watchdog *dev; + struct resource *res; + struct regmap *regmap; + int ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(struct armada_37xx_watchdog), + GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->wdt.info = &armada_37xx_wdt_info; + dev->wdt.ops = &armada_37xx_wdt_ops; + + regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "marvell,system-controller"); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + dev->cpu_misc = regmap; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + + /* init clock */ + dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return PTR_ERR(dev->clk); + + ret = clk_prepare_enable(dev->clk); + if (ret) + return ret; + + dev->clk_rate = clk_get_rate(dev->clk); + if (!dev->clk_rate) { + ret = -EINVAL; + goto disable_clk; + } + + /* + * Since the timeout in seconds is given as 32 bit unsigned int, and + * the counters hold 64 bit values, even after multiplication by clock + * rate the counter can hold timeout of UINT_MAX seconds. + */ + dev->wdt.min_timeout = 1; + dev->wdt.max_timeout = UINT_MAX; + dev->wdt.parent = &pdev->dev; + + /* default value, possibly override by module parameter or dtb */ + dev->wdt.timeout = WATCHDOG_TIMEOUT; + watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev); + + platform_set_drvdata(pdev, &dev->wdt); + watchdog_set_drvdata(&dev->wdt, dev); + + armada_37xx_wdt_set_timeout(&dev->wdt, dev->wdt.timeout); + + if (armada_37xx_wdt_is_running(dev)) + set_bit(WDOG_HW_RUNNING, &dev->wdt.status); + + watchdog_set_nowayout(&dev->wdt, nowayout); + ret = watchdog_register_device(&dev->wdt); + if (ret) + goto disable_clk; + + dev_info(&pdev->dev, "Initial timeout %d sec%s\n", + dev->wdt.timeout, nowayout ? ", nowayout" : ""); + + return 0; + +disable_clk: + clk_disable_unprepare(dev->clk); + return ret; +} + +static int armada_37xx_wdt_remove(struct platform_device *pdev) +{ + struct watchdog_device *wdt = platform_get_drvdata(pdev); + struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt); + + watchdog_unregister_device(wdt); + clk_disable_unprepare(dev->clk); + return 0; +} + +static void armada_37xx_wdt_shutdown(struct platform_device *pdev) +{ + struct watchdog_device *wdt = platform_get_drvdata(pdev); + + armada_37xx_wdt_stop(wdt); +} + +static int __maybe_unused armada_37xx_wdt_suspend(struct device *dev) +{ + struct watchdog_device *wdt = dev_get_drvdata(dev); + + return armada_37xx_wdt_stop(wdt); +} + +static int __maybe_unused armada_37xx_wdt_resume(struct device *dev) +{ + struct watchdog_device *wdt = dev_get_drvdata(dev); + + if (watchdog_active(wdt)) + return armada_37xx_wdt_start(wdt); + + return 0; +} + +static const struct dev_pm_ops armada_37xx_wdt_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(armada_37xx_wdt_suspend, + armada_37xx_wdt_resume) +}; + +#ifdef CONFIG_OF +static const struct of_device_id armada_37xx_wdt_match[] = { + { .compatible = "marvell,armada-3700-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, armada_37xx_wdt_match); +#endif + +static struct platform_driver armada_37xx_wdt_driver = { + .probe = armada_37xx_wdt_probe, + .remove = armada_37xx_wdt_remove, + .shutdown = armada_37xx_wdt_shutdown, + .driver = { + .name = "armada_37xx_wdt", + .of_match_table = of_match_ptr(armada_37xx_wdt_match), + .pm = &armada_37xx_wdt_dev_pm_ops, + }, +}; + +module_platform_driver(armada_37xx_wdt_driver); + +MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>"); +MODULE_DESCRIPTION("Armada 37xx CPU Watchdog"); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:armada_37xx_wdt"); diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 3ad1e44bef44..6e78a34c2370 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -25,7 +25,7 @@ /* If the kernel parameter wdt=1, the watchdog will be enabled at boot. * Also, the wdt_period sets the watchdog timer period timeout. * For E500 cpus the wdt_period sets which bit changing from 0->1 will - * trigger a watchog timeout. This watchdog timeout will occur 3 times, the + * trigger a watchdog timeout. This watchdog timeout will occur 3 times, the * first time nothing will happen, the second time a watchdog exception will * occur, and the final time the board will reset. */ diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 9dc62a461451..93562304f7aa 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -26,7 +26,7 @@ #include <linux/watchdog.h> #include <asm/nmi.h> -#define HPWDT_VERSION "2.0.0" +#define HPWDT_VERSION "2.0.1" #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) @@ -162,7 +162,7 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) if (ilo5 && ulReason == NMI_UNKNOWN && !mynmi) return NMI_DONE; - if (ilo5 && !pretimeout) + if (ilo5 && !pretimeout && !mynmi) return NMI_DONE; hpwdt_stop(); @@ -205,9 +205,7 @@ static struct watchdog_device hpwdt_dev = { .min_timeout = 1, .max_timeout = HPWDT_MAX_TIMER, .timeout = DEFAULT_MARGIN, -#ifdef CONFIG_HPWDT_NMI_DECODING .pretimeout = PRETIMEOUT_SEC, -#endif }; @@ -313,6 +311,12 @@ static int hpwdt_init_one(struct pci_dev *dev, if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL)) dev_warn(&dev->dev, "Invalid soft_margin: %d.\n", soft_margin); + if (pretimeout && hpwdt_dev.timeout <= PRETIMEOUT_SEC) { + dev_warn(&dev->dev, "timeout <= pretimeout. Setting pretimeout to zero\n"); + pretimeout = 0; + } + hpwdt_dev.pretimeout = pretimeout ? PRETIMEOUT_SEC : 0; + hpwdt_dev.parent = &dev->dev; retval = watchdog_register_device(&hpwdt_dev); if (retval < 0) { @@ -320,9 +324,12 @@ static int hpwdt_init_one(struct pci_dev *dev, goto error_wd_register; } - dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s" - ", timer margin: %d seconds (nowayout=%d).\n", - HPWDT_VERSION, hpwdt_dev.timeout, nowayout); + dev_info(&dev->dev, "HPE Watchdog Timer Driver: Version: %s\n", + HPWDT_VERSION); + dev_info(&dev->dev, "timeout: %d seconds (nowayout=%d)\n", + hpwdt_dev.timeout, nowayout); + dev_info(&dev->dev, "pretimeout: %s.\n", + pretimeout ? "on" : "off"); if (dev->subsystem_vendor == PCI_VENDOR_ID_HP_3PAR) ilo5 = true; @@ -364,6 +371,9 @@ MODULE_VERSION(HPWDT_VERSION); module_param(soft_margin, int, 0); MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); +module_param_named(timeout, soft_margin, int, 0); +MODULE_PARM_DESC(timeout, "Alias of soft_margin"); + module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); diff --git a/drivers/watchdog/iTCO_vendor.h b/drivers/watchdog/iTCO_vendor.h index 7b82a7c6e7c3..0f7373ba10d5 100644 --- a/drivers/watchdog/iTCO_vendor.h +++ b/drivers/watchdog/iTCO_vendor.h @@ -3,14 +3,10 @@ #ifdef CONFIG_ITCO_VENDOR_SUPPORT extern void iTCO_vendor_pre_start(struct resource *, unsigned int); extern void iTCO_vendor_pre_stop(struct resource *); -extern void iTCO_vendor_pre_keepalive(struct resource *, unsigned int); -extern void iTCO_vendor_pre_set_heartbeat(unsigned int); extern int iTCO_vendor_check_noreboot_on(void); #else #define iTCO_vendor_pre_start(acpibase, heartbeat) {} #define iTCO_vendor_pre_stop(acpibase) {} -#define iTCO_vendor_pre_keepalive(acpibase, heartbeat) {} -#define iTCO_vendor_pre_set_heartbeat(heartbeat) {} #define iTCO_vendor_check_noreboot_on() 1 /* 1=check noreboot; 0=don't check */ #endif diff --git a/drivers/watchdog/iTCO_vendor_support.c b/drivers/watchdog/iTCO_vendor_support.c index b6b2f90b5d44..68a9d9cc2eb8 100644 --- a/drivers/watchdog/iTCO_vendor_support.c +++ b/drivers/watchdog/iTCO_vendor_support.c @@ -38,7 +38,7 @@ /* List of vendor support modes */ /* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */ #define SUPERMICRO_OLD_BOARD 1 -/* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems */ +/* SuperMicro Pentium 4 / Xeon 4 / EMT64T Era Systems - no longer supported */ #define SUPERMICRO_NEW_BOARD 2 /* Broken BIOS */ #define BROKEN_BIOS 911 @@ -46,8 +46,7 @@ static int vendorsupport; module_param(vendorsupport, int, 0); MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default=" - "0 (none), 1=SuperMicro Pent3, 2=SuperMicro Pent4+, " - "911=Broken SMI BIOS"); + "0 (none), 1=SuperMicro Pent3, 911=Broken SMI BIOS"); /* * Vendor Specific Support @@ -98,143 +97,6 @@ static void supermicro_old_pre_stop(struct resource *smires) } /* - * Vendor Support: 2 - * Board: Super Micro Computer Inc. P4SBx, P4DPx - * iTCO chipset: ICH4 - * - * Code contributed by: R. Seretny <lkpatches@paypc.com> - * Documentation obtained by R. Seretny from SuperMicro Technical Support - * - * To enable Watchdog function: - * 1. BIOS - * For P4SBx: - * BIOS setup -> Advanced -> Integrated Peripherals -> Watch Dog Feature - * For P4DPx: - * BIOS setup -> Advanced -> I/O Device Configuration -> Watch Dog - * This setting enables or disables Watchdog function. When enabled, the - * default watchdog timer is set to be 5 minutes (about 4m35s). It is - * enough to load and run the OS. The application (service or driver) has - * to take over the control once OS is running up and before watchdog - * expires. - * - * 2. JUMPER - * For P4SBx: JP39 - * For P4DPx: JP37 - * This jumper is used for safety. Closed is enabled. This jumper - * prevents user enables watchdog in BIOS by accident. - * - * To enable Watch Dog function, both BIOS and JUMPER must be enabled. - * - * The documentation lists motherboards P4SBx and P4DPx series as of - * 20-March-2002. However, this code works flawlessly with much newer - * motherboards, such as my X6DHR-8G2 (SuperServer 6014H-82). - * - * The original iTCO driver as written does not actually reset the - * watchdog timer on these machines, as a result they reboot after five - * minutes. - * - * NOTE: You may leave the Watchdog function disabled in the SuperMicro - * BIOS to avoid a "boot-race"... This driver will enable watchdog - * functionality even if it's disabled in the BIOS once the /dev/watchdog - * file is opened. - */ - -/* I/O Port's */ -#define SM_REGINDEX 0x2e /* SuperMicro ICH4+ Register Index */ -#define SM_DATAIO 0x2f /* SuperMicro ICH4+ Register Data I/O */ - -/* Control Register's */ -#define SM_CTLPAGESW 0x07 /* SuperMicro ICH4+ Control Page Switch */ -#define SM_CTLPAGE 0x08 /* SuperMicro ICH4+ Control Page Num */ - -#define SM_WATCHENABLE 0x30 /* Watchdog enable: Bit 0: 0=off, 1=on */ - -#define SM_WATCHPAGE 0x87 /* Watchdog unlock control page */ - -#define SM_ENDWATCH 0xAA /* Watchdog lock control page */ - -#define SM_COUNTMODE 0xf5 /* Watchdog count mode select */ - /* (Bit 3: 0 = seconds, 1 = minutes */ - -#define SM_WATCHTIMER 0xf6 /* 8-bits, Watchdog timer counter (RW) */ - -#define SM_RESETCONTROL 0xf7 /* Watchdog reset control */ - /* Bit 6: timer is reset by kbd interrupt */ - /* Bit 7: timer is reset by mouse interrupt */ - -static void supermicro_new_unlock_watchdog(void) -{ - /* Write 0x87 to port 0x2e twice */ - outb(SM_WATCHPAGE, SM_REGINDEX); - outb(SM_WATCHPAGE, SM_REGINDEX); - /* Switch to watchdog control page */ - outb(SM_CTLPAGESW, SM_REGINDEX); - outb(SM_CTLPAGE, SM_DATAIO); -} - -static void supermicro_new_lock_watchdog(void) -{ - outb(SM_ENDWATCH, SM_REGINDEX); -} - -static void supermicro_new_pre_start(unsigned int heartbeat) -{ - unsigned int val; - - supermicro_new_unlock_watchdog(); - - /* Watchdog timer setting needs to be in seconds*/ - outb(SM_COUNTMODE, SM_REGINDEX); - val = inb(SM_DATAIO); - val &= 0xF7; - outb(val, SM_DATAIO); - - /* Write heartbeat interval to WDOG */ - outb(SM_WATCHTIMER, SM_REGINDEX); - outb((heartbeat & 255), SM_DATAIO); - - /* Make sure keyboard/mouse interrupts don't interfere */ - outb(SM_RESETCONTROL, SM_REGINDEX); - val = inb(SM_DATAIO); - val &= 0x3f; - outb(val, SM_DATAIO); - - /* enable watchdog by setting bit 0 of Watchdog Enable to 1 */ - outb(SM_WATCHENABLE, SM_REGINDEX); - val = inb(SM_DATAIO); - val |= 0x01; - outb(val, SM_DATAIO); - - supermicro_new_lock_watchdog(); -} - -static void supermicro_new_pre_stop(void) -{ - unsigned int val; - - supermicro_new_unlock_watchdog(); - - /* disable watchdog by setting bit 0 of Watchdog Enable to 0 */ - outb(SM_WATCHENABLE, SM_REGINDEX); - val = inb(SM_DATAIO); - val &= 0xFE; - outb(val, SM_DATAIO); - - supermicro_new_lock_watchdog(); -} - -static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat) -{ - supermicro_new_unlock_watchdog(); - - /* reset watchdog timeout to heartveat value */ - outb(SM_WATCHTIMER, SM_REGINDEX); - outb((heartbeat & 255), SM_DATAIO); - - supermicro_new_lock_watchdog(); -} - -/* * Vendor Support: 911 * Board: Some Intel ICHx based motherboards * iTCO chipset: ICH7+ @@ -298,9 +160,6 @@ void iTCO_vendor_pre_start(struct resource *smires, case SUPERMICRO_OLD_BOARD: supermicro_old_pre_start(smires); break; - case SUPERMICRO_NEW_BOARD: - supermicro_new_pre_start(heartbeat); - break; case BROKEN_BIOS: broken_bios_start(smires); break; @@ -314,9 +173,6 @@ void iTCO_vendor_pre_stop(struct resource *smires) case SUPERMICRO_OLD_BOARD: supermicro_old_pre_stop(smires); break; - case SUPERMICRO_NEW_BOARD: - supermicro_new_pre_stop(); - break; case BROKEN_BIOS: broken_bios_stop(smires); break; @@ -324,20 +180,6 @@ void iTCO_vendor_pre_stop(struct resource *smires) } EXPORT_SYMBOL(iTCO_vendor_pre_stop); -void iTCO_vendor_pre_keepalive(struct resource *smires, unsigned int heartbeat) -{ - if (vendorsupport == SUPERMICRO_NEW_BOARD) - supermicro_new_pre_set_heartbeat(heartbeat); -} -EXPORT_SYMBOL(iTCO_vendor_pre_keepalive); - -void iTCO_vendor_pre_set_heartbeat(unsigned int heartbeat) -{ - if (vendorsupport == SUPERMICRO_NEW_BOARD) - supermicro_new_pre_set_heartbeat(heartbeat); -} -EXPORT_SYMBOL(iTCO_vendor_pre_set_heartbeat); - int iTCO_vendor_check_noreboot_on(void) { switch (vendorsupport) { @@ -351,6 +193,12 @@ EXPORT_SYMBOL(iTCO_vendor_check_noreboot_on); static int __init iTCO_vendor_init_module(void) { + if (vendorsupport == SUPERMICRO_NEW_BOARD) { + pr_warn("Option vendorsupport=%d is no longer supported, " + "please use the w83627hf_wdt driver instead\n", + SUPERMICRO_NEW_BOARD); + return -EINVAL; + } pr_info("vendor-support=%d\n", vendorsupport); return 0; } diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 347f0389b089..0a5318b7865e 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -304,8 +304,6 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev) spin_lock(&p->io_lock); - iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout); - /* Reload the timer by writing to the TCO Timer Counter register */ if (p->iTCO_version >= 2) { outw(0x01, TCO_RLD(p)); @@ -342,8 +340,6 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) (p->iTCO_version == 1 && tmrval > 0x03f)) return -EINVAL; - iTCO_vendor_pre_set_heartbeat(tmrval); - /* Write new heartbeat to watchdog */ if (p->iTCO_version >= 2) { spin_lock(&p->io_lock); diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c index 7f43cefa0eae..83da84d6074b 100644 --- a/drivers/watchdog/lantiq_wdt.c +++ b/drivers/watchdog/lantiq_wdt.c @@ -8,11 +8,8 @@ * Based on EP93xx wdt driver */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/module.h> -#include <linux/fs.h> -#include <linux/miscdevice.h> +#include <linux/bitops.h> #include <linux/watchdog.h> #include <linux/of_platform.h> #include <linux/uaccess.h> @@ -40,169 +37,128 @@ * essentially the following two magic passwords need to be written to allow * IO access to the WDT core */ -#define LTQ_WDT_PW1 0x00BE0000 -#define LTQ_WDT_PW2 0x00DC0000 - -#define LTQ_WDT_CR 0x0 /* watchdog control register */ -#define LTQ_WDT_SR 0x8 /* watchdog status register */ +#define LTQ_WDT_CR_PW1 0x00BE0000 +#define LTQ_WDT_CR_PW2 0x00DC0000 + +#define LTQ_WDT_CR 0x0 /* watchdog control register */ +#define LTQ_WDT_CR_GEN BIT(31) /* enable bit */ +/* Pre-warning limit set to 1/16 of max WDT period */ +#define LTQ_WDT_CR_PWL (0x3 << 26) +/* set clock divider to 0x40000 */ +#define LTQ_WDT_CR_CLKDIV (0x3 << 24) +#define LTQ_WDT_CR_PW_MASK GENMASK(23, 16) /* Password field */ +#define LTQ_WDT_CR_MAX_TIMEOUT ((1 << 16) - 1) /* The reload field is 16 bit */ +#define LTQ_WDT_SR 0x8 /* watchdog status register */ +#define LTQ_WDT_SR_EN BIT(31) /* Enable */ +#define LTQ_WDT_SR_VALUE_MASK GENMASK(15, 0) /* Timer value */ -#define LTQ_WDT_SR_EN (0x1 << 31) /* enable bit */ -#define LTQ_WDT_SR_PWD (0x3 << 26) /* turn on power */ -#define LTQ_WDT_SR_CLKDIV (0x3 << 24) /* turn on clock and set */ - /* divider to 0x40000 */ #define LTQ_WDT_DIVIDER 0x40000 -#define LTQ_MAX_TIMEOUT ((1 << 16) - 1) /* the reload field is 16 bit */ static bool nowayout = WATCHDOG_NOWAYOUT; -static void __iomem *ltq_wdt_membase; -static unsigned long ltq_io_region_clk_rate; +struct ltq_wdt_hw { + int (*bootstatus_get)(struct device *dev); +}; -static unsigned long ltq_wdt_bootstatus; -static unsigned long ltq_wdt_in_use; -static int ltq_wdt_timeout = 30; -static int ltq_wdt_ok_to_close; +struct ltq_wdt_priv { + struct watchdog_device wdt; + void __iomem *membase; + unsigned long clk_rate; +}; -static void -ltq_wdt_enable(void) +static u32 ltq_wdt_r32(struct ltq_wdt_priv *priv, u32 offset) { - unsigned long int timeout = ltq_wdt_timeout * - (ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000; - if (timeout > LTQ_MAX_TIMEOUT) - timeout = LTQ_MAX_TIMEOUT; - - /* write the first password magic */ - ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR); - /* write the second magic plus the configuration and new timeout */ - ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV | - LTQ_WDT_PW2 | timeout, ltq_wdt_membase + LTQ_WDT_CR); + return __raw_readl(priv->membase + offset); } -static void -ltq_wdt_disable(void) +static void ltq_wdt_w32(struct ltq_wdt_priv *priv, u32 val, u32 offset) { - /* write the first password magic */ - ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR); - /* - * write the second password magic with no config - * this turns the watchdog off - */ - ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR); + __raw_writel(val, priv->membase + offset); } -static ssize_t -ltq_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) +static void ltq_wdt_mask(struct ltq_wdt_priv *priv, u32 clear, u32 set, + u32 offset) { - if (len) { - if (!nowayout) { - size_t i; - - ltq_wdt_ok_to_close = 0; - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - ltq_wdt_ok_to_close = 1; - else - ltq_wdt_ok_to_close = 0; - } - } - ltq_wdt_enable(); - } + u32 val = ltq_wdt_r32(priv, offset); - return len; + val &= ~(clear); + val |= set; + ltq_wdt_w32(priv, val, offset); } -static struct watchdog_info ident = { +static struct ltq_wdt_priv *ltq_wdt_get_priv(struct watchdog_device *wdt) +{ + return container_of(wdt, struct ltq_wdt_priv, wdt); +} + +static struct watchdog_info ltq_wdt_info = { .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | - WDIOF_CARDRESET, + WDIOF_CARDRESET, .identity = "ltq_wdt", }; -static long -ltq_wdt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) +static int ltq_wdt_start(struct watchdog_device *wdt) { - int ret = -ENOTTY; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info __user *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(ltq_wdt_bootstatus, (int __user *)arg); - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, (int __user *)arg); - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(ltq_wdt_timeout, (int __user *)arg); - if (!ret) - ltq_wdt_enable(); - /* intentional drop through */ - case WDIOC_GETTIMEOUT: - ret = put_user(ltq_wdt_timeout, (int __user *)arg); - break; - - case WDIOC_KEEPALIVE: - ltq_wdt_enable(); - ret = 0; - break; - } - return ret; + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); + u32 timeout; + + timeout = wdt->timeout * priv->clk_rate; + + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR); + /* write the second magic plus the configuration and new timeout */ + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT, + LTQ_WDT_CR_GEN | LTQ_WDT_CR_PWL | LTQ_WDT_CR_CLKDIV | + LTQ_WDT_CR_PW2 | timeout, + LTQ_WDT_CR); + + return 0; } -static int -ltq_wdt_open(struct inode *inode, struct file *file) +static int ltq_wdt_stop(struct watchdog_device *wdt) { - if (test_and_set_bit(0, <q_wdt_in_use)) - return -EBUSY; - ltq_wdt_in_use = 1; - ltq_wdt_enable(); + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); - return nonseekable_open(inode, file); + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR); + ltq_wdt_mask(priv, LTQ_WDT_CR_GEN | LTQ_WDT_CR_PW_MASK, + LTQ_WDT_CR_PW2, LTQ_WDT_CR); + + return 0; } -static int -ltq_wdt_release(struct inode *inode, struct file *file) +static int ltq_wdt_ping(struct watchdog_device *wdt) { - if (ltq_wdt_ok_to_close) - ltq_wdt_disable(); - else - pr_err("watchdog closed without warning\n"); - ltq_wdt_ok_to_close = 0; - clear_bit(0, <q_wdt_in_use); + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); + u32 timeout; + + timeout = wdt->timeout * priv->clk_rate; + + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK, LTQ_WDT_CR_PW1, LTQ_WDT_CR); + /* write the second magic plus the configuration and new timeout */ + ltq_wdt_mask(priv, LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT, + LTQ_WDT_CR_PW2 | timeout, LTQ_WDT_CR); return 0; } -static const struct file_operations ltq_wdt_fops = { - .owner = THIS_MODULE, - .write = ltq_wdt_write, - .unlocked_ioctl = ltq_wdt_ioctl, - .open = ltq_wdt_open, - .release = ltq_wdt_release, - .llseek = no_llseek, -}; +static unsigned int ltq_wdt_get_timeleft(struct watchdog_device *wdt) +{ + struct ltq_wdt_priv *priv = ltq_wdt_get_priv(wdt); + u64 timeout; -static struct miscdevice ltq_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = <q_wdt_fops, -}; + timeout = ltq_wdt_r32(priv, LTQ_WDT_SR) & LTQ_WDT_SR_VALUE_MASK; + return do_div(timeout, priv->clk_rate); +} -typedef int (*ltq_wdt_bootstatus_set)(struct platform_device *pdev); +static const struct watchdog_ops ltq_wdt_ops = { + .owner = THIS_MODULE, + .start = ltq_wdt_start, + .stop = ltq_wdt_stop, + .ping = ltq_wdt_ping, + .get_timeleft = ltq_wdt_get_timeleft, +}; -static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev) +static int ltq_wdt_xrx_bootstatus_get(struct device *dev) { - struct device *dev = &pdev->dev; struct regmap *rcu_regmap; u32 val; int err; @@ -216,14 +172,13 @@ static int ltq_wdt_bootstatus_xrx(struct platform_device *pdev) return err; if (val & LTQ_XRX_RCU_RST_STAT_WDT) - ltq_wdt_bootstatus = WDIOF_CARDRESET; + return WDIOF_CARDRESET; return 0; } -static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev) +static int ltq_wdt_falcon_bootstatus_get(struct device *dev) { - struct device *dev = &pdev->dev; struct regmap *rcu_regmap; u32 val; int err; @@ -238,62 +193,90 @@ static int ltq_wdt_bootstatus_falcon(struct platform_device *pdev) return err; if ((val & LTQ_FALCON_SYS1_CPU0RS_MASK) == LTQ_FALCON_SYS1_CPU0RS_WDT) - ltq_wdt_bootstatus = WDIOF_CARDRESET; + return WDIOF_CARDRESET; return 0; } -static int -ltq_wdt_probe(struct platform_device *pdev) +static int ltq_wdt_probe(struct platform_device *pdev) { - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct device *dev = &pdev->dev; + struct ltq_wdt_priv *priv; + struct watchdog_device *wdt; + struct resource *res; struct clk *clk; - ltq_wdt_bootstatus_set ltq_wdt_bootstatus_set; + const struct ltq_wdt_hw *ltq_wdt_hw; int ret; + u32 status; - ltq_wdt_membase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(ltq_wdt_membase)) - return PTR_ERR(ltq_wdt_membase); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - ltq_wdt_bootstatus_set = of_device_get_match_data(&pdev->dev); - if (ltq_wdt_bootstatus_set) { - ret = ltq_wdt_bootstatus_set(pdev); - if (ret) - return ret; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->membase = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->membase)) + return PTR_ERR(priv->membase); /* we do not need to enable the clock as it is always running */ clk = clk_get_io(); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "Failed to get clock\n"); - return -ENOENT; + priv->clk_rate = clk_get_rate(clk) / LTQ_WDT_DIVIDER; + if (!priv->clk_rate) { + dev_err(dev, "clock rate less than divider %i\n", + LTQ_WDT_DIVIDER); + return -EINVAL; } - ltq_io_region_clk_rate = clk_get_rate(clk); - clk_put(clk); - dev_info(&pdev->dev, "Init done\n"); - return misc_register(<q_wdt_miscdev); -} + wdt = &priv->wdt; + wdt->info = <q_wdt_info; + wdt->ops = <q_wdt_ops; + wdt->min_timeout = 1; + wdt->max_timeout = LTQ_WDT_CR_MAX_TIMEOUT / priv->clk_rate; + wdt->timeout = wdt->max_timeout; + wdt->parent = dev; + + ltq_wdt_hw = of_device_get_match_data(dev); + if (ltq_wdt_hw && ltq_wdt_hw->bootstatus_get) { + ret = ltq_wdt_hw->bootstatus_get(dev); + if (ret >= 0) + wdt->bootstatus = ret; + } -static int -ltq_wdt_remove(struct platform_device *pdev) -{ - misc_deregister(<q_wdt_miscdev); + watchdog_set_nowayout(wdt, nowayout); + watchdog_init_timeout(wdt, 0, dev); + + status = ltq_wdt_r32(priv, LTQ_WDT_SR); + if (status & LTQ_WDT_SR_EN) { + /* + * If the watchdog is already running overwrite it with our + * new settings. Stop is not needed as the start call will + * replace all settings anyway. + */ + ltq_wdt_start(wdt); + set_bit(WDOG_HW_RUNNING, &wdt->status); + } - return 0; + return devm_watchdog_register_device(dev, wdt); } +static const struct ltq_wdt_hw ltq_wdt_xrx100 = { + .bootstatus_get = ltq_wdt_xrx_bootstatus_get, +}; + +static const struct ltq_wdt_hw ltq_wdt_falcon = { + .bootstatus_get = ltq_wdt_falcon_bootstatus_get, +}; + static const struct of_device_id ltq_wdt_match[] = { - { .compatible = "lantiq,wdt", .data = NULL}, - { .compatible = "lantiq,xrx100-wdt", .data = ltq_wdt_bootstatus_xrx }, - { .compatible = "lantiq,falcon-wdt", .data = ltq_wdt_bootstatus_falcon }, + { .compatible = "lantiq,wdt", .data = NULL }, + { .compatible = "lantiq,xrx100-wdt", .data = <q_wdt_xrx100 }, + { .compatible = "lantiq,falcon-wdt", .data = <q_wdt_falcon }, {}, }; MODULE_DEVICE_TABLE(of, ltq_wdt_match); static struct platform_driver ltq_wdt_driver = { .probe = ltq_wdt_probe, - .remove = ltq_wdt_remove, .driver = { .name = "wdt", .of_match_table = ltq_wdt_match, diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index aca2d6323f8a..069072e6747d 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -17,8 +17,6 @@ * option) any later version. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> @@ -49,6 +47,7 @@ struct mpc8xxx_wdt { struct mpc8xxx_wdt_type { int prescaler; bool hw_enabled; + u32 rsr_mask; }; struct mpc8xxx_wdt_ddata { @@ -137,36 +136,55 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) struct mpc8xxx_wdt_ddata *ddata; u32 freq = fsl_get_sys_freq(); bool enabled; + struct device *dev = &ofdev->dev; - wdt_type = of_device_get_match_data(&ofdev->dev); + wdt_type = of_device_get_match_data(dev); if (!wdt_type) return -EINVAL; if (!freq || freq == -1) return -EINVAL; - ddata = devm_kzalloc(&ofdev->dev, sizeof(*ddata), GFP_KERNEL); + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) return -ENOMEM; res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); - ddata->base = devm_ioremap_resource(&ofdev->dev, res); + ddata->base = devm_ioremap_resource(dev, res); if (IS_ERR(ddata->base)) return PTR_ERR(ddata->base); enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN; if (!enabled && wdt_type->hw_enabled) { - pr_info("could not be enabled in software\n"); + dev_info(dev, "could not be enabled in software\n"); return -ENODEV; } + res = platform_get_resource(ofdev, IORESOURCE_MEM, 1); + if (res) { + bool status; + u32 __iomem *rsr = ioremap(res->start, resource_size(res)); + + if (!rsr) + return -ENOMEM; + + status = in_be32(rsr) & wdt_type->rsr_mask; + ddata->wdd.bootstatus = status ? WDIOF_CARDRESET : 0; + /* clear reset status bits related to watchdog timer */ + out_be32(rsr, wdt_type->rsr_mask); + iounmap(rsr); + + dev_info(dev, "Last boot was %scaused by watchdog\n", + status ? "" : "not "); + } + spin_lock_init(&ddata->lock); ddata->wdd.info = &mpc8xxx_wdt_info, ddata->wdd.ops = &mpc8xxx_wdt_ops, ddata->wdd.timeout = WATCHDOG_TIMEOUT; - watchdog_init_timeout(&ddata->wdd, timeout, &ofdev->dev); + watchdog_init_timeout(&ddata->wdd, timeout, dev); watchdog_set_nowayout(&ddata->wdd, nowayout); @@ -189,12 +207,13 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev) ret = watchdog_register_device(&ddata->wdd); if (ret) { - pr_err("cannot register watchdog device (err=%d)\n", ret); + dev_err(dev, "cannot register watchdog device (err=%d)\n", ret); return ret; } - pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n", - reset ? "reset" : "interrupt", ddata->wdd.timeout); + dev_info(dev, + "WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n", + reset ? "reset" : "interrupt", ddata->wdd.timeout); platform_set_drvdata(ofdev, ddata); return 0; @@ -204,8 +223,8 @@ static int mpc8xxx_wdt_remove(struct platform_device *ofdev) { struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev); - pr_crit("Watchdog removed, expect the %s soon!\n", - reset ? "reset" : "machine check exception"); + dev_crit(&ofdev->dev, "Watchdog removed, expect the %s soon!\n", + reset ? "reset" : "machine check exception"); watchdog_unregister_device(&ddata->wdd); return 0; @@ -216,6 +235,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { .compatible = "mpc83xx_wdt", .data = &(struct mpc8xxx_wdt_type) { .prescaler = 0x10000, + .rsr_mask = BIT(3), /* RSR Bit SWRS */ }, }, { @@ -223,6 +243,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { .data = &(struct mpc8xxx_wdt_type) { .prescaler = 0x10000, .hw_enabled = true, + .rsr_mask = BIT(20), /* RSTRSCR Bit WDT_RR */ }, }, { @@ -230,6 +251,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { .data = &(struct mpc8xxx_wdt_type) { .prescaler = 0x800, .hw_enabled = true, + .rsr_mask = BIT(28), /* RSR Bit SWRS */ }, }, {}, diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c index 88d81feba4e6..0d74c3e48979 100644 --- a/drivers/watchdog/renesas_wdt.c +++ b/drivers/watchdog/renesas_wdt.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Watchdog driver for Renesas WDT watchdog * * Copyright (C) 2015-17 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> * Copyright (C) 2015-17 Renesas Electronics Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. */ #include <linux/bitops.h> #include <linux/clk.h> @@ -234,6 +231,7 @@ static int rwdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&priv->wdev, priv); watchdog_set_nowayout(&priv->wdev, nowayout); watchdog_set_restart_priority(&priv->wdev, 0); + watchdog_stop_on_unregister(&priv->wdev); /* This overrides the default timeout only if DT configuration was found */ ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev); diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c index e618218d2374..781bb572e6af 100644 --- a/drivers/watchdog/rza_wdt.c +++ b/drivers/watchdog/rza_wdt.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * 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 <linux/bitops.h> @@ -14,6 +11,7 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/watchdog.h> @@ -34,12 +32,45 @@ #define WRCSR_RSTE BIT(6) #define WRCSR_CLEAR_WOVF 0xA500 /* special value */ +/* The maximum CKS register setting value to get the longest timeout */ +#define CKS_3BIT 0x7 +#define CKS_4BIT 0xF + +#define DIVIDER_3BIT 16384 /* Clock divider when CKS = 0x7 */ +#define DIVIDER_4BIT 4194304 /* Clock divider when CKS = 0xF */ + struct rza_wdt { struct watchdog_device wdev; void __iomem *base; struct clk *clk; + u8 count; + u8 cks; }; +static void rza_wdt_calc_timeout(struct rza_wdt *priv, int timeout) +{ + unsigned long rate = clk_get_rate(priv->clk); + unsigned int ticks; + + if (priv->cks == CKS_4BIT) { + ticks = DIV_ROUND_UP(timeout * rate, DIVIDER_4BIT); + + /* + * Since max_timeout was set in probe, we know that the timeout + * value passed will never calculate to a tick value greater + * than 256. + */ + priv->count = 256 - ticks; + + } else { + /* Start timer with longest timeout */ + priv->count = 0; + } + + pr_debug("%s: timeout set to %u (WTCNT=%d)\n", __func__, + timeout, priv->count); +} + static int rza_wdt_start(struct watchdog_device *wdev) { struct rza_wdt *priv = watchdog_get_drvdata(wdev); @@ -51,13 +82,12 @@ static int rza_wdt_start(struct watchdog_device *wdev) readb(priv->base + WRCSR); writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR); - /* - * Start timer with slowest clock source and reset option enabled. - */ + rza_wdt_calc_timeout(priv, wdev->timeout); + 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); + writew(WTCNT_MAGIC | priv->count, priv->base + WTCNT); + writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME | + WTSCR_CKS(priv->cks), priv->base + WTCSR); return 0; } @@ -75,8 +105,17 @@ static int rza_wdt_ping(struct watchdog_device *wdev) { struct rza_wdt *priv = watchdog_get_drvdata(wdev); - writew(WTCNT_MAGIC | 0, priv->base + WTCNT); + writew(WTCNT_MAGIC | priv->count, priv->base + WTCNT); + + pr_debug("%s: timeout = %u\n", __func__, wdev->timeout); + + return 0; +} +static int rza_set_timeout(struct watchdog_device *wdev, unsigned int timeout) +{ + wdev->timeout = timeout; + rza_wdt_start(wdev); return 0; } @@ -121,6 +160,7 @@ static const struct watchdog_ops rza_wdt_ops = { .start = rza_wdt_start, .stop = rza_wdt_stop, .ping = rza_wdt_ping, + .set_timeout = rza_set_timeout, .restart = rza_wdt_restart, }; @@ -150,20 +190,28 @@ static int rza_wdt_probe(struct platform_device *pdev) 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->cks = (u8)(uintptr_t)of_device_get_match_data(&pdev->dev); + if (priv->cks == CKS_4BIT) { + /* Assume slowest clock rate possible (CKS=0xF) */ + priv->wdev.max_timeout = (DIVIDER_4BIT * U8_MAX) / rate; + + } else if (priv->cks == CKS_3BIT) { + /* Assume slowest clock rate possible (CKS=7) */ + rate /= DIVIDER_3BIT; + + /* + * 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; @@ -179,7 +227,8 @@ static int rza_wdt_probe(struct platform_device *pdev) } static const struct of_device_id rza_wdt_of_match[] = { - { .compatible = "renesas,rza-wdt", }, + { .compatible = "renesas,r7s9210-wdt", .data = (void *)CKS_4BIT, }, + { .compatible = "renesas,rza-wdt", .data = (void *)CKS_3BIT, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rza_wdt_of_match); diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index 255169916dbb..1e93c1b0e3cf 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -247,11 +247,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) } } - ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "unable to set timeout value\n"); - return ret; - } + watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev); timeout = WDT_SEC2TICKS(wdd->timeout); diff --git a/drivers/watchdog/ts4800_wdt.c b/drivers/watchdog/ts4800_wdt.c index 2b8de8602b67..89843b16b04a 100644 --- a/drivers/watchdog/ts4800_wdt.c +++ b/drivers/watchdog/ts4800_wdt.c @@ -135,6 +135,7 @@ static int ts4800_wdt_probe(struct platform_device *pdev) /* set regmap and offset to know where to write */ wdt->feed_offset = reg; wdt->regmap = syscon_node_to_regmap(syscon_np); + of_node_put(syscon_np); if (IS_ERR(wdt->regmap)) { dev_err(&pdev->dev, "cannot get parent's regmap\n"); return PTR_ERR(wdt->regmap); diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c index b085ef1084ec..d56d0a927a1e 100644 --- a/drivers/watchdog/via_wdt.c +++ b/drivers/watchdog/via_wdt.c @@ -30,7 +30,7 @@ #define VIA_WDT_CONF_MMIO 0x02 /* 1: enable watchdog MMIO */ /* - * The MMIO region contains the watchog control register and the + * The MMIO region contains the watchdog control register and the * hardware timer counter. */ #define VIA_WDT_MMIO_LEN 8 /* MMIO region length in bytes */ @@ -82,7 +82,7 @@ static inline void wdt_reset(void) /* * Timer tick: the timer will make sure that the watchdog timer hardware * is being reset in time. The conditions to do this are: - * 1) the watchog timer has been started and /dev/watchdog is open + * 1) the watchdog timer has been started and /dev/watchdog is open * and there is still time left before userspace should send the * next heartbeat/ping. (note: the internal heartbeat is much smaller * then the external/userspace heartbeat). diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 7817836bff55..4b9365d4de7a 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -50,7 +50,7 @@ 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, nct6793, - nct6795, nct6102 }; + nct6795, nct6796, nct6102 }; static int timeout; /* in seconds */ module_param(timeout, int, 0); @@ -100,6 +100,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); #define NCT6792_ID 0xc9 #define NCT6793_ID 0xd1 #define NCT6795_ID 0xd3 +#define NCT6796_ID 0xd4 /* also NCT9697D, NCT9698D */ #define W83627HF_WDT_TIMEOUT 0xf6 #define W83697HF_WDT_TIMEOUT 0xf4 @@ -209,6 +210,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) case nct6792: case nct6793: case nct6795: + case nct6796: case nct6102: /* * These chips have a fixed WDTO# output pin (W83627UHG), @@ -407,6 +409,9 @@ static int wdt_find(int addr) case NCT6795_ID: ret = nct6795; break; + case NCT6796_ID: + ret = nct6796; + break; case NCT6102_ID: ret = nct6102; cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; @@ -450,6 +455,7 @@ static int __init wdt_init(void) "NCT6792", "NCT6793", "NCT6795", + "NCT6796", "NCT6102", }; diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index ffbdc4642ea5..f6c24b22b37c 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -1019,16 +1019,16 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) old_wd_data = NULL; } - mutex_lock(&wd_data->lock); - wd_data->wdd = NULL; - wdd->wd_data = NULL; - mutex_unlock(&wd_data->lock); - if (watchdog_active(wdd) && test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) { watchdog_stop(wdd); } + mutex_lock(&wd_data->lock); + wd_data->wdd = NULL; + wdd->wd_data = NULL; + mutex_unlock(&wd_data->lock); + hrtimer_cancel(&wd_data->timer); kthread_cancel_work_sync(&wd_data->work); |