ext: simplelink: import TI Power manager from SimpleLink SDK

This commit imports the TI Power manager from SimpleLink SDK 3.10.00.53

Signed-off-by: Vincent Wan <vincent.wan@linaro.org>
This commit is contained in:
Vincent Wan 2019-07-24 17:21:32 -07:00 committed by Kumar Gala
parent 112760ab5d
commit 4a893817e8
8 changed files with 5239 additions and 2 deletions

View File

@ -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

View File

@ -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]' | \

File diff suppressed because it is too large Load Diff

View File

@ -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 <ti/drivers/Power.h>
* #include <ti/drivers/power/PowerCC26X2.h>
* @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 <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/dpl/ClockP.h>
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>
/*! 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_ */

View File

@ -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 <stdbool.h>
#include <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26X2.h>
#include <ti/devices/DeviceFamily.h>
#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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <ti/drivers/Power.h>
* #include <ti/drivers/power/PowerCC26XX.h>
* @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 <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/dpl/ClockP.h>
#include <ti/drivers/Power.h>
/* \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_ */

View File

@ -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 <stdbool.h>
#include <ti/drivers/dpl/ClockP.h>
#include <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>
#include <ti/devices/DeviceFamily.h>
#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;
}
}