1
1
/*
2
- * Copyright (c) 2021, NXP
2
+ * Copyright 2021, 2025 NXP
3
3
*
4
4
* SPDX-License-Identifier: Apache-2.0
5
5
*/
16
16
17
17
#include <zephyr/logging/log.h>
18
18
19
+ #include <zephyr/pm/policy.h>
20
+ #include <zephyr/pm/device.h>
21
+
19
22
LOG_MODULE_REGISTER (pwm_mcux_sctimer , CONFIG_PWM_LOG_LEVEL );
20
23
21
24
#define CHANNEL_COUNT FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS
@@ -36,6 +39,11 @@ struct pwm_mcux_sctimer_data {
36
39
sctimer_pwm_signal_param_t channel [CHANNEL_COUNT ];
37
40
uint32_t match_period ;
38
41
uint32_t configured_chan ;
42
+ /* This flag keeps track of active/idle channels
43
+ * to determine whether to allow low power mode
44
+ * or not (see PWM_MCUX_SCTIMER_RETAIN_INACTIVE_STATE).
45
+ */
46
+ uint32_t active_chan ;
39
47
};
40
48
41
49
/* Helper to setup channel that has not previously been configured for PWM */
@@ -75,9 +83,50 @@ static int mcux_sctimer_new_channel(const struct device *dev,
75
83
76
84
SCTIMER_StartTimer (config -> base , kSCTIMER_Counter_U );
77
85
data -> configured_chan ++ ;
86
+ data -> active_chan ++ ;
87
+
78
88
return 0 ;
79
89
}
80
90
91
+ static void mcux_sctimer_pwm_update_channel (const struct device * dev , uint32_t channel ,
92
+ uint8_t duty_cycle )
93
+ {
94
+ const struct pwm_mcux_sctimer_config * config = dev -> config ;
95
+ struct pwm_mcux_sctimer_data * data = dev -> data ;
96
+ SCT_Type * base = config -> base ;
97
+
98
+ SCTIMER_UpdatePwmDutycycle (base , channel , duty_cycle ,
99
+ data -> event_number [channel ]);
100
+
101
+ if (duty_cycle > 0 && data -> channel [channel ].dutyCyclePercent == 0 ) {
102
+ data -> active_chan ++ ;
103
+ }
104
+
105
+ if (duty_cycle == 0 && data -> channel [channel ].dutyCyclePercent > 0 ) {
106
+ data -> active_chan -- ;
107
+ }
108
+
109
+ data -> channel [channel ].dutyCyclePercent = duty_cycle ;
110
+
111
+ if (data -> active_chan == 0 ) {
112
+ /* No channels active. Stop the sctimer to manually set the output.
113
+ * Set the output to inactive state. Output for other channels
114
+ * already at the correct level. Just need to update the current one.
115
+ */
116
+ SCTIMER_StopTimer (base , kSCTIMER_Counter_U );
117
+
118
+ if (data -> channel [channel ].level == kSCTIMER_HighTrue ) {
119
+ base -> OUTPUT &= ~(1UL << channel );
120
+ } else {
121
+ base -> OUTPUT |= (1UL << channel );
122
+ }
123
+
124
+ #ifndef CONFIG_PWM_MCUX_SCT_KEEP_STATE
125
+ pm_policy_device_power_lock_put (dev );
126
+ #endif
127
+ }
128
+ }
129
+
81
130
static int mcux_sctimer_pwm_set_cycles (const struct device * dev ,
82
131
uint32_t channel , uint32_t period_cycles ,
83
132
uint32_t pulse_cycles , pwm_flags_t flags )
@@ -106,21 +155,10 @@ static int mcux_sctimer_pwm_set_cycles(const struct device *dev,
106
155
duty_cycle = 100 * pulse_cycles / period_cycles ;
107
156
108
157
if (duty_cycle == 0 && data -> configured_chan == 1 ) {
109
- /* Only one channel is active . We can turn off the SCTimer
158
+ /* Only one channel is configured . We can turn off the SCTimer
110
159
* global counter.
111
160
*/
112
- SCT_Type * base = config -> base ;
113
-
114
- /* Stop timer so we can set output directly */
115
- SCTIMER_StopTimer (base , kSCTIMER_Counter_U );
116
-
117
- /* Set the output to inactive State */
118
- if (data -> channel [channel ].level == kSCTIMER_HighTrue ) {
119
- base -> OUTPUT &= ~(1UL << channel );
120
- } else {
121
- base -> OUTPUT |= (1UL << channel );
122
- }
123
-
161
+ mcux_sctimer_pwm_update_channel (dev , channel , duty_cycle );
124
162
return 0 ;
125
163
}
126
164
@@ -147,6 +185,10 @@ static int mcux_sctimer_pwm_set_cycles(const struct device *dev,
147
185
if (ret < 0 ) {
148
186
return ret ;
149
187
}
188
+
189
+ /* Block from losing power while PWM output is enabled. */
190
+ pm_policy_device_power_lock_get (dev );
191
+
150
192
} else if (data -> event_number [channel ] == EVENT_NOT_SET ) {
151
193
/* We have already configured a PWM signal, but this channel
152
194
* has not been setup. We can only support this channel
@@ -168,7 +210,7 @@ static int mcux_sctimer_pwm_set_cycles(const struct device *dev,
168
210
*/
169
211
if (data -> configured_chan != 1 ) {
170
212
LOG_ERR ("Cannot change PWM period when multiple "
171
- "channels active " );
213
+ "channels configured " );
172
214
return - ENOTSUP ;
173
215
}
174
216
@@ -183,8 +225,7 @@ static int mcux_sctimer_pwm_set_cycles(const struct device *dev,
183
225
data -> match_period = period_cycles ;
184
226
} else {
185
227
/* Only duty cycle needs to be updated */
186
- SCTIMER_UpdatePwmDutycycle (config -> base , channel , duty_cycle ,
187
- data -> event_number [channel ]);
228
+ mcux_sctimer_pwm_update_channel (dev , channel , duty_cycle );
188
229
}
189
230
190
231
return 0 ;
@@ -207,7 +248,7 @@ static int mcux_sctimer_pwm_get_cycles_per_sec(const struct device *dev,
207
248
return 0 ;
208
249
}
209
250
210
- static int mcux_sctimer_pwm_init (const struct device * dev )
251
+ static int mcux_sctimer_pwm_init_common (const struct device * dev )
211
252
{
212
253
const struct pwm_mcux_sctimer_config * config = dev -> config ;
213
254
struct pwm_mcux_sctimer_data * data = dev -> data ;
@@ -239,10 +280,51 @@ static int mcux_sctimer_pwm_init(const struct device *dev)
239
280
}
240
281
data -> match_period = 0 ;
241
282
data -> configured_chan = 0 ;
283
+ data -> active_chan = 0 ;
242
284
243
285
return 0 ;
244
286
}
245
287
288
+ /* Note: This driver does not save and restore state when entering or exiting
289
+ * low power mode due to limitations in the SDK drivers. Users should ensure
290
+ * that the required configuration is reapplied after waking up from low power
291
+ * states.
292
+ */
293
+ static int mcux_sctimer_pwm_driver_pm_action (const struct device * dev ,
294
+ enum pm_device_action action )
295
+ {
296
+ const struct pwm_mcux_sctimer_config * config = dev -> config ;
297
+ int err = 0 ;
298
+
299
+ switch (action ) {
300
+ case PM_DEVICE_ACTION_RESUME :
301
+ break ;
302
+ case PM_DEVICE_ACTION_TURN_ON :
303
+ err = mcux_sctimer_pwm_init_common (dev );
304
+ break ;
305
+ case PM_DEVICE_ACTION_SUSPEND :
306
+ break ;
307
+ case PM_DEVICE_ACTION_TURN_OFF :
308
+ /* Reaching this point implies that all the PWM channels
309
+ * are in idle state and the SCTimer counter is disabled.
310
+ */
311
+ err = pinctrl_apply_state (config -> pincfg , PINCTRL_STATE_SLEEP );
312
+ break ;
313
+ default :
314
+ err = - ENOTSUP ;
315
+ }
316
+
317
+ return err ;
318
+ }
319
+
320
+ static int mcux_sctimer_pwm_init (const struct device * dev )
321
+ {
322
+ /* The device init is done from the PM_DEVICE_ACTION_TURN_ON in
323
+ * the pm callback which is invoked by pm_device_driver_init.
324
+ */
325
+ return pm_device_driver_init (dev , mcux_sctimer_pwm_driver_pm_action );
326
+ }
327
+
246
328
static DEVICE_API (pwm , pwm_mcux_sctimer_driver_api ) = {
247
329
.set_cycles = mcux_sctimer_pwm_set_cycles ,
248
330
.get_cycles_per_sec = mcux_sctimer_pwm_get_cycles_per_sec ,
@@ -259,10 +341,10 @@ static DEVICE_API(pwm, pwm_mcux_sctimer_driver_api) = {
259
341
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
260
342
.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name),\
261
343
}; \
262
- \
344
+ PM_DEVICE_DT_INST_DEFINE(n, mcux_sctimer_pwm_driver_pm_action); \
263
345
DEVICE_DT_INST_DEFINE(n, \
264
346
mcux_sctimer_pwm_init, \
265
- NULL, \
347
+ PM_DEVICE_DT_INST_GET(n), \
266
348
&pwm_mcux_sctimer_data_##n, \
267
349
&pwm_mcux_sctimer_config_##n, \
268
350
POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
0 commit comments