/**
  ******************************************************************************
  * @file    kpm32xx_ddl_pwr.c
  * @author  Kiwi Software Team
  * @brief   Power controller DDL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the PWR peripheral:
  *          + Enter different type of power mode
  *          + API of operate SleepOnExit & SEVOnPend
  * @note
  *          V1.0.0, 2024/12/20.
  *
  * Copyright (c) 2024, Kiwi Instruments Co,. Ltd.
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *
  *   2. 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.
  *
  *   3. Neither the name of the copyright holder 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 HOLDER 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.
  ******************************************************************************
  */


#include "kpm32xx_ddl.h"


#ifdef DDL_PWR_MODULE_ENABLED

/**
  * @brief  Configure the elements of structure PWR_Init_T to default values.
  * @param  pPwrStruct  pointer to PWR_Init_T structure that contains
  *         the configuration information for Power Controller.
  * @retval None
  */
void DDL_PWR_StructInit(PWR_Init_T *pPwrStruct)
{
	pPwrStruct->enterSleepInstruction = PWR_ENTER_SLEEP_WFI;
	pPwrStruct->sevOnPendEnable = RESET;
	pPwrStruct->sleepOnExitEnable = RESET;
	pPwrStruct->flashStandbyEnable = SET;
}


/**
  * @brief  Enter Sleep mode.
  * @param  pPwrStruct  pointer to PWR_Init_T structure that contains
  *         the configuration information for Power Controller.
  * @note   When WFI entry is used, systick interrupt have to be disabled if not
  *         desired as the interrupt wake up source.
  * @retval None
  */
void DDL_PWR_EnterSleepMode(PWR_Init_T *pPwrStruct)
{
	/* Configure Sleep mode */
	CLEAR_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
	/* Configure SEVONPEND bit */
	SET_BITMASK(SCB->SCR, SCB_SCR_SEVONPEND_Msk, pPwrStruct->sevOnPendEnable << SCB_SCR_SEVONPEND_Pos);
	/* Configure SLEEPONEXIT bit */
	SET_BITMASK(SCB->SCR, SCB_SCR_SLEEPONEXIT_Msk, pPwrStruct->sleepOnExitEnable << SCB_SCR_SLEEPONEXIT_Pos);

	DDL_SuspendTick();
	if (pPwrStruct->enterSleepInstruction == PWR_ENTER_SLEEP_WFI)
	{
		__WFI();
	}
	else
	{
		__SEV();
		__WFE();
		__WFE();
	}
	DDL_ResumeTick();
}


/**
  * @brief  Enter Deep Sleep mode.
  * @param  pPwrStruct  pointer to PWR_Init_T structure that contains
  *         the configuration information for Power Controller.
  * @note   When WFI entry is used, systick interrupt have to be disabled if not
  *         desired as the interrupt wake up source.
  * @retval None
  */
void DDL_PWR_EnterDeepSleepMode(PWR_Init_T *pPwrStruct)
{
	/* Configure Deep Sleep mode */
	SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
	/* Configure SEVONPEND bit */
	SET_BITMASK(SCB->SCR, SCB_SCR_SEVONPEND_Msk, pPwrStruct->sevOnPendEnable << SCB_SCR_SEVONPEND_Pos);
	/* Configure SLEEPONEXIT bit */
	SET_BITMASK(SCB->SCR, SCB_SCR_SLEEPONEXIT_Msk, pPwrStruct->sleepOnExitEnable << SCB_SCR_SLEEPONEXIT_Pos);

	/* Configure Digital PMU enable */
	SET_BIT(SYSCFG->PMC, SYSCONF_PMC_PMUEN);

	DDL_SuspendTick();
	if (pPwrStruct->enterSleepInstruction == PWR_ENTER_SLEEP_WFI)
	{
		__WFI();
	}
	else
	{
		__SEV();
		__WFE();
		__WFE();
	}
	DDL_ResumeTick();
}


static void PWR_EnterStopModeClockConfig(void)
{
	RCC_ClkInit_T rccClkInitStruct = {0};

	/* Enable HIRC */
	DDL_RCC_HircEnable();
	DDL_RCC_MclkSrcConfig(RCC_MCLKSOURCE_HIRC);

	/* Select HIRC as system clock source */
	rccClkInitStruct.sysClkSource = RCC_SYSCLKSOURCE_MCLK;
	rccClkInitStruct.sysClkDivider = __SYSCLK_DIVIDER_1;
	DDL_RCC_ClkSrcConfig(&rccClkInitStruct);

	/* Disable HOSC and PLL */
	DDL_RCC_HoscDisable();
	DDL_RCC_PLLDisable();
}


/**
  * @brief  Enter Stop mode.
  * @param  pPwrStruct  pointer to PWR_Init_T structure that contains
  *         the configuration information for Power Controller.
  * @note   When WFI entry is used, systick interrupt have to be disabled if not
  *         desired as the interrupt wake up source.
  *         Before entering stop mode, it will swich FCLK to HIRC, close pll and HOSC,
  *         so after calling this API, remember to repPwrStructure the clock.
  * @retval None
  */
void DDL_PWR_EnterStopMode(PWR_Init_T *pPwrStruct)
{
	/* Configure Deep Sleep mode */
	SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
	/* Configure SEVONPEND bit */
	SET_BITMASK(SCB->SCR, SCB_SCR_SEVONPEND_Msk, pPwrStruct->sevOnPendEnable << SCB_SCR_SEVONPEND_Pos);
	/* Configure SLEEPONEXIT bit */
	SET_BITMASK(SCB->SCR, SCB_SCR_SLEEPONEXIT_Msk, pPwrStruct->sleepOnExitEnable << SCB_SCR_SLEEPONEXIT_Pos);

	/* Switch FCLK to MCLK, close pll and HOSC */
	PWR_EnterStopModeClockConfig();

	if (pPwrStruct->flashStandbyEnable)
	{
		__DDL_PWR_EFLASH_STANDBY_Enable();
	}

	/* Configure Digital PMU enable */
	SET_BIT(SYSCFG->PMC, SYSCONF_PMC_PMUEN);
	/* Configure Stop mode */
	SET_BIT(SYSCFG->PMC, SYSCONF_PMC_STP);

	DDL_SuspendTick();
	if (pPwrStruct->enterSleepInstruction == PWR_ENTER_SLEEP_WFI)
	{
		__WFI();
	}
	else
	{
		__SEV();
		__WFE();
		__WFE();
	}
	DDL_ResumeTick();

	if (pPwrStruct->flashStandbyEnable)
	{
		__DDL_PWR_EFLASH_STANDBY_Disable();
	}
}

#endif /* DDL_PWR_MODULE_ENABLED */

