[PATCH 2/5] mach-stm32: add multifunction timer driver support

Cheick Traore cheick.traore at foss.st.com
Thu Mar 6 11:56:17 CET 2025


Add support for STM32MP timer multi-function driver.
These timers can be use as counter, trigger or pwm generator.
This driver will be used to manage the main resources of the timer to
provide them to the functionnalities which need these ones.

Signed-off-by: Cheick Traore <cheick.traore at foss.st.com>
---

 arch/arm/mach-stm32mp/Kconfig               |  6 ++
 arch/arm/mach-stm32mp/Makefile              |  1 +
 arch/arm/mach-stm32mp/include/mach/timers.h | 55 ++++++++++++++
 arch/arm/mach-stm32mp/timers.c              | 82 +++++++++++++++++++++
 4 files changed, 144 insertions(+)
 create mode 100644 arch/arm/mach-stm32mp/include/mach/timers.h
 create mode 100644 arch/arm/mach-stm32mp/timers.c

diff --git a/arch/arm/mach-stm32mp/Kconfig b/arch/arm/mach-stm32mp/Kconfig
index 25663a99464..002da2e3d3b 100644
--- a/arch/arm/mach-stm32mp/Kconfig
+++ b/arch/arm/mach-stm32mp/Kconfig
@@ -153,6 +153,12 @@ config CMD_STM32KEY
 		This command is used to evaluate the secure boot on stm32mp SOC,
 		it is deactivated by default in real products.
 
+config MFD_STM32_TIMERS
+	bool "STM32 multifonction timer support"
+	help
+	  Select this to enable support for the multifunction timer found on
+	  STM32 devices.
+
 source "arch/arm/mach-stm32mp/Kconfig.13x"
 source "arch/arm/mach-stm32mp/Kconfig.15x"
 source "arch/arm/mach-stm32mp/Kconfig.25x"
diff --git a/arch/arm/mach-stm32mp/Makefile b/arch/arm/mach-stm32mp/Makefile
index db7ed19bd91..103e3410ad9 100644
--- a/arch/arm/mach-stm32mp/Makefile
+++ b/arch/arm/mach-stm32mp/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_STM32MP15X) += stm32mp1/
 obj-$(CONFIG_STM32MP13X) += stm32mp1/
 obj-$(CONFIG_STM32MP25X) += stm32mp2/
 
+obj-$(CONFIG_MFD_STM32_TIMERS) += timers.o
 obj-$(CONFIG_STM32_ECDSA_VERIFY) += ecdsa_romapi.o
 ifndef CONFIG_XPL_BUILD
 obj-y += cmd_stm32prog/
diff --git a/arch/arm/mach-stm32mp/include/mach/timers.h b/arch/arm/mach-stm32mp/include/mach/timers.h
new file mode 100644
index 00000000000..a84465bb28e
--- /dev/null
+++ b/arch/arm/mach-stm32mp/include/mach/timers.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025, STMicroelectronics - All Rights Reserved
+ * Author: Cheick Traore <cheick.traore at foss.st.com>
+ *
+ * Originally based on the Linux kernel v6.1 include/linux/mfd/stm32-timers.h.
+ */
+
+#ifndef __STM32_TIMERS_H
+#define __STM32_TIMERS_H
+
+#include <clk.h>
+
+#define TIM_CR1		0x00	/* Control Register 1      */
+#define TIM_CR2		0x04	/* Control Register 2      */
+#define TIM_SMCR	0x08	/* Slave mode control reg  */
+#define TIM_DIER	0x0C	/* DMA/interrupt register  */
+#define TIM_SR		0x10	/* Status register	   */
+#define TIM_EGR		0x14	/* Event Generation Reg    */
+#define TIM_CCMR1	0x18	/* Capt/Comp 1 Mode Reg    */
+#define TIM_CCMR2	0x1C	/* Capt/Comp 2 Mode Reg    */
+#define TIM_CCER	0x20	/* Capt/Comp Enable Reg    */
+#define TIM_CNT		0x24	/* Counter		   */
+#define TIM_PSC		0x28	/* Prescaler               */
+#define TIM_ARR		0x2c	/* Auto-Reload Register    */
+#define TIM_CCRx(x)	(0x34 + 4 * ((x) - 1))	/* Capt/Comp Register x (x ∈ {1, .. 4})	*/
+#define TIM_BDTR	0x44	/* Break and Dead-Time Reg */
+#define TIM_DCR		0x48	/* DMA control register    */
+#define TIM_DMAR	0x4C	/* DMA register for transfer */
+#define TIM_TISEL	0x68	/* Input Selection         */
+
+#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_CCER_CC1E	BIT(0)
+#define TIM_CCER_CC1P	BIT(1)	/* Capt/Comp 1  Polarity   */
+#define TIM_CCER_CC1NE	BIT(2)	/* Capt/Comp 1N out Ena    */
+#define TIM_CCER_CC1NP	BIT(3)	/* Capt/Comp 1N Polarity   */
+#define TIM_CCMR_PE	BIT(3)	/* Channel Preload Enable  */
+#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 MAX_TIM_PSC		0xFFFF
+
+struct stm32_timers_plat {
+	void __iomem *base;
+};
+
+struct stm32_timers_priv {
+	u32 max_arr;
+	ulong rate;
+};
+
+#endif
diff --git a/arch/arm/mach-stm32mp/timers.c b/arch/arm/mach-stm32mp/timers.c
new file mode 100644
index 00000000000..a3207895f40
--- /dev/null
+++ b/arch/arm/mach-stm32mp/timers.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025, STMicroelectronics - All Rights Reserved
+ * Author: Cheick Traore <cheick.traore at foss.st.com>
+ *
+ * Originally based on the Linux kernel v6.1 drivers/mfd/stm32-timers.c.
+ */
+
+#include <dm.h>
+#include <asm/io.h>
+#include <asm/arch/timers.h>
+#include <dm/device_compat.h>
+
+static void stm32_timers_get_arr_size(struct udevice *dev)
+{
+	struct stm32_timers_plat *plat = dev_get_plat(dev);
+	struct stm32_timers_priv *priv = dev_get_priv(dev);
+	u32 arr;
+
+	/* Backup ARR to restore it after getting the maximum value */
+	arr = readl(plat->base + TIM_ARR);
+
+	/*
+	 * Only the available bits will be written so when readback
+	 * we get the maximum value of auto reload register
+	 */
+	writel(~0L, plat->base + TIM_ARR);
+	priv->max_arr = readl(plat->base + TIM_ARR);
+	writel(arr, plat->base + TIM_ARR);
+}
+
+static int stm32_timers_of_to_plat(struct udevice *dev)
+{
+	struct stm32_timers_plat *plat = dev_get_plat(dev);
+
+	plat->base = dev_read_addr_ptr(dev);
+	if (!plat->base) {
+		dev_err(dev, "can't get address\n");
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static int stm32_timers_probe(struct udevice *dev)
+{
+	struct stm32_timers_priv *priv = dev_get_priv(dev);
+	struct clk clk;
+	int ret = 0;
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(&clk);
+	if (ret) {
+		dev_err(dev, "failed to enable clock: ret=%d\n", ret);
+		return ret;
+	}
+
+	priv->rate = clk_get_rate(&clk);
+
+	stm32_timers_get_arr_size(dev);
+
+	return ret;
+}
+
+static const struct udevice_id stm32_timers_ids[] = {
+	{ .compatible = "st,stm32-timers" },
+	{}
+};
+
+U_BOOT_DRIVER(stm32_timers) = {
+	.name  = "stm32_timers",
+	.id = UCLASS_NOP,
+	.of_match = stm32_timers_ids,
+	.of_to_plat = stm32_timers_of_to_plat,
+	.plat_auto = sizeof(struct stm32_timers_plat),
+	.probe = stm32_timers_probe,
+	.priv_auto = sizeof(struct stm32_timers_priv),
+	.bind = dm_scan_fdt_dev,
+};
-- 
2.34.1



More information about the U-Boot mailing list