1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include <internal/scmi_system_power.h>
9 
10 #include <mod_power_domain.h>
11 
12 #include <fwk_log.h>
13 #include <mod_scmi.h>
14 #include <mod_scmi_system_power.h>
15 #include <mod_timer.h>
16 
17 #include <fwk_assert.h>
18 #include <fwk_attributes.h>
19 #include <fwk_id.h>
20 #include <fwk_macros.h>
21 #include <fwk_mm.h>
22 #include <fwk_module.h>
23 #include <fwk_module_idx.h>
24 #include <fwk_status.h>
25 
26 #include <stddef.h>
27 
28 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
29 #    include <mod_resource_perms.h>
30 #endif
31 
32 #define INVALID_AGENT_ID UINT32_MAX
33 
34 struct mod_scmi_sys_power_ctx {
35     const struct mod_scmi_system_power_config *config;
36     const struct mod_scmi_from_protocol_api *scmi_api;
37     const struct mod_pd_restricted_api *pd_api;
38     fwk_id_t system_power_domain_id;
39     bool start_graceful_process;
40     struct mod_timer_alarm_api *alarm_api;
41 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
42     unsigned int agent_count;
43     fwk_id_t *system_power_notifications;
44 #endif
45 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
46     /* SCMI Resource Permissions API */
47     const struct mod_res_permissions_api *res_perms_api;
48 #endif
49 };
50 
51 static int scmi_sys_power_version_handler(fwk_id_t service_id,
52     const uint32_t *payload);
53 static int scmi_sys_power_attributes_handler(fwk_id_t service_id,
54     const uint32_t *payload);
55 static int scmi_sys_power_msg_attributes_handler(fwk_id_t service_id,
56     const uint32_t *payload);
57 static int scmi_sys_power_state_set_handler(fwk_id_t service_id,
58     const uint32_t *payload);
59 static int scmi_sys_power_state_get_handler(fwk_id_t service_id,
60     const uint32_t *payload);
61 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
62 static int scmi_sys_power_state_notify_handler(fwk_id_t service_id,
63     const uint32_t *payload);
64 #endif
65 
66 /*
67  * Internal variables
68  */
69 static struct mod_scmi_sys_power_ctx scmi_sys_power_ctx;
70 
71 static int (*const handler_table[MOD_SCMI_SYS_POWER_COMMAND_COUNT])(
72     fwk_id_t,
73     const uint32_t *) = {
74     [MOD_SCMI_PROTOCOL_VERSION] = scmi_sys_power_version_handler,
75     [MOD_SCMI_PROTOCOL_ATTRIBUTES] = scmi_sys_power_attributes_handler,
76     [MOD_SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
77         scmi_sys_power_msg_attributes_handler,
78     [MOD_SCMI_SYS_POWER_STATE_SET] = scmi_sys_power_state_set_handler,
79     [MOD_SCMI_SYS_POWER_STATE_GET] = scmi_sys_power_state_get_handler,
80 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
81     [MOD_SCMI_SYS_POWER_STATE_NOTIFY] = scmi_sys_power_state_notify_handler,
82 #endif
83 };
84 
85 static const unsigned int
86     payload_size_table[MOD_SCMI_SYS_POWER_COMMAND_COUNT] = {
87         [MOD_SCMI_PROTOCOL_VERSION] = 0,
88         [MOD_SCMI_PROTOCOL_ATTRIBUTES] = 0,
89         [MOD_SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
90             (unsigned int)sizeof(struct scmi_protocol_message_attributes_a2p),
91         [MOD_SCMI_SYS_POWER_STATE_SET] =
92             (unsigned int)sizeof(struct scmi_sys_power_state_set_a2p),
93         [MOD_SCMI_SYS_POWER_STATE_GET] = 0,
94 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
95         [MOD_SCMI_SYS_POWER_STATE_NOTIFY] =
96             sizeof(struct scmi_sys_power_state_notify_a2p),
97 #endif
98     };
99 
100 static enum mod_pd_system_shutdown
101     system_state2system_shutdown[SCMI_SYSTEM_STATE_MAX] = {
102         [SCMI_SYSTEM_STATE_SHUTDOWN] = MOD_PD_SYSTEM_SHUTDOWN,
103         [SCMI_SYSTEM_STATE_COLD_RESET] = MOD_PD_SYSTEM_COLD_RESET,
104         [SCMI_SYSTEM_STATE_WARM_RESET] = MOD_PD_SYSTEM_WARM_RESET,
105     };
106 
system_state_get(enum scmi_system_state * system_state)107 static int system_state_get(enum scmi_system_state *system_state)
108 {
109     int status;
110     unsigned int state;
111     enum mod_pd_state state_type;
112 
113     status = scmi_sys_power_ctx.pd_api->get_state(
114         scmi_sys_power_ctx.system_power_domain_id, &state);
115     if (status != FWK_SUCCESS) {
116         return status;
117     }
118 
119     state_type = (enum mod_pd_state)state;
120 
121     switch (state_type) {
122     case MOD_PD_STATE_OFF:
123         *system_state = SCMI_SYSTEM_STATE_SHUTDOWN;
124         break;
125 
126     case MOD_PD_STATE_ON:
127         *system_state = SCMI_SYSTEM_STATE_POWER_UP;
128         break;
129 
130     default:
131         *system_state = SCMI_SYSTEM_STATE_SUSPEND;
132     }
133 
134     return FWK_SUCCESS;
135 }
136 
137 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
scmi_sys_power_state_notify(fwk_id_t service_id,uint32_t system_state,bool forceful)138 static void scmi_sys_power_state_notify(fwk_id_t service_id,
139     uint32_t system_state, bool forceful)
140 {
141     unsigned int agent_id, i;
142     fwk_id_t id;
143     struct scmi_sys_power_state_notifier return_values;
144     int status;
145 
146     status = scmi_sys_power_ctx.scmi_api->get_agent_id(service_id, &agent_id);
147     if (status != FWK_SUCCESS) {
148         return;
149     }
150 
151     return_values.agent_id = (uint32_t)agent_id;
152     return_values.system_state = system_state;
153     if (forceful) {
154         return_values.flags = 0;
155     } else {
156         return_values.flags = 1;
157     }
158 
159     for (i = 0u; i < scmi_sys_power_ctx.agent_count; i++) {
160         id =  scmi_sys_power_ctx.system_power_notifications[i];
161         if (fwk_id_is_equal(id, FWK_ID_NONE) ||
162             fwk_id_is_equal(id, service_id)) {
163             continue;
164         }
165 
166         scmi_sys_power_ctx.scmi_api->notify(id,
167             MOD_SCMI_PROTOCOL_ID_SYS_POWER, SCMI_SYS_POWER_STATE_SET_NOTIFY,
168             &return_values, sizeof(return_values));
169     }
170 }
171 #endif
172 
graceful_timer_callback(uintptr_t mod_scmi_system_state)173 static void graceful_timer_callback(uintptr_t mod_scmi_system_state)
174 {
175     enum mod_pd_system_shutdown system_shutdown;
176     int status;
177 
178     FWK_LOG_INFO("SCMI_SYS_POWER: Graceful request timeout...");
179     FWK_LOG_INFO(
180         "SCMI_SYS_POWER: Forcing SCMI SYSTEM STATE %d",
181         (int)mod_scmi_system_state);
182 
183     scmi_sys_power_ctx.start_graceful_process = false;
184 
185     if (mod_scmi_system_state == SCMI_SYSTEM_STATE_SHUTDOWN) {
186         system_shutdown = system_state2system_shutdown[mod_scmi_system_state];
187         status = scmi_sys_power_ctx.pd_api->system_shutdown(system_shutdown);
188         if (status != FWK_SUCCESS) {
189             FWK_LOG_DEBUG("SCMI_SYS_POWER: %s @%d", __func__, __LINE__);
190         }
191     }
192 }
193 
194 /*
195  * PROTOCOL_VERSION
196  */
scmi_sys_power_version_handler(fwk_id_t service_id,const uint32_t * payload)197 static int scmi_sys_power_version_handler(fwk_id_t service_id,
198                                           const uint32_t *payload)
199 {
200     struct scmi_protocol_version_p2a return_values = {
201         .status = (int32_t)SCMI_SUCCESS,
202         .version = SCMI_PROTOCOL_VERSION_SYS_POWER,
203     };
204 
205     return scmi_sys_power_ctx.scmi_api->respond(
206         service_id, &return_values, sizeof(return_values));
207 }
208 
209 /*
210  * PROTOCOL_ATTRIBUTES
211  */
scmi_sys_power_attributes_handler(fwk_id_t service_id,const uint32_t * payload)212 static int scmi_sys_power_attributes_handler(fwk_id_t service_id,
213                                              const uint32_t *payload)
214 {
215     struct scmi_protocol_attributes_p2a return_values = {
216         .status = (int32_t)SCMI_SUCCESS,
217         .attributes = 0,
218     };
219 
220     return scmi_sys_power_ctx.scmi_api->respond(
221         service_id, &return_values, sizeof(return_values));
222 }
223 
224 /*
225  * PROTOCOL_MESSAGE_ATTRIBUTES
226  */
scmi_sys_power_msg_attributes_handler(fwk_id_t service_id,const uint32_t * payload)227 static int scmi_sys_power_msg_attributes_handler(fwk_id_t service_id,
228                                                  const uint32_t *payload)
229 {
230     const struct scmi_protocol_message_attributes_a2p *parameters;
231     unsigned int message_id;
232     struct scmi_protocol_message_attributes_p2a return_values;
233 
234     parameters = (const struct scmi_protocol_message_attributes_a2p*)payload;
235     message_id = parameters->message_id;
236 
237 /*
238  * NOT_SUPPORTED must be returned for notifications cmds,
239  * when notifications are not supported.
240  */
241 #ifndef BUILD_HAS_SCMI_NOTIFICATIONS
242     if (message_id == MOD_SCMI_SYS_POWER_STATE_NOTIFY) {
243         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
244 
245         goto exit;
246     }
247 #endif
248 
249     if ((message_id >= FWK_ARRAY_SIZE(handler_table)) ||
250         (handler_table[message_id] == NULL)) {
251         return_values.status = (int32_t)SCMI_NOT_FOUND;
252         goto exit;
253     }
254 
255     return_values = (struct scmi_protocol_message_attributes_p2a) {
256         .status = SCMI_SUCCESS,
257         .attributes = 0,
258     };
259 
260     if (message_id == MOD_SCMI_SYS_POWER_STATE_SET) {
261         return_values.attributes |= SYS_POWER_STATE_SET_ATTRIBUTES_SUSPEND |
262                                     SYS_POWER_STATE_SET_ATTRIBUTES_WARM_RESET;
263     }
264 
265 exit:
266     return scmi_sys_power_ctx.scmi_api->respond(
267         service_id,
268         &return_values,
269         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
270                                                  sizeof(return_values.status));
271 }
272 
273 /*
274  * SYSTEM_POWER_STATE_SET
275  */
scmi_sys_power_state_set_handler(fwk_id_t service_id,const uint32_t * payload)276 static int scmi_sys_power_state_set_handler(fwk_id_t service_id,
277                                             const uint32_t *payload)
278 {
279     int status = FWK_SUCCESS;
280     int respond_status;
281     const struct scmi_sys_power_state_set_a2p *parameters;
282     struct scmi_sys_power_state_set_p2a return_values = {
283         .status = (int32_t)SCMI_GENERIC_ERROR,
284     };
285     unsigned int agent_id;
286     enum scmi_agent_type agent_type;
287     enum mod_pd_system_shutdown system_shutdown;
288     uint32_t mod_scmi_system_state;
289     enum mod_scmi_sys_power_policy_status policy_status;
290     enum scmi_system_state sys_state_type;
291 
292     parameters = (const struct scmi_sys_power_state_set_a2p *)payload;
293 
294     if (parameters->flags & (uint32_t)(~STATE_SET_FLAGS_MASK)) {
295         return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
296         goto exit;
297     }
298 
299     status = scmi_sys_power_ctx.scmi_api->get_agent_id(service_id, &agent_id);
300     if (status != FWK_SUCCESS) {
301         goto exit;
302     }
303 
304     status = scmi_sys_power_ctx.scmi_api->get_agent_type(agent_id, &agent_type);
305     if (status != FWK_SUCCESS) {
306         goto exit;
307     }
308 
309     if ((agent_type != SCMI_AGENT_TYPE_PSCI) &&
310         (agent_type != SCMI_AGENT_TYPE_MANAGEMENT)) {
311         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
312         goto exit;
313     }
314 
315     mod_scmi_system_state = parameters->system_state;
316 
317     /*
318      * Note that the mod_scmi_system_state value may be changed by the policy
319      * handler.
320      */
321     status = scmi_sys_power_state_set_policy(
322         &policy_status,
323         &mod_scmi_system_state,
324         service_id,
325         (parameters->flags & (uint32_t)STATE_SET_FLAGS_GRACEFUL_REQUEST) != 0);
326 
327     if (status != FWK_SUCCESS) {
328         return_values.status = (int32_t)SCMI_GENERIC_ERROR;
329         goto exit;
330     }
331     if (policy_status == MOD_SCMI_SYS_POWER_SKIP_MESSAGE_HANDLER) {
332         return_values.status = (int32_t)SCMI_SUCCESS;
333         goto exit;
334     }
335 
336     sys_state_type = (enum scmi_system_state)mod_scmi_system_state;
337 
338     switch (sys_state_type) {
339     case SCMI_SYSTEM_STATE_SHUTDOWN:
340     case SCMI_SYSTEM_STATE_COLD_RESET:
341     case SCMI_SYSTEM_STATE_WARM_RESET:
342         system_shutdown = system_state2system_shutdown[mod_scmi_system_state];
343         status = scmi_sys_power_ctx.pd_api->system_shutdown(system_shutdown);
344         if ((status == FWK_SUCCESS) || (status == FWK_PENDING)) {
345             status = FWK_SUCCESS;
346             return_values.status = (int32_t)SCMI_SUCCESS;
347         }
348         goto exit;
349         break;
350 
351     case SCMI_SYSTEM_STATE_SUSPEND:
352         status = scmi_sys_power_ctx.pd_api->system_suspend(
353             scmi_sys_power_ctx.config->system_suspend_state);
354         if (status != FWK_SUCCESS) {
355             if (status == FWK_E_STATE) {
356                 status = FWK_SUCCESS;
357                 return_values.status = (int32_t)SCMI_DENIED;
358             }
359             goto exit;
360         }
361         break;
362 
363     case SCMI_SYSTEM_STATE_POWER_UP:
364         if ((agent_type != SCMI_AGENT_TYPE_MANAGEMENT) ||
365             (scmi_sys_power_ctx.config->system_view !=
366              MOD_SCMI_SYSTEM_VIEW_OSPM)) {
367             return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
368             goto exit;
369         }
370 
371         status =
372             system_state_get((enum scmi_system_state *)&mod_scmi_system_state);
373         if (status != FWK_SUCCESS) {
374             goto exit;
375         }
376 
377         if ((mod_scmi_system_state != SCMI_SYSTEM_STATE_SHUTDOWN) &&
378             (mod_scmi_system_state != SCMI_SYSTEM_STATE_SUSPEND)) {
379             return_values.status = (int32_t)SCMI_DENIED;
380             goto exit;
381         }
382 
383         status = scmi_sys_power_ctx.pd_api->set_state(
384             scmi_sys_power_ctx.config->wakeup_power_domain_id,
385             false,
386             scmi_sys_power_ctx.config->wakeup_composite_state);
387         if (status != FWK_SUCCESS) {
388             goto exit;
389         }
390         break;
391 
392     default:
393         return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
394         goto exit;
395     };
396 
397 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
398     /*
399      * Only send notifications if this is a forceful request. For graceful
400      * requests the notifications are sent before executing the command.
401      */
402     if (((parameters->flags & STATE_SET_FLAGS_GRACEFUL_REQUEST) == 0U) ||
403         (mod_scmi_system_state != SCMI_SYSTEM_STATE_SHUTDOWN)) {
404         scmi_sys_power_state_notify(
405             service_id,
406             mod_scmi_system_state,
407             (parameters->flags & STATE_SET_FLAGS_GRACEFUL_REQUEST) != 0);
408     }
409 #endif
410 
411     return_values.status = (int32_t)SCMI_SUCCESS;
412 
413 exit:
414     respond_status = scmi_sys_power_ctx.scmi_api->respond(
415         service_id,
416         &return_values,
417         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
418                                                  sizeof(return_values.status));
419 
420     if (respond_status != FWK_SUCCESS) {
421         FWK_LOG_DEBUG("SCMI_SYS_POWER: %s @%d", __func__, __LINE__);
422     }
423 
424     return status;
425 }
426 
427 /*
428  * SYSTEM_POWER_STATE_GET
429  */
scmi_sys_power_state_get_handler(fwk_id_t service_id,const uint32_t * payload)430 static int scmi_sys_power_state_get_handler(fwk_id_t service_id,
431                                             const uint32_t *payload)
432 {
433     int status = FWK_SUCCESS;
434     int respond_status;
435     struct scmi_sys_power_state_get_p2a return_values = {
436         .status = (int32_t)SCMI_GENERIC_ERROR,
437     };
438     enum scmi_system_state system_state;
439     unsigned int agent_id;
440     enum scmi_agent_type agent_type;
441 
442     status = scmi_sys_power_ctx.scmi_api->get_agent_id(service_id, &agent_id);
443     if (status != FWK_SUCCESS) {
444         goto exit;
445     }
446 
447     status = scmi_sys_power_ctx.scmi_api->get_agent_type(agent_id, &agent_type);
448     if (status != FWK_SUCCESS) {
449         goto exit;
450     }
451 
452     return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
453     if (scmi_sys_power_ctx.config->system_view == MOD_SCMI_SYSTEM_VIEW_FULL) {
454         goto exit;
455     } else {
456         if (agent_type == SCMI_AGENT_TYPE_PSCI) {
457             goto exit;
458         }
459 
460         status = system_state_get(&system_state);
461         if (status != FWK_SUCCESS) {
462             goto exit;
463         }
464 
465         return_values = (struct scmi_sys_power_state_get_p2a) {
466             .status = SCMI_SUCCESS,
467             .system_state = system_state,
468         };
469     }
470 
471 exit:
472     respond_status = scmi_sys_power_ctx.scmi_api->respond(
473         service_id,
474         &return_values,
475         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
476                                                  sizeof(return_values.status));
477 
478     if (respond_status != FWK_SUCCESS) {
479         FWK_LOG_DEBUG("SCMI_SYS_POWER: %s @%d", __func__, __LINE__);
480     }
481 
482     return status;
483 }
484 
485 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
486 /*
487  * SYSTEM_POWER_STATE_NOTIFY
488  */
scmi_sys_power_state_notify_handler(fwk_id_t service_id,const uint32_t * payload)489 static int scmi_sys_power_state_notify_handler(fwk_id_t service_id,
490                                                const uint32_t *payload)
491 {
492     unsigned int agent_id;
493     const struct scmi_sys_power_state_notify_a2p *parameters;
494     struct scmi_sys_power_state_notify_p2a return_values = {
495         .status = (int32_t)SCMI_GENERIC_ERROR,
496     };
497     int status, respond_status;
498 
499     status = scmi_sys_power_ctx.scmi_api->get_agent_id(service_id, &agent_id);
500     if (status != FWK_SUCCESS) {
501         goto exit;
502     }
503 
504     parameters = (const struct scmi_sys_power_state_notify_a2p *)payload;
505 
506     if (parameters->flags & (~STATE_NOTIFY_FLAGS_MASK)) {
507         return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
508         goto exit;
509     }
510 
511     if (parameters->flags & STATE_NOTIFY_FLAGS_MASK) {
512         scmi_sys_power_ctx.system_power_notifications[agent_id] = service_id;
513     } else {
514         scmi_sys_power_ctx.system_power_notifications[agent_id] = FWK_ID_NONE;
515     }
516 
517     return_values.status = (int32_t)SCMI_SUCCESS;
518 
519 exit:
520     respond_status = scmi_sys_power_ctx.scmi_api->respond(
521         service_id,
522         &return_values,
523         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
524                                                  sizeof(return_values.status));
525 
526     if (respond_status != FWK_SUCCESS) {
527         FWK_LOG_DEBUG("SCMI_SYS_POWER: %s @%d", __func__, __LINE__);
528     }
529 
530     return FWK_SUCCESS;
531 }
532 #endif
533 
534 /*
535  * SCMI System Power Policy Handlers
536  *
537  * The system_state_set policy for forceful commands returns the same state.
538  *
539  * The system_state_set policy for graceful commands sends notifications if
540  * supported then starts a timer. When the timer expires the command will be
541  * executed. The current policy only supports the shutdown command.
542  *
543  * Platforms can override this policy by implementing this interface in the
544  * platform code.
545  */
scmi_sys_power_state_set_policy(enum mod_scmi_sys_power_policy_status * policy_status,uint32_t * state,fwk_id_t service_id,bool graceful)546 FWK_WEAK int scmi_sys_power_state_set_policy(
547     enum mod_scmi_sys_power_policy_status *policy_status,
548     uint32_t *state,
549     fwk_id_t service_id,
550     bool graceful)
551 {
552     int status;
553 
554     *policy_status = MOD_SCMI_SYS_POWER_EXECUTE_MESSAGE_HANDLER;
555 
556     if (graceful) {
557         *policy_status = MOD_SCMI_SYS_POWER_SKIP_MESSAGE_HANDLER;
558 
559         if (!scmi_sys_power_ctx.start_graceful_process) {
560             /* Only shutdown command is enabled */
561             if (*state != SCMI_SYSTEM_STATE_SHUTDOWN) {
562                 return FWK_SUCCESS;
563             }
564             /* Starting the graceful request process */
565             scmi_sys_power_ctx.start_graceful_process = true;
566 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
567             scmi_sys_power_state_notify(service_id, *state, false);
568 #endif
569 
570             if (scmi_sys_power_ctx.alarm_api != NULL) {
571                 status = scmi_sys_power_ctx.alarm_api->start(
572                     scmi_sys_power_ctx.config->alarm_id,
573                     scmi_sys_power_ctx.config->graceful_timeout,
574                     MOD_TIMER_ALARM_TYPE_ONCE,
575                     graceful_timer_callback,
576                     *state);
577                 if (status != FWK_SUCCESS) {
578                     FWK_LOG_DEBUG("SCMI_SYS_POWER: %s @%d", __func__, __LINE__);
579                 }
580             } else {
581                 graceful_timer_callback(*state);
582             }
583         }
584     }
585 
586     return FWK_SUCCESS;
587 }
588 
589 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
590 
591 /*
592  * SCMI Resource Permissions handler
593  */
scmi_sys_power_permissions_handler(fwk_id_t service_id,const uint32_t * payload,size_t payload_size,unsigned int message_id)594 static int scmi_sys_power_permissions_handler(
595     fwk_id_t service_id,
596     const uint32_t *payload,
597     size_t payload_size,
598     unsigned int message_id)
599 {
600     enum mod_res_perms_permissions perms;
601     unsigned int agent_id;
602     int status;
603 
604     status = scmi_sys_power_ctx.scmi_api->get_agent_id(service_id, &agent_id);
605     if (status != FWK_SUCCESS) {
606         return FWK_E_ACCESS;
607     }
608 
609     if (message_id < 3) {
610         return FWK_SUCCESS;
611     }
612 
613     /*
614      * We check that the agent has permssions to access the command
615      */
616     perms = scmi_sys_power_ctx.res_perms_api->agent_has_message_permission(
617         agent_id, MOD_SCMI_PROTOCOL_ID_BASE, message_id);
618 
619     if (perms == MOD_RES_PERMS_ACCESS_ALLOWED) {
620         return FWK_SUCCESS;
621     } else {
622         return FWK_E_ACCESS;
623     }
624 }
625 
626 #endif
627 
628 /*
629  * SCMI module -> SCMI system power module interface
630  */
scmi_sys_power_get_scmi_protocol_id(fwk_id_t protocol_id,uint8_t * scmi_protocol_id)631 static int scmi_sys_power_get_scmi_protocol_id(fwk_id_t protocol_id,
632                                                uint8_t *scmi_protocol_id)
633 {
634     *scmi_protocol_id = (uint8_t)MOD_SCMI_PROTOCOL_ID_SYS_POWER;
635 
636     return FWK_SUCCESS;
637 }
638 
scmi_sys_power_handler(fwk_id_t protocol_id,fwk_id_t service_id,const uint32_t * payload,size_t payload_size,unsigned int message_id)639 static int scmi_sys_power_handler(fwk_id_t protocol_id,
640                                   fwk_id_t service_id,
641                                   const uint32_t *payload,
642                                   size_t payload_size,
643                                   unsigned int message_id)
644 {
645     int32_t return_value;
646 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
647     int status;
648 #endif
649 
650     static_assert(FWK_ARRAY_SIZE(handler_table) ==
651                   FWK_ARRAY_SIZE(payload_size_table),
652                   "[SCMI] System power protocol table sizes not consistent");
653 
654     fwk_assert(payload != NULL);
655 
656     if (message_id >= FWK_ARRAY_SIZE(handler_table)) {
657         return_value = (int32_t)SCMI_NOT_FOUND;
658         goto error;
659     }
660 
661     if (payload_size != payload_size_table[message_id]) {
662         /* Incorrect payload size or message is not supported */
663         return_value = (int32_t)SCMI_PROTOCOL_ERROR;
664         goto error;
665     }
666 
667 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
668     status = scmi_sys_power_permissions_handler(
669         service_id, payload, payload_size, message_id);
670     if (status != FWK_SUCCESS) {
671         return_value = (int32_t)SCMI_DENIED;
672         goto error;
673     }
674 #endif
675 
676     return handler_table[message_id](service_id, payload);
677 
678 error:
679     return scmi_sys_power_ctx.scmi_api->respond(
680         service_id, &return_value, sizeof(return_value));
681 }
682 
683 static struct mod_scmi_to_protocol_api scmi_sys_power_mod_scmi_to_protocol = {
684     .get_scmi_protocol_id = scmi_sys_power_get_scmi_protocol_id,
685     .message_handler = scmi_sys_power_handler,
686 };
687 
688 /*
689  * Framework handlers
690  */
scmi_sys_power_init(fwk_id_t module_id,unsigned int element_count,const void * data)691 static int scmi_sys_power_init(fwk_id_t module_id, unsigned int element_count,
692                                const void *data)
693 {
694     scmi_sys_power_ctx.config = data;
695     scmi_sys_power_ctx.start_graceful_process = false;
696 
697     return FWK_SUCCESS;
698 }
699 
700 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
scmi_sys_power_init_notifications(void)701 static int scmi_sys_power_init_notifications(void)
702 {
703     int status;
704     unsigned int i;
705 
706     status = scmi_sys_power_ctx.scmi_api->get_agent_count(
707         &scmi_sys_power_ctx.agent_count);
708     if (status != FWK_SUCCESS) {
709         return status;
710     }
711     fwk_assert(scmi_sys_power_ctx.agent_count != 0u);
712 
713     scmi_sys_power_ctx.system_power_notifications =
714         fwk_mm_calloc((size_t)scmi_sys_power_ctx.agent_count, sizeof(fwk_id_t));
715 
716     for (i = 0u; i < scmi_sys_power_ctx.agent_count; i++) {
717         scmi_sys_power_ctx.system_power_notifications[i] = FWK_ID_NONE;
718     }
719 
720     return FWK_SUCCESS;
721 
722 }
723 #endif
724 
scmi_sys_power_bind(fwk_id_t id,unsigned int round)725 static int scmi_sys_power_bind(fwk_id_t id, unsigned int round)
726 {
727     int status;
728     int pd_count;
729 
730     if (round != 0) {
731         return FWK_SUCCESS;
732     }
733 
734     /* Bind to SCMI module */
735     status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
736                              FWK_ID_API(FWK_MODULE_IDX_SCMI,
737                                         MOD_SCMI_API_IDX_PROTOCOL),
738                              &scmi_sys_power_ctx.scmi_api);
739     if (status != FWK_SUCCESS) {
740         return status;
741     }
742 
743 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
744     status = fwk_module_bind(
745         FWK_ID_MODULE(FWK_MODULE_IDX_RESOURCE_PERMS),
746         FWK_ID_API(FWK_MODULE_IDX_RESOURCE_PERMS, MOD_RES_PERM_RESOURCE_PERMS),
747         &scmi_sys_power_ctx.res_perms_api);
748     if (status != FWK_SUCCESS) {
749         return status;
750     }
751 #endif
752 
753     /* Bind to POWER DOMAIN module */
754     status = fwk_module_bind(fwk_module_id_power_domain,
755         mod_pd_api_id_restricted, &scmi_sys_power_ctx.pd_api);
756     if (status != FWK_SUCCESS) {
757         return status;
758     }
759 
760     pd_count = fwk_module_get_element_count(fwk_module_id_power_domain);
761     if (pd_count <= 0) {
762         return FWK_E_SUPPORT;
763     }
764 
765     scmi_sys_power_ctx.system_power_domain_id =
766         FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, pd_count - 1);
767 
768 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
769     /*
770      * If notifications is supported a timer is required to handle graceful
771      * requests.
772      */
773     if (!fwk_id_is_equal(scmi_sys_power_ctx.config->alarm_id, FWK_ID_NONE)) {
774         status = fwk_module_bind(
775             scmi_sys_power_ctx.config->alarm_id,
776             MOD_TIMER_API_ID_ALARM,
777             &scmi_sys_power_ctx.alarm_api);
778         if (status != FWK_SUCCESS) {
779             return status;
780         }
781         return scmi_sys_power_init_notifications();
782     }
783 #endif
784     return FWK_SUCCESS;
785 }
786 
scmi_sys_power_process_bind_request(fwk_id_t source_id,fwk_id_t _target_id,fwk_id_t api_id,const void ** api)787 static int scmi_sys_power_process_bind_request(fwk_id_t source_id,
788     fwk_id_t _target_id, fwk_id_t api_id, const void **api)
789 {
790     if (!fwk_id_is_equal(source_id, FWK_ID_MODULE(FWK_MODULE_IDX_SCMI))) {
791         return FWK_E_ACCESS;
792     }
793 
794     *api = &scmi_sys_power_mod_scmi_to_protocol;
795 
796     return FWK_SUCCESS;
797 }
798 
799 const struct fwk_module module_scmi_system_power = {
800     .api_count = 1,
801     .type = FWK_MODULE_TYPE_PROTOCOL,
802     .init = scmi_sys_power_init,
803     .bind = scmi_sys_power_bind,
804     .process_bind_request = scmi_sys_power_process_bind_request,
805 };
806