- 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.
 -----BEGIN PGP SIGNATURE-----
 
 iQJQBAABCgA6FiEEXyrViUccKBz9c35Jysd4L3sz/6YFAmfRsL4cHHBhdHJpY2Uu
 Y2hvdGFyZEBmb3NzLnN0LmNvbQAKCRDKx3gvezP/psU2D/9LPdhwyvmi595AgjNf
 0t4rqKKTPhjb43UZeuAnFOg4PJcH7eXd2tQq5htK+ir8jeLU7cZWRGjA6eXtRQni
 IKYtWlz4OY/xuQhJbQqMBGb1EVJE432LlfVp3rKpCpBMoANU+h//opd/m4CKsMYN
 SiIikf3Xasgy7APSGTQcNGZyzVcVPeL8e68hEAOuFfCBMmd4JRb7RMwro9mG+/SU
 9PzOLvCEfNTq9k87vT8SH4UQHv/NF8OaH6v77SJFt+RHdQr/G9MNbdP5IVwuWkgU
 CfcWRFzmofFACueZ/6vA+hFYarF5tsv043GJyJkTh/0CUbFx8vS0+x1WoL7YcviA
 5Ro+tEEfpB1F1qLyOJV698l1vm3Zj452oZ6e2PBPMGqfKy1vkBjh9OjhfqpjILHX
 uAkxxd/dx6FzK8jo4Qki7JT4W2MmD3jTeHsVunLOC1tS1f44MHwnjpZZH2ttXT/4
 VLPkIYf9kKJA/squ0Lx+ZCjFtiM/XenWHhQxsO9QxKUqSHgBZuwBj8VRNRrMs/N+
 3mN4PLNuYtagJjCvvsxiMg0aB7LH22kXA657nQfHM01WlR9I/B2K0aIil/I/kVV9
 b9LtUven4sXUEexszEY/xFxF2utU+Xh57wKh+3qm2XVw8pfSKItvBXBK/lo/ULWV
 62xL179ziFFGrE/2o/m87VT30w==
 =xL/c
 -----END PGP SIGNATURE-----

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.
This commit is contained in:
Tom Rini 2025-03-12 21:36:52 -06:00
commit eeefcacb85
35 changed files with 475 additions and 97 deletions

View File

@ -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 {

View File

@ -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 {

View File

@ -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>;

View File

@ -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 */

View File

@ -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>;
};

View File

@ -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;

View File

@ -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>;
};
};

View File

@ -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"

View File

@ -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/

View File

@ -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

View File

@ -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;
}

View File

@ -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,
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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="

View File

@ -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

View File

@ -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="

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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="

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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

205
drivers/pwm/pwm-stm32.c Normal file
View File

@ -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),
};

View File

@ -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();
/*
* 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)