On 4/24/21 6:52 PM, Wang Qing wrote: > Use the bark interrupt as the pretimeout notifier if available. > > When the watchdog timer expires in dual mode, an interrupt will be > triggered first, then the timing restarts. The reset signal will be > initiated when the timer expires again. > > The pretimeout notification shall occur at timeout-sec/2. > > 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. > > V4: > - Remove pretimeout related processing. > - Add dual mode control separately. > > V5: > - Fix some formatting and printing problems. > > V6: > - Realize pretimeout processing through dualmode. > > V7: > - Add set_pretimeout(). > > V8/V9: > - Fix some formatting problems. > > Signed-off-by: Wang Qing <wangqing@xxxxxxxx> Reviewed-by: Guenter Roeck <linux@xxxxxxxxxxxx> Note that the version history should be after "---". Guenter > --- > drivers/watchdog/mtk_wdt.c | 77 +++++++++++++++++++++++++++++++++++++++++++--- > 1 file changed, 72 insertions(+), 5 deletions(-) > > diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c > index 97ca993..7bb0774 > --- a/drivers/watchdog/mtk_wdt.c > +++ b/drivers/watchdog/mtk_wdt.c > @@ -25,9 +25,10 @@ > #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 > +#define WDT_MIN_TIMEOUT 2 > #define WDT_LENGTH_TIMEOUT(n) ((n) << 5) > > #define WDT_LENGTH 0x04 > @@ -187,12 +188,19 @@ static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev, > u32 reg; > > wdt_dev->timeout = timeout; > + /* > + * In dual mode, irq will be triggered at timeout / 2 > + * the real timeout occurs at timeout > + */ > + if (wdt_dev->pretimeout) > + wdt_dev->pretimeout = timeout / 2; > > /* > * One bit is the value of 512 ticks > * The clock has 32 KHz > */ > - reg = WDT_LENGTH_TIMEOUT(timeout << 6) | WDT_LENGTH_KEY; > + reg = WDT_LENGTH_TIMEOUT((timeout - wdt_dev->pretimeout) << 6) > + | WDT_LENGTH_KEY; > iowrite32(reg, wdt_base + WDT_LENGTH); > > mtk_wdt_ping(wdt_dev); > @@ -239,13 +247,48 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev) > 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 | WDT_MODE_DUAL_EN); > + else > + reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_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) > +{ > + struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdd); > + void __iomem *wdt_base = mtk_wdt->wdt_base; > + u32 reg = ioread32(wdt_base + WDT_MODE); > + > + if (timeout && !wdd->pretimeout) { > + wdd->pretimeout = wdd->timeout / 2; > + reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); > + } else if (!timeout && wdd->pretimeout) { > + wdd->pretimeout = 0; > + reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); > + } else { > + return 0; > + } > + > + reg |= WDT_MODE_KEY; > + iowrite32(reg, wdt_base + WDT_MODE); > + > + return mtk_wdt_set_timeout(wdd, wdd->timeout); > +} > + > +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 +296,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 +319,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 +331,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 = WDT_MAX_TIMEOUT / 2; > + } 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; >