Merge tag 'linux-watchdog-4.16-rc1' of git://www.linux-watchdog.org/linux-watchdog
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 7 Feb 2018 19:54:34 +0000 (11:54 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 7 Feb 2018 19:54:34 +0000 (11:54 -0800)
Pull watchdog updates from Wim Van Sebroeck:

 - new watchdog device drivers for Realtek RTD1295 and Spreadtrum SC9860
   platform

 - add support for the following devices: jz4780 SoC, AST25xx series SoC
   and r8a77970 SoC

 - convert to watchdog framework: i6300esb_wdt, xen_wdt and sp5100_tco

 - several fixes for watchdog core

 - remove at32ap700x and obsolete documentation

 - gpio: Convert to use GPIO descriptors

 - rename gemini into FTWDT010 as this IP block is generc from Faraday
   Technology

 - various clean-ups and small bugfixes

 - add Guenter Roeck as co-maintainer

 - change maintainers e-mail address

* tag 'linux-watchdog-4.16-rc1' of git://www.linux-watchdog.org/linux-watchdog: (74 commits)
  documentation: watchdog: remove documentation of w83697hf_wdt/w83697ug_wdt
  documentation: watchdog: remove documentation for ixp2000
  documentation: watchdog: remove documentation of at32ap700x_wdt
  watchdog: remove at32ap700x_wdt
  watchdog: sp5100_tco: Add support for recent FCH versions
  watchdog: sp5100-tco: Abort if watchdog is disabled by hardware
  watchdog: sp5100_tco: Use bit operations
  watchdog: sp5100_tco: Convert to use watchdog subsystem
  watchdog: sp5100_tco: Clean up function and variable names
  watchdog: sp5100_tco: Use dev_ print functions where possible
  watchdog: sp5100_tco: Match PCI device early
  watchdog: sp5100_tco: Clean up sp5100_tco_setupdevice
  watchdog: sp5100_tco: Use standard error codes
  watchdog: sp5100_tco: Use request_muxed_region where possible
  watchdog: sp5100_tco: Fix watchdog disable bit
  watchdog: sp5100_tco: Always use SP5100_IO_PM_{INDEX_REG,DATA_REG}
  watchdog: core: make sure the watchdog_worker is not deferred
  watchdog: mt7621: switch to using managed devm_watchdog_register_device()
  watchdog: mt7621: set WDOG_HW_RUNNING bit when appropriate
  watchdog: imx2_wdt: restore previous timeout after suspend+resume
  ...

43 files changed:
Documentation/devicetree/bindings/watchdog/cortina,gemini-watchdog.txt [deleted file]
Documentation/devicetree/bindings/watchdog/faraday,ftwdt010.txt [moved from Documentation/devicetree/bindings/watchdog/cortina,gemin-watchdog.txt with 55% similarity]
Documentation/devicetree/bindings/watchdog/ingenic,jz4740-wdt.txt
Documentation/devicetree/bindings/watchdog/realtek,rtd119x.txt [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/renesas-wdt.txt
Documentation/devicetree/bindings/watchdog/sprd-wdt.txt [new file with mode: 0644]
Documentation/watchdog/watchdog-parameters.txt
MAINTAINERS
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/advantechwdt.c
drivers/watchdog/alim1535_wdt.c
drivers/watchdog/aspeed_wdt.c
drivers/watchdog/at32ap700x_wdt.c [deleted file]
drivers/watchdog/da9062_wdt.c
drivers/watchdog/davinci_wdt.c
drivers/watchdog/dw_wdt.c
drivers/watchdog/eurotechwdt.c
drivers/watchdog/f71808e_wdt.c
drivers/watchdog/ftwdt010_wdt.c [new file with mode: 0644]
drivers/watchdog/gemini_wdt.c [deleted file]
drivers/watchdog/gpio_wdt.c
drivers/watchdog/hpwdt.c
drivers/watchdog/i6300esb.c
drivers/watchdog/ib700wdt.c
drivers/watchdog/imx2_wdt.c
drivers/watchdog/jz4740_wdt.c
drivers/watchdog/mei_wdt.c
drivers/watchdog/mpc8xxx_wdt.c
drivers/watchdog/mt7621_wdt.c
drivers/watchdog/orion_wdt.c
drivers/watchdog/pcwd_pci.c
drivers/watchdog/pcwd_usb.c
drivers/watchdog/rtd119x_wdt.c [new file with mode: 0644]
drivers/watchdog/sp5100_tco.c
drivers/watchdog/sp5100_tco.h
drivers/watchdog/sprd_wdt.c [new file with mode: 0644]
drivers/watchdog/stm32_iwdg.c
drivers/watchdog/sunxi_wdt.c
drivers/watchdog/watchdog_core.c
drivers/watchdog/watchdog_dev.c
drivers/watchdog/wdt_pci.c
drivers/watchdog/xen_wdt.c

diff --git a/Documentation/devicetree/bindings/watchdog/cortina,gemini-watchdog.txt b/Documentation/devicetree/bindings/watchdog/cortina,gemini-watchdog.txt
deleted file mode 100644 (file)
index bc4b865..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-Cortina Systems Gemini SoC Watchdog
-
-Required properties:
-- compatible : must be "cortina,gemini-watchdog"
-- reg : shall contain base register location and length
-- interrupts : shall contain the interrupt for the watchdog
-
-Optional properties:
-- timeout-sec : the default watchdog timeout in seconds.
-
-Example:
-
-watchdog@41000000 {
-       compatible = "cortina,gemini-watchdog";
-       reg = <0x41000000 0x1000>;
-       interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
-};
similarity index 55%
rename from Documentation/devicetree/bindings/watchdog/cortina,gemin-watchdog.txt
rename to Documentation/devicetree/bindings/watchdog/faraday,ftwdt010.txt
index bc4b865d178b4f386ef9fdb9bb4f81e14816d214..9ecdb502e605949f79b88fa7ab03ff90e98b590a 100644 (file)
@@ -1,7 +1,12 @@
-Cortina Systems Gemini SoC Watchdog
+Faraday Technology FTWDT010 watchdog
+
+This is an IP part from Faraday Technology found in the Gemini
+SoCs and others.
 
 Required properties:
-- compatible : must be "cortina,gemini-watchdog"
+- compatible : must be one of
+  "faraday,ftwdt010"
+  "cortina,gemini-watchdog", "faraday,ftwdt010"
 - reg : shall contain base register location and length
 - interrupts : shall contain the interrupt for the watchdog
 
@@ -11,7 +16,7 @@ Optional properties:
 Example:
 
 watchdog@41000000 {
-       compatible = "cortina,gemini-watchdog";
+       compatible = "faraday,ftwdt010";
        reg = <0x41000000 0x1000>;
        interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
 };
index 3c7a1cd13b1011f37753f67905561843ce984370..cb44918f01a8b61b9ebf7444d372cf74c4a6a075 100644 (file)
@@ -1,7 +1,7 @@
-Ingenic Watchdog Timer (WDT) Controller for JZ4740
+Ingenic Watchdog Timer (WDT) Controller for JZ4740 & JZ4780
 
 Required properties:
-compatible: "ingenic,jz4740-watchdog"
+compatible: "ingenic,jz4740-watchdog" or "ingenic,jz4780-watchdog"
 reg: Register address and length for watchdog registers
 
 Example:
diff --git a/Documentation/devicetree/bindings/watchdog/realtek,rtd119x.txt b/Documentation/devicetree/bindings/watchdog/realtek,rtd119x.txt
new file mode 100644 (file)
index 0000000..0565305
--- /dev/null
@@ -0,0 +1,17 @@
+Realtek RTD1295 Watchdog
+========================
+
+Required properties:
+
+- compatible :  Should be "realtek,rtd1295-watchdog"
+- reg        :  Specifies the physical base address and size of registers
+- clocks     :  Specifies one clock input
+
+
+Example:
+
+       watchdog@98007680 {
+               compatible = "realtek,rtd1295-watchdog";
+               reg = <0x98007680 0x100>;
+               clocks = <&osc27M>;
+       };
index bf6d1ca58af7d1989deda93d037b57a23ffd426d..74b2f03c151553f5ce78fd89373f07b09549b2c8 100644 (file)
@@ -4,10 +4,11 @@ Required properties:
 - compatible : Should be "renesas,<soctype>-wdt", and
               "renesas,rcar-gen3-wdt" or "renesas,rza-wdt" as fallback.
               Examples with soctypes are:
+                - "renesas,r7s72100-wdt" (RZ/A1)
                 - "renesas,r8a7795-wdt" (R-Car H3)
                 - "renesas,r8a7796-wdt" (R-Car M3-W)
+                - "renesas,r8a77970-wdt" (R-Car V3M)
                 - "renesas,r8a77995-wdt" (R-Car D3)
-                - "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
diff --git a/Documentation/devicetree/bindings/watchdog/sprd-wdt.txt b/Documentation/devicetree/bindings/watchdog/sprd-wdt.txt
new file mode 100644 (file)
index 0000000..aeaf3e0
--- /dev/null
@@ -0,0 +1,19 @@
+Spreadtrum SoCs Watchdog timer
+
+Required properties:
+- compatible : Should be "sprd,sp9860-wdt".
+- reg : Specifies base physical address and size of the registers.
+- interrupts : Exactly one interrupt specifier.
+- timeout-sec : Contain the default watchdog timeout in seconds.
+- clock-names : Contain the input clock names.
+- clocks : Phandles to input clocks.
+
+Example:
+       watchdog: watchdog@40310000 {
+               compatible = "sprd,sp9860-wdt";
+               reg = <0 0x40310000 0 0x1000>;
+               interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+               timeout-sec = <12>;
+               clock-names = "enable", "rtc_enable";
+               clocks = <&clk_aon_apb_gates1 8>, <&clk_aon_apb_rtc_gates 9>;
+       };
index 6f9d7b4189170f25143d852d9cf2699af211f997..beea975980f63236c7ca507aeb7fd7a2c610ef81 100644 (file)
@@ -40,11 +40,6 @@ margin: Watchdog margin in seconds (default=60)
 nowayout: Disable watchdog shutdown on close
        (default=kernel config parameter)
 -------------------------------------------------
-at32ap700x_wdt:
-timeout: Timeout value. Limited to be 1 or 2 seconds. (default=2)
-nowayout: Watchdog cannot be stopped once started
-       (default=kernel config parameter)
--------------------------------------------------
 at91rm9200_wdt:
 wdt_time: Watchdog time in seconds. (default=5)
 nowayout: Watchdog cannot be stopped once started
@@ -162,11 +157,6 @@ testmode: Watchdog test mode (1 = no reboot), default=0
 nowayout: Watchdog cannot be stopped once started
        (default=kernel config parameter)
 -------------------------------------------------
-ixp2000_wdt:
-heartbeat: Watchdog heartbeat in seconds (default 60s)
-nowayout: Watchdog cannot be stopped once started
-       (default=kernel config parameter)
--------------------------------------------------
 ixp4xx_wdt:
 heartbeat: Watchdog heartbeat in seconds (default 60s)
 nowayout: Watchdog cannot be stopped once started
@@ -381,19 +371,6 @@ timeout: Watchdog timeout in seconds. 1 <= timeout <= 255, default=60.
 nowayout: Watchdog cannot be stopped once started
        (default=kernel config parameter)
 -------------------------------------------------
-w83697hf_wdt:
-wdt_io: w83697hf/hg WDT io port (default 0x2e, 0 = autodetect)
-timeout: Watchdog timeout in seconds. 1<= timeout <=255 (default=60)
-nowayout: Watchdog cannot be stopped once started
-       (default=kernel config parameter)
-early_disable: Watchdog gets disabled at boot time (default=1)
--------------------------------------------------
-w83697ug_wdt:
-wdt_io: w83697ug/uf WDT io port (default 0x2e)
-timeout: Watchdog timeout in seconds. 1<= timeout <=255 (default=60)
-nowayout: Watchdog cannot be stopped once started
-       (default=kernel config parameter)
--------------------------------------------------
 w83877f_wdt:
 timeout: Watchdog timeout in seconds. (1<=timeout<=3600, default=30)
 nowayout: Watchdog cannot be stopped once started
index ba3842a9e73ef3392426ecfbbd04ec3decc0fa8a..c344e9e1177afb241231302b78c2c93689d03729 100644 (file)
@@ -14991,8 +14991,8 @@ S:      Maintained
 F:     drivers/input/tablet/wacom_serial4.c
 
 WATCHDOG DEVICE DRIVERS
-M:     Wim Van Sebroeck <wim@iguana.be>
-R:     Guenter Roeck <linux@roeck-us.net>
+M:     Wim Van Sebroeck <wim@linux-watchdog.org>
+M:     Guenter Roeck <linux@roeck-us.net>
 L:     linux-watchdog@vger.kernel.org
 W:     http://www.linux-watchdog.org/
 T:     git git://www.linux-watchdog.org/linux-watchdog.git
index 5bf613d3b7d61fbb8e52ceca58c6a49689a45422..aff773bcebdb59f14a88a3c92b779f5c03de3ba5 100644 (file)
@@ -328,16 +328,18 @@ config 977_WATCHDOG
 
          Not sure? It's safe to say N.
 
-config GEMINI_WATCHDOG
-       tristate "Gemini watchdog"
-       depends on ARCH_GEMINI
+config FTWDT010_WATCHDOG
+       tristate "Faraday Technology FTWDT010 watchdog"
+       depends on ARM || COMPILE_TEST
        select WATCHDOG_CORE
+       default ARCH_GEMINI
        help
-         Say Y here if to include support for the watchdog timer
-         embedded in the Cortina Systems Gemini family of devices.
+         Say Y here if to include support for the Faraday Technology
+         FTWDT010 watchdog timer embedded in the Cortina Systems Gemini
+         family of devices.
 
          To compile this driver as a module, choose M here: the
-         module will be called gemini_wdt.
+         module will be called ftwdt010_wdt.
 
 config IXP4XX_WATCHDOG
        tristate "IXP4xx Watchdog"
@@ -748,12 +750,12 @@ config RENESAS_RZAWDT
          Renesas RZ/A SoCs. These watchdogs can be used to reset a system.
 
 config ASPEED_WATCHDOG
-       tristate "Aspeed 2400 watchdog support"
+       tristate "Aspeed BMC watchdog support"
        depends on ARCH_ASPEED || COMPILE_TEST
        select WATCHDOG_CORE
        help
          Say Y here to include support for the watchdog timer
-         in Apseed BMC SoCs.
+         in Aspeed BMC SoCs.
 
          This driver is required to reboot the SoC.
 
@@ -794,14 +796,23 @@ config UNIPHIER_WATCHDOG
          To compile this driver as a module, choose M here: the
          module will be called uniphier_wdt.
 
-# AVR32 Architecture
+config RTD119X_WATCHDOG
+       bool "Realtek RTD119x/RTD129x watchdog support"
+       depends on ARCH_REALTEK || COMPILE_TEST
+       depends on OF
+       select WATCHDOG_CORE
+       default ARCH_REALTEK
+       help
+         Say Y here to include support for the watchdog timer in
+         Realtek RTD1295 SoCs.
 
-config AT32AP700X_WDT
-       tristate "AT32AP700x watchdog"
-       depends on CPU_AT32AP700X || COMPILE_TEST
+config SPRD_WATCHDOG
+       tristate "Spreadtrum watchdog support"
+       depends on ARCH_SPRD || COMPILE_TEST
+       select WATCHDOG_CORE
        help
-         Watchdog timer embedded into AT32AP700x devices. This will reboot
-         your system when the timeout is reached.
+         Say Y here to include watchdog timer supported
+         by Spreadtrum system.
 
 # BLACKFIN Architecture
 
@@ -1458,7 +1469,7 @@ config RC32434_WDT
 
 config INDYDOG
        tristate "Indy/I2 Hardware Watchdog"
-       depends on SGI_HAS_INDYDOG || (MIPS && COMPILE_TEST)
+       depends on SGI_HAS_INDYDOG
        help
          Hardware driver for the Indy's/I2's watchdog. This is a
          watchdog timer that will reboot the machine after a 60 second
index 135c5e81f25ea5a4ca823683f4ceab8d26a7e304..0474d38aa854fb2dd4f2db14bcfa167a13b4a5ab 100644 (file)
@@ -46,7 +46,7 @@ obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
 obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o
 obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
 obj-$(CONFIG_977_WATCHDOG) += wdt977.o
-obj-$(CONFIG_GEMINI_WATCHDOG) += gemini_wdt.o
+obj-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o
 obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
 obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
 obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
@@ -88,9 +88,8 @@ 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
+obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o
+obj-$(CONFIG_SPRD_WATCHDOG) += sprd_wdt.o
 
 # BLACKFIN Architecture
 obj-$(CONFIG_BFIN_WDT) += bfin_wdt.o
index 7d7db0c5a64e6cec4bd5311f0ddeb4f6f0faf9f2..f61944369c1ade3608b500834cefd7a9e6e8aba0 100644 (file)
@@ -181,7 +181,7 @@ static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                if (advwdt_set_heartbeat(new_timeout))
                        return -EINVAL;
                advwdt_ping();
-               /* Fall */
+               /* fall through */
        case WDIOC_GETTIMEOUT:
                return put_user(timeout, p);
        default:
index 3a17fbd39f8aed015f00ecafdbb5839a2e8296f4..60f0c2eb8531253e447ff03ef975a1e80d8fdfd5 100644 (file)
@@ -223,8 +223,8 @@ static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                if (ali_settimer(new_timeout))
                        return -EINVAL;
                ali_keepalive();
-               /* Fall */
        }
+               /* fall through */
        case WDIOC_GETTIMEOUT:
                return put_user(timeout, p);
        default:
index 79cc766cd30fdc411e112c1a318d32589627c7b6..ca5b91e2eb9257c1be5da003fec6282dff64ca5a 100644 (file)
@@ -243,9 +243,13 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
        if (of_property_read_bool(np, "aspeed,external-signal"))
                wdt->ctrl |= WDT_CTRL_WDT_EXT;
 
-       writel(wdt->ctrl, wdt->base + WDT_CTRL);
-
        if (readl(wdt->base + WDT_CTRL) & WDT_CTRL_ENABLE)  {
+               /*
+                * The watchdog is running, but invoke aspeed_wdt_start() to
+                * write wdt->ctrl to WDT_CTRL to ensure the watchdog's
+                * configuration conforms to the driver's expectations.
+                * Primarily, ensure we're using the 1MHz clock source.
+                */
                aspeed_wdt_start(&wdt->wdd);
                set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
        }
@@ -312,7 +316,18 @@ static struct platform_driver aspeed_watchdog_driver = {
                .of_match_table = of_match_ptr(aspeed_wdt_of_table),
        },
 };
-module_platform_driver(aspeed_watchdog_driver);
+
+static int __init aspeed_wdt_init(void)
+{
+       return platform_driver_register(&aspeed_watchdog_driver);
+}
+arch_initcall(aspeed_wdt_init);
+
+static void __exit aspeed_wdt_exit(void)
+{
+       platform_driver_unregister(&aspeed_watchdog_driver);
+}
+module_exit(aspeed_wdt_exit);
 
 MODULE_DESCRIPTION("Aspeed Watchdog Driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c
deleted file mode 100644 (file)
index 81ba892..0000000
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * Watchdog driver for Atmel AT32AP700X devices
- *
- * Copyright (C) 2005-2006 Atmel Corporation
- *
- * 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.
- *
- *
- * Errata: WDT Clear is blocked after WDT Reset
- *
- * A watchdog timer event will, after reset, block writes to the WDT_CLEAR
- * register, preventing the program to clear the next Watchdog Timer Reset.
- *
- * If you still want to use the WDT after a WDT reset a small code can be
- * insterted at the startup checking the AVR32_PM.rcause register for WDT reset
- * and use a GPIO pin to reset the system. This method requires that one of the
- * GPIO pins are available and connected externally to the RESET_N pin. After
- * the GPIO pin has pulled down the reset line the GPIO will be reset and leave
- * the pin tristated with pullup.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/miscdevice.h>
-#include <linux/fs.h>
-#include <linux/platform_device.h>
-#include <linux/watchdog.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-
-#define TIMEOUT_MIN            1
-#define TIMEOUT_MAX            2
-#define TIMEOUT_DEFAULT                TIMEOUT_MAX
-
-/* module parameters */
-static int timeout =  TIMEOUT_DEFAULT;
-module_param(timeout, int, 0);
-MODULE_PARM_DESC(timeout,
-               "Timeout value. Limited to be 1 or 2 seconds. (default="
-               __MODULE_STRING(TIMEOUT_DEFAULT) ")");
-
-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) ")");
-
-/* Watchdog registers and write/read macro */
-#define WDT_CTRL               0x00
-#define WDT_CTRL_EN               0
-#define WDT_CTRL_PSEL             8
-#define WDT_CTRL_KEY             24
-
-#define WDT_CLR                        0x04
-
-#define WDT_RCAUSE             0x10
-#define WDT_RCAUSE_POR            0
-#define WDT_RCAUSE_EXT            2
-#define WDT_RCAUSE_WDT            3
-#define WDT_RCAUSE_JTAG                   4
-#define WDT_RCAUSE_SERP                   5
-
-#define WDT_BIT(name)          (1 << WDT_##name)
-#define WDT_BF(name, value)    ((value) << WDT_##name)
-
-#define wdt_readl(dev, reg)                            \
-       __raw_readl((dev)->regs + WDT_##reg)
-#define wdt_writel(dev, reg, value)                    \
-       __raw_writel((value), (dev)->regs + WDT_##reg)
-
-struct wdt_at32ap700x {
-       void __iomem            *regs;
-       spinlock_t              io_lock;
-       int                     timeout;
-       int                     boot_status;
-       unsigned long           users;
-       struct miscdevice       miscdev;
-};
-
-static struct wdt_at32ap700x *wdt;
-static char expect_release;
-
-/*
- * Disable the watchdog.
- */
-static inline void at32_wdt_stop(void)
-{
-       unsigned long psel;
-
-       spin_lock(&wdt->io_lock);
-       psel = wdt_readl(wdt, CTRL) & WDT_BF(CTRL_PSEL, 0x0f);
-       wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0x55));
-       wdt_writel(wdt, CTRL, psel | WDT_BF(CTRL_KEY, 0xaa));
-       spin_unlock(&wdt->io_lock);
-}
-
-/*
- * Enable and reset the watchdog.
- */
-static inline void at32_wdt_start(void)
-{
-       /* 0xf is 2^16 divider = 2 sec, 0xe is 2^15 divider = 1 sec */
-       unsigned long psel = (wdt->timeout > 1) ? 0xf : 0xe;
-
-       spin_lock(&wdt->io_lock);
-       wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN)
-                       | WDT_BF(CTRL_PSEL, psel)
-                       | WDT_BF(CTRL_KEY, 0x55));
-       wdt_writel(wdt, CTRL, WDT_BIT(CTRL_EN)
-                       | WDT_BF(CTRL_PSEL, psel)
-                       | WDT_BF(CTRL_KEY, 0xaa));
-       spin_unlock(&wdt->io_lock);
-}
-
-/*
- * Pat the watchdog timer.
- */
-static inline void at32_wdt_pat(void)
-{
-       spin_lock(&wdt->io_lock);
-       wdt_writel(wdt, CLR, 0x42);
-       spin_unlock(&wdt->io_lock);
-}
-
-/*
- * Watchdog device is opened, and watchdog starts running.
- */
-static int at32_wdt_open(struct inode *inode, struct file *file)
-{
-       if (test_and_set_bit(1, &wdt->users))
-               return -EBUSY;
-
-       at32_wdt_start();
-       return nonseekable_open(inode, file);
-}
-
-/*
- * Close the watchdog device.
- */
-static int at32_wdt_close(struct inode *inode, struct file *file)
-{
-       if (expect_release == 42) {
-               at32_wdt_stop();
-       } else {
-               dev_dbg(wdt->miscdev.parent,
-                       "unexpected close, not stopping watchdog!\n");
-               at32_wdt_pat();
-       }
-       clear_bit(1, &wdt->users);
-       expect_release = 0;
-       return 0;
-}
-
-/*
- * Change the watchdog time interval.
- */
-static int at32_wdt_settimeout(int time)
-{
-       /*
-        * All counting occurs at 1 / SLOW_CLOCK (32 kHz) and max prescaler is
-        * 2 ^ 16 allowing up to 2 seconds timeout.
-        */
-       if ((time < TIMEOUT_MIN) || (time > TIMEOUT_MAX))
-               return -EINVAL;
-
-       /*
-        * Set new watchdog time. It will be used when at32_wdt_start() is
-        * called.
-        */
-       wdt->timeout = time;
-       return 0;
-}
-
-/*
- * Get the watchdog status.
- */
-static int at32_wdt_get_status(void)
-{
-       int rcause;
-       int status = 0;
-
-       rcause = wdt_readl(wdt, RCAUSE);
-
-       switch (rcause) {
-       case WDT_BIT(RCAUSE_EXT):
-               status = WDIOF_EXTERN1;
-               break;
-       case WDT_BIT(RCAUSE_WDT):
-               status = WDIOF_CARDRESET;
-               break;
-       case WDT_BIT(RCAUSE_POR):  /* fall through */
-       case WDT_BIT(RCAUSE_JTAG): /* fall through */
-       case WDT_BIT(RCAUSE_SERP): /* fall through */
-       default:
-               break;
-       }
-
-       return status;
-}
-
-static const struct watchdog_info at32_wdt_info = {
-       .identity       = "at32ap700x watchdog",
-       .options        = WDIOF_SETTIMEOUT |
-                         WDIOF_KEEPALIVEPING |
-                         WDIOF_MAGICCLOSE,
-};
-
-/*
- * Handle commands from user-space.
- */
-static long at32_wdt_ioctl(struct file *file,
-                               unsigned int cmd, unsigned long arg)
-{
-       int ret = -ENOTTY;
-       int time;
-       void __user *argp = (void __user *)arg;
-       int __user *p = argp;
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               ret = copy_to_user(argp, &at32_wdt_info,
-                               sizeof(at32_wdt_info)) ? -EFAULT : 0;
-               break;
-       case WDIOC_GETSTATUS:
-               ret = put_user(0, p);
-               break;
-       case WDIOC_GETBOOTSTATUS:
-               ret = put_user(wdt->boot_status, p);
-               break;
-       case WDIOC_SETOPTIONS:
-               ret = get_user(time, p);
-               if (ret)
-                       break;
-               if (time & WDIOS_DISABLECARD)
-                       at32_wdt_stop();
-               if (time & WDIOS_ENABLECARD)
-                       at32_wdt_start();
-               ret = 0;
-               break;
-       case WDIOC_KEEPALIVE:
-               at32_wdt_pat();
-               ret = 0;
-               break;
-       case WDIOC_SETTIMEOUT:
-               ret = get_user(time, p);
-               if (ret)
-                       break;
-               ret = at32_wdt_settimeout(time);
-               if (ret)
-                       break;
-               /* Enable new time value */
-               at32_wdt_start();
-               /* fall through */
-       case WDIOC_GETTIMEOUT:
-               ret = put_user(wdt->timeout, p);
-               break;
-       }
-
-       return ret;
-}
-
-static ssize_t at32_wdt_write(struct file *file, const char __user *data,
-                               size_t len, loff_t *ppos)
-{
-       /* See if we got the magic character 'V' and reload the timer */
-       if (len) {
-               if (!nowayout) {
-                       size_t i;
-
-                       /*
-                        * note: just in case someone wrote the magic
-                        * character five months ago...
-                        */
-                       expect_release = 0;
-
-                       /*
-                        * scan to see whether or not we got the magic
-                        * character
-                        */
-                       for (i = 0; i != len; i++) {
-                               char c;
-                               if (get_user(c, data + i))
-                                       return -EFAULT;
-                               if (c == 'V')
-                                       expect_release = 42;
-                       }
-               }
-               /* someone wrote to us, we should pat the watchdog */
-               at32_wdt_pat();
-       }
-       return len;
-}
-
-static const struct file_operations at32_wdt_fops = {
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .unlocked_ioctl = at32_wdt_ioctl,
-       .open           = at32_wdt_open,
-       .release        = at32_wdt_close,
-       .write          = at32_wdt_write,
-};
-
-static int __init at32_wdt_probe(struct platform_device *pdev)
-{
-       struct resource *regs;
-       int ret;
-
-       if (wdt) {
-               dev_dbg(&pdev->dev, "only 1 wdt instance supported.\n");
-               return -EBUSY;
-       }
-
-       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!regs) {
-               dev_dbg(&pdev->dev, "missing mmio resource\n");
-               return -ENXIO;
-       }
-
-       wdt = devm_kzalloc(&pdev->dev, sizeof(struct wdt_at32ap700x),
-                       GFP_KERNEL);
-       if (!wdt)
-               return -ENOMEM;
-
-       wdt->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
-       if (!wdt->regs) {
-               ret = -ENOMEM;
-               dev_dbg(&pdev->dev, "could not map I/O memory\n");
-               goto err_free;
-       }
-
-       spin_lock_init(&wdt->io_lock);
-       wdt->boot_status = at32_wdt_get_status();
-
-       /* Work-around for watchdog silicon errata. */
-       if (wdt->boot_status & WDIOF_CARDRESET) {
-               dev_info(&pdev->dev, "CPU must be reset with external "
-                               "reset or POR due to silicon errata.\n");
-               ret = -EIO;
-               goto err_free;
-       } else {
-               wdt->users = 0;
-       }
-
-       wdt->miscdev.minor      = WATCHDOG_MINOR;
-       wdt->miscdev.name       = "watchdog";
-       wdt->miscdev.fops       = &at32_wdt_fops;
-       wdt->miscdev.parent     = &pdev->dev;
-
-       platform_set_drvdata(pdev, wdt);
-
-       if (at32_wdt_settimeout(timeout)) {
-               at32_wdt_settimeout(TIMEOUT_DEFAULT);
-               dev_dbg(&pdev->dev,
-                       "default timeout invalid, set to %d sec.\n",
-                       TIMEOUT_DEFAULT);
-       }
-
-       ret = misc_register(&wdt->miscdev);
-       if (ret) {
-               dev_dbg(&pdev->dev, "failed to register wdt miscdev\n");
-               goto err_free;
-       }
-
-       dev_info(&pdev->dev,
-               "AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n",
-               wdt->regs, wdt->timeout, nowayout);
-
-       return 0;
-
-err_free:
-       wdt = NULL;
-       return ret;
-}
-
-static int __exit at32_wdt_remove(struct platform_device *pdev)
-{
-       if (wdt && platform_get_drvdata(pdev) == wdt) {
-               /* Stop the timer before we leave */
-               if (!nowayout)
-                       at32_wdt_stop();
-
-               misc_deregister(&wdt->miscdev);
-               wdt = NULL;
-       }
-       return 0;
-}
-
-static void at32_wdt_shutdown(struct platform_device *pdev)
-{
-       at32_wdt_stop();
-}
-
-#ifdef CONFIG_PM
-static int at32_wdt_suspend(struct platform_device *pdev, pm_message_t message)
-{
-       at32_wdt_stop();
-       return 0;
-}
-
-static int at32_wdt_resume(struct platform_device *pdev)
-{
-       if (wdt->users)
-               at32_wdt_start();
-       return 0;
-}
-#else
-#define at32_wdt_suspend NULL
-#define at32_wdt_resume NULL
-#endif
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:at32_wdt");
-
-static struct platform_driver at32_wdt_driver = {
-       .remove         = __exit_p(at32_wdt_remove),
-       .suspend        = at32_wdt_suspend,
-       .resume         = at32_wdt_resume,
-       .driver         = {
-               .name   = "at32_wdt",
-       },
-       .shutdown       = at32_wdt_shutdown,
-};
-
-module_platform_driver_probe(at32_wdt_driver, at32_wdt_probe);
-
-MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
-MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X");
-MODULE_LICENSE("GPL");
index 9083d3d922b0b4304a3623b307c3b6a66e99f2ef..814dff6045a443095c59773f9106bdc805f2423c 100644 (file)
@@ -46,22 +46,6 @@ static void da9062_set_window_start(struct da9062_watchdog *wdt)
        wdt->j_time_stamp = jiffies;
 }
 
-static void da9062_apply_window_protection(struct da9062_watchdog *wdt)
-{
-       unsigned long delay = msecs_to_jiffies(DA9062_RESET_PROTECTION_MS);
-       unsigned long timeout = wdt->j_time_stamp + delay;
-       unsigned long now = jiffies;
-       unsigned int diff_ms;
-
-       /* if time-limit has not elapsed then wait for remainder */
-       if (time_before(now, timeout)) {
-               diff_ms = jiffies_to_msecs(timeout-now);
-               dev_dbg(wdt->hw->dev,
-                       "Kicked too quickly. Delaying %u msecs\n", diff_ms);
-               msleep(diff_ms);
-       }
-}
-
 static unsigned int da9062_wdt_timeout_to_sel(unsigned int secs)
 {
        unsigned int i;
@@ -78,8 +62,6 @@ static int da9062_reset_watchdog_timer(struct da9062_watchdog *wdt)
 {
        int ret;
 
-       da9062_apply_window_protection(wdt);
-
        ret = regmap_update_bits(wdt->hw->regmap,
                           DA9062AA_CONTROL_F,
                           DA9062AA_WATCHDOG_MASK,
@@ -100,6 +82,13 @@ static int da9062_wdt_update_timeout_register(struct da9062_watchdog *wdt,
        if (ret)
                return ret;
 
+       regmap_update_bits(chip->regmap,
+                                 DA9062AA_CONTROL_D,
+                                 DA9062AA_TWDSCALE_MASK,
+                                 DA9062_TWDSCALE_DISABLE);
+
+       usleep_range(150, 300);
+
        return regmap_update_bits(chip->regmap,
                                  DA9062AA_CONTROL_D,
                                  DA9062AA_TWDSCALE_MASK,
@@ -175,6 +164,25 @@ static int da9062_wdt_set_timeout(struct watchdog_device *wdd,
        return ret;
 }
 
+static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action,
+                             void *data)
+{
+       struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd);
+       int ret;
+
+       ret = regmap_write(wdt->hw->regmap,
+                          DA9062AA_CONTROL_F,
+                          DA9062AA_SHUTDOWN_MASK);
+       if (ret)
+               dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n",
+                         ret);
+
+       /* wait for reset to assert... */
+       mdelay(500);
+
+       return ret;
+}
+
 static const struct watchdog_info da9062_watchdog_info = {
        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
        .identity = "DA9062 WDT",
@@ -186,6 +194,7 @@ static const struct watchdog_ops da9062_watchdog_ops = {
        .stop = da9062_wdt_stop,
        .ping = da9062_wdt_ping,
        .set_timeout = da9062_wdt_set_timeout,
+       .restart = da9062_wdt_restart,
 };
 
 static const struct of_device_id da9062_compatible_id_table[] = {
@@ -215,10 +224,13 @@ static int da9062_wdt_probe(struct platform_device *pdev)
        wdt->wdtdev.ops = &da9062_watchdog_ops;
        wdt->wdtdev.min_timeout = DA9062_WDT_MIN_TIMEOUT;
        wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
+       wdt->wdtdev.min_hw_heartbeat_ms = DA9062_RESET_PROTECTION_MS;
        wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
        wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
        wdt->wdtdev.parent = &pdev->dev;
 
+       watchdog_set_restart_priority(&wdt->wdtdev, 128);
+
        watchdog_set_drvdata(&wdt->wdtdev, wdt);
 
        ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
index 2f46487af86d05a72216030e188feb74777b546e..3e4c592c239f5a7701beafaa939d3d4d438ba9e8 100644 (file)
@@ -140,6 +140,42 @@ static unsigned int davinci_wdt_get_timeleft(struct watchdog_device *wdd)
        return wdd->timeout - timer_counter;
 }
 
+static int davinci_wdt_restart(struct watchdog_device *wdd,
+                              unsigned long action, void *data)
+{
+       struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd);
+       u32 tgcr, wdtcr;
+
+       /* disable, internal clock source */
+       iowrite32(0, davinci_wdt->base + TCR);
+
+       /* reset timer, set mode to 64-bit watchdog, and unreset */
+       tgcr = 0;
+       iowrite32(tgcr, davinci_wdt->base + TGCR);
+       tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET;
+       iowrite32(tgcr, davinci_wdt->base + TGCR);
+
+       /* clear counter and period regs */
+       iowrite32(0, davinci_wdt->base + TIM12);
+       iowrite32(0, davinci_wdt->base + TIM34);
+       iowrite32(0, davinci_wdt->base + PRD12);
+       iowrite32(0, davinci_wdt->base + PRD34);
+
+       /* put watchdog in pre-active state */
+       wdtcr = WDKEY_SEQ0 | WDEN;
+       iowrite32(wdtcr, davinci_wdt->base + WDTCR);
+
+       /* put watchdog in active state */
+       wdtcr = WDKEY_SEQ1 | WDEN;
+       iowrite32(wdtcr, davinci_wdt->base + WDTCR);
+
+       /* write an invalid value to the WDKEY field to trigger a restart */
+       wdtcr = 0x00004000;
+       iowrite32(wdtcr, davinci_wdt->base + WDTCR);
+
+       return 0;
+}
+
 static const struct watchdog_info davinci_wdt_info = {
        .options = WDIOF_KEEPALIVEPING,
        .identity = "DaVinci/Keystone Watchdog",
@@ -151,6 +187,7 @@ static const struct watchdog_ops davinci_wdt_ops = {
        .stop           = davinci_wdt_ping,
        .ping           = davinci_wdt_ping,
        .get_timeleft   = davinci_wdt_get_timeleft,
+       .restart        = davinci_wdt_restart,
 };
 
 static int davinci_wdt_probe(struct platform_device *pdev)
@@ -195,6 +232,7 @@ static int davinci_wdt_probe(struct platform_device *pdev)
 
        watchdog_set_drvdata(wdd, davinci_wdt);
        watchdog_set_nowayout(wdd, 1);
+       watchdog_set_restart_priority(wdd, 128);
 
        wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        davinci_wdt->base = devm_ioremap_resource(dev, wdt_mem);
index 36be987ff9efc11290a036740aef16bd0fc96e72..c2f4ff51623015ca32aca20d3ad74ff05fe38e29 100644 (file)
@@ -127,14 +127,27 @@ static int dw_wdt_start(struct watchdog_device *wdd)
 
        dw_wdt_set_timeout(wdd, wdd->timeout);
 
-       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_stop(struct watchdog_device *wdd)
+{
+       struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
+
+       if (!dw_wdt->rst) {
+               set_bit(WDOG_HW_RUNNING, &wdd->status);
+               return 0;
+       }
+
+       reset_control_assert(dw_wdt->rst);
+       reset_control_deassert(dw_wdt->rst);
+
+       return 0;
+}
+
 static int dw_wdt_restart(struct watchdog_device *wdd,
                          unsigned long action, void *data)
 {
@@ -173,6 +186,7 @@ static const struct watchdog_info dw_wdt_ident = {
 static const struct watchdog_ops dw_wdt_ops = {
        .owner          = THIS_MODULE,
        .start          = dw_wdt_start,
+       .stop           = dw_wdt_stop,
        .ping           = dw_wdt_ping,
        .set_timeout    = dw_wdt_set_timeout,
        .get_timeleft   = dw_wdt_get_timeleft,
index 38e96712264f92648304ba8282a2e4ba8be90319..47f77a6fdfd664bc9a817c5ee86bbfee58ee1997 100644 (file)
@@ -290,7 +290,7 @@ static long eurwdt_ioctl(struct file *file,
                eurwdt_timeout = time;
                eurwdt_set_timeout(time);
                spin_unlock(&eurwdt_lock);
-               /* Fall */
+               /* fall through */
 
        case WDIOC_GETTIMEOUT:
                return put_user(eurwdt_timeout, p);
index 8658dba2176816ed02d408081dfdca7e8b41bd99..e0678c14480f2c96619562e6c3d0098a0541189d 100644 (file)
@@ -627,7 +627,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
 
                if (new_options & WDIOS_ENABLECARD)
                        return watchdog_start();
-
+               /* fall through */
 
        case WDIOC_KEEPALIVE:
                watchdog_keepalive();
@@ -641,7 +641,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
                        return -EINVAL;
 
                watchdog_keepalive();
-               /* Fall */
+               /* fall through */
 
        case WDIOC_GETTIMEOUT:
                return put_user(watchdog.timeout, uarg.i);
diff --git a/drivers/watchdog/ftwdt010_wdt.c b/drivers/watchdog/ftwdt010_wdt.c
new file mode 100644 (file)
index 0000000..a9c2912
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Watchdog driver for Faraday Technology FTWDT010
+ *
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * Inspired by the out-of-tree drivers from OpenWRT:
+ * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/watchdog.h>
+
+#define FTWDT010_WDCOUNTER     0x0
+#define FTWDT010_WDLOAD                0x4
+#define FTWDT010_WDRESTART     0x8
+#define FTWDT010_WDCR          0xC
+
+#define WDRESTART_MAGIC                0x5AB9
+
+#define WDCR_CLOCK_5MHZ                BIT(4)
+#define WDCR_WDEXT             BIT(3)
+#define WDCR_WDINTR            BIT(2)
+#define WDCR_SYS_RST           BIT(1)
+#define WDCR_ENABLE            BIT(0)
+
+#define WDT_CLOCK              5000000         /* 5 MHz */
+
+struct ftwdt010_wdt {
+       struct watchdog_device  wdd;
+       struct device           *dev;
+       void __iomem            *base;
+       bool                    has_irq;
+};
+
+static inline
+struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd)
+{
+       return container_of(wdd, struct ftwdt010_wdt, wdd);
+}
+
+static int ftwdt010_wdt_start(struct watchdog_device *wdd)
+{
+       struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
+       u32 enable;
+
+       writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD);
+       writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
+       /* set clock before enabling */
+       enable = WDCR_CLOCK_5MHZ | WDCR_SYS_RST;
+       writel(enable, gwdt->base + FTWDT010_WDCR);
+       if (gwdt->has_irq)
+               enable |= WDCR_WDINTR;
+       enable |= WDCR_ENABLE;
+       writel(enable, gwdt->base + FTWDT010_WDCR);
+
+       return 0;
+}
+
+static int ftwdt010_wdt_stop(struct watchdog_device *wdd)
+{
+       struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
+
+       writel(0, gwdt->base + FTWDT010_WDCR);
+
+       return 0;
+}
+
+static int ftwdt010_wdt_ping(struct watchdog_device *wdd)
+{
+       struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
+
+       writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
+
+       return 0;
+}
+
+static int ftwdt010_wdt_set_timeout(struct watchdog_device *wdd,
+                                 unsigned int timeout)
+{
+       wdd->timeout = timeout;
+       if (watchdog_active(wdd))
+               ftwdt010_wdt_start(wdd);
+
+       return 0;
+}
+
+static irqreturn_t ftwdt010_wdt_interrupt(int irq, void *data)
+{
+       struct ftwdt010_wdt *gwdt = data;
+
+       watchdog_notify_pretimeout(&gwdt->wdd);
+
+       return IRQ_HANDLED;
+}
+
+static const struct watchdog_ops ftwdt010_wdt_ops = {
+       .start          = ftwdt010_wdt_start,
+       .stop           = ftwdt010_wdt_stop,
+       .ping           = ftwdt010_wdt_ping,
+       .set_timeout    = ftwdt010_wdt_set_timeout,
+       .owner          = THIS_MODULE,
+};
+
+static const struct watchdog_info ftwdt010_wdt_info = {
+       .options        = WDIOF_KEEPALIVEPING
+                       | WDIOF_MAGICCLOSE
+                       | WDIOF_SETTIMEOUT,
+       .identity       = KBUILD_MODNAME,
+};
+
+
+static int ftwdt010_wdt_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct ftwdt010_wdt *gwdt;
+       unsigned int reg;
+       int irq;
+       int ret;
+
+       gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL);
+       if (!gwdt)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       gwdt->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(gwdt->base))
+               return PTR_ERR(gwdt->base);
+
+       gwdt->dev = dev;
+       gwdt->wdd.info = &ftwdt010_wdt_info;
+       gwdt->wdd.ops = &ftwdt010_wdt_ops;
+       gwdt->wdd.min_timeout = 1;
+       gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK;
+       gwdt->wdd.parent = dev;
+
+       /*
+        * If 'timeout-sec' unspecified in devicetree, assume a 13 second
+        * default.
+        */
+       gwdt->wdd.timeout = 13U;
+       watchdog_init_timeout(&gwdt->wdd, 0, dev);
+
+       reg = readw(gwdt->base + FTWDT010_WDCR);
+       if (reg & WDCR_ENABLE) {
+               /* Watchdog was enabled by the bootloader, disable it. */
+               reg &= ~WDCR_ENABLE;
+               writel(reg, gwdt->base + FTWDT010_WDCR);
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq) {
+               ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0,
+                                      "watchdog bark", gwdt);
+               if (ret)
+                       return ret;
+               gwdt->has_irq = true;
+       }
+
+       ret = devm_watchdog_register_device(dev, &gwdt->wdd);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register watchdog\n");
+               return ret;
+       }
+
+       /* Set up platform driver data */
+       platform_set_drvdata(pdev, gwdt);
+       dev_info(dev, "FTWDT010 watchdog driver enabled\n");
+
+       return 0;
+}
+
+static int __maybe_unused ftwdt010_wdt_suspend(struct device *dev)
+{
+       struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev);
+       unsigned int reg;
+
+       reg = readw(gwdt->base + FTWDT010_WDCR);
+       reg &= ~WDCR_ENABLE;
+       writel(reg, gwdt->base + FTWDT010_WDCR);
+
+       return 0;
+}
+
+static int __maybe_unused ftwdt010_wdt_resume(struct device *dev)
+{
+       struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev);
+       unsigned int reg;
+
+       if (watchdog_active(&gwdt->wdd)) {
+               reg = readw(gwdt->base + FTWDT010_WDCR);
+               reg |= WDCR_ENABLE;
+               writel(reg, gwdt->base + FTWDT010_WDCR);
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops ftwdt010_wdt_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(ftwdt010_wdt_suspend,
+                               ftwdt010_wdt_resume)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id ftwdt010_wdt_match[] = {
+       { .compatible = "faraday,ftwdt010" },
+       { .compatible = "cortina,gemini-watchdog" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ftwdt010_wdt_match);
+#endif
+
+static struct platform_driver ftwdt010_wdt_driver = {
+       .probe          = ftwdt010_wdt_probe,
+       .driver         = {
+               .name   = "ftwdt010-wdt",
+               .of_match_table = of_match_ptr(ftwdt010_wdt_match),
+               .pm = &ftwdt010_wdt_dev_pm_ops,
+       },
+};
+module_platform_driver(ftwdt010_wdt_driver);
+MODULE_AUTHOR("Linus Walleij");
+MODULE_DESCRIPTION("Watchdog driver for Faraday Technology FTWDT010");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/gemini_wdt.c b/drivers/watchdog/gemini_wdt.c
deleted file mode 100644 (file)
index 8155aa6..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Watchdog driver for Cortina Systems Gemini SoC
- *
- * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
- *
- * Inspired by the out-of-tree drivers from OpenWRT:
- * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
- *
- * 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.
- */
-
-#include <linux/bitops.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/watchdog.h>
-
-#define GEMINI_WDCOUNTER       0x0
-#define GEMINI_WDLOAD          0x4
-#define GEMINI_WDRESTART       0x8
-#define GEMINI_WDCR            0xC
-
-#define WDRESTART_MAGIC                0x5AB9
-
-#define WDCR_CLOCK_5MHZ                BIT(4)
-#define WDCR_SYS_RST           BIT(1)
-#define WDCR_ENABLE            BIT(0)
-
-#define WDT_CLOCK              5000000         /* 5 MHz */
-
-struct gemini_wdt {
-       struct watchdog_device  wdd;
-       struct device           *dev;
-       void __iomem            *base;
-};
-
-static inline
-struct gemini_wdt *to_gemini_wdt(struct watchdog_device *wdd)
-{
-       return container_of(wdd, struct gemini_wdt, wdd);
-}
-
-static int gemini_wdt_start(struct watchdog_device *wdd)
-{
-       struct gemini_wdt *gwdt = to_gemini_wdt(wdd);
-
-       writel(wdd->timeout * WDT_CLOCK, gwdt->base + GEMINI_WDLOAD);
-       writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART);
-       /* set clock before enabling */
-       writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST,
-                       gwdt->base + GEMINI_WDCR);
-       writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE,
-                       gwdt->base + GEMINI_WDCR);
-
-       return 0;
-}
-
-static int gemini_wdt_stop(struct watchdog_device *wdd)
-{
-       struct gemini_wdt *gwdt = to_gemini_wdt(wdd);
-
-       writel(0, gwdt->base + GEMINI_WDCR);
-
-       return 0;
-}
-
-static int gemini_wdt_ping(struct watchdog_device *wdd)
-{
-       struct gemini_wdt *gwdt = to_gemini_wdt(wdd);
-
-       writel(WDRESTART_MAGIC, gwdt->base + GEMINI_WDRESTART);
-
-       return 0;
-}
-
-static int gemini_wdt_set_timeout(struct watchdog_device *wdd,
-                                 unsigned int timeout)
-{
-       wdd->timeout = timeout;
-       if (watchdog_active(wdd))
-               gemini_wdt_start(wdd);
-
-       return 0;
-}
-
-static irqreturn_t gemini_wdt_interrupt(int irq, void *data)
-{
-       struct gemini_wdt *gwdt = data;
-
-       watchdog_notify_pretimeout(&gwdt->wdd);
-
-       return IRQ_HANDLED;
-}
-
-static const struct watchdog_ops gemini_wdt_ops = {
-       .start          = gemini_wdt_start,
-       .stop           = gemini_wdt_stop,
-       .ping           = gemini_wdt_ping,
-       .set_timeout    = gemini_wdt_set_timeout,
-       .owner          = THIS_MODULE,
-};
-
-static const struct watchdog_info gemini_wdt_info = {
-       .options        = WDIOF_KEEPALIVEPING
-                       | WDIOF_MAGICCLOSE
-                       | WDIOF_SETTIMEOUT,
-       .identity       = KBUILD_MODNAME,
-};
-
-
-static int gemini_wdt_probe(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct resource *res;
-       struct gemini_wdt *gwdt;
-       unsigned int reg;
-       int irq;
-       int ret;
-
-       gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL);
-       if (!gwdt)
-               return -ENOMEM;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       gwdt->base = devm_ioremap_resource(dev, res);
-       if (IS_ERR(gwdt->base))
-               return PTR_ERR(gwdt->base);
-
-       irq = platform_get_irq(pdev, 0);
-       if (!irq)
-               return -EINVAL;
-
-       gwdt->dev = dev;
-       gwdt->wdd.info = &gemini_wdt_info;
-       gwdt->wdd.ops = &gemini_wdt_ops;
-       gwdt->wdd.min_timeout = 1;
-       gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK;
-       gwdt->wdd.parent = dev;
-
-       /*
-        * If 'timeout-sec' unspecified in devicetree, assume a 13 second
-        * default.
-        */
-       gwdt->wdd.timeout = 13U;
-       watchdog_init_timeout(&gwdt->wdd, 0, dev);
-
-       reg = readw(gwdt->base + GEMINI_WDCR);
-       if (reg & WDCR_ENABLE) {
-               /* Watchdog was enabled by the bootloader, disable it. */
-               reg &= ~WDCR_ENABLE;
-               writel(reg, gwdt->base + GEMINI_WDCR);
-       }
-
-       ret = devm_request_irq(dev, irq, gemini_wdt_interrupt, 0,
-                              "watchdog bark", gwdt);
-       if (ret)
-               return ret;
-
-       ret = devm_watchdog_register_device(dev, &gwdt->wdd);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to register watchdog\n");
-               return ret;
-       }
-
-       /* Set up platform driver data */
-       platform_set_drvdata(pdev, gwdt);
-       dev_info(dev, "Gemini watchdog driver enabled\n");
-
-       return 0;
-}
-
-static int __maybe_unused gemini_wdt_suspend(struct device *dev)
-{
-       struct gemini_wdt *gwdt = dev_get_drvdata(dev);
-       unsigned int reg;
-
-       reg = readw(gwdt->base + GEMINI_WDCR);
-       reg &= ~WDCR_ENABLE;
-       writel(reg, gwdt->base + GEMINI_WDCR);
-
-       return 0;
-}
-
-static int __maybe_unused gemini_wdt_resume(struct device *dev)
-{
-       struct gemini_wdt *gwdt = dev_get_drvdata(dev);
-       unsigned int reg;
-
-       if (watchdog_active(&gwdt->wdd)) {
-               reg = readw(gwdt->base + GEMINI_WDCR);
-               reg |= WDCR_ENABLE;
-               writel(reg, gwdt->base + GEMINI_WDCR);
-       }
-
-       return 0;
-}
-
-static const struct dev_pm_ops gemini_wdt_dev_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(gemini_wdt_suspend,
-                               gemini_wdt_resume)
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id gemini_wdt_match[] = {
-       { .compatible = "cortina,gemini-watchdog" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, gemini_wdt_match);
-#endif
-
-static struct platform_driver gemini_wdt_driver = {
-       .probe          = gemini_wdt_probe,
-       .driver         = {
-               .name   = "gemini-wdt",
-               .of_match_table = of_match_ptr(gemini_wdt_match),
-               .pm = &gemini_wdt_dev_pm_ops,
-       },
-};
-module_platform_driver(gemini_wdt_driver);
-MODULE_AUTHOR("Linus Walleij");
-MODULE_DESCRIPTION("Watchdog driver for Gemini");
-MODULE_LICENSE("GPL");
index cb66c2f99ff15d2d27e5f5542efcbfe53a993b48..3ade281903412ab96888e246a05012ea8ec21445 100644 (file)
@@ -12,7 +12,8 @@
 #include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/module.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/watchdog.h>
 
@@ -25,8 +26,7 @@ enum {
 };
 
 struct gpio_wdt_priv {
-       int                     gpio;
-       bool                    active_low;
+       struct gpio_desc        *gpiod;
        bool                    state;
        bool                    always_running;
        unsigned int            hw_algo;
@@ -35,11 +35,12 @@ struct gpio_wdt_priv {
 
 static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
 {
-       gpio_set_value_cansleep(priv->gpio, !priv->active_low);
+       /* Eternal ping */
+       gpiod_set_value_cansleep(priv->gpiod, 1);
 
        /* Put GPIO back to tristate */
        if (priv->hw_algo == HW_ALGO_TOGGLE)
-               gpio_direction_input(priv->gpio);
+               gpiod_direction_input(priv->gpiod);
 }
 
 static int gpio_wdt_ping(struct watchdog_device *wdd)
@@ -50,13 +51,13 @@ static int gpio_wdt_ping(struct watchdog_device *wdd)
        case HW_ALGO_TOGGLE:
                /* Toggle output pin */
                priv->state = !priv->state;
-               gpio_set_value_cansleep(priv->gpio, priv->state);
+               gpiod_set_value_cansleep(priv->gpiod, priv->state);
                break;
        case HW_ALGO_LEVEL:
                /* Pulse */
-               gpio_set_value_cansleep(priv->gpio, !priv->active_low);
+               gpiod_set_value_cansleep(priv->gpiod, 1);
                udelay(1);
-               gpio_set_value_cansleep(priv->gpio, priv->active_low);
+               gpiod_set_value_cansleep(priv->gpiod, 0);
                break;
        }
        return 0;
@@ -66,8 +67,8 @@ static int gpio_wdt_start(struct watchdog_device *wdd)
 {
        struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
 
-       priv->state = priv->active_low;
-       gpio_direction_output(priv->gpio, priv->state);
+       priv->state = 0;
+       gpiod_direction_output(priv->gpiod, priv->state);
 
        set_bit(WDOG_HW_RUNNING, &wdd->status);
 
@@ -80,7 +81,8 @@ static int gpio_wdt_stop(struct watchdog_device *wdd)
 
        if (!priv->always_running) {
                gpio_wdt_disable(priv);
-               clear_bit(WDOG_HW_RUNNING, &wdd->status);
+       } else {
+               set_bit(WDOG_HW_RUNNING, &wdd->status);
        }
 
        return 0;
@@ -101,44 +103,38 @@ static const struct watchdog_ops gpio_wdt_ops = {
 
 static int gpio_wdt_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
        struct gpio_wdt_priv *priv;
-       enum of_gpio_flags flags;
+       enum gpiod_flags gflags;
        unsigned int hw_margin;
-       unsigned long f = 0;
        const char *algo;
        int ret;
 
-       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
        platform_set_drvdata(pdev, priv);
 
-       priv->gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
-       if (!gpio_is_valid(priv->gpio))
-               return priv->gpio;
-
-       priv->active_low = flags & OF_GPIO_ACTIVE_LOW;
-
-       ret = of_property_read_string(pdev->dev.of_node, "hw_algo", &algo);
+       ret = of_property_read_string(np, "hw_algo", &algo);
        if (ret)
                return ret;
        if (!strcmp(algo, "toggle")) {
                priv->hw_algo = HW_ALGO_TOGGLE;
-               f = GPIOF_IN;
+               gflags = GPIOD_IN;
        } else if (!strcmp(algo, "level")) {
                priv->hw_algo = HW_ALGO_LEVEL;
-               f = priv->active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+               gflags = GPIOD_OUT_LOW;
        } else {
                return -EINVAL;
        }
 
-       ret = devm_gpio_request_one(&pdev->dev, priv->gpio, f,
-                                   dev_name(&pdev->dev));
-       if (ret)
-               return ret;
+       priv->gpiod = devm_gpiod_get(dev, NULL, gflags);
+       if (IS_ERR(priv->gpiod))
+               return PTR_ERR(priv->gpiod);
 
-       ret = of_property_read_u32(pdev->dev.of_node,
+       ret = of_property_read_u32(np,
                                   "hw_margin_ms", &hw_margin);
        if (ret)
                return ret;
@@ -146,7 +142,7 @@ static int gpio_wdt_probe(struct platform_device *pdev)
        if (hw_margin < 2 || hw_margin > 65535)
                return -EINVAL;
 
-       priv->always_running = of_property_read_bool(pdev->dev.of_node,
+       priv->always_running = of_property_read_bool(np,
                                                     "always-running");
 
        watchdog_set_drvdata(&priv->wdd, priv);
@@ -155,9 +151,9 @@ static int gpio_wdt_probe(struct platform_device *pdev)
        priv->wdd.ops           = &gpio_wdt_ops;
        priv->wdd.min_timeout   = SOFT_TIMEOUT_MIN;
        priv->wdd.max_hw_heartbeat_ms = hw_margin;
-       priv->wdd.parent        = &pdev->dev;
+       priv->wdd.parent        = dev;
 
-       if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
+       if (watchdog_init_timeout(&priv->wdd, 0, dev) < 0)
                priv->wdd.timeout = SOFT_TIMEOUT_DEF;
 
        watchdog_stop_on_reboot(&priv->wdd);
index 67fbe35ce7cfe5438f18b0ffb4ebdbfdc057b8fe..f1f00dfc0e68ce3eb323d7e7bf63ecb4f7ba399a 100644 (file)
@@ -52,6 +52,7 @@ static char expect_release;
 static unsigned long hpwdt_is_open;
 
 static void __iomem *pci_mem_addr;             /* the PCI-memory address */
+static unsigned long __iomem *hpwdt_nmistat;
 static unsigned long __iomem *hpwdt_timer_reg;
 static unsigned long __iomem *hpwdt_timer_con;
 
@@ -475,6 +476,11 @@ static int hpwdt_time_left(void)
 }
 
 #ifdef CONFIG_HPWDT_NMI_DECODING
+static int hpwdt_my_nmi(void)
+{
+       return ioread8(hpwdt_nmistat) & 0x6;
+}
+
 /*
  *     NMI Handler
  */
@@ -486,6 +492,9 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
        if (!hpwdt_nmi_decoding)
                return NMI_DONE;
 
+       if ((ulReason == NMI_UNKNOWN) && !hpwdt_my_nmi())
+               return NMI_DONE;
+
        spin_lock_irqsave(&rom_lock, rom_pl);
        if (!die_nmi_called && !is_icru && !is_uefi)
                asminline_call(&cmn_regs, cru_rom_addr);
@@ -700,7 +709,7 @@ static void dmi_find_icru(const struct dmi_header *dm, void *dummy)
                smbios_proliant_ptr = (struct smbios_proliant_info *) dm;
                if (smbios_proliant_ptr->misc_features & 0x01)
                        is_icru = 1;
-               if (smbios_proliant_ptr->misc_features & 0x408)
+               if (smbios_proliant_ptr->misc_features & 0x1400)
                        is_uefi = 1;
        }
 }
@@ -842,6 +851,7 @@ static int hpwdt_init_one(struct pci_dev *dev,
                retval = -ENOMEM;
                goto error_pci_iomap;
        }
+       hpwdt_nmistat   = pci_mem_addr + 0x6e;
        hpwdt_timer_reg = pci_mem_addr + 0x70;
        hpwdt_timer_con = pci_mem_addr + 0x72;
 
index d7befd58b391a98cf074be706f3230b9e7c26d01..950c71a8bb22fdfe5d6902df618ab5de6f8e8c1e 100644 (file)
  *     Version 0.02
  *  20050210 David Härdeman <david@2gen.com>
  *     Ported driver to kernel 2.6
+ *  20171016 Radu Rendec <rrendec@arista.com>
+ *     Change driver to use the watchdog subsystem
+ *     Add support for multiple 6300ESB devices
  */
 
 /*
  *      Includes, defines, variables, module parameters, ...
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/io.h>
 
 /* Module and version information */
-#define ESB_VERSION "0.05"
 #define ESB_MODULE_NAME "i6300ESB timer"
-#define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION
 
 /* PCI configuration registers */
 #define ESB_CONFIG_REG  0x60            /* Config register                   */
 #define ESB_LOCK_REG    0x68            /* WDT lock register                 */
 
 /* Memory mapped registers */
-#define ESB_TIMER1_REG (BASEADDR + 0x00)/* Timer1 value after each reset     */
-#define ESB_TIMER2_REG (BASEADDR + 0x04)/* Timer2 value after each reset     */
-#define ESB_GINTSR_REG (BASEADDR + 0x08)/* General Interrupt Status Register */
-#define ESB_RELOAD_REG (BASEADDR + 0x0c)/* Reload register                   */
+#define ESB_TIMER1_REG(w) ((w)->base + 0x00)/* Timer1 value after each reset */
+#define ESB_TIMER2_REG(w) ((w)->base + 0x04)/* Timer2 value after each reset */
+#define ESB_GINTSR_REG(w) ((w)->base + 0x08)/* General Interrupt Status Reg  */
+#define ESB_RELOAD_REG(w) ((w)->base + 0x0c)/* Reload register               */
 
 /* Lock register bits */
 #define ESB_WDT_FUNC    (0x01 << 2)   /* Watchdog functionality            */
 #define ESB_UNLOCK1     0x80            /* Step 1 to unlock reset registers  */
 #define ESB_UNLOCK2     0x86            /* Step 2 to unlock reset registers  */
 
-/* internal variables */
-static void __iomem *BASEADDR;
-static DEFINE_SPINLOCK(esb_lock); /* Guards the hardware */
-static unsigned long timer_alive;
-static struct pci_dev *esb_pci;
-static unsigned short triggered; /* The status of the watchdog upon boot */
-static char esb_expect_close;
-
-/* We can only use 1 card due to the /dev/watchdog restriction */
-static int cards_found;
-
 /* module parameters */
 /* 30 sec default heartbeat (1 < heartbeat < 2*1023) */
-#define WATCHDOG_HEARTBEAT 30
-static int heartbeat = WATCHDOG_HEARTBEAT;  /* in seconds */
+#define ESB_HEARTBEAT_MIN      1
+#define ESB_HEARTBEAT_MAX      2046
+#define ESB_HEARTBEAT_DEFAULT  30
+#define ESB_HEARTBEAT_RANGE __MODULE_STRING(ESB_HEARTBEAT_MIN) \
+       "<heartbeat<" __MODULE_STRING(ESB_HEARTBEAT_MAX)
+static int heartbeat; /* in seconds */
 module_param(heartbeat, int, 0);
 MODULE_PARM_DESC(heartbeat,
-               "Watchdog heartbeat in seconds. (1<heartbeat<2046, default="
-                               __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
+       "Watchdog heartbeat in seconds. (" ESB_HEARTBEAT_RANGE
+       ", default=" __MODULE_STRING(ESB_HEARTBEAT_DEFAULT) ")");
 
 static bool nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, bool, 0);
@@ -100,6 +92,15 @@ MODULE_PARM_DESC(nowayout,
                "Watchdog cannot be stopped once started (default="
                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
+/* internal variables */
+struct esb_dev {
+       struct watchdog_device wdd;
+       void __iomem *base;
+       struct pci_dev *pdev;
+};
+
+#define to_esb_dev(wptr) container_of(wptr, struct esb_dev, wdd)
+
 /*
  * Some i6300ESB specific functions
  */
@@ -110,61 +111,58 @@ MODULE_PARM_DESC(nowayout,
  * reload register. After this the appropriate registers can be written
  * to once before they need to be unlocked again.
  */
-static inline void esb_unlock_registers(void)
+static inline void esb_unlock_registers(struct esb_dev *edev)
 {
-       writew(ESB_UNLOCK1, ESB_RELOAD_REG);
-       writew(ESB_UNLOCK2, ESB_RELOAD_REG);
+       writew(ESB_UNLOCK1, ESB_RELOAD_REG(edev));
+       writew(ESB_UNLOCK2, ESB_RELOAD_REG(edev));
 }
 
-static int esb_timer_start(void)
+static int esb_timer_start(struct watchdog_device *wdd)
 {
+       struct esb_dev *edev = to_esb_dev(wdd);
+       int _wdd_nowayout = test_bit(WDOG_NO_WAY_OUT, &wdd->status);
        u8 val;
 
-       spin_lock(&esb_lock);
-       esb_unlock_registers();
-       writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
+       esb_unlock_registers(edev);
+       writew(ESB_WDT_RELOAD, ESB_RELOAD_REG(edev));
        /* Enable or Enable + Lock? */
-       val = ESB_WDT_ENABLE | (nowayout ? ESB_WDT_LOCK : 0x00);
-       pci_write_config_byte(esb_pci, ESB_LOCK_REG, val);
-       spin_unlock(&esb_lock);
+       val = ESB_WDT_ENABLE | (_wdd_nowayout ? ESB_WDT_LOCK : 0x00);
+       pci_write_config_byte(edev->pdev, ESB_LOCK_REG, val);
        return 0;
 }
 
-static int esb_timer_stop(void)
+static int esb_timer_stop(struct watchdog_device *wdd)
 {
+       struct esb_dev *edev = to_esb_dev(wdd);
        u8 val;
 
-       spin_lock(&esb_lock);
        /* First, reset timers as suggested by the docs */
-       esb_unlock_registers();
-       writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
+       esb_unlock_registers(edev);
+       writew(ESB_WDT_RELOAD, ESB_RELOAD_REG(edev));
        /* Then disable the WDT */
-       pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x0);
-       pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val);
-       spin_unlock(&esb_lock);
+       pci_write_config_byte(edev->pdev, ESB_LOCK_REG, 0x0);
+       pci_read_config_byte(edev->pdev, ESB_LOCK_REG, &val);
 
        /* Returns 0 if the timer was disabled, non-zero otherwise */
        return val & ESB_WDT_ENABLE;
 }
 
-static void esb_timer_keepalive(void)
+static int esb_timer_keepalive(struct watchdog_device *wdd)
 {
-       spin_lock(&esb_lock);
-       esb_unlock_registers();
-       writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
+       struct esb_dev *edev = to_esb_dev(wdd);
+
+       esb_unlock_registers(edev);
+       writew(ESB_WDT_RELOAD, ESB_RELOAD_REG(edev));
        /* FIXME: Do we need to flush anything here? */
-       spin_unlock(&esb_lock);
+       return 0;
 }
 
-static int esb_timer_set_heartbeat(int time)
+static int esb_timer_set_heartbeat(struct watchdog_device *wdd,
+               unsigned int time)
 {
+       struct esb_dev *edev = to_esb_dev(wdd);
        u32 val;
 
-       if (time < 0x1 || time > (2 * 0x03ff))
-               return -EINVAL;
-
-       spin_lock(&esb_lock);
-
        /* We shift by 9, so if we are passed a value of 1 sec,
         * val will be 1 << 9 = 512, then write that to two
         * timers => 2 * 512 = 1024 (which is decremented at 1KHz)
@@ -172,162 +170,39 @@ static int esb_timer_set_heartbeat(int time)
        val = time << 9;
 
        /* Write timer 1 */
-       esb_unlock_registers();
-       writel(val, ESB_TIMER1_REG);
+       esb_unlock_registers(edev);
+       writel(val, ESB_TIMER1_REG(edev));
 
        /* Write timer 2 */
-       esb_unlock_registers();
-       writel(val, ESB_TIMER2_REG);
+       esb_unlock_registers(edev);
+       writel(val, ESB_TIMER2_REG(edev));
 
        /* Reload */
-       esb_unlock_registers();
-       writew(ESB_WDT_RELOAD, ESB_RELOAD_REG);
+       esb_unlock_registers(edev);
+       writew(ESB_WDT_RELOAD, ESB_RELOAD_REG(edev));
 
        /* FIXME: Do we need to flush everything out? */
 
        /* Done */
-       heartbeat = time;
-       spin_unlock(&esb_lock);
-       return 0;
-}
-
-/*
- *     /dev/watchdog handling
- */
-
-static int esb_open(struct inode *inode, struct file *file)
-{
-       /* /dev/watchdog can only be opened once */
-       if (test_and_set_bit(0, &timer_alive))
-               return -EBUSY;
-
-       /* Reload and activate timer */
-       esb_timer_start();
-
-       return nonseekable_open(inode, file);
-}
-
-static int esb_release(struct inode *inode, struct file *file)
-{
-       /* Shut off the timer. */
-       if (esb_expect_close == 42)
-               esb_timer_stop();
-       else {
-               pr_crit("Unexpected close, not stopping watchdog!\n");
-               esb_timer_keepalive();
-       }
-       clear_bit(0, &timer_alive);
-       esb_expect_close = 0;
+       wdd->timeout = time;
        return 0;
 }
 
-static ssize_t esb_write(struct file *file, const char __user *data,
-                         size_t len, loff_t *ppos)
-{
-       /* See if we got the magic character 'V' and reload the timer */
-       if (len) {
-               if (!nowayout) {
-                       size_t i;
-
-                       /* note: just in case someone wrote the magic character
-                        * five months ago... */
-                       esb_expect_close = 0;
-
-                       /* scan to see whether or not we got the
-                        * magic character */
-                       for (i = 0; i != len; i++) {
-                               char c;
-                               if (get_user(c, data + i))
-                                       return -EFAULT;
-                               if (c == 'V')
-                                       esb_expect_close = 42;
-                       }
-               }
-
-               /* someone wrote to us, we should reload the timer */
-               esb_timer_keepalive();
-       }
-       return len;
-}
-
-static long esb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       int new_options, retval = -EINVAL;
-       int new_heartbeat;
-       void __user *argp = (void __user *)arg;
-       int __user *p = argp;
-       static const struct watchdog_info ident = {
-               .options =              WDIOF_SETTIMEOUT |
-                                       WDIOF_KEEPALIVEPING |
-                                       WDIOF_MAGICCLOSE,
-               .firmware_version =     0,
-               .identity =             ESB_MODULE_NAME,
-       };
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               return copy_to_user(argp, &ident,
-                                       sizeof(ident)) ? -EFAULT : 0;
-
-       case WDIOC_GETSTATUS:
-               return put_user(0, p);
-
-       case WDIOC_GETBOOTSTATUS:
-               return put_user(triggered, p);
-
-       case WDIOC_SETOPTIONS:
-       {
-               if (get_user(new_options, p))
-                       return -EFAULT;
-
-               if (new_options & WDIOS_DISABLECARD) {
-                       esb_timer_stop();
-                       retval = 0;
-               }
-
-               if (new_options & WDIOS_ENABLECARD) {
-                       esb_timer_start();
-                       retval = 0;
-               }
-               return retval;
-       }
-       case WDIOC_KEEPALIVE:
-               esb_timer_keepalive();
-               return 0;
-
-       case WDIOC_SETTIMEOUT:
-       {
-               if (get_user(new_heartbeat, p))
-                       return -EFAULT;
-               if (esb_timer_set_heartbeat(new_heartbeat))
-                       return -EINVAL;
-               esb_timer_keepalive();
-               /* Fall */
-       }
-       case WDIOC_GETTIMEOUT:
-               return put_user(heartbeat, p);
-       default:
-               return -ENOTTY;
-       }
-}
-
 /*
- *      Kernel Interfaces
+ * Watchdog Subsystem Interfaces
  */
 
-static const struct file_operations esb_fops = {
-       .owner = THIS_MODULE,
-       .llseek = no_llseek,
-       .write = esb_write,
-       .unlocked_ioctl = esb_ioctl,
-       .open = esb_open,
-       .release = esb_release,
+static struct watchdog_info esb_info = {
+       .identity = ESB_MODULE_NAME,
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 };
 
-static struct miscdevice esb_miscdev = {
-       .minor = WATCHDOG_MINOR,
-       .name = "watchdog",
-       .fops = &esb_fops,
+static const struct watchdog_ops esb_ops = {
+       .owner = THIS_MODULE,
+       .start = esb_timer_start,
+       .stop = esb_timer_stop,
+       .set_timeout = esb_timer_set_heartbeat,
+       .ping = esb_timer_keepalive,
 };
 
 /*
@@ -343,38 +218,38 @@ MODULE_DEVICE_TABLE(pci, esb_pci_tbl);
  *      Init & exit routines
  */
 
-static unsigned char esb_getdevice(struct pci_dev *pdev)
+static unsigned char esb_getdevice(struct esb_dev *edev)
 {
-       if (pci_enable_device(pdev)) {
-               pr_err("failed to enable device\n");
+       if (pci_enable_device(edev->pdev)) {
+               dev_err(&edev->pdev->dev, "failed to enable device\n");
                goto err_devput;
        }
 
-       if (pci_request_region(pdev, 0, ESB_MODULE_NAME)) {
-               pr_err("failed to request region\n");
+       if (pci_request_region(edev->pdev, 0, ESB_MODULE_NAME)) {
+               dev_err(&edev->pdev->dev, "failed to request region\n");
                goto err_disable;
        }
 
-       BASEADDR = pci_ioremap_bar(pdev, 0);
-       if (BASEADDR == NULL) {
+       edev->base = pci_ioremap_bar(edev->pdev, 0);
+       if (edev->base == NULL) {
                /* Something's wrong here, BASEADDR has to be set */
-               pr_err("failed to get BASEADDR\n");
+               dev_err(&edev->pdev->dev, "failed to get BASEADDR\n");
                goto err_release;
        }
 
        /* Done */
-       esb_pci = pdev;
+       dev_set_drvdata(&edev->pdev->dev, edev);
        return 1;
 
 err_release:
-       pci_release_region(pdev, 0);
+       pci_release_region(edev->pdev, 0);
 err_disable:
-       pci_disable_device(pdev);
+       pci_disable_device(edev->pdev);
 err_devput:
        return 0;
 }
 
-static void esb_initdevice(void)
+static void esb_initdevice(struct esb_dev *edev)
 {
        u8 val1;
        u16 val2;
@@ -391,96 +266,87 @@ static void esb_initdevice(void)
         * any interrupts as there is not much we can do with it
         * right now.
         */
-       pci_write_config_word(esb_pci, ESB_CONFIG_REG, 0x0003);
+       pci_write_config_word(edev->pdev, ESB_CONFIG_REG, 0x0003);
 
        /* Check that the WDT isn't already locked */
-       pci_read_config_byte(esb_pci, ESB_LOCK_REG, &val1);
+       pci_read_config_byte(edev->pdev, ESB_LOCK_REG, &val1);
        if (val1 & ESB_WDT_LOCK)
-               pr_warn("nowayout already set\n");
+               dev_warn(&edev->pdev->dev, "nowayout already set\n");
 
        /* Set the timer to watchdog mode and disable it for now */
-       pci_write_config_byte(esb_pci, ESB_LOCK_REG, 0x00);
+       pci_write_config_byte(edev->pdev, ESB_LOCK_REG, 0x00);
 
        /* Check if the watchdog was previously triggered */
-       esb_unlock_registers();
-       val2 = readw(ESB_RELOAD_REG);
+       esb_unlock_registers(edev);
+       val2 = readw(ESB_RELOAD_REG(edev));
        if (val2 & ESB_WDT_TIMEOUT)
-               triggered = WDIOF_CARDRESET;
+               edev->wdd.bootstatus = WDIOF_CARDRESET;
 
        /* Reset WDT_TIMEOUT flag and timers */
-       esb_unlock_registers();
-       writew((ESB_WDT_TIMEOUT | ESB_WDT_RELOAD), ESB_RELOAD_REG);
+       esb_unlock_registers(edev);
+       writew((ESB_WDT_TIMEOUT | ESB_WDT_RELOAD), ESB_RELOAD_REG(edev));
 
        /* And set the correct timeout value */
-       esb_timer_set_heartbeat(heartbeat);
+       esb_timer_set_heartbeat(&edev->wdd, edev->wdd.timeout);
 }
 
 static int esb_probe(struct pci_dev *pdev,
                const struct pci_device_id *ent)
 {
+       struct esb_dev *edev;
        int ret;
 
-       cards_found++;
-       if (cards_found == 1)
-               pr_info("Intel 6300ESB WatchDog Timer Driver v%s\n",
-                       ESB_VERSION);
-
-       if (cards_found > 1) {
-               pr_err("This driver only supports 1 device\n");
-               return -ENODEV;
-       }
+       edev = devm_kzalloc(&pdev->dev, sizeof(*edev), GFP_KERNEL);
+       if (!edev)
+               return -ENOMEM;
 
        /* Check whether or not the hardware watchdog is there */
-       if (!esb_getdevice(pdev) || esb_pci == NULL)
+       edev->pdev = pdev;
+       if (!esb_getdevice(edev))
                return -ENODEV;
 
-       /* Check that the heartbeat value is within it's range;
-          if not reset to the default */
-       if (heartbeat < 0x1 || heartbeat > 2 * 0x03ff) {
-               heartbeat = WATCHDOG_HEARTBEAT;
-               pr_info("heartbeat value must be 1<heartbeat<2046, using %d\n",
-                       heartbeat);
-       }
-
        /* Initialize the watchdog and make sure it does not run */
-       esb_initdevice();
+       edev->wdd.info = &esb_info;
+       edev->wdd.ops = &esb_ops;
+       edev->wdd.min_timeout = ESB_HEARTBEAT_MIN;
+       edev->wdd.max_timeout = ESB_HEARTBEAT_MAX;
+       edev->wdd.timeout = ESB_HEARTBEAT_DEFAULT;
+       if (watchdog_init_timeout(&edev->wdd, heartbeat, NULL))
+               dev_info(&pdev->dev,
+                       "heartbeat value must be " ESB_HEARTBEAT_RANGE
+                       ", using %u\n", edev->wdd.timeout);
+       watchdog_set_nowayout(&edev->wdd, nowayout);
+       watchdog_stop_on_reboot(&edev->wdd);
+       watchdog_stop_on_unregister(&edev->wdd);
+       esb_initdevice(edev);
 
        /* Register the watchdog so that userspace has access to it */
-       ret = misc_register(&esb_miscdev);
+       ret = watchdog_register_device(&edev->wdd);
        if (ret != 0) {
-               pr_err("cannot register miscdev on minor=%d (err=%d)\n",
-                      WATCHDOG_MINOR, ret);
+               dev_err(&pdev->dev,
+                       "cannot register watchdog device (err=%d)\n", ret);
                goto err_unmap;
        }
-       pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
-               BASEADDR, heartbeat, nowayout);
+       dev_info(&pdev->dev,
+               "initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
+               edev->base, edev->wdd.timeout, nowayout);
        return 0;
 
 err_unmap:
-       iounmap(BASEADDR);
-       pci_release_region(esb_pci, 0);
-       pci_disable_device(esb_pci);
-       esb_pci = NULL;
+       iounmap(edev->base);
+       pci_release_region(edev->pdev, 0);
+       pci_disable_device(edev->pdev);
        return ret;
 }
 
 static void esb_remove(struct pci_dev *pdev)
 {
-       /* Stop the timer before we leave */
-       if (!nowayout)
-               esb_timer_stop();
-
-       /* Deregister */
-       misc_deregister(&esb_miscdev);
-       iounmap(BASEADDR);
-       pci_release_region(esb_pci, 0);
-       pci_disable_device(esb_pci);
-       esb_pci = NULL;
-}
+       struct esb_dev *edev = dev_get_drvdata(&pdev->dev);
 
-static void esb_shutdown(struct pci_dev *pdev)
-{
-       esb_timer_stop();
+       watchdog_unregister_device(&edev->wdd);
+       iounmap(edev->base);
+       pci_release_region(edev->pdev, 0);
+       pci_disable_device(edev->pdev);
 }
 
 static struct pci_driver esb_driver = {
@@ -488,7 +354,6 @@ static struct pci_driver esb_driver = {
        .id_table       = esb_pci_tbl,
        .probe          = esb_probe,
        .remove         = esb_remove,
-       .shutdown       = esb_shutdown,
 };
 
 module_pci_driver(esb_driver);
index f2e4e1eeb8ddf96b93c6b051fcd744b3af3fab86..cc262284a6aa3d2231e592f7e2d1916b67c6820b 100644 (file)
@@ -218,7 +218,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                if (ibwdt_set_heartbeat(new_margin))
                        return -EINVAL;
                ibwdt_ping();
-               /* Fall */
+               /* fall through */
 
        case WDIOC_GETTIMEOUT:
                return put_user(timeout, p);
index 4874b0f18650f7bae4380f4a34b9ad5203ae49d5..518dfa1047cbd584d692930da7467d643c109a42 100644 (file)
@@ -169,15 +169,21 @@ static int imx2_wdt_ping(struct watchdog_device *wdog)
        return 0;
 }
 
-static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
-                               unsigned int new_timeout)
+static void __imx2_wdt_set_timeout(struct watchdog_device *wdog,
+                                  unsigned int new_timeout)
 {
        struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
 
-       wdog->timeout = new_timeout;
-
        regmap_update_bits(wdev->regmap, IMX2_WDT_WCR, IMX2_WDT_WCR_WT,
                           WDOG_SEC_TO_COUNT(new_timeout));
+}
+
+static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
+                               unsigned int new_timeout)
+{
+       __imx2_wdt_set_timeout(wdog, new_timeout);
+
+       wdog->timeout = new_timeout;
        return 0;
 }
 
@@ -371,7 +377,11 @@ static int imx2_wdt_suspend(struct device *dev)
 
        /* The watchdog IP block is running */
        if (imx2_wdt_is_running(wdev)) {
-               imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
+               /*
+                * Don't update wdog->timeout, we'll restore the current value
+                * during resume.
+                */
+               __imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME);
                imx2_wdt_ping(wdog);
        }
 
index 20627f22baf6a7004f74218b13152933d902b03b..aafbeb96561b506c1053162eb76f119e110cedbc 100644 (file)
@@ -146,6 +146,7 @@ static const struct watchdog_ops jz4740_wdt_ops = {
 #ifdef CONFIG_OF
 static const struct of_device_id jz4740_wdt_of_matches[] = {
        { .compatible = "ingenic,jz4740-watchdog", },
+       { .compatible = "ingenic,jz4780-watchdog", },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, jz4740_wdt_of_matches);
index ea60b29494fb693bf7898e3f456b6ff8e9dfdb6f..b8194b02abe08eab14892a27ee02fbfb147356cd 100644 (file)
@@ -526,12 +526,11 @@ static ssize_t mei_dbgfs_read_state(struct file *file, char __user *ubuf,
                                    size_t cnt, loff_t *ppos)
 {
        struct mei_wdt *wdt = file->private_data;
-       const size_t bufsz = 32;
-       char buf[bufsz];
+       char buf[32];
        ssize_t pos;
 
-       pos = scnprintf(buf, bufsz, "state: %s\n",
-                        mei_wdt_state_str(wdt->state));
+       pos = scnprintf(buf, sizeof(buf), "state: %s\n",
+                       mei_wdt_state_str(wdt->state));
 
        return simple_read_from_buffer(ubuf, cnt, ppos, buf, pos);
 }
index 6610e9217dbc237a7e5352df3cb9587a6770be67..aca2d6323f8ad14d11294895081e9a67178d11bd 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/timer.h>
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
 #include <linux/module.h>
 #include <linux/uaccess.h>
 #include <sysdev/fsl_soc.h>
 
+#define WATCHDOG_TIMEOUT 10
+
 struct mpc8xxx_wdt {
        __be32 res0;
        __be32 swcrr; /* System watchdog control register */
 #define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */
+#define SWCRR_SWF  0x00000008 /* Software Watchdog Freeze (mpc8xx). */
 #define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */
 #define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/
 #define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */
@@ -52,14 +54,15 @@ struct mpc8xxx_wdt_type {
 struct mpc8xxx_wdt_ddata {
        struct mpc8xxx_wdt __iomem *base;
        struct watchdog_device wdd;
-       struct timer_list timer;
        spinlock_t lock;
+       u16 swtc;
 };
 
-static u16 timeout = 0xffff;
+static u16 timeout;
 module_param(timeout, ushort, 0);
 MODULE_PARM_DESC(timeout,
-       "Watchdog timeout in ticks. (0<timeout<65536, default=65535)");
+       "Watchdog timeout in seconds. (1<timeout<65535, default="
+       __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
 
 static bool reset = 1;
 module_param(reset, bool, 0);
@@ -80,31 +83,27 @@ static void mpc8xxx_wdt_keepalive(struct mpc8xxx_wdt_ddata *ddata)
        spin_unlock(&ddata->lock);
 }
 
-static void mpc8xxx_wdt_timer_ping(struct timer_list *t)
-{
-       struct mpc8xxx_wdt_ddata *ddata = from_timer(ddata, t, timer);
-
-       mpc8xxx_wdt_keepalive(ddata);
-       /* We're pinging it twice faster than needed, just to be sure. */
-       mod_timer(&ddata->timer, jiffies + HZ * ddata->wdd.timeout / 2);
-}
-
 static int mpc8xxx_wdt_start(struct watchdog_device *w)
 {
        struct mpc8xxx_wdt_ddata *ddata =
                container_of(w, struct mpc8xxx_wdt_ddata, wdd);
-
-       u32 tmp = SWCRR_SWEN | SWCRR_SWPR;
+       u32 tmp = in_be32(&ddata->base->swcrr);
 
        /* Good, fire up the show */
+       tmp &= ~(SWCRR_SWTC | SWCRR_SWF | SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR);
+       tmp |= SWCRR_SWEN | SWCRR_SWPR | (ddata->swtc << 16);
+
        if (reset)
                tmp |= SWCRR_SWRI;
 
-       tmp |= timeout << 16;
-
        out_be32(&ddata->base->swcrr, tmp);
 
-       del_timer_sync(&ddata->timer);
+       tmp = in_be32(&ddata->base->swcrr);
+       if (!(tmp & SWCRR_SWEN))
+               return -EOPNOTSUPP;
+
+       ddata->swtc = tmp >> 16;
+       set_bit(WDOG_HW_RUNNING, &ddata->wdd.status);
 
        return 0;
 }
@@ -118,17 +117,8 @@ static int mpc8xxx_wdt_ping(struct watchdog_device *w)
        return 0;
 }
 
-static int mpc8xxx_wdt_stop(struct watchdog_device *w)
-{
-       struct mpc8xxx_wdt_ddata *ddata =
-               container_of(w, struct mpc8xxx_wdt_ddata, wdd);
-
-       mod_timer(&ddata->timer, jiffies);
-       return 0;
-}
-
 static struct watchdog_info mpc8xxx_wdt_info = {
-       .options = WDIOF_KEEPALIVEPING,
+       .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT,
        .firmware_version = 1,
        .identity = "MPC8xxx",
 };
@@ -137,7 +127,6 @@ static struct watchdog_ops mpc8xxx_wdt_ops = {
        .owner = THIS_MODULE,
        .start = mpc8xxx_wdt_start,
        .ping = mpc8xxx_wdt_ping,
-       .stop = mpc8xxx_wdt_stop,
 };
 
 static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
@@ -148,7 +137,6 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
        struct mpc8xxx_wdt_ddata *ddata;
        u32 freq = fsl_get_sys_freq();
        bool enabled;
-       unsigned int timeout_sec;
 
        wdt_type = of_device_get_match_data(&ofdev->dev);
        if (!wdt_type)
@@ -173,26 +161,17 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
        }
 
        spin_lock_init(&ddata->lock);
-       timer_setup(&ddata->timer, mpc8xxx_wdt_timer_ping, 0);
 
        ddata->wdd.info = &mpc8xxx_wdt_info,
        ddata->wdd.ops = &mpc8xxx_wdt_ops,
 
-       /* Calculate the timeout in seconds */
-       timeout_sec = (timeout * wdt_type->prescaler) / freq;
-
-       ddata->wdd.timeout = timeout_sec;
+       ddata->wdd.timeout = WATCHDOG_TIMEOUT;
+       watchdog_init_timeout(&ddata->wdd, timeout, &ofdev->dev);
 
        watchdog_set_nowayout(&ddata->wdd, nowayout);
 
-       ret = watchdog_register_device(&ddata->wdd);
-       if (ret) {
-               pr_err("cannot register watchdog device (err=%d)\n", ret);
-               return ret;
-       }
-
-       pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d (%d seconds)\n",
-               reset ? "reset" : "interrupt", timeout, timeout_sec);
+       ddata->swtc = min(ddata->wdd.timeout * freq / wdt_type->prescaler,
+                         0xffffU);
 
        /*
         * If the watchdog was previously enabled or we're running on
@@ -200,7 +179,22 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
         * userspace handles it.
         */
        if (enabled)
-               mod_timer(&ddata->timer, jiffies);
+               mpc8xxx_wdt_start(&ddata->wdd);
+
+       ddata->wdd.max_hw_heartbeat_ms = (ddata->swtc * wdt_type->prescaler) /
+                                        (freq / 1000);
+       ddata->wdd.min_timeout = ddata->wdd.max_hw_heartbeat_ms / 1000;
+       if (ddata->wdd.timeout < ddata->wdd.min_timeout)
+               ddata->wdd.timeout = ddata->wdd.min_timeout;
+
+       ret = watchdog_register_device(&ddata->wdd);
+       if (ret) {
+               pr_err("cannot register watchdog device (err=%d)\n", ret);
+               return ret;
+       }
+
+       pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec\n",
+               reset ? "reset" : "interrupt", ddata->wdd.timeout);
 
        platform_set_drvdata(ofdev, ddata);
        return 0;
@@ -212,7 +206,6 @@ static int mpc8xxx_wdt_remove(struct platform_device *ofdev)
 
        pr_crit("Watchdog removed, expect the %s soon!\n",
                reset ? "reset" : "machine check exception");
-       del_timer_sync(&ddata->timer);
        watchdog_unregister_device(&ddata->wdd);
 
        return 0;
index db38f801721871566251a1f34374236444a65d69..5c4a764717c4d09dcd7345180d622ef55c7124a4 100644 (file)
@@ -105,6 +105,11 @@ static int mt7621_wdt_bootcause(void)
        return 0;
 }
 
+static int mt7621_wdt_is_running(struct watchdog_device *w)
+{
+       return !!(rt_wdt_r32(TIMER_REG_TMR1CTL) & TMR1CTL_ENABLE);
+}
+
 static const struct watchdog_info mt7621_wdt_info = {
        .identity = "Mediatek Watchdog",
        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
@@ -128,7 +133,6 @@ static struct watchdog_device mt7621_wdt_dev = {
 static int mt7621_wdt_probe(struct platform_device *pdev)
 {
        struct resource *res;
-       int ret;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        mt7621_wdt_base = devm_ioremap_resource(&pdev->dev, res);
@@ -144,17 +148,22 @@ static int mt7621_wdt_probe(struct platform_device *pdev)
        watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout,
                              &pdev->dev);
        watchdog_set_nowayout(&mt7621_wdt_dev, nowayout);
-
-       ret = watchdog_register_device(&mt7621_wdt_dev);
-
-       return 0;
-}
-
-static int mt7621_wdt_remove(struct platform_device *pdev)
-{
-       watchdog_unregister_device(&mt7621_wdt_dev);
-
-       return 0;
+       if (mt7621_wdt_is_running(&mt7621_wdt_dev)) {
+               /*
+                * Make sure to apply timeout from watchdog core, taking
+                * the prescaler of this driver here into account (the
+                * boot loader might be using a different prescaler).
+                *
+                * To avoid spurious resets because of different scaling,
+                * we first disable the watchdog, set the new prescaler
+                * and timeout, and then re-enable the watchdog.
+                */
+               mt7621_wdt_stop(&mt7621_wdt_dev);
+               mt7621_wdt_start(&mt7621_wdt_dev);
+               set_bit(WDOG_HW_RUNNING, &mt7621_wdt_dev.status);
+       }
+
+       return devm_watchdog_register_device(&pdev->dev, &mt7621_wdt_dev);
 }
 
 static void mt7621_wdt_shutdown(struct platform_device *pdev)
@@ -170,7 +179,6 @@ MODULE_DEVICE_TABLE(of, mt7621_wdt_match);
 
 static struct platform_driver mt7621_wdt_driver = {
        .probe          = mt7621_wdt_probe,
-       .remove         = mt7621_wdt_remove,
        .shutdown       = mt7621_wdt_shutdown,
        .driver         = {
                .name           = KBUILD_MODNAME,
index 83af7d6cc37cc9e3c46afa5aa54b16a6a94c66ee..ea676d233e1e5e96539c68290c2eee1e0802236a 100644 (file)
@@ -576,7 +576,7 @@ static int orion_wdt_probe(struct platform_device *pdev)
        /*
         * Let's make sure the watchdog is fully stopped, unless it's
         * explicitly enabled. This may be the case if the module was
-        * removed and re-insterted, or if the bootloader explicitly
+        * removed and re-inserted, or if the bootloader explicitly
         * set a running watchdog before booting the kernel.
         */
        if (!orion_wdt_enabled(&dev->wdt))
index c0d07eef2640b9cb0f2469acda6a579e57a16d94..1f78f090862181fee23630c4f8d9a80912e96421 100644 (file)
@@ -545,8 +545,8 @@ static long pcipcwd_ioctl(struct file *file, unsigned int cmd,
                        return -EINVAL;
 
                pcipcwd_keepalive();
-               /* Fall */
        }
+               /* fall through */
 
        case WDIOC_GETTIMEOUT:
                return put_user(heartbeat, p);
index b9e376c8e2e36afa6fa7c7f172470b77033627e8..4d02f26156f9ce10f03993d8dd88b79b4715f848 100644 (file)
 #define DRIVER_VERSION "1.02"
 #define DRIVER_AUTHOR "Wim Van Sebroeck <wim@iguana.be>"
 #define DRIVER_DESC "Berkshire USB-PC Watchdog driver"
-#define DRIVER_LICENSE "GPL"
 #define DRIVER_NAME "pcwd_usb"
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_LICENSE("GPL");
 
 #define WATCHDOG_HEARTBEAT 0   /* default heartbeat =
                                                delay-time from dip-switches */
@@ -456,8 +455,8 @@ static long usb_pcwd_ioctl(struct file *file, unsigned int cmd,
                        return -EINVAL;
 
                usb_pcwd_keepalive(usb_pcwd_device);
-               /* Fall */
        }
+               /* fall through */
 
        case WDIOC_GETTIMEOUT:
                return put_user(heartbeat, p);
diff --git a/drivers/watchdog/rtd119x_wdt.c b/drivers/watchdog/rtd119x_wdt.c
new file mode 100644 (file)
index 0000000..d001c17
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Realtek RTD129x watchdog
+ *
+ * Copyright (c) 2017 Andreas Färber
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+#define RTD119X_TCWCR          0x0
+#define RTD119X_TCWTR          0x4
+#define RTD119X_TCWOV          0xc
+
+#define RTD119X_TCWCR_WDEN_DISABLED            0xa5
+#define RTD119X_TCWCR_WDEN_ENABLED             0xff
+#define RTD119X_TCWCR_WDEN_MASK                        0xff
+
+#define RTD119X_TCWTR_WDCLR                    BIT(0)
+
+struct rtd119x_watchdog_device {
+       struct watchdog_device wdt_dev;
+       void __iomem *base;
+       struct clk *clk;
+};
+
+static int rtd119x_wdt_start(struct watchdog_device *wdev)
+{
+       struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
+       u32 val;
+
+       val = readl_relaxed(data->base + RTD119X_TCWCR);
+       val &= ~RTD119X_TCWCR_WDEN_MASK;
+       val |= RTD119X_TCWCR_WDEN_ENABLED;
+       writel(val, data->base + RTD119X_TCWCR);
+
+       return 0;
+}
+
+static int rtd119x_wdt_stop(struct watchdog_device *wdev)
+{
+       struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
+       u32 val;
+
+       val = readl_relaxed(data->base + RTD119X_TCWCR);
+       val &= ~RTD119X_TCWCR_WDEN_MASK;
+       val |= RTD119X_TCWCR_WDEN_DISABLED;
+       writel(val, data->base + RTD119X_TCWCR);
+
+       return 0;
+}
+
+static int rtd119x_wdt_ping(struct watchdog_device *wdev)
+{
+       struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
+
+       writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR);
+
+       return rtd119x_wdt_start(wdev);
+}
+
+static int rtd119x_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
+{
+       struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
+
+       writel(val * clk_get_rate(data->clk), data->base + RTD119X_TCWOV);
+
+       data->wdt_dev.timeout = val;
+
+       return 0;
+}
+
+static const struct watchdog_ops rtd119x_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start          = rtd119x_wdt_start,
+       .stop           = rtd119x_wdt_stop,
+       .ping           = rtd119x_wdt_ping,
+       .set_timeout    = rtd119x_wdt_set_timeout,
+};
+
+static const struct watchdog_info rtd119x_wdt_info = {
+       .identity = "rtd119x-wdt",
+       .options = 0,
+};
+
+static const struct of_device_id rtd119x_wdt_dt_ids[] = {
+        { .compatible = "realtek,rtd1295-watchdog" },
+        { }
+};
+
+static int rtd119x_wdt_probe(struct platform_device *pdev)
+{
+       struct rtd119x_watchdog_device *data;
+       struct resource *res;
+       int ret;
+
+       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       data->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(data->base))
+               return PTR_ERR(data->base);
+
+       data->clk = of_clk_get(pdev->dev.of_node, 0);
+       if (IS_ERR(data->clk))
+               return PTR_ERR(data->clk);
+
+       ret = clk_prepare_enable(data->clk);
+       if (ret) {
+               clk_put(data->clk);
+               return ret;
+       }
+
+       data->wdt_dev.info = &rtd119x_wdt_info;
+       data->wdt_dev.ops = &rtd119x_wdt_ops;
+       data->wdt_dev.timeout = 120;
+       data->wdt_dev.max_timeout = 0xffffffff / clk_get_rate(data->clk);
+       data->wdt_dev.min_timeout = 1;
+       data->wdt_dev.parent = &pdev->dev;
+
+       watchdog_stop_on_reboot(&data->wdt_dev);
+       watchdog_set_drvdata(&data->wdt_dev, data);
+       platform_set_drvdata(pdev, data);
+
+       writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR);
+       rtd119x_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
+       rtd119x_wdt_stop(&data->wdt_dev);
+
+       ret = devm_watchdog_register_device(&pdev->dev, &data->wdt_dev);
+       if (ret) {
+               clk_disable_unprepare(data->clk);
+               clk_put(data->clk);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rtd119x_wdt_remove(struct platform_device *pdev)
+{
+       struct rtd119x_watchdog_device *data = platform_get_drvdata(pdev);
+
+       watchdog_unregister_device(&data->wdt_dev);
+
+       clk_disable_unprepare(data->clk);
+       clk_put(data->clk);
+
+       return 0;
+}
+
+static struct platform_driver rtd119x_wdt_driver = {
+       .probe = rtd119x_wdt_probe,
+       .remove = rtd119x_wdt_remove,
+       .driver = {
+               .name = "rtd1295-watchdog",
+               .of_match_table = rtd119x_wdt_dt_ids,
+       },
+};
+builtin_platform_driver(rtd119x_wdt_driver);
index 028618c5eebacfee0cbe59abbae4caad069068ec..41aaae2d5287faae869fad5591b0290cc0c7dadf 100644 (file)
  *     See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide",
  *         AMD Publication 45482 "AMD SB800-Series Southbridges Register
  *                                                           Reference Guide"
+ *         AMD Publication 48751 "BIOS and Kernel Developer’s Guide (BKDG)
+ *                             for AMD Family 16h Models 00h-0Fh Processors"
+ *         AMD Publication 51192 "AMD Bolton FCH Register Reference Guide"
+ *         AMD Publication 52740 "BIOS and Kernel Developer’s Guide (BKDG)
+ *                             for AMD Family 16h Models 30h-3Fh Processors"
  */
 
 /*
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/init.h>
-#include <linux/fs.h>
 #include <linux/pci.h>
-#include <linux/ioport.h>
 #include <linux/platform_device.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
 
 #include "sp5100_tco.h"
 
-/* Module and version information */
-#define TCO_VERSION "0.05"
-#define TCO_MODULE_NAME "SP5100 TCO timer"
-#define TCO_DRIVER_NAME   TCO_MODULE_NAME ", v" TCO_VERSION
+#define TCO_DRIVER_NAME        "sp5100-tco"
 
 /* internal variables */
-static u32 tcobase_phys;
-static u32 tco_wdt_fired;
-static void __iomem *tcobase;
-static unsigned int pm_iobase;
-static DEFINE_SPINLOCK(tco_lock);      /* Guards the hardware */
-static unsigned long timer_alive;
-static char tco_expect_close;
-static struct pci_dev *sp5100_tco_pci;
+
+enum tco_reg_layout {
+       sp5100, sb800, efch
+};
+
+struct sp5100_tco {
+       struct watchdog_device wdd;
+       void __iomem *tcobase;
+       enum tco_reg_layout tco_reg_layout;
+};
 
 /* the watchdog platform device */
 static struct platform_device *sp5100_tco_platform_device;
+/* the associated PCI device */
+static struct pci_dev *sp5100_tco_pci;
 
 /* module parameters */
 
@@ -74,83 +77,105 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started."
  * Some TCO specific functions
  */
 
-static bool tco_has_sp5100_reg_layout(struct pci_dev *dev)
+static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev)
 {
-       return dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
-              dev->revision < 0x40;
+       if (dev->vendor == PCI_VENDOR_ID_ATI &&
+           dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
+           dev->revision < 0x40) {
+               return sp5100;
+       } else if (dev->vendor == PCI_VENDOR_ID_AMD &&
+           ((dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
+            dev->revision >= 0x41) ||
+           (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
+            dev->revision >= 0x49))) {
+               return efch;
+       }
+       return sb800;
 }
 
-static void tco_timer_start(void)
+static int tco_timer_start(struct watchdog_device *wdd)
 {
+       struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
        u32 val;
-       unsigned long flags;
 
-       spin_lock_irqsave(&tco_lock, flags);
-       val = readl(SP5100_WDT_CONTROL(tcobase));
+       val = readl(SP5100_WDT_CONTROL(tco->tcobase));
        val |= SP5100_WDT_START_STOP_BIT;
-       writel(val, SP5100_WDT_CONTROL(tcobase));
-       spin_unlock_irqrestore(&tco_lock, flags);
+       writel(val, SP5100_WDT_CONTROL(tco->tcobase));
+
+       return 0;
 }
 
-static void tco_timer_stop(void)
+static int tco_timer_stop(struct watchdog_device *wdd)
 {
+       struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
        u32 val;
-       unsigned long flags;
 
-       spin_lock_irqsave(&tco_lock, flags);
-       val = readl(SP5100_WDT_CONTROL(tcobase));
+       val = readl(SP5100_WDT_CONTROL(tco->tcobase));
        val &= ~SP5100_WDT_START_STOP_BIT;
-       writel(val, SP5100_WDT_CONTROL(tcobase));
-       spin_unlock_irqrestore(&tco_lock, flags);
+       writel(val, SP5100_WDT_CONTROL(tco->tcobase));
+
+       return 0;
 }
 
-static void tco_timer_keepalive(void)
+static int tco_timer_ping(struct watchdog_device *wdd)
 {
+       struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
        u32 val;
-       unsigned long flags;
 
-       spin_lock_irqsave(&tco_lock, flags);
-       val = readl(SP5100_WDT_CONTROL(tcobase));
+       val = readl(SP5100_WDT_CONTROL(tco->tcobase));
        val |= SP5100_WDT_TRIGGER_BIT;
-       writel(val, SP5100_WDT_CONTROL(tcobase));
-       spin_unlock_irqrestore(&tco_lock, flags);
+       writel(val, SP5100_WDT_CONTROL(tco->tcobase));
+
+       return 0;
 }
 
-static int tco_timer_set_heartbeat(int t)
+static int tco_timer_set_timeout(struct watchdog_device *wdd,
+                                unsigned int t)
 {
-       unsigned long flags;
-
-       if (t < 0 || t > 0xffff)
-               return -EINVAL;
+       struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
 
        /* Write new heartbeat to watchdog */
-       spin_lock_irqsave(&tco_lock, flags);
-       writel(t, SP5100_WDT_COUNT(tcobase));
-       spin_unlock_irqrestore(&tco_lock, flags);
+       writel(t, SP5100_WDT_COUNT(tco->tcobase));
+
+       wdd->timeout = t;
 
-       heartbeat = t;
        return 0;
 }
 
-static void tco_timer_enable(void)
+static u8 sp5100_tco_read_pm_reg8(u8 index)
+{
+       outb(index, SP5100_IO_PM_INDEX_REG);
+       return inb(SP5100_IO_PM_DATA_REG);
+}
+
+static void sp5100_tco_update_pm_reg8(u8 index, u8 reset, u8 set)
+{
+       u8 val;
+
+       outb(index, SP5100_IO_PM_INDEX_REG);
+       val = inb(SP5100_IO_PM_DATA_REG);
+       val &= reset;
+       val |= set;
+       outb(val, SP5100_IO_PM_DATA_REG);
+}
+
+static void tco_timer_enable(struct sp5100_tco *tco)
 {
-       int val;
+       u32 val;
 
-       if (!tco_has_sp5100_reg_layout(sp5100_tco_pci)) {
+       switch (tco->tco_reg_layout) {
+       case sb800:
                /* For SB800 or later */
                /* Set the Watchdog timer resolution to 1 sec */
-               outb(SB800_PM_WATCHDOG_CONFIG, SB800_IO_PM_INDEX_REG);
-               val = inb(SB800_IO_PM_DATA_REG);
-               val |= SB800_PM_WATCHDOG_SECOND_RES;
-               outb(val, SB800_IO_PM_DATA_REG);
+               sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONFIG,
+                                         0xff, SB800_PM_WATCHDOG_SECOND_RES);
 
                /* Enable watchdog decode bit and watchdog timer */
-               outb(SB800_PM_WATCHDOG_CONTROL, SB800_IO_PM_INDEX_REG);
-               val = inb(SB800_IO_PM_DATA_REG);
-               val |= SB800_PCI_WATCHDOG_DECODE_EN;
-               val &= ~SB800_PM_WATCHDOG_DISABLE;
-               outb(val, SB800_IO_PM_DATA_REG);
-       } else {
+               sp5100_tco_update_pm_reg8(SB800_PM_WATCHDOG_CONTROL,
+                                         ~SB800_PM_WATCHDOG_DISABLE,
+                                         SB800_PCI_WATCHDOG_DECODE_EN);
+               break;
+       case sp5100:
                /* For SP5100 or SB7x0 */
                /* Enable watchdog decode bit */
                pci_read_config_dword(sp5100_tco_pci,
@@ -164,409 +189,287 @@ static void tco_timer_enable(void)
                                       val);
 
                /* Enable Watchdog timer and set the resolution to 1 sec */
-               outb(SP5100_PM_WATCHDOG_CONTROL, SP5100_IO_PM_INDEX_REG);
-               val = inb(SP5100_IO_PM_DATA_REG);
-               val |= SP5100_PM_WATCHDOG_SECOND_RES;
-               val &= ~SP5100_PM_WATCHDOG_DISABLE;
-               outb(val, SP5100_IO_PM_DATA_REG);
+               sp5100_tco_update_pm_reg8(SP5100_PM_WATCHDOG_CONTROL,
+                                         ~SP5100_PM_WATCHDOG_DISABLE,
+                                         SP5100_PM_WATCHDOG_SECOND_RES);
+               break;
+       case efch:
+               /* Set the Watchdog timer resolution to 1 sec and enable */
+               sp5100_tco_update_pm_reg8(EFCH_PM_DECODEEN3,
+                                         ~EFCH_PM_WATCHDOG_DISABLE,
+                                         EFCH_PM_DECODEEN_SECOND_RES);
+               break;
        }
 }
 
-/*
- *     /dev/watchdog handling
- */
-
-static int sp5100_tco_open(struct inode *inode, struct file *file)
+static u32 sp5100_tco_read_pm_reg32(u8 index)
 {
-       /* /dev/watchdog can only be opened once */
-       if (test_and_set_bit(0, &timer_alive))
-               return -EBUSY;
-
-       /* Reload and activate timer */
-       tco_timer_start();
-       tco_timer_keepalive();
-       return nonseekable_open(inode, file);
-}
-
-static int sp5100_tco_release(struct inode *inode, struct file *file)
-{
-       /* Shut off the timer. */
-       if (tco_expect_close == 42) {
-               tco_timer_stop();
-       } else {
-               pr_crit("Unexpected close, not stopping watchdog!\n");
-               tco_timer_keepalive();
-       }
-       clear_bit(0, &timer_alive);
-       tco_expect_close = 0;
-       return 0;
-}
-
-static ssize_t sp5100_tco_write(struct file *file, const char __user *data,
-                               size_t len, loff_t *ppos)
-{
-       /* See if we got the magic character 'V' and reload the timer */
-       if (len) {
-               if (!nowayout) {
-                       size_t i;
-
-                       /* note: just in case someone wrote the magic character
-                        * five months ago... */
-                       tco_expect_close = 0;
+       u32 val = 0;
+       int i;
 
-                       /* scan to see whether or not we got the magic character
-                        */
-                       for (i = 0; i != len; i++) {
-                               char c;
-                               if (get_user(c, data + i))
-                                       return -EFAULT;
-                               if (c == 'V')
-                                       tco_expect_close = 42;
-                       }
-               }
+       for (i = 3; i >= 0; i--)
+               val = (val << 8) + sp5100_tco_read_pm_reg8(index + i);
 
-               /* someone wrote to us, we should reload the timer */
-               tco_timer_keepalive();
-       }
-       return len;
+       return val;
 }
 
-static long sp5100_tco_ioctl(struct file *file, unsigned int cmd,
-                            unsigned long arg)
+static int sp5100_tco_setupdevice(struct device *dev,
+                                 struct watchdog_device *wdd)
 {
-       int new_options, retval = -EINVAL;
-       int new_heartbeat;
-       void __user *argp = (void __user *)arg;
-       int __user *p = argp;
-       static const struct watchdog_info ident = {
-               .options =              WDIOF_SETTIMEOUT |
-                                       WDIOF_KEEPALIVEPING |
-                                       WDIOF_MAGICCLOSE,
-               .firmware_version =     0,
-               .identity =             TCO_MODULE_NAME,
-       };
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               return copy_to_user(argp, &ident,
-                       sizeof(ident)) ? -EFAULT : 0;
-       case WDIOC_GETSTATUS:
-       case WDIOC_GETBOOTSTATUS:
-               return put_user(0, p);
-       case WDIOC_SETOPTIONS:
-               if (get_user(new_options, p))
-                       return -EFAULT;
-               if (new_options & WDIOS_DISABLECARD) {
-                       tco_timer_stop();
-                       retval = 0;
-               }
-               if (new_options & WDIOS_ENABLECARD) {
-                       tco_timer_start();
-                       tco_timer_keepalive();
-                       retval = 0;
-               }
-               return retval;
-       case WDIOC_KEEPALIVE:
-               tco_timer_keepalive();
-               return 0;
-       case WDIOC_SETTIMEOUT:
-               if (get_user(new_heartbeat, p))
-                       return -EFAULT;
-               if (tco_timer_set_heartbeat(new_heartbeat))
-                       return -EINVAL;
-               tco_timer_keepalive();
-               /* Fall through */
-       case WDIOC_GETTIMEOUT:
-               return put_user(heartbeat, p);
-       default:
-               return -ENOTTY;
-       }
-}
-
-/*
- * Kernel Interfaces
- */
-
-static const struct file_operations sp5100_tco_fops = {
-       .owner =                THIS_MODULE,
-       .llseek =               no_llseek,
-       .write =                sp5100_tco_write,
-       .unlocked_ioctl =       sp5100_tco_ioctl,
-       .open =                 sp5100_tco_open,
-       .release =              sp5100_tco_release,
-};
-
-static struct miscdevice sp5100_tco_miscdev = {
-       .minor =        WATCHDOG_MINOR,
-       .name =         "watchdog",
-       .fops =         &sp5100_tco_fops,
-};
-
-/*
- * Data for PCI driver interface
- *
- * This data only exists for exporting the supported
- * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
- * register a pci_driver, because someone else might
- * want to register another driver on the same PCI id.
- */
-static const struct pci_device_id sp5100_tco_pci_tbl[] = {
-       { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID,
-         PCI_ANY_ID, },
-       { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID,
-         PCI_ANY_ID, },
-       { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID,
-         PCI_ANY_ID, },
-       { 0, },                 /* End of list */
-};
-MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl);
-
-/*
- * Init & exit routines
- */
-static unsigned char sp5100_tco_setupdevice(void)
-{
-       struct pci_dev *dev = NULL;
-       const char *dev_name = NULL;
-       u32 val;
-       u32 index_reg, data_reg, base_addr;
+       struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
+       const char *dev_name;
+       u32 mmio_addr = 0, val;
+       int ret;
 
-       /* Match the PCI device */
-       for_each_pci_dev(dev) {
-               if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) {
-                       sp5100_tco_pci = dev;
-                       break;
-               }
+       /* Request the IO ports used by this driver */
+       if (!request_muxed_region(SP5100_IO_PM_INDEX_REG,
+                                 SP5100_PM_IOPORTS_SIZE, "sp5100_tco")) {
+               dev_err(dev, "I/O address 0x%04x already in use\n",
+                       SP5100_IO_PM_INDEX_REG);
+               return -EBUSY;
        }
 
-       if (!sp5100_tco_pci)
-               return 0;
-
-       pr_info("PCI Vendor ID: 0x%x, Device ID: 0x%x, Revision ID: 0x%x\n",
-               sp5100_tco_pci->vendor, sp5100_tco_pci->device,
-               sp5100_tco_pci->revision);
-
        /*
         * Determine type of southbridge chipset.
         */
-       if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) {
+       switch (tco->tco_reg_layout) {
+       case sp5100:
                dev_name = SP5100_DEVNAME;
-               index_reg = SP5100_IO_PM_INDEX_REG;
-               data_reg = SP5100_IO_PM_DATA_REG;
-               base_addr = SP5100_PM_WATCHDOG_BASE;
-       } else {
+               mmio_addr = sp5100_tco_read_pm_reg32(SP5100_PM_WATCHDOG_BASE) &
+                                                               0xfffffff8;
+               break;
+       case sb800:
                dev_name = SB800_DEVNAME;
-               index_reg = SB800_IO_PM_INDEX_REG;
-               data_reg = SB800_IO_PM_DATA_REG;
-               base_addr = SB800_PM_WATCHDOG_BASE;
-       }
-
-       /* Request the IO ports used by this driver */
-       pm_iobase = SP5100_IO_PM_INDEX_REG;
-       if (!request_region(pm_iobase, SP5100_PM_IOPORTS_SIZE, dev_name)) {
-               pr_err("I/O address 0x%04x already in use\n", pm_iobase);
-               goto exit;
+               mmio_addr = sp5100_tco_read_pm_reg32(SB800_PM_WATCHDOG_BASE) &
+                                                               0xfffffff8;
+               break;
+       case efch:
+               dev_name = SB800_DEVNAME;
+               val = sp5100_tco_read_pm_reg8(EFCH_PM_DECODEEN);
+               if (val & EFCH_PM_DECODEEN_WDT_TMREN)
+                       mmio_addr = EFCH_PM_WDT_ADDR;
+               break;
+       default:
+               return -ENODEV;
        }
 
-       /*
-        * First, Find the watchdog timer MMIO address from indirect I/O.
-        */
-       outb(base_addr+3, index_reg);
-       val = inb(data_reg);
-       outb(base_addr+2, index_reg);
-       val = val << 8 | inb(data_reg);
-       outb(base_addr+1, index_reg);
-       val = val << 8 | inb(data_reg);
-       outb(base_addr+0, index_reg);
-       /* Low three bits of BASE are reserved */
-       val = val << 8 | (inb(data_reg) & 0xf8);
-
-       pr_debug("Got 0x%04x from indirect I/O\n", val);
-
        /* Check MMIO address conflict */
-       if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
-                                                               dev_name))
-               goto setup_wdt;
-       else
-               pr_debug("MMIO address 0x%04x already in use\n", val);
-
-       /*
-        * Secondly, Find the watchdog timer MMIO address
-        * from SBResource_MMIO register.
-        */
-       if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) {
-               /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */
-               pci_read_config_dword(sp5100_tco_pci,
-                                     SP5100_SB_RESOURCE_MMIO_BASE, &val);
-       } else {
-               /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */
-               outb(SB800_PM_ACPI_MMIO_EN+3, SB800_IO_PM_INDEX_REG);
-               val = inb(SB800_IO_PM_DATA_REG);
-               outb(SB800_PM_ACPI_MMIO_EN+2, SB800_IO_PM_INDEX_REG);
-               val = val << 8 | inb(SB800_IO_PM_DATA_REG);
-               outb(SB800_PM_ACPI_MMIO_EN+1, SB800_IO_PM_INDEX_REG);
-               val = val << 8 | inb(SB800_IO_PM_DATA_REG);
-               outb(SB800_PM_ACPI_MMIO_EN+0, SB800_IO_PM_INDEX_REG);
-               val = val << 8 | inb(SB800_IO_PM_DATA_REG);
+       if (!mmio_addr ||
+           !devm_request_mem_region(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE,
+                                    dev_name)) {
+               if (mmio_addr)
+                       dev_dbg(dev, "MMIO address 0x%08x already in use\n",
+                               mmio_addr);
+               switch (tco->tco_reg_layout) {
+               case sp5100:
+                       /*
+                        * Secondly, Find the watchdog timer MMIO address
+                        * from SBResource_MMIO register.
+                        */
+                       /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */
+                       pci_read_config_dword(sp5100_tco_pci,
+                                             SP5100_SB_RESOURCE_MMIO_BASE,
+                                             &mmio_addr);
+                       if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN |
+                                         SB800_ACPI_MMIO_SEL)) !=
+                                                 SB800_ACPI_MMIO_DECODE_EN) {
+                               ret = -ENODEV;
+                               goto unreg_region;
+                       }
+                       mmio_addr &= ~0xFFF;
+                       mmio_addr += SB800_PM_WDT_MMIO_OFFSET;
+                       break;
+               case sb800:
+                       /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */
+                       mmio_addr =
+                               sp5100_tco_read_pm_reg32(SB800_PM_ACPI_MMIO_EN);
+                       if ((mmio_addr & (SB800_ACPI_MMIO_DECODE_EN |
+                                         SB800_ACPI_MMIO_SEL)) !=
+                                                 SB800_ACPI_MMIO_DECODE_EN) {
+                               ret = -ENODEV;
+                               goto unreg_region;
+                       }
+                       mmio_addr &= ~0xFFF;
+                       mmio_addr += SB800_PM_WDT_MMIO_OFFSET;
+                       break;
+               case efch:
+                       val = sp5100_tco_read_pm_reg8(EFCH_PM_ISACONTROL);
+                       if (!(val & EFCH_PM_ISACONTROL_MMIOEN)) {
+                               ret = -ENODEV;
+                               goto unreg_region;
+                       }
+                       mmio_addr = EFCH_PM_ACPI_MMIO_ADDR +
+                                   EFCH_PM_ACPI_MMIO_WDT_OFFSET;
+                       break;
+               }
+               dev_dbg(dev, "Got 0x%08x from SBResource_MMIO register\n",
+                       mmio_addr);
+               if (!devm_request_mem_region(dev, mmio_addr,
+                                            SP5100_WDT_MEM_MAP_SIZE,
+                                            dev_name)) {
+                       dev_dbg(dev, "MMIO address 0x%08x already in use\n",
+                               mmio_addr);
+                       ret = -EBUSY;
+                       goto unreg_region;
+               }
        }
 
-       /* The SBResource_MMIO is enabled and mapped memory space? */
-       if ((val & (SB800_ACPI_MMIO_DECODE_EN | SB800_ACPI_MMIO_SEL)) ==
-                                                 SB800_ACPI_MMIO_DECODE_EN) {
-               /* Clear unnecessary the low twelve bits */
-               val &= ~0xFFF;
-               /* Add the Watchdog Timer offset to base address. */
-               val += SB800_PM_WDT_MMIO_OFFSET;
-               /* Check MMIO address conflict */
-               if (request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE,
-                                                                  dev_name)) {
-                       pr_debug("Got 0x%04x from SBResource_MMIO register\n",
-                               val);
-                       goto setup_wdt;
-               } else
-                       pr_debug("MMIO address 0x%04x already in use\n", val);
-       } else
-               pr_debug("SBResource_MMIO is disabled(0x%04x)\n", val);
-
-       pr_notice("failed to find MMIO address, giving up.\n");
-       goto  unreg_region;
-
-setup_wdt:
-       tcobase_phys = val;
-
-       tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE);
-       if (!tcobase) {
-               pr_err("failed to get tcobase address\n");
-               goto unreg_mem_region;
+       tco->tcobase = devm_ioremap(dev, mmio_addr, SP5100_WDT_MEM_MAP_SIZE);
+       if (!tco->tcobase) {
+               dev_err(dev, "failed to get tcobase address\n");
+               ret = -ENOMEM;
+               goto unreg_region;
        }
 
-       pr_info("Using 0x%04x for watchdog MMIO address\n", val);
+       dev_info(dev, "Using 0x%08x for watchdog MMIO address\n", mmio_addr);
 
        /* Setup the watchdog timer */
-       tco_timer_enable();
+       tco_timer_enable(tco);
+
+       val = readl(SP5100_WDT_CONTROL(tco->tcobase));
+       if (val & SP5100_WDT_DISABLED) {
+               dev_err(dev, "Watchdog hardware is disabled\n");
+               ret = -ENODEV;
+               goto unreg_region;
+       }
 
-       /* Check that the watchdog action is set to reset the system */
-       val = readl(SP5100_WDT_CONTROL(tcobase));
        /*
         * Save WatchDogFired status, because WatchDogFired flag is
         * cleared here.
         */
-       tco_wdt_fired = val & SP5100_PM_WATCHDOG_FIRED;
-       val &= ~SP5100_PM_WATCHDOG_ACTION_RESET;
-       writel(val, SP5100_WDT_CONTROL(tcobase));
+       if (val & SP5100_WDT_FIRED)
+               wdd->bootstatus = WDIOF_CARDRESET;
+       /* Set watchdog action to reset the system */
+       val &= ~SP5100_WDT_ACTION_RESET;
+       writel(val, SP5100_WDT_CONTROL(tco->tcobase));
 
        /* Set a reasonable heartbeat before we stop the timer */
-       tco_timer_set_heartbeat(heartbeat);
+       tco_timer_set_timeout(wdd, wdd->timeout);
 
        /*
         * Stop the TCO before we change anything so we don't race with
         * a zeroed timer.
         */
-       tco_timer_stop();
+       tco_timer_stop(wdd);
 
-       /* Done */
-       return 1;
+       release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE);
 
-unreg_mem_region:
-       release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
-unreg_region:
-       release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
-exit:
        return 0;
-}
 
-static int sp5100_tco_init(struct platform_device *dev)
-{
-       int ret;
+unreg_region:
+       release_region(SP5100_IO_PM_INDEX_REG, SP5100_PM_IOPORTS_SIZE);
+       return ret;
+}
 
-       /*
-        * Check whether or not the hardware watchdog is there. If found, then
-        * set it up.
-        */
-       if (!sp5100_tco_setupdevice())
-               return -ENODEV;
+static struct watchdog_info sp5100_tco_wdt_info = {
+       .identity = "SP5100 TCO timer",
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+};
 
-       /* Check to see if last reboot was due to watchdog timeout */
-       pr_info("Last reboot was %striggered by watchdog.\n",
-               tco_wdt_fired ? "" : "not ");
+static const struct watchdog_ops sp5100_tco_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = tco_timer_start,
+       .stop = tco_timer_stop,
+       .ping = tco_timer_ping,
+       .set_timeout = tco_timer_set_timeout,
+};
 
-       /*
-        * Check that the heartbeat value is within it's range.
-        * If not, reset to the default.
-        */
-       if (tco_timer_set_heartbeat(heartbeat)) {
-               heartbeat = WATCHDOG_HEARTBEAT;
-               tco_timer_set_heartbeat(heartbeat);
-       }
+static int sp5100_tco_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct watchdog_device *wdd;
+       struct sp5100_tco *tco;
+       int ret;
 
-       ret = misc_register(&sp5100_tco_miscdev);
-       if (ret != 0) {
-               pr_err("cannot register miscdev on minor=%d (err=%d)\n",
-                      WATCHDOG_MINOR, ret);
-               goto exit;
+       tco = devm_kzalloc(dev, sizeof(*tco), GFP_KERNEL);
+       if (!tco)
+               return -ENOMEM;
+
+       tco->tco_reg_layout = tco_reg_layout(sp5100_tco_pci);
+
+       wdd = &tco->wdd;
+       wdd->parent = dev;
+       wdd->info = &sp5100_tco_wdt_info;
+       wdd->ops = &sp5100_tco_wdt_ops;
+       wdd->timeout = WATCHDOG_HEARTBEAT;
+       wdd->min_timeout = 1;
+       wdd->max_timeout = 0xffff;
+
+       if (watchdog_init_timeout(wdd, heartbeat, NULL))
+               dev_info(dev, "timeout value invalid, using %d\n",
+                        wdd->timeout);
+       watchdog_set_nowayout(wdd, nowayout);
+       watchdog_stop_on_reboot(wdd);
+       watchdog_stop_on_unregister(wdd);
+       watchdog_set_drvdata(wdd, tco);
+
+       ret = sp5100_tco_setupdevice(dev, wdd);
+       if (ret)
+               return ret;
+
+       ret = devm_watchdog_register_device(dev, wdd);
+       if (ret) {
+               dev_err(dev, "cannot register watchdog device (err=%d)\n", ret);
+               return ret;
        }
 
-       clear_bit(0, &timer_alive);
-
        /* Show module parameters */
-       pr_info("initialized (0x%p). heartbeat=%d sec (nowayout=%d)\n",
-               tcobase, heartbeat, nowayout);
-
-       return 0;
-
-exit:
-       iounmap(tcobase);
-       release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
-       release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
-       return ret;
-}
-
-static void sp5100_tco_cleanup(void)
-{
-       /* Stop the timer before we leave */
-       if (!nowayout)
-               tco_timer_stop();
-
-       /* Deregister */
-       misc_deregister(&sp5100_tco_miscdev);
-       iounmap(tcobase);
-       release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE);
-       release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE);
-}
+       dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n",
+                wdd->timeout, nowayout);
 
-static int sp5100_tco_remove(struct platform_device *dev)
-{
-       if (tcobase)
-               sp5100_tco_cleanup();
        return 0;
 }
 
-static void sp5100_tco_shutdown(struct platform_device *dev)
-{
-       tco_timer_stop();
-}
-
 static struct platform_driver sp5100_tco_driver = {
-       .probe          = sp5100_tco_init,
-       .remove         = sp5100_tco_remove,
-       .shutdown       = sp5100_tco_shutdown,
+       .probe          = sp5100_tco_probe,
        .driver         = {
-               .name   = TCO_MODULE_NAME,
+               .name   = TCO_DRIVER_NAME,
        },
 };
 
-static int __init sp5100_tco_init_module(void)
+/*
+ * Data for PCI driver interface
+ *
+ * This data only exists for exporting the supported
+ * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
+ * register a pci_driver, because someone else might
+ * want to register another driver on the same PCI id.
+ */
+static const struct pci_device_id sp5100_tco_pci_tbl[] = {
+       { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID,
+         PCI_ANY_ID, },
+       { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID,
+         PCI_ANY_ID, },
+       { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID,
+         PCI_ANY_ID, },
+       { 0, },                 /* End of list */
+};
+MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl);
+
+static int __init sp5100_tco_init(void)
 {
+       struct pci_dev *dev = NULL;
        int err;
 
-       pr_info("SP5100/SB800 TCO WatchDog Timer Driver v%s\n", TCO_VERSION);
+       /* Match the PCI device */
+       for_each_pci_dev(dev) {
+               if (pci_match_id(sp5100_tco_pci_tbl, dev) != NULL) {
+                       sp5100_tco_pci = dev;
+                       break;
+               }
+       }
+
+       if (!sp5100_tco_pci)
+               return -ENODEV;
+
+       pr_info("SP5100/SB800 TCO WatchDog Timer Driver\n");
 
        err = platform_driver_register(&sp5100_tco_driver);
        if (err)
                return err;
 
-       sp5100_tco_platform_device = platform_device_register_simple(
-                                       TCO_MODULE_NAME, -1, NULL, 0);
+       sp5100_tco_platform_device =
+               platform_device_register_simple(TCO_DRIVER_NAME, -1, NULL, 0);
        if (IS_ERR(sp5100_tco_platform_device)) {
                err = PTR_ERR(sp5100_tco_platform_device);
                goto unreg_platform_driver;
@@ -579,15 +482,14 @@ unreg_platform_driver:
        return err;
 }
 
-static void __exit sp5100_tco_cleanup_module(void)
+static void __exit sp5100_tco_exit(void)
 {
        platform_device_unregister(sp5100_tco_platform_device);
        platform_driver_unregister(&sp5100_tco_driver);
-       pr_info("SP5100/SB800 TCO Watchdog Module Unloaded\n");
 }
 
-module_init(sp5100_tco_init_module);
-module_exit(sp5100_tco_cleanup_module);
+module_init(sp5100_tco_init);
+module_exit(sp5100_tco_exit);
 
 MODULE_AUTHOR("Priyanka Gupta");
 MODULE_DESCRIPTION("TCO timer driver for SP5100/SB800 chipset");
index 1af4dee7133717f3165de37bdadc3c82c15a7b4a..87eaf357ae01fc373c1ae5d97498a1ee15ea2d42 100644 (file)
@@ -7,6 +7,8 @@
  *     TCO timer driver for sp5100 chipsets
  */
 
+#include <linux/bitops.h>
+
 /*
  * Some address definitions for the Watchdog
  */
 #define SP5100_WDT_CONTROL(base)       ((base) + 0x00) /* Watchdog Control */
 #define SP5100_WDT_COUNT(base)         ((base) + 0x04) /* Watchdog Count */
 
-#define SP5100_WDT_START_STOP_BIT      (1 << 0)
-#define SP5100_WDT_TRIGGER_BIT         (1 << 7)
+#define SP5100_WDT_START_STOP_BIT      BIT(0)
+#define SP5100_WDT_FIRED               BIT(1)
+#define SP5100_WDT_ACTION_RESET                BIT(2)
+#define SP5100_WDT_DISABLED            BIT(3)
+#define SP5100_WDT_TRIGGER_BIT         BIT(7)
 
 #define SP5100_PM_IOPORTS_SIZE         0x02
 
  * read them from a register.
  */
 
-/*  For SP5100/SB7x0 chipset */
+/*  For SP5100/SB7x0/SB8x0 chipset */
 #define SP5100_IO_PM_INDEX_REG         0xCD6
 #define SP5100_IO_PM_DATA_REG          0xCD7
 
+/* For SP5100/SB7x0 chipset */
 #define SP5100_SB_RESOURCE_MMIO_BASE   0x9C
 
 #define SP5100_PM_WATCHDOG_CONTROL     0x69
 #define SP5100_PM_WATCHDOG_BASE                0x6C
 
-#define SP5100_PM_WATCHDOG_FIRED       (1 << 1)
-#define SP5100_PM_WATCHDOG_ACTION_RESET        (1 << 2)
-
 #define SP5100_PCI_WATCHDOG_MISC_REG   0x41
-#define SP5100_PCI_WATCHDOG_DECODE_EN  (1 << 3)
+#define SP5100_PCI_WATCHDOG_DECODE_EN  BIT(3)
 
-#define SP5100_PM_WATCHDOG_DISABLE     (1 << 0)
-#define SP5100_PM_WATCHDOG_SECOND_RES  (3 << 1)
+#define SP5100_PM_WATCHDOG_DISABLE     ((u8)BIT(0))
+#define SP5100_PM_WATCHDOG_SECOND_RES  GENMASK(2, 1)
 
 #define SP5100_DEVNAME                 "SP5100 TCO"
 
-
 /*  For SB8x0(or later) chipset */
-#define SB800_IO_PM_INDEX_REG          0xCD6
-#define SB800_IO_PM_DATA_REG           0xCD7
-
 #define SB800_PM_ACPI_MMIO_EN          0x24
 #define SB800_PM_WATCHDOG_CONTROL      0x48
 #define SB800_PM_WATCHDOG_BASE         0x48
 #define SB800_PM_WATCHDOG_CONFIG       0x4C
 
-#define SB800_PCI_WATCHDOG_DECODE_EN   (1 << 0)
-#define SB800_PM_WATCHDOG_DISABLE      (1 << 2)
-#define SB800_PM_WATCHDOG_SECOND_RES   (3 << 0)
-#define SB800_ACPI_MMIO_DECODE_EN      (1 << 0)
-#define SB800_ACPI_MMIO_SEL            (1 << 1)
-
+#define SB800_PCI_WATCHDOG_DECODE_EN   BIT(0)
+#define SB800_PM_WATCHDOG_DISABLE      ((u8)BIT(1))
+#define SB800_PM_WATCHDOG_SECOND_RES   GENMASK(1, 0)
+#define SB800_ACPI_MMIO_DECODE_EN      BIT(0)
+#define SB800_ACPI_MMIO_SEL            BIT(1)
 
 #define SB800_PM_WDT_MMIO_OFFSET       0xB00
 
 #define SB800_DEVNAME                  "SB800 TCO"
+
+/* For recent chips with embedded FCH (rev 40+) */
+
+#define EFCH_PM_DECODEEN               0x00
+
+#define EFCH_PM_DECODEEN_WDT_TMREN     BIT(7)
+
+
+#define EFCH_PM_DECODEEN3              0x00
+#define EFCH_PM_DECODEEN_SECOND_RES    GENMASK(1, 0)
+#define EFCH_PM_WATCHDOG_DISABLE       ((u8)GENMASK(3, 2))
+
+/* WDT MMIO if enabled with PM00_DECODEEN_WDT_TMREN */
+#define EFCH_PM_WDT_ADDR               0xfeb00000
+
+#define EFCH_PM_ISACONTROL             0x04
+
+#define EFCH_PM_ISACONTROL_MMIOEN      BIT(1)
+
+#define EFCH_PM_ACPI_MMIO_ADDR         0xfed80000
+#define EFCH_PM_ACPI_MMIO_WDT_OFFSET   0x00000b00
diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c
new file mode 100644 (file)
index 0000000..a8b280f
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Spreadtrum watchdog driver
+ * Copyright (C) 2017 Spreadtrum - http://www.spreadtrum.com
+ *
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+#define SPRD_WDT_LOAD_LOW              0x0
+#define SPRD_WDT_LOAD_HIGH             0x4
+#define SPRD_WDT_CTRL                  0x8
+#define SPRD_WDT_INT_CLR               0xc
+#define SPRD_WDT_INT_RAW               0x10
+#define SPRD_WDT_INT_MSK               0x14
+#define SPRD_WDT_CNT_LOW               0x18
+#define SPRD_WDT_CNT_HIGH              0x1c
+#define SPRD_WDT_LOCK                  0x20
+#define SPRD_WDT_IRQ_LOAD_LOW          0x2c
+#define SPRD_WDT_IRQ_LOAD_HIGH         0x30
+
+/* WDT_CTRL */
+#define SPRD_WDT_INT_EN_BIT            BIT(0)
+#define SPRD_WDT_CNT_EN_BIT            BIT(1)
+#define SPRD_WDT_NEW_VER_EN            BIT(2)
+#define SPRD_WDT_RST_EN_BIT            BIT(3)
+
+/* WDT_INT_CLR */
+#define SPRD_WDT_INT_CLEAR_BIT         BIT(0)
+#define SPRD_WDT_RST_CLEAR_BIT         BIT(3)
+
+/* WDT_INT_RAW */
+#define SPRD_WDT_INT_RAW_BIT           BIT(0)
+#define SPRD_WDT_RST_RAW_BIT           BIT(3)
+#define SPRD_WDT_LD_BUSY_BIT           BIT(4)
+
+/* 1s equal to 32768 counter steps */
+#define SPRD_WDT_CNT_STEP              32768
+
+#define SPRD_WDT_UNLOCK_KEY            0xe551
+#define SPRD_WDT_MIN_TIMEOUT           3
+#define SPRD_WDT_MAX_TIMEOUT           60
+
+#define SPRD_WDT_CNT_HIGH_SHIFT                16
+#define SPRD_WDT_LOW_VALUE_MASK                GENMASK(15, 0)
+#define SPRD_WDT_LOAD_TIMEOUT          1000
+
+struct sprd_wdt {
+       void __iomem *base;
+       struct watchdog_device wdd;
+       struct clk *enable;
+       struct clk *rtc_enable;
+       int irq;
+};
+
+static inline struct sprd_wdt *to_sprd_wdt(struct watchdog_device *wdd)
+{
+       return container_of(wdd, struct sprd_wdt, wdd);
+}
+
+static inline void sprd_wdt_lock(void __iomem *addr)
+{
+       writel_relaxed(0x0, addr + SPRD_WDT_LOCK);
+}
+
+static inline void sprd_wdt_unlock(void __iomem *addr)
+{
+       writel_relaxed(SPRD_WDT_UNLOCK_KEY, addr + SPRD_WDT_LOCK);
+}
+
+static irqreturn_t sprd_wdt_isr(int irq, void *dev_id)
+{
+       struct sprd_wdt *wdt = (struct sprd_wdt *)dev_id;
+
+       sprd_wdt_unlock(wdt->base);
+       writel_relaxed(SPRD_WDT_INT_CLEAR_BIT, wdt->base + SPRD_WDT_INT_CLR);
+       sprd_wdt_lock(wdt->base);
+       watchdog_notify_pretimeout(&wdt->wdd);
+       return IRQ_HANDLED;
+}
+
+static u32 sprd_wdt_get_cnt_value(struct sprd_wdt *wdt)
+{
+       u32 val;
+
+       val = readl_relaxed(wdt->base + SPRD_WDT_CNT_HIGH) <<
+               SPRD_WDT_CNT_HIGH_SHIFT;
+       val |= readl_relaxed(wdt->base + SPRD_WDT_CNT_LOW) &
+               SPRD_WDT_LOW_VALUE_MASK;
+
+       return val;
+}
+
+static int sprd_wdt_load_value(struct sprd_wdt *wdt, u32 timeout,
+                              u32 pretimeout)
+{
+       u32 val, delay_cnt = 0;
+       u32 tmr_step = timeout * SPRD_WDT_CNT_STEP;
+       u32 prtmr_step = pretimeout * SPRD_WDT_CNT_STEP;
+
+       sprd_wdt_unlock(wdt->base);
+       writel_relaxed((tmr_step >> SPRD_WDT_CNT_HIGH_SHIFT) &
+                     SPRD_WDT_LOW_VALUE_MASK, wdt->base + SPRD_WDT_LOAD_HIGH);
+       writel_relaxed((tmr_step & SPRD_WDT_LOW_VALUE_MASK),
+                      wdt->base + SPRD_WDT_LOAD_LOW);
+       writel_relaxed((prtmr_step >> SPRD_WDT_CNT_HIGH_SHIFT) &
+                       SPRD_WDT_LOW_VALUE_MASK,
+                      wdt->base + SPRD_WDT_IRQ_LOAD_HIGH);
+       writel_relaxed(prtmr_step & SPRD_WDT_LOW_VALUE_MASK,
+                      wdt->base + SPRD_WDT_IRQ_LOAD_LOW);
+       sprd_wdt_lock(wdt->base);
+
+       /*
+        * Waiting the load value operation done,
+        * it needs two or three RTC clock cycles.
+        */
+       do {
+               val = readl_relaxed(wdt->base + SPRD_WDT_INT_RAW);
+               if (!(val & SPRD_WDT_LD_BUSY_BIT))
+                       break;
+
+               cpu_relax();
+       } while (delay_cnt++ < SPRD_WDT_LOAD_TIMEOUT);
+
+       if (delay_cnt >= SPRD_WDT_LOAD_TIMEOUT)
+               return -EBUSY;
+       return 0;
+}
+
+static int sprd_wdt_enable(struct sprd_wdt *wdt)
+{
+       u32 val;
+       int ret;
+
+       ret = clk_prepare_enable(wdt->enable);
+       if (ret)
+               return ret;
+       ret = clk_prepare_enable(wdt->rtc_enable);
+       if (ret)
+               return ret;
+
+       sprd_wdt_unlock(wdt->base);
+       val = readl_relaxed(wdt->base + SPRD_WDT_CTRL);
+       val |= SPRD_WDT_NEW_VER_EN;
+       writel_relaxed(val, wdt->base + SPRD_WDT_CTRL);
+       sprd_wdt_lock(wdt->base);
+       return 0;
+}
+
+static void sprd_wdt_disable(void *_data)
+{
+       struct sprd_wdt *wdt = _data;
+
+       sprd_wdt_unlock(wdt->base);
+       writel_relaxed(0x0, wdt->base + SPRD_WDT_CTRL);
+       sprd_wdt_lock(wdt->base);
+
+       clk_disable_unprepare(wdt->rtc_enable);
+       clk_disable_unprepare(wdt->enable);
+}
+
+static int sprd_wdt_start(struct watchdog_device *wdd)
+{
+       struct sprd_wdt *wdt = to_sprd_wdt(wdd);
+       u32 val;
+       int ret;
+
+       ret = sprd_wdt_load_value(wdt, wdd->timeout, wdd->pretimeout);
+       if (ret)
+               return ret;
+
+       sprd_wdt_unlock(wdt->base);
+       val = readl_relaxed(wdt->base + SPRD_WDT_CTRL);
+       val |= SPRD_WDT_CNT_EN_BIT | SPRD_WDT_INT_EN_BIT | SPRD_WDT_RST_EN_BIT;
+       writel_relaxed(val, wdt->base + SPRD_WDT_CTRL);
+       sprd_wdt_lock(wdt->base);
+       set_bit(WDOG_HW_RUNNING, &wdd->status);
+
+       return 0;
+}
+
+static int sprd_wdt_stop(struct watchdog_device *wdd)
+{
+       struct sprd_wdt *wdt = to_sprd_wdt(wdd);
+       u32 val;
+
+       sprd_wdt_unlock(wdt->base);
+       val = readl_relaxed(wdt->base + SPRD_WDT_CTRL);
+       val &= ~(SPRD_WDT_CNT_EN_BIT | SPRD_WDT_RST_EN_BIT |
+               SPRD_WDT_INT_EN_BIT);
+       writel_relaxed(val, wdt->base + SPRD_WDT_CTRL);
+       sprd_wdt_lock(wdt->base);
+       return 0;
+}
+
+static int sprd_wdt_set_timeout(struct watchdog_device *wdd,
+                               u32 timeout)
+{
+       struct sprd_wdt *wdt = to_sprd_wdt(wdd);
+
+       if (timeout == wdd->timeout)
+               return 0;
+
+       wdd->timeout = timeout;
+
+       return sprd_wdt_load_value(wdt, timeout, wdd->pretimeout);
+}
+
+static int sprd_wdt_set_pretimeout(struct watchdog_device *wdd,
+                                  u32 new_pretimeout)
+{
+       struct sprd_wdt *wdt = to_sprd_wdt(wdd);
+
+       if (new_pretimeout < wdd->min_timeout)
+               return -EINVAL;
+
+       wdd->pretimeout = new_pretimeout;
+
+       return sprd_wdt_load_value(wdt, wdd->timeout, new_pretimeout);
+}
+
+static u32 sprd_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+       struct sprd_wdt *wdt = to_sprd_wdt(wdd);
+       u32 val;
+
+       val = sprd_wdt_get_cnt_value(wdt);
+       val = val / SPRD_WDT_CNT_STEP;
+
+       return val;
+}
+
+static const struct watchdog_ops sprd_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = sprd_wdt_start,
+       .stop = sprd_wdt_stop,
+       .set_timeout = sprd_wdt_set_timeout,
+       .set_pretimeout = sprd_wdt_set_pretimeout,
+       .get_timeleft = sprd_wdt_get_timeleft,
+};
+
+static const struct watchdog_info sprd_wdt_info = {
+       .options = WDIOF_SETTIMEOUT |
+                  WDIOF_PRETIMEOUT |
+                  WDIOF_MAGICCLOSE |
+                  WDIOF_KEEPALIVEPING,
+       .identity = "Spreadtrum Watchdog Timer",
+};
+
+static int sprd_wdt_probe(struct platform_device *pdev)
+{
+       struct resource *wdt_res;
+       struct sprd_wdt *wdt;
+       int ret;
+
+       wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+       if (!wdt)
+               return -ENOMEM;
+
+       wdt_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       wdt->base = devm_ioremap_resource(&pdev->dev, wdt_res);
+       if (IS_ERR(wdt->base)) {
+               dev_err(&pdev->dev, "failed to map memory resource\n");
+               return PTR_ERR(wdt->base);
+       }
+
+       wdt->enable = devm_clk_get(&pdev->dev, "enable");
+       if (IS_ERR(wdt->enable)) {
+               dev_err(&pdev->dev, "can't get the enable clock\n");
+               return PTR_ERR(wdt->enable);
+       }
+
+       wdt->rtc_enable = devm_clk_get(&pdev->dev, "rtc_enable");
+       if (IS_ERR(wdt->rtc_enable)) {
+               dev_err(&pdev->dev, "can't get the rtc enable clock\n");
+               return PTR_ERR(wdt->rtc_enable);
+       }
+
+       wdt->irq = platform_get_irq(pdev, 0);
+       if (wdt->irq < 0) {
+               dev_err(&pdev->dev, "failed to get IRQ resource\n");
+               return wdt->irq;
+       }
+
+       ret = devm_request_irq(&pdev->dev, wdt->irq, sprd_wdt_isr,
+                              IRQF_NO_SUSPEND, "sprd-wdt", (void *)wdt);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register irq\n");
+               return ret;
+       }
+
+       wdt->wdd.info = &sprd_wdt_info;
+       wdt->wdd.ops = &sprd_wdt_ops;
+       wdt->wdd.parent = &pdev->dev;
+       wdt->wdd.min_timeout = SPRD_WDT_MIN_TIMEOUT;
+       wdt->wdd.max_timeout = SPRD_WDT_MAX_TIMEOUT;
+       wdt->wdd.timeout = SPRD_WDT_MAX_TIMEOUT;
+
+       ret = sprd_wdt_enable(wdt);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable wdt\n");
+               return ret;
+       }
+       ret = devm_add_action(&pdev->dev, sprd_wdt_disable, wdt);
+       if (ret) {
+               sprd_wdt_disable(wdt);
+               dev_err(&pdev->dev, "Failed to add wdt disable action\n");
+               return ret;
+       }
+
+       watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);
+       watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev);
+
+       ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdd);
+       if (ret) {
+               sprd_wdt_disable(wdt);
+               dev_err(&pdev->dev, "failed to register watchdog\n");
+               return ret;
+       }
+       platform_set_drvdata(pdev, wdt);
+
+       return 0;
+}
+
+static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev)
+{
+       struct watchdog_device *wdd = dev_get_drvdata(dev);
+       struct sprd_wdt *wdt = dev_get_drvdata(dev);
+
+       if (watchdog_active(wdd))
+               sprd_wdt_stop(&wdt->wdd);
+       sprd_wdt_disable(wdt);
+
+       return 0;
+}
+
+static int __maybe_unused sprd_wdt_pm_resume(struct device *dev)
+{
+       struct watchdog_device *wdd = dev_get_drvdata(dev);
+       struct sprd_wdt *wdt = dev_get_drvdata(dev);
+       int ret;
+
+       ret = sprd_wdt_enable(wdt);
+       if (ret)
+               return ret;
+
+       if (watchdog_active(wdd)) {
+               ret = sprd_wdt_start(&wdt->wdd);
+               if (ret) {
+                       sprd_wdt_disable(wdt);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops sprd_wdt_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(sprd_wdt_pm_suspend,
+                               sprd_wdt_pm_resume)
+};
+
+static const struct of_device_id sprd_wdt_match_table[] = {
+       { .compatible = "sprd,sp9860-wdt", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, sprd_wdt_match_table);
+
+static struct platform_driver sprd_watchdog_driver = {
+       .probe  = sprd_wdt_probe,
+       .driver = {
+               .name = "sprd-wdt",
+               .of_match_table = sprd_wdt_match_table,
+               .pm = &sprd_wdt_pm_ops,
+       },
+};
+module_platform_driver(sprd_watchdog_driver);
+
+MODULE_AUTHOR("Eric Long <eric.long@spreadtrum.com>");
+MODULE_DESCRIPTION("Spreadtrum Watchdog Timer Controller Driver");
+MODULE_LICENSE("GPL v2");
index be64a8699de3d07d8638db4a7aa207304583ed98..c97ad5619cb007d18e26f87eeae9d1035e5dfbdd 100644 (file)
@@ -1,12 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Driver for STM32 Independent Watchdog
  *
- * Copyright (C) Yannick Fertre 2017
- * Author: Yannick Fertre <yannick.fertre@st.com>
+ * Copyright (C) STMicroelectronics 2017
+ * Author: Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
  *
  * This driver is based on tegra_wdt.c
  *
- * License terms:  GNU General Public License (GPL), version 2
  */
 
 #include <linux/clk.h>
index 9728fa32c357b88f5cab03300c96aeaf9f6f1968..802e31b1416d5a11e84eefc2c614ecd58b27a295 100644 (file)
@@ -234,7 +234,6 @@ MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids);
 static int sunxi_wdt_probe(struct platform_device *pdev)
 {
        struct sunxi_wdt_dev *sunxi_wdt;
-       const struct of_device_id *device;
        struct resource *res;
        int err;
 
@@ -242,12 +241,10 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
        if (!sunxi_wdt)
                return -EINVAL;
 
-       device = of_match_device(sunxi_wdt_dt_ids, &pdev->dev);
-       if (!device)
+       sunxi_wdt->wdt_regs = of_device_get_match_data(&pdev->dev);
+       if (!sunxi_wdt->wdt_regs)
                return -ENODEV;
 
-       sunxi_wdt->wdt_regs = device->data;
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(sunxi_wdt->wdt_base))
index 8a8d952f8df96c15a1b7a4462f50edd233fb38f3..eb8fa25f8eb2957951195ec2ad0e94afb22f2319 100644 (file)
@@ -97,6 +97,7 @@ static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
 
 /**
  * watchdog_init_timeout() - initialize the timeout field
+ * @wdd: watchdog device
  * @timeout_parm: timeout module parameter
  * @dev: Device that stores the timeout-sec property
  *
index 1e971a50d7fb74ffc1d7af27e39ef4a3df4d4cc9..ffbdc4642ea55cbe3dd6e6105e4b47c0d35bb163 100644 (file)
 #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/hrtimer.h>     /* For hrtimers */
 #include <linux/kernel.h>      /* For printk/panic/... */
 #include <linux/kref.h>                /* For data references */
+#include <linux/kthread.h>     /* For kthread_work */
 #include <linux/miscdevice.h>  /* For handling misc devices */
 #include <linux/module.h>      /* For module stuff/... */
 #include <linux/mutex.h>       /* For mutexes */
 #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 <uapi/linux/sched/types.h>    /* For struct sched_param */
+
 #include "watchdog_core.h"
 #include "watchdog_pretimeout.h"
 
@@ -65,9 +67,10 @@ 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;
+       ktime_t last_keepalive;
+       ktime_t last_hw_keepalive;
+       struct hrtimer timer;
+       struct kthread_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 ? */
@@ -79,7 +82,7 @@ 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 struct kthread_worker *watchdog_kworker;
 
 static bool handle_boot_enabled =
        IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED);
@@ -107,18 +110,19 @@ static inline bool watchdog_need_worker(struct watchdog_device *wdd)
                (t && !watchdog_active(wdd) && watchdog_hw_running(wdd));
 }
 
-static long watchdog_next_keepalive(struct watchdog_device *wdd)
+static ktime_t 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;
+       ktime_t keepalive_interval;
+       ktime_t last_heartbeat, latest_heartbeat;
+       ktime_t virt_timeout;
        unsigned int hw_heartbeat_ms;
 
-       virt_timeout = wd_data->last_keepalive + msecs_to_jiffies(timeout_ms);
+       virt_timeout = ktime_add(wd_data->last_keepalive,
+                                ms_to_ktime(timeout_ms));
        hw_heartbeat_ms = min_not_zero(timeout_ms, wdd->max_hw_heartbeat_ms);
-       keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2);
+       keepalive_interval = ms_to_ktime(hw_heartbeat_ms / 2);
 
        if (!watchdog_active(wdd))
                return keepalive_interval;
@@ -128,8 +132,11 @@ static long watchdog_next_keepalive(struct watchdog_device *wdd)
         * 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);
+       last_heartbeat = ktime_sub(virt_timeout, ms_to_ktime(hw_heartbeat_ms));
+       latest_heartbeat = ktime_sub(last_heartbeat, ktime_get());
+       if (ktime_before(latest_heartbeat, keepalive_interval))
+               return latest_heartbeat;
+       return keepalive_interval;
 }
 
 static inline void watchdog_update_worker(struct watchdog_device *wdd)
@@ -137,29 +144,33 @@ 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);
+               ktime_t t = watchdog_next_keepalive(wdd);
 
                if (t > 0)
-                       mod_delayed_work(watchdog_wq, &wd_data->work, t);
+                       hrtimer_start(&wd_data->timer, t, HRTIMER_MODE_REL);
        } else {
-               cancel_delayed_work(&wd_data->work);
+               hrtimer_cancel(&wd_data->timer);
        }
 }
 
 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);
+       ktime_t earliest_keepalive, now;
        int err;
 
-       if (time_is_after_jiffies(earliest_keepalive)) {
-               mod_delayed_work(watchdog_wq, &wd_data->work,
-                                earliest_keepalive - jiffies);
+       earliest_keepalive = ktime_add(wd_data->last_hw_keepalive,
+                                      ms_to_ktime(wdd->min_hw_heartbeat_ms));
+       now = ktime_get();
+
+       if (ktime_after(earliest_keepalive, now)) {
+               hrtimer_start(&wd_data->timer,
+                             ktime_sub(earliest_keepalive, now),
+                             HRTIMER_MODE_REL);
                return 0;
        }
 
-       wd_data->last_hw_keepalive = jiffies;
+       wd_data->last_hw_keepalive = now;
 
        if (wdd->ops->ping)
                err = wdd->ops->ping(wdd);  /* ping the watchdog */
@@ -192,7 +203,7 @@ static int watchdog_ping(struct watchdog_device *wdd)
 
        set_bit(_WDOG_KEEPALIVE, &wd_data->status);
 
-       wd_data->last_keepalive = jiffies;
+       wd_data->last_keepalive = ktime_get();
        return __watchdog_ping(wdd);
 }
 
@@ -203,12 +214,11 @@ static bool watchdog_worker_should_ping(struct watchdog_core_data *wd_data)
        return wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd));
 }
 
-static void watchdog_ping_work(struct work_struct *work)
+static void watchdog_ping_work(struct kthread_work *work)
 {
        struct watchdog_core_data *wd_data;
 
-       wd_data = container_of(to_delayed_work(work), struct watchdog_core_data,
-                              work);
+       wd_data = container_of(work, struct watchdog_core_data, work);
 
        mutex_lock(&wd_data->lock);
        if (watchdog_worker_should_ping(wd_data))
@@ -216,6 +226,16 @@ static void watchdog_ping_work(struct work_struct *work)
        mutex_unlock(&wd_data->lock);
 }
 
+static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer)
+{
+       struct watchdog_core_data *wd_data;
+
+       wd_data = container_of(timer, struct watchdog_core_data, timer);
+
+       kthread_queue_work(watchdog_kworker, &wd_data->work);
+       return HRTIMER_NORESTART;
+}
+
 /*
  *     watchdog_start: wrapper to start the watchdog.
  *     @wdd: the watchdog device to start
@@ -230,7 +250,7 @@ static void watchdog_ping_work(struct work_struct *work)
 static int watchdog_start(struct watchdog_device *wdd)
 {
        struct watchdog_core_data *wd_data = wdd->wd_data;
-       unsigned long started_at;
+       ktime_t started_at;
        int err;
 
        if (watchdog_active(wdd))
@@ -238,7 +258,7 @@ static int watchdog_start(struct watchdog_device *wdd)
 
        set_bit(_WDOG_KEEPALIVE, &wd_data->status);
 
-       started_at = jiffies;
+       started_at = ktime_get();
        if (watchdog_hw_running(wdd) && wdd->ops->ping)
                err = wdd->ops->ping(wdd);
        else
@@ -720,7 +740,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
                err = watchdog_ping(wdd);
                if (err < 0)
                        break;
-               /* Fall */
+               /* fall through */
        case WDIOC_GETTIMEOUT:
                /* timeout == 0 means that we don't know the timeout */
                if (wdd->timeout == 0) {
@@ -769,6 +789,7 @@ static int watchdog_open(struct inode *inode, struct file *file)
 {
        struct watchdog_core_data *wd_data;
        struct watchdog_device *wdd;
+       bool hw_running;
        int err;
 
        /* Get the corresponding watchdog device */
@@ -788,7 +809,8 @@ 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 (!watchdog_hw_running(wdd) && !try_module_get(wdd->ops->owner)) {
+       hw_running = watchdog_hw_running(wdd);
+       if (!hw_running && !try_module_get(wdd->ops->owner)) {
                err = -EBUSY;
                goto out_clear;
        }
@@ -799,7 +821,7 @@ static int watchdog_open(struct inode *inode, struct file *file)
 
        file->private_data = wd_data;
 
-       if (!watchdog_hw_running(wdd))
+       if (!hw_running)
                kref_get(&wd_data->kref);
 
        /* dev/watchdog is a virtual (and thus non-seekable) filesystem */
@@ -919,10 +941,12 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
        wd_data->wdd = wdd;
        wdd->wd_data = wd_data;
 
-       if (!watchdog_wq)
+       if (IS_ERR_OR_NULL(watchdog_kworker))
                return -ENODEV;
 
-       INIT_DELAYED_WORK(&wd_data->work, watchdog_ping_work);
+       kthread_init_work(&wd_data->work, watchdog_ping_work);
+       hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       wd_data->timer.function = watchdog_timer_expired;
 
        if (wdd->id == 0) {
                old_wd_data = wd_data;
@@ -958,21 +982,20 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno)
        }
 
        /* Record time of most recent heartbeat as 'just before now'. */
-       wd_data->last_hw_keepalive = jiffies - 1;
+       wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1);
 
        /*
         * If the watchdog is running, prevent its driver from being unloaded,
         * and schedule an immediate ping.
         */
        if (watchdog_hw_running(wdd)) {
-               if (handle_boot_enabled) {
-                       __module_get(wdd->ops->owner);
-                       kref_get(&wd_data->kref);
-                       queue_delayed_work(watchdog_wq, &wd_data->work, 0);
-               } else {
+               __module_get(wdd->ops->owner);
+               kref_get(&wd_data->kref);
+               if (handle_boot_enabled)
+                       hrtimer_start(&wd_data->timer, 0, HRTIMER_MODE_REL);
+               else
                        pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n",
-                                       wdd->id);
-               }
+                               wdd->id);
        }
 
        return 0;
@@ -1006,7 +1029,8 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
                watchdog_stop(wdd);
        }
 
-       cancel_delayed_work_sync(&wd_data->work);
+       hrtimer_cancel(&wd_data->timer);
+       kthread_cancel_work_sync(&wd_data->work);
 
        kref_put(&wd_data->kref, watchdog_core_data_release);
 }
@@ -1110,13 +1134,14 @@ void watchdog_dev_unregister(struct watchdog_device *wdd)
 int __init watchdog_dev_init(void)
 {
        int err;
+       struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1,};
 
-       watchdog_wq = alloc_workqueue("watchdogd",
-                                     WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
-       if (!watchdog_wq) {
-               pr_err("Failed to create watchdog workqueue\n");
-               return -ENOMEM;
+       watchdog_kworker = kthread_create_worker(0, "watchdogd");
+       if (IS_ERR(watchdog_kworker)) {
+               pr_err("Failed to create watchdog kworker\n");
+               return PTR_ERR(watchdog_kworker);
        }
+       sched_setscheduler(watchdog_kworker->task, SCHED_FIFO, &param);
 
        err = class_register(&watchdog_class);
        if (err < 0) {
@@ -1135,7 +1160,7 @@ int __init watchdog_dev_init(void)
 err_alloc:
        class_unregister(&watchdog_class);
 err_register:
-       destroy_workqueue(watchdog_wq);
+       kthread_destroy_worker(watchdog_kworker);
        return err;
 }
 
@@ -1149,7 +1174,7 @@ void __exit watchdog_dev_exit(void)
 {
        unregister_chrdev_region(watchdog_devt, MAX_DOGS);
        class_unregister(&watchdog_class);
-       destroy_workqueue(watchdog_wq);
+       kthread_destroy_worker(watchdog_kworker);
 }
 
 module_param(handle_boot_enabled, bool, 0444);
index bc7addc2dc06d0f9ca4bfa09abdbedfb4252090f..10e2cda0ee5a426ba48bbbf625134fe76fdb0edd 100644 (file)
@@ -430,7 +430,7 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd,
                if (wdtpci_set_heartbeat(new_heartbeat))
                        return -EINVAL;
                wdtpci_ping();
-               /* Fall */
+               /* fall through */
        case WDIOC_GETTIMEOUT:
                return put_user(heartbeat, p);
        default:
index cf0e650c2015a5d1771f8b90c1e0cda2b9572350..f1c016d015b3bff4297ce7d245c92f89c14239a5 100644 (file)
@@ -9,10 +9,7 @@
  *     2 of the License, or (at your option) any later version.
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define DRV_NAME       "wdt"
-#define DRV_VERSION    "0.01"
+#define DRV_NAME       "xen_wdt"
 
 #include <linux/bug.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/ktime.h>
 #include <linux/init.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
 #include <xen/xen.h>
 #include <asm/xen/hypercall.h>
 #include <xen/interface/sched.h>
 
 static struct platform_device *platform_device;
-static DEFINE_SPINLOCK(wdt_lock);
 static struct sched_watchdog wdt;
-static __kernel_time_t wdt_expires;
-static bool is_active, expect_release;
+static time64_t wdt_expires;
 
 #define WATCHDOG_TIMEOUT 60 /* in seconds */
-static unsigned int timeout = WATCHDOG_TIMEOUT;
+static unsigned int timeout;
 module_param(timeout, uint, S_IRUGO);
 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
        "(default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
@@ -49,20 +41,18 @@ module_param(nowayout, bool, S_IRUGO);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
        "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
-static inline __kernel_time_t set_timeout(void)
+static inline time64_t set_timeout(struct watchdog_device *wdd)
 {
-       wdt.timeout = timeout;
-       return ktime_to_timespec(ktime_get()).tv_sec + timeout;
+       wdt.timeout = wdd->timeout;
+       return ktime_get_seconds() + wdd->timeout;
 }
 
-static int xen_wdt_start(void)
+static int xen_wdt_start(struct watchdog_device *wdd)
 {
-       __kernel_time_t expires;
+       time64_t expires;
        int err;
 
-       spin_lock(&wdt_lock);
-
-       expires = set_timeout();
+       expires = set_timeout(wdd);
        if (!wdt.id)
                err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
        else
@@ -74,36 +64,28 @@ static int xen_wdt_start(void)
        } else
                BUG_ON(!err);
 
-       spin_unlock(&wdt_lock);
-
        return err;
 }
 
-static int xen_wdt_stop(void)
+static int xen_wdt_stop(struct watchdog_device *wdd)
 {
        int err = 0;
 
-       spin_lock(&wdt_lock);
-
        wdt.timeout = 0;
        if (wdt.id)
                err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
        if (!err)
                wdt.id = 0;
 
-       spin_unlock(&wdt_lock);
-
        return err;
 }
 
-static int xen_wdt_kick(void)
+static int xen_wdt_kick(struct watchdog_device *wdd)
 {
-       __kernel_time_t expires;
+       time64_t expires;
        int err;
 
-       spin_lock(&wdt_lock);
-
-       expires = set_timeout();
+       expires = set_timeout(wdd);
        if (wdt.id)
                err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
        else
@@ -111,195 +93,72 @@ static int xen_wdt_kick(void)
        if (!err)
                wdt_expires = expires;
 
-       spin_unlock(&wdt_lock);
-
-       return err;
-}
-
-static int xen_wdt_open(struct inode *inode, struct file *file)
-{
-       int err;
-
-       /* /dev/watchdog can only be opened once */
-       if (xchg(&is_active, true))
-               return -EBUSY;
-
-       err = xen_wdt_start();
-       if (err == -EBUSY)
-               err = xen_wdt_kick();
-       return err ?: nonseekable_open(inode, file);
-}
-
-static int xen_wdt_release(struct inode *inode, struct file *file)
-{
-       int err = 0;
-
-       if (expect_release)
-               err = xen_wdt_stop();
-       else {
-               pr_crit("unexpected close, not stopping watchdog!\n");
-               xen_wdt_kick();
-       }
-       is_active = err;
-       expect_release = false;
        return err;
 }
 
-static ssize_t xen_wdt_write(struct file *file, const char __user *data,
-                            size_t len, loff_t *ppos)
+static unsigned int xen_wdt_get_timeleft(struct watchdog_device *wdd)
 {
-       /* See if we got the magic character 'V' and reload the timer */
-       if (len) {
-               if (!nowayout) {
-                       size_t i;
-
-                       /* in case it was set long ago */
-                       expect_release = false;
-
-                       /* scan to see whether or not we got the magic
-                          character */
-                       for (i = 0; i != len; i++) {
-                               char c;
-                               if (get_user(c, data + i))
-                                       return -EFAULT;
-                               if (c == 'V')
-                                       expect_release = true;
-                       }
-               }
-
-               /* someone wrote to us, we should reload the timer */
-               xen_wdt_kick();
-       }
-       return len;
+       return wdt_expires - ktime_get_seconds();
 }
 
-static long xen_wdt_ioctl(struct file *file, unsigned int cmd,
-                         unsigned long arg)
-{
-       int new_options, retval = -EINVAL;
-       int new_timeout;
-       int __user *argp = (void __user *)arg;
-       static const struct watchdog_info ident = {
-               .options =              WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
-               .firmware_version =     0,
-               .identity =             DRV_NAME,
-       };
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
-
-       case WDIOC_GETSTATUS:
-       case WDIOC_GETBOOTSTATUS:
-               return put_user(0, argp);
-
-       case WDIOC_SETOPTIONS:
-               if (get_user(new_options, argp))
-                       return -EFAULT;
-
-               if (new_options & WDIOS_DISABLECARD)
-                       retval = xen_wdt_stop();
-               if (new_options & WDIOS_ENABLECARD) {
-                       retval = xen_wdt_start();
-                       if (retval == -EBUSY)
-                               retval = xen_wdt_kick();
-               }
-               return retval;
-
-       case WDIOC_KEEPALIVE:
-               xen_wdt_kick();
-               return 0;
-
-       case WDIOC_SETTIMEOUT:
-               if (get_user(new_timeout, argp))
-                       return -EFAULT;
-               if (!new_timeout)
-                       return -EINVAL;
-               timeout = new_timeout;
-               xen_wdt_kick();
-               /* fall through */
-       case WDIOC_GETTIMEOUT:
-               return put_user(timeout, argp);
-
-       case WDIOC_GETTIMELEFT:
-               retval = wdt_expires - ktime_to_timespec(ktime_get()).tv_sec;
-               return put_user(retval, argp);
-       }
-
-       return -ENOTTY;
-}
+static struct watchdog_info xen_wdt_info = {
+       .identity = DRV_NAME,
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+};
 
-static const struct file_operations xen_wdt_fops = {
-       .owner =                THIS_MODULE,
-       .llseek =               no_llseek,
-       .write =                xen_wdt_write,
-       .unlocked_ioctl =       xen_wdt_ioctl,
-       .open =                 xen_wdt_open,
-       .release =              xen_wdt_release,
+static const struct watchdog_ops xen_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = xen_wdt_start,
+       .stop = xen_wdt_stop,
+       .ping = xen_wdt_kick,
+       .get_timeleft = xen_wdt_get_timeleft,
 };
 
-static struct miscdevice xen_wdt_miscdev = {
-       .minor =        WATCHDOG_MINOR,
-       .name =         "watchdog",
-       .fops =         &xen_wdt_fops,
+static struct watchdog_device xen_wdt_dev = {
+       .info = &xen_wdt_info,
+       .ops = &xen_wdt_ops,
+       .timeout = WATCHDOG_TIMEOUT,
 };
 
-static int xen_wdt_probe(struct platform_device *dev)
+static int xen_wdt_probe(struct platform_device *pdev)
 {
        struct sched_watchdog wd = { .id = ~0 };
        int ret = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wd);
 
-       switch (ret) {
-       case -EINVAL:
-               if (!timeout) {
-                       timeout = WATCHDOG_TIMEOUT;
-                       pr_info("timeout value invalid, using %d\n", timeout);
-               }
-
-               ret = misc_register(&xen_wdt_miscdev);
-               if (ret) {
-                       pr_err("cannot register miscdev on minor=%d (%d)\n",
-                              WATCHDOG_MINOR, ret);
-                       break;
-               }
-
-               pr_info("initialized (timeout=%ds, nowayout=%d)\n",
-                       timeout, nowayout);
-               break;
-
-       case -ENOSYS:
-               pr_info("not supported\n");
-               ret = -ENODEV;
-               break;
-
-       default:
-               pr_info("bogus return value %d\n", ret);
-               break;
+       if (ret == -ENOSYS) {
+               dev_err(&pdev->dev, "watchdog not supported by hypervisor\n");
+               return -ENODEV;
        }
 
-       return ret;
-}
+       if (ret != -EINVAL) {
+               dev_err(&pdev->dev, "unexpected hypervisor error (%d)\n", ret);
+               return -ENODEV;
+       }
 
-static int xen_wdt_remove(struct platform_device *dev)
-{
-       /* Stop the timer before we leave */
-       if (!nowayout)
-               xen_wdt_stop();
+       if (watchdog_init_timeout(&xen_wdt_dev, timeout, NULL))
+               dev_info(&pdev->dev, "timeout value invalid, using %d\n",
+                       xen_wdt_dev.timeout);
+       watchdog_set_nowayout(&xen_wdt_dev, nowayout);
+       watchdog_stop_on_reboot(&xen_wdt_dev);
+       watchdog_stop_on_unregister(&xen_wdt_dev);
+
+       ret = devm_watchdog_register_device(&pdev->dev, &xen_wdt_dev);
+       if (ret) {
+               dev_err(&pdev->dev, "cannot register watchdog device (%d)\n",
+                       ret);
+               return ret;
+       }
 
-       misc_deregister(&xen_wdt_miscdev);
+       dev_info(&pdev->dev, "initialized (timeout=%ds, nowayout=%d)\n",
+               xen_wdt_dev.timeout, nowayout);
 
        return 0;
 }
 
-static void xen_wdt_shutdown(struct platform_device *dev)
-{
-       xen_wdt_stop();
-}
-
 static int xen_wdt_suspend(struct platform_device *dev, pm_message_t state)
 {
        typeof(wdt.id) id = wdt.id;
-       int rc = xen_wdt_stop();
+       int rc = xen_wdt_stop(&xen_wdt_dev);
 
        wdt.id = id;
        return rc;
@@ -310,13 +169,11 @@ static int xen_wdt_resume(struct platform_device *dev)
        if (!wdt.id)
                return 0;
        wdt.id = 0;
-       return xen_wdt_start();
+       return xen_wdt_start(&xen_wdt_dev);
 }
 
 static struct platform_driver xen_wdt_driver = {
        .probe          = xen_wdt_probe,
-       .remove         = xen_wdt_remove,
-       .shutdown       = xen_wdt_shutdown,
        .suspend        = xen_wdt_suspend,
        .resume         = xen_wdt_resume,
        .driver         = {
@@ -331,8 +188,6 @@ static int __init xen_wdt_init_module(void)
        if (!xen_domain())
                return -ENODEV;
 
-       pr_info("Xen WatchDog Timer Driver v%s\n", DRV_VERSION);
-
        err = platform_driver_register(&xen_wdt_driver);
        if (err)
                return err;
@@ -351,7 +206,6 @@ static void __exit xen_wdt_cleanup_module(void)
 {
        platform_device_unregister(platform_device);
        platform_driver_unregister(&xen_wdt_driver);
-       pr_info("module unloaded\n");
 }
 
 module_init(xen_wdt_init_module);
@@ -359,5 +213,4 @@ module_exit(xen_wdt_cleanup_module);
 
 MODULE_AUTHOR("Jan Beulich <jbeulich@novell.com>");
 MODULE_DESCRIPTION("Xen WatchDog Timer Driver");
-MODULE_VERSION(DRV_VERSION);
 MODULE_LICENSE("GPL");