11/*
2- * Copyright (c) 2024, NXP
2+ * Copyright (c) 2024-2025 NXP
33 *
44 * SPDX-License-Identifier: Apache-2.0
55 */
88
99#include <errno.h>
1010#include <zephyr/drivers/pwm.h>
11+ #include <zephyr/irq.h>
1112#include <fsl_qtmr.h>
1213#include <fsl_clock.h>
1314#include <zephyr/drivers/pinctrl.h>
@@ -26,10 +27,29 @@ struct pwm_mcux_qtmr_config {
2627 const struct pinctrl_dev_config * pincfg ;
2728 const struct device * clock_dev ;
2829 clock_control_subsys_t clock_subsys ;
30+ #ifdef CONFIG_PWM_CAPTURE
31+ void (* irq_config_func )(const struct device * dev );
32+ #endif /* CONFIG_PWM_CAPTURE */
2933};
3034
35+ #ifdef CONFIG_PWM_CAPTURE
36+ struct pwm_mcux_qtmr_capture_data {
37+ pwm_capture_callback_handler_t callback ;
38+ void * user_data ;
39+ uint32_t overflow_count ;
40+ uint32_t channel ;
41+ bool continuous : 1 ;
42+ bool overflowed : 1 ;
43+ bool pulse_capture : 1 ;
44+ bool first_edge_captured : 1 ;
45+ };
46+ #endif /* CONFIG_PWM_CAPTURE */
47+
3148struct pwm_mcux_qtmr_data {
3249 struct k_mutex lock ;
50+ #ifdef CONFIG_PWM_CAPTURE
51+ struct pwm_mcux_qtmr_capture_data capture ;
52+ #endif /* CONFIG_PWM_CAPTURE */
3353};
3454
3555static int mcux_qtmr_pwm_set_cycles (const struct device * dev , uint32_t channel ,
@@ -124,6 +144,198 @@ static int mcux_qtmr_pwm_get_cycles_per_sec(const struct device *dev, uint32_t c
124144
125145 return 0 ;
126146}
147+ #ifdef CONFIG_PWM_CAPTURE
148+ static inline bool mcux_qtmr_channel_is_active (const struct device * dev , uint32_t channel )
149+ {
150+ const struct pwm_mcux_qtmr_config * config = dev -> config ;
151+
152+ return (config -> base -> CHANNEL [channel ].CTRL & TMR_CTRL_CM_MASK ) != 0U ;
153+ }
154+
155+ static int mcux_qtmr_configure_capture (const struct device * dev ,
156+ uint32_t channel , pwm_flags_t flags ,
157+ pwm_capture_callback_handler_t cb ,
158+ void * user_data )
159+ {
160+ const struct pwm_mcux_qtmr_config * config = dev -> config ;
161+ struct pwm_mcux_qtmr_data * data = dev -> data ;
162+ bool inverted = (flags & PWM_POLARITY_MASK ) == PWM_POLARITY_INVERTED ;
163+
164+ if (channel >= CHANNEL_COUNT ) {
165+ LOG_ERR ("invalid channel %d" , channel );
166+ return - EINVAL ;
167+ }
168+
169+ if (mcux_qtmr_channel_is_active (dev , channel )) {
170+ LOG_ERR ("pwm capture in progress" );
171+ return - EBUSY ;
172+ }
173+
174+ if (!(flags & PWM_CAPTURE_TYPE_MASK )) {
175+ LOG_ERR ("No capture type specified" );
176+ return - EINVAL ;
177+ }
178+
179+ if ((flags & PWM_CAPTURE_TYPE_MASK ) == PWM_CAPTURE_TYPE_BOTH ) {
180+ LOG_ERR ("Cannot capture both period and pulse width" );
181+ return - ENOTSUP ;
182+ }
183+
184+ data -> capture .callback = cb ;
185+ data -> capture .user_data = user_data ;
186+ data -> capture .channel = channel ;
187+ data -> capture .continuous =
188+ (flags & PWM_CAPTURE_MODE_MASK ) == PWM_CAPTURE_MODE_CONTINUOUS ;
189+
190+ if (flags & PWM_CAPTURE_TYPE_PERIOD ) {
191+ data -> capture .pulse_capture = false;
192+ /* set reloadOnCapture to true to reload counter when capture event happends,
193+ * so only second caputre value is needed when calculating ticks
194+ */
195+ QTMR_SetupInputCapture (config -> base ,
196+ (qtmr_channel_selection_t )channel ,
197+ (qtmr_input_source_t )channel ,
198+ inverted , true, kQTMR_RisingEdge );
199+ } else {
200+ data -> capture .pulse_capture = true;
201+ QTMR_SetupInputCapture (config -> base ,
202+ (qtmr_channel_selection_t )channel ,
203+ (qtmr_input_source_t )channel ,
204+ inverted , true, kQTMR_RisingAndFallingEdge );
205+ }
206+ QTMR_EnableInterrupts (config -> base , channel ,
207+ kQTMR_EdgeInterruptEnable | kQTMR_OverflowInterruptEnable );
208+
209+ return 0 ;
210+ }
211+
212+ static int mcux_qtmr_enable_capture (const struct device * dev , uint32_t channel )
213+ {
214+ const struct pwm_mcux_qtmr_config * config = dev -> config ;
215+ struct pwm_mcux_qtmr_data * data = dev -> data ;
216+
217+ if (channel >= CHANNEL_COUNT ) {
218+ LOG_ERR ("invalid channel %d" , channel );
219+ return - EINVAL ;
220+ }
221+
222+ if (!data -> capture .callback ) {
223+ LOG_ERR ("PWM capture not configured" );
224+ return - EINVAL ;
225+ }
226+
227+ if (mcux_qtmr_channel_is_active (dev , channel )) {
228+ LOG_ERR ("PWM capture already enabled" );
229+ return - EBUSY ;
230+ }
231+
232+ data -> capture .overflowed = false;
233+ data -> capture .first_edge_captured = false;
234+ data -> capture .overflow_count = 0 ;
235+ QTMR_StartTimer (config -> base , channel , kQTMR_PriSrcRiseEdge );
236+
237+ return 0 ;
238+ }
239+
240+ static int mcux_qtmr_disable_capture (const struct device * dev , uint32_t channel )
241+ {
242+ const struct pwm_mcux_qtmr_config * config = dev -> config ;
243+
244+ if (channel >= CHANNEL_COUNT ) {
245+ LOG_ERR ("invalid channel %d" , channel );
246+ return - EINVAL ;
247+ }
248+
249+ QTMR_StopTimer (config -> base , channel );
250+ return 0 ;
251+ }
252+
253+ static int mcux_qtmr_calc_ticks (uint32_t overflows , uint32_t capture ,
254+ uint32_t * result )
255+ {
256+ uint32_t pulse ;
257+
258+ /* Calculate cycles from overflow counter */
259+ if (u32_mul_overflow (overflows , 0xFFFFU , & pulse )) {
260+ return - ERANGE ;
261+ }
262+
263+ /* Add pulse width */
264+ if (u32_add_overflow (pulse , capture , & pulse )) {
265+ return - ERANGE ;
266+ }
267+
268+ * result = pulse ;
269+
270+ return 0 ;
271+ }
272+
273+ static void mcux_qtmr_isr (const struct device * dev )
274+ {
275+ const struct pwm_mcux_qtmr_config * config = dev -> config ;
276+ struct pwm_mcux_qtmr_data * data = dev -> data ;
277+ uint32_t ticks = 0 ;
278+ uint32_t timeCapt = 0 ;
279+ int err = 0 ;
280+ uint32_t flags ;
281+
282+ flags = QTMR_GetStatus (config -> base , data -> capture .channel );
283+ QTMR_ClearStatusFlags (config -> base , data -> capture .channel , flags );
284+
285+ if ((flags & kQTMR_OverflowFlag ) != 0U ) {
286+ data -> capture .overflowed |= u32_add_overflow (1 ,
287+ data -> capture .overflow_count , & data -> capture .overflow_count );
288+ }
289+
290+ if ((flags & kQTMR_EdgeFlag ) == 0U ) {
291+ return ;
292+ }
293+
294+ if (!data -> capture .first_edge_captured ) {
295+ data -> capture .first_edge_captured = true;
296+ data -> capture .overflow_count = 0 ;
297+ data -> capture .overflowed = false;
298+ return ;
299+ }
300+
301+ if (data -> capture .overflowed ) {
302+ err = - ERANGE ;
303+ } else {
304+ /* calculate ticks from second capture */
305+ timeCapt = config -> base -> CHANNEL [data -> capture .channel ].CAPT ;
306+ err = mcux_qtmr_calc_ticks (data -> capture .overflow_count , timeCapt , & ticks );
307+ }
308+
309+ if (data -> capture .pulse_capture ) {
310+ data -> capture .callback (dev , data -> capture .channel ,
311+ 0 , ticks , err , data -> capture .user_data );
312+ } else {
313+ data -> capture .callback (dev , data -> capture .channel ,
314+ ticks , 0 , err , data -> capture .user_data );
315+ }
316+
317+ /* Prepare for next capture */
318+ data -> capture .overflowed = false;
319+ data -> capture .overflow_count = 0 ;
320+
321+ if (data -> capture .continuous ) {
322+ if (data -> capture .pulse_capture ) {
323+ data -> capture .first_edge_captured = false;
324+ } else {
325+ /* No action required. In continuous period capture mode,
326+ * first edge of next period captureis second edge of this
327+ * capture (this edge)
328+ */
329+ }
330+ } else {
331+ /* Stop timer and disable interrupts for single capture*/
332+ data -> capture .first_edge_captured = false;
333+ QTMR_DisableInterrupts (config -> base , data -> capture .channel ,
334+ kQTMR_EdgeInterruptEnable | kQTMR_OverflowInterruptEnable );
335+ QTMR_StopTimer (config -> base , data -> capture .channel );
336+ }
337+ }
338+ #endif /* CONFIG_PWM_CAPTURE */
127339
128340static int mcux_qtmr_pwm_init (const struct device * dev )
129341{
@@ -142,6 +354,13 @@ static int mcux_qtmr_pwm_init(const struct device *dev)
142354 QTMR_GetDefaultConfig (& qtmr_config );
143355 qtmr_config .primarySource = kQTMR_ClockDivide_1 + (31 - __builtin_clz (config -> prescaler ));
144356
357+ #ifdef CONFIG_PWM_CAPTURE
358+ config -> irq_config_func (dev );
359+
360+ qtmr_config .faultFilterCount = CONFIG_PWM_CAPTURE_MCUX_QTMR_FILTER_COUNT ;
361+ qtmr_config .faultFilterPeriod = CONFIG_PWM_CAPTURE_MCUX_QTMR_FILTER_PERIOD ;
362+ #endif /* CONFIG_PWM_CAPTURE */
363+
145364 for (int i = 0 ; i < CHANNEL_COUNT ; i ++ ) {
146365 QTMR_Init (config -> base , i , & qtmr_config );
147366 }
@@ -152,22 +371,50 @@ static int mcux_qtmr_pwm_init(const struct device *dev)
152371static DEVICE_API (pwm , pwm_mcux_qtmr_driver_api ) = {
153372 .set_cycles = mcux_qtmr_pwm_set_cycles ,
154373 .get_cycles_per_sec = mcux_qtmr_pwm_get_cycles_per_sec ,
374+ #ifdef CONFIG_PWM_CAPTURE
375+ .configure_capture = mcux_qtmr_configure_capture ,
376+ .enable_capture = mcux_qtmr_enable_capture ,
377+ .disable_capture = mcux_qtmr_disable_capture ,
378+ #endif
155379};
156380
157- #define PWM_MCUX_QTMR_DEVICE_INIT (n ) \
158- PINCTRL_DT_INST_DEFINE(n); \
159- static struct pwm_mcux_qtmr_data pwm_mcux_qtmr_data_##n; \
160- \
161- static const struct pwm_mcux_qtmr_config pwm_mcux_qtmr_config_##n = { \
381+ #ifdef CONFIG_PWM_CAPTURE
382+ #define QTMR_CONFIG_FUNC (n ) \
383+ static void mcux_qtmr_config_func_##n(const struct device *dev) \
384+ { \
385+ IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \
386+ mcux_qtmr_isr, DEVICE_DT_INST_GET(n), 0); \
387+ irq_enable(DT_INST_IRQN(n)); \
388+ }
389+ #define QTMR_CFG_CAPTURE_INIT (n ) \
390+ .irq_config_func = mcux_qtmr_config_func_##n
391+ #define QTMR_INIT_CFG (n ) QTMR_DECLARE_CFG(n, QTMR_CFG_CAPTURE_INIT(n))
392+ #else /* !CONFIG_PWM_CAPTURE */
393+ #define QTMR_CONFIG_FUNC (n )
394+ #define QTMR_CFG_CAPTURE_INIT
395+ #define QTMR_INIT_CFG (n ) QTMR_DECLARE_CFG(n, QTMR_CFG_CAPTURE_INIT)
396+ #endif /* !CONFIG_PWM_CAPTURE */
397+
398+ #define QTMR_DECLARE_CFG (n , CAPTURE_INIT ) \
399+ static const struct pwm_mcux_qtmr_config pwm_mcux_qtmr_config_##n = { \
162400 .base = (TMR_Type *)DT_INST_REG_ADDR(n), \
163401 .prescaler = DT_INST_PROP(n, prescaler), \
164402 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
165403 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
166404 .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \
167- }; \
168- \
169- DEVICE_DT_INST_DEFINE(n, mcux_qtmr_pwm_init, NULL, &pwm_mcux_qtmr_data_##n, \
170- &pwm_mcux_qtmr_config_##n, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
171- &pwm_mcux_qtmr_driver_api);
405+ CAPTURE_INIT \
406+ }
172407
173- DT_INST_FOREACH_STATUS_OKAY (PWM_MCUX_QTMR_DEVICE_INIT )
408+ #define QTMR_DEVICE (n ) \
409+ PINCTRL_DT_INST_DEFINE(n); \
410+ static const struct pwm_mcux_qtmr_config pwm_mcux_qtmr_config_##n; \
411+ static struct pwm_mcux_qtmr_data pwm_mcux_qtmr_data_##n; \
412+ DEVICE_DT_INST_DEFINE(n, &mcux_qtmr_pwm_init, NULL, \
413+ &pwm_mcux_qtmr_data_##n, \
414+ &pwm_mcux_qtmr_config_##n, \
415+ POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
416+ &pwm_mcux_qtmr_driver_api); \
417+ QTMR_CONFIG_FUNC(n) \
418+ QTMR_INIT_CFG(n);
419+
420+ DT_INST_FOREACH_STATUS_OKAY (QTMR_DEVICE )
0 commit comments