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