1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Description:
8  *      SCMI Performance FastChannels support.
9  *      This module works with the support of SCMI Performance.
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 #include <mod_timer.h>
17 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
18 #    include "perf_plugins_handler.h"
19 #endif
20 
21 #ifdef BUILD_HAS_MOD_TRANSPORT
22 #    include <mod_transport.h>
23 #endif
24 
25 #include <fwk_core.h>
26 #include <fwk_event.h>
27 #include <fwk_id.h>
28 #include <fwk_log.h>
29 #include <fwk_macros.h>
30 #include <fwk_module.h>
31 #include <fwk_string.h>
32 
33 struct mod_scmi_perf_fc_ctx {
34     struct mod_scmi_perf_ctx *perf_ctx;
35 
36     struct mod_scmi_perf_private_api_perf_stub *api_fch_stub;
37     const struct mod_timer_alarm_api *fc_alarm_api;
38 
39     uint32_t fast_channels_rate_limit;
40 
41     volatile uint32_t pending_req_count;
42 
43 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
44 
45     /*
46      * For a timer based fast channel interrupt type we must register
47      * fast channel callback only once with the fast channel driver
48      * This will hold the status if callback is registered with the
49      * fast channel driver.
50      */
51     bool callback_registered;
52 #endif
53 };
54 
55 static unsigned int fast_channel_elem_size[MOD_SCMI_PERF_FAST_CHANNEL_COUNT] = {
56     [MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET] = sizeof(uint32_t),
57     [MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_SET] =
58         sizeof(struct mod_scmi_perf_fast_channel_limit),
59     [MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET] = sizeof(uint32_t),
60     [MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_GET] =
61         sizeof(struct mod_scmi_perf_fast_channel_limit)
62 };
63 
64 static struct mod_scmi_perf_fc_ctx perf_fch_ctx;
65 
66 static void fast_channel_callback(uintptr_t param);
67 
68 /*
69  * Static Helpers
70  */
71 
72 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
perf_fch_get_ctx(fwk_id_t domain_id)73 static inline struct scmi_perf_domain_ctx *perf_fch_get_ctx(fwk_id_t domain_id)
74 {
75     return &perf_fch_ctx.perf_ctx
76                 ->domain_ctx_table[fwk_id_get_element_idx(domain_id)];
77 }
78 #endif
79 
80 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
81 
get_fc_set_level_addr(uint32_t domain_idx)82 static inline uint32_t *get_fc_set_level_addr(uint32_t domain_idx)
83 {
84     const struct scmi_perf_domain_ctx *domain_ctx;
85     struct mod_scmi_perf_ctx *perf_ctx = perf_fch_ctx.perf_ctx;
86     domain_ctx = &perf_ctx->domain_ctx_table[domain_idx];
87     const struct fast_channel_ctx *fch_ctx =
88         &domain_ctx->fch_ctx[MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET];
89     return (uint32_t *)((uintptr_t)fch_ctx->fch_address.local_view_address);
90 }
91 
get_fc_set_limit_addr(uint32_t domain_idx)92 static inline struct mod_scmi_perf_fast_channel_limit *get_fc_set_limit_addr(
93     uint32_t domain_idx)
94 {
95     const struct scmi_perf_domain_ctx *domain_ctx;
96     struct mod_scmi_perf_ctx *perf_ctx = perf_fch_ctx.perf_ctx;
97     domain_ctx = &perf_ctx->domain_ctx_table[domain_idx];
98 
99     const struct fast_channel_ctx *fch_ctx =
100         &domain_ctx->fch_ctx[MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_SET];
101 
102     return (struct mod_scmi_perf_fast_channel_limit
103                 *)((uintptr_t)fch_ctx->fch_address.local_view_address);
104 }
105 
perf_fch_set_fch_get_level(uint32_t domain_idx,uint32_t level)106 void perf_fch_set_fch_get_level(uint32_t domain_idx, uint32_t level)
107 {
108     const struct scmi_perf_domain_ctx *domain_ctx;
109     const struct fast_channel_ctx *fch_ctx;
110     uint32_t *get_level;
111     struct mod_scmi_perf_ctx *perf_ctx = perf_fch_ctx.perf_ctx;
112     domain_ctx = &perf_ctx->domain_ctx_table[domain_idx];
113     if (perf_fch_domain_has_fastchannels(domain_idx)) {
114         fch_ctx = &domain_ctx->fch_ctx[MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET];
115         get_level =
116             (uint32_t *)((uintptr_t)fch_ctx->fch_address.local_view_address);
117         if (get_level != NULL) { /* note: get_level may not be defined */
118             *get_level = level;
119         }
120     }
121 }
122 
get_fch_config(unsigned int domain_idx,unsigned int fch_idx)123 static const struct scmi_perf_fch_config *get_fch_config(
124     unsigned int domain_idx,
125     unsigned int fch_idx)
126 {
127     return &(*perf_fch_ctx.perf_ctx->config->domains)[domain_idx]
128                 .fch_config[fch_idx];
129 }
130 
get_fch_ctx(unsigned int domain_idx,unsigned int fch_idx)131 static struct fast_channel_ctx *get_fch_ctx(
132     unsigned int domain_idx,
133     unsigned int fch_idx)
134 {
135     return &perf_fch_ctx.perf_ctx->domain_ctx_table[domain_idx]
136                 .fch_ctx[fch_idx];
137 }
138 
fch_context_init(const struct scmi_perf_fch_config * fch_config,struct fast_channel_ctx * fch_ctx)139 static int fch_context_init(
140     const struct scmi_perf_fch_config *fch_config,
141     struct fast_channel_ctx *fch_ctx)
142 {
143     int status = FWK_E_DATA;
144     enum mod_transport_fch_interrupt_type interrupt_type;
145 
146     status = fch_ctx->transport_fch_api->transport_get_fch_address(
147         fch_config->transport_id, &fch_ctx->fch_address);
148 
149     if (status != FWK_SUCCESS) {
150         return FWK_E_DATA;
151     }
152 
153     status = fch_ctx->transport_fch_api->transport_get_fch_interrupt_type(
154         fch_config->transport_id, &interrupt_type);
155 
156     if (status != FWK_SUCCESS) {
157         return FWK_E_DATA;
158     }
159 
160     if (interrupt_type == MOD_TRANSPORT_FCH_INTERRUPT_TYPE_TIMER &&
161         !perf_fch_ctx.callback_registered) {
162         /*
163          * For polled fast channels, we need to register one single
164          * call back for all channels so register this only once.
165          */
166         status = fch_ctx->transport_fch_api->transport_fch_register_callback(
167             fch_config->transport_id, (uintptr_t)NULL, fast_channel_callback);
168 
169         if (status != FWK_SUCCESS) {
170             return FWK_E_DATA;
171         }
172 
173         perf_fch_ctx.callback_registered = true;
174     } else if (interrupt_type == MOD_TRANSPORT_FCH_INTERRUPT_TYPE_HW) {
175         status = fch_ctx->transport_fch_api->transport_fch_register_callback(
176             fch_config->transport_id, (uintptr_t)NULL, fast_channel_callback);
177 
178         if (status != FWK_SUCCESS) {
179             return FWK_E_DATA;
180         }
181     }
182 
183     return FWK_SUCCESS;
184 }
185 #endif
186 
decrement_pending_req_count(void)187 static inline void decrement_pending_req_count(void)
188 {
189     if (perf_fch_ctx.pending_req_count > 0) {
190         perf_fch_ctx.pending_req_count--;
191     }
192 }
193 
log_and_increment_pending_req_count(void)194 static inline void log_and_increment_pending_req_count(void)
195 {
196     if (perf_fch_ctx.pending_req_count > 0) {
197         FWK_LOG_INFO("[SCMI-PERF] Multiple FC events pending");
198     }
199 
200     perf_fch_ctx.pending_req_count++;
201 }
202 
203 /*
204  * SCMI Performance helpers
205  */
206 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
perf_fch_domain_has_fastchannels(uint32_t domain_idx)207 bool perf_fch_domain_has_fastchannels(uint32_t domain_idx)
208 {
209     const struct mod_scmi_perf_domain_config *domain =
210         &(*perf_fch_ctx.perf_ctx->config->domains)[domain_idx];
211     return domain->supports_fast_channels;
212 }
213 #endif
214 
perf_fch_prot_msg_attributes_has_fastchannels(const struct scmi_protocol_message_attributes_a2p * parameters)215 bool perf_fch_prot_msg_attributes_has_fastchannels(
216     const struct scmi_protocol_message_attributes_a2p *parameters)
217 {
218     return (
219         (parameters->message_id <= MOD_SCMI_PERF_LEVEL_GET) &&
220         (parameters->message_id >= MOD_SCMI_PERF_LIMITS_SET));
221 }
222 
respond_to_scmi(fwk_id_t service_id,struct scmi_perf_describe_fc_p2a * return_values)223 static inline int respond_to_scmi(
224     fwk_id_t service_id,
225     struct scmi_perf_describe_fc_p2a *return_values)
226 {
227     return perf_fch_ctx.perf_ctx->scmi_api->respond(
228         service_id,
229         return_values,
230         (return_values->status == SCMI_SUCCESS) ?
231             sizeof(*return_values) :
232             sizeof(return_values->status));
233 }
234 
235 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
perf_fch_describe_fast_channels(fwk_id_t service_id,const uint32_t * payload)236 int perf_fch_describe_fast_channels(
237     fwk_id_t service_id,
238     const uint32_t *payload)
239 {
240     uint32_t chan_index = 0;
241     struct mod_scmi_perf_ctx *perf_ctx = perf_fch_ctx.perf_ctx;
242     const struct scmi_perf_domain_ctx *domain_ctx;
243     const struct fast_channel_ctx *fch_ctx;
244     const struct scmi_perf_describe_fc_a2p *parameters;
245     struct scmi_perf_describe_fc_p2a return_values = {
246         .status = (int32_t)SCMI_SUCCESS,
247     };
248 
249     enum scmi_perf_command_id message_id;
250 
251     parameters = (const struct scmi_perf_describe_fc_a2p *)payload;
252 
253     if (parameters->domain_id >= perf_fch_ctx.perf_ctx->domain_count) {
254         return_values.status = (int32_t)SCMI_NOT_FOUND;
255 
256         return respond_to_scmi(service_id, &return_values);
257     }
258 
259     domain_ctx = &perf_ctx->domain_ctx_table[parameters->domain_id];
260 
261     if (!perf_fch_domain_has_fastchannels(parameters->domain_id)) {
262         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
263 
264         return respond_to_scmi(service_id, &return_values);
265     }
266 
267     if (parameters->message_id >= MOD_SCMI_PERF_COMMAND_COUNT) {
268         return_values.status = (int32_t)SCMI_NOT_FOUND;
269 
270         return respond_to_scmi(service_id, &return_values);
271     }
272 
273     message_id = (enum scmi_perf_command_id)parameters->message_id;
274 
275     switch (message_id) {
276     case MOD_SCMI_PERF_LEVEL_GET:
277         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET;
278         break;
279 
280     case MOD_SCMI_PERF_LEVEL_SET:
281         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET;
282         break;
283 
284     case MOD_SCMI_PERF_LIMITS_SET:
285         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_SET;
286         break;
287 
288     case MOD_SCMI_PERF_LIMITS_GET:
289         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_GET;
290         break;
291 
292     default:
293         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
294         break;
295     }
296 
297     /* Check for failed cases above */
298     if (return_values.status != SCMI_SUCCESS) {
299         return respond_to_scmi(service_id, &return_values);
300     }
301 
302     fch_ctx = &domain_ctx->fch_ctx[chan_index];
303 
304     if (fch_ctx->fch_address.target_view_address == 0x0) {
305         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
306 
307         return respond_to_scmi(service_id, &return_values);
308     }
309 
310     return_values.attributes = 0; /* Doorbell not supported */
311     return_values.rate_limit = perf_fch_ctx.fast_channels_rate_limit;
312     return_values.chan_addr_low =
313         (uint32_t)(fch_ctx->fch_address.target_view_address & ~0UL);
314     return_values.chan_addr_high =
315         (uint32_t)((uint64_t)fch_ctx->fch_address.target_view_address >> 32);
316     return_values.chan_size = fch_ctx->fch_address.length;
317 
318     return respond_to_scmi(service_id, &return_values);
319 }
320 #else
perf_fch_describe_fast_channels(fwk_id_t service_id,const uint32_t * payload)321 int perf_fch_describe_fast_channels(
322     fwk_id_t service_id,
323     const uint32_t *payload)
324 {
325     const struct mod_scmi_perf_domain_config *domain;
326     const struct scmi_perf_describe_fc_a2p *parameters;
327     struct scmi_perf_describe_fc_p2a return_values = {
328         .status = (int32_t)SCMI_SUCCESS,
329     };
330     uint32_t chan_size = 0, chan_index = 0;
331     enum scmi_perf_command_id message_id;
332 
333     parameters = (const struct scmi_perf_describe_fc_a2p *)payload;
334 
335     if (parameters->domain_id >= perf_fch_ctx.perf_ctx->domain_count) {
336         return_values.status = (int32_t)SCMI_NOT_FOUND;
337 
338         return respond_to_scmi(service_id, &return_values);
339     }
340 
341     domain = &(*perf_fch_ctx.perf_ctx->config->domains)[parameters->domain_id];
342 
343     if (domain->fast_channels_addr_scp == NULL) {
344         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
345 
346         return respond_to_scmi(service_id, &return_values);
347     }
348 
349     if (parameters->message_id >= MOD_SCMI_PERF_COMMAND_COUNT) {
350         return_values.status = (int32_t)SCMI_NOT_FOUND;
351 
352         return respond_to_scmi(service_id, &return_values);
353     }
354 
355     message_id = (enum scmi_perf_command_id)parameters->message_id;
356 
357     switch (message_id) {
358     case MOD_SCMI_PERF_LEVEL_GET:
359         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET;
360         chan_size =
361             fast_channel_elem_size[MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET];
362         break;
363 
364     case MOD_SCMI_PERF_LEVEL_SET:
365         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET;
366         chan_size =
367             fast_channel_elem_size[MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET];
368         break;
369 
370     case MOD_SCMI_PERF_LIMITS_SET:
371         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_SET;
372         chan_size =
373             fast_channel_elem_size[MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_SET];
374         break;
375 
376     case MOD_SCMI_PERF_LIMITS_GET:
377         chan_index = (uint32_t)MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_GET;
378         chan_size =
379             fast_channel_elem_size[MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_GET];
380         break;
381 
382     default:
383         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
384         break;
385     }
386 
387     /* Check for failed cases above */
388     if (return_values.status != SCMI_SUCCESS) {
389         return respond_to_scmi(service_id, &return_values);
390     }
391 
392     if (domain->fast_channels_addr_ap == NULL ||
393         domain->fast_channels_addr_ap[chan_index] == 0x0) {
394         return_values.status = (int32_t)SCMI_NOT_SUPPORTED;
395 
396         return respond_to_scmi(service_id, &return_values);
397     }
398 
399     return_values.attributes = 0; /* Doorbell not supported */
400     return_values.rate_limit = perf_fch_ctx.fast_channels_rate_limit;
401     return_values.chan_addr_low =
402         (uint32_t)(domain->fast_channels_addr_ap[chan_index] & ~0UL);
403     return_values.chan_addr_high =
404         (uint32_t)(domain->fast_channels_addr_ap[chan_index] >> 32);
405     return_values.chan_size = chan_size;
406 
407     return respond_to_scmi(service_id, &return_values);
408 }
409 #endif
410 
411 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
adjust_level_for_limits(const struct mod_scmi_perf_level_limits * limits,uint32_t * level)412 static void adjust_level_for_limits(
413     const struct mod_scmi_perf_level_limits *limits,
414     uint32_t *level)
415 {
416     if (*level < limits->minimum) {
417         *level = limits->minimum;
418     } else if (*level > limits->maximum) {
419         *level = limits->maximum;
420     }
421 }
422 
423 /*
424  * Evaluate the level & limits in one go.
425  * Because the limits may also come from the external plugins, their value may
426  * not always be exact to the OPPs, so allow approximation.
427  */
perf_eval_performance(fwk_id_t domain_id,const struct mod_scmi_perf_level_limits * limits,uint32_t * level)428 static void perf_eval_performance(
429     fwk_id_t domain_id,
430     const struct mod_scmi_perf_level_limits *limits,
431     uint32_t *level)
432 {
433     struct scmi_perf_domain_ctx *domain_ctx;
434     int status;
435 
436     if (limits->minimum > limits->maximum) {
437         return;
438     }
439 
440     domain_ctx = perf_fch_get_ctx(domain_id);
441 
442     adjust_level_for_limits(limits, level);
443     if ((limits->minimum == domain_ctx->level_limits.minimum) &&
444         (limits->maximum == domain_ctx->level_limits.maximum)) {
445         status = perf_fch_ctx.api_fch_stub->find_opp_for_level(
446             domain_ctx, level, true);
447         if (status != FWK_SUCCESS) {
448             FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
449         }
450         return;
451     }
452 
453     perf_fch_ctx.api_fch_stub->notify_limits_updated(
454         domain_id, limits->minimum, limits->maximum);
455 
456     domain_ctx->level_limits.minimum = limits->minimum;
457     domain_ctx->level_limits.maximum = limits->maximum;
458 
459     status =
460         perf_fch_ctx.api_fch_stub->find_opp_for_level(domain_ctx, level, true);
461     if (status != FWK_SUCCESS) {
462         FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
463     }
464 }
465 #endif
466 
467 /*
468  * Fast Channel Polling
469  */
fast_channel_callback(uintptr_t param)470 static void fast_channel_callback(uintptr_t param)
471 {
472     int status;
473 
474     struct fwk_event_light event = (struct fwk_event_light){
475         .id = FWK_ID_EVENT(
476             FWK_MODULE_IDX_SCMI_PERF,
477             SCMI_PERF_EVENT_IDX_FAST_CHANNELS_PROCESS),
478         .source_id = FWK_ID_MODULE(FWK_MODULE_IDX_SCMI_PERF),
479         .target_id = FWK_ID_MODULE(FWK_MODULE_IDX_SCMI_PERF),
480     };
481 
482     status = fwk_put_event(&event);
483     if (status != FWK_SUCCESS) {
484         FWK_LOG_DEBUG("[SCMI-PERF] Error creating FC process event.");
485         return;
486     }
487 
488     log_and_increment_pending_req_count();
489 }
490 
load_tlimits(struct mod_scmi_perf_fast_channel_limit * set_limit,uint32_t * tmax,uint32_t * tmin,struct scmi_perf_domain_ctx * domain_ctx)491 static inline void load_tlimits(
492     struct mod_scmi_perf_fast_channel_limit *set_limit,
493     uint32_t *tmax,
494     uint32_t *tmin,
495     struct scmi_perf_domain_ctx *domain_ctx)
496 {
497     if (set_limit != NULL) {
498         *tmax = set_limit->range_max;
499         *tmin = set_limit->range_min;
500     } else {
501         *tmax = domain_ctx->level_limits.maximum;
502         *tmin = domain_ctx->level_limits.minimum;
503     }
504 }
505 
load_tlevel(uint32_t * set_level,uint32_t * tlevel,struct scmi_perf_domain_ctx * domain_ctx)506 static inline void load_tlevel(
507     uint32_t *set_level,
508     uint32_t *tlevel,
509     struct scmi_perf_domain_ctx *domain_ctx)
510 {
511     *tlevel = (set_level != NULL) ? *set_level : domain_ctx->curr_level;
512 }
513 
514 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
perf_fch_process_plugins_handler(void)515 static void perf_fch_process_plugins_handler(void)
516 {
517     struct mod_scmi_perf_fast_channel_limit *set_limit;
518     struct scmi_perf_domain_ctx *domain_ctx;
519     uint32_t *set_level;
520     uint32_t tlevel, tmax, tmin;
521     unsigned int i;
522     int status;
523 
524     struct mod_scmi_perf_ctx *perf_ctx = perf_fch_ctx.perf_ctx;
525     struct fc_perf_update update;
526 
527     for (i = 0; i < perf_ctx->domain_count; i++) {
528         if (perf_fch_domain_has_fastchannels(i)) {
529             set_limit = get_fc_set_limit_addr(i);
530             set_level = get_fc_set_level_addr(i);
531 
532             domain_ctx = &perf_ctx->domain_ctx_table[i];
533             load_tlimits(set_limit, &tmax, &tmin, domain_ctx);
534 
535             load_tlevel(set_level, &tlevel, domain_ctx);
536 
537             update = (struct fc_perf_update){
538                 .domain_id = get_dependency_id(i),
539                 .level = tlevel,
540                 .max_limit = tmax,
541                 .min_limit = tmin,
542             };
543 
544             perf_plugins_handler_update(i, &update);
545         }
546     }
547 
548     for (i = 0; i < perf_ctx->domain_count; i++) {
549         if (perf_fch_domain_has_fastchannels(i)) {
550             set_limit = get_fc_set_limit_addr(i);
551             set_level = get_fc_set_level_addr(i);
552 
553             domain_ctx = &perf_ctx->domain_ctx_table[i];
554 
555             load_tlimits(set_limit, &tmax, &tmin, domain_ctx);
556 
557             load_tlevel(set_level, &tlevel, domain_ctx);
558 
559             update = (struct fc_perf_update){
560                 .domain_id = get_dependency_id(i),
561                 .level = tlevel,
562                 .max_limit = tmax,
563                 .min_limit = tmin,
564             };
565 
566             perf_plugins_handler_get(i, &update);
567 
568             tlevel = update.level;
569             tmax = update.adj_max_limit;
570             tmin = update.adj_min_limit;
571 
572             perf_eval_performance(
573                 FWK_ID_ELEMENT(FWK_MODULE_IDX_SCMI_PERF, i),
574                 &((struct mod_scmi_perf_level_limits){
575                     .minimum = tmin,
576                     .maximum = tmax,
577                 }),
578                 &tlevel);
579 
580             status = perf_fch_ctx.perf_ctx->dvfs_api->set_level(
581                 get_dependency_id(i), 0, tlevel);
582             if (status != FWK_SUCCESS) {
583                 FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
584             }
585         }
586     }
587 
588     decrement_pending_req_count();
589 }
590 #endif
591 
592 #ifndef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
perf_fch_process(void)593 static void perf_fch_process(void)
594 {
595     struct mod_scmi_perf_fast_channel_limit *set_limit;
596     struct scmi_perf_domain_ctx *domain_ctx;
597     uint32_t *set_level;
598     uint32_t tlevel, tmax, tmin;
599     unsigned int i;
600     int status;
601 
602     struct mod_scmi_perf_ctx *perf_ctx = perf_fch_ctx.perf_ctx;
603 
604     for (i = 0; i < perf_ctx->domain_count; i++) {
605         if (perf_fch_domain_has_fastchannels(i)) {
606             set_limit = get_fc_set_limit_addr(i);
607             set_level = get_fc_set_level_addr(i);
608 
609             domain_ctx = &perf_ctx->domain_ctx_table[i];
610 
611             load_tlimits(set_limit, &tmax, &tmin, domain_ctx);
612 
613             load_tlevel(set_level, &tlevel, domain_ctx);
614 
615             if (set_level != NULL && tlevel > 0) {
616                 status = perf_fch_ctx.api_fch_stub->perf_set_level(
617                     get_dependency_id(i), 0, tlevel);
618                 if (status != FWK_SUCCESS) {
619                     FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
620                 }
621             }
622             if (set_limit != NULL) {
623                 if ((tmax == 0) && (tmin == 0)) {
624                     continue;
625                 }
626                 status = perf_fch_ctx.api_fch_stub->perf_set_limits(
627                     get_dependency_id(i),
628                     0,
629                     &((struct mod_scmi_perf_level_limits){
630                         .minimum = tmin,
631                         .maximum = tmax,
632                     }));
633                 if (status != FWK_SUCCESS) {
634                     FWK_LOG_DEBUG("[SCMI-PERF] %s @%d", __func__, __LINE__);
635                 }
636             }
637         }
638     }
639 
640     decrement_pending_req_count();
641 }
642 #endif
643 
644 /*
645  * Framework Handlers, via SCMI-Perf
646  */
perf_fch_init(fwk_id_t module_id,unsigned int element_count,const void * data,struct mod_scmi_perf_ctx * mod_ctx,struct mod_scmi_perf_private_api_perf_stub * api)647 int perf_fch_init(
648     fwk_id_t module_id,
649     unsigned int element_count,
650     const void *data,
651     struct mod_scmi_perf_ctx *mod_ctx,
652     struct mod_scmi_perf_private_api_perf_stub *api)
653 {
654     perf_fch_ctx.perf_ctx = mod_ctx;
655     perf_fch_ctx.api_fch_stub = api;
656 
657     return FWK_SUCCESS;
658 }
659 
660 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
perf_fch_bind(fwk_id_t id,unsigned int round)661 int perf_fch_bind(fwk_id_t id, unsigned int round)
662 {
663     unsigned int domain_idx;
664     const struct scmi_perf_fch_config *fch_config;
665     static struct fast_channel_ctx *fch_ctx;
666     int status;
667     /* Bind fast channels */
668     for (domain_idx = 0; domain_idx < perf_fch_ctx.perf_ctx->domain_count;
669          domain_idx++) {
670         unsigned int fch_idx;
671         for (fch_idx = 0; fch_idx < MOD_SCMI_PERF_FAST_CHANNEL_COUNT;
672              fch_idx++) {
673             fch_config = get_fch_config(domain_idx, fch_idx);
674             fch_ctx = get_fch_ctx(domain_idx, fch_idx);
675             status = fwk_module_bind(
676                 fch_config->transport_id,
677                 fch_config->transport_api_id,
678                 &fch_ctx->transport_fch_api);
679             if (status != FWK_SUCCESS) {
680                 return FWK_E_PANIC;
681             }
682         }
683     }
684 
685     return FWK_SUCCESS;
686 }
687 #endif
688 
get_fch_local_address(unsigned int domain_idx,unsigned int fch_idx)689 static void *get_fch_local_address(
690     unsigned int domain_idx,
691     unsigned int fch_idx)
692 {
693 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
694     const struct scmi_perf_fch_config *fch_config;
695     struct fast_channel_ctx *fch_ctx;
696     int status;
697 
698     fch_config = get_fch_config(domain_idx, fch_idx);
699     fch_ctx = get_fch_ctx(domain_idx, fch_idx);
700 
701     status = fch_context_init(fch_config, fch_ctx);
702 
703     if (status != FWK_SUCCESS) {
704         return NULL;
705     }
706 
707     return (void *)fch_ctx->fch_address.local_view_address;
708 #endif
709 }
710 
711 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
initialize_fch_channels_plugins_handler(void)712 static int initialize_fch_channels_plugins_handler(void)
713 {
714     struct mod_scmi_perf_fast_channel_limit *fc_limits;
715 
716     struct scmi_perf_domain_ctx *domain_ctx;
717     struct mod_dvfs_opp opp;
718     unsigned int j, i;
719     void *fc_elem;
720     int status;
721 
722     struct perf_opp_table *opp_table = NULL;
723 
724     /*
725      * Initialise FastChannels level to sustained level and limits to min/max
726      * OPPs.
727      */
728     for (i = 0; i < perf_fch_ctx.perf_ctx->domain_count; i++) {
729         if (perf_fch_domain_has_fastchannels(i)) {
730             for (j = 0; j < MOD_SCMI_PERF_FAST_CHANNEL_COUNT; j++) {
731                 fc_elem = get_fch_local_address(i, j);
732                 if (fc_elem != NULL) {
733                     if ((j == MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_SET) ||
734                         (j == MOD_SCMI_PERF_FAST_CHANNEL_LEVEL_GET)) {
735                         status =
736                             perf_fch_ctx.perf_ctx->dvfs_api->get_sustained_opp(
737                                 get_dependency_id(i), &opp);
738                         if (status != FWK_SUCCESS) {
739                             return status;
740                         }
741 
742                         fwk_str_memcpy(
743                             fc_elem, &opp.level, fast_channel_elem_size[j]);
744                     } else {
745                         /* _LIMIT_SET or _LIMIT_GET */
746                         domain_ctx =
747                             &perf_fch_ctx.perf_ctx->domain_ctx_table[i];
748                         opp_table = domain_ctx->opp_table;
749 
750                         fc_limits =
751                             (struct mod_scmi_perf_fast_channel_limit *)fc_elem;
752                         fc_limits->range_min = opp_table->opps[0].level;
753                         fc_limits->range_max =
754                             opp_table->opps[opp_table->opp_count - 1].level;
755                     }
756                 }
757             }
758         }
759     }
760 
761     return FWK_SUCCESS;
762 }
763 #endif
764 
765 #ifndef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
initialize_fch_channels(void)766 static void initialize_fch_channels(void)
767 {
768     unsigned int j, i;
769     void *fc_elem;
770 
771     /* Initialise FastChannels to 0 */
772     for (i = 0; i < perf_fch_ctx.perf_ctx->domain_count; i++) {
773         if (perf_fch_domain_has_fastchannels(i)) {
774             for (j = 0; j < MOD_SCMI_PERF_FAST_CHANNEL_COUNT; j++) {
775                 fc_elem = get_fch_local_address(i, j);
776                 if (fc_elem != NULL) {
777                     fwk_str_memset(fc_elem, 0, fast_channel_elem_size[j]);
778                 }
779             }
780         }
781     }
782 }
783 #endif
784 
perf_fch_start(fwk_id_t id)785 int perf_fch_start(fwk_id_t id)
786 {
787 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
788     return initialize_fch_channels_plugins_handler();
789 #else
790     initialize_fch_channels();
791 
792     return FWK_SUCCESS;
793 #endif
794 }
795 
perf_fch_process_event(const struct fwk_event * event)796 int perf_fch_process_event(const struct fwk_event *event)
797 {
798     int status;
799     enum scmi_perf_event_idx event_idx =
800         (enum scmi_perf_event_idx)fwk_id_get_event_idx(event->id);
801 
802     switch (event_idx) {
803     case SCMI_PERF_EVENT_IDX_FAST_CHANNELS_PROCESS:
804 
805 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
806         perf_fch_process_plugins_handler();
807 #else
808         perf_fch_process();
809 #endif
810 
811         status = FWK_SUCCESS;
812         break;
813 
814     default:
815         status = FWK_E_PARAM;
816         break;
817     }
818 
819     return status;
820 }
821