Skip to content

Commit c6e64a1

Browse files
javlandsnashif
authored andcommitted
drivers: pwm: extend API to support events
Extend the PWM API to support events, as some controllers allow interrupts if e.g., a pwm period has ended or a fault occured. Signed-off-by: Jaro Van Landschoot <jaro.vanlandschoot@basalte.be>
1 parent ff43475 commit c6e64a1

File tree

3 files changed

+247
-0
lines changed

3 files changed

+247
-0
lines changed

drivers/pwm/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ config PWM_CAPTURE
3232
This option extends the Zephyr PWM API with the ability to capture PWM
3333
period/pulse widths.
3434

35+
config PWM_EVENT
36+
bool "Provide API for PWM event [EXPERIMENTAL]"
37+
select EXPERIMENTAL
38+
help
39+
This option extends the Zephyr PWM API with the ability to configure
40+
and receive PWM events.
41+
3542
# zephyr-keep-sorted-start
3643
source "drivers/pwm/Kconfig.ambiq_timer"
3744
source "drivers/pwm/Kconfig.b91"

include/zephyr/drivers/pwm.h

Lines changed: 158 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 Basalte bv
45
*
56
* SPDX-License-Identifier: Apache-2.0
67
*/
@@ -35,6 +36,7 @@
3536
#include <zephyr/devicetree.h>
3637
#include <zephyr/sys_clock.h>
3738
#include <zephyr/sys/math_extras.h>
39+
#include <zephyr/sys/slist.h>
3840
#include <zephyr/toolchain.h>
3941

4042
#include <zephyr/dt-bindings/pwm/pwm.h>
@@ -75,6 +77,27 @@ extern "C" {
7577

7678
/** @} */
7779

80+
/**
81+
* @name PWM event types
82+
* @anchor PWM_EVENT_TYPES
83+
* @{
84+
*/
85+
86+
#define PWM_EVENT_TYPE_SHIFT 0U
87+
88+
/** Configure the event to trigger at the end of each PWM period */
89+
#define PWM_EVENT_TYPE_PERIOD (1U << PWM_EVENT_TYPE_SHIFT)
90+
91+
/** Configure the event to trigger at a fault. These can be used to
92+
* react to error events that caused a change of behaviour of the PWM
93+
* peripheral (often disabling the output). These error events can have
94+
* an internal (e.g., oscillator malfunction) or external (e.g., pio)
95+
* source
96+
*/
97+
#define PWM_EVENT_TYPE_FAULT (2U << PWM_EVENT_TYPE_SHIFT)
98+
99+
/** @} */
100+
78101
/**
79102
* @brief Provides a type to hold PWM configuration flags.
80103
*
@@ -86,6 +109,14 @@ extern "C" {
86109

87110
typedef uint16_t pwm_flags_t;
88111

112+
/**
113+
* @brief Provides a type to hold PWM events.
114+
*
115+
* @see @ref PWM_EVENT_TYPES.
116+
*/
117+
118+
typedef uint16_t pwm_events_t;
119+
89120
/**
90121
* @brief Container for PWM information specified in devicetree.
91122
*
@@ -400,6 +431,52 @@ typedef void (*pwm_capture_callback_handler_t)(const struct device *dev,
400431
uint32_t pulse_cycles,
401432
int status, void *user_data);
402433

434+
struct pwm_event_callback;
435+
436+
/**
437+
* @brief PWM event callback handler function signature.
438+
*
439+
* @note The callback handler can be called in an interrupt context.
440+
*
441+
* @note @kconfig{CONFIG_PWM_EVENT} must be selected to enable PWM event
442+
* support.
443+
*
444+
* @param[in] dev PWM device instance.
445+
* @param callback Original struct gpio_callback owning this handler.
446+
* @param channel PWM channel.
447+
* @param events Event mask. See @ref PWM_EVENT_TYPES.
448+
*
449+
*/
450+
typedef void (*pwm_event_callback_handler_t)(const struct device *dev,
451+
struct pwm_event_callback *callback, uint32_t channel,
452+
pwm_events_t events);
453+
454+
/**
455+
* @brief PWM event callback structure
456+
*
457+
* Used to register an event callback in the driver instance callback list.
458+
* As many callbacks as needed can be added as long as each of them
459+
* are unique pointers of struct pwm_event_callback.
460+
* Beware such structure should not be allocated on stack.
461+
*
462+
* Note: to help setting it, see pwm_init_event_callback().
463+
*
464+
*/
465+
struct pwm_event_callback {
466+
/** @cond INTERNAL_HIDDEN */
467+
sys_snode_t node;
468+
/** @endcond */
469+
470+
/** Actual callback function being called when relevant. */
471+
pwm_event_callback_handler_t handler;
472+
473+
/** Channel the callback is interested in. */
474+
uint32_t channel;
475+
476+
/** A mask of events the callback is interested in. */
477+
pwm_events_t event_mask;
478+
};
479+
403480
/** @cond INTERNAL_HIDDEN */
404481
/**
405482
* @brief PWM driver API call to configure PWM pin period and pulse width.
@@ -440,6 +517,16 @@ typedef int (*pwm_disable_capture_t)(const struct device *dev,
440517
uint32_t channel);
441518
#endif /* CONFIG_PWM_CAPTURE */
442519

520+
#ifdef CONFIG_PWM_EVENT
521+
522+
/**
523+
* @brief PWM driver API to manage callbacks for events.
524+
* @see pwm_set_event_callback() and pwm_remove_event_callback() for argument description.
525+
*/
526+
typedef int (*pwm_manage_event_callback_t)(const struct device *dev,
527+
struct pwm_event_callback *callback, bool set);
528+
#endif /* CONFIG_PWM_EVENT */
529+
443530
/** @brief PWM driver API definition. */
444531
__subsystem struct pwm_driver_api {
445532
pwm_set_cycles_t set_cycles;
@@ -449,6 +536,9 @@ __subsystem struct pwm_driver_api {
449536
pwm_enable_capture_t enable_capture;
450537
pwm_disable_capture_t disable_capture;
451538
#endif /* CONFIG_PWM_CAPTURE */
539+
#ifdef CONFIG_PWM_EVENT
540+
pwm_manage_event_callback_t manage_event_callback;
541+
#endif /* CONFIG_PWM_EVENT */
452542
};
453543
/** @endcond */
454544

@@ -933,6 +1023,74 @@ static inline int pwm_capture_nsec(const struct device *dev, uint32_t channel,
9331023
return 0;
9341024
}
9351025

1026+
#if defined(CONFIG_PWM_EVENT) || defined(__DOXYGEN__)
1027+
/**
1028+
* @brief Helper to initialize a struct pwm_event_callback properly.
1029+
*
1030+
* @param callback A valid application's callback structure pointer.
1031+
* @param handler A valid handler function pointer.
1032+
* @param channel Relevant channel for the handler.
1033+
* @param event_mask A bit mask of relevant events for the handler.
1034+
*/
1035+
static inline void pwm_init_event_callback(struct pwm_event_callback *callback,
1036+
pwm_event_callback_handler_t handler, uint32_t channel,
1037+
pwm_events_t event_mask)
1038+
{
1039+
__ASSERT_NO_MSG(callback != NULL);
1040+
__ASSERT_NO_MSG(handler != NULL);
1041+
1042+
callback->handler = handler;
1043+
callback->channel = channel;
1044+
callback->event_mask = event_mask;
1045+
}
1046+
1047+
/**
1048+
* @brief Add an application event callback.
1049+
*
1050+
* @note As many callbacks as needed can be added on a PWM device instance.
1051+
*
1052+
* @param[in] dev PWM device instance.
1053+
* @param callback A valid applications callback structure pointer.
1054+
*
1055+
* @retval 0 on success.
1056+
* @retval -ENOSYS if driver does not manage event callbacks.
1057+
* @retval negative errno on failure.
1058+
*/
1059+
static inline int pwm_add_event_callback(const struct device *dev,
1060+
struct pwm_event_callback *callback)
1061+
{
1062+
const struct pwm_driver_api *api = (const struct pwm_driver_api *)dev->api;
1063+
1064+
if (api->manage_event_callback == NULL) {
1065+
return -ENOSYS;
1066+
}
1067+
1068+
return api->manage_event_callback(dev, callback, true);
1069+
}
1070+
1071+
/**
1072+
* @brief Remove an application event callback.
1073+
*
1074+
* @param[in] dev PWM device instance.
1075+
* @param callback A valid applications callback structure pointer.
1076+
*
1077+
* @retval 0 on success.
1078+
* @retval -ENOSYS if driver does not manage event callbacks.
1079+
* @retval negative errno on failure.
1080+
*/
1081+
static inline int pwm_remove_event_callback(const struct device *dev,
1082+
struct pwm_event_callback *callback)
1083+
{
1084+
const struct pwm_driver_api *api = (const struct pwm_driver_api *)dev->api;
1085+
1086+
if (api->manage_event_callback == NULL) {
1087+
return -ENOSYS;
1088+
}
1089+
1090+
return api->manage_event_callback(dev, callback, false);
1091+
}
1092+
#endif /* defined(CONFIG_PWM_EVENT) || defined(__DOXYGEN__) */
1093+
9361094
/**
9371095
* @brief Validate that the PWM device is ready.
9381096
*
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2025 Basalte bv
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_DRIVERS_PWM_PWM_UTILS_H_
8+
#define ZEPHYR_INCLUDE_DRIVERS_PWM_PWM_UTILS_H_
9+
10+
#include <stdbool.h>
11+
#include <stdint.h>
12+
#include <errno.h>
13+
14+
#include <zephyr/devicetree.h>
15+
#include <zephyr/drivers/pwm.h>
16+
#include <zephyr/sys/__assert.h>
17+
#include <zephyr/sys/slist.h>
18+
19+
#ifdef __cplusplus
20+
extern "C" {
21+
#endif
22+
23+
/**
24+
* @brief Generic function to insert or remove a callback from a callback list.
25+
*
26+
* @param callbacks A pointer to the original list of callbacks (can be NULL).
27+
* @param callback A pointer of the callback to insert or remove from the list.
28+
* @param set A boolean indicating insertion or removal of the callback.
29+
*
30+
* @retval 0 on success.
31+
* @retval negate errno on failure.
32+
*/
33+
static inline int pwm_manage_event_callback(sys_slist_t *callbacks,
34+
struct pwm_event_callback *callback, bool set)
35+
{
36+
__ASSERT_NO_MSG(callback != NULL);
37+
__ASSERT_NO_MSG(callback->handler != NULL);
38+
39+
if (!sys_slist_is_empty(callbacks)) {
40+
if (!sys_slist_find_and_remove(callbacks, &callback->node)) {
41+
if (!set) {
42+
return -EINVAL;
43+
}
44+
}
45+
} else if (!set) {
46+
return -EINVAL;
47+
}
48+
49+
if (set) {
50+
sys_slist_prepend(callbacks, &callback->node);
51+
}
52+
53+
return 0;
54+
}
55+
56+
/**
57+
* @brief Generic function to go through and fire callback from a callback lists.
58+
*
59+
* @param list A pointer on the pwm event callback list.
60+
* @param dev A pointer on the pwm driver instance.
61+
* @param channel The channel that fired the interrupt.
62+
* @param events The event that fired the interrupt.
63+
*/
64+
static inline void pwm_fire_event_callbacks(sys_slist_t *list, const struct device *dev,
65+
uint32_t channel, pwm_events_t events)
66+
{
67+
struct pwm_event_callback *cb, *tmp;
68+
69+
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(list, cb, tmp, node) {
70+
if (cb->channel == channel) {
71+
__ASSERT_NO_MSG(cb->handler != NULL);
72+
73+
cb->handler(dev, cb, channel, events);
74+
}
75+
}
76+
}
77+
78+
#ifdef __cplusplus
79+
}
80+
#endif
81+
82+
#endif /* ZEPHYR_INCLUDE_DRIVERS_PWM_PWM_UTILS_H_ */

0 commit comments

Comments
 (0)