Skip to content

Commit 2f77f50

Browse files
committed
drivers: pwm: add support for enabling DMA requests
Extend the PWM API with optional API functions for enabling DMA requests Possible solution for #88670 Signed-off-by: Vincent Surkijn <vincent.surkijn@siemens.com>
1 parent e7c454c commit 2f77f50

File tree

4 files changed

+159
-1
lines changed

4 files changed

+159
-1
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
@@ -39,6 +40,12 @@ config PWM_EVENT
3940
This option extends the Zephyr PWM API with the ability to configure
4041
and receive PWM events.
4142

43+
config PWM_WITH_DMA
44+
bool "Provide API for using PWM with DMA"
45+
help
46+
This option extends the Zephyr PWM API with the ability to trigger DMA
47+
requests from PWM channels.
48+
4249
# zephyr-keep-sorted-start
4350
source "drivers/pwm/Kconfig.ambiq_timer"
4451
source "drivers/pwm/Kconfig.b91"

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
*/
@@ -193,6 +194,18 @@ static void __maybe_unused (*const clear_capture_interrupt[])(TIM_TypeDef *) = {
193194
LL_TIM_ClearFlag_CC3, LL_TIM_ClearFlag_CC4
194195
};
195196

197+
/* Channel to enable DMA request flag mapping. */
198+
static void __maybe_unused (*const enable_dma_interrupt[])(TIM_TypeDef *) = {
199+
LL_TIM_EnableDMAReq_CC1, LL_TIM_EnableDMAReq_CC2,
200+
LL_TIM_EnableDMAReq_CC3, LL_TIM_EnableDMAReq_CC4
201+
};
202+
203+
/* Channel to disable DMA request flag mapping. */
204+
static void __maybe_unused (*const disable_dma_interrupt[])(TIM_TypeDef *) = {
205+
LL_TIM_DisableDMAReq_CC1, LL_TIM_DisableDMAReq_CC2,
206+
LL_TIM_DisableDMAReq_CC3, LL_TIM_DisableDMAReq_CC4
207+
};
208+
196209
/**
197210
* Obtain LL polarity from PWM flags.
198211
*
@@ -630,6 +643,51 @@ static void pwm_stm32_isr(const struct device *dev)
630643

631644
#endif /* CONFIG_PWM_CAPTURE */
632645

646+
#ifdef CONFIG_PWM_WITH_DMA
647+
static int pwm_stm32_enable_dma(const struct device *dev,
648+
uint32_t channel)
649+
{
650+
const struct pwm_stm32_config *cfg = dev->config;
651+
652+
/* DMA requests are only supported on Capture/Compare channels.
653+
* However, these DMA request can also be used in PWM output mode to
654+
* dynamically update the duty cycle once per period. Therefore, enabling
655+
* or disabling DMA requests on PWM channels should not be limited to
656+
* Capture/Compare driver functions.
657+
*/
658+
if ((channel < 1u) || (channel > 4u)) {
659+
LOG_ERR("DMA for PWM only exists on channels 1, 2, 3 and 4.");
660+
return -ENOTSUP;
661+
}
662+
663+
enable_dma_interrupt[channel - 1](cfg->timer);
664+
665+
return 0;
666+
}
667+
668+
static int pwm_stm32_disable_dma(const struct device *dev,
669+
uint32_t channel)
670+
{
671+
const struct pwm_stm32_config *cfg = dev->config;
672+
673+
/* DMA requests are only supported on Capture/Compare channels.
674+
* However, these DMA requests can also be used in PWM output mode to
675+
* dynamically update the duty cycle once per period. Therefore, enabling
676+
* or disabling DMA requests on PWM channels should not be limited to
677+
* Capture/Compare driver functions.
678+
*/
679+
if ((channel < 1u) || (channel > 4u)) {
680+
LOG_ERR("DMA for PWM only exists on channels 1, 2, 3 and 4.");
681+
return -ENOTSUP;
682+
}
683+
684+
disable_dma_interrupt[channel - 1](cfg->timer);
685+
686+
return 0;
687+
}
688+
689+
#endif /* CONFIG_PWM_WITH_DMA */
690+
633691
static int pwm_stm32_get_cycles_per_sec(const struct device *dev,
634692
uint32_t channel, uint64_t *cycles)
635693
{
@@ -649,6 +707,10 @@ static DEVICE_API(pwm, pwm_stm32_driver_api) = {
649707
.enable_capture = pwm_stm32_enable_capture,
650708
.disable_capture = pwm_stm32_disable_capture,
651709
#endif /* CONFIG_PWM_CAPTURE */
710+
#ifdef CONFIG_PWM_WITH_DMA
711+
.enable_dma = pwm_stm32_enable_dma,
712+
.disable_dma = pwm_stm32_disable_dma,
713+
#endif /* CONFIG_PWM_WITH_DMA */
652714
};
653715

654716
static int pwm_stm32_init(const struct device *dev)

include/zephyr/drivers/pwm.h

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Copyright (c) 2016 Intel Corporation.
33
* Copyright (c) 2020-2021 Vestas Wind Systems A/S
44
* Copyright (c) 2025 Basalte bv
5+
* Copyright (c) 2025 Siemens SA
56
*
67
* SPDX-License-Identifier: Apache-2.0
78
*/
@@ -19,7 +20,7 @@
1920
* @brief Interfaces for Pulse Width Modulation (PWM) controllers.
2021
* @defgroup pwm_interface PWM
2122
* @since 1.0
22-
* @version 1.0.0
23+
* @version 1.1.0
2324
* @ingroup io_interfaces
2425
* @{
2526
*
@@ -527,6 +528,20 @@ typedef int (*pwm_manage_event_callback_t)(const struct device *dev,
527528
struct pwm_event_callback *callback, bool set);
528529
#endif /* CONFIG_PWM_EVENT */
529530

531+
#ifdef CONFIG_PWM_WITH_DMA
532+
/**
533+
* @brief PWM driver API call to enable PWM DMA requests.
534+
* @see pwm_enable_dma() for argument description
535+
*/
536+
typedef int (*pwm_enable_dma_t)(const struct device *dev, uint32_t channel);
537+
538+
/**
539+
* @brief PWM driver API call to disable PWM DMA requests.
540+
* @see pwm_disable_dma() for argument description
541+
*/
542+
typedef int (*pwm_disable_dma_t)(const struct device *dev, uint32_t channel);
543+
#endif /* CONFIG_PWM_WITH_DMA */
544+
530545
/** @brief PWM driver API definition. */
531546
__subsystem struct pwm_driver_api {
532547
pwm_set_cycles_t set_cycles;
@@ -539,6 +554,10 @@ __subsystem struct pwm_driver_api {
539554
#ifdef CONFIG_PWM_EVENT
540555
pwm_manage_event_callback_t manage_event_callback;
541556
#endif /* CONFIG_PWM_EVENT */
557+
#ifdef CONFIG_PWM_WITH_DMA
558+
pwm_enable_dma_t enable_dma;
559+
pwm_disable_dma_t disable_dma;
560+
#endif /* CONFIG_PWM_WITH_DMA */
542561
};
543562
/** @endcond */
544563

@@ -882,6 +901,56 @@ static inline int z_impl_pwm_disable_capture(const struct device *dev,
882901
}
883902
#endif /* CONFIG_PWM_CAPTURE */
884903

904+
#if defined(CONFIG_PWM_WITH_DMA) || defined(__DOXYGEN__)
905+
/**
906+
* @brief Enable DMA requests triggered by PWM cycles for a single PWM channel.
907+
*
908+
* @param[in] dev PWM device instance.
909+
* @param channel PWM channel.
910+
*
911+
* @retval 0 If successful.
912+
* @retval -EINVAL if invalid function parameters were given
913+
* @retval -ENOSYS if DMA for PWM is not supported
914+
* @retval -ENOTSUP if the PWM channel does not support DMA
915+
*/
916+
__syscall int pwm_enable_dma(const struct device *dev, uint32_t channel);
917+
918+
static inline int z_impl_pwm_enable_dma(const struct device *dev, uint32_t channel)
919+
{
920+
const struct pwm_driver_api *api = (const struct pwm_driver_api *)dev->api;
921+
922+
if (api->enable_dma == NULL) {
923+
return -ENOSYS;
924+
}
925+
926+
return api->enable_dma(dev, channel);
927+
}
928+
929+
/**
930+
* @brief Disable DMA requests triggered by PWM cycles for a single PWM channel.
931+
*
932+
* @param[in] dev PWM device instance.
933+
* @param channel PWM channel.
934+
*
935+
* @retval 0 If successful.
936+
* @retval -EINVAL if invalid function parameters were given
937+
* @retval -ENOSYS if DMA for PWM is not supported
938+
* @retval -ENOTSUP if the PWM channel does not support DMA
939+
*/
940+
__syscall int pwm_disable_dma(const struct device *dev, uint32_t channel);
941+
942+
static inline int z_impl_pwm_disable_dma(const struct device *dev, uint32_t channel)
943+
{
944+
const struct pwm_driver_api *api = (const struct pwm_driver_api *)dev->api;
945+
946+
if (api->disable_dma == NULL) {
947+
return -ENOSYS;
948+
}
949+
950+
return api->disable_dma(dev, channel);
951+
}
952+
#endif /* CONFIG_PWM_WITH_DMA */
953+
885954
/**
886955
* @brief Capture a single PWM period/pulse width in clock cycles for a single
887956
* PWM input.

0 commit comments

Comments
 (0)