Use the bark interrupt as the pretimeout notifier if available.

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

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

diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c
index 97ca993..8b919cc
--- 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,35 @@ 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);
+       if (wdt_dev->pretimeout)
+               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;
+       watchdog_notify_pretimeout(wdd);
+
+       return IRQ_HANDLED;
+}
+
 static const struct watchdog_info mtk_wdt_info = {
        .identity       = DRV_NAME,
        .options        = WDIOF_SETTIMEOUT |
@@ -253,12 +271,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 +294,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 +306,21 @@ 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;
-- 
2.7.4

Reply via email to