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