/*
 * -------------------------------------------
 *    MSP432 DriverLib - v3_21_00_05 
 * -------------------------------------------
 *
 * --COPYRIGHT--,BSD,BSD
 * Copyright (c) 2016, 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.
 * --/COPYRIGHT--*/
/* Standard Includes */
#include <stdint.h>

/* DriverLib Includes */
#include <cs.h>
#include <debug.h>
#include <sysctl.h>
#include <interrupt.h>

/* Statics */
static uint32_t hfxtFreq;
static uint32_t lfxtFreq;

#ifdef DEBUG

bool _CSIsClockDividerValid(uint8_t divider)
{
    return ((divider == CS_CLOCK_DIVIDER_1) || (divider == CS_CLOCK_DIVIDER_2)
            || (divider == CS_CLOCK_DIVIDER_4) || (divider == CS_CLOCK_DIVIDER_8)
            || (divider == CS_CLOCK_DIVIDER_16) || (divider == CS_CLOCK_DIVIDER_32)
            || (divider == CS_CLOCK_DIVIDER_64) || (divider == CS_CLOCK_DIVIDER_128));
}

#endif

static uint32_t _CSGetHFXTFrequency()
{
    if (hfxtFreq >= CS_1MHZ && hfxtFreq <= CS_4MHZ)
        return CS_CTL2_HFXTFREQ_0;
    else if (hfxtFreq > CS_4MHZ && hfxtFreq <= CS_8MHZ)
        return CS_CTL2_HFXTFREQ_1;
    else if (hfxtFreq > CS_8MHZ && hfxtFreq <= CS_16MHZ)
        return CS_CTL2_HFXTFREQ_2;
    else if (hfxtFreq > CS_16MHZ && hfxtFreq <= CS_24MHZ)
        return CS_CTL2_HFXTFREQ_3;
    else if (hfxtFreq > CS_24MHZ && hfxtFreq <= CS_32MHZ)
        return CS_CTL2_HFXTFREQ_4;
    else if (hfxtFreq > CS_32MHZ && hfxtFreq <= CS_40MHZ)
        return CS_CTL2_HFXTFREQ_5;
    else if (hfxtFreq > CS_40MHZ && hfxtFreq <= CS_48MHZ)
        return CS_CTL2_HFXTFREQ_5;
    else
    {
        ASSERT(false);
        return 0;
    }

}

static uint32_t _CSGetDividerValue(uint32_t wDivider)
{
    switch (wDivider)
    {
    case CS_CLOCK_DIVIDER_1:
        return 1;
    case CS_CLOCK_DIVIDER_2:
        return 2;
    case CS_CLOCK_DIVIDER_4:
        return 4;
    case CS_CLOCK_DIVIDER_8:
        return 8;
    case CS_CLOCK_DIVIDER_16:
        return 16;
    case CS_CLOCK_DIVIDER_32:
        return 32;
    case CS_CLOCK_DIVIDER_64:
        return 64;
    case CS_CLOCK_DIVIDER_128:
        return 128;
    default:
        ASSERT(false);
        return 1;
    }
}

static uint32_t _CSComputeCLKFrequency(uint32_t wClockSource, uint32_t wDivider)
{
    uint_fast8_t bDivider;

    bDivider = _CSGetDividerValue(wDivider);

    switch (wClockSource)
    {
    case CS_LFXTCLK_SELECT:
    {
        if (BITBAND_PERI(CS->IFG, CS_IFG_LFXTIFG_OFS))
        {
            CS_clearInterruptFlag(CS_LFXT_FAULT);

            if (BITBAND_PERI(CS->IFG, CS_IFG_LFXTIFG_OFS))
            {
                if (BITBAND_PERI(CS->CLKEN, CS_CLKEN_REFOFSEL_OFS))
                    return (128000 / bDivider);
                else
                    return (32768 / bDivider);
            }
        }
        return lfxtFreq / bDivider;
    }
    case CS_HFXTCLK_SELECT:
    {
        if (BITBAND_PERI(CS->IFG, CS_IFG_HFXTIFG_OFS))
        {
            CS_clearInterruptFlag(CS_HFXT_FAULT);

            if (BITBAND_PERI(CS->IFG, CS_IFG_HFXTIFG_OFS))
            {
                if (BITBAND_PERI(CS->CLKEN, CS_CLKEN_REFOFSEL_OFS))
                    return (128000 / bDivider);
                else
                    return (32768 / bDivider);
            }
        }
        return hfxtFreq / bDivider;
    }
    case CS_VLOCLK_SELECT:
        return CS_VLOCLK_FREQUENCY / bDivider;
    case CS_REFOCLK_SELECT:
    {
        if (BITBAND_PERI(CS->CLKEN, CS_CLKEN_REFOFSEL_OFS))
            return (128000 / bDivider);
        else
            return (32768 / bDivider);
    }
    case CS_DCOCLK_SELECT:
        return (CS_getDCOFrequency() / bDivider);
    case CS_MODOSC_SELECT:
        return CS_MODCLK_FREQUENCY / bDivider;
    default:
        ASSERT(false);
        return 0;
    }
}

//******************************************************************************
// Internal function for getting DCO nominal frequency
//******************************************************************************
static uint32_t _CSGetDOCFrequency(void)
{
    uint32_t dcoFreq;

    switch (CS->CTL0 & CS_CTL0_DCORSEL_MASK)
    {
    case CS_CTL0_DCORSEL_0:
        dcoFreq = 1500000;
        break;
    case CS_CTL0_DCORSEL_1:
        dcoFreq = 3000000;
        break;
    case CS_CTL0_DCORSEL_2:
        dcoFreq = 6000000;
        break;
    case CS_CTL0_DCORSEL_3:
        dcoFreq = 12000000;
        break;
    case CS_CTL0_DCORSEL_4:
        dcoFreq = 24000000;
        break;
    case CS_CTL0_DCORSEL_5:
        dcoFreq = 48000000;
        break;
    default:
        dcoFreq = 0;
    }

    return (dcoFreq);
}

void CS_setExternalClockSourceFrequency(uint32_t lfxt_XT_CLK_frequency,
        uint32_t hfxt_XT_CLK_frequency)
{
    hfxtFreq = hfxt_XT_CLK_frequency;
    lfxtFreq = lfxt_XT_CLK_frequency;
}

void CS_initClockSignal(uint32_t selectedClockSignal, uint32_t clockSource,
        uint32_t clockSourceDivider)
{
    ASSERT(_CSIsClockDividerValid(clockSourceDivider));

    /* Unlocking the CS Module */
    CS->KEY = CS_KEY;

    switch (selectedClockSignal)
    {
    case CS_ACLK:
    {
        /* Making sure that the clock signal for ACLK isn't set to anything
         * invalid
         */
        ASSERT(
                (selectedClockSignal != CS_DCOCLK_SELECT)
                && (selectedClockSignal != CS_MODOSC_SELECT)
                && (selectedClockSignal != CS_HFXTCLK_SELECT));

        /* Waiting for the clock source ready bit to be valid before
         * changing */
        while (!BITBAND_PERI(CS->STAT, CS_STAT_ACLK_READY_OFS))
            ;

        /* Setting the divider and source */
        CS->CTL1 = ((clockSourceDivider >> CS_ACLK_DIV_BITPOS)
                | (clockSource << CS_ACLK_SRC_BITPOS))
                | (CS->CTL1 & ~(CS_CTL1_SELA_MASK | CS_CTL1_DIVA_MASK));

        /* Waiting for ACLK to be ready again */
        while (!BITBAND_PERI(CS->STAT, CS_STAT_ACLK_READY_OFS))
            ;

        break;
    }
    case CS_MCLK:
    {

        /* Waiting for the clock source ready bit to be valid before
         * changing */
        while (!BITBAND_PERI(CS->STAT, CS_STAT_MCLK_READY_OFS))
            ;

        CS->CTL1 = ((clockSourceDivider >> CS_MCLK_DIV_BITPOS)
                | (clockSource << CS_MCLK_SRC_BITPOS))
                | (CS->CTL1 & ~(CS_CTL1_SELM_MASK | CS_CTL1_DIVM_MASK));

        /* Waiting for MCLK to be ready */
        while (!BITBAND_PERI(CS->STAT, CS_STAT_MCLK_READY_OFS))
            ;

        break;
    }
    case CS_SMCLK:
    {
        /* Waiting for the clock source ready bit to be valid before
         * changing */
        while (!BITBAND_PERI(CS->STAT, CS_STAT_SMCLK_READY_OFS))
            ;

        CS->CTL1 = ((clockSourceDivider >> CS_SMCLK_DIV_BITPOS)
                | (clockSource << CS_HSMCLK_SRC_BITPOS))
                | (CS->CTL1 & ~(CS_CTL1_DIVS_MASK | CS_CTL1_SELS_MASK));

        /* Waiting for SMCLK to be ready */
        while (!BITBAND_PERI(CS->STAT, CS_STAT_SMCLK_READY_OFS))
            ;

        break;
    }
    case CS_HSMCLK:
    {
        /* Waiting for the clock source ready bit to be valid before
         * changing */
        while (!BITBAND_PERI(CS->STAT, CS_STAT_HSMCLK_READY_OFS))
            ;

        CS->CTL1 = ((clockSourceDivider >> CS_HSMCLK_DIV_BITPOS)
                | (clockSource << CS_HSMCLK_SRC_BITPOS))
                | (CS->CTL1 & ~(CS_CTL1_DIVHS_MASK | CS_CTL1_SELS_MASK));

        /* Waiting for HSMCLK to be ready */
        while (!BITBAND_PERI(CS->STAT, CS_STAT_HSMCLK_READY_OFS))
            ;

        break;
    }
    case CS_BCLK:
    {

        /* Waiting for the clock source ready bit to be valid before
         * changing */
        while (!BITBAND_PERI(CS->STAT, CS_STAT_BCLK_READY_OFS))
            ;

        /* Setting the clock source and then returning
         * (cannot divide CLK)
         */
        if (clockSource == CS_LFXTCLK_SELECT)
            BITBAND_PERI(CS->CTL1, CS_CTL1_SELB_OFS) = 0;
        else if (clockSource == CS_REFOCLK_SELECT)
            BITBAND_PERI(CS->CTL1, CS_CTL1_SELB_OFS) = 1;
        else
            ASSERT(false);

        /* Waiting for BCLK to be ready */
        while (!BITBAND_PERI(CS->STAT, CS_STAT_BCLK_READY_OFS))
            ;

        break;
    }
    default:
    {
        /* Should never get here */
        ASSERT(false);
    }
    }

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

bool CS_startHFXT(bool bypassMode)
{
    return CS_startHFXTWithTimeout(bypassMode, 0);
}

bool CS_startHFXTWithTimeout(bool bypassMode, uint32_t timeout)
{
    uint32_t wHFFreqRange;
    uint_fast8_t bNMIStatus;
    bool boolTimeout;

    /* Unlocking the CS Module */
    CS->KEY = CS_KEY;

    /* Saving status and temporarily disabling NMIs for UCS faults */
    bNMIStatus = SysCtl_getNMISourceStatus() & SYSCTL_CS_SRC;
    SysCtl_disableNMISource(SYSCTL_CS_SRC);

    /* Determining which frequency range to use */
    wHFFreqRange = _CSGetHFXTFrequency();
    boolTimeout = (timeout == 0) ? false : true;

    /* Setting to maximum drive strength  */
    BITBAND_PERI(CS->CTL2, CS_CTL2_HFXTDRIVE_OFS) = 1;
    CS->CTL2 = (CS->CTL2 & (~CS_CTL2_HFXTFREQ_MASK)) | (wHFFreqRange);

    if (bypassMode)
    {
        BITBAND_PERI(CS->CTL2, CS_CTL2_HFXTBYPASS_OFS) = 1;
    } else
    {
        BITBAND_PERI(CS->CTL2, CS_CTL2_HFXTBYPASS_OFS) = 0;
    }

    /* Starting and Waiting for frequency stabilization */
    BITBAND_PERI(CS->CTL2, CS_CTL2_HFXT_EN_OFS) = 1;
    while (BITBAND_PERI(CS->IFG, CS_IFG_HFXTIFG_OFS))
    {
        if (boolTimeout && ((--timeout) == 0))
            break;

        BITBAND_PERI(CS->CLRIFG,CS_CLRIFG_CLR_HFXTIFG_OFS) = 1;
    }
    
    /* Setting the drive strength */
    if (!bypassMode)
    {
        if (wHFFreqRange != CS_CTL2_HFXTFREQ_0)
            BITBAND_PERI(CS->CTL2, CS_CTL2_HFXTDRIVE_OFS) = 1;
        else
            BITBAND_PERI(CS->CTL2, CS_CTL2_HFXTDRIVE_OFS) = 0;
    }

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;

    /* Enabling the NMI state */
    SysCtl_enableNMISource(bNMIStatus);
    
    if(boolTimeout && timeout == 0)
        return false;

    return true;
}

bool CS_startLFXT(uint32_t xtDrive)
{
    return CS_startLFXTWithTimeout(xtDrive, 0);
}

bool CS_startLFXTWithTimeout(uint32_t xtDrive, uint32_t timeout)
{
    uint8_t bNMIStatus;
    bool boolBypassMode, boolTimeout;

    ASSERT(lfxtFreq != 0)
    ASSERT(
            (xtDrive == CS_LFXT_DRIVE0) || (xtDrive == CS_LFXT_DRIVE1)
            || (xtDrive == CS_LFXT_DRIVE2)
            || (xtDrive == CS_LFXT_DRIVE3)
            || (xtDrive == CS_LFXT_BYPASS));

    /* Unlocking the CS Module */
    CS->KEY = CS_KEY;

    /* Saving status and temporarily disabling NMIs for UCS faults */
    bNMIStatus = SysCtl_getNMISourceStatus() & SYSCTL_CS_SRC;
    SysCtl_disableNMISource(SYSCTL_CS_SRC);
    boolBypassMode = (xtDrive == CS_LFXT_BYPASS) ? true : false;
    boolTimeout = (timeout == 0) ? false : true;

    /* Setting to maximum drive strength  */
    if (boolBypassMode)
    {
        BITBAND_PERI(CS->CTL2, CS_CTL2_LFXTBYPASS_OFS) = 1;
    } else
    {
        CS->CTL2 |= (CS_LFXT_DRIVE3);
        BITBAND_PERI(CS->CTL2, CS_CTL2_LFXTBYPASS_OFS) = 0;
    }

    /* Waiting for frequency stabilization */
    BITBAND_PERI(CS->CTL2, CS_CTL2_LFXT_EN_OFS) = 1;

    while (BITBAND_PERI(CS->IFG, CS_IFG_LFXTIFG_OFS))
    {
        if (boolTimeout && ((--timeout) == 0))
            break;

        BITBAND_PERI(CS->CLRIFG,CS_CLRIFG_CLR_LFXTIFG_OFS) = 1;
    }

    /* Setting the drive strength */
    if (!boolBypassMode)
    {
        CS->CTL2 = ((CS->CTL2 & ~CS_LFXT_DRIVE3) | xtDrive);
    }

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;

    /* Enabling the NMI state */
    SysCtl_enableNMISource(bNMIStatus);
    
    if(boolTimeout && timeout == 0)
        return false;

    return true;
}

void CS_enableClockRequest(uint32_t selectClock)
{
    ASSERT(
            selectClock == CS_ACLK || selectClock == CS_HSMCLK
            || selectClock == CS_SMCLK || selectClock == CS_MCLK);

    /* Unlocking the module */
    CS->KEY = CS_KEY;

    CS->CLKEN |= selectClock;

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_disableClockRequest(uint32_t selectClock)
{
    ASSERT(
            selectClock == CS_ACLK || selectClock == CS_HSMCLK
            || selectClock == CS_SMCLK || selectClock == CS_MCLK);

    /* Unlocking the module */
    CS->KEY = CS_KEY;

    CS->CLKEN &= ~selectClock;

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_setReferenceOscillatorFrequency(uint8_t referenceFrequency)
{
    ASSERT(
            referenceFrequency == CS_REFO_32KHZ
            || referenceFrequency == CS_REFO_128KHZ);

    /* Unlocking the module */
    CS->KEY = CS_KEY;

    BITBAND_PERI(CS->CLKEN, CS_CLKEN_REFOFSEL_OFS) = referenceFrequency;

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_enableDCOExternalResistor(void)
{
    /* Unlocking the module */
    CS->KEY = CS_KEY;

    BITBAND_PERI(CS->CTL0,CS_CTL0_DCORES_OFS) = 1;

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_setDCOExternalResistorCalibration(uint_fast8_t calData, 
                                            uint_fast8_t freqRange)
{
    uint_fast8_t rselVal;

    /* Unlocking the module */
    CS->KEY = CS_KEY;

    rselVal = (CS->CTL0 | CS_CTL0_DCORSEL_MASK)>>CS_CTL0_DCORSEL_OFS;

    CS->CTL0 &= ~CS_CTL0_DCORSEL_MASK;

    if( (freqRange == CS_OVER32MHZ) && ( TLV->HWREV > DEVICE_PG1_1))
    {
        CS->DCOERCAL1 &= ~CS_DCOERCAL1_DCO_FCAL_RSEL5_MASK;
        CS->DCOERCAL1 |= (calData);
    }
    else
    {
        CS->DCOERCAL0 &= ~CS_DCOERCAL0_DCO_FCAL_RSEL04_MASK;
        CS->DCOERCAL0 |= (calData)<<CS_DCOERCAL0_DCO_FCAL_RSEL04_OFS;
    }

    CS->CTL0 |= (rselVal)<<CS_CTL0_DCORSEL_OFS;

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;

}

void CS_disableDCOExternalResistor(void)
{
    /* Unlocking the module */
    CS->KEY = CS_KEY;

    BITBAND_PERI(CS->CTL0,CS_CTL0_DCORES_OFS) = 0;

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_setDCOCenteredFrequency(uint32_t dcoFreq)
{
    ASSERT(
            dcoFreq == CS_DCO_FREQUENCY_1_5 || dcoFreq == CS_DCO_FREQUENCY_3
            || dcoFreq == CS_DCO_FREQUENCY_6
            || dcoFreq == CS_DCO_FREQUENCY_12
            || dcoFreq == CS_DCO_FREQUENCY_24
            || dcoFreq == CS_DCO_FREQUENCY_48);

    /* Unlocking the CS Module */
    CS->KEY = CS_KEY;

    /* Resetting Tuning Parameters and Setting the frequency */
    CS->CTL0 = ((CS->CTL0 & ~CS_CTL0_DCORSEL_MASK) | dcoFreq);

    /* Locking the CS Module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_tuneDCOFrequency(int16_t tuneParameter)
{
    CS->KEY = CS_KEY;

    uint16_t dcoTuneMask = 0x1FFF;
    uint16_t dcoTuneSigned = 0x1000;

    if (TLV->HWREV > DEVICE_PG1_1) {
        dcoTuneMask = 0x3FF;
        dcoTuneSigned = 0x200;
    }

    if (tuneParameter < 0)
    {
        CS->CTL0 = ((CS->CTL0 & ~dcoTuneMask) | (tuneParameter
                    & dcoTuneMask) | dcoTuneSigned);
    } 
    else
    {
        CS->CTL0 = ((CS->CTL0 & ~dcoTuneMask) | (tuneParameter
                    & dcoTuneMask));
    }
     
     BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

uint32_t CS_getDCOFrequency(void)
{
    float dcoConst;
    int32_t calVal;
    uint32_t centeredFreq;
    int16_t dcoTune;
    uint_fast8_t tlvLength;
    SysCtl_CSCalTLV_Info *csInfo;
    uint32_t retVal;

    centeredFreq = _CSGetDOCFrequency();

    /* Parsing the TLV and getting the maximum erase pulses */
    SysCtl_getTLVInfo(TLV_TAG_CS, 0, &tlvLength, (uint32_t**)&csInfo);

    if(tlvLength == 0)
    {
        return centeredFreq;
    }

    /* Checking to see if we need to do signed conversion */
    if ( TLV->HWREV > DEVICE_PG1_1)
    {
        dcoTune = CS->CTL0 & 0x3FF;
        if (dcoTune & 0x200)
        {
            dcoTune = dcoTune | 0xFE00;
        }
    }
    else
    {
        dcoTune = CS->CTL0 & 0x1FFF;
        if (dcoTune & 0x1000)
        {
            dcoTune = dcoTune | 0xF000;
        }
    }
    
    if (dcoTune == 0)
        return (uint32_t) centeredFreq;

    /* DCORSEL = 5  */
    if ((centeredFreq == 48000000) && ( TLV->HWREV > DEVICE_PG1_1))
    {
         /* External Resistor */
        if (BITBAND_PERI(CS->CTL0, CS_CTL0_DCORES_OFS))
        {
            dcoConst = *((float *) &csInfo->rDCOER_CONSTK_RSEL5);
            calVal = csInfo->rDCOER_FCAL_RSEL5;
        }
        /* Internal Resistor */
        else
        {
            dcoConst = *((float *) &csInfo->rDCOIR_CONSTK_RSEL5);
            calVal = csInfo->rDCOIR_FCAL_RSEL5;
        }
    }
    /* DCORSEL = 4 */
    else
    {
        /* External Resistor */
        if (BITBAND_PERI(CS->CTL0, CS_CTL0_DCORES_OFS))
        {
            dcoConst = *((float *) &csInfo->rDCOER_CONSTK_RSEL04);
            calVal = csInfo->rDCOER_FCAL_RSEL04;
        }
        /* Internal Resistor */
        else
        {
            dcoConst = *((float *) &csInfo->rDCOIR_CONSTK_RSEL04);
            calVal = csInfo->rDCOIR_FCAL_RSEL04;
        }
    }

    if( TLV->HWREV > DEVICE_PG1_1 )
    {
        retVal = (uint32_t) (centeredFreq)
            / (1 - ((dcoConst * dcoTune)
                            / ((1 + dcoConst * (768 - calVal)))));
    }
    else
    {
        retVal = (uint32_t) (centeredFreq)
            / (1 - ((dcoConst * dcoTune)
                            / (8 * (1 + dcoConst * (768 - calVal)))));
    }    
    return retVal;
}

void CS_setDCOFrequency(uint32_t dcoFrequency)
{
    int32_t nomFreq, calVal, dcoSigned;
    int16_t dcoTune;
    float dcoConst;
    bool rsel5 = false;
    dcoSigned = (int32_t) dcoFrequency;
    uint_fast8_t tlvLength;
     SysCtl_CSCalTLV_Info *csInfo;

    if (dcoFrequency < 2000000)
    {
        nomFreq = CS_15MHZ;
        CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_1_5);
    } else if (dcoFrequency < 4000000)
    {
        nomFreq = CS_3MHZ;
        CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_3);
    } else if (dcoFrequency < 8000000)
    {
        nomFreq = CS_6MHZ;
        CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_6);
    } else if (dcoFrequency < 16000000)
    {
        nomFreq = CS_12MHZ;
        CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_12);
    } else if (dcoFrequency < 32000000)
    {
        nomFreq = CS_24MHZ;
        CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_24);
    } else if (dcoFrequency < 640000001)
    {
        nomFreq = CS_48MHZ;
        CS_setDCOCenteredFrequency(CS_DCO_FREQUENCY_48);
        rsel5 = true;
    } else
    {
        ASSERT(false);
        return;
    }
    
    /* Parsing the TLV and getting the maximum erase pulses */
    SysCtl_getTLVInfo(TLV_TAG_CS, 0, &tlvLength, (uint32_t**)&csInfo);

    if(dcoFrequency == nomFreq || tlvLength == 0)
    {
       CS_tuneDCOFrequency(0);
       return;
    }

    if ((rsel5) && ( TLV->HWREV > DEVICE_PG1_1))
    {
        /* External Resistor*/
        if (BITBAND_PERI(CS->CTL0, CS_CTL0_DCORES_OFS))
        {
            dcoConst = *((float *) &csInfo->rDCOER_CONSTK_RSEL5);
            calVal = csInfo->rDCOER_FCAL_RSEL5;
        }
        /* Internal Resistor */
        else
        {
            dcoConst = *((float *) &csInfo->rDCOIR_CONSTK_RSEL5);
            calVal = csInfo->rDCOIR_FCAL_RSEL5;
        }
    }
    /* DCORSEL = 4 */
    else
    {
        /* External Resistor */
        if (BITBAND_PERI(CS->CTL0, CS_CTL0_DCORES_OFS))
        {
            dcoConst = *((float *) &csInfo->rDCOER_CONSTK_RSEL04);
            calVal = csInfo->rDCOER_FCAL_RSEL04;
        }
        /* Internal Resistor */
        else
        {
            dcoConst = *((float *) &csInfo->rDCOIR_CONSTK_RSEL04);
            calVal = csInfo->rDCOIR_FCAL_RSEL04;
        }
    }

    if ( TLV->HWREV > DEVICE_PG1_1)
        dcoTune = (int16_t) (((dcoSigned - nomFreq)
                * (1.0f + dcoConst * (768.0f - calVal)))
                / (dcoSigned * dcoConst));
    else
        dcoTune = (int16_t) (((dcoSigned - nomFreq)
                * (1.0f + dcoConst * (768.0f - calVal)) * 8.0f)
                / (dcoSigned * dcoConst));

    CS_tuneDCOFrequency(dcoTune);

}

uint32_t CS_getBCLK(void)
{
    if (BITBAND_PERI(CS->CTL1, CS_CTL1_SELB_OFS))
        return _CSComputeCLKFrequency(CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1);
    else
        return _CSComputeCLKFrequency(CS_LFXTCLK_SELECT, CS_CLOCK_DIVIDER_1);
}

uint32_t CS_getHSMCLK(void)
{
    uint32_t wSource, wDivider;

    wSource = (CS->CTL1 & CS_CTL1_SELS_MASK) >> CS_HSMCLK_SRC_BITPOS;
    wDivider = ((CS->CTL1 & CS_CTL1_DIVHS_MASK) << CS_HSMCLK_DIV_BITPOS);

    return _CSComputeCLKFrequency(wSource, wDivider);
}

uint32_t CS_getACLK(void)
{
    uint32_t wSource, wDivider;

    wSource = (CS->CTL1 & CS_CTL1_SELA_MASK) >> CS_ACLK_SRC_BITPOS;
    wDivider = ((CS->CTL1 & CS_CTL1_DIVA_MASK) << CS_ACLK_DIV_BITPOS);

    return _CSComputeCLKFrequency(wSource, wDivider);
}

uint32_t CS_getSMCLK(void)
{
    uint32_t wDivider, wSource;

    wSource = (CS->CTL1 & CS_CTL1_SELS_MASK) >> CS_HSMCLK_SRC_BITPOS;
    wDivider = ((CS->CTL1 & CS_CTL1_DIVS_MASK));

    return _CSComputeCLKFrequency(wSource, wDivider);

}

uint32_t CS_getMCLK(void)
{
    uint32_t wSource, wDivider;

    wSource = (CS->CTL1 & CS_CTL1_SELM_MASK) << CS_MCLK_SRC_BITPOS;
    wDivider = ((CS->CTL1 & CS_CTL1_DIVM_MASK) << CS_MCLK_DIV_BITPOS);

    return _CSComputeCLKFrequency(wSource, wDivider);
}

void CS_enableFaultCounter(uint_fast8_t counterSelect)
{
    ASSERT(counterSelect == CS_HFXT_FAULT_COUNTER ||
            counterSelect == CS_HFXT_FAULT_COUNTER);

    /* Unlocking the module */
    CS->KEY = CS_KEY;

    if (counterSelect == CS_HFXT_FAULT_COUNTER)
    {
        BITBAND_PERI(CS->CTL3, CS_CTL3_FCNTHF_EN_OFS) = 1;
    } else
    {
        BITBAND_PERI(CS->CTL3, CS_CTL3_FCNTLF_EN_OFS) = 1;
    }

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_disableFaultCounter(uint_fast8_t counterSelect)
{
    ASSERT(counterSelect == CS_HFXT_FAULT_COUNTER ||
            counterSelect == CS_HFXT_FAULT_COUNTER);

    /* Unlocking the module */
    CS->KEY = CS_KEY;

    if (counterSelect == CS_HFXT_FAULT_COUNTER)
    {
        BITBAND_PERI(CS->CTL3, CS_CTL3_FCNTHF_EN_OFS) = 0;
    } else
    {
        BITBAND_PERI(CS->CTL3, CS_CTL3_FCNTLF_EN_OFS) = 0;
    }

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_resetFaultCounter(uint_fast8_t counterSelect)
{
    ASSERT(counterSelect == CS_HFXT_FAULT_COUNTER ||
            counterSelect == CS_HFXT_FAULT_COUNTER);

    /* Unlocking the module */
    CS->KEY = CS_KEY;

    if (counterSelect == CS_HFXT_FAULT_COUNTER)
    {
        BITBAND_PERI(CS->CTL3, CS_CTL3_RFCNTHF_OFS) = 1;
    } else
    {
        BITBAND_PERI(CS->CTL3, CS_CTL3_RFCNTLF_OFS) = 1;
    }

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_startFaultCounter(uint_fast8_t counterSelect, uint_fast8_t countValue)
{
    ASSERT(counterSelect == CS_HFXT_FAULT_COUNTER ||
            counterSelect == CS_HFXT_FAULT_COUNTER);

    ASSERT(countValue == CS_FAULT_COUNTER_4096_CYCLES ||
            countValue == CS_FAULT_COUNTER_8192_CYCLES ||
            countValue == CS_FAULT_COUNTER_16384_CYCLES ||
            countValue == CS_FAULT_COUNTER_32768_CYCLES);

    /* Unlocking the module */
    CS->KEY = CS_KEY;

    if (counterSelect == CS_HFXT_FAULT_COUNTER)
    {
        CS->CTL3 = ((CS->CTL3 & ~CS_CTL3_FCNTHF_MASK) | (countValue << 4));
    } else
    {
        CS->CTL3 = ((CS->CTL3 & ~CS_CTL3_FCNTLF_MASK) | (countValue));
    }

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_enableInterrupt(uint32_t flags)
{
    /* Unlocking the module */
    CS->KEY = CS_KEY;

    CS->IE |= flags;

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_disableInterrupt(uint32_t flags)
{
    /* Unlocking the module */
    CS->KEY = CS_KEY;

    CS->IE &= ~flags;

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

uint32_t CS_getInterruptStatus(void)
{
    return CS->IFG;
}

uint32_t CS_getEnabledInterruptStatus(void)
{
    return CS_getInterruptStatus() & CS->IE;
}

void CS_clearInterruptFlag(uint32_t flags)
{
    /* Unlocking the module */
    CS->KEY = CS_KEY;

    CS->CLRIFG |= flags;

    /* Locking the module */
    BITBAND_PERI(CS->KEY, CS_KEY_KEY_OFS) = 1;
}

void CS_registerInterrupt(void (*intHandler)(void))
{
    //
    // Register the interrupt handler, returning an error if an error occurs.
    //
    Interrupt_registerInterrupt(INT_CS, intHandler);

    //
    // Enable the system control interrupt.
    //
    Interrupt_enableInterrupt(INT_CS);
}

void CS_unregisterInterrupt(void)
{
    //
    // Disable the interrupt.
    //
    Interrupt_disableInterrupt(INT_CS);

    //
    // Unregister the interrupt handler.
    //
    Interrupt_unregisterInterrupt(INT_CS);
}

