1 /*
2  * Copyright (c) 2018 Intel Corporation.
3  * Copyright (c) 2021 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/pm/state.h>
9 #include <zephyr/toolchain.h>
10 
11 BUILD_ASSERT(DT_NODE_EXISTS(DT_PATH(cpus)),
12 	     "cpus node not defined in Devicetree");
13 
14 /**
15  * Check CPU power state consistency.
16  *
17  * @param i Power state index.
18  * @param node_id CPU node identifier.
19  */
20 #define CHECK_POWER_STATE_CONSISTENCY(i, node_id)			       \
21 	BUILD_ASSERT(							       \
22 		DT_PROP_BY_PHANDLE_IDX_OR(node_id, cpu_power_states, i,	       \
23 					  min_residency_us, 0U) >=	       \
24 		DT_PROP_BY_PHANDLE_IDX_OR(node_id, cpu_power_states, i,	       \
25 					  exit_latency_us, 0U),		       \
26 		"Found CPU power state with min_residency < exit_latency")
27 
28 /**
29  * @brief Check CPU power states consistency
30  *
31  * All states should have a minimum residency >= than the exit latency.
32  *
33  * @param node_id A CPU node identifier.
34  */
35 #define CHECK_POWER_STATES_CONSISTENCY(node_id)				       \
36 	LISTIFY(DT_PROP_LEN_OR(node_id, cpu_power_states, 0),		       \
37 		CHECK_POWER_STATE_CONSISTENCY, (;), node_id);		       \
38 
39 /* Check that all power states are consistent */
40 DT_FOREACH_CHILD(DT_PATH(cpus), CHECK_POWER_STATES_CONSISTENCY)
41 
42 #define DEFINE_CPU_STATES(n) \
43 	static const struct pm_state_info pmstates_##n[] \
44 		= PM_STATE_INFO_LIST_FROM_DT_CPU(n);
45 #define CPU_STATE_REF(n) pmstates_##n
46 
47 DT_FOREACH_CHILD(DT_PATH(cpus), DEFINE_CPU_STATES);
48 
49 /** CPU power states information for each CPU */
50 static const struct pm_state_info *cpus_states[] = {
51 	DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus), CPU_STATE_REF, (,))
52 };
53 
54 /** Number of states for each CPU */
55 static const uint8_t states_per_cpu[] = {
56 	DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus), DT_NUM_CPU_POWER_STATES, (,))
57 };
58 
59 #define DEFINE_DISABLED_PM_STATE(node) \
60 	IF_ENABLED(DT_NODE_HAS_STATUS(node, disabled), (PM_STATE_INFO_DT_INIT(node),))
61 
62 /** Check if power states exists in the Devicetree. */
63 #define POWER_STATES_EXISTS()						       \
64 	UTIL_OR(DT_NODE_EXISTS(DT_PATH(cpus, power_states)),		       \
65 		DT_NODE_EXISTS(DT_PATH(power_states)))
66 
67 /** Get node with power states. Macro assumes that power states exists. */
68 #define POWER_STATES_NODE()						       \
69 	COND_CODE_1(DT_NODE_EXISTS(DT_PATH(cpus, power_states)),	       \
70 		    (DT_PATH(cpus, power_states)), (DT_PATH(power_states)))
71 
72 /* Array with all states which are disabled but can be forced. */
73 static const struct pm_state_info disabled_states[] = {
74 	IF_ENABLED(POWER_STATES_EXISTS(),
75 		   (DT_FOREACH_CHILD(POWER_STATES_NODE(), DEFINE_DISABLED_PM_STATE)))
76 };
77 
pm_state_cpu_get_all(uint8_t cpu,const struct pm_state_info ** states)78 uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states)
79 {
80 	if (cpu >= ARRAY_SIZE(cpus_states)) {
81 		return 0;
82 	}
83 
84 	*states = cpus_states[cpu];
85 
86 	return states_per_cpu[cpu];
87 }
88 
pm_state_get(uint8_t cpu,enum pm_state state,uint8_t substate_id)89 const struct pm_state_info *pm_state_get(uint8_t cpu, enum pm_state state, uint8_t substate_id)
90 {
91 	__ASSERT_NO_MSG(cpu < ARRAY_SIZE(cpus_states));
92 	const struct pm_state_info *states = cpus_states[cpu];
93 	uint8_t cnt = states_per_cpu[cpu];
94 
95 	for (uint8_t i = 0; i < cnt; i++) {
96 		if ((states[i].state == state) && (states[i].substate_id == substate_id)) {
97 			return &states[i];
98 		}
99 	}
100 
101 	for (uint8_t i = 0; i < ARRAY_SIZE(disabled_states); i++) {
102 		if ((disabled_states[i].state == state) &&
103 		    (disabled_states[i].substate_id == substate_id)) {
104 			return &disabled_states[i];
105 		}
106 	}
107 
108 	return NULL;
109 }
110