Skip to content

Commit 5a313e1

Browse files
nordic-krchkartben
authored andcommitted
pm: Add option to quickly detect power state availability
When all states are locked or latency requirement cannot be met by any power state it is important to be able to quickly exit suspend procedure because that usually means that application requires high performance. Add function for detecting if any power state is available. Additionally, add function pm_policy_state_is_available for checking if given state is available which means that it is not locked and fulfills current latency requirement. Signed-off-by: Krzysztof Chruściński <[email protected]>
1 parent e41909a commit 5a313e1

File tree

3 files changed

+121
-27
lines changed

3 files changed

+121
-27
lines changed

include/zephyr/pm/policy.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,31 @@ void pm_policy_state_lock_put(enum pm_state state, uint8_t substate_id);
137137
*/
138138
bool pm_policy_state_lock_is_active(enum pm_state state, uint8_t substate_id);
139139

140+
/**
141+
* @brief Check if a power state is available.
142+
*
143+
* It is unavailable if locked or latency requirement cannot be fulfilled in that state.
144+
*
145+
* @param state Power state.
146+
* @param substate_id Power substate ID. Use PM_ALL_SUBSTATES to affect all the
147+
* substates in the given power state.
148+
*
149+
* @retval true if power state is active.
150+
* @retval false if power state is not active.
151+
*/
152+
bool pm_policy_state_is_available(enum pm_state state, uint8_t substate_id);
153+
154+
/**
155+
* @brief Check if any power state can be used.
156+
*
157+
* Function allows to quickly check if any power state is available and exit
158+
* suspend operation early.
159+
*
160+
* @retval true if any power state is active.
161+
* @retval false if all power states are unavailable.
162+
*/
163+
bool pm_policy_state_any_active(void);
164+
140165
/**
141166
* @brief Register an event.
142167
*

subsys/pm/pm.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ bool pm_system_suspend(int32_t kernel_ticks)
147147

148148
SYS_PORT_TRACING_FUNC_ENTER(pm, system_suspend, kernel_ticks);
149149

150+
if (!pm_policy_state_any_active()) {
151+
/* Return early if all states are unavailable. */
152+
return false;
153+
}
154+
150155
/*
151156
* CPU needs to be fully wake up before the event is triggered.
152157
* We need to find out first the ticks to the next event

subsys/pm/policy/policy_state_lock.c

Lines changed: 91 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,44 +10,56 @@
1010
#include <zephyr/sys/__assert.h>
1111
#include <zephyr/sys/atomic.h>
1212
#include <zephyr/toolchain.h>
13+
#include <zephyr/spinlock.h>
1314

1415
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
1516

16-
#define DT_SUB_LOCK_INIT(node_id) \
17-
{ .state = PM_STATE_DT_INIT(node_id), \
18-
.substate_id = DT_PROP_OR(node_id, substate_id, 0), \
19-
.lock = ATOMIC_INIT(0), \
17+
#define DT_SUB_LOCK_INIT(node_id) \
18+
{ .state = PM_STATE_DT_INIT(node_id), \
19+
.substate_id = DT_PROP_OR(node_id, substate_id, 0), \
20+
.exit_latency_us = DT_PROP_OR(node_id, exit_latency_us, 0), \
2021
},
2122

2223
/**
2324
* State and substate lock structure.
2425
*
25-
* This struct is associating a reference counting to each <state,substate>
26-
* couple to be used with the pm_policy_substate_lock_* functions.
26+
* Struct holds all power states defined in the device tree. Array with counter
27+
* variables is in RAM and n-th counter is used for n-th power state. Structure
28+
* also holds exit latency for each state. It is used to disable power states
29+
* based on current latency requirement.
2730
*
2831
* Operations on this array are in the order of O(n) with the number of power
2932
* states and this is mostly due to the random nature of the substate value
3033
* (that can be anything from a small integer value to a bitmask). We can
3134
* probably do better with an hashmap.
3235
*/
33-
static struct {
36+
static const struct {
3437
enum pm_state state;
3538
uint8_t substate_id;
36-
atomic_t lock;
37-
} substate_lock_t[] = {
39+
uint32_t exit_latency_us;
40+
} substates[] = {
3841
DT_FOREACH_STATUS_OKAY(zephyr_power_state, DT_SUB_LOCK_INIT)
3942
};
43+
static atomic_t lock_cnt[ARRAY_SIZE(substates)];
44+
static atomic_t latency_mask = BIT_MASK(ARRAY_SIZE(substates));
45+
static atomic_t unlock_mask = BIT_MASK(ARRAY_SIZE(substates));
46+
static struct k_spinlock lock;
4047

4148
#endif
4249

4350
void pm_policy_state_lock_get(enum pm_state state, uint8_t substate_id)
4451
{
4552
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
46-
for (size_t i = 0; i < ARRAY_SIZE(substate_lock_t); i++) {
47-
if (substate_lock_t[i].state == state &&
48-
(substate_lock_t[i].substate_id == substate_id ||
49-
substate_id == PM_ALL_SUBSTATES)) {
50-
atomic_inc(&substate_lock_t[i].lock);
53+
for (size_t i = 0; i < ARRAY_SIZE(substates); i++) {
54+
if (substates[i].state == state &&
55+
(substates[i].substate_id == substate_id || substate_id == PM_ALL_SUBSTATES)) {
56+
k_spinlock_key_t key = k_spin_lock(&lock);
57+
58+
if (lock_cnt[i] == 0) {
59+
unlock_mask &= ~BIT(i);
60+
}
61+
lock_cnt[i]++;
62+
k_spin_unlock(&lock, key);
5163
}
5264
}
5365
#endif
@@ -56,15 +68,17 @@ void pm_policy_state_lock_get(enum pm_state state, uint8_t substate_id)
5668
void pm_policy_state_lock_put(enum pm_state state, uint8_t substate_id)
5769
{
5870
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
59-
for (size_t i = 0; i < ARRAY_SIZE(substate_lock_t); i++) {
60-
if (substate_lock_t[i].state == state &&
61-
(substate_lock_t[i].substate_id == substate_id ||
62-
substate_id == PM_ALL_SUBSTATES)) {
63-
atomic_t cnt = atomic_dec(&substate_lock_t[i].lock);
64-
65-
ARG_UNUSED(cnt);
71+
for (size_t i = 0; i < ARRAY_SIZE(substates); i++) {
72+
if (substates[i].state == state &&
73+
(substates[i].substate_id == substate_id || substate_id == PM_ALL_SUBSTATES)) {
74+
k_spinlock_key_t key = k_spin_lock(&lock);
6675

67-
__ASSERT(cnt >= 1, "Unbalanced state lock get/put");
76+
__ASSERT(lock_cnt[i] > 0, "Unbalanced state lock get/put");
77+
lock_cnt[i]--;
78+
if (lock_cnt[i] == 0) {
79+
unlock_mask |= BIT(i);
80+
}
81+
k_spin_unlock(&lock, key);
6882
}
6983
}
7084
#endif
@@ -73,14 +87,64 @@ void pm_policy_state_lock_put(enum pm_state state, uint8_t substate_id)
7387
bool pm_policy_state_lock_is_active(enum pm_state state, uint8_t substate_id)
7488
{
7589
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
76-
for (size_t i = 0; i < ARRAY_SIZE(substate_lock_t); i++) {
77-
if (substate_lock_t[i].state == state &&
78-
(substate_lock_t[i].substate_id == substate_id ||
79-
substate_id == PM_ALL_SUBSTATES)) {
80-
return (atomic_get(&substate_lock_t[i].lock) != 0);
90+
for (size_t i = 0; i < ARRAY_SIZE(substates); i++) {
91+
if (substates[i].state == state &&
92+
(substates[i].substate_id == substate_id || substate_id == PM_ALL_SUBSTATES)) {
93+
return atomic_get(&lock_cnt[i]) != 0;
8194
}
8295
}
8396
#endif
8497

8598
return false;
8699
}
100+
101+
bool pm_policy_state_is_available(enum pm_state state, uint8_t substate_id)
102+
{
103+
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
104+
for (size_t i = 0; i < ARRAY_SIZE(substates); i++) {
105+
if (substates[i].state == state &&
106+
(substates[i].substate_id == substate_id || substate_id == PM_ALL_SUBSTATES)) {
107+
return (atomic_get(&lock_cnt[i]) == 0) &&
108+
(atomic_get(&latency_mask) & BIT(i));
109+
}
110+
}
111+
#endif
112+
113+
return false;
114+
}
115+
116+
bool pm_policy_state_any_active(void)
117+
{
118+
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
119+
/* Check if there is any power state that is not locked and not disabled due
120+
* to latency requirements.
121+
*/
122+
return atomic_get(&unlock_mask) & atomic_get(&latency_mask);
123+
#endif
124+
return true;
125+
}
126+
127+
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
128+
/* Callback is called whenever latency requirement changes. It is called under lock. */
129+
static void pm_policy_latency_update_locked(int32_t max_latency_us)
130+
{
131+
for (size_t i = 0; i < ARRAY_SIZE(substates); i++) {
132+
if (substates[i].exit_latency_us >= max_latency_us) {
133+
latency_mask &= ~BIT(i);
134+
} else {
135+
latency_mask |= BIT(i);
136+
}
137+
}
138+
}
139+
140+
static int pm_policy_latency_init(void)
141+
{
142+
static struct pm_policy_latency_subscription sub;
143+
144+
pm_policy_latency_changed_subscribe(&sub, pm_policy_latency_update_locked);
145+
146+
return 0;
147+
}
148+
149+
SYS_INIT(pm_policy_latency_init, PRE_KERNEL_1, 0);
150+
#endif

0 commit comments

Comments
 (0)