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