Commit 1e75a9f3 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 1c3d7700 d1ed3ba4
* 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>;
};
...@@ -52,6 +52,8 @@ struct watchdog_device { ...@@ -52,6 +52,8 @@ struct watchdog_device {
unsigned int timeout; unsigned int timeout;
unsigned int min_timeout; unsigned int min_timeout;
unsigned int max_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 reboot_nb;
struct notifier_block restart_nb; struct notifier_block restart_nb;
void *driver_data; void *driver_data;
...@@ -73,8 +75,21 @@ It contains following fields: ...@@ -73,8 +75,21 @@ It contains following fields:
additional information about the watchdog timer itself. (Like it's unique name) 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. * ops: a pointer to the list of watchdog operations that the watchdog supports.
* timeout: the watchdog timer's timeout value (in seconds). * 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). * 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 * reboot_nb: notifier block that is registered for reboot notifications, for
internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core
will stop the watchdog on such notifications. will stop the watchdog on such notifications.
...@@ -123,17 +138,20 @@ are: ...@@ -123,17 +138,20 @@ are:
device. device.
The routine needs a pointer to the watchdog timer device structure as a 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. 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 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 all other routines/operations are optional. They only need to be provided if
they are supported. These optional routines/operations are: 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 * ping: this is the routine that sends a keepalive ping to the watchdog timer
hardware. hardware.
The routine needs a pointer to the watchdog timer device structure as a 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: ...@@ -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 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 routine should set the timeout value of the watchdog_device to the
achieved timeout value (which may be different from the requested one 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 (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the
watchdog's info structure). 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. * 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 * restart: this routine restarts the machine. It returns 0 on success or a
negative errno code for failure. negative errno code for failure.
...@@ -169,11 +196,19 @@ The 'ref' and 'unref' operations are no longer used and deprecated. ...@@ -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 The status bits should (preferably) be set with the set_bit and clear_bit alike
bit-operations. The status bits that are defined are: bit-operations. The status bits that are defined are:
* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device * 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 is active or not from user perspective. User space is expected to send
set this status bit (Note: when you register the watchdog timer device with heartbeat requests to the driver while this flag is set.
this bit set, then opening /dev/watchdog will skip the start operation)
* WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. * 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. 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 To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog
timer device) you can either: timer device) you can either:
......
...@@ -200,6 +200,11 @@ mv64x60_wdt: ...@@ -200,6 +200,11 @@ mv64x60_wdt:
nowayout: Watchdog cannot be stopped once started nowayout: Watchdog cannot be stopped once started
(default=kernel config parameter) (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: nuc900_wdt:
heartbeat: Watchdog heartbeats in seconds. heartbeat: Watchdog heartbeats in seconds.
(default = 15) (default = 15)
...@@ -284,6 +289,13 @@ sbc_fitpc2_wdt: ...@@ -284,6 +289,13 @@ sbc_fitpc2_wdt:
margin: Watchdog margin in seconds (default 60s) margin: Watchdog margin in seconds (default 60s)
nowayout: Watchdog cannot be stopped once started 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: sc1200wdt:
isapnp: When set to 0 driver ISA PnP support will be disabled (default=1) isapnp: When set to 0 driver ISA PnP support will be disabled (default=1)
io: io port io: io port
......
...@@ -11965,6 +11965,12 @@ M: David Härdeman <david@hardeman.nu> ...@@ -11965,6 +11965,12 @@ M: David Härdeman <david@hardeman.nu>
S: Maintained S: Maintained
F: drivers/media/rc/winbond-cir.c 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 WINSYSTEMS WS16C48 GPIO DRIVER
M: William Breathitt Gray <vilhelm.gray@gmail.com> M: William Breathitt Gray <vilhelm.gray@gmail.com>
L: linux-gpio@vger.kernel.org L: linux-gpio@vger.kernel.org
......
...@@ -194,21 +194,6 @@ void __init lpc32xx_map_io(void) ...@@ -194,21 +194,6 @@ void __init lpc32xx_map_io(void)
iotable_init(lpc32xx_io_desc, ARRAY_SIZE(lpc32xx_io_desc)); 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) static int __init lpc32xx_check_uid(void)
{ {
u32 uid[4]; u32 uid[4];
......
...@@ -30,7 +30,6 @@ extern void lpc32xx_timer_init(void); ...@@ -30,7 +30,6 @@ extern void lpc32xx_timer_init(void);
extern void __init lpc32xx_init_irq(void); extern void __init lpc32xx_init_irq(void);
extern void __init lpc32xx_map_io(void); extern void __init lpc32xx_map_io(void);
extern void __init lpc32xx_serial_init(void); extern void __init lpc32xx_serial_init(void);
extern void lpc23xx_restart(enum reboot_mode, const char *);
/* /*
......
...@@ -262,5 +262,4 @@ DT_MACHINE_START(LPC32XX_DT, "LPC32XX SoC (Flattened Device Tree)") ...@@ -262,5 +262,4 @@ DT_MACHINE_START(LPC32XX_DT, "LPC32XX SoC (Flattened Device Tree)")
.init_time = lpc32xx_timer_init, .init_time = lpc32xx_timer_init,
.init_machine = lpc3250_machine_init, .init_machine = lpc3250_machine_init,
.dt_compat = lpc32xx_dt_compat, .dt_compat = lpc32xx_dt_compat,
.restart = lpc23xx_restart,
MACHINE_END MACHINE_END
...@@ -202,6 +202,26 @@ config ARM_SP805_WATCHDOG ...@@ -202,6 +202,26 @@ config ARM_SP805_WATCHDOG
ARM Primecell SP805 Watchdog timer. This will reboot your system when ARM Primecell SP805 Watchdog timer. This will reboot your system when
the timeout is reached. 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 config ASM9260_WATCHDOG
tristate "Alphascale ASM9260 watchdog" tristate "Alphascale ASM9260 watchdog"
depends on MACH_ASM9260 depends on MACH_ASM9260
...@@ -330,6 +350,7 @@ config SA1100_WATCHDOG ...@@ -330,6 +350,7 @@ config SA1100_WATCHDOG
config DW_WATCHDOG config DW_WATCHDOG
tristate "Synopsys DesignWare watchdog" tristate "Synopsys DesignWare watchdog"
depends on HAS_IOMEM depends on HAS_IOMEM
select WATCHDOG_CORE
help help
Say Y here if to include support for the Synopsys DesignWare Say Y here if to include support for the Synopsys DesignWare
watchdog timer found in many chips. watchdog timer found in many chips.
...@@ -399,6 +420,7 @@ config DAVINCI_WATCHDOG ...@@ -399,6 +420,7 @@ config DAVINCI_WATCHDOG
config ORION_WATCHDOG config ORION_WATCHDOG
tristate "Orion watchdog" tristate "Orion watchdog"
depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU
depends on ARM
select WATCHDOG_CORE select WATCHDOG_CORE
help help
Say Y here if to include support for the watchdog timer Say Y here if to include support for the watchdog timer
...@@ -468,6 +490,7 @@ config NUC900_WATCHDOG ...@@ -468,6 +490,7 @@ config NUC900_WATCHDOG
config TS4800_WATCHDOG config TS4800_WATCHDOG
tristate "TS-4800 Watchdog" tristate "TS-4800 Watchdog"
depends on HAS_IOMEM && OF depends on HAS_IOMEM && OF
depends on SOC_IMX51 || COMPILE_TEST
select WATCHDOG_CORE select WATCHDOG_CORE
select MFD_SYSCON select MFD_SYSCON
help help
...@@ -713,6 +736,15 @@ config ALIM7101_WDT ...@@ -713,6 +736,15 @@ config ALIM7101_WDT
Most people will say N. 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 config F71808E_WDT
tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog" tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog"
depends on X86 depends on X86
...@@ -1142,6 +1174,7 @@ config W83627HF_WDT ...@@ -1142,6 +1174,7 @@ config W83627HF_WDT
NCT6779 NCT6779
NCT6791 NCT6791
NCT6792 NCT6792
NCT6102D/04D/06D
This watchdog simply watches your kernel to make sure it doesn't This watchdog simply watches your kernel to make sure it doesn't
freeze, and if it does, it reboots your computer after a certain freeze, and if it does, it reboots your computer after a certain
...@@ -1229,6 +1262,17 @@ config INTEL_MEI_WDT ...@@ -1229,6 +1262,17 @@ config INTEL_MEI_WDT
To compile this driver as a module, choose M here: To compile this driver as a module, choose M here:
the module will be called mei_wdt. 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 # M32R Architecture
# M68K Architecture # M68K Architecture
...@@ -1392,10 +1436,12 @@ config BCM7038_WDT ...@@ -1392,10 +1436,12 @@ config BCM7038_WDT
tristate "BCM7038 Watchdog" tristate "BCM7038 Watchdog"
select WATCHDOG_CORE select WATCHDOG_CORE
depends on HAS_IOMEM depends on HAS_IOMEM
depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
help help
Watchdog driver for the built-in hardware in Broadcom 7038 SoCs. Watchdog driver for the built-in hardware in Broadcom 7038 and
later SoCs used in set-top boxes. BCM7038 was made public
Say 'Y or 'M' here to enable the driver. during the 2004 CES, and since then, many Broadcom chips use this
watchdog block, including some cable modem chips.
config IMGPDC_WDT config IMGPDC_WDT
tristate "Imagination Technologies PDC Watchdog Timer" tristate "Imagination Technologies PDC Watchdog Timer"
......
...@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o ...@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
# ARM Architecture # ARM Architecture
obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o 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_ASM9260_WATCHDOG) += asm9260_wdt.o
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
...@@ -88,6 +89,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o ...@@ -88,6 +89,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
obj-$(CONFIG_ALIM7101_WDT) += alim7101_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_F71808E_WDT) += f71808e_wdt.o
obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o
obj-$(CONFIG_GEODE_WDT) += geodewdt.o obj-$(CONFIG_GEODE_WDT) += geodewdt.o
...@@ -127,6 +129,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.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_SCU_WATCHDOG) += intel_scu_watchdog.o
obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o
obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o
# M32R Architecture # M32R Architecture
......
...@@ -154,6 +154,11 @@ static int atlas7_wdt_probe(struct platform_device *pdev) ...@@ -154,6 +154,11 @@ static int atlas7_wdt_probe(struct platform_device *pdev)
writel(0, wdt->base + ATLAS7_WDT_CNT_CTRL); writel(0, wdt->base + ATLAS7_WDT_CNT_CTRL);
wdt->tick_rate = clk_get_rate(clk); wdt->tick_rate = clk_get_rate(clk);
if (!wdt->tick_rate) {
ret = -EINVAL;
goto err1;
}
wdt->clk = clk; wdt->clk = clk;
atlas7_wdd.min_timeout = 1; atlas7_wdd.min_timeout = 1;
atlas7_wdd.max_timeout = UINT_MAX / wdt->tick_rate; atlas7_wdd.max_timeout = UINT_MAX / wdt->tick_rate;
......
...@@ -87,7 +87,8 @@ static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd, ...@@ -87,7 +87,8 @@ static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd,
return 0; 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); struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
......
...@@ -119,7 +119,8 @@ static int da9063_wdt_set_timeout(struct watchdog_device *wdd, ...@@ -119,7 +119,8 @@ static int da9063_wdt_set_timeout(struct watchdog_device *wdd,
return ret; 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); struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
int ret; int ret;
......
...@@ -48,7 +48,8 @@ static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks) ...@@ -48,7 +48,8 @@ static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks)
spin_unlock_irqrestore(&wdt->lock, flags); 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); struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
......
...@@ -12,9 +12,8 @@ ...@@ -12,9 +12,8 @@
* and these are a function of the input clock frequency. * and these are a function of the input clock frequency.
* *
* The DesignWare watchdog cannot be stopped once it has been started so we * 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. * do not implement a stop function. The watchdog core will continue to send
* If we receive an expected close for the watchdog then we keep the timer * heartbeat requests after the watchdog device has been closed.
* running, otherwise the timer is stopped and the watchdog will expire.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
...@@ -22,12 +21,9 @@ ...@@ -22,12 +21,9 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/fs.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/notifier.h> #include <linux/notifier.h>
...@@ -35,8 +31,6 @@ ...@@ -35,8 +31,6 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/timer.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#define WDOG_CONTROL_REG_OFFSET 0x00 #define WDOG_CONTROL_REG_OFFSET 0x00
...@@ -57,53 +51,50 @@ module_param(nowayout, bool, 0); ...@@ -57,53 +51,50 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
#define WDT_TIMEOUT (HZ / 2) struct dw_wdt {
static struct {
void __iomem *regs; void __iomem *regs;
struct clk *clk; struct clk *clk;
unsigned long in_use;
unsigned long next_heartbeat;
struct timer_list timer;
int expect_close;
struct notifier_block restart_handler; 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; 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 * There are 16 possible timeout values in 0..15 where the number of
* cycles is 2 ^ (16 + i) and the watchdog counts down. * 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); 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; int i, top_val = DW_WDT_MAX_TOP;
/* /*
...@@ -111,7 +102,7 @@ static int dw_wdt_set_top(unsigned top_s) ...@@ -111,7 +102,7 @@ static int dw_wdt_set_top(unsigned top_s)
* always look for >=. * always look for >=.
*/ */
for (i = 0; i <= DW_WDT_MAX_TOP; ++i) 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; top_val = i;
break; break;
} }
...@@ -123,33 +114,43 @@ static int dw_wdt_set_top(unsigned top_s) ...@@ -123,33 +114,43 @@ static int dw_wdt_set_top(unsigned top_s)
* effectively get a pat of the watchdog right here. * effectively get a pat of the watchdog right here.
*/ */
writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, 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);
/* wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val);
* 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();
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, 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; u32 val;
writel(0, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); dw_wdt = container_of(this, struct dw_wdt, restart_handler);
val = readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
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) if (val & WDOG_CONTROL_REG_WDT_EN_MASK)
writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + writel(WDOG_COUNTER_RESTART_KICK_VALUE,
WDOG_COUNTER_RESTART_REG_OFFSET); dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET);
else else
writel(WDOG_CONTROL_REG_WDT_EN_MASK, 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... */ /* wait for reset to assert... */
mdelay(500); mdelay(500);
...@@ -157,74 +158,12 @@ static int dw_wdt_restart_handle(struct notifier_block *this, ...@@ -157,74 +158,12 @@ static int dw_wdt_restart_handle(struct notifier_block *this,
return NOTIFY_DONE; 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) || struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
(!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;
}
}
}
dw_wdt_set_next_heartbeat(); return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
dw_wdt_keepalive(); clk_get_rate(dw_wdt->clk);
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);
} }
static const struct watchdog_info dw_wdt_ident = { static const struct watchdog_info dw_wdt_ident = {
...@@ -233,78 +172,33 @@ static const struct watchdog_info dw_wdt_ident = { ...@@ -233,78 +172,33 @@ static const struct watchdog_info dw_wdt_ident = {
.identity = "Synopsys DesignWare Watchdog", .identity = "Synopsys DesignWare Watchdog",
}; };
static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static const struct watchdog_ops dw_wdt_ops = {
{ .owner = THIS_MODULE,
unsigned long val; .start = dw_wdt_start,
int timeout; .ping = dw_wdt_ping,
.set_timeout = dw_wdt_set_timeout,
switch (cmd) { .get_timeleft = dw_wdt_get_timeleft,
case WDIOC_GETSUPPORT: };
return copy_to_user((void __user *)arg, &dw_wdt_ident,
sizeof(dw_wdt_ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, (int __user *)arg);
case WDIOC_KEEPALIVE:
dw_wdt_set_next_heartbeat();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(val, (int __user *)arg))
return -EFAULT;
timeout = dw_wdt_set_top(val);
return put_user(timeout , (int __user *)arg);
case WDIOC_GETTIMEOUT:
return put_user(dw_wdt_get_top(), (int __user *)arg);
case WDIOC_GETTIMELEFT:
/* Get the time left until expiry. */
if (get_user(val, (int __user *)arg))
return -EFAULT;
return put_user(dw_wdt_time_left(), (int __user *)arg);
default:
return -ENOTTY;
}
}
static int dw_wdt_release(struct inode *inode, struct file *filp)
{
clear_bit(0, &dw_wdt.in_use);
if (!dw_wdt.expect_close) {
del_timer(&dw_wdt.timer);
if (!nowayout)
pr_crit("unexpected close, system will reboot soon\n");
else
pr_crit("watchdog cannot be disabled, system will reboot soon\n");
}
dw_wdt.expect_close = 0;
return 0;
}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int dw_wdt_suspend(struct device *dev) static int dw_wdt_suspend(struct device *dev)
{ {
clk_disable_unprepare(dw_wdt.clk); struct dw_wdt *dw_wdt = dev_get_drvdata(dev);
clk_disable_unprepare(dw_wdt->clk);
return 0; return 0;
} }
static int dw_wdt_resume(struct device *dev) static int dw_wdt_resume(struct device *dev)
{ {
int err = clk_prepare_enable(dw_wdt.clk); struct dw_wdt *dw_wdt = dev_get_drvdata(dev);
int err = clk_prepare_enable(dw_wdt->clk);
if (err) if (err)
return err; return err;
dw_wdt_keepalive(); dw_wdt_ping(&dw_wdt->wdd);
return 0; return 0;
} }
...@@ -312,67 +206,82 @@ static int dw_wdt_resume(struct device *dev) ...@@ -312,67 +206,82 @@ static int dw_wdt_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume); static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume);
static const struct file_operations wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = dw_wdt_open,
.write = dw_wdt_write,
.unlocked_ioctl = dw_wdt_ioctl,
.release = dw_wdt_release
};
static struct miscdevice dw_wdt_miscdev = {
.fops = &wdt_fops,
.name = "watchdog",
.minor = WATCHDOG_MINOR,
};
static int dw_wdt_drv_probe(struct platform_device *pdev) static int dw_wdt_drv_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct watchdog_device *wdd;
struct dw_wdt *dw_wdt;
struct resource *mem;
int ret; int ret;
struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dw_wdt.regs = devm_ioremap_resource(&pdev->dev, mem); dw_wdt = devm_kzalloc(dev, sizeof(*dw_wdt), GFP_KERNEL);
if (IS_ERR(dw_wdt.regs)) if (!dw_wdt)
return PTR_ERR(dw_wdt.regs); return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dw_wdt->regs = devm_ioremap_resource(dev, mem);
if (IS_ERR(dw_wdt->regs))
return PTR_ERR(dw_wdt->regs);
dw_wdt.clk = devm_clk_get(&pdev->dev, NULL); dw_wdt->clk = devm_clk_get(dev, NULL);
if (IS_ERR(dw_wdt.clk)) if (IS_ERR(dw_wdt->clk))
return PTR_ERR(dw_wdt.clk); return PTR_ERR(dw_wdt->clk);
ret = clk_prepare_enable(dw_wdt.clk); ret = clk_prepare_enable(dw_wdt->clk);
if (ret) if (ret)
return ret; return ret;
ret = misc_register(&dw_wdt_miscdev); wdd = &dw_wdt->wdd;
wdd->info = &dw_wdt_ident;
wdd->ops = &dw_wdt_ops;
wdd->min_timeout = 1;
wdd->max_hw_heartbeat_ms =
dw_wdt_top_in_seconds(dw_wdt, DW_WDT_MAX_TOP) * 1000;
wdd->parent = dev;
watchdog_set_drvdata(wdd, dw_wdt);
watchdog_set_nowayout(wdd, nowayout);
watchdog_init_timeout(wdd, 0, dev);
/*
* If the watchdog is already running, use its already configured
* timeout. Otherwise use the default or the value provided through
* devicetree.
*/
if (dw_wdt_is_enabled(dw_wdt)) {
wdd->timeout = dw_wdt_get_top(dw_wdt);
set_bit(WDOG_HW_RUNNING, &wdd->status);
} else {
wdd->timeout = DW_WDT_DEFAULT_SECONDS;
watchdog_init_timeout(wdd, 0, dev);
}
platform_set_drvdata(pdev, dw_wdt);
ret = watchdog_register_device(wdd);
if (ret) if (ret)
goto out_disable_clk; goto out_disable_clk;
dw_wdt.restart_handler.notifier_call = dw_wdt_restart_handle; dw_wdt->restart_handler.notifier_call = dw_wdt_restart_handle;
dw_wdt.restart_handler.priority = 128; dw_wdt->restart_handler.priority = 128;
ret = register_restart_handler(&dw_wdt.restart_handler); ret = register_restart_handler(&dw_wdt->restart_handler);
if (ret) if (ret)
pr_warn("cannot register restart handler\n"); pr_warn("cannot register restart handler\n");
dw_wdt_set_next_heartbeat();
setup_timer(&dw_wdt.timer, dw_wdt_ping, 0);
mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
return 0; return 0;
out_disable_clk: out_disable_clk:
clk_disable_unprepare(dw_wdt.clk); clk_disable_unprepare(dw_wdt->clk);
return ret; return ret;
} }
static int dw_wdt_drv_remove(struct platform_device *pdev) static int dw_wdt_drv_remove(struct platform_device *pdev)
{ {
unregister_restart_handler(&dw_wdt.restart_handler); struct dw_wdt *dw_wdt = platform_get_drvdata(pdev);
misc_deregister(&dw_wdt_miscdev);
clk_disable_unprepare(dw_wdt.clk); unregister_restart_handler(&dw_wdt->restart_handler);
watchdog_unregister_device(&dw_wdt->wdd);
clk_disable_unprepare(dw_wdt->clk);
return 0; return 0;
} }
......
/*
* Watchdog timer driver for the WinSystems EBC-C384
* Copyright (C) 2016 William Breathitt Gray
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#define MODULE_NAME "ebc-c384_wdt"
#define WATCHDOG_TIMEOUT 60
/*
* The timeout value in minutes must fit in a single byte when sent to the
* watchdog timer; the maximum timeout possible is 15300 (255 * 60) seconds.
*/
#define WATCHDOG_MAX_TIMEOUT 15300
#define BASE_ADDR 0x564
#define ADDR_EXTENT 5
#define CFG_ADDR (BASE_ADDR + 1)
#define PET_ADDR (BASE_ADDR + 2)
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) ")");
static unsigned timeout;
module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
static int ebc_c384_wdt_start(struct watchdog_device *wdev)
{
unsigned t = wdev->timeout;
/* resolution is in minutes for timeouts greater than 255 seconds */
if (t > 255)
t = DIV_ROUND_UP(t, 60);
outb(t, PET_ADDR);
return 0;
}
static int ebc_c384_wdt_stop(struct watchdog_device *wdev)
{
outb(0x00, PET_ADDR);
return 0;
}
static int ebc_c384_wdt_set_timeout(struct watchdog_device *wdev, unsigned t)
{
/* resolution is in minutes for timeouts greater than 255 seconds */
if (t > 255) {
/* round second resolution up to minute granularity */
wdev->timeout = roundup(t, 60);
/* set watchdog timer for minutes */
outb(0x00, CFG_ADDR);
} else {
wdev->timeout = t;
/* set watchdog timer for seconds */
outb(0x80, CFG_ADDR);
}
return 0;
}
static const struct watchdog_ops ebc_c384_wdt_ops = {
.start = ebc_c384_wdt_start,
.stop = ebc_c384_wdt_stop,
.set_timeout = ebc_c384_wdt_set_timeout
};
static const struct watchdog_info ebc_c384_wdt_info = {
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT,
.identity = MODULE_NAME
};
static int __init ebc_c384_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct watchdog_device *wdd;
if (!devm_request_region(dev, BASE_ADDR, ADDR_EXTENT, dev_name(dev))) {
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
BASE_ADDR, BASE_ADDR + ADDR_EXTENT);
return -EBUSY;
}
wdd = devm_kzalloc(dev, sizeof(*wdd), GFP_KERNEL);
if (!wdd)
return -ENOMEM;
wdd->info = &ebc_c384_wdt_info;
wdd->ops = &ebc_c384_wdt_ops;
wdd->timeout = WATCHDOG_TIMEOUT;
wdd->min_timeout = 1;
wdd->max_timeout = WATCHDOG_MAX_TIMEOUT;
watchdog_set_nowayout(wdd, nowayout);
if (watchdog_init_timeout(wdd, timeout, dev))
dev_warn(dev, "Invalid timeout (%u seconds), using default (%u seconds)\n",
timeout, WATCHDOG_TIMEOUT);
platform_set_drvdata(pdev, wdd);
return watchdog_register_device(wdd);
}
static int ebc_c384_wdt_remove(struct platform_device *pdev)
{
struct watchdog_device *wdd = platform_get_drvdata(pdev);
watchdog_unregister_device(wdd);
return 0;
}
static struct platform_driver ebc_c384_wdt_driver = {
.driver = {
.name = MODULE_NAME
},
.remove = ebc_c384_wdt_remove
};
static struct platform_device *ebc_c384_wdt_device;
static int __init ebc_c384_wdt_init(void)
{
int err;
if (!dmi_match(DMI_BOARD_NAME, "EBC-C384 SBC"))
return -ENODEV;
ebc_c384_wdt_device = platform_device_alloc(MODULE_NAME, -1);
if (!ebc_c384_wdt_device)
return -ENOMEM;
err = platform_device_add(ebc_c384_wdt_device);
if (err)
goto err_platform_device;
err = platform_driver_probe(&ebc_c384_wdt_driver, ebc_c384_wdt_probe);
if (err)
goto err_platform_driver;
return 0;
err_platform_driver:
platform_device_del(ebc_c384_wdt_device);
err_platform_device:
platform_device_put(ebc_c384_wdt_device);
return err;
}
static void __exit ebc_c384_wdt_exit(void)
{
platform_device_unregister(ebc_c384_wdt_device);
platform_driver_unregister(&ebc_c384_wdt_driver);
}
module_init(ebc_c384_wdt_init);
module_exit(ebc_c384_wdt_exit);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("WinSystems EBC-C384 watchdog timer driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" MODULE_NAME);
...@@ -150,7 +150,8 @@ static int pdc_wdt_start(struct watchdog_device *wdt_dev) ...@@ -150,7 +150,8 @@ static int pdc_wdt_start(struct watchdog_device *wdt_dev)
return 0; return 0;
} }
static int pdc_wdt_restart(struct watchdog_device *wdt_dev) static int pdc_wdt_restart(struct watchdog_device *wdt_dev,
unsigned long action, void *data)
{ {
struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
......
...@@ -25,14 +25,12 @@ ...@@ -25,14 +25,12 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/timer.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#define DRIVER_NAME "imx2-wdt" #define DRIVER_NAME "imx2-wdt"
...@@ -60,7 +58,6 @@ ...@@ -60,7 +58,6 @@
struct imx2_wdt_device { struct imx2_wdt_device {
struct clk *clk; struct clk *clk;
struct regmap *regmap; struct regmap *regmap;
struct timer_list timer; /* Pings the watchdog when closed */
struct watchdog_device wdog; struct watchdog_device wdog;
}; };
...@@ -80,7 +77,8 @@ static const struct watchdog_info imx2_wdt_info = { ...@@ -80,7 +77,8 @@ static const struct watchdog_info imx2_wdt_info = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
}; };
static int imx2_wdt_restart(struct watchdog_device *wdog) static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action,
void *data)
{ {
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
unsigned int wcr_enable = IMX2_WDT_WCR_WDE; unsigned int wcr_enable = IMX2_WDT_WCR_WDE;
...@@ -146,16 +144,6 @@ static int imx2_wdt_ping(struct watchdog_device *wdog) ...@@ -146,16 +144,6 @@ static int imx2_wdt_ping(struct watchdog_device *wdog)
return 0; return 0;
} }
static void imx2_wdt_timer_ping(unsigned long arg)
{
struct watchdog_device *wdog = (struct watchdog_device *)arg;
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
/* ping it every wdog->timeout / 2 seconds to prevent reboot */
imx2_wdt_ping(wdog);
mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2);
}
static int imx2_wdt_set_timeout(struct watchdog_device *wdog, static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
unsigned int new_timeout) unsigned int new_timeout)
{ {
...@@ -172,40 +160,19 @@ static int imx2_wdt_start(struct watchdog_device *wdog) ...@@ -172,40 +160,19 @@ static int imx2_wdt_start(struct watchdog_device *wdog)
{ {
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
if (imx2_wdt_is_running(wdev)) { if (imx2_wdt_is_running(wdev))
/* delete the timer that pings the watchdog after close */
del_timer_sync(&wdev->timer);
imx2_wdt_set_timeout(wdog, wdog->timeout); imx2_wdt_set_timeout(wdog, wdog->timeout);
} else else
imx2_wdt_setup(wdog); imx2_wdt_setup(wdog);
return imx2_wdt_ping(wdog); set_bit(WDOG_HW_RUNNING, &wdog->status);
}
static int imx2_wdt_stop(struct watchdog_device *wdog)
{
/*
* We don't need a clk_disable, it cannot be disabled once started.
* We use a timer to ping the watchdog while /dev/watchdog is closed
*/
imx2_wdt_timer_ping((unsigned long)wdog);
return 0;
}
static inline void imx2_wdt_ping_if_active(struct watchdog_device *wdog)
{
struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
if (imx2_wdt_is_running(wdev)) { return imx2_wdt_ping(wdog);
imx2_wdt_set_timeout(wdog, wdog->timeout);
imx2_wdt_timer_ping((unsigned long)wdog);
}
} }
static const struct watchdog_ops imx2_wdt_ops = { static const struct watchdog_ops imx2_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = imx2_wdt_start, .start = imx2_wdt_start,
.stop = imx2_wdt_stop,
.ping = imx2_wdt_ping, .ping = imx2_wdt_ping,
.set_timeout = imx2_wdt_set_timeout, .set_timeout = imx2_wdt_set_timeout,
.restart = imx2_wdt_restart, .restart = imx2_wdt_restart,
...@@ -253,7 +220,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) ...@@ -253,7 +220,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
wdog->info = &imx2_wdt_info; wdog->info = &imx2_wdt_info;
wdog->ops = &imx2_wdt_ops; wdog->ops = &imx2_wdt_ops;
wdog->min_timeout = 1; wdog->min_timeout = 1;
wdog->max_timeout = IMX2_WDT_MAX_TIME; wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000;
wdog->parent = &pdev->dev; wdog->parent = &pdev->dev;
ret = clk_prepare_enable(wdev->clk); ret = clk_prepare_enable(wdev->clk);
...@@ -274,9 +241,10 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) ...@@ -274,9 +241,10 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
watchdog_set_restart_priority(wdog, 128); watchdog_set_restart_priority(wdog, 128);
watchdog_init_timeout(wdog, timeout, &pdev->dev); watchdog_init_timeout(wdog, timeout, &pdev->dev);
setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog); if (imx2_wdt_is_running(wdev)) {
imx2_wdt_set_timeout(wdog, wdog->timeout);
imx2_wdt_ping_if_active(wdog); set_bit(WDOG_HW_RUNNING, &wdog->status);
}
/* /*
* Disable the watchdog power down counter at boot. Otherwise the power * Disable the watchdog power down counter at boot. Otherwise the power
...@@ -309,7 +277,6 @@ static int __exit imx2_wdt_remove(struct platform_device *pdev) ...@@ -309,7 +277,6 @@ static int __exit imx2_wdt_remove(struct platform_device *pdev)
watchdog_unregister_device(wdog); watchdog_unregister_device(wdog);
if (imx2_wdt_is_running(wdev)) { if (imx2_wdt_is_running(wdev)) {
del_timer_sync(&wdev->timer);
imx2_wdt_ping(wdog); imx2_wdt_ping(wdog);
dev_crit(&pdev->dev, "Device removed: Expect reboot!\n"); dev_crit(&pdev->dev, "Device removed: Expect reboot!\n");
} }
...@@ -323,10 +290,9 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) ...@@ -323,10 +290,9 @@ static void imx2_wdt_shutdown(struct platform_device *pdev)
if (imx2_wdt_is_running(wdev)) { if (imx2_wdt_is_running(wdev)) {
/* /*
* We are running, we need to delete the timer but will * We are running, configure max timeout before reboot
* give max timeout before reboot will take place * will take place.
*/ */
del_timer_sync(&wdev->timer);
imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
imx2_wdt_ping(wdog); imx2_wdt_ping(wdog);
dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n"); dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n");
...@@ -344,10 +310,6 @@ static int imx2_wdt_suspend(struct device *dev) ...@@ -344,10 +310,6 @@ static int imx2_wdt_suspend(struct device *dev)
if (imx2_wdt_is_running(wdev)) { if (imx2_wdt_is_running(wdev)) {
imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
imx2_wdt_ping(wdog); imx2_wdt_ping(wdog);
/* The watchdog is not active */
if (!watchdog_active(wdog))
del_timer_sync(&wdev->timer);
} }
clk_disable_unprepare(wdev->clk); clk_disable_unprepare(wdev->clk);
...@@ -373,19 +335,10 @@ static int imx2_wdt_resume(struct device *dev) ...@@ -373,19 +335,10 @@ static int imx2_wdt_resume(struct device *dev)
* watchdog again. * watchdog again.
*/ */
imx2_wdt_setup(wdog); imx2_wdt_setup(wdog);
}
if (imx2_wdt_is_running(wdev)) {
imx2_wdt_set_timeout(wdog, wdog->timeout); imx2_wdt_set_timeout(wdog, wdog->timeout);
imx2_wdt_ping(wdog); imx2_wdt_ping(wdog);
} else if (imx2_wdt_is_running(wdev)) {
/* Resuming from non-deep sleep state. */
imx2_wdt_set_timeout(wdog, wdog->timeout);
imx2_wdt_ping(wdog);
/*
* But the watchdog is not active, then start
* the timer again.
*/
if (!watchdog_active(wdog))
mod_timer(&wdev->timer,
jiffies + wdog->timeout * HZ / 2);
} }
return 0; return 0;
......
...@@ -153,7 +153,8 @@ static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev) ...@@ -153,7 +153,8 @@ static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev)
return 0; return 0;
} }
static int lpc18xx_wdt_restart(struct watchdog_device *wdt_dev) static int lpc18xx_wdt_restart(struct watchdog_device *wdt_dev,
unsigned long action, void *data)
{ {
struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev); struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
unsigned long flags; unsigned long flags;
......
...@@ -62,7 +62,8 @@ struct meson_wdt_dev { ...@@ -62,7 +62,8 @@ struct meson_wdt_dev {
const struct meson_wdt_data *data; const struct meson_wdt_data *data;
}; };
static int meson_wdt_restart(struct watchdog_device *wdt_dev) static int meson_wdt_restart(struct watchdog_device *wdt_dev,
unsigned long action, void *data)
{ {
struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
u32 tc_reboot = MESON_WDT_DC_RESET; u32 tc_reboot = MESON_WDT_DC_RESET;
......
...@@ -31,7 +31,8 @@ struct moxart_wdt_dev { ...@@ -31,7 +31,8 @@ struct moxart_wdt_dev {
static int heartbeat; static int heartbeat;
static int moxart_wdt_restart(struct watchdog_device *wdt_dev) static int moxart_wdt_restart(struct watchdog_device *wdt_dev,
unsigned long action, void *data)
{ {
struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev); struct moxart_wdt_dev *moxart_wdt = watchdog_get_drvdata(wdt_dev);
......
...@@ -64,7 +64,8 @@ struct mtk_wdt_dev { ...@@ -64,7 +64,8 @@ struct mtk_wdt_dev {
void __iomem *wdt_base; void __iomem *wdt_base;
}; };
static int mtk_wdt_restart(struct watchdog_device *wdt_dev) static int mtk_wdt_restart(struct watchdog_device *wdt_dev,
unsigned long action, void *data)
{ {
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
void __iomem *wdt_base; void __iomem *wdt_base;
......
/*
* Copyright (C) 2016 National Instruments Corp.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/watchdog.h>
#define NIWD_CONTROL 0x01
#define NIWD_COUNTER2 0x02
#define NIWD_COUNTER1 0x03
#define NIWD_COUNTER0 0x04
#define NIWD_SEED2 0x05
#define NIWD_SEED1 0x06
#define NIWD_SEED0 0x07
#define NIWD_IO_SIZE 0x08
#define NIWD_CONTROL_MODE 0x80
#define NIWD_CONTROL_PROC_RESET 0x20
#define NIWD_CONTROL_PET 0x10
#define NIWD_CONTROL_RUNNING 0x08
#define NIWD_CONTROL_CAPTURECOUNTER 0x04
#define NIWD_CONTROL_RESET 0x02
#define NIWD_CONTROL_ALARM 0x01
#define NIWD_PERIOD_NS 30720
#define NIWD_MIN_TIMEOUT 1
#define NIWD_MAX_TIMEOUT 515
#define NIWD_DEFAULT_TIMEOUT 60
#define NIWD_NAME "ni903x_wdt"
struct ni903x_wdt {
struct device *dev;
u16 io_base;
struct watchdog_device wdd;
};
static unsigned int timeout;
module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. (default="
__MODULE_STRING(NIWD_DEFAULT_TIMEOUT) ")");
static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, S_IRUGO);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static void ni903x_start(struct ni903x_wdt *wdt)
{
u8 control = inb(wdt->io_base + NIWD_CONTROL);
outb(control | NIWD_CONTROL_RESET, wdt->io_base + NIWD_CONTROL);
outb(control | NIWD_CONTROL_PET, wdt->io_base + NIWD_CONTROL);
}
static int ni903x_wdd_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
u32 counter = timeout * (1000000000 / NIWD_PERIOD_NS);
outb(((0x00FF0000 & counter) >> 16), wdt->io_base + NIWD_SEED2);
outb(((0x0000FF00 & counter) >> 8), wdt->io_base + NIWD_SEED1);
outb((0x000000FF & counter), wdt->io_base + NIWD_SEED0);
wdd->timeout = timeout;
return 0;
}
static unsigned int ni903x_wdd_get_timeleft(struct watchdog_device *wdd)
{
struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
u8 control, counter0, counter1, counter2;
u32 counter;
control = inb(wdt->io_base + NIWD_CONTROL);
control |= NIWD_CONTROL_CAPTURECOUNTER;
outb(control, wdt->io_base + NIWD_CONTROL);
counter2 = inb(wdt->io_base + NIWD_COUNTER2);
counter1 = inb(wdt->io_base + NIWD_COUNTER1);
counter0 = inb(wdt->io_base + NIWD_COUNTER0);
counter = (counter2 << 16) | (counter1 << 8) | counter0;
return counter / (1000000000 / NIWD_PERIOD_NS);
}
static int ni903x_wdd_ping(struct watchdog_device *wdd)
{
struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
u8 control;
control = inb(wdt->io_base + NIWD_CONTROL);
outb(control | NIWD_CONTROL_PET, wdt->io_base + NIWD_CONTROL);
return 0;
}
static int ni903x_wdd_start(struct watchdog_device *wdd)
{
struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
outb(NIWD_CONTROL_RESET | NIWD_CONTROL_PROC_RESET,
wdt->io_base + NIWD_CONTROL);
ni903x_wdd_set_timeout(wdd, wdd->timeout);
ni903x_start(wdt);
return 0;
}
static int ni903x_wdd_stop(struct watchdog_device *wdd)
{
struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
outb(NIWD_CONTROL_RESET, wdt->io_base + NIWD_CONTROL);
return 0;
}
static acpi_status ni903x_resources(struct acpi_resource *res, void *data)
{
struct ni903x_wdt *wdt = data;
u16 io_size;
switch (res->type) {
case ACPI_RESOURCE_TYPE_IO:
if (wdt->io_base != 0) {
dev_err(wdt->dev, "too many IO resources\n");
return AE_ERROR;
}
wdt->io_base = res->data.io.minimum;
io_size = res->data.io.address_length;
if (io_size < NIWD_IO_SIZE) {
dev_err(wdt->dev, "memory region too small\n");
return AE_ERROR;
}
if (!devm_request_region(wdt->dev, wdt->io_base, io_size,
NIWD_NAME)) {
dev_err(wdt->dev, "failed to get memory region\n");
return AE_ERROR;
}
return AE_OK;
case ACPI_RESOURCE_TYPE_END_TAG:
default:
/* Ignore unsupported resources, e.g. IRQ */
return AE_OK;
}
}
static const struct watchdog_info ni903x_wdd_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "NI Watchdog",
};
static const struct watchdog_ops ni903x_wdd_ops = {
.owner = THIS_MODULE,
.start = ni903x_wdd_start,
.stop = ni903x_wdd_stop,
.ping = ni903x_wdd_ping,
.set_timeout = ni903x_wdd_set_timeout,
.get_timeleft = ni903x_wdd_get_timeleft,
};
static int ni903x_acpi_add(struct acpi_device *device)
{
struct device *dev = &device->dev;
struct watchdog_device *wdd;
struct ni903x_wdt *wdt;
acpi_status status;
int ret;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
device->driver_data = wdt;
wdt->dev = dev;
status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
ni903x_resources, wdt);
if (ACPI_FAILURE(status) || wdt->io_base == 0) {
dev_err(dev, "failed to get resources\n");
return -ENODEV;
}
wdd = &wdt->wdd;
wdd->info = &ni903x_wdd_info;
wdd->ops = &ni903x_wdd_ops;
wdd->min_timeout = NIWD_MIN_TIMEOUT;
wdd->max_timeout = NIWD_MAX_TIMEOUT;
wdd->timeout = NIWD_DEFAULT_TIMEOUT;
wdd->parent = dev;
watchdog_set_drvdata(wdd, wdt);
watchdog_set_nowayout(wdd, nowayout);
ret = watchdog_init_timeout(wdd, timeout, dev);
if (ret)
dev_err(dev, "unable to set timeout value, using default\n");
ret = watchdog_register_device(wdd);
if (ret) {
dev_err(dev, "failed to register watchdog\n");
return ret;
}
/* Switch from boot mode to user mode */
outb(NIWD_CONTROL_RESET | NIWD_CONTROL_MODE,
wdt->io_base + NIWD_CONTROL);
dev_dbg(dev, "io_base=0x%04X, timeout=%d, nowayout=%d\n",
wdt->io_base, timeout, nowayout);
return 0;
}
static int ni903x_acpi_remove(struct acpi_device *device)
{
struct ni903x_wdt *wdt = acpi_driver_data(device);
ni903x_wdd_stop(&wdt->wdd);
watchdog_unregister_device(&wdt->wdd);
return 0;
}
static const struct acpi_device_id ni903x_device_ids[] = {
{"NIC775C", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, ni903x_device_ids);
static struct acpi_driver ni903x_acpi_driver = {
.name = NIWD_NAME,
.ids = ni903x_device_ids,
.ops = {
.add = ni903x_acpi_add,
.remove = ni903x_acpi_remove,
},
};
module_acpi_driver(ni903x_acpi_driver);
MODULE_DESCRIPTION("NI 903x Watchdog");
MODULE_AUTHOR("Jeff Westfahl <jeff.westfahl@ni.com>");
MODULE_AUTHOR("Kyle Roeschley <kyle.roeschley@ni.com>");
MODULE_LICENSE("GPL");
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <mach/hardware.h> #include <mach/hardware.h>
/* WatchDog Timer - Chapter 23 Page 207 */ /* WatchDog Timer - Chapter 23 Page 207 */
...@@ -124,6 +126,41 @@ static int pnx4008_wdt_set_timeout(struct watchdog_device *wdd, ...@@ -124,6 +126,41 @@ static int pnx4008_wdt_set_timeout(struct watchdog_device *wdd,
return 0; return 0;
} }
static int pnx4008_restart_handler(struct watchdog_device *wdd,
unsigned long mode, void *cmd)
{
const char *boot_cmd = cmd;
/*
* Verify if a "cmd" passed from the userspace program rebooting
* the system; if available, handle it.
* - For details, see the 'reboot' syscall in kernel/reboot.c
* - If the received "cmd" is not supported, use the default mode.
*/
if (boot_cmd) {
if (boot_cmd[0] == 'h')
mode = REBOOT_HARD;
else if (boot_cmd[0] == 's')
mode = REBOOT_SOFT;
}
if (mode == REBOOT_SOFT) {
/* Force match output active */
writel(EXT_MATCH0, WDTIM_EMR(wdt_base));
/* Internal reset on match output (RESOUT_N not asserted) */
writel(M_RES1, WDTIM_MCTRL(wdt_base));
} else {
/* Instant assert of RESETOUT_N with pulse length 1mS */
writel(13000, WDTIM_PULSE(wdt_base));
writel(M_RES2 | RESFRC1 | RESFRC2, WDTIM_MCTRL(wdt_base));
}
/* Wait for watchdog to reset system */
mdelay(1000);
return NOTIFY_DONE;
}
static const struct watchdog_info pnx4008_wdt_ident = { static const struct watchdog_info pnx4008_wdt_ident = {
.options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
...@@ -135,6 +172,7 @@ static const struct watchdog_ops pnx4008_wdt_ops = { ...@@ -135,6 +172,7 @@ static const struct watchdog_ops pnx4008_wdt_ops = {
.start = pnx4008_wdt_start, .start = pnx4008_wdt_start,
.stop = pnx4008_wdt_stop, .stop = pnx4008_wdt_stop,
.set_timeout = pnx4008_wdt_set_timeout, .set_timeout = pnx4008_wdt_set_timeout,
.restart = pnx4008_restart_handler,
}; };
static struct watchdog_device pnx4008_wdd = { static struct watchdog_device pnx4008_wdd = {
...@@ -169,6 +207,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev) ...@@ -169,6 +207,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
WDIOF_CARDRESET : 0; WDIOF_CARDRESET : 0;
pnx4008_wdd.parent = &pdev->dev; pnx4008_wdd.parent = &pdev->dev;
watchdog_set_nowayout(&pnx4008_wdd, nowayout); watchdog_set_nowayout(&pnx4008_wdd, nowayout);
watchdog_set_restart_priority(&pnx4008_wdd, 128);
pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */ pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */
...@@ -178,8 +217,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev) ...@@ -178,8 +217,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
goto disable_clk; goto disable_clk;
} }
dev_info(&pdev->dev, "PNX4008 Watchdog Timer: heartbeat %d sec\n", dev_info(&pdev->dev, "heartbeat %d sec\n", pnx4008_wdd.timeout);
pnx4008_wdd.timeout);
return 0; return 0;
......
...@@ -70,7 +70,8 @@ static int qcom_wdt_set_timeout(struct watchdog_device *wdd, ...@@ -70,7 +70,8 @@ static int qcom_wdt_set_timeout(struct watchdog_device *wdd,
return qcom_wdt_start(wdd); return qcom_wdt_start(wdd);
} }
static int qcom_wdt_restart(struct watchdog_device *wdd) static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
void *data)
{ {
struct qcom_wdt *wdt = to_qcom_wdt(wdd); struct qcom_wdt *wdt = to_qcom_wdt(wdd);
u32 timeout; u32 timeout;
......
...@@ -237,7 +237,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -237,7 +237,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
return -EINVAL; return -EINVAL;
/* Fall through */ /* Fall through */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return copy_to_user(argp, &timeout, sizeof(int)); return copy_to_user(argp, &timeout, sizeof(int)) ? -EFAULT : 0;
default: default:
return -ENOTTY; return -ENOTTY;
} }
......
...@@ -47,6 +47,8 @@ ...@@ -47,6 +47,8 @@
#define S3C2410_WTDAT 0x04 #define S3C2410_WTDAT 0x04
#define S3C2410_WTCNT 0x08 #define S3C2410_WTCNT 0x08
#define S3C2410_WTCNT_MAXCNT 0xffff
#define S3C2410_WTCON_RSTEN (1 << 0) #define S3C2410_WTCON_RSTEN (1 << 0)
#define S3C2410_WTCON_INTEN (1 << 2) #define S3C2410_WTCON_INTEN (1 << 2)
#define S3C2410_WTCON_ENABLE (1 << 5) #define S3C2410_WTCON_ENABLE (1 << 5)
...@@ -56,8 +58,11 @@ ...@@ -56,8 +58,11 @@
#define S3C2410_WTCON_DIV64 (2 << 3) #define S3C2410_WTCON_DIV64 (2 << 3)
#define S3C2410_WTCON_DIV128 (3 << 3) #define S3C2410_WTCON_DIV128 (3 << 3)
#define S3C2410_WTCON_MAXDIV 0x80
#define S3C2410_WTCON_PRESCALE(x) ((x) << 8) #define S3C2410_WTCON_PRESCALE(x) ((x) << 8)
#define S3C2410_WTCON_PRESCALE_MASK (0xff << 8) #define S3C2410_WTCON_PRESCALE_MASK (0xff << 8)
#define S3C2410_WTCON_PRESCALE_MAX 0xff
#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0) #define CONFIG_S3C2410_WATCHDOG_ATBOOT (0)
#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15) #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15)
...@@ -198,6 +203,14 @@ do { \ ...@@ -198,6 +203,14 @@ do { \
/* functions */ /* functions */
static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock)
{
unsigned long freq = clk_get_rate(clock);
return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
/ S3C2410_WTCON_MAXDIV);
}
static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb) static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb)
{ {
return container_of(nb, struct s3c2410_wdt, freq_transition); return container_of(nb, struct s3c2410_wdt, freq_transition);
...@@ -349,7 +362,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou ...@@ -349,7 +362,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou
return 0; return 0;
} }
static int s3c2410wdt_restart(struct watchdog_device *wdd) static int s3c2410wdt_restart(struct watchdog_device *wdd, unsigned long action,
void *data)
{ {
struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
void __iomem *wdt_base = wdt->reg_base; void __iomem *wdt_base = wdt->reg_base;
...@@ -567,6 +581,9 @@ static int s3c2410wdt_probe(struct platform_device *pdev) ...@@ -567,6 +581,9 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
return ret; return ret;
} }
wdt->wdt_device.min_timeout = 1;
wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt->clock);
ret = s3c2410wdt_cpufreq_register(wdt); ret = s3c2410wdt_cpufreq_register(wdt);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "failed to register cpufreq\n"); dev_err(dev, "failed to register cpufreq\n");
......
/*
* SBSA(Server Base System Architecture) Generic Watchdog driver
*
* Copyright (c) 2015, Linaro Ltd.
* Author: Fu Wei <fu.wei@linaro.org>
* Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
* Al Stone <al.stone@linaro.org>
* Timur Tabi <timur@codeaurora.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 2 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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 about the hardware specification of this device:
* ARM DEN0029B - Server Base System Architecture (SBSA)
*
* This driver can operate ARM SBSA Generic Watchdog as a single stage watchdog
* or a two stages watchdog, it's set up by the module parameter "action".
* In the single stage mode, when the timeout is reached, your system
* will be reset by WS1. The first signal (WS0) is ignored.
* In the two stages mode, when the timeout is reached, the first signal (WS0)
* will trigger panic. If the system is getting into trouble and cannot be reset
* by panic or restart properly by the kdump kernel(if supported), then the
* second stage (as long as the first stage) will be reached, system will be
* reset by WS1. This function can help administrator to backup the system
* context info by panic console output or kdump.
*
* SBSA GWDT:
* if action is 1 (the two stages mode):
* |--------WOR-------WS0--------WOR-------WS1
* |----timeout-----(panic)----timeout-----reset
*
* if action is 0 (the single stage mode):
* |------WOR-----WS0(ignored)-----WOR------WS1
* |--------------timeout-------------------reset
*
* Note: Since this watchdog timer has two stages, and each stage is determined
* by WOR, in the single stage mode, the timeout is (WOR * 2); in the two
* stages mode, the timeout is WOR. The maximum timeout in the two stages mode
* is half of that in the single stage mode.
*
*/
#include <linux/io.h>
#include <linux/interrupt.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/uaccess.h>
#include <linux/watchdog.h>
#include <asm/arch_timer.h>
#define DRV_NAME "sbsa-gwdt"
#define WATCHDOG_NAME "SBSA Generic Watchdog"
/* SBSA Generic Watchdog register definitions */
/* refresh frame */
#define SBSA_GWDT_WRR 0x000
/* control frame */
#define SBSA_GWDT_WCS 0x000
#define SBSA_GWDT_WOR 0x008
#define SBSA_GWDT_WCV 0x010
/* refresh/control frame */
#define SBSA_GWDT_W_IIDR 0xfcc
#define SBSA_GWDT_IDR 0xfd0
/* Watchdog Control and Status Register */
#define SBSA_GWDT_WCS_EN BIT(0)
#define SBSA_GWDT_WCS_WS0 BIT(1)
#define SBSA_GWDT_WCS_WS1 BIT(2)
/**
* struct sbsa_gwdt - Internal representation of the SBSA GWDT
* @wdd: kernel watchdog_device structure
* @clk: store the System Counter clock frequency, in Hz.
* @refresh_base: Virtual address of the watchdog refresh frame
* @control_base: Virtual address of the watchdog control frame
*/
struct sbsa_gwdt {
struct watchdog_device wdd;
u32 clk;
void __iomem *refresh_base;
void __iomem *control_base;
};
#define DEFAULT_TIMEOUT 10 /* seconds */
static unsigned int timeout;
module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. (>=0, default="
__MODULE_STRING(DEFAULT_TIMEOUT) ")");
/*
* action refers to action taken when watchdog gets WS0
* 0 = skip
* 1 = panic
* defaults to skip (0)
*/
static int action;
module_param(action, int, 0);
MODULE_PARM_DESC(action, "after watchdog gets WS0 interrupt, do: "
"0 = skip(*) 1 = panic");
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, S_IRUGO);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
/*
* watchdog operation functions
*/
static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
wdd->timeout = timeout;
if (action)
writel(gwdt->clk * timeout,
gwdt->control_base + SBSA_GWDT_WOR);
else
/*
* In the single stage mode, The first signal (WS0) is ignored,
* the timeout is (WOR * 2), so the WOR should be configured
* to half value of timeout.
*/
writel(gwdt->clk / 2 * timeout,
gwdt->control_base + SBSA_GWDT_WOR);
return 0;
}
static unsigned int sbsa_gwdt_get_timeleft(struct watchdog_device *wdd)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
u64 timeleft = 0;
/*
* In the single stage mode, if WS0 is deasserted
* (watchdog is in the first stage),
* timeleft = WOR + (WCV - system counter)
*/
if (!action &&
!(readl(gwdt->control_base + SBSA_GWDT_WCS) & SBSA_GWDT_WCS_WS0))
timeleft += readl(gwdt->control_base + SBSA_GWDT_WOR);
timeleft += readq(gwdt->control_base + SBSA_GWDT_WCV) -
arch_counter_get_cntvct();
do_div(timeleft, gwdt->clk);
return timeleft;
}
static int sbsa_gwdt_keepalive(struct watchdog_device *wdd)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
/*
* Writing WRR for an explicit watchdog refresh.
* You can write anyting (like 0).
*/
writel(0, gwdt->refresh_base + SBSA_GWDT_WRR);
return 0;
}
static unsigned int sbsa_gwdt_status(struct watchdog_device *wdd)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
u32 status = readl(gwdt->control_base + SBSA_GWDT_WCS);
/* is the watchdog timer running? */
return (status & SBSA_GWDT_WCS_EN) << WDOG_ACTIVE;
}
static int sbsa_gwdt_start(struct watchdog_device *wdd)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
/* writing WCS will cause an explicit watchdog refresh */
writel(SBSA_GWDT_WCS_EN, gwdt->control_base + SBSA_GWDT_WCS);
return 0;
}
static int sbsa_gwdt_stop(struct watchdog_device *wdd)
{
struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
/* Simply write 0 to WCS to clean WCS_EN bit */
writel(0, gwdt->control_base + SBSA_GWDT_WCS);
return 0;
}
static irqreturn_t sbsa_gwdt_interrupt(int irq, void *dev_id)
{
panic(WATCHDOG_NAME " timeout");
return IRQ_HANDLED;
}
static struct watchdog_info sbsa_gwdt_info = {
.identity = WATCHDOG_NAME,
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE |
WDIOF_CARDRESET,
};
static struct watchdog_ops sbsa_gwdt_ops = {
.owner = THIS_MODULE,
.start = sbsa_gwdt_start,
.stop = sbsa_gwdt_stop,
.status = sbsa_gwdt_status,
.ping = sbsa_gwdt_keepalive,
.set_timeout = sbsa_gwdt_set_timeout,
.get_timeleft = sbsa_gwdt_get_timeleft,
};
static int sbsa_gwdt_probe(struct platform_device *pdev)
{
void __iomem *rf_base, *cf_base;
struct device *dev = &pdev->dev;
struct watchdog_device *wdd;
struct sbsa_gwdt *gwdt;
struct resource *res;
int ret, irq;
u32 status;
gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL);
if (!gwdt)
return -ENOMEM;
platform_set_drvdata(pdev, gwdt);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
cf_base = devm_ioremap_resource(dev, res);
if (IS_ERR(cf_base))
return PTR_ERR(cf_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
rf_base = devm_ioremap_resource(dev, res);
if (IS_ERR(rf_base))
return PTR_ERR(rf_base);
/*
* Get the frequency of system counter from the cp15 interface of ARM
* Generic timer. We don't need to check it, because if it returns "0",
* system would panic in very early stage.
*/
gwdt->clk = arch_timer_get_cntfrq();
gwdt->refresh_base = rf_base;
gwdt->control_base = cf_base;
wdd = &gwdt->wdd;
wdd->parent = dev;
wdd->info = &sbsa_gwdt_info;
wdd->ops = &sbsa_gwdt_ops;
wdd->min_timeout = 1;
wdd->max_timeout = U32_MAX / gwdt->clk;
wdd->timeout = DEFAULT_TIMEOUT;
watchdog_set_drvdata(wdd, gwdt);
watchdog_set_nowayout(wdd, nowayout);
status = readl(cf_base + SBSA_GWDT_WCS);
if (status & SBSA_GWDT_WCS_WS1) {
dev_warn(dev, "System reset by WDT.\n");
wdd->bootstatus |= WDIOF_CARDRESET;
}
if (action) {
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
action = 0;
dev_warn(dev, "unable to get ws0 interrupt.\n");
} else {
/*
* In case there is a pending ws0 interrupt, just ping
* the watchdog before registering the interrupt routine
*/
writel(0, rf_base + SBSA_GWDT_WRR);
if (devm_request_irq(dev, irq, sbsa_gwdt_interrupt, 0,
pdev->name, gwdt)) {
action = 0;
dev_warn(dev, "unable to request IRQ %d.\n",
irq);
}
}
if (!action)
dev_warn(dev, "falling back to single stage mode.\n");
}
/*
* In the single stage mode, The first signal (WS0) is ignored,
* the timeout is (WOR * 2), so the maximum timeout should be doubled.
*/
if (!action)
wdd->max_timeout *= 2;
watchdog_init_timeout(wdd, timeout, dev);
/*
* Update timeout to WOR.
* Because of the explicit watchdog refresh mechanism,
* it's also a ping, if watchdog is enabled.
*/
sbsa_gwdt_set_timeout(wdd, wdd->timeout);
ret = watchdog_register_device(wdd);
if (ret)
return ret;
dev_info(dev, "Initialized with %ds timeout @ %u Hz, action=%d.%s\n",
wdd->timeout, gwdt->clk, action,
status & SBSA_GWDT_WCS_EN ? " [enabled]" : "");
return 0;
}
static void sbsa_gwdt_shutdown(struct platform_device *pdev)
{
struct sbsa_gwdt *gwdt = platform_get_drvdata(pdev);
sbsa_gwdt_stop(&gwdt->wdd);
}
static int sbsa_gwdt_remove(struct platform_device *pdev)
{
struct sbsa_gwdt *gwdt = platform_get_drvdata(pdev);
watchdog_unregister_device(&gwdt->wdd);
return 0;
}
/* Disable watchdog if it is active during suspend */
static int __maybe_unused sbsa_gwdt_suspend(struct device *dev)
{
struct sbsa_gwdt *gwdt = dev_get_drvdata(dev);
if (watchdog_active(&gwdt->wdd))
sbsa_gwdt_stop(&gwdt->wdd);
return 0;
}
/* Enable watchdog if necessary */
static int __maybe_unused sbsa_gwdt_resume(struct device *dev)
{
struct sbsa_gwdt *gwdt = dev_get_drvdata(dev);
if (watchdog_active(&gwdt->wdd))
sbsa_gwdt_start(&gwdt->wdd);
return 0;
}
static const struct dev_pm_ops sbsa_gwdt_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(sbsa_gwdt_suspend, sbsa_gwdt_resume)
};
static const struct of_device_id sbsa_gwdt_of_match[] = {
{ .compatible = "arm,sbsa-gwdt", },
{},
};
MODULE_DEVICE_TABLE(of, sbsa_gwdt_of_match);
static const struct platform_device_id sbsa_gwdt_pdev_match[] = {
{ .name = DRV_NAME, },
{},
};
MODULE_DEVICE_TABLE(platform, sbsa_gwdt_pdev_match);
static struct platform_driver sbsa_gwdt_driver = {
.driver = {
.name = DRV_NAME,
.pm = &sbsa_gwdt_pm_ops,
.of_match_table = sbsa_gwdt_of_match,
},
.probe = sbsa_gwdt_probe,
.remove = sbsa_gwdt_remove,
.shutdown = sbsa_gwdt_shutdown,
.id_table = sbsa_gwdt_pdev_match,
};
module_platform_driver(sbsa_gwdt_driver);
MODULE_DESCRIPTION("SBSA Generic Watchdog Driver");
MODULE_AUTHOR("Fu Wei <fu.wei@linaro.org>");
MODULE_AUTHOR("Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>");
MODULE_AUTHOR("Al Stone <al.stone@linaro.org>");
MODULE_AUTHOR("Timur Tabi <timur@codeaurora.org>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
...@@ -83,7 +83,8 @@ static const int wdt_timeout_map[] = { ...@@ -83,7 +83,8 @@ static const int wdt_timeout_map[] = {
}; };
static int sunxi_wdt_restart(struct watchdog_device *wdt_dev) static int sunxi_wdt_restart(struct watchdog_device *wdt_dev,
unsigned long action, void *data)
{ {
struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev);
void __iomem *wdt_base = sunxi_wdt->wdt_base; void __iomem *wdt_base = sunxi_wdt->wdt_base;
......
...@@ -139,6 +139,10 @@ static int tangox_wdt_probe(struct platform_device *pdev) ...@@ -139,6 +139,10 @@ static int tangox_wdt_probe(struct platform_device *pdev)
return err; return err;
dev->clk_rate = clk_get_rate(dev->clk); dev->clk_rate = clk_get_rate(dev->clk);
if (!dev->clk_rate) {
err = -EINVAL;
goto err;
}
dev->wdt.parent = &pdev->dev; dev->wdt.parent = &pdev->dev;
dev->wdt.info = &tangox_wdt_info; dev->wdt.info = &tangox_wdt_info;
...@@ -171,10 +175,8 @@ static int tangox_wdt_probe(struct platform_device *pdev) ...@@ -171,10 +175,8 @@ static int tangox_wdt_probe(struct platform_device *pdev)
} }
err = watchdog_register_device(&dev->wdt); err = watchdog_register_device(&dev->wdt);
if (err) { if (err)
clk_disable_unprepare(dev->clk); goto err;
return err;
}
platform_set_drvdata(pdev, dev); platform_set_drvdata(pdev, dev);
...@@ -187,6 +189,10 @@ static int tangox_wdt_probe(struct platform_device *pdev) ...@@ -187,6 +189,10 @@ static int tangox_wdt_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n"); dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n");
return 0; return 0;
err:
clk_disable_unprepare(dev->clk);
return err;
} }
static int tangox_wdt_remove(struct platform_device *pdev) static int tangox_wdt_remove(struct platform_device *pdev)
......
...@@ -45,10 +45,11 @@ ...@@ -45,10 +45,11 @@
static int wdt_io; static int wdt_io;
static int cr_wdt_timeout; /* WDT timeout register */ static int cr_wdt_timeout; /* WDT timeout register */
static int cr_wdt_control; /* WDT control register */ static int cr_wdt_control; /* WDT control register */
static int cr_wdt_csr; /* WDT control & status register */
enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf,
w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p,
w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792 }; w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6102 };
static int timeout; /* in seconds */ static int timeout; /* in seconds */
module_param(timeout, int, 0); module_param(timeout, int, 0);
...@@ -92,15 +93,21 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); ...@@ -92,15 +93,21 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define W83667HG_B_ID 0xb3 #define W83667HG_B_ID 0xb3
#define NCT6775_ID 0xb4 #define NCT6775_ID 0xb4
#define NCT6776_ID 0xc3 #define NCT6776_ID 0xc3
#define NCT6102_ID 0xc4
#define NCT6779_ID 0xc5 #define NCT6779_ID 0xc5
#define NCT6791_ID 0xc8 #define NCT6791_ID 0xc8
#define NCT6792_ID 0xc9 #define NCT6792_ID 0xc9
#define W83627HF_WDT_TIMEOUT 0xf6 #define W83627HF_WDT_TIMEOUT 0xf6
#define W83697HF_WDT_TIMEOUT 0xf4 #define W83697HF_WDT_TIMEOUT 0xf4
#define NCT6102D_WDT_TIMEOUT 0xf1
#define W83627HF_WDT_CONTROL 0xf5 #define W83627HF_WDT_CONTROL 0xf5
#define W83697HF_WDT_CONTROL 0xf3 #define W83697HF_WDT_CONTROL 0xf3
#define NCT6102D_WDT_CONTROL 0xf0
#define W836X7HF_WDT_CSR 0xf7
#define NCT6102D_WDT_CSR 0xf2
static void superio_outb(int reg, int val) static void superio_outb(int reg, int val)
{ {
...@@ -197,6 +204,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) ...@@ -197,6 +204,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
case nct6779: case nct6779:
case nct6791: case nct6791:
case nct6792: case nct6792:
case nct6102:
/* /*
* These chips have a fixed WDTO# output pin (W83627UHG), * These chips have a fixed WDTO# output pin (W83627UHG),
* or support more than one WDTO# output pin. * or support more than one WDTO# output pin.
...@@ -229,8 +237,8 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) ...@@ -229,8 +237,8 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
superio_outb(cr_wdt_control, t); superio_outb(cr_wdt_control, t);
/* reset trigger, disable keyboard & mouse turning off watchdog */ /* reset trigger, disable keyboard & mouse turning off watchdog */
t = superio_inb(0xF7) & ~0xD0; t = superio_inb(cr_wdt_csr) & ~0xD0;
superio_outb(0xF7, t); superio_outb(cr_wdt_csr, t);
superio_exit(); superio_exit();
...@@ -322,6 +330,7 @@ static int wdt_find(int addr) ...@@ -322,6 +330,7 @@ static int wdt_find(int addr)
cr_wdt_timeout = W83627HF_WDT_TIMEOUT; cr_wdt_timeout = W83627HF_WDT_TIMEOUT;
cr_wdt_control = W83627HF_WDT_CONTROL; cr_wdt_control = W83627HF_WDT_CONTROL;
cr_wdt_csr = W836X7HF_WDT_CSR;
ret = superio_enter(); ret = superio_enter();
if (ret) if (ret)
...@@ -387,6 +396,12 @@ static int wdt_find(int addr) ...@@ -387,6 +396,12 @@ static int wdt_find(int addr)
case NCT6792_ID: case NCT6792_ID:
ret = nct6792; ret = nct6792;
break; break;
case NCT6102_ID:
ret = nct6102;
cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
cr_wdt_control = NCT6102D_WDT_CONTROL;
cr_wdt_csr = NCT6102D_WDT_CSR;
break;
case 0xff: case 0xff:
ret = -ENODEV; ret = -ENODEV;
break; break;
...@@ -422,6 +437,7 @@ static int __init wdt_init(void) ...@@ -422,6 +437,7 @@ static int __init wdt_init(void)
"NCT6779", "NCT6779",
"NCT6791", "NCT6791",
"NCT6792", "NCT6792",
"NCT6102",
}; };
wdt_io = 0x2e; wdt_io = 0x2e;
......
...@@ -164,7 +164,7 @@ static int watchdog_restart_notifier(struct notifier_block *nb, ...@@ -164,7 +164,7 @@ static int watchdog_restart_notifier(struct notifier_block *nb,
int ret; int ret;
ret = wdd->ops->restart(wdd); ret = wdd->ops->restart(wdd, action, data);
if (ret) if (ret)
return NOTIFY_BAD; return NOTIFY_BAD;
...@@ -199,7 +199,7 @@ static int __watchdog_register_device(struct watchdog_device *wdd) ...@@ -199,7 +199,7 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
return -EINVAL; return -EINVAL;
/* Mandatory operations need to be supported */ /* Mandatory operations need to be supported */
if (wdd->ops->start == NULL || wdd->ops->stop == NULL) if (!wdd->ops->start || (!wdd->ops->stop && !wdd->max_hw_heartbeat_ms))
return -EINVAL; return -EINVAL;
watchdog_check_min_max_timeout(wdd); watchdog_check_min_max_timeout(wdd);
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/errno.h> /* For the -ENODEV/... values */ #include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/fs.h> /* For file operations */ #include <linux/fs.h> /* For file operations */
#include <linux/init.h> /* For __init/__exit/... */ #include <linux/init.h> /* For __init/__exit/... */
#include <linux/jiffies.h> /* For timeout functions */
#include <linux/kernel.h> /* For printk/panic/... */ #include <linux/kernel.h> /* For printk/panic/... */
#include <linux/kref.h> /* For data references */ #include <linux/kref.h> /* For data references */
#include <linux/miscdevice.h> /* For handling misc devices */ #include <linux/miscdevice.h> /* For handling misc devices */
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
#include <linux/slab.h> /* For memory functions */ #include <linux/slab.h> /* For memory functions */
#include <linux/types.h> /* For standard types (like size_t) */ #include <linux/types.h> /* For standard types (like size_t) */
#include <linux/watchdog.h> /* For watchdog specific items */ #include <linux/watchdog.h> /* For watchdog specific items */
#include <linux/workqueue.h> /* For workqueue */
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
#include "watchdog_core.h" #include "watchdog_core.h"
...@@ -61,6 +63,9 @@ struct watchdog_core_data { ...@@ -61,6 +63,9 @@ struct watchdog_core_data {
struct cdev cdev; struct cdev cdev;
struct watchdog_device *wdd; struct watchdog_device *wdd;
struct mutex lock; struct mutex lock;
unsigned long last_keepalive;
unsigned long last_hw_keepalive;
struct delayed_work work;
unsigned long status; /* Internal status bits */ unsigned long status; /* Internal status bits */
#define _WDOG_DEV_OPEN 0 /* Opened ? */ #define _WDOG_DEV_OPEN 0 /* Opened ? */
#define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ #define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */
...@@ -71,6 +76,91 @@ static dev_t watchdog_devt; ...@@ -71,6 +76,91 @@ static dev_t watchdog_devt;
/* Reference to watchdog device behind /dev/watchdog */ /* Reference to watchdog device behind /dev/watchdog */
static struct watchdog_core_data *old_wd_data; static struct watchdog_core_data *old_wd_data;
static struct workqueue_struct *watchdog_wq;
static inline bool watchdog_need_worker(struct watchdog_device *wdd)
{
/* All variables in milli-seconds */
unsigned int hm = wdd->max_hw_heartbeat_ms;
unsigned int t = wdd->timeout * 1000;
/*
* A worker to generate heartbeat requests is needed if all of the
* following conditions are true.
* - Userspace activated the watchdog.
* - The driver provided a value for the maximum hardware timeout, and
* thus is aware that the framework supports generating heartbeat
* requests.
* - Userspace requests a longer timeout than the hardware can handle.
*/
return hm && ((watchdog_active(wdd) && t > hm) ||
(t && !watchdog_active(wdd) && watchdog_hw_running(wdd)));
}
static long watchdog_next_keepalive(struct watchdog_device *wdd)
{
struct watchdog_core_data *wd_data = wdd->wd_data;
unsigned int timeout_ms = wdd->timeout * 1000;
unsigned long keepalive_interval;
unsigned long last_heartbeat;
unsigned long virt_timeout;
unsigned int hw_heartbeat_ms;
virt_timeout = wd_data->last_keepalive + msecs_to_jiffies(timeout_ms);
hw_heartbeat_ms = min(timeout_ms, wdd->max_hw_heartbeat_ms);
keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2);
if (!watchdog_active(wdd))
return keepalive_interval;
/*
* To ensure that the watchdog times out wdd->timeout seconds
* after the most recent ping from userspace, the last
* worker ping has to come in hw_heartbeat_ms before this timeout.
*/
last_heartbeat = virt_timeout - msecs_to_jiffies(hw_heartbeat_ms);
return min_t(long, last_heartbeat - jiffies, keepalive_interval);
}
static inline void watchdog_update_worker(struct watchdog_device *wdd)
{
struct watchdog_core_data *wd_data = wdd->wd_data;
if (watchdog_need_worker(wdd)) {
long t = watchdog_next_keepalive(wdd);
if (t > 0)
mod_delayed_work(watchdog_wq, &wd_data->work, t);
} else {
cancel_delayed_work(&wd_data->work);
}
}
static int __watchdog_ping(struct watchdog_device *wdd)
{
struct watchdog_core_data *wd_data = wdd->wd_data;
unsigned long earliest_keepalive = wd_data->last_hw_keepalive +
msecs_to_jiffies(wdd->min_hw_heartbeat_ms);
int err;
if (time_is_after_jiffies(earliest_keepalive)) {
mod_delayed_work(watchdog_wq, &wd_data->work,
earliest_keepalive - jiffies);
return 0;
}
wd_data->last_hw_keepalive = jiffies;
if (wdd->ops->ping)
err = wdd->ops->ping(wdd); /* ping the watchdog */
else
err = wdd->ops->start(wdd); /* restart watchdog */
watchdog_update_worker(wdd);
return err;
}
/* /*
* watchdog_ping: ping the watchdog. * watchdog_ping: ping the watchdog.
* @wdd: the watchdog device to ping * @wdd: the watchdog device to ping
...@@ -85,17 +175,28 @@ static struct watchdog_core_data *old_wd_data; ...@@ -85,17 +175,28 @@ static struct watchdog_core_data *old_wd_data;
static int watchdog_ping(struct watchdog_device *wdd) static int watchdog_ping(struct watchdog_device *wdd)
{ {
int err; struct watchdog_core_data *wd_data = wdd->wd_data;
if (!watchdog_active(wdd)) if (!watchdog_active(wdd) && !watchdog_hw_running(wdd))
return 0; return 0;
if (wdd->ops->ping) wd_data->last_keepalive = jiffies;
err = wdd->ops->ping(wdd); /* ping the watchdog */ return __watchdog_ping(wdd);
else }
err = wdd->ops->start(wdd); /* restart watchdog */
return err; static void watchdog_ping_work(struct work_struct *work)
{
struct watchdog_core_data *wd_data;
struct watchdog_device *wdd;
wd_data = container_of(to_delayed_work(work), struct watchdog_core_data,
work);
mutex_lock(&wd_data->lock);
wdd = wd_data->wdd;
if (wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd)))
__watchdog_ping(wdd);
mutex_unlock(&wd_data->lock);
} }
/* /*
...@@ -111,14 +212,23 @@ static int watchdog_ping(struct watchdog_device *wdd) ...@@ -111,14 +212,23 @@ static int watchdog_ping(struct watchdog_device *wdd)
static int watchdog_start(struct watchdog_device *wdd) static int watchdog_start(struct watchdog_device *wdd)
{ {
struct watchdog_core_data *wd_data = wdd->wd_data;
unsigned long started_at;
int err; int err;
if (watchdog_active(wdd)) if (watchdog_active(wdd))
return 0; return 0;
err = wdd->ops->start(wdd); started_at = jiffies;
if (err == 0) if (watchdog_hw_running(wdd) && wdd->ops->ping)
err = wdd->ops->ping(wdd);
else
err = wdd->ops->start(wdd);
if (err == 0) {
set_bit(WDOG_ACTIVE, &wdd->status); set_bit(WDOG_ACTIVE, &wdd->status);
wd_data->last_keepalive = started_at;
watchdog_update_worker(wdd);
}
return err; return err;
} }
...@@ -137,7 +247,7 @@ static int watchdog_start(struct watchdog_device *wdd) ...@@ -137,7 +247,7 @@ static int watchdog_start(struct watchdog_device *wdd)
static int watchdog_stop(struct watchdog_device *wdd) static int watchdog_stop(struct watchdog_device *wdd)
{ {
int err; int err = 0;
if (!watchdog_active(wdd)) if (!watchdog_active(wdd))
return 0; return 0;
...@@ -148,9 +258,15 @@ static int watchdog_stop(struct watchdog_device *wdd) ...@@ -148,9 +258,15 @@ static int watchdog_stop(struct watchdog_device *wdd)
return -EBUSY; return -EBUSY;
} }
err = wdd->ops->stop(wdd); if (wdd->ops->stop)
if (err == 0) err = wdd->ops->stop(wdd);
else
set_bit(WDOG_HW_RUNNING, &wdd->status);
if (err == 0) {
clear_bit(WDOG_ACTIVE, &wdd->status); clear_bit(WDOG_ACTIVE, &wdd->status);
watchdog_update_worker(wdd);
}
return err; return err;
} }
...@@ -183,13 +299,22 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd) ...@@ -183,13 +299,22 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd)
static int watchdog_set_timeout(struct watchdog_device *wdd, static int watchdog_set_timeout(struct watchdog_device *wdd,
unsigned int timeout) unsigned int timeout)
{ {
if (!wdd->ops->set_timeout || !(wdd->info->options & WDIOF_SETTIMEOUT)) int err = 0;
if (!(wdd->info->options & WDIOF_SETTIMEOUT))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (watchdog_timeout_invalid(wdd, timeout)) if (watchdog_timeout_invalid(wdd, timeout))
return -EINVAL; return -EINVAL;
return wdd->ops->set_timeout(wdd, timeout); if (wdd->ops->set_timeout)
err = wdd->ops->set_timeout(wdd, timeout);
else
wdd->timeout = timeout;
watchdog_update_worker(wdd);
return err;
} }
/* /*
...@@ -538,7 +663,7 @@ static int watchdog_open(struct inode *inode, struct file *file) ...@@ -538,7 +663,7 @@ static int watchdog_open(struct inode *inode, struct file *file)
* If the /dev/watchdog device is open, we don't want the module * If the /dev/watchdog device is open, we don't want the module
* to be unloaded. * to be unloaded.
*/ */
if (!try_module_get(wdd->ops->owner)) { if (!watchdog_hw_running(wdd) && !try_module_get(wdd->ops->owner)) {
err = -EBUSY; err = -EBUSY;
goto out_clear; goto out_clear;
} }
...@@ -549,7 +674,8 @@ static int watchdog_open(struct inode *inode, struct file *file) ...@@ -549,7 +674,8 @@ static int watchdog_open(struct inode *inode, struct file *file)
file->private_data = wd_data; file->private_data = wd_data;
kref_get(&wd_data->kref); if (!watchdog_hw_running(wdd))
kref_get(&wd_data->kref);
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */ /* dev/watchdog is a virtual (and thus non-seekable) filesystem */
return nonseekable_open(inode, file); return nonseekable_open(inode, file);
...@@ -585,6 +711,7 @@ static int watchdog_release(struct inode *inode, struct file *file) ...@@ -585,6 +711,7 @@ static int watchdog_release(struct inode *inode, struct file *file)
struct watchdog_core_data *wd_data = file->private_data; struct watchdog_core_data *wd_data = file->private_data;
struct watchdog_device *wdd; struct watchdog_device *wdd;
int err = -EBUSY; int err = -EBUSY;
bool running;
mutex_lock(&wd_data->lock); mutex_lock(&wd_data->lock);
...@@ -609,14 +736,24 @@ static int watchdog_release(struct inode *inode, struct file *file) ...@@ -609,14 +736,24 @@ static int watchdog_release(struct inode *inode, struct file *file)
watchdog_ping(wdd); watchdog_ping(wdd);
} }
cancel_delayed_work_sync(&wd_data->work);
watchdog_update_worker(wdd);
/* make sure that /dev/watchdog can be re-opened */ /* make sure that /dev/watchdog can be re-opened */
clear_bit(_WDOG_DEV_OPEN, &wd_data->status); clear_bit(_WDOG_DEV_OPEN, &wd_data->status);
done: done:
running = wdd && watchdog_hw_running(wdd);
mutex_unlock(&wd_data->lock); mutex_unlock(&wd_data->lock);
/* Allow the owner module to be unloaded again */ /*
module_put(wd_data->cdev.owner); * Allow the owner module to be unloaded again unless the watchdog
kref_put(&wd_data->kref, watchdog_core_data_release); * is still running. If the watchdog is still running, it can not
* be stopped, and its driver must not be unloaded.
*/
if (!running) {
module_put(wd_data->cdev.owner);
kref_put(&wd_data->kref, watchdog_core_data_release);
}
return 0; return 0;
} }
...@@ -658,6 +795,11 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) ...@@ -658,6 +795,11 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
wd_data->wdd = wdd; wd_data->wdd = wdd;
wdd->wd_data = wd_data; wdd->wd_data = wd_data;
if (!watchdog_wq)
return -ENODEV;
INIT_DELAYED_WORK(&wd_data->work, watchdog_ping_work);
if (wdd->id == 0) { if (wdd->id == 0) {
old_wd_data = wd_data; old_wd_data = wd_data;
watchdog_miscdev.parent = wdd->parent; watchdog_miscdev.parent = wdd->parent;
...@@ -688,8 +830,23 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) ...@@ -688,8 +830,23 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
old_wd_data = NULL; old_wd_data = NULL;
kref_put(&wd_data->kref, watchdog_core_data_release); kref_put(&wd_data->kref, watchdog_core_data_release);
} }
return err;
} }
return err;
/* Record time of most recent heartbeat as 'just before now'. */
wd_data->last_hw_keepalive = jiffies - 1;
/*
* If the watchdog is running, prevent its driver from being unloaded,
* and schedule an immediate ping.
*/
if (watchdog_hw_running(wdd)) {
__module_get(wdd->ops->owner);
kref_get(&wd_data->kref);
queue_delayed_work(watchdog_wq, &wd_data->work, 0);
}
return 0;
} }
/* /*
...@@ -715,6 +872,8 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) ...@@ -715,6 +872,8 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
wdd->wd_data = NULL; wdd->wd_data = NULL;
mutex_unlock(&wd_data->lock); mutex_unlock(&wd_data->lock);
cancel_delayed_work_sync(&wd_data->work);
kref_put(&wd_data->kref, watchdog_core_data_release); kref_put(&wd_data->kref, watchdog_core_data_release);
} }
...@@ -780,6 +939,13 @@ int __init watchdog_dev_init(void) ...@@ -780,6 +939,13 @@ int __init watchdog_dev_init(void)
{ {
int err; int err;
watchdog_wq = alloc_workqueue("watchdogd",
WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
if (!watchdog_wq) {
pr_err("Failed to create watchdog workqueue\n");
return -ENOMEM;
}
err = class_register(&watchdog_class); err = class_register(&watchdog_class);
if (err < 0) { if (err < 0) {
pr_err("couldn't register class\n"); pr_err("couldn't register class\n");
...@@ -806,4 +972,5 @@ void __exit watchdog_dev_exit(void) ...@@ -806,4 +972,5 @@ void __exit watchdog_dev_exit(void)
{ {
unregister_chrdev_region(watchdog_devt, MAX_DOGS); unregister_chrdev_region(watchdog_devt, MAX_DOGS);
class_unregister(&watchdog_class); class_unregister(&watchdog_class);
destroy_workqueue(watchdog_wq);
} }
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
#define ZIIRAVE_STATE_OFF 0x1 #define ZIIRAVE_STATE_OFF 0x1
#define ZIIRAVE_STATE_ON 0x2 #define ZIIRAVE_STATE_ON 0x2
static char *ziirave_reasons[] = {"power cycle", "triggered", NULL, NULL, static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
"host request", NULL, "illegal configuration", "host request", NULL, "illegal configuration",
"illegal instruction", "illegal trap", "illegal instruction", "illegal trap",
"unknown"}; "unknown"};
......
...@@ -10,8 +10,9 @@ ...@@ -10,8 +10,9 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/device.h>
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <uapi/linux/watchdog.h> #include <uapi/linux/watchdog.h>
...@@ -46,7 +47,7 @@ struct watchdog_ops { ...@@ -46,7 +47,7 @@ struct watchdog_ops {
unsigned int (*status)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *);
int (*set_timeout)(struct watchdog_device *, unsigned int); int (*set_timeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *); unsigned int (*get_timeleft)(struct watchdog_device *);
int (*restart)(struct watchdog_device *); int (*restart)(struct watchdog_device *, unsigned long, void *);
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
}; };
...@@ -61,14 +62,21 @@ struct watchdog_ops { ...@@ -61,14 +62,21 @@ struct watchdog_ops {
* @bootstatus: Status of the watchdog device at boot. * @bootstatus: Status of the watchdog device at boot.
* @timeout: The watchdog devices timeout value (in seconds). * @timeout: The watchdog devices timeout value (in seconds).
* @min_timeout:The watchdog devices minimum timeout value (in seconds). * @min_timeout:The watchdog devices minimum timeout value (in seconds).
* @max_timeout:The watchdog devices maximum timeout value (in seconds). * @max_timeout:The watchdog devices maximum timeout value (in seconds)
* as configurable from user space. Only relevant if
* max_hw_heartbeat_ms is not provided.
* @min_hw_heartbeat_ms:
* Minimum time between heartbeats, in milli-seconds.
* @max_hw_heartbeat_ms:
* Hardware limit for maximum timeout, in milli-seconds.
* Replaces max_timeout if specified.
* @reboot_nb: The notifier block to stop watchdog on reboot. * @reboot_nb: The notifier block to stop watchdog on reboot.
* @restart_nb: The notifier block to register a restart function. * @restart_nb: The notifier block to register a restart function.
* @driver_data:Pointer to the drivers private data. * @driver_data:Pointer to the drivers private data.
* @wd_data: Pointer to watchdog core internal data. * @wd_data: Pointer to watchdog core internal data.
* @status: Field that contains the devices internal status bits. * @status: Field that contains the devices internal status bits.
* @deferred: entry in wtd_deferred_reg_list which is used to * @deferred: Entry in wtd_deferred_reg_list which is used to
* register early initialized watchdogs. * register early initialized watchdogs.
* *
* The watchdog_device structure contains all information about a * The watchdog_device structure contains all information about a
* watchdog timer device. * watchdog timer device.
...@@ -89,6 +97,8 @@ struct watchdog_device { ...@@ -89,6 +97,8 @@ struct watchdog_device {
unsigned int timeout; unsigned int timeout;
unsigned int min_timeout; unsigned int min_timeout;
unsigned int max_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 reboot_nb;
struct notifier_block restart_nb; struct notifier_block restart_nb;
void *driver_data; void *driver_data;
...@@ -98,6 +108,7 @@ struct watchdog_device { ...@@ -98,6 +108,7 @@ struct watchdog_device {
#define WDOG_ACTIVE 0 /* Is the watchdog running/active */ #define WDOG_ACTIVE 0 /* Is the watchdog running/active */
#define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */ #define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */
#define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ #define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */
#define WDOG_HW_RUNNING 3 /* True if HW watchdog running */
struct list_head deferred; struct list_head deferred;
}; };
...@@ -110,6 +121,15 @@ static inline bool watchdog_active(struct watchdog_device *wdd) ...@@ -110,6 +121,15 @@ static inline bool watchdog_active(struct watchdog_device *wdd)
return test_bit(WDOG_ACTIVE, &wdd->status); return test_bit(WDOG_ACTIVE, &wdd->status);
} }
/*
* Use the following function to check whether or not the hardware watchdog
* is running
*/
static inline bool watchdog_hw_running(struct watchdog_device *wdd)
{
return test_bit(WDOG_HW_RUNNING, &wdd->status);
}
/* Use the following function to set the nowayout feature */ /* Use the following function to set the nowayout feature */
static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout) static inline void watchdog_set_nowayout(struct watchdog_device *wdd, bool nowayout)
{ {
...@@ -128,13 +148,18 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne ...@@ -128,13 +148,18 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
{ {
/* /*
* The timeout is invalid if * The timeout is invalid if
* - the requested value is larger than UINT_MAX / 1000
* (since internal calculations are done in milli-seconds),
* or
* - the requested value is smaller than the configured minimum timeout, * - the requested value is smaller than the configured minimum timeout,
* or * or
* - a maximum timeout is configured, and the requested value is larger * - a maximum hardware timeout is not configured, a maximum timeout
* than the maximum timeout. * is configured, and the requested value is larger than the
* configured maximum timeout.
*/ */
return t < wdd->min_timeout || return t > UINT_MAX / 1000 || t < wdd->min_timeout ||
(wdd->max_timeout && t > wdd->max_timeout); (!wdd->max_hw_heartbeat_ms && wdd->max_timeout &&
t > wdd->max_timeout);
} }
/* Use the following functions to manipulate watchdog driver specific data */ /* Use the following functions to manipulate watchdog driver specific data */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment