1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * Power domain notification handling.
9 */
10
11 #include <internal/power_domain_notifications.h>
12
13 #include <fwk_log.h>
14 #include <fwk_module.h>
15 #include <fwk_notification.h>
16
17 /*
18 * Check whether a power state pre-transition notification must be sent.
19 *
20 * \param pd Description of the power domain
21 * \param state Power state the power domain has to transit to
22 *
23 * \retval true A power state pre-transition notification must be sent.
24 * \retval false A power state pre-transition notification doesn't have to be
25 * sent.
26 */
check_power_state_pre_transition_notification(struct pd_ctx * pd,unsigned int state)27 static bool check_power_state_pre_transition_notification(
28 struct pd_ctx *pd,
29 unsigned int state)
30 {
31 if ((state == pd->power_state_pre_transition_notification_ctx.state) &&
32 pd->power_state_pre_transition_notification_ctx.valid) {
33 return (
34 pd->power_state_pre_transition_notification_ctx.response_status !=
35 FWK_SUCCESS);
36 }
37
38 return true;
39 }
40
pd_process_notification(const struct fwk_event * event,struct fwk_event * resp)41 int pd_process_notification(
42 const struct fwk_event *event,
43 struct fwk_event *resp)
44 {
45 struct pd_ctx *pd;
46
47 /* Only responses are expected. */
48 if (!event->is_response) {
49 fwk_unexpected();
50 return FWK_E_SUPPORT;
51 }
52
53 if (fwk_id_is_equal(event->id, mod_pd_notification_id_pre_shutdown)) {
54 return process_pre_shutdown_notification_response();
55 }
56
57 if (!fwk_module_is_valid_element_id(event->target_id)) {
58 fwk_unexpected();
59 return FWK_E_PARAM;
60 }
61
62 pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(event->target_id)];
63
64 if (fwk_id_is_equal(
65 event->id, mod_pd_notification_id_power_state_transition)) {
66 return process_power_state_transition_notification_response(pd);
67 }
68
69 return process_power_state_pre_transition_notification_response(
70 pd,
71 (struct mod_pd_power_state_pre_transition_notification_resp_params *)
72 event->params);
73 }
74
process_pre_shutdown_notification_response(void)75 int process_pre_shutdown_notification_response(void)
76 {
77 if (mod_pd_ctx.system_shutdown.ongoing) {
78 mod_pd_ctx.system_shutdown.notifications_count--;
79
80 if (mod_pd_ctx.system_shutdown.notifications_count == 0) {
81 /* All notifications for system shutdown have been received */
82 perform_shutdown(mod_pd_ctx.system_shutdown.system_shutdown);
83 }
84 return FWK_SUCCESS;
85 } else {
86 return FWK_E_PARAM;
87 }
88 }
89
process_power_state_pre_transition_notification_response(struct pd_ctx * pd,struct mod_pd_power_state_pre_transition_notification_resp_params * params)90 int process_power_state_pre_transition_notification_response(
91 struct pd_ctx *pd,
92 struct mod_pd_power_state_pre_transition_notification_resp_params *params)
93 {
94 if (pd->power_state_pre_transition_notification_ctx.pending_responses ==
95 0) {
96 fwk_unexpected();
97 return FWK_E_PANIC;
98 }
99
100 if (params->status != FWK_SUCCESS) {
101 pd->power_state_pre_transition_notification_ctx.response_status =
102 FWK_E_DEVICE;
103 }
104
105 pd->power_state_pre_transition_notification_ctx.pending_responses--;
106 if (pd->power_state_pre_transition_notification_ctx.pending_responses !=
107 0) {
108 return FWK_SUCCESS;
109 }
110
111 if (pd->power_state_pre_transition_notification_ctx.valid == true) {
112 /*
113 * All the notification responses have been received, the requested
114 * state for the power domain has not changed in the
115 * meantime and all the notified entities agreed on the power state
116 * transition, proceed with it.
117 */
118 if (pd->power_state_pre_transition_notification_ctx.response_status ==
119 FWK_SUCCESS) {
120 return initiate_power_state_transition(pd);
121 }
122 } else {
123 /*
124 * All the notification responses have been received but the
125 * requested state for the power domain has changed, start the
126 * processings for the new requested state.
127 */
128 if ((pd->requested_state == pd->state_requested_to_driver) ||
129 (!is_allowed_by_parent_and_children(pd, pd->requested_state))) {
130 return FWK_SUCCESS;
131 }
132
133 if (!initiate_power_state_pre_transition_notification(pd)) {
134 return initiate_power_state_transition(pd);
135 }
136 }
137
138 return FWK_SUCCESS;
139 }
140
process_power_state_transition_notification_response(struct pd_ctx * pd)141 int process_power_state_transition_notification_response(struct pd_ctx *pd)
142 {
143 struct fwk_event notification_event = { 0 };
144 struct mod_pd_power_state_transition_notification_params *params;
145
146 if (pd->power_state_transition_notification_ctx.pending_responses == 0) {
147 fwk_unexpected();
148 return FWK_E_PANIC;
149 }
150
151 pd->power_state_transition_notification_ctx.pending_responses--;
152 if (pd->power_state_transition_notification_ctx.pending_responses != 0) {
153 return FWK_SUCCESS;
154 }
155
156 if (pd->power_state_transition_notification_ctx.state ==
157 pd->current_state) {
158 /* All notifications received, complete the transition report */
159
160 unsigned int previous_state =
161 pd->power_state_transition_notification_ctx.previous_state;
162
163 pd->power_state_transition_notification_ctx.previous_state =
164 pd->current_state;
165 /*
166 * Complete the report state change now that we have all notifications
167 */
168 if (is_deeper_state(pd->current_state, previous_state)) {
169 process_power_state_transition_report_deeper_state(pd);
170 } else if (is_shallower_state(pd->current_state, previous_state)) {
171 process_power_state_transition_report_shallower_state(pd);
172 }
173
174 return FWK_SUCCESS;
175 }
176
177 /*
178 * While receiving the responses, the power state of the power domain
179 * has changed. Send a notification for the current power state.
180 */
181 notification_event.id = mod_pd_notification_id_power_state_transition;
182 notification_event.response_requested = true;
183 notification_event.source_id = FWK_ID_NONE;
184
185 params = (struct mod_pd_power_state_transition_notification_params *)
186 notification_event.params;
187 params->state = pd->current_state;
188
189 pd->power_state_transition_notification_ctx.state = pd->current_state;
190 return fwk_notification_notify(
191 ¬ification_event,
192 &pd->power_state_transition_notification_ctx.pending_responses);
193 }
194
initiate_power_state_pre_transition_notification(struct pd_ctx * pd)195 bool initiate_power_state_pre_transition_notification(struct pd_ctx *pd)
196 {
197 unsigned int state;
198 struct fwk_event notification_event = {
199 .id = mod_pd_notification_id_power_state_pre_transition,
200 .response_requested = true,
201 .source_id = FWK_ID_NONE
202 };
203 struct mod_pd_power_state_pre_transition_notification_params *params;
204 int status;
205
206 if (pd->config->disable_state_transition_notifications == true) {
207 return false;
208 }
209
210 state = pd->requested_state;
211 if (!check_power_state_pre_transition_notification(pd, state)) {
212 return false;
213 }
214
215 /*
216 * If still waiting for some responses on the previous power state
217 * pre-transition notification, wait for them before to issue the next one.
218 */
219 if (pd->power_state_pre_transition_notification_ctx.pending_responses !=
220 0) {
221 return true;
222 }
223
224 params = (struct mod_pd_power_state_pre_transition_notification_params *)
225 notification_event.params;
226 params->current_state = pd->current_state;
227 params->target_state = state;
228
229 notification_event.source_id = pd->id;
230 status = fwk_notification_notify(
231 ¬ification_event,
232 &pd->power_state_pre_transition_notification_ctx.pending_responses);
233 if (status != FWK_SUCCESS) {
234 FWK_LOG_DEBUG("[PD] %s @%d", __func__, __LINE__);
235 }
236
237 pd->power_state_pre_transition_notification_ctx.state = state;
238 pd->power_state_pre_transition_notification_ctx.response_status =
239 FWK_SUCCESS;
240 pd->power_state_pre_transition_notification_ctx.valid = true;
241
242 return (
243 pd->power_state_pre_transition_notification_ctx.pending_responses != 0);
244 }
245
check_and_notify_system_shutdown(enum mod_pd_system_shutdown system_shutdown)246 bool check_and_notify_system_shutdown(
247 enum mod_pd_system_shutdown system_shutdown)
248 {
249 struct mod_pd_pre_shutdown_notif_params *params;
250 int status;
251
252 struct fwk_event notification = { .id = mod_pd_notification_id_pre_shutdown,
253 .source_id = fwk_module_id_power_domain,
254 .response_requested = true };
255
256 params = (struct mod_pd_pre_shutdown_notif_params *)notification.params;
257 params->system_shutdown = system_shutdown;
258
259 status = fwk_notification_notify(
260 ¬ification, &mod_pd_ctx.system_shutdown.notifications_count);
261 if (status != FWK_SUCCESS) {
262 FWK_LOG_DEBUG("[PD] %s @%d", __func__, __LINE__);
263 }
264
265 return (mod_pd_ctx.system_shutdown.notifications_count != 0);
266 }
267
notify_warm_reset(void)268 int notify_warm_reset(void)
269 {
270 unsigned int count;
271 struct fwk_event notification = {
272 .id = mod_pd_notification_id_pre_warm_reset,
273 .source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_POWER_DOMAIN),
274 };
275
276 return fwk_notification_notify(¬ification, &count);
277 }
278