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