Use the bark interrupt as the pretimeout notifier if available.

By default, the pretimeout notification shall occur one second earlier
than the timeout.

V2:
- panic() by default if WATCHDOG_PRETIMEOUT_GOV is not enabled.

V3:
- Modify the pretimeout behavior, manually reset after the pretimeout
- is processed and wait until timeout.

Signed-off-by: Wang Qing <wangq...@vivo.com>
---
 drivers/watchdog/mtk_wdt.c | 62 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 57 insertions(+), 5 deletions(-)

diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c
index 97ca993..7bef1e3
--- a/drivers/watchdog/mtk_wdt.c
+++ b/drivers/watchdog/mtk_wdt.c
@@ -25,6 +25,7 @@
 #include <linux/reset-controller.h>
 #include <linux/types.h>
 #include <linux/watchdog.h>
+#include <linux/interrupt.h>
 
 #define WDT_MAX_TIMEOUT                31
 #define WDT_MIN_TIMEOUT                1
@@ -234,18 +235,46 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev)
        void __iomem *wdt_base = mtk_wdt->wdt_base;
        int ret;
 
-       ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
+       ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout - 
wdt_dev->pretimeout);
        if (ret < 0)
                return ret;
 
        reg = ioread32(wdt_base + WDT_MODE);
-       reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
+       reg &= ~WDT_MODE_IRQ_EN;
+       if (wdt_dev->pretimeout)
+               reg |= WDT_MODE_IRQ_EN;
+       else
+               reg &= ~WDT_MODE_IRQ_EN;
        reg |= (WDT_MODE_EN | WDT_MODE_KEY);
        iowrite32(reg, wdt_base + WDT_MODE);
 
        return 0;
 }
 
+static int mtk_wdt_set_pretimeout(struct watchdog_device *wdd,
+                                  unsigned int timeout)
+{
+       wdd->pretimeout = timeout;
+       return mtk_wdt_start(wdd);
+}
+
+static irqreturn_t mtk_wdt_isr(int irq, void *arg)
+{
+       struct watchdog_device *wdd = arg;
+       struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdd);
+       void __iomem *wdt_base = mtk_wdt->wdt_base;
+
+       watchdog_notify_pretimeout(wdd);
+       /*
+        * Guaranteed to be reset when the timeout
+        * expires under any situations
+        */
+       mdelay(1000*wdd->pretimeout);
+       writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST);
+
+       return IRQ_HANDLED;
+}
+
 static const struct watchdog_info mtk_wdt_info = {
        .identity       = DRV_NAME,
        .options        = WDIOF_SETTIMEOUT |
@@ -253,12 +282,21 @@ static const struct watchdog_info mtk_wdt_info = {
                          WDIOF_MAGICCLOSE,
 };
 
+static const struct watchdog_info mtk_wdt_pt_info = {
+       .identity       = DRV_NAME,
+       .options        = WDIOF_SETTIMEOUT |
+                         WDIOF_PRETIMEOUT |
+                         WDIOF_KEEPALIVEPING |
+                         WDIOF_MAGICCLOSE,
+};
+
 static const struct watchdog_ops mtk_wdt_ops = {
        .owner          = THIS_MODULE,
        .start          = mtk_wdt_start,
        .stop           = mtk_wdt_stop,
        .ping           = mtk_wdt_ping,
        .set_timeout    = mtk_wdt_set_timeout,
+       .set_pretimeout = mtk_wdt_set_pretimeout,
        .restart        = mtk_wdt_restart,
 };
 
@@ -267,7 +305,7 @@ static int mtk_wdt_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct mtk_wdt_dev *mtk_wdt;
        const struct mtk_wdt_data *wdt_data;
-       int err;
+       int err, irq;
 
        mtk_wdt = devm_kzalloc(dev, sizeof(*mtk_wdt), GFP_KERNEL);
        if (!mtk_wdt)
@@ -279,7 +317,22 @@ static int mtk_wdt_probe(struct platform_device *pdev)
        if (IS_ERR(mtk_wdt->wdt_base))
                return PTR_ERR(mtk_wdt->wdt_base);
 
-       mtk_wdt->wdt_dev.info = &mtk_wdt_info;
+       irq = platform_get_irq(pdev, 0);
+       if (irq > 0) {
+               err = devm_request_irq(&pdev->dev, irq, mtk_wdt_isr, 0, 
"wdt_bark",
+                                                       &mtk_wdt->wdt_dev);
+               if (err)
+                       return err;
+
+               mtk_wdt->wdt_dev.info = &mtk_wdt_pt_info;
+               mtk_wdt->wdt_dev.pretimeout = 1;
+       } else {
+               if (irq == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+
+               mtk_wdt->wdt_dev.info = &mtk_wdt_info;
+       }
+
        mtk_wdt->wdt_dev.ops = &mtk_wdt_ops;
        mtk_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT;
        mtk_wdt->wdt_dev.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT * 1000;
@@ -360,7 +413,6 @@ static struct platform_driver mtk_wdt_driver = {
 };
 
 module_platform_driver(mtk_wdt_driver);
-
 module_param(timeout, uint, 0);
 MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
 
-- 
2.7.4

Reply via email to