1 /*
2 * Copyright (c) 2020 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/sys/printk.h>
8 #include <zephyr/types.h>
9 #include <zephyr/pm/device.h>
10 #include <zephyr/pm/device_runtime.h>
11 #include <zephyr/ztest.h>
12 #include <ksched.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/pm/pm.h>
15 #include "dummy_driver.h"
16
17 #define SLEEP_MSEC 100
18 #define SLEEP_TIMEOUT K_MSEC(SLEEP_MSEC)
19
20 /* for checking power suspend and resume order between system and devices */
21 static bool enter_low_power;
22 static bool notify_app_entry;
23 static bool notify_app_exit;
24 static bool set_pm;
25 static bool leave_idle;
26 static bool idle_entered;
27 static bool testing_device_runtime;
28 static bool testing_device_order;
29 static bool testing_device_lock;
30
31 static const struct device *device_dummy;
32 static struct dummy_driver_api *api;
33
34 static const struct device *const device_a =
35 DEVICE_DT_GET(DT_INST(0, test_device_pm));
36 static const struct device *const device_c =
37 DEVICE_DT_GET(DT_INST(2, test_device_pm));
38
39 /*
40 * According with the initialization level, devices A, B and C are
41 * initialized in the following order A -> B -> C.
42 *
43 * The power management subsystem uses this order to suspend and resume
44 * devices. Devices are suspended in the reverse order:
45 *
46 * C -> B -> A
47 *
48 * While resuming uses the initialization order:
49 *
50 * A -> B -> C
51 *
52 * This test checks if these order is correct checking devices A and C states
53 * when suspending / resuming device B.
54 */
55
device_a_pm_action(const struct device * dev,enum pm_device_action pm_action)56 static int device_a_pm_action(const struct device *dev,
57 enum pm_device_action pm_action)
58 {
59 ARG_UNUSED(dev);
60 ARG_UNUSED(pm_action);
61
62 return 0;
63 }
64
65 PM_DEVICE_DT_DEFINE(DT_INST(0, test_device_pm), device_a_pm_action);
66
67 DEVICE_DT_DEFINE(DT_INST(0, test_device_pm), NULL,
68 PM_DEVICE_DT_GET(DT_INST(0, test_device_pm)), NULL, NULL,
69 PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
70 NULL);
71
72
device_b_pm_action(const struct device * dev,enum pm_device_action pm_action)73 static int device_b_pm_action(const struct device *dev,
74 enum pm_device_action pm_action)
75 {
76 enum pm_device_state state_a;
77 enum pm_device_state state_c;
78
79 if (!testing_device_order) {
80 return 0;
81 }
82
83 (void)pm_device_state_get(device_a, &state_a);
84 (void)pm_device_state_get(device_c, &state_c);
85
86 switch (pm_action) {
87 case PM_DEVICE_ACTION_RESUME:
88 /* Check if device C is still suspended */
89 zassert_equal(state_c, PM_DEVICE_STATE_SUSPENDED,
90 "Inconsistent states");
91 /* Check if device A is already active */
92 zassert_equal(state_a, PM_DEVICE_STATE_ACTIVE,
93 "Inconsistent states");
94 break;
95 case PM_DEVICE_ACTION_SUSPEND:
96 /* Check if device C is already suspended */
97 zassert_equal(state_c, PM_DEVICE_STATE_SUSPENDED,
98 "Inconsistent states");
99 /* Check if device A is still active */
100 zassert_equal(state_a, PM_DEVICE_STATE_ACTIVE,
101 "Inconsistent states");
102 break;
103 default:
104 break;
105 }
106
107 return 0;
108 }
109
110 PM_DEVICE_DT_DEFINE(DT_INST(1, test_device_pm), device_b_pm_action);
111
112 DEVICE_DT_DEFINE(DT_INST(1, test_device_pm), NULL,
113 PM_DEVICE_DT_GET(DT_INST(1, test_device_pm)), NULL, NULL,
114 PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
115 NULL);
116
device_c_pm_action(const struct device * dev,enum pm_device_action pm_action)117 static int device_c_pm_action(const struct device *dev,
118 enum pm_device_action pm_action)
119 {
120 ARG_UNUSED(dev);
121 ARG_UNUSED(pm_action);
122
123 return 0;
124 }
125
126 PM_DEVICE_DT_DEFINE(DT_INST(2, test_device_pm), device_c_pm_action);
127
128 DEVICE_DT_DEFINE(DT_INST(2, test_device_pm), NULL,
129 PM_DEVICE_DT_GET(DT_INST(2, test_device_pm)), NULL, NULL,
130 POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
131 NULL);
132
device_init_failed(const struct device * dev)133 static int device_init_failed(const struct device *dev)
134 {
135 ARG_UNUSED(dev);
136
137 /* Return error to mark device as not ready. */
138 return -EIO;
139 }
140
device_d_pm_action(const struct device * dev,enum pm_device_action pm_action)141 static int device_d_pm_action(const struct device *dev,
142 enum pm_device_action pm_action)
143 {
144 ARG_UNUSED(dev);
145 ARG_UNUSED(pm_action);
146
147 zassert_unreachable("Entered PM handler for unready device");
148
149 return 0;
150 }
151
152 PM_DEVICE_DT_DEFINE(DT_INST(3, test_device_pm), device_d_pm_action);
153
154 DEVICE_DT_DEFINE(DT_INST(3, test_device_pm), device_init_failed,
155 PM_DEVICE_DT_GET(DT_INST(3, test_device_pm)), NULL, NULL,
156 POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
157 NULL);
158
pm_state_set(enum pm_state state,uint8_t substate_id)159 void pm_state_set(enum pm_state state, uint8_t substate_id)
160 {
161 ARG_UNUSED(substate_id);
162 ARG_UNUSED(state);
163
164 enum pm_device_state device_power_state;
165
166 /* If testing device order this function does not need to anything */
167 if (testing_device_order) {
168 return;
169 }
170
171 if (testing_device_lock) {
172 pm_device_state_get(device_a, &device_power_state);
173
174 /*
175 * If the device has its state locked the device has
176 * to be ACTIVE
177 */
178 zassert_true(device_power_state == PM_DEVICE_STATE_ACTIVE,
179 NULL);
180 return;
181 }
182
183
184 /* at this point, notify_pm_state_entry() implemented in
185 * this file has been called and set_pm should have been set
186 */
187 zassert_true(set_pm == true,
188 "Notification to enter suspend was not sent to the App");
189
190 /* this function is called after devices enter low power state */
191 pm_device_state_get(device_dummy, &device_power_state);
192
193 if (testing_device_runtime) {
194 /* If device runtime is enable, the device should still be
195 * active
196 */
197 zassert_true(device_power_state == PM_DEVICE_STATE_ACTIVE);
198 } else {
199 /* at this point, devices have been deactivated */
200 zassert_false(device_power_state == PM_DEVICE_STATE_ACTIVE);
201 }
202
203 /* this function is called when system entering low power state, so
204 * parameter state should not be PM_STATE_ACTIVE
205 */
206 zassert_false(state == PM_STATE_ACTIVE,
207 "Entering low power state with a wrong parameter");
208 }
209
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)210 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
211 {
212 ARG_UNUSED(state);
213 ARG_UNUSED(substate_id);
214
215 /* pm_system_suspend is entered with irq locked
216 * unlock irq before leave pm_system_suspend
217 */
218 irq_unlock(0);
219 }
220
221 /* Our PM policy handler */
pm_policy_next_state(uint8_t cpu,int32_t ticks)222 const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks)
223 {
224 static struct pm_state_info info;
225
226 ARG_UNUSED(cpu);
227
228 /* make sure this is idle thread */
229 zassert_true(z_is_idle_thread_object(_current));
230 zassert_true(ticks == _kernel.idle);
231 zassert_false(k_can_yield());
232 idle_entered = true;
233
234 if (enter_low_power) {
235 enter_low_power = false;
236 notify_app_entry = true;
237 info.state = PM_STATE_SUSPEND_TO_IDLE;
238 } else {
239 /* only test pm_policy_next_state()
240 * no PM operation done
241 */
242 info.state = PM_STATE_ACTIVE;
243 }
244 return &info;
245 }
246
247 /* implement in application, called by idle thread */
notify_pm_state_entry(enum pm_state state)248 static void notify_pm_state_entry(enum pm_state state)
249 {
250 enum pm_device_state device_power_state;
251
252 /* enter suspend */
253 zassert_true(notify_app_entry == true,
254 "Notification to enter suspend was not sent to the App");
255 zassert_true(z_is_idle_thread_object(_current));
256 zassert_equal(state, PM_STATE_SUSPEND_TO_IDLE);
257
258 pm_device_state_get(device_dummy, &device_power_state);
259 if (testing_device_runtime) {
260 /* If device runtime is enable, the device should still be
261 * active
262 */
263 zassert_true(device_power_state == PM_DEVICE_STATE_ACTIVE);
264 } else {
265 /* at this point, devices should not be active */
266 zassert_false(device_power_state == PM_DEVICE_STATE_ACTIVE);
267 }
268 set_pm = true;
269 notify_app_exit = true;
270 }
271
272 /* implement in application, called by idle thread */
notify_pm_state_exit(enum pm_state state)273 static void notify_pm_state_exit(enum pm_state state)
274 {
275 enum pm_device_state device_power_state;
276
277 /* leave suspend */
278 zassert_true(notify_app_exit == true,
279 "Notification to leave suspend was not sent to the App");
280 zassert_true(z_is_idle_thread_object(_current));
281 zassert_equal(state, PM_STATE_SUSPEND_TO_IDLE);
282
283 /* at this point, devices are active again*/
284 pm_device_state_get(device_dummy, &device_power_state);
285 zassert_equal(device_power_state, PM_DEVICE_STATE_ACTIVE);
286 leave_idle = true;
287
288 }
289
290 /*
291 * @brief test power idle
292 *
293 * @details
294 * - The global idle routine executes when no other work is available
295 * - The idle routine provide a timeout parameter to the suspend routine
296 * indicating the amount of time guaranteed to expire before the next
297 * timeout, pm_policy_next_state() handle this parameter.
298 * - In this case, pm_policy_next_sate() return PM_STATE_ACTIVE,
299 * so there is no low power operation happen.
300 *
301 * @see pm_policy_next_state()
302 *
303 * @ingroup power_tests
304 */
ZTEST(power_management_1cpu,test_power_idle)305 ZTEST(power_management_1cpu, test_power_idle)
306 {
307 TC_PRINT("give way to idle thread\n");
308 k_sleep(SLEEP_TIMEOUT);
309 zassert_true(idle_entered, "Never entered idle thread");
310 }
311
312 static struct pm_notifier notifier = {
313 .state_entry = notify_pm_state_entry,
314 .state_exit = notify_pm_state_exit,
315 };
316
317 /*
318 * @brief test power state transition
319 *
320 * @details
321 * - The system support control of power state ordering between
322 * subsystems and devices
323 * - The application can control system power state transitions in idle thread
324 * through pm_notify_pm_state_entry and pm_notify_pm_state_exit
325 *
326 * @see pm_notify_pm_state_entry(), pm_notify_pm_state_exit()
327 *
328 * @ingroup power_tests
329 */
ZTEST(power_management_1cpu,test_power_state_trans)330 ZTEST(power_management_1cpu, test_power_state_trans)
331 {
332 int ret;
333
334 pm_notifier_register(¬ifier);
335 enter_low_power = true;
336
337 ret = pm_device_runtime_disable(device_dummy);
338 zassert_true(ret == 0, "Failed to disable device runtime PM");
339
340 /* give way to idle thread */
341 k_sleep(SLEEP_TIMEOUT);
342 zassert_true(leave_idle);
343
344 ret = pm_device_runtime_enable(device_dummy);
345 zassert_true(ret == 0, "Failed to enable device runtime PM");
346
347 pm_notifier_unregister(¬ifier);
348 }
349
350 /*
351 * @brief notification between system and device
352 *
353 * @details
354 * - device driver notify its power state change by pm_device_runtime_get and
355 * pm_device_runtime_put_async
356 * - system inform device system power state change through device interface
357 * pm_action_cb
358 *
359 * @see pm_device_runtime_get(), pm_device_runtime_put_async(),
360 * pm_device_action_run(), pm_device_state_get()
361 *
362 * @ingroup power_tests
363 */
ZTEST(power_management_1cpu,test_power_state_notification)364 ZTEST(power_management_1cpu, test_power_state_notification)
365 {
366 int ret;
367 enum pm_device_state device_power_state;
368
369 pm_notifier_register(¬ifier);
370 enter_low_power = true;
371
372 ret = api->open(device_dummy);
373 zassert_true(ret == 0, "Fail to open device");
374
375 pm_device_state_get(device_dummy, &device_power_state);
376 zassert_equal(device_power_state, PM_DEVICE_STATE_ACTIVE);
377
378
379 /* The device should be kept active even when the system goes idle */
380 testing_device_runtime = true;
381
382 k_sleep(SLEEP_TIMEOUT);
383 zassert_true(leave_idle);
384
385 api->close(device_dummy);
386 pm_device_state_get(device_dummy, &device_power_state);
387 zassert_equal(device_power_state, PM_DEVICE_STATE_SUSPENDED);
388 pm_notifier_unregister(¬ifier);
389 testing_device_runtime = false;
390 }
391
ZTEST(power_management_1cpu,test_device_order)392 ZTEST(power_management_1cpu, test_device_order)
393 {
394 zassert_true(device_is_ready(device_a), "device a not ready");
395 zassert_true(device_is_ready(device_c), "device c not ready");
396
397 testing_device_order = true;
398 enter_low_power = true;
399
400 k_sleep(SLEEP_TIMEOUT);
401
402 testing_device_order = false;
403 }
404
405 /**
406 * @brief Test the device busy APIs.
407 */
ZTEST(power_management_1cpu,test_busy)408 ZTEST(power_management_1cpu, test_busy)
409 {
410 bool busy;
411
412 busy = pm_device_is_any_busy();
413 zassert_false(busy);
414
415 pm_device_busy_set(device_dummy);
416
417 busy = pm_device_is_any_busy();
418 zassert_true(busy);
419
420 busy = pm_device_is_busy(device_dummy);
421 zassert_true(busy);
422
423 pm_device_busy_clear(device_dummy);
424
425 busy = pm_device_is_any_busy();
426 zassert_false(busy);
427
428 busy = pm_device_is_busy(device_dummy);
429 zassert_false(busy);
430 }
431
ZTEST(power_management_1cpu,test_device_state_lock)432 ZTEST(power_management_1cpu, test_device_state_lock)
433 {
434 pm_device_state_lock(device_a);
435 zassert_true(pm_device_state_is_locked(device_a));
436
437 testing_device_lock = true;
438 enter_low_power = true;
439
440 k_sleep(SLEEP_TIMEOUT);
441
442 pm_device_state_unlock(device_a);
443
444 testing_device_lock = false;
445 }
446
power_management_1cpu_teardown(void * data)447 void power_management_1cpu_teardown(void *data)
448 {
449 pm_notifier_unregister(¬ifier);
450 }
451
power_management_1cpu_setup(void)452 static void *power_management_1cpu_setup(void)
453 {
454 device_dummy = device_get_binding(DUMMY_DRIVER_NAME);
455 api = (struct dummy_driver_api *)device_dummy->api;
456 return NULL;
457 }
458
459 ZTEST_SUITE(power_management_1cpu, NULL, power_management_1cpu_setup,
460 ztest_simple_1cpu_before, ztest_simple_1cpu_after,
461 power_management_1cpu_teardown);
462