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