1 /*
2 * Copyright (c) 2018 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr.h>
8 #include <kernel.h>
9 #include <string.h>
10 #include <device.h>
11 #include <pm/policy.h>
12
13 #define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */
14 #include <logging/log.h>
15 LOG_MODULE_DECLARE(power);
16
17 #if defined(CONFIG_PM_DEVICE)
18 extern const struct device *__pm_device_slots_start[];
19
20 /* Number of devices successfully suspended. */
21 static size_t num_susp;
22
_pm_devices(enum pm_device_state state)23 static int _pm_devices(enum pm_device_state state)
24 {
25 const struct device *devs;
26 size_t devc;
27
28 devc = z_device_get_all_static(&devs);
29
30 num_susp = 0;
31
32 for (const struct device *dev = devs + devc - 1; dev >= devs; dev--) {
33 int ret;
34
35 /* ignore busy devices */
36 if (pm_device_is_busy(dev) || pm_device_wakeup_is_enabled(dev)) {
37 continue;
38 }
39
40 ret = pm_device_state_set(dev, state);
41 /* ignore devices not supporting or already at the given state */
42 if ((ret == -ENOSYS) || (ret == -ENOTSUP) || (ret == -EALREADY)) {
43 continue;
44 } else if (ret < 0) {
45 LOG_ERR("Device %s did not enter %s state (%d)",
46 dev->name, pm_device_state_str(state), ret);
47 return ret;
48 }
49
50 __pm_device_slots_start[num_susp] = dev;
51 num_susp++;
52 }
53
54 return 0;
55 }
56
pm_suspend_devices(void)57 int pm_suspend_devices(void)
58 {
59 return _pm_devices(PM_DEVICE_STATE_SUSPENDED);
60 }
61
pm_low_power_devices(void)62 int pm_low_power_devices(void)
63 {
64 return _pm_devices(PM_DEVICE_STATE_LOW_POWER);
65 }
66
pm_resume_devices(void)67 void pm_resume_devices(void)
68 {
69 int32_t i;
70
71 for (i = (num_susp - 1); i >= 0; i--) {
72 pm_device_state_set(__pm_device_slots_start[i],
73 PM_DEVICE_STATE_ACTIVE);
74 }
75
76 num_susp = 0;
77 }
78 #endif /* defined(CONFIG_PM_DEVICE) */
79
pm_device_state_str(enum pm_device_state state)80 const char *pm_device_state_str(enum pm_device_state state)
81 {
82 switch (state) {
83 case PM_DEVICE_STATE_ACTIVE:
84 return "active";
85 case PM_DEVICE_STATE_LOW_POWER:
86 return "low power";
87 case PM_DEVICE_STATE_SUSPENDED:
88 return "suspended";
89 case PM_DEVICE_STATE_OFF:
90 return "off";
91 default:
92 return "";
93 }
94 }
95
pm_device_state_set(const struct device * dev,enum pm_device_state state)96 int pm_device_state_set(const struct device *dev,
97 enum pm_device_state state)
98 {
99 int ret;
100 enum pm_device_action action;
101
102 if (dev->pm_control == NULL) {
103 return -ENOSYS;
104 }
105
106 if (atomic_test_bit(&dev->pm->flags, PM_DEVICE_FLAG_TRANSITIONING)) {
107 return -EBUSY;
108 }
109
110 switch (state) {
111 case PM_DEVICE_STATE_SUSPENDED:
112 if (dev->pm->state == PM_DEVICE_STATE_SUSPENDED) {
113 return -EALREADY;
114 } else if (dev->pm->state == PM_DEVICE_STATE_OFF) {
115 return -ENOTSUP;
116 }
117
118 action = PM_DEVICE_ACTION_SUSPEND;
119 break;
120 case PM_DEVICE_STATE_ACTIVE:
121 if (dev->pm->state == PM_DEVICE_STATE_ACTIVE) {
122 return -EALREADY;
123 }
124
125 action = PM_DEVICE_ACTION_RESUME;
126 break;
127 case PM_DEVICE_STATE_LOW_POWER:
128 if (dev->pm->state == state) {
129 return -EALREADY;
130 }
131
132 action = PM_DEVICE_ACTION_LOW_POWER;
133 break;
134 case PM_DEVICE_STATE_OFF:
135 if (dev->pm->state == state) {
136 return -EALREADY;
137 }
138
139 action = PM_DEVICE_ACTION_TURN_OFF;
140 break;
141 default:
142 return -ENOTSUP;
143 }
144
145 ret = dev->pm_control(dev, action);
146 if (ret < 0) {
147 return ret;
148 }
149
150 dev->pm->state = state;
151
152 return 0;
153 }
154
pm_device_state_get(const struct device * dev,enum pm_device_state * state)155 int pm_device_state_get(const struct device *dev,
156 enum pm_device_state *state)
157 {
158 if (dev->pm_control == NULL) {
159 return -ENOSYS;
160 }
161
162 *state = dev->pm->state;
163
164 return 0;
165 }
166
pm_device_is_any_busy(void)167 bool pm_device_is_any_busy(void)
168 {
169 const struct device *devs;
170 size_t devc;
171
172 devc = z_device_get_all_static(&devs);
173
174 for (const struct device *dev = devs; dev < (devs + devc); dev++) {
175 if (atomic_test_bit(&dev->pm->flags, PM_DEVICE_FLAG_BUSY)) {
176 return true;
177 }
178 }
179
180 return false;
181 }
182
pm_device_is_busy(const struct device * dev)183 bool pm_device_is_busy(const struct device *dev)
184 {
185 return atomic_test_bit(&dev->pm->flags, PM_DEVICE_FLAG_BUSY);
186 }
187
pm_device_busy_set(const struct device * dev)188 void pm_device_busy_set(const struct device *dev)
189 {
190 atomic_set_bit(&dev->pm->flags, PM_DEVICE_FLAG_BUSY);
191 }
192
pm_device_busy_clear(const struct device * dev)193 void pm_device_busy_clear(const struct device *dev)
194 {
195 atomic_clear_bit(&dev->pm->flags, PM_DEVICE_FLAG_BUSY);
196 }
197
pm_device_wakeup_enable(struct device * dev,bool enable)198 bool pm_device_wakeup_enable(struct device *dev, bool enable)
199 {
200 atomic_val_t flags, new_flags;
201
202 flags = atomic_get(&dev->pm->flags);
203
204 if ((flags & BIT(PM_DEVICE_FLAGS_WS_CAPABLE)) == 0U) {
205 return false;
206 }
207
208 if (enable) {
209 new_flags = flags |
210 BIT(PM_DEVICE_FLAGS_WS_ENABLED);
211 } else {
212 new_flags = flags & ~BIT(PM_DEVICE_FLAGS_WS_ENABLED);
213 }
214
215 return atomic_cas(&dev->pm->flags, flags, new_flags);
216 }
217
pm_device_wakeup_is_enabled(const struct device * dev)218 bool pm_device_wakeup_is_enabled(const struct device *dev)
219 {
220 return atomic_test_bit(&dev->pm->flags,
221 PM_DEVICE_FLAGS_WS_ENABLED);
222 }
223
pm_device_wakeup_is_capable(const struct device * dev)224 bool pm_device_wakeup_is_capable(const struct device *dev)
225 {
226 return atomic_test_bit(&dev->pm->flags,
227 PM_DEVICE_FLAGS_WS_CAPABLE);
228 }
229