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