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