1 /*
2 * Copyright 2024 Microchip Technology Inc. and its subsidiaries.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <stddef.h>
7 #include <stdint.h>
8
9 #include <device_mec5.h>
10 #include "mec_pcfg.h"
11 #include "mec_defs.h"
12 #include "mec_pcr_api.h"
13 #include "mec_pwm_api.h"
14 #include "mec_retval.h"
15
16 /* Two frequency sources: 48MHz and 100KHz(configurable). Need PCR API */
17
18 #define MEC5_PWM_INSTANCE_SPACING 0x10u
19
20 #define MEC5_PWM_FIN_HIGH 48000000U
21
22 // MEC5_PWM_INSTANCES 9 or 12
23
24 /* MEC5 PWM
25 * PWM Frequency source is either the 48MHz PLL ouput(default) or the PCR
26 * slow clock output. PCR slow clock is the 48MHz PLL divided by a 10-bit
27 * value in the PCR block. We do not change the slow clock since it is used by
28 * other peripherals.
29 *
30 * PWM Frequency = (1 / prescaler + 1) * Fin / ((Count_on + 1) + (Count_off + 1))
31 *
32 * Duty cycle = (Count_on + 1) / ((Count_on + 1) + (Count_off + 1))
33 *
34 * prescaler is a 4-bit field in PWM.Config
35 * Count_on and Count_off are 16-bit value.
36 */
37
38 struct mec5_pwm_info {
39 uintptr_t base;
40 uint16_t pcr_id;
41 };
42
43 static const struct mec5_pwm_info pwm_instances[] = {
44 { MEC_PWM0_BASE, MEC_PCR_PWM0, },
45 { MEC_PWM1_BASE, MEC_PCR_PWM1, },
46 { MEC_PWM2_BASE, MEC_PCR_PWM2, },
47 { MEC_PWM3_BASE, MEC_PCR_PWM3, },
48 { MEC_PWM4_BASE, MEC_PCR_PWM4, },
49 { MEC_PWM5_BASE, MEC_PCR_PWM5, },
50 { MEC_PWM6_BASE, MEC_PCR_PWM6, },
51 { MEC_PWM7_BASE, MEC_PCR_PWM7, },
52 { MEC_PWM8_BASE, MEC_PCR_PWM8, },
53 #if MEC5_PWM_INSTANCES == 12
54 { MEC_PWM9_BASE, MEC_PCR_PWM9, },
55 { MEC_PWM10_BASE, MEC_PCR_PWM10, },
56 { MEC_PWM11_BASE, MEC_PCR_PWM11, },
57 #endif
58 };
59
get_pwm_info(uintptr_t base)60 static struct mec5_pwm_info const *get_pwm_info(uintptr_t base)
61 {
62 for (size_t n = 0u; n < MEC5_PWM_INSTANCES; n++) {
63 const struct mec5_pwm_info *p = &pwm_instances[n];
64
65 if (p->base == base) {
66 return p;
67 }
68 }
69
70 return NULL;
71 }
72
pwm_disable(struct mec_pwm_regs * regs)73 static void pwm_disable(struct mec_pwm_regs *regs)
74 {
75 regs->CONFIG &= (uint32_t)~MEC_BIT(MEC_PWM_CONFIG_ENABLE_Pos);
76 }
77
pwm_enable(struct mec_pwm_regs * regs)78 static void pwm_enable(struct mec_pwm_regs *regs)
79 {
80 regs->CONFIG |= MEC_BIT(MEC_PWM_CONFIG_ENABLE_Pos);
81 }
82
83 /* set output to inactive state based upon invert bit */
pwm_off(struct mec_pwm_regs * regs)84 static void pwm_off(struct mec_pwm_regs *regs)
85 {
86 regs->CNT_ON = 0u;
87 regs->CNT_OFF = 1u;
88 }
89
90 /* set output to active state based upon invert bit */
pwm_on(struct mec_pwm_regs * regs)91 static void pwm_on(struct mec_pwm_regs *regs)
92 {
93 regs->CNT_OFF = 0u;
94 regs->CNT_ON = 1u;
95 }
96
97 /* MEC5 PWM output frequency depends upon three parameters:
98 * Eq 1. Fpwm = Fin / ((ps+1) * ((cnt_on+1) + (cnt_off+1)))
99 * Eq 2. duty cycle = (cnt_on+1) / ((cnt_on+1) + (cnt_off+1))
100 * ps = 4-bit prescale value: 0 <= ps <= 15
101 * cnt_on and cnt_off are 16-bit register values where the value 0
102 * has special meaning:
103 * cnt_on != 0 and cnt_off == 0 means set PWM output to active value.
104 * cnt_on == 0 and cnt_off != 0 means set PWM output to inactive value.
105 * The active value depends upon the invert bit in the configuration register.
106 * Config.invert = 0. active value = 1 (high)
107 * Config.invert = 1. active value = 0 (low)
108 *
109 * The denominator of 1 represents the number of Fin cycles in a PWM period.
110 * period_cycles = (ps+1) * (cnt_on+1) + (ps+1) * (cnt_off+1)
111 * where pulse_cycles is the number of Fin cycles in the active state and
112 * pulse_off_cycles is the number of Fin cycles in the inactive state.
113 *
114 * pulse_cycles = (ps+1) * (cnt_on+1)
115 * pulse_off_cycles = period_cycles - pulse_cycles = (ps+1) * (cnt_off+1)
116 *
117 * Given period_cycles and pulse_cycles as 32-bit unsigned values and knowing
118 * Fin is either 48MHz or the current PCR slow clock value (48MHz / slow_divider)
119 * We iterate until 0 <= cnt_on and cnt_off <= 0xffff.
120 * If either cnt_on or cnt_off goes to 0 then the HW can't support the request
121 * and we return an error.
122 *
123 * NOTE: HW updates new values written to cnt_on and cnt_off registers are kept
124 * in internal holding registers until the internal counter completes the OFF
125 * time count. Changes to the prescaler in the Config register will take effect
126 * immediately. The prescaler controls the rate of the internal counter.
127 *
128 * Fin = 48MHz
129 * Max Fpwm(ps=0, cnt_on=1, cnt_off=1) = 48e6 / 4 = 12 MHz
130 * Min Fpwm(ps=15, cnt_on=65535, cnt_off=65535) = 48e6 / (16 * 131072) = 22.9 Hz
131 * Fin = PCR slow clock defaults to 100KHz but could be modified by application.
132 * Max Fpwm(ps=0, cnt_on=1, cnt_off=1) = 100e3 / 4 = 25 KHz
133 * Min Fpwm(ps=15, cnt_on=65535, cnt_off=65535) = 100e3 / (16 * 131072) = 47.7 mHz (T=20.97 s)
134 *
135 * Max Freq divider = 4
136 * Min Freq divider = 16 * 131072 = 2097152 (0x200000)
137 *
138 * Example: Fin=48MHz period_cycles = 30,000,000. pulse_cycles = 15,000,000
139 * Fpwm = 48e6 / 30e6 = 1.6 which is less than 4.
140 *
141 * Fin=100KHz period_cycles = 30,000,000. pulse_cycles = 15,000,000
142 * Scale period_cycles and pulse_cycles?
143 * period_cycles = (100e3/48e6) * 30,000,000 = 62500 which is < 65536
144 * pulse_cycles = (100e3/48e6) * 15,000,000 = 31250 which is < 65536
145 * pulse_off_cycles = 62500 - 31250 = 31250
146 * Fpwm = 100e3 / (1 * (31250 + 31250)) = 1.6
147 *
148 */
prog_pwm_fd(struct mec_pwm_regs * regs,uint32_t period_cycles,uint32_t pulse_cycles)149 static int prog_pwm_fd(struct mec_pwm_regs *regs, uint32_t period_cycles, uint32_t pulse_cycles)
150 {
151 uint32_t perc, pulc, ps1, cnt_on, cnt_off, cfg, fin, fpwm, flags;
152
153 flags = 0u;
154 fin = MEC5_PWM_FIN_HIGH;
155 perc = period_cycles;
156 pulc = pulse_cycles;
157
158 fpwm = fin / perc;
159 if (fpwm < 4u) {
160 fin = mec_hal_pcr_slow_clock_freq_get();
161 if (!fin) { /* slow clock turned off! */
162 return MEC_RET_ERR;
163 }
164 /* adjust perc and pulc */
165 perc = (uint32_t)(((uint64_t)fin * (uint64_t)period_cycles) / MEC5_PWM_FIN_HIGH);
166 pulc = (uint32_t)(((uint64_t)fin * (uint64_t)pulse_cycles) / MEC5_PWM_FIN_HIGH);
167 flags |= MEC_BIT(0);
168 }
169
170 for (ps1 = 1u; ps1 < 17u; ps1++) {
171 cnt_on = pulc / ps1;
172 cnt_off = (perc - pulc) / ps1;
173 if ((cnt_on < 0x10000u) && (cnt_off < 0x10000u)) {
174 break;
175 }
176 }
177
178 if ((ps1 > 16u) || (cnt_on < 2u) || (cnt_off < 2u)) {
179 return MEC_RET_ERR;
180 }
181
182 ps1--;
183 cnt_on--;
184 cnt_off--;
185 cfg = regs->CONFIG & (uint32_t)~(MEC_PWM_CONFIG_CLKDIV_Msk | MEC_PWM_CONFIG_CLK_SRC_SLOW_Msk);
186 cfg |= ((ps1 << MEC_PWM_CONFIG_CLKDIV_Pos) & MEC_PWM_CONFIG_CLKDIV_Msk);
187 if (flags & MEC_BIT(0)) {
188 cfg |= MEC_BIT(MEC_PWM_CONFIG_CLK_SRC_SLOW_Pos);
189 }
190 regs->CNT_ON = cnt_on;
191 regs->CNT_OFF = cnt_off;
192 regs->CONFIG = cfg;
193
194 return MEC_RET_OK;
195 }
196
prog_pwm(struct mec_pwm_regs * regs,uint32_t period_cycles,uint32_t pulse_cycles)197 static int prog_pwm(struct mec_pwm_regs *regs, uint32_t period_cycles, uint32_t pulse_cycles)
198 {
199 if (!period_cycles && !pulse_cycles) { /* both 0 disable PWM */
200 pwm_disable(regs);
201 return MEC_RET_OK;
202 }
203
204 /* period_cycles is non-zero and pulse_cycles is zero set output to inactive(off) state */
205 if (!period_cycles || !pulse_cycles) {
206 pwm_off(regs);
207 return MEC_RET_OK;
208 }
209
210 /* period_cycles is non-zero and pulse_cycles is zero set output to active(on) state */
211 if (period_cycles == pulse_cycles) {
212 pwm_on(regs);
213 return MEC_RET_OK;
214 }
215
216 return prog_pwm_fd(regs, period_cycles, pulse_cycles);
217 }
218
219 /* ---- Public API ---- */
220
221 /* Return the maximum PWM frequency in cycles per second for the high
222 * frequency range. PWM Finput = 48MHz PLL output.
223 */
mec_hal_pwm_hi_freq_input(void)224 uint32_t mec_hal_pwm_hi_freq_input(void)
225 {
226 return (uint32_t)MEC5_PWM_FIN_HIGH;
227 }
228
229 /* Return the maximum PWM frequency in cycles per second for the low
230 * frequency range. PWM Finput = PCR slow clock (divided down 48MHz PLL output).
231 * NOTE: slow clock defaults to 100KHz and is used by other peripherals. The
232 * slow clock divider in the PCR block is programmable.
233 */
mec_hal_pwm_lo_freq_input(void)234 uint32_t mec_hal_pwm_lo_freq_input(void)
235 {
236 return mec_hal_pcr_slow_clock_freq_get();
237 }
238
239 /* set output to inactive state based upon invert bit */
mec_hal_pwm_off(struct mec_pwm_regs * regs)240 int mec_hal_pwm_off(struct mec_pwm_regs *regs)
241 {
242 if (!regs) {
243 return MEC_RET_ERR_INVAL;
244 }
245
246 pwm_off(regs);
247
248 return MEC_RET_OK;
249 }
250
251 /* set output to active state based upon invert bit */
mec_hal_pwm_on(struct mec_pwm_regs * regs)252 int mec_hal_pwm_on(struct mec_pwm_regs *regs)
253 {
254 if (!regs) {
255 return MEC_RET_ERR_INVAL;
256 }
257
258 pwm_on(regs);
259
260 return MEC_RET_OK;
261 }
262
263 /* Initialze a PWM instance
264 * period_cycles is the number of PWM input frequency cycles in the desired
265 * PWM output period.
266 * pulse_cycles is the number of PMW input frequency cycles in the desired
267 * PWM active pulse width.
268 * flags - Enable after configuration, use low frequeny range instead of high
269 * range, invert output (active output is low instead of high), or reset the
270 * PWM using PCR peripheral reset before configuration.
271 *
272 * Use pulse_cycles as Count_on
273 * Use period_cycles as Fpwm
274 *
275 * Given: Fpwm, Fin, Count_on
276 * Derive: ps and Count_off
277 * Count_off = period_cycles - pulse_cycles
278 *
279 */
mec_hal_pwm_init(struct mec_pwm_regs * regs,uint32_t period_cycles,uint32_t pulse_cycles,uint32_t flags)280 int mec_hal_pwm_init(struct mec_pwm_regs *regs, uint32_t period_cycles,
281 uint32_t pulse_cycles, uint32_t flags)
282 {
283 int ret = 0;
284 const struct mec5_pwm_info *info = get_pwm_info((uintptr_t)regs);
285
286 if (!info) {
287 return MEC_RET_ERR_INVAL;
288 }
289
290 regs->CONFIG = 0;
291
292 mec_hal_pcr_clr_blk_slp_en(info->pcr_id);
293 if (flags & MEC5_PWM_CFG_RESET) {
294 mec_hal_pcr_blk_reset(info->pcr_id);
295 }
296
297 if (flags & MEC5_PWM_CFG_INVERT) {
298 regs->CONFIG |= MEC_BIT(MEC_PWM_CONFIG_INVERT_Pos);
299 }
300
301 ret = prog_pwm(regs, period_cycles, pulse_cycles);
302 if (ret) {
303 return ret;
304 }
305
306 if (flags & MEC5_PWM_CFG_ENABLE) {
307 pwm_enable(regs);
308 }
309
310 return MEC_RET_OK;
311 }
312
mec_hal_pwm_reset(struct mec_pwm_regs * regs)313 int mec_hal_pwm_reset(struct mec_pwm_regs *regs)
314 {
315 const struct mec5_pwm_info *info = get_pwm_info((uintptr_t)regs);
316
317 if (!info) {
318 return MEC_RET_ERR_INVAL;
319 }
320
321 mec_hal_pcr_blk_reset(info->pcr_id);
322
323 return MEC_RET_OK;
324 }
325
mec_hal_pwm_set_polarity(struct mec_pwm_regs * regs,uint8_t polarity_inverted)326 int mec_hal_pwm_set_polarity(struct mec_pwm_regs *regs, uint8_t polarity_inverted)
327 {
328 if (!regs) {
329 return MEC_RET_ERR_INVAL;
330 }
331
332 if (polarity_inverted) {
333 regs->CONFIG |= MEC_BIT(MEC_PWM_CONFIG_INVERT_Pos);
334 } else {
335 regs->CONFIG &= (uint32_t)~MEC_BIT(MEC_PWM_CONFIG_INVERT_Pos);
336 }
337
338 return MEC_RET_OK;
339 }
340
mec_hal_pwm_enable(struct mec_pwm_regs * regs,uint8_t enable)341 int mec_hal_pwm_enable(struct mec_pwm_regs *regs, uint8_t enable)
342 {
343 if (enable) {
344 regs->CONFIG |= MEC_BIT(MEC_PWM_CONFIG_ENABLE_Pos);
345 } else {
346 regs->CONFIG &= (uint32_t)~MEC_BIT(MEC_PWM_CONFIG_ENABLE_Pos);
347 }
348
349 return 0;
350 }
351
mec_hal_pwm_is_enabled(struct mec_pwm_regs * regs)352 int mec_hal_pwm_is_enabled(struct mec_pwm_regs *regs)
353 {
354 if (regs->CONFIG & MEC_BIT(MEC_PWM_CONFIG_ENABLE_Pos)) {
355 return 1;
356 }
357
358 return 0;
359 }
360
mec_hal_pwm_get_freq_in(struct mec_pwm_regs * regs)361 uint32_t mec_hal_pwm_get_freq_in(struct mec_pwm_regs *regs)
362 {
363 uint32_t fin = MEC5_PWM_FIN_HIGH;
364
365 if (regs->CONFIG & MEC_BIT(MEC_PWM_CONFIG_CLK_SRC_SLOW_Pos)) {
366 fin = mec_hal_pcr_slow_clock_freq_get();
367 }
368
369 return fin;
370 }
371
mec_hal_pwm_get_count(struct mec_pwm_regs * regs,uint8_t on_count)372 uint32_t mec_hal_pwm_get_count(struct mec_pwm_regs *regs, uint8_t on_count)
373 {
374 if (on_count) {
375 return (uint32_t)regs->CNT_ON;
376 } else {
377 return (uint32_t)regs->CNT_OFF;
378 }
379 }
380
mec_hal_pwm_get_freq_out(struct mec_pwm_regs * regs)381 uint32_t mec_hal_pwm_get_freq_out(struct mec_pwm_regs *regs)
382 {
383 uint32_t fin = MEC5_PWM_FIN_HIGH;
384 uint32_t fpwm = 0, ps = 0, cnt_on = 0, cnt_off = 0;
385
386 if (regs->CONFIG & MEC_BIT(MEC_PWM_CONFIG_CLK_SRC_SLOW_Pos)) {
387 fin = mec_hal_pcr_slow_clock_freq_get();
388 }
389
390 if (!fin) {
391 return 0u;
392 }
393
394 ps = ((regs->CONFIG & MEC_PWM_CONFIG_CLKDIV_Msk) >> MEC_PWM_CONFIG_CLKDIV_Pos) + 1u;
395 cnt_on = regs->CNT_ON + 1u;
396 cnt_off = regs->CNT_OFF + 1u;
397
398 /* ps in [1,16], cnt_on and cnt_off in [1, 0x10000]. No overflow */
399 fpwm = fin / (ps * (cnt_on + cnt_off));
400
401 return fpwm;
402 }
403
mec_hal_pwm_set_freq_out(struct mec_pwm_regs * regs,uint32_t period_cycles,uint32_t pulse_cycles)404 int mec_hal_pwm_set_freq_out(struct mec_pwm_regs *regs, uint32_t period_cycles,
405 uint32_t pulse_cycles)
406 {
407 return prog_pwm(regs, period_cycles, pulse_cycles);
408 }
409
410 /* end mec_pwm.c */
411