Skip to content

Commit 2b49266

Browse files
Flavio Ceolinnashif
authored andcommitted
power: Add notifier API
Get rid of weak functions adding a new API to register an object to receive notifications when the system changes power state. Signed-off-by: Flavio Ceolin <flavio.ceolin@intel.com>
1 parent 8a5154d commit 2b49266

File tree

5 files changed

+132
-33
lines changed

5 files changed

+132
-33
lines changed

include/power/power.h

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,34 @@ extern unsigned char pm_idle_exit_notify;
6565
* @{
6666
*/
6767

68+
/**
69+
* Power management notifier struct
70+
*
71+
* This struct contains callbacks that are called when the target enters and
72+
* exits power states.
73+
*
74+
* As currently implemented the entry callback is invoked when
75+
* transitioning from PM_STATE_ACTIVE to another state, and the exit
76+
* callback is invoked when transitioning from a non-active state to
77+
* PM_STATE_ACTIVE. This behavior may change in the future.
78+
*
79+
* @note These callbacks can be called from the ISR of the event
80+
* that caused the kernel exit from idling.
81+
*/
82+
struct pm_notifier {
83+
sys_snode_t _node;
84+
/**
85+
* Application defined function for doing any target specific operations
86+
* for power state entry.
87+
*/
88+
void (*state_entry)(enum power_states state);
89+
/**
90+
* Application defined function for doing any target specific operations
91+
* for power state exit.
92+
*/
93+
void (*state_exit)(enum power_states state);
94+
};
95+
6896
/**
6997
* @brief Check if particular power state is a sleep state.
7098
*
@@ -299,20 +327,27 @@ enum power_states pm_system_suspend(int32_t ticks);
299327
void pm_power_state_exit_post_ops(enum power_states state);
300328

301329
/**
302-
* @brief Application defined function for power state entry
330+
* @brief Register a power management notifier
331+
*
332+
* Register the given notifier from the power management notification
333+
* list.
303334
*
304-
* Application defined function for doing any target specific operations
305-
* for power state entry.
335+
* @param notifier pm_notifier object to be registered.
306336
*/
307-
void pm_notify_power_state_entry(enum power_states state);
337+
void pm_notifier_register(struct pm_notifier *notifier);
308338

309339
/**
310-
* @brief Application defined function for sleep state exit
340+
* @brief Unregister a power management notifier
341+
*
342+
* Remove the given notifier from the power management notification
343+
* list. After that this object callbacks will not be called.
344+
*
345+
* @param notifier pm_notifier object to be unregistered.
311346
*
312-
* Application defined function for doing any target specific operations
313-
* for sleep state exit.
347+
* @return 0 if the notifier was successfully removed, a negative value
348+
* otherwise.
314349
*/
315-
void pm_notify_power_state_exit(enum power_states state);
350+
int pm_notifier_unregister(struct pm_notifier *notifier);
316351

317352
/**
318353
* @}

samples/boards/mec15xxevb_assy6853/power_management/src/power_mgmt.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ static void pm_latency_check(void)
8585
}
8686

8787
/* Hooks to count entry/exit */
88-
void pm_notify_power_state_entry(enum power_states state)
88+
static void notify_pm_state_entry(enum power_states state)
8989
{
9090
if (!checks_enabled) {
9191
return;
@@ -106,7 +106,7 @@ void pm_notify_power_state_entry(enum power_states state)
106106
}
107107
}
108108

109-
void pm_notify_power_state_exit(enum power_states state)
109+
static void notify_pm_state_exit(enum power_states state)
110110
{
111111
if (!checks_enabled) {
112112
return;
@@ -251,6 +251,11 @@ static void resume_all_tasks(void)
251251
k_thread_resume(&thread_b_id);
252252
}
253253

254+
static struct pm_notifier notifier = {
255+
.state_entry = notify_pm_state_entry,
256+
.state_exit = notify_pm_state_exit,
257+
};
258+
254259
int test_pwr_mgmt_multithread(bool use_logging, uint8_t cycles)
255260
{
256261
uint8_t iterations = cycles;
@@ -260,6 +265,7 @@ int test_pwr_mgmt_multithread(bool use_logging, uint8_t cycles)
260265
* https://github.com/zephyrproject-rtos/zephyr/issues/20033
261266
*/
262267

268+
pm_notifier_register(&notifier);
263269
create_tasks();
264270

265271
LOG_WRN("PM multi-thread test started for cycles: %d, logging: %d",
@@ -312,6 +318,7 @@ int test_pwr_mgmt_multithread(bool use_logging, uint8_t cycles)
312318
LOG_INF("PM multi-thread completed");
313319
pm_check_counters(cycles);
314320
pm_reset_counters();
321+
pm_notifier_unregister(&notifier);
315322

316323
return 0;
317324
}
@@ -323,6 +330,7 @@ int test_pwr_mgmt_singlethread(bool use_logging, uint8_t cycles)
323330
LOG_WRN("PM single-thread test started for cycles: %d, logging: %d",
324331
cycles, use_logging);
325332

333+
pm_notifier_register(&notifier);
326334
checks_enabled = true;
327335
while (iterations-- > 0) {
328336

@@ -359,6 +367,7 @@ int test_pwr_mgmt_singlethread(bool use_logging, uint8_t cycles)
359367
LOG_INF("PM single-thread completed");
360368
pm_check_counters(cycles);
361369
pm_reset_counters();
370+
pm_notifier_unregister(&notifier);
362371

363372
return 0;
364373
}

subsys/power/power.c

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ LOG_MODULE_REGISTER(power);
1818
static int post_ops_done = 1;
1919
static enum power_states forced_pm_state = POWER_STATE_AUTO;
2020
static enum power_states pm_state;
21+
static sys_slist_t pm_notifiers = SYS_SLIST_STATIC_INIT(&pm_notifiers);
22+
static struct k_spinlock pm_notifier_lock;
2123

2224
#ifdef CONFIG_PM_DEBUG
2325

@@ -64,16 +66,6 @@ static void pm_log_debug_info(enum power_states state) { }
6466
void pm_dump_debug_info(void) { }
6567
#endif
6668

67-
__weak void pm_notify_power_state_entry(enum power_states state)
68-
{
69-
/* This function can be overridden by the application. */
70-
}
71-
72-
__weak void pm_notify_power_state_exit(enum power_states state)
73-
{
74-
/* This function can be overridden by the application. */
75-
}
76-
7769
void pm_power_state_force(enum power_states state)
7870
{
7971
__ASSERT(state >= POWER_STATE_AUTO &&
@@ -89,12 +81,38 @@ void pm_power_state_force(enum power_states state)
8981
#endif
9082
}
9183

84+
/*
85+
* Function called to notify when the system is entering / exiting a
86+
* power state
87+
*/
88+
static inline void pm_state_notify(bool entering_state)
89+
{
90+
struct pm_notifier *notifier;
91+
k_spinlock_key_t pm_notifier_key;
92+
void (*callback)(enum power_states state);
93+
94+
pm_notifier_key = k_spin_lock(&pm_notifier_lock);
95+
SYS_SLIST_FOR_EACH_CONTAINER(&pm_notifiers, notifier, _node) {
96+
if (entering_state) {
97+
callback = notifier->state_entry;
98+
} else {
99+
callback = notifier->state_exit;
100+
}
101+
102+
if (callback) {
103+
callback(pm_state);
104+
}
105+
}
106+
k_spin_unlock(&pm_notifier_lock, pm_notifier_key);
107+
}
108+
92109

93110
static enum power_states _handle_device_abort(enum power_states state)
94111
{
95112
LOG_DBG("Some devices didn't enter suspend state!");
96113
pm_resume_devices();
97-
pm_notify_power_state_exit(pm_state);
114+
pm_state_notify(false);
115+
98116
pm_state = POWER_STATE_ACTIVE;
99117
return pm_state;
100118
}
@@ -118,7 +136,7 @@ static enum power_states pm_policy_mgr(int32_t ticks)
118136
pm_is_deep_sleep_state(pm_state) : 0;
119137

120138
post_ops_done = 0;
121-
pm_notify_power_state_entry(pm_state);
139+
pm_state_notify(true);
122140

123141
if (deep_sleep) {
124142
/* Suspend peripherals. */
@@ -161,7 +179,7 @@ static enum power_states pm_policy_mgr(int32_t ticks)
161179
post_ops_done = 1;
162180
/* clear forced_pm_state */
163181
forced_pm_state = POWER_STATE_AUTO;
164-
pm_notify_power_state_exit(pm_state);
182+
pm_state_notify(false);
165183
pm_power_state_exit_post_ops(pm_state);
166184
}
167185

@@ -193,11 +211,33 @@ void pm_system_resume(void)
193211
*/
194212
if (!post_ops_done) {
195213
post_ops_done = 1;
196-
pm_notify_power_state_exit(pm_state);
214+
pm_state_notify(false);
197215
pm_power_state_exit_post_ops(pm_state);
198216
}
199217
}
200218

219+
void pm_notifier_register(struct pm_notifier *notifier)
220+
{
221+
k_spinlock_key_t pm_notifier_key = k_spin_lock(&pm_notifier_lock);
222+
223+
sys_slist_append(&pm_notifiers, &notifier->_node);
224+
k_spin_unlock(&pm_notifier_lock, pm_notifier_key);
225+
}
226+
227+
int pm_notifier_unregister(struct pm_notifier *notifier)
228+
{
229+
int ret = -EINVAL;
230+
k_spinlock_key_t pm_notifier_key;
231+
232+
pm_notifier_key = k_spin_lock(&pm_notifier_lock);
233+
if (sys_slist_find_and_remove(&pm_notifiers, &(notifier->_node))) {
234+
ret = 0;
235+
}
236+
k_spin_unlock(&pm_notifier_lock, pm_notifier_key);
237+
238+
return ret;
239+
}
240+
201241
#if CONFIG_PM_DEVICE
202242
static int pm_init(const struct device *dev)
203243
{
@@ -206,6 +246,5 @@ static int pm_init(const struct device *dev)
206246
pm_create_device_list();
207247
return 0;
208248
}
209-
210249
SYS_INIT(pm_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
211250
#endif /* CONFIG_PM_DEVICE */

tests/subsys/power/power_mgmt/src/main.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ static struct dummy_driver_api *api;
3232
*/
3333
__weak void pm_power_state_set(enum power_states state)
3434
{
35-
/* at this point, pm_notify_power_state_entry() implemented in
35+
/* at this point, notify_pm_state_entry() implemented in
3636
* this file has been called and set_pm should have been set
3737
*/
3838
zassert_true(set_pm == true,
@@ -88,7 +88,7 @@ enum power_states pm_policy_next_state(int ticks)
8888
}
8989

9090
/* implement in application, called by idle thread */
91-
void pm_notify_power_state_entry(enum power_states state)
91+
static void notify_pm_state_entry(enum power_states state)
9292
{
9393
uint32_t device_power_state;
9494

@@ -106,7 +106,7 @@ void pm_notify_power_state_entry(enum power_states state)
106106
}
107107

108108
/* implement in application, called by idle thread */
109-
void pm_notify_power_state_exit(enum power_states state)
109+
static void notify_pm_state_exit(enum power_states state)
110110
{
111111
uint32_t device_power_state;
112112

@@ -152,9 +152,9 @@ void test_power_idle(void)
152152
* - The system support control of power state ordering between
153153
* subsystems and devices
154154
* - The application can control system power state transitions in idle thread
155-
* through pm_notify_power_state_entry and pm_notify_power_state_exit
155+
* through pm_notify_pm_state_entry and pm_notify_pm_state_exit
156156
*
157-
* @see pm_notify_power_state_entry(), pm_notify_power_state_exit()
157+
* @see pm_notify_pm_state_entry(), pm_notify_pm_state_exit()
158158
*
159159
* @ingroup power_tests
160160
*/
@@ -211,6 +211,13 @@ void test_teardown(void)
211211

212212
void test_main(void)
213213
{
214+
struct pm_notifier notifier = {
215+
.state_entry = notify_pm_state_entry,
216+
.state_exit = notify_pm_state_exit,
217+
};
218+
219+
pm_notifier_register(&notifier);
220+
214221
ztest_test_suite(power_management_test,
215222
ztest_1cpu_unit_test(test_power_idle),
216223
ztest_unit_test_setup_teardown(test_power_state_trans,
@@ -221,4 +228,5 @@ void test_main(void)
221228
test_setup,
222229
test_teardown));
223230
ztest_run_test_suite(power_management_test);
231+
pm_notifier_unregister(&notifier);
224232
}

tests/subsys/power/power_mgmt_soc/src/power_mgmt.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ static void pm_latency_check(void)
6868
"Sleep entry latency is higher than expected");
6969
}
7070

71-
/* Hooks to count entry/exit */
72-
void pm_notify_power_state_entry(enum power_states state)
71+
static void notify_pm_state_entry(enum power_states state)
7372
{
7473
if (!checks_enabled) {
7574
return;
@@ -84,7 +83,7 @@ void pm_notify_power_state_entry(enum power_states state)
8483
}
8584
}
8685

87-
void pm_notify_power_state_exit(enum power_states state)
86+
static void notify_pm_state_exit(enum power_states state)
8887
{
8988
if (!checks_enabled) {
9089
return;
@@ -97,6 +96,11 @@ void pm_notify_power_state_exit(enum power_states state)
9796
}
9897
}
9998

99+
static struct pm_notifier notifier = {
100+
.state_entry = notify_pm_state_entry,
101+
.state_exit = notify_pm_state_exit,
102+
};
103+
100104
static void pm_check_counters(uint8_t cycles)
101105
{
102106
for (int i = 0; i < SLP_STATES_SUPPORTED; i++) {
@@ -218,6 +222,7 @@ int test_pwr_mgmt_multithread(uint8_t cycles)
218222
{
219223
uint8_t iterations = cycles;
220224

225+
pm_notifier_register(&notifier);
221226
create_tasks();
222227

223228
LOG_INF("PM multi-thread test started for cycles: %d", cycles);
@@ -257,6 +262,7 @@ int test_pwr_mgmt_multithread(uint8_t cycles)
257262
}
258263

259264
destroy_tasks();
265+
pm_notifier_unregister(&notifier);
260266

261267
LOG_INF("PM multi-thread completed");
262268
pm_check_counters(cycles);
@@ -271,6 +277,7 @@ int test_pwr_mgmt_singlethread(uint8_t cycles)
271277

272278
LOG_INF("PM single-thread test started for cycles: %d", cycles);
273279

280+
pm_notifier_register(&notifier);
274281
checks_enabled = true;
275282
while (iterations-- > 0) {
276283

@@ -296,6 +303,7 @@ int test_pwr_mgmt_singlethread(uint8_t cycles)
296303
pm_exit_marker();
297304
}
298305

306+
pm_notifier_unregister(&notifier);
299307
LOG_INF("PM single-thread completed");
300308
pm_check_counters(cycles);
301309
pm_reset_counters();

0 commit comments

Comments
 (0)