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