Merge tag 'linux-watchdog-5.15-rc1' of git://www.linux-watchdog.org/linux-watchdog
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 Sep 2021 20:52:46 +0000 (13:52 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 Sep 2021 20:52:46 +0000 (13:52 -0700)
Pull watchdog updates from Wim Van Sebroeck:

 - add Mediatek MT7986 & MT8195 wdt support

 - add Maxim MAX63xx

 - drop bd70528 support

 - rewrite ixp4xx to watchdog framework

 - constify static struct watchdog_ops for sl28cpld_wdt, mpc8xxx_wdt and
   tqmx86

 - introduce watchdog_dev_suspend/resume

 - several fixes and improvements

* tag 'linux-watchdog-5.15-rc1' of git://www.linux-watchdog.org/linux-watchdog:
  dt-bindings: watchdog: Add compatible for Mediatek MT7986
  watchdog: ixp4xx: Rewrite driver to use core
  watchdog: Start watchdog in watchdog_set_last_hw_keepalive only if appropriate
  watchdog: max63xx_wdt: Add device tree probing
  dt-bindings: watchdog: Add Maxim MAX63xx bindings
  watchdog: mediatek: mt8195: add wdt support
  dt-bindings: reset: mt8195: add toprgu reset-controller header file
  watchdog: tqmx86: Constify static struct watchdog_ops
  watchdog: mpc8xxx_wdt: Constify static struct watchdog_ops
  watchdog: sl28cpld_wdt: Constify static struct watchdog_ops
  watchdog: iTCO_wdt: Fix detection of SMI-off case
  watchdog: bcm2835_wdt: consider system-power-controller property
  watchdog: imx2_wdg: notify wdog core to stop ping worker on suspend
  watchdog: introduce watchdog_dev_suspend/resume
  watchdog: Fix NULL pointer dereference when releasing cdev
  watchdog: only run driver set_pretimeout op if device supports it
  watchdog: bd70528 drop bd70528 support

17 files changed:
Documentation/devicetree/bindings/watchdog/maxim,max63xx.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/mtk-wdt.txt
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/bcm2835_wdt.c
drivers/watchdog/bd70528_wdt.c [deleted file]
drivers/watchdog/iTCO_wdt.c
drivers/watchdog/imx2_wdt.c
drivers/watchdog/max63xx_wdt.c
drivers/watchdog/mpc8xxx_wdt.c
drivers/watchdog/mtk_wdt.c
drivers/watchdog/sl28cpld_wdt.c
drivers/watchdog/tqmx86_wdt.c
drivers/watchdog/watchdog_core.c
drivers/watchdog/watchdog_dev.c
include/dt-bindings/reset/mt8195-resets.h [new file with mode: 0644]
include/linux/watchdog.h

diff --git a/Documentation/devicetree/bindings/watchdog/maxim,max63xx.yaml b/Documentation/devicetree/bindings/watchdog/maxim,max63xx.yaml
new file mode 100644 (file)
index 0000000..f2105ee
--- /dev/null
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/watchdog/maxim,max63xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim 63xx Watchdog Timers
+
+allOf:
+  - $ref: "watchdog.yaml#"
+
+maintainers:
+  - Marc Zyngier <maz@kernel.org>
+  - Linus Walleij <linus.walleij@linaro.org>
+
+properties:
+  compatible:
+    oneOf:
+      - const: maxim,max6369
+      - const: maxim,max6370
+      - const: maxim,max6371
+      - const: maxim,max6372
+      - const: maxim,max6373
+      - const: maxim,max6374
+
+  reg:
+    description: This is a 1-byte memory-mapped address
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    wdt: watchdog@50000000 {
+        compatible = "maxim,max6369";
+        reg = <0x50000000 0x1>;
+        timeout-sec = <10>;
+    };
+
+...
index 416d716..a4e31ce 100644 (file)
@@ -13,6 +13,7 @@ Required properties:
        "mediatek,mt7622-wdt", "mediatek,mt6589-wdt": for MT7622
        "mediatek,mt7623-wdt", "mediatek,mt6589-wdt": for MT7623
        "mediatek,mt7629-wdt", "mediatek,mt6589-wdt": for MT7629
+       "mediatek,mt7986-wdt", "mediatek,mt6589-wdt": for MT7986
        "mediatek,mt8183-wdt": for MT8183
        "mediatek,mt8516-wdt", "mediatek,mt6589-wdt": for MT8516
        "mediatek,mt8192-wdt": for MT8192
index 0bc7046..b81fe4f 100644 (file)
@@ -168,18 +168,6 @@ config SOFT_WATCHDOG_PRETIMEOUT
          watchdog. Be aware that governors might affect the watchdog because it
          is purely software, e.g. the panic governor will stall it!
 
-config BD70528_WATCHDOG
-       tristate "ROHM BD70528 PMIC Watchdog"
-       depends on MFD_ROHM_BD70528
-       select WATCHDOG_CORE
-       help
-         Support for the watchdog in the ROHM BD70528 PMIC. Watchdog trigger
-         cause system reset.
-
-         Say Y here to include support for the ROHM BD70528 watchdog.
-         Alternatively say M to compile the driver as a module,
-         which will be called bd70528_wdt.
-
 config BD957XMUF_WATCHDOG
        tristate "ROHM BD9576MUF and BD9573MUF PMIC Watchdog"
        depends on MFD_ROHM_BD957XMUF
index abaf2eb..1bd2d6f 100644 (file)
@@ -204,7 +204,6 @@ obj-$(CONFIG_WATCHDOG_SUN4V)                += sun4v_wdt.o
 obj-$(CONFIG_XEN_WDT) += xen_wdt.o
 
 # Architecture Independent
-obj-$(CONFIG_BD70528_WATCHDOG) += bd70528_wdt.o
 obj-$(CONFIG_BD957XMUF_WATCHDOG) += bd9576_wdt.o
 obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
 obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
index dec6ca0..9490717 100644 (file)
@@ -205,9 +205,13 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
        if (err)
                return err;
 
-       if (pm_power_off == NULL) {
-               pm_power_off = bcm2835_power_off;
-               bcm2835_power_off_wdt = wdt;
+       if (of_device_is_system_power_controller(pdev->dev.parent->of_node)) {
+               if (!pm_power_off) {
+                       pm_power_off = bcm2835_power_off;
+                       bcm2835_power_off_wdt = wdt;
+               } else {
+                       dev_info(dev, "Poweroff handler already present!\n");
+               }
        }
 
        dev_info(dev, "Broadcom BCM2835 watchdog timer");
diff --git a/drivers/watchdog/bd70528_wdt.c b/drivers/watchdog/bd70528_wdt.c
deleted file mode 100644 (file)
index 0170b37..0000000
+++ /dev/null
@@ -1,291 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// Copyright (C) 2018 ROHM Semiconductors
-// ROHM BD70528MWV watchdog driver
-
-#include <linux/bcd.h>
-#include <linux/kernel.h>
-#include <linux/mfd/rohm-bd70528.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-#include <linux/watchdog.h>
-
-/*
- * Max time we can set is 1 hour, 59 minutes and 59 seconds
- * and Minimum time is 1 second
- */
-#define WDT_MAX_MS     ((2 * 60 * 60 - 1) * 1000)
-#define WDT_MIN_MS     1000
-#define DEFAULT_TIMEOUT        60
-
-#define WD_CTRL_MAGIC1 0x55
-#define WD_CTRL_MAGIC2 0xAA
-
-struct wdtbd70528 {
-       struct device *dev;
-       struct regmap *regmap;
-       struct rohm_regmap_dev *mfd;
-       struct watchdog_device wdt;
-};
-
-/**
- * bd70528_wdt_set - arm or disarm watchdog timer
- *
- * @data:      device data for the PMIC instance we want to operate on
- * @enable:    new state of WDT. zero to disable, non zero to enable
- * @old_state: previous state of WDT will be filled here
- *
- * Arm or disarm WDT on BD70528 PMIC. Expected to be called only by
- * BD70528 RTC and BD70528 WDT drivers. The rtc_timer_lock must be taken
- * by calling bd70528_wdt_lock before calling bd70528_wdt_set.
- */
-int bd70528_wdt_set(struct rohm_regmap_dev *data, int enable, int *old_state)
-{
-       int ret, i;
-       unsigned int tmp;
-       struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
-                                                chip);
-       u8 wd_ctrl_arr[3] = { WD_CTRL_MAGIC1, WD_CTRL_MAGIC2, 0 };
-       u8 *wd_ctrl = &wd_ctrl_arr[2];
-
-       ret = regmap_read(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, &tmp);
-       if (ret)
-               return ret;
-
-       *wd_ctrl = (u8)tmp;
-
-       if (old_state) {
-               if (*wd_ctrl & BD70528_MASK_WDT_EN)
-                       *old_state |= BD70528_WDT_STATE_BIT;
-               else
-                       *old_state &= ~BD70528_WDT_STATE_BIT;
-               if ((!enable) == (!(*old_state & BD70528_WDT_STATE_BIT)))
-                       return 0;
-       }
-
-       if (enable) {
-               if (*wd_ctrl & BD70528_MASK_WDT_EN)
-                       return 0;
-               *wd_ctrl |= BD70528_MASK_WDT_EN;
-       } else {
-               if (*wd_ctrl & BD70528_MASK_WDT_EN)
-                       *wd_ctrl &= ~BD70528_MASK_WDT_EN;
-               else
-                       return 0;
-       }
-
-       for (i = 0; i < 3; i++) {
-               ret = regmap_write(bd70528->chip.regmap, BD70528_REG_WDT_CTRL,
-                                  wd_ctrl_arr[i]);
-               if (ret)
-                       return ret;
-       }
-
-       ret = regmap_read(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, &tmp);
-       if ((tmp & BD70528_MASK_WDT_EN) != (*wd_ctrl & BD70528_MASK_WDT_EN)) {
-               dev_err(bd70528->chip.dev,
-                       "Watchdog ctrl mismatch (hw) 0x%x (set) 0x%x\n",
-                       tmp, *wd_ctrl);
-               ret = -EIO;
-       }
-
-       return ret;
-}
-EXPORT_SYMBOL(bd70528_wdt_set);
-
-/**
- * bd70528_wdt_lock - take WDT lock
- *
- * @data:      device data for the PMIC instance we want to operate on
- *
- * Lock WDT for arming/disarming in order to avoid race condition caused
- * by WDT state changes initiated by WDT and RTC drivers.
- */
-void bd70528_wdt_lock(struct rohm_regmap_dev *data)
-{
-       struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
-                                                chip);
-
-       mutex_lock(&bd70528->rtc_timer_lock);
-}
-EXPORT_SYMBOL(bd70528_wdt_lock);
-
-/**
- * bd70528_wdt_unlock - unlock WDT lock
- *
- * @data:      device data for the PMIC instance we want to operate on
- *
- * Unlock WDT lock which has previously been taken by call to
- * bd70528_wdt_lock.
- */
-void bd70528_wdt_unlock(struct rohm_regmap_dev *data)
-{
-       struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
-                                                chip);
-
-       mutex_unlock(&bd70528->rtc_timer_lock);
-}
-EXPORT_SYMBOL(bd70528_wdt_unlock);
-
-static int bd70528_wdt_set_locked(struct wdtbd70528 *w, int enable)
-{
-       return bd70528_wdt_set(w->mfd, enable, NULL);
-}
-
-static int bd70528_wdt_change(struct wdtbd70528 *w, int enable)
-{
-       int ret;
-
-       bd70528_wdt_lock(w->mfd);
-       ret = bd70528_wdt_set_locked(w, enable);
-       bd70528_wdt_unlock(w->mfd);
-
-       return ret;
-}
-
-static int bd70528_wdt_start(struct watchdog_device *wdt)
-{
-       struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
-
-       dev_dbg(w->dev, "WDT ping...\n");
-       return bd70528_wdt_change(w, 1);
-}
-
-static int bd70528_wdt_stop(struct watchdog_device *wdt)
-{
-       struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
-
-       dev_dbg(w->dev, "WDT stopping...\n");
-       return bd70528_wdt_change(w, 0);
-}
-
-static int bd70528_wdt_set_timeout(struct watchdog_device *wdt,
-                                  unsigned int timeout)
-{
-       unsigned int hours;
-       unsigned int minutes;
-       unsigned int seconds;
-       int ret;
-       struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
-
-       seconds = timeout;
-       hours = timeout / (60 * 60);
-       /* Maximum timeout is 1h 59m 59s => hours is 1 or 0 */
-       if (hours)
-               seconds -= (60 * 60);
-       minutes = seconds / 60;
-       seconds = seconds % 60;
-
-       bd70528_wdt_lock(w->mfd);
-
-       ret = bd70528_wdt_set_locked(w, 0);
-       if (ret)
-               goto out_unlock;
-
-       ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_HOUR,
-                                BD70528_MASK_WDT_HOUR, hours);
-       if (ret) {
-               dev_err(w->dev, "Failed to set WDT hours\n");
-               goto out_en_unlock;
-       }
-       ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_MINUTE,
-                                BD70528_MASK_WDT_MINUTE, bin2bcd(minutes));
-       if (ret) {
-               dev_err(w->dev, "Failed to set WDT minutes\n");
-               goto out_en_unlock;
-       }
-       ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_SEC,
-                                BD70528_MASK_WDT_SEC, bin2bcd(seconds));
-       if (ret)
-               dev_err(w->dev, "Failed to set WDT seconds\n");
-       else
-               dev_dbg(w->dev, "WDT tmo set to %u\n", timeout);
-
-out_en_unlock:
-       ret = bd70528_wdt_set_locked(w, 1);
-out_unlock:
-       bd70528_wdt_unlock(w->mfd);
-
-       return ret;
-}
-
-static const struct watchdog_info bd70528_wdt_info = {
-       .identity = "bd70528-wdt",
-       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
-};
-
-static const struct watchdog_ops bd70528_wdt_ops = {
-       .start          = bd70528_wdt_start,
-       .stop           = bd70528_wdt_stop,
-       .set_timeout    = bd70528_wdt_set_timeout,
-};
-
-static int bd70528_wdt_probe(struct platform_device *pdev)
-{
-       struct rohm_regmap_dev *bd70528;
-       struct wdtbd70528 *w;
-       int ret;
-       unsigned int reg;
-
-       bd70528 = dev_get_drvdata(pdev->dev.parent);
-       if (!bd70528) {
-               dev_err(&pdev->dev, "No MFD driver data\n");
-               return -EINVAL;
-       }
-       w = devm_kzalloc(&pdev->dev, sizeof(*w), GFP_KERNEL);
-       if (!w)
-               return -ENOMEM;
-
-       w->regmap = bd70528->regmap;
-       w->mfd = bd70528;
-       w->dev = &pdev->dev;
-
-       w->wdt.info = &bd70528_wdt_info;
-       w->wdt.ops =  &bd70528_wdt_ops;
-       w->wdt.min_hw_heartbeat_ms = WDT_MIN_MS;
-       w->wdt.max_hw_heartbeat_ms = WDT_MAX_MS;
-       w->wdt.parent = pdev->dev.parent;
-       w->wdt.timeout = DEFAULT_TIMEOUT;
-       watchdog_set_drvdata(&w->wdt, w);
-       watchdog_init_timeout(&w->wdt, 0, pdev->dev.parent);
-
-       ret = bd70528_wdt_set_timeout(&w->wdt, w->wdt.timeout);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to set the watchdog timeout\n");
-               return ret;
-       }
-
-       bd70528_wdt_lock(w->mfd);
-       ret = regmap_read(w->regmap, BD70528_REG_WDT_CTRL, &reg);
-       bd70528_wdt_unlock(w->mfd);
-
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to get the watchdog state\n");
-               return ret;
-       }
-       if (reg & BD70528_MASK_WDT_EN) {
-               dev_dbg(&pdev->dev, "watchdog was running during probe\n");
-               set_bit(WDOG_HW_RUNNING, &w->wdt.status);
-       }
-
-       ret = devm_watchdog_register_device(&pdev->dev, &w->wdt);
-       if (ret < 0)
-               dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret);
-
-       return ret;
-}
-
-static struct platform_driver bd70528_wdt = {
-       .driver = {
-               .name = "bd70528-wdt"
-       },
-       .probe = bd70528_wdt_probe,
-};
-
-module_platform_driver(bd70528_wdt);
-
-MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
-MODULE_DESCRIPTION("BD70528 watchdog driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:bd70528-wdt");
index b3f6046..643c6c2 100644 (file)
@@ -362,7 +362,7 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
         * Otherwise, the BIOS generally reboots when the SMI triggers.
         */
        if (p->smi_res &&
-           (SMI_EN(p) & (TCO_EN | GBL_SMI_EN)) != (TCO_EN | GBL_SMI_EN))
+           (inl(SMI_EN(p)) & (TCO_EN | GBL_SMI_EN)) != (TCO_EN | GBL_SMI_EN))
                tmrval /= 2;
 
        /* from the specs: */
index cc86018..51bfb79 100644 (file)
@@ -317,6 +317,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
        watchdog_set_nowayout(wdog, nowayout);
        watchdog_set_restart_priority(wdog, 128);
        watchdog_init_timeout(wdog, timeout, dev);
+       watchdog_stop_ping_on_suspend(wdog);
 
        if (imx2_wdt_is_running(wdev)) {
                imx2_wdt_set_timeout(wdog, wdog->timeout);
index 3a89962..9e1541c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/spinlock.h>
 #include <linux/io.h>
 #include <linux/slab.h>
+#include <linux/property.h>
 
 #define DEFAULT_HEARTBEAT 60
 #define MAX_HEARTBEAT     60
@@ -99,8 +100,8 @@ static const struct max63xx_timeout max6373_table[] = {
        { },
 };
 
-static struct max63xx_timeout *
-max63xx_select_timeout(struct max63xx_timeout *table, int value)
+static const struct max63xx_timeout *
+max63xx_select_timeout(const struct max63xx_timeout *table, int value)
 {
        while (table->twd) {
                if (value <= table->twd) {
@@ -202,14 +203,17 @@ static int max63xx_wdt_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct max63xx_wdt *wdt;
-       struct max63xx_timeout *table;
+       const struct max63xx_timeout *table;
        int err;
 
        wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
        if (!wdt)
                return -ENOMEM;
 
-       table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
+       /* Attempt to use fwnode first */
+       table = device_get_match_data(dev);
+       if (!table)
+               table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
 
        if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
                heartbeat = DEFAULT_HEARTBEAT;
@@ -255,11 +259,23 @@ static const struct platform_device_id max63xx_id_table[] = {
 };
 MODULE_DEVICE_TABLE(platform, max63xx_id_table);
 
+static const struct of_device_id max63xx_dt_id_table[] = {
+       { .compatible = "maxim,max6369", .data = max6369_table, },
+       { .compatible = "maxim,max6370", .data = max6369_table, },
+       { .compatible = "maxim,max6371", .data = max6371_table, },
+       { .compatible = "maxim,max6372", .data = max6371_table, },
+       { .compatible = "maxim,max6373", .data = max6373_table, },
+       { .compatible = "maxim,max6374", .data = max6373_table, },
+       { }
+};
+MODULE_DEVICE_TABLE(of, max63xx_dt_id_table);
+
 static struct platform_driver max63xx_wdt_driver = {
        .probe          = max63xx_wdt_probe,
        .id_table       = max63xx_id_table,
        .driver         = {
                .name   = "max63xx_wdt",
+               .of_match_table = max63xx_dt_id_table,
        },
 };
 
index 2f7ded3..1c569be 100644 (file)
@@ -118,7 +118,7 @@ static struct watchdog_info mpc8xxx_wdt_info = {
        .identity = "MPC8xxx",
 };
 
-static struct watchdog_ops mpc8xxx_wdt_ops = {
+static const struct watchdog_ops mpc8xxx_wdt_ops = {
        .owner = THIS_MODULE,
        .start = mpc8xxx_wdt_start,
        .ping = mpc8xxx_wdt_ping,
index 16b6aff..796fbb0 100644 (file)
@@ -12,6 +12,7 @@
 #include <dt-bindings/reset-controller/mt2712-resets.h>
 #include <dt-bindings/reset-controller/mt8183-resets.h>
 #include <dt-bindings/reset-controller/mt8192-resets.h>
+#include <dt-bindings/reset/mt8195-resets.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/init.h>
@@ -82,6 +83,10 @@ static const struct mtk_wdt_data mt8192_data = {
        .toprgu_sw_rst_num = MT8192_TOPRGU_SW_RST_NUM,
 };
 
+static const struct mtk_wdt_data mt8195_data = {
+       .toprgu_sw_rst_num = MT8195_TOPRGU_SW_RST_NUM,
+};
+
 static int toprgu_reset_update(struct reset_controller_dev *rcdev,
                               unsigned long id, bool assert)
 {
@@ -408,6 +413,7 @@ static const struct of_device_id mtk_wdt_dt_ids[] = {
        { .compatible = "mediatek,mt6589-wdt" },
        { .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data },
        { .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data },
+       { .compatible = "mediatek,mt8195-wdt", .data = &mt8195_data },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids);
index 2de9329..9ce456f 100644 (file)
@@ -108,7 +108,7 @@ static const struct watchdog_info sl28cpld_wdt_info = {
        .identity = "sl28cpld watchdog",
 };
 
-static struct watchdog_ops sl28cpld_wdt_ops = {
+static const struct watchdog_ops sl28cpld_wdt_ops = {
        .owner = THIS_MODULE,
        .start = sl28cpld_wdt_start,
        .stop = sl28cpld_wdt_stop,
index 72d0b0a..83860e9 100644 (file)
@@ -62,7 +62,7 @@ static const struct watchdog_info tqmx86_wdt_info = {
        .identity       = "TQMx86 Watchdog",
 };
 
-static struct watchdog_ops tqmx86_wdt_ops = {
+static const struct watchdog_ops tqmx86_wdt_ops = {
        .owner          = THIS_MODULE,
        .start          = tqmx86_wdt_start,
        .set_timeout    = tqmx86_wdt_set_timeout,
index 5df0a22..3fe8a7e 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/idr.h>         /* For ida_* macros */
 #include <linux/err.h>         /* For IS_ERR macros */
 #include <linux/of.h>          /* For of_get_timeout_sec */
+#include <linux/suspend.h>
 
 #include "watchdog_core.h"     /* For watchdog_dev_register/... */
 
@@ -185,6 +186,33 @@ static int watchdog_restart_notifier(struct notifier_block *nb,
        return NOTIFY_DONE;
 }
 
+static int watchdog_pm_notifier(struct notifier_block *nb, unsigned long mode,
+                               void *data)
+{
+       struct watchdog_device *wdd;
+       int ret = 0;
+
+       wdd = container_of(nb, struct watchdog_device, pm_nb);
+
+       switch (mode) {
+       case PM_HIBERNATION_PREPARE:
+       case PM_RESTORE_PREPARE:
+       case PM_SUSPEND_PREPARE:
+               ret = watchdog_dev_suspend(wdd);
+               break;
+       case PM_POST_HIBERNATION:
+       case PM_POST_RESTORE:
+       case PM_POST_SUSPEND:
+               ret = watchdog_dev_resume(wdd);
+               break;
+       }
+
+       if (ret)
+               return NOTIFY_BAD;
+
+       return NOTIFY_DONE;
+}
+
 /**
  * watchdog_set_restart_priority - Change priority of restart handler
  * @wdd: watchdog device
@@ -292,6 +320,15 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
                                wdd->id, ret);
        }
 
+       if (test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status)) {
+               wdd->pm_nb.notifier_call = watchdog_pm_notifier;
+
+               ret = register_pm_notifier(&wdd->pm_nb);
+               if (ret)
+                       pr_warn("watchdog%d: Cannot register pm handler (%d)\n",
+                               wdd->id, ret);
+       }
+
        return 0;
 }
 
index 3bab324..3a3d8b5 100644 (file)
@@ -401,7 +401,7 @@ static int watchdog_set_pretimeout(struct watchdog_device *wdd,
        if (watchdog_pretimeout_invalid(wdd, timeout))
                return -EINVAL;
 
-       if (wdd->ops->set_pretimeout)
+       if (wdd->ops->set_pretimeout && (wdd->info->options & WDIOF_PRETIMEOUT))
                err = wdd->ops->set_pretimeout(wdd, timeout);
        else
                wdd->pretimeout = timeout;
@@ -1096,6 +1096,8 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
                watchdog_stop(wdd);
        }
 
+       watchdog_hrtimer_pretimeout_stop(wdd);
+
        mutex_lock(&wd_data->lock);
        wd_data->wdd = NULL;
        wdd->wd_data = NULL;
@@ -1103,7 +1105,6 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd)
 
        hrtimer_cancel(&wd_data->timer);
        kthread_cancel_work_sync(&wd_data->work);
-       watchdog_hrtimer_pretimeout_stop(wdd);
 
        put_device(&wd_data->dev);
 }
@@ -1172,7 +1173,10 @@ int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd,
 
        wd_data->last_hw_keepalive = ktime_sub(now, ms_to_ktime(last_ping_ms));
 
-       return __watchdog_ping(wdd);
+       if (watchdog_hw_running(wdd) && handle_boot_enabled)
+               return __watchdog_ping(wdd);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(watchdog_set_last_hw_keepalive);
 
@@ -1227,6 +1231,53 @@ void __exit watchdog_dev_exit(void)
        kthread_destroy_worker(watchdog_kworker);
 }
 
+int watchdog_dev_suspend(struct watchdog_device *wdd)
+{
+       struct watchdog_core_data *wd_data = wdd->wd_data;
+       int ret = 0;
+
+       if (!wdd->wd_data)
+               return -ENODEV;
+
+       /* ping for the last time before suspend */
+       mutex_lock(&wd_data->lock);
+       if (watchdog_worker_should_ping(wd_data))
+               ret = __watchdog_ping(wd_data->wdd);
+       mutex_unlock(&wd_data->lock);
+
+       if (ret)
+               return ret;
+
+       /*
+        * make sure that watchdog worker will not kick in when the wdog is
+        * suspended
+        */
+       hrtimer_cancel(&wd_data->timer);
+       kthread_cancel_work_sync(&wd_data->work);
+
+       return 0;
+}
+
+int watchdog_dev_resume(struct watchdog_device *wdd)
+{
+       struct watchdog_core_data *wd_data = wdd->wd_data;
+       int ret = 0;
+
+       if (!wdd->wd_data)
+               return -ENODEV;
+
+       /*
+        * __watchdog_ping will also retrigger hrtimer and therefore restore the
+        * ping worker if needed.
+        */
+       mutex_lock(&wd_data->lock);
+       if (watchdog_worker_should_ping(wd_data))
+               ret = __watchdog_ping(wd_data->wdd);
+       mutex_unlock(&wd_data->lock);
+
+       return ret;
+}
+
 module_param(handle_boot_enabled, bool, 0444);
 MODULE_PARM_DESC(handle_boot_enabled,
        "Watchdog core auto-updates boot enabled watchdogs before userspace takes over (default="
diff --git a/include/dt-bindings/reset/mt8195-resets.h b/include/dt-bindings/reset/mt8195-resets.h
new file mode 100644 (file)
index 0000000..a26bccc
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)*/
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Christine Zhu <christine.zhu@mediatek.com>
+ */
+
+#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8195
+#define _DT_BINDINGS_RESET_CONTROLLER_MT8195
+
+#define MT8195_TOPRGU_CONN_MCU_SW_RST          0
+#define MT8195_TOPRGU_INFRA_GRST_SW_RST        1
+#define MT8195_TOPRGU_APU_SW_RST               2
+#define MT8195_TOPRGU_INFRA_AO_GRST_SW_RST     6
+#define MT8195_TOPRGU_MMSYS_SW_RST             7
+#define MT8195_TOPRGU_MFG_SW_RST               8
+#define MT8195_TOPRGU_VENC_SW_RST              9
+#define MT8195_TOPRGU_VDEC_SW_RST              10
+#define MT8195_TOPRGU_IMG_SW_RST               11
+#define MT8195_TOPRGU_APMIXEDSYS_SW_RST        13
+#define MT8195_TOPRGU_AUDIO_SW_RST             14
+#define MT8195_TOPRGU_CAMSYS_SW_RST            15
+#define MT8195_TOPRGU_EDPTX_SW_RST             16
+#define MT8195_TOPRGU_ADSPSYS_SW_RST           21
+#define MT8195_TOPRGU_DPTX_SW_RST              22
+#define MT8195_TOPRGU_SPMI_MST_SW_RST          23
+
+#define MT8195_TOPRGU_SW_RST_NUM               16
+
+#endif  /* _DT_BINDINGS_RESET_CONTROLLER_MT8195 */
index 9b19e6b..9966019 100644 (file)
@@ -107,6 +107,7 @@ struct watchdog_device {
        unsigned int max_hw_heartbeat_ms;
        struct notifier_block reboot_nb;
        struct notifier_block restart_nb;
+       struct notifier_block pm_nb;
        void *driver_data;
        struct watchdog_core_data *wd_data;
        unsigned long status;
@@ -116,6 +117,7 @@ struct watchdog_device {
 #define WDOG_STOP_ON_REBOOT    2       /* Should be stopped on reboot */
 #define WDOG_HW_RUNNING                3       /* True if HW watchdog running */
 #define WDOG_STOP_ON_UNREGISTER        4       /* Should be stopped on unregister */
+#define WDOG_NO_PING_ON_SUSPEND        5       /* Ping worker should be stopped on suspend */
        struct list_head deferred;
 };
 
@@ -156,6 +158,12 @@ static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd)
        set_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status);
 }
 
+/* Use the following function to stop the wdog ping worker when suspending */
+static inline void watchdog_stop_ping_on_suspend(struct watchdog_device *wdd)
+{
+       set_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status);
+}
+
 /* Use the following function to check if a timeout value is invalid */
 static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t)
 {
@@ -209,6 +217,8 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd,
                                  unsigned int timeout_parm, struct device *dev);
 extern int watchdog_register_device(struct watchdog_device *);
 extern void watchdog_unregister_device(struct watchdog_device *);
+int watchdog_dev_suspend(struct watchdog_device *wdd);
+int watchdog_dev_resume(struct watchdog_device *wdd);
 
 int watchdog_set_last_hw_keepalive(struct watchdog_device *, unsigned int);