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