]> begriffs open source - cmsis-freertos/blob - Demo/RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio/freedom-metal/src/drivers/sifive_pwm0.c
Update README.md - branch main is now the base branch
[cmsis-freertos] / Demo / RISC-V_RV32_SiFive_HiFive1-RevB_FreedomStudio / freedom-metal / src / drivers / sifive_pwm0.c
1 /* Copyright 2020 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
3
4 #include <metal/machine/platform.h>
5
6 #ifdef METAL_SIFIVE_PWM0
7 #include <metal/clock.h>
8 #include <metal/compiler.h>
9 #include <metal/drivers/sifive_gpio0.h>
10 #include <metal/drivers/sifive_pwm0.h>
11 #include <metal/io.h>
12 #include <metal/machine.h>
13 #include <metal/time.h>
14 #include <stdio.h>
15
16 /* Register fields */
17 #define METAL_PWMCFG_STICKY (1UL << 8)
18 #define METAL_PWMCFG_ZEROCMP (1UL << 9)
19 #define METAL_PWMCFG_DEGLITCH (1UL << 10)
20 #define METAL_PWMCFG_ENALWAYS (1UL << 12)
21 #define METAL_PWMCFG_ENONESHOT (1UL << 13)
22 #define METAL_PWMCFG_CMPCENTER(x) (1UL << (16 + x))
23 #define METAL_PWMCFG_CMPIP(x) (1UL << (28 + x))
24 #define METAL_SIFIVE_PWM0_PWMCMP(x) (METAL_SIFIVE_PWM0_PWMCMP0 + (x * 4))
25
26 /* Macros to access registers */
27 #define METAL_PWM_REG(offset) ((base + offset))
28 #define METAL_PWM_REGW(offset)                                                 \
29     (__METAL_ACCESS_ONCE((__metal_io_u32 *)METAL_PWM_REG(offset)))
30
31 /* Macro to get PWM compare count */
32 #define METAL_PWM_GETCMPVAL(duty) (duty * pwm->count_val) / 100U
33 /* Max duty cycle value */
34 #define METAL_PWM_MAXDUTY 100UL
35 /* Max pre-scalar value */
36 #define METAL_PWM_MAXPRESCAL 15UL
37
38 /* Check endianess */
39 #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
40 #error *** Unsupported endianess ***
41 #endif
42
43 #if (METAL_MAX_PWM0_NCMP > METAL_MAX_PWM_CHANNELS)
44 #error *** METAL_MAX_PWM_CHANNELS exceeded ***
45 #endif
46
47 /* Return values */
48 #define METAL_PWM_RET_OK 0
49 #define METAL_PWM_RET_ERR -1
50
51 static void pre_rate_change_callback(void *priv) {
52     struct metal_pwm *gpwm = priv;
53     /* Disable active PWM instance. */
54     gpwm->vtable->stop(gpwm, 0);
55 }
56
57 static void post_rate_change_callback(void *priv) {
58     struct __metal_driver_sifive_pwm0 *pwm = priv;
59     struct metal_pwm *gpwm = priv;
60     unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm);
61     unsigned int cmp_count = __metal_driver_sifive_pwm0_comparator_count(gpwm);
62     unsigned int idx = 0;
63     unsigned int duty;
64
65     /* Check if PWM frequency was set */
66     if (pwm->freq != 0) {
67         /* Set frequency after clock rate change */
68         gpwm->vtable->set_freq(gpwm, 0, pwm->freq);
69
70         /* Set duty cycle after clock rate change */
71         while (++idx < cmp_count) {
72             duty = pwm->duty[idx];
73             if (duty != 0) {
74                 METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCMP(idx)) =
75                     METAL_PWM_GETCMPVAL(duty);
76             }
77         }
78     }
79 }
80
81 static int __metal_driver_sifive_pwm0_enable(struct metal_pwm *gpwm) {
82     struct __metal_driver_sifive_gpio0 *pinmux =
83         __metal_driver_sifive_pwm0_pinmux(gpwm);
84     unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm);
85     struct __metal_driver_sifive_pwm0 *pwm = (void *)gpwm;
86     int ret = METAL_PWM_RET_ERR;
87
88     if (base != 0) {
89
90         if ((pinmux != NULL) && (gpwm != NULL)) {
91             /* Configure PWM I/O pins */
92             long pinmux_output_selector =
93                 __metal_driver_sifive_pwm0_pinmux_output_selector(gpwm);
94             long pinmux_source_selector =
95                 __metal_driver_sifive_pwm0_pinmux_source_selector(gpwm);
96
97             pinmux->gpio.vtable->enable_io((struct metal_gpio *)pinmux,
98                                            pinmux_output_selector,
99                                            pinmux_source_selector);
100         }
101
102         /* Initialize default values */
103         pwm->max_count =
104             (1UL << __metal_driver_sifive_pwm0_compare_width(gpwm)) - 1;
105         pwm->freq = 0;
106         pwm->count_val = 0;
107         METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) = 0;
108         ret = METAL_PWM_RET_OK;
109     }
110     return ret;
111 }
112
113 static int __metal_driver_sifive_pwm0_disable(struct metal_pwm *gpwm) {
114     struct __metal_driver_sifive_gpio0 *pinmux =
115         __metal_driver_sifive_pwm0_pinmux(gpwm);
116     int ret = METAL_PWM_RET_ERR;
117
118     if (gpwm != NULL) {
119
120         if (pinmux != NULL) {
121             /* Disable PWM I/O pins */
122             long pinmux_source_selector =
123                 __metal_driver_sifive_pwm0_pinmux_source_selector(gpwm);
124             pinmux->gpio.vtable->disable_io((struct metal_gpio *)pinmux,
125                                             pinmux_source_selector);
126         }
127
128         ret = METAL_PWM_RET_OK;
129     }
130     return ret;
131 }
132
133 static int __metal_driver_sifive_pwm0_set_freq(struct metal_pwm *gpwm,
134                                                unsigned int idx,
135                                                unsigned int freq) {
136     struct metal_clock *clock = __metal_driver_sifive_pwm0_clock(gpwm);
137     unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm);
138     unsigned int cmp_count = __metal_driver_sifive_pwm0_comparator_count(gpwm);
139     struct __metal_driver_sifive_pwm0 *pwm = (void *)gpwm;
140     unsigned int clock_rate;
141     unsigned int count;
142     unsigned int prescale = 0;
143     int ret = METAL_PWM_RET_ERR;
144
145     if ((clock != NULL) && (gpwm != NULL) && (idx < cmp_count)) {
146         clock_rate = clock->vtable->get_rate_hz(clock);
147         /* Register clock rate change call-backs */
148         if (pwm->freq == 0) {
149             pwm->pre_rate_change_callback.callback = &pre_rate_change_callback;
150             pwm->pre_rate_change_callback.priv = pwm;
151             metal_clock_register_pre_rate_change_callback(
152                 clock, &(pwm->pre_rate_change_callback));
153
154             pwm->post_rate_change_callback.callback =
155                 &post_rate_change_callback;
156             pwm->post_rate_change_callback.priv = pwm;
157             metal_clock_register_post_rate_change_callback(
158                 clock, &(pwm->post_rate_change_callback));
159         }
160
161         /* Calculate count value for given PWM frequency */
162         do {
163             count = (clock_rate / (1UL << prescale)) / freq;
164         } while ((count > pwm->max_count) &&
165                  (prescale++ < METAL_PWM_MAXPRESCAL));
166
167         pwm->freq = (clock_rate / (1UL << prescale)) / count;
168         pwm->count_val = --count;
169
170         /* Update values into registers */
171         METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCMP0) = count;
172         METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) |= (prescale & 0x0FUL);
173         ret = METAL_PWM_RET_OK;
174
175 #if defined(METAL_PWM_DEBUG)
176         printf("PWM requested freq:%u set freq:%u \n", freq, pwm->freq);
177         printf("CPU Clk:%u Prescale:%u Count:%u \n", clock_rate, prescale,
178                count);
179 #endif
180     }
181     return ret;
182 }
183
184 static int
185 __metal_driver_sifive_pwm0_set_duty(struct metal_pwm *gpwm, unsigned int idx,
186                                     unsigned int duty,
187                                     metal_pwm_phase_correct_t phase_corr) {
188     struct __metal_driver_sifive_pwm0 *pwm = (void *)gpwm;
189     unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm);
190     unsigned int cmp_count = __metal_driver_sifive_pwm0_comparator_count(gpwm);
191     int ret = METAL_PWM_RET_ERR;
192
193     /* Check if duty value is within limits, duty cycle cannot be set for
194      * PWMCMP0 */
195     if ((idx > 0) && (idx < cmp_count) && (duty <= METAL_PWM_MAXDUTY)) {
196         /* Calculate PWM compare count value for given duty cycle */
197         METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCMP(idx)) =
198             METAL_PWM_GETCMPVAL(duty);
199         pwm->duty[idx] = duty;
200
201         /* Enable / Disable phase correct PWM mode */
202         if (phase_corr == METAL_PWM_PHASE_CORRECT_ENABLE) {
203             METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) |=
204                 METAL_PWMCFG_CMPCENTER(idx);
205         } else {
206             METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) &=
207                 ~METAL_PWMCFG_CMPCENTER(idx);
208         }
209         ret = METAL_PWM_RET_OK;
210     }
211     return ret;
212 }
213
214 static unsigned int __metal_driver_sifive_pwm0_get_duty(struct metal_pwm *gpwm,
215                                                         unsigned int idx) {
216     struct __metal_driver_sifive_pwm0 *pwm = (void *)gpwm;
217     unsigned int cmp_count = __metal_driver_sifive_pwm0_comparator_count(gpwm);
218     unsigned int duty = 0;
219
220     /* Check for valid parameters and get configured duty cycle value */
221     if ((pwm != NULL) && (idx > 0) && (idx < cmp_count)) {
222         duty = pwm->duty[idx];
223     }
224     return duty;
225 }
226
227 static unsigned int __metal_driver_sifive_pwm0_get_freq(struct metal_pwm *gpwm,
228                                                         unsigned int idx) {
229     struct __metal_driver_sifive_pwm0 *pwm = (void *)gpwm;
230     unsigned int freq = 0;
231
232     (void)idx; /* Unused parameter, no support for per channel frequency */
233
234     /* Check for valid parameters and get configured PWM frequency value */
235     if (pwm != NULL) {
236         freq = pwm->freq;
237     }
238     return freq;
239 }
240
241 static int __metal_driver_sifive_pwm0_trigger(struct metal_pwm *gpwm,
242                                               unsigned int idx,
243                                               metal_pwm_run_mode_t mode) {
244     unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm);
245     int ret = METAL_PWM_RET_ERR;
246
247     (void)idx; /* Unused parameter,for later use */
248
249     if (base != 0) {
250         /* Configure for requested PWM run mode */
251         if (mode == METAL_PWM_CONTINUOUS) {
252             METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) |= METAL_PWMCFG_DEGLITCH |
253                                                         METAL_PWMCFG_ZEROCMP |
254                                                         METAL_PWMCFG_ENALWAYS;
255         } else {
256             METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) |= METAL_PWMCFG_DEGLITCH |
257                                                         METAL_PWMCFG_ZEROCMP |
258                                                         METAL_PWMCFG_ENONESHOT;
259         }
260         ret = METAL_PWM_RET_OK;
261     }
262     return ret;
263 }
264
265 static int __metal_driver_sifive_pwm0_stop(struct metal_pwm *gpwm,
266                                            unsigned int idx) {
267     unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm);
268     int ret = METAL_PWM_RET_ERR;
269
270     (void)idx; /* Unused parameter,for later use */
271
272     if (base != 0) {
273         /* Disable always running mode */
274         METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) &= ~METAL_PWMCFG_ENALWAYS;
275         ret = METAL_PWM_RET_OK;
276     }
277     return ret;
278 }
279
280 static int
281 __metal_driver_sifive_pwm0_cfg_interrupt(struct metal_pwm *gpwm,
282                                          metal_pwm_interrupt_t flag) {
283     unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm);
284     int ret = METAL_PWM_RET_ERR;
285
286     if (base != 0) {
287         if (flag == METAL_PWM_INTERRUPT_ENABLE) {
288             /* Enable sticky bit, to make sure interrupts are not forgotten */
289             METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) |= METAL_PWMCFG_STICKY;
290         } else {
291             METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) &= ~METAL_PWMCFG_STICKY;
292         }
293         ret = METAL_PWM_RET_OK;
294     }
295     return ret;
296 }
297
298 static int __metal_driver_sifive_pwm0_clr_interrupt(struct metal_pwm *gpwm,
299                                                     unsigned int idx) {
300     unsigned long base = __metal_driver_sifive_pwm0_control_base(gpwm);
301     unsigned int cmp_count = __metal_driver_sifive_pwm0_comparator_count(gpwm);
302     int ret = METAL_PWM_RET_ERR;
303
304     if ((base != 0) && (idx < cmp_count)) {
305         /* Clear interrupt pending bit for given PWM comparator */
306         METAL_PWM_REGW(METAL_SIFIVE_PWM0_PWMCFG) &= ~METAL_PWMCFG_CMPIP(idx);
307         ret = METAL_PWM_RET_OK;
308     }
309     return ret;
310 }
311
312 static struct metal_interrupt *
313 __metal_driver_sifive_pwm0_interrupt_controller(struct metal_pwm *gpwm) {
314     return __metal_driver_sifive_pwm0_interrupt_parent(gpwm);
315 }
316
317 static int __metal_driver_sifive_pwm0_interrupt_id(struct metal_pwm *gpwm,
318                                                    unsigned int idx) {
319     return __metal_driver_sifive_pwm0_interrupt_lines(gpwm, idx);
320 }
321
322 __METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_pwm0) = {
323     .pwm.enable = __metal_driver_sifive_pwm0_enable,
324     .pwm.disable = __metal_driver_sifive_pwm0_disable,
325     .pwm.set_duty = __metal_driver_sifive_pwm0_set_duty,
326     .pwm.set_freq = __metal_driver_sifive_pwm0_set_freq,
327     .pwm.get_duty = __metal_driver_sifive_pwm0_get_duty,
328     .pwm.get_freq = __metal_driver_sifive_pwm0_get_freq,
329     .pwm.trigger = __metal_driver_sifive_pwm0_trigger,
330     .pwm.stop = __metal_driver_sifive_pwm0_stop,
331     .pwm.cfg_interrupt = __metal_driver_sifive_pwm0_cfg_interrupt,
332     .pwm.clr_interrupt = __metal_driver_sifive_pwm0_clr_interrupt,
333     .pwm.get_interrupt_controller =
334         __metal_driver_sifive_pwm0_interrupt_controller,
335     .pwm.get_interrupt_id = __metal_driver_sifive_pwm0_interrupt_id,
336 };
337
338 #endif /* METAL_SIFIVE_PWM0 */
339
340 typedef int no_empty_translation_units;