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         &notification_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         &notification_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         &notification, &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(&notification, &count);
277 }
278