1 /*
2 * Copyright 2024 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7
8 #define DT_DRV_COMPAT nxp_flexio_pwm
9
10 #include <errno.h>
11 #include <zephyr/drivers/pwm.h>
12 #include <fsl_flexio.h>
13 #include <fsl_clock.h>
14 #include <zephyr/drivers/pinctrl.h>
15 #include <zephyr/drivers/clock_control.h>
16
17 #include <zephyr/logging/log.h>
18 #include <zephyr/drivers/misc/nxp_flexio/nxp_flexio.h>
19
20
21 LOG_MODULE_REGISTER(pwm_nxp_flexio, CONFIG_PWM_LOG_LEVEL);
22
23 #define FLEXIO_PWM_TIMER_CMP_MAX_VALUE (0xFFFFU)
24 #define FLEXIO_PWM_TIMCMP_CMP_UPPER_SHIFT (0x8U)
25 #define FLEXIO_MAX_PWM_CHANNELS 8
26
27 enum pwm_nxp_flexio_polarity {
28 FLEXIO_PWM_ACTIVE_HIGH = 0x0U,
29 FLEXIO_PWM_ACTIVE_LOW = 0x1U
30 };
31
32 enum pwm_nxp_flexio_timerinit {
33 /** Timer Initial output is logic one */
34 FLEXIO_PWM_TIMER_INIT_HIGH = 0x00U,
35 /** Timer Initial output is logic zero */
36 FLEXIO_PWM_TIMER_INIT_LOW = 0x1U
37 };
38
39 enum pwm_nxp_flexio_prescaler {
40 /* Decrement counter on Flexio clock */
41 FLEXIO_PWM_CLK_DIV_1 = 0U,
42 /* Decrement counter on Flexio clock divided by 16 */
43 FLEXIO_PWM_CLK_DIV_16 = 4U,
44 /* Decrement counter on Flexio clock divided by 256 */
45 FLEXIO_PWM_CLK_DIV_256 = 5U
46 };
47
48 enum pwm_nxp_flexio_timer_mode {
49 /** Timer disabled */
50 FLEXIO_PWM_TIMER_DISABLED = 0x00U,
51 /** Timer in 8 bit Pwm High mode */
52 FLEXIO_PWM_TIMER_PWM_HIGH = 0x02U,
53 /** Timer in 8 bit Pwm Low mode */
54 FLEXIO_PWM_TIMER_PWM_LOW = 0x06U
55 };
56
57 enum pwm_nxp_flexio_timer_pin {
58 /** Timer Pin output disabled */
59 FLEXIO_PWM_TIMER_PIN_OUTPUT_DISABLE = 0x00U,
60 /** Timer Pin Output mode */
61 FLEXIO_PWM_TIMER_PIN_OUTPUT_ENABLE = 0x03U
62 };
63
64 struct pwm_nxp_flexio_channel_config {
65 /** Flexio used pin index */
66 uint8_t pin_id;
67 /** Counter decrement clock prescaler */
68 enum pwm_nxp_flexio_prescaler prescaler;
69 /** Actual Prescaler divisor */
70 uint8_t prescaler_div;
71 };
72
73 struct pwm_nxp_flexio_pulse_info {
74 uint8_t pwm_pulse_channels;
75 struct pwm_nxp_flexio_channel_config *pwm_info;
76 };
77
78 struct pwm_nxp_flexio_config {
79 const struct device *flexio_dev;
80 FLEXIO_Type *flexio_base;
81 const struct pinctrl_dev_config *pincfg;
82 const struct device *clock_dev;
83 clock_control_subsys_t clock_subsys;
84 const struct pwm_nxp_flexio_pulse_info *pulse_info;
85 const struct nxp_flexio_child *child;
86 };
87
88 struct pwm_nxp_flexio_data {
89 uint32_t period_cycles[FLEXIO_MAX_PWM_CHANNELS];
90 uint32_t flexio_clk;
91 };
92
pwm_nxp_flexio_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)93 static int pwm_nxp_flexio_set_cycles(const struct device *dev,
94 uint32_t channel, uint32_t period_cycles,
95 uint32_t pulse_cycles, pwm_flags_t flags)
96 {
97 const struct pwm_nxp_flexio_config *config = dev->config;
98 struct pwm_nxp_flexio_data *data = dev->data;
99 flexio_timer_config_t timerConfig;
100 struct pwm_nxp_flexio_channel_config *pwm_info;
101 FLEXIO_Type *flexio_base = (FLEXIO_Type *)(config->flexio_base);
102 struct nxp_flexio_child *child = (struct nxp_flexio_child *)(config->child);
103 enum pwm_nxp_flexio_polarity polarity;
104
105 /* Check received parameters for sanity */
106 if (channel >= config->pulse_info->pwm_pulse_channels) {
107 LOG_ERR("Invalid channel");
108 return -EINVAL;
109 }
110
111 if (period_cycles == 0) {
112 LOG_ERR("Channel can not be set to inactive level");
113 return -ENOTSUP;
114 }
115
116 if (FLEXIO_PWM_TIMER_CMP_MAX_VALUE <= (uint16_t)pulse_cycles) {
117 LOG_ERR("Duty cycle is out of range");
118 return -EINVAL;
119 }
120
121 if (FLEXIO_PWM_TIMER_CMP_MAX_VALUE <= (uint16_t)(period_cycles - pulse_cycles)) {
122 LOG_ERR("low period of the cycle is out of range");
123 return -EINVAL;
124 }
125
126 if (pulse_cycles > period_cycles) {
127 LOG_ERR("Duty cycle cannot be greater than 100 percent");
128 return -EINVAL;
129 }
130
131 pwm_info = &config->pulse_info->pwm_info[channel];
132
133 if ((flags & PWM_POLARITY_INVERTED) == 0) {
134 polarity = FLEXIO_PWM_ACTIVE_HIGH;
135 } else {
136 polarity = FLEXIO_PWM_ACTIVE_LOW;
137 }
138
139 if (polarity == FLEXIO_PWM_ACTIVE_HIGH) {
140 timerConfig.timerOutput = kFLEXIO_TimerOutputOneNotAffectedByReset;
141 timerConfig.timerMode = kFLEXIO_TimerModeDual8BitPWM;
142
143 } else {
144 timerConfig.timerOutput = kFLEXIO_TimerOutputZeroNotAffectedByReset;
145 timerConfig.timerMode = kFLEXIO_TimerModeDual8BitPWMLow;
146 }
147
148 data->period_cycles[channel] = period_cycles;
149
150 timerConfig.timerCompare = ((uint8_t)(pulse_cycles - 1U)) |
151 ((uint8_t)(data->period_cycles[channel] - pulse_cycles - 1U)
152 << FLEXIO_PWM_TIMCMP_CMP_UPPER_SHIFT);
153
154 timerConfig.timerDecrement = pwm_info->prescaler;
155 timerConfig.timerStop = kFLEXIO_TimerStopBitDisabled;
156 timerConfig.timerEnable = kFLEXIO_TimerEnabledAlways;
157 timerConfig.timerDisable = kFLEXIO_TimerDisableNever;
158 timerConfig.timerStart = kFLEXIO_TimerStartBitDisabled;
159 timerConfig.timerReset = kFLEXIO_TimerResetNever;
160 timerConfig.triggerSource = kFLEXIO_TimerTriggerSourceInternal;
161
162 /* Enable the pin out for the selected timer */
163 timerConfig.pinConfig = FLEXIO_PWM_TIMER_PIN_OUTPUT_ENABLE;
164 timerConfig.pinPolarity = polarity;
165
166 /* Select the pin that the selected timer will output the signal on */
167 timerConfig.pinSelect = pwm_info->pin_id;
168
169 FLEXIO_SetTimerConfig(flexio_base, child->res.timer_index[channel], &timerConfig);
170
171 #if (defined(FSL_FEATURE_FLEXIO_HAS_PIN_REGISTER) && FSL_FEATURE_FLEXIO_HAS_PIN_REGISTER)
172 /* Disable pin override if active to support channels working in cases not 0% 100% */
173 if (FLEXIO_GetPinOverride(flexio_base, pwm_info->pin_id)) {
174 FLEXIO_ConfigPinOverride(flexio_base, pwm_info->pin_id, false);
175 }
176 #endif
177 return 0;
178 }
179
pwm_nxp_flexio_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)180 static int pwm_nxp_flexio_get_cycles_per_sec(const struct device *dev,
181 uint32_t channel,
182 uint64_t *cycles)
183 {
184 const struct pwm_nxp_flexio_config *config = dev->config;
185 struct pwm_nxp_flexio_data *data = dev->data;
186 struct pwm_nxp_flexio_channel_config *pwm_info;
187
188 /* If get_cycles is called directly after init */
189 if (data->period_cycles[channel] == 0) {
190 LOG_ERR("First set the period of this channel to a non zero value");
191 return -ENOTSUP;
192 }
193
194 pwm_info = &config->pulse_info->pwm_info[channel];
195 *cycles = (uint64_t)(((data->flexio_clk) * 2) /
196 ((data->period_cycles[channel]) * (pwm_info->prescaler_div)));
197
198 return 0;
199 }
200
mcux_flexio_pwm_init(const struct device * dev)201 static int mcux_flexio_pwm_init(const struct device *dev)
202 {
203 const struct pwm_nxp_flexio_config *config = dev->config;
204 struct pwm_nxp_flexio_data *data = dev->data;
205 flexio_timer_config_t timerConfig;
206 uint8_t ch_id = 0;
207 int err;
208 struct pwm_nxp_flexio_channel_config *pwm_info;
209 FLEXIO_Type *flexio_base = (FLEXIO_Type *)(config->flexio_base);
210 struct nxp_flexio_child *child = (struct nxp_flexio_child *)(config->child);
211
212 if (!device_is_ready(config->clock_dev)) {
213 return -ENODEV;
214 }
215
216 if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
217 &data->flexio_clk)) {
218 return -EINVAL;
219 }
220
221 err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
222 if (err) {
223 return err;
224 }
225
226 err = nxp_flexio_child_attach(config->flexio_dev, child);
227 if (err < 0) {
228 return err;
229 }
230
231 for (ch_id = 0; ch_id < config->pulse_info->pwm_pulse_channels; ch_id++) {
232 pwm_info = &config->pulse_info->pwm_info[ch_id];
233
234 /* Reset timer settings */
235 (void)memset(&timerConfig, 0, sizeof(timerConfig));
236 FLEXIO_SetTimerConfig(flexio_base, child->res.timer_index[ch_id], &timerConfig);
237
238 #if (defined(FSL_FEATURE_FLEXIO_HAS_PIN_REGISTER) && FSL_FEATURE_FLEXIO_HAS_PIN_REGISTER)
239 /* Reset the value driven on the corresponding pin */
240 FLEXIO_SetPinLevel(flexio_base, pwm_info->pin_id, false);
241 FLEXIO_ConfigPinOverride(flexio_base, pwm_info->pin_id, false);
242 #endif
243 /* Timer output is logic one and is not affected by timer reset */
244 timerConfig.timerOutput = kFLEXIO_TimerOutputOneNotAffectedByReset;
245 /* Set the timer mode to dual 8-bit counter PWM high */
246 timerConfig.timerMode = kFLEXIO_TimerModeDual8BitPWM;
247
248 /* Timer scaling factor w.r.t Flexio Clock */
249 timerConfig.timerDecrement = pwm_info->prescaler;
250
251 /* Program the PWM pulse */
252 timerConfig.timerCompare = 0;
253
254 /* Configure Timer CFG and CTL bits to support PWM mode */
255 timerConfig.timerStop = kFLEXIO_TimerStopBitDisabled;
256 timerConfig.timerEnable = kFLEXIO_TimerEnabledAlways;
257 timerConfig.timerDisable = kFLEXIO_TimerDisableNever;
258 timerConfig.timerStart = kFLEXIO_TimerStartBitDisabled;
259 timerConfig.timerReset = kFLEXIO_TimerResetNever;
260 timerConfig.triggerSource = kFLEXIO_TimerTriggerSourceInternal;
261
262 /* Enable the pin out and set a default polarity for the selected timer */
263 timerConfig.pinConfig = FLEXIO_PWM_TIMER_PIN_OUTPUT_ENABLE;
264 timerConfig.pinPolarity = kFLEXIO_PinActiveHigh;
265
266 /* Select the pin that the selected timer will output the signal on */
267 timerConfig.pinSelect = pwm_info->pin_id;
268
269 FLEXIO_SetTimerConfig(flexio_base, child->res.timer_index[ch_id], &timerConfig);
270 }
271
272 return 0;
273 }
274
275 static DEVICE_API(pwm, pwm_nxp_flexio_driver_api) = {
276 .set_cycles = pwm_nxp_flexio_set_cycles,
277 .get_cycles_per_sec = pwm_nxp_flexio_get_cycles_per_sec,
278 };
279
280 #define _FLEXIO_PWM_PULSE_GEN_CONFIG(n) \
281 { \
282 .pin_id = DT_PROP(n, pin_id), \
283 .prescaler = _CONCAT(FLEXIO_PWM_CLK_DIV_, DT_PROP(n, prescaler)), \
284 .prescaler_div = DT_PROP(n, prescaler), \
285 },
286
287 #define FLEXIO_PWM_PULSE_GEN_CONFIG(n) \
288 static struct pwm_nxp_flexio_channel_config flexio_pwm_##n##_init[] = { \
289 DT_INST_FOREACH_CHILD_STATUS_OKAY(n, _FLEXIO_PWM_PULSE_GEN_CONFIG) \
290 }; \
291 static const struct pwm_nxp_flexio_pulse_info flexio_pwm_##n##_info = { \
292 .pwm_pulse_channels = ARRAY_SIZE(flexio_pwm_##n##_init), \
293 .pwm_info = flexio_pwm_##n##_init, \
294 };
295
296 #define FLEXIO_PWM_TIMER_INDEX_INIT(n) \
297 static uint8_t flexio_pwm_##n##_timer_index[ARRAY_SIZE(flexio_pwm_##n##_init)];
298
299 #define FLEXIO_PWM_CHILD_CONFIG(n) \
300 static const struct nxp_flexio_child mcux_flexio_pwm_child_##n = { \
301 .isr = NULL, \
302 .user_data = NULL, \
303 .res = { \
304 .shifter_index = NULL, \
305 .shifter_count = 0, \
306 .timer_index = (uint8_t *)flexio_pwm_##n##_timer_index, \
307 .timer_count = ARRAY_SIZE(flexio_pwm_##n##_init) \
308 } \
309 };
310
311 #define FLEXIO_PWM_PULSE_GEN_GET_CONFIG(n) \
312 .pulse_info = &flexio_pwm_##n##_info,
313
314
315 #define PWM_NXP_FLEXIO_PWM_INIT(n) \
316 PINCTRL_DT_INST_DEFINE(n); \
317 FLEXIO_PWM_PULSE_GEN_CONFIG(n) \
318 FLEXIO_PWM_TIMER_INDEX_INIT(n) \
319 FLEXIO_PWM_CHILD_CONFIG(n) \
320 static const struct pwm_nxp_flexio_config pwm_nxp_flexio_config_##n = { \
321 .flexio_dev = DEVICE_DT_GET(DT_INST_PARENT(n)), \
322 .flexio_base = (FLEXIO_Type *)DT_REG_ADDR(DT_INST_PARENT(n)), \
323 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
324 .clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(n))), \
325 .clock_subsys = (clock_control_subsys_t)DT_CLOCKS_CELL(DT_INST_PARENT(n), name),\
326 .child = &mcux_flexio_pwm_child_##n, \
327 FLEXIO_PWM_PULSE_GEN_GET_CONFIG(n) \
328 }; \
329 \
330 static struct pwm_nxp_flexio_data pwm_nxp_flexio_data_##n; \
331 DEVICE_DT_INST_DEFINE(n, \
332 &mcux_flexio_pwm_init, \
333 NULL, \
334 &pwm_nxp_flexio_data_##n, \
335 &pwm_nxp_flexio_config_##n, \
336 POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
337 &pwm_nxp_flexio_driver_api);
338
339 DT_INST_FOREACH_STATUS_OKAY(PWM_NXP_FLEXIO_PWM_INIT)
340