>On 4/21/21 7:45 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. >> >> Signed-off-by: Wang Qing <wangqing@xxxxxxxx> >> --- >> drivers/watchdog/mtk_wdt.c | 53 +++++++++++++++++++++++++++++++++++++++++----- >> 1 file changed, 48 insertions(+), 5 deletions(-) >> >> diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c >> index 97ca993..ebc648b >> --- 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 >> @@ -184,15 +185,22 @@ static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev, >> { >> struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); >> void __iomem *wdt_base = mtk_wdt->wdt_base; >> + unsigned int timeout_interval; >> u32 reg; >> >> - wdt_dev->timeout = timeout; >> + timeout_interval = wdt_dev->timeout = timeout; >> + /* >> + * In dual mode, irq will be triggered at timeout/2 >> + * the real timeout occurs at timeout >> + */ >> + if (wdt_dev->pretimeout) >> + timeout_interval = wdt_dev->pretimeout = timeout/2; > >Please run checkpatch --strict and fix what it reports. >Also, there should be a set_pretimeout function to set the >pretimeout. It is ok to update it here, but it should be set >in its own function to make sure that the actual value >is reported back to userspace. > >Thanks, >Guenter The reason why the set_pretimeout interface is not provided is because the pretimeout is fixed after the timeout is set, we need to modify timeout after setting pretimeout, which is puzzling. I will point out this behavior in the doc and comments, or implement the set_pretimeout interface only as a print prompt. What do you think of it? Thanks, Qing > >> >> /* >> * 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_interval << 6) | WDT_LENGTH_KEY; >> iowrite32(reg, wdt_base + WDT_LENGTH); >> >> mtk_wdt_ping(wdt_dev); >> @@ -239,13 +247,25 @@ 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 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,6 +273,14 @@ 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, >> @@ -267,7 +295,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 +307,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; >> >