rt-thread/bsp/hc32/libraries/hc32_drivers/drv_pm.c

263 lines
6.4 KiB
C

/*
* Copyright (C) 2022-2024, Xiaohua Semiconductor Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-06-12 CDT first version
*/
#include <board.h>
#include <drv_pm.h>
#include <drv_common.h>
#include <drv_wktm.h>
#include <drv_config.h>
#if defined(RT_USING_PM)
#if defined(BSP_USING_PM)
#define LOG_TAG "drv_pm"
#include <drv_log.h>
#define IS_PWC_UNLOCKED() ((CM_PWC->FPRC & PWC_FPRC_FPRCB1) == PWC_FPRC_FPRCB1)
typedef void (* run_switch_func_type)(void);
typedef void (* sleep_enter_func_type)(void);
static void _sleep_enter_idle(void);
static void _sleep_enter_deep(void);
static void _sleep_enter_standby(void);
static void _sleep_enter_shutdown(void);
static void _run_switch_high_to_low(void);
static void _run_switch_low_to_high(void);
static run_switch_func_type _run_switch_func[PM_RUN_MODE_MAX][PM_RUN_MODE_MAX] =
{
{RT_NULL, RT_NULL, RT_NULL, _run_switch_high_to_low},
{RT_NULL, RT_NULL, RT_NULL, _run_switch_high_to_low},
{RT_NULL, RT_NULL, RT_NULL, RT_NULL},
{_run_switch_low_to_high, _run_switch_low_to_high, RT_NULL, RT_NULL},
};
static sleep_enter_func_type _sleep_enter_func[PM_SLEEP_MODE_MAX] =
{
RT_NULL,
_sleep_enter_idle,
RT_NULL,
_sleep_enter_deep,
_sleep_enter_standby,
_sleep_enter_shutdown,
};
static void _uart_console_reconfig(void)
{
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
rt_device_control(rt_console_get_device(), RT_DEVICE_CTRL_CONFIG, &config);
}
/**
* @brief Enter sleep mode.
* @param [in] u8SleepType specifies the type of enter sleep's command.
* @arg PWC_SLEEP_WFI Enter sleep mode by WFI, and wake-up by interrupt handle.
* @arg PWC_SLEEP_WFE_INT Enter sleep mode by WFE, and wake-up by interrupt request(SEVONPEND=1)
* @arg PWC_SLEEP_WFE_EVT Enter sleep mode by WFE, and wake-up by event(SEVONPEND=0).
* @retval None
*/
__WEAKDEF void pwc_seep_enter(uint8_t u8SleepType)
{
DDL_ASSERT(IS_PWC_UNLOCKED());
CLR_REG16_BIT(CM_PWC->STPMCR, PWC_STPMCR_STOP);
CLR_REG8_BIT(CM_PWC->PWRC0, PWC_PWRC0_PWDN);
if (PWC_SLEEP_WFI == u8SleepType)
{
__WFI();
}
else
{
if (PWC_SLEEP_WFE_INT == u8SleepType)
{
SET_REG32_BIT(SCB->SCR, SCB_SCR_SEVONPEND_Msk);
}
else
{
CLR_REG32_BIT(SCB->SCR, SCB_SCR_SEVONPEND_Msk);
}
__SEV();
__WFE();
__WFE();
}
}
static void _sleep_enter_idle(void)
{
struct pm_sleep_mode_idle_config sleep_idle_cfg = PM_SLEEP_IDLE_CFG;
pwc_seep_enter(sleep_idle_cfg.pwc_sleep_type);
}
static void _sleep_enter_deep(void)
{
struct pm_sleep_mode_deep_config sleep_deep_cfg = PM_SLEEP_DEEP_CFG;
RT_ASSERT(PM_SLEEP_CHECK(PM_SLEEP_MODE_DEEP));
(void)PWC_STOP_Config(&sleep_deep_cfg.cfg);
if (PWC_PWRC2_DVS == (READ_REG8(CM_PWC->PWRC2) & PWC_PWRC2_DVS))
{
CLR_REG8_BIT(CM_PWC->PWRC1, PWC_PWRC1_STPDAS);
}
else
{
SET_REG8_BIT(CM_PWC->PWRC1, PWC_PWRC1_STPDAS);
}
PWC_STOP_Enter(sleep_deep_cfg.pwc_stop_type);
}
static void _sleep_enter_standby(void)
{
struct pm_sleep_mode_standby_config sleep_standby_cfg = PM_SLEEP_STANDBY_CFG;
RT_ASSERT(PM_SLEEP_CHECK(PM_SLEEP_MODE_SHUTDOWN));
RT_ASSERT(sleep_standby_cfg.cfg.u8Mode == PWC_PD_MD1 || sleep_standby_cfg.cfg.u8Mode == PWC_PD_MD2);
(void)PWC_PD_Config(&sleep_standby_cfg.cfg);
PWC_PD_ClearWakeupStatus(PWC_PD_WKUP_FLAG_ALL);
__set_FAULTMASK(1);
PWC_PD_Enter();
}
static void _sleep_enter_shutdown(void)
{
struct pm_sleep_mode_shutdown_config sleep_shutdown_cfg = PM_SLEEP_SHUTDOWN_CFG;
RT_ASSERT(PM_SLEEP_CHECK(PM_SLEEP_MODE_SHUTDOWN));
RT_ASSERT(sleep_shutdown_cfg.cfg.u8Mode == PWC_PD_MD3 || sleep_shutdown_cfg.cfg.u8Mode == PWC_PD_MD4);
(void)PWC_PD_Config(&sleep_shutdown_cfg.cfg);
PWC_PD_ClearWakeupStatus(PWC_PD_WKUP_FLAG_ALL);
__set_FAULTMASK(1);
PWC_PD_Enter();
}
/**
* @param pm pointer to power manage structure
*/
static void _pm_sleep(struct rt_pm *pm, uint8_t mode)
{
RT_ASSERT(mode < PM_SLEEP_MODE_MAX);
if (_sleep_enter_func[mode] != NULL)
{
_sleep_enter_func[mode]();
}
}
static void _run_switch_high_to_low(void)
{
struct pm_run_mode_config st_run_mode_cfg = PM_RUN_MODE_CFG;
st_run_mode_cfg.sys_clk_cfg(PM_RUN_MODE_LOW_SPEED);
SysTick_Configuration();
PWC_HighSpeedToLowSpeed();
}
static void _run_switch_low_to_high(void)
{
PWC_LowSpeedToHighSpeed();
struct pm_run_mode_config st_run_mode_cfg = PM_RUN_MODE_CFG;
st_run_mode_cfg.sys_clk_cfg(PM_RUN_MODE_HIGH_SPEED);
SysTick_Configuration();
}
static void _pm_run(struct rt_pm *pm, uint8_t mode)
{
static uint8_t last_mode = PM_RUN_MODE_NORMAL_SPEED;
if (mode == last_mode)
return;
if (_run_switch_func[last_mode][mode] != RT_NULL)
{
_run_switch_func[last_mode][mode]();
}
_uart_console_reconfig();
last_mode = mode;
}
/**
* This function calculate the PM tick from OS tick
*
* @param tick OS tick
*
* @return the PM tick
*/
static rt_tick_t _pm_wakeup_timer_tick_from_os_tick(rt_tick_t tick)
{
rt_uint32_t freq = hc32_wktm_get_countfreq();
return (freq * tick / RT_TICK_PER_SECOND);
}
/**
* This function start the timer of pm
*
* @param pm Pointer to power manage structure
* @param timeout How many OS Ticks that MCU can sleep
*/
static void _pm_wakeup_timer_start(struct rt_pm *pm, rt_uint32_t timeout)
{
RT_ASSERT(pm != RT_NULL);
/* Convert OS Tick to pmtimer timeout value */
timeout = _pm_wakeup_timer_tick_from_os_tick(timeout);
/* Enter __WAKEUP_TIMER_MODE */
hc32_wktm_start(timeout);
}
/**
* This function stop the timer of pm
*
* @param pm Pointer to power manage structure
*/
static void _pm_wakeup_timer_stop(struct rt_pm *pm)
{
RT_ASSERT(pm != RT_NULL);
/* Reset pmtimer status */
hc32_wktm_stop();
}
/**
* This function initialize the power manager
* @note timer feature: only work as wake up timer
*/
int rt_hw_pm_init(void)
{
static const struct rt_pm_ops _ops =
{
_pm_sleep,
_pm_run,
_pm_wakeup_timer_start,
_pm_wakeup_timer_stop,
RT_NULL
};
rt_uint8_t timer_mask = PM_TICKLESS_TIMER_ENABLE_MASK;
/* initialize system pm module */
rt_system_pm_init(&_ops, timer_mask, RT_NULL);
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_pm_init);
#endif
#endif /* RT_USING_PM */