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