1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Description:
8  *     SCMI performance domain management protocol support.
9  */
10 
11 #include <internal/scmi_perf.h>
12 
13 #include <mod_dvfs.h>
14 #ifdef BUILD_HAS_MOD_PERF_CONTROLLER
15 #    include <mod_perf_controller.h>
16 #endif
17 #include <mod_scmi.h>
18 #include <mod_scmi_perf.h>
19 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
20 #    include "perf_plugins_handler.h"
21 #endif
22 #include <mod_timer.h>
23 #include "scmi_perf.h"
24 
25 #include <fwk_assert.h>
26 #include <fwk_core.h>
27 #include <fwk_event.h>
28 #include <fwk_id.h>
29 #include <fwk_log.h>
30 #include <fwk_macros.h>
31 #include <fwk_mm.h>
32 #include <fwk_module.h>
33 #include <fwk_module_idx.h>
34 #include <fwk_status.h>
35 #include <fwk_string.h>
36 
37 #include <stdbool.h>
38 #include <stddef.h>
39 
40 static struct mod_scmi_perf_ctx scmi_perf_ctx;
41 
42 /*
43  * SCMI PERF Helpers
44  */
45 
46 /* This identifier is either:
47  * - exactly the one built by the perf-plugins-handler (sub-element type)
48  * - or the DVFS domain
49  */
get_dependency_id(unsigned int el_idx)50 fwk_id_t get_dependency_id(unsigned int el_idx)
51 {
52 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
53     return perf_plugins_get_dependency_id(el_idx);
54 #else
55     return FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, el_idx);
56 #endif
57 }
58 
get_ctx(fwk_id_t domain_id)59 static inline struct scmi_perf_domain_ctx *get_ctx(fwk_id_t domain_id)
60 {
61     return &scmi_perf_ctx.domain_ctx_table[fwk_id_get_element_idx(domain_id)];
62 }
63 
opp_for_level_found(uint32_t * level,struct perf_opp_table * opp_table,size_t i)64 static inline int opp_for_level_found(
65     uint32_t *level,
66     struct perf_opp_table *opp_table,
67     size_t i)
68 {
69     *level = opp_table->opps[i].level;
70 
71     return FWK_SUCCESS;
72 }
73 
find_opp_for_level(struct scmi_perf_domain_ctx * domain_ctx,uint32_t * level,bool use_nearest)74 static int find_opp_for_level(
75     struct scmi_perf_domain_ctx *domain_ctx,
76     uint32_t *level,
77     bool use_nearest)
78 {
79     struct perf_opp_table *opp_table;
80     size_t i;
81     uint32_t opp_level, limit_max;
82 
83     opp_table = domain_ctx->opp_table;
84     limit_max = domain_ctx->level_limits.maximum;
85 
86     for (i = 0; i < opp_table->opp_count; i++) {
87         opp_level = opp_table->opps[i].level;
88 
89         if ((use_nearest &&
90              ((opp_level < *level) && (opp_level < limit_max))) ||
91             (!use_nearest && (opp_level != *level))) {
92             /*
93              * The current OPP level is either below the desired level
94              * or not exact match found.
95              */
96             continue;
97         } else {
98             /*
99              * Either found exact match, or the current OPP is above the limit.
100              * Must be within limits.
101              */
102             if ((opp_level > limit_max) && (i > 0)) {
103                 i--;
104             }
105 
106             return opp_for_level_found(level, opp_table, i);
107         }
108     }
109 
110     /* Either not exact match or approximate to the highest level */
111     if (use_nearest) {
112         i--;
113 
114         return opp_for_level_found(level, opp_table, i);
115     }
116 
117     return FWK_E_RANGE;
118 }
119 
120 #if defined(BUILD_HAS_SCMI_PERF_PROTOCOL_OPS) || \
121     defined(BUILD_HAS_SCMI_PERF_FAST_CHANNELS)
perf_set_level(fwk_id_t domain_id,unsigned int agent_id,uint32_t perf_level)122 static int perf_set_level(
123     fwk_id_t domain_id,
124     unsigned int agent_id,
125     uint32_t perf_level)
126 {
127     struct scmi_perf_domain_ctx *domain_ctx;
128     int status;
129 
130     domain_ctx = get_ctx(domain_id);
131 
132     status = find_opp_for_level(
133         domain_ctx, &perf_level, scmi_perf_ctx.config->approximate_level);
134     if (status != FWK_SUCCESS) {
135         return status;
136     }
137 
138     if ((perf_level < domain_ctx->level_limits.minimum) ||
139         (perf_level > domain_ctx->level_limits.maximum)) {
140         return FWK_E_RANGE;
141     }
142 #    ifdef BUILD_HAS_MOD_PERF_CONTROLLER
143     return scmi_perf_ctx.perf_controller_api->set_performance_level(
144         domain_id, agent_id, perf_level);
145 #    else
146     return scmi_perf_ctx.dvfs_api->set_level(domain_id, agent_id, perf_level);
147 #    endif
148 }
149 #endif
150 
validate_new_limits(struct scmi_perf_domain_ctx * domain_ctx,const struct mod_scmi_perf_level_limits * limits)151 static int validate_new_limits(
152     struct scmi_perf_domain_ctx *domain_ctx,
153     const struct mod_scmi_perf_level_limits *limits)
154 {
155     uint32_t limit;
156     int status;
157 
158     if (scmi_perf_ctx.config->approximate_level) {
159         /* When approx level is chosen, a level is always found */
160         return FWK_SUCCESS;
161     }
162 
163     limit = limits->minimum;
164     status = find_opp_for_level(domain_ctx, &limit, false);
165     if (status != FWK_SUCCESS) {
166         return status;
167     }
168 
169     limit = limits->maximum;
170     return find_opp_for_level(domain_ctx, &limit, false);
171 }
172 
173 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
scmi_perf_notify_limits_fch_updated(fwk_id_t domain_id,uint32_t range_min,uint32_t range_max)174 static void scmi_perf_notify_limits_fch_updated(
175     fwk_id_t domain_id,
176     uint32_t range_min,
177     uint32_t range_max)
178 {
179     unsigned int idx;
180     idx = fwk_id_get_element_idx(domain_id);
181     struct mod_scmi_perf_fast_channel_limit *get_limit;
182     struct scmi_perf_domain_ctx *domain_ctx;
183     const struct fast_channel_ctx *fch_ctx;
184     domain_ctx = &scmi_perf_ctx.domain_ctx_table[idx];
185     if (perf_fch_domain_has_fastchannels(idx)) {
186         fch_ctx = &domain_ctx->fch_ctx[MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_GET];
187         get_limit = (struct mod_scmi_perf_fast_channel_limit
188                          *)((uintptr_t)fch_ctx->fch_address.local_view_address);
189         if (get_limit != NULL) { /* note: get_limit may not be defined */
190             get_limit->range_max = range_max;
191             get_limit->range_min = range_min;
192         }
193     }
194 }
195 #endif
196 
197 /*
198  * A domain limits range has been updated. Depending on the system
199  * configuration we may send an SCMI notification to the agents which
200  * have registered for these notifications and/or update the associated
201  * fast channels.
202  */
scmi_perf_notify_limits_updated(fwk_id_t domain_id,uint32_t range_min,uint32_t range_max)203 static void scmi_perf_notify_limits_updated(
204     fwk_id_t domain_id,
205     uint32_t range_min,
206     uint32_t range_max)
207 {
208 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
209     scmi_perf_notify_limits_fch_updated(domain_id, range_min, range_max);
210 #endif
211 
212 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
213     struct perf_plugins_perf_report perf_report = {
214         .dep_dom_id = domain_id,
215         .max_limit = range_max,
216         .min_limit = range_min,
217     };
218 
219     perf_plugins_handler_report(&perf_report);
220 #endif
221 
222 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
223     unsigned int idx;
224     idx = fwk_id_get_element_idx(domain_id);
225     perf_prot_ops_notify_limits(idx, range_min, range_max);
226 #endif
227 }
228 
perf_set_limits(fwk_id_t domain_id,unsigned int agent_id,const struct mod_scmi_perf_level_limits * limits)229 int perf_set_limits(
230     fwk_id_t domain_id,
231     unsigned int agent_id,
232     const struct mod_scmi_perf_level_limits *limits)
233 {
234     struct scmi_perf_domain_ctx *domain_ctx;
235     uint32_t needle;
236     int status;
237     bool needs_new_level = true;
238 
239     if (limits->minimum > limits->maximum) {
240         return FWK_E_PARAM;
241     }
242 
243     domain_ctx = get_ctx(domain_id);
244 
245     if ((limits->minimum == domain_ctx->level_limits.minimum) &&
246         (limits->maximum == domain_ctx->level_limits.maximum)) {
247         return FWK_SUCCESS;
248     }
249 
250     status = validate_new_limits(domain_ctx, limits);
251     if (status != FWK_SUCCESS) {
252         return status;
253     }
254 
255     /* Adjust opp for new limits */
256     if (domain_ctx->curr_level < limits->minimum) {
257         needle = limits->minimum;
258     } else if (domain_ctx->curr_level > limits->maximum) {
259         needle = limits->maximum;
260     } else {
261         /* No level transition necessary */
262         needs_new_level = false;
263     }
264 
265     scmi_perf_notify_limits_updated(
266         domain_id, limits->minimum, limits->maximum);
267 
268     domain_ctx->level_limits.minimum = limits->minimum;
269     domain_ctx->level_limits.maximum = limits->maximum;
270 
271     if (!needs_new_level) {
272         return FWK_SUCCESS;
273     }
274 
275     status = find_opp_for_level(
276         domain_ctx, &needle, scmi_perf_ctx.config->approximate_level);
277     if (status != FWK_SUCCESS) {
278         return status;
279     }
280 
281 #ifdef BUILD_HAS_MOD_PERF_CONTROLLER
282     return scmi_perf_ctx.perf_controller_api->set_performance_level(
283         domain_id, agent_id, needle);
284 #else
285     return scmi_perf_ctx.dvfs_api->set_level(domain_id, agent_id, needle);
286 #endif
287 }
288 
289 /*
290  * A domain performance level has been updated. Depending on the system
291  * configuration we may send an SCMI notification to the agents which
292  * have registered for these notifications and/or update the associated
293  * fast channels.
294  */
scmi_perf_notify_level_updated(fwk_id_t domain_id,uintptr_t cookie,uint32_t level)295 static void scmi_perf_notify_level_updated(
296     fwk_id_t domain_id,
297     uintptr_t cookie,
298     uint32_t level)
299 {
300     struct scmi_perf_domain_ctx *domain_ctx;
301 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
302     fwk_id_t dep_dom_id;
303 #endif
304 
305 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
306     unsigned int idx = fwk_id_get_element_idx(domain_id);
307 #endif
308 
309 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
310     struct perf_plugins_perf_report perf_report = {
311         .dep_dom_id = domain_id,
312         .level = level,
313     };
314 
315     perf_plugins_handler_report(&perf_report);
316 #endif
317 
318 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
319     /*
320      * The SCMI spec enforces that "[PERFORMANCE_LEVEL_GET] this command returns
321      * the current performance level of a domain", thus when a physical domain
322      * has been updated, we update all the relevant logical domains.
323      */
324     for (uint32_t i = 0; i < scmi_perf_ctx.domain_count; i++) {
325         dep_dom_id = get_dependency_id((unsigned int)i);
326 
327         if (fwk_id_get_element_idx(dep_dom_id) ==
328             fwk_id_get_element_idx(domain_id)) {
329             perf_fch_set_fch_get_level(i, level);
330         }
331     }
332 #endif
333 
334 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
335     perf_prot_ops_notify_level(idx, level, cookie);
336 #endif
337 
338 #ifdef BUILD_HAS_MOD_STATISTICS
339     perf_prot_ops_update_stats(domain_id, level);
340 #endif
341 
342     domain_ctx = get_ctx(domain_id);
343     domain_ctx->curr_level = level;
344 }
345 
346 static struct mod_scmi_perf_updated_api perf_update_api = {
347     .notify_level_updated = scmi_perf_notify_level_updated,
348 };
349 
350 #if defined(BUILD_HAS_SCMI_PERF_PROTOCOL_OPS) || \
351     defined(BUILD_HAS_SCMI_PERF_FAST_CHANNELS)
352 static struct mod_scmi_perf_private_api_perf_stub api_perf_stub = {
353     .perf_set_level = perf_set_level,
354     .perf_set_limits = perf_set_limits,
355     .find_opp_for_level = find_opp_for_level,
356     .notify_limits_updated = scmi_perf_notify_limits_updated,
357 };
358 #endif
359 
360 /*
361  * Framework handlers
362  */
scmi_perf_init(fwk_id_t module_id,unsigned int element_count,const void * data)363 static int scmi_perf_init(fwk_id_t module_id, unsigned int element_count,
364                           const void *data)
365 {
366     int dvfs_doms_count;
367     const struct mod_scmi_perf_config *config =
368         (const struct mod_scmi_perf_config *)data;
369 
370 #if defined(BUILD_HAS_SCMI_PERF_PROTOCOL_OPS) || \
371     defined(BUILD_HAS_SCMI_PERF_FAST_CHANNELS)
372     int status;
373 #endif
374 
375     if ((config == NULL) || (config->domains == NULL)) {
376         return FWK_E_PARAM;
377     }
378 
379     dvfs_doms_count =
380         fwk_module_get_element_count(FWK_ID_MODULE(FWK_MODULE_IDX_DVFS));
381     if (dvfs_doms_count <= 0) {
382         return FWK_E_SUPPORT;
383     }
384 
385     scmi_perf_ctx.dvfs_doms_count = (unsigned int)dvfs_doms_count;
386 
387     scmi_perf_ctx.domain_ctx_table = fwk_mm_calloc(
388         config->perf_doms_count, sizeof(struct scmi_perf_domain_ctx));
389 
390     scmi_perf_ctx.config = config;
391     scmi_perf_ctx.domain_count = (uint32_t)config->perf_doms_count;
392 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
393     status = perf_fch_init(
394         module_id, element_count, data, &scmi_perf_ctx, &api_perf_stub);
395     if (status != FWK_SUCCESS) {
396         return FWK_E_PANIC;
397     }
398 #endif
399 
400 #ifdef BUILD_HAS_SCMI_PERF_PROTOCOL_OPS
401     status = perf_prot_ops_init(
402         module_id, element_count, data, &scmi_perf_ctx, &api_perf_stub);
403     if (status != FWK_SUCCESS) {
404         return FWK_E_PANIC;
405     }
406 #endif
407 
408 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
409     return perf_plugins_handler_init(config);
410 #else
411     return FWK_SUCCESS;
412 #endif
413 }
414 
scmi_perf_bind(fwk_id_t id,unsigned int round)415 static int scmi_perf_bind(fwk_id_t id, unsigned int round)
416 {
417 #if defined(BUILD_HAS_SCMI_PERF_PROTOCOL_OPS) || \
418     defined(BUILD_HAS_SCMI_PERF_FAST_CHANNELS) || \
419     defined(BUILD_HAS_MOD_PERF_CONTROLLER)
420     int status;
421 #endif
422 
423     if (round == 1) {
424         return FWK_SUCCESS;
425     }
426 
427 #if defined(BUILD_HAS_SCMI_PERF_FAST_CHANNELS)
428     status = perf_fch_bind(id, 0);
429     if (status != FWK_SUCCESS) {
430         return status;
431     }
432 #endif
433 #ifdef BUILD_HAS_SCMI_PERF_PROTOCOL_OPS
434     status = fwk_module_bind(
435         FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
436         FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),
437         &scmi_perf_ctx.scmi_api);
438     if (status != FWK_SUCCESS) {
439         return status;
440     }
441 
442     status = perf_prot_ops_bind(id, 0);
443     if (status != FWK_SUCCESS) {
444         return status;
445     }
446 #endif
447 
448 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
449     status = perf_plugins_handler_bind();
450     if (status != FWK_SUCCESS) {
451         return status;
452     }
453 #endif
454 
455 #ifdef BUILD_HAS_MOD_PERF_CONTROLLER
456     status = fwk_module_bind(
457         FWK_ID_MODULE(FWK_MODULE_IDX_PERF_CONTROLLER),
458         FWK_ID_API(
459             FWK_MODULE_IDX_PERF_CONTROLLER, MOD_PERF_CONTROLLER_PERF_API),
460         &scmi_perf_ctx.perf_controller_api);
461 
462     if (status != FWK_SUCCESS) {
463         return status;
464     }
465 #endif
466 
467     return fwk_module_bind(
468         FWK_ID_MODULE(FWK_MODULE_IDX_DVFS),
469         FWK_ID_API(FWK_MODULE_IDX_DVFS, MOD_DVFS_API_IDX_DVFS),
470         &scmi_perf_ctx.dvfs_api);
471 }
472 
scmi_perf_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)473 static int scmi_perf_process_bind_request(fwk_id_t source_id,
474     fwk_id_t target_id, fwk_id_t api_id, const void **api)
475 {
476     enum scmi_perf_api_idx api_id_type =
477         (enum scmi_perf_api_idx)fwk_id_get_api_idx(api_id);
478 
479     switch (api_id_type) {
480 #ifdef BUILD_HAS_SCMI_PERF_PROTOCOL_OPS
481     case MOD_SCMI_PERF_PROTOCOL_API:
482         perf_prot_ops_process_bind_request(source_id, target_id, api_id, api);
483         break;
484 #endif
485 
486     case MOD_SCMI_PERF_DVFS_UPDATE_API:
487         if (!fwk_id_is_equal(source_id, fwk_module_id_dvfs)) {
488             /* Only DVFS can use this API */
489             return FWK_E_ACCESS;
490         }
491 
492         *api = &perf_update_api;
493         break;
494 
495     case MOD_SCMI_PERF_PLUGINS_API:
496 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
497         return perf_plugins_handler_process_bind_request(
498             source_id, target_id, api_id, api);
499 #else
500         return FWK_E_ACCESS;
501 #endif
502         break;
503 
504     default:
505         return FWK_E_ACCESS;
506     }
507 
508     return FWK_SUCCESS;
509 }
510 
511 
scmi_perf_start(fwk_id_t id)512 static int scmi_perf_start(fwk_id_t id)
513 {
514     int status = FWK_SUCCESS;
515 
516     struct scmi_perf_domain_ctx *domain_ctx;
517     fwk_id_t domain_id;
518     size_t opp_count;
519     unsigned int i;
520 
521     const struct mod_dvfs_domain_config *dvfs_config;
522     struct perf_opp_table *opp_table = NULL;
523     const struct mod_scmi_perf_domain_config *domain_cfg;
524     unsigned int dom_idx;
525     bool has_phy_group;
526 
527     scmi_perf_ctx.opp_table = fwk_mm_calloc(
528         scmi_perf_ctx.dvfs_doms_count, sizeof(struct perf_opp_table));
529 
530     for (i = 0; i < scmi_perf_ctx.dvfs_doms_count; i++) {
531         opp_table = &scmi_perf_ctx.opp_table[i];
532 
533         domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, i);
534 
535         /* Get the number of levels for this DVFS domain */
536         status = scmi_perf_ctx.dvfs_api->get_opp_count(domain_id, &opp_count);
537         if (status != FWK_SUCCESS) {
538             return status;
539         }
540 
541         dvfs_config =
542             (const struct mod_dvfs_domain_config *)fwk_module_get_data(
543                 domain_id);
544 
545         opp_table->opps = &dvfs_config->opps[0];
546         opp_table->opp_count = opp_count;
547         opp_table->dvfs_id = domain_id;
548     }
549 
550     /* Assign to each performance domain the correct OPP table */
551     for (dom_idx = 0; dom_idx < scmi_perf_ctx.domain_count; dom_idx++) {
552         domain_cfg = &(*scmi_perf_ctx.config->domains)[dom_idx];
553 
554         has_phy_group = fwk_optional_id_is_defined(domain_cfg->phy_group_id);
555         if (has_phy_group) {
556             domain_id = domain_cfg->phy_group_id;
557         } else {
558             domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, dom_idx);
559         }
560 
561         domain_ctx = &scmi_perf_ctx.domain_ctx_table[dom_idx];
562 
563         /*
564          * Search the corresponding physical domain for this performance domain
565          * and assign the correct opp_table.
566          */
567         for (i = 0; i < scmi_perf_ctx.dvfs_doms_count; i++) {
568             opp_table = &scmi_perf_ctx.opp_table[i];
569 
570             if (fwk_id_is_equal(opp_table->dvfs_id, domain_id)) {
571                 domain_ctx->opp_table = opp_table;
572 
573                 /* init limits */
574                 domain_ctx->level_limits.minimum = opp_table->opps[0].level;
575                 domain_ctx->level_limits.maximum =
576                     opp_table->opps[opp_table->opp_count - 1].level;
577 
578                 break;
579             }
580         }
581         if (domain_ctx->opp_table == NULL) {
582             /* The corresponding physical domain did not have a match */
583             return FWK_E_PANIC;
584         }
585     }
586 
587 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
588     status = perf_fch_start(id);
589     if (status != FWK_SUCCESS) {
590         return status;
591     }
592 #endif
593 
594 #ifdef BUILD_HAS_SCMI_PERF_PROTOCOL_OPS
595     status = perf_prot_ops_start(id);
596     if (status != FWK_SUCCESS) {
597         return status;
598     }
599 #endif
600 
601     return status;
602 }
603 
604 /* Handle internal events */
process_internal_event(const struct fwk_event * event)605 static int process_internal_event(const struct fwk_event *event)
606 {
607     int status;
608     enum scmi_perf_event_idx event_idx =
609         (enum scmi_perf_event_idx)fwk_id_get_event_idx(event->id);
610 
611     switch (event_idx) {
612 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
613     case SCMI_PERF_EVENT_IDX_FAST_CHANNELS_PROCESS:
614         status = perf_fch_process_event(event);
615         break;
616 #endif
617     default:
618         status = FWK_E_PARAM;
619         break;
620     }
621 
622     return status;
623 }
624 
scmi_perf_process_event(const struct fwk_event * event,struct fwk_event * resp_event)625 static int scmi_perf_process_event(
626     const struct fwk_event *event,
627     struct fwk_event *resp_event)
628 {
629 #ifdef BUILD_HAS_SCMI_PERF_PROTOCOL_OPS
630     if ((fwk_id_get_module_idx(event->source_id) ==
631          fwk_id_get_module_idx(fwk_module_id_scmi)) ||
632         (fwk_id_get_module_idx(event->source_id) ==
633          fwk_id_get_module_idx(fwk_module_id_dvfs))) {
634         /* Handle requests from SCMi and responses from DVFS */
635         return perf_prot_ops_process_events(event, resp_event);
636     }
637 #endif
638 
639     /* Response internal events */
640     if (fwk_id_get_module_idx(event->source_id) ==
641         fwk_id_get_module_idx(fwk_module_id_scmi_perf)) {
642         return process_internal_event(event);
643     }
644 
645     return FWK_E_PARAM;
646 }
647 
648 /* SCMI Performance Management Protocol Definition */
649 const struct fwk_module module_scmi_perf = {
650     .api_count = (unsigned int)MOD_SCMI_PERF_API_COUNT,
651     .event_count = (unsigned int)SCMI_PERF_EVENT_IDX_COUNT,
652     .type = FWK_MODULE_TYPE_PROTOCOL,
653     .init = scmi_perf_init,
654     .bind = scmi_perf_bind,
655     .start = scmi_perf_start,
656     .process_bind_request = scmi_perf_process_bind_request,
657     .process_event = scmi_perf_process_event,
658 };
659