]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/sifive_fe310-g000_pll.c
c91328565b13e20af9fd5b2266dacbe1748ecc67
[freertos] / FreeRTOS / Demo / RISC-V_RV32_SiFive_HiFive1_FreedomStudio / freedom-metal / src / drivers / sifive_fe310-g000_pll.c
1 /* Copyright 2018 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
3
4 #include <metal/machine/platform.h>
5
6 #ifdef METAL_SIFIVE_FE310_G000_PLL
7
8 #include <stdio.h>
9 #include <limits.h>
10
11 #include <metal/machine.h>
12 #include <metal/drivers/sifive_fe310-g000_pll.h>
13 #include <stdlib.h>
14
15 #define PLL_R        0x00000007UL
16 #define PLL_F        0x000003F0UL
17 #define PLL_Q        0x00000C00UL
18 #define PLL_SEL      0x00010000UL
19 #define PLL_REFSEL   0x00020000UL
20 #define PLL_BYPASS   0x00040000UL
21 #define PLL_LOCK     0x80000000UL
22
23 #define DIV_DIV      0x0000003FUL
24 #define DIV_1        0x00000100UL
25
26 #define PLL_R_SHIFT(r)  ((r << 0) & PLL_R)
27 #define PLL_F_SHIFT(f)  ((f << 4) & PLL_F) 
28 #define PLL_Q_SHIFT(q)  ((q << 10) & PLL_Q)
29 #define PLL_DIV_SHIFT(d)    ((d << 0) & DIV_DIV)
30
31 struct pll_config_t {
32     unsigned long multiplier;
33     unsigned long divisor;
34     unsigned long min_input_rate;
35     unsigned long max_input_rate;
36     unsigned long r;
37     unsigned long f;
38     unsigned long q;
39     long d; /* < 0 if disabled */
40 };
41
42 static const struct pll_config_t pll_configs[] = {
43     /*
44      * multiplier
45      * ^  divisor
46      * |  ^      min_input_rate
47      * |  |      ^        max_input_rate
48      * |  |      |        ^      r
49      * |  |      |        |      ^   f
50      * |  |      |        |      |   ^  q
51      * |  |      |        |      |   |  ^   d
52      * |  |      |        |      |   |  |   ^
53      * |  |      |        |      |   |  |   | */
54     { 1, 32, 12000000, 24000000, 1, 31, 3, 63}, 
55     { 1, 32, 24000000, 48000000, 3, 31, 2, 63}, 
56     { 1, 16,  6000000, 12000000, 0, 31, 3, 63}, 
57     { 1, 16, 12000000, 24000000, 1, 31, 2, 63}, 
58     { 1, 16, 24000000, 48000000, 3, 31, 2, 31}, 
59     { 1,  8,  6000000, 12000000, 0, 31, 3, 31}, 
60     { 1,  8, 12000000, 24000000, 1, 31, 2, 31}, 
61     { 1,  8, 24000000, 48000000, 3, 31, 2, 15}, 
62     { 1,  4,  6000000, 12000000, 0, 31, 3, 15}, 
63     { 1,  4, 12000000, 24000000, 1, 31, 2, 15}, 
64     { 1,  4, 24000000, 48000000, 3, 31, 2,  7}, 
65     { 1,  2,  6000000, 12000000, 0, 31, 2, 15}, 
66     { 1,  2, 12000000, 24000000, 1, 31, 1, 15}, 
67     { 1,  2, 24000000, 48000000, 3, 31, 1,  7}, 
68     { 2,  1,  6000000, 12000000, 0, 31, 1,  7}, 
69     { 2,  1, 12000000, 24000000, 1, 31, 1,  3}, 
70     { 2,  1, 24000000, 48000000, 3, 31, 3, -1}, 
71     { 4,  1,  6000000, 12000000, 0, 31, 3,  0}, 
72     { 4,  1, 12000000, 24000000, 1, 31, 3, -1}, 
73     { 4,  1, 24000000, 48000000, 3, 31, 2, -1}, 
74     { 6,  1,  6000000, 10666666, 0, 35, 1,  2}, 
75     { 6,  1, 10666666, 12000000, 0, 23, 3, -1}, 
76     { 6,  1, 12000000, 16000000, 1, 47, 3, -1}, 
77     { 6,  1, 16000000, 18000000, 1, 23, 2, -1}, 
78     { 6,  1, 18000000, 21333333, 2, 35, 2, -1}, 
79     { 8,  1,  6000000, 12000000, 0, 31, 3, -1}, 
80     { 8,  1, 12000000, 24000000, 1, 31, 2, -1}, 
81     { 8,  1, 24000000, 48000000, 3, 31, 1, -1}, 
82     {10,  1,  6000000,  9600000, 0, 39, 3, -1}, 
83     {10,  1,  9600000, 12000000, 0, 19, 2, -1}, 
84     {10,  1, 12000000, 19200000, 1, 39, 2, -1}, 
85     {10,  1, 19200000, 24000000, 1, 19, 1, -1}, 
86     {10,  1, 24000000, 38400000, 3, 39, 1, -1}, 
87     {12,  1,  6000000,  8000000, 0, 47, 3, -1}, 
88     {12,  1,  8000000, 12000000, 0, 23, 2, -1}, 
89     {12,  1, 12000000, 16000000, 1, 47, 2, -1}, 
90     {12,  1, 16000000, 24000000, 1, 23, 1, -1}, 
91     {12,  1, 24000000, 30000000, 3, 47, 1, -1}, 
92     {12,  1, 30000000, 32000000, 3, 47, 1, -1}, 
93     {14,  1,  6000000,  6857142, 0, 55, 3, -1}, 
94     {14,  1,  6857143, 12000000, 0, 27, 2, -1}, 
95     {14,  1, 12000000, 13714285, 1, 55, 2, -1}, 
96     {14,  1, 13714286, 24000000, 1, 27, 1, -1}, 
97     {14,  1, 24000000, 27428571, 3, 55, 1, -1}, 
98     {16,  1,  6000000, 12000000, 0, 31, 2, -1}, 
99     {16,  1, 12000000, 24000000, 1, 31, 1, -1}, 
100     {18,  1,  6000000, 10666666, 0, 35, 2, -1}, 
101     {18,  1, 10666667, 12000000, 0, 17, 1, -1}, 
102     {18,  1, 12000000, 21333333, 1, 35, 1, -1}, 
103     {20,  1,  6000000,  9600000, 0, 39, 2, -1}, 
104     {20,  1,  9600000, 12000000, 0, 19, 1, -1}, 
105     {20,  1, 12000000, 19200000, 1, 39, 1, -1}, 
106     {22,  1,  6000000,  8727272, 0, 43, 2, -1}, 
107     {22,  1,  8727273, 12000000, 0, 21, 1, -1}, 
108     {22,  1, 12000000, 17454545, 1, 43, 1, -1}, 
109     {24,  1,  6000000,  8000000, 0, 47, 2, -1}, 
110     {24,  1,  8000000, 12000000, 0, 23, 1, -1}, 
111     {24,  1, 12000000, 16000000, 1, 47, 1, -1}, 
112     {26,  1,  6000000,  7384615, 0, 51, 2, -1}, 
113     {26,  1,  7384616, 12000000, 0, 25, 1, -1}, 
114     {26,  1, 12000000, 14768230, 1, 51, 1, -1}, 
115     {28,  1,  6000000,  6857142, 0, 55, 2, -1}, 
116     {28,  1,  6857143, 12000000, 0, 27, 1, -1}, 
117     {28,  1, 12000000, 13714285, 1, 55, 1, -1}, 
118     {30,  1,  6000000,  6400000, 0, 59, 2, -1}, 
119     {30,  1,  6400000, 12000000, 0, 29, 1, -1}, 
120     {30,  1, 12000000, 12800000, 1, 59, 1, -1}, 
121     {32,  1,  6000000, 12000000, 0, 31, 1, -1}
122 };
123
124 #define PLL_CONFIG_NOT_VALID -1
125
126 void __metal_driver_sifive_fe310_g000_pll_init(struct __metal_driver_sifive_fe310_g000_pll *pll);
127
128 /* Given the rate of the PLL input frequency and a PLL configuration, what
129  * will the resulting PLL output frequency be?
130  * Arguments:
131  *  - pll_input_rate the PLL input frequency in hertz
132  *  - config the PLL configuration
133  * Returns:
134  *  - PLL_CONFIG_NOT_VALID if the configuration is not valid for the input frequency
135  *  - the output frequency, in hertz */
136 static long get_pll_config_freq(long pll_input_rate, const struct pll_config_t *config)
137 {
138     if(pll_input_rate < config->min_input_rate || pll_input_rate > config->max_input_rate)
139         return PLL_CONFIG_NOT_VALID;
140
141     return pll_input_rate * config->multiplier / config->divisor;
142 }
143
144 #ifdef __METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE
145
146 static void metal_sifive_fe310_g000_pll_init(void) __attribute__((constructor));
147 static void metal_sifive_fe310_g000_pll_init(void) {
148     long init_rate = __metal_driver_sifive_fe310_g000_pll_init_rate();
149     /* If the PLL init_rate is zero, don't initialize the PLL */
150     if(init_rate != 0)
151         __metal_driver_sifive_fe310_g000_pll_init(__METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE);
152 }
153
154 #endif /* __METAL_DT_SIFIVE_FE310_G000__PLL_HANDLE */
155
156 void __metal_driver_sifive_fe310_g000_pll_init(struct __metal_driver_sifive_fe310_g000_pll *pll) {
157     struct metal_clock *pllref = __metal_driver_sifive_fe310_g000_pll_pllref(&(pll->clock));
158     long init_rate = __metal_driver_sifive_fe310_g000_pll_init_rate();
159     long config_offset = __metal_driver_sifive_fe310_g000_pll_config_offset();
160     long base = __metal_driver_sifive_fe310_g000_prci_base();
161
162     __metal_io_u32 *pllcfg = (__metal_io_u32 *) (base + config_offset);
163
164     /* If the PLL clock has had a _pre_rate_change_callback configured, call it */
165     if(pll->clock._pre_rate_change_callback != NULL)
166         pll->clock._pre_rate_change_callback(pll->clock._pre_rate_change_callback_priv);
167
168     /* If we're running off of the PLL, switch off before we start configuring it*/
169     if((__METAL_ACCESS_ONCE(pllcfg) & PLL_SEL) == 0)
170         __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_SEL);
171
172     /* Make sure we're running off of the external oscillator for stability */
173     if(pllref != NULL)
174         __METAL_ACCESS_ONCE(pllcfg) |= PLL_REFSEL;
175
176     /* Configure the PLL to run at the requested init frequency.
177      * Using the vtable instead of the user API because we want to control
178      * when the callbacks occur. */
179     pll->clock.vtable->set_rate_hz(&(pll->clock), init_rate);
180
181     /* If the PLL clock has had a rate_change_callback configured, call it */
182     if(pll->clock._post_rate_change_callback != NULL)
183         pll->clock._post_rate_change_callback(pll->clock._post_rate_change_callback_priv);
184 }
185
186 long __metal_driver_sifive_fe310_g000_pll_get_rate_hz(const struct metal_clock *clock)
187 {
188     struct metal_clock *pllref = __metal_driver_sifive_fe310_g000_pll_pllref(clock);
189     struct metal_clock *pllsel0 = __metal_driver_sifive_fe310_g000_pll_pllsel0(clock);
190     long config_offset = __metal_driver_sifive_fe310_g000_pll_config_offset(clock);
191     struct __metal_driver_sifive_fe310_g000_prci *config_base =
192       __metal_driver_sifive_fe310_g000_pll_config_base(clock);
193     long divider_offset = __metal_driver_sifive_fe310_g000_pll_divider_offset(clock);
194     struct __metal_driver_sifive_fe310_g000_prci *divider_base =
195       __metal_driver_sifive_fe310_g000_pll_divider_base(clock);
196     const struct __metal_driver_vtable_sifive_fe310_g000_prci *vtable =
197       __metal_driver_sifive_fe310_g000_prci_vtable();
198
199     long cfg = vtable->get_reg(config_base, config_offset);
200     long div = vtable->get_reg(divider_base, divider_offset);
201
202     /* At the end of the PLL there's one big mux: it either selects the HFROSC
203      * (bypassing the PLL entirely) or uses the PLL. */
204     if (__METAL_GET_FIELD(cfg, PLL_SEL) == 0)
205         return metal_clock_get_rate_hz(pllsel0);
206
207     /* There's a clock mux before the PLL that selects between the HFROSC adn
208      * the HFXOSC as the PLL's input clock. */
209     long ref_hz = metal_clock_get_rate_hz(__METAL_GET_FIELD(cfg, PLL_REFSEL) ? pllref : pllsel0);
210
211     /* It's possible to bypass the PLL, which is an internal bpyass.  This
212      * still obays the PLL's input clock mu. */
213     if (__METAL_GET_FIELD(cfg, PLL_BYPASS))
214         return ref_hz;
215
216     /* Logically the PLL is a three stage div-mul-div. */
217     long div_r = __METAL_GET_FIELD(cfg, PLL_R) + 1;
218     long mul_f = 2 * (__METAL_GET_FIELD(cfg, PLL_F) + 1);
219     if (__METAL_GET_FIELD(cfg, PLL_Q) == 0)
220         return -1;
221     long div_q = 1 << __METAL_GET_FIELD(cfg, PLL_Q);
222
223     /* In addition to the dividers inherent in the PLL, there's an additional
224      * clock divider that lives after the PLL and lets us pick a more
225      * interesting range of frequencies. */
226     long pllout = (((ref_hz / div_r) * mul_f) / div_q);
227     if (__METAL_GET_FIELD(div, DIV_1))
228         return pllout;
229
230     return pllout / (2 * (__METAL_GET_FIELD(div, DIV_DIV) + 1));
231 }
232
233 /* Find a valid configuration for the PLL which is closest to the desired
234  * output frequency.
235  * Arguments:
236  *  - ref_hz PLL input frequency
237  *  - rate desired PLL output frequency
238  * Returns:
239  *  -1 if no valid configuration is available
240  *  the index into pll_configs of a valid configuration */
241 static int find_closest_config(long ref_hz, long rate)
242 {
243     int closest_index = -1;
244     long closest_diff = LONG_MAX;
245
246     /* We're probably trying for a fast output frequency, so start from
247      * the high end of the configs. */
248     for(int i = (sizeof(pll_configs) / sizeof(pll_configs[0])) - 1; i >= 0; i--)
249     {
250         long config_freq = get_pll_config_freq(ref_hz, &(pll_configs[i]));
251         if(config_freq != PLL_CONFIG_NOT_VALID)
252         {
253             long freq_diff = abs(config_freq - rate);
254             if(freq_diff < closest_diff)
255             {
256                 closest_index = i;
257                 closest_diff = freq_diff;
258             }
259         }
260     }
261
262     return closest_index;
263 }
264
265 /* Configure the PLL and wait for it to lock */
266 static void configure_pll(__metal_io_u32 *pllcfg, __metal_io_u32 *plloutdiv, const struct pll_config_t *config)
267 {
268     __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_R);
269     __METAL_ACCESS_ONCE(pllcfg) |= PLL_R_SHIFT(config->r);
270
271     __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_F);
272     __METAL_ACCESS_ONCE(pllcfg) |= PLL_F_SHIFT(config->f);
273
274     __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_Q);
275     __METAL_ACCESS_ONCE(pllcfg) |= PLL_Q_SHIFT(config->q);
276
277     if(config->d < 0)
278     {
279         /* disable final divider */
280         __METAL_ACCESS_ONCE(plloutdiv) |= DIV_1;
281
282         __METAL_ACCESS_ONCE(plloutdiv) &= ~(DIV_DIV);
283         __METAL_ACCESS_ONCE(plloutdiv) |= PLL_DIV_SHIFT(1);
284     }
285     else
286     {
287         __METAL_ACCESS_ONCE(plloutdiv) &= ~(DIV_1);
288
289         __METAL_ACCESS_ONCE(plloutdiv) &= ~(DIV_DIV);
290         __METAL_ACCESS_ONCE(plloutdiv) |= PLL_DIV_SHIFT(config->d);
291     }
292
293     __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_BYPASS);
294
295     /* Wait for PLL to lock */
296     while((__METAL_ACCESS_ONCE(pllcfg) & PLL_LOCK) == 0) ;
297 }
298
299 long __metal_driver_sifive_fe310_g000_pll_set_rate_hz(struct metal_clock *clock, long rate)
300 {
301     struct metal_clock *pllref = __metal_driver_sifive_fe310_g000_pll_pllref(clock);
302     struct metal_clock *pllsel0 = __metal_driver_sifive_fe310_g000_pll_pllsel0(clock);
303     long config_offset = __metal_driver_sifive_fe310_g000_pll_config_offset(clock);
304     long divider_offset = __metal_driver_sifive_fe310_g000_pll_divider_offset(clock);
305     long base = __metal_driver_sifive_fe310_g000_prci_base();
306
307     __metal_io_u32 *pllcfg = (__metal_io_u32 *) (base + config_offset);
308     __metal_io_u32 *plloutdiv = (__metal_io_u32 *) (base + divider_offset);
309
310     /* We can't modify the PLL if coreclk is driven by it, so switch it off */
311     if (__METAL_ACCESS_ONCE(pllcfg) & PLL_SEL)
312         __METAL_ACCESS_ONCE(pllcfg) &= ~(PLL_SEL);
313
314     /* There's a clock mux before the PLL that selects between the HFROSC and
315      * the HFXOSC as the PLL's input clock. */
316     long ref_hz = metal_clock_get_rate_hz(__METAL_ACCESS_ONCE(pllcfg) & PLL_REFSEL ? pllref : pllsel0);
317
318     /* if the desired rate is within 75%-125% of the input clock, bypass the PLL */
319     if((ref_hz * 3 / 4) <= rate && (ref_hz * 5 / 4) >= rate)
320     {
321         __METAL_ACCESS_ONCE(pllcfg) |= PLL_BYPASS;
322     }
323     else
324     {
325         int config_index = find_closest_config(ref_hz, rate);
326         if(config_index != -1)
327         {
328             configure_pll(pllcfg, plloutdiv, &(pll_configs[config_index]));
329         }
330         else
331         {
332             /* unable to find a valid configuration */
333             __METAL_ACCESS_ONCE(pllcfg) |= PLL_BYPASS;
334         }
335     }
336
337     /* Enable the PLL */
338     __METAL_ACCESS_ONCE(pllcfg) |= PLL_SEL;
339
340     return __metal_driver_sifive_fe310_g000_pll_get_rate_hz(clock);
341 }
342
343 #ifdef __METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE
344 static void use_hfxosc(void) __attribute__((constructor));
345 static void use_hfxosc(void)
346 {
347     long init_rate = __metal_driver_sifive_fe310_g000_pll_init_rate();
348     metal_clock_set_rate_hz(
349         &__METAL_DT_SIFIVE_FE310_G000_PLL_HANDLE->clock, init_rate
350     );
351 }
352 #endif
353
354 __METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_fe310_g000_pll) = {
355     .init = __metal_driver_sifive_fe310_g000_pll_init,
356     .clock.get_rate_hz = __metal_driver_sifive_fe310_g000_pll_get_rate_hz,
357     .clock.set_rate_hz = __metal_driver_sifive_fe310_g000_pll_set_rate_hz,
358 };
359
360 #endif /* METAL_SIFIVE_FE310_G000_PLL */