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