]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
clocksource/drivers/stm32-lptimer: Add support for suspend / resume
authorFabrice Gasnier <fabrice.gasnier@foss.st.com>
Mon, 24 Feb 2025 17:21:01 +0000 (18:21 +0100)
committerDaniel Lezcano <daniel.lezcano@linaro.org>
Fri, 7 Mar 2025 16:55:59 +0000 (17:55 +0100)
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>
drivers/clocksource/timer-stm32-lp.c

index a4c95161cb22c4df6d8baf5a88a9a2d65b58d2ad..9cd487dd5a3a47d5a0c030b0c4e2243396ed738d 100644 (file)
@@ -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;
 }