587 lines
14 KiB
C
587 lines
14 KiB
C
/***************************************************************************
|
|
*
|
|
* Copyright 2015-2019 BES.
|
|
* All rights reserved. All unpublished rights reserved.
|
|
*
|
|
* No part of this work may be used or reproduced in any form or by any
|
|
* means, or stored in a database or retrieval system, without prior written
|
|
* permission of BES.
|
|
*
|
|
* Use of this work is governed by a license granted by BES.
|
|
* This work contains confidential and proprietary information of
|
|
* BES. which is protected by copyright, trade secret,
|
|
* trademark and other intellectual property rights.
|
|
*
|
|
****************************************************************************/
|
|
#include "hal_sleep.h"
|
|
#include "hal_cmu.h"
|
|
#include "hal_location.h"
|
|
#include "hal_trace.h"
|
|
#include "hal_timer.h"
|
|
#include "hal_sysfreq.h"
|
|
#include "hal_dma.h"
|
|
#include "hal_norflash.h"
|
|
#include "hal_gpadc.h"
|
|
#include "analog.h"
|
|
#include "pmu.h"
|
|
#include "cmsis.h"
|
|
|
|
//static uint8_t SRAM_STACK_LOC sleep_stack[128];
|
|
|
|
static HAL_SLEEP_HOOK_HANDLER sleep_hook_handler[HAL_SLEEP_HOOK_USER_QTY];
|
|
static HAL_DEEP_SLEEP_HOOK_HANDLER deep_sleep_hook_handler[HAL_DEEP_SLEEP_HOOK_USER_QTY];
|
|
|
|
static uint32_t cpu_wake_lock_map;
|
|
static uint32_t sys_wake_lock_map;
|
|
static uint32_t chip_wake_lock_map;
|
|
|
|
#ifdef SLEEP_STATS_TRACE
|
|
static uint32_t stats_trace_interval;
|
|
static uint32_t stats_trace_time;
|
|
#endif
|
|
static uint32_t stats_interval;
|
|
static uint32_t stats_start_time;
|
|
static uint32_t light_sleep_time;
|
|
static uint32_t sys_deep_sleep_time;
|
|
static uint32_t chip_deep_sleep_time;
|
|
static bool stats_started;
|
|
static bool stats_valid;
|
|
static uint8_t light_sleep_ratio;
|
|
static uint8_t sys_deep_sleep_ratio;
|
|
static uint8_t chip_deep_sleep_ratio;
|
|
|
|
void hal_sleep_start_stats(uint32_t stats_interval_ms, uint32_t trace_interval_ms)
|
|
{
|
|
uint32_t lock;
|
|
|
|
lock = int_lock();
|
|
if (stats_interval_ms) {
|
|
stats_interval = MS_TO_TICKS(stats_interval_ms);
|
|
stats_start_time = hal_sys_timer_get();
|
|
light_sleep_time = 0;
|
|
sys_deep_sleep_time = 0;
|
|
chip_deep_sleep_time = 0;
|
|
stats_valid = false;
|
|
stats_started = true;
|
|
} else {
|
|
stats_started = false;
|
|
}
|
|
int_unlock(lock);
|
|
|
|
#ifdef SLEEP_STATS_TRACE
|
|
if (stats_interval_ms && trace_interval_ms) {
|
|
stats_trace_interval = MS_TO_TICKS(trace_interval_ms);
|
|
} else {
|
|
stats_trace_interval = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int hal_sleep_get_stats(struct CPU_USAGE_T *usage)
|
|
{
|
|
int ret;
|
|
uint32_t lock;
|
|
|
|
lock = int_lock();
|
|
if (stats_valid) {
|
|
usage->light_sleep = light_sleep_ratio;
|
|
usage->sys_deep_sleep = sys_deep_sleep_ratio;
|
|
usage->chip_deep_sleep = chip_deep_sleep_ratio;
|
|
usage->busy = 100 - (light_sleep_ratio + sys_deep_sleep_ratio + chip_deep_sleep_ratio);
|
|
ret = 0;
|
|
} else {
|
|
ret = 1;
|
|
}
|
|
int_unlock(lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hal_sleep_cpu_busy(void)
|
|
{
|
|
if (cpu_wake_lock_map || hal_cmu_lpu_busy()) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int hal_sleep_sys_busy(void)
|
|
{
|
|
if (sys_wake_lock_map || hal_sysfreq_busy() || hal_dma_busy()) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int hal_sleep_set_sleep_hook(enum HAL_SLEEP_HOOK_USER_T user, HAL_SLEEP_HOOK_HANDLER handler)
|
|
{
|
|
if (user >= ARRAY_SIZE(sleep_hook_handler)) {
|
|
return 1;
|
|
}
|
|
sleep_hook_handler[user] = handler;
|
|
return 0;
|
|
}
|
|
|
|
static int SRAM_TEXT_LOC hal_sleep_exec_sleep_hook(void)
|
|
{
|
|
int i;
|
|
int ret;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(sleep_hook_handler); i++) {
|
|
if (sleep_hook_handler[i]) {
|
|
ret = sleep_hook_handler[i]();
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_sleep_set_deep_sleep_hook(enum HAL_DEEP_SLEEP_HOOK_USER_T user, HAL_DEEP_SLEEP_HOOK_HANDLER handler)
|
|
{
|
|
if (user >= ARRAY_SIZE(deep_sleep_hook_handler)) {
|
|
return 1;
|
|
}
|
|
deep_sleep_hook_handler[user] = handler;
|
|
return 0;
|
|
}
|
|
|
|
static int SRAM_TEXT_LOC hal_sleep_exec_deep_sleep_hook(void)
|
|
{
|
|
int i;
|
|
int ret;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(deep_sleep_hook_handler); i++) {
|
|
if (deep_sleep_hook_handler[i]) {
|
|
ret = deep_sleep_hook_handler[i]();
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SRAM_TEXT_LOC hal_sleep_irq_pending(void)
|
|
{
|
|
#if defined(__GIC_PRESENT) && (__GIC_PRESENT)
|
|
int i;
|
|
|
|
for (i = 0; i < (USER_IRQn_QTY + 31) / 32; i++) {
|
|
if (GICDistributor->ICPENDR[i] & GICDistributor->ISENABLER[i]) {
|
|
return 1;
|
|
}
|
|
}
|
|
#else
|
|
#if 0
|
|
int i;
|
|
|
|
for (i = 0; i < (USER_IRQn_QTY + 31) / 32; i++) {
|
|
if (NVIC->ICPR[i] & NVIC->ISER[i]) {
|
|
return 1;
|
|
}
|
|
}
|
|
#else
|
|
// If there is any pending and enabled exception (including sysTick)
|
|
if (SCB->ICSR & SCB_ICSR_VECTPENDING_Msk) {
|
|
return 1;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SRAM_TEXT_LOC hal_sleep_specific_irq_pending(const uint32_t *irq, uint32_t cnt)
|
|
{
|
|
int i;
|
|
uint32_t check_cnt;
|
|
|
|
check_cnt = (USER_IRQn_QTY + 31) / 32;
|
|
if (check_cnt > cnt) {
|
|
check_cnt = cnt;
|
|
}
|
|
|
|
#if defined(__GIC_PRESENT) && (__GIC_PRESENT)
|
|
for (i = 0; i < check_cnt; i++) {
|
|
if (GICDistributor->ICPENDR[i] & GICDistributor->ISENABLER[i] & irq[i]) {
|
|
return 1;
|
|
}
|
|
}
|
|
#else
|
|
for (i = 0; i < check_cnt; i++) {
|
|
if (NVIC->ICPR[i] & NVIC->ISER[i] & irq[i]) {
|
|
return 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
void WEAK bt_drv_sleep(void)
|
|
{
|
|
}
|
|
|
|
void WEAK bt_drv_wakeup(void)
|
|
{
|
|
}
|
|
|
|
static enum HAL_SLEEP_STATUS_T SRAM_TEXT_LOC hal_sleep_lowpower_mode(void)
|
|
{
|
|
enum HAL_SLEEP_STATUS_T ret;
|
|
enum HAL_CMU_LPU_SLEEP_MODE_T mode;
|
|
uint32_t time = 0;
|
|
|
|
ret = HAL_SLEEP_STATUS_LIGHT;
|
|
|
|
// Deep sleep hook
|
|
if (hal_sleep_exec_deep_sleep_hook() || hal_trace_busy()) {
|
|
return ret;
|
|
}
|
|
|
|
if (stats_started) {
|
|
time = hal_sys_timer_get();
|
|
}
|
|
|
|
// Modules (except for psram and flash) sleep
|
|
hal_gpadc_sleep();
|
|
if (chip_wake_lock_map == 0) {
|
|
analog_sleep();
|
|
pmu_sleep();
|
|
}
|
|
bt_drv_sleep();
|
|
|
|
// End of sleep
|
|
|
|
//psram_sleep();
|
|
hal_norflash_sleep(HAL_NORFLASH_ID_0);
|
|
|
|
// Now neither psram nor flash are usable
|
|
|
|
if (!hal_sleep_irq_pending()) {
|
|
if (chip_wake_lock_map) {
|
|
mode = HAL_CMU_LPU_SLEEP_MODE_SYS;
|
|
} else {
|
|
mode = HAL_CMU_LPU_SLEEP_MODE_CHIP;
|
|
}
|
|
hal_cmu_lpu_sleep(mode);
|
|
ret = HAL_SLEEP_STATUS_DEEP;
|
|
}
|
|
|
|
hal_norflash_wakeup(HAL_NORFLASH_ID_0);
|
|
//psram_wakeup();
|
|
|
|
// Now both psram and flash are usable
|
|
|
|
if (chip_wake_lock_map == 0) {
|
|
pmu_wakeup();
|
|
analog_wakeup();
|
|
}
|
|
bt_drv_wakeup();
|
|
|
|
hal_gpadc_wakeup();
|
|
// Modules (except for psram and flash) wakeup
|
|
|
|
// End of wakeup
|
|
|
|
if (stats_started) {
|
|
time = hal_sys_timer_get() - time;
|
|
if (chip_wake_lock_map) {
|
|
sys_deep_sleep_time += time;
|
|
} else {
|
|
chip_deep_sleep_time += time;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// GCC has trouble in detecting static function usage in embedded ASM statements.
|
|
// The following function might be optimized away if there is no explicted call in C codes.
|
|
// Specifying "used" (or "noclone") attribute on the function can avoid the mistaken optimization.
|
|
static enum HAL_SLEEP_STATUS_T SRAM_TEXT_LOC NOINLINE USED hal_sleep_proc(int light_sleep)
|
|
{
|
|
enum HAL_SLEEP_STATUS_T ret;
|
|
uint32_t time = 0;
|
|
uint32_t interval;
|
|
|
|
ret = HAL_SLEEP_STATUS_LIGHT;
|
|
|
|
// Check the sleep conditions in interrupt-locked context
|
|
if (hal_sleep_cpu_busy()) {
|
|
// Cannot sleep
|
|
} else {
|
|
// Sleep hook
|
|
if (hal_sleep_exec_sleep_hook()) {
|
|
goto _exit_sleep;
|
|
}
|
|
|
|
if (hal_sleep_sys_busy()) {
|
|
// Light sleep
|
|
|
|
if (stats_started) {
|
|
time = hal_sys_timer_get();
|
|
}
|
|
|
|
#ifdef NO_LIGHT_SLEEP
|
|
// WFI during USB ISO transfer might generate very weak (0.1 mV) 1K tone interference ???
|
|
while (!hal_sleep_irq_pending());
|
|
#else
|
|
#ifndef __ARM_ARCH_ISA_ARM
|
|
SCB->SCR = 0;
|
|
#endif
|
|
__DSB();
|
|
__WFI();
|
|
#endif
|
|
|
|
if (stats_started) {
|
|
light_sleep_time += hal_sys_timer_get() - time;
|
|
}
|
|
#ifdef DEBUG
|
|
} else if (hal_trace_busy()) {
|
|
// Light sleep with trace busy only
|
|
|
|
if (stats_started) {
|
|
time = hal_sys_timer_get();
|
|
}
|
|
|
|
// No irq will be generated when trace becomes idle, so the trace status should
|
|
// be kept polling actively instead of entering WFI
|
|
while (!hal_sleep_irq_pending() && hal_trace_busy());
|
|
|
|
if (stats_started) {
|
|
light_sleep_time += hal_sys_timer_get() - time;
|
|
}
|
|
|
|
if (!hal_sleep_irq_pending()) {
|
|
goto _deep_sleep;
|
|
}
|
|
#endif
|
|
} else {
|
|
// Deep sleep
|
|
|
|
_deep_sleep: POSSIBLY_UNUSED;
|
|
|
|
if (light_sleep) {
|
|
ret = HAL_SLEEP_STATUS_DEEP;
|
|
} else {
|
|
ret = hal_sleep_lowpower_mode();
|
|
}
|
|
}
|
|
}
|
|
|
|
_exit_sleep:
|
|
if (stats_started) {
|
|
time = hal_sys_timer_get();
|
|
interval = time - stats_start_time;
|
|
if (interval >= stats_interval) {
|
|
if (light_sleep_time > UINT32_MAX / 100) {
|
|
light_sleep_ratio = (uint64_t)light_sleep_time * 100 / interval;
|
|
} else {
|
|
light_sleep_ratio = light_sleep_time * 100 / interval;
|
|
}
|
|
if (sys_deep_sleep_time > UINT32_MAX / 100) {
|
|
sys_deep_sleep_ratio = (uint64_t)sys_deep_sleep_time * 100 / interval;
|
|
} else {
|
|
sys_deep_sleep_ratio = sys_deep_sleep_time * 100 / interval;
|
|
}
|
|
if (chip_deep_sleep_time > UINT32_MAX / 100) {
|
|
chip_deep_sleep_ratio = (uint64_t)chip_deep_sleep_time * 100 / interval;
|
|
} else {
|
|
chip_deep_sleep_ratio = chip_deep_sleep_time * 100 / interval;
|
|
}
|
|
stats_valid = true;
|
|
light_sleep_time = 0;
|
|
sys_deep_sleep_time = 0;
|
|
chip_deep_sleep_time = 0;
|
|
stats_start_time = time;
|
|
}
|
|
#ifdef SLEEP_STATS_TRACE
|
|
if (stats_valid && stats_trace_interval) {
|
|
uint32_t time = hal_sys_timer_get();
|
|
if (time - stats_trace_time >= stats_trace_interval) {
|
|
TRACE(4,"CPU USAGE: busy=%d light=%d sys_deep=%d chip_deep=%d",
|
|
100 - (light_sleep_ratio + sys_deep_sleep_ratio + chip_deep_sleep_ratio),
|
|
light_sleep_ratio, sys_deep_sleep_ratio, chip_deep_sleep_ratio);
|
|
stats_trace_time = time;
|
|
#ifdef DEBUG_SLEEP_USER
|
|
TRACE(4,"SLEEP_USER: cpulock=0x%X syslock=0x%X irq=0x%08X_%08X",
|
|
cpu_wake_lock_map, sys_wake_lock_map, (NVIC->ICPR[1] & NVIC->ISER[1]), (NVIC->ICPR[0] & NVIC->ISER[0]));
|
|
hal_sysfreq_print();
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifndef __ARM_ARCH_ISA_ARM
|
|
static enum HAL_SLEEP_STATUS_T SRAM_TEXT_LOC NOINLINE USED NAKED hal_sleep_deep_sleep_wrapper(void)
|
|
{
|
|
asm volatile(
|
|
"push {r4, lr} \n"
|
|
// Switch current stack pointer to MSP
|
|
"mrs r4, control \n"
|
|
"bic r4, #2 \n"
|
|
"msr control, r4 \n"
|
|
"isb \n"
|
|
"movs r0, #0 \n"
|
|
"bl hal_sleep_proc \n"
|
|
// Switch current stack pointer back to PSP
|
|
"orr r4, #2 \n"
|
|
"msr control, r4 \n"
|
|
"isb \n"
|
|
"pop {r4, pc} \n"
|
|
);
|
|
|
|
#ifndef __ARMCC_VERSION
|
|
return HAL_SLEEP_STATUS_LIGHT;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
enum HAL_SLEEP_STATUS_T SRAM_TEXT_LOC hal_sleep_enter_sleep(void)
|
|
{
|
|
enum HAL_SLEEP_STATUS_T ret;
|
|
uint32_t lock;
|
|
|
|
ret = HAL_SLEEP_STATUS_LIGHT;
|
|
|
|
#ifdef NO_SLEEP
|
|
return ret;
|
|
#endif
|
|
|
|
lock = int_lock_global();
|
|
|
|
#ifndef __ARM_ARCH_ISA_ARM
|
|
if (__get_CONTROL() & 0x02) {
|
|
ret = hal_sleep_deep_sleep_wrapper();
|
|
} else
|
|
#endif
|
|
{
|
|
ret = hal_sleep_proc(false);
|
|
}
|
|
|
|
int_unlock_global(lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
enum HAL_SLEEP_STATUS_T SRAM_TEXT_LOC hal_sleep_light_sleep(void)
|
|
{
|
|
enum HAL_SLEEP_STATUS_T ret;
|
|
uint32_t lock;
|
|
|
|
ret = HAL_SLEEP_STATUS_LIGHT;
|
|
|
|
#ifdef NO_SLEEP
|
|
return ret;
|
|
#endif
|
|
|
|
lock = int_lock_global();
|
|
|
|
ret = hal_sleep_proc(true);
|
|
|
|
int_unlock_global(lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int hal_cpu_wake_lock(enum HAL_CPU_WAKE_LOCK_USER_T user)
|
|
{
|
|
uint32_t lock;
|
|
|
|
if (user >= HAL_CPU_WAKE_LOCK_USER_QTY) {
|
|
return 1;
|
|
}
|
|
|
|
lock = int_lock();
|
|
cpu_wake_lock_map |= (1 << user);
|
|
int_unlock(lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_cpu_wake_unlock(enum HAL_CPU_WAKE_LOCK_USER_T user)
|
|
{
|
|
uint32_t lock;
|
|
|
|
if (user >= HAL_CPU_WAKE_LOCK_USER_QTY) {
|
|
return 1;
|
|
}
|
|
|
|
lock = int_lock();
|
|
cpu_wake_lock_map &= ~(1 << user);
|
|
int_unlock(lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_sys_wake_lock(enum HAL_SYS_WAKE_LOCK_USER_T user)
|
|
{
|
|
uint32_t lock;
|
|
|
|
if (user >= HAL_SYS_WAKE_LOCK_USER_QTY) {
|
|
return 1;
|
|
}
|
|
|
|
lock = int_lock();
|
|
sys_wake_lock_map |= (1 << user);
|
|
int_unlock(lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_sys_wake_unlock(enum HAL_SYS_WAKE_LOCK_USER_T user)
|
|
{
|
|
uint32_t lock;
|
|
|
|
if (user >= HAL_SYS_WAKE_LOCK_USER_QTY) {
|
|
return 1;
|
|
}
|
|
|
|
lock = int_lock();
|
|
sys_wake_lock_map &= ~(1 << user);
|
|
int_unlock(lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_chip_wake_lock(enum HAL_CHIP_WAKE_LOCK_USER_T user)
|
|
{
|
|
uint32_t lock;
|
|
|
|
if (user >= HAL_CHIP_WAKE_LOCK_USER_QTY) {
|
|
return 1;
|
|
}
|
|
|
|
lock = int_lock();
|
|
chip_wake_lock_map |= (1 << user);
|
|
int_unlock(lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_chip_wake_unlock(enum HAL_CHIP_WAKE_LOCK_USER_T user)
|
|
{
|
|
uint32_t lock;
|
|
|
|
if (user >= HAL_CHIP_WAKE_LOCK_USER_QTY) {
|
|
return 1;
|
|
}
|
|
|
|
lock = int_lock();
|
|
chip_wake_lock_map &= ~(1 << user);
|
|
int_unlock(lock);
|
|
|
|
return 0;
|
|
}
|
|
|