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_mod_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_mod_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 domains, 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             dev_ctx->max = perf_plugins_ctx.full_perf_table.level[i];
239 
240             write_back_adj_values(dev_ctx, i);
241         }
242     }
243 }
244 
plugins_policy_update(struct fc_perf_update * fc_update)245 static void plugins_policy_update(struct fc_perf_update *fc_update)
246 {
247     struct perf_plugins_dev_ctx *dev_ctx;
248 
249     dev_ctx = perf_ph_get_ctx(fc_update->domain_id);
250 
251     fc_update->level = dev_ctx->max;
252     fc_update->adj_min_limit = dev_ctx->lmin;
253     fc_update->adj_max_limit = are_limits_valid(dev_ctx->lmin, dev_ctx->lmax) ?
254         dev_ctx->lmax :
255         dev_ctx->lmin;
256 }
257 
plugins_policy_update_no_plugins(struct fc_perf_update * fc_update)258 static void plugins_policy_update_no_plugins(struct fc_perf_update *fc_update)
259 {
260     struct perf_plugins_dev_ctx *dev_ctx;
261     unsigned int phy_dom;
262 
263     dev_ctx = perf_ph_get_ctx(fc_update->domain_id);
264 
265     if (dev_ctx->log_dom_count == 1) {
266         /* No plugins, no logical domains */
267         fc_update->adj_max_limit = fc_update->max_limit;
268         fc_update->adj_min_limit = fc_update->min_limit;
269     } else {
270         /* No plugins, with logical domains */
271         phy_dom = dev_ctx->log_dom_count;
272 
273         fc_update->level = dev_ctx->perf_table.level[phy_dom];
274         fc_update->adj_max_limit = dev_ctx->perf_table.adj_max_limit[phy_dom];
275         fc_update->adj_min_limit = dev_ctx->perf_table.adj_min_limit[phy_dom];
276     }
277 }
278 
get_perf_table(struct perf_plugins_perf_update * dom,struct perf_plugins_dev_ctx * ctx,unsigned int dom_ix)279 static inline void get_perf_table(
280     struct perf_plugins_perf_update *dom,
281     struct perf_plugins_dev_ctx *ctx,
282     unsigned int dom_ix)
283 {
284     dom->level = &ctx->perf_table.level[dom_ix];
285     dom->max_limit = &ctx->perf_table.max_limit[dom_ix];
286     dom->adj_max_limit = &ctx->perf_table.adj_max_limit[dom_ix];
287     dom->min_limit = &ctx->perf_table.min_limit[dom_ix];
288     dom->adj_min_limit = &ctx->perf_table.adj_min_limit[dom_ix];
289 }
290 
assign_data_for_plugins(fwk_id_t id,struct perf_plugins_perf_update * snapshot,enum plugin_domain_type dom_type)291 static void assign_data_for_plugins(
292     fwk_id_t id,
293     struct perf_plugins_perf_update *snapshot,
294     enum plugin_domain_type dom_type)
295 {
296     struct perf_plugins_dev_ctx *dev_ctx;
297     unsigned int dom_idx;
298 
299     dev_ctx = perf_ph_get_ctx(id);
300 
301     /* Determine the first entry on the table */
302     if (dom_type == PERF_PLUGIN_DOM_TYPE_FULL) {
303         snapshot->level = &perf_plugins_ctx.full_perf_table.level[0];
304 
305         snapshot->max_limit = &perf_plugins_ctx.full_perf_table.max_limit[0];
306         snapshot->min_limit = &perf_plugins_ctx.full_perf_table.min_limit[0];
307 
308         snapshot->adj_max_limit =
309             &perf_plugins_ctx.full_perf_table.adj_max_limit[0];
310         snapshot->adj_min_limit =
311             &perf_plugins_ctx.full_perf_table.adj_min_limit[0];
312 
313         return;
314     } else if (dom_type == PERF_PLUGIN_DOM_TYPE_LOGICAL) {
315         /* Provide the beginning of the table */
316         dom_idx = 0;
317     } else {
318         /* Provide the last column of the table */
319         dom_idx = dev_ctx->log_dom_count;
320     }
321 
322     /* Alter the module identifier only */
323     snapshot->domain_id = FWK_ID_SUB_ELEMENT(
324         FWK_MODULE_IDX_SCMI_PERF,
325         fwk_id_get_element_idx(id),
326         fwk_id_get_sub_element_idx(id));
327 
328     get_perf_table(snapshot, dev_ctx, dom_idx);
329 }
330 
domain_aggregate(struct perf_plugins_perf_update * this_dom,struct perf_plugins_perf_update * phy_dom)331 static inline void domain_aggregate(
332     struct perf_plugins_perf_update *this_dom,
333     struct perf_plugins_perf_update *phy_dom)
334 {
335     /*
336      * Choose the best values of performance for the physical domain which would
337      * satisfy all the logical domains.
338      *
339      * - pick the max for the level
340      * - pick the max for the min limit
341      * - pick the min for the max limit
342      */
343     if (this_dom->level[0] > phy_dom->level[0]) {
344         phy_dom->level[0] = this_dom->level[0];
345     }
346 
347     if (this_dom->max_limit[0] < phy_dom->max_limit[0]) {
348         phy_dom->max_limit[0] = this_dom->max_limit[0];
349     }
350 
351     if (this_dom->min_limit[0] > phy_dom->min_limit[0]) {
352         phy_dom->min_limit[0] = this_dom->min_limit[0];
353     }
354 }
355 
store_and_aggregate(struct fc_perf_update * fc_update)356 static void store_and_aggregate(struct fc_perf_update *fc_update)
357 {
358     unsigned int this_dom_idx, phy_dom_idx;
359     struct perf_plugins_dev_ctx *dev_ctx;
360     struct perf_plugins_perf_update this_dom;
361     struct perf_plugins_perf_update phy_dom;
362     unsigned int phy_ix;
363 
364     dev_ctx = perf_ph_get_ctx(fc_update->domain_id);
365 
366     this_dom_idx = fwk_id_get_sub_element_idx(fc_update->domain_id);
367     phy_dom_idx = dev_ctx->log_dom_count;
368 
369     get_perf_table(&this_dom, dev_ctx, this_dom_idx);
370     get_perf_table(&phy_dom, dev_ctx, phy_dom_idx);
371 
372     this_dom.level[0] = fc_update->level;
373     this_dom.max_limit[0] = fc_update->max_limit;
374     this_dom.min_limit[0] = fc_update->min_limit;
375     this_dom.adj_max_limit[0] = fc_update->max_limit;
376     this_dom.adj_min_limit[0] = fc_update->min_limit;
377 
378     if (this_dom_idx == 0) {
379         /* Init max/min for physical domain at the 1st iteration */
380         phy_dom.level[0] = 0;
381         phy_dom.max_limit[0] = UINT32_MAX;
382         phy_dom.min_limit[0] = 0;
383 
384         /* as well as ctx */
385         dev_ctx->lmin = 0;
386         dev_ctx->lmax = UINT32_MAX;
387     }
388 
389     domain_aggregate(&this_dom, &phy_dom);
390 
391     phy_dom.adj_max_limit[0] = phy_dom.max_limit[0];
392     phy_dom.adj_min_limit[0] = phy_dom.min_limit[0];
393 
394     if (this_dom_idx == (phy_dom_idx - 1)) {
395         /* Copy values, all-performance table */
396         phy_ix = fwk_id_get_element_idx(fc_update->domain_id);
397 
398         perf_plugins_ctx.full_perf_table.level[phy_ix] = phy_dom.level[0];
399 
400         perf_plugins_ctx.full_perf_table.max_limit[phy_ix] =
401             phy_dom.max_limit[0];
402         perf_plugins_ctx.full_perf_table.min_limit[phy_ix] =
403             phy_dom.min_limit[0];
404 
405         perf_plugins_ctx.full_perf_table.adj_max_limit[phy_ix] =
406             phy_dom.adj_max_limit[0];
407         perf_plugins_ctx.full_perf_table.adj_min_limit[phy_ix] =
408             phy_dom.adj_min_limit[0];
409     }
410 }
411 
perf_plugins_handler_update(unsigned int perf_dom_idx,struct fc_perf_update * fc_update)412 void perf_plugins_handler_update(
413     unsigned int perf_dom_idx,
414     struct fc_perf_update *fc_update)
415 {
416     struct perf_plugins_api *plugin_api;
417     struct perf_plugins_perf_update perf_snapshot;
418     unsigned int this_dom_idx, last_logical_dom_idx;
419     struct perf_plugins_dev_ctx *dev_ctx;
420     enum plugin_domain_type dom_type;
421     const struct mod_scmi_perf_config *config;
422     int status;
423 
424     store_and_aggregate(fc_update);
425 
426     dev_ctx = perf_ph_get_ctx(fc_update->domain_id);
427 
428     this_dom_idx = fwk_id_get_sub_element_idx(fc_update->domain_id);
429     last_logical_dom_idx = dev_ctx->log_dom_count - 1;
430 
431     config = perf_plugins_ctx.config;
432 
433     /*
434      * EITHER last logical domain OR it's only physical domain,
435      * call the plugin - if any - with the data snapshot for this domain.
436      */
437     if (this_dom_idx == last_logical_dom_idx) {
438         if (config->plugins_count == 0) {
439             /* coordination-only */
440             plugins_policy_sync_level_limits(
441                 dev_ctx, PERF_PLUGIN_DOM_TYPE_LOGICAL);
442             return;
443         }
444 
445         for (size_t i = 0; i < config->plugins_count; i++) {
446             dom_type = config->plugins[i].dom_type;
447 
448             if ((perf_dom_idx != (config->perf_doms_count - 1)) &&
449                 (dom_type == PERF_PLUGIN_DOM_TYPE_FULL)) {
450                 /*
451                  * For plugins that need the full snapshot, wait until the
452                  * last performance domain.
453                  */
454                 continue;
455             }
456 
457             assign_data_for_plugins(
458                 fc_update->domain_id, &perf_snapshot, dom_type);
459 
460             plugin_api = perf_plugins_ctx.plugins_api_table[i];
461             status = plugin_api->update(&perf_snapshot);
462             if (status != FWK_SUCCESS) {
463                 FWK_LOG_DEBUG(
464                     "[P-Handler] Update: Plugin%u returned error %i",
465                     (unsigned int)i,
466                     status);
467             }
468 
469             plugins_policy_sync_level_limits(dev_ctx, dom_type);
470         }
471     }
472 }
473 
perf_plugins_handler_get(unsigned int perf_dom_idx,struct fc_perf_update * fc_update)474 void perf_plugins_handler_get(
475     unsigned int perf_dom_idx,
476     struct fc_perf_update *fc_update)
477 {
478     unsigned int this_dom_idx, last_logical_dom_idx;
479     const struct mod_scmi_perf_config *config;
480     struct perf_plugins_dev_ctx *dev_ctx;
481 
482     dev_ctx = perf_ph_get_ctx(fc_update->domain_id);
483 
484     this_dom_idx = fwk_id_get_sub_element_idx(fc_update->domain_id);
485     last_logical_dom_idx = dev_ctx->log_dom_count - 1;
486 
487     if (this_dom_idx != last_logical_dom_idx) {
488         /*
489          * Any other logical domain, no need to forward the request to DVFS,
490          * thus use previous values.
491          */
492         fc_update->level = dev_ctx->max;
493         fc_update->adj_max_limit = dev_ctx->lmax;
494         fc_update->adj_min_limit = dev_ctx->lmin;
495     } else {
496         config = perf_plugins_ctx.config;
497 
498         if (config->plugins_count == 0) {
499             /* domains coordination only */
500             plugins_policy_update_no_plugins(fc_update);
501         } else {
502             plugins_policy_update(fc_update);
503         }
504     }
505 }
506 
perf_plugins_handler_report(struct perf_plugins_perf_report * data)507 void perf_plugins_handler_report(struct perf_plugins_perf_report *data)
508 {
509     const struct mod_scmi_perf_config *config;
510     struct perf_plugins_api *plugin_api;
511     int status;
512 
513     config = perf_plugins_ctx.config;
514 
515     if (config->plugins_count == 0) {
516         return;
517     }
518 
519     for (size_t i = 0; i < config->plugins_count; i++) {
520         plugin_api = perf_plugins_ctx.plugins_api_table[i];
521 
522         if (plugin_api->report != NULL) {
523             status = plugin_api->report(data);
524             if (status != FWK_SUCCESS) {
525                 FWK_LOG_DEBUG(
526                     "[P-Handler] Report: Plugin%u returned error %i",
527                     (unsigned int)i,
528                     status);
529             }
530         }
531     }
532 }
533 
perf_plugins_alloc_tables(struct perf_plugins_perf_update * table,size_t count)534 static void perf_plugins_alloc_tables(
535     struct perf_plugins_perf_update *table,
536     size_t count)
537 {
538     table->level = fwk_mm_calloc(count, sizeof(uint32_t));
539     table->max_limit = fwk_mm_calloc(count, sizeof(uint32_t));
540     table->adj_max_limit = fwk_mm_calloc(count, sizeof(uint32_t));
541     table->min_limit = fwk_mm_calloc(count, sizeof(uint32_t));
542     table->adj_min_limit = fwk_mm_calloc(count, sizeof(uint32_t));
543 }
544 
perf_plugins_handler_init(const struct mod_scmi_perf_config * config)545 int perf_plugins_handler_init(const struct mod_scmi_perf_config *config)
546 {
547     const struct mod_scmi_perf_domain_config *domain;
548     int dvfs_doms_count;
549     struct perf_plugins_dev_ctx *dev_ctx;
550     unsigned int pgroup, ldom = 0;
551     size_t all_doms_count;
552     unsigned int phy_group;
553     bool has_phy_group;
554 
555     dvfs_doms_count =
556         fwk_module_get_element_count(FWK_ID_MODULE(FWK_MODULE_IDX_DVFS));
557     if (dvfs_doms_count <= 0) {
558         return FWK_E_SUPPORT;
559     }
560 
561     perf_plugins_ctx.dvfs_doms_count = (size_t)dvfs_doms_count;
562 
563     perf_plugins_ctx.dev_ctx = fwk_mm_calloc(
564         (size_t)dvfs_doms_count, sizeof(struct perf_plugins_dev_ctx));
565 
566     /*
567      * Iterate all over the SCMI performance domain table to build the number of
568      * logical domains that belong to the same physical domain.
569      */
570     for (size_t i = 0; i < config->perf_doms_count; i++) {
571         domain = &(*config->domains)[i];
572 
573         has_phy_group = fwk_optional_id_is_defined(domain->phy_group_id);
574         if (has_phy_group) {
575             phy_group = fwk_id_get_element_idx(domain->phy_group_id);
576         } else {
577             phy_group = i;
578         }
579 
580         dev_ctx = &perf_plugins_ctx.dev_ctx[phy_group];
581         dev_ctx->log_dom_count++;
582     }
583 
584     /*
585      * Allocate a table for each of the physical domain to contain the
586      * requested levels/limits by each of its logical domains.
587      * The size will be: number of logical domains + 1 (for physical domain).
588      *
589      * At the same time, initialise min and max.
590      */
591     for (size_t i = 0; i < perf_plugins_ctx.dvfs_doms_count; i++) {
592         dev_ctx = &perf_plugins_ctx.dev_ctx[i];
593 
594         all_doms_count = (size_t)(dev_ctx->log_dom_count + 1);
595 
596         perf_plugins_alloc_tables(&dev_ctx->perf_table, all_doms_count);
597 
598         dev_ctx->lmin = 0;
599         dev_ctx->lmax = UINT32_MAX;
600         dev_ctx->perf_table.domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, i);
601     }
602 
603     /*
604      * Allocate a table to store the physical/aggregated values for each of the
605      * physical domain for plugins that require the full picture of performance.
606      */
607     perf_plugins_alloc_tables(
608         &perf_plugins_ctx.full_perf_table, perf_plugins_ctx.dvfs_doms_count);
609 
610     /*
611      * The data provided for a full snapshot will always have a module
612      * identifier.
613      */
614     perf_plugins_ctx.full_perf_table.domain_id =
615         FWK_ID_MODULE(FWK_MODULE_IDX_SCMI_PERF);
616 
617     /* Allocate a table of dependency domains identifiers */
618     perf_plugins_ctx.dep_id_table =
619         fwk_mm_calloc(config->perf_doms_count, sizeof(fwk_id_t));
620 
621     domain = &(*config->domains)[0];
622 
623     /*
624      * Build the dependency domains.
625      *
626      * If we find the optional identifier for physical domain description,
627      * we assume that SCMI performance domains are grouped and ordered together
628      * for the same physical domain.
629      */
630     for (size_t j = 0; j < config->perf_doms_count; j++) {
631         has_phy_group = fwk_optional_id_is_defined(domain->phy_group_id);
632         if (has_phy_group) {
633             /* Custom mapping */
634             pgroup = fwk_id_get_element_idx(domain->phy_group_id);
635             domain = &(*config->domains)[j];
636 
637             if (fwk_id_get_element_idx(domain->phy_group_id) != pgroup) {
638                 /* Restart count of logical domains */
639                 ldom = 0;
640             }
641 
642             perf_plugins_ctx.dep_id_table[j] = FWK_ID_SUB_ELEMENT(
643                 FWK_MODULE_IDX_DVFS,
644                 fwk_id_get_element_idx(domain->phy_group_id),
645                 ldom);
646 
647             ldom++;
648         } else {
649             /* Direct implicit mapping to dvfs */
650             perf_plugins_ctx.dep_id_table[j] =
651                 FWK_ID_SUB_ELEMENT(FWK_MODULE_IDX_DVFS, j, 0);
652         }
653     }
654 
655     perf_plugins_ctx.config = config;
656 
657     return FWK_SUCCESS;
658 }
659 
perf_plugins_handler_bind(void)660 int perf_plugins_handler_bind(void)
661 {
662     fwk_id_t plugin_id;
663     const struct mod_scmi_perf_config *config = perf_plugins_ctx.config;
664     int status;
665 
666     if (config->plugins_count == 0) {
667         return FWK_SUCCESS;
668     }
669 
670     perf_plugins_ctx.plugins_api_table =
671         fwk_mm_calloc(config->plugins_count, sizeof(struct perf_plugins_api *));
672 
673     /* Bind to all the performance plugins */
674     for (size_t i = 0; i < config->plugins_count; i++) {
675         plugin_id = config->plugins[i].id;
676 
677         status = fwk_module_bind(
678             plugin_id,
679             FWK_ID_API(
680                 fwk_id_get_module_idx(plugin_id), MOD_SCMI_PERF_PLUGIN_API),
681             &perf_plugins_ctx.plugins_api_table[i]);
682         if (status != FWK_SUCCESS) {
683             return FWK_E_PARAM;
684         }
685     }
686 
687     return FWK_SUCCESS;
688 }
689 
perf_plugins_handler_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** ph_process_bind_request_api)690 int perf_plugins_handler_process_bind_request(
691     fwk_id_t source_id,
692     fwk_id_t target_id,
693     fwk_id_t api_id,
694     const void **ph_process_bind_request_api)
695 {
696     *ph_process_bind_request_api = &handler_api;
697 
698     return FWK_SUCCESS;
699 }
700 #endif /* BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER */
701