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(&notifier);
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(&notifier);
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(&notifier);
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(&notifier);
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(&notifier);
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