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 to initialize an entry of a struct pm_state_info array
163  * when using UTIL_LISTIFY in PM_STATE_INFO_LIST_FROM_DT_CPU.
164  *
165  * @param i UTIL_LISTIFY entry index.
166  * @param node_id A node identifier with compatible zephyr,power-state
167  */
168 #define Z_PM_STATE_INFO_FROM_DT_CPU(i, node_id) \
169 	PM_STATE_INFO_DT_INIT(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i))
170 
171 /**
172  * @brief Helper macro to initialize an entry of a struct pm_state array when
173  * using UTIL_LISTIFY in PM_STATE_LIST_FROM_DT_CPU.
174  *
175  * @param i UTIL_LISTIFY entry index.
176  * @param node_id A node identifier with compatible zephyr,power-state
177  */
178 #define Z_PM_STATE_FROM_DT_CPU(i, node_id) \
179 	PM_STATE_DT_INIT(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i))
180 
181 /** @endcond */
182 
183 /**
184  * @brief Initializer for struct pm_state_info given a DT node identifier with
185  * zephyr,power-state compatible.
186  *
187  * @param node_id A node identifier with compatible zephyr,power-state
188  */
189 #define PM_STATE_INFO_DT_INIT(node_id)					       \
190 	{								       \
191 		.state = PM_STATE_DT_INIT(node_id),			       \
192 		.substate_id = DT_PROP_OR(node_id, substate_id, 0),	       \
193 		.min_residency_us = DT_PROP_OR(node_id, min_residency_us, 0),  \
194 		.exit_latency_us = DT_PROP_OR(node_id, exit_latency_us, 0),    \
195 	}
196 
197 /**
198  * @brief Initializer for enum pm_state given a DT node identifier with
199  * zephyr,power-state compatible.
200  *
201  * @param node_id A node identifier with compatible zephyr,power-state
202  */
203 #define PM_STATE_DT_INIT(node_id) \
204 	DT_ENUM_IDX(node_id, power_state_name)
205 
206 /**
207  * @brief Obtain number of CPU power states supported by the given CPU node
208  * identifier.
209  *
210  * @param node_id A CPU node identifier.
211  * @return Number of supported CPU power states.
212  */
213 #define DT_NUM_CPU_POWER_STATES(node_id) \
214 	DT_PROP_LEN_OR(node_id, cpu_power_states, 0)
215 
216 /**
217  * @brief Initialize an array of struct pm_state_info with information from all
218  * the states present in the given CPU node identifier.
219  *
220  * Example devicetree fragment:
221  *
222  * @code{.dts}
223  *	cpus {
224  *		...
225  *		cpu0: cpu@0 {
226  *			device_type = "cpu";
227  *			...
228  *			cpu-power-states = <&state0 &state1>;
229  *		};
230  *	};
231  *
232  *	...
233  *	power-states {
234  *		state0: state0 {
235  *			compatible = "zephyr,power-state";
236  *			power-state-name = "suspend-to-idle";
237  *			min-residency-us = <10000>;
238  *			exit-latency-us = <100>;
239  *		};
240  *
241  *		state1: state1 {
242  *			compatible = "zephyr,power-state";
243  *			power-state-name = "suspend-to-ram";
244  *			min-residency-us = <50000>;
245  *			exit-latency-us = <500>;
246  *		};
247  *	};
248  * @endcode
249  *
250  * Example usage:
251  *
252  * @code{.c}
253  * const struct pm_state_info states[] =
254  *	PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
255  * @endcode
256  *
257  * @param node_id A CPU node identifier.
258  */
259 #define PM_STATE_INFO_LIST_FROM_DT_CPU(node_id)				       \
260 	{								       \
261 		LISTIFY(DT_NUM_CPU_POWER_STATES(node_id),		       \
262 			Z_PM_STATE_INFO_FROM_DT_CPU, (,), node_id)	       \
263 	}
264 
265 /**
266  * @brief Initialize an array of struct pm_state with information from all the
267  * states present in the given CPU node identifier.
268  *
269  * Example devicetree fragment:
270  *
271  * @code{.dts}
272  *	cpus {
273  *		...
274  *		cpu0: cpu@0 {
275  *			device_type = "cpu";
276  *			...
277  *			cpu-power-states = <&state0 &state1>;
278  *		};
279  *	};
280  *
281  *	...
282  *	power-states {
283  *		state0: state0 {
284  *			compatible = "zephyr,power-state";
285  *			power-state-name = "suspend-to-idle";
286  *			min-residency-us = <10000>;
287  *			exit-latency-us = <100>;
288  *		};
289  *
290  *		state1: state1 {
291  *			compatible = "zephyr,power-state";
292  *			power-state-name = "suspend-to-ram";
293  *			min-residency-us = <50000>;
294  *			exit-latency-us = <500>;
295  *		};
296  *	};
297  * @endcode
298  *
299  * Example usage:
300  *
301  * @code{.c}
302  * const enum pm_state states[] = PM_STATE_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
303  * @endcode
304  *
305  * @param node_id A CPU node identifier.
306  */
307 #define PM_STATE_LIST_FROM_DT_CPU(node_id)				       \
308 	{								       \
309 		LISTIFY(DT_NUM_CPU_POWER_STATES(node_id),		       \
310 			Z_PM_STATE_FROM_DT_CPU, (,), node_id)		       \
311 	}
312 
313 
314 /**
315  * Obtain information about all supported states by a CPU.
316  *
317  * @param cpu CPU index.
318  * @param states Where to store the list of supported states.
319  *
320  * @return Number of supported states.
321  */
322 uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states);
323 
324 /**
325  * @}
326  */
327 
328 #ifdef __cplusplus
329 }
330 #endif
331 
332 #endif
333