1 /*
2 * Copyright (c) 2017, NXP
3 * Copyright (c) 2020-2021 Vestas Wind Systems A/S
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT nxp_kinetis_ftm_pwm
9
10 #include <zephyr/drivers/clock_control.h>
11 #include <errno.h>
12 #include <zephyr/drivers/pwm.h>
13 #include <zephyr/irq.h>
14 #include <soc.h>
15 #include <fsl_ftm.h>
16 #include <fsl_clock.h>
17 #include <zephyr/drivers/pinctrl.h>
18
19 #include <zephyr/logging/log.h>
20
21 LOG_MODULE_REGISTER(pwm_mcux_ftm, CONFIG_PWM_LOG_LEVEL);
22
23 #define MAX_CHANNELS ARRAY_SIZE(FTM0->CONTROLS)
24
25 /* PWM capture operates on channel pairs */
26 #define MAX_CAPTURE_PAIRS (MAX_CHANNELS / 2U)
27 #define PAIR_1ST_CH(pair) (pair * 2U)
28 #define PAIR_2ND_CH(pair) (PAIR_1ST_CH(pair) + 1)
29
30 struct mcux_ftm_config {
31 FTM_Type *base;
32 const struct device *clock_dev;
33 clock_control_subsys_t clock_subsys;
34 ftm_clock_source_t ftm_clock_source;
35 ftm_clock_prescale_t prescale;
36 uint8_t channel_count;
37 ftm_pwm_mode_t mode;
38 #ifdef CONFIG_PWM_CAPTURE
39 void (*irq_config_func)(const struct device *dev);
40 #endif /* CONFIG_PWM_CAPTURE */
41 const struct pinctrl_dev_config *pincfg;
42 };
43
44 struct mcux_ftm_capture_data {
45 ftm_dual_edge_capture_param_t param;
46 pwm_capture_callback_handler_t callback;
47 void *user_data;
48 uint32_t first_edge_overflows;
49 uint16_t first_edge_cnt;
50 bool first_edge_overflow;
51 bool pulse_capture;
52 };
53
54 struct mcux_ftm_data {
55 uint32_t clock_freq;
56 uint32_t period_cycles;
57 ftm_chnl_pwm_config_param_t channel[MAX_CHANNELS];
58 #ifdef CONFIG_PWM_CAPTURE
59 uint32_t overflows;
60 struct mcux_ftm_capture_data capture[MAX_CAPTURE_PAIRS];
61 #endif /* CONFIG_PWM_CAPTURE */
62 };
63
mcux_ftm_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)64 static int mcux_ftm_set_cycles(const struct device *dev, uint32_t channel,
65 uint32_t period_cycles, uint32_t pulse_cycles,
66 pwm_flags_t flags)
67 {
68 const struct mcux_ftm_config *config = dev->config;
69 struct mcux_ftm_data *data = dev->data;
70 status_t status;
71 #ifdef CONFIG_PWM_CAPTURE
72 uint32_t pair = channel / 2U;
73 uint32_t irqs;
74 #endif /* CONFIG_PWM_CAPTURE */
75
76 if (period_cycles == 0U) {
77 LOG_ERR("Channel can not be set to inactive level");
78 return -ENOTSUP;
79 }
80
81 if (channel >= config->channel_count) {
82 LOG_ERR("Invalid channel");
83 return -ENOTSUP;
84 }
85
86 #ifdef CONFIG_PWM_CAPTURE
87 irqs = FTM_GetEnabledInterrupts(config->base);
88 if (irqs & BIT(PAIR_2ND_CH(pair))) {
89 LOG_ERR("Cannot set PWM, capture in progress on pair %d", pair);
90 return -EBUSY;
91 }
92 #endif /* CONFIG_PWM_CAPTURE */
93
94 data->channel[channel].dutyValue = pulse_cycles;
95
96 if ((flags & PWM_POLARITY_INVERTED) == 0) {
97 data->channel[channel].level = kFTM_HighTrue;
98 } else {
99 data->channel[channel].level = kFTM_LowTrue;
100 }
101
102 LOG_DBG("pulse_cycles=%d, period_cycles=%d, flags=%d",
103 pulse_cycles, period_cycles, flags);
104
105 if (period_cycles != data->period_cycles) {
106 #ifdef CONFIG_PWM_CAPTURE
107 if (irqs & BIT_MASK(ARRAY_SIZE(data->channel))) {
108 LOG_ERR("Cannot change period, capture in progress");
109 return -EBUSY;
110 }
111 #endif /* CONFIG_PWM_CAPTURE */
112
113 if (data->period_cycles != 0) {
114 /* Only warn when not changing from zero */
115 LOG_WRN("Changing period cycles from %d to %d"
116 " affects all %d channels in %s",
117 data->period_cycles, period_cycles,
118 config->channel_count, dev->name);
119 }
120
121 data->period_cycles = period_cycles;
122
123 FTM_StopTimer(config->base);
124 FTM_SetTimerPeriod(config->base, period_cycles);
125
126 FTM_SetSoftwareTrigger(config->base, true);
127 FTM_StartTimer(config->base, config->ftm_clock_source);
128 }
129
130 status = FTM_SetupPwmMode(config->base, data->channel,
131 config->channel_count, config->mode);
132 if (status != kStatus_Success) {
133 LOG_ERR("Could not set up pwm");
134 return -ENOTSUP;
135 }
136 FTM_SetSoftwareTrigger(config->base, true);
137
138 return 0;
139 }
140
141 #ifdef CONFIG_PWM_CAPTURE
mcux_ftm_configure_capture(const struct device * dev,uint32_t channel,pwm_flags_t flags,pwm_capture_callback_handler_t cb,void * user_data)142 static int mcux_ftm_configure_capture(const struct device *dev,
143 uint32_t channel, pwm_flags_t flags,
144 pwm_capture_callback_handler_t cb,
145 void *user_data)
146 {
147 const struct mcux_ftm_config *config = dev->config;
148 struct mcux_ftm_data *data = dev->data;
149 ftm_dual_edge_capture_param_t *param;
150 uint32_t pair = channel / 2U;
151
152 if (channel & 0x1U) {
153 LOG_ERR("PWM capture only supported on even channels");
154 return -ENOTSUP;
155 }
156
157 if (pair >= ARRAY_SIZE(data->capture)) {
158 LOG_ERR("Invalid channel pair %d", pair);
159 return -EINVAL;
160 }
161
162 if (FTM_GetEnabledInterrupts(config->base) & BIT(PAIR_2ND_CH(pair))) {
163 LOG_ERR("Capture already active on channel pair %d", pair);
164 return -EBUSY;
165 }
166
167 if (!(flags & PWM_CAPTURE_TYPE_MASK)) {
168 LOG_ERR("No capture type specified");
169 return -EINVAL;
170 }
171
172 if ((flags & PWM_CAPTURE_TYPE_MASK) == PWM_CAPTURE_TYPE_BOTH) {
173 LOG_ERR("Cannot capture both period and pulse width");
174 return -ENOTSUP;
175 }
176
177 data->capture[pair].callback = cb;
178 data->capture[pair].user_data = user_data;
179 param = &data->capture[pair].param;
180
181 if ((flags & PWM_CAPTURE_MODE_MASK) == PWM_CAPTURE_MODE_CONTINUOUS) {
182 param->mode = kFTM_Continuous;
183 } else {
184 param->mode = kFTM_OneShot;
185 }
186
187 if (flags & PWM_CAPTURE_TYPE_PERIOD) {
188 data->capture[pair].pulse_capture = false;
189
190 if (flags & PWM_POLARITY_INVERTED) {
191 param->currChanEdgeMode = kFTM_FallingEdge;
192 param->nextChanEdgeMode = kFTM_FallingEdge;
193 } else {
194 param->currChanEdgeMode = kFTM_RisingEdge;
195 param->nextChanEdgeMode = kFTM_RisingEdge;
196 }
197 } else {
198 data->capture[pair].pulse_capture = true;
199
200 if (flags & PWM_POLARITY_INVERTED) {
201 param->currChanEdgeMode = kFTM_FallingEdge;
202 param->nextChanEdgeMode = kFTM_RisingEdge;
203 } else {
204 param->currChanEdgeMode = kFTM_RisingEdge;
205 param->nextChanEdgeMode = kFTM_FallingEdge;
206 }
207 }
208
209 return 0;
210 }
211
mcux_ftm_enable_capture(const struct device * dev,uint32_t channel)212 static int mcux_ftm_enable_capture(const struct device *dev, uint32_t channel)
213 {
214 const struct mcux_ftm_config *config = dev->config;
215 struct mcux_ftm_data *data = dev->data;
216 uint32_t pair = channel / 2U;
217
218 if (channel & 0x1U) {
219 LOG_ERR("PWM capture only supported on even channels");
220 return -ENOTSUP;
221 }
222
223 if (pair >= ARRAY_SIZE(data->capture)) {
224 LOG_ERR("Invalid channel pair %d", pair);
225 return -EINVAL;
226 }
227
228 if (!data->capture[pair].callback) {
229 LOG_ERR("PWM capture not configured");
230 return -EINVAL;
231 }
232
233 if (FTM_GetEnabledInterrupts(config->base) & BIT(PAIR_2ND_CH(pair))) {
234 LOG_ERR("Capture already active on channel pair %d", pair);
235 return -EBUSY;
236 }
237
238 FTM_ClearStatusFlags(config->base, BIT(PAIR_1ST_CH(pair)) |
239 BIT(PAIR_2ND_CH(pair)));
240
241 FTM_SetupDualEdgeCapture(config->base, pair, &data->capture[pair].param,
242 CONFIG_PWM_CAPTURE_MCUX_FTM_FILTER_VALUE);
243
244 FTM_EnableInterrupts(config->base, BIT(PAIR_1ST_CH(pair)) |
245 BIT(PAIR_2ND_CH(pair)));
246
247 return 0;
248 }
249
mcux_ftm_disable_capture(const struct device * dev,uint32_t channel)250 static int mcux_ftm_disable_capture(const struct device *dev, uint32_t channel)
251 {
252 const struct mcux_ftm_config *config = dev->config;
253 struct mcux_ftm_data *data = dev->data;
254 uint32_t pair = channel / 2U;
255
256 if (channel & 0x1U) {
257 LOG_ERR("PWM capture only supported on even channels");
258 return -ENOTSUP;
259 }
260
261 if (pair >= ARRAY_SIZE(data->capture)) {
262 LOG_ERR("Invalid channel pair %d", pair);
263 return -EINVAL;
264 }
265
266 FTM_DisableInterrupts(config->base, BIT(PAIR_1ST_CH(pair)) |
267 BIT(PAIR_2ND_CH(pair)));
268
269 /* Clear Dual Edge Capture Enable bit */
270 config->base->COMBINE &= ~(1UL << (FTM_COMBINE_DECAP0_SHIFT +
271 (FTM_COMBINE_COMBINE1_SHIFT * pair)));
272
273 return 0;
274 }
275
mcux_ftm_capture_first_edge(const struct device * dev,uint32_t channel,uint16_t cnt,bool overflow)276 static void mcux_ftm_capture_first_edge(const struct device *dev, uint32_t channel,
277 uint16_t cnt, bool overflow)
278 {
279 const struct mcux_ftm_config *config = dev->config;
280 struct mcux_ftm_data *data = dev->data;
281 struct mcux_ftm_capture_data *capture;
282 uint32_t pair = channel / 2U;
283
284 __ASSERT_NO_MSG(pair < ARRAY_SIZE(data->capture));
285 capture = &data->capture[pair];
286
287 FTM_DisableInterrupts(config->base, BIT(PAIR_1ST_CH(pair)));
288
289 capture->first_edge_cnt = cnt;
290 capture->first_edge_overflows = data->overflows;
291 capture->first_edge_overflow = overflow;
292
293 LOG_DBG("pair = %d, 1st cnt = %u, 1st ovf = %d", pair, cnt, overflow);
294 }
295
mcux_ftm_capture_second_edge(const struct device * dev,uint32_t channel,uint16_t cnt,bool overflow)296 static void mcux_ftm_capture_second_edge(const struct device *dev, uint32_t channel,
297 uint16_t cnt, bool overflow)
298
299 {
300 const struct mcux_ftm_config *config = dev->config;
301 struct mcux_ftm_data *data = dev->data;
302 uint32_t second_edge_overflows = data->overflows;
303 struct mcux_ftm_capture_data *capture;
304 uint32_t pair = channel / 2U;
305 uint32_t overflows;
306 uint32_t first_cnv;
307 uint32_t second_cnv;
308 uint32_t cycles = 0;
309 int status = 0;
310
311 __ASSERT_NO_MSG(pair < ARRAY_SIZE(data->capture));
312 capture = &data->capture[pair];
313
314 first_cnv = config->base->CONTROLS[PAIR_1ST_CH(pair)].CnV;
315 second_cnv = config->base->CONTROLS[PAIR_2ND_CH(pair)].CnV;
316
317 if (capture->pulse_capture) {
318 /* Clear both edge flags for pulse capture to capture first edge overflow counter */
319 FTM_ClearStatusFlags(config->base, BIT(PAIR_1ST_CH(pair)) | BIT(PAIR_2ND_CH(pair)));
320 } else {
321 /* Only clear second edge flag for period capture as next first edge is this edge */
322 FTM_ClearStatusFlags(config->base, BIT(PAIR_2ND_CH(pair)));
323 }
324
325 if (unlikely(capture->first_edge_overflow && first_cnv > capture->first_edge_cnt)) {
326 /* Compensate for the overflow registered in the same IRQ */
327 capture->first_edge_overflows--;
328 }
329
330 if (unlikely(overflow && second_cnv > cnt)) {
331 /* Compensate for the overflow registered in the same IRQ */
332 second_edge_overflows--;
333 }
334
335 overflows = second_edge_overflows - capture->first_edge_overflows;
336
337 /* Calculate cycles, check for overflows */
338 if (overflows > 0) {
339 if (u32_mul_overflow(overflows, config->base->MOD, &cycles)) {
340 LOG_ERR("overflow while calculating cycles");
341 status = -ERANGE;
342 } else {
343 cycles -= first_cnv;
344 if (u32_add_overflow(cycles, second_cnv, &cycles)) {
345 LOG_ERR("overflow while calculating cycles");
346 cycles = 0;
347 status = -ERANGE;
348 }
349 }
350 } else {
351 cycles = second_cnv - first_cnv;
352 }
353
354 LOG_DBG("pair = %d, 1st ovfs = %u, 2nd ovfs = %u, ovfs = %u, 1st cnv = %u, "
355 "2nd cnv = %u, cycles = %u, 2nd cnt = %u, 2nd ovf = %d",
356 pair, capture->first_edge_overflows, second_edge_overflows, overflows, first_cnv,
357 second_cnv, cycles, cnt, overflow);
358
359 if (capture->pulse_capture) {
360 capture->callback(dev, pair, 0, cycles, status,
361 capture->user_data);
362 } else {
363 capture->callback(dev, pair, cycles, 0, status,
364 capture->user_data);
365 }
366
367 if (capture->param.mode == kFTM_OneShot) {
368 /* One-shot capture done */
369 FTM_DisableInterrupts(config->base, BIT(PAIR_2ND_CH(pair)));
370 } else if (capture->pulse_capture) {
371 /* Prepare for first edge of next pulse capture */
372 FTM_EnableInterrupts(config->base, BIT(PAIR_1ST_CH(pair)));
373 } else {
374 /* First edge of next period capture is second edge of this capture (this edge) */
375 capture->first_edge_cnt = cnt;
376 capture->first_edge_overflows = second_edge_overflows;
377 capture->first_edge_overflow = false;
378 }
379 }
380
mcux_ftm_isr(const struct device * dev)381 static void mcux_ftm_isr(const struct device *dev)
382 {
383 const struct mcux_ftm_config *config = dev->config;
384 struct mcux_ftm_data *data = dev->data;
385 bool overflow = false;
386 uint32_t flags;
387 uint32_t irqs;
388 uint16_t cnt;
389 uint32_t ch;
390
391 flags = FTM_GetStatusFlags(config->base);
392 irqs = FTM_GetEnabledInterrupts(config->base);
393 cnt = config->base->CNT;
394
395 if (flags & kFTM_TimeOverflowFlag) {
396 data->overflows++;
397 overflow = true;
398 FTM_ClearStatusFlags(config->base, kFTM_TimeOverflowFlag);
399 }
400
401 for (ch = 0; ch < MAX_CHANNELS; ch++) {
402 if ((flags & BIT(ch)) && (irqs & BIT(ch))) {
403 if (ch & 1) {
404 mcux_ftm_capture_second_edge(dev, ch, cnt, overflow);
405 } else {
406 mcux_ftm_capture_first_edge(dev, ch, cnt, overflow);
407 }
408 }
409 }
410 }
411 #endif /* CONFIG_PWM_CAPTURE */
412
mcux_ftm_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)413 static int mcux_ftm_get_cycles_per_sec(const struct device *dev,
414 uint32_t channel, uint64_t *cycles)
415 {
416 const struct mcux_ftm_config *config = dev->config;
417 struct mcux_ftm_data *data = dev->data;
418
419 *cycles = data->clock_freq >> config->prescale;
420
421 return 0;
422 }
423
mcux_ftm_init(const struct device * dev)424 static int mcux_ftm_init(const struct device *dev)
425 {
426 const struct mcux_ftm_config *config = dev->config;
427 struct mcux_ftm_data *data = dev->data;
428 ftm_chnl_pwm_config_param_t *channel = data->channel;
429 ftm_config_t ftm_config;
430 int i;
431 int err;
432
433 err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
434 if (err != 0) {
435 return err;
436 }
437
438 if (config->channel_count > ARRAY_SIZE(data->channel)) {
439 LOG_ERR("Invalid channel count");
440 return -EINVAL;
441 }
442
443 if (!device_is_ready(config->clock_dev)) {
444 LOG_ERR("clock control device not ready");
445 return -ENODEV;
446 }
447
448 if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
449 &data->clock_freq)) {
450 LOG_ERR("Could not get clock frequency");
451 return -EINVAL;
452 }
453
454 for (i = 0; i < config->channel_count; i++) {
455 channel->chnlNumber = i;
456 channel->level = kFTM_NoPwmSignal;
457 channel->dutyValue = 0;
458 channel->firstEdgeValue = 0;
459 channel++;
460 }
461
462 FTM_GetDefaultConfig(&ftm_config);
463 ftm_config.prescale = config->prescale;
464
465 FTM_Init(config->base, &ftm_config);
466
467 #ifdef CONFIG_PWM_CAPTURE
468 config->irq_config_func(dev);
469 FTM_EnableInterrupts(config->base,
470 kFTM_TimeOverflowInterruptEnable);
471
472 data->period_cycles = 0xFFFFU;
473 FTM_SetTimerPeriod(config->base, data->period_cycles);
474 FTM_SetSoftwareTrigger(config->base, true);
475 FTM_StartTimer(config->base, config->ftm_clock_source);
476 #endif /* CONFIG_PWM_CAPTURE */
477
478 return 0;
479 }
480
481 static const struct pwm_driver_api mcux_ftm_driver_api = {
482 .set_cycles = mcux_ftm_set_cycles,
483 .get_cycles_per_sec = mcux_ftm_get_cycles_per_sec,
484 #ifdef CONFIG_PWM_CAPTURE
485 .configure_capture = mcux_ftm_configure_capture,
486 .enable_capture = mcux_ftm_enable_capture,
487 .disable_capture = mcux_ftm_disable_capture,
488 #endif /* CONFIG_PWM_CAPTURE */
489 };
490
491 #define TO_FTM_PRESCALE_DIVIDE(val) _DO_CONCAT(kFTM_Prescale_Divide_, val)
492
493 #ifdef CONFIG_PWM_CAPTURE
494 #define FTM_CONFIG_FUNC(n) \
495 static void mcux_ftm_config_func_##n(const struct device *dev) \
496 { \
497 IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \
498 mcux_ftm_isr, DEVICE_DT_INST_GET(n), 0); \
499 irq_enable(DT_INST_IRQN(n)); \
500 }
501 #define FTM_CFG_CAPTURE_INIT(n) \
502 .irq_config_func = mcux_ftm_config_func_##n
503 #define FTM_INIT_CFG(n) FTM_DECLARE_CFG(n, FTM_CFG_CAPTURE_INIT(n))
504 #else /* !CONFIG_PWM_CAPTURE */
505 #define FTM_CONFIG_FUNC(n)
506 #define FTM_CFG_CAPTURE_INIT
507 #define FTM_INIT_CFG(n) FTM_DECLARE_CFG(n, FTM_CFG_CAPTURE_INIT)
508 #endif /* !CONFIG_PWM_CAPTURE */
509
510 #define FTM_DECLARE_CFG(n, CAPTURE_INIT) \
511 static const struct mcux_ftm_config mcux_ftm_config_##n = { \
512 .base = (FTM_Type *)DT_INST_REG_ADDR(n),\
513 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
514 .clock_subsys = (clock_control_subsys_t) \
515 DT_INST_CLOCKS_CELL(n, name), \
516 .ftm_clock_source = kFTM_FixedClock, \
517 .prescale = TO_FTM_PRESCALE_DIVIDE(DT_INST_PROP(n, prescaler)),\
518 .channel_count = FSL_FEATURE_FTM_CHANNEL_COUNTn((FTM_Type *) \
519 DT_INST_REG_ADDR(n)), \
520 .mode = kFTM_EdgeAlignedPwm, \
521 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
522 CAPTURE_INIT \
523 }
524
525 #define FTM_DEVICE(n) \
526 PINCTRL_DT_INST_DEFINE(n); \
527 static struct mcux_ftm_data mcux_ftm_data_##n; \
528 static const struct mcux_ftm_config mcux_ftm_config_##n; \
529 DEVICE_DT_INST_DEFINE(n, &mcux_ftm_init, \
530 NULL, &mcux_ftm_data_##n, \
531 &mcux_ftm_config_##n, \
532 POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
533 &mcux_ftm_driver_api); \
534 FTM_CONFIG_FUNC(n) \
535 FTM_INIT_CFG(n);
536
537 DT_INST_FOREACH_STATUS_OKAY(FTM_DEVICE)
538