diff options
Diffstat (limited to 'drivers/watchdog/rzg2l_wdt.c')
-rw-r--r-- | drivers/watchdog/rzg2l_wdt.c | 83 |
1 files changed, 47 insertions, 36 deletions
diff --git a/drivers/watchdog/rzg2l_wdt.c b/drivers/watchdog/rzg2l_wdt.c index 6b426df34fd6..6eea0ee4af49 100644 --- a/drivers/watchdog/rzg2l_wdt.c +++ b/drivers/watchdog/rzg2l_wdt.c @@ -21,8 +21,11 @@ #define WDTSET 0x04 #define WDTTIM 0x08 #define WDTINT 0x0C +#define PECR 0x10 +#define PEEN 0x14 #define WDTCNT_WDTEN BIT(0) #define WDTINT_INTDISP BIT(0) +#define PEEN_FORCE BIT(0) #define WDT_DEFAULT_TIMEOUT 60U @@ -43,6 +46,8 @@ struct rzg2l_wdt_priv { struct reset_control *rstc; unsigned long osc_clk_rate; unsigned long delay; + struct clk *pclk; + struct clk *osc_clk; }; static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv) @@ -53,7 +58,7 @@ static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv) static u32 rzg2l_wdt_get_cycle_usec(unsigned long cycle, u32 wdttime) { - u64 timer_cycle_us = 1024 * 1024 * (wdttime + 1) * MICRO; + u64 timer_cycle_us = 1024 * 1024ULL * (wdttime + 1) * MICRO; return div64_ul(timer_cycle_us, cycle); } @@ -86,7 +91,6 @@ static int rzg2l_wdt_start(struct watchdog_device *wdev) { struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); - reset_control_deassert(priv->rstc); pm_runtime_get_sync(wdev->parent); /* Initialize time out */ @@ -106,7 +110,26 @@ static int rzg2l_wdt_stop(struct watchdog_device *wdev) struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); pm_runtime_put(wdev->parent); - reset_control_assert(priv->rstc); + reset_control_reset(priv->rstc); + + return 0; +} + +static int rzg2l_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout) +{ + struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); + + wdev->timeout = timeout; + + /* + * If the watchdog is active, reset the module for updating the WDTSET + * register so that it is updated with new timeout values. + */ + if (watchdog_active(wdev)) { + pm_runtime_put(wdev->parent); + reset_control_reset(priv->rstc); + rzg2l_wdt_start(wdev); + } return 0; } @@ -116,15 +139,14 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev, { struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); - /* Reset the module before we modify any register */ - reset_control_reset(priv->rstc); - pm_runtime_get_sync(wdev->parent); + clk_prepare_enable(priv->pclk); + clk_prepare_enable(priv->osc_clk); - /* smallest counter value to reboot soon */ - rzg2l_wdt_write(priv, WDTSET_COUNTER_VAL(1), WDTSET); + /* Generate Reset (WDTRSTB) Signal on parity error */ + rzg2l_wdt_write(priv, 0, PECR); - /* Enable watchdog timer*/ - rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT); + /* Force parity error */ + rzg2l_wdt_write(priv, PEEN_FORCE, PEEN); return 0; } @@ -148,15 +170,15 @@ static const struct watchdog_ops rzg2l_wdt_ops = { .start = rzg2l_wdt_start, .stop = rzg2l_wdt_stop, .ping = rzg2l_wdt_ping, + .set_timeout = rzg2l_wdt_set_timeout, .restart = rzg2l_wdt_restart, }; -static void rzg2l_wdt_reset_assert_pm_disable_put(void *data) +static void rzg2l_wdt_reset_assert_pm_disable(void *data) { struct watchdog_device *wdev = data; struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); - pm_runtime_put(wdev->parent); pm_runtime_disable(wdev->parent); reset_control_assert(priv->rstc); } @@ -166,7 +188,6 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rzg2l_wdt_priv *priv; unsigned long pclk_rate; - struct clk *wdt_clk; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -178,22 +199,20 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) return PTR_ERR(priv->base); /* Get watchdog main clock */ - wdt_clk = clk_get(&pdev->dev, "oscclk"); - if (IS_ERR(wdt_clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(wdt_clk), "no oscclk"); + priv->osc_clk = devm_clk_get(&pdev->dev, "oscclk"); + if (IS_ERR(priv->osc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->osc_clk), "no oscclk"); - priv->osc_clk_rate = clk_get_rate(wdt_clk); - clk_put(wdt_clk); + priv->osc_clk_rate = clk_get_rate(priv->osc_clk); if (!priv->osc_clk_rate) return dev_err_probe(&pdev->dev, -EINVAL, "oscclk rate is 0"); /* Get Peripheral clock */ - wdt_clk = clk_get(&pdev->dev, "pclk"); - if (IS_ERR(wdt_clk)) - return dev_err_probe(&pdev->dev, PTR_ERR(wdt_clk), "no pclk"); + priv->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(priv->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), "no pclk"); - pclk_rate = clk_get_rate(wdt_clk); - clk_put(wdt_clk); + pclk_rate = clk_get_rate(priv->pclk); if (!pclk_rate) return dev_err_probe(&pdev->dev, -EINVAL, "pclk rate is 0"); @@ -204,13 +223,11 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(priv->rstc), "failed to get cpg reset"); - reset_control_deassert(priv->rstc); + ret = reset_control_deassert(priv->rstc); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert"); + pm_runtime_enable(&pdev->dev); - ret = pm_runtime_resume_and_get(&pdev->dev); - if (ret < 0) { - dev_err(dev, "pm_runtime_resume_and_get failed ret=%pe", ERR_PTR(ret)); - goto out_pm_get; - } priv->wdev.info = &rzg2l_wdt_ident; priv->wdev.ops = &rzg2l_wdt_ops; @@ -222,7 +239,7 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&priv->wdev, priv); ret = devm_add_action_or_reset(&pdev->dev, - rzg2l_wdt_reset_assert_pm_disable_put, + rzg2l_wdt_reset_assert_pm_disable, &priv->wdev); if (ret < 0) return ret; @@ -235,12 +252,6 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) dev_warn(dev, "Specified timeout invalid, using default"); return devm_watchdog_register_device(&pdev->dev, &priv->wdev); - -out_pm_get: - pm_runtime_disable(dev); - reset_control_assert(priv->rstc); - - return ret; } static const struct of_device_id rzg2l_wdt_ids[] = { |