1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2015-2023, 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 #include <mod_scmi.h>
15 #include <mod_scmi_perf.h>
16 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
17 #    include "perf_plugins_handler.h"
18 #endif
19 #include <mod_timer.h>
20 
21 #include <fwk_assert.h>
22 #include <fwk_core.h>
23 #include <fwk_event.h>
24 #include <fwk_id.h>
25 #include <fwk_log.h>
26 #include <fwk_macros.h>
27 #include <fwk_mm.h>
28 #include <fwk_module.h>
29 #include <fwk_module_idx.h>
30 #include <fwk_status.h>
31 #include <fwk_string.h>
32 
33 #include <stdbool.h>
34 #include <stddef.h>
35 
36 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
37 #    include <mod_resource_perms.h>
38 #endif
39 
40 #ifdef BUILD_HAS_MOD_STATISTICS
41 #    include <mod_stats.h>
42 #endif
43 
44 #define MOD_SCMI_PERF_NOTIFICATION_COUNT 2
45 
46 static int scmi_perf_protocol_version_handler(
47     fwk_id_t service_id, const uint32_t *payload);
48 static int scmi_perf_protocol_attributes_handler(
49     fwk_id_t service_id, const uint32_t *payload);
50 static int scmi_perf_protocol_message_attributes_handler(
51     fwk_id_t service_id, const uint32_t *payload);
52 static int scmi_perf_domain_attributes_handler(
53     fwk_id_t service_id, const uint32_t *payload);
54 static int scmi_perf_describe_levels_handler(
55     fwk_id_t service_id, const uint32_t *payload);
56 static int scmi_perf_level_set_handler(
57     fwk_id_t service_id, const uint32_t *payload);
58 static int scmi_perf_level_get_handler(
59     fwk_id_t service_id, const uint32_t *payload);
60 static int scmi_perf_limits_set_handler(
61     fwk_id_t service_id, const uint32_t *payload);
62 static int scmi_perf_limits_get_handler(
63     fwk_id_t service_id, const uint32_t *payload);
64 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
65 static int scmi_perf_describe_fast_channels(
66     fwk_id_t service_id, const uint32_t *payload);
67 #endif
68 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
69 static int scmi_perf_limits_notify(
70     fwk_id_t service_id, const uint32_t *payload);
71 static int scmi_perf_level_notify(
72     fwk_id_t service_id, const uint32_t *payload);
73 #endif
74 
75 /* Forward declaration */
76 static void scmi_perf_notify_limits_updated(
77     fwk_id_t domain_id,
78     uint32_t range_min,
79     uint32_t range_max);
80 
81 static int (
82     *handler_table[MOD_SCMI_PERF_COMMAND_COUNT])(fwk_id_t, const uint32_t *) = {
83     [MOD_SCMI_PROTOCOL_VERSION] = scmi_perf_protocol_version_handler,
84     [MOD_SCMI_PROTOCOL_ATTRIBUTES] = scmi_perf_protocol_attributes_handler,
85     [MOD_SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
86         scmi_perf_protocol_message_attributes_handler,
87     [MOD_SCMI_PERF_DOMAIN_ATTRIBUTES] = scmi_perf_domain_attributes_handler,
88     [MOD_SCMI_PERF_DESCRIBE_LEVELS] = scmi_perf_describe_levels_handler,
89     [MOD_SCMI_PERF_LIMITS_SET] = scmi_perf_limits_set_handler,
90     [MOD_SCMI_PERF_LIMITS_GET] = scmi_perf_limits_get_handler,
91     [MOD_SCMI_PERF_LEVEL_SET] = scmi_perf_level_set_handler,
92     [MOD_SCMI_PERF_LEVEL_GET] = scmi_perf_level_get_handler,
93 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
94     [MOD_SCMI_PERF_DESCRIBE_FAST_CHANNEL] = scmi_perf_describe_fast_channels,
95 #endif
96 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
97     [MOD_SCMI_PERF_NOTIFY_LIMITS] = scmi_perf_limits_notify,
98     [MOD_SCMI_PERF_NOTIFY_LEVEL] = scmi_perf_level_notify
99 #endif
100 };
101 
102 static unsigned int payload_size_table[MOD_SCMI_PERF_COMMAND_COUNT] = {
103     [MOD_SCMI_PROTOCOL_VERSION] = 0,
104     [MOD_SCMI_PROTOCOL_ATTRIBUTES] = 0,
105     [MOD_SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
106         (unsigned int)sizeof(struct scmi_protocol_message_attributes_a2p),
107     [MOD_SCMI_PERF_DOMAIN_ATTRIBUTES] =
108         (unsigned int)sizeof(struct scmi_perf_domain_attributes_a2p),
109     [MOD_SCMI_PERF_DESCRIBE_LEVELS] =
110         (unsigned int)sizeof(struct scmi_perf_describe_levels_a2p),
111     [MOD_SCMI_PERF_LEVEL_SET] =
112         (unsigned int)sizeof(struct scmi_perf_level_set_a2p),
113     [MOD_SCMI_PERF_LEVEL_GET] =
114         (unsigned int)sizeof(struct scmi_perf_level_get_a2p),
115     [MOD_SCMI_PERF_LIMITS_SET] =
116         (unsigned int)sizeof(struct scmi_perf_limits_set_a2p),
117     [MOD_SCMI_PERF_LIMITS_GET] =
118         (unsigned int)sizeof(struct scmi_perf_limits_get_a2p),
119 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
120     [MOD_SCMI_PERF_DESCRIBE_FAST_CHANNEL] =
121         sizeof(struct scmi_perf_describe_fc_a2p),
122 #endif
123 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
124     [MOD_SCMI_PERF_NOTIFY_LIMITS] = sizeof(struct scmi_perf_notify_limits_a2p),
125     [MOD_SCMI_PERF_NOTIFY_LEVEL] = sizeof(struct scmi_perf_notify_level_a2p)
126 #endif
127 };
128 
129 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
130 static unsigned int fast_channel_elem_size[] = {
131     [MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET] = sizeof(uint32_t),
132     [MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_SET] =
133         sizeof(struct mod_scmi_perf_fast_channel_limit),
134     [MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET] = sizeof(uint32_t),
135     [MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_GET] =
136         sizeof(struct mod_scmi_perf_fast_channel_limit)
137 };
138 #endif
139 
140 struct perf_operations {
141     /*
142      * Service identifier currently requesting operation.
143      * A 'none' value means that there is no pending request.
144      */
145     fwk_id_t service_id;
146 };
147 
148 struct perf_opp_table {
149     /* Pointer to DVFS OPP table */
150     struct mod_dvfs_opp *opps;
151 
152     /* Number of OPPs */
153     size_t opp_count;
154 
155     /* The DVFS identifier for this OPP table */
156     fwk_id_t dvfs_id;
157 };
158 
159 /*!
160  * \brief Domain context.
161  */
162 struct scmi_perf_domain_ctx {
163     /* Current limits */
164     struct mod_scmi_perf_level_limits level_limits;
165 
166     /* Current level */
167     uint32_t curr_level;
168 
169     /* Tables of OPPs */
170     struct perf_opp_table *opp_table;
171 };
172 
173 struct mod_scmi_perf_ctx {
174     /* SCMI Performance Module Configuration */
175     const struct mod_scmi_perf_config *config;
176 
177     /* Number of performance domains */
178     uint32_t domain_count;
179 
180     /* SCMI module API */
181     const struct mod_scmi_from_protocol_api *scmi_api;
182 
183     /* DVFS module API */
184     const struct mod_dvfs_domain_api *dvfs_api;
185 
186 #ifdef BUILD_HAS_MOD_STATISTICS
187     /* Statistics module API */
188     const struct mod_stats_api *stats_api;
189 #endif
190 
191     /* Pointer to a table of operations */
192     struct perf_operations *perf_ops_table;
193 
194 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
195     /* Number of active agents */
196     unsigned int agent_count;
197 
198     /* SCMI notification API */
199     const struct mod_scmi_notification_api *scmi_notification_api;
200 #endif
201 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
202     /* Alarm API for fast channels */
203     const struct mod_timer_alarm_api *fc_alarm_api;
204 
205     /* Alarm for fast channels */
206     fwk_id_t fast_channels_alarm_id;
207 
208     /* Fast Channels Polling Rate Limit */
209     uint32_t fast_channels_rate_limit;
210 
211     /* Fast Channels process number of pending requests */
212     volatile uint32_t fch_pending_req_count;
213 #endif
214 
215 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
216     /* SCMI Resource Permissions API */
217     const struct mod_res_permissions_api *res_perms_api;
218 #endif
219 
220     struct scmi_perf_domain_ctx *domain_ctx_table;
221 
222     struct perf_opp_table *opp_table;
223 
224     unsigned int dvfs_doms_count;
225 };
226 
227 static struct mod_scmi_perf_ctx scmi_perf_ctx;
228 
229 /* Event indices */
230 enum scmi_perf_event_idx {
231     SCMI_PERF_EVENT_IDX_LEVEL_GET_REQUEST,
232 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
233     SCMI_PERF_EVENT_IDX_FAST_CHANNELS_PROCESS,
234 #endif
235     SCMI_PERF_EVENT_IDX_COUNT,
236 };
237 
238 struct scmi_perf_event_parameters {
239     fwk_id_t domain_id;
240 };
241 
242 static const fwk_id_t scmi_perf_get_level =
243     FWK_ID_EVENT_INIT(FWK_MODULE_IDX_SCMI_PERF,
244                       SCMI_PERF_EVENT_IDX_LEVEL_GET_REQUEST);
245 
246 /*
247  * SCMI PERF Helpers
248  */
249 
250 /* This identifier is either:
251  * - the element type version of the one built by the perf-plugins-handler
252  *      (sub-element type)
253  * - or the DVFS domain
254  */
get_dvfs_dependency_id(unsigned int el_idx)255 static inline fwk_id_t get_dvfs_dependency_id(unsigned int el_idx)
256 {
257 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
258     fwk_id_t id;
259 
260     id = perf_plugins_get_dependency_id(el_idx);
261     return fwk_id_build_element_id(id, el_idx);
262 #else
263     return FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, el_idx);
264 #endif
265 }
266 
267 /* This identifier is either:
268  * - exactly the one built by the perf-plugins-handler (sub-element type)
269  * - or the DVFS domain
270  */
get_dependency_id(unsigned int el_idx)271 static inline fwk_id_t get_dependency_id(unsigned int el_idx)
272 {
273 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
274     return perf_plugins_get_dependency_id(el_idx);
275 #else
276     return FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, el_idx);
277 #endif
278 }
279 
get_ctx(fwk_id_t domain_id)280 static inline struct scmi_perf_domain_ctx *get_ctx(fwk_id_t domain_id)
281 {
282     return &scmi_perf_ctx.domain_ctx_table[fwk_id_get_element_idx(domain_id)];
283 }
284 
285 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
get_fc_set_limit_addr(const struct mod_scmi_perf_domain_config * domain)286 static inline struct mod_scmi_perf_fast_channel_limit *get_fc_set_limit_addr(
287     const struct mod_scmi_perf_domain_config *domain)
288 {
289     return (struct mod_scmi_perf_fast_channel_limit
290                 *)((uintptr_t)domain->fast_channels_addr_scp
291                        [MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_SET]);
292 }
get_fc_set_level_addr(const struct mod_scmi_perf_domain_config * domain)293 static inline uint32_t *get_fc_set_level_addr(
294     const struct mod_scmi_perf_domain_config *domain)
295 {
296     return (uint32_t *)((uintptr_t)domain->fast_channels_addr_scp
297                             [MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET]);
298 }
299 #endif
300 
opp_for_level_found(uint32_t * level,struct perf_opp_table * opp_table,size_t i)301 static inline int opp_for_level_found(
302     uint32_t *level,
303     struct perf_opp_table *opp_table,
304     size_t i)
305 {
306     *level = opp_table->opps[i].level;
307 
308     return FWK_SUCCESS;
309 }
310 
find_opp_for_level(struct scmi_perf_domain_ctx * domain_ctx,uint32_t * level,bool use_nearest)311 static int find_opp_for_level(
312     struct scmi_perf_domain_ctx *domain_ctx,
313     uint32_t *level,
314     bool use_nearest)
315 {
316     struct perf_opp_table *opp_table;
317     size_t i;
318     uint32_t opp_level, limit_max;
319 
320     opp_table = domain_ctx->opp_table;
321     limit_max = domain_ctx->level_limits.maximum;
322 
323     for (i = 0; i < opp_table->opp_count; i++) {
324         opp_level = opp_table->opps[i].level;
325 
326         if ((use_nearest &&
327              ((opp_level < *level) && (opp_level < limit_max))) ||
328             (!use_nearest && (opp_level != *level))) {
329             /*
330              * The current OPP level is either below the desired level
331              * or not exact match found.
332              */
333             continue;
334         } else {
335             /*
336              * Either found exact match, or the current OPP is above the limit.
337              * Must be within limits.
338              */
339             if ((opp_level > limit_max) && (i > 0)) {
340                 i--;
341             }
342 
343             return opp_for_level_found(level, opp_table, i);
344         }
345     }
346 
347     /* Either not exact match or approximate to the highest level */
348     if (use_nearest) {
349         i--;
350 
351         return opp_for_level_found(level, opp_table, i);
352     }
353 
354     return FWK_E_RANGE;
355 }
356 
perf_set_level(fwk_id_t domain_id,unsigned int agent_id,uint32_t perf_level)357 static int perf_set_level(
358     fwk_id_t domain_id,
359     unsigned int agent_id,
360     uint32_t perf_level)
361 {
362     struct scmi_perf_domain_ctx *domain_ctx;
363     int status;
364 
365     domain_ctx = get_ctx(domain_id);
366 
367     status = find_opp_for_level(
368         domain_ctx, &perf_level, scmi_perf_ctx.config->approximate_level);
369     if (status != FWK_SUCCESS) {
370         return status;
371     }
372 
373     if ((perf_level < domain_ctx->level_limits.minimum) ||
374         (perf_level > domain_ctx->level_limits.maximum)) {
375         return FWK_E_RANGE;
376     }
377 
378     return scmi_perf_ctx.dvfs_api->set_level(domain_id, agent_id, perf_level);
379 }
380 
validate_new_limits(struct scmi_perf_domain_ctx * domain_ctx,const struct mod_scmi_perf_level_limits * limits)381 static int validate_new_limits(
382     struct scmi_perf_domain_ctx *domain_ctx,
383     const struct mod_scmi_perf_level_limits *limits)
384 {
385     uint32_t limit;
386     int status;
387 
388     if (scmi_perf_ctx.config->approximate_level) {
389         /* When approx level is chosen, a level is always found */
390         return FWK_SUCCESS;
391     }
392 
393     limit = limits->minimum;
394     status = find_opp_for_level(domain_ctx, &limit, false);
395     if (status != FWK_SUCCESS) {
396         return status;
397     }
398 
399     limit = limits->maximum;
400     return find_opp_for_level(domain_ctx, &limit, false);
401 }
402 
perf_set_limits(fwk_id_t domain_id,unsigned int agent_id,const struct mod_scmi_perf_level_limits * limits)403 int perf_set_limits(
404     fwk_id_t domain_id,
405     unsigned int agent_id,
406     const struct mod_scmi_perf_level_limits *limits)
407 {
408     struct scmi_perf_domain_ctx *domain_ctx;
409     uint32_t needle;
410     int status;
411     bool needs_new_level = true;
412 
413     if (limits->minimum > limits->maximum) {
414         return FWK_E_PARAM;
415     }
416 
417     domain_ctx = get_ctx(domain_id);
418 
419     if ((limits->minimum == domain_ctx->level_limits.minimum) &&
420         (limits->maximum == domain_ctx->level_limits.maximum)) {
421         return FWK_SUCCESS;
422     }
423 
424     status = validate_new_limits(domain_ctx, limits);
425     if (status != FWK_SUCCESS) {
426         return status;
427     }
428 
429     /* Adjust opp for new limits */
430     if (domain_ctx->curr_level < limits->minimum) {
431         needle = limits->minimum;
432     } else if (domain_ctx->curr_level > limits->maximum) {
433         needle = limits->maximum;
434     } else {
435         /* No level transition necessary */
436         needs_new_level = false;
437     }
438 
439     scmi_perf_notify_limits_updated(
440         domain_id, limits->minimum, limits->maximum);
441 
442     domain_ctx->level_limits.minimum = limits->minimum;
443     domain_ctx->level_limits.maximum = limits->maximum;
444 
445     if (!needs_new_level) {
446         return FWK_SUCCESS;
447     }
448 
449     status = find_opp_for_level(
450         domain_ctx, &needle, scmi_perf_ctx.config->approximate_level);
451     if (status != FWK_SUCCESS) {
452         return status;
453     }
454 
455     return scmi_perf_ctx.dvfs_api->set_level(domain_id, agent_id, needle);
456 }
457 
458 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
adjust_level_for_limits(const struct mod_scmi_perf_level_limits * limits,uint32_t * level)459 static void adjust_level_for_limits(
460     const struct mod_scmi_perf_level_limits *limits,
461     uint32_t *level)
462 {
463     if (*level < limits->minimum) {
464         *level = limits->minimum;
465     } else if (*level > limits->maximum) {
466         *level = limits->maximum;
467     }
468 }
469 
470 /*
471  * Evaluate the level & limits in one go.
472  * Because the limits may also come from the external plugins, their value may
473  * not always be exact to the OPPs, so allow approximation.
474  */
perf_eval_performance(fwk_id_t domain_id,const struct mod_scmi_perf_level_limits * limits,uint32_t * level)475 void perf_eval_performance(
476     fwk_id_t domain_id,
477     const struct mod_scmi_perf_level_limits *limits,
478     uint32_t *level)
479 {
480     struct scmi_perf_domain_ctx *domain_ctx;
481     int status;
482 
483     if (limits->minimum > limits->maximum) {
484         return;
485     }
486 
487     domain_ctx = get_ctx(domain_id);
488     adjust_level_for_limits(limits, level);
489 
490     if ((limits->minimum == domain_ctx->level_limits.minimum) &&
491         (limits->maximum == domain_ctx->level_limits.maximum)) {
492         status = find_opp_for_level(domain_ctx, level, true);
493         if (status != FWK_SUCCESS) {
494             FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
495         }
496 
497         return;
498     }
499 
500     scmi_perf_notify_limits_updated(
501         domain_id, limits->minimum, limits->maximum);
502 
503     domain_ctx->level_limits.minimum = limits->minimum;
504     domain_ctx->level_limits.maximum = limits->maximum;
505 
506     status = find_opp_for_level(domain_ctx, level, true);
507     if (status != FWK_SUCCESS) {
508         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
509     }
510 }
511 #endif
512 
513 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
514 
515 /*
516  * SCMI Resource Permissions handler
517  */
get_perf_domain_id(const uint32_t * payload,unsigned int * domain_id)518 static int get_perf_domain_id(const uint32_t *payload, unsigned int *domain_id)
519 {
520     /*
521      * Every SCMI Performance message is formatted with the domain ID
522      * as the first message element. We will use the perf_limits_get
523      * message as a basic format to retrieve the domain ID to avoid
524      * unnecessary code.
525      */
526     const struct scmi_perf_limits_get_a2p *parameters =
527         (const struct scmi_perf_limits_get_a2p *)payload;
528 
529     if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
530         return FWK_E_PARAM;
531     }
532 
533     *domain_id = parameters->domain_id;
534     return FWK_SUCCESS;
535 }
536 
scmi_perf_permissions_handler(fwk_id_t service_id,const uint32_t * payload,unsigned int message_id)537 static int scmi_perf_permissions_handler(
538     fwk_id_t service_id,
539     const uint32_t *payload,
540     unsigned int message_id)
541 {
542     enum mod_res_perms_permissions perms;
543     unsigned int agent_id, domain_id;
544     int status;
545 
546     status = scmi_perf_ctx.scmi_api->get_agent_id(service_id, &agent_id);
547     if (status != FWK_SUCCESS) {
548         return FWK_E_ACCESS;
549     }
550 
551     if (message_id < 3) {
552         perms = scmi_perf_ctx.res_perms_api->agent_has_protocol_permission(
553             agent_id, MOD_SCMI_PROTOCOL_ID_PERF);
554         if (perms == MOD_RES_PERMS_ACCESS_ALLOWED) {
555             return FWK_SUCCESS;
556         }
557         return FWK_E_ACCESS;
558     }
559 
560     status = get_perf_domain_id(payload, &domain_id);
561     if (status != FWK_SUCCESS) {
562         return FWK_E_PARAM;
563     }
564 
565     perms = scmi_perf_ctx.res_perms_api->agent_has_resource_permission(
566         agent_id, MOD_SCMI_PROTOCOL_ID_PERF, message_id, domain_id);
567 
568     if (perms == MOD_RES_PERMS_ACCESS_ALLOWED) {
569         return FWK_SUCCESS;
570     } else {
571         return FWK_E_ACCESS;
572     }
573 }
574 
575 #endif
576 
577 /*
578  * Protocol command handlers
579  */
580 
scmi_perf_protocol_version_handler(fwk_id_t service_id,const uint32_t * payload)581 static int scmi_perf_protocol_version_handler(fwk_id_t service_id,
582                                               const uint32_t *payload)
583 {
584     struct scmi_protocol_version_p2a return_values = {
585         .status = (int32_t)SCMI_SUCCESS,
586         .version = SCMI_PROTOCOL_VERSION_PERF,
587     };
588 
589     return scmi_perf_ctx.scmi_api->respond(
590         service_id, &return_values, sizeof(return_values));
591 }
592 
scmi_perf_protocol_attributes_handler(fwk_id_t service_id,const uint32_t * payload)593 static int scmi_perf_protocol_attributes_handler(fwk_id_t service_id,
594                                                  const uint32_t *payload)
595 {
596 #ifdef BUILD_HAS_MOD_STATISTICS
597     int status;
598 #endif
599     struct scmi_perf_protocol_attributes_p2a return_values = {
600         .status = (int32_t)SCMI_SUCCESS,
601         .attributes =
602             SCMI_PERF_PROTOCOL_ATTRIBUTES(true, scmi_perf_ctx.domain_count),
603     };
604     uint32_t addr_low = 0, addr_high = 0, len = 0;
605 
606 #ifdef BUILD_HAS_MOD_STATISTICS
607     status = scmi_perf_ctx.stats_api->get_statistics_desc(
608         fwk_module_id_scmi_perf, &addr_low, &addr_high, &len);
609     if (status != FWK_SUCCESS) {
610         return_values.status = (int32_t)SCMI_GENERIC_ERROR;
611     }
612 #endif
613 
614     return_values.statistics_len = len;
615     return_values.statistics_address_low = addr_low;
616     return_values.statistics_address_high = addr_high;
617 
618     return scmi_perf_ctx.scmi_api->respond(
619         service_id, &return_values, sizeof(return_values));
620 }
621 
scmi_perf_protocol_message_attributes_handler(fwk_id_t service_id,const uint32_t * payload)622 static int scmi_perf_protocol_message_attributes_handler(fwk_id_t service_id,
623                                                        const uint32_t *payload)
624 {
625     const struct scmi_protocol_message_attributes_a2p *parameters;
626     struct scmi_protocol_message_attributes_p2a return_values;
627 
628     parameters = (const struct scmi_protocol_message_attributes_a2p *)
629                  payload;
630 
631     if ((parameters->message_id < FWK_ARRAY_SIZE(handler_table)) &&
632         (handler_table[parameters->message_id] != NULL)) {
633         return_values = (struct scmi_protocol_message_attributes_p2a) {
634             .status = SCMI_SUCCESS,
635         };
636     } else {
637         return_values.status = (int32_t)SCMI_NOT_FOUND;
638     }
639 
640     return_values.attributes = 0;
641 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
642     if ((parameters->message_id <= MOD_SCMI_PERF_LEVEL_GET) &&
643         (parameters->message_id >= MOD_SCMI_PERF_LIMITS_SET)) {
644         return_values.attributes = 1; /* Fast Channel available */
645     }
646 #endif
647 
648     return scmi_perf_ctx.scmi_api->respond(
649         service_id,
650         &return_values,
651         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
652                                                  sizeof(return_values.status));
653 }
654 
scmi_perf_domain_attributes_handler(fwk_id_t service_id,const uint32_t * payload)655 static int scmi_perf_domain_attributes_handler(fwk_id_t service_id,
656                                                const uint32_t *payload)
657 {
658     int status, respond_status;
659     unsigned int agent_id;
660     const struct scmi_perf_domain_attributes_a2p *parameters;
661     uint32_t permissions = 0;
662     fwk_id_t domain_id;
663     struct mod_dvfs_opp opp;
664     struct scmi_perf_domain_attributes_p2a return_values = {
665         .status = (int32_t)SCMI_GENERIC_ERROR,
666     };
667     bool notifications = false;
668     bool fast_channels = false;
669 
670     parameters = (const struct scmi_perf_domain_attributes_a2p *)payload;
671 
672     /* Validate the domain identifier */
673     if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
674         status = FWK_SUCCESS;
675         return_values.status = (int32_t)SCMI_NOT_FOUND;
676 
677         goto exit;
678     }
679 
680     status = scmi_perf_ctx.scmi_api->get_agent_id(service_id, &agent_id);
681     if (status != FWK_SUCCESS) {
682         goto exit;
683     }
684 
685 #ifndef BUILD_HAS_MOD_RESOURCE_PERMS
686     permissions = ((uint32_t)MOD_SCMI_PERF_PERMS_SET_LIMITS) |
687         ((uint32_t)MOD_SCMI_PERF_PERMS_SET_LEVEL);
688 #else
689     status = scmi_perf_permissions_handler(
690         service_id, payload, (unsigned int)MOD_SCMI_PERF_LIMITS_SET);
691     if (status == FWK_SUCCESS) {
692         permissions = (uint8_t)MOD_SCMI_PERF_PERMS_SET_LIMITS;
693     }
694     status = scmi_perf_permissions_handler(
695         service_id, payload, (unsigned int)MOD_SCMI_PERF_LEVEL_SET);
696     if (status == FWK_SUCCESS) {
697         permissions |= (uint32_t)MOD_SCMI_PERF_PERMS_SET_LEVEL;
698     }
699 #endif
700 
701     domain_id = get_dvfs_dependency_id(parameters->domain_id);
702     status = scmi_perf_ctx.dvfs_api->get_sustained_opp(domain_id, &opp);
703     if (status != FWK_SUCCESS) {
704         goto exit;
705     }
706 
707 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
708     notifications = true;
709 #endif
710 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
711     const struct mod_scmi_perf_domain_config *domain =
712         &(*scmi_perf_ctx.config->domains)[parameters->domain_id];
713 
714     if (domain->fast_channels_addr_scp != NULL) {
715         fast_channels = true;
716     }
717 #endif
718     return_values = (struct scmi_perf_domain_attributes_p2a){
719         .status = SCMI_SUCCESS,
720         .attributes = SCMI_PERF_DOMAIN_ATTRIBUTES(
721             (uint32_t)notifications,
722             (uint32_t)notifications,
723             ((permissions & (uint32_t)MOD_SCMI_PERF_PERMS_SET_LEVEL) !=
724              (uint32_t)0) ?
725                 1U :
726                 0U,
727             ((permissions & (uint32_t)MOD_SCMI_PERF_PERMS_SET_LIMITS) !=
728              (uint32_t)0) ?
729                 1U :
730                 0U,
731             (uint32_t)fast_channels),
732         .rate_limit = 0, /* Unsupported */
733         .sustained_freq = opp.frequency,
734         .sustained_perf_level = opp.level,
735     };
736 
737     /* Copy the domain name into the mailbox */
738     fwk_str_strncpy(
739         (char *)return_values.name,
740         fwk_module_get_element_name(domain_id),
741         sizeof(return_values.name) - 1);
742 
743 exit:
744     respond_status = scmi_perf_ctx.scmi_api->respond(
745         service_id,
746         &return_values,
747         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
748                                                  sizeof(return_values.status));
749 
750     if (respond_status != FWK_SUCCESS) {
751         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
752     }
753 
754     return status;
755 }
756 
scmi_perf_describe_levels_handler(fwk_id_t service_id,const uint32_t * payload)757 static int scmi_perf_describe_levels_handler(fwk_id_t service_id,
758                                              const uint32_t *payload)
759 {
760     int status, respond_status;
761     size_t max_payload_size;
762     const struct scmi_perf_describe_levels_a2p *parameters;
763     fwk_id_t domain_id;
764     struct scmi_perf_level perf_level;
765     unsigned int num_levels, level_index, level_index_max;
766     size_t payload_size;
767     size_t opp_count;
768     struct mod_dvfs_opp opp;
769     uint16_t latency;
770     struct scmi_perf_describe_levels_p2a return_values = {
771         .status = (int32_t)SCMI_GENERIC_ERROR,
772     };
773 
774     payload_size = sizeof(return_values);
775 
776     status = scmi_perf_ctx.scmi_api->get_max_payload_size(service_id,
777                                                           &max_payload_size);
778     if (status != FWK_SUCCESS) {
779         goto exit;
780     }
781 
782     status = (SCMI_PERF_LEVELS_MAX(max_payload_size) > 0) ?
783         FWK_SUCCESS : FWK_E_SIZE;
784     if (status != FWK_SUCCESS) {
785         goto exit;
786     }
787 
788     parameters = (const struct scmi_perf_describe_levels_a2p *)payload;
789 
790     /* Validate the domain identifier */
791     if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
792         return_values.status = (int32_t)SCMI_NOT_FOUND;
793 
794         goto exit;
795     }
796 
797     /* Get the number of operating points for the domain */
798     domain_id = get_dependency_id(parameters->domain_id);
799     status = scmi_perf_ctx.dvfs_api->get_opp_count(domain_id, &opp_count);
800     if (status != FWK_SUCCESS) {
801         goto exit;
802     }
803 
804     /* Validate level index */
805     level_index = parameters->level_index;
806     if (level_index >= opp_count) {
807         return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
808 
809         goto exit;
810     }
811 
812     /* Identify the maximum number of performance levels we can send at once */
813     if (SCMI_PERF_LEVELS_MAX(max_payload_size) < (opp_count - level_index)) {
814         num_levels = (unsigned int)SCMI_PERF_LEVELS_MAX(max_payload_size);
815     } else {
816         num_levels = (unsigned int)(opp_count - level_index);
817     }
818 
819     level_index_max = (level_index + num_levels - 1);
820 
821     status = scmi_perf_ctx.dvfs_api->get_latency(domain_id, &latency);
822     if (status != FWK_SUCCESS) {
823         goto exit;
824     }
825 
826     /* Copy DVFS data into returned data structure */
827     for (; level_index <= level_index_max; level_index++,
828          payload_size += sizeof(perf_level)) {
829 
830         status = scmi_perf_ctx.dvfs_api->get_nth_opp(
831             domain_id, level_index, &opp);
832         if (status != FWK_SUCCESS) {
833             goto exit;
834         }
835 
836         if (opp.power != 0) {
837             perf_level.power_cost = opp.power;
838         } else {
839             perf_level.power_cost = opp.voltage;
840         }
841         perf_level.performance_level = opp.level;
842         perf_level.attributes = latency;
843 
844         status = scmi_perf_ctx.scmi_api->write_payload(service_id, payload_size,
845             &perf_level, sizeof(perf_level));
846         if (status != FWK_SUCCESS) {
847             goto exit;
848         }
849     }
850 
851     return_values = (struct scmi_perf_describe_levels_p2a) {
852         .status = SCMI_SUCCESS,
853         .num_levels = SCMI_PERF_NUM_LEVELS(num_levels,
854             (opp_count - level_index_max - 1))
855     };
856 
857     status = scmi_perf_ctx.scmi_api->write_payload(service_id, 0,
858         &return_values, sizeof(return_values));
859 
860 exit:
861     respond_status = scmi_perf_ctx.scmi_api->respond(
862         service_id,
863         (return_values.status == SCMI_SUCCESS) ? NULL : &return_values.status,
864         (return_values.status == SCMI_SUCCESS) ? payload_size :
865                                                  sizeof(return_values.status));
866     if (respond_status != FWK_SUCCESS) {
867         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
868     }
869 
870     return status;
871 }
872 
scmi_perf_limits_set_handler(fwk_id_t service_id,const uint32_t * payload)873 static int scmi_perf_limits_set_handler(fwk_id_t service_id,
874                                         const uint32_t *payload)
875 {
876     int status, respond_status;
877     unsigned int agent_id;
878     const struct scmi_perf_limits_set_a2p *parameters;
879     uint32_t range_min, range_max;
880     fwk_id_t domain_id;
881     struct scmi_perf_limits_set_p2a return_values = {
882         .status = (int32_t)SCMI_GENERIC_ERROR,
883     };
884     enum mod_scmi_perf_policy_status policy_status;
885 
886     parameters = (const struct scmi_perf_limits_set_a2p *)payload;
887 
888     if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
889         status = FWK_SUCCESS;
890         return_values.status = (int32_t)SCMI_NOT_FOUND;
891 
892         goto exit;
893     }
894 
895     status = scmi_perf_ctx.scmi_api->get_agent_id(service_id, &agent_id);
896     if (status != FWK_SUCCESS) {
897         goto exit;
898     }
899 
900     if (parameters->range_min > parameters->range_max) {
901         return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
902         goto exit;
903     }
904 
905     domain_id = get_dependency_id(parameters->domain_id);
906     range_min = parameters->range_min;
907     range_max = parameters->range_max;
908 
909     status = scmi_perf_limits_set_policy(&policy_status, &range_min,
910         &range_max, agent_id, domain_id);
911 
912     if (status != FWK_SUCCESS) {
913         return_values.status = (int32_t)SCMI_GENERIC_ERROR;
914         goto exit;
915     }
916     if (policy_status == MOD_SCMI_PERF_SKIP_MESSAGE_HANDLER) {
917         return_values.status = (int32_t)SCMI_SUCCESS;
918         goto exit;
919     }
920 
921     status = perf_set_limits(
922         domain_id,
923         agent_id,
924         &((struct mod_scmi_perf_level_limits){ .minimum = range_min,
925                                                .maximum = range_max }));
926 
927     /*
928      * Return immediately to the caller, fire-and-forget.
929      */
930 
931     if ((status == FWK_SUCCESS) || (status == FWK_PENDING)) {
932         return_values.status = (int32_t)SCMI_SUCCESS;
933     } else {
934         return_values.status = (int32_t)SCMI_OUT_OF_RANGE;
935     }
936 
937 exit:
938     respond_status = scmi_perf_ctx.scmi_api->respond(
939         service_id,
940         &return_values,
941         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
942                                                  sizeof(return_values.status));
943     if (respond_status != FWK_SUCCESS) {
944         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
945     }
946 
947     return status;
948 }
949 
scmi_perf_limits_get_handler(fwk_id_t service_id,const uint32_t * payload)950 static int scmi_perf_limits_get_handler(fwk_id_t service_id,
951                                         const uint32_t *payload)
952 {
953     fwk_id_t domain_id;
954     const struct scmi_perf_limits_get_a2p *parameters;
955     struct scmi_perf_limits_get_p2a return_values;
956     struct scmi_perf_domain_ctx *domain_ctx;
957 
958     parameters = (const struct scmi_perf_limits_get_a2p *)payload;
959     if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
960         return_values.status = (int32_t)SCMI_NOT_FOUND;
961 
962         goto exit;
963     }
964 
965     domain_id = get_dependency_id(parameters->domain_id);
966     domain_ctx = get_ctx(domain_id);
967 
968     return_values.status = (int32_t)SCMI_SUCCESS;
969     return_values.range_min = domain_ctx->level_limits.minimum;
970     return_values.range_max = domain_ctx->level_limits.maximum;
971 
972 exit:
973     return scmi_perf_ctx.scmi_api->respond(
974         service_id,
975         &return_values,
976         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
977                                                  sizeof(return_values.status));
978 }
979 
scmi_perf_level_set_handler(fwk_id_t service_id,const uint32_t * payload)980 static int scmi_perf_level_set_handler(fwk_id_t service_id,
981                                        const uint32_t *payload)
982 {
983     int status, respond_status;
984     unsigned int agent_id;
985     const struct scmi_perf_level_set_a2p *parameters;
986     fwk_id_t domain_id;
987     struct scmi_perf_level_set_p2a return_values = {
988         .status = (int32_t)SCMI_GENERIC_ERROR,
989     };
990     uint32_t perf_level;
991     enum mod_scmi_perf_policy_status policy_status;
992     parameters = (const struct scmi_perf_level_set_a2p *)payload;
993 
994     if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
995         status = FWK_SUCCESS;
996         return_values.status = (int32_t)SCMI_NOT_FOUND;
997 
998         goto exit;
999     }
1000 
1001     status = scmi_perf_ctx.scmi_api->get_agent_id(service_id, &agent_id);
1002     if (status != FWK_SUCCESS) {
1003         goto exit;
1004     }
1005 
1006     /*
1007      * Note that the policy handler may change the performance level
1008      */
1009     domain_id = get_dependency_id(parameters->domain_id);
1010     perf_level = parameters->performance_level;
1011 
1012     status = scmi_perf_level_set_policy(&policy_status, &perf_level, agent_id,
1013         domain_id);
1014 
1015     if (status != FWK_SUCCESS) {
1016         return_values.status = (int32_t)SCMI_GENERIC_ERROR;
1017         goto exit;
1018     }
1019     if (policy_status == MOD_SCMI_PERF_SKIP_MESSAGE_HANDLER) {
1020         return_values.status = (int32_t)SCMI_SUCCESS;
1021         goto exit;
1022     }
1023 
1024     status = perf_set_level(domain_id, agent_id, perf_level);
1025 
1026     /*
1027      * Return immediately to the caller, fire-and-forget.
1028      */
1029     if ((status == FWK_SUCCESS) || (status == FWK_PENDING)) {
1030         return_values.status = (int32_t)SCMI_SUCCESS;
1031     } else if (status == FWK_E_RANGE) {
1032         return_values.status = (int32_t)SCMI_OUT_OF_RANGE;
1033     }
1034 
1035 exit:
1036     respond_status = scmi_perf_ctx.scmi_api->respond(
1037         service_id,
1038         &return_values,
1039         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
1040                                                  sizeof(return_values.status));
1041 
1042     if (respond_status != FWK_SUCCESS) {
1043         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1044     }
1045 
1046     return status;
1047 }
1048 
scmi_perf_level_get_handler(fwk_id_t service_id,const uint32_t * payload)1049 static int scmi_perf_level_get_handler(fwk_id_t service_id,
1050                                        const uint32_t *payload)
1051 {
1052     int status, respond_status;
1053     const struct scmi_perf_level_get_a2p *parameters;
1054     struct scmi_perf_event_parameters *evt_params;
1055     struct scmi_perf_level_get_p2a return_values;
1056 
1057     parameters = (const struct scmi_perf_level_get_a2p *)payload;
1058     if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
1059         status = FWK_SUCCESS;
1060         return_values.status = (int32_t)SCMI_NOT_FOUND;
1061 
1062         goto exit;
1063     }
1064 
1065     /* Check if there is already a request pending for this domain */
1066     if (!fwk_id_is_equal(
1067             scmi_perf_ctx
1068                 .perf_ops_table[fwk_id_get_element_idx(
1069                     get_dependency_id(parameters->domain_id))]
1070                 .service_id,
1071             FWK_ID_NONE)) {
1072         return_values.status = (int32_t)SCMI_BUSY;
1073         status = FWK_SUCCESS;
1074 
1075         goto exit;
1076     }
1077 
1078     /* The get_level request is processed within the event being generated */
1079     struct fwk_event event = {
1080         .target_id = fwk_module_id_scmi_perf,
1081         .id = scmi_perf_get_level,
1082     };
1083 
1084     evt_params = (struct scmi_perf_event_parameters *)event.params;
1085     evt_params->domain_id = get_dependency_id(parameters->domain_id);
1086 
1087     status = fwk_put_event(&event);
1088     if (status != FWK_SUCCESS) {
1089         return_values.status = (int32_t)SCMI_GENERIC_ERROR;
1090 
1091         goto exit;
1092     }
1093 
1094     /* Store service identifier to indicate there is a pending request */
1095     scmi_perf_ctx.perf_ops_table[fwk_id_get_element_idx(evt_params->domain_id)]
1096         .service_id = service_id;
1097 
1098     return FWK_SUCCESS;
1099 
1100 exit:
1101     respond_status = scmi_perf_ctx.scmi_api->respond(
1102         service_id,
1103         &return_values,
1104         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
1105                                                  sizeof(return_values.status));
1106 
1107     if (respond_status != FWK_SUCCESS) {
1108         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1109     }
1110 
1111     return status;
1112 }
1113 
1114 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
scmi_perf_limits_notify(fwk_id_t service_id,const uint32_t * payload)1115 static int scmi_perf_limits_notify(fwk_id_t service_id,
1116                                    const uint32_t *payload)
1117 {
1118     unsigned int agent_id;
1119     int status, respond_status;
1120     unsigned int id;
1121     const struct scmi_perf_notify_limits_a2p *parameters;
1122     struct scmi_perf_notify_limits_p2a return_values = {
1123         .status = (int32_t)SCMI_GENERIC_ERROR,
1124     };
1125 
1126     parameters = (const struct scmi_perf_notify_limits_a2p *)payload;
1127     id = parameters->domain_id;
1128     if (id >= scmi_perf_ctx.domain_count) {
1129         status = FWK_SUCCESS;
1130         return_values.status = (int32_t)SCMI_NOT_FOUND;
1131 
1132         goto exit;
1133     }
1134 
1135     if ((parameters->notify_enable &
1136          ~SCMI_PERF_NOTIFY_LIMITS_NOTIFY_ENABLE_MASK) != 0x0) {
1137         status = FWK_SUCCESS;
1138         return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
1139 
1140         goto exit;
1141     }
1142 
1143     status = scmi_perf_ctx.scmi_api->get_agent_id(service_id, &agent_id);
1144     if (status != FWK_SUCCESS) {
1145         goto exit;
1146     }
1147 
1148     if (parameters->notify_enable) {
1149         status = scmi_perf_ctx.scmi_notification_api
1150                      ->scmi_notification_add_subscriber(
1151                          MOD_SCMI_PROTOCOL_ID_PERF,
1152                          id,
1153                          MOD_SCMI_PERF_NOTIFY_LIMITS,
1154                          service_id);
1155     } else {
1156         status = scmi_perf_ctx.scmi_notification_api
1157                      ->scmi_notification_remove_subscriber(
1158                          MOD_SCMI_PROTOCOL_ID_PERF,
1159                          agent_id,
1160                          id,
1161                          MOD_SCMI_PERF_NOTIFY_LIMITS);
1162     }
1163     if (status != FWK_SUCCESS) {
1164         goto exit;
1165     }
1166 
1167     return_values.status = (int32_t)SCMI_SUCCESS;
1168 
1169 exit:
1170     respond_status = scmi_perf_ctx.scmi_api->respond(
1171         service_id,
1172         &return_values,
1173         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
1174                                                  sizeof(return_values.status));
1175 
1176     if (respond_status != FWK_SUCCESS) {
1177         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1178     }
1179 
1180     return status;
1181 }
1182 
scmi_perf_level_notify(fwk_id_t service_id,const uint32_t * payload)1183 static int scmi_perf_level_notify(fwk_id_t service_id,
1184                                    const uint32_t *payload)
1185 {
1186     unsigned int agent_id;
1187     int status, respond_status;
1188     unsigned int id;
1189     const struct scmi_perf_notify_level_a2p *parameters;
1190     struct scmi_perf_notify_level_p2a return_values = {
1191         .status = (int32_t)SCMI_GENERIC_ERROR,
1192     };
1193 
1194     parameters = (const struct scmi_perf_notify_level_a2p *)payload;
1195     id = parameters->domain_id;
1196     if (id >= scmi_perf_ctx.domain_count) {
1197         status = FWK_SUCCESS;
1198         return_values.status = (int32_t)SCMI_NOT_FOUND;
1199 
1200         goto exit;
1201     }
1202 
1203     if ((parameters->notify_enable &
1204          ~SCMI_PERF_NOTIFY_LEVEL_NOTIFY_ENABLE_MASK) != 0x0) {
1205         status = FWK_SUCCESS;
1206         return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
1207 
1208         goto exit;
1209     }
1210 
1211     id = parameters->domain_id;
1212 
1213     status = scmi_perf_ctx.scmi_api->get_agent_id(service_id, &agent_id);
1214     if (status != FWK_SUCCESS) {
1215         goto exit;
1216     }
1217 
1218     if (parameters->notify_enable) {
1219         status = scmi_perf_ctx.scmi_notification_api
1220                      ->scmi_notification_add_subscriber(
1221                          MOD_SCMI_PROTOCOL_ID_PERF,
1222                          id,
1223                          MOD_SCMI_PERF_NOTIFY_LEVEL,
1224                          service_id);
1225     } else {
1226         status = scmi_perf_ctx.scmi_notification_api
1227                      ->scmi_notification_remove_subscriber(
1228                          MOD_SCMI_PROTOCOL_ID_PERF,
1229                          agent_id,
1230                          id,
1231                          MOD_SCMI_PERF_NOTIFY_LEVEL);
1232     }
1233     if (status != FWK_SUCCESS) {
1234         goto exit;
1235     }
1236 
1237     return_values.status = (int32_t)SCMI_SUCCESS;
1238 
1239 exit:
1240     respond_status = scmi_perf_ctx.scmi_api->respond(
1241         service_id,
1242         &return_values,
1243         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
1244                                                  sizeof(return_values.status));
1245 
1246     if (respond_status != FWK_SUCCESS) {
1247         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1248     }
1249 
1250     return status;
1251 }
1252 #endif
1253 
1254 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
1255 
1256 /*
1257  * Note that the Fast Channel doorbell is not supported in this
1258  * implementation.
1259  */
scmi_perf_describe_fast_channels(fwk_id_t service_id,const uint32_t * payload)1260 static int scmi_perf_describe_fast_channels(fwk_id_t service_id,
1261                                             const uint32_t *payload)
1262 {
1263     const struct mod_scmi_perf_domain_config *domain;
1264     const struct scmi_perf_describe_fc_a2p *parameters;
1265     struct scmi_perf_describe_fc_p2a return_values = {
1266         .status = (int32_t)SCMI_GENERIC_ERROR,
1267     };
1268     uint32_t chan_size, chan_index;
1269     enum scmi_perf_command_id message_id;
1270 
1271     parameters = (const struct scmi_perf_describe_fc_a2p *)payload;
1272 
1273     /* Validate the domain identifier */
1274     if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
1275         return_values.status = (int32_t)SCMI_NOT_FOUND;
1276 
1277         goto exit;
1278     }
1279 
1280     domain = &(*scmi_perf_ctx.config->domains)[parameters->domain_id];
1281 
1282     if (domain->fast_channels_addr_scp == NULL) {
1283         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
1284 
1285         goto exit;
1286     }
1287 
1288     if (parameters->message_id >= MOD_SCMI_PERF_COMMAND_COUNT) {
1289         return_values.status = (int32_t)SCMI_NOT_FOUND;
1290 
1291         goto exit;
1292     }
1293 
1294     message_id = (enum scmi_perf_command_id)parameters->message_id;
1295 
1296     switch (message_id) {
1297     case MOD_SCMI_PERF_LEVEL_GET:
1298         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET;
1299         chan_size =
1300             fast_channel_elem_size[MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET];
1301         break;
1302 
1303     case MOD_SCMI_PERF_LEVEL_SET:
1304         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET;
1305         chan_size =
1306             fast_channel_elem_size[MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET];
1307         break;
1308 
1309     case MOD_SCMI_PERF_LIMITS_SET:
1310         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_SET;
1311         chan_size =
1312             fast_channel_elem_size[MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_SET];
1313         break;
1314 
1315     case MOD_SCMI_PERF_LIMITS_GET:
1316         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_GET;
1317         chan_size =
1318             fast_channel_elem_size[MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_GET];
1319         break;
1320 
1321     default:
1322         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
1323         goto exit;
1324     }
1325     if (domain->fast_channels_addr_ap == NULL ||
1326         domain->fast_channels_addr_ap[chan_index] == 0x0) {
1327         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
1328         goto exit;
1329     }
1330     return_values.status = (int32_t)SCMI_SUCCESS;
1331     return_values.attributes = 0; /* Doorbell not supported */
1332     return_values.rate_limit = scmi_perf_ctx.fast_channels_rate_limit;
1333     return_values.chan_addr_low =
1334         (uint32_t)(domain->fast_channels_addr_ap[chan_index] & ~0UL);
1335     return_values.chan_addr_high =
1336         (uint32_t)(domain->fast_channels_addr_ap[chan_index] >> 32);
1337     return_values.chan_size = chan_size;
1338 
1339 exit:
1340     return scmi_perf_ctx.scmi_api->respond(
1341         service_id,
1342         &return_values,
1343         (return_values.status == SCMI_SUCCESS) ? sizeof(return_values) :
1344                                                  sizeof(return_values.status));
1345 }
1346 
1347 /*
1348  * Fast Channel Polling
1349  */
fast_channel_callback(uintptr_t param)1350 static void fast_channel_callback(uintptr_t param)
1351 {
1352     int status;
1353     struct fwk_event_light event = (struct fwk_event_light){
1354         .id = FWK_ID_EVENT_INIT(
1355             FWK_MODULE_IDX_SCMI_PERF,
1356             SCMI_PERF_EVENT_IDX_FAST_CHANNELS_PROCESS),
1357         .source_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_SCMI_PERF),
1358         .target_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_SCMI_PERF),
1359     };
1360 
1361     status = fwk_put_event(&event);
1362     if (status != FWK_SUCCESS) {
1363         FWK_LOG_ERR("[SCMI-PERF] Error creating FC process event.");
1364         return;
1365     }
1366 
1367     if (scmi_perf_ctx.fch_pending_req_count > 0) {
1368         FWK_LOG_INFO("[SCMI-PERF] Multiple FC events pending");
1369     }
1370 
1371     scmi_perf_ctx.fch_pending_req_count++;
1372 }
1373 
fast_channels_process(void)1374 static void fast_channels_process(void)
1375 {
1376     const struct mod_scmi_perf_domain_config *domain;
1377     struct mod_scmi_perf_fast_channel_limit *set_limit;
1378     struct scmi_perf_domain_ctx *domain_ctx;
1379     uint32_t *set_level;
1380     uint32_t tlevel, tmax, tmin;
1381     unsigned int i;
1382     int status;
1383 
1384 #    ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
1385     struct fc_perf_update update;
1386 
1387     for (i = 0; i < scmi_perf_ctx.domain_count; i++) {
1388         domain = &(*scmi_perf_ctx.config->domains)[i];
1389         if (domain->fast_channels_addr_scp != NULL) {
1390             set_limit = get_fc_set_limit_addr(domain);
1391             set_level = get_fc_set_level_addr(domain);
1392 
1393             domain_ctx = &scmi_perf_ctx.domain_ctx_table[i];
1394 
1395             if (set_limit != NULL) {
1396                 tmax = set_limit->range_max;
1397                 tmin = set_limit->range_min;
1398             } else {
1399                 tmax = domain_ctx->level_limits.maximum;
1400                 tmin = domain_ctx->level_limits.minimum;
1401             }
1402 
1403             tlevel = (set_level != NULL) ? *set_level : domain_ctx->curr_level;
1404 
1405             update = (struct fc_perf_update){
1406                 .domain_id = get_dependency_id(i),
1407                 .level = tlevel,
1408                 .max_limit = tmax,
1409                 .min_limit = tmin,
1410             };
1411 
1412             perf_plugins_handler_update(i, &update);
1413         }
1414     }
1415 
1416     for (i = 0; i < scmi_perf_ctx.domain_count; i++) {
1417         domain = &(*scmi_perf_ctx.config->domains)[i];
1418         if (domain->fast_channels_addr_scp != NULL) {
1419             set_limit = get_fc_set_limit_addr(domain);
1420             set_level = get_fc_set_level_addr(domain);
1421 
1422             domain_ctx = &scmi_perf_ctx.domain_ctx_table[i];
1423 
1424             if (set_limit != NULL) {
1425                 tmax = set_limit->range_max;
1426                 tmin = set_limit->range_min;
1427             } else {
1428                 tmax = domain_ctx->level_limits.maximum;
1429                 tmin = domain_ctx->level_limits.minimum;
1430             }
1431 
1432             tlevel = (set_level != NULL) ? *set_level : domain_ctx->curr_level;
1433 
1434             update = (struct fc_perf_update){
1435                 .domain_id = get_dependency_id(i),
1436                 .level = tlevel,
1437                 .max_limit = tmax,
1438                 .min_limit = tmin,
1439             };
1440 
1441             perf_plugins_handler_get(i, &update);
1442 
1443             tlevel = update.level;
1444             tmax = update.adj_max_limit;
1445             tmin = update.adj_min_limit;
1446 
1447             perf_eval_performance(
1448                 FWK_ID_ELEMENT(FWK_MODULE_IDX_SCMI_PERF, i),
1449                 &((struct mod_scmi_perf_level_limits){
1450                     .minimum = tmin,
1451                     .maximum = tmax,
1452                 }),
1453                 &tlevel);
1454 
1455             status = scmi_perf_ctx.dvfs_api->set_level(
1456                 get_dependency_id(i), 0, tlevel);
1457             if (status != FWK_SUCCESS) {
1458                 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1459             }
1460 
1461 #    else
1462 
1463     for (i = 0; i < scmi_perf_ctx.domain_count; i++) {
1464         domain = &(*scmi_perf_ctx.config->domains)[i];
1465         if (domain->fast_channels_addr_scp != NULL) {
1466             set_limit = get_fc_set_limit_addr(domain);
1467             set_level = get_fc_set_level_addr(domain);
1468 
1469             domain_ctx = &scmi_perf_ctx.domain_ctx_table[i];
1470 
1471             if (set_limit != NULL) {
1472                 tmax = set_limit->range_max;
1473                 tmin = set_limit->range_min;
1474             } else {
1475                 tmax = domain_ctx->level_limits.maximum;
1476                 tmin = domain_ctx->level_limits.minimum;
1477             }
1478 
1479             tlevel = (set_level != NULL) ? *set_level : domain_ctx->curr_level;
1480 
1481             /*
1482              * Check for set_level
1483              */
1484             if (set_level != NULL && tlevel > 0) {
1485                 status = perf_set_level(get_dependency_id(i), 0, tlevel);
1486                 if (status != FWK_SUCCESS) {
1487                     FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1488                 }
1489             }
1490             if (set_limit != NULL) {
1491                 if ((tmax == 0) && (tmin == 0)) {
1492                     continue;
1493                 }
1494                 status = perf_set_limits(
1495                     get_dependency_id(i),
1496                     0,
1497                     &((struct mod_scmi_perf_level_limits){
1498                         .minimum = tmin,
1499                         .maximum = tmax,
1500                     }));
1501                 if (status != FWK_SUCCESS) {
1502                     FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1503                 }
1504             }
1505 
1506 #    endif
1507         }
1508     }
1509     if (scmi_perf_ctx.fch_pending_req_count > 0) {
1510         scmi_perf_ctx.fch_pending_req_count--;
1511     }
1512 }
1513 
1514 #endif
1515 
1516 /*
1517  * SCMI Performance Policy Handlers
1518  */
1519 FWK_WEAK int scmi_perf_limits_set_policy(
1520     enum mod_scmi_perf_policy_status *policy_status,
1521     uint32_t *range_min,
1522     uint32_t *range_max,
1523     unsigned int agent_id,
1524     fwk_id_t domain_id)
1525 {
1526     *policy_status = MOD_SCMI_PERF_EXECUTE_MESSAGE_HANDLER;
1527 
1528     return FWK_SUCCESS;
1529 }
1530 
1531 FWK_WEAK int scmi_perf_level_set_policy(
1532     enum mod_scmi_perf_policy_status *policy_status,
1533     uint32_t *level,
1534     unsigned int agent_id,
1535     fwk_id_t domain_id)
1536 {
1537     *policy_status = MOD_SCMI_PERF_EXECUTE_MESSAGE_HANDLER;
1538 
1539     return FWK_SUCCESS;
1540 }
1541 
1542 /*
1543  * SCMI module -> SCMI performance module interface
1544  */
1545 
1546 static int scmi_perf_get_scmi_protocol_id(fwk_id_t protocol_id,
1547                                           uint8_t *scmi_protocol_id)
1548 {
1549     *scmi_protocol_id = (uint8_t)MOD_SCMI_PROTOCOL_ID_PERF;
1550 
1551     return FWK_SUCCESS;
1552 }
1553 
1554 static int scmi_perf_message_handler(fwk_id_t protocol_id, fwk_id_t service_id,
1555     const uint32_t *payload, size_t payload_size, unsigned int message_id)
1556 {
1557     int32_t return_value;
1558 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
1559     int status;
1560 #endif
1561 
1562     static_assert(FWK_ARRAY_SIZE(handler_table) ==
1563         FWK_ARRAY_SIZE(payload_size_table),
1564         "[SCMI] Performance management protocol table sizes not consistent");
1565     fwk_assert(payload != NULL);
1566 
1567     if (message_id >= FWK_ARRAY_SIZE(handler_table)) {
1568         return_value = (int32_t)SCMI_NOT_FOUND;
1569         goto error;
1570     }
1571 
1572     if (handler_table[message_id] == NULL) {
1573         return_value = (int32_t)SCMI_NOT_SUPPORTED;
1574         goto error;
1575     }
1576 
1577     if (payload_size != payload_size_table[message_id]) {
1578         return_value = (int32_t)SCMI_PROTOCOL_ERROR;
1579         goto error;
1580     }
1581 
1582 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
1583     status = scmi_perf_permissions_handler(service_id, payload, message_id);
1584     if (status != FWK_SUCCESS) {
1585         if (status == FWK_E_PARAM) {
1586             return_value = (int32_t)SCMI_NOT_FOUND;
1587         } else {
1588             return_value = (int32_t)SCMI_DENIED;
1589         }
1590         goto error;
1591     }
1592 #endif
1593 
1594     return handler_table[message_id](service_id, payload);
1595 
1596 error:
1597     return scmi_perf_ctx.scmi_api->respond(
1598         service_id, &return_value, sizeof(return_value));
1599 }
1600 
1601 static struct mod_scmi_to_protocol_api scmi_perf_mod_scmi_to_protocol_api = {
1602     .get_scmi_protocol_id = scmi_perf_get_scmi_protocol_id,
1603     .message_handler = scmi_perf_message_handler
1604 };
1605 
1606 /*
1607  * Static helpers for responding to SCMI.
1608  */
1609 static void scmi_perf_respond(
1610     void *return_values,
1611     fwk_id_t domain_id,
1612     int size)
1613 {
1614     int respond_status;
1615     int idx = (int)fwk_id_get_element_idx(domain_id);
1616     fwk_id_t service_id;
1617 
1618     /*
1619      * The service identifier used for the response is retrieved from the
1620      * domain operations table.
1621      */
1622     service_id = scmi_perf_ctx.perf_ops_table[idx].service_id;
1623 
1624     respond_status =
1625         scmi_perf_ctx.scmi_api->respond(service_id, return_values, size);
1626 
1627     if (respond_status != FWK_SUCCESS) {
1628         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1629     }
1630 
1631     /*
1632      * Set the service identifier to 'none' to indicate the domain is
1633      * available again.
1634      */
1635     scmi_perf_ctx.perf_ops_table[idx].service_id = FWK_ID_NONE;
1636 }
1637 
1638 /*
1639  * A domain limits range has been updated. Depending on the system
1640  * configuration we may send an SCMI notification to the agents which
1641  * have registered for these notifications and/or update the associated
1642  * fast channels.
1643  */
1644 static void scmi_perf_notify_limits_updated(
1645     fwk_id_t domain_id,
1646     uint32_t range_min,
1647     uint32_t range_max)
1648 {
1649 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
1650     struct scmi_perf_limits_changed limits_changed;
1651 #endif
1652     unsigned int idx;
1653     const struct mod_scmi_perf_domain_config *domain;
1654     struct mod_scmi_perf_fast_channel_limit *get_limit;
1655 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
1656     int status;
1657 #endif
1658 
1659     idx = fwk_id_get_element_idx(domain_id);
1660 
1661     domain = &(*scmi_perf_ctx.config->domains)[idx];
1662     if (domain->fast_channels_addr_scp != NULL) {
1663         get_limit = (struct mod_scmi_perf_fast_channel_limit
1664                          *)((uintptr_t)domain->fast_channels_addr_scp
1665                                 [MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_GET]);
1666         if (get_limit != NULL) { /* note: get_limit may not be defined */
1667             get_limit->range_max = range_max;
1668             get_limit->range_min = range_min;
1669         }
1670     }
1671 
1672 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
1673     struct perf_plugins_perf_report perf_report = {
1674         .dep_dom_id = domain_id,
1675         .max_limit = range_max,
1676         .min_limit = range_min,
1677     };
1678 
1679     perf_plugins_handler_report(&perf_report);
1680 #endif
1681 
1682 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
1683     limits_changed.agent_id = (uint32_t)0;
1684     limits_changed.domain_id = (uint32_t)idx;
1685     limits_changed.range_min = range_min;
1686     limits_changed.range_max = range_max;
1687 
1688     status = scmi_perf_ctx.scmi_notification_api->scmi_notification_notify(
1689         MOD_SCMI_PROTOCOL_ID_PERF,
1690         MOD_SCMI_PERF_NOTIFY_LIMITS,
1691         SCMI_PERF_LIMITS_CHANGED,
1692         &limits_changed,
1693         sizeof(limits_changed));
1694     if (status != FWK_SUCCESS) {
1695         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1696     }
1697 #endif
1698 }
1699 
1700 /*
1701  * A domain performance level has been updated. Depending on the system
1702  * configuration we may send an SCMI notification to the agents which
1703  * have registered for these notifications and/or update the associated
1704  * fast channels.
1705  */
1706 static void scmi_perf_notify_level_updated(
1707     fwk_id_t domain_id,
1708     uintptr_t cookie,
1709     uint32_t level)
1710 {
1711 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
1712     struct scmi_perf_level_changed level_changed;
1713 #endif
1714 
1715     struct scmi_perf_domain_ctx *domain_ctx;
1716     const struct mod_scmi_perf_domain_config *domain;
1717     uint32_t *get_level;
1718 
1719 #ifdef BUILD_HAS_MOD_STATISTICS
1720     size_t level_id;
1721 #endif
1722 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
1723     fwk_id_t dep_dom_id;
1724 #endif
1725 #if defined(BUILD_HAS_MOD_STATISTICS) || defined(BUILD_HAS_SCMI_NOTIFICATIONS)
1726     int idx = (int)fwk_id_get_element_idx(domain_id);
1727     int status;
1728 #endif
1729 
1730 #ifdef BUILD_HAS_MOD_STATISTICS
1731     status = scmi_perf_ctx.dvfs_api->get_level_id(domain_id, level, &level_id);
1732     if (status == FWK_SUCCESS) {
1733         status = scmi_perf_ctx.stats_api->update_domain(
1734             fwk_module_id_scmi_perf,
1735             FWK_ID_ELEMENT(FWK_MODULE_IDX_SCMI_PERF, idx),
1736             level_id);
1737         if (status != FWK_SUCCESS) {
1738             FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1739         }
1740     } else {
1741         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1742     }
1743 #endif
1744 
1745 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
1746     struct perf_plugins_perf_report perf_report = {
1747         .dep_dom_id = domain_id,
1748         .level = level,
1749     };
1750 
1751     perf_plugins_handler_report(&perf_report);
1752 
1753     /*
1754      * The SCMI spec enforces that "[PERFORMANCE_LEVEL_GET] this command returns
1755      * the current performance level of a domain", thus when a physical domain
1756      * has been updated, we update all the relevant logical domains.
1757      */
1758     for (unsigned int i = 0; i < scmi_perf_ctx.domain_count; i++) {
1759         dep_dom_id = get_dependency_id(i);
1760 
1761         if (fwk_id_get_element_idx(dep_dom_id) ==
1762             fwk_id_get_element_idx(domain_id)) {
1763             domain = &(*scmi_perf_ctx.config->domains)[i];
1764 
1765             if (domain->fast_channels_addr_scp != NULL) {
1766                 get_level =
1767                     (uint32_t *)((uintptr_t)domain->fast_channels_addr_scp
1768                                      [MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET]);
1769                 if (get_level != NULL) { /* get_level may not be defined */
1770                     *get_level = level;
1771                 }
1772             }
1773         }
1774     }
1775 
1776 #else
1777     domain =
1778         &(*scmi_perf_ctx.config->domains)[fwk_id_get_element_idx(domain_id)];
1779     if (domain->fast_channels_addr_scp != NULL) {
1780         get_level = (uint32_t *)((uintptr_t)domain->fast_channels_addr_scp
1781                                      [MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET]);
1782         if (get_level != NULL) { /* note: get_level may not be defined */
1783             *get_level = level;
1784         }
1785     }
1786 #endif
1787 
1788 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
1789     level_changed.agent_id = (uint32_t)cookie;
1790     level_changed.domain_id = (uint32_t)idx;
1791     level_changed.performance_level = level;
1792 
1793     status = scmi_perf_ctx.scmi_notification_api->scmi_notification_notify(
1794         MOD_SCMI_PROTOCOL_ID_PERF,
1795         MOD_SCMI_PERF_NOTIFY_LEVEL,
1796         SCMI_PERF_LEVEL_CHANGED,
1797         &level_changed,
1798         sizeof(level_changed));
1799     if (status != FWK_SUCCESS) {
1800         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
1801     }
1802 #endif
1803 
1804     domain_ctx = get_ctx(domain_id);
1805     domain_ctx->curr_level = level;
1806 }
1807 
1808 static struct mod_scmi_perf_updated_api perf_update_api = {
1809     .notify_level_updated = scmi_perf_notify_level_updated,
1810 };
1811 
1812 /*
1813  * Framework handlers
1814  */
1815 static int scmi_perf_init(fwk_id_t module_id, unsigned int element_count,
1816                           const void *data)
1817 {
1818     int dvfs_doms_count;
1819     uint32_t i;
1820     const struct mod_scmi_perf_config *config =
1821         (const struct mod_scmi_perf_config *)data;
1822 
1823     if ((config == NULL) || (config->domains == NULL)) {
1824         return FWK_E_PARAM;
1825     }
1826 
1827     dvfs_doms_count =
1828         fwk_module_get_element_count(FWK_ID_MODULE(FWK_MODULE_IDX_DVFS));
1829     if (dvfs_doms_count <= 0) {
1830         return FWK_E_SUPPORT;
1831     }
1832 
1833     scmi_perf_ctx.dvfs_doms_count = (unsigned int)dvfs_doms_count;
1834 
1835     scmi_perf_ctx.perf_ops_table =
1836         fwk_mm_calloc(config->perf_doms_count, sizeof(struct perf_operations));
1837 
1838     scmi_perf_ctx.domain_ctx_table = fwk_mm_calloc(
1839         config->perf_doms_count, sizeof(struct scmi_perf_domain_ctx));
1840 
1841     scmi_perf_ctx.config = config;
1842     scmi_perf_ctx.domain_count = (uint32_t)config->perf_doms_count;
1843 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
1844     scmi_perf_ctx.fast_channels_alarm_id = config->fast_channels_alarm_id;
1845     if (config->fast_channels_rate_limit < SCMI_PERF_FC_MIN_RATE_LIMIT) {
1846         scmi_perf_ctx.fast_channels_rate_limit = SCMI_PERF_FC_MIN_RATE_LIMIT;
1847     } else {
1848         scmi_perf_ctx.fast_channels_rate_limit =
1849             config->fast_channels_rate_limit;
1850     }
1851 #endif
1852 
1853     /* Initialize table */
1854     for (i = 0; i < scmi_perf_ctx.domain_count; i++) {
1855         scmi_perf_ctx.perf_ops_table[i].service_id = FWK_ID_NONE;
1856     }
1857 
1858 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
1859     return perf_plugins_handler_init(config);
1860 #else
1861     return FWK_SUCCESS;
1862 #endif
1863 }
1864 
1865 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
1866 static int scmi_init_notifications(int domains)
1867 {
1868     int status;
1869 
1870     status = scmi_perf_ctx.scmi_api->get_agent_count(
1871         &scmi_perf_ctx.agent_count);
1872     if (status != FWK_SUCCESS) {
1873         return status;
1874     }
1875 
1876     fwk_assert(scmi_perf_ctx.agent_count != 0u);
1877 
1878     status = scmi_perf_ctx.scmi_notification_api->scmi_notification_init(
1879         MOD_SCMI_PROTOCOL_ID_PERF,
1880         scmi_perf_ctx.agent_count,
1881         domains,
1882         MOD_SCMI_PERF_NOTIFICATION_COUNT);
1883 
1884     return status;
1885 }
1886 #endif
1887 
1888 static int scmi_perf_bind(fwk_id_t id, unsigned int round)
1889 {
1890     int status;
1891 
1892     if (round == 1) {
1893         return FWK_SUCCESS;
1894     }
1895 
1896     status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
1897         FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),
1898         &scmi_perf_ctx.scmi_api);
1899     if (status != FWK_SUCCESS) {
1900         return status;
1901     }
1902 
1903 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
1904     status = fwk_module_bind(
1905         FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
1906         FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_NOTIFICATION),
1907         &scmi_perf_ctx.scmi_notification_api);
1908     if (status != FWK_SUCCESS) {
1909         return status;
1910     }
1911 #endif
1912 
1913 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
1914     if (!fwk_id_is_equal(scmi_perf_ctx.config->fast_channels_alarm_id,
1915         FWK_ID_NONE)) {
1916         status = fwk_module_bind(scmi_perf_ctx.config->fast_channels_alarm_id,
1917             MOD_TIMER_API_ID_ALARM, &scmi_perf_ctx.fc_alarm_api);
1918         if (status != FWK_SUCCESS) {
1919             return FWK_E_PANIC;
1920         }
1921     }
1922 #endif
1923 
1924 #ifdef BUILD_HAS_MOD_STATISTICS
1925     if (scmi_perf_ctx.config->stats_enabled) {
1926         status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_STATISTICS),
1927             FWK_ID_API(FWK_MODULE_IDX_STATISTICS, MOD_STATS_API_IDX_STATS),
1928             &scmi_perf_ctx.stats_api);
1929         if (status != FWK_SUCCESS) {
1930             return FWK_E_PANIC;
1931         }
1932     }
1933 #endif
1934 
1935 #ifdef BUILD_HAS_MOD_RESOURCE_PERMS
1936     status = fwk_module_bind(
1937         FWK_ID_MODULE(FWK_MODULE_IDX_RESOURCE_PERMS),
1938         FWK_ID_API(FWK_MODULE_IDX_RESOURCE_PERMS, MOD_RES_PERM_RESOURCE_PERMS),
1939         &scmi_perf_ctx.res_perms_api);
1940     if (status != FWK_SUCCESS) {
1941         return status;
1942     }
1943 #endif
1944 
1945 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
1946     status = perf_plugins_handler_bind();
1947     if (status != FWK_SUCCESS) {
1948         return status;
1949     }
1950 #endif
1951 
1952     return fwk_module_bind(
1953         FWK_ID_MODULE(FWK_MODULE_IDX_DVFS),
1954         FWK_ID_API(FWK_MODULE_IDX_DVFS, MOD_DVFS_API_IDX_DVFS),
1955         &scmi_perf_ctx.dvfs_api);
1956 }
1957 
1958 static int scmi_perf_process_bind_request(fwk_id_t source_id,
1959     fwk_id_t target_id, fwk_id_t api_id, const void **api)
1960 {
1961     enum scmi_perf_api_idx api_id_type =
1962         (enum scmi_perf_api_idx)fwk_id_get_api_idx(api_id);
1963 
1964     switch (api_id_type) {
1965     case MOD_SCMI_PERF_PROTOCOL_API:
1966         *api = &scmi_perf_mod_scmi_to_protocol_api;
1967         break;
1968 
1969     case MOD_SCMI_PERF_DVFS_UPDATE_API:
1970         if (!fwk_id_is_equal(source_id, fwk_module_id_dvfs)) {
1971             /* Only DVFS can use this API */
1972             return FWK_E_ACCESS;
1973         }
1974 
1975         *api = &perf_update_api;
1976         break;
1977 
1978     case MOD_SCMI_PERF_PLUGINS_API:
1979 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
1980         return perf_plugins_handler_process_bind_request(
1981             source_id, target_id, api_id, api);
1982 #else
1983         return FWK_E_ACCESS;
1984 #endif
1985         break;
1986 
1987     default:
1988         return FWK_E_ACCESS;
1989     }
1990 
1991     return FWK_SUCCESS;
1992 }
1993 
1994 #ifdef BUILD_HAS_MOD_STATISTICS
1995 static int scmi_perf_stats_start(void)
1996 {
1997     const struct mod_scmi_perf_domain_config *domain;
1998     int status = FWK_SUCCESS;
1999     int stats_domains = 0;
2000     unsigned int i;
2001 
2002     if (!scmi_perf_ctx.config->stats_enabled) {
2003         return FWK_E_SUPPORT;
2004     }
2005 
2006     /* Count how many domains have statistics */
2007     for (i = 0; i < scmi_perf_ctx.domain_count; i++) {
2008         domain = &(*scmi_perf_ctx.config->domains)[i];
2009         if (domain->stats_collected) {
2010             stats_domains++;
2011         }
2012     }
2013 
2014     status = scmi_perf_ctx.stats_api->init_stats(fwk_module_id_scmi_perf,
2015         scmi_perf_ctx.domain_count, stats_domains);
2016 
2017     if (status != FWK_SUCCESS) {
2018         return status;
2019     }
2020 
2021     for (i = 0; i < scmi_perf_ctx.domain_count; i++) {
2022         domain = &(*scmi_perf_ctx.config->domains)[i];
2023         /* Add this domain to track statistics when needed */
2024         if (domain->stats_collected) {
2025             fwk_id_t domain_id;
2026             size_t opp_count;
2027 
2028             domain_id = get_dependency_id(i);
2029             status = scmi_perf_ctx.dvfs_api->get_opp_count(domain_id,
2030                 &opp_count);
2031 
2032             if (status != FWK_SUCCESS) {
2033                 return status;
2034             }
2035 
2036             status = scmi_perf_ctx.stats_api->add_domain(
2037                 fwk_module_id_scmi_perf,
2038                 FWK_ID_ELEMENT(FWK_MODULE_IDX_SCMI_PERF, i),
2039                 (int)opp_count);
2040 
2041             if (status != FWK_SUCCESS) {
2042                 return status;
2043             }
2044         }
2045     }
2046 
2047     return scmi_perf_ctx.stats_api->start_stats(fwk_module_id_scmi_perf);
2048 }
2049 #endif
2050 
2051 static int scmi_perf_start(fwk_id_t id)
2052 {
2053     int status = FWK_SUCCESS;
2054 
2055     struct scmi_perf_domain_ctx *domain_ctx;
2056     fwk_id_t domain_id;
2057     size_t opp_count;
2058     unsigned int i;
2059 
2060     const struct mod_dvfs_domain_config *dvfs_config;
2061     struct perf_opp_table *opp_table = NULL;
2062     const struct mod_scmi_perf_domain_config *domain_cfg;
2063     unsigned int dom_idx;
2064     bool has_phy_group;
2065 
2066     scmi_perf_ctx.opp_table = fwk_mm_calloc(
2067         scmi_perf_ctx.dvfs_doms_count, sizeof(struct perf_opp_table));
2068 
2069     for (i = 0; i < scmi_perf_ctx.dvfs_doms_count; i++) {
2070         opp_table = &scmi_perf_ctx.opp_table[i];
2071 
2072         domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, i);
2073 
2074         /* Get the number of levels for this DVFS domain */
2075         status = scmi_perf_ctx.dvfs_api->get_opp_count(domain_id, &opp_count);
2076         if (status != FWK_SUCCESS) {
2077             return status;
2078         }
2079 
2080         dvfs_config =
2081             (const struct mod_dvfs_domain_config *)fwk_module_get_data(
2082                 domain_id);
2083 
2084         opp_table->opps = &dvfs_config->opps[0];
2085         opp_table->opp_count = opp_count;
2086         opp_table->dvfs_id = domain_id;
2087     }
2088 
2089     /* Assign to each performance domain the correct OPP table */
2090     for (dom_idx = 0; dom_idx < scmi_perf_ctx.domain_count; dom_idx++) {
2091         domain_cfg = &(*scmi_perf_ctx.config->domains)[dom_idx];
2092 
2093         has_phy_group = fwk_optional_id_is_defined(domain_cfg->phy_group_id);
2094         if (has_phy_group) {
2095             domain_id = domain_cfg->phy_group_id;
2096         } else {
2097             domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, dom_idx);
2098         }
2099 
2100         domain_ctx = &scmi_perf_ctx.domain_ctx_table[dom_idx];
2101 
2102         /*
2103          * Search the corresponding physical domain for this performance domain
2104          * and assign the correct opp_table.
2105          */
2106         for (i = 0; i < scmi_perf_ctx.dvfs_doms_count; i++) {
2107             opp_table = &scmi_perf_ctx.opp_table[i];
2108 
2109             if (fwk_id_is_equal(opp_table->dvfs_id, domain_id)) {
2110                 domain_ctx->opp_table = opp_table;
2111 
2112                 /* init limits */
2113                 domain_ctx->level_limits.minimum = opp_table->opps[0].level;
2114                 domain_ctx->level_limits.maximum =
2115                     opp_table->opps[opp_table->opp_count - 1].level;
2116 
2117                 break;
2118             }
2119         }
2120         if (domain_ctx->opp_table == NULL) {
2121             /* The corresponding physical domain did not have a match */
2122             return FWK_E_PANIC;
2123         }
2124     }
2125 
2126 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
2127 
2128     const struct mod_scmi_perf_domain_config *domain;
2129     unsigned int j;
2130     void *fc_elem;
2131     uint32_t fc_interval_msecs;
2132 
2133     /*
2134      * Set up the Fast Channel polling if required
2135      */
2136     if (!fwk_id_is_equal(scmi_perf_ctx.config->fast_channels_alarm_id,
2137         FWK_ID_NONE)) {
2138         if (scmi_perf_ctx.config->fast_channels_rate_limit <
2139             SCMI_PERF_FC_MIN_RATE_LIMIT) {
2140             fc_interval_msecs = (uint32_t)SCMI_PERF_FC_MIN_RATE_LIMIT / 1000;
2141         } else {
2142             fc_interval_msecs = (uint32_t)
2143             scmi_perf_ctx.config->fast_channels_rate_limit / 1000;
2144         }
2145         status = scmi_perf_ctx.fc_alarm_api->start(
2146             scmi_perf_ctx.config->fast_channels_alarm_id,
2147             fc_interval_msecs, MOD_TIMER_ALARM_TYPE_PERIODIC,
2148             fast_channel_callback, (uintptr_t)0);
2149         if (status != FWK_SUCCESS) {
2150             return status;
2151         }
2152     }
2153 
2154 #    ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
2155     struct mod_scmi_perf_fast_channel_limit *fc_limits;
2156     struct mod_dvfs_opp opp;
2157 
2158     /*
2159      * Initialise FastChannels level to sustained level and limits to min/max
2160      * OPPs.
2161      */
2162     for (i = 0; i < scmi_perf_ctx.domain_count; i++) {
2163         domain = &(*scmi_perf_ctx.config->domains)[i];
2164         if (domain->fast_channels_addr_scp != NULL) {
2165             for (j = 0; j < MOD_SCMI_PERF_FAST_CHANNEL_ADDR_INDEX_COUNT; j++) {
2166                 fc_elem = (void *)(uintptr_t)domain->fast_channels_addr_scp[j];
2167                 if (fc_elem != NULL) {
2168                     if ((j == MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET) ||
2169                         (j == MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET)) {
2170                         status = scmi_perf_ctx.dvfs_api->get_sustained_opp(
2171                             get_dependency_id(i), &opp);
2172                         if (status != FWK_SUCCESS) {
2173                             return status;
2174                         }
2175 
2176                         fwk_str_memcpy(
2177                             fc_elem, &opp.level, fast_channel_elem_size[j]);
2178                     } else {
2179                         /* _LIMIT_SET or _LIMIT_GET */
2180                         domain_ctx = &scmi_perf_ctx.domain_ctx_table[i];
2181                         opp_table = domain_ctx->opp_table;
2182 
2183                         fc_limits =
2184                             (struct mod_scmi_perf_fast_channel_limit *)fc_elem;
2185                         fc_limits->range_min = opp_table->opps[0].level;
2186                         fc_limits->range_max =
2187                             opp_table->opps[opp_table->opp_count - 1].level;
2188                     }
2189                 }
2190             }
2191         }
2192     }
2193 
2194 #    else
2195     /* Initialise FastChannels to 0 */
2196     for (i = 0; i < scmi_perf_ctx.domain_count; i++) {
2197         domain = &(*scmi_perf_ctx.config->domains)[i];
2198         if (domain->fast_channels_addr_scp != NULL) {
2199             for (j = 0; j < MOD_SCMI_PERF_FAST_CHANNEL_ADDR_INDEX_COUNT; j++) {
2200                 fc_elem = (void *)(uintptr_t)domain->fast_channels_addr_scp[j];
2201                 if (fc_elem != NULL) {
2202                     fwk_str_memset(fc_elem, 0, fast_channel_elem_size[j]);
2203                 }
2204             }
2205         }
2206     }
2207 #    endif
2208 #endif
2209 
2210 #ifdef BUILD_HAS_MOD_STATISTICS
2211     status = scmi_perf_stats_start();
2212     if (status != FWK_SUCCESS) {
2213         return status;
2214     }
2215 #endif
2216 
2217 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
2218     status = scmi_init_notifications((int)scmi_perf_ctx.domain_count);
2219     if (status != FWK_SUCCESS) {
2220         return status;
2221     }
2222 #endif
2223 
2224     return status;
2225 }
2226 
2227 /*
2228  * Handle a request for get_level/limits.
2229  */
2230 static int process_request_event(const struct fwk_event *event)
2231 {
2232     int status;
2233     struct scmi_perf_event_parameters *params;
2234     struct scmi_perf_level_get_p2a return_values_level;
2235     struct mod_dvfs_opp opp;
2236 
2237     /* request event to DVFS HAL */
2238     if (fwk_id_is_equal(event->id, scmi_perf_get_level)) {
2239         params = (struct scmi_perf_event_parameters *)event->params;
2240 
2241         status = scmi_perf_ctx.dvfs_api->get_current_opp(params->domain_id,
2242                                                        &opp);
2243         if (status == FWK_SUCCESS) {
2244             /* DVFS value is ready */
2245             return_values_level = (struct scmi_perf_level_get_p2a){
2246                 .status = SCMI_SUCCESS,
2247                 .performance_level = opp.level,
2248             };
2249 
2250             scmi_perf_respond(
2251                 &return_values_level,
2252                 params->domain_id,
2253                 (int)sizeof(return_values_level));
2254 
2255             return status;
2256         } else if (status == FWK_PENDING) {
2257             /* DVFS value will be provided through a response event */
2258             return FWK_SUCCESS;
2259         } else {
2260             return_values_level = (struct scmi_perf_level_get_p2a) {
2261                 .status = SCMI_HARDWARE_ERROR,
2262             };
2263 
2264             scmi_perf_respond(
2265                 &return_values_level,
2266                 params->domain_id,
2267                 (int)sizeof(return_values_level.status));
2268 
2269             return FWK_E_DEVICE;
2270         }
2271     }
2272 
2273     return FWK_E_PARAM;
2274 }
2275 
2276 /*
2277  * Handle a response event from the HAL which indicates that the
2278  * requested operation has completed.
2279  */
2280 static int process_response_event(const struct fwk_event *event)
2281 {
2282     struct mod_dvfs_params_response *params_level;
2283     struct scmi_perf_level_get_p2a return_values_level;
2284 
2285     if (fwk_id_is_equal(event->id, mod_dvfs_event_id_get_opp)) {
2286         params_level = (struct mod_dvfs_params_response *)
2287             event->params;
2288         return_values_level = (struct scmi_perf_level_get_p2a) {
2289             .status = params_level->status,
2290             .performance_level = params_level->performance_level,
2291         };
2292         scmi_perf_respond(
2293             &return_values_level,
2294             event->source_id,
2295             (return_values_level.status == SCMI_SUCCESS) ?
2296                 (int)sizeof(return_values_level) :
2297                 (int)sizeof(return_values_level.status));
2298     }
2299 
2300     return FWK_SUCCESS;
2301 }
2302 
2303 /* Handle internal events */
2304 static int process_internal_event(const struct fwk_event *event)
2305 {
2306     int status;
2307     enum scmi_perf_event_idx event_idx =
2308         (enum scmi_perf_event_idx)fwk_id_get_event_idx(event->id);
2309 
2310     switch (event_idx) {
2311 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
2312     case SCMI_PERF_EVENT_IDX_FAST_CHANNELS_PROCESS:
2313         fast_channels_process();
2314         status = FWK_SUCCESS;
2315         break;
2316 #endif
2317     default:
2318         status = FWK_E_PARAM;
2319         break;
2320     }
2321 
2322     return status;
2323 }
2324 
2325 static int scmi_perf_process_event(const struct fwk_event *event,
2326                                     struct fwk_event *resp_event)
2327 {
2328     /* Request events from SCMI */
2329     if (fwk_id_get_module_idx(event->source_id) ==
2330         fwk_id_get_module_idx(fwk_module_id_scmi)) {
2331         return process_request_event(event);
2332     }
2333 
2334     /* Response events from DVFS */
2335     if (fwk_id_get_module_idx(event->source_id) ==
2336         fwk_id_get_module_idx(fwk_module_id_dvfs)) {
2337         return process_response_event(event);
2338     }
2339 
2340     /* Response internal events */
2341     if (fwk_id_get_module_idx(event->source_id) ==
2342         fwk_id_get_module_idx(fwk_module_id_scmi_perf)) {
2343         return process_internal_event(event);
2344     }
2345 
2346     return FWK_E_PARAM;
2347 }
2348 
2349 /* SCMI Performance Management Protocol Definition */
2350 const struct fwk_module module_scmi_perf = {
2351     .api_count = (unsigned int)MOD_SCMI_PERF_API_COUNT,
2352     .event_count = (unsigned int)SCMI_PERF_EVENT_IDX_COUNT,
2353     .type = FWK_MODULE_TYPE_PROTOCOL,
2354     .init = scmi_perf_init,
2355     .bind = scmi_perf_bind,
2356     .start = scmi_perf_start,
2357     .process_bind_request = scmi_perf_process_bind_request,
2358     .process_event = scmi_perf_process_event,
2359 };
2360