/******************************************************************************
 *
 * 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 pmc.c
 *
 * @author  
 *
 * @version 2.0
 *
 * @date Apr-19-2013
 *
 * @brief Power management routines
 *
 *****************************************************************************/

#include "derivative.h"
#include "pmc.h"
#include "mcg.h"
#include "fsl_types.h"
//#include "rtc.h"

/*****************************************************************************
* Function: vfnEnterWait
*
* Description: Puts the processor into wait mode 
*
* Input: (none)
*
* Output (none)
*****************************************************************************/
void vfnEnterWait(void)
{
    wait();
}

/*****************************************************************************
* Function: vfnEnterStop
*
* Description: Puts the processor into normal stop mode 
*
* Input: (none)
*
* Output (none)
*****************************************************************************/
void vfnEnterStop(void)
{
    /* Set the LPLLSM field to 0b000 for normal STOP mode - Need to retain state of LPWUI bit 8 */
    SMC_PMCTRL =  SMC_PMCTRL_STOPM(0);           // set LPLLSM = 0b000
    
    stop();
}

/*****************************************************************************
* Function: u8EnterVlpr
*
* Description: Puts the processor into VLPR mode. MCG must have been previously 
*              configured to be into BLPI or BLPE. Core frequency must be up to
*              4MHz, FLASH frequency must be up to 1MHz
*
* Input:
*       -u8LpwuiValue: Wakeup value
*          0->Interrupt does not affect
*          1->Interrupt puts the MCU in RUN mode 
*
* Output:
*       -unsigned char: Error value
*               PMC_OK->Function successfully executed
*               PMC_ERR->Error occurred
*****************************************************************************/
unsigned char u8EnterVlpr(unsigned char u8LpwuiValue)
{
   if(gu8ActualClkMode != MCG_BLPI && gu8ActualClkMode != MCG_BLPE)
     return PMC_ERR;    //Error, MCG mode must be either BLPI or BLPE to enter this mode
  
   if(gu32CoreFreqKhz > PMC_MAX_VLPR_CORE_FREQ_KHZ)
     return PMC_ERR;    //Error, core frequency is out of spec
   
   if(gu32FlashFreqKhz > PMC_MAX_VLPR_FLASH_FREQ_KHZ)
     return PMC_ERR;    //Error, flash frequency is out of spec
   
   /* Check if not already in VLPR mode */
   if(SMC_PMSTAT & 4)
   {
     return PMC_OK; //Already in VLPR
   }
   
   /* Set the selected LPWUI value */
   vfnSetLpwui(u8LpwuiValue);
      
  /* Write to PMPROT to allow all power modes */
    SMC_PMPROT = 0x2A;
   
  /* Write PMCTRL to go into VLPR */
    SMC_PMCTRL = SMC_PMCTRL_RUNM(2) | SMC_PMCTRL_STOPM(2);
    
  /* Wait for regulators to turn off */
    while(PMC_REGSC & PMC_REGSC_REGONS_MASK);
    
  /* Wait for RUN -> VLPR transition */
    while(!(SMC_PMSTAT & 4));
    
    return PMC_OK;
    /* Now in VLPR mode */
}


/*****************************************************************************
* Function: u8EnterVlpw
*
* Description: Puts the processor into VLPW mode. MCG must have been previously 
*              configured to be into BLPI or BLPE. Core frequency must be up to
*              4MHz, FLASH frequency must be up to 1MHz
*
* Input:
*       -u8LpwuiValue: Wakeup value
*          0->Interrupt does not affect
*          1->Interrupt puts the MCU in RUN mode 
*
* Output:
*       -unsigned char: Error value
*               PMC_OK->Function successfully executed
*               PMC_ERR->Error occurred
*****************************************************************************/
unsigned char u8EnterVlpw(unsigned char u8LpwuiValue)     
{                
  unsigned char u8Error; 
  
   u8Error = u8EnterVlpr(u8LpwuiValue);      //Enter to VLPR, 1 Means: Any interrupt could wake up from VLPR
   
   if(u8Error)
     return PMC_ERR;                         //Error occurred when entering to VLPR
   
   vfnEnterWait();                           //Enter to VLPW
   
   return PMC_OK;                            //Return PMC_OK on wakeup
}                

                

/*****************************************************************************
* Function: u8EnterVlps
*
* Description: Puts the processor into VLPS mode. MCG must have been previously 
*              configured to be into BLPI or BLPE. Core frequency must be up to
*              4MHz, FLASH frequency must be up to 1MHz
*
* Input:
*       -u8LpwuiValue: Wakeup value
*          0->Interrupt does not affect
*          1->Interrupt puts the MCU in RUN mode 
*
* Output:
*       -unsigned char: Error value
*               PMC_OK->Function successfully executed
*               PMC_ERR->Error occurred
*****************************************************************************/
unsigned char u8EnterVlps(unsigned char u8LpwuiValue)
{
    if(gu8ActualClkMode != MCG_BLPI && gu8ActualClkMode != MCG_BLPE)
     return PMC_ERR;    //Error, MCG mode must be either BLPI or BLPE to enter this mode

    if(gu32CoreFreqKhz > PMC_MAX_VLPR_CORE_FREQ_KHZ)
     return PMC_ERR;    //Error, core frequency is to big for this power mode

    if(gu32FlashFreqKhz > PMC_MAX_VLPR_FLASH_FREQ_KHZ)
     return PMC_ERR;    //Error, flash frequency is to big for this power mode

    vfnClockMonitor(OFF);

    vfnJtagTdoPullUpEnable();  
    
    /* Set the selected LPWUI value */
    vfnSetLpwui(u8LpwuiValue);
        
    /* Write to PMPROT to allow LLS power modes */
    SMC_PMPROT = 0x2A;   //Allow enter to all power modes

    /* Reset SMC_PMCTRL to avoid errors */
     SMC_PMCTRL = 0;

    /* Set the LPLLSM field to 0b010 for VLPS mode - Need to set state of LPWUI bit 8 */
     #if (!defined(MCU_MK51D7))
     SMC_PMCTRL = (SMC_PMCTRL & (SMC_PMCTRL_RUNM_MASK |~SMC_PMCTRL_LPWUI_MASK)) |
                  SMC_PMCTRL_STOPM(0x2);
     #else
     SMC_PMCTRL = (SMC_PMCTRL & SMC_PMCTRL_RUNM_MASK |
                  SMC_PMCTRL_STOPM(0x2));
     #endif

    /* Now execute the stop instruction to go into VLPS */
    stop();
    
    /* Return PMC_OK on wakeup */
    return PMC_OK;
}


/*****************************************************************************
* Function: u8ExitVlpr
*
* Description: Puts the processor into NORMAL RUN mode. This function does not
*              return the MCG configuration to itīs previous state
*
* Input: (none)
*
* Output:
*       -unsigned char: Error value
*               PMC_OK->Function successfully executed
*               PMC_ERR->Error occurred
*****************************************************************************/
unsigned char u8ExitVlpr(void)
{
    /* Clear RUNM */
    SMC_PMCTRL &= ~(SMC_PMCTRL_RUNM(0x3));
                   
    /* Wait for normal RUN regulation mode to be confirmed */                   
    while (!(SMC_PMSTAT & SMC_PMSTAT_PMSTAT(1<<0))); 
           
    while(!(PMC_REGSC & PMC_REGSC_REGONS_MASK)); 
    
    return PMC_OK;
}

/*****************************************************************************
* Function: u8EnterLls
*
* Description: Puts the processor into LLS mode. 
*              IMPORTANT: LLWU must be configured prior enter this function.
*              LLWU is the wakeup method for LLS             
*
* Input:(none)
*
* Output:
*       -unsigned char: Error value
*               PMC_OK->Function successfully executed
*               PMC_ERR->Error occurred
*****************************************************************************/
unsigned char u8EnterLls(void)
{
    /* Write to PMPROT to allow LLS power modes */
    SMC_PMPROT = 0x2A;   //Allow enter to all power modes

    /* Reset SMC_PMCTRL to avoid errors */
    SMC_PMCTRL = 0;
        
    /* Set the LPLLSM field to 0b011 for LLS mode  */
    SMC_PMCTRL  =  SMC_PMCTRL_STOPM(3);           // set LPLLSM = 0b11
        
    /* Now execute the stop instruction to go into LLS */
    stop();
    
    /* Return PMC_OK on wakeup*/
    return PMC_OK;
}

/*****************************************************************************
* Function: u8EnterVlls
*
* Description: Puts the processor into the selected VLLS mode. 
*              IMPORTANT: LLWU must be configured prior enter this function.
*              LLWU is the wakeup method for VLLS modes             
*
* Input:
*       -u8VllsMode: VLLS mode to enter (i.e. PMC_VLLS3 enters VLLS3 mode)
*
* Output:
*       -unsigned char: Error value
*               PMC_OK->Function successfully executed
*               PMC_ERR->Error occurred
*****************************************************************************/
unsigned char u8EnterVlls(unsigned char u8VllsMode)
{
    if(u8VllsMode > PMC_VLLS3)
      return PMC_ERR;           //Error, unknown VLLS mode
    
    /* Write to PMPROT to allow VLLS3 power modes */
    SMC_PMPROT = 0x2A;   //Allow enter to all power modes
        
    /* Reset SMC_PMCTRL to avoid errors */
    SMC_PMCTRL = 0;
    
    /* Set the LPLLSM field to 0b101 for VLLS3 mode  */
    SMC_PMCTRL =  SMC_PMCTRL_STOPM(4);           // set STOPM = 0b100 VLLSx
    SMC_VLLSCTRL = SMC_VLLSCTRL_VLLSM(u8VllsMode);       // select VLLS
    
    /* Now execute the stop instruction to go into VLLS */
    stop();
    
    return PMC_OK;
}

/********************************************************************/
/* Enable low power wake up on interrupt. This function can be used
 * to set the LPWUI bit. When this bit is set VLPx modes will exit
 * to normal run mode. When this bit is cleared VLPx modes will exit
 * to VLPR mode.
 *
 * Parameters:
 *      -u8Set: 1-->Enable LPWUI  0-->Disable LPWUI
 */

void vfnSetLpwui(unsigned char u8Set)
{
  #if (!defined(MCU_MK51D7))
  if(u8Set)
    SMC_PMCTRL |= SMC_PMCTRL_LPWUI_MASK;
  else
    SMC_PMCTRL &= ~SMC_PMCTRL_LPWUI_MASK;
  #endif
}


/*****************************************************************************
* Function: vfnJtagTdoPullUpEnable
*
* Description: Configure JTAG TDO pull-up             
*
* Input:(none)
*
* Output:(none)
*****************************************************************************/
void vfnJtagTdoPullUpEnable(void)
{
    PORTA_PCR2 = PORT_PCR_PE_MASK | PORT_PCR_PS_MASK;//JTAG_TDO      
}

/********************************************************************/
/* vfnClockMonitor turns on or off all the MCG clock monitors
 *
 * Parameters:
 * u8State (1 = ON, 0 = OFF)
 */
void vfnClockMonitor(unsigned char u8State)
{
    if(u8State)
    {
      MCG_C6 |= MCG_C6_CME0_MASK;
      MCG_C8 |= MCG_C8_CME1_MASK;
    }    
    else
    {
      MCG_C6 &= ~MCG_C6_CME0_MASK;
      MCG_C8 &= ~MCG_C8_CME1_MASK;
    }
}

/***********************************************************************/
/*
 * Configures the ARM system control register for STOP (deep sleep) mode
 * and then executes the WFI instruction to enter the mode.
 *
 * Parameters:
 * none
 *
 * Note: Might want to change this later to allow for passing in a parameter
 *       to optionally set the sleep on exit bit.
 */

void stop (void)
{
	/* Set the SLEEPDEEP bit to enable deep sleep mode (STOP) */
	SCB_SCR |= SCB_SCR_SLEEPDEEP_MASK;	

	/* WFI instruction will start entry into STOP mode */
	asm("WFI");
}

/***********************************************************************/
/*
 * Configures the ARM system control register for WAIT (sleep) mode
 * and then executes the WFI instruction to enter the mode.
 *
 * Parameters:
 * none
 *
 * Note: Might want to change this later to allow for passing in a parameter
 *       to optionally set the sleep on exit bit.
 */

void wait (void)
{
	/* Clear the SLEEPDEEP bit to make sure we go into WAIT (sleep) mode instead
	 * of deep sleep.
	 */
	SCB_SCR &= ~SCB_SCR_SLEEPDEEP_MASK;	

	/* WFI instruction will start entry into WAIT mode */
	asm("WFI");
}

/* End of file */
