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 ¬ification_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 ¬ification_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