1 /*
2 * Copyright (c) 2021 Intel Corporation.
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 #define NUMBER_OF_DEVICES 3
12
13 #define TEST_DOMAIN DT_NODELABEL(test_domain)
14 #define TEST_DEVA DT_NODELABEL(test_dev_a)
15 #define TEST_DEVB DT_NODELABEL(test_dev_b)
16
17 static const struct device *const domain = DEVICE_DT_GET(TEST_DOMAIN);
18 static const struct device *const deva = DEVICE_DT_GET(TEST_DEVA);
19 static const struct device *const devb = DEVICE_DT_GET(TEST_DEVB);
20 static const struct device *devc;
21 static int testing_domain_on_notitication;
22 static int testing_domain_off_notitication;
23
domain_pm_action(const struct device * dev,enum pm_device_action action)24 static int domain_pm_action(const struct device *dev,
25 enum pm_device_action action)
26 {
27 int rc = 0;
28
29 switch (action) {
30 case PM_DEVICE_ACTION_RESUME:
31 /* Switch power on */
32 pm_device_children_action_run(dev, PM_DEVICE_ACTION_TURN_ON, NULL);
33 break;
34 case PM_DEVICE_ACTION_SUSPEND:
35 pm_device_children_action_run(dev, PM_DEVICE_ACTION_TURN_OFF, NULL);
36 break;
37 case PM_DEVICE_ACTION_TURN_ON:
38 __fallthrough;
39 case PM_DEVICE_ACTION_TURN_OFF:
40 break;
41 default:
42 rc = -ENOTSUP;
43 }
44
45 return rc;
46
47 }
48
deva_pm_action(const struct device * dev,enum pm_device_action pm_action)49 static int deva_pm_action(const struct device *dev,
50 enum pm_device_action pm_action)
51 {
52 ARG_UNUSED(dev);
53
54 if (testing_domain_on_notitication > 0) {
55 if (pm_action == PM_DEVICE_ACTION_TURN_ON) {
56 testing_domain_on_notitication--;
57 }
58 } else if (testing_domain_off_notitication > 0) {
59 if (pm_action == PM_DEVICE_ACTION_TURN_OFF) {
60 testing_domain_off_notitication--;
61 }
62 }
63
64 return 0;
65 }
66
67 /*
68 * Device B will return -ENOTSUP for TURN_ON and TURN_OFF actions.
69 * This way we can check if the subsystem properly handled its state.
70 */
devb_pm_action(const struct device * dev,enum pm_device_action pm_action)71 static int devb_pm_action(const struct device *dev,
72 enum pm_device_action pm_action)
73 {
74 int ret = 0;
75
76 ARG_UNUSED(dev);
77
78 if (testing_domain_on_notitication > 0) {
79 if (pm_action == PM_DEVICE_ACTION_TURN_ON) {
80 ret = -ENOTSUP;
81 testing_domain_on_notitication--;
82 }
83 } else if (testing_domain_off_notitication > 0) {
84 if (pm_action == PM_DEVICE_ACTION_TURN_OFF) {
85 ret = -ENOTSUP;
86 testing_domain_off_notitication--;
87 }
88 }
89
90 return ret;
91 }
92
93
94 PM_DEVICE_DT_DEFINE(TEST_DOMAIN, domain_pm_action);
95 DEVICE_DT_DEFINE(TEST_DOMAIN, NULL, PM_DEVICE_DT_GET(TEST_DOMAIN),
96 NULL, NULL, POST_KERNEL, 10, NULL);
97
98 PM_DEVICE_DT_DEFINE(TEST_DEVA, deva_pm_action);
99 DEVICE_DT_DEFINE(TEST_DEVA, NULL, PM_DEVICE_DT_GET(TEST_DEVA),
100 NULL, NULL, POST_KERNEL, 20, NULL);
101
102 PM_DEVICE_DT_DEFINE(TEST_DEVB, devb_pm_action);
103 DEVICE_DT_DEFINE(TEST_DEVB, NULL, PM_DEVICE_DT_GET(TEST_DEVB),
104 NULL, NULL, POST_KERNEL, 30, NULL);
105
106 PM_DEVICE_DEFINE(devc, deva_pm_action);
107 DEVICE_DEFINE(devc, "devc", NULL, PM_DEVICE_GET(devc),
108 NULL, NULL, POST_KERNEL, 40, NULL);
109
110 /**
111 * @brief Test the power domain behavior
112 *
113 * Scenarios tested:
114 *
115 * - get + put multiple devices under a domain
116 * - notification when domain state changes
117 */
ZTEST(power_domain_1cpu,test_power_domain_device_runtime)118 ZTEST(power_domain_1cpu, test_power_domain_device_runtime)
119 {
120 int ret;
121 enum pm_device_state state;
122
123 devc = DEVICE_GET(devc);
124
125 pm_device_init_suspended(domain);
126 pm_device_init_suspended(deva);
127 pm_device_init_suspended(devb);
128 pm_device_init_suspended(devc);
129
130 pm_device_runtime_enable(domain);
131 pm_device_runtime_enable(deva);
132 pm_device_runtime_enable(devb);
133 pm_device_runtime_enable(devc);
134
135 ret = pm_device_power_domain_remove(devc, domain);
136 zassert_equal(ret, -ENOENT);
137
138 ret = pm_device_power_domain_add(devc, domain);
139 zassert_equal(ret, 0);
140
141 /* At this point all devices should be SUSPENDED */
142 pm_device_state_get(domain, &state);
143 zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
144
145 pm_device_state_get(deva, &state);
146 zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
147
148 pm_device_state_get(devb, &state);
149 zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
150
151 pm_device_state_get(devc, &state);
152 zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
153
154 /* Now test if "get" a device will resume the domain */
155 ret = pm_device_runtime_get(deva);
156 zassert_equal(ret, 0);
157
158 pm_device_state_get(deva, &state);
159 zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
160
161 pm_device_state_get(domain, &state);
162 zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
163
164 ret = pm_device_runtime_get(devc);
165 zassert_equal(ret, 0);
166
167 ret = pm_device_runtime_get(devb);
168 zassert_equal(ret, 0);
169
170 ret = pm_device_runtime_put(deva);
171 zassert_equal(ret, 0);
172
173 /*
174 * The domain has to still be active since device B
175 * is still in use.
176 */
177 pm_device_state_get(domain, &state);
178 zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
179
180 /*
181 * Now the domain should be suspended since there is no
182 * one using it.
183 */
184 ret = pm_device_runtime_put(devb);
185 zassert_equal(ret, 0);
186
187 ret = pm_device_runtime_put(devc);
188 zassert_equal(ret, 0);
189
190 pm_device_state_get(domain, &state);
191 zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
192
193 /*
194 * With the domain suspended the device state should be OFF, since
195 * the power was completely cut.
196 */
197 pm_device_state_get(devb, &state);
198 zassert_equal(state, PM_DEVICE_STATE_OFF);
199
200 pm_device_state_get(deva, &state);
201 zassert_equal(state, PM_DEVICE_STATE_OFF);
202
203 /*
204 * Now lets test that devices are notified when the domain
205 * changes its state.
206 */
207
208 /* Three devices has to get the notification */
209 testing_domain_on_notitication = NUMBER_OF_DEVICES;
210 ret = pm_device_runtime_get(domain);
211 zassert_equal(ret, 0);
212
213 zassert_equal(testing_domain_on_notitication, 0);
214
215 testing_domain_off_notitication = NUMBER_OF_DEVICES;
216 ret = pm_device_runtime_put(domain);
217 zassert_equal(ret, 0);
218
219 zassert_equal(testing_domain_off_notitication, 0);
220
221 ret = pm_device_power_domain_remove(devc, domain);
222 zassert_equal(ret, 0);
223 }
224
225 #define TEST_DOMAIN_BALANCED DT_NODELABEL(test_domain_balanced)
226 #define TEST_DEV_BALANCED DT_NODELABEL(test_dev_balanced)
227
228 PM_DEVICE_DT_DEFINE(TEST_DOMAIN_BALANCED, domain_pm_action);
229 DEVICE_DT_DEFINE(TEST_DOMAIN_BALANCED, NULL, PM_DEVICE_DT_GET(TEST_DOMAIN_BALANCED),
230 NULL, NULL, POST_KERNEL, 10, NULL);
231
232 PM_DEVICE_DT_DEFINE(TEST_DEV_BALANCED, deva_pm_action);
233 DEVICE_DT_DEFINE(TEST_DEV_BALANCED, NULL, PM_DEVICE_DT_GET(TEST_DEV_BALANCED),
234 NULL, NULL, POST_KERNEL, 20, NULL);
235
236 /**
237 * @brief Test power domain requests are balanced
238 *
239 * Scenarios tested:
240 *
241 * - get + put device with a PD while PM is disabled
242 */
ZTEST(power_domain_1cpu,test_power_domain_device_balanced)243 ZTEST(power_domain_1cpu, test_power_domain_device_balanced)
244 {
245 const struct device *balanced_domain = DEVICE_DT_GET(TEST_DOMAIN_BALANCED);
246 const struct device *dev = DEVICE_DT_GET(TEST_DEV_BALANCED);
247 enum pm_device_state state;
248 int ret;
249
250 /* Init domain */
251 pm_device_init_suspended(balanced_domain);
252 pm_device_runtime_enable(balanced_domain);
253
254 /* At this point domain should be SUSPENDED */
255 pm_device_state_get(balanced_domain, &state);
256 zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
257
258 /* Get and put the device without PM enabled should not change the domain */
259 ret = pm_device_runtime_get(dev);
260 zassert_equal(ret, 0);
261 ret = pm_device_runtime_put(dev);
262 zassert_equal(ret, 0);
263
264 pm_device_state_get(balanced_domain, &state);
265 zassert_equal(state, PM_DEVICE_STATE_SUSPENDED);
266
267 /* Same thing with the domain in active state */
268 ret = pm_device_runtime_get(balanced_domain);
269 zassert_equal(ret, 0);
270 pm_device_state_get(balanced_domain, &state);
271 zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
272
273 ret = pm_device_runtime_get(dev);
274 zassert_equal(ret, 0);
275 ret = pm_device_runtime_put(dev);
276 zassert_equal(ret, 0);
277
278 pm_device_state_get(balanced_domain, &state);
279 zassert_equal(state, PM_DEVICE_STATE_ACTIVE);
280 }
281
ZTEST(power_domain_1cpu,test_on_power_domain)282 ZTEST(power_domain_1cpu, test_on_power_domain)
283 {
284 zassert_true(device_is_ready(domain), "Device is not ready!");
285 zassert_true(device_is_ready(deva), "Device is not ready!");
286 devc = DEVICE_GET(devc);
287 zassert_true(device_is_ready(devc), "Device is not ready!");
288
289 pm_device_power_domain_remove(deva, domain);
290 zassert_false(pm_device_on_power_domain(deva), "deva is in the power domain.");
291 pm_device_power_domain_add(deva, domain);
292 zassert_true(pm_device_on_power_domain(deva), "deva is not in the power domain.");
293
294 pm_device_power_domain_add(devc, domain);
295 zassert_true(pm_device_on_power_domain(devc), "devc is not in the power domain.");
296 pm_device_power_domain_remove(devc, domain);
297 zassert_false(pm_device_on_power_domain(devc), "devc in the power domain.");
298 }
299
300 ZTEST_SUITE(power_domain_1cpu, NULL, NULL, ztest_simple_1cpu_before,
301 ztest_simple_1cpu_after, NULL);
302