]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
clocksource/drivers/stm32-lptimer: Add support for stm32mp25
authorFabrice Gasnier <fabrice.gasnier@foss.st.com>
Tue, 29 Apr 2025 12:51:29 +0000 (14:51 +0200)
committerLee Jones <lee@kernel.org>
Tue, 13 May 2025 10:13:02 +0000 (11:13 +0100)
On stm32mp25, DIER (former IER) must only be modified when the lptimer
is enabled. On earlier SoCs, it must be only be modified when it is
disabled. There's also a new DIEROK flag, to ensure register access
has completed.
Add a new "set_evt" routine to be used on stm32mp25, called depending
on the version register, read by the MFD core (LPTIM_VERR).

Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20250429125133.1574167-4-fabrice.gasnier@foss.st.com
Signed-off-by: Lee Jones <lee@kernel.org>
drivers/clocksource/timer-stm32-lp.c

index 928da2f6de69e3e11ffa5b41b6c5cf46e72c1183..6e7944ffd7c032f4d18685e89bda7fcf8a1530cc 100644 (file)
@@ -5,6 +5,7 @@
  *         Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
  */
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/clockchips.h>
 #include <linux/interrupt.h>
@@ -27,6 +28,7 @@ struct stm32_lp_private {
        u32 psc;
        struct device *dev;
        struct clk *clk;
+       u32 version;
 };
 
 static struct stm32_lp_private*
@@ -47,12 +49,46 @@ static int stm32_clkevent_lp_shutdown(struct clock_event_device *clkevt)
        return 0;
 }
 
-static int stm32_clkevent_lp_set_timer(unsigned long evt,
-                                      struct clock_event_device *clkevt,
-                                      int is_periodic)
+static int stm32mp25_clkevent_lp_set_evt(struct stm32_lp_private *priv, unsigned long evt)
 {
-       struct stm32_lp_private *priv = to_priv(clkevt);
+       int ret;
+       u32 val;
+
+       regmap_read(priv->reg, STM32_LPTIM_CR, &val);
+       if (!FIELD_GET(STM32_LPTIM_ENABLE, val)) {
+               /* Enable LPTIMER to be able to write into IER and ARR registers */
+               regmap_write(priv->reg, STM32_LPTIM_CR, STM32_LPTIM_ENABLE);
+               /*
+                * After setting the ENABLE bit, a delay of two counter clock cycles is needed
+                * before the LPTIM is actually enabled. For 32KHz rate, this makes approximately
+                * 62.5 micro-seconds, round it up.
+                */
+               udelay(63);
+       }
+       /* set next event counter */
+       regmap_write(priv->reg, STM32_LPTIM_ARR, evt);
+       /* enable ARR interrupt */
+       regmap_write(priv->reg, STM32_LPTIM_IER, STM32_LPTIM_ARRMIE);
+
+       /* Poll DIEROK and ARROK to ensure register access has completed */
+       ret = regmap_read_poll_timeout_atomic(priv->reg, STM32_LPTIM_ISR, val,
+                                             (val & STM32_LPTIM_DIEROK_ARROK) ==
+                                             STM32_LPTIM_DIEROK_ARROK,
+                                             10, 500);
+       if (ret) {
+               dev_err(priv->dev, "access to LPTIM timed out\n");
+               /* Disable LPTIMER */
+               regmap_write(priv->reg, STM32_LPTIM_CR, 0);
+               return ret;
+       }
+       /* Clear DIEROK and ARROK flags */
+       regmap_write(priv->reg, STM32_LPTIM_ICR, STM32_LPTIM_DIEROKCF_ARROKCF);
 
+       return 0;
+}
+
+static void stm32_clkevent_lp_set_evt(struct stm32_lp_private *priv, unsigned long evt)
+{
        /* disable LPTIMER to be able to write into IER register*/
        regmap_write(priv->reg, STM32_LPTIM_CR, 0);
        /* enable ARR interrupt */
@@ -61,6 +97,22 @@ static int stm32_clkevent_lp_set_timer(unsigned long evt,
        regmap_write(priv->reg, STM32_LPTIM_CR, STM32_LPTIM_ENABLE);
        /* set next event counter */
        regmap_write(priv->reg, STM32_LPTIM_ARR, evt);
+}
+
+static int stm32_clkevent_lp_set_timer(unsigned long evt,
+                                      struct clock_event_device *clkevt,
+                                      int is_periodic)
+{
+       struct stm32_lp_private *priv = to_priv(clkevt);
+       int ret;
+
+       if (priv->version == STM32_LPTIM_VERR_23) {
+               ret = stm32mp25_clkevent_lp_set_evt(priv, evt);
+               if (ret)
+                       return ret;
+       } else {
+               stm32_clkevent_lp_set_evt(priv, evt);
+       }
 
        /* start counter */
        if (is_periodic)
@@ -176,6 +228,7 @@ static int stm32_clkevent_lp_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        priv->reg = ddata->regmap;
+       priv->version = ddata->version;
        priv->clk = ddata->clk;
        ret = clk_prepare_enable(priv->clk);
        if (ret)