]> begriffs open source - freertos/blob - FreeRTOS/Demo/RISC-V-Qemu-sifive_e-FreedomStudio/freedom-e-sdk/drivers/fe300prci/fe300prci_driver.c
Add a starting point for a Freedom Studio Risc V project.
[freertos] / FreeRTOS / Demo / RISC-V-Qemu-sifive_e-FreedomStudio / freedom-e-sdk / drivers / fe300prci / fe300prci_driver.c
1 // See LICENSE file for license details\r
2 \r
3 #include "platform.h"\r
4 \r
5 #ifdef PRCI_CTRL_ADDR\r
6 #include "fe300prci/fe300prci_driver.h"\r
7 #include <unistd.h>\r
8 \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
18   }\r
19 \r
20 uint32_t PRCI_measure_mcycle_freq(uint32_t mtime_ticks, uint32_t mtime_freq)\r
21 {\r
22 \r
23   uint32_t start_mtime = CLINT_REG(CLINT_MTIME);\r
24   uint32_t end_mtime = start_mtime + mtime_ticks + 1;\r
25 \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
30   }\r
31 \r
32   // Don't start measuring until mtime edge.\r
33   uint32_t tmp = start_mtime;\r
34   do {\r
35     start_mtime = CLINT_REG(CLINT_MTIME);\r
36   } while (start_mtime == tmp);\r
37   \r
38   uint64_t start_mcycle;\r
39   rdmcycle(&start_mcycle);\r
40   \r
41   while (CLINT_REG(CLINT_MTIME) < end_mtime) ;\r
42   \r
43   uint64_t end_mcycle;\r
44   rdmcycle(&end_mcycle);\r
45   uint32_t difference = (uint32_t) (end_mcycle - start_mcycle);\r
46 \r
47   uint64_t freq = ((uint64_t) difference * mtime_freq) / mtime_ticks;\r
48   return (uint32_t) freq & 0xFFFFFFFF;\r
49   \r
50 }\r
51  \r
52 \r
53 void PRCI_use_hfrosc(int div, int trim)\r
54 {\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
57   \r
58   PRCI_REG(PRCI_HFROSCCFG) = (ROSC_DIV(div) | ROSC_TRIM(trim) | ROSC_EN(1));\r
59 \r
60   while ((PRCI_REG(PRCI_HFROSCCFG) & ROSC_RDY(1)) == 0);\r
61   \r
62   PRCI_REG(PRCI_PLLCFG) &= ~PLL_SEL(1);\r
63 }\r
64 \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
68 {\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
73   }\r
74   \r
75   // Set PLL Source to be HFXOSC if desired.\r
76   uint32_t config_value = 0;\r
77 \r
78   config_value |= PLL_REFSEL(refsel);\r
79   \r
80   if (bypass) {\r
81     // Bypass\r
82     config_value |= PLL_BYPASS(1);\r
83 \r
84     PRCI_REG(PRCI_PLLCFG) = config_value;\r
85 \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
89   } else {\r
90   \r
91     // To overclock, use the hfrosc\r
92     if (hfrosctrim >= 0 && hfroscdiv >= 0) {\r
93       PRCI_use_hfrosc(hfroscdiv, hfrosctrim);\r
94     }\r
95     \r
96     // Set DIV Settings for PLL\r
97     \r
98     // (Legal values of f_REF are 6-48MHz)\r
99 \r
100     // Set DIVR to divide-by-2 to get 8MHz frequency\r
101     // (legal values of f_R are 6-12 MHz)\r
102 \r
103     config_value |= PLL_BYPASS(1);\r
104     config_value |= PLL_R(r);\r
105 \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
111 \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
115 \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
119     } else {\r
120       PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV(finaldiv-1));\r
121     }\r
122 \r
123     PRCI_REG(PRCI_PLLCFG) = config_value;\r
124 \r
125     // Un-Bypass the PLL.\r
126     PRCI_REG(PRCI_PLLCFG) &= ~PLL_BYPASS(1);\r
127 \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
135     \r
136     // Now it is safe to check for PLL Lock\r
137     while ((PRCI_REG(PRCI_PLLCFG) & PLL_LOCK(1)) == 0);\r
138 \r
139   }\r
140 \r
141   // Switch over to PLL Clock source\r
142   PRCI_REG(PRCI_PLLCFG) |= PLL_SEL(1);\r
143 \r
144   // If we're running off HFXOSC, turn off the HFROSC to\r
145   // save power.\r
146   if (refsel) {\r
147     PRCI_REG(PRCI_HFROSCCFG) &= ~ROSC_EN(1);\r
148   }\r
149   \r
150 }\r
151 \r
152 void PRCI_use_default_clocks()\r
153 {\r
154   // Turn off the LFROSC\r
155   AON_REG(AON_LFROSC) &= ~ROSC_EN(1);\r
156 \r
157   // Use HFROSC\r
158   PRCI_use_hfrosc(4, 16);\r
159 }\r
160 \r
161 void PRCI_use_hfxosc(uint32_t finaldiv)\r
162 {\r
163   \r
164   PRCI_use_pll(1, // Use HFXTAL\r
165                1, // Bypass = 1\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
169                finaldiv,\r
170                -1,\r
171                -1);\r
172 }\r
173 \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
179 // this way.\r
180 // It returns the actual measured CPU frequency.\r
181 \r
182 uint32_t PRCI_set_hfrosctrim_for_f_cpu(uint32_t f_cpu, PRCI_freq_target target )\r
183 {\r
184 \r
185   uint32_t hfrosctrim = 0;\r
186   uint32_t hfroscdiv = 4;\r
187   uint32_t prev_trim = 0;\r
188 \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
197 \r
198   // This will undershoot for frequencies not divisible by 16.\r
199   uint32_t desired_hfrosc_freq = (f_cpu/ 16);\r
200 \r
201   PRCI_use_hfrosc(hfroscdiv, hfrosctrim);\r
202   \r
203   // Ignore the first run (for icache reasons)\r
204   uint32_t cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ);\r
205 \r
206   cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ);\r
207   uint32_t prev_freq = cpu_freq;\r
208   \r
209   while ((cpu_freq < desired_hfrosc_freq) && (hfrosctrim < 0x1F)){\r
210     prev_trim = hfrosctrim;\r
211     prev_freq = cpu_freq;\r
212     hfrosctrim ++;\r
213     PRCI_use_hfrosc(hfroscdiv, hfrosctrim);\r
214     cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ);\r
215   } \r
216 \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
221     return cpu_freq;\r
222   }\r
223   \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
228     return cpu_freq;\r
229   }\r
230 \r
231   // Check for over/undershoot\r
232   switch(target) {\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
236     } else {\r
237       PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, hfrosctrim);\r
238     }\r
239     break;\r
240   case(PRCI_FREQ_UNDERSHOOT):\r
241     PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim);\r
242     break;\r
243   default:\r
244     PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, hfrosctrim);\r
245   }\r
246 \r
247   cpu_freq =  PRCI_measure_mcycle_freq(1000, RTC_FREQ);\r
248   return cpu_freq;\r
249 \r
250 }\r
251 \r
252 #endif\r