1 /*
2  * Copyright (c) 2021 Nordic Semiconductor ASA.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/pm/device.h>
9 #include <zephyr/pm/device_runtime.h>
10 
11 #include "test_driver.h"
12 #include "zephyr/sys/util_macro.h"
13 
14 
15 static const struct device *test_dev;
16 
17 #ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
18 static struct k_thread get_runner_td;
19 K_THREAD_STACK_DEFINE(get_runner_stack, 1024);
20 
get_runner(void * arg1,void * arg2,void * arg3)21 static void get_runner(void *arg1, void *arg2, void *arg3)
22 {
23 	int ret;
24 	bool ongoing;
25 
26 	ARG_UNUSED(arg1);
27 	ARG_UNUSED(arg2);
28 	ARG_UNUSED(arg3);
29 
30 	/* make sure we test blocking path (suspend is ongoing) */
31 	ongoing = test_driver_pm_ongoing(test_dev);
32 	zassert_equal(ongoing, true);
33 
34 	/* usage: 0, +1, resume: yes */
35 	ret = pm_device_runtime_get(test_dev);
36 	zassert_equal(ret, 0);
37 }
38 #endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
39 
test_api_setup(void * data)40 void test_api_setup(void *data)
41 {
42 	int ret;
43 	enum pm_device_state state;
44 
45 	test_driver_pm_retval(test_dev, 0);
46 
47 	/* check API always returns 0 when runtime PM is disabled */
48 	ret = pm_device_runtime_get(test_dev);
49 	zassert_equal(ret, 0);
50 	ret = pm_device_runtime_put(test_dev);
51 	zassert_equal(ret, 0);
52 	ret = pm_device_runtime_put_async(test_dev, K_NO_WAIT);
53 #ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
54 	zassert_equal(ret, 0);
55 #else
56 	zassert_equal(ret, -ENOSYS);
57 #endif
58 
59 	/* enable runtime PM */
60 	ret = pm_device_runtime_enable(test_dev);
61 	zassert_equal(ret, 0);
62 
63 	(void)pm_device_state_get(test_dev, &state);
64 	zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
65 
66 	/* enabling again should succeed (no-op) */
67 	ret = pm_device_runtime_enable(test_dev);
68 	zassert_equal(ret, 0);
69 }
70 
test_api_teardown(void * data)71 static void test_api_teardown(void *data)
72 {
73 	int ret;
74 	enum pm_device_state state;
75 
76 	/* let test driver finish async PM (in case it was left pending due to
77 	 * a failure)
78 	 */
79 	if (test_driver_pm_ongoing(test_dev)) {
80 		test_driver_pm_done(test_dev);
81 	}
82 
83 	/* disable runtime PM, make sure device is left into active state */
84 	ret = pm_device_runtime_disable(test_dev);
85 	zassert_equal(ret, 0);
86 
87 	(void)pm_device_state_get(test_dev, &state);
88 	zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
89 }
90 
91 /**
92  * @brief Test the behavior of the device runtime PM API.
93  *
94  * Scenarios tested:
95  *
96  * - get + put
97  * - get + asynchronous put until suspended
98  * - get + asynchronous put + get (while suspend still ongoing)
99  */
ZTEST(device_runtime_api,test_api)100 ZTEST(device_runtime_api, test_api)
101 {
102 	int ret;
103 	enum pm_device_state state;
104 
105 	/* device is initially suspended */
106 	(void)pm_device_state_get(test_dev, &state);
107 	zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
108 	zassert_equal(pm_device_runtime_usage(test_dev), 0);
109 
110 	/*** get + put ***/
111 
112 	/* usage: 0, 0, resume: no */
113 	test_driver_pm_retval(test_dev, -EIO);
114 	ret = pm_device_runtime_get(test_dev);
115 	zassert_equal(ret, -EIO);
116 	test_driver_pm_retval(test_dev, 0);
117 
118 	/* usage: 0, +1, resume: yes */
119 	ret = pm_device_runtime_get(test_dev);
120 	zassert_equal(ret, 0);
121 
122 	(void)pm_device_state_get(test_dev, &state);
123 	zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
124 
125 	/* usage: 1, +1, resume: no */
126 	ret = pm_device_runtime_get(test_dev);
127 	zassert_equal(ret, 0);
128 	zassert_equal(pm_device_runtime_usage(test_dev), 2);
129 
130 	/* usage: 2, -1, suspend: no */
131 	ret = pm_device_runtime_put(test_dev);
132 	zassert_equal(ret, 0);
133 
134 	(void)pm_device_state_get(test_dev, &state);
135 	zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
136 
137 	/* usage: 1, 0, suspend: no */
138 	test_driver_pm_retval(test_dev, -EIO);
139 	ret = pm_device_runtime_put(test_dev);
140 	zassert_equal(ret, -EIO);
141 	test_driver_pm_retval(test_dev, 0);
142 
143 	/* usage: 1, -1, suspend: yes */
144 	ret = pm_device_runtime_put(test_dev);
145 	zassert_equal(ret, 0);
146 	zassert_equal(pm_device_runtime_usage(test_dev), 0);
147 
148 	(void)pm_device_state_get(test_dev, &state);
149 	zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
150 
151 	/* usage: 0, -1, suspend: no (unbalanced call) */
152 	ret = pm_device_runtime_put(test_dev);
153 	zassert_equal(ret, -EALREADY);
154 	zassert_equal(pm_device_runtime_usage(test_dev), 0);
155 
156 #ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
157 	/*** get + asynchronous put until suspended ***/
158 
159 	/* usage: 0, +1, resume: yes */
160 	ret = pm_device_runtime_get(test_dev);
161 	zassert_equal(ret, 0);
162 	zassert_equal(pm_device_runtime_usage(test_dev), 1);
163 
164 	(void)pm_device_state_get(test_dev, &state);
165 	zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
166 
167 	test_driver_pm_async(test_dev);
168 
169 	/* usage: 1, -1, suspend: yes (queued) */
170 	ret = pm_device_runtime_put_async(test_dev, K_NO_WAIT);
171 	zassert_equal(ret, 0);
172 	zassert_equal(pm_device_runtime_usage(test_dev), 0);
173 
174 	if (IS_ENABLED(CONFIG_TEST_PM_DEVICE_ISR_SAFE)) {
175 		/* In sync mode async put is equivalent as normal put. */
176 		(void)pm_device_state_get(test_dev, &state);
177 		zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
178 		zassert_equal(pm_device_runtime_usage(test_dev), 0);
179 	} else {
180 		(void)pm_device_state_get(test_dev, &state);
181 		zassert_equal(state, PM_DEVICE_STATE_SUSPENDING);
182 
183 		/* usage: 0, -1, suspend: no (unbalanced call) */
184 		ret = pm_device_runtime_put(test_dev);
185 		zassert_equal(ret, -EALREADY);
186 
187 		/* usage: 0, -1, suspend: no (unbalanced call) */
188 		ret = pm_device_runtime_put_async(test_dev, K_NO_WAIT);
189 		zassert_equal(ret, -EALREADY);
190 		zassert_equal(pm_device_runtime_usage(test_dev), 0);
191 
192 		/* unblock test driver and let it finish */
193 		test_driver_pm_done(test_dev);
194 		k_yield();
195 
196 		(void)pm_device_state_get(test_dev, &state);
197 		zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
198 
199 		/*** get + asynchronous put + get (while suspend still ongoing) ***/
200 
201 		/* usage: 0, +1, resume: yes */
202 		ret = pm_device_runtime_get(test_dev);
203 		zassert_equal(ret, 0);
204 
205 		(void)pm_device_state_get(test_dev, &state);
206 		zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
207 
208 		test_driver_pm_async(test_dev);
209 
210 		/* usage: 1, -1, suspend: yes (queued) */
211 		ret = pm_device_runtime_put_async(test_dev, K_NO_WAIT);
212 		zassert_equal(ret, 0);
213 
214 		(void)pm_device_state_get(test_dev, &state);
215 		zassert_equal(state, PM_DEVICE_STATE_SUSPENDING);
216 
217 		/* let suspension start */
218 		k_yield();
219 
220 		/* create and start get_runner thread
221 		 * get_runner thread is used to test synchronous path while asynchronous
222 		 * is ongoing. It is important to set its priority >= to the system work
223 		 * queue to make sure sync path run by the thread is forced to wait.
224 		 */
225 		k_thread_create(&get_runner_td, get_runner_stack,
226 				K_THREAD_STACK_SIZEOF(get_runner_stack), get_runner,
227 				NULL, NULL, NULL,
228 				COND_CODE_1(CONFIG_PM_DEVICE_RUNTIME_USE_DEDICATED_WQ,
229 				(CONFIG_PM_DEVICE_RUNTIME_DEDICATED_WQ_PRIO),
230 				(CONFIG_SYSTEM_WORKQUEUE_PRIORITY)), 0, K_NO_WAIT);
231 		k_yield();
232 
233 		/* let driver suspend to finish and wait until get_runner finishes
234 		 * resuming the driver
235 		 */
236 		test_driver_pm_done(test_dev);
237 		k_thread_join(&get_runner_td, K_FOREVER);
238 
239 		(void)pm_device_state_get(test_dev, &state);
240 		zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
241 
242 		/* Test if getting a device before an async operation starts does
243 		 * not trigger any device pm action.
244 		 */
245 		size_t count = test_driver_pm_count(test_dev);
246 
247 		ret = pm_device_runtime_put_async(test_dev, K_MSEC(10));
248 		zassert_equal(ret, 0);
249 
250 		(void)pm_device_state_get(test_dev, &state);
251 		zassert_equal(state, PM_DEVICE_STATE_SUSPENDING);
252 
253 		ret = pm_device_runtime_get(test_dev);
254 		zassert_equal(ret, 0);
255 
256 		/* Now lets check if the calls above have triggered a device
257 		 * pm action
258 		 */
259 		zassert_equal(count, test_driver_pm_count(test_dev));
260 
261 		/*
262 		 * test if async put with a delay respects the given time.
263 		 */
264 		ret = pm_device_runtime_put_async(test_dev, K_MSEC(100));
265 
266 		(void)pm_device_state_get(test_dev, &state);
267 		zassert_equal(state, PM_DEVICE_STATE_SUSPENDING);
268 
269 		k_sleep(K_MSEC(80));
270 
271 		/* It should still be suspending since we have waited less than
272 		 * the delay we've set.
273 		 */
274 		(void)pm_device_state_get(test_dev, &state);
275 		zassert_equal(state, PM_DEVICE_STATE_SUSPENDING);
276 
277 		k_sleep(K_MSEC(30));
278 
279 		/* Now it should be already suspended */
280 		(void)pm_device_state_get(test_dev, &state);
281 		zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
282 	}
283 
284 	/* Put operation should fail due the state be locked. */
285 	ret = pm_device_runtime_disable(test_dev);
286 	zassert_equal(ret, 0);
287 	zassert_equal(pm_device_runtime_usage(test_dev), -ENOTSUP);
288 #endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
289 }
290 
291 DEVICE_DEFINE(pm_unsupported_device, "PM Unsupported", NULL, NULL, NULL, NULL,
292 	      POST_KERNEL, 0, NULL);
293 
ZTEST(device_runtime_api,test_unsupported)294 ZTEST(device_runtime_api, test_unsupported)
295 {
296 	const struct device *const dev = DEVICE_GET(pm_unsupported_device);
297 
298 	zassert_false(pm_device_runtime_is_enabled(dev), "");
299 	zassert_equal(pm_device_runtime_enable(dev), -ENOTSUP, "");
300 	zassert_equal(pm_device_runtime_disable(dev), -ENOTSUP, "");
301 	zassert_equal(pm_device_runtime_get(dev), 0, "");
302 	zassert_equal(pm_device_runtime_put(dev), 0, "");
303 #ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
304 	zassert_equal(pm_device_runtime_put_async(dev, K_NO_WAIT), 0, "");
305 #else
306 	zassert_equal(pm_device_runtime_put_async(dev, K_NO_WAIT), -ENOSYS, "");
307 #endif
308 }
309 
dev_pm_control(const struct device * dev,enum pm_device_action action)310 int dev_pm_control(const struct device *dev, enum pm_device_action action)
311 {
312 	ARG_UNUSED(dev);
313 	ARG_UNUSED(action);
314 
315 	return 0;
316 }
317 
318 PM_DEVICE_DT_DEFINE(DT_NODELABEL(test_dev), dev_pm_control);
319 DEVICE_DT_DEFINE(DT_NODELABEL(test_dev), NULL, PM_DEVICE_DT_GET(DT_NODELABEL(test_dev)),
320 		 NULL, NULL, POST_KERNEL, 80, NULL);
321 
ZTEST(device_runtime_api,test_pm_device_runtime_auto)322 ZTEST(device_runtime_api, test_pm_device_runtime_auto)
323 {
324 	const struct device *const dev = DEVICE_DT_GET(DT_NODELABEL(test_dev));
325 
326 	zassert_true(pm_device_runtime_is_enabled(dev), "");
327 	zassert_equal(pm_device_runtime_get(dev), 0, "");
328 	zassert_equal(pm_device_runtime_put(dev), 0, "");
329 }
330 
device_runtime_api_setup(void)331 void *device_runtime_api_setup(void)
332 {
333 	test_dev = device_get_binding("test_driver");
334 	zassert_not_null(test_dev);
335 	return NULL;
336 }
337 
338 ZTEST_SUITE(device_runtime_api, NULL, device_runtime_api_setup,
339 			test_api_setup, test_api_teardown, NULL);
340