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