diff --git a/efm32/src/app.h b/efm32/src/app.h
index 2824af0..528e882 100644
--- a/efm32/src/app.h
+++ b/efm32/src/app.h
@@ -8,6 +8,8 @@
#ifndef SRC_APP_H_
#define SRC_APP_H_
+
+
#define PRINTING_USE_VCOM
#define USING_DEV_BOARD
diff --git a/efm32/src/crypto.c b/efm32/src/crypto.c
index 8bab5f0..3d1eb7e 100644
--- a/efm32/src/crypto.c
+++ b/efm32/src/crypto.c
@@ -311,13 +311,13 @@ void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_I
if ( uECC_sign(_signing_key, data, len, sig, curve) == 0)
{
- printf("error, uECC failed\n");
+ printf2(TAG_ERR,"error, uECC failed\n");
exit(1);
}
return;
fail:
- printf("error, invalid key length\n");
+ printf2(TAG_ERR,"error, invalid key length: %d\n", _key_len);
exit(1);
}
diff --git a/efm32/src/device.c b/efm32/src/device.c
index d1c0268..603b44b 100644
--- a/efm32/src/device.c
+++ b/efm32/src/device.c
@@ -303,11 +303,52 @@ int authenticator_is_backup_initialized()
uint8_t adc_rng(void);
+void bootloader_init(void)
+{
+ /* Chip errata */
+
+
+
+ // status LEDS
+ GPIO_PinModeSet(gpioPortF,
+ 4,
+ gpioModePushPull,
+ 0);
+
+ GPIO_PinModeSet(gpioPortF,
+ 5,
+ gpioModePushPull,
+ 1);
+
+ // EFM8 RDY/BUSY
+ GPIO_PinModeSet(RDY_PIN, gpioModeInput, 0);
+
+ // EFM8 MSG Available
+ GPIO_PinModeSet(MSG_AVAIL_PIN, gpioModeInput, 0);
+
+ // SPI R/w Indicator
+ GPIO_PinModeSet(RW_PIN, gpioModePushPull, 1);
+
+ // USB message rdy ext int
+ // GPIO_ExtIntConfig(gpioPortC, 9, 9, 1, 0,1);
+ // NVIC_EnableIRQ(GPIO_ODD_IRQn);
+
+
+ printing_init();
+
+
+ MSC_Init();
+
+
+
+}
+
void device_init(void)
{
/* Chip errata */
+
CHIP_Init();
enter_DefaultMode_from_RESET();
@@ -341,6 +382,7 @@ void device_init(void)
init_adc();
MSC_Init();
+
init_atomic_counter();
if (sizeof(AuthenticatorState) > PAGE_SIZE)
{
@@ -360,4 +402,6 @@ void device_init(void)
buf[i] = adc_rng();
}
dump_hex(buf,sizeof(buf));
+
+
}
diff --git a/efm32boot/.cproject b/efm32boot/.cproject
new file mode 100644
index 0000000..41587a3
--- /dev/null
+++ b/efm32boot/.cproject
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/efm32boot/.project b/efm32boot/.project
new file mode 100644
index 0000000..6753d30
--- /dev/null
+++ b/efm32boot/.project
@@ -0,0 +1,37 @@
+
+
+ efm32boot
+
+
+
+
+
+ org.eclipse.cdt.managedbuilder.core.genmakebuilder
+ clean,full,incremental,
+
+
+
+
+
+ org.eclipse.cdt.core.cnature
+ com.silabs.ss.framework.ide.project.sls.core.SLSProjectNature
+ org.eclipse.cdt.managedbuilder.core.managedBuildNature
+
+
+
+ crypto
+ 2
+ $%7BPARENT-1-PROJECT_LOC%7D/crypto
+
+
+ efm32
+ 2
+ $%7BPARENT-1-PROJECT_LOC%7D/efm32
+
+
+ fido2
+ 2
+ $%7BPARENT-1-PROJECT_LOC%7D/fido2
+
+
+
diff --git a/efm32boot/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs b/efm32boot/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs
new file mode 100644
index 0000000..b4554b4
--- /dev/null
+++ b/efm32boot/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs
@@ -0,0 +1,2 @@
+copiedFilesOriginState={}
+eclipse.preferences.version=1
diff --git a/efm32boot/CMSIS/EFM32PG1B/startup_gcc_efm32pg1b.s b/efm32boot/CMSIS/EFM32PG1B/startup_gcc_efm32pg1b.s
new file mode 100644
index 0000000..2a683c7
--- /dev/null
+++ b/efm32boot/CMSIS/EFM32PG1B/startup_gcc_efm32pg1b.s
@@ -0,0 +1,317 @@
+/* @file startup_efm32pg1b.S
+ * @brief startup file for Silicon Labs EFM32PG1B devices.
+ * For use with GCC for ARM Embedded Processors
+ * @version 5.2.2
+ * Date: 12 June 2014
+ *
+ */
+/* Copyright (c) 2011 - 2014 ARM LIMITED
+
+ All rights reserved.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ - Neither the name of ARM nor the names of its contributors may be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+ *
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ ---------------------------------------------------------------------------*/
+
+ .syntax unified
+ .arch armv7-m
+ .section .stack
+ .align 3
+#ifdef __STACK_SIZE
+ .equ Stack_Size, __STACK_SIZE
+#else
+ .equ Stack_Size, 0x00000400
+#endif
+ .globl __StackTop
+ .globl __StackLimit
+__StackLimit:
+ .space Stack_Size
+ .size __StackLimit, . - __StackLimit
+__StackTop:
+ .size __StackTop, . - __StackTop
+
+ .section .heap
+ .align 3
+#ifdef __HEAP_SIZE
+ .equ Heap_Size, __HEAP_SIZE
+#else
+ .equ Heap_Size, 0x00000C00
+#endif
+ .globl __HeapBase
+ .globl __HeapLimit
+__HeapBase:
+ .if Heap_Size
+ .space Heap_Size
+ .endif
+ .size __HeapBase, . - __HeapBase
+__HeapLimit:
+ .size __HeapLimit, . - __HeapLimit
+
+ .section .vectors
+ .align 2
+ .globl __Vectors
+__Vectors:
+ .long __StackTop /* Top of Stack */
+ .long Reset_Handler /* Reset Handler */
+ .long NMI_Handler /* NMI Handler */
+ .long HardFault_Handler /* Hard Fault Handler */
+ .long MemManage_Handler /* MPU Fault Handler */
+ .long BusFault_Handler /* Bus Fault Handler */
+ .long UsageFault_Handler /* Usage Fault Handler */
+ .long Default_Handler /* Reserved */
+ .long Default_Handler /* Reserved */
+ .long Default_Handler /* Reserved */
+ .long Default_Handler /* Reserved */
+ .long SVC_Handler /* SVCall Handler */
+ .long DebugMon_Handler /* Debug Monitor Handler */
+ .long Default_Handler /* Reserved */
+ .long PendSV_Handler /* PendSV Handler */
+ .long SysTick_Handler /* SysTick Handler */
+
+ /* External interrupts */
+ .long EMU_IRQHandler /* 0 - EMU */
+ .long Default_Handler /* 1 - Reserved */
+ .long WDOG0_IRQHandler /* 2 - WDOG0 */
+ .long Default_Handler /* 3 - Reserved */
+ .long Default_Handler /* 4 - Reserved */
+ .long Default_Handler /* 5 - Reserved */
+ .long Default_Handler /* 6 - Reserved */
+ .long Default_Handler /* 7 - Reserved */
+ .long LDMA_IRQHandler /* 8 - LDMA */
+ .long GPIO_EVEN_IRQHandler /* 9 - GPIO_EVEN */
+ .long TIMER0_IRQHandler /* 10 - TIMER0 */
+ .long USART0_RX_IRQHandler /* 11 - USART0_RX */
+ .long USART0_TX_IRQHandler /* 12 - USART0_TX */
+ .long ACMP0_IRQHandler /* 13 - ACMP0 */
+ .long ADC0_IRQHandler /* 14 - ADC0 */
+ .long IDAC0_IRQHandler /* 15 - IDAC0 */
+ .long I2C0_IRQHandler /* 16 - I2C0 */
+ .long GPIO_ODD_IRQHandler /* 17 - GPIO_ODD */
+ .long TIMER1_IRQHandler /* 18 - TIMER1 */
+ .long USART1_RX_IRQHandler /* 19 - USART1_RX */
+ .long USART1_TX_IRQHandler /* 20 - USART1_TX */
+ .long LEUART0_IRQHandler /* 21 - LEUART0 */
+ .long PCNT0_IRQHandler /* 22 - PCNT0 */
+ .long CMU_IRQHandler /* 23 - CMU */
+ .long MSC_IRQHandler /* 24 - MSC */
+ .long CRYPTO_IRQHandler /* 25 - CRYPTO */
+ .long LETIMER0_IRQHandler /* 26 - LETIMER0 */
+ .long Default_Handler /* 27 - Reserved */
+ .long Default_Handler /* 28 - Reserved */
+ .long RTCC_IRQHandler /* 29 - RTCC */
+ .long Default_Handler /* 30 - Reserved */
+ .long CRYOTIMER_IRQHandler /* 31 - CRYOTIMER */
+ .long Default_Handler /* 32 - Reserved */
+ .long FPUEH_IRQHandler /* 33 - FPUEH */
+
+
+ .size __Vectors, . - __Vectors
+
+ .text
+ .thumb
+ .thumb_func
+ .align 2
+ .globl Reset_Handler
+ .type Reset_Handler, %function
+Reset_Handler:
+#ifndef __NO_SYSTEM_INIT
+ ldr r0, =SystemInit
+ blx r0
+#endif
+
+/* Firstly it copies data from read only memory to RAM. There are two schemes
+ * to copy. One can copy more than one sections. Another can only copy
+ * one section. The former scheme needs more instructions and read-only
+ * data to implement than the latter.
+ * Macro __STARTUP_COPY_MULTIPLE is used to choose between two schemes. */
+
+#ifdef __STARTUP_COPY_MULTIPLE
+/* Multiple sections scheme.
+ *
+ * Between symbol address __copy_table_start__ and __copy_table_end__,
+ * there are array of triplets, each of which specify:
+ * offset 0: LMA of start of a section to copy from
+ * offset 4: VMA of start of a section to copy to
+ * offset 8: size of the section to copy. Must be multiply of 4
+ *
+ * All addresses must be aligned to 4 bytes boundary.
+ */
+ ldr r4, =__copy_table_start__
+ ldr r5, =__copy_table_end__
+
+.L_loop0:
+ cmp r4, r5
+ bge .L_loop0_done
+ ldr r1, [r4]
+ ldr r2, [r4, #4]
+ ldr r3, [r4, #8]
+
+.L_loop0_0:
+ subs r3, #4
+ ittt ge
+ ldrge r0, [r1, r3]
+ strge r0, [r2, r3]
+ bge .L_loop0_0
+
+ adds r4, #12
+ b .L_loop0
+
+.L_loop0_done:
+#else
+/* Single section scheme.
+ *
+ * The ranges of copy from/to are specified by following symbols
+ * __etext: LMA of start of the section to copy from. Usually end of text
+ * __data_start__: VMA of start of the section to copy to
+ * __data_end__: VMA of end of the section to copy to
+ *
+ * All addresses must be aligned to 4 bytes boundary.
+ */
+ ldr r1, =__etext
+ ldr r2, =__data_start__
+ ldr r3, =__data_end__
+
+.L_loop1:
+ cmp r2, r3
+ ittt lt
+ ldrlt r0, [r1], #4
+ strlt r0, [r2], #4
+ blt .L_loop1
+#endif /*__STARTUP_COPY_MULTIPLE */
+
+/* This part of work usually is done in C library startup code. Otherwise,
+ * define this macro to enable it in this startup.
+ *
+ * There are two schemes too. One can clear multiple BSS sections. Another
+ * can only clear one section. The former is more size expensive than the
+ * latter.
+ *
+ * Define macro __STARTUP_CLEAR_BSS_MULTIPLE to choose the former.
+ * Otherwise efine macro __STARTUP_CLEAR_BSS to choose the later.
+ */
+#ifdef __STARTUP_CLEAR_BSS_MULTIPLE
+/* Multiple sections scheme.
+ *
+ * Between symbol address __zero_table_start__ and __zero_table_end__,
+ * there are array of tuples specifying:
+ * offset 0: Start of a BSS section
+ * offset 4: Size of this BSS section. Must be multiply of 4
+ */
+ ldr r3, =__zero_table_start__
+ ldr r4, =__zero_table_end__
+
+.L_loop2:
+ cmp r3, r4
+ bge .L_loop2_done
+ ldr r1, [r3]
+ ldr r2, [r3, #4]
+ movs r0, 0
+
+.L_loop2_0:
+ subs r2, #4
+ itt ge
+ strge r0, [r1, r2]
+ bge .L_loop2_0
+ adds r3, #8
+ b .L_loop2
+.L_loop2_done:
+#elif defined (__STARTUP_CLEAR_BSS)
+/* Single BSS section scheme.
+ *
+ * The BSS section is specified by following symbols
+ * __bss_start__: start of the BSS section.
+ * __bss_end__: end of the BSS section.
+ *
+ * Both addresses must be aligned to 4 bytes boundary.
+ */
+ ldr r1, =__bss_start__
+ ldr r2, =__bss_end__
+
+ movs r0, 0
+.L_loop3:
+ cmp r1, r2
+ itt lt
+ strlt r0, [r1], #4
+ blt .L_loop3
+#endif /* __STARTUP_CLEAR_BSS_MULTIPLE || __STARTUP_CLEAR_BSS */
+
+#ifndef __START
+#define __START _start
+#endif
+ bl __START
+
+ .pool
+ .size Reset_Handler, . - Reset_Handler
+
+ .align 1
+ .thumb_func
+ .weak Default_Handler
+ .type Default_Handler, %function
+Default_Handler:
+ b .
+ .size Default_Handler, . - Default_Handler
+
+/* Macro to define default handlers. Default handler
+ * will be weak symbol and just dead loops. They can be
+ * overwritten by other handlers */
+ .macro def_irq_handler handler_name
+ .weak \handler_name
+ .set \handler_name, Default_Handler
+ .endm
+
+ def_irq_handler NMI_Handler
+ def_irq_handler HardFault_Handler
+ def_irq_handler MemManage_Handler
+ def_irq_handler BusFault_Handler
+ def_irq_handler UsageFault_Handler
+ def_irq_handler SVC_Handler
+ def_irq_handler DebugMon_Handler
+ def_irq_handler PendSV_Handler
+ def_irq_handler SysTick_Handler
+
+
+ def_irq_handler EMU_IRQHandler
+ def_irq_handler WDOG0_IRQHandler
+ def_irq_handler LDMA_IRQHandler
+ def_irq_handler GPIO_EVEN_IRQHandler
+ def_irq_handler TIMER0_IRQHandler
+ def_irq_handler USART0_RX_IRQHandler
+ def_irq_handler USART0_TX_IRQHandler
+ def_irq_handler ACMP0_IRQHandler
+ def_irq_handler ADC0_IRQHandler
+ def_irq_handler IDAC0_IRQHandler
+ def_irq_handler I2C0_IRQHandler
+ def_irq_handler GPIO_ODD_IRQHandler
+ def_irq_handler TIMER1_IRQHandler
+ def_irq_handler USART1_RX_IRQHandler
+ def_irq_handler USART1_TX_IRQHandler
+ def_irq_handler LEUART0_IRQHandler
+ def_irq_handler PCNT0_IRQHandler
+ def_irq_handler CMU_IRQHandler
+ def_irq_handler MSC_IRQHandler
+ def_irq_handler CRYPTO_IRQHandler
+ def_irq_handler LETIMER0_IRQHandler
+ def_irq_handler RTCC_IRQHandler
+ def_irq_handler CRYOTIMER_IRQHandler
+ def_irq_handler FPUEH_IRQHandler
+
+ .end
diff --git a/efm32boot/CMSIS/EFM32PG1B/system_efm32pg1b.c b/efm32boot/CMSIS/EFM32PG1B/system_efm32pg1b.c
new file mode 100644
index 0000000..c52e3e1
--- /dev/null
+++ b/efm32boot/CMSIS/EFM32PG1B/system_efm32pg1b.c
@@ -0,0 +1,389 @@
+/***************************************************************************//**
+ * @file system_efm32pg1b.c
+ * @brief CMSIS Cortex-M3/M4 System Layer for EFM32 devices.
+ * @version 5.2.2
+ ******************************************************************************
+ * # License
+ * Copyright 2017 Silicon Laboratories, Inc. http://www.silabs.com
+ ******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.@n
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.@n
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Laboratories, Inc.
+ * has no obligation to support this Software. Silicon Laboratories, Inc. is
+ * providing the Software "AS IS", with no express or implied warranties of any
+ * kind, including, but not limited to, any implied warranties of
+ * merchantability or fitness for any particular purpose or warranties against
+ * infringement of any proprietary rights of a third party.
+ *
+ * Silicon Laboratories, Inc. will not be liable for any consequential,
+ * incidental, or special damages, or any other relief, or for any claim by
+ * any third party, arising from your use of this Software.
+ *
+ *****************************************************************************/
+
+#include
+#include "em_device.h"
+
+/*******************************************************************************
+ ****************************** DEFINES ************************************
+ ******************************************************************************/
+
+/** LFRCO frequency, tuned to below frequency during manufacturing. */
+#define EFM32_LFRCO_FREQ (32768UL)
+/** ULFRCO frequency */
+#define EFM32_ULFRCO_FREQ (1000UL)
+
+/*******************************************************************************
+ ************************** LOCAL VARIABLES ********************************
+ ******************************************************************************/
+
+/* System oscillator frequencies. These frequencies are normally constant */
+/* for a target, but they are made configurable in order to allow run-time */
+/* handling of different boards. The crystal oscillator clocks can be set */
+/* compile time to a non-default value by defining respective EFM_nFXO_FREQ */
+/* values according to board design. By defining the EFM_nFXO_FREQ to 0, */
+/* one indicates that the oscillator is not present, in order to save some */
+/* SW footprint. */
+
+#ifndef EFM32_HFRCO_MAX_FREQ
+/** Maximum HFRCO frequency */
+#define EFM32_HFRCO_MAX_FREQ (38000000UL)
+#endif
+
+#ifndef EFM32_HFXO_FREQ
+/** HFXO frequency */
+#define EFM32_HFXO_FREQ (40000000UL)
+#endif
+
+#ifndef EFM32_HFRCO_STARTUP_FREQ
+/** HFRCO startup frequency */
+#define EFM32_HFRCO_STARTUP_FREQ (19000000UL)
+#endif
+
+
+/* Do not define variable if HF crystal oscillator not present */
+#if (EFM32_HFXO_FREQ > 0UL)
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+/** System HFXO clock. */
+static uint32_t SystemHFXOClock = EFM32_HFXO_FREQ;
+/** @endcond (DO_NOT_INCLUDE_WITH_DOXYGEN) */
+#endif
+
+#ifndef EFM32_LFXO_FREQ
+/** LFXO frequency */
+#define EFM32_LFXO_FREQ (EFM32_LFRCO_FREQ)
+#endif
+/* Do not define variable if LF crystal oscillator not present */
+#if (EFM32_LFXO_FREQ > 0UL)
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+/** System LFXO clock. */
+static uint32_t SystemLFXOClock = 32768UL;
+/** @endcond (DO_NOT_INCLUDE_WITH_DOXYGEN) */
+#endif
+
+
+/*******************************************************************************
+ ************************** GLOBAL VARIABLES *******************************
+ ******************************************************************************/
+
+/**
+ * @brief
+ * System System Clock Frequency (Core Clock).
+ *
+ * @details
+ * Required CMSIS global variable that must be kept up-to-date.
+ */
+uint32_t SystemCoreClock = EFM32_HFRCO_STARTUP_FREQ;
+
+
+/**
+ * @brief
+ * System HFRCO frequency
+ *
+ * @note
+ * This is an EFM32 proprietary variable, not part of the CMSIS definition.
+ *
+ * @details
+ * Frequency of the system HFRCO oscillator
+ */
+uint32_t SystemHfrcoFreq = EFM32_HFRCO_STARTUP_FREQ;
+
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Get the current core clock frequency.
+ *
+ * @details
+ * Calculate and get the current core clock frequency based on the current
+ * configuration. Assuming that the SystemCoreClock global variable is
+ * maintained, the core clock frequency is stored in that variable as well.
+ * This function will however calculate the core clock based on actual HW
+ * configuration. It will also update the SystemCoreClock global variable.
+ *
+ * @note
+ * This is an EFM32 proprietary function, not part of the CMSIS definition.
+ *
+ * @return
+ * The current core clock frequency in Hz.
+ ******************************************************************************/
+uint32_t SystemCoreClockGet(void)
+{
+ uint32_t ret;
+ uint32_t presc;
+
+ ret = SystemHFClockGet();
+ presc = (CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK) >>
+ _CMU_HFCOREPRESC_PRESC_SHIFT;
+ ret /= (presc + 1);
+
+ /* Keep CMSIS system clock variable up-to-date */
+ SystemCoreClock = ret;
+
+ return ret;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Get the maximum core clock frequency.
+ *
+ * @note
+ * This is an EFM32 proprietary function, not part of the CMSIS definition.
+ *
+ * @return
+ * The maximum core clock frequency in Hz.
+ ******************************************************************************/
+uint32_t SystemMaxCoreClockGet(void)
+{
+ return (EFM32_HFRCO_MAX_FREQ > EFM32_HFXO_FREQ ? \
+ EFM32_HFRCO_MAX_FREQ : EFM32_HFXO_FREQ);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ * Get the current HFCLK frequency.
+ *
+ * @note
+ * This is an EFM32 proprietary function, not part of the CMSIS definition.
+ *
+ * @return
+ * The current HFCLK frequency in Hz.
+ ******************************************************************************/
+uint32_t SystemHFClockGet(void)
+{
+ uint32_t ret;
+
+ switch (CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK)
+ {
+ case CMU_HFCLKSTATUS_SELECTED_LFXO:
+#if (EFM32_LFXO_FREQ > 0)
+ ret = SystemLFXOClock;
+#else
+ /* We should not get here, since core should not be clocked. May */
+ /* be caused by a misconfiguration though. */
+ ret = 0;
+#endif
+ break;
+
+ case CMU_HFCLKSTATUS_SELECTED_LFRCO:
+ ret = EFM32_LFRCO_FREQ;
+ break;
+
+ case CMU_HFCLKSTATUS_SELECTED_HFXO:
+#if (EFM32_HFXO_FREQ > 0)
+ ret = SystemHFXOClock;
+#else
+ /* We should not get here, since core should not be clocked. May */
+ /* be caused by a misconfiguration though. */
+ ret = 0;
+#endif
+ break;
+
+ default: /* CMU_HFCLKSTATUS_SELECTED_HFRCO */
+ ret = SystemHfrcoFreq;
+ break;
+ }
+
+ return ret / (1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
+ >> _CMU_HFPRESC_PRESC_SHIFT));
+}
+
+
+/**************************************************************************//**
+ * @brief
+ * Get high frequency crystal oscillator clock frequency for target system.
+ *
+ * @note
+ * This is an EFM32 proprietary function, not part of the CMSIS definition.
+ *
+ * @return
+ * HFXO frequency in Hz.
+ *****************************************************************************/
+uint32_t SystemHFXOClockGet(void)
+{
+ /* External crystal oscillator present? */
+#if (EFM32_HFXO_FREQ > 0)
+ return SystemHFXOClock;
+#else
+ return 0;
+#endif
+}
+
+
+/**************************************************************************//**
+ * @brief
+ * Set high frequency crystal oscillator clock frequency for target system.
+ *
+ * @note
+ * This function is mainly provided for being able to handle target systems
+ * with different HF crystal oscillator frequencies run-time. If used, it
+ * should probably only be used once during system startup.
+ *
+ * @note
+ * This is an EFM32 proprietary function, not part of the CMSIS definition.
+ *
+ * @param[in] freq
+ * HFXO frequency in Hz used for target.
+ *****************************************************************************/
+void SystemHFXOClockSet(uint32_t freq)
+{
+ /* External crystal oscillator present? */
+#if (EFM32_HFXO_FREQ > 0)
+ SystemHFXOClock = freq;
+
+ /* Update core clock frequency if HFXO is used to clock core */
+ if ((CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK) == CMU_HFCLKSTATUS_SELECTED_HFXO)
+ {
+ /* The function will update the global variable */
+ SystemCoreClockGet();
+ }
+#else
+ (void)freq; /* Unused parameter */
+#endif
+}
+
+
+/**************************************************************************//**
+ * @brief
+ * Initialize the system.
+ *
+ * @details
+ * Do required generic HW system init.
+ *
+ * @note
+ * This function is invoked during system init, before the main() routine
+ * and any data has been initialized. For this reason, it cannot do any
+ * initialization of variables etc.
+ *****************************************************************************/
+void SystemInit(void)
+{
+#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
+ /* Set floating point coprosessor access mode. */
+ SCB->CPACR |= ((3UL << 10*2) | /* set CP10 Full Access */
+ (3UL << 11*2) ); /* set CP11 Full Access */
+#endif
+}
+
+
+/**************************************************************************//**
+ * @brief
+ * Get low frequency RC oscillator clock frequency for target system.
+ *
+ * @note
+ * This is an EFM32 proprietary function, not part of the CMSIS definition.
+ *
+ * @return
+ * LFRCO frequency in Hz.
+ *****************************************************************************/
+uint32_t SystemLFRCOClockGet(void)
+{
+ /* Currently we assume that this frequency is properly tuned during */
+ /* manufacturing and is not changed after reset. If future requirements */
+ /* for re-tuning by user, we can add support for that. */
+ return EFM32_LFRCO_FREQ;
+}
+
+
+/**************************************************************************//**
+ * @brief
+ * Get ultra low frequency RC oscillator clock frequency for target system.
+ *
+ * @note
+ * This is an EFM32 proprietary function, not part of the CMSIS definition.
+ *
+ * @return
+ * ULFRCO frequency in Hz.
+ *****************************************************************************/
+uint32_t SystemULFRCOClockGet(void)
+{
+ /* The ULFRCO frequency is not tuned, and can be very inaccurate */
+ return EFM32_ULFRCO_FREQ;
+}
+
+
+/**************************************************************************//**
+ * @brief
+ * Get low frequency crystal oscillator clock frequency for target system.
+ *
+ * @note
+ * This is an EFM32 proprietary function, not part of the CMSIS definition.
+ *
+ * @return
+ * LFXO frequency in Hz.
+ *****************************************************************************/
+uint32_t SystemLFXOClockGet(void)
+{
+ /* External crystal oscillator present? */
+#if (EFM32_LFXO_FREQ > 0)
+ return SystemLFXOClock;
+#else
+ return 0;
+#endif
+}
+
+
+/**************************************************************************//**
+ * @brief
+ * Set low frequency crystal oscillator clock frequency for target system.
+ *
+ * @note
+ * This function is mainly provided for being able to handle target systems
+ * with different HF crystal oscillator frequencies run-time. If used, it
+ * should probably only be used once during system startup.
+ *
+ * @note
+ * This is an EFM32 proprietary function, not part of the CMSIS definition.
+ *
+ * @param[in] freq
+ * LFXO frequency in Hz used for target.
+ *****************************************************************************/
+void SystemLFXOClockSet(uint32_t freq)
+{
+ /* External crystal oscillator present? */
+#if (EFM32_LFXO_FREQ > 0)
+ SystemLFXOClock = freq;
+
+ /* Update core clock frequency if LFXO is used to clock core */
+ if ((CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK) == CMU_HFCLKSTATUS_SELECTED_LFXO)
+ {
+ /* The function will update the global variable */
+ SystemCoreClockGet();
+ }
+#else
+ (void)freq; /* Unused parameter */
+#endif
+}
diff --git a/efm32boot/efm32boot.hwconf b/efm32boot/efm32boot.hwconf
new file mode 100644
index 0000000..ec25abf
--- /dev/null
+++ b/efm32boot/efm32boot.hwconf
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/efm32boot/emlib/em_assert.c b/efm32boot/emlib/em_assert.c
new file mode 100644
index 0000000..71225e0
--- /dev/null
+++ b/efm32boot/emlib/em_assert.c
@@ -0,0 +1,81 @@
+/***************************************************************************//**
+ * @file em_assert.c
+ * @brief Assert API
+ * @version 5.2.2
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+#include "em_assert.h"
+#include
+
+/***************************************************************************//**
+ * @addtogroup emlib
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup ASSERT
+ * @{
+ ******************************************************************************/
+
+#if defined(DEBUG_EFM)
+/***************************************************************************//**
+ * @brief
+ * EFM internal assert handling.
+ *
+ * This function is invoked through EFM_ASSERT() macro usage only, it should
+ * not be used explicitly.
+ *
+ * This implementation simply enters an indefinite loop, allowing
+ * the use of a debugger to determine cause of failure. By defining
+ * DEBUG_EFM_USER to the preprocessor for all files, a user defined version
+ * of this function must be defined and will be invoked instead, possibly
+ * providing output of assertion location.
+ *
+ * @note
+ * This function is not used unless @ref DEBUG_EFM is defined
+ * during preprocessing of EFM_ASSERT() usage.
+ *
+ * @param[in] file
+ * Name of source file where assertion failed.
+ *
+ * @param[in] line
+ * Line number in source file where assertion failed.
+ ******************************************************************************/
+void assertEFM(const char *file, int line)
+{
+ (void)file; /* Unused parameter */
+ (void)line; /* Unused parameter */
+
+ while (true) {
+ }
+}
+#endif /* DEBUG_EFM */
+
+/** @} (end addtogroup ASSERT) */
+/** @} (end addtogroup emlib) */
diff --git a/efm32boot/emlib/em_cmu.c b/efm32boot/emlib/em_cmu.c
new file mode 100644
index 0000000..96316ff
--- /dev/null
+++ b/efm32boot/emlib/em_cmu.c
@@ -0,0 +1,5310 @@
+/***************************************************************************//**
+ * @file em_cmu.c
+ * @brief Clock management unit (CMU) Peripheral API
+ * @version 5.2.2
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+#include "em_cmu.h"
+#if defined(CMU_PRESENT)
+
+#include
+#include
+#include "em_assert.h"
+#include "em_bus.h"
+#include "em_emu.h"
+#include "em_cmu.h"
+#include "em_system.h"
+#include "em_common.h"
+
+/***************************************************************************//**
+ * @addtogroup emlib
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup CMU
+ * @brief Clock management unit (CMU) Peripheral API
+ * @details
+ * This module contains functions to control the CMU peripheral of Silicon
+ * Labs 32-bit MCUs and SoCs. The CMU controls oscillators and clocks.
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ****************************** DEFINES ************************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+#if defined(_SILICON_LABS_32B_SERIES_1)
+/** Maximum allowed core frequency when using 0 wait-states on flash access. */
+#define CMU_MAX_FREQ_0WS 26000000
+/** Maximum allowed core frequency when using 1 wait-states on flash access */
+#define CMU_MAX_FREQ_1WS 40000000
+/** Maximum allowed core frequency when using 2 wait-states on flash access */
+#define CMU_MAX_FREQ_2WS 54000000
+/** Maximum allowed core frequency when using 3 wait-states on flash access */
+#define CMU_MAX_FREQ_3WS 72000000
+#elif defined(_SILICON_LABS_32B_SERIES_0)
+/** Maximum allowed core frequency when using 0 wait-states on flash access. */
+#define CMU_MAX_FREQ_0WS 16000000
+/** Maximum allowed core frequency when using 1 wait-states on flash access */
+#define CMU_MAX_FREQ_1WS 32000000
+#else
+#error "Max Flash wait-state frequencies are not defined for this platform."
+#endif
+
+/** Maximum frequency for HFLE interface */
+#if defined(CMU_CTRL_HFLE)
+/** Maximum HFLE frequency for series 0 EFM32 and EZR32 Wonder Gecko. */
+#if defined(_SILICON_LABS_32B_SERIES_0) \
+ && (defined(_EFM32_WONDER_FAMILY) \
+ || defined(_EZR32_WONDER_FAMILY))
+#define CMU_MAX_FREQ_HFLE 24000000
+/** Maximum HFLE frequency for other series 0 parts with maximum core clock
+ higher than 32MHz. */
+#elif defined(_SILICON_LABS_32B_SERIES_0) \
+ && (defined(_EFM32_GIANT_FAMILY) \
+ || defined(_EZR32_LEOPARD_FAMILY))
+#define CMU_MAX_FREQ_HFLE maxFreqHfle()
+#endif
+#elif defined(CMU_CTRL_WSHFLE)
+/** Maximum HFLE frequency for series 1 parts */
+#define CMU_MAX_FREQ_HFLE 32000000
+#endif
+
+#if defined(CMU_STATUS_HFXOSHUNTOPTRDY)
+#define HFXO_TUNING_READY_FLAGS (CMU_STATUS_HFXOPEAKDETRDY | CMU_STATUS_HFXOSHUNTOPTRDY)
+#define HFXO_TUNING_MODE_AUTO (_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_AUTOCMD)
+#define HFXO_TUNING_MODE_CMD (_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_CMD)
+#elif defined(CMU_STATUS_HFXOPEAKDETRDY)
+#define HFXO_TUNING_READY_FLAGS (CMU_STATUS_HFXOPEAKDETRDY)
+#define HFXO_TUNING_MODE_AUTO (_CMU_HFXOCTRL_PEAKDETMODE_AUTOCMD)
+#define HFXO_TUNING_MODE_CMD (_CMU_HFXOCTRL_PEAKDETMODE_CMD)
+#endif
+
+#if defined(CMU_HFXOCTRL_MODE_EXTCLK)
+/** HFXO external clock mode is renamed from EXTCLK to DIGEXTCLK. */
+#define CMU_HFXOCTRL_MODE_DIGEXTCLK CMU_HFXOCTRL_MODE_EXTCLK
+#endif
+
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+#define VSCALE_DEFAULT (EMU_VScaleGet())
+#else
+#define VSCALE_DEFAULT 0
+#endif
+
+/*******************************************************************************
+ ************************** LOCAL VARIABLES ********************************
+ ******************************************************************************/
+
+#if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
+static CMU_AUXHFRCOFreq_TypeDef auxHfrcoFreq = cmuAUXHFRCOFreq_19M0Hz;
+#endif
+#if defined(_CMU_STATUS_HFXOSHUNTOPTRDY_MASK)
+#define HFXO_INVALID_TRIM (~_CMU_HFXOTRIMSTATUS_MASK)
+#endif
+
+#if defined(CMU_OSCENCMD_DPLLEN)
+/** Table of HFRCOCTRL values and their associated min/max frequencies and
+ optional band enumerator. */
+static const struct hfrcoCtrlTableElement{
+ uint32_t minFreq;
+ uint32_t maxFreq;
+ uint32_t value;
+ CMU_HFRCOFreq_TypeDef band;
+} hfrcoCtrlTable[] =
+{
+ // minFreq maxFreq HFRCOCTRL value band
+ { 860000, 1050000, 0xBC601F00, cmuHFRCOFreq_1M0Hz },
+ { 1050000, 1280000, 0xBC611F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 1280000, 1480000, 0xBCA21F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 1480000, 1800000, 0xAD231F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 1800000, 2110000, 0xBA601F00, cmuHFRCOFreq_2M0Hz },
+ { 2110000, 2560000, 0xBA611F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 2560000, 2970000, 0xBAA21F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 2970000, 3600000, 0xAB231F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 3600000, 4220000, 0xB8601F00, cmuHFRCOFreq_4M0Hz },
+ { 4220000, 5120000, 0xB8611F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 5120000, 5930000, 0xB8A21F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 5930000, 7520000, 0xA9231F00, cmuHFRCOFreq_7M0Hz },
+ { 7520000, 9520000, 0x99241F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 9520000, 11800000, 0x99251F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 11800000, 14400000, 0x99261F00, cmuHFRCOFreq_13M0Hz },
+ { 14400000, 17200000, 0x99271F00, cmuHFRCOFreq_16M0Hz },
+ { 17200000, 19700000, 0x99481F00, cmuHFRCOFreq_19M0Hz },
+ { 19700000, 23800000, 0x99491F35, (CMU_HFRCOFreq_TypeDef)0 },
+ { 23800000, 28700000, 0x994A1F00, cmuHFRCOFreq_26M0Hz },
+ { 28700000, 34800000, 0x996B1F00, cmuHFRCOFreq_32M0Hz },
+#if defined(_DEVINFO_HFRCOCAL16_MASK)
+ { 34800000, 42800000, 0x996C1F00, cmuHFRCOFreq_38M0Hz },
+ { 42800000, 51600000, 0x996D1F00, cmuHFRCOFreq_48M0Hz },
+ { 51600000, 60500000, 0x998E1F00, cmuHFRCOFreq_56M0Hz },
+ { 60500000, 72000000, 0xA98F1F00, cmuHFRCOFreq_64M0Hz }
+#else
+ { 34800000, 40000000, 0x996C1F00, cmuHFRCOFreq_38M0Hz }
+#endif
+};
+
+#define HFRCOCTRLTABLE_ENTRIES (sizeof(hfrcoCtrlTable) \
+ / sizeof(struct hfrcoCtrlTableElement))
+#endif // CMU_OSCENCMD_DPLLEN
+
+#if defined(_SILICON_LABS_32B_SERIES_1) && defined(_EMU_STATUS_VSCALE_MASK)
+/* Devices with Voltage Scaling needs extra handling of wait states. */
+static const struct flashWsTableElement{
+ uint32_t maxFreq;
+ uint8_t vscale;
+ uint8_t ws;
+} flashWsTable[] =
+{
+#if (_SILICON_LABS_GECKO_INTERNAL_SDID == 100)
+ { 18000000, 0, 0 }, /* 0 wait states at max frequency 18 MHz and 1.2V */
+ { 36000000, 0, 1 }, /* 1 wait states at max frequency 36 MHz and 1.2V */
+ { 54000000, 0, 2 }, /* 2 wait states at max frequency 54 MHz and 1.2V */
+ { 72000000, 0, 3 }, /* 3 wait states at max frequency 72 MHz and 1.2V */
+ { 7000000, 2, 0 }, /* 0 wait states at max frequency 7 MHz and 1.0V */
+ { 14000000, 2, 1 }, /* 1 wait states at max frequency 14 MHz and 1.0V */
+ { 21000000, 2, 2 }, /* 2 wait states at max frequency 21 MHz and 1.0V */
+#else
+ { 25000000, 0, 0 }, /* 0 wait states at max frequency 25 MHz and 1.2V */
+ { 40000000, 0, 1 }, /* 1 wait states at max frequency 40 MHz and 1.2V */
+ { 7000000, 2, 0 }, /* 0 wait states at max frequency 7 MHz and 1.0V */
+ { 14000000, 2, 1 }, /* 1 wait states at max frequency 14 MHz and 1.0V */
+ { 21000000, 2, 2 }, /* 2 wait states at max frequency 21 MHz and 1.0V */
+#endif
+};
+
+#define FLASH_WS_TABLE_ENTRIES (sizeof(flashWsTable) / sizeof(flashWsTable[0]))
+#endif
+
+#if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK) \
+ || defined(_CMU_USHFRCOTUNE_MASK)
+#ifndef EFM32_USHFRCO_STARTUP_FREQ
+#define EFM32_USHFRCO_STARTUP_FREQ (48000000UL)
+#endif
+
+static uint32_t ushfrcoFreq = EFM32_USHFRCO_STARTUP_FREQ;
+#endif
+
+/*******************************************************************************
+ ************************** LOCAL PROTOTYPES *******************************
+ ******************************************************************************/
+#if defined(_CMU_HFRCOCTRL_FREQRANGE_MASK)
+static uint32_t CMU_HFRCODevinfoGet(CMU_HFRCOFreq_TypeDef freq);
+#endif
+
+#if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK)
+static uint32_t CMU_USHFRCODevinfoGet(CMU_USHFRCOFreq_TypeDef freq);
+#endif
+
+/** @endcond */
+
+/*******************************************************************************
+ ************************** LOCAL FUNCTIONS ********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+#if defined(_SILICON_LABS_32B_SERIES_0) \
+ && (defined(_EFM32_GIANT_FAMILY) \
+ || defined(_EZR32_LEOPARD_FAMILY))
+/***************************************************************************//**
+ * @brief
+ * Return max allowed frequency for low energy peripherals.
+ ******************************************************************************/
+static uint32_t maxFreqHfle(void)
+{
+ uint16_t majorMinorRev;
+
+ switch (SYSTEM_GetFamily()) {
+ case systemPartFamilyEfm32Leopard:
+ case systemPartFamilyEzr32Leopard:
+ /* CHIP MAJOR bit [5:0] */
+ majorMinorRev = (((ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK)
+ >> _ROMTABLE_PID0_REVMAJOR_SHIFT) << 8);
+ /* CHIP MINOR bit [7:4] */
+ majorMinorRev |= (((ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK)
+ >> _ROMTABLE_PID2_REVMINORMSB_SHIFT) << 4);
+ /* CHIP MINOR bit [3:0] */
+ majorMinorRev |= ((ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK)
+ >> _ROMTABLE_PID3_REVMINORLSB_SHIFT);
+
+ if (majorMinorRev >= 0x0204) {
+ return 24000000;
+ } else {
+ return 32000000;
+ }
+
+ case systemPartFamilyEfm32Giant:
+ return 32000000;
+
+ default:
+ /* Invalid device family. */
+ EFM_ASSERT(false);
+ return 0;
+ }
+}
+#endif
+
+#if defined(CMU_MAX_FREQ_HFLE)
+
+/* Unified definitions for HFLE wait-state and prescaler fields. */
+#if defined(CMU_CTRL_HFLE)
+#define _GENERIC_HFLE_WS_MASK _CMU_CTRL_HFLE_MASK
+#define _GENERIC_HFLE_WS_SHIFT _CMU_CTRL_HFLE_SHIFT
+#define GENERIC_HFLE_PRESC_REG CMU->HFCORECLKDIV
+#define _GENERIC_HFLE_PRESC_MASK _CMU_HFCORECLKDIV_HFCORECLKLEDIV_MASK
+#define _GENERIC_HFLE_PRESC_SHIFT _CMU_HFCORECLKDIV_HFCORECLKLEDIV_SHIFT
+#elif defined(CMU_CTRL_WSHFLE)
+#define _GENERIC_HFLE_WS_MASK _CMU_CTRL_WSHFLE_MASK
+#define _GENERIC_HFLE_WS_SHIFT _CMU_CTRL_WSHFLE_SHIFT
+#define GENERIC_HFLE_PRESC_REG CMU->HFPRESC
+#define _GENERIC_HFLE_PRESC_MASK _CMU_HFPRESC_HFCLKLEPRESC_MASK
+#define _GENERIC_HFLE_PRESC_SHIFT _CMU_HFPRESC_HFCLKLEPRESC_SHIFT
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Set HFLE wait-states and HFCLKLE prescaler.
+ *
+ * @param[in] maxLeFreq
+ * Max LE frequency
+ ******************************************************************************/
+static void setHfLeConfig(uint32_t hfFreq)
+{
+ unsigned int hfleWs;
+ uint32_t hflePresc;
+
+ /* Check for 1 bit fields. BUS_RegBitWrite() below are going to fail if the
+ fields are changed to more than 1 bit. */
+ EFM_ASSERT((_GENERIC_HFLE_WS_MASK >> _GENERIC_HFLE_WS_SHIFT) == 0x1);
+
+ /* - Enable HFLE wait-state if to allow access to LE peripherals when HFBUSCLK is
+ above maxLeFreq.
+ - Set HFLE prescaler. Allowed HFLE clock frequency is maxLeFreq. */
+
+ hfleWs = 1;
+ if (hfFreq <= CMU_MAX_FREQ_HFLE) {
+ hfleWs = 0;
+ hflePresc = 0;
+ } else if (hfFreq <= (2 * CMU_MAX_FREQ_HFLE)) {
+ hflePresc = 1;
+ } else {
+ hflePresc = 2;
+ }
+ BUS_RegBitWrite(&CMU->CTRL, _GENERIC_HFLE_WS_SHIFT, hfleWs);
+ GENERIC_HFLE_PRESC_REG = (GENERIC_HFLE_PRESC_REG & ~_GENERIC_HFLE_PRESC_MASK)
+ | (hflePresc << _GENERIC_HFLE_PRESC_SHIFT);
+}
+
+#if defined(_CMU_CTRL_HFLE_MASK)
+/***************************************************************************//**
+ * @brief
+ * Get HFLE wait-state configuration.
+ *
+ * @return
+ * Current wait-state configuration.
+ ******************************************************************************/
+static uint32_t getHfLeConfig(void)
+{
+ uint32_t ws = BUS_RegBitRead(&CMU->CTRL, _GENERIC_HFLE_WS_SHIFT);
+ return ws;
+}
+#endif
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Get the AUX clock frequency. Used by MSC flash programming and LESENSE,
+ * by default also as debug clock.
+ *
+ * @return
+ * AUX Frequency in Hz
+ ******************************************************************************/
+static uint32_t auxClkGet(void)
+{
+ uint32_t ret;
+
+#if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
+ ret = auxHfrcoFreq;
+
+#elif defined(_CMU_AUXHFRCOCTRL_BAND_MASK)
+ /* All series 0 families except EFM32G */
+ switch (CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_BAND_MASK) {
+ case CMU_AUXHFRCOCTRL_BAND_1MHZ:
+ if ( SYSTEM_GetProdRev() >= 19 ) {
+ ret = 1200000;
+ } else {
+ ret = 1000000;
+ }
+ break;
+
+ case CMU_AUXHFRCOCTRL_BAND_7MHZ:
+ if ( SYSTEM_GetProdRev() >= 19 ) {
+ ret = 6600000;
+ } else {
+ ret = 7000000;
+ }
+ break;
+
+ case CMU_AUXHFRCOCTRL_BAND_11MHZ:
+ ret = 11000000;
+ break;
+
+ case CMU_AUXHFRCOCTRL_BAND_14MHZ:
+ ret = 14000000;
+ break;
+
+ case CMU_AUXHFRCOCTRL_BAND_21MHZ:
+ ret = 21000000;
+ break;
+
+#if defined(_CMU_AUXHFRCOCTRL_BAND_28MHZ)
+ case CMU_AUXHFRCOCTRL_BAND_28MHZ:
+ ret = 28000000;
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0;
+ break;
+ }
+
+#else
+ /* Gecko has a fixed 14Mhz AUXHFRCO clock */
+ ret = 14000000;
+
+#endif
+
+ return ret;
+}
+
+#if defined (_CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK) \
+ || defined (_CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK)
+/***************************************************************************//**
+ * @brief
+ * Get the HFSRCCLK frequency.
+ *
+ * @return
+ * HFSRCCLK Frequency in Hz
+ ******************************************************************************/
+static uint32_t hfSrcClkGet(void)
+{
+ uint32_t ret;
+
+ ret = SystemHFClockGet();
+ return ret * (1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
+ >> _CMU_HFPRESC_PRESC_SHIFT));
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Get the Debug Trace clock frequency
+ *
+ * @return
+ * Debug Trace frequency in Hz
+ ******************************************************************************/
+static uint32_t dbgClkGet(void)
+{
+ uint32_t ret;
+ CMU_Select_TypeDef clk;
+
+ /* Get selected clock source */
+ clk = CMU_ClockSelectGet(cmuClock_DBG);
+
+ switch (clk) {
+ case cmuSelect_HFCLK:
+ ret = SystemHFClockGet();
+ break;
+
+ case cmuSelect_AUXHFRCO:
+ ret = auxClkGet();
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+#if defined(_CMU_ADCCTRL_MASK)
+/***************************************************************************//**
+ * @brief
+ * Get the ADC n asynchronous clock frequency
+ *
+ * @return
+ * ADC n asynchronous frequency in Hz
+ ******************************************************************************/
+static uint32_t adcAsyncClkGet(uint32_t adc)
+{
+ uint32_t ret;
+ CMU_Select_TypeDef clk;
+
+ /* Get selected clock source */
+ switch (adc) {
+ case 0:
+ clk = CMU_ClockSelectGet(cmuClock_ADC0ASYNC);
+ break;
+
+#if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK)
+ case 1:
+ clk = CMU_ClockSelectGet(cmuClock_ADC1ASYNC);
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ return 0;
+ }
+
+ switch (clk) {
+ case cmuSelect_Disabled:
+ ret = 0;
+ break;
+
+ case cmuSelect_AUXHFRCO:
+ ret = auxClkGet();
+ break;
+
+ case cmuSelect_HFXO:
+ ret = SystemHFXOClockGet();
+ break;
+
+ case cmuSelect_HFSRCCLK:
+ ret = hfSrcClkGet();
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+#endif
+
+#if defined(_CMU_SDIOCTRL_MASK)
+/***************************************************************************//**
+ * @brief
+ * Get the SDIO reference clock frequency
+ *
+ * @return
+ * SDIO reference clock frequency in Hz
+ ******************************************************************************/
+static uint32_t sdioRefClkGet(void)
+{
+ uint32_t ret;
+ CMU_Select_TypeDef clk;
+
+ /* Get selected clock source */
+ clk = CMU_ClockSelectGet(cmuClock_SDIOREF);
+
+ switch (clk) {
+ case cmuSelect_HFRCO:
+ ret = SystemHfrcoFreq;
+ break;
+
+ case cmuSelect_HFXO:
+ ret = SystemHFXOClockGet();
+ break;
+
+ case cmuSelect_AUXHFRCO:
+ ret = auxClkGet();
+ break;
+
+ case cmuSelect_USHFRCO:
+ ret = ushfrcoFreq;
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+#endif
+
+#if defined(_CMU_QSPICTRL_MASK)
+/***************************************************************************//**
+ * @brief
+ * Get the QSPI n reference clock frequency
+ *
+ * @return
+ * QSPI n reference clock frequency in Hz
+ ******************************************************************************/
+static uint32_t qspiRefClkGet(uint32_t qspi)
+{
+ uint32_t ret;
+ CMU_Select_TypeDef clk;
+
+ /* Get selected clock source */
+ switch (qspi) {
+ case 0:
+ clk = CMU_ClockSelectGet(cmuClock_QSPI0REF);
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ return 0;
+ }
+
+ switch (clk) {
+ case cmuSelect_HFRCO:
+ ret = SystemHfrcoFreq;
+ break;
+
+ case cmuSelect_HFXO:
+ ret = SystemHFXOClockGet();
+ break;
+
+ case cmuSelect_AUXHFRCO:
+ ret = auxClkGet();
+ break;
+
+ case cmuSelect_USHFRCO:
+ ret = ushfrcoFreq;
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+#endif
+
+#if defined(USBR_CLOCK_PRESENT)
+/***************************************************************************//**
+ * @brief
+ * Get the USB rate clock frequency
+ *
+ * @return
+ * USB rate clock frequency in Hz
+ ******************************************************************************/
+static uint32_t usbRateClkGet(void)
+{
+ uint32_t ret;
+ CMU_Select_TypeDef clk;
+
+ clk = CMU_ClockSelectGet(cmuClock_USBR);
+
+ switch (clk) {
+ case cmuSelect_USHFRCO:
+ ret = ushfrcoFreq;
+ break;
+
+ case cmuSelect_HFXO:
+ ret = SystemHFXOClockGet();
+ break;
+
+ case cmuSelect_HFXOX2:
+ ret = 2u * SystemHFXOClockGet();
+ break;
+
+ case cmuSelect_HFRCO:
+ ret = SystemHfrcoFreq;
+ break;
+
+ case cmuSelect_LFXO:
+ ret = SystemLFXOClockGet();
+ break;
+
+ case cmuSelect_LFRCO:
+ ret = SystemLFRCOClockGet();
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Configure flash access wait states in order to support given core clock
+ * frequency.
+ *
+ * @param[in] coreFreq
+ * Core clock frequency to configure flash wait-states for
+ *
+ * @param[in] vscale
+ * Voltage Scale level. Supported levels are 0 and 2 where 0 is the default.
+ ******************************************************************************/
+static void flashWaitStateControl(uint32_t coreFreq, int vscale)
+{
+ uint32_t mode;
+ bool mscLocked;
+#if defined(MSC_READCTRL_MODE_WS0SCBTP)
+ bool scbtpEn; /* Suppressed Conditional Branch Target Prefetch setting. */
+#endif
+ (void) vscale; /* vscale parameter is only used on some devices */
+
+ /* Make sure the MSC is unlocked */
+ mscLocked = MSC->LOCK;
+ MSC->LOCK = MSC_UNLOCK_CODE;
+
+ /* Get mode and SCBTP enable */
+ mode = MSC->READCTRL & _MSC_READCTRL_MODE_MASK;
+#if defined(MSC_READCTRL_MODE_WS0SCBTP)
+ /* Devices with MODE and SCBTP in same register field */
+ switch (mode) {
+ case MSC_READCTRL_MODE_WS0:
+ case MSC_READCTRL_MODE_WS1:
+#if defined(MSC_READCTRL_MODE_WS2)
+ case MSC_READCTRL_MODE_WS2:
+#endif
+ scbtpEn = false;
+ break;
+
+ default: /* WSxSCBTP */
+ scbtpEn = true;
+ break;
+ }
+
+ /* Set mode based on the core clock frequency and SCBTP enable */
+ if (false) {
+ }
+#if defined(MSC_READCTRL_MODE_WS2)
+ else if (coreFreq > CMU_MAX_FREQ_1WS) {
+ mode = (scbtpEn ? MSC_READCTRL_MODE_WS2SCBTP : MSC_READCTRL_MODE_WS2);
+ }
+#endif
+ else if ((coreFreq <= CMU_MAX_FREQ_1WS) && (coreFreq > CMU_MAX_FREQ_0WS)) {
+ mode = (scbtpEn ? MSC_READCTRL_MODE_WS1SCBTP : MSC_READCTRL_MODE_WS1);
+ } else {
+ mode = (scbtpEn ? MSC_READCTRL_MODE_WS0SCBTP : MSC_READCTRL_MODE_WS0);
+ }
+
+#elif defined(_SILICON_LABS_32B_SERIES_1) && defined(_EMU_STATUS_VSCALE_MASK)
+
+ /* These devices have specific requirements on the supported flash wait state
+ * depending on frequency and voltage scale level. */
+ uint32_t i;
+ for (i = 0; i < FLASH_WS_TABLE_ENTRIES; i++) {
+ if ((flashWsTable[i].vscale == vscale)
+ && (coreFreq <= flashWsTable[i].maxFreq)) {
+ break; // found matching entry
+ }
+ }
+
+ if (i == FLASH_WS_TABLE_ENTRIES) {
+ EFM_ASSERT(false);
+ mode = 3; // worst case flash wait state for unsupported cases
+ } else {
+ mode = flashWsTable[i].ws;
+ }
+ mode = mode << _MSC_READCTRL_MODE_SHIFT;
+
+#else
+ /* Devices where MODE and SCBTP are in separate fields and where the device
+ * either does not support voltage scale or where the voltage scale does
+ * not impact flash wait state configuration. */
+ if (coreFreq <= CMU_MAX_FREQ_0WS) {
+ mode = 0;
+ } else if (coreFreq <= CMU_MAX_FREQ_1WS) {
+ mode = 1;
+ }
+#if defined(MSC_READCTRL_MODE_WS2)
+ else if (coreFreq <= CMU_MAX_FREQ_2WS) {
+ mode = 2;
+ }
+#endif
+#if defined(MSC_READCTRL_MODE_WS3)
+ else if (coreFreq <= CMU_MAX_FREQ_3WS) {
+ mode = 3;
+ }
+#endif
+ mode = mode << _MSC_READCTRL_MODE_SHIFT;
+
+#endif
+
+ /* BUS_RegMaskedWrite cannot be used here as it would temporarily set the
+ mode field to WS0 */
+ MSC->READCTRL = (MSC->READCTRL & ~_MSC_READCTRL_MODE_MASK) | mode;
+
+ if (mscLocked) {
+ MSC->LOCK = 0;
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Configure flash access wait states to most conservative setting for
+ * this target. Retain SCBTP (Suppressed Conditional Branch Target Prefetch)
+ * setting.
+ ******************************************************************************/
+static void flashWaitStateMax(void)
+{
+ flashWaitStateControl(SystemMaxCoreClockGet(), 0);
+}
+
+#if defined(_MSC_RAMCTRL_RAMWSEN_MASK)
+/***************************************************************************//**
+ * @brief
+ * Configure RAM access wait states in order to support given core clock
+ * frequency.
+ *
+ * @param[in] coreFreq
+ * Core clock frequency to configure RAM wait-states for
+ *
+ * @param[in] vscale
+ * Voltage Scale level. Supported levels are 0 and 2 where 0 is the default.
+ ******************************************************************************/
+static void setRamWaitState(uint32_t coreFreq, int vscale)
+{
+ uint32_t limit = 38000000;
+ if (vscale == 2) {
+ limit = 16000000;
+ }
+
+ if (coreFreq > limit) {
+ BUS_RegMaskedSet(&MSC->RAMCTRL, (MSC_RAMCTRL_RAMWSEN
+ | MSC_RAMCTRL_RAM1WSEN
+ | MSC_RAMCTRL_RAM2WSEN));
+ } else {
+ BUS_RegMaskedClear(&MSC->RAMCTRL, (MSC_RAMCTRL_RAMWSEN
+ | MSC_RAMCTRL_RAM1WSEN
+ | MSC_RAMCTRL_RAM2WSEN));
+ }
+}
+#endif
+
+#if defined(_MSC_CTRL_WAITMODE_MASK)
+/***************************************************************************//**
+ * @brief
+ * Configure wait state for peripheral accesses over the bus to support
+ * given bus clock frequency.
+ *
+ * @param[in] busFreq
+ * peripheral bus clock frequency to configure wait-states for
+ *
+ * @param[in] vscale
+ * The voltage scale to configure wait-states for. Expected values are
+ * 0 or 2.
+ *
+ * @li 0 = 1.2 V (VSCALE2)
+ * @li 2 = 1.0 V (VSCALE0)
+ * ******************************************************************************/
+static void setBusWaitState(uint32_t busFreq, int vscale)
+{
+ if ((busFreq > 50000000) && (vscale == 0)) {
+ BUS_RegMaskedSet(&MSC->CTRL, MSC_CTRL_WAITMODE_WS1);
+ } else {
+ BUS_RegMaskedClear(&MSC->CTRL, MSC_CTRL_WAITMODE_WS1);
+ }
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Configure various wait states necessary to switch to a certain frequency
+ * and a certain voltage scale.
+ *
+ * @details
+ * This function will setup the necessary flash, bus and RAM wait states.
+ * Updating the wait state configuration must be done before
+ * increasing the clock frequency, and it must be done after decreasing the
+ * clock frequency. Updating the wait state configuration must be done before
+ * core voltage is decreased, and it must be done after a core voltage is
+ * increased.
+ *
+ * @param[in] coreFreq
+ * Core clock frequency to configure wait-states for.
+ *
+ * @param[in] vscale
+ * The voltage scale to configure wait-states for. Expected values are
+ * 0 or 2, higher number is lower voltage.
+ *
+ * @li 0 = 1.2 V (VSCALE2)
+ * @li 2 = 1.0 V (VSCALE0)
+ *
+ ******************************************************************************/
+void CMU_UpdateWaitStates(uint32_t freq, int vscale)
+{
+ flashWaitStateControl(freq, vscale);
+#if defined(_MSC_RAMCTRL_RAMWSEN_MASK)
+ setRamWaitState(freq, vscale);
+#endif
+#if defined(_MSC_CTRL_WAITMODE_MASK)
+ setBusWaitState(freq, vscale);
+#endif
+}
+
+#if defined(_CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK)
+/***************************************************************************//**
+ * @brief
+ * Return upper value for CMU_HFXOSTEADYSTATECTRL_REGISH
+ ******************************************************************************/
+static uint32_t getRegIshUpperVal(uint32_t steadyStateRegIsh)
+{
+ uint32_t regIshUpper;
+ const uint32_t upperMax = _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK
+ >> _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_SHIFT;
+ /* Add 3 as specified in register description for CMU_HFXOSTEADYSTATECTRL_REGISHUPPER. */
+ regIshUpper = SL_MIN(steadyStateRegIsh + 3, upperMax);
+ regIshUpper <<= _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_SHIFT;
+ return regIshUpper;
+}
+#endif
+
+#if defined(_CMU_HFXOCTRL_MASK)
+/***************************************************************************//**
+ * @brief
+ * Get the HFXO tuning mode
+ *
+ * @return
+ * The current HFXO tuning mode from the HFXOCTRL register.
+ ******************************************************************************/
+__STATIC_INLINE uint32_t getHfxoTuningMode(void)
+{
+#if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK)
+ return (CMU->HFXOCTRL & _CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK);
+#else
+ return (CMU->HFXOCTRL & _CMU_HFXOCTRL_PEAKDETMODE_MASK);
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set the HFXO tuning mode
+ *
+ * @param[in] mode
+ * the new HFXO tuning mode, this can be HFXO_TUNING_MODE_AUTO or
+ * HFXO_TUNING_MODE_CMD.
+ ******************************************************************************/
+__STATIC_INLINE void setHfxoTuningMode(uint32_t mode)
+{
+#if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK)
+ CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) | mode;
+#else
+ CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETMODE_MASK) | mode;
+#endif
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Get the LFnCLK frequency based on current configuration.
+ *
+ * @param[in] lfClkBranch
+ * Selected LF branch
+ *
+ * @return
+ * The LFnCLK frequency in Hz. If no LFnCLK is selected (disabled), 0 is
+ * returned.
+ ******************************************************************************/
+static uint32_t lfClkGet(CMU_Clock_TypeDef lfClkBranch)
+{
+ uint32_t sel;
+ uint32_t ret = 0;
+
+ switch (lfClkBranch) {
+ case cmuClock_LFA:
+ case cmuClock_LFB:
+#if defined(_CMU_LFCCLKEN0_MASK)
+ case cmuClock_LFC:
+#endif
+#if defined(_CMU_LFECLKSEL_MASK)
+ case cmuClock_LFE:
+#endif
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+
+ sel = CMU_ClockSelectGet(lfClkBranch);
+
+ /* Get clock select field */
+ switch (lfClkBranch) {
+ case cmuClock_LFA:
+#if defined(_CMU_LFCLKSEL_MASK)
+ sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFA_MASK) >> _CMU_LFCLKSEL_LFA_SHIFT;
+#elif defined(_CMU_LFACLKSEL_MASK)
+ sel = (CMU->LFACLKSEL & _CMU_LFACLKSEL_LFA_MASK) >> _CMU_LFACLKSEL_LFA_SHIFT;
+#else
+ EFM_ASSERT(0);
+#endif
+ break;
+
+ case cmuClock_LFB:
+#if defined(_CMU_LFCLKSEL_MASK)
+ sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFB_MASK) >> _CMU_LFCLKSEL_LFB_SHIFT;
+#elif defined(_CMU_LFBCLKSEL_MASK)
+ sel = (CMU->LFBCLKSEL & _CMU_LFBCLKSEL_LFB_MASK) >> _CMU_LFBCLKSEL_LFB_SHIFT;
+#else
+ EFM_ASSERT(0);
+#endif
+ break;
+
+#if defined(_CMU_LFCCLKEN0_MASK)
+ case cmuClock_LFC:
+#if defined(_CMU_LFCLKSEL_LFC_MASK)
+ sel = (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFC_MASK) >> _CMU_LFCLKSEL_LFC_SHIFT;
+#elif defined(_CMU_LFCCLKSEL_LFC_MASK)
+ sel = (CMU->LFCCLKSEL & _CMU_LFCCLKSEL_LFC_MASK) >> _CMU_LFCCLKSEL_LFC_SHIFT;
+#else
+ EFM_ASSERT(0);
+#endif
+ break;
+#endif
+
+#if defined(_CMU_LFECLKSEL_MASK)
+ case cmuClock_LFE:
+ sel = (CMU->LFECLKSEL & _CMU_LFECLKSEL_LFE_MASK) >> _CMU_LFECLKSEL_LFE_SHIFT;
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+
+ /* Get clock frequency */
+#if defined(_CMU_LFCLKSEL_MASK)
+ switch (sel) {
+ case _CMU_LFCLKSEL_LFA_LFRCO:
+ ret = SystemLFRCOClockGet();
+ break;
+
+ case _CMU_LFCLKSEL_LFA_LFXO:
+ ret = SystemLFXOClockGet();
+ break;
+
+#if defined(_CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2)
+ case _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2:
+#if defined(CMU_MAX_FREQ_HFLE)
+ /* HFLE bit is or'ed by hardware with HFCORECLKLEDIV to reduce the
+ * frequency of CMU_HFCORECLKLEDIV2. */
+ ret = SystemCoreClockGet() / (1U << (getHfLeConfig() + 1));
+#else
+ ret = SystemCoreClockGet() / 2U;
+#endif
+ break;
+#endif
+
+ case _CMU_LFCLKSEL_LFA_DISABLED:
+ ret = 0;
+#if defined(CMU_LFCLKSEL_LFAE)
+ /* Check LF Extended bit setting for LFA or LFB ULFRCO clock */
+ if ((lfClkBranch == cmuClock_LFA) || (lfClkBranch == cmuClock_LFB)) {
+ if (CMU->LFCLKSEL >> (lfClkBranch == cmuClock_LFA
+ ? _CMU_LFCLKSEL_LFAE_SHIFT
+ : _CMU_LFCLKSEL_LFBE_SHIFT)) {
+ ret = SystemULFRCOClockGet();
+ }
+ }
+#endif
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0U;
+ break;
+ }
+#endif /* _CMU_LFCLKSEL_MASK */
+
+#if defined(_CMU_LFACLKSEL_MASK)
+ switch (sel) {
+ case _CMU_LFACLKSEL_LFA_LFRCO:
+ ret = SystemLFRCOClockGet();
+ break;
+
+ case _CMU_LFACLKSEL_LFA_LFXO:
+ ret = SystemLFXOClockGet();
+ break;
+
+ case _CMU_LFACLKSEL_LFA_ULFRCO:
+ ret = SystemULFRCOClockGet();
+ break;
+
+#if defined(CMU_LFACLKSEL_LFA_PLFRCO)
+ case _CMU_LFACLKSEL_LFA_PLFRCO:
+ ret = SystemLFRCOClockGet();
+ break;
+#endif
+
+#if defined(_CMU_LFACLKSEL_LFA_HFCLKLE)
+ case _CMU_LFACLKSEL_LFA_HFCLKLE:
+ ret = SystemCoreClockGet()
+ / CMU_Log2ToDiv(((CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK)
+ >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT) + 1);
+ break;
+#elif defined(_CMU_LFBCLKSEL_LFB_HFCLKLE)
+ case _CMU_LFBCLKSEL_LFB_HFCLKLE:
+ ret = SystemCoreClockGet()
+ / CMU_Log2ToDiv(((CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK)
+ >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT) + 1);
+ break;
+#endif
+
+ case _CMU_LFACLKSEL_LFA_DISABLED:
+ ret = 0;
+ break;
+ }
+#endif
+
+ return ret;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Wait for ongoing sync of register(s) to low frequency domain to complete.
+ *
+ * @param[in] mask
+ * Bitmask corresponding to SYNCBUSY register defined bits, indicating
+ * registers that must complete any ongoing synchronization.
+ ******************************************************************************/
+__STATIC_INLINE void syncReg(uint32_t mask)
+{
+ /* Avoid deadlock if modifying the same register twice when freeze mode is */
+ /* activated. */
+ if (CMU->FREEZE & CMU_FREEZE_REGFREEZE) {
+ return;
+ }
+
+ /* Wait for any pending previous write operation to have been completed */
+ /* in low frequency domain */
+ while (CMU->SYNCBUSY & mask) {
+ }
+}
+
+#if defined(USBC_CLOCK_PRESENT)
+/***************************************************************************//**
+ * @brief
+ * Get the USBC frequency
+ *
+ * @return
+ * USBC frequency in Hz
+ ******************************************************************************/
+static uint32_t usbCClkGet(void)
+{
+ uint32_t ret;
+ CMU_Select_TypeDef clk;
+
+ /* Get selected clock source */
+ clk = CMU_ClockSelectGet(cmuClock_USBC);
+
+ switch (clk) {
+ case cmuSelect_LFXO:
+ ret = SystemLFXOClockGet();
+ break;
+ case cmuSelect_LFRCO:
+ ret = SystemLFRCOClockGet();
+ break;
+#if defined (_CMU_USHFRCOCTRL_MASK)
+ case cmuSelect_USHFRCO:
+ ret = ushfrcoFreq;
+ break;
+#endif
+ case cmuSelect_HFCLK:
+ ret = SystemHFClockGet();
+ break;
+ default:
+ /* Clock is not enabled */
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+#endif
+
+/** @endcond */
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+#if defined(_CMU_AUXHFRCOCTRL_BAND_MASK)
+/***************************************************************************//**
+ * @brief
+ * Get AUXHFRCO band in use.
+ *
+ * @return
+ * AUXHFRCO band in use.
+ ******************************************************************************/
+CMU_AUXHFRCOBand_TypeDef CMU_AUXHFRCOBandGet(void)
+{
+ return (CMU_AUXHFRCOBand_TypeDef)((CMU->AUXHFRCOCTRL
+ & _CMU_AUXHFRCOCTRL_BAND_MASK)
+ >> _CMU_AUXHFRCOCTRL_BAND_SHIFT);
+}
+#endif /* _CMU_AUXHFRCOCTRL_BAND_MASK */
+
+#if defined(_CMU_AUXHFRCOCTRL_BAND_MASK)
+/***************************************************************************//**
+ * @brief
+ * Set AUXHFRCO band and the tuning value based on the value in the
+ * calibration table made during production.
+ *
+ * @param[in] band
+ * AUXHFRCO band to activate.
+ ******************************************************************************/
+void CMU_AUXHFRCOBandSet(CMU_AUXHFRCOBand_TypeDef band)
+{
+ uint32_t tuning;
+
+ /* Read tuning value from calibration table */
+ switch (band) {
+ case cmuAUXHFRCOBand_1MHz:
+ tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND1_MASK)
+ >> _DEVINFO_AUXHFRCOCAL0_BAND1_SHIFT;
+ break;
+
+ case cmuAUXHFRCOBand_7MHz:
+ tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND7_MASK)
+ >> _DEVINFO_AUXHFRCOCAL0_BAND7_SHIFT;
+ break;
+
+ case cmuAUXHFRCOBand_11MHz:
+ tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND11_MASK)
+ >> _DEVINFO_AUXHFRCOCAL0_BAND11_SHIFT;
+ break;
+
+ case cmuAUXHFRCOBand_14MHz:
+ tuning = (DEVINFO->AUXHFRCOCAL0 & _DEVINFO_AUXHFRCOCAL0_BAND14_MASK)
+ >> _DEVINFO_AUXHFRCOCAL0_BAND14_SHIFT;
+ break;
+
+ case cmuAUXHFRCOBand_21MHz:
+ tuning = (DEVINFO->AUXHFRCOCAL1 & _DEVINFO_AUXHFRCOCAL1_BAND21_MASK)
+ >> _DEVINFO_AUXHFRCOCAL1_BAND21_SHIFT;
+ break;
+
+#if defined(_CMU_AUXHFRCOCTRL_BAND_28MHZ)
+ case cmuAUXHFRCOBand_28MHz:
+ tuning = (DEVINFO->AUXHFRCOCAL1 & _DEVINFO_AUXHFRCOCAL1_BAND28_MASK)
+ >> _DEVINFO_AUXHFRCOCAL1_BAND28_SHIFT;
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Set band/tuning */
+ CMU->AUXHFRCOCTRL = (CMU->AUXHFRCOCTRL
+ & ~(_CMU_AUXHFRCOCTRL_BAND_MASK
+ | _CMU_AUXHFRCOCTRL_TUNING_MASK))
+ | (band << _CMU_AUXHFRCOCTRL_BAND_SHIFT)
+ | (tuning << _CMU_AUXHFRCOCTRL_TUNING_SHIFT);
+}
+#endif /* _CMU_AUXHFRCOCTRL_BAND_MASK */
+
+#if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
+/**************************************************************************//**
+ * @brief
+ * Get the AUXHFRCO frequency calibration word in DEVINFO
+ *
+ * @param[in] freq
+ * Frequency in Hz
+ *
+ * @return
+ * AUXHFRCO calibration word for a given frequency
+ *****************************************************************************/
+static uint32_t CMU_AUXHFRCODevinfoGet(CMU_AUXHFRCOFreq_TypeDef freq)
+{
+ switch (freq) {
+ /* 1, 2 and 4MHz share the same calibration word */
+ case cmuAUXHFRCOFreq_1M0Hz:
+ case cmuAUXHFRCOFreq_2M0Hz:
+ case cmuAUXHFRCOFreq_4M0Hz:
+ return DEVINFO->AUXHFRCOCAL0;
+
+ case cmuAUXHFRCOFreq_7M0Hz:
+ return DEVINFO->AUXHFRCOCAL3;
+
+ case cmuAUXHFRCOFreq_13M0Hz:
+ return DEVINFO->AUXHFRCOCAL6;
+
+ case cmuAUXHFRCOFreq_16M0Hz:
+ return DEVINFO->AUXHFRCOCAL7;
+
+ case cmuAUXHFRCOFreq_19M0Hz:
+ return DEVINFO->AUXHFRCOCAL8;
+
+ case cmuAUXHFRCOFreq_26M0Hz:
+ return DEVINFO->AUXHFRCOCAL10;
+
+ case cmuAUXHFRCOFreq_32M0Hz:
+ return DEVINFO->AUXHFRCOCAL11;
+
+ case cmuAUXHFRCOFreq_38M0Hz:
+ return DEVINFO->AUXHFRCOCAL12;
+
+#if defined(DEVINFO_AUXHFRCOCAL14)
+ case cmuAUXHFRCOFreq_48M0Hz:
+ return DEVINFO->AUXHFRCOCAL13;
+
+ case cmuAUXHFRCOFreq_50M0Hz:
+ return DEVINFO->AUXHFRCOCAL14;
+#endif
+
+ default: /* cmuAUXHFRCOFreq_UserDefined */
+ return 0;
+ }
+}
+#endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */
+
+#if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
+/***************************************************************************//**
+ * @brief
+ * Get current AUXHFRCO frequency.
+ *
+ * @return
+ * AUXHFRCO frequency
+ ******************************************************************************/
+CMU_AUXHFRCOFreq_TypeDef CMU_AUXHFRCOBandGet(void)
+{
+ return auxHfrcoFreq;
+}
+#endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */
+
+#if defined(_CMU_AUXHFRCOCTRL_FREQRANGE_MASK)
+/***************************************************************************//**
+ * @brief
+ * Set AUXHFRCO calibration for the selected target frequency.
+ *
+ * @param[in] setFreq
+ * AUXHFRCO frequency to set
+ ******************************************************************************/
+void CMU_AUXHFRCOBandSet(CMU_AUXHFRCOFreq_TypeDef setFreq)
+{
+ uint32_t freqCal;
+
+ /* Get DEVINFO index, set global auxHfrcoFreq */
+ freqCal = CMU_AUXHFRCODevinfoGet(setFreq);
+ EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX));
+ auxHfrcoFreq = setFreq;
+
+ /* Wait for any previous sync to complete, and then set calibration data
+ for the selected frequency. */
+ while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT)) ;
+
+ /* Set divider in AUXHFRCOCTRL for 1, 2 and 4MHz */
+ switch (setFreq) {
+ case cmuAUXHFRCOFreq_1M0Hz:
+ freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK)
+ | CMU_AUXHFRCOCTRL_CLKDIV_DIV4;
+ break;
+
+ case cmuAUXHFRCOFreq_2M0Hz:
+ freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK)
+ | CMU_AUXHFRCOCTRL_CLKDIV_DIV2;
+ break;
+
+ case cmuAUXHFRCOFreq_4M0Hz:
+ freqCal = (freqCal & ~_CMU_AUXHFRCOCTRL_CLKDIV_MASK)
+ | CMU_AUXHFRCOCTRL_CLKDIV_DIV1;
+ break;
+
+ default:
+ break;
+ }
+ CMU->AUXHFRCOCTRL = freqCal;
+}
+#endif /* _CMU_AUXHFRCOCTRL_FREQRANGE_MASK */
+
+/***************************************************************************//**
+ * @brief
+ * Calibrate clock.
+ *
+ * @details
+ * Run a calibration for HFCLK against a selectable reference clock. Please
+ * refer to the reference manual, CMU chapter, for further details.
+ *
+ * @note
+ * This function will not return until calibration measurement is completed.
+ *
+ * @param[in] HFCycles
+ * The number of HFCLK cycles to run calibration. Increasing this number
+ * increases precision, but the calibration will take more time.
+ *
+ * @param[in] ref
+ * The reference clock used to compare HFCLK with.
+ *
+ * @return
+ * The number of ticks the reference clock after HFCycles ticks on the HF
+ * clock.
+ ******************************************************************************/
+uint32_t CMU_Calibrate(uint32_t HFCycles, CMU_Osc_TypeDef ref)
+{
+ EFM_ASSERT(HFCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT));
+
+ /* Set reference clock source */
+ switch (ref) {
+ case cmuOsc_LFXO:
+ CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFXO;
+ break;
+
+ case cmuOsc_LFRCO:
+ CMU->CALCTRL = CMU_CALCTRL_UPSEL_LFRCO;
+ break;
+
+ case cmuOsc_HFXO:
+ CMU->CALCTRL = CMU_CALCTRL_UPSEL_HFXO;
+ break;
+
+ case cmuOsc_HFRCO:
+ CMU->CALCTRL = CMU_CALCTRL_UPSEL_HFRCO;
+ break;
+
+ case cmuOsc_AUXHFRCO:
+ CMU->CALCTRL = CMU_CALCTRL_UPSEL_AUXHFRCO;
+ break;
+
+#if defined (_CMU_USHFRCOCTRL_MASK)
+ case cmuOsc_USHFRCO:
+ CMU->CALCTRL = CMU_CALCTRL_UPSEL_USHFRCO;
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ return 0;
+ }
+
+ /* Set top value */
+ CMU->CALCNT = HFCycles;
+
+ /* Start calibration */
+ CMU->CMD = CMU_CMD_CALSTART;
+
+#if defined(CMU_STATUS_CALRDY)
+ /* Wait until calibration completes */
+ while (!BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT)) {
+ }
+#else
+ /* Wait until calibration completes */
+ while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) {
+ }
+#endif
+
+ return CMU->CALCNT;
+}
+
+#if defined(_CMU_CALCTRL_UPSEL_MASK) && defined(_CMU_CALCTRL_DOWNSEL_MASK)
+/***************************************************************************//**
+ * @brief
+ * Configure clock calibration
+ *
+ * @details
+ * Configure a calibration for a selectable clock source against another
+ * selectable reference clock.
+ * Refer to the reference manual, CMU chapter, for further details.
+ *
+ * @note
+ * After configuration, a call to CMU_CalibrateStart() is required, and
+ * the resulting calibration value can be read out with the
+ * CMU_CalibrateCountGet() function call.
+ *
+ * @param[in] downCycles
+ * The number of downSel clock cycles to run calibration. Increasing this
+ * number increases precision, but the calibration will take more time.
+ *
+ * @param[in] downSel
+ * The clock which will be counted down downCycles
+ *
+ * @param[in] upSel
+ * The reference clock, the number of cycles generated by this clock will
+ * be counted and added up, the result can be given with the
+ * CMU_CalibrateCountGet() function call.
+ ******************************************************************************/
+void CMU_CalibrateConfig(uint32_t downCycles, CMU_Osc_TypeDef downSel,
+ CMU_Osc_TypeDef upSel)
+{
+ /* Keep untouched configuration settings */
+ uint32_t calCtrl = CMU->CALCTRL
+ & ~(_CMU_CALCTRL_UPSEL_MASK | _CMU_CALCTRL_DOWNSEL_MASK);
+
+ /* 20 bits of precision to calibration count register */
+ EFM_ASSERT(downCycles <= (_CMU_CALCNT_CALCNT_MASK >> _CMU_CALCNT_CALCNT_SHIFT));
+
+ /* Set down counting clock source - down counter */
+ switch (downSel) {
+ case cmuOsc_LFXO:
+ calCtrl |= CMU_CALCTRL_DOWNSEL_LFXO;
+ break;
+
+ case cmuOsc_LFRCO:
+ calCtrl |= CMU_CALCTRL_DOWNSEL_LFRCO;
+ break;
+
+ case cmuOsc_HFXO:
+ calCtrl |= CMU_CALCTRL_DOWNSEL_HFXO;
+ break;
+
+ case cmuOsc_HFRCO:
+ calCtrl |= CMU_CALCTRL_DOWNSEL_HFRCO;
+ break;
+
+ case cmuOsc_AUXHFRCO:
+ calCtrl |= CMU_CALCTRL_DOWNSEL_AUXHFRCO;
+ break;
+
+#if defined (_CMU_USHFRCOCTRL_MASK)
+ case cmuOsc_USHFRCO:
+ calCtrl |= CMU_CALCTRL_DOWNSEL_USHFRCO;
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+
+ /* Set top value to be counted down by the downSel clock */
+ CMU->CALCNT = downCycles;
+
+ /* Set reference clock source - up counter */
+ switch (upSel) {
+ case cmuOsc_LFXO:
+ calCtrl |= CMU_CALCTRL_UPSEL_LFXO;
+ break;
+
+ case cmuOsc_LFRCO:
+ calCtrl |= CMU_CALCTRL_UPSEL_LFRCO;
+ break;
+
+ case cmuOsc_HFXO:
+ calCtrl |= CMU_CALCTRL_UPSEL_HFXO;
+ break;
+
+ case cmuOsc_HFRCO:
+ calCtrl |= CMU_CALCTRL_UPSEL_HFRCO;
+ break;
+
+ case cmuOsc_AUXHFRCO:
+ calCtrl |= CMU_CALCTRL_UPSEL_AUXHFRCO;
+ break;
+
+#if defined (_CMU_USHFRCOCTRL_MASK)
+ case cmuOsc_USHFRCO:
+ calCtrl |= CMU_CALCTRL_UPSEL_USHFRCO;
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+
+ CMU->CALCTRL = calCtrl;
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Get calibration count register
+ * @note
+ * If continuous calibrartion mode is active, calibration busy will almost
+ * always be off, and we just need to read the value, where the normal case
+ * would be that this function call has been triggered by the CALRDY
+ * interrupt flag.
+ * @return
+ * Calibration count, the number of UPSEL clocks (see CMU_CalibrateConfig)
+ * in the period of DOWNSEL oscillator clock cycles configured by a previous
+ * write operation to CMU->CALCNT
+ ******************************************************************************/
+uint32_t CMU_CalibrateCountGet(void)
+{
+ /* Wait until calibration completes, UNLESS continuous calibration mode is */
+ /* active */
+#if defined(CMU_CALCTRL_CONT)
+ if (!BUS_RegBitRead(&CMU->CALCTRL, _CMU_CALCTRL_CONT_SHIFT)) {
+#if defined(CMU_STATUS_CALRDY)
+ /* Wait until calibration completes */
+ while (!BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALRDY_SHIFT)) {
+ }
+#else
+ /* Wait until calibration completes */
+ while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) {
+ }
+#endif
+ }
+#else
+ while (BUS_RegBitRead(&CMU->STATUS, _CMU_STATUS_CALBSY_SHIFT)) {
+ }
+#endif
+ return CMU->CALCNT;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get clock divisor/prescaler.
+ *
+ * @param[in] clock
+ * Clock point to get divisor/prescaler for. Notice that not all clock points
+ * have a divisor/prescaler. Please refer to CMU overview in reference manual.
+ *
+ * @return
+ * The current clock point divisor/prescaler. 1 is returned
+ * if @p clock specifies a clock point without a divisor/prescaler.
+ ******************************************************************************/
+CMU_ClkDiv_TypeDef CMU_ClockDivGet(CMU_Clock_TypeDef clock)
+{
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ return 1 + (uint32_t)CMU_ClockPrescGet(clock);
+
+#elif defined(_SILICON_LABS_32B_SERIES_0)
+ uint32_t divReg;
+ CMU_ClkDiv_TypeDef ret;
+
+ /* Get divisor reg id */
+ divReg = (clock >> CMU_DIV_REG_POS) & CMU_DIV_REG_MASK;
+
+ switch (divReg) {
+#if defined(_CMU_CTRL_HFCLKDIV_MASK)
+ case CMU_HFCLKDIV_REG:
+ ret = 1 + ((CMU->CTRL & _CMU_CTRL_HFCLKDIV_MASK)
+ >> _CMU_CTRL_HFCLKDIV_SHIFT);
+ break;
+#endif
+
+ case CMU_HFPERCLKDIV_REG:
+ ret = (CMU_ClkDiv_TypeDef)((CMU->HFPERCLKDIV
+ & _CMU_HFPERCLKDIV_HFPERCLKDIV_MASK)
+ >> _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT);
+ ret = CMU_Log2ToDiv(ret);
+ break;
+
+ case CMU_HFCORECLKDIV_REG:
+ ret = (CMU_ClkDiv_TypeDef)((CMU->HFCORECLKDIV
+ & _CMU_HFCORECLKDIV_HFCORECLKDIV_MASK)
+ >> _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT);
+ ret = CMU_Log2ToDiv(ret);
+ break;
+
+ case CMU_LFAPRESC0_REG:
+ switch (clock) {
+ case cmuClock_RTC:
+ ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK)
+ >> _CMU_LFAPRESC0_RTC_SHIFT);
+ ret = CMU_Log2ToDiv(ret);
+ break;
+
+#if defined(_CMU_LFAPRESC0_LETIMER0_MASK)
+ case cmuClock_LETIMER0:
+ ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK)
+ >> _CMU_LFAPRESC0_LETIMER0_SHIFT);
+ ret = CMU_Log2ToDiv(ret);
+ break;
+#endif
+
+#if defined(_CMU_LFAPRESC0_LCD_MASK)
+ case cmuClock_LCDpre:
+ ret = (CMU_ClkDiv_TypeDef)(((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK)
+ >> _CMU_LFAPRESC0_LCD_SHIFT)
+ + CMU_DivToLog2(cmuClkDiv_16));
+ ret = CMU_Log2ToDiv(ret);
+ break;
+#endif
+
+#if defined(_CMU_LFAPRESC0_LESENSE_MASK)
+ case cmuClock_LESENSE:
+ ret = (CMU_ClkDiv_TypeDef)((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK)
+ >> _CMU_LFAPRESC0_LESENSE_SHIFT);
+ ret = CMU_Log2ToDiv(ret);
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ ret = cmuClkDiv_1;
+ break;
+ }
+ break;
+
+ case CMU_LFBPRESC0_REG:
+ switch (clock) {
+#if defined(_CMU_LFBPRESC0_LEUART0_MASK)
+ case cmuClock_LEUART0:
+ ret = (CMU_ClkDiv_TypeDef)((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK)
+ >> _CMU_LFBPRESC0_LEUART0_SHIFT);
+ ret = CMU_Log2ToDiv(ret);
+ break;
+#endif
+
+#if defined(_CMU_LFBPRESC0_LEUART1_MASK)
+ case cmuClock_LEUART1:
+ ret = (CMU_ClkDiv_TypeDef)((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK)
+ >> _CMU_LFBPRESC0_LEUART1_SHIFT);
+ ret = CMU_Log2ToDiv(ret);
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ ret = cmuClkDiv_1;
+ break;
+ }
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ ret = cmuClkDiv_1;
+ break;
+ }
+
+ return ret;
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set clock divisor/prescaler.
+ *
+ * @note
+ * If setting a LF clock prescaler, synchronization into the low frequency
+ * domain is required. If the same register is modified before a previous
+ * update has completed, this function will stall until the previous
+ * synchronization has completed. Please refer to CMU_FreezeEnable() for
+ * a suggestion on how to reduce stalling time in some use cases.
+ *
+ * @param[in] clock
+ * Clock point to set divisor/prescaler for. Notice that not all clock points
+ * have a divisor/prescaler, please refer to CMU overview in the reference
+ * manual.
+ *
+ * @param[in] div
+ * The clock divisor to use (<= cmuClkDiv_512).
+ ******************************************************************************/
+void CMU_ClockDivSet(CMU_Clock_TypeDef clock, CMU_ClkDiv_TypeDef div)
+{
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ CMU_ClockPrescSet(clock, (CMU_ClkPresc_TypeDef)(div - 1));
+
+#elif defined(_SILICON_LABS_32B_SERIES_0)
+ uint32_t freq;
+ uint32_t divReg;
+
+ /* Get divisor reg id */
+ divReg = (clock >> CMU_DIV_REG_POS) & CMU_DIV_REG_MASK;
+
+ switch (divReg) {
+#if defined(_CMU_CTRL_HFCLKDIV_MASK)
+ case CMU_HFCLKDIV_REG:
+ EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_8));
+
+ /* Configure worst case wait states for flash access before setting divisor */
+ flashWaitStateMax();
+
+ /* Set divider */
+ CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFCLKDIV_MASK)
+ | ((div - 1) << _CMU_CTRL_HFCLKDIV_SHIFT);
+
+ /* Update CMSIS core clock variable */
+ /* (The function will update the global variable) */
+ freq = SystemCoreClockGet();
+
+ /* Optimize flash access wait state setting for current core clk */
+ CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
+ break;
+#endif
+
+ case CMU_HFPERCLKDIV_REG:
+ EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512));
+ /* Convert to correct scale */
+ div = CMU_DivToLog2(div);
+ CMU->HFPERCLKDIV = (CMU->HFPERCLKDIV & ~_CMU_HFPERCLKDIV_HFPERCLKDIV_MASK)
+ | (div << _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT);
+ break;
+
+ case CMU_HFCORECLKDIV_REG:
+ EFM_ASSERT((div >= cmuClkDiv_1) && (div <= cmuClkDiv_512));
+
+ /* Configure worst case wait states for flash access before setting divisor */
+ flashWaitStateMax();
+
+#if defined(CMU_MAX_FREQ_HFLE)
+ setHfLeConfig(SystemHFClockGet() / div);
+#endif
+
+ /* Convert to correct scale */
+ div = CMU_DivToLog2(div);
+
+ CMU->HFCORECLKDIV = (CMU->HFCORECLKDIV
+ & ~_CMU_HFCORECLKDIV_HFCORECLKDIV_MASK)
+ | (div << _CMU_HFCORECLKDIV_HFCORECLKDIV_SHIFT);
+
+ /* Update CMSIS core clock variable */
+ /* (The function will update the global variable) */
+ freq = SystemCoreClockGet();
+
+ /* Optimize wait state setting for current core clk */
+ CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
+ break;
+
+ case CMU_LFAPRESC0_REG:
+ switch (clock) {
+ case cmuClock_RTC:
+ EFM_ASSERT(div <= cmuClkDiv_32768);
+
+ /* LF register about to be modified require sync. busy check */
+ syncReg(CMU_SYNCBUSY_LFAPRESC0);
+
+ /* Convert to correct scale */
+ div = CMU_DivToLog2(div);
+
+ CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTC_MASK)
+ | (div << _CMU_LFAPRESC0_RTC_SHIFT);
+ break;
+
+#if defined(_CMU_LFAPRESC0_LETIMER0_MASK)
+ case cmuClock_LETIMER0:
+ EFM_ASSERT(div <= cmuClkDiv_32768);
+
+ /* LF register about to be modified require sync. busy check */
+ syncReg(CMU_SYNCBUSY_LFAPRESC0);
+
+ /* Convert to correct scale */
+ div = CMU_DivToLog2(div);
+
+ CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER0_MASK)
+ | (div << _CMU_LFAPRESC0_LETIMER0_SHIFT);
+ break;
+#endif
+
+#if defined(LCD_PRESENT)
+ case cmuClock_LCDpre:
+ EFM_ASSERT((div >= cmuClkDiv_16) && (div <= cmuClkDiv_128));
+
+ /* LF register about to be modified require sync. busy check */
+ syncReg(CMU_SYNCBUSY_LFAPRESC0);
+
+ /* Convert to correct scale */
+ div = CMU_DivToLog2(div);
+
+ CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LCD_MASK)
+ | ((div - CMU_DivToLog2(cmuClkDiv_16))
+ << _CMU_LFAPRESC0_LCD_SHIFT);
+ break;
+#endif /* defined(LCD_PRESENT) */
+
+#if defined(LESENSE_PRESENT)
+ case cmuClock_LESENSE:
+ EFM_ASSERT(div <= cmuClkDiv_8);
+
+ /* LF register about to be modified require sync. busy check */
+ syncReg(CMU_SYNCBUSY_LFAPRESC0);
+
+ /* Convert to correct scale */
+ div = CMU_DivToLog2(div);
+
+ CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LESENSE_MASK)
+ | (div << _CMU_LFAPRESC0_LESENSE_SHIFT);
+ break;
+#endif /* defined(LESENSE_PRESENT) */
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+ break;
+
+ case CMU_LFBPRESC0_REG:
+ switch (clock) {
+#if defined(_CMU_LFBPRESC0_LEUART0_MASK)
+ case cmuClock_LEUART0:
+ EFM_ASSERT(div <= cmuClkDiv_8);
+
+ /* LF register about to be modified require sync. busy check */
+ syncReg(CMU_SYNCBUSY_LFBPRESC0);
+
+ /* Convert to correct scale */
+ div = CMU_DivToLog2(div);
+
+ CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART0_MASK)
+ | (((uint32_t)div) << _CMU_LFBPRESC0_LEUART0_SHIFT);
+ break;
+#endif
+
+#if defined(_CMU_LFBPRESC0_LEUART1_MASK)
+ case cmuClock_LEUART1:
+ EFM_ASSERT(div <= cmuClkDiv_8);
+
+ /* LF register about to be modified require sync. busy check */
+ syncReg(CMU_SYNCBUSY_LFBPRESC0);
+
+ /* Convert to correct scale */
+ div = CMU_DivToLog2(div);
+
+ CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART1_MASK)
+ | (((uint32_t)div) << _CMU_LFBPRESC0_LEUART1_SHIFT);
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Enable/disable a clock.
+ *
+ * @details
+ * In general, module clocking is disabled after a reset. If a module
+ * clock is disabled, the registers of that module are not accessible and
+ * reading from such registers may return undefined values. Writing to
+ * registers of clock disabled modules have no effect. One should normally
+ * avoid accessing module registers of a module with a disabled clock.
+ *
+ * @note
+ * If enabling/disabling a LF clock, synchronization into the low frequency
+ * domain is required. If the same register is modified before a previous
+ * update has completed, this function will stall until the previous
+ * synchronization has completed. Please refer to CMU_FreezeEnable() for
+ * a suggestion on how to reduce stalling time in some use cases.
+ *
+ * @param[in] clock
+ * The clock to enable/disable. Notice that not all defined clock
+ * points have separate enable/disable control, please refer to CMU overview
+ * in reference manual.
+ *
+ * @param[in] enable
+ * @li true - enable specified clock.
+ * @li false - disable specified clock.
+ ******************************************************************************/
+void CMU_ClockEnable(CMU_Clock_TypeDef clock, bool enable)
+{
+ volatile uint32_t *reg;
+ uint32_t bit;
+ uint32_t sync = 0;
+
+ /* Identify enable register */
+ switch ((clock >> CMU_EN_REG_POS) & CMU_EN_REG_MASK) {
+#if defined(_CMU_CTRL_HFPERCLKEN_MASK)
+ case CMU_CTRL_EN_REG:
+ reg = &CMU->CTRL;
+ break;
+#endif
+
+#if defined(_CMU_HFCORECLKEN0_MASK)
+ case CMU_HFCORECLKEN0_EN_REG:
+ reg = &CMU->HFCORECLKEN0;
+#if defined(CMU_MAX_FREQ_HFLE)
+ setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
+#endif
+ break;
+#endif
+
+#if defined(_CMU_HFBUSCLKEN0_MASK)
+ case CMU_HFBUSCLKEN0_EN_REG:
+ reg = &CMU->HFBUSCLKEN0;
+ break;
+#endif
+
+#if defined(_CMU_HFPERCLKDIV_MASK)
+ case CMU_HFPERCLKDIV_EN_REG:
+ reg = &CMU->HFPERCLKDIV;
+ break;
+#endif
+
+ case CMU_HFPERCLKEN0_EN_REG:
+ reg = &CMU->HFPERCLKEN0;
+ break;
+
+#if defined(_CMU_HFPERCLKEN1_MASK)
+ case CMU_HFPERCLKEN1_EN_REG:
+ reg = &CMU->HFPERCLKEN1;
+ break;
+#endif
+
+ case CMU_LFACLKEN0_EN_REG:
+ reg = &CMU->LFACLKEN0;
+ sync = CMU_SYNCBUSY_LFACLKEN0;
+ break;
+
+ case CMU_LFBCLKEN0_EN_REG:
+ reg = &CMU->LFBCLKEN0;
+ sync = CMU_SYNCBUSY_LFBCLKEN0;
+ break;
+
+#if defined(_CMU_LFCCLKEN0_MASK)
+ case CMU_LFCCLKEN0_EN_REG:
+ reg = &CMU->LFCCLKEN0;
+ sync = CMU_SYNCBUSY_LFCCLKEN0;
+ break;
+#endif
+
+#if defined(_CMU_LFECLKEN0_MASK)
+ case CMU_LFECLKEN0_EN_REG:
+ reg = &CMU->LFECLKEN0;
+ sync = CMU_SYNCBUSY_LFECLKEN0;
+ break;
+#endif
+
+#if defined(_CMU_SDIOCTRL_MASK)
+ case CMU_SDIOREF_EN_REG:
+ reg = &CMU->SDIOCTRL;
+ enable = !enable;
+ break;
+#endif
+
+#if defined(_CMU_QSPICTRL_MASK)
+ case CMU_QSPI0REF_EN_REG:
+ reg = &CMU->QSPICTRL;
+ enable = !enable;
+ break;
+#endif
+#if defined(_CMU_USBCTRL_MASK)
+ case CMU_USBRCLK_EN_REG:
+ reg = &CMU->USBCTRL;
+ break;
+#endif
+
+ case CMU_PCNT_EN_REG:
+ reg = &CMU->PCNTCTRL;
+ break;
+
+ default: /* Cannot enable/disable clock point */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Get bit position used to enable/disable */
+ bit = (clock >> CMU_EN_BIT_POS) & CMU_EN_BIT_MASK;
+
+ /* LF synchronization required? */
+ if (sync) {
+ syncReg(sync);
+ }
+
+ /* Set/clear bit as requested */
+ BUS_RegBitWrite(reg, bit, enable);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get clock frequency for a clock point.
+ *
+ * @param[in] clock
+ * Clock point to fetch frequency for.
+ *
+ * @return
+ * The current frequency in Hz.
+ ******************************************************************************/
+uint32_t CMU_ClockFreqGet(CMU_Clock_TypeDef clock)
+{
+ uint32_t ret;
+
+ switch (clock & (CMU_CLK_BRANCH_MASK << CMU_CLK_BRANCH_POS)) {
+ case (CMU_HF_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = SystemHFClockGet();
+ break;
+
+ case (CMU_HFPER_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = SystemHFClockGet();
+ /* Calculate frequency after HFPER divider. */
+#if defined(_CMU_HFPERCLKDIV_HFPERCLKDIV_MASK)
+ ret >>= (CMU->HFPERCLKDIV & _CMU_HFPERCLKDIV_HFPERCLKDIV_MASK)
+ >> _CMU_HFPERCLKDIV_HFPERCLKDIV_SHIFT;
+#endif
+#if defined(_CMU_HFPERPRESC_PRESC_MASK)
+ ret /= 1U + ((CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK)
+ >> _CMU_HFPERPRESC_PRESC_SHIFT);
+#endif
+ break;
+
+#if defined(_SILICON_LABS_32B_SERIES_1)
+#if defined(CRYPTO_PRESENT) \
+ || defined(LDMA_PRESENT) \
+ || defined(GPCRC_PRESENT) \
+ || defined(PRS_PRESENT) \
+ || defined(GPIO_PRESENT)
+ case (CMU_HFBUS_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = SystemHFClockGet();
+ break;
+#endif
+
+ case (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = SystemHFClockGet();
+ ret /= 1U + ((CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK)
+ >> _CMU_HFCOREPRESC_PRESC_SHIFT);
+ break;
+
+ case (CMU_HFEXP_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = SystemHFClockGet();
+ ret /= 1U + ((CMU->HFEXPPRESC & _CMU_HFEXPPRESC_PRESC_MASK)
+ >> _CMU_HFEXPPRESC_PRESC_SHIFT);
+ break;
+#endif
+
+#if defined(_SILICON_LABS_32B_SERIES_0)
+#if defined(AES_PRESENT) \
+ || defined(DMA_PRESENT) \
+ || defined(EBI_PRESENT) \
+ || defined(USB_PRESENT)
+ case (CMU_HFCORE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ {
+ ret = SystemCoreClockGet();
+ } break;
+#endif
+#endif
+
+ case (CMU_LFA_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFA);
+ break;
+
+#if defined(_CMU_LFACLKEN0_RTC_MASK)
+ case (CMU_RTC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFA);
+ ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK)
+ >> _CMU_LFAPRESC0_RTC_SHIFT;
+ break;
+#endif
+
+#if defined(_CMU_LFECLKEN0_RTCC_MASK)
+ case (CMU_RTCC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFE);
+ break;
+#endif
+
+#if defined(_CMU_LFACLKEN0_LETIMER0_MASK)
+ case (CMU_LETIMER0_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFA);
+#if defined(_SILICON_LABS_32B_SERIES_0)
+ ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK)
+ >> _CMU_LFAPRESC0_LETIMER0_SHIFT;
+#else
+ ret /= CMU_Log2ToDiv((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK)
+ >> _CMU_LFAPRESC0_LETIMER0_SHIFT);
+#endif
+ break;
+#endif
+
+#if defined(_CMU_LFACLKEN0_LCD_MASK)
+ case (CMU_LCDPRE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFA);
+#if defined(_SILICON_LABS_32B_SERIES_0)
+ ret >>= ((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK)
+ >> _CMU_LFAPRESC0_LCD_SHIFT)
+ + CMU_DivToLog2(cmuClkDiv_16);
+#else
+ ret /= CMU_Log2ToDiv((CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK)
+ >> _CMU_LFAPRESC0_LCD_SHIFT);
+#endif
+ break;
+
+#if defined(_CMU_LCDCTRL_MASK)
+ case (CMU_LCD_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFA);
+ ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK)
+ >> _CMU_LFAPRESC0_LCD_SHIFT;
+ ret /= 1U + ((CMU->LCDCTRL & _CMU_LCDCTRL_FDIV_MASK)
+ >> _CMU_LCDCTRL_FDIV_SHIFT);
+ break;
+#endif
+#endif
+
+#if defined(_CMU_LFACLKEN0_LESENSE_MASK)
+ case (CMU_LESENSE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFA);
+ ret >>= (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK)
+ >> _CMU_LFAPRESC0_LESENSE_SHIFT;
+ break;
+#endif
+
+ case (CMU_LFB_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFB);
+ break;
+
+#if defined(_CMU_LFBCLKEN0_LEUART0_MASK)
+ case (CMU_LEUART0_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFB);
+#if defined(_SILICON_LABS_32B_SERIES_0)
+ ret >>= (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK)
+ >> _CMU_LFBPRESC0_LEUART0_SHIFT;
+#else
+ ret /= CMU_Log2ToDiv((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK)
+ >> _CMU_LFBPRESC0_LEUART0_SHIFT);
+#endif
+ break;
+#endif
+
+#if defined(_CMU_LFBCLKEN0_LEUART1_MASK)
+ case (CMU_LEUART1_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFB);
+#if defined(_SILICON_LABS_32B_SERIES_0)
+ ret >>= (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK)
+ >> _CMU_LFBPRESC0_LEUART1_SHIFT;
+#else
+ ret /= CMU_Log2ToDiv((CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK)
+ >> _CMU_LFBPRESC0_LEUART1_SHIFT);
+#endif
+ break;
+#endif
+
+#if defined(_CMU_LFBCLKEN0_CSEN_MASK)
+ case (CMU_CSEN_LF_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFB);
+ ret /= CMU_Log2ToDiv(((CMU->LFBPRESC0 & _CMU_LFBPRESC0_CSEN_MASK)
+ >> _CMU_LFBPRESC0_CSEN_SHIFT) + 4);
+ break;
+#endif
+
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ case (CMU_LFE_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = lfClkGet(cmuClock_LFE);
+ break;
+#endif
+
+ case (CMU_DBG_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = dbgClkGet();
+ break;
+
+ case (CMU_AUX_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = auxClkGet();
+ break;
+
+#if defined(USBC_CLOCK_PRESENT)
+ case (CMU_USBC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = usbCClkGet();
+ break;
+#endif
+
+#if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK)
+ case (CMU_ADC0ASYNC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = adcAsyncClkGet(0);
+#if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK)
+ ret /= 1U + ((CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKDIV_MASK)
+ >> _CMU_ADCCTRL_ADC0CLKDIV_SHIFT);
+#endif
+ break;
+#endif
+
+#if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK)
+ case (CMU_ADC1ASYNC_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = adcAsyncClkGet(1);
+#if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK)
+ ret /= 1U + ((CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKDIV_MASK)
+ >> _CMU_ADCCTRL_ADC1CLKDIV_SHIFT);
+#endif
+ break;
+#endif
+
+#if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK)
+ case (CMU_SDIOREF_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = sdioRefClkGet();
+ break;
+#endif
+
+#if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK)
+ case (CMU_QSPI0REF_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = qspiRefClkGet(0);
+ break;
+#endif
+
+#if defined(USBR_CLOCK_PRESENT)
+ case (CMU_USBR_CLK_BRANCH << CMU_CLK_BRANCH_POS):
+ ret = usbRateClkGet();
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+#if defined(_SILICON_LABS_32B_SERIES_1)
+/***************************************************************************//**
+ * @brief
+ * Get clock prescaler.
+ *
+ * @param[in] clock
+ * Clock point to get the prescaler for. Notice that not all clock points
+ * have a prescaler. Please refer to CMU overview in reference manual.
+ *
+ * @return
+ * The prescaler value of the current clock point. 0 is returned
+ * if @p clock specifies a clock point without a prescaler.
+ ******************************************************************************/
+uint32_t CMU_ClockPrescGet(CMU_Clock_TypeDef clock)
+{
+ uint32_t prescReg;
+ uint32_t ret;
+
+ /* Get prescaler register id. */
+ prescReg = (clock >> CMU_PRESC_REG_POS) & CMU_PRESC_REG_MASK;
+
+ switch (prescReg) {
+ case CMU_HFPRESC_REG:
+ ret = (CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
+ >> _CMU_HFPRESC_PRESC_SHIFT;
+ break;
+
+ case CMU_HFEXPPRESC_REG:
+ ret = (CMU->HFEXPPRESC & _CMU_HFEXPPRESC_PRESC_MASK)
+ >> _CMU_HFEXPPRESC_PRESC_SHIFT;
+ break;
+
+ case CMU_HFCLKLEPRESC_REG:
+ ret = (CMU->HFPRESC & _CMU_HFPRESC_HFCLKLEPRESC_MASK)
+ >> _CMU_HFPRESC_HFCLKLEPRESC_SHIFT;
+ break;
+
+ case CMU_HFPERPRESC_REG:
+ ret = (CMU->HFPERPRESC & _CMU_HFPERPRESC_PRESC_MASK)
+ >> _CMU_HFPERPRESC_PRESC_SHIFT;
+ break;
+
+ case CMU_HFCOREPRESC_REG:
+ ret = (CMU->HFCOREPRESC & _CMU_HFCOREPRESC_PRESC_MASK)
+ >> _CMU_HFCOREPRESC_PRESC_SHIFT;
+ break;
+
+ case CMU_LFAPRESC0_REG:
+ switch (clock) {
+#if defined(_CMU_LFAPRESC0_LETIMER0_MASK)
+ case cmuClock_LETIMER0:
+ ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER0_MASK)
+ >> _CMU_LFAPRESC0_LETIMER0_SHIFT;
+ /* Convert the exponent to prescaler value. */
+ ret = CMU_Log2ToDiv(ret) - 1U;
+ break;
+#endif
+
+#if defined(_CMU_LFAPRESC0_LESENSE_MASK)
+ case cmuClock_LESENSE:
+ ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LESENSE_MASK)
+ >> _CMU_LFAPRESC0_LESENSE_SHIFT;
+ /* Convert the exponent to prescaler value. */
+ ret = CMU_Log2ToDiv(ret) - 1U;
+ break;
+#endif
+
+#if defined(_CMU_LFAPRESC0_LETIMER1_MASK)
+ case cmuClock_LETIMER1:
+ ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LETIMER1_MASK)
+ >> _CMU_LFAPRESC0_LETIMER1_SHIFT;
+ ret = CMU_Log2ToDiv(ret) - 1U;
+ break;
+#endif
+
+#if defined(_CMU_LFAPRESC0_LCD_MASK)
+ case cmuClock_LCD:
+ case cmuClock_LCDpre:
+ ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_LCD_MASK)
+ >> _CMU_LFAPRESC0_LCD_SHIFT;
+ ret = CMU_Log2ToDiv(ret) - 1U;
+ break;
+#endif
+
+#if defined(_CMU_LFAPRESC0_RTC_MASK)
+ case cmuClock_RTC:
+ ret = (CMU->LFAPRESC0 & _CMU_LFAPRESC0_RTC_MASK)
+ >> _CMU_LFAPRESC0_RTC_SHIFT;
+ ret = CMU_Log2ToDiv(ret) - 1U;
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0U;
+ break;
+ }
+ break;
+
+ case CMU_LFBPRESC0_REG:
+ switch (clock) {
+#if defined(_CMU_LFBPRESC0_LEUART0_MASK)
+ case cmuClock_LEUART0:
+ ret = (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART0_MASK)
+ >> _CMU_LFBPRESC0_LEUART0_SHIFT;
+ /* Convert the exponent to prescaler value. */
+ ret = CMU_Log2ToDiv(ret) - 1U;
+ break;
+#endif
+
+#if defined(_CMU_LFBPRESC0_LEUART1_MASK)
+ case cmuClock_LEUART1:
+ ret = (CMU->LFBPRESC0 & _CMU_LFBPRESC0_LEUART1_MASK)
+ >> _CMU_LFBPRESC0_LEUART1_SHIFT;
+ /* Convert the exponent to prescaler value. */
+ ret = CMU_Log2ToDiv(ret) - 1U;
+ break;
+#endif
+
+#if defined(_CMU_LFBPRESC0_CSEN_MASK)
+ case cmuClock_CSEN_LF:
+ ret = (CMU->LFBPRESC0 & _CMU_LFBPRESC0_CSEN_MASK)
+ >> _CMU_LFBPRESC0_CSEN_SHIFT;
+ /* Convert the exponent to prescaler value. */
+ ret = CMU_Log2ToDiv(ret + 4) - 1U;
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0U;
+ break;
+ }
+ break;
+
+ case CMU_LFEPRESC0_REG:
+ switch (clock) {
+#if defined(RTCC_PRESENT)
+ case cmuClock_RTCC:
+ /* No need to compute with LFEPRESC0_RTCC - DIV1 is the only */
+ /* allowed value. Convert the exponent to prescaler value. */
+ ret = _CMU_LFEPRESC0_RTCC_DIV1;
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0U;
+ break;
+#endif
+ }
+ break;
+
+ case CMU_ADCASYNCDIV_REG:
+ switch (clock) {
+#if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK)
+ case cmuClock_ADC0ASYNC:
+ ret = (CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKDIV_MASK)
+ >> _CMU_ADCCTRL_ADC0CLKDIV_SHIFT;
+ break;
+#endif
+#if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK)
+ case cmuClock_ADC1ASYNC:
+ ret = (CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKDIV_MASK)
+ >> _CMU_ADCCTRL_ADC1CLKDIV_SHIFT;
+ break;
+#endif
+ default:
+ EFM_ASSERT(0);
+ ret = 0U;
+ break;
+ }
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0U;
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+#if defined(_SILICON_LABS_32B_SERIES_1)
+/***************************************************************************//**
+ * @brief
+ * Set clock prescaler.
+ *
+ * @note
+ * If setting a LF clock prescaler, synchronization into the low frequency
+ * domain is required. If the same register is modified before a previous
+ * update has completed, this function will stall until the previous
+ * synchronization has completed. Please refer to CMU_FreezeEnable() for
+ * a suggestion on how to reduce stalling time in some use cases.
+ *
+ * @param[in] clock
+ * Clock point to set prescaler for. Notice that not all clock points
+ * have a prescaler, please refer to CMU overview in the reference manual.
+ *
+ * @param[in] presc
+ * The clock prescaler to use.
+ ******************************************************************************/
+void CMU_ClockPrescSet(CMU_Clock_TypeDef clock, CMU_ClkPresc_TypeDef presc)
+{
+ uint32_t freq;
+ uint32_t prescReg;
+
+ /* Get divisor reg id */
+ prescReg = (clock >> CMU_PRESC_REG_POS) & CMU_PRESC_REG_MASK;
+
+ switch (prescReg) {
+ case CMU_HFPRESC_REG:
+ EFM_ASSERT(presc < 32U);
+
+ /* Configure worst case wait-states for flash and HFLE. */
+ flashWaitStateMax();
+ setHfLeConfig(CMU_MAX_FREQ_HFLE + 1);
+
+ CMU->HFPRESC = (CMU->HFPRESC & ~_CMU_HFPRESC_PRESC_MASK)
+ | (presc << _CMU_HFPRESC_PRESC_SHIFT);
+
+ /* Update CMSIS core clock variable (this function updates the global variable).
+ Optimize flash and HFLE wait states. */
+ freq = SystemCoreClockGet();
+ CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
+
+ setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
+ break;
+
+ case CMU_HFEXPPRESC_REG:
+ EFM_ASSERT(presc < 32U);
+
+ CMU->HFEXPPRESC = (CMU->HFEXPPRESC & ~_CMU_HFEXPPRESC_PRESC_MASK)
+ | (presc << _CMU_HFEXPPRESC_PRESC_SHIFT);
+ break;
+
+ case CMU_HFCLKLEPRESC_REG:
+#if defined (CMU_HFPRESC_HFCLKLEPRESC_DIV8)
+ EFM_ASSERT(presc < 3U);
+#else
+ EFM_ASSERT(presc < 2U);
+#endif
+
+ /* Specifies the clock divider for HFCLKLE. This clock divider must be set
+ * high enough for the divided clock frequency to be at or below the max
+ * frequency allowed for the HFCLKLE clock. */
+ CMU->HFPRESC = (CMU->HFPRESC & ~_CMU_HFPRESC_HFCLKLEPRESC_MASK)
+ | (presc << _CMU_HFPRESC_HFCLKLEPRESC_SHIFT);
+ break;
+
+ case CMU_HFPERPRESC_REG:
+ EFM_ASSERT(presc < 512U);
+
+ CMU->HFPERPRESC = (CMU->HFPERPRESC & ~_CMU_HFPERPRESC_PRESC_MASK)
+ | (presc << _CMU_HFPERPRESC_PRESC_SHIFT);
+ break;
+
+ case CMU_HFCOREPRESC_REG:
+ EFM_ASSERT(presc < 512U);
+
+ /* Configure worst case wait-states for flash and HFLE. */
+ flashWaitStateMax();
+ setHfLeConfig(CMU_MAX_FREQ_HFLE + 1);
+
+ CMU->HFCOREPRESC = (CMU->HFCOREPRESC & ~_CMU_HFCOREPRESC_PRESC_MASK)
+ | (presc << _CMU_HFCOREPRESC_PRESC_SHIFT);
+
+ /* Update CMSIS core clock variable (this function updates the global variable).
+ Optimize flash and HFLE wait states. */
+ freq = SystemCoreClockGet();
+ CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
+ setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
+ break;
+
+ case CMU_LFAPRESC0_REG:
+ switch (clock) {
+#if defined(RTC_PRESENT)
+ case cmuClock_RTC:
+ EFM_ASSERT(presc <= 32768U);
+
+ /* Convert prescaler value to DIV exponent scale. */
+ presc = CMU_PrescToLog2(presc);
+
+ /* LF register about to be modified require sync. Busy check. */
+ syncReg(CMU_SYNCBUSY_LFAPRESC0);
+
+ CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTC_MASK)
+ | (presc << _CMU_LFAPRESC0_RTC_SHIFT);
+ break;
+#endif
+
+#if defined(RTCC_PRESENT)
+ case cmuClock_RTCC:
+#if defined(_CMU_LFEPRESC0_RTCC_MASK)
+ /* DIV1 is the only accepted value. */
+ EFM_ASSERT(presc <= 0U);
+
+ /* LF register about to be modified require sync. Busy check.. */
+ syncReg(CMU_SYNCBUSY_LFEPRESC0);
+
+ CMU->LFEPRESC0 = (CMU->LFEPRESC0 & ~_CMU_LFEPRESC0_RTCC_MASK)
+ | (presc << _CMU_LFEPRESC0_RTCC_SHIFT);
+#else
+ EFM_ASSERT(presc <= 32768U);
+
+ /* Convert prescaler value to DIV exponent scale. */
+ presc = CMU_PrescToLog2(presc);
+
+ /* LF register about to be modified require sync. Busy check. */
+ syncReg(CMU_SYNCBUSY_LFAPRESC0);
+
+ CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_RTCC_MASK)
+ | (presc << _CMU_LFAPRESC0_RTCC_SHIFT);
+#endif
+ break;
+#endif
+
+#if defined(_CMU_LFAPRESC0_LETIMER0_MASK)
+ case cmuClock_LETIMER0:
+ EFM_ASSERT(presc <= 32768U);
+
+ /* Convert prescaler value to DIV exponent scale. */
+ presc = CMU_PrescToLog2(presc);
+
+ /* LF register about to be modified require sync. Busy check. */
+ syncReg(CMU_SYNCBUSY_LFAPRESC0);
+
+ CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LETIMER0_MASK)
+ | (presc << _CMU_LFAPRESC0_LETIMER0_SHIFT);
+ break;
+#endif
+
+#if defined(_CMU_LFAPRESC0_LESENSE_MASK)
+ case cmuClock_LESENSE:
+ EFM_ASSERT(presc <= 8);
+
+ /* Convert prescaler value to DIV exponent scale. */
+ presc = CMU_PrescToLog2(presc);
+
+ /* LF register about to be modified require sync. Busy check. */
+ syncReg(CMU_SYNCBUSY_LFAPRESC0);
+
+ CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LESENSE_MASK)
+ | (presc << _CMU_LFAPRESC0_LESENSE_SHIFT);
+ break;
+#endif
+
+#if defined(_CMU_LFAPRESC0_LCD_MASK)
+ case cmuClock_LCDpre:
+ case cmuClock_LCD:
+ {
+ EFM_ASSERT(presc <= 32768U);
+
+ /* Convert prescaler value to DIV exponent scale. */
+ presc = CMU_PrescToLog2(presc);
+
+ /* LF register about to be modified require sync. Busy check. */
+ syncReg(CMU_SYNCBUSY_LFAPRESC0);
+
+ CMU->LFAPRESC0 = (CMU->LFAPRESC0 & ~_CMU_LFAPRESC0_LCD_MASK)
+ | (presc << _CMU_LFAPRESC0_LCD_SHIFT);
+ } break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+ break;
+
+ case CMU_LFBPRESC0_REG:
+ switch (clock) {
+#if defined(_CMU_LFBPRESC0_LEUART0_MASK)
+ case cmuClock_LEUART0:
+ EFM_ASSERT(presc <= 8U);
+
+ /* Convert prescaler value to DIV exponent scale. */
+ presc = CMU_PrescToLog2(presc);
+
+ /* LF register about to be modified require sync. Busy check. */
+ syncReg(CMU_SYNCBUSY_LFBPRESC0);
+
+ CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART0_MASK)
+ | (presc << _CMU_LFBPRESC0_LEUART0_SHIFT);
+ break;
+#endif
+
+#if defined(_CMU_LFBPRESC0_LEUART1_MASK)
+ case cmuClock_LEUART1:
+ EFM_ASSERT(presc <= 8U);
+
+ /* Convert prescaler value to DIV exponent scale. */
+ presc = CMU_PrescToLog2(presc);
+
+ /* LF register about to be modified require sync. Busy check. */
+ syncReg(CMU_SYNCBUSY_LFBPRESC0);
+
+ CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_LEUART1_MASK)
+ | (presc << _CMU_LFBPRESC0_LEUART1_SHIFT);
+ break;
+#endif
+
+#if defined(_CMU_LFBPRESC0_CSEN_MASK)
+ case cmuClock_CSEN_LF:
+ EFM_ASSERT((presc <= 127U) && (presc >= 15U));
+
+ /* Convert prescaler value to DIV exponent scale.
+ * DIV16 is the lowest supported prescaler. */
+ presc = CMU_PrescToLog2(presc) - 4;
+
+ /* LF register about to be modified require sync. Busy check. */
+ syncReg(CMU_SYNCBUSY_LFBPRESC0);
+
+ CMU->LFBPRESC0 = (CMU->LFBPRESC0 & ~_CMU_LFBPRESC0_CSEN_MASK)
+ | (presc << _CMU_LFBPRESC0_CSEN_SHIFT);
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+ break;
+
+ case CMU_LFEPRESC0_REG:
+ switch (clock) {
+#if defined(_CMU_LFEPRESC0_RTCC_MASK)
+ case cmuClock_RTCC:
+ EFM_ASSERT(presc <= 0U);
+
+ /* LF register about to be modified require sync. Busy check. */
+ syncReg(CMU_SYNCBUSY_LFEPRESC0);
+
+ CMU->LFEPRESC0 = (CMU->LFEPRESC0 & ~_CMU_LFEPRESC0_RTCC_MASK)
+ | (presc << _CMU_LFEPRESC0_RTCC_SHIFT);
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+ break;
+
+ case CMU_ADCASYNCDIV_REG:
+ switch (clock) {
+#if defined(_CMU_ADCCTRL_ADC0CLKDIV_MASK)
+ case cmuClock_ADC0ASYNC:
+ EFM_ASSERT(presc <= 3);
+ CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC0CLKDIV_MASK)
+ | (presc << _CMU_ADCCTRL_ADC0CLKDIV_SHIFT);
+ break;
+#endif
+
+#if defined(_CMU_ADCCTRL_ADC1CLKDIV_MASK)
+ case cmuClock_ADC1ASYNC:
+ EFM_ASSERT(presc <= 3);
+ CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC1CLKDIV_MASK)
+ | (presc << _CMU_ADCCTRL_ADC1CLKDIV_SHIFT);
+ break;
+#endif
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Get currently selected reference clock used for a clock branch.
+ *
+ * @param[in] clock
+ * Clock branch to fetch selected ref. clock for. One of:
+ * @li #cmuClock_HF
+ * @li #cmuClock_LFA
+ * @li #cmuClock_LFB @if _CMU_LFCLKSEL_LFAE_ULFRCO
+ * @li #cmuClock_LFC
+ * @endif @if _SILICON_LABS_32B_SERIES_1
+ * @li #cmuClock_LFE
+ * @endif
+ * @li #cmuClock_DBG @if DOXYDOC_USB_PRESENT
+ * @li #cmuClock_USBC
+ * @endif
+ *
+ * @return
+ * Reference clock used for clocking selected branch, #cmuSelect_Error if
+ * invalid @p clock provided.
+ ******************************************************************************/
+CMU_Select_TypeDef CMU_ClockSelectGet(CMU_Clock_TypeDef clock)
+{
+ CMU_Select_TypeDef ret = cmuSelect_Disabled;
+ uint32_t selReg;
+
+ selReg = (clock >> CMU_SEL_REG_POS) & CMU_SEL_REG_MASK;
+
+ switch (selReg) {
+ case CMU_HFCLKSEL_REG:
+#if defined(_CMU_HFCLKSTATUS_MASK)
+ switch (CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK) {
+ case CMU_HFCLKSTATUS_SELECTED_LFXO:
+ ret = cmuSelect_LFXO;
+ break;
+
+ case CMU_HFCLKSTATUS_SELECTED_LFRCO:
+ ret = cmuSelect_LFRCO;
+ break;
+
+ case CMU_HFCLKSTATUS_SELECTED_HFXO:
+ ret = cmuSelect_HFXO;
+ break;
+
+ default:
+ ret = cmuSelect_HFRCO;
+ break;
+ }
+#else
+ switch (CMU->STATUS
+ & (CMU_STATUS_HFRCOSEL
+ | CMU_STATUS_HFXOSEL
+ | CMU_STATUS_LFRCOSEL
+#if defined(CMU_STATUS_USHFRCODIV2SEL)
+ | CMU_STATUS_USHFRCODIV2SEL
+#endif
+ | CMU_STATUS_LFXOSEL)) {
+ case CMU_STATUS_LFXOSEL:
+ ret = cmuSelect_LFXO;
+ break;
+
+ case CMU_STATUS_LFRCOSEL:
+ ret = cmuSelect_LFRCO;
+ break;
+
+ case CMU_STATUS_HFXOSEL:
+ ret = cmuSelect_HFXO;
+ break;
+
+#if defined(CMU_STATUS_USHFRCODIV2SEL)
+ case CMU_STATUS_USHFRCODIV2SEL:
+ ret = cmuSelect_USHFRCODIV2;
+ break;
+#endif
+
+ default:
+ ret = cmuSelect_HFRCO;
+ break;
+ }
+#endif
+ break;
+
+#if defined(_CMU_LFCLKSEL_MASK) || defined(_CMU_LFACLKSEL_MASK)
+ case CMU_LFACLKSEL_REG:
+#if defined(_CMU_LFCLKSEL_MASK)
+ switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFA_MASK) {
+ case CMU_LFCLKSEL_LFA_LFRCO:
+ ret = cmuSelect_LFRCO;
+ break;
+
+ case CMU_LFCLKSEL_LFA_LFXO:
+ ret = cmuSelect_LFXO;
+ break;
+
+#if defined(CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2)
+ case CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2:
+ ret = cmuSelect_HFCLKLE;
+ break;
+#endif
+
+ default:
+#if defined(CMU_LFCLKSEL_LFAE)
+ if (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFAE_MASK) {
+ ret = cmuSelect_ULFRCO;
+ break;
+ }
+#else
+ ret = cmuSelect_Disabled;
+#endif
+ break;
+ }
+
+#elif defined(_CMU_LFACLKSEL_MASK)
+ switch (CMU->LFACLKSEL & _CMU_LFACLKSEL_LFA_MASK) {
+ case CMU_LFACLKSEL_LFA_LFRCO:
+ ret = cmuSelect_LFRCO;
+ break;
+
+ case CMU_LFACLKSEL_LFA_LFXO:
+ ret = cmuSelect_LFXO;
+ break;
+
+ case CMU_LFACLKSEL_LFA_ULFRCO:
+ ret = cmuSelect_ULFRCO;
+ break;
+
+#if defined(_CMU_LFACLKSEL_LFA_HFCLKLE)
+ case CMU_LFACLKSEL_LFA_HFCLKLE:
+ ret = cmuSelect_HFCLKLE;
+ break;
+#endif
+
+#if defined(CMU_LFACLKSEL_LFA_PLFRCO)
+ case CMU_LFACLKSEL_LFA_PLFRCO:
+ ret = cmuSelect_PLFRCO;
+ break;
+#endif
+
+ default:
+ ret = cmuSelect_Disabled;
+ break;
+ }
+#endif
+ break;
+#endif /* _CMU_LFCLKSEL_MASK || _CMU_LFACLKSEL_MASK */
+
+#if defined(_CMU_LFCLKSEL_MASK) || defined(_CMU_LFBCLKSEL_MASK)
+ case CMU_LFBCLKSEL_REG:
+#if defined(_CMU_LFCLKSEL_MASK)
+ switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFB_MASK) {
+ case CMU_LFCLKSEL_LFB_LFRCO:
+ ret = cmuSelect_LFRCO;
+ break;
+
+ case CMU_LFCLKSEL_LFB_LFXO:
+ ret = cmuSelect_LFXO;
+ break;
+
+#if defined(CMU_LFCLKSEL_LFB_HFCORECLKLEDIV2)
+ case CMU_LFCLKSEL_LFB_HFCORECLKLEDIV2:
+ ret = cmuSelect_HFCLKLE;
+ break;
+#endif
+
+#if defined(CMU_LFCLKSEL_LFB_HFCLKLE)
+ case CMU_LFCLKSEL_LFB_HFCLKLE:
+ ret = cmuSelect_HFCLKLE;
+ break;
+#endif
+
+ default:
+#if defined(CMU_LFCLKSEL_LFBE)
+ if (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFBE_MASK) {
+ ret = cmuSelect_ULFRCO;
+ break;
+ }
+#else
+ ret = cmuSelect_Disabled;
+#endif
+ break;
+ }
+
+#elif defined(_CMU_LFBCLKSEL_MASK)
+ switch (CMU->LFBCLKSEL & _CMU_LFBCLKSEL_LFB_MASK) {
+ case CMU_LFBCLKSEL_LFB_LFRCO:
+ ret = cmuSelect_LFRCO;
+ break;
+
+ case CMU_LFBCLKSEL_LFB_LFXO:
+ ret = cmuSelect_LFXO;
+ break;
+
+ case CMU_LFBCLKSEL_LFB_ULFRCO:
+ ret = cmuSelect_ULFRCO;
+ break;
+
+ case CMU_LFBCLKSEL_LFB_HFCLKLE:
+ ret = cmuSelect_HFCLKLE;
+ break;
+
+#if defined(CMU_LFBCLKSEL_LFB_PLFRCO)
+ case CMU_LFBCLKSEL_LFB_PLFRCO:
+ ret = cmuSelect_PLFRCO;
+ break;
+#endif
+
+ default:
+ ret = cmuSelect_Disabled;
+ break;
+ }
+#endif
+ break;
+#endif /* _CMU_LFCLKSEL_MASK || _CMU_LFBCLKSEL_MASK */
+
+#if defined(_CMU_LFCLKSEL_LFC_MASK)
+ case CMU_LFCCLKSEL_REG:
+ switch (CMU->LFCLKSEL & _CMU_LFCLKSEL_LFC_MASK) {
+ case CMU_LFCLKSEL_LFC_LFRCO:
+ ret = cmuSelect_LFRCO;
+ break;
+
+ case CMU_LFCLKSEL_LFC_LFXO:
+ ret = cmuSelect_LFXO;
+ break;
+
+ default:
+ ret = cmuSelect_Disabled;
+ break;
+ }
+ break;
+#endif
+
+#if defined(_CMU_LFECLKSEL_LFE_MASK)
+ case CMU_LFECLKSEL_REG:
+ switch (CMU->LFECLKSEL & _CMU_LFECLKSEL_LFE_MASK) {
+ case CMU_LFECLKSEL_LFE_LFRCO:
+ ret = cmuSelect_LFRCO;
+ break;
+
+ case CMU_LFECLKSEL_LFE_LFXO:
+ ret = cmuSelect_LFXO;
+ break;
+
+ case CMU_LFECLKSEL_LFE_ULFRCO:
+ ret = cmuSelect_ULFRCO;
+ break;
+
+#if defined (_CMU_LFECLKSEL_LFE_HFCLKLE)
+ case CMU_LFECLKSEL_LFE_HFCLKLE:
+ ret = cmuSelect_HFCLKLE;
+ break;
+#endif
+
+#if defined(CMU_LFECLKSEL_LFE_PLFRCO)
+ case CMU_LFECLKSEL_LFE_PLFRCO:
+ ret = cmuSelect_PLFRCO;
+ break;
+#endif
+
+ default:
+ ret = cmuSelect_Disabled;
+ break;
+ }
+ break;
+#endif /* CMU_LFECLKSEL_REG */
+
+ case CMU_DBGCLKSEL_REG:
+#if defined(_CMU_DBGCLKSEL_DBG_MASK)
+ switch (CMU->DBGCLKSEL & _CMU_DBGCLKSEL_DBG_MASK) {
+ case CMU_DBGCLKSEL_DBG_HFCLK:
+ ret = cmuSelect_HFCLK;
+ break;
+
+ case CMU_DBGCLKSEL_DBG_AUXHFRCO:
+ ret = cmuSelect_AUXHFRCO;
+ break;
+ }
+
+#elif defined(_CMU_CTRL_DBGCLK_MASK)
+ switch (CMU->CTRL & _CMU_CTRL_DBGCLK_MASK) {
+ case CMU_CTRL_DBGCLK_AUXHFRCO:
+ ret = cmuSelect_AUXHFRCO;
+ break;
+
+ case CMU_CTRL_DBGCLK_HFCLK:
+ ret = cmuSelect_HFCLK;
+ break;
+ }
+#else
+ ret = cmuSelect_AUXHFRCO;
+#endif
+ break;
+
+#if defined(USBC_CLOCK_PRESENT)
+ case CMU_USBCCLKSEL_REG:
+ switch (CMU->STATUS
+ & (CMU_STATUS_USBCLFXOSEL
+#if defined(_CMU_STATUS_USBCHFCLKSEL_MASK)
+ | CMU_STATUS_USBCHFCLKSEL
+#endif
+#if defined(_CMU_STATUS_USBCUSHFRCOSEL_MASK)
+ | CMU_STATUS_USBCUSHFRCOSEL
+#endif
+ | CMU_STATUS_USBCLFRCOSEL)) {
+#if defined(_CMU_STATUS_USBCHFCLKSEL_MASK)
+ case CMU_STATUS_USBCHFCLKSEL:
+ ret = cmuSelect_HFCLK;
+ break;
+#endif
+
+#if defined(_CMU_STATUS_USBCUSHFRCOSEL_MASK)
+ case CMU_STATUS_USBCUSHFRCOSEL:
+ ret = cmuSelect_USHFRCO;
+ break;
+#endif
+
+ case CMU_STATUS_USBCLFXOSEL:
+ ret = cmuSelect_LFXO;
+ break;
+
+ case CMU_STATUS_USBCLFRCOSEL:
+ ret = cmuSelect_LFRCO;
+ break;
+
+ default:
+ ret = cmuSelect_Disabled;
+ break;
+ }
+ break;
+#endif
+
+#if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK)
+ case CMU_ADC0ASYNCSEL_REG:
+ switch (CMU->ADCCTRL & _CMU_ADCCTRL_ADC0CLKSEL_MASK) {
+ case CMU_ADCCTRL_ADC0CLKSEL_DISABLED:
+ ret = cmuSelect_Disabled;
+ break;
+
+ case CMU_ADCCTRL_ADC0CLKSEL_AUXHFRCO:
+ ret = cmuSelect_AUXHFRCO;
+ break;
+
+ case CMU_ADCCTRL_ADC0CLKSEL_HFXO:
+ ret = cmuSelect_HFXO;
+ break;
+
+ case CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK:
+ ret = cmuSelect_HFSRCCLK;
+ break;
+ }
+ break;
+#endif
+
+#if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK)
+ case CMU_ADC1ASYNCSEL_REG:
+ switch (CMU->ADCCTRL & _CMU_ADCCTRL_ADC1CLKSEL_MASK) {
+ case CMU_ADCCTRL_ADC1CLKSEL_DISABLED:
+ ret = cmuSelect_Disabled;
+ break;
+
+ case CMU_ADCCTRL_ADC1CLKSEL_AUXHFRCO:
+ ret = cmuSelect_AUXHFRCO;
+ break;
+
+ case CMU_ADCCTRL_ADC1CLKSEL_HFXO:
+ ret = cmuSelect_HFXO;
+ break;
+
+ case CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK:
+ ret = cmuSelect_HFSRCCLK;
+ break;
+ }
+ break;
+#endif
+
+#if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK)
+ case CMU_SDIOREFSEL_REG:
+ switch (CMU->SDIOCTRL & _CMU_SDIOCTRL_SDIOCLKSEL_MASK) {
+ case CMU_SDIOCTRL_SDIOCLKSEL_HFRCO:
+ ret = cmuSelect_HFRCO;
+ break;
+
+ case CMU_SDIOCTRL_SDIOCLKSEL_HFXO:
+ ret = cmuSelect_HFXO;
+ break;
+
+ case CMU_SDIOCTRL_SDIOCLKSEL_AUXHFRCO:
+ ret = cmuSelect_AUXHFRCO;
+ break;
+
+ case CMU_SDIOCTRL_SDIOCLKSEL_USHFRCO:
+ ret = cmuSelect_USHFRCO;
+ break;
+ }
+ break;
+#endif
+
+#if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK)
+ case CMU_QSPI0REFSEL_REG:
+ switch (CMU->QSPICTRL & _CMU_QSPICTRL_QSPI0CLKSEL_MASK) {
+ case CMU_QSPICTRL_QSPI0CLKSEL_HFRCO:
+ ret = cmuSelect_HFRCO;
+ break;
+
+ case CMU_QSPICTRL_QSPI0CLKSEL_HFXO:
+ ret = cmuSelect_HFXO;
+ break;
+
+ case CMU_QSPICTRL_QSPI0CLKSEL_AUXHFRCO:
+ ret = cmuSelect_AUXHFRCO;
+ break;
+
+ case CMU_QSPICTRL_QSPI0CLKSEL_USHFRCO:
+ ret = cmuSelect_USHFRCO;
+ break;
+ }
+ break;
+#endif
+
+#if defined(_CMU_USBCTRL_USBCLKSEL_MASK)
+ case CMU_USBRCLKSEL_REG:
+ switch (CMU->USBCTRL & _CMU_USBCTRL_USBCLKSEL_MASK) {
+ case CMU_USBCTRL_USBCLKSEL_USHFRCO:
+ ret = cmuSelect_USHFRCO;
+ break;
+
+ case CMU_USBCTRL_USBCLKSEL_HFXO:
+ ret = cmuSelect_HFXO;
+ break;
+
+ case CMU_USBCTRL_USBCLKSEL_HFXOX2:
+ ret = cmuSelect_HFXOX2;
+ break;
+
+ case CMU_USBCTRL_USBCLKSEL_HFRCO:
+ ret = cmuSelect_HFRCO;
+ break;
+
+ case CMU_USBCTRL_USBCLKSEL_LFXO:
+ ret = cmuSelect_LFXO;
+ break;
+
+ case CMU_USBCTRL_USBCLKSEL_LFRCO:
+ ret = cmuSelect_LFRCO;
+ break;
+ }
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ ret = cmuSelect_Error;
+ break;
+ }
+
+ return ret;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Select reference clock/oscillator used for a clock branch.
+ *
+ * @details
+ * Notice that if a selected reference is not enabled prior to selecting its
+ * use, it will be enabled, and this function will wait for the selected
+ * oscillator to be stable. It will however NOT be disabled if another
+ * reference clock is selected later.
+ *
+ * This feature is particularly important if selecting a new reference
+ * clock for the clock branch clocking the core, otherwise the system
+ * may halt.
+ *
+ * @param[in] clock
+ * Clock branch to select reference clock for. One of:
+ * @li #cmuClock_HF
+ * @li #cmuClock_LFA
+ * @li #cmuClock_LFB
+ * @if _CMU_LFCCLKEN0_MASK
+ * @li #cmuClock_LFC
+ * @endif
+ * @if _CMU_LFECLKEN0_MASK
+ * @li #cmuClock_LFE
+ * @endif
+ * @li #cmuClock_DBG
+ * @if _CMU_CMD_USBCLKSEL_MASK
+ * @li #cmuClock_USBC
+ * @endif
+ * @if _CMU_USBCTRL_MASK
+ * @li #cmuClock_USBR
+ * @endif
+ *
+ * @param[in] ref
+ * Reference selected for clocking, please refer to reference manual for
+ * for details on which reference is available for a specific clock branch.
+ * @li #cmuSelect_HFRCO
+ * @li #cmuSelect_LFRCO
+ * @li #cmuSelect_HFXO
+ * @if _CMU_HFXOCTRL_HFXOX2EN_MASK
+ * @li #cmuSelect_HFXOX2
+ * @endif
+ * @li #cmuSelect_LFXO
+ * @li #cmuSelect_HFCLKLE
+ * @li #cmuSelect_AUXHFRCO
+ * @if _CMU_USHFRCOCTRL_MASK
+ * @li #cmuSelect_USHFRCO
+ * @endif
+ * @li #cmuSelect_HFCLK
+ * @ifnot DOXYDOC_EFM32_GECKO_FAMILY
+ * @li #cmuSelect_ULFRCO
+ * @li #cmuSelect_PLFRCO
+ * @endif
+ ******************************************************************************/
+void CMU_ClockSelectSet(CMU_Clock_TypeDef clock, CMU_Select_TypeDef ref)
+{
+ uint32_t select = cmuOsc_HFRCO;
+ CMU_Osc_TypeDef osc = cmuOsc_HFRCO;
+ uint32_t freq;
+ uint32_t tmp;
+ uint32_t selRegId;
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ volatile uint32_t *selReg = NULL;
+#endif
+#if defined(CMU_LFCLKSEL_LFAE_ULFRCO)
+ uint32_t lfExtended = 0;
+#endif
+
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+ uint32_t vScaleFrequency = 0; /* Use default */
+
+ /* Start voltage upscaling before clock is set. */
+ if (clock == cmuClock_HF) {
+ if (ref == cmuSelect_HFXO) {
+ vScaleFrequency = SystemHFXOClockGet();
+ } else if ((ref == cmuSelect_HFRCO)
+ && (CMU_HFRCOBandGet() > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) {
+ vScaleFrequency = CMU_HFRCOBandGet();
+ }
+ if (vScaleFrequency != 0) {
+ EMU_VScaleEM01ByClock(vScaleFrequency, false);
+ }
+ }
+#endif
+
+ selRegId = (clock >> CMU_SEL_REG_POS) & CMU_SEL_REG_MASK;
+
+ switch (selRegId) {
+ case CMU_HFCLKSEL_REG:
+ switch (ref) {
+ case cmuSelect_LFXO:
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ select = CMU_HFCLKSEL_HF_LFXO;
+#elif defined(_SILICON_LABS_32B_SERIES_0)
+ select = CMU_CMD_HFCLKSEL_LFXO;
+#endif
+ osc = cmuOsc_LFXO;
+ break;
+
+ case cmuSelect_LFRCO:
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ select = CMU_HFCLKSEL_HF_LFRCO;
+#elif defined(_SILICON_LABS_32B_SERIES_0)
+ select = CMU_CMD_HFCLKSEL_LFRCO;
+#endif
+ osc = cmuOsc_LFRCO;
+ break;
+
+ case cmuSelect_HFXO:
+#if defined(CMU_HFCLKSEL_HF_HFXO)
+ select = CMU_HFCLKSEL_HF_HFXO;
+#elif defined(CMU_CMD_HFCLKSEL_HFXO)
+ select = CMU_CMD_HFCLKSEL_HFXO;
+#endif
+ osc = cmuOsc_HFXO;
+#if defined(CMU_MAX_FREQ_HFLE)
+ /* Set 1 HFLE wait-state until the new HFCLKLE frequency is known.
+ This is known after 'select' is written below. */
+ setHfLeConfig(CMU_MAX_FREQ_HFLE + 1);
+#endif
+#if defined(CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ)
+ /* Adjust HFXO buffer current for frequencies above 32MHz */
+ if (SystemHFXOClockGet() > 32000000) {
+ CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFXOBUFCUR_MASK)
+ | CMU_CTRL_HFXOBUFCUR_BOOSTABOVE32MHZ;
+ } else {
+ CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_HFXOBUFCUR_MASK)
+ | CMU_CTRL_HFXOBUFCUR_BOOSTUPTO32MHZ;
+ }
+#endif
+ break;
+
+ case cmuSelect_HFRCO:
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ select = CMU_HFCLKSEL_HF_HFRCO;
+#elif defined(_SILICON_LABS_32B_SERIES_0)
+ select = CMU_CMD_HFCLKSEL_HFRCO;
+#endif
+ osc = cmuOsc_HFRCO;
+#if defined(CMU_MAX_FREQ_HFLE)
+ /* Set 1 HFLE wait-state until the new HFCLKLE frequency is known.
+ This is known after 'select' is written below. */
+ setHfLeConfig(CMU_MAX_FREQ_HFLE + 1);
+#endif
+ break;
+
+#if defined(CMU_CMD_HFCLKSEL_USHFRCODIV2)
+ case cmuSelect_USHFRCODIV2:
+ select = CMU_CMD_HFCLKSEL_USHFRCODIV2;
+ osc = cmuOsc_USHFRCO;
+ break;
+#endif
+
+#if defined(CMU_LFCLKSEL_LFAE_ULFRCO) || defined(CMU_LFACLKSEL_LFA_ULFRCO)
+ case cmuSelect_ULFRCO:
+ /* ULFRCO cannot be used as HFCLK */
+ EFM_ASSERT(0);
+ return;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(osc, true, true);
+
+ /* Configure worst case wait states for flash access before selecting */
+ flashWaitStateMax();
+
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+ /* Wait for voltage upscaling to complete before clock is set. */
+ if (vScaleFrequency != 0) {
+ EMU_VScaleWait();
+ }
+#endif
+
+ /* Switch to selected oscillator */
+#if defined(_CMU_HFCLKSEL_MASK)
+ CMU->HFCLKSEL = select;
+#else
+ CMU->CMD = select;
+#endif
+#if defined(CMU_MAX_FREQ_HFLE)
+ /* Update HFLE configuration after 'select' is set.
+ Note that the HFCLKLE clock is connected differently on planform 1 and 2 */
+ setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
+#endif
+
+ /* Update CMSIS core clock variable */
+ /* (The function will update the global variable) */
+ freq = SystemCoreClockGet();
+
+ /* Optimize flash access wait state setting for currently selected core clk */
+ CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
+
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+ /* Keep EMU module informed on source HF clock frequency. This will apply voltage
+ downscaling after clock is set if downscaling is configured. */
+ if (vScaleFrequency == 0) {
+ EMU_VScaleEM01ByClock(0, true);
+ }
+#endif
+ break;
+
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ case CMU_LFACLKSEL_REG:
+ selReg = (selReg == NULL) ? &CMU->LFACLKSEL : selReg;
+#if !defined(_CMU_LFACLKSEL_LFA_HFCLKLE)
+ /* HFCLKCLE can not be used as LFACLK */
+ EFM_ASSERT(ref != cmuSelect_HFCLKLE);
+#endif
+ /* Fall through and select clock source */
+
+#if defined(_CMU_LFCCLKSEL_MASK)
+ case CMU_LFCCLKSEL_REG:
+ selReg = (selReg == NULL) ? &CMU->LFCCLKSEL : selReg;
+#if !defined(_CMU_LFCCLKSEL_LFC_HFCLKLE)
+ /* HFCLKCLE can not be used as LFCCLK */
+ EFM_ASSERT(ref != cmuSelect_HFCLKLE);
+#endif
+#endif
+ /* Fall through and select clock source */
+
+ case CMU_LFECLKSEL_REG:
+ selReg = (selReg == NULL) ? &CMU->LFECLKSEL : selReg;
+#if !defined(_CMU_LFECLKSEL_LFE_HFCLKLE)
+ /* HFCLKCLE can not be used as LFECLK */
+ EFM_ASSERT(ref != cmuSelect_HFCLKLE);
+#endif
+ /* Fall through and select clock source */
+
+ case CMU_LFBCLKSEL_REG:
+ selReg = (selReg == NULL) ? &CMU->LFBCLKSEL : selReg;
+ switch (ref) {
+ case cmuSelect_Disabled:
+ tmp = _CMU_LFACLKSEL_LFA_DISABLED;
+ break;
+
+ case cmuSelect_LFXO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
+ tmp = _CMU_LFACLKSEL_LFA_LFXO;
+ break;
+
+ case cmuSelect_LFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
+ tmp = _CMU_LFACLKSEL_LFA_LFRCO;
+ break;
+
+ case cmuSelect_HFCLKLE:
+ /* Ensure correct HFLE wait-states and enable HFCLK to LE */
+ setHfLeConfig(SystemCoreClockGet());
+ BUS_RegBitWrite(&CMU->HFBUSCLKEN0, _CMU_HFBUSCLKEN0_LE_SHIFT, 1);
+ tmp = _CMU_LFBCLKSEL_LFB_HFCLKLE;
+ break;
+
+ case cmuSelect_ULFRCO:
+ /* ULFRCO is always on, there is no need to enable it. */
+ tmp = _CMU_LFACLKSEL_LFA_ULFRCO;
+ break;
+
+#if defined(_CMU_STATUS_PLFRCOENS_MASK)
+ case cmuSelect_PLFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_PLFRCO, true, true);
+ tmp = _CMU_LFACLKSEL_LFA_PLFRCO;
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ return;
+ }
+ *selReg = tmp;
+ break;
+
+#elif defined(_SILICON_LABS_32B_SERIES_0)
+ case CMU_LFACLKSEL_REG:
+ case CMU_LFBCLKSEL_REG:
+ switch (ref) {
+ case cmuSelect_Disabled:
+ tmp = _CMU_LFCLKSEL_LFA_DISABLED;
+ break;
+
+ case cmuSelect_LFXO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
+ tmp = _CMU_LFCLKSEL_LFA_LFXO;
+ break;
+
+ case cmuSelect_LFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
+ tmp = _CMU_LFCLKSEL_LFA_LFRCO;
+ break;
+
+ case cmuSelect_HFCLKLE:
+#if defined(CMU_MAX_FREQ_HFLE)
+ /* Set HFLE wait-state and divider */
+ freq = SystemCoreClockGet();
+ setHfLeConfig(freq);
+#endif
+ /* Ensure HFCORE to LE clocking is enabled */
+ BUS_RegBitWrite(&CMU->HFCORECLKEN0, _CMU_HFCORECLKEN0_LE_SHIFT, 1);
+ tmp = _CMU_LFCLKSEL_LFA_HFCORECLKLEDIV2;
+ break;
+
+#if defined(CMU_LFCLKSEL_LFAE_ULFRCO)
+ case cmuSelect_ULFRCO:
+ /* ULFRCO is always enabled */
+ tmp = _CMU_LFCLKSEL_LFA_DISABLED;
+ lfExtended = 1;
+ break;
+#endif
+
+ default:
+ /* Illegal clock source for LFA/LFB selected */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Apply select */
+ if (selRegId == CMU_LFACLKSEL_REG) {
+#if defined(_CMU_LFCLKSEL_LFAE_MASK)
+ CMU->LFCLKSEL = (CMU->LFCLKSEL
+ & ~(_CMU_LFCLKSEL_LFA_MASK | _CMU_LFCLKSEL_LFAE_MASK))
+ | (tmp << _CMU_LFCLKSEL_LFA_SHIFT)
+ | (lfExtended << _CMU_LFCLKSEL_LFAE_SHIFT);
+#else
+ CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFA_MASK)
+ | (tmp << _CMU_LFCLKSEL_LFA_SHIFT);
+#endif
+ } else {
+#if defined(_CMU_LFCLKSEL_LFBE_MASK)
+ CMU->LFCLKSEL = (CMU->LFCLKSEL
+ & ~(_CMU_LFCLKSEL_LFB_MASK | _CMU_LFCLKSEL_LFBE_MASK))
+ | (tmp << _CMU_LFCLKSEL_LFB_SHIFT)
+ | (lfExtended << _CMU_LFCLKSEL_LFBE_SHIFT);
+#else
+ CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFB_MASK)
+ | (tmp << _CMU_LFCLKSEL_LFB_SHIFT);
+#endif
+ }
+ break;
+
+#if defined(_CMU_LFCLKSEL_LFC_MASK)
+ case CMU_LFCCLKSEL_REG:
+ switch (ref) {
+ case cmuSelect_Disabled:
+ tmp = _CMU_LFCLKSEL_LFA_DISABLED;
+ break;
+
+ case cmuSelect_LFXO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
+ tmp = _CMU_LFCLKSEL_LFC_LFXO;
+ break;
+
+ case cmuSelect_LFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
+ tmp = _CMU_LFCLKSEL_LFC_LFRCO;
+ break;
+
+ default:
+ /* Illegal clock source for LFC selected */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Apply select */
+ CMU->LFCLKSEL = (CMU->LFCLKSEL & ~_CMU_LFCLKSEL_LFC_MASK)
+ | (tmp << _CMU_LFCLKSEL_LFC_SHIFT);
+ break;
+#endif
+#endif
+
+#if defined(_CMU_DBGCLKSEL_DBG_MASK) || defined(CMU_CTRL_DBGCLK)
+ case CMU_DBGCLKSEL_REG:
+ switch (ref) {
+#if defined(_CMU_DBGCLKSEL_DBG_MASK)
+ case cmuSelect_AUXHFRCO:
+ /* Select AUXHFRCO as debug clock */
+ CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_AUXHFRCO;
+ break;
+
+ case cmuSelect_HFCLK:
+ /* Select divided HFCLK as debug clock */
+ CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_HFCLK;
+ break;
+#endif
+
+#if defined(CMU_CTRL_DBGCLK)
+ case cmuSelect_AUXHFRCO:
+ /* Select AUXHFRCO as debug clock */
+ CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK))
+ | CMU_CTRL_DBGCLK_AUXHFRCO;
+ break;
+
+ case cmuSelect_HFCLK:
+ /* Select divided HFCLK as debug clock */
+ CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_DBGCLK_MASK))
+ | CMU_CTRL_DBGCLK_HFCLK;
+ break;
+#endif
+
+ default:
+ /* Illegal clock source for debug selected */
+ EFM_ASSERT(0);
+ return;
+ }
+ break;
+#endif
+
+#if defined(USBC_CLOCK_PRESENT)
+ case CMU_USBCCLKSEL_REG:
+ switch (ref) {
+ case cmuSelect_LFXO:
+ /* Select LFXO as clock source for USB, can only be used in sleep mode */
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
+
+ /* Switch oscillator */
+ CMU->CMD = CMU_CMD_USBCCLKSEL_LFXO;
+
+ /* Wait until clock is activated */
+ while ((CMU->STATUS & CMU_STATUS_USBCLFXOSEL) == 0) {
+ }
+ break;
+
+ case cmuSelect_LFRCO:
+ /* Select LFRCO as clock source for USB, can only be used in sleep mode */
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
+
+ /* Switch oscillator */
+ CMU->CMD = CMU_CMD_USBCCLKSEL_LFRCO;
+
+ /* Wait until clock is activated */
+ while ((CMU->STATUS & CMU_STATUS_USBCLFRCOSEL) == 0) {
+ }
+ break;
+
+#if defined(CMU_STATUS_USBCHFCLKSEL)
+ case cmuSelect_HFCLK:
+ /* Select undivided HFCLK as clock source for USB */
+ /* Oscillator must already be enabled to avoid a core lockup */
+ CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV;
+ /* Wait until clock is activated */
+ while ((CMU->STATUS & CMU_STATUS_USBCHFCLKSEL) == 0) {
+ }
+ break;
+#endif
+
+#if defined(CMU_CMD_USBCCLKSEL_USHFRCO)
+ case cmuSelect_USHFRCO:
+ /* Select USHFRCO as clock source for USB */
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
+
+ /* Switch oscillator */
+ CMU->CMD = CMU_CMD_USBCCLKSEL_USHFRCO;
+
+ /* Wait until clock is activated */
+ while ((CMU->STATUS & CMU_STATUS_USBCUSHFRCOSEL) == 0) {
+ }
+ break;
+#endif
+
+ default:
+ /* Illegal clock source for USB */
+ EFM_ASSERT(0);
+ return;
+ }
+ break;
+#endif
+
+#if defined(_CMU_ADCCTRL_ADC0CLKSEL_MASK)
+ case CMU_ADC0ASYNCSEL_REG:
+ switch (ref) {
+ case cmuSelect_Disabled:
+ tmp = _CMU_ADCCTRL_ADC0CLKSEL_DISABLED;
+ break;
+
+ case cmuSelect_AUXHFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true);
+ tmp = _CMU_ADCCTRL_ADC0CLKSEL_AUXHFRCO;
+ break;
+
+ case cmuSelect_HFXO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
+ tmp = _CMU_ADCCTRL_ADC0CLKSEL_HFXO;
+ break;
+
+ case cmuSelect_HFSRCCLK:
+ tmp = _CMU_ADCCTRL_ADC0CLKSEL_HFSRCCLK;
+ break;
+
+ default:
+ /* Illegal clock source for ADC0ASYNC selected */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Apply select */
+ CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC0CLKSEL_MASK)
+ | (tmp << _CMU_ADCCTRL_ADC0CLKSEL_SHIFT);
+ break;
+#endif
+
+#if defined(_CMU_ADCCTRL_ADC1CLKSEL_MASK)
+ case CMU_ADC1ASYNCSEL_REG:
+ switch (ref) {
+ case cmuSelect_Disabled:
+ tmp = _CMU_ADCCTRL_ADC1CLKSEL_DISABLED;
+ break;
+
+ case cmuSelect_AUXHFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true);
+ tmp = _CMU_ADCCTRL_ADC1CLKSEL_AUXHFRCO;
+ break;
+
+ case cmuSelect_HFXO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
+ tmp = _CMU_ADCCTRL_ADC1CLKSEL_HFXO;
+ break;
+
+ case cmuSelect_HFSRCCLK:
+ tmp = _CMU_ADCCTRL_ADC1CLKSEL_HFSRCCLK;
+ break;
+
+ default:
+ /* Illegal clock source for ADC1ASYNC selected */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Apply select */
+ CMU->ADCCTRL = (CMU->ADCCTRL & ~_CMU_ADCCTRL_ADC1CLKSEL_MASK)
+ | (tmp << _CMU_ADCCTRL_ADC1CLKSEL_SHIFT);
+ break;
+#endif
+
+#if defined(_CMU_SDIOCTRL_SDIOCLKSEL_MASK)
+ case CMU_SDIOREFSEL_REG:
+ switch (ref) {
+ case cmuSelect_HFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_HFRCO, true, true);
+ tmp = _CMU_SDIOCTRL_SDIOCLKSEL_HFRCO;
+ break;
+
+ case cmuSelect_HFXO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
+ tmp = _CMU_SDIOCTRL_SDIOCLKSEL_HFXO;
+ break;
+
+ case cmuSelect_AUXHFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true);
+ tmp = _CMU_SDIOCTRL_SDIOCLKSEL_AUXHFRCO;
+ break;
+
+ case cmuSelect_USHFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
+ tmp = _CMU_SDIOCTRL_SDIOCLKSEL_USHFRCO;
+ break;
+
+ default:
+ /* Illegal clock source for SDIOREF selected */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Apply select */
+ CMU->SDIOCTRL = (CMU->SDIOCTRL & ~_CMU_SDIOCTRL_SDIOCLKSEL_MASK)
+ | (tmp << _CMU_SDIOCTRL_SDIOCLKSEL_SHIFT);
+ break;
+#endif
+
+#if defined(_CMU_QSPICTRL_QSPI0CLKSEL_MASK)
+ case CMU_QSPI0REFSEL_REG:
+ switch (ref) {
+ case cmuSelect_HFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_HFRCO, true, true);
+ tmp = _CMU_QSPICTRL_QSPI0CLKSEL_HFRCO;
+ break;
+
+ case cmuSelect_HFXO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
+ tmp = _CMU_QSPICTRL_QSPI0CLKSEL_HFXO;
+ break;
+
+ case cmuSelect_AUXHFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true);
+ tmp = _CMU_QSPICTRL_QSPI0CLKSEL_AUXHFRCO;
+ break;
+
+ case cmuSelect_USHFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
+ tmp = _CMU_QSPICTRL_QSPI0CLKSEL_USHFRCO;
+ break;
+
+ default:
+ /* Illegal clock source for QSPI0REF selected */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Apply select */
+ CMU->QSPICTRL = (CMU->QSPICTRL & ~_CMU_QSPICTRL_QSPI0CLKSEL_MASK)
+ | (tmp << _CMU_QSPICTRL_QSPI0CLKSEL_SHIFT);
+ break;
+#endif
+
+#if defined(_CMU_USBCTRL_USBCLKSEL_MASK)
+ case CMU_USBRCLKSEL_REG:
+ switch (ref) {
+ case cmuSelect_USHFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_USHFRCO, true, true);
+ tmp = _CMU_USBCTRL_USBCLKSEL_USHFRCO;
+ break;
+
+ case cmuSelect_HFXO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
+ tmp = _CMU_USBCTRL_USBCLKSEL_HFXO;
+ break;
+
+ case cmuSelect_HFXOX2:
+ /* Only allowed for HFXO frequencies up to 25 MHz */
+ EFM_ASSERT(SystemHFXOClockGet() <= 25000000u);
+
+ /* Enable HFXO X2 */
+ CMU->HFXOCTRL |= CMU_HFXOCTRL_HFXOX2EN;
+
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
+
+ tmp = _CMU_USBCTRL_USBCLKSEL_HFXOX2;
+ break;
+
+ case cmuSelect_HFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_HFRCO, true, true);
+ tmp = _CMU_USBCTRL_USBCLKSEL_HFRCO;
+ break;
+
+ case cmuSelect_LFXO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
+ tmp = _CMU_USBCTRL_USBCLKSEL_LFXO;
+ break;
+
+ case cmuSelect_LFRCO:
+ /* Ensure selected oscillator is enabled, waiting for it to stabilize */
+ CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
+ tmp = _CMU_USBCTRL_USBCLKSEL_LFRCO;
+ break;
+
+ default:
+ /* Illegal clock source for USBR selected */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Apply select */
+ CMU->USBCTRL = (CMU->USBCTRL & ~_CMU_USBCTRL_USBCLKSEL_MASK)
+ | (tmp << _CMU_USBCTRL_USBCLKSEL_SHIFT);
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+}
+
+#if defined(CMU_OSCENCMD_DPLLEN)
+/**************************************************************************//**
+ * @brief
+ * Lock the DPLL to a given frequency.
+ *
+ * The frequency is given by: Fout = Fref * (N+1) / (M+1).
+ *
+ * @note
+ * This function does not check if the given N & M values will actually
+ * produce the desired target frequency.
+ * Any peripheral running off HFRCO should be switched to HFRCODIV2 prior to
+ * calling this function to avoid over-clocking.
+ *
+ * @param[in] init
+ * DPLL setup parameters.
+ *
+ * @return
+ * Returns false on invalid target frequency or DPLL locking error.
+ *****************************************************************************/
+bool CMU_DPLLLock(CMU_DPLLInit_TypeDef *init)
+{
+ int index = 0;
+ unsigned int i;
+ bool hfrcoDiv2 = false;
+ uint32_t hfrcoCtrlVal, lockStatus, sysFreq;
+
+ EFM_ASSERT(init->frequency >= hfrcoCtrlTable[0].minFreq);
+ EFM_ASSERT(init->frequency
+ <= hfrcoCtrlTable[HFRCOCTRLTABLE_ENTRIES - 1].maxFreq);
+ EFM_ASSERT(init->n >= 32);
+ EFM_ASSERT(init->n <= (_CMU_DPLLCTRL1_N_MASK >> _CMU_DPLLCTRL1_N_SHIFT));
+ EFM_ASSERT(init->m <= (_CMU_DPLLCTRL1_M_MASK >> _CMU_DPLLCTRL1_M_SHIFT));
+ EFM_ASSERT(init->ssInterval <= (_CMU_HFRCOSS_SSINV_MASK
+ >> _CMU_HFRCOSS_SSINV_SHIFT));
+ EFM_ASSERT(init->ssAmplitude <= (_CMU_HFRCOSS_SSAMP_MASK
+ >> _CMU_HFRCOSS_SSAMP_SHIFT));
+
+#if defined(_EMU_STATUS_VSCALE_MASK)
+ if ((EMU_VScaleGet() == emuVScaleEM01_LowPower)
+ && (init->frequency > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) {
+ EFM_ASSERT(false);
+ return false;
+ }
+#endif
+
+ // Find correct HFRCO band, and retrieve a HFRCOCTRL value.
+ for (i = 0; i < HFRCOCTRLTABLE_ENTRIES; i++) {
+ if ((init->frequency >= hfrcoCtrlTable[i].minFreq)
+ && (init->frequency <= hfrcoCtrlTable[i].maxFreq)) {
+ index = i; // Correct band found
+ break;
+ }
+ }
+ if (index == HFRCOCTRLTABLE_ENTRIES) {
+ EFM_ASSERT(false);
+ return false; // Target frequency out of spec.
+ }
+ hfrcoCtrlVal = hfrcoCtrlTable[index].value;
+
+ // Check if we have a calibrated HFRCOCTRL.TUNING value in device DI page.
+ if (hfrcoCtrlTable[index].band != (CMU_HFRCOFreq_TypeDef)0) {
+ uint32_t tuning;
+
+ tuning = (CMU_HFRCODevinfoGet(hfrcoCtrlTable[index].band)
+ & _CMU_HFRCOCTRL_TUNING_MASK)
+ >> _CMU_HFRCOCTRL_TUNING_SHIFT;
+
+ // When HFRCOCTRL.FINETUNINGEN is enabled, the center frequency
+ // of the band shifts down by 5.8%. We subtract 9 to compensate.
+ if (tuning > 9) {
+ tuning -= 9;
+ } else {
+ tuning = 0;
+ }
+
+ hfrcoCtrlVal |= tuning << _CMU_HFRCOCTRL_TUNING_SHIFT;
+ }
+
+ // Update CMSIS frequency SystemHfrcoFreq value.
+ SystemHfrcoFreq = init->frequency;
+
+ // Set max wait-states while changing core clock.
+ if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
+ flashWaitStateMax();
+ }
+
+ // Update HFLE configuration before updating HFRCO, use new DPLL frequency.
+ if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
+ setHfLeConfig(init->frequency);
+
+ // Switch to HFRCO/2 before setting DPLL to avoid over-clocking.
+ hfrcoDiv2 = (CMU->HFCLKSTATUS & _CMU_HFCLKSTATUS_SELECTED_MASK)
+ == CMU_HFCLKSTATUS_SELECTED_HFRCODIV2;
+ CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFRCODIV2;
+ }
+
+ CMU->OSCENCMD = CMU_OSCENCMD_DPLLDIS;
+ while ((CMU->STATUS & (CMU_STATUS_DPLLENS | CMU_STATUS_DPLLRDY)) != 0) ;
+ CMU->IFC = CMU_IFC_DPLLRDY | CMU_IFC_DPLLLOCKFAILLOW
+ | CMU_IFC_DPLLLOCKFAILHIGH;
+ CMU->DPLLCTRL1 = (init->n << _CMU_DPLLCTRL1_N_SHIFT)
+ | (init->m << _CMU_DPLLCTRL1_M_SHIFT);
+ CMU->HFRCOCTRL = hfrcoCtrlVal;
+ CMU->DPLLCTRL = (init->refClk << _CMU_DPLLCTRL_REFSEL_SHIFT)
+ | (init->autoRecover << _CMU_DPLLCTRL_AUTORECOVER_SHIFT)
+ | (init->edgeSel << _CMU_DPLLCTRL_EDGESEL_SHIFT)
+ | (init->lockMode << _CMU_DPLLCTRL_MODE_SHIFT);
+ CMU->OSCENCMD = CMU_OSCENCMD_DPLLEN;
+ while ((lockStatus = (CMU->IF & (CMU_IF_DPLLRDY
+ | CMU_IF_DPLLLOCKFAILLOW
+ | CMU_IF_DPLLLOCKFAILHIGH))) == 0) ;
+
+ if ((CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO)
+ && (hfrcoDiv2 == false)) {
+ CMU->HFCLKSEL = CMU_HFCLKSEL_HF_HFRCO;
+ }
+
+ // If HFRCO is selected as HF clock, optimize flash access wait-state
+ // configuration for this frequency and update CMSIS core clock variable.
+ if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
+ // Call SystemCoreClockGet() to update CMSIS core clock variable.
+ sysFreq = SystemCoreClockGet();
+ EFM_ASSERT(sysFreq <= init->frequency);
+ EFM_ASSERT(sysFreq <= SystemHfrcoFreq);
+ EFM_ASSERT(init->frequency == SystemHfrcoFreq);
+ CMU_UpdateWaitStates(sysFreq, VSCALE_DEFAULT);
+ }
+
+ // Reduce HFLE frequency if possible.
+ setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
+
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+ // Update voltage scaling.
+ EMU_VScaleEM01ByClock(0, true);
+#endif
+
+ if (lockStatus == CMU_IF_DPLLRDY) {
+ return true;
+ }
+ return false;
+}
+#endif // CMU_OSCENCMD_DPLLEN
+
+/**************************************************************************//**
+ * @brief
+ * CMU low frequency register synchronization freeze control.
+ *
+ * @details
+ * Some CMU registers requires synchronization into the low frequency (LF)
+ * domain. The freeze feature allows for several such registers to be
+ * modified before passing them to the LF domain simultaneously (which
+ * takes place when the freeze mode is disabled).
+ *
+ * Another usage scenario of this feature, is when using an API (such
+ * as the CMU API) for modifying several bit fields consecutively in the
+ * same register. If freeze mode is enabled during this sequence, stalling
+ * can be avoided.
+ *
+ * @note
+ * When enabling freeze mode, this function will wait for all current
+ * ongoing CMU synchronization to LF domain to complete (Normally
+ * synchronization will not be in progress.) However for this reason, when
+ * using freeze mode, modifications of registers requiring LF synchronization
+ * should be done within one freeze enable/disable block to avoid unecessary
+ * stalling.
+ *
+ * @param[in] enable
+ * @li true - enable freeze, modified registers are not propagated to the
+ * LF domain
+ * @li false - disable freeze, modified registers are propagated to LF
+ * domain
+ *****************************************************************************/
+void CMU_FreezeEnable(bool enable)
+{
+ if (enable) {
+ /* Wait for any ongoing LF synchronization to complete. This is just to */
+ /* protect against the rare case when a user */
+ /* - modifies a register requiring LF sync */
+ /* - then enables freeze before LF sync completed */
+ /* - then modifies the same register again */
+ /* since modifying a register while it is in sync progress should be */
+ /* avoided. */
+ while (CMU->SYNCBUSY) {
+ }
+
+ CMU->FREEZE = CMU_FREEZE_REGFREEZE;
+ } else {
+ CMU->FREEZE = 0;
+ }
+}
+
+#if defined(_CMU_HFRCOCTRL_BAND_MASK)
+/***************************************************************************//**
+ * @brief
+ * Get HFRCO band in use.
+ *
+ * @return
+ * HFRCO band in use.
+ ******************************************************************************/
+CMU_HFRCOBand_TypeDef CMU_HFRCOBandGet(void)
+{
+ return (CMU_HFRCOBand_TypeDef)((CMU->HFRCOCTRL & _CMU_HFRCOCTRL_BAND_MASK)
+ >> _CMU_HFRCOCTRL_BAND_SHIFT);
+}
+#endif /* _CMU_HFRCOCTRL_BAND_MASK */
+
+#if defined(_CMU_HFRCOCTRL_BAND_MASK)
+/***************************************************************************//**
+ * @brief
+ * Set HFRCO band and the tuning value based on the value in the calibration
+ * table made during production.
+ *
+ * @param[in] band
+ * HFRCO band to activate.
+ ******************************************************************************/
+void CMU_HFRCOBandSet(CMU_HFRCOBand_TypeDef band)
+{
+ uint32_t tuning;
+ uint32_t freq;
+ CMU_Select_TypeDef osc;
+
+ /* Read tuning value from calibration table */
+ switch (band) {
+ case cmuHFRCOBand_1MHz:
+ tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND1_MASK)
+ >> _DEVINFO_HFRCOCAL0_BAND1_SHIFT;
+ break;
+
+ case cmuHFRCOBand_7MHz:
+ tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND7_MASK)
+ >> _DEVINFO_HFRCOCAL0_BAND7_SHIFT;
+ break;
+
+ case cmuHFRCOBand_11MHz:
+ tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND11_MASK)
+ >> _DEVINFO_HFRCOCAL0_BAND11_SHIFT;
+ break;
+
+ case cmuHFRCOBand_14MHz:
+ tuning = (DEVINFO->HFRCOCAL0 & _DEVINFO_HFRCOCAL0_BAND14_MASK)
+ >> _DEVINFO_HFRCOCAL0_BAND14_SHIFT;
+ break;
+
+ case cmuHFRCOBand_21MHz:
+ tuning = (DEVINFO->HFRCOCAL1 & _DEVINFO_HFRCOCAL1_BAND21_MASK)
+ >> _DEVINFO_HFRCOCAL1_BAND21_SHIFT;
+ break;
+
+#if defined(_CMU_HFRCOCTRL_BAND_28MHZ)
+ case cmuHFRCOBand_28MHz:
+ tuning = (DEVINFO->HFRCOCAL1 & _DEVINFO_HFRCOCAL1_BAND28_MASK)
+ >> _DEVINFO_HFRCOCAL1_BAND28_SHIFT;
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* If HFRCO is used for core clock, we have to consider flash access WS. */
+ osc = CMU_ClockSelectGet(cmuClock_HF);
+ if (osc == cmuSelect_HFRCO) {
+ /* Configure worst case wait states for flash access before setting divider */
+ flashWaitStateMax();
+ }
+
+ /* Set band/tuning */
+ CMU->HFRCOCTRL = (CMU->HFRCOCTRL
+ & ~(_CMU_HFRCOCTRL_BAND_MASK | _CMU_HFRCOCTRL_TUNING_MASK))
+ | (band << _CMU_HFRCOCTRL_BAND_SHIFT)
+ | (tuning << _CMU_HFRCOCTRL_TUNING_SHIFT);
+
+ /* If HFRCO is used for core clock, optimize flash WS */
+ if (osc == cmuSelect_HFRCO) {
+ /* Call SystemCoreClockGet() to update CMSIS core clock variable. */
+ freq = SystemCoreClockGet();
+ CMU_UpdateWaitStates(freq, VSCALE_DEFAULT);
+ }
+
+#if defined(CMU_MAX_FREQ_HFLE)
+ /* Reduce HFLE frequency if possible. */
+ setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
+#endif
+}
+#endif /* _CMU_HFRCOCTRL_BAND_MASK */
+
+#if defined(_CMU_HFRCOCTRL_FREQRANGE_MASK)
+/**************************************************************************//**
+ * @brief
+ * Get the HFRCO frequency calibration word in DEVINFO
+ *
+ * @param[in] freq
+ * Frequency in Hz
+ *
+ * @return
+ * HFRCO calibration word for a given frequency
+ *****************************************************************************/
+static uint32_t CMU_HFRCODevinfoGet(CMU_HFRCOFreq_TypeDef freq)
+{
+ switch (freq) {
+ /* 1, 2 and 4MHz share the same calibration word */
+ case cmuHFRCOFreq_1M0Hz:
+ case cmuHFRCOFreq_2M0Hz:
+ case cmuHFRCOFreq_4M0Hz:
+ return DEVINFO->HFRCOCAL0;
+
+ case cmuHFRCOFreq_7M0Hz:
+ return DEVINFO->HFRCOCAL3;
+
+ case cmuHFRCOFreq_13M0Hz:
+ return DEVINFO->HFRCOCAL6;
+
+ case cmuHFRCOFreq_16M0Hz:
+ return DEVINFO->HFRCOCAL7;
+
+ case cmuHFRCOFreq_19M0Hz:
+ return DEVINFO->HFRCOCAL8;
+
+ case cmuHFRCOFreq_26M0Hz:
+ return DEVINFO->HFRCOCAL10;
+
+ case cmuHFRCOFreq_32M0Hz:
+ return DEVINFO->HFRCOCAL11;
+
+ case cmuHFRCOFreq_38M0Hz:
+ return DEVINFO->HFRCOCAL12;
+
+#if defined(_DEVINFO_HFRCOCAL16_MASK)
+ case cmuHFRCOFreq_48M0Hz:
+ return DEVINFO->HFRCOCAL13;
+
+ case cmuHFRCOFreq_56M0Hz:
+ return DEVINFO->HFRCOCAL14;
+
+ case cmuHFRCOFreq_64M0Hz:
+ return DEVINFO->HFRCOCAL15;
+
+ case cmuHFRCOFreq_72M0Hz:
+ return DEVINFO->HFRCOCAL16;
+#endif
+
+ default: /* cmuHFRCOFreq_UserDefined */
+ return 0;
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get current HFRCO frequency.
+ *
+ * @return
+ * HFRCO frequency
+ ******************************************************************************/
+CMU_HFRCOFreq_TypeDef CMU_HFRCOBandGet(void)
+{
+ return (CMU_HFRCOFreq_TypeDef)SystemHfrcoFreq;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set HFRCO calibration for the selected target frequency.
+ *
+ * @param[in] setFreq
+ * HFRCO frequency to set
+ ******************************************************************************/
+void CMU_HFRCOBandSet(CMU_HFRCOFreq_TypeDef setFreq)
+{
+ uint32_t freqCal;
+ uint32_t sysFreq;
+ uint32_t prevFreq;
+
+ /* Get DEVINFO index, set CMSIS frequency SystemHfrcoFreq */
+ freqCal = CMU_HFRCODevinfoGet(setFreq);
+ EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX));
+ prevFreq = SystemHfrcoFreq;
+ SystemHfrcoFreq = (uint32_t)setFreq;
+
+ /* Set max wait-states while changing core clock */
+ if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
+ flashWaitStateMax();
+ }
+
+ /* Wait for any previous sync to complete, and then set calibration data
+ for the selected frequency. */
+ while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_HFRCOBSY_SHIFT)) ;
+
+ /* Check for valid calibration data */
+ EFM_ASSERT(freqCal != UINT_MAX);
+
+ /* Set divider in HFRCOCTRL for 1, 2 and 4MHz */
+ switch (setFreq) {
+ case cmuHFRCOFreq_1M0Hz:
+ freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK)
+ | CMU_HFRCOCTRL_CLKDIV_DIV4;
+ break;
+
+ case cmuHFRCOFreq_2M0Hz:
+ freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK)
+ | CMU_HFRCOCTRL_CLKDIV_DIV2;
+ break;
+
+ case cmuHFRCOFreq_4M0Hz:
+ freqCal = (freqCal & ~_CMU_HFRCOCTRL_CLKDIV_MASK)
+ | CMU_HFRCOCTRL_CLKDIV_DIV1;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Update HFLE configuration before updating HFRCO.
+ Use the new set frequency. */
+ if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
+ /* setFreq is worst-case as dividers may reduce the HFLE frequency. */
+ setHfLeConfig(setFreq);
+ }
+
+ if (setFreq > prevFreq) {
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+ /* When increasing frequency we need to voltage scale before the change */
+ EMU_VScaleEM01ByClock(setFreq, true);
+#endif
+ }
+
+ CMU->HFRCOCTRL = freqCal;
+
+ /* If HFRCO is selected as HF clock, optimize flash access wait-state configuration
+ for this frequency and update CMSIS core clock variable. */
+ if (CMU_ClockSelectGet(cmuClock_HF) == cmuSelect_HFRCO) {
+ /* Call SystemCoreClockGet() to update CMSIS core clock variable. */
+ sysFreq = SystemCoreClockGet();
+ EFM_ASSERT(sysFreq <= (uint32_t)setFreq);
+ EFM_ASSERT(sysFreq <= SystemHfrcoFreq);
+ EFM_ASSERT(setFreq == SystemHfrcoFreq);
+ CMU_UpdateWaitStates(sysFreq, VSCALE_DEFAULT);
+ }
+
+ /* Reduce HFLE frequency if possible. */
+ setHfLeConfig(CMU_ClockFreqGet(cmuClock_HFLE));
+
+ if (setFreq <= prevFreq) {
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+ /* When decreasing frequency we need to voltage scale after the change */
+ EMU_VScaleEM01ByClock(0, true);
+#endif
+ }
+}
+#endif /* _CMU_HFRCOCTRL_FREQRANGE_MASK */
+
+#if defined(_CMU_HFRCOCTRL_SUDELAY_MASK)
+/***************************************************************************//**
+ * @brief
+ * Get the HFRCO startup delay.
+ *
+ * @details
+ * Please refer to the reference manual for further details.
+ *
+ * @return
+ * The startup delay in use.
+ ******************************************************************************/
+uint32_t CMU_HFRCOStartupDelayGet(void)
+{
+ return (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_SUDELAY_MASK)
+ >> _CMU_HFRCOCTRL_SUDELAY_SHIFT;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set the HFRCO startup delay.
+ *
+ * @details
+ * Please refer to the reference manual for further details.
+ *
+ * @param[in] delay
+ * The startup delay to set (<= 31).
+ ******************************************************************************/
+void CMU_HFRCOStartupDelaySet(uint32_t delay)
+{
+ EFM_ASSERT(delay <= 31);
+
+ delay &= _CMU_HFRCOCTRL_SUDELAY_MASK >> _CMU_HFRCOCTRL_SUDELAY_SHIFT;
+ CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_SUDELAY_MASK))
+ | (delay << _CMU_HFRCOCTRL_SUDELAY_SHIFT);
+}
+#endif
+
+#if defined(_CMU_USHFRCOCTRL_FREQRANGE_MASK)
+/**************************************************************************//**
+ * @brief
+ * Get the USHFRCO frequency calibration word in DEVINFO
+ *
+ * @param[in] freq
+ * Frequency in Hz
+ *
+ * @return
+ * USHFRCO calibration word for a given frequency
+ *****************************************************************************/
+static uint32_t CMU_USHFRCODevinfoGet(CMU_USHFRCOFreq_TypeDef freq)
+{
+ switch (freq) {
+ case cmuUSHFRCOFreq_16M0Hz:
+ return DEVINFO->USHFRCOCAL7;
+
+ case cmuUSHFRCOFreq_32M0Hz:
+ return DEVINFO->USHFRCOCAL11;
+
+ case cmuUSHFRCOFreq_48M0Hz:
+ return DEVINFO->USHFRCOCAL13;
+
+ case cmuUSHFRCOFreq_50M0Hz:
+ return DEVINFO->USHFRCOCAL14;
+
+ default: /* cmuUSHFRCOFreq_UserDefined */
+ return 0;
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get current USHFRCO frequency.
+ *
+ * @return
+ * HFRCO frequency
+ ******************************************************************************/
+CMU_USHFRCOFreq_TypeDef CMU_USHFRCOBandGet(void)
+{
+ return (CMU_USHFRCOFreq_TypeDef) ushfrcoFreq;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set USHFRCO calibration for the selected target frequency.
+ *
+ * @param[in] setFreq
+ * USHFRCO frequency to set
+ ******************************************************************************/
+void CMU_USHFRCOBandSet(CMU_USHFRCOFreq_TypeDef setFreq)
+{
+ uint32_t freqCal;
+
+ /* Get DEVINFO calibration values */
+ freqCal = CMU_USHFRCODevinfoGet(setFreq);
+ EFM_ASSERT((freqCal != 0) && (freqCal != UINT_MAX));
+ ushfrcoFreq = (uint32_t)setFreq;
+
+ /* Wait for any previous sync to complete, and then set calibration data
+ for the selected frequency. */
+ while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_USHFRCOBSY_SHIFT)) ;
+
+ CMU->USHFRCOCTRL = freqCal;
+}
+#endif /* _CMU_USHFRCOCTRL_FREQRANGE_MASK */
+
+#if defined(_CMU_HFXOCTRL_AUTOSTARTEM0EM1_MASK)
+/***************************************************************************//**
+ * @brief
+ * Enable or disable HFXO autostart
+ *
+ * @param[in] userSel
+ * Additional user specified enable bit.
+ *
+ * @param[in] enEM0EM1Start
+ * If true, HFXO is automatically started upon entering EM0/EM1 entry from
+ * EM2/EM3. HFXO selection has to be handled by the user.
+ * If false, HFXO is not started automatically when entering EM0/EM1.
+ *
+ * @param[in] enEM0EM1StartSel
+ * If true, HFXO is automatically started and immediately selected upon
+ * entering EM0/EM1 entry from EM2/EM3. Note that this option stalls the use of
+ * HFSRCCLK until HFXO becomes ready.
+ * If false, HFXO is not started or selected automatically when entering
+ * EM0/EM1.
+ ******************************************************************************/
+void CMU_HFXOAutostartEnable(uint32_t userSel,
+ bool enEM0EM1Start,
+ bool enEM0EM1StartSel)
+{
+ uint32_t hfxoFreq;
+ uint32_t hfxoCtrl;
+
+ /* Mask supported enable bits. */
+#if defined(_CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK)
+ userSel &= _CMU_HFXOCTRL_AUTOSTARTRDYSELRAC_MASK;
+#else
+ userSel = 0;
+#endif
+
+ hfxoCtrl = CMU->HFXOCTRL & ~(userSel
+ | _CMU_HFXOCTRL_AUTOSTARTEM0EM1_MASK
+ | _CMU_HFXOCTRL_AUTOSTARTSELEM0EM1_MASK);
+
+ hfxoCtrl |= userSel
+ | (enEM0EM1Start ? CMU_HFXOCTRL_AUTOSTARTEM0EM1 : 0)
+ | (enEM0EM1StartSel ? CMU_HFXOCTRL_AUTOSTARTSELEM0EM1 : 0);
+
+ /* Set wait-states for HFXO if automatic start and select is configured. */
+ if (userSel || enEM0EM1StartSel) {
+ hfxoFreq = SystemHFXOClockGet();
+ CMU_UpdateWaitStates(hfxoFreq, VSCALE_DEFAULT);
+ setHfLeConfig(hfxoFreq);
+ }
+
+ /* Update HFXOCTRL after wait-states are updated as HF may automatically switch
+ to HFXO when automatic select is enabled . */
+ CMU->HFXOCTRL = hfxoCtrl;
+}
+#endif
+
+/**************************************************************************//**
+ * @brief
+ * Set HFXO control registers
+ *
+ * @note
+ * HFXO configuration should be obtained from a configuration tool,
+ * app note or xtal datasheet. This function disables the HFXO to ensure
+ * a valid state before update.
+ *
+ * @param[in] hfxoInit
+ * HFXO setup parameters
+ *****************************************************************************/
+void CMU_HFXOInit(const CMU_HFXOInit_TypeDef *hfxoInit)
+{
+ /* Do not disable HFXO if it is currently selected as HF/Core clock */
+ EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_HFXO);
+
+ /* HFXO must be disabled before reconfiguration */
+ CMU_OscillatorEnable(cmuOsc_HFXO, false, true);
+
+#if defined(_SILICON_LABS_32B_SERIES_1) && (_SILICON_LABS_GECKO_INTERNAL_SDID >= 100)
+ uint32_t tmp = CMU_HFXOCTRL_MODE_XTAL;
+
+ switch (hfxoInit->mode) {
+ case cmuOscMode_Crystal:
+ tmp = CMU_HFXOCTRL_MODE_XTAL;
+ break;
+ case cmuOscMode_External:
+ tmp = CMU_HFXOCTRL_MODE_DIGEXTCLK;
+ break;
+ case cmuOscMode_AcCoupled:
+ tmp = CMU_HFXOCTRL_MODE_ACBUFEXTCLK;
+ break;
+ default:
+ EFM_ASSERT(false); /* Unsupported configuration */
+ }
+
+ /* HFXO Doubler can only be enabled on crystals up to max 25 MHz */
+ if (SystemHFXOClockGet() <= 25000000) {
+ tmp |= CMU_HFXOCTRL_HFXOX2EN;
+ }
+
+ CMU->HFXOCTRL = (CMU->HFXOCTRL & ~(_CMU_HFXOCTRL_MODE_MASK
+ | _CMU_HFXOCTRL_HFXOX2EN_MASK))
+ | tmp;
+
+ /* Set tuning for startup and steady state */
+ CMU->HFXOSTARTUPCTRL = (hfxoInit->ctuneStartup << _CMU_HFXOSTARTUPCTRL_CTUNE_SHIFT)
+ | (hfxoInit->xoCoreBiasTrimStartup << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_SHIFT);
+
+ CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL & ~(_CMU_HFXOSTEADYSTATECTRL_CTUNE_MASK
+ | _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK))
+ | (hfxoInit->ctuneSteadyState << _CMU_HFXOSTEADYSTATECTRL_CTUNE_SHIFT)
+ | (hfxoInit->xoCoreBiasTrimSteadyState << _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_SHIFT);
+
+ /* Set timeouts */
+ CMU->HFXOTIMEOUTCTRL = (hfxoInit->timeoutPeakDetect << _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_SHIFT)
+ | (hfxoInit->timeoutSteady << _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_SHIFT)
+ | (hfxoInit->timeoutStartup << _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_SHIFT);
+
+#elif defined(_CMU_HFXOCTRL_MASK)
+ /* Verify that the deprecated autostart fields are not used,
+ * @ref CMU_HFXOAutostartEnable must be used instead. */
+ EFM_ASSERT(!(hfxoInit->autoStartEm01
+ || hfxoInit->autoSelEm01
+ || hfxoInit->autoStartSelOnRacWakeup));
+
+ uint32_t tmp = CMU_HFXOCTRL_MODE_XTAL;
+
+ /* AC coupled external clock not supported */
+ EFM_ASSERT(hfxoInit->mode != cmuOscMode_AcCoupled);
+ if (hfxoInit->mode == cmuOscMode_External) {
+ tmp = CMU_HFXOCTRL_MODE_DIGEXTCLK;
+ }
+
+ /* Apply control settings */
+ CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_MODE_MASK)
+ | tmp;
+ BUS_RegBitWrite(&CMU->HFXOCTRL, _CMU_HFXOCTRL_LOWPOWER_SHIFT, hfxoInit->lowPowerMode);
+
+ /* Set XTAL tuning parameters */
+
+#if defined(_CMU_HFXOCTRL1_PEAKDETTHR_MASK)
+ /* Set peak detection threshold */
+ CMU->HFXOCTRL1 = (CMU->HFXOCTRL1 & ~_CMU_HFXOCTRL1_PEAKDETTHR_MASK)
+ | (hfxoInit->thresholdPeakDetect << _CMU_HFXOCTRL1_PEAKDETTHR_SHIFT);
+#endif
+ /* Set tuning for startup and steady state */
+ CMU->HFXOSTARTUPCTRL = (hfxoInit->ctuneStartup << _CMU_HFXOSTARTUPCTRL_CTUNE_SHIFT)
+ | (hfxoInit->xoCoreBiasTrimStartup << _CMU_HFXOSTARTUPCTRL_IBTRIMXOCORE_SHIFT);
+
+ CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL & ~(_CMU_HFXOSTEADYSTATECTRL_CTUNE_MASK
+ | _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK
+ | _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK
+ | _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK))
+ | (hfxoInit->ctuneSteadyState << _CMU_HFXOSTEADYSTATECTRL_CTUNE_SHIFT)
+ | (hfxoInit->xoCoreBiasTrimSteadyState << _CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_SHIFT)
+ | (hfxoInit->regIshSteadyState << _CMU_HFXOSTEADYSTATECTRL_REGISH_SHIFT)
+ | getRegIshUpperVal(hfxoInit->regIshSteadyState);
+
+ /* Set timeouts */
+ CMU->HFXOTIMEOUTCTRL = (hfxoInit->timeoutPeakDetect << _CMU_HFXOTIMEOUTCTRL_PEAKDETTIMEOUT_SHIFT)
+ | (hfxoInit->timeoutSteady << _CMU_HFXOTIMEOUTCTRL_STEADYTIMEOUT_SHIFT)
+ | (hfxoInit->timeoutStartup << _CMU_HFXOTIMEOUTCTRL_STARTUPTIMEOUT_SHIFT)
+ | (hfxoInit->timeoutShuntOptimization << _CMU_HFXOTIMEOUTCTRL_SHUNTOPTTIMEOUT_SHIFT);
+
+#else
+ CMU->CTRL = (CMU->CTRL & ~(_CMU_CTRL_HFXOTIMEOUT_MASK
+ | _CMU_CTRL_HFXOBOOST_MASK
+ | _CMU_CTRL_HFXOMODE_MASK
+ | _CMU_CTRL_HFXOGLITCHDETEN_MASK))
+ | (hfxoInit->timeout << _CMU_CTRL_HFXOTIMEOUT_SHIFT)
+ | (hfxoInit->boost << _CMU_CTRL_HFXOBOOST_SHIFT)
+ | (hfxoInit->mode << _CMU_CTRL_HFXOMODE_SHIFT)
+ | (hfxoInit->glitchDetector ? CMU_CTRL_HFXOGLITCHDETEN : 0);
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get the LCD framerate divisor (FDIV) setting.
+ *
+ * @return
+ * The LCD framerate divisor.
+ ******************************************************************************/
+uint32_t CMU_LCDClkFDIVGet(void)
+{
+#if defined(LCD_PRESENT) && defined(_CMU_LCDCTRL_MASK)
+ return (CMU->LCDCTRL & _CMU_LCDCTRL_FDIV_MASK) >> _CMU_LCDCTRL_FDIV_SHIFT;
+#else
+ return 0;
+#endif /* defined(LCD_PRESENT) */
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set the LCD framerate divisor (FDIV) setting.
+ *
+ * @note
+ * The FDIV field (CMU LCDCTRL register) should only be modified while the
+ * LCD module is clock disabled (CMU LFACLKEN0.LCD bit is 0). This function
+ * will NOT modify FDIV if the LCD module clock is enabled. Please refer to
+ * CMU_ClockEnable() for disabling/enabling LCD clock.
+ *
+ * @param[in] div
+ * The FDIV setting to use.
+ ******************************************************************************/
+void CMU_LCDClkFDIVSet(uint32_t div)
+{
+#if defined(LCD_PRESENT) && defined(_CMU_LCDCTRL_MASK)
+ EFM_ASSERT(div <= cmuClkDiv_128);
+
+ /* Do not allow modification if LCD clock enabled */
+ if (CMU->LFACLKEN0 & CMU_LFACLKEN0_LCD) {
+ return;
+ }
+
+ div <<= _CMU_LCDCTRL_FDIV_SHIFT;
+ div &= _CMU_LCDCTRL_FDIV_MASK;
+ CMU->LCDCTRL = (CMU->LCDCTRL & ~_CMU_LCDCTRL_FDIV_MASK) | div;
+#else
+ (void)div; /* Unused parameter */
+#endif /* defined(LCD_PRESENT) */
+}
+
+/**************************************************************************//**
+ * @brief
+ * Set LFXO control registers
+ *
+ * @note
+ * LFXO configuration should be obtained from a configuration tool,
+ * app note or xtal datasheet. This function disables the LFXO to ensure
+ * a valid state before update.
+ *
+ * @param[in] lfxoInit
+ * LFXO setup parameters
+ *****************************************************************************/
+void CMU_LFXOInit(const CMU_LFXOInit_TypeDef *lfxoInit)
+{
+ /* Do not disable LFXO if it is currently selected as HF/Core clock */
+ EFM_ASSERT(CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_LFXO);
+
+ /* LFXO must be disabled before reconfiguration */
+ CMU_OscillatorEnable(cmuOsc_LFXO, false, false);
+
+#if defined(_CMU_LFXOCTRL_MASK)
+ BUS_RegMaskedWrite(&CMU->LFXOCTRL,
+ _CMU_LFXOCTRL_TUNING_MASK
+ | _CMU_LFXOCTRL_GAIN_MASK
+ | _CMU_LFXOCTRL_TIMEOUT_MASK
+ | _CMU_LFXOCTRL_MODE_MASK,
+ (lfxoInit->ctune << _CMU_LFXOCTRL_TUNING_SHIFT)
+ | (lfxoInit->gain << _CMU_LFXOCTRL_GAIN_SHIFT)
+ | (lfxoInit->timeout << _CMU_LFXOCTRL_TIMEOUT_SHIFT)
+ | (lfxoInit->mode << _CMU_LFXOCTRL_MODE_SHIFT));
+#else
+ bool cmuBoost = (lfxoInit->boost & 0x2);
+ BUS_RegMaskedWrite(&CMU->CTRL,
+ _CMU_CTRL_LFXOTIMEOUT_MASK
+ | _CMU_CTRL_LFXOBOOST_MASK
+ | _CMU_CTRL_LFXOMODE_MASK,
+ (lfxoInit->timeout << _CMU_CTRL_LFXOTIMEOUT_SHIFT)
+ | ((cmuBoost ? 1 : 0) << _CMU_CTRL_LFXOBOOST_SHIFT)
+ | (lfxoInit->mode << _CMU_CTRL_LFXOMODE_SHIFT));
+#endif
+
+#if defined(_EMU_AUXCTRL_REDLFXOBOOST_MASK)
+ bool emuReduce = (lfxoInit->boost & 0x1);
+ BUS_RegBitWrite(&EMU->AUXCTRL, _EMU_AUXCTRL_REDLFXOBOOST_SHIFT, emuReduce ? 1 : 0);
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Enable/disable oscillator.
+ *
+ * @note
+ * WARNING: When this function is called to disable either cmuOsc_LFXO or
+ * cmuOsc_HFXO the LFXOMODE or HFXOMODE fields of the CMU_CTRL register
+ * are reset to the reset value. I.e. if external clock sources are selected
+ * in either LFXOMODE or HFXOMODE fields, the configuration will be cleared
+ * and needs to be reconfigured if needed later.
+ *
+ * @param[in] osc
+ * The oscillator to enable/disable.
+ *
+ * @param[in] enable
+ * @li true - enable specified oscillator.
+ * @li false - disable specified oscillator.
+ *
+ * @param[in] wait
+ * Only used if @p enable is true.
+ * @li true - wait for oscillator start-up time to timeout before returning.
+ * @li false - do not wait for oscillator start-up time to timeout before
+ * returning.
+ ******************************************************************************/
+void CMU_OscillatorEnable(CMU_Osc_TypeDef osc, bool enable, bool wait)
+{
+ uint32_t rdyBitPos;
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ uint32_t ensBitPos;
+#endif
+#if defined(_CMU_STATUS_HFXOPEAKDETRDY_MASK)
+ uint32_t hfxoTrimStatus;
+#endif
+
+ uint32_t enBit;
+ uint32_t disBit;
+
+ switch (osc) {
+ case cmuOsc_HFRCO:
+ enBit = CMU_OSCENCMD_HFRCOEN;
+ disBit = CMU_OSCENCMD_HFRCODIS;
+ rdyBitPos = _CMU_STATUS_HFRCORDY_SHIFT;
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ ensBitPos = _CMU_STATUS_HFRCOENS_SHIFT;
+#endif
+ break;
+
+ case cmuOsc_HFXO:
+ enBit = CMU_OSCENCMD_HFXOEN;
+ disBit = CMU_OSCENCMD_HFXODIS;
+ rdyBitPos = _CMU_STATUS_HFXORDY_SHIFT;
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ ensBitPos = _CMU_STATUS_HFXOENS_SHIFT;
+#endif
+ break;
+
+ case cmuOsc_AUXHFRCO:
+ enBit = CMU_OSCENCMD_AUXHFRCOEN;
+ disBit = CMU_OSCENCMD_AUXHFRCODIS;
+ rdyBitPos = _CMU_STATUS_AUXHFRCORDY_SHIFT;
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ ensBitPos = _CMU_STATUS_AUXHFRCOENS_SHIFT;
+#endif
+ break;
+
+ case cmuOsc_LFRCO:
+ enBit = CMU_OSCENCMD_LFRCOEN;
+ disBit = CMU_OSCENCMD_LFRCODIS;
+ rdyBitPos = _CMU_STATUS_LFRCORDY_SHIFT;
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ ensBitPos = _CMU_STATUS_LFRCOENS_SHIFT;
+#endif
+ break;
+
+ case cmuOsc_LFXO:
+ enBit = CMU_OSCENCMD_LFXOEN;
+ disBit = CMU_OSCENCMD_LFXODIS;
+ rdyBitPos = _CMU_STATUS_LFXORDY_SHIFT;
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ ensBitPos = _CMU_STATUS_LFXOENS_SHIFT;
+#endif
+ break;
+
+#if defined(_CMU_STATUS_USHFRCOENS_MASK)
+ case cmuOsc_USHFRCO:
+ enBit = CMU_OSCENCMD_USHFRCOEN;
+ disBit = CMU_OSCENCMD_USHFRCODIS;
+ rdyBitPos = _CMU_STATUS_USHFRCORDY_SHIFT;
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ ensBitPos = _CMU_STATUS_USHFRCOENS_SHIFT;
+#endif
+ break;
+#endif
+
+#if defined(_CMU_STATUS_PLFRCOENS_MASK)
+ case cmuOsc_PLFRCO:
+ enBit = CMU_OSCENCMD_PLFRCOEN;
+ disBit = CMU_OSCENCMD_PLFRCODIS;
+ rdyBitPos = _CMU_STATUS_PLFRCORDY_SHIFT;
+ ensBitPos = _CMU_STATUS_PLFRCOENS_SHIFT;
+ break;
+#endif
+
+ default:
+ /* Undefined clock source or cmuOsc_ULFRCO. ULFRCO is always enabled,
+ and cannot be disabled. Ie. the definition of cmuOsc_ULFRCO is primarely
+ intended for information: the ULFRCO is always on. */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ if (enable) {
+ #if defined(_CMU_HFXOCTRL_MASK)
+ bool firstHfxoEnable = false;
+
+ /* Enabling the HFXO for the first time requires special handling. We use the
+ * PEAKDETSHUTOPTMODE field of the HFXOCTRL register to see if this is the
+ * first time the HFXO is enabled. */
+ if ((osc == cmuOsc_HFXO) && (getHfxoTuningMode() == HFXO_TUNING_MODE_AUTO)) {
+ /* REGPWRSEL must be set to DVDD before the HFXO can be enabled. */
+#if defined(_EMU_PWRCTRL_REGPWRSEL_MASK)
+ EFM_ASSERT(EMU->PWRCTRL & EMU_PWRCTRL_REGPWRSEL_DVDD);
+#endif
+
+ firstHfxoEnable = true;
+ /* First time we enable an external clock we should switch to CMD mode to make sure that
+ * we only do SCO and not PDA tuning. */
+ if ((CMU->HFXOCTRL & (_CMU_HFXOCTRL_MODE_MASK)) == CMU_HFXOCTRL_MODE_DIGEXTCLK) {
+ setHfxoTuningMode(HFXO_TUNING_MODE_CMD);
+ }
+ }
+#endif
+ CMU->OSCENCMD = enBit;
+
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ /* Always wait for ENS to go high */
+ while (!BUS_RegBitRead(&CMU->STATUS, ensBitPos)) {
+ }
+#endif
+
+ /* Wait for clock to become ready after enable */
+ if (wait) {
+ while (!BUS_RegBitRead(&CMU->STATUS, rdyBitPos)) ;
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ if ((osc == cmuOsc_HFXO) && firstHfxoEnable) {
+ if ((CMU->HFXOCTRL & _CMU_HFXOCTRL_MODE_MASK) == CMU_HFXOCTRL_MODE_DIGEXTCLK) {
+#if defined(CMU_CMD_HFXOSHUNTOPTSTART)
+ /* External clock mode should only do shunt current optimization. */
+ CMU_OscillatorTuningOptimize(cmuOsc_HFXO, cmuHFXOTuningMode_ShuntCommand, true);
+#endif
+ } else {
+ /* Wait for peak detection and shunt current optimization to complete. */
+ CMU_OscillatorTuningWait(cmuOsc_HFXO, cmuHFXOTuningMode_Auto);
+ }
+
+ /* Disable the HFXO again to apply the trims. Apply trim from HFXOTRIMSTATUS
+ when disabled. */
+ hfxoTrimStatus = CMU_OscillatorTuningGet(cmuOsc_HFXO);
+ CMU_OscillatorEnable(cmuOsc_HFXO, false, true);
+ CMU_OscillatorTuningSet(cmuOsc_HFXO, hfxoTrimStatus);
+
+ /* Restart in CMD mode. */
+ CMU->OSCENCMD = enBit;
+ while (!BUS_RegBitRead(&CMU->STATUS, rdyBitPos)) ;
+ }
+#endif
+ }
+ } else {
+ CMU->OSCENCMD = disBit;
+
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ /* Always wait for ENS to go low */
+ while (BUS_RegBitRead(&CMU->STATUS, ensBitPos)) {
+ }
+#endif
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get oscillator frequency tuning setting.
+ *
+ * @param[in] osc
+ * Oscillator to get tuning value for, one of:
+ * @li #cmuOsc_LFRCO
+ * @li #cmuOsc_HFRCO @if _CMU_USHFRCOCTRL_TUNING_MASK
+ * @li #cmuOsc_USHFRCO
+ * @endif
+ * @li #cmuOsc_AUXHFRCO
+ * @li #cmuOsc_HFXO if CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE is defined
+ *
+ * @return
+ * The oscillator frequency tuning setting in use.
+ ******************************************************************************/
+uint32_t CMU_OscillatorTuningGet(CMU_Osc_TypeDef osc)
+{
+ uint32_t ret;
+
+ switch (osc) {
+ case cmuOsc_LFRCO:
+ ret = (CMU->LFRCOCTRL & _CMU_LFRCOCTRL_TUNING_MASK)
+ >> _CMU_LFRCOCTRL_TUNING_SHIFT;
+ break;
+
+ case cmuOsc_HFRCO:
+ ret = (CMU->HFRCOCTRL & _CMU_HFRCOCTRL_TUNING_MASK)
+ >> _CMU_HFRCOCTRL_TUNING_SHIFT;
+ break;
+
+#if defined (_CMU_USHFRCOCTRL_TUNING_MASK)
+ case cmuOsc_USHFRCO:
+ ret = (CMU->USHFRCOCTRL & _CMU_USHFRCOCTRL_TUNING_MASK)
+ >> _CMU_USHFRCOCTRL_TUNING_SHIFT;
+ break;
+#endif
+
+ case cmuOsc_AUXHFRCO:
+ ret = (CMU->AUXHFRCOCTRL & _CMU_AUXHFRCOCTRL_TUNING_MASK)
+ >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT;
+ break;
+
+#if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK)
+ case cmuOsc_HFXO:
+ ret = CMU->HFXOTRIMSTATUS & (_CMU_HFXOTRIMSTATUS_IBTRIMXOCORE_MASK
+#if defined(_CMU_HFXOTRIMSTATUS_REGISH_MASK)
+ | _CMU_HFXOTRIMSTATUS_REGISH_MASK
+#endif
+ );
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set the oscillator frequency tuning control.
+ *
+ * @note
+ * Oscillator tuning is done during production, and the tuning value is
+ * automatically loaded after a reset. Changing the tuning value from the
+ * calibrated value is for more advanced use. Certain oscillators also have
+ * build-in tuning optimization.
+ *
+ * @param[in] osc
+ * Oscillator to set tuning value for, one of:
+ * @li #cmuOsc_LFRCO
+ * @li #cmuOsc_HFRCO @if _CMU_USHFRCOCTRL_TUNING_MASK
+ * @li #cmuOsc_USHFRCO
+ * @endif
+ * @li #cmuOsc_AUXHFRCO
+ * @li #cmuOsc_HFXO if PEAKDETSHUNTOPTMODE is available. Note that CMD mode is set.
+ *
+ * @param[in] val
+ * The oscillator frequency tuning setting to use.
+ ******************************************************************************/
+void CMU_OscillatorTuningSet(CMU_Osc_TypeDef osc, uint32_t val)
+{
+#if defined(_CMU_HFXOSTEADYSTATECTRL_REGISH_MASK)
+ uint32_t regIshUpper;
+#endif
+
+ switch (osc) {
+ case cmuOsc_LFRCO:
+ EFM_ASSERT(val <= (_CMU_LFRCOCTRL_TUNING_MASK
+ >> _CMU_LFRCOCTRL_TUNING_SHIFT));
+ val &= (_CMU_LFRCOCTRL_TUNING_MASK >> _CMU_LFRCOCTRL_TUNING_SHIFT);
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_LFRCOBSY_SHIFT)) ;
+#endif
+ CMU->LFRCOCTRL = (CMU->LFRCOCTRL & ~(_CMU_LFRCOCTRL_TUNING_MASK))
+ | (val << _CMU_LFRCOCTRL_TUNING_SHIFT);
+ break;
+
+ case cmuOsc_HFRCO:
+ EFM_ASSERT(val <= (_CMU_HFRCOCTRL_TUNING_MASK
+ >> _CMU_HFRCOCTRL_TUNING_SHIFT));
+ val &= (_CMU_HFRCOCTRL_TUNING_MASK >> _CMU_HFRCOCTRL_TUNING_SHIFT);
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_HFRCOBSY_SHIFT)) {
+ }
+#endif
+ CMU->HFRCOCTRL = (CMU->HFRCOCTRL & ~(_CMU_HFRCOCTRL_TUNING_MASK))
+ | (val << _CMU_HFRCOCTRL_TUNING_SHIFT);
+ break;
+
+#if defined (_CMU_USHFRCOCTRL_TUNING_MASK)
+ case cmuOsc_USHFRCO:
+ EFM_ASSERT(val <= (_CMU_USHFRCOCTRL_TUNING_MASK
+ >> _CMU_USHFRCOCTRL_TUNING_SHIFT));
+ val &= (_CMU_USHFRCOCTRL_TUNING_MASK >> _CMU_USHFRCOCTRL_TUNING_SHIFT);
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_USHFRCOBSY_SHIFT)) {
+ }
+#endif
+ CMU->USHFRCOCTRL = (CMU->USHFRCOCTRL & ~(_CMU_USHFRCOCTRL_TUNING_MASK))
+ | (val << _CMU_USHFRCOCTRL_TUNING_SHIFT);
+ break;
+#endif
+
+ case cmuOsc_AUXHFRCO:
+ EFM_ASSERT(val <= (_CMU_AUXHFRCOCTRL_TUNING_MASK
+ >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT));
+ val &= (_CMU_AUXHFRCOCTRL_TUNING_MASK >> _CMU_AUXHFRCOCTRL_TUNING_SHIFT);
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ while (BUS_RegBitRead(&CMU->SYNCBUSY, _CMU_SYNCBUSY_AUXHFRCOBSY_SHIFT)) {
+ }
+#endif
+ CMU->AUXHFRCOCTRL = (CMU->AUXHFRCOCTRL & ~(_CMU_AUXHFRCOCTRL_TUNING_MASK))
+ | (val << _CMU_AUXHFRCOCTRL_TUNING_SHIFT);
+ break;
+
+#if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK)
+ case cmuOsc_HFXO:
+
+ /* Do set PEAKDETSHUNTOPTMODE or HFXOSTEADYSTATECTRL if HFXO is enabled */
+ EFM_ASSERT(!(CMU->STATUS & CMU_STATUS_HFXOENS));
+
+ /* Switch to command mode. Automatic SCO and PDA calibration is not done
+ at the next enable. Set user REGISH, REGISHUPPER and IBTRIMXOCORE. */
+ CMU->HFXOCTRL = (CMU->HFXOCTRL & ~_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK)
+ | CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_CMD;
+
+#if defined(_CMU_HFXOSTEADYSTATECTRL_REGISH_MASK)
+ regIshUpper = getRegIshUpperVal((val & _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK)
+ >> _CMU_HFXOSTEADYSTATECTRL_REGISH_SHIFT);
+ CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL
+ & ~(_CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK
+ | _CMU_HFXOSTEADYSTATECTRL_REGISH_MASK
+ | _CMU_HFXOSTEADYSTATECTRL_REGISHUPPER_MASK))
+ | val
+ | regIshUpper;
+#else
+ CMU->HFXOSTEADYSTATECTRL = (CMU->HFXOSTEADYSTATECTRL
+ & ~_CMU_HFXOSTEADYSTATECTRL_IBTRIMXOCORE_MASK)
+ | val;
+#endif
+
+ break;
+#endif
+
+ default:
+ EFM_ASSERT(0);
+ break;
+ }
+}
+
+#if defined(_CMU_HFXOCTRL_PEAKDETSHUNTOPTMODE_MASK) || defined(_CMU_HFXOCTRL_PEAKDETMODE_MASK)
+/***************************************************************************//**
+ * @brief
+ * Wait for oscillator tuning optimization.
+ *
+ * @param[in] osc
+ * Oscillator to set tuning value for, one of:
+ * @li #cmuOsc_HFXO
+ *
+ * @param[in] mode
+ * Tuning optimization mode.
+ *
+ * @return
+ * Returns false on invalid parameters or oscillator error status.
+ ******************************************************************************/
+bool CMU_OscillatorTuningWait(CMU_Osc_TypeDef osc,
+ CMU_HFXOTuningMode_TypeDef mode)
+{
+ uint32_t waitFlags;
+ EFM_ASSERT(osc == cmuOsc_HFXO);
+
+ /* Currently implemented for HFXO with PEAKDETSHUNTOPTMODE only */
+ (void)osc;
+
+ if (getHfxoTuningMode() == HFXO_TUNING_MODE_AUTO) {
+ waitFlags = HFXO_TUNING_READY_FLAGS;
+ } else {
+ /* Set wait flags for each command and wait */
+ switch (mode) {
+#if defined(_CMU_STATUS_HFXOSHUNTOPTRDY_MASK)
+ case cmuHFXOTuningMode_ShuntCommand:
+ waitFlags = CMU_STATUS_HFXOSHUNTOPTRDY;
+ break;
+#endif
+ case cmuHFXOTuningMode_Auto:
+ waitFlags = HFXO_TUNING_READY_FLAGS;
+ break;
+
+#if defined(CMU_CMD_HFXOSHUNTOPTSTART)
+ case cmuHFXOTuningMode_PeakShuntCommand:
+ waitFlags = HFXO_TUNING_READY_FLAGS;
+ break;
+#endif
+
+ default:
+ waitFlags = _CMU_STATUS_MASK;
+ EFM_ASSERT(false);
+ }
+ }
+ while ((CMU->STATUS & waitFlags) != waitFlags) ;
+
+#if defined(CMU_IF_HFXOPEAKDETERR)
+ /* Check error flags */
+ if (waitFlags & CMU_STATUS_HFXOPEAKDETRDY) {
+ return (CMU->IF & CMU_IF_HFXOPEAKDETERR ? true : false);
+ }
+#endif
+ return true;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Start and optionally wait for oscillator tuning optimization.
+ *
+ * @param[in] osc
+ * Oscillator to set tuning value for, one of:
+ * @li #cmuOsc_HFXO
+ *
+ * @param[in] mode
+ * Tuning optimization mode.
+ *
+ * @param[in] wait
+ * Wait for tuning optimization to complete.
+ * true - wait for tuning optimization to complete.
+ * false - return without waiting.
+ *
+ * @return
+ * Returns false on invalid parameters or oscillator error status.
+ ******************************************************************************/
+bool CMU_OscillatorTuningOptimize(CMU_Osc_TypeDef osc,
+ CMU_HFXOTuningMode_TypeDef mode,
+ bool wait)
+{
+ switch (osc) {
+ case cmuOsc_HFXO:
+ if (mode) {
+#if defined(CMU_IF_HFXOPEAKDETERR)
+ /* Clear error flag before command write */
+ CMU->IFC = CMU_IFC_HFXOPEAKDETERR;
+#endif
+ CMU->CMD = mode;
+ }
+ if (wait) {
+ return CMU_OscillatorTuningWait(osc, mode);
+ }
+ break;
+
+ default:
+ EFM_ASSERT(false);
+ }
+ return true;
+}
+#endif
+
+/**************************************************************************//**
+ * @brief
+ * Determine if currently selected PCNTn clock used is external or LFBCLK.
+ *
+ * @param[in] instance
+ * PCNT instance number to get currently selected clock source for.
+ *
+ * @return
+ * @li true - selected clock is external clock.
+ * @li false - selected clock is LFBCLK.
+ *****************************************************************************/
+bool CMU_PCNTClockExternalGet(unsigned int instance)
+{
+ uint32_t setting;
+
+ switch (instance) {
+#if defined(_CMU_PCNTCTRL_PCNT0CLKEN_MASK)
+ case 0:
+ setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT0CLKSEL_PCNT0S0;
+ break;
+
+#if defined(_CMU_PCNTCTRL_PCNT1CLKEN_MASK)
+ case 1:
+ setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT1CLKSEL_PCNT1S0;
+ break;
+
+#if defined(_CMU_PCNTCTRL_PCNT2CLKEN_MASK)
+ case 2:
+ setting = CMU->PCNTCTRL & CMU_PCNTCTRL_PCNT2CLKSEL_PCNT2S0;
+ break;
+#endif
+#endif
+#endif
+
+ default:
+ setting = 0;
+ break;
+ }
+ return (setting ? true : false);
+}
+
+/**************************************************************************//**
+ * @brief
+ * Select PCNTn clock.
+ *
+ * @param[in] instance
+ * PCNT instance number to set selected clock source for.
+ *
+ * @param[in] external
+ * Set to true to select external clock, false to select LFBCLK.
+ *****************************************************************************/
+void CMU_PCNTClockExternalSet(unsigned int instance, bool external)
+{
+#if defined(PCNT_PRESENT)
+ uint32_t setting = 0;
+
+ EFM_ASSERT(instance < PCNT_COUNT);
+
+ if (external) {
+ setting = 1;
+ }
+
+ BUS_RegBitWrite(&(CMU->PCNTCTRL), (instance * 2) + 1, setting);
+
+#else
+ (void)instance; /* Unused parameter */
+ (void)external; /* Unused parameter */
+#endif
+}
+
+#if defined(_CMU_USHFRCOCONF_BAND_MASK)
+/***************************************************************************//**
+ * @brief
+ * Get USHFRCO band in use.
+ *
+ * @return
+ * USHFRCO band in use.
+ ******************************************************************************/
+CMU_USHFRCOBand_TypeDef CMU_USHFRCOBandGet(void)
+{
+ return (CMU_USHFRCOBand_TypeDef)((CMU->USHFRCOCONF
+ & _CMU_USHFRCOCONF_BAND_MASK)
+ >> _CMU_USHFRCOCONF_BAND_SHIFT);
+}
+#endif
+
+#if defined(_CMU_USHFRCOCONF_BAND_MASK)
+/***************************************************************************//**
+ * @brief
+ * Set USHFRCO band to use.
+ *
+ * @param[in] band
+ * USHFRCO band to activate.
+ ******************************************************************************/
+void CMU_USHFRCOBandSet(CMU_USHFRCOBand_TypeDef band)
+{
+ uint32_t tuning;
+ uint32_t fineTuning;
+ CMU_Select_TypeDef osc;
+
+ /* Cannot switch band if USHFRCO is already selected as HF clock. */
+ osc = CMU_ClockSelectGet(cmuClock_HF);
+ EFM_ASSERT((CMU_USHFRCOBandGet() != band) && (osc != cmuSelect_USHFRCO));
+
+ /* Read tuning value from calibration table */
+ switch (band) {
+ case cmuUSHFRCOBand_24MHz:
+ tuning = (DEVINFO->USHFRCOCAL0 & _DEVINFO_USHFRCOCAL0_BAND24_TUNING_MASK)
+ >> _DEVINFO_USHFRCOCAL0_BAND24_TUNING_SHIFT;
+ fineTuning = (DEVINFO->USHFRCOCAL0
+ & _DEVINFO_USHFRCOCAL0_BAND24_FINETUNING_MASK)
+ >> _DEVINFO_USHFRCOCAL0_BAND24_FINETUNING_SHIFT;
+ ushfrcoFreq = 24000000UL;
+ break;
+
+ case cmuUSHFRCOBand_48MHz:
+ tuning = (DEVINFO->USHFRCOCAL0 & _DEVINFO_USHFRCOCAL0_BAND48_TUNING_MASK)
+ >> _DEVINFO_USHFRCOCAL0_BAND48_TUNING_SHIFT;
+ fineTuning = (DEVINFO->USHFRCOCAL0
+ & _DEVINFO_USHFRCOCAL0_BAND48_FINETUNING_MASK)
+ >> _DEVINFO_USHFRCOCAL0_BAND48_FINETUNING_SHIFT;
+ /* Enable the clock divider before switching the band from 24 to 48MHz */
+ BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 0);
+ ushfrcoFreq = 48000000UL;
+ break;
+
+ default:
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Set band and tuning */
+ CMU->USHFRCOCONF = (CMU->USHFRCOCONF & ~_CMU_USHFRCOCONF_BAND_MASK)
+ | (band << _CMU_USHFRCOCONF_BAND_SHIFT);
+ CMU->USHFRCOCTRL = (CMU->USHFRCOCTRL & ~_CMU_USHFRCOCTRL_TUNING_MASK)
+ | (tuning << _CMU_USHFRCOCTRL_TUNING_SHIFT);
+ CMU->USHFRCOTUNE = (CMU->USHFRCOTUNE & ~_CMU_USHFRCOTUNE_FINETUNING_MASK)
+ | (fineTuning << _CMU_USHFRCOTUNE_FINETUNING_SHIFT);
+
+ /* Disable the clock divider after switching the band from 48 to 24MHz */
+ if (band == cmuUSHFRCOBand_24MHz) {
+ BUS_RegBitWrite(&CMU->USHFRCOCONF, _CMU_USHFRCOCONF_USHFRCODIV2DIS_SHIFT, 1);
+ }
+}
+#endif
+
+/** @} (end addtogroup CMU) */
+/** @} (end addtogroup emlib) */
+#endif /* __EM_CMU_H */
diff --git a/efm32boot/emlib/em_cryotimer.c b/efm32boot/emlib/em_cryotimer.c
new file mode 100644
index 0000000..66f4ac5
--- /dev/null
+++ b/efm32boot/emlib/em_cryotimer.c
@@ -0,0 +1,61 @@
+/***************************************************************************//**
+ * @file em_cryotimer.c
+ * @brief Ultra Low Energy Timer/Counter (CRYOTIMER) peripheral API
+ * @version 5.2.2
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.@n
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.@n
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+#include "em_cryotimer.h"
+#include "em_bus.h"
+
+#if defined(CRYOTIMER_PRESENT) && (CRYOTIMER_COUNT == 1)
+
+/***************************************************************************//**
+ * @brief
+ * Initialize the CRYOTIMER.
+ *
+ * @details
+ * Use this function to initialize the CRYOTIMER.
+ * Select prescaler setting and select low frequency oscillator.
+ * Refer to the configuration structure @ref CRYOTIMER_Init_TypeDef for more
+ * details.
+ *
+ * @param[in] init
+ * Pointer to initialization structure.
+ ******************************************************************************/
+void CRYOTIMER_Init(const CRYOTIMER_Init_TypeDef *init)
+{
+ CRYOTIMER->PERIODSEL = (uint32_t)init->period & _CRYOTIMER_PERIODSEL_MASK;
+ CRYOTIMER->CTRL = ((uint32_t)init->enable << _CRYOTIMER_CTRL_EN_SHIFT)
+ | ((uint32_t)init->debugRun << _CRYOTIMER_CTRL_DEBUGRUN_SHIFT)
+ | ((uint32_t)init->osc << _CRYOTIMER_CTRL_OSCSEL_SHIFT)
+ | ((uint32_t)init->presc << _CRYOTIMER_CTRL_PRESC_SHIFT);
+ CRYOTIMER_EM4WakeupEnable(init->em4Wakeup);
+}
+
+#endif /* defined(CRYOTIMER_PRESENT) && (CRYOTIMER_COUNT > 0) */
diff --git a/efm32boot/emlib/em_emu.c b/efm32boot/emlib/em_emu.c
new file mode 100644
index 0000000..1ba8a46
--- /dev/null
+++ b/efm32boot/emlib/em_emu.c
@@ -0,0 +1,2587 @@
+/***************************************************************************//**
+ * @file em_emu.c
+ * @brief Energy Management Unit (EMU) Peripheral API
+ * @version 5.2.2
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+#include
+
+#include "em_emu.h"
+#if defined(EMU_PRESENT) && (EMU_COUNT > 0)
+
+#include "em_cmu.h"
+#include "em_system.h"
+#include "em_common.h"
+#include "em_assert.h"
+
+/***************************************************************************//**
+ * @addtogroup emlib
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup EMU
+ * @brief Energy Management Unit (EMU) Peripheral API
+ * @details
+ * This module contains functions to control the EMU peripheral of Silicon
+ * Labs 32-bit MCUs and SoCs. The EMU handles the different low energy modes
+ * in Silicon Labs microcontrollers.
+ * @{
+ ******************************************************************************/
+
+/* Consistency check, since restoring assumes similar bitpositions in */
+/* CMU OSCENCMD and STATUS regs */
+#if (CMU_STATUS_AUXHFRCOENS != CMU_OSCENCMD_AUXHFRCOEN)
+#error Conflict in AUXHFRCOENS and AUXHFRCOEN bitpositions
+#endif
+#if (CMU_STATUS_HFXOENS != CMU_OSCENCMD_HFXOEN)
+#error Conflict in HFXOENS and HFXOEN bitpositions
+#endif
+#if (CMU_STATUS_LFRCOENS != CMU_OSCENCMD_LFRCOEN)
+#error Conflict in LFRCOENS and LFRCOEN bitpositions
+#endif
+#if (CMU_STATUS_LFXOENS != CMU_OSCENCMD_LFXOEN)
+#error Conflict in LFXOENS and LFXOEN bitpositions
+#endif
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+#if defined(_SILICON_LABS_32B_SERIES_0)
+/* Fix for errata EMU_E107 - non-WIC interrupt masks.
+ * Zero Gecko and future families are not affected by errata EMU_E107 */
+#if defined(_EFM32_GECKO_FAMILY)
+#define ERRATA_FIX_EMU_E107_EN
+#define NON_WIC_INT_MASK_0 (~(0x0dfc0323U))
+#define NON_WIC_INT_MASK_1 (~(0x0U))
+
+#elif defined(_EFM32_TINY_FAMILY)
+#define ERRATA_FIX_EMU_E107_EN
+#define NON_WIC_INT_MASK_0 (~(0x001be323U))
+#define NON_WIC_INT_MASK_1 (~(0x0U))
+
+#elif defined(_EFM32_GIANT_FAMILY)
+#define ERRATA_FIX_EMU_E107_EN
+#define NON_WIC_INT_MASK_0 (~(0xff020e63U))
+#define NON_WIC_INT_MASK_1 (~(0x00000046U))
+
+#elif defined(_EFM32_WONDER_FAMILY)
+#define ERRATA_FIX_EMU_E107_EN
+#define NON_WIC_INT_MASK_0 (~(0xff020e63U))
+#define NON_WIC_INT_MASK_1 (~(0x00000046U))
+
+#endif
+#endif
+
+/* Fix for errata EMU_E108 - High Current Consumption on EM4 Entry. */
+#if defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_HAPPY_FAMILY)
+#define ERRATA_FIX_EMU_E108_EN
+#endif
+
+/* Fix for errata EMU_E208 - Occasional Full Reset After Exiting EM4H */
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+#define ERRATA_FIX_EMU_E208_EN
+#endif
+
+/* Enable FETCNT tuning errata fix */
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+#define ERRATA_FIX_DCDC_FETCNT_SET_EN
+#endif
+
+/* Enable LN handshake errata fix */
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+#define ERRATA_FIX_DCDC_LNHS_BLOCK_EN
+typedef enum {
+ errataFixDcdcHsInit,
+ errataFixDcdcHsTrimSet,
+ errataFixDcdcHsBypassLn,
+ errataFixDcdcHsLnWaitDone
+} errataFixDcdcHs_TypeDef;
+static errataFixDcdcHs_TypeDef errataFixDcdcHsState = errataFixDcdcHsInit;
+#endif
+
+/* Used to figure out if a memory address is inside or outside of a RAM block.
+ * A memory address is inside a RAM block if the address is greater than the
+ * RAM block address. */
+#define ADDRESS_NOT_IN_BLOCK(addr, block) ((addr) <= (block))
+
+/* RAM Block layout for various device families. Note that some devices
+ * have special layout in RAM0 and some devices have a special RAM block
+ * at the end of their block layout. */
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_84)
+#define RAM1_BLOCKS 2
+#define RAM1_BLOCK_SIZE 0x10000 // 64 kB blocks
+#define RAM2_BLOCKS 1
+#define RAM2_BLOCK_SIZE 0x800 // 2 kB block
+#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_89)
+#define RAM0_BLOCKS 2
+#define RAM0_BLOCK_SIZE 0x4000
+#define RAM1_BLOCKS 2
+#define RAM1_BLOCK_SIZE 0x4000 // 16 kB blocks
+#define RAM2_BLOCKS 1
+#define RAM2_BLOCK_SIZE 0x800 // 2 kB block
+#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_95)
+#define RAM0_BLOCKS 1
+#define RAM0_BLOCK_SIZE 0x4000 // 16 kB block
+#define RAM1_BLOCKS 1
+#define RAM1_BLOCK_SIZE 0x4000 // 16 kB block
+#define RAM2_BLOCKS 1
+#define RAM2_BLOCK_SIZE 0x800 // 2 kB block
+#elif defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_GIANT_FAMILY)
+#define RAM0_BLOCKS 4
+#define RAM0_BLOCK_SIZE 0x8000 // 32 kB blocks
+#elif defined(_SILICON_LABS_32B_SERIES_0) && defined(_EFM32_GECKO_FAMILY)
+#define RAM0_BLOCKS 4
+#define RAM0_BLOCK_SIZE 0x1000 // 4 kB blocks
+#elif defined(_SILICON_LABS_32B_SERIES_1) && defined(_EFM32_GIANT_FAMILY)
+#define RAM0_BLOCKS 8
+#define RAM0_BLOCK_SIZE 0x4000 // 16 kB blocks
+#define RAM1_BLOCKS 8
+#define RAM1_BLOCK_SIZE 0x4000 // 16 kB blocks
+#define RAM2_BLOCKS 4
+#define RAM2_BLOCK_SIZE 0x10000 // 64 kB blocks
+#endif
+
+#if defined(_SILICON_LABS_32B_SERIES_0)
+/* RAM_MEM_END on Gecko devices have a value larger than the SRAM_SIZE */
+#define RAM0_END (SRAM_BASE + SRAM_SIZE - 1)
+#else
+#define RAM0_END RAM_MEM_END
+#endif
+
+#if defined(CMU_STATUS_HFXOSHUNTOPTRDY)
+#define HFXO_STATUS_READY_FLAGS (CMU_STATUS_HFXOPEAKDETRDY | CMU_STATUS_HFXOSHUNTOPTRDY)
+#elif defined(CMU_STATUS_HFXOPEAKDETRDY)
+#define HFXO_STATUS_READY_FLAGS (CMU_STATUS_HFXOPEAKDETRDY)
+#endif
+
+/** @endcond */
+
+#if defined(_EMU_DCDCCTRL_MASK)
+/* DCDCTODVDD output range min/max */
+#if !defined(PWRCFG_DCDCTODVDD_VMIN)
+#define PWRCFG_DCDCTODVDD_VMIN 1800
+#endif
+#if !defined(PWRCFG_DCDCTODVDD_VMAX)
+#define PWRCFG_DCDCTODVDD_VMAX 3000
+#endif
+#endif
+
+/*******************************************************************************
+ *************************** LOCAL VARIABLES ********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/* Static user configuration */
+#if defined(_EMU_DCDCCTRL_MASK)
+static uint16_t dcdcMaxCurrent_mA;
+static uint16_t dcdcEm01LoadCurrent_mA;
+static EMU_DcdcLnReverseCurrentControl_TypeDef dcdcReverseCurrentControl;
+#endif
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+static EMU_EM01Init_TypeDef vScaleEM01Config = { false };
+#endif
+/** @endcond */
+
+/*******************************************************************************
+ ************************** LOCAL FUNCTIONS ********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+/* Convert from level to EM0 and 1 command bit */
+__STATIC_INLINE uint32_t vScaleEM01Cmd(EMU_VScaleEM01_TypeDef level)
+{
+ return EMU_CMD_EM01VSCALE0 << (_EMU_STATUS_VSCALE_VSCALE0 - (uint32_t)level);
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Save/restore/update oscillator, core clock and voltage scaling configuration on
+ * EM2 or EM3 entry/exit.
+ *
+ * @details
+ * Hardware may automatically change oscillator and voltage scaling configuration
+ * when going into or out of an energy mode. Static data in this function keeps track of
+ * such configuration bits and is used to restore state if needed.
+ *
+ ******************************************************************************/
+typedef enum {
+ emState_Save, /* Save EMU and CMU state */
+ emState_Restore, /* Restore and unlock */
+} emState_TypeDef;
+
+static void emState(emState_TypeDef action)
+{
+ uint32_t oscEnCmd;
+ uint32_t cmuLocked;
+ static uint32_t cmuStatus;
+ static CMU_Select_TypeDef hfClock;
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+ static uint8_t vScaleStatus;
+#endif
+
+ /* Save or update state */
+ if (action == emState_Save) {
+ /* Save configuration. */
+ cmuStatus = CMU->STATUS;
+ hfClock = CMU_ClockSelectGet(cmuClock_HF);
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+ /* Save vscale */
+ EMU_VScaleWait();
+ vScaleStatus = (uint8_t)((EMU->STATUS & _EMU_STATUS_VSCALE_MASK)
+ >> _EMU_STATUS_VSCALE_SHIFT);
+#endif
+ } else if (action == emState_Restore) { /* Restore state */
+ /* Apply saved configuration. */
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+ /* Restore EM0 and 1 voltage scaling level. EMU_VScaleWait() is called later,
+ just before HF clock select is set. */
+ EMU->CMD = vScaleEM01Cmd((EMU_VScaleEM01_TypeDef)vScaleStatus);
+#endif
+
+ /* CMU registers may be locked */
+ cmuLocked = CMU->LOCK & CMU_LOCK_LOCKKEY_LOCKED;
+ CMU_Unlock();
+
+ /* AUXHFRCO are automatically disabled (except if using debugger). */
+ /* HFRCO, USHFRCO and HFXO are automatically disabled. */
+ /* LFRCO/LFXO may be disabled by SW in EM3. */
+ /* Restore according to status prior to entering energy mode. */
+ oscEnCmd = 0;
+ oscEnCmd |= ((cmuStatus & CMU_STATUS_HFRCOENS) ? CMU_OSCENCMD_HFRCOEN : 0);
+ oscEnCmd |= ((cmuStatus & CMU_STATUS_AUXHFRCOENS) ? CMU_OSCENCMD_AUXHFRCOEN : 0);
+ oscEnCmd |= ((cmuStatus & CMU_STATUS_LFRCOENS) ? CMU_OSCENCMD_LFRCOEN : 0);
+ oscEnCmd |= ((cmuStatus & CMU_STATUS_HFXOENS) ? CMU_OSCENCMD_HFXOEN : 0);
+ oscEnCmd |= ((cmuStatus & CMU_STATUS_LFXOENS) ? CMU_OSCENCMD_LFXOEN : 0);
+#if defined(_CMU_STATUS_USHFRCOENS_MASK)
+ oscEnCmd |= ((cmuStatus & CMU_STATUS_USHFRCOENS) ? CMU_OSCENCMD_USHFRCOEN : 0);
+#endif
+ CMU->OSCENCMD = oscEnCmd;
+
+#if defined(_EMU_STATUS_VSCALE_MASK)
+ /* Wait for upscale to complete and then restore selected clock */
+ EMU_VScaleWait();
+#endif
+
+ if (hfClock != cmuSelect_HFRCO) {
+ CMU_ClockSelectSet(cmuClock_HF, hfClock);
+ }
+
+ /* If HFRCO was disabled before entering Energy Mode, turn it off again */
+ /* as it is automatically enabled by wake up */
+ if ( !(cmuStatus & CMU_STATUS_HFRCOENS) ) {
+ CMU->OSCENCMD = CMU_OSCENCMD_HFRCODIS;
+ }
+
+ /* Restore CMU register locking */
+ if (cmuLocked) {
+ CMU_Lock();
+ }
+ }
+}
+
+#if defined(ERRATA_FIX_EMU_E107_EN)
+/* Get enable conditions for errata EMU_E107 fix. */
+__STATIC_INLINE bool getErrataFixEmuE107En(void)
+{
+ /* SYSTEM_ChipRevisionGet could have been used here, but we would like a
+ * faster implementation in this case.
+ */
+ uint16_t majorMinorRev;
+
+ /* CHIP MAJOR bit [3:0] */
+ majorMinorRev = ((ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK)
+ >> _ROMTABLE_PID0_REVMAJOR_SHIFT)
+ << 8;
+ /* CHIP MINOR bit [7:4] */
+ majorMinorRev |= ((ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK)
+ >> _ROMTABLE_PID2_REVMINORMSB_SHIFT)
+ << 4;
+ /* CHIP MINOR bit [3:0] */
+ majorMinorRev |= (ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK)
+ >> _ROMTABLE_PID3_REVMINORLSB_SHIFT;
+
+#if defined(_EFM32_GECKO_FAMILY)
+ return (majorMinorRev <= 0x0103);
+#elif defined(_EFM32_TINY_FAMILY)
+ return (majorMinorRev <= 0x0102);
+#elif defined(_EFM32_GIANT_FAMILY)
+ return (majorMinorRev <= 0x0103) || (majorMinorRev == 0x0204);
+#elif defined(_EFM32_WONDER_FAMILY)
+ return (majorMinorRev == 0x0100);
+#else
+ /* Zero Gecko and future families are not affected by errata EMU_E107 */
+ return false;
+#endif
+}
+#endif
+
+/* LP prepare / LN restore P/NFET count */
+#define DCDC_LP_PFET_CNT 7
+#define DCDC_LP_NFET_CNT 7
+#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
+static void currentLimitersUpdate(void);
+static void dcdcFetCntSet(bool lpModeSet)
+{
+ uint32_t tmp;
+ static uint32_t emuDcdcMiscCtrlReg;
+
+ if (lpModeSet) {
+ emuDcdcMiscCtrlReg = EMU->DCDCMISCCTRL;
+ tmp = EMU->DCDCMISCCTRL
+ & ~(_EMU_DCDCMISCCTRL_PFETCNT_MASK | _EMU_DCDCMISCCTRL_NFETCNT_MASK);
+ tmp |= (DCDC_LP_PFET_CNT << _EMU_DCDCMISCCTRL_PFETCNT_SHIFT)
+ | (DCDC_LP_NFET_CNT << _EMU_DCDCMISCCTRL_NFETCNT_SHIFT);
+ EMU->DCDCMISCCTRL = tmp;
+ currentLimitersUpdate();
+ } else {
+ EMU->DCDCMISCCTRL = emuDcdcMiscCtrlReg;
+ currentLimitersUpdate();
+ }
+}
+#endif
+
+#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_EN)
+static void dcdcHsFixLnBlock(void)
+{
+#define EMU_DCDCSTATUS (*(volatile uint32_t *)(EMU_BASE + 0x7C))
+ if ((errataFixDcdcHsState == errataFixDcdcHsTrimSet)
+ || (errataFixDcdcHsState == errataFixDcdcHsBypassLn)) {
+ /* Wait for LNRUNNING */
+ if ((EMU->DCDCCTRL & _EMU_DCDCCTRL_DCDCMODE_MASK) == EMU_DCDCCTRL_DCDCMODE_LOWNOISE) {
+ while (!(EMU_DCDCSTATUS & (0x1 << 16))) ;
+ }
+ errataFixDcdcHsState = errataFixDcdcHsLnWaitDone;
+ }
+}
+#endif
+
+#if defined(_EMU_CTRL_EM23VSCALE_MASK)
+/* Configure EMU and CMU for EM2 and 3 voltage downscale */
+static void vScaleDownEM23Setup(void)
+{
+ uint32_t hfSrcClockFrequency;
+
+ EMU_VScaleEM23_TypeDef scaleEM23Voltage =
+ (EMU_VScaleEM23_TypeDef)((EMU->CTRL & _EMU_CTRL_EM23VSCALE_MASK)
+ >> _EMU_CTRL_EM23VSCALE_SHIFT);
+
+ EMU_VScaleEM01_TypeDef currentEM01Voltage =
+ (EMU_VScaleEM01_TypeDef)((EMU->STATUS & _EMU_STATUS_VSCALE_MASK)
+ >> _EMU_STATUS_VSCALE_SHIFT);
+
+ /* Wait until previous scaling is done. */
+ EMU_VScaleWait();
+
+ /* Inverse coding. */
+ if ((uint32_t)scaleEM23Voltage > (uint32_t)currentEM01Voltage) {
+ /* Set safe clock and wait-states. */
+ if (scaleEM23Voltage == emuVScaleEM23_LowPower) {
+ hfSrcClockFrequency = CMU_ClockDivGet(cmuClock_HF) * CMU_ClockFreqGet(cmuClock_HF);
+ /* Set default low power voltage HFRCO band as HF clock. */
+ if (hfSrcClockFrequency > CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX) {
+ CMU_HFRCOBandSet(cmuHFRCOFreq_19M0Hz);
+ }
+ CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFRCO);
+ } else {
+ /* Other voltage scaling levels are not currently supported. */
+ EFM_ASSERT(false);
+ }
+ } else {
+ /* Same voltage or hardware will scale to min(EMU_CTRL_EM23VSCALE, EMU_STATUS_VSCALE) */
+ }
+}
+#endif
+/** @endcond */
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Enter energy mode 2 (EM2).
+ *
+ * @details
+ * When entering EM2, the high frequency clocks are disabled, ie HFXO, HFRCO
+ * and AUXHFRCO (for AUXHFRCO, see exception note below). When re-entering
+ * EM0, HFRCO is re-enabled and the core will be clocked by the configured
+ * HFRCO band. This ensures a quick wakeup from EM2.
+ *
+ * However, prior to entering EM2, the core may have been using another
+ * oscillator than HFRCO. The @p restore parameter gives the user the option
+ * to restore all HF oscillators according to state prior to entering EM2,
+ * as well as the clock used to clock the core. This restore procedure is
+ * handled by SW. However, since handled by SW, it will not be restored
+ * before completing the interrupt function(s) waking up the core!
+ *
+ * @note
+ * If restoring core clock to use the HFXO oscillator, which has been
+ * disabled during EM2 mode, this function will stall until the oscillator
+ * has stabilized. Stalling time can be reduced by adding interrupt
+ * support detecting stable oscillator, and an asynchronous switch to the
+ * original oscillator. See CMU documentation. Such a feature is however
+ * outside the scope of the implementation in this function.
+ * @par
+ * If HFXO is re-enabled by this function, and NOT used to clock the core,
+ * this function will not wait for HFXO to stabilize. This must be considered
+ * by the application if trying to use features relying on that oscillator
+ * upon return.
+ * @par
+ * If a debugger is attached, the AUXHFRCO will not be disabled if enabled
+ * upon entering EM2. It will thus remain enabled when returning to EM0
+ * regardless of the @p restore parameter.
+ * @par
+ * If HFXO autostart and select is enabled by using CMU_HFXOAutostartEnable(),
+ * the starting and selecting of the core clocks will be identical to the user
+ * independently of the value of the @p restore parameter when waking up on
+ * the wakeup sources corresponding to the autostart and select setting.
+ * @par
+ * If voltage scaling is supported, the restore parameter is true and the EM0
+ * voltage scaling level is set higher than the EM2 level, then the EM0 level is
+ * also restored.
+ *
+ * @param[in] restore
+ * @li true - save and restore oscillators, clocks and voltage scaling, see
+ * function details.
+ * @li false - do not save and restore oscillators and clocks, see function
+ * details.
+ * @par
+ * The @p restore option should only be used if all clock control is done
+ * via the CMU API.
+ ******************************************************************************/
+void EMU_EnterEM2(bool restore)
+{
+#if defined(ERRATA_FIX_EMU_E107_EN)
+ bool errataFixEmuE107En;
+ uint32_t nonWicIntEn[2];
+#endif
+
+ /* Only save EMU and CMU state if restored on wake-up. */
+ if (restore) {
+ emState(emState_Save);
+ }
+
+#if defined(_EMU_CTRL_EM23VSCALE_MASK)
+ vScaleDownEM23Setup();
+#endif
+
+ /* Enter Cortex deep sleep mode */
+ SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
+
+ /* Fix for errata EMU_E107 - store non-WIC interrupt enable flags.
+ Disable the enabled non-WIC interrupts. */
+#if defined(ERRATA_FIX_EMU_E107_EN)
+ errataFixEmuE107En = getErrataFixEmuE107En();
+ if (errataFixEmuE107En) {
+ nonWicIntEn[0] = NVIC->ISER[0] & NON_WIC_INT_MASK_0;
+ NVIC->ICER[0] = nonWicIntEn[0];
+#if (NON_WIC_INT_MASK_1 != (~(0x0U)))
+ nonWicIntEn[1] = NVIC->ISER[1] & NON_WIC_INT_MASK_1;
+ NVIC->ICER[1] = nonWicIntEn[1];
+#endif
+ }
+#endif
+
+#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
+ dcdcFetCntSet(true);
+#endif
+#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_EN)
+ dcdcHsFixLnBlock();
+#endif
+
+ __WFI();
+
+#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
+ dcdcFetCntSet(false);
+#endif
+
+ /* Fix for errata EMU_E107 - restore state of non-WIC interrupt enable flags. */
+#if defined(ERRATA_FIX_EMU_E107_EN)
+ if (errataFixEmuE107En) {
+ NVIC->ISER[0] = nonWicIntEn[0];
+#if (NON_WIC_INT_MASK_1 != (~(0x0U)))
+ NVIC->ISER[1] = nonWicIntEn[1];
+#endif
+ }
+#endif
+
+ /* Restore oscillators/clocks and voltage scaling if supported. */
+ if (restore) {
+ emState(emState_Restore);
+ } else {
+ /* If not restoring, and original clock was not HFRCO, we have to */
+ /* update CMSIS core clock variable since HF clock has changed */
+ /* to HFRCO. */
+ SystemCoreClockUpdate();
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Enter energy mode 3 (EM3).
+ *
+ * @details
+ * When entering EM3, the high frequency clocks are disabled by HW, ie HFXO,
+ * HFRCO and AUXHFRCO (for AUXHFRCO, see exception note below). In addition,
+ * the low frequency clocks, ie LFXO and LFRCO are disabled by SW. When
+ * re-entering EM0, HFRCO is re-enabled and the core will be clocked by the
+ * configured HFRCO band. This ensures a quick wakeup from EM3.
+ *
+ * However, prior to entering EM3, the core may have been using another
+ * oscillator than HFRCO. The @p restore parameter gives the user the option
+ * to restore all HF/LF oscillators according to state prior to entering EM3,
+ * as well as the clock used to clock the core. This restore procedure is
+ * handled by SW. However, since handled by SW, it will not be restored
+ * before completing the interrupt function(s) waking up the core!
+ *
+ * @note
+ * If restoring core clock to use an oscillator other than HFRCO, this
+ * function will stall until the oscillator has stabilized. Stalling time
+ * can be reduced by adding interrupt support detecting stable oscillator,
+ * and an asynchronous switch to the original oscillator. See CMU
+ * documentation. Such a feature is however outside the scope of the
+ * implementation in this function.
+ * @par
+ * If HFXO/LFXO/LFRCO are re-enabled by this function, and NOT used to clock
+ * the core, this function will not wait for those oscillators to stabilize.
+ * This must be considered by the application if trying to use features
+ * relying on those oscillators upon return.
+ * @par
+ * If a debugger is attached, the AUXHFRCO will not be disabled if enabled
+ * upon entering EM3. It will thus remain enabled when returning to EM0
+ * regardless of the @p restore parameter.
+ * @par
+ * If voltage scaling is supported, the restore parameter is true and the EM0
+ * voltage scaling level is set higher than the EM3 level, then the EM0 level is
+ * also restored.
+ *
+ * @param[in] restore
+ * @li true - save and restore oscillators, clocks and voltage scaling, see
+ * function details.
+ * @li false - do not save and restore oscillators and clocks, see function
+ * details.
+ * @par
+ * The @p restore option should only be used if all clock control is done
+ * via the CMU API.
+ ******************************************************************************/
+void EMU_EnterEM3(bool restore)
+{
+ uint32_t cmuLocked;
+
+#if defined(ERRATA_FIX_EMU_E107_EN)
+ bool errataFixEmuE107En;
+ uint32_t nonWicIntEn[2];
+#endif
+
+ /* Only save EMU and CMU state if restored on wake-up. */
+ if (restore) {
+ emState(emState_Save);
+ }
+
+#if defined(_EMU_CTRL_EM23VSCALE_MASK)
+ vScaleDownEM23Setup();
+#endif
+
+ /* CMU registers may be locked */
+ cmuLocked = CMU->LOCK & CMU_LOCK_LOCKKEY_LOCKED;
+ CMU_Unlock();
+
+ /* Disable LF oscillators */
+ CMU->OSCENCMD = CMU_OSCENCMD_LFXODIS | CMU_OSCENCMD_LFRCODIS;
+
+ /* Restore CMU register locking */
+ if (cmuLocked) {
+ CMU_Lock();
+ }
+
+ /* Enter Cortex deep sleep mode */
+ SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
+
+ /* Fix for errata EMU_E107 - store non-WIC interrupt enable flags.
+ Disable the enabled non-WIC interrupts. */
+#if defined(ERRATA_FIX_EMU_E107_EN)
+ errataFixEmuE107En = getErrataFixEmuE107En();
+ if (errataFixEmuE107En) {
+ nonWicIntEn[0] = NVIC->ISER[0] & NON_WIC_INT_MASK_0;
+ NVIC->ICER[0] = nonWicIntEn[0];
+#if (NON_WIC_INT_MASK_1 != (~(0x0U)))
+ nonWicIntEn[1] = NVIC->ISER[1] & NON_WIC_INT_MASK_1;
+ NVIC->ICER[1] = nonWicIntEn[1];
+#endif
+ }
+#endif
+
+#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
+ dcdcFetCntSet(true);
+#endif
+#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_EN)
+ dcdcHsFixLnBlock();
+#endif
+
+ __WFI();
+
+#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
+ dcdcFetCntSet(false);
+#endif
+
+ /* Fix for errata EMU_E107 - restore state of non-WIC interrupt enable flags. */
+#if defined(ERRATA_FIX_EMU_E107_EN)
+ if (errataFixEmuE107En) {
+ NVIC->ISER[0] = nonWicIntEn[0];
+#if (NON_WIC_INT_MASK_1 != (~(0x0U)))
+ NVIC->ISER[1] = nonWicIntEn[1];
+#endif
+ }
+#endif
+
+ /* Restore oscillators/clocks and voltage scaling if supported. */
+ if (restore) {
+ emState(emState_Restore);
+ } else {
+ /* If not restoring, and original clock was not HFRCO, we have to */
+ /* update CMSIS core clock variable since HF clock has changed */
+ /* to HFRCO. */
+ SystemCoreClockUpdate();
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Save CMU HF clock select state, oscillator enable and voltage scaling
+ * (if available) before @ref EMU_EnterEM2() or @ref EMU_EnterEM3() are called
+ * with the restore parameter set to false. Calling this function is
+ * equivalent to calling @ref EMU_EnterEM2() or @ref EMU_EnterEM3() with the
+ * restore parameter set to true, but it allows the state to be saved without
+ * going to sleep. The state can be restored manually by calling
+ * @ref EMU_Restore().
+ ******************************************************************************/
+void EMU_Save(void)
+{
+ emState(emState_Save);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Restore CMU HF clock select state, oscillator enable and voltage scaling
+ * (if available) after @ref EMU_EnterEM2() or @ref EMU_EnterEM3() are called
+ * with the restore parameter set to false. Calling this function is
+ * equivalent to calling @ref EMU_EnterEM2() or @ref EMU_EnterEM3() with the
+ * restore parameter set to true, but it allows the application to evaluate the
+ * wakeup reason before restoring state.
+ ******************************************************************************/
+void EMU_Restore(void)
+{
+ emState(emState_Restore);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Enter energy mode 4 (EM4).
+ *
+ * @note
+ * Only a power on reset or external reset pin can wake the device from EM4.
+ ******************************************************************************/
+void EMU_EnterEM4(void)
+{
+ int i;
+
+#if defined(_EMU_EM4CTRL_EM4ENTRY_SHIFT)
+ uint32_t em4seq2 = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4ENTRY_MASK)
+ | (2 << _EMU_EM4CTRL_EM4ENTRY_SHIFT);
+ uint32_t em4seq3 = (EMU->EM4CTRL & ~_EMU_EM4CTRL_EM4ENTRY_MASK)
+ | (3 << _EMU_EM4CTRL_EM4ENTRY_SHIFT);
+#else
+ uint32_t em4seq2 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK)
+ | (2 << _EMU_CTRL_EM4CTRL_SHIFT);
+ uint32_t em4seq3 = (EMU->CTRL & ~_EMU_CTRL_EM4CTRL_MASK)
+ | (3 << _EMU_CTRL_EM4CTRL_SHIFT);
+#endif
+
+ /* Make sure register write lock is disabled */
+ EMU_Unlock();
+
+#if defined(_EMU_EM4CTRL_MASK)
+ if ((EMU->EM4CTRL & _EMU_EM4CTRL_EM4STATE_MASK) == EMU_EM4CTRL_EM4STATE_EM4S) {
+ uint32_t dcdcMode = EMU->DCDCCTRL & _EMU_DCDCCTRL_DCDCMODE_MASK;
+ if (dcdcMode == EMU_DCDCCTRL_DCDCMODE_LOWNOISE
+ || dcdcMode == EMU_DCDCCTRL_DCDCMODE_LOWPOWER) {
+ /* DCDC is not supported in EM4S so we switch DCDC to bypass mode before
+ * entering EM4S */
+ EMU_DCDCModeSet(emuDcdcMode_Bypass);
+ }
+ }
+#endif
+
+#if defined(_EMU_EM4CTRL_MASK) && defined(ERRATA_FIX_EMU_E208_EN)
+ if (EMU->EM4CTRL & EMU_EM4CTRL_EM4STATE_EM4H) {
+ /* Fix for errata EMU_E208 - Occasional Full Reset After Exiting EM4H.
+ * Full description of errata fix can be found in the errata document. */
+ __disable_irq();
+ *(volatile uint32_t *)(EMU_BASE + 0x190) = 0x0000ADE8UL;
+ *(volatile uint32_t *)(EMU_BASE + 0x198) |= (0x1UL << 7);
+ *(volatile uint32_t *)(EMU_BASE + 0x88) |= (0x1UL << 8);
+ }
+#endif
+
+#if defined(ERRATA_FIX_EMU_E108_EN)
+ /* Fix for errata EMU_E108 - High Current Consumption on EM4 Entry. */
+ __disable_irq();
+ *(volatile uint32_t *)0x400C80E4 = 0;
+#endif
+
+#if defined(ERRATA_FIX_DCDC_FETCNT_SET_EN)
+ dcdcFetCntSet(true);
+#endif
+#if defined(ERRATA_FIX_DCDC_LNHS_BLOCK_EN)
+ dcdcHsFixLnBlock();
+#endif
+
+ for (i = 0; i < 4; i++) {
+#if defined(_EMU_EM4CTRL_EM4ENTRY_SHIFT)
+ EMU->EM4CTRL = em4seq2;
+ EMU->EM4CTRL = em4seq3;
+ }
+ EMU->EM4CTRL = em4seq2;
+#else
+ EMU->CTRL = em4seq2;
+ EMU->CTRL = em4seq3;
+ }
+ EMU->CTRL = em4seq2;
+#endif
+}
+
+#if defined(_EMU_EM4CTRL_MASK)
+/***************************************************************************//**
+ * @brief
+ * Enter energy mode 4 hibernate (EM4H).
+ *
+ * @note
+ * Retention of clocks and GPIO in EM4 can be configured using
+ * @ref EMU_EM4Init before calling this function.
+ ******************************************************************************/
+void EMU_EnterEM4H(void)
+{
+ BUS_RegBitWrite(&EMU->EM4CTRL, _EMU_EM4CTRL_EM4STATE_SHIFT, 1);
+ EMU_EnterEM4();
+}
+
+/***************************************************************************//**
+ * @brief
+ * Enter energy mode 4 shutoff (EM4S).
+ *
+ * @note
+ * Retention of clocks and GPIO in EM4 can be configured using
+ * @ref EMU_EM4Init before calling this function.
+ ******************************************************************************/
+void EMU_EnterEM4S(void)
+{
+ BUS_RegBitWrite(&EMU->EM4CTRL, _EMU_EM4CTRL_EM4STATE_SHIFT, 0);
+ EMU_EnterEM4();
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Power down memory block.
+ *
+ * @param[in] blocks
+ * Specifies a logical OR of bits indicating memory blocks to power down.
+ * Bit 0 selects block 1, bit 1 selects block 2, etc. Memory block 0 cannot
+ * be disabled. Please refer to the reference manual for available
+ * memory blocks for a device.
+ *
+ * @note
+ * Only a POR reset can power up the specified memory block(s) after powerdown.
+ *
+ * @deprecated
+ * This function is deprecated, use @ref EMU_RamPowerDown() instead which
+ * maps a user provided memory range into RAM blocks to power down.
+ ******************************************************************************/
+void EMU_MemPwrDown(uint32_t blocks)
+{
+#if defined(_EMU_MEMCTRL_MASK)
+ EMU->MEMCTRL = blocks & _EMU_MEMCTRL_MASK;
+#elif defined(_EMU_RAM0CTRL_MASK)
+ EMU->RAM0CTRL = blocks & _EMU_RAM0CTRL_MASK;
+#else
+ (void)blocks;
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Power down RAM memory blocks.
+ *
+ * @details
+ * This function will power down all the RAM blocks that are within a given
+ * range. The RAM block layout is different between device families, so this
+ * function can be used in a generic way to power down a RAM memory region
+ * which is known to be unused.
+ *
+ * This function will only power down blocks which are completely enclosed
+ * by the memory range given by [start, end).
+ *
+ * Here is an example of how to power down all RAM blocks except the first
+ * one. The first RAM block is special in that it cannot be powered down
+ * by the hardware. The size of this first RAM block is device specific
+ * see the reference manual to find the RAM block sizes.
+ *
+ * @code
+ * EMU_RamPowerDown(SRAM_BASE, SRAM_BASE + SRAM_SIZE);
+ * @endcode
+ *
+ * @note
+ * Only a POR reset can power up the specified memory block(s) after powerdown.
+ *
+ * @param[in] start
+ * The start address of the RAM region to power down. This address is
+ * inclusive.
+ *
+ * @param[in] end
+ * The end address of the RAM region to power down. This address is
+ * exclusive. If this parameter is 0, then all RAM blocks contained in the
+ * region from start to the upper RAM address will be powered down.
+ ******************************************************************************/
+void EMU_RamPowerDown(uint32_t start, uint32_t end)
+{
+ uint32_t mask = 0;
+
+ if (end == 0) {
+ end = SRAM_BASE + SRAM_SIZE;
+ }
+
+ // Check to see if something in RAM0 can be powered down
+ if (end > RAM0_END) {
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_84) // EFM32xG12 and EFR32xG12
+ // Block 0 is 16 kB and cannot be powered off
+ mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20004000) << 0; // Block 1, 16 kB
+ mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20008000) << 1; // Block 2, 16 kB
+ mask |= ADDRESS_NOT_IN_BLOCK(start, 0x2000C000) << 2; // Block 3, 16 kB
+ mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20010000) << 3; // Block 4, 64 kB
+#elif defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80) // EFM32xG1 and EFR32xG1
+ // Block 0 is 4 kB and cannot be powered off
+ mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20001000) << 0; // Block 1, 4 kB
+ mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20002000) << 1; // Block 2, 8 kB
+ mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20004000) << 2; // Block 3, 8 kB
+ mask |= ADDRESS_NOT_IN_BLOCK(start, 0x20006000) << 3; // Block 4, 7 kB
+#elif defined(RAM0_BLOCKS)
+ // These platforms have equally sized RAM blocks
+ for (int i = 1; i < RAM0_BLOCKS; i++) {
+ mask |= ADDRESS_NOT_IN_BLOCK(start, RAM_MEM_BASE + (i * RAM0_BLOCK_SIZE)) << (i - 1);
+ }
+#endif
+ }
+
+ // Power down the selected blocks
+#if defined(_EMU_MEMCTRL_MASK)
+ EMU->MEMCTRL = EMU->MEMCTRL | mask;
+#elif defined(_EMU_RAM0CTRL_MASK)
+ EMU->RAM0CTRL = EMU->RAM0CTRL | mask;
+#else
+ // These devices are unable to power down RAM blocks
+ (void) mask;
+ (void) start;
+#endif
+
+#if defined(RAM1_MEM_END)
+ mask = 0;
+ if (end > RAM1_MEM_END) {
+ for (int i = 0; i < RAM1_BLOCKS; i++) {
+ mask |= ADDRESS_NOT_IN_BLOCK(start, RAM1_MEM_BASE + (i * RAM1_BLOCK_SIZE)) << i;
+ }
+ }
+ EMU->RAM1CTRL |= mask;
+#endif
+
+#if defined(RAM2_MEM_END)
+ mask = 0;
+ if (end > RAM2_MEM_END) {
+ for (int i = 0; i < RAM2_BLOCKS; i++) {
+ mask |= ADDRESS_NOT_IN_BLOCK(start, RAM2_MEM_BASE + (i * RAM2_BLOCK_SIZE)) << i;
+ }
+ }
+ EMU->RAM2CTRL |= mask;
+#endif
+}
+
+#if defined(_EMU_EM23PERNORETAINCTRL_MASK)
+/***************************************************************************//**
+ * @brief
+ * Set EM2 3 peripheral retention control.
+ *
+ * @param[in] periMask
+ * Peripheral select mask. Use | operator to select multiple peripheral, for example
+ * @ref emuPeripheralRetention_LEUART0 | @ref emuPeripheralRetention_VDAC0.
+ * @param[in] enable
+ * Peripheral retention enable (true) or disable (false).
+ *
+ *
+ * @note
+ * Only peripheral retention disable is currently supported. Peripherals are
+ * enabled by default, and can only be disabled.
+ ******************************************************************************/
+void EMU_PeripheralRetention(EMU_PeripheralRetention_TypeDef periMask, bool enable)
+{
+ EFM_ASSERT(!enable);
+ EMU->EM23PERNORETAINCTRL = periMask & emuPeripheralRetention_ALL;
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Update EMU module with CMU oscillator selection/enable status.
+ *
+ * @deprecated
+ * Oscillator status is saved in @ref EMU_EnterEM2() and @ref EMU_EnterEM3().
+ ******************************************************************************/
+void EMU_UpdateOscConfig(void)
+{
+ emState(emState_Save);
+}
+
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+/***************************************************************************//**
+ * @brief
+ * Voltage scale in EM0 and 1 by clock frequency.
+ *
+ * @param[in] clockFrequency
+ * Use CMSIS HF clock if 0, or override to custom clock. Providing a
+ * custom clock frequency is required if using a non-standard HFXO
+ * frequency.
+ * @param[in] wait
+ * Wait for scaling to complete.
+ *
+ * @note
+ * This function is primarily needed by the @ref CMU module.
+ ******************************************************************************/
+void EMU_VScaleEM01ByClock(uint32_t clockFrequency, bool wait)
+{
+ uint32_t hfSrcClockFrequency;
+ uint32_t hfPresc = 1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
+ >> _CMU_HFPRESC_PRESC_SHIFT);
+
+ /* VSCALE frequency is HFSRCCLK */
+ if (clockFrequency == 0) {
+ hfSrcClockFrequency = SystemHFClockGet() * hfPresc;
+ } else {
+ hfSrcClockFrequency = clockFrequency;
+ }
+
+ /* Apply EM0 and 1 voltage scaling command. */
+ if (vScaleEM01Config.vScaleEM01LowPowerVoltageEnable
+ && (hfSrcClockFrequency < CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX)) {
+ EMU_VScaleEM01(emuVScaleEM01_LowPower, wait);
+ } else {
+ EMU_VScaleEM01(emuVScaleEM01_HighPerformance, wait);
+ }
+}
+#endif
+
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+/***************************************************************************//**
+ * @brief
+ * Force voltage scaling in EM0 and 1 to a specific voltage level.
+ *
+ * @param[in] voltage
+ * Target VSCALE voltage level.
+ * @param[in] wait
+ * Wait for scaling to complate.
+ *
+ * @note
+ * This function is useful for upscaling before programming Flash from @ref MSC,
+ * and downscaling after programming is done. Flash programming is only supported
+ * at @ref emuVScaleEM01_HighPerformance.
+ *
+ * @note
+ * This function ignores @ref vScaleEM01LowPowerVoltageEnable set from @ref
+ * EMU_EM01Init().
+ ******************************************************************************/
+void EMU_VScaleEM01(EMU_VScaleEM01_TypeDef voltage, bool wait)
+{
+ uint32_t hfSrcClockFrequency;
+ uint32_t hfPresc = 1U + ((CMU->HFPRESC & _CMU_HFPRESC_PRESC_MASK)
+ >> _CMU_HFPRESC_PRESC_SHIFT);
+ uint32_t hfFreq = SystemHFClockGet();
+ EMU_VScaleEM01_TypeDef current = EMU_VScaleGet();
+
+ if (current == voltage) {
+ /* Voltage is already at correct level. */
+ return;
+ }
+
+ hfSrcClockFrequency = hfFreq * hfPresc;
+
+ if (voltage == emuVScaleEM01_LowPower) {
+ EFM_ASSERT(hfSrcClockFrequency <= CMU_VSCALEEM01_LOWPOWER_VOLTAGE_CLOCK_MAX);
+ /* Update wait states before scaling down voltage */
+ CMU_UpdateWaitStates(hfFreq, emuVScaleEM01_LowPower);
+ }
+
+ EMU->CMD = vScaleEM01Cmd(voltage);
+
+ if (voltage == emuVScaleEM01_HighPerformance) {
+ /* Update wait states after scaling up voltage */
+ CMU_UpdateWaitStates(hfFreq, emuVScaleEM01_HighPerformance);
+ }
+
+ if (wait) {
+ EMU_VScaleWait();
+ }
+}
+#endif
+
+#if defined(_EMU_CMD_EM01VSCALE0_MASK)
+/***************************************************************************//**
+ * @brief
+ * Update EMU module with Energy Mode 0 and 1 configuration
+ *
+ * @param[in] em01Init
+ * Energy Mode 0 and 1 configuration structure
+ ******************************************************************************/
+void EMU_EM01Init(const EMU_EM01Init_TypeDef *em01Init)
+{
+ vScaleEM01Config.vScaleEM01LowPowerVoltageEnable =
+ em01Init->vScaleEM01LowPowerVoltageEnable;
+ EMU_VScaleEM01ByClock(0, true);
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Update EMU module with Energy Mode 2 and 3 configuration
+ *
+ * @param[in] em23Init
+ * Energy Mode 2 and 3 configuration structure
+ ******************************************************************************/
+void EMU_EM23Init(const EMU_EM23Init_TypeDef *em23Init)
+{
+#if defined(_EMU_CTRL_EMVREG_MASK)
+ EMU->CTRL = em23Init->em23VregFullEn ? (EMU->CTRL | EMU_CTRL_EMVREG)
+ : (EMU->CTRL & ~EMU_CTRL_EMVREG);
+#elif defined(_EMU_CTRL_EM23VREG_MASK)
+ EMU->CTRL = em23Init->em23VregFullEn ? (EMU->CTRL | EMU_CTRL_EM23VREG)
+ : (EMU->CTRL & ~EMU_CTRL_EM23VREG);
+#else
+ (void)em23Init;
+#endif
+
+#if defined(_EMU_CTRL_EM23VSCALE_MASK)
+ EMU->CTRL = (EMU->CTRL & ~_EMU_CTRL_EM23VSCALE_MASK)
+ | (em23Init->vScaleEM23Voltage << _EMU_CTRL_EM23VSCALE_SHIFT);
+#endif
+}
+
+#if defined(_EMU_EM4CONF_MASK) || defined(_EMU_EM4CTRL_MASK)
+/***************************************************************************//**
+ * @brief
+ * Update EMU module with Energy Mode 4 configuration
+ *
+ * @param[in] em4Init
+ * Energy Mode 4 configuration structure
+ ******************************************************************************/
+void EMU_EM4Init(const EMU_EM4Init_TypeDef *em4Init)
+{
+#if defined(_EMU_EM4CONF_MASK)
+ /* Init for platforms with EMU->EM4CONF register */
+ uint32_t em4conf = EMU->EM4CONF;
+
+ /* Clear fields that will be reconfigured */
+ em4conf &= ~(_EMU_EM4CONF_LOCKCONF_MASK
+ | _EMU_EM4CONF_OSC_MASK
+ | _EMU_EM4CONF_BURTCWU_MASK
+ | _EMU_EM4CONF_VREGEN_MASK);
+
+ /* Configure new settings */
+ em4conf |= (em4Init->lockConfig << _EMU_EM4CONF_LOCKCONF_SHIFT)
+ | (em4Init->osc)
+ | (em4Init->buRtcWakeup << _EMU_EM4CONF_BURTCWU_SHIFT)
+ | (em4Init->vreg << _EMU_EM4CONF_VREGEN_SHIFT);
+
+ /* Apply configuration. Note that lock can be set after this stage. */
+ EMU->EM4CONF = em4conf;
+
+#elif defined(_EMU_EM4CTRL_MASK)
+ /* Init for platforms with EMU->EM4CTRL register */
+
+ uint32_t em4ctrl = EMU->EM4CTRL;
+
+ em4ctrl &= ~(_EMU_EM4CTRL_RETAINLFXO_MASK
+ | _EMU_EM4CTRL_RETAINLFRCO_MASK
+ | _EMU_EM4CTRL_RETAINULFRCO_MASK
+ | _EMU_EM4CTRL_EM4STATE_MASK
+ | _EMU_EM4CTRL_EM4IORETMODE_MASK);
+
+ em4ctrl |= (em4Init->retainLfxo ? EMU_EM4CTRL_RETAINLFXO : 0)
+ | (em4Init->retainLfrco ? EMU_EM4CTRL_RETAINLFRCO : 0)
+ | (em4Init->retainUlfrco ? EMU_EM4CTRL_RETAINULFRCO : 0)
+ | (em4Init->em4State ? EMU_EM4CTRL_EM4STATE_EM4H : 0)
+ | (em4Init->pinRetentionMode);
+
+ EMU->EM4CTRL = em4ctrl;
+#endif
+
+#if defined(_EMU_CTRL_EM4HVSCALE_MASK)
+ EMU->CTRL = (EMU->CTRL & ~_EMU_CTRL_EM4HVSCALE_MASK)
+ | (em4Init->vScaleEM4HVoltage << _EMU_CTRL_EM4HVSCALE_SHIFT);
+#endif
+}
+#endif
+
+#if defined(BU_PRESENT)
+/***************************************************************************//**
+ * @brief
+ * Configure Backup Power Domain settings
+ *
+ * @param[in] bupdInit
+ * Backup power domain initialization structure
+ ******************************************************************************/
+void EMU_BUPDInit(const EMU_BUPDInit_TypeDef *bupdInit)
+{
+ uint32_t reg;
+
+ /* Set power connection configuration */
+ reg = EMU->PWRCONF & ~(_EMU_PWRCONF_PWRRES_MASK
+ | _EMU_PWRCONF_VOUTSTRONG_MASK
+ | _EMU_PWRCONF_VOUTMED_MASK
+ | _EMU_PWRCONF_VOUTWEAK_MASK);
+
+ reg |= bupdInit->resistor
+ | (bupdInit->voutStrong << _EMU_PWRCONF_VOUTSTRONG_SHIFT)
+ | (bupdInit->voutMed << _EMU_PWRCONF_VOUTMED_SHIFT)
+ | (bupdInit->voutWeak << _EMU_PWRCONF_VOUTWEAK_SHIFT);
+
+ EMU->PWRCONF = reg;
+
+ /* Set backup domain inactive mode configuration */
+ reg = EMU->BUINACT & ~(_EMU_BUINACT_PWRCON_MASK);
+ reg |= (bupdInit->inactivePower);
+ EMU->BUINACT = reg;
+
+ /* Set backup domain active mode configuration */
+ reg = EMU->BUACT & ~(_EMU_BUACT_PWRCON_MASK);
+ reg |= (bupdInit->activePower);
+ EMU->BUACT = reg;
+
+ /* Set power control configuration */
+ reg = EMU->BUCTRL & ~(_EMU_BUCTRL_PROBE_MASK
+ | _EMU_BUCTRL_BODCAL_MASK
+ | _EMU_BUCTRL_STATEN_MASK
+ | _EMU_BUCTRL_EN_MASK);
+
+ /* Note use of ->enable to both enable BUPD, use BU_VIN pin input and
+ release reset */
+ reg |= bupdInit->probe
+ | (bupdInit->bodCal << _EMU_BUCTRL_BODCAL_SHIFT)
+ | (bupdInit->statusPinEnable << _EMU_BUCTRL_STATEN_SHIFT)
+ | (bupdInit->enable << _EMU_BUCTRL_EN_SHIFT);
+
+ /* Enable configuration */
+ EMU->BUCTRL = reg;
+
+ /* If enable is true, enable BU_VIN input power pin, if not disable it */
+ EMU_BUPinEnable(bupdInit->enable);
+
+ /* If enable is true, release BU reset, if not keep reset asserted */
+ BUS_RegBitWrite(&(RMU->CTRL), _RMU_CTRL_BURSTEN_SHIFT, !bupdInit->enable);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Configure Backup Power Domain BOD Threshold value
+ * @note
+ * These values are precalibrated
+ * @param[in] mode Active or Inactive mode
+ * @param[in] value
+ ******************************************************************************/
+void EMU_BUThresholdSet(EMU_BODMode_TypeDef mode, uint32_t value)
+{
+ EFM_ASSERT(value < 8);
+ EFM_ASSERT(value <= (_EMU_BUACT_BUEXTHRES_MASK >> _EMU_BUACT_BUEXTHRES_SHIFT));
+
+ switch (mode) {
+ case emuBODMode_Active:
+ EMU->BUACT = (EMU->BUACT & ~_EMU_BUACT_BUEXTHRES_MASK)
+ | (value << _EMU_BUACT_BUEXTHRES_SHIFT);
+ break;
+ case emuBODMode_Inactive:
+ EMU->BUINACT = (EMU->BUINACT & ~_EMU_BUINACT_BUENTHRES_MASK)
+ | (value << _EMU_BUINACT_BUENTHRES_SHIFT);
+ break;
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Configure Backup Power Domain BOD Threshold Range
+ * @note
+ * These values are precalibrated
+ * @param[in] mode Active or Inactive mode
+ * @param[in] value
+ ******************************************************************************/
+void EMU_BUThresRangeSet(EMU_BODMode_TypeDef mode, uint32_t value)
+{
+ EFM_ASSERT(value < 4);
+ EFM_ASSERT(value <= (_EMU_BUACT_BUEXRANGE_MASK >> _EMU_BUACT_BUEXRANGE_SHIFT));
+
+ switch (mode) {
+ case emuBODMode_Active:
+ EMU->BUACT = (EMU->BUACT & ~_EMU_BUACT_BUEXRANGE_MASK)
+ | (value << _EMU_BUACT_BUEXRANGE_SHIFT);
+ break;
+ case emuBODMode_Inactive:
+ EMU->BUINACT = (EMU->BUINACT & ~_EMU_BUINACT_BUENRANGE_MASK)
+ | (value << _EMU_BUINACT_BUENRANGE_SHIFT);
+ break;
+ }
+}
+#endif
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+#if defined(_EMU_DCDCCTRL_MASK)
+/* Translate fields with different names across platform generations to common names. */
+#if defined(_EMU_DCDCMISCCTRL_LPCMPBIAS_MASK)
+#define _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_MASK _EMU_DCDCMISCCTRL_LPCMPBIAS_MASK
+#define _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT _EMU_DCDCMISCCTRL_LPCMPBIAS_SHIFT
+#elif defined(_EMU_DCDCMISCCTRL_LPCMPBIASEM234H_MASK)
+#define _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_MASK _EMU_DCDCMISCCTRL_LPCMPBIASEM234H_MASK
+#define _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT _EMU_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT
+#endif
+#if defined(_EMU_DCDCLPCTRL_LPCMPHYSSEL_MASK)
+#define _GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK _EMU_DCDCLPCTRL_LPCMPHYSSEL_MASK
+#define _GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_SHIFT _EMU_DCDCLPCTRL_LPCMPHYSSEL_SHIFT
+#elif defined(_EMU_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK)
+#define _GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK _EMU_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK
+#define _GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_SHIFT _EMU_DCDCLPCTRL_LPCMPHYSSELEM234H_SHIFT
+#endif
+
+/* Internal DCDC trim modes. */
+typedef enum {
+ dcdcTrimMode_EM234H_LP = 0,
+#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
+ dcdcTrimMode_EM01_LP,
+#endif
+ dcdcTrimMode_LN,
+} dcdcTrimMode_TypeDef;
+
+/***************************************************************************//**
+ * @brief
+ * Load DCDC calibration constants from DI page. Const means calibration
+ * data that does not change depending on other configuration parameters.
+ *
+ * @return
+ * False if calibration registers are locked
+ ******************************************************************************/
+static bool dcdcConstCalibrationLoad(void)
+{
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+ uint32_t val;
+ volatile uint32_t *reg;
+
+ /* DI calib data in flash */
+ volatile uint32_t* const diCal_EMU_DCDCLNFREQCTRL = (volatile uint32_t *)(0x0FE08038);
+ volatile uint32_t* const diCal_EMU_DCDCLNVCTRL = (volatile uint32_t *)(0x0FE08040);
+ volatile uint32_t* const diCal_EMU_DCDCLPCTRL = (volatile uint32_t *)(0x0FE08048);
+ volatile uint32_t* const diCal_EMU_DCDCLPVCTRL = (volatile uint32_t *)(0x0FE08050);
+ volatile uint32_t* const diCal_EMU_DCDCTRIM0 = (volatile uint32_t *)(0x0FE08058);
+ volatile uint32_t* const diCal_EMU_DCDCTRIM1 = (volatile uint32_t *)(0x0FE08060);
+
+ if (DEVINFO->DCDCLPVCTRL0 != UINT_MAX) {
+ val = *(diCal_EMU_DCDCLNFREQCTRL + 1);
+ reg = (volatile uint32_t *)*diCal_EMU_DCDCLNFREQCTRL;
+ *reg = val;
+
+ val = *(diCal_EMU_DCDCLNVCTRL + 1);
+ reg = (volatile uint32_t *)*diCal_EMU_DCDCLNVCTRL;
+ *reg = val;
+
+ val = *(diCal_EMU_DCDCLPCTRL + 1);
+ reg = (volatile uint32_t *)*diCal_EMU_DCDCLPCTRL;
+ *reg = val;
+
+ val = *(diCal_EMU_DCDCLPVCTRL + 1);
+ reg = (volatile uint32_t *)*diCal_EMU_DCDCLPVCTRL;
+ *reg = val;
+
+ val = *(diCal_EMU_DCDCTRIM0 + 1);
+ reg = (volatile uint32_t *)*diCal_EMU_DCDCTRIM0;
+ *reg = val;
+
+ val = *(diCal_EMU_DCDCTRIM1 + 1);
+ reg = (volatile uint32_t *)*diCal_EMU_DCDCTRIM1;
+ *reg = val;
+
+ return true;
+ }
+ EFM_ASSERT(false);
+ /* Return when assertions are disabled */
+ return false;
+
+#else
+ return true;
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set recommended and validated current optimization and timing settings
+ *
+ ******************************************************************************/
+static void dcdcValidatedConfigSet(void)
+{
+/* Disable LP mode hysterysis in the state machine control */
+#define EMU_DCDCMISCCTRL_LPCMPHYSDIS (0x1UL << 1)
+/* Comparator threshold on the high side */
+#define EMU_DCDCMISCCTRL_LPCMPHYSHI (0x1UL << 2)
+#define EMU_DCDCSMCTRL (*(volatile uint32_t *)(EMU_BASE + 0x44))
+
+ uint32_t lnForceCcm;
+
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+ uint32_t dcdcTiming;
+ SYSTEM_ChipRevision_TypeDef rev;
+#endif
+
+ /* Enable duty cycling of the bias */
+ EMU->DCDCLPCTRL |= EMU_DCDCLPCTRL_LPVREFDUTYEN;
+
+ /* Set low-noise RCO for LNFORCECCM configuration
+ * LNFORCECCM is default 1 for EFR32
+ * LNFORCECCM is default 0 for EFM32
+ */
+ lnForceCcm = BUS_RegBitRead(&EMU->DCDCMISCCTRL, _EMU_DCDCMISCCTRL_LNFORCECCM_SHIFT);
+ if (lnForceCcm) {
+ /* 7MHz is recommended for LNFORCECCM = 1 */
+ EMU_DCDCLnRcoBandSet(emuDcdcLnRcoBand_7MHz);
+ } else {
+ /* 3MHz is recommended for LNFORCECCM = 0 */
+ EMU_DCDCLnRcoBandSet(emuDcdcLnRcoBand_3MHz);
+ }
+
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+ EMU->DCDCTIMING &= ~_EMU_DCDCTIMING_DUTYSCALE_MASK;
+ EMU->DCDCMISCCTRL |= EMU_DCDCMISCCTRL_LPCMPHYSDIS
+ | EMU_DCDCMISCCTRL_LPCMPHYSHI;
+
+ SYSTEM_ChipRevisionGet(&rev);
+ if ((rev.major == 1)
+ && (rev.minor < 3)
+ && (errataFixDcdcHsState == errataFixDcdcHsInit)) {
+ /* LPCMPWAITDIS = 1 */
+ EMU_DCDCSMCTRL |= 1;
+
+ dcdcTiming = EMU->DCDCTIMING;
+ dcdcTiming &= ~(_EMU_DCDCTIMING_LPINITWAIT_MASK
+ | _EMU_DCDCTIMING_LNWAIT_MASK
+ | _EMU_DCDCTIMING_BYPWAIT_MASK);
+
+ dcdcTiming |= ((180 << _EMU_DCDCTIMING_LPINITWAIT_SHIFT)
+ | (12 << _EMU_DCDCTIMING_LNWAIT_SHIFT)
+ | (180 << _EMU_DCDCTIMING_BYPWAIT_SHIFT));
+ EMU->DCDCTIMING = dcdcTiming;
+
+ errataFixDcdcHsState = errataFixDcdcHsTrimSet;
+ }
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Compute current limiters:
+ * LNCLIMILIMSEL: LN current limiter threshold
+ * LPCLIMILIMSEL: LP current limiter threshold
+ * DCDCZDETCTRL: zero detector limiter threshold
+ ******************************************************************************/
+static void currentLimitersUpdate(void)
+{
+ uint32_t lncLimSel;
+ uint32_t zdetLimSel;
+ uint32_t pFetCnt;
+ uint16_t maxReverseCurrent_mA;
+
+ /* 80mA as recommended peak in Application Note AN0948.
+ The peak current is the average current plus 50% of the current ripple.
+ Hence, a 14mA average current is recommended in LP mode. Since LP PFETCNT is also
+ a constant, we get lpcLimImSel = 1. The following calculation is provided
+ for documentation only. */
+ const uint32_t lpcLim = (((14 + 40) + ((14 + 40) / 2))
+ / (5 * (DCDC_LP_PFET_CNT + 1)))
+ - 1;
+ const uint32_t lpcLimSel = lpcLim << _EMU_DCDCMISCCTRL_LPCLIMILIMSEL_SHIFT;
+
+ /* Get enabled PFETs */
+ pFetCnt = (EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_PFETCNT_MASK)
+ >> _EMU_DCDCMISCCTRL_PFETCNT_SHIFT;
+
+ /* Compute LN current limiter threshold from nominal user input current and
+ LN PFETCNT as described in the register description for
+ EMU_DCDCMISCCTRL_LNCLIMILIMSEL. */
+ lncLimSel = (((dcdcMaxCurrent_mA + 40) + ((dcdcMaxCurrent_mA + 40) / 2))
+ / (5 * (pFetCnt + 1)))
+ - 1;
+
+ /* Saturate the register field value */
+ lncLimSel = SL_MIN(lncLimSel,
+ _EMU_DCDCMISCCTRL_LNCLIMILIMSEL_MASK
+ >> _EMU_DCDCMISCCTRL_LNCLIMILIMSEL_SHIFT);
+
+ lncLimSel <<= _EMU_DCDCMISCCTRL_LNCLIMILIMSEL_SHIFT;
+
+ /* Check for overflow */
+ EFM_ASSERT((lncLimSel & ~_EMU_DCDCMISCCTRL_LNCLIMILIMSEL_MASK) == 0x0);
+ EFM_ASSERT((lpcLimSel & ~_EMU_DCDCMISCCTRL_LPCLIMILIMSEL_MASK) == 0x0);
+
+ EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_LNCLIMILIMSEL_MASK
+ | _EMU_DCDCMISCCTRL_LPCLIMILIMSEL_MASK))
+ | (lncLimSel | lpcLimSel);
+
+ /* Compute reverse current limit threshold for the zero detector from user input
+ maximum reverse current and LN PFETCNT as described in the register description
+ for EMU_DCDCZDETCTRL_ZDETILIMSEL. */
+ if (dcdcReverseCurrentControl >= 0) {
+ /* If dcdcReverseCurrentControl < 0, then EMU_DCDCZDETCTRL_ZDETILIMSEL is "don't care" */
+ maxReverseCurrent_mA = (uint16_t)dcdcReverseCurrentControl;
+
+ zdetLimSel = ( ((maxReverseCurrent_mA + 40) + ((maxReverseCurrent_mA + 40) / 2))
+ / ((2 * (pFetCnt + 1)) + ((pFetCnt + 1) / 2)) );
+ /* Saturate the register field value */
+ zdetLimSel = SL_MIN(zdetLimSel,
+ _EMU_DCDCZDETCTRL_ZDETILIMSEL_MASK
+ >> _EMU_DCDCZDETCTRL_ZDETILIMSEL_SHIFT);
+
+ zdetLimSel <<= _EMU_DCDCZDETCTRL_ZDETILIMSEL_SHIFT;
+
+ /* Check for overflow */
+ EFM_ASSERT((zdetLimSel & ~_EMU_DCDCZDETCTRL_ZDETILIMSEL_MASK) == 0x0);
+
+ EMU->DCDCZDETCTRL = (EMU->DCDCZDETCTRL & ~_EMU_DCDCZDETCTRL_ZDETILIMSEL_MASK)
+ | zdetLimSel;
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set static variables that hold the user set maximum peak current
+ * and reverse current. Update limiters.
+ *
+ * @param[in] maxCurrent_mA
+ * Set the maximum peak current that the DCDC can draw from the power source.
+ * @param[in] reverseCurrentControl
+ * Reverse current control as defined by
+ * @ref EMU_DcdcLnReverseCurrentControl_TypeDef. Positive values have unit mA.
+ ******************************************************************************/
+static void userCurrentLimitsSet(uint32_t maxCurrent_mA,
+ EMU_DcdcLnReverseCurrentControl_TypeDef reverseCurrentControl)
+{
+ dcdcMaxCurrent_mA = maxCurrent_mA;
+ dcdcReverseCurrentControl = reverseCurrentControl;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set DCDC low noise compensator control register
+ *
+ * @param[in] comp
+ * Low-noise mode compensator trim setpoint
+ ******************************************************************************/
+static void compCtrlSet(EMU_DcdcLnCompCtrl_TypeDef comp)
+{
+ switch (comp) {
+ case emuDcdcLnCompCtrl_1u0F:
+ EMU->DCDCLNCOMPCTRL = 0x57204077UL;
+ break;
+
+ case emuDcdcLnCompCtrl_4u7F:
+ EMU->DCDCLNCOMPCTRL = 0xB7102137UL;
+ break;
+
+ default:
+ EFM_ASSERT(false);
+ break;
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Load EMU_DCDCLPCTRL_LPCMPHYSSEL depending on LP bias, LP feedback
+ * attenuation and DEVINFOREV.
+ *
+ * @param[in] lpAttenuation
+ * LP feedback attenuation.
+ * @param[in] lpCmpBias
+ * lpCmpBias selection.
+ * @param[in] trimMode
+ * DCDC trim mode.
+ ******************************************************************************/
+static bool lpCmpHystCalibrationLoad(bool lpAttenuation,
+ uint8_t lpCmpBias,
+ dcdcTrimMode_TypeDef trimMode)
+{
+ uint32_t lpcmpHystSel;
+
+ /* Get calib data revision */
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+ uint8_t devinfoRev = SYSTEM_GetDevinfoRev();
+
+ /* Load LPATT indexed calibration data */
+ if (devinfoRev < 4)
+#else
+ /* Format change not present of newer families. */
+ if (false)
+#endif
+ {
+ lpcmpHystSel = DEVINFO->DCDCLPCMPHYSSEL0;
+
+ if (lpAttenuation) {
+ lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT1_MASK)
+ >> _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT1_SHIFT;
+ } else {
+ lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT0_MASK)
+ >> _DEVINFO_DCDCLPCMPHYSSEL0_LPCMPHYSSELLPATT0_SHIFT;
+ }
+ } else {
+ /* devinfoRev >= 4: load LPCMPBIAS indexed calibration data */
+ lpcmpHystSel = DEVINFO->DCDCLPCMPHYSSEL1;
+ switch (lpCmpBias) {
+ case 0:
+ lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS0_MASK)
+ >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS0_SHIFT;
+ break;
+
+ case 1:
+ lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS1_MASK)
+ >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS1_SHIFT;
+ break;
+
+ case 2:
+ lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS2_MASK)
+ >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS2_SHIFT;
+ break;
+
+ case 3:
+ lpcmpHystSel = (lpcmpHystSel & _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS3_MASK)
+ >> _DEVINFO_DCDCLPCMPHYSSEL1_LPCMPHYSSELLPCMPBIAS3_SHIFT;
+ break;
+
+ default:
+ EFM_ASSERT(false);
+ /* Return when assertions are disabled */
+ return false;
+ }
+ }
+
+ /* Set trims */
+ if (trimMode == dcdcTrimMode_EM234H_LP) {
+ /* Make sure the sel value is within the field range. */
+ lpcmpHystSel <<= _GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_SHIFT;
+ if (lpcmpHystSel & ~_GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK) {
+ EFM_ASSERT(false);
+ /* Return when assertions are disabled */
+ return false;
+ }
+ EMU->DCDCLPCTRL = (EMU->DCDCLPCTRL & ~_GENERIC_DCDCLPCTRL_LPCMPHYSSELEM234H_MASK) | lpcmpHystSel;
+ }
+
+#if defined(_EMU_DCDCLPEM01CFG_LPCMPHYSSELEM01_MASK)
+ if (trimMode == dcdcTrimMode_EM01_LP) {
+ /* Make sure the sel value is within the field range. */
+ lpcmpHystSel <<= _EMU_DCDCLPEM01CFG_LPCMPHYSSELEM01_SHIFT;
+ if (lpcmpHystSel & ~_EMU_DCDCLPEM01CFG_LPCMPHYSSELEM01_MASK) {
+ EFM_ASSERT(false);
+ /* Return when assertions are disabled */
+ return false;
+ }
+ EMU->DCDCLPEM01CFG = (EMU->DCDCLPEM01CFG & ~_EMU_DCDCLPEM01CFG_LPCMPHYSSELEM01_MASK) | lpcmpHystSel;
+ }
+#endif
+
+ return true;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Load LPVREF low and high from DEVINFO.
+ *
+ * @param[out] vrefL
+ * LPVREF low from DEVINFO.
+ * @param[out] vrefH
+ * LPVREF high from DEVINFO.
+ * @param[in] lpAttenuation
+ * LP feedback attenuation.
+ * @param[in] lpcmpBias
+ * lpcmpBias to lookup in DEVINFO.
+ ******************************************************************************/
+static void lpGetDevinfoVrefLowHigh(uint32_t *vrefL,
+ uint32_t *vrefH,
+ bool lpAttenuation,
+ uint8_t lpcmpBias)
+{
+ uint32_t vrefLow = 0;
+ uint32_t vrefHigh = 0;
+
+ /* Find VREF high and low in DEVINFO indexed by LPCMPBIAS (lpcmpBias)
+ and LPATT (lpAttenuation) */
+ uint32_t switchVal = (lpcmpBias << 8) | (lpAttenuation ? 1 : 0);
+ switch (switchVal) {
+ case ((0 << 8) | 1):
+ vrefLow = DEVINFO->DCDCLPVCTRL2;
+ vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS0_MASK)
+ >> _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS0_SHIFT;
+ vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS0_MASK)
+ >> _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS0_SHIFT;
+ break;
+
+ case ((1 << 8) | 1):
+ vrefLow = DEVINFO->DCDCLPVCTRL2;
+ vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS1_MASK)
+ >> _DEVINFO_DCDCLPVCTRL2_3V0LPATT1LPCMPBIAS1_SHIFT;
+ vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS1_MASK)
+ >> _DEVINFO_DCDCLPVCTRL2_1V8LPATT1LPCMPBIAS1_SHIFT;
+ break;
+
+ case ((2 << 8) | 1):
+ vrefLow = DEVINFO->DCDCLPVCTRL3;
+ vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS2_MASK)
+ >> _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS2_SHIFT;
+ vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS2_MASK)
+ >> _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS2_SHIFT;
+ break;
+
+ case ((3 << 8) | 1):
+ vrefLow = DEVINFO->DCDCLPVCTRL3;
+ vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS3_MASK)
+ >> _DEVINFO_DCDCLPVCTRL3_3V0LPATT1LPCMPBIAS3_SHIFT;
+ vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS3_MASK)
+ >> _DEVINFO_DCDCLPVCTRL3_1V8LPATT1LPCMPBIAS3_SHIFT;
+ break;
+
+ case ((0 << 8) | 0):
+ vrefLow = DEVINFO->DCDCLPVCTRL0;
+ vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS0_MASK)
+ >> _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS0_SHIFT;
+ vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS0_MASK)
+ >> _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS0_SHIFT;
+ break;
+
+ case ((1 << 8) | 0):
+ vrefLow = DEVINFO->DCDCLPVCTRL0;
+ vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS1_MASK)
+ >> _DEVINFO_DCDCLPVCTRL0_1V8LPATT0LPCMPBIAS1_SHIFT;
+ vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS1_MASK)
+ >> _DEVINFO_DCDCLPVCTRL0_1V2LPATT0LPCMPBIAS1_SHIFT;
+ break;
+
+ case ((2 << 8) | 0):
+ vrefLow = DEVINFO->DCDCLPVCTRL1;
+ vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS2_MASK)
+ >> _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS2_SHIFT;
+ vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS2_MASK)
+ >> _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS2_SHIFT;
+ break;
+
+ case ((3 << 8) | 0):
+ vrefLow = DEVINFO->DCDCLPVCTRL1;
+ vrefHigh = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS3_MASK)
+ >> _DEVINFO_DCDCLPVCTRL1_1V8LPATT0LPCMPBIAS3_SHIFT;
+ vrefLow = (vrefLow & _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS3_MASK)
+ >> _DEVINFO_DCDCLPVCTRL1_1V2LPATT0LPCMPBIAS3_SHIFT;
+ break;
+
+ default:
+ EFM_ASSERT(false);
+ break;
+ }
+ *vrefL = vrefLow;
+ *vrefH = vrefHigh;
+}
+
+/** @endcond */
+
+/***************************************************************************//**
+ * @brief
+ * Set DCDC regulator operating mode
+ *
+ * @param[in] dcdcMode
+ * DCDC mode
+ ******************************************************************************/
+void EMU_DCDCModeSet(EMU_DcdcMode_TypeDef dcdcMode)
+{
+ uint32_t currentDcdcMode;
+
+ /* Wait for any previous write sync to complete and read DCDC mode. */
+ while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
+ currentDcdcMode = (EMU->DCDCCTRL & _EMU_DCDCCTRL_DCDCMODE_MASK);
+
+ /* Enable bypass current limiter when not in bypass mode to prevent
+ excessive current between VREGVDD and DVDD supplies when reentering bypass mode. */
+ if (currentDcdcMode != EMU_DCDCCTRL_DCDCMODE_BYPASS) {
+ BUS_RegBitWrite(&EMU->DCDCCLIMCTRL, _EMU_DCDCCLIMCTRL_BYPLIMEN_SHIFT, 1);
+ }
+
+ if ((EMU_DcdcMode_TypeDef)currentDcdcMode == dcdcMode) {
+ /* Mode already set. If already in bypass, make sure bypass current limiter
+ is disabled. */
+ if (dcdcMode == emuDcdcMode_Bypass) {
+ BUS_RegBitWrite(&EMU->DCDCCLIMCTRL, _EMU_DCDCCLIMCTRL_BYPLIMEN_SHIFT, 0);
+ }
+ return;
+ }
+
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+
+ /* Fix for errata DCDC_E203 */
+ if ((currentDcdcMode == EMU_DCDCCTRL_DCDCMODE_BYPASS)
+ && (dcdcMode == emuDcdcMode_LowNoise)) {
+ errataFixDcdcHsState = errataFixDcdcHsBypassLn;
+ }
+
+#else
+
+ /* Fix for errata DCDC_E204 */
+ if (((currentDcdcMode == EMU_DCDCCTRL_DCDCMODE_OFF) || (currentDcdcMode == EMU_DCDCCTRL_DCDCMODE_BYPASS))
+ && ((dcdcMode == emuDcdcMode_LowPower) || (dcdcMode == emuDcdcMode_LowNoise))) {
+ /* Always start in LOWNOISE mode and then switch to LOWPOWER mode once LOWNOISE startup is complete. */
+ EMU_IntClear(EMU_IFC_DCDCLNRUNNING);
+ while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
+ EMU->DCDCCTRL = (EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODE_MASK) | EMU_DCDCCTRL_DCDCMODE_LOWNOISE;
+ while (!(EMU_IntGet() & EMU_IF_DCDCLNRUNNING)) ;
+ }
+#endif
+
+ /* Set user requested mode. */
+ while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
+ EMU->DCDCCTRL = (EMU->DCDCCTRL & ~_EMU_DCDCCTRL_DCDCMODE_MASK) | dcdcMode;
+
+ /* Disable bypass current limiter after bypass mode is entered.
+ Enable the limiter if any other mode is entered. */
+ while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
+ BUS_RegBitWrite(&EMU->DCDCCLIMCTRL, _EMU_DCDCCLIMCTRL_BYPLIMEN_SHIFT, dcdcMode == emuDcdcMode_Bypass ? 0 : 1);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set DCDC LN regulator conduction mode
+ *
+ * @param[in] conductionMode
+ * DCDC LN conduction mode.
+ * @param[in] rcoDefaultSet
+ * The default DCDC RCO band for the conductionMode will be used if true.
+ * Otherwise the current RCO configuration is used.
+ ******************************************************************************/
+void EMU_DCDCConductionModeSet(EMU_DcdcConductionMode_TypeDef conductionMode, bool rcoDefaultSet)
+{
+ EMU_DcdcMode_TypeDef currentDcdcMode
+ = (EMU_DcdcMode_TypeDef)(EMU->DCDCCTRL & _EMU_DCDCCTRL_DCDCMODE_MASK);
+ EMU_DcdcLnRcoBand_TypeDef rcoBand
+ = (EMU_DcdcLnRcoBand_TypeDef)((EMU->DCDCLNFREQCTRL & _EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
+ >> _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT);
+
+ /* Set bypass mode and wait for bypass mode to settle before
+ EMU_DCDCMISCCTRL_LNFORCECCM is set. Restore current DCDC mode. */
+ EMU_IntClear(EMU_IFC_DCDCINBYPASS);
+ EMU_DCDCModeSet(emuDcdcMode_Bypass);
+ while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
+ while (!(EMU_IntGet() & EMU_IF_DCDCINBYPASS)) ;
+ if (conductionMode == emuDcdcConductionMode_DiscontinuousLN) {
+ EMU->DCDCMISCCTRL &= ~EMU_DCDCMISCCTRL_LNFORCECCM;
+ if (rcoDefaultSet) {
+ EMU_DCDCLnRcoBandSet(emuDcdcLnRcoBand_3MHz);
+ } else {
+ /* emuDcdcConductionMode_DiscontinuousLN supports up to 4MHz LN RCO. */
+ EFM_ASSERT(rcoBand <= emuDcdcLnRcoBand_4MHz);
+ }
+ } else {
+ EMU->DCDCMISCCTRL |= EMU_DCDCMISCCTRL_LNFORCECCM;
+ if (rcoDefaultSet) {
+ EMU_DCDCLnRcoBandSet(emuDcdcLnRcoBand_7MHz);
+ }
+ }
+ EMU_DCDCModeSet(currentDcdcMode);
+ /* Update slice configuration as it depends on conduction mode and RCO band. */
+ EMU_DCDCOptimizeSlice(dcdcEm01LoadCurrent_mA);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Configure DCDC regulator
+ *
+ * @note
+ * If the power circuit is configured for NODCDC as described in Section
+ * 11.3.4.3 of the Reference Manual, do not call this function. Instead call
+ * EMU_DCDCPowerOff().
+ *
+ * @param[in] dcdcInit
+ * DCDC initialization structure
+ *
+ * @return
+ * True if initialization parameters are valid
+ ******************************************************************************/
+bool EMU_DCDCInit(const EMU_DCDCInit_TypeDef *dcdcInit)
+{
+ uint32_t lpCmpBiasSelEM234H;
+
+#if defined(_EMU_PWRCFG_MASK)
+ /* Set external power configuration. This enables writing to the other
+ DCDC registers. */
+ EMU->PWRCFG = EMU_PWRCFG_PWRCFG_DCDCTODVDD;
+
+ /* EMU->PWRCFG is write-once and POR reset only. Check that
+ we could set the desired power configuration. */
+ if ((EMU->PWRCFG & _EMU_PWRCFG_PWRCFG_MASK) != EMU_PWRCFG_PWRCFG_DCDCTODVDD) {
+ /* If this assert triggers unexpectedly, please power cycle the
+ kit to reset the power configuration. */
+ EFM_ASSERT(false);
+ /* Return when assertions are disabled */
+ return false;
+ }
+#endif
+
+ /* Load DCDC calibration data from the DI page */
+ dcdcConstCalibrationLoad();
+
+ /* Check current parameters */
+ EFM_ASSERT(dcdcInit->maxCurrent_mA <= 200);
+ EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= dcdcInit->maxCurrent_mA);
+ EFM_ASSERT(dcdcInit->reverseCurrentControl <= 200);
+
+ if (dcdcInit->dcdcMode == emuDcdcMode_LowNoise) {
+ /* DCDC low-noise supports max 200mA */
+ EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= 200);
+ }
+#if (_SILICON_LABS_GECKO_INTERNAL_SDID != 80)
+ else if (dcdcInit->dcdcMode == emuDcdcMode_LowPower) {
+ /* Up to 10mA is supported for EM01-LP mode. */
+ EFM_ASSERT(dcdcInit->em01LoadCurrent_mA <= 10);
+ }
+#endif
+
+ /* EM2/3/4 current above 10mA is not supported */
+ EFM_ASSERT(dcdcInit->em234LoadCurrent_uA <= 10000);
+
+ if (dcdcInit->em234LoadCurrent_uA < 75) {
+ lpCmpBiasSelEM234H = 0;
+ } else if (dcdcInit->em234LoadCurrent_uA < 500) {
+ lpCmpBiasSelEM234H = 1 << _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT;
+ } else if (dcdcInit->em234LoadCurrent_uA < 2500) {
+ lpCmpBiasSelEM234H = 2 << _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT;
+ } else {
+ lpCmpBiasSelEM234H = 3 << _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT;
+ }
+
+ /* ==== THESE NEXT STEPS ARE STRONGLY ORDER DEPENDENT ==== */
+
+ /* Set DCDC low-power mode comparator bias selection */
+
+ /* 1. Set DCDC low-power mode comparator bias selection and forced CCM
+ => Updates DCDCMISCCTRL_LNFORCECCM */
+ EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_MASK
+ | _EMU_DCDCMISCCTRL_LNFORCECCM_MASK))
+ | ((uint32_t)lpCmpBiasSelEM234H
+ | (dcdcInit->reverseCurrentControl >= 0
+ ? EMU_DCDCMISCCTRL_LNFORCECCM : 0));
+#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
+ /* Only 10mA EM01-LP current is supported */
+ EMU->DCDCLPEM01CFG = (EMU->DCDCLPEM01CFG & ~_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
+ | EMU_DCDCLPEM01CFG_LPCMPBIASEM01_BIAS3;
+#endif
+
+ /* 2. Set recommended and validated current optimization settings
+ <= Depends on LNFORCECCM
+ => Updates DCDCLNFREQCTRL_RCOBAND */
+ dcdcValidatedConfigSet();
+
+ /* 3. Updated static currents and limits user data.
+ Limiters are updated in EMU_DCDCOptimizeSlice() */
+ userCurrentLimitsSet(dcdcInit->maxCurrent_mA,
+ dcdcInit->reverseCurrentControl);
+ dcdcEm01LoadCurrent_mA = dcdcInit->em01LoadCurrent_mA;
+
+ /* 4. Optimize LN slice based on given user input load current
+ <= Depends on DCDCMISCCTRL_LNFORCECCM and DCDCLNFREQCTRL_RCOBAND
+ <= Depends on dcdcInit->maxCurrent_mA and dcdcInit->reverseCurrentControl
+ => Updates DCDCMISCCTRL_P/NFETCNT
+ => Updates DCDCMISCCTRL_LNCLIMILIMSEL and DCDCMISCCTRL_LPCLIMILIMSEL
+ => Updates DCDCZDETCTRL_ZDETILIMSEL */
+ EMU_DCDCOptimizeSlice(dcdcInit->em01LoadCurrent_mA);
+
+ /* ======================================================= */
+
+ /* Set DCDC low noise mode compensator control register. */
+ compCtrlSet(dcdcInit->dcdcLnCompCtrl);
+
+ /* Set DCDC output voltage */
+ if (!EMU_DCDCOutputVoltageSet(dcdcInit->mVout, true, true)) {
+ EFM_ASSERT(false);
+ /* Return when assertions are disabled */
+ return false;
+ }
+
+#if (_SILICON_LABS_GECKO_INTERNAL_SDID == 80)
+ /* Select analog peripheral power supply. This must be done before
+ DCDC mode is set for all EFM32xG1 and EFR32xG1 devices. */
+ BUS_RegBitWrite(&EMU->PWRCTRL,
+ _EMU_PWRCTRL_ANASW_SHIFT,
+ dcdcInit->anaPeripheralPower ? 1 : 0);
+#endif
+
+#if defined(_EMU_PWRCTRL_REGPWRSEL_MASK)
+ /* Select DVDD as input to the digital regulator. The switch to DVDD will take
+ effect once the DCDC output is stable. */
+ EMU->PWRCTRL |= EMU_PWRCTRL_REGPWRSEL_DVDD;
+#endif
+
+ /* Set EM0 DCDC operating mode. Output voltage set in
+ EMU_DCDCOutputVoltageSet() above takes effect if mode
+ is changed from bypass/off mode. */
+ EMU_DCDCModeSet(dcdcInit->dcdcMode);
+
+#if (_SILICON_LABS_GECKO_INTERNAL_SDID != 80)
+ /* Select analog peripheral power supply. This must be done after
+ DCDC mode is set for all devices other than EFM32xG1 and EFR32xG1. */
+ BUS_RegBitWrite(&EMU->PWRCTRL,
+ _EMU_PWRCTRL_ANASW_SHIFT,
+ dcdcInit->anaPeripheralPower ? 1 : 0);
+#endif
+
+ return true;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set DCDC output voltage
+ *
+ * @param[in] mV
+ * Target DCDC output voltage in mV
+ *
+ * @return
+ * True if the mV parameter is valid
+ ******************************************************************************/
+bool EMU_DCDCOutputVoltageSet(uint32_t mV,
+ bool setLpVoltage,
+ bool setLnVoltage)
+{
+#if defined(_DEVINFO_DCDCLNVCTRL0_3V0LNATT1_MASK)
+
+#define DCDC_TRIM_MODES ((uint8_t)dcdcTrimMode_LN + 1)
+ bool validOutVoltage;
+ bool attenuationSet;
+ uint32_t mVlow = 0;
+ uint32_t mVhigh = 0;
+ uint32_t mVdiff;
+ uint32_t vrefVal[DCDC_TRIM_MODES] = { 0 };
+ uint32_t vrefLow[DCDC_TRIM_MODES] = { 0 };
+ uint32_t vrefHigh[DCDC_TRIM_MODES] = { 0 };
+ uint8_t lpcmpBias[DCDC_TRIM_MODES] = { 0 };
+
+ /* Check that the set voltage is within valid range.
+ Voltages are obtained from the datasheet. */
+ validOutVoltage = ((mV >= PWRCFG_DCDCTODVDD_VMIN)
+ && (mV <= PWRCFG_DCDCTODVDD_VMAX));
+
+ if (!validOutVoltage) {
+ EFM_ASSERT(false);
+ /* Return when assertions are disabled */
+ return false;
+ }
+
+ /* Set attenuation to use and low/high range. */
+ attenuationSet = (mV > 1800);
+ if (attenuationSet) {
+ mVlow = 1800;
+ mVhigh = 3000;
+ mVdiff = mVhigh - mVlow;
+ } else {
+ mVlow = 1200;
+ mVhigh = 1800;
+ mVdiff = mVhigh - mVlow;
+ }
+
+ /* Get 2-point calib data from DEVINFO */
+
+ /* LN mode */
+ if (attenuationSet) {
+ vrefLow[dcdcTrimMode_LN] = DEVINFO->DCDCLNVCTRL0;
+ vrefHigh[dcdcTrimMode_LN] = (vrefLow[dcdcTrimMode_LN] & _DEVINFO_DCDCLNVCTRL0_3V0LNATT1_MASK)
+ >> _DEVINFO_DCDCLNVCTRL0_3V0LNATT1_SHIFT;
+ vrefLow[dcdcTrimMode_LN] = (vrefLow[dcdcTrimMode_LN] & _DEVINFO_DCDCLNVCTRL0_1V8LNATT1_MASK)
+ >> _DEVINFO_DCDCLNVCTRL0_1V8LNATT1_SHIFT;
+ } else {
+ vrefLow[dcdcTrimMode_LN] = DEVINFO->DCDCLNVCTRL0;
+ vrefHigh[dcdcTrimMode_LN] = (vrefLow[dcdcTrimMode_LN] & _DEVINFO_DCDCLNVCTRL0_1V8LNATT0_MASK)
+ >> _DEVINFO_DCDCLNVCTRL0_1V8LNATT0_SHIFT;
+ vrefLow[dcdcTrimMode_LN] = (vrefLow[dcdcTrimMode_LN] & _DEVINFO_DCDCLNVCTRL0_1V2LNATT0_MASK)
+ >> _DEVINFO_DCDCLNVCTRL0_1V2LNATT0_SHIFT;
+ }
+
+ /* LP EM234H mode */
+ lpcmpBias[dcdcTrimMode_EM234H_LP] = (EMU->DCDCMISCCTRL & _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_MASK)
+ >> _GENERIC_DCDCMISCCTRL_LPCMPBIASEM234H_SHIFT;
+ lpGetDevinfoVrefLowHigh(&vrefLow[dcdcTrimMode_EM234H_LP],
+ &vrefHigh[dcdcTrimMode_EM234H_LP],
+ attenuationSet,
+ lpcmpBias[dcdcTrimMode_EM234H_LP]);
+
+#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
+ /* LP EM01 mode */
+ lpcmpBias[dcdcTrimMode_EM01_LP] = (EMU->DCDCLPEM01CFG & _EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
+ >> _EMU_DCDCLPEM01CFG_LPCMPBIASEM01_SHIFT;
+ lpGetDevinfoVrefLowHigh(&vrefLow[dcdcTrimMode_EM01_LP],
+ &vrefHigh[dcdcTrimMode_EM01_LP],
+ attenuationSet,
+ lpcmpBias[dcdcTrimMode_EM01_LP]);
+#endif
+
+ /* Calculate output voltage trims */
+ vrefVal[dcdcTrimMode_LN] = ((mV - mVlow) * (vrefHigh[dcdcTrimMode_LN] - vrefLow[dcdcTrimMode_LN]))
+ / mVdiff;
+ vrefVal[dcdcTrimMode_LN] += vrefLow[dcdcTrimMode_LN];
+
+ vrefVal[dcdcTrimMode_EM234H_LP] = ((mV - mVlow) * (vrefHigh[dcdcTrimMode_EM234H_LP] - vrefLow[dcdcTrimMode_EM234H_LP]))
+ / mVdiff;
+ vrefVal[dcdcTrimMode_EM234H_LP] += vrefLow[dcdcTrimMode_EM234H_LP];
+
+#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
+ vrefVal[dcdcTrimMode_EM01_LP] = ((mV - mVlow) * (vrefHigh[dcdcTrimMode_EM01_LP] - vrefLow[dcdcTrimMode_EM01_LP]))
+ / mVdiff;
+ vrefVal[dcdcTrimMode_EM01_LP] += vrefLow[dcdcTrimMode_EM01_LP];
+#endif
+
+ /* Range checks */
+ if ((vrefVal[dcdcTrimMode_LN] > vrefHigh[dcdcTrimMode_LN])
+ || (vrefVal[dcdcTrimMode_LN] < vrefLow[dcdcTrimMode_LN])
+#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
+ || (vrefVal[dcdcTrimMode_EM01_LP] > vrefHigh[dcdcTrimMode_EM01_LP])
+ || (vrefVal[dcdcTrimMode_EM01_LP] < vrefLow[dcdcTrimMode_EM01_LP])
+#endif
+ || (vrefVal[dcdcTrimMode_EM234H_LP] > vrefHigh[dcdcTrimMode_EM234H_LP])
+ || (vrefVal[dcdcTrimMode_EM234H_LP] < vrefLow[dcdcTrimMode_EM234H_LP])) {
+ EFM_ASSERT(false);
+ /* Return when assertions are disabled */
+ return false;
+ }
+
+ /* Update output voltage tuning for LN and LP modes. */
+ if (setLnVoltage) {
+ EMU->DCDCLNVCTRL = (EMU->DCDCLNVCTRL & ~(_EMU_DCDCLNVCTRL_LNVREF_MASK | _EMU_DCDCLNVCTRL_LNATT_MASK))
+ | (vrefVal[dcdcTrimMode_LN] << _EMU_DCDCLNVCTRL_LNVREF_SHIFT)
+ | (attenuationSet ? EMU_DCDCLNVCTRL_LNATT : 0);
+ }
+
+ if (setLpVoltage) {
+ /* Load LP EM234H comparator hysteresis calibration */
+ if (!(lpCmpHystCalibrationLoad(attenuationSet, lpcmpBias[dcdcTrimMode_EM234H_LP], dcdcTrimMode_EM234H_LP))) {
+ EFM_ASSERT(false);
+ /* Return when assertions are disabled */
+ return false;
+ }
+
+#if defined(_EMU_DCDCLPEM01CFG_LPCMPBIASEM01_MASK)
+ /* Load LP EM234H comparator hysteresis calibration */
+ if (!(lpCmpHystCalibrationLoad(attenuationSet, lpcmpBias[dcdcTrimMode_EM01_LP], dcdcTrimMode_EM01_LP))) {
+ EFM_ASSERT(false);
+ /* Return when assertions are disabled */
+ return false;
+ }
+
+ /* LP VREF is that max of trims for EM01 and EM234H. */
+ vrefVal[dcdcTrimMode_EM234H_LP] = SL_MAX(vrefVal[dcdcTrimMode_EM234H_LP], vrefVal[dcdcTrimMode_EM01_LP]);
+#endif
+
+ /* Don't exceed max available code as specified in the reference manual for EMU_DCDCLPVCTRL. */
+ vrefVal[dcdcTrimMode_EM234H_LP] = SL_MIN(vrefVal[dcdcTrimMode_EM234H_LP], 0xE7U);
+ EMU->DCDCLPVCTRL = (EMU->DCDCLPVCTRL & ~(_EMU_DCDCLPVCTRL_LPVREF_MASK | _EMU_DCDCLPVCTRL_LPATT_MASK))
+ | (vrefVal[dcdcTrimMode_EM234H_LP] << _EMU_DCDCLPVCTRL_LPVREF_SHIFT)
+ | (attenuationSet ? EMU_DCDCLPVCTRL_LPATT : 0);
+ }
+#endif
+ return true;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Optimize DCDC slice count based on the estimated average load current
+ * in EM0
+ *
+ * @param[in] em0LoadCurrent_mA
+ * Estimated average EM0 load current in mA.
+ ******************************************************************************/
+void EMU_DCDCOptimizeSlice(uint32_t em0LoadCurrent_mA)
+{
+ uint32_t sliceCount = 0;
+ uint32_t rcoBand = (EMU->DCDCLNFREQCTRL & _EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
+ >> _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT;
+
+ /* Set recommended slice count */
+ if ((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) && (rcoBand >= emuDcdcLnRcoBand_5MHz)) {
+ if (em0LoadCurrent_mA < 20) {
+ sliceCount = 4;
+ } else if ((em0LoadCurrent_mA >= 20) && (em0LoadCurrent_mA < 40)) {
+ sliceCount = 8;
+ } else {
+ sliceCount = 16;
+ }
+ } else if ((!(EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK)) && (rcoBand <= emuDcdcLnRcoBand_4MHz)) {
+ if (em0LoadCurrent_mA < 10) {
+ sliceCount = 4;
+ } else if ((em0LoadCurrent_mA >= 10) && (em0LoadCurrent_mA < 20)) {
+ sliceCount = 8;
+ } else {
+ sliceCount = 16;
+ }
+ } else if ((EMU->DCDCMISCCTRL & _EMU_DCDCMISCCTRL_LNFORCECCM_MASK) && (rcoBand <= emuDcdcLnRcoBand_4MHz)) {
+ if (em0LoadCurrent_mA < 40) {
+ sliceCount = 8;
+ } else {
+ sliceCount = 16;
+ }
+ } else {
+ /* This configuration is not recommended. EMU_DCDCInit() applies a recommended
+ configuration. */
+ EFM_ASSERT(false);
+ }
+
+ /* The selected slices are PSLICESEL + 1 */
+ sliceCount--;
+
+ /* Apply slice count to both N and P slice */
+ sliceCount = (sliceCount << _EMU_DCDCMISCCTRL_PFETCNT_SHIFT
+ | sliceCount << _EMU_DCDCMISCCTRL_NFETCNT_SHIFT);
+ EMU->DCDCMISCCTRL = (EMU->DCDCMISCCTRL & ~(_EMU_DCDCMISCCTRL_PFETCNT_MASK
+ | _EMU_DCDCMISCCTRL_NFETCNT_MASK))
+ | sliceCount;
+
+ /* Update current limiters */
+ currentLimitersUpdate();
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set DCDC Low-noise RCO band.
+ *
+ * @param[in] band
+ * RCO band to set.
+ ******************************************************************************/
+void EMU_DCDCLnRcoBandSet(EMU_DcdcLnRcoBand_TypeDef band)
+{
+ uint32_t forcedCcm;
+ forcedCcm = BUS_RegBitRead(&EMU->DCDCMISCCTRL, _EMU_DCDCMISCCTRL_LNFORCECCM_SHIFT);
+
+ /* DCM mode supports up to 4MHz LN RCO. */
+ EFM_ASSERT((!forcedCcm && band <= emuDcdcLnRcoBand_4MHz) || forcedCcm);
+
+ EMU->DCDCLNFREQCTRL = (EMU->DCDCLNFREQCTRL & ~_EMU_DCDCLNFREQCTRL_RCOBAND_MASK)
+ | (band << _EMU_DCDCLNFREQCTRL_RCOBAND_SHIFT);
+
+ /* Update slice configuration as this depends on the RCO band. */
+ EMU_DCDCOptimizeSlice(dcdcEm01LoadCurrent_mA);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Power off the DCDC regulator.
+ *
+ * @details
+ * This function powers off the DCDC controller. This function should only be
+ * used if the external power circuit is wired for no DCDC. If the external power
+ * circuit is wired for DCDC usage, then use EMU_DCDCInit() and set the
+ * DCDC in bypass mode to disable DCDC.
+ *
+ * @return
+ * Return false if the DCDC could not be disabled.
+ ******************************************************************************/
+bool EMU_DCDCPowerOff(void)
+{
+ bool dcdcModeSet;
+
+#if defined(_EMU_PWRCFG_MASK)
+ /* Set DCDCTODVDD only to enable write access to EMU->DCDCCTRL */
+ EMU->PWRCFG = EMU_PWRCFG_PWRCFG_DCDCTODVDD;
+#endif
+
+ /* Select DVDD as input to the digital regulator */
+#if defined(EMU_PWRCTRL_IMMEDIATEPWRSWITCH)
+ EMU->PWRCTRL |= EMU_PWRCTRL_REGPWRSEL_DVDD | EMU_PWRCTRL_IMMEDIATEPWRSWITCH;
+#elif defined(EMU_PWRCTRL_REGPWRSEL_DVDD)
+ EMU->PWRCTRL |= EMU_PWRCTRL_REGPWRSEL_DVDD;
+#endif
+
+ /* Set DCDC to OFF and disable LP in EM2/3/4. Verify that the required
+ mode could be set. */
+ while (EMU->DCDCSYNC & EMU_DCDCSYNC_DCDCCTRLBUSY) ;
+ EMU->DCDCCTRL = EMU_DCDCCTRL_DCDCMODE_OFF;
+
+ dcdcModeSet = (EMU->DCDCCTRL == EMU_DCDCCTRL_DCDCMODE_OFF);
+ EFM_ASSERT(dcdcModeSet);
+
+ return dcdcModeSet;
+}
+#endif
+
+#if defined(EMU_STATUS_VMONRDY)
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/***************************************************************************//**
+ * @brief
+ * Get calibrated threshold value.
+ *
+ * @details
+ * All VMON channels have two calibration fields in the DI page that
+ * describes the threshold at 1.86V and 2.98V. This function will convert
+ * the uncalibrated input voltage threshold in millivolts into a calibrated
+ * threshold.
+ *
+ * @param[in] channel
+ * VMON channel
+ *
+ * @param[in] threshold
+ * Desired threshold in millivolts.
+ *
+ * @return
+ * Calibrated threshold value to use. First digit of return value is placed
+ * in the "fine" register fields while the next digits are placed in the
+ * "coarse" register fields.
+ ******************************************************************************/
+static uint32_t vmonCalibratedThreshold(EMU_VmonChannel_TypeDef channel,
+ int threshold)
+{
+ uint32_t tLow;
+ uint32_t tHigh;
+ uint32_t calReg;
+
+ /* Get calibration values for 1.86V and 2.98V */
+ switch (channel) {
+ case emuVmonChannel_AVDD:
+ calReg = DEVINFO->VMONCAL0;
+ tLow = (10 * ((calReg & _DEVINFO_VMONCAL0_AVDD1V86THRESCOARSE_MASK)
+ >> _DEVINFO_VMONCAL0_AVDD1V86THRESCOARSE_SHIFT))
+ + ((calReg & _DEVINFO_VMONCAL0_AVDD1V86THRESFINE_MASK)
+ >> _DEVINFO_VMONCAL0_AVDD1V86THRESFINE_SHIFT);
+ tHigh = (10 * ((calReg & _DEVINFO_VMONCAL0_AVDD2V98THRESCOARSE_MASK)
+ >> _DEVINFO_VMONCAL0_AVDD2V98THRESCOARSE_SHIFT))
+ + ((calReg & _DEVINFO_VMONCAL0_AVDD2V98THRESFINE_MASK)
+ >> _DEVINFO_VMONCAL0_AVDD2V98THRESFINE_SHIFT);
+ break;
+ case emuVmonChannel_ALTAVDD:
+ calReg = DEVINFO->VMONCAL0;
+ tLow = (10 * ((calReg & _DEVINFO_VMONCAL0_ALTAVDD1V86THRESCOARSE_MASK)
+ >> _DEVINFO_VMONCAL0_ALTAVDD1V86THRESCOARSE_SHIFT))
+ + ((calReg & _DEVINFO_VMONCAL0_ALTAVDD1V86THRESFINE_MASK)
+ >> _DEVINFO_VMONCAL0_ALTAVDD1V86THRESFINE_SHIFT);
+ tHigh = (10 * ((calReg & _DEVINFO_VMONCAL0_ALTAVDD2V98THRESCOARSE_MASK)
+ >> _DEVINFO_VMONCAL0_ALTAVDD2V98THRESCOARSE_SHIFT))
+ + ((calReg & _DEVINFO_VMONCAL0_ALTAVDD2V98THRESFINE_MASK)
+ >> _DEVINFO_VMONCAL0_ALTAVDD2V98THRESFINE_SHIFT);
+ break;
+ case emuVmonChannel_DVDD:
+ calReg = DEVINFO->VMONCAL1;
+ tLow = (10 * ((calReg & _DEVINFO_VMONCAL1_DVDD1V86THRESCOARSE_MASK)
+ >> _DEVINFO_VMONCAL1_DVDD1V86THRESCOARSE_SHIFT))
+ + ((calReg & _DEVINFO_VMONCAL1_DVDD1V86THRESFINE_MASK)
+ >> _DEVINFO_VMONCAL1_DVDD1V86THRESFINE_SHIFT);
+ tHigh = (10 * ((calReg & _DEVINFO_VMONCAL1_DVDD2V98THRESCOARSE_MASK)
+ >> _DEVINFO_VMONCAL1_DVDD2V98THRESCOARSE_SHIFT))
+ + ((calReg & _DEVINFO_VMONCAL1_DVDD2V98THRESFINE_MASK)
+ >> _DEVINFO_VMONCAL1_DVDD2V98THRESFINE_SHIFT);
+ break;
+ case emuVmonChannel_IOVDD0:
+ calReg = DEVINFO->VMONCAL1;
+ tLow = (10 * ((calReg & _DEVINFO_VMONCAL1_IO01V86THRESCOARSE_MASK)
+ >> _DEVINFO_VMONCAL1_IO01V86THRESCOARSE_SHIFT))
+ + ((calReg & _DEVINFO_VMONCAL1_IO01V86THRESFINE_MASK)
+ >> _DEVINFO_VMONCAL1_IO01V86THRESFINE_SHIFT);
+ tHigh = (10 * ((calReg & _DEVINFO_VMONCAL1_IO02V98THRESCOARSE_MASK)
+ >> _DEVINFO_VMONCAL1_IO02V98THRESCOARSE_SHIFT))
+ + ((calReg & _DEVINFO_VMONCAL1_IO02V98THRESFINE_MASK)
+ >> _DEVINFO_VMONCAL1_IO02V98THRESFINE_SHIFT);
+ break;
+ default:
+ EFM_ASSERT(false);
+ return threshold;
+ }
+
+ if (tHigh <= tLow) {
+ /* Uncalibrated device guard */
+ return threshold;
+ }
+
+ /* Calculate threshold.
+ *
+ * Note that volt is used in the reference manual, however we are interested
+ * in millivolt results. We also increase the precision of Va and Vb in the
+ * calculation instead of using floating points.
+ */
+ uint32_t va = (1120 * 100) / (tHigh - tLow);
+ uint32_t vb = (1860 * 100) - (va * tLow);
+ /* Round threshold to nearest integer value. */
+ return ((threshold * 100) - vb + (va / 2)) / va;
+}
+
+/** @endcond */
+
+/***************************************************************************//**
+ * @brief
+ * Initialize VMON channel.
+ *
+ * @details
+ * Initialize a VMON channel without hysteresis. If the channel supports
+ * separate rise and fall triggers, both thresholds will be set to the same
+ * value. The threshold will be converted to a register field value based
+ * on calibration values from the DI page.
+ *
+ * @param[in] vmonInit
+ * VMON initialization struct
+ ******************************************************************************/
+void EMU_VmonInit(const EMU_VmonInit_TypeDef *vmonInit)
+{
+ uint32_t thresholdCoarse, thresholdFine;
+ uint32_t threshold;
+
+ EFM_ASSERT((vmonInit->threshold >= 1620) && (vmonInit->threshold <= 3400));
+
+ threshold = vmonCalibratedThreshold(vmonInit->channel, vmonInit->threshold);
+ thresholdFine = threshold % 10;
+ thresholdCoarse = threshold / 10;
+
+ /* Saturate threshold to max values. */
+ if (thresholdCoarse > 0xF) {
+ thresholdCoarse = 0xF;
+ thresholdFine = 9;
+ }
+
+ switch (vmonInit->channel) {
+ case emuVmonChannel_AVDD:
+ EMU->VMONAVDDCTRL = (thresholdCoarse << _EMU_VMONAVDDCTRL_RISETHRESCOARSE_SHIFT)
+ | (thresholdFine << _EMU_VMONAVDDCTRL_RISETHRESFINE_SHIFT)
+ | (thresholdCoarse << _EMU_VMONAVDDCTRL_FALLTHRESCOARSE_SHIFT)
+ | (thresholdFine << _EMU_VMONAVDDCTRL_FALLTHRESFINE_SHIFT)
+ | (vmonInit->riseWakeup ? EMU_VMONAVDDCTRL_RISEWU : 0)
+ | (vmonInit->fallWakeup ? EMU_VMONAVDDCTRL_FALLWU : 0)
+ | (vmonInit->enable ? EMU_VMONAVDDCTRL_EN : 0);
+ break;
+ case emuVmonChannel_ALTAVDD:
+ EMU->VMONALTAVDDCTRL = (thresholdCoarse << _EMU_VMONALTAVDDCTRL_THRESCOARSE_SHIFT)
+ | (thresholdFine << _EMU_VMONALTAVDDCTRL_THRESFINE_SHIFT)
+ | (vmonInit->riseWakeup ? EMU_VMONALTAVDDCTRL_RISEWU : 0)
+ | (vmonInit->fallWakeup ? EMU_VMONALTAVDDCTRL_FALLWU : 0)
+ | (vmonInit->enable ? EMU_VMONALTAVDDCTRL_EN : 0);
+ break;
+ case emuVmonChannel_DVDD:
+ EMU->VMONDVDDCTRL = (thresholdCoarse << _EMU_VMONDVDDCTRL_THRESCOARSE_SHIFT)
+ | (thresholdFine << _EMU_VMONDVDDCTRL_THRESFINE_SHIFT)
+ | (vmonInit->riseWakeup ? EMU_VMONDVDDCTRL_RISEWU : 0)
+ | (vmonInit->fallWakeup ? EMU_VMONDVDDCTRL_FALLWU : 0)
+ | (vmonInit->enable ? EMU_VMONDVDDCTRL_EN : 0);
+ break;
+ case emuVmonChannel_IOVDD0:
+ EMU->VMONIO0CTRL = (thresholdCoarse << _EMU_VMONIO0CTRL_THRESCOARSE_SHIFT)
+ | (thresholdFine << _EMU_VMONIO0CTRL_THRESFINE_SHIFT)
+ | (vmonInit->retDisable ? EMU_VMONIO0CTRL_RETDIS : 0)
+ | (vmonInit->riseWakeup ? EMU_VMONIO0CTRL_RISEWU : 0)
+ | (vmonInit->fallWakeup ? EMU_VMONIO0CTRL_FALLWU : 0)
+ | (vmonInit->enable ? EMU_VMONIO0CTRL_EN : 0);
+ break;
+ default:
+ EFM_ASSERT(false);
+ return;
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Initialize VMON channel with hysteresis (separate rise and fall triggers).
+ *
+ * @details
+ * Initialize a VMON channel which supports hysteresis. The AVDD channel is
+ * the only channel to support separate rise and fall triggers. The rise and
+ * fall thresholds will be converted to a register field value based on
+ * calibration values from the DI page.
+ *
+ * @param[in] vmonInit
+ * VMON Hysteresis initialization struct
+ ******************************************************************************/
+void EMU_VmonHystInit(const EMU_VmonHystInit_TypeDef *vmonInit)
+{
+ uint32_t riseThreshold;
+ uint32_t fallThreshold;
+
+ /* VMON supports voltages between 1620 mV and 3400 mV (inclusive) */
+ EFM_ASSERT((vmonInit->riseThreshold >= 1620) && (vmonInit->riseThreshold <= 3400));
+ EFM_ASSERT((vmonInit->fallThreshold >= 1620) && (vmonInit->fallThreshold <= 3400));
+ /* Fall threshold has to be lower than rise threshold */
+ EFM_ASSERT(vmonInit->fallThreshold <= vmonInit->riseThreshold);
+
+ riseThreshold = vmonCalibratedThreshold(vmonInit->channel, vmonInit->riseThreshold);
+ fallThreshold = vmonCalibratedThreshold(vmonInit->channel, vmonInit->fallThreshold);
+
+ switch (vmonInit->channel) {
+ case emuVmonChannel_AVDD:
+ EMU->VMONAVDDCTRL = ((riseThreshold / 10) << _EMU_VMONAVDDCTRL_RISETHRESCOARSE_SHIFT)
+ | ((riseThreshold % 10) << _EMU_VMONAVDDCTRL_RISETHRESFINE_SHIFT)
+ | ((fallThreshold / 10) << _EMU_VMONAVDDCTRL_FALLTHRESCOARSE_SHIFT)
+ | ((fallThreshold % 10) << _EMU_VMONAVDDCTRL_FALLTHRESFINE_SHIFT)
+ | (vmonInit->riseWakeup ? EMU_VMONAVDDCTRL_RISEWU : 0)
+ | (vmonInit->fallWakeup ? EMU_VMONAVDDCTRL_FALLWU : 0)
+ | (vmonInit->enable ? EMU_VMONAVDDCTRL_EN : 0);
+ break;
+ default:
+ EFM_ASSERT(false);
+ return;
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Enable or disable a VMON channel
+ *
+ * @param[in] channel
+ * VMON channel to enable/disable
+ *
+ * @param[in] enable
+ * Whether to enable or disable
+ ******************************************************************************/
+void EMU_VmonEnable(EMU_VmonChannel_TypeDef channel, bool enable)
+{
+ uint32_t volatile * reg;
+ uint32_t bit;
+
+ switch (channel) {
+ case emuVmonChannel_AVDD:
+ reg = &(EMU->VMONAVDDCTRL);
+ bit = _EMU_VMONAVDDCTRL_EN_SHIFT;
+ break;
+ case emuVmonChannel_ALTAVDD:
+ reg = &(EMU->VMONALTAVDDCTRL);
+ bit = _EMU_VMONALTAVDDCTRL_EN_SHIFT;
+ break;
+ case emuVmonChannel_DVDD:
+ reg = &(EMU->VMONDVDDCTRL);
+ bit = _EMU_VMONDVDDCTRL_EN_SHIFT;
+ break;
+ case emuVmonChannel_IOVDD0:
+ reg = &(EMU->VMONIO0CTRL);
+ bit = _EMU_VMONIO0CTRL_EN_SHIFT;
+ break;
+ default:
+ EFM_ASSERT(false);
+ return;
+ }
+
+ BUS_RegBitWrite(reg, bit, enable);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get the status of a voltage monitor channel.
+ *
+ * @param[in] channel
+ * VMON channel to get status for
+ *
+ * @return
+ * Status of the selected VMON channel. True if channel is triggered.
+ ******************************************************************************/
+bool EMU_VmonChannelStatusGet(EMU_VmonChannel_TypeDef channel)
+{
+ uint32_t bit;
+ switch (channel) {
+ case emuVmonChannel_AVDD:
+ bit = _EMU_STATUS_VMONAVDD_SHIFT;
+ break;
+ case emuVmonChannel_ALTAVDD:
+ bit = _EMU_STATUS_VMONALTAVDD_SHIFT;
+ break;
+ case emuVmonChannel_DVDD:
+ bit = _EMU_STATUS_VMONDVDD_SHIFT;
+ break;
+ case emuVmonChannel_IOVDD0:
+ bit = _EMU_STATUS_VMONIO0_SHIFT;
+ break;
+ default:
+ EFM_ASSERT(false);
+ bit = 0;
+ }
+
+ return BUS_RegBitRead(&EMU->STATUS, bit);
+}
+#endif /* EMU_STATUS_VMONRDY */
+
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+/***************************************************************************//**
+ * @brief
+ * Adjust the bias refresh rate
+ *
+ * @details
+ * This function is only meant to be used under high-temperature operation on
+ * EFR32xG1 and EFM32xG1 devices. Adjusting the bias mode will
+ * increase the typical current consumption. See application note 1027
+ * and errata documents for further details.
+ *
+ * @param [in] mode
+ * The new bias refresh rate
+ ******************************************************************************/
+void EMU_SetBiasMode(EMU_BiasMode_TypeDef mode)
+{
+#define EMU_TESTLOCK (*(volatile uint32_t *) (EMU_BASE + 0x190))
+#define EMU_BIASCONF (*(volatile uint32_t *) (EMU_BASE + 0x164))
+#define EMU_BIASTESTCTRL (*(volatile uint32_t *) (EMU_BASE + 0x19C))
+#define CMU_ULFRCOCTRL (*(volatile uint32_t *) (CMU_BASE + 0x03C))
+
+ uint32_t freq = 0x2u;
+ bool emuTestLocked = false;
+
+ if (mode == emuBiasMode_1KHz) {
+ freq = 0x0u;
+ }
+
+ if (EMU_TESTLOCK == 0x1u) {
+ emuTestLocked = true;
+ EMU_TESTLOCK = 0xADE8u;
+ }
+
+ if (mode == emuBiasMode_Continuous) {
+ EMU_BIASCONF &= ~0x74u;
+ } else {
+ EMU_BIASCONF |= 0x74u;
+ }
+
+ EMU_BIASTESTCTRL |= 0x8u;
+ CMU_ULFRCOCTRL = (CMU_ULFRCOCTRL & ~0xC00u)
+ | ((freq & 0x3u) << 10u);
+ EMU_BIASTESTCTRL &= ~0x8u;
+
+ if (emuTestLocked) {
+ EMU_TESTLOCK = 0u;
+ }
+}
+#endif
+
+/** @} (end addtogroup EMU) */
+/** @} (end addtogroup emlib) */
+#endif /* __EM_EMU_H */
diff --git a/efm32boot/emlib/em_gpio.c b/efm32boot/emlib/em_gpio.c
new file mode 100644
index 0000000..3b1d146
--- /dev/null
+++ b/efm32boot/emlib/em_gpio.c
@@ -0,0 +1,367 @@
+/***************************************************************************//**
+ * @file em_gpio.c
+ * @brief General Purpose IO (GPIO) peripheral API
+ * devices.
+ * @version 5.2.2
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+#include "em_gpio.h"
+
+#if defined(GPIO_COUNT) && (GPIO_COUNT > 0)
+
+/***************************************************************************//**
+ * @addtogroup emlib
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup GPIO
+ * @brief General Purpose Input/Output (GPIO) API
+ * @details
+ * This module contains functions to control the GPIO peripheral of Silicon
+ * Labs 32-bit MCUs and SoCs. The GPIO peripheral is used for pin configuration
+ * and direct pin manipulation and sensing as well as routing for peripheral
+ * pin connections.
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ******************************* DEFINES ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/** Validation of pin typically usable in assert statements. */
+#define GPIO_DRIVEMODE_VALID(mode) ((mode) <= 3)
+#define GPIO_STRENGHT_VALID(strenght) (!((strenght) \
+ & ~(_GPIO_P_CTRL_DRIVESTRENGTH_MASK \
+ | _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK)))
+/** @endcond */
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Sets the pin location of the debug pins (Serial Wire interface).
+ *
+ * @note
+ * Changing the pins used for debugging uncontrolled, may result in a lockout.
+ *
+ * @param[in] location
+ * The debug pin location to use (0-3).
+ ******************************************************************************/
+void GPIO_DbgLocationSet(unsigned int location)
+{
+#if defined (_GPIO_ROUTE_SWLOCATION_MASK)
+ EFM_ASSERT(location < AFCHANLOC_MAX);
+
+ GPIO->ROUTE = (GPIO->ROUTE & ~_GPIO_ROUTE_SWLOCATION_MASK)
+ | (location << _GPIO_ROUTE_SWLOCATION_SHIFT);
+#else
+ (void)location;
+#endif
+}
+
+#if defined (_GPIO_P_CTRL_DRIVEMODE_MASK)
+/***************************************************************************//**
+ * @brief
+ * Sets the drive mode for a GPIO port.
+ *
+ * @param[in] port
+ * The GPIO port to access.
+ *
+ * @param[in] mode
+ * Drive mode to use for port.
+ ******************************************************************************/
+void GPIO_DriveModeSet(GPIO_Port_TypeDef port, GPIO_DriveMode_TypeDef mode)
+{
+ EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_DRIVEMODE_VALID(mode));
+
+ GPIO->P[port].CTRL = (GPIO->P[port].CTRL & ~(_GPIO_P_CTRL_DRIVEMODE_MASK))
+ | (mode << _GPIO_P_CTRL_DRIVEMODE_SHIFT);
+}
+#endif
+
+#if defined (_GPIO_P_CTRL_DRIVESTRENGTH_MASK)
+/***************************************************************************//**
+ * @brief
+ * Sets the drive strength for a GPIO port.
+ *
+ * @param[in] port
+ * The GPIO port to access.
+ *
+ * @param[in] strength
+ * Drive strength to use for port.
+ ******************************************************************************/
+void GPIO_DriveStrengthSet(GPIO_Port_TypeDef port,
+ GPIO_DriveStrength_TypeDef strength)
+{
+ EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_STRENGHT_VALID(strength));
+ BUS_RegMaskedWrite(&GPIO->P[port].CTRL,
+ _GPIO_P_CTRL_DRIVESTRENGTH_MASK | _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK,
+ strength);
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Configure GPIO external pin interrupt.
+ *
+ * @details
+ * If reconfiguring a GPIO interrupt that is already enabled, it is generally
+ * recommended to disable it first, see GPIO_Disable().
+ *
+ * The actual GPIO interrupt handler must be in place before enabling the
+ * interrupt.
+ *
+ * Notice that any pending interrupt for the selected interrupt is cleared
+ * by this function.
+ *
+ * @note
+ * On series 0 devices the pin number parameter is not used. The
+ * pin number used on these devices is hardwired to the interrupt with the
+ * same number. @n
+ * On series 1 devices, pin number can be selected freely within a group.
+ * Interrupt numbers are divided into 4 groups (intNo / 4) and valid pin
+ * number within the interrupt groups are:
+ * 0: pins 0-3
+ * 1: pins 4-7
+ * 2: pins 8-11
+ * 3: pins 12-15
+ *
+ * @param[in] port
+ * The port to associate with @p pin.
+ *
+ * @param[in] pin
+ * The pin number on the port.
+ *
+ * @param[in] intNo
+ * The interrupt number to trigger.
+ *
+ * @param[in] risingEdge
+ * Set to true if interrupts shall be enabled on rising edge, otherwise false.
+ *
+ * @param[in] fallingEdge
+ * Set to true if interrupts shall be enabled on falling edge, otherwise false.
+ *
+ * @param[in] enable
+ * Set to true if interrupt shall be enabled after configuration completed,
+ * false to leave disabled. See GPIO_IntDisable() and GPIO_IntEnable().
+ ******************************************************************************/
+void GPIO_ExtIntConfig(GPIO_Port_TypeDef port,
+ unsigned int pin,
+ unsigned int intNo,
+ bool risingEdge,
+ bool fallingEdge,
+ bool enable)
+{
+ uint32_t tmp = 0;
+#if !defined(_GPIO_EXTIPINSELL_MASK)
+ (void)pin;
+#endif
+
+ EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
+#if defined(_GPIO_EXTIPINSELL_MASK)
+ EFM_ASSERT(GPIO_INTNO_PIN_VALID(intNo, pin));
+#endif
+
+ /* There are two registers controlling the interrupt configuration:
+ * The EXTIPSELL register controls pins 0-7 and EXTIPSELH controls
+ * pins 8-15. */
+ if (intNo < 8) {
+ BUS_RegMaskedWrite(&GPIO->EXTIPSELL,
+ _GPIO_EXTIPSELL_EXTIPSEL0_MASK
+ << (_GPIO_EXTIPSELL_EXTIPSEL1_SHIFT * intNo),
+ port << (_GPIO_EXTIPSELL_EXTIPSEL1_SHIFT * intNo));
+ } else {
+ tmp = intNo - 8;
+ BUS_RegMaskedWrite(&GPIO->EXTIPSELH,
+ _GPIO_EXTIPSELH_EXTIPSEL8_MASK
+ << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp),
+ port << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp));
+ }
+
+#if defined(_GPIO_EXTIPINSELL_MASK)
+ /* There are two registers controlling the interrupt/pin number mapping:
+ * The EXTIPINSELL register controls interrupt 0-7 and EXTIPINSELH controls
+ * interrupt 8-15. */
+ if (intNo < 8) {
+ BUS_RegMaskedWrite(&GPIO->EXTIPINSELL,
+ _GPIO_EXTIPINSELL_EXTIPINSEL0_MASK
+ << (_GPIO_EXTIPINSELL_EXTIPINSEL1_SHIFT * intNo),
+ ((pin % 4) & _GPIO_EXTIPINSELL_EXTIPINSEL0_MASK)
+ << (_GPIO_EXTIPINSELL_EXTIPINSEL1_SHIFT * intNo));
+ } else {
+ BUS_RegMaskedWrite(&GPIO->EXTIPINSELH,
+ _GPIO_EXTIPINSELH_EXTIPINSEL8_MASK
+ << (_GPIO_EXTIPINSELH_EXTIPINSEL9_SHIFT * tmp),
+ ((pin % 4) & _GPIO_EXTIPINSELH_EXTIPINSEL8_MASK)
+ << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp));
+ }
+#endif
+
+ /* Enable/disable rising edge */
+ BUS_RegBitWrite(&(GPIO->EXTIRISE), intNo, risingEdge);
+
+ /* Enable/disable falling edge */
+ BUS_RegBitWrite(&(GPIO->EXTIFALL), intNo, fallingEdge);
+
+ /* Clear any pending interrupt */
+ GPIO->IFC = 1 << intNo;
+
+ /* Finally enable/disable interrupt */
+ BUS_RegBitWrite(&(GPIO->IEN), intNo, enable);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set the mode for a GPIO pin.
+ *
+ * @param[in] port
+ * The GPIO port to access.
+ *
+ * @param[in] pin
+ * The pin number in the port.
+ *
+ * @param[in] mode
+ * The desired pin mode.
+ *
+ * @param[in] out
+ * Value to set for pin in DOUT register. The DOUT setting is important for
+ * even some input mode configurations, determining pull-up/down direction.
+ ******************************************************************************/
+void GPIO_PinModeSet(GPIO_Port_TypeDef port,
+ unsigned int pin,
+ GPIO_Mode_TypeDef mode,
+ unsigned int out)
+{
+ EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
+
+ /* If disabling pin, do not modify DOUT in order to reduce chance for */
+ /* glitch/spike (may not be sufficient precaution in all use cases) */
+ if (mode != gpioModeDisabled) {
+ if (out) {
+ GPIO_PinOutSet(port, pin);
+ } else {
+ GPIO_PinOutClear(port, pin);
+ }
+ }
+
+ /* There are two registers controlling the pins for each port. The MODEL
+ * register controls pins 0-7 and MODEH controls pins 8-15. */
+ if (pin < 8) {
+ GPIO->P[port].MODEL = (GPIO->P[port].MODEL & ~(0xFu << (pin * 4)))
+ | (mode << (pin * 4));
+ } else {
+ GPIO->P[port].MODEH = (GPIO->P[port].MODEH & ~(0xFu << ((pin - 8) * 4)))
+ | (mode << ((pin - 8) * 4));
+ }
+
+ if (mode == gpioModeDisabled) {
+ if (out) {
+ GPIO_PinOutSet(port, pin);
+ } else {
+ GPIO_PinOutClear(port, pin);
+ }
+ }
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get the mode for a GPIO pin.
+ *
+ * @param[in] port
+ * The GPIO port to access.
+ *
+ * @param[in] pin
+ * The pin number in the port.
+ *
+ * @return
+ * The pin mode.
+ ******************************************************************************/
+GPIO_Mode_TypeDef GPIO_PinModeGet(GPIO_Port_TypeDef port,
+ unsigned int pin)
+{
+ EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
+
+ if (pin < 8) {
+ return (GPIO_Mode_TypeDef) ((GPIO->P[port].MODEL >> (pin * 4)) & 0xF);
+ } else {
+ return (GPIO_Mode_TypeDef) ((GPIO->P[port].MODEH >> ((pin - 8) * 4)) & 0xF);
+ }
+}
+
+#if defined(_GPIO_EM4WUEN_MASK)
+/**************************************************************************//**
+ * @brief
+ * Enable GPIO pin wake-up from EM4. When the function exits,
+ * EM4 mode can be safely entered.
+ *
+ * @note
+ * It is assumed that the GPIO pin modes are set correctly.
+ * Valid modes are @ref gpioModeInput and @ref gpioModeInputPull.
+ *
+ * @param[in] pinmask
+ * Bitmask containing the bitwise logic OR of which GPIO pin(s) to enable.
+ * Refer to Reference Manuals for pinmask to GPIO port/pin mapping.
+ * @param[in] polaritymask
+ * Bitmask containing the bitwise logic OR of GPIO pin(s) wake-up polarity.
+ * Refer to Reference Manuals for pinmask to GPIO port/pin mapping.
+ *****************************************************************************/
+void GPIO_EM4EnablePinWakeup(uint32_t pinmask, uint32_t polaritymask)
+{
+ EFM_ASSERT((pinmask & ~_GPIO_EM4WUEN_MASK) == 0);
+
+#if defined(_GPIO_EM4WUPOL_MASK)
+ EFM_ASSERT((polaritymask & ~_GPIO_EM4WUPOL_MASK) == 0);
+ GPIO->EM4WUPOL &= ~pinmask; /* Set wakeup polarity */
+ GPIO->EM4WUPOL |= pinmask & polaritymask;
+#elif defined(_GPIO_EXTILEVEL_MASK)
+ EFM_ASSERT((polaritymask & ~_GPIO_EXTILEVEL_MASK) == 0);
+ GPIO->EXTILEVEL &= ~pinmask;
+ GPIO->EXTILEVEL |= pinmask & polaritymask;
+#endif
+ GPIO->EM4WUEN |= pinmask; /* Enable wakeup */
+
+ GPIO_EM4SetPinRetention(true); /* Enable pin retention */
+
+#if defined(_GPIO_CMD_EM4WUCLR_MASK)
+ GPIO->CMD = GPIO_CMD_EM4WUCLR; /* Clear wake-up logic */
+#elif defined(_GPIO_IFC_EM4WU_MASK)
+ GPIO_IntClear(pinmask);
+#endif
+}
+#endif
+
+/** @} (end addtogroup GPIO) */
+/** @} (end addtogroup emlib) */
+
+#endif /* defined(GPIO_COUNT) && (GPIO_COUNT > 0) */
diff --git a/efm32boot/emlib/em_msc.c b/efm32boot/emlib/em_msc.c
new file mode 100644
index 0000000..2391d2a
--- /dev/null
+++ b/efm32boot/emlib/em_msc.c
@@ -0,0 +1,1163 @@
+/***************************************************************************//**
+ * @file em_msc.c
+ * @brief Flash controller (MSC) Peripheral API
+ * @version 5.5.0
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. www.silabs.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+#include "em_msc.h"
+#if defined(MSC_COUNT) && (MSC_COUNT > 0)
+
+#include "em_system.h"
+#if defined(_MSC_TIMEBASE_MASK)
+#include "em_cmu.h"
+#endif
+#include "em_assert.h"
+#if defined(_MSC_ECCCTRL_MASK)
+#include "em_cmu.h"
+#include "em_core.h"
+#endif
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+#if defined(__ICCARM__)
+/* Suppress warnings originating from use of EFM_ASSERT() with IAR:
+ EFM_ASSERT() is implemented as a local ramfunc */
+#pragma diag_suppress=Ta022
+#endif
+
+#if defined(EM_MSC_RUN_FROM_FLASH) && defined(_EFM32_GECKO_FAMILY)
+#error "Running Flash write/erase operations from Flash is not supported on EFM32G."
+#endif
+
+/*******************************************************************************
+ ****************************** DEFINES ******************************
+ ******************************************************************************/
+#if defined(MSC_WRITECTRL_WDOUBLE)
+#define WORDS_PER_DATA_PHASE (FLASH_SIZE < (512 * 1024) ? 1 : 2)
+#else
+#define WORDS_PER_DATA_PHASE (1)
+#endif
+
+#if defined(_SILICON_LABS_GECKO_INTERNAL_SDID_80)
+/* Fix for errata FLASH_E201 - Potential program failure after Power On */
+#define ERRATA_FIX_FLASH_E201_EN
+#endif
+
+#if defined(_MSC_ECCCTRL_MASK)
+#if defined(_SILICON_LABS_32B_SERIES_1_CONFIG_1)
+/* On Series 1 Config 1, EFM32GG11, ECC is supported for RAM0 and RAM1
+ banks (not RAM2). It is necessary to figure out which is biggest to
+ calculate the number of DMA descriptors needed. */
+#define ECC_RAM_SIZE_MAX (SL_MAX(RAM0_MEM_SIZE, RAM1_MEM_SIZE))
+
+#define ECC_RAM0_MEM_BASE (RAM0_MEM_BASE)
+#define ECC_RAM0_MEM_SIZE (RAM0_MEM_SIZE)
+
+#define ECC_RAM1_MEM_BASE (RAM1_MEM_BASE)
+#define ECC_RAM1_MEM_SIZE (RAM1_MEM_SIZE)
+
+#define ECC_CTRL_REG_ADDR (&MSC->ECCCTRL)
+#define ECC_RAM0_WRITE_EN (_MSC_ECCCTRL_RAMECCEWEN_SHIFT)
+#define ECC_RAM0_CHECK_EN (_MSC_ECCCTRL_RAMECCCHKEN_SHIFT)
+#define ECC_RAM1_WRITE_EN (_MSC_ECCCTRL_RAM1ECCEWEN_SHIFT)
+#define ECC_RAM1_CHECK_EN (_MSC_ECCCTRL_RAM1ECCCHKEN_SHIFT)
+
+#define ECC_IFC_REG_ADDR (&MSC->IFC)
+#define ECC_IFC_MASK (MSC_IFC_RAMERR1B | MSC_IFC_RAMERR2B \
+ | MSC_IFC_RAM1ERR1B | MSC_IFC_RAM1ERR2B)
+#else
+#error Unknown device.
+#endif
+
+#define ECC_DMA_MAX_XFERCNT (_LDMA_CH_CTRL_XFERCNT_MASK \
+ >> _LDMA_CH_CTRL_XFERCNT_SHIFT)
+#define ECC_DMA_DESC_SIZE ((ECC_DMA_MAX_XFERCNT + 1) * 4) /* 4 bytes units */
+
+#define ECC_DMA_DESCS (ECC_RAM_SIZE_MAX / ECC_DMA_DESC_SIZE)
+
+#endif
+
+/*******************************************************************************
+ ****************************** TYPEDEFS ******************************
+ ******************************************************************************/
+typedef enum {
+ mscWriteIntSafe,
+ mscWriteFast,
+} MSC_WriteStrategy_Typedef;
+
+#if defined(_MSC_ECCCTRL_MASK)
+typedef struct {
+ volatile uint32_t *ctrlReg;
+ uint32_t writeEnBit;
+ uint32_t checkEnBit;
+ volatile uint32_t *ifClearReg;
+ uint32_t ifClearMask;
+ uint32_t base;
+ uint32_t size;
+} MSC_EccBank_Typedef;
+#endif
+
+/*******************************************************************************
+ ****************************** LOCALS *******************************
+ ******************************************************************************/
+#if defined(_MSC_ECCCTRL_MASK)
+static const MSC_EccBank_Typedef eccBank[MSC_ECC_BANKS] =
+{
+ { ECC_CTRL_REG_ADDR, ECC_RAM0_WRITE_EN, ECC_RAM0_CHECK_EN,
+ ECC_IFC_REG_ADDR, ECC_IFC_MASK,
+ ECC_RAM0_MEM_BASE, ECC_RAM0_MEM_SIZE },
+#if MSC_ECC_BANKS > 1
+ { ECC_CTRL_REG_ADDR, ECC_RAM1_WRITE_EN, ECC_RAM1_CHECK_EN,
+ ECC_IFC_REG_ADDR, ECC_IFC_MASK,
+ ECC_RAM1_MEM_BASE, ECC_RAM1_MEM_SIZE },
+#endif
+};
+#endif
+
+/*******************************************************************************
+ ****************************** FUNCTIONS ******************************
+ ******************************************************************************/
+MSC_RAMFUNC_DECLARATOR MSC_Status_TypeDef
+MSC_WriteWordI(uint32_t *address,
+ void const *data,
+ uint32_t numBytes,
+ MSC_WriteStrategy_Typedef writeStrategy);
+
+MSC_RAMFUNC_DECLARATOR MSC_Status_TypeDef
+MSC_LoadWriteData(uint32_t* data,
+ uint32_t numWords,
+ MSC_WriteStrategy_Typedef writeStrategy);
+
+MSC_RAMFUNC_DECLARATOR MSC_Status_TypeDef
+MSC_LoadVerifyAddress(uint32_t* address);
+
+#if !defined(EM_MSC_RUN_FROM_FLASH)
+
+MSC_RAMFUNC_DECLARATOR void mscRfAssertEFM(const char *file, int line);
+
+/***************************************************************************//**
+ * @brief
+ * Local ramfunc assertEFM.
+ *
+ * A local ramfunc version of assertEFM is needed because certain MSC functions
+ * are allocated to RAM. The Flash may get erased and code normally located in
+ * Flash must therefore have a RAM copy.
+ *
+ * This function is invoked through EFM_ASSERT() macro usage only and should
+ * not be used explicitly.
+ *
+ * @param[in] file
+ * The source file where assertion failed.
+ *
+ * @param[in] line
+ * A line number in the source file where assertion failed.
+ ******************************************************************************/
+MSC_RAMFUNC_DEFINITION_BEGIN
+void mscRfAssertEFM(const char *file, int line)
+{
+ (void)file; /* Unused parameter */
+ (void)line; /* Unused parameter */
+
+ while (true) {
+ }
+}
+MSC_RAMFUNC_DEFINITION_END
+
+/* Undef the define from em_assert.h and redirect to a local ramfunc version. */
+#undef EFM_ASSERT
+#if defined(DEBUG_EFM) || defined(DEBUG_EFM_USER)
+#define EFM_ASSERT(expr) ((expr) ? ((void)0) : mscRfAssertEFM(__FILE__, __LINE__))
+#else
+#define EFM_ASSERT(expr) ((void)(expr))
+#endif /* defined(DEBUG_EFM) || defined(DEBUG_EFM_USER) */
+
+#endif /* !EM_MSC_RUN_FROM_FLASH */
+
+/** @endcond */
+
+/***************************************************************************//**
+ * @addtogroup emlib
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup MSC
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Enables the flash controller for writing.
+ * @note
+ * This function must be called before flash operations when
+ * AUXHFRCO clock has been changed from a default band.
+ ******************************************************************************/
+void MSC_Init(void)
+{
+#if defined(_MSC_TIMEBASE_MASK)
+ uint32_t freq, cycles;
+#endif
+
+#if defined(_EMU_STATUS_VSCALE_MASK)
+ /* VSCALE must be done. Flash erase and write requires VSCALE2. */
+ EFM_ASSERT(!(EMU->STATUS & _EMU_STATUS_VSCALEBUSY_MASK));
+ EFM_ASSERT((EMU->STATUS & _EMU_STATUS_VSCALE_MASK) == EMU_STATUS_VSCALE_VSCALE2);
+#endif
+
+ /* Unlock the MSC module. */
+ MSC->LOCK = MSC_UNLOCK_CODE;
+ /* Disable writing to the Flash. */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+
+#if defined(_MSC_TIMEBASE_MASK)
+ /* Configure MSC->TIMEBASE according to a selected frequency. */
+ freq = CMU_ClockFreqGet(cmuClock_AUX);
+
+ /* Timebase 5us is used for the 1/1.2 MHz band only. Note that the 1 MHz band
+ is tuned to 1.2 MHz on newer revisions. */
+ if (freq > 1200000) {
+ /* Calculate a number of clock cycles for 1 us as a base period. */
+ freq = (freq * 11) / 10;
+ cycles = (freq / 1000000) + 1;
+
+ /* Configure clock cycles for flash timing. */
+ MSC->TIMEBASE = (MSC->TIMEBASE & ~(_MSC_TIMEBASE_BASE_MASK
+ | _MSC_TIMEBASE_PERIOD_MASK))
+ | MSC_TIMEBASE_PERIOD_1US
+ | (cycles << _MSC_TIMEBASE_BASE_SHIFT);
+ } else {
+ /* Calculate a number of clock cycles for 5 us as a base period. */
+ freq = (freq * 5 * 11) / 10;
+ cycles = (freq / 1000000) + 1;
+
+ /* Configure clock cycles for flash timing */
+ MSC->TIMEBASE = (MSC->TIMEBASE & ~(_MSC_TIMEBASE_BASE_MASK
+ | _MSC_TIMEBASE_PERIOD_MASK))
+ | MSC_TIMEBASE_PERIOD_5US
+ | (cycles << _MSC_TIMEBASE_BASE_SHIFT);
+ }
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Disables the flash controller for writing.
+ ******************************************************************************/
+void MSC_Deinit(void)
+{
+ /* Disable writing to the Flash. */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+ /* Lock the MSC module.*/
+ MSC->LOCK = 0;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Set the MSC code execution configuration.
+ *
+ * @param[in] execConfig
+ * The code execution configuration.
+ ******************************************************************************/
+void MSC_ExecConfigSet(MSC_ExecConfig_TypeDef *execConfig)
+{
+ uint32_t mscReadCtrl;
+
+#if defined(MSC_READCTRL_MODE_WS0SCBTP)
+ mscReadCtrl = MSC->READCTRL & _MSC_READCTRL_MODE_MASK;
+ if ((mscReadCtrl == MSC_READCTRL_MODE_WS0) && (execConfig->scbtEn)) {
+ mscReadCtrl |= MSC_READCTRL_MODE_WS0SCBTP;
+ } else if ((mscReadCtrl == MSC_READCTRL_MODE_WS1) && (execConfig->scbtEn)) {
+ mscReadCtrl |= MSC_READCTRL_MODE_WS1SCBTP;
+ } else if ((mscReadCtrl == MSC_READCTRL_MODE_WS0SCBTP) && (!execConfig->scbtEn)) {
+ mscReadCtrl |= MSC_READCTRL_MODE_WS0;
+ } else if ((mscReadCtrl == MSC_READCTRL_MODE_WS1SCBTP) && (!execConfig->scbtEn)) {
+ mscReadCtrl |= MSC_READCTRL_MODE_WS1;
+ } else {
+ /* No change needed. */
+ }
+#endif
+
+ mscReadCtrl = MSC->READCTRL & ~(0
+#if defined(MSC_READCTRL_SCBTP)
+ | MSC_READCTRL_SCBTP
+#endif
+#if defined(MSC_READCTRL_USEHPROT)
+ | MSC_READCTRL_USEHPROT
+#endif
+#if defined(MSC_READCTRL_PREFETCH)
+ | MSC_READCTRL_PREFETCH
+#endif
+#if defined(MSC_READCTRL_ICCDIS)
+ | MSC_READCTRL_ICCDIS
+#endif
+#if defined(MSC_READCTRL_AIDIS)
+ | MSC_READCTRL_AIDIS
+#endif
+#if defined(MSC_READCTRL_IFCDIS)
+ | MSC_READCTRL_IFCDIS
+#endif
+ );
+ mscReadCtrl |= (0
+#if defined(MSC_READCTRL_SCBTP)
+ | (execConfig->scbtEn ? MSC_READCTRL_SCBTP : 0)
+#endif
+#if defined(MSC_READCTRL_USEHPROT)
+ | (execConfig->useHprot ? MSC_READCTRL_USEHPROT : 0)
+#endif
+#if defined(MSC_READCTRL_PREFETCH)
+ | (execConfig->prefetchEn ? MSC_READCTRL_PREFETCH : 0)
+#endif
+#if defined(MSC_READCTRL_ICCDIS)
+ | (execConfig->iccDis ? MSC_READCTRL_ICCDIS : 0)
+#endif
+#if defined(MSC_READCTRL_AIDIS)
+ | (execConfig->aiDis ? MSC_READCTRL_AIDIS : 0)
+#endif
+#if defined(MSC_READCTRL_IFCDIS)
+ | (execConfig->ifcDis ? MSC_READCTRL_IFCDIS : 0)
+#endif
+ );
+
+ MSC->READCTRL = mscReadCtrl;
+}
+
+#if defined(_MSC_ECCCTRL_MASK)
+
+/***************************************************************************//**
+ * @brief
+ * DMA read and write existing values (for ECC initialization).
+ *
+ * @details
+ * This function uses DMA to read and write the existing data values in
+ * the RAM region specified by start and size. The function will use the
+ * 2 DMA channels specified by the channels[2] array.
+ *
+ * @param[in] start
+ * A start address of the address range in RAM to read/write.
+ *
+ * @param[in] size
+ * A size of the address range in RAM to read/write.
+ *
+ * @param[in] channels[2]
+ * An array of 2 DMA channels to use.
+ ******************************************************************************/
+static void mscEccReadWriteExistingDma(uint32_t start,
+ uint32_t size,
+ uint32_t channels[2])
+{
+ uint32_t descCnt = 0;
+ uint32_t dmaDesc[ECC_DMA_DESCS][4];
+ uint32_t chMask = (1 << channels[0]) | (1 << channels[1]);
+ /* Assert that the 2 DMA channel numbers are different. */
+ EFM_ASSERT(channels[0] != channels[1]);
+
+ /* Make sure that the ECC_RAM_SIZE_MAX is a multiple of ECC_DMA_DESC_SIZE
+ to match the total xfer size of the descriptor chain with the largest
+ ECC RAM bank. */
+ EFM_ASSERT((ECC_RAM_SIZE_MAX % ECC_DMA_DESC_SIZE) == 0);
+
+ /* Initialize the LDMA descriptor chain. */
+ do {
+ dmaDesc[descCnt][0] = /* DMA desc CTRL word */
+ LDMA_CH_CTRL_STRUCTTYPE_TRANSFER
+ | LDMA_CH_CTRL_STRUCTREQ
+ | _LDMA_CH_CTRL_XFERCNT_MASK
+ | LDMA_CH_CTRL_BLOCKSIZE_ALL
+ | LDMA_CH_CTRL_REQMODE_ALL
+ | LDMA_CH_CTRL_SRCINC_ONE
+ | LDMA_CH_CTRL_SIZE_WORD
+ | LDMA_CH_CTRL_DSTINC_ONE;
+
+ /* A source and destination address. */
+ dmaDesc[descCnt][1] = start;
+ dmaDesc[descCnt][2] = start;
+ /* A link to the next descriptor. */
+ dmaDesc[descCnt][3] = LDMA_CH_LINK_LINK
+ | (((uint32_t) &dmaDesc[descCnt + 1][0])
+ & _LDMA_CH_LINK_LINKADDR_MASK);
+
+ start += ECC_DMA_DESC_SIZE;
+ size -= ECC_DMA_DESC_SIZE;
+ descCnt++;
+ } while (size);
+
+ /* Divide the descriptor list in two parts, one for each channel,
+ by setting the link bit and address 0 of the descriptor in the middle
+ to 0. */
+ dmaDesc[(descCnt / 2) - 1][3] = 0;
+
+ /* Set the last descriptor link bit and address to 0. */
+ dmaDesc[descCnt - 1][3] = 0;
+
+ /* Start the LDMA clock. */
+ CMU_ClockEnable(cmuClock_LDMA, true);
+
+ /* Round robin scheduling for all channels (0 = no fixed priority channels).
+ */
+ LDMA->CTRL = 0 << _LDMA_CTRL_NUMFIXED_SHIFT;
+ LDMA->CHEN = 0;
+ LDMA->DBGHALT = 0;
+ LDMA->REQDIS = 0;
+
+ /* Disable LDMA interrupts and clear interrupt status. */
+ LDMA->IEN = 0;
+ LDMA->IFC = 0xFFFFFFFF;
+
+ /* Disable looping. */
+ LDMA->CH[channels[0]].LOOP = 0;
+ LDMA->CH[channels[1]].LOOP = 0;
+
+ /* Set the descriptor address for the first channel. */
+ LDMA->CH[channels[0]].LINK = ((uint32_t)&dmaDesc[0][0])
+ & _LDMA_CH_LINK_LINKADDR_MASK;
+ /* Set the descriptor address for the second channel. */
+ LDMA->CH[channels[1]].LINK = ((uint32_t)&dmaDesc[descCnt / 2][0])
+ & _LDMA_CH_LINK_LINKADDR_MASK;
+ /* Clear the channel done flags. */
+ BUS_RegMaskedClear(&LDMA->CHDONE, chMask);
+
+ /* Start transfer by loading descriptors. */
+ LDMA->LINKLOAD = chMask;
+
+ /* Wait until finished. */
+ while (!(((LDMA->CHEN & chMask) == 0)
+ && ((LDMA->CHDONE & chMask) == chMask))) {
+ }
+
+ /* Stop the LDMA clock. */
+ CMU_ClockEnable(cmuClock_LDMA, false);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Initialize ECC for a given memory bank.
+ *
+ * @brief
+ * This function initializes ECC for a given memory bank which is specified
+ * with the MSC_EccBank_Typedef structure input parameter.
+ *
+ * @param[in] eccBank
+ * The ECC memory bank device structure.
+ *
+ * @param[in] dmaChannels
+ * An array of 2 DMA channels that may be used during ECC initialization.
+ *
+ ******************************************************************************/
+static void mscEccBankInit(const MSC_EccBank_Typedef *eccBank,
+ uint32_t dmaChannels[2])
+{
+ uint32_t ctrlReg;
+
+ CORE_DECLARE_IRQ_STATE;
+
+ CORE_ENTER_CRITICAL();
+
+ /* Enable the ECC write. Keep ECC checking disabled during initialization. */
+ ctrlReg = *eccBank->ctrlReg;
+ ctrlReg |= 1 << eccBank->writeEnBit;
+ *eccBank->ctrlReg = ctrlReg;
+
+ /* Initialize ECC syndromes by using DMA to read and write the existing
+ data values in RAM. */
+ mscEccReadWriteExistingDma(eccBank->base, eccBank->size, dmaChannels);
+
+ /* Clear any ECC errors that may have been reported before or during
+ initialization. */
+ *eccBank->ifClearReg = eccBank->ifClearMask;
+
+ /* Enable the ECC decoder to detect and report ECC errors. */
+ ctrlReg |= 1 << eccBank->checkEnBit;
+ *eccBank->ctrlReg = ctrlReg;
+
+ CORE_EXIT_CRITICAL();
+}
+
+/***************************************************************************//**
+ * @brief
+ * Disable ECC for a given memory bank.
+ *
+ * @brief
+ * This function disables ECC for a given memory bank which is specified
+ * with the MSC_EccBank_Typedef structure input parameter.
+ *
+ * @param[in] eccBank
+ * ECC memory bank device structure.
+ *
+ ******************************************************************************/
+static void mscEccBankDisable(const MSC_EccBank_Typedef *eccBank)
+{
+ /* Disable ECC write (encoder) and checking (decoder). */
+ *eccBank->ctrlReg &= ~((1 << eccBank->writeEnBit) | (1 << eccBank->checkEnBit));
+}
+
+/***************************************************************************//**
+ * @brief
+ * Configure Error Correcting Code (ECC)
+ *
+ * @details
+ * This function configures ECC support according to the configuration
+ * input parameter. If the user requests enabling ECC for a given RAM bank,
+ * this function will initialize ECC memory (syndromes) for the bank by
+ * reading and writing the existing values in memory, i.e., all data is
+ * preserved. The initialization process runs in a critical section
+ * disallowing interrupts and thread scheduling and will consume a
+ * considerable amount of clock cycles. Therefore, carefully
+ * assess where to call this function. Consider increasing
+ * the clock frequency to reduce the execution time.
+ * This function makes use of 2 DMA channels to move data to/from RAM in an
+ * efficient way. The user can select which 2 DMA channels to use
+ * to avoid conflicts with the application. However, make sure
+ * that no other DMA operations take place while this function is executing.
+ * If the application is using the DMA controller prior to calling this
+ * function, the application will need to reinitialize DMA registers after
+ * this function has completed.
+ *
+ * @note
+ * This function protects the ECC initialization procedure from interrupts
+ * and other threads by using a critical section (defined by em_core.h)
+ * When running on an RTOS, the user may need to override CORE_EnterCritical
+ * CORE_ExitCritical which are declared as 'SL_WEAK' in em_core.c.
+ *
+ * @param[in] eccConfig
+ * ECC configuration
+ ******************************************************************************/
+void MSC_EccConfigSet(MSC_EccConfig_TypeDef *eccConfig)
+{
+ unsigned int cnt;
+
+#if defined(_SILICON_LABS_32B_SERIES_1_CONFIG_1)
+ /* On Series 1 Config 1, EFM32GG11, disable the ECC fault enable. */
+ MSC->CTRL &= ~MSC_CTRL_RAMECCERRFAULTEN;
+#endif
+
+ /* Loop through the ECC banks array and enable or disable according to
+ the eccConfig->enableEccBank array. */
+ for (cnt = 0; cnt < MSC_ECC_BANKS; cnt++) {
+ if (eccConfig->enableEccBank[cnt]) {
+ mscEccBankInit(&eccBank[cnt], eccConfig->dmaChannels);
+ } else {
+ mscEccBankDisable(&eccBank[cnt]);
+ }
+ }
+}
+
+#endif /* #if defined(_MSC_ECCCTRL_MASK) */
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/***************************************************************************//**
+ * @brief
+ * Perform the address phase of the flash write cycle.
+ * @details
+ * This function performs the address phase of a flash write operation by
+ * writing the given flash address to the ADDRB register and issuing the
+ * LADDRIM command to load the address.
+ * @param[in] address
+ * An address in flash memory. Must be aligned at a 4 byte boundary.
+ * @return
+ * Returns the status of the address load operation, #MSC_Status_TypeDef
+ * @verbatim
+ * mscReturnOk - The operation completed successfully.
+ * mscReturnInvalidAddr - The operation tried to erase a non-flash area.
+ * mscReturnLocked - The operation tried to erase a locked area of the Flash.
+ * @endverbatim
+ ******************************************************************************/
+MSC_RAMFUNC_DEFINITION_BEGIN
+MSC_Status_TypeDef MSC_LoadVerifyAddress(uint32_t* address)
+{
+ uint32_t status;
+ uint32_t timeOut;
+
+ /* Wait for the MSC to become ready. */
+ timeOut = MSC_PROGRAM_TIMEOUT;
+ while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) {
+ timeOut--;
+ }
+
+ /* Check for timeout. */
+ if (timeOut == 0) {
+ return mscReturnTimeOut;
+ }
+ /* Load the address. */
+ MSC->ADDRB = (uint32_t)address;
+ MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
+
+ status = MSC->STATUS;
+ if (status & (MSC_STATUS_INVADDR | MSC_STATUS_LOCKED)) {
+ /* Check for an invalid address. */
+ if (status & MSC_STATUS_INVADDR) {
+ return mscReturnInvalidAddr;
+ }
+ /* Check for the write protected page. */
+ if (status & MSC_STATUS_LOCKED) {
+ return mscReturnLocked;
+ }
+ }
+ return mscReturnOk;
+}
+MSC_RAMFUNC_DEFINITION_END
+
+/***************************************************************************//**
+ * @brief
+ * Perform a flash data write phase.
+ * @details
+ * This function performs the data phase of a flash write operation by loading
+ * the given number of 32-bit words to the WDATA register.
+ * @param[in] data
+ * A pointer to the first data word to load.
+ * @param[in] numWords
+ * A number of data words (32-bit) to load.
+ * @param[in] writeStrategy
+ * A write strategy to apply.
+ * @return
+ * Returns the status of the data load operation.
+ * @verbatim
+ * mscReturnOk - An operation completed successfully.
+ * mscReturnTimeOut - An operation timed out waiting for the flash operation
+ * to complete.
+ * @endverbatim
+ ******************************************************************************/
+MSC_RAMFUNC_DEFINITION_BEGIN
+MSC_Status_TypeDef MSC_LoadWriteData(uint32_t* data,
+ uint32_t numWords,
+ MSC_WriteStrategy_Typedef writeStrategy)
+{
+ uint32_t timeOut;
+ uint32_t wordIndex;
+ bool useWDouble = false;
+ MSC_Status_TypeDef retval = mscReturnOk;
+#if !defined(_EFM32_GECKO_FAMILY)
+ uint32_t irqState;
+#endif
+
+#if defined(_MSC_WRITECTRL_LPWRITE_MASK) && defined(_MSC_WRITECTRL_WDOUBLE_MASK)
+ /* If the LPWRITE (Low Power Write) is NOT enabled, set WDOUBLE (Write Double word). */
+ if (!(MSC->WRITECTRL & MSC_WRITECTRL_LPWRITE)) {
+#if defined(_SILICON_LABS_32B_SERIES_0)
+ /* If the number of words to be written is odd, align by writing
+ a single word first, before setting the WDOUBLE bit. */
+ if (numWords & 0x1) {
+ /* Wait for the MSC to become ready for the next word. */
+ timeOut = MSC_PROGRAM_TIMEOUT;
+ while ((!(MSC->STATUS & MSC_STATUS_WDATAREADY)) && (timeOut != 0)) {
+ timeOut--;
+ }
+ /* Check for timeout. */
+ if (timeOut == 0) {
+ return mscReturnTimeOut;
+ }
+ /* Clear the double word option to write the initial single word. */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE;
+ /* Write first data word. */
+ MSC->WDATA = *data++;
+ MSC->WRITECMD = MSC_WRITECMD_WRITEONCE;
+
+ /* Wait for the operation to finish. It may be required to change the WDOUBLE
+ configuration after the initial write. It should not be changed while BUSY. */
+ timeOut = MSC_PROGRAM_TIMEOUT;
+ while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) {
+ timeOut--;
+ }
+ /* Check for timeout. */
+ if (timeOut == 0) {
+ return mscReturnTimeOut;
+ }
+ /* Subtract this initial odd word for the write loop below. */
+ numWords -= 1;
+ retval = mscReturnOk;
+ }
+ /* Set the double word option to write two words per
+ data phase. */
+#endif
+ MSC->WRITECTRL |= MSC_WRITECTRL_WDOUBLE;
+ useWDouble = true;
+ }
+#endif /* defined( _MSC_WRITECTRL_LPWRITE_MASK ) && defined( _MSC_WRITECTRL_WDOUBLE_MASK ) */
+
+ /* Write the rest as a double word write if wordsPerDataPhase == 2 */
+ if (numWords > 0) {
+ /**** Write strategy: mscWriteIntSafe ****/
+ if (writeStrategy == mscWriteIntSafe) {
+ /* Requires a system core clock at 1MHz or higher */
+ EFM_ASSERT(SystemCoreClock >= 1000000);
+ wordIndex = 0;
+ while (wordIndex < numWords) {
+ if (!useWDouble) {
+ MSC->WDATA = *data++;
+ wordIndex++;
+ MSC->WRITECMD = MSC_WRITECMD_WRITEONCE;
+ } else { // useWDouble == true
+ /* Trigger a double write according to flash properties. */
+#if defined(_SILICON_LABS_32B_SERIES_0) && defined(_MSC_WRITECTRL_WDOUBLE_MASK)
+ MSC->WDATA = *data++;
+ while (!(MSC->STATUS & MSC_STATUS_WDATAREADY)) ;
+ MSC->WDATA = *data++;
+ wordIndex += 2;
+ MSC->WRITECMD = MSC_WRITECMD_WRITEONCE;
+
+#elif defined(_SILICON_LABS_32B_SERIES_1) && defined(_MSC_WRITECTRL_WDOUBLE_MASK)
+ while (!(MSC->STATUS & MSC_STATUS_WDATAREADY)) ;
+ do {
+ MSC->WDATA = *data++;
+ wordIndex++;
+ } while ((MSC->STATUS & MSC_STATUS_WDATAREADY)
+ && (wordIndex < numWords));
+ MSC->WRITECMD = MSC_WRITECMD_WRITETRIG;
+#endif
+ }
+
+ /* Wait for the transaction to finish. */
+ timeOut = MSC_PROGRAM_TIMEOUT;
+ while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) {
+ timeOut--;
+ }
+ /* Check for a timeout. */
+ if (timeOut == 0) {
+ retval = mscReturnTimeOut;
+ break;
+ }
+#if defined(_EFM32_GECKO_FAMILY)
+ MSC->ADDRB += 4;
+ MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
+#endif
+ }
+ }
+ /**** Write strategy: mscWriteFast ****/
+ else {
+#if defined(_EFM32_GECKO_FAMILY)
+ /* Gecko does not have auto-increment of ADDR. */
+ EFM_ASSERT(false);
+#else
+ /* Requires a system core clock at 14 MHz or higher. */
+ EFM_ASSERT(SystemCoreClock >= 14000000);
+
+ /*
+ * Protect from interrupts to be sure to satisfy the us timing
+ * needs of the MSC flash programming state machine.
+ */
+ irqState = __get_PRIMASK();
+ __disable_irq();
+
+ wordIndex = 0;
+ while (wordIndex < numWords) {
+ /* Wait for the MSC to be ready for the next word. */
+ while (!(MSC->STATUS & MSC_STATUS_WDATAREADY)) {
+ /* If the write to MSC->WDATA below missed the 30 us timeout and the
+ following MSC_WRITECMD_WRITETRIG command arrived while
+ MSC_STATUS_BUSY is 1, the MSC_WRITECMD_WRITETRIG could be ignored by
+ the MSC. In this case, MSC_STATUS_WORDTIMEOUT is set to 1
+ and MSC_STATUS_BUSY is 0. A new trigger is therefore needed to
+ complete write of data in MSC->WDATA.
+ If WDATAREADY became high since entering the loop, exit and continue
+ to the next WDATA write.
+ */
+ if ((MSC->STATUS & (MSC_STATUS_WORDTIMEOUT
+ | MSC_STATUS_BUSY
+ | MSC_STATUS_WDATAREADY))
+ == MSC_STATUS_WORDTIMEOUT) {
+ MSC->WRITECMD = MSC_WRITECMD_WRITETRIG;
+ }
+ }
+
+ if (!useWDouble) {
+ MSC->WDATA = *data;
+ MSC->WRITECMD = MSC_WRITECMD_WRITETRIG;
+ data++;
+ wordIndex++;
+ } else { // useWDouble == true
+ /* Trigger double write according to flash properties. */
+#if defined(_SILICON_LABS_32B_SERIES_0)
+ MSC->WDATA = *data;
+ if (wordIndex & 0x1) {
+ MSC->WRITECMD = MSC_WRITECMD_WRITETRIG;
+ }
+ data++;
+ wordIndex++;
+
+#elif (_SILICON_LABS_32B_SERIES_1_CONFIG >= 2)
+ do {
+ MSC->WDATA = *data++;
+ wordIndex++;
+ } while ((MSC->STATUS & MSC_STATUS_WDATAREADY)
+ && (wordIndex < numWords));
+ MSC->WRITECMD = MSC_WRITECMD_WRITETRIG;
+#endif
+ }
+ }
+
+ if (irqState == 0) {
+ /* Restore the previous interrupt state. */
+ __enable_irq();
+ }
+
+ /* Wait for the transaction to finish. */
+ timeOut = MSC_PROGRAM_TIMEOUT;
+ while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) {
+ timeOut--;
+ }
+ /* Check for a timeout. */
+ if (timeOut == 0) {
+ retval = mscReturnTimeOut;
+ }
+#endif
+ } /* writeStrategy */
+ }
+
+#if defined(_MSC_WRITECTRL_WDOUBLE_MASK)
+ /* Clear a double word option, which should not be left on when returning. */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE;
+#endif
+
+ return retval;
+}
+MSC_RAMFUNC_DEFINITION_END
+
+/***************************************************************************//**
+ * @brief
+ * An internal flash write function with the select write strategy parameter.
+ * @param[in] address
+ * A write address.
+ * @param[in] data
+ * A pointer to the first data word to load.
+ * @param[in] numBytes
+ * A nsumber of data bytes to load, which must be a multiple of 4 bytes.
+ * @param[in] writeStrategy
+ * A wWrite strategy to apply.
+ * @return
+ * Returns the status of the data load operation.
+ ******************************************************************************/
+MSC_RAMFUNC_DEFINITION_BEGIN
+MSC_Status_TypeDef MSC_WriteWordI(uint32_t *address,
+ void const *data,
+ uint32_t numBytes,
+ MSC_WriteStrategy_Typedef writeStrategy)
+{
+ uint32_t wordCount;
+ uint32_t numWords;
+ uint32_t pageWords;
+ uint32_t* pData;
+ MSC_Status_TypeDef retval = mscReturnOk;
+
+ /* Check alignment (must be aligned to words). */
+ EFM_ASSERT(((uint32_t) address & 0x3) == 0);
+
+ /* Check a number of bytes. Must be divisible by four. */
+ EFM_ASSERT((numBytes & 0x3) == 0);
+
+#if defined(_EMU_STATUS_VSCALE_MASK)
+ /* VSCALE must be done and flash write requires VSCALE2. */
+ EFM_ASSERT(!(EMU->STATUS & _EMU_STATUS_VSCALEBUSY_MASK));
+ EFM_ASSERT((EMU->STATUS & _EMU_STATUS_VSCALE_MASK) == EMU_STATUS_VSCALE_VSCALE2);
+#endif
+
+ /* Enable writing to the MSC module. */
+ MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
+
+ /* Convert bytes to words. */
+ numWords = numBytes >> 2;
+ EFM_ASSERT(numWords > 0);
+
+ /* The following loop splits the data into chunks corresponding to flash pages.
+ The address is loaded only once per page because the hardware automatically
+ increments the address internally for each data load inside a page. */
+ for (wordCount = 0, pData = (uint32_t *)data; wordCount < numWords; ) {
+ /* First, the address is loaded. The address is auto-incremented within a page.
+ Therefore, the address phase is only needed once for each page. */
+ retval = MSC_LoadVerifyAddress(address + wordCount);
+ if (mscReturnOk != retval) {
+ return retval;
+ }
+ /* Compute the number of words to write to the current page. */
+ pageWords =
+ (FLASH_PAGE_SIZE
+ - (((uint32_t) (address + wordCount)) & (FLASH_PAGE_SIZE - 1)))
+ / sizeof(uint32_t);
+ if (pageWords > numWords - wordCount) {
+ pageWords = numWords - wordCount;
+ }
+ /* Write the data in the current page. */
+ retval = MSC_LoadWriteData(pData, pageWords, writeStrategy);
+ if (mscReturnOk != retval) {
+ break;
+ }
+ wordCount += pageWords;
+ pData += pageWords;
+ }
+
+#if defined(ERRATA_FIX_FLASH_E201_EN)
+ /* Fix for errata FLASH_E201 - Potential program failure after Power On.
+ *
+ * Check if the first word was programmed correctly. If a failure is detected,
+ * retry programming of the first word.
+ *
+ * A full description of the errata is in the errata document. */
+ pData = (uint32_t *) data;
+ if (*address != *pData) {
+ retval = MSC_LoadVerifyAddress(address);
+ if (mscReturnOk == retval) {
+ retval = MSC_LoadWriteData(pData, 1, writeStrategy);
+ }
+ }
+#endif
+
+ /* Disable writing to the MSC module. */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+
+#if defined(_MSC_WRITECTRL_WDOUBLE_MASK)
+#if (WORDS_PER_DATA_PHASE == 2)
+ /* Turn off the double word write cycle support. */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WDOUBLE;
+#endif
+#endif
+
+ return retval;
+}
+MSC_RAMFUNC_DEFINITION_END
+
+/** @endcond */
+
+/***************************************************************************//**
+ * @brief
+ * Erases a page in flash memory.
+ * @note
+ * It is recommended to run this code from RAM. On the Gecko family, it is required
+ * to run this function from RAM.
+ *
+ * For IAR IDE, Rowley IDE, SimplicityStudio IDE, Atollic IDE, and ARM GCC IDE, this is
+ * achieved automatically by using attributes in the function proctype. For Keil
+ * uVision IDE, define a section called "ram_code" and place this manually in
+ * the project's scatter file.
+ *
+ * @param[in] startAddress
+ * A pointer to the flash page to erase. Must be aligned to the beginning of the page
+ * boundary.
+ * @return
+ * Returns the status of erase operation, #MSC_Status_TypeDef
+ * @verbatim
+ * mscReturnOk - The operation completed successfully.
+ * mscReturnInvalidAddr - The operation tried to erase a non-flash area.
+ * mscReturnLocked - The operation tried to erase a locked area of the flash.
+ * mscReturnTimeOut - The operation timed out waiting for the flash operation
+ * to complete.
+ * @endverbatim
+ ******************************************************************************/
+MSC_RAMFUNC_DEFINITION_BEGIN
+MSC_Status_TypeDef MSC_ErasePage(uint32_t *startAddress)
+{
+ uint32_t timeOut = MSC_PROGRAM_TIMEOUT;
+
+ /* An address must be aligned to pages. */
+ EFM_ASSERT((((uint32_t) startAddress) & (FLASH_PAGE_SIZE - 1)) == 0);
+#if defined(_EMU_STATUS_VSCALE_MASK)
+ /* VSCALE must be done and flash erase requires VSCALE2. */
+ EFM_ASSERT(!(EMU->STATUS & _EMU_STATUS_VSCALEBUSY_MASK));
+ EFM_ASSERT((EMU->STATUS & _EMU_STATUS_VSCALE_MASK) == EMU_STATUS_VSCALE_VSCALE2);
+#endif
+
+ /* Enable writing to the MSC module. */
+ MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
+
+ /* Load an address. */
+ MSC->ADDRB = (uint32_t)startAddress;
+ MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
+
+ /* Check for an invalid address. */
+ if (MSC->STATUS & MSC_STATUS_INVADDR) {
+ /* Disable writing to the MSC */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+ return mscReturnInvalidAddr;
+ }
+ /* Check for write protected page. */
+ if (MSC->STATUS & MSC_STATUS_LOCKED) {
+ /* Disable writing to the MSC module. */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+ return mscReturnLocked;
+ }
+ /* Send erase page command. */
+ MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE;
+
+ /* Wait for the erase to complete. */
+ while ((MSC->STATUS & MSC_STATUS_BUSY) && (timeOut != 0)) {
+ timeOut--;
+ }
+ if (timeOut == 0) {
+ /* Disable writing to the MSC module. */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+ return mscReturnTimeOut;
+ }
+ /* Disable writing to the MSC module. */
+ MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
+ return mscReturnOk;
+}
+MSC_RAMFUNC_DEFINITION_END
+
+/***************************************************************************//**
+ * @brief
+ * Writes data to flash memory. This function is interrupt-safe, but slower than
+ * MSC_WriteWordFast(), which writes to flash with interrupts disabled.
+ * Write data must be aligned to words and contain a number of bytes that is
+ * divisible by four.
+ * @note
+ * It is recommended to erase the flash page before performing a write.
+ *
+ * It is recommended to run this code from RAM. On the Gecko family, it is required
+ * to run this function from RAM.
+ *
+ * For IAR IDE, Rowley IDE, SimplicityStudio IDE, Atollic IDE, and ARM GCC IDE,
+ * this is done automatically by using attributes in the function proctype.
+ * For Keil uVision IDE, define a section called "ram_code" and place it
+ * manually in the project's scatter file.
+ *
+ * This function requires a system core clock at 1 MHz or higher.
+ *
+ * @param[in] address
+ * A pointer to the flash word to write to. Must be aligned to words.
+ * @param[in] data
+ * Data to write to flash.
+ * @param[in] numBytes
+ * A number of bytes to write from flash. NB: Must be divisible by four.
+ * @return
+ * Returns the status of the write operation.
+ * @verbatim
+ * flashReturnOk - The operation completed successfully.
+ * flashReturnInvalidAddr - The operation tried to erase a non-flash area.
+ * flashReturnLocked - The operation tried to erase a locked area of the Flash.
+ * flashReturnTimeOut - The operation timed out waiting for the flash operation
+ * to complete, or the MSC module timed out waiting for the software to write
+ * the next word into the DWORD register.
+ * @endverbatim
+ ******************************************************************************/
+MSC_RAMFUNC_DEFINITION_BEGIN
+MSC_Status_TypeDef MSC_WriteWord(uint32_t *address,
+ void const *data,
+ uint32_t numBytes)
+{
+ return MSC_WriteWordI(address, data, numBytes, mscWriteIntSafe);
+}
+MSC_RAMFUNC_DEFINITION_END
+
+#if !defined(_EFM32_GECKO_FAMILY)
+/***************************************************************************//**
+ * @brief
+ * Writes data to flash memory. This function is faster than MSC_WriteWord(),
+ * but it disables interrupts. Write data must be aligned to words and contain
+ * a number of bytes that is divisible by four.
+ * @note
+ * It is recommended to erase the flash page before performing a write.
+ * It is required to run this function from RAM on parts that include a
+ * flash write buffer.
+ *
+ * For IAR IDE, Rowley IDE, SimplicityStudio IDE, Atollic IDE, and ARM GCC IDE,
+ * this is done automatically by using attributes in the function proctype.
+ * For Keil uVision IDE, define a section called "ram_code" and place this manually
+ * in the project's scatter file.
+ *
+ * @param[in] address
+ * A pointer to the flash word to write to. Must be aligned to words.
+ * @param[in] data
+ * Data to write to flash.
+ * @param[in] numBytes
+ * A number of bytes to write from the Flash. NB: Must be divisible by four.
+ * @return
+ * Returns the status of the write operation.
+ * @verbatim
+ * flashReturnOk - The operation completed successfully.
+ * flashReturnInvalidAddr - The operation tried to erase a non-flash area.
+ * flashReturnLocked - The operation tried to erase a locked area of the flash.
+ * flashReturnTimeOut - The operation timed out waiting for flash operation
+ * to complete. Or the MSC timed out waiting for the software to write
+ * the next word into the DWORD register.
+ * @endverbatim
+ ******************************************************************************/
+#if !defined (EM_MSC_RUN_FROM_FLASH) || (_SILICON_LABS_GECKO_INTERNAL_SDID < 84)
+MSC_RAMFUNC_DEFINITION_BEGIN
+MSC_Status_TypeDef MSC_WriteWordFast(uint32_t *address,
+ void const *data,
+ uint32_t numBytes)
+{
+ return MSC_WriteWordI(address, data, numBytes, mscWriteFast);
+}
+MSC_RAMFUNC_DEFINITION_END
+
+#endif
+#endif
+
+#if defined(_MSC_MASSLOCK_MASK)
+/***************************************************************************//**
+ * @brief
+ * Erase the entire Flash in one operation.
+ *
+ * @note
+ * This command will erase the entire contents of the device.
+ * Use with care, both a debug session and all contents of the flash will be
+ * lost. The lock bit, MLW will prevent this operation from executing and
+ * might prevent a successful mass erase.
+ ******************************************************************************/
+MSC_RAMFUNC_DEFINITION_BEGIN
+MSC_Status_TypeDef MSC_MassErase(void)
+{
+ /* Enable writing to the MSC module. */
+ MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
+
+ /* Unlock the device mass erase. */
+ MSC->MASSLOCK = MSC_MASSLOCK_LOCKKEY_UNLOCK;
+
+ /* Erase the first 512 K block. */
+ MSC->WRITECMD = MSC_WRITECMD_ERASEMAIN0;
+
+ /* Waiting for erase to complete. */
+ while ((MSC->STATUS & MSC_STATUS_BUSY) != 0U) {
+ }
+
+#if ((FLASH_SIZE >= (512 * 1024)) && defined(_MSC_WRITECMD_ERASEMAIN1_MASK))
+ /* Erase the second 512 K block. */
+ MSC->WRITECMD = MSC_WRITECMD_ERASEMAIN1;
+
+ /* Waiting for erase to complete. */
+ while ((MSC->STATUS & MSC_STATUS_BUSY) != 0U) {
+ }
+#endif
+
+ /* Restore the mass erase lock. */
+ MSC->MASSLOCK = MSC_MASSLOCK_LOCKKEY_LOCK;
+
+ /* This will only successfully return if calling function is also in SRAM. */
+ return mscReturnOk;
+}
+MSC_RAMFUNC_DEFINITION_END
+
+#endif
+
+/** @} (end addtogroup MSC) */
+/** @} (end addtogroup emlib) */
+#endif /* defined(MSC_COUNT) && (MSC_COUNT > 0) */
diff --git a/efm32boot/emlib/em_system.c b/efm32boot/emlib/em_system.c
new file mode 100644
index 0000000..6877ac4
--- /dev/null
+++ b/efm32boot/emlib/em_system.c
@@ -0,0 +1,114 @@
+/***************************************************************************//**
+ * @file em_system.c
+ * @brief System Peripheral API
+ * @version 5.2.2
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+#include "em_system.h"
+#include "em_assert.h"
+#include
+
+/***************************************************************************//**
+ * @addtogroup emlib
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup SYSTEM
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Get chip major/minor revision.
+ *
+ * @param[out] rev
+ * Location to place chip revision info.
+ ******************************************************************************/
+void SYSTEM_ChipRevisionGet(SYSTEM_ChipRevision_TypeDef *rev)
+{
+ uint8_t tmp;
+
+ EFM_ASSERT(rev);
+
+ /* CHIP FAMILY bit [5:2] */
+ tmp = (((ROMTABLE->PID1 & _ROMTABLE_PID1_FAMILYMSB_MASK) >> _ROMTABLE_PID1_FAMILYMSB_SHIFT) << 2);
+ /* CHIP FAMILY bit [1:0] */
+ tmp |= ((ROMTABLE->PID0 & _ROMTABLE_PID0_FAMILYLSB_MASK) >> _ROMTABLE_PID0_FAMILYLSB_SHIFT);
+ rev->family = tmp;
+
+ /* CHIP MAJOR bit [3:0] */
+ rev->major = (ROMTABLE->PID0 & _ROMTABLE_PID0_REVMAJOR_MASK) >> _ROMTABLE_PID0_REVMAJOR_SHIFT;
+
+ /* CHIP MINOR bit [7:4] */
+ tmp = (((ROMTABLE->PID2 & _ROMTABLE_PID2_REVMINORMSB_MASK) >> _ROMTABLE_PID2_REVMINORMSB_SHIFT) << 4);
+ /* CHIP MINOR bit [3:0] */
+ tmp |= ((ROMTABLE->PID3 & _ROMTABLE_PID3_REVMINORLSB_MASK) >> _ROMTABLE_PID3_REVMINORLSB_SHIFT);
+ rev->minor = tmp;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get factory calibration value for a given peripheral register.
+ *
+ * @param[in] regAddress
+ * Peripheral calibration register address to get calibration value for. If
+ * a calibration value is found then this register is updated with the
+ * calibration value.
+ *
+ * @return
+ * True if a calibration value exists, false otherwise.
+ ******************************************************************************/
+bool SYSTEM_GetCalibrationValue(volatile uint32_t *regAddress)
+{
+ SYSTEM_CalAddrVal_TypeDef * p, * end;
+
+ p = (SYSTEM_CalAddrVal_TypeDef *)(DEVINFO_BASE & 0xFFFFF000);
+ end = (SYSTEM_CalAddrVal_TypeDef *)DEVINFO_BASE;
+
+ for (; p < end; p++) {
+ if (p->address == 0xFFFFFFFF) {
+ /* Found table terminator */
+ return false;
+ }
+ if (p->address == (uint32_t)regAddress) {
+ *regAddress = p->calValue;
+ return true;
+ }
+ }
+ /* Nothing found for regAddress */
+ return false;
+}
+
+/** @} (end addtogroup SYSTEM) */
+/** @} (end addtogroup emlib) */
diff --git a/efm32boot/emlib/em_usart.c b/efm32boot/emlib/em_usart.c
new file mode 100644
index 0000000..3a5e65c
--- /dev/null
+++ b/efm32boot/emlib/em_usart.c
@@ -0,0 +1,1161 @@
+/***************************************************************************//**
+ * @file em_usart.c
+ * @brief Universal synchronous/asynchronous receiver/transmitter (USART/UART)
+ * Peripheral API
+ * @version 5.2.2
+ *******************************************************************************
+ * # License
+ * Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+#include "em_usart.h"
+#if defined(USART_COUNT) && (USART_COUNT > 0)
+
+#include "em_cmu.h"
+#include "em_bus.h"
+#include "em_assert.h"
+
+/***************************************************************************//**
+ * @addtogroup emlib
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup USART
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ ******************************* DEFINES ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/** Validation of USART register block pointer reference for assert statements. */
+#if (USART_COUNT == 1) && defined(USART0)
+#define USART_REF_VALID(ref) ((ref) == USART0)
+
+#elif (USART_COUNT == 1) && defined(USART1)
+#define USART_REF_VALID(ref) ((ref) == USART1)
+
+#elif (USART_COUNT == 2) && defined(USART2)
+#define USART_REF_VALID(ref) (((ref) == USART1) || ((ref) == USART2))
+
+#elif (USART_COUNT == 2)
+#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1))
+
+#elif (USART_COUNT == 3)
+#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) \
+ || ((ref) == USART2))
+#elif (USART_COUNT == 4)
+#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) \
+ || ((ref) == USART2) || ((ref) == USART3))
+#elif (USART_COUNT == 5)
+#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) \
+ || ((ref) == USART2) || ((ref) == USART3) \
+ || ((ref) == USART4))
+#elif (USART_COUNT == 6)
+#define USART_REF_VALID(ref) (((ref) == USART0) || ((ref) == USART1) \
+ || ((ref) == USART2) || ((ref) == USART3) \
+ || ((ref) == USART4) || ((ref) == USART5))
+#else
+#error "Undefined number of USARTs."
+#endif
+
+#if defined(USARTRF_COUNT) && (USARTRF_COUNT > 0)
+#if (USARTRF_COUNT == 1) && defined(USARTRF0)
+#define USARTRF_REF_VALID(ref) ((ref) == USARTRF0)
+#elif (USARTRF_COUNT == 1) && defined(USARTRF1)
+#define USARTRF_REF_VALID(ref) ((ref) == USARTRF1)
+#else
+#define USARTRF_REF_VALID(ref) (0)
+#endif
+#else
+#define USARTRF_REF_VALID(ref) (0)
+#endif
+
+#if defined(_EZR32_HAPPY_FAMILY)
+#define USART_IRDA_VALID(ref) ((ref) == USART0)
+#elif defined(_EFM32_HAPPY_FAMILY)
+#define USART_IRDA_VALID(ref) (((ref) == USART0) || ((ref) == USART1))
+#elif defined(USART0)
+#define USART_IRDA_VALID(ref) ((ref) == USART0)
+#elif (USART_COUNT == 1) && defined(USART1)
+#define USART_IRDA_VALID(ref) ((ref) == USART1)
+#elif defined(USARTRF0)
+#define USART_IRDA_VALID(ref) ((ref) == USARTRF0)
+#else
+#define USART_IRDA_VALID(ref) (0)
+#endif
+
+#if defined(_SILICON_LABS_32B_SERIES_1)
+ #if defined(USART3)
+ #define USART_I2S_VALID(ref) (((ref) == USART1) || ((ref) == USART3))
+ #else
+ #define USART_I2S_VALID(ref) ((ref) == USART1)
+ #endif
+#elif defined(_SILICON_LABS_32B_SERIES_0)
+ #if defined(_EZR32_HAPPY_FAMILY)
+ #define USART_I2S_VALID(ref) ((ref) == USART0)
+ #elif defined(_EFM32_HAPPY_FAMILY)
+ #define USART_I2S_VALID(ref) (((ref) == USART0) || ((ref) == USART1))
+ #elif defined(_EFM32_TINY_FAMILY) || defined(_EFM32_ZERO_FAMILY)
+ #define USART_I2S_VALID(ref) ((ref) == USART1)
+ #elif defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY)
+ #define USART_I2S_VALID(ref) (((ref) == USART1) || ((ref) == USART2))
+#endif
+#endif
+
+#if (UART_COUNT == 1)
+#define UART_REF_VALID(ref) ((ref) == UART0)
+#elif (UART_COUNT == 2)
+#define UART_REF_VALID(ref) (((ref) == UART0) || ((ref) == UART1))
+#else
+#define UART_REF_VALID(ref) (0)
+#endif
+
+#if defined(_USART_CLKDIV_DIVEXT_MASK)
+#define CLKDIV_MASK (_USART_CLKDIV_DIV_MASK | _USART_CLKDIV_DIVEXT_MASK)
+#else
+#define CLKDIV_MASK _USART_CLKDIV_DIV_MASK
+#endif
+
+/** @endcond */
+
+/*******************************************************************************
+ ************************** GLOBAL FUNCTIONS *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ * Configure USART/UART operating in asynchronous mode to use a given
+ * baudrate (or as close as possible to specified baudrate).
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] refFreq
+ * USART/UART reference clock frequency in Hz that will be used. If set to 0,
+ * the currently configured reference clock is assumed.
+ *
+ * @param[in] baudrate
+ * Baudrate to try to achieve for USART/UART.
+ *
+ * @param[in] ovs
+ * Oversampling to be used. Normal is 16x oversampling, but lower oversampling
+ * may be used to achieve higher rates or better baudrate accuracy in some
+ * cases. Notice that lower oversampling frequency makes channel more
+ * vulnerable to bit faults during reception due to clock inaccuracies
+ * compared to link partner.
+ ******************************************************************************/
+void USART_BaudrateAsyncSet(USART_TypeDef *usart,
+ uint32_t refFreq,
+ uint32_t baudrate,
+ USART_OVS_TypeDef ovs)
+{
+ uint32_t clkdiv;
+ uint32_t oversample;
+
+ /* Inhibit divide by 0 */
+ EFM_ASSERT(baudrate);
+
+ /*
+ * We want to use integer division to avoid forcing in float division
+ * utils, and yet keep rounding effect errors to a minimum.
+ *
+ * CLKDIV in asynchronous mode is given by:
+ *
+ * CLKDIV = 256 * (fHFPERCLK/(oversample * br) - 1)
+ * or
+ * CLKDIV = (256 * fHFPERCLK)/(oversample * br) - 256
+ *
+ * The basic problem with integer division in the above formula is that
+ * the dividend (256 * fHFPERCLK) may become higher than max 32 bit
+ * integer. Yet, we want to evaluate dividend first before dividing in
+ * order to get as small rounding effects as possible. We do not want
+ * to make too harsh restrictions on max fHFPERCLK value either.
+ *
+ * One can possibly factorize 256 and oversample/br. However,
+ * since the last 6 or 3 bits of CLKDIV are don't care, we can base our
+ * integer arithmetic on the below formula
+ *
+ * CLKDIV / 64 = (4 * fHFPERCLK)/(oversample * br) - 4 (3 bits dont care)
+ * or
+ * CLKDIV / 8 = (32 * fHFPERCLK)/(oversample * br) - 32 (6 bits dont care)
+ *
+ * and calculate 1/64 of CLKDIV first. This allows for fHFPERCLK
+ * up to 1GHz without overflowing a 32 bit value!
+ */
+
+ /* HFPERCLK used to clock all USART/UART peripheral modules */
+ if (!refFreq) {
+ refFreq = CMU_ClockFreqGet(cmuClock_HFPER);
+ }
+
+ /* Map oversampling */
+ switch (ovs) {
+ case usartOVS16:
+ EFM_ASSERT(baudrate <= (refFreq / 16));
+ oversample = 16;
+ break;
+
+ case usartOVS8:
+ EFM_ASSERT(baudrate <= (refFreq / 8));
+ oversample = 8;
+ break;
+
+ case usartOVS6:
+ EFM_ASSERT(baudrate <= (refFreq / 6));
+ oversample = 6;
+ break;
+
+ case usartOVS4:
+ EFM_ASSERT(baudrate <= (refFreq / 4));
+ oversample = 4;
+ break;
+
+ default:
+ /* Invalid input */
+ EFM_ASSERT(0);
+ return;
+ }
+
+ /* Calculate and set CLKDIV with fractional bits.
+ * The added (oversample*baudrate)/2 in the first line is to round the
+ * divisor to the nearest fractional divisor. */
+#if defined(_SILICON_LABS_32B_SERIES_0) && !defined(_EFM32_HAPPY_FAMILY)
+ /* Devices with 2 fractional bits. CLKDIV[7:6] */
+ clkdiv = 4 * refFreq + (oversample * baudrate) / 2;
+ clkdiv /= (oversample * baudrate);
+ clkdiv -= 4;
+ clkdiv *= 64;
+#else
+ /* Devices with 5 fractional bits. CLKDIV[7:3] */
+ clkdiv = 32 * refFreq + (oversample * baudrate) / 2;
+ clkdiv /= (oversample * baudrate);
+ clkdiv -= 32;
+ clkdiv *= 8;
+#endif
+
+ /* Verify that resulting clock divider is within limits */
+ EFM_ASSERT(clkdiv <= CLKDIV_MASK);
+
+ /* Make sure we don't write to reserved bits */
+ clkdiv &= CLKDIV_MASK;
+
+ usart->CTRL &= ~_USART_CTRL_OVS_MASK;
+ usart->CTRL |= ovs;
+ usart->CLKDIV = clkdiv;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Calculate baudrate for USART/UART given reference frequency, clock division
+ * and oversampling rate (if async mode).
+ *
+ * @details
+ * This function returns the baudrate that a USART/UART module will use if
+ * configured with the given frequency, clock divisor and mode. Notice that
+ * this function will not use actual HW configuration. It can be used
+ * to determinate if a given configuration is sufficiently accurate for the
+ * application.
+ *
+ * @param[in] refFreq
+ * USART/UART HF peripheral frequency used.
+ *
+ * @param[in] clkdiv
+ * Clock division factor to be used.
+ *
+ * @param[in] syncmode
+ * @li true - synchronous mode operation.
+ * @li false - asynchronous mode operation.
+ *
+ * @param[in] ovs
+ * Oversampling used if asynchronous mode. Not used if @p syncmode is true.
+ *
+ * @return
+ * Baudrate with given settings.
+ ******************************************************************************/
+uint32_t USART_BaudrateCalc(uint32_t refFreq,
+ uint32_t clkdiv,
+ bool syncmode,
+ USART_OVS_TypeDef ovs)
+{
+ uint32_t oversample;
+ uint64_t divisor;
+ uint64_t factor;
+ uint64_t remainder;
+ uint64_t quotient;
+ uint32_t br;
+
+ /* Out of bound clkdiv ? */
+ EFM_ASSERT(clkdiv <= CLKDIV_MASK);
+
+ /* Mask out unused bits */
+ clkdiv &= CLKDIV_MASK;
+
+ /* We want to use integer division to avoid forcing in float division */
+ /* utils, and yet keep rounding effect errors to a minimum. */
+
+ /* Baudrate calculation depends on if synchronous or asynchronous mode */
+ if (syncmode) {
+ /*
+ * Baudrate is given by:
+ *
+ * br = fHFPERCLK/(2 * (1 + (CLKDIV / 256)))
+ *
+ * which can be rewritten to
+ *
+ * br = (128 * fHFPERCLK)/(256 + CLKDIV)
+ */
+ oversample = 1; /* Not used in sync mode, ie 1 */
+ factor = 128;
+ } else {
+ /*
+ * Baudrate in asynchronous mode is given by:
+ *
+ * br = fHFPERCLK/(oversample * (1 + (CLKDIV / 256)))
+ *
+ * which can be rewritten to
+ *
+ * br = (256 * fHFPERCLK)/(oversample * (256 + CLKDIV))
+ *
+ * First of all we can reduce the 256 factor of the dividend with
+ * (part of) oversample part of the divisor.
+ */
+
+ switch (ovs) {
+ case usartOVS16:
+ oversample = 1;
+ factor = 256 / 16;
+ break;
+
+ case usartOVS8:
+ oversample = 1;
+ factor = 256 / 8;
+ break;
+
+ case usartOVS6:
+ oversample = 3;
+ factor = 256 / 2;
+ break;
+
+ default:
+ oversample = 1;
+ factor = 256 / 4;
+ break;
+ }
+ }
+
+ /*
+ * The basic problem with integer division in the above formula is that
+ * the dividend (factor * fHFPERCLK) may become larger than a 32 bit
+ * integer. Yet we want to evaluate dividend first before dividing in
+ * order to get as small rounding effects as possible. We do not want
+ * to make too harsh restrictions on max fHFPERCLK value either.
+ *
+ * For division a/b, we can write
+ *
+ * a = qb + r
+ *
+ * where q is the quotient and r is the remainder, both integers.
+ *
+ * The orignal baudrate formula can be rewritten as
+ *
+ * br = xa / b = x(qb + r)/b = xq + xr/b
+ *
+ * where x is 'factor', a is 'refFreq' and b is 'divisor', referring to
+ * variable names.
+ */
+
+ /*
+ * Divisor will never exceed max 32 bit value since
+ * clkdiv <= _USART_CLKDIV_DIV_MASK (currently 0x1FFFC0 or 0x7FFFF8)
+ * and 'oversample' has been reduced to <= 3.
+ */
+ divisor = oversample * (256 + clkdiv);
+
+ quotient = refFreq / divisor;
+ remainder = refFreq % divisor;
+
+ /* factor <= 128 and since divisor >= 256, the below cannot exceed max */
+ /* 32 bit value. However, factor * remainder can become larger than 32-bit */
+ /* because of the size of _USART_CLKDIV_DIV_MASK on some families. */
+ br = (uint32_t)(factor * quotient);
+
+ /*
+ * factor <= 128 and remainder < (oversample*(256 + clkdiv)), which
+ * means dividend (factor * remainder) worst case is
+ * 128 * (3 * (256 + _USART_CLKDIV_DIV_MASK)) = 0x1_8001_7400.
+ */
+ br += (uint32_t)((factor * remainder) / divisor);
+
+ return br;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Get current baudrate for USART/UART.
+ *
+ * @details
+ * This function returns the actual baudrate (not considering oscillator
+ * inaccuracies) used by a USART/UART peripheral.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @return
+ * Current baudrate.
+ ******************************************************************************/
+uint32_t USART_BaudrateGet(USART_TypeDef *usart)
+{
+ uint32_t freq;
+ USART_OVS_TypeDef ovs;
+ bool syncmode;
+
+ if (usart->CTRL & USART_CTRL_SYNC) {
+ syncmode = true;
+ } else {
+ syncmode = false;
+ }
+
+ /* HFPERCLK used to clock all USART/UART peripheral modules */
+ freq = CMU_ClockFreqGet(cmuClock_HFPER);
+ ovs = (USART_OVS_TypeDef)(usart->CTRL & _USART_CTRL_OVS_MASK);
+ return USART_BaudrateCalc(freq, usart->CLKDIV, syncmode, ovs);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Configure USART operating in synchronous mode to use a given baudrate
+ * (or as close as possible to specified baudrate).
+ *
+ * @details
+ * The configuration will be set to use a baudrate <= the specified baudrate
+ * in order to ensure that the baudrate does not exceed the specified value.
+ *
+ * Fractional clock division is suppressed, although the HW design allows it.
+ * It could cause half clock cycles to exceed specified limit, and thus
+ * potentially violate specifications for the slave device. In some special
+ * situations fractional clock division may be useful even in synchronous
+ * mode, but in those cases it must be directly adjusted, possibly assisted
+ * by USART_BaudrateCalc():
+ *
+ * @param[in] usart
+ * Pointer to USART peripheral register block. (Cannot be used on UART
+ * modules.)
+ *
+ * @param[in] refFreq
+ * USART reference clock frequency in Hz that will be used. If set to 0,
+ * the currently configured reference clock is assumed.
+ *
+ * @param[in] baudrate
+ * Baudrate to try to achieve for USART.
+ ******************************************************************************/
+void USART_BaudrateSyncSet(USART_TypeDef *usart, uint32_t refFreq, uint32_t baudrate)
+{
+ uint32_t clkdiv;
+
+ /* Inhibit divide by 0 */
+ EFM_ASSERT(baudrate);
+
+ /*
+ * CLKDIV in synchronous mode is given by:
+ *
+ * CLKDIV = 256 * (fHFPERCLK/(2 * br) - 1)
+ */
+
+ /* HFPERCLK used to clock all USART/UART peripheral modules */
+ if (!refFreq) {
+ refFreq = CMU_ClockFreqGet(cmuClock_HFPER);
+ }
+
+ clkdiv = (refFreq - 1) / (2 * baudrate);
+ clkdiv = clkdiv << 8;
+
+ /* Verify that resulting clock divider is within limits */
+ EFM_ASSERT(!(clkdiv & ~CLKDIV_MASK));
+
+ usart->CLKDIV = clkdiv;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Enable/disable USART/UART receiver and/or transmitter.
+ *
+ * @details
+ * Notice that this function does not do any configuration. Enabling should
+ * normally be done after initialization is done (if not enabled as part
+ * of init).
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] enable
+ * Select status for receiver/transmitter.
+ ******************************************************************************/
+void USART_Enable(USART_TypeDef *usart, USART_Enable_TypeDef enable)
+{
+ uint32_t tmp;
+
+ /* Make sure the module exists on the selected chip */
+ EFM_ASSERT(USART_REF_VALID(usart)
+ || USARTRF_REF_VALID(usart)
+ || UART_REF_VALID(usart) );
+
+ /* Disable as specified */
+ tmp = ~((uint32_t) (enable));
+ tmp &= _USART_CMD_RXEN_MASK | _USART_CMD_TXEN_MASK;
+ usart->CMD = tmp << 1;
+
+ /* Enable as specified */
+ usart->CMD = (uint32_t) (enable);
+}
+
+/***************************************************************************//**
+ * @brief
+ * Init USART/UART for normal asynchronous mode.
+ *
+ * @details
+ * This function will configure basic settings in order to operate in normal
+ * asynchronous mode.
+ *
+ * Special control setup not covered by this function must be done after
+ * using this function by direct modification of the CTRL register.
+ *
+ * Notice that pins used by the USART/UART module must be properly configured
+ * by the user explicitly, in order for the USART/UART to work as intended.
+ * (When configuring pins, one should remember to consider the sequence of
+ * configuration, in order to avoid unintended pulses/glitches on output
+ * pins.)
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] init
+ * Pointer to initialization structure used to configure basic async setup.
+ ******************************************************************************/
+void USART_InitAsync(USART_TypeDef *usart, const USART_InitAsync_TypeDef *init)
+{
+ /* Make sure the module exists on the selected chip */
+ EFM_ASSERT(USART_REF_VALID(usart)
+ || USARTRF_REF_VALID(usart)
+ || UART_REF_VALID(usart) );
+
+ /* Init USART registers to HW reset state. */
+ USART_Reset(usart);
+
+#if defined(USART_INPUT_RXPRS) && defined(USART_CTRL_MVDIS)
+ /* Disable majority vote if specified. */
+ if (init->mvdis) {
+ usart->CTRL |= USART_CTRL_MVDIS;
+ }
+
+ /* Configure PRS input mode. */
+ if (init->prsRxEnable) {
+ usart->INPUT = (uint32_t) init->prsRxCh | USART_INPUT_RXPRS;
+ }
+#endif
+
+ /* Configure databits, stopbits and parity */
+ usart->FRAME = (uint32_t)init->databits
+ | (uint32_t)init->stopbits
+ | (uint32_t)init->parity;
+
+ /* Configure baudrate */
+ USART_BaudrateAsyncSet(usart, init->refFreq, init->baudrate, init->oversampling);
+
+#if defined(_USART_TIMING_CSHOLD_MASK)
+ usart->TIMING = ((init->autoCsHold << _USART_TIMING_CSHOLD_SHIFT)
+ & _USART_TIMING_CSHOLD_MASK)
+ | ((init->autoCsSetup << _USART_TIMING_CSSETUP_SHIFT)
+ & _USART_TIMING_CSSETUP_MASK);
+ if (init->autoCsEnable) {
+ usart->CTRL |= USART_CTRL_AUTOCS;
+ }
+#endif
+ /* Finally enable (as specified) */
+ usart->CMD = (uint32_t)init->enable;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Init USART for synchronous mode.
+ *
+ * @details
+ * This function will configure basic settings in order to operate in
+ * synchronous mode.
+ *
+ * Special control setup not covered by this function must be done after
+ * using this function by direct modification of the CTRL register.
+ *
+ * Notice that pins used by the USART module must be properly configured
+ * by the user explicitly, in order for the USART to work as intended.
+ * (When configuring pins, one should remember to consider the sequence of
+ * configuration, in order to avoid unintended pulses/glitches on output
+ * pins.)
+ *
+ * @param[in] usart
+ * Pointer to USART peripheral register block. (UART does not support this
+ * mode.)
+ *
+ * @param[in] init
+ * Pointer to initialization structure used to configure basic async setup.
+ ******************************************************************************/
+void USART_InitSync(USART_TypeDef *usart, const USART_InitSync_TypeDef *init)
+{
+ /* Make sure the module exists on the selected chip */
+ EFM_ASSERT(USART_REF_VALID(usart) || USARTRF_REF_VALID(usart) );
+
+ /* Init USART registers to HW reset state. */
+ USART_Reset(usart);
+
+ /* Set bits for synchronous mode */
+ usart->CTRL |= (USART_CTRL_SYNC)
+ | (uint32_t)init->clockMode
+ | (init->msbf ? USART_CTRL_MSBF : 0);
+
+#if defined(_USART_CTRL_AUTOTX_MASK)
+ usart->CTRL |= init->autoTx ? USART_CTRL_AUTOTX : 0;
+#endif
+
+#if defined(_USART_INPUT_RXPRS_MASK)
+ /* Configure PRS input mode. */
+ if (init->prsRxEnable) {
+ usart->INPUT = (uint32_t)init->prsRxCh | USART_INPUT_RXPRS;
+ }
+#endif
+
+ /* Configure databits, leave stopbits and parity at reset default (not used) */
+ usart->FRAME = (uint32_t)init->databits
+ | USART_FRAME_STOPBITS_DEFAULT
+ | USART_FRAME_PARITY_DEFAULT;
+
+ /* Configure baudrate */
+ USART_BaudrateSyncSet(usart, init->refFreq, init->baudrate);
+
+ /* Finally enable (as specified) */
+ if (init->master) {
+ usart->CMD = USART_CMD_MASTEREN;
+ }
+
+#if defined(_USART_TIMING_CSHOLD_MASK)
+ usart->TIMING = ((init->autoCsHold << _USART_TIMING_CSHOLD_SHIFT)
+ & _USART_TIMING_CSHOLD_MASK)
+ | ((init->autoCsSetup << _USART_TIMING_CSSETUP_SHIFT)
+ & _USART_TIMING_CSSETUP_MASK);
+ if (init->autoCsEnable) {
+ usart->CTRL |= USART_CTRL_AUTOCS;
+ }
+#endif
+
+ usart->CMD = (uint32_t)init->enable;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Init USART for asynchronous IrDA mode.
+ *
+ * @details
+ * This function will configure basic settings in order to operate in
+ * asynchronous IrDA mode.
+ *
+ * Special control setup not covered by this function must be done after
+ * using this function by direct modification of the CTRL and IRCTRL
+ * registers.
+ *
+ * Notice that pins used by the USART/UART module must be properly configured
+ * by the user explicitly, in order for the USART/UART to work as intended.
+ * (When configuring pins, one should remember to consider the sequence of
+ * configuration, in order to avoid unintended pulses/glitches on output
+ * pins.)
+ *
+ * @param[in] usart
+ * Pointer to USART peripheral register block.
+ *
+ * @param[in] init
+ * Pointer to initialization structure used to configure async IrDA setup.
+ *
+ * @note
+ * Not all USART instances support IrDA. See the datasheet for your device.
+ *
+ ******************************************************************************/
+void USARTn_InitIrDA(USART_TypeDef *usart, const USART_InitIrDA_TypeDef *init)
+{
+ EFM_ASSERT(USART_IRDA_VALID(usart));
+
+ /* Init USART as async device */
+ USART_InitAsync(usart, &(init->async));
+
+ /* Set IrDA modulation to RZI (return-to-zero-inverted) */
+ usart->CTRL |= USART_CTRL_TXINV;
+
+ /* Invert Rx signal before demodulator if enabled */
+ if (init->irRxInv) {
+ usart->CTRL |= USART_CTRL_RXINV;
+ }
+
+ /* Configure IrDA */
+ usart->IRCTRL |= (uint32_t)init->irPw
+ | (uint32_t)init->irPrsSel
+ | ((uint32_t)init->irFilt << _USART_IRCTRL_IRFILT_SHIFT)
+ | ((uint32_t)init->irPrsEn << _USART_IRCTRL_IRPRSEN_SHIFT);
+
+ /* Enable IrDA */
+ usart->IRCTRL |= USART_IRCTRL_IREN;
+}
+
+#if defined(_USART_I2SCTRL_MASK)
+/***************************************************************************//**
+ * @brief
+ * Init USART for I2S mode.
+ *
+ * @details
+ * This function will configure basic settings in order to operate in I2S
+ * mode.
+ *
+ * Special control setup not covered by this function must be done after
+ * using this function by direct modification of the CTRL and I2SCTRL
+ * registers.
+ *
+ * Notice that pins used by the USART module must be properly configured
+ * by the user explicitly, in order for the USART to work as intended.
+ * (When configuring pins, one should remember to consider the sequence of
+ * configuration, in order to avoid unintended pulses/glitches on output
+ * pins.)
+ *
+ * @param[in] usart
+ * Pointer to USART peripheral register block. (UART does not support this
+ * mode.)
+ *
+ * @param[in] init
+ * Pointer to initialization structure used to configure basic I2S setup.
+ *
+ * @note
+ * This function does not apply to all USART's. Refer to chip manuals.
+ *
+ ******************************************************************************/
+void USART_InitI2s(USART_TypeDef *usart, USART_InitI2s_TypeDef *init)
+{
+ USART_Enable_TypeDef enable;
+
+ /* Make sure the module exists on the selected chip */
+ EFM_ASSERT(USART_I2S_VALID(usart));
+
+ /* Override the enable setting. */
+ enable = init->sync.enable;
+ init->sync.enable = usartDisable;
+
+ /* Init USART as a sync device. */
+ USART_InitSync(usart, &init->sync);
+
+ /* Configure and enable I2CCTRL register acording to selected mode. */
+ usart->I2SCTRL = (uint32_t)init->format
+ | (uint32_t)init->justify
+ | (init->delay ? USART_I2SCTRL_DELAY : 0)
+ | (init->dmaSplit ? USART_I2SCTRL_DMASPLIT : 0)
+ | (init->mono ? USART_I2SCTRL_MONO : 0)
+ | USART_I2SCTRL_EN;
+
+ if (enable != usartDisable) {
+ USART_Enable(usart, enable);
+ }
+}
+#endif
+
+/***************************************************************************//**
+ * @brief
+ * Initialize automatic transmissions using PRS channel as trigger
+ * @note
+ * Initialize USART with USART_Init() before setting up PRS configuration
+ *
+ * @param[in] usart Pointer to USART to configure
+ * @param[in] init Pointer to initialization structure
+ ******************************************************************************/
+void USART_InitPrsTrigger(USART_TypeDef *usart, const USART_PrsTriggerInit_TypeDef *init)
+{
+ uint32_t trigctrl;
+
+ /* Clear values that will be reconfigured */
+ trigctrl = usart->TRIGCTRL & ~(_USART_TRIGCTRL_RXTEN_MASK
+ | _USART_TRIGCTRL_TXTEN_MASK
+#if defined(USART_TRIGCTRL_AUTOTXTEN)
+ | _USART_TRIGCTRL_AUTOTXTEN_MASK
+#endif
+ | _USART_TRIGCTRL_TSEL_MASK);
+
+#if defined(USART_TRIGCTRL_AUTOTXTEN)
+ if (init->autoTxTriggerEnable) {
+ trigctrl |= USART_TRIGCTRL_AUTOTXTEN;
+ }
+#endif
+ if (init->txTriggerEnable) {
+ trigctrl |= USART_TRIGCTRL_TXTEN;
+ }
+ if (init->rxTriggerEnable) {
+ trigctrl |= USART_TRIGCTRL_RXTEN;
+ }
+ trigctrl |= init->prsTriggerChannel;
+
+ /* Enable new configuration */
+ usart->TRIGCTRL = trigctrl;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Reset USART/UART to same state as after a HW reset.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ ******************************************************************************/
+void USART_Reset(USART_TypeDef *usart)
+{
+ /* Make sure the module exists on the selected chip */
+ EFM_ASSERT(USART_REF_VALID(usart)
+ || USARTRF_REF_VALID(usart)
+ || UART_REF_VALID(usart) );
+
+ /* Make sure disabled first, before resetting other registers */
+ usart->CMD = USART_CMD_RXDIS | USART_CMD_TXDIS | USART_CMD_MASTERDIS
+ | USART_CMD_RXBLOCKDIS | USART_CMD_TXTRIDIS | USART_CMD_CLEARTX
+ | USART_CMD_CLEARRX;
+ usart->CTRL = _USART_CTRL_RESETVALUE;
+ usart->FRAME = _USART_FRAME_RESETVALUE;
+ usart->TRIGCTRL = _USART_TRIGCTRL_RESETVALUE;
+ usart->CLKDIV = _USART_CLKDIV_RESETVALUE;
+ usart->IEN = _USART_IEN_RESETVALUE;
+ usart->IFC = _USART_IFC_MASK;
+#if defined(_USART_ROUTEPEN_MASK) || defined(_UART_ROUTEPEN_MASK)
+ usart->ROUTEPEN = _USART_ROUTEPEN_RESETVALUE;
+ usart->ROUTELOC0 = _USART_ROUTELOC0_RESETVALUE;
+ usart->ROUTELOC1 = _USART_ROUTELOC1_RESETVALUE;
+#else
+ usart->ROUTE = _USART_ROUTE_RESETVALUE;
+#endif
+
+ if (USART_IRDA_VALID(usart)) {
+ usart->IRCTRL = _USART_IRCTRL_RESETVALUE;
+ }
+
+#if defined(_USART_INPUT_RESETVALUE)
+ usart->INPUT = _USART_INPUT_RESETVALUE;
+#endif
+
+#if defined(_USART_I2SCTRL_RESETVALUE)
+ if (USART_I2S_VALID(usart)) {
+ usart->I2SCTRL = _USART_I2SCTRL_RESETVALUE;
+ }
+#endif
+}
+
+/***************************************************************************//**
+ * @brief
+ * Receive one 4-8 bit frame, (or part of 10-16 bit frame).
+ *
+ * @details
+ * This function is normally used to receive one frame when operating with
+ * frame length 4-8 bits. Please refer to @ref USART_RxExt() for reception of
+ * 9 bit frames.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if the buffer is empty, until data is received.
+ * Alternatively the user can explicitly check whether data is available, and
+ * if data is avaliable, call @ref USART_RxDataGet() to read the RXDATA
+ * register directly.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @return
+ * Data received.
+ ******************************************************************************/
+uint8_t USART_Rx(USART_TypeDef *usart)
+{
+ while (!(usart->STATUS & USART_STATUS_RXDATAV))
+ ;
+
+ return (uint8_t)usart->RXDATA;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Receive two 4-8 bit frames, or one 10-16 bit frame.
+ *
+ * @details
+ * This function is normally used to receive one frame when operating with
+ * frame length 10-16 bits. Please refer to @ref USART_RxDoubleExt() for
+ * reception of two 9 bit frames.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is empty, until data is received.
+ * Alternatively the user can explicitly check whether data is available, and
+ * if data is avaliable, call @ref USART_RxDoubleGet() to read the RXDOUBLE
+ * register directly.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @return
+ * Data received.
+ ******************************************************************************/
+uint16_t USART_RxDouble(USART_TypeDef *usart)
+{
+ while (!(usart->STATUS & USART_STATUS_RXFULL))
+ ;
+
+ return (uint16_t)usart->RXDOUBLE;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Receive two 4-9 bit frames, or one 10-16 bit frame with extended
+ * information.
+ *
+ * @details
+ * This function is normally used to receive one frame when operating with
+ * frame length 10-16 bits and additional RX status information is required.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is empty, until data is received.
+ * Alternatively the user can explicitly check whether data is available, and
+ * if data is avaliable, call @ref USART_RxDoubleXGet() to read the RXDOUBLEX
+ * register directly.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @return
+ * Data received.
+ ******************************************************************************/
+uint32_t USART_RxDoubleExt(USART_TypeDef *usart)
+{
+ while (!(usart->STATUS & USART_STATUS_RXFULL))
+ ;
+
+ return usart->RXDOUBLEX;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Receive one 4-9 bit frame, (or part of 10-16 bit frame) with extended
+ * information.
+ *
+ * @details
+ * This function is normally used to receive one frame when operating with
+ * frame length 4-9 bits and additional RX status information is required.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is empty, until data is received.
+ * Alternatively the user can explicitly check whether data is available, and
+ * if data is avaliable, call @ref USART_RxDataXGet() to read the RXDATAX
+ * register directly.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @return
+ * Data received.
+ ******************************************************************************/
+uint16_t USART_RxExt(USART_TypeDef *usart)
+{
+ while (!(usart->STATUS & USART_STATUS_RXDATAV))
+ ;
+
+ return (uint16_t)usart->RXDATAX;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Perform one 8 bit frame SPI transfer.
+ *
+ * @note
+ * This function will stall if the transmit buffer is full. When a transmit
+ * buffer becomes available, data is written and the function will wait until
+ * the data is fully transmitted. The SPI return value is then read out and
+ * returned.
+ *
+ * @param[in] usart
+ * Pointer to USART peripheral register block.
+ *
+ * @param[in] data
+ * Data to transmit.
+ *
+ * @return
+ * Data received.
+ ******************************************************************************/
+uint8_t USART_SpiTransfer(USART_TypeDef *usart, uint8_t data)
+{
+ while (!(usart->STATUS & USART_STATUS_TXBL))
+ ;
+ usart->TXDATA = (uint32_t)data;
+ while (!(usart->STATUS & USART_STATUS_TXC))
+ ;
+ return (uint8_t)usart->RXDATA;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Transmit one 4-9 bit frame.
+ *
+ * @details
+ * Depending on frame length configuration, 4-8 (least significant) bits from
+ * @p data are transmitted. If frame length is 9, 8 bits are transmitted from
+ * @p data and one bit as specified by CTRL register, BIT8DV field. Please
+ * refer to USART_TxExt() for transmitting 9 bit frame with full control of
+ * all 9 bits.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is full, until buffer becomes available.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] data
+ * Data to transmit. See details above for further info.
+ ******************************************************************************/
+void USART_Tx(USART_TypeDef *usart, uint8_t data)
+{
+ /* Check that transmit buffer is empty */
+ while (!(usart->STATUS & USART_STATUS_TXBL))
+ ;
+ usart->TXDATA = (uint32_t)data;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Transmit two 4-9 bit frames, or one 10-16 bit frame.
+ *
+ * @details
+ * Depending on frame length configuration, 4-8 (least significant) bits from
+ * each byte in @p data are transmitted. If frame length is 9, 8 bits are
+ * transmitted from each byte in @p data adding one bit as specified by CTRL
+ * register, BIT8DV field, to each byte. Please refer to USART_TxDoubleExt()
+ * for transmitting two 9 bit frames with full control of all 9 bits.
+ *
+ * If frame length is 10-16, 10-16 (least significant) bits from @p data
+ * are transmitted.
+ *
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is full, until buffer becomes available.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] data
+ * Data to transmit, the least significant byte holds the frame transmitted
+ * first. See details above for further info.
+ ******************************************************************************/
+void USART_TxDouble(USART_TypeDef *usart, uint16_t data)
+{
+ /* Check that transmit buffer is empty */
+ while (!(usart->STATUS & USART_STATUS_TXBL))
+ ;
+ usart->TXDOUBLE = (uint32_t)data;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Transmit two 4-9 bit frames, or one 10-16 bit frame with extended control.
+ *
+ * @details
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is full, until buffer becomes available.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] data
+ * Data to transmit with extended control. Contains two 16 bit words
+ * concatenated. Least significant word holds frame transitted first. If frame
+ * length is 4-9, two frames with 4-9 least significant bits from each 16 bit
+ * word are transmitted.
+ * @par
+ * If frame length is 10-16 bits, 8 data bits are taken from the least
+ * significant 16 bit word, and the remaining bits from the other 16 bit word.
+ * @par
+ * Additional control bits are available as documented in the reference
+ * manual (set to 0 if not used). For 10-16 bit frame length, these control
+ * bits are taken from the most significant 16 bit word.
+ ******************************************************************************/
+void USART_TxDoubleExt(USART_TypeDef *usart, uint32_t data)
+{
+ /* Check that transmit buffer is empty */
+ while (!(usart->STATUS & USART_STATUS_TXBL))
+ ;
+ usart->TXDOUBLEX = data;
+}
+
+/***************************************************************************//**
+ * @brief
+ * Transmit one 4-9 bit frame with extended control.
+ *
+ * @details
+ * Notice that possible parity/stop bits in asynchronous mode are not
+ * considered part of specified frame bit length.
+ *
+ * @note
+ * This function will stall if buffer is full, until buffer becomes available.
+ *
+ * @param[in] usart
+ * Pointer to USART/UART peripheral register block.
+ *
+ * @param[in] data
+ * Data to transmit with extended control. Least significant bits contains
+ * frame bits, and additional control bits are available as documented in
+ * the reference manual (set to 0 if not used).
+ ******************************************************************************/
+void USART_TxExt(USART_TypeDef *usart, uint16_t data)
+{
+ /* Check that transmit buffer is empty */
+ while (!(usart->STATUS & USART_STATUS_TXBL))
+ ;
+ usart->TXDATAX = (uint32_t)data;
+}
+
+/** @} (end addtogroup USART) */
+/** @} (end addtogroup emlib) */
+#endif /* defined(USART_COUNT) && (USART_COUNT > 0) */
diff --git a/efm32boot/inc/app.h b/efm32boot/inc/app.h
new file mode 100644
index 0000000..a01d6f5
--- /dev/null
+++ b/efm32boot/inc/app.h
@@ -0,0 +1,22 @@
+/*
+ * app.h
+ *
+ * Created on: Jun 26, 2018
+ * Author: conor
+ */
+
+#ifndef SRC_APP_H_
+#define SRC_APP_H_
+
+#define IS_BOOTLOADER
+
+#define PRINTING_USE_VCOM
+
+#define USING_DEV_BOARD
+
+#define BRIDGE_TO_WALLET
+
+void printing_init();
+
+
+#endif /* SRC_APP_H_ */
diff --git a/efm32boot/src/crypto.c b/efm32boot/src/crypto.c
new file mode 100644
index 0000000..b1efb8e
--- /dev/null
+++ b/efm32boot/src/crypto.c
@@ -0,0 +1,345 @@
+/*
+ * Wrapper for crypto implementation on device
+ *
+ * */
+#include
+#include
+#include
+
+
+
+#include "util.h"
+#include "crypto.h"
+
+#ifdef USE_SOFTWARE_IMPLEMENTATION
+
+#include "sha256.h"
+#include "uECC.h"
+#include "aes.h"
+#include "ctap.h"
+#include "device.h"
+#include "app.h"
+
+#if defined(USING_PC) || defined(IS_BOOTLOADER)
+typedef enum
+{
+ MBEDTLS_ECP_DP_NONE = 0,
+ MBEDTLS_ECP_DP_SECP192R1, /*!< 192-bits NIST curve */
+ MBEDTLS_ECP_DP_SECP224R1, /*!< 224-bits NIST curve */
+ MBEDTLS_ECP_DP_SECP256R1, /*!< 256-bits NIST curve */
+ MBEDTLS_ECP_DP_SECP384R1, /*!< 384-bits NIST curve */
+ MBEDTLS_ECP_DP_SECP521R1, /*!< 521-bits NIST curve */
+ MBEDTLS_ECP_DP_BP256R1, /*!< 256-bits Brainpool curve */
+ MBEDTLS_ECP_DP_BP384R1, /*!< 384-bits Brainpool curve */
+ MBEDTLS_ECP_DP_BP512R1, /*!< 512-bits Brainpool curve */
+ MBEDTLS_ECP_DP_CURVE25519, /*!< Curve25519 */
+ MBEDTLS_ECP_DP_SECP192K1, /*!< 192-bits "Koblitz" curve */
+ MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
+ MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
+} mbedtls_ecp_group_id;
+#endif
+
+
+const uint8_t attestation_cert_der[];
+const uint16_t attestation_cert_der_size;
+const uint8_t attestation_key[];
+const uint16_t attestation_key_size;
+
+
+
+static SHA256_CTX sha256_ctx;
+static const struct uECC_Curve_t * _es256_curve = NULL;
+static const uint8_t * _signing_key = NULL;
+static int _key_len = 0;
+
+// Secrets for testing only
+static uint8_t master_secret[32] = "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff"
+ "\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00";
+
+static uint8_t transport_secret[32] = "\x10\x01\x22\x33\x44\x55\x66\x77\x87\x90\x0a\xbb\x3c\xd8\xee\xff"
+ "\xff\xee\x8d\x1c\x3b\xfa\x99\x88\x77\x86\x55\x44\xd3\xff\x33\x00";
+
+
+
+void crypto_sha256_init()
+{
+ sha256_init(&sha256_ctx);
+}
+
+void crypto_reset_master_secret()
+{
+ ctap_generate_rng(master_secret, 32);
+}
+
+
+void crypto_sha256_update(uint8_t * data, size_t len)
+{
+ sha256_update(&sha256_ctx, data, len);
+}
+
+void crypto_sha256_update_secret()
+{
+ sha256_update(&sha256_ctx, master_secret, 32);
+}
+
+void crypto_sha256_final(uint8_t * hash)
+{
+ sha256_final(&sha256_ctx, hash);
+}
+
+void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
+{
+ uint8_t buf[64];
+ int i;
+ memset(buf, 0, sizeof(buf));
+
+ if (key == CRYPTO_MASTER_KEY)
+ {
+ key = master_secret;
+ klen = sizeof(master_secret);
+ }
+
+ if(klen > 64)
+ {
+ printf("Error, key size must be <= 64\n");
+ exit(1);
+ }
+
+ memmove(buf, key, klen);
+
+ for (i = 0; i < sizeof(buf); i++)
+ {
+ buf[i] = buf[i] ^ 0x36;
+ }
+
+ crypto_sha256_init();
+ crypto_sha256_update(buf, 64);
+}
+
+void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
+{
+ uint8_t buf[64];
+ int i;
+ crypto_sha256_final(hmac);
+ memset(buf, 0, sizeof(buf));
+ if (key == CRYPTO_MASTER_KEY)
+ {
+ key = master_secret;
+ klen = sizeof(master_secret);
+ }
+
+
+ if(klen > 64)
+ {
+ printf("Error, key size must be <= 64\n");
+ exit(1);
+ }
+ memmove(buf, key, klen);
+
+ for (i = 0; i < sizeof(buf); i++)
+ {
+ buf[i] = buf[i] ^ 0x5c;
+ }
+
+ crypto_sha256_init();
+ crypto_sha256_update(buf, 64);
+ crypto_sha256_update(hmac, 32);
+ crypto_sha256_final(hmac);
+}
+
+
+void crypto_ecc256_init()
+{
+ uECC_set_rng((uECC_RNG_Function)ctap_generate_rng);
+ _es256_curve = uECC_secp256r1();
+}
+
+
+void crypto_ecc256_load_attestation_key()
+{
+ _signing_key = attestation_key;
+ _key_len = 32;
+}
+
+void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig)
+{
+ if ( uECC_sign(_signing_key, data, len, sig, _es256_curve) == 0)
+ {
+ printf("error, uECC failed\n");
+ exit(1);
+ }
+}
+
+void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2)
+{
+ static uint8_t privkey[32];
+ generate_private_key(data,len,data2,len2,privkey);
+ _signing_key = privkey;
+ _key_len = 32;
+}
+
+void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_ID)
+{
+
+ const struct uECC_Curve_t * curve = NULL;
+
+ switch(MBEDTLS_ECP_ID)
+ {
+ case MBEDTLS_ECP_DP_SECP256R1:
+ curve = uECC_secp256r1();
+ if (_key_len != 32) goto fail;
+ break;
+ default:
+ printf("error, invalid ECDSA alg specifier\n");
+ exit(1);
+ }
+
+ if ( uECC_sign(_signing_key, data, len, sig, curve) == 0)
+ {
+ printf("error, uECC failed\n");
+ exit(1);
+ }
+ return;
+
+fail:
+ printf("error, invalid key length\n");
+ exit(1);
+
+}
+
+void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, uint8_t * privkey)
+{
+ crypto_sha256_hmac_init(CRYPTO_MASTER_KEY, 0, privkey);
+ crypto_sha256_update(data, len);
+ crypto_sha256_update(data2, len2);
+ crypto_sha256_update(master_secret, 32);
+ crypto_sha256_hmac_final(CRYPTO_MASTER_KEY, 0, privkey);
+}
+
+
+/*int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve);*/
+void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y)
+{
+ uint8_t privkey[32];
+ uint8_t pubkey[64];
+
+ generate_private_key(data,len,NULL,0,privkey);
+
+ memset(pubkey,0,sizeof(pubkey));
+ uECC_compute_public_key(privkey, pubkey, _es256_curve);
+ memmove(x,pubkey,32);
+ memmove(y,pubkey+32,32);
+}
+
+void crypto_load_external_key(uint8_t * key, int len)
+{
+ _signing_key = key;
+ _key_len = len;
+}
+
+
+void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey)
+{
+ if (uECC_make_key(pubkey, privkey, _es256_curve) != 1)
+ {
+ printf("Error, uECC_make_key failed\n");
+ exit(1);
+ }
+}
+
+void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret)
+{
+ if (uECC_shared_secret(pubkey, privkey, shared_secret, _es256_curve) != 1)
+ {
+ printf("Error, uECC_shared_secret failed\n");
+ exit(1);
+ }
+
+}
+
+struct AES_ctx aes_ctx;
+void crypto_aes256_init(uint8_t * key, uint8_t * nonce)
+{
+ if (key == CRYPTO_TRANSPORT_KEY)
+ {
+ AES_init_ctx(&aes_ctx, transport_secret);
+ }
+ else
+ {
+ AES_init_ctx(&aes_ctx, key);
+ }
+ if (nonce == NULL)
+ {
+ memset(aes_ctx.Iv, 0, 16);
+ }
+ else
+ {
+ memmove(aes_ctx.Iv, nonce, 16);
+ }
+}
+
+// prevent round key recomputation
+void crypto_aes256_reset_iv(uint8_t * nonce)
+{
+ if (nonce == NULL)
+ {
+ memset(aes_ctx.Iv, 0, 16);
+ }
+ else
+ {
+ memmove(aes_ctx.Iv, nonce, 16);
+ }
+}
+
+void crypto_aes256_decrypt(uint8_t * buf, int length)
+{
+ AES_CBC_decrypt_buffer(&aes_ctx, buf, length);
+}
+
+void crypto_aes256_encrypt(uint8_t * buf, int length)
+{
+ AES_CBC_encrypt_buffer(&aes_ctx, buf, length);
+}
+
+
+const uint8_t attestation_cert_der[] =
+"\x30\x82\x01\xfb\x30\x82\x01\xa1\xa0\x03\x02\x01\x02\x02\x01\x00\x30\x0a\x06\x08"
+"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
+"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e"
+"\x06\x03\x55\x04\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x30\x20\x17\x0d\x31\x38"
+"\x30\x35\x31\x30\x30\x33\x30\x36\x32\x30\x5a\x18\x0f\x32\x30\x36\x38\x30\x34\x32"
+"\x37\x30\x33\x30\x36\x32\x30\x5a\x30\x7c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
+"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x0f\x30\x0d"
+"\x06\x03\x55\x04\x07\x0c\x06\x4c\x61\x75\x72\x65\x6c\x31\x15\x30\x13\x06\x03\x55"
+"\x04\x0a\x0c\x0c\x54\x45\x53\x54\x20\x43\x4f\x4d\x50\x41\x4e\x59\x31\x22\x30\x20"
+"\x06\x03\x55\x04\x0b\x0c\x19\x41\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72"
+"\x20\x41\x74\x74\x65\x73\x74\x61\x74\x69\x6f\x6e\x31\x14\x30\x12\x06\x03\x55\x04"
+"\x03\x0c\x0b\x63\x6f\x6e\x6f\x72\x70\x70\x2e\x63\x6f\x6d\x30\x59\x30\x13\x06\x07"
+"\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00"
+"\x04\x45\xa9\x02\xc1\x2e\x9c\x0a\x33\xfa\x3e\x84\x50\x4a\xb8\x02\xdc\x4d\xb9\xaf"
+"\x15\xb1\xb6\x3a\xea\x8d\x3f\x03\x03\x55\x65\x7d\x70\x3f\xb4\x02\xa4\x97\xf4\x83"
+"\xb8\xa6\xf9\x3c\xd0\x18\xad\x92\x0c\xb7\x8a\x5a\x3e\x14\x48\x92\xef\x08\xf8\xca"
+"\xea\xfb\x32\xab\x20\xa3\x62\x30\x60\x30\x46\x06\x03\x55\x1d\x23\x04\x3f\x30\x3d"
+"\xa1\x30\xa4\x2e\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31"
+"\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e\x06\x03\x55\x04"
+"\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x82\x09\x00\xf7\xc9\xec\x89\xf2\x63\x94"
+"\xd9\x30\x09\x06\x03\x55\x1d\x13\x04\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04"
+"\x04\x03\x02\x04\xf0\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x48\x00"
+"\x30\x45\x02\x20\x18\x38\xb0\x45\x03\x69\xaa\xa7\xb7\x38\x62\x01\xaf\x24\x97\x5e"
+"\x7e\x74\x64\x1b\xa3\x7b\xf7\xe6\xd3\xaf\x79\x28\xdb\xdc\xa5\x88\x02\x21\x00\xcd"
+"\x06\xf1\xe3\xab\x16\x21\x8e\xd8\xc0\x14\xaf\x09\x4f\x5b\x73\xef\x5e\x9e\x4b\xe7"
+"\x35\xeb\xdd\x9b\x6d\x8f\x7d\xf3\xc4\x3a\xd7";
+
+
+const uint16_t attestation_cert_der_size = sizeof(attestation_cert_der)-1;
+
+
+const uint8_t attestation_key[] = "\xcd\x67\xaa\x31\x0d\x09\x1e\xd1\x6e\x7e\x98\x92\xaa\x07\x0e\x19\x94\xfc\xd7\x14\xae\x7c\x40\x8f\xb9\x46\xb7\x2e\x5f\xe7\x5d\x30";
+const uint16_t attestation_key_size = sizeof(attestation_key)-1;
+
+
+#else
+#error "No crypto implementation defined"
+#endif
+
+
diff --git a/efm32boot/src/main.c b/efm32boot/src/main.c
new file mode 100644
index 0000000..d826c73
--- /dev/null
+++ b/efm32boot/src/main.c
@@ -0,0 +1,28 @@
+#include "em_device.h"
+#include "em_chip.h"
+#include "device.h"
+#include "app.h"
+#include "InitDevice.h"
+
+void bootloader_init(void);
+
+int main(void)
+{
+ /* Chip errata */
+ CHIP_Init();
+
+ EMU_enter_DefaultMode_from_RESET();
+ CMU_enter_DefaultMode_from_RESET();
+// ADC0_enter_DefaultMode_from_RESET();
+ USART0_enter_DefaultMode_from_RESET();
+ USART1_enter_DefaultMode_from_RESET();
+ LDMA_enter_DefaultMode_from_RESET();
+ CRYOTIMER_enter_DefaultMode_from_RESET();
+ PORTIO_enter_DefaultMode_from_RESET();
+
+ bootloader_init();
+
+ /* Infinite loop */
+ while (1) {
+ }
+}
diff --git a/fido2/ctap.c b/fido2/ctap.c
index e7b5b02..a3dc1b6 100644
--- a/fido2/ctap.c
+++ b/fido2/ctap.c
@@ -1404,7 +1404,7 @@ static uint16_t key_addr_offset(int index)
uint16_t ctap_key_len(uint8_t index)
{
int i = ctap_keys_stored();
- if (i >= MAX_KEYS || index >= MAX_KEYS)
+ if (index >= i || index >= MAX_KEYS)
{
return 0;
}
@@ -1449,7 +1449,7 @@ int8_t ctap_load_key(uint8_t index, uint8_t * key)
int i = ctap_keys_stored();
uint16_t offset;
uint16_t len;
- if (i >= MAX_KEYS || index >= MAX_KEYS)
+ if (index >= i || index >= MAX_KEYS)
{
return ERR_NO_KEY_SPACE;
}
diff --git a/fido2/main.c b/fido2/main.c
index f7d801c..b0fb0a3 100644
--- a/fido2/main.c
+++ b/fido2/main.c
@@ -24,10 +24,10 @@ int main(int argc, char * argv[])
set_logging_mask(
/*0*/
-// TAG_GEN|
+ TAG_GEN|
/*TAG_MC |*/
/*TAG_GA |*/
- /*TAG_WALLET |*/
+ TAG_WALLET |
TAG_STOR |
/*TAG_CP |*/
// TAG_CTAP|
diff --git a/fido2/wallet.c b/fido2/wallet.c
index d3084bf..2383aeb 100644
--- a/fido2/wallet.c
+++ b/fido2/wallet.c
@@ -15,7 +15,7 @@
#include "storage.h"
#include "device.h"
-#ifdef USING_PC
+#if defined(USING_PC) || defined(IS_BOOTLOADER)
typedef enum
{
MBEDTLS_ECP_DP_NONE = 0,