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