1 /*
2  * Copyright (c) 2024 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/pm/device.h>
9 #include <zephyr/pm/device_runtime.h>
10 
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_DECLARE(pm_device, CONFIG_PM_DEVICE_LOG_LEVEL);
13 
14 #define DT_PM_DEVICE_ENABLED(node_id)					\
15 	COND_CODE_1(DT_PROP(node_id, zephyr_pm_device_disabled),	\
16 		(), (1 +))
17 
18 #define DT_PM_DEVICE_NEEDED			\
19 	(DT_FOREACH_STATUS_OKAY(zephyr_power_state, DT_PM_DEVICE_ENABLED) 0)
20 
21 #if DT_PM_DEVICE_NEEDED
22 TYPE_SECTION_START_EXTERN(const struct device *, pm_device_slots);
23 
24 /* Number of devices successfully suspended. */
25 static size_t num_susp;
26 
pm_suspend_devices(void)27 bool pm_suspend_devices(void)
28 {
29 	const struct device *devs;
30 	size_t devc;
31 
32 	devc = z_device_get_all_static(&devs);
33 
34 	num_susp = 0;
35 
36 	for (const struct device *dev = devs + devc - 1; dev >= devs; dev--) {
37 		int ret;
38 
39 		/*
40 		 * Ignore uninitialized devices, busy devices, wake up sources, and
41 		 * devices with runtime PM enabled.
42 		 */
43 		if (!device_is_ready(dev) || pm_device_is_busy(dev) ||
44 		    pm_device_wakeup_is_enabled(dev) ||
45 		    pm_device_runtime_is_enabled(dev)) {
46 			continue;
47 		}
48 
49 		ret = pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);
50 		/* ignore devices not supporting or already at the given state */
51 		if ((ret == -ENOSYS) || (ret == -ENOTSUP) || (ret == -EALREADY)) {
52 			continue;
53 		} else if (ret < 0) {
54 			LOG_ERR("Device %s did not enter %s state (%d)",
55 				dev->name,
56 				pm_device_state_str(PM_DEVICE_STATE_SUSPENDED),
57 				ret);
58 			return false;
59 		}
60 
61 		TYPE_SECTION_START(pm_device_slots)[num_susp] = dev;
62 		num_susp++;
63 	}
64 
65 	return true;
66 }
67 
pm_resume_devices(void)68 void pm_resume_devices(void)
69 {
70 	for (int i = (num_susp - 1); i >= 0; i--) {
71 		pm_device_action_run(TYPE_SECTION_START(pm_device_slots)[i],
72 				    PM_DEVICE_ACTION_RESUME);
73 	}
74 
75 	num_susp = 0;
76 }
77 
78 #else /* !DT_PM_DEVICE_NEEDED */
79 
pm_resume_devices(void)80 void pm_resume_devices(void)
81 {
82 }
83 
pm_suspend_devices(void)84 bool pm_suspend_devices(void)
85 {
86 	return true;
87 }
88 
89 #endif
90