1 /***************************************************************************//**
2  * @file
3  * @brief CMSIS Cortex-M33 system support for EFR32MG21 devices.
4  ******************************************************************************
5  * # License
6  * <b>Copyright 2024 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 #if defined(SL_COMPONENT_CATALOG_PRESENT)
35 #include "sl_component_catalog.h"
36 
37 #endif
38 #if defined(SL_CATALOG_CLOCK_MANAGER_PRESENT)
39 #include "sl_clock_manager_oscillator_config.h"
40 
41 #endif
42 
43 /*******************************************************************************
44  ******************************   DEFINES   ************************************
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 nFXO_FREQ
51 // values according to board design. By defining the nFXO_FREQ to 0,
52 // one indicates that the oscillator is not present, in order to save some
53 // SW footprint.
54 
55 #if !defined(FSRCO_FREQ)
56 // FSRCO frequency
57 #define FSRCO_FREQ    (20000000UL)
58 #endif
59 
60 #if !defined(HFXO_FREQ)
61 // HFXO frequency
62 #define HFXO_FREQ    (38400000UL)
63 #endif
64 
65 #if !defined(HFRCODPLL_STARTUP_FREQ)
66 // HFRCODPLL startup frequency
67 #define HFRCODPLL_STARTUP_FREQ    (19000000UL)
68 #endif
69 
70 #if !defined(HFRCODPLL_MAX_FREQ)
71 // Maximum HFRCODPLL frequency
72 #define HFRCODPLL_MAX_FREQ    (80000000UL)
73 #endif
74 
75 // CLKIN0 input
76 #if defined(SL_CLOCK_MANAGER_CLKIN0_FREQ)
77 // Clock Manager takes control of this define when present.
78 #define CLKIN0_FREQ    (SL_CLOCK_MANAGER_CLKIN0_FREQ)
79 #elif !defined(CLKIN0_FREQ)
80 #define CLKIN0_FREQ    (0UL)
81 #endif
82 
83 #if !defined(LFRCO_MAX_FREQ)
84 // LFRCO frequency, tuned to below frequency during manufacturing.
85 #define LFRCO_FREQ    (32768UL)
86 #endif
87 
88 #if !defined(ULFRCO_FREQ)
89 // ULFRCO frequency
90 #define ULFRCO_FREQ    (1000UL)
91 #endif
92 
93 #if !defined(LFXO_FREQ)
94 // LFXO frequency
95 #define LFXO_FREQ    (LFRCO_FREQ)
96 #endif
97 
98 /*******************************************************************************
99  **************************   LOCAL VARIABLES   ********************************
100  ******************************************************************************/
101 
102 #if (HFXO_FREQ > 0) && !defined(SYSTEM_NO_STATIC_MEMORY)
103 // NOTE: Gecko bootloaders can't have static variable allocation.
104 // System HFXO clock frequency
105 static uint32_t SystemHFXOClock = HFXO_FREQ;
106 #endif
107 
108 #if (LFXO_FREQ > 0) && !defined(SYSTEM_NO_STATIC_MEMORY)
109 // System LFXO clock frequency
110 static uint32_t SystemLFXOClock = LFXO_FREQ;
111 #endif
112 
113 #if !defined(SYSTEM_NO_STATIC_MEMORY)
114 // System HFRCODPLL clock frequency
115 static uint32_t SystemHFRCODPLLClock = HFRCODPLL_STARTUP_FREQ;
116 #endif
117 
118 /*******************************************************************************
119  **************************   GLOBAL VARIABLES   *******************************
120  ******************************************************************************/
121 
122 #if !defined(SYSTEM_NO_STATIC_MEMORY)
123 
124 /**
125  * @brief
126  *   System System Clock Frequency (Core Clock).
127  *
128  * @details
129  *   Required CMSIS global variable that must be kept up-to-date.
130  */
131 uint32_t SystemCoreClock = HFRCODPLL_STARTUP_FREQ;
132 
133 #endif
134 
135 /*---------------------------------------------------------------------------
136  * Exception / Interrupt Vector table
137  *---------------------------------------------------------------------------*/
138 extern const tVectorEntry __VECTOR_TABLE[16 + EXT_IRQ_COUNT];
139 
140 /*******************************************************************************
141  **************************   GLOBAL FUNCTIONS   *******************************
142  ******************************************************************************/
143 
144 /**************************************************************************//**
145  * @brief
146  *   Initialize the system.
147  *
148  * @details
149  *   Do required generic HW system init.
150  *
151  * @note
152  *   This function is invoked during system init, before the main() routine
153  *   and any data has been initialized. For this reason, it cannot do any
154  *   initialization of variables etc.
155  *****************************************************************************/
SystemInit(void)156 void SystemInit(void)
157 {
158 #if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U)
159   SCB->VTOR = (uint32_t) (&__VECTOR_TABLE[0]);
160 #endif
161 
162 #if defined(UNALIGNED_SUPPORT_DISABLE)
163   SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk;
164 #endif
165 
166 #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
167   SCB->CPACR |= ((3U << 10U * 2U)           /* set CP10 Full Access */
168                  | (3U << 11U * 2U));       /* set CP11 Full Access */
169 #endif
170 
171 /* Secure app takes care of moving between the security states.
172  * SL_TRUSTZONE_SECURE MACRO is for secure access.
173  * SL_TRUSTZONE_NONSECURE MACRO is for non-secure access.
174  * When both the MACROS are not defined, during start-up below code makes sure
175  * that all the peripherals are accessed from non-secure address except SMU,
176  * as SMU is used to configure the trustzone state of the system. */
177 #if !defined(SL_TRUSTZONE_SECURE) && !defined(SL_TRUSTZONE_NONSECURE) \
178   && defined(__TZ_PRESENT)
179 
180   // config SMU to Secure and other peripherals to Non-Secure.
181   SMU->PPUSATD0_CLR = _SMU_PPUSATD0_MASK;
182 #if defined (SEMAILBOX_PRESENT)
183   SMU->PPUSATD1_CLR = (_SMU_PPUSATD1_MASK & (~SMU_PPUSATD1_SMU & ~SMU_PPUSATD1_SEMAILBOX));
184 #else
185   SMU->PPUSATD1_CLR = (_SMU_PPUSATD1_MASK & ~SMU_PPUSATD1_SMU);
186 #endif
187 
188   // SAU treats all accesses as non-secure
189 #if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
190   SAU->CTRL = SAU_CTRL_ALLNS_Msk;
191   __DSB();
192   __ISB();
193 #else
194   #error "The startup code requires access to the CMSE toolchain extension to set proper SAU settings."
195 #endif // __ARM_FEATURE_CMSE
196 
197 // Clear and Enable the SMU PPUSEC and BMPUSEC interrupt.
198   NVIC_ClearPendingIRQ(SMU_SECURE_IRQn);
199   SMU->IF_CLR = SMU_IF_PPUSEC | SMU_IF_BMPUSEC;
200   NVIC_EnableIRQ(SMU_SECURE_IRQn);
201   SMU->IEN = SMU_IEN_PPUSEC | SMU_IEN_BMPUSEC;
202 #endif //SL_TRUSTZONE_SECURE
203 }
204 
205 /**************************************************************************//**
206  * @brief
207  *   Get current HFRCODPLL frequency.
208  *
209  * @note
210  *   This is a EFR32MG21 specific function, not part of the
211  *   CMSIS definition.
212  *
213  * @return
214  *   HFRCODPLL frequency in Hz.
215  *****************************************************************************/
SystemHFRCODPLLClockGet(void)216 uint32_t SystemHFRCODPLLClockGet(void)
217 {
218 #if !defined(SYSTEM_NO_STATIC_MEMORY)
219   return SystemHFRCODPLLClock;
220 #else
221   uint32_t ret = 0UL;
222   // Get oscillator frequency band
223   switch ((HFRCO0->CAL & _HFRCO_CAL_FREQRANGE_MASK)
224           >> _HFRCO_CAL_FREQRANGE_SHIFT) {
225     case 0:
226       switch (HFRCO0->CAL & _HFRCO_CAL_CLKDIV_MASK) {
227         case HFRCO_CAL_CLKDIV_DIV1:
228           ret = 4000000UL;
229           break;
230 
231         case HFRCO_CAL_CLKDIV_DIV2:
232           ret = 2000000UL;
233           break;
234 
235         case HFRCO_CAL_CLKDIV_DIV4:
236           ret = 1000000UL;
237           break;
238 
239         default:
240           ret = 0UL;
241           break;
242       }
243       break;
244 
245     case 3:
246       ret = 7000000UL;
247       break;
248 
249     case 6:
250       ret = 13000000UL;
251       break;
252 
253     case 7:
254       ret = 16000000UL;
255       break;
256 
257     case 8:
258       ret = 19000000UL;
259       break;
260 
261     case 10:
262       ret = 26000000UL;
263       break;
264 
265     case 11:
266       ret = 32000000UL;
267       break;
268 
269     case 12:
270       ret = 38000000UL;
271       break;
272 
273     case 13:
274       ret = 48000000UL;
275       break;
276 
277     case 14:
278       ret = 56000000UL;
279       break;
280 
281     case 15:
282       ret = 64000000UL;
283       break;
284 
285     case 16:
286       ret = 80000000UL;
287       break;
288 
289     default:
290       break;
291   }
292   return ret;
293 #endif
294 }
295 
296 /**************************************************************************//**
297  * @brief
298  *   Set HFRCODPLL frequency value.
299  *
300  * @note
301  *   This is a EFR32MG21 specific function, not part of the
302  *   CMSIS definition.
303  *
304  * @param[in] freq
305  *   HFRCODPLL frequency in Hz.
306  *****************************************************************************/
SystemHFRCODPLLClockSet(uint32_t freq)307 void SystemHFRCODPLLClockSet(uint32_t freq)
308 {
309 #if !defined(SYSTEM_NO_STATIC_MEMORY)
310   SystemHFRCODPLLClock = freq;
311 #else
312   (void) freq; // Unused parameter
313 #endif
314 }
315 
316 /***************************************************************************//**
317  * @brief
318  *   Get the current system clock frequency (SYSCLK).
319  *
320  * @details
321  *   Calculate and get the current core clock frequency based on the current
322  *   hardware configuration.
323  *
324  * @note
325  *   This is an EFR32MG21 specific function, not part of the
326  *   CMSIS definition.
327  *
328  * @return
329  *   Current system clock (SYSCLK) frequency in Hz.
330  ******************************************************************************/
SystemSYSCLKGet(void)331 uint32_t SystemSYSCLKGet(void)
332 {
333   uint32_t ret = 0U;
334 
335   // Find clock source
336   switch (CMU->SYSCLKCTRL & _CMU_SYSCLKCTRL_CLKSEL_MASK) {
337     case _CMU_SYSCLKCTRL_CLKSEL_HFRCODPLL:
338       ret = SystemHFRCODPLLClockGet();
339       break;
340 
341 #if (HFXO_FREQ > 0U)
342     case _CMU_SYSCLKCTRL_CLKSEL_HFXO:
343 #if defined(SYSTEM_NO_STATIC_MEMORY)
344       ret = HFXO_FREQ;
345 #else
346       ret = SystemHFXOClock;
347 #endif
348       break;
349 #endif
350 
351 #if (CLKIN0_FREQ > 0U)
352     case _CMU_SYSCLKCTRL_CLKSEL_CLKIN0:
353       ret = CLKIN0_FREQ;
354       break;
355 #endif
356 
357     case _CMU_SYSCLKCTRL_CLKSEL_FSRCO:
358       ret = FSRCO_FREQ;
359       break;
360 
361     default:
362       // Unknown clock source.
363       while (1) {
364       }
365   }
366   return ret;
367 }
368 
369 /***************************************************************************//**
370  * @brief
371  *   Get the current system core clock frequency (HCLK).
372  *
373  * @details
374  *   Calculate and get the current core clock frequency based on the current
375  *   configuration. Assuming that the SystemCoreClock global variable is
376  *   maintained, the core clock frequency is stored in that variable as well.
377  *   This function will however calculate the core clock based on actual HW
378  *   configuration. It will also update the SystemCoreClock global variable.
379  *
380  * @note
381  *   This is a EFR32MG21 specific function, not part of the
382  *   CMSIS definition.
383  *
384  * @return
385  *   The current core clock (HCLK) frequency in Hz.
386  ******************************************************************************/
SystemHCLKGet(void)387 uint32_t SystemHCLKGet(void)
388 {
389   uint32_t presc, ret;
390 
391   ret = SystemSYSCLKGet();
392 
393   presc = (CMU->SYSCLKCTRL & _CMU_SYSCLKCTRL_HCLKPRESC_MASK)
394           >> _CMU_SYSCLKCTRL_HCLKPRESC_SHIFT;
395 
396   ret /= presc + 1U;
397 
398 #if !defined(SYSTEM_NO_STATIC_MEMORY)
399   // Keep CMSIS system clock variable up-to-date
400   SystemCoreClock = ret;
401 #endif
402 
403   return ret;
404 }
405 
406 /***************************************************************************//**
407  * @brief
408  *   Get the maximum core clock frequency.
409  *
410  * @note
411  *   This is a EFR32MG21 specific function, not part of the
412  *   CMSIS definition.
413  *
414  * @return
415  *   The maximum core clock frequency in Hz.
416  ******************************************************************************/
SystemMaxCoreClockGet(void)417 uint32_t SystemMaxCoreClockGet(void)
418 {
419   return(HFRCODPLL_MAX_FREQ > HFXO_FREQ \
420          ? HFRCODPLL_MAX_FREQ : HFXO_FREQ);
421 }
422 
423 /**************************************************************************//**
424  * @brief
425  *   Get high frequency crystal oscillator clock frequency for target system.
426  *
427  * @note
428  *   This is a EFR32MG21 specific function, not part of the
429  *   CMSIS definition.
430  *
431  * @return
432  *   HFXO frequency in Hz. 0 if the external crystal oscillator is not present.
433  *****************************************************************************/
SystemHFXOClockGet(void)434 uint32_t SystemHFXOClockGet(void)
435 {
436   // The external crystal oscillator is not present if HFXO_FREQ==0
437 #if (HFXO_FREQ > 0U)
438 #if defined(SYSTEM_NO_STATIC_MEMORY)
439   return HFXO_FREQ;
440 #else
441   return SystemHFXOClock;
442 #endif
443 #else
444   return 0U;
445 #endif
446 }
447 
448 /**************************************************************************//**
449  * @brief
450  *   Set high frequency crystal oscillator clock frequency for target system.
451  *
452  * @note
453  *   This function is mainly provided for being able to handle target systems
454  *   with different HF crystal oscillator frequencies run-time. If used, it
455  *   should probably only be used once during system startup.
456  *
457  * @note
458  *   This is a EFR32MG21 specific function, not part of the
459  *   CMSIS definition.
460  *
461  * @param[in] freq
462  *   HFXO frequency in Hz used for target.
463  *****************************************************************************/
SystemHFXOClockSet(uint32_t freq)464 void SystemHFXOClockSet(uint32_t freq)
465 {
466   // External crystal oscillator present?
467 #if (HFXO_FREQ > 0) && !defined(SYSTEM_NO_STATIC_MEMORY)
468   SystemHFXOClock = freq;
469 
470   // Update core clock frequency if HFXO is used to clock core
471   if ((CMU->SYSCLKCTRL & _CMU_SYSCLKCTRL_CLKSEL_MASK)
472       == _CMU_SYSCLKCTRL_CLKSEL_HFXO) {
473     // This function will update the global variable
474     SystemHCLKGet();
475   }
476 #else
477   (void) freq; // Unused parameter
478 #endif
479 }
480 
481 /**************************************************************************//**
482  * @brief
483  *   Get current CLKIN0 frequency.
484  *
485  * @note
486  *   This is a EFR32MG21 specific function, not part of the
487  *   CMSIS definition.
488  *
489  * @return
490  *   CLKIN0 frequency in Hz.
491  *****************************************************************************/
SystemCLKIN0Get(void)492 uint32_t SystemCLKIN0Get(void)
493 {
494   return CLKIN0_FREQ;
495 }
496 
497 /**************************************************************************//**
498  * @brief
499  *   Get FSRCO frequency.
500  *
501  * @note
502  *   This is a EFR32MG21 specific function, not part of the
503  *   CMSIS definition.
504  *
505  * @return
506  *   FSRCO frequency in Hz.
507  *****************************************************************************/
SystemFSRCOClockGet(void)508 uint32_t SystemFSRCOClockGet(void)
509 {
510   return FSRCO_FREQ;
511 }
512 
513 /**************************************************************************//**
514  * @brief
515  *   Get current HFRCOEM23 frequency.
516  *
517  * @note
518  *   This is a EFR32MG21 specific function, not part of the
519  *   CMSIS definition.
520  *
521  * @return
522  *   HFRCOEM23 frequency in Hz.
523  *****************************************************************************/
SystemHFRCOEM23ClockGet(void)524 uint32_t SystemHFRCOEM23ClockGet(void)
525 {
526   uint32_t ret = 0UL;
527   // Get oscillator frequency band
528   switch ((HFRCOEM23->CAL & _HFRCO_CAL_FREQRANGE_MASK)
529           >> _HFRCO_CAL_FREQRANGE_SHIFT) {
530     case 0:
531       switch (HFRCOEM23->CAL & _HFRCO_CAL_CLKDIV_MASK) {
532         case HFRCO_CAL_CLKDIV_DIV1:
533           ret = 4000000UL;
534           break;
535 
536         case HFRCO_CAL_CLKDIV_DIV2:
537           ret = 2000000UL;
538           break;
539 
540         case HFRCO_CAL_CLKDIV_DIV4:
541           ret = 1000000UL;
542           break;
543 
544         default:
545           ret = 0UL;
546           break;
547       }
548       break;
549 
550     case 6:
551       ret = 13000000UL;
552       break;
553 
554     case 7:
555       ret = 16000000UL;
556       break;
557 
558     case 8:
559       ret = 19000000UL;
560       break;
561 
562     case 10:
563       ret = 26000000UL;
564       break;
565 
566     case 11:
567       ret = 32000000UL;
568       break;
569 
570     case 12:
571       ret = 40000000UL;
572       break;
573 
574     default:
575       break;
576   }
577   return ret;
578 }
579 
580 /**************************************************************************//**
581  * @brief
582  *   Get low frequency RC oscillator clock frequency for target system.
583  *
584  * @note
585  *   This is a EFR32MG21 specific function, not part of the
586  *   CMSIS definition.
587  *
588  * @return
589  *   LFRCO frequency in Hz.
590  *****************************************************************************/
SystemLFRCOClockGet(void)591 uint32_t SystemLFRCOClockGet(void)
592 {
593   return LFRCO_FREQ;
594 }
595 
596 /**************************************************************************//**
597  * @brief
598  *   Get ultra low frequency RC oscillator clock frequency for target system.
599  *
600  * @note
601  *   This is a EFR32MG21 specific function, not part of the
602  *   CMSIS definition.
603  *
604  * @return
605  *   ULFRCO frequency in Hz.
606  *****************************************************************************/
SystemULFRCOClockGet(void)607 uint32_t SystemULFRCOClockGet(void)
608 {
609   // The ULFRCO frequency is not tuned, and can be very inaccurate
610   return ULFRCO_FREQ;
611 }
612 
613 /**************************************************************************//**
614  * @brief
615  *   Get low frequency crystal oscillator clock frequency for target system.
616  *
617  * @note
618  *   This is a EFR32MG21 specific function, not part of the
619  *   CMSIS definition.
620  *
621  * @return
622  *   LFXO frequency in Hz.
623  *****************************************************************************/
SystemLFXOClockGet(void)624 uint32_t SystemLFXOClockGet(void)
625 {
626   // External crystal present?
627 #if (LFXO_FREQ > 0U)
628 #if defined(SYSTEM_NO_STATIC_MEMORY)
629   return LFXO_FREQ;
630 #else
631   return SystemLFXOClock;
632 #endif
633 #else
634   return 0U;
635 #endif
636 }
637 
638 /**************************************************************************//**
639  * @brief
640  *   Set low frequency crystal oscillator clock frequency for target system.
641  *
642  * @note
643  *   This function is mainly provided for being able to handle target systems
644  *   with different HF crystal oscillator frequencies run-time. If used, it
645  *   should probably only be used once during system startup.
646  *
647  * @note
648  *   This is a EFR32MG21 specific function, not part of the
649  *   CMSIS definition.
650  *
651  * @param[in] freq
652  *   LFXO frequency in Hz used for target.
653  *****************************************************************************/
SystemLFXOClockSet(uint32_t freq)654 void SystemLFXOClockSet(uint32_t freq)
655 {
656   // External crystal oscillator present?
657 #if (LFXO_FREQ > 0U) && !defined(SYSTEM_NO_STATIC_MEMORY)
658   SystemLFXOClock = freq;
659 #else
660   (void) freq; // Unused parameter
661 #endif
662 }
663