1 // See LICENSE file for license details
\r
3 #include "platform.h"
\r
5 #ifdef PRCI_CTRL_ADDR
\r
6 #include "fe300prci/fe300prci_driver.h"
\r
9 #define rdmcycle(x) { \
\r
10 uint32_t lo, hi, hi2; \
\r
11 __asm__ __volatile__ ("1:\n\t" \
\r
12 "csrr %0, mcycleh\n\t" \
\r
13 "csrr %1, mcycle\n\t" \
\r
14 "csrr %2, mcycleh\n\t" \
\r
15 "bne %0, %2, 1b\n\t" \
\r
16 : "=r" (hi), "=r" (lo), "=r" (hi2)) ; \
\r
17 *(x) = lo | ((uint64_t) hi << 32); \
\r
20 uint32_t PRCI_measure_mcycle_freq(uint32_t mtime_ticks, uint32_t mtime_freq)
\r
23 uint32_t start_mtime = CLINT_REG(CLINT_MTIME);
\r
24 uint32_t end_mtime = start_mtime + mtime_ticks + 1;
\r
26 // Make sure we won't get rollover.
\r
27 while (end_mtime < start_mtime){
\r
28 start_mtime = CLINT_REG(CLINT_MTIME);
\r
29 end_mtime = start_mtime + mtime_ticks + 1;
\r
32 // Don't start measuring until mtime edge.
\r
33 uint32_t tmp = start_mtime;
\r
35 start_mtime = CLINT_REG(CLINT_MTIME);
\r
36 } while (start_mtime == tmp);
\r
38 uint64_t start_mcycle;
\r
39 rdmcycle(&start_mcycle);
\r
41 while (CLINT_REG(CLINT_MTIME) < end_mtime) ;
\r
43 uint64_t end_mcycle;
\r
44 rdmcycle(&end_mcycle);
\r
45 uint32_t difference = (uint32_t) (end_mcycle - start_mcycle);
\r
47 uint64_t freq = ((uint64_t) difference * mtime_freq) / mtime_ticks;
\r
48 return (uint32_t) freq & 0xFFFFFFFF;
\r
53 void PRCI_use_hfrosc(int div, int trim)
\r
55 // Make sure the HFROSC is running at its default setting
\r
56 // It is OK to change this even if we are running off of it.
\r
58 PRCI_REG(PRCI_HFROSCCFG) = (ROSC_DIV(div) | ROSC_TRIM(trim) | ROSC_EN(1));
\r
60 while ((PRCI_REG(PRCI_HFROSCCFG) & ROSC_RDY(1)) == 0);
\r
62 PRCI_REG(PRCI_PLLCFG) &= ~PLL_SEL(1);
\r
65 void PRCI_use_pll(int refsel, int bypass,
\r
66 int r, int f, int q, int finaldiv,
\r
67 int hfroscdiv, int hfrosctrim)
\r
69 // Ensure that we aren't running off the PLL before we mess with it.
\r
70 if (PRCI_REG(PRCI_PLLCFG) & PLL_SEL(1)) {
\r
71 // Make sure the HFROSC is running at its default setting
\r
72 PRCI_use_hfrosc(4, 16);
\r
75 // Set PLL Source to be HFXOSC if desired.
\r
76 uint32_t config_value = 0;
\r
78 config_value |= PLL_REFSEL(refsel);
\r
82 config_value |= PLL_BYPASS(1);
\r
84 PRCI_REG(PRCI_PLLCFG) = config_value;
\r
86 // If we don't have an HFXTAL, this doesn't really matter.
\r
87 // Set our Final output divide to divide-by-1:
\r
88 PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0));
\r
91 // To overclock, use the hfrosc
\r
92 if (hfrosctrim >= 0 && hfroscdiv >= 0) {
\r
93 PRCI_use_hfrosc(hfroscdiv, hfrosctrim);
\r
96 // Set DIV Settings for PLL
\r
98 // (Legal values of f_REF are 6-48MHz)
\r
100 // Set DIVR to divide-by-2 to get 8MHz frequency
\r
101 // (legal values of f_R are 6-12 MHz)
\r
103 config_value |= PLL_BYPASS(1);
\r
104 config_value |= PLL_R(r);
\r
106 // Set DIVF to get 512Mhz frequncy
\r
107 // There is an implied multiply-by-2, 16Mhz.
\r
108 // So need to write 32-1
\r
109 // (legal values of f_F are 384-768 MHz)
\r
110 config_value |= PLL_F(f);
\r
112 // Set DIVQ to divide-by-2 to get 256 MHz frequency
\r
113 // (legal values of f_Q are 50-400Mhz)
\r
114 config_value |= PLL_Q(q);
\r
116 // Set our Final output divide to divide-by-1:
\r
117 if (finaldiv == 1){
\r
118 PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0));
\r
120 PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV(finaldiv-1));
\r
123 PRCI_REG(PRCI_PLLCFG) = config_value;
\r
125 // Un-Bypass the PLL.
\r
126 PRCI_REG(PRCI_PLLCFG) &= ~PLL_BYPASS(1);
\r
128 // Wait for PLL Lock
\r
129 // Note that the Lock signal can be glitchy.
\r
130 // Need to wait 100 us
\r
131 // RTC is running at 32kHz.
\r
132 // So wait 4 ticks of RTC.
\r
133 uint32_t now = CLINT_REG(CLINT_MTIME);
\r
134 while (CLINT_REG(CLINT_MTIME) - now < 4) ;
\r
136 // Now it is safe to check for PLL Lock
\r
137 while ((PRCI_REG(PRCI_PLLCFG) & PLL_LOCK(1)) == 0);
\r
141 // Switch over to PLL Clock source
\r
142 PRCI_REG(PRCI_PLLCFG) |= PLL_SEL(1);
\r
144 // If we're running off HFXOSC, turn off the HFROSC to
\r
147 PRCI_REG(PRCI_HFROSCCFG) &= ~ROSC_EN(1);
\r
152 void PRCI_use_default_clocks()
\r
154 // Turn off the LFROSC
\r
155 AON_REG(AON_LFROSC) &= ~ROSC_EN(1);
\r
158 PRCI_use_hfrosc(4, 16);
\r
161 void PRCI_use_hfxosc(uint32_t finaldiv)
\r
164 PRCI_use_pll(1, // Use HFXTAL
\r
166 0, // PLL settings don't matter
\r
167 0, // PLL settings don't matter
\r
168 0, // PLL settings don't matter
\r
174 // This is a generic function, which
\r
175 // doesn't span the entire range of HFROSC settings.
\r
176 // It only adjusts the trim, which can span a hundred MHz or so.
\r
177 // This function does not check the legality of the PLL settings
\r
178 // at all, and it is quite possible to configure invalid PLL settings
\r
180 // It returns the actual measured CPU frequency.
\r
182 uint32_t PRCI_set_hfrosctrim_for_f_cpu(uint32_t f_cpu, PRCI_freq_target target )
\r
185 uint32_t hfrosctrim = 0;
\r
186 uint32_t hfroscdiv = 4;
\r
187 uint32_t prev_trim = 0;
\r
189 // In this function we use PLL settings which
\r
190 // will give us a 32x multiplier from the output
\r
191 // of the HFROSC source to the output of the
\r
192 // PLL. We first measure our HFROSC to get the
\r
193 // right trim, then finally use it as the PLL source.
\r
194 // We should really check here that the f_cpu
\r
195 // requested is something in the limit of the PLL. For
\r
196 // now that is up to the user.
\r
198 // This will undershoot for frequencies not divisible by 16.
\r
199 uint32_t desired_hfrosc_freq = (f_cpu/ 16);
\r
201 PRCI_use_hfrosc(hfroscdiv, hfrosctrim);
\r
203 // Ignore the first run (for icache reasons)
\r
204 uint32_t cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ);
\r
206 cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ);
\r
207 uint32_t prev_freq = cpu_freq;
\r
209 while ((cpu_freq < desired_hfrosc_freq) && (hfrosctrim < 0x1F)){
\r
210 prev_trim = hfrosctrim;
\r
211 prev_freq = cpu_freq;
\r
213 PRCI_use_hfrosc(hfroscdiv, hfrosctrim);
\r
214 cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ);
\r
217 // We couldn't go low enough
\r
218 if (prev_freq > desired_hfrosc_freq){
\r
219 PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim);
\r
220 cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
\r
224 // We couldn't go high enough
\r
225 if (cpu_freq < desired_hfrosc_freq){
\r
226 PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim);
\r
227 cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
\r
231 // Check for over/undershoot
\r
233 case(PRCI_FREQ_CLOSEST):
\r
234 if ((desired_hfrosc_freq - prev_freq) < (cpu_freq - desired_hfrosc_freq)) {
\r
235 PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim);
\r
237 PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, hfrosctrim);
\r
240 case(PRCI_FREQ_UNDERSHOOT):
\r
241 PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim);
\r
244 PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, hfrosctrim);
\r
247 cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ);
\r