159 lines
3.7 KiB
C
159 lines
3.7 KiB
C
/*
|
|
* Copyright (c) 2018 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr.h>
|
|
#include <kernel.h>
|
|
#include <init.h>
|
|
#include <string.h>
|
|
#include <soc.h>
|
|
#include "policy/pm_policy.h"
|
|
|
|
#define LOG_LEVEL CONFIG_PM_LOG_LEVEL
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(power);
|
|
|
|
static int post_ops_done = 1;
|
|
static enum power_states pm_state;
|
|
|
|
#ifdef CONFIG_PM_CONTROL_OS_DEBUG
|
|
|
|
struct pm_debug_info {
|
|
u32_t count;
|
|
u32_t last_res;
|
|
u32_t total_res;
|
|
};
|
|
|
|
static struct pm_debug_info pm_dbg_info[SYS_POWER_STATE_MAX];
|
|
static u32_t timer_start, timer_end;
|
|
|
|
static inline void sys_pm_debug_start_timer(void)
|
|
{
|
|
timer_start = k_cycle_get_32();
|
|
}
|
|
|
|
static inline void sys_pm_debug_stop_timer(void)
|
|
{
|
|
timer_end = k_cycle_get_32();
|
|
}
|
|
|
|
static void sys_pm_log_debug_info(enum power_states state)
|
|
{
|
|
u32_t res = timer_end - timer_start;
|
|
|
|
pm_dbg_info[state].count++;
|
|
pm_dbg_info[state].last_res = res;
|
|
pm_dbg_info[state].total_res += res;
|
|
}
|
|
|
|
void sys_pm_dump_debug_info(void)
|
|
{
|
|
for (int i = 0; i < SYS_POWER_STATE_MAX; i++) {
|
|
LOG_DBG("PM:state = %d, count = %d last_res = %d, "
|
|
"total_res = %d\n", i, pm_dbg_info[i].count,
|
|
pm_dbg_info[i].last_res, pm_dbg_info[i].total_res);
|
|
}
|
|
}
|
|
#else
|
|
static inline void sys_pm_debug_start_timer(void) { }
|
|
static inline void sys_pm_debug_stop_timer(void) { }
|
|
static void sys_pm_log_debug_info(enum power_states state) { }
|
|
void sys_pm_dump_debug_info(void) { }
|
|
#endif
|
|
|
|
int _sys_soc_suspend(s32_t ticks)
|
|
{
|
|
int sys_state;
|
|
|
|
post_ops_done = 0;
|
|
|
|
sys_state = sys_pm_policy_next_state(ticks, &pm_state);
|
|
|
|
switch (sys_state) {
|
|
case SYS_PM_LOW_POWER_STATE:
|
|
sys_pm_notify_lps_entry(pm_state);
|
|
|
|
/* Do CPU LPS operations */
|
|
sys_pm_debug_start_timer();
|
|
_sys_soc_set_power_state(pm_state);
|
|
sys_pm_debug_stop_timer();
|
|
break;
|
|
case SYS_PM_DEEP_SLEEP:
|
|
/* Don't need pm idle exit event notification */
|
|
_sys_soc_pm_idle_exit_notification_disable();
|
|
|
|
sys_pm_notify_lps_entry(pm_state);
|
|
|
|
/* Save device states and turn off peripherals as necessary */
|
|
if (sys_pm_suspend_devices()) {
|
|
LOG_ERR("System level device suspend failed\n");
|
|
break;
|
|
}
|
|
|
|
/* Enter CPU deep sleep state */
|
|
sys_pm_debug_start_timer();
|
|
_sys_soc_set_power_state(pm_state);
|
|
sys_pm_debug_stop_timer();
|
|
|
|
/* Turn on peripherals and restore device states as necessary */
|
|
sys_pm_resume_devices();
|
|
break;
|
|
default:
|
|
/* No PM operations */
|
|
LOG_DBG("\nNo PM operations done\n");
|
|
break;
|
|
}
|
|
|
|
if (sys_state != SYS_PM_NOT_HANDLED) {
|
|
|
|
sys_pm_log_debug_info(pm_state);
|
|
/*
|
|
* Do any arch or soc specific post operations specific to the
|
|
* power state.
|
|
*/
|
|
if (!post_ops_done) {
|
|
post_ops_done = 1;
|
|
sys_pm_notify_lps_exit(pm_state);
|
|
_sys_soc_power_state_post_ops(pm_state);
|
|
}
|
|
}
|
|
|
|
return sys_state;
|
|
}
|
|
|
|
void _sys_soc_resume(void)
|
|
{
|
|
/*
|
|
* This notification is called from the ISR of the event
|
|
* that caused exit from kernel idling after PM operations.
|
|
*
|
|
* Some CPU low power states require enabling of interrupts
|
|
* atomically when entering those states. The wake up from
|
|
* such a state first executes code in the ISR of the interrupt
|
|
* that caused the wake. This hook will be called from the ISR.
|
|
* For such CPU LPS states, do post operations and restores here.
|
|
* The kernel scheduler will get control after the ISR finishes
|
|
* and it may schedule another thread.
|
|
*
|
|
* Call _sys_soc_pm_idle_exit_notification_disable() if this
|
|
* notification is not required.
|
|
*/
|
|
if (!post_ops_done) {
|
|
post_ops_done = 1;
|
|
sys_pm_notify_lps_exit(pm_state);
|
|
_sys_soc_power_state_post_ops(pm_state);
|
|
}
|
|
}
|
|
|
|
static int sys_pm_init(struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
sys_pm_create_device_list();
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(sys_pm_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|