diff options
31 files changed, 1131 insertions, 79 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/of-xilinx-wdt.txt b/Documentation/devicetree/bindings/watchdog/of-xilinx-wdt.txt index 6d63782a7378..c6ae9c9d5e3e 100644 --- a/Documentation/devicetree/bindings/watchdog/of-xilinx-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/of-xilinx-wdt.txt @@ -7,6 +7,8 @@ Required properties: - reg : Physical base address and size Optional properties: +- clocks : Input clock specifier. Refer to common clock + bindings. - clock-frequency : Frequency of clock in Hz - xlnx,wdt-enable-once : 0 - Watchdog can be restarted 1 - Watchdog can be enabled just once @@ -17,6 +19,7 @@ Example: axi-timebase-wdt@40100000 { clock-frequency = <50000000>; compatible = "xlnx,xps-timebase-wdt-1.00.a"; + clocks = <&clkc 15>; reg = <0x40100000 0x10000>; xlnx,wdt-enable-once = <0x0>; xlnx,wdt-interval = <0x1b>; diff --git a/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt b/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt index 039c5ca45577..b949039bc502 100644 --- a/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt @@ -9,8 +9,7 @@ functionality. Required properties -- compatible : Must be one of: "st,stih407-lpc" "st,stih416-lpc" - "st,stih415-lpc" "st,stid127-lpc" +- compatible : Should be: "st,stih407-lpc" - reg : LPC registers base address + size - interrupts : LPC interrupt line number and associated flags - clocks : Clock used by LPC device (See: ../clock/clock-bindings.txt) diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 7f31125c123e..ea277478982f 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -48,8 +48,10 @@ struct watchdog_device { const struct attribute_group **groups; const struct watchdog_info *info; const struct watchdog_ops *ops; + const struct watchdog_governor *gov; unsigned int bootstatus; unsigned int timeout; + unsigned int pretimeout; unsigned int min_timeout; unsigned int max_timeout; unsigned int min_hw_heartbeat_ms; @@ -74,9 +76,11 @@ It contains following fields: * info: a pointer to a watchdog_info structure. This structure gives some additional information about the watchdog timer itself. (Like it's unique name) * ops: a pointer to the list of watchdog operations that the watchdog supports. +* gov: a pointer to the assigned watchdog device pretimeout governor or NULL. * timeout: the watchdog timer's timeout value (in seconds). This is the time after which the system will reboot if user space does not send a heartbeat request if WDOG_ACTIVE is set. +* pretimeout: the watchdog timer's pretimeout value (in seconds). * min_timeout: the watchdog timer's minimum timeout value (in seconds). If set, the minimum configurable value for 'timeout'. * max_timeout: the watchdog timer's maximum timeout value (in seconds), @@ -121,6 +125,7 @@ struct watchdog_ops { int (*ping)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *); int (*set_timeout)(struct watchdog_device *, unsigned int); + int (*set_pretimeout)(struct watchdog_device *, unsigned int); unsigned int (*get_timeleft)(struct watchdog_device *); int (*restart)(struct watchdog_device *); void (*ref)(struct watchdog_device *) __deprecated; @@ -188,6 +193,23 @@ they are supported. These optional routines/operations are: If set_timeout is not provided but, WDIOF_SETTIMEOUT is set, the watchdog infrastructure updates the timeout value of the watchdog_device internally to the requested value. + If the pretimeout feature is used (WDIOF_PRETIMEOUT), then set_timeout must + also take care of checking if pretimeout is still valid and set up the timer + accordingly. This can't be done in the core without races, so it is the + duty of the driver. +* set_pretimeout: this routine checks and changes the pretimeout value of + the watchdog. It is optional because not all watchdogs support pretimeout + notification. The timeout value is not an absolute time, but the number of + seconds before the actual timeout would happen. It returns 0 on success, + -EINVAL for "parameter out of range" and -EIO for "could not write value to + the watchdog". A value of 0 disables pretimeout notification. + (Note: the WDIOF_PRETIMEOUT needs to be set in the options field of the + watchdog's info structure). + If the watchdog driver does not have to perform any action but setting the + watchdog_device.pretimeout, this callback can be omitted. That means if + set_pretimeout is not provided but WDIOF_PRETIMEOUT is set, the watchdog + infrastructure updates the pretimeout value of the watchdog_device internally + to the requested value. * get_timeleft: this routines returns the time that's left before a reset. * restart: this routine restarts the machine. It returns 0 on success or a negative errno code for failure. @@ -268,3 +290,14 @@ User should follow the following guidelines for setting the priority: * 128: default restart handler, use if no other handler is expected to be available, and/or if restart is sufficient to restart the entire system * 255: highest priority, will preempt all other restart handlers + +To raise a pretimeout notification, the following function should be used: + +void watchdog_notify_pretimeout(struct watchdog_device *wdd) + +The function can be called in the interrupt context. If watchdog pretimeout +governor framework (kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV symbol) is enabled, +an action is taken by a preconfigured pretimeout governor preassigned to +the watchdog device. If watchdog pretimeout governor framework is not +enabled, watchdog_notify_pretimeout() prints a notification message to +the kernel log buffer. diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 50dbaa805658..fdd3228e0678 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1844,4 +1844,53 @@ config USBPCWATCHDOG Most people will say N. +comment "Watchdog Pretimeout Governors" + +config WATCHDOG_PRETIMEOUT_GOV + bool "Enable watchdog pretimeout governors" + help + The option allows to select watchdog pretimeout governors. + +if WATCHDOG_PRETIMEOUT_GOV + +choice + prompt "Default Watchdog Pretimeout Governor" + default WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC + help + This option selects a default watchdog pretimeout governor. + The governor takes its action, if a watchdog is capable + to report a pretimeout event. + +config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP + bool "noop" + select WATCHDOG_PRETIMEOUT_GOV_NOOP + help + Use noop watchdog pretimeout governor by default. If noop + governor is selected by a user, write a short message to + the kernel log buffer and don't do any system changes. + +config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC + bool "panic" + select WATCHDOG_PRETIMEOUT_GOV_PANIC + help + Use panic watchdog pretimeout governor by default, if + a watchdog pretimeout event happens, consider that + a watchdog feeder is dead and reboot is unavoidable. + +endchoice + +config WATCHDOG_PRETIMEOUT_GOV_NOOP + tristate "Noop watchdog pretimeout governor" + help + Noop watchdog pretimeout governor, only an informational + message is added to kernel log buffer. + +config WATCHDOG_PRETIMEOUT_GOV_PANIC + tristate "Panic watchdog pretimeout governor" + help + Panic watchdog pretimeout governor, on watchdog pretimeout + event put the kernel into panic. + +endif # WATCHDOG_PRETIMEOUT_GOV + endif # WATCHDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index cba00430151b..caa9f4aa492a 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -3,9 +3,15 @@ # # The WatchDog Timer Driver Core. -watchdog-objs += watchdog_core.o watchdog_dev.o obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o +watchdog-objs += watchdog_core.o watchdog_dev.o + +watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV) += watchdog_pretimeout.o + +obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP) += pretimeout_noop.o +obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC) += pretimeout_panic.o + # Only one watchdog can succeed. We probe the ISA/PCI/USB based # watchdog-cards first, then the architecture specific watchdog # drivers and then the architecture independent "softdog" driver. diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c index c9686b2fdafd..d0b59ba0f661 100644 --- a/drivers/watchdog/asm9260_wdt.c +++ b/drivers/watchdog/asm9260_wdt.c @@ -389,7 +389,6 @@ MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match); static struct platform_driver asm9260_wdt_driver = { .driver = { .name = "asm9260-wdt", - .owner = THIS_MODULE, .of_match_table = asm9260_wdt_of_match, }, .probe = asm9260_wdt_probe, diff --git a/drivers/watchdog/bcm7038_wdt.c b/drivers/watchdog/bcm7038_wdt.c index 4245b65d645c..e238df4d75a2 100644 --- a/drivers/watchdog/bcm7038_wdt.c +++ b/drivers/watchdog/bcm7038_wdt.c @@ -107,7 +107,7 @@ static struct watchdog_info bcm7038_wdt_info = { WDIOF_MAGICCLOSE }; -static struct watchdog_ops bcm7038_wdt_ops = { +static const struct watchdog_ops bcm7038_wdt_ops = { .owner = THIS_MODULE, .start = bcm7038_wdt_start, .stop = bcm7038_wdt_stop, diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index 4dda9024e229..98acef72334d 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -269,7 +269,7 @@ static struct watchdog_info cdns_wdt_info = { }; /* Watchdog Core Ops */ -static struct watchdog_ops cdns_wdt_ops = { +static const struct watchdog_ops cdns_wdt_ops = { .owner = THIS_MODULE, .start = cdns_wdt_start, .stop = cdns_wdt_stop, @@ -424,8 +424,10 @@ static int __maybe_unused cdns_wdt_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct cdns_wdt *wdt = platform_get_drvdata(pdev); - cdns_wdt_stop(&wdt->cdns_wdt_device); - clk_disable_unprepare(wdt->clk); + if (watchdog_active(&wdt->cdns_wdt_device)) { + cdns_wdt_stop(&wdt->cdns_wdt_device); + clk_disable_unprepare(wdt->clk); + } return 0; } @@ -442,12 +444,14 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct cdns_wdt *wdt = platform_get_drvdata(pdev); - ret = clk_prepare_enable(wdt->clk); - if (ret) { - dev_err(dev, "unable to enable clock\n"); - return ret; + if (watchdog_active(&wdt->cdns_wdt_device)) { + ret = clk_prepare_enable(wdt->clk); + if (ret) { + dev_err(dev, "unable to enable clock\n"); + return ret; + } + cdns_wdt_start(&wdt->cdns_wdt_device); } - cdns_wdt_start(&wdt->cdns_wdt_device); return 0; } diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 2acb51cf5504..3c6a3de13a1b 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -54,6 +54,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " struct dw_wdt { void __iomem *regs; struct clk *clk; + unsigned long rate; struct notifier_block restart_handler; struct watchdog_device wdd; }; @@ -72,7 +73,7 @@ static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top) * There are 16 possible timeout values in 0..15 where the number of * cycles is 2 ^ (16 + i) and the watchdog counts down. */ - return (1U << (16 + top)) / clk_get_rate(dw_wdt->clk); + return (1U << (16 + top)) / dw_wdt->rate; } static int dw_wdt_get_top(struct dw_wdt *dw_wdt) @@ -163,7 +164,7 @@ static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) struct dw_wdt *dw_wdt = to_dw_wdt(wdd); return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) / - clk_get_rate(dw_wdt->clk); + dw_wdt->rate; } static const struct watchdog_info dw_wdt_ident = { @@ -231,6 +232,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) if (ret) return ret; + dw_wdt->rate = clk_get_rate(dw_wdt->clk); + if (dw_wdt->rate == 0) { + ret = -EINVAL; + goto out_disable_clk; + } + wdd = &dw_wdt->wdd; wdd->info = &dw_wdt_ident; wdd->ops = &dw_wdt_ops; diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 8f89bd8a826a..70c7194e2810 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -39,7 +39,7 @@ #include <asm/nmi.h> #include <asm/frame.h> -#define HPWDT_VERSION "1.3.3" +#define HPWDT_VERSION "1.4.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) @@ -814,7 +814,8 @@ static int hpwdt_init_one(struct pci_dev *dev, * not run on a legacy ASM box. * So we only support the G5 ProLiant servers and higher. */ - if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { + if (dev->subsystem_vendor != PCI_VENDOR_ID_HP && + dev->subsystem_vendor != PCI_VENDOR_ID_HP_3PAR) { dev_warn(&dev->dev, "This server does not have an iLO2+ ASIC.\n"); return -ENODEV; @@ -823,7 +824,8 @@ static int hpwdt_init_one(struct pci_dev *dev, /* * Ignore all auxilary iLO devices with the following PCI ID */ - if (dev->subsystem_device == 0x1979) + if (dev->subsystem_vendor == PCI_VENDOR_ID_HP && + dev->subsystem_device == 0x1979) return -ENODEV; if (pci_enable_device(dev)) { diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 54cab189a763..06fcb6c8c917 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -629,7 +629,7 @@ static int iTCO_wdt_resume_noirq(struct device *dev) return 0; } -static struct dev_pm_ops iTCO_wdt_pm = { +static const struct dev_pm_ops iTCO_wdt_pm = { .suspend_noirq = iTCO_wdt_suspend_noirq, .resume_noirq = iTCO_wdt_resume_noirq, }; diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 62f346bb4348..4874b0f18650 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -24,6 +24,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> @@ -37,18 +38,23 @@ #define IMX2_WDT_WCR 0x00 /* Control Register */ #define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */ -#define IMX2_WDT_WCR_WDA (1 << 5) /* -> External Reset WDOG_B */ -#define IMX2_WDT_WCR_SRS (1 << 4) /* -> Software Reset Signal */ -#define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */ -#define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */ -#define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */ +#define IMX2_WDT_WCR_WDA BIT(5) /* -> External Reset WDOG_B */ +#define IMX2_WDT_WCR_SRS BIT(4) /* -> Software Reset Signal */ +#define IMX2_WDT_WCR_WRE BIT(3) /* -> WDOG Reset Enable */ +#define IMX2_WDT_WCR_WDE BIT(2) /* -> Watchdog Enable */ +#define IMX2_WDT_WCR_WDZST BIT(0) /* -> Watchdog timer Suspend */ #define IMX2_WDT_WSR 0x02 /* Service Register */ #define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */ #define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */ #define IMX2_WDT_WRSR 0x04 /* Reset Status Register */ -#define IMX2_WDT_WRSR_TOUT (1 << 1) /* -> Reset due to Timeout */ +#define IMX2_WDT_WRSR_TOUT BIT(1) /* -> Reset due to Timeout */ + +#define IMX2_WDT_WICR 0x06 /* Interrupt Control Register */ +#define IMX2_WDT_WICR_WIE BIT(15) /* -> Interrupt Enable */ +#define IMX2_WDT_WICR_WTIS BIT(14) /* -> Interrupt Status */ +#define IMX2_WDT_WICR_WICT 0xFF /* -> Interrupt Count Timeout */ #define IMX2_WDT_WMCR 0x08 /* Misc Register */ @@ -80,6 +86,12 @@ static const struct watchdog_info imx2_wdt_info = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, }; +static const struct watchdog_info imx2_wdt_pretimeout_info = { + .identity = "imx2+ watchdog", + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | + WDIOF_PRETIMEOUT, +}; + static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action, void *data) { @@ -169,6 +181,35 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog, return 0; } +static int imx2_wdt_set_pretimeout(struct watchdog_device *wdog, + unsigned int new_pretimeout) +{ + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + if (new_pretimeout >= IMX2_WDT_MAX_TIME) + return -EINVAL; + + wdog->pretimeout = new_pretimeout; + + regmap_update_bits(wdev->regmap, IMX2_WDT_WICR, + IMX2_WDT_WICR_WIE | IMX2_WDT_WICR_WICT, + IMX2_WDT_WICR_WIE | (new_pretimeout << 1)); + return 0; +} + +static irqreturn_t imx2_wdt_isr(int irq, void *wdog_arg) +{ + struct watchdog_device *wdog = wdog_arg; + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + regmap_write_bits(wdev->regmap, IMX2_WDT_WICR, + IMX2_WDT_WICR_WTIS, IMX2_WDT_WICR_WTIS); + + watchdog_notify_pretimeout(wdog); + + return IRQ_HANDLED; +} + static int imx2_wdt_start(struct watchdog_device *wdog) { struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); @@ -188,6 +229,7 @@ static const struct watchdog_ops imx2_wdt_ops = { .start = imx2_wdt_start, .ping = imx2_wdt_ping, .set_timeout = imx2_wdt_set_timeout, + .set_pretimeout = imx2_wdt_set_pretimeout, .restart = imx2_wdt_restart, }; @@ -236,6 +278,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000; wdog->parent = &pdev->dev; + ret = platform_get_irq(pdev, 0); + if (ret > 0) + if (!devm_request_irq(&pdev->dev, ret, imx2_wdt_isr, 0, + dev_name(&pdev->dev), wdog)) + wdog->info = &imx2_wdt_pretimeout_info; + ret = clk_prepare_enable(wdev->clk); if (ret) return ret; diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c index 5bf931ce1353..8e302d0e346c 100644 --- a/drivers/watchdog/kempld_wdt.c +++ b/drivers/watchdog/kempld_wdt.c @@ -430,7 +430,7 @@ static struct watchdog_info kempld_wdt_info = { WDIOF_PRETIMEOUT }; -static struct watchdog_ops kempld_wdt_ops = { +static const struct watchdog_ops kempld_wdt_ops = { .owner = THIS_MODULE, .start = kempld_wdt_start, .stop = kempld_wdt_stop, diff --git a/drivers/watchdog/mt7621_wdt.c b/drivers/watchdog/mt7621_wdt.c index 4a2290f900a8..d5735c12067d 100644 --- a/drivers/watchdog/mt7621_wdt.c +++ b/drivers/watchdog/mt7621_wdt.c @@ -139,7 +139,6 @@ static int mt7621_wdt_probe(struct platform_device *pdev) if (!IS_ERR(mt7621_wdt_reset)) reset_control_deassert(mt7621_wdt_reset); - mt7621_wdt_dev.dev = &pdev->dev; mt7621_wdt_dev.bootstatus = mt7621_wdt_bootcause(); watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout, diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c index b2e1b4cbbdc1..fae7fe929ea3 100644 --- a/drivers/watchdog/of_xilinx_wdt.c +++ b/drivers/watchdog/of_xilinx_wdt.c @@ -10,6 +10,7 @@ * 2 of the License, or (at your option) any later version. */ +#include <linux/clk.h> #include <linux/err.h> #include <linux/module.h> #include <linux/types.h> @@ -45,6 +46,7 @@ struct xwdt_device { u32 wdt_interval; spinlock_t spinlock; struct watchdog_device xilinx_wdt_wdd; + struct clk *clk; }; static int xilinx_wdt_start(struct watchdog_device *wdd) @@ -195,16 +197,30 @@ static int xwdt_probe(struct platform_device *pdev) spin_lock_init(&xdev->spinlock); watchdog_set_drvdata(xilinx_wdt_wdd, xdev); + xdev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(xdev->clk)) { + if (PTR_ERR(xdev->clk) == -ENOENT) + xdev->clk = NULL; + else + return PTR_ERR(xdev->clk); + } + + rc = clk_prepare_enable(xdev->clk); + if (rc) { + dev_err(&pdev->dev, "unable to enable clock\n"); + return rc; + } + rc = xwdt_selftest(xdev); if (rc == XWT_TIMER_FAILED) { dev_err(&pdev->dev, "SelfTest routine error\n"); - return rc; + goto err_clk_disable; } rc = watchdog_register_device(xilinx_wdt_wdd); if (rc) { dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc); - return rc; + goto err_clk_disable; } dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n", @@ -213,6 +229,10 @@ static int xwdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, xdev); return 0; +err_clk_disable: + clk_disable_unprepare(xdev->clk); + + return rc; } static int xwdt_remove(struct platform_device *pdev) @@ -220,6 +240,7 @@ static int xwdt_remove(struct platform_device *pdev) struct xwdt_device *xdev = platform_get_drvdata(pdev); watchdog_unregister_device(&xdev->xilinx_wdt_wdd); + clk_disable_unprepare(xdev->clk); return 0; } diff --git a/drivers/watchdog/pretimeout_noop.c b/drivers/watchdog/pretimeout_noop.c new file mode 100644 index 000000000000..85f5299d197c --- /dev/null +++ b/drivers/watchdog/pretimeout_noop.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015-2016 Mentor Graphics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/watchdog.h> + +#include "watchdog_pretimeout.h" + +/** + * pretimeout_noop - No operation on watchdog pretimeout event + * @wdd - watchdog_device + * + * This function prints a message about pretimeout to kernel log. + */ +static void pretimeout_noop(struct watchdog_device *wdd) +{ + pr_alert("watchdog%d: pretimeout event\n", wdd->id); +} + +static struct watchdog_governor watchdog_gov_noop = { + .name = "noop", + .pretimeout = pretimeout_noop, +}; + +static int __init watchdog_gov_noop_register(void) +{ + return watchdog_register_governor(&watchdog_gov_noop); +} + +static void __exit watchdog_gov_noop_unregister(void) +{ + watchdog_unregister_governor(&watchdog_gov_noop); +} +module_init(watchdog_gov_noop_register); +module_exit(watchdog_gov_noop_unregister); + +MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>"); +MODULE_DESCRIPTION("Panic watchdog pretimeout governor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/pretimeout_panic.c b/drivers/watchdog/pretimeout_panic.c new file mode 100644 index 000000000000..0c197a1c97f4 --- /dev/null +++ b/drivers/watchdog/pretimeout_panic.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015-2016 Mentor Graphics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/watchdog.h> + +#include "watchdog_pretimeout.h" + +/** + * pretimeout_panic - Panic on watchdog pretimeout event + * @wdd - watchdog_device + * + * Panic, watchdog has not been fed till pretimeout event. + */ +static void pretimeout_panic(struct watchdog_device *wdd) +{ + panic("watchdog pretimeout event\n"); +} + +static struct watchdog_governor watchdog_gov_panic = { + .name = "panic", + .pretimeout = pretimeout_panic, +}; + +static int __init watchdog_gov_panic_register(void) +{ + return watchdog_register_governor(&watchdog_gov_panic); +} + +static void __exit watchdog_gov_panic_unregister(void) +{ + watchdog_unregister_governor(&watchdog_gov_panic); +} +module_init(watchdog_gov_panic_register); +module_exit(watchdog_gov_panic_unregister); + +MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>"); +MODULE_DESCRIPTION("Panic watchdog pretimeout governor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/rn5t618_wdt.c b/drivers/watchdog/rn5t618_wdt.c index d1c12278cb6a..0805ee2acd7a 100644 --- a/drivers/watchdog/rn5t618_wdt.c +++ b/drivers/watchdog/rn5t618_wdt.c @@ -136,7 +136,7 @@ static struct watchdog_info rn5t618_wdt_info = { .identity = DRIVER_NAME, }; -static struct watchdog_ops rn5t618_wdt_ops = { +static const struct watchdog_ops rn5t618_wdt_ops = { .owner = THIS_MODULE, .start = rn5t618_wdt_start, .stop = rn5t618_wdt_stop, diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c index 1967919ae743..14b4fd428fff 100644 --- a/drivers/watchdog/rt2880_wdt.c +++ b/drivers/watchdog/rt2880_wdt.c @@ -158,7 +158,6 @@ static int rt288x_wdt_probe(struct platform_device *pdev) rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE; - rt288x_wdt_dev.dev = &pdev->dev; rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); rt288x_wdt_dev.parent = &pdev->dev; diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index b067edf246df..c7bdc986dca1 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -72,10 +72,27 @@ static void softdog_fire(unsigned long data) static struct timer_list softdog_ticktock = TIMER_INITIALIZER(softdog_fire, 0, 0); +static struct watchdog_device softdog_dev; + +static void softdog_pretimeout(unsigned long data) +{ + watchdog_notify_pretimeout(&softdog_dev); +} + +static struct timer_list softdog_preticktock = + TIMER_INITIALIZER(softdog_pretimeout, 0, 0); + static int softdog_ping(struct watchdog_device *w) { if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ))) __module_get(THIS_MODULE); + + if (w->pretimeout) + mod_timer(&softdog_preticktock, jiffies + + (w->timeout - w->pretimeout) * HZ); + else + del_timer(&softdog_preticktock); + return 0; } @@ -84,15 +101,18 @@ static int softdog_stop(struct watchdog_device *w) if (del_timer(&softdog_ticktock)) module_put(THIS_MODULE); + del_timer(&softdog_preticktock); + return 0; } static struct watchdog_info softdog_info = { .identity = "Software Watchdog", - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | + WDIOF_PRETIMEOUT, }; -static struct watchdog_ops softdog_ops = { +static const struct watchdog_ops softdog_ops = { .owner = THIS_MODULE, .start = softdog_ping, .stop = softdog_stop, diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c index 14e9badf2bfa..e6100e447dd8 100644 --- a/drivers/watchdog/st_lpc_wdt.c +++ b/drivers/watchdog/st_lpc_wdt.c @@ -52,27 +52,6 @@ struct st_wdog { bool warm_reset; }; -static struct st_wdog_syscfg stid127_syscfg = { - .reset_type_reg = 0x004, - .reset_type_mask = BIT(2), - .enable_reg = 0x000, - .enable_mask = BIT(2), -}; - -static struct st_wdog_syscfg stih415_syscfg = { - .reset_type_reg = 0x0B8, - .reset_type_mask = BIT(6), - .enable_reg = 0x0B4, - .enable_mask = BIT(7), -}; - -static struct st_wdog_syscfg stih416_syscfg = { - .reset_type_reg = 0x88C, - .reset_type_mask = BIT(6), - .enable_reg = 0x888, - .enable_mask = BIT(7), -}; - static struct st_wdog_syscfg stih407_syscfg = { .enable_reg = 0x204, .enable_mask = BIT(19), @@ -83,18 +62,6 @@ static const struct of_device_id st_wdog_match[] = { .compatible = "st,stih407-lpc", .data = &stih407_syscfg, }, - { - .compatible = "st,stih416-lpc", - .data = &stih416_syscfg, - }, - { - .compatible = "st,stih415-lpc" |