Merge git://www.linux-watchdog.org/linux-watchdog
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 20 Mar 2016 02:35:51 +0000 (19:35 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 20 Mar 2016 02:35:51 +0000 (19:35 -0700)
Pull watchdog updates from Wim Van Sebroeck:

 - new drivers for: NI 903x/913x watchdog driver, WinSystems EBC-C384
   watchdog timer and ARM SBSA watchdog driver

 - Support for NCT6102D devices

 - Improvements of the generic watchdog framework (improve restart
   handler, make set_timeout optional, introduce infrastructure
   triggered keepalives, ...

 - improvements on the pnx4008 watchdog driver

 - several smaller fixes and improvements

* git://www.linux-watchdog.org/linux-watchdog: (28 commits)
  watchdog: Ensure that wdd is not dereferenced if NULL
  watchdog: imx2: Convert to use infrastructure triggered keepalives
  watchdog: dw_wdt: Convert to use watchdog infrastructure
  watchdog: Add support for minimum time between heartbeats
  watchdog: Make stop function optional
  watchdog: Introduce WDOG_HW_RUNNING flag
  watchdog: Introduce hardware maximum heartbeat in watchdog core
  watchdog: Make set_timeout function optional
  arm: lpc32xx: remove restart handler
  arm: lpc32xx: phy3250 remove restart hook
  watchdog: pnx4008: restart: support "cmd" from userspace
  watchdog: pnx4008: add support for soft reset
  watchdog: pnx4008: add restart handler
  watchdog: pnx4008: update logging during power-on
  watchdog: tangox_wdt: test clock rate to avoid division by 0
  watchdog: atlas7_wdt: test clock rate to avoid division by 0
  watchdog: s3c2410_wdt: Add max and min timeout values
  Watchdog: introduce ARM SBSA watchdog driver
  Documentation: add sbsa-gwdt driver documentation
  watchdog: Add watchdog timer support for the WinSystems EBC-C384
  ...

34 files changed:
Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt [new file with mode: 0644]
Documentation/watchdog/watchdog-kernel-api.txt
Documentation/watchdog/watchdog-parameters.txt
MAINTAINERS
arch/arm/mach-lpc32xx/common.c
arch/arm/mach-lpc32xx/common.h
arch/arm/mach-lpc32xx/phy3250.c
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/atlas7_wdt.c
drivers/watchdog/bcm47xx_wdt.c
drivers/watchdog/da9063_wdt.c
drivers/watchdog/digicolor_wdt.c
drivers/watchdog/dw_wdt.c
drivers/watchdog/ebc-c384_wdt.c [new file with mode: 0644]
drivers/watchdog/imgpdc_wdt.c
drivers/watchdog/imx2_wdt.c
drivers/watchdog/lpc18xx_wdt.c
drivers/watchdog/meson_wdt.c
drivers/watchdog/moxart_wdt.c
drivers/watchdog/mtk_wdt.c
drivers/watchdog/ni903x_wdt.c [new file with mode: 0644]
drivers/watchdog/pnx4008_wdt.c
drivers/watchdog/qcom-wdt.c
drivers/watchdog/rc32434_wdt.c
drivers/watchdog/s3c2410_wdt.c
drivers/watchdog/sbsa_gwdt.c [new file with mode: 0644]
drivers/watchdog/sunxi_wdt.c
drivers/watchdog/tangox_wdt.c
drivers/watchdog/w83627hf_wdt.c
drivers/watchdog/watchdog_core.c
drivers/watchdog/watchdog_dev.c
drivers/watchdog/ziirave_wdt.c
include/linux/watchdog.h

diff --git a/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt b/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt
new file mode 100644 (file)
index 0000000..6f2d5f9
--- /dev/null
@@ -0,0 +1,31 @@
+* SBSA (Server Base System Architecture) Generic Watchdog
+
+The SBSA Generic Watchdog Timer is used to force a reset of the system
+after two stages of timeout have elapsed.  A detailed definition of the
+watchdog timer can be found in the ARM document: ARM-DEN-0029 - Server
+Base System Architecture (SBSA)
+
+Required properties:
+- compatible: Should at least contain "arm,sbsa-gwdt".
+
+- reg: Each entry specifies the base physical address of a register frame
+  and the length of that frame; currently, two frames must be defined,
+  in this order:
+  1: Watchdog control frame;
+  2: Refresh frame.
+
+- interrupts: Should contain the Watchdog Signal 0 (WS0) SPI (Shared
+  Peripheral Interrupt) number of SBSA Generic Watchdog.
+
+Optional properties
+- timeout-sec: Watchdog timeout values (in seconds).
+
+Example for FVP Foundation Model v8:
+
+watchdog@2a440000 {
+       compatible = "arm,sbsa-gwdt";
+       reg = <0x0 0x2a440000 0 0x1000>,
+             <0x0 0x2a450000 0 0x1000>;
+       interrupts = <0 27 4>;
+       timeout-sec = <30>;
+};
index 55120a055a14d57f98053cb7147b25ad4c2a6214..917eeeabfa5eaefd3396c66e4b1032dd49a6f2b4 100644 (file)
@@ -52,6 +52,8 @@ struct watchdog_device {
        unsigned int timeout;
        unsigned int min_timeout;
        unsigned int max_timeout;
+       unsigned int min_hw_heartbeat_ms;
+       unsigned int max_hw_heartbeat_ms;
        struct notifier_block reboot_nb;
        struct notifier_block restart_nb;
        void *driver_data;
@@ -73,8 +75,21 @@ It contains following fields:
   additional information about the watchdog timer itself. (Like it's unique name)
 * ops: a pointer to the list of watchdog operations that the watchdog supports.
 * timeout: the watchdog timer's timeout value (in seconds).
+  This is the time after which the system will reboot if user space does
+  not send a heartbeat request if WDOG_ACTIVE is set.
 * min_timeout: the watchdog timer's minimum timeout value (in seconds).
-* max_timeout: the watchdog timer's maximum timeout value (in seconds).
+  If set, the minimum configurable value for 'timeout'.
+* max_timeout: the watchdog timer's maximum timeout value (in seconds),
+  as seen from userspace. If set, the maximum configurable value for
+  'timeout'. Not used if max_hw_heartbeat_ms is non-zero.
+* min_hw_heartbeat_ms: Minimum time between heartbeats sent to the chip,
+  in milli-seconds.
+* max_hw_heartbeat_ms: Maximum hardware heartbeat, in milli-seconds.
+  If set, the infrastructure will send heartbeats to the watchdog driver
+  if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE
+  is set and userspace failed to send a heartbeat for at least 'timeout'
+  seconds. max_hw_heartbeat_ms must be set if a driver does not implement
+  the stop function.
 * reboot_nb: notifier block that is registered for reboot notifications, for
   internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core
   will stop the watchdog on such notifications.
@@ -123,17 +138,20 @@ are:
   device.
   The routine needs a pointer to the watchdog timer device structure as a
   parameter. It returns zero on success or a negative errno code for failure.
-* stop: with this routine the watchdog timer device is being stopped.
-  The routine needs a pointer to the watchdog timer device structure as a
-  parameter. It returns zero on success or a negative errno code for failure.
-  Some watchdog timer hardware can only be started and not be stopped. The
-  driver supporting this hardware needs to make sure that a start and stop
-  routine is being provided. This can be done by using a timer in the driver
-  that regularly sends a keepalive ping to the watchdog timer hardware.
 
 Not all watchdog timer hardware supports the same functionality. That's why
 all other routines/operations are optional. They only need to be provided if
 they are supported. These optional routines/operations are:
+* stop: with this routine the watchdog timer device is being stopped.
+  The routine needs a pointer to the watchdog timer device structure as a
+  parameter. It returns zero on success or a negative errno code for failure.
+  Some watchdog timer hardware can only be started and not be stopped. A
+  driver supporting such hardware does not have to implement the stop routine.
+  If a driver has no stop function, the watchdog core will set WDOG_HW_RUNNING
+  and start calling the driver's keepalive pings function after the watchdog
+  device is closed.
+  If a watchdog driver does not implement the stop function, it must set
+  max_hw_heartbeat_ms.
 * ping: this is the routine that sends a keepalive ping to the watchdog timer
   hardware.
   The routine needs a pointer to the watchdog timer device structure as a
@@ -153,9 +171,18 @@ they are supported. These optional routines/operations are:
   and -EIO for "could not write value to the watchdog". On success this
   routine should set the timeout value of the watchdog_device to the
   achieved timeout value (which may be different from the requested one
-  because the watchdog does not necessarily has a 1 second resolution).
+  because the watchdog does not necessarily have a 1 second resolution).
+  Drivers implementing max_hw_heartbeat_ms set the hardware watchdog heartbeat
+  to the minimum of timeout and max_hw_heartbeat_ms. Those drivers set the
+  timeout value of the watchdog_device either to the requested timeout value
+  (if it is larger than max_hw_heartbeat_ms), or to the achieved timeout value.
   (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the
   watchdog's info structure).
+  If the watchdog driver does not have to perform any action but setting the
+  watchdog_device.timeout, this callback can be omitted.
+  If set_timeout is not provided but, WDIOF_SETTIMEOUT is set, the watchdog
+  infrastructure updates the timeout value of the watchdog_device internally
+  to the requested value.
 * get_timeleft: this routines returns the time that's left before a reset.
 * restart: this routine restarts the machine. It returns 0 on success or a
   negative errno code for failure.
@@ -169,11 +196,19 @@ The 'ref' and 'unref' operations are no longer used and deprecated.
 The status bits should (preferably) be set with the set_bit and clear_bit alike
 bit-operations. The status bits that are defined are:
 * WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device
-  is active or not. When the watchdog is active after booting, then you should
-  set this status bit (Note: when you register the watchdog timer device with
-  this bit set, then opening /dev/watchdog will skip the start operation)
+  is active or not from user perspective. User space is expected to send
+  heartbeat requests to the driver while this flag is set.
 * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog.
   If this bit is set then the watchdog timer will not be able to stop.
+* WDOG_HW_RUNNING: Set by the watchdog driver if the hardware watchdog is
+  running. The bit must be set if the watchdog timer hardware can not be
+  stopped. The bit may also be set if the watchdog timer is running after
+  booting, before the watchdog device is opened. If set, the watchdog
+  infrastructure will send keepalives to the watchdog hardware while
+  WDOG_ACTIVE is not set.
+  Note: when you register the watchdog timer device with this bit set,
+  then opening /dev/watchdog will skip the start operation but send a keepalive
+  request instead.
 
   To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog
   timer device) you can either:
index 4e4b6f10d8410f84d8ea64ac51c68a6932bf2158..c161399a6b5c134932512b6ae74e6e1676f5051c 100644 (file)
@@ -200,6 +200,11 @@ mv64x60_wdt:
 nowayout: Watchdog cannot be stopped once started
        (default=kernel config parameter)
 -------------------------------------------------
+ni903x_wdt:
+timeout: Initial watchdog timeout in seconds (0<timeout<516, default=60)
+nowayout: Watchdog cannot be stopped once started
+       (default=kernel config parameter)
+-------------------------------------------------
 nuc900_wdt:
 heartbeat: Watchdog heartbeats in seconds.
        (default = 15)
@@ -284,6 +289,13 @@ sbc_fitpc2_wdt:
 margin: Watchdog margin in seconds (default 60s)
 nowayout: Watchdog cannot be stopped once started
 -------------------------------------------------
+sbsa_gwdt:
+timeout: Watchdog timeout in seconds. (default 10s)
+action: Watchdog action at the first stage timeout,
+       set to 0 to ignore, 1 to panic. (default=0)
+nowayout: Watchdog cannot be stopped once started
+       (default=kernel config parameter)
+-------------------------------------------------
 sc1200wdt:
 isapnp: When set to 0 driver ISA PnP support will be disabled (default=1)
 io: io port
index 0cbfc69a2303893862e635d7416a9f7d1478c866..606528bb16afc9ae180a299efb9346ddf4c06cbd 100644 (file)
@@ -11965,6 +11965,12 @@ M:     David Härdeman <david@hardeman.nu>
 S:     Maintained
 F:     drivers/media/rc/winbond-cir.c
 
+WINSYSTEMS EBC-C384 WATCHDOG DRIVER
+M:     William Breathitt Gray <vilhelm.gray@gmail.com>
+L:     linux-watchdog@vger.kernel.org
+S:     Maintained
+F:     drivers/watchdog/ebc-c384_wdt.c
+
 WINSYSTEMS WS16C48 GPIO DRIVER
 M:     William Breathitt Gray <vilhelm.gray@gmail.com>
 L:     linux-gpio@vger.kernel.org
index 716e83eb1db8ec1fe31bce19c318228ee2ce515b..5b7a1e78c3a5ed37e24506585d1d53d3551b5d82 100644 (file)
@@ -194,21 +194,6 @@ void __init lpc32xx_map_io(void)
        iotable_init(lpc32xx_io_desc, ARRAY_SIZE(lpc32xx_io_desc));
 }
 
-void lpc23xx_restart(enum reboot_mode mode, const char *cmd)
-{
-       /* Make sure WDT clocks are enabled */
-       __raw_writel(LPC32XX_CLKPWR_PWMCLK_WDOG_EN,
-               LPC32XX_CLKPWR_TIMER_CLK_CTRL);
-
-       /* Instant assert of RESETOUT_N with pulse length 1mS */
-       __raw_writel(13000, io_p2v(LPC32XX_WDTIM_BASE + 0x18));
-       __raw_writel(0x70, io_p2v(LPC32XX_WDTIM_BASE + 0xC));
-
-       /* Wait for watchdog to reset system */
-       while (1)
-               ;
-}
-
 static int __init lpc32xx_check_uid(void)
 {
        u32 uid[4];
index 1cd8853b2f9b7479a79ad54d52eb43c78cd9e511..2d90801ed1e13c19dfd8c867b535c324b1f81909 100644 (file)
@@ -30,7 +30,6 @@ extern void lpc32xx_timer_init(void);
 extern void __init lpc32xx_init_irq(void);
 extern void __init lpc32xx_map_io(void);
 extern void __init lpc32xx_serial_init(void);
-extern void lpc23xx_restart(enum reboot_mode, const char *);
 
 
 /*
index ee06fabdf60e69e014317c9703787da5f31dc79f..e6f03783ad7385d7cb9b899650e7a62aa3f7ac3b 100644 (file)
@@ -262,5 +262,4 @@ DT_MACHINE_START(LPC32XX_DT, "LPC32XX SoC (Flattened Device Tree)")
        .init_time      = lpc32xx_timer_init,
        .init_machine   = lpc3250_machine_init,
        .dt_compat      = lpc32xx_dt_compat,
-       .restart        = lpc23xx_restart,
 MACHINE_END
index 9289da313d985f434f7c350a88db97321437372e..fb947655badd02de231dd0777e88499c8a552044 100644 (file)
@@ -202,6 +202,26 @@ config ARM_SP805_WATCHDOG
          ARM Primecell SP805 Watchdog timer. This will reboot your system when
          the timeout is reached.
 
+config ARM_SBSA_WATCHDOG
+       tristate "ARM SBSA Generic Watchdog"
+       depends on ARM64
+       depends on ARM_ARCH_TIMER
+       select WATCHDOG_CORE
+       help
+         ARM SBSA Generic Watchdog has two stage timeouts:
+         the first signal (WS0) is for alerting the system by interrupt,
+         the second one (WS1) is a real hardware reset.
+         More details: ARM DEN0029B - Server Base System Architecture (SBSA)
+
+         This driver can operate ARM SBSA Generic Watchdog as a single stage
+         or a two stages watchdog, it depends on the module parameter "action".
+
+         Note: the maximum timeout in the two stages mode is half of that in
+         the single stage mode.
+
+         To compile this driver as module, choose M here: The module
+         will be called sbsa_gwdt.
+
 config ASM9260_WATCHDOG
        tristate "Alphascale ASM9260 watchdog"
        depends on MACH_ASM9260
@@ -330,6 +350,7 @@ config SA1100_WATCHDOG
 config DW_WATCHDOG
        tristate "Synopsys DesignWare watchdog"
        depends on HAS_IOMEM
+       select WATCHDOG_CORE
        help
          Say Y here if to include support for the Synopsys DesignWare
          watchdog timer found in many chips.
@@ -399,6 +420,7 @@ config DAVINCI_WATCHDOG
 config ORION_WATCHDOG
        tristate "Orion watchdog"
        depends on ARCH_ORION5X || ARCH_DOVE || MACH_DOVE || ARCH_MVEBU
+       depends on ARM
        select WATCHDOG_CORE
        help
          Say Y here if to include support for the watchdog timer
@@ -468,6 +490,7 @@ config NUC900_WATCHDOG
 config TS4800_WATCHDOG
        tristate "TS-4800 Watchdog"
        depends on HAS_IOMEM && OF
+       depends on SOC_IMX51 || COMPILE_TEST
        select WATCHDOG_CORE
        select MFD_SYSCON
        help
@@ -713,6 +736,15 @@ config ALIM7101_WDT
 
          Most people will say N.
 
+config EBC_C384_WDT
+       tristate "WinSystems EBC-C384 Watchdog Timer"
+       depends on X86
+       select WATCHDOG_CORE
+       help
+         Enables watchdog timer support for the watchdog timer on the
+         WinSystems EBC-C384 motherboard. The timeout may be configured via
+         the timeout module parameter.
+
 config F71808E_WDT
        tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog"
        depends on X86
@@ -1142,6 +1174,7 @@ config W83627HF_WDT
                NCT6779
                NCT6791
                NCT6792
+               NCT6102D/04D/06D
 
          This watchdog simply watches your kernel to make sure it doesn't
          freeze, and if it does, it reboots your computer after a certain
@@ -1229,6 +1262,17 @@ config INTEL_MEI_WDT
          To compile this driver as a module, choose M here:
          the module will be called mei_wdt.
 
+config NI903X_WDT
+       tristate "NI 903x/913x Watchdog"
+       depends on X86 && ACPI
+       select WATCHDOG_CORE
+       ---help---
+         This is the driver for the watchdog timer on the National Instruments
+         903x/913x real-time controllers.
+
+         To compile this driver as a module, choose M here: the module will be
+         called ni903x_wdt.
+
 # M32R Architecture
 
 # M68K Architecture
@@ -1392,10 +1436,12 @@ config BCM7038_WDT
        tristate "BCM7038 Watchdog"
        select WATCHDOG_CORE
        depends on HAS_IOMEM
+       depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
        help
-        Watchdog driver for the built-in hardware in Broadcom 7038 SoCs.
-
-        Say 'Y or 'M' here to enable the driver.
+        Watchdog driver for the built-in hardware in Broadcom 7038 and
+        later SoCs used in set-top boxes.  BCM7038 was made public
+        during the 2004 CES, and since then, many Broadcom chips use this
+        watchdog block, including some cable modem chips.
 
 config IMGPDC_WDT
        tristate "Imagination Technologies PDC Watchdog Timer"
index 14bd772d3e66673cfcb2a495a9fe41863d0faf00..feb6270fdbde5f4d19eb133f52d706e34b6b06f9 100644 (file)
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
 
 # ARM Architecture
 obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
+obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o
 obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
 obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
 obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
@@ -88,6 +89,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
 obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
 obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
 obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
+obj-$(CONFIG_EBC_C384_WDT) += ebc-c384_wdt.o
 obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
 obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o
 obj-$(CONFIG_GEODE_WDT) += geodewdt.o
@@ -127,6 +129,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
 obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
 obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
 obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o
+obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o
 
 # M32R Architecture
 
index df6d9242a319587b3820b3fa621c7aa0d65ef42c..ed80734befae16ea294b97fa6f1bfc5249a5d6bc 100644 (file)
@@ -154,6 +154,11 @@ static int atlas7_wdt_probe(struct platform_device *pdev)
        writel(0, wdt->base + ATLAS7_WDT_CNT_CTRL);
 
        wdt->tick_rate = clk_get_rate(clk);
+       if (!wdt->tick_rate) {
+               ret = -EINVAL;
+               goto err1;
+       }
+
        wdt->clk = clk;
        atlas7_wdd.min_timeout = 1;
        atlas7_wdd.max_timeout = UINT_MAX / wdt->tick_rate;
index df1c2a4b01650a182fac16cfe2fef1e2a2c778ea..a1900b9ab6c4e651b799f39b6c0f80ae62657d05 100644 (file)
@@ -87,7 +87,8 @@ static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd,
        return 0;
 }
 
-static int bcm47xx_wdt_restart(struct watchdog_device *wdd)
+static int bcm47xx_wdt_restart(struct watchdog_device *wdd,
+                              unsigned long action, void *data)
 {
        struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
 
index 11e887572649a00539506a6faf8dcc012eeac43c..a100f648880d85315e4354176c890706a38e58cb 100644 (file)
@@ -119,7 +119,8 @@ static int da9063_wdt_set_timeout(struct watchdog_device *wdd,
        return ret;
 }
 
-static int da9063_wdt_restart(struct watchdog_device *wdd)
+static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action,
+                             void *data)
 {
        struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
        int ret;
index 1ccb0b239348d953b50e4c717a360cbde8945091..77df772406b084b93104f13221bdc248d7317bef 100644 (file)
@@ -48,7 +48,8 @@ static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks)
        spin_unlock_irqrestore(&wdt->lock, flags);
 }
 
-static int dc_wdt_restart(struct watchdog_device *wdog)
+static int dc_wdt_restart(struct watchdog_device *wdog, unsigned long action,
+                         void *data)
 {
        struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
 
index 8fefa4ad46d4d0a5fa928bf3c5694388b473e91b..2acb51cf55041dc2b71e677e9820d885217bdf69 100644 (file)
@@ -12,9 +12,8 @@
  * and these are a function of the input clock frequency.
  *
  * The DesignWare watchdog cannot be stopped once it has been started so we
- * use a software timer to implement a ping that will keep the watchdog alive.
- * If we receive an expected close for the watchdog then we keep the timer
- * running, otherwise the timer is stopped and the watchdog will expire.
+ * do not implement a stop function. The watchdog core will continue to send
+ * heartbeat requests after the watchdog device has been closed.
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <linux/bitops.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
-#include <linux/device.h>
 #include <linux/err.h>
-#include <linux/fs.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/notifier.h>
@@ -35,8 +31,6 @@
 #include <linux/pm.h>
 #include <linux/platform_device.h>
 #include <linux/reboot.h>
-#include <linux/timer.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
 
 #define WDOG_CONTROL_REG_OFFSET                    0x00
@@ -57,53 +51,50 @@ module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
                 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
-#define WDT_TIMEOUT            (HZ / 2)
-
-static struct {
+struct dw_wdt {
        void __iomem            *regs;
        struct clk              *clk;
-       unsigned long           in_use;
-       unsigned long           next_heartbeat;
-       struct timer_list       timer;
-       int                     expect_close;
        struct notifier_block   restart_handler;
-} dw_wdt;
+       struct watchdog_device  wdd;
+};
+
+#define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd)
 
-static inline int dw_wdt_is_enabled(void)
+static inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt)
 {
-       return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) &
+       return readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET) &
                WDOG_CONTROL_REG_WDT_EN_MASK;
 }
 
-static inline int dw_wdt_top_in_seconds(unsigned top)
+static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top)
 {
        /*
         * There are 16 possible timeout values in 0..15 where the number of
         * cycles is 2 ^ (16 + i) and the watchdog counts down.
         */
-       return (1U << (16 + top)) / clk_get_rate(dw_wdt.clk);
+       return (1U << (16 + top)) / clk_get_rate(dw_wdt->clk);
 }
 
-static int dw_wdt_get_top(void)
+static int dw_wdt_get_top(struct dw_wdt *dw_wdt)
 {
-       int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
+       int top = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
 
-       return dw_wdt_top_in_seconds(top);
+       return dw_wdt_top_in_seconds(dw_wdt, top);
 }
 
-static inline void dw_wdt_set_next_heartbeat(void)
+static int dw_wdt_ping(struct watchdog_device *wdd)
 {
-       dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ;
-}
+       struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
 
-static void dw_wdt_keepalive(void)
-{
-       writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs +
+       writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs +
               WDOG_COUNTER_RESTART_REG_OFFSET);
+
+       return 0;
 }
 
-static int dw_wdt_set_top(unsigned top_s)
+static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s)
 {
+       struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
        int i, top_val = DW_WDT_MAX_TOP;
 
        /*
@@ -111,7 +102,7 @@ static int dw_wdt_set_top(unsigned top_s)
         * always look for >=.
         */
        for (i = 0; i <= DW_WDT_MAX_TOP; ++i)
-               if (dw_wdt_top_in_seconds(i) >= top_s) {
+               if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) {
                        top_val = i;
                        break;
                }
@@ -123,33 +114,43 @@ static int dw_wdt_set_top(unsigned top_s)
         * effectively get a pat of the watchdog right here.
         */
        writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT,
-               dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
+              dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
 
-       /*
-        * Add an explicit pat to handle versions of the watchdog that
-        * don't have TOPINIT.  This won't hurt on versions that have
-        * it.
-        */
-       dw_wdt_keepalive();
+       wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val);
 
-       dw_wdt_set_next_heartbeat();
+       return 0;
+}
+
+static int dw_wdt_start(struct watchdog_device *wdd)
+{
+       struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
+
+       dw_wdt_set_timeout(wdd, wdd->timeout);
 
-       return dw_wdt_top_in_seconds(top_val);
+       set_bit(WDOG_HW_RUNNING, &wdd->status);
+
+       writel(WDOG_CONTROL_REG_WDT_EN_MASK,
+              dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
+
+       return 0;
 }
 
 static int dw_wdt_restart_handle(struct notifier_block *this,
-                               unsigned long mode, void *cmd)
+                                unsigned long mode, void *cmd)
 {
+       struct dw_wdt *dw_wdt;
        u32 val;
 
-       writel(0, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
-       val = readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
+       dw_wdt = container_of(this, struct dw_wdt, restart_handler);
+
+       writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
+       val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
        if (val & WDOG_CONTROL_REG_WDT_EN_MASK)
-               writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs +
-                       WDOG_COUNTER_RESTART_REG_OFFSET);
+               writel(WDOG_COUNTER_RESTART_KICK_VALUE,
+                      dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET);
        else
                writel(WDOG_CONTROL_REG_WDT_EN_MASK,
-                      dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
+                      dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
 
        /* wait for reset to assert... */
        mdelay(500);
@@ -157,74 +158,12 @@ static int dw_wdt_restart_handle(struct notifier_block *this,
        return NOTIFY_DONE;
 }
 
-static void dw_wdt_ping(unsigned long data)
+static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd)
 {
-       if (time_before(jiffies, dw_wdt.next_heartbeat) ||
-           (!nowayout && !dw_wdt.in_use)) {
-               dw_wdt_keepalive();
-               mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
-       } else
-               pr_crit("keepalive missed, machine will reset\n");
-}
-
-static int dw_wdt_open(struct inode *inode, struct file *filp)
-{
-       if (test_and_set_bit(0, &dw_wdt.in_use))
-               return -EBUSY;
-
-       /* Make sure we don't get unloaded. */
-       __module_get(THIS_MODULE);
-
-       if (!dw_wdt_is_enabled()) {
-               /*
-                * The watchdog is not currently enabled. Set the timeout to
-                * something reasonable and then start it.
-                */
-               dw_wdt_set_top(DW_WDT_DEFAULT_SECONDS);
-               writel(WDOG_CONTROL_REG_WDT_EN_MASK,
-                      dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
-       }
-
-       dw_wdt_set_next_heartbeat();
-
-       return nonseekable_open(inode, filp);
-}
-
-static ssize_t dw_wdt_write(struct file *filp, const char __user *buf,
-                           size_t len, loff_t *offset)
-{
-       if (!len)
-               return 0;
-
-       if (!nowayout) {
-               size_t i;
-
-               dw_wdt.expect_close = 0;
-
-               for (i = 0; i < len; ++i) {
-                       char c;
-
-                       if (get_user(c, buf + i))
-                               return -EFAULT;
-
-                       if (c == 'V') {
-                               dw_wdt.expect_close = 1;
-                               break;
-                       }
-               }
-       }
+       struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
 
-       dw_wdt_set_next_heartbeat();
-       dw_wdt_keepalive();
-       mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
-
-       return len;
-}
-
-static u32 dw_wdt_time_left(void)
-{
-       return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
-               clk_get_rate(dw_wdt.clk);
+       return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
+               clk_get_rate(dw_wdt->clk);
 }
 
 static const struct watchdog_info dw_wdt_ident = {
@@ -233,78 +172,33 @@ static const struct watchdog_info dw_wdt_ident = {
        .identity       = "Synopsys DesignWare Watchdog",
 };
 
-static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
-       unsigned long val;
-       int timeout;
-
-       switch (cmd) {
-       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;
-}
+static const struct watchdog_ops dw_wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = dw_wdt_start,
+       .ping           = dw_wdt_ping,
+       .set_timeout    = dw_wdt_set_timeout,
+       .get_timeleft   = dw_wdt_get_timeleft,
+};
 
 #ifdef CONFIG_PM_SLEEP
 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;
 }
 
 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)
                return err;
 
-       dw_wdt_keepalive();
+       dw_wdt_ping(&dw_wdt->wdd);
 
        return 0;
 }
@@ -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 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)
 {
+       struct device *dev = &pdev->dev;
+       struct watchdog_device *wdd;
+       struct dw_wdt *dw_wdt;
+       struct resource *mem;
        int ret;
-       struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
-       dw_wdt.regs = devm_ioremap_resource(&pdev->dev, mem);
-       if (IS_ERR(dw_wdt.regs))
-               return PTR_ERR(dw_wdt.regs);
+       dw_wdt = devm_kzalloc(dev, sizeof(*dw_wdt), GFP_KERNEL);
+       if (!dw_wdt)
+               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);
-       if (IS_ERR(dw_wdt.clk))
-               return PTR_ERR(dw_wdt.clk);
+       dw_wdt->clk = devm_clk_get(dev, NULL);
+       if (IS_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)
                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)
                goto out_disable_clk;
 
-       dw_wdt.restart_handler.notifier_call = dw_wdt_restart_handle;
-       dw_wdt.restart_handler.priority = 128;
-       ret = register_restart_handler(&dw_wdt.restart_handler);
+       dw_wdt->restart_handler.notifier_call = dw_wdt_restart_handle;
+       dw_wdt->restart_handler.priority = 128;
+       ret = register_restart_handler(&dw_wdt->restart_handler);
        if (ret)
                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;
 
 out_disable_clk:
-       clk_disable_unprepare(dw_wdt.clk);
-
+       clk_disable_unprepare(dw_wdt->clk);
        return ret;
 }
 
 static int dw_wdt_drv_remove(struct platform_device *pdev)
 {
-       unregister_restart_handler(&dw_wdt.restart_handler);
-
-       misc_deregister(&dw_wdt_miscdev);
+       struct dw_wdt *dw_wdt = platform_get_drvdata(pdev);
 
-       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;
 }
diff --git a/drivers/watchdog/ebc-c384_wdt.c b/drivers/watchdog/ebc-c384_wdt.c
new file mode 100644 (file)
index 0000000..77fda0b
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * 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);
index 3679f2e1922f559000e3b91a53a783a3400afac1..516fbef00856edeceb59fba31968c1c2328bd24e 100644 (file)
@@ -150,7 +150,8 @@ static int pdc_wdt_start(struct watchdog_device *wdt_dev)
        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);
 
index e47966aa2db0eb4d2caa22217fd821988b141967..331aed831dac8419198d78e77b895b2d5ec883e0 100644 (file)
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/io.h>
-#include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
-#include <linux/timer.h>
 #include <linux/watchdog.h>
 
 #define DRIVER_NAME "imx2-wdt"
@@ -60,7 +58,6 @@
 struct imx2_wdt_device {
        struct clk *clk;
        struct regmap *regmap;
-       struct timer_list timer;        /* Pings the watchdog when closed */
        struct watchdog_device wdog;
 };
 
@@ -80,7 +77,8 @@ static const struct watchdog_info imx2_wdt_info = {
        .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);
        unsigned int wcr_enable = IMX2_WDT_WCR_WDE;
@@ -146,16 +144,6 @@ static int imx2_wdt_ping(struct watchdog_device *wdog)
        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,
                                unsigned int new_timeout)
 {
@@ -172,40 +160,19 @@ static int imx2_wdt_start(struct watchdog_device *wdog)
 {
        struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
 
-       if (imx2_wdt_is_running(wdev)) {
-               /* delete the timer that pings the watchdog after close */
-               del_timer_sync(&wdev->timer);
+       if (imx2_wdt_is_running(wdev))
                imx2_wdt_set_timeout(wdog, wdog->timeout);
-       else
+       else
                imx2_wdt_setup(wdog);
 
-       return imx2_wdt_ping(wdog);
-}
-
-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);
+       set_bit(WDOG_HW_RUNNING, &wdog->status);
 
-       if (imx2_wdt_is_running(wdev)) {
-               imx2_wdt_set_timeout(wdog, wdog->timeout);
-               imx2_wdt_timer_ping((unsigned long)wdog);
-       }
+       return imx2_wdt_ping(wdog);
 }
 
 static const struct watchdog_ops imx2_wdt_ops = {
        .owner = THIS_MODULE,
        .start = imx2_wdt_start,
-       .stop = imx2_wdt_stop,
        .ping = imx2_wdt_ping,
        .set_timeout = imx2_wdt_set_timeout,
        .restart = imx2_wdt_restart,
@@ -253,7 +220,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
        wdog->info              = &imx2_wdt_info;
        wdog->ops               = &imx2_wdt_ops;
        wdog->min_timeout       = 1;
-       wdog->max_timeout       = IMX2_WDT_MAX_TIME;
+       wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000;
        wdog->parent            = &pdev->dev;
 
        ret = clk_prepare_enable(wdev->clk);
@@ -274,9 +241,10 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
        watchdog_set_restart_priority(wdog, 128);
        watchdog_init_timeout(wdog, timeout, &pdev->dev);
 
-       setup_timer(&wdev->timer, imx2_wdt_timer_ping, (unsigned long)wdog);
-
-       imx2_wdt_ping_if_active(wdog);
+       if (imx2_wdt_is_running(wdev)) {
+               imx2_wdt_set_timeout(wdog, wdog->timeout);
+               set_bit(WDOG_HW_RUNNING, &wdog->status);
+       }
 
        /*
         * 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)
        watchdog_unregister_device(wdog);
 
        if (imx2_wdt_is_running(wdev)) {
-               del_timer_sync(&wdev->timer);
                imx2_wdt_ping(wdog);
                dev_crit(&pdev->dev, "Device removed: Expect reboot!\n");
        }
@@ -323,10 +290,9 @@ static void imx2_wdt_shutdown(struct platform_device *pdev)
 
        if (imx2_wdt_is_running(wdev)) {
                /*
-                * We are running, we need to delete the timer but will
-                * give max timeout before reboot will take place
+                * We are running, configure max timeout before reboot
+                * will take place.
                 */
-               del_timer_sync(&wdev->timer);
                imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
                imx2_wdt_ping(wdog);
                dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n");
@@ -344,10 +310,6 @@ static int imx2_wdt_suspend(struct device *dev)
        if (imx2_wdt_is_running(wdev)) {
                imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
                imx2_wdt_ping(wdog);
-
-               /* The watchdog is not active */
-               if (!watchdog_active(wdog))
-                       del_timer_sync(&wdev->timer);
        }
 
        clk_disable_unprepare(wdev->clk);
@@ -373,19 +335,10 @@ static int imx2_wdt_resume(struct device *dev)
                 * watchdog again.
                 */
                imx2_wdt_setup(wdog);
+       }
+       if (imx2_wdt_is_running(wdev)) {
                imx2_wdt_set_timeout(wdog, wdog->timeout);
                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;
index 6914c83aa6d9bff842b79c5fb711c23c7b82f9be..fd171e6caa167457777ced3220d6bd277f0dde34 100644 (file)
@@ -153,7 +153,8 @@ static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev)
        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);
        unsigned long flags;
index aea5d2f44ad7b87f344fed2815ecd7990e9778aa..56ea1caf71c35d3bfde164dacdb8775f4fba98bb 100644 (file)
@@ -62,7 +62,8 @@ struct meson_wdt_dev {
        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);
        u32 tc_reboot = MESON_WDT_DC_RESET;
index 885c81bc42106abeb5ae696927132f69a927459e..2c4a73d1e2144393c52c7e259c61265b27de0a15 100644 (file)
@@ -31,7 +31,8 @@ struct moxart_wdt_dev {
 
 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);
 
index b78776c05554ce2d51ccb110f7e2d8754a007671..7ed417a765c70ec0a5ff38e809944c225396edf6 100644 (file)
@@ -64,7 +64,8 @@ struct mtk_wdt_dev {
        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);
        void __iomem *wdt_base;
diff --git a/drivers/watchdog/ni903x_wdt.c b/drivers/watchdog/ni903x_wdt.c
new file mode 100644 (file)
index 0000000..dc67742
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * 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");
index 313cd1c6fda0efcfa03897f1d23fc18b138e3779..0529aed158a4f22f56088599c71475b587e6d6f7 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
 #include <mach/hardware.h>
 
 /* WatchDog Timer - Chapter 23 Page 207 */
@@ -124,6 +126,41 @@ static int pnx4008_wdt_set_timeout(struct watchdog_device *wdd,
        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 = {
        .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
            WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
@@ -135,6 +172,7 @@ static const struct watchdog_ops pnx4008_wdt_ops = {
        .start = pnx4008_wdt_start,
        .stop = pnx4008_wdt_stop,
        .set_timeout = pnx4008_wdt_set_timeout,
+       .restart = pnx4008_restart_handler,
 };
 
 static struct watchdog_device pnx4008_wdd = {
@@ -169,6 +207,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
                        WDIOF_CARDRESET : 0;
        pnx4008_wdd.parent = &pdev->dev;
        watchdog_set_nowayout(&pnx4008_wdd, nowayout);
+       watchdog_set_restart_priority(&pnx4008_wdd, 128);
 
        pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */
 
@@ -178,8 +217,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
                goto disable_clk;
        }
 
-       dev_info(&pdev->dev, "PNX4008 Watchdog Timer: heartbeat %d sec\n",
-                pnx4008_wdd.timeout);
+       dev_info(&pdev->dev, "heartbeat %d sec\n", pnx4008_wdd.timeout);
 
        return 0;
 
index 424f9a952fee41d1a95332c3ae4f7682876fef2d..20563ccb7be0f5400c1166b0895adc73da758f30 100644 (file)
@@ -70,7 +70,8 @@ static int qcom_wdt_set_timeout(struct watchdog_device *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);
        u32 timeout;
index 71e78ef4b736a1b035e69f5cef063b3a528aaebc..3a75f3b53452cdb51f49882a26480cbcf8673fb9 100644 (file)
@@ -237,7 +237,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
                        return -EINVAL;
                /* Fall through */
        case WDIOC_GETTIMEOUT:
-               return copy_to_user(argp, &timeout, sizeof(int));
+               return copy_to_user(argp, &timeout, sizeof(int)) ? -EFAULT : 0;
        default:
                return -ENOTTY;
        }
index 0093450441fefb44e110d4c1494cef5bec9cc214..59e95762a6de776ba9a6bcc793138eccb8396675 100644 (file)
@@ -47,6 +47,8 @@
 #define S3C2410_WTDAT          0x04
 #define S3C2410_WTCNT          0x08
 
+#define S3C2410_WTCNT_MAXCNT   0xffff
+
 #define S3C2410_WTCON_RSTEN    (1 << 0)
 #define S3C2410_WTCON_INTEN    (1 << 2)
 #define S3C2410_WTCON_ENABLE   (1 << 5)
 #define S3C2410_WTCON_DIV64    (2 << 3)
 #define S3C2410_WTCON_DIV128   (3 << 3)
 
+#define S3C2410_WTCON_MAXDIV   0x80
+
 #define S3C2410_WTCON_PRESCALE(x)      ((x) << 8)
 #define S3C2410_WTCON_PRESCALE_MASK    (0xff << 8)
+#define S3C2410_WTCON_PRESCALE_MAX     0xff
 
 #define CONFIG_S3C2410_WATCHDOG_ATBOOT         (0)
 #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME   (15)
@@ -198,6 +203,14 @@ do {                                                       \
 
 /* 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)
 {
        return container_of(nb, struct s3c2410_wdt, freq_transition);
@@ -349,7 +362,8 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeou
        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);
        void __iomem *wdt_base = wdt->reg_base;
@@ -567,6 +581,9 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
                return ret;
        }
 
+       wdt->wdt_device.min_timeout = 1;
+       wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt->clock);
+
        ret = s3c2410wdt_cpufreq_register(wdt);
        if (ret < 0) {
                dev_err(dev, "failed to register cpufreq\n");
diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c
new file mode 100644 (file)
index 0000000..ad383f6
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * 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);
index e027deb5474037c869d09e716d92d35037df06e0..953bb7b7446f95367d39923a6c6a11a186a9052a 100644 (file)
@@ -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);
        void __iomem *wdt_base = sunxi_wdt->wdt_base;
index 709c1ed6fd79b9237710802b238eb6a2fa85ef0a..cfbed7e051b619f5edec8c93a3b964373d9b9e0f 100644 (file)
@@ -139,6 +139,10 @@ static int tangox_wdt_probe(struct platform_device *pdev)
                return err;
 
        dev->clk_rate = clk_get_rate(dev->clk);
+       if (!dev->clk_rate) {
+               err = -EINVAL;
+               goto err;
+       }
 
        dev->wdt.parent = &pdev->dev;
        dev->wdt.info = &tangox_wdt_info;
@@ -171,10 +175,8 @@ static int tangox_wdt_probe(struct platform_device *pdev)
        }
 
        err = watchdog_register_device(&dev->wdt);
-       if (err) {
-               clk_disable_unprepare(dev->clk);
-               return err;
-       }
+       if (err)
+               goto err;
 
        platform_set_drvdata(pdev, dev);
 
@@ -187,6 +189,10 @@ static int tangox_wdt_probe(struct platform_device *pdev)
        dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n");
 
        return 0;
+
+ err:
+       clk_disable_unprepare(dev->clk);
+       return err;
 }
 
 static int tangox_wdt_remove(struct platform_device *pdev)
index cab14bc9106c230fc1f4c30c214840718e42e032..09e8003039dc5bfa246698a15eaf6bf5150ed05d 100644 (file)
 static int wdt_io;
 static int cr_wdt_timeout;     /* WDT timeout 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,
             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 */
 module_param(timeout, int, 0);
@@ -92,15 +93,21 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
 #define W83667HG_B_ID          0xb3
 #define NCT6775_ID             0xb4
 #define NCT6776_ID             0xc3
+#define NCT6102_ID             0xc4
 #define NCT6779_ID             0xc5
 #define NCT6791_ID             0xc8
 #define NCT6792_ID             0xc9
 
 #define W83627HF_WDT_TIMEOUT   0xf6
 #define W83697HF_WDT_TIMEOUT   0xf4
+#define NCT6102D_WDT_TIMEOUT   0xf1
 
 #define W83627HF_WDT_CONTROL   0xf5
 #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)
 {
@@ -197,6 +204,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
        case nct6779:
        case nct6791:
        case nct6792:
+       case nct6102:
                /*
                 * These chips have a fixed WDTO# output pin (W83627UHG),
                 * or support more than one WDTO# output pin.
@@ -229,8 +237,8 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
        superio_outb(cr_wdt_control, t);
 
        /* reset trigger, disable keyboard & mouse turning off watchdog */
-       t = superio_inb(0xF7) & ~0xD0;
-       superio_outb(0xF7, t);
+       t = superio_inb(cr_wdt_csr) & ~0xD0;
+       superio_outb(cr_wdt_csr, t);
 
        superio_exit();
 
@@ -322,6 +330,7 @@ static int wdt_find(int addr)
 
        cr_wdt_timeout = W83627HF_WDT_TIMEOUT;
        cr_wdt_control = W83627HF_WDT_CONTROL;
+       cr_wdt_csr = W836X7HF_WDT_CSR;
 
        ret = superio_enter();
        if (ret)
@@ -387,6 +396,12 @@ static int wdt_find(int addr)
        case NCT6792_ID:
                ret = nct6792;
                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:
                ret = -ENODEV;
                break;
@@ -422,6 +437,7 @@ static int __init wdt_init(void)
                "NCT6779",
                "NCT6791",
                "NCT6792",
+               "NCT6102",
        };
 
        wdt_io = 0x2e;
index e600fd93b7de50f359f68a6007275ef0a6530a09..c1658fe73d58a19a774ebdcb227b13c6b0b5038b 100644 (file)
@@ -164,7 +164,7 @@ static int watchdog_restart_notifier(struct notifier_block *nb,
 
        int ret;
 
-       ret = wdd->ops->restart(wdd);
+       ret = wdd->ops->restart(wdd, action, data);
        if (ret)
                return NOTIFY_BAD;
 
@@ -199,7 +199,7 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
                return -EINVAL;
 
        /* 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;
 
        watchdog_check_min_max_timeout(wdd);
index ba2ecce4aae685df4ce30a8263fec56f3e0b729d..e2c5abbb45ffd77d0b838fcebdab0536a637af8e 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/errno.h>       /* For the -ENODEV/... values */
 #include <linux/fs.h>          /* For file operations */
 #include <linux/init.h>                /* For __init/__exit/... */
+#include <linux/jiffies.h>     /* For timeout functions */
 #include <linux/kernel.h>      /* For printk/panic/... */
 #include <linux/kref.h>                /* For data references */
 #include <linux/miscdevice.h>  /* For handling misc devices */
@@ -44,6 +45,7 @@
 #include <linux/slab.h>                /* For memory functions */
 #include <linux/types.h>       /* For standard types (like size_t) */
 #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 "watchdog_core.h"
@@ -61,6 +63,9 @@ struct watchdog_core_data {
        struct cdev cdev;
        struct watchdog_device *wdd;
        struct mutex lock;
+       unsigned long last_keepalive;
+       unsigned long last_hw_keepalive;
+       struct delayed_work work;
        unsigned long status;           /* Internal status bits */
 #define _WDOG_DEV_OPEN         0       /* Opened ? */
 #define _WDOG_ALLOW_RELEASE    1       /* Did we receive the magic char ? */
@@ -71,6 +76,91 @@ static dev_t watchdog_devt;
 /* Reference to watchdog device behind /dev/watchdog */
 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.
  *     @wdd: the watchdog device to ping
@@ -85,17 +175,28 @@ static struct watchdog_core_data *old_wd_data;
 
 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;
 
-       if (wdd->ops->ping)
-               err = wdd->ops->ping(wdd);      /* ping the watchdog */
-       else
-               err = wdd->ops->start(wdd);     /* restart watchdog */
+       wd_data->last_keepalive = jiffies;
+       return __watchdog_ping(wdd);
+}
 
-       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)
 
 static int watchdog_start(struct watchdog_device *wdd)
 {
+       struct watchdog_core_data *wd_data = wdd->wd_data;
+       unsigned long started_at;
        int err;
 
        if (watchdog_active(wdd))
                return 0;
 
-       err = wdd->ops->start(wdd);
-       if (err == 0)
+       started_at = jiffies;
+       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);
+               wd_data->last_keepalive = started_at;
+               watchdog_update_worker(wdd);
+       }
 
        return err;
 }
@@ -137,7 +247,7 @@ static int watchdog_start(struct watchdog_device *wdd)
 
 static int watchdog_stop(struct watchdog_device *wdd)
 {
-       int err;
+       int err = 0;
 
        if (!watchdog_active(wdd))
                return 0;
@@ -148,9 +258,15 @@ static int watchdog_stop(struct watchdog_device *wdd)
                return -EBUSY;
        }
 
-       err = wdd->ops->stop(wdd);
-       if (err == 0)
+       if (wdd->ops->stop)
+               err = wdd->ops->stop(wdd);
+       else
+               set_bit(WDOG_HW_RUNNING, &wdd->status);
+
+       if (err == 0) {
                clear_bit(WDOG_ACTIVE, &wdd->status);
+               watchdog_update_worker(wdd);
+       }
 
        return err;
 }
@@ -183,13 +299,22 @@ static unsigned int watchdog_get_status(struct watchdog_device *wdd)
 static int watchdog_set_timeout(struct watchdog_device *wdd,
                                                        unsigned int timeout)
 {
-       if (!wdd->ops->set_timeout || !(wdd->info->options & WDIOF_SETTIMEOUT))
+       int err = 0;
+
+       if (!(wdd->info->options & WDIOF_SETTIMEOUT))
                return -EOPNOTSUPP;
 
        if (watchdog_timeout_invalid(wdd, timeout))
                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)
         * If the /dev/watchdog device is open, we don't want the module
         * to be unloaded.
         */
-       if (!try_module_get(wdd->ops->owner)) {
+       if (!watchdog_hw_running(wdd) && !try_module_get(wdd->ops->owner)) {
                err = -EBUSY;
                goto out_clear;
        }
@@ -549,7 +674,8 @@ static int watchdog_open(struct inode *inode, struct file *file)
 
        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 */
        return nonseekable_open(inode, 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_device *wdd;
        int err = -EBUSY;
+       bool running;
 
        mutex_lock(&wd_data->lock);
 
@@ -609,14 +736,24 @@ static int watchdog_release(struct inode *inode, struct file *file)
                watchdog_ping(wdd);
        }
 
+       cancel_delayed_work_sync(&wd_data->work);
+       watchdog_update_worker(wdd);
+
        /* make sure that /dev/watchdog can be re-opened */
        clear_bit(_WDOG_DEV_OPEN, &wd_data->status);
 
 done:
+       running = wdd && watchdog_hw_running(wdd);
        mutex_unlock(&wd_data->lock);
-       /* Allow the owner module to be unloaded again */
-       module_put(wd_data->cdev.owner);
-       kref_put(&wd_data->kref, watchdog_core_data_release);
+       /*
+        * Allow the owner module to be unloaded again unless the watchdog
+        * 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;
 }
 
@@ -658,6 +795,11 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
        wd_data->wdd = wdd;
        wdd->wd_data = wd_data;
 
+       if (!watchdog_wq)
+               return -ENODEV;
+
+       INIT_DELAYED_WORK(&wd_data->work, watchdog_ping_work);
+
        if (wdd->id == 0) {
                old_wd_data = wd_data;
                watchdog_miscdev.parent = wdd->parent;
@@ -688,8 +830,23 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
                        old_wd_data = NULL;
                        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)
        wdd->wd_data = NULL;
        mutex_unlock(&wd_data->lock);
 
+       cancel_delayed_work_sync(&wd_data->work);
+
        kref_put(&wd_data->kref, watchdog_core_data_release);
 }
 
@@ -780,6 +939,13 @@ int __init watchdog_dev_init(void)
 {
        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);
        if (err < 0) {
                pr_err("couldn't register class\n");
@@ -806,4 +972,5 @@ void __exit watchdog_dev_exit(void)
 {
        unregister_chrdev_region(watchdog_devt, MAX_DOGS);
        class_unregister(&watchdog_class);
+       destroy_workqueue(watchdog_wq);
 }
index 0c7cb7302cf06586917a8d237b052782c756a17d..cbe373de36598572bc47557a008386e7838086c8 100644 (file)
@@ -36,7 +36,7 @@
 #define ZIIRAVE_STATE_OFF      0x1
 #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",
                                  "illegal instruction", "illegal trap",
                                  "unknown"};
index b585fa2507eea4fd841b6cd27c4919e137e0a8ee..51732d6c9555decf5264da14a8d611524fab9b4e 100644 (file)
@@ -10,8 +10,9 @@
 
 
 #include <linux/bitops.h>
-#include <linux/device.h>
 #include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
 #include <linux/notifier.h>
 #include <uapi/linux/watchdog.h>
 
@@ -46,7 +47,7 @@ struct watchdog_ops {
        unsigned int (*status)(struct watchdog_device *);
        int (*set_timeout)(struct watchdog_device *, unsigned int);
        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);
 };
 
@@ -61,14 +62,21 @@ struct watchdog_ops {
  * @bootstatus:        Status of the watchdog device at boot.
  * @timeout:   The watchdog devices 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.
  * @restart_nb:        The notifier block to register a restart function.
  * @driver_data:Pointer to the drivers private data.
  * @wd_data:   Pointer to watchdog core internal data.
  * @status:    Field that contains the devices internal status bits.
- * @deferred: entry in wtd_deferred_reg_list which is used to
- *                        register early initialized watchdogs.
+ * @deferred:  Entry in wtd_deferred_reg_list which is used to
+ *             register early initialized watchdogs.
  *
  * The watchdog_device structure contains all information about a
  * watchdog timer device.
@@ -89,6 +97,8 @@ struct watchdog_device {
        unsigned int timeout;
        unsigned int min_timeout;
        unsigned int max_timeout;
+       unsigned int min_hw_heartbeat_ms;
+       unsigned int max_hw_heartbeat_ms;
        struct notifier_block reboot_nb;
        struct notifier_block restart_nb;
        void *driver_data;
@@ -98,6 +108,7 @@ struct watchdog_device {
 #define WDOG_ACTIVE            0       /* Is the watchdog running/active */
 #define WDOG_NO_WAY_OUT                1       /* Is 'nowayout' feature set ? */
 #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;
 };
 
@@ -110,6 +121,15 @@ static inline bool watchdog_active(struct watchdog_device *wdd)
        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 */
 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
 {
        /*
         * 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,
         * or
-        * - a maximum timeout is configured, and the requested value is larger
-        *   than the maximum timeout.
+        * - a maximum hardware timeout is not configured, a maximum timeout
+        *   is configured, and the requested value is larger than the
+        *   configured maximum timeout.
         */
-       return t < wdd->min_timeout ||
-               (wdd->max_timeout && t > wdd->max_timeout);
+       return t > UINT_MAX / 1000 || t < wdd->min_timeout ||
+               (!wdd->max_hw_heartbeat_ms && wdd->max_timeout &&
+                t > wdd->max_timeout);
 }
 
 /* Use the following functions to manipulate watchdog driver specific data */
This page took 0.064951 seconds and 5 git commands to generate.