1 /*
2  * Copyright (c) 2018 Intel Corporation.
3  * Copyright (c) 2022 Nordic Semiconductor ASA
4  * Copyright 2025 NXP
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <zephyr/pm/policy.h>
10 #include <zephyr/pm/state.h>
11 #include <zephyr/sys/util_macro.h>
12 #include <zephyr/pm/device.h>
13 
14 struct pm_state_device_constraint {
15 	const char *const dev;
16 	size_t pm_constraints_size;
17 	struct pm_state_constraint *constraints;
18 };
19 
20 /**
21  * @brief Synthesize the name of the object that holds a device pm constraint.
22  *
23  * @param dev_id Device identifier.
24  */
25 #define PM_CONSTRAINTS_NAME(node_id) _CONCAT(__devicepmconstraints_, node_id)
26 
27 /**
28  * @brief initialize a device pm constraint with information from devicetree.
29  *
30  * @param node_id Node identifier.
31  */
32 #define PM_STATE_CONSTRAINT_INIT(node_id)                                     \
33 	{                                                                     \
34 		.state = PM_STATE_DT_INIT(node_id),                           \
35 		.substate_id = DT_PROP_OR(node_id, substate_id, 0),           \
36 	}
37 
38 /**
39  * @brief Helper macro to define a device pm constraints.
40  */
41 #define PM_STATE_CONSTRAINT_DEFINE(i, node_id)                                  \
42 	COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_PHANDLE_BY_IDX(node_id,          \
43 		zephyr_disabling_power_states, i)),                             \
44 		(PM_STATE_CONSTRAINT_INIT(DT_PHANDLE_BY_IDX(node_id,            \
45 		zephyr_disabling_power_states, i)),), ())
46 
47 /**
48  * @brief Helper macro to generate a list of device pm constraints.
49  */
50 #define PM_STATE_CONSTRAINTS_DEFINE(node_id)                                           \
51 	{                                                                              \
52 		LISTIFY(DT_PROP_LEN_OR(node_id, zephyr_disabling_power_states, 0),     \
53 			PM_STATE_CONSTRAINT_DEFINE, (), node_id)                       \
54 	}
55 
56 /**
57  * @brief Helper macro to define an array of device pm constraints.
58  */
59 #define CONSTRAINTS_DEFINE(node_id)                         \
60 	Z_DECL_ALIGN(struct pm_state_constraint)            \
61 		PM_CONSTRAINTS_NAME(node_id)[] =            \
62 		PM_STATE_CONSTRAINTS_DEFINE(node_id);
63 
64 #define DEVICE_CONSTRAINTS_DEFINE(node_id)                                           \
65 	COND_CODE_0(DT_NODE_HAS_PROP(node_id, zephyr_disabling_power_states), (),    \
66 		(CONSTRAINTS_DEFINE(node_id)))
67 
68 DT_FOREACH_STATUS_OKAY_NODE(DEVICE_CONSTRAINTS_DEFINE)
69 
70 /**
71  * @brief Helper macro to initialize a pm state device constraint
72  */
73 #define PM_STATE_DEVICE_CONSTRAINT_INIT(node_id)                                              \
74 	{                                                                                     \
75 		.dev = DEVICE_DT_NAME(node_id),                                                \
76 		.pm_constraints_size = DT_PROP_LEN(node_id, zephyr_disabling_power_states),   \
77 		.constraints = PM_CONSTRAINTS_NAME(node_id),                                  \
78 	},
79 
80 /**
81  * @brief Helper macro to initialize a pm state device constraint
82  */
83 #define PM_STATE_DEVICE_CONSTRAINT_DEFINE(node_id)                                      \
84 	COND_CODE_0(DT_NODE_HAS_PROP(node_id, zephyr_disabling_power_states), (),       \
85 		(PM_STATE_DEVICE_CONSTRAINT_INIT(node_id)))
86 
87 static struct pm_state_device_constraint _devices_constraints[] = {
88 	DT_FOREACH_STATUS_OKAY_NODE(PM_STATE_DEVICE_CONSTRAINT_DEFINE)
89 };
90 
91 /* returns device's constraints in _devices_constraints, NULL if not found */
92 static struct pm_state_device_constraint *
pm_policy_priv_device_find_device_constraints(const struct device * dev)93 pm_policy_priv_device_find_device_constraints(const struct device *dev)
94 {
95 #if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
96 	if (dev == NULL) {
97 		return NULL;
98 	}
99 
100 	for (size_t i = 0; i < ARRAY_SIZE(_devices_constraints); i++) {
101 		const struct device *device = device_get_binding(_devices_constraints[i].dev);
102 
103 		if (device == dev) {
104 			return &_devices_constraints[i];
105 		}
106 	}
107 #endif
108 	return NULL;
109 }
110 
pm_policy_device_power_lock_get(const struct device * dev)111 void pm_policy_device_power_lock_get(const struct device *dev)
112 {
113 #if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
114 	struct pm_state_device_constraint *constraints =
115 				pm_policy_priv_device_find_device_constraints(dev);
116 	if (constraints == NULL) {
117 		return;
118 	}
119 
120 	for (size_t j = 0; j < constraints->pm_constraints_size; j++) {
121 		pm_policy_state_lock_get(constraints->constraints[j].state,
122 					 constraints->constraints[j].substate_id);
123 	}
124 #endif
125 }
126 
pm_policy_device_power_lock_put(const struct device * dev)127 void pm_policy_device_power_lock_put(const struct device *dev)
128 {
129 #if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
130 	struct pm_state_device_constraint *constraints =
131 				pm_policy_priv_device_find_device_constraints(dev);
132 	if (constraints == NULL) {
133 		return;
134 	}
135 
136 	for (size_t j = 0; j < constraints->pm_constraints_size; j++) {
137 		pm_policy_state_lock_put(constraints->constraints[j].state,
138 					 constraints->constraints[j].substate_id);
139 	}
140 #endif
141 }
142 
pm_policy_device_is_disabling_state(const struct device * dev,enum pm_state state,uint8_t substate_id)143 bool pm_policy_device_is_disabling_state(const struct device *dev,
144 					 enum pm_state state, uint8_t substate_id)
145 {
146 #if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
147 	struct pm_state_device_constraint *constraints =
148 				pm_policy_priv_device_find_device_constraints(dev);
149 	if (constraints == NULL) {
150 		return false;
151 	}
152 
153 	for (size_t j = 0; j < constraints->pm_constraints_size; j++) {
154 		if (constraints->constraints[j].state == state &&
155 		    constraints->constraints[j].substate_id == substate_id) {
156 			return true;
157 		}
158 	}
159 #endif
160 	return false;
161 }
162