blob: 1b320b54497ee84c7d0274867ea193ca928b2d1b [file] [log] [blame]
/*
* Copyright (c) 2018 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "test.h"
/* In Tickless Kernel mode, time is passed in milliseconds instead of ticks */
#define TICKS_TO_SECONDS_MULTIPLIER 1000
#define TIME_UNIT_STRING "milliseconds"
#define MIN_TIME_TO_SUSPEND ((SECONDS_TO_SLEEP * \
TICKS_TO_SECONDS_MULTIPLIER) - \
(TICKS_TO_SECONDS_MULTIPLIER / 2))
#ifdef CONFIG_TICKLESS_KERNEL
#define SECS_TO_TICKS 1000
#else
#define SECS_TO_TICKS 100
#endif
#define LPS_MAX_THR (60 * SECS_TO_TICKS)
#define LPS_1_MAX_THR (120 * SECS_TO_TICKS)
/* Suspend Devices based on Ordered List */
void suspend_devices(void);
/* Resume Devices */
void resume_devices(void);
static int post_ops_done = 1;
static int pm_state;
/* This Function decides which Low Power Mode to Enter based on Idleness.
* In actual Application this decision can be made using time (ticks)
* And RTC timer can be programmed to wake the device after time elapsed.
*/
static int pm_policy_get_state(s32_t ticks)
{
/*
* Power Management Policy:
* 0 - 60 Sec: Enter Constant Latency Mode
* 61 - 120 Sec: Enter Low Power state
* 121 and above : Enter Deep Sleep state
*/
if ((ticks != K_FOREVER) && ticks <= LPS_MAX_THR) {
return SYS_POWER_STATE_CPU_LPS;
} else if ((ticks > LPS_MAX_THR) &&
(ticks <= LPS_1_MAX_THR)) {
return SYS_POWER_STATE_CPU_LPS_1;
} else {
return SYS_POWER_STATE_DEEP_SLEEP;
}
}
static void low_power_state_exit(void)
{
/* Perform some Application specific task on Low Power Mode Exit */
/* Turn on suspended peripherals/clocks as necessary */
printk("-- Low Power State exit !\n");
}
static int low_power_state_entry(void)
{
if (pm_state == SYS_POWER_STATE_CPU_LPS) {
printk("--> Entering into Constant Latency State -");
} else {
printk("--> Entering into Low Power State -");
}
_sys_soc_set_power_state(pm_state);
return SYS_PM_LOW_POWER_STATE;
}
static void low_power_suspend_exit(void)
{
/* Turn on peripherals and restore device states as necessary */
resume_devices();
printk("-- Low Power Suspend State exit!\n");
}
static int low_power_suspend_entry(void)
{
printk("--> Entering into Deep Sleep State -");
/* Don't need pm idle exit event notification */
_sys_soc_pm_idle_exit_notification_disable();
/* Save device states and turn off peripherals as necessary */
suspend_devices();
_sys_soc_set_power_state(SYS_POWER_STATE_DEEP_SLEEP);
/* Exiting from Deep Sleep State */
low_power_suspend_exit();
return SYS_PM_DEEP_SLEEP;
}
/* Function name : _sys_soc_suspend
* Return Value : Power Mode Entered Success/Failure
* Input : Idleness in number of Ticks
*
* Description: All request from Idle thread to Enter into
* Low Power Mode or Deep Sleep State land in this function
*/
int _sys_soc_suspend(s32_t ticks)
{
int ret = SYS_PM_NOT_HANDLED;
post_ops_done = 0;
if ((ticks != K_FOREVER) && (ticks < MIN_TIME_TO_SUSPEND)) {
printk("Not enough time for PM operations " TIME_UNIT_STRING
" : %d).\n", ticks);
return SYS_PM_NOT_HANDLED;
}
pm_state = pm_policy_get_state(ticks);
switch (pm_state) {
case SYS_POWER_STATE_CPU_LPS:
case SYS_POWER_STATE_CPU_LPS_1:
/* Do CPU LPS operations */
ret = low_power_state_entry();
break;
case SYS_POWER_STATE_DEEP_SLEEP:
/* Do Low Power Suspend operations */
ret = low_power_suspend_entry();
break;
default:
/* No PM operations */
printk("\nNo PM operations done\n");
ret = SYS_PM_NOT_HANDLED;
break;
}
if (ret != SYS_PM_NOT_HANDLED) {
/*
* Do any arch or soc specific post operations specific to the
* power state.
*/
if (!post_ops_done) {
if ((pm_state == SYS_POWER_STATE_CPU_LPS_1) ||
(pm_state == SYS_POWER_STATE_CPU_LPS)) {
low_power_state_exit();
}
post_ops_done = 1;
_sys_soc_power_state_post_ops(pm_state);
}
}
return ret;
}
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) {
if (pm_state == SYS_POWER_STATE_CPU_LPS) {
low_power_state_exit();
}
post_ops_done = 1;
_sys_soc_power_state_post_ops(pm_state);
}
}