1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * Metrics Analyzer
9 */
10
11 #include "mod_metrics_analyzer.h"
12
13 #include <interface_power_management.h>
14
15 #include <fwk_id.h>
16 #include <fwk_mm.h>
17 #include <fwk_module.h>
18 #include <fwk_module_idx.h>
19
20 struct mod_metric_ctx {
21 const struct mod_metrics_analyzer_interactor *limit_provider_config;
22 struct interface_power_management_api *limit_provider_api;
23 uint32_t limit;
24 };
25
26 struct mod_domain_ctx {
27 struct mod_metric_ctx *metrics;
28 size_t metrics_count;
29 uint32_t aggregate_limit;
30 const struct mod_metrics_analyzer_domain_config *config;
31 struct interface_power_management_api *limit_consumer_api;
32 };
33
34 struct mod_metrics_analyzer_ctx {
35 /* list of system domains context */
36 struct mod_domain_ctx *domain;
37 /* Number of domains */
38 size_t domain_count;
39 /* Metrics analyzer configuration (on the module scope) */
40 const struct mod_metrics_analyzer_config *config;
41 };
42
43 static struct mod_metrics_analyzer_ctx metrics_analyzer_ctx;
44
collect_domain_limits(struct mod_domain_ctx * domain_ctx)45 static int collect_domain_limits(struct mod_domain_ctx *domain_ctx)
46 {
47 int status = FWK_SUCCESS;
48 if (domain_ctx == NULL) {
49 return FWK_E_PARAM;
50 }
51
52 domain_ctx->aggregate_limit = UINT32_MAX;
53 for (size_t i = 0; i < domain_ctx->metrics_count; ++i) {
54 uint32_t power_limit;
55 struct mod_metric_ctx *metric_ctx = &domain_ctx->metrics[i];
56 status = metric_ctx->limit_provider_api->get_limit(
57 metric_ctx->limit_provider_config->domain_id, &power_limit);
58 if (status == FWK_SUCCESS) {
59 metric_ctx->limit = power_limit;
60 domain_ctx->aggregate_limit =
61 FWK_MIN(domain_ctx->aggregate_limit, metric_ctx->limit);
62 } else {
63 metric_ctx->limit = UINT32_MAX;
64 }
65 }
66
67 return FWK_SUCCESS;
68 }
69
report_domain_aggregate_limit(struct mod_domain_ctx * domain_ctx)70 static int report_domain_aggregate_limit(struct mod_domain_ctx *domain_ctx)
71 {
72 if (domain_ctx == NULL) {
73 return FWK_E_PARAM;
74 }
75 if (domain_ctx->limit_consumer_api == NULL ||
76 domain_ctx->limit_consumer_api->set_limit == NULL) {
77 return FWK_E_PANIC;
78 }
79
80 return domain_ctx->limit_consumer_api->set_limit(
81 domain_ctx->config->limit_consumer.domain_id,
82 domain_ctx->aggregate_limit);
83 }
84
collect_domains_limits(struct mod_domain_ctx * domains_ctx,size_t domain_count)85 static int collect_domains_limits(
86 struct mod_domain_ctx *domains_ctx,
87 size_t domain_count)
88 {
89 if (domain_count && domains_ctx == NULL) {
90 return FWK_E_PARAM;
91 }
92
93 for (size_t i = 0; i < domain_count; ++i) {
94 (void)collect_domain_limits(&domains_ctx[i]);
95 }
96
97 return FWK_SUCCESS;
98 }
99
report_domains_aggregate_limit(struct mod_domain_ctx * domains_ctx,size_t domain_count)100 static int report_domains_aggregate_limit(
101 struct mod_domain_ctx *domains_ctx,
102 size_t domain_count)
103 {
104 if (domain_count && domains_ctx == NULL) {
105 return FWK_E_PARAM;
106 }
107
108 for (size_t i = 0; i < domain_count; ++i) {
109 (void)report_domain_aggregate_limit(&domains_ctx[i]);
110 }
111
112 return FWK_SUCCESS;
113 }
114
analyze(void)115 static int analyze(void)
116 {
117 int status = FWK_E_INIT;
118
119 status = collect_domains_limits(
120 metrics_analyzer_ctx.domain, metrics_analyzer_ctx.domain_count);
121 if (status != FWK_SUCCESS) {
122 return status;
123 }
124
125 status = report_domains_aggregate_limit(
126 metrics_analyzer_ctx.domain, metrics_analyzer_ctx.domain_count);
127
128 return status;
129 }
130
131 static struct mod_metrics_analyzer_analyze_api analyze_api = {
132 .analyze = analyze,
133 };
134
135 /*
136 * Framework handlers
137 */
138
metrics_analyzer_init(fwk_id_t module_id,unsigned int element_count,const void * config_data)139 static int metrics_analyzer_init(
140 fwk_id_t module_id,
141 unsigned int element_count,
142 const void *config_data)
143 {
144 if (element_count == 0U ||
145 !fwk_id_is_equal(
146 module_id, FWK_ID_MODULE(FWK_MODULE_IDX_METRICS_ANALYZER))) {
147 return FWK_E_PARAM;
148 }
149
150 metrics_analyzer_ctx.domain_count = element_count;
151 metrics_analyzer_ctx.domain =
152 fwk_mm_calloc(element_count, sizeof(metrics_analyzer_ctx.domain[0]));
153 return FWK_SUCCESS;
154 }
155
metrics_analyzer_element_init(fwk_id_t element_id,unsigned int sub_element_count,const void * data)156 static int metrics_analyzer_element_init(
157 fwk_id_t element_id,
158 unsigned int sub_element_count,
159 const void *data)
160 {
161 if ((sub_element_count == 0U) || (sub_element_count >= (1U << 8)) ||
162 (data == NULL)) {
163 return FWK_E_PARAM;
164 }
165
166 size_t domain_idx = fwk_id_get_element_idx(element_id);
167 metrics_analyzer_ctx.domain[domain_idx].config = data;
168 metrics_analyzer_ctx.domain[domain_idx].aggregate_limit = UINT32_MAX;
169 metrics_analyzer_ctx.domain[domain_idx].metrics_count = sub_element_count;
170 metrics_analyzer_ctx.domain[domain_idx].metrics = fwk_mm_calloc(
171 sub_element_count,
172 sizeof(metrics_analyzer_ctx.domain[domain_idx].metrics[0]));
173
174 for (size_t i = 0; i < sub_element_count; ++i) {
175 metrics_analyzer_ctx.domain[domain_idx].metrics[i].limit = UINT32_MAX;
176 metrics_analyzer_ctx.domain[domain_idx]
177 .metrics[i]
178 .limit_provider_config =
179 &metrics_analyzer_ctx.domain[domain_idx].config->limit_providers[i];
180 }
181
182 return FWK_SUCCESS;
183 }
184
metrics_analyzer_bind_element(fwk_id_t id)185 static int metrics_analyzer_bind_element(fwk_id_t id)
186 {
187 int status = FWK_E_INIT;
188 size_t domain_idx = fwk_id_get_element_idx(id);
189 struct mod_domain_ctx *domain_ctx =
190 &metrics_analyzer_ctx.domain[domain_idx];
191 for (size_t i = 0; i < domain_ctx->metrics_count; ++i) {
192 struct mod_metric_ctx *metrics_ctx = &domain_ctx->metrics[i];
193 status = fwk_module_bind(
194 metrics_ctx->limit_provider_config->domain_id,
195 metrics_ctx->limit_provider_config->api_id,
196 &metrics_ctx->limit_provider_api);
197 if (status != FWK_SUCCESS) {
198 return status;
199 }
200 }
201
202 status = fwk_module_bind(
203 domain_ctx->config->limit_consumer.domain_id,
204 domain_ctx->config->limit_consumer.api_id,
205 &domain_ctx->limit_consumer_api);
206
207 return status;
208 }
209
metrics_analyzer_bind(fwk_id_t id,unsigned int round)210 static int metrics_analyzer_bind(fwk_id_t id, unsigned int round)
211 {
212 /* Bind our elements in round 0 */
213 if (round == 0 && fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
214 return metrics_analyzer_bind_element(id);
215 }
216
217 return FWK_SUCCESS;
218 }
219
metrics_analyzer_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)220 static int metrics_analyzer_process_bind_request(
221 fwk_id_t source_id,
222 fwk_id_t target_id,
223 fwk_id_t api_id,
224 const void **api)
225 {
226 /* Only allow binding to the module */
227 if (api == NULL ||
228 !fwk_id_is_equal(
229 target_id, FWK_ID_MODULE(FWK_MODULE_IDX_METRICS_ANALYZER))) {
230 return FWK_E_PARAM;
231 }
232
233 if (!fwk_id_is_equal(
234 api_id,
235 FWK_ID_API(
236 FWK_MODULE_IDX_METRICS_ANALYZER,
237 MOD_METRICS_ANALYZER_API_IDX_ANALYZE))) {
238 return FWK_E_PARAM;
239 }
240
241 *api = &analyze_api;
242 return FWK_SUCCESS;
243 }
244
metrics_analyzer_start(fwk_id_t id)245 static int metrics_analyzer_start(fwk_id_t id)
246 {
247 return FWK_SUCCESS;
248 }
249
250 /* metrics_analyzer module definition */
251 const struct fwk_module module_metrics_analyzer = {
252 .type = FWK_MODULE_TYPE_SERVICE,
253 .api_count = MOD_METRICS_ANALYZER_API_IDX_COUNT,
254 .init = metrics_analyzer_init,
255 .element_init = metrics_analyzer_element_init,
256 .bind = metrics_analyzer_bind,
257 .start = metrics_analyzer_start,
258 .process_bind_request = metrics_analyzer_process_bind_request,
259 };
260