1 /***************************************************************************//**
2  * @file
3  * @brief CMSIS Cortex-M4 System Layer for EFM32WG 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 EFM32_LFRCO_FREQ  (32768UL)
40 /** ULFRCO frequency. */
41 #define EFM32_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 EFM32_nFXO_FREQ */
51 /* values according to board design. By defining the EFM32_nFXO_FREQ to 0, */
52 /* one indicates that the oscillator is not present, in order to save some */
53 /* SW footprint. */
54 
55 #ifndef EFM32_HFXO_FREQ
56 /** HFXO frequency. */
57 #define EFM32_HFXO_FREQ (48000000UL)
58 #endif
59 
60 /** Maximum HFRCO frequency. */
61 #define EFM32_HFRCO_MAX_FREQ (28000000UL)
62 
63 /* Do not define variable if HF crystal oscillator not present */
64 #if (EFM32_HFXO_FREQ > 0U)
65 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
66 /** System HFXO clock. */
67 static uint32_t SystemHFXOClock = EFM32_HFXO_FREQ;
68 /** @endcond (DO_NOT_INCLUDE_WITH_DOXYGEN) */
69 #endif
70 
71 #ifndef EFM32_LFXO_FREQ
72 /** LFXO frequency. */
73 #define EFM32_LFXO_FREQ (EFM32_LFRCO_FREQ)
74 #endif
75 
76 /* Do not define variable if LF crystal oscillator not present */
77 #if (EFM32_LFXO_FREQ > 0U)
78 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
79 /** System LFXO clock. */
80 static uint32_t SystemLFXOClock = EFM32_LFXO_FREQ;
81 /** @endcond (DO_NOT_INCLUDE_WITH_DOXYGEN) */
82 #endif
83 
84 /* Inline function to get the chip's Production Revision. */
GetProdRev(void)85 __STATIC_INLINE uint8_t GetProdRev(void)
86 {
87   return (uint8_t)((DEVINFO->PART & _DEVINFO_PART_PROD_REV_MASK)
88                    >> _DEVINFO_PART_PROD_REV_SHIFT);
89 }
90 
91 /*******************************************************************************
92  **************************   GLOBAL VARIABLES   *******************************
93  ******************************************************************************/
94 
95 /**
96  * @brief
97  *   System System Clock Frequency (Core Clock).
98  *
99  * @details
100  *   Required CMSIS global variable that must be kept up-to-date.
101  */
102 uint32_t SystemCoreClock = 14000000UL;
103 
104 /*******************************************************************************
105  **************************   GLOBAL FUNCTIONS   *******************************
106  ******************************************************************************/
107 
108 /***************************************************************************//**
109  * @brief
110  *   Get the current core clock frequency.
111  *
112  * @details
113  *   Calculate and get the current core clock frequency based on the current
114  *   configuration. Assuming that the SystemCoreClock global variable is
115  *   maintained, the core clock frequency is stored in that variable as well.
116  *   This function will however calculate the core clock based on actual HW
117  *   configuration. It will also update the SystemCoreClock global variable.
118  *
119  * @note
120  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
121  *
122  * @return
123  *   The current core clock frequency in Hz.
124  ******************************************************************************/
SystemCoreClockGet(void)125 uint32_t SystemCoreClockGet(void)
126 {
127   uint32_t ret;
128 
129   ret = SystemHFClockGet();
130   ret >>= (CMU->HFCORECLKDIV & _CMU_HFCORECLKDIV_HFCORECLKDIV_MASK)
131           >> _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT;
132 
133   /* Keep CMSIS variable up-to-date just in case */
134   SystemCoreClock = ret;
135 
136   return ret;
137 }
138 
139 /***************************************************************************//**
140  * @brief
141  *   Get the maximum core clock frequency.
142  *
143  * @note
144  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
145  *
146  * @return
147  *   The maximum core clock frequency in Hz.
148  ******************************************************************************/
SystemMaxCoreClockGet(void)149 uint32_t SystemMaxCoreClockGet(void)
150 {
151 #if (EFM32_HFRCO_MAX_FREQ > EFM32_HFXO_FREQ)
152   return EFM32_HFRCO_MAX_FREQ;
153 #else
154   return EFM32_HFXO_FREQ;
155 #endif
156 }
157 
158 /***************************************************************************//**
159  * @brief
160  *   Get the current HFCLK frequency.
161  *
162  * @note
163  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
164  *
165  * @return
166  *   The current HFCLK frequency in Hz.
167  ******************************************************************************/
SystemHFClockGet(void)168 uint32_t SystemHFClockGet(void)
169 {
170   uint32_t ret;
171 
172   switch (CMU->STATUS & (CMU_STATUS_HFRCOSEL | CMU_STATUS_HFXOSEL
173                          | CMU_STATUS_LFRCOSEL | CMU_STATUS_LFXOSEL)) {
174     case CMU_STATUS_LFXOSEL:
175 #if (EFM32_LFXO_FREQ > 0U)
176       ret = SystemLFXOClock;
177 #else
178       /* We should not get here, since core should not be clocked. May */
179       /* be caused by a misconfiguration though. */
180       ret = 0U;
181 #endif
182       break;
183 
184     case CMU_STATUS_LFRCOSEL:
185       ret = EFM32_LFRCO_FREQ;
186       break;
187 
188     case CMU_STATUS_HFXOSEL:
189 #if (EFM32_HFXO_FREQ > 0U)
190       ret = SystemHFXOClock;
191 #else
192       /* We should not get here, since core should not be clocked. May */
193       /* be caused by a misconfiguration though. */
194       ret = 0U;
195 #endif
196       break;
197 
198     default: /* CMU_STATUS_HFRCOSEL */
199       switch (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_BAND_MASK) {
200         case CMU_HFRCOCTRL_BAND_28MHZ:
201           ret = 28000000U;
202           break;
203 
204         case CMU_HFRCOCTRL_BAND_21MHZ:
205           ret = 21000000U;
206           break;
207 
208         case CMU_HFRCOCTRL_BAND_14MHZ:
209           ret = 14000000U;
210           break;
211 
212         case CMU_HFRCOCTRL_BAND_11MHZ:
213           ret = 11000000U;
214           break;
215 
216         case CMU_HFRCOCTRL_BAND_7MHZ:
217           if ( GetProdRev() >= 19U ) {
218             ret = 6600000U;
219           } else {
220             ret = 7000000U;
221           }
222           break;
223 
224         case CMU_HFRCOCTRL_BAND_1MHZ:
225           if ( GetProdRev() >= 19U ) {
226             ret = 1200000U;
227           } else {
228             ret = 1000000U;
229           }
230           break;
231 
232         default:
233           ret = 0U;
234           break;
235       }
236       break;
237   }
238 
239   return ret / (1U + ((CMU->CTRL & _CMU_CTRL_HFCLKDIV_MASK)
240                       >> _CMU_CTRL_HFCLKDIV_SHIFT));
241 }
242 
243 /***************************************************************************//**
244  * @brief
245  *   Get high frequency crystal oscillator clock frequency for target system.
246  *
247  * @note
248  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
249  *
250  * @return
251  *   HFXO frequency in Hz.
252  ******************************************************************************/
SystemHFXOClockGet(void)253 uint32_t SystemHFXOClockGet(void)
254 {
255   /* External crystal oscillator present? */
256 #if (EFM32_HFXO_FREQ > 0U)
257   return SystemHFXOClock;
258 #else
259   return 0U;
260 #endif
261 }
262 
263 /***************************************************************************//**
264  * @brief
265  *   Set high frequency crystal oscillator clock frequency for target system.
266  *
267  * @note
268  *   This function is mainly provided for being able to handle target systems
269  *   with different HF crystal oscillator frequencies run-time. If used, it
270  *   should probably only be used once during system startup.
271  *
272  * @note
273  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
274  *
275  * @param[in] freq
276  *   HFXO frequency in Hz used for target.
277  ******************************************************************************/
SystemHFXOClockSet(uint32_t freq)278 void SystemHFXOClockSet(uint32_t freq)
279 {
280   /* External crystal oscillator present? */
281 #if (EFM32_HFXO_FREQ > 0U)
282   SystemHFXOClock = freq;
283 
284   /* Update core clock frequency if HFXO is used to clock core */
285   if ((CMU->STATUS & CMU_STATUS_HFXOSEL) != 0U) {
286     /* The function will update the global variable */
287     (void)SystemCoreClockGet();
288   }
289 #else
290   (void)freq; /* Unused parameter */
291 #endif
292 }
293 
294 /***************************************************************************//**
295  * @brief
296  *   Initialize the system.
297  *
298  * @details
299  *   Do required generic HW system init.
300  *
301  * @note
302  *   This function is invoked during system init, before the main() routine
303  *   and any data has been initialized. For this reason, it cannot do any
304  *   initialization of variables etc.
305  ******************************************************************************/
SystemInit(void)306 void SystemInit(void)
307 {
308 #if defined(__VTOR_PRESENT) && (__VTOR_PRESENT == 1U)
309   SCB->VTOR = (uint32_t)&__Vectors;
310 #endif
311 
312 #if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U)
313   /* Set floating point coprosessor access mode. */
314   SCB->CPACR |= ((3UL << 10 * 2)                    /* set CP10 Full Access */
315                  | (3UL << 11 * 2));                /* set CP11 Full Access */
316 #endif
317 
318 #if defined(UNALIGNED_SUPPORT_DISABLE)
319   SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk;
320 #endif
321 }
322 
323 /***************************************************************************//**
324  * @brief
325  *   Get low frequency RC oscillator clock frequency for target system.
326  *
327  * @note
328  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
329  *
330  * @return
331  *   LFRCO frequency in Hz.
332  ******************************************************************************/
SystemLFRCOClockGet(void)333 uint32_t SystemLFRCOClockGet(void)
334 {
335   /* Currently we assume that this frequency is properly tuned during */
336   /* manufacturing and is not changed after reset. If future requirements */
337   /* for re-tuning by user, we can add support for that. */
338   return EFM32_LFRCO_FREQ;
339 }
340 
341 /***************************************************************************//**
342  * @brief
343  *   Get ultra low frequency RC oscillator clock frequency for target system.
344  *
345  * @note
346  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
347  *
348  * @return
349  *   ULFRCO frequency in Hz.
350  ******************************************************************************/
SystemULFRCOClockGet(void)351 uint32_t SystemULFRCOClockGet(void)
352 {
353   /* The ULFRCO frequency is not tuned, and can be very inaccurate */
354   return EFM32_ULFRCO_FREQ;
355 }
356 
357 /***************************************************************************//**
358  * @brief
359  *   Get low frequency crystal oscillator clock frequency for target system.
360  *
361  * @note
362  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
363  *
364  * @return
365  *   LFXO frequency in Hz.
366  ******************************************************************************/
SystemLFXOClockGet(void)367 uint32_t SystemLFXOClockGet(void)
368 {
369   /* External crystal oscillator present? */
370 #if (EFM32_LFXO_FREQ > 0U)
371   return SystemLFXOClock;
372 #else
373   return 0U;
374 #endif
375 }
376 
377 /***************************************************************************//**
378  * @brief
379  *   Set low frequency crystal oscillator clock frequency for target system.
380  *
381  * @note
382  *   This function is mainly provided for being able to handle target systems
383  *   with different HF crystal oscillator frequencies run-time. If used, it
384  *   should probably only be used once during system startup.
385  *
386  * @note
387  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
388  *
389  * @param[in] freq
390  *   LFXO frequency in Hz used for target.
391  ******************************************************************************/
SystemLFXOClockSet(uint32_t freq)392 void SystemLFXOClockSet(uint32_t freq)
393 {
394   /* External crystal oscillator present? */
395 #if (EFM32_LFXO_FREQ > 0U)
396   SystemLFXOClock = freq;
397 
398   /* Update core clock frequency if LFXO is used to clock core */
399   if ((CMU->STATUS & CMU_STATUS_LFXOSEL) != 0U) {
400     /* The function will update the global variable */
401     (void)SystemCoreClockGet();
402   }
403 #else
404   (void)freq; /* Unused parameter */
405 #endif
406 }
407