1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * SCMI performance protocol/commands handling support.
9 */
10
11 #include <internal/scmi_perf.h>
12
13 #include <mod_dvfs.h>
14 #include <mod_scmi.h>
15 #include <mod_scmi_perf.h>
16
17 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
18 # include "perf_plugins_handler.h"
19 #endif
20
21 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
22 # include <mod_resource_perms.h>
23 #endif
24
25 #ifdef BUILD_HAS_MOD_STATISTICS
26 # include <mod_stats.h>
27 #endif
28
29 #include <fwk_core.h>
30 #include <fwk_event.h>
31 #include <fwk_id.h>
32 #include <fwk_log.h>
33 #include <fwk_mm.h>
34 #include <fwk_module.h>
35 #include <fwk_module_idx.h>
36 #include <fwk_status.h>
37 #include <fwk_string.h>
38
39 static const fwk_id_t scmi_perf_get_level = FWK_ID_EVENT_INIT(
40 FWK_MODULE_IDX_SCMI_PERF,
41 SCMI_PERF_EVENT_IDX_LEVEL_GET_REQUEST);
42
43 struct scmi_perf_event_parameters {
44 fwk_id_t domain_id;
45 };
46
47 #define MOD_SCMI_PERF_NOTIFICATION_COUNT 2
48
49 static int scmi_perf_protocol_version_handler(
50 fwk_id_t service_id,
51 const uint32_t *payload);
52 static int scmi_perf_protocol_attributes_handler(
53 fwk_id_t service_id,
54 const uint32_t *payload);
55 static int scmi_perf_protocol_message_attributes_handler(
56 fwk_id_t service_id,
57 const uint32_t *payload);
58 static int scmi_perf_domain_attributes_handler(
59 fwk_id_t service_id,
60 const uint32_t *payload);
61 static int scmi_perf_describe_levels_handler(
62 fwk_id_t service_id,
63 const uint32_t *payload);
64 static int scmi_perf_level_set_handler(
65 fwk_id_t service_id,
66 const uint32_t *payload);
67 static int scmi_perf_level_get_handler(
68 fwk_id_t service_id,
69 const uint32_t *payload);
70 static int scmi_perf_limits_set_handler(
71 fwk_id_t service_id,
72 const uint32_t *payload);
73 static int scmi_perf_limits_get_handler(
74 fwk_id_t service_id,
75 const uint32_t *payload);
76 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
77 static int scmi_perf_describe_fast_channels(
78 fwk_id_t service_id,
79 const uint32_t *payload);
80 #endif
81 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
82 static int scmi_perf_limits_notify(
83 fwk_id_t service_id,
84 const uint32_t *payload);
85 static int scmi_perf_level_notify(fwk_id_t service_id, const uint32_t *payload);
86 #endif
87
88 static handler_table_t handler_table[MOD_SCMI_PERF_COMMAND_COUNT] = {
89 [MOD_SCMI_PROTOCOL_VERSION] = scmi_perf_protocol_version_handler,
90 [MOD_SCMI_PROTOCOL_ATTRIBUTES] = scmi_perf_protocol_attributes_handler,
91 [MOD_SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
92 scmi_perf_protocol_message_attributes_handler,
93 [MOD_SCMI_PERF_DOMAIN_ATTRIBUTES] = scmi_perf_domain_attributes_handler,
94 [MOD_SCMI_PERF_DESCRIBE_LEVELS] = scmi_perf_describe_levels_handler,
95 [MOD_SCMI_PERF_LIMITS_SET] = scmi_perf_limits_set_handler,
96 [MOD_SCMI_PERF_LIMITS_GET] = scmi_perf_limits_get_handler,
97 [MOD_SCMI_PERF_LEVEL_SET] = scmi_perf_level_set_handler,
98 [MOD_SCMI_PERF_LEVEL_GET] = scmi_perf_level_get_handler,
99 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
100 [MOD_SCMI_PERF_DESCRIBE_FAST_CHANNEL] = scmi_perf_describe_fast_channels,
101 #endif
102 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
103 [MOD_SCMI_PERF_NOTIFY_LIMITS] = scmi_perf_limits_notify,
104 [MOD_SCMI_PERF_NOTIFY_LEVEL] = scmi_perf_level_notify
105 #endif
106 };
107
108 static size_t payload_size_table[MOD_SCMI_PERF_COMMAND_COUNT] = {
109 [MOD_SCMI_PROTOCOL_VERSION] = 0,
110 [MOD_SCMI_PROTOCOL_ATTRIBUTES] = 0,
111 [MOD_SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
112 (unsigned int)sizeof(struct scmi_protocol_message_attributes_a2p),
113 [MOD_SCMI_PERF_DOMAIN_ATTRIBUTES] =
114 (unsigned int)sizeof(struct scmi_perf_domain_attributes_a2p),
115 [MOD_SCMI_PERF_DESCRIBE_LEVELS] =
116 (unsigned int)sizeof(struct scmi_perf_describe_levels_a2p),
117 [MOD_SCMI_PERF_LEVEL_SET] =
118 (unsigned int)sizeof(struct scmi_perf_level_set_a2p),
119 [MOD_SCMI_PERF_LEVEL_GET] =
120 (unsigned int)sizeof(struct scmi_perf_level_get_a2p),
121 [MOD_SCMI_PERF_LIMITS_SET] =
122 (unsigned int)sizeof(struct scmi_perf_limits_set_a2p),
123 [MOD_SCMI_PERF_LIMITS_GET] =
124 (unsigned int)sizeof(struct scmi_perf_limits_get_a2p),
125 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
126 [MOD_SCMI_PERF_DESCRIBE_FAST_CHANNEL] =
127 sizeof(struct scmi_perf_describe_fc_a2p),
128 #endif
129 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
130 [MOD_SCMI_PERF_NOTIFY_LIMITS] = sizeof(struct scmi_perf_notify_limits_a2p),
131 [MOD_SCMI_PERF_NOTIFY_LEVEL] = sizeof(struct scmi_perf_notify_level_a2p)
132 #endif
133 };
134
135 static struct scmi_perf_protocol_ctx {
136 struct mod_scmi_perf_ctx *scmi_perf_ctx;
137
138 struct mod_scmi_perf_private_api_perf_stub *api_stub;
139
140 /* Pointer to a table of operations */
141 struct perf_operations *perf_ops_table;
142
143 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
144 /* Number of active agents */
145 unsigned int agent_count;
146
147 /* SCMI notification API */
148 const struct mod_scmi_notification_api *scmi_notification_api;
149 #endif
150
151 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
152 /* SCMI Resource Permissions API */
153 const struct mod_res_permissions_api *res_perms_api;
154 #endif
155
156 #ifdef BUILD_HAS_MOD_STATISTICS
157 /* Statistics module API */
158 const struct mod_stats_api *stats_api;
159 #endif
160
161 } perf_prot_ctx;
162
163 /* This identifier is either:
164 * - the element type version of the one built by the perf-plugins-handler
165 * (sub-element type)
166 * - or the DVFS domain
167 */
get_dvfs_dependency_id(unsigned int el_idx)168 static inline fwk_id_t get_dvfs_dependency_id(unsigned int el_idx)
169 {
170 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
171 fwk_id_t id;
172
173 id = perf_plugins_get_dependency_id(el_idx);
174 return fwk_id_build_element_id(
175 fwk_module_id_dvfs, fwk_id_get_element_idx(id));
176 #else
177 return FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, el_idx);
178 #endif
179 }
180
181 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
182
183 /*
184 * SCMI Resource Permissions handler
185 */
get_perf_domain_id(const uint32_t * payload,unsigned int * domain_id)186 static int get_perf_domain_id(const uint32_t *payload, unsigned int *domain_id)
187 {
188 /*
189 * Every SCMI Performance message is formatted with the domain ID
190 * as the first message element. We will use the perf_limits_get
191 * message as a basic format to retrieve the domain ID to avoid
192 * unnecessary code.
193 */
194 const struct scmi_perf_limits_get_a2p *parameters =
195 (const struct scmi_perf_limits_get_a2p *)payload;
196
197 if (parameters->domain_id >= perf_prot_ctx.scmi_perf_ctx->domain_count) {
198 return FWK_E_PARAM;
199 }
200
201 *domain_id = parameters->domain_id;
202 return FWK_SUCCESS;
203 }
204
scmi_perf_permissions_handler(fwk_id_t service_id,const uint32_t * payload,unsigned int message_id)205 static int scmi_perf_permissions_handler(
206 fwk_id_t service_id,
207 const uint32_t *payload,
208 unsigned int message_id)
209 {
210 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
211 enum mod_res_perms_permissions perms;
212 unsigned int agent_id, domain_id;
213 int status;
214
215 status = scmi_perf_ctx->scmi_api->get_agent_id(service_id, &agent_id);
216 if (status != FWK_SUCCESS) {
217 return FWK_E_ACCESS;
218 }
219
220 status = get_perf_domain_id(payload, &domain_id);
221 if (status != FWK_SUCCESS) {
222 return FWK_E_PARAM;
223 }
224
225 perms = perf_prot_ctx.res_perms_api->agent_has_resource_permission(
226 agent_id, MOD_SCMI_PROTOCOL_ID_PERF, message_id, domain_id);
227
228 if (perms == MOD_RES_PERMS_ACCESS_ALLOWED) {
229 return FWK_SUCCESS;
230 } else {
231 return FWK_E_ACCESS;
232 }
233 }
234
235 #endif
236
237 /*
238 * Protocol command handlers
239 */
240
scmi_perf_protocol_version_handler(fwk_id_t service_id,const uint32_t * payload)241 static int scmi_perf_protocol_version_handler(
242 fwk_id_t service_id,
243 const uint32_t *payload)
244 {
245 struct scmi_protocol_version_p2a return_values = {
246 .status = (int32_t)SCMI_SUCCESS,
247 .version = SCMI_PROTOCOL_VERSION_PERF,
248 };
249
250 return perf_prot_ctx.scmi_perf_ctx->scmi_api->respond(
251 service_id, &return_values, sizeof(return_values));
252 }
253
scmi_perf_protocol_attributes_handler(fwk_id_t service_id,const uint32_t * payload)254 static int scmi_perf_protocol_attributes_handler(
255 fwk_id_t service_id,
256 const uint32_t *payload)
257 {
258 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
259
260 #ifdef BUILD_HAS_MOD_STATISTICS
261 int status;
262 #endif
263 struct scmi_perf_protocol_attributes_p2a return_values = {
264 .status = (int32_t)SCMI_SUCCESS,
265 .attributes =
266 SCMI_PERF_PROTOCOL_ATTRIBUTES(true, scmi_perf_ctx->domain_count),
267 };
268 uint32_t addr_low = 0, addr_high = 0, len = 0;
269
270 #ifdef BUILD_HAS_MOD_STATISTICS
271 status = perf_prot_ctx.stats_api->get_statistics_desc(
272 fwk_module_id_scmi_perf, &addr_low, &addr_high, &len);
273 if (status != FWK_SUCCESS) {
274 return_values.status = (int32_t)SCMI_GENERIC_ERROR;
275 }
276 #endif
277
278 return_values.statistics_len = len;
279 return_values.statistics_address_low = addr_low;
280 return_values.statistics_address_high = addr_high;
281
282 return scmi_perf_ctx->scmi_api->respond(
283 service_id, &return_values, sizeof(return_values));
284 }
285
scmi_perf_protocol_message_attributes_handler(fwk_id_t service_id,const uint32_t * payload)286 static int scmi_perf_protocol_message_attributes_handler(
287 fwk_id_t service_id,
288 const uint32_t *payload)
289 {
290 const struct scmi_protocol_message_attributes_a2p *parameters;
291 struct scmi_protocol_message_attributes_p2a return_values;
292
293 parameters = (const struct scmi_protocol_message_attributes_a2p *)payload;
294
295 if ((parameters->message_id < FWK_ARRAY_SIZE(handler_table)) &&
296 (handler_table[parameters->message_id] != NULL)) {
297 return_values = (struct scmi_protocol_message_attributes_p2a){
298 .status = SCMI_SUCCESS,
299 };
300 } else {
301 return_values.status = (int32_t)SCMI_NOT_FOUND;
302 }
303
304 return_values.attributes = 0;
305 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
306 if (perf_fch_prot_msg_attributes_has_fastchannels(parameters)) {
307 return_values.attributes = 1; /* Fast Channel available */
308 }
309 #endif
310
311 return perf_prot_ctx.scmi_perf_ctx->scmi_api->respond(
312 service_id,
313 &return_values,
314 (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
315 sizeof(return_values.status));
316 }
317
scmi_perf_domain_attributes_handler(fwk_id_t service_id,const uint32_t * payload)318 static int scmi_perf_domain_attributes_handler(
319 fwk_id_t service_id,
320 const uint32_t *payload)
321 {
322 int status, respond_status;
323 unsigned int agent_id;
324 const struct scmi_perf_domain_attributes_a2p *parameters;
325 uint32_t permissions = 0;
326 fwk_id_t domain_id;
327 struct mod_dvfs_opp opp;
328 struct scmi_perf_domain_attributes_p2a return_values = {
329 .status = (int32_t)SCMI_GENERIC_ERROR,
330 };
331 bool notifications = false;
332 bool fast_channels = false;
333 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
334
335 parameters = (const struct scmi_perf_domain_attributes_a2p *)payload;
336
337 /* Validate the domain identifier */
338 if (parameters->domain_id >= scmi_perf_ctx->domain_count) {
339 status = FWK_SUCCESS;
340 return_values.status = (int32_t)SCMI_NOT_FOUND;
341
342 goto exit;
343 }
344
345 status = scmi_perf_ctx->scmi_api->get_agent_id(service_id, &agent_id);
346 if (status != FWK_SUCCESS) {
347 goto exit;
348 }
349
350 #ifndef BUILD_HAS_MOD_RESOURCE_PERMS
351 permissions = ((uint32_t)MOD_SCMI_PERF_PERMS_SET_LIMITS) |
352 ((uint32_t)MOD_SCMI_PERF_PERMS_SET_LEVEL);
353 #else
354 status = scmi_perf_permissions_handler(
355 service_id, payload, (unsigned int)MOD_SCMI_PERF_LIMITS_SET);
356 if (status == FWK_SUCCESS) {
357 permissions = (uint8_t)MOD_SCMI_PERF_PERMS_SET_LIMITS;
358 }
359 status = scmi_perf_permissions_handler(
360 service_id, payload, (unsigned int)MOD_SCMI_PERF_LEVEL_SET);
361 if (status == FWK_SUCCESS) {
362 permissions |= (uint32_t)MOD_SCMI_PERF_PERMS_SET_LEVEL;
363 }
364 #endif
365
366 domain_id = get_dvfs_dependency_id(parameters->domain_id);
367 status = scmi_perf_ctx->dvfs_api->get_sustained_opp(domain_id, &opp);
368 if (status != FWK_SUCCESS) {
369 goto exit;
370 }
371
372 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
373 notifications = true;
374 #endif
375 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
376 fast_channels = perf_fch_domain_has_fastchannels(parameters->domain_id);
377 #endif
378 return_values = (struct scmi_perf_domain_attributes_p2a){
379 .status = SCMI_SUCCESS,
380 .attributes = SCMI_PERF_DOMAIN_ATTRIBUTES(
381 (uint32_t)notifications,
382 (uint32_t)notifications,
383 ((permissions & (uint32_t)MOD_SCMI_PERF_PERMS_SET_LEVEL) !=
384 (uint32_t)0) ?
385 1U :
386 0U,
387 ((permissions & (uint32_t)MOD_SCMI_PERF_PERMS_SET_LIMITS) !=
388 (uint32_t)0) ?
389 1U :
390 0U,
391 (uint32_t)fast_channels),
392 .rate_limit = 0, /* Unsupported */
393 .sustained_freq = opp.frequency,
394 .sustained_perf_level = opp.level,
395 };
396
397 /* Copy the domain name into the mailbox */
398 fwk_str_strncpy(
399 (char *)return_values.name,
400 fwk_module_get_element_name(fwk_id_build_element_id(
401 domain_id, fwk_id_get_element_idx(domain_id))),
402 sizeof(return_values.name) - 1);
403
404 exit:
405 respond_status = scmi_perf_ctx->scmi_api->respond(
406 service_id,
407 &return_values,
408 (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
409 sizeof(return_values.status));
410
411 if (respond_status != FWK_SUCCESS) {
412 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
413 }
414
415 return status;
416 }
417
scmi_perf_describe_levels_handler(fwk_id_t service_id,const uint32_t * payload)418 static int scmi_perf_describe_levels_handler(
419 fwk_id_t service_id,
420 const uint32_t *payload)
421 {
422 int status, respond_status;
423 size_t max_payload_size;
424 const struct scmi_perf_describe_levels_a2p *parameters;
425 fwk_id_t domain_id;
426 struct scmi_perf_level perf_level;
427 unsigned int num_levels, level_index, level_index_max;
428 size_t payload_size;
429 size_t opp_count;
430 struct mod_dvfs_opp opp;
431 uint16_t latency;
432 struct scmi_perf_describe_levels_p2a return_values = {
433 .status = (int32_t)SCMI_GENERIC_ERROR,
434 };
435 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
436
437 payload_size = sizeof(return_values);
438
439 status = scmi_perf_ctx->scmi_api->get_max_payload_size(
440 service_id, &max_payload_size);
441 if (status != FWK_SUCCESS) {
442 goto exit;
443 }
444
445 status =
446 (SCMI_PERF_LEVELS_MAX(max_payload_size) > 0) ? FWK_SUCCESS : FWK_E_SIZE;
447 if (status != FWK_SUCCESS) {
448 goto exit;
449 }
450
451 parameters = (const struct scmi_perf_describe_levels_a2p *)payload;
452
453 /* Validate the domain identifier */
454 if (parameters->domain_id >= scmi_perf_ctx->domain_count) {
455 return_values.status = (int32_t)SCMI_NOT_FOUND;
456
457 goto exit;
458 }
459
460 /* Get the number of operating points for the domain */
461 domain_id = get_dependency_id(parameters->domain_id);
462 status = scmi_perf_ctx->dvfs_api->get_opp_count(domain_id, &opp_count);
463 if (status != FWK_SUCCESS) {
464 goto exit;
465 }
466
467 /* Validate level index */
468 level_index = parameters->level_index;
469 if (level_index >= opp_count) {
470 return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
471
472 goto exit;
473 }
474
475 /* Identify the maximum number of performance levels we can send at once */
476 if (SCMI_PERF_LEVELS_MAX(max_payload_size) < (opp_count - level_index)) {
477 num_levels = (unsigned int)SCMI_PERF_LEVELS_MAX(max_payload_size);
478 } else {
479 num_levels = (unsigned int)(opp_count - level_index);
480 }
481
482 level_index_max = (level_index + num_levels - 1);
483
484 status = scmi_perf_ctx->dvfs_api->get_latency(domain_id, &latency);
485 if (status != FWK_SUCCESS) {
486 goto exit;
487 }
488
489 /* Copy DVFS data into returned data structure */
490 for (; level_index <= level_index_max;
491 level_index++, payload_size += sizeof(perf_level)) {
492 status =
493 scmi_perf_ctx->dvfs_api->get_nth_opp(domain_id, level_index, &opp);
494 if (status != FWK_SUCCESS) {
495 goto exit;
496 }
497
498 if (opp.power != 0) {
499 perf_level.power_cost = opp.power;
500 } else {
501 perf_level.power_cost = opp.voltage;
502 }
503 perf_level.performance_level = opp.level;
504 perf_level.attributes = latency;
505
506 status = scmi_perf_ctx->scmi_api->write_payload(
507 service_id, payload_size, &perf_level, sizeof(perf_level));
508 if (status != FWK_SUCCESS) {
509 goto exit;
510 }
511 }
512
513 return_values = (struct scmi_perf_describe_levels_p2a){
514 .status = SCMI_SUCCESS,
515 .num_levels =
516 SCMI_PERF_NUM_LEVELS(num_levels, (opp_count - level_index_max - 1))
517 };
518
519 status = scmi_perf_ctx->scmi_api->write_payload(
520 service_id, 0, &return_values, sizeof(return_values));
521
522 exit:
523 respond_status = scmi_perf_ctx->scmi_api->respond(
524 service_id,
525 (return_values.status == SCMI_SUCCESS) ? NULL : &return_values.status,
526 (return_values.status == SCMI_SUCCESS) ? payload_size :
527 sizeof(return_values.status));
528 if (respond_status != FWK_SUCCESS) {
529 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
530 }
531
532 return status;
533 }
534
scmi_perf_limits_set_handler(fwk_id_t service_id,const uint32_t * payload)535 static int scmi_perf_limits_set_handler(
536 fwk_id_t service_id,
537 const uint32_t *payload)
538 {
539 int status, respond_status;
540 unsigned int agent_id;
541 const struct scmi_perf_limits_set_a2p *parameters;
542 uint32_t range_min, range_max;
543 fwk_id_t domain_id;
544 struct scmi_perf_limits_set_p2a return_values = {
545 .status = (int32_t)SCMI_GENERIC_ERROR,
546 };
547 enum mod_scmi_perf_policy_status policy_status;
548 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
549
550 parameters = (const struct scmi_perf_limits_set_a2p *)payload;
551
552 if (parameters->domain_id >= scmi_perf_ctx->domain_count) {
553 status = FWK_SUCCESS;
554 return_values.status = (int32_t)SCMI_NOT_FOUND;
555
556 goto exit;
557 }
558
559 status = scmi_perf_ctx->scmi_api->get_agent_id(service_id, &agent_id);
560 if (status != FWK_SUCCESS) {
561 goto exit;
562 }
563
564 if (parameters->range_min > parameters->range_max) {
565 return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
566 goto exit;
567 }
568
569 domain_id = get_dependency_id(parameters->domain_id);
570 range_min = parameters->range_min;
571 range_max = parameters->range_max;
572
573 status = scmi_perf_limits_set_policy(
574 &policy_status, &range_min, &range_max, agent_id, domain_id);
575
576 if (status != FWK_SUCCESS) {
577 return_values.status = (int32_t)SCMI_GENERIC_ERROR;
578 goto exit;
579 }
580 if (policy_status == MOD_SCMI_PERF_SKIP_MESSAGE_HANDLER) {
581 return_values.status = (int32_t)SCMI_SUCCESS;
582 goto exit;
583 }
584
585 status = perf_prot_ctx.api_stub->perf_set_limits(
586 domain_id,
587 agent_id,
588 &((struct mod_scmi_perf_level_limits){ .minimum = range_min,
589 .maximum = range_max }));
590
591 /*
592 * Return immediately to the caller, fire-and-forget.
593 */
594 if ((status == FWK_SUCCESS) || (status == FWK_PENDING)) {
595 return_values.status = (int32_t)SCMI_SUCCESS;
596 } else {
597 return_values.status = (int32_t)SCMI_OUT_OF_RANGE;
598 }
599
600 exit:
601 respond_status = scmi_perf_ctx->scmi_api->respond(
602 service_id,
603 &return_values,
604 (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
605 sizeof(return_values.status));
606 if (respond_status != FWK_SUCCESS) {
607 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
608 }
609
610 return status;
611 }
612
scmi_perf_limits_get_handler(fwk_id_t service_id,const uint32_t * payload)613 static int scmi_perf_limits_get_handler(
614 fwk_id_t service_id,
615 const uint32_t *payload)
616 {
617 fwk_id_t domain_id;
618 const struct scmi_perf_limits_get_a2p *parameters;
619 struct scmi_perf_limits_get_p2a return_values;
620 struct scmi_perf_domain_ctx *domain_ctx;
621
622 parameters = (const struct scmi_perf_limits_get_a2p *)payload;
623 if (parameters->domain_id >= perf_prot_ctx.scmi_perf_ctx->domain_count) {
624 return_values.status = (int32_t)SCMI_NOT_FOUND;
625
626 goto exit;
627 }
628
629 domain_id = get_dependency_id(parameters->domain_id);
630 domain_ctx = &perf_prot_ctx.scmi_perf_ctx
631 ->domain_ctx_table[fwk_id_get_element_idx(domain_id)];
632
633 return_values.status = (int32_t)SCMI_SUCCESS;
634 return_values.range_min = domain_ctx->level_limits.minimum;
635 return_values.range_max = domain_ctx->level_limits.maximum;
636
637 exit:
638 return perf_prot_ctx.scmi_perf_ctx->scmi_api->respond(
639 service_id,
640 &return_values,
641 (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
642 sizeof(return_values.status));
643 }
644
scmi_perf_level_set_handler(fwk_id_t service_id,const uint32_t * payload)645 static int scmi_perf_level_set_handler(
646 fwk_id_t service_id,
647 const uint32_t *payload)
648 {
649 int status, respond_status;
650 unsigned int agent_id;
651 const struct scmi_perf_level_set_a2p *parameters;
652 fwk_id_t domain_id;
653 struct scmi_perf_level_set_p2a return_values = {
654 .status = (int32_t)SCMI_GENERIC_ERROR,
655 };
656 uint32_t perf_level;
657 enum mod_scmi_perf_policy_status policy_status;
658 parameters = (const struct scmi_perf_level_set_a2p *)payload;
659 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
660
661 if (parameters->domain_id >= scmi_perf_ctx->domain_count) {
662 status = FWK_SUCCESS;
663 return_values.status = (int32_t)SCMI_NOT_FOUND;
664
665 goto exit;
666 }
667
668 status = scmi_perf_ctx->scmi_api->get_agent_id(service_id, &agent_id);
669 if (status != FWK_SUCCESS) {
670 goto exit;
671 }
672
673 /*
674 * Note that the policy handler may change the performance level
675 */
676 domain_id = get_dependency_id(parameters->domain_id);
677 perf_level = parameters->performance_level;
678
679 status = scmi_perf_level_set_policy(
680 &policy_status, &perf_level, agent_id, domain_id);
681
682 if (status != FWK_SUCCESS) {
683 return_values.status = (int32_t)SCMI_GENERIC_ERROR;
684 goto exit;
685 }
686 if (policy_status == MOD_SCMI_PERF_SKIP_MESSAGE_HANDLER) {
687 return_values.status = (int32_t)SCMI_SUCCESS;
688 goto exit;
689 }
690
691 status =
692 perf_prot_ctx.api_stub->perf_set_level(domain_id, agent_id, perf_level);
693
694 /*
695 * Return immediately to the caller, fire-and-forget.
696 */
697 if ((status == FWK_SUCCESS) || (status == FWK_PENDING)) {
698 return_values.status = (int32_t)SCMI_SUCCESS;
699 } else if (status == FWK_E_RANGE) {
700 return_values.status = (int32_t)SCMI_OUT_OF_RANGE;
701 }
702
703 exit:
704 respond_status = scmi_perf_ctx->scmi_api->respond(
705 service_id,
706 &return_values,
707 (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
708 sizeof(return_values.status));
709 if (respond_status != FWK_SUCCESS) {
710 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
711 }
712
713 return status;
714 }
715
scmi_perf_level_get_handler(fwk_id_t service_id,const uint32_t * payload)716 static int scmi_perf_level_get_handler(
717 fwk_id_t service_id,
718 const uint32_t *payload)
719 {
720 int status, respond_status;
721 const struct scmi_perf_level_get_a2p *parameters;
722 struct scmi_perf_event_parameters *evt_params;
723 struct scmi_perf_level_get_p2a return_values;
724 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
725
726 parameters = (const struct scmi_perf_level_get_a2p *)payload;
727 if (parameters->domain_id >= scmi_perf_ctx->domain_count) {
728 status = FWK_SUCCESS;
729 return_values.status = (int32_t)SCMI_NOT_FOUND;
730
731 goto exit;
732 }
733
734 /* Check if there is already a request pending for this domain */
735 if (!fwk_id_is_equal(
736 perf_prot_ctx
737 .perf_ops_table[fwk_id_get_element_idx(
738 get_dependency_id(parameters->domain_id))]
739 .service_id,
740 FWK_ID_NONE)) {
741 return_values.status = (int32_t)SCMI_BUSY;
742 status = FWK_SUCCESS;
743
744 goto exit;
745 }
746
747 /* The get_level request is processed within the event being generated */
748 struct fwk_event event = {
749 .target_id = fwk_module_id_scmi_perf,
750 .id = scmi_perf_get_level,
751 };
752
753 evt_params = (struct scmi_perf_event_parameters *)event.params;
754 evt_params->domain_id = get_dependency_id(parameters->domain_id);
755
756 status = fwk_put_event(&event);
757 if (status != FWK_SUCCESS) {
758 return_values.status = (int32_t)SCMI_GENERIC_ERROR;
759
760 goto exit;
761 }
762
763 /* Store service identifier to indicate there is a pending request */
764 perf_prot_ctx.perf_ops_table[fwk_id_get_element_idx(evt_params->domain_id)]
765 .service_id = service_id;
766
767 return FWK_SUCCESS;
768
769 exit:
770 respond_status = scmi_perf_ctx->scmi_api->respond(
771 service_id,
772 &return_values,
773 (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
774 sizeof(return_values.status));
775 if (respond_status != FWK_SUCCESS) {
776 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
777 }
778
779 return status;
780 }
781
782 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
scmi_perf_limits_notify(fwk_id_t service_id,const uint32_t * payload)783 static int scmi_perf_limits_notify(fwk_id_t service_id, const uint32_t *payload)
784 {
785 unsigned int agent_id;
786 int status, respond_status;
787 unsigned int id;
788 const struct scmi_perf_notify_limits_a2p *parameters;
789 struct scmi_perf_notify_limits_p2a return_values = {
790 .status = (int32_t)SCMI_GENERIC_ERROR,
791 };
792 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
793
794 parameters = (const struct scmi_perf_notify_limits_a2p *)payload;
795 id = parameters->domain_id;
796 if (id >= scmi_perf_ctx->domain_count) {
797 status = FWK_SUCCESS;
798 return_values.status = (int32_t)SCMI_NOT_FOUND;
799
800 goto exit;
801 }
802
803 if ((parameters->notify_enable &
804 ~SCMI_PERF_NOTIFY_LIMITS_NOTIFY_ENABLE_MASK) != 0x0) {
805 status = FWK_SUCCESS;
806 return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
807
808 goto exit;
809 }
810
811 status = scmi_perf_ctx->scmi_api->get_agent_id(service_id, &agent_id);
812 if (status != FWK_SUCCESS) {
813 goto exit;
814 }
815
816 if (parameters->notify_enable) {
817 status = perf_prot_ctx.scmi_notification_api
818 ->scmi_notification_add_subscriber(
819 MOD_SCMI_PROTOCOL_ID_PERF,
820 id,
821 MOD_SCMI_PERF_NOTIFY_LIMITS,
822 service_id);
823 } else {
824 status = perf_prot_ctx.scmi_notification_api
825 ->scmi_notification_remove_subscriber(
826 MOD_SCMI_PROTOCOL_ID_PERF,
827 agent_id,
828 id,
829 MOD_SCMI_PERF_NOTIFY_LIMITS);
830 }
831 if (status != FWK_SUCCESS) {
832 goto exit;
833 }
834
835 return_values.status = (int32_t)SCMI_SUCCESS;
836
837 exit:
838 respond_status = scmi_perf_ctx->scmi_api->respond(
839 service_id,
840 &return_values,
841 (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
842 sizeof(return_values.status));
843 if (respond_status != FWK_SUCCESS) {
844 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
845 }
846
847 return status;
848 }
849
scmi_perf_level_notify(fwk_id_t service_id,const uint32_t * payload)850 static int scmi_perf_level_notify(fwk_id_t service_id, const uint32_t *payload)
851 {
852 unsigned int agent_id;
853 int status, respond_status;
854 unsigned int id;
855 const struct scmi_perf_notify_level_a2p *parameters;
856 struct scmi_perf_notify_level_p2a return_values = {
857 .status = (int32_t)SCMI_GENERIC_ERROR,
858 };
859 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
860
861 parameters = (const struct scmi_perf_notify_level_a2p *)payload;
862 id = parameters->domain_id;
863 if (id >= scmi_perf_ctx->domain_count) {
864 status = FWK_SUCCESS;
865 return_values.status = (int32_t)SCMI_NOT_FOUND;
866
867 goto exit;
868 }
869
870 if ((parameters->notify_enable &
871 ~SCMI_PERF_NOTIFY_LEVEL_NOTIFY_ENABLE_MASK) != 0x0) {
872 status = FWK_SUCCESS;
873 return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
874
875 goto exit;
876 }
877
878 id = parameters->domain_id;
879
880 status = scmi_perf_ctx->scmi_api->get_agent_id(service_id, &agent_id);
881 if (status != FWK_SUCCESS) {
882 goto exit;
883 }
884
885 if (parameters->notify_enable) {
886 status = perf_prot_ctx.scmi_notification_api
887 ->scmi_notification_add_subscriber(
888 MOD_SCMI_PROTOCOL_ID_PERF,
889 id,
890 MOD_SCMI_PERF_NOTIFY_LEVEL,
891 service_id);
892 } else {
893 status = perf_prot_ctx.scmi_notification_api
894 ->scmi_notification_remove_subscriber(
895 MOD_SCMI_PROTOCOL_ID_PERF,
896 agent_id,
897 id,
898 MOD_SCMI_PERF_NOTIFY_LEVEL);
899 }
900 if (status != FWK_SUCCESS) {
901 goto exit;
902 }
903
904 return_values.status = (int32_t)SCMI_SUCCESS;
905
906 exit:
907 respond_status = scmi_perf_ctx->scmi_api->respond(
908 service_id,
909 &return_values,
910 (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
911 sizeof(return_values.status));
912 if (respond_status != FWK_SUCCESS) {
913 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
914 }
915
916 return status;
917 }
918 #endif
919
920 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
921
922 /*
923 * Note that the Fast Channel doorbell is not supported in this
924 * implementation.
925 */
scmi_perf_describe_fast_channels(fwk_id_t service_id,const uint32_t * payload)926 static int scmi_perf_describe_fast_channels(
927 fwk_id_t service_id,
928 const uint32_t *payload)
929 {
930 return perf_fch_describe_fast_channels(service_id, payload);
931 }
932 #endif
933
934 /*
935 * SCMI Performance Policy Handlers
936 */
scmi_perf_limits_set_policy(enum mod_scmi_perf_policy_status * policy_status,uint32_t * range_min,uint32_t * range_max,unsigned int agent_id,fwk_id_t domain_id)937 FWK_WEAK int scmi_perf_limits_set_policy(
938 enum mod_scmi_perf_policy_status *policy_status,
939 uint32_t *range_min,
940 uint32_t *range_max,
941 unsigned int agent_id,
942 fwk_id_t domain_id)
943 {
944 *policy_status = MOD_SCMI_PERF_EXECUTE_MESSAGE_HANDLER;
945
946 return FWK_SUCCESS;
947 }
948
scmi_perf_level_set_policy(enum mod_scmi_perf_policy_status * policy_status,uint32_t * level,unsigned int agent_id,fwk_id_t domain_id)949 FWK_WEAK int scmi_perf_level_set_policy(
950 enum mod_scmi_perf_policy_status *policy_status,
951 uint32_t *level,
952 unsigned int agent_id,
953 fwk_id_t domain_id)
954 {
955 *policy_status = MOD_SCMI_PERF_EXECUTE_MESSAGE_HANDLER;
956
957 return FWK_SUCCESS;
958 }
959
960 /*
961 * SCMI module -> SCMI performance module interface
962 */
963
scmi_perf_get_scmi_protocol_id(fwk_id_t protocol_id,uint8_t * scmi_protocol_id)964 static int scmi_perf_get_scmi_protocol_id(
965 fwk_id_t protocol_id,
966 uint8_t *scmi_protocol_id)
967 {
968 *scmi_protocol_id = (uint8_t)MOD_SCMI_PROTOCOL_ID_PERF;
969
970 return FWK_SUCCESS;
971 }
972
scmi_perf_message_handler(fwk_id_t protocol_id,fwk_id_t service_id,const uint32_t * payload,size_t payload_size,unsigned int message_id)973 static int scmi_perf_message_handler(
974 fwk_id_t protocol_id,
975 fwk_id_t service_id,
976 const uint32_t *payload,
977 size_t payload_size,
978 unsigned int message_id)
979 {
980 int validation_result;
981
982 static_assert(
983 FWK_ARRAY_SIZE(handler_table) == FWK_ARRAY_SIZE(payload_size_table),
984 "[SCMI] Performance management protocol table sizes not consistent");
985
986 validation_result =
987 perf_prot_ctx.scmi_perf_ctx->scmi_api->scmi_message_validation(
988 MOD_SCMI_PROTOCOL_ID_PERF,
989 service_id,
990 payload,
991 payload_size,
992 message_id,
993 payload_size_table,
994 (unsigned int)MOD_SCMI_PERF_COMMAND_COUNT,
995 handler_table);
996
997 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
998 if ((message_id >= MOD_SCMI_MESSAGE_ID_ATTRIBUTE) &&
999 (validation_result == SCMI_SUCCESS)) {
1000 int status =
1001 scmi_perf_permissions_handler(service_id, payload, message_id);
1002 if (status != FWK_SUCCESS) {
1003 if (status == FWK_E_PARAM) {
1004 validation_result = (int32_t)SCMI_NOT_FOUND;
1005 } else {
1006 validation_result = (int32_t)SCMI_DENIED;
1007 }
1008 }
1009 }
1010 #endif
1011 if (validation_result == SCMI_SUCCESS) {
1012 return handler_table[message_id](service_id, payload);
1013 } else {
1014 return perf_prot_ctx.scmi_perf_ctx->scmi_api->respond(
1015 service_id, &validation_result, sizeof(validation_result));
1016 }
1017 }
1018
1019 static struct mod_scmi_to_protocol_api scmi_perf_mod_scmi_to_protocol_api = {
1020 .get_scmi_protocol_id = scmi_perf_get_scmi_protocol_id,
1021 .message_handler = scmi_perf_message_handler
1022 };
1023
1024 /*
1025 * Static helpers for responding to SCMI.
1026 */
scmi_perf_respond(void * return_values,fwk_id_t domain_id,int size)1027 static void scmi_perf_respond(void *return_values, fwk_id_t domain_id, int size)
1028 {
1029 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
1030 int idx = (int)fwk_id_get_element_idx(domain_id);
1031 fwk_id_t service_id;
1032 int respond_status;
1033
1034 /*
1035 * The service identifier used for the response is retrieved from the
1036 * domain operations table.
1037 */
1038 service_id = perf_prot_ctx.perf_ops_table[idx].service_id;
1039
1040 respond_status =
1041 scmi_perf_ctx->scmi_api->respond(service_id, return_values, size);
1042 if (respond_status != FWK_SUCCESS) {
1043 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1044 }
1045
1046 /*
1047 * Set the service identifier to 'none' to indicate the domain is
1048 * available again.
1049 */
1050 perf_prot_ctx.perf_ops_table[idx].service_id = FWK_ID_NONE;
1051 }
1052
1053 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
perf_prot_ops_notify_level(unsigned int domain_idx,uint32_t level,uint32_t cookie)1054 void perf_prot_ops_notify_level(
1055 unsigned int domain_idx,
1056 uint32_t level,
1057 uint32_t cookie)
1058 {
1059 int status;
1060 struct scmi_perf_level_changed level_changed;
1061
1062 level_changed.agent_id = (uint32_t)cookie;
1063 level_changed.domain_id = (uint32_t)domain_idx;
1064 level_changed.performance_level = level;
1065
1066 status = perf_prot_ctx.scmi_notification_api->scmi_notification_notify(
1067 MOD_SCMI_PROTOCOL_ID_PERF,
1068 MOD_SCMI_PERF_NOTIFY_LEVEL,
1069 SCMI_PERF_LEVEL_CHANGED,
1070 &level_changed,
1071 sizeof(level_changed));
1072 if (status != FWK_SUCCESS) {
1073 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1074 }
1075 }
1076
perf_prot_ops_notify_limits(unsigned int domain_idx,uint32_t range_min,uint32_t range_max)1077 void perf_prot_ops_notify_limits(
1078 unsigned int domain_idx,
1079 uint32_t range_min,
1080 uint32_t range_max)
1081 {
1082 struct scmi_perf_limits_changed limits_changed;
1083 int status;
1084
1085 limits_changed.agent_id = (uint32_t)0;
1086 limits_changed.domain_id = (uint32_t)domain_idx;
1087 limits_changed.range_min = range_min;
1088 limits_changed.range_max = range_max;
1089
1090 status = perf_prot_ctx.scmi_notification_api->scmi_notification_notify(
1091 MOD_SCMI_PROTOCOL_ID_PERF,
1092 MOD_SCMI_PERF_NOTIFY_LIMITS,
1093 SCMI_PERF_LIMITS_CHANGED,
1094 &limits_changed,
1095 sizeof(limits_changed));
1096 if (status != FWK_SUCCESS) {
1097 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1098 }
1099 }
1100 #endif
1101
1102 #ifdef BUILD_HAS_MOD_STATISTICS
perf_prot_ops_update_stats(fwk_id_t domain_id,uint32_t level)1103 void perf_prot_ops_update_stats(fwk_id_t domain_id, uint32_t level)
1104 {
1105 int idx = (int)fwk_id_get_element_idx(domain_id);
1106 size_t level_id;
1107 int status;
1108
1109 status = perf_prot_ctx.scmi_perf_ctx->dvfs_api->get_level_id(
1110 domain_id, level, &level_id);
1111 if (status == FWK_SUCCESS) {
1112 status = perf_prot_ctx.stats_api->update_domain(
1113 fwk_module_id_scmi_perf,
1114 FWK_ID_ELEMENT(FWK_MODULE_IDX_SCMI_PERF, idx),
1115 level_id);
1116 if (status != FWK_SUCCESS) {
1117 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1118 }
1119 } else {
1120 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1121 }
1122 }
1123 #endif
1124
1125 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
scmi_init_notifications(int domains)1126 static int scmi_init_notifications(int domains)
1127 {
1128 int status;
1129
1130 status = perf_prot_ctx.scmi_perf_ctx->scmi_api->get_agent_count(
1131 &perf_prot_ctx.agent_count);
1132 if (status != FWK_SUCCESS) {
1133 return status;
1134 }
1135
1136 fwk_assert(perf_prot_ctx.agent_count != 0u);
1137
1138 status = perf_prot_ctx.scmi_notification_api->scmi_notification_init(
1139 MOD_SCMI_PROTOCOL_ID_PERF,
1140 perf_prot_ctx.agent_count,
1141 domains,
1142 MOD_SCMI_PERF_NOTIFICATION_COUNT);
1143
1144 return status;
1145 }
1146 #endif
1147
1148 #ifdef BUILD_HAS_MOD_STATISTICS
scmi_perf_stats_start(void)1149 static int scmi_perf_stats_start(void)
1150 {
1151 const struct mod_scmi_perf_domain_config *domain;
1152 int status = FWK_SUCCESS;
1153 int stats_domains = 0;
1154 unsigned int i;
1155
1156 struct mod_scmi_perf_ctx *scmi_perf_ctx = perf_prot_ctx.scmi_perf_ctx;
1157
1158 if (!scmi_perf_ctx->config->stats_enabled) {
1159 return FWK_E_SUPPORT;
1160 }
1161
1162 /* Count how many domains have statistics */
1163 for (i = 0; i < scmi_perf_ctx->domain_count; i++) {
1164 domain = &(*scmi_perf_ctx->config->domains)[i];
1165 if (domain->stats_collected) {
1166 stats_domains++;
1167 }
1168 }
1169
1170 status = perf_prot_ctx.stats_api->init_stats(
1171 fwk_module_id_scmi_perf, scmi_perf_ctx->domain_count, stats_domains);
1172
1173 if (status != FWK_SUCCESS) {
1174 return status;
1175 }
1176
1177 for (i = 0; i < scmi_perf_ctx->domain_count; i++) {
1178 domain = &(*scmi_perf_ctx->config->domains)[i];
1179 /* Add this domain to track statistics when needed */
1180 if (domain->stats_collected) {
1181 fwk_id_t domain_id;
1182 size_t opp_count;
1183
1184 domain_id = get_dependency_id(i);
1185 status =
1186 scmi_perf_ctx->dvfs_api->get_opp_count(domain_id, &opp_count);
1187
1188 if (status != FWK_SUCCESS) {
1189 return status;
1190 }
1191
1192 status = perf_prot_ctx.stats_api->add_domain(
1193 fwk_module_id_scmi_perf,
1194 FWK_ID_ELEMENT(FWK_MODULE_IDX_SCMI_PERF, i),
1195 (int)opp_count);
1196
1197 if (status != FWK_SUCCESS) {
1198 return status;
1199 }
1200 }
1201 }
1202
1203 return perf_prot_ctx.stats_api->start_stats(fwk_module_id_scmi_perf);
1204 }
1205 #endif
1206
perf_prot_ops_init(fwk_id_t module_id,unsigned int element_count,const void * data,struct mod_scmi_perf_ctx * mod_ctx,struct mod_scmi_perf_private_api_perf_stub * api)1207 int perf_prot_ops_init(
1208 fwk_id_t module_id,
1209 unsigned int element_count,
1210 const void *data,
1211 struct mod_scmi_perf_ctx *mod_ctx,
1212 struct mod_scmi_perf_private_api_perf_stub *api)
1213 {
1214 uint32_t i;
1215
1216 perf_prot_ctx.scmi_perf_ctx = mod_ctx;
1217 perf_prot_ctx.api_stub = api;
1218
1219 perf_prot_ctx.perf_ops_table = fwk_mm_calloc(
1220 perf_prot_ctx.scmi_perf_ctx->config->perf_doms_count,
1221 sizeof(struct perf_operations));
1222
1223 /* Initialize table */
1224 for (i = 0; i < perf_prot_ctx.scmi_perf_ctx->domain_count; i++) {
1225 perf_prot_ctx.perf_ops_table[i].service_id = FWK_ID_NONE;
1226 }
1227
1228 return FWK_SUCCESS;
1229 }
1230
perf_prot_ops_bind(fwk_id_t id,unsigned int round)1231 int perf_prot_ops_bind(fwk_id_t id, unsigned int round)
1232 {
1233 int status = FWK_SUCCESS;
1234
1235 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
1236 status = fwk_module_bind(
1237 FWK_ID_MODULE(FWK_MODULE_IDX_RESOURCE_PERMS),
1238 FWK_ID_API(FWK_MODULE_IDX_RESOURCE_PERMS, MOD_RES_PERM_RESOURCE_PERMS),
1239 &perf_prot_ctx.res_perms_api);
1240 if (status != FWK_SUCCESS) {
1241 return status;
1242 }
1243 #endif
1244
1245 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
1246 status = fwk_module_bind(
1247 FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
1248 FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_NOTIFICATION),
1249 &perf_prot_ctx.scmi_notification_api);
1250 if (status != FWK_SUCCESS) {
1251 return status;
1252 }
1253 #endif
1254
1255 #ifdef BUILD_HAS_MOD_STATISTICS
1256 if (perf_prot_ctx.scmi_perf_ctx->config->stats_enabled) {
1257 status = fwk_module_bind(
1258 FWK_ID_MODULE(FWK_MODULE_IDX_STATISTICS),
1259 FWK_ID_API(FWK_MODULE_IDX_STATISTICS, MOD_STATS_API_IDX_STATS),
1260 &perf_prot_ctx.stats_api);
1261 if (status != FWK_SUCCESS) {
1262 return FWK_E_PANIC;
1263 }
1264 }
1265 #endif
1266
1267 return status;
1268 }
1269
perf_prot_ops_start(fwk_id_t id)1270 int perf_prot_ops_start(fwk_id_t id)
1271 {
1272 int status = FWK_SUCCESS;
1273
1274 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
1275 status =
1276 scmi_init_notifications((int)perf_prot_ctx.scmi_perf_ctx->domain_count);
1277 if (status != FWK_SUCCESS) {
1278 return status;
1279 }
1280 #endif
1281
1282 #ifdef BUILD_HAS_MOD_STATISTICS
1283 status = scmi_perf_stats_start();
1284 if (status != FWK_SUCCESS) {
1285 return status;
1286 }
1287 #endif
1288
1289 return status;
1290 }
1291
perf_prot_ops_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** prot_bind_request_api)1292 void perf_prot_ops_process_bind_request(
1293 fwk_id_t source_id,
1294 fwk_id_t target_id,
1295 fwk_id_t api_id,
1296 const void **prot_bind_request_api)
1297 {
1298 *prot_bind_request_api = &scmi_perf_mod_scmi_to_protocol_api;
1299 }
1300
1301 /*
1302 * Handle a request for get_level/limits.
1303 */
process_request_event(const struct fwk_event * event)1304 static int process_request_event(const struct fwk_event *event)
1305 {
1306 int status;
1307 struct scmi_perf_event_parameters *params;
1308 struct scmi_perf_level_get_p2a return_values_level;
1309 struct mod_dvfs_opp opp;
1310
1311 /* request event to DVFS HAL */
1312 if (fwk_id_is_equal(event->id, scmi_perf_get_level)) {
1313 params = (struct scmi_perf_event_parameters *)event->params;
1314
1315 status = perf_prot_ctx.scmi_perf_ctx->dvfs_api->get_current_opp(
1316 params->domain_id, &opp);
1317 if (status == FWK_SUCCESS) {
1318 /* DVFS value is ready */
1319 return_values_level = (struct scmi_perf_level_get_p2a){
1320 .status = SCMI_SUCCESS,
1321 .performance_level = opp.level,
1322 };
1323
1324 scmi_perf_respond(
1325 &return_values_level,
1326 params->domain_id,
1327 (int)sizeof(return_values_level));
1328
1329 return status;
1330 } else if (status == FWK_PENDING) {
1331 /* DVFS value will be provided through a response event */
1332 return FWK_SUCCESS;
1333 } else {
1334 return_values_level = (struct scmi_perf_level_get_p2a){
1335 .status = SCMI_HARDWARE_ERROR,
1336 };
1337
1338 scmi_perf_respond(
1339 &return_values_level,
1340 params->domain_id,
1341 (int)sizeof(return_values_level.status));
1342
1343 return FWK_E_DEVICE;
1344 }
1345 }
1346
1347 return FWK_E_PARAM;
1348 }
1349
1350 /*
1351 * Handle a response event from the HAL which indicates that the
1352 * requested operation has completed.
1353 */
process_response_event(const struct fwk_event * event)1354 static int process_response_event(const struct fwk_event *event)
1355 {
1356 struct mod_dvfs_params_response *params_level;
1357 struct scmi_perf_level_get_p2a return_values_level;
1358
1359 if (fwk_id_is_equal(event->id, mod_dvfs_event_id_get_opp)) {
1360 params_level = (struct mod_dvfs_params_response *)event->params;
1361 return_values_level = (struct scmi_perf_level_get_p2a){
1362 .status = params_level->status,
1363 .performance_level = params_level->performance_level,
1364 };
1365 scmi_perf_respond(
1366 &return_values_level,
1367 event->source_id,
1368 (return_values_level.status == SCMI_SUCCESS) ?
1369 (int)sizeof(return_values_level) :
1370 (int)sizeof(return_values_level.status));
1371 }
1372
1373 return FWK_SUCCESS;
1374 }
1375
perf_prot_ops_process_events(const struct fwk_event * event,struct fwk_event * resp_event)1376 int perf_prot_ops_process_events(
1377 const struct fwk_event *event,
1378 struct fwk_event *resp_event)
1379 {
1380 /* Request events from SCMI */
1381 if (fwk_id_get_module_idx(event->source_id) ==
1382 fwk_id_get_module_idx(fwk_module_id_scmi)) {
1383 return process_request_event(event);
1384 }
1385
1386 /* Response events from DVFS */
1387 if (fwk_id_get_module_idx(event->source_id) ==
1388 fwk_id_get_module_idx(fwk_module_id_dvfs)) {
1389 return process_response_event(event);
1390 }
1391
1392 return FWK_E_PARAM;
1393 }
1394
1395 // EOF
1396