1 /*
2  * Copyright (c) 2021 Vestas Wind Systems A/S
3  * Copyright 2024 NXP
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT nxp_kinetis_pwt
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_pwt.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_pwt, CONFIG_PWM_LOG_LEVEL);
22 
23 /* Number of PWT input ports */
24 #define PWT_INPUTS 4U
25 
26 struct mcux_pwt_config {
27 	PWT_Type *base;
28 	const struct device *clock_dev;
29 	clock_control_subsys_t clock_subsys;
30 	pwt_clock_source_t pwt_clock_source;
31 	pwt_clock_prescale_t prescale;
32 	void (*irq_config_func)(const struct device *dev);
33 	const struct pinctrl_dev_config *pincfg;
34 };
35 
36 struct mcux_pwt_data {
37 	uint32_t clock_freq;
38 	uint32_t period_cycles;
39 	uint32_t high_overflows;
40 	uint32_t low_overflows;
41 	pwm_capture_callback_handler_t callback;
42 	void *user_data;
43 	pwt_config_t pwt_config;
44 	bool continuous : 1;
45 	bool inverted : 1;
46 	bool overflowed : 1;
47 };
48 
mcux_pwt_is_active(const struct device * dev)49 static inline bool mcux_pwt_is_active(const struct device *dev)
50 {
51 	const struct mcux_pwt_config *config = dev->config;
52 
53 	return !!(config->base->CS & PWT_CS_PWTEN_MASK);
54 }
55 
mcux_pwt_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)56 static int mcux_pwt_set_cycles(const struct device *dev, uint32_t channel,
57 			       uint32_t period_cycles, uint32_t pulse_cycles,
58 			       pwm_flags_t flags)
59 {
60 	ARG_UNUSED(dev);
61 	ARG_UNUSED(channel);
62 	ARG_UNUSED(period_cycles);
63 	ARG_UNUSED(pulse_cycles);
64 	ARG_UNUSED(flags);
65 
66 	LOG_ERR("pwt only supports pwm capture");
67 
68 	return -ENOTSUP;
69 }
70 
mcux_pwt_configure_capture(const struct device * dev,uint32_t channel,pwm_flags_t flags,pwm_capture_callback_handler_t cb,void * user_data)71 static int mcux_pwt_configure_capture(const struct device *dev,
72 				      uint32_t channel, pwm_flags_t flags,
73 				      pwm_capture_callback_handler_t cb,
74 				      void *user_data)
75 {
76 	const struct mcux_pwt_config *config = dev->config;
77 	struct mcux_pwt_data *data = dev->data;
78 
79 	if (channel >= PWT_INPUTS) {
80 		LOG_ERR("invalid channel %d", channel);
81 		return -EINVAL;
82 	}
83 
84 	if (mcux_pwt_is_active(dev)) {
85 		LOG_ERR("pwm capture in progress");
86 		return -EBUSY;
87 	}
88 
89 #if defined(CONFIG_SOC_SERIES_KE1XZ)
90 	if ((flags & PWM_CAPTURE_TYPE_MASK) == PWM_CAPTURE_TYPE_BOTH) {
91 		LOG_ERR("Cannot capture both period and pulse width");
92 		return -ENOTSUP;
93 	}
94 
95 	if (((flags & PWM_CAPTURE_TYPE_MASK) == PWM_CAPTURE_TYPE_PERIOD) &&
96 		((flags & PWM_POLARITY_MASK) == PWM_POLARITY_NORMAL)) {
97 		LOG_ERR("Cannot capture period in normal polarity (active-high pulse)");
98 		return -ENOTSUP;
99 	}
100 #endif
101 
102 	data->callback = cb;
103 	data->user_data = user_data;
104 
105 	data->pwt_config.inputSelect = channel;
106 
107 	data->continuous =
108 		(flags & PWM_CAPTURE_MODE_MASK) == PWM_CAPTURE_MODE_CONTINUOUS;
109 	data->inverted =
110 		(flags & PWM_POLARITY_MASK) == PWM_POLARITY_INVERTED;
111 
112 	PWT_Init(config->base, &data->pwt_config);
113 	PWT_EnableInterrupts(config->base,
114 			     kPWT_PulseWidthReadyInterruptEnable |
115 			     kPWT_CounterOverflowInterruptEnable);
116 
117 	return 0;
118 }
119 
mcux_pwt_enable_capture(const struct device * dev,uint32_t channel)120 static int mcux_pwt_enable_capture(const struct device *dev, uint32_t channel)
121 {
122 	const struct mcux_pwt_config *config = dev->config;
123 	struct mcux_pwt_data *data = dev->data;
124 
125 	if (channel >= PWT_INPUTS) {
126 		LOG_ERR("invalid channel %d", channel);
127 		return -EINVAL;
128 	}
129 
130 	if (!data->callback) {
131 		LOG_ERR("PWM capture not configured");
132 		return -EINVAL;
133 	}
134 
135 	if (mcux_pwt_is_active(dev)) {
136 		LOG_ERR("PWM capture already enabled");
137 		return -EBUSY;
138 	}
139 
140 	data->overflowed = false;
141 	data->high_overflows = 0;
142 	data->low_overflows = 0;
143 	PWT_StartTimer(config->base);
144 
145 	return 0;
146 }
147 
mcux_pwt_disable_capture(const struct device * dev,uint32_t channel)148 static int mcux_pwt_disable_capture(const struct device *dev, uint32_t channel)
149 {
150 	const struct mcux_pwt_config *config = dev->config;
151 
152 	if (channel >= PWT_INPUTS) {
153 		LOG_ERR("invalid channel %d", channel);
154 		return -EINVAL;
155 	}
156 
157 	PWT_StopTimer(config->base);
158 
159 	return 0;
160 }
161 
mcux_pwt_calc_period(uint16_t ppw,uint16_t npw,uint32_t high_overflows,uint32_t low_overflows,uint32_t * result)162 static int mcux_pwt_calc_period(uint16_t ppw, uint16_t npw,
163 				uint32_t high_overflows,
164 				uint32_t low_overflows,
165 				uint32_t *result)
166 {
167 	uint32_t period;
168 
169 	/* Calculate sum of overflow counters */
170 	if (u32_add_overflow(high_overflows, low_overflows, &period)) {
171 		return -ERANGE;
172 	}
173 
174 	/* Calculate cycles from sum of overflow counters */
175 	if (u32_mul_overflow(period, 0xFFFFU, &period)) {
176 		return -ERANGE;
177 	}
178 
179 	/* Add positive pulse width */
180 	if (u32_add_overflow(period, ppw, &period)) {
181 		return -ERANGE;
182 	}
183 
184 	/* Add negative pulse width */
185 	if (u32_add_overflow(period, npw, &period)) {
186 		return -ERANGE;
187 	}
188 
189 	*result = period;
190 
191 	return 0;
192 }
193 
mcux_pwt_calc_pulse(uint16_t pw,uint32_t overflows,uint32_t * result)194 static int mcux_pwt_calc_pulse(uint16_t pw, uint32_t overflows,
195 			       uint32_t *result)
196 {
197 	uint32_t pulse;
198 
199 	/* Calculate cycles from overflow counter */
200 	if (u32_mul_overflow(overflows, 0xFFFFU, &pulse)) {
201 		return -ERANGE;
202 	}
203 
204 	/* Add pulse width */
205 	if (u32_add_overflow(pulse, pw, &pulse)) {
206 		return -ERANGE;
207 	}
208 
209 	*result = pulse;
210 
211 	return 0;
212 }
213 
mcux_pwt_isr(const struct device * dev)214 static void mcux_pwt_isr(const struct device *dev)
215 {
216 	const struct mcux_pwt_config *config = dev->config;
217 	struct mcux_pwt_data *data = dev->data;
218 	uint32_t period = 0;
219 	uint32_t pulse = 0;
220 	uint32_t flags;
221 	uint16_t ppw;
222 	uint16_t npw;
223 	int err;
224 
225 	flags = PWT_GetStatusFlags(config->base);
226 
227 	if (flags & kPWT_CounterOverflowFlag) {
228 		if (config->base->CR & PWT_CR_LVL_MASK) {
229 			data->overflowed |= u32_add_overflow(1,
230 				data->high_overflows, &data->high_overflows);
231 		} else {
232 			data->overflowed |= u32_add_overflow(1,
233 				data->low_overflows, &data->low_overflows);
234 		}
235 
236 		PWT_ClearStatusFlags(config->base, kPWT_CounterOverflowFlag);
237 	}
238 
239 	if (flags & kPWT_PulseWidthValidFlag) {
240 		ppw = PWT_ReadPositivePulseWidth(config->base);
241 		npw = PWT_ReadNegativePulseWidth(config->base);
242 
243 		if (!data->continuous) {
244 			PWT_StopTimer(config->base);
245 		}
246 
247 		if (data->inverted) {
248 			err = mcux_pwt_calc_pulse(npw, data->low_overflows,
249 						  &pulse);
250 		} else {
251 			err = mcux_pwt_calc_pulse(ppw, data->high_overflows,
252 						  &pulse);
253 		}
254 
255 		if (err == 0) {
256 			err = mcux_pwt_calc_period(ppw, npw,
257 						   data->high_overflows,
258 						   data->low_overflows,
259 						   &period);
260 		}
261 
262 		if (data->overflowed) {
263 			err = -ERANGE;
264 		}
265 
266 		LOG_DBG("period = %d, pulse = %d, err = %d", period, pulse,
267 			err);
268 
269 		if (data->callback) {
270 			data->callback(dev, data->pwt_config.inputSelect,
271 				       period, pulse, err, data->user_data);
272 		}
273 
274 		data->overflowed = false;
275 		data->high_overflows = 0;
276 		data->low_overflows = 0;
277 		PWT_ClearStatusFlags(config->base, kPWT_PulseWidthValidFlag);
278 	}
279 }
280 
mcux_pwt_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)281 static int mcux_pwt_get_cycles_per_sec(const struct device *dev,
282 				       uint32_t channel, uint64_t *cycles)
283 {
284 	const struct mcux_pwt_config *config = dev->config;
285 	struct mcux_pwt_data *data = dev->data;
286 
287 	ARG_UNUSED(channel);
288 
289 	*cycles = data->clock_freq >> config->prescale;
290 
291 	return 0;
292 }
293 
mcux_pwt_init(const struct device * dev)294 static int mcux_pwt_init(const struct device *dev)
295 {
296 	const struct mcux_pwt_config *config = dev->config;
297 	struct mcux_pwt_data *data = dev->data;
298 	pwt_config_t *pwt_config = &data->pwt_config;
299 	int err;
300 
301 	if (!device_is_ready(config->clock_dev)) {
302 		LOG_ERR("clock control device not ready");
303 		return -ENODEV;
304 	}
305 
306 	if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
307 				   &data->clock_freq)) {
308 		LOG_ERR("could not get clock frequency");
309 		return -EINVAL;
310 	}
311 
312 	PWT_GetDefaultConfig(pwt_config);
313 	pwt_config->clockSource = config->pwt_clock_source;
314 	pwt_config->prescale = config->prescale;
315 	pwt_config->enableFirstCounterLoad = true;
316 	PWT_Init(config->base, pwt_config);
317 
318 	err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
319 	if (err) {
320 		return err;
321 	}
322 
323 	config->irq_config_func(dev);
324 
325 	return 0;
326 }
327 
328 static DEVICE_API(pwm, mcux_pwt_driver_api) = {
329 	.set_cycles = mcux_pwt_set_cycles,
330 	.get_cycles_per_sec = mcux_pwt_get_cycles_per_sec,
331 	.configure_capture = mcux_pwt_configure_capture,
332 	.enable_capture = mcux_pwt_enable_capture,
333 	.disable_capture = mcux_pwt_disable_capture,
334 };
335 
336 #define TO_PWT_PRESCALE_DIVIDE(val) _DO_CONCAT(kPWT_Prescale_Divide_, val)
337 
338 #define PWT_DEVICE(n) \
339 	static void mcux_pwt_config_func_##n(const struct device *dev);	\
340 									\
341 	PINCTRL_DT_INST_DEFINE(n);					\
342 									\
343 	static const struct mcux_pwt_config mcux_pwt_config_##n = {	\
344 		.base = (PWT_Type *)DT_INST_REG_ADDR(n),		\
345 		.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)),	\
346 		.clock_subsys =						\
347 		(clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name),	\
348 		.pwt_clock_source = kPWT_BusClock,			\
349 		.prescale =						\
350 		TO_PWT_PRESCALE_DIVIDE(DT_INST_PROP(n, prescaler)),	\
351 		.irq_config_func = mcux_pwt_config_func_##n,		\
352 		.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),		\
353 	};								\
354 									\
355 	static struct mcux_pwt_data mcux_pwt_data_##n;			\
356 									\
357 	DEVICE_DT_INST_DEFINE(n, &mcux_pwt_init,			\
358 			NULL, &mcux_pwt_data_##n,			\
359 			&mcux_pwt_config_##n,				\
360 			POST_KERNEL,					\
361 			CONFIG_PWM_INIT_PRIORITY,			\
362 			&mcux_pwt_driver_api);				\
363 									\
364 	static void mcux_pwt_config_func_##n(const struct device *dev)	\
365 	{								\
366 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority),	\
367 			mcux_pwt_isr, DEVICE_DT_INST_GET(n), 0);	\
368 		irq_enable(DT_INST_IRQN(n));				\
369 	}
370 
371 DT_INST_FOREACH_STATUS_OKAY(PWT_DEVICE)
372