diff options
author | Fabrice Gasnier <fabrice.gasnier@foss.st.com> | 2025-02-24 18:21:01 +0100 |
---|---|---|
committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2025-03-07 17:55:59 +0100 |
commit | b5a497a7972a49c31d39b057f86dd6f58b882a72 (patch) | |
tree | f20d3ddaf290b8a75c71c40df06eb1e2862b3d70 /drivers/clocksource/timer-stm32-lp.c | |
parent | f7803f7905e133a5fa5b596da5bae89b1528757e (diff) |
clocksource/drivers/stm32-lptimer: Add support for suspend / resume
Add support for power management on STM32 LPTIMER clocksource driver:
- Upon clockevents_suspend(), shutdown the LPTIMER, and balance the
clk_prepare_enable() from the probe routine.
- Upon clockevents_resume(), restore the prescaler that may have been lost
during low power mode, and restore the clock.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Link: https://lore.kernel.org/r/20250224172101.3448398-1-fabrice.gasnier@foss.st.com
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Diffstat (limited to 'drivers/clocksource/timer-stm32-lp.c')
-rw-r--r-- | drivers/clocksource/timer-stm32-lp.c | 32 |
1 files changed, 29 insertions, 3 deletions
diff --git a/drivers/clocksource/timer-stm32-lp.c b/drivers/clocksource/timer-stm32-lp.c index a4c95161cb22..9cd487dd5a3a 100644 --- a/drivers/clocksource/timer-stm32-lp.c +++ b/drivers/clocksource/timer-stm32-lp.c @@ -24,7 +24,9 @@ struct stm32_lp_private { struct regmap *reg; struct clock_event_device clkevt; unsigned long period; + u32 psc; struct device *dev; + struct clk *clk; }; static struct stm32_lp_private* @@ -120,6 +122,27 @@ static void stm32_clkevent_lp_set_prescaler(struct stm32_lp_private *priv, /* Adjust rate and period given the prescaler value */ *rate = DIV_ROUND_CLOSEST(*rate, (1 << i)); priv->period = DIV_ROUND_UP(*rate, HZ); + priv->psc = i; +} + +static void stm32_clkevent_lp_suspend(struct clock_event_device *clkevt) +{ + struct stm32_lp_private *priv = to_priv(clkevt); + + stm32_clkevent_lp_shutdown(clkevt); + + /* balance clk_prepare_enable() from the probe */ + clk_disable_unprepare(priv->clk); +} + +static void stm32_clkevent_lp_resume(struct clock_event_device *clkevt) +{ + struct stm32_lp_private *priv = to_priv(clkevt); + + clk_prepare_enable(priv->clk); + + /* restore prescaler */ + regmap_write(priv->reg, STM32_LPTIM_CFGR, priv->psc << CFGR_PSC_OFFSET); } static void stm32_clkevent_lp_init(struct stm32_lp_private *priv, @@ -134,6 +157,8 @@ static void stm32_clkevent_lp_init(struct stm32_lp_private *priv, priv->clkevt.set_state_oneshot = stm32_clkevent_lp_set_oneshot; priv->clkevt.set_next_event = stm32_clkevent_lp_set_next_event; priv->clkevt.rating = STM32_LP_RATING; + priv->clkevt.suspend = stm32_clkevent_lp_suspend; + priv->clkevt.resume = stm32_clkevent_lp_resume; clockevents_config_and_register(&priv->clkevt, rate, 0x1, STM32_LPTIM_MAX_ARR); @@ -151,11 +176,12 @@ static int stm32_clkevent_lp_probe(struct platform_device *pdev) return -ENOMEM; priv->reg = ddata->regmap; - ret = clk_prepare_enable(ddata->clk); + priv->clk = ddata->clk; + ret = clk_prepare_enable(priv->clk); if (ret) return -EINVAL; - rate = clk_get_rate(ddata->clk); + rate = clk_get_rate(priv->clk); if (!rate) { ret = -EINVAL; goto out_clk_disable; @@ -191,7 +217,7 @@ static int stm32_clkevent_lp_probe(struct platform_device *pdev) return 0; out_clk_disable: - clk_disable_unprepare(ddata->clk); + clk_disable_unprepare(priv->clk); return ret; } |