/*
 * 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);
}
