1 /*
2 * Copyright (c) 2006-2024, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2024-02-26 Yilin Sun Initial version.
9 */
10
11 #include <rtthread.h>
12 #include <rtdevice.h>
13
14 #include "fsl_ctimer.h"
15
16 #define MCX_PWM_MATCH_COUNT 4
17
18 #ifdef RT_USING_PWM
19
20 typedef struct
21 {
22 struct rt_device_pwm pwm_device;
23 CTIMER_Type *ct_instance;
24 uint32_t counter_period_ps;
25 } mcx_pwm_obj_t;
26
27 static CTIMER_Type *mcx_pwm_instances[] = CTIMER_BASE_PTRS;
28
29 static mcx_pwm_obj_t mcx_pwm_list[ARRAY_SIZE(mcx_pwm_instances)];
30
mcx_pwm_current_period_channel(mcx_pwm_obj_t * pwm)31 static int mcx_pwm_current_period_channel(mcx_pwm_obj_t *pwm)
32 {
33 CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
34
35 uint32_t mcr = ct->MCR;
36
37 for (uint8_t i = 0; i < 4; i++)
38 {
39 if (mcr & (1U << (3 * i + CTIMER_MCR_MR0R_SHIFT)))
40 {
41 return i;
42 }
43 }
44
45 return -1;
46 }
47
mcx_pwm_first_free_channel(mcx_pwm_obj_t * pwm)48 static int mcx_pwm_first_free_channel(mcx_pwm_obj_t *pwm)
49 {
50 CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
51
52 int pc = mcx_pwm_current_period_channel(pwm);
53
54 uint32_t pwmc = ct->PWMC;
55
56 for (uint8_t i = 0; i < 4; i++)
57 {
58 if (pwmc & (1U << i))
59 {
60 /* Skip this channel if there's an active PWM output */
61 continue;
62 }
63
64 if (i == pc)
65 {
66 /* Skip this channel if this channel is the current period channel */
67 continue;
68 }
69
70 return i;
71 }
72
73 /* There are no free channels left. */
74 return -1;
75 }
76
mcx_pwm_period_set(mcx_pwm_obj_t * pwm,uint32_t period_ns)77 static int mcx_pwm_period_set(mcx_pwm_obj_t *pwm, uint32_t period_ns)
78 {
79 CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
80
81 int p_channel = mcx_pwm_current_period_channel(pwm);
82 if (p_channel < 0)
83 {
84 return -EINVAL;
85 }
86
87 /* Store new values in shadow registers */
88 ct->MSR[p_channel] = period_ns * 1000 / pwm->counter_period_ps;
89
90 /* Enable period channel interrupt to check a reload event occurs.
91 * Since interrupts are not configured from NVIC, so no ISR will occur.
92 * Check IR[MRnINT] for reload point.
93 */
94 uint32_t mcr_mask = (CTIMER_MCR_MR0RL_MASK << p_channel) | (CTIMER_MCR_MR0I_MASK << (3 * p_channel));
95
96 for (uint8_t i = 0; i < 4; i++)
97 {
98 if (ct->PWMC & (1U << i))
99 {
100 /* Channel PWM output is enabled, calculate new values and store into shadow registers */
101 uint32_t new_mr = ct->MR[i] * ct->MSR[p_channel] / ct->MR[p_channel];
102 ct->MSR[i] = new_mr;
103
104 /* Update MRnRL map */
105 mcr_mask |= CTIMER_MCR_MR0RL_MASK << i;
106 }
107 }
108
109 /* Reload MRs on next counter reset, enable reload MR interrupt */
110 ct->MCR |= mcr_mask;
111
112 while ((ct->IR & (CTIMER_IR_MR0INT_MASK << p_channel)) == 0U)
113 {
114 /* -- */
115 }
116
117 /* Disable reload channel interrupt and MSR synchronization */
118 ct->MCR &= ~mcr_mask;
119
120 /* Clear interrupt flags. */
121 ct->IR |= (CTIMER_IR_MR0INT_MASK << p_channel);
122
123 return 0;
124 }
125
mcx_pwm_period_get(mcx_pwm_obj_t * pwm,uint32_t * period_ns)126 static int mcx_pwm_period_get(mcx_pwm_obj_t *pwm, uint32_t *period_ns)
127 {
128 CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
129
130 int p_channel = mcx_pwm_current_period_channel(pwm);
131 if (p_channel < 0)
132 {
133 return -1;
134 }
135
136 *period_ns = ct->MR[p_channel] * pwm->counter_period_ps / 1000;
137
138 return 0;
139 }
140
mcx_pwm_pulse_set(mcx_pwm_obj_t * pwm,uint8_t channel,uint32_t pulse_ns)141 static int mcx_pwm_pulse_set(mcx_pwm_obj_t *pwm, uint8_t channel, uint32_t pulse_ns)
142 {
143 CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
144
145 int p_channel = mcx_pwm_current_period_channel(pwm);
146 if (p_channel < 0)
147 {
148 return -1;
149 }
150
151 /* Up-counting counters, the polarity is inversed */
152 ct->MSR[channel] = ct->MR[p_channel] - pulse_ns * 1000 / pwm->counter_period_ps;
153
154 /* Reload MRn on the next cycle */
155 ct->MCR |= (CTIMER_MCR_MR0RL_MASK << channel);
156
157 /* Wait for new duty cycle loaded into the MRn */
158 while (ct->MR[channel] != ct->MSR[channel])
159 {
160 /* -- */
161 }
162
163 /* Disable shadow register updates */
164 ct->MCR &= ~(CTIMER_MCR_MR0RL_MASK << channel);
165
166 return 0;
167 }
168
mcx_pwm_pulse_get(mcx_pwm_obj_t * pwm,uint8_t channel,uint32_t * pulse_ns)169 static int mcx_pwm_pulse_get(mcx_pwm_obj_t *pwm, uint8_t channel, uint32_t *pulse_ns)
170 {
171 CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
172
173 int p_channel = mcx_pwm_current_period_channel(pwm);
174 if (p_channel < 0)
175 {
176 return -1;
177 }
178
179 /* Up-counting counters, the polarity is inversed */
180 *pulse_ns = (ct->MR[p_channel] - ct->MR[channel]) * pwm->counter_period_ps / 1000;
181
182 return 0;
183 }
184
mcx_drv_pwm_get(mcx_pwm_obj_t * pwm,struct rt_pwm_configuration * configuration)185 static rt_err_t mcx_drv_pwm_get(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
186 {
187 if (mcx_pwm_period_get(pwm, &configuration->period) < 0)
188 {
189 return -RT_EFAULT;
190 }
191
192 if (mcx_pwm_pulse_get(pwm, configuration->channel, &configuration->pulse) < 0)
193 {
194 return -RT_EFAULT;
195 }
196
197 return RT_EOK;
198 }
199
mcx_drv_pwm_set(mcx_pwm_obj_t * pwm,struct rt_pwm_configuration * configuration)200 static rt_err_t mcx_drv_pwm_set(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
201 {
202 CTIMER_Type *ct = pwm->ct_instance;
203
204 uint32_t period = configuration->period * 1000 / pwm->counter_period_ps;
205 uint8_t channel = configuration->channel;
206
207 if ((ct->TCR & CTIMER_TCR_CEN_MASK) == 0U)
208 {
209 /* There's two conditions for a all-zero TCR: either a reset condition or timer is stopped. */
210 /* In either case, we need to initialize the timer instance (AHB RST CTRL). */
211
212 /* TODO: Do not use SDK functions */
213
214 ctimer_config_t ct_cfg =
215 {
216 .mode = kCTIMER_TimerMode,
217 .prescale = 1U,
218 };
219
220 /* Frequency: 150MHz max., we got 32bit counters, we can take that. */
221 /* Approx. maximum period: 28.6 seconds. */
222
223 CTIMER_Init(ct, &ct_cfg);
224
225 /* Current timer is not running, we are the first channel being configured. */
226
227 ct->TC = 0U; /* Reset counter */
228 ct->PC = 0U; /* Reset prescaler counter */
229 ct->PR = 0U; /* Prescaler, divide by 1 to get best resolution */
230 ct->MCR = 0U; /* Reset interrupt and reset condition */
231 ct->EMR = 0U; /* Do nothing on match event and output 0 as default state */
232 ct->PWMC = 0U; /* Disable all PWM channels, outputs will be controlled by EMn */
233
234 /* Here, we have a favoritism of using channel 3 as period channel, unless channel 3 is used for output */
235 if (channel != 3)
236 {
237 ct->MR[3] = period;
238 ct->MCR |= CTIMER_MCR_MR3R_MASK;
239 }
240 else
241 {
242 /* Use channel 2 as period channel. */
243 ct->MR[2] = period;
244 ct->MCR |= CTIMER_MCR_MR2R_MASK;
245 }
246
247 /* Start counter */
248 ct->TCR |= CTIMER_TCR_CEN_MASK;
249 }
250 else
251 {
252 /*
253 * Due to the nature of the CTimer, one of the 4 match channels is needed for period control (frequency)
254 * To find out which one is the current period channel, check the MRxR bit for each match output.
255 * If we are configuring the same match being used as periodic channel, configure the next free match as period
256 * then current channel can be re-used. If all 4 channels are in use then the function will fail with an errno.
257 */
258
259 /* The timer is running, check whether we need to re-locate the period channel */
260 int p_channel = mcx_pwm_current_period_channel(pwm);
261 if (p_channel < 0)
262 {
263 return -RT_EINVAL;
264 }
265
266 if (p_channel == channel)
267 {
268 /* We need to re-locate the period channel */
269
270 int f_channel = mcx_pwm_first_free_channel(pwm);
271 if (f_channel < 0)
272 {
273 /* There's no free channel, bail out. */
274 return -RT_EBUSY;
275 }
276
277 /* Transfer the period channel to first free channel */
278
279 /* Step 1: Copy current period to first free channel */
280 ct->MR[f_channel] = ct->MR[p_channel];
281
282 /* Step 2: Enable reset for new period channel */
283 /* Note: it's safe doing it here since both old and new channel MRs contains same value */
284 ct->MCR |= (CTIMER_MCR_MR0R_MASK << (3 * f_channel));
285
286 /* Step 3: Disable reset for old period channel */
287 ct->MCR &= ~(CTIMER_MCR_MR0R_MASK << (3 * p_channel));
288
289 /* The old period channel is now available for PWM output */
290 p_channel = f_channel;
291 }
292
293 if (mcx_pwm_period_set(pwm, configuration->period) < 0)
294 {
295 return -RT_EINVAL;
296 }
297 }
298
299 if (mcx_pwm_pulse_set(pwm, channel, configuration->pulse) < 0)
300 {
301 return -RT_EINVAL;
302 }
303
304 return 0;
305 }
306
mcx_drv_pwm_enable(mcx_pwm_obj_t * pwm,struct rt_pwm_configuration * configuration)307 static rt_err_t mcx_drv_pwm_enable(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
308 {
309 CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
310
311 ct->PWMC |= (1U << configuration->channel);
312
313 return 0;
314 }
315
mcx_drv_pwm_disable(mcx_pwm_obj_t * pwm,struct rt_pwm_configuration * configuration)316 static rt_err_t mcx_drv_pwm_disable(mcx_pwm_obj_t *pwm, struct rt_pwm_configuration *configuration)
317 {
318 CTIMER_Type *ct = (CTIMER_Type *)pwm->ct_instance;
319
320 ct->PWMC &= ~(1U << configuration->channel);
321
322 return 0;
323 }
324
mcx_drv_pwm_control(struct rt_device_pwm * device,int cmd,void * args)325 static rt_err_t mcx_drv_pwm_control(struct rt_device_pwm *device, int cmd, void *args)
326 {
327 mcx_pwm_obj_t *pwm = device->parent.user_data;
328 struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)args;
329
330 switch (cmd)
331 {
332 case PWM_CMD_ENABLE:
333 return mcx_drv_pwm_enable(pwm, configuration);
334
335 case PWM_CMD_DISABLE:
336 return mcx_drv_pwm_disable(pwm, configuration);
337
338 case PWM_CMD_SET:
339 return mcx_drv_pwm_set(pwm, configuration);
340
341 case PWM_CMD_GET:
342 return mcx_drv_pwm_get(pwm, configuration);
343
344 default:
345 return -RT_EINVAL;
346 }
347
348 return RT_EOK;
349 }
350
351 static struct rt_pwm_ops mcx_pwm_ops =
352 {
353 .control = mcx_drv_pwm_control,
354 };
355
mcx_pwm_init(void)356 int mcx_pwm_init(void)
357 {
358 rt_err_t ret;
359 char name_buf[8];
360
361 for (uint8_t i = 0; i < ARRAY_SIZE(mcx_pwm_instances); i++)
362 {
363 mcx_pwm_list[i].ct_instance = mcx_pwm_instances[i];
364 mcx_pwm_list[i].counter_period_ps = 1000000000000ULL / CLOCK_GetCTimerClkFreq(i);
365
366 rt_snprintf(name_buf, sizeof(name_buf), "pwm%d", i);
367
368 ret = rt_device_pwm_register(&mcx_pwm_list[i].pwm_device, name_buf, &mcx_pwm_ops, &mcx_pwm_list[i]);
369 if (ret != RT_EOK)
370 {
371 return ret;
372 }
373 }
374
375 return RT_EOK;
376 }
377
378 INIT_DEVICE_EXPORT(mcx_pwm_init);
379
380 #endif /* RT_USING_PWM */
381