2021-04-14 20:23:22

by 王擎

[permalink] [raw]
Subject: [PATCH V2] watchdog: mtk: support pre-timeout when the bark irq is available

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

Signed-off-by: Wang Qing <[email protected]>
---
drivers/watchdog/mtk_wdt.c | 57 ++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 52 insertions(+), 5 deletions(-)

diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c
index 97ca993..b0ec933
--- 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,41 @@ 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;
+if (IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV))
+ watchdog_notify_pretimeout(wdd);
+else
+ //panic by decault instead of WDT_SWRST;
+ panic("MTk Watchdog bark!\n");
+
+ return IRQ_HANDLED;
+}
+
static const struct watchdog_info mtk_wdt_info = {
.identity = DRV_NAME,
.options = WDIOF_SETTIMEOUT |
@@ -253,12 +277,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 +300,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 +312,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 +408,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


2021-04-15 00:00:10

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH V2] watchdog: mtk: support pre-timeout when the bark irq is available

On 4/14/21 12:28 AM, Wang Qing wrote:
> 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
>
> Signed-off-by: Wang Qing <[email protected]>
> ---
> drivers/watchdog/mtk_wdt.c | 57 ++++++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 52 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c
> index 97ca993..b0ec933
> --- 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,41 @@ 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);
>


You said previously that the pretimeout would prevent the real timeout / reset
from happening, and the driver has no provision to set the real timeout.
That makes this a no-go.

> 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;
> +if (IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV))
> + watchdog_notify_pretimeout(wdd);
> +else
> + //panic by decault instead of WDT_SWRST;
> + panic("MTk Watchdog bark!\n");
> +
The idea behind the pretimeout governor is that it can be disabled
by setting it to "none", and if pretimeout governors are disabled
it is supposed to be a no-op. The above bypasses this mechanism
and is not acceptable.

This suggests that the real timeout indeed doesn't happen when
WDT_MODE_IRQ_EN is set. Again, this is unacceptable.

On a side note, formatting is way off here.

Guenter

> + return IRQ_HANDLED;
> +}
> +
> static const struct watchdog_info mtk_wdt_info = {
> .identity = DRV_NAME,
> .options = WDIOF_SETTIMEOUT |
> @@ -253,12 +277,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 +300,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 +312,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 +408,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");
>
>