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