545 lines
14 KiB
C
545 lines
14 KiB
C
/* ----------------------------------------------------------------------------
|
|
* 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");
|
|
}
|
|
|
|
/** @} */
|