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:
Piotr Zięcik 2019-01-11 10:53:57 +01:00 committed by Carles Cufí
commit b6bf56c3fc
7 changed files with 80 additions and 88 deletions

View file

@ -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 */

View file

@ -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;
} }

View file

@ -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"

View file

@ -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;
} }

View file

@ -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;
} }

View file

@ -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;
} }

View file

@ -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);