Merge tag 'linux-watchdog-5.4-rc1' of git://www.linux-watchdog.org/linux-watchdog
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 27 Sep 2019 18:17:38 +0000 (11:17 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 27 Sep 2019 18:17:38 +0000 (11:17 -0700)
Pull watchdog updates from Wim Van Sebroeck:

 - addition of AST2600, i.MX7ULP and F81803 watchdog support

 - removal of the w90x900 and ks8695 drivers

 - ziirave_wdt improvements

 - small fixes and improvements

* tag 'linux-watchdog-5.4-rc1' of git://www.linux-watchdog.org/linux-watchdog: (51 commits)
  watchdog: f71808e_wdt: Add F81803 support
  watchdog: qcom: remove unnecessary variable from private storage
  watchdog: qcom: support pre-timeout when the bark irq is available
  watchdog: imx_sc: this patch just fixes whitespaces
  watchdog: apseed: Add access_cs0 option for alt-boot
  watchdog: aspeed: add support for dual boot
  watchdog: orion_wdt: use timer1 as a pretimeout
  watchdog: Add i.MX7ULP watchdog support
  dt-bindings: watchdog: Add i.MX7ULP bindings
  dt-bindings: watchdog: sun4i: Add the watchdog clock
  dt-bindings: watchdog: sun4i: Add the watchdog interrupts
  dt-bindings: watchdog: Convert Allwinner watchdog to a schema
  dt-bindings: watchdog: Add YAML schemas for the generic watchdog bindings
  watchdog: aspeed: Add support for AST2600
  dt-bindings: watchdog: Add ast2600 compatible
  watchdog: ziirave_wdt: Update checked I2C functionality mask
  watchdog: ziirave_wdt: Drop ziirave_firm_write_block_data()
  watchdog: ziirave_wdt: Fix DOWNLOAD_START payload
  watchdog: ziirave_wdt: Drop status polling code
  watchdog: ziirave_wdt: Fix RESET_PROCESSOR payload
  ...

24 files changed:
Documentation/ABI/testing/sysfs-class-watchdog
Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
Documentation/devicetree/bindings/watchdog/fsl-imx7ulp-wdt.txt [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt [deleted file]
Documentation/devicetree/bindings/watchdog/watchdog.yaml [new file with mode: 0644]
Documentation/watchdog/watchdog-parameters.rst
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/aspeed_wdt.c
drivers/watchdog/ath79_wdt.c
drivers/watchdog/cpwd.c
drivers/watchdog/diag288_wdt.c
drivers/watchdog/f71808e_wdt.c
drivers/watchdog/imx2_wdt.c
drivers/watchdog/imx7ulp_wdt.c [new file with mode: 0644]
drivers/watchdog/imx_sc_wdt.c
drivers/watchdog/jz4740_wdt.c
drivers/watchdog/ks8695_wdt.c [deleted file]
drivers/watchdog/nuc900_wdt.c [deleted file]
drivers/watchdog/orion_wdt.c
drivers/watchdog/qcom-wdt.c
drivers/watchdog/sprd_wdt.c
drivers/watchdog/ziirave_wdt.c

index 6317ade..675f9b5 100644 (file)
@@ -72,3 +72,37 @@ Description:
                It is a read/write file. When read, the currently assigned
                pretimeout governor is returned.  When written, it sets
                the pretimeout governor.
+
+What:          /sys/class/watchdog/watchdog1/access_cs0
+Date:          August 2019
+Contact:       Ivan Mikhaylov <i.mikhaylov@yadro.com>,
+               Alexander Amelkin <a.amelkin@yadro.com>
+Description:
+               It is a read/write file. This attribute exists only if the
+               system has booted from the alternate flash chip due to
+               expiration of a watchdog timer of AST2400/AST2500 when
+               alternate boot function was enabled with 'aspeed,alt-boot'
+               devicetree option for that watchdog or with an appropriate
+               h/w strapping (for WDT2 only).
+
+               At alternate flash the 'access_cs0' sysfs node provides:
+                       ast2400: a way to get access to the primary SPI flash
+                               chip at CS0 after booting from the alternate
+                               chip at CS1.
+                       ast2500: a way to restore the normal address mapping
+                               from (CS0->CS1, CS1->CS0) to (CS0->CS0,
+                               CS1->CS1).
+
+               Clearing the boot code selection and timeout counter also
+               resets to the initial state the chip select line mapping. When
+               the SoC is in normal mapping state (i.e. booted from CS0),
+               clearing those bits does nothing for both versions of the SoC.
+               For alternate boot mode (booted from CS1 due to wdt2
+               expiration) the behavior differs as described above.
+
+               This option can be used with wdt2 (watchdog1) only.
+
+               When read, the current status of the boot code selection is
+               shown. When written with any non-zero value, it clears
+               the boot code selection and the timeout counter, which results
+               in chipselect reset for AST2400/AST2500.
diff --git a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml
new file mode 100644 (file)
index 0000000..3a54f58
--- /dev/null
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/watchdog/allwinner,sun4i-a10-wdt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner A10 Watchdog Device Tree Bindings
+
+allOf:
+  - $ref: "watchdog.yaml#"
+
+maintainers:
+  - Chen-Yu Tsai <wens@csie.org>
+  - Maxime Ripard <maxime.ripard@bootlin.com>
+
+properties:
+  compatible:
+    oneOf:
+      - const: allwinner,sun4i-a10-wdt
+      - const: allwinner,sun6i-a31-wdt
+      - items:
+          - const: allwinner,sun50i-a64-wdt
+          - const: allwinner,sun6i-a31-wdt
+      - items:
+          - const: allwinner,sun50i-h6-wdt
+          - const: allwinner,sun6i-a31-wdt
+      - items:
+          - const: allwinner,suniv-f1c100s-wdt
+          - const: allwinner,sun4i-a10-wdt
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - interrupts
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    wdt: watchdog@1c20c90 {
+        compatible = "allwinner,sun4i-a10-wdt";
+        reg = <0x01c20c90 0x10>;
+        interrupts = <24>;
+        clocks = <&osc24M>;
+        timeout-sec = <10>;
+    };
+
+...
index c5077a1..d78d4a8 100644 (file)
@@ -4,6 +4,7 @@ Required properties:
  - compatible: must be one of:
        - "aspeed,ast2400-wdt"
        - "aspeed,ast2500-wdt"
+       - "aspeed,ast2600-wdt"
 
  - reg: physical base address of the controller and length of memory mapped
    region
diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx7ulp-wdt.txt b/Documentation/devicetree/bindings/watchdog/fsl-imx7ulp-wdt.txt
new file mode 100644 (file)
index 0000000..f902508
--- /dev/null
@@ -0,0 +1,22 @@
+* Freescale i.MX7ULP Watchdog Timer (WDT) Controller
+
+Required properties:
+- compatible : Should be "fsl,imx7ulp-wdt"
+- reg : Should contain WDT registers location and length
+- interrupts : Should contain WDT interrupt
+- clocks: Should contain a phandle pointing to the gated peripheral clock.
+
+Optional properties:
+- timeout-sec : Contains the watchdog timeout in seconds
+
+Examples:
+
+wdog1: watchdog@403d0000 {
+       compatible = "fsl,imx7ulp-wdt";
+       reg = <0x403d0000 0x10000>;
+       interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
+       clocks = <&pcc2 IMX7ULP_CLK_WDG1>;
+       assigned-clocks = <&pcc2 IMX7ULP_CLK_WDG1>;
+       assigned-clocks-parents = <&scg1 IMX7ULP_CLK_FIRC_BUS_CLK>;
+       timeout-sec = <40>;
+};
diff --git a/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt b/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt
deleted file mode 100644 (file)
index e65198d..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-Allwinner SoCs Watchdog timer
-
-Required properties:
-
-- compatible : should be one of
-       "allwinner,sun4i-a10-wdt"
-       "allwinner,sun6i-a31-wdt"
-       "allwinner,sun50i-a64-wdt","allwinner,sun6i-a31-wdt"
-       "allwinner,sun50i-h6-wdt","allwinner,sun6i-a31-wdt"
-       "allwinner,suniv-f1c100s-wdt", "allwinner,sun4i-a10-wdt"
-- reg : Specifies base physical address and size of the registers.
-
-Optional properties:
-- timeout-sec : Contains the watchdog timeout in seconds
-
-Example:
-
-wdt: watchdog@1c20c90 {
-       compatible = "allwinner,sun4i-a10-wdt";
-       reg = <0x01c20c90 0x10>;
-       timeout-sec = <10>;
-};
diff --git a/Documentation/devicetree/bindings/watchdog/watchdog.yaml b/Documentation/devicetree/bindings/watchdog/watchdog.yaml
new file mode 100644 (file)
index 0000000..187bf6c
--- /dev/null
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/watchdog/watchdog.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Watchdog Generic Bindings
+
+maintainers:
+  - Guenter Roeck <linux@roeck-us.net>
+  - Wim Van Sebroeck <wim@linux-watchdog.org>
+
+description: |
+  This document describes generic bindings which can be used to
+  describe watchdog devices in a device tree.
+
+properties:
+  $nodename:
+    pattern: "^watchdog(@.*|-[0-9a-f])?$"
+
+  timeout-sec:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description:
+      Contains the watchdog timeout in seconds.
+
+...
index a3985cc..223c993 100644 (file)
@@ -301,15 +301,6 @@ ixp4xx_wdt:
 
 -------------------------------------------------
 
-ks8695_wdt:
-    wdt_time:
-       Watchdog time in seconds. (default=5)
-    nowayout:
-       Watchdog cannot be stopped once started
-       (default=kernel config parameter)
-
--------------------------------------------------
-
 machzwd:
     nowayout:
        Watchdog cannot be stopped once started
@@ -375,16 +366,6 @@ nic7018_wdt:
 
 -------------------------------------------------
 
-nuc900_wdt:
-    heartbeat:
-       Watchdog heartbeats in seconds.
-       (default = 15)
-    nowayout:
-       Watchdog cannot be stopped once started
-       (default=kernel config parameter)
-
--------------------------------------------------
-
 omap_wdt:
     timer_margin:
        initial watchdog timeout (in seconds)
index a45f9e3..58e7c10 100644 (file)
@@ -477,13 +477,6 @@ config IXP4XX_WATCHDOG
 
          Say N if you are unsure.
 
-config KS8695_WATCHDOG
-       tristate "KS8695 watchdog"
-       depends on ARCH_KS8695
-       help
-         Watchdog timer embedded into KS8695 processor. This will reboot your
-         system when the timeout is reached.
-
 config HAVE_S3C2410_WATCHDOG
        bool
        help
@@ -662,15 +655,6 @@ config STMP3XXX_RTC_WATCHDOG
          To compile this driver as a module, choose M here: the
          module will be called stmp3xxx_rtc_wdt.
 
-config NUC900_WATCHDOG
-       tristate "Nuvoton NUC900 watchdog"
-       depends on ARCH_W90X900 || COMPILE_TEST
-       help
-         Say Y here if to include support for the watchdog timer
-         for the Nuvoton NUC900 series SoCs.
-         To compile this driver as a module, choose M here: the
-         module will be called nuc900_wdt.
-
 config TS4800_WATCHDOG
        tristate "TS-4800 Watchdog"
        depends on HAS_IOMEM && OF
@@ -740,6 +724,19 @@ config IMX_SC_WDT
          To compile this driver as a module, choose M here: the
          module will be called imx_sc_wdt.
 
+config IMX7ULP_WDT
+       tristate "IMX7ULP Watchdog"
+       depends on ARCH_MXC || COMPILE_TEST
+       select WATCHDOG_CORE
+       help
+         This is the driver for the hardware watchdog on the Freescale
+         IMX7ULP and later processors. If you have one of these
+         processors and wish to have watchdog support enabled,
+         say Y, otherwise say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called imx7ulp_wdt.
+
 config UX500_WATCHDOG
        tristate "ST-Ericsson Ux500 watchdog"
        depends on MFD_DB8500_PRCMU
@@ -1046,8 +1043,8 @@ config F71808E_WDT
        depends on X86
        help
          This is the driver for the hardware watchdog on the Fintek F71808E,
-         F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866
-         Super I/O controllers.
+         F71862FG, F71868, F71869, F71882FG, F71889FG, F81803, F81865, and
+         F81866 Super I/O controllers.
 
          You can compile this driver directly into the kernel, or use
          it as a module.  The module will be called f71808e_wdt.
index 7caa920..2ee352b 100644 (file)
@@ -49,7 +49,6 @@ obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
 obj-$(CONFIG_977_WATCHDOG) += wdt977.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
 obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
 obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o
@@ -64,11 +63,11 @@ obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
 obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
 obj-$(CONFIG_NPCM7XX_WATCHDOG) += npcm_wdt.o
 obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
-obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
 obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
 obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
 obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
 obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o
+obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o
 obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
 obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
index cc71861..4ec0906 100644 (file)
@@ -34,6 +34,7 @@ static const struct aspeed_wdt_config ast2500_config = {
 static const struct of_device_id aspeed_wdt_of_table[] = {
        { .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config },
        { .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config },
+       { .compatible = "aspeed,ast2600-wdt", .data = &ast2500_config },
        { },
 };
 MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
@@ -53,6 +54,8 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
 #define   WDT_CTRL_ENABLE              BIT(0)
 #define WDT_TIMEOUT_STATUS     0x10
 #define   WDT_TIMEOUT_STATUS_BOOT_SECONDARY    BIT(1)
+#define WDT_CLEAR_TIMEOUT_STATUS       0x14
+#define   WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION    BIT(0)
 
 /*
  * WDT_RESET_WIDTH controls the characteristics of the external pulse (if
@@ -165,6 +168,60 @@ static int aspeed_wdt_restart(struct watchdog_device *wdd,
        return 0;
 }
 
+/* access_cs0 shows if cs0 is accessible, hence the reverted bit */
+static ssize_t access_cs0_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct aspeed_wdt *wdt = dev_get_drvdata(dev);
+       u32 status = readl(wdt->base + WDT_TIMEOUT_STATUS);
+
+       return sprintf(buf, "%u\n",
+                     !(status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY));
+}
+
+static ssize_t access_cs0_store(struct device *dev,
+                               struct device_attribute *attr, const char *buf,
+                               size_t size)
+{
+       struct aspeed_wdt *wdt = dev_get_drvdata(dev);
+       unsigned long val;
+
+       if (kstrtoul(buf, 10, &val))
+               return -EINVAL;
+
+       if (val)
+               writel(WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION,
+                      wdt->base + WDT_CLEAR_TIMEOUT_STATUS);
+
+       return size;
+}
+
+/*
+ * This attribute exists only if the system has booted from the alternate
+ * flash with 'alt-boot' option.
+ *
+ * At alternate flash the 'access_cs0' sysfs node provides:
+ *   ast2400: a way to get access to the primary SPI flash chip at CS0
+ *            after booting from the alternate chip at CS1.
+ *   ast2500: a way to restore the normal address mapping from
+ *            (CS0->CS1, CS1->CS0) to (CS0->CS0, CS1->CS1).
+ *
+ * Clearing the boot code selection and timeout counter also resets to the
+ * initial state the chip select line mapping. When the SoC is in normal
+ * mapping state (i.e. booted from CS0), clearing those bits does nothing for
+ * both versions of the SoC. For alternate boot mode (booted from CS1 due to
+ * wdt2 expiration) the behavior differs as described above.
+ *
+ * This option can be used with wdt2 (watchdog1) only.
+ */
+static DEVICE_ATTR_RW(access_cs0);
+
+static struct attribute *bswitch_attrs[] = {
+       &dev_attr_access_cs0.attr,
+       NULL
+};
+ATTRIBUTE_GROUPS(bswitch);
+
 static const struct watchdog_ops aspeed_wdt_ops = {
        .start          = aspeed_wdt_start,
        .stop           = aspeed_wdt_stop,
@@ -259,7 +316,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
                set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
        }
 
-       if (of_device_is_compatible(np, "aspeed,ast2500-wdt")) {
+       if ((of_device_is_compatible(np, "aspeed,ast2500-wdt")) ||
+               (of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {
                u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
 
                reg &= config->ext_pulse_width_mask;
@@ -306,9 +364,16 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
        }
 
        status = readl(wdt->base + WDT_TIMEOUT_STATUS);
-       if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY)
+       if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) {
                wdt->wdd.bootstatus = WDIOF_CARDRESET;
 
+               if (of_device_is_compatible(np, "aspeed,ast2400-wdt") ||
+                   of_device_is_compatible(np, "aspeed,ast2500-wdt"))
+                       wdt->wdd.groups = bswitch_groups;
+       }
+
+       dev_set_drvdata(dev, wdt);
+
        return devm_watchdog_register_device(dev, &wdt->wdd);
 }
 
index 2e09981..75de664 100644 (file)
@@ -302,7 +302,7 @@ static int ath79_wdt_remove(struct platform_device *pdev)
        return 0;
 }
 
-static void ath97_wdt_shutdown(struct platform_device *pdev)
+static void ath79_wdt_shutdown(struct platform_device *pdev)
 {
        ath79_wdt_disable();
 }
@@ -318,7 +318,7 @@ MODULE_DEVICE_TABLE(of, ath79_wdt_match);
 static struct platform_driver ath79_wdt_driver = {
        .probe          = ath79_wdt_probe,
        .remove         = ath79_wdt_remove,
-       .shutdown       = ath97_wdt_shutdown,
+       .shutdown       = ath79_wdt_shutdown,
        .driver         = {
                .name   = DRIVER_NAME,
                .of_match_table = of_match_ptr(ath79_wdt_match),
index b973b31..9393be5 100644 (file)
@@ -473,29 +473,6 @@ static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        return 0;
 }
 
-static long cpwd_compat_ioctl(struct file *file, unsigned int cmd,
-                             unsigned long arg)
-{
-       int rval = -ENOIOCTLCMD;
-
-       switch (cmd) {
-       /* solaris ioctls are specific to this driver */
-       case WIOCSTART:
-       case WIOCSTOP:
-       case WIOCGSTAT:
-               mutex_lock(&cpwd_mutex);
-               rval = cpwd_ioctl(file, cmd, arg);
-               mutex_unlock(&cpwd_mutex);
-               break;
-
-       /* everything else is handled by the generic compat layer */
-       default:
-               break;
-       }
-
-       return rval;
-}
-
 static ssize_t cpwd_write(struct file *file, const char __user *buf,
                          size_t count, loff_t *ppos)
 {
@@ -520,7 +497,7 @@ static ssize_t cpwd_read(struct file *file, char __user *buffer,
 static const struct file_operations cpwd_fops = {
        .owner =                THIS_MODULE,
        .unlocked_ioctl =       cpwd_ioctl,
-       .compat_ioctl =         cpwd_compat_ioctl,
+       .compat_ioctl =         compat_ptr_ioctl,
        .open =                 cpwd_open,
        .write =                cpwd_write,
        .read =                 cpwd_read,
index 181440b..aafc8d9 100644 (file)
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/slab.h>
-#include <linux/miscdevice.h>
 #include <linux/watchdog.h>
 #include <linux/suspend.h>
 #include <asm/ebcdic.h>
 #include <asm/diag.h>
 #include <linux/io.h>
-#include <linux/uaccess.h>
 
 #define MAX_CMDLEN 240
 #define DEFAULT_CMD "SYSTEM RESTART"
@@ -70,7 +68,6 @@ MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is ac
 module_param_named(nowayout, nowayout_info, bool, 0444);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = CONFIG_WATCHDOG_NOWAYOUT)");
 
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("vmwatchdog");
 
 static int __diag288(unsigned int func, unsigned int timeout,
index ff5cf1b..e46104c 100644 (file)
 #define SIO_REG_DEVID          0x20    /* Device ID (2 bytes) */
 #define SIO_REG_DEVREV         0x22    /* Device revision */
 #define SIO_REG_MANID          0x23    /* Fintek ID (2 bytes) */
+#define SIO_REG_CLOCK_SEL      0x26    /* Clock select */
 #define SIO_REG_ROM_ADDR_SEL   0x27    /* ROM address select */
 #define SIO_F81866_REG_PORT_SEL        0x27    /* F81866 Multi-Function Register */
+#define SIO_REG_TSI_LEVEL_SEL  0x28    /* TSI Level select */
 #define SIO_REG_MFUNCT1                0x29    /* Multi function select 1 */
 #define SIO_REG_MFUNCT2                0x2a    /* Multi function select 2 */
 #define SIO_REG_MFUNCT3                0x2b    /* Multi function select 3 */
@@ -49,6 +51,7 @@
 #define SIO_F71869A_ID         0x1007  /* Chipset ID */
 #define SIO_F71882_ID          0x0541  /* Chipset ID */
 #define SIO_F71889_ID          0x0723  /* Chipset ID */
+#define SIO_F81803_ID          0x1210  /* Chipset ID */
 #define SIO_F81865_ID          0x0704  /* Chipset ID */
 #define SIO_F81866_ID          0x1010  /* Chipset ID */
 
@@ -108,7 +111,7 @@ MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
        " given initial timeout. Zero (default) disables this feature.");
 
 enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
-            f81865, f81866};
+            f81803, f81865, f81866};
 
 static const char *f71808e_names[] = {
        "f71808fg",
@@ -118,6 +121,7 @@ static const char *f71808e_names[] = {
        "f71869",
        "f71882fg",
        "f71889fg",
+       "f81803",
        "f81865",
        "f81866",
 };
@@ -370,6 +374,14 @@ static int watchdog_start(void)
                        superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf);
                break;
 
+       case f81803:
+               /* Enable TSI Level register bank */
+               superio_clear_bit(watchdog.sioaddr, SIO_REG_CLOCK_SEL, 3);
+               /* Set pin 27 to WDTRST# */
+               superio_outb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f &
+                       superio_inb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL));
+               break;
+
        case f81865:
                /* Set pin 70 to WDTRST# */
                superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5);
@@ -809,6 +821,9 @@ static int __init f71808e_find(int sioaddr)
                /* Confirmed (by datasheet) not to have a watchdog. */
                err = -ENODEV;
                goto exit;
+       case SIO_F81803_ID:
+               watchdog.type = f81803;
+               break;
        case SIO_F81865_ID:
                watchdog.type = f81865;
                break;
index 32af397..8d019a9 100644 (file)
@@ -55,7 +55,7 @@
 
 #define IMX2_WDT_WMCR          0x08            /* Misc Register */
 
-#define IMX2_WDT_MAX_TIME      128
+#define IMX2_WDT_MAX_TIME      128U
 #define IMX2_WDT_DEFAULT_TIME  60              /* in seconds */
 
 #define WDOG_SEC_TO_COUNT(s)   ((s * 2 - 1) << 8)
@@ -180,7 +180,7 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
 {
        unsigned int actual;
 
-       actual = min(new_timeout, wdog->max_hw_heartbeat_ms * 1000);
+       actual = min(new_timeout, IMX2_WDT_MAX_TIME);
        __imx2_wdt_set_timeout(wdog, actual);
        wdog->timeout = new_timeout;
        return 0;
diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c
new file mode 100644 (file)
index 0000000..5ce5102
--- /dev/null
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+#define WDOG_CS                        0x0
+#define WDOG_CS_CMD32EN                BIT(13)
+#define WDOG_CS_ULK            BIT(11)
+#define WDOG_CS_RCS            BIT(10)
+#define WDOG_CS_EN             BIT(7)
+#define WDOG_CS_UPDATE         BIT(5)
+
+#define WDOG_CNT       0x4
+#define WDOG_TOVAL     0x8
+
+#define REFRESH_SEQ0   0xA602
+#define REFRESH_SEQ1   0xB480
+#define REFRESH                ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0)
+
+#define UNLOCK_SEQ0    0xC520
+#define UNLOCK_SEQ1    0xD928
+#define UNLOCK         ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0)
+
+#define DEFAULT_TIMEOUT        60
+#define MAX_TIMEOUT    128
+#define WDOG_CLOCK_RATE        1000
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0000);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct imx7ulp_wdt_device {
+       struct notifier_block restart_handler;
+       struct watchdog_device wdd;
+       void __iomem *base;
+       struct clk *clk;
+};
+
+static inline void imx7ulp_wdt_enable(void __iomem *base, bool enable)
+{
+       u32 val = readl(base + WDOG_CS);
+
+       writel(UNLOCK, base + WDOG_CNT);
+       if (enable)
+               writel(val | WDOG_CS_EN, base + WDOG_CS);
+       else
+               writel(val & ~WDOG_CS_EN, base + WDOG_CS);
+}
+
+static inline bool imx7ulp_wdt_is_enabled(void __iomem *base)
+{
+       u32 val = readl(base + WDOG_CS);
+
+       return val & WDOG_CS_EN;
+}
+
+static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
+{
+       struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
+
+       writel(REFRESH, wdt->base + WDOG_CNT);
+
+       return 0;
+}
+
+static int imx7ulp_wdt_start(struct watchdog_device *wdog)
+{
+       struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
+
+       imx7ulp_wdt_enable(wdt->base, true);
+
+       return 0;
+}
+
+static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
+{
+       struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
+
+       imx7ulp_wdt_enable(wdt->base, false);
+
+       return 0;
+}
+
+static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
+                                  unsigned int timeout)
+{
+       struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
+       u32 val = WDOG_CLOCK_RATE * timeout;
+
+       writel(UNLOCK, wdt->base + WDOG_CNT);
+       writel(val, wdt->base + WDOG_TOVAL);
+
+       wdog->timeout = timeout;
+
+       return 0;
+}
+
+static const struct watchdog_ops imx7ulp_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = imx7ulp_wdt_start,
+       .stop  = imx7ulp_wdt_stop,
+       .ping  = imx7ulp_wdt_ping,
+       .set_timeout = imx7ulp_wdt_set_timeout,
+};
+
+static const struct watchdog_info imx7ulp_wdt_info = {
+       .identity = "i.MX7ULP watchdog timer",
+       .options  = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+                   WDIOF_MAGICCLOSE,
+};
+
+static inline void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
+{
+       u32 val;
+
+       /* unlock the wdog for reconfiguration */
+       writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
+       writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
+
+       /* set an initial timeout value in TOVAL */
+       writel(timeout, base + WDOG_TOVAL);
+       /* enable 32bit command sequence and reconfigure */
+       val = BIT(13) | BIT(8) | BIT(5);
+       writel(val, base + WDOG_CS);
+}
+
+static void imx7ulp_wdt_action(void *data)
+{
+       clk_disable_unprepare(data);
+}
+
+static int imx7ulp_wdt_probe(struct platform_device *pdev)
+{
+       struct imx7ulp_wdt_device *imx7ulp_wdt;
+       struct device *dev = &pdev->dev;
+       struct watchdog_device *wdog;
+       int ret;
+
+       imx7ulp_wdt = devm_kzalloc(dev, sizeof(*imx7ulp_wdt), GFP_KERNEL);
+       if (!imx7ulp_wdt)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, imx7ulp_wdt);
+
+       imx7ulp_wdt->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(imx7ulp_wdt->base))
+               return PTR_ERR(imx7ulp_wdt->base);
+
+       imx7ulp_wdt->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(imx7ulp_wdt->clk)) {
+               dev_err(dev, "Failed to get watchdog clock\n");
+               return PTR_ERR(imx7ulp_wdt->clk);
+       }
+
+       ret = clk_prepare_enable(imx7ulp_wdt->clk);
+       if (ret)
+               return ret;
+
+       ret = devm_add_action_or_reset(dev, imx7ulp_wdt_action, imx7ulp_wdt->clk);
+       if (ret)
+               return ret;
+
+       wdog = &imx7ulp_wdt->wdd;
+       wdog->info = &imx7ulp_wdt_info;
+       wdog->ops = &imx7ulp_wdt_ops;
+       wdog->min_timeout = 1;
+       wdog->max_timeout = MAX_TIMEOUT;
+       wdog->parent = dev;
+       wdog->timeout = DEFAULT_TIMEOUT;
+
+       watchdog_init_timeout(wdog, 0, dev);
+       watchdog_stop_on_reboot(wdog);
+       watchdog_stop_on_unregister(wdog);
+       watchdog_set_drvdata(wdog, imx7ulp_wdt);
+       imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
+
+       return devm_watchdog_register_device(dev, wdog);
+}
+
+static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
+{
+       struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
+
+       if (watchdog_active(&imx7ulp_wdt->wdd))
+               imx7ulp_wdt_stop(&imx7ulp_wdt->wdd);
+
+       clk_disable_unprepare(imx7ulp_wdt->clk);
+
+       return 0;
+}
+
+static int __maybe_unused imx7ulp_wdt_resume(struct device *dev)
+{
+       struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
+       u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE;
+       int ret;
+
+       ret = clk_prepare_enable(imx7ulp_wdt->clk);
+       if (ret)
+               return ret;
+
+       if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base))
+               imx7ulp_wdt_init(imx7ulp_wdt->base, timeout);
+
+       if (watchdog_active(&imx7ulp_wdt->wdd))
+               imx7ulp_wdt_start(&imx7ulp_wdt->wdd);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend,
+                        imx7ulp_wdt_resume);
+
+static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
+       { .compatible = "fsl,imx7ulp-wdt", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids);
+
+static struct platform_driver imx7ulp_wdt_driver = {
+       .probe          = imx7ulp_wdt_probe,
+       .driver         = {
+               .name   = "imx7ulp-wdt",
+               .pm     = &imx7ulp_wdt_pm_ops,
+               .of_match_table = imx7ulp_wdt_dt_ids,
+       },
+};
+module_platform_driver(imx7ulp_wdt_driver);
+
+MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
+MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver");
+MODULE_LICENSE("GPL v2");
index 78eaaf7..7ea5cf5 100644 (file)
@@ -175,12 +175,9 @@ static int imx_sc_wdt_probe(struct platform_device *pdev)
        watchdog_stop_on_unregister(wdog);
 
        ret = devm_watchdog_register_device(dev, wdog);
-       if (ret) {
-               dev_err(dev, "Failed to register watchdog device\n");
-               return ret;
-       }
+       if (ret)
+               return ret;
+
        ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG,
                                       SC_IRQ_WDOG,
                                       true);
index d4a9091..c6052ae 100644 (file)
@@ -162,7 +162,6 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct jz4740_wdt_drvdata *drvdata;
        struct watchdog_device *jz4740_wdt;
-       int ret;
 
        drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata),
                               GFP_KERNEL);
diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c
deleted file mode 100644 (file)
index 1550ce3..0000000
+++ /dev/null
@@ -1,319 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Watchdog driver for Kendin/Micrel KS8695.
- *
- * (C) 2007 Andrew Victor
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/bitops.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/miscdevice.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/types.h>
-#include <linux/watchdog.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-#include <mach/hardware.h>
-
-#define KS8695_TMR_OFFSET      (0xF0000 + 0xE400)
-#define KS8695_TMR_VA          (KS8695_IO_VA + KS8695_TMR_OFFSET)
-
-/*
- * Timer registers
- */
-#define KS8695_TMCON           (0x00)          /* Timer Control Register */
-#define KS8695_T0TC            (0x08)          /* Timer 0 Timeout Count Register */
-#define TMCON_T0EN             (1 << 0)        /* Timer 0 Enable */
-
-/* Timer0 Timeout Counter Register */
-#define T0TC_WATCHDOG          (0xff)          /* Enable watchdog mode */
-
-#define WDT_DEFAULT_TIME       5       /* seconds */
-#define WDT_MAX_TIME           171     /* seconds */
-
-static int wdt_time = WDT_DEFAULT_TIME;
-static bool nowayout = WATCHDOG_NOWAYOUT;
-
-module_param(wdt_time, int, 0);
-MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
-                                       __MODULE_STRING(WDT_DEFAULT_TIME) ")");
-
-#ifdef CONFIG_WATCHDOG_NOWAYOUT
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
-                               __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-#endif
-
-
-static unsigned long ks8695wdt_busy;
-static DEFINE_SPINLOCK(ks8695_lock);
-
-/* ......................................................................... */
-
-/*
- * Disable the watchdog.
- */
-static inline void ks8695_wdt_stop(void)
-{
-       unsigned long tmcon;
-
-       spin_lock(&ks8695_lock);
-       /* disable timer0 */
-       tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
-       __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
-       spin_unlock(&ks8695_lock);
-}
-
-/*
- * Enable and reset the watchdog.
- */
-static inline void ks8695_wdt_start(void)
-{
-       unsigned long tmcon;
-       unsigned long tval = wdt_time * KS8695_CLOCK_RATE;
-
-       spin_lock(&ks8695_lock);
-       /* disable timer0 */
-       tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
-       __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
-
-       /* program timer0 */
-       __raw_writel(tval | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC);
-
-       /* re-enable timer0 */
-       tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
-       __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
-       spin_unlock(&ks8695_lock);
-}
-
-/*
- * Reload the watchdog timer.  (ie, pat the watchdog)
- */
-static inline void ks8695_wdt_reload(void)
-{
-       unsigned long tmcon;
-
-       spin_lock(&ks8695_lock);
-       /* disable, then re-enable timer0 */
-       tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
-       __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
-       __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
-       spin_unlock(&ks8695_lock);
-}
-
-/*
- * Change the watchdog time interval.
- */
-static int ks8695_wdt_settimeout(int new_time)
-{
-       /*
-        * All counting occurs at KS8695_CLOCK_RATE / 128 = 0.256 Hz
-        *
-        * Since WDV is a 16-bit counter, the maximum period is
-        * 65536 / 0.256 = 256 seconds.
-        */
-       if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
-               return -EINVAL;
-
-       /* Set new watchdog time. It will be used when
-          ks8695_wdt_start() is called. */
-       wdt_time = new_time;
-       return 0;
-}
-
-/* ......................................................................... */
-
-/*
- * Watchdog device is opened, and watchdog starts running.
- */
-static int ks8695_wdt_open(struct inode *inode, struct file *file)
-{
-       if (test_and_set_bit(0, &ks8695wdt_busy))
-               return -EBUSY;
-
-       ks8695_wdt_start();
-       return stream_open(inode, file);
-}
-
-/*
- * Close the watchdog device.
- * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
- *  disabled.
- */
-static int ks8695_wdt_close(struct inode *inode, struct file *file)
-{
-       /* Disable the watchdog when file is closed */
-       if (!nowayout)
-               ks8695_wdt_stop();
-       clear_bit(0, &ks8695wdt_busy);
-       return 0;
-}
-
-static const struct watchdog_info ks8695_wdt_info = {
-       .identity       = "ks8695 watchdog",
-       .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
-};
-
-/*
- * Handle commands from user-space.
- */
-static long ks8695_wdt_ioctl(struct file *file, unsigned int cmd,
-                                                       unsigned long arg)
-{
-       void __user *argp = (void __user *)arg;
-       int __user *p = argp;
-       int new_value;
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               return copy_to_user(argp, &ks8695_wdt_info,
-                                       sizeof(ks8695_wdt_info)) ? -EFAULT : 0;
-       case WDIOC_GETSTATUS:
-       case WDIOC_GETBOOTSTATUS:
-               return put_user(0, p);
-       case WDIOC_SETOPTIONS:
-               if (get_user(new_value, p))
-                       return -EFAULT;
-               if (new_value & WDIOS_DISABLECARD)
-                       ks8695_wdt_stop();
-               if (new_value & WDIOS_ENABLECARD)
-                       ks8695_wdt_start();
-               return 0;
-       case WDIOC_KEEPALIVE:
-               ks8695_wdt_reload();    /* pat the watchdog */
-               return 0;
-       case WDIOC_SETTIMEOUT:
-               if (get_user(new_value, p))
-                       return -EFAULT;
-               if (ks8695_wdt_settimeout(new_value))
-                       return -EINVAL;
-               /* Enable new time value */
-               ks8695_wdt_start();
-               /* Return current value */
-               return put_user(wdt_time, p);
-       case WDIOC_GETTIMEOUT:
-               return put_user(wdt_time, p);
-       default:
-               return -ENOTTY;
-       }
-}
-
-/*
- * Pat the watchdog whenever device is written to.
- */
-static ssize_t ks8695_wdt_write(struct file *file, const char *data,
-                                               size_t len, loff_t *ppos)
-{
-       ks8695_wdt_reload();            /* pat the watchdog */
-       return len;
-}
-
-/* ......................................................................... */
-
-static const struct file_operations ks8695wdt_fops = {
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .unlocked_ioctl = ks8695_wdt_ioctl,
-       .open           = ks8695_wdt_open,
-       .release        = ks8695_wdt_close,
-       .write          = ks8695_wdt_write,
-};
-
-static struct miscdevice ks8695wdt_miscdev = {
-       .minor          = WATCHDOG_MINOR,
-       .name           = "watchdog",
-       .fops           = &ks8695wdt_fops,
-};
-
-static int ks8695wdt_probe(struct platform_device *pdev)
-{
-       int res;
-
-       if (ks8695wdt_miscdev.parent)
-               return -EBUSY;
-       ks8695wdt_miscdev.parent = &pdev->dev;
-
-       res = misc_register(&ks8695wdt_miscdev);
-       if (res)
-               return res;
-
-       pr_info("KS8695 Watchdog Timer enabled (%d seconds%s)\n",
-               wdt_time, nowayout ? ", nowayout" : "");
-       return 0;
-}
-
-static int ks8695wdt_remove(struct platform_device *pdev)
-{
-       misc_deregister(&ks8695wdt_miscdev);
-       ks8695wdt_miscdev.parent = NULL;
-
-       return 0;
-}
-
-static void ks8695wdt_shutdown(struct platform_device *pdev)
-{
-       ks8695_wdt_stop();
-}
-
-#ifdef CONFIG_PM
-
-static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message)
-{
-       ks8695_wdt_stop();
-       return 0;
-}
-
-static int ks8695wdt_resume(struct platform_device *pdev)
-{
-       if (ks8695wdt_busy)
-               ks8695_wdt_start();
-       return 0;
-}
-
-#else
-#define ks8695wdt_suspend NULL
-#define ks8695wdt_resume       NULL
-#endif
-
-static struct platform_driver ks8695wdt_driver = {
-       .probe          = ks8695wdt_probe,
-       .remove         = ks8695wdt_remove,
-       .shutdown       = ks8695wdt_shutdown,
-       .suspend        = ks8695wdt_suspend,
-       .resume         = ks8695wdt_resume,
-       .driver         = {
-               .name   = "ks8695_wdt",
-       },
-};
-
-static int __init ks8695_wdt_init(void)
-{
-       /* Check that the heartbeat value is within range;
-          if not reset to the default */
-       if (ks8695_wdt_settimeout(wdt_time)) {
-               ks8695_wdt_settimeout(WDT_DEFAULT_TIME);
-               pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i"
-                                       ", using %d\n", wdt_time, WDT_MAX_TIME);
-       }
-       return platform_driver_register(&ks8695wdt_driver);
-}
-
-static void __exit ks8695_wdt_exit(void)
-{
-       platform_driver_unregister(&ks8695wdt_driver);
-}
-
-module_init(ks8695_wdt_init);
-module_exit(ks8695_wdt_exit);
-
-MODULE_AUTHOR("Andrew Victor");
-MODULE_DESCRIPTION("Watchdog driver for KS8695");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ks8695_wdt");
diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c
deleted file mode 100644 (file)
index db124ce..0000000
+++ /dev/null
@@ -1,302 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2009 Nuvoton technology corporation.
- *
- * Wan ZongShun <mcuos.com@gmail.com>
- */
-
-#include <linux/bitops.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/kernel.h>
-#include <linux/miscdevice.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/types.h>
-#include <linux/watchdog.h>
-#include <linux/uaccess.h>
-
-#define REG_WTCR               0x1c
-#define WTCLK                  (0x01 << 10)
-#define WTE                    (0x01 << 7)     /*wdt enable*/
-#define WTIS                   (0x03 << 4)
-#define WTIF                   (0x01 << 3)
-#define WTRF                   (0x01 << 2)
-#define WTRE                   (0x01 << 1)
-#define WTR                    (0x01 << 0)
-/*
- * The watchdog time interval can be calculated via following formula:
- * WTIS                real time interval (formula)
- * 0x00                ((2^ 14 ) * ((external crystal freq) / 256))seconds
- * 0x01                ((2^ 16 ) * ((external crystal freq) / 256))seconds
- * 0x02                ((2^ 18 ) * ((external crystal freq) / 256))seconds
- * 0x03                ((2^ 20 ) * ((external crystal freq) / 256))seconds
- *
- * The external crystal freq is 15Mhz in the nuc900 evaluation board.
- * So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
- * 0x03 = +- 16.92 seconds..
- */
-#define WDT_HW_TIMEOUT         0x02
-#define WDT_TIMEOUT            (HZ/2)
-#define WDT_HEARTBEAT          15
-
-static int heartbeat = WDT_HEARTBEAT;
-module_param(heartbeat, int, 0);
-MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
-       "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
-
-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) ")");
-
-struct nuc900_wdt {
-       struct clk       *wdt_clock;
-       struct platform_device *pdev;
-       void __iomem     *wdt_base;
-       char             expect_close;
-       struct timer_list timer;
-       spinlock_t       wdt_lock;
-       unsigned long next_heartbeat;
-};
-
-static unsigned long nuc900wdt_busy;
-static struct nuc900_wdt *nuc900_wdt;
-
-static inline void nuc900_wdt_keepalive(void)
-{
-       unsigned int val;
-
-       spin_lock(&nuc900_wdt->wdt_lock);
-
-       val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
-       val |= (WTR | WTIF);
-       __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
-
-       spin_unlock(&nuc900_wdt->wdt_lock);
-}
-
-static inline void nuc900_wdt_start(void)
-{
-       unsigned int val;
-
-       spin_lock(&nuc900_wdt->wdt_lock);
-
-       val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
-       val |= (WTRE | WTE | WTR | WTCLK | WTIF);
-       val &= ~WTIS;
-       val |= (WDT_HW_TIMEOUT << 0x04);
-       __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
-
-       spin_unlock(&nuc900_wdt->wdt_lock);
-
-       nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
-       mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
-}
-
-static inline void nuc900_wdt_stop(void)
-{
-       unsigned int val;
-
-       del_timer(&nuc900_wdt->timer);
-
-       spin_lock(&nuc900_wdt->wdt_lock);
-
-       val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
-       val &= ~WTE;
-       __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
-
-       spin_unlock(&nuc900_wdt->wdt_lock);
-}
-
-static inline void nuc900_wdt_ping(void)
-{
-       nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
-}
-
-static int nuc900_wdt_open(struct inode *inode, struct file *file)
-{
-
-       if (test_and_set_bit(0, &nuc900wdt_busy))
-               return -EBUSY;
-
-       nuc900_wdt_start();
-
-       return stream_open(inode, file);
-}
-
-static int nuc900_wdt_close(struct inode *inode, struct file *file)
-{
-       if (nuc900_wdt->expect_close == 42)
-               nuc900_wdt_stop();
-       else {
-               dev_crit(&nuc900_wdt->pdev->dev,
-                       "Unexpected close, not stopping watchdog!\n");
-               nuc900_wdt_ping();
-       }
-
-       nuc900_wdt->expect_close = 0;
-       clear_bit(0, &nuc900wdt_busy);
-       return 0;
-}
-
-static const struct watchdog_info nuc900_wdt_info = {
-       .identity       = "nuc900 watchdog",
-       .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
-                                               WDIOF_MAGICCLOSE,
-};
-
-static long nuc900_wdt_ioctl(struct file *file,
-                                       unsigned int cmd, unsigned long arg)
-{
-       void __user *argp = (void __user *)arg;
-       int __user *p = argp;
-       int new_value;
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               return copy_to_user(argp, &nuc900_wdt_info,
-                               sizeof(nuc900_wdt_info)) ? -EFAULT : 0;
-       case WDIOC_GETSTATUS:
-       case WDIOC_GETBOOTSTATUS:
-               return put_user(0, p);
-
-       case WDIOC_KEEPALIVE:
-               nuc900_wdt_ping();
-               return 0;
-
-       case WDIOC_SETTIMEOUT:
-               if (get_user(new_value, p))
-                       return -EFAULT;
-
-               heartbeat = new_value;
-               nuc900_wdt_ping();
-
-               return put_user(new_value, p);
-       case WDIOC_GETTIMEOUT:
-               return put_user(heartbeat, p);
-       default:
-               return -ENOTTY;
-       }
-}
-
-static ssize_t nuc900_wdt_write(struct file *file, const char __user *data,
-                                               size_t len, loff_t *ppos)
-{
-       if (!len)
-               return 0;
-
-       /* Scan for magic character */
-       if (!nowayout) {
-               size_t i;
-
-               nuc900_wdt->expect_close = 0;
-
-               for (i = 0; i < len; i++) {
-                       char c;
-                       if (get_user(c, data + i))
-                               return -EFAULT;
-                       if (c == 'V') {
-                               nuc900_wdt->expect_close = 42;
-                               break;
-                       }
-               }
-       }
-
-       nuc900_wdt_ping();
-       return len;
-}
-
-static void nuc900_wdt_timer_ping(struct timer_list *unused)
-{
-       if (time_before(jiffies, nuc900_wdt->next_heartbeat)) {
-               nuc900_wdt_keepalive();
-               mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
-       } else
-               dev_warn(&nuc900_wdt->pdev->dev, "Will reset the machine !\n");
-}
-
-static const struct file_operations nuc900wdt_fops = {
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .unlocked_ioctl = nuc900_wdt_ioctl,
-       .open           = nuc900_wdt_open,
-       .release        = nuc900_wdt_close,
-       .write          = nuc900_wdt_write,
-};
-
-static struct miscdevice nuc900wdt_miscdev = {
-       .minor          = WATCHDOG_MINOR,
-       .name           = "watchdog",
-       .fops           = &nuc900wdt_fops,
-};
-
-static int nuc900wdt_probe(struct platform_device *pdev)
-{
-       int ret = 0;
-
-       nuc900_wdt = devm_kzalloc(&pdev->dev, sizeof(*nuc900_wdt),
-                               GFP_KERNEL);
-       if (!nuc900_wdt)
-               return -ENOMEM;
-
-       nuc900_wdt->pdev = pdev;
-
-       spin_lock_init(&nuc900_wdt->wdt_lock);
-
-       nuc900_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(nuc900_wdt->wdt_base))
-               return PTR_ERR(nuc900_wdt->wdt_base);
-
-       nuc900_wdt->wdt_clock = devm_clk_get(&pdev->dev, NULL);
-       if (IS_ERR(nuc900_wdt->wdt_clock)) {
-               dev_err(&pdev->dev, "failed to find watchdog clock source\n");
-               return PTR_ERR(nuc900_wdt->wdt_clock);
-       }
-
-       clk_enable(nuc900_wdt->wdt_clock);
-
-       timer_setup(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0);
-
-       ret = misc_register(&nuc900wdt_miscdev);
-       if (ret) {
-               dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n",
-                       WATCHDOG_MINOR, ret);
-               goto err_clk;
-       }
-
-       return 0;
-
-err_clk:
-       clk_disable(nuc900_wdt->wdt_clock);
-       return ret;
-}
-
-static int nuc900wdt_remove(struct platform_device *pdev)
-{
-       misc_deregister(&nuc900wdt_miscdev);
-
-       clk_disable(nuc900_wdt->wdt_clock);
-
-       return 0;
-}
-
-static struct platform_driver nuc900wdt_driver = {
-       .probe          = nuc900wdt_probe,
-       .remove         = nuc900wdt_remove,
-       .driver         = {
-               .name   = "nuc900-wdt",
-       },
-};
-
-module_platform_driver(nuc900wdt_driver);
-
-MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
-MODULE_DESCRIPTION("Watchdog driver for NUC900");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:nuc900-wdt");
index cdb0d17..1cccf8e 100644 (file)
  * Watchdog timer block registers.
  */
 #define TIMER_CTRL             0x0000
-#define TIMER_A370_STATUS      0x04
+#define TIMER1_FIXED_ENABLE_BIT        BIT(12)
+#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
+#define TIMER1_ENABLE_BIT      BIT(2)
+
+#define TIMER_A370_STATUS      0x0004
+#define WDT_A370_EXPIRED       BIT(31)
+#define TIMER1_STATUS_BIT      BIT(8)
+
+#define TIMER1_VAL_OFF         0x001c
 
 #define WDT_MAX_CYCLE_COUNT    0xffffffff
 
@@ -43,9 +51,6 @@
 #define WDT_A370_RATIO_SHIFT   5
 #define WDT_A370_RATIO         (1 << WDT_A370_RATIO_SHIFT)
 
-#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
-#define WDT_A370_EXPIRED       BIT(31)
-
 static bool nowayout = WATCHDOG_NOWAYOUT;
 static int heartbeat = -1;             /* module parameter (seconds) */
 
@@ -158,6 +163,7 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev,
                                   struct orion_watchdog *dev)
 {
        int ret;
+       u32 val;
 
        dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed");
        if (IS_ERR(dev->clk))
@@ -168,10 +174,9 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev,
                return ret;
        }
 
-       /* Enable the fixed watchdog clock input */
-       atomic_io_modify(dev->reg + TIMER_CTRL,
-                        WDT_AXP_FIXED_ENABLE_BIT,
-                        WDT_AXP_FIXED_ENABLE_BIT);
+       /* Fix the wdt and timer1 clock freqency to 25MHz */
+       val = WDT_AXP_FIXED_ENABLE_BIT | TIMER1_FIXED_ENABLE_BIT;
+       atomic_io_modify(dev->reg + TIMER_CTRL, val, val);
 
        dev->clk_rate = clk_get_rate(dev->clk);
        return 0;
@@ -183,6 +188,10 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev)
        /* Reload watchdog duration */
        writel(dev->clk_rate * wdt_dev->timeout,
               dev->reg + dev->data->wdt_counter_offset);
+       if (dev->wdt.info->options & WDIOF_PRETIMEOUT)
+               writel(dev->clk_rate * (wdt_dev->timeout - wdt_dev->pretimeout),
+                      dev->reg + TIMER1_VAL_OFF);
+
        return 0;
 }
 
@@ -194,13 +203,18 @@ static int armada375_start(struct watchdog_device *wdt_dev)
        /* Set watchdog duration */
        writel(dev->clk_rate * wdt_dev->timeout,
               dev->reg + dev->data->wdt_counter_offset);
+       if (dev->wdt.info->options & WDIOF_PRETIMEOUT)
+               writel(dev->clk_rate * (wdt_dev->timeout - wdt_dev->pretimeout),
+                      dev->reg + TIMER1_VAL_OFF);
 
        /* Clear the watchdog expiration bit */
        atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
 
        /* Enable watchdog timer */
-       atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
-                                               dev->data->wdt_enable_bit);
+       reg = dev->data->wdt_enable_bit;
+       if (dev->wdt.info->options & WDIOF_PRETIMEOUT)
+               reg |= TIMER1_ENABLE_BIT;
+       atomic_io_modify(dev->reg + TIMER_CTRL, reg, reg);
 
        /* Enable reset on watchdog */
        reg = readl(dev->rstout);
@@ -277,7 +291,7 @@ static int orion_stop(struct watchdog_device *wdt_dev)
 static int armada375_stop(struct watchdog_device *wdt_dev)
 {
        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
-       u32 reg;
+       u32 reg, mask;
 
        /* Disable reset on watchdog */
        atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit,
@@ -287,7 +301,10 @@ static int armada375_stop(struct watchdog_device *wdt_dev)
        writel(reg, dev->rstout);
 
        /* Disable watchdog timer */
-       atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
+       mask = dev->data->wdt_enable_bit;
+       if (wdt_dev->info->options & WDIOF_PRETIMEOUT)
+               mask |= TIMER1_ENABLE_BIT;
+       atomic_io_modify(dev->reg + TIMER_CTRL, mask, 0);
 
        return 0;
 }
@@ -349,7 +366,7 @@ static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
        return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate;
 }
 
-static const struct watchdog_info orion_wdt_info = {
+static struct watchdog_info orion_wdt_info = {
        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
        .identity = "Orion Watchdog",
 };
@@ -368,6 +385,16 @@ static irqreturn_t orion_wdt_irq(int irq, void *devid)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t orion_wdt_pre_irq(int irq, void *devid)
+{
+       struct orion_watchdog *dev = devid;
+
+       atomic_io_modify(dev->reg + TIMER_A370_STATUS,
+                        TIMER1_STATUS_BIT, 0);
+       watchdog_notify_pretimeout(&dev->wdt);
+       return IRQ_HANDLED;
+}
+
 /*
  * The original devicetree binding for this driver specified only
  * one memory resource, so in order to keep DT backwards compatibility
@@ -589,6 +616,19 @@ static int orion_wdt_probe(struct platform_device *pdev)
                }
        }
 
+       /* Optional 2nd interrupt for pretimeout */
+       irq = platform_get_irq(pdev, 1);
+       if (irq > 0) {
+               orion_wdt_info.options |= WDIOF_PRETIMEOUT;
+               ret = devm_request_irq(&pdev->dev, irq, orion_wdt_pre_irq,
+                                      0, pdev->name, dev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to request IRQ\n");
+                       goto disable_clk;
+               }
+       }
+
+
        watchdog_set_nowayout(&dev->wdt, nowayout);
        ret = watchdog_register_device(&dev->wdt);
        if (ret)
index 7be7f87..a494543 100644 (file)
@@ -1,8 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /* Copyright (c) 2014, The Linux Foundation. All rights reserved.
  */
+#include <linux/bits.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -19,6 +21,9 @@ enum wdt_reg {
        WDT_BITE_TIME,
 };
 
+#define QCOM_WDT_ENABLE                BIT(0)
+#define QCOM_WDT_ENABLE_IRQ    BIT(1)
+
 static const u32 reg_offset_data_apcs_tmr[] = {
        [WDT_RST] = 0x38,
        [WDT_EN] = 0x40,
@@ -37,7 +42,6 @@ static const u32 reg_offset_data_kpss[] = {
 
 struct qcom_wdt {
        struct watchdog_device  wdd;
-       struct clk              *clk;
        unsigned long           rate;
        void __iomem            *base;
        const u32               *layout;
@@ -54,15 +58,35 @@ struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd)
        return container_of(wdd, struct qcom_wdt, wdd);
 }
 
+static inline int qcom_get_enable(struct watchdog_device *wdd)
+{
+       int enable = QCOM_WDT_ENABLE;
+
+       if (wdd->pretimeout)
+               enable |= QCOM_WDT_ENABLE_IRQ;
+
+       return enable;
+}
+
+static irqreturn_t qcom_wdt_isr(int irq, void *arg)
+{
+       struct watchdog_device *wdd = arg;
+
+       watchdog_notify_pretimeout(wdd);
+
+       return IRQ_HANDLED;
+}
+
 static int qcom_wdt_start(struct watchdog_device *wdd)
 {
        struct qcom_wdt *wdt = to_qcom_wdt(wdd);
+       unsigned int bark = wdd->timeout - wdd->pretimeout;
 
        writel(0, wdt_addr(wdt, WDT_EN));
        writel(1, wdt_addr(wdt, WDT_RST));
-       writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
+       writel(bark * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
        writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME));
-       writel(1, wdt_addr(wdt, WDT_EN));
+       writel(qcom_get_enable(wdd), wdt_addr(wdt, WDT_EN));
        return 0;
 }
 
@@ -89,6 +113,13 @@ static int qcom_wdt_set_timeout(struct watchdog_device *wdd,
        return qcom_wdt_start(wdd);
 }
 
+static int qcom_wdt_set_pretimeout(struct watchdog_device *wdd,
+                                  unsigned int timeout)
+{
+       wdd->pretimeout = timeout;
+       return qcom_wdt_start(wdd);
+}
+
 static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
                            void *data)
 {
@@ -105,7 +136,7 @@ static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
        writel(1, wdt_addr(wdt, WDT_RST));
        writel(timeout, wdt_addr(wdt, WDT_BARK_TIME));
        writel(timeout, wdt_addr(wdt, WDT_BITE_TIME));
-       writel(1, wdt_addr(wdt, WDT_EN));
+       writel(QCOM_WDT_ENABLE, wdt_addr(wdt, WDT_EN));
 
        /*
         * Actually make sure the above sequence hits hardware before sleeping.
@@ -121,6 +152,7 @@ static const struct watchdog_ops qcom_wdt_ops = {
        .stop           = qcom_wdt_stop,
        .ping           = qcom_wdt_ping,
        .set_timeout    = qcom_wdt_set_timeout,
+       .set_pretimeout = qcom_wdt_set_pretimeout,
        .restart        = qcom_wdt_restart,
        .owner          = THIS_MODULE,
 };
@@ -133,6 +165,15 @@ static const struct watchdog_info qcom_wdt_info = {
        .identity       = KBUILD_MODNAME,
 };
 
+static const struct watchdog_info qcom_wdt_pt_info = {
+       .options        = WDIOF_KEEPALIVEPING
+                       | WDIOF_MAGICCLOSE
+                       | WDIOF_SETTIMEOUT
+                       | WDIOF_PRETIMEOUT
+                       | WDIOF_CARDRESET,
+       .identity       = KBUILD_MODNAME,
+};
+
 static void qcom_clk_disable_unprepare(void *data)
 {
        clk_disable_unprepare(data);
@@ -146,7 +187,8 @@ static int qcom_wdt_probe(struct platform_device *pdev)
        struct device_node *np = dev->of_node;
        const u32 *regs;
        u32 percpu_offset;
-       int ret;
+       int irq, ret;
+       struct clk *clk;
 
        regs = of_device_get_match_data(dev);
        if (!regs) {
@@ -173,19 +215,18 @@ static int qcom_wdt_probe(struct platform_device *pdev)
        if (IS_ERR(wdt->base))
                return PTR_ERR(wdt->base);
 
-       wdt->clk = devm_clk_get(dev, NULL);
-       if (IS_ERR(wdt->clk)) {
+       clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(clk)) {
                dev_err(dev, "failed to get input clock\n");
-               return PTR_ERR(wdt->clk);
+               return PTR_ERR(clk);
        }
 
-       ret = clk_prepare_enable(wdt->clk);
+       ret = clk_prepare_enable(clk);
        if (ret) {
                dev_err(dev, "failed to setup clock\n");
                return ret;
        }
-       ret = devm_add_action_or_reset(dev, qcom_clk_disable_unprepare,
-                                      wdt->clk);
+       ret = devm_add_action_or_reset(dev, qcom_clk_disable_unprepare, clk);
        if (ret)
                return ret;
 
@@ -197,14 +238,31 @@ static int qcom_wdt_probe(struct platform_device *pdev)
         * that it would bite before a second elapses it's usefulness is
         * limited.  Bail if this is the case.
         */
-       wdt->rate = clk_get_rate(wdt->clk);
+       wdt->rate = clk_get_rate(clk);
        if (wdt->rate == 0 ||
            wdt->rate > 0x10000000U) {
                dev_err(dev, "invalid clock rate\n");
                return -EINVAL;
        }
 
-       wdt->wdd.info = &qcom_wdt_info;
+       /* check if there is pretimeout support */
+       irq = platform_get_irq(pdev, 0);
+       if (irq > 0) {
+               ret = devm_request_irq(dev, irq, qcom_wdt_isr,
+                                      IRQF_TRIGGER_RISING,
+                                      "wdt_bark", &wdt->wdd);
+               if (ret)
+                       return ret;
+
+               wdt->wdd.info = &qcom_wdt_pt_info;
+               wdt->wdd.pretimeout = 1;
+       } else {
+               if (irq == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+
+               wdt->wdd.info = &qcom_wdt_info;
+       }
+
        wdt->wdd.ops = &qcom_wdt_ops;
        wdt->wdd.min_timeout = 1;
        wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
index edba4e2..0bb17b0 100644 (file)
@@ -284,10 +284,8 @@ static int sprd_wdt_probe(struct platform_device *pdev)
        }
 
        wdt->irq = platform_get_irq(pdev, 0);
-       if (wdt->irq < 0) {
-               dev_err(dev, "failed to get IRQ resource\n");
+       if (wdt->irq < 0)
                return wdt->irq;
-       }
 
        ret = devm_request_irq(dev, wdt->irq, sprd_wdt_isr, IRQF_NO_SUSPEND,
                               "sprd-wdt", (void *)wdt);
index dec660c..4a363a8 100644 (file)
 #include <linux/version.h>
 #include <linux/watchdog.h>
 
+#include <asm/unaligned.h>
+
 #define ZIIRAVE_TIMEOUT_MIN    3
 #define ZIIRAVE_TIMEOUT_MAX    255
+#define ZIIRAVE_TIMEOUT_DEFAULT        30
 
 #define ZIIRAVE_PING_VALUE     0x0
 
@@ -48,16 +51,12 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
 
 #define ZIIRAVE_FIRM_PKT_TOTAL_SIZE    20
 #define ZIIRAVE_FIRM_PKT_DATA_SIZE     16
-#define ZIIRAVE_FIRM_FLASH_MEMORY_START        0x1600
-#define ZIIRAVE_FIRM_FLASH_MEMORY_END  0x2bbf
+#define ZIIRAVE_FIRM_FLASH_MEMORY_START        (2 * 0x1600)
+#define ZIIRAVE_FIRM_FLASH_MEMORY_END  (2 * 0x2bbf)
+#define ZIIRAVE_FIRM_PAGE_SIZE         128
 
 /* Received and ready for next Download packet. */
 #define ZIIRAVE_FIRM_DOWNLOAD_ACK      1
-/* Currently writing to flash. Retry Download status in a moment! */
-#define ZIIRAVE_FIRM_DOWNLOAD_BUSY     2
-
-/* Wait for ACK timeout in ms */
-#define ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT      50
 
 /* Firmware commands */
 #define ZIIRAVE_CMD_DOWNLOAD_START             0x10
@@ -68,6 +67,12 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
 #define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER         0x0c
 #define ZIIRAVE_CMD_DOWNLOAD_PACKET            0x0e
 
+#define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC   1
+#define ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC      1
+
+#define ZIIRAVE_FW_VERSION_FMT "02.%02u.%02u"
+#define ZIIRAVE_BL_VERSION_FMT "01.%02u.%02u"
+
 struct ziirave_wdt_rev {
        unsigned char major;
        unsigned char minor;
@@ -165,67 +170,37 @@ static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd)
        return ret;
 }
 
-static int ziirave_firm_wait_for_ack(struct watchdog_device *wdd)
+static int ziirave_firm_read_ack(struct watchdog_device *wdd)
 {
        struct i2c_client *client = to_i2c_client(wdd->parent);
        int ret;
-       unsigned long timeout;
 
-       timeout = jiffies + msecs_to_jiffies(ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT);
-       do {
-               if (time_after(jiffies, timeout))
-                       return -ETIMEDOUT;
-
-               usleep_range(5000, 10000);
-
-               ret = i2c_smbus_read_byte(client);
-               if (ret < 0) {
-                       dev_err(&client->dev, "Failed to read byte\n");
-                       return ret;
-               }
-       } while (ret == ZIIRAVE_FIRM_DOWNLOAD_BUSY);
+       ret = i2c_smbus_read_byte(client);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed to read status byte\n");
+               return ret;
+       }
 
        return ret == ZIIRAVE_FIRM_DOWNLOAD_ACK ? 0 : -EIO;
 }
 
-static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u16 addr)
+static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u32 addr)
 {
        struct i2c_client *client = to_i2c_client(wdd->parent);
+       const u16 addr16 = (u16)addr / 2;
        u8 address[2];
 
-       address[0] = addr & 0xff;
-       address[1] = (addr >> 8) & 0xff;
+       put_unaligned_le16(addr16, address);
 
        return i2c_smbus_write_block_data(client,
                                          ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR,
-                                         ARRAY_SIZE(address), address);
-}
-
-static int ziirave_firm_write_block_data(struct watchdog_device *wdd,
-                                        u8 command, u8 length, const u8 *data,
-                                        bool wait_for_ack)
-{
-       struct i2c_client *client = to_i2c_client(wdd->parent);
-       int ret;
-
-       ret = i2c_smbus_write_block_data(client, command, length, data);
-       if (ret) {
-               dev_err(&client->dev,
-                       "Failed to send command 0x%02x: %d\n", command, ret);
-               return ret;
-       }
-
-       if (wait_for_ack)
-               ret = ziirave_firm_wait_for_ack(wdd);
-
-       return ret;
+                                         sizeof(address), address);
 }
 
-static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command,
-                                  u8 byte, bool wait_for_ack)
+static bool ziirave_firm_addr_readonly(u32 addr)
 {
-       return ziirave_firm_write_block_data(wdd, command, 1, &byte,
-                                            wait_for_ack);
+       return addr < ZIIRAVE_FIRM_FLASH_MEMORY_START ||
+              addr > ZIIRAVE_FIRM_FLASH_MEMORY_END;
 }
 
 /*
@@ -240,35 +215,53 @@ static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command,
  *     Data0 .. Data15: Array of 16 bytes of data.
  *     Checksum: Checksum byte to verify data integrity.
  */
-static int ziirave_firm_write_pkt(struct watchdog_device *wdd,
-                                 const struct ihex_binrec *rec)
+static int __ziirave_firm_write_pkt(struct watchdog_device *wdd,
+                                   u32 addr, const u8 *data, u8 len)
 {
+       const u16 addr16 = (u16)addr / 2;
        struct i2c_client *client = to_i2c_client(wdd->parent);
        u8 i, checksum = 0, packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE];
        int ret;
-       u16 addr;
 
-       memset(packet, 0, ARRAY_SIZE(packet));
+       /* Check max data size */
+       if (len > ZIIRAVE_FIRM_PKT_DATA_SIZE) {
+               dev_err(&client->dev, "Firmware packet too long (%d)\n",
+                       len);
+               return -EMSGSIZE;
+       }
+
+       /*
+        * Ignore packets that are targeting program memory outisde of
+        * app partition, since they will be ignored by the
+        * bootloader. At the same time, we need to make sure we'll
+        * allow zero length packet that will be sent as the last step
+        * of firmware update
+        */
+       if (len && ziirave_firm_addr_readonly(addr))
+               return 0;
 
        /* Packet length */
-       packet[0] = (u8)be16_to_cpu(rec->len);
+       packet[0] = len;
        /* Packet address */
-       addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1;
-       packet[1] = addr & 0xff;
-       packet[2] = (addr & 0xff00) >> 8;
+       put_unaligned_le16(addr16, packet + 1);
 
-       /* Packet data */
-       if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE)
-               return -EMSGSIZE;
-       memcpy(packet + 3, rec->data, be16_to_cpu(rec->len));
+       memcpy(packet + 3, data, len);
+       memset(packet + 3 + len, 0, ZIIRAVE_FIRM_PKT_DATA_SIZE - len);
 
        /* Packet checksum */
-       for (i = 0; i < ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1; i++)
+       for (i = 0; i < len + 3; i++)
                checksum += packet[i];
        packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1] = checksum;
 
-       ret = ziirave_firm_write_block_data(wdd, ZIIRAVE_CMD_DOWNLOAD_PACKET,
-                                           ARRAY_SIZE(packet), packet, true);
+       ret = i2c_smbus_write_block_data(client, ZIIRAVE_CMD_DOWNLOAD_PACKET,
+                                        sizeof(packet), packet);
+       if (ret) {
+               dev_err(&client->dev,
+                       "Failed to send DOWNLOAD_PACKET: %d\n", ret);
+               return ret;
+       }
+
+       ret = ziirave_firm_read_ack(wdd);
        if (ret)
                dev_err(&client->dev,
                      "Failed to write firmware packet at address 0x%04x: %d\n",
@@ -277,6 +270,30 @@ static int ziirave_firm_write_pkt(struct watchdog_device *wdd,
        return ret;
 }
 
+static int ziirave_firm_write_pkt(struct watchdog_device *wdd,
+                                 u32 addr, const u8 *data, u8 len)
+{
+       const u8 max_write_len = ZIIRAVE_FIRM_PAGE_SIZE -
+               (addr - ALIGN_DOWN(addr, ZIIRAVE_FIRM_PAGE_SIZE));
+       int ret;
+
+       if (len > max_write_len) {
+               /*
+                * If data crossed page boundary we need to split this
+                * write in two
+                */
+               ret = __ziirave_firm_write_pkt(wdd, addr, data, max_write_len);
+               if (ret)
+                       return ret;
+
+               addr += max_write_len;
+               data += max_write_len;
+               len  -= max_write_len;
+       }
+
+       return __ziirave_firm_write_pkt(wdd, addr, data, len);
+}
+
 static int ziirave_firm_verify(struct watchdog_device *wdd,
                               const struct firmware *fw)
 {
@@ -284,16 +301,12 @@ static int ziirave_firm_verify(struct watchdog_device *wdd,
        const struct ihex_binrec *rec;
        int i, ret;
        u8 data[ZIIRAVE_FIRM_PKT_DATA_SIZE];
-       u16 addr;
 
        for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
-               /* Zero length marks end of records */
-               if (!be16_to_cpu(rec->len))
-                       break;
+               const u16 len = be16_to_cpu(rec->len);
+               const u32 addr = be32_to_cpu(rec->addr);
 
-               addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1;
-               if (addr < ZIIRAVE_FIRM_FLASH_MEMORY_START ||
-                   addr > ZIIRAVE_FIRM_FLASH_MEMORY_END)
+               if (ziirave_firm_addr_readonly(addr))
                        continue;
 
                ret = ziirave_firm_set_read_addr(wdd, addr);
@@ -304,7 +317,7 @@ static int ziirave_firm_verify(struct watchdog_device *wdd,
                        return ret;
                }
 
-               for (i = 0; i < ARRAY_SIZE(data); i++) {
+               for (i = 0; i < len; i++) {
                        ret = i2c_smbus_read_byte_data(client,
                                                ZIIRAVE_CMD_DOWNLOAD_READ_BYTE);
                        if (ret < 0) {
@@ -315,7 +328,7 @@ static int ziirave_firm_verify(struct watchdog_device *wdd,
                        data[i] = ret;
                }
 
-               if (memcmp(data, rec->data, be16_to_cpu(rec->len))) {
+               if (memcmp(data, rec->data, len)) {
                        dev_err(&client->dev,
                                "Firmware mismatch at address 0x%04x\n", addr);
                        return -EINVAL;
@@ -329,97 +342,45 @@ static int ziirave_firm_upload(struct watchdog_device *wdd,
                               const struct firmware *fw)
 {
        struct i2c_client *client = to_i2c_client(wdd->parent);
-       int ret, words_till_page_break;
        const struct ihex_binrec *rec;
-       struct ihex_binrec *rec_new;
+       int ret;
 
-       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_JUMP_TO_BOOTLOADER, 1,
-                                     false);
-       if (ret)
+       ret = i2c_smbus_write_byte_data(client,
+                                       ZIIRAVE_CMD_JUMP_TO_BOOTLOADER,
+                                       ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC);
+       if (ret) {
+               dev_err(&client->dev, "Failed to jump to bootloader\n");
                return ret;
+       }
 
        msleep(500);
 
-       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_START, 1, true);
-       if (ret)
+       ret = i2c_smbus_write_byte(client, ZIIRAVE_CMD_DOWNLOAD_START);
+       if (ret) {
+               dev_err(&client->dev, "Failed to start download\n");
                return ret;
+       }
+
+       ret = ziirave_firm_read_ack(wdd);
+       if (ret) {
+               dev_err(&client->dev, "No ACK for start download\n");
+               return ret;
+       }
 
        msleep(500);
 
        for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
-               /* Zero length marks end of records */
-               if (!be16_to_cpu(rec->len))
-                       break;
-
-               /* Check max data size */
-               if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) {
-                       dev_err(&client->dev, "Firmware packet too long (%d)\n",
-                               be16_to_cpu(rec->len));
-                       return -EMSGSIZE;
-               }
-
-               /* Calculate words till page break */
-               words_till_page_break = (64 - ((be32_to_cpu(rec->addr) >> 1) &
-                                        0x3f));
-               if ((be16_to_cpu(rec->len) >> 1) > words_till_page_break) {
-                       /*
-                        * Data in passes page boundary, so we need to split in
-                        * two blocks of data. Create a packet with the first
-                        * block of data.
-                        */
-                       rec_new = kzalloc(sizeof(struct ihex_binrec) +
-                                         (words_till_page_break << 1),
-                                         GFP_KERNEL);
-                       if (!rec_new)
-                               return -ENOMEM;
-
-                       rec_new->len = cpu_to_be16(words_till_page_break << 1);
-                       rec_new->addr = rec->addr;
-                       memcpy(rec_new->data, rec->data,
-                              be16_to_cpu(rec_new->len));
-
-                       ret = ziirave_firm_write_pkt(wdd, rec_new);
-                       kfree(rec_new);
-                       if (ret)
-                               return ret;
-
-                       /* Create a packet with the second block of data */
-                       rec_new = kzalloc(sizeof(struct ihex_binrec) +
-                                         be16_to_cpu(rec->len) -
-                                         (words_till_page_break << 1),
-                                         GFP_KERNEL);
-                       if (!rec_new)
-                               return -ENOMEM;
-
-                       /* Remaining bytes */
-                       rec_new->len = rec->len -
-                                      cpu_to_be16(words_till_page_break << 1);
-
-                       rec_new->addr = cpu_to_be32(be32_to_cpu(rec->addr) +
-                                       (words_till_page_break << 1));
-
-                       memcpy(rec_new->data,
-                              rec->data + (words_till_page_break << 1),
-                              be16_to_cpu(rec_new->len));
-
-                       ret = ziirave_firm_write_pkt(wdd, rec_new);
-                       kfree(rec_new);
-                       if (ret)
-                               return ret;
-               } else {
-                       ret = ziirave_firm_write_pkt(wdd, rec);
-                       if (ret)
-                               return ret;
-               }
+               ret = ziirave_firm_write_pkt(wdd, be32_to_cpu(rec->addr),
+                                            rec->data, be16_to_cpu(rec->len));
+               if (ret)
+                       return ret;
        }
 
-       /* For end of download, the length field will be set to 0 */
-       rec_new = kzalloc(sizeof(struct ihex_binrec) + 1, GFP_KERNEL);
-       if (!rec_new)
-               return -ENOMEM;
-
-       ret = ziirave_firm_write_pkt(wdd, rec_new);
-       kfree(rec_new);
+       /*
+        * Finish firmware download process by sending a zero length
+        * payload
+        */
+       ret = ziirave_firm_write_pkt(wdd, 0, NULL, 0);
        if (ret) {
                dev_err(&client->dev, "Failed to send EMPTY packet: %d\n", ret);
                return ret;
@@ -437,15 +398,22 @@ static int ziirave_firm_upload(struct watchdog_device *wdd,
        }
 
        /* End download operation */
-       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_END, 1, false);
-       if (ret)
+       ret = i2c_smbus_write_byte(client, ZIIRAVE_CMD_DOWNLOAD_END);
+       if (ret) {
+               dev_err(&client->dev,
+                       "Failed to end firmware download: %d\n", ret);
                return ret;
+       }
 
        /* Reset the processor */
-       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_RESET_PROCESSOR, 1,
-                                     false);
-       if (ret)
+       ret = i2c_smbus_write_byte_data(client,
+                                       ZIIRAVE_CMD_RESET_PROCESSOR,
+                                       ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC);
+       if (ret) {
+               dev_err(&client->dev,
+                       "Failed to reset the watchdog: %d\n", ret);
                return ret;
+       }
 
        msleep(500);
 
@@ -478,7 +446,7 @@ static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev,
        if (ret)
                return ret;
 
-       ret = sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major,
+       ret = sprintf(buf, ZIIRAVE_FW_VERSION_FMT, w_priv->firmware_rev.major,
                      w_priv->firmware_rev.minor);
 
        mutex_unlock(&w_priv->sysfs_mutex);
@@ -501,7 +469,7 @@ static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev,
        if (ret)
                return ret;
 
-       ret = sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major,
+       ret = sprintf(buf, ZIIRAVE_BL_VERSION_FMT, w_priv->bootloader_rev.major,
                      w_priv->bootloader_rev.minor);
 
        mutex_unlock(&w_priv->sysfs_mutex);
@@ -568,7 +536,8 @@ static ssize_t ziirave_wdt_sysfs_store_firm(struct device *dev,
                goto unlock_mutex;
        }
 
-       dev_info(&client->dev, "Firmware updated to version 02.%02u.%02u\n",
+       dev_info(&client->dev,
+                "Firmware updated to version " ZIIRAVE_FW_VERSION_FMT "\n",
                 w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
 
        /* Restore the watchdog timeout */
@@ -611,7 +580,7 @@ static int ziirave_wdt_init_duration(struct i2c_client *client)
                                                   &reset_duration);
                if (ret) {
                        dev_info(&client->dev,
-                                "Unable to set reset pulse duration, using default\n");
+                        "No reset pulse duration specified, using default\n");
                        return 0;
                }
        }
@@ -633,7 +602,10 @@ static int ziirave_wdt_probe(struct i2c_client *client,
        struct ziirave_wdt_data *w_priv;
        int val;
 
-       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_BYTE |
+                                    I2C_FUNC_SMBUS_BYTE_DATA |
+                                    I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
                return -ENODEV;
 
        w_priv = devm_kzalloc(&client->dev, sizeof(*w_priv), GFP_KERNEL);
@@ -658,57 +630,80 @@ static int ziirave_wdt_probe(struct i2c_client *client,
         */
        if (w_priv->wdd.timeout == 0) {
                val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIMEOUT);
-               if (val < 0)
+               if (val < 0) {
+                       dev_err(&client->dev, "Failed to read timeout\n");
                        return val;
+               }
 
-               if (val < ZIIRAVE_TIMEOUT_MIN)
-                       return -ENODEV;
+               if (val > ZIIRAVE_TIMEOUT_MAX ||
+                   val < ZIIRAVE_TIMEOUT_MIN)
+                       val = ZIIRAVE_TIMEOUT_DEFAULT;
 
                w_priv->wdd.timeout = val;
-       } else {
-               ret = ziirave_wdt_set_timeout(&w_priv->wdd,
-                                             w_priv->wdd.timeout);
-               if (ret)
-                       return ret;
+       }
 
-               dev_info(&client->dev, "Timeout set to %ds.",
-                        w_priv->wdd.timeout);
+       ret = ziirave_wdt_set_timeout(&w_priv->wdd, w_priv->wdd.timeout);
+       if (ret) {
+               dev_err(&client->dev, "Failed to set timeout\n");
+               return ret;
        }
 
+       dev_info(&client->dev, "Timeout set to %ds\n", w_priv->wdd.timeout);
+
        watchdog_set_nowayout(&w_priv->wdd, nowayout);
 
        i2c_set_clientdata(client, w_priv);
 
        /* If in unconfigured state, set to stopped */
        val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_STATE);
-       if (val < 0)
+       if (val < 0) {
+               dev_err(&client->dev, "Failed to read state\n");
                return val;
+       }
 
        if (val == ZIIRAVE_STATE_INITIAL)
                ziirave_wdt_stop(&w_priv->wdd);
 
        ret = ziirave_wdt_init_duration(client);
-       if (ret)
+       if (ret) {
+               dev_err(&client->dev, "Failed to init duration\n");
                return ret;
+       }
 
        ret = ziirave_wdt_revision(client, &w_priv->firmware_rev,
                                   ZIIRAVE_WDT_FIRM_VER_MAJOR);
-       if (ret)
+       if (ret) {
+               dev_err(&client->dev, "Failed to read firmware version\n");
                return ret;
+       }
+
+       dev_info(&client->dev,
+                "Firmware version: " ZIIRAVE_FW_VERSION_FMT "\n",
+                w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
 
        ret = ziirave_wdt_revision(client, &w_priv->bootloader_rev,
                                   ZIIRAVE_WDT_BOOT_VER_MAJOR);
-       if (ret)
+       if (ret) {
+               dev_err(&client->dev, "Failed to read bootloader version\n");
                return ret;
+       }
+
+       dev_info(&client->dev,
+                "Bootloader version: " ZIIRAVE_BL_VERSION_FMT "\n",
+                w_priv->bootloader_rev.major, w_priv->bootloader_rev.minor);
 
        w_priv->reset_reason = i2c_smbus_read_byte_data(client,
                                                ZIIRAVE_WDT_RESET_REASON);
-       if (w_priv->reset_reason < 0)
+       if (w_priv->reset_reason < 0) {
+               dev_err(&client->dev, "Failed to read reset reason\n");
                return w_priv->reset_reason;
+       }
 
        if (w_priv->reset_reason >= ARRAY_SIZE(ziirave_reasons) ||
-           !ziirave_reasons[w_priv->reset_reason])
+           !ziirave_reasons[w_priv->reset_reason]) {
+               dev_err(&client->dev, "Invalid reset reason\n");
                return -ENODEV;
+       }
 
        ret = watchdog_register_device(&w_priv->wdd);