Line data Source code
1 0 : /*
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 : #include <errno.h>
13 :
14 : #ifdef __cplusplus
15 : extern "C" {
16 : #endif
17 :
18 : /**
19 : * @brief System Power Management States
20 : * @defgroup subsys_pm_states States
21 : * @ingroup subsys_pm
22 : * @{
23 : */
24 :
25 : /**
26 : * @enum pm_state Power management state
27 : */
28 1 : enum pm_state {
29 : /**
30 : * @brief Runtime active state
31 : *
32 : * The system is fully powered and active.
33 : *
34 : * @note This state is correlated with ACPI G0/S0 state
35 : */
36 : PM_STATE_ACTIVE,
37 : /**
38 : * @brief Runtime idle state
39 : *
40 : * Runtime idle is a system sleep state in which all of the cores
41 : * enter deepest possible idle state and wait for interrupts, no
42 : * requirements for the devices, leaving them at the states where
43 : * they are.
44 : *
45 : * @note This state is correlated with ACPI S0ix state
46 : */
47 : PM_STATE_RUNTIME_IDLE,
48 : /**
49 : * @brief Suspend to idle state
50 : *
51 : * The system goes through a normal platform suspend where it puts
52 : * all of the cores in deepest possible idle state and *may* puts
53 : * peripherals into low-power states. No operating state is lost (ie.
54 : * the cpu core does not lose execution context), so the system can go
55 : * back to where it left off easily enough.
56 : *
57 : * @note This state is correlated with ACPI S1 state
58 : */
59 : PM_STATE_SUSPEND_TO_IDLE,
60 : /**
61 : * @brief Standby state
62 : *
63 : * In addition to putting peripherals into low-power states all
64 : * non-boot CPUs are powered off. It should allow more energy to be
65 : * saved relative to suspend to idle, but the resume latency will
66 : * generally be greater than for that state. But it should be the same
67 : * state with suspend to idle state on uniprocessor system.
68 : *
69 : * @note This state is correlated with ACPI S2 state
70 : */
71 : PM_STATE_STANDBY,
72 : /**
73 : * @brief Suspend to ram state
74 : *
75 : * This state offers significant energy savings by powering off as much
76 : * of the system as possible, where memory should be placed into the
77 : * self-refresh mode to retain its contents. The state of devices and
78 : * CPUs is saved and held in memory, and it may require some boot-
79 : * strapping code in ROM to resume the system from it.
80 : *
81 : * @note This state is correlated with ACPI S3 state
82 : */
83 : PM_STATE_SUSPEND_TO_RAM,
84 : /**
85 : * @brief Suspend to disk state
86 : *
87 : * This state offers significant energy savings by powering off as much
88 : * of the system as possible, including the memory. The contents of
89 : * memory are written to disk or other non-volatile storage, and on
90 : * resume it's read back into memory with the help of boot-strapping
91 : * code, restores the system to the same point of execution where it
92 : * went to suspend to disk.
93 : *
94 : * @note This state is correlated with ACPI S4 state
95 : */
96 : PM_STATE_SUSPEND_TO_DISK,
97 : /**
98 : * @brief Soft off state
99 : *
100 : * This state consumes a minimal amount of power and requires a large
101 : * latency in order to return to runtime active state. The contents of
102 : * system(CPU and memory) will not be preserved, so the system will be
103 : * restarted as if from initial power-up and kernel boot.
104 : *
105 : * @note This state is correlated with ACPI G2/S5 state
106 : */
107 : PM_STATE_SOFT_OFF,
108 : /** Number of power management states (internal use) */
109 : PM_STATE_COUNT,
110 : };
111 :
112 : /**
113 : * Information about a power management state
114 : */
115 1 : struct pm_state_info {
116 0 : enum pm_state state;
117 :
118 : /**
119 : * Some platforms have multiple states that map to
120 : * one Zephyr power state. This property allows the platform
121 : * distinguish them. e.g:
122 : *
123 : * @code{.dts}
124 : * power-states {
125 : * state0: state0 {
126 : * compatible = "zephyr,power-state";
127 : * power-state-name = "suspend-to-idle";
128 : * substate-id = <1>;
129 : * min-residency-us = <10000>;
130 : * exit-latency-us = <100>;
131 : * };
132 : * state1: state1 {
133 : * compatible = "zephyr,power-state";
134 : * power-state-name = "suspend-to-idle";
135 : * substate-id = <2>;
136 : * min-residency-us = <20000>;
137 : * exit-latency-us = <200>;
138 : * zephyr,pm-device-disabled;
139 : * };
140 : * };
141 : * @endcode
142 : */
143 1 : uint8_t substate_id;
144 :
145 : /**
146 : * Whether or not this state triggers device power management.
147 : *
148 : * When this property is false the power management subsystem
149 : * will suspend devices before entering this state and will
150 : * properly resume them when leaving it.
151 : */
152 1 : bool pm_device_disabled;
153 :
154 : /**
155 : * Minimum residency duration in microseconds. It is the minimum
156 : * time for a given idle state to be worthwhile energywise.
157 : *
158 : * @note 0 means that this property is not available for this state.
159 : */
160 1 : uint32_t min_residency_us;
161 :
162 : /**
163 : * Worst case latency in microseconds required to exit the idle state.
164 : *
165 : * @note 0 means that this property is not available for this state.
166 : */
167 1 : uint32_t exit_latency_us;
168 : };
169 :
170 : /**
171 : * Information needed to be able to reference a power state as a constraint
172 : * on some device or system functions.
173 : */
174 1 : struct pm_state_constraint {
175 : /**
176 : * Power management state
177 : *
178 : * @see pm_state
179 : **/
180 1 : enum pm_state state;
181 : /**
182 : * Power management sub-state
183 : *
184 : * @see pm_state
185 : **/
186 1 : uint8_t substate_id;
187 : };
188 :
189 : /**
190 : * Collection of multiple power state constraints.
191 : */
192 1 : struct pm_state_constraints {
193 : /** Array of power state constraints */
194 1 : struct pm_state_constraint *list;
195 : /** Number of constraints in the list */
196 1 : size_t count;
197 : };
198 :
199 : /** @cond INTERNAL_HIDDEN */
200 :
201 : /**
202 : * @brief Helper macro that expands to 1 if a phandle node is enabled, 0 otherwise.
203 : *
204 : * @param node_id Node identifier.
205 : * @param prop Property holding phandle-array.
206 : * @param idx Index within the array.
207 : */
208 : #define Z_DT_PHANDLE_01(node_id, prop, idx) \
209 : COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_PHANDLE_BY_IDX(node_id, prop, idx)), \
210 : (1), (0))
211 :
212 : /**
213 : * @brief Helper macro to initialize an entry of a struct pm_state_info array
214 : * when using UTIL_LISTIFY in PM_STATE_INFO_LIST_FROM_DT_CPU.
215 : *
216 : * @note Only enabled states are initialized.
217 : *
218 : * @param i UTIL_LISTIFY entry index.
219 : * @param node_id A node identifier with compatible zephyr,power-state
220 : */
221 : #define Z_PM_STATE_INFO_FROM_DT_CPU(i, node_id) \
222 : COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i)), \
223 : (PM_STATE_INFO_DT_INIT(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i)),), ())
224 :
225 : /**
226 : * @brief Helper macro to initialize an entry of a struct pm_state array when
227 : * using UTIL_LISTIFY in PM_STATE_LIST_FROM_DT_CPU.
228 : *
229 : * @note Only enabled states are initialized.
230 : *
231 : * @param i UTIL_LISTIFY entry index.
232 : * @param node_id A node identifier with compatible zephyr,power-state
233 : */
234 : #define Z_PM_STATE_FROM_DT_CPU(i, node_id) \
235 : COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i)), \
236 : (PM_STATE_DT_INIT(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i)),), ())
237 :
238 : /** @endcond */
239 :
240 : /**
241 : * @brief Initializer for struct pm_state_info given a DT node identifier with
242 : * zephyr,power-state compatible.
243 : *
244 : * @param node_id A node identifier with compatible zephyr,power-state
245 : */
246 1 : #define PM_STATE_INFO_DT_INIT(node_id) \
247 : { \
248 : .state = PM_STATE_DT_INIT(node_id), \
249 : .substate_id = DT_PROP_OR(node_id, substate_id, 0), \
250 : .min_residency_us = DT_PROP_OR(node_id, min_residency_us, 0), \
251 : .exit_latency_us = DT_PROP_OR(node_id, exit_latency_us, 0), \
252 : .pm_device_disabled = DT_PROP(node_id, zephyr_pm_device_disabled), \
253 : }
254 :
255 : /**
256 : * @brief Initializer for enum pm_state given a DT node identifier with
257 : * zephyr,power-state compatible.
258 : *
259 : * @param node_id A node identifier with compatible zephyr,power-state
260 : */
261 1 : #define PM_STATE_DT_INIT(node_id) \
262 : DT_ENUM_IDX(node_id, power_state_name)
263 :
264 : /**
265 : * @brief Obtain number of CPU power states supported and enabled by the given
266 : * CPU node identifier.
267 : *
268 : * @param node_id A CPU node identifier.
269 : * @return Number of supported and enabled CPU power states.
270 : */
271 1 : #define DT_NUM_CPU_POWER_STATES(node_id) \
272 : COND_CODE_1(DT_NODE_HAS_PROP(node_id, cpu_power_states), \
273 : (DT_FOREACH_PROP_ELEM_SEP(node_id, cpu_power_states, Z_DT_PHANDLE_01, (+))), \
274 : (0))
275 :
276 : /**
277 : * @brief Initialize an array of struct pm_state_info with information from all
278 : * the states present and enabled in the given CPU node identifier.
279 : *
280 : * Example devicetree fragment:
281 : *
282 : * @code{.dts}
283 : * cpus {
284 : * ...
285 : * cpu0: cpu@0 {
286 : * device_type = "cpu";
287 : * ...
288 : * cpu-power-states = <&state0 &state1>;
289 : * };
290 : *
291 : * power-states {
292 : * state0: state0 {
293 : * compatible = "zephyr,power-state";
294 : * power-state-name = "suspend-to-idle";
295 : * min-residency-us = <10000>;
296 : * exit-latency-us = <100>;
297 : * };
298 : *
299 : * state1: state1 {
300 : * compatible = "zephyr,power-state";
301 : * power-state-name = "suspend-to-ram";
302 : * min-residency-us = <50000>;
303 : * exit-latency-us = <500>;
304 : * zephyr,pm-device-disabled;
305 : * };
306 : * };
307 : * };
308 :
309 : * @endcode
310 : *
311 : * Example usage:
312 : *
313 : * @code{.c}
314 : * const struct pm_state_info states[] =
315 : * PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
316 : * @endcode
317 : *
318 : * @param node_id A CPU node identifier.
319 : */
320 1 : #define PM_STATE_INFO_LIST_FROM_DT_CPU(node_id) \
321 : { \
322 : LISTIFY(DT_PROP_LEN_OR(node_id, cpu_power_states, 0), \
323 : Z_PM_STATE_INFO_FROM_DT_CPU, (), node_id) \
324 : }
325 :
326 : /**
327 : * @brief Initialize an array of struct pm_state with information from all the
328 : * states present and enabled in the given CPU node identifier.
329 : *
330 : * Example devicetree fragment:
331 : *
332 : * @code{.dts}
333 : * cpus {
334 : * ...
335 : * cpu0: cpu@0 {
336 : * device_type = "cpu";
337 : * ...
338 : * cpu-power-states = <&state0 &state1>;
339 : * };
340 : *
341 : * power-states {
342 : * state0: state0 {
343 : * compatible = "zephyr,power-state";
344 : * power-state-name = "suspend-to-idle";
345 : * min-residency-us = <10000>;
346 : * exit-latency-us = <100>;
347 : * };
348 : *
349 : * state1: state1 {
350 : * compatible = "zephyr,power-state";
351 : * power-state-name = "suspend-to-ram";
352 : * min-residency-us = <50000>;
353 : * exit-latency-us = <500>;
354 : * };
355 : * };
356 : * };
357 : * @endcode
358 : *
359 : * Example usage:
360 : *
361 : * @code{.c}
362 : * const enum pm_state states[] = PM_STATE_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
363 : * @endcode
364 : *
365 : * @param node_id A CPU node identifier.
366 : */
367 1 : #define PM_STATE_LIST_FROM_DT_CPU(node_id) \
368 : { \
369 : LISTIFY(DT_PROP_LEN_OR(node_id, cpu_power_states, 0), \
370 : Z_PM_STATE_FROM_DT_CPU, (), node_id) \
371 : }
372 :
373 : /**
374 : * @brief initialize a device pm constraint with information from devicetree.
375 : *
376 : * @param node_id Node identifier.
377 : */
378 1 : #define PM_STATE_CONSTRAINT_INIT(node_id) \
379 : { \
380 : .state = PM_STATE_DT_INIT(node_id), \
381 : .substate_id = DT_PROP_OR(node_id, substate_id, 0), \
382 : }
383 :
384 : #define Z_PM_STATE_CONSTRAINT_REF(node_id, phandle, idx) \
385 : PM_STATE_CONSTRAINT_INIT(DT_PHANDLE_BY_IDX(node_id, phandle, idx))
386 :
387 : #define Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, phandles) \
388 : _CONCAT_4(node_id, _, phandles, _constraints)
389 :
390 : /**
391 : * @brief Define a list of power state constraints from devicetree.
392 : *
393 : * This macro creates an array of pm_state_constraint structures initialized
394 : * with power state information from the specified devicetree property.
395 : *
396 : * @param node_id Devicetree node identifier.
397 : * @param prop Property name containing the list of power state phandles.
398 : */
399 1 : #define PM_STATE_CONSTRAINTS_LIST_DEFINE(node_id, prop) \
400 : struct pm_state_constraint Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, prop)[] = \
401 : { \
402 : DT_FOREACH_PROP_ELEM_SEP(node_id, prop, Z_PM_STATE_CONSTRAINT_REF, (,)) \
403 : }
404 :
405 : /**
406 : * @brief Get power state constraints structure from devicetree.
407 : *
408 : * This macro creates a structure containing a pointer to the constraints list
409 : * and the count of constraints, suitable for initializing a pm_state_constraints
410 : * structure. Must be used after the PM_STATE_CONSTRAINTS_LIST_DEFINE call for the same
411 : * @param prop to refer to the array of constraints.
412 : *
413 : * @param node_id Devicetree node identifier.
414 : * @param prop Property name containing the list of power state phandles.
415 : */
416 1 : #define PM_STATE_CONSTRAINTS_GET(node_id, prop) \
417 : { \
418 : .list = Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, prop), \
419 : .count = DT_PROP_LEN(node_id, prop), \
420 : }
421 :
422 : #if defined(CONFIG_PM) || defined(__DOXYGEN__)
423 : /**
424 : * Obtain information about all supported states by a CPU.
425 : *
426 : * @param cpu CPU index.
427 : * @param states Where to store the list of supported states.
428 : *
429 : * @return Number of supported states.
430 : */
431 1 : uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states);
432 :
433 : /**
434 : * Get power state structure.
435 : *
436 : * Function searches in all states assigned to the CPU and in disabled states.
437 : *
438 : * @param cpu CPU index.
439 : * @param state Power state.
440 : * @param substate_id Substate.
441 : *
442 : * @return Pointer to the power state structure or NULL if state is not found.
443 : */
444 1 : const struct pm_state_info *pm_state_get(uint8_t cpu, enum pm_state state, uint8_t substate_id);
445 :
446 : /**
447 : * @brief Convert a pm_state enum value to its string representation.
448 : *
449 : * @param state Power state.
450 : *
451 : * @return A constant string representing the state.
452 : */
453 1 : const char *pm_state_to_str(enum pm_state state);
454 :
455 :
456 : /**
457 : * @brief Parse a string and convert it to a pm_state enum value.
458 : *
459 : * @param name Input string (e.g., "suspend-to-ram").
460 : * @param out Pointer to store the parsed pm_state value.
461 : *
462 : * @return 0 on success, -EINVAL if the string is invalid or NULL.
463 : */
464 1 : int pm_state_from_str(const char *name, enum pm_state *out);
465 :
466 : /**
467 : * @brief Check if a power management constraint matches any in a set of constraints.
468 : *
469 : * @param constraints Pointer to the power state constraints structure.
470 : * @param match The constraint to match against.
471 : *
472 : * @return true if the constraint matches, false otherwise.
473 : */
474 1 : bool pm_state_in_constraints(const struct pm_state_constraints *constraints,
475 : const struct pm_state_constraint match);
476 :
477 : /**
478 : * @}
479 : */
480 :
481 : #else /* CONFIG_PM */
482 :
483 : static inline uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states)
484 : {
485 : ARG_UNUSED(cpu);
486 : ARG_UNUSED(states);
487 :
488 : return 0;
489 : }
490 :
491 : static inline const struct pm_state_info *pm_state_get(uint8_t cpu,
492 : enum pm_state state,
493 : uint8_t substate_id)
494 : {
495 : ARG_UNUSED(cpu);
496 : ARG_UNUSED(state);
497 : ARG_UNUSED(substate_id);
498 :
499 : return NULL;
500 : }
501 :
502 : static inline bool pm_state_in_constraints(struct pm_state_constraints *constraints,
503 : struct pm_state_constraint match)
504 : {
505 : ARG_UNUSED(constraints);
506 : ARG_UNUSED(match);
507 :
508 : return false;
509 : }
510 : #endif /* CONFIG_PM */
511 :
512 : #ifdef __cplusplus
513 : }
514 : #endif
515 :
516 : #endif
|