summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2025-03-12 21:36:52 -0600
committerTom Rini <trini@konsulko.com>2025-03-12 21:36:52 -0600
commiteeefcacb851f7f0bccabc3089a725f5ce86f5c70 (patch)
tree913e063346db8a36c2094aabb35561f9669990fb
parent19a342f9122780a67d96887eae3ab62a189126d5 (diff)
parent1a87755ecd0d36ec29acdbcbc9b381e76cfd6cfd (diff)
Merge tag 'u-boot-stm32-20250312' of https://source.denx.de/u-boot/custodians/u-boot-stm into next
CI: https://source.denx.de/u-boot/custodians/u-boot-stm/-/pipelines/25112 - Add drivers for MFD STM32 TIMERS and STM32 PWM and enable them on stm32mp135f-dk - Restrict _debug_uart_init() usage in STM32 serial driver - Add support for environment in eMMC on STM32MP13xx DHCOR SoM - Introduce DH STM32MP15xx DHSOM board specific defconfigs - Fix CONFIG_BOOTCOUNT_ALTBOOTCMD update on DH STM32MP1 DHSOM - Update maintainer for board stm32f746-disco - Fix Linux cmdline for stm32f769-disco - Cleanup in stm32f***-u-boot.dtsi and in board_late_init() by removing legacy led and button management.
-rw-r--r--arch/arm/dts/stm32746g-eval-u-boot.dtsi10
-rw-r--r--arch/arm/dts/stm32f746-disco-u-boot.dtsi10
-rw-r--r--arch/arm/dts/stm32f769-disco-u-boot.dtsi10
-rw-r--r--arch/arm/dts/stm32mp13-pinctrl.dtsi15
-rw-r--r--arch/arm/dts/stm32mp135f-dhcor-dhsbc-u-boot.dtsi22
-rw-r--r--arch/arm/dts/stm32mp135f-dk.dts14
-rw-r--r--arch/arm/dts/stm32mp13xx-dhcor-u-boot.dtsi2
-rw-r--r--arch/arm/mach-stm32mp/Kconfig6
-rw-r--r--arch/arm/mach-stm32mp/Makefile1
-rw-r--r--arch/arm/mach-stm32mp/include/mach/timers.h55
-rw-r--r--arch/arm/mach-stm32mp/stm32mp1/cpu.c2
-rw-r--r--arch/arm/mach-stm32mp/timers.c82
-rw-r--r--board/st/stm32f746-disco/MAINTAINERS2
-rw-r--r--board/st/stm32f746-disco/stm32f746-disco.c36
-rw-r--r--configs/stm32746g-eval_defconfig1
-rw-r--r--configs/stm32746g-eval_spl_defconfig1
-rw-r--r--configs/stm32f746-disco_defconfig1
-rw-r--r--configs/stm32f746-disco_spl_defconfig1
-rw-r--r--configs/stm32f769-disco_defconfig2
-rw-r--r--configs/stm32mp13_defconfig4
-rw-r--r--configs/stm32mp13_dhcor_defconfig5
-rw-r--r--configs/stm32mp15_dhcom_basic.config8
-rw-r--r--configs/stm32mp15_dhcom_basic_defconfig9
-rw-r--r--configs/stm32mp15_dhcom_drc02_basic_defconfig4
-rw-r--r--configs/stm32mp15_dhcom_pdk2_basic_defconfig4
-rw-r--r--configs/stm32mp15_dhcom_picoitx_basic_defconfig4
-rw-r--r--configs/stm32mp15_dhcor_avenger96_basic_defconfig4
-rw-r--r--configs/stm32mp15_dhcor_basic.config8
-rw-r--r--configs/stm32mp15_dhcor_basic_defconfig9
-rw-r--r--configs/stm32mp15_dhcor_drc_compact_basic_defconfig4
-rw-r--r--configs/stm32mp15_dhcor_testbench_basic_defconfig4
-rw-r--r--drivers/pwm/Kconfig8
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/pwm-stm32.c205
-rw-r--r--drivers/serial/serial_stm32.c18
35 files changed, 475 insertions, 97 deletions
diff --git a/arch/arm/dts/stm32746g-eval-u-boot.dtsi b/arch/arm/dts/stm32746g-eval-u-boot.dtsi
index 1c288acec99..32b5c7cea4b 100644
--- a/arch/arm/dts/stm32746g-eval-u-boot.dtsi
+++ b/arch/arm/dts/stm32746g-eval-u-boot.dtsi
@@ -22,16 +22,6 @@
mmc0 = &sdio1;
spi0 = &qspi;
};
-
- button1 {
- compatible = "st,button1";
- button-gpio = <&gpioc 13 0>;
- };
-
- led1 {
- compatible = "st,led1";
- led-gpio = <&gpiof 10 0>;
- };
};
&fmc {
diff --git a/arch/arm/dts/stm32f746-disco-u-boot.dtsi b/arch/arm/dts/stm32f746-disco-u-boot.dtsi
index 1b42d6cbbc1..38d797e49a0 100644
--- a/arch/arm/dts/stm32f746-disco-u-boot.dtsi
+++ b/arch/arm/dts/stm32f746-disco-u-boot.dtsi
@@ -22,16 +22,6 @@
mmc0 = &sdio1;
spi0 = &qspi;
};
-
- button1 {
- compatible = "st,button1";
- button-gpio = <&gpioi 11 0>;
- };
-
- led1 {
- compatible = "st,led1";
- led-gpio = <&gpioi 1 0>;
- };
};
&ltdc {
diff --git a/arch/arm/dts/stm32f769-disco-u-boot.dtsi b/arch/arm/dts/stm32f769-disco-u-boot.dtsi
index add55c96e21..7c99a6e61b6 100644
--- a/arch/arm/dts/stm32f769-disco-u-boot.dtsi
+++ b/arch/arm/dts/stm32f769-disco-u-boot.dtsi
@@ -23,16 +23,6 @@
spi0 = &qspi;
};
- button1 {
- compatible = "st,button1";
- button-gpio = <&gpioa 0 0>;
- };
-
- led1 {
- compatible = "st,led1";
- led-gpio = <&gpioj 5 0>;
- };
-
panel: panel {
compatible = "orisetech,otm8009a";
reset-gpios = <&gpioj 15 1>;
diff --git a/arch/arm/dts/stm32mp13-pinctrl.dtsi b/arch/arm/dts/stm32mp13-pinctrl.dtsi
index c01d39f03ea..52c2a9f24d7 100644
--- a/arch/arm/dts/stm32mp13-pinctrl.dtsi
+++ b/arch/arm/dts/stm32mp13-pinctrl.dtsi
@@ -215,6 +215,21 @@
};
};
+ pwm1_ch3n_pins_a: pwm1-ch3n-0 {
+ pins {
+ pinmux = <STM32_PINMUX('E', 12, AF1)>; /* TIM1_CH3N */
+ bias-pull-down;
+ drive-push-pull;
+ slew-rate = <0>;
+ };
+ };
+
+ pwm1_ch3n_sleep_pins_a: pwm1-ch3n-sleep-0 {
+ pins {
+ pinmux = <STM32_PINMUX('E', 12, ANALOG)>; /* TIM1_CH3N */
+ };
+ };
+
pwm3_pins_a: pwm3-0 {
pins {
pinmux = <STM32_PINMUX('B', 1, AF2)>; /* TIM3_CH4 */
diff --git a/arch/arm/dts/stm32mp135f-dhcor-dhsbc-u-boot.dtsi b/arch/arm/dts/stm32mp135f-dhcor-dhsbc-u-boot.dtsi
index d718aae16ca..eace94f5fa4 100644
--- a/arch/arm/dts/stm32mp135f-dhcor-dhsbc-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp135f-dhcor-dhsbc-u-boot.dtsi
@@ -23,3 +23,25 @@
&usbphyc {
bootph-all;
};
+
+&st33htph {
+ reset-gpios = <&gpioe 12 GPIO_ACTIVE_LOW>;
+};
+
+/* LDO2 is expansion connector 3V3 supply on STM32MP13xx DHCOR DHSBC rev.200 */
+&vdd_ldo2 {
+ bootph-all;
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+};
+
+/* LDO5 is carrier board 3V3 supply on STM32MP13xx DHCOR DHSBC rev.200 */
+&vdd_sd {
+ bootph-all;
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+};
diff --git a/arch/arm/dts/stm32mp135f-dk.dts b/arch/arm/dts/stm32mp135f-dk.dts
index eea740d097c..275823da3c6 100644
--- a/arch/arm/dts/stm32mp135f-dk.dts
+++ b/arch/arm/dts/stm32mp135f-dk.dts
@@ -9,6 +9,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/leds/common.h>
+#include <dt-bindings/pwm/pwm.h>
#include <dt-bindings/regulator/st,stm32mp13-regulator.h>
#include "stm32mp135.dtsi"
#include "stm32mp13xf.dtsi"
@@ -207,6 +208,19 @@
status = "disabled";
};
+&timers1 {
+ /* spare dmas for other usage */
+ /delete-property/dmas;
+ /delete-property/dma-names;
+ status = "okay";
+ pwm1: pwm {
+ pinctrl-0 = <&pwm1_ch3n_pins_a>;
+ pinctrl-1 = <&pwm1_ch3n_sleep_pins_a>;
+ pinctrl-names = "default", "sleep";
+ status = "okay";
+ };
+};
+
&timers3 {
/delete-property/dmas;
/delete-property/dma-names;
diff --git a/arch/arm/dts/stm32mp13xx-dhcor-u-boot.dtsi b/arch/arm/dts/stm32mp13xx-dhcor-u-boot.dtsi
index 30e3b91bccc..9ff42ab8248 100644
--- a/arch/arm/dts/stm32mp13xx-dhcor-u-boot.dtsi
+++ b/arch/arm/dts/stm32mp13xx-dhcor-u-boot.dtsi
@@ -13,6 +13,8 @@
config {
dh,ddr3-coding-gpios = <&gpiod 5 0>, <&gpiod 9 0>;
dh,som-coding-gpios = <&gpioa 13 0>, <&gpioi 1 0>;
+ u-boot,mmc-env-offset = <0x3fc000>;
+ u-boot,mmc-env-offset-redundant = <0x3fc000>;
};
};
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@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/stm32mp1/cpu.c b/arch/arm/mach-stm32mp/stm32mp1/cpu.c
index d5eaf6711b6..18175fd12cc 100644
--- a/arch/arm/mach-stm32mp/stm32mp1/cpu.c
+++ b/arch/arm/mach-stm32mp/stm32mp1/cpu.c
@@ -138,8 +138,6 @@ int mach_cpu_init(void)
if (IS_ENABLED(CONFIG_CMD_STM32PROG_SERIAL) &&
(boot_mode & TAMP_BOOT_DEVICE_MASK) == BOOT_SERIAL_UART)
gd->flags |= GD_FLG_SILENT | GD_FLG_DISABLE_CONSOLE;
- else if (IS_ENABLED(CONFIG_DEBUG_UART) && IS_ENABLED(CONFIG_XPL_BUILD))
- debug_uart_init();
return 0;
}
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@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,
+};
diff --git a/board/st/stm32f746-disco/MAINTAINERS b/board/st/stm32f746-disco/MAINTAINERS
index 18e4c99c4fb..f9c3af6fb8b 100644
--- a/board/st/stm32f746-disco/MAINTAINERS
+++ b/board/st/stm32f746-disco/MAINTAINERS
@@ -1,5 +1,5 @@
STM32F746 DISCOVERY BOARD
-M: Vikas Manocha <vikas.manocha@st.com>
+M: Patrice Chotard <patrice.chotard@foss.st.com>
S: Maintained
F: doc/board/st/
F: board/st/stm32f746-disco
diff --git a/board/st/stm32f746-disco/stm32f746-disco.c b/board/st/stm32f746-disco/stm32f746-disco.c
index 8966a09501e..07bc8a5f0a2 100644
--- a/board/st/stm32f746-disco/stm32f746-disco.c
+++ b/board/st/stm32f746-disco/stm32f746-disco.c
@@ -76,42 +76,6 @@ u32 spl_boot_device(void)
}
#endif
-int board_late_init(void)
-{
- struct gpio_desc gpio = {};
- int node;
-
- node = fdt_node_offset_by_compatible(gd->fdt_blob, 0, "st,led1");
- if (node < 0)
- return -1;
-
- gpio_request_by_name_nodev(offset_to_ofnode(node), "led-gpio", 0, &gpio,
- GPIOD_IS_OUT);
-
- if (dm_gpio_is_valid(&gpio)) {
- dm_gpio_set_value(&gpio, 0);
- mdelay(10);
- dm_gpio_set_value(&gpio, 1);
- }
-
- /* read button 1*/
- node = fdt_node_offset_by_compatible(gd->fdt_blob, 0, "st,button1");
- if (node < 0)
- return -1;
-
- gpio_request_by_name_nodev(offset_to_ofnode(node), "button-gpio", 0,
- &gpio, GPIOD_IS_IN);
-
- if (dm_gpio_is_valid(&gpio)) {
- if (dm_gpio_get_value(&gpio))
- puts("usr button is at HIGH LEVEL\n");
- else
- puts("usr button is at LOW LEVEL\n");
- }
-
- return 0;
-}
-
int board_init(void)
{
#ifdef CONFIG_ETH_DESIGNWARE
diff --git a/configs/stm32746g-eval_defconfig b/configs/stm32746g-eval_defconfig
index 4346ecd6e42..bd3a48b20a2 100644
--- a/configs/stm32746g-eval_defconfig
+++ b/configs/stm32746g-eval_defconfig
@@ -21,7 +21,6 @@ CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk consoleblank=0 ignore_loglevel"
CONFIG_SYS_PBSIZE=1050
# CONFIG_DISPLAY_CPUINFO is not set
-CONFIG_BOARD_LATE_INIT=y
CONFIG_SYS_PROMPT="U-Boot > "
CONFIG_CMD_GPT=y
CONFIG_CMD_MMC=y
diff --git a/configs/stm32746g-eval_spl_defconfig b/configs/stm32746g-eval_spl_defconfig
index 2756ad5508f..d47d059d23b 100644
--- a/configs/stm32746g-eval_spl_defconfig
+++ b/configs/stm32746g-eval_spl_defconfig
@@ -30,7 +30,6 @@ CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk consoleblank=0 ignore_loglevel"
CONFIG_SYS_PBSIZE=1050
# CONFIG_DISPLAY_CPUINFO is not set
-CONFIG_BOARD_LATE_INIT=y
CONFIG_SPL_PAD_TO=0x9000
CONFIG_SPL_NO_BSS_LIMIT=y
CONFIG_SPL_BOARD_INIT=y
diff --git a/configs/stm32f746-disco_defconfig b/configs/stm32f746-disco_defconfig
index 35a489c34e0..f6fbf83f68f 100644
--- a/configs/stm32f746-disco_defconfig
+++ b/configs/stm32f746-disco_defconfig
@@ -21,7 +21,6 @@ CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk consoleblank=0 ignore_loglevel"
CONFIG_SYS_PBSIZE=1050
# CONFIG_DISPLAY_CPUINFO is not set
-CONFIG_BOARD_LATE_INIT=y
CONFIG_SYS_PROMPT="U-Boot > "
CONFIG_CMD_GPT=y
CONFIG_CMD_MMC=y
diff --git a/configs/stm32f746-disco_spl_defconfig b/configs/stm32f746-disco_spl_defconfig
index 6826b1cb755..dcf077dbfee 100644
--- a/configs/stm32f746-disco_spl_defconfig
+++ b/configs/stm32f746-disco_spl_defconfig
@@ -30,7 +30,6 @@ CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk consoleblank=0 ignore_loglevel"
CONFIG_SYS_PBSIZE=1050
# CONFIG_DISPLAY_CPUINFO is not set
-CONFIG_BOARD_LATE_INIT=y
CONFIG_SPL_PAD_TO=0x9000
CONFIG_SPL_NO_BSS_LIMIT=y
CONFIG_SPL_BOARD_INIT=y
diff --git a/configs/stm32f769-disco_defconfig b/configs/stm32f769-disco_defconfig
index 5be221afd2f..9edda0e36b2 100644
--- a/configs/stm32f769-disco_defconfig
+++ b/configs/stm32f769-disco_defconfig
@@ -18,7 +18,7 @@ CONFIG_AUTOBOOT_KEYED=y
CONFIG_AUTOBOOT_PROMPT="Hit SPACE in %d seconds to stop autoboot.\n"
CONFIG_AUTOBOOT_STOP_STR=" "
CONFIG_USE_BOOTARGS=y
-CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk consoleblank=0 ignore_loglevel"
+CONFIG_BOOTARGS="console=ttySTM0,115200n8 earlyprintk consoleblank=0 ignore_loglevel"
CONFIG_SYS_PBSIZE=1050
# CONFIG_DISPLAY_CPUINFO is not set
CONFIG_CYCLIC_MAX_CPU_TIME_US=8000
diff --git a/configs/stm32mp13_defconfig b/configs/stm32mp13_defconfig
index 0acd1487c9a..4c6a7f82fe5 100644
--- a/configs/stm32mp13_defconfig
+++ b/configs/stm32mp13_defconfig
@@ -10,6 +10,7 @@ CONFIG_SYS_LOAD_ADDR=0xc2000000
CONFIG_STM32MP13X=y
CONFIG_DDR_CACHEABLE_SIZE=0x8000000
CONFIG_CMD_STM32KEY=y
+CONFIG_MFD_STM32_TIMERS=y
CONFIG_TARGET_ST_STM32MP13X=y
CONFIG_ENV_OFFSET_REDUND=0x940000
CONFIG_CMD_STM32PROG=y
@@ -31,6 +32,7 @@ CONFIG_CMD_UNZIP=y
CONFIG_CMD_CLK=y
CONFIG_CMD_FUSE=y
CONFIG_CMD_GPIO=y
+CONFIG_CMD_PWM=y
CONFIG_CMD_I2C=y
CONFIG_CMD_LSBLK=y
CONFIG_CMD_MMC=y
@@ -80,6 +82,8 @@ CONFIG_DM_REGULATOR=y
CONFIG_DM_REGULATOR_FIXED=y
CONFIG_DM_REGULATOR_GPIO=y
CONFIG_DM_REGULATOR_SCMI=y
+CONFIG_DM_PWM=y
+CONFIG_PWM_STM32=y
CONFIG_RESET_SCMI=y
CONFIG_DM_RNG=y
CONFIG_RNG_STM32=y
diff --git a/configs/stm32mp13_dhcor_defconfig b/configs/stm32mp13_dhcor_defconfig
index 4dc3954128d..2da9287ea7b 100644
--- a/configs/stm32mp13_dhcor_defconfig
+++ b/configs/stm32mp13_dhcor_defconfig
@@ -28,10 +28,14 @@ CONFIG_CMD_RNG=y
CONFIG_CMD_LOG=y
CONFIG_CMD_UBI=y
CONFIG_ENV_IS_NOWHERE=y
+CONFIG_ENV_IS_IN_MMC=y
+CONFIG_ENV_MMC_USE_DT=y
CONFIG_ENV_SPI_MAX_HZ=50000000
CONFIG_CLK_SCMI=y
CONFIG_SET_DFU_ALT_INFO=y
CONFIG_SYS_I2C_EEPROM_ADDR=0x50
+CONFIG_SYS_MMC_ENV_DEV=0
+CONFIG_SYS_MMC_ENV_PART=1
CONFIG_PHY_REALTEK=y
CONFIG_DM_REGULATOR_SCMI=y
CONFIG_RESET_SCMI=y
@@ -44,4 +48,3 @@ CONFIG_OPTEE=y
CONFIG_USB_ONBOARD_HUB=y
CONFIG_USB_HUB_DEBOUNCE_TIMEOUT=2000
CONFIG_ERRNO_STR=y
-CONFIG_BOOTCOUNT_ALTBOOTCMD="
diff --git a/configs/stm32mp15_dhcom_basic.config b/configs/stm32mp15_dhcom_basic.config
new file mode 100644
index 00000000000..d78916bb5b2
--- /dev/null
+++ b/configs/stm32mp15_dhcom_basic.config
@@ -0,0 +1,8 @@
+#include <configs/stm32mp15_dhsom.config>
+
+CONFIG_ARM=y
+CONFIG_ARCH_STM32MP=y
+CONFIG_SYS_MEMTEST_START=0xc0000000
+CONFIG_SYS_MEMTEST_END=0xc4000000
+CONFIG_SYS_I2C_EEPROM_BUS=3
+CONFIG_SYS_I2C_EEPROM_ADDR=0x50
diff --git a/configs/stm32mp15_dhcom_basic_defconfig b/configs/stm32mp15_dhcom_basic_defconfig
index f89c921925d..297092bd746 100644
--- a/configs/stm32mp15_dhcom_basic_defconfig
+++ b/configs/stm32mp15_dhcom_basic_defconfig
@@ -1,11 +1,4 @@
-#include <configs/stm32mp15_dhsom.config>
+#include <configs/stm32mp15_dhcom_basic.config>
-CONFIG_ARM=y
-CONFIG_ARCH_STM32MP=y
CONFIG_DEFAULT_DEVICE_TREE="st/stm32mp157c-dhcom-pdk2"
-CONFIG_SYS_MEMTEST_START=0xc0000000
-CONFIG_SYS_MEMTEST_END=0xc4000000
-CONFIG_SYS_I2C_EEPROM_BUS=3
CONFIG_OF_LIST="st/stm32mp157c-dhcom-pdk2 st/stm32mp153c-dhcom-drc02 st/stm32mp157c-dhcom-picoitx"
-CONFIG_SYS_I2C_EEPROM_ADDR=0x50
-CONFIG_BOOTCOUNT_ALTBOOTCMD="
diff --git a/configs/stm32mp15_dhcom_drc02_basic_defconfig b/configs/stm32mp15_dhcom_drc02_basic_defconfig
new file mode 100644
index 00000000000..838c3db253d
--- /dev/null
+++ b/configs/stm32mp15_dhcom_drc02_basic_defconfig
@@ -0,0 +1,4 @@
+#include <configs/stm32mp15_dhcom_basic.config>
+
+CONFIG_DEFAULT_DEVICE_TREE="st/stm32mp153c-dhcom-drc02"
+CONFIG_OF_LIST="st/stm32mp153c-dhcom-drc02"
diff --git a/configs/stm32mp15_dhcom_pdk2_basic_defconfig b/configs/stm32mp15_dhcom_pdk2_basic_defconfig
new file mode 100644
index 00000000000..c6996233c9f
--- /dev/null
+++ b/configs/stm32mp15_dhcom_pdk2_basic_defconfig
@@ -0,0 +1,4 @@
+#include <configs/stm32mp15_dhcom_basic.config>
+
+CONFIG_DEFAULT_DEVICE_TREE="st/stm32mp157c-dhcom-pdk2"
+CONFIG_OF_LIST="st/stm32mp157c-dhcom-pdk2"
diff --git a/configs/stm32mp15_dhcom_picoitx_basic_defconfig b/configs/stm32mp15_dhcom_picoitx_basic_defconfig
new file mode 100644
index 00000000000..5682edbfcbf
--- /dev/null
+++ b/configs/stm32mp15_dhcom_picoitx_basic_defconfig
@@ -0,0 +1,4 @@
+#include <configs/stm32mp15_dhcom_basic.config>
+
+CONFIG_DEFAULT_DEVICE_TREE="st/stm32mp157c-dhcom-picoitx"
+CONFIG_OF_LIST="st/stm32mp157c-dhcom-picoitx"
diff --git a/configs/stm32mp15_dhcor_avenger96_basic_defconfig b/configs/stm32mp15_dhcor_avenger96_basic_defconfig
new file mode 100644
index 00000000000..5d27cd5ed7e
--- /dev/null
+++ b/configs/stm32mp15_dhcor_avenger96_basic_defconfig
@@ -0,0 +1,4 @@
+#include <configs/stm32mp15_dhcor_basic.config>
+
+CONFIG_DEFAULT_DEVICE_TREE="st/stm32mp157a-dhcor-avenger96"
+CONFIG_OF_LIST="st/stm32mp157a-dhcor-avenger96"
diff --git a/configs/stm32mp15_dhcor_basic.config b/configs/stm32mp15_dhcor_basic.config
new file mode 100644
index 00000000000..e9c0cb9f95a
--- /dev/null
+++ b/configs/stm32mp15_dhcor_basic.config
@@ -0,0 +1,8 @@
+#include <configs/stm32mp15_dhsom.config>
+
+CONFIG_ARM=y
+CONFIG_ARCH_STM32MP=y
+CONFIG_SYS_I2C_EEPROM_BUS=2
+CONFIG_SYS_I2C_EEPROM_ADDR=0x53
+CONFIG_PHY_MICREL=y
+CONFIG_PHY_MICREL_KSZ90X1=y
diff --git a/configs/stm32mp15_dhcor_basic_defconfig b/configs/stm32mp15_dhcor_basic_defconfig
index bde668761b3..beb6d1d5a9a 100644
--- a/configs/stm32mp15_dhcor_basic_defconfig
+++ b/configs/stm32mp15_dhcor_basic_defconfig
@@ -1,11 +1,4 @@
-#include <configs/stm32mp15_dhsom.config>
+#include <configs/stm32mp15_dhcor_basic.config>
-CONFIG_ARM=y
-CONFIG_ARCH_STM32MP=y
CONFIG_DEFAULT_DEVICE_TREE="st/stm32mp157a-dhcor-avenger96"
-CONFIG_SYS_I2C_EEPROM_BUS=2
CONFIG_OF_LIST="st/stm32mp157a-dhcor-avenger96 st/stm32mp151a-dhcor-testbench st/stm32mp153c-dhcor-drc-compact"
-CONFIG_SYS_I2C_EEPROM_ADDR=0x53
-CONFIG_PHY_MICREL=y
-CONFIG_PHY_MICREL_KSZ90X1=y
-CONFIG_BOOTCOUNT_ALTBOOTCMD="
diff --git a/configs/stm32mp15_dhcor_drc_compact_basic_defconfig b/configs/stm32mp15_dhcor_drc_compact_basic_defconfig
new file mode 100644
index 00000000000..7b1d73a33b5
--- /dev/null
+++ b/configs/stm32mp15_dhcor_drc_compact_basic_defconfig
@@ -0,0 +1,4 @@
+#include <configs/stm32mp15_dhcor_basic.config>
+
+CONFIG_DEFAULT_DEVICE_TREE="st/stm32mp153c-dhcor-drc-compact"
+CONFIG_OF_LIST="st/stm32mp153c-dhcor-drc-compact"
diff --git a/configs/stm32mp15_dhcor_testbench_basic_defconfig b/configs/stm32mp15_dhcor_testbench_basic_defconfig
new file mode 100644
index 00000000000..7ba327cbd82
--- /dev/null
+++ b/configs/stm32mp15_dhcor_testbench_basic_defconfig
@@ -0,0 +1,4 @@
+#include <configs/stm32mp15_dhcor_basic.config>
+
+CONFIG_DEFAULT_DEVICE_TREE="st/stm32mp151a-dhcor-testbench"
+CONFIG_OF_LIST="st/stm32mp151a-dhcor-testbench"
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 6e79868d0ef..de312656746 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -105,6 +105,14 @@ config PWM_TEGRA
32KHz clock is supported by the driver but the duty cycle is
configurable.
+config PWM_STM32
+ bool "Enable support for STM32 PWM"
+ depends on DM_PWM && MFD_STM32_TIMERS
+ help
+ This enables PWM driver for STMicroelectronics STM32 pulse width
+ modulation. It uses STM32 timer devices that can have up to 4 output
+ channels, with complementary outputs and configurable polarity.
+
config PWM_SUNXI
bool "Enable support for the Allwinner Sunxi PWM"
depends on DM_PWM
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index e4d10c8dc3e..76305b93bc9 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -22,5 +22,6 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o
obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o
obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o
+obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o
obj-$(CONFIG_PWM_TI_EHRPWM) += pwm-ti-ehrpwm.o
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c
new file mode 100644
index 00000000000..5fa649b5903
--- /dev/null
+++ b/drivers/pwm/pwm-stm32.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025, STMicroelectronics - All Rights Reserved
+ * Author: Cheick Traore <cheick.traore@foss.st.com>
+ *
+ * Originally based on the Linux kernel v6.10 drivers/pwm/pwm-stm32.c.
+ */
+
+#include <div64.h>
+#include <dm.h>
+#include <pwm.h>
+#include <asm/io.h>
+#include <asm/arch/timers.h>
+#include <dm/device_compat.h>
+#include <linux/time.h>
+
+#define CCMR_CHANNEL_SHIFT 8
+#define CCMR_CHANNEL_MASK 0xFF
+
+struct stm32_pwm_priv {
+ bool have_complementary_output;
+ bool invert_polarity;
+};
+
+static u32 active_channels(struct stm32_timers_plat *plat)
+{
+ return readl(plat->base + TIM_CCER) & TIM_CCER_CCXE;
+}
+
+static int stm32_pwm_set_config(struct udevice *dev, uint channel,
+ uint period_ns, uint duty_ns)
+{
+ struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev));
+ struct stm32_timers_priv *priv = dev_get_priv(dev_get_parent(dev));
+ unsigned long long prd, div, dty;
+ unsigned int prescaler = 0;
+ u32 ccmr, mask, shift;
+
+ if (duty_ns > period_ns)
+ return -EINVAL;
+
+ /*
+ * Period and prescaler values depends on clock rate
+ * First we need to find the minimal value for prescaler such that
+ *
+ * period_ns * clkrate
+ * ------------------------------ < max_arr + 1
+ * NSEC_PER_SEC * (prescaler + 1)
+ *
+ * This equation is equivalent to
+ *
+ * period_ns * clkrate
+ * ---------------------------- < prescaler + 1
+ * NSEC_PER_SEC * (max_arr + 1)
+ *
+ * Using integer division and knowing that the right hand side is
+ * integer, this is further equivalent to
+ *
+ * (period_ns * clkrate) // (NSEC_PER_SEC * (max_arr + 1)) ≤ prescaler
+ */
+
+ div = (unsigned long long)priv->rate * period_ns;
+ do_div(div, NSEC_PER_SEC);
+ prd = div;
+
+ do_div(div, priv->max_arr + 1);
+ prescaler = div;
+ if (prescaler > MAX_TIM_PSC)
+ return -EINVAL;
+
+ do_div(prd, prescaler + 1);
+ if (!prd)
+ return -EINVAL;
+
+ /*
+ * All channels share the same prescaler and counter so when two
+ * channels are active at the same time we can't change them
+ */
+ if (active_channels(plat) & ~(1 << channel * 4)) {
+ u32 psc, arr;
+
+ psc = readl(plat->base + TIM_PSC);
+ arr = readl(plat->base + TIM_ARR);
+ if (psc != prescaler || arr != prd - 1)
+ return -EBUSY;
+ }
+
+ writel(prescaler, plat->base + TIM_PSC);
+ writel(prd - 1, plat->base + TIM_ARR);
+ setbits_le32(plat->base + TIM_CR1, TIM_CR1_ARPE);
+
+ /* Calculate the duty cycles */
+ dty = prd * duty_ns;
+ do_div(dty, period_ns);
+
+ writel(dty, plat->base + TIM_CCRx(channel + 1));
+
+ /* Configure output mode */
+ shift = (channel & 0x1) * CCMR_CHANNEL_SHIFT;
+ ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift;
+ mask = CCMR_CHANNEL_MASK << shift;
+ if (channel < 2)
+ clrsetbits_le32(plat->base + TIM_CCMR1, mask, ccmr);
+ else
+ clrsetbits_le32(plat->base + TIM_CCMR2, mask, ccmr);
+
+ setbits_le32(plat->base + TIM_BDTR, TIM_BDTR_MOE);
+
+ return 0;
+}
+
+static int stm32_pwm_set_enable(struct udevice *dev, uint channel,
+ bool enable)
+{
+ struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev));
+ struct stm32_pwm_priv *priv = dev_get_priv(dev);
+ u32 mask;
+
+ /* Enable channel */
+ mask = TIM_CCER_CC1E << (channel * 4);
+ if (priv->have_complementary_output)
+ mask |= TIM_CCER_CC1NE << (channel * 4);
+
+ if (enable) {
+ setbits_le32(plat->base + TIM_CCER, mask);
+ /* Make sure that registers are updated */
+ setbits_le32(plat->base + TIM_EGR, TIM_EGR_UG);
+ /* Enable controller */
+ setbits_le32(plat->base + TIM_CR1, TIM_CR1_CEN);
+ } else {
+ clrbits_le32(plat->base + TIM_CCER, mask);
+ /* When all channels are disabled, we can disable the controller */
+ if (!active_channels(plat))
+ clrbits_le32(plat->base + TIM_CR1, TIM_CR1_CEN);
+ }
+
+ return 0;
+}
+
+static int stm32_pwm_set_invert(struct udevice *dev, uint channel,
+ bool polarity)
+{
+ struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev));
+ struct stm32_pwm_priv *priv = dev_get_priv(dev);
+ u32 mask;
+
+ mask = TIM_CCER_CC1P << (channel * 4);
+ if (priv->have_complementary_output)
+ mask |= TIM_CCER_CC1NP << (channel * 4);
+
+ clrsetbits_le32(plat->base + TIM_CCER, mask, polarity ? mask : 0);
+
+ return 0;
+}
+
+static void stm32_pwm_detect_complementary(struct udevice *dev)
+{
+ struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev));
+ struct stm32_pwm_priv *priv = dev_get_priv(dev);
+ u32 ccer;
+
+ /*
+ * If complementary bit doesn't exist writing 1 will have no
+ * effect so we can detect it.
+ */
+ setbits_le32(plat->base + TIM_CCER, TIM_CCER_CC1NE);
+ ccer = readl(plat->base + TIM_CCER);
+ clrbits_le32(plat->base + TIM_CCER, TIM_CCER_CC1NE);
+
+ priv->have_complementary_output = (ccer != 0);
+}
+
+static int stm32_pwm_probe(struct udevice *dev)
+{
+ struct stm32_timers_priv *timer = dev_get_priv(dev_get_parent(dev));
+
+ if (timer->rate > 1000000000) {
+ dev_err(dev, "Clock freq too high (%lu)\n", timer->rate);
+ return -EINVAL;
+ }
+
+ stm32_pwm_detect_complementary(dev);
+
+ return 0;
+}
+
+static const struct pwm_ops stm32_pwm_ops = {
+ .set_config = stm32_pwm_set_config,
+ .set_enable = stm32_pwm_set_enable,
+ .set_invert = stm32_pwm_set_invert,
+};
+
+static const struct udevice_id stm32_pwm_ids[] = {
+ { .compatible = "st,stm32-pwm" },
+ { }
+};
+
+U_BOOT_DRIVER(stm32_pwm) = {
+ .name = "stm32_pwm",
+ .id = UCLASS_PWM,
+ .of_match = stm32_pwm_ids,
+ .ops = &stm32_pwm_ops,
+ .probe = stm32_pwm_probe,
+ .priv_auto = sizeof(struct stm32_pwm_priv),
+};
diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c
index 1ee58142b3f..1675a9cb9d1 100644
--- a/drivers/serial/serial_stm32.c
+++ b/drivers/serial/serial_stm32.c
@@ -299,13 +299,19 @@ static inline struct stm32_uart_info *_debug_uart_info(void)
static inline void _debug_uart_init(void)
{
- void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE);
- struct stm32_uart_info *uart_info = _debug_uart_info();
+ void __maybe_unused __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE);
+ struct stm32_uart_info *uart_info __maybe_unused = _debug_uart_info();
- _stm32_serial_init(base, uart_info);
- _stm32_serial_setbrg(base, uart_info,
- CONFIG_DEBUG_UART_CLOCK,
- CONFIG_BAUDRATE);
+ /*
+ * debug_uart_init() is only usable when SPL_BUILD is enabled
+ * (STM32MP1 case only)
+ */
+ if (IS_ENABLED(CONFIG_DEBUG_UART) && IS_ENABLED(CONFIG_SPL_BUILD)) {
+ _stm32_serial_init(base, uart_info);
+ _stm32_serial_setbrg(base, uart_info,
+ CONFIG_DEBUG_UART_CLOCK,
+ CONFIG_BAUDRATE);
+ }
}
static inline void _debug_uart_putc(int c)