Commit b5a497a7 authored by Fabrice Gasnier's avatar Fabrice Gasnier Committed by Daniel Lezcano
Browse files

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: default avatarFabrice Gasnier <fabrice.gasnier@foss.st.com>
Link: https://lore.kernel.org/r/20250224172101.3448398-1-fabrice.gasnier@foss.st.com


Signed-off-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
parent f7803f79
Loading
Loading
Loading
Loading
+29 −3
Original line number Diff line number Diff line
@@ -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;
}