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