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