545 lines
14 KiB
C
Raw Permalink Normal View History

2025-09-28 09:58:38 +08:00
/* ----------------------------------------------------------------------------
* Copyright (c) 2020-2030 OnMicro Limited. All rights reserved.
*
* 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 OnMicroelectronics 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.
*
* -------------------------------------------------------------------------- */
/**
* @file pm.c
* @brief power manager driver
* @date 01. April 2020
* @author OnMicro SW Team
*
* @version
* Version 1.0
* - Initial release
*
* @{
*/
/*******************************************************************************
* INCLUDES
*/
#include "om.h"
#include "om_driver.h"
#include "trc_io.h"
/*******************************************************************************
* MACROS
*/
#define PM_SLEEP_STATE_TABLE_SIZE /*lint -e506 */ (PM_ID_NUM/32 + ((PM_ID_NUM%32) ? 1 : 0))
#define PM_SLEEP_STORE_CALLBACK_NUM 20
#define PM_SLEEP_CHECKER_NUM 10
/*******************************************************************************
* TYPEDEFS
*/
typedef struct {
pm_checker_priority_t priority;
pm_checker_callback_t callback;
} pm_checker_t;
typedef struct {
bool sleep_enable;
bool ultra_sleep_enable;
uint16_t min_sleep_time;
volatile uint32_t sleep_state[PM_SLEEP_STATE_TABLE_SIZE];
pm_checker_t checker[PM_SLEEP_CHECKER_NUM];
pm_sleep_callback_t notify_cb;
pm_sleep_callback_t store_cb[PM_SLEEP_STORE_CALLBACK_NUM];
} pm_env_t;
/*******************************************************************************
* CONST & VARIABLES
*/
static pm_env_t pm_env;
/*******************************************************************************
* LOCAL FUNCTIONS
*/
/**
* @brief pm system enter deepsleep
**/
__RAM_CODE
static void pm_system_enter_deepsleep(void)
{
SystemEnterDeepSleep();
// if icache power off in sleep, cache should be re-enable, @ref drv_icache_powerdown_in_sleep_enable()
drv_icache_enable();
}
/**
* @brief pm sleep checker check
*
* @return status
**/
__RAM_CODES("PM")
static pm_status_t pm_sleep_checker_check(void)
{
int i;
pm_status_t status = drv_pmu_sleep_status();
if (status <= PM_STATUS_IDLE)
return status;
for(i = 0; i < PM_SLEEP_CHECKER_NUM; ++i) {
if(pm_env.checker[i].callback != NULL) {
pm_status_t checker_status = pm_env.checker[i].callback();
status = OM_MIN(status, checker_status);
if (status <= PM_STATUS_IDLE) {
break;
}
}
}
return status;
}
/**
* @brief pm sleep state check
*
* @return status
**/
__RAM_CODES("PM")
static pm_status_t pm_sleep_state_check(void)
{
int i;
bool all0 = true;
for (i=0; i < PM_SLEEP_STATE_TABLE_SIZE; ++i) {
if (pm_env.sleep_state[i]) {
all0 = false;
break;
}
}
return all0 ? PM_STATUS_DEEP_SLEEP : PM_STATUS_IDLE;
}
/**
* @brief system sleep notify
*
* @param[in] sleep_state sleep state
* @param[in] power_status power status
**/
__RAM_CODES("PM")
static void pm_sleep_notify(pm_sleep_state_t sleep_state, pm_status_t power_status)
{
if (sleep_state != PM_SLEEP_LEAVE_BOTTOM_HALF) {
for (int i = 0; i < PM_SLEEP_STORE_CALLBACK_NUM; ++i) {
if (pm_env.store_cb[i])
pm_env.store_cb[i](sleep_state, power_status);
}
}
if (pm_env.notify_cb) {
pm_env.notify_cb(sleep_state, power_status);
}
}
/**
* @brief system enter ultar sleep
**/
static void pm_sleep_enter_ultra_sleep(void)
{
pm_sleep_notify(PM_SLEEP_STORE, PM_STATUS_DEEP_SLEEP);
drv_pmu_sleep_enter(PM_STATUS_DEEP_SLEEP, /*lint -e747 reboot*/true);
while(1);
}
/**
* @brief system enter sleep
**/
__RAM_CODES("PM")
static void pm_sleep_enter_common_sleep(pm_status_t power_status)
{
pm_sleep_notify(PM_SLEEP_STORE, power_status);
drv_pmu_sleep_enter(power_status, /*lint -e747 reboot*/false);
pm_system_enter_deepsleep();
drv_pmu_sleep_leave(PMU_SLEEP_LEAVE_STEP1_ON_RC32M, power_status);
pm_sleep_notify(PM_SLEEP_RESTORE_HSI, power_status);
drv_pmu_sleep_leave(PMU_SLEEP_LEAVE_STEP2_WAIT_XTAL32M, power_status);
pm_sleep_notify(PM_SLEEP_RESTORE_HSE, power_status);
drv_pmu_sleep_leave(PMU_SLEEP_LEAVE_STEP3_FINISH, power_status);
pm_sleep_notify(PM_SLEEP_LEAVE_BOTTOM_HALF, power_status);
}
/**
* @brief system enter deep sleep
**/
static void pm_sleep_enter_deep_sleep(void)
{
pm_sleep_enter_common_sleep(PM_STATUS_DEEP_SLEEP);
}
/**
* @brief system enter deep sleep
**/
__RAM_CODES("PM")
static void pm_sleep_enter_light_sleep(void)
{
pm_sleep_enter_common_sleep(PM_STATUS_SLEEP);
}
/*******************************************************************************
* PUBLIC FUNCTIONS
*/
/**
* @brief system sleep
*
* @param[in] status status
**/
__RAM_CODES("PM")
void pm_sleep(pm_status_t status)
{
/* IRQ has been disabled */
switch(status) {
case PM_STATUS_SLEEP:
TRC_IO(TRC_IO_PM_SLEEP, 1);
pm_sleep_enter_light_sleep();
TRC_IO(TRC_IO_PM_SLEEP, 0);
break;
case PM_STATUS_DEEP_SLEEP:
if (pm_env.ultra_sleep_enable) {
TRC_IO(TRC_IO_PM_ULTRA_SLEEP, 1);
pm_sleep_enter_ultra_sleep();
TRC_IO(TRC_IO_PM_ULTRA_SLEEP, 0);
} else {
TRC_IO(TRC_IO_PM_DEEP_SLEEP, 1);
pm_sleep_enter_deep_sleep();
TRC_IO(TRC_IO_PM_DEEP_SLEEP, 0);
}
break;
case PM_STATUS_IDLE:
TRC_IO(TRC_IO_PM_IDLE, 1);
__WFI();
TRC_IO(TRC_IO_PM_IDLE, 0);
break;
default:
break;
}
}
/**
* @brief system sleep min time set
*
* @param[in] time 32k tick
**/
void pm_sleep_min_time_set(uint16_t tick)
{
pm_env.min_sleep_time = tick;
}
/**
*******************************************************************************
* @brief pm sleep min time get
*
* @return 32k tick
*******************************************************************************
*/
__RAM_CODES("PM")
uint32_t pm_sleep_min_time_get(void)
{
return pm_env.min_sleep_time;
}
/**
*******************************************************************************
* @brief pm sleep ultra sleep mode enable
*
* @param[in] enable enable
*******************************************************************************
*/
void pm_sleep_ultra_sleep_mode_enable(bool enable)
{
pm_env.ultra_sleep_enable = enable;
}
/**
*******************************************************************************
* @brief pm sleep ultra sleep mode enable
*
* @return is enabled
*******************************************************************************
*/
bool pm_sleep_ultra_sleep_mode_is_enabled(void)
{
return pm_env.ultra_sleep_enable;
}
/**
*******************************************************************************
* @brief pm sleep enable
*
* @param[in] enable enable
*******************************************************************************
**/
void pm_sleep_enable(bool enable)
{
pm_env.sleep_enable = enable;
}
/**
*******************************************************************************
* @brief pm sleep prevent
*
* @param[in] id id, reference @ref pm_id_t
* If I2C0 need to prevent system into sleep mode, id = PM_ID_I2C0;
* If I2C2 need to prevent system into sleep mode, id = PM_ID_I2C2;
* ...
*
*******************************************************************************
**/
void pm_sleep_prevent(pm_id_t id)
{
uint32_t group = (int)id / 32;
uint32_t mask = 1u << ((int)id % 32);
OM_CRITICAL_BEGIN();
pm_env.sleep_state[group] |= mask;
OM_CRITICAL_END();
}
/**
*******************************************************************************
* @brief pm sleep allow
*
* @param[in] id id, reference @ref pm_id_t
* If I2C0 need to allow system into sleep mode, id = PM_ID_I2C0;
* If I2C2 need to allow system into sleep mode, id = PM_ID_I2C2;
* ...
*
*******************************************************************************
**/
void pm_sleep_allow(pm_id_t id)
{
uint32_t group = (int)id / 32;
uint32_t mask_n = ~(1u << ((int)id % 32));
OM_CRITICAL_BEGIN();
pm_env.sleep_state[group] &= mask_n;
OM_CRITICAL_END();
}
/**
*******************************************************************************
* @brief pm sleep checker register
*
* @param[in] priority priority
* @param[in] checker_cb checker cb
*******************************************************************************
**/
void pm_sleep_checker_callback_register(pm_checker_priority_t priority, pm_checker_callback_t checker_cb)
{
int n, i, j;
for(n = 0; n < PM_SLEEP_CHECKER_NUM; ++n) {
if(pm_env.checker[n].callback == NULL) {
// sort and insert
for (i = 0; i < n; ++i) {
// check priority
if (pm_env.checker[i].priority < priority) {
// move
for (j = n; j > i; --j) {
pm_env.checker[j] = pm_env.checker[j-1];
}
// insert
pm_env.checker[i].priority = priority;
pm_env.checker[i].callback = checker_cb;
return;
}
}
pm_env.checker[n].priority = priority;
pm_env.checker[n].callback = checker_cb;
return;
} else if (pm_env.checker[n].callback == checker_cb) {
return;
}
}
OM_ASSERT(0);
}
/**
* @brief system sleep notify user callback register
*
* @param[in] notify_cb sleep notify cb
**/
void pm_sleep_notify_user_callback_register(pm_sleep_callback_t notify_cb)
{
pm_env.notify_cb = notify_cb;
}
/**
* @brief system sleep store restore callback register
*
* @param[in] sleep_enter_cb sleep enter cb
* @param[in] sleep_leave_cb sleep leave cb
**/
void pm_sleep_store_restore_callback_register(pm_sleep_callback_t store_cb)
{
int i;
for (i = 0; i < PM_SLEEP_STORE_CALLBACK_NUM; ++i) {
if (pm_env.store_cb[i] == NULL) {
pm_env.store_cb[i] = store_cb;
return;
} else if (pm_env.store_cb[i] == store_cb) {
return;
}
}
OM_ASSERT(0);
}
/**
*******************************************************************************
* @brief pm sleep check
*
* @return sleep status
*******************************************************************************
*/
__RAM_CODES("PM")
pm_status_t pm_sleep_check(void)
{
pm_status_t status;
// 1st. check pm self sleep state
status = pm_sleep_state_check();
// 2nd. check registed sleep checker
if (status > PM_STATUS_IDLE) {
TRC_IO(TRC_IO_PM_CHECKER, 1);
status = pm_sleep_checker_check();
TRC_IO(TRC_IO_PM_CHECKER, 0);
}
return status;
}
/**
*******************************************************************************
* @brief pm power manage
*******************************************************************************
**/
__RAM_CODES("PM")
void pm_power_manage(void)
{
pm_status_t status;
// 1st. check pm sleep
status = pm_sleep_check();
// 2th. check sleep enable
if (!pm_env.sleep_enable) {
status = OM_MIN(PM_STATUS_IDLE, status);
}
pm_sleep(status);
}
/**
*******************************************************************************
* @brief pm init
*******************************************************************************
*/
void pm_init(void)
{
pm_env.min_sleep_time = PMU_TIMER_MS2TICK(3);
//#ifdef CONFIG_SYSTEM_FROM_ROM_EXIT_DEEPSLEEP_ENABLE
SystemFromRomExitDeepSleepEnable();
//#endif
}
/**
*******************************************************************************
* @brief dump pm information
*******************************************************************************
*/
void pm_dump(void *printf_dump_func)
{
void (*__printf)(const char *format, ...) = /*lint -e{611}*/ (void (*)(const char *format, ...))printf_dump_func;
if (__printf == NULL) {
return;
}
// pm prevent status
__printf("[PM] prevent status:");
for (uint8_t i = 0; i < PM_SLEEP_STATE_TABLE_SIZE; i++) {
__printf("0x%08X ", pm_env.sleep_state[i]);
}
__printf("\n");
// pm sleep enable
__printf("[PM] sleep enable:%s ultra sleep enable:%s\n",
pm_env.sleep_enable ? "yes" : "no",
pm_env.ultra_sleep_enable ? "yes" : "no"
);
// checker
__printf("[PM] checker: ");
for (uint8_t i = 0; i < PM_SLEEP_CHECKER_NUM; i++) {
if (pm_env.checker[i].callback != NULL) {
__printf("cb_addr:0x%08X(%d) ", pm_env.checker[i].callback, pm_env.checker[i].priority);
}
}
__printf("\n");
// store cb
__printf("[PM] store_cb: ");
for (uint8_t i = 0; i < PM_SLEEP_STORE_CALLBACK_NUM; i++) {
if (pm_env.store_cb[i] != NULL) {
__printf("cb_addr:0x%08X ", pm_env.store_cb[i]);
}
}
__printf("\n");
}
/** @} */