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