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 * zephyr,pm-device-disabled;
138 * };
139 * };
140 * @endcode
141 */
142 uint8_t substate_id;
143
144 /**
145 * Whether or not this state triggers device power management.
146 *
147 * When this property is false the power management subsystem
148 * will suspend devices before entering this state and will
149 * properly resume them when leaving it.
150 */
151 bool pm_device_disabled;
152
153 /**
154 * Minimum residency duration in microseconds. It is the minimum
155 * time for a given idle state to be worthwhile energywise.
156 *
157 * @note 0 means that this property is not available for this state.
158 */
159 uint32_t min_residency_us;
160
161 /**
162 * Worst case latency in microseconds required to exit the idle state.
163 *
164 * @note 0 means that this property is not available for this state.
165 */
166 uint32_t exit_latency_us;
167 };
168
169 /**
170 * Power state information needed to lock a power state.
171 */
172 struct pm_state_constraint {
173 /**
174 * Power management state
175 *
176 * @see pm_state
177 **/
178 enum pm_state state;
179 /**
180 * Power management sub-state
181 *
182 * @see pm_state
183 **/
184 uint8_t substate_id;
185 };
186
187 /** @cond INTERNAL_HIDDEN */
188
189 /**
190 * @brief Helper macro that expands to 1 if a phandle node is enabled, 0 otherwise.
191 *
192 * @param node_id Node identifier.
193 * @param prop Property holding phandle-array.
194 * @param idx Index within the array.
195 */
196 #define Z_DT_PHANDLE_01(node_id, prop, idx) \
197 COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, prop, idx), okay), \
198 (1), (0))
199
200 /**
201 * @brief Helper macro to initialize an entry of a struct pm_state_info array
202 * when using UTIL_LISTIFY in PM_STATE_INFO_LIST_FROM_DT_CPU.
203 *
204 * @note Only enabled states are initialized.
205 *
206 * @param i UTIL_LISTIFY entry index.
207 * @param node_id A node identifier with compatible zephyr,power-state
208 */
209 #define Z_PM_STATE_INFO_FROM_DT_CPU(i, node_id) \
210 COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i), okay), \
211 (PM_STATE_INFO_DT_INIT(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i)),), ())
212
213 /**
214 * @brief Helper macro to initialize an entry of a struct pm_state array when
215 * using UTIL_LISTIFY in PM_STATE_LIST_FROM_DT_CPU.
216 *
217 * @note Only enabled states are initialized.
218 *
219 * @param i UTIL_LISTIFY entry index.
220 * @param node_id A node identifier with compatible zephyr,power-state
221 */
222 #define Z_PM_STATE_FROM_DT_CPU(i, node_id) \
223 COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i), okay), \
224 (PM_STATE_DT_INIT(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i)),), ())
225
226 /** @endcond */
227
228 /**
229 * @brief Initializer for struct pm_state_info given a DT node identifier with
230 * zephyr,power-state compatible.
231 *
232 * @param node_id A node identifier with compatible zephyr,power-state
233 */
234 #define PM_STATE_INFO_DT_INIT(node_id) \
235 { \
236 .state = PM_STATE_DT_INIT(node_id), \
237 .substate_id = DT_PROP_OR(node_id, substate_id, 0), \
238 .min_residency_us = DT_PROP_OR(node_id, min_residency_us, 0), \
239 .exit_latency_us = DT_PROP_OR(node_id, exit_latency_us, 0), \
240 .pm_device_disabled = DT_PROP(node_id, zephyr_pm_device_disabled), \
241 }
242
243 /**
244 * @brief Initializer for enum pm_state given a DT node identifier with
245 * zephyr,power-state compatible.
246 *
247 * @param node_id A node identifier with compatible zephyr,power-state
248 */
249 #define PM_STATE_DT_INIT(node_id) \
250 DT_ENUM_IDX(node_id, power_state_name)
251
252 /**
253 * @brief Obtain number of CPU power states supported and enabled by the given
254 * CPU node identifier.
255 *
256 * @param node_id A CPU node identifier.
257 * @return Number of supported and enabled CPU power states.
258 */
259 #define DT_NUM_CPU_POWER_STATES(node_id) \
260 COND_CODE_1(DT_NODE_HAS_PROP(node_id, cpu_power_states), \
261 (DT_FOREACH_PROP_ELEM_SEP(node_id, cpu_power_states, Z_DT_PHANDLE_01, (+))), \
262 (0))
263
264 /**
265 * @brief Initialize an array of struct pm_state_info with information from all
266 * the states present and enabled in the given CPU node identifier.
267 *
268 * Example devicetree fragment:
269 *
270 * @code{.dts}
271 * cpus {
272 * ...
273 * cpu0: cpu@0 {
274 * device_type = "cpu";
275 * ...
276 * cpu-power-states = <&state0 &state1>;
277 * };
278 *
279 * power-states {
280 * state0: state0 {
281 * compatible = "zephyr,power-state";
282 * power-state-name = "suspend-to-idle";
283 * min-residency-us = <10000>;
284 * exit-latency-us = <100>;
285 * };
286 *
287 * state1: state1 {
288 * compatible = "zephyr,power-state";
289 * power-state-name = "suspend-to-ram";
290 * min-residency-us = <50000>;
291 * exit-latency-us = <500>;
292 * zephyr,pm-device-disabled;
293 * };
294 * };
295 * };
296
297 * @endcode
298 *
299 * Example usage:
300 *
301 * @code{.c}
302 * const struct pm_state_info states[] =
303 * PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
304 * @endcode
305 *
306 * @param node_id A CPU node identifier.
307 */
308 #define PM_STATE_INFO_LIST_FROM_DT_CPU(node_id) \
309 { \
310 LISTIFY(DT_PROP_LEN_OR(node_id, cpu_power_states, 0), \
311 Z_PM_STATE_INFO_FROM_DT_CPU, (), node_id) \
312 }
313
314 /**
315 * @brief Initialize an array of struct pm_state with information from all the
316 * states present and enabled in the given CPU node identifier.
317 *
318 * Example devicetree fragment:
319 *
320 * @code{.dts}
321 * cpus {
322 * ...
323 * cpu0: cpu@0 {
324 * device_type = "cpu";
325 * ...
326 * cpu-power-states = <&state0 &state1>;
327 * };
328 *
329 * power-states {
330 * state0: state0 {
331 * compatible = "zephyr,power-state";
332 * power-state-name = "suspend-to-idle";
333 * min-residency-us = <10000>;
334 * exit-latency-us = <100>;
335 * };
336 *
337 * state1: state1 {
338 * compatible = "zephyr,power-state";
339 * power-state-name = "suspend-to-ram";
340 * min-residency-us = <50000>;
341 * exit-latency-us = <500>;
342 * };
343 * };
344 * };
345 * @endcode
346 *
347 * Example usage:
348 *
349 * @code{.c}
350 * const enum pm_state states[] = PM_STATE_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
351 * @endcode
352 *
353 * @param node_id A CPU node identifier.
354 */
355 #define PM_STATE_LIST_FROM_DT_CPU(node_id) \
356 { \
357 LISTIFY(DT_PROP_LEN_OR(node_id, cpu_power_states, 0), \
358 Z_PM_STATE_FROM_DT_CPU, (), node_id) \
359 }
360
361
362 #if defined(CONFIG_PM) || defined(__DOXYGEN__)
363 /**
364 * Obtain information about all supported states by a CPU.
365 *
366 * @param cpu CPU index.
367 * @param states Where to store the list of supported states.
368 *
369 * @return Number of supported states.
370 */
371 uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states);
372
373 /**
374 * @}
375 */
376
377 #else /* CONFIG_PM */
378
pm_state_cpu_get_all(uint8_t cpu,const struct pm_state_info ** states)379 static inline uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states)
380 {
381 ARG_UNUSED(cpu);
382 ARG_UNUSED(states);
383
384 return 0;
385 }
386
387 #endif /* CONFIG_PM */
388
389 #ifdef __cplusplus
390 }
391 #endif
392
393 #endif
394