1 /***************************************************************************//**
2  * @file
3  * @brief CMSIS Cortex-M0+ System Layer for EFM32HG 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 (24000000UL)
58 #endif
59 
60 /** Maximum HFRCO frequency. */
61 #define EFM32_HFRCO_MAX_FREQ (21000000UL)
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 /*******************************************************************************
85  **************************   GLOBAL VARIABLES   *******************************
86  ******************************************************************************/
87 
88 /**
89  * @brief
90  *   System System Clock Frequency (Core Clock).
91  *
92  * @details
93  *   Required CMSIS global variable that must be kept up-to-date.
94  */
95 uint32_t SystemCoreClock = 14000000UL;
96 
97 /*******************************************************************************
98  **************************   GLOBAL FUNCTIONS   *******************************
99  ******************************************************************************/
100 
101 /***************************************************************************//**
102  * @brief
103  *   Get the current core clock frequency.
104  *
105  * @details
106  *   Calculate and get the current core clock frequency based on the current
107  *   configuration. Assuming that the SystemCoreClock global variable is
108  *   maintained, the core clock frequency is stored in that variable as well.
109  *   This function will however calculate the core clock based on actual HW
110  *   configuration. It will also update the SystemCoreClock global variable.
111  *
112  * @note
113  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
114  *
115  * @return
116  *   The current core clock frequency in Hz.
117  ******************************************************************************/
SystemCoreClockGet(void)118 uint32_t SystemCoreClockGet(void)
119 {
120   uint32_t ret;
121 
122   ret = SystemHFClockGet();
123   ret >>= (CMU->HFCORECLKDIV & _CMU_HFCORECLKDIV_HFCORECLKDIV_MASK)
124           >> _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT;
125 
126   /* Keep CMSIS variable up-to-date just in case */
127   SystemCoreClock = ret;
128 
129   return ret;
130 }
131 
132 /***************************************************************************//**
133  * @brief
134  *   Get the maximum core clock frequency.
135  *
136  * @note
137  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
138  *
139  * @return
140  *   The maximum core clock frequency in Hz.
141  ******************************************************************************/
SystemMaxCoreClockGet(void)142 uint32_t SystemMaxCoreClockGet(void)
143 {
144 #if (EFM32_HFRCO_MAX_FREQ > EFM32_HFXO_FREQ)
145   return EFM32_HFRCO_MAX_FREQ;
146 #else
147   return EFM32_HFXO_FREQ;
148 #endif
149 }
150 
151 /***************************************************************************//**
152  * @brief
153  *   Get the current HFCLK frequency.
154  *
155  * @note
156  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
157  *
158  * @return
159  *   The current HFCLK frequency in Hz.
160  ******************************************************************************/
SystemHFClockGet(void)161 uint32_t SystemHFClockGet(void)
162 {
163   uint32_t ret;
164 
165   switch (CMU->STATUS & (CMU_STATUS_HFRCOSEL | CMU_STATUS_HFXOSEL
166                          | CMU_STATUS_LFRCOSEL | CMU_STATUS_LFXOSEL
167 #if defined(CMU_STATUS_USHFRCODIV2SEL)
168                          | CMU_STATUS_USHFRCODIV2SEL
169 #endif
170                          )) {
171     case CMU_STATUS_LFXOSEL:
172 #if (EFM32_LFXO_FREQ > 0U)
173       ret = SystemLFXOClock;
174 #else
175       /* We should not get here, since core should not be clocked. May */
176       /* be caused by a misconfiguration though. */
177       ret = 0U;
178 #endif
179       break;
180 
181     case CMU_STATUS_LFRCOSEL:
182       ret = EFM32_LFRCO_FREQ;
183       break;
184 
185     case CMU_STATUS_HFXOSEL:
186 #if (EFM32_HFXO_FREQ > 0U)
187       ret = SystemHFXOClock;
188 #else
189       /* We should not get here, since core should not be clocked. May */
190       /* be caused by a misconfiguration though. */
191       ret = 0U;
192 #endif
193       break;
194 
195 #if defined(CMU_STATUS_USHFRCODIV2SEL)
196     case CMU_STATUS_USHFRCODIV2SEL:
197       ret = 24000000U;
198       break;
199 #endif
200 
201     default: /* CMU_STATUS_HFRCOSEL */
202       switch (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_BAND_MASK) {
203         case CMU_HFRCOCTRL_BAND_21MHZ:
204           ret = 21000000U;
205           break;
206 
207         case CMU_HFRCOCTRL_BAND_14MHZ:
208           ret = 14000000U;
209           break;
210 
211         case CMU_HFRCOCTRL_BAND_11MHZ:
212           ret = 11000000U;
213           break;
214 
215         case CMU_HFRCOCTRL_BAND_7MHZ:
216           ret = 6600000U;
217           break;
218 
219         case CMU_HFRCOCTRL_BAND_1MHZ:
220           ret = 1200000U;
221           break;
222 
223         default:
224           ret = 0U;
225           break;
226       }
227       break;
228   }
229 
230   return ret / (1U + ((CMU->CTRL & _CMU_CTRL_HFCLKDIV_MASK)
231                       >> _CMU_CTRL_HFCLKDIV_SHIFT));
232 }
233 
234 /***************************************************************************//**
235  * @brief
236  *   Get high frequency crystal oscillator clock frequency for target system.
237  *
238  * @note
239  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
240  *
241  * @return
242  *   HFXO frequency in Hz.
243  ******************************************************************************/
SystemHFXOClockGet(void)244 uint32_t SystemHFXOClockGet(void)
245 {
246   /* External crystal oscillator present? */
247 #if (EFM32_HFXO_FREQ > 0U)
248   return SystemHFXOClock;
249 #else
250   return 0U;
251 #endif
252 }
253 
254 /***************************************************************************//**
255  * @brief
256  *   Set high frequency crystal oscillator clock frequency for target system.
257  *
258  * @note
259  *   This function is mainly provided for being able to handle target systems
260  *   with different HF crystal oscillator frequencies run-time. If used, it
261  *   should probably only be used once during system startup.
262  *
263  * @note
264  *   This is an EFM32 proprietary function, not part of the CMSIS definition.
265  *
266  * @param[in] freq
267  *   HFXO frequency in Hz used for target.
268  ******************************************************************************/
SystemHFXOClockSet(uint32_t freq)269 void SystemHFXOClockSet(uint32_t freq)
270 {
271   /* External crystal oscillator present? */
272 #if (EFM32_HFXO_FREQ > 0U)
273   SystemHFXOClock = freq;
274 
275   /* Update core clock frequency if HFXO is used to clock core */
276   if ((CMU->STATUS & CMU_STATUS_HFXOSEL) != 0U) {
277     /* The function will update the global variable */
278     (void)SystemCoreClockGet();
279   }
280 #else
281   (void)freq; /* Unused parameter */
282 #endif
283 }
284 
285 /***************************************************************************//**
286  * @brief
287  *   Initialize the system.
288  *
289  * @details
290  *   Do required generic HW system init.
291  *
292  * @note
293  *   This function is invoked during system init, before the main() routine
294  *   and any data has been initialized. For this reason, it cannot do any
295  *   initialization of variables etc.
296  ******************************************************************************/
SystemInit(void)297 void SystemInit(void)
298 {
299 #if defined(__VTOR_PRESENT) && (__VTOR_PRESENT == 1U)
300   SCB->VTOR = (uint32_t)&__Vectors;
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 EFM32 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 EFM32_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 EFM32 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 EFM32_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 EFM32 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 (EFM32_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 EFM32 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 (EFM32_LFXO_FREQ > 0U)
377   SystemLFXOClock = freq;
378 
379   /* Update core clock frequency if LFXO is used to clock core */
380   if ((CMU->STATUS & CMU_STATUS_LFXOSEL) != 0U) {
381     /* The function will update the global variable */
382     (void)SystemCoreClockGet();
383   }
384 #else
385   (void)freq; /* Unused parameter */
386 #endif
387 }
388