1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Description:
8  *     Power domain management support.
9  */
10 
11 #include <internal/power_domain.h>
12 #include <internal/power_domain_notifications.h>
13 
14 #include <mod_power_domain.h>
15 
16 #include <fwk_assert.h>
17 #include <fwk_core.h>
18 #include <fwk_event.h>
19 #include <fwk_id.h>
20 #include <fwk_log.h>
21 #include <fwk_macros.h>
22 #include <fwk_mm.h>
23 #include <fwk_module.h>
24 #include <fwk_module_idx.h>
25 #include <fwk_notification.h>
26 #include <fwk_status.h>
27 
28 #include <inttypes.h>
29 #include <stdbool.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 
33 /* Mask of the core composite states */
34 static const uint32_t core_composite_state_mask_table[] = {
35     MOD_PD_CS_STATE_MASK << MOD_PD_CS_LEVEL_0_STATE_SHIFT,
36     MOD_PD_CS_STATE_MASK << MOD_PD_CS_LEVEL_1_STATE_SHIFT,
37     MOD_PD_CS_STATE_MASK << MOD_PD_CS_LEVEL_2_STATE_SHIFT,
38     MOD_PD_CS_STATE_MASK << MOD_PD_CS_LEVEL_3_STATE_SHIFT,
39 };
40 
41 /*
42  * Internal variables
43  */
44 struct mod_pd_mod_ctx mod_pd_ctx;
45 
46 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
47 static const char driver_error_msg[] = "[PD] Driver error %s (%d) in %s @%d";
48 #endif
49 
50 /*
51  * Wrapper functions for returning default values when notifications are
52  * disabled
53  */
54 
warm_reset_notification_wrapper(void)55 static inline int warm_reset_notification_wrapper(void)
56 {
57 #ifdef BUILD_HAS_NOTIFICATION
58     return notify_warm_reset();
59 #else
60     return FWK_SUCCESS;
61 #endif
62 }
63 
power_state_pre_transition_notification_wrapper(struct pd_ctx * pd)64 static inline bool power_state_pre_transition_notification_wrapper(
65     struct pd_ctx *pd)
66 {
67 #ifdef BUILD_HAS_NOTIFICATION
68     return initiate_power_state_pre_transition_notification(pd);
69 #else
70     return false;
71 #endif
72 }
73 
notify_system_shutdown_wrapper(enum mod_pd_system_shutdown system_shutdown)74 static inline bool notify_system_shutdown_wrapper(
75     enum mod_pd_system_shutdown system_shutdown)
76 {
77 #ifdef BUILD_HAS_NOTIFICATION
78     return check_and_notify_system_shutdown(system_shutdown);
79 #else
80     return false;
81 #endif
82 }
83 
84 /*
85  * Utility functions
86  */
87 
88 /* Sub-routine of 'pd_post_init()', to build the power domain tree */
connect_pd_tree(void)89 static int connect_pd_tree(void)
90 {
91     unsigned int index;
92     struct pd_ctx *pd, *parent;
93 
94     for (index = 0; index < mod_pd_ctx.pd_count; index++) {
95         pd = &mod_pd_ctx.pd_ctx_table[index];
96         if (pd->config->parent_idx >= mod_pd_ctx.pd_count) {
97             pd->parent = NULL;
98             continue;
99         }
100 
101         parent = pd->parent = &mod_pd_ctx.pd_ctx_table[pd->config->parent_idx];
102         if (parent == NULL) {
103             return FWK_E_DATA;
104         }
105         fwk_list_push_tail(&parent->children_list, &pd->child_node);
106     }
107 
108     return FWK_SUCCESS;
109 }
110 
initiate_power_state_transition(struct pd_ctx * pd)111 int initiate_power_state_transition(struct pd_ctx *pd)
112 {
113     int status;
114     unsigned int state = pd->requested_state;
115     unsigned int mapped_state;
116 
117     if ((pd->driver_api->deny != NULL) &&
118         pd->driver_api->deny(pd->driver_id, state)) {
119 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_DEBUG
120         FWK_LOG_DEBUG(
121             "[PD] Transition of %s to state <%s> denied by driver",
122             fwk_module_get_element_name(pd->id),
123             get_state_name(pd, state));
124 #endif
125         return FWK_E_DEVICE;
126     }
127 
128     mapped_state = retrieve_mapped_state(pd, state);
129     status = pd->driver_api->set_state(pd->driver_id, mapped_state);
130 
131     if (status == FWK_SUCCESS) {
132 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_DEBUG
133         FWK_LOG_DEBUG(
134             "[PD] Transition of %s from <%s> to <%s> succeeded",
135             fwk_module_get_element_name(pd->id),
136             get_state_name(pd, pd->state_requested_to_driver),
137             get_state_name(pd, state));
138 #endif
139         pd->driver_state = mapped_state;
140         pd->state_requested_to_driver = state;
141     }
142 
143 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
144     if (status != FWK_SUCCESS) {
145         FWK_LOG_ERR(
146             "[PD] Transition of %s from <%s> to <%s> failed: %s",
147             fwk_module_get_element_name(pd->id),
148             get_state_name(pd, pd->state_requested_to_driver),
149             get_state_name(pd, state),
150             fwk_status_str(status));
151     }
152 #endif
153 
154     return status;
155 }
156 
157 /*
158  * Send a power domain's delayed response to a set state request.
159  *
160  * \param pd Description of the power domain in charge of the response
161  * \param resp_status Response status
162  */
send_pd_set_state_delayed_response(struct pd_ctx * pd,int resp_status)163 static void send_pd_set_state_delayed_response(
164     struct pd_ctx *pd,
165     int resp_status)
166 {
167     int status;
168     struct fwk_event resp_event;
169     const struct pd_set_state_request *req_params =
170         (struct pd_set_state_request *)(&resp_event.params);
171     struct pd_set_state_response *resp_params =
172         (struct pd_set_state_response *)(&resp_event.params);
173 
174     if (!pd->response.pending) {
175         return;
176     }
177 
178     status = fwk_get_delayed_response(pd->id, pd->response.cookie, &resp_event);
179     pd->response.pending = false;
180 
181     if (status != FWK_SUCCESS) {
182         return;
183     }
184 
185     resp_params->composite_state = req_params->composite_state;
186     resp_params->status = resp_status;
187 
188     status = fwk_put_event(&resp_event);
189     if (status != FWK_SUCCESS) {
190         FWK_LOG_DEBUG("[PD] %s @%d", __func__, __LINE__);
191     }
192 }
193 
194 /*
195  * Process a 'set state' request
196  *
197  * \param lowest_pd  Description of the target of the 'set state' request
198  * \param req_params Parameters of the 'set state' request
199  * \param [out] Response event
200  */
process_set_state_request(struct pd_ctx * lowest_pd,const struct fwk_event * event,struct fwk_event * resp_event)201 static void process_set_state_request(
202     struct pd_ctx *lowest_pd,
203     const struct fwk_event *event,
204     struct fwk_event *resp_event)
205 {
206     int status;
207     struct pd_set_state_request *req_params;
208     struct pd_set_state_response *resp_params;
209     uint32_t composite_state;
210     bool up, first_power_state_transition_initiated, composite_state_operation;
211     unsigned int lowest_level, highest_level, level;
212     unsigned int nb_pds, pd_index, state, prev_state;
213     struct pd_ctx *pd, *pd_in_charge_of_response;
214     const struct pd_ctx *parent;
215     const uint32_t *state_mask_table = NULL;
216 
217     req_params = (struct pd_set_state_request *)event->params;
218     resp_params = (struct pd_set_state_response *)resp_event->params;
219     pd_in_charge_of_response = NULL;
220     first_power_state_transition_initiated = false;
221 
222     /* A set state request cancels the completion of system suspend. */
223     mod_pd_ctx.system_suspend.last_core_off_ongoing = false;
224 
225     composite_state = req_params->composite_state;
226     up = is_upwards_transition_propagation(lowest_pd, composite_state);
227 
228     /*
229      * It has already been tested as part of the composite state validation that
230      * 'highest_level >= lowest_level' and 'highest_level' is lower
231      * than the highest power level.
232      */
233     lowest_level = 0;
234     highest_level = (unsigned int)get_highest_level_from_composite_state(
235         lowest_pd, composite_state);
236     nb_pds = highest_level + 1U;
237 
238     status = FWK_SUCCESS;
239     pd = lowest_pd;
240 
241     composite_state_operation = pd->cs_support;
242     if (composite_state_operation) {
243         state_mask_table = pd->composite_state_mask_table;
244     }
245 
246     for (pd_index = 0; pd_index < nb_pds; pd_index++, pd = pd->parent) {
247         if (up) {
248             level = pd_index;
249         } else {
250             /*
251              * When walking down the power domain tree, get the context of the
252              * next power domain to process as well as its level.
253              */
254             pd = lowest_pd;
255             for (level = lowest_level; level < (highest_level - pd_index);
256                  level++) {
257                 pd = pd->parent;
258             }
259         }
260 
261         if (composite_state_operation) {
262             state = get_level_state_from_composite_state(
263                 state_mask_table, composite_state, (int)level);
264         } else {
265             state = composite_state;
266         }
267 
268         if (state == pd->requested_state) {
269             continue;
270         }
271 
272         /*
273          * Check that the requested power state is compatible with the states
274          * currently requested for the parent and children of the power domain.
275          */
276         parent = pd->parent;
277         if ((parent != NULL) &&
278             (!is_allowed_by_child(pd, parent->requested_state, state))) {
279             status = FWK_E_PWRSTATE;
280             break;
281         }
282 
283         if (!is_allowed_by_children(pd, state)) {
284             continue;
285         }
286 
287         /*
288          * A new valid power state is requested for the power domain. Send any
289          * pending response concerning the previous requested power state.
290          */
291         prev_state = pd->requested_state;
292         pd->requested_state = state;
293         pd->power_state_pre_transition_notification_ctx.valid = false;
294         send_pd_set_state_delayed_response(pd, FWK_E_OVERWRITTEN);
295 
296         if (pd->state_requested_to_driver == state) {
297             continue;
298         }
299 
300         /*
301          * The driver must be called thus the processing of the set state
302          * request is going to be asynchronous. Assign the responsibility of
303          * the response to the request to the power domain. If there is no
304          * need for a driver call for the ancestors or descendants of the power
305          * domain as part of the processing of the requested composite state,
306          * the response to the request will be sent when the transition to the
307          * new requested power state is completed.
308          */
309         pd_in_charge_of_response = pd;
310 
311         /*
312          * If a power state transition has already been initiated for an
313          * ancestor or descendant, we don't initiate the power state transition
314          * now. It will be initiated on completion of the transition of one
315          * of its ancestor or descendant.
316          */
317         if (first_power_state_transition_initiated) {
318             continue;
319         }
320 
321         /*
322          * If the parent or a child is not currently in a power state
323          * compatible with the new requested state for the power domain, do not
324          * initiate the transition now as well. It will be initiated when the
325          * parent and the children are in a proper state.
326          */
327         if (!is_allowed_by_parent_and_children(pd, state)) {
328             continue;
329         }
330 
331         /*
332          * Defer the power state transition if power state pre-transition
333          * notification responses need to be waited for.
334          */
335         if (power_state_pre_transition_notification_wrapper(pd)) {
336             continue;
337         }
338 
339         status = initiate_power_state_transition(pd);
340         if (status != FWK_SUCCESS) {
341             /*
342              * If the power state transition failed, then this power domain is
343              * no longer in charge to delay the response.
344              */
345             pd_in_charge_of_response = NULL;
346 
347             /* The power state change failed, restore the previous state */
348             pd->requested_state = prev_state;
349             break;
350         }
351 
352         first_power_state_transition_initiated = true;
353     }
354 
355     if (!event->response_requested) {
356         return;
357     }
358 
359     if (pd_in_charge_of_response != NULL) {
360         resp_event->is_delayed_response = true;
361         resp_event->source_id = pd_in_charge_of_response->id;
362         pd_in_charge_of_response->response.pending = true;
363         pd_in_charge_of_response->response.cookie = resp_event->cookie;
364     } else {
365         resp_params->status = status;
366         resp_params->composite_state = composite_state;
367     }
368 }
369 
370 /*
371  * Complete a system suspend
372  *
373  * Following the shutdown of the last standing core put all of its ancestors
374  * in the MOD_PD_STATE_OFF state but the system power domain which is put
375  * into the state that has been asked for.
376  *
377  * target_pd Description of the power domain target of the 'set composite state'
378  *     request to suspend the system in the desired state.
379  */
complete_system_suspend(struct pd_ctx * target_pd)380 static int complete_system_suspend(struct pd_ctx *target_pd)
381 {
382     unsigned int level;
383     unsigned int shift, composite_state = 0;
384     struct pd_ctx *pd = target_pd;
385     struct fwk_event event, resp_event;
386     struct pd_set_state_request *event_params =
387         (struct pd_set_state_request *)event.params;
388     struct pd_set_state_response *resp_params =
389         (struct pd_set_state_response *)(&resp_event.params);
390     const uint32_t *state_mask_table;
391     int table_size;
392 
393     if (!pd->cs_support) {
394         return FWK_E_PARAM;
395     }
396 
397     state_mask_table = pd->composite_state_mask_table;
398     table_size = (int)pd->composite_state_mask_table_size;
399 
400     mod_pd_ctx.system_suspend.suspend_ongoing = true;
401 
402     /*
403      * Traverse the PD tree bottom-up from current power domain to the top
404      * to build the composite state with MOD_PD_STATE_OFF power state for all
405      * levels but the last one.
406      */
407     level = 0U;
408     do {
409         shift = number_of_bits_to_shift(state_mask_table[level]);
410         composite_state |=
411             ((pd->parent != NULL) ? MOD_PD_STATE_OFF :
412                                     mod_pd_ctx.system_suspend.state)
413             << shift;
414         pd = pd->parent;
415         level++;
416     } while ((pd != NULL) && (level < (unsigned int)table_size));
417 
418     /*
419      * Finally, we need to update the highest valid level in the composite
420      * state.
421      */
422     composite_state |= (--level) << MOD_PD_CS_LEVEL_SHIFT;
423 
424     event = (struct fwk_event){ 0 };
425     event_params->composite_state = composite_state;
426 
427     resp_event = (struct fwk_event){ 0 };
428 
429     process_set_state_request(target_pd, &event, &resp_event);
430 
431     return resp_params->status;
432 }
433 
434 /*
435  * Process a 'get state' request.
436  *
437  * pd Description of the target of the 'get state' request
438  * state the required state to be filled in
439  */
process_get_state_request(struct pd_ctx * pd,unsigned int * state)440 static void process_get_state_request(struct pd_ctx *pd, unsigned int *state)
441 {
442     unsigned int level = 0U;
443     struct pd_ctx *const base_pd = pd;
444     unsigned int composite_state = 0U;
445     uint32_t shift;
446     const uint32_t *state_mask_table;
447     int table_size, cs_idx = 0;
448 
449     if (!pd->cs_support) {
450         *state = pd->current_state;
451     } else {
452         state_mask_table = pd->composite_state_mask_table;
453         table_size = (int)pd->composite_state_mask_table_size;
454 
455         /*
456          * Traverse the PD tree bottom-up from current power domain to the top,
457          * collecting node's states and placing them in the correct position in
458          * the composite state.
459          */
460         do {
461             shift = number_of_bits_to_shift(state_mask_table[cs_idx]);
462             composite_state |= pd->current_state << shift;
463             pd = pd->parent;
464             cs_idx++;
465             level++;
466         } while (pd != NULL && cs_idx < table_size);
467 
468         /*
469          * Finally, we need to update the highest valid level in
470          * the composite state.
471          */
472         if (base_pd->composite_state_levels_mask) {
473             shift =
474                 number_of_bits_to_shift(base_pd->composite_state_levels_mask);
475             composite_state |= (--level) << shift;
476         }
477 
478         *state = composite_state;
479     }
480 }
481 
482 /*
483  * Process a 'reset' request.
484  *
485  * pd Description of the target of the 'reset' request
486  * resp_params Parameters of the 'reset' request response to be filled in
487  */
process_reset_request(struct pd_ctx * pd,struct pd_response * resp_params)488 static void process_reset_request(
489     struct pd_ctx *pd,
490     struct pd_response *resp_params)
491 {
492     int status;
493     struct pd_ctx *child = NULL;
494     struct fwk_slist *c_node = NULL;
495 
496     status = FWK_E_PWRSTATE;
497     if (pd->requested_state == MOD_PD_STATE_OFF) {
498         goto exit;
499     }
500 
501     FWK_LIST_FOR_EACH(
502         &pd->children_list, c_node, struct pd_ctx, child_node, child)
503     {
504         if ((child->requested_state != MOD_PD_STATE_OFF) ||
505             (child->current_state != MOD_PD_STATE_OFF)) {
506             goto exit;
507         }
508     }
509 
510     status = pd->driver_api->reset(pd->driver_id);
511 
512 exit:
513     resp_params->status = status;
514 }
515 
516 /*
517  * Process a power state transition report describing a transition to a deeper
518  * state.
519  *
520  * \param pd Target power domain context
521  */
process_power_state_transition_report_deeper_state(struct pd_ctx * pd)522 void process_power_state_transition_report_deeper_state(struct pd_ctx *pd)
523 {
524     struct pd_ctx *parent = pd->parent;
525     unsigned int requested_state;
526     int status;
527 
528     if (parent == NULL) {
529         return;
530     }
531 
532     requested_state = parent->requested_state;
533 
534     if (parent->state_requested_to_driver == requested_state) {
535         return;
536     }
537 
538     if (!is_allowed_by_parent_and_children(parent, requested_state)) {
539         return;
540     }
541 
542     if (!power_state_pre_transition_notification_wrapper(parent)) {
543         status = initiate_power_state_transition(parent);
544         if (status != FWK_SUCCESS) {
545             FWK_LOG_DEBUG("[PD] %s @%d", __func__, __LINE__);
546         }
547     }
548     return;
549 }
550 
551 /*
552  * Process a power state transition report describing a transition to a
553  * shallower state.
554  *
555  * \param pd Target power domain context
556  */
process_power_state_transition_report_shallower_state(struct pd_ctx * pd)557 void process_power_state_transition_report_shallower_state(struct pd_ctx *pd)
558 {
559     struct pd_ctx *child = NULL;
560     unsigned int requested_state;
561     struct fwk_slist *c_node = NULL;
562     int status;
563 
564     FWK_LIST_FOR_EACH(
565         &pd->children_list, c_node, struct pd_ctx, child_node, child)
566     {
567         requested_state = child->requested_state;
568         if (child->state_requested_to_driver == requested_state) {
569             continue;
570         }
571 
572         if (!is_allowed_by_parent_and_children(child, requested_state)) {
573             return;
574         }
575 
576         if (!power_state_pre_transition_notification_wrapper(child)) {
577             status = initiate_power_state_transition(child);
578             if (status != FWK_SUCCESS) {
579                 FWK_LOG_DEBUG("[PD] %s @%d", __func__, __LINE__);
580             }
581         }
582     }
583 }
584 
585 /*
586  * Process a power state transition report
587  *
588  * \param pd Description of the target of the power state transition report
589  * \param report_params Parameters of the power state transition report
590  */
process_power_state_transition_report(struct pd_ctx * pd,const struct pd_power_state_transition_report * report_params)591 static void process_power_state_transition_report(
592     struct pd_ctx *pd,
593     const struct pd_power_state_transition_report *report_params)
594 {
595     unsigned int previous_state;
596 #ifdef BUILD_HAS_NOTIFICATION
597     struct fwk_event notification_event = {
598         .id = mod_pd_notification_id_power_state_transition,
599         .response_requested = true,
600         .source_id = FWK_ID_NONE
601     };
602     struct mod_pd_power_state_transition_notification_params *params;
603 
604     struct fwk_event notification_event_suspend = { 0 };
605 
606     notification_event_suspend.id = mod_pd_notification_id_system_suspend;
607     notification_event_suspend.response_requested = false;
608     notification_event_suspend.source_id = fwk_module_id_power_domain;
609 
610 #endif
611 
612     int status;
613 
614     unsigned int new_state = report_params->state;
615 
616     if (new_state == pd->driver_state) {
617         send_pd_set_state_delayed_response(pd, FWK_SUCCESS);
618     }
619 
620     previous_state = pd->current_state;
621     /*
622      * This must be updated with the requested state,
623      * not the received state, to compensate for the
624      * possible state mapping that may have occured.
625      */
626     pd->current_state = pd->requested_state;
627 
628 #ifdef BUILD_HAS_NOTIFICATION
629     if (pd->power_state_transition_notification_ctx.pending_responses == 0 &&
630         pd->config->disable_state_transition_notifications == false) {
631         params = (struct mod_pd_power_state_transition_notification_params *)
632                      notification_event.params;
633         params->state = new_state;
634         pd->power_state_transition_notification_ctx.state = new_state;
635         status = fwk_notification_notify(
636             &notification_event,
637             &pd->power_state_transition_notification_ctx.pending_responses);
638         if (status != FWK_SUCCESS) {
639             FWK_LOG_DEBUG("[PD] %s @%d", __func__, __LINE__);
640         }
641     }
642 #endif
643 
644     if ((mod_pd_ctx.system_suspend.last_core_off_ongoing) &&
645         (pd == mod_pd_ctx.system_suspend.last_core_pd)) {
646         mod_pd_ctx.system_suspend.last_core_off_ongoing = false;
647 #ifdef BUILD_HAS_NOTIFICATION
648         if (mod_pd_ctx.config->enable_system_suspend_notification == false) {
649             status = complete_system_suspend(pd);
650         } else {
651             status = fwk_notification_notify(
652                 &notification_event_suspend,
653                 &mod_pd_ctx.system_suspend_notification.notifications_count);
654             if (status != FWK_SUCCESS) {
655                 FWK_LOG_DEBUG("[PD] %s @%d", __func__, __LINE__);
656             }
657         }
658 #else
659         status = complete_system_suspend(pd);
660 #endif
661         if (status != FWK_SUCCESS) {
662             FWK_LOG_DEBUG("[PD] %s @%d", __func__, __LINE__);
663         }
664 
665         return;
666     }
667 
668     if (pd->parent == NULL) {
669         /* this is the top pd (SYSTOP) */
670         if (mod_pd_ctx.system_suspend.state != MOD_PD_STATE_ON) {
671             /* has gone down, invalidate the system suspend ongoing */
672             mod_pd_ctx.system_suspend.suspend_ongoing = false;
673         }
674     }
675 
676 #ifdef BUILD_HAS_NOTIFICATION
677     /*
678      * If notifications are pending, the transition report is delayed until all
679      * the state change notifications responses have arrived.
680      */
681     if (pd->power_state_transition_notification_ctx.pending_responses > 0) {
682         /*
683          * Save previous state which will be used once all the notifications
684          * have arrived to continue for deeper or shallower state for the next
685          * power domain.
686          */
687         pd->power_state_transition_notification_ctx.previous_state =
688             previous_state;
689 
690         return;
691     }
692 #endif
693 
694     /* Update the pd states to follow the new transition */
695     pd->requested_state = pd->state_requested_to_driver = pd->current_state;
696 
697     if (is_deeper_state(new_state, previous_state)) {
698         process_power_state_transition_report_deeper_state(pd);
699     } else if (is_shallower_state(new_state, previous_state)) {
700         process_power_state_transition_report_shallower_state(pd);
701     }
702 }
703 
704 /*
705  * Process a 'system suspend' request
706  *
707  * req_params Parameters of the 'system suspend' request
708  * resp_params Parameters of the 'system suspend' request response to be filled
709  *     in
710  */
process_system_suspend_request(const struct pd_system_suspend_request * req_params,struct pd_response * resp_params)711 static void process_system_suspend_request(
712     const struct pd_system_suspend_request *req_params,
713     struct pd_response *resp_params)
714 {
715     int status;
716     unsigned int pd_idx;
717     struct pd_ctx *pd;
718     struct pd_ctx *last_core_pd = NULL;
719     struct pd_ctx *last_cluster_pd = NULL;
720 
721     /*
722      * All core related power domains have to be in the MOD_PD_STATE_OFF state
723      * but one core and its ancestors.
724      */
725     for (pd_idx = 0; pd_idx < mod_pd_ctx.pd_count; pd_idx++) {
726         pd = &mod_pd_ctx.pd_ctx_table[pd_idx];
727         if ((pd->requested_state == MOD_PD_STATE_OFF) &&
728             (pd->current_state == MOD_PD_STATE_OFF)) {
729             continue;
730         }
731 
732         if (pd->config->attributes.pd_type == MOD_PD_TYPE_CORE) {
733             if (last_core_pd != NULL) {
734                 resp_params->status = FWK_E_STATE;
735                 return;
736             }
737             last_core_pd = pd;
738         } else if (pd->config->attributes.pd_type == MOD_PD_TYPE_CLUSTER) {
739             if (last_cluster_pd != NULL) {
740                 resp_params->status = FWK_E_STATE;
741                 return;
742             }
743             last_cluster_pd = pd;
744         }
745     }
746 
747     if (last_core_pd == NULL) {
748         status = complete_system_suspend(
749             (last_cluster_pd != NULL) ? last_cluster_pd :
750                                         mod_pd_ctx.system_pd_ctx);
751     } else {
752         status = last_core_pd->driver_api->prepare_core_for_system_suspend(
753             last_core_pd->driver_id);
754         if (status == FWK_SUCCESS) {
755             mod_pd_ctx.system_suspend.last_core_off_ongoing = true;
756 
757             mod_pd_ctx.system_suspend.last_core_pd = last_core_pd;
758             mod_pd_ctx.system_suspend.state = req_params->state;
759             last_core_pd->requested_state =
760                 last_core_pd->state_requested_to_driver =
761                     (unsigned int)MOD_PD_STATE_OFF;
762         }
763     }
764 
765     resp_params->status = status;
766 }
767 
perform_shutdown_send_response(struct system_shutdown_ctx * ctx)768 static int perform_shutdown_send_response(struct system_shutdown_ctx *ctx)
769 {
770     int status;
771     struct fwk_event delayed_resp;
772     struct fwk_event *response_event;
773     struct pd_response *resp_params;
774 
775     if (ctx->is_delayed_response) {
776         status = fwk_get_delayed_response(
777             fwk_module_id_power_domain, ctx->cookie, &delayed_resp);
778         if (status != FWK_SUCCESS) {
779             return status;
780         }
781 
782         delayed_resp.source_id = fwk_module_id_power_domain;
783         response_event = &delayed_resp;
784     } else {
785         response_event = ctx->response_event;
786     }
787 
788     resp_params = (struct pd_response *)response_event->params;
789     resp_params->status = FWK_E_PANIC;
790     status = fwk_put_event(response_event);
791     return status;
792 }
793 
perform_shutdown(enum mod_pd_system_shutdown system_shutdown)794 void perform_shutdown(enum mod_pd_system_shutdown system_shutdown)
795 {
796     struct pd_ctx *pd;
797     unsigned int pd_idx;
798     fwk_id_t pd_id;
799     int status;
800     struct mod_pd_driver_api *api;
801 
802     for (pd_idx = 0; pd_idx < mod_pd_ctx.pd_count; pd_idx++) {
803         pd = &mod_pd_ctx.pd_ctx_table[pd_idx];
804         pd_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, pd_idx);
805         api = pd->driver_api;
806 
807         (void)pd_id;
808         FWK_LOG_INFO(
809             "[PD] Shutting down %s", fwk_module_get_element_name(pd_id));
810 
811         if (api->shutdown != NULL) {
812             status = pd->driver_api->shutdown(pd->driver_id, system_shutdown);
813             if (status == FWK_PENDING) {
814                 /*
815                  * Only drivers implementing shutdown API can defer request for
816                  * now.
817                  */
818                 return;
819             }
820         } else {
821             if ((api->deny != NULL) &&
822                 api->deny(pd->driver_id, MOD_PD_STATE_OFF)) {
823                 status = FWK_E_DEVICE;
824             } else {
825                 status = api->set_state(pd->driver_id, MOD_PD_STATE_OFF);
826             }
827         }
828 
829         if (status != FWK_SUCCESS) {
830             FWK_LOG_ERR(
831                 "[PD] Shutdown of %s returned %s (%d)",
832                 fwk_module_get_element_name(pd_id),
833                 fwk_status_str(status),
834                 status);
835         } else {
836             FWK_LOG_INFO(
837                 "[PD] %s shutdown", fwk_module_get_element_name(pd_id));
838         }
839 
840         pd->requested_state = pd->state_requested_to_driver =
841             pd->current_state = (unsigned int)MOD_PD_STATE_OFF;
842     }
843 
844     /*
845      * At this time, the system is already down or will be down soon.
846      * Regardless, we tentatively send the response event to the caller, should
847      * the system fail to complete the shutdown process, the agent may want to
848      * be notified.
849      */
850     if (mod_pd_ctx.system_shutdown.is_response_requested) {
851         status = perform_shutdown_send_response(&mod_pd_ctx.system_shutdown);
852         fwk_assert(status == FWK_SUCCESS);
853     }
854 
855     mod_pd_ctx.system_shutdown.ongoing = false;
856 
857     return;
858 }
859 
860 /*
861  * Process a 'system shutdown' request
862  */
process_system_shutdown_request(const struct fwk_event * event,struct fwk_event * resp)863 static void process_system_shutdown_request(
864     const struct fwk_event *event,
865     struct fwk_event *resp)
866 {
867     enum mod_pd_system_shutdown system_shutdown;
868 
869     const struct pd_system_shutdown_request *req_params =
870         (struct pd_system_shutdown_request *)event->params;
871     struct pd_response *resp_params = (struct pd_response *)resp->params;
872 
873     system_shutdown = req_params->system_shutdown;
874 
875     switch (system_shutdown) {
876     case MOD_PD_SYSTEM_WARM_RESET:
877         resp_params->status = warm_reset_notification_wrapper();
878         break;
879 
880     default:
881         /* Check and send pre-shutdown notifications */
882         if (notify_system_shutdown_wrapper(system_shutdown)) {
883             mod_pd_ctx.system_shutdown.ongoing = true;
884             mod_pd_ctx.system_shutdown.system_shutdown = system_shutdown;
885 
886             if (event->response_requested) {
887                 mod_pd_ctx.system_shutdown.is_delayed_response = true;
888                 mod_pd_ctx.system_shutdown.cookie = event->cookie;
889                 resp->is_delayed_response = true;
890             }
891 
892             /*
893              * The shutdown procedure will be completed once all the
894              * notification responses have been received.
895              */
896             return;
897         } else {
898             if (event->response_requested) {
899                 mod_pd_ctx.system_shutdown.is_delayed_response = false;
900                 mod_pd_ctx.system_shutdown.response_event = resp;
901             }
902         }
903 
904         mod_pd_ctx.system_shutdown.is_response_requested =
905             event->response_requested;
906         perform_shutdown(system_shutdown);
907 
908         resp_params->status = FWK_E_PANIC;
909     }
910 }
911 
912 /*
913  * API functions
914  */
915 
916 /* Functions common to the public and restricted API */
pd_get_domain_type(fwk_id_t pd_id,enum mod_pd_type * type)917 static int pd_get_domain_type(fwk_id_t pd_id, enum mod_pd_type *type)
918 {
919     struct pd_ctx *pd;
920 
921     if (type == NULL) {
922         return FWK_E_PARAM;
923     }
924 
925     if (!fwk_module_is_valid_element_id(pd_id)) {
926         return FWK_E_PARAM;
927     }
928 
929     pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
930 
931     *type = pd->config->attributes.pd_type;
932 
933     return FWK_SUCCESS;
934 }
935 
pd_get_domain_parent_id(fwk_id_t pd_id,fwk_id_t * parent_pd_id)936 static int pd_get_domain_parent_id(fwk_id_t pd_id, fwk_id_t *parent_pd_id)
937 {
938     const struct pd_ctx *pd;
939 
940     if (parent_pd_id == NULL) {
941         return FWK_E_PARAM;
942     }
943 
944     if (!fwk_module_is_valid_element_id(pd_id)) {
945         return FWK_E_PARAM;
946     }
947 
948     pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
949 
950     *parent_pd_id = (pd->parent != NULL) ? pd->parent->id : FWK_ID_NONE;
951 
952     return FWK_SUCCESS;
953 }
954 
955 /* Functions specific to the restricted API */
pd_set_state(fwk_id_t pd_id,bool response_requested,uint32_t state)956 static int pd_set_state(fwk_id_t pd_id, bool response_requested, uint32_t state)
957 {
958     struct pd_ctx *pd;
959     struct fwk_event req;
960     struct pd_set_state_request *req_params =
961         (struct pd_set_state_request *)(&req.params);
962 
963     pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
964 
965     if (pd->cs_support) {
966         if (!is_valid_composite_state(pd, state)) {
967             return FWK_E_PARAM;
968         }
969     } else {
970         if (!is_valid_state(pd, state)) {
971             return FWK_E_PARAM;
972         }
973     }
974 
975     req = (struct fwk_event){
976         .id = FWK_ID_EVENT(
977             FWK_MODULE_IDX_POWER_DOMAIN, MOD_PD_PUBLIC_EVENT_IDX_SET_STATE),
978         .source_id = pd->driver_id,
979         .target_id = pd_id,
980         .response_requested = response_requested,
981     };
982 
983     req_params->composite_state = state;
984 
985     return fwk_put_event(&req);
986 }
987 
pd_get_state(fwk_id_t pd_id,unsigned int * state)988 static int pd_get_state(fwk_id_t pd_id, unsigned int *state)
989 {
990     struct pd_ctx *pd = NULL;
991 
992     if (state == NULL) {
993         return FWK_E_PARAM;
994     }
995 
996     pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
997 
998     process_get_state_request(pd, state);
999 
1000     return FWK_SUCCESS;
1001 }
1002 
pd_system_suspend(unsigned int state)1003 static int pd_system_suspend(unsigned int state)
1004 {
1005     unsigned int i, active_cores;
1006     struct pd_ctx *pd;
1007     struct fwk_event req;
1008     struct pd_system_suspend_request *req_params =
1009         (struct pd_system_suspend_request *)(&req.params);
1010 
1011     /* System suspend is only supported if exactly one core remains active */
1012     active_cores = 0u;
1013     for (i = 0; i < mod_pd_ctx.pd_count; i++) {
1014         pd = &mod_pd_ctx.pd_ctx_table[i];
1015 
1016         if (pd->config->attributes.pd_type == MOD_PD_TYPE_CORE &&
1017             is_state_in_transition(pd, (unsigned int)MOD_PD_STATE_OFF)) {
1018             active_cores += 1;
1019         }
1020     }
1021 
1022     if (active_cores > 1) {
1023         FWK_LOG_ERR("[PD] Invalid system suspend request.");
1024         return FWK_E_STATE;
1025     }
1026 
1027     req = (struct fwk_event){
1028         .id = FWK_ID_EVENT(
1029             FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_SYSTEM_SUSPEND),
1030         .target_id = fwk_module_id_power_domain,
1031     };
1032 
1033     req_params->state = state;
1034 
1035     return fwk_put_event(&req);
1036 }
1037 
pd_system_shutdown(enum mod_pd_system_shutdown system_shutdown)1038 static int pd_system_shutdown(enum mod_pd_system_shutdown system_shutdown)
1039 {
1040     int status;
1041     struct fwk_event req;
1042     struct pd_system_shutdown_request *req_params =
1043         (struct pd_system_shutdown_request *)(&req.params);
1044 
1045     req = (struct fwk_event){
1046         .id = FWK_ID_EVENT(
1047             FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_SYSTEM_SHUTDOWN),
1048         .target_id = fwk_module_id_power_domain,
1049     };
1050 
1051     req_params->system_shutdown = system_shutdown;
1052 
1053     status = fwk_put_event(&req);
1054     if (status == FWK_SUCCESS) {
1055         return FWK_PENDING;
1056     }
1057 
1058     return status;
1059 }
1060 
1061 /* Functions specific to the driver input API */
1062 
pd_reset(fwk_id_t pd_id,bool response_requested)1063 static int pd_reset(fwk_id_t pd_id, bool response_requested)
1064 {
1065     struct fwk_event_light req;
1066     struct pd_ctx *pd;
1067 
1068     pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
1069 
1070     req = (struct fwk_event_light){
1071         .id = FWK_ID_EVENT(FWK_MODULE_IDX_POWER_DOMAIN, PD_EVENT_IDX_RESET),
1072         .source_id = pd->driver_id,
1073         .target_id = pd_id,
1074         .response_requested = response_requested,
1075     };
1076 
1077     return fwk_put_event(&req);
1078 }
1079 
report_power_state_transition(const struct pd_ctx * pd,unsigned int state)1080 static int report_power_state_transition(
1081     const struct pd_ctx *pd,
1082     unsigned int state)
1083 {
1084     struct fwk_event report;
1085     struct pd_power_state_transition_report *report_params =
1086         (struct pd_power_state_transition_report *)(&report.params);
1087 
1088     report =
1089         (struct fwk_event){ .source_id = pd->driver_id,
1090                             .target_id = pd->id,
1091                             .id = FWK_ID_EVENT(
1092                                 FWK_MODULE_IDX_POWER_DOMAIN,
1093                                 PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION) };
1094     report_params->state = state;
1095 
1096     return fwk_put_event(&report);
1097 }
1098 
pd_report_power_state_transition(fwk_id_t pd_id,unsigned int state)1099 static int pd_report_power_state_transition(fwk_id_t pd_id, unsigned int state)
1100 {
1101     const struct pd_ctx *pd =
1102         &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
1103 
1104     return report_power_state_transition(pd, state);
1105 }
1106 
pd_get_last_core_pd_id(fwk_id_t * last_core_pd_id)1107 static int pd_get_last_core_pd_id(fwk_id_t *last_core_pd_id)
1108 {
1109     bool system_suspend_ongoing = mod_pd_ctx.system_suspend.suspend_ongoing;
1110     if (last_core_pd_id == NULL) {
1111         return FWK_E_PARAM;
1112     }
1113 
1114     if (!system_suspend_ongoing) {
1115         return FWK_E_PWRSTATE;
1116     }
1117 
1118     *last_core_pd_id = mod_pd_ctx.system_suspend.last_core_pd->id;
1119 
1120     return FWK_SUCCESS;
1121 }
1122 
1123 /* Module APIs */
1124 
1125 static const struct mod_pd_public_api pd_public_api = {
1126     .get_domain_type = pd_get_domain_type,
1127     .get_domain_parent_id = pd_get_domain_parent_id,
1128 };
1129 
1130 static const struct mod_pd_restricted_api pd_restricted_api = {
1131     .get_domain_type = pd_get_domain_type,
1132     .get_domain_parent_id = pd_get_domain_parent_id,
1133 
1134     .set_state = pd_set_state,
1135     .get_state = pd_get_state,
1136     .reset = pd_reset,
1137     .system_suspend = pd_system_suspend,
1138     .system_shutdown = pd_system_shutdown
1139 };
1140 
1141 static const struct mod_pd_driver_input_api pd_driver_input_api = {
1142     .set_state = pd_set_state,
1143     .reset = pd_reset,
1144     .report_power_state_transition = pd_report_power_state_transition,
1145     .get_last_core_pd_id = pd_get_last_core_pd_id,
1146 };
1147 
1148 /*
1149  * Framework handlers
1150  */
pd_init(fwk_id_t module_id,unsigned int dev_count,const void * data)1151 static int pd_init(fwk_id_t module_id, unsigned int dev_count, const void *data)
1152 {
1153     if ((data == NULL) || (dev_count == 0)) {
1154         return FWK_E_PARAM;
1155     }
1156 
1157     mod_pd_ctx.config = (struct mod_power_domain_config *)data;
1158 
1159     if ((mod_pd_ctx.config->authorized_id_table == NULL) &&
1160         (mod_pd_ctx.config->authorized_id_table_size != 0)) {
1161         return FWK_E_PARAM;
1162     }
1163 
1164     mod_pd_ctx.pd_ctx_table = fwk_mm_calloc(dev_count, sizeof(struct pd_ctx));
1165 
1166     mod_pd_ctx.pd_count = dev_count;
1167     mod_pd_ctx.system_pd_ctx = &mod_pd_ctx.pd_ctx_table[dev_count - 1];
1168 
1169     return FWK_SUCCESS;
1170 }
1171 
pd_power_domain_init(fwk_id_t pd_id,unsigned int unused,const void * config)1172 static int pd_power_domain_init(
1173     fwk_id_t pd_id,
1174     unsigned int unused,
1175     const void *config)
1176 {
1177     const struct mod_power_domain_element_config *pd_config =
1178         (const struct mod_power_domain_element_config *)config;
1179     struct pd_ctx *pd;
1180     unsigned int state;
1181 
1182     pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
1183 
1184     fwk_list_init(&pd->children_list);
1185 
1186     if (pd_config->attributes.pd_type >= MOD_PD_TYPE_COUNT) {
1187         return FWK_E_PARAM;
1188     }
1189 
1190     if ((pd_config->allowed_state_mask_table == NULL) ||
1191         (pd_config->allowed_state_mask_table_size == 0)) {
1192         return FWK_E_PARAM;
1193     }
1194 
1195     pd->allowed_state_mask_table = pd_config->allowed_state_mask_table;
1196     pd->allowed_state_mask_table_size =
1197         pd_config->allowed_state_mask_table_size;
1198 
1199     for (state = 0; state < pd->allowed_state_mask_table_size; state++) {
1200         pd->valid_state_mask |= pd->allowed_state_mask_table[state];
1201     }
1202 
1203     if ((pd_config->composite_state_mask_table != NULL) &&
1204         (pd_config->composite_state_mask_table_size > 0)) {
1205         pd->composite_state_mask_table = pd_config->composite_state_mask_table;
1206         pd->composite_state_mask_table_size =
1207             pd_config->composite_state_mask_table_size;
1208         pd->composite_state_levels_mask =
1209             pd_config->composite_state_levels_mask;
1210         pd->cs_support = true;
1211 
1212     } else if (pd_config->attributes.pd_type == MOD_PD_TYPE_CORE) {
1213         pd->composite_state_mask_table = core_composite_state_mask_table;
1214         pd->composite_state_mask_table_size =
1215             FWK_ARRAY_SIZE(core_composite_state_mask_table);
1216         pd->composite_state_levels_mask = MOD_PD_CS_STATE_MASK
1217             << MOD_PD_CS_LEVEL_SHIFT;
1218         pd->cs_support = true;
1219 
1220     } else {
1221         pd->cs_support = false;
1222     }
1223 
1224     pd->id = pd_id;
1225     pd->config = pd_config;
1226 
1227     return FWK_SUCCESS;
1228 }
1229 
pd_post_init(fwk_id_t module_id)1230 static int pd_post_init(fwk_id_t module_id)
1231 {
1232     int status;
1233 
1234     status = connect_pd_tree();
1235     if (status != FWK_SUCCESS) {
1236         return status;
1237     }
1238 
1239     return FWK_SUCCESS;
1240 }
1241 
pd_bind(fwk_id_t id,unsigned int round)1242 static int pd_bind(fwk_id_t id, unsigned int round)
1243 {
1244     int status;
1245     struct pd_ctx *pd;
1246     const struct mod_power_domain_element_config *config;
1247     struct mod_pd_driver_api *driver_api = NULL;
1248 
1249     /* Nothing to do but during the first round of calls */
1250     if (round != 0) {
1251         return FWK_SUCCESS;
1252     }
1253 
1254     if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
1255         return FWK_SUCCESS;
1256     }
1257 
1258     pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(id)];
1259     config = pd->config;
1260 
1261     status = fwk_module_bind(config->driver_id, config->api_id, &driver_api);
1262     if (status != FWK_SUCCESS) {
1263         return status;
1264     }
1265 
1266     pd->driver_id = config->driver_id;
1267     if ((driver_api->set_state == NULL) || (driver_api->get_state == NULL) ||
1268         (driver_api->reset == NULL) ||
1269         ((config->attributes.pd_type == MOD_PD_TYPE_CORE) &&
1270          (driver_api->prepare_core_for_system_suspend == NULL))) {
1271         return FWK_E_PARAM;
1272     }
1273 
1274     pd->driver_api = driver_api;
1275 
1276     return FWK_SUCCESS;
1277 }
1278 
pd_start(fwk_id_t id)1279 static int pd_start(fwk_id_t id)
1280 {
1281     int status;
1282     int index;
1283     struct pd_ctx *pd;
1284     unsigned int state;
1285 
1286     /* Nothing to do for elements */
1287     if (fwk_module_is_valid_element_id(id)) {
1288         return FWK_SUCCESS;
1289     }
1290 
1291     for (index = (int)(mod_pd_ctx.pd_count - 1); index >= 0; index--) {
1292         pd = &mod_pd_ctx.pd_ctx_table[index];
1293         pd->requested_state = (unsigned int)MOD_PD_STATE_OFF;
1294         pd->state_requested_to_driver = (unsigned int)MOD_PD_STATE_OFF;
1295         pd->current_state = (unsigned int)MOD_PD_STATE_OFF;
1296 
1297         /*
1298          * If the power domain parent is powered down, don't call the driver
1299          * to get the power domain state as the power domain registers may
1300          * not be accessible. That way, the drivers don't have to care about
1301          * this case.
1302          */
1303         if ((pd->parent != NULL) &&
1304             (pd->parent->requested_state == MOD_PD_STATE_OFF)) {
1305             continue;
1306         }
1307 
1308         /* Get the current power state of the power domain from its driver. */
1309         status = pd->driver_api->get_state(pd->driver_id, &state);
1310         if (status != FWK_SUCCESS) {
1311 #if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
1312             FWK_LOG_ERR(
1313                 driver_error_msg,
1314                 fwk_status_str(status),
1315                 status,
1316                 __func__,
1317                 __LINE__);
1318 #endif
1319         } else {
1320             pd->requested_state = pd->state_requested_to_driver = state;
1321 
1322             if (state == MOD_PD_STATE_OFF) {
1323                 continue;
1324             }
1325 
1326             status = report_power_state_transition(pd, state);
1327             if (status != FWK_SUCCESS) {
1328                 FWK_LOG_DEBUG("[PD] %s @%d", __func__, __LINE__);
1329             }
1330         }
1331     }
1332 
1333     return FWK_SUCCESS;
1334 }
1335 
pd_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)1336 static int pd_process_bind_request(
1337     fwk_id_t source_id,
1338     fwk_id_t target_id,
1339     fwk_id_t api_id,
1340     const void **api)
1341 {
1342     struct pd_ctx *pd;
1343     unsigned int id_idx;
1344 
1345     enum mod_pd_api_idx api_id_type =
1346         (enum mod_pd_api_idx)fwk_id_get_api_idx(api_id);
1347 
1348     switch (api_id_type) {
1349     case MOD_PD_API_IDX_PUBLIC:
1350         if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE)) {
1351             return FWK_E_ACCESS;
1352         }
1353         *api = &pd_public_api;
1354         break;
1355 
1356     case MOD_PD_API_IDX_RESTRICTED:
1357         if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE)) {
1358             return FWK_E_ACCESS;
1359         }
1360         if (mod_pd_ctx.config->authorized_id_table_size == 0) {
1361             *api = &pd_restricted_api;
1362             return FWK_SUCCESS;
1363         }
1364         for (id_idx = 0; id_idx < mod_pd_ctx.config->authorized_id_table_size;
1365              id_idx++) {
1366             if (fwk_id_is_equal(
1367                     source_id,
1368                     mod_pd_ctx.config->authorized_id_table[id_idx])) {
1369                 *api = &pd_restricted_api;
1370                 return FWK_SUCCESS;
1371             }
1372         }
1373         return FWK_E_ACCESS;
1374 
1375     case MOD_PD_API_IDX_DRIVER_INPUT:
1376         if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) {
1377             return FWK_E_ACCESS;
1378         }
1379         pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(target_id)];
1380         if (!fwk_id_is_equal(source_id, pd->driver_id)) {
1381             return FWK_E_ACCESS;
1382         }
1383         *api = &pd_driver_input_api;
1384         break;
1385 
1386     default:
1387         return FWK_E_PARAM;
1388     }
1389 
1390     return FWK_SUCCESS;
1391 }
1392 
pd_process_event(const struct fwk_event * event,struct fwk_event * resp)1393 static int pd_process_event(
1394     const struct fwk_event *event,
1395     struct fwk_event *resp)
1396 {
1397     struct pd_ctx *pd = NULL;
1398 
1399     if (fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT)) {
1400         pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(event->target_id)];
1401     }
1402 
1403     switch (fwk_id_get_event_idx(event->id)) {
1404     case (unsigned int)MOD_PD_PUBLIC_EVENT_IDX_SET_STATE:
1405         fwk_assert(pd != NULL);
1406 
1407         process_set_state_request(pd, event, resp);
1408 
1409         return FWK_SUCCESS;
1410 
1411     case (unsigned int)PD_EVENT_IDX_RESET:
1412         fwk_assert(pd != NULL);
1413 
1414         process_reset_request(pd, (struct pd_response *)resp->params);
1415 
1416         return FWK_SUCCESS;
1417 
1418     case (unsigned int)PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION:
1419         fwk_assert(pd != NULL);
1420 
1421         process_power_state_transition_report(
1422             pd, (struct pd_power_state_transition_report *)event->params);
1423 
1424         return FWK_SUCCESS;
1425 
1426     case (unsigned int)PD_EVENT_IDX_SYSTEM_SUSPEND:
1427         process_system_suspend_request(
1428             (struct pd_system_suspend_request *)event->params,
1429             (struct pd_response *)resp->params);
1430 
1431         return FWK_SUCCESS;
1432 
1433     case (unsigned int)PD_EVENT_IDX_SYSTEM_SHUTDOWN:
1434         process_system_shutdown_request(event, resp);
1435 
1436         return FWK_SUCCESS;
1437 
1438     default:
1439         FWK_LOG_ERR(
1440             "[PD] Invalid power state request: %s.", FWK_ID_STR(event->id));
1441 
1442         return FWK_E_PARAM;
1443     }
1444 }
1445 
1446 /* Module definition */
1447 const struct fwk_module module_power_domain = {
1448     .type = FWK_MODULE_TYPE_HAL,
1449     .api_count = (unsigned int)MOD_PD_API_IDX_COUNT,
1450     .event_count = (unsigned int)PD_EVENT_COUNT,
1451 #ifdef BUILD_HAS_NOTIFICATION
1452     .notification_count = (unsigned int)MOD_PD_NOTIFICATION_COUNT,
1453 #endif
1454     .init = pd_init,
1455     .element_init = pd_power_domain_init,
1456     .post_init = pd_post_init,
1457     .bind = pd_bind,
1458     .start = pd_start,
1459     .process_bind_request = pd_process_bind_request,
1460     .process_event = pd_process_event,
1461 #ifdef BUILD_HAS_NOTIFICATION
1462     .process_notification = pd_process_notification
1463 #endif
1464 };
1465