/*******************************************************************************
* File Name: cyPm.c
* Version 4.20
*
*  Description:
*   Provides an API for the power management.
*
*  Note:
*   Documentation of the API's in this file is located in the
*   System Reference Guide provided with PSoC Creator.
*
********************************************************************************
* Copyright 2011-2014, Cypress Semiconductor Corporation.  All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/

#include "cyPm.h"
#include "CyLib.h"


/*******************************************************************************
* Function Name: CySysPmSleep
********************************************************************************
*
* Summary:
*  Puts the part into the Sleep state. This is a CPU-centric power mode.
*  It means that the CPU has indicated that it is in the sleep mode and
*  its main clock can be removed. It is identical to Active from a peripheral
*  point of view. Any enabled interrupts can cause wakeup from the  Sleep mode.
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void CySysPmSleep(void)
{
    uint8 interruptState;

    interruptState = CyEnterCriticalSection();

    /* CM0 enters Sleep mode upon execution of WFI */
    CY_PM_CM0_SCR_REG &= (uint32) (~CY_PM_CM0_SCR_SLEEPDEEP);

    /* Sleep and wait for interrupt */
    CY_PM_WFI;

    CyExitCriticalSection(interruptState);
}


/*******************************************************************************
* Function Name: CySysPmDeepSleep
********************************************************************************
*
* Summary:
*  Puts the part into the Deep Sleep state. If the firmware attempts to enter
*  this mode before the system is ready (that is, when
*  PWR_CONTROL.LPM_READY = 0), then the device will go into the Sleep mode
*  instead and automatically enter the originally intended mode when the
*  holdoff expires.
*
*  The wakeup occurs when an interrupt is received from a DeepSleep or
*  Hibernate peripheral. For more details, see a corresponding
*  peripheral's datasheet.
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void CySysPmDeepSleep(void)
{
    uint8 interruptState;
    #if(CY_IP_SRSSV2)
        volatile uint32 clkSelectReg;
    #endif /* (CY_IP_SRSSV2) */

    interruptState = CyEnterCriticalSection();

    #if(CY_IP_SRSSV2)
        /* Device enters DeepSleep mode when CPU asserts SLEEPDEEP signal */
        CY_PM_PWR_CONTROL_REG &= (uint32) (~CY_PM_PWR_CONTROL_HIBERNATE);
    #endif /* (CY_IP_SRSSV2) */

    #if (CY_PSOC4_4100 || CY_PSOC4_4200)
        CY_PM_CPUSS_CONFIG_REG |= CY_PM_CPUSS_CONFIG_FLSH_ACC_BYPASS;
    #endif /* (CY_PSOC4_4100 || CY_PSOC4_4200) */

    /* Adjust delay to wait for references to settle on wakeup from Deep Sleep */
    CY_PM_PWR_KEY_DELAY_REG = CY_SFLASH_DPSLP_KEY_DELAY_REG;

    /* CM0 enters DeepSleep/Hibernate mode upon execution of WFI */
    CY_PM_CM0_SCR_REG |= CY_PM_CM0_SCR_SLEEPDEEP;

    #if(CY_IP_SRSSV2)
        /* Preserve system clock configuration and
        * reduce sysclk to <=12 MHz (Cypress ID #158710, #179888).
        */
        clkSelectReg = CY_SYS_CLK_SELECT_REG;
        CySysClkWriteSysclkDiv(CY_SYS_CLK_SYSCLK_DIV4);
    #endif /* (CY_IP_SRSSV2) */

    /* Sleep and wait for interrupt */
    CY_PM_WFI;

    #if(CY_IP_SRSSV2)
        /* Restore system clock configuration */
        CY_SYS_CLK_SELECT_REG = clkSelectReg;
    #endif /* (CY_IP_SRSSV2) */

    #if (CY_PSOC4_4100 || CY_PSOC4_4200)
        CY_PM_CPUSS_CONFIG_REG &= (uint32) (~CY_PM_CPUSS_CONFIG_FLSH_ACC_BYPASS);
    #endif /* (CY_PSOC4_4100 || CY_PSOC4_4200) */

    CyExitCriticalSection(interruptState);
}


#if(CY_IP_SRSSV2)

    /*******************************************************************************
    * Function Name: CySysPmHibernate
    ********************************************************************************
    *
    * Summary:
    *  Puts the part into the Hibernate state. Only SRAM and UDBs are retained;
    *  most internal supplies are off. Wakeup is possible from a pin or a hibernate
    *  comparator only.
    *
    *  It is expected that the firmware has already frozen the IO-Cells using
    *  CySysPmFreezeIo() function before the call to this function. If this is
    *  omitted, the IO-cells will be frozen in the same way as they are
    *  in the Active to Deep Sleep transition, but will lose their state on wake up
    *  (because of the reset occurring at that time).
    *
    *  Because all the CPU state is lost, the CPU will start up at the reset vector.
    *  To save the firmware state through the Hibernate low power mode, a
    *  corresponding variable should be defined with CY_NOINIT attribute. It
    *  prevents data from being initialized to zero on startup. The interrupt
    *  cause of the hibernate peripheral is retained, such that it can be either
    *  read by the firmware or cause an interrupt after the firmware has booted and
    *  enabled the corresponding interrupt. To distinguish the wakeup from
    *  the Hibernate mode and the general Reset event, the CySysPmGetResetReason()
    *  function could be used.
    *
    * Parameters:
    *  None
    *
    * Return:
    *  None
    *
    *******************************************************************************/
    void CySysPmHibernate(void)
    {
        uint8 interruptState;

        interruptState = CyEnterCriticalSection();

        /* Device enters Hibernate mode when CPU asserts SLEEPDEEP signal */
        CY_PM_PWR_CONTROL_REG |= CY_PM_PWR_CONTROL_HIBERNATE;

        /* Adjust delay to wait for references to settle on wakeup from hibernate */
        CY_PM_PWR_KEY_DELAY_REG = CY_SFLASH_HIB_KEY_DELAY_REG;

        /* CM0 enters DeepSleep/Hibernate mode upon execution of WFI */
        CY_PM_CM0_SCR_REG |= CY_PM_CM0_SCR_SLEEPDEEP;

        /* Save token that will retain through a STOP/WAKEUP sequence
         * thus could be used by CySysPmGetResetReason() to differentiate
         * WAKEUP from a general RESET event.
         */
        CY_PM_PWR_STOP_REG = (CY_PM_PWR_STOP_REG & (uint32)(~CY_PM_PWR_STOP_TOKEN_MASK)) | CY_PM_PWR_STOP_TOKEN_HIB;

        /* Sleep and wait for interrupt. Wakeup from Hibernate is performed
         * through RESET state, causing a normal Boot procedure to occur.
         * The WFI instruction doesn't put the core to sleep if its wake condition
         * is true when the instruction is executed.
         */
        CY_PM_WFI;

        CyExitCriticalSection(interruptState);
    }


    /*******************************************************************************
    * Function Name: CySysPmStop
    ********************************************************************************
    *
    * Summary:
    *  Puts the part into the Stop state. All internal supplies are off;
    *  no state is retained.
    *
    *  Wakeup from Stop is performed by toggling the wakeup pin, causing
    *  a normal boot procedure to occur. To configure the wakeup pin,
    *  the Digital Input Pin component should be placed on the schematic,
    *  assigned to the wakeup pin, and resistively pulled up or down to the inverse
    *  state of the wakeup polarity. To distinguish the wakeup from the Stop mode
    *  and the general Reset event, CySysPmGetResetReason() function could be used.
    *  The wakeup pin is active low by default. The wakeup pin polarity
    *  could be changed with the CySysPmSetWakeupPolarity() function.
    *
    *  This function freezes IO cells implicitly. It is not possible to enter
    *  the STOP mode before freezing the IO cells. The IO cells remain frozen after
    *  awake from the Stop mode until the firmware unfreezes them after booting
    *  explicitly with CySysPmUnfreezeIo() function call.
    *
    * Parameters:
    *  None
    *
    * Return:
    *  None
    *
    *******************************************************************************/
    void CySysPmStop(void)
    {
        (void) CyEnterCriticalSection();

        /* Update token to indicate Stop mode transition. Preserve only polarity. */
        CY_PM_PWR_STOP_REG = (CY_PM_PWR_STOP_REG & CY_PM_PWR_STOP_POLARITY) | CY_PM_PWR_STOP_TOKEN_STOP;

        /* Freeze IO-Cells to save IO-Cell state */
        CySysPmFreezeIo();

        /* Initiates transition to Stop state */
        CY_PM_PWR_STOP_REG = CY_PM_PWR_STOP_REG | CY_PM_PWR_STOP_STOP;

        /* Depending on the clock frequency and internal timing delays,
         * the final AHB transaction may or may not complete. To guard against
         * accidentally executing an unintended instruction, it is recommended
         * to add 2 NOP cycles after the final write to the STOP register.
         */
        CY_NOP;
        CY_NOP;

        /* Should never get to this WFI instruction */
        CY_PM_WFI;

        /* Wakeup from Stop is performed by toggling of Wakeup pin,
         * causing a normal Boot procedure to occur. No need to exit
         * from the critical section.
         */
    }


    /*******************************************************************************
    * Function Name: CySysPmSetWakeupPolarity
    ********************************************************************************
    *
    * Summary:
    *  Wake up from the stop mode is performed by toggling the wakeup pin,
    *  causing a normal boot procedure to occur. This function assigns
    *  the wakeup pin active level. Setting the wakeup pin to this level will cause
    *  the wakeup from stop mode. The wakeup pin is active low by default.
    *
    * Parameters:
    *  polarity: Wakeup pin active level:
    *   Value    Define                          Level
    *    0        CY_PM_STOP_WAKEUP_ACTIVE_LOW    Logical zero will wakeup the chip
    *    1        CY_PM_STOP_WAKEUP_ACTIVE_HIGH   Logical one will wakeup the chip
    *
    * Return:
    *  None
    *
    *******************************************************************************/
    void CySysPmSetWakeupPolarity(uint32 polarity)
    {
        uint8 interruptState;

        interruptState = CyEnterCriticalSection();

        CY_PM_PWR_STOP_REG = (CY_PM_STOP_WAKEUP_ACTIVE_LOW != polarity) ?
                            (CY_PM_PWR_STOP_REG | CY_PM_PWR_STOP_POLARITY) :
                            (CY_PM_PWR_STOP_REG & (uint32) (~CY_PM_PWR_STOP_POLARITY));

        CyExitCriticalSection(interruptState);
    }


    /*******************************************************************************
    * Function Name: CySysPmGetResetReason
    ********************************************************************************
    *
    * Summary:
    *  Retrieves the last reset reason - transition from OFF/XRES/STOP/HIBERNATE to
    *  the RESET state. Note that waking up from STOP using XRES will be perceived
    *  as a general RESET.
    *
    * Parameters:
    *  None
    *
    * Return:
    *  Reset reason.
    *  CY_PM_RESET_REASON_UNKN        - unknown
    *  CY_PM_RESET_REASON_XRES        - transition from OFF/XRES to RESET
    *  CY_PM_RESET_REASON_WAKEUP_HIB  - transition/wakeup from HIBERNATE to RESET
    *  CY_PM_RESET_REASON_WAKEUP_STOP - transition/wakeup from STOP to RESET
    *
    *******************************************************************************/
    uint32 CySysPmGetResetReason(void)
    {
        uint32 reason = CY_PM_RESET_REASON_UNKN;

        switch(CY_PM_PWR_STOP_REG & CY_PM_PWR_STOP_TOKEN_MASK)
        {
        /* Power up, XRES */
        case CY_PM_PWR_STOP_TOKEN_XRES:
            reason = CY_PM_RESET_REASON_XRES;
            break;

        /* Wakeup from Hibernate */
        case CY_PM_PWR_STOP_TOKEN_HIB:
            reason = CY_PM_RESET_REASON_WAKEUP_HIB;
            break;

        /* Wakeup from Stop (through WAKEUP pin assert) */
        case CY_PM_PWR_STOP_TOKEN_STOP:
            reason = CY_PM_RESET_REASON_WAKEUP_STOP;
            break;

        /* Unknown reason */
        default:
            break;
        }

        return (reason);
    }


    /*******************************************************************************
    * Function Name: CySysPmFreezeIo
    ********************************************************************************
    *
    * Summary:
    *  Freezes IO-Cells directly to save the IO-Cell state on wake up from the
    *  Hibernate or Stop state. It is not required to call this function before
    *  entering the Stop mode, since CySysPmStop() function freezes IO-Cells
    *  implicitly.
    *
    * Parameters:
    *  None
    *
    * Return:
    *  None
    *
    *******************************************************************************/
    void CySysPmFreezeIo(void)
    {
        uint8 interruptState;

        interruptState = CyEnterCriticalSection();

        /* Check FREEZE state to avoid recurrent IO-Cells freeze attempt,
         * since the second call to this function will cause accidental switch
         * to the STOP mode (the system will enter STOP mode immediately after
         * writing to STOP bit since both UNLOCK and FREEZE have been set correctly
         * in a previous call to this function).
         */
        if (0u == (CY_PM_PWR_STOP_REG & CY_PM_PWR_STOP_FREEZE))
        {
            /* Preserve last reset reason and disable overrides the next freeze command by peripherals */
            CY_PM_PWR_STOP_REG = CY_PM_PWR_STOP_STOP | CY_PM_PWR_STOP_FREEZE | CY_PM_PWR_STOP_UNLOCK |
                                (CY_PM_PWR_STOP_REG & (CY_PM_PWR_STOP_TOKEN_MASK | CY_PM_PWR_STOP_POLARITY));

            /* If reading after writing, read this register three times to delay
            *  enough time for internal settling.
            */
            (void) CY_PM_PWR_STOP_REG;
            (void) CY_PM_PWR_STOP_REG;

            /* Second write causes the freeze of IO-Cells to save IO-Cell state */
            CY_PM_PWR_STOP_REG = CY_PM_PWR_STOP_REG;
        }

        CyExitCriticalSection(interruptState);
    }


    /*******************************************************************************
    * Function Name: CySysPmUnfreezeIo
    ********************************************************************************
    *
    * Summary:
    *  The IO-Cells remain frozen after awake from Hibernate or Stop mode until
    *  the firmware unfreezes them after booting. The call of this function
    *  unfreezes IO-Cells explicitly.
    *
    * Parameters:
    *  None
    *
    * Return:
    *  None
    *
    *******************************************************************************/
    void CySysPmUnfreezeIo(void)
    {
        uint8 interruptState;

        interruptState = CyEnterCriticalSection();

        /* Preserve last reset reason and wakeup polarity. Then, unfreeze I/O:
         * write PWR_STOP.FREEZE=0, .UNLOCK=0x3A, .STOP=0, .TOKEN
         */
        CY_PM_PWR_STOP_REG = CY_PM_PWR_STOP_UNLOCK |
                            (CY_PM_PWR_STOP_REG & (CY_PM_PWR_STOP_TOKEN_MASK | CY_PM_PWR_STOP_POLARITY));

        /* If reading after writing, read this register three times to delay
        *  enough time for internal settling.
        */
        (void) CY_PM_PWR_STOP_REG;
        (void) CY_PM_PWR_STOP_REG;

        /* Lock STOP mode: write PWR_STOP.FREEZE=0, UNLOCK=0x00, STOP=0, .TOKEN */
        CY_PM_PWR_STOP_REG &= (CY_PM_PWR_STOP_TOKEN_MASK | CY_PM_PWR_STOP_POLARITY);

        CyExitCriticalSection(interruptState);
    }

#else

    /*******************************************************************************
    * Function Name: CySysPmSetWakeupHoldoff
    ********************************************************************************
    *
    * Summary:
    *  Sets the Deep Sleep wakeup time by scaling the hold-off to the HFCLK
    *  frequency.
    *
    *  This function must be called before increasing HFCLK clock frequency. It can
    *  optionally be called after lowering HFCLK clock frequency in order to improve
    *  Deep Sleep wakeup time.
    *
    *  It is functionally acceptable to leave the default hold-off setting, but
    *  Deep Sleep wakeup time may exceed the specification.
    *
    *  This function is applicable only for the 4000 device family.
    *
    * Parameters:
    *  uint32 hfclkFrequencyMhz: The HFCLK frequency in MHz.
    *
    * Return:
    *  None
    *
    *******************************************************************************/
    void CySysPmSetWakeupHoldoff(uint32 hfclkFrequencyMhz)
    {
        CY_PM_PWR_KEY_DELAY_REG = ((((uint32)(CY_PM_PWR_KEY_DELAY_REG_DEFAULT << 16u) /
            CY_PM_PWR_KEY_DELAY_FREQ_DEFAULT) * hfclkFrequencyMhz) >> 16u) + 1u;
    }

#endif /* (CY_IP_SRSSV2) */


/* [] END OF FILE */
