On 4/9/21 2:55 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. > > Signed-off-by: Wang Qing <wangqing@xxxxxxxx> > --- > 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); That looks suspiciously like the real watchdog won't happen at all. What will happen if the pretimeout governor is set to none ? Guenter > 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; >