diff --git a/simplelink/CMakeLists.txt b/simplelink/CMakeLists.txt index 55e2a03..25eb8a6 100644 --- a/simplelink/CMakeLists.txt +++ b/simplelink/CMakeLists.txt @@ -54,6 +54,35 @@ if(CONFIG_HAS_CC3220SDK) PROPERTIES COMPILE_FLAGS -Wno-incompatible-pointer-types) # driver.c warns on incompatible-pointer-types endif() +elseif(CONFIG_HAS_CC13X2_CC26X2_SDK) + zephyr_include_directories( + . + source + ) + + zephyr_library() + zephyr_library_compile_definitions(${COMPILER}) + zephyr_library_sources( + source/ti/drivers/power/PowerCC26X2.c + source/ti/drivers/power/PowerCC26X2_calibrateRCOSC.c + source/ti/drivers/utils/List.c + + kernel/zephyr/dpl/ClockP_zephyr.c + kernel/zephyr/dpl/HwiP_zephyr.c + ) + + if(CONFIG_SOC_CC1352R) + set_source_files_properties(source/ti/drivers/power/PowerCC26X2.c + PROPERTIES COMPILE_DEFINITIONS "DeviceFamily_CC13X2;${COMPILER}" ) + set_source_files_properties(source/ti/drivers/power/PowerCC26X2_calibrateRCOSC.c + PROPERTIES COMPILE_DEFINITIONS "DeviceFamily_CC13X2;${COMPILER}" ) + else() + set_source_files_properties(source/ti/drivers/power/PowerCC26X2.c + PROPERTIES COMPILE_DEFINITIONS "DeviceFamily_CC26X2;${COMPILER}" ) + set_source_files_properties(source/ti/drivers/power/PowerCC26X2_calibrateRCOSC.c + PROPERTIES COMPILE_DEFINITIONS "DeviceFamily_CC26X2;${COMPILER}" ) + endif() + elseif(CONFIG_HAS_MSP432P4XXSDK) zephyr_include_directories( source diff --git a/simplelink/README b/simplelink/README index b8a1adb..c289b3f 100644 --- a/simplelink/README +++ b/simplelink/README @@ -14,11 +14,13 @@ The current version supported in Zephyr is the SimpleLink CC13x2 and CC26x2 SDK The driver library source is copied from the SDK, as follows: - EXT_DIR=$ZEPHYR_BASE/ext/hal/ti/simplelink/source/ti + EXT_DIR=$HOME/hal_ti/simplelink/source/ti pushd simplelink_cc13x2_26x2_sdk_3_10_00_53/source/ti find devices/cc13x2_cc26x2/{driverlib,inc,rf_patches} -depth -name '*.[c|h]' -print0 | cpio --null -pvdm $EXT_DIR find devices/cc13x2_cc26x2/startup_files -depth -name ccfg.c -print0 | cpio --null -pvd $EXT_DIR + cp -r drivers/power/* $EXT_DIR/drivers/power/. find $EXT_DIR/devices/cc13x2_cc26x2 -name '*.[c|h]' -exec dos2unix {} \; + find $EXT_DIR/drivers/power -name '*.[c|h]' -exec dos2unix {} \; popd The source file startup_files/ccfg.c has been modified to use the appropriate @@ -40,7 +42,7 @@ The current version supported in Zephyr is the SimpleLink CC32xx SDK Source files from select subdirectories are copied from the source/ti/ subdirectory of the SDK installation, as follows: - EXT_DIR=$ZEPHYR_BASE/ext/hal/ti/simplelink/source/ti + EXT_DIR=$HOME/hal_ti/simplelink/source/ti find devices -not -path '*/\.*' -name '*.[c|h]' | \ cpio -pdm $EXT_DIR find drivers -name '*.[c|h]' | \ diff --git a/simplelink/source/ti/drivers/power/PowerCC26X2.c b/simplelink/source/ti/drivers/power/PowerCC26X2.c new file mode 100644 index 0000000..ca6beb5 --- /dev/null +++ b/simplelink/source/ti/drivers/power/PowerCC26X2.c @@ -0,0 +1,1317 @@ +/* + * Copyright (c) 2015-2019, Texas Instruments Incorporated + * 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 Texas Instruments Incorporated 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 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. + */ +/* + * ======== PowerCC26X2.c ======== + */ + +#include + +#include +#include +#include +#include + +#include +#include + +/* driverlib header files */ +#include +#include DeviceFamily_constructPath(inc/hw_types.h) +#include DeviceFamily_constructPath(inc/hw_prcm.h) +#include DeviceFamily_constructPath(inc/hw_nvic.h) +#include DeviceFamily_constructPath(inc/hw_aux_sysif.h) +#include DeviceFamily_constructPath(inc/hw_aon_rtc.h) +#include DeviceFamily_constructPath(inc/hw_memmap.h) +#include DeviceFamily_constructPath(inc/hw_ccfg.h) +#include DeviceFamily_constructPath(inc/hw_rfc_pwr.h) +#include DeviceFamily_constructPath(inc/hw_aon_pmctl.h) +#include DeviceFamily_constructPath(inc/hw_fcfg1.h) +#include DeviceFamily_constructPath(inc/hw_ints.h) +#include DeviceFamily_constructPath(driverlib/sys_ctrl.h) +#include DeviceFamily_constructPath(driverlib/pwr_ctrl.h) +#include DeviceFamily_constructPath(driverlib/prcm.h) +#include DeviceFamily_constructPath(driverlib/aon_ioc.h) +#include DeviceFamily_constructPath(driverlib/aon_rtc.h) +#include DeviceFamily_constructPath(driverlib/aon_event.h) +#include DeviceFamily_constructPath(driverlib/osc.h) +#include DeviceFamily_constructPath(driverlib/cpu.h) +#include DeviceFamily_constructPath(driverlib/vims.h) +#include DeviceFamily_constructPath(driverlib/rfc.h) +#include DeviceFamily_constructPath(driverlib/sys_ctrl.h) +#include DeviceFamily_constructPath(driverlib/driverlib_release.h) +#include DeviceFamily_constructPath(driverlib/setup.h) +#include DeviceFamily_constructPath(driverlib/ccfgread.h) + +static unsigned int configureXOSCHF(unsigned int action); +static unsigned int nopResourceHandler(unsigned int action); +static unsigned int configureRFCoreClocks(unsigned int action); +static void switchXOSCHF(void); +static void disableLFClockQualifiers(void); +static void emptyClockFunc(uintptr_t arg); +static int_fast16_t notify(uint_fast16_t eventType); +static void oscillatorISR(uintptr_t arg); + +/* RCOSC calibration functions functions */ +extern void PowerCC26X2_calibrate(void); +extern bool PowerCC26X2_initiateCalibration(void); +extern void PowerCC26X2_auxISR(uintptr_t arg); +extern void PowerCC26X2_RCOSC_clockFunc(uintptr_t arg); + +/* Externs */ +extern const PowerCC26X2_Config PowerCC26X2_config; + +/* Module_State */ +PowerCC26X2_ModuleState PowerCC26X2_module = { + .notifyList = {0}, /* list of registered notifications */ + .constraintMask = 0, /* the constraint mask */ + .clockObj = {0}, /* Clock object for scheduling wakeups */ + .tdcHwi = {0}, /* hwi object for calibration */ + .oscHwi = {0}, /* hwi object for oscillators */ + .nDeltaFreqCurr = 0, /* RCOSC calibration variable */ + .nCtrimCurr = 0, /* RCOSC calibration variable */ + .nCtrimFractCurr = 0, /* RCOSC calibration variable */ + .nCtrimNew = 0, /* RCOSC calibration variable */ + .nCtrimFractNew = 0, /* RCOSC calibration variable */ + .nRtrimNew = 0, /* RCOSC calibration variable */ + .nRtrimCurr = 0, /* RCOSC calibration variable */ + .nDeltaFreqNew = 0, /* RCOSC calibration variable */ + .bRefine = false, /* RCOSC calibration variable */ + .state = Power_ACTIVE, /* current transition state */ + .xoscPending = false, /* is XOSC_HF activation in progress? */ + .calLF = false, /* calibrate RCOSC_LF? */ + .auxHwiState = 0, /* calibration AUX ISR state */ + .busyCal = false, /* already busy calibrating */ + .calStep = 0, /* current calibration step */ + .firstLF = true, /* is this first LF calibration? */ + .enablePolicy = false, /* default value is false */ + .initialized = false, /* whether Power_init has been called */ + .constraintCounts = { 0, 0, 0, 0, 0, 0, 0 }, + .resourceCounts = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + .resourceHandlers = { + configureRFCoreClocks, + configureXOSCHF, + nopResourceHandler + }, /* special resource handler functions */ + .policyFxn = 0 /* power policyFxn */ +}; + +/* resource database */ +const PowerCC26XX_ResourceRecord resourceDB[PowerCC26X2_NUMRESOURCES] = { + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER0}, /* PERIPH_GPT0 */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER1}, /* PERIPH_GPT1 */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER2}, /* PERIPH_GPT2 */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER3}, /* PERIPH_GPT3 */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_SERIAL, PRCM_PERIPH_SSI0}, /* PERIPH_SSI0 */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_SSI1}, /* PERIPH_SSI1 */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_SERIAL, PRCM_PERIPH_UART0}, /* PERIPH_UART0 */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_SERIAL, PRCM_PERIPH_I2C0}, /* PERIPH_I2C0 */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TRNG}, /* PERIPH_TRNG */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_GPIO}, /* PERIPH_GPIO */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_UDMA}, /* PERIPH_UDMA */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_CRYPTO}, /* PERIPH_CRYPTO */ + {PowerCC26XX_PERIPH | PowerCC26XX_PERIPH_UDMA, PRCM_PERIPH_I2S}, /* PERIPH_I2S */ + {PowerCC26XX_SPECIAL | PowerCC26XX_DOMAIN_RFCORE, 0}, /* PERIPH_RFCORE */ + {PowerCC26XX_SPECIAL | PowerCC26XX_NOPARENT, 1}, /* XOSC_HF */ + {PowerCC26XX_DOMAIN | PowerCC26XX_NOPARENT, PRCM_DOMAIN_PERIPH}, /* DOMAIN_PERIPH */ + {PowerCC26XX_DOMAIN | PowerCC26XX_NOPARENT, PRCM_DOMAIN_SERIAL}, /* DOMAIN_SERIAL */ + {PowerCC26XX_DOMAIN | PowerCC26XX_NOPARENT, PRCM_DOMAIN_RFCORE}, /* DOMAIN_RFCORE */ + {PowerCC26XX_SPECIAL | PowerCC26XX_NOPARENT, 2}, /* DOMAIN_SYSBUS */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_PKA}, /* PERIPH_PKA */ + {PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_UART1}, /* PERIPH_UART1 */ +}; + + +/* ****************** Power APIs ******************** */ + +/* + * ======== Power_disablePolicy ======== + * Do not run the configured policy + */ +bool Power_disablePolicy(void) +{ + bool enablePolicy = PowerCC26X2_module.enablePolicy; + PowerCC26X2_module.enablePolicy = false; + + return (enablePolicy); +} + +/* + * ======== Power_enablePolicy ======== + * Run the configured policy + */ +void Power_enablePolicy(void) +{ + PowerCC26X2_module.enablePolicy = true; +} + +/* + * ======== Power_getConstraintMask ======== + * Get a bitmask indicating the constraints that have been registered with + * Power. + */ +uint_fast32_t Power_getConstraintMask(void) +{ + return (PowerCC26X2_module.constraintMask); +} + +/* + * ======== Power_getDependencyCount ======== + * Get the count of dependencies that are currently declared upon a resource. + */ +int_fast16_t Power_getDependencyCount(uint_fast16_t resourceId) +{ + DebugP_assert(resourceId < PowerCC26X2_NUMRESOURCES); + + return ((int_fast16_t)PowerCC26X2_module.resourceCounts[resourceId]); +} + +/* + * ======== Power_getTransitionLatency ======== + * Get the transition latency for a sleep state. The latency is reported + * in units of microseconds. + */ +uint_fast32_t Power_getTransitionLatency(uint_fast16_t sleepState, + uint_fast16_t type) +{ + uint32_t latency = 0; + + if (type == Power_RESUME) { + if (sleepState == PowerCC26XX_STANDBY) { + latency = PowerCC26X2_RESUMETIMESTANDBY; + } + } + else { + if (sleepState == PowerCC26XX_STANDBY) { + latency = PowerCC26X2_TOTALTIMESTANDBY; + } + } + + return (latency); +} + +/* + * ======== Power_getTransitionState ======== + * Get the current sleep transition state. + */ +uint_fast16_t Power_getTransitionState(void) +{ + return (PowerCC26X2_module.state); +} + +/* + * ======== Power_idleFunc ======== + * Function needs to be plugged into the idle loop. + * It calls the configured policy function if the + * 'enablePolicy' flag is set. + */ +void Power_idleFunc() +{ + if (PowerCC26X2_module.enablePolicy) { + if (PowerCC26X2_module.policyFxn != NULL) { + (*(PowerCC26X2_module.policyFxn))(); + } + } +} + +/* + * ======== Power_init ======== + */ +int_fast16_t Power_init() +{ + ClockP_Params clockParams; + uint32_t ccfgLfClkSrc; + + /* CC26X2 PG1.0 trap. If we are running on PG1.0, spin forever. + * This hardware revision is no longer supported. This trap is + * provided to aid in automatically identifying PG1.0 devices + * in circulation and will be removed later in the year. + */ + if (!((HWREG(FCFG1_BASE + FCFG1_O_TFW_FT) % 10000) >= 683)) { + while (1); + } + + /* if this function has already been called, just return */ + if (PowerCC26X2_module.initialized) { + return (Power_SOK); + } + + /* set module state field 'initialized' to true */ + PowerCC26X2_module.initialized = true; + + /* set the module state enablePolicy field */ + PowerCC26X2_module.enablePolicy = PowerCC26X2_config.enablePolicy; + + /* copy the Power policy function to module state */ + PowerCC26X2_module.policyFxn = PowerCC26X2_config.policyFxn; + + /* construct the Clock object for scheduling of wakeups */ + /* initiated and started by the power policy */ + ClockP_Params_init(&clockParams); + clockParams.period = 0; + clockParams.startFlag = false; + clockParams.arg = 0; + ClockP_construct(&PowerCC26X2_module.clockObj, + &emptyClockFunc, + 0, + &clockParams); + + /* + * If RCOSC calibration is enabled, construct a Clock object for + * delays. Set timeout to 8 Clock tick periods to get + * ceil(8x10us/30.5us/SCLK_LF_period)*30.5us/SCLK_LF_period = ~90us. + * The total time we need to wait for AUX_SYSIF_TDCREFCLKCTL_ACK + * is about 105us and the ClockP_start() call needs about 21us. + * All together, that makes ~111us. A decent approximation of the + * ideal wait duration. + * In practice, the COMPARE_MARGIN that is currently still in + * the kernel Timer.c implementation may make it take longer + * than 90us to time out. + */ + ClockP_Params_init(&clockParams); + clockParams.period = 0; + clockParams.startFlag = false; + clockParams.arg = 0; + ClockP_construct(&PowerCC26X2_module.calibrationClock, + &PowerCC26X2_RCOSC_clockFunc, + 8, + &clockParams); + + HwiP_construct(&PowerCC26X2_module.oscHwi, + INT_OSC_COMB, + oscillatorISR, NULL); + HWREG(PRCM_BASE + PRCM_O_OSCIMSC) = 0; + + /* construct the TDC hwi */ + HwiP_construct(&PowerCC26X2_module.tdcHwi, + INT_AUX_COMB, + PowerCC26X2_auxISR, NULL); + + DRIVERLIB_ASSERT_CURR_RELEASE(); + + /* read the LF clock source from CCFG */ + ccfgLfClkSrc = CCFGRead_SCLK_LF_OPTION(); + + /* check if should calibrate RCOSC_LF */ + if (PowerCC26X2_config.calibrateRCOSC_LF) { + /* verify RCOSC_LF is the LF clock source */ + if (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_RCOSC_LF) { + PowerCC26X2_module.calLF = true; + } + } + + /* + * if LF source is RCOSC_LF or XOSC_LF: assert DISALLOW_STANDBY constraint + * and start a timeout to check for activation + */ + if ((ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_RCOSC_LF) || + (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_XOSC_LF)) { + + /* Turn on oscillator interrupt for SCLK_LF switching */ + HWREG(PRCM_BASE + PRCM_O_OSCIMSC) |= PRCM_OSCIMSC_LFSRCDONEIM_M; + + /* disallow STANDBY pending LF clock quailifier disabling */ + Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY); + } + else if (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_EXTERNAL_LF) { + /* + * else, if the LF clock source is external, can disable clock qualifiers + * now; no need to assert DISALLOW_STANDBY or start the Clock object + */ + + /* yes, disable the LF clock qualifiers */ + DDI16BitfieldWrite( + AUX_DDI0_OSC_BASE, + DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_BYPASS_XOSC_LF_CLK_QUAL_M| + DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_M, + DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_S, + 0x3); + + /* enable clock loss detection */ + OSCClockLossEventEnable(); + } + else if(ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_XOSC_HF_DLF) { + /* else, user has requested LF to be derived from XOSC_HF */ + + /* Turn on oscillator interrupt for SCLK_LF switching. + * When using HPOSC, the LF clock will already have switched + * and the interrupt will fire once interrupts are enabled + * again when the OS starts. + * When using a regular HF crystal, it may take a little + * time for the crystal to start up + */ + HWREG(PRCM_BASE + PRCM_O_OSCIMSC) |= PRCM_OSCIMSC_LFSRCDONEIM_M; + + /* disallow standby since we cannot go into standby with + * an HF derived LF clock + */ + Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY); + } + + /* if VIMS RAM is configured as GPRAM: set retention constraint */ + if (!CCFGRead_DIS_GPRAM()) { + Power_setConstraint(PowerCC26XX_RETAIN_VIMS_CACHE_IN_STANDBY); + } + + return (Power_SOK); +} + +/* + * ======== Power_registerNotify ======== + * Register a function to be called on a specific power event. + * + */ +int_fast16_t Power_registerNotify(Power_NotifyObj * pNotifyObj, + uint_fast16_t eventTypes, Power_NotifyFxn notifyFxn, uintptr_t clientArg) +{ + int_fast16_t status = Power_SOK; + + /* check for NULL pointers */ + if ((pNotifyObj == NULL) || (notifyFxn == NULL)) { + status = Power_EINVALIDPOINTER; + } + + else { + /* fill in notify object elements */ + pNotifyObj->eventTypes = eventTypes; + pNotifyObj->notifyFxn = notifyFxn; + pNotifyObj->clientArg = clientArg; + + /* place notify object on event notification queue */ + List_put(&PowerCC26X2_module.notifyList, (List_Elem*)pNotifyObj); + } + + return (status); +} + +/* + * ======== Power_releaseConstraint ======== + * Release a previously declared constraint. + */ +int_fast16_t Power_releaseConstraint(uint_fast16_t constraintId) +{ + unsigned int key; + uint8_t count; + + /* assert constraintId is valid */ + DebugP_assert(constraintId < PowerCC26X2_NUMCONSTRAINTS); + + key = HwiP_disable(); + + /* get the count of the constraint */ + count = PowerCC26X2_module.constraintCounts[constraintId]; + + DebugP_assert(count != 0); + + count--; + + /* save the updated count */ + PowerCC26X2_module.constraintCounts[constraintId] = count; + + if (count == 0) { + PowerCC26X2_module.constraintMask &= ~(1 << constraintId); + } + + HwiP_restore(key); + + return (Power_SOK); +} + +/* + * ======== Power_releaseDependency ======== + * Release a previously declared dependency. + */ +int_fast16_t Power_releaseDependency(uint_fast16_t resourceId) +{ + uint8_t parent; + uint8_t count; + uint32_t id; + unsigned int key; + + /* assert resourceId is valid */ + DebugP_assert(resourceId < PowerCC26X2_NUMRESOURCES); + + /* disable interrupts */ + key = HwiP_disable(); + + /* read and decrement the reference count */ + count = PowerCC26X2_module.resourceCounts[resourceId]; + + DebugP_assert(count != 0); + + count--; + + /* save the reference count */ + PowerCC26X2_module.resourceCounts[resourceId] = count; + + /* if this was the last dependency being released.., */ + if (count == 0) { + /* deactivate this resource ... */ + id = resourceDB[resourceId].driverlibID; + + /* is resource a peripheral?... */ + if (resourceDB[resourceId].flags & PowerCC26XX_PERIPH) { + PRCMPeripheralRunDisable(id); + PRCMPeripheralSleepDisable(id); + PRCMPeripheralDeepSleepDisable(id); + PRCMLoadSet(); + while (!PRCMLoadGet()) { + ; + } + } + /* else, does resource require a special handler?... */ + else if (resourceDB[resourceId].flags & PowerCC26XX_SPECIAL) { + /* call the special handler */ + PowerCC26X2_module.resourceHandlers[id](PowerCC26XX_DISABLE); + } + + /* else resource is a power domain */ + else { + PRCMPowerDomainOff(id); + while (PRCMPowerDomainStatus(id) != PRCM_DOMAIN_POWER_OFF) { + ; + } + } + + /* propagate release up the dependency tree ... */ + + /* check for a first parent */ + parent = resourceDB[resourceId].flags & PowerCC26XX_PARENTMASK; + + /* if 1st parent, make recursive call to release that dependency */ + if (parent < PowerCC26X2_NUMRESOURCES) { + Power_releaseDependency(parent); + } + } + + /* re-enable interrupts */ + HwiP_restore(key); + + return (Power_SOK); +} + +/* + * ======== Power_setConstraint ======== + * Declare an operational constraint. + */ +int_fast16_t Power_setConstraint(uint_fast16_t constraintId) +{ + unsigned int key; + + /* assert constraint id is valid */ + DebugP_assert(constraintId < PowerCC26X2_NUMCONSTRAINTS); + + /* disable interrupts */ + key = HwiP_disable(); + + /* set the specified constraint in the constraintMask */ + PowerCC26X2_module.constraintMask |= 1 << constraintId; + + /* increment the specified constraint count */ + PowerCC26X2_module.constraintCounts[constraintId]++; + + /* re-enable interrupts */ + HwiP_restore(key); + + return (Power_SOK); +} + +/* + * ======== Power_setDependency ======== + * Declare a dependency upon a resource. + */ +int_fast16_t Power_setDependency(uint_fast16_t resourceId) +{ + uint8_t parent; + uint8_t count; + uint32_t id; + unsigned int key; + + /* assert resourceId is valid */ + DebugP_assert(resourceId < PowerCC26X2_NUMRESOURCES); + + /* disable interrupts */ + key = HwiP_disable(); + + /* read and increment reference count */ + count = PowerCC26X2_module.resourceCounts[resourceId]++; + + /* if resource was NOT activated previously ... */ + if (count == 0) { + /* propagate set up the dependency tree ... */ + + /* check for a first parent */ + parent = resourceDB[resourceId].flags & PowerCC26XX_PARENTMASK; + + /* if first parent, make recursive call to set that dependency */ + if (parent < PowerCC26X2_NUMRESOURCES) { + Power_setDependency(parent); + } + + /* now activate this resource ... */ + id = resourceDB[resourceId].driverlibID; + + /* is resource a peripheral?... */ + if (resourceDB[resourceId].flags & PowerCC26XX_PERIPH) { + PRCMPeripheralRunEnable(id); + PRCMPeripheralSleepEnable(id); + PRCMPeripheralDeepSleepEnable(id); + PRCMLoadSet(); + while (!PRCMLoadGet()) { + ; + } + } + /* else, does resource require a special handler?... */ + else if (resourceDB[resourceId].flags & PowerCC26XX_SPECIAL) { + /* call the special handler */ + PowerCC26X2_module.resourceHandlers[id](PowerCC26XX_ENABLE); + } + /* else resource is a power domain */ + else { + PRCMPowerDomainOn(id); + while (PRCMPowerDomainStatus(id) != PRCM_DOMAIN_POWER_ON) { + ; + } + } + } + + /* re-enable interrupts */ + HwiP_restore(key); + + return (Power_SOK); +} + +/* + * ======== Power_setPolicy ======== + * Set the Power policy function + */ +void Power_setPolicy(Power_PolicyFxn policy) +{ + PowerCC26X2_module.policyFxn = policy; +} + +/* + * ======== Power_shutdown ======== + */ +int_fast16_t Power_shutdown(uint_fast16_t shutdownState, + uint_fast32_t shutdownTime) +{ + int_fast16_t status = Power_EFAIL; + unsigned int constraints; + unsigned int hwiKey; + + /* disable interrupts */ + hwiKey = HwiP_disable(); + + /* check if there is a constraint to prohibit shutdown */ + constraints = Power_getConstraintMask(); + if (constraints & (1 << PowerCC26XX_DISALLOW_SHUTDOWN)) { + status = Power_ECHANGE_NOT_ALLOWED; + } + + /* OK to shutdown ... */ + else if (PowerCC26X2_module.state == Power_ACTIVE) { + /* set new transition state to entering shutdown */ + PowerCC26X2_module.state = Power_ENTERING_SHUTDOWN; + + /* signal all clients registered for pre-shutdown notification */ + status = notify(PowerCC26XX_ENTERING_SHUTDOWN); + + /* check for any error */ + if (status != Power_SOK) { + PowerCC26X2_module.state = Power_ACTIVE; + HwiP_restore(hwiKey); + return (status); + } + + /* Ensure the JTAG domain is turned off + * otherwise MCU domain can't be turned off. + */ + HWREG(AON_PMCTL_BASE + AON_PMCTL_O_JTAGCFG) = 0; + + SysCtrlAonSync(); + + /* now proceed with shutdown sequence ... */ + SysCtrlShutdownWithAbort(); + } + else { + status = Power_EBUSY; + } + + /* NOTE: if shutdown succeeded, should never get here */ + + /* return failure status */ + PowerCC26X2_module.state = Power_ACTIVE; + + /* re-enable interrupts */ + HwiP_restore(hwiKey); + + /* if get here, failed to shutdown, return error code */ + return (status); +} + +/* + * ======== Power_sleep ======== + */ +int_fast16_t Power_sleep(uint_fast16_t sleepState) +{ + int_fast16_t status = Power_SOK; + int_fast16_t notifyStatus = Power_SOK; + int_fast16_t lateNotifyStatus = Power_SOK; + unsigned int xosc_hf_active = false; + uint_fast16_t postEventLate; + uint32_t poweredDomains = 0; + uint_fast16_t preEvent; + uint_fast16_t postEvent; + unsigned int constraints; + bool retainCache = false; + uint32_t modeVIMS; + unsigned int swiKey; + + /* first validate the sleep code */ + if (sleepState != PowerCC26XX_STANDBY) { + status = Power_EINVALIDINPUT; + } + + else { + + /* check to make sure Power is not busy with another transition */ + if (PowerCC26X2_module.state == Power_ACTIVE) { + /* set transition state to entering sleep */ + PowerCC26X2_module.state = Power_ENTERING_SLEEP; + } + else { + status = Power_EBUSY; + } + + if (status == Power_SOK) { + + /* setup sleep vars */ + preEvent = PowerCC26XX_ENTERING_STANDBY; + postEvent = PowerCC26XX_AWAKE_STANDBY; + postEventLate = PowerCC26XX_AWAKE_STANDBY_LATE; + + /* disable Task scheduling */ + PowerCC26XX_schedulerDisable(); + + /* signal all clients registered for pre-sleep notification */ + status = notify(preEvent); + + /* check for any error */ + if (status != Power_SOK) { + PowerCC26X2_module.state = Power_ACTIVE; + PowerCC26XX_schedulerRestore(); + return (status); + } + + /* now disable Swi scheduling */ + swiKey = SwiP_disable(); + + /* 1. Query and save domain states before powering them off */ + if (Power_getDependencyCount(PowerCC26XX_DOMAIN_RFCORE)) { + poweredDomains |= PRCM_DOMAIN_RFCORE; + } + if (Power_getDependencyCount(PowerCC26XX_DOMAIN_SERIAL)){ + poweredDomains |= PRCM_DOMAIN_SERIAL; + } + if (Power_getDependencyCount(PowerCC26XX_DOMAIN_PERIPH)) { + poweredDomains |= PRCM_DOMAIN_PERIPH; + } + + /* 2. If XOSC_HF is active or we are waiting to switch + * to it, force it off. Otherwise, the XOSC_HF may be + * automatically turned on by the hardware without + * a call to configureXOSCHF(PowerCC26XX_ENABLE) + * This is not necessarily a problem. However exactly + * what the cutoff point is where the hardware considers + * the XOSC_HF "on" without having switched to is not + * considered by this driver. + */ + if (OSCClockSourceGet(OSC_SRC_CLK_HF) == OSC_XOSC_HF || + PowerCC26X2_module.xoscPending == true) { + xosc_hf_active = true; + configureXOSCHF(PowerCC26XX_DISABLE); + } + + /* query constraints to determine if cache should be retained */ + constraints = Power_getConstraintMask(); + if (constraints & (1 << PowerCC26XX_RETAIN_VIMS_CACHE_IN_STANDBY)) { + retainCache = true; + } + else { + retainCache = false; + + // Get the current VIMS mode + do { + modeVIMS = VIMSModeGet(VIMS_BASE); + } while (modeVIMS == VIMS_MODE_CHANGING); + } + + /* 3. + * - Freeze the IOs on the boundary between MCU and AON + * - Make sure AON writes take effect + * - Request power off of every PD in the MCU voltage domain + * - Ensure that no clocks are forced on in Crypto, DMA and I2S + * - Gate running deep sleep clocks for Crypto, DMA and I2S + * - Load the new clock settings + * - Configure the VIMS power domain mode to power up flash + * again after coming out of standby. + * - Request uLDO during standby + * - Use recharge comparator + * - Ensure all writes have taken effect + * - Ensure UDMA, Crypto and I2C clocks are turned off + * - Ensure all non-CPU power domains are turned off + * - Turn off cache retention if requested + * - Invoke deep sleep to go to standby + */ + SysCtrlStandby(retainCache, + VIMS_ON_CPU_ON_MODE, + SYSCTRL_PREFERRED_RECHARGE_MODE); + + /* 4. If didn't retain VIMS in standby, re-enable retention now */ + if (retainCache == false) { + + /* 5.1 If previously in a cache mode, restore the mode now */ + if (modeVIMS == VIMS_MODE_ENABLED) { + VIMSModeSet(VIMS_BASE, modeVIMS); + } + + /* 5.2 Re-enable retention */ + PRCMCacheRetentionEnable(); + } + + /* 6. Start re-powering power domains */ + PRCMPowerDomainOn(poweredDomains); + + /* 7. Restore deep sleep clocks of Crypto and DMA */ + if (Power_getDependencyCount(PowerCC26XX_PERIPH_CRYPTO)) { + PRCMPeripheralDeepSleepEnable( + resourceDB[PowerCC26XX_PERIPH_CRYPTO].driverlibID); + } + if (Power_getDependencyCount(PowerCC26XX_PERIPH_UDMA)) { + PRCMPeripheralDeepSleepEnable( + resourceDB[PowerCC26XX_PERIPH_UDMA].driverlibID); + } + + /* 8. Make sure clock settings take effect */ + PRCMLoadSet(); + + /* 9. Release request for uLDO */ + PRCMMcuUldoConfigure(false); + + /* 10. Set transition state to EXITING_SLEEP */ + PowerCC26X2_module.state = Power_EXITING_SLEEP; + + /* 11. Wait until all power domains are back on */ + while (PRCMPowerDomainStatus(poweredDomains) != PRCM_DOMAIN_POWER_ON); + + /* 12. Wait for the RTC shadow values to be updated so that + * the early notification callbacks can read out valid RTC values. + * This can likely be removed as the 2MHz MF clock will have ticked by now. + */ + SysCtrlAonSync(); + + /* + * 13. Signal clients registered for early post-sleep notification; + * this should be used to initialize any timing critical or IO + * dependent hardware + */ + notifyStatus = notify(postEvent); + + /* 14. Disable IO freeze and ensure RTC shadow value is updated */ + AONIOCFreezeDisable(); + SysCtrlAonSync(); + + /* 15. If XOSC_HF was forced off above, initiate switch back */ + if (xosc_hf_active == true) { + configureXOSCHF(PowerCC26XX_ENABLE); + } + + /* 16. Re-enable interrupts */ + CPUcpsie(); + + /* + * 17. Signal all clients registered for late post-sleep + * notification + */ + lateNotifyStatus = notify(postEventLate); + + /* + * 18. Now clear the transition state before re-enabling + * scheduler + */ + PowerCC26X2_module.state = Power_ACTIVE; + + /* Re-enable Swi scheduling */ + SwiP_restore(swiKey); + + /* re-enable Task scheduling */ + PowerCC26XX_schedulerRestore(); + + /* if there was a notification error, set return status */ + if ((notifyStatus != Power_SOK) || + (lateNotifyStatus != Power_SOK)) { + status = Power_EFAIL; + } + } + } + + return (status); +} + +/* + * ======== Power_unregisterNotify ======== + * Unregister for a power notification. + * + */ +void Power_unregisterNotify(Power_NotifyObj * pNotifyObj) +{ + unsigned int key; + + /* remove notify object from its event queue */ + key = HwiP_disable(); + + /* remove notify object from its event queue */ + List_remove(&PowerCC26X2_module.notifyList, (List_Elem *)pNotifyObj); + + HwiP_restore(key); +} + +/* ****************** CC26XX specific APIs ******************** */ + +/* + * ======== PowerCC26XX_calibrate ======== + * Plug this function into the PowerCC26X2_Config structure + * if calibration is needed. + */ +bool PowerCC26XX_calibrate(unsigned int arg) +{ + bool retVal = false; + + switch (arg) { + case PowerCC26X2_INITIATE_CALIBRATE: + retVal = PowerCC26X2_initiateCalibration(); + break; + + case PowerCC26X2_DO_CALIBRATE: + PowerCC26X2_calibrate(); + break; + default: + while (1); + } + + return (retVal); +} + +/* + * ======== PowerCC26XX_doWFI ======== + */ +void PowerCC26XX_doWFI(void) +{ + __asm(" wfi"); +} + +/* + * ======== PowerCC26X2_getClockHandle ======== + */ +ClockP_Handle PowerCC26XX_getClockHandle() +{ + return ((ClockP_Handle)&PowerCC26X2_module.clockObj); +} + +/* + * ======== PowerCC26XX_noCalibrate ======== + * Plug this function into the PowerCC26X2 config structure if calibration + * is not needed. + */ +bool PowerCC26XX_noCalibrate(unsigned int arg) +{ + return (0); +} + +/* + * ======== PowerCC26XX_getXoscStartupTime ======== + * Get the estimated crystal oscillator startup time + */ +uint32_t PowerCC26XX_getXoscStartupTime(uint32_t timeUntilWakeupInMs) +{ + return (OSCHF_GetStartupTime(timeUntilWakeupInMs)); +} + +/* + * ======== PowerCC26X2_injectCalibration ======== + * Explicitly trigger RCOSC calibration + */ +bool PowerCC26XX_injectCalibration(void) +{ + if ((*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_INITIATE_CALIBRATE)) { + /* here if AUX SMPH was available, start calibration now ... */ + (*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_DO_CALIBRATE); + return (true); + } + + return (false); +} + +/* + * ======== PowerCC26XX_isStableXOSC_HF ======== + * Check if XOSC_HF has stabilized. + */ +bool PowerCC26XX_isStableXOSC_HF(void) +{ + bool ready = true; + unsigned int key; + + key = HwiP_disable(); + + /* only query if HF source is ready if there is a pending change */ + if (PowerCC26X2_module.xoscPending) { + ready = OSCHfSourceReady(); + } + + HwiP_restore(key); + + return (ready); +} + +/* + * ======== PowerCC26XX_switchXOSC_HF ======== + * Switch to enable XOSC_HF. + * May only be called when using the PowerCC26XX_SWITCH_XOSC_HF_MANUALLY + * constraint. + * May only be called after ensuring the XOSC_HF is stable by calling + * PowerCC26XX_isStableXOSC_HF(). + */ +void PowerCC26XX_switchXOSC_HF(void) +{ + bool readyToCal; + unsigned int key; + + key = HwiP_disable(); + + /* Since PowerCC26X2_isStableXOSC_HF() should have been called before this + * function, we can just switch without handling the case when the XOSC_HF + * is not ready or PowerCC26X2_module.xoscPending is not true. + */ + OSCHF_AttemptToSwitchToXosc(); + + /* Since configureXOSCHF() was called prior to this function to turn + * on the XOSC_HF, PowerCC26X2_module.xoscPending will be true and + * we can safely set it to false. + */ + PowerCC26X2_module.xoscPending = false; + + /* Allow going into IDLE again since we sucessfully switched + * to XOSC_HF + */ + Power_releaseConstraint(PowerCC26XX_DISALLOW_IDLE); + + HwiP_restore(key); + + /* initiate RCOSC calibration */ + readyToCal = (*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_INITIATE_CALIBRATE); + + /* now notify clients that were waiting for a switch notification */ + notify(PowerCC26XX_XOSC_HF_SWITCHED); + + /* if ready to start first cal measurment, do it now */ + if (readyToCal == true) { + (*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_DO_CALIBRATE); + } +} + +/* * * * * * * * * * * internal and support functions * * * * * * * * * * */ + +/* + * ======== oscillatorISR ======== + */ +static void oscillatorISR(uintptr_t arg) +{ + uint32_t rawStatus = HWREG(PRCM_BASE + PRCM_O_OSCRIS); + uint32_t intStatusMask = HWREG(PRCM_BASE + PRCM_O_OSCIMSC); + + /* Turn off mask for all flags we will handle */ + HWREG(PRCM_BASE + PRCM_O_OSCIMSC) = intStatusMask & ~rawStatus; + + /* XOSC_LF or RCOSC_LF qualified */ + if (rawStatus & PRCM_OSCRIS_LFSRCDONERIS_M & intStatusMask) { + disableLFClockQualifiers(); + } + + /* XOSC_HF ready to switch to */ + if (rawStatus & PRCM_OSCIMSC_HFSRCPENDIM_M & intStatusMask) { + switchXOSCHF(); + } + + /* Clear flags we will handle. Does not really work as expected as + * the flags seem to level-detect and not edge-detect. Until the + * underlying trigger is taken care of, the flag will not deassert + * even when cleared. + * We're clearing at the end in order to prevent the flag from + * immediately asserting again if the underlying trigger was + * not handled yet. + * SCLK_LF switched can never be cleared after triggering + * only masked out. XOSC_HF ready to switch can be cleared + * after switching to XOSC_HF. + */ + HWREG(PRCM_BASE + PRCM_O_OSCICR) = intStatusMask & rawStatus; +} + +/* + * ======== emptyClockFunc ======== + * Clock function used by power policy to schedule early wakeups. + */ +static void emptyClockFunc(uintptr_t arg) +{ +} + +/* + * ======== disableLFClockQualifiers ======== + * Clock function used for delayed disable of LF clock qualifiers. + */ +static void disableLFClockQualifiers(void) +{ + uint32_t sourceLF; + + /* query LF clock source */ + sourceLF = OSCClockSourceGet(OSC_SRC_CLK_LF); + + /* is LF source either RCOSC_LF or XOSC_LF yet? */ + if ((sourceLF == OSC_RCOSC_LF) || (sourceLF == OSC_XOSC_LF)) { + + /* yes, disable the LF clock qualifiers */ + DDI16BitfieldWrite( + AUX_DDI0_OSC_BASE, + DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_BYPASS_XOSC_LF_CLK_QUAL_M| + DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_M, + DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_S, + 0x3 + ); + + /* enable clock loss detection */ + OSCClockLossEventEnable(); + + /* now finish by releasing the standby disallow constraint */ + Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY); + } + else if(sourceLF == OSC_XOSC_HF) { + /* yes, disable the LF clock qualifiers */ + DDI16BitfieldWrite( + AUX_DDI0_OSC_BASE, + DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_BYPASS_XOSC_LF_CLK_QUAL_M| + DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_M, + DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_S, + 0x3 + ); + + /* enable clock loss detection */ + OSCClockLossEventEnable(); + + /* do not allow standby since the LF clock is HF derived */ + } + +} + +/* + * ======== nopResourceFunc ======== + * special resource handler + */ +static unsigned int nopResourceHandler(unsigned int action) +{ + return (0); +} + +/* + * ======== notify ======== + * Send notifications to registered clients. + * Note: Task scheduling is disabled when this function is called. + */ +static int_fast16_t notify(uint_fast16_t eventType) +{ + int_fast16_t notifyStatus; + Power_NotifyFxn notifyFxn; + uintptr_t clientArg; + List_Elem *elem; + + /* if queue is empty, return immediately */ + if (!List_empty(&PowerCC26X2_module.notifyList)) { + /* point to first client notify object */ + elem = List_head(&PowerCC26X2_module.notifyList); + + /* walk the queue and notify each registered client of the event */ + do { + if (((Power_NotifyObj *)elem)->eventTypes & eventType) { + /* pull params from notify object */ + notifyFxn = ((Power_NotifyObj *)elem)->notifyFxn; + clientArg = ((Power_NotifyObj *)elem)->clientArg; + + /* call the client's notification function */ + notifyStatus = (int_fast16_t)(*(Power_NotifyFxn)notifyFxn)( + eventType, 0, clientArg); + + /* if client declared error stop all further notifications */ + if (notifyStatus != Power_NOTIFYDONE) { + return (Power_EFAIL); + } + } + + /* get next element in the notification queue */ + elem = List_next(elem); + + } while (elem != NULL); + } + + return (Power_SOK); +} + +/* + * ======== configureRFCoreClocks ======== + * Special dependency function for controlling RF core clocks. + */ +static unsigned int configureRFCoreClocks(unsigned int action) +{ + if (action == PowerCC26XX_ENABLE) { + RFCClockEnable(); + } + else { + RFCClockDisable(); + } + + return (0); +} + +/* + * ======== switchXOSCHF ======== + * Switching to XOSC_HF when it has stabilized. + */ +static void switchXOSCHF(void) +{ + bool readyToCal; + unsigned int key; + + key = HwiP_disable(); + + /* Switch to the XOSC_HF. Since this function is only called + * after we get an interrupt signifying it is ready to switch, + * it should always succeed. + * If it does not succeed, try again. It is fine if we spin, + * there is no sensible recovery mechanism from such an error. + */ + while (!OSCHF_AttemptToSwitchToXosc()); + + /* The only time we should get here is when PowerCC26X2_module.xoscPending == true + * holds. + * Allow going into IDLE again since we sucessfully switched + * to XOSC_HF + */ + Power_releaseConstraint(PowerCC26XX_DISALLOW_IDLE); + + PowerCC26X2_module.xoscPending = false; + + + HwiP_restore(key); + + /* initiate RCOSC calibration */ + readyToCal = (*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_INITIATE_CALIBRATE); + + /* now notify clients that were waiting for a switch notification */ + notify(PowerCC26XX_XOSC_HF_SWITCHED); + + /* if ready to start first cal measurment, do it now */ + if (readyToCal == true) { + (*(PowerCC26X2_config.calibrateFxn))(PowerCC26X2_DO_CALIBRATE); + } +} + +/* + * ======== configureXOSCHF ======== + */ +static unsigned int configureXOSCHF(unsigned int action) +{ + /* By checking action == PowerCC26XX_ENABLE and PowerCC26X2_module.xoscPending + * carefully, the function should be idempotent. Calling it with the same + * action more than once will not have any effect until the hardware triggers + * a software state change. + */ + if (action == PowerCC26XX_ENABLE && + OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_XOSC_HF && + PowerCC26X2_module.xoscPending == false) { + + OSCHF_TurnOnXosc(); + + PowerCC26X2_module.xoscPending = true; + + /* Unless it is disallowed, unmask the XOSC_HF ready to switch flag */ + if (!(Power_getConstraintMask() & (1 << PowerCC26XX_SWITCH_XOSC_HF_MANUALLY))) { + + /* Clearing the flag in the ISR does not always work. Clear it again just in case */ + HWREG(PRCM_BASE + PRCM_O_OSCICR) = PRCM_OSCICR_HFSRCPENDC_M; + + /* Turn on oscillator interrupt for SCLK_HF switching */ + HWREG(PRCM_BASE + PRCM_O_OSCIMSC) |= PRCM_OSCIMSC_HFSRCPENDIM_M; + } + + /* If the device goes into IDLE in between turning on XOSC_HF and + * and switching SCLK_HF to XOSC_HF, the INT_OSC_COMB HFSRCPEND + * trigger will be suppressed. + * The DISALLOW_IDLE constraint should only ever be set whenever + * we transition from xoscPending == false to true. + */ + Power_setConstraint(PowerCC26XX_DISALLOW_IDLE); + } + + /* when release XOSC_HF, auto switch to RCOSC_HF */ + else if (action == PowerCC26XX_DISABLE) { + OSCHF_SwitchToRcOscTurnOffXosc(); + + /* If we have not actually switched to XOSC_HF yet, we need to + * undo what we did above when turning on XOSC_HF. Otherwise, + * we may not balance the constraints correctly or get + * unexpected interrupts. + */ + if (PowerCC26X2_module.xoscPending) { + /* Remove HFSRCPEND from the OSC_COMB interrupt mask */ + uint32_t oscMask = HWREG(PRCM_BASE + PRCM_O_OSCIMSC); + HWREG(PRCM_BASE + PRCM_O_OSCIMSC) = oscMask & ~ PRCM_OSCIMSC_HFSRCPENDIM_M; + + /* Clear any residual trigger for HFSRCPEND */ + HWREG(PRCM_BASE + PRCM_O_OSCICR) = PRCM_OSCICR_HFSRCPENDC; + + Power_releaseConstraint(PowerCC26XX_DISALLOW_IDLE); + + PowerCC26X2_module.xoscPending = false; + } + } + return (0); +} diff --git a/simplelink/source/ti/drivers/power/PowerCC26X2.h b/simplelink/source/ti/drivers/power/PowerCC26X2.h new file mode 100644 index 0000000..8804cad --- /dev/null +++ b/simplelink/source/ti/drivers/power/PowerCC26X2.h @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2017-2018, Texas Instruments Incorporated + * 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 Texas Instruments Incorporated 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 THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQueueNTIAL 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. + */ +/** ============================================================================ + * @file PowerCC26X2.h + * + * @brief Power manager interface for CC26X2 + * + * The Power header file should be included in an application as follows: + * @code + * #include + * #include + * @endcode + * + * Refer to @ref Power.h for a complete description of APIs. + * + * ## Implementation # + * This header file defines the power resources, constraints, events, sleep + * states and transition latencies for CC26X2. + * + * ============================================================================ + */ + +#ifndef ti_drivers_power_PowerCC26X2_ +#define ti_drivers_power_PowerCC26X2_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +/*! The latency to reserve for resume from STANDBY (usec). */ +#define PowerCC26X2_RESUMETIMESTANDBY 750 + +/*! The total latency to reserve for entry to and exit from STANDBY (usec). */ +#define PowerCC26X2_TOTALTIMESTANDBY 1000 + +/*! The initial delay when waking from STANDBY (usec). */ +#define PowerCC26X2_WAKEDELAYSTANDBY 240 + +/*! The initial wait time (usec) before checking if RCOSC_LF is stable. */ +#define PowerCC26X2_INITIALWAITRCOSC_LF 1000 + +/*! The retry wait time (usec) when checking to see if RCOSC_LF is stable. */ +#define PowerCC26X2_RETRYWAITRCOSC_LF 1000 + +/*! The initial wait time (usec) before checking if XOSC_HF is stable. */ +#define PowerCC26X2_INITIALWAITXOSC_HF 50 + +/*! The retry wait time (usec) when checking to see if XOSC_HF is stable. */ +#define PowerCC26X2_RETRYWAITXOSC_HF 50 + +/*! The initial wait time (usec) before checking if XOSC_LF is stable. */ +#define PowerCC26X2_INITIALWAITXOSC_LF 10000 + +/*! The retry wait time (usec) when checking to see if XOSC_LF is stable. */ +#define PowerCC26X2_RETRYWAITXOSC_LF 5000 + +#define PowerCC26X2_PERIPH_PKA PowerCC26XX_NUMRESOURCES /*!< Resource ID: PKA Module */ + +#define PowerCC26X2_PERIPH_UART1 PowerCC26XX_NUMRESOURCES + 1 /*!< Resource ID: UART1 */ + +/* \cond */ +#define PowerCC26X2_NUMRESOURCES (PowerCC26XX_NUMRESOURCES + 2) /* Number of resources in database */ +/* \endcond */ + +/* \cond */ +#define PowerCC26X2_NUMCONSTRAINTS (PowerCC26XX_NUMCONSTRAINTS + 0) /* Number of constraints supported */ +/* \endcond */ + +/* \cond */ +/* + * Calibration stages + */ +#define PowerCC26X2_SETUP_CALIBRATE 1 +#define PowerCC26X2_INITIATE_CALIBRATE 2 +#define PowerCC26X2_DO_CALIBRATE 3 +/* \endcond */ + + +/*! @brief Global configuration structure */ +typedef struct PowerCC26X2_Config { + /*! + * @brief The Power Policy's initialization function + * + * If the policy does not have an initialization function, 'NULL' + * should be specified. + */ + Power_PolicyInitFxn policyInitFxn; + /*! + * @brief The Power Policy function + * + * When enabled, this function is invoked in the idle loop, to + * opportunistically select and activate sleep states. + * + * Two reference policies are provided: + * + * PowerCC26X2_doWFI() - a simple policy that invokes CPU wait for + * interrupt (WFI) + * + * PowerCC26X2_standbyPolicy() - an agressive policy that considers + * constraints, time until next scheduled work, and sleep state + * latencies, and optionally puts the device into the STANDBY state, + * the IDLE state, or as a minimum, WFI. + * + * Custom policies can be written, and specified via this function pointer. + * + * In addition to this static selection, the Power Policy can be + * dynamically changed at runtime, via the Power_setPolicy() API. + */ + Power_PolicyFxn policyFxn; + /*! + * @brief The function to be used for activating RC Oscillator (RCOSC) + * calibration + * + * Calibration is normally enabled, via specification of the function + * PowerCC26X2_calibrate(). This enables high accuracy operation, and + * faster high frequency crystal oscillator (XOSC_HF) startups. + * + * To disable RCOSC calibration, the function PowerCC26X2_noCalibrate() + * should be specified. + */ + bool (*calibrateFxn)(unsigned int); + /*! + * @brief Boolean specifying if the Power Policy function is enabled + * + * If 'true', the policy function will be invoked once for each pass + * of the idle loop. + * + * If 'false', the policy will not be invoked. + * + * In addition to this static setting, the power policy can be dynamically + * enabled and disabled at runtime, via the Power_enablePolicy() and + * Power_disablePolicy() functions, respectively. + */ + bool enablePolicy; + /*! + * @brief Boolean specifying whether the low frequency RC oscillator + * (RCOSC_LF) should be calibrated. + * + * If RCOSC calibration is enabled (above, via specification of + * an appropriate calibrateFxn), this Boolean specifies whether + * RCOSC_LF should be calibrated. + */ + bool calibrateRCOSC_LF; + /*! + * @brief Boolean specifying whether the high frequency RC oscillator + * (RCOSC_HF) should be calibrated. + * + * If RCOSC calibration is enabled (above, via specification of + * an appropriate calibrateFxn), this Boolean specifies whether + * RCOSC_HF should be calibrated. + */ + bool calibrateRCOSC_HF; +} PowerCC26X2_Config; + +/*! + * @brief PowerCC26X2_ModuleState + * + * Power manager state structure. The application must not access any members + * of this structure! + */ +typedef struct PowerCC26X2_ModuleState { + List_List notifyList; /*!< Event notification list */ + uint32_t constraintMask; /*!< Aggregate constraints mask */ + ClockP_Struct clockObj; /*!< Clock object for scheduling wakeups */ + ClockP_Struct calibrationClock; /*!< Clock object for scheduling wakeups */ + HwiP_Struct oscHwi; /*!< Hwi object for oscillator stabilisation */ + HwiP_Struct tdcHwi; /*!< Hwi object for RCOSC calibration */ + int32_t nDeltaFreqCurr; /*!< RCOSC calibration variable */ + int32_t nCtrimCurr; /*!< RCOSC calibration variable */ + int32_t nCtrimFractCurr; /*!< RCOSC calibration variable */ + int32_t nCtrimNew; /*!< RCOSC calibration variable */ + int32_t nCtrimFractNew; /*!< RCOSC calibration variable */ + int32_t nRtrimNew; /*!< RCOSC calibration variable */ + int32_t nRtrimCurr; /*!< RCOSC calibration variable */ + int32_t nDeltaFreqNew; /*!< RCOSC calibration variable */ + bool bRefine; /*!< RCOSC calibration variable */ + uint32_t state; /*!< Current transition state */ + bool xoscPending; /*!< Is XOSC_HF activation in progress? */ + bool calLF; /*!< Calibrate RCOSC_LF? */ + uint8_t auxHwiState; /*!< The AUX ISR calibration state */ + bool busyCal; /*!< Already busy calibrating? */ + uint32_t calStep; /*!< The current calibration step */ + bool firstLF; /*!< Is this the first LF calibration? */ + bool enablePolicy; /*!< Is the Power policy enabled? */ + bool initialized; /*!< Has Power_init() been called? */ + uint8_t constraintCounts[PowerCC26X2_NUMCONSTRAINTS]; + /*!< Array to maintain constraint reference counts */ + uint8_t resourceCounts[PowerCC26X2_NUMRESOURCES]; + /*!< Array to maintain resource dependency reference counts */ + unsigned int (*resourceHandlers[3])(unsigned int); + /*!< Array of special dependency handler functions */ + Power_PolicyFxn policyFxn; /*!< The Power policy function */ +} PowerCC26X2_ModuleState; + + +#ifdef __cplusplus +} +#endif + +#endif /* POWER_CC26X2_ */ diff --git a/simplelink/source/ti/drivers/power/PowerCC26X2_calibrateRCOSC.c b/simplelink/source/ti/drivers/power/PowerCC26X2_calibrateRCOSC.c new file mode 100644 index 0000000..45f6bb3 --- /dev/null +++ b/simplelink/source/ti/drivers/power/PowerCC26X2_calibrateRCOSC.c @@ -0,0 +1,791 @@ +/* + * Copyright (c) 2017-2018, Texas Instruments Incorporated + * 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 Texas Instruments Incorporated 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 THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQueueNTIAL 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. + */ +/* + * ======== PowerCC26X2_calibrateRCOSC.c ======== + */ + +#include + +#include + +#include +#include + +#include +#include DeviceFamily_constructPath(inc/hw_aux_evctl.h) +#include DeviceFamily_constructPath(inc/hw_aux_smph.h) +#include DeviceFamily_constructPath(inc/hw_aux_sysif.h) +#include DeviceFamily_constructPath(inc/hw_aux_tdc.h) +#include DeviceFamily_constructPath(inc/hw_ddi_0_osc.h) +#include DeviceFamily_constructPath(inc/hw_ddi.h) +#include DeviceFamily_constructPath(driverlib/aon_batmon.h) +#include DeviceFamily_constructPath(driverlib/ddi.h) +#include DeviceFamily_constructPath(driverlib/ioc.h) +#include DeviceFamily_constructPath(driverlib/osc.h) +#include DeviceFamily_constructPath(driverlib/sys_ctrl.h) + +#define AUX_TDC_SEMAPHORE_NUMBER 1 /* semaphore 1 protects TDC */ +#define NUM_RCOSC_LF_PERIODS_TO_MEASURE 32 /* x RCOSC_LF periods vs XOSC_HF */ +#define NUM_RCOSC_HF_PERIODS_TO_MEASURE 1 /* x RCOSC_HF periods vs XOSC_HF */ +#define ACLK_REF_SRC_RCOSC_HF 0 /* Use RCOSC_HF for ACLK REF */ +#define ACLK_REF_SRC_RCOSC_LF 2 /* Use RCOSC_LF for ACLK REF */ +#define SCLK_LF_OPTION_RCOSC_LF 3 /* defined in cc26_ccfg.xls */ +#define RCOSC_HF_LOW_THRESHOLD_TDC_VALUE 1535 /* If TDC value is within threshold range, no need for another TDC measurement */ +#define RCOSC_HF_PERFECT_TDC_VALUE 1536 /* RCOSC_HF runs at perfect 48 MHz when ending up with this TDC value */ +#define RCOSC_HF_HIGH_THRESHOLD_TDC_VALUE 1537 /* If TDC value is within threshold range, no need for another TDC measurement */ + +#define DDI_0_OSC_O_CTL1_LOCAL 0x00000004 /* offset */ +#define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M 0x007C0000 /* mask */ +#define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S 18 /* shift */ +#define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_M 0x00020000 /* mask */ +#define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_S 17 /* shift */ +#define DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M 0x00000C00 /* offset */ +#define DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S 10 /* shift */ + +/* AUX ISR states */ +#define WAIT_SMPH 0 /* just took SMPH, start RCOSC_LF */ +#define CAL_RCOSC_LF 1 /* just finished RCOSC_LF, start first RCOSC_HF */ +#define CAL_RCOSC_HF1 2 /* just finished 1st RCOSC_HF, start 2nd */ +#define CAL_RCOSC_HF2 3 /* just finished 2nd RCOSC_HF, decide best */ + +/* calibration states */ +#define PowerCC26X2_STATE_TDC_INIT 0 +#define PowerCC26X2_STATE_CAL_LF_1 1 +#define PowerCC26X2_STATE_CAL_LF_2 2 +#define PowerCC26X2_STATE_CAL_HF1_1 3 +#define PowerCC26X2_STATE_CAL_HF1_2 4 +#define PowerCC26X2_STATE_CAL_HF2 5 +#define PowerCC26X2_STATE_CLEANUP 6 + +/* FSM results */ +typedef enum PowerCC26X2_FsmResult_ { + PowerCC26X2_FSM_RESULT_RUN_FSM, + PowerCC26X2_FSM_RESULT_WAIT_FOR_TDC, + PowerCC26X2_FSM_RESULT_DONE, + PowerCC26X2_FSM_RESULT_ERROR, +} PowerCC26X2_FsmResult; + +/* macros */ +#define Min(a,b) (((a)<(b))?(a):(b)) +#define Max(a,b) (((a)>(b))?(a):(b)) +#define Abs(x) ((x) < 0 ? -(x) : (x)) +#define Scale_rndInf(x) ((3 * (x) + (((x) < 0) ? -2 : 2)) / 4) + +#ifndef PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION + #define PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION 0 +#endif + +#if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION +volatile unsigned int gotSEM = 0; +volatile unsigned int calLFi = 0; +volatile unsigned int calHF1i = 0; +volatile unsigned int calHF2i = 0; +volatile bool doneCal = false; +unsigned int tdcResult_LF = 0; +unsigned int tdcResult_HF1 = 0; +unsigned int tdcResult_HF2 = 0; +unsigned int numISRs = 0; +#endif + +/* Forward declarations */ +static bool getTdcSemaphore(); +static void updateSubSecInc(uint32_t tdcResult); +static void calibrateRcoscHf1(int32_t tdcResult); +static void calibrateRcoscHf2(int32_t tdcResult); +static PowerCC26X2_FsmResult runCalibrateFsm(void); +void PowerCC26X2_calibrate(void); +void PowerCC26X2_RCOSC_clockFunc(uintptr_t arg); + +/* Externs */ +extern PowerCC26X2_ModuleState PowerCC26X2_module; +extern const PowerCC26X2_Config PowerCC26X2_config; + +/* + * ======== PowerCC26X2_initiateCalibration ======== + * Initiate calibration of RCOSC_LF and RCOSCHF + */ +bool PowerCC26X2_initiateCalibration() +{ + unsigned int hwiKey; + bool busy = false; + bool status; + bool gotSem; + + if ((PowerCC26X2_module.calLF == false) && + (PowerCC26X2_config.calibrateRCOSC_HF == false)) { + return (false); + } + + /* make sure calibration is not already in progress */ + hwiKey = HwiP_disable(); + + if (PowerCC26X2_module.busyCal == false) { + PowerCC26X2_module.busyCal = true; + } + else { + busy = true; + } + + HwiP_restore(hwiKey); + + if (busy == true) { + return (false); + } + +#if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION + gotSEM = 0; + calLFi = 0; + calHF1i = 0; + calHF2i = 0; + doneCal = false; +#endif + + /* set contraint to prohibit standby during calibration sequence */ + Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY); + + /* set dependency to keep XOSC_HF active during calibration sequence */ + Power_setDependency(PowerCC26XX_XOSC_HF); + + /* initiate acquisition of semaphore protecting TDC */ + gotSem = getTdcSemaphore(); + + /* if didn't acquire semaphore, must wait for autotake ISR */ + if (gotSem == false) { + PowerCC26X2_module.auxHwiState = WAIT_SMPH; + status = false; /* false: don't do anything else until acquire SMPH */ + } + + /* else, semaphore acquired, OK to proceed with first measurement */ + else { +#if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION + gotSEM = 1; +#endif + status = true; /* true: OK to start first measurement */ + } + + return (status); +} + +/* + * ======== PowerCC26X2_auxISR ======== + * ISR for the AUX combo interrupt event. Implements Hwi state machine to + * step through the RCOSC calibration steps. + */ +void PowerCC26X2_auxISR(uintptr_t arg) +{ + uint32_t tdcResult; + +#if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION + numISRs++; +#endif + + /* + * disable all events that are part of AUX_COMBINED_INTERRUPT. + * This interrupt is reserved for use during RCOSC calibration. + * Other AUX perihperals that want to generate interrupts to CM3 + * must use dedicated interrupt lines or go through AON combined. + */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = 0; + + /* ****** state = WAIT_SMPH: arrive here if just took the SMPH ****** */ + if (PowerCC26X2_module.auxHwiState == WAIT_SMPH) { +#if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION + gotSEM = 1; +#endif + } + + /* **** state = CAL_RCOSC_LF: here when just finished LF counting **** */ + else if (PowerCC26X2_module.auxHwiState == CAL_RCOSC_LF) { + + tdcResult = HWREG(AUX_TDC_BASE + AUX_TDC_O_RESULT); + +#if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION + tdcResult_LF = tdcResult; +#endif + /* update the RTC SUBSECINC register based on LF measurement result */ + updateSubSecInc(tdcResult); +#if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION + calLFi = 1; +#endif + /* if doing HF calibration initiate it now */ + if (PowerCC26X2_config.calibrateRCOSC_HF) { + PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_LF_2; /* next: trigger LF */ + } + + /* else, start cleanup */ + else { + PowerCC26X2_module.calStep = PowerCC26X2_STATE_CLEANUP; /* next: cleanup */ + } + } + + /* ****** state = CAL_RCOSC_HF1: here when just finished 1st RCOSC_HF */ + else if (PowerCC26X2_module.auxHwiState == CAL_RCOSC_HF1) { + + tdcResult = HWREG(AUX_TDC_BASE + AUX_TDC_O_RESULT); + +#if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION + tdcResult_HF1 = tdcResult; + calHF1i = 1; +#endif + + /* use first HF measurement to setup new trim values */ + calibrateRcoscHf1(tdcResult); + + /* if HF setting perfect, nothing more to do, calibration is done */ + if ((tdcResult >= RCOSC_HF_LOW_THRESHOLD_TDC_VALUE) && + (tdcResult <= RCOSC_HF_HIGH_THRESHOLD_TDC_VALUE)) { + PowerCC26X2_module.calStep = PowerCC26X2_STATE_CLEANUP; /* next: cleanup */ + } + + /* else, tweak trims, initiate another HF measurement */ + else { + + PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_HF1_2; /* next: HF meas. #2 */ + } + } + + /* ****** state = just finished second RCOSC_HF measurement ****** */ + else if (PowerCC26X2_module.auxHwiState == CAL_RCOSC_HF2) { + + tdcResult = HWREG(AUX_TDC_BASE + AUX_TDC_O_RESULT); + +#if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION + tdcResult_HF2 = tdcResult; +#endif + /* look for improvement on #2, else revert to previous trim values */ + calibrateRcoscHf2(tdcResult); + + PowerCC26X2_module.calStep = PowerCC26X2_STATE_CLEANUP; /* next: cleanup */ + } + + /* do the next calibration step... */ + PowerCC26X2_calibrate(); +} + +/* + * ======== PowerCC26X2_calibrate ======== + */ +void PowerCC26X2_calibrate(void) +{ + PowerCC26X2_FsmResult fsmResult; + + do { + fsmResult = runCalibrateFsm(); + } while (fsmResult == PowerCC26X2_FSM_RESULT_RUN_FSM); + + switch (fsmResult) { + case PowerCC26X2_FSM_RESULT_WAIT_FOR_TDC: + /* Intentional fall-through */ + case PowerCC26X2_FSM_RESULT_DONE: + /* Do nothing. Calibration is complete or the + * TDC harware will execute in the background + * and continue the operation. */ + break; + default: + /* Something went wrong. No good way to recover. */ + while(1); + } +} + +/* + * ======== runCalibrateFsm ======== + * Execute one state of the clock calibration FSM. + */ +static PowerCC26X2_FsmResult runCalibrateFsm(void) { + + switch (PowerCC26X2_module.calStep) { + + case PowerCC26X2_STATE_TDC_INIT: + + /* Turn on TDC clock */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCCLKCTL) = AUX_SYSIF_TDCCLKCTL_REQ; + + /* set saturation config to 2^24 */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_SATCFG) = AUX_TDC_SATCFG_LIMIT_R24; + + /* set start and stop trigger sources and polarity */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGSRC) = + (AUX_TDC_TRIGSRC_STOP_SRC_ACLK_REF | + AUX_TDC_TRIGSRC_STOP_POL_HIGH) | + (AUX_TDC_TRIGSRC_START_SRC_ACLK_REF | + AUX_TDC_TRIGSRC_START_POL_HIGH); + + /* set TDC_SRC clock to be XOSC_HF/2 = 24 MHz */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, + DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_M, + DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_S, 2); + + /* read back to ensure no race condition between OSC_DIG and AUX_SYSIF */ + DDI16BitfieldRead(AUX_DDI0_OSC_BASE, + DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_M, + DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_S); + + /* set AUX_SYSIF:TDCCLKCTL.REQ... */ + HWREG(AUX_SYSIF_BASE +AUX_SYSIF_O_TDCCLKCTL) = AUX_SYSIF_TDCCLKCTL_REQ; + + /* finish wait for AUX_SYSIF:TDCCLKCTL.ACK to be set ... */ + while(!(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCCLKCTL) & + AUX_SYSIF_TDCCLKCTL_ACK)); + + /* Enable trig count */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGCNTCFG) = AUX_TDC_TRIGCNTCFG_EN; + + /* if LF calibration enabled start LF measurement */ + if (PowerCC26X2_module.calLF) { + + /* clear UPD_REQ, new sub-second increment is NOT available */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINCCTL) = 0; + + /* set next Swi state */ + PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_LF_1; + } + + /* else, start first HF measurement */ + else { + /* set next Swi state */ + PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_HF1_1; + } + + /* abort TDC */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_CTL) = AUX_TDC_CTL_CMD_ABORT; + + /* clear AUX_SYSIFTDCREFCLKCTL.REQ... */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = 0; + + /* finish wait for AUX_SYSIFTDCREFCLKCTL.ACK to be cleared ... */ + while(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) & AUX_SYSIF_TDCREFCLKCTL_ACK); + + return PowerCC26X2_FSM_RESULT_RUN_FSM; + + case PowerCC26X2_STATE_CAL_LF_1: + + /* set next Hwi state before triggering TDC */ + PowerCC26X2_module.auxHwiState = CAL_RCOSC_LF; + + /* set the ACLK reference clock */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_S, + ACLK_REF_SRC_RCOSC_LF); + + /* set AUX_SYSIFTDCREFCLKCTL.REQ */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = AUX_SYSIF_TDCREFCLKCTL_REQ; + + /* Delay for ~110us total until TDCRECLKCTL_ACK is ready */ + ClockP_start(ClockP_handle(&PowerCC26X2_module.calibrationClock)); + + return PowerCC26X2_FSM_RESULT_WAIT_FOR_TDC; + + case PowerCC26X2_STATE_CAL_LF_2: + + PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_HF1_1; + + /* clear AUX_SYSIFTDCREFCLKCTL.REQ... */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = 0; + + /* wait for AUX_SYSIFTDCREFCLKCTL.ACK to be cleared ... */ + while(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) & AUX_SYSIF_TDCREFCLKCTL_ACK); + + return PowerCC26X2_FSM_RESULT_RUN_FSM; + + case PowerCC26X2_STATE_CAL_HF1_1: + + PowerCC26X2_module.auxHwiState = CAL_RCOSC_HF1; + + /* set the ACLK reference clock */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, + DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_S, + ACLK_REF_SRC_RCOSC_HF); + + /* read back to ensure no race condition between OSC_DIG and AUX_SYSIF */ + DDI16BitfieldRead(AUX_DDI0_OSC_BASE, + DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M); + + /* set AUX_SYSIFTDCREFCLKCTL.REQ */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = AUX_SYSIF_TDCREFCLKCTL_REQ; + + /* Delay for ~110us total until TDCRECLKCTL_ACK is ready */ + ClockP_start(ClockP_handle(&PowerCC26X2_module.calibrationClock)); + + return PowerCC26X2_FSM_RESULT_WAIT_FOR_TDC; + + case PowerCC26X2_STATE_CAL_HF1_2: + + PowerCC26X2_module.calStep = PowerCC26X2_STATE_CAL_HF2; + + /* clear AUX_SYSIFTDCREFCLKCTL.REQ... */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = 0; + + /* wait for AUX_SYSIFTDCREFCLKCTL.ACK to be cleared ... */ + while(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) & AUX_SYSIF_TDCREFCLKCTL_ACK); + + return PowerCC26X2_FSM_RESULT_RUN_FSM; + + case PowerCC26X2_STATE_CAL_HF2: + + PowerCC26X2_module.auxHwiState = CAL_RCOSC_HF2; + + /* set the ACLK reference clock */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, + DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_S, + ACLK_REF_SRC_RCOSC_HF); + + /* read back to ensure no race condition between OSC_DIG and AUX_SYSIF */ + DDI16BitfieldRead(AUX_DDI0_OSC_BASE, + DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M); + + /* set AUX_SYSIFTDCREFCLKCTL.REQ */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = AUX_SYSIF_TDCREFCLKCTL_REQ; + + /* Delay for ~110us total until TDCRECLKCTL_ACK is ready */ + ClockP_start(ClockP_handle(&PowerCC26X2_module.calibrationClock)); + + return PowerCC26X2_FSM_RESULT_WAIT_FOR_TDC; + + case PowerCC26X2_STATE_CLEANUP: + + /* release the TDC clock request */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCCLKCTL) = 0; + + /* release the TDC reference clock request */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) = 0; + + /* wait for AUX_SYSIF:TDCCLKCTL.ACK to be cleared ... */ + while ((HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCCLKCTL) & + AUX_SYSIF_TDCCLKCTL_ACK)); + /* wait for AUX_SYSIFTDCREFCLKCTL.ACK to be cleared ... */ + while(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) & + AUX_SYSIF_TDCREFCLKCTL_ACK); + + /* + * Disable all interrupts as part of AUX_COMBINED interrupt + * Once we release semaphore, the sensor controller is allowed + * to use the TDC. When it does, we must ensure that this + * does not cause any unexpected interrupts to the CM3. + */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = 0; + + /* release AUX semaphore */ + HWREG(AUX_SMPH_BASE + AUX_SMPH_O_SMPH1) = 1; + + /* release the power down constraints and XOSC_HF dependency */ + Power_releaseDependency(PowerCC26XX_XOSC_HF); + Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY); + + /* set next state */ + PowerCC26X2_module.calStep = PowerCC26X2_STATE_TDC_INIT; + +#if PowerCC26X2_INSTRUMENT_RCOSC_CALIBRATION + doneCal = true; + calHF2i = 1; +#endif + PowerCC26X2_module.busyCal = false; + + return PowerCC26X2_FSM_RESULT_DONE; + + default: + return PowerCC26X2_FSM_RESULT_ERROR; + } +} + +void PowerCC26X2_RCOSC_clockFunc(uintptr_t arg) { + + /* Wait any remaining time for TDCREFCLKCTL_ACK. Should not spin here at all. */ + while(!(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_TDCREFCLKCTL) & AUX_SYSIF_TDCREFCLKCTL_ACK)); + + /* Set number of periods of ACLK to count */ + if (PowerCC26X2_module.calStep == PowerCC26X2_STATE_CAL_LF_1) { + HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGCNTLOAD) = NUM_RCOSC_LF_PERIODS_TO_MEASURE; + } + else { + HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGCNTLOAD) = NUM_RCOSC_HF_PERIODS_TO_MEASURE; + } + + /* Reset/clear result of TDC */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_CTL) = AUX_TDC_CTL_CMD_CLR_RESULT; + + /* Clear possible pending interrupt source */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) = AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_TDC_DONE; + + /* Enable TDC done interrupt as part of AUX_COMBINED interrupt */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_TDC_DONE; + + /* Run TDC (start synchronously) */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_CTL) = AUX_TDC_CTL_CMD_RUN_SYNC_START; +} + +/* + * ======== getTdcSemaphore ======== + * Get TDC semaphore (number 1) + */ +static bool getTdcSemaphore() +{ + unsigned int own; + + /* try to acquire SMPH */ + own = HWREG(AUX_SMPH_BASE + AUX_SMPH_O_SMPH1); + + /* if acquired SMPH: done */ + if (own != 0) { + return (true); + } + + /* clear the interrupt source, can only be cleared when we don't have semaphore */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) = AUX_EVCTL_EVTOMCUFLAGSCLR_AUX_SMPH_AUTOTAKE_DONE; + + /* + * else, did not acquire the semaphore, enable SMPH_AUTOTAKE_DONE event + * (don't OR, write entire register, no other interrupts can be enabled!) + */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = AUX_EVCTL_COMBEVTOMCUMASK_AUX_SMPH_AUTOTAKE_DONE; + + /* start AUTOTAKE of semaphore for TDC access */ + HWREG(AUX_SMPH_BASE + AUX_SMPH_O_AUTOTAKE) = AUX_TDC_SEMAPHORE_NUMBER; + + return (false); +} + +/* + * ======== updateSubSecInc ======== + * Update the SUBSECINC register based on measured RCOSC_LF frequency + */ +static void updateSubSecInc(uint32_t tdcResult) +{ + int32_t newSubSecInc; + uint32_t oldSubSecInc; + uint32_t subSecInc; + int32_t hposcOffset; + int32_t hposcOffsetInv; + + /* + * Calculate the new SUBSECINC + * Here's the formula: AON_RTC:SUBSECINC = (45813 * NR) / 256 + * Based on measuring 32 LF clock periods + */ + newSubSecInc = (45813 * tdcResult) / 256; + + /* Compensate HPOSC drift if HPOSC is in use */ + if(OSC_IsHPOSCEnabled()) { + /* Get the HPOSC relative offset at this temperature */ + hposcOffset = OSC_HPOSCRelativeFrequencyOffsetGet(AONBatMonTemperatureGetDegC()); + /* Convert to RF core format */ + hposcOffsetInv = OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert(hposcOffset); + /* Adjust SUBSECINC */ + newSubSecInc += (((newSubSecInc >> 4) * (hposcOffsetInv >> 3)) >> 15); + } + + /* Apply filter, but not for first calibration */ + if (PowerCC26X2_module.firstLF) { + /* Don't apply filter first time, to converge faster */ + subSecInc = newSubSecInc; + /* No longer first measurement */ + PowerCC26X2_module.firstLF = false; + } + else { + /* Read old SUBSECINC value */ + oldSubSecInc = HWREG(AON_RTC_BASE + AON_RTC_O_SUBSECINC) & 0x00FFFFFF; + /* Apply filter, 0.5 times old value, 0.5 times new value */ + subSecInc = (oldSubSecInc * 1 + newSubSecInc * 1) / 2; + } + + /* Update SUBSECINC values */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINC0) = subSecInc; + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINC1) = subSecInc >> 16; + + /* update to use new values */ + HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_RTCSUBSECINCCTL) = AUX_SYSIF_RTCSUBSECINCCTL_UPD_REQ; +} + +/* + * ======== PowerCC26X2_calibrateRcoscHf1 ======== + * Calibrate RCOSC_HF agains XOSC_HF: compute and setup new trims + */ +static void calibrateRcoscHf1(int32_t tdcResult) +{ + /* *** STEP 1: Find RCOSC_HF-XOSC_HF frequency offset with current trim settings */ + /* Read in current trim settings */ + PowerCC26X2_module.nCtrimCurr = + (DDI32RegRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_RCOSCHFCTL) & + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_M) >> + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_S; + + PowerCC26X2_module.nCtrimFractCurr = + (DDI32RegRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL) + & DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M) >> + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S; + + PowerCC26X2_module.nRtrimCurr = + (DDI32RegRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ATESTCTL) + & DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M) >> + DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S; + + + /* + * Find RCOSC_HF-XOSC_HF frequency offset with current trim settings + * Positive value => RCOSC_HF runs slow, CTRIM(FRACT) should be increased + * Negative value => RCOSC_HF runs fast, CTRIM(FRACT) should be decreased + * Resolution: 31.25 kHz; CTRIMFRACT resolution ~30 kHz + */ + PowerCC26X2_module.nDeltaFreqCurr = (int32_t) tdcResult - RCOSC_HF_PERFECT_TDC_VALUE; + + /* *** STEP 2: Attempt to calculate more optimal settings */ + if (PowerCC26X2_module.nDeltaFreqCurr == 0) { + /* If perfect, don't perform second measurement and keep current settings */ + PowerCC26X2_module.bRefine = false; + return; + } + if (PowerCC26X2_module.bRefine) { + /* + * Trying to find better match across CTRIM/RTRIM. Due to mismatches the + * first try might not have been more optimal than the current setting. + * Continue refining, starting from stored values + */ + } else { + /* Start from current values */ + PowerCC26X2_module.nCtrimFractNew = PowerCC26X2_module.nCtrimFractCurr; + PowerCC26X2_module.nCtrimNew = PowerCC26X2_module.nCtrimCurr; + PowerCC26X2_module.nRtrimNew = PowerCC26X2_module.nRtrimCurr; + PowerCC26X2_module.nDeltaFreqNew = PowerCC26X2_module.nDeltaFreqCurr; + } + + /* + * Calculate change to CTRIMFRACT with safe assumptions of gain, + * apply delta to current CTRIMFRACT and convert to valid CTRIM/CTRIMFRACT + */ + PowerCC26X2_module.nCtrimFractNew = PowerCC26X2_module.nCtrimFractNew + + Scale_rndInf(PowerCC26X2_module.nDeltaFreqNew); + PowerCC26X2_module.nCtrimNew = PowerCC26X2_module.nCtrimCurr; + + /* One step of CTRIM is about 500 kHz, so limit to one CTRIM step */ + if (PowerCC26X2_module.nCtrimFractNew < 1) { + if (PowerCC26X2_module.nRtrimNew == 3) { + /* We try the slow RTRIM in this CTRIM first */ + PowerCC26X2_module.nCtrimFractNew = Max(1, PowerCC26X2_module.nCtrimFractNew + 21); + PowerCC26X2_module.nRtrimNew = 0; + } + else { + /* Step down one CTRIM and use fast RTRIM */ + PowerCC26X2_module.nCtrimFractNew = Max(1, PowerCC26X2_module.nCtrimFractNew + 32 - 21); + PowerCC26X2_module.nCtrimNew = Max(0, PowerCC26X2_module.nCtrimNew - 1); + PowerCC26X2_module.nRtrimNew = 3; + } + } + else if (PowerCC26X2_module.nCtrimFractNew > 30) { + if (PowerCC26X2_module.nRtrimNew == 0) { + /* We try the slow RTRIM in this CTRIM first */ + PowerCC26X2_module.nCtrimFractNew = Min(30, PowerCC26X2_module.nCtrimFractNew - 21); + PowerCC26X2_module.nRtrimNew = 3; + } + else { + /* Step down one CTRIM and use fast RTRIM */ + PowerCC26X2_module.nCtrimFractNew = Min(30, PowerCC26X2_module.nCtrimFractNew - 32 + 21); + PowerCC26X2_module.nCtrimNew = Min(0x3F, PowerCC26X2_module.nCtrimNew + 1); + PowerCC26X2_module.nRtrimNew = 0; + } + } + else + { + /* We're within sweet spot of current CTRIM => no change */ + } + + /* Find RCOSC_HF vs XOSC_HF frequency offset with new trim settings */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_RCOSCHFCTL, + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_M, + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_S, + PowerCC26X2_module.nCtrimNew); + + /* Enable RCOSCHFCTRIMFRACT_EN */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_M, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_S, + 1); + + /* Modify CTRIM_FRACT */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S, + PowerCC26X2_module.nCtrimFractNew); + + /* Modify RTRIM */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ATESTCTL, + DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M, + DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S, + PowerCC26X2_module.nRtrimNew); +} + +/* + * ======== Power_calibrateRcoscHf2 ======== + * Calibrate RCOSC_HF agains XOSC_HF: determine better result, set new trims + */ +static void calibrateRcoscHf2(int32_t tdcResult) +{ + + PowerCC26X2_module.nDeltaFreqNew = (int32_t) tdcResult - RCOSC_HF_PERFECT_TDC_VALUE; + /* Calculate new delta freq */ + + /* *** STEP 4: Determine whether the new settings are better or worse */ + if (Abs(PowerCC26X2_module.nDeltaFreqNew) <= Abs(PowerCC26X2_module.nDeltaFreqCurr)) { + /* New settings are better or same -> make current by keeping in registers */ + PowerCC26X2_module.bRefine = false; + } + else { + /* First measurement was better than second, restore current settings */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_RCOSCHFCTL, + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_M, + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_S, + PowerCC26X2_module.nCtrimCurr); + + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S, + PowerCC26X2_module.nCtrimFractCurr); + + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ATESTCTL, + DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M, + DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S, + PowerCC26X2_module.nRtrimCurr); + + /* Enter a refinement mode where we keep searching for better matches */ + PowerCC26X2_module.bRefine = true; + } + +} diff --git a/simplelink/source/ti/drivers/power/PowerCC26XX.c b/simplelink/source/ti/drivers/power/PowerCC26XX.c new file mode 100644 index 0000000..0cfa419 --- /dev/null +++ b/simplelink/source/ti/drivers/power/PowerCC26XX.c @@ -0,0 +1,1394 @@ +/* + * Copyright (c) 2015-2018, Texas Instruments Incorporated + * 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 Texas Instruments Incorporated 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 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. + */ +/* + * ======== PowerCC26XX.c ======== + */ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include DeviceFamily_constructPath(inc/hw_types.h) +#include DeviceFamily_constructPath(inc/hw_prcm.h) +#include DeviceFamily_constructPath(inc/hw_nvic.h) +#include DeviceFamily_constructPath(inc/hw_aon_wuc.h) +#include DeviceFamily_constructPath(inc/hw_aon_rtc.h) +#include DeviceFamily_constructPath(inc/hw_memmap.h) +#include DeviceFamily_constructPath(inc/hw_ccfg.h) +#include DeviceFamily_constructPath(inc/hw_rfc_pwr.h) +#include DeviceFamily_constructPath(driverlib/sys_ctrl.h) +#include DeviceFamily_constructPath(driverlib/pwr_ctrl.h) +#include DeviceFamily_constructPath(driverlib/prcm.h) +#include DeviceFamily_constructPath(driverlib/aon_wuc.h) +#include DeviceFamily_constructPath(driverlib/aon_ioc.h) +#include DeviceFamily_constructPath(driverlib/aon_rtc.h) +#include DeviceFamily_constructPath(driverlib/aon_event.h) +#include DeviceFamily_constructPath(driverlib/aux_wuc.h) +#include DeviceFamily_constructPath(driverlib/osc.h) +#include DeviceFamily_constructPath(driverlib/cpu.h) +#include DeviceFamily_constructPath(driverlib/vims.h) +#include DeviceFamily_constructPath(driverlib/rfc.h) +#include DeviceFamily_constructPath(driverlib/sys_ctrl.h) +#include DeviceFamily_constructPath(driverlib/driverlib_release.h) +#include DeviceFamily_constructPath(driverlib/setup.h) +#include DeviceFamily_constructPath(driverlib/ccfgread.h) + +static unsigned int configureXOSCHF(unsigned int action); +static unsigned int nopResourceHandler(unsigned int action); +static unsigned int configureRFCoreClocks(unsigned int action); +static void switchXOSCHFclockFunc(uintptr_t arg0); +static void lfClockReadyCallback(uintptr_t arg); +static void disableLfClkQualifiersEnableClkLoss(); +static void emptyClockFunc(uintptr_t arg); +static int_fast16_t notify(uint_fast16_t eventType); + +/* RCOSC calibration functions functions */ +extern void PowerCC26XX_doCalibrate(void); +extern bool PowerCC26XX_initiateCalibration(void); +extern void PowerCC26XX_auxISR(uintptr_t arg); +extern void PowerCC26XX_RCOSC_clockFunc(uintptr_t arg); + +/* Externs */ +extern const PowerCC26XX_Config PowerCC26XX_config; + +/* Module_State */ +PowerCC26XX_ModuleState PowerCC26XX_module = { + .notifyList = { NULL }, /* list of registered notifications */ + .constraintMask = 0, /* the constraint mask */ + .clockObj = { 0 }, /* Clock object for scheduling wakeups */ + .xoscClockObj = { 0 }, /* Clock object for XOSC_HF switching */ + .lfClockObj = { 0 }, /* Clock object for LF clock check */ + .calClockStruct = { 0 }, /* Clock object for RCOSC calibration */ + .hwiStruct = { 0 }, /* hwi object for calibration */ + .nDeltaFreqCurr = 0, /* RCOSC calibration variable */ + .nCtrimCurr = 0, /* RCOSC calibration variable */ + .nCtrimFractCurr = 0, /* RCOSC calibration variable */ + .nCtrimNew = 0, /* RCOSC calibration variable */ + .nCtrimFractNew = 0, /* RCOSC calibration variable */ + .nRtrimNew = 0, /* RCOSC calibration variable */ + .nRtrimCurr = 0, /* RCOSC calibration variable */ + .nDeltaFreqNew = 0, /* RCOSC calibration variable */ + .bRefine = false, /* RCOSC calibration variable */ + .state = Power_ACTIVE, /* current transition state */ + .xoscPending = false, /* is XOSC_HF activation in progress? */ + .calLF = false, /* calibrate RCOSC_LF? */ + .hwiState = 0, /* calibration AUX ISR state */ + .busyCal = false, /* already busy calibrating */ + .calStep = 1, /* current calibration step */ + .firstLF = true, /* is this first LF calibration? */ + .enablePolicy = false, /* default value is false */ + .initialized = false, /* whether Power_init has been called */ +#if defined(DeviceFamily_CC26X0R2) + .emulatorAttached = false, /* emulator attached during boot */ +#endif + .constraintCounts = { 0, 0, 0, 0, 0, 0, 0 }, + .resourceCounts = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + .resourceHandlers = { + configureRFCoreClocks, + configureXOSCHF, + nopResourceHandler + }, /* special resource handler functions */ + .policyFxn = NULL /* power policyFxn */ +}; + +/* resource database */ +const PowerCC26XX_ResourceRecord resourceDB[PowerCC26XX_NUMRESOURCES] = { + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER0 }, /* PERIPH_GPT0 */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER1 }, /* PERIPH_GPT1 */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER2 }, /* PERIPH_GPT2 */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TIMER3 }, /* PERIPH_GPT3 */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_SERIAL, PRCM_PERIPH_SSI0 }, /* PERIPH_SSI0 */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_SSI1 }, /* PERIPH_SSI1 */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_SERIAL, PRCM_PERIPH_UART0 }, /* PERIPH_UART0 */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_SERIAL, PRCM_PERIPH_I2C0 }, /* PERIPH_I2C0 */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_TRNG }, /* PERIPH_TRNG */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_GPIO }, /* PERIPH_GPIO */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_UDMA }, /* PERIPH_UDMA */ + { PowerCC26XX_PERIPH | PowerCC26XX_DOMAIN_PERIPH, PRCM_PERIPH_CRYPTO }, /* PERIPH_CRYPTO */ + { PowerCC26XX_PERIPH | PowerCC26XX_PERIPH_UDMA, PRCM_PERIPH_I2S }, /* PERIPH_I2S */ + { PowerCC26XX_SPECIAL | PowerCC26XX_DOMAIN_RFCORE, 0 }, /* PERIPH_RFCORE */ + { PowerCC26XX_SPECIAL | PowerCC26XX_NOPARENT, 1 }, /* XOSC_HF */ + { PowerCC26XX_DOMAIN | PowerCC26XX_NOPARENT, PRCM_DOMAIN_PERIPH }, /* DOMAIN_PERIPH */ + { PowerCC26XX_DOMAIN | PowerCC26XX_NOPARENT, PRCM_DOMAIN_SERIAL }, /* DOMAIN_SERIAL */ + { PowerCC26XX_DOMAIN | PowerCC26XX_NOPARENT, PRCM_DOMAIN_RFCORE }, /* DOMAIN_RFCORE */ + { PowerCC26XX_SPECIAL | PowerCC26XX_NOPARENT, 2 } /* DOMAIN_SYSBUS */ +}; + + +/* ****************** Power APIs ******************** */ + +/* + * ======== Power_disablePolicy ======== + * Do not run the configured policy + */ +bool Power_disablePolicy(void) +{ + bool enablePolicy = PowerCC26XX_module.enablePolicy; + PowerCC26XX_module.enablePolicy = false; + + return (enablePolicy); +} + +/* + * ======== Power_enablePolicy ======== + * Run the configured policy + */ +void Power_enablePolicy(void) +{ + PowerCC26XX_module.enablePolicy = true; +} + +/* + * ======== Power_getConstraintMask ======== + * Get a bitmask indicating the constraints that have been registered with + * Power. + */ +uint_fast32_t Power_getConstraintMask(void) +{ + return (PowerCC26XX_module.constraintMask); +} + +/* + * ======== Power_getDependencyCount ======== + * Get the count of dependencies that are currently declared upon a resource. + */ +int_fast16_t Power_getDependencyCount(uint_fast16_t resourceId) +{ + DebugP_assert(resourceId < PowerCC26XX_NUMRESOURCES); + + return ((int_fast16_t)PowerCC26XX_module.resourceCounts[resourceId]); +} + +/* + * ======== Power_getTransitionLatency ======== + * Get the transition latency for a sleep state. The latency is reported + * in units of microseconds. + */ +uint_fast32_t Power_getTransitionLatency(uint_fast16_t sleepState, + uint_fast16_t type) +{ + uint32_t latency = 0; + + if (type == Power_RESUME) { + if (sleepState == PowerCC26XX_STANDBY) { + latency = PowerCC26XX_RESUMETIMESTANDBY; + } + } + else { + if (sleepState == PowerCC26XX_STANDBY) { + latency = PowerCC26XX_TOTALTIMESTANDBY; + } + } + + return (latency); +} + +/* + * ======== Power_getTransitionState ======== + * Get the current sleep transition state. + */ +uint_fast16_t Power_getTransitionState(void) +{ + return (PowerCC26XX_module.state); +} + +/* + * ======== Power_idleFunc ======== + * Function needs to be plugged into the idle loop. + * It calls the configured policy function if the + * 'enablePolicy' flag is set. + */ +void Power_idleFunc() +{ + if (PowerCC26XX_module.enablePolicy) { + if (PowerCC26XX_module.policyFxn != NULL) { + (*(PowerCC26XX_module.policyFxn))(); + } + } +} + +/* + * ======== Power_init ======== + */ +int_fast16_t Power_init() +{ + ClockP_Params clockParams; + uint32_t ccfgLfClkSrc; + uint32_t timeout; + + /* if this function has already been called, just return */ + if (PowerCC26XX_module.initialized) { + return (Power_SOK); + } + +#if defined(DeviceFamily_CC26X0R2) + /* check to see if the JTAG_PD is on, meaning the emulator was attached during boot and */ + /* that the user is in an active debug session */ + PowerCC26XX_module.emulatorAttached = (HWREG(AON_WUC_BASE + AON_WUC_O_PWRSTAT) & AON_WUC_PWRSTAT_JTAG_PD_ON) == AON_WUC_PWRSTAT_JTAG_PD_ON; +#endif + + /* set module state field 'initialized' to true */ + PowerCC26XX_module.initialized = true; + + /* set the module state enablePolicy field */ + PowerCC26XX_module.enablePolicy = PowerCC26XX_config.enablePolicy; + + /* copy the Power policy function to module state */ + PowerCC26XX_module.policyFxn = PowerCC26XX_config.policyFxn; + + /* construct the Clock object for scheduling of wakeups */ + /* initiated and started by the power policy */ + ClockP_Params_init(&clockParams); + clockParams.period = 0; + clockParams.startFlag = false; + clockParams.arg = 0; + ClockP_construct(&PowerCC26XX_module.clockObj, &emptyClockFunc, + 0, &clockParams); + + /* construct the Clock object for XOSC_HF switching */ + /* initiated and started by Power module when activating XOSC_HF */ + ClockP_construct(&PowerCC26XX_module.xoscClockObj, &switchXOSCHFclockFunc, + 0, &clockParams); + + /* construct the Clock object for disabling LF clock quailifiers */ + /* one shot, auto start, first expires at 100 msec */ + ClockP_construct(&PowerCC26XX_module.lfClockObj, &lfClockReadyCallback, + 0, &clockParams); + + (*(PowerCC26XX_config.calibrateFxn))(PowerCC26XX_SETUP_CALIBRATE); + + DRIVERLIB_ASSERT_CURR_RELEASE(); + + /* read the LF clock source from CCFG */ + ccfgLfClkSrc = CCFGRead_SCLK_LF_OPTION(); + + /* check if should calibrate RCOSC_LF */ + if (PowerCC26XX_config.calibrateRCOSC_LF) { + /* verify RCOSC_LF is the LF clock source */ + if (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_RCOSC_LF) { + PowerCC26XX_module.calLF = true; + } + } + + /* + * if LF source is RCOSC_LF or XOSC_LF: assert DISALLOW_STANDBY constraint + * and start a timeout to check for activation + */ + if ((ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_RCOSC_LF) || + (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_XOSC_LF)) { + + /* disallow STANDBY pending LF clock quailifier disabling */ + Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY); + + /* determine timeout */ + if (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_RCOSC_LF) { + timeout = PowerCC26XX_INITIALWAITRCOSC_LF; + } + else { + timeout = PowerCC26XX_INITIALWAITXOSC_LF; + } + + /* start the Clock object */ + ClockP_setTimeout(ClockP_handle(&PowerCC26XX_module.lfClockObj), + (timeout / ClockP_tickPeriod)); + ClockP_start(ClockP_handle(&PowerCC26XX_module.lfClockObj)); + } + + /* + * else, if the LF clock source is external, can disable clock qualifiers + * now; no need to assert DISALLOW_STANDBY or start the Clock object + */ + else if (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_EXTERNAL_LF) { + + /* Disable clock qualifiers and enable clock loss */ + disableLfClkQualifiersEnableClkLoss(); + } + /* + * else, user has requested LF to be derived from XOSC_HF + */ + else if(ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_XOSC_HF_DLF) + { + /* disallow standby */ + Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY); + + /* wait for the XOSC_HF to power up if it's not ready.. */ + if(OSCClockSourceGet(OSC_SRC_CLK_LF) == OSC_XOSC_HF) + { + /* XOSC_HF is ready. Simply disable clock qualifiers and enable clock loss */ + disableLfClkQualifiersEnableClkLoss(NULL); + } + else + { + /* XOSC_HF is not ready yet, schedule clock to check again later */ + timeout = PowerCC26XX_INITIALWAITXOSC_HF / ClockP_tickPeriod; + /* start the Clock object */ + ClockP_setTimeout(ClockP_handle(&PowerCC26XX_module.lfClockObj), + (timeout / ClockP_tickPeriod)); + ClockP_start(ClockP_handle(&PowerCC26XX_module.lfClockObj)); + } + } + + /* if VIMS RAM is configured as GPRAM: set retention constraint */ + if (!CCFGRead_DIS_GPRAM()) { + Power_setConstraint(PowerCC26XX_RETAIN_VIMS_CACHE_IN_STANDBY); + } + + return (Power_SOK); +} + +/* + * ======== Power_registerNotify ======== + * Register a function to be called on a specific power event. + * + */ +int_fast16_t Power_registerNotify(Power_NotifyObj * pNotifyObj, + uint_fast16_t eventTypes, Power_NotifyFxn notifyFxn, uintptr_t clientArg) +{ + int_fast16_t status = Power_SOK; + + /* check for NULL pointers */ + if ((pNotifyObj == NULL) || (notifyFxn == NULL)) { + status = Power_EINVALIDPOINTER; + } + + else { + /* fill in notify object elements */ + pNotifyObj->eventTypes = eventTypes; + pNotifyObj->notifyFxn = notifyFxn; + pNotifyObj->clientArg = clientArg; + + /* place notify object on event notification queue */ + List_put(&PowerCC26XX_module.notifyList, (List_Elem*)pNotifyObj); + } + + return (status); +} + +/* + * ======== Power_releaseConstraint ======== + * Release a previously declared constraint. + */ +int_fast16_t Power_releaseConstraint(uint_fast16_t constraintId) +{ + unsigned int key; + uint8_t count; + + DebugP_assert(constraintId < PowerCC26XX_NUMCONSTRAINTS); + + key = HwiP_disable(); + + /* get the count of the constraint */ + count = PowerCC26XX_module.constraintCounts[constraintId]; + + DebugP_assert(count != 0); + + count--; + + /* save the updated count */ + PowerCC26XX_module.constraintCounts[constraintId] = count; + + if (count == 0) { + PowerCC26XX_module.constraintMask &= ~(1 << constraintId); + } + + HwiP_restore(key); + + return (Power_SOK); +} + +/* + * ======== Power_releaseDependency ======== + * Release a previously declared dependency. + */ +int_fast16_t Power_releaseDependency(uint_fast16_t resourceId) +{ + uint8_t parent; + uint8_t count; + uint32_t id; + unsigned int key; + + /* assert resourceId is valid */ + DebugP_assert(resourceId < PowerCC26XX_NUMRESOURCES); + + /* disable interrupts */ + key = HwiP_disable(); + + /* read and decrement the reference count */ + count = PowerCC26XX_module.resourceCounts[resourceId]; + + DebugP_assert(count != 0); + + count--; + + /* save the reference count */ + PowerCC26XX_module.resourceCounts[resourceId] = count; + + /* if this was the last dependency being released.., */ + if (count == 0) { + /* deactivate this resource ... */ + id = resourceDB[resourceId].driverlibID; + + /* is resource a peripheral?... */ + if (resourceDB[resourceId].flags & PowerCC26XX_PERIPH) { + PRCMPeripheralRunDisable(id); + PRCMPeripheralSleepDisable(id); + PRCMPeripheralDeepSleepDisable(id); + PRCMLoadSet(); + while (!PRCMLoadGet()) { + ; + } + } + /* else, does resource require a special handler?... */ + else if (resourceDB[resourceId].flags & PowerCC26XX_SPECIAL) { + /* call the special handler */ + PowerCC26XX_module.resourceHandlers[id](PowerCC26XX_DISABLE); + } + + /* else resource is a power domain */ + else { + PRCMPowerDomainOff(id); + while (PRCMPowerDomainStatus(id) != PRCM_DOMAIN_POWER_OFF) { + ; + } + } + + /* propagate release up the dependency tree ... */ + + /* check for a first parent */ + parent = resourceDB[resourceId].flags & PowerCC26XX_PARENTMASK; + + /* if 1st parent, make recursive call to release that dependency */ + if (parent < PowerCC26XX_NUMRESOURCES) { + Power_releaseDependency(parent); + } + } + + /* re-enable interrupts */ + HwiP_restore(key); + + return (Power_SOK); +} + +/* + * ======== Power_setConstraint ======== + * Declare an operational constraint. + */ +int_fast16_t Power_setConstraint(uint_fast16_t constraintId) +{ + unsigned int key; + + DebugP_assert(constraintId < PowerCC26XX_NUMCONSTRAINTS); + + /* disable interrupts */ + key = HwiP_disable(); + + /* set the specified constraint in the constraintMask */ + PowerCC26XX_module.constraintMask |= 1 << constraintId; + + /* increment the specified constraint count */ + PowerCC26XX_module.constraintCounts[constraintId]++; + + /* re-enable interrupts */ + HwiP_restore(key); + + return (Power_SOK); +} + +/* + * ======== Power_setDependency ======== + * Declare a dependency upon a resource. + */ +int_fast16_t Power_setDependency(uint_fast16_t resourceId) +{ + uint8_t parent; + uint8_t count; + uint32_t id; + unsigned int key; + + DebugP_assert(resourceId < PowerCC26XX_NUMRESOURCES); + + /* disable interrupts */ + key = HwiP_disable(); + + /* read and increment reference count */ + count = PowerCC26XX_module.resourceCounts[resourceId]++; + + /* if resource was NOT activated previously ... */ + if (count == 0) { + /* propagate set up the dependency tree ... */ + + /* check for a first parent */ + parent = resourceDB[resourceId].flags & PowerCC26XX_PARENTMASK; + + /* if first parent, make recursive call to set that dependency */ + if (parent < PowerCC26XX_NUMRESOURCES) { + Power_setDependency(parent); + } + + /* now activate this resource ... */ + id = resourceDB[resourceId].driverlibID; + + /* is resource a peripheral?... */ + if (resourceDB[resourceId].flags & PowerCC26XX_PERIPH) { + PRCMPeripheralRunEnable(id); + PRCMPeripheralSleepEnable(id); + PRCMPeripheralDeepSleepEnable(id); + PRCMLoadSet(); + while (!PRCMLoadGet()) { + ; + } + } + /* else, does resource require a special handler?... */ + else if (resourceDB[resourceId].flags & PowerCC26XX_SPECIAL) { + /* call the special handler */ + PowerCC26XX_module.resourceHandlers[id](PowerCC26XX_ENABLE); + } + /* else resource is a power domain */ + else { + PRCMPowerDomainOn(id); + while (PRCMPowerDomainStatus(id) != PRCM_DOMAIN_POWER_ON) { + ; + } + } + } + + /* re-enable interrupts */ + HwiP_restore(key); + + return (Power_SOK); +} + +/* + * ======== Power_setPolicy ======== + * Set the Power policy function + */ +void Power_setPolicy(Power_PolicyFxn policy) +{ + PowerCC26XX_module.policyFxn = policy; +} + +/* + * ======== Power_shutdown ======== + */ +int_fast16_t Power_shutdown(uint_fast16_t shutdownState, + uint_fast32_t shutdownTime) +{ + int_fast16_t status = Power_EFAIL; + unsigned int constraints; + unsigned int hwiKey; + + /* disable interrupts */ + hwiKey = HwiP_disable(); + + /* check if there is a constraint to prohibit shutdown */ + constraints = Power_getConstraintMask(); + if (constraints & (1 << PowerCC26XX_DISALLOW_SHUTDOWN)) { + status = Power_ECHANGE_NOT_ALLOWED; + } + + /* OK to shutdown ... */ + else if (PowerCC26XX_module.state == Power_ACTIVE) { + /* set new transition state to entering shutdown */ + PowerCC26XX_module.state = Power_ENTERING_SHUTDOWN; + + /* signal all clients registered for pre-shutdown notification */ + status = notify(PowerCC26XX_ENTERING_SHUTDOWN); + + /* check for any error */ + if (status != Power_SOK) { + PowerCC26XX_module.state = Power_ACTIVE; + HwiP_restore(hwiKey); + return (status); + } + + /* now proceed with shutdown sequence ... */ + + /* If the JTAG_PD is on, make sure that the DUT reboots without + * stopping for halt-in-boot when it enters shutdown. */ +#if defined(DeviceFamily_CC26X0R2) + uint32_t aonSysctrlResetctl; + + if((HWREG(AON_WUC_BASE + AON_WUC_O_PWRSTAT) & AON_WUC_PWRSTAT_JTAG_PD_ON) && + (!PowerCC26XX_module.emulatorAttached)) { + /* set BOOT_DET = b10. + * The next time the device enters shutdown the + * device will start booting immediately because the JTAG_PD is already on. + * However since since BOOT_DET == b10, the boot code will run not wait + * for a GPIO interrupt, but rather run to completion and branch to the + * flash image with the JTAG_PD turned off. + */ + aonSysctrlResetctl = HWREG( AON_SYSCTL_BASE + AON_SYSCTL_O_RESETCTL ) & + ~( AON_SYSCTL_RESETCTL_BOOT_DET_1_CLR_M | AON_SYSCTL_RESETCTL_BOOT_DET_0_CLR_M | + AON_SYSCTL_RESETCTL_BOOT_DET_1_SET_M | AON_SYSCTL_RESETCTL_BOOT_DET_0_SET_M ); + /* To get BOOT_DET = b10, set BOOT_DET_1_SET and BOOT_DET_0_CLR*/ + HWREG(AON_SYSCTL_BASE + AON_SYSCTL_O_RESETCTL) = aonSysctrlResetctl | + (AON_SYSCTL_RESETCTL_BOOT_DET_0_CLR | AON_SYSCTL_RESETCTL_BOOT_DET_1_SET); + } +#endif + + /* 1. Switch HF, MF, and LF clocks to source from RCOSC_HF */ + if (OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_RCOSC_HF) { + /* 1.1. Source HF and MF from RCOSC_HF */ + OSCClockSourceSet(OSC_SRC_CLK_HF | OSC_SRC_CLK_MF, OSC_RCOSC_HF); + while (!OSCHfSourceReady()); + OSCHfSourceSwitch(); + } + /* 1.2. Source LF from RCOSC_LF */ + OSCClockSourceSet(OSC_SRC_CLK_LF, OSC_RCOSC_LF); + while (OSCClockSourceGet(OSC_SRC_CLK_LF) != OSC_RCOSC_LF); + + /* 2. Make sure DMA and CRYTO clocks are off in deep-sleep */ + PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_CRYPTO); + PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_UDMA); + PRCMLoadSet(); + while (!PRCMLoadGet()) { + ; + } + + /* 3. Power OFF AUX and disconnect from bus */ + AUXWUCPowerCtrl(AUX_WUC_POWER_OFF); + + /* 4. Remove AUX force ON */ + HWREG(AON_WUC_BASE + AON_WUC_O_AUXCTL) &= + ~AON_WUC_AUXCTL_AUX_FORCE_ON; + + /* + * 5. Reset AON event source IDs to avoid pending events powering + * on MCU/AUX + */ + HWREG(AON_EVENT_BASE + AON_EVENT_O_MCUWUSEL) = 0x3F3F3F3F; + HWREG(AON_EVENT_BASE + AON_EVENT_O_AUXWUSEL) = 0x003F3F3F; + + /* sync AON */ + SysCtrlAonSync(); + + /* + * 6. Enable shutdown - this latches the IOs, so configuration of + * IOCFGx registers must be done prior to this + */ + AONWUCShutDownEnable(); + + /* 7. Sync AON */ + SysCtrlAonSync(); + + /* 8. Wait until AUX powered off */ + while (AONWUCPowerStatusGet() & AONWUC_AUX_POWER_ON); + + /* 9. Request to power off MCU when go to deep sleep */ + PRCMMcuPowerOff(); + + /* + * 10. Turn off power domains inside MCU VD (BUS, FL_BUS, RFC, + * CPU) + */ + PRCMPowerDomainOff(PRCM_DOMAIN_RFCORE | PRCM_DOMAIN_SERIAL | + PRCM_DOMAIN_PERIPH | PRCM_DOMAIN_CPU | PRCM_DOMAIN_VIMS); + + /* 11. Deep sleep to activate shutdown */ + PRCMDeepSleep(); + } + else { + status = Power_EBUSY; + } + + /* NOTE: if shutdown succeeded, should never get here */ + + /* return failure status */ + PowerCC26XX_module.state = Power_ACTIVE; + + /* re-enable interrupts */ + HwiP_restore(hwiKey); + + /* if get here, failed to shutdown, return error code */ + return (status); +} + +/* + * ======== Power_sleep ======== + */ +int_fast16_t Power_sleep(uint_fast16_t sleepState) +{ + int_fast16_t status = Power_SOK; + int_fast16_t notifyStatus = Power_SOK; + int_fast16_t lateNotifyStatus = Power_SOK; + unsigned int xosc_hf_active = false; + uint_fast16_t postEventLate; + uint32_t poweredDomains = 0; + uint_fast16_t preEvent; + uint_fast16_t postEvent; + unsigned int constraints; + bool retainCache = false; + uint32_t modeVIMS; + unsigned int swiKey; + +#if defined(DeviceFamily_CC26X0R2) + /* has JTAG_PD been turned AFTER boot due to TCK noise? */ + if((HWREG(AON_WUC_BASE + AON_WUC_O_PWRSTAT) & AON_WUC_PWRSTAT_JTAG_PD_ON) && (!PowerCC26XX_module.emulatorAttached)) + { + /* notify all subscribers */ + notify(PowerCC26XX_JTAG_PD_TURNED_ON); + } +#endif + + /* first validate the sleep code */ + if (sleepState != PowerCC26XX_STANDBY) { + status = Power_EINVALIDINPUT; + } + + else { + + /* check to make sure Power is not busy with another transition */ + if (PowerCC26XX_module.state == Power_ACTIVE) { + /* set transition state to entering sleep */ + PowerCC26XX_module.state = Power_ENTERING_SLEEP; + } + else { + status = Power_EBUSY; + } + + if (status == Power_SOK) { + + /* setup sleep vars */ + preEvent = PowerCC26XX_ENTERING_STANDBY; + postEvent = PowerCC26XX_AWAKE_STANDBY; + postEventLate = PowerCC26XX_AWAKE_STANDBY_LATE; + + /* disable Task scheduling; allow Swis and Hwis for notifications */ + PowerCC26XX_schedulerDisable(); + + /* signal all clients registered for pre-sleep notification */ + status = notify(preEvent); + + /* check for any error */ + if (status != Power_SOK) { + PowerCC26XX_module.state = Power_ACTIVE; + PowerCC26XX_schedulerRestore(); + return (status); + } + + /* now disable Swi scheduling */ + swiKey = SwiP_disable(); + + /* 1. Freeze the IOs on the boundary between MCU and AON */ + AONIOCFreezeEnable(); + + /* 2. If XOSC_HF is active, force it off */ + if(OSCClockSourceGet(OSC_SRC_CLK_HF) == OSC_XOSC_HF) { + xosc_hf_active = true; + configureXOSCHF(PowerCC26XX_DISABLE); + } + + /* 3. Allow AUX to power down */ + AONWUCAuxWakeupEvent(AONWUC_AUX_ALLOW_SLEEP); + + /* 4. Make sure writes take effect */ + SysCtrlAonSync(); + + /* now proceed to transition to Power_STANDBY ... */ + + /* 5. Query and save domain states before powering them off */ + if (Power_getDependencyCount(PowerCC26XX_DOMAIN_RFCORE)) { + poweredDomains |= PRCM_DOMAIN_RFCORE; + } + if (Power_getDependencyCount(PowerCC26XX_DOMAIN_SERIAL)){ + poweredDomains |= PRCM_DOMAIN_SERIAL; + } + if (Power_getDependencyCount(PowerCC26XX_DOMAIN_PERIPH)) { + poweredDomains |= PRCM_DOMAIN_PERIPH; + } + + /* 6. Gate running deep sleep clocks for Crypto and DMA */ + if (Power_getDependencyCount(PowerCC26XX_PERIPH_CRYPTO)) { + PRCMPeripheralDeepSleepDisable( + resourceDB[PowerCC26XX_PERIPH_CRYPTO].driverlibID); + } + if (Power_getDependencyCount(PowerCC26XX_PERIPH_UDMA)) { + PRCMPeripheralDeepSleepDisable( + resourceDB[PowerCC26XX_PERIPH_UDMA].driverlibID); + } + /* 7. Make sure clock settings take effect */ + PRCMLoadSet(); + + /* 8. Request power off of domains in the MCU voltage domain */ + PRCMPowerDomainOff(poweredDomains | PRCM_DOMAIN_CPU); + + /* 9. Request uLDO during standby */ + PRCMMcuUldoConfigure(true); + + /* query constraints to determine if cache should be retained */ + constraints = Power_getConstraintMask(); + if (constraints & (1 << PowerCC26XX_RETAIN_VIMS_CACHE_IN_STANDBY)) { + retainCache = true; + } + + /* 10. If don't want VIMS retention in standby, disable it now... */ + if (retainCache == false) { + + /* 10.1 Get the current VIMS mode */ + do { + modeVIMS = VIMSModeGet(VIMS_BASE); + } while (modeVIMS == VIMS_MODE_CHANGING); + + /* 10.2 If in a cache mode, turn VIMS off */ + if (modeVIMS == VIMS_MODE_ENABLED) { + + /* 10.3 Now turn off the VIMS */ + VIMSModeSet(VIMS_BASE, VIMS_MODE_OFF); + } + + /* 10.4 Now disable retention */ + PRCMCacheRetentionDisable(); + } + + /* 11. Setup recharge parameters */ + SysCtrlSetRechargeBeforePowerDown(XOSC_IN_HIGH_POWER_MODE); + + /* 12. Make sure all writes have taken effect */ + SysCtrlAonSync(); + + /* 13. Invoke deep sleep to go to STANDBY */ + PRCMDeepSleep(); + + /* 14. If didn't retain VIMS in standby, re-enable retention now */ + if (retainCache == false) { + + /* 14.1 If previously in a cache mode, restore the mode now */ + if (modeVIMS == VIMS_MODE_ENABLED) { + VIMSModeSet(VIMS_BASE, modeVIMS); + } + + /* 14.2 Re-enable retention */ + PRCMCacheRetentionEnable(); + } + + /* 15. Start forcing on power to AUX */ + AONWUCAuxWakeupEvent(AONWUC_AUX_WAKEUP); + + /* 16. Start re-powering power domains */ + PRCMPowerDomainOn(poweredDomains); + + /* 17. Restore deep sleep clocks of Crypto and DMA */ + if (Power_getDependencyCount(PowerCC26XX_PERIPH_CRYPTO)) { + PRCMPeripheralDeepSleepEnable( + resourceDB[PowerCC26XX_PERIPH_CRYPTO].driverlibID); + } + if (Power_getDependencyCount(PowerCC26XX_PERIPH_UDMA)) { + PRCMPeripheralDeepSleepEnable( + resourceDB[PowerCC26XX_PERIPH_UDMA].driverlibID); + } + + /* 18. Make sure clock settings take effect */ + PRCMLoadSet(); + + /* 19. Release request for uLDO */ + PRCMMcuUldoConfigure(false); + + /* 20. Set transition state to EXITING_SLEEP */ + PowerCC26XX_module.state = Power_EXITING_SLEEP; + + /* 21. Wait until all power domains are back on */ + while (PRCMPowerDomainStatus(poweredDomains) != + PRCM_DOMAIN_POWER_ON) { + ; + } + + /* 22. Wait for the RTC shadow values to be updated so that + * the early notification callbacks can read out valid RTC values + */ + SysCtrlAonSync(); + + /* + * 23. Signal clients registered for early post-sleep notification; + * this should be used to initialize any timing critical or IO + * dependent hardware + */ + notifyStatus = notify(postEvent); + + /* 24. Disable IO freeze and ensure RTC shadow value is updated */ + AONIOCFreezeDisable(); + SysCtrlAonSync(); + + /* 25. Wait for AUX to power up */ + while(!(AONWUCPowerStatusGet() & AONWUC_AUX_POWER_ON)) {}; + + /* 26. If XOSC_HF was forced off above, initiate switch back */ + if (xosc_hf_active == true) { + configureXOSCHF(PowerCC26XX_ENABLE); + } + + /* 27. Re-enable interrupts */ + CPUcpsie(); + + /* + * 28. Signal all clients registered for late post-sleep + * notification + */ + lateNotifyStatus = notify(postEventLate); + + /* + * 29. Now clear the transition state before re-enabling + * scheduler + */ + PowerCC26XX_module.state = Power_ACTIVE; + + /* 30. Re-enable Swi scheduling */ + SwiP_restore(swiKey); + + /* 31. Adjust recharge parameters */ + SysCtrlAdjustRechargeAfterPowerDown(PowerCC26XX_config.vddrRechargeMargin); + + /* re-enable Task scheduling */ + PowerCC26XX_schedulerRestore(); + + /* if there was a notification error, set return status */ + if ((notifyStatus != Power_SOK) || + (lateNotifyStatus != Power_SOK)) { + status = Power_EFAIL; + } + } + } + + return (status); +} + +/* + * ======== Power_unregisterNotify ======== + * Unregister for a power notification. + * + */ +void Power_unregisterNotify(Power_NotifyObj * pNotifyObj) +{ + unsigned int key; + + /* remove notify object from its event queue */ + key = HwiP_disable(); + + /* remove notify object from its event queue */ + List_remove(&PowerCC26XX_module.notifyList, (List_Elem *)pNotifyObj); + + HwiP_restore(key); +} + +/* ****************** CC26XX specific APIs ******************** */ + +/* + * ======== PowerCC26XX_calibrate ======== + * Plug this function into the PowerCC26XX_Config structure + * if calibration is needed. + */ +bool PowerCC26XX_calibrate(unsigned int arg) +{ + bool retVal = false; + ClockP_Params clockParams; + + switch (arg) { + case PowerCC26XX_SETUP_CALIBRATE: + /* + * If RCOSC calibration is enabled, construct a Clock object for + * delays. Set timeout to '1' Clock tick period for the minimal + * delay. The object will explicitly started by Power module when + * appropriate + */ + ClockP_Params_init(&clockParams); + clockParams.period = 0; + clockParams.startFlag = false; + clockParams.arg = 0; + ClockP_construct(&PowerCC26XX_module.calClockStruct, + &PowerCC26XX_RCOSC_clockFunc, 1, &clockParams); + + /* construct the Hwi */ + HwiP_construct(&PowerCC26XX_module.hwiStruct, + 44, PowerCC26XX_auxISR, NULL); + + break; + + case PowerCC26XX_INITIATE_CALIBRATE: + retVal = PowerCC26XX_initiateCalibration(); + break; + + case PowerCC26XX_DO_CALIBRATE: + PowerCC26XX_doCalibrate(); + break; + } + + return (retVal); +} + +/* + * ======== PowerCC26XX_doWFI ======== + */ +void PowerCC26XX_doWFI(void) +{ + __asm(" wfi"); +} + +/* + * ======== PowerCC26XX_getClockHandle ======== + */ +ClockP_Handle PowerCC26XX_getClockHandle() +{ + return ((ClockP_Handle)&PowerCC26XX_module.clockObj); +} + +/* + * ======== PowerCC26XX_noCalibrate ======== + * Plug this function into the PowerCC26XX config structure if calibration + * is not needed. + */ +bool PowerCC26XX_noCalibrate(unsigned int arg) +{ + return (0); +} + +/* + * ======== PowerCC26XX_getXoscStartupTime ======== + * Get the estimated crystal oscillator startup time + */ +uint32_t PowerCC26XX_getXoscStartupTime(uint32_t timeUntilWakeupInMs) +{ + return (OSCHF_GetStartupTime(timeUntilWakeupInMs)); +} + +/* + * ======== PowerCC26XX_injectCalibration ======== + * Explicitly trigger RCOSC calibration + */ +bool PowerCC26XX_injectCalibration(void) +{ + if ((*(PowerCC26XX_config.calibrateFxn))(PowerCC26XX_INITIATE_CALIBRATE)) { + /* here if AUX SMPH was available, start calibration now ... */ + (*(PowerCC26XX_config.calibrateFxn))(PowerCC26XX_DO_CALIBRATE); + return (true); + } + + return (false); +} + +/* + * ======== PowerCC26XX_isStableXOSC_HF ======== + * Check if XOSC_HF has stabilized. + */ +bool PowerCC26XX_isStableXOSC_HF(void) +{ + bool ready = true; + unsigned int key; + + key = HwiP_disable(); + + /* only query if HF source is ready if there is a pending change */ + if (PowerCC26XX_module.xoscPending) { + ready = OSCHfSourceReady(); + } + + HwiP_restore(key); + + return (ready); +} + +/* + * ======== PowerCC26XX_switchXOSC_HF ======== + * Switch to enable XOSC_HF. + * May only be called when using the PowerCC26XX_SWITCH_XOSC_HF_MANUALLY + * constraint. + * May only be called after ensuring the XOSC_HF is stable by calling + * PowerCC26XX_isStableXOSC_HF(). + */ +void PowerCC26XX_switchXOSC_HF(void) +{ + /* This function is just a veneer to call the static callback function for + * the XOSC_HF clock. This way, if the switching does fail because a constraint + * stopped it from switching, a clock will be scheduled into the future to try + * again. This could happen if there is an ongoing operation from another bus + * master that reads from flash such as SPI or AES DMA operations. + */ + switchXOSCHFclockFunc((uintptr_t) NULL); +} + +/* * * * * * * * * * * internal and support functions * * * * * * * * * * */ + +/* + * ======== emptyClockFunc ======== + * Clock function used by power policy to schedule early wakeups. + */ +static void emptyClockFunc(uintptr_t arg) +{ +} + +/* + * ======== disableLfClkQualifiersEnableClkLoss ======== + * Function used to disable LF clock qualifiers and enable clock loss + */ +static void disableLfClkQualifiersEnableClkLoss() +{ + /* Disable the LF clock qualifiers */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_BYPASS_XOSC_LF_CLK_QUAL_M | + DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_M, + DDI_0_OSC_CTL0_BYPASS_RCOSC_LF_CLK_QUAL_S, 0x3); + + /* Enable clock loss detection */ + OSCClockLossEventEnable(); +} + +/* + * ======== lfClockReadyCallback ======== + * Clock function callback used to check if the LF clock is ready + */ +static void lfClockReadyCallback(uintptr_t arg) +{ + uint32_t ccfgLfClkSrc; + uint32_t sourceLF; + uint32_t timeout; + + /* query LF clock source */ + sourceLF = OSCClockSourceGet(OSC_SRC_CLK_LF); + + /* is LF source either RCOSC_LF or XOSC_LF yet? */ + if ((sourceLF == OSC_RCOSC_LF) || (sourceLF == OSC_XOSC_LF)) { + + /* Disable clock qualifiers and enable clock loss */ + disableLfClkQualifiersEnableClkLoss(); + + /* now finish by releasing the standby disallow constraint */ + Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY); + } + /* is LF source XOSC_HF yet? */ + else if(sourceLF == OSC_XOSC_HF) + { + /* Disable clock qualifiers and enable clock loss */ + disableLfClkQualifiersEnableClkLoss(); + + /* Keep PowerCC26XX_DISALLOW_STANDBY set, not allowed to enter standby + * when LF clock is sourced from from XOSC_HF + */ + } + + /* not yet, LF still derived from RCOSC_HF, restart clock to check back later */ + else { + + /* read the LF clock source from CCFG */ + ccfgLfClkSrc = CCFGRead_SCLK_LF_OPTION(); + + /* determine retry timeout */ + if (ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_RCOSC_LF) { + timeout = PowerCC26XX_RETRYWAITRCOSC_LF; + } + else if(ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_XOSC_LF){ + timeout = PowerCC26XX_RETRYWAITXOSC_LF; + } + else { + /* ccfgLfClkSrc == CCFGREAD_SCLK_LF_OPTION_XOSC_HF_DLF */ + timeout = PowerCC26XX_RETRYWAITXOSC_HF; + } + /* retrigger LF Clock to fire again */ + ClockP_setTimeout(ClockP_handle(&PowerCC26XX_module.lfClockObj), + (timeout / ClockP_tickPeriod)); + ClockP_start(ClockP_handle(&PowerCC26XX_module.lfClockObj)); + } +} + +/* + * ======== nopResourceFunc ======== + * special resource handler + */ +static unsigned int nopResourceHandler(unsigned int action) +{ + return (0); +} + +/* + * ======== notify ======== + * Send notifications to registered clients. + * Note: Task scheduling is disabled when this function is called. + */ +static int_fast16_t notify(uint_fast16_t eventType) +{ + int_fast16_t notifyStatus; + Power_NotifyFxn notifyFxn; + uintptr_t clientArg; + List_Elem *elem; + + /* if queue is empty, return immediately */ + if (!List_empty(&PowerCC26XX_module.notifyList)) { + /* point to first client notify object */ + elem = List_head(&PowerCC26XX_module.notifyList); + + /* walk the queue and notify each registered client of the event */ + do { + if (((Power_NotifyObj *)elem)->eventTypes & eventType) { + /* pull params from notify object */ + notifyFxn = ((Power_NotifyObj *)elem)->notifyFxn; + clientArg = ((Power_NotifyObj *)elem)->clientArg; + + /* call the client's notification function */ + notifyStatus = (int_fast16_t)(*(Power_NotifyFxn)notifyFxn)( + eventType, 0, clientArg); + + /* if client declared error stop all further notifications */ + if (notifyStatus != Power_NOTIFYDONE) { + return (Power_EFAIL); + } + } + + /* get next element in the notification queue */ + elem = List_next(elem); + + } while (elem != NULL); + } + + return (Power_SOK); +} + +/* + * ======== configureRFCoreClocks ======== + * Special dependency function for controlling RF core clocks. + */ +static unsigned int configureRFCoreClocks(unsigned int action) +{ + if (action == PowerCC26XX_ENABLE) { + RFCClockEnable(); + } + else { + RFCClockDisable(); + } + + return (0); +} + +/* + * ======== switchXOSCHFclockFunc ======== + * Clock function used for delayed switching to XOSC_HF. + */ +static void switchXOSCHFclockFunc(uintptr_t arg0) +{ + bool readyToCal; + uint32_t timeout; + unsigned int key; + + key = HwiP_disable(); + + /* if pending switch has already been made, just send out notifications */ + if (PowerCC26XX_module.xoscPending == false) { + + /* initiate RCOSC calibration */ + readyToCal = (*(PowerCC26XX_config.calibrateFxn))(PowerCC26XX_INITIATE_CALIBRATE); + + /* notify clients that were waiting for a switch notification */ + notify(PowerCC26XX_XOSC_HF_SWITCHED); + + /* if ready to start first cal measurment, do it now */ + if (readyToCal == true) { + (*(PowerCC26XX_config.calibrateFxn))(PowerCC26XX_DO_CALIBRATE); + } + } + + /* else, if HF ready to switch and we are allowed to, do it now ... */ + else if (!(Power_getConstraintMask() & (1 << PowerCC26XX_DISALLOW_XOSC_HF_SWITCHING)) && OSCHfSourceReady()) { + OSCHF_AttemptToSwitchToXosc(); + + PowerCC26XX_module.xoscPending = false; + + /* initiate RCOSC calibration */ + readyToCal = (*(PowerCC26XX_config.calibrateFxn))(PowerCC26XX_INITIATE_CALIBRATE); + + /* now notify clients that were waiting for a switch notification */ + notify(PowerCC26XX_XOSC_HF_SWITCHED); + + /* if ready to start first cal measurment, do it now */ + if (readyToCal == true) { + (*(PowerCC26XX_config.calibrateFxn))(PowerCC26XX_DO_CALIBRATE); + } + } + + /* else, wait some more, then see if can switch ... */ + else { + /* calculate wait timeout in units of ticks */ + timeout = PowerCC26XX_RETRYWAITXOSC_HF / ClockP_tickPeriod; + if (timeout == 0) { + timeout = 1; /* wait at least 1 tick */ + } + + /* re-start Clock object with retry timeout */ + ClockP_setTimeout( + ClockP_handle(&PowerCC26XX_module.xoscClockObj), timeout); + ClockP_start(ClockP_handle(&PowerCC26XX_module.xoscClockObj)); + } + + HwiP_restore(key); +} + +/* + * ======== configureXOSCHF ======== + */ +static unsigned int configureXOSCHF(unsigned int action) +{ + uint32_t timeout; + + if (action == PowerCC26XX_ENABLE && OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_XOSC_HF) { + OSCHF_TurnOnXosc(); + + PowerCC26XX_module.xoscPending = true; + + /* Unless it is disallowed, estimate the required stabilisation + * time and start a clock. + * When the clock times out, the callback will try and switch to + * the XOSC_HF. If the XOSC_HF is not ready yet, the callback + * will start a new clock to try again. + */ + if (!(Power_getConstraintMask() & (1 << PowerCC26XX_SWITCH_XOSC_HF_MANUALLY))) { + /* calculate wait timeout in units of ticks */ + timeout = PowerCC26XX_INITIALWAITXOSC_HF / ClockP_tickPeriod; + if (timeout == 0) { + timeout = 1; /* wait at least 1 tick */ + } + + /* start Clock object with initial timeout */ + ClockP_stop(ClockP_handle(&PowerCC26XX_module.xoscClockObj)); + ClockP_setTimeout(ClockP_handle(&PowerCC26XX_module.xoscClockObj), + timeout); + ClockP_start(ClockP_handle(&PowerCC26XX_module.xoscClockObj)); + } + } + + /* when release XOSC_HF, auto switch to RCOSC_HF */ + else { + OSCHF_SwitchToRcOscTurnOffXosc(); + } + return (0); +} diff --git a/simplelink/source/ti/drivers/power/PowerCC26XX.h b/simplelink/source/ti/drivers/power/PowerCC26XX.h new file mode 100644 index 0000000..da601fc --- /dev/null +++ b/simplelink/source/ti/drivers/power/PowerCC26XX.h @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2015-2018, Texas Instruments Incorporated + * 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 Texas Instruments Incorporated 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 THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQueueNTIAL 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. + */ +/** ============================================================================ + * @file PowerCC26XX.h + * + * @brief Power manager interface for CC26XX/CC13XX + * + * The Power header file should be included in an application as follows: + * @code + * #include + * #include + * @endcode + * + * Refer to @ref Power.h for a complete description of APIs. + * + * ## Implementation # + * This header file defines the power resources, constraints, events, sleep + * states and transition latencies for CC26XX/CC13XX. + * + * ============================================================================ + */ + +#ifndef ti_drivers_power_PowerCC26XX_ +#define ti_drivers_power_PowerCC26XX_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* \cond */ +typedef uint8_t PowerCC26XX_Resource; /* Resource identifier */ +/* \endcond */ + +/*! The latency to reserve for resume from STANDBY (usec). */ +#define PowerCC26XX_RESUMETIMESTANDBY 750 + +/*! The total latency to reserve for entry to and exit from STANDBY (usec). */ +#define PowerCC26XX_TOTALTIMESTANDBY 1000 + +/*! The initial delay when waking from STANDBY (usec). */ +#define PowerCC26XX_WAKEDELAYSTANDBY 240 + +/*! The initial wait time (usec) before checking if RCOSC_LF is stable. */ +#define PowerCC26XX_INITIALWAITRCOSC_LF 1000 + +/*! The retry wait time (usec) when checking to see if RCOSC_LF is stable. */ +#define PowerCC26XX_RETRYWAITRCOSC_LF 1000 + +/*! The initial wait time (usec) before checking if XOSC_HF is stable. */ +#define PowerCC26XX_INITIALWAITXOSC_HF 50 + +/*! The retry wait time (usec) when checking to see if XOSC_HF is stable. */ +#define PowerCC26XX_RETRYWAITXOSC_HF 50 + +/*! The initial wait time (usec) before checking if XOSC_LF is stable. */ +#define PowerCC26XX_INITIALWAITXOSC_LF 10000 + +/*! The retry wait time (usec) when checking to see if XOSC_LF is stable. */ +#define PowerCC26XX_RETRYWAITXOSC_LF 5000 + +/* resource IDs */ +#define PowerCC26XX_PERIPH_GPT0 0 +/*!< Resource ID: General Purpose Timer 0 */ + +#define PowerCC26XX_PERIPH_GPT1 1 +/*!< Resource ID: General Purpose Timer 1 */ + +#define PowerCC26XX_PERIPH_GPT2 2 +/*!< Resource ID: General Purpose Timer 2 */ + +#define PowerCC26XX_PERIPH_GPT3 3 +/*!< Resource ID: General Purpose Timer 3 */ + +#define PowerCC26XX_PERIPH_SSI0 4 +/*!< Resource ID: Synchronous Serial Interface 0 */ + +#define PowerCC26XX_PERIPH_SSI1 5 +/*!< Resource ID: Synchronous Serial Interface 1 */ + +#define PowerCC26XX_PERIPH_UART0 6 /*!< Resource ID: UART 0 */ + +#define PowerCC26XX_PERIPH_I2C0 7 /*!< Resource ID: I2C 0 */ + +#define PowerCC26XX_PERIPH_TRNG 8 +/*!< Resource ID: True Random Number Generator */ + +#define PowerCC26XX_PERIPH_GPIO 9 /*!< Resource ID: General Purpose I/Os */ + +#define PowerCC26XX_PERIPH_UDMA 10 /*!< Resource ID: uDMA Controller */ + +#define PowerCC26XX_PERIPH_CRYPTO 11 /*!< Resource ID: AES Security Module */ + +#define PowerCC26XX_PERIPH_I2S 12 /*!< Resource ID: I2S */ + +#define PowerCC26XX_PERIPH_RFCORE 13 /*!< Resource ID: RF Core Module */ + +#define PowerCC26XX_XOSC_HF 14 +/*!< Resource ID: High Frequency Crystal Oscillator */ + +#define PowerCC26XX_DOMAIN_PERIPH 15 +/*!< Resource ID: Peripheral Power Domain */ + +#define PowerCC26XX_DOMAIN_SERIAL 16 +/*!< Resource ID: Serial Power Domain */ + +#define PowerCC26XX_DOMAIN_RFCORE 17 +/*!< Resource ID: RF Core Power Domain */ + +#define PowerCC26XX_DOMAIN_SYSBUS 18 +/*!< Resource ID: System Bus Power Domain */ + +/* \cond */ +#define PowerCC26XX_NUMRESOURCES 19 /* Number of resources in database */ +/* \endcond */ + +/* \cond */ +/* resource record bitmasks */ +#define PowerCC26XX_PERIPH 0x80 /* resource is a peripheral */ +#define PowerCC26XX_SPECIAL 0x40 /* resource requires special handler */ +#define PowerCC26XX_DOMAIN 0x00 /* resource is a domain */ +#define PowerCC26XX_PARENTMASK 0x3F /* parent resource mask */ +#define PowerCC26XX_NOPARENT 0x3F /* if resource has no parent */ +/* \endcond */ + +#define PowerCC26XX_STANDBY 0x1 /*!< The STANDBY sleep state */ +/* \cond */ +/* internal flags for enabling/disabling resources */ +#define PowerCC26XX_ENABLE 1 +#define PowerCC26XX_DISABLE 0 +/* \endcond */ + +/* constraints */ +#define PowerCC26XX_RETAIN_VIMS_CACHE_IN_STANDBY 0 +/*!< Constraint: VIMS RAM must be retained while in STANDBY */ + +#define PowerCC26XX_DISALLOW_SHUTDOWN 1 +/*!< Constraint: Disallow a transition to the SHUTDOWN state */ + +#define PowerCC26XX_DISALLOW_STANDBY 2 +/*!< Constraint: Disallow a transition to the STANDBY sleep state */ + +#define PowerCC26XX_DISALLOW_IDLE 3 +/*!< Constraint: Disallow a transition to the IDLE sleep state */ + +#define PowerCC26XX_NEED_FLASH_IN_IDLE 4 +/*!< Constraint: Flash memory needs to enabled during IDLE */ + +#define PowerCC26XX_SWITCH_XOSC_HF_MANUALLY 5 +/*!< Constraint: Prevent power driver from starting an RTOS clock and + * automatically switching to the XOSC_HF when it is ready. The power + * driver will turn on the XOSC_HF and return control to the application. + * The application must poll the status of the XOSC_HF and make sure that it + * is stable before manually switching to it. + * If the constraint is released before the application has switched to the + * XOSC_HF, the application is still responsible for switching to the + * XOSC_HF. + * Failing to do so may cause an undefined internal state in the power + * driver. + */ + +#define PowerCC26XX_DISALLOW_XOSC_HF_SWITCHING 6 +/*!< Constraint: Prevent power driver from switching to XOSC_HF when the crystal is + * ready. The RTOS clock will be rescheduled to try again in the future. + * This is a workaround to prevent the flash from being accessed by a bus master + * other than the CPU while switching to XOSC_HF. This would cause a bus stall. + * This functionality is only implemented on CC26X0, CC26X0R2, and CC13X0 as the + * bug was fixed in hardware on later devices. + */ + +/* \cond */ +#define PowerCC26XX_NUMCONSTRAINTS 7 /* Number of constraints supported */ +/* \endcond */ + +/* \cond */ +/* Deprecated constraint names */ +#define PowerCC26XX_SD_DISALLOW PowerCC26XX_DISALLOW_SHUTDOWN +#define PowerCC26XX_SB_DISALLOW PowerCC26XX_DISALLOW_STANDBY +#define PowerCC26XX_IDLE_PD_DISALLOW PowerCC26XX_DISALLOW_IDLE +#define PowerCC26XX_XOSC_HF_SWITCHING_DISALLOW PowerCC26XX_DISALLOW_XOSC_HF_SWITCHING +#define PowerCC26XX_SB_VIMS_CACHE_RETAIN PowerCC26XX_RETAIN_VIMS_CACHE_IN_STANDBY +/* \endcond */ + +/* + * Events + * + * Each event must be a power of two and must be sequential + * without any gaps. + */ +#define PowerCC26XX_ENTERING_STANDBY 0x1 +/*!< Power event: The device is entering the STANDBY sleep state */ + +#define PowerCC26XX_ENTERING_SHUTDOWN 0x2 +/*!< Power event: The device is entering the SHUTDOWN state */ + +#define PowerCC26XX_AWAKE_STANDBY 0x4 +/*!< Power event: The device is waking up from the STANDBY sleep state */ + +#define PowerCC26XX_AWAKE_STANDBY_LATE 0x8 +/*!< Power event: The device is waking up from STANDBY (this event is sent later during wakeup, after interrupts are re-enabled) */ + +#define PowerCC26XX_XOSC_HF_SWITCHED 0x10 +/*!< Power event: The high frequency (HF) clock source has been switched to XOSC_HF */ + +#define PowerCC26XX_JTAG_PD_TURNED_ON 0x20 +/*!< \warning Note that this power event is only supported by the CC2640R2 device! + * + * The JTAG subsystem on the CC26xx devices is automatically enabled after receiving + * 8 pulses on the TCK pin. This will cause the device to draw more power in all + * power modes (Active, Idle, Standby, Shutdown). + * The ::PowerCC26XX_JTAG_PD_TURNED_ON power event will + * let you know when this has happened outside of a debug session due to noise on the pin. + * This allows the application to do a reset of the device when it's convenient in order + * disable the JTAG subsystem and conserve power. + * + * In order to turn off the JTAG_PD the application should subscribe to this event. + * In the callback function the application can call Power_shutdown() and + * this will force a reset of the device. + * Alternatively the the callback function can post another event so that the application can + * reset the device when it's more convenient to do so. + * + * When Power_shutdown() is called when the JTAG subsystem is on, + * the device will reset and branch to the flash image again, + * only now with the JTAG_PD turned off, thus the excess power is gone. + * The wakeup source as read through the SysCtrlResetSourceGet() will in this case + * return RSTSRC_WAKEUP_FROM_SHUTDOWN. + * + * The power driver will, each time before entering standby, check to see if the + * JTAG_PD has been turned on after boot. If so, it will notify all subscribers to the + * ::PowerCC26XX_JTAG_PD_TURNED_ON event. + * If the JTAG_PD was turned on during boot, which is the case when + * using the debugger, the notification will NOT be sent even if the event is registered. + * This is because when actively developing code with an IDE and emulator, the user typically + * wants to be able to debug their code through standby without the device resetting. + * + * Summary of when the ::PowerCC26XX_JTAG_PD_TURNED_ON notification function will be called. + * + * | JTAG_PD state | Notification function registered | Notification function called | + * |----------------------|----------------------------------|------------------------ + * | Off | Don't care | No + * | Turned on during boot| Don't care | No + * | Turned on after boot | No | No + * | Turned on after boot | Yes | Yes + * + * \warning If the ::PowerCC26XX_JTAG_PD_TURNED_ON event is registered, and the notification + * callback function calls Power_shutdown() it will not be possible to attach + * an emulator to a running target. This is becasue the device will reset as soon as the + * emulator turns on the JTAG_PD as part of the connect sequence. + * + * Code snippet on how to register the notification and the callback function: + * @code + * void jtagPdTurnedOnCallbackFxn() + * { + * // Optionally save any critical application information + * // gracefullyShutdownApplication(); + * // Call shutdown, this will reset device, and the application will reboot with JTAG_PD off. + * Power_shutdown(NULL, NULL); + * // Power_shutdown(...) should never return, device will reset. + * } + * + * void taskFxn(UArg a0, UArg a1) + * { + * ... + * // Register "JTAG power domain turned on" notification function + * // Everytime the device is about to enter standby, the power driver will check + * // to see if the JTAG_PD has been turned on after boot. If so, the notification + * // function will be called before entering standby... + * Power_registerNotify(&jtagPdTurnedOnNotifyObj, PowerCC26XX_JTAG_PD_TURNED_ON, (Fxn)jtagPdTurnedOnCallbackFxn, NULL); + * ... + * } + * @endcode + */ + + +/* \cond */ +#define PowerCC26XX_NUMEVENTS 6 /* Number of events supported */ +/* \endcond */ + +/* \cond */ +/* + * Calibration stages + */ +#define PowerCC26XX_SETUP_CALIBRATE 1 +#define PowerCC26XX_INITIATE_CALIBRATE 2 +#define PowerCC26XX_DO_CALIBRATE 3 +/* \endcond */ + +/* \cond */ +/*! @brief Power resource database record format */ +typedef struct PowerCC26XX_ResourceRecord { + uint8_t flags; /* resource type | first parent */ + uint16_t driverlibID; /* corresponding driverlib ID for this resource */ +} PowerCC26XX_ResourceRecord; +/* \endcond */ + +/*! @brief Global configuration structure */ +typedef struct PowerCC26XX_Config { + /*! + * @brief The Power Policy's initialization function + * + * If the policy does not have an initialization function, 'NULL' + * should be specified. + */ + Power_PolicyInitFxn policyInitFxn; + /*! + * @brief The Power Policy function + * + * When enabled, this function is invoked in the idle loop, to + * opportunistically select and activate sleep states. + * + * Two reference policies are provided: + * + * PowerCC26XX_doWFI() - a simple policy that invokes CPU wait for + * interrupt (WFI) + * + * PowerCC26XX_standbyPolicy() - an agressive policy that considers + * constraints, time until next scheduled work, and sleep state + * latencies, and optionally puts the device into the STANDBY state, + * the IDLE state, or as a minimum, WFI. + * + * Custom policies can be written, and specified via this function pointer. + * + * In addition to this static selection, the Power Policy can be + * dynamically changed at runtime, via the Power_setPolicy() API. + */ + Power_PolicyFxn policyFxn; + /*! + * @brief The function to be used for activating RC Oscillator (RCOSC) + * calibration + * + * Calibration is normally enabled, via specification of the function + * PowerCC26XX_calibrate(). This enables high accuracy operation, and + * faster high frequency crystal oscillator (XOSC_HF) startups. + * + * To disable RCOSC calibration, the function PowerCC26XX_noCalibrate() + * should be specified. + */ + bool (*calibrateFxn)(unsigned int); + /*! + * @brief Time in system ticks that specifies the maximum duration the device + * may spend in standby. + * + * When the power driver tries to put the device into standby and determines + * the next wakeup should usually be further into the future than + * maxStandbyDuration system ticks, the power driver will schedule a wakeup + * maxStandbyDuration into the future. When the device wakes up after + * being in standby for maxStandbyDuration ticks, the power driver will + * repeat this process and go back into standby if the state of the system + * allows it. + * + * Inserting such periodic wakeups can be used to automatically calibrate + * the RCOSC with a maximum period between calibrations or to force the + * recalculation of the initial VDDR recharge period. This assumes that + * the constraint to prohibit standby is not set and that periods of + * inactivity are long enough for the power driver to put the device + * into standby. + * + * The value 0 is invalid. When PowerCC26XX_Config.enableMaxStandbyDuration is + * set to false, any value (including 0) is ignored and the feature is + * disabled. + * This feature should not be used to disallow entering standby; + * the PowerCC26XX_DISALLOW_STANDBY constraint should be used for + * this purpose. + */ + uint32_t maxStandbyDuration; + /*! + * @brief Margin in SCLK_LF periods subtracted from previous longest + * VDDR recharge period. + * + * As the device comes out of standby, it updated its previous initial + * VDDR recharge period to be closer to the longest recharge period + * experienced during the time spent in standby before waking up. + * + * vddrRechargeMargin is subtracted from the longest VDDR recharge + * period in SysCtrlAdjustRechargeAfterPowerDown to ensure there is + * some margin between the new initial and converged VDDR recharge + * period. The converged recharge period at a certain temperature + * is board and device dependent. + * + * The default value of 0 disables this feature. + */ + uint16_t vddrRechargeMargin; + /*! + * @brief Boolean that enables limiting the duration spent in standby + * + * If false, the power driver will put the device into standby as + * appropriate without duration restrictions. + * + * If true, the the power driver will force a wakeup every + * PowerCC26XX_Config.maxStandbyDuration system ticks before reevaluating + * the state of the system. + * + * This is set to false by default. + */ + bool enableMaxStandbyDuration; + /*! + * @brief Boolean specifying if the Power Policy function is enabled + * + * If 'true', the policy function will be invoked once for each pass + * of the idle loop. + * + * If 'false', the policy will not be invoked. + * + * In addition to this static setting, the power policy can be dynamically + * enabled and disabled at runtime, via the Power_enablePolicy() and + * Power_disablePolicy() functions, respectively. + */ + bool enablePolicy; + /*! + * @brief Boolean specifying whether the low frequency RC oscillator + * (RCOSC_LF) should be calibrated. + * + * If RCOSC calibration is enabled (above, via specification of + * an appropriate calibrateFxn), this Boolean specifies whether + * RCOSC_LF should be calibrated. + */ + bool calibrateRCOSC_LF; + /*! + * @brief Boolean specifying whether the high frequency RC oscillator + * (RCOSC_HF) should be calibrated. + * + * If RCOSC calibration is enabled (above, via specification of + * an appropriate calibrateFxn), this Boolean specifies whether + * RCOSC_HF should be calibrated. + */ + bool calibrateRCOSC_HF; +} PowerCC26XX_Config; + +/*! + * @brief PowerCC26XX_ModuleState + * + * Power manager state structure. The application must not access any members + * of this structure! + */ +typedef struct PowerCC26XX_ModuleState { + List_List notifyList; /*!< Event notification list */ + uint32_t constraintMask; /*!< Aggregate constraints mask */ + ClockP_Struct clockObj; /*!< Clock object for scheduling wakeups */ + ClockP_Struct xoscClockObj; /*!< Clock object for XOSC_HF switching */ + ClockP_Struct lfClockObj; /*!< Clock object for LF clock checking */ + ClockP_Struct calClockStruct; /*!< Clock object for RCOSC calibration */ + HwiP_Struct hwiStruct; /*!< Hwi object for RCOSC calibration */ + int32_t nDeltaFreqCurr; /*!< RCOSC calibration variable */ + int32_t nCtrimCurr; /*!< RCOSC calibration variable */ + int32_t nCtrimFractCurr; /*!< RCOSC calibration variable */ + int32_t nCtrimNew; /*!< RCOSC calibration variable */ + int32_t nCtrimFractNew; /*!< RCOSC calibration variable */ + int32_t nRtrimNew; /*!< RCOSC calibration variable */ + int32_t nRtrimCurr; /*!< RCOSC calibration variable */ + int32_t nDeltaFreqNew; /*!< RCOSC calibration variable */ + bool bRefine; /*!< RCOSC calibration variable */ + uint32_t state; /*!< Current transition state */ + bool xoscPending; /*!< Is XOSC_HF activation in progress? */ + bool calLF; /*!< Calibrate RCOSC_LF? */ + uint8_t hwiState; /*!< The AUX ISR calibration state */ + bool busyCal; /*!< Already busy calibrating? */ + uint8_t calStep; /*!< The current calibration step */ + bool firstLF; /*!< Is this the first LF calibration? */ + bool enablePolicy; /*!< Is the Power policy enabled? */ + bool initialized; /*!< Has Power_init() been called? */ +#if defined(DeviceFamily_CC26X0R2) + bool emulatorAttached; /*!< Was an emulator detected during boot? */ +#endif + uint8_t constraintCounts[PowerCC26XX_NUMCONSTRAINTS]; + /*!< Array to maintain constraint reference counts */ + uint8_t resourceCounts[PowerCC26XX_NUMRESOURCES]; + /*!< Array to maintain resource dependency reference counts */ + unsigned int (*resourceHandlers[3])(unsigned int); + /*!< Array of special dependency handler functions */ + Power_PolicyFxn policyFxn; /*!< The Power policy function */ +} PowerCC26XX_ModuleState; + +/*! + * @brief The RC Oscillator (RCOSC) calibration function + * + * The function to be used for performing RCOSC calibation. This is the + * default calibration function, and is specified via the calibrateFxn + * pointer in the PowerCC26XX_Config structure. + * + * @param arg used internally + * + * @return used internally + */ +bool PowerCC26XX_calibrate(unsigned int arg); + +/*! + * @brief The Wait for interrupt (WFI) policy + * + * This is a lightweight Power Policy which simply invokes CPU wait for + * interrupt. + * + * This policy can be selected statically via the policyFxn pointer in the + * PowerCC26XX_Config structure, or dynamically at runtime, via + * Power_setPolicy(). + */ +void PowerCC26XX_doWFI(void); + +/*! + * @brief Get the handle of the Clock object used for scheduling device + * wakeups + * + * During initialization, the Power Manager creates a Clock object that a + * Power Policy can use to schedule device wakeups. This function can + * be called by a policy function to get the handle of this pre-allocated + * Clock object. + * + * @return The handle of the Clock object + */ +ClockP_Handle PowerCC26XX_getClockHandle(void); + +/*! + * @brief Get the estimated HF crystal oscillator (XOSC_HF) startup delay, + * for a given delay from now, until startup is initiated + * + * @param timeUntilWakeupInMs The estimated time until the next wakeup + * event, in units of milliseconds + * + * @return The estimated HF crystal oscillator startup latency, in + * units of microseconds. + */ +uint32_t PowerCC26XX_getXoscStartupTime(uint32_t timeUntilWakeupInMs); + +/*! + * @brief Explicitly trigger RC oscillator calibration + * + * When enabled, RCOSC calibration is normally triggered upon each device + * wakeup from STANDBY. To trigger more frequent calibration, an application + * can explicitly call this function, to initiate an immediate calibration + * cycle. + * + * @return true if calibration was actually initiated otherwise false + */ +bool PowerCC26XX_injectCalibration(void); + +/*! + * @brief Function to specify when RCOSC calibration is to be disabled + * + * This function should be specified as the 'calibrateFxn' in the + * PowerCC26XX_Config structure when RCOSC calibration is to be disabled. + * + * Note that the reason a function pointer is used here (versus a simple + * Boolean) is so that references to internal calibration subroutines can be + * removed, to eliminate pulling the calibration code into the application + * image; this enables a significant reduction in memory footprint when + * calibration is disabled. + * + * @param arg used internally + * + * @return used internally + */ +bool PowerCC26XX_noCalibrate(unsigned int arg); + +/*! + * @brief Check if the XOSC_HF is stable and ready to be switched to + * + * @pre Set PowerCC26XX_SWITCH_XOSC_HF_MANUALLY in the early standby + * wakeup notification. + * + * This function should be called when using the + * PowerCC26XX_SWITCH_XOSC_HF_MANUALLY power constraint to ensure that + * the XOSC_HF is stable before switching to it. + * + * \sa PowerCC26XX_switchXOSC_HF() + */ +bool PowerCC26XX_isStableXOSC_HF(void); + +/*! + * @brief Switch the HF clock source to XOSC_HF + * + * @pre PowerCC26XX_switchXOSC_HF() returns true. + * + * This function should only be called when using the + * PowerCC26XX_SWITCH_XOSC_HF_MANUALLY power constraint after ensuring + * the XOSC_HF is stable. + * If the driver cannot switch to the XOSC_HF despite the crystal being + * stable, a clock will be scheduled in the future and the callback will + * try to switch again. + * + * \sa PowerCC26XX_isStableXOSC_HF() + */ +void PowerCC26XX_switchXOSC_HF(void); + +/*! + * @brief The STANDBY Power Policy + * + * This is an agressive Power Policy, which considers active constraints, + * sleep state transition latencies, and time until the next scheduled + * work, and automatically transitions the device into the deepest sleep state + * possible. + * + * The first goal is to enter STANDBY; if that is not appropriate + * given current conditions (e.g., the sleep transition latency is greater + * greater than the time until the next scheduled Clock event), then + * the secondary goal is the IDLE state; if that is disallowed (e.g., if + * the PowerCC26XX_DISALLOW_IDLE constraint is declared), then the policy + * will fallback and simply invoke WFI, to clock gate the CPU until the next + * interrupt. + * + * In order for this policy to run, it must be selected as the Power + * Policy (either by being specified as the 'policyFxn' in the + * PowerCC26XX_Config structure, or specified at runtime with + * Power_setPolicy()), and the Power Policy must be enabled (either via + * 'enablePolicy' in the PowerCC26XX_Config structure, or via a call to + * Power_enablePolicy() at runtime). + */ +void PowerCC26XX_standbyPolicy(void); + +void PowerCC26XX_schedulerDisable(void); +void PowerCC26XX_schedulerRestore(void); + +#define Power_getPerformanceLevel(void) 0 +#define Power_setPerformanceLevel(level) Power_EFAIL + +#ifdef __cplusplus +} +#endif + +#endif /* POWER_CC26XX_ */ diff --git a/simplelink/source/ti/drivers/power/PowerCC26XX_calibrateRCOSC.c b/simplelink/source/ti/drivers/power/PowerCC26XX_calibrateRCOSC.c new file mode 100644 index 0000000..3ec874e --- /dev/null +++ b/simplelink/source/ti/drivers/power/PowerCC26XX_calibrateRCOSC.c @@ -0,0 +1,820 @@ +/* + * Copyright (c) 2015-2018, Texas Instruments Incorporated + * 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 Texas Instruments Incorporated 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 THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQueueNTIAL 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. + */ +/* + * ======== PowerCC26XX_calibrateRCOSC.c ======== + */ + +#include + +#include +#include + +#include +#include + +#include +#include DeviceFamily_constructPath(inc/hw_aux_evctl.h) +#include DeviceFamily_constructPath(inc/hw_aux_smph.h) +#include DeviceFamily_constructPath(inc/hw_aux_wuc.h) +#include DeviceFamily_constructPath(inc/hw_aux_tdc.h) +#include DeviceFamily_constructPath(inc/hw_ddi_0_osc.h) +#include DeviceFamily_constructPath(inc/hw_ddi.h) +#include DeviceFamily_constructPath(inc/hw_ccfg.h) +#include DeviceFamily_constructPath(driverlib/aon_batmon.h) +#include DeviceFamily_constructPath(driverlib/ddi.h) +#include DeviceFamily_constructPath(driverlib/ioc.h) +#include DeviceFamily_constructPath(driverlib/osc.h) +#include DeviceFamily_constructPath(driverlib/gpio.h) +#include DeviceFamily_constructPath(driverlib/sys_ctrl.h) +#include DeviceFamily_constructPath(driverlib/aux_wuc.h) + +#define AUX_TDC_SEMAPHORE_NUMBER 1 /* semaphore 1 protects TDC */ +#define NUM_RCOSC_LF_PERIODS_TO_MEASURE 32 /* x RCOSC_LF periods vs XOSC_HF */ +#define NUM_RCOSC_HF_PERIODS_TO_MEASURE 1 /* x RCOSC_HF periods vs XOSC_HF */ +#define ACLK_REF_SRC_RCOSC_HF 0 /* Use RCOSC_HF for ACLK REF */ +#define ACLK_REF_SRC_RCOSC_LF 2 /* Use RCOSC_LF for ACLK REF */ +#define SCLK_LF_OPTION_RCOSC_LF 3 /* defined in cc26_ccfg.xls */ +#define RCOSC_HF_LOW_THRESHOLD_TDC_VALUE 1535 /* If TDC value is within threshold range, no need for another TDC measurement */ +#define RCOSC_HF_PERFECT_TDC_VALUE 1536 /* RCOSC_HF runs at perfect 48 MHz when ending up with this TDC value */ +#define RCOSC_HF_HIGH_THRESHOLD_TDC_VALUE 1537 /* If TDC value is within threshold range, no need for another TDC measurement */ + +#define DDI_0_OSC_O_CTL1_LOCAL 0x00000004 /* offset */ +#define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M 0x007C0000 /* mask */ +#define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S 18 /* shift */ +#define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_M 0x00020000 /* mask */ +#define DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_S 17 /* shift */ +#define DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M 0x00000C00 /* offset */ +#define DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S 10 /* shift */ + +/* AUX ISR states */ +#define WAIT_SMPH 0 /* just took SMPH, start RCOSC_LF */ +#define CAL_RCOSC_LF 1 /* just finished RCOSC_LF, start first RCOSC_HF */ +#define CAL_RCOSC_HF1 2 /* just finished 1st RCOSC_HF, start 2nd */ +#define CAL_RCOSC_HF2 3 /* just finished 2nd RCOSC_HF, decide best */ + +/* calibration steps */ +#define STEP_TDC_INIT_1 1 +#define STEP_TDC_INIT_2 2 +#define STEP_CAL_LF_1 3 +#define STEP_CAL_LF_2 4 +#define STEP_CAL_LF_3 5 +#define STEP_CAL_HF1_1 6 +#define STEP_CAL_HF1_2 7 +#define STEP_CAL_HF1_3 8 +#define STEP_CAL_HF2_1 9 +#define STEP_CAL_HF2_2 10 +#define STEP_CLEANUP_1 11 +#define STEP_CLEANUP_2 12 + +/* macros */ +#define Min(a,b) (((a)<(b))?(a):(b)) +#define Max(a,b) (((a)>(b))?(a):(b)) +#define Abs(x) ((x) < 0 ? -(x) : (x)) +#define Scale_rndInf(x) ((3 * (x) + (((x) < 0) ? -2 : 2)) / 4) + +#define INSTRUMENT 0 + +#if INSTRUMENT +volatile unsigned int gotSEM = 0; +volatile unsigned int calLFi = 0; +volatile unsigned int calHF1i = 0; +volatile unsigned int calHF2i = 0; +volatile bool doneCal = false; +unsigned int tdcResult_LF = 0; +unsigned int tdcResult_HF1 = 0; +unsigned int tdcResult_HF2 = 0; +unsigned int numISRs = 0; +unsigned int calClocks = 0; +#endif + +/* Forward declarations */ +static bool getTdcSemaphore(); +static void updateSubSecInc(uint32_t tdcResult); +static void calibrateRcoscHf1(int32_t tdcResult); +static void calibrateRcoscHf2(int32_t tdcResult); +void PowerCC26XX_doCalibrate(void); + +/* Externs */ +extern PowerCC26XX_ModuleState PowerCC26XX_module; +extern const PowerCC26XX_Config PowerCC26XX_config; + +/* + * ======== PowerCC26XX_initiateCalibration ======== + * Initiate calibration of RCOSC_LF and RCOSCHF + */ +bool PowerCC26XX_initiateCalibration() +{ + unsigned int hwiKey; + bool busy = false; + bool status; + bool gotSem; + + if ((PowerCC26XX_module.calLF == false) && + (PowerCC26XX_config.calibrateRCOSC_HF == false)) { + return (false); + } + + /* make sure calibration is not already in progress */ + hwiKey = HwiP_disable(); + + if (PowerCC26XX_module.busyCal == false) { + PowerCC26XX_module.busyCal = true; + } + else { + busy = true; + } + + HwiP_restore(hwiKey); + + if (busy == true) { + return (false); + } + +#if INSTRUMENT + gotSEM = 0; + calLFi = 0; + calHF1i = 0; + calHF2i = 0; + doneCal = false; +#endif + + /* set contraint to prohibit standby during calibration sequence */ + Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY); + + /* set dependency to keep XOSC_HF active during calibration sequence */ + Power_setDependency(PowerCC26XX_XOSC_HF); + + /* initiate acquisition of semaphore protecting TDC */ + gotSem = getTdcSemaphore(); + + /* if didn't acquire semaphore, must wait for autotake ISR */ + if (gotSem == false) { + PowerCC26XX_module.hwiState = WAIT_SMPH; + status = false; /* false: don't do anything else until acquire SMPH */ + } + + /* else, semaphore acquired, OK to proceed with first measurement */ + else { +#if INSTRUMENT + gotSEM = 1; +#endif + status = true; /* true: OK to start first measurement */ + } + + return (status); +} + +/* + * ======== PowerCC26XX_auxISR ======== + * ISR for the AUX combo interrupt event. Implements Hwi state machine to + * step through the RCOSC calibration steps. + */ +void PowerCC26XX_auxISR(uintptr_t arg) +{ + uint32_t tdcResult; + +#if INSTRUMENT + numISRs++; +#endif + + /* + * disable all events that are part of AUX_COMBINED_INTERRUPT. + * This interrupt is reserved for use during RCOSC calibration. + * Other AUX perihperals that want to generate interrupts to CM3 + * must use dedicated interrupt lines or go through AON combined. + */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = 0; + + /* ****** state = WAIT_SMPH: arrive here if just took the SMPH ****** */ + if (PowerCC26XX_module.hwiState == WAIT_SMPH) { +#if INSTRUMENT + gotSEM = 1; +#endif + } + + /* **** state = CAL_RCOSC_LF: here when just finished LF counting **** */ + else if (PowerCC26XX_module.hwiState == CAL_RCOSC_LF) { + + tdcResult = HWREG(AUX_TDC_BASE + AUX_TDC_O_RESULT); + +#if INSTRUMENT + tdcResult_LF = tdcResult; +#endif + /* update the RTC SUBSECINC register based on LF measurement result */ + updateSubSecInc(tdcResult); +#if INSTRUMENT + calLFi = 1; +#endif + /* if doing HF calibration initiate it now */ + if (PowerCC26XX_config.calibrateRCOSC_HF) { + PowerCC26XX_module.calStep = STEP_CAL_LF_3; /* next: trigger LF */ + } + + /* else, start cleanup */ + else { + PowerCC26XX_module.calStep = STEP_CLEANUP_1; /* next: cleanup */ + } + } + + /* ****** state = CAL_RCOSC_HF1: here when just finished 1st RCOSC_HF */ + else if (PowerCC26XX_module.hwiState == CAL_RCOSC_HF1) { + + tdcResult = HWREG(AUX_TDC_BASE + AUX_TDC_O_RESULT); + +#if INSTRUMENT + tdcResult_HF1 = tdcResult; + calHF1i = 1; +#endif + + /* use first HF measurement to setup new trim values */ + calibrateRcoscHf1(tdcResult); + + /* if HF setting perfect, nothing more to do, calibration is done */ + if ((tdcResult >= RCOSC_HF_LOW_THRESHOLD_TDC_VALUE) && + (tdcResult <= RCOSC_HF_HIGH_THRESHOLD_TDC_VALUE)) { + PowerCC26XX_module.calStep = STEP_CLEANUP_1; /* next: cleanup */ + } + + /* else, tweak trims, initiate another HF measurement */ + else { + + PowerCC26XX_module.calStep = STEP_CAL_HF1_3; /* next: HF meas. #2 */ + } + } + + /* ****** state = just finished second RCOSC_HF measurement ****** */ + else if (PowerCC26XX_module.hwiState == CAL_RCOSC_HF2) { + + tdcResult = HWREG(AUX_TDC_BASE + AUX_TDC_O_RESULT); + +#if INSTRUMENT + tdcResult_HF2 = tdcResult; +#endif + /* look for improvement on #2, else revert to previous trim values */ + calibrateRcoscHf2(tdcResult); + + PowerCC26XX_module.calStep = STEP_CLEANUP_1; /* next: cleanup */ + } + + /* do the next calibration step... */ + PowerCC26XX_doCalibrate(); +} + +/* + * ======== PowerCC26XX_doCalibrate ======== + */ +void PowerCC26XX_doCalibrate(void) +{ + switch (PowerCC26XX_module.calStep) { + + case STEP_TDC_INIT_1: + + /* turn on clock to TDC module */ + AUXWUCClockEnable(AUX_WUC_TDCIF_CLOCK); + + /* set saturation config to 2^24 */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_SATCFG) = + AUX_TDC_SATCFG_LIMIT_R24; + + /* set start and stop trigger sources and polarity */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGSRC) = + (AUX_TDC_TRIGSRC_STOP_SRC_ACLK_REF | + AUX_TDC_TRIGSRC_STOP_POL_HIGH) | + (AUX_TDC_TRIGSRC_START_SRC_ACLK_REF | + AUX_TDC_TRIGSRC_START_POL_HIGH); + + /* set TDC_SRC clock to be XOSC_HF/2 = 24 MHz */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_M, + DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_S, 2); + + /* read back to ensure no race condition between OSC_DIG and AUX_WUC */ + DDI16BitfieldRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_M, DDI_0_OSC_CTL0_ACLK_TDC_SRC_SEL_S); + + /* set AUX_WUC:TDCCLKCTL.REQ... */ + HWREG(AUX_WUC_BASE + AUX_WUC_O_TDCCLKCTL) = AUX_WUC_TDCCLKCTL_REQ; + + /* set next state */ + PowerCC26XX_module.calStep = STEP_TDC_INIT_2; + + /* start Clock object to delay while wait for ACK */ + ClockP_start(ClockP_handle(&PowerCC26XX_module.calClockStruct)); + + break; + + case STEP_TDC_INIT_2: + + /* Enable trig count */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGCNTCFG) = + AUX_TDC_TRIGCNTCFG_EN; + + /* if LF calibration enabled start LF measurement */ + if (PowerCC26XX_module.calLF) { + + /* clear UPD_REQ, new sub-second increment is NOT available */ + HWREG(AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINCCTL) = 0; + + /* set next Swi state */ + PowerCC26XX_module.calStep = STEP_CAL_LF_1; + } + + /* else, start first HF measurement */ + else { + /* set next Swi state */ + PowerCC26XX_module.calStep = STEP_CAL_HF1_1; + } + + /* abort TDC */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_CTL) = AUX_TDC_CTL_CMD_ABORT; + + /* clear AUX_WUC:REFCLKCTL.REQ... */ + HWREG(AUX_WUC_BASE + AUX_WUC_O_REFCLKCTL) = 0; + + /* if not ready, start Clock object to delay while wait for ACK */ + if (HWREG(AUX_WUC_BASE + AUX_WUC_O_REFCLKCTL) & + AUX_WUC_REFCLKCTL_ACK) { + + /* start Clock object to delay while wait for ACK */ + ClockP_start(ClockP_handle(&PowerCC26XX_module.calClockStruct)); + + break; + } + + /* else, if ready now, fall thru to next step ... */ + + + case STEP_CAL_LF_1: + case STEP_CAL_HF1_1: + case STEP_CAL_HF2_1: + + if (PowerCC26XX_module.calStep == STEP_CAL_LF_1) { + + /* set the ACLK reference clock */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_S, + ACLK_REF_SRC_RCOSC_LF); + + /* set next Swi state */ + PowerCC26XX_module.calStep = STEP_CAL_LF_2; + } + else { + + /* set the ACLK reference clock */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL0, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_M, + DDI_0_OSC_CTL0_ACLK_REF_SRC_SEL_S, + ACLK_REF_SRC_RCOSC_HF); + + /* set next Swi state */ + if (PowerCC26XX_module.calStep == STEP_CAL_HF1_1) { + PowerCC26XX_module.calStep = STEP_CAL_HF1_2; + } + else { + PowerCC26XX_module.calStep = STEP_CAL_HF2_2; + } + } + + /* set AUX_WUC:REFCLKCTL.REQ */ + HWREG(AUX_WUC_BASE + AUX_WUC_O_REFCLKCTL) = AUX_WUC_REFCLKCTL_REQ; + + /* start Clock object to delay while wait for ACK */ + ClockP_start(ClockP_handle(&PowerCC26XX_module.calClockStruct)); + + break; + + case STEP_CAL_LF_2: + case STEP_CAL_HF1_2: + case STEP_CAL_HF2_2: + + if (PowerCC26XX_module.calStep == STEP_CAL_LF_2) { + + /* Set number of periods of ACLK to count */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGCNTLOAD) = + NUM_RCOSC_LF_PERIODS_TO_MEASURE; + + /* set next Hwi state before triggering TDC */ + PowerCC26XX_module.hwiState = CAL_RCOSC_LF; + } + else { + + /* Set number of periods of ACLK to count */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_TRIGCNTLOAD) = + NUM_RCOSC_HF_PERIODS_TO_MEASURE; + + /* set next Hwi state before triggering TDC */ + if (PowerCC26XX_module.calStep == STEP_CAL_HF2_2) { + PowerCC26XX_module.hwiState = CAL_RCOSC_HF2; + } + else { + PowerCC26XX_module.hwiState = CAL_RCOSC_HF1; + } + } + + /* Reset/clear result of TDC */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_CTL) = AUX_TDC_CTL_CMD_CLR_RESULT; + + /* Clear possible pending interrupt source */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) = + AUX_EVCTL_EVTOMCUFLAGSCLR_TDC_DONE; + + /* Enable TDC done interrupt as part of AUX_COMBINED interrupt */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = + AUX_EVCTL_COMBEVTOMCUMASK_TDC_DONE; + + /* Run TDC (start synchronously) */ + HWREG(AUX_TDC_BASE + AUX_TDC_O_CTL) = + AUX_TDC_CTL_CMD_RUN_SYNC_START; + + break; + + case STEP_CAL_LF_3: + case STEP_CAL_HF1_3: + + /* set next Swi state */ + if (PowerCC26XX_module.calStep == STEP_CAL_LF_3) { + PowerCC26XX_module.calStep = STEP_CAL_HF1_1; + } + else { + PowerCC26XX_module.calStep = STEP_CAL_HF2_1; + } + + /* clear AUX_WUC:REFCLKCTL.REQ... */ + HWREG(AUX_WUC_BASE + AUX_WUC_O_REFCLKCTL) = 0; + + /* start Clock object to delay while wait for ACK */ + ClockP_start(ClockP_handle(&PowerCC26XX_module.calClockStruct)); + + break; + + case STEP_CLEANUP_1: + + /* release the TDC clock request */ + HWREG(AUX_WUC_BASE + AUX_WUC_O_TDCCLKCTL) = 0; + + /* release the TDC reference clock request */ + HWREG(AUX_WUC_BASE + AUX_WUC_O_REFCLKCTL) = 0; + + /* set next state */ + PowerCC26XX_module.calStep = STEP_CLEANUP_2; + + /* start Clock object to delay while wait for ACK */ + ClockP_start(ClockP_handle(&PowerCC26XX_module.calClockStruct)); + + break; + + case STEP_CLEANUP_2: + + /* + * Disable all interrupts as part of AUX_COMBINED interrupt + * Once we release semaphore, the sensor controller is allowed + * to use the TDC. When it does, we must ensure that this + * does not cause any unexpected interrupts to the CM3. + */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = 0; + + /* release AUX semaphore */ + HWREG(AUX_SMPH_BASE + AUX_SMPH_O_SMPH1) = 1; + + /* release the power down constraints and XOSC_HF dependency */ + Power_releaseDependency(PowerCC26XX_XOSC_HF); + Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY); + + /* set next state */ + PowerCC26XX_module.calStep = STEP_TDC_INIT_1; + +#if INSTRUMENT + doneCal = true; + calHF2i = 1; +#endif + PowerCC26XX_module.busyCal = false; + break; + + default: + for (;;) { + } + } +} + +/* + * ======== PowerCC26XX_RCOSC_clockFunc ======== + */ +void PowerCC26XX_RCOSC_clockFunc(uintptr_t arg) +{ +#if INSTRUMENT + calClocks++; +#endif + + switch (PowerCC26XX_module.calStep) { + + case STEP_TDC_INIT_2: + /* finish wait for AUX_WUC:TDCCLKCTL.ACK to be set ... */ + while(!(HWREG(AUX_WUC_BASE + AUX_WUC_O_TDCCLKCTL) & + AUX_WUC_TDCCLKCTL_ACK)); + break; + + case STEP_CAL_LF_1: + case STEP_CAL_HF1_1: + case STEP_CAL_HF2_1: + /* finish wait for AUX_WUC:REFCLKCTL.ACK to be cleared ... */ + while(HWREG(AUX_WUC_BASE + AUX_WUC_O_REFCLKCTL) & + AUX_WUC_REFCLKCTL_ACK); + break; + + case STEP_CAL_LF_2: + case STEP_CAL_HF1_2: + case STEP_CAL_HF2_2: + /* finish wait for AUX_WUC:REFCLKCTL.ACK to be set ... */ + while(!(HWREG(AUX_WUC_BASE + AUX_WUC_O_REFCLKCTL) & + AUX_WUC_REFCLKCTL_ACK)); + break; + + case STEP_CLEANUP_2: + /* finish wait for AUX_WUC:TDCCLKCTL.ACK to be cleared ... */ + while ((HWREG(AUX_WUC_BASE + AUX_WUC_O_TDCCLKCTL) & + AUX_WUC_TDCCLKCTL_ACK)); + /* finish wait for AUX_WUC:REFCLKCTL.ACK to be cleared ... */ + while(HWREG(AUX_WUC_BASE + AUX_WUC_O_REFCLKCTL) & + AUX_WUC_REFCLKCTL_ACK); + break; + + default: + for (;;) { + } + } + + PowerCC26XX_doCalibrate(); +} + +/* + * ======== getTdcSemaphore ======== + * Get TDC semaphore (number 1) + */ +static bool getTdcSemaphore() +{ + unsigned int own; + + /* try to acquire SMPH */ + own = HWREG(AUX_SMPH_BASE + AUX_SMPH_O_SMPH1); + + /* if acquired SMPH: done */ + if (own != 0) { + return (true); + } + + /* clear the interrupt source, can only be cleared when we don't have semaphore */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_EVTOMCUFLAGSCLR) = + AUX_EVCTL_EVTOMCUFLAGSCLR_SMPH_AUTOTAKE_DONE; + + /* + * else, did not acquire the semaphore, enable SMPH_AUTOTAKE_DONE event + * (don't OR, write entire register, no other interrupts can be enabled!) + */ + HWREG(AUX_EVCTL_BASE + AUX_EVCTL_O_COMBEVTOMCUMASK) = + AUX_EVCTL_COMBEVTOMCUMASK_SMPH_AUTOTAKE_DONE; + + /* start AUTOTAKE of semaphore for TDC access */ + HWREG(AUX_SMPH_BASE + AUX_SMPH_O_AUTOTAKE) = AUX_TDC_SEMAPHORE_NUMBER; + + return (false); +} + +/* + * ======== updateSubSecInc ======== + * Update the SUBSECINC register based on measured RCOSC_LF frequency + */ +static void updateSubSecInc(uint32_t tdcResult) +{ + int32_t newSubSecInc; + uint32_t oldSubSecInc; + uint32_t subSecInc; + uint32_t ccfgModeConfReg; + int32_t hposcOffset; + int32_t hposcOffsetInv; + + /* + * Calculate the new SUBSECINC + * Here's the formula: AON_RTC:SUBSECINC = (45813 * NR) / 256 + * Based on measuring 32 LF clock periods + */ + newSubSecInc = (45813 * tdcResult) / 256; + + /* TODO: Replace with ccfgread driverlib call */ + ccfgModeConfReg = HWREG( CCFG_BASE + CCFG_O_MODE_CONF ); + /* Compensate HPOSC drift if HPOSC is in use */ + if(((ccfgModeConfReg & CCFG_MODE_CONF_XOSC_FREQ_M ) >> CCFG_MODE_CONF_XOSC_FREQ_S) == 1) { + /* Get the HPOSC relative offset at this temperature */ + hposcOffset = OSC_HPOSCRelativeFrequencyOffsetGet(AONBatMonTemperatureGetDegC()); + /* Convert to RF core format */ + hposcOffsetInv = OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert(hposcOffset); + /* Adjust SUBSECINC */ + newSubSecInc += (((newSubSecInc >> 4) * (hposcOffsetInv >> 3)) >> 15); + } + + /* Apply filter, but not for first calibration */ + if (PowerCC26XX_module.firstLF) { + /* Don't apply filter first time, to converge faster */ + subSecInc = newSubSecInc; + /* No longer first measurement */ + PowerCC26XX_module.firstLF = false; + } + else { + /* Read old SUBSECINC value */ + oldSubSecInc = HWREG(AON_RTC_BASE + AON_RTC_O_SUBSECINC) & 0x00FFFFFF; + /* Apply filter, 0.5 times old value, 0.5 times new value */ + subSecInc = (oldSubSecInc * 1 + newSubSecInc * 1) / 2; + } + + /* Update SUBSECINC values */ + HWREG(AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINC0) = subSecInc; + HWREG(AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINC1) = subSecInc >> 16; + + /* update to use new values */ + HWREG(AUX_WUC_BASE + AUX_WUC_O_RTCSUBSECINCCTL) = + AUX_WUC_RTCSUBSECINCCTL_UPD_REQ; +} + +/* + * ======== PowerCC26XX_calibrateRcoscHf1 ======== + * Calibrate RCOSC_HF agains XOSC_HF: compute and setup new trims + */ +static void calibrateRcoscHf1(int32_t tdcResult) +{ + /* *** STEP 1: Find RCOSC_HF-XOSC_HF frequency offset with current trim settings */ + /* Read in current trim settings */ + PowerCC26XX_module.nCtrimCurr = + (DDI32RegRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_RCOSCHFCTL) & + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_M) >> + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_S; + + PowerCC26XX_module.nCtrimFractCurr = + (DDI32RegRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL) + & DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M) >> + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S; + + PowerCC26XX_module.nRtrimCurr = + (DDI32RegRead(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ATESTCTL) + & DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M) >> + DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S; + + + /* + * Find RCOSC_HF-XOSC_HF frequency offset with current trim settings + * Positive value => RCOSC_HF runs slow, CTRIM(FRACT) should be increased + * Negative value => RCOSC_HF runs fast, CTRIM(FRACT) should be decreased + * Resolution: 31.25 kHz; CTRIMFRACT resolution ~30 kHz + */ + PowerCC26XX_module.nDeltaFreqCurr = (int32_t) tdcResult - RCOSC_HF_PERFECT_TDC_VALUE; + + /* *** STEP 2: Attempt to calculate more optimal settings */ + if (PowerCC26XX_module.nDeltaFreqCurr == 0) { + /* If perfect, don't perform second measurement and keep current settings */ + PowerCC26XX_module.bRefine = false; + return; + } + if (PowerCC26XX_module.bRefine) { + /* + * Trying to find better match across CTRIM/RTRIM. Due to mismatches the + * first try might not have been more optimal than the current setting. + * Continue refining, starting from stored values + */ + } else { + /* Start from current values */ + PowerCC26XX_module.nCtrimFractNew = PowerCC26XX_module.nCtrimFractCurr; + PowerCC26XX_module.nCtrimNew = PowerCC26XX_module.nCtrimCurr; + PowerCC26XX_module.nRtrimNew = PowerCC26XX_module.nRtrimCurr; + PowerCC26XX_module.nDeltaFreqNew = PowerCC26XX_module.nDeltaFreqCurr; + } + + /* + * Calculate change to CTRIMFRACT with safe assumptions of gain, + * apply delta to current CTRIMFRACT and convert to valid CTRIM/CTRIMFRACT + */ + PowerCC26XX_module.nCtrimFractNew = PowerCC26XX_module.nCtrimFractNew + + Scale_rndInf(PowerCC26XX_module.nDeltaFreqNew); + PowerCC26XX_module.nCtrimNew = PowerCC26XX_module.nCtrimCurr; + + /* One step of CTRIM is about 500 kHz, so limit to one CTRIM step */ + if (PowerCC26XX_module.nCtrimFractNew < 1) { + if (PowerCC26XX_module.nRtrimNew == 3) { + /* We try the slow RTRIM in this CTRIM first */ + PowerCC26XX_module.nCtrimFractNew = Max(1, PowerCC26XX_module.nCtrimFractNew + 21); + PowerCC26XX_module.nRtrimNew = 0; + } + else { + /* Step down one CTRIM and use fast RTRIM */ + PowerCC26XX_module.nCtrimFractNew = Max(1, PowerCC26XX_module.nCtrimFractNew + 32 - 21); + PowerCC26XX_module.nCtrimNew = Max(0, PowerCC26XX_module.nCtrimNew - 1); + PowerCC26XX_module.nRtrimNew = 3; + } + } + else if (PowerCC26XX_module.nCtrimFractNew > 30) { + if (PowerCC26XX_module.nRtrimNew == 0) { + /* We try the slow RTRIM in this CTRIM first */ + PowerCC26XX_module.nCtrimFractNew = Min(30, PowerCC26XX_module.nCtrimFractNew - 21); + PowerCC26XX_module.nRtrimNew = 3; + } + else { + /* Step down one CTRIM and use fast RTRIM */ + PowerCC26XX_module.nCtrimFractNew = Min(30, PowerCC26XX_module.nCtrimFractNew - 32 + 21); + PowerCC26XX_module.nCtrimNew = Min(0x3F, PowerCC26XX_module.nCtrimNew + 1); + PowerCC26XX_module.nRtrimNew = 0; + } + } + else + { + /* We're within sweet spot of current CTRIM => no change */ + } + + /* Find RCOSC_HF vs XOSC_HF frequency offset with new trim settings */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_RCOSCHFCTL, + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_M, + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_S, + PowerCC26XX_module.nCtrimNew); + + /* Enable RCOSCHFCTRIMFRACT_EN */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_M, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_EN_LOCAL_S, + 1); + + /* Modify CTRIM_FRACT */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S, + PowerCC26XX_module.nCtrimFractNew); + + /* Modify RTRIM */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ATESTCTL, + DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M, + DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S, + PowerCC26XX_module.nRtrimNew); +} + +/* + * ======== Power_calibrateRcoscHf2 ======== + * Calibrate RCOSC_HF agains XOSC_HF: determine better result, set new trims + */ +static void calibrateRcoscHf2(int32_t tdcResult) +{ + + PowerCC26XX_module.nDeltaFreqNew = (int32_t) tdcResult - RCOSC_HF_PERFECT_TDC_VALUE; + /* Calculate new delta freq */ + + /* *** STEP 4: Determine whether the new settings are better or worse */ + if (Abs(PowerCC26XX_module.nDeltaFreqNew) <= Abs(PowerCC26XX_module.nDeltaFreqCurr)) { + /* New settings are better or same -> make current by keeping in registers */ + PowerCC26XX_module.bRefine = false; + } + else { + /* First measurement was better than second, restore current settings */ + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_RCOSCHFCTL, + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_M, + DDI_0_OSC_RCOSCHFCTL_RCOSCHF_CTRIM_S, + PowerCC26XX_module.nCtrimCurr); + + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_CTL1_LOCAL, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_M, + DDI_0_OSC_CTL1_RCOSCHFCTRIMFRACT_LOCAL_S, + PowerCC26XX_module.nCtrimFractCurr); + + DDI16BitfieldWrite(AUX_DDI0_OSC_BASE, DDI_0_OSC_O_ATESTCTL, + DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_M, + DDI_0_OSC_ATESTCTL_SET_RCOSC_HF_FINE_RESISTOR_LOCAL_S, + PowerCC26XX_module.nRtrimCurr); + + /* Enter a refinement mode where we keep searching for better matches */ + PowerCC26XX_module.bRefine = true; + } + +}