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