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
|
||||
/**
|
||||
* @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 */
|
||||
|
||||
|
|
|
@ -42,12 +42,14 @@ static void _deep_sleep(enum power_states state)
|
|||
void sys_set_power_state(enum power_states state)
|
||||
{
|
||||
switch (state) {
|
||||
#if (defined(CONFIG_SYS_POWER_LOW_POWER_STATE))
|
||||
case SYS_POWER_STATE_CPU_LPS:
|
||||
qm_ss_power_cpu_ss1(QM_SS_POWER_CPU_SS1_TIMER_ON);
|
||||
break;
|
||||
case SYS_POWER_STATE_CPU_LPS_1:
|
||||
qm_ss_power_cpu_ss2();
|
||||
break;
|
||||
#endif
|
||||
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
|
||||
case SYS_POWER_STATE_DEEP_SLEEP:
|
||||
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)
|
||||
{
|
||||
u32_t limit;
|
||||
|
||||
switch (state) {
|
||||
#if (defined(CONFIG_SYS_POWER_LOW_POWER_STATE))
|
||||
case SYS_POWER_STATE_CPU_LPS_1:
|
||||
/* 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);
|
||||
case SYS_POWER_STATE_CPU_LPS:
|
||||
__builtin_arc_seti(0);
|
||||
break;
|
||||
#endif
|
||||
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP))
|
||||
case SYS_POWER_STATE_DEEP_SLEEP:
|
||||
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);
|
||||
__builtin_arc_seti(0);
|
||||
break;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ config PM_CONTROL_STATE_LOCK
|
|||
help
|
||||
Enable OS Power Management state locking capability
|
||||
if any application wants to temporarily disable certain
|
||||
System Low Power states while doing any critical work
|
||||
or needs quick response from hardware resources.
|
||||
Power States while doing any critical work or needs quick
|
||||
response from hardware resources.
|
||||
|
||||
config PM_CONTROL_OS_DEBUG
|
||||
bool "Enable OS Power Management debug hooks"
|
||||
|
|
|
@ -15,65 +15,35 @@
|
|||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(power);
|
||||
|
||||
struct pm_ctrl_info {
|
||||
int pm_state;
|
||||
atomic_t count;
|
||||
};
|
||||
static atomic_t power_state_disable_count[SYS_POWER_STATE_MAX];
|
||||
|
||||
static struct pm_ctrl_info pm_ctrl[] = {
|
||||
{SYS_PM_LOW_POWER_STATE, ATOMIC_INIT(0)},
|
||||
{SYS_PM_DEEP_SLEEP, ATOMIC_INIT(0)},
|
||||
};
|
||||
|
||||
void sys_pm_ctrl_disable_state(int state)
|
||||
void sys_pm_ctrl_disable_state(enum power_states state)
|
||||
{
|
||||
if (state == SYS_PM_LOW_POWER_STATE) {
|
||||
__ASSERT(pm_ctrl[0].count < UINT_MAX,
|
||||
"Low Power state count overflowed\n");
|
||||
atomic_inc(&pm_ctrl[0].count);
|
||||
} else if (state == SYS_PM_DEEP_SLEEP) {
|
||||
__ASSERT(pm_ctrl[1].count < UINT_MAX,
|
||||
"Deep Sleep state count overflowed\n");
|
||||
atomic_inc(&pm_ctrl[1].count);
|
||||
} else {
|
||||
LOG_WRN("\nInvalid PM state");
|
||||
}
|
||||
atomic_val_t v;
|
||||
|
||||
__ASSERT(state < SYS_POWER_STATE_MAX, "Invalid power state!");
|
||||
v = atomic_inc(&power_state_disable_count[state]);
|
||||
__ASSERT(v < UINT_MAX, "Power state disable count overflowed!");
|
||||
|
||||
/* Make compiler happy when assertions are disabled. */
|
||||
(void)(v);
|
||||
}
|
||||
|
||||
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) {
|
||||
__ASSERT(pm_ctrl[0].count > 0,
|
||||
"Low Power state count underflowed\n");
|
||||
atomic_dec(&pm_ctrl[0].count);
|
||||
} else if (state == SYS_PM_DEEP_SLEEP) {
|
||||
__ASSERT(pm_ctrl[1].count > 0,
|
||||
"Deep Sleep state count underflowed\n");
|
||||
atomic_dec(&pm_ctrl[1].count);
|
||||
} else {
|
||||
LOG_WRN("\nInvalid PM state");
|
||||
}
|
||||
atomic_val_t v;
|
||||
|
||||
__ASSERT(state < SYS_POWER_STATE_MAX, "Invalid power state!");
|
||||
v = atomic_dec(&power_state_disable_count[state]);
|
||||
__ASSERT(v > 0, "Power state disable count underflowed!");
|
||||
|
||||
/* Make compiler happy when assertions are disabled. */
|
||||
(void)(v);
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
return (atomic_get(&power_state_disable_count[state]) == 0);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
static int cur_pm_idx;
|
||||
int i = cur_pm_idx;
|
||||
|
||||
if (cur_pm_idx >= ARRAY_SIZE(pm_policy)) {
|
||||
cur_pm_idx = 0;
|
||||
}
|
||||
do {
|
||||
i = (i + 1) % ARRAY_SIZE(pm_policy);
|
||||
|
||||
*pm_state = pm_policy[cur_pm_idx].pm_state;
|
||||
LOG_DBG("pm_state: %d, idx: %d\n", *pm_state, cur_pm_idx);
|
||||
#ifdef CONFIG_PM_CONTROL_STATE_LOCK
|
||||
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--) {
|
||||
#ifdef CONFIG_PM_CONTROL_STATE_LOCK
|
||||
if (!sys_pm_ctrl_is_state_enabled(pm_policy[i].pm_state)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if ((ticks == K_FOREVER) ||
|
||||
(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("ticks: %d, pm_state: %d, min_residency: %d, idx: %d\n",
|
||||
ticks, *pm_state, pm_policy[i].min_residency, i);
|
||||
|
||||
return pm_policy[i].sys_state;
|
||||
LOG_DBG("No suitable power state found!");
|
||||
return SYS_PM_NOT_HANDLED;
|
||||
}
|
||||
|
|
|
@ -81,15 +81,6 @@ int sys_suspend(s32_t ticks)
|
|||
|
||||
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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue