power: pm_ctrl: Allow fine-grained power state locking
This commit enables fine-grained power state locking. Now, each power state could be independently enabled or disabled. Signed-off-by: Piotr Zięcik <piotr.ziecik@nordicsemi.no>
This commit is contained in:
parent
bfc66346b8
commit
b6bf56c3fc
7 changed files with 80 additions and 88 deletions
|
@ -162,25 +162,38 @@ extern void sys_pm_dump_debug_info(void);
|
||||||
|
|
||||||
#ifdef CONFIG_PM_CONTROL_STATE_LOCK
|
#ifdef CONFIG_PM_CONTROL_STATE_LOCK
|
||||||
/**
|
/**
|
||||||
* @brief Disable system PM state
|
* @brief Disable particular power state
|
||||||
*
|
*
|
||||||
* Disable system Low power states like LPS or Deep Sleep states.
|
* @details Disabled state cannot be selected by the Zephyr power
|
||||||
|
* management policies. Application defined policy should
|
||||||
|
* use the @ref sys_pm_ctrl_is_state_enabled function to
|
||||||
|
* check if given state could is enabled and could be used.
|
||||||
|
*
|
||||||
|
* @param [in] state Power state to be disabled.
|
||||||
*/
|
*/
|
||||||
extern void sys_pm_ctrl_disable_state(int state);
|
extern void sys_pm_ctrl_disable_state(enum power_states state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enable system PM state
|
* @brief Enable particular power state
|
||||||
*
|
*
|
||||||
* Enable system Low power states like LPS or Deep Sleep states.
|
* @details Enabled state can be selected by the Zephyr power
|
||||||
|
* management policies. Application defined policy should
|
||||||
|
* use the @ref sys_pm_ctrl_is_state_enabled function to
|
||||||
|
* check if given state could is enabled and could be used.
|
||||||
|
* By default all power states are enabled.
|
||||||
|
*
|
||||||
|
* @param [in] state Power state to be enabled.
|
||||||
*/
|
*/
|
||||||
extern void sys_pm_ctrl_enable_state(int state);
|
extern void sys_pm_ctrl_enable_state(enum power_states state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get enable status of a PM state
|
* @brief Check if particular power state is enabled
|
||||||
*
|
*
|
||||||
* Get enable status of a system PM state.
|
* This function returns true if given power state is enabled.
|
||||||
|
*
|
||||||
|
* @param [in] state Power state.
|
||||||
*/
|
*/
|
||||||
extern bool sys_pm_ctrl_is_state_enabled(int state);
|
extern bool sys_pm_ctrl_is_state_enabled(enum power_states state);
|
||||||
|
|
||||||
#endif /* CONFIG_PM_CONTROL_STATE_LOCK */
|
#endif /* CONFIG_PM_CONTROL_STATE_LOCK */
|
||||||
|
|
||||||
|
|
|
@ -42,12 +42,14 @@ static void _deep_sleep(enum power_states state)
|
||||||
void sys_set_power_state(enum power_states state)
|
void sys_set_power_state(enum power_states state)
|
||||||
{
|
{
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
#if (defined(CONFIG_SYS_POWER_LOW_POWER_STATE))
|
||||||
case SYS_POWER_STATE_CPU_LPS:
|
case SYS_POWER_STATE_CPU_LPS:
|
||||||
qm_ss_power_cpu_ss1(QM_SS_POWER_CPU_SS1_TIMER_ON);
|
qm_ss_power_cpu_ss1(QM_SS_POWER_CPU_SS1_TIMER_ON);
|
||||||
break;
|
break;
|
||||||
case SYS_POWER_STATE_CPU_LPS_1:
|
case SYS_POWER_STATE_CPU_LPS_1:
|
||||||
qm_ss_power_cpu_ss2();
|
qm_ss_power_cpu_ss2();
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
|
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
|
||||||
case SYS_POWER_STATE_DEEP_SLEEP:
|
case SYS_POWER_STATE_DEEP_SLEEP:
|
||||||
qm_ss_power_soc_lpss_enable();
|
qm_ss_power_soc_lpss_enable();
|
||||||
|
@ -66,16 +68,17 @@ void sys_set_power_state(enum power_states state)
|
||||||
|
|
||||||
void sys_power_state_post_ops(enum power_states state)
|
void sys_power_state_post_ops(enum power_states state)
|
||||||
{
|
{
|
||||||
u32_t limit;
|
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
#if (defined(CONFIG_SYS_POWER_LOW_POWER_STATE))
|
||||||
case SYS_POWER_STATE_CPU_LPS_1:
|
case SYS_POWER_STATE_CPU_LPS_1:
|
||||||
/* Expire the timer as it is disabled in SS2. */
|
/* Expire the timer as it is disabled in SS2. */
|
||||||
limit = _arc_v2_aux_reg_read(_ARC_V2_TMR0_LIMIT);
|
u32_t limit = _arc_v2_aux_reg_read(_ARC_V2_TMR0_LIMIT);
|
||||||
_arc_v2_aux_reg_write(_ARC_V2_TMR0_COUNT, limit - 1);
|
_arc_v2_aux_reg_write(_ARC_V2_TMR0_COUNT, limit - 1);
|
||||||
case SYS_POWER_STATE_CPU_LPS:
|
case SYS_POWER_STATE_CPU_LPS:
|
||||||
__builtin_arc_seti(0);
|
__builtin_arc_seti(0);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
|
||||||
case SYS_POWER_STATE_DEEP_SLEEP:
|
case SYS_POWER_STATE_DEEP_SLEEP:
|
||||||
qm_ss_power_soc_lpss_disable();
|
qm_ss_power_soc_lpss_disable();
|
||||||
|
|
||||||
|
@ -98,7 +101,7 @@ void sys_power_state_post_ops(enum power_states state)
|
||||||
QM_IR_UNMASK_INTERRUPTS(QM_INTERRUPT_ROUTER->rtc_0_int_mask);
|
QM_IR_UNMASK_INTERRUPTS(QM_INTERRUPT_ROUTER->rtc_0_int_mask);
|
||||||
__builtin_arc_seti(0);
|
__builtin_arc_seti(0);
|
||||||
break;
|
break;
|
||||||
break;
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ config PM_CONTROL_STATE_LOCK
|
||||||
help
|
help
|
||||||
Enable OS Power Management state locking capability
|
Enable OS Power Management state locking capability
|
||||||
if any application wants to temporarily disable certain
|
if any application wants to temporarily disable certain
|
||||||
System Low Power states while doing any critical work
|
Power States while doing any critical work or needs quick
|
||||||
or needs quick response from hardware resources.
|
response from hardware resources.
|
||||||
|
|
||||||
config PM_CONTROL_OS_DEBUG
|
config PM_CONTROL_OS_DEBUG
|
||||||
bool "Enable OS Power Management debug hooks"
|
bool "Enable OS Power Management debug hooks"
|
||||||
|
|
|
@ -15,65 +15,35 @@
|
||||||
#include <logging/log.h>
|
#include <logging/log.h>
|
||||||
LOG_MODULE_DECLARE(power);
|
LOG_MODULE_DECLARE(power);
|
||||||
|
|
||||||
struct pm_ctrl_info {
|
static atomic_t power_state_disable_count[SYS_POWER_STATE_MAX];
|
||||||
int pm_state;
|
|
||||||
atomic_t count;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct pm_ctrl_info pm_ctrl[] = {
|
void sys_pm_ctrl_disable_state(enum power_states state)
|
||||||
{SYS_PM_LOW_POWER_STATE, ATOMIC_INIT(0)},
|
|
||||||
{SYS_PM_DEEP_SLEEP, ATOMIC_INIT(0)},
|
|
||||||
};
|
|
||||||
|
|
||||||
void sys_pm_ctrl_disable_state(int state)
|
|
||||||
{
|
{
|
||||||
if (state == SYS_PM_LOW_POWER_STATE) {
|
atomic_val_t v;
|
||||||
__ASSERT(pm_ctrl[0].count < UINT_MAX,
|
|
||||||
"Low Power state count overflowed\n");
|
__ASSERT(state < SYS_POWER_STATE_MAX, "Invalid power state!");
|
||||||
atomic_inc(&pm_ctrl[0].count);
|
v = atomic_inc(&power_state_disable_count[state]);
|
||||||
} else if (state == SYS_PM_DEEP_SLEEP) {
|
__ASSERT(v < UINT_MAX, "Power state disable count overflowed!");
|
||||||
__ASSERT(pm_ctrl[1].count < UINT_MAX,
|
|
||||||
"Deep Sleep state count overflowed\n");
|
/* Make compiler happy when assertions are disabled. */
|
||||||
atomic_inc(&pm_ctrl[1].count);
|
(void)(v);
|
||||||
} else {
|
|
||||||
LOG_WRN("\nInvalid PM state");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sys_pm_ctrl_enable_state(int state)
|
void sys_pm_ctrl_enable_state(enum power_states state)
|
||||||
{
|
{
|
||||||
if (state == SYS_PM_LOW_POWER_STATE) {
|
atomic_val_t v;
|
||||||
__ASSERT(pm_ctrl[0].count > 0,
|
|
||||||
"Low Power state count underflowed\n");
|
__ASSERT(state < SYS_POWER_STATE_MAX, "Invalid power state!");
|
||||||
atomic_dec(&pm_ctrl[0].count);
|
v = atomic_dec(&power_state_disable_count[state]);
|
||||||
} else if (state == SYS_PM_DEEP_SLEEP) {
|
__ASSERT(v > 0, "Power state disable count underflowed!");
|
||||||
__ASSERT(pm_ctrl[1].count > 0,
|
|
||||||
"Deep Sleep state count underflowed\n");
|
/* Make compiler happy when assertions are disabled. */
|
||||||
atomic_dec(&pm_ctrl[1].count);
|
(void)(v);
|
||||||
} else {
|
|
||||||
LOG_WRN("\nInvalid PM state");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sys_pm_ctrl_is_state_enabled(int state)
|
bool sys_pm_ctrl_is_state_enabled(enum power_states state)
|
||||||
{
|
{
|
||||||
bool enabled = true;
|
__ASSERT(state < SYS_POWER_STATE_MAX, "Invalid power state!");
|
||||||
|
|
||||||
switch (state) {
|
return (atomic_get(&power_state_disable_count[state]) == 0);
|
||||||
case SYS_PM_LOW_POWER_STATE:
|
|
||||||
if (pm_ctrl[0].count) {
|
|
||||||
enabled = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SYS_PM_DEEP_SLEEP:
|
|
||||||
if (pm_ctrl[1].count) {
|
|
||||||
enabled = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_WRN("\nInvalid PM state");
|
|
||||||
enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return enabled;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,13 +57,23 @@ static struct sys_pm_policy pm_policy[] = {
|
||||||
int sys_pm_policy_next_state(s32_t ticks, enum power_states *pm_state)
|
int sys_pm_policy_next_state(s32_t ticks, enum power_states *pm_state)
|
||||||
{
|
{
|
||||||
static int cur_pm_idx;
|
static int cur_pm_idx;
|
||||||
|
int i = cur_pm_idx;
|
||||||
|
|
||||||
if (cur_pm_idx >= ARRAY_SIZE(pm_policy)) {
|
do {
|
||||||
cur_pm_idx = 0;
|
i = (i + 1) % ARRAY_SIZE(pm_policy);
|
||||||
}
|
|
||||||
|
|
||||||
*pm_state = pm_policy[cur_pm_idx].pm_state;
|
#ifdef CONFIG_PM_CONTROL_STATE_LOCK
|
||||||
LOG_DBG("pm_state: %d, idx: %d\n", *pm_state, cur_pm_idx);
|
if (!sys_pm_ctrl_is_state_enabled(pm_policy[i].pm_state)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
cur_pm_state = i;
|
||||||
|
*pm_state = pm_policy[cur_pm_state].pm_state;
|
||||||
|
|
||||||
return pm_policy[cur_pm_idx++].sys_state;
|
LOG_DBG("pm_state: %d, idx: %d\n", *pm_state, i);
|
||||||
|
return pm_policy[cur_pm_state].sys_state;
|
||||||
|
} while (i != curr_pm_idx);
|
||||||
|
|
||||||
|
LOG_DBG("No suitable power state found!");
|
||||||
|
return SYS_PM_NOT_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,15 +73,20 @@ int sys_pm_policy_next_state(s32_t ticks, enum power_states *pm_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = ARRAY_SIZE(pm_policy) - 1; i >= 0; i--) {
|
for (i = ARRAY_SIZE(pm_policy) - 1; i >= 0; i--) {
|
||||||
|
#ifdef CONFIG_PM_CONTROL_STATE_LOCK
|
||||||
|
if (!sys_pm_ctrl_is_state_enabled(pm_policy[i].pm_state)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if ((ticks == K_FOREVER) ||
|
if ((ticks == K_FOREVER) ||
|
||||||
(ticks >= pm_policy[i].min_residency)) {
|
(ticks >= pm_policy[i].min_residency)) {
|
||||||
break;
|
*pm_state = pm_policy[i].pm_state;
|
||||||
|
LOG_DBG("ticks: %d, pm_state: %d, min_residency: %d\n",
|
||||||
|
ticks, *pm_state, pm_policy[i].min_residency);
|
||||||
|
return pm_policy[i].sys_state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*pm_state = pm_policy[i].pm_state;
|
LOG_DBG("No suitable power state found!");
|
||||||
LOG_DBG("ticks: %d, pm_state: %d, min_residency: %d, idx: %d\n",
|
return SYS_PM_NOT_HANDLED;
|
||||||
ticks, *pm_state, pm_policy[i].min_residency, i);
|
|
||||||
|
|
||||||
return pm_policy[i].sys_state;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,15 +81,6 @@ int sys_suspend(s32_t ticks)
|
||||||
|
|
||||||
sys_state = sys_pm_policy_next_state(ticks, &pm_state);
|
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) {
|
switch (sys_state) {
|
||||||
case SYS_PM_LOW_POWER_STATE:
|
case SYS_PM_LOW_POWER_STATE:
|
||||||
sys_pm_notify_lps_entry(pm_state);
|
sys_pm_notify_lps_entry(pm_state);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue