1 /*
2 * Copyright (c) 2020 Intel corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #ifndef ZEPHYR_INCLUDE_PM_STATE_H_
8 #define ZEPHYR_INCLUDE_PM_STATE_H_
9
10 #include <zephyr/sys/util.h>
11 #include <zephyr/devicetree.h>
12
13 #ifdef __cplusplus
14 extern "C" {
15 #endif
16
17 /**
18 * @brief System Power Management States
19 * @defgroup subsys_pm_states States
20 * @ingroup subsys_pm
21 * @{
22 */
23
24 /**
25 * @enum pm_state Power management state
26 */
27 enum pm_state {
28 /**
29 * @brief Runtime active state
30 *
31 * The system is fully powered and active.
32 *
33 * @note This state is correlated with ACPI G0/S0 state
34 */
35 PM_STATE_ACTIVE,
36 /**
37 * @brief Runtime idle state
38 *
39 * Runtime idle is a system sleep state in which all of the cores
40 * enter deepest possible idle state and wait for interrupts, no
41 * requirements for the devices, leaving them at the states where
42 * they are.
43 *
44 * @note This state is correlated with ACPI S0ix state
45 */
46 PM_STATE_RUNTIME_IDLE,
47 /**
48 * @brief Suspend to idle state
49 *
50 * The system goes through a normal platform suspend where it puts
51 * all of the cores in deepest possible idle state and *may* puts
52 * peripherals into low-power states. No operating state is lost (ie.
53 * the cpu core does not lose execution context), so the system can go
54 * back to where it left off easily enough.
55 *
56 * @note This state is correlated with ACPI S1 state
57 */
58 PM_STATE_SUSPEND_TO_IDLE,
59 /**
60 * @brief Standby state
61 *
62 * In addition to putting peripherals into low-power states all
63 * non-boot CPUs are powered off. It should allow more energy to be
64 * saved relative to suspend to idle, but the resume latency will
65 * generally be greater than for that state. But it should be the same
66 * state with suspend to idle state on uniprocessor system.
67 *
68 * @note This state is correlated with ACPI S2 state
69 */
70 PM_STATE_STANDBY,
71 /**
72 * @brief Suspend to ram state
73 *
74 * This state offers significant energy savings by powering off as much
75 * of the system as possible, where memory should be placed into the
76 * self-refresh mode to retain its contents. The state of devices and
77 * CPUs is saved and held in memory, and it may require some boot-
78 * strapping code in ROM to resume the system from it.
79 *
80 * @note This state is correlated with ACPI S3 state
81 */
82 PM_STATE_SUSPEND_TO_RAM,
83 /**
84 * @brief Suspend to disk state
85 *
86 * This state offers significant energy savings by powering off as much
87 * of the system as possible, including the memory. The contents of
88 * memory are written to disk or other non-volatile storage, and on
89 * resume it's read back into memory with the help of boot-strapping
90 * code, restores the system to the same point of execution where it
91 * went to suspend to disk.
92 *
93 * @note This state is correlated with ACPI S4 state
94 */
95 PM_STATE_SUSPEND_TO_DISK,
96 /**
97 * @brief Soft off state
98 *
99 * This state consumes a minimal amount of power and requires a large
100 * latency in order to return to runtime active state. The contents of
101 * system(CPU and memory) will not be preserved, so the system will be
102 * restarted as if from initial power-up and kernel boot.
103 *
104 * @note This state is correlated with ACPI G2/S5 state
105 */
106 PM_STATE_SOFT_OFF,
107 /** Number of power management states (internal use) */
108 PM_STATE_COUNT,
109 };
110
111 /**
112 * Information about a power management state
113 */
114 struct pm_state_info {
115 enum pm_state state;
116
117 /**
118 * Some platforms have multiple states that map to
119 * one Zephyr power state. This property allows the platform
120 * distinguish them. e.g:
121 *
122 * @code{.dts}
123 * power-states {
124 * state0: state0 {
125 * compatible = "zephyr,power-state";
126 * power-state-name = "suspend-to-idle";
127 * substate-id = <1>;
128 * min-residency-us = <10000>;
129 * exit-latency-us = <100>;
130 * };
131 * state1: state1 {
132 * compatible = "zephyr,power-state";
133 * power-state-name = "suspend-to-idle";
134 * substate-id = <2>;
135 * min-residency-us = <20000>;
136 * exit-latency-us = <200>;
137 * };
138 * };
139 * @endcode
140 */
141 uint8_t substate_id;
142
143 /**
144 * Minimum residency duration in microseconds. It is the minimum
145 * time for a given idle state to be worthwhile energywise.
146 *
147 * @note 0 means that this property is not available for this state.
148 */
149 uint32_t min_residency_us;
150
151 /**
152 * Worst case latency in microseconds required to exit the idle state.
153 *
154 * @note 0 means that this property is not available for this state.
155 */
156 uint32_t exit_latency_us;
157 };
158
159 /** @cond INTERNAL_HIDDEN */
160
161 /**
162 * @brief Helper macro that expands to 1 if a phandle node is enabled, 0 otherwise.
163 *
164 * @param node_id Node identifier.
165 * @param prop Property holding phandle-array.
166 * @param idx Index within the array.
167 */
168 #define Z_DT_PHANDLE_01(node_id, prop, idx) \
169 COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, prop, idx), okay), \
170 (1), (0))
171
172 /**
173 * @brief Helper macro to initialize an entry of a struct pm_state_info array
174 * when using UTIL_LISTIFY in PM_STATE_INFO_LIST_FROM_DT_CPU.
175 *
176 * @note Only enabled states are initialized.
177 *
178 * @param i UTIL_LISTIFY entry index.
179 * @param node_id A node identifier with compatible zephyr,power-state
180 */
181 #define Z_PM_STATE_INFO_FROM_DT_CPU(i, node_id) \
182 COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i), okay), \
183 (PM_STATE_INFO_DT_INIT(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i)),), ())
184
185 /**
186 * @brief Helper macro to initialize an entry of a struct pm_state array when
187 * using UTIL_LISTIFY in PM_STATE_LIST_FROM_DT_CPU.
188 *
189 * @note Only enabled states are initialized.
190 *
191 * @param i UTIL_LISTIFY entry index.
192 * @param node_id A node identifier with compatible zephyr,power-state
193 */
194 #define Z_PM_STATE_FROM_DT_CPU(i, node_id) \
195 COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i), okay), \
196 (PM_STATE_DT_INIT(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i)),), ())
197
198 /** @endcond */
199
200 /**
201 * @brief Initializer for struct pm_state_info given a DT node identifier with
202 * zephyr,power-state compatible.
203 *
204 * @param node_id A node identifier with compatible zephyr,power-state
205 */
206 #define PM_STATE_INFO_DT_INIT(node_id) \
207 { \
208 .state = PM_STATE_DT_INIT(node_id), \
209 .substate_id = DT_PROP_OR(node_id, substate_id, 0), \
210 .min_residency_us = DT_PROP_OR(node_id, min_residency_us, 0), \
211 .exit_latency_us = DT_PROP_OR(node_id, exit_latency_us, 0), \
212 }
213
214 /**
215 * @brief Initializer for enum pm_state given a DT node identifier with
216 * zephyr,power-state compatible.
217 *
218 * @param node_id A node identifier with compatible zephyr,power-state
219 */
220 #define PM_STATE_DT_INIT(node_id) \
221 DT_ENUM_IDX(node_id, power_state_name)
222
223 /**
224 * @brief Obtain number of CPU power states supported and enabled by the given
225 * CPU node identifier.
226 *
227 * @param node_id A CPU node identifier.
228 * @return Number of supported and enabled CPU power states.
229 */
230 #define DT_NUM_CPU_POWER_STATES(node_id) \
231 COND_CODE_1(DT_NODE_HAS_PROP(node_id, cpu_power_states), \
232 (DT_FOREACH_PROP_ELEM_SEP(node_id, cpu_power_states, Z_DT_PHANDLE_01, (+))), \
233 (0))
234
235 /**
236 * @brief Initialize an array of struct pm_state_info with information from all
237 * the states present and enabled in the given CPU node identifier.
238 *
239 * Example devicetree fragment:
240 *
241 * @code{.dts}
242 * cpus {
243 * ...
244 * cpu0: cpu@0 {
245 * device_type = "cpu";
246 * ...
247 * cpu-power-states = <&state0 &state1>;
248 * };
249 *
250 * power-states {
251 * state0: state0 {
252 * compatible = "zephyr,power-state";
253 * power-state-name = "suspend-to-idle";
254 * min-residency-us = <10000>;
255 * exit-latency-us = <100>;
256 * };
257 *
258 * state1: state1 {
259 * compatible = "zephyr,power-state";
260 * power-state-name = "suspend-to-ram";
261 * min-residency-us = <50000>;
262 * exit-latency-us = <500>;
263 * };
264 * };
265 * };
266
267 * @endcode
268 *
269 * Example usage:
270 *
271 * @code{.c}
272 * const struct pm_state_info states[] =
273 * PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
274 * @endcode
275 *
276 * @param node_id A CPU node identifier.
277 */
278 #define PM_STATE_INFO_LIST_FROM_DT_CPU(node_id) \
279 { \
280 LISTIFY(DT_PROP_LEN_OR(node_id, cpu_power_states, 0), \
281 Z_PM_STATE_INFO_FROM_DT_CPU, (), node_id) \
282 }
283
284 /**
285 * @brief Initialize an array of struct pm_state with information from all the
286 * states present and enabled in the given CPU node identifier.
287 *
288 * Example devicetree fragment:
289 *
290 * @code{.dts}
291 * cpus {
292 * ...
293 * cpu0: cpu@0 {
294 * device_type = "cpu";
295 * ...
296 * cpu-power-states = <&state0 &state1>;
297 * };
298 *
299 * power-states {
300 * state0: state0 {
301 * compatible = "zephyr,power-state";
302 * power-state-name = "suspend-to-idle";
303 * min-residency-us = <10000>;
304 * exit-latency-us = <100>;
305 * };
306 *
307 * state1: state1 {
308 * compatible = "zephyr,power-state";
309 * power-state-name = "suspend-to-ram";
310 * min-residency-us = <50000>;
311 * exit-latency-us = <500>;
312 * };
313 * };
314 * };
315 * @endcode
316 *
317 * Example usage:
318 *
319 * @code{.c}
320 * const enum pm_state states[] = PM_STATE_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
321 * @endcode
322 *
323 * @param node_id A CPU node identifier.
324 */
325 #define PM_STATE_LIST_FROM_DT_CPU(node_id) \
326 { \
327 LISTIFY(DT_PROP_LEN_OR(node_id, cpu_power_states, 0), \
328 Z_PM_STATE_FROM_DT_CPU, (), node_id) \
329 }
330
331
332 #if defined(CONFIG_PM) || defined(__DOXYGEN__)
333 /**
334 * Obtain information about all supported states by a CPU.
335 *
336 * @param cpu CPU index.
337 * @param states Where to store the list of supported states.
338 *
339 * @return Number of supported states.
340 */
341 uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states);
342
343 /**
344 * @}
345 */
346
347 #else /* CONFIG_PM */
348
pm_state_cpu_get_all(uint8_t cpu,const struct pm_state_info ** states)349 static inline uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states)
350 {
351 ARG_UNUSED(cpu);
352 ARG_UNUSED(states);
353
354 return 0;
355 }
356
357 #endif /* CONFIG_PM */
358
359 #ifdef __cplusplus
360 }
361 #endif
362
363 #endif
364