1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Description:
8  *      SCMI Performance Plugins Handler
9  *
10  *      This module is an extension of the SCMI-Performance module to support
11  *      the addition of optional performance-related modules (plugins). Such
12  *      modules can affect the final performance of one or more domains.
13  *      It can also be used when the domains exposed by SCMI are different from
14  *      the real frequency domains available in hardware as this layer can
15  *      perform the domain aggregation.
16  */
17 
18 #include "perf_plugins_handler.h"
19 #include "scmi_perf.h"
20 
21 #include <mod_dvfs.h>
22 
23 #include <fwk_assert.h>
24 #include <fwk_id.h>
25 #include <fwk_log.h>
26 #include <fwk_mm.h>
27 #include <fwk_module.h>
28 #include <fwk_module_idx.h>
29 #include <fwk_status.h>
30 
31 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
32 struct perf_plugins_dev_ctx {
33     /*
34      * Each logical domain within the same dependency - note that physical
35      * domain index is equal to this count.
36      */
37     unsigned int log_dom_count;
38 
39     /*
40      * Cause of the logical/physical domains handling, the size of each of the
41      * tables will be (sub_element + 1) which is equivalent to
42      * (number of logical domains + one physical domain).
43      * This is done to optimise the internal handling of the data for both types
44      * of domains.
45      *
46      * The last entry of each table is reserved for the physical domain.
47      */
48     struct perf_plugins_perf_update perf_table;
49 
50     /* For this domain, store max/min */
51     uint32_t max;
52     uint32_t lmax;
53     uint32_t lmin;
54 };
55 
56 struct perf_plugins_ctx {
57     struct perf_plugins_api **plugins_api_table;
58 
59     const struct mod_scmi_perf_config *config;
60 
61     struct perf_plugins_dev_ctx *dev_ctx;
62 
63     /*
64      * Table of dependency domain.
65      *
66      * It is an artefact to contain both the information of physical and
67      * logical within the same identifier in the following way:
68      * FWK_ID_SUB_ELEMENT(FWK_MODULE_IDX_DVFS, physical_domain, logical_domain)
69      *
70      * This helps the internal logic for handling the domain aggregation back
71      * and forth with scmi-perf.
72      * It assumes that DVFS ignores the identifier to be a sub-element.
73      */
74     fwk_id_t *dep_id_table;
75 
76     struct perf_plugins_perf_update full_perf_table;
77 
78     size_t dvfs_doms_count;
79 };
80 
81 static struct perf_plugins_ctx perf_plugins_ctx;
82 
perf_ph_get_ctx(fwk_id_t domain_id)83 static inline struct perf_plugins_dev_ctx *perf_ph_get_ctx(fwk_id_t domain_id)
84 {
85     return &perf_plugins_ctx.dev_ctx[fwk_id_get_element_idx(domain_id)];
86 }
87 
get_phy_domain_id(fwk_id_t domain_id)88 static inline fwk_id_t get_phy_domain_id(fwk_id_t domain_id)
89 {
90     return FWK_ID_ELEMENT(
91         FWK_MODULE_IDX_DVFS, fwk_id_get_element_idx(domain_id));
92 }
93 
perf_plugins_get_dependency_id(unsigned int dom_idx)94 fwk_id_t perf_plugins_get_dependency_id(unsigned int dom_idx)
95 {
96     return perf_plugins_ctx.dep_id_table[dom_idx];
97 }
98 
are_limits_valid(uint32_t min,uint32_t max)99 static inline bool are_limits_valid(uint32_t min, uint32_t max)
100 {
101     return (max > min);
102 }
103 
plugin_set_limits(struct plugin_limits_req * data)104 static int plugin_set_limits(struct plugin_limits_req *data)
105 {
106     struct perf_plugins_dev_ctx *dev_ctx;
107     fwk_id_t domain_id;
108     int status;
109 
110     if (data == NULL) {
111         return FWK_E_PARAM;
112     }
113 
114     struct mod_scmi_perf_level_limits req_limits = {
115         .minimum = data->min_limit,
116         .maximum = data->max_limit,
117     };
118 
119     domain_id = get_phy_domain_id(data->domain_id);
120     dev_ctx = perf_ph_get_ctx(domain_id);
121 
122     /* Compare with the aggregated value stored for this physical domain */
123     if (req_limits.maximum > dev_ctx->lmax) {
124         req_limits.maximum = dev_ctx->lmax;
125     }
126 
127     if (req_limits.minimum < dev_ctx->lmin) {
128         req_limits.minimum = dev_ctx->lmin;
129     }
130 
131     if (!are_limits_valid(req_limits.minimum, req_limits.maximum)) {
132         return FWK_E_PARAM;
133     }
134 
135     status = perf_set_limits(domain_id, 0, &req_limits);
136     if (status != FWK_SUCCESS) {
137         return FWK_E_DEVICE;
138     }
139 
140     return FWK_SUCCESS;
141 }
142 
143 static struct perf_plugins_handler_api handler_api = {
144     .plugin_set_limits = plugin_set_limits,
145 };
146 
147 /*
148  * Write back the adjusted values for use by the next plugins.
149  * Note that for logical oomains, the aggregated values are written.
150  */
write_back_adj_values(struct perf_plugins_dev_ctx * dev_ctx,size_t phy_dom_idx)151 static void write_back_adj_values(
152     struct perf_plugins_dev_ctx *dev_ctx,
153     size_t phy_dom_idx)
154 {
155     for (unsigned int i = 0; i < (dev_ctx->log_dom_count + 1); i++) {
156         dev_ctx->perf_table.adj_min_limit[i] = dev_ctx->lmin;
157         dev_ctx->perf_table.adj_max_limit[i] = dev_ctx->lmax;
158     }
159 
160     perf_plugins_ctx.full_perf_table.adj_min_limit[phy_dom_idx] = dev_ctx->lmin;
161     perf_plugins_ctx.full_perf_table.adj_max_limit[phy_dom_idx] = dev_ctx->lmax;
162 }
163 
plugins_policy_sync_level_limits(struct perf_plugins_dev_ctx * dev_ctx,enum plugin_domain_type dom_type)164 static void plugins_policy_sync_level_limits(
165     struct perf_plugins_dev_ctx *dev_ctx,
166     enum plugin_domain_type dom_type)
167 {
168     uint32_t *adj_max_limit_table, *adj_min_limit_table;
169     uint32_t *adj_min_lim_full_table, *adj_max_lim_full_table;
170     uint32_t needle_lim_max, needle_lim_min;
171     unsigned int phy_dom, phy_dom_idx;
172     uint32_t *level_table, *min_limit_table, *max_limit_table;
173 
174     adj_min_lim_full_table = perf_plugins_ctx.full_perf_table.adj_min_limit;
175     adj_max_lim_full_table = perf_plugins_ctx.full_perf_table.adj_max_limit;
176 
177     adj_max_limit_table = dev_ctx->perf_table.adj_max_limit;
178     adj_min_limit_table = dev_ctx->perf_table.adj_min_limit;
179 
180     if (dom_type != PERF_PLUGIN_DOM_TYPE_FULL) {
181         phy_dom = dev_ctx->log_dom_count;
182         level_table = dev_ctx->perf_table.level;
183         min_limit_table = dev_ctx->perf_table.min_limit;
184         max_limit_table = dev_ctx->perf_table.max_limit;
185 
186         /*
187          * Conservative approach for adjusted values:
188          * - pick the max for the min limit
189          * - pick the min for the max limit
190          */
191         needle_lim_min = min_limit_table[phy_dom];
192         needle_lim_max = max_limit_table[phy_dom];
193 
194         /* Find min/max on adjusted values, physical included */
195         for (unsigned int i = 0; i < (phy_dom + 1); i++) {
196             if (adj_min_limit_table[i] >= needle_lim_min) {
197                 needle_lim_min = adj_min_limit_table[i];
198             }
199 
200             if (adj_max_limit_table[i] <= needle_lim_max) {
201                 needle_lim_max = adj_max_limit_table[i];
202             }
203         }
204 
205         /*
206          * Now we apply the same policy against the temporary results for the
207          * plugins updates so far.
208          */
209         if (needle_lim_min > dev_ctx->lmin) {
210             dev_ctx->lmin = needle_lim_min;
211         }
212         if (needle_lim_max < dev_ctx->lmax) {
213             dev_ctx->lmax = needle_lim_max;
214         }
215 
216         dev_ctx->max = level_table[phy_dom];
217 
218         phy_dom_idx = fwk_id_get_element_idx(dev_ctx->perf_table.domain_id);
219         write_back_adj_values(dev_ctx, (size_t)phy_dom_idx);
220 
221     } else {
222         /*
223          * This needs to iterate over all the domains, since we are at the last
224          * domain and this plugin may have changed the limits for all the
225          * domains.
226          */
227         for (size_t i = 0; i < perf_plugins_ctx.dvfs_doms_count; i++) {
228             dev_ctx = perf_ph_get_ctx(FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, i));
229 
230             /* Update with adjusted min-max */
231             if (adj_min_lim_full_table[i] > dev_ctx->lmin) {
232                 dev_ctx->lmin = adj_min_lim_full_table[i];
233             }
234             if (adj_max_lim_full_table[i] < dev_ctx->lmax) {
235                 dev_ctx->lmax = adj_max_lim_full_table[i];
236             }
237 
238             write_back_adj_values(dev_ctx, i);
239         }
240     }
241 }
242 
plugins_policy_update(struct fc_perf_update * fc_update)243 static void plugins_policy_update(struct fc_perf_update *fc_update)
244 {
245     struct perf_plugins_dev_ctx *dev_ctx;
246 
247     dev_ctx = perf_ph_get_ctx(fc_update->domain_id);
248 
249     fc_update->level = dev_ctx->max;
250     fc_update->adj_min_limit = dev_ctx->lmin;
251     fc_update->adj_max_limit = are_limits_valid(dev_ctx->lmin, dev_ctx->lmax) ?
252         dev_ctx->lmax :
253         dev_ctx->lmin;
254 }
255 
plugins_policy_update_no_plugins(struct fc_perf_update * fc_update)256 static void plugins_policy_update_no_plugins(struct fc_perf_update *fc_update)
257 {
258     struct perf_plugins_dev_ctx *dev_ctx;
259     unsigned int phy_dom;
260 
261     dev_ctx = perf_ph_get_ctx(fc_update->domain_id);
262 
263     if (dev_ctx->log_dom_count == 1) {
264         /* No plugins, no logical domains */
265         fc_update->adj_max_limit = fc_update->max_limit;
266         fc_update->adj_min_limit = fc_update->min_limit;
267     } else {
268         /* No plugins, with logical domains */
269         phy_dom = dev_ctx->log_dom_count;
270 
271         fc_update->level = dev_ctx->perf_table.level[phy_dom];
272         fc_update->adj_max_limit = dev_ctx->perf_table.adj_max_limit[phy_dom];
273         fc_update->adj_min_limit = dev_ctx->perf_table.adj_min_limit[phy_dom];
274     }
275 }
276 
get_perf_table(struct perf_plugins_perf_update * dom,struct perf_plugins_dev_ctx * ctx,unsigned int dom_ix)277 static inline void get_perf_table(
278     struct perf_plugins_perf_update *dom,
279     struct perf_plugins_dev_ctx *ctx,
280     unsigned int dom_ix)
281 {
282     dom->level = &ctx->perf_table.level[dom_ix];
283     dom->max_limit = &ctx->perf_table.max_limit[dom_ix];
284     dom->adj_max_limit = &ctx->perf_table.adj_max_limit[dom_ix];
285     dom->min_limit = &ctx->perf_table.min_limit[dom_ix];
286     dom->adj_min_limit = &ctx->perf_table.adj_min_limit[dom_ix];
287 }
288 
assign_data_for_plugins(fwk_id_t id,struct perf_plugins_perf_update * snapshot,enum plugin_domain_type dom_type)289 static void assign_data_for_plugins(
290     fwk_id_t id,
291     struct perf_plugins_perf_update *snapshot,
292     enum plugin_domain_type dom_type)
293 {
294     struct perf_plugins_dev_ctx *dev_ctx;
295     unsigned int dom_idx;
296 
297     dev_ctx = perf_ph_get_ctx(id);
298 
299     /* Determine the first entry on the table */
300     if (dom_type == PERF_PLUGIN_DOM_TYPE_FULL) {
301         snapshot->level = &perf_plugins_ctx.full_perf_table.level[0];
302 
303         snapshot->max_limit = &perf_plugins_ctx.full_perf_table.max_limit[0];
304         snapshot->min_limit = &perf_plugins_ctx.full_perf_table.min_limit[0];
305 
306         snapshot->adj_max_limit =
307             &perf_plugins_ctx.full_perf_table.adj_max_limit[0];
308         snapshot->adj_min_limit =
309             &perf_plugins_ctx.full_perf_table.adj_min_limit[0];
310 
311         return;
312     } else if (dom_type == PERF_PLUGIN_DOM_TYPE_LOGICAL) {
313         /* Provide the beginning of the table */
314         dom_idx = 0;
315     } else {
316         /* Provide the last column of the table */
317         dom_idx = dev_ctx->log_dom_count;
318     }
319 
320     /* Alter the module identifier only */
321     snapshot->domain_id = FWK_ID_SUB_ELEMENT(
322         FWK_MODULE_IDX_SCMI_PERF,
323         fwk_id_get_element_idx(id),
324         fwk_id_get_sub_element_idx(id));
325 
326     get_perf_table(snapshot, dev_ctx, dom_idx);
327 }
328 
domain_aggregate(struct perf_plugins_perf_update * this_dom,struct perf_plugins_perf_update * phy_dom)329 static inline void domain_aggregate(
330     struct perf_plugins_perf_update *this_dom,
331     struct perf_plugins_perf_update *phy_dom)
332 {
333     /*
334      * Choose the best values of performance for the physical domain which would
335      * satisfy all the logical domains.
336      *
337      * - pick the max for the level
338      * - pick the max for the min limit
339      * - pick the min for the max limit
340      */
341     if (this_dom->level[0] > phy_dom->level[0]) {
342         phy_dom->level[0] = this_dom->level[0];
343     }
344 
345     if (this_dom->max_limit[0] < phy_dom->max_limit[0]) {
346         phy_dom->max_limit[0] = this_dom->max_limit[0];
347     }
348 
349     if (this_dom->min_limit[0] > phy_dom->min_limit[0]) {
350         phy_dom->min_limit[0] = this_dom->min_limit[0];
351     }
352 }
353 
store_and_aggregate(struct fc_perf_update * fc_update)354 static void store_and_aggregate(struct fc_perf_update *fc_update)
355 {
356     unsigned int this_dom_idx, phy_dom_idx;
357     struct perf_plugins_dev_ctx *dev_ctx;
358     struct perf_plugins_perf_update this_dom;
359     struct perf_plugins_perf_update phy_dom;
360     unsigned int phy_ix;
361 
362     dev_ctx = perf_ph_get_ctx(fc_update->domain_id);
363 
364     this_dom_idx = fwk_id_get_sub_element_idx(fc_update->domain_id);
365     phy_dom_idx = dev_ctx->log_dom_count;
366 
367     get_perf_table(&this_dom, dev_ctx, this_dom_idx);
368     get_perf_table(&phy_dom, dev_ctx, phy_dom_idx);
369 
370     this_dom.level[0] = fc_update->level;
371     this_dom.max_limit[0] = fc_update->max_limit;
372     this_dom.min_limit[0] = fc_update->min_limit;
373     this_dom.adj_max_limit[0] = fc_update->max_limit;
374     this_dom.adj_min_limit[0] = fc_update->min_limit;
375 
376     if (this_dom_idx == 0) {
377         /* Init max/min for physical domain at the 1st iteration */
378         phy_dom.level[0] = 0;
379         phy_dom.max_limit[0] = UINT32_MAX;
380         phy_dom.min_limit[0] = 0;
381 
382         /* as well as ctx */
383         dev_ctx->lmin = 0;
384         dev_ctx->lmax = UINT32_MAX;
385     }
386 
387     domain_aggregate(&this_dom, &phy_dom);
388 
389     phy_dom.adj_max_limit[0] = phy_dom.max_limit[0];
390     phy_dom.adj_min_limit[0] = phy_dom.min_limit[0];
391 
392     if (this_dom_idx == (phy_dom_idx - 1)) {
393         /* Copy values, all-performance table */
394         phy_ix = fwk_id_get_element_idx(fc_update->domain_id);
395 
396         perf_plugins_ctx.full_perf_table.level[phy_ix] = phy_dom.level[0];
397 
398         perf_plugins_ctx.full_perf_table.max_limit[phy_ix] =
399             phy_dom.max_limit[0];
400         perf_plugins_ctx.full_perf_table.min_limit[phy_ix] =
401             phy_dom.min_limit[0];
402 
403         perf_plugins_ctx.full_perf_table.adj_max_limit[phy_ix] =
404             phy_dom.adj_max_limit[0];
405         perf_plugins_ctx.full_perf_table.adj_min_limit[phy_ix] =
406             phy_dom.adj_min_limit[0];
407     }
408 }
409 
perf_plugins_handler_update(unsigned int perf_dom_idx,struct fc_perf_update * fc_update)410 void perf_plugins_handler_update(
411     unsigned int perf_dom_idx,
412     struct fc_perf_update *fc_update)
413 {
414     struct perf_plugins_api *api;
415     struct perf_plugins_perf_update perf_snapshot;
416     unsigned int this_dom_idx, last_logical_dom_idx;
417     struct perf_plugins_dev_ctx *dev_ctx;
418     enum plugin_domain_type dom_type;
419     const struct mod_scmi_perf_config *config;
420     int status;
421 
422     store_and_aggregate(fc_update);
423 
424     dev_ctx = perf_ph_get_ctx(fc_update->domain_id);
425 
426     this_dom_idx = fwk_id_get_sub_element_idx(fc_update->domain_id);
427     last_logical_dom_idx = dev_ctx->log_dom_count - 1;
428 
429     config = perf_plugins_ctx.config;
430 
431     /*
432      * EITHER last logical domain OR it's only physical domain,
433      * call the plugin - if any - with the data snapshot for this domain.
434      */
435     if ((this_dom_idx == last_logical_dom_idx) &&
436         (config->plugins_count != 0)) {
437         for (size_t i = 0; i < config->plugins_count; i++) {
438             dom_type = config->plugins[i].dom_type;
439 
440             if ((perf_dom_idx != (config->perf_doms_count - 1)) &&
441                 (dom_type == PERF_PLUGIN_DOM_TYPE_FULL)) {
442                 /*
443                  * For plugins that need the full snapshot, wait until the
444                  * last performance domain.
445                  */
446                 continue;
447             }
448 
449             assign_data_for_plugins(
450                 fc_update->domain_id, &perf_snapshot, dom_type);
451 
452             api = perf_plugins_ctx.plugins_api_table[i];
453             status = api->update(&perf_snapshot);
454             if (status != FWK_SUCCESS) {
455                 FWK_LOG_DEBUG(
456                     "[P-Handler] Update: Plugin%u returned error %i",
457                     (unsigned int)i,
458                     status);
459             }
460 
461             plugins_policy_sync_level_limits(dev_ctx, dom_type);
462         }
463     }
464 }
465 
perf_plugins_handler_get(unsigned int perf_dom_idx,struct fc_perf_update * fc_update)466 void perf_plugins_handler_get(
467     unsigned int perf_dom_idx,
468     struct fc_perf_update *fc_update)
469 {
470     unsigned int this_dom_idx, last_logical_dom_idx;
471     const struct mod_scmi_perf_config *config;
472     struct perf_plugins_dev_ctx *dev_ctx;
473 
474     dev_ctx = perf_ph_get_ctx(fc_update->domain_id);
475 
476     this_dom_idx = fwk_id_get_sub_element_idx(fc_update->domain_id);
477     last_logical_dom_idx = dev_ctx->log_dom_count - 1;
478 
479     config = perf_plugins_ctx.config;
480 
481     if (this_dom_idx != last_logical_dom_idx) {
482         /*
483          * Any other logical domain, no need to forward the request to DVFS,
484          * thus use previous values.
485          */
486         fc_update->level = dev_ctx->max;
487         fc_update->adj_max_limit = dev_ctx->lmax;
488         fc_update->adj_min_limit = dev_ctx->lmin;
489     } else {
490         if (config->plugins_count == 0) {
491             /* domains coordination only */
492             plugins_policy_update_no_plugins(fc_update);
493         } else {
494             plugins_policy_update(fc_update);
495         }
496     }
497 }
498 
perf_plugins_handler_report(struct perf_plugins_perf_report * data)499 void perf_plugins_handler_report(struct perf_plugins_perf_report *data)
500 {
501     struct perf_plugins_api *api;
502     int status;
503 
504     if (perf_plugins_ctx.config->plugins_count == 0) {
505         return;
506     }
507 
508     for (size_t i = 0; i < perf_plugins_ctx.config->plugins_count; i++) {
509         api = perf_plugins_ctx.plugins_api_table[i];
510 
511         if (api->report != NULL) {
512             status = api->report(data);
513             if (status != FWK_SUCCESS) {
514                 FWK_LOG_DEBUG(
515                     "[P-Handler] Report: Plugin%u returned error %i",
516                     (unsigned int)i,
517                     status);
518             }
519         }
520     }
521 }
522 
perf_plugins_alloc_tables(struct perf_plugins_perf_update * table,size_t count)523 static void perf_plugins_alloc_tables(
524     struct perf_plugins_perf_update *table,
525     size_t count)
526 {
527     table->level = fwk_mm_calloc(count, sizeof(uint32_t));
528     table->max_limit = fwk_mm_calloc(count, sizeof(uint32_t));
529     table->adj_max_limit = fwk_mm_calloc(count, sizeof(uint32_t));
530     table->min_limit = fwk_mm_calloc(count, sizeof(uint32_t));
531     table->adj_min_limit = fwk_mm_calloc(count, sizeof(uint32_t));
532 }
533 
perf_plugins_handler_init(const struct mod_scmi_perf_config * config)534 int perf_plugins_handler_init(const struct mod_scmi_perf_config *config)
535 {
536     const struct mod_scmi_perf_domain_config *domain;
537     int dvfs_doms_count;
538     struct perf_plugins_dev_ctx *dev_ctx;
539     unsigned int pgroup, ldom = 0;
540     size_t all_doms_count;
541     unsigned int phy_group;
542     bool has_phy_group;
543 
544     dvfs_doms_count =
545         fwk_module_get_element_count(FWK_ID_MODULE(FWK_MODULE_IDX_DVFS));
546     if (dvfs_doms_count <= 0) {
547         return FWK_E_SUPPORT;
548     }
549 
550     perf_plugins_ctx.dvfs_doms_count = (size_t)dvfs_doms_count;
551 
552     perf_plugins_ctx.dev_ctx = fwk_mm_calloc(
553         (size_t)dvfs_doms_count, sizeof(struct perf_plugins_dev_ctx));
554 
555     /*
556      * Iterate all over the SCMI performance domain table to build the number of
557      * logical domains that belong to the same physical domain.
558      */
559     for (size_t i = 0; i < config->perf_doms_count; i++) {
560         domain = &(*config->domains)[i];
561 
562         has_phy_group = fwk_optional_id_is_defined(domain->phy_group_id);
563         if (has_phy_group) {
564             phy_group = fwk_id_get_element_idx(domain->phy_group_id);
565         } else {
566             phy_group = i;
567         }
568 
569         dev_ctx = &perf_plugins_ctx.dev_ctx[phy_group];
570         dev_ctx->log_dom_count++;
571     }
572 
573     /*
574      * Allocate a table for each of the physical domain to contain the
575      * requested levels/limits by each of its logical domains.
576      * The size will be: number of logical domains + 1 (for physical domain).
577      *
578      * At the same time, initialise min and max.
579      */
580     for (size_t i = 0; i < perf_plugins_ctx.dvfs_doms_count; i++) {
581         dev_ctx = &perf_plugins_ctx.dev_ctx[i];
582 
583         all_doms_count = (size_t)(dev_ctx->log_dom_count + 1);
584 
585         perf_plugins_alloc_tables(&dev_ctx->perf_table, all_doms_count);
586 
587         dev_ctx->lmin = 0;
588         dev_ctx->lmax = UINT32_MAX;
589         dev_ctx->perf_table.domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, i);
590     }
591 
592     /*
593      * Allocate a table to store the physical/aggregated values for each of the
594      * physical domain for plugins that require the full picture of performance.
595      */
596     perf_plugins_alloc_tables(
597         &perf_plugins_ctx.full_perf_table, perf_plugins_ctx.dvfs_doms_count);
598 
599     /*
600      * The data provided for a full snapshot will always have a module
601      * identifier.
602      */
603     perf_plugins_ctx.full_perf_table.domain_id =
604         FWK_ID_MODULE(FWK_MODULE_IDX_SCMI_PERF);
605 
606     /* Allocate a table of dependency domains identifiers */
607     perf_plugins_ctx.dep_id_table =
608         fwk_mm_calloc(config->perf_doms_count, sizeof(fwk_id_t));
609 
610     domain = &(*config->domains)[0];
611 
612     /*
613      * Build the dependency domains.
614      *
615      * If we find the optional identifier for physical domain description,
616      * we assume that SCMI performance domains are grouped and ordered together
617      * for the same physical domain.
618      */
619     for (size_t j = 0; j < config->perf_doms_count; j++) {
620         has_phy_group = fwk_optional_id_is_defined(domain->phy_group_id);
621         if (has_phy_group) {
622             /* Custom mapping */
623             pgroup = fwk_id_get_element_idx(domain->phy_group_id);
624             domain = &(*config->domains)[j];
625 
626             if (fwk_id_get_element_idx(domain->phy_group_id) != pgroup) {
627                 /* Restart count of logical domains */
628                 ldom = 0;
629             }
630 
631             perf_plugins_ctx.dep_id_table[j] = FWK_ID_SUB_ELEMENT(
632                 FWK_MODULE_IDX_DVFS,
633                 fwk_id_get_element_idx(domain->phy_group_id),
634                 ldom);
635 
636             ldom++;
637         } else {
638             /* Direct implicit mapping to dvfs */
639             perf_plugins_ctx.dep_id_table[j] =
640                 FWK_ID_SUB_ELEMENT(FWK_MODULE_IDX_DVFS, j, 0);
641         }
642     }
643 
644     perf_plugins_ctx.config = config;
645 
646     return FWK_SUCCESS;
647 }
648 
perf_plugins_handler_bind(void)649 int perf_plugins_handler_bind(void)
650 {
651     fwk_id_t plugin_id;
652     const struct mod_scmi_perf_config *config = perf_plugins_ctx.config;
653     int status;
654 
655     if (config->plugins_count == 0) {
656         return FWK_SUCCESS;
657     }
658 
659     perf_plugins_ctx.plugins_api_table =
660         fwk_mm_calloc(config->plugins_count, sizeof(struct perf_plugins_api *));
661 
662     /* Bind to all the performance plugins */
663     for (size_t i = 0; i < config->plugins_count; i++) {
664         plugin_id = config->plugins[i].id;
665 
666         status = fwk_module_bind(
667             plugin_id,
668             FWK_ID_API(
669                 fwk_id_get_module_idx(plugin_id), MOD_SCMI_PERF_PLUGIN_API),
670             &perf_plugins_ctx.plugins_api_table[i]);
671         if (status != FWK_SUCCESS) {
672             return FWK_E_PARAM;
673         }
674     }
675 
676     return FWK_SUCCESS;
677 }
678 
perf_plugins_handler_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)679 int perf_plugins_handler_process_bind_request(
680     fwk_id_t source_id,
681     fwk_id_t target_id,
682     fwk_id_t api_id,
683     const void **api)
684 {
685     *api = &handler_api;
686 
687     return FWK_SUCCESS;
688 }
689 #endif /* BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER */
690