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