watchdog: renesas_wdt: don't sleep in atomic context
In the restart handler, we hit multiple OOPSes. One because of usleep_range() and one because of RPM. So, instead of re-using rwdt_start(), we implement an atomic version of it in the restart handler. As a little bonus, reboot will occur sooner because we can now use the smallest divider in the custom restart routine. Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> Link: https://lore.kernel.org/r/20201219173415.21848-1-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
This commit is contained in:
parent
17f0d1b9bd
commit
fa01fa7003
@ -9,6 +9,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@ -50,6 +51,7 @@ struct rwdt_priv {
|
||||
struct watchdog_device wdev;
|
||||
unsigned long clk_rate;
|
||||
u8 cks;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void rwdt_write(struct rwdt_priv *priv, u32 val, unsigned int reg)
|
||||
@ -125,13 +127,30 @@ static unsigned int rwdt_get_timeleft(struct watchdog_device *wdev)
|
||||
return DIV_BY_CLKS_PER_SEC(priv, 65536 - val);
|
||||
}
|
||||
|
||||
/* needs to be atomic - no RPM, no usleep_range, no scheduling! */
|
||||
static int rwdt_restart(struct watchdog_device *wdev, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
struct rwdt_priv *priv = watchdog_get_drvdata(wdev);
|
||||
u8 val;
|
||||
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
/* Stop the timer before we modify any register */
|
||||
val = readb_relaxed(priv->base + RWTCSRA) & ~RWTCSRA_TME;
|
||||
rwdt_write(priv, val, RWTCSRA);
|
||||
/* Delay 2 cycles before setting watchdog counter */
|
||||
udelay(DIV_ROUND_UP(2 * 1000000, priv->clk_rate));
|
||||
|
||||
rwdt_start(wdev);
|
||||
rwdt_write(priv, 0xffff, RWTCNT);
|
||||
/* smallest divider to reboot soon */
|
||||
rwdt_write(priv, 0, RWTCSRA);
|
||||
|
||||
readb_poll_timeout_atomic(priv->base + RWTCSRA, val,
|
||||
!(val & RWTCSRA_WRFLG), 1, 100);
|
||||
|
||||
rwdt_write(priv, RWTCSRA_TME, RWTCSRA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -191,7 +210,6 @@ static int rwdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rwdt_priv *priv;
|
||||
struct clk *clk;
|
||||
unsigned long clks_per_sec;
|
||||
int ret, i;
|
||||
u8 csra;
|
||||
@ -207,13 +225,13 @@ static int rwdt_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
priv->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
priv->clk_rate = clk_get_rate(clk);
|
||||
priv->clk_rate = clk_get_rate(priv->clk);
|
||||
csra = readb_relaxed(priv->base + RWTCSRA);
|
||||
priv->wdev.bootstatus = csra & RWTCSRA_WOVF ? WDIOF_CARDRESET : 0;
|
||||
pm_runtime_put(dev);
|
||||
|
Loading…
Reference in New Issue
Block a user