1 /*
2  * Copyright (c) 2019, Linaro
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_imx_pwm
8 
9 #include <errno.h>
10 #include <zephyr/drivers/pwm.h>
11 #include <zephyr/drivers/clock_control.h>
12 #include <soc.h>
13 #include <fsl_pwm.h>
14 #include <zephyr/drivers/pinctrl.h>
15 #include <zephyr/kernel.h>
16 
17 #include <zephyr/logging/log.h>
18 
19 LOG_MODULE_REGISTER(pwm_mcux, CONFIG_PWM_LOG_LEVEL);
20 
21 #define CHANNEL_COUNT 2
22 
23 struct pwm_mcux_config {
24 	PWM_Type *base;
25 	uint8_t index;
26 	const struct device *clock_dev;
27 	clock_control_subsys_t clock_subsys;
28 	pwm_clock_prescale_t prescale;
29 	pwm_register_reload_t reload;
30 	pwm_mode_t mode;
31 	bool run_wait;
32 	bool run_debug;
33 	const struct pinctrl_dev_config *pincfg;
34 };
35 
36 struct pwm_mcux_data {
37 	uint32_t period_cycles[CHANNEL_COUNT];
38 	pwm_signal_param_t channel[CHANNEL_COUNT];
39 	struct k_mutex lock;
40 };
41 
mcux_pwm_set_cycles_internal(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)42 static int mcux_pwm_set_cycles_internal(const struct device *dev, uint32_t channel,
43 			       uint32_t period_cycles, uint32_t pulse_cycles,
44 			       pwm_flags_t flags)
45 {
46 	const struct pwm_mcux_config *config = dev->config;
47 	struct pwm_mcux_data *data = dev->data;
48 	pwm_level_select_t level;
49 
50 	if (flags & PWM_POLARITY_INVERTED) {
51 		level = kPWM_LowTrue;
52 	} else {
53 		level = kPWM_HighTrue;
54 	}
55 
56 	if (period_cycles != data->period_cycles[channel]
57 	    || level != data->channel[channel].level) {
58 		uint32_t clock_freq;
59 		status_t status;
60 
61 		data->period_cycles[channel] = period_cycles;
62 
63 		if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
64 				&clock_freq)) {
65 			return -EINVAL;
66 		}
67 
68 		data->channel[channel].pwmchannelenable = true;
69 
70 		PWM_StopTimer(config->base, 1U << config->index);
71 
72 		/*
73 		 * We will directly write the duty cycle pulse width
74 		 * and full pulse width into the VALx registers to
75 		 * setup PWM with higher resolution.
76 		 * Therefore we use dummy values for the duty cycle
77 		 * and frequency.
78 		 */
79 		data->channel[channel].dutyCyclePercent = 0;
80 		data->channel[channel].level = level;
81 
82 		status = PWM_SetupPwm(config->base, config->index,
83 				      &data->channel[channel], 1U,
84 				      config->mode, 1U, clock_freq);
85 		if (status != kStatus_Success) {
86 			LOG_ERR("Could not set up pwm");
87 			return -ENOTSUP;
88 		}
89 
90 		/* Setup VALx values directly for edge aligned PWM */
91 		if (channel == 0) {
92 			/* Side A */
93 			PWM_SetVALxValue(config->base, config->index,
94 					 kPWM_ValueRegister_0,
95 					 (uint16_t)(period_cycles / 2U));
96 			PWM_SetVALxValue(config->base, config->index,
97 					 kPWM_ValueRegister_1,
98 					 (uint16_t)(period_cycles - 1U));
99 			PWM_SetVALxValue(config->base, config->index,
100 					 kPWM_ValueRegister_2, 0U);
101 			PWM_SetVALxValue(config->base, config->index,
102 					 kPWM_ValueRegister_3,
103 					 (uint16_t)pulse_cycles);
104 		} else {
105 			/* Side B */
106 			PWM_SetVALxValue(config->base, config->index,
107 					 kPWM_ValueRegister_0,
108 					 (uint16_t)(period_cycles / 2U));
109 			PWM_SetVALxValue(config->base, config->index,
110 					 kPWM_ValueRegister_1,
111 					 (uint16_t)(period_cycles - 1U));
112 			PWM_SetVALxValue(config->base, config->index,
113 					 kPWM_ValueRegister_4, 0U);
114 			PWM_SetVALxValue(config->base, config->index,
115 					 kPWM_ValueRegister_5,
116 					 (uint16_t)pulse_cycles);
117 		}
118 
119 		PWM_SetPwmLdok(config->base, 1U << config->index, true);
120 
121 		PWM_StartTimer(config->base, 1U << config->index);
122 	} else {
123 		/* Setup VALx values directly for edge aligned PWM */
124 		if (channel == 0) {
125 			/* Side A */
126 			PWM_SetVALxValue(config->base, config->index,
127 					 kPWM_ValueRegister_2, 0U);
128 			PWM_SetVALxValue(config->base, config->index,
129 					 kPWM_ValueRegister_3,
130 					 (uint16_t)pulse_cycles);
131 		} else {
132 			/* Side B */
133 			PWM_SetVALxValue(config->base, config->index,
134 					 kPWM_ValueRegister_4, 0U);
135 			PWM_SetVALxValue(config->base, config->index,
136 					 kPWM_ValueRegister_5,
137 					 (uint16_t)pulse_cycles);
138 		}
139 		PWM_SetPwmLdok(config->base, 1U << config->index, true);
140 	}
141 
142 	return 0;
143 }
144 
mcux_pwm_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)145 static int mcux_pwm_set_cycles(const struct device *dev, uint32_t channel,
146 			       uint32_t period_cycles, uint32_t pulse_cycles,
147 			       pwm_flags_t flags)
148 {
149 	struct pwm_mcux_data *data = dev->data;
150 	int result;
151 
152 	if (channel >= CHANNEL_COUNT) {
153 		LOG_ERR("Invalid channel");
154 		return -EINVAL;
155 	}
156 
157 	if (period_cycles == 0) {
158 		LOG_ERR("Channel can not be set to inactive level");
159 		return -ENOTSUP;
160 	}
161 
162 	if (period_cycles > UINT16_MAX) {
163 		/* 16-bit resolution */
164 		LOG_ERR("Too long period (%u), adjust pwm prescaler!",
165 			period_cycles);
166 		/* TODO: dynamically adjust prescaler */
167 		return -EINVAL;
168 	}
169 	k_mutex_lock(&data->lock, K_FOREVER);
170 	result = mcux_pwm_set_cycles_internal(dev, channel, period_cycles, pulse_cycles, flags);
171 	k_mutex_unlock(&data->lock);
172 	return result;
173 }
174 
mcux_pwm_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)175 static int mcux_pwm_get_cycles_per_sec(const struct device *dev,
176 				       uint32_t channel, uint64_t *cycles)
177 {
178 	const struct pwm_mcux_config *config = dev->config;
179 	uint32_t clock_freq;
180 
181 	if (clock_control_get_rate(config->clock_dev, config->clock_subsys,
182 			&clock_freq)) {
183 		return -EINVAL;
184 	}
185 	*cycles = clock_freq >> config->prescale;
186 
187 	return 0;
188 }
189 
pwm_mcux_init(const struct device * dev)190 static int pwm_mcux_init(const struct device *dev)
191 {
192 	const struct pwm_mcux_config *config = dev->config;
193 	struct pwm_mcux_data *data = dev->data;
194 	pwm_config_t pwm_config;
195 	status_t status;
196 	int i, err;
197 
198 	k_mutex_init(&data->lock);
199 
200 	if (!device_is_ready(config->clock_dev)) {
201 		LOG_ERR("clock control device not ready");
202 		return -ENODEV;
203 	}
204 
205 	err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
206 	if (err < 0) {
207 		return err;
208 	}
209 
210 	LOG_DBG("Set prescaler %d, reload mode %d",
211 			1 << config->prescale, config->reload);
212 
213 	PWM_GetDefaultConfig(&pwm_config);
214 	pwm_config.prescale = config->prescale;
215 	pwm_config.reloadLogic = config->reload;
216 	pwm_config.clockSource = kPWM_BusClock;
217 	pwm_config.enableDebugMode = config->run_debug;
218 #if !defined(FSL_FEATURE_PWM_HAS_NO_WAITEN) || (!FSL_FEATURE_PWM_HAS_NO_WAITEN)
219 	pwm_config.enableWait = config->run_wait;
220 #endif
221 
222 	status = PWM_Init(config->base, config->index, &pwm_config);
223 	if (status != kStatus_Success) {
224 		LOG_ERR("Unable to init PWM");
225 		return -EIO;
226 	}
227 
228 	/* Disable fault sources */
229 	for (i = 0; i < FSL_FEATURE_PWM_FAULT_CH_COUNT; i++) {
230 		config->base->SM[config->index].DISMAP[i] = 0x0000;
231 	}
232 
233 	data->channel[0].pwmChannel = kPWM_PwmA;
234 	data->channel[0].level = kPWM_HighTrue;
235 	data->channel[1].pwmChannel = kPWM_PwmB;
236 	data->channel[1].level = kPWM_HighTrue;
237 
238 	return 0;
239 }
240 
241 static DEVICE_API(pwm, pwm_mcux_driver_api) = {
242 	.set_cycles = mcux_pwm_set_cycles,
243 	.get_cycles_per_sec = mcux_pwm_get_cycles_per_sec,
244 };
245 
246 #define PWM_DEVICE_INIT_MCUX(n)			  \
247 	static struct pwm_mcux_data pwm_mcux_data_ ## n;		  \
248 	PINCTRL_DT_INST_DEFINE(n);					  \
249 									  \
250 	static const struct pwm_mcux_config pwm_mcux_config_ ## n = {     \
251 		.base = (PWM_Type *)DT_REG_ADDR(DT_INST_PARENT(n)),	  \
252 		.index = DT_INST_PROP(n, index),			  \
253 		.mode = kPWM_EdgeAligned,				  \
254 		.prescale = _CONCAT(kPWM_Prescale_Divide_, DT_INST_PROP(n, nxp_prescaler)),\
255 		.reload = DT_ENUM_IDX_OR(DT_DRV_INST(n), nxp_reload,\
256 				kPWM_ReloadPwmFullCycle),\
257 		.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)),		\
258 		.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name),\
259 		.run_wait = DT_INST_PROP(n, run_in_wait),		  \
260 		.run_debug = DT_INST_PROP(n, run_in_debug),		  \
261 		.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),		  \
262 	};								  \
263 									  \
264 	DEVICE_DT_INST_DEFINE(n,					  \
265 			    pwm_mcux_init,				  \
266 			    NULL,					  \
267 			    &pwm_mcux_data_ ## n,			  \
268 			    &pwm_mcux_config_ ## n,			  \
269 			    POST_KERNEL, CONFIG_PWM_INIT_PRIORITY,	  \
270 			    &pwm_mcux_driver_api);
271 
272 DT_INST_FOREACH_STATUS_OKAY(PWM_DEVICE_INIT_MCUX)
273