1 /***************************************************************************//**
2  * @file
3  * @brief CMSIS Cortex-M3/M4 System Layer for EFR32 devices.
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2020 Silicon Laboratories Inc. www.silabs.com</b>
7  *******************************************************************************
8  *
9  * SPDX-License-Identifier: Zlib
10  *
11  * The licensor of this software is Silicon Laboratories Inc.
12  *
13  * This software is provided 'as-is', without any express or implied
14  * warranty. In no event will the authors be held liable for any damages
15  * arising from the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software
23  *    in a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  * 2. Altered source versions must be plainly marked as such, and must not be
26  *    misrepresented as being the original software.
27  * 3. This notice may not be removed or altered from any source distribution.
28  *
29  ******************************************************************************/
30 
31 #include <stdint.h>
32 #include "em_device.h"
33 
34 /*******************************************************************************
35  ******************************   DEFINES   ************************************
36  ******************************************************************************/
37 
38 /** LFRCO frequency, tuned to below frequency during manufacturing. */
39 #define EFR32_LFRCO_FREQ  (32768UL)
40 /** ULFRCO frequency */
41 #define EFR32_ULFRCO_FREQ (1000UL)
42 
43 /*******************************************************************************
44  **************************   LOCAL VARIABLES   ********************************
45  ******************************************************************************/
46 
47 /* System oscillator frequencies. These frequencies are normally constant */
48 /* for a target, but they are made configurable in order to allow run-time */
49 /* handling of different boards. The crystal oscillator clocks can be set */
50 /* compile time to a non-default value by defining respective EFR32_nFXO_FREQ */
51 /* values according to board design. By defining the EFR32_nFXO_FREQ to 0, */
52 /* one indicates that the oscillator is not present, in order to save some */
53 /* SW footprint. */
54 
55 #ifndef EFR32_HFRCO_MAX_FREQ
56 /** Maximum HFRCO frequency */
57 #define EFR32_HFRCO_MAX_FREQ            (38000000UL)
58 #endif
59 
60 #ifndef EFR32_HFXO_FREQ
61 /** HFXO frequency */
62 #define EFR32_HFXO_FREQ                 (38400000UL)
63 #endif
64 
65 #ifndef EFR32_HFRCO_STARTUP_FREQ
66 /** HFRCO startup frequency */
67 #define EFR32_HFRCO_STARTUP_FREQ        (19000000UL)
68 #endif
69 
70 /* Do not define variable if HF crystal oscillator not present */
71 #if (EFR32_HFXO_FREQ > 0U)
72 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
73 /** System HFXO clock. */
74 static uint32_t SystemHFXOClock = EFR32_HFXO_FREQ;
75 /** @endcond (DO_NOT_INCLUDE_WITH_DOXYGEN) */
76 #endif
77 
78 #ifndef EFR32_LFXO_FREQ
79 /** LFXO frequency */
80 #define EFR32_LFXO_FREQ (EFR32_LFRCO_FREQ)
81 #endif
82 /* Do not define variable if LF crystal oscillator not present */
83 #if (EFR32_LFXO_FREQ > 0U)
84 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
85 /** System LFXO clock. */
86 static uint32_t SystemLFXOClock = EFR32_LFXO_FREQ;
87 /** @endcond (DO_NOT_INCLUDE_WITH_DOXYGEN) */
88 #endif
89 
90 /*******************************************************************************
91  **************************   GLOBAL VARIABLES   *******************************
92  ******************************************************************************/
93 
94 /**
95  * @brief
96  *   System System Clock Frequency (Core Clock).
97  *
98  * @details
99  *   Required CMSIS global variable that must be kept up-to-date.
100  */
101 uint32_t SystemCoreClock = EFR32_HFRCO_STARTUP_FREQ;
102 
103 /**
104  * @brief
105  *   System HFRCO frequency
106  *
107  * @note
108  *   This is an EFR32 proprietary variable, not part of the CMSIS definition.
109  *
110  * @details
111  *   Frequency of the system HFRCO oscillator
112  */
113 uint32_t SystemHfrcoFreq = EFR32_HFRCO_STARTUP_FREQ;
114 
115 /*******************************************************************************
116  **************************   GLOBAL FUNCTIONS   *******************************
117  ******************************************************************************/
118 
119 /***************************************************************************//**
120  * @brief
121  *   Get the current core clock frequency.
122  *
123  * @details
124  *   Calculate and get the current core clock frequency based on the current
125  *   configuration. Assuming that the SystemCoreClock global variable is
126  *   maintained, the core clock frequency is stored in that variable as well.
127  *   This function will however calculate the core clock based on actual HW
128  *   configuration. It will also update the SystemCoreClock global variable.
129  *
130  * @note
131  *   This is an EFR32 proprietary function, not part of the CMSIS definition.
132  *
133  * @return
134  *   The current core clock frequency in Hz.
135  ******************************************************************************/
SystemCoreClockGet(void)136 uint32_t SystemCoreClockGet(void)
137 {
138   uint32_t ret;
139   uint32_t presc;
140 
141   ret   = SystemHFClockGet();
142   presc = (CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK)
143           >> _CMU_HFCOREPRESC_PRESC_SHIFT;
144   ret  /= presc + 1U;
145 
146   /* Keep CMSIS system clock variable up-to-date */
147   SystemCoreClock = ret;
148 
149   return ret;
150 }
151 
152 /***************************************************************************//**
153  * @brief
154  *   Get the maximum core clock frequency.
155  *
156  * @note
157  *   This is an EFR32 proprietary function, not part of the CMSIS definition.
158  *
159  * @return
160  *   The maximum core clock frequency in Hz.
161  ******************************************************************************/
SystemMaxCoreClockGet(void)162 uint32_t SystemMaxCoreClockGet(void)
163 {
164 #if (EFR32_HFRCO_MAX_FREQ > EFR32_HFXO_FREQ)
165   return EFR32_HFRCO_MAX_FREQ;
166 #else
167   return EFR32_HFXO_FREQ;
168 #endif
169 }
170 
171 /***************************************************************************//**
172  * @brief
173  *   Get the current HFCLK frequency.
174  *
175  * @note
176  *   This is an EFR proprietary function, not part of the CMSIS definition.
177  *
178  * @return
179  *   The current HFCLK frequency in Hz.
180  ******************************************************************************/
SystemHFClockGet(void)181 uint32_t SystemHFClockGet(void)
182 {
183   uint32_t ret;
184 
185   switch (CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK) {
186     case CMU_HFCLKSTATUS_SELECTED_LFXO:
187 #if (EFR32_LFXO_FREQ > 0U)
188       ret = SystemLFXOClock;
189 #else
190       /* We should not get here, since core should not be clocked. May */
191       /* be caused by a misconfiguration though. */
192       ret = 0U;
193 #endif
194       break;
195 
196     case CMU_HFCLKSTATUS_SELECTED_LFRCO:
197       ret = EFR32_LFRCO_FREQ;
198       break;
199 
200     case CMU_HFCLKSTATUS_SELECTED_HFXO:
201 #if (EFR32_HFXO_FREQ > 0U)
202       ret = SystemHFXOClock;
203 #else
204       /* We should not get here, since core should not be clocked. May */
205       /* be caused by a misconfiguration though. */
206       ret = 0U;
207 #endif
208       break;
209 
210     case CMU_HFCLKSTATUS_SELECTED_HFRCODIV2:
211       ret = SystemHfrcoFreq / 2;
212       break;
213 
214     default: /* CMU_HFCLKSTATUS_SELECTED_HFRCO */
215       ret = SystemHfrcoFreq;
216       break;
217   }
218 
219   return ret / (1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
220                       >> _CMU_HFPRESC_PRESC_SHIFT));
221 }
222 
223 /***************************************************************************//**
224  * @brief
225  *   Get high frequency crystal oscillator clock frequency for target system.
226  *
227  * @note
228  *   This is an EFR proprietary function, not part of the CMSIS definition.
229  *
230  * @return
231  *   HFXO frequency in Hz.
232  ******************************************************************************/
SystemHFXOClockGet(void)233 uint32_t SystemHFXOClockGet(void)
234 {
235   /* External crystal oscillator present? */
236 #if (EFR32_HFXO_FREQ > 0U)
237   return SystemHFXOClock;
238 #else
239   return 0U;
240 #endif
241 }
242 
243 /***************************************************************************//**
244  * @brief
245  *   Set high frequency crystal oscillator clock frequency for target system.
246  *
247  * @note
248  *   This function is mainly provided for being able to handle target systems
249  *   with different HF crystal oscillator frequencies run-time. If used, it
250  *   should probably only be used once during system startup.
251  *
252  * @note
253  *   This is an EFR proprietary function, not part of the CMSIS definition.
254  *
255  * @param[in] freq
256  *   HFXO frequency in Hz used for target.
257  ******************************************************************************/
SystemHFXOClockSet(uint32_t freq)258 void SystemHFXOClockSet(uint32_t freq)
259 {
260   /* External crystal oscillator present? */
261 #if (EFR32_HFXO_FREQ > 0U)
262   SystemHFXOClock = freq;
263 
264   /* Update core clock frequency if HFXO is used to clock core */
265   if ((CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK)
266       == CMU_HFCLKSTATUS_SELECTED_HFXO) {
267     /* The function will update the global variable */
268     (void)SystemCoreClockGet();
269   }
270 #else
271   (void)freq; /* Unused parameter */
272 #endif
273 }
274 
275 /***************************************************************************//**
276  * @brief
277  *   Initialize the system.
278  *
279  * @details
280  *   Do required generic HW system init.
281  *
282  * @note
283  *   This function is invoked during system init, before the main() routine
284  *   and any data has been initialized. For this reason, it cannot do any
285  *   initialization of variables etc.
286  ******************************************************************************/
SystemInit(void)287 void SystemInit(void)
288 {
289 #if defined(__VTOR_PRESENT) && (__VTOR_PRESENT == 1U)
290   SCB->VTOR = (uint32_t)&__Vectors;
291 #endif
292 
293 #if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U)
294   /* Set floating point coprosessor access mode. */
295   SCB->CPACR |= ((3UL << 10 * 2)                    /* set CP10 Full Access */
296                  | (3UL << 11 * 2));                /* set CP11 Full Access */
297 #endif
298 
299 #if defined(UNALIGNED_SUPPORT_DISABLE)
300   SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk;
301 #endif
302 }
303 
304 /***************************************************************************//**
305  * @brief
306  *   Get low frequency RC oscillator clock frequency for target system.
307  *
308  * @note
309  *   This is an EFR proprietary function, not part of the CMSIS definition.
310  *
311  * @return
312  *   LFRCO frequency in Hz.
313  ******************************************************************************/
SystemLFRCOClockGet(void)314 uint32_t SystemLFRCOClockGet(void)
315 {
316   /* Currently we assume that this frequency is properly tuned during */
317   /* manufacturing and is not changed after reset. If future requirements */
318   /* for re-tuning by user, we can add support for that. */
319   return EFR32_LFRCO_FREQ;
320 }
321 
322 /***************************************************************************//**
323  * @brief
324  *   Get ultra low frequency RC oscillator clock frequency for target system.
325  *
326  * @note
327  *   This is an EFR proprietary function, not part of the CMSIS definition.
328  *
329  * @return
330  *   ULFRCO frequency in Hz.
331  ******************************************************************************/
SystemULFRCOClockGet(void)332 uint32_t SystemULFRCOClockGet(void)
333 {
334   /* The ULFRCO frequency is not tuned, and can be very inaccurate */
335   return EFR32_ULFRCO_FREQ;
336 }
337 
338 /***************************************************************************//**
339  * @brief
340  *   Get low frequency crystal oscillator clock frequency for target system.
341  *
342  * @note
343  *   This is an EFR proprietary function, not part of the CMSIS definition.
344  *
345  * @return
346  *   LFXO frequency in Hz.
347  ******************************************************************************/
SystemLFXOClockGet(void)348 uint32_t SystemLFXOClockGet(void)
349 {
350   /* External crystal oscillator present? */
351 #if (EFR32_LFXO_FREQ > 0U)
352   return SystemLFXOClock;
353 #else
354   return 0U;
355 #endif
356 }
357 
358 /***************************************************************************//**
359  * @brief
360  *   Set low frequency crystal oscillator clock frequency for target system.
361  *
362  * @note
363  *   This function is mainly provided for being able to handle target systems
364  *   with different HF crystal oscillator frequencies run-time. If used, it
365  *   should probably only be used once during system startup.
366  *
367  * @note
368  *   This is an EFR proprietary function, not part of the CMSIS definition.
369  *
370  * @param[in] freq
371  *   LFXO frequency in Hz used for target.
372  ******************************************************************************/
SystemLFXOClockSet(uint32_t freq)373 void SystemLFXOClockSet(uint32_t freq)
374 {
375   /* External crystal oscillator present? */
376 #if (EFR32_LFXO_FREQ > 0U)
377   SystemLFXOClock = freq;
378 
379   /* Update core clock frequency if LFXO is used to clock core */
380   if ((CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK)
381       == CMU_HFCLKSTATUS_SELECTED_LFXO) {
382     /* The function will update the global variable */
383     (void)SystemCoreClockGet();
384   }
385 #else
386   (void)freq; /* Unused parameter */
387 #endif
388 }
389