/******************************************************************************
 *
 * Freescale Semiconductor Inc.
 * (c) Copyright 2004-2013 Freescale Semiconductor, Inc.
 * ALL RIGHTS RESERVED.
 *
 ******************************************************************************
 *
 * THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED 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 FREESCALE OR ITS 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.
 *
 **************************************************************************//*!
 *
 * @file mcg.c
 *
 * @author  
 *
 * @version 2.0
 *
 * @date Apr-12-2013
 *
 * @brief MCG clock generation routines
 *
 *****************************************************************************/

 /***
 / Header files
 ***/
  #include "derivative.h"
  #include "mcg.h"
 
 /***
 / Globals
 ***/
unsigned long gu32McgFreqKhz = MCG_OOR_FREQ_HZ/1000;
unsigned long gu32CoreFreqKhz = MCG_OOR_FREQ_HZ/1000;
unsigned long gu32PeriFreqKhz = MCG_OOR_FREQ_HZ/1000;
unsigned long gu32FlashFreqKhz = MCG_OOR_FREQ_HZ/2000;

unsigned char gu8ActualClkMode = MCG_FEI;
 /***
  * Functions
  ****/
  
  /*****************************************************************************
  * Function: u8PllInit
  *
  * Description: Initializes PLL to the desired PLL option
  *
  * Inputs:
  *     -u8PlloutOption: PLL frequency desired (i.e. "PLL48" for 48MHz)
  *     -u8OscSource: Oscillator source (i.e. MCG_XTAL)
  *     -u8ClkValue: Input clock value (i.e. XTAL4 for a 4MHz XTAL)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8PllInit(unsigned char u8PlloutOption, unsigned char u8OscSource, unsigned char u8ClkValue)
  {
    unsigned long u32PllFreq;
    
    if (gu8ActualClkMode != MCG_FEI)
      return MCG_ERR;  //Error, MCG must be in FEI to execute this function. 

    if(u8OscSource > MCG_EXTCLK)
      return MCG_ERR; //Error, unknown oscillator source clock
    
    if(u8OscSource == MCG_XTAL && u8ClkValue > XTAL32)
      return MCG_ERR; //Error, unsupported input clock value
    
    if(u8OscSource == MCG_EXTCLK && u8ClkValue > CLK50)
      return MCG_ERR; //Error, unsupported input clock value
    
  /*************************** FEI to FBE **********************************/
  if (u8OscSource == MCG_EXTCLK)
  {
      MCG_C2 = 0;
  }
  else if(u8ClkValue >= XTAL8)
  {
  // Enable external oscillator, RANGE=2, HGO=1, EREFS=1, LP=0, IRCS=0
      MCG_C2 = MCG_C2_RANGE0(2) | MCG_C2_HGO0_MASK | MCG_C2_EREFS0_MASK;
  }
  else
  {
    // Enable external oscillator, RANGE=1, HGO=1, EREFS=1, LP=0, IRCS=0
      MCG_C2 = MCG_C2_RANGE0(1) | MCG_C2_HGO0_MASK | MCG_C2_EREFS0_MASK;
  }
    
  // Select external oscilator and Reference Divider and clear IREFS to start ext osc
  // CLKS=2, FRDIV=3, IREFS=0, IRCLKEN=0, IREFSTEN=0
    MCG_C1 = MCG_C1_CLKS(2) | MCG_C1_FRDIV(3);

    /* if we aren't using an osc input we don't need to wait for the osc to init */
   if (u8OscSource == MCG_EXTCLK)
  {
      while (!(MCG_S & MCG_S_OSCINIT0_MASK)){};  // wait for oscillator to initialize
  }

    while (MCG_S & MCG_S_IREFST_MASK){}; // wait for Reference clock Status bit to clear

    while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2){}; // Wait for clock status bits to show clock source is ext ref clk

    
  /***************************** FBE to PBE **********************************/

    MCG_C5 = MCG_C5_PRDIV0(u8ClkValue); // Set PLL ref divider to match the crystal used


  // Ensure MCG_C6 is at the reset default of 0. LOLIE disabled, PLL disabled, clk monitor disabled, PLL VCO divider is clear
    MCG_C6 = 0x0;
    
  // Select the PLL VCO divider and system clock dividers depending on clocking option
    switch (u8PlloutOption) {
      case PLL50:
        // Set system options dividers
        //MCG=PLL, core = MCG, bus = MCG, FlexBus = MCG, Flash clock= MCG/2
        vfnSetSysDividers(CLKDIV_1,CLKDIV_1,CLKDIV_2);
        // Set the VCO divider and enable the PLL for 50MHz, LOLIE=0, PLLS=1, CME=0, VDIV=1
        MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV0(1); //VDIV = 1 (x25)
        u32PllFreq = 50000;
        break;
     case PLL100:
        // Set system options dividers
        //MCG=PLL, core = MCG, bus = MCG/2, FlexBus = MCG/2, Flash clock= MCG/4
       vfnSetSysDividers(CLKDIV_1,CLKDIV_2,CLKDIV_4);
        // Set the VCO divider and enable the PLL for 100MHz, LOLIE=0, PLLS=1, CME=0, VDIV=26
        MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV0(26); //VDIV = 26 (x50)
        u32PllFreq = 100000;
        break;
      case PLL96:
        // Set system options dividers
        //MCG=PLL, core = MCG, bus = MCG/2, FlexBus = MCG/2, Flash clock= MCG/4
        vfnSetSysDividers(CLKDIV_1,CLKDIV_2,CLKDIV_4);
        // Set the VCO divider and enable the PLL for 96MHz, LOLIE=0, PLLS=1, CME=0, VDIV=24
        MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV0(24); //VDIV = 24 (x48)
        u32PllFreq = 96000;
        break;
     case PLL48:
        // Set system options dividers
        //MCG=PLL, core = MCG, bus = MCG, FlexBus = MCG, Flash clock= MCG/2
         vfnSetSysDividers(CLKDIV_1,CLKDIV_1,CLKDIV_2);
        // Set the VCO divider and enable the PLL for 48MHz, LOLIE=0, PLLS=1, CME=0, VDIV=0
        MCG_C6 = MCG_C6_PLLS_MASK; //VDIV = 0 (x24)
        u32PllFreq = 48000;
        break;
    }
    while (!(MCG_S & MCG_S_PLLST_MASK)){}; // wait for PLL status bit to set

    while (!(MCG_S & MCG_S_LOCK0_MASK)){}; // Wait for LOCK bit to set

  /****************************** PBE to PEE **********************************/

  // Transition into PEE by setting CLKS to 0
  // CLKS=0, FRDIV=3, IREFS=0, IRCLKEN=0, IREFSTEN=0
    MCG_C1 &= ~MCG_C1_CLKS_MASK;

  // Wait for clock status bits to update
    while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x3){};

  /******************************** PEE ***************************************/
    
    gu8ActualClkMode = MCG_PEE;
    
    gu32McgFreqKhz = u32PllFreq;
    vfnUpdateClkFreq();
    
    return MCG_OK;
  } //pll_init

  /*****************************************************************************
  * Function: u8McgPee2Blpi
  *
  * Description: Moves from PEE to BLPI
  *
  * Inputs:
  *     -u8ClkValue: Input clock value (i.e. XTAL4 for a 4MHz XTAL)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgPee2Blpi(unsigned char u8ClkValue)
  {
      unsigned char u8TempReg;
      // Transition from PEE to BLPI: PEE -> PBE -> FBE -> FBI -> BLPI
      
      if (gu8ActualClkMode != MCG_PEE)
      return MCG_ERR;  //Error, MCG must be in PEE to execute this function. 
    
      // Step 1: PEE -> PBE
      MCG_C1 |= MCG_C1_CLKS(2);  // System clock from external reference OSC, not PLL.
      while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2){};  // Wait for clock status to update.
      
      // Step 2: PBE -> FBE
      MCG_C6 &= ~MCG_C6_PLLS_MASK;  // Clear PLLS to select FLL, still running system from ext OSC.
      while (MCG_S & MCG_S_PLLST_MASK){};  // Wait for PLL status flag to reflect FLL selected.
      
      // Step 3: FBE -> FBI
      MCG_C2 &= ~MCG_C2_LP_MASK;  // FLL remains active in bypassed modes.
      MCG_C2 |= MCG_C2_IRCS_MASK;  // Select fast internal reference
      u8TempReg = MCG_C1;
      u8TempReg &= ~(MCG_C1_CLKS_MASK | MCG_C1_IREFS_MASK);
      u8TempReg |= (MCG_C1_CLKS(1) | MCG_C1_IREFS_MASK);  // Select internal reference (fast IREF clock) as MCG clock source.
      MCG_C1 = u8TempReg;
    
      while (MCG_S & MCG_S_IREFST_MASK){};  // Wait for Reference Status bit to update.
      while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x1){};  // Wait for clock status bits to update
      
      // Step 4: FBI -> BLPI
      MCG_C1 |= MCG_C1_IREFSTEN_MASK;  // Keep internal reference clock running in STOP modes.
      MCG_C2 |= MCG_C2_LP_MASK;  // FLL remains disabled in bypassed modes.
      while (!(MCG_S & MCG_S_IREFST_MASK)){};  // Wait for Reference Status bit to update.
      while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x1){};  // Wait for clock status bits to update.
    
      gu8ActualClkMode = MCG_BLPI;
      
      gu32McgFreqKhz = ((u8ClkValue+1)*2000);
      vfnUpdateClkFreq();
      
      return MCG_OK;
  } // end MCG PEE to BLPI
  
  /*****************************************************************************
  * Function: u8McgBlpi2Pee
  *
  * Description: Moves from BLPI to PEE
  *
  * Inputs:
  *     -u8PlloutOption: PLL frequency desired (i.e. "PLL48" for 48MHz)
  *     -u8OscSource: Oscillator source (i.e. MCG_XTAL)
  *     -u8ClkValue: Input clock value (i.e. XTAL4 for a 4MHz XTAL)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgBlpi2Pee(unsigned char u8PlloutOption, unsigned char u8OscSource, unsigned char u8ClkValue)
  {
      unsigned char u8TempReg;
      unsigned char u8Error;
      // Transition from BLPI to PEE: BLPI -> FBI -> FEI -> FBE -> PBE -> PEE
      
      if (gu8ActualClkMode != MCG_BLPI)
        return MCG_ERR;  //Error, MCG must be in BLPI to execute this function. 
    
      // Step 1: BLPI -> FBI
      MCG_C2 &= ~MCG_C2_LP_MASK;  // FLL remains active in bypassed modes.
      while (!(MCG_S & MCG_S_IREFST_MASK)){};  // Wait for Reference Status bit to update.
      while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x1){};  // Wait for clock status bits to update
      
      // Step 2: FBI -> FEI
      MCG_C2 &= ~MCG_C2_LP_MASK;  // FLL remains active in bypassed modes.
      u8TempReg = MCG_C2;  // assign temporary variable of MCG_C2 contents
      u8TempReg &= ~MCG_C2_RANGE0_MASK;  // set RANGE field location to zero
      u8TempReg |= (0x2 << 0x4);  // OR in new values
      MCG_C2 = u8TempReg;  // store new value in MCG_C2
      MCG_C4 = 0x0E;  // Low-range DCO output (~10MHz bus).  FCTRIM=%0111.
      MCG_C1 = 0x04;  // Select internal clock as MCG source, FRDIV=%000, internal reference selected.
   
      while (!(MCG_S & MCG_S_IREFST_MASK)){};   // Wait for Reference Status bit to update 
      while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x0){}; // Wait for clock status bits to update
      
      // Handle FEI to PEE transitions using standard clock initialization routine.
      u8Error = u8PllInit(u8PlloutOption, u8OscSource, u8ClkValue); 
      
      //PllInit function will update the clock values
      
      if(u8Error)
      return MCG_ERR;
      
      return MCG_OK;
  } // end MCG BLPI to PEE

  /*****************************************************************************
  * Function: u8McgFee2Fei
  *
  * Description: Moves from FEE to FEI
  *
  * Inputs:
  *     -u8DrsVal: Value for DRS register (see reference manual)
  *     -u8Dmx32Val: Value for DMX32 register (see reference manual)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgFee2Fei(unsigned char u8DrsVal, unsigned char u8Dmx32Val)
  {
    unsigned char u8TempReg;
    
    if (gu8ActualClkMode != MCG_FEE)
      return MCG_ERR;  //Error, MCG must be in FEI to execute this function. 
    
    // first ensure clock monitor is disabled otherwise a loss of clock reset will occur
    MCG_C6 &= ~MCG_C6_CME0_MASK;
    
    MCG_C1 |= MCG_C1_IREFS_MASK; // select internal reference
    
    // wait for Reference clock to switch to internal reference 
    while (!(MCG_S & MCG_S_IREFST_MASK)){}
    
    // Select the system oscillator as the MCG reference clock.
    // Not typically requred to set this as default is system oscillator.
    // This is not required as part of moving to FEI but is performed here to ensure the system
    // oscillator is used in future mode changes.
    MCG_C7 &= ~MCG_C7_OSCSEL_MASK;
    
    u8TempReg = MCG_C4;
    u8TempReg &= ~(MCG_C4_DMX32_MASK | MCG_C4_DRST_DRS_MASK); // clear the DMX32 bit and DRS field
    u8TempReg |= (MCG_C4_DRST_DRS(u8DrsVal) | (u8Dmx32Val << MCG_C4_DMX32_SHIFT)); // select DRS range and dmx32 setting
    MCG_C4 = u8TempReg;
    
    gu32McgFreqKhz = vfnFllFreq(MCG_SLOW_IRC_HZ);
    vfnUpdateClkFreq();
    
    gu8ActualClkMode = MCG_FEI;
    
    return MCG_OK;
  } // fee_fei


  /*****************************************************************************
  * Function: u8McgFei2Fbe
  *
  * Description: Moves from FEI to FBE
  *
  * Inputs:
  *     -u8OscSource: Oscillator source (i.e. MCG_XTAL)
  *     -u8ClkValue: Input clock value (i.e. XTAL4 for a 4MHz XTAL)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgFei2Fbe(unsigned char u8OscSource, unsigned char u8ClkValue)
  {
    
    if (gu8ActualClkMode != MCG_FEI)
      return MCG_ERR;  //Error, MCG must be in FEI to execute this function. 
    
    if(u8OscSource > MCG_EXTCLK)
      return MCG_ERR;

    if(u8OscSource == MCG_XTAL && u8ClkValue > XTAL32)
      return MCG_ERR;

    if(u8OscSource == MCG_EXTCLK && u8ClkValue > CLK50)
      return MCG_ERR;

    if (u8OscSource == MCG_EXTCLK)
    {
      MCG_C2 = 0;
      
     // after initialization of oscillator release latched state of oscillator and GPIO
      PMC_REGSC |= PMC_REGSC_ACKISO_MASK;
    }
    else if(u8ClkValue >= XTAL8)
    {
    // Enable external oscillator, RANGE=2, HGO=1, EREFS=1, LP=0, IRCS=0
      MCG_C2 = MCG_C2_RANGE0(2) | MCG_C2_HGO0_MASK | MCG_C2_EREFS0_MASK;
    }
    else
    {
    // Enable external oscillator, RANGE=1, HGO=1, EREFS=1, LP=0, IRCS=0
      MCG_C2 = MCG_C2_RANGE0(1) | MCG_C2_HGO0_MASK | MCG_C2_EREFS0_MASK;
    }

    // Select external oscilator and Reference Divider and clear IREFS to start ext osc
    // CLKS=2, FRDIV=3, IREFS=0, IRCLKEN=0, IREFSTEN=0
    MCG_C1 = MCG_C1_CLKS(2) | MCG_C1_FRDIV(3);

    /* if we aren't using an osc input we don't need to wait for the osc to init */
    if (u8OscSource == MCG_XTAL)
    {
      while (!(MCG_S & MCG_S_OSCINIT0_MASK)){};  // wait for oscillator to initialize
    }

    while (MCG_S & MCG_S_IREFST_MASK){}; // wait for Reference clock Status bit to clear

    while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2){}; // Wait for clock status bits to show clock source

    gu8ActualClkMode = MCG_FBE;

    gu32McgFreqKhz = ((u8ClkValue+1)*2000);
    vfnUpdateClkFreq();

    return MCG_OK;
  }

  /*****************************************************************************
  * Function: u8McgFbe2Pbe
  *
  * Description: Moves from FBE to PBE
  *
  * Inputs:
  *     -u8PrdivVal: PRDIV register value (see reference manual)
  *     -u8VdivVal: VDIV register value (see reference manual)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgFbe2Pbe(unsigned char u8PrdivVal, unsigned char u8VdivVal)
  { 
    if (gu8ActualClkMode != MCG_FBE)
      return MCG_ERR;  //Error, MCG must be in FBE to execute this function. 
    
    MCG_C5 = MCG_C5_PRDIV0(u8PrdivVal);    //set PLL ref divider

    // the PLLS bit is set to enable the PLL, MCGOUT still sourced from ext ref clk  
    MCG_C6 = MCG_C6_PLLS_MASK | MCG_C6_VDIV0(u8VdivVal);

    while (!(MCG_S & MCG_S_PLLST_MASK)){}; // wait for PLL status bit to set

    while (!(MCG_S & MCG_S_LOCK0_MASK)){}; // Wait for LOCK bit to set
    // now in PBE
    
    gu8ActualClkMode = MCG_PBE;
    
    // PBE frequency = FBE frequency, no need to change frequency
    return MCG_OK;
  }

  /*****************************************************************************
  * Function: u8McgPbe2Pee
  *
  * Description: Moves from PBE to PEE
  *
  * Inputs:
  *     -u8ClkValue: Input clock value (i.e. XTAL4 for a 4MHz XTAL)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgPbe2Pee(unsigned char u8ClkValue)
  {
    unsigned char u8Prdiv, u8Vdiv;  
    
    if (gu8ActualClkMode != MCG_PBE)
      return MCG_ERR;  //Error, MCG must be in PBE to execute this function. 
    
    MCG_C1 &= ~MCG_C1_CLKS_MASK; // switch CLKS mux to select PLL as MCG_OUT
    // Wait for clock status bits to update 
    while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x3){} 

    u8Prdiv = ((MCG_C5 & MCG_C5_PRDIV0_MASK) + 1);
    u8Vdiv = ((MCG_C6 & MCG_C6_VDIV0_MASK) + 24);
    
    gu8ActualClkMode = MCG_PEE;
    
    gu32McgFreqKhz = (((((u8ClkValue + 1) * 2)/u8Prdiv) * u8Vdiv) * 1000);
    vfnUpdateClkFreq();
    
    return MCG_OK;
  }

  /*****************************************************************************
  * Function: u8McgPee2Pbe
  *
  * Description: Moves from PEE to PBE
  *
  * Inputs:
  *     -u8ClkValue: Input clock value (i.e. XTAL4 for a 4MHz XTAL)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgPee2Pbe(unsigned char u8ClkValue)
  {  
    if (gu8ActualClkMode != MCG_PEE)
      return MCG_ERR;  //Error, MCG must be in PEE to execute this function. 
    
    MCG_C1 |= MCG_C1_CLKS(2); // switch CLKS mux to select external reference clock as MCG_OUT
    // Wait for clock status bits to update 
    
    while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2){}; 
    
    gu8ActualClkMode = MCG_PBE;
    
    gu32McgFreqKhz = ((u8ClkValue + 1) * 2000);
    vfnUpdateClkFreq();
    
    return MCG_OK;
  } // pee_pbe, freq = REF_CLK

  /*****************************************************************************
  * Function: u8McgPbe2Fbe
  *
  * Description: Moves from PBE to FBE
  *
  * Inputs: (none)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgPbe2Fbe (void)
  {
   if (gu8ActualClkMode != MCG_PBE)
      return MCG_ERR;  //Error, MCG must be in PBE to execute this function. 
    
    MCG_C6 &= ~MCG_C6_PLLS_MASK; // clear PLLs to disable PLL, still clocked from ext ref clk
    
    while (MCG_S & MCG_S_PLLST_MASK){}; // wait for PLLS status bit to set
  // FBE frequency = PBE frequency, no need to change frequency  
    
    gu8ActualClkMode = MCG_PBE;
    
    return MCG_OK;
  } 

  /*****************************************************************************
  * Function: u8McgFbe2Fbi
  *
  * Description: Moves from FBE to FEI
  *
  * Inputs:
  *     -u8IrcOption: Desired internal reference clock (i.e. MCG_IRC_FAST for fast IRC)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgFbe2Fbi(unsigned char u8IrcOption)
  {
    unsigned char u8TempReg;
    
    if (gu8ActualClkMode != MCG_FBE)
      return MCG_ERR;  //Error, MCG must be in FBE to execute this function. 
    
    if(u8IrcOption > MCG_IRC_SLOW)
      return MCG_ERR;
    
    if(u8IrcOption == MCG_IRC_FAST)
    MCG_C2 |= MCG_C2_IRCS_MASK; // select fast IRC by setting IRCS
    
    if(u8IrcOption == MCG_IRC_SLOW)
    MCG_C2 &= ~MCG_C2_IRCS_MASK; // select slow IRC by clearing IRCS  
    
    u8TempReg = MCG_C1;
    u8TempReg &= ~MCG_C1_CLKS_MASK;
    u8TempReg |= MCG_C1_CLKS(1); // select IRC as MCGOUT
    MCG_C1 = u8TempReg; // update MCG_C1
    
    while (!(MCG_S & MCG_S_IRCST_MASK)){}; // wait until internal reference switches to fast clock.
    
    while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x1){}; // Wait for clock status bits to update

    gu8ActualClkMode = MCG_FBI;
    
    if(u8IrcOption == MCG_IRC_FAST)
    gu32McgFreqKhz = (MCG_FAST_IRC_HZ/1000);
    
    if(u8IrcOption == MCG_IRC_SLOW)
    gu32McgFreqKhz = (MCG_SLOW_IRC_HZ/1000);
    
    vfnUpdateClkFreq();
    
    return MCG_OK;
  } 

  /*****************************************************************************
  * Function: u8McgFbi2Blpi
  *
  * Description: Moves from FBI to BLPI
  *
  * Inputs: (none)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgFbi2Blpi(void)
  { 
   if (gu8ActualClkMode != MCG_FBI)
      return MCG_ERR;  //Error, MCG must be in FBI to execute this function. 
    
    MCG_C2 |= MCG_C2_LP_MASK; //set LP bit to disable the FLL 
  // no change in MCGOUT frequency  
    
    gu8ActualClkMode = MCG_BLPI;
    return MCG_OK;
  } 
  
  /*****************************************************************************
  * Function: u8McgFei2Fbi
  *
  * Description: Moves from FEI to FBI
  *
  * Inputs: 
  * 	-u8IrcOption: IRC to use
  * 				  -MCG_IRC_SLOW: Uses slow IRC
  * 				  -MCG_IRC_FAST: Uses fast IRC
  * 				  
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgFei2Fbi (unsigned char u8IrcOption)
  {
	  if(gu8ActualClkMode != MCG_FEI)
		  return MCG_ERR;			//MCG must be in FEI prior enter this function
	  
		//Select IRC as clock source
		MCG_C1 |= MCG_C1_CLKS(1) | MCG_C1_IREFS_MASK;
		
		//Wait for IRC to become the clock source
		while((MCG_S & MCG_S_CLKST_MASK) != MCG_S_CLKST(1));
		
		//Disable PLL selection
		MCG_C6 &= ~MCG_C6_PLLS_MASK;
		
		if(u8IrcOption == MCG_IRC_FAST)
		{
			//Change IRC to Fast IRC
			MCG_C2 |= MCG_C2_IRCS_MASK;
			
			//Wait for fast IRC to become the clock source
			while(!(MCG_S & MCG_S_IRCST_MASK));		
			
			//Update MCG frequency
			gu32McgFreqKhz = (MCG_FAST_IRC_HZ/1000);
		}
		else
		{
			//Change IRC to Slow IRC
			MCG_C2 &= ~MCG_C2_IRCS_MASK;
			
			//Wait for slow IRC to become the clock source
			while((MCG_S & MCG_S_IRCST_MASK));	
			
			//Update MCG frequency
			gu32McgFreqKhz = (MCG_SLOW_IRC_HZ/1000);
		}
		
	    //Update frequency indicators
	    vfnUpdateClkFreq();
		
		gu8ActualClkMode = MCG_FBI;
		
		return MCG_OK;
  }

  /*****************************************************************************
  * Function: u8McgPbe2Blpe
  *
  * Description: Moves from PBE to BLPE
  *
  * Inputs: (none)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgPbe2Blpe (void)
  {
   if (gu8ActualClkMode != MCG_PBE)
      return MCG_ERR;  //Error, MCG must be in PBE to execute this function. 
    
    // Check MCG is in PBE mode
    if (!((((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) == 0x2) && // check CLKS mux has selcted external reference
        (!(MCG_S & MCG_S_IREFST_MASK)) &&                               // check FLL ref is external ref clk
        (MCG_S & MCG_S_PLLST_MASK) &&                                   // check PLLS mux has selected PLL
        (!(MCG_C2 & MCG_C2_LP_MASK))))                                  // check MCG_C2[LP] bit is not set   
    {
      return MCG_ERR;                                                   // return error code
    }

    // To enter BLPE mode the LP bit must be set, disabling the PLL  
    MCG_C2 |= MCG_C2_LP_MASK;

    gu8ActualClkMode = MCG_BLPE;

    // Now in BLPE mode, no change in frequency
    return MCG_OK;  
  } // pbe_blpe
 
  /*****************************************************************************
  * Function: u8McgBlpe2Pbe
  *
  * Description: Moves from BLPE to PBE
  *
  * Inputs: (none)
  *
  * Output:
  *     -unsigned char: Function result
  *                     MCG_OK: Function correctly executed
  *                     MCG_ERR: Error occurred
  ******************************************************************************/
  unsigned char u8McgBlpe2Pbe (void)
  {
   if (gu8ActualClkMode != MCG_BLPE)
      return MCG_ERR;  //Error, MCG must be in BLPE to execute this function. 
    
    MCG_C2 &= ~(MCG_C2_LP_MASK);           // Enable PLL  
    
    while (!(MCG_S & MCG_S_PLLST_MASK)){}; // wait for PLL status bit to set

    while (!(MCG_S & MCG_S_LOCK0_MASK)){}; // Wait for LOCK bit to set
    
    gu8ActualClkMode = MCG_PBE;
    
    return MCG_OK;
    //Now in PBE mode
  }
 
  /*****************************************************************************
  * Function: vfnUpdateClkFreq
  *
  * Description: Updates the variables containing the current frequency values
  *
  * Inputs: (none)
  *
  * Output: (none)
  ******************************************************************************/ 
  void vfnUpdateClkFreq (void)
  {
    gu32CoreFreqKhz = (gu32McgFreqKhz/(((SIM_CLKDIV1&SIM_CLKDIV1_OUTDIV1_MASK)>>SIM_CLKDIV1_OUTDIV1_SHIFT)+1));
    gu32PeriFreqKhz = (gu32McgFreqKhz/(((SIM_CLKDIV1&SIM_CLKDIV1_OUTDIV2_MASK)>>SIM_CLKDIV1_OUTDIV2_SHIFT)+1));
    gu32FlashFreqKhz = (gu32McgFreqKhz/(((SIM_CLKDIV1&SIM_CLKDIV1_OUTDIV4_MASK)>>SIM_CLKDIV1_OUTDIV4_SHIFT)+1));
  }
  
  /*****************************************************************************
  * Function: vfnSetSysDividers
  *
  * Description: Change the clock dividers
  *
  * Inputs:
  *     -u8OutDiv1: Clock divider for Outdiv1 (Core) i.e. CLKDIV_2 for divide by 2
  *     -u8OutDiv2: Clock divider for Outdiv2 (BUS) i.e. CLKDIV_2 for divide by 2
  *     -u8OutDiv4: Clock divider for Outdiv4 (FLASH) i.e. CLKDIV_2 for divide by 2
  *
  * Output: (none)
  ******************************************************************************/ 
  void vfnSetSysDividers(unsigned char u8Outdiv1, unsigned char u8Outdiv2, unsigned char u8Outdiv4)
  {   
    // set clock dividers to desired value  
    SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIV1(u8Outdiv1) | SIM_CLKDIV1_OUTDIV2(u8Outdiv2) 
                 | SIM_CLKDIV1_OUTDIV4(u8Outdiv4);
    
    vfnUpdateClkFreq();
    
  } // set_sys_dividers
  
  /*****************************************************************************
  * Function: vfnFllFreq
  *
  * Description: Update FLL frequency value
  *
  * Inputs:
  *     -i32FllRef: FLL reference
  *
  * Output: (none)
  ******************************************************************************/
  int vfnFllFreq(int i32FllRef)
  {
    int i32FllFreqKhz;
    
    if (MCG_C4 & MCG_C4_DMX32_MASK) // if DMX32 set
    {
      switch ((MCG_C4 & MCG_C4_DRST_DRS_MASK) >> MCG_C4_DRST_DRS_SHIFT) // determine multiplier based on DRS
      {
      case 0:
        i32FllFreqKhz = ((i32FllRef * 732) / 1000);
        break;
      case 1:
        i32FllFreqKhz = ((i32FllRef * 1464) / 1000);
        break;
      case 2:
        i32FllFreqKhz = ((i32FllRef * 2197) / 1000);
        break;
      case 3:
        i32FllFreqKhz = ((i32FllRef * 2929) / 1000);
        break;
      }
    }
    else // if DMX32 = 0
    {
      switch ((MCG_C4 & MCG_C4_DRST_DRS_MASK) >> MCG_C4_DRST_DRS_SHIFT) // determine multiplier based on DRS
      {
      case 0:
        i32FllFreqKhz = ((i32FllRef * 640) / 1000);
        break;
      case 1:
        i32FllFreqKhz = ((i32FllRef * 1280) / 1000);
        break;
      case 2:
        i32FllFreqKhz = ((i32FllRef * 1920) / 1000);
        break;
      case 3:
        i32FllFreqKhz = ((i32FllRef * 2560) / 1000);
        break;
      }
    }    
    return i32FllFreqKhz;  
  } // fll_freq

