arm: stm32mp2: add multifunction timer support for stm32mp25
authorCheick Traore <cheick.traore@foss.st.com>
Fri, 20 Jun 2025 15:49:58 +0000 (17:49 +0200)
committerPatrice Chotard <patrice.chotard@foss.st.com>
Tue, 29 Jul 2025 14:44:58 +0000 (16:44 +0200)
Add support for STM32MP25 SoC.
Identification and hardware configuration registers allow to read the
timer version and capabilities (counter width, ...).
So, rework the probe to avoid touching ARR register by simply read the
counter width when available. This may avoid messing with a possibly
running timer.
Also add useful bit fields to stm32-timers header file.

Signed-off-by: Cheick Traore <cheick.traore@foss.st.com>
Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
arch/arm/mach-stm32mp/include/mach/timers.h
arch/arm/mach-stm32mp/timers.c

index a84465b..8209dd8 100644 (file)
 #define TIM_DMAR       0x4C    /* DMA register for transfer */
 #define TIM_TISEL      0x68    /* Input Selection         */
 
+#define TIM_HWCFGR2    0x3EC   /* hardware configuration 2 Reg (MP25)  */
+#define TIM_HWCFGR1    0x3F0   /* hardware configuration 1 Reg (MP25)  */
+#define TIM_IPIDR      0x3F8   /* IP identification Reg (MP25)         */
+
 #define TIM_CR1_CEN    BIT(0)  /* Counter Enable          */
 #define TIM_CR1_ARPE   BIT(7)
 #define TIM_CCER_CCXE  (BIT(0) | BIT(4) | BIT(8) | BIT(12))
 #define TIM_CCMR_M1    (BIT(6) | BIT(5))  /* Channel PWM Mode 1 */
 #define TIM_BDTR_MOE   BIT(15) /* Main Output Enable      */
 #define TIM_EGR_UG     BIT(0)  /* Update Generation       */
+#define TIM_HWCFGR2_CNT_WIDTH  GENMASK(15, 8)  /* Counter width */
+#define TIM_HWCFGR1_NB_OF_DT   GENMASK(7, 4)   /* Complementary outputs & dead-time generators */
 
 #define MAX_TIM_PSC            0xFFFF
 
+#define STM32MP25_TIM_IPIDR    0x00120002
+
 struct stm32_timers_plat {
        void __iomem *base;
+       u32 ipidr;
 };
 
 struct stm32_timers_priv {
index a320789..1940ba4 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/io.h>
 #include <asm/arch/timers.h>
 #include <dm/device_compat.h>
+#include <linux/bitfield.h>
 
 static void stm32_timers_get_arr_size(struct udevice *dev)
 {
@@ -29,6 +30,33 @@ static void stm32_timers_get_arr_size(struct udevice *dev)
        writel(arr, plat->base + TIM_ARR);
 }
 
+static int stm32_timers_probe_hwcfgr(struct udevice *dev)
+{
+       struct stm32_timers_plat *plat = dev_get_plat(dev);
+       struct stm32_timers_priv *priv = dev_get_priv(dev);
+       u32 val;
+
+       if (!plat->ipidr) {
+               /* fallback to legacy method for probing counter width */
+               stm32_timers_get_arr_size(dev);
+               return 0;
+       }
+
+       val = readl(plat->base + TIM_IPIDR);
+       /* Sanity check on IP identification register */
+       if (val != plat->ipidr) {
+               dev_err(dev, "Unexpected identification: %u\n", val);
+               return -EINVAL;
+       }
+
+       val = readl(plat->base + TIM_HWCFGR2);
+       /* Counter width in bits, max reload value is BIT(width) - 1 */
+       priv->max_arr = BIT(FIELD_GET(TIM_HWCFGR2_CNT_WIDTH, val)) - 1;
+       dev_dbg(dev, "TIM width: %ld\n", FIELD_GET(TIM_HWCFGR2_CNT_WIDTH, val));
+
+       return 0;
+}
+
 static int stm32_timers_of_to_plat(struct udevice *dev)
 {
        struct stm32_timers_plat *plat = dev_get_plat(dev);
@@ -38,6 +66,7 @@ static int stm32_timers_of_to_plat(struct udevice *dev)
                dev_err(dev, "can't get address\n");
                return -ENOENT;
        }
+       plat->ipidr = (u32)dev_get_driver_data(dev);
 
        return 0;
 }
@@ -60,13 +89,16 @@ static int stm32_timers_probe(struct udevice *dev)
 
        priv->rate = clk_get_rate(&clk);
 
-       stm32_timers_get_arr_size(dev);
+       ret = stm32_timers_probe_hwcfgr(dev);
+       if (ret)
+               clk_disable(&clk);
 
        return ret;
 }
 
 static const struct udevice_id stm32_timers_ids[] = {
        { .compatible = "st,stm32-timers" },
+       { .compatible = "st,stm32mp25-timers", .data = STM32MP25_TIM_IPIDR },
        {}
 };