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 #define DEFINE_CPU_STATES(n) \
15 	static const struct pm_state_info pmstates_##n[] \
16 		= PM_STATE_INFO_LIST_FROM_DT_CPU(n);
17 #define CPU_STATE_REF(n) pmstates_##n
18 
19 DT_FOREACH_CHILD(DT_PATH(cpus), DEFINE_CPU_STATES);
20 
21 /** CPU power states information for each CPU */
22 static const struct pm_state_info *cpus_states[] = {
23 	DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus), CPU_STATE_REF, (,))
24 };
25 
26 /** Number of states for each CPU */
27 static const uint8_t states_per_cpu[] = {
28 	DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus), DT_NUM_CPU_POWER_STATES, (,))
29 };
30 
31 #define DEFINE_DISABLED_PM_STATE(node) \
32 	IF_ENABLED(DT_NODE_HAS_STATUS(node, disabled), (PM_STATE_INFO_DT_INIT(node),))
33 
34 /** Check if power states exists in the Devicetree. */
35 #define POWER_STATES_EXISTS()						       \
36 	UTIL_OR(DT_NODE_EXISTS(DT_PATH(cpus, power_states)),		       \
37 		DT_NODE_EXISTS(DT_PATH(power_states)))
38 
39 /** Get node with power states. Macro assumes that power states exists. */
40 #define POWER_STATES_NODE()						       \
41 	COND_CODE_1(DT_NODE_EXISTS(DT_PATH(cpus, power_states)),	       \
42 		    (DT_PATH(cpus, power_states)), (DT_PATH(power_states)))
43 
44 /* Array with all states which are disabled but can be forced. */
45 static const struct pm_state_info disabled_states[] = {
46 	IF_ENABLED(POWER_STATES_EXISTS(),
47 		   (DT_FOREACH_CHILD(POWER_STATES_NODE(), DEFINE_DISABLED_PM_STATE)))
48 };
49 
pm_state_cpu_get_all(uint8_t cpu,const struct pm_state_info ** states)50 uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states)
51 {
52 	if (cpu >= ARRAY_SIZE(cpus_states)) {
53 		return 0;
54 	}
55 
56 	*states = cpus_states[cpu];
57 
58 	return states_per_cpu[cpu];
59 }
60 
pm_state_get(uint8_t cpu,enum pm_state state,uint8_t substate_id)61 const struct pm_state_info *pm_state_get(uint8_t cpu, enum pm_state state, uint8_t substate_id)
62 {
63 	__ASSERT_NO_MSG(cpu < ARRAY_SIZE(cpus_states));
64 	const struct pm_state_info *states = cpus_states[cpu];
65 	uint8_t cnt = states_per_cpu[cpu];
66 
67 	for (uint8_t i = 0; i < cnt; i++) {
68 		if ((states[i].state == state) && (states[i].substate_id == substate_id)) {
69 			return &states[i];
70 		}
71 	}
72 
73 	for (uint8_t i = 0; i < ARRAY_SIZE(disabled_states); i++) {
74 		if ((disabled_states[i].state == state) &&
75 		    (disabled_states[i].substate_id == substate_id)) {
76 			return &disabled_states[i];
77 		}
78 	}
79 
80 	return NULL;
81 }
82 
pm_state_to_str(enum pm_state state)83 const char *pm_state_to_str(enum pm_state state)
84 {
85 	switch (state) {
86 	case PM_STATE_ACTIVE:
87 		return "active";
88 	case PM_STATE_RUNTIME_IDLE:
89 		return "runtime-idle";
90 	case PM_STATE_SUSPEND_TO_IDLE:
91 		return "suspend-to-idle";
92 	case PM_STATE_STANDBY:
93 		return "standby";
94 	case PM_STATE_SUSPEND_TO_RAM:
95 		return "suspend-to-ram";
96 	case PM_STATE_SUSPEND_TO_DISK:
97 		return "suspend-to-disk";
98 	case PM_STATE_SOFT_OFF:
99 		return "soft-off";
100 	default:
101 		return "UNKNOWN";
102 	}
103 }
104 
pm_state_from_str(const char * name,enum pm_state * out)105 int pm_state_from_str(const char *name, enum pm_state *out)
106 {
107 	if (strcmp(name, "active") == 0) {
108 		*out = PM_STATE_ACTIVE;
109 	} else if (strcmp(name, "runtime-idle") == 0) {
110 		*out = PM_STATE_RUNTIME_IDLE;
111 	} else if (strcmp(name, "suspend-to-idle") == 0) {
112 		*out = PM_STATE_SUSPEND_TO_IDLE;
113 	} else if (strcmp(name, "standby") == 0) {
114 		*out = PM_STATE_STANDBY;
115 	} else if (strcmp(name, "suspend-to-ram") == 0) {
116 		*out = PM_STATE_SUSPEND_TO_RAM;
117 	} else if (strcmp(name, "suspend-to-disk") == 0) {
118 		*out = PM_STATE_SUSPEND_TO_DISK;
119 	} else if (strcmp(name, "soft-off") == 0) {
120 		*out = PM_STATE_SOFT_OFF;
121 	} else {
122 		return -EINVAL;
123 	}
124 
125 	return 0;
126 }
127 
pm_state_in_constraints(const struct pm_state_constraints * constraints,const struct pm_state_constraint match)128 bool pm_state_in_constraints(const struct pm_state_constraints *constraints,
129 			     const struct pm_state_constraint match)
130 {
131 	struct pm_state_constraint *constraints_list = constraints->list;
132 	size_t num_constraints = constraints->count;
133 	bool match_found = false;
134 
135 	for (int i = 0; i < num_constraints; i++) {
136 		enum pm_state state = constraints_list[i].state;
137 		uint8_t substate = constraints_list[i].substate_id;
138 
139 		match_found |= ((state == match.state) && (substate == match.substate_id));
140 	}
141 
142 	return match_found;
143 }
144