/* * Copyright (c) 2018 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include "policy/pm_policy.h" #define LOG_LEVEL CONFIG_PM_LOG_LEVEL #include 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_suspend(s32_t ticks) { int sys_state; post_ops_done = 0; sys_state = sys_pm_policy_next_state(ticks, &pm_state); #ifdef CONFIG_PM_CONTROL_STATE_LOCK /* Check if PM state is locked */ if ((sys_state != SYS_PM_NOT_HANDLED) && !sys_pm_ctrl_is_state_enabled(sys_state)) { LOG_DBG("PM state locked %d\n", sys_state); return SYS_PM_NOT_HANDLED; } #endif 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_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_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_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_power_state_post_ops(pm_state); } } return sys_state; } void sys_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_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_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);