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