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 domain management protocol support.
9 */
10
11 #include <internal/scmi_perf.h>
12
13 #include <mod_dvfs.h>
14 #ifdef BUILD_HAS_MOD_PERF_CONTROLLER
15 # include <mod_perf_controller.h>
16 #endif
17 #include <mod_scmi.h>
18 #include <mod_scmi_perf.h>
19 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
20 # include "perf_plugins_handler.h"
21 #endif
22 #include <mod_timer.h>
23 #include "scmi_perf.h"
24
25 #include <fwk_assert.h>
26 #include <fwk_core.h>
27 #include <fwk_event.h>
28 #include <fwk_id.h>
29 #include <fwk_log.h>
30 #include <fwk_macros.h>
31 #include <fwk_mm.h>
32 #include <fwk_module.h>
33 #include <fwk_module_idx.h>
34 #include <fwk_status.h>
35 #include <fwk_string.h>
36
37 #include <stdbool.h>
38 #include <stddef.h>
39
40 static struct mod_scmi_perf_ctx scmi_perf_ctx;
41
42 /*
43 * SCMI PERF Helpers
44 */
45
46 /* This identifier is either:
47 * - exactly the one built by the perf-plugins-handler (sub-element type)
48 * - or the DVFS domain
49 */
get_dependency_id(unsigned int el_idx)50 fwk_id_t get_dependency_id(unsigned int el_idx)
51 {
52 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
53 return perf_plugins_get_dependency_id(el_idx);
54 #else
55 return FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, el_idx);
56 #endif
57 }
58
get_ctx(fwk_id_t domain_id)59 static inline struct scmi_perf_domain_ctx *get_ctx(fwk_id_t domain_id)
60 {
61 return &scmi_perf_ctx.domain_ctx_table[fwk_id_get_element_idx(domain_id)];
62 }
63
opp_for_level_found(uint32_t * level,struct perf_opp_table * opp_table,size_t i)64 static inline int opp_for_level_found(
65 uint32_t *level,
66 struct perf_opp_table *opp_table,
67 size_t i)
68 {
69 *level = opp_table->opps[i].level;
70
71 return FWK_SUCCESS;
72 }
73
find_opp_for_level(struct scmi_perf_domain_ctx * domain_ctx,uint32_t * level,bool use_nearest)74 static int find_opp_for_level(
75 struct scmi_perf_domain_ctx *domain_ctx,
76 uint32_t *level,
77 bool use_nearest)
78 {
79 struct perf_opp_table *opp_table;
80 size_t i;
81 uint32_t opp_level, limit_max;
82
83 opp_table = domain_ctx->opp_table;
84 limit_max = domain_ctx->level_limits.maximum;
85
86 for (i = 0; i < opp_table->opp_count; i++) {
87 opp_level = opp_table->opps[i].level;
88
89 if ((use_nearest &&
90 ((opp_level < *level) && (opp_level < limit_max))) ||
91 (!use_nearest && (opp_level != *level))) {
92 /*
93 * The current OPP level is either below the desired level
94 * or not exact match found.
95 */
96 continue;
97 } else {
98 /*
99 * Either found exact match, or the current OPP is above the limit.
100 * Must be within limits.
101 */
102 if ((opp_level > limit_max) && (i > 0)) {
103 i--;
104 }
105
106 return opp_for_level_found(level, opp_table, i);
107 }
108 }
109
110 /* Either not exact match or approximate to the highest level */
111 if (use_nearest) {
112 i--;
113
114 return opp_for_level_found(level, opp_table, i);
115 }
116
117 return FWK_E_RANGE;
118 }
119
120 #if defined(BUILD_HAS_SCMI_PERF_PROTOCOL_OPS) || \
121 defined(BUILD_HAS_SCMI_PERF_FAST_CHANNELS)
perf_set_level(fwk_id_t domain_id,unsigned int agent_id,uint32_t perf_level)122 static int perf_set_level(
123 fwk_id_t domain_id,
124 unsigned int agent_id,
125 uint32_t perf_level)
126 {
127 struct scmi_perf_domain_ctx *domain_ctx;
128 int status;
129
130 domain_ctx = get_ctx(domain_id);
131
132 status = find_opp_for_level(
133 domain_ctx, &perf_level, scmi_perf_ctx.config->approximate_level);
134 if (status != FWK_SUCCESS) {
135 return status;
136 }
137
138 if ((perf_level < domain_ctx->level_limits.minimum) ||
139 (perf_level > domain_ctx->level_limits.maximum)) {
140 return FWK_E_RANGE;
141 }
142 # ifdef BUILD_HAS_MOD_PERF_CONTROLLER
143 return scmi_perf_ctx.perf_controller_api->set_performance_level(
144 domain_id, agent_id, perf_level);
145 # else
146 return scmi_perf_ctx.dvfs_api->set_level(domain_id, agent_id, perf_level);
147 # endif
148 }
149 #endif
150
validate_new_limits(struct scmi_perf_domain_ctx * domain_ctx,const struct mod_scmi_perf_level_limits * limits)151 static int validate_new_limits(
152 struct scmi_perf_domain_ctx *domain_ctx,
153 const struct mod_scmi_perf_level_limits *limits)
154 {
155 uint32_t limit;
156 int status;
157
158 if (scmi_perf_ctx.config->approximate_level) {
159 /* When approx level is chosen, a level is always found */
160 return FWK_SUCCESS;
161 }
162
163 limit = limits->minimum;
164 status = find_opp_for_level(domain_ctx, &limit, false);
165 if (status != FWK_SUCCESS) {
166 return status;
167 }
168
169 limit = limits->maximum;
170 return find_opp_for_level(domain_ctx, &limit, false);
171 }
172
173 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
scmi_perf_notify_limits_fch_updated(fwk_id_t domain_id,uint32_t range_min,uint32_t range_max)174 static void scmi_perf_notify_limits_fch_updated(
175 fwk_id_t domain_id,
176 uint32_t range_min,
177 uint32_t range_max)
178 {
179 unsigned int idx;
180 idx = fwk_id_get_element_idx(domain_id);
181 struct mod_scmi_perf_fast_channel_limit *get_limit;
182 struct scmi_perf_domain_ctx *domain_ctx;
183 const struct fast_channel_ctx *fch_ctx;
184 domain_ctx = &scmi_perf_ctx.domain_ctx_table[idx];
185 if (perf_fch_domain_has_fastchannels(idx)) {
186 fch_ctx = &domain_ctx->fch_ctx[MOD_SCMI_PERF_FAST_CHANNEL_LIMIT_GET];
187 get_limit = (struct mod_scmi_perf_fast_channel_limit
188 *)((uintptr_t)fch_ctx->fch_address.local_view_address);
189 if (get_limit != NULL) { /* note: get_limit may not be defined */
190 get_limit->range_max = range_max;
191 get_limit->range_min = range_min;
192 }
193 }
194 }
195 #endif
196
197 /*
198 * A domain limits range has been updated. Depending on the system
199 * configuration we may send an SCMI notification to the agents which
200 * have registered for these notifications and/or update the associated
201 * fast channels.
202 */
scmi_perf_notify_limits_updated(fwk_id_t domain_id,uint32_t range_min,uint32_t range_max)203 static void scmi_perf_notify_limits_updated(
204 fwk_id_t domain_id,
205 uint32_t range_min,
206 uint32_t range_max)
207 {
208 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
209 scmi_perf_notify_limits_fch_updated(domain_id, range_min, range_max);
210 #endif
211
212 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
213 struct perf_plugins_perf_report perf_report = {
214 .dep_dom_id = domain_id,
215 .max_limit = range_max,
216 .min_limit = range_min,
217 };
218
219 perf_plugins_handler_report(&perf_report);
220 #endif
221
222 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
223 unsigned int idx;
224 idx = fwk_id_get_element_idx(domain_id);
225 perf_prot_ops_notify_limits(idx, range_min, range_max);
226 #endif
227 }
228
perf_set_limits(fwk_id_t domain_id,unsigned int agent_id,const struct mod_scmi_perf_level_limits * limits)229 int perf_set_limits(
230 fwk_id_t domain_id,
231 unsigned int agent_id,
232 const struct mod_scmi_perf_level_limits *limits)
233 {
234 struct scmi_perf_domain_ctx *domain_ctx;
235 uint32_t needle;
236 int status;
237 bool needs_new_level = true;
238
239 if (limits->minimum > limits->maximum) {
240 return FWK_E_PARAM;
241 }
242
243 domain_ctx = get_ctx(domain_id);
244
245 if ((limits->minimum == domain_ctx->level_limits.minimum) &&
246 (limits->maximum == domain_ctx->level_limits.maximum)) {
247 return FWK_SUCCESS;
248 }
249
250 status = validate_new_limits(domain_ctx, limits);
251 if (status != FWK_SUCCESS) {
252 return status;
253 }
254
255 /* Adjust opp for new limits */
256 if (domain_ctx->curr_level < limits->minimum) {
257 needle = limits->minimum;
258 } else if (domain_ctx->curr_level > limits->maximum) {
259 needle = limits->maximum;
260 } else {
261 /* No level transition necessary */
262 needs_new_level = false;
263 }
264
265 scmi_perf_notify_limits_updated(
266 domain_id, limits->minimum, limits->maximum);
267
268 domain_ctx->level_limits.minimum = limits->minimum;
269 domain_ctx->level_limits.maximum = limits->maximum;
270
271 if (!needs_new_level) {
272 return FWK_SUCCESS;
273 }
274
275 status = find_opp_for_level(
276 domain_ctx, &needle, scmi_perf_ctx.config->approximate_level);
277 if (status != FWK_SUCCESS) {
278 return status;
279 }
280
281 #ifdef BUILD_HAS_MOD_PERF_CONTROLLER
282 return scmi_perf_ctx.perf_controller_api->set_performance_level(
283 domain_id, agent_id, needle);
284 #else
285 return scmi_perf_ctx.dvfs_api->set_level(domain_id, agent_id, needle);
286 #endif
287 }
288
289 /*
290 * A domain performance level has been updated. Depending on the system
291 * configuration we may send an SCMI notification to the agents which
292 * have registered for these notifications and/or update the associated
293 * fast channels.
294 */
scmi_perf_notify_level_updated(fwk_id_t domain_id,uintptr_t cookie,uint32_t level)295 static void scmi_perf_notify_level_updated(
296 fwk_id_t domain_id,
297 uintptr_t cookie,
298 uint32_t level)
299 {
300 struct scmi_perf_domain_ctx *domain_ctx;
301 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
302 fwk_id_t dep_dom_id;
303 #endif
304
305 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
306 unsigned int idx = fwk_id_get_element_idx(domain_id);
307 #endif
308
309 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
310 struct perf_plugins_perf_report perf_report = {
311 .dep_dom_id = domain_id,
312 .level = level,
313 };
314
315 perf_plugins_handler_report(&perf_report);
316 #endif
317
318 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
319 /*
320 * The SCMI spec enforces that "[PERFORMANCE_LEVEL_GET] this command returns
321 * the current performance level of a domain", thus when a physical domain
322 * has been updated, we update all the relevant logical domains.
323 */
324 for (uint32_t i = 0; i < scmi_perf_ctx.domain_count; i++) {
325 dep_dom_id = get_dependency_id((unsigned int)i);
326
327 if (fwk_id_get_element_idx(dep_dom_id) ==
328 fwk_id_get_element_idx(domain_id)) {
329 perf_fch_set_fch_get_level(i, level);
330 }
331 }
332 #endif
333
334 #ifdef BUILD_HAS_SCMI_NOTIFICATIONS
335 perf_prot_ops_notify_level(idx, level, cookie);
336 #endif
337
338 #ifdef BUILD_HAS_MOD_STATISTICS
339 perf_prot_ops_update_stats(domain_id, level);
340 #endif
341
342 domain_ctx = get_ctx(domain_id);
343 domain_ctx->curr_level = level;
344 }
345
346 static struct mod_scmi_perf_updated_api perf_update_api = {
347 .notify_level_updated = scmi_perf_notify_level_updated,
348 };
349
350 #if defined(BUILD_HAS_SCMI_PERF_PROTOCOL_OPS) || \
351 defined(BUILD_HAS_SCMI_PERF_FAST_CHANNELS)
352 static struct mod_scmi_perf_private_api_perf_stub api_perf_stub = {
353 .perf_set_level = perf_set_level,
354 .perf_set_limits = perf_set_limits,
355 .find_opp_for_level = find_opp_for_level,
356 .notify_limits_updated = scmi_perf_notify_limits_updated,
357 };
358 #endif
359
360 /*
361 * Framework handlers
362 */
scmi_perf_init(fwk_id_t module_id,unsigned int element_count,const void * data)363 static int scmi_perf_init(fwk_id_t module_id, unsigned int element_count,
364 const void *data)
365 {
366 int dvfs_doms_count;
367 const struct mod_scmi_perf_config *config =
368 (const struct mod_scmi_perf_config *)data;
369
370 #if defined(BUILD_HAS_SCMI_PERF_PROTOCOL_OPS) || \
371 defined(BUILD_HAS_SCMI_PERF_FAST_CHANNELS)
372 int status;
373 #endif
374
375 if ((config == NULL) || (config->domains == NULL)) {
376 return FWK_E_PARAM;
377 }
378
379 dvfs_doms_count =
380 fwk_module_get_element_count(FWK_ID_MODULE(FWK_MODULE_IDX_DVFS));
381 if (dvfs_doms_count <= 0) {
382 return FWK_E_SUPPORT;
383 }
384
385 scmi_perf_ctx.dvfs_doms_count = (unsigned int)dvfs_doms_count;
386
387 scmi_perf_ctx.domain_ctx_table = fwk_mm_calloc(
388 config->perf_doms_count, sizeof(struct scmi_perf_domain_ctx));
389
390 scmi_perf_ctx.config = config;
391 scmi_perf_ctx.domain_count = (uint32_t)config->perf_doms_count;
392 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
393 status = perf_fch_init(
394 module_id, element_count, data, &scmi_perf_ctx, &api_perf_stub);
395 if (status != FWK_SUCCESS) {
396 return FWK_E_PANIC;
397 }
398 #endif
399
400 #ifdef BUILD_HAS_SCMI_PERF_PROTOCOL_OPS
401 status = perf_prot_ops_init(
402 module_id, element_count, data, &scmi_perf_ctx, &api_perf_stub);
403 if (status != FWK_SUCCESS) {
404 return FWK_E_PANIC;
405 }
406 #endif
407
408 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
409 return perf_plugins_handler_init(config);
410 #else
411 return FWK_SUCCESS;
412 #endif
413 }
414
scmi_perf_bind(fwk_id_t id,unsigned int round)415 static int scmi_perf_bind(fwk_id_t id, unsigned int round)
416 {
417 #if defined(BUILD_HAS_SCMI_PERF_PROTOCOL_OPS) || \
418 defined(BUILD_HAS_SCMI_PERF_FAST_CHANNELS) || \
419 defined(BUILD_HAS_MOD_PERF_CONTROLLER)
420 int status;
421 #endif
422
423 if (round == 1) {
424 return FWK_SUCCESS;
425 }
426
427 #if defined(BUILD_HAS_SCMI_PERF_FAST_CHANNELS)
428 status = perf_fch_bind(id, 0);
429 if (status != FWK_SUCCESS) {
430 return status;
431 }
432 #endif
433 #ifdef BUILD_HAS_SCMI_PERF_PROTOCOL_OPS
434 status = fwk_module_bind(
435 FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
436 FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),
437 &scmi_perf_ctx.scmi_api);
438 if (status != FWK_SUCCESS) {
439 return status;
440 }
441
442 status = perf_prot_ops_bind(id, 0);
443 if (status != FWK_SUCCESS) {
444 return status;
445 }
446 #endif
447
448 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
449 status = perf_plugins_handler_bind();
450 if (status != FWK_SUCCESS) {
451 return status;
452 }
453 #endif
454
455 #ifdef BUILD_HAS_MOD_PERF_CONTROLLER
456 status = fwk_module_bind(
457 FWK_ID_MODULE(FWK_MODULE_IDX_PERF_CONTROLLER),
458 FWK_ID_API(
459 FWK_MODULE_IDX_PERF_CONTROLLER, MOD_PERF_CONTROLLER_PERF_API),
460 &scmi_perf_ctx.perf_controller_api);
461
462 if (status != FWK_SUCCESS) {
463 return status;
464 }
465 #endif
466
467 return fwk_module_bind(
468 FWK_ID_MODULE(FWK_MODULE_IDX_DVFS),
469 FWK_ID_API(FWK_MODULE_IDX_DVFS, MOD_DVFS_API_IDX_DVFS),
470 &scmi_perf_ctx.dvfs_api);
471 }
472
scmi_perf_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)473 static int scmi_perf_process_bind_request(fwk_id_t source_id,
474 fwk_id_t target_id, fwk_id_t api_id, const void **api)
475 {
476 enum scmi_perf_api_idx api_id_type =
477 (enum scmi_perf_api_idx)fwk_id_get_api_idx(api_id);
478
479 switch (api_id_type) {
480 #ifdef BUILD_HAS_SCMI_PERF_PROTOCOL_OPS
481 case MOD_SCMI_PERF_PROTOCOL_API:
482 perf_prot_ops_process_bind_request(source_id, target_id, api_id, api);
483 break;
484 #endif
485
486 case MOD_SCMI_PERF_DVFS_UPDATE_API:
487 if (!fwk_id_is_equal(source_id, fwk_module_id_dvfs)) {
488 /* Only DVFS can use this API */
489 return FWK_E_ACCESS;
490 }
491
492 *api = &perf_update_api;
493 break;
494
495 case MOD_SCMI_PERF_PLUGINS_API:
496 #ifdef BUILD_HAS_SCMI_PERF_PLUGIN_HANDLER
497 return perf_plugins_handler_process_bind_request(
498 source_id, target_id, api_id, api);
499 #else
500 return FWK_E_ACCESS;
501 #endif
502 break;
503
504 default:
505 return FWK_E_ACCESS;
506 }
507
508 return FWK_SUCCESS;
509 }
510
511
scmi_perf_start(fwk_id_t id)512 static int scmi_perf_start(fwk_id_t id)
513 {
514 int status = FWK_SUCCESS;
515
516 struct scmi_perf_domain_ctx *domain_ctx;
517 fwk_id_t domain_id;
518 size_t opp_count;
519 unsigned int i;
520
521 const struct mod_dvfs_domain_config *dvfs_config;
522 struct perf_opp_table *opp_table = NULL;
523 const struct mod_scmi_perf_domain_config *domain_cfg;
524 unsigned int dom_idx;
525 bool has_phy_group;
526
527 scmi_perf_ctx.opp_table = fwk_mm_calloc(
528 scmi_perf_ctx.dvfs_doms_count, sizeof(struct perf_opp_table));
529
530 for (i = 0; i < scmi_perf_ctx.dvfs_doms_count; i++) {
531 opp_table = &scmi_perf_ctx.opp_table[i];
532
533 domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, i);
534
535 /* Get the number of levels for this DVFS domain */
536 status = scmi_perf_ctx.dvfs_api->get_opp_count(domain_id, &opp_count);
537 if (status != FWK_SUCCESS) {
538 return status;
539 }
540
541 dvfs_config =
542 (const struct mod_dvfs_domain_config *)fwk_module_get_data(
543 domain_id);
544
545 opp_table->opps = &dvfs_config->opps[0];
546 opp_table->opp_count = opp_count;
547 opp_table->dvfs_id = domain_id;
548 }
549
550 /* Assign to each performance domain the correct OPP table */
551 for (dom_idx = 0; dom_idx < scmi_perf_ctx.domain_count; dom_idx++) {
552 domain_cfg = &(*scmi_perf_ctx.config->domains)[dom_idx];
553
554 has_phy_group = fwk_optional_id_is_defined(domain_cfg->phy_group_id);
555 if (has_phy_group) {
556 domain_id = domain_cfg->phy_group_id;
557 } else {
558 domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, dom_idx);
559 }
560
561 domain_ctx = &scmi_perf_ctx.domain_ctx_table[dom_idx];
562
563 /*
564 * Search the corresponding physical domain for this performance domain
565 * and assign the correct opp_table.
566 */
567 for (i = 0; i < scmi_perf_ctx.dvfs_doms_count; i++) {
568 opp_table = &scmi_perf_ctx.opp_table[i];
569
570 if (fwk_id_is_equal(opp_table->dvfs_id, domain_id)) {
571 domain_ctx->opp_table = opp_table;
572
573 /* init limits */
574 domain_ctx->level_limits.minimum = opp_table->opps[0].level;
575 domain_ctx->level_limits.maximum =
576 opp_table->opps[opp_table->opp_count - 1].level;
577
578 break;
579 }
580 }
581 if (domain_ctx->opp_table == NULL) {
582 /* The corresponding physical domain did not have a match */
583 return FWK_E_PANIC;
584 }
585 }
586
587 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
588 status = perf_fch_start(id);
589 if (status != FWK_SUCCESS) {
590 return status;
591 }
592 #endif
593
594 #ifdef BUILD_HAS_SCMI_PERF_PROTOCOL_OPS
595 status = perf_prot_ops_start(id);
596 if (status != FWK_SUCCESS) {
597 return status;
598 }
599 #endif
600
601 return status;
602 }
603
604 /* Handle internal events */
process_internal_event(const struct fwk_event * event)605 static int process_internal_event(const struct fwk_event *event)
606 {
607 int status;
608 enum scmi_perf_event_idx event_idx =
609 (enum scmi_perf_event_idx)fwk_id_get_event_idx(event->id);
610
611 switch (event_idx) {
612 #ifdef BUILD_HAS_SCMI_PERF_FAST_CHANNELS
613 case SCMI_PERF_EVENT_IDX_FAST_CHANNELS_PROCESS:
614 status = perf_fch_process_event(event);
615 break;
616 #endif
617 default:
618 status = FWK_E_PARAM;
619 break;
620 }
621
622 return status;
623 }
624
scmi_perf_process_event(const struct fwk_event * event,struct fwk_event * resp_event)625 static int scmi_perf_process_event(
626 const struct fwk_event *event,
627 struct fwk_event *resp_event)
628 {
629 #ifdef BUILD_HAS_SCMI_PERF_PROTOCOL_OPS
630 if ((fwk_id_get_module_idx(event->source_id) ==
631 fwk_id_get_module_idx(fwk_module_id_scmi)) ||
632 (fwk_id_get_module_idx(event->source_id) ==
633 fwk_id_get_module_idx(fwk_module_id_dvfs))) {
634 /* Handle requests from SCMi and responses from DVFS */
635 return perf_prot_ops_process_events(event, resp_event);
636 }
637 #endif
638
639 /* Response internal events */
640 if (fwk_id_get_module_idx(event->source_id) ==
641 fwk_id_get_module_idx(fwk_module_id_scmi_perf)) {
642 return process_internal_event(event);
643 }
644
645 return FWK_E_PARAM;
646 }
647
648 /* SCMI Performance Management Protocol Definition */
649 const struct fwk_module module_scmi_perf = {
650 .api_count = (unsigned int)MOD_SCMI_PERF_API_COUNT,
651 .event_count = (unsigned int)SCMI_PERF_EVENT_IDX_COUNT,
652 .type = FWK_MODULE_TYPE_PROTOCOL,
653 .init = scmi_perf_init,
654 .bind = scmi_perf_bind,
655 .start = scmi_perf_start,
656 .process_bind_request = scmi_perf_process_bind_request,
657 .process_event = scmi_perf_process_event,
658 };
659