1318 lines
43 KiB
C
1318 lines
43 KiB
C
/*
|
|
* 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 <stdbool.h>
|
|
|
|
#include <ti/drivers/dpl/HwiP.h>
|
|
#include <ti/drivers/dpl/ClockP.h>
|
|
#include <ti/drivers/dpl/SwiP.h>
|
|
#include <ti/drivers/dpl/DebugP.h>
|
|
|
|
#include <ti/drivers/Power.h>
|
|
#include <ti/drivers/power/PowerCC26X2.h>
|
|
|
|
/* driverlib header files */
|
|
#include <ti/devices/DeviceFamily.h>
|
|
#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);
|
|
}
|