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