Merge git://www.linux-watchdog.org/linux-watchdog
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 11 Jul 2017 16:59:37 +0000 (09:59 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 11 Jul 2017 16:59:37 +0000 (09:59 -0700)
Pull watchdog updates from Wim Van Sebroeck:

 - Add Renesas RZ/A WDT Watchdog driver

 - STM32 Independent WatchDoG (IWDG) support

 - UniPhier watchdog support

 - Add F71868 support

 - Add support for NCT6793D and NCT6795D

 - dw_wdt: add reset lines support

 - core: add option to avoid early handling of watchdog

 - core: introduce watchdog_worker_should_ping helper

 - Cleanups and improvements for sama5d4, intel-mid_wdt, s3c2410_wdt,
   orion_wdt, gpio_wdt, it87_wdt, meson_wdt, davinci_wdt, bcm47xx_wdt,
   zx2967_wdt, cadence_wdt

* git://www.linux-watchdog.org/linux-watchdog: (32 commits)
  watchdog: introduce watchdog_worker_should_ping helper
  watchdog: uniphier: add UniPhier watchdog driver
  dt-bindings: watchdog: add description for UniPhier WDT controller
  watchdog: cadence_wdt: make of_device_ids const.
  watchdog: zx2967: constify zx2967_wdt_ops.
  watchdog: bcm47xx_wdt: constify bcm47xx_wdt_hard_ops and bcm47xx_wdt_soft_ops
  watchdog: davinci: Add missing clk_disable_unprepare().
  watchdog: davinci: Handle return value of clk_prepare_enable
  watchdog: meson: Handle return value of clk_prepare_enable
  watchdog: it87: Add support for various Super-IO chips
  watchdog: it87: Use infrastructure to stop watchdog on reboot
  watchdog: it87: Drop support for resetting watchdog though CIR and Game port
  watchdog: it87: Convert to use watchdog core infrastructure
  watchdog: it87: Drop FSF mailing address
  watchdog: dw_wdt: get reset lines from dt
  watchdog: bindings: dw_wdt: add reset lines
  watchdog: w83627hf: Add support for NCT6793D and NCT6795D
  watchdog: core: add option to avoid early handling of watchdog
  watchdog: f71808e_wdt: Add F71868 support
  watchdog: Add STM32 IWDG driver
  ...

26 files changed:
Documentation/devicetree/bindings/watchdog/da9062-wdt.txt [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/dw_wdt.txt
Documentation/devicetree/bindings/watchdog/renesas-wdt.txt
Documentation/devicetree/bindings/watchdog/st,stm32-iwdg.txt [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/uniphier-wdt.txt [new file with mode: 0644]
Documentation/watchdog/watchdog-parameters.txt
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/bcm47xx_wdt.c
drivers/watchdog/cadence_wdt.c
drivers/watchdog/davinci_wdt.c
drivers/watchdog/dw_wdt.c
drivers/watchdog/f71808e_wdt.c
drivers/watchdog/gpio_wdt.c
drivers/watchdog/intel-mid_wdt.c
drivers/watchdog/it87_wdt.c
drivers/watchdog/meson_gxbb_wdt.c
drivers/watchdog/orion_wdt.c
drivers/watchdog/rza_wdt.c [new file with mode: 0644]
drivers/watchdog/s3c2410_wdt.c
drivers/watchdog/sama5d4_wdt.c
drivers/watchdog/stm32_iwdg.c [new file with mode: 0644]
drivers/watchdog/uniphier_wdt.c [new file with mode: 0644]
drivers/watchdog/w83627hf_wdt.c
drivers/watchdog/watchdog_dev.c
drivers/watchdog/zx2967_wdt.c

diff --git a/Documentation/devicetree/bindings/watchdog/da9062-wdt.txt b/Documentation/devicetree/bindings/watchdog/da9062-wdt.txt
new file mode 100644 (file)
index 0000000..b935b52
--- /dev/null
@@ -0,0 +1,23 @@
+* Dialog Semiconductor DA9062/61 Watchdog Timer
+
+Required properties:
+
+- compatible: should be one of the following valid compatible string lines:
+       "dlg,da9061-watchdog", "dlg,da9062-watchdog"
+       "dlg,da9062-watchdog"
+
+Example: DA9062
+
+       pmic0: da9062@58 {
+               watchdog {
+                       compatible = "dlg,da9062-watchdog";
+               };
+       };
+
+Example: DA9061 using a fall-back compatible for the DA9062 watchdog driver
+
+       pmic0: da9061@58 {
+               watchdog {
+                       compatible = "dlg,da9061-watchdog", "dlg,da9062-watchdog";
+               };
+       };
index 08e16f6..eb09144 100644 (file)
@@ -10,6 +10,8 @@ Required Properties:
 Optional Properties:
 
 - interrupts   : The interrupt used for the watchdog timeout warning.
+- resets       : phandle pointing to the system reset controller with
+               line index for the watchdog.
 
 Example:
 
@@ -18,4 +20,5 @@ Example:
                reg = <0xffd02000 0x1000>;
                interrupts = <0 171 4>;
                clocks = <&per_base_clk>;
+               resets = <&rst WDT0_RESET>;
        };
index da24e31..9e306af 100644 (file)
@@ -2,10 +2,11 @@ Renesas Watchdog Timer (WDT) Controller
 
 Required properties:
 - compatible : Should be "renesas,<soctype>-wdt", and
-              "renesas,rcar-gen3-wdt" as fallback.
+              "renesas,rcar-gen3-wdt" or "renesas,rza-wdt" as fallback.
               Examples with soctypes are:
                 - "renesas,r8a7795-wdt" (R-Car H3)
                 - "renesas,r8a7796-wdt" (R-Car M3-W)
+                - "renesas,r7s72100-wdt" (RZ/A1)
 
   When compatible with the generic version, nodes must list the SoC-specific
   version corresponding to the platform first, followed by the generic
@@ -17,6 +18,7 @@ Required properties:
 Optional properties:
 - timeout-sec : Contains the watchdog timeout in seconds
 - power-domains : the power domain the WDT belongs to
+- interrupts: Some WDTs have an interrupt when used in interval timer mode
 
 Examples:
 
diff --git a/Documentation/devicetree/bindings/watchdog/st,stm32-iwdg.txt b/Documentation/devicetree/bindings/watchdog/st,stm32-iwdg.txt
new file mode 100644 (file)
index 0000000..cc13b10
--- /dev/null
@@ -0,0 +1,19 @@
+STM32 Independent WatchDoG (IWDG)
+---------------------------------
+
+Required properties:
+- compatible: "st,stm32-iwdg"
+- reg: physical base address and length of the registers set for the device
+- clocks: must contain a single entry describing the clock input
+
+Optional Properties:
+- timeout-sec: Watchdog timeout value in seconds.
+
+Example:
+
+iwdg: watchdog@40003000 {
+       compatible = "st,stm32-iwdg";
+       reg = <0x40003000 0x400>;
+       clocks = <&clk_lsi>;
+       timeout-sec = <32>;
+};
diff --git a/Documentation/devicetree/bindings/watchdog/uniphier-wdt.txt b/Documentation/devicetree/bindings/watchdog/uniphier-wdt.txt
new file mode 100644 (file)
index 0000000..bf63375
--- /dev/null
@@ -0,0 +1,20 @@
+UniPhier watchdog timer controller
+
+This UniPhier watchdog timer controller must be under sysctrl node.
+
+Required properties:
+- compatible: should be "socionext,uniphier-wdt"
+
+Example:
+
+       sysctrl@61840000 {
+               compatible = "socionext,uniphier-ld11-sysctrl",
+                            "simple-mfd", "syscon";
+               reg = <0x61840000 0x4000>;
+
+               watchdog {
+                       compatible = "socionext,uniphier-wdt";
+               }
+
+               other nodes ...
+       };
index 914518a..b352636 100644 (file)
@@ -369,6 +369,12 @@ timeout: Watchdog timeout in seconds. (0<timeout<N, default=60)
 nowayout: Watchdog cannot be stopped once started
        (default=kernel config parameter)
 -------------------------------------------------
+uniphier_wdt:
+timeout: Watchdog timeout in power of two seconds.
+       (1 <= timeout <= 128, default=64)
+nowayout: Watchdog cannot be stopped once started
+       (default=kernel config parameter)
+-------------------------------------------------
 w83627hf_wdt:
 wdt_io: w83627hf/thf WDT io port (default 0x2E)
 timeout: Watchdog timeout in seconds. 1 <= timeout <= 255, default=60.
index e6e31a1..c722cbf 100644 (file)
@@ -46,6 +46,17 @@ config WATCHDOG_NOWAYOUT
          get killed. If you say Y here, the watchdog cannot be stopped once
          it has been started.
 
+config WATCHDOG_HANDLE_BOOT_ENABLED
+       bool "Update boot-enabled watchdog until userspace takes over"
+       default y
+       help
+         The default watchdog behaviour (which you get if you say Y here) is
+         to ping watchdog devices that were enabled before the driver has
+         been loaded until control is taken over from userspace using the
+         /dev/watchdog file. If you say N here, the kernel will not update
+         the watchdog on its own. Thus if your userspace does not start fast
+         enough your device will reboot.
+
 config WATCHDOG_SYSFS
        bool "Read different watchdog information through sysfs"
        help
@@ -721,6 +732,14 @@ config RENESAS_WDT
          This driver adds watchdog support for the integrated watchdogs in the
          Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT).
 
+config RENESAS_RZAWDT
+       tristate "Renesas RZ/A WDT Watchdog"
+       depends on ARCH_RENESAS || COMPILE_TEST
+       select WATCHDOG_CORE
+       help
+         This driver adds watchdog support for the integrated watchdogs in the
+         Renesas RZ/A SoCs. These watchdogs can be used to reset a system.
+
 config ASPEED_WATCHDOG
        tristate "Aspeed 2400 watchdog support"
        depends on ARCH_ASPEED || COMPILE_TEST
@@ -744,6 +763,30 @@ config ZX2967_WATCHDOG
          To compile this driver as a module, choose M here: the
          module will be called zx2967_wdt.
 
+config STM32_WATCHDOG
+       tristate "STM32 Independent WatchDoG (IWDG) support"
+       depends on ARCH_STM32
+       select WATCHDOG_CORE
+       default y
+       help
+         Say Y here to include support for the watchdog timer
+         in stm32 SoCs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called stm32_iwdg.
+
+config UNIPHIER_WATCHDOG
+       tristate "UniPhier watchdog support"
+       depends on ARCH_UNIPHIER || COMPILE_TEST
+       depends on OF && MFD_SYSCON
+       select WATCHDOG_CORE
+       help
+         Say Y here to include support watchdog timer embedded
+         into the UniPhier system.
+
+         To compile this driver as a module, choose M here: the
+         module will be called uniphier_wdt.
+
 # AVR32 Architecture
 
 config AT32AP700X_WDT
@@ -829,11 +872,12 @@ config EBC_C384_WDT
          the timeout module parameter.
 
 config F71808E_WDT
-       tristate "Fintek F71808E, F71862FG, F71869, F71882FG and F71889FG Watchdog"
+       tristate "Fintek F718xx, F818xx Super I/O Watchdog"
        depends on X86
        help
-         This is the driver for the hardware watchdog on the Fintek
-         F71808E, F71862FG, F71869, F71882FG and F71889FG Super I/O controllers.
+         This is the driver for the hardware watchdog on the Fintek F71808E,
+         F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866
+         Super I/O controllers.
 
          You can compile this driver directly into the kernel, or use
          it as a module.  The module will be called f71808e_wdt.
@@ -1037,13 +1081,12 @@ config IT8712F_WDT
 config IT87_WDT
        tristate "IT87 Watchdog Timer"
        depends on X86
+       select WATCHDOG_CORE
        ---help---
-         This is the driver for the hardware watchdog on the ITE IT8620,
-         IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728
-         Super I/O chips.
-
-         If the driver does not work, then make sure that the game port in
-         the BIOS is enabled.
+         This is the driver for the hardware watchdog on the ITE IT8607,
+         IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686, IT8702,
+         IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728, and
+         IT8783 Super I/O chips.
 
          This watchdog simply watches your kernel to make sure it doesn't
          freeze, and if it does, it reboots your computer after a certain
index a2126e2..56adf9f 100644 (file)
@@ -82,8 +82,11 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
 obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
 obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o
 obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
+obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o
 obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
 obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o
+obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
+obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
 
 # AVR32 Architecture
 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
index 35725e2..2365828 100644 (file)
@@ -97,7 +97,7 @@ static int bcm47xx_wdt_restart(struct watchdog_device *wdd,
        return 0;
 }
 
-static struct watchdog_ops bcm47xx_wdt_hard_ops = {
+static const struct watchdog_ops bcm47xx_wdt_hard_ops = {
        .owner          = THIS_MODULE,
        .start          = bcm47xx_wdt_hard_start,
        .stop           = bcm47xx_wdt_hard_stop,
@@ -168,7 +168,7 @@ static const struct watchdog_info bcm47xx_wdt_info = {
                                WDIOF_MAGICCLOSE,
 };
 
-static struct watchdog_ops bcm47xx_wdt_soft_ops = {
+static const struct watchdog_ops bcm47xx_wdt_soft_ops = {
        .owner          = THIS_MODULE,
        .start          = bcm47xx_wdt_soft_start,
        .stop           = bcm47xx_wdt_soft_stop,
index 86e0b5d..05c0000 100644 (file)
@@ -458,7 +458,7 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume);
 
-static struct of_device_id cdns_wdt_of_match[] = {
+static const struct of_device_id cdns_wdt_of_match[] = {
        { .compatible = "cdns,wdt-r1p2", },
        { /* end of table */ }
 };
index 0e731d7..2f46487 100644 (file)
@@ -173,7 +173,11 @@ static int davinci_wdt_probe(struct platform_device *pdev)
                return PTR_ERR(davinci_wdt->clk);
        }
 
-       clk_prepare_enable(davinci_wdt->clk);
+       ret = clk_prepare_enable(davinci_wdt->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to prepare clock\n");
+               return ret;
+       }
 
        platform_set_drvdata(pdev, davinci_wdt);
 
@@ -198,8 +202,10 @@ static int davinci_wdt_probe(struct platform_device *pdev)
                return PTR_ERR(davinci_wdt->base);
 
        ret = watchdog_register_device(wdd);
-       if (ret < 0)
+       if (ret < 0) {
+               clk_disable_unprepare(davinci_wdt->clk);
                dev_err(dev, "cannot register watchdog device\n");
+       }
 
        return ret;
 }
index 914da3a..36be987 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/of.h>
 #include <linux/pm.h>
 #include <linux/platform_device.h>
+#include <linux/reset.h>
 #include <linux/watchdog.h>
 
 #define WDOG_CONTROL_REG_OFFSET                    0x00
@@ -54,6 +55,7 @@ struct dw_wdt {
        struct clk              *clk;
        unsigned long           rate;
        struct watchdog_device  wdd;
+       struct reset_control    *rst;
 };
 
 #define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd)
@@ -234,6 +236,14 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
                goto out_disable_clk;
        }
 
+       dw_wdt->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
+       if (IS_ERR(dw_wdt->rst)) {
+               ret = PTR_ERR(dw_wdt->rst);
+               goto out_disable_clk;
+       }
+
+       reset_control_deassert(dw_wdt->rst);
+
        wdd = &dw_wdt->wdd;
        wdd->info = &dw_wdt_ident;
        wdd->ops = &dw_wdt_ops;
@@ -279,6 +289,7 @@ static int dw_wdt_drv_remove(struct platform_device *pdev)
        struct dw_wdt *dw_wdt = platform_get_drvdata(pdev);
 
        watchdog_unregister_device(&dw_wdt->wdd);
+       reset_control_assert(dw_wdt->rst);
        clk_disable_unprepare(dw_wdt->clk);
 
        return 0;
index 1b7e916..8658dba 100644 (file)
@@ -57,6 +57,7 @@
 #define SIO_F71808_ID          0x0901  /* Chipset ID */
 #define SIO_F71858_ID          0x0507  /* Chipset ID */
 #define SIO_F71862_ID          0x0601  /* Chipset ID */
+#define SIO_F71868_ID          0x1106  /* Chipset ID */
 #define SIO_F71869_ID          0x0814  /* Chipset ID */
 #define SIO_F71869A_ID         0x1007  /* Chipset ID */
 #define SIO_F71882_ID          0x0541  /* Chipset ID */
@@ -101,7 +102,7 @@ MODULE_PARM_DESC(timeout,
 static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH;
 module_param(pulse_width, uint, 0);
 MODULE_PARM_DESC(pulse_width,
-       "Watchdog signal pulse width. 0(=level), 1 ms, 25 ms, 125 ms or 5000 ms"
+       "Watchdog signal pulse width. 0(=level), 1, 25, 30, 125, 150, 5000 or 6000 ms"
                        " (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")");
 
 static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN;
@@ -119,13 +120,14 @@ module_param(start_withtimeout, uint, 0);
 MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
        " given initial timeout. Zero (default) disables this feature.");
 
-enum chips { f71808fg, f71858fg, f71862fg, f71869, f71882fg, f71889fg, f81865,
-            f81866};
+enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
+            f81865, f81866};
 
 static const char *f71808e_names[] = {
        "f71808fg",
        "f71858fg",
        "f71862fg",
+       "f71868",
        "f71869",
        "f71882fg",
        "f71889fg",
@@ -252,16 +254,23 @@ static int watchdog_set_timeout(int timeout)
 static int watchdog_set_pulse_width(unsigned int pw)
 {
        int err = 0;
+       unsigned int t1 = 25, t2 = 125, t3 = 5000;
+
+       if (watchdog.type == f71868) {
+               t1 = 30;
+               t2 = 150;
+               t3 = 6000;
+       }
 
        mutex_lock(&watchdog.lock);
 
-       if        (pw <=    1) {
+       if        (pw <=  1) {
                watchdog.pulse_val = 0;
-       } else if (pw <=   25) {
+       } else if (pw <= t1) {
                watchdog.pulse_val = 1;
-       } else if (pw <=  125) {
+       } else if (pw <= t2) {
                watchdog.pulse_val = 2;
-       } else if (pw <= 5000) {
+       } else if (pw <= t3) {
                watchdog.pulse_val = 3;
        } else {
                pr_err("pulse width out of range\n");
@@ -354,6 +363,7 @@ static int watchdog_start(void)
                        goto exit_superio;
                break;
 
+       case f71868:
        case f71869:
                /* GPIO14 --> WDTRST# */
                superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4);
@@ -792,6 +802,9 @@ static int __init f71808e_find(int sioaddr)
                watchdog.type = f71862fg;
                err = f71862fg_pin_configure(0); /* validate module parameter */
                break;
+       case SIO_F71868_ID:
+               watchdog.type = f71868;
+               break;
        case SIO_F71869_ID:
        case SIO_F71869A_ID:
                watchdog.type = f71869;
index 93457ca..cb66c2f 100644 (file)
@@ -18,7 +18,6 @@
 
 #define SOFT_TIMEOUT_MIN       1
 #define SOFT_TIMEOUT_DEF       60
-#define SOFT_TIMEOUT_MAX       0xffff
 
 enum {
        HW_ALGO_TOGGLE,
@@ -30,11 +29,7 @@ struct gpio_wdt_priv {
        bool                    active_low;
        bool                    state;
        bool                    always_running;
-       bool                    armed;
        unsigned int            hw_algo;
-       unsigned int            hw_margin;
-       unsigned long           last_jiffies;
-       struct timer_list       timer;
        struct watchdog_device  wdd;
 };
 
@@ -47,21 +42,10 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
                gpio_direction_input(priv->gpio);
 }
 
-static void gpio_wdt_hwping(unsigned long data)
+static int gpio_wdt_ping(struct watchdog_device *wdd)
 {
-       struct watchdog_device *wdd = (struct watchdog_device *)data;
        struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
 
-       if (priv->armed && time_after(jiffies, priv->last_jiffies +
-                                     msecs_to_jiffies(wdd->timeout * 1000))) {
-               dev_crit(wdd->parent,
-                        "Timer expired. System will reboot soon!\n");
-               return;
-       }
-
-       /* Restart timer */
-       mod_timer(&priv->timer, jiffies + priv->hw_margin);
-
        switch (priv->hw_algo) {
        case HW_ALGO_TOGGLE:
                /* Toggle output pin */
@@ -75,55 +59,33 @@ static void gpio_wdt_hwping(unsigned long data)
                gpio_set_value_cansleep(priv->gpio, priv->active_low);
                break;
        }
-}
-
-static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv)
-{
-       priv->state = priv->active_low;
-       gpio_direction_output(priv->gpio, priv->state);
-       priv->last_jiffies = jiffies;
-       gpio_wdt_hwping((unsigned long)&priv->wdd);
+       return 0;
 }
 
 static int gpio_wdt_start(struct watchdog_device *wdd)
 {
        struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
 
-       gpio_wdt_start_impl(priv);
-       priv->armed = true;
+       priv->state = priv->active_low;
+       gpio_direction_output(priv->gpio, priv->state);
 
-       return 0;
+       set_bit(WDOG_HW_RUNNING, &wdd->status);
+
+       return gpio_wdt_ping(wdd);
 }
 
 static int gpio_wdt_stop(struct watchdog_device *wdd)
 {
        struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
 
-       priv->armed = false;
        if (!priv->always_running) {
-               mod_timer(&priv->timer, 0);
                gpio_wdt_disable(priv);
+               clear_bit(WDOG_HW_RUNNING, &wdd->status);
        }
 
        return 0;
 }
 
-static int gpio_wdt_ping(struct watchdog_device *wdd)
-{
-       struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
-
-       priv->last_jiffies = jiffies;
-
-       return 0;
-}
-
-static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
-{
-       wdd->timeout = t;
-
-       return gpio_wdt_ping(wdd);
-}
-
 static const struct watchdog_info gpio_wdt_ident = {
        .options        = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
                          WDIOF_SETTIMEOUT,
@@ -135,7 +97,6 @@ static const struct watchdog_ops gpio_wdt_ops = {
        .start          = gpio_wdt_start,
        .stop           = gpio_wdt_stop,
        .ping           = gpio_wdt_ping,
-       .set_timeout    = gpio_wdt_set_timeout,
 };
 
 static int gpio_wdt_probe(struct platform_device *pdev)
@@ -185,9 +146,6 @@ static int gpio_wdt_probe(struct platform_device *pdev)
        if (hw_margin < 2 || hw_margin > 65535)
                return -EINVAL;
 
-       /* Use safe value (1/2 of real timeout) */
-       priv->hw_margin = msecs_to_jiffies(hw_margin / 2);
-
        priv->always_running = of_property_read_bool(pdev->dev.of_node,
                                                     "always-running");
 
@@ -196,31 +154,26 @@ static int gpio_wdt_probe(struct platform_device *pdev)
        priv->wdd.info          = &gpio_wdt_ident;
        priv->wdd.ops           = &gpio_wdt_ops;
        priv->wdd.min_timeout   = SOFT_TIMEOUT_MIN;
-       priv->wdd.max_timeout   = SOFT_TIMEOUT_MAX;
+       priv->wdd.max_hw_heartbeat_ms = hw_margin;
        priv->wdd.parent        = &pdev->dev;
 
        if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
                priv->wdd.timeout = SOFT_TIMEOUT_DEF;
 
-       setup_timer(&priv->timer, gpio_wdt_hwping, (unsigned long)&priv->wdd);
-
        watchdog_stop_on_reboot(&priv->wdd);
 
-       ret = watchdog_register_device(&priv->wdd);
-       if (ret)
-               return ret;
-
        if (priv->always_running)
-               gpio_wdt_start_impl(priv);
+               gpio_wdt_start(&priv->wdd);
 
-       return 0;
+       ret = watchdog_register_device(&priv->wdd);
+
+       return ret;
 }
 
 static int gpio_wdt_remove(struct platform_device *pdev)
 {
        struct gpio_wdt_priv *priv = platform_get_drvdata(pdev);
 
-       del_timer_sync(&priv->timer);
        watchdog_unregister_device(&priv->wdd);
 
        return 0;
index 45e4d02..72c108a 100644 (file)
@@ -147,8 +147,21 @@ static int mid_wdt_probe(struct platform_device *pdev)
                return ret;
        }
 
-       /* Make sure the watchdog is not running */
-       wdt_stop(wdt_dev);
+       /*
+        * The firmware followed by U-Boot leaves the watchdog running
+        * with the default threshold which may vary. When we get here
+        * we should make a decision to prevent any side effects before
+        * user space daemon will take care of it. The best option,
+        * taking into consideration that there is no way to read values
+        * back from hardware, is to enforce watchdog being run with
+        * deterministic values.
+        */
+       ret = wdt_start(wdt_dev);
+       if (ret)
+               return ret;
+
+       /* Make sure the watchdog is serviced */
+       set_bit(WDOG_HW_RUNNING, &wdt_dev->status);
 
        ret = devm_watchdog_register_device(&pdev->dev, wdt_dev);
        if (ret) {
index b9878c4..dd1e7ea 100644 (file)
@@ -12,8 +12,9 @@
  *                 http://www.ite.com.tw/
  *
  *     Support of the watchdog timers, which are available on
- *     IT8620, IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726,
- *     IT8728 and IT8783.
+ *     IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686,
+ *     IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728,
+ *     and IT8783.
  *
  *     This program is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License
  *     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.
- *
- *     You should have received a copy of the GNU General Public License
- *     along with this program; if not, write to the Free Software
- *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
 #include <linux/watchdog.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-
 
-#define WATCHDOG_VERSION       "1.14"
 #define WATCHDOG_NAME          "IT87 WDT"
-#define DRIVER_VERSION         WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
-#define WD_MAGIC               'V'
 
 /* Defaults for Module Parameter */
-#define DEFAULT_NOGAMEPORT     0
-#define DEFAULT_NOCIR          0
-#define DEFAULT_EXCLUSIVE      1
 #define DEFAULT_TIMEOUT                60
 #define DEFAULT_TESTMODE       0
 #define DEFAULT_NOWAYOUT       WATCHDOG_NOWAYOUT
 
 /* Logical device Numbers LDN */
 #define GPIO           0x07
-#define GAMEPORT       0x09
-#define CIR            0x0a
 
 /* Configuration Registers and Functions */
 #define LDNREG         0x07
 #define CHIPID         0x20
 #define CHIPREV                0x22
-#define ACTREG         0x30
-#define BASEREG                0x60
 
 /* Chip Id numbers */
 #define NO_DEV_ID      0xffff
+#define IT8607_ID      0x8607
 #define IT8620_ID      0x8620
+#define IT8622_ID      0x8622
+#define IT8625_ID      0x8625
+#define IT8628_ID      0x8628
+#define IT8655_ID      0x8655
+#define IT8665_ID      0x8665
+#define IT8686_ID      0x8686
 #define IT8702_ID      0x8702
 #define IT8705_ID      0x8705
 #define IT8712_ID      0x8712
 #define WDTVALLSB      0x73
 #define WDTVALMSB      0x74
 
-/* GPIO Bits WDTCTRL */
-#define WDT_CIRINT     0x80
-#define WDT_MOUSEINT   0x40
-#define WDT_KYBINT     0x20
-#define WDT_GAMEPORT   0x10 /* not in it8718, it8720, it8721, it8728 */
-#define WDT_FORCE      0x02
-#define WDT_ZERO       0x01
-
 /* GPIO Bits WDTCFG */
 #define WDT_TOV1       0x80
 #define WDT_KRST       0x40
 #define WDT_PWROK      0x10 /* not in it8721 */
 #define WDT_INT_MASK   0x0f
 
-/* CIR Configuration Register LDN=0x0a */
-#define CIR_ILS                0x70
-
-/* The default Base address is not always available, we use this */
-#define CIR_BASE       0x0208
-
-/* CIR Controller */
-#define CIR_DR(b)      (b)
-#define CIR_IER(b)     (b + 1)
-#define CIR_RCR(b)     (b + 2)
-#define CIR_TCR1(b)    (b + 3)
-#define CIR_TCR2(b)    (b + 4)
-#define CIR_TSR(b)     (b + 5)
-#define CIR_RSR(b)     (b + 6)
-#define CIR_BDLR(b)    (b + 5)
-#define CIR_BDHR(b)    (b + 6)
-#define CIR_IIR(b)     (b + 7)
-
-/* Default Base address of Game port */
-#define GP_BASE_DEFAULT        0x0201
-
-/* wdt_status */
-#define WDTS_TIMER_RUN 0
-#define WDTS_DEV_OPEN  1
-#define WDTS_KEEPALIVE 2
-#define WDTS_LOCKED    3
-#define WDTS_USE_GP    4
-#define WDTS_EXPECTED  5
-#define WDTS_USE_CIR   6
-
-static unsigned int base, gpact, ciract, max_units, chip_type;
-static unsigned long wdt_status;
-
-static int nogameport = DEFAULT_NOGAMEPORT;
-static int nocir      = DEFAULT_NOCIR;
-static int exclusive  = DEFAULT_EXCLUSIVE;
-static int timeout    = DEFAULT_TIMEOUT;
-static int testmode   = DEFAULT_TESTMODE;
-static bool nowayout   = DEFAULT_NOWAYOUT;
-
-module_param(nogameport, int, 0);
-MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
-               __MODULE_STRING(DEFAULT_NOGAMEPORT));
-module_param(nocir, int, 0);
-MODULE_PARM_DESC(nocir, "Forbid the use of Consumer IR interrupts to reset timer, default="
-               __MODULE_STRING(DEFAULT_NOCIR));
-module_param(exclusive, int, 0);
-MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default="
-               __MODULE_STRING(DEFAULT_EXCLUSIVE));
+static unsigned int max_units, chip_type;
+
+static unsigned int timeout = DEFAULT_TIMEOUT;
+static int testmode = DEFAULT_TESTMODE;
+static bool nowayout = DEFAULT_NOWAYOUT;
+
 module_param(timeout, int, 0);
 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
                __MODULE_STRING(DEFAULT_TIMEOUT));
@@ -231,88 +167,59 @@ static inline void superio_outw(int val, int reg)
 }
 
 /* Internal function, should be called after superio_select(GPIO) */
-static void wdt_update_timeout(void)
+static void _wdt_update_timeout(unsigned int t)
 {
        unsigned char cfg = WDT_KRST;
-       int tm = timeout;
 
        if (testmode)
                cfg = 0;
 
-       if (tm <= max_units)
+       if (t <= max_units)
                cfg |= WDT_TOV1;
        else
-               tm /= 60;
+               t /= 60;
 
        if (chip_type != IT8721_ID)
                cfg |= WDT_PWROK;
 
        superio_outb(cfg, WDTCFG);
-       superio_outb(tm, WDTVALLSB);
+       superio_outb(t, WDTVALLSB);
        if (max_units > 255)
-               superio_outb(tm>>8, WDTVALMSB);
+               superio_outb(t >> 8, WDTVALMSB);
 }
 
-static int wdt_round_time(int t)
+static int wdt_update_timeout(unsigned int t)
 {
-       t += 59;
-       t -= t % 60;
-       return t;
-}
+       int ret;
 
-/* watchdog timer handling */
-
-static void wdt_keepalive(void)
-{
-       if (test_bit(WDTS_USE_GP, &wdt_status))
-               inb(base);
-       else if (test_bit(WDTS_USE_CIR, &wdt_status))
-               /* The timer reloads with around 5 msec delay */
-               outb(0x55, CIR_DR(base));
-       else {
-               if (superio_enter())
-                       return;
-
-               superio_select(GPIO);
-               wdt_update_timeout();
-               superio_exit();
-       }
-       set_bit(WDTS_KEEPALIVE, &wdt_status);
-}
-
-static int wdt_start(void)
-{
-       int ret = superio_enter();
+       ret = superio_enter();
        if (ret)
                return ret;
 
        superio_select(GPIO);
-       if (test_bit(WDTS_USE_GP, &wdt_status))
-               superio_outb(WDT_GAMEPORT, WDTCTRL);
-       else if (test_bit(WDTS_USE_CIR, &wdt_status))
-               superio_outb(WDT_CIRINT, WDTCTRL);
-       wdt_update_timeout();
-
+       _wdt_update_timeout(t);
        superio_exit();
 
        return 0;
 }
 
-static int wdt_stop(void)
+static int wdt_round_time(int t)
 {
-       int ret = superio_enter();
-       if (ret)
-               return ret;
+       t += 59;
+       t -= t % 60;
+       return t;
+}
 
-       superio_select(GPIO);
-       superio_outb(0x00, WDTCTRL);
-       superio_outb(WDT_TOV1, WDTCFG);
-       superio_outb(0x00, WDTVALLSB);
-       if (max_units > 255)
-               superio_outb(0x00, WDTVALMSB);
+/* watchdog timer handling */
 
-       superio_exit();
-       return 0;
+static int wdt_start(struct watchdog_device *wdd)
+{
+       return wdt_update_timeout(wdd->timeout);
+}
+
+static int wdt_stop(struct watchdog_device *wdd)
+{
+       return wdt_update_timeout(0);
 }
 
 /**
@@ -325,292 +232,44 @@ static int wdt_stop(void)
  *     Used within WDIOC_SETTIMEOUT watchdog device ioctl.
  */
 
-static int wdt_set_timeout(int t)
+static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
 {
-       if (t < 1 || t > max_units * 60)
-               return -EINVAL;
+       int ret = 0;
 
        if (t > max_units)
-               timeout = wdt_round_time(t);
-       else
-               timeout = t;
-
-       if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
-               int ret = superio_enter();
-               if (ret)
-                       return ret;
-
-               superio_select(GPIO);
-               wdt_update_timeout();
-               superio_exit();
-       }
-       return 0;
-}
-
-/**
- *     wdt_get_status - determines the status supported by watchdog ioctl
- *     @status: status returned to user space
- *
- *     The status bit of the device does not allow to distinguish
- *     between a regular system reset and a watchdog forced reset.
- *     But, in test mode it is useful, so it is supported through
- *     WDIOC_GETSTATUS watchdog ioctl. Additionally the driver
- *     reports the keepalive signal and the acception of the magic.
- *
- *     Used within WDIOC_GETSTATUS watchdog device ioctl.
- */
-
-static int wdt_get_status(int *status)
-{
-       *status = 0;
-       if (testmode) {
-               int ret = superio_enter();
-               if (ret)
-                       return ret;
-
-               superio_select(GPIO);
-               if (superio_inb(WDTCTRL) & WDT_ZERO) {
-                       superio_outb(0x00, WDTCTRL);
-                       clear_bit(WDTS_TIMER_RUN, &wdt_status);
-                       *status |= WDIOF_CARDRESET;
-               }
-
-               superio_exit();
-       }
-       if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
-               *status |= WDIOF_KEEPALIVEPING;
-       if (test_bit(WDTS_EXPECTED, &wdt_status))
-               *status |= WDIOF_MAGICCLOSE;
-       return 0;
-}
-
-/* /dev/watchdog handling */
-
-/**
- *     wdt_open - watchdog file_operations .open
- *     @inode: inode of the device
- *     @file: file handle to the device
- *
- *     The watchdog timer starts by opening the device.
- *
- *     Used within the file operation of the watchdog device.
- */
+               t = wdt_round_time(t);
 
-static int wdt_open(struct inode *inode, struct file *file)
-{
-       if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
-               return -EBUSY;
-       if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
-               int ret;
-               if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
-                       __module_get(THIS_MODULE);
-
-               ret = wdt_start();
-               if (ret) {
-                       clear_bit(WDTS_LOCKED, &wdt_status);
-                       clear_bit(WDTS_TIMER_RUN, &wdt_status);
-                       clear_bit(WDTS_DEV_OPEN, &wdt_status);
-                       return ret;
-               }
-       }
-       return nonseekable_open(inode, file);
-}
+       wdd->timeout = t;
 
-/**
- *     wdt_release - watchdog file_operations .release
- *     @inode: inode of the device
- *     @file: file handle to the device
- *
- *     Closing the watchdog device either stops the watchdog timer
- *     or in the case, that nowayout is set or the magic character
- *     wasn't written, a critical warning about an running watchdog
- *     timer is given.
- *
- *     Used within the file operation of the watchdog device.
- */
+       if (watchdog_hw_running(wdd))
+               ret = wdt_update_timeout(t);
 
-static int wdt_release(struct inode *inode, struct file *file)
-{
-       if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
-               if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
-                       int ret = wdt_stop();
-                       if (ret) {
-                               /*
-                                * Stop failed. Just keep the watchdog alive
-                                * and hope nothing bad happens.
-                                */
-                               set_bit(WDTS_EXPECTED, &wdt_status);
-                               wdt_keepalive();
-                               return ret;
-                       }
-                       clear_bit(WDTS_TIMER_RUN, &wdt_status);
-               } else {
-                       wdt_keepalive();
-                       pr_crit("unexpected close, not stopping watchdog!\n");
-               }
-       }
-       clear_bit(WDTS_DEV_OPEN, &wdt_status);
-       return 0;
-}
-
-/**
- *     wdt_write - watchdog file_operations .write
- *     @file: file handle to the watchdog
- *     @buf: buffer to write
- *     @count: count of bytes
- *     @ppos: pointer to the position to write. No seeks allowed
- *
- *     A write to a watchdog device is defined as a keepalive signal. Any
- *     write of data will do, as we don't define content meaning.
- *
- *     Used within the file operation of the watchdog device.
- */
-
-static ssize_t wdt_write(struct file *file, const char __user *buf,
-                           size_t count, loff_t *ppos)
-{
-       if (count) {
-               clear_bit(WDTS_EXPECTED, &wdt_status);
-               wdt_keepalive();
-       }
-       if (!nowayout) {
-               size_t ofs;
-
-       /* note: just in case someone wrote the magic character long ago */
-               for (ofs = 0; ofs != count; ofs++) {
-                       char c;
-                       if (get_user(c, buf + ofs))
-                               return -EFAULT;
-                       if (c == WD_MAGIC)
-                               set_bit(WDTS_EXPECTED, &wdt_status);
-               }
-       }
-       return count;
+       return ret;
 }
 
 static const struct watchdog_info ident = {
        .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
-       .firmware_version =     1,
+       .firmware_version = 1,
        .identity = WATCHDOG_NAME,
 };
 
-/**
- *     wdt_ioctl - watchdog file_operations .unlocked_ioctl
- *     @file: file handle to the device
- *     @cmd: watchdog command
- *     @arg: argument pointer
- *
- *     The watchdog API defines a common set of functions for all watchdogs
- *     according to their available features.
- *
- *     Used within the file operation of the watchdog device.
- */
-
-static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       int rc = 0, status, new_options, new_timeout;
-       union {
-               struct watchdog_info __user *ident;
-               int __user *i;
-       } uarg;
-
-       uarg.i = (int __user *)arg;
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               return copy_to_user(uarg.ident,
-                                   &ident, sizeof(ident)) ? -EFAULT : 0;
-
-       case WDIOC_GETSTATUS:
-               rc = wdt_get_status(&status);
-               if (rc)
-                       return rc;
-               return put_user(status, uarg.i);
-
-       case WDIOC_GETBOOTSTATUS:
-               return put_user(0, uarg.i);
-
-       case WDIOC_KEEPALIVE:
-               wdt_keepalive();
-               return 0;
-
-       case WDIOC_SETOPTIONS:
-               if (get_user(new_options, uarg.i))
-                       return -EFAULT;
-
-               switch (new_options) {
-               case WDIOS_DISABLECARD:
-                       if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
-                               rc = wdt_stop();
-                               if (rc)
-                                       return rc;
-                       }
-                       clear_bit(WDTS_TIMER_RUN, &wdt_status);
-                       return 0;
-
-               case WDIOS_ENABLECARD:
-                       if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
-                               rc = wdt_start();
-                               if (rc) {
-                                       clear_bit(WDTS_TIMER_RUN, &wdt_status);
-                                       return rc;
-                               }
-                       }
-                       return 0;
-
-               default:
-                       return -EFAULT;
-               }
-
-       case WDIOC_SETTIMEOUT:
-               if (get_user(new_timeout, uarg.i))
-                       return -EFAULT;
-               rc = wdt_set_timeout(new_timeout);
-       case WDIOC_GETTIMEOUT:
-               if (put_user(timeout, uarg.i))
-                       return -EFAULT;
-               return rc;
-
-       default:
-               return -ENOTTY;
-       }
-}
-
-static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
-       void *unused)
-{
-       if (code == SYS_DOWN || code == SYS_HALT)
-               wdt_stop();
-       return NOTIFY_DONE;
-}
-
-static const struct file_operations wdt_fops = {
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .write          = wdt_write,
-       .unlocked_ioctl = wdt_ioctl,
-       .open           = wdt_open,
-       .release        = wdt_release,
+static struct watchdog_ops wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = wdt_start,
+       .stop = wdt_stop,
+       .set_timeout = wdt_set_timeout,
 };
 
-static struct miscdevice wdt_miscdev = {
-       .minor          = WATCHDOG_MINOR,
-       .name           = "watchdog",
-       .fops           = &wdt_fops,
-};
-
-static struct notifier_block wdt_notifier = {
-       .notifier_call = wdt_notify_sys,
+static struct watchdog_device wdt_dev = {
+       .info = &ident,
+       .ops = &wdt_ops,
+       .min_timeout = 1,
 };
 
 static int __init it87_wdt_init(void)
 {
-       int rc = 0;
-       int try_gameport = !nogameport;
        u8  chip_rev;
-       int gp_rreq_fail = 0;
-
-       wdt_status = 0;
+       int rc;
 
        rc = superio_enter();
        if (rc)
@@ -631,14 +290,20 @@ static int __init it87_wdt_init(void)
        case IT8726_ID:
                max_units = 65535;
                break;
+       case IT8607_ID:
        case IT8620_ID:
+       case IT8622_ID:
+       case IT8625_ID:
+       case IT8628_ID:
+       case IT8655_ID:
+       case IT8665_ID:
+       case IT8686_ID:
        case IT8718_ID:
        case IT8720_ID:
        case IT8721_ID:
        case IT8728_ID:
        case IT8783_ID:
                max_units = 65535;
-               try_gameport = 0;
                break;
        case IT8705_ID:
                pr_err("Unsupported Chip found, Chip %04x Revision %02x\n",
@@ -660,48 +325,7 @@ static int __init it87_wdt_init(void)
        superio_select(GPIO);
        superio_outb(WDT_TOV1, WDTCFG);
        superio_outb(0x00, WDTCTRL);
-
-       /* First try to get Gameport support */
-       if (try_gameport) {
-               superio_select(GAMEPORT);
-               base = superio_inw(BASEREG);
-               if (!base) {
-                       base = GP_BASE_DEFAULT;
-                       superio_outw(base, BASEREG);
-               }
-               gpact = superio_inb(ACTREG);
-               superio_outb(0x01, ACTREG);
-               if (request_region(base, 1, WATCHDOG_NAME))
-                       set_bit(WDTS_USE_GP, &wdt_status);
-               else
-                       gp_rreq_fail = 1;
-       }
-
-       /* If we haven't Gameport support, try to get CIR support */
-       if (!nocir && !test_bit(WDTS_USE_GP, &wdt_status)) {
-               if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
-                       if (gp_rreq_fail)
-                               pr_err("I/O Address 0x%04x and 0x%04x already in use\n",
-                                      base, CIR_BASE);
-                       else
-                               pr_err("I/O Address 0x%04x already in use\n",
-                                      CIR_BASE);
-                       rc = -EIO;
-                       goto err_out;
-               }
-               base = CIR_BASE;
-
-               superio_select(CIR);
-               superio_outw(base, BASEREG);
-               superio_outb(0x00, CIR_ILS);
-               ciract = superio_inb(ACTREG);
-               superio_outb(0x01, ACTREG);
-               if (gp_rreq_fail) {
-                       superio_select(GAMEPORT);
-                       superio_outb(gpact, ACTREG);
-               }
-               set_bit(WDTS_USE_CIR, &wdt_status);
-       }
+       superio_exit();
 
        if (timeout < 1 || timeout > max_units * 60) {
                timeout = DEFAULT_TIMEOUT;
@@ -712,83 +336,25 @@ static int __init it87_wdt_init(void)
        if (timeout > max_units)
                timeout = wdt_round_time(timeout);
 
-       rc = register_reboot_notifier(&wdt_notifier);
-       if (rc) {
-               pr_err("Cannot register reboot notifier (err=%d)\n", rc);
-               goto err_out_region;
-       }
+       wdt_dev.timeout = timeout;
+       wdt_dev.max_timeout = max_units * 60;
 
-       rc = misc_register(&wdt_miscdev);
+       watchdog_stop_on_reboot(&wdt_dev);
+       rc = watchdog_register_device(&wdt_dev);
        if (rc) {
-               pr_err("Cannot register miscdev on minor=%d (err=%d)\n",
-                      wdt_miscdev.minor, rc);
-               goto err_out_reboot;
-       }
-
-       /* Initialize CIR to use it as keepalive source */
-       if (test_bit(WDTS_USE_CIR, &wdt_status)) {
-               outb(0x00, CIR_RCR(base));
-               outb(0xc0, CIR_TCR1(base));
-               outb(0x5c, CIR_TCR2(base));
-               outb(0x10, CIR_IER(base));
-               outb(0x00, CIR_BDHR(base));
-               outb(0x01, CIR_BDLR(base));
-               outb(0x09, CIR_IER(base));
+               pr_err("Cannot register watchdog device (err=%d)\n", rc);
+               return rc;
        }
 
-       pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d nocir=%d)\n",
-               chip_type, chip_rev, timeout,
-               nowayout, testmode, exclusive, nogameport, nocir);
+       pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
+               chip_type, chip_rev, timeout, nowayout, testmode);
 
-       superio_exit();
        return 0;
-
-err_out_reboot:
-       unregister_reboot_notifier(&wdt_notifier);
-err_out_region:
-       if (test_bit(WDTS_USE_GP, &wdt_status))
-               release_region(base, 1);
-       else if (test_bit(WDTS_USE_CIR, &wdt_status)) {
-               release_region(base, 8);
-               superio_select(CIR);
-               superio_outb(ciract, ACTREG);
-       }
-err_out:
-       if (try_gameport) {
-               superio_select(GAMEPORT);
-               superio_outb(gpact, ACTREG);
-       }
-
-       superio_exit();
-       return rc;
 }
 
 static void __exit it87_wdt_exit(void)
 {
-       if (superio_enter() == 0) {
-               superio_select(GPIO);
-               superio_outb(0x00, WDTCTRL);
-               superio_outb(0x00, WDTCFG);
-               superio_outb(0x00, WDTVALLSB);
-               if (max_units > 255)
-                       superio_outb(0x00, WDTVALMSB);
-               if (test_bit(WDTS_USE_GP, &wdt_status)) {
-                       superio_select(GAMEPORT);
-                       superio_outb(gpact, ACTREG);
-               } else if (test_bit(WDTS_USE_CIR, &wdt_status)) {
-                       superio_select(CIR);
-                       superio_outb(ciract, ACTREG);
-               }
-               superio_exit();
-       }
-
-       misc_deregister(&wdt_miscdev);
-       unregister_reboot_notifier(&wdt_notifier);
-
-       if (test_bit(WDTS_USE_GP, &wdt_status))
-               release_region(base, 1);
-       else if (test_bit(WDTS_USE_CIR, &wdt_status))
-               release_region(base, 8);
+       watchdog_unregister_device(&wdt_dev);
 }
 
 module_init(it87_wdt_init);
index 45d4766..69a5a57 100644 (file)
@@ -203,7 +203,9 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev)
        if (IS_ERR(data->clk))
                return PTR_ERR(data->clk);
 
-       clk_prepare_enable(data->clk);
+       ret = clk_prepare_enable(data->clk);
+       if (ret)
+               return ret;
 
        platform_set_drvdata(pdev, data);
 
index 39be4dd..83af7d6 100644 (file)
@@ -651,5 +651,5 @@ module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("platform:orion_wdt");
diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c
new file mode 100644 (file)
index 0000000..e618218
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Renesas RZ/A Series WDT Driver
+ *
+ * Copyright (C) 2017 Renesas Electronics America, Inc.
+ * Copyright (C) 2017 Chris Brandt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+#define DEFAULT_TIMEOUT                30
+
+/* Watchdog Timer Registers */
+#define WTCSR                  0
+#define WTCSR_MAGIC            0xA500
+#define WTSCR_WT               BIT(6)
+#define WTSCR_TME              BIT(5)
+#define WTSCR_CKS(i)           (i)
+
+#define WTCNT                  2
+#define WTCNT_MAGIC            0x5A00
+
+#define WRCSR                  4
+#define WRCSR_MAGIC            0x5A00
+#define WRCSR_RSTE             BIT(6)
+#define WRCSR_CLEAR_WOVF       0xA500  /* special value */
+
+struct rza_wdt {
+       struct watchdog_device wdev;
+       void __iomem *base;
+       struct clk *clk;
+};
+
+static int rza_wdt_start(struct watchdog_device *wdev)
+{
+       struct rza_wdt *priv = watchdog_get_drvdata(wdev);
+
+       /* Stop timer */
+       writew(WTCSR_MAGIC | 0, priv->base + WTCSR);
+
+       /* Must dummy read WRCSR:WOVF at least once before clearing */
+       readb(priv->base + WRCSR);
+       writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR);
+
+       /*
+        * Start timer with slowest clock source and reset option enabled.
+        */
+       writew(WRCSR_MAGIC | WRCSR_RSTE, priv->base + WRCSR);
+       writew(WTCNT_MAGIC | 0, priv->base + WTCNT);
+       writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME | WTSCR_CKS(7),
+              priv->base + WTCSR);
+
+       return 0;
+}
+
+static int rza_wdt_stop(struct watchdog_device *wdev)
+{
+       struct rza_wdt *priv = watchdog_get_drvdata(wdev);
+
+       writew(WTCSR_MAGIC | 0, priv->base + WTCSR);
+
+       return 0;
+}
+
+static int rza_wdt_ping(struct watchdog_device *wdev)
+{
+       struct rza_wdt *priv = watchdog_get_drvdata(wdev);
+
+       writew(WTCNT_MAGIC | 0, priv->base + WTCNT);
+
+       return 0;
+}
+
+static int rza_wdt_restart(struct watchdog_device *wdev, unsigned long action,
+                           void *data)
+{
+       struct rza_wdt *priv = watchdog_get_drvdata(wdev);
+
+       /* Stop timer */
+       writew(WTCSR_MAGIC | 0, priv->base + WTCSR);
+
+       /* Must dummy read WRCSR:WOVF at least once before clearing */
+       readb(priv->base + WRCSR);
+       writew(WRCSR_CLEAR_WOVF, priv->base + WRCSR);
+
+       /*
+        * Start timer with fastest clock source and only 1 clock left before
+        * overflow with reset option enabled.
+        */
+       writew(WRCSR_MAGIC | WRCSR_RSTE, priv->base + WRCSR);
+       writew(WTCNT_MAGIC | 255, priv->base + WTCNT);
+       writew(WTCSR_MAGIC | WTSCR_WT | WTSCR_TME, priv->base + WTCSR);
+
+       /*
+        * Actually make sure the above sequence hits hardware before sleeping.
+        */
+       wmb();
+
+       /* Wait for WDT overflow (reset) */
+       udelay(20);
+
+       return 0;
+}
+
+static const struct watchdog_info rza_wdt_ident = {
+       .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
+       .identity = "Renesas RZ/A WDT Watchdog",
+};
+
+static const struct watchdog_ops rza_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = rza_wdt_start,
+       .stop = rza_wdt_stop,
+       .ping = rza_wdt_ping,
+       .restart = rza_wdt_restart,
+};
+
+static int rza_wdt_probe(struct platform_device *pdev)
+{
+       struct rza_wdt *priv;
+       struct resource *res;
+       unsigned long rate;
+       int ret;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       priv->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(priv->clk))
+               return PTR_ERR(priv->clk);
+
+       rate = clk_get_rate(priv->clk);
+       if (rate < 16384) {
+               dev_err(&pdev->dev, "invalid clock rate (%ld)\n", rate);
+               return -ENOENT;
+       }
+
+       /* Assume slowest clock rate possible (CKS=7) */
+       rate /= 16384;
+
+       priv->wdev.info = &rza_wdt_ident,
+       priv->wdev.ops = &rza_wdt_ops,
+       priv->wdev.parent = &pdev->dev;
+
+       /*
+        * Since the max possible timeout of our 8-bit count register is less
+        * than a second, we must use max_hw_heartbeat_ms.
+        */
+       priv->wdev.max_hw_heartbeat_ms = (1000 * U8_MAX) / rate;
+       dev_dbg(&pdev->dev, "max hw timeout of %dms\n",
+                priv->wdev.max_hw_heartbeat_ms);
+
+       priv->wdev.min_timeout = 1;
+       priv->wdev.timeout = DEFAULT_TIMEOUT;
+
+       watchdog_init_timeout(&priv->wdev, 0, &pdev->dev);
+       watchdog_set_drvdata(&priv->wdev, priv);
+
+       ret = devm_watchdog_register_device(&pdev->dev, &priv->wdev);
+       if (ret)
+               dev_err(&pdev->dev, "Cannot register watchdog device\n");
+
+       return ret;
+}
+
+static const struct of_device_id rza_wdt_of_match[] = {
+       { .compatible = "renesas,rza-wdt", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rza_wdt_of_match);
+
+static struct platform_driver rza_wdt_driver = {
+       .probe = rza_wdt_probe,
+       .driver = {
+               .name = "rza_wdt",
+               .of_match_table = rza_wdt_of_match,
+       },
+};
+
+module_platform_driver(rza_wdt_driver);
+
+MODULE_DESCRIPTION("Renesas RZ/A WDT Driver");
+MODULE_AUTHOR("Chris Brandt <chris.brandt@renesas.com>");
+MODULE_LICENSE("GPL v2");
index 6ed9759..adaa435 100644 (file)
@@ -1,5 +1,4 @@
-/* linux/drivers/char/watchdog/s3c2410_wdt.c
- *
+/*
  * Copyright (c) 2004 Simtec Electronics
  *     Ben Dooks <ben@simtec.co.uk>
  *
  * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-*/
+ */
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -37,6 +32,7 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
 #include <linux/delay.h>
@@ -94,8 +90,7 @@ MODULE_PARM_DESC(tmr_atboot,
                        __MODULE_STRING(S3C2410_WATCHDOG_ATBOOT));
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
                        __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
-                       "0 to reboot (default 0)");
+MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)");
 
 /**
  * struct s3c2410_wdt_variant - Per-variant config data
@@ -131,7 +126,7 @@ struct s3c2410_wdt {
        unsigned long           wtdat_save;
        struct watchdog_device  wdt_device;
        struct notifier_block   freq_transition;
-       struct s3c2410_wdt_variant *drv_data;
+       const struct s3c2410_wdt_variant *drv_data;
        struct regmap *pmureg;
 };
 
@@ -310,7 +305,8 @@ static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt)
        return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
 }
 
-static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout)
+static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
+                                   unsigned int timeout)
 {
        struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
        unsigned long freq = clk_get_rate(wdt->clock);
@@ -401,7 +397,7 @@ static const struct watchdog_ops s3c2410wdt_ops = {
        .restart = s3c2410wdt_restart,
 };
 
-static struct watchdog_device s3c2410_wdd = {
+static const struct watchdog_device s3c2410_wdd = {
        .info = &s3c2410_wdt_ident,
        .ops = &s3c2410wdt_ops,
        .timeout = S3C2410_WATCHDOG_DEFAULT_TIME,
@@ -507,22 +503,24 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
        return 0;
 }
 
-static inline struct s3c2410_wdt_variant *
+static inline const struct s3c2410_wdt_variant *
 s3c2410_get_wdt_drv_data(struct platform_device *pdev)
 {
-       if (pdev->dev.of_node) {
-               const struct of_device_id *match;
-               match = of_match_node(s3c2410_wdt_match, pdev->dev.of_node);
-               return (struct s3c2410_wdt_variant *)match->data;
-       } else {
-               return (struct s3c2410_wdt_variant *)
-                       platform_get_device_id(pdev)->driver_data;
+       const struct s3c2410_wdt_variant *variant;
+
+       variant = of_device_get_match_data(&pdev->dev);
+       if (!variant) {
+               /* Device matched by platform_device_id */
+               variant = (struct s3c2410_wdt_variant *)
+                          platform_get_device_id(pdev)->driver_data;
        }
+
+       return variant;
 }
 
 static int s3c2410wdt_probe(struct platform_device *pdev)
 {
-       struct device *dev;
+       struct device *dev = &pdev->dev;
        struct s3c2410_wdt *wdt;
        struct resource *wdt_mem;
        struct resource *wdt_irq;
@@ -530,13 +528,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
        int started = 0;
        int ret;
 
-       dev = &pdev->dev;
-
        wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
        if (!wdt)
                return -ENOMEM;
 
-       wdt->dev = &pdev->dev;
+       wdt->dev = dev;
        spin_lock_init(&wdt->lock);
        wdt->wdt_device = s3c2410_wdd;
 
@@ -592,7 +588,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
        /* see if we can actually set the requested timer margin, and if
         * not, try the default value */
 
-       watchdog_init_timeout(&wdt->wdt_device, tmr_margin, &pdev->dev);
+       watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev);
        ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
                                        wdt->wdt_device.timeout);
        if (ret) {
@@ -601,11 +597,10 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
 
                if (started == 0)
                        dev_info(dev,
-                          "tmr_margin value out of range, default %d used\n",
-                              S3C2410_WATCHDOG_DEFAULT_TIME);
+                                "tmr_margin value out of range, default %d used\n",
+                                S3C2410_WATCHDOG_DEFAULT_TIME);
                else
-                       dev_info(dev, "default timer value is out of range, "
-                                                       "cannot start\n");
+                       dev_info(dev, "default timer value is out of range, cannot start\n");
        }
 
        ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0,
@@ -619,7 +614,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
        watchdog_set_restart_priority(&wdt->wdt_device, 128);
 
        wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
-       wdt->wdt_device.parent = &pdev->dev;
+       wdt->wdt_device.parent = dev;
 
        ret = watchdog_register_device(&wdt->wdt_device);
        if (ret) {
@@ -754,7 +749,6 @@ static struct platform_driver s3c2410wdt_driver = {
 
 module_platform_driver(s3c2410wdt_driver);
 
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "
-             "Dimitry Andric <dimitry.andric@tomtom.com>");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Dimitry Andric <dimitry.andric@tomtom.com>");
 MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
 MODULE_LICENSE("GPL");
index 362fd22..0ae947c 100644 (file)
@@ -228,15 +228,13 @@ static int sama5d4_wdt_probe(struct platform_device *pdev)
 
        wdt->reg_base = regs;
 
-       if (pdev->dev.of_node) {
-               irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
-               if (!irq)
-                       dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
+       irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+       if (!irq)
+               dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
 
-               ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
-               if (ret)
-                       return ret;
-       }
+       ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
+       if (ret)
+               return ret;
 
        if ((wdt->mr & AT91_WDT_WDFIEN) && irq) {
                ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler,
@@ -302,6 +300,11 @@ static int sama5d4_wdt_resume(struct device *dev)
 {
        struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
 
+       /*
+        * FIXME: writing MR also pings the watchdog which may not be desired.
+        * This should only be done when the registers are lost on suspend but
+        * there is no way to get this information right now.
+        */
        sama5d4_wdt_init(wdt);
 
        return 0;
diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c
new file mode 100644 (file)
index 0000000..6c501b7
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Driver for STM32 Independent Watchdog
+ *
+ * Copyright (C) Yannick Fertre 2017
+ * Author: Yannick Fertre <yannick.fertre@st.com>
+ *
+ * This driver is based on tegra_wdt.c
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+/* IWDG registers */
+#define IWDG_KR                0x00 /* Key register */
+#define IWDG_PR                0x04 /* Prescaler Register */
+#define IWDG_RLR       0x08 /* ReLoad Register */
+#define IWDG_SR                0x0C /* Status Register */
+#define IWDG_WINR      0x10 /* Windows Register */
+
+/* IWDG_KR register bit mask */
+#define KR_KEY_RELOAD  0xAAAA /* reload counter enable */
+#define KR_KEY_ENABLE  0xCCCC /* peripheral enable */
+#define KR_KEY_EWA     0x5555 /* write access enable */
+#define KR_KEY_DWA     0x0000 /* write access disable */
+
+/* IWDG_PR register bit values */
+#define PR_4           0x00 /* prescaler set to 4 */
+#define PR_8           0x01 /* prescaler set to 8 */
+#define PR_16          0x02 /* prescaler set to 16 */
+#define PR_32          0x03 /* prescaler set to 32 */
+#define PR_64          0x04 /* prescaler set to 64 */
+#define PR_128         0x05 /* prescaler set to 128 */
+#define PR_256         0x06 /* prescaler set to 256 */
+
+/* IWDG_RLR register values */
+#define RLR_MIN                0x07C /* min value supported by reload register */
+#define RLR_MAX                0xFFF /* max value supported by reload register */
+
+/* IWDG_SR register bit mask */
+#define FLAG_PVU       BIT(0) /* Watchdog prescaler value update */
+#define FLAG_RVU       BIT(1) /* Watchdog counter reload value update */
+
+/* set timeout to 100000 us */
+#define TIMEOUT_US     100000
+#define SLEEP_US       1000
+
+struct stm32_iwdg {
+       struct watchdog_device  wdd;
+       void __iomem            *regs;
+       struct clk              *clk;
+       unsigned int            rate;
+};
+
+static inline u32 reg_read(void __iomem *base, u32 reg)
+{
+       return readl_relaxed(base + reg);
+}
+
+static inline void reg_write(void __iomem *base, u32 reg, u32 val)
+{
+       writel_relaxed(val, base + reg);
+}
+
+static int stm32_iwdg_start(struct watchdog_device *wdd)
+{
+       struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
+       u32 val = FLAG_PVU | FLAG_RVU;
+       u32 reload;
+       int ret;
+
+       dev_dbg(wdd->parent, "%s\n", __func__);
+
+       /* prescaler fixed to 256 */
+       reload = clamp_t(unsigned int, ((wdd->timeout * wdt->rate) / 256) - 1,
+                        RLR_MIN, RLR_MAX);
+
+       /* enable write access */
+       reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA);
+
+       /* set prescaler & reload registers */
+       reg_write(wdt->regs, IWDG_PR, PR_256); /* prescaler fix to 256 */
+       reg_write(wdt->regs, IWDG_RLR, reload);
+       reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE);
+
+       /* wait for the registers to be updated (max 100ms) */
+       ret = readl_relaxed_poll_timeout(wdt->regs + IWDG_SR, val,
+                                        !(val & (FLAG_PVU | FLAG_RVU)),
+                                        SLEEP_US, TIMEOUT_US);
+       if (ret) {
+               dev_err(wdd->parent,
+                       "Fail to set prescaler or reload registers\n");
+               return ret;
+       }
+
+       /* reload watchdog */
+       reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD);
+
+       return 0;
+}
+
+static int stm32_iwdg_ping(struct watchdog_device *wdd)
+{
+       struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
+
+       dev_dbg(wdd->parent, "%s\n", __func__);
+
+       /* reload watchdog */
+       reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD);
+
+       return 0;
+}
+
+static int stm32_iwdg_set_timeout(struct watchdog_device *wdd,
+                                 unsigned int timeout)
+{
+       dev_dbg(wdd->parent, "%s timeout: %d sec\n", __func__, timeout);
+
+       wdd->timeout = timeout;
+
+       if (watchdog_active(wdd))
+               return stm32_iwdg_start(wdd);
+
+       return 0;
+}
+
+static const struct watchdog_info stm32_iwdg_info = {
+       .options        = WDIOF_SETTIMEOUT |
+                         WDIOF_MAGICCLOSE |
+                         WDIOF_KEEPALIVEPING,
+       .identity       = "STM32 Independent Watchdog",
+};
+
+static struct watchdog_ops stm32_iwdg_ops = {
+       .owner          = THIS_MODULE,
+       .start          = stm32_iwdg_start,
+       .ping           = stm32_iwdg_ping,
+       .set_timeout    = stm32_iwdg_set_timeout,
+};
+
+static int stm32_iwdg_probe(struct platform_device *pdev)
+{
+       struct watchdog_device *wdd;
+       struct stm32_iwdg *wdt;
+       struct resource *res;
+       void __iomem *regs;
+       struct clk *clk;
+       int ret;
+
+       /* This is the timer base. */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(regs)) {
+               dev_err(&pdev->dev, "Could not get resource\n");
+               return PTR_ERR(regs);
+       }
+
+       clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "Unable to get clock\n");
+               return PTR_ERR(clk);
+       }
+
+       ret = clk_prepare_enable(clk);
+       if (ret) {
+               dev_err(&pdev->dev, "Unable to prepare clock %p\n", clk);
+               return ret;
+       }
+
+       /*
+        * Allocate our watchdog driver data, which has the
+        * struct watchdog_device nested within it.
+        */
+       wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+       if (!wdt) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       /* Initialize struct stm32_iwdg. */
+       wdt->regs = regs;
+       wdt->clk = clk;
+       wdt->rate = clk_get_rate(clk);
+
+       /* Initialize struct watchdog_device. */
+       wdd = &wdt->wdd;
+       wdd->info = &stm32_iwdg_info;
+       wdd->ops = &stm32_iwdg_ops;
+       wdd->min_timeout = ((RLR_MIN + 1) * 256) / wdt->rate;
+       wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * 256 * 1000) / wdt->rate;
+       wdd->parent = &pdev->dev;
+
+       watchdog_set_drvdata(wdd, wdt);
+       watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
+
+       ret = watchdog_init_timeout(wdd, 0, &pdev->dev);
+       if (ret)
+               dev_warn(&pdev->dev,
+                        "unable to set timeout value, using default\n");
+
+       ret = watchdog_register_device(wdd);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register watchdog device\n");
+               goto err;
+       }
+
+       platform_set_drvdata(pdev, wdt);
+
+       return 0;
+err:
+       clk_disable_unprepare(clk);
+
+       return ret;
+}
+
+static int stm32_iwdg_remove(struct platform_device *pdev)
+{
+       struct stm32_iwdg *wdt = platform_get_drvdata(pdev);
+
+       watchdog_unregister_device(&wdt->wdd);
+       clk_disable_unprepare(wdt->clk);
+
+       return 0;
+}
+
+static const struct of_device_id stm32_iwdg_of_match[] = {
+       { .compatible = "st,stm32-iwdg" },
+       { /* end node */ }
+};
+MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
+
+static struct platform_driver stm32_iwdg_driver = {
+       .probe          = stm32_iwdg_probe,
+       .remove         = stm32_iwdg_remove,
+       .driver = {
+               .name   = "iwdg",
+               .of_match_table = stm32_iwdg_of_match,
+       },
+};
+module_platform_driver(stm32_iwdg_driver);
+
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 Independent Watchdog Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/watchdog/uniphier_wdt.c b/drivers/watchdog/uniphier_wdt.c
new file mode 100644 (file)
index 0000000..0ea2339
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Watchdog driver for the UniPhier watchdog timer
+ *
+ * (c) Copyright 2014 Panasonic Corporation
+ * (c) Copyright 2016 Socionext Inc.
+ * All rights reserved.
+ *
+ * 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/bitops.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+/* WDT timer setting register */
+#define WDTTIMSET                      0x3004
+#define   WDTTIMSET_PERIOD_MASK                (0xf << 0)
+#define   WDTTIMSET_PERIOD_1_SEC       (0x3 << 0)
+
+/* WDT reset selection register */
+#define WDTRSTSEL                      0x3008
+#define   WDTRSTSEL_RSTSEL_MASK                (0x3 << 0)
+#define   WDTRSTSEL_RSTSEL_BOTH                (0x0 << 0)
+#define   WDTRSTSEL_RSTSEL_IRQ_ONLY    (0x2 << 0)
+
+/* WDT control register */
+#define WDTCTRL                                0x300c
+#define   WDTCTRL_STATUS               BIT(8)
+#define   WDTCTRL_CLEAR                        BIT(1)
+#define   WDTCTRL_ENABLE               BIT(0)
+
+#define SEC_TO_WDTTIMSET_PRD(sec) \
+               (ilog2(sec) + WDTTIMSET_PERIOD_1_SEC)
+
+#define WDTST_TIMEOUT                  1000 /* usec */
+
+#define WDT_DEFAULT_TIMEOUT            64   /* Default is 64 seconds */
+#define WDT_PERIOD_MIN                 1
+#define WDT_PERIOD_MAX                 128
+
+static unsigned int timeout = 0;
+static bool nowayout = WATCHDOG_NOWAYOUT;
+
+struct uniphier_wdt_dev {
+       struct watchdog_device wdt_dev;
+       struct regmap   *regmap;
+};
+
+/*
+ * UniPhier Watchdog operations
+ */
+static int uniphier_watchdog_ping(struct watchdog_device *w)
+{
+       struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
+       unsigned int val;
+       int ret;
+
+       /* Clear counter */
+       ret = regmap_write_bits(wdev->regmap, WDTCTRL,
+                               WDTCTRL_CLEAR, WDTCTRL_CLEAR);
+       if (!ret)
+               /*
+                * As SoC specification, after clear counter,
+                * it needs to wait until counter status is 1.
+                */
+               ret = regmap_read_poll_timeout(wdev->regmap, WDTCTRL, val,
+                                              (val & WDTCTRL_STATUS),
+                                              0, WDTST_TIMEOUT);
+
+       return ret;
+}
+
+static int __uniphier_watchdog_start(struct regmap *regmap, unsigned int sec)
+{
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read_poll_timeout(regmap, WDTCTRL, val,
+                                      !(val & WDTCTRL_STATUS),
+                                      0, WDTST_TIMEOUT);
+       if (ret)
+               return ret;
+
+       /* Setup period */
+       ret = regmap_write(regmap, WDTTIMSET,
+                          SEC_TO_WDTTIMSET_PRD(sec));
+       if (ret)
+               return ret;
+
+       /* Enable and clear watchdog */
+       ret = regmap_write(regmap, WDTCTRL, WDTCTRL_ENABLE | WDTCTRL_CLEAR);
+       if (!ret)
+               /*
+                * As SoC specification, after clear counter,
+                * it needs to wait until counter status is 1.
+                */
+               ret = regmap_read_poll_timeout(regmap, WDTCTRL, val,
+                                              (val & WDTCTRL_STATUS),
+                                              0, WDTST_TIMEOUT);
+
+       return ret;
+}
+
+static int __uniphier_watchdog_stop(struct regmap *regmap)
+{
+       /* Disable and stop watchdog */
+       return regmap_write_bits(regmap, WDTCTRL, WDTCTRL_ENABLE, 0);
+}
+
+static int __uniphier_watchdog_restart(struct regmap *regmap, unsigned int sec)
+{
+       int ret;
+
+       ret = __uniphier_watchdog_stop(regmap);
+       if (ret)
+               return ret;
+
+       return __uniphier_watchdog_start(regmap, sec);
+}
+
+static int uniphier_watchdog_start(struct watchdog_device *w)
+{
+       struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
+       unsigned int tmp_timeout;
+
+       tmp_timeout = roundup_pow_of_two(w->timeout);
+
+       return __uniphier_watchdog_start(wdev->regmap, tmp_timeout);
+}
+
+static int uniphier_watchdog_stop(struct watchdog_device *w)
+{
+       struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
+
+       return __uniphier_watchdog_stop(wdev->regmap);
+}
+
+static int uniphier_watchdog_set_timeout(struct watchdog_device *w,
+                                        unsigned int t)
+{
+       struct uniphier_wdt_dev *wdev = watchdog_get_drvdata(w);
+       unsigned int tmp_timeout;
+       int ret;
+
+       tmp_timeout = roundup_pow_of_two(t);
+       if (tmp_timeout == w->timeout)
+               return 0;
+
+       if (watchdog_active(w)) {
+               ret = __uniphier_watchdog_restart(wdev->regmap, tmp_timeout);
+               if (ret)
+                       return ret;
+       }
+
+       w->timeout = tmp_timeout;
+
+       return 0;
+}
+
+/*
+ * Kernel Interfaces
+ */
+static const struct watchdog_info uniphier_wdt_info = {
+       .identity       = "uniphier-wdt",
+       .options        = WDIOF_SETTIMEOUT |
+                         WDIOF_KEEPALIVEPING |
+                         WDIOF_MAGICCLOSE |
+                         WDIOF_OVERHEAT,
+};
+
+static const struct watchdog_ops uniphier_wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = uniphier_watchdog_start,
+       .stop           = uniphier_watchdog_stop,
+       .ping           = uniphier_watchdog_ping,
+       .set_timeout    = uniphier_watchdog_set_timeout,
+};
+
+static int uniphier_wdt_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct uniphier_wdt_dev *wdev;
+       struct regmap *regmap;
+       struct device_node *parent;
+       int ret;
+
+       wdev = devm_kzalloc(dev, sizeof(*wdev), GFP_KERNEL);
+       if (!wdev)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, wdev);
+
+       parent = of_get_parent(dev->of_node); /* parent should be syscon node */
+       regmap = syscon_node_to_regmap(parent);
+       of_node_put(parent);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       wdev->regmap = regmap;
+       wdev->wdt_dev.info = &uniphier_wdt_info;
+       wdev->wdt_dev.ops = &uniphier_wdt_ops;
+       wdev->wdt_dev.max_timeout = WDT_PERIOD_MAX;
+       wdev->wdt_dev.min_timeout = WDT_PERIOD_MIN;
+       wdev->wdt_dev.parent = dev;
+
+       if (watchdog_init_timeout(&wdev->wdt_dev, timeout, dev) < 0) {
+               wdev->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT;
+       }
+       watchdog_set_nowayout(&wdev->wdt_dev, nowayout);
+       watchdog_stop_on_reboot(&wdev->wdt_dev);
+
+       watchdog_set_drvdata(&wdev->wdt_dev, wdev);
+
+       uniphier_watchdog_stop(&wdev->wdt_dev);
+       ret = regmap_write(wdev->regmap, WDTRSTSEL, WDTRSTSEL_RSTSEL_BOTH);
+       if (ret)
+               return ret;
+
+       ret = devm_watchdog_register_device(dev, &wdev->wdt_dev);
+       if (ret)
+               return ret;
+
+       dev_info(dev, "watchdog driver (timeout=%d sec, nowayout=%d)\n",
+                wdev->wdt_dev.timeout, nowayout);
+
+       return 0;
+}
+
+static const struct of_device_id uniphier_wdt_dt_ids[] = {
+       { .compatible = "socionext,uniphier-wdt" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, uniphier_wdt_dt_ids);
+
+static struct platform_driver uniphier_wdt_driver = {
+       .probe          = uniphier_wdt_probe,
+       .driver         = {
+               .name           = "uniphier-wdt",
+               .of_match_table = uniphier_wdt_dt_ids,
+       },
+};
+
+module_platform_driver(uniphier_wdt_driver);
+
+module_param(timeout, uint, 0000);
+MODULE_PARM_DESC(timeout,
+       "Watchdog timeout seconds in power of 2. (0 < timeout < 128, default="
+                               __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
+
+module_param(nowayout, bool, 0000);
+MODULE_PARM_DESC(nowayout,
+       "Watchdog cannot be stopped once started (default="
+                               __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+MODULE_AUTHOR("Keiji Hayashibara <hayashibara.keiji@socionext.com>");
+MODULE_DESCRIPTION("UniPhier Watchdog Device Driver");
+MODULE_LICENSE("GPL v2");
index 98fd186..d9ba049 100644 (file)
@@ -49,7 +49,8 @@ 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, nct6102 };
+            w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793,
+            nct6795, nct6102 };
 
 static int timeout;                    /* in seconds */
 module_param(timeout, int, 0);
@@ -97,6 +98,8 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
 #define NCT6779_ID             0xc5
 #define NCT6791_ID             0xc8
 #define NCT6792_ID             0xc9
+#define NCT6793_ID             0xd1
+#define NCT6795_ID             0xd3
 
 #define W83627HF_WDT_TIMEOUT   0xf6
 #define W83697HF_WDT_TIMEOUT   0xf4
@@ -204,6 +207,8 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
        case nct6779:
        case nct6791:
        case nct6792:
+       case nct6793:
+       case nct6795:
        case nct6102:
                /*
                 * These chips have a fixed WDTO# output pin (W83627UHG),
@@ -396,6 +401,12 @@ static int wdt_find(int addr)
        case NCT6792_ID:
                ret = nct6792;
                break;
+       case NCT6793_ID:
+               ret = nct6793;
+               break;
+       case NCT6795_ID:
+               ret = nct6795;
+               break;
        case NCT6102_ID:
                ret = nct6102;
                cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
@@ -437,6 +448,8 @@ static int __init wdt_init(void)
                "NCT6779",
                "NCT6791",
                "NCT6792",
+               "NCT6793",
+               "NCT6795",
                "NCT6102",
        };
 
index d5d2bbd..0826e66 100644 (file)
@@ -80,6 +80,9 @@ static struct watchdog_core_data *old_wd_data;
 
 static struct workqueue_struct *watchdog_wq;
 
+static bool handle_boot_enabled =
+       IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED);
+
 static inline bool watchdog_need_worker(struct watchdog_device *wdd)
 {
        /* All variables in milli-seconds */
@@ -192,18 +195,23 @@ static int watchdog_ping(struct watchdog_device *wdd)
        return __watchdog_ping(wdd);
 }
 
+static bool watchdog_worker_should_ping(struct watchdog_core_data *wd_data)
+{
+       struct watchdog_device *wdd = wd_data->wdd;
+
+       return wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd));
+}
+
 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);
+       if (watchdog_worker_should_ping(wd_data))
+               __watchdog_ping(wd_data->wdd);
        mutex_unlock(&wd_data->lock);
 }
 
@@ -956,9 +964,14 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
         * 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);
+               if (handle_boot_enabled) {
+                       __module_get(wdd->ops->owner);
+                       kref_get(&wd_data->kref);
+                       queue_delayed_work(watchdog_wq, &wd_data->work, 0);
+               } else {
+                       pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n",
+                                       wdd->id);
+               }
        }
 
        return 0;
@@ -1106,3 +1119,8 @@ void __exit watchdog_dev_exit(void)
        class_unregister(&watchdog_class);
        destroy_workqueue(watchdog_wq);
 }
+
+module_param(handle_boot_enabled, bool, 0444);
+MODULE_PARM_DESC(handle_boot_enabled,
+       "Watchdog core auto-updates boot enabled watchdogs before userspace takes over (default="
+       __MODULE_STRING(IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED)) ")");
index c982527..69ec585 100644 (file)
@@ -154,7 +154,7 @@ static const struct watchdog_info zx2967_wdt_ident = {
        .identity         =     "zx2967 watchdog",
 };
 
-static struct watchdog_ops zx2967_wdt_ops = {
+static const struct watchdog_ops zx2967_wdt_ops = {
        .owner = THIS_MODULE,
        .start = zx2967_wdt_start,
        .stop = zx2967_wdt_stop,