1 /*
2  * Copyright (c) 2018 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/device.h>
8 #include <zephyr/pm/device.h>
9 #include <zephyr/pm/device_runtime.h>
10 #include <zephyr/sys/iterable_sections.h>
11 
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(pm_device, CONFIG_PM_DEVICE_LOG_LEVEL);
14 
15 static const enum pm_device_state action_target_state[] = {
16 	[PM_DEVICE_ACTION_SUSPEND] = PM_DEVICE_STATE_SUSPENDED,
17 	[PM_DEVICE_ACTION_RESUME] = PM_DEVICE_STATE_ACTIVE,
18 	[PM_DEVICE_ACTION_TURN_OFF] = PM_DEVICE_STATE_OFF,
19 	[PM_DEVICE_ACTION_TURN_ON] = PM_DEVICE_STATE_SUSPENDED,
20 };
21 static const enum pm_device_state action_expected_state[] = {
22 	[PM_DEVICE_ACTION_SUSPEND] = PM_DEVICE_STATE_ACTIVE,
23 	[PM_DEVICE_ACTION_RESUME] = PM_DEVICE_STATE_SUSPENDED,
24 	[PM_DEVICE_ACTION_TURN_OFF] = PM_DEVICE_STATE_SUSPENDED,
25 	[PM_DEVICE_ACTION_TURN_ON] = PM_DEVICE_STATE_OFF,
26 };
27 
pm_device_state_str(enum pm_device_state state)28 const char *pm_device_state_str(enum pm_device_state state)
29 {
30 	switch (state) {
31 	case PM_DEVICE_STATE_ACTIVE:
32 		return "active";
33 	case PM_DEVICE_STATE_SUSPENDED:
34 		return "suspended";
35 	case PM_DEVICE_STATE_OFF:
36 		return "off";
37 	default:
38 		return "";
39 	}
40 }
41 
pm_device_action_run(const struct device * dev,enum pm_device_action action)42 int pm_device_action_run(const struct device *dev,
43 			 enum pm_device_action action)
44 {
45 	struct pm_device_base *pm = dev->pm_base;
46 	int ret;
47 
48 	if (pm == NULL) {
49 		return -ENOSYS;
50 	}
51 
52 	/* Validate action against current state */
53 	if (pm->state == action_target_state[action]) {
54 		return -EALREADY;
55 	}
56 	if (pm->state != action_expected_state[action]) {
57 		return -ENOTSUP;
58 	}
59 
60 	ret = pm->action_cb(dev, action);
61 	if (ret < 0) {
62 		/*
63 		 * TURN_ON and TURN_OFF are actions triggered by a power domain
64 		 * when it is resumed or suspended, which means that the energy
65 		 * to the device will be removed or added. For this reason, if
66 		 * the transition fails or the device does not handle these
67 		 * actions its state still needs to updated to reflect its
68 		 * physical behavior.
69 		 *
70 		 * The function will still return the error code so the domain
71 		 * can take whatever action is more appropriated.
72 		 */
73 		switch (action) {
74 		case PM_DEVICE_ACTION_TURN_ON:
75 			/* Store an error flag when the transition explicitly fails */
76 			if (ret != -ENOTSUP) {
77 				atomic_set_bit(&pm->flags, PM_DEVICE_FLAG_TURN_ON_FAILED);
78 			}
79 			__fallthrough;
80 		case PM_DEVICE_ACTION_TURN_OFF:
81 			pm->state = action_target_state[action];
82 			break;
83 		default:
84 			break;
85 		}
86 		return ret;
87 	}
88 
89 	pm->state = action_target_state[action];
90 	/* Power up flags are no longer relevant */
91 	if (action == PM_DEVICE_ACTION_TURN_OFF) {
92 		atomic_clear_bit(&pm->flags, PM_DEVICE_FLAG_PD_CLAIMED);
93 		atomic_clear_bit(&pm->flags, PM_DEVICE_FLAG_TURN_ON_FAILED);
94 	}
95 
96 	return 0;
97 }
98 
power_domain_add_or_remove(const struct device * dev,const struct device * domain,bool add)99 static int power_domain_add_or_remove(const struct device *dev,
100 				      const struct device *domain,
101 				      bool add)
102 {
103 #if defined(CONFIG_DEVICE_DEPS_DYNAMIC)
104 	device_handle_t *rv = domain->deps;
105 	device_handle_t dev_handle = -1;
106 	size_t i = 0, region = 0;
107 
108 	/*
109 	 * Supported devices are stored as device handle and not
110 	 * device pointers. So, it is necessary to find what is
111 	 * the handle associated to the given device.
112 	 */
113 	STRUCT_SECTION_FOREACH(device, iter_dev) {
114 		if (iter_dev == dev) {
115 			dev_handle = i + 1;
116 			break;
117 		}
118 
119 		i++;
120 	}
121 
122 	/*
123 	 * The last part is to find an available slot in the
124 	 * supported section of handles array and replace it
125 	 * with the device handle.
126 	 */
127 	while (region != 2) {
128 		if (*rv == Z_DEVICE_DEPS_SEP) {
129 			region++;
130 		}
131 		rv++;
132 	}
133 
134 	i = 0;
135 	while (rv[i] != Z_DEVICE_DEPS_ENDS) {
136 		if (add == false) {
137 			if (rv[i] == dev_handle) {
138 				dev->pm_base->domain = NULL;
139 				rv[i] = DEVICE_HANDLE_NULL;
140 				return 0;
141 			}
142 		} else {
143 			if (rv[i] == DEVICE_HANDLE_NULL) {
144 				dev->pm_base->domain = domain;
145 				rv[i] = dev_handle;
146 				return 0;
147 			}
148 		}
149 		++i;
150 	}
151 
152 	return add ? -ENOSPC : -ENOENT;
153 #else
154 	ARG_UNUSED(dev);
155 	ARG_UNUSED(domain);
156 	ARG_UNUSED(add);
157 
158 	return -ENOSYS;
159 #endif
160 }
161 
pm_device_power_domain_remove(const struct device * dev,const struct device * domain)162 int pm_device_power_domain_remove(const struct device *dev,
163 				  const struct device *domain)
164 {
165 	return power_domain_add_or_remove(dev, domain, false);
166 }
167 
pm_device_power_domain_add(const struct device * dev,const struct device * domain)168 int pm_device_power_domain_add(const struct device *dev,
169 			       const struct device *domain)
170 {
171 	return power_domain_add_or_remove(dev, domain, true);
172 }
173 
174 #ifdef CONFIG_DEVICE_DEPS
175 struct pm_visitor_context {
176 	pm_device_action_failed_cb_t failure_cb;
177 	enum pm_device_action action;
178 };
179 
pm_device_children_visitor(const struct device * dev,void * context)180 static int pm_device_children_visitor(const struct device *dev, void *context)
181 {
182 	struct pm_visitor_context *visitor_context = context;
183 	int rc;
184 
185 	rc = pm_device_action_run(dev, visitor_context->action);
186 	if ((visitor_context->failure_cb != NULL) && (rc < 0)) {
187 		/* Stop the iteration if the callback requests it */
188 		if (!visitor_context->failure_cb(dev, rc)) {
189 			return rc;
190 		}
191 	}
192 	return 0;
193 }
194 
pm_device_children_action_run(const struct device * dev,enum pm_device_action action,pm_device_action_failed_cb_t failure_cb)195 void pm_device_children_action_run(const struct device *dev,
196 				   enum pm_device_action action,
197 				   pm_device_action_failed_cb_t failure_cb)
198 {
199 	struct pm_visitor_context visitor_context = {
200 		.failure_cb = failure_cb,
201 		.action = action
202 	};
203 
204 	(void)device_supported_foreach(dev, pm_device_children_visitor, &visitor_context);
205 }
206 #endif
207 
pm_device_state_get(const struct device * dev,enum pm_device_state * state)208 int pm_device_state_get(const struct device *dev,
209 			enum pm_device_state *state)
210 {
211 	struct pm_device_base *pm = dev->pm_base;
212 
213 	if (pm == NULL) {
214 		return -ENOSYS;
215 	}
216 
217 	*state = pm->state;
218 
219 	return 0;
220 }
221 
pm_device_is_any_busy(void)222 bool pm_device_is_any_busy(void)
223 {
224 	const struct device *devs;
225 	size_t devc;
226 
227 	devc = z_device_get_all_static(&devs);
228 
229 	for (const struct device *dev = devs; dev < (devs + devc); dev++) {
230 		struct pm_device_base *pm = dev->pm_base;
231 
232 		if (pm == NULL) {
233 			continue;
234 		}
235 
236 		if (atomic_test_bit(&pm->flags, PM_DEVICE_FLAG_BUSY)) {
237 			return true;
238 		}
239 	}
240 
241 	return false;
242 }
243 
pm_device_is_busy(const struct device * dev)244 bool pm_device_is_busy(const struct device *dev)
245 {
246 	struct pm_device_base *pm = dev->pm_base;
247 
248 	if (pm == NULL) {
249 		return false;
250 	}
251 
252 	return atomic_test_bit(&pm->flags, PM_DEVICE_FLAG_BUSY);
253 }
254 
pm_device_busy_set(const struct device * dev)255 void pm_device_busy_set(const struct device *dev)
256 {
257 	struct pm_device_base *pm = dev->pm_base;
258 
259 	if (pm == NULL) {
260 		return;
261 	}
262 
263 	atomic_set_bit(&pm->flags, PM_DEVICE_FLAG_BUSY);
264 }
265 
pm_device_busy_clear(const struct device * dev)266 void pm_device_busy_clear(const struct device *dev)
267 {
268 	struct pm_device_base *pm = dev->pm_base;
269 
270 	if (pm == NULL) {
271 		return;
272 	}
273 
274 	atomic_clear_bit(&pm->flags, PM_DEVICE_FLAG_BUSY);
275 }
276 
pm_device_wakeup_enable(const struct device * dev,bool enable)277 bool pm_device_wakeup_enable(const struct device *dev, bool enable)
278 {
279 	atomic_val_t flags, new_flags;
280 	struct pm_device_base *pm = dev->pm_base;
281 
282 	if (pm == NULL) {
283 		return false;
284 	}
285 
286 	flags = atomic_get(&pm->flags);
287 
288 	if ((flags & BIT(PM_DEVICE_FLAG_WS_CAPABLE)) == 0U) {
289 		return false;
290 	}
291 
292 	if (enable) {
293 		new_flags = flags |
294 			    BIT(PM_DEVICE_FLAG_WS_ENABLED);
295 	} else {
296 		new_flags = flags & ~BIT(PM_DEVICE_FLAG_WS_ENABLED);
297 	}
298 
299 	return atomic_cas(&pm->flags, flags, new_flags);
300 }
301 
pm_device_wakeup_is_enabled(const struct device * dev)302 bool pm_device_wakeup_is_enabled(const struct device *dev)
303 {
304 	struct pm_device_base *pm = dev->pm_base;
305 
306 	if (pm == NULL) {
307 		return false;
308 	}
309 
310 	return atomic_test_bit(&pm->flags,
311 			       PM_DEVICE_FLAG_WS_ENABLED);
312 }
313 
pm_device_wakeup_is_capable(const struct device * dev)314 bool pm_device_wakeup_is_capable(const struct device *dev)
315 {
316 	struct pm_device_base *pm = dev->pm_base;
317 
318 	if (pm == NULL) {
319 		return false;
320 	}
321 
322 	return atomic_test_bit(&pm->flags,
323 			       PM_DEVICE_FLAG_WS_CAPABLE);
324 }
325 
pm_device_on_power_domain(const struct device * dev)326 bool pm_device_on_power_domain(const struct device *dev)
327 {
328 #ifdef CONFIG_PM_DEVICE_POWER_DOMAIN
329 	struct pm_device_base *pm = dev->pm_base;
330 
331 	if (pm == NULL) {
332 		return false;
333 	}
334 	return pm->domain != NULL;
335 #else
336 	ARG_UNUSED(dev);
337 	return false;
338 #endif
339 }
340 
pm_device_is_powered(const struct device * dev)341 bool pm_device_is_powered(const struct device *dev)
342 {
343 #ifdef CONFIG_PM_DEVICE_POWER_DOMAIN
344 	struct pm_device_base *pm = dev->pm_base;
345 
346 	/* If a device doesn't support PM or is not under a PM domain,
347 	 * assume it is always powered on.
348 	 */
349 	return (pm == NULL) ||
350 	       (pm->domain == NULL) ||
351 	       (pm->domain->pm_base->state == PM_DEVICE_STATE_ACTIVE);
352 #else
353 	ARG_UNUSED(dev);
354 	return true;
355 #endif
356 }
357 
pm_device_driver_init(const struct device * dev,pm_device_action_cb_t action_cb)358 int pm_device_driver_init(const struct device *dev,
359 			  pm_device_action_cb_t action_cb)
360 {
361 	struct pm_device_base *pm = dev->pm_base;
362 	int rc;
363 
364 	/* Work only needs to be performed if the device is powered */
365 	if (!pm_device_is_powered(dev)) {
366 		/* Start in off mode */
367 		pm_device_init_off(dev);
368 		return 0;
369 	}
370 
371 	/* Run power-up logic */
372 	rc = action_cb(dev, PM_DEVICE_ACTION_TURN_ON);
373 	if (rc != 0) {
374 		return rc;
375 	}
376 
377 	/* If device has no PM structure */
378 	if (pm == NULL) {
379 		/* Device should always be active */
380 		return action_cb(dev, PM_DEVICE_ACTION_RESUME);
381 	}
382 
383 	/* If device will have PM device runtime enabled */
384 	if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) &&
385 	    atomic_test_bit(&pm->flags, PM_DEVICE_FLAG_RUNTIME_AUTO)) {
386 		/* Init into suspend mode.
387 		 * This saves a SUSPENDED->ACTIVE->SUSPENDED cycle.
388 		 */
389 		pm_device_init_suspended(dev);
390 		return 0;
391 	}
392 
393 	/* Startup into active mode */
394 	return action_cb(dev, PM_DEVICE_ACTION_RESUME);
395 }
396