Skip to content

Commit fa368da

Browse files
committed
drivers:pwm: add support for enabling DMA requests
Extend the PWM API with optional API functions for enabling DMA requests triggered by a given PWM channel. Possible solution for #88670 Signed-off-by: Vincent Surkijn <[email protected]>
1 parent ab448a8 commit fa368da

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed

drivers/pwm/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# PWM configuration options
22

33
# Copyright (c) 2015 Intel Corporation
4+
# Copyright (c) 2025 Siemens SA
45
# SPDX-License-Identifier: Apache-2.0
56

67
menuconfig PWM
@@ -32,6 +33,12 @@ config PWM_CAPTURE
3233
This option extends the Zephyr PWM API with the ability to capture PWM
3334
period/pulse widths.
3435

36+
config PWM_WITH_DMA
37+
bool "Provide API for using PWM with DMA"
38+
help
39+
This option extends the Zephyr PWM API with the ability to trigger DMA
40+
requests from PWM channels.
41+
3542
source "drivers/pwm/Kconfig.b91"
3643

3744
source "drivers/pwm/Kconfig.cc13xx_cc26xx_timer"

drivers/pwm/pwm_handlers.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Copyright (c) 2017 Intel Corporation
33
* Copyright (c) 2020-2021 Vestas Wind Systems A/S
4+
* Copyright (c) 2025 Siemens SA
45
*
56
* SPDX-License-Identifier: Apache-2.0
67
*/
@@ -78,3 +79,22 @@ static inline int z_vrfy_pwm_capture_cycles(const struct device *dev,
7879
#include <zephyr/syscalls/pwm_capture_cycles_mrsh.c>
7980

8081
#endif /* CONFIG_PWM_CAPTURE */
82+
83+
#ifdef CONFIG_PWM_WITH_DMA
84+
static inline int z_vrfy_pwm_enable_dma(const struct device *dev,
85+
uint32_t channel)
86+
{
87+
K_OOPS(K_SYSCALL_DRIVER_PWM(dev, enable_dma));
88+
return z_impl_pwm_enable_dma((const struct device *)dev, channel);
89+
}
90+
#include <zephyr/syscalls/pwm_enable_dma_mrsh.c>
91+
92+
static inline int z_vrfy_pwm_disable_dma(const struct device *dev,
93+
uint32_t channel)
94+
{
95+
K_OOPS(K_SYSCALL_DRIVER_PWM(dev, disable_dma));
96+
return z_impl_pwm_disable_dma((const struct device *)dev, channel);
97+
}
98+
#include <zephyr/syscalls/pwm_disable_dma_mrsh.c>
99+
100+
#endif /* CONFIG_PWM_WITH_DMA */

drivers/pwm/pwm_stm32.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Copyright (c) 2016 Linaro Limited.
33
* Copyright (c) 2020 Teslabs Engineering S.L.
44
* Copyright (c) 2023 Nobleo Technology
5+
* Copyright (c) 2025 Siemens SA
56
*
67
* SPDX-License-Identifier: Apache-2.0
78
*/
@@ -182,6 +183,18 @@ static void __maybe_unused (*const clear_capture_interrupt[])(TIM_TypeDef *) = {
182183
LL_TIM_ClearFlag_CC3, LL_TIM_ClearFlag_CC4
183184
};
184185

186+
/** Channel to enable DMA request flag mapping. */
187+
static void __maybe_unused (*const enable_dma_interrupt[])(TIM_TypeDef *) = {
188+
LL_TIM_EnableDMAReq_CC1, LL_TIM_EnableDMAReq_CC2,
189+
LL_TIM_EnableDMAReq_CC3, LL_TIM_EnableDMAReq_CC4
190+
};
191+
192+
/** Channel to disable DMA request flag mapping. */
193+
static void __maybe_unused (*const disable_dma_interrupt[])(TIM_TypeDef *) = {
194+
LL_TIM_DisableDMAReq_CC1, LL_TIM_DisableDMAReq_CC2,
195+
LL_TIM_DisableDMAReq_CC3, LL_TIM_DisableDMAReq_CC4
196+
};
197+
185198
/**
186199
* Obtain LL polarity from PWM flags.
187200
*
@@ -756,6 +769,51 @@ static void pwm_stm32_isr(const struct device *dev)
756769

757770
#endif /* CONFIG_PWM_CAPTURE */
758771

772+
#ifdef CONFIG_PWM_WITH_DMA
773+
static int pwm_stm32_enable_dma(const struct device *dev,
774+
uint32_t channel)
775+
{
776+
const struct pwm_stm32_config *cfg = dev->config;
777+
778+
/* DMA requests are only supported on Capture/Compare channels.
779+
* However, these DMA request can also be used in PWM output mode to
780+
* dynamically update the duty cycle once per period. Therefore, enabling
781+
* or disabling DMA requests on PWM channels should not be limited to
782+
* Capture/Compare driver functions.
783+
*/
784+
if ((channel < 1u) || (channel > 4u)) {
785+
LOG_ERR("DMA for PWM only exists on channels 1, 2, 3 and 4.");
786+
return -ENOTSUP;
787+
}
788+
789+
enable_dma_interrupt[channel - 1](cfg->timer);
790+
791+
return 0;
792+
}
793+
794+
static int pwm_stm32_disable_dma(const struct device *dev,
795+
uint32_t channel)
796+
{
797+
const struct pwm_stm32_config *cfg = dev->config;
798+
799+
/* DMA requests are only supported on Capture/Compare channels.
800+
* However, these DMA requests can also be used in PWM output mode to
801+
* dynamically update the duty cycle once per period. Therefore, enabling
802+
* or disabling DMA requests on PWM channels should not be limited to
803+
* Capture/Compare driver functions.
804+
*/
805+
if ((channel < 1u) || (channel > 4u)) {
806+
LOG_ERR("DMA for PWM only exists on channels 1, 2, 3 and 4.");
807+
return -ENOTSUP;
808+
}
809+
810+
disable_dma_interrupt[channel - 1](cfg->timer);
811+
812+
return 0;
813+
}
814+
815+
#endif /* CONFIG_PWM_WITH_DMA */
816+
759817
static int pwm_stm32_get_cycles_per_sec(const struct device *dev,
760818
uint32_t channel, uint64_t *cycles)
761819
{
@@ -775,6 +833,10 @@ static DEVICE_API(pwm, pwm_stm32_driver_api) = {
775833
.enable_capture = pwm_stm32_enable_capture,
776834
.disable_capture = pwm_stm32_disable_capture,
777835
#endif /* CONFIG_PWM_CAPTURE */
836+
#ifdef CONFIG_PWM_WITH_DMA
837+
.enable_dma = pwm_stm32_enable_dma,
838+
.disable_dma = pwm_stm32_disable_dma,
839+
#endif /* CONFIG_PWM_WITH_DMA */
778840
};
779841

780842
static int pwm_stm32_init(const struct device *dev)

include/zephyr/drivers/pwm.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Copyright (c) 2016 Intel Corporation.
33
* Copyright (c) 2020-2021 Vestas Wind Systems A/S
4+
* Copyright (c) 2025 Siemens SA
45
*
56
* SPDX-License-Identifier: Apache-2.0
67
*/
@@ -434,6 +435,22 @@ typedef int (*pwm_disable_capture_t)(const struct device *dev,
434435
uint32_t channel);
435436
#endif /* CONFIG_PWM_CAPTURE */
436437

438+
#ifdef CONFIG_PWM_WITH_DMA
439+
/**
440+
* @brief PWM driver API call to enable PWM DMA requests.
441+
* @see pwm_enable_dma() for argument description
442+
*/
443+
typedef int (*pwm_enable_dma_t)(const struct device *dev,
444+
uint32_t channel);
445+
446+
/**
447+
* @brief PWM driver API call to disable PWM DMA requests.
448+
* @see pwm_disable_dma() for argument description
449+
*/
450+
typedef int (*pwm_disable_dma_t)(const struct device *dev,
451+
uint32_t channel);
452+
#endif /* CONFIG_PWM_WITH_DMA */
453+
437454
/** @brief PWM driver API definition. */
438455
__subsystem struct pwm_driver_api {
439456
pwm_set_cycles_t set_cycles;
@@ -443,6 +460,10 @@ __subsystem struct pwm_driver_api {
443460
pwm_enable_capture_t enable_capture;
444461
pwm_disable_capture_t disable_capture;
445462
#endif /* CONFIG_PWM_CAPTURE */
463+
#ifdef CONFIG_PWM_WITH_DMA
464+
pwm_enable_dma_t enable_dma;
465+
pwm_disable_dma_t disable_dma;
466+
#endif /* CONFIG_PWM_WITH_DMA */
446467
};
447468
/** @endcond */
448469

@@ -786,6 +807,60 @@ static inline int z_impl_pwm_disable_capture(const struct device *dev,
786807
}
787808
#endif /* CONFIG_PWM_CAPTURE */
788809

810+
#ifdef CONFIG_PWM_WITH_DMA
811+
/**
812+
* @brief Enable DMA requests triggered by PWM cycles for a single PWM channel.
813+
*
814+
* @param[in] dev PWM device instance.
815+
* @param channel PWM channel.
816+
*
817+
* @retval 0 If successful.
818+
* @retval -EINVAL if invalid function parameters were given
819+
* @retval -ENOSYS if DMA for PWM is not supported
820+
* @retval -ENOTSUP if the PWM channel does not support DMA
821+
*/
822+
__syscall int pwm_enable_dma(const struct device *dev, uint32_t channel);
823+
824+
static inline int z_impl_pwm_enable_dma(const struct device *dev,
825+
uint32_t channel)
826+
{
827+
const struct pwm_driver_api *api =
828+
(const struct pwm_driver_api *)dev->api;
829+
830+
if (api->enable_dma == NULL) {
831+
return -ENOSYS;
832+
}
833+
834+
return api->enable_dma(dev, channel);
835+
}
836+
837+
/**
838+
* @brief Disable DMA requests triggered by PWM cycles for a single PWM channel.
839+
*
840+
* @param[in] dev PWM device instance.
841+
* @param channel PWM channel.
842+
*
843+
* @retval 0 If successful.
844+
* @retval -EINVAL if invalid function parameters were given
845+
* @retval -ENOSYS if DMA for PWM is not supported
846+
* @retval -ENOTSUP if the PWM channel does not support DMA
847+
*/
848+
__syscall int pwm_disable_dma(const struct device *dev, uint32_t channel);
849+
850+
static inline int z_impl_pwm_disable_dma(const struct device *dev,
851+
uint32_t channel)
852+
{
853+
const struct pwm_driver_api *api =
854+
(const struct pwm_driver_api *)dev->api;
855+
856+
if (api->disable_dma == NULL) {
857+
return -ENOSYS;
858+
}
859+
860+
return api->disable_dma(dev, channel);
861+
}
862+
#endif /* CONFIG_PWM_WITH_DMA */
863+
789864
/**
790865
* @brief Capture a single PWM period/pulse width in clock cycles for a single
791866
* PWM input.

0 commit comments

Comments
 (0)