diff --git a/.gitignore b/.gitignore index 3be0b88..a059597 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,6 @@ tools/attest sdk_15.0.0/ _build/ + +GNU\ * +Keil\ * diff --git a/crypto/aes_gcm.c b/crypto/aes-gcm/aes_gcm.c similarity index 100% rename from crypto/aes_gcm.c rename to crypto/aes-gcm/aes_gcm.c diff --git a/crypto/sha256.c b/crypto/sha256/sha256.c similarity index 100% rename from crypto/sha256.c rename to crypto/sha256/sha256.c diff --git a/crypto/sha256.h b/crypto/sha256/sha256.h similarity index 100% rename from crypto/sha256.h rename to crypto/sha256/sha256.h diff --git a/device.h b/device.h deleted file mode 100644 index cb1e2d7..0000000 --- a/device.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _DEVICE_H -#define _DEVICE_H - -void device_init(); - -uint64_t millis(); - -// HID message size in bytes -#define HID_MESSAGE_SIZE 64 - -void usbhid_init(); - -int usbhid_recv(uint8_t * msg); - -void usbhid_send(uint8_t * msg); - -void usbhid_close(); - -void main_loop_delay(); - -void heartbeat(); - -#endif diff --git a/efm32/.cproject b/efm32/.cproject new file mode 100644 index 0000000..6cc2d33 --- /dev/null +++ b/efm32/.cproject @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/efm32/.project b/efm32/.project new file mode 100644 index 0000000..513394f --- /dev/null +++ b/efm32/.project @@ -0,0 +1,39 @@ + + + EFM32 + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + com.silabs.ss.framework.ide.project.sls.core.SLSProjectNature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + crypto + 2 + C:/Users/conor/Desktop/u2f-one/crypto/ + + + fido2 + 2 + C:/Users/conor/Desktop/u2f-one/fido2/ + + + diff --git a/efm32/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs b/efm32/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs new file mode 100644 index 0000000..b4554b4 --- /dev/null +++ b/efm32/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs @@ -0,0 +1,2 @@ +copiedFilesOriginState={} +eclipse.preferences.version=1 diff --git a/efm32/.settings/org.eclipse.cdt.codan.core.prefs b/efm32/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100644 index 0000000..75be390 --- /dev/null +++ b/efm32/.settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,70 @@ +eclipse.preferences.version=1 +org.eclipse.cdt.codan.checkers.errnoreturn=Warning +org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} +org.eclipse.cdt.codan.checkers.errreturnvalue=Error +org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.checkers.nocommentinside=-Error +org.eclipse.cdt.codan.checkers.nocommentinside.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.checkers.nolinecomment=-Error +org.eclipse.cdt.codan.checkers.nolinecomment.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.checkers.noreturn=Error +org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} +org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error +org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error +org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning +org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning +org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false} +org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning +org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error +org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning +org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true} +org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error +org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error +org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error +org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error +org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info +org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning +org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error +org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error +org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error +org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning +org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning +org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning +org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning +org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false} +org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning +org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false} +org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} +org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} +org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")} +org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} diff --git a/efm32/CMSIS/EFM32PG1B/startup_gcc_efm32pg1b.s b/efm32/CMSIS/EFM32PG1B/startup_gcc_efm32pg1b.s new file mode 100644 index 0000000..2a683c7 --- /dev/null +++ b/efm32/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/efm32/CMSIS/EFM32PG1B/system_efm32pg1b.c b/efm32/CMSIS/EFM32PG1B/system_efm32pg1b.c new file mode 100644 index 0000000..c52e3e1 --- /dev/null +++ b/efm32/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/efm32/EFM32.hwconf b/efm32/EFM32.hwconf new file mode 100644 index 0000000..15d21ae --- /dev/null +++ b/efm32/EFM32.hwconf @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/efm32/Makefile b/efm32/Makefile new file mode 100644 index 0000000..4b9558a --- /dev/null +++ b/efm32/Makefile @@ -0,0 +1,23 @@ + +CC=arm-none-eabi-gcc + +all: + cd 'GNU ARM v7.2.1 - Debug' && make all + + +#arm-none-eabi-gcc -g -gdwarf-2 -mcpu=cortex-m4 -mthumb -std=c99 '-DDEBUG=1' '-DEFM32PG1B200F256GM48=1' -IC:/Users/conor/Desktop/u2f-one/crypto/sha256 -IC:/Users/conor/Desktop/u2f-one/crypto/micro-ecc -IC:/Users/conor/Desktop/u2f-one/crypto/tiny-AES-c -I"C:\Users\conor\Desktop\u2f-one\efm32\inc" -IC:/Users/conor/Desktop/u2f-one/fido2 -IC:/Users/conor/Desktop/u2f-one/tinycbor/src -I"C:/SiliconLabs/SimplicityStudio/v4/developer/sdks/gecko_sdk_suite/v1.1//platform/CMSIS/Include" -I"C:/SiliconLabs/SimplicityStudio/v4/developer/sdks/gecko_sdk_suite/v1.1//hardware/kit/common/drivers" -I"C:/SiliconLabs/SimplicityStudio/v4/developer/sdks/gecko_sdk_suite/v1.1//hardware/kit/SLSTK3401A_EFM32PG/config" -I"C:/SiliconLabs/SimplicityStudio/v4/developer/sdks/gecko_sdk_suite/v1.1//platform/Device/SiliconLabs/EFM32PG1B/Include" -I"C:/SiliconLabs/SimplicityStudio/v4/developer/sdks/gecko_sdk_suite/v1.1//platform/emlib/inc" -I"C:/SiliconLabs/SimplicityStudio/v4/developer/sdks/gecko_sdk_suite/v1.1//hardware/kit/common/bsp" -O0 -Wall -c -fmessage-length=0 -mno-sched-prolog -fno-builtin -ffunction-sections -fdata-sections -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -MMD -MP -MF"src/device.d" -MT"src/device.o" -o "src/device.o" "../src/device.c" + + +#arm-none-eabi-gcc -g -gdwarf-2 -mcpu=cortex-m4 -mthumb -T "EFM32.ld" -Xlinker --gc-sections -Xlinker -Map="EFM32.map" -mfpu=fpv4-sp-d16 -mfloat-abi=softfp --specs=nano.specs -o EFM32.axf "./CMSIS/EFM32PG1B/startup_gcc_efm32pg1b.o" "./CMSIS/EFM32PG1B/system_efm32pg1b.o" "./crypto/micro-ecc/uECC.o" "./crypto/sha256/sha256.o" "./crypto/tiny-AES-c/aes.o" "./emlib/em_assert.o" "./emlib/em_cmu.o" "./emlib/em_emu.o" "./emlib/em_gpio.o" "./emlib/em_system.o" "./emlib/em_usart.o" "./fido2/crypto.o" "./fido2/ctap.o" "./fido2/ctap_parse.o" "./fido2/ctaphid.o" "./fido2/log.o" "./fido2/main.o" "./fido2/stubs.o" "./fido2/test_power.o" "./fido2/u2f.o" "./fido2/util.o" "./src/InitDevice.o" "./src/device.o" "./src/main.o" "./src/printing.o" "./src/retargetio.o" -Wl,--start-group -lgcc -lc -lnosys -Wl,--end-group + + +cbor: + cd ../tinycbor/ && make clean + cd ../tinycbor/ && make CC="$(CC)" \ +LDFLAGS="-lgcc -lc -lnosys --specs=nosys.specs -mcpu=cortex-m4 -mfloat-abi=softfp -mfpu=fpv4-sp-d16 -mthumb " \ +CFLAGS="-g -gdwarf-2 -mcpu=cortex-m4 -mthumb -std=c99 -DEFM32PG1B200F256GM48=1 -O3 -Wall -c -fmessage-length=0 -mno-sched-prolog -fno-builtin -ffunction-sections -fdata-sections -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -MMD -MP " + + + +clean: + cd 'GNU ARM v7.2.1 - Debug' && make clean diff --git a/efm32/emlib/em_assert.c b/efm32/emlib/em_assert.c new file mode 100644 index 0000000..71225e0 --- /dev/null +++ b/efm32/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/efm32/emlib/em_cmu.c b/efm32/emlib/em_cmu.c new file mode 100644 index 0000000..96316ff --- /dev/null +++ b/efm32/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/efm32/emlib/em_cryotimer.c b/efm32/emlib/em_cryotimer.c new file mode 100644 index 0000000..66f4ac5 --- /dev/null +++ b/efm32/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/efm32/emlib/em_emu.c b/efm32/emlib/em_emu.c new file mode 100644 index 0000000..1ba8a46 --- /dev/null +++ b/efm32/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/efm32/emlib/em_gpio.c b/efm32/emlib/em_gpio.c new file mode 100644 index 0000000..3b1d146 --- /dev/null +++ b/efm32/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/efm32/emlib/em_system.c b/efm32/emlib/em_system.c new file mode 100644 index 0000000..6877ac4 --- /dev/null +++ b/efm32/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/efm32/emlib/em_timer.c b/efm32/emlib/em_timer.c new file mode 100644 index 0000000..6999d6a --- /dev/null +++ b/efm32/emlib/em_timer.c @@ -0,0 +1,253 @@ +/***************************************************************************//** + * @file em_timer.c + * @brief Timer/counter (TIMER) 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_timer.h" +#if defined(TIMER_COUNT) && (TIMER_COUNT > 0) + +#include "em_assert.h" + +/***************************************************************************//** + * @addtogroup emlib + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup TIMER + * @brief Timer/Counter (TIMER) Peripheral API + * @details + * The timer module consists of three main parts: + * @li General timer config and enable control. + * @li Compare/capture control. + * @li Dead time insertion control (may not be available for all timers). + * @{ + ******************************************************************************/ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Initialize TIMER. + * + * @details + * Notice that counter top must be configured separately with for instance + * TIMER_TopSet(). In addition, compare/capture and dead-time insertion + * init must be initialized separately if used. That should probably + * be done prior to the use of this function if configuring the TIMER to + * start when initialization is completed. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] init + * Pointer to TIMER initialization structure. + ******************************************************************************/ +void TIMER_Init(TIMER_TypeDef *timer, const TIMER_Init_TypeDef *init) +{ + EFM_ASSERT(TIMER_REF_VALID(timer)); + + /* Stop timer if specified to be disabled (dosn't hurt if already stopped) */ + if (!(init->enable)) { + timer->CMD = TIMER_CMD_STOP; + } + + /* Reset counter */ + timer->CNT = _TIMER_CNT_RESETVALUE; + + timer->CTRL = ((uint32_t)(init->prescale) << _TIMER_CTRL_PRESC_SHIFT) + | ((uint32_t)(init->clkSel) << _TIMER_CTRL_CLKSEL_SHIFT) + | ((uint32_t)(init->fallAction) << _TIMER_CTRL_FALLA_SHIFT) + | ((uint32_t)(init->riseAction) << _TIMER_CTRL_RISEA_SHIFT) + | ((uint32_t)(init->mode) << _TIMER_CTRL_MODE_SHIFT) + | (init->debugRun ? TIMER_CTRL_DEBUGRUN : 0) + | (init->dmaClrAct ? TIMER_CTRL_DMACLRACT : 0) + | (init->quadModeX4 ? TIMER_CTRL_QDM_X4 : 0) + | (init->oneShot ? TIMER_CTRL_OSMEN : 0) + +#if defined(TIMER_CTRL_X2CNT) && defined(TIMER_CTRL_ATI) + | (init->count2x ? TIMER_CTRL_X2CNT : 0) + | (init->ati ? TIMER_CTRL_ATI : 0) +#endif + | (init->sync ? TIMER_CTRL_SYNC : 0); + + /* Start timer if specified to be enabled (dosn't hurt if already started) */ + if (init->enable) { + timer->CMD = TIMER_CMD_START; + } +} + +/***************************************************************************//** + * @brief + * Initialize TIMER compare/capture channel. + * + * @details + * Notice that if operating channel in compare mode, the CCV and CCVB register + * must be set separately as required. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] ch + * Compare/capture channel to init for. + * + * @param[in] init + * Pointer to TIMER initialization structure. + ******************************************************************************/ +void TIMER_InitCC(TIMER_TypeDef *timer, + unsigned int ch, + const TIMER_InitCC_TypeDef *init) +{ + EFM_ASSERT(TIMER_REF_VALID(timer)); + EFM_ASSERT(TIMER_CH_VALID(ch)); + + timer->CC[ch].CTRL = + ((uint32_t)(init->eventCtrl) << _TIMER_CC_CTRL_ICEVCTRL_SHIFT) + | ((uint32_t)(init->edge) << _TIMER_CC_CTRL_ICEDGE_SHIFT) + | ((uint32_t)(init->prsSel) << _TIMER_CC_CTRL_PRSSEL_SHIFT) + | ((uint32_t)(init->cufoa) << _TIMER_CC_CTRL_CUFOA_SHIFT) + | ((uint32_t)(init->cofoa) << _TIMER_CC_CTRL_COFOA_SHIFT) + | ((uint32_t)(init->cmoa) << _TIMER_CC_CTRL_CMOA_SHIFT) + | ((uint32_t)(init->mode) << _TIMER_CC_CTRL_MODE_SHIFT) + | (init->filter ? TIMER_CC_CTRL_FILT_ENABLE : 0) + | (init->prsInput ? TIMER_CC_CTRL_INSEL_PRS : 0) + | (init->coist ? TIMER_CC_CTRL_COIST : 0) + | (init->outInvert ? TIMER_CC_CTRL_OUTINV : 0); +} + +#if defined(_TIMER_DTCTRL_MASK) +/***************************************************************************//** + * @brief + * Initialize the TIMER DTI unit. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + * + * @param[in] init + * Pointer to TIMER DTI initialization structure. + ******************************************************************************/ +void TIMER_InitDTI(TIMER_TypeDef *timer, const TIMER_InitDTI_TypeDef *init) +{ + EFM_ASSERT(TIMER0 == timer); + + /* Make sure the DTI unit is disabled while initializing. */ + TIMER_EnableDTI(timer, false); + + /* Setup the DTCTRL register. + The enable bit will be set at the end of the function if specified. */ + timer->DTCTRL = + (init->autoRestart ? TIMER_DTCTRL_DTDAS : 0) + | (init->activeLowOut ? TIMER_DTCTRL_DTIPOL : 0) + | (init->invertComplementaryOut ? TIMER_DTCTRL_DTCINV : 0) + | (init->enablePrsSource ? TIMER_DTCTRL_DTPRSEN : 0) + | ((uint32_t)(init->prsSel) << _TIMER_DTCTRL_DTPRSSEL_SHIFT); + + /* Setup the DTTIME register. */ + timer->DTTIME = + ((uint32_t)(init->prescale) << _TIMER_DTTIME_DTPRESC_SHIFT) + | ((uint32_t)(init->riseTime) << _TIMER_DTTIME_DTRISET_SHIFT) + | ((uint32_t)(init->fallTime) << _TIMER_DTTIME_DTFALLT_SHIFT); + + /* Setup the DTFC register. */ + timer->DTFC = + (init->enableFaultSourceCoreLockup ? TIMER_DTFC_DTLOCKUPFEN : 0) + | (init->enableFaultSourceDebugger ? TIMER_DTFC_DTDBGFEN : 0) + | (init->enableFaultSourcePrsSel0 ? TIMER_DTFC_DTPRS0FEN : 0) + | (init->enableFaultSourcePrsSel1 ? TIMER_DTFC_DTPRS1FEN : 0) + | ((uint32_t)(init->faultAction) << _TIMER_DTFC_DTFA_SHIFT) + | ((uint32_t)(init->faultSourcePrsSel0) << _TIMER_DTFC_DTPRS0FSEL_SHIFT) + | ((uint32_t)(init->faultSourcePrsSel1) << _TIMER_DTFC_DTPRS1FSEL_SHIFT); + + /* Setup the DTOGEN register. */ + timer->DTOGEN = init->outputsEnableMask; + + /* Clear any previous DTI faults. */ + TIMER_ClearDTIFault(timer, TIMER_GetDTIFault(timer)); + + /* Enable/disable before returning. */ + TIMER_EnableDTI(timer, init->enable); +} +#endif + +/***************************************************************************//** + * @brief + * Reset TIMER to same state as after a HW reset. + * + * @note + * The ROUTE register is NOT reset by this function, in order to allow for + * centralized setup of this feature. + * + * @param[in] timer + * Pointer to TIMER peripheral register block. + ******************************************************************************/ +void TIMER_Reset(TIMER_TypeDef *timer) +{ + int i; + + EFM_ASSERT(TIMER_REF_VALID(timer)); + + /* Make sure disabled first, before resetting other registers */ + timer->CMD = TIMER_CMD_STOP; + + timer->CTRL = _TIMER_CTRL_RESETVALUE; + timer->IEN = _TIMER_IEN_RESETVALUE; + timer->IFC = _TIMER_IFC_MASK; + timer->TOPB = _TIMER_TOPB_RESETVALUE; + /* Write TOP after TOPB to invalidate TOPB (clear TIMER_STATUS_TOPBV) */ + timer->TOP = _TIMER_TOP_RESETVALUE; + timer->CNT = _TIMER_CNT_RESETVALUE; + /* Do not reset route register, setting should be done independently */ + /* (Note: ROUTE register may be locked by DTLOCK register.) */ + + for (i = 0; TIMER_CH_VALID(i); i++) { + timer->CC[i].CTRL = _TIMER_CC_CTRL_RESETVALUE; + timer->CC[i].CCV = _TIMER_CC_CCV_RESETVALUE; + timer->CC[i].CCVB = _TIMER_CC_CCVB_RESETVALUE; + } + + /* Reset dead time insertion module, no effect on timers without DTI */ + +#if defined(TIMER_DTLOCK_LOCKKEY_UNLOCK) + /* Unlock DTI registers first in case locked */ + timer->DTLOCK = TIMER_DTLOCK_LOCKKEY_UNLOCK; + + timer->DTCTRL = _TIMER_DTCTRL_RESETVALUE; + timer->DTTIME = _TIMER_DTTIME_RESETVALUE; + timer->DTFC = _TIMER_DTFC_RESETVALUE; + timer->DTOGEN = _TIMER_DTOGEN_RESETVALUE; + timer->DTFAULTC = _TIMER_DTFAULTC_MASK; +#endif +} + +/** @} (end addtogroup TIMER) */ +/** @} (end addtogroup emlib) */ +#endif /* defined(TIMER_COUNT) && (TIMER_COUNT > 0) */ diff --git a/efm32/emlib/em_usart.c b/efm32/emlib/em_usart.c new file mode 100644 index 0000000..3a5e65c --- /dev/null +++ b/efm32/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/efm32/inc/InitDevice.h b/efm32/inc/InitDevice.h new file mode 100644 index 0000000..31af980 --- /dev/null +++ b/efm32/inc/InitDevice.h @@ -0,0 +1,48 @@ +//========================================================= +// inc/InitDevice.h: generated by Hardware Configurator +// +// This file will be regenerated when saving a document. +// leave the sections inside the "$[...]" comment tags alone +// or they will be overwritten! +//========================================================= +#ifndef __INIT_DEVICE_H__ +#define __INIT_DEVICE_H__ + +// USER CONSTANTS +// USER PROTOTYPES + +// $[Mode Transition Prototypes] +extern void enter_DefaultMode_from_RESET(void); +// [Mode Transition Prototypes]$ + +// $[Config(Per-Module Mode)Transition Prototypes] +extern void EMU_enter_DefaultMode_from_RESET(void); +extern void LFXO_enter_DefaultMode_from_RESET(void); +extern void CMU_enter_DefaultMode_from_RESET(void); +extern void ADC0_enter_DefaultMode_from_RESET(void); +extern void ACMP0_enter_DefaultMode_from_RESET(void); +extern void ACMP1_enter_DefaultMode_from_RESET(void); +extern void IDAC0_enter_DefaultMode_from_RESET(void); +extern void RTCC_enter_DefaultMode_from_RESET(void); +extern void USART0_enter_DefaultMode_from_RESET(void); +extern void USART1_enter_DefaultMode_from_RESET(void); +extern void LEUART0_enter_DefaultMode_from_RESET(void); +extern void WDOG0_enter_DefaultMode_from_RESET(void); +extern void I2C0_enter_DefaultMode_from_RESET(void); +extern void GPCRC_enter_DefaultMode_from_RESET(void); +extern void LDMA_enter_DefaultMode_from_RESET(void); +extern void TIMER0_enter_DefaultMode_from_RESET(void); +extern void TIMER1_enter_DefaultMode_from_RESET(void); +extern void LETIMER0_enter_DefaultMode_from_RESET(void); +extern void CRYOTIMER_enter_DefaultMode_from_RESET(void); +extern void PCNT0_enter_DefaultMode_from_RESET(void); +extern void PRS_enter_DefaultMode_from_RESET(void); +extern void PORTIO_enter_DefaultMode_from_RESET(void); +// [Config(Per-Module Mode)Transition Prototypes]$ + +// $[User-defined pin name abstraction] + +// [User-defined pin name abstraction]$ + +#endif + diff --git a/efm32/src/InitDevice.c b/efm32/src/InitDevice.c new file mode 100644 index 0000000..2574816 --- /dev/null +++ b/efm32/src/InitDevice.c @@ -0,0 +1,547 @@ +//========================================================= +// src/InitDevice.c: generated by Hardware Configurator +// +// This file will be regenerated when saving a document. +// leave the sections inside the "$[...]" comment tags alone +// or they will be overwritten! +//========================================================= + +// USER INCLUDES +#include "InitDevice.h" + +// USER PROTOTYPES +// USER FUNCTIONS + +// $[Library includes] +#include "em_system.h" +#include "em_emu.h" +#include "em_cmu.h" +#include "em_device.h" +#include "em_chip.h" +#include "em_assert.h" +#include "em_cryotimer.h" +#include "em_gpio.h" +#include "em_usart.h" +// [Library includes]$ + +//============================================================================== +// enter_DefaultMode_from_RESET +//============================================================================== +extern void enter_DefaultMode_from_RESET(void) { + // $[Config Calls] + CHIP_Init(); + + EMU_enter_DefaultMode_from_RESET(); + CMU_enter_DefaultMode_from_RESET(); + USART0_enter_DefaultMode_from_RESET(); + CRYOTIMER_enter_DefaultMode_from_RESET(); + PORTIO_enter_DefaultMode_from_RESET(); + // [Config Calls]$ + +} + +//================================================================================ +// EMU_enter_DefaultMode_from_RESET +//================================================================================ +extern void EMU_enter_DefaultMode_from_RESET(void) { + + // $[EMU Initialization] + /* Initialize DCDC regulator */ + EMU_DCDCInit_TypeDef dcdcInit = EMU_DCDCINIT_DEFAULT; + + dcdcInit.powerConfig = emuPowerConfig_DcdcToDvdd; + dcdcInit.dcdcMode = emuDcdcMode_LowNoise; + dcdcInit.mVout = 1800; + dcdcInit.em01LoadCurrent_mA = 15; + dcdcInit.em234LoadCurrent_uA = 10; + dcdcInit.maxCurrent_mA = 200; + dcdcInit.anaPeripheralPower = emuDcdcAnaPeripheralPower_DCDC; + dcdcInit.reverseCurrentControl = 160; + + EMU_DCDCInit(&dcdcInit); + /* Initialize EM2/EM3 mode */ + EMU_EM23Init_TypeDef em23Init = EMU_EM23INIT_DEFAULT; + + em23Init.em23VregFullEn = 0; + + EMU_EM23Init(&em23Init); + /* Initialize EM4H/S mode */ + EMU_EM4Init_TypeDef em4Init = EMU_EM4INIT_DEFAULT; + + em4Init.retainLfrco = 0; + em4Init.retainLfxo = 0; + em4Init.retainUlfrco = 0; + em4Init.em4State = emuEM4Shutoff; + em4Init.pinRetentionMode = emuPinRetentionDisable; + + EMU_EM4Init(&em4Init); + // [EMU Initialization]$ + +} + +//================================================================================ +// LFXO_enter_DefaultMode_from_RESET +//================================================================================ +extern void LFXO_enter_DefaultMode_from_RESET(void) { + +} + +//================================================================================ +// CMU_enter_DefaultMode_from_RESET +//================================================================================ +extern void CMU_enter_DefaultMode_from_RESET(void) { + + // $[High Frequency Clock Setup] + /* Initializing HFXO */ + CMU_HFXOInit_TypeDef hfxoInit = CMU_HFXOINIT_DEFAULT; + + CMU_HFXOInit(&hfxoInit); + + /* Setting system HFRCO frequency */ + CMU_HFRCOFreqSet (cmuHFRCOFreq_38M0Hz); + + /* Using HFRCO as high frequency clock, HFCLK */ + CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFRCO); + // [High Frequency Clock Setup]$ + + // $[LE clocks enable] + /* Enable LFRCO oscillator, and wait for it to be stable */ + CMU_OscillatorEnable(cmuOsc_LFRCO, true, true); + + // [LE clocks enable]$ + + // $[LFACLK Setup] + /* LFACLK is disabled */ + // [LFACLK Setup]$ + // $[LFBCLK Setup] + /* LFBCLK is disabled */ + // [LFBCLK Setup]$ + // $[LFECLK Setup] + /* LFECLK is disabled */ + // [LFECLK Setup]$ + // $[Peripheral Clock enables] + /* Enable clock for HF peripherals */ + CMU_ClockEnable(cmuClock_HFPER, true); + + /* Enable clock for CRYOTIMER */ + CMU_ClockEnable(cmuClock_CRYOTIMER, true); + + /* Enable clock for USART0 */ + CMU_ClockEnable(cmuClock_USART0, true); + + /* Enable clock for GPIO by default */ + CMU_ClockEnable(cmuClock_GPIO, true); + + // [Peripheral Clock enables]$ + + // $[Clock output] + /* Disable CLKOUT0 output */ + CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_CLKOUTSEL0_MASK) + | CMU_CTRL_CLKOUTSEL0_DISABLED; + /* Disable CLKOUT1 output */ + CMU->CTRL = (CMU->CTRL & ~_CMU_CTRL_CLKOUTSEL1_MASK) + | CMU_CTRL_CLKOUTSEL1_DISABLED; + + // [Clock output]$ + + // $[CMU_IO] + /* Disable CLKOUT0 pin */ + CMU->ROUTEPEN &= ~CMU_ROUTEPEN_CLKOUT0PEN; + + /* Disable CLKOUT1 pin */ + CMU->ROUTEPEN &= ~CMU_ROUTEPEN_CLKOUT1PEN; + + // [CMU_IO]$ + +} + +//================================================================================ +// ADC0_enter_DefaultMode_from_RESET +//================================================================================ +extern void ADC0_enter_DefaultMode_from_RESET(void) { + + // $[ADC0_Init] + // [ADC0_Init]$ + + // $[ADC0_InputConfiguration] + // [ADC0_InputConfiguration]$ + +} + +//================================================================================ +// ACMP0_enter_DefaultMode_from_RESET +//================================================================================ +extern void ACMP0_enter_DefaultMode_from_RESET(void) { + + // $[ACMP0_Init] + // [ACMP0_Init]$ + + // $[ACMP0_IO] + // [ACMP0_IO]$ + +} + +//================================================================================ +// ACMP1_enter_DefaultMode_from_RESET +//================================================================================ +extern void ACMP1_enter_DefaultMode_from_RESET(void) { + + // $[ACMP1_Init] + // [ACMP1_Init]$ + + // $[ACMP1_IO] + // [ACMP1_IO]$ + +} + +//================================================================================ +// IDAC0_enter_DefaultMode_from_RESET +//================================================================================ +extern void IDAC0_enter_DefaultMode_from_RESET(void) { + +} + +//================================================================================ +// RTCC_enter_DefaultMode_from_RESET +//================================================================================ +extern void RTCC_enter_DefaultMode_from_RESET(void) { + + // $[Compare/Capture Channel 0 init] + // [Compare/Capture Channel 0 init]$ + + // $[Compare/Capture Channel 1 init] + // [Compare/Capture Channel 1 init]$ + + // $[Compare/Capture Channel 2 init] + // [Compare/Capture Channel 2 init]$ + + // $[RTCC init] + // [RTCC init]$ + +} + +//================================================================================ +// USART0_enter_DefaultMode_from_RESET +//================================================================================ +extern void USART0_enter_DefaultMode_from_RESET(void) { + + // $[USART_InitAsync] + USART_InitAsync_TypeDef initasync = USART_INITASYNC_DEFAULT; + + initasync.enable = usartDisable; + initasync.baudrate = 115200; + initasync.databits = usartDatabits8; + initasync.parity = usartNoParity; + initasync.stopbits = usartStopbits1; + initasync.oversampling = usartOVS16; +#if defined( USART_INPUT_RXPRS ) && defined( USART_CTRL_MVDIS ) + initasync.mvdis = 0; + initasync.prsRxEnable = 0; + initasync.prsRxCh = 0; +#endif + + USART_InitAsync(USART0, &initasync); + // [USART_InitAsync]$ + + // $[USART_InitSync] + // [USART_InitSync]$ + + // $[USART_InitPrsTrigger] + USART_PrsTriggerInit_TypeDef initprs = USART_INITPRSTRIGGER_DEFAULT; + + initprs.rxTriggerEnable = 0; + initprs.txTriggerEnable = 0; + initprs.prsTriggerChannel = usartPrsTriggerCh0; + + USART_InitPrsTrigger(USART0, &initprs); + // [USART_InitPrsTrigger]$ + + // $[USART_InitIO] + /* Disable CLK pin */ + USART0->ROUTELOC0 = (USART0->ROUTELOC0 & (~_USART_ROUTELOC0_CLKLOC_MASK)) + | USART_ROUTELOC0_CLKLOC_LOC0; + USART0->ROUTEPEN = USART0->ROUTEPEN & (~USART_ROUTEPEN_CLKPEN); + + /* Disable CS pin */ + USART0->ROUTELOC0 = (USART0->ROUTELOC0 & (~_USART_ROUTELOC0_CSLOC_MASK)) + | USART_ROUTELOC0_CSLOC_LOC0; + USART0->ROUTEPEN = USART0->ROUTEPEN & (~USART_ROUTEPEN_CSPEN); + + /* Set up CTS pin */ + USART0->ROUTELOC1 = (USART0->ROUTELOC1 & (~_USART_ROUTELOC1_CTSLOC_MASK)) + | USART_ROUTELOC1_CTSLOC_LOC30; + USART0->ROUTEPEN = USART0->ROUTEPEN | USART_ROUTEPEN_CTSPEN; + + /* Set up RTS pin */ + USART0->ROUTELOC1 = (USART0->ROUTELOC1 & (~_USART_ROUTELOC1_RTSLOC_MASK)) + | USART_ROUTELOC1_RTSLOC_LOC30; + USART0->ROUTEPEN = USART0->ROUTEPEN | USART_ROUTEPEN_RTSPEN; + + /* Set up RX pin */ + USART0->ROUTELOC0 = (USART0->ROUTELOC0 & (~_USART_ROUTELOC0_RXLOC_MASK)) + | USART_ROUTELOC0_RXLOC_LOC0; + USART0->ROUTEPEN = USART0->ROUTEPEN | USART_ROUTEPEN_RXPEN; + + /* Set up TX pin */ + USART0->ROUTELOC0 = (USART0->ROUTELOC0 & (~_USART_ROUTELOC0_TXLOC_MASK)) + | USART_ROUTELOC0_TXLOC_LOC0; + USART0->ROUTEPEN = USART0->ROUTEPEN | USART_ROUTEPEN_TXPEN; + + // [USART_InitIO]$ + + // $[USART_Misc] + /* Disable CTS */ + USART0->CTRLX = USART0->CTRLX & (~USART_CTRLX_CTSEN); + /* Set CTS active low */ + USART0->CTRLX = USART0->CTRLX & (~USART_CTRLX_CTSINV); + /* Set RTS active low */ + USART0->CTRLX = USART0->CTRLX & (~USART_CTRLX_RTSINV); + /* Set CS active low */ + USART0->CTRL = USART0->CTRL & (~USART_CTRL_CSINV); + /* Set TX active high */ + USART0->CTRL = USART0->CTRL & (~USART_CTRL_TXINV); + /* Set RX active high */ + USART0->CTRL = USART0->CTRL & (~USART_CTRL_RXINV); + // [USART_Misc]$ + + // $[USART_Enable] + + /* Enable USART if opted by user */ + USART_Enable(USART0, usartEnable); + // [USART_Enable]$ + +} + +//================================================================================ +// USART1_enter_DefaultMode_from_RESET +//================================================================================ +extern void USART1_enter_DefaultMode_from_RESET(void) { + + // $[USART_InitAsync] + // [USART_InitAsync]$ + + // $[USART_InitSync] + // [USART_InitSync]$ + + // $[USART_InitPrsTrigger] + // [USART_InitPrsTrigger]$ + + // $[USART_InitIO] + // [USART_InitIO]$ + + // $[USART_Misc] + // [USART_Misc]$ + + // $[USART_Enable] + // [USART_Enable]$ + +} + +//================================================================================ +// LEUART0_enter_DefaultMode_from_RESET +//================================================================================ +extern void LEUART0_enter_DefaultMode_from_RESET(void) { + + // $[LEUART0 initialization] + // [LEUART0 initialization]$ + +} + +//================================================================================ +// WDOG0_enter_DefaultMode_from_RESET +//================================================================================ +extern void WDOG0_enter_DefaultMode_from_RESET(void) { + + // $[WDOG Initialization] + // [WDOG Initialization]$ + +} + +//================================================================================ +// I2C0_enter_DefaultMode_from_RESET +//================================================================================ +extern void I2C0_enter_DefaultMode_from_RESET(void) { + + // $[I2C0 I/O setup] + // [I2C0 I/O setup]$ + + // $[I2C0 initialization] + // [I2C0 initialization]$ + +} + +//================================================================================ +// GPCRC_enter_DefaultMode_from_RESET +//================================================================================ +extern void GPCRC_enter_DefaultMode_from_RESET(void) { + +} + +//================================================================================ +// LDMA_enter_DefaultMode_from_RESET +//================================================================================ +extern void LDMA_enter_DefaultMode_from_RESET(void) { + +} + +//================================================================================ +// TIMER0_enter_DefaultMode_from_RESET +//================================================================================ +extern void TIMER0_enter_DefaultMode_from_RESET(void) { + + // $[TIMER0 I/O setup] + // [TIMER0 I/O setup]$ + + // $[TIMER0 initialization] + // [TIMER0 initialization]$ + + // $[TIMER0 CC0 init] + // [TIMER0 CC0 init]$ + + // $[TIMER0 CC1 init] + // [TIMER0 CC1 init]$ + + // $[TIMER0 CC2 init] + // [TIMER0 CC2 init]$ + + // $[TIMER0 DTI init] + // [TIMER0 DTI init]$ + +} + +//================================================================================ +// TIMER1_enter_DefaultMode_from_RESET +//================================================================================ +extern void TIMER1_enter_DefaultMode_from_RESET(void) { + + // $[TIMER1 I/O setup] + // [TIMER1 I/O setup]$ + + // $[TIMER1 initialization] + // [TIMER1 initialization]$ + + // $[TIMER1 CC0 init] + // [TIMER1 CC0 init]$ + + // $[TIMER1 CC1 init] + // [TIMER1 CC1 init]$ + + // $[TIMER1 CC2 init] + // [TIMER1 CC2 init]$ + + // $[TIMER1 CC3 init] + // [TIMER1 CC3 init]$ + +} + +//================================================================================ +// LETIMER0_enter_DefaultMode_from_RESET +//================================================================================ +extern void LETIMER0_enter_DefaultMode_from_RESET(void) { + + // $[LETIMER0 Compare Values] + // [LETIMER0 Compare Values]$ + + // $[LETIMER0 Repeat Values] + // [LETIMER0 Repeat Values]$ + + // $[LETIMER0 Initialization] + // [LETIMER0 Initialization]$ + + // $[LETIMER0 PRS Input Triggers] + // [LETIMER0 PRS Input Triggers]$ + + // $[LETIMER0 I/O setup] + // [LETIMER0 I/O setup]$ + +} + +//================================================================================ +// CRYOTIMER_enter_DefaultMode_from_RESET +//================================================================================ +extern void CRYOTIMER_enter_DefaultMode_from_RESET(void) { + + // $[CRYOTIMER_Init] + CRYOTIMER_Init_TypeDef cryoInit = CRYOTIMER_INIT_DEFAULT; + + /* General settings */ + cryoInit.enable = 1; + cryoInit.debugRun = 0; + cryoInit.em4Wakeup = 0; + + /* Clocking settings */ + /* With a frequency of 32768Hz on LFRCO, this will result in a 0.98 ms timeout */ + cryoInit.osc = cryotimerOscLFRCO; + cryoInit.presc = cryotimerPresc_32; + cryoInit.period = cryotimerPeriod_1; + CRYOTIMER_Init(&cryoInit); + // [CRYOTIMER_Init]$ + +} + +//================================================================================ +// PCNT0_enter_DefaultMode_from_RESET +//================================================================================ +extern void PCNT0_enter_DefaultMode_from_RESET(void) { + + // $[PCNT0 I/O setup] + // [PCNT0 I/O setup]$ + + // $[PCNT0 initialization] + // [PCNT0 initialization]$ + +} + +//================================================================================ +// PRS_enter_DefaultMode_from_RESET +//================================================================================ +extern void PRS_enter_DefaultMode_from_RESET(void) { + + // $[PRS initialization] + // [PRS initialization]$ + +} + +//================================================================================ +// PORTIO_enter_DefaultMode_from_RESET +//================================================================================ +extern void PORTIO_enter_DefaultMode_from_RESET(void) { + + // $[Port A Configuration] + + /* Pin PA0 is configured to Push-pull */ + GPIO_PinModeSet(gpioPortA, 0, gpioModePushPull, 0); + + /* Pin PA1 is configured to Input enabled with pull-up */ + GPIO_PinModeSet(gpioPortA, 1, gpioModeInputPull, 1); + + /* Pin PA3 is configured to Push-pull */ + GPIO_PinModeSet(gpioPortA, 3, gpioModePushPull, 0); + + /* Pin PA5 is configured to Push-pull */ + GPIO_PinModeSet(gpioPortA, 5, gpioModePushPull, 1); + // [Port A Configuration]$ + + // $[Port B Configuration] + // [Port B Configuration]$ + + // $[Port C Configuration] + // [Port C Configuration]$ + + // $[Port D Configuration] + // [Port D Configuration]$ + + // $[Port E Configuration] + // [Port E Configuration]$ + + // $[Port F Configuration] + + /* Pin PF4 is configured to Push-pull */ + GPIO_PinModeSet(gpioPortF, 4, gpioModePushPull, 0); + + /* Pin PF5 is configured to Push-pull */ + GPIO_PinModeSet(gpioPortF, 5, gpioModePushPull, 0); + // [Port F Configuration]$ + +} + diff --git a/efm32/src/app.h b/efm32/src/app.h new file mode 100644 index 0000000..3a93e7d --- /dev/null +++ b/efm32/src/app.h @@ -0,0 +1,18 @@ +/* + * app.h + * + * Created on: Jun 26, 2018 + * Author: conor + */ + +#ifndef SRC_APP_H_ +#define SRC_APP_H_ + +#define PRINTING_USE_VCOM + +#define USING_DEV_BOARD + +void printing_init(); + + +#endif /* SRC_APP_H_ */ diff --git a/efm32/src/device.c b/efm32/src/device.c new file mode 100644 index 0000000..b7b3d77 --- /dev/null +++ b/efm32/src/device.c @@ -0,0 +1,131 @@ +/* + * device.c + * + * Created on: Jun 27, 2018 + * Author: conor + */ +#include +#include +#include + +#include "em_chip.h" +#include "em_gpio.h" + +#include "cbor.h" +#include "log.h" +#include "ctaphid.h" +#include "util.h" + +// Generate @num bytes of random numbers to @dest +// return 1 if success, error otherwise +int ctap_generate_rng(uint8_t * dst, size_t num) +{ + int i; + for (i = 0; i < num; i++) + { + *dst++ = rand(); + } + return 1; +} + +uint32_t _c1 = 0, _c2 = 0; +uint32_t ctap_atomic_count(int sel) +{ + if (sel == 0) + { + _c1++; + return _c1; + } + else + { + _c2++; + return _c2; + } +} + +// Verify the user +// return 1 if user is verified, 0 if not +int ctap_user_verification(uint8_t arg) +{ + return 1; +} + +// Test for user presence +// Return 1 for user is present, 0 user not present +int ctap_user_presence_test() +{ + return 1; +} + +// Must be implemented by application +// data is HID_MESSAGE_SIZE long in bytes +void ctaphid_write_block(uint8_t * data) +{ + dump_hex(data, HID_MESSAGE_SIZE); +} + +void heartbeat() +{ + static int beat = 0; + GPIO_PinOutToggle(gpioPortF,4); + GPIO_PinOutToggle(gpioPortF,5); +// printf("heartbeat %d\r\n", CRYOTIMER->CNT); +} + +uint64_t millis() +{ + return CRYOTIMER->CNT; +} + + +void usbhid_init() +{ + +} + +int usbhid_recv(uint8_t * msg) +{ + return 0; +} + +void usbhid_send(uint8_t * msg) +{ +} + +void usbhid_close() +{ +} + +void main_loop_delay() +{ +} + + +void device_init(void) +{ + /* Chip errata */ + CHIP_Init(); + enter_DefaultMode_from_RESET(); + + GPIO_PinModeSet(gpioPortF, + 4, + gpioModePushPull, + 0); + + GPIO_PinModeSet(gpioPortF, + 5, + gpioModePushPull, + 1); + + + + + printing_init(); + + CborEncoder test; + uint8_t buf[20]; + cbor_encoder_init(&test, buf, 20, 0); + + printf("Device init\r\n"); + +} diff --git a/efm32/src/main.c b/efm32/src/main.c new file mode 100644 index 0000000..bde5aed --- /dev/null +++ b/efm32/src/main.c @@ -0,0 +1,13 @@ +#include +#include + +#include "em_chip.h" +#include "em_cmu.h" +#include "em_emu.h" +#include "em_core.h" +#include "em_gpio.h" + +#include "InitDevice.h" + +#include "app.h" +#include "cbor.h" diff --git a/efm32/src/printing.c b/efm32/src/printing.c new file mode 100644 index 0000000..c09fd30 --- /dev/null +++ b/efm32/src/printing.c @@ -0,0 +1,82 @@ +#include "em_chip.h" +#include "em_cmu.h" +#include "em_emu.h" +#include "em_core.h" +#include "em_usart.h" +#include "em_gpio.h" +#include +#include + +#include "app.h" +#ifndef PRINTING_USE_VCOM +int RETARGET_WriteChar(char c) +{ + return ITM_SendChar(c); +} + +int RETARGET_ReadChar(void) +{ + return 0; +} + +void setupSWOForPrint(void) +{ + /* Enable GPIO clock. */ + CMU_ClockEnable(cmuClock_GPIO, true); + + /* Enable Serial wire output pin */ + GPIO->ROUTEPEN |= GPIO_ROUTEPEN_SWVPEN; + + /* Set location 0 */ + GPIO->ROUTELOC0 = GPIO_ROUTELOC0_SWVLOC_LOC0; + + /* Enable output on pin - GPIO Port F, Pin 2 */ + GPIO->P[5].MODEL &= ~(_GPIO_P_MODEL_MODE2_MASK); + GPIO->P[5].MODEL |= GPIO_P_MODEL_MODE2_PUSHPULL; + + /* Enable debug clock AUXHFRCO */ + CMU_OscillatorEnable(cmuOsc_AUXHFRCO, true, true); + CMU->OSCENCMD = CMU_OSCENCMD_AUXHFRCOEN; + + /* Wait until clock is ready */ + while (!(CMU->STATUS & CMU_STATUS_AUXHFRCORDY)); + + /* Enable trace in core debug */ + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + ITM->LAR = 0xC5ACCE55; + ITM->TER = 0x0; + ITM->TCR = 0x0; + TPI->SPPR = 2; + TPI->ACPR = 0x15; // changed from 0x0F on Giant, etc. to account for 19 MHz default AUXHFRCO frequency + ITM->TPR = 0x0; + DWT->CTRL = 0x400003FE; + ITM->TCR = 0x0001000D; + TPI->FFCR = 0x00000100; + ITM->TER = 0x1; +} + + +void printing_init() +{ + setupSWOForPrint(); +} +#else + +int RETARGET_WriteChar(char c) +{ + USART_Tx(USART0,c); + return 0; +} + +int RETARGET_ReadChar(void) +{ + return 0; +} + + +void printing_init() +{ +// GPIO_PinModeSet(gpioPortA,5,gpioModePushPull,1); // VCOM enable +} + +#endif diff --git a/efm32/src/retargetio.c b/efm32/src/retargetio.c new file mode 100644 index 0000000..05e0143 --- /dev/null +++ b/efm32/src/retargetio.c @@ -0,0 +1,475 @@ +/***************************************************************************//** + * @file + * @brief Provide stdio retargeting for all supported toolchains. + * @version 5.5.0 + ******************************************************************************* + * # License + * Copyright 2015 Silicon Labs, Inc. http://www.silabs.com + ******************************************************************************* + * + * This file is licensed under the Silabs License Agreement. See the file + * "Silabs_License_Agreement.txt" for details. Before using this software for + * any purpose, you must agree to the terms of that agreement. + * + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup RetargetIo + * @{ This module provide low-level stubs for retargetting stdio for all + * supported toolchains. + * The stubs are minimal yet sufficient implementations. + * Refer to chapter 12 in the reference manual for newlib 1.17.0 + * for details on implementing newlib stubs. + ******************************************************************************/ + +extern int RETARGET_ReadChar(void); +extern int RETARGET_WriteChar(char c); + +#if !defined(__CROSSWORKS_ARM) && defined(__GNUC__) + +#include +#include +#include +#include +#include "em_device.h" + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +int fileno(FILE *); +/** @endcond */ + +int _close(int file); +int _fstat(int file, struct stat *st); +int _isatty(int file); +int _lseek(int file, int ptr, int dir); +int _read(int file, char *ptr, int len); +caddr_t _sbrk(int incr); +int _write(int file, const char *ptr, int len); + +extern char _end; /**< Defined by the linker */ + +/**************************************************************************//** + * @brief + * Close a file. + * + * @param[in] file + * File you want to close. + * + * @return + * Returns 0 when the file is closed. + *****************************************************************************/ +int _close(int file) +{ + (void) file; + return 0; +} + +/**************************************************************************//** + * @brief Exit the program. + * @param[in] status The value to return to the parent process as the + * exit status (not used). + *****************************************************************************/ +void _exit(int status) +{ + (void) status; + while (1) { + } /* Hang here forever... */ +} + +/**************************************************************************//** + * @brief + * Status of an open file. + * + * @param[in] file + * Check status for this file. + * + * @param[in] st + * Status information. + * + * @return + * Returns 0 when st_mode is set to character special. + *****************************************************************************/ +int _fstat(int file, struct stat *st) +{ + (void) file; + st->st_mode = S_IFCHR; + return 0; +} + +/**************************************************************************//** + * @brief Get process ID. + *****************************************************************************/ +int _getpid(void) +{ + return 1; +} + +/**************************************************************************//** + * @brief + * Query whether output stream is a terminal. + * + * @param[in] file + * Descriptor for the file. + * + * @return + * Returns 1 when query is done. + *****************************************************************************/ +int _isatty(int file) +{ + (void) file; + return 1; +} + +/**************************************************************************//** + * @brief Send signal to process. + * @param[in] pid Process id (not used). + * @param[in] sig Signal to send (not used). + *****************************************************************************/ +int _kill(int pid, int sig) +{ + (void)pid; + (void)sig; + return -1; +} + +/**************************************************************************//** + * @brief + * Set position in a file. + * + * @param[in] file + * Descriptor for the file. + * + * @param[in] ptr + * Poiter to the argument offset. + * + * @param[in] dir + * Directory whence. + * + * @return + * Returns 0 when position is set. + *****************************************************************************/ +int _lseek(int file, int ptr, int dir) +{ + (void) file; + (void) ptr; + (void) dir; + return 0; +} + +/**************************************************************************//** + * @brief + * Read from a file. + * + * @param[in] file + * Descriptor for the file you want to read from. + * + * @param[in] ptr + * Pointer to the chacaters that are beeing read. + * + * @param[in] len + * Number of characters to be read. + * + * @return + * Number of characters that have been read. + *****************************************************************************/ +int _read(int file, char *ptr, int len) +{ + int c, rxCount = 0; + + (void) file; + + while (len--) { + if ((c = RETARGET_ReadChar()) != -1) { + *ptr++ = c; + rxCount++; + } else { + break; + } + } + + if (rxCount <= 0) { + return -1; /* Error exit */ + } + + return rxCount; +} + +/**************************************************************************//** + * @brief + * Increase heap. + * + * @param[in] incr + * Number of bytes you want increment the program's data space. + * + * @return + * Rsturns a pointer to the start of the new area. + *****************************************************************************/ +caddr_t _sbrk(int incr) +{ + static char *heap_end; + char *prev_heap_end; + + if (heap_end == 0) { + heap_end = &_end; + } + + prev_heap_end = heap_end; + heap_end += incr; + + return (caddr_t) prev_heap_end; +} + +/**************************************************************************//** + * @brief + * Write to a file. + * + * @param[in] file + * Descriptor for the file you want to write to. + * + * @param[in] ptr + * Pointer to the text you want to write + * + * @param[in] len + * Number of characters to be written. + * + * @return + * Number of characters that have been written. + *****************************************************************************/ +int _write(int file, const char *ptr, int len) +{ + int txCount; + + (void) file; + + for (txCount = 0; txCount < len; txCount++) { + RETARGET_WriteChar(*ptr++); + } + + return len; +} +#endif /* !defined( __CROSSWORKS_ARM ) && defined( __GNUC__ ) */ + +#if defined(__ICCARM__) +/******************* + * + * Copyright 1998-2003 IAR Systems. All rights reserved. + * + * $Revision: 38614 $ + * + * This is a template implementation of the "__write" function used by + * the standard library. Replace it with a system-specific + * implementation. + * + * The "__write" function should output "size" number of bytes from + * "buffer" in some application-specific way. It should return the + * number of characters written, or _LLIO_ERROR on failure. + * + * If "buffer" is zero then __write should perform flushing of + * internal buffers, if any. In this case "handle" can be -1 to + * indicate that all handles should be flushed. + * + * The template implementation below assumes that the application + * provides the function "MyLowLevelPutchar". It should return the + * character written, or -1 on failure. + * + ********************/ + +#include +#include +#include "em_common.h" + +_STD_BEGIN + +/**************************************************************************//** + * @brief Transmit buffer to USART1 + * @param buffer Array of characters to send + * @param nbytes Number of bytes to transmit + * @return Number of bytes sent + *****************************************************************************/ +static int TxBuf(uint8_t *buffer, int nbytes) +{ + int i; + + for (i = 0; i < nbytes; i++) { + RETARGET_WriteChar(*buffer++); + } + return nbytes; +} + +/* + * If the __write implementation uses internal buffering, uncomment + * the following line to ensure that we are called with "buffer" as 0 + * (i.e. flush) when the application terminates. + */ +size_t __write(int handle, const unsigned char * buffer, size_t size) +{ + /* Remove the #if #endif pair to enable the implementation */ + + size_t nChars = 0; + + if (buffer == 0) { + /* + * This means that we should flush internal buffers. Since we + * don't we just return. (Remember, "handle" == -1 means that all + * handles should be flushed.) + */ + return 0; + } + + /* This template only writes to "standard out" and "standard err", + * for all other file handles it returns failure. */ + if (handle != _LLIO_STDOUT && handle != _LLIO_STDERR) { + return _LLIO_ERROR; + } + + /* Hook into USART1 transmit function here */ + if (TxBuf((uint8_t *) buffer, size) != size) { + return _LLIO_ERROR; + } else { + nChars = size; + } + + return nChars; +} + +size_t __read(int handle, unsigned char * buffer, size_t size) +{ + /* Remove the #if #endif pair to enable the implementation */ + int nChars = 0; + + /* This template only reads from "standard in", for all other file + * handles it returns failure. */ + if (handle != _LLIO_STDIN) { + return _LLIO_ERROR; + } + + for (/* Empty */; size > 0; --size) { + int c = RETARGET_ReadChar(); + if (c < 0) { + break; + } + + *buffer++ = c; + ++nChars; + } + + return nChars; +} + +_STD_END + +#endif /* defined( __ICCARM__ ) */ + +#if defined(__CROSSWORKS_ARM) + +/* Pass each of these function straight to the USART */ +int __putchar(int ch) +{ + return(RETARGET_WriteChar(ch)); +} + +int __getchar(void) +{ + return(RETARGET_ReadChar()); +} + +#endif /* defined( __CROSSWORKS_ARM ) */ + +#if defined(__CC_ARM) +/******************************************************************************/ +/* RETARGET.C: 'Retarget' layer for target-dependent low level functions */ +/******************************************************************************/ +/* This file is part of the uVision/ARM development tools. */ +/* Copyright (c) 2005-2006 Keil Software. All rights reserved. */ +/* This software may only be used under the terms of a valid, current, */ +/* end user licence from KEIL for a compatible version of KEIL software */ +/* development tools. Nothing else gives you the right to use this software. */ +/******************************************************************************/ + +#include + +/* #pragma import(__use_no_semihosting_swi) */ + +struct __FILE{ + int handle; +}; + +/**Standard output stream*/ +FILE __stdout; + +/**************************************************************************//** + * @brief + * Writes character to file + * + * @param[in] f + * File + * + * @param[in] ch + * Character + * + * @return + * Written character + *****************************************************************************/ +int fputc(int ch, FILE *f) +{ + return(RETARGET_WriteChar(ch)); +} + +/**************************************************************************//** + * @brief + * Reads character from file + * + * @param[in] f + * File + * + * @return + * Character + *****************************************************************************/ +int fgetc(FILE *f) +{ + return(RETARGET_ReadChar()); +} + +/**************************************************************************//** + * @brief + * Tests the error indicator for the stream pointed + * to by file + * + * @param[in] f + * File + * + * @return + * Returns non-zero if it is set + *****************************************************************************/ +int ferror(FILE *f) +{ + /* Your implementation of ferror */ + return EOF; +} + +/**************************************************************************//** + * @brief + * Writes a character to the console + * + * @param[in] ch + * Character + *****************************************************************************/ +void _ttywrch(int ch) +{ + RETARGET_WriteChar(ch); +} + +/**************************************************************************//** + * @brief + * Library exit function. This function is called if stack + * overflow occurs. + * + * @param[in] return_code + * Return code + *****************************************************************************/ +void _sys_exit(int return_code) +{ + label: goto label;/* endless loop */ +} +#endif /* defined( __CC_ARM ) */ + +/** @} (end group RetargetIo) */ diff --git a/efm8/.cproject b/efm8/.cproject new file mode 100644 index 0000000..efc88e1 --- /dev/null +++ b/efm8/.cproject @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/efm8/.project b/efm8/.project new file mode 100644 index 0000000..f26a120 --- /dev/null +++ b/efm8/.project @@ -0,0 +1,27 @@ + + + efm8 + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + com.silabs.ss.framework.ide.project.sls.core.SLSProjectNature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/efm8/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs b/efm8/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs new file mode 100644 index 0000000..b4554b4 --- /dev/null +++ b/efm8/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs @@ -0,0 +1,2 @@ +copiedFilesOriginState={} +eclipse.preferences.version=1 diff --git a/efm8/.settings/org.eclipse.ltk.core.refactoring.prefs b/efm8/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 0000000..b196c64 --- /dev/null +++ b/efm8/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/efm8/efm8.hwconf b/efm8/efm8.hwconf new file mode 100644 index 0000000..90bd159 --- /dev/null +++ b/efm8/efm8.hwconf @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/efm8/inc/InitDevice.h b/efm8/inc/InitDevice.h new file mode 100644 index 0000000..9c1b01a --- /dev/null +++ b/efm8/inc/InitDevice.h @@ -0,0 +1,33 @@ +//========================================================= +// inc/InitDevice.h: generated by Hardware Configurator +// +// This file will be regenerated when saving a document. +// leave the sections inside the "$[...]" comment tags alone +// or they will be overwritten! +//========================================================= +#ifndef __INIT_DEVICE_H__ +#define __INIT_DEVICE_H__ + +// USER CONSTANTS +// USER PROTOTYPES + +// $[Mode Transition Prototypes] +extern void enter_DefaultMode_from_RESET(void); +// [Mode Transition Prototypes]$ + +// $[Config(Per-Module Mode)Transition Prototypes] +extern void WDT_0_enter_DefaultMode_from_RESET(void); +extern void PORTS_0_enter_DefaultMode_from_RESET(void); +extern void PBCFG_0_enter_DefaultMode_from_RESET(void); +extern void CIP51_0_enter_DefaultMode_from_RESET(void); +extern void CLOCK_0_enter_DefaultMode_from_RESET(void); +extern void TIMER16_2_enter_DefaultMode_from_RESET(void); +extern void TIMER16_3_enter_DefaultMode_from_RESET(void); +extern void TIMER_SETUP_0_enter_DefaultMode_from_RESET(void); +extern void UARTE_1_enter_DefaultMode_from_RESET(void); +extern void INTERRUPT_0_enter_DefaultMode_from_RESET(void); +extern void USBLIB_0_enter_DefaultMode_from_RESET(void); +// [Config(Per-Module Mode)Transition Prototypes]$ + +#endif + diff --git a/efm8/inc/app.h b/efm8/inc/app.h new file mode 100644 index 0000000..4ab7d29 --- /dev/null +++ b/efm8/inc/app.h @@ -0,0 +1,13 @@ +/* + * app.h + * + * Created on: Jun 25, 2018 + * Author: conor + */ + +#ifndef INC_APP_H_ +#define INC_APP_H_ + +#define USE_PRINTING + +#endif /* INC_APP_H_ */ diff --git a/efm8/inc/config/usbconfig.h b/efm8/inc/config/usbconfig.h new file mode 100644 index 0000000..3a82317 --- /dev/null +++ b/efm8/inc/config/usbconfig.h @@ -0,0 +1,157 @@ +/******************************************************************************* + * @file usbconfig.h + * @brief USB protocol stack library, application supplied configuration options. + *******************************************************************************/ + +//============================================================================= +// inc/config/usbconfig.h: generated by Hardware Configurator +// +// This file will be regenerated when saving a document. leave the sections +// inside the "$[...]" comment tags alone or they will be overwritten! +//============================================================================= +#ifndef __SILICON_LABS_USBCONFIG_H +#define __SILICON_LABS_USBCONFIG_H + +// ----------------------------------------------------------------------------- +// Specify bus- or self-powered +// ----------------------------------------------------------------------------- +// $[Device Power] +#define SLAB_USB_BUS_POWERED 1 +// [Device Power]$ + +// ----------------------------------------------------------------------------- +// Specify USB speed +// ----------------------------------------------------------------------------- +// $[USB Speed] +#define SLAB_USB_FULL_SPEED 1 +// [USB Speed]$ + +// ----------------------------------------------------------------------------- +// Enable or disable the clock recovery +// ----------------------------------------------------------------------------- +// $[Clock Recovery] +#define SLAB_USB_CLOCK_RECOVERY_ENABLED 1 +// [Clock Recovery]$ + +// ----------------------------------------------------------------------------- +// Enable or disable remote wakeup +// ----------------------------------------------------------------------------- +// $[Remote Wake-up] +#define SLAB_USB_REMOTE_WAKEUP_ENABLED 0 +// [Remote Wake-up]$ + +// ----------------------------------------------------------------------------- +// Specify number of interfaces and whether any interfaces support alternate +// settings +// ----------------------------------------------------------------------------- +// $[Number of Interfaces] +#define SLAB_USB_NUM_INTERFACES 1 +#define SLAB_USB_SUPPORT_ALT_INTERFACES 0 +// [Number of Interfaces]$ + +// ----------------------------------------------------------------------------- +// Enable or disable each endpoint +// ----------------------------------------------------------------------------- +// $[Endpoints Used] +#define SLAB_USB_EP1IN_USED 1 +#define SLAB_USB_EP1OUT_USED 1 +#define SLAB_USB_EP2IN_USED 0 +#define SLAB_USB_EP2OUT_USED 0 +#define SLAB_USB_EP3IN_USED 0 +#define SLAB_USB_EP3OUT_USED 0 +// [Endpoints Used]$ + +// ----------------------------------------------------------------------------- +// Specify maximum packet size for each endpoint +// ----------------------------------------------------------------------------- +// $[Endpoint Max Packet Size] +#define SLAB_USB_EP1IN_MAX_PACKET_SIZE 64 +#define SLAB_USB_EP1OUT_MAX_PACKET_SIZE 64 +#define SLAB_USB_EP2IN_MAX_PACKET_SIZE 1 +#define SLAB_USB_EP2OUT_MAX_PACKET_SIZE 1 +#define SLAB_USB_EP3IN_MAX_PACKET_SIZE 1 +#define SLAB_USB_EP3OUT_MAX_PACKET_SIZE 1 +// [Endpoint Max Packet Size]$ + +// ----------------------------------------------------------------------------- +// Specify transfer type of each endpoint +// ----------------------------------------------------------------------------- +// $[Endpoint Transfer Type] +#define SLAB_USB_EP1IN_TRANSFER_TYPE USB_EPTYPE_INTR +#define SLAB_USB_EP1OUT_TRANSFER_TYPE USB_EPTYPE_INTR +#define SLAB_USB_EP2IN_TRANSFER_TYPE USB_EPTYPE_BULK +#define SLAB_USB_EP2OUT_TRANSFER_TYPE USB_EPTYPE_BULK +#define SLAB_USB_EP3IN_TRANSFER_TYPE USB_EPTYPE_ISOC +#define SLAB_USB_EP3OUT_TRANSFER_TYPE USB_EPTYPE_ISOC +// [Endpoint Transfer Type]$ + +// ----------------------------------------------------------------------------- +// Enable or disable callback functions +// ----------------------------------------------------------------------------- +// $[Callback Functions] +#define SLAB_USB_HANDLER_CB 0 +#define SLAB_USB_IS_SELF_POWERED_CB 1 +#define SLAB_USB_RESET_CB 1 +#define SLAB_USB_SETUP_CMD_CB 1 +#define SLAB_USB_SOF_CB 0 +#define SLAB_USB_STATE_CHANGE_CB 1 +// [Callback Functions]$ + +// ----------------------------------------------------------------------------- +// Specify number of languages supported by string descriptors. +// ----------------------------------------------------------------------------- +// $[Number of Languages] +#define SLAB_USB_NUM_LANGUAGES 1 +// [Number of Languages]$ + +// ----------------------------------------------------------------------------- +// If only one descriptor language is supported, specify that language here. +// If multiple descriptor languages are supported, this value is ignored and +// the supported languages must listed in the +// myUsbStringTableLanguageIDsDescriptor structure. +// ----------------------------------------------------------------------------- +// $[USB Language] +#define SLAB_USB_LANGUAGE USB_LANGID_ENUS +// [USB Language]$ + +// ----------------------------------------------------------------------------- +// +// Set the power saving mode +// +// SLAB_USB_PWRSAVE_MODE configures when the device will automatically enter +// the USB power-save mode. It is a bitmask constant with bit values: +// USB_PWRSAVE_MODE_OFF - No energy saving mode selected +// USB_PWRSAVE_MODE_ONSUSPEND - Enter USB power-save mode on USB suspend +// USB_PWRSAVE_MODE_ONVBUSOFF - Enter USB power-save mode when not attached +// to the USB host. +// USB_PWRSAVE_MODE_FASTWAKE - Exit USB power-save mode more quickly, but +// consume more power while in USB power-save +// mode. +// While the device is in USB power-save mode +// (typically during USB suspend), the +// internal voltage regulator stays in normal +// power mode instead of entering suspend +// power mode. +// This is an advanced feature that may be +// useful in certain applications that support +// remote wakeup. +// +// ----------------------------------------------------------------------------- +// $[Power Save Mode] +#define SLAB_USB_PWRSAVE_MODE USB_PWRSAVE_MODE_OFF +// [Power Save Mode]$ + +// ----------------------------------------------------------------------------- +// Enable or disable polled mode +// +// When enabled, the application must call USBD_Run() periodically to process +// USB events. +// When disabled, USB events will be handled automatically by an interrupt +// handler. +// ----------------------------------------------------------------------------- +// $[Polled Mode] +#define SLAB_USB_POLLED_MODE 0 +// [Polled Mode]$ + +#endif // __SILICON_LABS_USBCONFIG_H + diff --git a/efm8/inc/descriptors.h b/efm8/inc/descriptors.h new file mode 100644 index 0000000..4bad6cb --- /dev/null +++ b/efm8/inc/descriptors.h @@ -0,0 +1,60 @@ +/******************************************************************************* + * @file descriptors.h + * @brief USB descriptors header file. + *******************************************************************************/ + +//============================================================================= +// inc/descriptors.h: generated by Hardware Configurator +// +// This file will be regenerated when saving a document. leave the sections +// inside the "$[...]" comment tags alone or they will be overwritten! +//============================================================================= +#ifndef __SILICON_LABS_DESCRIPTORS_H +#define __SILICON_LABS_DESCRIPTORS_H + +//----------------------------------------------------------------------------- +// Includes +//----------------------------------------------------------------------------- +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// -------------------- USB Identification ------------------------------------ +// +// ********** +// NOTE: YOU MUST PROVIDE YOUR OWN USB VID/PID (below) +// ********** +// +// Following are the definition of the USB VID and PID. These are, by default, +// values that are assigned to Silicon Labs. These values are provided merely +// as an example. You may not use the Silicon Labs VID/PID values in your +// product. You must provide your own assigned VID and PID values. +//----------------------------------------------------------------------------- +// $[Vendor ID] +#define USB_VENDOR_ID htole16(0x10c4) +// [Vendor ID]$ + +// $[Product ID] +#define USB_PRODUCT_ID htole16(0x8acf) +// [Product ID]$ + +extern SI_SEGMENT_VARIABLE(ReportDescriptor0[34], const uint8_t, SI_SEG_CODE); + +extern SI_SEGMENT_VARIABLE(deviceDesc[], const USB_DeviceDescriptor_TypeDef, SI_SEG_CODE); +extern SI_SEGMENT_VARIABLE(configDesc[], const uint8_t, SI_SEG_CODE); +extern SI_SEGMENT_VARIABLE(initstruct, const USBD_Init_TypeDef, SI_SEG_CODE); + +#define HID_PACKET_SIZE 64 + +#ifdef __cplusplus +} +#endif + +#endif // __SILICON_LABS_DESCRIPTORS_H + diff --git a/efm8/inc/printing.h b/efm8/inc/printing.h new file mode 100644 index 0000000..25c0320 --- /dev/null +++ b/efm8/inc/printing.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016, Conor Patrick + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 THE COPYRIGHT OWNER OR 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. + + */ + +#ifndef PRINTING_H_ +#define PRINTING_H_ + +#include +#include +#include +#include "app.h" + +#define watchdog() (WDTCN = 0xA5) + +#define reboot() (RSTSRC = 1 << 4) + +void u2f_delay(uint32_t ms); + +void usb_write(uint8_t* buf, uint8_t len); + + + +#ifdef USE_PRINTING + + void dump_hex(uint8_t* hex, uint8_t len); + + void cputd(uint32_t i); + void cputx(uint32_t i); + +#define cputb(x) cputx((uint8_t) (x)) +#define cputl(x) cputd((uint32_t) (x)) +#define cputlx(x) cputx((uint32_t) (x)) + + void cprints(const char * str); + void cprintb(const char * tag, uint8_t c, ...); + void cprintd(const char * tag, uint8_t c, ...); + void cprintx(const char * tag, uint8_t c, ...); + void cprintl(const char * tag, uint8_t c, ...); + void cprintlx(const char * tag, uint8_t c, ...); + +#else + + #define cprintx(x) + #define cprintb(x) + #define cprintlx(x) + #define cprintl(x) + #define cprintd(x) + #define cprints(x) + + #define cputx(x) + #define cputb(x) + #define cputl(x) + #define cputlx(x) + + #define putf(x) + #define dump_hex(x) + +#endif + + + + +#endif /* BSP_H_ */ diff --git a/efm8/lib/efm8_assert/assert.c b/efm8/lib/efm8_assert/assert.c new file mode 100644 index 0000000..b9a65c7 --- /dev/null +++ b/efm8/lib/efm8_assert/assert.c @@ -0,0 +1,12 @@ +/**************************************************************************//** + * Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved. + * + * http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt + *****************************************************************************/ + +#ifndef NDEBUG +void slab_Assert() +{ + while ( 1 ); +} +#endif diff --git a/efm8/lib/efm8_assert/assert.h b/efm8/lib/efm8_assert/assert.h new file mode 100644 index 0000000..5b951a5 --- /dev/null +++ b/efm8/lib/efm8_assert/assert.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * Copyright (c) 2014 by Silicon Laboratories Inc. All rights reserved. + * + * http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt + *****************************************************************************/ + +#ifndef __ASSERT_H__ + +#include "efm8_config.h" + +/**************************************************************************//** + * @addtogroup efm8_assert + * @{ + * + * @brief Runtime assert for EFM8 + * + * This module contains a runtime assert macro. It can be compiled out by setting + * the NDEBUG flag. + * + *****************************************************************************/ + + +/**************************************************************************//** + * @def NDEBUG + * @brief Controls if the asserts are present. + * + * Asserts are removed if this symbol is defined + * + *****************************************************************************/ + +/**************************************************************************//** + * @def USER_ASSERT + * @brief User implemented assert function. + * + * When asserts are enabled the default handler can be be replaced with a user defined + * function of the form 'void userAssertName( const char * file, int line )' by setting + * the value of USER_ASSERT to the userAssertName. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_ASSERT(expr) + * @brief default implementation of assert_failed. + * + * This function can be replaced by a user defined assert function by setting the USER_ASSERT flag + *****************************************************************************/ + +#ifdef NDEBUG + #define SLAB_ASSERT(expr) +#else + #ifdef USER_ASSERT + #define SLAB_ASSERT(expr) ((expr) ? ((void)0) : USER_ASSERT( __FILE__, __LINE__ )) + #else + void slab_Assert(); + //Yes this is smaller than if(!expr){assert} + #define SLAB_ASSERT(expr) if(expr){}else{slab_Assert();} + #endif +#endif + +#endif //!__ASSERT_H__ diff --git a/efm8/lib/efm8_usb/Readme.txt b/efm8/lib/efm8_usb/Readme.txt new file mode 100644 index 0000000..8bcff83 --- /dev/null +++ b/efm8/lib/efm8_usb/Readme.txt @@ -0,0 +1,63 @@ +------------------------------------------------------------------------------- + Readme.txt +------------------------------------------------------------------------------- + +Copyright 2014 Silicon Laboratories, Inc. +http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt + +Program Description: +------------------- + +This is the generic EFM8 USB Firmware Library. Please see the EFM8 Libraries +Documentation for more information (/doc/EFM8/software/Lib/index.html). + +Known Issues and Limitations: +---------------------------- + +1) The library does not reset its Data Toggle after receiving a SET_INTERFACE + request. + +Target and Tool Chain Information: +--------------------------------- + +Target: EFM8UB1, EFM8UB2, EFM8UB3, EFM8UB4, C8051F320/1, C8051F326/7, C8051F34x, C8051F38x +Tool chain: Keil + +File List: +--------- + +/inc/efm8_usb.h +/src/efm8_usbd.c +/src/efm8_usbdch9.c +/src/efm8_usbdep.c +/src/efm8_usbdint.c + +Release Information: +------------------- + +Version 1.0.0 + - Initial release. + +Version 1.0.1 + - Fixed bug in logic of remote wakeup feature where the device would + attempt to wake the host before enabling its USB transceiver. + - Fixed bug where the device would stall the Data Phase instead of the + Setup Phase when sending a procedural stall on Endpoint 0. + - Fixed bug where a bus-powered device would look at VBUS after a USB Reset + to determine if it should enter the Default or Attached State. VBUS is + always present on a bus-powered device, so it should automatically enter + the Default State. + - Removed code that generated a compiler warning when + USB_PWRSAVE_MODE_FASTWAKE was enabled. + - Improved documentation of USB_PWRSAVE_MODE_FASTWAKE feature. + +Version 1.0.2 + - Added ability to detect short OUT packet in Isochronous mode and + stuff the buffer with zeroes to keep isochronous stream in sync. + +Version 1.0.3 + - Added support for EFM8UB3 and EFM8UB4 devices. + +------------------------------------------------------------------------------- + End Of File +------------------------------------------------------------------------------- diff --git a/efm8/lib/efm8_usb/inc/efm8_usb.h b/efm8/lib/efm8_usb/inc/efm8_usb.h new file mode 100644 index 0000000..fe70a9c --- /dev/null +++ b/efm8/lib/efm8_usb/inc/efm8_usb.h @@ -0,0 +1,2325 @@ +/***************************************************************************//** + * Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved. + * + * http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt + ******************************************************************************/ + +#ifndef __SILICON_LABS_EFM8_USB_H__ +#define __SILICON_LABS_EFM8_USB_H__ + +#include "si_toolchain.h" +#include "usbconfig.h" +#include +#include +#include +#include + +/***************************************************************************//** + * @addtogroup Efm8_usb + * @brief USB Device Protocol Stack for EFM8 devices + * @{ + * + * @section usb_device_contents Contents + * + * @li @ref usb_device_library_revision + * @li @ref usb_device_intro + * @li @ref usb_device_api + * @li @ref usb_device_conf + * @li @ref usb_device_powersave + * @li @ref usb_device_transfers + * @li @ref usb_device_pitfalls + * + * @n @section usb_device_library_revision EFM8 USB Library Revision + * Library Revision: 1.0.3 + * + * @n @section usb_device_intro Introduction + * + * The USB device protocol stack provides an API which makes it possible to + * create USB devices with a minimum of effort. The device stack supports Control, + * Bulk, Interrupt, and Isochronous transfers. + * + * The stack is highly configurable to suit various needs and includes + * demonstration projects to get you started fast. + * + * We recommend that you read through this documentation, then proceed to build + * and test a few example projects before you start designing your own device. + * + * @n @section usb_library_architecture_diagram Library Architecture Diagram + * + * @image html USB_Library_Architecture.png + * + * @n @section usb_device_api The EFM8 USB Library API + * + * This section contains brief descriptions of the functions in the API. You will + * find detailed information on input and output parameters and return values by + * clicking on the hyper-linked function names. It is also a good idea to study + * the code in the USB demonstration projects. + * + * Your application code must include one header file: @em efm8_usb.h. + * + * All functions defined in the API can be called from within interrupt handlers. + * + * @subsection usb_device_api_functions API Functions + * + * @ref USBD_Init() @n + * This function is called to register your device and all its properties with + * the device stack. The application must fill in a @ref USBD_Init_TypeDef + * structure prior to calling. When this function has been called your device + * is ready to be enumerated by the USB host. + * + * @ref USBD_Read(), @ref USBD_Write() @n + * These functions initiate data transfers. + * @n @htmlonly USBD_Read() @endhtmlonly initiate a transfer of data @em + * from host @em to device (an @em OUT transfer in USB terminology). + * @n @htmlonly USBD_Write() @endhtmlonly initiate a transfer of data @em from + * device @em to host (an @em IN transfer). + * + * When the USB host actually performs the transfer, your application will be + * notified by means of a call to the @ref USBD_XferCompleteCb() callback + * function (optionally). Refer to @ref TransferCallback for details of the + * callback functionality. + * + * @ref USBD_AbortTransfer(), @ref USBD_AbortAllTransfers() @n + * These functions terminate transfers that are initiated with @htmlonly + * USBD_Read() or USBD_Write() @endhtmlonly but that have not completed yet. + * These functions will deactivate the transfer setup to make the USB device + * endpoint hardware ready for new (and potentially) different transfers. + * + * @ref USBD_Connect(), @ref USBD_Disconnect() @n + * These functions turn the data-line (D+ or D-) pull-up on or off. They can + * be used to force re-enumeration. It's good practice to delay at least one + * second between @htmlonly USBD_Disconnect() and USBD_Connect() @endhtmlonly + * to allow the USB host to unload the currently active device driver. + * + * @ref USBD_EpIsBusy() @n + * Checks if an endpoint is busy. + * + * @ref USBD_StallEp(), @ref USBD_UnStallEp() @n + * These functions stall or un-stall an endpoint. This functionality may not + * be needed by your application. They may be useful when implementing some + * USB classes, e.g. a mass storage devices use them extensively. + * + * @ref USBD_Stop() @n + * Removes the data-line (D+ or D-) pull-up and disables the USB block. The + * application should call @htmlonly USBD_Init() after calling + * USBD_Stop() @endhtmlonly to restart USB operation. + * + * @ref USBD_Suspend() @n + * Puts the device in its low-power suspend mode. This function will not exit + * until a wakeup event (resume signaling, VBUS attachment/removal, or remote + * wakeup source interrupt) occurs. The USB Library can be configured to + * automatically call this function by configuring @ref SLAB_USB_PWRSAVE_MODE. + * + * @ref USBD_RemoteWakeup() @n + * Used in SUSPENDED state (see @ref USB_Status_TypeDef) to signal resume to + * host. The function will be called automatically by the library if the + * @ref USBD_RemoteWakeupCb() function returns true. The function will + * also check that the host has sent a SET_FEATURE request to enable Remote + * Wakeup before issuing the resume. + * + * @ref USBD_GetUsbState() @n + * Returns the device USB state (see @ref USBD_State_TypeDef). Refer to + * Figure 9-1. "Device State Diagram" in the USB revision 2.0 specification. + * + * @ref USBD_Run() @n + * When @ref SLAB_USB_POLLED_MODE is set to 1, the USB interrupt is disabled + * and the application must periodically call @htmlonly USBD_Run() + * @endhtmlonly to handle USB events. + * + * @n @subsection usb_device_api_callback Callback Functions + * + * @subsubsection usb_device_api_mandatory_callbacks Mandatory Callback Functions + * + * @n @anchor TransferCallback + * @ref USBD_XferCompleteCb() is called each time a packet is sent or + * received. It is called with three parameters, the status of the transfer, + * the number of bytes transferred and the number of bytes remaining. The + * transfer complete callback can be enabled or disabled by setting the + * callback parameters of @ref USBD_Write() and @ref USBD_Read() to + * true or false. + * @note This callback is called from within the USB interrupt handler if + * @ref SLAB_USB_POLLED_MODE is set to 1. Otherwise, it is called from + * @ref USBD_Run(). + * + * @n + * @subsubsection usb_device_api_optional_callbacks Optional Callback Functions + * + * @n These callbacks are all optional, and it is up to the application + * programmer to decide if the application needs the functionality they + * provide. Each callback is enabled or disabled by setting a constant in + * usbconfig.h. + * @note These callbacks are called from within the USB interrupt handler if + * @ref SLAB_USB_POLLED_MODE is set to 1. Otherwise, they are called + * from @ref USBD_Run(). + * + * @n USBD_ResetCb() is called each time reset signaling is sensed on the USB + * wire. + * + * @n USBD_SofCb() is called with the frame number as a parameter on each SOF + * interrupt. + * + * @n USBD_DeviceStateChangeCb() is called whenever the device state changes. + * Some uses of this include detecting that a USB suspend has been issued + * in order to reduce current consumption or calling USBD_Read() after + * entering the Configured state. The USB HID keyboard example + * project has a good example on how to use this callback. + * + * @n USBD_IsSelfPoweredCb() is called by the device stack when host + * queries the device with a GET_STATUS command to check if the device is + * currently self-powered or bus-powered. This feature is only applicable on + * self-powered devices which can also operate when only bus power is + * available. + * + * @n USBD_SetupCmdCb() is called each time a setup command is received from + * the host. Use this callback to override or extend the default handling of + * standard setup commands, and to implement class- or vendor-specific setup + * commands. The USB HID keyboard example project has a good example of how + * to use this callback. + * + * @n @section usb_device_conf Configuring the Library + * + * Your application must provide a header file named @em usbconfig.h. This file + * must contain the following \#define's. See @ref usb_config for + * documentation of these constants.@n @n + * @code + * // ----------------------------------------------------------------------------- + * // Specify bus- or self-powered + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_BUS_POWERED 0 + * + * // ----------------------------------------------------------------------------- + * // Specify USB speed + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_FULL_SPEED 1 + * + * // ----------------------------------------------------------------------------- + * // Enable or disable the clock recovery + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_CLOCK_RECOVERY_ENABLED 1 + * + * // ----------------------------------------------------------------------------- + * // Enable or disable remote wakeup + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_REMOTE_WAKEUP_ENABLED 0 + * + * // ----------------------------------------------------------------------------- + * // Specify number of interfaces and whether any interfaces support alternate + * // settings + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_NUM_INTERFACES 1 + * #define SLAB_USB_SUPPORT_ALT_INTERFACES 0 + * + * // ----------------------------------------------------------------------------- + * // Enable or disable each endpoint + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_EP1IN_USED 1 + * #define SLAB_USB_EP1OUT_USED 0 + * #define SLAB_USB_EP2IN_USED 0 + * #define SLAB_USB_EP2OUT_USED 0 + * #define SLAB_USB_EP3IN_USED 0 + * #define SLAB_USB_EP3OUT_USED 0 + * + * // ----------------------------------------------------------------------------- + * // Specify the maximum packet size for each endpoint + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_EP1IN_MAX_PACKET_SIZE 64 + * #define SLAB_USB_EP1OUT_MAX_PACKET_SIZE 0 + * #define SLAB_USB_EP2IN_MAX_PACKET_SIZE 0 + * #define SLAB_USB_EP2OUT_MAX_PACKET_SIZE 0 + * #define SLAB_USB_EP3IN_MAX_PACKET_SIZE 0 + * #define SLAB_USB_EP3OUT_MAX_PACKET_SIZE 0 + * + * // ----------------------------------------------------------------------------- + * // Specify transfer type of each endpoint + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_EP1IN_TRANSFER_TYPE USB_EPTYPE_INTR + * #define SLAB_USB_EP1OUT_TRANSFER_TYPE USB_EPTYPE_BULK + * #define SLAB_USB_EP2IN_TRANSFER_TYPE USB_EPTYPE_INTR + * #define SLAB_USB_EP2OUT_TRANSFER_TYPE USB_EPTYPE_BULK + * #define SLAB_USB_EP3IN_TRANSFER_TYPE USB_EPTYPE_ISOC + * #define SLAB_USB_EP3OUT_TRANSFER_TYPE USB_EPTYPE_ISOC + * + * // ----------------------------------------------------------------------------- + * // Enable or disable callback functions + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_RESET_CB 1 + * #define SLAB_USB_SOF_CB 1 + * #define SLAB_USB_STATE_CHANGE_CB 1 + * #define SLAB_USB_IS_SELF_POWERED_CB 1 + * #define SLAB_USB_SETUP_CMD_CB 1 + * #define SLAB_USB_HANDLER_CB 0 + * + * // ----------------------------------------------------------------------------- + * // Specify number of languages supported by string descriptors + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_NUM_LANGUAGES 1 + * + * // ----------------------------------------------------------------------------- + * // If only one descriptor language is supported, specify that language here. + * // If multiple descriptor languages are supported, this value is ignored and + * // the supported languages must listed in the + * // myUsbStringTableLanguageIDsDescriptor structure. + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_LANGUAGE USB_LANGID_ENUS + * + * // ----------------------------------------------------------------------------- + * // Enable use of UTF-8 strings for string descriptors. + * // If this option is enabled then packed string descriptors that are created + * // with UTF8_PACKED_STATIC_CONST_STRING_DESC() can be UTF-8 encoded and they + * // will be decoded into UCS-2 16-bit wide character format used for USB string + * // descriptors. If this feature is not needed then it can be disabled to save + * // some code memory space. + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_UTF8_STRINGS 1 + * + * // ----------------------------------------------------------------------------- + * // Set the power saving mode + * // + * // SLAB_USB_PWRSAVE_MODE configures when the device will automatically enter + * // the USB power-save mode. It is a bitmask constant with bit values: + * // + * // USB_PWRSAVE_MODE_OFF - No energy saving mode selected + * // USB_PWRSAVE_MODE_ONSUSPEND - Enter USB power-save mode on USB suspend + * // USB_PWRSAVE_MODE_ONVBUSOFF - Enter USB power-save mode when not attached + * // to the USB host. + * // USB_PWRSAVE_MODE_FASTWAKE - Exit USB power-save mode more quickly, but + * // consume more power while in USB power-save + * // mode. + * // While the device is in USB power-save mode + * // (typically during USB suspend), the + * // internal voltage regulator stays in normal + * // power mode instead of entering suspend + * // power mode. + * // This is an advanced feature that may be + * // useful in certain applications that support + * // remote wakeup. + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONVBUSOFF \ + * | USB_PWRSAVE_MODE_ONSUSPEND) + * + * // ----------------------------------------------------------------------------- + * // Enable or disable polled mode + * // + * // When enabled, the application must call USBD_Run() periodically to process + * // USB events. + * // When disabled, USB events will be handled automatically by an interrupt + * // handler. + * // ----------------------------------------------------------------------------- + * #define SLAB_USB_POLLED_MODE 0 + * @endcode + * + * @n @section usb_device_powersave Energy-saving options + * + * The device stack provides built-in energy saving options.These options + * are configured by setting flags in @ref SLAB_USB_PWRSAVE_MODE in @em + * usbconfig.h. These flags are bitmasks and can be or'd together.@n@n + * + * Energy-Saving Option Flags: + * + * @ref USB_PWRSAVE_MODE_OFF@n The device will not automatically enter its + * low-power suspned mode after detecting a USB suspend. The application + * firmware may still call @ref USBD_Suspend() to manually enter suspend mode. + * + * @ref USB_PWRSAVE_MODE_ONSUSPEND@n Enter a low-power suspend mode + * when a USB suspend is detected. When resume signaling is detected, + * the stack will exit the low-power mode. + * + * @ref USB_PWRSAVE_MODE_ONVBUSOFF@n Enter the low-power suspend + * mode any time the device detects that VBUS is not present. When VBUS is + * attached, the stack will exit the low-power mode. The USB Specification + * does not define the state of the device when VBUS is not present, but it + * may be desirable for some applications to enter suspend mode when in this + * undefined state. + * + * @ref USB_PWRSAVE_MODE_FASTWAKE@n Keep the internal regulator at + * its normal operating state while in the low-power suspend state. This + * allows the device to wake from suspend more quickly than it would from its + * suspend state. This option can be useful in applications that support + * remote wakeup and need to exit suspend in time to recognize some external + * signal (i.e. a byte received on the UART). The device will still consume + * low enough power to meet the USB Suspend Current Specification, but it will + * be slightly higher than it would otherwise be. + * + * The USB HID Keyboard device example project demonstrates some of these + * energy-saving options. + * + * Example: + * Leave all energy saving to the stack, the device enters low-power mode on + * suspend and when detached from host. @n + * In usbconfig.h: + * @code + * #define SLAB_USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONSUSPEND | USB_PWRSAVE_MODE_ONVBUSOFF) + * @endcode + * + * @n @section usb_device_transfers Transfer Operation + * + * @subsection usb_device_transfers_overview Overview + * + * A USB transfer consists of one or more packets. For an IN transfer, the + * packets are sent from the device to the host. For an OUT transfer, the + * packets are sent from the host to the device. @ref USBD_Write() initiates + * an IN transfer, while @ref USBD_Read() initiates an OUT transfer. + * + * @subsection usb_device_transfers_types Transfer Types + * + * There are four USB transfer types: @ref usb_device_transfer_types_control, + * @ref usb_device_transfer_types_bulk, @ref usb_device_transfer_types_interrupt, + * and @ref usb_device_transfer_types_isochronous. + * + * @subsubsection usb_device_transfer_types_control Control + * + * Control transfers are used to send configuration and status + * information, and also to send vendor-defined data. The USB Library only + * supports control transfers on Endpoint 0. @n @n + * The application firmware can handle control requests by looking at the + * contents of the setup packet in @ref USBD_SetupCmdCb(). If the application + * supports a particular request, it can call @ref USBD_Read() or @ref + * USBD_Write() with epAddr set to EP0 and return @ref + * USB_STATUS_OK. If it does not need to handle the request, it should return + * @ref USB_STATUS_REQ_UNHANDLED. This will notify the library that it should + * try to handle the setup command. The library will automatically service + * Standard (i.e. Chapter 9) requests, so @ref USBD_SetupCmdCb() should return + * @ref USB_STATUS_REQ_UNHANDLED unless it is a class- or vendor-specific + * request. If neither the library nor the application supports a setup + * request, the library will issue a stall. + * + * @subsubsection usb_device_transfer_types_bulk Bulk + * + * Bulk transfers are used to send large, non-periodic data. Examples include + * sending a file to a Mass Storage Device or a print-job to a printer. A bulk + * transfer may consist of one or more packets. + * + * Endpoints are configured for bulk mode in usbconfig.h. As an + * example: @code + * #define SLAB_USB_EP1OUT_TRANSFER_TYPE USB_EPTYPE_BULK@endcode + * configures Endpout 1 OUT transfers for bulk mode. + * + * The @ref byteCount parameter of @ref USBD_Write() and @ref USBD_Read() + * configures the maximum length for a given bulk transfer. The transfer will + * complete when the device sends or receives either: + * 1. A packet less than its maximum packet size + * 2. Exactly the number of bytes specified in @ref byteCount + * Note that @ref USBD_XferCompleteCb() will be called for each packet sent or + * received for the duration of a transfer. + * + * @subsubsection usb_device_transfer_types_interrupt Interrupt + * + * Interrupt transfers are used to send low-bandwidth, hight-latency data at + * a non-periodic rate. Examples include input devices, such as mice, + * keyboards, and joysticks. An interrupt transfer may consist of one or more + * packets. + * + * Endpoints are configured for interrupt mode in usbconfig.h. As an + * example: @code + * #define SLAB_USB_EP1OUT_TRANSFER_TYPE USB_EPTYPE_INTR@endcode + * configures Endpout 1 OUT transfers for interrupt mode. + * + * Interrupt transfers work identically to bulk transfer in the USB Library. + * Refer to @ref usb_device_transfer_types_bulk for more information. + * + * @subsubsection usb_device_transfer_types_isochronous Isochronous + * + * Isochronous transfers are used to send periodic, continuous data. Automatic + * error-checking is not included with isochronous transfers as it is with all + * other transfer types. Examples include streaming audio and video. As + * isochronous data is sent at a continuous rate, it typically consists of + * one IN and/or OUT packet per frame. + * + * Endpoint 3 is the only endpoint in the USB Library that supports + * isochronous transfers. Endpoints are configured for isochronous mode in + * usbconfig.h. As an example: @code + * #define SLAB_USB_EP3OUT_TRANSFER_TYPE USB_EPTYPE_ISOC@endcode + * configures Endpout 3 OUT transfers for isochronous mode. + * + * The library works differently for isochronous transfers. The application + * must define a circular buffer to hold isochronous data. When calling + * USBD_Read() or USBD_Write(), dat is the first address of this + * buffer and byteCount is its length. The library will read from or + * write to this buffer as soon as the host issues a request, so it is the + * responsibility of the application firmware to ensure that this buffer is + * fed or consumed at the correct rate to prevent an underrun/overrun + * condition. + * + * The parameters of @ref USBD_XferCompleteCb() take on a different meaning in + * isochronous mode. For OUT transfers, xferred is the number of + * bute received in the last packet and remaining is the current + * index into the circular buffer. For IN transfers, xferred is + * ignored, remaining is the current index into the circular buffer, + * and the return value is the number of bytes to transmit in the next + * packet. + * + * @n @section usb_device_pitfalls Pitfalls + * + * @subsection usb_device_pitfalls_nonreentrancy Non-Reentrancy + * + * Due to the non-reentrant architecture of the 8051, it is recommended + * that all calls to a particular API function be made from functions of the + * same interrupt priority (main loop, low priority, or high priority). + * + * The interrupt priority of the USB callback functions is determined by the + * constant @ref SLAB_USB_POLLED_MODE. When 0, the callbacks are called from + * the USB Interrupt Handler. When 1, the callbacks are called from + * USBD_Run(), which is typically called from the main loop. + * If an API function must be called from functions of differing interrupt + * priorities, there are a number of ways to ensure that the calls are made + * safely: + * + * 1. Disable the interrupt source of the higher-priority function before + * making the call. Restore the interrupt enable setting after the call + * returns: + * + * (Assuming @htmlonly USBD_Write() is called from main() and + * USBD_XferCompleteCb() @endhtmlonly, the call from main() should + * disable and restore the USB interrupt): + * @code + * bool usbIntsEnabled = USB_GetIntsEnabled(); + * + * USB_DisableInts(); + * + * USBD_Write(EP1IN, myBuf, 1, true); + * + * if (usbIntsEnabled) + * { + * USB_EnableInts(); + * } + * @endcode + * + * 2. Add the compiler-specific reentrant keyword to the function + * definition(s) in efm8_usbd.c: + * @code + * int8_t USBD_AbortTransfer(uint8_t epAddr) reentrant + * @endcode + * and to the function prototype definition(s) in efm8_usb.h: + * @code + * int8_t USBD_AbortTransfer(uint8_t epAddr) reentrant; + * @endcode + * Using the reentrant keyword may require the application to provide + * a heap for local variable allocation. Additionally, it will reduce + * the performance and increase the code size of the modified function. + * 3. Make a copy of the function(s) and rename it. Call the original + * function in once context, and the renamed version in another. + * + * @subsection usb_device_pitfalls_buffer_allocation Buffer Allocation + * + * Dynamically allocated buffers passed to @ref USBD_Write() and @ref + * USBD_Read() must not be freed until the transfer completes. + * + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup efm8_usb_constants Constants + * @{ + ******************************************************************************/ + +// ----------------------------------------------------------------------------- +// Global Constants + +// SETUP request, direction of data stage +#define USB_SETUP_DIR_OUT 0 ///< Setup request data stage OUT direction value. +#define USB_SETUP_DIR_IN 1 ///< Setup request data stage IN direction value. +#define USB_SETUP_DIR_MASK 0x80 ///< Setup request data stage direction mask. +#define USB_SETUP_DIR_D2H 0x80 ///< Setup request data stage IN direction mask. +#define USB_SETUP_DIR_H2D 0x00 ///< Setup request data stage OUT direction mask. + +// SETUP request type +#define USB_SETUP_TYPE_STANDARD 0 ///< Standard setup request value. +#define USB_SETUP_TYPE_CLASS 1 ///< Class setup request value. +#define USB_SETUP_TYPE_VENDOR 2 ///< Vendor setup request value. +#define USB_SETUP_TYPE_STANDARD_MASK 0x00 ///< Standard setup request mask. +#define USB_SETUP_TYPE_CLASS_MASK 0x20 ///< Class setup request mask. +#define USB_SETUP_TYPE_VENDOR_MASK 0x40 ///< Vendor setup request mask. + +// SETUP request recipient +#define USB_SETUP_RECIPIENT_DEVICE 0 ///< Setup request device recipient value. +#define USB_SETUP_RECIPIENT_INTERFACE 1 ///< Setup request interface recipient value. +#define USB_SETUP_RECIPIENT_ENDPOINT 2 ///< Setup request endpoint recipient value. +#define USB_SETUP_RECIPIENT_OTHER 3 ///< Setup request other recipient value. + +// bmRequestType bitmasks +#define USB_BMREQUESTTYPE_RECIPIENT 0x1F ///< Recipient is bmRequestType[4:0] +#define USB_BMREQUESTTYPE_TYPE 0x60 ///< Type is bmRequestType[6:5] +#define USB_BMREQUESTTYPE_DIRECTION 0x80 ///< Recipient is bmRequestType[7] + +// SETUP standard request codes for Full Speed devices +#define GET_STATUS 0 ///< Standard setup request GET_STATUS. +#define CLEAR_FEATURE 1 ///< Standard setup request CLEAR_FEATURE. +#define SET_FEATURE 3 ///< Standard setup request SET_FEATURE. +#define SET_ADDRESS 5 ///< Standard setup request SET_ADDRESS. +#define GET_DESCRIPTOR 6 ///< Standard setup request GET_DESCRIPTOR. +#define SET_DESCRIPTOR 7 ///< Standard setup request SET_DESCRIPTOR. +#define GET_CONFIGURATION 8 ///< Standard setup request GET_CONFIGURATION. +#define SET_CONFIGURATION 9 ///< Standard setup request SET_CONFIGURATION. +#define GET_INTERFACE 10 ///< Standard setup request GET_INTERFACE. +#define SET_INTERFACE 11 ///< Standard setup request SET_INTERFACE. +#define SYNCH_FRAME 12 ///< Standard setup request SYNCH_FRAME. + +// SETUP class request codes +#define USB_HID_GET_REPORT 0x01 ///< HID class setup request GET_REPORT. +#define USB_HID_GET_IDLE 0x02 ///< HID class setup request GET_IDLE. +#define USB_HID_SET_REPORT 0x09 ///< HID class setup request SET_REPORT. +#define USB_HID_SET_IDLE 0x0A ///< HID class setup request SET_IDLE. +#define USB_HID_SET_PROTOCOL 0x0B ///< HID class setup request SET_PROTOCOL. +#define USB_CDC_SETLINECODING 0x20 ///< CDC class setup request SET_LINE_CODING. +#define USB_CDC_GETLINECODING 0x21 ///< CDC class setup request GET_LINE_CODING. +#define USB_CDC_SETCTRLLINESTATE 0x22 ///< CDC class setup request SET_CONTROL_LINE_STATE. +#define USB_MSD_BOTRESET 0xFF ///< MSD class setup request Bulk only transfer reset. +#define USB_MSD_GETMAXLUN 0xFE ///< MSD class setup request Get Max LUN. + +// SETUP command GET/SET_DESCRIPTOR descriptor types +#define USB_DEVICE_DESCRIPTOR 1 ///< DEVICE descriptor value. +#define USB_CONFIG_DESCRIPTOR 2 ///< CONFIGURATION descriptor value. +#define USB_STRING_DESCRIPTOR 3 ///< STRING descriptor value. +#define USB_INTERFACE_DESCRIPTOR 4 ///< INTERFACE descriptor value. +#define USB_ENDPOINT_DESCRIPTOR 5 ///< ENDPOINT descriptor value. +#define USB_DEVICE_QUALIFIER_DESCRIPTOR 6 ///< DEVICE_QUALIFIER descriptor value. +#define USB_OTHER_SPEED_CONFIG_DESCRIPTOR 7 ///< OTHER_SPEED_CONFIGURATION descriptor value. +#define USB_INTERFACE_POWER_DESCRIPTOR 8 ///< INTERFACE_POWER descriptor value. +#define USB_HUB_DESCRIPTOR 0x29 ///< HUB descriptor value. +#define USB_HID_DESCRIPTOR 0x21 ///< HID descriptor value. +#define USB_HID_REPORT_DESCRIPTOR 0x22 ///< HID REPORT descriptor value. +#define USB_CS_INTERFACE_DESCRIPTOR 0x24 ///< Audio Class-specific Descriptor Type. + +#define USB_DEVICE_DESCSIZE 18 ///< Device descriptor size. +#define USB_CONFIG_DESCSIZE 9 ///< Configuration descriptor size. +#define USB_INTERFACE_DESCSIZE 9 ///< Interface descriptor size. +#define USB_ENDPOINT_DESCSIZE 7 ///< Endpoint descriptor size. +#define USB_DEVICE_QUALIFIER_DESCSIZE 10 ///< Device qualifier descriptor size. +#define USB_OTHER_SPEED_CONFIG_DESCSIZE 9 ///< Device other speed configuration descriptor size. +#define USB_HID_DESCSIZE 9 ///< HID descriptor size. +#define USB_CDC_HEADER_FND_DESCSIZE 5 ///< CDC Header functional descriptor size. +#define USB_CDC_CALLMNG_FND_DESCSIZE 5 ///< CDC Call Management functional descriptor size. +#define USB_CDC_ACM_FND_DESCSIZE 4 ///< CDC Abstract Control Management functional descriptor size. + +// String descriptor locations +#define USB_STRING_DESCRIPTOR_ENCODING 0 ///< Denotes whether string descriptor is UTF-8 or binary +#define USB_STRING_DESCRIPTOR_LENGTH 1 ///< Length of string descriptor +#define USB_STRING_DESCRIPTOR_TYPE 2 ///< Type of string descriptor (USB_STRING_DESCRIPTOR) +#define USB_STRING_DESCRIPTOR_NAME 3 ///< The string encoded as per USB_STRING_DESCRIPTOR_PACKED + +// String descriptor encoding types +#define USB_STRING_DESCRIPTOR_UTF16LE 0 ///< The string is in UTF-16LE encoding +#define USB_STRING_DESCRIPTOR_UTF16LE_PACKED 1 ///< The string is in packed UTF-16LE encoding (the 0x00 + /// characters between ASCII characters are omitted) +#define USB_STRING_DESCRIPTOR_UTF8 2 ///< The string is in UTF-8 encoding + +// Misc. USB definitions +#define USB_FULL_EP0_SIZE 64 ///< The size of endpoint 0 at full speed. +#define USB_FULL_INT_BULK_MAX_EP_SIZE 64 ///< The max size of any full speed bulk/interrupt endpoint. +#define USB_FULL_ISOC_MAX_EP_SIZE 1023 ///< The max size of any full speed isochronous endpoint. +#define USB_LOW_EP0_SIZE 8 ///< The size of endpoint 0 at low speed. +#define USB_LOW_INT_BULK_MAX_EP_SIZE 8 ///< The max size of any low speed bulk/interrupt endpoint. +#define USB_LOW_ISOC_MAX_EP_SIZE 0 ///< The max size of any low speed isochronous endpoint. +#define USB_EPTYPE_CTRL 0 ///< Endpoint type control. +#define USB_EPTYPE_ISOC 1 ///< Endpoint type isochronous. +#define USB_EPTYPE_BULK 2 ///< Endpoint type bulk. +#define USB_EPTYPE_INTR 3 ///< Endpoint type interrupt. +#define USB_EP_DIR_IN 0x80 ///< Endpoint IN direction mask. +#define USB_EP_DIR_OUT 0x00 ///< Endponit OUT direction mask. +#define USB_SETUP_PKT_SIZE 8 ///< Setup request packet size. +#define USB_EPNUM_MASK 0x0F ///< Endpoint number mask. +#define USB_LANGID_ENUS 0x0409 ///< English-United States language id. +#define USB_LANGID_NOBO 0x0414 ///< Norwegian-Bokmal language id. +#define USB_MAX_DEVICE_ADDRESS 127 ///< Maximum allowable device address. +#define MAX_USB_EP_NUM 15 ///< Limit imposed by the USB standard +#define USB_VENDOR_ID_SILICON_LABS 0x10C4 ///< Silicon Labs VID + +#define CONFIG_DESC_BM_REMOTEWAKEUP 0x20 ///< Configuration descriptor attribute macro. +#define CONFIG_DESC_BM_SELFPOWERED 0x40 ///< Configuration descriptor attribute macro. +#define CONFIG_DESC_BM_RESERVED_D7 0x80 ///< Configuration descriptor attribute macro. +#define CONFIG_DESC_BM_TRANSFERTYPE 0x03 ///< Configuration descriptor transfer type bitmask. +#define CONFIG_DESC_MAXPOWER_mA(x) (((x)+1)/2) ///< Configuration descriptor power macro. + +#define DEVICE_IS_SELFPOWERED 0x0001 ///< Standard request GET_STATUS bitmask. +#define REMOTE_WAKEUP_ENABLED 0x0002 ///< Standard request GET_STATUS bitmask. +#define USB_FEATURE_ENDPOINT_HALT 0 ///< Standard request CLEAR/SET_FEATURE bitmask. +#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 ///< Standard request CLEAR/SET_FEATURE bitmask. + +#define HUB_FEATURE_PORT_RESET 4 ///< HUB class request CLEAR/SET_PORT_FEATURE feature selector. +#define HUB_FEATURE_PORT_POWER 8 ///< HUB class request CLEAR/SET_PORT_FEATURE feature selector. +#define HUB_FEATURE_C_PORT_CONNECTION 16 ///< HUB class request CLEAR/SET_PORT_FEATURE feature selector. +#define HUB_FEATURE_C_PORT_RESET 20 ///< HUB class request CLEAR/SET_PORT_FEATURE feature selector. +#define HUB_FEATURE_PORT_INDICATOR 22 ///< HUB class request CLEAR/SET_PORT_FEATURE feature selector. + +#define USB_CLASS_CDC 2 ///< CDC device/interface class code. +#define USB_CLASS_CDC_DATA 0x0A ///< CDC Data interface class code. +#define USB_CLASS_CDC_ACM 2 ///< CDC Abstract Control Model interface subclass code. +#define USB_CLASS_CDC_HFN 0 ///< CDC class Header Functional Descriptor subtype. +#define USB_CLASS_CDC_CMNGFN 1 ///< CDC class Call Management Functional Descriptor subtype. +#define USB_CLASS_CDC_ACMFN 2 ///< CDC class Abstract Control Management Functional Descriptor subtype. +#define USB_CLASS_CDC_UNIONFN 6 ///< CDC class Union Functional Descriptor subtype. + +#define USB_CLASS_HID 3 ///< HID device/interface class code. +#define USB_CLASS_HID_KEYBOARD 1 ///< HID keyboard interface protocol code. +#define USB_CLASS_HID_MOUSE 2 ///< HID mouse interface protocol code. + +#define USB_CLASS_HUB 9 ///< HUB device/interface class code. + +#define USB_CLASS_MSD 8 ///< MSD device/interface class code. +#define USB_CLASS_MSD_BOT_TRANSPORT 0x50 ///< MSD Bulk Only Transport protocol. +#define USB_CLASS_MSD_SCSI_CMDSET 6 ///< MSD Subclass SCSI transparent command set. +#define USB_CLASS_MSD_CSW_CMDPASSED 0 ///< MSD BOT Command status wrapper command passed code. +#define USB_CLASS_MSD_CSW_CMDFAILED 1 ///< MSD BOT Command status wrapper command failed code. +#define USB_CLASS_MSD_CSW_PHASEERROR 2 ///< MSD BOT Command status wrapper cmd phase error code. + +#define USB_CLASS_VENDOR_SPECIFIC 0xFF ///< Vendor Specific class +#define USB_SUBCLASS_VENDOR_SPECIFIC 0xFF ///< Vendor Specific sub-class + +/// @brief USB power save modes +#define USB_PWRSAVE_MODE_OFF 0 ///< No energy saving option selected. +#define USB_PWRSAVE_MODE_ONSUSPEND 1 ///< Enter USB power-save mode on suspend. +#define USB_PWRSAVE_MODE_ONVBUSOFF 2 ///< Enter USB power-save mode when not attached to the USB host. +#define USB_PWRSAVE_MODE_FASTWAKE 4 ///< Exit USB power-save mode more quickly. This is useful for + ///< some applications that support remote wakeup. + +/// @brief Endpoint 0 packet size +#if SLAB_USB_FULL_SPEED +#define USB_EP0_SIZE USB_FULL_EP0_SIZE +#else +#define USB_EP0_SIZE USB_LOW_EP0_SIZE +#endif // SLABS_USB_FULL_SPEED + +/// @brief Total number of USB endpoints used by the device +#define SLAB_USB_NUM_EPS_USED (SLAB_USB_EP1IN_USED \ + + SLAB_USB_EP1OUT_USED \ + + SLAB_USB_EP2IN_USED \ + + SLAB_USB_EP2OUT_USED \ + + SLAB_USB_EP3IN_USED \ + + SLAB_USB_EP3OUT_USED \ + + 1) +/** @} (end addtogroup efm8_usb_constants Constants) */ + +/***************************************************************************//** + * @addtogroup efm8_usb_macros Macros + * @{ + ******************************************************************************/ + +// ----------------------------------------------------------------------------- +// Global Macros + +/// Macro for getting minimum value. +#ifndef EFM8_MIN +#define EFM8_MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/// Macro for getting maximum value. +#ifndef EFM8_MAX +#define EFM8_MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef UNREFERENCED_ARGUMENT +#if defined __C51__ +/// Macro for removing unreferenced arguments from compiler warnings +#define UNREFERENCED_ARGUMENT(arg) (0, arg) +#elif defined __ICC8051__ +/// Macro for removing unreferenced arguments from compiler warnings +#define UNREFERENCED_ARGUMENT(arg) ((void)arg) +#endif +#endif + +/***************************************************************************//** + * @brief Macro for creating USB-compliant UTF-16LE UNICODE string + * descriptor from a C string. + * @details This macro should be used for ASCII strings in which all + * characters are represented by a single ASCII byte (i.e. + * U.S. English strings). + * The USB Library will expand variables created with this macro + * by inserting a 0x00 between each character. This allows the + * string to be stored in a "packed", or compressed, format. + * @n@n This example sends "Silicon Labs" as the Manufacturer String: + * + * #define MFR_STRING "Silicon Labs" + * + * UTF16LE_PACKED_STATIC_CONST_STRING_DESC(manufacturer[], \ + * MFR_STRING); + * @param __name + * The name of the variable that holds the string descriptor + * @param __val + * The value of the string descriptor + ******************************************************************************/ +#define UTF16LE_PACKED_STATIC_CONST_STRING_DESC(__name, __val, __size) \ + SI_SEGMENT_VARIABLE(__name, static const USB_StringDescriptor_TypeDef, SI_SEG_CODE) = \ + { USB_STRING_DESCRIPTOR_UTF16LE_PACKED, __size * 2, USB_STRING_DESCRIPTOR, __val } + +/***************************************************************************//** + * @brief Macro for creating USB-compliant UTF-16LE UNICODE string + * descriptor from a UTF-8 string. + * @details This macro should be used for UTF-8 strings in which all + * characters are represented by a valid UTF-8 byte sequence + * of 1-3 bytes per character. The USB library will expand + * variables created with this macro by decoding the UTF-8 + * sequence into 16-bit wide UCS-2 characters required for USB + * string descriptors. + * @n@n This example set an array named _manufacturer[]_ to a + * series of symbols: an anchor, a lightning bolt, and a + * fußball as the Manufacturer String: + * + * #define MFR_STRING "⚓⚡⚽" + * + * // This string has 3 Unicode characters so the __len + * // parameter is 3, even though it will take 3 bytes to + * // represent each character + * UTF8_PACKED_STATIC_CONST_STRING_DESC(manufacturer[], 3, \ + * MFR_STRING); + * @param __name + * The name of the variable that holds the string descriptor + * @param __len + * Number of Unicode characters (or codepoints) in the string + * @param __val + * The value of the string descriptor + ******************************************************************************/ +#define UTF8_PACKED_STATIC_CONST_STRING_DESC(__name, __len, __val) \ + SI_SEGMENT_VARIABLE(__name, static const USB_StringDescriptor_TypeDef, SI_SEG_CODE) = \ + { USB_STRING_DESCRIPTOR_UTF8, (__len) * 2, USB_STRING_DESCRIPTOR, __val } + +/***************************************************************************//** + * @brief Macro for creating USB-compliant UTF-16LE UNICODE string + * descriptor from a UTF-8 string. + * @details This macro should be used for UTF-8 strings in which all + * characters are represented by a valid UTF-8 byte sequence + * of 1-3 bytes per character. The USB library will expand + * variables created with this macro by decoding the UTF-8 + * sequence into 16-bit wide UCS-2 characters required for USB + * string descriptors. + * @n@n This example set an array named _manufacturer[]_ to a + * series of symbols: an anchor, a lightning bolt, and a + * fußball as the Manufacturer String: + * + * #define MFR_STRING "⚓⚡⚽" + * + * // This string has 3 Unicode characters so the __len + * // parameter is 3, even though it will take 3 bytes to + * // represent each character + * UTF8_PACKED_STATIC_CONST_STRING_DESC(manufacturer[], 3, \ + * MFR_STRING); + * @param __name + * The name of the variable that holds the string descriptor + * @param __len + * Number of Unicode characters (or codepoints) in the string + * @param __val + * The value of the string descriptor + ******************************************************************************/ +#define UTF8_PACKED_STATIC_CONST_STRING_DESC(__name, __len, __val) \ + SI_SEGMENT_VARIABLE(__name, static const USB_StringDescriptor_TypeDef, SI_SEG_CODE) = \ + { USB_STRING_DESCRIPTOR_UTF8, (__len) * 2, USB_STRING_DESCRIPTOR, __val } + +/***************************************************************************//** + * @brief Macro for creating USB-compliant UTF-16LE UNICODE string + * descriptor from a C array initializer. + * @details This macro should be used for converting an array initializer + * into a string descriptor. Unlike @ref + * UTF16LE_PACKED_STATIC_CONST_STRING_DESC(), the library will not + * attempt to unpack variables created with this macro, so the + * array will be sent exactly as it is defined. + * @n@n This example sends "Mouse" as the Product String: + * + * #define PROD_STRING 'M',0,'o',0,'u',0,'s',0,'e',0 + * + * ARRAY_STATIC_CONST_STRING_DESC(product[], \ + * 10, \ + * PROD_STRING); + * @param __name + * The name of the variable that holds the string descriptor + * @param __len + * Number of characters (including nulls) in the string to send + * @param __val + * The array initializer. + ******************************************************************************/ +#define ARRAY_STATIC_CONST_STRING_DESC(__name, __len, __val) \ + SI_SEGMENT_VARIABLE(__name, static const USB_StringDescriptor_TypeDef, SI_SEG_CODE) = \ + { USB_STRING_DESCRIPTOR_UTF16LE, __len + 2, USB_STRING_DESCRIPTOR, __val } + +/***************************************************************************//** + * @brief Macro for creating USB-compliant UTF-16LE UNICODE string + * descriptor from a UTF-16 C string. + * @details This macro should be used for strings which are already + * represented in UTF-16. This is an advanced option that should + * only be used for some foreign languages. + * @param __name + * The name of the variable that holds the string descriptor + * @param __val + * The value of the string descriptor + ******************************************************************************/ +#define UTF16LE_STATIC_CONST_STRING_DESC(__name, __val) \ + SI_SEGMENT_VARIABLE(__name, static const USB_StringDescriptor_TypeDef, SI_SEG_CODE) = \ + { USB_STRING_DESCRIPTOR_UTF16LE, sizeof(__val) + 2, USB_STRING_DESCRIPTOR, __val } + +/***************************************************************************//** + * @brief Macro for creating Language ID's String Descriptor (String + * Descriptor 0) + * @details This macro should be used to create the Language ID String + * Descriptor. + * @n@n This example USB device only support U.S. English: + * + * #define LANG_STRING htole16(SLAB_USB_LANGUAGE) + * + * LANGID_STATIC_CONST_STRING_DESC(langDesc[], LANG_STRING); + * + * This example USB device support Norwegian U.S. English: + * + * #define LANG_STRING htole16(USB_LANGID_NOBO), \ + * htole16(USB_LANGID_ENUS) + * + * LANGID_STATIC_CONST_STRING_DESC(langDesc[], LANG_STRING); + * + * @param __name + * The name of the variable that holds the string descriptor + * @param __val + * The value of the string descriptor + ******************************************************************************/ +#define LANGID_STATIC_CONST_STRING_DESC(__name, __val) \ + SI_SEGMENT_VARIABLE(__name, static const USB_LangId_StringDescriptor_Typedef, SI_SEG_CODE) = \ + { htole16(((SLAB_USB_NUM_LANGUAGES * 2) + 2) + (USB_STRING_DESCRIPTOR << 8)), __val } + +/** @} (end addtogroup efm8_usb_macros Macros) */ + +/***************************************************************************//** + * @addtogroup efm8_usb_typedefs Typedefs + * @{ + ******************************************************************************/ + +// ----------------------------------------------------------------------------- +// Typedefs + +/// @brief USB transfer status enumerator. +typedef enum +{ + USB_STATUS_OK = 0, ///< No errors detected. + USB_STATUS_REQ_ERR = -1, ///< Setup request error. + USB_STATUS_EP_BUSY = -2, ///< Endpoint is busy. + USB_STATUS_REQ_UNHANDLED = -3, ///< Setup request not handled. + USB_STATUS_ILLEGAL = -4, ///< Illegal operation attempted. + USB_STATUS_EP_STALLED = -5, ///< Endpoint is stalled. + USB_STATUS_EP_ABORTED = -6, ///< Endpoint transfer was aborted. + USB_STATUS_EP_ERROR = -7, ///< Endpoint transfer error. + USB_STATUS_EP_NAK = -8, ///< Endpoint NAK'ed transfer request. + USB_STATUS_DEVICE_UNCONFIGURED = -9, ///< Device is unconfigured. + USB_STATUS_DEVICE_SUSPENDED = -10, ///< Device is suspended. + USB_STATUS_DEVICE_RESET = -11, ///< Device is/was reset. + USB_STATUS_TIMEOUT = -12, ///< Transfer timeout. + USB_STATUS_DEVICE_REMOVED = -13, ///< Device was removed. + USB_STATUS_EP_RX_BUFFER_OVERRUN = -14, ///< Not enough data in the Rx buffer to hold the + USB_STATUS_DATA_ERROR = -15, ///< OUT packet had CRC or bit-stuffing error + ///< last received packet +} USB_Status_TypeDef; + +/// @brief USB device state enumerator. +typedef enum +{ + USBD_STATE_NONE = 0, ///< Device state is undefined/unknown. + USBD_STATE_ATTACHED = 1, ///< Device state is ATTACHED. + USBD_STATE_POWERED = 2, ///< Device state is POWERED. + USBD_STATE_DEFAULT = 3, ///< Device state is DEFAULT. + USBD_STATE_ADDRESSED = 4, ///< Device state is ADDRESSED. + USBD_STATE_SUSPENDED = 5, ///< Device state is SUSPENDED. + USBD_STATE_CONFIGURED = 6, ///< Device state is CONFIGURED. + USBD_STATE_LASTMARKER = 7, ///< Device state enum end marker. +} USBD_State_TypeDef; + +/// @cond DO_NOT_INCLUDE_WITH_DOXYGEN +/// @brief Endpoint states +typedef enum +{ + D_EP_DISABLED = 0, ///< Endpoint is disabled + D_EP_IDLE = 1, ///< Endpoint is idle + D_EP_TRANSMITTING = 2, ///< Endpoint is transmitting data + D_EP_RECEIVING = 3, ///< Endpoint is receiving data + D_EP_STATUS = 4, ///< Endpoint is in status stage + D_EP_STALL = 5, ///< Endpoint is stalling + D_EP_HALT = 6, ///< Endpoint is halted + D_EP_LASTMARKER = 7 ///< End of EpState enum +} USBD_EpState_TypeDef; +/// @endcond DO_NOT_INCLUDE_WITH_DOXYGEN + +/// @brief Endpoint access address +typedef enum +{ + EP0, +#if (SLAB_USB_EP1IN_USED) + EP1IN, +#endif +#if (SLAB_USB_EP2IN_USED) + EP2IN, +#endif +#if (SLAB_USB_EP3IN_USED) + EP3IN, +#endif +#if (SLAB_USB_EP1OUT_USED) + EP1OUT, +#endif +#if (SLAB_USB_EP2OUT_USED) + EP2OUT, +#endif +#if (SLAB_USB_EP3OUT_USED) + EP3OUT, +#endif +} USB_EP_Index_TypeDef; + +/// @brief USB Setup type. +typedef struct +{ + struct + { + uint8_t Recipient : 5; ///< Request recipient (device, interface, endpoint, other) + uint8_t Type : 2; ///< Request type (standard, class or vendor). + uint8_t Direction : 1; ///< Transfer direction of SETUP data phase. + } bmRequestType; + + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} USB_Setup_TypeDef; + +/// @brief USB Setup Union type. +typedef union +{ + USB_Setup_TypeDef setup; + uint8_t c[8]; + uint16_t i[4]; +} USB_Setup_UnionDef; + +/// @brief USB Device Descriptor. +typedef struct +{ + uint8_t bLength; ///< Size of this descriptor in bytes + uint8_t bDescriptorType; ///< Constant DEVICE Descriptor Type + uint16_t bcdUSB; ///< USB Specification Release Number in BCD + uint8_t bDeviceClass; ///< Class code (assigned by the USB-IF) + uint8_t bDeviceSubClass; ///< Subclass code (assigned by the USB-IF) + uint8_t bDeviceProtocol; ///< Protocol code (assigned by the USB-IF) + uint8_t bMaxPacketSize0; ///< Maximum packet size for endpoint zero + uint16_t idVendor; ///< Vendor ID (assigned by the USB-IF) + uint16_t idProduct; ///< Product ID (assigned by the manufacturer) + uint16_t bcdDevice; ///< Device release number in binary-coded decimal + uint8_t iManufacturer; ///< Index of string descriptor describing manufacturer + uint8_t iProduct; ///< Index of string descriptor describing product + uint8_t iSerialNumber; ///< Index of string descriptor describing the serial number + uint8_t bNumConfigurations; ///< Number of possible configurations +} USB_DeviceDescriptor_TypeDef; + + +/// @brief USB Configuration Descriptor. +typedef struct +{ + uint8_t bLength; ///< Size of this descriptor in bytes + uint8_t bDescriptorType; ///< Constant CONFIGURATION Descriptor Type + uint16_t wTotalLength; ///< Total length of data returned for this + ///< configuration. Includes the combined length of all + ///< descriptors (configuration, interface, endpoint, + ///< and class- or vendor-specific) returned for this + ///< configuration. + uint8_t bNumInterfaces; ///< Number of interfaces supported by this + ///< configuration + uint8_t bConfigurationValue; ///< Value to use as an argument to the + ///< SetConfiguration request to select this + ///< configuration. + uint8_t iConfiguration; ///< Index of string descriptor describing this + ///< configuration. + uint8_t bmAttributes; ///< Configuration characteristics. + ///< @n D7: Reserved (set to one) + ///< @n D6: Self-powered + ///< @n D5: Remote Wakeup + ///< @n D4...0: Reserved (reset to zero) + uint8_t bMaxPower; ///< Maximum power consumption of the USB device, unit + ///< is 2mA per LSB +} USB_ConfigurationDescriptor_TypeDef; + + +/// @brief USB Interface Descriptor. +typedef struct +{ + uint8_t bLength; ///< Size of this descriptor in bytes. + uint8_t bDescriptorType; ///< Constant INTERFACE Descriptor Type. + uint8_t bInterfaceNumber; ///< Number of this interface. Zero-based value + ///< identifying the index in the array of concurrent + ///< interfaces supported by this configuration. + uint8_t bAlternateSetting; ///< Value used to select this alternate setting for + ///< the interface identified in the prior field. + uint8_t bNumEndpoints; ///< Number of endpoints used by this interface + ///< (excluding endpoint zero). If this value is zero, + ///< this interface only uses the Default Control Pipe. + uint8_t bInterfaceClass; ///< Class code (assigned by the USB-IF). A value + ///< of zero is reserved for future standardization. If + ///< this field is set to FFH, the interface class is + ///< vendor-specific. All other values are reserved for + ///< assignment by the USB-IF. + uint8_t bInterfaceSubClass; ///< Subclass code (assigned by the USB-IF). These codes + ///< are qualified by the value of the bInterfaceClass + ///< field. If the bInterfaceClass field is reset to + ///< zero, this field must also be reset to zero. If + ///< the bInterfaceClass field is not set to FFH, all + ///< values are reserved for assignment by the USB-IF. + uint8_t bInterfaceProtocol; ///< Protocol code (assigned by the USB). These codes + ///< are qualified by the value of the bInterfaceClass + ///< and the bInterfaceSubClass fields. If an interface + ///< supports class-specific requests, this code + ///< identifies the protocols that the device uses as + ///< defined by the specification of the device class. + ///< If this field is reset to zero, the device does + ///< not use a class-specific protocol on this + ///< interface. If this field is set to FFH, the device + ///< uses a vendor-specific protocol for this interface + uint8_t iInterface; ///< Index of string descriptor describing this + ///< interface. +} USB_InterfaceDescriptor_TypeDef; + + +/// @brief USB Endpoint Descriptor. +typedef struct +{ + uint8_t bLength; ///< Size of this descriptor in bytes + uint8_t bDescriptorType; ///< Constant ENDPOINT Descriptor Type + uint8_t bEndpointAddress; ///< The address of the endpoint + uint8_t bmAttributes; ///< This field describes the endpoint attributes + uint16_t wMaxPacketSize; ///< Maximum packet size for the endpoint + uint8_t bInterval; ///< Interval for polling EP for data transfers +} USB_EndpointDescriptor_TypeDef; + +/// @brief USB String Descriptor. +typedef uint8_t USB_StringDescriptor_TypeDef; ///< The string descriptor + +/// @brief USB Language ID String Descriptor. +typedef uint16_t USB_LangId_StringDescriptor_Typedef; ///< The language ID string descriptor + +#if (SLAB_USB_NUM_LANGUAGES == 1) +/// @brief USB String Table Structure. +typedef SI_VARIABLE_SEGMENT_POINTER(, USB_StringDescriptor_TypeDef, SI_SEG_GENERIC) USB_StringTable_TypeDef; +#elif (SLAB_USB_NUM_LANGUAGES > 1) +typedef struct +{ + uint16_t *languageIDs; + USB_StringDescriptor_TypeDef * * *languageArray; +} USB_StringTable_TypeDef; +#endif // ( SLAB_USB_NUM_LANGUAGES == 1 ) + +/// @brief USB Device stack initialization structure. +/// @details This structure is passed to @ref USBD_Init() when starting up +/// the device. +typedef struct +{ + SI_VARIABLE_SEGMENT_POINTER(deviceDescriptor, USB_DeviceDescriptor_TypeDef, SI_SEG_GENERIC); ///< Pointer to the device descriptor + SI_VARIABLE_SEGMENT_POINTER(configDescriptor, USB_ConfigurationDescriptor_TypeDef, SI_SEG_GENERIC); ///< Pointer to the configuration descriptor + SI_VARIABLE_SEGMENT_POINTER(stringDescriptors, USB_StringTable_TypeDef, SI_SEG_GENERIC); ///< Pointer to an array of string descriptor pointers + uint8_t numberOfStrings; ///< Number of strings in string descriptor array +} USBD_Init_TypeDef; + +/// @cond DO_NOT_INCLUDE_WITH_DOXYGEN +// Endpoint structure +typedef struct +{ + SI_VARIABLE_SEGMENT_POINTER(buf, uint8_t, SI_SEG_GENERIC); + uint16_t remaining; + USBD_EpState_TypeDef state; + union + { + struct + { + uint8_t callback : 1; + uint8_t outPacketPending : 1; + uint8_t inPacketPending : 1; + uint8_t waitForRead : 1; + } bits; + uint8_t c; + } misc; +} USBD_Ep_TypeDef; + +// USB Device structure +typedef struct +{ + uint8_t configurationValue; +#if SLAB_USB_REMOTE_WAKEUP_ENABLED + uint8_t remoteWakeupEnabled; +#endif + uint8_t numberOfStrings; + USBD_State_TypeDef state; + USBD_State_TypeDef savedState; + USB_Setup_TypeDef setup; + union + { + struct + { + uint8_t type : 7; + uint8_t init : 1; + } encoding; + uint8_t c; + } ep0String; + USBD_Ep_TypeDef ep0; +#if SLAB_USB_EP1IN_USED + USBD_Ep_TypeDef ep1in; +#endif +#if SLAB_USB_EP2IN_USED + USBD_Ep_TypeDef ep2in; +#endif +#if SLAB_USB_EP3IN_USED + USBD_Ep_TypeDef ep3in; +#endif +#if SLAB_USB_EP1OUT_USED + USBD_Ep_TypeDef ep1out; +#endif +#if SLAB_USB_EP2OUT_USED + USBD_Ep_TypeDef ep2out; +#endif +#if SLAB_USB_EP3OUT_USED + USBD_Ep_TypeDef ep3out; +#endif +#if ((SLAB_USB_EP3IN_USED) && (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC)) + uint16_t ep3inIsoIdx; +#endif +#if ((SLAB_USB_EP3OUT_USED) && (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC)) + uint16_t ep3outIsoIdx; +#endif +#if SLAB_USB_SUPPORT_ALT_INTERFACES + uint8_t interfaceAltSetting[SLAB_USB_NUM_INTERFACES]; +#endif + SI_VARIABLE_SEGMENT_POINTER(deviceDescriptor, USB_DeviceDescriptor_TypeDef, SI_SEG_GENERIC); + SI_VARIABLE_SEGMENT_POINTER(configDescriptor, USB_ConfigurationDescriptor_TypeDef, SI_SEG_GENERIC); + SI_VARIABLE_SEGMENT_POINTER(stringDescriptors, USB_StringTable_TypeDef, SI_SEG_GENERIC); +} USBD_Device_TypeDef; +/// @endcond DO_NOT_INCLUDE_WITH_DOXYGEN + +/** @} (end addtogroup efm8_usb_typedefs Typedefs) */ + +/***************************************************************************//** + * @addtogroup efm8_usb_constants Constants + * @{ + ******************************************************************************/ + +// ----------------------------------------------------------------------------- +// Compiler-specific memory segment definitions + +#ifndef MEM_MODEL_SEG + +// ----------------------------------------------------------------------------- +// Memory Model-Specific Location +// +// MEM_MODEL_SEG is the default memory segment used for a given +// memory model. Some variables use this symbol in order to reduce the amount +// of code space and number of cycles it takes to access them. +// The user can override this value by defining it in his usbconfig.h file. +// For example: +// +// #define MEM_MODEL_LOC SI_SEG_XDATA +// +// will place these variables in XRAM regardless of the memory model used to +// build the project. +// ----------------------------------------------------------------------------- + +#if (defined SDCC) || (defined __SDCC) + +#if (__SDCC_MODEL_SMALL) +#define MEM_MODEL_SEG SI_SEG_IDATA +#elif defined __SDCC_MODEL_MEDIUM +#define MEM_MODEL_SEG SI_SEG_PDATA +#elif (defined __SDCC_MODEL_LARGE) || (defined __SDCC_MODEL_HUGE) +#define MEM_MODEL_SEG SI_SEG_XDATA +#else +#error "Illegal memory model setting." +#endif + +#elif defined __RC51__ + +#if (__MEMORY_MODEL__ == 0) // TINY +#define MEM_MODEL_SEG SI_SEG_IDATA +#elif (__MEMORY_MODEL__ == 1) // SMALL +#define MEM_MODEL_SEG SI_SEG_IDATA +#elif (__MEMORY_MODEL__ == 2) // COMPACT +#define MEM_MODEL_SEG SI_SEG_PDATA +#elif (__MEMORY_MODEL__ == 3) // LARGE +#define MEM_MODEL_SEG SI_SEG_XDATA +#elif (__MEMORY_MODEL__ == 4) // HUGE +#define MEM_MODEL_SEG SI_SEG_PDATA +#else +#error "Illegal memory model setting." +#endif + +#elif defined __C51__ + +#if (__MODEL__ == 0) // SMALL +#define MEM_MODEL_SEG SI_SEG_IDATA +#elif (__MODEL__ == 1) // COMPACT +#define MEM_MODEL_SEG SI_SEG_PDATA +#elif (__MODEL__ == 2) // LARGE +#define MEM_MODEL_SEG SI_SEG_XDATA +#else +#error "Illegal memory model setting." +#endif + +#elif defined _CC51 + +#if (_MODEL == 's') // SMALL +#define MEM_MODEL_SEG SI_SEG_IDATA +#elif (_MODEL == 'a') // AUXPAGE +#define MEM_MODEL_SEG SI_SEG_PDATA +#elif (_MODEL == 'l') // LARGE +#define MEM_MODEL_SEG SI_SEG_XDATA +#elif (_MODEL == 'r') // REENTRANT +#define MEM_MODEL_SEG SI_SEG_XDATA +#else +#error "Illegal memory model setting." +#endif + +#elif defined __ICC8051__ +#if (__DATA_MODEL__ == 0) // TINY +#define MEM_MODEL_SEG SI_SEG_IDATA +#elif (__DATA_MODEL__ == 1) // SMALL +#define MEM_MODEL_SEG SI_SEG_IDATA +#elif (__DATA_MODEL__ == 2) // LARGE +#define MEM_MODEL_SEG SI_SEG_XDATA +#elif (__DATA_MODEL__ == 3) // GENERIC +#define MEM_MODEL_SEG SI_SEG_XDATA +#elif (__DATA_MODEL__ == 4) // FAR +#define MEM_MODEL_SEG SI_SEG_XDATA +#else +#error "Illegal memory model setting." +#endif + +#endif +#endif // #ifndef MEM_MODEL_SEG + +/** @} (end addtogroup efm8_usb_constants Constants) */ + +/// @cond DO_NOT_INCLUDE_WITH_DOXYGEN +void USBD_SetUsbState(USBD_State_TypeDef newState); +USB_Status_TypeDef USBDCH9_SetupCmd(void); +/// @endcond DO_NOT_INCLUDE_WITH_DOXYGEN + +// ----------------------------------------------------------------------------- +// Library Configuration Definitions + +/**************************************************************************//** + * @addtogroup efm8_usb_config Library Configuration + * @{ + * + * @details Library configuration constants read from usbconfig.h. + * + * This library will look for configuration constants in **usbconfig.h**. + * This file is provided/written by the user and should be + * located in a directory that is part of the include path. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_BUS_POWERED + * @brief Configures the USB device for bus-powered or self-powered mode. + * @details + * When '1' the USB device is bus-powered. + * When '0' the USB device is self-powered. + * + * Default setting is '1' and may be overridden by defining in 'usbconfig.h'. + * + * @note The EFM8UB1, EFM8UB3, and EFM8UB4 devices can be configured to ignore + * the voltage on the VBUS pin and to instead use that pin as GPIO. If + * this feature is used, SLAB_USB_BUS_POWERED should be set to '1' even if + * the device is not drawing its power from the VBUS line. + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_FULL_SPEED + * @brief Configures the USB device for full-speed or low-speed operation. + * @details + * When '1' the USB device is full-speed + * When '0' the USB device is low-speed + * + * Default setting is '1' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_CLOCK_RECOVERY_ENABLED + * @brief Enables/disables the USB Clock Recovery + * @details USB Clock Recovery uses the incoming USB dat stream to adjust the + * internal oscillator. This allows the internal oscillator to meet the + * requirements for USB clock tolerance. + * + * When '1' the USB clock recovery is enabled + * When '0' the USB clock recovery is disabled + * + * Default setting is '1' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_REMOTE_WAKEUP_ENABLED + * @brief Enables/disables remote wakeup capability + * @details Remote wakeup allow the USB device to wake the host from suspend. + * When enabled, the library will call @ref USBD_RemoteWakeupCb() to determine + * if the remote wakeup source caused the device to wake up. If it did, the + * library will exit suspend mode and call @ref USBD_RemoteWakeup() to wake + * up the host. + * + * When '1' remote wakeup is enabled + * When '0' remote wakeup is disabled + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_NUM_INTERFACES + * @brief The number of interfaces available in the configuration + * @details + * Default setting is '1' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_SUPPORT_ALT_INTERFACES + * @brief Enables/disables alternate interface settings + * @details If any of the interfaces support alternate settings, this should be + * set to '1'. Upon receiveing a SET_INTERFACE request, the library will call + * @ref USBD_SetInterfaceCb(), which should return @ref USB_STATUS_OK if the + * alternate setting is valid or @ref USB_STATUS_REQ_ERR if it is not. + * + * When '1' alternate inteface settings are supported + * When '0' alternate interface settings are not supported, and the library will + * respond to any SET_INTERFACE request with a procedural stall + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP1IN_USED + * @brief Enables/disables Endpoint 1 IN + * @details + * When '1' Endpoint 1 IN is enabled + * When '0' Endpoint 1 IN is disabled + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP1OUT_USED + * @brief Enables/disables Endpoint 1 OUT + * @details + * When '1' Endpoint 1 OUT is enabled + * When '0' Endpoint 1 OUT is disabled + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP2IN_USED + * @brief Enables/disables Endpoint 2 IN + * @details + * When '1' Endpoint 2 IN is enabled + * When '0' Endpoint 2 IN is disabled + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP2OUT_USED + * @brief Enables/disables Endpoint 2 OUT + * @details + * When '1' Endpoint 2 OUT is enabled + * When '0' Endpoint 2 OUT is disabled + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP3IN_USED + * @brief Enables/disables Endpoint 3 IN + * @details + * When '1' Endpoint 3 IN is enabled + * When '0' Endpoint 3 IN is disabled + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP3OUT_USED + * @brief Enables/disables Endpoint 3 OUT + * @details + * When '1' Endpoint 3 OUT is enabled + * When '0' Endpoint 3 OUT is disabled + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP1IN_MAX_PACKET_SIZE + * @brief The maximum packet size that can be received on Endpoint 1 IN + * @details + * Default setting is '64' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP1OUT_MAX_PACKET_SIZE + * @brief The maximum packet size that can be transmitted on Endpoint 1 OUT + * @details + * Default setting is '64' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP2IN_MAX_PACKET_SIZE + * @brief The maximum packet size that can be received on Endpoint 2 IN + * @details + * Default setting is '64' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP2OUT_MAX_PACKET_SIZE + * @brief The maximum packet size that can be transmitted on Endpoint 2 OUT + * @details + * Default setting is '64' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP3IN_MAX_PACKET_SIZE + * @brief The maximum packet size that can be received on Endpoint 3 IN + * @details + * Default setting is '64' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP3OUT_MAX_PACKET_SIZE + * @brief The maximum packet size that can be transmitted on Endpoint 3 OUT + * @details + * Default setting is '64' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP1IN_TRANSFER_TYPE + * @brief Transfer type on Endpoint 1 IN + * @details + * May take one of the following values: + * USB_EPTYPE_INTR - Interrupt + * USB_EPTYPE_BULK - Bulk + * + * Default setting is @ref USB_EPTYPE_INTR and may be overridden by defining in + * 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP1OUT_TRANSFER_TYPE + * @brief Transfer type on Endpoint 1 OUT + * @details + * May take one of the following values: + * USB_EPTYPE_INTR - Interrupt + * USB_EPTYPE_BULK - Bulk + * + * Default setting is @ref USB_EPTYPE_INTR and may be overridden by defining in + * 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP2IN_TRANSFER_TYPE + * @brief Transfer type on Endpoint 2 IN + * @details + * May take one of the following values: + * USB_EPTYPE_INTR - Interrupt + * USB_EPTYPE_BULK - Bulk + * + * Default setting is @ref USB_EPTYPE_INTR and may be overridden by defining in + * 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP2OUT_TRANSFER_TYPE + * @brief Transfer type on Endpoint 2 OUT + * @details + * May take one of the following values: + * USB_EPTYPE_INTR - Interrupt + * USB_EPTYPE_BULK - Bulk + * + * Default setting is @ref USB_EPTYPE_INTR and may be overridden by defining in + * 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP3IN_TRANSFER_TYPE + * @brief Transfer type on Endpoint 3 IN + * @details + * May take one of the following values: + * USB_EPTYPE_INTR - Interrupt + * USB_EPTYPE_BULK - Bulk + * USB_EPTYPE_ISOC - Isochronous + * + * Default setting is @ref USB_EPTYPE_INTR and may be overridden by defining in + * 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_EP3OUT_TRANSFER_TYPE + * @brief Transfer type on Endpoint 3 OUT + * @details + * May take one of the following values: + * USB_EPTYPE_INTR - Interrupt + * USB_EPTYPE_BULK - Bulk + * USB_EPTYPE_ISOC - Isochronous + * + * Default setting is @ref USB_EPTYPE_INTR and may be overridden by defining in + * 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_RESET_CB + * @brief Enables/disables the USB Reset callback function + * @details + * When '1' @ref USBD_ResetCb() is called upon reception of a USB Reset + * When '0' @ref USBD_ResetCb() is not called upon reception of a USB Reset + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_SOF_CB + * @brief Enables/disables the USB Start-Of-Frame callback function + * @details + * When '1' @ref USBD_SofCb() is called upon reception of a Start-of-Frame + * packet + * When '0' @ref USBD_SofCb() is not called upon reception of a Start-of-Frame + * packet + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_STATE_CHANGE_CB + * @brief Enables/disables the USB State Change callback function + * @details + * When '1' @ref USBD_DeviceStateChangeCb() is called when the USB device state + * changes + * When '0' @ref USBD_DeviceStateChangeCb() is not called when the USB device + * state changes + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_IS_SELF_POWERED_CB + * @brief Enables/disables the USB Self-Powered callback function + * @details + * When '1' @ref USBD_IsSelfPoweredCb() is called upon reception of a + * GET_STATUS (Self-Powered) request + * When '0' @ref USBD_IsSelfPoweredCb() is not called upon reception of a + * GET_STATUS (Self-Powered) request + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_SETUP_CMD_CB + * @brief Enables/disables the USB Setup Command callback function + * @details + * When '1' @ref USBD_SetupCmdCb() is called upon reception of a Setup Request + * When '0' @ref USBD_SetupCmdCb() is not called upon reception of a Setup + * Request + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_HANDLER_CB + * @brief Enables/disables the USB Handler Entry and Exit callback functions + * @details + * When '1' @ref USBD_EnterHandler() will be called before the USB handler + * executes and @ref USBD_ExitHandler() will be called after the USB handler + * completes + * When '0' @ref USBD_EnterHandler() and @ref USBD_ExitHandler() will not be + * called + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_NUM_LANGUAGES + * @brief Number of languages supported by the USB string descriptors + * @details + * Default setting is '1' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_LANGUAGE + * @brief + * Defines the language of the USB string descriptors when + * @ref SLAB_USB_NUM_LANGUAGES is '1'. + * + * @details + * When @ref SLAB_USB_NUM_LANGUAGES is greater than '1', the supported languages + * must be defined in a separate table, and a structure of type + * @ref USB_StringDescriptor_TypeDef must be defined for each supported + * language + * + * Default setting is @ref USB_LANGID_ENUS and may be overridden by defining in + * 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_UTF8_STRINGS + * @brief + * Enables UTF-8 string decoding for USB string descriptors that are created + * using @ref UTF8_PACKED_STATIC_CONST_STRING_DESC. + * + * @details + * If this option is enabled, USB descriptor strings that are created using + * @ref UTF8_PACKED_STATIC_CONST_STRING_DESC can be encoded as UTF-8 which + * allows for Unicode characters (up to 16-bits wide) to be used for USB + * string descriptors. The UTF-8 strings will be decoded into UCS-2 16-bit + * wide character format required by USB. If this feature is not needed then + * this option can be disabled to save code memory space. If this option is + * disabled, then @ref UTF8_PACKED_STATIC_CONST_STRING_DESC should not be used. + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_PWRSAVE_MODE + * @brief Configures the power-saving options supported by the device + * + * @details + * Default setting is @ref USB_PWRSAVE_MODE_ONSUSPEND and may be overridden by + * defining in 'usbconfig.h'. + * + * SLAB_USB_PWRSAVE_MODE configures when the device will automatically enter + * the USB power-save mode. It is a bitmask constant with bit values: + * + * @ref USB_PWRSAVE_MODE_OFF - No energy saving option selected + * @ref USB_PWRSAVE_MODE_ONSUSPEND - Enter USB power-save mode on USB suspend + * @ref USB_PWRSAVE_MODE_ONVBUSOFF - Enter USB power-save mode when not + * attached to the USB host. + * @ref USB_PWRSAVE_MODE_FASTWAKE - Exit USB power-save mode more quickly. + * This is useful for some applications that + * support remote wakeup. + *****************************************************************************/ + +/**************************************************************************//** + * @def SLAB_USB_POLLED_MODE + * @brief Enables/disables USB library polled mode + * @details + * When '1' the library will run in polled mode + * When '0' the library will run in interrupt mode + * + * Default setting is '0' and may be overridden by defining in 'usbconfig.h'. + * + *****************************************************************************/ + +/** @} (end addtogroup efm8_usb_config Library Configuration) */ + +// Set default USB library configurations if the value is not configured in +// usbconfig.h + +#ifndef SLAB_USB_BUS_POWERED +#define SLAB_USB_BUS_POWERED 1 +#endif + +#ifndef SLAB_USB_FULL_SPEED +#define SLAB_USB_FULL_SPEED 1 +#endif + +#ifndef SLAB_USB_CLOCK_RECOVERY_ENABLED +#define SLAB_USB_CLOCK_RECOVERY_ENABLED 1 +#endif + +#ifndef SLAB_USB_REMOTE_WAKEUP_ENABLED +#define SLAB_USB_REMOTE_WAKEUP_ENABLED 0 +#endif + +#ifndef SLAB_USB_NUM_INTERFACES +#define SLAB_USB_NUM_INTERFACES 1 +#endif + +#ifndef SLAB_USB_SUPPORT_ALT_INTERFACES +#define SLAB_USB_SUPPORT_ALT_INTERFACES 0 +#endif + +#ifndef SLAB_USB_EP1IN_USED +#define SLAB_USB_EP1IN_USED 0 +#endif + +#ifndef SLAB_USB_EP1OUT_USED +#define SLAB_USB_EP1OUT_USED 0 +#endif + +#ifndef SLAB_USB_EP2IN_USED +#define SLAB_USB_EP2IN_USED 0 +#endif + +#ifndef SLAB_USB_EP2OUT_USED +#define SLAB_USB_EP2OUT_USED 0 +#endif + +#ifndef SLAB_USB_EP3IN_USED +#define SLAB_USB_EP3IN_USED 0 +#endif + +#ifndef SLAB_USB_EP3OUT_USED +#define SLAB_USB_EP3OUT_USED 0 +#endif + +#ifndef SLAB_USB_EP1IN_MAX_PACKET_SIZE +#define SLAB_USB_EP1IN_MAX_PACKET_SIZE 64 +#endif + +#ifndef SLAB_USB_EP1OUT_MAX_PACKET_SIZE +#define SLAB_USB_EP1OUT_MAX_PACKET_SIZE 64 +#endif + +#ifndef SLAB_USB_EP2IN_MAX_PACKET_SIZE +#define SLAB_USB_EP2IN_MAX_PACKET_SIZE 64 +#endif + +#ifndef SLAB_USB_EP2OUT_MAX_PACKET_SIZE +#define SLAB_USB_EP2OUT_MAX_PACKET_SIZE 64 +#endif + +#ifndef SLAB_USB_EP3IN_MAX_PACKET_SIZE +#define SLAB_USB_EP3IN_MAX_PACKET_SIZE 64 +#endif + +#ifndef SLAB_USB_EP3OUT_MAX_PACKET_SIZE +#define SLAB_USB_EP3OUT_MAX_PACKET_SIZE 64 +#endif + +#ifndef SLAB_USB_EP1IN_TRANSFER_TYPE +#define SLAB_USB_EP1IN_TRANSFER_TYPE USB_EPTYPE_INTR +#endif + +#ifndef SLAB_USB_EP1OUT_TRANSFER_TYPE +#define SLAB_USB_EP1OUT_TRANSFER_TYPE USB_EPTYPE_INTR +#endif + +#ifndef SLAB_USB_EP2IN_TRANSFER_TYPE +#define SLAB_USB_EP2IN_TRANSFER_TYPE USB_EPTYPE_INTR +#endif + +#ifndef SLAB_USB_EP2OUT_TRANSFER_TYPE +#define SLAB_USB_EP2OUT_TRANSFER_TYPE USB_EPTYPE_INTR +#endif + +#ifndef SLAB_USB_EP3IN_TRANSFER_TYPE +#define SLAB_USB_EP3IN_TRANSFER_TYPE USB_EPTYPE_INTR +#endif + +#ifndef SLAB_USB_EP3OUT_TRANSFER_TYPE +#define SLAB_USB_EP3OUT_TRANSFER_TYPE USB_EPTYPE_INTR +#endif + +#ifndef SLAB_USB_RESET_CB +#define SLAB_USB_RESET_CB 0 +#endif + +#ifndef SLAB_USB_SOF_CB +#define SLAB_USB_SOF_CB 0 +#endif + +#ifndef SLAB_USB_STATE_CHANGE_CB +#define SLAB_USB_STATE_CHANGE_CB 0 +#endif + +#ifndef SLAB_USB_IS_SELF_POWERED_CB +#define SLAB_USB_IS_SELF_POWERED_CB 0 +#endif + +#ifndef SLAB_USB_SETUP_CMD_CB +#define SLAB_USB_SETUP_CMD_CB 0 +#endif + +#ifndef SLAB_USB_HANDLER_CB +#define SLAB_USB_HANDLER_CB 0 +#endif + +#ifndef SLAB_USB_NUM_LANGUAGES +#define SLAB_USB_NUM_LANGUAGES 1 +#endif + +#ifndef SLAB_USB_LANGUAGE +#define SLAB_USB_LANGUAGE USB_LANGID_ENUS +#endif + +#ifndef SLAB_USB_UTF8_STRINGS +#define SLAB_USB_UTF8_STRINGS 0 +#endif + +#ifndef SLAB_USB_PWRSAVE_MODE +#define SLAB_USB_PWRSAVE_MODE USB_PWRSAVE_MODE_ONSUSPEND +#endif + +#ifndef SLAB_USB_POLLED_MODE +#define SLAB_USB_POLLED_MODE 0 +#endif + +#if SLAB_USB_POLLED_MODE +void usbIrqHandler(void); +#endif + +/***************************************************************************//** + * @addtogroup efm8_api API Functions + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Abort all pending transfers. + * + * @details + * Aborts transfers for all endpoints currently in use. Pending + * transfers on the default endpoint (EP0) are not aborted. + ******************************************************************************/ +void USBD_AbortAllTransfers(void); + +/***************************************************************************//** + * @brief + * Abort a pending transfer on a specific endpoint. + * + * @param epAddr + * The address of the endpoint to abort. + * @return + * @ref USB_STATUS_OK is the transfer aborted, @ref USB_STATUS_ILLEGAL + * otherwise + ******************************************************************************/ +int8_t USBD_AbortTransfer(uint8_t epAddr); + +/***************************************************************************//** + * @brief + * Start USB device operation. + * + * @details + * Device operation is started by connecting a pull-up resistor on the + * appropriate USB data line. + ******************************************************************************/ +void USBD_Connect(void); + +/***************************************************************************//** + * @brief + * Stop USB device operation. + * + * @details + * Device operation is stopped by disconnecting the pull-up resistor from the + * appropriate USB data line. Often referred to as a "soft" disconnect. + ******************************************************************************/ +void USBD_Disconnect(void); + +/***************************************************************************//** + * @brief + * Check if an endpoint is busy doing a transfer. + * + * @param epAddr + * The address of the endpoint to check. + * + * @return + * True if endpoint is busy, false otherwise. + ******************************************************************************/ +bool USBD_EpIsBusy(uint8_t epAddr); + +/***************************************************************************//** + * @brief + * Get current USB device state. + * + * @return + * Device USB state. See @ref USBD_State_TypeDef. + ******************************************************************************/ +USBD_State_TypeDef USBD_GetUsbState(void); + +/***************************************************************************//** + * @brief + * Initializes USB device hardware and internal protocol stack data structures, + * then connects the data-line (D+ or D-) pullup resistor to signal host that + * enumeration can begin. + * + * @note + * You may later use @ref USBD_Disconnect() and @ref USBD_Connect() to force + * reenumeration. + * + * @param p + * Pointer to device initialization struct. See @ref USBD_Init_TypeDef. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int8_t USBD_Init(SI_VARIABLE_SEGMENT_POINTER(p, const USBD_Init_TypeDef, SI_SEG_GENERIC)); + +/***************************************************************************//** + * @brief + * Start a read (OUT) transfer on an endpoint. + * + * @note + * If it is possible that the host will send more data than your device + * expects, round buffer size up to the next multiple of maxpacket size. + * + * @param epAddr + * Endpoint address. + * + * @param dat + * Pointer to transfer data buffer. + * + * @param byteCount + * Transfer length. + * + * @param callback + * Boolean to determine if USB_XferCompleteCb should be called for this + * transfer. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int8_t USBD_Read(uint8_t epAddr, + SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), + uint16_t byteCount, + bool callback); + +/***************************************************************************//** + * @brief + * Perform a remote wakeup signaling sequence. + * + * @note + * This function is typically called by the library if @ref + * USBD_RemoteWakeupCb() returns true, so it does not need to be called by + * application code. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int8_t USBD_RemoteWakeup(void); + +/***************************************************************************//** + * @brief + * Processes USB events when the library is configured for polled mode + * + * @details + * The USB library can be configured for interrupt (SLAB_USB_POLLED_MODE == 0) + * or polled (SLAB_USB_POLLED_MODE == 1) mode. + * + * When in interrupt mode, the USB interrupt handler will trigger + * when a USB event occurs. Callback functions will be called as needed. + * + * When in polled mode, the application must call USBD_Run() periodically to + * check for and process USB events. This may be useful in complex systems or + * when using an RTOS to perform all USB processing in the main loop instead + * of in the interrupt context. + * + ******************************************************************************/ +void USBD_Run(void); + +/***************************************************************************//** + * @brief + * Set an endpoint in the stalled (halted) state. + * + * @param epAddr + * The address of the endpoint to stall. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int8_t USBD_StallEp(uint8_t epAddr); + +/***************************************************************************//** + * @brief + * Stop USB device stack operation. + * + * @details + * The data-line pullup resistor is turned off, USB interrupts are disabled, + * and finally the USB pins are disabled. + ******************************************************************************/ +void USBD_Stop(void); + +/***************************************************************************//** + * @brief + * Enters USB suspend mode + * + * @details + * Disables USB transceiver, VDD Monitor, and prefetch engine. Suspends the + * internal regulator and internal oscillator. + * This function will not exit until the device recognizes resume signaling, + * VBUS attachment/removal, or a remote wakeup source interrupt. + * Before exiting, restores the states of the USB transceiver, + * VDD Monitor, prefetch engine, and internal regulator. + ******************************************************************************/ +void USBD_Suspend(void); + +/***************************************************************************//** + * @brief + * Reset stall state on a stalled (halted) endpoint. + * + * @param epAddr + * The address of the endpoint to un-stall. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int8_t USBD_UnStallEp(uint8_t epAddr); + +/***************************************************************************//** + * @brief + * Start a write (IN) transfer on an endpoint. + * + * @param epAddr + * Endpoint address. + * + * @param dat + * Pointer to transfer data buffer. This buffer must be WORD (4 byte) aligned. + * + * @param byteCount + * Transfer length. + * + * @param callback + * Boolean to determine if USB_XferCompleteCb should be called for this + * transfer. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int8_t USBD_Write(uint8_t epAddr, + SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), + uint16_t byteCount, + bool callback); + +/** @} (end addtogroup efm8_api API Functions) */ + +/***************************************************************************//** + * @addtogroup efm8_callbacks Callback Functions + * @{ + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * USB Handler Entry callback function. + * @details + * Some systems may wish to be in a low-power state when between USB events. + * This low-power state may configure the system clock to a very low + * frequency. In order to reduce the execution time of the USB handler, this + * function is called before the handler executes to allow the system to switch + * to a higher clock source. When all USB processing is complete, + * @ref USBD_ExitHandler() will be called to allow the system to return + * to the low-power state. + * This callback function is optionally enabled by setting + * @ref SLAB_USB_HANDLER_CB to 1. + ******************************************************************************/ +void USBD_EnterHandler(void); + +/***************************************************************************//** + * @brief + * USB Handler Exit callback function. + * @details + * Some systems may wish to be in a low-power state when between USB events. + * This low-power state may configure the system clock to a very low + * frequency. This function is called after all USB processing is finished + * to allow a system that was previously configured by + * @ref USBD_EnterHandler() for high power to return to a low power state. + * This callback function is optionally enabled by setting + * @ref SLAB_USB_HANDLER_CB to 1. + ******************************************************************************/ +void USBD_ExitHandler(void); + +/***************************************************************************//** + * @brief + * USB Reset callback function. + * @details + * Called whenever USB reset signaling is detected on the USB port. + ******************************************************************************/ +void USBD_ResetCb(void); + +/***************************************************************************//** + * @brief + * USB Start Of Frame (SOF) interrupt callback function. + * + * @details + * Called at each SOF interrupt (if enabled), + * + * @param sofNr + * Current frame number. The value rolls over to 0 after 16383 (0x3FFF). + ******************************************************************************/ +void USBD_SofCb(uint16_t sofNr); + +/***************************************************************************//** + * @brief + * USB State change callback function. + * + * @details + * Called whenever the USB state of the device changes + * + * @param oldState + * The device USB state just left. See @ref USBD_State_TypeDef. + * + * @param newState + * New (the current) USB device state. See @ref USBD_State_TypeDef. + ******************************************************************************/ +void USBD_DeviceStateChangeCb(USBD_State_TypeDef oldState, + USBD_State_TypeDef newState); + +/***************************************************************************//** + * @brief + * USB power mode callback function. + * + * @details + * Called whenever the device stack needs to know if the device is currently + * self- or bus-powered. Typically when host has issued a @ref GET_STATUS + * setup command. + * + * @return + * True if self-powered, false otherwise. + ******************************************************************************/ +bool USBD_IsSelfPoweredCb(void); + +/***************************************************************************//** + * @brief + * USB setup request callback function. + * + * @details + * Called on each setup request received from host. This gives the system a + * possibility to extend or override standard requests, and to implement class + * or vendor specific requests. Return @ref USB_STATUS_OK if the request is + * handled, return @ref USB_STATUS_REQ_ERR if it is an illegal request or + * return @ref USB_STATUS_REQ_UNHANDLED to pass the request on to the default + * request handler. + * + * @param setup + * Pointer to a USB setup packet. See @ref USB_Setup_TypeDef. + * + * @return + * An appropriate status/error code. See @ref USB_Status_TypeDef. + ******************************************************************************/ +USB_Status_TypeDef USBD_SetupCmdCb(SI_VARIABLE_SEGMENT_POINTER(setup, + USB_Setup_TypeDef, + MEM_MODEL_SEG)); + +/***************************************************************************//** + * @brief + * USB set interface callback function. + * + * @details + * Called each time the SET_INTERFACE request is made. + * + * @param interface + * Number of the interface to set. + * + * @param altSetting + * Alternate setting for the interface + * + * @return + * @ref USB_STATUS_OK if the alternate interface is valid and can be set, + * @ref USB_STATUS_REQ_ERR otherwise + ******************************************************************************/ +USB_Status_TypeDef USBD_SetInterfaceCb(uint8_t interface, uint8_t altSetting); + +/***************************************************************************//** + * @brief + * Queries the application to see if a remote wakeup occurred. + * @details + * If remote wakeup is enabled via @ref SLAB_USB_REMOTE_WAKEUP_ENABLED, the + * USB library will query the application after waking from suspend to see if + * the remote wakeup source was the reason for the wakeup. If this function + * returns True, the library will call @ref USBD_RemoteWakeup() to wake up the + * host and exit suspend mode. + * @return + * True if the remote wakeup source was the reason the device woke from + * suspend, false otherwise. + * + ******************************************************************************/ +bool USBD_RemoteWakeupCb(void); + +/***************************************************************************//** + * @brief + * Delays 10 - 15 ms while resume signaling is active during a remote + * wakeup event + * + ******************************************************************************/ +void USBD_RemoteWakeupDelay(void); + +/***************************************************************************//** + * @brief + * Processes USB events when the library is configured for polled mode + * + * @ details + * The USB library can be configured for interrupt + * (@ref SLAB_USB_POLLED_MODE == 0) or polled (@ref SLAB_USB_POLLED_MODE == 1) + * mode. + * + * When in interrupt mode, the USB interrupt handler will trigger + * when a USB event occurs. Callback functions will be called as needed. + * + * When in polled mode, the application must call USBD_Run() periodically to + * check for and process USB events. This may be useful in complex systems or + * when using an RTOS to perform all USB processing in the main loop instead + * of in the interrupt context. + * + ******************************************************************************/ +void USBD_Run(void); + +/***************************************************************************//** + * @brief + * USB transfer complete callback function. + * + * @details + * Called each time a packet is sent on an IN endpoint or received on an + * OUT endpoint. + * + * @param epAddr + * Endpoint on which the transfer occurred + * + * @param status + * Status of the endpoint + * + * @param xferred + * For bulk, interrupt, and control transfers: + * Number of bytes transferred since the last USBD_Write() or USBD_Read() + * call. + * For isochronous IN transfers: + * This parameter is not used + * For isochronous OUT transfers: + * the number of bytes received in the last packet + * + * @param remaining + * For bulk, interrupt, and control transfers: + * Number of bytes left to send or receive on the endpoint + * For isochronous transfers: + * The current index into the circular buffer holding isochronous data + * + * @return + * For bulk, interrupt, and control transfers: + * '0' + * For isochronous IN transfers: + * the number of bytes to transmit in the next packet + * For isochronous OUT ransfers: + * '0' + ******************************************************************************/ +uint16_t USBD_XferCompleteCb(uint8_t epAddr, \ + USB_Status_TypeDef status, \ + uint16_t xferred, \ + uint16_t remaining); + +/** @} (end addtogroup efm8_callbacks Callback Functions) */ + +/// @cond DO_NOT_INCLUDE_WITH_DOXYGEN +// -------------------- FIFO Access Functions --------------------------------- +void USB_ReadFIFO(uint8_t fifoNum, uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC)); +void USB_WriteFIFO(uint8_t fifoNum, uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), bool txPacket); +/// @endcond DO_NOT_INCLUDE_WITH_DOXYGEN + +// -------------------- Include Files ------------------------------------------ + + // Error if peripheral driver not in use + #include "usb_0.h" + +/** @} (end addtogroup Efm8_usb) */ + +#endif // __SILICON_LABS_EFM8_USB_H__ diff --git a/efm8/lib/efm8_usb/src/efm8_usbd.c b/efm8/lib/efm8_usb/src/efm8_usbd.c new file mode 100644 index 0000000..34f94e4 --- /dev/null +++ b/efm8/lib/efm8_usb/src/efm8_usbd.c @@ -0,0 +1,780 @@ +/**************************************************************************//** + * Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved. + * + * http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt + *****************************************************************************/ + +#include "si_toolchain.h" +#include "efm8_usb.h" +#include "assert.h" +#include + +// ----------------------------------------------------------------------------- +// Global Variables + +/// Tracks the state of the USB device and endpoints and contains pointers +/// to all descriptors. +SI_SEGMENT_VARIABLE(myUsbDevice, USBD_Device_TypeDef, MEM_MODEL_SEG); + +// ----------------------------------------------------------------------------- +// Macros + +/// Returns the requested endpoint object of type USBD_Ep_TypeDef +/// This macro does not check that epAddr is valid, so the calling function +/// should verify epAddr before using the macro. +#define GetEp(epAddr) (&myUsbDevice.ep0 + epAddr) + + +#if SLAB_USB_POLLED_MODE +#define DISABLE_USB_INTS {} +#define ENABLE_USB_INTS {} + +#else +/// Saves the current state of the USB Interrupt Enable to a variable called +/// usbIntsEnabled, then disables USB interrupts. +#define DISABLE_USB_INTS { usbIntsEnabled = USB_GetIntsEnabled(); USB_DisableInts(); } + +/// Sets the USB Interrupt Enable bit to the value of usbIntsEnabled. +/// @ref DISABLE_USB_INTS must be used before this macro is used. +#define ENABLE_USB_INTS { if (usbIntsEnabled) {USB_EnableInts(); } } +#endif // SLAB_USB_POLLED_MODE + +// Function in efm8_usbdint.c to force load the module for libraries +extern void forceModuleLoad_usbint(void); + +// ----------------------------------------------------------------------------- +// USB API Functions + +void USBD_AbortAllTransfers(void) +{ + uint8_t i; + bool usbIntsEnabled; + + USB_SaveSfrPage(); + DISABLE_USB_INTS; + + // Call USBD_AbortTransfer() for each endpoint + for (i = 1; i < SLAB_USB_NUM_EPS_USED; i++) + { + USBD_AbortTransfer(i); + } + + ENABLE_USB_INTS; + USB_RestoreSfrPage(); +} + +int8_t USBD_AbortTransfer(uint8_t epAddr) +{ + SI_VARIABLE_SEGMENT_POINTER(ep, USBD_Ep_TypeDef, MEM_MODEL_SEG); + int8_t retVal = USB_STATUS_OK; + bool usbIntsEnabled; + + USB_SaveSfrPage(); + + // Verify this is a valid endpoint address and is not Endpoint 0. + if ((epAddr == EP0) || (epAddr >= SLAB_USB_NUM_EPS_USED)) + { + SLAB_ASSERT(false); + retVal = USB_STATUS_ILLEGAL; + } + else + { + DISABLE_USB_INTS; + ep = GetEp(epAddr); + + // If the state of the endpoint is already idle, there is not need to abort + // a transfer + if (ep->state != D_EP_IDLE) + { + switch (epAddr) + { + #if SLAB_USB_EP1IN_USED + case EP1IN: + USB_AbortInEp(1); + break; + #endif + #if SLAB_USB_EP2IN_USED + case EP2IN: + USB_AbortInEp(2); + break; + #endif + #if SLAB_USB_EP3IN_USED + case EP3IN: + USB_AbortInEp(3); + break; + #endif + #if SLAB_USB_EP1OUT_USED + case EP1OUT: + USB_AbortOutEp(1); + break; + #endif + #if SLAB_USB_EP2OUT_USED + case EP2OUT: + USB_AbortOutEp(2); + break; + #endif + #if SLAB_USB_EP3OUT_USED + case EP3OUT: + USB_AbortOutEp(3); + break; + #endif + } + + // Set the endpoint state to idle and clear out endpoint state variables + ep->state = D_EP_IDLE; + ep->misc.c = 0; + } + } + + ENABLE_USB_INTS; + USB_RestoreSfrPage(); + + return retVal; +} + +void USBD_Connect(void) +{ + USB_SaveSfrPage(); + myUsbDevice.ep0.state = D_EP_IDLE; + USB_EnablePullUpResistor(); + USB_EnableTransceiver(); + USB_RestoreSfrPage(); +} + +void USBD_Disconnect(void) +{ + USB_SaveSfrPage(); + USB_DisablePullUpResistor(); + USB_RestoreSfrPage(); +} + +bool USBD_EpIsBusy(uint8_t epAddr) +{ + SI_VARIABLE_SEGMENT_POINTER(ep, USBD_Ep_TypeDef, MEM_MODEL_SEG); + + // Verify this is a valid endpoint address + if (epAddr >= SLAB_USB_NUM_EPS_USED) + { + SLAB_ASSERT(false); + return true; + } + + ep = GetEp(epAddr); + + if (ep->state == D_EP_IDLE) + { + return false; + } + + return true; +} + +USBD_State_TypeDef USBD_GetUsbState(void) +{ + return myUsbDevice.state; +} + +int8_t USBD_Init(SI_VARIABLE_SEGMENT_POINTER(p, const USBD_Init_TypeDef, SI_SEG_GENERIC)) +{ + uint8_t i; + + USB_SaveSfrPage(); + USB_DisableInts(); + + // This forces the liner to bring in the contents efm8_usbdint + // It is place here since all users MUST call this function + // for the library to work properly + forceModuleLoad_usbint(); + + + // Zero out the myUsbDevice struct, then initialize all non-zero members + for (i = 0; i < sizeof(myUsbDevice); i++) + { + *((SI_VARIABLE_SEGMENT_POINTER(, uint8_t, MEM_MODEL_SEG))&myUsbDevice + i) = 0; + } + + // Get the USB descriptors from p + myUsbDevice.deviceDescriptor = p->deviceDescriptor; + myUsbDevice.configDescriptor = p->configDescriptor; + myUsbDevice.stringDescriptors = p->stringDescriptors; + myUsbDevice.numberOfStrings = p->numberOfStrings; + + // Enable USB clock +#if SLAB_USB_FULL_SPEED + USB_SetClockIntOsc(); + USB_SelectFullSpeed(); +#else + USB_SetClockIntOscDiv8(); + USB_SelectLowSpeed(); +#endif // SLAB_USB_FULL_SPEED + + // Enable or disable VBUS detection +#if SLAB_USB_BUS_POWERED + USB_VbusDetectDisable(); +#else + USB_VbusDetectEnable(); +#endif + + USB_ForceReset(); + USB_EnableDeviceInts(); + USBD_Connect(); + + // If VBUS is present, the state should be Default. + // Otherwise, it is Attached. +#if SLAB_USB_BUS_POWERED + myUsbDevice.state = USBD_STATE_DEFAULT; +#else + if (USB_IsVbusOn()) + { + myUsbDevice.state = USBD_STATE_DEFAULT; + } + else + { + myUsbDevice.state = USBD_STATE_ATTACHED; + } +#endif + + // Only enable USB interrupts when not in polled mode +#if (SLAB_USB_POLLED_MODE == 0) + USB_EnableInts(); +#endif + + USB_RestoreSfrPage(); + USB_DisableInhibit(); + + return USB_STATUS_OK; +} + +int8_t USBD_Read(uint8_t epAddr, + SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), + uint16_t byteCount, + bool callback) +{ + bool usbIntsEnabled; + SI_VARIABLE_SEGMENT_POINTER(ep, USBD_Ep_TypeDef, MEM_MODEL_SEG); + + USB_SaveSfrPage(); + + // Verify the endpoint address is valid. + switch (epAddr) + { + case EP0: +#if SLAB_USB_EP1OUT_USED + case EP1OUT: +#endif +#if SLAB_USB_EP2OUT_USED + case EP2OUT: +#endif +#if SLAB_USB_EP3OUT_USED + case EP3OUT: +#endif + break; +#if SLAB_USB_EP1IN_USED + case EP1IN: +#endif +#if SLAB_USB_EP2IN_USED + case EP2IN: +#endif +#if SLAB_USB_EP3IN_USED + case EP3IN: +#endif + default: + SLAB_ASSERT(false); + return USB_STATUS_ILLEGAL; + } + + // If the device has not been configured, we cannot start a transfer. + if ((epAddr != EP0) && (myUsbDevice.state != USBD_STATE_CONFIGURED)) + { + return USB_STATUS_DEVICE_UNCONFIGURED; + } + + ep = GetEp(epAddr); + + // If the endpoint is not idle, we cannot start a new transfer. + // Return the appropriate error code. + if (ep->state != D_EP_IDLE) + { + if (ep->state == D_EP_STALL) + { + return USB_STATUS_EP_STALLED; + } + else + { + return USB_STATUS_EP_BUSY; + } + } + + DISABLE_USB_INTS; + + ep->buf = dat; + ep->remaining = byteCount; + ep->state = D_EP_RECEIVING; + ep->misc.bits.callback = callback; + ep->misc.bits.waitForRead = false; + + // If isochronous, set the buffer index to 0 +#if ((SLAB_USB_EP3OUT_USED) && (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC)) + if (epAddr == EP3OUT) + { + myUsbDevice.ep3outIsoIdx = 0; + } +#endif + + ENABLE_USB_INTS; + USB_RestoreSfrPage(); + + return USB_STATUS_OK; +} + +#if SLAB_USB_REMOTE_WAKEUP_ENABLED +int8_t USBD_RemoteWakeup(void) +{ + // The device must be suspended and Remote Wakeup must have been previously + // configured with a SET_FEATURE (Remote Wakeup) command. + if ((myUsbDevice.state != USBD_STATE_SUSPENDED) || + (myUsbDevice.remoteWakeupEnabled == false)) + { + return USB_STATUS_ILLEGAL; + } + + USB_ForceResume(); + USBD_RemoteWakeupDelay(); // Application will provide the delay between + // starting and stopping the resume signal. + USB_ClearResume(); + + return USB_STATUS_OK; +} +#endif // SLAB_USB_REMOTE_WAKEUP_ENABLED + +#if SLAB_USB_POLLED_MODE +void USBD_Run(void) +{ + usbIrqHandler(); +} +#endif // SLAB_USB_POLLED_MODE + +int8_t USBD_StallEp(uint8_t epAddr) +{ + bool usbIntsEnabled; + + USB_SaveSfrPage(); + + // Verify the endpoint address is valid and not Endpoint 0. + if ((epAddr == EP0) || (epAddr >= SLAB_USB_NUM_EPS_USED)) + { + SLAB_ASSERT(false); + return USB_STATUS_ILLEGAL; + } + + DISABLE_USB_INTS; + + // Halt the appropriate endpoint by sending a stall and setting the endpoint + // state to Halted (D_EP_HALT). + switch (epAddr) + { +#if SLAB_USB_EP1IN_USED + case (EP1IN): + myUsbDevice.ep1in.state = D_EP_HALT; + USB_SetIndex(1); + USB_EpnInStall(); + break; +#endif +#if SLAB_USB_EP2IN_USED + case (EP2IN): + myUsbDevice.ep2in.state = D_EP_HALT; + USB_SetIndex(2); + USB_EpnInStall(); + break; +#endif +#if SLAB_USB_EP3IN_USED + case (EP3IN): + myUsbDevice.ep3in.state = D_EP_HALT; + USB_SetIndex(3); + USB_EpnInStall(); + break; +#endif +#if SLAB_USB_EP1OUT_USED + case (EP1OUT): + myUsbDevice.ep1out.state = D_EP_HALT; + USB_SetIndex(1); + USB_EpnOutStall(); + break; +#endif +#if SLAB_USB_EP2OUT_USED + case (EP2OUT): + myUsbDevice.ep2out.state = D_EP_HALT; + USB_SetIndex(2); + USB_EpnOutStall(); + break; +#endif +#if SLAB_USB_EP3OUT_USED + case (EP3OUT): + myUsbDevice.ep3out.state = D_EP_HALT; + USB_SetIndex(3); + USB_EpnOutStall(); + break; +#endif + } + + ENABLE_USB_INTS; + USB_RestoreSfrPage(); + + return USB_STATUS_OK; +} + +void USBD_Stop(void) +{ + USB_DisableInts(); + USBD_Disconnect(); + USBD_SetUsbState(USBD_STATE_NONE); +} + +void USBD_Suspend(void) +{ +#if (!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_FASTWAKE)) + uint8_t i; +#endif + bool regulatorEnabled, prefetchEnabled; +#if SLAB_USB_REMOTE_WAKEUP_ENABLED + bool remoteWakeup = false; +#endif + + USB_SaveSfrPage(); + + // If the USB_PWRSAVE_MODE_ONVBUSOFF is enabled, we can enter suspend if VBUS + // is not present even if the USB has not detected a suspend event. +#if ((!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) || \ + (SLAB_USB_BUS_POWERED)) + if (USB_IsSuspended() == true) +#else + if ((USB_IsSuspended() == true) || (USB_IsVbusOn() == false)) +#endif + { + USB_SuspendTransceiver(); + +#if SLAB_USB_FULL_SPEED + USB_SetSuspendClock(); +#endif + + // Get the state of the prefetch engine enable bit and disable the prefetch + // engine + prefetchEnabled = USB_IsPrefetchEnabled(); + USB_DisablePrefetch(); + + // Get the state of the internal regulator before suspending it. + if (USB_IsRegulatorEnabled() == true) + { + regulatorEnabled = true; + +#if (SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_FASTWAKE) + USB_SuspendRegulatorFastWake(); +#else + USB_SuspendRegulator(); + + // Wait at least 12 clock instructions before halting the internal oscillator + for (i = 0; i < 3; i++) + { + } +#endif + } + else + { + regulatorEnabled = false; + } + + do + { + USB_SuspendOscillator(); + + // When we arrive here, the device has waked from suspend mode. + +#if SLAB_USB_REMOTE_WAKEUP_ENABLED + // If remote wakeup is enabled, query the application if the remote + // wakeup event occurred. If so, exit USBD_Suspend(). + if (USB_IsSuspended() == true) + { + remoteWakeup = USBD_RemoteWakeupCb(); + if (remoteWakeup == true) + { + break; + } + } +#endif +#if ((!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) && \ + (SLAB_USB_BUS_POWERED == 0)) + // If the USB_PWRSAVE_MODE_ONVBUSOFF mode is disabled, VBUS has been + // removed, so exit USBD_Suspend(). + if (USB_IsVbusOn() == false) + { + break; + } +#endif + +#if ((!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) || \ + (SLAB_USB_BUS_POWERED)) + } while (USB_IsSuspended() == true); +#else + } while ((USB_IsSuspended() == true) || (USB_IsVbusOn() == false)); +#endif + // Restore the internal regulator + if (regulatorEnabled == true) + { + USB_UnsuspendRegulator(); + } + + // Restore the prefetch engine + if (prefetchEnabled == true) + { + USB_EnablePrefetch(); + } + +#if SLAB_USB_FULL_SPEED + // Restore the clock + USB_SetNormalClock(); +#endif + USB_EnableTransceiver(); + +#if SLAB_USB_REMOTE_WAKEUP_ENABLED + // If the device woke from suspend due to a remote wakeup source, call + // USBD_RemoteWakeup() here to wake up the host. + if (remoteWakeup == true) + { + // Wake up the host + if (USBD_RemoteWakeup() == USB_STATUS_OK) + { + // If the remote wakeup succeeded, transition out of USB suspend state + USBD_SetUsbState(myUsbDevice.savedState); + } + } +#endif + } + + USB_RestoreSfrPage(); +} + +int8_t USBD_UnStallEp(uint8_t epAddr) +{ + bool usbIntsEnabled; + + USB_SaveSfrPage(); + + // Verify the endpoint address is valid and not Endpoint 0. + if ((epAddr == EP0) || (epAddr >= SLAB_USB_NUM_EPS_USED)) + { + SLAB_ASSERT(false); + return USB_STATUS_ILLEGAL; + } + else + { + DISABLE_USB_INTS; + + // End the stall condition and set the endpoint state to idle. + switch (epAddr) + { +#if SLAB_USB_EP1IN_USED + case (EP1IN): + myUsbDevice.ep1in.state = D_EP_IDLE; + USB_SetIndex(1); + USB_EpnInEndStall(); + break; +#endif +#if SLAB_USB_EP2IN_USED + case (EP2IN): + myUsbDevice.ep2in.state = D_EP_IDLE; + USB_SetIndex(2); + USB_EpnInEndStall(); + break; +#endif +#if SLAB_USB_EP3IN_USED + case (EP3IN): + myUsbDevice.ep3in.state = D_EP_IDLE; + USB_SetIndex(3); + USB_EpnInEndStall(); + break; +#endif +#if SLAB_USB_EP1OUT_USED + case (EP1OUT): + myUsbDevice.ep1out.state = D_EP_IDLE; + USB_SetIndex(1); + USB_EpnOutEndStall(); + break; +#endif +#if SLAB_USB_EP2OUT_USED + case (EP2OUT): + myUsbDevice.ep2out.state = D_EP_IDLE; + USB_SetIndex(2); + USB_EpnOutEndStall(); + break; +#endif +#if SLAB_USB_EP3OUT_USED + case (EP3OUT): + myUsbDevice.ep3out.state = D_EP_IDLE; + USB_SetIndex(3); + USB_EpnOutEndStall(); + break; +#endif + } + + ENABLE_USB_INTS; + USB_RestoreSfrPage(); + } + + return USB_STATUS_OK; +} + +int8_t USBD_Write(uint8_t epAddr, + SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), + uint16_t byteCount, + bool callback) +{ + bool usbIntsEnabled; + SI_VARIABLE_SEGMENT_POINTER(ep, USBD_Ep_TypeDef, MEM_MODEL_SEG); + + USB_SaveSfrPage(); + + // Verify the endpoint address is valid. + switch (epAddr) + { + case EP0: +#if SLAB_USB_EP1IN_USED + case EP1IN: +#endif +#if SLAB_USB_EP2IN_USED + case EP2IN: +#endif +#if SLAB_USB_EP3IN_USED + case EP3IN: +#endif + break; +#if SLAB_USB_EP1OUT_USED + case EP1OUT: +#endif +#if SLAB_USB_EP2OUT_USED + case EP2OUT: +#endif +#if SLAB_USB_EP3OUT_USED + case EP3OUT: +#endif + default: + SLAB_ASSERT(false); + return USB_STATUS_ILLEGAL; + } + + // If the device is not configured and it is not Endpoint 0, we cannot begin + // a transfer. + if ((epAddr != EP0) && (myUsbDevice.state != USBD_STATE_CONFIGURED)) + { + return USB_STATUS_DEVICE_UNCONFIGURED; + } + + ep = GetEp(epAddr); + + // If the endpoint is not idle, we cannot start a new transfer. + // Return the appropriate error code. + if (ep->state != D_EP_IDLE) + { + if (ep->state == D_EP_STALL) + { + return USB_STATUS_EP_STALLED; + } + else + { + return USB_STATUS_EP_BUSY; + } + } + + DISABLE_USB_INTS; + + ep->buf = dat; + ep->remaining = byteCount; + ep->state = D_EP_TRANSMITTING; + ep->misc.bits.callback = callback; + + switch (epAddr) + { + // For Endpoint 0, set the inPacketPending flag to true. The USB handler + // will see this on the next SOF and begin the transfer. + case (EP0): + myUsbDevice.ep0.misc.bits.inPacketPending = true; + break; + + // For data endpoints, we will call USB_WriteFIFO here to reduce latency + // between the call to USBD_Write() and the first packet being sent. +#if SLAB_USB_EP1IN_USED + case (EP1IN): + USB_WriteFIFO(1, + (byteCount > SLAB_USB_EP1IN_MAX_PACKET_SIZE) ? SLAB_USB_EP1IN_MAX_PACKET_SIZE : byteCount, + myUsbDevice.ep1in.buf, + true); + break; +#endif // SLAB_USB_EP1IN_USED +#if SLAB_USB_EP2IN_USED + case (EP2IN): + USB_WriteFIFO(2, + (byteCount > SLAB_USB_EP2IN_MAX_PACKET_SIZE) ? SLAB_USB_EP2IN_MAX_PACKET_SIZE : byteCount, + myUsbDevice.ep2in.buf, + true); + break; +#endif // SLAB_USB_EP2IN_USED +#if SLAB_USB_EP3IN_USED + case (EP3IN): +#if ((SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_INTR)) + USB_WriteFIFO(3, + (byteCount > SLAB_USB_EP3IN_MAX_PACKET_SIZE) ? SLAB_USB_EP3IN_MAX_PACKET_SIZE : byteCount, + myUsbDevice.ep3in.buf, + true); +#elif (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC) + myUsbDevice.ep3in.misc.bits.inPacketPending = true; + myUsbDevice.ep3inIsoIdx = 0; +#endif + break; +#endif // SLAB_USB_EP3IN_USED + } + + ENABLE_USB_INTS; + USB_RestoreSfrPage(); + + return USB_STATUS_OK; +} + +// ----------------------------------------------------------------------------- +// UtilityFunctions + +void USBD_SetUsbState(USBD_State_TypeDef newState) +{ +#if (SLAB_USB_SUPPORT_ALT_INTERFACES) + uint8_t i; +#endif + USBD_State_TypeDef currentState; + + currentState = myUsbDevice.state; + + // If the device is un-configuring, disable the data endpoints and clear out + // alternate interface settings + if ((currentState >= USBD_STATE_SUSPENDED) + && (newState < USBD_STATE_SUSPENDED)) + { + USBD_AbortAllTransfers(); + +#if (SLAB_USB_SUPPORT_ALT_INTERFACES) + for (i = 0; i < SLAB_USB_NUM_INTERFACES; i++) + { + myUsbDevice.interfaceAltSetting[i] = 0; + } +#endif + } + if (newState == USBD_STATE_SUSPENDED) + { + myUsbDevice.savedState = currentState; + } + + myUsbDevice.state = newState; + +#if SLAB_USB_STATE_CHANGE_CB + if (currentState != newState) + { + USBD_DeviceStateChangeCb(currentState, newState); + } +#endif +} diff --git a/efm8/lib/efm8_usb/src/efm8_usbdch9.c b/efm8/lib/efm8_usb/src/efm8_usbdch9.c new file mode 100644 index 0000000..6bb2a47 --- /dev/null +++ b/efm8/lib/efm8_usb/src/efm8_usbdch9.c @@ -0,0 +1,870 @@ +/**************************************************************************//** + * Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved. + * + * http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt + *****************************************************************************/ + +#include "si_toolchain.h" +#include "efm8_usb.h" +#include +#include + +// ----------------------------------------------------------------------------- +// Function Prototypes + +static USB_Status_TypeDef ClearFeature(void); +static USB_Status_TypeDef GetConfiguration(void); +static USB_Status_TypeDef GetDescriptor(void); +static USB_Status_TypeDef GetInterface(void); +static USB_Status_TypeDef GetStatus(void); +static USB_Status_TypeDef SetAddress(void); +static USB_Status_TypeDef SetConfiguration(void); +static USB_Status_TypeDef SetFeature(void); +static USB_Status_TypeDef SetInterface(void); +static void USBD_ActivateAllEps(bool forceIdle); +static void EP0_Write(SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), uint16_t numBytes); + +// ----------------------------------------------------------------------------- +// Global Variables + +extern SI_SEGMENT_VARIABLE(myUsbDevice, USBD_Device_TypeDef, MEM_MODEL_SEG); +const SI_SEGMENT_VARIABLE(txZero[2], uint8_t, SI_SEG_CODE); + +// ----------------------------------------------------------------------------- +// Static Global Variables + +static uint16_t pStatus; + +// ----------------------------------------------------------------------------- +// Chapter 9 Functions + +/***************************************************************************//** + * @brief Processes Standard Request (Chapter 9 Command) + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +USB_Status_TypeDef USBDCH9_SetupCmd(void) +{ + USB_Status_TypeDef status = USB_STATUS_OK; + + switch (myUsbDevice.setup.bRequest) + { + case GET_STATUS: + status = GetStatus(); + break; + + case CLEAR_FEATURE: + status = ClearFeature(); + break; + + case SET_FEATURE: + status = SetFeature(); + break; + + case SET_ADDRESS: + status = SetAddress(); + break; + + case GET_DESCRIPTOR: + status = GetDescriptor(); + break; + + case GET_CONFIGURATION: + status = GetConfiguration(); + break; + + case SET_CONFIGURATION: + status = SetConfiguration(); + break; + + case GET_INTERFACE: + status = GetInterface(); + break; + + case SET_INTERFACE: + status = SetInterface(); + break; + + default: + status = USB_STATUS_REQ_ERR; + break; + } + + return status; +} + +/***************************************************************************//** + * @brief Clears the requested feature + * @details Supports CLEAR_FEATURE for Remote Wakeup and Endpoint Halt + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +static USB_Status_TypeDef ClearFeature(void) +{ + USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR; + + if (myUsbDevice.setup.wLength == 0) + { + switch (myUsbDevice.setup.bmRequestType.Recipient) + { + #if SLAB_USB_REMOTE_WAKEUP_ENABLED + case USB_SETUP_RECIPIENT_DEVICE: + if ((myUsbDevice.setup.wIndex == 0) + && (myUsbDevice.setup.wValue == USB_FEATURE_DEVICE_REMOTE_WAKEUP) + && (myUsbDevice.state >= USBD_STATE_ADDRESSED)) + { + // Remote wakeup feature clear + myUsbDevice.remoteWakeupEnabled = false; + retVal = USB_STATUS_OK; + } + break; + #endif // SLAB_USB_REMOTE_WAKEUP_ENABLED + case USB_SETUP_RECIPIENT_ENDPOINT: + if (myUsbDevice.setup.wValue == USB_FEATURE_ENDPOINT_HALT) + { + // Device does not support halting endpoint 0, but do not return + // an error as this is a valid request + if (((myUsbDevice.setup.wIndex & ~USB_EP_DIR_IN) == 0) + && (myUsbDevice.state >= USBD_STATE_ADDRESSED)) + { + retVal = USB_STATUS_OK; + } + else if (((myUsbDevice.setup.wIndex & ~USB_SETUP_DIR_D2H) < SLAB_USB_NUM_EPS_USED) + && (myUsbDevice.state == USBD_STATE_CONFIGURED)) + { + retVal = USB_STATUS_OK; + USB_SetIndex((myUsbDevice.setup.wIndex & 0xFF) & ~USB_SETUP_DIR_D2H); + +#if (SLAB_USB_EP1IN_USED || SLAB_USB_EP2IN_USED || SLAB_USB_EP3IN_USED) + if ((myUsbDevice.setup.wIndex & 0xFF) & USB_EP_DIR_IN) + { + USB_EpnInEndStallAndClearDataToggle(); + } +#endif +#if (SLAB_USB_EP1OUT_USED || SLAB_USB_EP2OUT_USED || SLAB_USB_EP3OUT_USED) + if (((myUsbDevice.setup.wIndex & 0xFF) & USB_EP_DIR_IN) == 0) + { + USB_EpnOutEndStallAndClearDataToggle(); + } +#endif + + switch (myUsbDevice.setup.wIndex & 0xFF) + { +#if SLAB_USB_EP1OUT_USED + case (USB_EP_DIR_OUT | 1): + if (myUsbDevice.ep1out.state != D_EP_RECEIVING) + { + myUsbDevice.ep1out.state = D_EP_IDLE; + } + break; +#endif +#if SLAB_USB_EP2OUT_USED + case (USB_EP_DIR_OUT | 2): + if (myUsbDevice.ep2out.state != D_EP_RECEIVING) + { + myUsbDevice.ep2out.state = D_EP_IDLE; + } + break; +#endif +#if SLAB_USB_EP3OUT_USED + case (USB_EP_DIR_OUT | 3): + if (myUsbDevice.ep3out.state != D_EP_RECEIVING) + { + myUsbDevice.ep3out.state = D_EP_IDLE; + } + break; +#endif +#if SLAB_USB_EP1IN_USED + case (USB_EP_DIR_IN | 1): + if (myUsbDevice.ep1in.state != D_EP_TRANSMITTING) + { + myUsbDevice.ep1in.state = D_EP_IDLE; + } + break; +#endif +#if SLAB_USB_EP2IN_USED + case (USB_EP_DIR_IN | 2): + if (myUsbDevice.ep2in.state != D_EP_TRANSMITTING) + { + myUsbDevice.ep2in.state = D_EP_IDLE; + } + break; +#endif +#if SLAB_USB_EP3IN_USED + case (USB_EP_DIR_IN | 3): + if (myUsbDevice.ep3in.state != D_EP_TRANSMITTING) + { + myUsbDevice.ep3in.state = D_EP_IDLE; + } + break; +#endif + } + } + } + } + } + return retVal; +} + +/***************************************************************************//** + * @brief Gets the current configuration value + * @details Zero means the device is not configured, a non-zero value + * is the configuration value of the configured device. + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +static USB_Status_TypeDef GetConfiguration(void) +{ + USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR; + + if ((myUsbDevice.setup.wIndex == 0) + && (myUsbDevice.setup.wValue == 0) + && (myUsbDevice.setup.wLength == 1) + && (myUsbDevice.setup.bmRequestType.Direction == USB_SETUP_DIR_IN) + && (myUsbDevice.setup.bmRequestType.Recipient == USB_SETUP_RECIPIENT_DEVICE)) + { + if (myUsbDevice.state == USBD_STATE_ADDRESSED) + { + EP0_Write((SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))txZero, 1); + retVal = USB_STATUS_OK; + } + else if (myUsbDevice.state == USBD_STATE_CONFIGURED) + { + EP0_Write(&myUsbDevice.configurationValue, 1); + retVal = USB_STATUS_OK; + } + } + return retVal; +} + +/***************************************************************************//** + * @brief Sends the requested USB Descriptor + * @details Supports single or multiple languages (configured by + * @ref SLAB_USB_NUM_LANGUAGES). + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +static USB_Status_TypeDef GetDescriptor(void) +{ +#if (SLAB_USB_NUM_LANGUAGES > 1) + bool langSupported; + uint8_t lang; +#endif + + uint8_t index; + uint16_t length = 0; + SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC); + USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR; + + if (*((uint8_t *)&myUsbDevice.setup.bmRequestType) == + (USB_SETUP_DIR_D2H | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE)) + { + index = myUsbDevice.setup.wValue & 0xFF; + + switch (myUsbDevice.setup.wValue >> 8) + { + case USB_DEVICE_DESCRIPTOR: + if (index != 0) + { + break; + } + dat = (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))myUsbDevice.deviceDescriptor; + length = myUsbDevice.deviceDescriptor->bLength; + break; + + case USB_CONFIG_DESCRIPTOR: + if (index != 0) + { + break; + } + dat = (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))myUsbDevice.configDescriptor; + length = le16toh(myUsbDevice.configDescriptor->wTotalLength); + break; + + case USB_STRING_DESCRIPTOR: + #if (SLAB_USB_NUM_LANGUAGES == 1) + + dat = (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))myUsbDevice.stringDescriptors[index]; + + // Index 0 is the language string. If SLAB_USB_NUM_LANGUAGES == 1, we + // know the length will be 4 and the format will be UTF16LE. + if (index == 0) + { + length = 4; + myUsbDevice.ep0String.encoding.type = USB_STRING_DESCRIPTOR_UTF16LE; + } + // Otherwise, verify the language is correct (either the value set as + // SLAB_USB_LANGUAGE in usbconfig.h, or 0). + else if ((myUsbDevice.setup.wIndex == 0) || (myUsbDevice.setup.wIndex == SLAB_USB_LANGUAGE)) + { + // Verify the index is valid + if (index < myUsbDevice.numberOfStrings) + { + length = *(dat + USB_STRING_DESCRIPTOR_LENGTH); + myUsbDevice.ep0String.encoding.type = *(dat + USB_STRING_DESCRIPTOR_ENCODING); + dat += USB_STRING_DESCRIPTOR_LENGTH; + myUsbDevice.ep0String.encoding.init = true; + } + } + #elif (SLAB_USB_NUM_LANGUAGES > 1) + + langSupported = false; + + // Index 0 is the language. + if (index == 0) + { + dat = ((SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))myUsbDevice.stringDescriptors->languageArray[0][index]); + length = *((uint8_t *)dat); + myUsbDevice.ep0String.encoding.type = USB_STRING_DESCRIPTOR_UTF16LE; + } + else + { + // Otherwise, verify the language is one of the supported languages or 0. + for (lang = 0; lang < SLAB_USB_NUM_LANGUAGES; lang++) + { + if ((myUsbDevice.stringDescriptors->languageIDs[lang] == myUsbDevice.setup.wIndex) + || (myUsbDevice.stringDescriptors->languageIDs[lang] == 0)) + { + langSupported = true; + break; + } + } + if ((langSupported == true) && (index < myUsbDevice.numberOfStrings)) + { + dat = ((SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))myUsbDevice.stringDescriptors->languageArray[lang][index]); + length = *(dat + USB_STRING_DESCRIPTOR_LENGTH); + myUsbDevice.ep0String.encoding.type = *(dat + USB_STRING_DESCRIPTOR_ENCODING); + dat += USB_STRING_DESCRIPTOR_LENGTH; + + if (myUsbDevice.ep0String.encoding.type == USB_STRING_DESCRIPTOR_UTF16LE_PACKED) + { + myUsbDevice.ep0String.encoding.init = true; + } + else + { + myUsbDevice.ep0String.encoding.init = false; + } + } + } + #endif // ( SLAB_USB_NUM_LANGUAGES == 1 ) + } + + // If there is a descriptor to send, get the proper length, then call + // EP0_Write() to send. + if (length) + { + if (length > myUsbDevice.setup.wLength) + { + length = myUsbDevice.setup.wLength; + } + + EP0_Write(dat, length); + + retVal = USB_STATUS_OK; + } + } + + return retVal; +} + +/***************************************************************************//** + * @brief Sends the current interface alternate setting + * @details Sends 0x0000 if alternate interfaces are not supported. + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +static USB_Status_TypeDef GetInterface(void) +{ + uint16_t interface = myUsbDevice.setup.wIndex; + USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR; + + if ((interface < SLAB_USB_NUM_INTERFACES) + && (myUsbDevice.setup.wLength == 1) + && (myUsbDevice.setup.wValue == 0) + && (*((uint8_t *)&myUsbDevice.setup.bmRequestType) == + (USB_SETUP_DIR_D2H | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_INTERFACE))) + { + if (myUsbDevice.state == USBD_STATE_CONFIGURED) + { +#if (SLAB_USB_SUPPORT_ALT_INTERFACES) + // Return the alternate setting for the specified interface + EP0_Write((SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))&myUsbDevice.interfaceAltSetting[interface], 1); +#else + // Alternate interfaces are not supported, so return 0x0000. + EP0_Write((SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))&txZero, 1); +#endif + retVal = USB_STATUS_OK; + } + } + return retVal; +} + +/***************************************************************************//** + * @brief Sends the requested Remote Wakeup, Self-Powered, or + * Endpoint Status + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +static USB_Status_TypeDef GetStatus(void) +{ + USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR; + + if ((myUsbDevice.setup.wLength == 2) + && (myUsbDevice.setup.wValue == 0) + && (myUsbDevice.setup.bmRequestType.Direction == USB_SETUP_DIR_IN) + && (myUsbDevice.state >= USBD_STATE_ADDRESSED)) + { + pStatus = htole16(0); // Default return value is 0x0000 + + switch (myUsbDevice.setup.bmRequestType.Recipient) + { + case USB_SETUP_RECIPIENT_DEVICE: + if (myUsbDevice.setup.wIndex == 0) + { + #if SLAB_USB_REMOTE_WAKEUP_ENABLED + // Remote wakeup feature status + if (myUsbDevice.remoteWakeupEnabled) + { + pStatus |= htole16(REMOTE_WAKEUP_ENABLED); + } + #endif // SLAB_USB_REMOTE_WAKEUP_ENABLED + + #if SLAB_USB_IS_SELF_POWERED_CB + // Current self/bus power status + if (USBD_IsSelfPoweredCb()) + { + pStatus |= htole16(DEVICE_IS_SELFPOWERED); + } + #elif (SLAB_USB_BUS_POWERED == 0) + pStatus |= htole16(DEVICE_IS_SELFPOWERED); + #endif // SLAB_USB_IS_SELF_POWERED_CB + + retVal = USB_STATUS_OK; + } + break; + + case USB_SETUP_RECIPIENT_INTERFACE: + if (myUsbDevice.setup.wIndex < SLAB_USB_NUM_INTERFACES) + { + retVal = USB_STATUS_OK; + } + break; + + + case USB_SETUP_RECIPIENT_ENDPOINT: + // Device does not support halting endpoint 0, but do not give + // an error as this is a valid request + if (((myUsbDevice.setup.wIndex & ~USB_EP_DIR_IN) == 0) + && (myUsbDevice.state == USBD_STATE_ADDRESSED)) + { + retVal = USB_STATUS_OK; + } + else if (myUsbDevice.state == USBD_STATE_CONFIGURED) + { + switch (myUsbDevice.setup.wIndex & 0xFF) + { + #if SLAB_USB_EP1OUT_USED + case (USB_EP_DIR_OUT | 1): + if (myUsbDevice.ep1out.state == D_EP_HALT) + { + pStatus = htole16(1); + } + retVal = USB_STATUS_OK; + break; + #endif + #if SLAB_USB_EP2OUT_USED + case (USB_EP_DIR_OUT | 2): + if (myUsbDevice.ep2out.state == D_EP_HALT) + { + pStatus = htole16(1); + } + retVal = USB_STATUS_OK; + break; + #endif + #if SLAB_USB_EP3OUT_USED + case (USB_EP_DIR_OUT | 3): + if (myUsbDevice.ep3out.state == D_EP_HALT) + { + pStatus = htole16(1); + } + retVal = USB_STATUS_OK; + break; + #endif + #if SLAB_USB_EP1IN_USED + case (USB_EP_DIR_IN | 1): + if (myUsbDevice.ep1in.state == D_EP_HALT) + { + pStatus = htole16(1); + } + retVal = USB_STATUS_OK; + break; + #endif + #if SLAB_USB_EP2IN_USED + case (USB_EP_DIR_IN | 2): + if (myUsbDevice.ep2in.state == D_EP_HALT) + { + pStatus = htole16(1); + } + retVal = USB_STATUS_OK; + break; + #endif + #if SLAB_USB_EP3IN_USED + case (USB_EP_DIR_IN | 3): + if (myUsbDevice.ep3in.state == D_EP_HALT) + { + pStatus = htole16(1); + } + retVal = USB_STATUS_OK; + break; + #endif + } + } + break; + } + + // If the command was valid, send the requested status. + if (retVal == USB_STATUS_OK) + { + EP0_Write((SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))&pStatus, 2); + } + } + + return retVal; +} + +/***************************************************************************//** + * @brief Sets the Address + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +static USB_Status_TypeDef SetAddress(void) +{ + USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR; + + if ((myUsbDevice.setup.wValue < 128) + && (myUsbDevice.setup.wLength == 0) + && (myUsbDevice.setup.bmRequestType.Recipient == USB_SETUP_RECIPIENT_DEVICE) + && (myUsbDevice.setup.wIndex == 0)) + { + // If the device is in the Default state and the address is non-zero, put + // the device in the Addressed state. + if (myUsbDevice.state == USBD_STATE_DEFAULT) + { + if (myUsbDevice.setup.wValue != 0) + { + USBD_SetUsbState(USBD_STATE_ADDRESSED); + } + retVal = USB_STATUS_OK; + } + // If the device is already addressed and the address is zero, put the + // device in the Default state. + else if (myUsbDevice.state == USBD_STATE_ADDRESSED) + { + if (myUsbDevice.setup.wValue == 0) + { + USBD_SetUsbState(USBD_STATE_DEFAULT); + } + retVal = USB_STATUS_OK; + } + + // Set the new address if the request was valid. + if (retVal == USB_STATUS_OK) + { + USB_SetAddress(myUsbDevice.setup.wValue); + } + } + + return retVal; +} + +/***************************************************************************//** + * @brief Sets the Configuration + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +static USB_Status_TypeDef SetConfiguration(void) +{ + USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR; + + if (((myUsbDevice.setup.wValue >> 8) == 0) + && (myUsbDevice.setup.bmRequestType.Recipient == USB_SETUP_RECIPIENT_DEVICE) + && (myUsbDevice.setup.wLength == 0) + && (myUsbDevice.setup.wIndex == 0)) + { + // If the device is in the Addressed state and a valid Configuration value + // was sent, enter the Configured state. + if (myUsbDevice.state == USBD_STATE_ADDRESSED) + { + if ((myUsbDevice.setup.wValue == 0) + || (myUsbDevice.setup.wValue == myUsbDevice.configDescriptor->bConfigurationValue)) + { + myUsbDevice.configurationValue = myUsbDevice.setup.wValue; + if (myUsbDevice.setup.wValue == myUsbDevice.configDescriptor->bConfigurationValue) + { + USBD_ActivateAllEps(true); + USBD_SetUsbState(USBD_STATE_CONFIGURED); + } + retVal = USB_STATUS_OK; + } + } + // If the device is in the Configured state and Configuration zero is sent, + // abort all transfer and enter the Addressed state. + else if (myUsbDevice.state == USBD_STATE_CONFIGURED) + { + if ((myUsbDevice.setup.wValue == 0) + || (myUsbDevice.setup.wValue == myUsbDevice.configDescriptor->bConfigurationValue)) + { + myUsbDevice.configurationValue = myUsbDevice.setup.wValue; + if (myUsbDevice.setup.wValue == 0) + { + USBD_SetUsbState(USBD_STATE_ADDRESSED); + USBD_AbortAllTransfers(); + } + else + { + // Reenable device endpoints, will reset data toggles + USBD_ActivateAllEps(false); + } + retVal = USB_STATUS_OK; + } + } + } + + return retVal; +} + +/***************************************************************************//** + * @brief Sets the Remote Wakeup or Endpoint Halt Feature + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +static USB_Status_TypeDef SetFeature(void) +{ + USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR; + + if (myUsbDevice.setup.wLength == 0) + { + switch (myUsbDevice.setup.bmRequestType.Recipient) + { + #if SLAB_USB_REMOTE_WAKEUP_ENABLED + case USB_SETUP_RECIPIENT_DEVICE: + if ((myUsbDevice.setup.wIndex == 0) // ITF no. 0 + && (myUsbDevice.setup.wValue == USB_FEATURE_DEVICE_REMOTE_WAKEUP) + && (myUsbDevice.state == USBD_STATE_CONFIGURED)) + { + myUsbDevice.remoteWakeupEnabled = true; + retVal = USB_STATUS_OK; + } + break; + #endif // SLAB_USB_REMOTE_WAKEUP_ENABLED + case USB_SETUP_RECIPIENT_ENDPOINT: + // Device does not support halting endpoint 0, but do not return + // an error as this is a valid request + if (((myUsbDevice.setup.wIndex & ~USB_EP_DIR_IN) == 0) + && (myUsbDevice.state >= USBD_STATE_ADDRESSED)) + { + retVal = USB_STATUS_OK; + } + else if ((((myUsbDevice.setup.wIndex) & ~USB_SETUP_DIR_D2H) < SLAB_USB_NUM_EPS_USED) + && (myUsbDevice.setup.wValue == USB_FEATURE_ENDPOINT_HALT) + && (myUsbDevice.state == USBD_STATE_CONFIGURED)) + { + retVal = USB_STATUS_OK; + USB_SetIndex((myUsbDevice.setup.wIndex & 0xFF) & ~USB_SETUP_DIR_D2H); + + // Enable Stalls on the specified endpoint. +#if (SLAB_USB_EP1IN_USED || SLAB_USB_EP2IN_USED || SLAB_USB_EP3IN_USED) + if ((myUsbDevice.setup.wIndex & 0xFF) & USB_EP_DIR_IN) + { + USB_EpnInStall(); + } +#endif +#if (SLAB_USB_EP1OUT_USED || SLAB_USB_EP2OUT_USED || SLAB_USB_EP3OUT_USED) + if (((myUsbDevice.setup.wIndex & 0xFF) & USB_EP_DIR_IN) == 0) + { + USB_EpnOutStall(); + } +#endif + + // Put the specified endpoint in the Halted state. + switch (myUsbDevice.setup.wIndex & 0xFF) + { + #if SLAB_USB_EP1OUT_USED + case (USB_EP_DIR_OUT | 1): + myUsbDevice.ep1out.state = D_EP_HALT; + break; + #endif + #if SLAB_USB_EP2OUT_USED + case (USB_EP_DIR_OUT | 2): + myUsbDevice.ep2out.state = D_EP_HALT; + break; + #endif + #if SLAB_USB_EP3OUT_USED + case (USB_EP_DIR_OUT | 3): + myUsbDevice.ep3out.state = D_EP_HALT; + break; + #endif + #if SLAB_USB_EP1IN_USED + case (USB_EP_DIR_IN | 1): + myUsbDevice.ep1in.state = D_EP_HALT; + break; + #endif + #if SLAB_USB_EP2IN_USED + case (USB_EP_DIR_IN | 2): + myUsbDevice.ep2in.state = D_EP_HALT; + break; + #endif + #if SLAB_USB_EP3IN_USED + case (USB_EP_DIR_IN | 3): + myUsbDevice.ep3in.state = D_EP_HALT; + break; + #endif + } + } + } + } + + return retVal; +} + +/***************************************************************************//** + * @brief Sets the Interface and Alternate Interface (if supported) + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +static USB_Status_TypeDef SetInterface(void) +{ + USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR; + uint8_t interface = (uint8_t)myUsbDevice.setup.wIndex; + uint8_t altSetting = (uint8_t)myUsbDevice.setup.wValue; + + if ((interface < SLAB_USB_NUM_INTERFACES) + && (myUsbDevice.state == USBD_STATE_CONFIGURED) + && (myUsbDevice.setup.wLength == 0) +#if (SLAB_USB_SUPPORT_ALT_INTERFACES == 0) + && (altSetting == 0) +#endif + && (myUsbDevice.setup.bmRequestType.Recipient == USB_SETUP_RECIPIENT_INTERFACE)) + { +#if (SLAB_USB_SUPPORT_ALT_INTERFACES) + if (USBD_SetInterfaceCb(interface, altSetting) == USB_STATUS_OK) + { + myUsbDevice.interfaceAltSetting[interface] = altSetting; + retVal = USB_STATUS_OK; + } +#else +#if (SLAB_USB_NUM_INTERFACES == 1) + // Reset data toggles on EP's + USBD_ActivateAllEps(false); +#endif // ( SLAB_USB_NUM_INTERFACES == 1 ) + retVal = USB_STATUS_OK; +#endif // ( SLAB_USB_SUPPORT_ALT_INTERFACES ) + } + + return retVal; +} + +// ----------------------------------------------------------------------------- +// Utility Functions + +/***************************************************************************//** + * @brief Enables all endpoints for data transfers + * @return Status of request (type @ref USB_Status_TypeDef) + * @note This function takes no parameters, but it uses the setup command + * stored in @ref myUsbDevice.setup. + ******************************************************************************/ +static void USBD_ActivateAllEps(bool forceIdle) +{ + if (forceIdle == true) + { +#if SLAB_USB_EP1IN_USED + myUsbDevice.ep1in.state = D_EP_IDLE; +#endif +#if SLAB_USB_EP2IN_USED + myUsbDevice.ep2in.state = D_EP_IDLE; +#endif +#if SLAB_USB_EP3IN_USED + myUsbDevice.ep3in.state = D_EP_IDLE; +#endif +#if SLAB_USB_EP1OUT_USED + myUsbDevice.ep1out.state = D_EP_IDLE; +#endif +#if SLAB_USB_EP2OUT_USED + myUsbDevice.ep2out.state = D_EP_IDLE; +#endif +#if SLAB_USB_EP3OUT_USED + myUsbDevice.ep3out.state = D_EP_IDLE; +#endif + } + +#if SLAB_USB_EP1IN_USED + USB_ActivateEp(1, // ep + SLAB_USB_EP1IN_MAX_PACKET_SIZE, // packetSize + 1, // inDir + SLAB_USB_EP1OUT_USED, // splitMode + 0); // isoMod +#endif // SLAB_USB_EP1IN_USED +#if SLAB_USB_EP2IN_USED + USB_ActivateEp(2, // ep + SLAB_USB_EP2IN_MAX_PACKET_SIZE, // packetSize + 1, // inDir + SLAB_USB_EP2OUT_USED, // splitMode + 0); // isoMod +#endif // SLAB_USB_EP2IN_USED +#if SLAB_USB_EP3IN_USED + USB_ActivateEp(3, // ep + SLAB_USB_EP3IN_MAX_PACKET_SIZE, // packetSize + 1, // inDir + SLAB_USB_EP3OUT_USED, // splitMode + (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC)); // isoMod +#endif // SLAB_USB_EP3IN_USED +#if SLAB_USB_EP1OUT_USED + USB_ActivateEp(1, // ep + SLAB_USB_EP1OUT_MAX_PACKET_SIZE, // packetSize + 0, // inDir + SLAB_USB_EP1IN_USED, // splitMode + 0); // isoMod +#endif // SLAB_USB_EP1OUT_USED +#if SLAB_USB_EP2OUT_USED + USB_ActivateEp(2, // ep + SLAB_USB_EP2OUT_MAX_PACKET_SIZE, // packetSize + 0, // inDir + SLAB_USB_EP2IN_USED, // splitMode + 0); // isoMod +#endif // SLAB_USB_EP2OUT_USED +#if SLAB_USB_EP3OUT_USED + USB_ActivateEp(3, // ep + SLAB_USB_EP3OUT_MAX_PACKET_SIZE, // packetSize + 0, // inDir + SLAB_USB_EP3IN_USED, // splitMode + (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC)); // isoMod +#endif // SLAB_USB_EP1OUT_USED +} + +/***************************************************************************//** + * @brief Sets up an Endpoint 0 Write + * @param dat + * Data to transmit on Endpoint 0 + * @param numBytes + * Number of bytes to transmit on Endpoint 0 + ******************************************************************************/ +static void EP0_Write(SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), uint16_t numBytes) +{ + if (myUsbDevice.ep0.state == D_EP_IDLE) + { + myUsbDevice.ep0.buf = dat; + myUsbDevice.ep0.remaining = numBytes; + myUsbDevice.ep0.state = D_EP_TRANSMITTING; + myUsbDevice.ep0.misc.c = 0; + } +} diff --git a/efm8/lib/efm8_usb/src/efm8_usbdep.c b/efm8/lib/efm8_usb/src/efm8_usbdep.c new file mode 100644 index 0000000..2982658 --- /dev/null +++ b/efm8/lib/efm8_usb/src/efm8_usbdep.c @@ -0,0 +1,1028 @@ +/**************************************************************************//** + * Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved. + * + * http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt + *****************************************************************************/ + +#include "si_toolchain.h" +#include "efm8_usb.h" +#include +#include + +extern SI_SEGMENT_VARIABLE(myUsbDevice, USBD_Device_TypeDef, MEM_MODEL_SEG); + +// ----------------------------------------------------------------------------- +// Function Prototypes + +// ------------------------------- +// Memory-specific FIFO access functions +#ifdef SI_GPTR + +static void USB_ReadFIFO_Idata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_IDATA), uint8_t fifoNum); +static void USB_WriteFIFO_Idata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_IDATA)); + +static void USB_ReadFIFO_Xdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_XDATA), uint8_t fifoNum); +static void USB_WriteFIFO_Xdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_XDATA)); + +#if SI_GPTR_MTYPE_PDATA != SI_GPTR_MTYPE_XDATA +static void USB_ReadFIFO_Pdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_PDATA), uint8_t fifoNum); +static void USB_WriteFIFO_Pdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_PDATA)); +#endif + +#if SI_GPTR_MTYPE_DATA != SI_GPTR_MTYPE_IDATA +static void USB_ReadFIFO_Data(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_DATA), uint8_t fifoNum); +static void USB_WriteFIFO_Data(uint8_t numBytes, uint8_t SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_DATA)); +#endif + +static void USB_WriteFIFO_Code(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_CODE)); + +#else + +// ------------------------------- +// Generic FIFO access functions +static void USB_ReadFIFO_Generic(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), uint8_t fifoNum); +static void USB_WriteFIFO_Generic(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC)); + +#endif // #ifdef SI_GPTR + +#if (SLAB_USB_EP3OUT_USED && (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC)) +static void memclearXdata(SI_VARIABLE_SEGMENT_POINTER(s, uint8_t, SI_SEG_XDATA), + uint16_t n); +#endif + +// ----------------------------------------------------------------------------- +// Functions + +/***************************************************************************//** + * @brief Reads Isochronous data from the Endpoint FIFO + * @param fifoNum + * USB Endpoint FIFO to read + * @param numBytes + * Number of bytes to read from the FIFO + * @param dat + * Pointer to buffer to hold data read from the FIFO + ******************************************************************************/ +#if (SLAB_USB_EP3OUT_USED && (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC) && (SLAB_USB_EP3OUT_MAX_PACKET_SIZE > 255)) +// ---------------------------------------------------------------------------- +// If Isochronous mode is enabled and the max packet size is greater than 255, +// break the FIFO reads up into multiple reads of 255 or less bytes. +// ---------------------------------------------------------------------------- +void USB_ReadFIFOIso(uint8_t fifoNum, uint16_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC)) +{ + uint8_t numBytesRead; + + // USB_ReadFIFO() accepts a maximum of 255 bytes. If the number of bytes to + // send is greated than 255, call USB_ReadFIFO() multiple times. + while (numBytes > 0) + { + numBytesRead = (numBytes > 255) ? 255 : numBytes; + USB_ReadFIFO(fifoNum, numBytesRead, dat); + numBytes -= numBytesRead; + dat += numBytesRead; + } +} +#else +#define USB_ReadFIFOIso(a, b, c) USB_ReadFIFO(a, b, c) +#endif + +/***************************************************************************//** + * @brief Writes Isochronous data to the Endpoint FIFO + * @param fifoNum + * USB Endpoint FIFO to write + * @param numBytes + * Number of bytes to write to the FIFO + * @param dat + * Pointer to buffer hoding data to write to the FIFO + ******************************************************************************/ +#if (SLAB_USB_EP3IN_USED && (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC) && (SLAB_USB_EP3IN_MAX_PACKET_SIZE > 255)) +// ---------------------------------------------------------------------------- +// If Isochronous mode is enabled and the max packet size is greater than 255, +// break the FIFO writes up into multiple writes of 255 or less bytes. +// ---------------------------------------------------------------------------- +void USB_WriteFIFOIso(uint8_t fifoNum, uint16_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC)) +{ + uint8_t numBytesWrite; + + // USB_WriteFIFO() accepts a maximum of 255 bytes. If the number of bytes to + // send is greated than 255, call USB_WriteFIFO() multiple times. + while (numBytes > 0) + { + numBytesWrite = (numBytes > 255) ? 255 : numBytes; + numBytes -= numBytesWrite; + USB_WriteFIFO(fifoNum, numBytesWrite, dat, (numBytes == 0)); + dat += numBytesWrite; + } +} +#else +#define USB_WriteFIFOIso(a, b, c) USB_WriteFIFO(a, b, c, true) +#endif + +#if SLAB_USB_EP1IN_USED +/***************************************************************************//** + * @brief Handle Endpoint 1 IN transfer interrupt + * @note This function takes no parameters, but it uses the EP1IN status + * variables stored in @ref myUsbDevice.ep1in. + ******************************************************************************/ +void handleUsbIn1Int(void) +{ + uint8_t xferred; + bool callback; + + USB_SetIndex(1); + + if (USB_EpnInGetSentStall()) + { + USB_EpnInClearSentStall(); + } + else if (myUsbDevice.ep1in.state == D_EP_TRANSMITTING) + { + xferred = (myUsbDevice.ep1in.remaining > SLAB_USB_EP1IN_MAX_PACKET_SIZE) + ? SLAB_USB_EP1IN_MAX_PACKET_SIZE : myUsbDevice.ep1in.remaining; + myUsbDevice.ep1in.remaining -= xferred; + myUsbDevice.ep1in.buf += xferred; + + callback = myUsbDevice.ep1in.misc.bits.callback; + + // Load more data + if (myUsbDevice.ep1in.remaining > 0) + { + USB_WriteFIFO(1, + (myUsbDevice.ep1in.remaining > SLAB_USB_EP1IN_MAX_PACKET_SIZE) + ? SLAB_USB_EP1IN_MAX_PACKET_SIZE + : myUsbDevice.ep1in.remaining, + myUsbDevice.ep1in.buf, + true); + } + else + { + myUsbDevice.ep1in.misc.bits.callback = false; + myUsbDevice.ep1in.state = D_EP_IDLE; + } + + if (callback == true) + { + USBD_XferCompleteCb(EP1IN, USB_STATUS_OK, xferred, myUsbDevice.ep1in.remaining); + } + + } +} +#endif // SLAB_USB_EP1IN_USED + +#if SLAB_USB_EP2IN_USED +/***************************************************************************//** + * @brief Handle Endpoint 2 IN transfer interrupt + * @note This function takes no parameters, but it uses the EP2IN status + * variables stored in @ref myUsbDevice.ep2in. + ******************************************************************************/ +void handleUsbIn2Int(void) +{ + uint8_t xferred; + bool callback; + + USB_SetIndex(2); + + if (USB_EpnInGetSentStall()) + { + USB_EpnInClearSentStall(); + } + else if (myUsbDevice.ep2in.state == D_EP_TRANSMITTING) + { + xferred = (myUsbDevice.ep2in.remaining > SLAB_USB_EP2IN_MAX_PACKET_SIZE) + ? SLAB_USB_EP2IN_MAX_PACKET_SIZE : myUsbDevice.ep2in.remaining; + myUsbDevice.ep2in.remaining -= xferred; + myUsbDevice.ep2in.buf += xferred; + + callback = myUsbDevice.ep2in.misc.bits.callback; + + // Load more data + if (myUsbDevice.ep2in.remaining > 0) + { + USB_WriteFIFO(2, + (myUsbDevice.ep2in.remaining > SLAB_USB_EP2IN_MAX_PACKET_SIZE) + ? SLAB_USB_EP2IN_MAX_PACKET_SIZE + : myUsbDevice.ep2in.remaining, + myUsbDevice.ep2in.buf, + true); + } + else + { + myUsbDevice.ep2in.misc.bits.callback = false; + myUsbDevice.ep2in.state = D_EP_IDLE; + } + + if (callback == true) + { + USBD_XferCompleteCb(EP2IN, USB_STATUS_OK, xferred, myUsbDevice.ep2in.remaining); + } + + } +} +#endif // SLAB_USB_EP2IN_USED + +#if SLAB_USB_EP3IN_USED +/***************************************************************************//** + * @brief Handle Endpoint 3 IN transfer interrupt + * @details Endpoint 3 IN is the only IN endpoint that supports isochronous + * transfers. + * @note This function takes no parameters, but it uses the EP3IN status + * variables stored in @ref myUsbDevice.ep3in. + ******************************************************************************/ +void handleUsbIn3Int(void) +{ +#if SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC + uint16_t xferred, nextIdx; +#else + uint8_t xferred; + bool callback; +#endif + + USB_SetIndex(3); + + if (USB_EpnInGetSentStall()) + { + USB_EpnInClearSentStall(); + } + else if (myUsbDevice.ep3in.state == D_EP_TRANSMITTING) + { +#if ((SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_INTR)) + xferred = (myUsbDevice.ep3in.remaining > SLAB_USB_EP3IN_MAX_PACKET_SIZE) + ? SLAB_USB_EP3IN_MAX_PACKET_SIZE : myUsbDevice.ep3in.remaining; + myUsbDevice.ep3in.remaining -= xferred; + myUsbDevice.ep3in.buf += xferred; +#endif + +#if ((SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_INTR)) + + callback = myUsbDevice.ep3in.misc.bits.callback; + +#elif (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC) + if (myUsbDevice.ep3in.misc.bits.callback == true) + { + // In Isochronous mode, the meaning of the USBD_XferCompleteCb parameters changes: + // xferred is ignored + // remaining is the current index into the circular buffer + // the return value is the number of bytes to transmit in the next packet + xferred = USBD_XferCompleteCb(EP3IN, USB_STATUS_OK, 0, myUsbDevice.ep3inIsoIdx); + if (xferred == 0) + { + myUsbDevice.ep3in.misc.bits.inPacketPending = true; + return; + } + } +#endif + // Load more data +#if ((SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_INTR)) + if (myUsbDevice.ep3in.remaining > 0) + { + USB_WriteFIFO(3, + (myUsbDevice.ep3in.remaining > SLAB_USB_EP3IN_MAX_PACKET_SIZE) + ? SLAB_USB_EP3IN_MAX_PACKET_SIZE + : myUsbDevice.ep3in.remaining, + myUsbDevice.ep3in.buf, + true); + } + else + { + myUsbDevice.ep3in.misc.bits.callback = false; + myUsbDevice.ep3in.state = D_EP_IDLE; + } + + if (callback == true) + { + USBD_XferCompleteCb(EP3IN, USB_STATUS_OK, xferred, myUsbDevice.ep3in.remaining); + } +#elif (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC) + nextIdx = xferred + myUsbDevice.ep3inIsoIdx; + myUsbDevice.ep3in.misc.bits.inPacketPending = false; + + // Check if the next index is past the end of the circular buffer. + // If so, break the write up into two calls to USB_WriteFIFOIso() + if (nextIdx > myUsbDevice.ep3in.remaining) + { + USB_WriteFIFOIso(3, myUsbDevice.ep3in.remaining - myUsbDevice.ep3inIsoIdx, &myUsbDevice.ep3in.buf[myUsbDevice.ep3inIsoIdx]); + myUsbDevice.ep3inIsoIdx = nextIdx - myUsbDevice.ep3in.remaining; + USB_WriteFIFOIso(3, myUsbDevice.ep3inIsoIdx, myUsbDevice.ep3in.buf); + } + else + { + USB_WriteFIFOIso(3, xferred, &myUsbDevice.ep3in.buf[myUsbDevice.ep3inIsoIdx]); + myUsbDevice.ep3inIsoIdx = nextIdx; + } +#endif // ( ( SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_BULK ) || ( SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_INTR ) ) + } +} +#endif // SLAB_USB_EP3IN_USED + +#if SLAB_USB_EP1OUT_USED +/***************************************************************************//** + * @brief Handle Endpoint 1 OUT transfer interrupt + * @note This function takes no parameters, but it uses the EP1OUT status + * variables stored in @ref myUsbDevice.ep1out. + ******************************************************************************/ +void handleUsbOut1Int(void) +{ + uint8_t count; + USB_Status_TypeDef status; + bool xferComplete = false; + + USB_SetIndex(1); + + if (USB_EpnOutGetSentStall()) + { + USB_EpnOutClearSentStall(); + } + else if (USB_EpnGetOutPacketReady()) + { + count = USB_EpOutGetCount(); + + // If USBD_Read() has not been called, return an error + if (myUsbDevice.ep1out.state != D_EP_RECEIVING) + { + myUsbDevice.ep1out.misc.bits.outPacketPending = true; + status = USB_STATUS_EP_ERROR; + } + // Check for overrun of user buffer + else if (myUsbDevice.ep1out.remaining < count) + { + myUsbDevice.ep1out.state = D_EP_IDLE; + myUsbDevice.ep1out.misc.bits.outPacketPending = true; + status = USB_STATUS_EP_RX_BUFFER_OVERRUN; + } + else + { + USB_ReadFIFO(1, count, myUsbDevice.ep1out.buf); + + myUsbDevice.ep1out.misc.bits.outPacketPending = false; + myUsbDevice.ep1out.remaining -= count; + myUsbDevice.ep1out.buf += count; + + if ((myUsbDevice.ep1out.remaining == 0) || (count != SLAB_USB_EP1OUT_MAX_PACKET_SIZE)) + { + myUsbDevice.ep1out.state = D_EP_IDLE; + xferComplete = true; + } + + status = USB_STATUS_OK; + USB_EpnClearOutPacketReady(); + } + if (myUsbDevice.ep1out.misc.bits.callback == true) + { + if (xferComplete == true) + { + myUsbDevice.ep1out.misc.bits.callback = false; + } + + USBD_XferCompleteCb(EP1OUT, status, count, myUsbDevice.ep1out.remaining); + } + } +} +#endif // EP1OUT_USED + +#if SLAB_USB_EP2OUT_USED +/***************************************************************************//** + * @brief Handle Endpoint 2 OUT transfer interrupt + * @note This function takes no parameters, but it uses the EP2OUT status + * variables stored in @ref myUsbDevice.ep2out. + ******************************************************************************/ +void handleUsbOut2Int(void) +{ + uint8_t count; + USB_Status_TypeDef status; + bool xferComplete = false; + + USB_SetIndex(2); + + if (USB_EpnOutGetSentStall()) + { + USB_EpnOutClearSentStall(); + } + else if (USB_EpnGetOutPacketReady()) + { + count = USB_EpOutGetCount(); + + // If USBD_Read() has not been called, return an error + if (myUsbDevice.ep2out.state != D_EP_RECEIVING) + { + myUsbDevice.ep2out.misc.bits.outPacketPending = true; + status = USB_STATUS_EP_ERROR; + } + // Check for overrun of user buffer + else if (myUsbDevice.ep2out.remaining < count) + { + myUsbDevice.ep2out.state = D_EP_IDLE; + myUsbDevice.ep2out.misc.bits.outPacketPending = true; + status = USB_STATUS_EP_RX_BUFFER_OVERRUN; + } + else + { + USB_ReadFIFO(2, count, myUsbDevice.ep2out.buf); + + myUsbDevice.ep2out.misc.bits.outPacketPending = false; + myUsbDevice.ep2out.remaining -= count; + myUsbDevice.ep2out.buf += count; + + if ((myUsbDevice.ep2out.remaining == 0) || (count != SLAB_USB_EP2OUT_MAX_PACKET_SIZE)) + { + myUsbDevice.ep2out.state = D_EP_IDLE; + xferComplete = true; + } + + status = USB_STATUS_OK; + USB_EpnClearOutPacketReady(); + } + if (myUsbDevice.ep2out.misc.bits.callback == true) + { + if (xferComplete == true) + { + myUsbDevice.ep2out.misc.bits.callback = false; + } + + USBD_XferCompleteCb(EP2OUT, status, count, myUsbDevice.ep2out.remaining); + } + } +} +#endif // EP2OUT_USED + +#if SLAB_USB_EP3OUT_USED +/***************************************************************************//** + * @brief Handle Endpoint 3 OUT transfer interrupt + * @details Endpoint 3 OUT is the only OUT endpoint that supports + * isochronous transfers. + * @note This function takes no parameters, but it uses the EP3OUT status + * variables stored in @ref myUsbDevice.ep3out. + ******************************************************************************/ +#if ((SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_INTR)) +void handleUsbOut3Int(void) +{ + uint8_t count; + USB_Status_TypeDef status; + bool xferComplete = false; + + USB_SetIndex(3); + + if (USB_EpnOutGetSentStall()) + { + USB_EpnOutClearSentStall(); + } + else if (USB_EpnGetOutPacketReady()) + { + count = USB_EpOutGetCount(); + + // If USBD_Read() has not been called, return an error + if (myUsbDevice.ep3out.state != D_EP_RECEIVING) + { + myUsbDevice.ep3out.misc.bits.outPacketPending = true; + status = USB_STATUS_EP_ERROR; + } + // Check for overrun of user buffer + else if (myUsbDevice.ep3out.remaining < count) + { + myUsbDevice.ep3out.state = D_EP_IDLE; + myUsbDevice.ep3out.misc.bits.outPacketPending = true; + status = USB_STATUS_EP_RX_BUFFER_OVERRUN; + } + else + { + USB_ReadFIFO(3, count, myUsbDevice.ep3out.buf); + + myUsbDevice.ep3out.misc.bits.outPacketPending = false; + myUsbDevice.ep3out.remaining -= count; + myUsbDevice.ep3out.buf += count; + + if ((myUsbDevice.ep3out.remaining == 0) || (count != SLAB_USB_EP3OUT_MAX_PACKET_SIZE)) + { + myUsbDevice.ep3out.state = D_EP_IDLE; + xferComplete = true; + } + + status = USB_STATUS_OK; + USB_EpnClearOutPacketReady(); + } + if (myUsbDevice.ep3out.misc.bits.callback == true) + { + if (xferComplete == true) + { + myUsbDevice.ep3out.misc.bits.callback = false; + } + + USBD_XferCompleteCb(EP3OUT, status, count, myUsbDevice.ep3out.remaining); + } + } +} + +#elif (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC) +void handleUsbOut3Int(void) +{ + uint16_t nextIdx; + uint16_t numZeroBytesFromCb; +#if (SLAB_USB_EP3OUT_MAX_PACKET_SIZE > 255) + uint16_t count; +#else + uint8_t count; +#endif + USB_Status_TypeDef status = USB_STATUS_OK; + bool xferComplete = false; + + USB_SetIndex(3); + + if (USB_EpnOutGetSentStall()) + { + USB_EpnOutClearSentStall(); + } + else if (USB_EpnGetOutPacketReady()) + { + count = USB_EpOutGetCount(); + + // If USBD_Read() has not been called, return an error + if (myUsbDevice.ep3out.state != D_EP_RECEIVING) + { + myUsbDevice.ep3out.misc.bits.outPacketPending = true; + status = USB_STATUS_EP_ERROR; + } + else + { + // DATERR bit set (i.e. CRC/bit-stuffing error) + if (USB_EpnGetDataError() + #ifdef SLAB_USB_ISOC_OUT_MIN_PACKET_SIZE + || (count < SLAB_USB_ISOC_OUT_MIN_PACKET_SIZE) + #endif + #ifdef SLAB_USB_ISOC_OUT_MAX_PACKET_SIZE + || (count > SLAB_USB_ISOC_OUT_MAX_PACKET_SIZE) + #endif + ) + { + status = USB_STATUS_DATA_ERROR; + } + +#ifdef SLAB_USB_ISOC_OUT_PACKETSIZE_MOD2 + if ((count % 2) != 0) + { + status = USB_STATUS_DATA_ERROR; + } +#elif defined SLAB_USB_ISOC_OUT_PACKETSIZE_MOD4 + if (( count % 4) != 0) + { + status = USB_STATUS_DATA_ERROR; + } +#elif defined SLAB_USB_ISOC_OUT_PACKETSIZE_MOD6 + if (count % 6) != 0) + { + status = USB_STATUS_DATA_ERROR; + } +#endif + + if (status == USB_STATUS_DATA_ERROR) + { + count = 0; + // Flush FIFO to get rid of bad packet + USB_EpnOutFlush(); + myUsbDevice.ep3out.misc.bits.outPacketPending = false; + // Flush clears OPRDY, so no need to call USB_EpnClearOutPacketReady() now + } + else // No data error + { + nextIdx = count + myUsbDevice.ep3outIsoIdx; + + // In isochronous mode, a circular buffer is used to hold the data + // If the next index into the circular buffer passes the end of the + // buffer, make two calls to USB_ReadFIFOIso() + if (nextIdx > myUsbDevice.ep3out.remaining) + { + USB_ReadFIFOIso(3, myUsbDevice.ep3out.remaining - myUsbDevice.ep3outIsoIdx, &myUsbDevice.ep3out.buf[myUsbDevice.ep3outIsoIdx]); + myUsbDevice.ep3outIsoIdx = nextIdx - myUsbDevice.ep3out.remaining; + USB_ReadFIFOIso(3, myUsbDevice.ep3outIsoIdx, myUsbDevice.ep3out.buf); + } + else + { + USB_ReadFIFOIso(3, count, &myUsbDevice.ep3out.buf[myUsbDevice.ep3outIsoIdx]); + myUsbDevice.ep3outIsoIdx = nextIdx; + } + + myUsbDevice.ep3out.misc.bits.outPacketPending = false; + USB_EpnClearOutPacketReady(); + } + } + + if (myUsbDevice.ep3out.misc.bits.callback == true) + { + if (xferComplete == true) + { + myUsbDevice.ep3out.misc.bits.callback = false; + } + + // In Isochronous mode, the meaning of the USBD_XferCompleteCb parameters changes: + // xferred is the number of bytes received in the last packet + // remaining is the current index into the circular buffer + numZeroBytesFromCb = USBD_XferCompleteCb(EP3OUT, status, count, myUsbDevice.ep3outIsoIdx); + + // If data error occurred, the callback return value specifies how many zero-valued bytes to queue + if (numZeroBytesFromCb && (status == USB_STATUS_DATA_ERROR)) + { + uint16_t numZeroBytesToWrite; + SI_SEGMENT_VARIABLE_SEGMENT_POINTER(bufPtr, + uint8_t, + SI_SEG_XDATA, + SI_SEG_DATA); + + // Clear status after calling USBD_XferCompleteCb() + status = USB_STATUS_OK; + + // Add the specified number of zero-value bytes + nextIdx = numZeroBytesFromCb + myUsbDevice.ep3outIsoIdx; + + // Next index is past the end of the buffer (requires two writes) + if (nextIdx > myUsbDevice.ep3out.remaining) + { + // Write up to the end of the buffer + numZeroBytesToWrite = myUsbDevice.ep3out.remaining - myUsbDevice.ep3outIsoIdx; + bufPtr = &myUsbDevice.ep3out.buf[myUsbDevice.ep3outIsoIdx]; + memclearXdata(bufPtr, numZeroBytesToWrite); + + // Write the rest, starting at beginning of buffer + myUsbDevice.ep3outIsoIdx = nextIdx - myUsbDevice.ep3out.remaining; + numZeroBytesToWrite = myUsbDevice.ep3outIsoIdx; + bufPtr = &myUsbDevice.ep3out.buf[0]; + memclearXdata(bufPtr, numZeroBytesToWrite); + } + // Next index is not past the end of the buffer + else + { + bufPtr = &myUsbDevice.ep3out.buf[myUsbDevice.ep3outIsoIdx]; + memclearXdata(bufPtr, numZeroBytesFromCb); + myUsbDevice.ep3outIsoIdx = nextIdx; + } + } + } + } +} + +/***************************************************************************//** + * @brief Sets all elements in a contiguous array of XDATA to zero + * @param s + * Pointer to the block of memory to fill + * @param n + * Number of bytes to be set to the value + ******************************************************************************/ +static void memclearXdata(SI_VARIABLE_SEGMENT_POINTER(s, uint8_t, SI_SEG_XDATA), + uint16_t n) +{ + while (n) + { + *s++ = 0; + n--; + } +} + +#endif // #if ((SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_BULK) || (SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_INTR)) +#endif // EP3OUT_USED + +/***************************************************************************//** + * @brief Reads data from the USB FIFO + * @param fifoNum + * USB Endpoint FIFO to read + * @param numBytes + * Number of bytes to read from the FIFO + * @param dat + * Pointer to buffer to hold data read from the FIFO + ******************************************************************************/ +void USB_ReadFIFO(uint8_t fifoNum, uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC)) +{ + if (numBytes > 0) + { + USB_EnableReadFIFO(fifoNum); + + // Convert generic pointer to memory-specific pointer and call the + // the corresponding memory-specific function, if possible. + // The memory-specific functions are much faster than the generic functions. +#ifdef SI_GPTR + + switch (((SI_GEN_PTR_t *)&dat)->gptr.memtype) + { + case SI_GPTR_MTYPE_IDATA: + USB_ReadFIFO_Idata(numBytes, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_IDATA))dat, fifoNum); + break; + + // For some compilers, IDATA and DATA are treated the same. + // Only call the USB_ReadFIFO_Data() if the compiler differentiates + // between DATA and IDATA. +#if (SI_GPTR_MTYPE_DATA != SI_GPTR_MTYPE_IDATA) + case SI_GPTR_MTYPE_DATA: + USB_ReadFIFO_Data(numBytes, dat, fifoNum); + break; +#endif + + case SI_GPTR_MTYPE_XDATA: + USB_ReadFIFO_Xdata(numBytes, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_XDATA))dat, fifoNum); + break; + + // For some compilers, XDATA and PDATA are treated the same. + // Only call the USB_ReadFIFO_Pdata() if the compiler differentiates + // between XDATA and PDATA. +#if (SI_GPTR_MTYPE_PDATA != SI_GPTR_MTYPE_XDATA) + case SI_GPTR_MTYPE_PDATA: + USB_ReadFIFO_Pdata(numBytes, dat, fifoNum); + break; +#endif + + default: + break; + } + +#else + USB_ReadFIFO_Generic(numBytes, dat, fifoNum); +#endif // #ifdef SI_GPTR + + USB_DisableReadFIFO(fifoNum); + } +} + +/***************************************************************************//** + * @brief Writes data to the USB FIFO + * @param fifoNum + * USB Endpoint FIFO to write + * @param numBytes + * Number of bytes to write to the FIFO + * @param dat + * Pointer to buffer hoding data to write to the FIFO + * @param txPacket + * If TRUE, the packet will be sent immediately after loading the + * FIFO + * If FALSE, the packet will be stored in the FIFO and the + * transmission must be started at a later time + ******************************************************************************/ +void USB_WriteFIFO(uint8_t fifoNum, uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), bool txPacket) +{ + USB_EnableWriteFIFO(fifoNum); + + // Convert generic pointer to memory-specific pointer and call the + // the corresponding memory-specific function, if possible. + // The memory-specific functions are much faster than the generic functions. +#ifdef SI_GPTR + + switch (((SI_GEN_PTR_t *)&dat)->gptr.memtype) + { + case SI_GPTR_MTYPE_IDATA: + USB_WriteFIFO_Idata(numBytes, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_IDATA))dat); + break; + + // For some compilers, IDATA and DATA are treated the same. + // Only call the USB_WriteFIFO_Data() if the compiler differentiates between + // DATA and IDATA. +#if (SI_GPTR_MTYPE_DATA != SI_GPTR_MTYPE_IDATA) + case SI_GPTR_MTYPE_DATA: + USB_WriteFIFO_Data(numBytes, dat); + break; +#endif + + case SI_GPTR_MTYPE_XDATA: + USB_WriteFIFO_Xdata(numBytes, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_XDATA))dat); + break; + + // For some compilers, XDATA and PDATA are treated the same. + // Only call the USB_WriteFIFO_Pdata() if the compiler differentiates + // between XDATA and PDATA. +#if (SI_GPTR_MTYPE_PDATA != SI_GPTR_MTYPE_XDATA) + case SI_GPTR_MTYPE_PDATA: + USB_WriteFIFO_Pdata(numBytes, dat); + break; +#endif + + case SI_GPTR_MTYPE_CODE: + USB_WriteFIFO_Code(numBytes, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_CODE))dat); + break; + + default: + break; + } + +#else + USB_WriteFIFO_Generic(numBytes, dat); +#endif // #ifdef SI_GPTR + + USB_DisableWriteFIFO(fifoNum); + + if ((txPacket == true) && (fifoNum > 0)) + { + USB_SetIndex(fifoNum); + USB_EpnSetInPacketReady(); + } +} + +// ----------------------------------------------------------------------------- +// Memory-Specific FIFO Access Functions +// +// Memory-specific functions are much faster (more than 2x) than generic +// generic functions, so we will use memory-specific functions if possible. +// ----------------------------------------------------------------------------- + +#ifdef SI_GPTR +/***************************************************************************//** + * @brief Reads data from the USB FIFO to a buffer in IRAM + * @param numBytes + * Number of bytes to read from the FIFO + * @param dat + * Pointer to IDATA buffer to hold data read from the FIFO + * @param fifoNum + * USB FIFO to read + ******************************************************************************/ +static void USB_ReadFIFO_Idata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_IDATA), uint8_t fifoNum) +{ + while (--numBytes) + { + USB_GetFIFOByte(dat); + dat++; + } + USB_GetLastFIFOByte(dat, fifoNum); +} + +/***************************************************************************//** + * @brief Writes data held in IRAM to the USB FIFO + * @details The FIFO to write must be set before calling the function with + * @ref USB_EnableWriteFIFO(). + * @param numBytes + * Number of bytes to write to the FIFO + * @param dat + * Pointer to IDATA buffer holding data to write to the FIFO + ******************************************************************************/ +static void USB_WriteFIFO_Idata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_IDATA)) +{ + while (numBytes--) + { + USB_SetFIFOByte(*dat); + dat++; + } +} + +/***************************************************************************//** + * @brief Reads data from the USB FIFO to a buffer in XRAM + * @param numBytes + * Number of bytes to read from the FIFO + * @param dat + * Pointer to XDATA buffer to hold data read from the FIFO + * @param fifoNum + * USB FIFO to read + ******************************************************************************/ +static void USB_ReadFIFO_Xdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_XDATA), uint8_t fifoNum) +{ + while (--numBytes) + { + USB_GetFIFOByte(dat); + dat++; + } + USB_GetLastFIFOByte(dat, fifoNum); +} + +/***************************************************************************//** + * @brief Writes data held in XRAM to the USB FIFO + * @details The FIFO to write must be set before calling the function with + * @ref USB_EnableWriteFIFO(). + * @param numBytes + * Number of bytes to write to the FIFO + * @param dat + * Pointer to XDATA buffer holding data to write to the FIFO + ******************************************************************************/ +static void USB_WriteFIFO_Xdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_XDATA)) +{ + while (numBytes--) + { + USB_SetFIFOByte(*dat); + dat++; + } +} + +#if SI_GPTR_MTYPE_PDATA != SI_GPTR_MTYPE_XDATA +/***************************************************************************//** + * @brief Reads data from the USB FIFO to a buffer in paged XRAM + * @param numBytes + * Number of bytes to read from the FIFO + * @param dat + * Pointer to PDATA buffer to hold data read from the FIFO + * @param fifoNum + * USB FIFO to read + ******************************************************************************/ +static void USB_ReadFIFO_Pdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_PDATA), uint8_t fifoNum) +{ + while (--numBytes) + { + USB_GetFIFOByte(dat); + dat++; + } + USB_GetLastFIFOByte(dat, fifoNum); +} + +/***************************************************************************//** + * @brief Writes data held in paged XRAM to the USB FIFO + * @details The FIFO to write must be set before calling the function with + * @ref USB_EnableWriteFIFO(). + * @param numBytes + * Number of bytes to write to the FIFO + * @param dat + * Pointer to PDATA buffer holding data to write to the FIFO + ******************************************************************************/ +static void USB_WriteFIFO_Pdata(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_PDATA)) +{ + while (numBytes--) + { + USB_SetFIFOByte(*dat); + dat++; + } +} + +#endif + +#if SI_GPTR_MTYPE_DATA != SI_GPTR_MTYPE_IDATA +/***************************************************************************//** + * @brief Reads data from the USB FIFO to a buffer in DRAM + * @param numBytes + * Number of bytes to read from the FIFO + * @param dat + * Pointer to DATA buffer to hold data read from the FIFO + * @param fifoNum + * USB FIFO to read + ******************************************************************************/ +static void USB_ReadFIFO_Data(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_DATA), uint8_t fifoNum) +{ + while (--numBytes) + { + USB_GetFIFOByte(dat); + dat++; + } + USB_GetLastFIFOByte(dat, fifoNum); +} + +/***************************************************************************//** + * @brief Writes data held in DRAM to the USB FIFO + * @details The FIFO to write must be set before calling the function with + * @ref USB_EnableWriteFIFO(). + * @param numBytes + * Number of bytes to write to the FIFO + * @param dat + * Pointer to DATA buffer to hold data read from the FIFO + ******************************************************************************/ +static void USB_WriteFIFO_Data(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_DATA)) +{ + while (numBytes--) + { + USB_SetFIFOByte(*dat); + dat++; + } +} +#endif + +/***************************************************************************//** + * @brief Writes data held in code space to the USB FIFO + * @details The FIFO to write must be set before calling the function with + * @ref USB_EnableWriteFIFO(). + * @param numBytes + * Number of bytes to write to the FIFO + * @param dat + * Pointer to CODE buffer holding data to write to the FIFO + ******************************************************************************/ +static void USB_WriteFIFO_Code(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_CODE)) +{ + while (numBytes--) + { + USB_SetFIFOByte(*dat); + dat++; + } +} + +#else +/***************************************************************************//** + * @brief Reads data from the USB FIFO to a buffer in generic memory space + * @param numBytes + * Number of bytes to read from the FIFO + * @param dat + * Pointer to generic buffer to hold data read from the FIFO + * @param fifoNum + * USB FIFO to read + ******************************************************************************/ +static void USB_ReadFIFO_Generic(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC), uint8_t fifoNum) +{ + while (--numBytes) + { + USB_GetFIFOByte(dat); + dat++; + } + USB_GetLastFIFOByte(dat, fifoNum); +} + +/***************************************************************************//** + * @brief Writes data held in generic memory space to the USB FIFO + * @details The FIFO to write must be set before calling the function with + * @ref USB_EnableWriteFIFO(). + * @param numBytes + * Number of bytes to write to the FIFO + * @param dat + * Pointer to generic buffer holding data to write to the FIFO + ******************************************************************************/ +static void USB_WriteFIFO_Generic(uint8_t numBytes, SI_VARIABLE_SEGMENT_POINTER(dat, uint8_t, SI_SEG_GENERIC)) +{ + while (numBytes--) + { + USB_SetFIFOByte(*dat); + dat++; + } +} + +#endif // #ifdef SI_GPTR diff --git a/efm8/lib/efm8_usb/src/efm8_usbdint.c b/efm8/lib/efm8_usb/src/efm8_usbdint.c new file mode 100644 index 0000000..39d744f --- /dev/null +++ b/efm8/lib/efm8_usb/src/efm8_usbdint.c @@ -0,0 +1,687 @@ +/**************************************************************************//** + * Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved. + * + * http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt + *****************************************************************************/ + +#include "si_toolchain.h" +#include "efm8_usb.h" +#include +#include + +// ----------------------------------------------------------------------------- +// Global variables + +extern SI_SEGMENT_VARIABLE(myUsbDevice, USBD_Device_TypeDef, MEM_MODEL_SEG); +extern SI_SEGMENT_VARIABLE(txZero[2], const uint8_t, SI_SEG_CODE); + +// ----------------------------------------------------------------------------- +// Function prototypes + +static void handleUsbEp0Int(void); +static void handleUsbResetInt(void); +static void handleUsbSuspendInt(void); +static void handleUsbResumeInt(void); +static void handleUsbEp0Tx(void); +static void handleUsbEp0Rx(void); +static void USB_ReadFIFOSetup(void); + +#if (SLAB_USB_EP1IN_USED) +void handleUsbIn1Int(void); +#endif // SLAB_USB_EP1IN_USED +#if (SLAB_USB_EP2IN_USED) +void handleUsbIn2Int(void); +#endif // SLAB_USB_EP2IN_USED +#if (SLAB_USB_EP3IN_USED) +void handleUsbIn3Int(void); +#endif // SLAB_USB_EP3IN_USED + +#if (SLAB_USB_EP1OUT_USED) +void handleUsbOut1Int(void); +#endif // SLAB_USB_EP1OUT_USED +#if (SLAB_USB_EP2OUT_USED) +void handleUsbOut2Int(void); +#endif // SLAB_USB_EP2OUT_USED +#if (SLAB_USB_EP3OUT_USED) +void handleUsbOut3Int(void); +#endif // SLAB_USB_EP3OUT_USED + +void SendEp0Stall(void); + +#if SLAB_USB_UTF8_STRINGS == 1 +static uint8_t decodeUtf8toUcs2( + const uint8_t *pUtf8in, + SI_VARIABLE_SEGMENT_POINTER(pUcs2out, uint16_t, MEM_MODEL_SEG)); +#endif + +// ----------------------------------------------------------------------------- +// Functions + +/***************************************************************************//** + * @brief First-level handler for USB peripheral interrupt + * @details If @ref SLAB_USB_POLLED_MODE is 1, this becomes a regular + * function instead of an ISR and must be called by the application + * periodically. + ******************************************************************************/ +#if (SLAB_USB_POLLED_MODE == 0) +SI_INTERRUPT(usbIrqHandler, USB0_IRQn) +#else +void usbIrqHandler(void) +#endif +{ + uint8_t statusCommon, statusIn, statusOut, indexSave; + +#if SLAB_USB_HANDLER_CB + // Callback to user before processing + USBD_EnterHandler(); +#endif + + // Get the interrupt sources + statusCommon = USB_GetCommonInts(); + statusIn = USB_GetInInts(); + statusOut = USB_GetOutInts(); + +#if SLAB_USB_POLLED_MODE + if ((statusCommon == 0) && (statusIn == 0) && (statusOut == 0)) + { + return; + } +#endif + + // Save the current index + indexSave = USB_GetIndex(); + + // Check Common USB Interrupts + if (USB_IsSofIntActive(statusCommon)) + { +#if SLAB_USB_SOF_CB + USBD_SofCb(USB_GetSofNumber()); +#endif // SLAB_USB_SOF_CB + + // Check for unhandled USB packets on EP0 and set the corresponding IN or + // OUT interrupt active flag if necessary. + if (((myUsbDevice.ep0.misc.bits.outPacketPending == true) && (myUsbDevice.ep0.state == D_EP_RECEIVING)) || + ((myUsbDevice.ep0.misc.bits.inPacketPending == true) && (myUsbDevice.ep0.state == D_EP_TRANSMITTING))) + { + USB_SetEp0IntActive(statusIn); + } + // Check for unhandled USB OUT packets and set the corresponding OUT + // interrupt active flag if necessary. +#if SLAB_USB_EP1OUT_USED + if ((myUsbDevice.ep1out.misc.bits.outPacketPending == true) && (myUsbDevice.ep1out.state == D_EP_RECEIVING)) + { + USB_SetOut1IntActive(statusOut); + } +#endif +#if SLAB_USB_EP2OUT_USED + if ((myUsbDevice.ep2out.misc.bits.outPacketPending == true) && (myUsbDevice.ep2out.state == D_EP_RECEIVING)) + { + USB_SetOut2IntActive(statusOut); + } +#endif +#if SLAB_USB_EP3OUT_USED + if ((myUsbDevice.ep3out.misc.bits.outPacketPending == true) && (myUsbDevice.ep3out.state == D_EP_RECEIVING)) + { + USB_SetOut3IntActive(statusOut); + } +#endif +#if (SLAB_USB_EP3IN_USED && (SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC)) + if ((myUsbDevice.ep3in.misc.bits.inPacketPending == true) && (myUsbDevice.ep3in.state == D_EP_TRANSMITTING)) + { + USB_SetIn3IntActive(statusIn); + } +#endif + } + + if (USB_IsResetIntActive(statusCommon)) + { + handleUsbResetInt(); + + // If VBUS is not present on detection of a USB reset, enter suspend mode. +#if (SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF) + if (USB_IsVbusOn() == false) + { + USB_SetSuspendIntActive(statusCommon); + } +#endif + } + + if (USB_IsResumeIntActive(statusCommon)) + { + handleUsbResumeInt(); + } + + if (USB_IsSuspendIntActive(statusCommon)) + { + handleUsbSuspendInt(); + } + +#if SLAB_USB_EP3IN_USED + if (USB_IsIn3IntActive(statusIn)) + { + handleUsbIn3Int(); + } +#endif // EP3IN_USED + +#if SLAB_USB_EP3OUT_USED + if (USB_IsOut3IntActive(statusOut)) + { + handleUsbOut3Int(); + } +#endif // EP3OUT_USED + +#if SLAB_USB_EP2IN_USED + if (USB_IsIn2IntActive(statusIn)) + { + handleUsbIn2Int(); + } +#endif // EP2IN_USED + +#if SLAB_USB_EP1IN_USED + if (USB_IsIn1IntActive(statusIn)) + { + handleUsbIn1Int(); + } +#endif // EP1IN_USED + +#if SLAB_USB_EP2OUT_USED + if (USB_IsOut2IntActive(statusOut)) + { + handleUsbOut2Int(); + } +#endif // EP2OUT_USED + +#if SLAB_USB_EP1OUT_USED + if (USB_IsOut1IntActive(statusOut)) + { + handleUsbOut1Int(); + } +#endif // EP1OUT_USED + + // Check USB Endpoint 0 Interrupt + if (USB_IsEp0IntActive(statusIn)) + { + handleUsbEp0Int(); + } + + // Restore index + USB_SetIndex(indexSave); + +#if SLAB_USB_HANDLER_CB + // Callback to user before exiting + USBD_ExitHandler(); +#endif +} + +/***************************************************************************//** + * @brief Handles Endpoint 0 transfer interrupt + ******************************************************************************/ +static void handleUsbEp0Int(void) +{ + USB_Status_TypeDef retVal = USB_STATUS_REQ_UNHANDLED; + + USB_SetIndex(0); + + if (USB_Ep0SentStall() || USB_GetSetupEnd()) + { + USB_Ep0ClearSentStall(); + USB_ServicedSetupEnd(); + myUsbDevice.ep0.state = D_EP_IDLE; + myUsbDevice.ep0.misc.c = 0; + } + if (USB_Ep0OutPacketReady()) + { + if (myUsbDevice.ep0.misc.bits.waitForRead == true) + { + myUsbDevice.ep0.misc.bits.outPacketPending = true; + } + else if (myUsbDevice.ep0.state == D_EP_IDLE) + { + myUsbDevice.ep0String.c = USB_STRING_DESCRIPTOR_UTF16LE; + USB_ReadFIFOSetup(); + + // Vendor unique, Class or Standard setup commands override? +#if SLAB_USB_SETUP_CMD_CB + retVal = USBD_SetupCmdCb(&myUsbDevice.setup); + + if (retVal == USB_STATUS_REQ_UNHANDLED) + { +#endif + if (myUsbDevice.setup.bmRequestType.Type == USB_SETUP_TYPE_STANDARD) + { + retVal = USBDCH9_SetupCmd(); + } +#if SLAB_USB_SETUP_CMD_CB + } +#endif + + // Reset index to 0 in case the call to USBD_SetupCmdCb() or + // USBDCH9_SetupCmd() changed it. + USB_SetIndex(0); + + // Put the Enpoint 0 hardware into the correct state here. + if (retVal == USB_STATUS_OK) + { + // If wLength is 0, there is no Data Phase + // Set both the Serviced Out Packet Ready and Data End bits + if (myUsbDevice.setup.wLength == 0) + { + USB_Ep0SetLastOutPacketReady(); + } + // If wLength is non-zero, there is a Data Phase. + // Set only the Serviced Out Packet Ready bit. + else + { + USB_Ep0ServicedOutPacketReady(); + +#if SLAB_USB_SETUP_CMD_CB + // If OUT packet but callback didn't set up a USBD_Read and we are expecting a + // data byte then we need to wait for the read to be setup and NACK packets until + // USBD_Read is called. + if ((myUsbDevice.setup.bmRequestType.Direction == USB_SETUP_DIR_OUT) + && (myUsbDevice.ep0.state != D_EP_RECEIVING)) + { + myUsbDevice.ep0.misc.bits.waitForRead = true; + } +#endif + } + } + // If the setup transaction detected an error, send a stall + else + { + SendEp0Stall(); + } + } + else if (myUsbDevice.ep0.state == D_EP_RECEIVING) + { + handleUsbEp0Rx(); + } + else + { + myUsbDevice.ep0.misc.bits.outPacketPending = true; + } + } + if ((myUsbDevice.ep0.state == D_EP_TRANSMITTING) && (USB_Ep0InPacketReady() == 0)) + { + handleUsbEp0Tx(); + } +} + +/***************************************************************************//** + * @brief Reads and formats a setup packet + ******************************************************************************/ +static void USB_ReadFIFOSetup(void) +{ + SI_VARIABLE_SEGMENT_POINTER(ptr, uint16_t, MEM_MODEL_SEG) = (SI_VARIABLE_SEGMENT_POINTER(, uint16_t, MEM_MODEL_SEG))&myUsbDevice.setup; + + USB_ReadFIFO(0, 8, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))ptr); + + // Modify for Endian-ness of the compiler + ptr[1] = le16toh(ptr[1]); + ptr[2] = le16toh(ptr[2]); + ptr[3] = le16toh(ptr[3]); +} + +/***************************************************************************//** + * @brief Handles USB port reset interrupt + * @details After receiving a USB reset, halt all endpoints except for + * Endpoint 0, set the device state, and configure USB hardware. + ******************************************************************************/ +static void handleUsbResetInt(void) +{ + // Setup EP0 to receive SETUP packets + myUsbDevice.ep0.state = D_EP_IDLE; + + // Halt all other endpoints +#if SLAB_USB_EP1IN_USED + myUsbDevice.ep1in.state = D_EP_HALT; +#endif +#if SLAB_USB_EP2IN_USED + myUsbDevice.ep2in.state = D_EP_HALT; +#endif +#if SLAB_USB_EP3IN_USED + myUsbDevice.ep3in.state = D_EP_HALT; +#endif +#if SLAB_USB_EP1OUT_USED + myUsbDevice.ep1out.state = D_EP_HALT; +#endif +#if SLAB_USB_EP2OUT_USED + myUsbDevice.ep2out.state = D_EP_HALT; +#endif +#if SLAB_USB_EP3OUT_USED + myUsbDevice.ep3out.state = D_EP_HALT; +#endif + + // After a USB reset, some USB hardware configurations will be reset and must + // be reconfigured. + + // Re-enable clock recovery +#if SLAB_USB_CLOCK_RECOVERY_ENABLED +#if SLAB_USB_FULL_SPEED + USB_EnableFullSpeedClockRecovery(); +#else + USB_EnableLowSpeedClockRecovery(); +#endif +#endif + + // Re-enable USB interrupts + USB_EnableSuspendDetection(); + USB_EnableDeviceInts(); + + // If the device is bus-powered, always put it in the Default state. + // If the device is self-powered and VBUS is present, put the device in the + // Default state. Otherwise, put it in the Attached state. +#if (!SLAB_USB_BUS_POWERED) && \ + (!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) + if (USB_IsVbusOn()) + { + USBD_SetUsbState(USBD_STATE_DEFAULT); + } + else + { + USBD_SetUsbState(USBD_STATE_ATTACHED); + } +#else + USBD_SetUsbState(USBD_STATE_DEFAULT); +#endif // (!(SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF)) + +#if SLAB_USB_RESET_CB + // Make the USB Reset Callback + USBD_ResetCb(); +#endif +} + +/***************************************************************************//** + * @brief Handle USB port suspend interrupt + * @details After receiving a USB reset, set the device state and + * call @ref USBD_Suspend() if configured to do so in + * @ref SLAB_USB_PWRSAVE_MODE + ******************************************************************************/ +static void handleUsbSuspendInt(void) +{ + if (myUsbDevice.state >= USBD_STATE_POWERED) + { + USBD_SetUsbState(USBD_STATE_SUSPENDED); + +#if (SLAB_USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONSUSPEND) + USBD_Suspend(); +#endif + } +} + +/***************************************************************************//** + * @brief Handles USB port resume interrupt + * @details Restore the device state to its previous value. + ******************************************************************************/ +static void handleUsbResumeInt(void) +{ + USBD_SetUsbState(myUsbDevice.savedState); +} + +/***************************************************************************//** + * @brief Handles transmit data phase on Endpoint 0 + ******************************************************************************/ +static void handleUsbEp0Tx(void) +{ + uint8_t count, count_snapshot, i; + bool callback = myUsbDevice.ep0.misc.bits.callback; + + // The number of bytes to send in the next packet must be less than or equal + // to the maximum EP0 packet size. + count = (myUsbDevice.ep0.remaining >= USB_EP0_SIZE) ? + USB_EP0_SIZE : myUsbDevice.ep0.remaining; + + // Save the packet size for future use. + count_snapshot = count; + + // Strings can use the USB_STRING_DESCRIPTOR_UTF16LE_PACKED type to pack + // UTF16LE data without the zero's between each character. + // If the current string is of type USB_STRING_DESCRIPTOR_UTF16LE_PACKED, + // unpack it by inserting a zero between each character in the string. + if ((myUsbDevice.ep0String.encoding.type == USB_STRING_DESCRIPTOR_UTF16LE_PACKED) +#if SLAB_USB_UTF8_STRINGS == 1 + || (myUsbDevice.ep0String.encoding.type == USB_STRING_DESCRIPTOR_UTF8) +#endif + ) + { + // If ep0String.encoding.init is true, this is the beginning of the string. + // The first two bytes of the string are the bLength and bDescriptorType + // fields. These are not packed like the reset of the string, so write them + // to the FIFO and set ep0String.encoding.init to false. + if (myUsbDevice.ep0String.encoding.init == true) + { + USB_WriteFIFO(0, 2, myUsbDevice.ep0.buf, false); + myUsbDevice.ep0.buf += 2; + count -= 2; + myUsbDevice.ep0String.encoding.init = false; + } + + // Insert a 0x00 between each character of the string. + for (i = 0; i < count / 2; i++) + { +#if SLAB_USB_UTF8_STRINGS == 1 + if (myUsbDevice.ep0String.encoding.type == USB_STRING_DESCRIPTOR_UTF8) + { + SI_SEGMENT_VARIABLE(ucs2, uint16_t, MEM_MODEL_SEG); + uint8_t utf8count; + + // decode the utf8 into ucs2 for usb string + utf8count = decodeUtf8toUcs2(myUsbDevice.ep0.buf, &ucs2); + + // if consumed utf8 bytes is 0, it means either null byte was + // input or bad utf8 byte sequence. Either way its an error and + // there's not much we can do. So just advance the input string + // by one character and keep going until count is expired. + if (utf8count == 0) + { + utf8count = 1; + } + + // adjust to next char in utf8 byte sequence + myUsbDevice.ep0.buf += utf8count; + ucs2 = htole16(ucs2); // usb 16-bit chars are little endian + USB_WriteFIFO(0, 2, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))&ucs2, false); + } + else +#endif + { + USB_WriteFIFO(0, 1, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))myUsbDevice.ep0.buf, false); + myUsbDevice.ep0.buf++; + USB_WriteFIFO(0, 1, (SI_VARIABLE_SEGMENT_POINTER(, uint8_t, SI_SEG_GENERIC))&txZero, false); + } + } + } + // For any data other than USB_STRING_DESCRIPTOR_UTF16LE_PACKED, just send the + // data normally. + else + { + USB_WriteFIFO(0, count, myUsbDevice.ep0.buf, false); + myUsbDevice.ep0.buf += count; + } + + myUsbDevice.ep0.misc.bits.inPacketPending = false; + myUsbDevice.ep0.remaining -= count_snapshot; + + // If the last packet of the transfer is exactly the maximum EP0 packet size, + // we will have to send a ZLP (zero-length packet) after the last data packet + // to signal to the host that the transfer is complete. + // Check for the ZLP packet case here. + if ((myUsbDevice.ep0.remaining == 0) && (count_snapshot != USB_EP0_SIZE)) + { + USB_Ep0SetLastInPacketReady(); + myUsbDevice.ep0.state = D_EP_IDLE; + myUsbDevice.ep0String.c = USB_STRING_DESCRIPTOR_UTF16LE; + myUsbDevice.ep0.misc.c = 0; + } + else + { + // Do not call USB_Ep0SetLastInPacketReady() because we still need to send + // the ZLP. + USB_Ep0SetInPacketReady(); + } + // Make callback if requested + if (callback == true) + { + USBD_XferCompleteCb(EP0, USB_STATUS_OK, count_snapshot, myUsbDevice.ep0.remaining); + } +} + +/***************************************************************************//** + * @brief Handles receive data phase on Endpoint 0 + ******************************************************************************/ +void handleUsbEp0Rx(void) +{ + uint8_t count; + USB_Status_TypeDef status; + bool callback = myUsbDevice.ep0.misc.bits.callback; + + // Get the number of bytes received + count = USB_Ep0GetCount(); + + // If the call to USBD_Read() did not give a large enough buffer to hold this + // data, set the outPacketPending flag and signal an RX overrun. + if (myUsbDevice.ep0.remaining < count) + { + myUsbDevice.ep0.state = D_EP_IDLE; + myUsbDevice.ep0.misc.bits.outPacketPending = true; + status = USB_STATUS_EP_RX_BUFFER_OVERRUN; + } + else + { + USB_ReadFIFO(0, count, myUsbDevice.ep0.buf); + myUsbDevice.ep0.buf += count; + myUsbDevice.ep0.remaining -= count; + status = USB_STATUS_OK; + + // If the last packet of the transfer is exactly the maximum EP0 packet + // size, we will must wait to receive a ZLP (zero-length packet) after the + // last data packet. This signals that the host has completed the transfer. + // Check for the ZLP packet case here. + if ((myUsbDevice.ep0.remaining == 0) && (count != USB_EP0_SIZE)) + { + USB_Ep0SetLastOutPacketReady(); + myUsbDevice.ep0.state = D_EP_IDLE; + myUsbDevice.ep0.misc.bits.callback = false; + } + else + { + // Do not call USB_Ep0SetLastOutPacketReady() until we get the ZLP. + USB_Ep0ServicedOutPacketReady(); + } + } + + // Make callback if requested + if (callback == true) + { + USBD_XferCompleteCb(EP0, status, count, myUsbDevice.ep0.remaining); + } +} + + +/***************************************************************************//** + * @brief Send a procedural stall on Endpoint 0 + ******************************************************************************/ +void SendEp0Stall(void) +{ + USB_SetIndex(0); + myUsbDevice.ep0.state = D_EP_STALL; + USB_Ep0SendStall(); +} + +#if SLAB_USB_UTF8_STRINGS == 1 +/***************************************************************************//** + * Decodes UTF-8 to UCS-2 (16-bit) character encoding that is used + * for USB string descriptors. + * + * @param pUtf8in pointer to next character in UTF-8 string + * @param pUcs2out pointer to location for 16-bit character output + * + * Decodes a UTF-8 byte sequence into a single UCS-2 character. This + * will only decode up to 16-bit code point and will not handle the + * 21-bit case (4 bytes input). + * + * For valid cases, the UTF8 character sequence decoded into a 16-bit + * character and stored at the location pointed at by _pUcs2out_. + * The function will then return the number of input bytes that were + * consumed (1, 2, or 3). The caller can use the return value to find + * the start of the next character sequence in a utf-8 string. + * + * If either of the input pointers are NULL, then 0 is returned. + * + * If the first input character is NULL, then the output 16-bit value + * will be set to NULL and the function will return 0. + * + * If any other invalid sequence is detected, then the 16-bit output + * will be set to the equivalent of the question mark character (0x003F) + * and the return code will be 0. + * + * @return count of UTF8 bytes consumed + ******************************************************************************/ +static uint8_t decodeUtf8toUcs2( + const uint8_t *pUtf8in, + SI_VARIABLE_SEGMENT_POINTER(pUcs2out, uint16_t, MEM_MODEL_SEG)) +{ + uint8_t ret = 0; + + // check the input pointers + if (!pUtf8in || !pUcs2out) + { + return 0; + } + + // set default decode to error '?'; + *pUcs2out = '?'; + + // valid cases: + // 0xxxxxxx (7 bits) + // 110xxxxx 10xxxxxx (11 bits) + // 1110xxxx 10xxxxxx 10xxxxxx (16 bits) + + // null input + if (pUtf8in[0] == 0) + { + *pUcs2out = 0; + ret = 0; + } + + // 7-bit char + else if (pUtf8in[0] < 128) + { + *pUcs2out = pUtf8in[0]; + ret = 1; + } + + // 11-bit char + else if ((pUtf8in[0] & 0xE0) == 0xC0) + { + if ((pUtf8in[1] & 0xC0) == 0x80) + { + *pUcs2out = ((pUtf8in[0] & 0x1F) << 6) | (pUtf8in[1] & 0x3F); + ret = 2; + } + } + + // 16-bit char + else if ((pUtf8in[0] & 0xF0) == 0xE0) + { + if ((pUtf8in[1] & 0xC0) == 0x80) + { + if ((pUtf8in[2] & 0xC0) == 0x80) + { + *pUcs2out = ((pUtf8in[0] & 0x0F) << 12) + | ((pUtf8in[1] & 0x3F) << 6) + | (pUtf8in[2] & 0x3F); + ret = 3; + } + } + } + + return ret; +} +#endif // SLAB_USB_UTF8_STRINGS + +// This function is called from USBD_Init(). It forces the user project to pull +// this module from the library so that the declared ISR can be seen and +// included. If this is not done then this entire module by never be included +// and the ISR will not be present. +void forceModuleLoad_usbint(void){} diff --git a/efm8/lib/efm8ub1/peripheralDrivers/inc/usb_0.h b/efm8/lib/efm8ub1/peripheralDrivers/inc/usb_0.h new file mode 100644 index 0000000..d094fde --- /dev/null +++ b/efm8/lib/efm8ub1/peripheralDrivers/inc/usb_0.h @@ -0,0 +1,2091 @@ +/***************************************************************************//** + * Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved. + * + * http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt + ******************************************************************************/ + +#ifndef __SILICON_LABS_EFM8_USB_0_H__ +#define __SILICON_LABS_EFM8_USB_0_H__ + +#include "SI_EFM8UB1_Register_Enums.h" +#include +#include + +/******************************************************************************/ + +/** + * + * @addtogroup usb_0_group USB0 Driver + * @{ + * + * @brief Peripheral driver for USB 0 + * + * # Introduction # + * + * This module provides an API for using the USB0 peripheral. + * The API provides access to the USB hardware. A full-featured + * USB stack (EFM8 USB Library) is available in the SDK at "\lib\efm8_usb." + * The primary purpose of this USB peripheral driver is to abstract hardware + * accesses so that the EFM8 USB Library can run on multiple EFM8 devices + * (e.g. EFM8UB1, EFM8UB2). However, this driver can also be used to build + * custom USB stacks and applications in cases where greater optimization or + * performance than what the EFM8 USB Library provides is required. + * + ******************************************************************************/ + +/***************************************************************************//** + * @addtogroup usb_0_runtime USB0 Runtime API + * @brief + * Functions and macros to access the USB hardware. + * @{ + ******************************************************************************/ + +// ------------------------------- +// Macros + +/***************************************************************************//** + * @brief Reads an indirect USB register + * @details Sets USB0ADR and polls on the busy bit. + * When the macro completes, the value can be read from USB0DAT. + * @param addr + * The address of the USB indirect register to read + * @return The value of the USB indirect register is held in USB0DAT. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern uint8_t USB_READ_BYTE(uint8_t addr); +#else +#define USB_READ_BYTE(addr) \ + do \ + { \ + USB0ADR = (USB0ADR_BUSY__SET | (addr)); \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Writes an indirect USB register + * @details Sets USB0ADR, writes a value to USB0DAT, and waits for the busy + * bit to clear. + * @param addr + * The address of the USB indirect register to read + * @param dat + * The value to write to the USB indirect register + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_WRITE_BYTE(uint8_t addr, uint8_t dat); +#else +#define USB_WRITE_BYTE(addr, dat) \ + do \ + { \ + USB0ADR = (addr); \ + USB0DAT = (dat); \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Sets bits in an indirect USB register + * @details Sets the bits in the bitmask of the indirect USB register + * without disturbing the value of other bits in the indirect + * register. + * @param addr + * The address of the USB indirect register to write + * @param bitmask + * The bits to set + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SET_BITS(uint8_t addr, uint8_t bitmask); +#else +#define USB_SET_BITS(addr, bitmask) \ + do \ + { \ + USB0ADR = (USB0ADR_BUSY__SET | (addr)); \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + USB0DAT = (USB0DAT | (bitmask)); \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Clears bits in an indirect USB register + * @details Clears the bits in the bitmask of an indirect USB register + * without disturbing the value of other bits in the indirect + * register. + * @param addr + * The address of the USB indirect register to write + * @param bitmask + * The bits to clear + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_CLEAR_BITS(uint8_t addr, uint8_t bitmask); +#else +#define USB_CLEAR_BITS(addr, bitmask) \ + do \ + { \ + USB0ADR = (USB0ADR_BUSY__SET | (addr)); \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + USB0DAT = (USB0DAT & ~(bitmask)); \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Enables USB interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableInts(void); +#else +#define USB_EnableInts() \ + do \ + { \ + SFRPAGE = PG2_PAGE; \ + EIE2 |= EIE2_EUSB0__ENABLED; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Disables USB interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableInts(void); +#else +#define USB_DisableInts() \ + do \ + { \ + SFRPAGE = PG2_PAGE; \ + EIE2 &= ~EIE2_EUSB0__ENABLED; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Returns state of USB interrupt enabler + * @return TRUE if USB interrupts are enabled, FALSE otherwise. + ******************************************************************************/ +extern bool USB_GetIntsEnabled(void); + +/***************************************************************************//** + * @brief Enables VBUS detection + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_VbusDetectEnable(void); +#else +#define USB_VbusDetectEnable() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0CF |= USB0CF_VBUSEN__ENABLED; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Disables VBUS detection + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_VbusDetectDisable(void); +#else +#define USB_VbusDetectDisable() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0CF &= ~USB0CF_VBUSEN__ENABLED; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Check status of VBUS signal + * @return TRUE if VBUS signal is present, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsVbusOn(void); +#else +#define USB_IsVbusOn() ((bool) P3_B1) +#endif + +/***************************************************************************//** + * @brief Enables the USB pull-up resistor + * @details Enables either the D+ or the D- pull-up resistor, depending on + * whether @ref USB_SelectFullSpeed() or @ref USB_SelectLowSpeed() + * was previously called. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnablePullUpResistor(void); +#else +#define USB_EnablePullUpResistor() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0XCN |= USB0XCN_PREN__PULL_UP_ENABLED; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Disables the USB pull-up resistor + * @details Disables either the D+ or the D- pull-up resistor, depending on + * whether @ref USB_SelectFullSpeed() or @ref USB_SelectLowSpeed() + * was previously called. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisablePullUpResistor(void); +#else +#define USB_DisablePullUpResistor() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0XCN &= ~USB0XCN_PREN__PULL_UP_ENABLED; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Enables the USB transceiver + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableTransceiver(void); +#else +#define USB_EnableTransceiver() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0XCN |= USB0XCN_PHYEN__ENABLED; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Disables the USB transceiver + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableTransceiver(void); +#else +#define USB_DisableTransceiver() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0XCN &= ~USB0XCN_PHYEN__ENABLED; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Puts the USB in full-speed mode. + * @details Configures the USB to operate as a full-speed device by + * enabling the D+ pull-up resistor. After calling this + * function, the user must call @ref USB_EnablePullUpResistor(). + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SelectFullSpeed(void); +#else +#define USB_SelectFullSpeed() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0XCN |= USB0XCN_SPEED__FULL_SPEED; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Puts the USB in low-speed mode. + * @details Configures the USB to operate as a low-speed device by + * enabling the D- pull-up resistor. After calling this + * function, the user must call @ref USB_EnablePullUpResistor(). + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SelectLowSpeed(void); +#else +#define USB_SelectLowSpeed() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0XCN &= ~USB0XCN_SPEED__FULL_SPEED; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Suspends the transceiver + * @details Puts the USB transceiver in suspend mode. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SuspendTransceiver(void); +#else +#define USB_SuspendTransceiver() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0XCN &= ~(USB0XCN_PHYEN__ENABLED | USB0XCN_Dp__HIGH | USB0XCN_Dn__HIGH);\ + } while (0) +#endif + +/***************************************************************************//** + * @brief Selects the internal oscillator as the USB clock + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetClockIntOsc(void); +#else +#define USB_SetClockIntOsc() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0CF &= ~USB0CF_USBCLK__FMASK; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Selects the internal oscillator / 8 as the USB clock + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetClockIntOscDiv8(void); +#else +#define USB_SetClockIntOscDiv8() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0CF &= ~USB0CF_USBCLK__FMASK; \ + USB0CF |= USB0CF_USBCLK__HFOSC1_DIV_8; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Selects the external oscillator as the USB clock + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetClockExtOsc(void); +#else +#define USB_SetClockExtOsc() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0CF &= ~USB0CF_USBCLK__FMASK; \ + USB0CF |= USB0CF_USBCLK__EXTOSC; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Selects the external oscillator / 2 as the USB clock + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetClockExtOscDiv2(void); +#else +#define USB_SetClockExtOscDiv2() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0CF &= ~USB0CF_USBCLK__FMASK; \ + USB0CF |= USB0CF_USBCLK__EXTOSC_DIV_2; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Selects the external oscillator / 3 as the USB clock + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetClockExtOscDiv3(void); +#else +#define USB_SetClockExtOscDiv3() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0CF &= ~USB0CF_USBCLK__FMASK; \ + USB0CF |= USB0CF_USBCLK__EXTOSC_DIV_3; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Selects the external oscillator / 4 as the USB clock + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetClockExtOscDiv4(void); +#else +#define USB_SetClockExtOscDiv4() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0CF &= ~USB0CF_USBCLK__FMASK; \ + USB0CF |= USB0CF_USBCLK__EXTOSC_DIV_4; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Selects the low-frequency oscillator as the USB clock + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetClockLfo(void); +#else +#define USB_SetClockLfo() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + USB0CF &= ~USB0CF_USBCLK__FMASK; \ + USB0CF |= USB0CF_USBCLK__LFOSC; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Selects the normal setting for the USB clock + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetNormalClock(void); +#else +#define USB_SetNormalClock() USB_SetClockIntOsc() +#endif + +/***************************************************************************//** + * @brief Selects the low-power setting for the USB clock + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetSuspendClock(void); +#else +#define USB_SetSuspendClock() USB_SetClockIntOscDiv8() +#endif + +/***************************************************************************//** + * @brief Suspends REG1 + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SuspendRegulator(void); +#else +#define USB_SuspendRegulator() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + REG1CN |= REG1CN_SUSEN__SUSPEND; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Does not use regulator low-power modes + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SuspendRegulatorFastWake(void); +#else +#define USB_SuspendRegulatorFastWake() +#endif + +/***************************************************************************//** + * @brief Takes REG0 and REG1 out of suspend + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_UnsuspendRegulator(void); +#else +#define USB_UnsuspendRegulator() \ + do \ + { \ + SFRPAGE = PG3_PAGE; \ + REG1CN &= ~REG1CN_SUSEN__SUSPEND; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Determine if the internal regulator is enabled + * @return TRUE if the internal regulator is enabled, FALSE otherwise + ******************************************************************************/ +extern bool USB_IsRegulatorEnabled(void); + +/***************************************************************************//** + * @brief Disable the prefetch engine + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisablePrefetch(void); +#else +#define USB_DisablePrefetch() \ + do \ + { \ + SFRPAGE = PG2_PAGE; \ + PFE0CN &= ~(PFE0CN_PFEN__ENABLED | PFE0CN_FLRT__SYSCLK_BELOW_50_MHZ); \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Enable the prefetch engine + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnablePrefetch(void); +#else +#define USB_EnablePrefetch() \ + do \ + { \ + SFRPAGE = PG2_PAGE; \ + PFE0CN |= (PFE0CN_PFEN__ENABLED | PFE0CN_FLRT__SYSCLK_BELOW_50_MHZ); \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Determine if the prefetch engine is enabled + * @return TRUE if prefetch engine is enabled, FALSE otherwise. + ******************************************************************************/ +extern bool USB_IsPrefetchEnabled(void); + +/***************************************************************************//** + * @brief Suspends internal oscillator + ******************************************************************************/ +extern void USB_SuspendOscillator(void); + +/***************************************************************************//** + * @brief Enables clock recovery in full speed mode + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableFullSpeedClockRecovery(void); +#else +#define USB_EnableFullSpeedClockRecovery() \ + USB_WRITE_BYTE(CLKREC, (CLKREC_CRE__ENABLED | 0x0F)) +#endif + +/***************************************************************************//** + * @brief Enables clock recovery in low speed mode + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableLowSpeedClockRecovery(void); +#else +#define USB_EnableLowSpeedClockRecovery() \ + USB_WRITE_BYTE(CLKREC, \ + (CLKREC_CRE__ENABLED \ + | CLKREC_CRLOW__LOW_SPEED \ + | 0x0F)) +#endif + +/***************************************************************************//** + * @brief Disables clock recovery + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableClockRecovery(void); +#else +#define USB_DisableClockRecovery() USB_WRITE_BYTE(CLKREC, 0x0F) +#endif + +/***************************************************************************//** + * @brief Sets the USB function address + * @param addr + * USB Function Address value + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetAddress(uint8_t addr); +#else +#define USB_SetAddress(addr) \ + USB_WRITE_BYTE(FADDR, (FADDR_UPDATE__SET | (addr))) +#endif + +/***************************************************************************//** + * @brief Disable the USB Inhibit feature + * @details The USB block is inhibited after a power-on-reset or an + * asynchronous reset. Software should disable the inhibit bit + * after all USB and transceiver initialization is complete. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableInhibit(void); +#else +#define USB_DisableInhibit() \ + USB_WRITE_BYTE(POWER, (POWER_USBINH__ENABLED | POWER_SUSEN__ENABLED)) +#endif + +/***************************************************************************//** + * @brief Forces a USB reset + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_ForceReset(void); +#else +#define USB_ForceReset() USB_WRITE_BYTE(POWER, POWER_USBRST__SET) +#endif + +/***************************************************************************//** + * @brief Forces USB resume signaling + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_ForceResume(void); +#else +#define USB_ForceResume() \ + USB_WRITE_BYTE(POWER, (POWER_RESUME__START | POWER_SUSEN__ENABLED)) +#endif + +/***************************************************************************//** + * @brief Clears USB resume signaling + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_ClearResume(void); +#else +#define USB_ClearResume() USB_WRITE_BYTE(POWER, POWER_SUSEN__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB suspend detection + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableSuspendDetection(void); +#else +#define USB_EnableSuspendDetection() USB_WRITE_BYTE(POWER, POWER_SUSEN__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB suspend detection + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableSuspendDetection(void); +#else +#define USB_DisableSuspendDetection() USB_WRITE_BYTE(POWER, 0) +#endif + +/***************************************************************************//** + * @brief Setup End Serviced + * @details Software should call this function after servicing a Setup End + * event. Setup End is detected by calling usbGetSetupEnd + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_ServicedSetupEnd(void); +#else +#define USB_ServicedSetupEnd() \ + USB_WRITE_BYTE(E0CSR, E0CSR_SSUEND__SET) +#endif + +/***************************************************************************//** + * @brief Out Packet Ready Serviced + * @details Software should call this function after servicing a received + * Endpoint 0 packet. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_Ep0ServicedOutPacketReady(void); +#else +#define USB_Ep0ServicedOutPacketReady() \ + USB_WRITE_BYTE(E0CSR, E0CSR_SOPRDY__SET) +#endif + +/***************************************************************************//** + * @brief Sets In Packet Ready and Data End on Endpoint 0 + * @details This should be called instead of @ref USB_Ep0SetInPacketReady() + * when sending the last packet of a setup data phase. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_Ep0SetLastInPacketReady(void); +#else +#define USB_Ep0SetLastInPacketReady() \ + USB_WRITE_BYTE(E0CSR, (E0CSR_INPRDY__SET | E0CSR_DATAEND__SET)) +#endif + +/***************************************************************************//** + * @brief Sets In Packet Ready and Data End on Endpoint 0 + * @details This should be called instead of @ref USB_Ep0SetInPacketReady() + * when sending a zero-length packet. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_Ep0SetZLPInPacketReady(void); +#else +#define USB_Ep0SetZLPInPacketReady() \ + USB_WRITE_BYTE(E0CSR, (E0CSR_INPRDY__SET | E0CSR_DATAEND__SET)) +#endif + +/***************************************************************************//** + * @brief Serviced Out Packet Ready and Data End on Endpoint 0 + * @details This should be called instead of @ref USB_ServicedSetupEnd() + * after servicing the last incoming data packet. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_Ep0SetLastOutPacketReady(void); +#else +#define USB_Ep0SetLastOutPacketReady() \ + USB_WRITE_BYTE(E0CSR, (E0CSR_SOPRDY__SET | E0CSR_DATAEND__SET)) +#endif + +/***************************************************************************//** + * @brief Sends a stall on Endpoint 0 + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_Ep0SendStall(void); +#else +#define USB_Ep0SendStall() \ + USB_WRITE_BYTE(E0CSR, (E0CSR_SOPRDY__SET | E0CSR_SDSTL__SET)) +#endif + +/***************************************************************************//** + * @brief Clears sent stall condition on Endpoint 0 + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_Ep0ClearSentStall(void); +#else +#define USB_Ep0ClearSentStall() USB_WRITE_BYTE(E0CSR, 0) +#endif + +/***************************************************************************//** + * @brief Sets InPacketReady on Endpoint 0 + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_Ep0SetInPacketReady(void); +#else +#define USB_Ep0SetInPacketReady() USB_WRITE_BYTE(E0CSR, E0CSR_INPRDY__SET) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Start-of-Frame Interrupt + * @param CMINT_snapshot + * Snapshot of the CMINT register taken previously with the + * @ref USB_GetCommonInts() function. + * @return TRUE if Start-of-Frame Interrupt is active, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsSofIntActive(uint8_t CMINT_snapshot); +#else +#define USB_IsSofIntActive(CMINT_snapshot) ((CMINT_snapshot) & CMINT_SOF__SET) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Reset Interrupt + * @param CMINT_snapshot + * Snapshot of the CMINT register taken previously with the + * @ref USB_GetCommonInts() function. + * @return TRUE if USB Reset Interrupt is active, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsResetIntActive(uint8_t CMINT_snapshot); +#else +#define USB_IsResetIntActive(CMINT_snapshot) \ + ((CMINT_snapshot) & CMINT_RSTINT__SET) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Resume Interrupt + * @param CMINT_snapshot + * Snapshot of the CMINT register taken previously with the + * @ref USB_GetCommonInts() function. + * @return TRUE if USB Resume Interrupt is active, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsResumeIntActive(uint8_t CMINT_snapshot); +#else +#define USB_IsResumeIntActive(CMINT_snapshot) \ + ((CMINT_snapshot) & CMINT_RSUINT__SET) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Suspend Interrupt + * @param CMINT_snapshot + * Snapshot of the CMINT register taken previously with the + * @ref USB_GetCommonInts() function. + * @return TRUE if USB Suspend Interrupt is active, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsSuspendIntActive(uint8_t CMINT_snapshot); +#else +#define USB_IsSuspendIntActive(CMINT_snapshot) \ + ((CMINT_snapshot) & CMINT_SUSINT__SET) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Endpoint 0 Interrupt + * @param IN1INT_snapshot + * Snapshot of the IN1INT register taken previously with the + * @ref USB_GetInInts() function. + * @return TRUE if USB Endpoint 0 Interrupt is active, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsEp0IntActive(uint8_t IN1INT_snapshot); +#else +#define USB_IsEp0IntActive(IN1INT_snapshot) \ + ((IN1INT_snapshot) & IN1INT_EP0__SET) +#endif + +/***************************************************************************//** + * @brief Returns 1 if any USB IN Interrupt is active + * @param IN1INT_snapshot + * Snapshot of the IN1INT register taken previously with the + * @ref USB_GetInInts() function. + * @return TRUE if any USB IN Interrupt is active, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsInIntActive(uint8_t IN1INT_snapshot); +#else +#define USB_IsInIntActive(IN1INT_snapshot) \ + ((IN1INT_snapshot) & (IN1INT_IN1__SET | IN1INT_IN2__SET | IN1INT_IN3__SET)) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Endpoint 1 IN Interrupt + * @param IN1INT_snapshot + * Snapshot of the IN1INT register taken previously with the + * @ref USB_GetInInts() function. + * @return TRUE if USB Endpoint 1 IN Interrupt is active, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsIn1IntActive(uint8_t IN1INT_snapshot); +#else +#define USB_IsIn1IntActive(IN1INT_snapshot) \ + ((IN1INT_snapshot) & IN1INT_IN1__SET) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Endpoint 2 IN Interrupt + * @param IN1INT_snapshot + * Snapshot of the IN1INT register taken previously with the + * @ref USB_GetInInts() function. + * @return TRUE if USB Endpoint 2 IN Interrupt is active, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsIn2IntActive(uint8_t IN1INT_snapshot); +#else +#define USB_IsIn2IntActive(IN1INT_snapshot) \ + ((IN1INT_snapshot) & IN1INT_IN2__SET) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Endpoint 3 IN Interrupt + * @param IN1INT_snapshot + * Snapshot of the IN1INT register taken previously with the + * @ref USB_GetInInts() function. + * @return TRUE if USB Endpoint 3 IN Interrupt is active, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsIn3IntActive(uint8_t IN1INT_snapshot); +#else +#define USB_IsIn3IntActive(IN1INT_snapshot) \ + ((IN1INT_snapshot) & IN1INT_IN3__SET) +#endif + +/***************************************************************************//** + * @brief Returns 1 if any USB Endpoint OUT Interrupt is active + * @param OUT1INT_snapshot + * Snapshot of the OUT1INT register taken previously with the + * @ref USB_GetOutInts() function. + * @return TRUE if any USB OUT Interrupt is active, FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsOutIntActive(uint8_t OUT1INT_snapshot); +#else +#define USB_IsOutIntActive(OUT1INT_snapshot) \ + ((OUT1INT_snapshot) \ + & (OUT1INT_OUT1__SET | OUT1INT_OUT2__SET | OUT1INT_OUT3__SET)) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Endpoint 1 OUT Interrupt + * @param OUT1INT_snapshot + * Snapshot of the OUT1INT register taken previously with the + * @ref USB_GetOutInts() function. + * @return TRUE if USB Endpoint 1 OUT Interrupt is active, + * FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsOut1IntActive(uint8_t OUT1INT_snapshot); +#else +#define USB_IsOut1IntActive(OUT1INT_snapshot) \ + ((OUT1INT_snapshot) & OUT1INT_OUT1__SET) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Endpoint 2 OUT Interrupt + * @param OUT1INT_snapshot + * Snapshot of the OUT1INT register taken previously with the + * @ref USB_GetOutInts() function. + * @return TRUE if USB Endpoint 2 OUT Interrupt is active, + * FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsOut2IntActive(uint8_t OUT1INT_snapshot); +#else +#define USB_IsOut2IntActive(OUT1INT_snapshot) \ + ((OUT1INT_snapshot) & OUT1INT_OUT2__SET) +#endif + +/***************************************************************************//** + * @brief Returns state of USB Endpoint 3 OUT Interrupt + * @param OUT1INT_snapshot + * Snapshot of the OUT1INT register taken previously with the + * @ref USB_GetOutInts() function. + * @return TRUE if USB Endpoint 3 OUT Interrupt is active, + * FALSE otherwise. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern bool USB_IsOut3IntActive(uint8_t OUT1INT_snapshot); +#else +#define USB_IsOut3IntActive(OUT1INT_snapshot) \ + ((OUT1INT_snapshot) & OUT1INT_OUT3__SET) +#endif + +/***************************************************************************//** + * @brief Sets the suspend interrupt flag to active + * @param CMINT_snapshot + * Snapshot of the CMINT register taken previously with the + * @ref USB_GetCommonInts() function. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetSuspendIntActive(uint8_t CMINT_snapshot); +#else +#define USB_SetSuspendIntActive(CMINT_snapshot) \ + ((CMINT_snapshot) |= CMINT_SUSINT__SET) +#endif + +/***************************************************************************//** + * @brief Sets the EP0 interrupt flag to active + * @param IN1INT_snapshot + * Snapshot of the IN1INT register taken previously with the + * @ref USB_GetInInts() function. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetEp0IntActive(uint8_t IN1INT_snapshot); +#else +#define USB_SetEp0IntActive(IN1INT_snapshot) \ + ((IN1INT_snapshot) |= IN1INT_EP0__SET) +#endif + +/***************************************************************************//** + * @brief Sets the IN 1 interrupt flag to active + * @param IN1INT_snapshot + * Snapshot of the IN1INT register taken previously with the + * @ref USB_GetInInts() function. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetIn1IntActive(uint8_t IN1INT_snapshot); +#else +#define USB_SetIn1IntActive(IN1INT_snapshot) \ + ((IN1INT_snapshot) |= IN1INT_IN1__SET) +#endif + +/***************************************************************************//** + * @brief Sets the IN 12interrupt flag to active + * @param IN1INT_snapshot + * Snapshot of the IN1INT register taken previously with the + * @ref USB_GetInInts() function. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetIn2IntActive(uint8_t IN1INT_snapshot); +#else +#define USB_SetIn2IntActive(IN1INT_snapshot) \ + ((IN1INT_snapshot) |= IN1INT_IN2__SET) +#endif + +/***************************************************************************//** + * @brief Sets the IN 3 interrupt flag to active + * @param IN1INT_snapshot + * Snapshot of the IN1INT register taken previously with the + * @ref USB_GetInInts() function. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetIn3IntActive(uint8_t IN1INT_snapshot); +#else +#define USB_SetIn3IntActive(IN1INT_snapshot) \ + ((IN1INT_snapshot) |= IN1INT_IN3__SET) +#endif + +/***************************************************************************//** + * @brief Sets the OUT 1 interrupt flag to active + * @param OUT1INT_snapshot + * Snapshot of the OUT1INT register taken previously with the + * @ref USB_GetOutInts() function. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetOut1IntActive(uint8_t OUT1INT_snapshot); +#else +#define USB_SetOut1IntActive(OUT1INT_snapshot) \ + ((OUT1INT_snapshot) |= OUT1INT_OUT1__SET) +#endif + +/***************************************************************************//** + * @brief Sets the OUT 2 interrupt flag to active + * @param OUT1INT_snapshot + * Snapshot of the OUT1INT register taken previously with the + * @ref USB_GetOutInts() function. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetOut2IntActive(uint8_t OUT1INT_snapshot); +#else +#define USB_SetOut2IntActive(OUT1INT_snapshot) \ + ((OUT1INT_snapshot) |= OUT1INT_OUT2__SET) +#endif + +/***************************************************************************//** + * @brief Sets the OUT 3 interrupt flag to active + * @param OUT1INT_snapshot + * Snapshot of the OUT1INT register taken previously with the + * @ref USB_GetOutInts() function. + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetOut3IntActive(uint8_t OUT1INT_snapshot); +#else +#define USB_SetOut3IntActive(OUT1INT_snapshot) \ + ((OUT1INT_snapshot) |= OUT1INT_OUT3__SET) +#endif + +/***************************************************************************//** + * @brief Enables USB Start-of-Frame, Reset, Resume, and + * Suspend Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableDeviceInts(void); +#else +#define USB_EnableDeviceInts() \ + USB_WRITE_BYTE(CMIE, \ + (CMIE_SOFE__ENABLED \ + | CMIE_RSTINTE__ENABLED \ + | CMIE_RSUINTE__ENABLED \ + | CMIE_SUSINTE__ENABLED)) +#endif + +/***************************************************************************//** + * @brief Enables USB Start-of-Frame Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableSofInt(void); +#else +#define USB_EnableSofInt() USB_SET_BITS(CMIE, CMIE_SOFE__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Start-of-Frame Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableSofInt(void); +#else +#define USB_DisableSofInt() USB_CLEAR_BITS(CMIE, CMIE_SOFE__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Reset Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableResetInt(void); +#else +#define USB_EnableResetInt() USB_SET_BITS(CMIE, CMIE_RSTINTE__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Reset Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableResetInt(void); +#else +#define USB_DisableResetInt() USB_CLEAR_BITS(CMIE, CMIE_RSTINTE__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Resume Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableResumeInt(void); +#else +#define USB_EnableResumeInt() USB_SET_BITS(CMIE, CMIE_RSUINTE__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Resume Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableResumeInt(void); +#else +#define USB_DisableResumeInt() USB_CLEAR_BITS(CMIE, CMIE_RSUINTE__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Suspend Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableSuspendInt(void); +#else +#define USB_EnableSuspendInt() USB_SET_BITS(CMIE, CMIE_SUSINTE__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Suspend Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableSuspendInt(void); +#else +#define USB_DisableSuspendInt() USB_CLEAR_BITS(CMIE, CMIE_SUSINTE__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Endpoint 0 Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableEp0Int(void); +#else +#define USB_EnableEp0Int() USB_SET_BITS(IN1IE, IN1IE_EP0E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Endpoint 0 Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableEp0Int(void); +#else +#define USB_DisableEp0Int() USB_CLEAR_BITS(IN1IE, IN1IE_EP0E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Endpoint 1 IN Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableIn1Int(void); +#else +#define USB_EnableIn1Int() USB_SET_BITS(IN1IE, IN1IE_IN1E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Endpoint 1 IN Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableIn1Int(void); +#else +#define USB_DisableIn1Int() USB_CLEAR_BITS(IN1IE, IN1IE_IN1E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Endpoint 2 IN Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableIn2Int(void); +#else +#define USB_EnableIn2Int() USB_SET_BITS(IN1IE, IN1IE_IN2E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Endpoint 2 IN Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableIn2Int(void); +#else +#define USB_DisableIn2Int() USB_CLEAR_BITS(IN1IE, IN1IE_IN2E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Endpoint 3 IN Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableIn3Int(void); +#else +#define USB_EnableIn3Int() USB_SET_BITS(IN1IE, IN1IE_IN3E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Endpoint 3 IN Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableIn3Int(void); +#else +#define USB_DisableIn3Int() USB_CLEAR_BITS(IN1IE, IN1IE_IN3E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Endpoint 1 OUT Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableOut1Int(void); +#else +#define USB_EnableOut1Int() USB_SET_BITS(OUT1IE, OUT1IE_OUT1E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Endpoint 1 OUT Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableOut1Int(void); +#else +#define USB_DisableOut1Int() USB_CLEAR_BITS(OUT1IE, OUT1IE_OUT1E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Endpoint 2 OUT Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableOut2Int(void); +#else +#define USB_EnableOut2Int() USB_SET_BITS(OUT1IE, OUT1IE_OUT2E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Endpoint 2 OUT Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableOut2Int(void); +#else +#define USB_DisableOut2Int() USB_CLEAR_BITS(OUT1IE, OUT1IE_OUT2E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Endpoint 3 OUT Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableOut3Int(void); +#else +#define USB_EnableOut3Int() USB_SET_BITS(OUT1IE, OUT1IE_OUT3E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Endpoint 3 OUT Interrupts + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableOut3Int(void); +#else +#define USB_DisableOut3Int() USB_CLEAR_BITS(OUT1IE, OUT1IE_OUT3E__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Endpoint 1 + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableEp1(void); +#else +#define USB_EnableEp1() USB_SET_BITS(EENABLE, EENABLE_EEN1__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Endpoint 1 + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableEp1(void); +#else +#define USB_DisableEp1() USB_CLEAR_BITS(EENABLE, EENABLE_EEN1__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Endpoint 2 + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableEp2(void); +#else +#define USB_EnableEp2() USB_SET_BITS(EENABLE, EENABLE_EEN2__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Endpoint 2 + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableEp2(void); +#else +#define USB_DisableEp2() USB_CLEAR_BITS(EENABLE, EENABLE_EEN2__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables USB Endpoint 3 + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableEp3(void); +#else +#define USB_EnableEp3() USB_SET_BITS(EENABLE, EENABLE_EEN3__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables USB Endpoint 3 + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableEp3(void); +#else +#define USB_DisableEp3() USB_CLEAR_BITS(EENABLE, EENABLE_EEN3__ENABLED) +#endif + +/***************************************************************************//** + * @brief Configures Endpoint N for OUT + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnDirectionOut(void); +#else +#define USB_EpnDirectionOut() USB_CLEAR_BITS(EINCSRH, EINCSRH_DIRSEL__IN) +#endif + +/***************************************************************************//** + * @brief Configures Endpoint N for IN + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnDirectionIn(void); +#else +#define USB_EpnDirectionIn() USB_SET_BITS(EINCSRH, EINCSRH_DIRSEL__IN) +#endif + +/***************************************************************************//** + * @brief Enables split mode on Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnEnableSplitMode(void); +#else +#define USB_EpnEnableSplitMode() \ + USB_SET_BITS(EINCSRH, EINCSRH_SPLIT__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables split mode Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnDisableSplitMode(void); +#else +#define USB_EpnDisableSplitMode() \ + USB_CLEAR_BITS(EINCSRH, EINCSRH_SPLIT__ENABLED) +#endif + +/***************************************************************************//** + * @brief Resets the IN endpoint data toggle to '0' + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInClearDataToggle(void); +#else +#define USB_EpnInClearDataToggle() USB_SET_BITS(EINCSRL, EINCSRL_CLRDT__BMASK) +#endif + +/***************************************************************************//** + * @brief Clears sent stall condition on Endpoint N IN + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInClearSentStall(void); +#else +#define USB_EpnInClearSentStall() USB_WRITE_BYTE(EINCSRL, 0) +#endif + +/***************************************************************************//** + * @brief Sends a stall for each IN token on Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInStall(void); +#else +#define USB_EpnInStall() USB_WRITE_BYTE(EINCSRL, EINCSRL_SDSTL__SET) +#endif + +/***************************************************************************//** + * @brief Stops stalling for each IN token on Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInEndStall(void); +#else +#define USB_EpnInEndStall() USB_WRITE_BYTE(EINCSRL, 0) +#endif + +/***************************************************************************//** + * @brief Stops stalling for each IN token on Endpoint N and clears + * the data toggle + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInEndStallAndClearDataToggle(void); +#else +#define USB_EpnInEndStallAndClearDataToggle() \ + USB_WRITE_BYTE(EINCSRL, EINCSRL_CLRDT__BMASK) +#endif + +/***************************************************************************//** + * @brief Flushes In Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInFlush(void); +#else +#define USB_EpnInFlush() \ + do \ + { \ + USB_WRITE_BYTE(EINCSRL, EINCSRL_FLUSH__SET); \ + do \ + { \ + USB_READ_BYTE(EINCSRL); \ + } while (USB0DAT & EINCSRL_FLUSH__SET); \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Clears underrun condition on In Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInClearUnderrun(void); +#else +#define USB_EpnInClearUnderrun() USB_CLEAR_BITS(EINCSRL, EINCSRL_UNDRUN__SET) +#endif + +/***************************************************************************//** + * @brief Sets InPacketReady on In Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnSetInPacketReady(void); +#else +#define USB_EpnSetInPacketReady() USB_SET_BITS(EINCSRL, EINCSRL_INPRDY__SET) +#endif + +/***************************************************************************//** + * @brief Enables double buffering on In Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInEnableDoubleBuffer(void); +#else +#define USB_EpnInEnableDoubleBuffer() \ + USB_SET_BITS(EINCSRH, EINCSRH_DBIEN__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables double buffering on In Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInDisableDoubleBuffer(void); +#else +#define USB_EpnInDisableDoubleBuffer() \ + USB_CLEAR_BITS(EINCSRH, EINCSRH_DBIEN__ENABLED) +#endif + +/***************************************************************************//** + * @brief Configures In Endpoint N for Interrupt/Bulk Mode + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInEnableInterruptBulkMode(void); +#else +#define USB_EpnInEnableInterruptBulkMode() \ + USB_CLEAR_BITS(EINCSRH, EINCSRH_ISO__ENABLED) +#endif + +/***************************************************************************//** + * @brief Configures In Endpoint N for Isochronous Mode + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInEnableIsochronousMode(void); +#else +#define USB_EpnInEnableIsochronousMode() \ + USB_SET_BITS(EINCSRH, EINCSRH_ISO__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables forced data toggle on In Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInEnableForcedDataToggle(void); +#else +#define USB_EpnInEnableForcedDataToggle() \ + USB_SET_BITS(EINCSRH, EINCSRH_FCDT__ALWAYS_TOGGLE) +#endif + +/***************************************************************************//** + * @brief Disables forced data toggle on In Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnInDisableForcedDataToggle(void); +#else +#define USB_EpnInDisableForcedDataToggle() \ + USB_CLEAR_BITS(EINCSRH, EINCSRH_FCDT__ALWAYS_TOGGLE) +#endif + +/***************************************************************************//** + * @brief Resets the OUT endpoint data toggle to '0' + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutClearDataToggle(void); +#else +#define USB_EpnOutClearDataToggle() \ + USB_SET_BITS(EOUTCSRL, EOUTCSRL_CLRDT__BMASK) +#endif + +/***************************************************************************//** + * @brief Clears sent stall condition on Endpoint N OUT + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutClearSentStall(void); +#else +#define USB_EpnOutClearSentStall() \ + USB_CLEAR_BITS(EOUTCSRL, EOUTCSRL_STSTL__BMASK) +#endif + +/***************************************************************************//** + * @brief Sends a stall for each OUT token on Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutStall(void); +#else +#define USB_EpnOutStall() \ + USB_SET_BITS(EOUTCSRL, EOUTCSRL_SDSTL__SET) +#endif + +/***************************************************************************//** + * @brief Stops stalling for each OUT token on Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutEndStall(void); +#else +#define USB_EpnOutEndStall() USB_CLEAR_BITS(EOUTCSRL, EOUTCSRL_SDSTL__SET) +#endif + +/***************************************************************************//** + * @brief Stops stalling for each OUT token on Endpoint N and clears + * the data toggle + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutEndStallAndClearDataToggle(void); +#else +#define USB_EpnOutEndStallAndClearDataToggle() \ + do \ + { \ + USB_READ_BYTE(EOUTCSRL); \ + USB0DAT &= ~EOUTCSRL_SDSTL__SET; \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + USB0DAT |= EOUTCSRL_CLRDT__BMASK; \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Flushes OUT Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutFlush(void); +#else +#define USB_EpnOutFlush() \ + do \ + { \ + USB_WRITE_BYTE(EOUTCSRL, EOUTCSRL_FLUSH__SET); \ + do \ + { \ + USB_READ_BYTE(EOUTCSRL); \ + } while (USB0DAT & EOUTCSRL_FLUSH__SET); \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Clears overrun condition on OUT Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutClearOverrun(void); +#else +#define USB_EpnOutClearOverrun() USB_CLEAR_BITS(EOUTCSRL, EOUTCSRL_OVRUN__SET) +#endif + +/***************************************************************************//** + * @brief Clears OutPacketReady on Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnClearOutPacketReady(void); +#else +#define USB_EpnClearOutPacketReady() \ + USB_CLEAR_BITS(EOUTCSRL, EOUTCSRL_OPRDY__SET) +#endif + +/***************************************************************************//** + * @brief Enables double buffering on OUT Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutEnableDoubleBuffer(void); +#else +#define USB_EpnOutEnableDoubleBuffer() \ + USB_SET_BITS(EOUTCSRH, EOUTCSRH_DBIEN__ENABLED) +#endif + +/***************************************************************************//** + * @brief Disables double buffering on OUT Endpoint N + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutDisableDoubleBuffer(void); +#else +#define USB_EpnOutDisableDoubleBuffer() \ + USB_CLEAR_BITS(EOUTCSRH, EOUTCSRH_DBIEN__ENABLED) +#endif + +/***************************************************************************//** + * @brief Configures OUT Endpoint N for Interrupt/Bulk Mode + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutEnableInterruptBulkMode(void); +#else +#define USB_EpnOutEnableInterruptBulkMode() \ + USB_CLEAR_BITS(EOUTCSRH, EOUTCSRH_ISO__ENABLED) +#endif + +/***************************************************************************//** + * @brief Configures OUT Endpoint N for Isochronous Mode + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EpnOutEnableIsochronousMode(void); +#else +#define USB_EpnOutEnableIsochronousMode() \ + USB_SET_BITS(EOUTCSRH, EOUTCSRH_ISO__ENABLED) +#endif + +/***************************************************************************//** + * @brief Enables FIFO read + * @param fifoNum + * FIFO to read + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableReadFIFO(uint8_t fifoNum); +#else +#define USB_EnableReadFIFO(fifoNum) \ + do \ + { \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + USB0ADR = (USB0ADR_BUSY__SET \ + | USB0ADR_AUTORD__ENABLED \ + | (FIFO0 | (fifoNum))); \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Disables FIFO read + * @param fifoNum + * FIFO that was read from + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableReadFIFO(uint8_t fifoNum); +#else +#define USB_DisableReadFIFO(fifoNum) +#endif + +/***************************************************************************//** + * @brief Reads a byte from the FIFO + * @param readDat + * Memory location to write the byte read from the FIFO + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_GetFIFOByte(uint8_t *readDat); +#else +#define USB_GetFIFOByte(readDat) \ + do \ + { \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + *(readDat) = USB0DAT; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Reads the last byte from the FIFO + * @details The last read must be done with the AUTORD bit cleared. + * This prevents the read from triggering another read + * immediately thereafter. + * @param readDat + * Memory location to write the byte read from the FIFO + * @param fifoNum + * FIFO to read + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_GetLastFIFOByte(uint8_t *readDat, uint8_t fifoNum); +#else +#define USB_GetLastFIFOByte(readDat, fifoNum) \ + do \ + { \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + USB0ADR = (FIFO0 | (fifoNum));\ + *(readDat) = USB0DAT; \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Enables FIFO write + * @param fifoNum + * FIFO to write + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_EnableWriteFIFO(uint8_t fifoNum); +#else +#define USB_EnableWriteFIFO(fifoNum) \ + do \ + { \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + USB0ADR = (FIFO0 | (fifoNum)); \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Disables FIFO write + * @param fifoNum + * FIFO that was written to + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_DisableWriteFIFO(uint8_t fifoNum); +#else +#define USB_DisableWriteFIFO(fifoNum) +#endif + +/***************************************************************************//** + * @brief Writes a byte to the FIFO + * @param writeDat + * Data to write to the FIFO + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SetFIFOByte(uint8_t writeDat); +#else +#define USB_SetFIFOByte(writeDat) \ + do \ + { \ + while (USB0ADR & USB0ADR_BUSY__SET) {} \ + USB0DAT = (writeDat); \ + } while (0) +#endif + +/***************************************************************************//** + * @brief Saves the current SFR page + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_SaveSfrPage(); +#else +#define USB_SaveSfrPage() uint8_t SfrPageSave = SFRPAGE +#endif + +/***************************************************************************//** + * @brief Restores the SFR page + * @note @ref USB_SaveSfrPage() must be called before calling this macro + * @note This function is implemented as a macro. + ******************************************************************************/ +#ifdef IS_DOXYGEN +extern void USB_RestoreSfrPage(); +#else +#define USB_RestoreSfrPage() SFRPAGE = SfrPageSave +#endif + +// ------------------------------- +// Function Prototypes + +/***************************************************************************//** + * @brief Writes a value to INDEX + * @param epsel + * Endpoint index to target + ******************************************************************************/ +extern void USB_SetIndex(uint8_t epsel); + +/***************************************************************************//** + * @brief Reads the USB common interrupt register + * @return Value of CMINT + ******************************************************************************/ +extern uint8_t USB_GetCommonInts(void); + +/***************************************************************************//** + * @brief Reads the USB in interrupt register + * @return Value of IN1INT + ******************************************************************************/ +extern uint8_t USB_GetInInts(void); + +/***************************************************************************//** + * @brief Reads the out interrupt register + * @return Value of OUT1INT + ******************************************************************************/ +extern uint8_t USB_GetOutInts(void); + +/***************************************************************************//** + * @brief Reads the value in INDEX + * @return Value of INDEX + ******************************************************************************/ +extern uint8_t USB_GetIndex(void); + +/***************************************************************************//** + * @brief Determines if the USB is currently suspended + * @return TRUE if USB is in suspend mode + ******************************************************************************/ +extern bool USB_IsSuspended(void); + +/***************************************************************************//** + * @brief Gets Setup End state + * @return TRUE when a control transaction end before software has + * set the DATAEND bit. + ******************************************************************************/ +extern bool USB_GetSetupEnd(void); + +/***************************************************************************//** + * @brief Determines if STALL was send on Endpoint 0 + * @return TRUE after a STALL was sent on Endpoint 0 + ******************************************************************************/ +extern bool USB_Ep0SentStall(void); + +/***************************************************************************//** + * @brief Determines if Out Packet Ready is set on Endpoint 0 + * @return TRUE if Out Packet Ready is set on Endpoint 0 + ******************************************************************************/ +extern bool USB_Ep0InPacketReady(void); + +/***************************************************************************//** + * @brief Determines if In Packet Ready is set on Endpoint 0 + * @return TRUE if In Packet Ready is set on Endpoint 0 + ******************************************************************************/ +extern bool USB_Ep0OutPacketReady(void); + +/***************************************************************************//** + * @brief Gets Endpoint 0 data count + * @return Number of received data bytes in the Endpoint 0 FIFO + ******************************************************************************/ +extern uint8_t USB_Ep0GetCount(void); + +/***************************************************************************//** + * @brief Checks if stall was sent on IN Endpoint N + * @return TRUE if stall was sent on IN Endpoint N, FALSE otherwise + ******************************************************************************/ +extern bool USB_EpnInGetSentStall(void); + +/***************************************************************************//** + * @brief Checks if stall was sent on OUT Endpoint N + * @return TRUE if stall was sent on OUT Endpoint N, FALSE otherwise + ******************************************************************************/ +extern bool USB_EpnGetInPacketReady(void); + +/***************************************************************************//** + * @brief Checks if stall was sent on OUT Endpoint N + * @return TRUE if stall was sent on OUT Endpoint N, FALSE otherwise + ******************************************************************************/ +extern bool USB_EpnOutGetSentStall(void); + +/***************************************************************************//** + * @brief Gets OutPacketReady on OUT Endpoint N + * @return TRUE if OUTPacketReady is set, FALSE otherwise + ******************************************************************************/ +extern bool USB_EpnGetOutPacketReady(void); + +/***************************************************************************//** + * @brief Gets DataError on OUT Endpoint N + * @return TRUE if Data Error bit is set, FALSE otherwise + ******************************************************************************/ +extern bool USB_EpnGetDataError(void); + +/***************************************************************************//** + * @brief Gets number of bytes in the OUT FIFO + * OUT packet + * @return Number of bytes in the FIFO from the last received + * packet + ******************************************************************************/ +extern uint16_t USB_EpOutGetCount(void); + +/***************************************************************************//** + * @brief Reads the USB frame number + * @return The frame number on the most recent SOF packet + ******************************************************************************/ +extern uint16_t USB_GetSofNumber(void); + +/***************************************************************************//** + * @brief Aborts pending IN transactions on the selected endpoint + * @param fifoNum + * Endpoint to abort + ******************************************************************************/ +extern void USB_AbortInEp(uint8_t fifoNum); + +/***************************************************************************//** + * @brief Aborts pending OUT transactions on the selected endpoint + * @param fifoNum + * Endpoint to abort + ******************************************************************************/ +extern void USB_AbortOutEp(uint8_t fifoNum); + +/***************************************************************************//** + * @brief Activates the selected endpoint + * @param ep + * Endpoint to access + * @param packetSize + * Maximum packet size for endpoint + * @param inDir + * Set to 1 if endpoint is IN, 0 if it is OUT + * @param splitMode + * Set to 1 if endpoint is in split mode, 0 if it is not + * @param isoMode + * Set to 1 if endpoint is in isochronous mode, 0 if it is not + ******************************************************************************/ +extern void USB_ActivateEp(uint8_t ep, + uint16_t packetSize, + bool inDir, + bool splitMode, + bool isoMode); + +/** @} (end addtogroup usb_0_runtime USB0 Runtime API) */ +/** @} (end addtogroup usb_0_group USB0 Driver) */ + +// ----------------------------------------------------------------------------- +// Error Checking + +// ------------------------------- +// Verify that the maximum packet size specified for an endpoint is not too +// large for that endpoint + +#ifdef SLAB_USB_EP1IN_USED + #if SLAB_USB_EP1IN_USED + #if SLAB_USB_EP1IN_TRANSFER_TYPE == USB_EPTYPE_ISOC + #error Isochronous transfers are not supported on Endpoint 1. + #else // #if SLAB_USB_EP1IN_TRANSFER_TYPE == USB_EPTYPE_ISOC + #if SLAB_USB_EP1IN_MAX_PACKET_SIZE > 64 + #error EP1IN packet size too large. Interrupt/Bulk packet size must be 64 bytes or less. + #endif // #if SLAB_USB_EP1IN_MAX_PACKET_SIZE > 64 + #endif // #if SLAB_USB_EP1IN_TRANSFER_TYPE == USB_EPTYPE_ISOC + #endif // #if SLAB_USB_EP1IN_USED +#endif // #ifdef SLAB_USB_EP1IN_USED + +#ifdef SLAB_USB_EP1OUT_USED + #if SLAB_USB_EP1OUT_USED + #if SLAB_USB_EP1OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC + #error Isochronous transfers are not supported on Endpoint 1. + #else // #if SLAB_USB_EP1OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC + #if SLAB_USB_EP1OUT_MAX_PACKET_SIZE > 64 + #error EP1OUT packet size too large. Interrupt/Bulk packet size must be 64 bytes or less. + #endif // #if SLAB_USB_EP1OUT_MAX_PACKET_SIZE > 64 + #endif // #if SLAB_USB_EP1OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC + #endif // #if SLAB_USB_EP1OUT_USED +#endif // #ifdef SLAB_USB_EP1OUT_USED + +#ifdef SLAB_USB_EP2IN_USED + #if SLAB_USB_EP2IN_USED + #if SLAB_USB_EP2IN_TRANSFER_TYPE == USB_EPTYPE_ISOC + #error Isochronous transfers are not supported on Endpoint 2. + #else // #if SLAB_USB_EP2IN_TRANSFER_TYPE == USB_EPTYPE_ISOC + #if SLAB_USB_EP2IN_MAX_PACKET_SIZE > 64 + #error EP2IN packet size too large. Interrupt/Bulk packet size must be 64 bytes or less. + #endif // #if SLAB_USB_EP2IN_MAX_PACKET_SIZE > 64 + #endif // #if SLAB_USB_EP2IN_TRANSFER_TYPE == USB_EPTYPE_ISOC + #endif // #if SLAB_USB_EP2IN_USED +#endif // #ifdef SLAB_USB_EP2IN_USED + +#ifdef SLAB_USB_EP2OUT_USED + #if SLAB_USB_EP2OUT_USED + #if SLAB_USB_EP2OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC + #error Isochronous transfers are not supported on Endpoint 2. + #else // #if SLAB_USB_EP2OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC + #if SLAB_USB_EP2OUT_MAX_PACKET_SIZE > 64 + #error EP2OUT packet size too large. Interrupt/Bulk packet size must be 64 bytes or less. + #endif // #if SLAB_USB_EP2OUT_MAX_PACKET_SIZE > 64 + #endif // #if SLAB_USB_EP2OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC + #endif // #if SLAB_USB_EP2OUT_USED +#endif // #ifdef SLAB_USB_EP2OUT_USED + +#ifdef SLAB_USB_EP3IN_USED + #if SLAB_USB_EP3IN_USED + #if SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC + #if SLAB_USB_EP3OUT_USED + #if SLAB_USB_EP3IN_MAX_PACKET_SIZE > 256 + #error EP3IN packet size too large. FIFO 3 split mode packet size must be 256 bytes or less. + #endif // #if SLAB_USB_EP3IN_MAX_PACKET_SIZE > 256 + #else // #if SLAB_USB_EP3OUT_USED + #if SLAB_USB_EP3IN_MAX_PACKET_SIZE > 512 + #error EP3IN packet size too large. FIFO 3 packet size must be 512 bytes or less. + #endif // #if SLAB_USB_EP3IN_MAX_PACKET_SIZE > 512 + #endif // #if SLAB_USB_EP3OUT_USED + #else // #if SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC + #if SLAB_USB_EP3IN_MAX_PACKET_SIZE > 64 + #error EP3IN packet size too large. Interrupt/Bulk packet size must be 64 bytes or less. + #endif // #if SLAB_USB_EP3IN_MAX_PACKET_SIZE > 64 + #endif // #if SLAB_USB_EP3IN_TRANSFER_TYPE == USB_EPTYPE_ISOC + #endif // #if SLAB_USB_EP3IN_USED +#endif // #ifdef SLAB_USB_EP3IN_USED + +#ifdef SLAB_USB_EP3OUT_USED + #if SLAB_USB_EP3OUT_USED + #if SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC + #if SLAB_USB_EP3IN_USED + #if SLAB_USB_EP3OUT_MAX_PACKET_SIZE > 256 + #error EP3OUT packet size too large. FIFO 3 split mode packet size must be 256 bytes or less. + #endif // #if SLAB_USB_EP3OUT_MAX_PACKET_SIZE > 256 + #else // #if SLAB_USB_EP3IN_USED + #if SLAB_USB_EP3OUT_MAX_PACKET_SIZE > 512 + #error EP3OUT packet size too large. FIFO 3 packet size must be 512 bytes or less. + #endif // #if SLAB_USB_EP3OUT_MAX_PACKET_SIZE > 512 + #endif // #if SLAB_USB_EP3IN_USED + #else // #if SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC + #if SLAB_USB_EP3OUT_MAX_PACKET_SIZE > 64 + #error EP3OUT packet size too large. Interrupt/Bulk packet size must be 64 bytes or less. + #endif // #if SLAB_USB_EP3OUT_MAX_PACKET_SIZE > 64 + #endif // #if SLAB_USB_EP3OUT_TRANSFER_TYPE == USB_EPTYPE_ISOC + #endif // #if SLAB_USB_EP3OUT_USED +#endif // #ifdef SLAB_USB_EP3OUT_USED + +#endif // __SILICON_LABS_EFM8_USB_0_H__ diff --git a/efm8/lib/efm8ub1/peripheralDrivers/src/usb_0.c b/efm8/lib/efm8ub1/peripheralDrivers/src/usb_0.c new file mode 100644 index 0000000..91a03ea --- /dev/null +++ b/efm8/lib/efm8ub1/peripheralDrivers/src/usb_0.c @@ -0,0 +1,238 @@ +/**************************************************************************//** + * Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved. + * + * http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt + *****************************************************************************/ + +#include "usb_0.h" +#include +#include + +/** @addtogroup usb_0_runtime USB0 Runtime API */ + +// ----------------------------------------------------------------------------- +// Functions + +// ------------------------------- +// Utility Functions + +/**************************************************************************//** + * @brief Reads a 16-bit indirect USB register value + * @param [in] regAddr + * Address of high byte of 16-bit USB indirect register to read + * @return 16-bit register value + *****************************************************************************/ +static uint16_t USB_GetShortRegister(uint8_t regAddr) +{ + uint16_t retVal; + + USB_READ_BYTE(regAddr); + retVal = (USB0DAT << 8); + USB_READ_BYTE((regAddr - 1)); + retVal |= USB0DAT; + + return retVal; +} + +// ------------------------------- +// USB0 Peripheral Driver Functions + +void USB_SetIndex(uint8_t epsel) +{ + USB_WRITE_BYTE(INDEX, epsel); +} + +uint8_t USB_GetCommonInts(void) +{ + USB_READ_BYTE(CMINT); + return USB0DAT; +} + +uint8_t USB_GetInInts(void) +{ + USB_READ_BYTE(IN1INT); + return USB0DAT; +} + +uint8_t USB_GetOutInts(void) +{ + USB_READ_BYTE(OUT1INT); + return USB0DAT; +} + +uint8_t USB_GetIndex(void) +{ + USB_READ_BYTE(INDEX); + return USB0DAT; +} + +bool USB_IsSuspended(void) +{ + USB_READ_BYTE(POWER); + return USB0DAT & POWER_SUSMD__SUSPENDED; +} + +bool USB_GetSetupEnd(void) +{ + USB_READ_BYTE(E0CSR); + return USB0DAT & E0CSR_SUEND__SET; +} + +bool USB_Ep0SentStall(void) +{ + USB_READ_BYTE(E0CSR); + return USB0DAT & E0CSR_STSTL__SET; +} + +bool USB_Ep0OutPacketReady(void) +{ + USB_READ_BYTE(E0CSR); + return USB0DAT & E0CSR_OPRDY__SET; +} + +bool USB_Ep0InPacketReady(void) +{ + USB_READ_BYTE(E0CSR); + return USB0DAT & E0CSR_INPRDY__SET; +} + +uint8_t USB_Ep0GetCount(void) +{ + USB_READ_BYTE(E0CNT); + return USB0DAT; +} + +bool USB_EpnInGetSentStall(void) +{ + USB_READ_BYTE(EINCSRL); + return (bool)(USB0DAT & EINCSRL_STSTL__SET); +} + +void USB_AbortInEp(uint8_t fifoNum) +{ + USB_SetIndex(fifoNum); + USB_EpnInFlush(); + USB_EpnInFlush(); +} + +bool USB_EpnOutGetSentStall(void) +{ + USB_READ_BYTE(EOUTCSRL); + return (bool)(USB0DAT & EOUTCSRL_STSTL__SET); +} + +bool USB_EpnGetOutPacketReady(void) +{ + USB_READ_BYTE(EOUTCSRL); + return (bool)(USB0DAT & EOUTCSRL_OPRDY__SET); +} + +bool USB_EpnGetDataError(void) +{ + USB_READ_BYTE(EOUTCSRL); + return (bool)(USB0DAT & EOUTCSRL_DATERR__SET); +} + +uint16_t USB_EpOutGetCount(void) +{ + return USB_GetShortRegister(EOUTCNTH); +} + +void USB_AbortOutEp(uint8_t fifoNum) +{ + USB_SetIndex(fifoNum); + USB_EpnOutFlush(); + USB_EpnOutFlush(); +} + +void USB_ActivateEp(uint8_t ep, + uint16_t packetSize, + bool inDir, + bool splitMode, + bool isoMode) +{ + uint8_t CSRH_mask = 0; + uint16_t fifoSize; + + USB_SetIndex(ep); + + // Determine the available fifoSize for a given endpoint based on the + // splitMode setting + fifoSize = (splitMode == true) ? (16 << ep) : (32 << ep); + + if (packetSize <= fifoSize) + { + CSRH_mask |= EINCSRH_DBIEN__ENABLED; + } + + if (isoMode == true) + { + CSRH_mask |= EINCSRH_ISO__ENABLED; + } + + if (inDir == true) + { + CSRH_mask |= EINCSRH_DIRSEL__IN; + + if (splitMode == true) + { + CSRH_mask |= EINCSRH_SPLIT__ENABLED; + } + USB_WRITE_BYTE(EINCSRL, EINCSRL_CLRDT__BMASK); + USB_WRITE_BYTE(EINCSRH, CSRH_mask); + } + else // OUT + { + USB_WRITE_BYTE(EOUTCSRL, EOUTCSRL_CLRDT__BMASK); + USB_WRITE_BYTE(EOUTCSRH, CSRH_mask); + + if (splitMode == false) + { + USB_WRITE_BYTE(EINCSRH, 0); + } + } +} + +uint16_t USB_GetSofNumber(void) +{ + return USB_GetShortRegister(FRAMEH); +} + +bool USB_GetIntsEnabled(void) +{ + SFRPAGE = PG2_PAGE; + return (bool)(EIE2 & EIE2_EUSB0__ENABLED); +} + +bool USB_IsPrefetchEnabled(void) +{ + SFRPAGE = PG2_PAGE; + return (bool)(PFE0CN & PFE0CN_PFEN__ENABLED); +} + +bool USB_IsRegulatorEnabled(void) +{ + SFRPAGE = PG3_PAGE; + return !(REG1CN & REG1CN_REG1ENB__DISABLED); +} + +void USB_SuspendOscillator(void) +{ + uint8_t clkSelSave = CLKSEL & 0x7F; + + CLKSEL = (CLKSEL_CLKDIV__SYSCLK_DIV_8 | CLKSEL_CLKSL__HFOSC0); + SFRPAGE = LEGACY_PAGE; + PCON1 |= PCON1_SUSPEND__SUSPEND; + CLKSEL = clkSelSave; + + // If the target frequency is over 24MHz, our write to CLKSEL will be ignored. + // If this is the case we need to do two writes: one to 24 MHz followed by the + // actual value. + if ((CLKSEL & 0x7F) != clkSelSave) + { + CLKSEL = (CLKSEL_CLKDIV__SYSCLK_DIV_1 | CLKSEL_CLKSL__HFOSC0); + CLKSEL = clkSelSave; + } +} + +/** @} (end addtogroup usb_0_runtime USB0 Runtime API) */ diff --git a/efm8/src/InitDevice.c b/efm8/src/InitDevice.c new file mode 100644 index 0000000..20a38e9 --- /dev/null +++ b/efm8/src/InitDevice.c @@ -0,0 +1,366 @@ +//========================================================= +// src/InitDevice.c: generated by Hardware Configurator +// +// This file will be regenerated when saving a document. +// leave the sections inside the "$[...]" comment tags alone +// or they will be overwritten! +//========================================================= + +// USER INCLUDES +#include +#include "InitDevice.h" + +// USER PROTOTYPES +// USER FUNCTIONS + +// $[Library Includes] +#include "efm8_usb.h" +#include "descriptors.h" +#include "usb_0.h" +// [Library Includes]$ + +//============================================================================== +// enter_DefaultMode_from_RESET +//============================================================================== +extern void enter_DefaultMode_from_RESET(void) { + // $[Config Calls] + // Save the SFRPAGE + uint8_t SFRPAGE_save = SFRPAGE; + WDT_0_enter_DefaultMode_from_RESET(); + PORTS_0_enter_DefaultMode_from_RESET(); + PBCFG_0_enter_DefaultMode_from_RESET(); + CIP51_0_enter_DefaultMode_from_RESET(); + CLOCK_0_enter_DefaultMode_from_RESET(); + TIMER16_2_enter_DefaultMode_from_RESET(); + TIMER16_3_enter_DefaultMode_from_RESET(); + TIMER_SETUP_0_enter_DefaultMode_from_RESET(); + UARTE_1_enter_DefaultMode_from_RESET(); + INTERRUPT_0_enter_DefaultMode_from_RESET(); + USBLIB_0_enter_DefaultMode_from_RESET(); + // Restore the SFRPAGE + SFRPAGE = SFRPAGE_save; + // [Config Calls]$ + +} + +extern void INTERRUPT_0_enter_DefaultMode_from_RESET(void) { + // $[EIE1 - Extended Interrupt Enable 1] + // [EIE1 - Extended Interrupt Enable 1]$ + + // $[EIE2 - Extended Interrupt Enable 2] + // [EIE2 - Extended Interrupt Enable 2]$ + + // $[EIP1H - Extended Interrupt Priority 1 High] + // [EIP1H - Extended Interrupt Priority 1 High]$ + + // $[EIP1 - Extended Interrupt Priority 1 Low] + // [EIP1 - Extended Interrupt Priority 1 Low]$ + + // $[EIP2 - Extended Interrupt Priority 2] + // [EIP2 - Extended Interrupt Priority 2]$ + + // $[EIP2H - Extended Interrupt Priority 2 High] + // [EIP2H - Extended Interrupt Priority 2 High]$ + + // $[IE - Interrupt Enable] + /*********************************************************************** + - Enable each interrupt according to its individual mask setting + - Disable external interrupt 0 + - Disable external interrupt 1 + - Disable all SPI0 interrupts + - Disable all Timer 0 interrupt + - Disable all Timer 1 interrupt + - Disable Timer 2 interrupt + - Disable UART0 interrupt + ***********************************************************************/ + SFRPAGE = 0x00; + IE = IE_EA__ENABLED | IE_EX0__DISABLED | IE_EX1__DISABLED + | IE_ESPI0__DISABLED | IE_ET0__DISABLED | IE_ET1__DISABLED + | IE_ET2__DISABLED | IE_ES0__DISABLED; + // [IE - Interrupt Enable]$ + + // $[IP - Interrupt Priority] + // [IP - Interrupt Priority]$ + + // $[IPH - Interrupt Priority High] + // [IPH - Interrupt Priority High]$ + +} + +extern void USBLIB_0_enter_DefaultMode_from_RESET(void) { + // $[USBD Init] + USBD_Init (&initstruct); + // [USBD Init]$ + +} + +extern void CLOCK_0_enter_DefaultMode_from_RESET(void) { + // $[HFOSC1 Setup] + // Ensure SYSCLK is > 24 MHz before switching to HFOSC1 + SFRPAGE = 0x00; + CLKSEL = CLKSEL_CLKSL__HFOSC0 | CLKSEL_CLKDIV__SYSCLK_DIV_1; + while ((CLKSEL & CLKSEL_DIVRDY__BMASK) == CLKSEL_DIVRDY__NOT_READY) + ; + // [HFOSC1 Setup]$ + + // $[CLKSEL - Clock Select] + /*********************************************************************** + - Clock derived from the Internal High Frequency Oscillator 1 + - SYSCLK is equal to selected clock source divided by 1 + ***********************************************************************/ + CLKSEL = CLKSEL_CLKSL__HFOSC1 | CLKSEL_CLKDIV__SYSCLK_DIV_1; + while ((CLKSEL & CLKSEL_DIVRDY__BMASK) == CLKSEL_DIVRDY__NOT_READY) + ; + // [CLKSEL - Clock Select]$ + +} + +extern void WDT_0_enter_DefaultMode_from_RESET(void) { + // $[WDTCN - Watchdog Timer Control] + SFRPAGE = 0x00; + //Disable Watchdog with key sequence + WDTCN = 0xDE; //First key + WDTCN = 0xAD; //Second key + // [WDTCN - Watchdog Timer Control]$ + +} + +extern void CIP51_0_enter_DefaultMode_from_RESET(void) { + // $[PFE0CN - Prefetch Engine Control] + /*********************************************************************** + - Enable the prefetch engine + - SYSCLK < 50 MHz + ***********************************************************************/ + SFRPAGE = 0x10; + PFE0CN = PFE0CN_PFEN__ENABLED | PFE0CN_FLRT__SYSCLK_BELOW_50_MHZ; + // [PFE0CN - Prefetch Engine Control]$ + +} + +extern void PBCFG_0_enter_DefaultMode_from_RESET(void) { + // $[XBR2 - Port I/O Crossbar 2] + /*********************************************************************** + - Weak Pullups enabled + - Crossbar enabled + - UART1 TX1 RX1 routed to Port pins + - UART1 RTS1 unavailable at Port pin + - UART1 CTS1 unavailable at Port pin + ***********************************************************************/ + XBR2 = XBR2_WEAKPUD__PULL_UPS_ENABLED | XBR2_XBARE__ENABLED + | XBR2_URT1E__ENABLED | XBR2_URT1RTSE__DISABLED + | XBR2_URT1CTSE__DISABLED; + // [XBR2 - Port I/O Crossbar 2]$ + + // $[PRTDRV - Port Drive Strength] + // [PRTDRV - Port Drive Strength]$ + + // $[XBR0 - Port I/O Crossbar 0] + // [XBR0 - Port I/O Crossbar 0]$ + + // $[XBR1 - Port I/O Crossbar 1] + // [XBR1 - Port I/O Crossbar 1]$ + +} + +extern void TIMER_SETUP_0_enter_DefaultMode_from_RESET(void) { + // $[CKCON0 - Clock Control 0] + /*********************************************************************** + - System clock divided by 12 + - Counter/Timer 0 uses the clock defined by the prescale field, SCA + - Timer 2 high byte uses the clock defined by T2XCLK in TMR2CN0 + - Timer 2 low byte uses the system clock + - Timer 3 high byte uses the clock defined by T3XCLK in TMR3CN0 + - Timer 3 low byte uses the system clock + - Timer 1 uses the clock defined by the prescale field, SCA + ***********************************************************************/ + CKCON0 = CKCON0_SCA__SYSCLK_DIV_12 | CKCON0_T0M__PRESCALE + | CKCON0_T2MH__EXTERNAL_CLOCK | CKCON0_T2ML__SYSCLK + | CKCON0_T3MH__EXTERNAL_CLOCK | CKCON0_T3ML__SYSCLK + | CKCON0_T1M__PRESCALE; + // [CKCON0 - Clock Control 0]$ + + // $[CKCON1 - Clock Control 1] + // [CKCON1 - Clock Control 1]$ + + // $[TMOD - Timer 0/1 Mode] + // [TMOD - Timer 0/1 Mode]$ + + // $[TCON - Timer 0/1 Control] + // [TCON - Timer 0/1 Control]$ + +} + +extern void UARTE_1_enter_DefaultMode_from_RESET(void) { + // $[SBCON1 - UART1 Baud Rate Generator Control] + /*********************************************************************** + - Enable the baud rate generator + - Prescaler = 1 + ***********************************************************************/ + SFRPAGE = 0x20; + SBCON1 = SBCON1_BREN__ENABLED | SBCON1_BPS__DIV_BY_1; + // [SBCON1 - UART1 Baud Rate Generator Control]$ + + // $[SMOD1 - UART1 Mode] + // [SMOD1 - UART1 Mode]$ + + // $[UART1FCN0 - UART1 FIFO Control 0] + // [UART1FCN0 - UART1 FIFO Control 0]$ + + // $[SBRLH1 - UART1 Baud Rate Generator High Byte] + /*********************************************************************** + - UART1 Baud Rate Reload High = 0xFF + ***********************************************************************/ + SBRLH1 = (0xFF << SBRLH1_BRH__SHIFT); + // [SBRLH1 - UART1 Baud Rate Generator High Byte]$ + + // $[SBRLL1 - UART1 Baud Rate Generator Low Byte] + /*********************************************************************** + - UART1 Baud Rate Reload Low = 0x30 + ***********************************************************************/ + SBRLL1 = (0x30 << SBRLL1_BRL__SHIFT); + // [SBRLL1 - UART1 Baud Rate Generator Low Byte]$ + + // $[UART1LIN - UART1 LIN Configuration] + // [UART1LIN - UART1 LIN Configuration]$ + + // $[SCON1 - UART1 Serial Port Control] + /*********************************************************************** + - UART1 reception enabled + ***********************************************************************/ + SCON1 |= SCON1_REN__RECEIVE_ENABLED; + // [SCON1 - UART1 Serial Port Control]$ + + // $[UART1FCN1 - UART1 FIFO Control 1] + // [UART1FCN1 - UART1 FIFO Control 1]$ + +} + +extern void TIMER16_2_enter_DefaultMode_from_RESET(void) { + // $[Timer Initialization] + // Save Timer Configuration + uint8_t TMR2CN0_TR2_save; + TMR2CN0_TR2_save = TMR2CN0 & TMR2CN0_TR2__BMASK; + // Stop Timer + TMR2CN0 &= ~(TMR2CN0_TR2__BMASK); + // [Timer Initialization]$ + + // $[TMR2CN1 - Timer 2 Control 1] + // [TMR2CN1 - Timer 2 Control 1]$ + + // $[TMR2CN0 - Timer 2 Control] + // [TMR2CN0 - Timer 2 Control]$ + + // $[TMR2H - Timer 2 High Byte] + // [TMR2H - Timer 2 High Byte]$ + + // $[TMR2L - Timer 2 Low Byte] + // [TMR2L - Timer 2 Low Byte]$ + + // $[TMR2RLH - Timer 2 Reload High Byte] + /*********************************************************************** + - Timer 2 Reload High Byte = 0x44 + ***********************************************************************/ + TMR2RLH = (0x44 << TMR2RLH_TMR2RLH__SHIFT); + // [TMR2RLH - Timer 2 Reload High Byte]$ + + // $[TMR2RLL - Timer 2 Reload Low Byte] + /*********************************************************************** + - Timer 2 Reload Low Byte = 0x80 + ***********************************************************************/ + TMR2RLL = (0x80 << TMR2RLL_TMR2RLL__SHIFT); + // [TMR2RLL - Timer 2 Reload Low Byte]$ + + // $[TMR2CN0] + /*********************************************************************** + - Start Timer 2 running + ***********************************************************************/ + TMR2CN0 |= TMR2CN0_TR2__RUN; + // [TMR2CN0]$ + + // $[Timer Restoration] + // Restore Timer Configuration + TMR2CN0 |= TMR2CN0_TR2_save; + // [Timer Restoration]$ + +} + +extern void TIMER16_3_enter_DefaultMode_from_RESET(void) { + // $[Timer Initialization] + // Save Timer Configuration + uint8_t TMR3CN0_TR3_save; + TMR3CN0_TR3_save = TMR3CN0 & TMR3CN0_TR3__BMASK; + // Stop Timer + TMR3CN0 &= ~(TMR3CN0_TR3__BMASK); + // [Timer Initialization]$ + + // $[TMR3CN1 - Timer 3 Control 1] + // [TMR3CN1 - Timer 3 Control 1]$ + + // $[TMR3CN0 - Timer 3 Control] + // [TMR3CN0 - Timer 3 Control]$ + + // $[TMR3H - Timer 3 High Byte] + // [TMR3H - Timer 3 High Byte]$ + + // $[TMR3L - Timer 3 Low Byte] + // [TMR3L - Timer 3 Low Byte]$ + + // $[TMR3RLH - Timer 3 Reload High Byte] + // [TMR3RLH - Timer 3 Reload High Byte]$ + + // $[TMR3RLL - Timer 3 Reload Low Byte] + // [TMR3RLL - Timer 3 Reload Low Byte]$ + + // $[TMR3CN0] + // [TMR3CN0]$ + + // $[Timer Restoration] + // Restore Timer Configuration + TMR3CN0 |= TMR3CN0_TR3_save; + // [Timer Restoration]$ + +} + +extern void PORTS_0_enter_DefaultMode_from_RESET(void) { + // $[P0 - Port 0 Pin Latch] + // [P0 - Port 0 Pin Latch]$ + + // $[P0MDOUT - Port 0 Output Mode] + /*********************************************************************** + - P0.0 output is push-pull + - P0.1 output is open-drain + - P0.2 output is open-drain + - P0.3 output is open-drain + - P0.4 output is open-drain + - P0.5 output is open-drain + - P0.6 output is open-drain + - P0.7 output is open-drain + ***********************************************************************/ + P0MDOUT = P0MDOUT_B0__PUSH_PULL | P0MDOUT_B1__OPEN_DRAIN + | P0MDOUT_B2__OPEN_DRAIN | P0MDOUT_B3__OPEN_DRAIN + | P0MDOUT_B4__OPEN_DRAIN | P0MDOUT_B5__OPEN_DRAIN + | P0MDOUT_B6__OPEN_DRAIN | P0MDOUT_B7__OPEN_DRAIN; + // [P0MDOUT - Port 0 Output Mode]$ + + // $[P0MDIN - Port 0 Input Mode] + // [P0MDIN - Port 0 Input Mode]$ + + // $[P0SKIP - Port 0 Skip] + // [P0SKIP - Port 0 Skip]$ + + // $[P0MASK - Port 0 Mask] + // [P0MASK - Port 0 Mask]$ + + // $[P0MAT - Port 0 Match] + // [P0MAT - Port 0 Match]$ + +} + +extern void PORTS_1_enter_DefaultMode_from_RESET(void) { + +} + +extern void PORTS_2_enter_DefaultMode_from_RESET(void) { + +} + diff --git a/efm8/src/SILABS_STARTUP.A51 b/efm8/src/SILABS_STARTUP.A51 new file mode 100644 index 0000000..bef2e8d --- /dev/null +++ b/efm8/src/SILABS_STARTUP.A51 @@ -0,0 +1,203 @@ +$NOMOD51 +;------------------------------------------------------------------------------ +; This file is part of the C51 Compiler package +; Copyright (c) 1988-2005 Keil Elektronik GmbH and Keil Software, Inc. +; Version 8.01 +; +; *** <<< Use Configuration Wizard in Context Menu >>> *** +;------------------------------------------------------------------------------ +; STARTUP.A51: This code is executed after processor reset. +; +; To translate this file use A51 with the following invocation: +; +; A51 STARTUP.A51 +; +; To link the modified STARTUP.OBJ file to your application use the following +; Lx51 invocation: +; +; Lx51 your object file list, STARTUP.OBJ controls +; +;------------------------------------------------------------------------------ +; +; User-defined Power-On Initialization of Memory +; +; With the following EQU statements the initialization of memory +; at processor reset can be defined: +; +; IDATALEN: IDATA memory size <0x0-0x100> +; Note: The absolute start-address of IDATA memory is always 0 +; The IDATA space overlaps physically the DATA and BIT areas. +IDATALEN EQU 80H +; +; XDATASTART: XDATA memory start address <0x0-0xFFFF> +; The absolute start address of XDATA memory +XDATASTART EQU 0 +; +; XDATALEN: XDATA memory size <0x0-0xFFFF> +; The length of XDATA memory in bytes. +XDATALEN EQU 0 +; +; PDATASTART: PDATA memory start address <0x0-0xFFFF> +; The absolute start address of PDATA memory +PDATASTART EQU 0H +; +; PDATALEN: PDATA memory size <0x0-0xFF> +; The length of PDATA memory in bytes. +PDATALEN EQU 0H +; +; +;------------------------------------------------------------------------------ +; +; Reentrant Stack Initialization +; +; The following EQU statements define the stack pointer for reentrant +; functions and initialized it: +; +; Stack Space for reentrant functions in the SMALL model. +; IBPSTACK: Enable SMALL model reentrant stack +; Stack space for reentrant functions in the SMALL model. +IBPSTACK EQU 0 ; set to 1 if small reentrant is used. +; IBPSTACKTOP: End address of SMALL model stack <0x0-0xFF> +; Set the top of the stack to the highest location. +IBPSTACKTOP EQU 0xFF +1 ; default 0FFH+1 +; +; +; Stack Space for reentrant functions in the LARGE model. +; XBPSTACK: Enable LARGE model reentrant stack +; Stack space for reentrant functions in the LARGE model. +XBPSTACK EQU 0 ; set to 1 if large reentrant is used. +; XBPSTACKTOP: End address of LARGE model stack <0x0-0xFFFF> +; Set the top of the stack to the highest location. +XBPSTACKTOP EQU 0xFFFF +1 ; default 0FFFFH+1 +; +; +; Stack Space for reentrant functions in the COMPACT model. +; PBPSTACK: Enable COMPACT model reentrant stack +; Stack space for reentrant functions in the COMPACT model. +PBPSTACK EQU 0 ; set to 1 if compact reentrant is used. +; +; PBPSTACKTOP: End address of COMPACT model stack <0x0-0xFFFF> +; Set the top of the stack to the highest location. +PBPSTACKTOP EQU 0xFF +1 ; default 0FFH+1 +; +; +;------------------------------------------------------------------------------ +; +; Memory Page for Using the Compact Model with 64 KByte xdata RAM +; Compact Model Page Definition +; +; Define the XDATA page used for PDATA variables. +; PPAGE must conform with the PPAGE set in the linker invocation. +; +; Enable pdata memory page initalization +PPAGEENABLE EQU 0 ; set to 1 if pdata object are used. +; +; PPAGE number <0x0-0xFF> +; uppermost 256-byte address of the page used for PDATA variables. +PPAGE EQU 0 +; +; SFR address which supplies uppermost address byte <0x0-0xFF> +; most 8051 variants use P2 as uppermost address byte +PPAGE_SFR DATA 0A0H +; +; +;------------------------------------------------------------------------------ + +; Standard SFR Symbols +ACC DATA 0E0H +B DATA 0F0H +SP DATA 81H +DPL DATA 82H +DPH DATA 83H + + NAME ?C_STARTUP + + +?C_C51STARTUP SEGMENT CODE +?STACK SEGMENT IDATA + + RSEG ?STACK + DS 1 + + EXTRN CODE (?C_START) + PUBLIC ?C_STARTUP + + CSEG AT 0 +?C_STARTUP: LJMP STARTUP1 + + RSEG ?C_C51STARTUP + +STARTUP1: + +$IF (SILABS_STARTUP = 1) +EXTRN CODE (SiLabs_Startup) + LCALL SiLabs_Startup +$ENDIF + +IF IDATALEN <> 0 + MOV R0,#IDATALEN - 1 + CLR A +IDATALOOP: MOV @R0,A + DJNZ R0,IDATALOOP +ENDIF + +IF XDATALEN <> 0 + MOV DPTR,#XDATASTART + MOV R7,#LOW (XDATALEN) + IF (LOW (XDATALEN)) <> 0 + MOV R6,#(HIGH (XDATALEN)) +1 + ELSE + MOV R6,#HIGH (XDATALEN) + ENDIF + CLR A +XDATALOOP: MOVX @DPTR,A + INC DPTR + DJNZ R7,XDATALOOP + DJNZ R6,XDATALOOP +ENDIF + +IF PPAGEENABLE <> 0 + MOV PPAGE_SFR,#PPAGE +ENDIF + +IF PDATALEN <> 0 + MOV R0,#LOW (PDATASTART) + MOV R7,#LOW (PDATALEN) + CLR A +PDATALOOP: MOVX @R0,A + INC R0 + DJNZ R7,PDATALOOP +ENDIF + +IF IBPSTACK <> 0 +EXTRN DATA (?C_IBP) + + MOV ?C_IBP,#LOW IBPSTACKTOP +ENDIF + +IF XBPSTACK <> 0 +EXTRN DATA (?C_XBP) + + MOV ?C_XBP,#HIGH XBPSTACKTOP + MOV ?C_XBP+1,#LOW XBPSTACKTOP +ENDIF + +IF PBPSTACK <> 0 +EXTRN DATA (?C_PBP) + MOV ?C_PBP,#LOW PBPSTACKTOP +ENDIF + + MOV SP,#?STACK-1 + +; This code is required if you use L51_BANK.A51 with Banking Mode 4 +; Code Banking +; Select Bank 0 for L51_BANK.A51 Mode 4 +$IF (USE_BANKING = 1) +; Initialize bank mechanism to code bank 0 when using L51_BANK.A51 with Banking Mode 4. +EXTRN CODE (?B_SWITCH0) + CALL ?B_SWITCH0 ; init bank mechanism to code bank 0 +$ENDIF +; + LJMP ?C_START + + END diff --git a/efm8/src/callback.c b/efm8/src/callback.c new file mode 100644 index 0000000..57e5168 --- /dev/null +++ b/efm8/src/callback.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2016, Conor Patrick + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 THE COPYRIGHT OWNER OR 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. + + */ +#include +#include +#include +#include "descriptors.h" + +#define UNUSED(expr) do { (void)(expr); } while (0) + +#define HID_INTERFACE_INDEX 0 + +uint8_t tmpBuffer; + + + +void USBD_ResetCb(void) { +// u2f_print_ev("USBD_ResetCb\r\n"); +} + + +void USBD_DeviceStateChangeCb(USBD_State_TypeDef oldState, + USBD_State_TypeDef newState) { + + UNUSED(oldState); + UNUSED(newState); + +// u2f_print_ev("USBD_DeviceStateChangeCb\r\n"); +} + +bool USBD_IsSelfPoweredCb(void) { + return false; +} + +// Necessary routine for USB HID +USB_Status_TypeDef USBD_SetupCmdCb( + SI_VARIABLE_SEGMENT_POINTER(setup, USB_Setup_TypeDef, MEM_MODEL_SEG)) { + + USB_Status_TypeDef retVal = USB_STATUS_REQ_UNHANDLED; + + + if ((setup->bmRequestType.Type == USB_SETUP_TYPE_STANDARD) + && (setup->bmRequestType.Direction == USB_SETUP_DIR_IN) + && (setup->bmRequestType.Recipient == USB_SETUP_RECIPIENT_INTERFACE)) { + // A HID device must extend the standard GET_DESCRIPTOR command + // with support for HID descriptors. + + switch (setup->bRequest) { + case GET_DESCRIPTOR: + if (setup->wIndex == 0) + { + if ((setup->wValue >> 8) == USB_HID_REPORT_DESCRIPTOR) { + + USBD_Write(EP0, ReportDescriptor0, + EFM8_MIN(sizeof(ReportDescriptor0), setup->wLength), + false); + retVal = USB_STATUS_OK; + + } else if ((setup->wValue >> 8) == USB_HID_DESCRIPTOR) { + + USBD_Write(EP0, (&configDesc[18]), + EFM8_MIN(USB_HID_DESCSIZE, setup->wLength), false); + retVal = USB_STATUS_OK; + + } + } + break; + } + } + else if ((setup->bmRequestType.Type == USB_SETUP_TYPE_CLASS) + && (setup->bmRequestType.Recipient == USB_SETUP_RECIPIENT_INTERFACE) + && (setup->wIndex == HID_INTERFACE_INDEX)) + { + // Implement the necessary HID class specific commands. + switch (setup->bRequest) + { + case USB_HID_SET_IDLE: + if (((setup->wValue & 0xFF) == 0) // Report ID + && (setup->wLength == 0) + && (setup->bmRequestType.Direction != USB_SETUP_DIR_IN)) + { + retVal = USB_STATUS_OK; + } + break; + + case USB_HID_GET_IDLE: + if ((setup->wValue == 0) // Report ID + && (setup->wLength == 1) + && (setup->bmRequestType.Direction == USB_SETUP_DIR_IN)) + { + tmpBuffer = 24; + USBD_Write(EP0, &tmpBuffer, 1, false); + retVal = USB_STATUS_OK; + } + break; + default: + break; + } + } + + return retVal; +} + + + + +uint8_t hidmsgbuf[64]; +uint16_t USBD_XferCompleteCb(uint8_t epAddr, USB_Status_TypeDef status, + uint16_t xferred, uint16_t remaining ) { + + UNUSED(status); + UNUSED(xferred); + UNUSED(remaining); + + + if (epAddr == EP1OUT) + { +// set_app_u2f_hid_msg((struct u2f_hid_msg *) hidmsgbuf ); + } + return 0; +} + + diff --git a/efm8/src/descriptors.c b/efm8/src/descriptors.c new file mode 100644 index 0000000..ee84d4e --- /dev/null +++ b/efm8/src/descriptors.c @@ -0,0 +1,174 @@ +//============================================================================= +// src/descriptors.c: generated by Hardware Configurator +// +// This file is only generated if it does not exist. Modifications in this file +// will persist even if Configurator generates code. To refresh this file, +// you must first delete it and then regenerate code. +//============================================================================= +//----------------------------------------------------------------------------- +// Includes +//----------------------------------------------------------------------------- +#include +#include +#include +#include +#include +#include "descriptors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// HID Report Descriptor for Interface 0 +SI_SEGMENT_VARIABLE(ReportDescriptor0[34], + const uint8_t, + SI_SEG_CODE) = +{ + + 0x06, 0xd0, 0xf1,// USAGE_PAGE (FIDO Alliance) + 0x09, 0x01,// USAGE (Keyboard) + 0xa1, 0x01,// COLLECTION (Application) + + 0x09, 0x20, // USAGE (Input Report Data) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, HID_PACKET_SIZE, // REPORT_COUNT (64) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x09, 0x21, // USAGE(Output Report Data) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, HID_PACKET_SIZE, // REPORT_COUNT (64) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) + + + 0xc0,// END_COLLECTION + +}; +SI_SEGMENT_VARIABLE(deviceDesc[], + const USB_DeviceDescriptor_TypeDef, + SI_SEG_CODE) = +{ + USB_DEVICE_DESCSIZE, // bLength + USB_DEVICE_DESCRIPTOR,// bLength + htole16(0x0200),// bcdUSB + 0,// bDeviceClass + 0,// bDeviceSubClass + 0,// bDeviceProtocol + 64,// bMaxPacketSize + USB_VENDOR_ID,// idVendor + USB_PRODUCT_ID,// idProduct + htole16(0x0100),// bcdDevice + 1,// iManufacturer + 2,// iProduct + 3,// iSerialNumber + 1,// bNumConfigurations +}; + +SI_SEGMENT_VARIABLE(configDesc[], + const uint8_t, + SI_SEG_CODE) = +{ + USB_CONFIG_DESCSIZE, // bLength + USB_CONFIG_DESCRIPTOR,// bLength + 0x29,// wTotalLength(LSB) + 0x00,// wTotalLength(MSB) + 1,// bNumInterfaces + 1,// bConfigurationValue + 0,// iConfiguration + + CONFIG_DESC_BM_RESERVED_D7,// bmAttrib: Bus powered + + CONFIG_DESC_MAXPOWER_mA(100),// bMaxPower: 100 mA + + //Interface 0 Descriptor + USB_INTERFACE_DESCSIZE,// bLength + USB_INTERFACE_DESCRIPTOR,// bDescriptorType + 0,// bInterfaceNumber + 0,// bAlternateSetting + 2,// bNumEndpoints + 3,// bInterfaceClass: HID (Human Interface Device) + 0,// bInterfaceSubClass + 0,// bInterfaceProtocol + 4,// iInterface + + //HID Descriptor + USB_HID_DESCSIZE,// bLength + USB_HID_DESCRIPTOR,// bLength + 0x11,// bcdHID (LSB) + 0x01,// bcdHID (MSB) + 0,// bCountryCode + 1,// bNumDescriptors + USB_HID_REPORT_DESCRIPTOR,// bDescriptorType + sizeof( ReportDescriptor0 ),// wDescriptorLength(LSB) + sizeof( ReportDescriptor0 )>>8,// wDescriptorLength(MSB) + + //Endpoint 1 IN Descriptor + USB_ENDPOINT_DESCSIZE,// bLength + USB_ENDPOINT_DESCRIPTOR,// bDescriptorType + 0x81,// bEndpointAddress + USB_EPTYPE_INTR,// bAttrib + HID_PACKET_SIZE,// wMaxPacketSize (LSB) + 0x00,// wMaxPacketSize (MSB) + 5,// bInterval + + //Endpoint 1 OUT Descriptor + USB_ENDPOINT_DESCSIZE,// bLength + USB_ENDPOINT_DESCRIPTOR,// bDescriptorType + 0x01,// bEndpointAddress + USB_EPTYPE_INTR,// bAttrib + HID_PACKET_SIZE,// wMaxPacketSize (LSB) + 0x00,// wMaxPacketSize (MSB) + 5,// bInterval +}; + +#define LANG_STRING htole16( SLAB_USB_LANGUAGE ) +#define MFR_STRING 'S','i','l','i','c','o','n',' ','L','a','b','s','\0' +#define MFR_SIZE 13 +#define PROD_STRING 'E','O','S',' ','W','a','l','l','e','t','\0' +#define PROD_SIZE 11 +#define SER_STRING '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','\0' +#define SER_SIZE 17 +#define CFG_STRING 'C','o','n','f','i','g',' ','#','1','\0' +#define CFG_SIZE 10 +#define INT0_STRING 'E','O','S',' ','W','a','l','l','e','t','\0' +#define INT0_SIZE 11 + +LANGID_STATIC_CONST_STRING_DESC( langDesc[], LANG_STRING ); +UTF16LE_PACKED_STATIC_CONST_STRING_DESC( mfrDesc[], MFR_STRING, MFR_SIZE); +UTF16LE_PACKED_STATIC_CONST_STRING_DESC( prodDesc[], PROD_STRING, PROD_SIZE); +UTF16LE_PACKED_STATIC_CONST_STRING_DESC( serDesc[], SER_STRING, SER_SIZE); +UTF16LE_PACKED_STATIC_CONST_STRING_DESC( cfgDesc[], CFG_STRING, CFG_SIZE); +UTF16LE_PACKED_STATIC_CONST_STRING_DESC( int0Desc[], INT0_STRING, INT0_SIZE); + + + +//----------------------------------------------------------------------------- +SI_SEGMENT_POINTER(myUsbStringTable_USEnglish[], + static const USB_StringDescriptor_TypeDef, + const SI_SEG_CODE) = +{ + langDesc, + mfrDesc, + prodDesc, + serDesc, + int0Desc, + +}; + +//----------------------------------------------------------------------------- +SI_SEGMENT_VARIABLE(initstruct, + const USBD_Init_TypeDef, + SI_SEG_CODE) = +{ + deviceDesc, // deviceDescriptor + configDesc,// configDescriptor + myUsbStringTable_USEnglish,// stringDescriptors + sizeof(myUsbStringTable_USEnglish) / sizeof(void *)// numberOfStrings +}; + +#ifdef __cplusplus +} +#endif + diff --git a/efm8/src/main.c b/efm8/src/main.c new file mode 100644 index 0000000..ba0029f --- /dev/null +++ b/efm8/src/main.c @@ -0,0 +1,17 @@ +#include +#include "InitDevice.h" +#include "efm8_usb.h" +#include "printing.h" + + + + +int main(void) { + enter_DefaultMode_from_RESET(); + + cprints("hello,world\r\n"); + + while (1) { + + } +} diff --git a/efm8/src/printing.c b/efm8/src/printing.c new file mode 100644 index 0000000..ca5b87c --- /dev/null +++ b/efm8/src/printing.c @@ -0,0 +1,171 @@ +/* + * printing.c + * + * Created on: Jun 25, 2018 + * Author: conor + */ + +#include +#include +#include +#include +#include "printing.h" + +void putf(char c) +{ + uint8_t i; + SBUF0 = c; + // Blocking delay that works for 115200 baud on this device (<1ms) + for (i=0; i<200; i++){} + for (i=0; i<200; i++){} + for (i=0; i<190; i++){} + watchdog(); +} + + +void dump_hex(uint8_t* hex, uint8_t len) +{ + uint8_t i; + for (i=0 ; i < len ; i++) + { + if (hex[i]<0x10) + { + putf('0'); + } + cputb(hex[i]); + } + cprints("\r\n"); +} + + +void cprints(char* d) +{ + while(*d) + { + // UART0 output queue + putf(*d++); + } +} + +static void int2str_reduce_n(char ** snum, uint32_t copy, uint8_t n) +{ + do + { + copy /= n; + }while(copy); +} + + +static const char * __digits = "0123456789abcdef"; +static char xdata __int2str_buf[9]; + +static void int2str_map_n(char ** snum, uint32_t i, uint8_t n) +{ + do + { + *--*snum = __digits[i % n]; + i /= n; + }while(i); +} + +#define dint2str(i) __int2strn(i,10) +#define xint2str(i) __int2strn(i,16) + +char * __int2strn(int32_t i, uint8_t n) +{ + char * snum = __int2str_buf; + if (i<0) *snum++ = '-'; + int2str_reduce_n(&snum, i, n); + *snum = '\0'; + int2str_map_n(&snum, i, n); + return snum; +} + +void cputd(int32_t i) +{ + cprints(dint2str((int32_t)i)); +} + +void cputx(int32_t i) +{ + cprints(xint2str(i)); +} + +static void put_space() +{ + cprints(" "); +} +static void put_line() +{ + cprints("\r\n"); +} + +void cprintd(const char * tag, uint8_t c, ...) +{ + va_list args; + cprints(tag); + va_start(args,c); + while(c--) + { + cputd((int32_t)va_arg(args, int16_t)); + + } + put_line(); + va_end(args); +} + +void cprintl(const char * tag, uint8_t c, ...) +{ + va_list args; + cprints(tag); + va_start(args,c); + while(c--) + { + cputl(va_arg(args, int32_t)); + cprints(" "); + } + put_line(); + va_end(args); +} + +void cprintx(const char * tag, uint8_t c, ...) +{ + va_list args; + cprints(tag); + va_start(args,c); + while(c--) + { + cputx((int32_t)va_arg(args, uint16_t)); + cprints(" "); + } + put_line(); + va_end(args); +} + +void cprintb(const char * tag, uint8_t c, ...) +{ + va_list args; + cprints(tag); + va_start(args,c); + while(c--) + { + cputb(va_arg(args, uint8_t)); + put_space(); + } + put_line(); + va_end(args); +} + +void cprintlx(const char * tag, uint8_t c, ...) +{ + va_list args; + cprints(tag); + va_start(args,c); + while(c--) + { + cputlx(va_arg(args, int32_t)); + put_space(); + } + put_line(); + va_end(args); +} diff --git a/cose_key.h b/fido2/cose_key.h similarity index 100% rename from cose_key.h rename to fido2/cose_key.h diff --git a/crypto.c b/fido2/crypto.c similarity index 99% rename from crypto.c rename to fido2/crypto.c index 3007cd9..d11efe3 100644 --- a/crypto.c +++ b/fido2/crypto.c @@ -6,6 +6,8 @@ #include #include + + #include "util.h" #include "crypto.h" diff --git a/crypto.h b/fido2/crypto.h similarity index 100% rename from crypto.h rename to fido2/crypto.h diff --git a/ctap.c b/fido2/ctap.c similarity index 100% rename from ctap.c rename to fido2/ctap.c diff --git a/ctap.h b/fido2/ctap.h similarity index 91% rename from ctap.h rename to fido2/ctap.h index 9fd4c73..013fa9d 100644 --- a/ctap.h +++ b/fido2/ctap.h @@ -2,6 +2,7 @@ #define _CTAP_H #include "cbor.h" +#include "device.h" #define CTAP_MAKE_CREDENTIAL 0x01 #define CTAP_GET_ASSERTION 0x02 @@ -257,25 +258,7 @@ void ctap_reset(); int8_t ctap_device_locked(); -// Test for user presence -// Return 1 for user is present, 0 user not present -extern int ctap_user_presence_test(); -// Generate @num bytes of random numbers to @dest -// return 1 if success, error otherwise -extern int ctap_generate_rng(uint8_t * dst, size_t num); - -// Increment atomic counter and return it. -// Must support two counters, @sel selects counter0 or counter1. -uint32_t ctap_atomic_count(int sel); - -// Verify the user -// return 1 if user is verified, 0 if not -extern int ctap_user_verification(uint8_t arg); - -// Must be implemented by application -// data is HID_MESSAGE_SIZE long in bytes -extern void ctaphid_write_block(uint8_t * data); #endif diff --git a/ctap_errors.h b/fido2/ctap_errors.h similarity index 100% rename from ctap_errors.h rename to fido2/ctap_errors.h diff --git a/ctap_parse.c b/fido2/ctap_parse.c similarity index 100% rename from ctap_parse.c rename to fido2/ctap_parse.c diff --git a/ctap_parse.h b/fido2/ctap_parse.h similarity index 100% rename from ctap_parse.h rename to fido2/ctap_parse.h diff --git a/ctaphid.c b/fido2/ctaphid.c similarity index 100% rename from ctaphid.c rename to fido2/ctaphid.c diff --git a/ctaphid.h b/fido2/ctaphid.h similarity index 100% rename from ctaphid.h rename to fido2/ctaphid.h diff --git a/fido2/device.h b/fido2/device.h new file mode 100644 index 0000000..425f2ef --- /dev/null +++ b/fido2/device.h @@ -0,0 +1,44 @@ +#ifndef _DEVICE_H +#define _DEVICE_H + +void device_init(); + +uint64_t millis(); + +// HID message size in bytes +#define HID_MESSAGE_SIZE 64 + +void usbhid_init(); + +int usbhid_recv(uint8_t * msg); + +void usbhid_send(uint8_t * msg); + +void usbhid_close(); + +void main_loop_delay(); + +void heartbeat(); + + +// Test for user presence +// Return 1 for user is present, 0 user not present +extern int ctap_user_presence_test(); + +// Generate @num bytes of random numbers to @dest +// return 1 if success, error otherwise +extern int ctap_generate_rng(uint8_t * dst, size_t num); + +// Increment atomic counter and return it. +// Must support two counters, @sel selects counter0 or counter1. +uint32_t ctap_atomic_count(int sel); + +// Verify the user +// return 1 if user is verified, 0 if not +extern int ctap_user_verification(uint8_t arg); + +// Must be implemented by application +// data is HID_MESSAGE_SIZE long in bytes +extern void ctaphid_write_block(uint8_t * data); + +#endif diff --git a/log.c b/fido2/log.c similarity index 100% rename from log.c rename to fido2/log.c diff --git a/log.h b/fido2/log.h similarity index 100% rename from log.h rename to fido2/log.h diff --git a/main.c b/fido2/main.c similarity index 100% rename from main.c rename to fido2/main.c index ea23f4d..5c2c02f 100644 --- a/main.c +++ b/fido2/main.c @@ -38,8 +38,8 @@ int main(int argc, char * argv[]) TAG_ERR ); - printf1(TAG_GEN,"init device\n"); device_init(); + printf1(TAG_GEN,"init device\n"); printf1(TAG_GEN,"init ctaphid\n"); ctaphid_init(); diff --git a/stubs.c b/fido2/stubs.c similarity index 100% rename from stubs.c rename to fido2/stubs.c diff --git a/test_power.c b/fido2/test_power.c similarity index 100% rename from test_power.c rename to fido2/test_power.c diff --git a/u2f.c b/fido2/u2f.c similarity index 100% rename from u2f.c rename to fido2/u2f.c diff --git a/u2f.h b/fido2/u2f.h similarity index 100% rename from u2f.h rename to fido2/u2f.h diff --git a/util.c b/fido2/util.c similarity index 100% rename from util.c rename to fido2/util.c diff --git a/util.h b/fido2/util.h similarity index 100% rename from util.h rename to fido2/util.h diff --git a/old-crypto.c b/old-crypto.c deleted file mode 100644 index 9fa66cc..0000000 --- a/old-crypto.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Wrapper for crypto implementation on device - * - * */ -#include -#include -#include - -#include "sdk_common.h" -#include "nrf_assert.h" -#include "nrf_log.h" -#include "nrf_log_ctrl.h" -#include "nrf_log_default_backends.h" -#include "nrf_crypto.h" -#include "nrf_crypto_ecc.h" -#include "nrf_crypto_error.h" -#include "nrf_crypto_ecdsa.h" -#include "mem_manager.h" - - -#include "util.h" -#include "crypto.h" -#include "sha256.h" -#include "uECC.h" -#include "aes.h" -#include "ctap.h" - - -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 uint8_t * _signing_key = NULL; - -// 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() -{ - int ret; - ret = nrf_mem_init(); - if (ret != NRF_SUCCESS) - { - printf("nrf_mem_init fail %d\n", ret); - exit(1); - } - - ret = nrf_crypto_init(); - if (ret != NRF_SUCCESS) - { - printf("nrf_crypto_init fail 0x%02x\n", ret); - printf("nrf_crypto_init fail %s\n", nrf_strerror_get(ret)); - printf("nrf_crypto_init fail %s\n", nrf_strerror_get(ret)); - printf("nrf_crypto_init fail %s\n", nrf_strerror_get(ret)); - exit(1); - } - - uECC_set_rng((uECC_RNG_Function)ctap_generate_rng); -} - - -void crypto_ecc256_load_attestation_key() -{ - _signing_key = attestation_key; -} - -void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig) -{ - nrf_crypto_ecc_private_key_t privkey; - nrf_crypto_ecdsa_sign_context_t context; - ret_code_t ret = NRF_SUCCESS; - size_t sigsz; - - /*dump_hex(_signing_key,32);*/ - memset(&privkey, 0, sizeof(nrf_crypto_ecc_private_key_t)); - memset(&context, 0, sizeof(nrf_crypto_ecdsa_sign_context_t)); - - ret = nrf_crypto_ecc_private_key_from_raw(&g_nrf_crypto_ecc_secp256r1_curve_info, - &privkey, - _signing_key, - (size_t)32); - if (ret != NRF_SUCCESS) - { - printf("private_key_from_raw failed\n"); - exit(1); - } - - sigsz = 64; - ret = nrf_crypto_ecdsa_sign(&context, - &privkey, - data, - (size_t)len, - sig, - &sigsz); - if (ret != NRF_SUCCESS) - { - printf("crypto_ecdsa failed\n"); - exit(1); - } - - if (sigsz != 64) - { - printf("sig wrong size %d\n", sigsz); - exit(1); - } - - ret = nrf_crypto_ecc_private_key_free(&privkey); - - if (ret != NRF_SUCCESS) - { - printf("crypto free failed\n"); - exit(1); - } - -} - - -/*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) -{ - nrf_crypto_ecc_private_key_t nrfprivkey; - nrf_crypto_ecc_public_key_calculate_context_t context; - nrf_crypto_ecc_public_key_t nrfpubkey; - uint8_t privkey[32]; - uint8_t pubkey[64]; - size_t sz; - ret_code_t ret = NRF_SUCCESS; - memset(&nrfprivkey, 0, sizeof(nrf_crypto_ecc_private_key_t)); - memset(&context, 0, sizeof(nrf_crypto_ecc_public_key_calculate_context_t)); - - generate_private_key(data,len,NULL,0,privkey); - - - ret = nrf_crypto_ecc_private_key_from_raw(&g_nrf_crypto_ecc_secp256r1_curve_info, - &nrfprivkey, - privkey, - (size_t)32); - if (ret != NRF_SUCCESS) - { - printf("private_key_from_raw failed\n"); - exit(1); - } - - ret = nrf_crypto_ecc_public_key_calculate(&context, &nrfprivkey, &nrfpubkey); - - if (ret != NRF_SUCCESS) - { - printf("public key compute failed: %s\n", nrf_strerror_get(ret)); - exit(1); - } - - sz = sizeof(pubkey); - nrf_crypto_ecc_public_key_to_raw(&nrfpubkey, pubkey, &sz); - - memmove(x,pubkey,32); - memmove(y,pubkey+32,32); - - nrf_crypto_ecc_public_key_free(&nrfpubkey); - nrf_crypto_ecc_private_key_free(&nrfprivkey); - -} - -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; -} - -void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey) -{ - if (uECC_make_key(pubkey, privkey, uECC_secp256r1()) != 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, uECC_secp256r1()) != 1) - { - printf("Error, uECC_shared_secret failed\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); -} - -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; - - diff --git a/device.c b/pc/device.c similarity index 100% rename from device.c rename to pc/device.c diff --git a/convert_log_to_c.py b/tools/convert_log_to_c.py similarity index 100% rename from convert_log_to_c.py rename to tools/convert_log_to_c.py diff --git a/ctap_test.py b/tools/ctap_test.py similarity index 100% rename from ctap_test.py rename to tools/ctap_test.py diff --git a/tools/gencert/attest b/tools/gencert/attest new file mode 100644 index 0000000..1496084 --- /dev/null +++ b/tools/gencert/attest @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB+zCCAaGgAwIBAgIBADAKBggqhkjOPQQDAjAsMQswCQYDVQQGEwJVUzELMAkG +A1UECAwCTUQxEDAOBgNVBAoMB1RFU1QgQ0EwIBcNMTgwNTEwMDMwNjIwWhgPMjA2 +ODA0MjcwMzA2MjBaMHwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNRDEPMA0GA1UE +BwwGTGF1cmVsMRUwEwYDVQQKDAxURVNUIENPTVBBTlkxIjAgBgNVBAsMGUF1dGhl +bnRpY2F0b3IgQXR0ZXN0YXRpb24xFDASBgNVBAMMC2Nvbm9ycHAuY29tMFkwEwYH +KoZIzj0CAQYIKoZIzj0DAQcDQgAERakCwS6cCjP6PoRQSrgC3E25rxWxtjrqjT8D +A1VlfXA/tAKkl/SDuKb5PNAYrZIMt4paPhRIku8I+Mrq+zKrIKNiMGAwRgYDVR0j +BD8wPaEwpC4wLDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1EMRAwDgYDVQQKDAdU +RVNUIENBggkA98nsifJjlNkwCQYDVR0TBAIwADALBgNVHQ8EBAMCBPAwCgYIKoZI +zj0EAwIDSAAwRQIgGDiwRQNpqqe3OGIBrySXXn50ZBuje/fm0695KNvcpYgCIQDN +BvHjqxYhjtjAFK8JT1tz716eS+c1692bbY9988Q61w== +-----END CERTIFICATE----- diff --git a/tools/ca_sign.sh b/tools/gencert/ca_sign.sh similarity index 100% rename from tools/ca_sign.sh rename to tools/gencert/ca_sign.sh diff --git a/tools/cbytes.py b/tools/gencert/cbytes.py similarity index 100% rename from tools/cbytes.py rename to tools/gencert/cbytes.py diff --git a/tools/dump_pem.py b/tools/gencert/dump_pem.py similarity index 100% rename from tools/dump_pem.py rename to tools/gencert/dump_pem.py diff --git a/tools/genca.sh b/tools/gencert/genca.sh similarity index 100% rename from tools/genca.sh rename to tools/gencert/genca.sh diff --git a/tools/v3.ext b/tools/gencert/v3.ext similarity index 100% rename from tools/v3.ext rename to tools/gencert/v3.ext