diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-19 19:35:51 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-19 19:35:51 -0700 |
commit | 1e75a9f34a5ed5902707fb74b468356c55142b71 (patch) | |
tree | 810ff2a5e9d393242e663b321c8a80a17cebe11d | |
parent | 1c3d770043583d99118d52cad309f586ef8e7d4a (diff) | |
parent | d1ed3ba4e3d76b4ebec239c64f990c26d7935700 (diff) |
Merge git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
- new drivers for: NI 903x/913x watchdog driver, WinSystems EBC-C384
watchdog timer and ARM SBSA watchdog driver
- Support for NCT6102D devices
- Improvements of the generic watchdog framework (improve restart
handler, make set_timeout optional, introduce infrastructure
triggered keepalives, ...
- improvements on the pnx4008 watchdog driver
- several smaller fixes and improvements
* git://www.linux-watchdog.org/linux-watchdog: (28 commits)
watchdog: Ensure that wdd is not dereferenced if NULL
watchdog: imx2: Convert to use infrastructure triggered keepalives
watchdog: dw_wdt: Convert to use watchdog infrastructure
watchdog: Add support for minimum time between heartbeats
watchdog: Make stop function optional
watchdog: Introduce WDOG_HW_RUNNING flag
watchdog: Introduce hardware maximum heartbeat in watchdog core
watchdog: Make set_timeout function optional
arm: lpc32xx: remove restart handler
arm: lpc32xx: phy3250 remove restart hook
watchdog: pnx4008: restart: support "cmd" from userspace
watchdog: pnx4008: add support for soft reset
watchdog: pnx4008: add restart handler
watchdog: pnx4008: update logging during power-on
watchdog: tangox_wdt: test clock rate to avoid division by 0
watchdog: atlas7_wdt: test clock rate to avoid division by 0
watchdog: s3c2410_wdt: Add max and min timeout values
Watchdog: introduce ARM SBSA watchdog driver
Documentation: add sbsa-gwdt driver documentation
watchdog: Add watchdog timer support for the WinSystems EBC-C384
...
34 files changed, 1482 insertions, 354 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt b/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt new file mode 100644 index 000000000000..6f2d5f91964d --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt @@ -0,0 +1,31 @@ +* SBSA (Server Base System Architecture) Generic Watchdog + +The SBSA Generic Watchdog Timer is used to force a reset of the system +after two stages of timeout have elapsed. A detailed definition of the +watchdog timer can be found in the ARM document: ARM-DEN-0029 - Server +Base System Architecture (SBSA) + +Required properties: +- compatible: Should at least contain "arm,sbsa-gwdt". + +- reg: Each entry specifies the base physical address of a register frame + and the length of that frame; currently, two frames must be defined, + in this order: + 1: Watchdog control frame; + 2: Refresh frame. + +- interrupts: Should contain the Watchdog Signal 0 (WS0) SPI (Shared + Peripheral Interrupt) number of SBSA Generic Watchdog. + +Optional properties +- timeout-sec: Watchdog timeout values (in seconds). + +Example for FVP Foundation Model v8: + +watchdog@2a440000 { + compatible = "arm,sbsa-gwdt"; + reg = <0x0 0x2a440000 0 0x1000>, + <0x0 0x2a450000 0 0x1000>; + interrupts = <0 27 4>; + timeout-sec = <30>; +}; diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 55120a055a14..917eeeabfa5e 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -52,6 +52,8 @@ struct watchdog_device { unsigned int timeout; unsigned int min_timeout; unsigned int max_timeout; + unsigned int min_hw_heartbeat_ms; + unsigned int max_hw_heartbeat_ms; struct notifier_block reboot_nb; struct notifier_block restart_nb; void *driver_data; @@ -73,8 +75,21 @@ It contains following fields: 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. * 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. * min_timeout: the watchdog timer's minimum timeout value (in seconds). -* max_timeout: the watchdog timer's maximum timeout value (in seconds). + If set, the minimum configurable value for 'timeout'. +* max_timeout: the watchdog timer's maximum timeout value (in seconds), + as seen from userspace. If set, the maximum configurable value for + 'timeout'. Not used if max_hw_heartbeat_ms is non-zero. +* min_hw_heartbeat_ms: Minimum time between heartbeats sent to the chip, + in milli-seconds. +* max_hw_heartbeat_ms: Maximum hardware heartbeat, in milli-seconds. + If set, the infrastructure will send heartbeats to the watchdog driver + if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE + is set and userspace failed to send a heartbeat for at least 'timeout' + seconds. max_hw_heartbeat_ms must be set if a driver does not implement + the stop function. * reboot_nb: notifier block that is registered for reboot notifications, for internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core will stop the watchdog on such notifications. @@ -123,17 +138,20 @@ are: device. The routine needs a pointer to the watchdog timer device structure as a parameter. It returns zero on success or a negative errno code for failure. -* stop: with this routine the watchdog timer device is being stopped. - The routine needs a pointer to the watchdog timer device structure as a - parameter. It returns zero on success or a negative errno code for failure. - Some watchdog timer hardware can only be started and not be stopped. The - driver supporting this hardware needs to make sure that a start and stop - routine is being provided. This can be done by using a timer in the driver - that regularly sends a keepalive ping to the watchdog timer hardware. Not all watchdog timer hardware supports the same functionality. That's why all other routines/operations are optional. They only need to be provided if they are supported. These optional routines/operations are: +* stop: with this routine the watchdog timer device is being stopped. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. + Some watchdog timer hardware can only be started and not be stopped. A + driver supporting such hardware does not have to implement the stop routine. + If a driver has no stop function, the watchdog core will set WDOG_HW_RUNNING + and start calling the driver's keepalive pings function after the watchdog + device is closed. + If a watchdog driver does not implement the stop function, it must set + max_hw_heartbeat_ms. * ping: this is the routine that sends a keepalive ping to the watchdog timer hardware. The routine needs a pointer to the watchdog timer device structure as a @@ -153,9 +171,18 @@ they are supported. These optional routines/operations are: and -EIO for "could not write value to the watchdog". On success this routine should set the timeout value of the watchdog_device to the achieved timeout value (which may be different from the requested one - because the watchdog does not necessarily has a 1 second resolution). + because the watchdog does not necessarily have a 1 second resolution). + Drivers implementing max_hw_heartbeat_ms set the hardware watchdog heartbeat + to the minimum of timeout and max_hw_heartbeat_ms. Those drivers set the + timeout value of the watchdog_device either to the requested timeout value + (if it is larger than max_hw_heartbeat_ms), or to the achieved timeout value. (Note: the WDIOF_SETTIMEOUT 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.timeout, this callback can be omitted. + 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. * 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. @@ -169,11 +196,19 @@ The 'ref' and 'unref' operations are no longer used and deprecated. The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are: * WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device - is active or not. When the watchdog is active after booting, then you should - set this status bit (Note: when you register the watchdog timer device with - this bit set, then opening /dev/watchdog will skip the start operation) + is active or not from user perspective. User space is expected to send + heartbeat requests to the driver while this flag is set. * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. If this bit is set then the watchdog timer will not be able to stop. +* WDOG_HW_RUNNING: Set by the watchdog driver if the hardware watchdog is + running. The bit must be set if the watchdog timer hardware can not be + stopped. The bit may also be set if the watchdog timer is running after + booting, before the watchdog device is opened. If set, the watchdog + infrastructure will send keepalives to the watchdog hardware while + WDOG_ACTIVE is not set. + Note: when you register the watchdog timer device with this bit set, + then opening /dev/watchdog will skip the start operation but send a keepalive + request instead. To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog timer device) you can either: diff --git a/Documentation/watchdog/watchdog-parameters.txt b/Documentation/watchdog/watchdog-parameters.txt index 4e4b6f10d841..c161399a6b5c 100644 --- a/Documentation/watchdog/watchdog-parameters.txt +++ b/Documentation/watchdog/watchdog-parameters.txt @@ -200,6 +200,11 @@ mv64x60_wdt: nowayout: Watchdog cannot be stopped once started (default=kernel config parameter) ------------------------------------------------- +ni903x_wdt: +timeout: Initial watchdog timeout in seconds (0<timeout<516, default=60) +nowayout: Watchdog cannot be stopped once started + (default=kernel config parameter) +------------------------------------------------- nuc900_wdt: heartbeat: Watchdog heartbeats in seconds. (default = 15) @@ -284,6 +289,13 @@ sbc_fitpc2_wdt: margin: Watchdog margin in seconds (default 60s) nowayout: Watchdog cannot be stopped once started ------------------------------------------------- +sbsa_gwdt: +timeout: Watchdog timeout in seconds. (default 10s) +action: Watchdog action at the first stage timeout, + set to 0 to ignore, 1 to panic. (default=0) +nowayout: Watchdog cannot be stopped once started + (default=kernel config parameter) +------------------------------------------------- sc1200wdt: isapnp: When set to 0 driver ISA PnP support will be disabled (default=1) io: io port diff --git a/MAINTAINERS b/MAINTAINERS index 0cbfc69a2303..606528bb16af 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11965,6 +11965,12 @@ M: David Härdeman <david@hardeman.nu> S: Maintained F: drivers/media/rc/winbond-cir.c +WINSYSTEMS EBC-C384 WATCHDOG DRIVER +M: William Breathitt Gray <vilhelm.gray@gmail.com> +L: linux-watchdog@vger.kernel.org +S: Maintained +F: drivers/watchdog/ebc-c384_wdt.c + WINSYSTEMS WS16C48 GPIO DRIVER M: William Breathitt Gray <vilhelm.gray@gmail.com> L: linux-gpio@vger.kernel.org diff --git a/arch/arm/mach-lpc32xx/common.c b/arch/arm/mach-lpc32xx/common.c index 716e83eb1db8..5b7a1e78c3a5 100644 --- a/arch/arm/mach-lpc32xx/common.c +++ b/arch/arm/mach-lpc32xx/common.c @@ -194,21 +194,6 @@ void __init lpc32xx_map_io(void) iotable_init(lpc32xx_io_desc, ARRAY_SIZE(lpc32xx_io_desc)); } -void lpc23xx_restart(enum reboot_mode mode, const char *cmd) -{ - /* Make sure WDT clocks are enabled */ - __raw_writel(LPC32XX_CLKPWR_PWMCLK_WDOG_EN, - LPC32XX_CLKPWR_TIMER_CLK_CTRL); - - /* Instant assert of RESETOUT_N with pulse length 1mS */ - __raw_writel(13000, io_p2v(LPC32XX_WDTIM_BASE + 0x18)); - __raw_writel(0x70, io_p2v(LPC32XX_WDTIM_BASE + 0xC)); - - /* Wait for watchdog to reset system */ - while (1) - ; -} - static int __init lpc32xx_check_uid(void) { u32 uid[4]; diff --git a/arch/arm/mach-lpc32xx/common.h b/arch/arm/mach-lpc32xx/common.h index 1cd8853b2f9b..2d90801ed1e1 100644 --- a/arch/arm/mach-lpc32xx/common.h +++ b/arch/arm/mach-lpc32xx/common.h @@ -30,7 +30,6 @@ extern void lpc32xx_timer_init(void); extern void __init lpc32xx_init_irq(void); extern void __init lpc32xx_map_io(void); extern void __init lpc32xx_serial_init(void); -extern void lpc23xx_restart(enum reboot_mode, const char *); /* diff --git a/arch/arm/mach-lpc32xx/phy3250.c b/arch/arm/mach-lpc32xx/phy3250.c index ee06fabdf60e..e6f03783ad73 100644 --- a/arch/arm/mach-lpc32xx/phy3250.c +++ b/arch/arm/mach-lpc32xx/phy3250.c @@ -262,5 +262,4 @@ DT_MACHINE_START(LPC32XX_DT, "LPC32XX SoC (Flattened Device Tree)") .init_time = lpc32xx_timer_init, .init_machine = lpc3250_machine_init, .dt_compat = lpc32xx_dt_compat, - .restart = lpc23xx_restart, MACHINE_END diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 9289da313d98..fb947655badd 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -202,6 +202,26 @@ config ARM_SP805_WATCHDOG ARM Primecell SP805 Watchdog timer. This will reboot your system when the timeout is reached. +config ARM_SBSA_WATCHDOG + tristate "ARM SBSA Generic Watchdog" + depends on ARM64 + depends on ARM_ARCH_TIMER + select WATCHDOG_CORE + help + ARM SBSA Generic Watchdog has two stage timeouts: + the first signal (WS0) is for alerting the system by interrupt, + the second one (WS1) is a real hardware reset. + More details: ARM DEN0029B - Server Base System Architecture (SBSA) + + This driver can operate ARM SBSA Generic Watchdog as a single stage + or a two stages watchdog, it depends on the module parameter "action". + + Note: the maximum timeout in the two stages mode is half of that in + the single stage mode. + + To compile this driver as module, choose M here: The module + will be called sbsa_gwdt. + config ASM9260_WATCHDOG tristate "Alphascale ASM9260 watchdog" depends on MACH_ASM9260 @@ -330,6 +350,7 @@ config SA1100_WATCHDOG config DW_WATCHDOG tristate "Synopsys DesignWare watchdog" depends on HAS_IOMEM + select WATCHDOG_CORE help Say Y here if to include support for the Synopsys DesignWare watchdog timer found in many chips. @@ -399,6 +420,7 @@ config DAVINCI_WATCHDOG config ORION_WATCHDOG tristate "Orion watchdog" depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU + depends on ARM select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer @@ -468,6 +490,7 @@ config NUC900_WATCHDOG config TS4800_WATCHDOG tristate "TS-4800 Watchdog" depends on HAS_IOMEM && OF + depends on SOC_IMX51 || COMPILE_TEST select WATCHDOG_CORE select MFD_SYSCON help @@ -713,6 +736,15 @@ config ALIM7101_WDT Most people will say N. +config EBC_C384_WDT + tristate "WinSystems EBC-C384 Watchdog Timer" + depends on X86 + select WATCHDOG_CORE + help + Enables watchdog timer support for the watchdog timer on the + WinSystems EBC-C384 motherboard. The timeout may be configured via + the timeout module parameter. + config F71808E_WDT tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog" depends on X86 @@ -1142,6 +1174,7 @@ config W83627HF_WDT NCT6779 NCT6791 NCT6792 + NCT6102D/04D/06D This watchdog simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain @@ -1229,6 +1262,17 @@ config INTEL_MEI_WDT To compile this driver as a module, choose M here: the module will be called mei_wdt. +config NI903X_WDT + tristate "NI 903x/913x Watchdog" + depends on X86 && ACPI + select WATCHDOG_CORE + ---help--- + This is the driver for the watchdog timer on the National Instruments + 903x/913x real-time controllers. + + To compile this driver as a module, choose M here: the module will be + called ni903x_wdt. + # M32R Architecture # M68K Architecture @@ -1392,10 +1436,12 @@ config BCM7038_WDT tristate "BCM7038 Watchdog" select WATCHDOG_CORE depends on HAS_IOMEM + depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST help - Watchdog driver for the built-in hardware in Broadcom 7038 SoCs. - - Say 'Y or 'M' here to enable the driver. + Watchdog driver for the built-in hardware in Broadcom 7038 and + later SoCs used in set-top boxes. BCM7038 was made public + during the 2004 CES, and since then, many Broadcom chips use this + watchdog block, including some cable modem chips. config IMGPDC_WDT tristate "Imagination Technologies PDC Watchdog Timer" diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 14bd772d3e66..feb6270fdbde 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -30,6 +30,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_ASM9260_WATCHDOG) += asm9260_wdt.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o @@ -88,6 +89,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o +obj-$(CONFIG_EBC_C384_WDT) += ebc-c384_wdt.o obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o obj-$(CONFIG_GEODE_WDT) += geodewdt.o @@ -127,6 +129,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o +obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o # M32R Architecture diff --git a/drivers/watchdog/atlas7_wdt.c b/drivers/watchdog/atlas7_wdt.c index df6d9242a319..ed80734befae 100644 --- a/drivers/watchdog/atlas7_wdt.c +++ b/drivers/watchdog/atlas7_wdt.c @@ -154,6 +154,11 @@ static int atlas7_wdt_probe(struct platform_device *pdev) writel(0, wdt->base + ATLAS7_WDT_CNT_CTRL); wdt->tick_rate = clk_get_rate(clk); + if (!wdt->tick_rate) { + ret = -EINVAL; + goto err1; + } + wdt->clk = clk; atlas7_wdd.min_timeout = 1; atlas7_wdd.max_timeout = UINT_MAX / wdt->tick_rate; diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index df1c2a4b0165..a1900b9ab6c4 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -87,7 +87,8 @@ static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd, return 0; } -static int bcm47xx_wdt_restart(struct watchdog_device *wdd) +static int bcm47xx_wdt_restart(struct watchdog_device *wdd, + unsigned long action, void *data) { struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index 11e887572649..a100f648880d 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c @@ -119,7 +119,8 @@ static int da9063_wdt_set_timeout(struct watchdog_device *wdd, return ret; } -static int da9063_wdt_restart(struct watchdog_device *wdd) +static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action, + void *data) { struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); int ret; diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c index 1ccb0b239348..77df772406b0 100644 --- a/drivers/watchdog/digicolor_wdt.c +++ b/drivers/watchdog/digicolor_wdt.c @@ -48,7 +48,8 @@ static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks) spin_unlock_irqrestore(&wdt->lock, flags); } -static int dc_wdt_restart(struct watchdog_device *wdog) +static int dc_wdt_restart(struct watchdog_device *wdog, unsigned long action, + void *data) { struct dc_wdt *wdt = watchdog_get_drvdata(wdog); diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 8fefa4ad46d4..2acb51cf5504 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -12,9 +12,8 @@ * and these are a function of the input clock frequency. * * The DesignWare watchdog cannot be stopped once it has been started so we - * use a software timer to implement a ping that will keep the watchdog alive. - * If we receive an expected close for the watchdog then we keep the timer - * running, otherwise the timer is stopped and the watchdog will expire. + * do not implement a stop function. The watchdog core will continue to send + * heartbeat requests after the watchdog device has been closed. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -22,12 +21,9 @@ #include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> -#include <linux/device.h> #include <linux/err.h> -#include <linux/fs.h> #include <linux/io.h> #include <linux/kernel.h> -#include <linux/miscdevice.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/notifier.h> @@ -35,8 +31,6 @@ #include <linux/pm.h> #include <linux/platform_device.h> #include <linux/reboot.h> -#include <linux/timer.h> -#include <linux/uaccess.h> #include <linux/watchdog.h> #define WDOG_CONTROL_REG_OFFSET 0x00 @@ -57,53 +51,50 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -#define WDT_TIMEOUT (HZ / 2) - -static struct { +struct dw_wdt { void __iomem *regs; struct clk *clk; - unsigned long in_use; - unsigned long next_heartbeat; - struct timer_list timer; - int expect_close; struct notifier_block restart_handler; -} dw_wdt; + struct watchdog_device wdd; +}; + +#define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd) -static inline int dw_wdt_is_enabled(void) +static inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt) { - return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) & + return readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET) & WDOG_CONTROL_REG_WDT_EN_MASK; } -static inline int dw_wdt_top_in_seconds(unsigned top) +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)) / clk_get_rate(dw_wdt->clk); } -static int dw_wdt_get_top(void) +static int dw_wdt_get_top(struct dw_wdt *dw_wdt) { - int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; + int top = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; - return dw_wdt_top_in_seconds(top); + return dw_wdt_top_in_seconds(dw_wdt, top); } -static inline void dw_wdt_set_next_heartbeat(void) +static int dw_wdt_ping(struct watchdog_device *wdd) { - dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ; -} + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); -static void dw_wdt_keepalive(void) -{ - writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + + writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET); + + return 0; } -static int dw_wdt_set_top(unsigned top_s) +static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) { + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); int i, top_val = DW_WDT_MAX_TOP; /* @@ -111,7 +102,7 @@ static int dw_wdt_set_top(unsigned top_s) * always look for >=. */ for (i = 0; i <= DW_WDT_MAX_TOP; ++i) - if (dw_wdt_top_in_seconds(i) >= top_s) { + if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) { top_val = i; break; } @@ -123,33 +114,43 @@ static int dw_wdt_set_top(unsigned top_s) * effectively get a pat of the watchdog right here. */ writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, - dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); - /* - * Add an explicit pat to handle versions of the watchdog that - * don't have TOPINIT. This won't hurt on versions that have - * it. - */ - dw_wdt_keepalive(); + wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val); - dw_wdt_set_next_heartbeat(); + return 0; +} + +static int dw_wdt_start(struct watchdog_device *wdd) +{ + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); + + dw_wdt_set_timeout(wdd, wdd->timeout); - return dw_wdt_top_in_seconds(top_val); + set_bit(WDOG_HW_RUNNING, &wdd->status); + + writel(WDOG_CONTROL_REG_WDT_EN_MASK, + dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + + return 0; } static int dw_wdt_restart_handle(struct notifier_block *this, - unsigned long mode, void *cmd) + unsigned long mode, void *cmd) { + struct dw_wdt *dw_wdt; u32 val; - writel(0, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); - val = readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); + dw_wdt = container_of(this, struct dw_wdt, restart_handler); + + writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); if (val & WDOG_CONTROL_REG_WDT_EN_MASK) - writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + - WDOG_COUNTER_RESTART_REG_OFFSET); + writel(WDOG_COUNTER_RESTART_KICK_VALUE, + dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET); else writel(WDOG_CONTROL_REG_WDT_EN_MASK, - dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); + dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); /* wait for reset to assert... */ mdelay(500); @@ -157,74 +158,12 @@ static int dw_wdt_restart_handle(struct notifier_block *this, return NOTIFY_DONE; } -static void dw_wdt_ping(unsigned long data) +static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) { - if (time_before(jiffies, dw_wdt.next_heartbeat) || - (!nowayout && !dw_wdt.in_use)) { - dw_wdt_keepalive(); - mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); - } else - pr_crit("keepalive missed, machine will reset\n"); -} - -static int dw_wdt_open(struct inode *inode, struct file *filp) -{ - if (test_and_set_bit(0, &dw_wdt.in_use)) - return -EBUSY; - - /* Make sure we don't get unloaded. */ - __module_get(THIS_MODULE); - - if (!dw_wdt_is_enabled()) { - /* - * The watchdog is not currently enabled. Set the timeout to - * something reasonable and then start it. - */ - dw_wdt_set_top(DW_WDT_DEFAULT_SECONDS); - writel(WDOG_CONTROL_REG_WDT_EN_MASK, - dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); - } - - dw_wdt_set_next_heartbeat(); - - return nonseekable_open(inode, filp); -} - -static ssize_t dw_wdt_write(struct file *filp, const char __user *buf, - size_t len, loff_t *offset) -{ - if (!len) - return 0; - - if (!nowayout) { - size_t i; - - dw_wdt.expect_close = 0; - - for (i = 0; i < len; ++i) { - char c; - - if (get_user(c, buf + i)) - return -EFAULT; - - if (c == 'V') { - dw_wdt.expect_close = 1; - break; - } - } - } + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); - dw_wdt_set_next_heartbeat(); - dw_wdt_keepalive(); - mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); - - return len; -} - -static u32 dw_wdt_time_left(void) -{ - return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) / - clk_get_rate(dw_wdt.clk); + return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) / + clk_get_rate(dw_wdt->clk); } static const struct watchdog_info dw_wdt_ident = { @@ -233,78 +172,33 @@ static const struct watchdog_info dw_wdt_ident = { .identity = "Synopsys DesignWare Watchdog", }; -static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - unsigned long val; - int timeout; - - swit |