xref: /optee_os/core/drivers/scmi-msg/perf_domain.c (revision f1cec17a35226380e01b5d50ce42736f584827dd)
1*f1cec17aSPascal Paillet // SPDX-License-Identifier: BSD-3-Clause
2*f1cec17aSPascal Paillet /*
3*f1cec17aSPascal Paillet  * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved.
4*f1cec17aSPascal Paillet  * Copyright (c) 2021, Linaro Limited
5*f1cec17aSPascal Paillet  * Copyright (c) 2024, STMicroelectronics
6*f1cec17aSPascal Paillet  */
7*f1cec17aSPascal Paillet #include <assert.h>
8*f1cec17aSPascal Paillet #include <confine_array_index.h>
9*f1cec17aSPascal Paillet #include <drivers/scmi-msg.h>
10*f1cec17aSPascal Paillet #include <drivers/scmi.h>
11*f1cec17aSPascal Paillet #include <mm/core_memprot.h>
12*f1cec17aSPascal Paillet #include <string.h>
13*f1cec17aSPascal Paillet #include <util.h>
14*f1cec17aSPascal Paillet 
15*f1cec17aSPascal Paillet #include "common.h"
16*f1cec17aSPascal Paillet #include "perf_domain.h"
17*f1cec17aSPascal Paillet 
18*f1cec17aSPascal Paillet #define VERBOSE_MSG(...)		FMSG(__VA_ARGS__)
19*f1cec17aSPascal Paillet 
20*f1cec17aSPascal Paillet static bool message_id_is_supported(unsigned int message_id);
21*f1cec17aSPascal Paillet 
22*f1cec17aSPascal Paillet /* Weak handlers platform shall override on purpose */
plat_scmi_perf_count(unsigned int channel_id __unused)23*f1cec17aSPascal Paillet size_t __weak plat_scmi_perf_count(unsigned int channel_id __unused)
24*f1cec17aSPascal Paillet {
25*f1cec17aSPascal Paillet 	return 0;
26*f1cec17aSPascal Paillet }
27*f1cec17aSPascal Paillet 
plat_scmi_perf_statistics_buf(unsigned int channel_id __unused,size_t * stats_len)28*f1cec17aSPascal Paillet void __weak *plat_scmi_perf_statistics_buf(unsigned int channel_id __unused,
29*f1cec17aSPascal Paillet 					   size_t *stats_len)
30*f1cec17aSPascal Paillet {
31*f1cec17aSPascal Paillet 	*stats_len = 0;
32*f1cec17aSPascal Paillet 
33*f1cec17aSPascal Paillet 	return NULL;
34*f1cec17aSPascal Paillet }
35*f1cec17aSPascal Paillet 
plat_scmi_perf_domain_name(unsigned int channel_id __unused,unsigned int domain_id __unused)36*f1cec17aSPascal Paillet const char __weak *plat_scmi_perf_domain_name(unsigned int channel_id __unused,
37*f1cec17aSPascal Paillet 					      unsigned int domain_id __unused)
38*f1cec17aSPascal Paillet {
39*f1cec17aSPascal Paillet 	return NULL;
40*f1cec17aSPascal Paillet }
41*f1cec17aSPascal Paillet 
plat_scmi_perf_sustained_freq(unsigned int channel_id __unused,unsigned int domain_id __unused,unsigned int * freq __unused)42*f1cec17aSPascal Paillet int32_t __weak plat_scmi_perf_sustained_freq(unsigned int channel_id __unused,
43*f1cec17aSPascal Paillet 					     unsigned int domain_id __unused,
44*f1cec17aSPascal Paillet 					     unsigned int *freq __unused)
45*f1cec17aSPascal Paillet {
46*f1cec17aSPascal Paillet 	return SCMI_NOT_SUPPORTED;
47*f1cec17aSPascal Paillet }
48*f1cec17aSPascal Paillet 
plat_scmi_perf_levels_array(unsigned int channel_id __unused,unsigned int domain_id __unused,size_t start_index __unused,unsigned int * elt __unused,size_t * nb_elts __unused)49*f1cec17aSPascal Paillet int32_t __weak plat_scmi_perf_levels_array(unsigned int channel_id __unused,
50*f1cec17aSPascal Paillet 					   unsigned int domain_id __unused,
51*f1cec17aSPascal Paillet 					   size_t start_index __unused,
52*f1cec17aSPascal Paillet 					   unsigned int *elt __unused,
53*f1cec17aSPascal Paillet 					   size_t *nb_elts __unused)
54*f1cec17aSPascal Paillet {
55*f1cec17aSPascal Paillet 	return SCMI_NOT_SUPPORTED;
56*f1cec17aSPascal Paillet }
57*f1cec17aSPascal Paillet 
plat_scmi_perf_level_latency(unsigned int channel_id __unused,unsigned int domain_id __unused,unsigned int level __unused,unsigned int * latency)58*f1cec17aSPascal Paillet int32_t __weak plat_scmi_perf_level_latency(unsigned int channel_id __unused,
59*f1cec17aSPascal Paillet 					    unsigned int domain_id __unused,
60*f1cec17aSPascal Paillet 					    unsigned int level __unused,
61*f1cec17aSPascal Paillet 					    unsigned int *latency)
62*f1cec17aSPascal Paillet {
63*f1cec17aSPascal Paillet 	/* Use 1 microsecond because the Linux kernel treats 0 as eternal */
64*f1cec17aSPascal Paillet 	*latency = 1;
65*f1cec17aSPascal Paillet 
66*f1cec17aSPascal Paillet 	return SCMI_SUCCESS;
67*f1cec17aSPascal Paillet }
68*f1cec17aSPascal Paillet 
plat_scmi_perf_level_power_cost(unsigned int channel_id __unused,unsigned int domain_id __unused,unsigned int level __unused,unsigned int * cost __unused)69*f1cec17aSPascal Paillet int32_t __weak plat_scmi_perf_level_power_cost(unsigned int channel_id __unused,
70*f1cec17aSPascal Paillet 					       unsigned int domain_id __unused,
71*f1cec17aSPascal Paillet 					       unsigned int level __unused,
72*f1cec17aSPascal Paillet 					       unsigned int *cost __unused)
73*f1cec17aSPascal Paillet {
74*f1cec17aSPascal Paillet 	*cost = 0;
75*f1cec17aSPascal Paillet 
76*f1cec17aSPascal Paillet 	return SCMI_SUCCESS;
77*f1cec17aSPascal Paillet }
78*f1cec17aSPascal Paillet 
plat_scmi_perf_level_get(unsigned int channel_id __unused,unsigned int domain_id __unused,unsigned int * level __unused)79*f1cec17aSPascal Paillet int32_t __weak plat_scmi_perf_level_get(unsigned int channel_id __unused,
80*f1cec17aSPascal Paillet 					unsigned int domain_id __unused,
81*f1cec17aSPascal Paillet 					unsigned int *level __unused)
82*f1cec17aSPascal Paillet {
83*f1cec17aSPascal Paillet 	return SCMI_NOT_SUPPORTED;
84*f1cec17aSPascal Paillet }
85*f1cec17aSPascal Paillet 
plat_scmi_perf_level_set(unsigned int channel_id __unused,unsigned int domain_id __unused,unsigned int level __unused)86*f1cec17aSPascal Paillet int32_t __weak plat_scmi_perf_level_set(unsigned int channel_id __unused,
87*f1cec17aSPascal Paillet 					unsigned int domain_id __unused,
88*f1cec17aSPascal Paillet 					unsigned int level __unused)
89*f1cec17aSPascal Paillet {
90*f1cec17aSPascal Paillet 	return SCMI_NOT_SUPPORTED;
91*f1cec17aSPascal Paillet }
92*f1cec17aSPascal Paillet 
protocol_version(struct scmi_msg * msg)93*f1cec17aSPascal Paillet static void protocol_version(struct scmi_msg *msg)
94*f1cec17aSPascal Paillet {
95*f1cec17aSPascal Paillet 	struct scmi_protocol_version_p2a return_values = {
96*f1cec17aSPascal Paillet 		.status = SCMI_SUCCESS,
97*f1cec17aSPascal Paillet 		.version = SCMI_PROTOCOL_VERSION_PERF_DOMAIN,
98*f1cec17aSPascal Paillet 	};
99*f1cec17aSPascal Paillet 
100*f1cec17aSPascal Paillet 	VERBOSE_MSG("SCMI perf %#"PRIx32, SCMI_PROTOCOL_VERSION_PERF_DOMAIN);
101*f1cec17aSPascal Paillet 
102*f1cec17aSPascal Paillet 	if (msg->in_size) {
103*f1cec17aSPascal Paillet 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
104*f1cec17aSPascal Paillet 		return;
105*f1cec17aSPascal Paillet 	}
106*f1cec17aSPascal Paillet 
107*f1cec17aSPascal Paillet 	scmi_write_response(msg, &return_values, sizeof(return_values));
108*f1cec17aSPascal Paillet }
109*f1cec17aSPascal Paillet 
protocol_attributes(struct scmi_msg * msg)110*f1cec17aSPascal Paillet static void protocol_attributes(struct scmi_msg *msg)
111*f1cec17aSPascal Paillet {
112*f1cec17aSPascal Paillet 	unsigned int channel_id = msg->channel_id;
113*f1cec17aSPascal Paillet 	size_t count = plat_scmi_perf_count(channel_id);
114*f1cec17aSPascal Paillet 	uint32_t power_in_mw = 0;
115*f1cec17aSPascal Paillet 	struct scmi_perf_protocol_attributes_p2a return_values = {
116*f1cec17aSPascal Paillet 		.status = SCMI_SUCCESS,
117*f1cec17aSPascal Paillet 		.attributes = SCMI_PERF_PROTOCOL_ATTRIBUTES(power_in_mw, count),
118*f1cec17aSPascal Paillet 	};
119*f1cec17aSPascal Paillet 	void *stats_buf = NULL;
120*f1cec17aSPascal Paillet 	size_t stats_len = 0;
121*f1cec17aSPascal Paillet 
122*f1cec17aSPascal Paillet 	VERBOSE_MSG("channel %u: %zu performance domains", channel_id, count);
123*f1cec17aSPascal Paillet 
124*f1cec17aSPascal Paillet 	if (msg->in_size) {
125*f1cec17aSPascal Paillet 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
126*f1cec17aSPascal Paillet 		return;
127*f1cec17aSPascal Paillet 	}
128*f1cec17aSPascal Paillet 
129*f1cec17aSPascal Paillet 	stats_buf = plat_scmi_perf_statistics_buf(channel_id, &stats_len);
130*f1cec17aSPascal Paillet 	if (stats_len) {
131*f1cec17aSPascal Paillet 		paddr_t stats_pa = virt_to_phys(stats_buf);
132*f1cec17aSPascal Paillet 
133*f1cec17aSPascal Paillet 		if (stats_pa && tee_vbuf_is_non_sec(stats_buf, stats_len)) {
134*f1cec17aSPascal Paillet 			return_values.statistics_len = stats_len;
135*f1cec17aSPascal Paillet 			reg_pair_from_64((uint64_t)stats_pa,
136*f1cec17aSPascal Paillet 					 &return_values.statistics_address_high,
137*f1cec17aSPascal Paillet 					 &return_values.statistics_address_low);
138*f1cec17aSPascal Paillet 		} else {
139*f1cec17aSPascal Paillet 			IMSG("Disable SCMI perf statistics: invalid buffer");
140*f1cec17aSPascal Paillet 			DMSG("Stats buffer va %p, pa %#"PRIxPA", size %zu",
141*f1cec17aSPascal Paillet 			     stats_buf, stats_pa, stats_len);
142*f1cec17aSPascal Paillet 		}
143*f1cec17aSPascal Paillet 	}
144*f1cec17aSPascal Paillet 
145*f1cec17aSPascal Paillet 	scmi_write_response(msg, &return_values, sizeof(return_values));
146*f1cec17aSPascal Paillet }
147*f1cec17aSPascal Paillet 
protocol_message_attributes(struct scmi_msg * msg)148*f1cec17aSPascal Paillet static void protocol_message_attributes(struct scmi_msg *msg)
149*f1cec17aSPascal Paillet {
150*f1cec17aSPascal Paillet 	struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in;
151*f1cec17aSPascal Paillet 	struct scmi_protocol_message_attributes_p2a return_values = {
152*f1cec17aSPascal Paillet 		.status = SCMI_SUCCESS,
153*f1cec17aSPascal Paillet 		/* For this protocol, attributes shall be zero */
154*f1cec17aSPascal Paillet 		.attributes = 0,
155*f1cec17aSPascal Paillet 	};
156*f1cec17aSPascal Paillet 
157*f1cec17aSPascal Paillet 	if (msg->in_size != sizeof(*in_args)) {
158*f1cec17aSPascal Paillet 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
159*f1cec17aSPascal Paillet 		return;
160*f1cec17aSPascal Paillet 	}
161*f1cec17aSPascal Paillet 
162*f1cec17aSPascal Paillet 	if (!message_id_is_supported(in_args->message_id)) {
163*f1cec17aSPascal Paillet 		scmi_status_response(msg, SCMI_NOT_FOUND);
164*f1cec17aSPascal Paillet 		return;
165*f1cec17aSPascal Paillet 	}
166*f1cec17aSPascal Paillet 
167*f1cec17aSPascal Paillet 	scmi_write_response(msg, &return_values, sizeof(return_values));
168*f1cec17aSPascal Paillet }
169*f1cec17aSPascal Paillet 
sanitize_message(struct scmi_msg * msg,unsigned int * domain_id,size_t exp_in_size)170*f1cec17aSPascal Paillet static int32_t sanitize_message(struct scmi_msg *msg, unsigned int *domain_id,
171*f1cec17aSPascal Paillet 				size_t exp_in_size)
172*f1cec17aSPascal Paillet {
173*f1cec17aSPascal Paillet 	size_t domain_count = plat_scmi_perf_count(msg->channel_id);
174*f1cec17aSPascal Paillet 
175*f1cec17aSPascal Paillet 	*domain_id = confine_array_index(*domain_id, domain_count);
176*f1cec17aSPascal Paillet 
177*f1cec17aSPascal Paillet 	if (msg->in_size != exp_in_size)
178*f1cec17aSPascal Paillet 		return SCMI_PROTOCOL_ERROR;
179*f1cec17aSPascal Paillet 
180*f1cec17aSPascal Paillet 	if (*domain_id >= domain_count)
181*f1cec17aSPascal Paillet 		return SCMI_INVALID_PARAMETERS;
182*f1cec17aSPascal Paillet 
183*f1cec17aSPascal Paillet 	return SCMI_SUCCESS;
184*f1cec17aSPascal Paillet }
185*f1cec17aSPascal Paillet 
scmi_perf_domain_attributes(struct scmi_msg * msg)186*f1cec17aSPascal Paillet static void scmi_perf_domain_attributes(struct scmi_msg *msg)
187*f1cec17aSPascal Paillet {
188*f1cec17aSPascal Paillet 	const struct scmi_perf_attributes_a2p *in_args = (void *)msg->in;
189*f1cec17aSPascal Paillet 	/* It is safe to read in_args->domain_id before sanitize_message() */
190*f1cec17aSPascal Paillet 	unsigned int domain_id = in_args->domain_id;
191*f1cec17aSPascal Paillet 	int32_t res = SCMI_GENERIC_ERROR;
192*f1cec17aSPascal Paillet 	struct scmi_perf_attributes_p2a return_values = {
193*f1cec17aSPascal Paillet 		.attributes = SCMI_PERF_DOMAIN_ATTRIBUTES_CAN_SET_LEVEL,
194*f1cec17aSPascal Paillet 		.status = SCMI_SUCCESS,
195*f1cec17aSPascal Paillet 	};
196*f1cec17aSPascal Paillet 	const char *name = NULL;
197*f1cec17aSPascal Paillet 
198*f1cec17aSPascal Paillet 	FMSG("channel %u: domain %u", msg->channel_id, domain_id);
199*f1cec17aSPascal Paillet 
200*f1cec17aSPascal Paillet 	res = sanitize_message(msg, &domain_id, sizeof(*in_args));
201*f1cec17aSPascal Paillet 	if (res) {
202*f1cec17aSPascal Paillet 		scmi_status_response(msg, res);
203*f1cec17aSPascal Paillet 		return;
204*f1cec17aSPascal Paillet 	}
205*f1cec17aSPascal Paillet 
206*f1cec17aSPascal Paillet 	name = plat_scmi_perf_domain_name(msg->channel_id, domain_id);
207*f1cec17aSPascal Paillet 	if (!name) {
208*f1cec17aSPascal Paillet 		scmi_status_response(msg, SCMI_NOT_FOUND);
209*f1cec17aSPascal Paillet 		return;
210*f1cec17aSPascal Paillet 	}
211*f1cec17aSPascal Paillet 
212*f1cec17aSPascal Paillet 	res = plat_scmi_perf_sustained_freq(msg->channel_id, domain_id,
213*f1cec17aSPascal Paillet 					    &return_values.sustained_freq);
214*f1cec17aSPascal Paillet 	if (res) {
215*f1cec17aSPascal Paillet 		scmi_status_response(msg, res);
216*f1cec17aSPascal Paillet 		return;
217*f1cec17aSPascal Paillet 	}
218*f1cec17aSPascal Paillet 
219*f1cec17aSPascal Paillet 	COPY_NAME_IDENTIFIER(return_values.name, name);
220*f1cec17aSPascal Paillet 
221*f1cec17aSPascal Paillet 	/*
222*f1cec17aSPascal Paillet 	 * .rate_limit and .sustained_perf_level are
223*f1cec17aSPascal Paillet 	 * implicitly set to 0.
224*f1cec17aSPascal Paillet 	 */
225*f1cec17aSPascal Paillet 
226*f1cec17aSPascal Paillet 	VERBOSE_MSG("channel %u: domain %u: name \"%s\"", msg->channel_id,
227*f1cec17aSPascal Paillet 		    domain_id, name);
228*f1cec17aSPascal Paillet 
229*f1cec17aSPascal Paillet 	scmi_write_response(msg, &return_values, sizeof(return_values));
230*f1cec17aSPascal Paillet }
231*f1cec17aSPascal Paillet 
scmi_perf_level_get(struct scmi_msg * msg)232*f1cec17aSPascal Paillet static void scmi_perf_level_get(struct scmi_msg *msg)
233*f1cec17aSPascal Paillet {
234*f1cec17aSPascal Paillet 	const struct scmi_perf_level_get_a2p *in_args = (void *)msg->in;
235*f1cec17aSPascal Paillet 	/* It is safe to read in_args->domain_id before sanitize_message() */
236*f1cec17aSPascal Paillet 	unsigned int domain_id = in_args->domain_id;
237*f1cec17aSPascal Paillet 	int32_t res = SCMI_GENERIC_ERROR;
238*f1cec17aSPascal Paillet 	unsigned int level = 0;
239*f1cec17aSPascal Paillet 	struct scmi_perf_level_get_p2a return_values = {
240*f1cec17aSPascal Paillet 		.status = SCMI_SUCCESS,
241*f1cec17aSPascal Paillet 	};
242*f1cec17aSPascal Paillet 
243*f1cec17aSPascal Paillet 	VERBOSE_MSG("channel %u, domain %u", msg->channel_id, domain_id);
244*f1cec17aSPascal Paillet 
245*f1cec17aSPascal Paillet 	res = sanitize_message(msg, &domain_id, sizeof(*in_args));
246*f1cec17aSPascal Paillet 	if (res) {
247*f1cec17aSPascal Paillet 		scmi_status_response(msg, res);
248*f1cec17aSPascal Paillet 		return;
249*f1cec17aSPascal Paillet 	}
250*f1cec17aSPascal Paillet 
251*f1cec17aSPascal Paillet 	res = plat_scmi_perf_level_get(msg->channel_id, domain_id, &level);
252*f1cec17aSPascal Paillet 	if (res) {
253*f1cec17aSPascal Paillet 		scmi_status_response(msg, res);
254*f1cec17aSPascal Paillet 		return;
255*f1cec17aSPascal Paillet 	}
256*f1cec17aSPascal Paillet 
257*f1cec17aSPascal Paillet 	assert(level <= UINT32_MAX);
258*f1cec17aSPascal Paillet 	return_values.performance_level = level;
259*f1cec17aSPascal Paillet 
260*f1cec17aSPascal Paillet 	VERBOSE_MSG("channel %u, domain %u: level %u", msg->channel_id,
261*f1cec17aSPascal Paillet 		    domain_id, level);
262*f1cec17aSPascal Paillet 
263*f1cec17aSPascal Paillet 	scmi_write_response(msg, &return_values, sizeof(return_values));
264*f1cec17aSPascal Paillet }
265*f1cec17aSPascal Paillet 
scmi_perf_level_set(struct scmi_msg * msg)266*f1cec17aSPascal Paillet static void scmi_perf_level_set(struct scmi_msg *msg)
267*f1cec17aSPascal Paillet {
268*f1cec17aSPascal Paillet 	const struct scmi_perf_level_set_a2p *in_args = (void *)msg->in;
269*f1cec17aSPascal Paillet 	unsigned int channel_id = msg->channel_id;
270*f1cec17aSPascal Paillet 	/* It is safe to read in in_args before sanitize_message() */
271*f1cec17aSPascal Paillet 	unsigned int domain_id = in_args->domain_id;
272*f1cec17aSPascal Paillet 	unsigned int level = in_args->performance_level;
273*f1cec17aSPascal Paillet 	int32_t res = SCMI_GENERIC_ERROR;
274*f1cec17aSPascal Paillet 
275*f1cec17aSPascal Paillet 	VERBOSE_MSG("channel %u, domain %u: set level %u", channel_id,
276*f1cec17aSPascal Paillet 		    domain_id, level);
277*f1cec17aSPascal Paillet 
278*f1cec17aSPascal Paillet 	res = sanitize_message(msg, &domain_id, sizeof(*in_args));
279*f1cec17aSPascal Paillet 	if (res == SCMI_SUCCESS)
280*f1cec17aSPascal Paillet 		res = plat_scmi_perf_level_set(channel_id, domain_id, level);
281*f1cec17aSPascal Paillet 
282*f1cec17aSPascal Paillet 	scmi_status_response(msg, res);
283*f1cec17aSPascal Paillet }
284*f1cec17aSPascal Paillet 
285*f1cec17aSPascal Paillet /* List levels array by small chunks fitting in SCMI message max payload size */
286*f1cec17aSPascal Paillet #define LEVELS_ARRAY_SIZE \
287*f1cec17aSPascal Paillet 	((SCMI_SEC_PAYLOAD_SIZE - \
288*f1cec17aSPascal Paillet 	  sizeof(struct scmi_perf_describe_levels_a2p)) / \
289*f1cec17aSPascal Paillet 	 sizeof(struct scmi_perf_level))
290*f1cec17aSPascal Paillet 
scmi_perf_describe_levels(struct scmi_msg * msg)291*f1cec17aSPascal Paillet static void scmi_perf_describe_levels(struct scmi_msg *msg)
292*f1cec17aSPascal Paillet {
293*f1cec17aSPascal Paillet 	const struct scmi_perf_describe_levels_a2p *in_args = (void *)msg->in;
294*f1cec17aSPascal Paillet 	size_t nb_levels = 0;
295*f1cec17aSPascal Paillet 	/* It is safe to read in_args->domain_id before sanitize_message() */
296*f1cec17aSPascal Paillet 	unsigned int domain_id = in_args->domain_id;
297*f1cec17aSPascal Paillet 	int32_t res = SCMI_GENERIC_ERROR;
298*f1cec17aSPascal Paillet 	/* Use the stack to get the returned a portion of the level array */
299*f1cec17aSPascal Paillet 	unsigned int plat_levels[LEVELS_ARRAY_SIZE] = { 0 };
300*f1cec17aSPascal Paillet 	size_t ret_nb = 0;
301*f1cec17aSPascal Paillet 	size_t rem_nb = 0;
302*f1cec17aSPascal Paillet 
303*f1cec17aSPascal Paillet 	VERBOSE_MSG("channel %u, domain %u", msg->channel_id, domain_id);
304*f1cec17aSPascal Paillet 
305*f1cec17aSPascal Paillet 	res = sanitize_message(msg, &domain_id, sizeof(*in_args));
306*f1cec17aSPascal Paillet 	if (res)
307*f1cec17aSPascal Paillet 		goto err;
308*f1cec17aSPascal Paillet 
309*f1cec17aSPascal Paillet 	res = plat_scmi_perf_levels_array(msg->channel_id, domain_id, 0,
310*f1cec17aSPascal Paillet 					  NULL, &nb_levels);
311*f1cec17aSPascal Paillet 	if (res)
312*f1cec17aSPascal Paillet 		goto err;
313*f1cec17aSPascal Paillet 
314*f1cec17aSPascal Paillet 	if (in_args->level_index >= nb_levels) {
315*f1cec17aSPascal Paillet 		res = SCMI_INVALID_PARAMETERS;
316*f1cec17aSPascal Paillet 		goto err;
317*f1cec17aSPascal Paillet 	}
318*f1cec17aSPascal Paillet 
319*f1cec17aSPascal Paillet 	ret_nb = MIN(ARRAY_SIZE(plat_levels), nb_levels - in_args->level_index);
320*f1cec17aSPascal Paillet 	rem_nb = nb_levels - in_args->level_index - ret_nb;
321*f1cec17aSPascal Paillet 
322*f1cec17aSPascal Paillet 	res =  plat_scmi_perf_levels_array(msg->channel_id, domain_id,
323*f1cec17aSPascal Paillet 					   in_args->level_index, plat_levels,
324*f1cec17aSPascal Paillet 					   &ret_nb);
325*f1cec17aSPascal Paillet 
326*f1cec17aSPascal Paillet 	if (res == SCMI_SUCCESS) {
327*f1cec17aSPascal Paillet 		struct scmi_perf_describe_levels_p2a p2a = {
328*f1cec17aSPascal Paillet 			.status = SCMI_SUCCESS,
329*f1cec17aSPascal Paillet 			.num_levels = SCMI_PERF_NUM_LEVELS(ret_nb, rem_nb),
330*f1cec17aSPascal Paillet 		};
331*f1cec17aSPascal Paillet 		struct scmi_perf_level *levels = NULL;
332*f1cec17aSPascal Paillet 		size_t n = 0;
333*f1cec17aSPascal Paillet 
334*f1cec17aSPascal Paillet 		memcpy(msg->out, &p2a, sizeof(p2a));
335*f1cec17aSPascal Paillet 
336*f1cec17aSPascal Paillet 		/* By construction these values are 32bit aligned */
337*f1cec17aSPascal Paillet 		levels = (void *)(uintptr_t)(msg->out + sizeof(p2a));
338*f1cec17aSPascal Paillet 
339*f1cec17aSPascal Paillet 		for (n = 0; n < ret_nb; n++) {
340*f1cec17aSPascal Paillet 			unsigned int latency = 0;
341*f1cec17aSPascal Paillet 			unsigned int power_cost = 0;
342*f1cec17aSPascal Paillet 
343*f1cec17aSPascal Paillet 			res = plat_scmi_perf_level_latency(msg->channel_id,
344*f1cec17aSPascal Paillet 							   domain_id,
345*f1cec17aSPascal Paillet 							   plat_levels[n],
346*f1cec17aSPascal Paillet 							   &latency);
347*f1cec17aSPascal Paillet 			if (res != SCMI_SUCCESS)
348*f1cec17aSPascal Paillet 				goto err;
349*f1cec17aSPascal Paillet 
350*f1cec17aSPascal Paillet 			assert(latency <= UINT16_MAX);
351*f1cec17aSPascal Paillet 			latency &= SCMI_PERF_LEVEL_ATTRIBUTES_LATENCY_US_MASK;
352*f1cec17aSPascal Paillet 
353*f1cec17aSPascal Paillet 			res = plat_scmi_perf_level_power_cost(msg->channel_id,
354*f1cec17aSPascal Paillet 							      domain_id,
355*f1cec17aSPascal Paillet 							      plat_levels[n],
356*f1cec17aSPascal Paillet 							      &power_cost);
357*f1cec17aSPascal Paillet 			if (res != SCMI_SUCCESS)
358*f1cec17aSPascal Paillet 				goto err;
359*f1cec17aSPascal Paillet 
360*f1cec17aSPascal Paillet 			levels[n] = (struct scmi_perf_level){
361*f1cec17aSPascal Paillet 				.performance_level = plat_levels[n],
362*f1cec17aSPascal Paillet 				.power_cost = power_cost,
363*f1cec17aSPascal Paillet 				.attributes = latency,
364*f1cec17aSPascal Paillet 			};
365*f1cec17aSPascal Paillet 		}
366*f1cec17aSPascal Paillet 
367*f1cec17aSPascal Paillet 		msg->out_size_out =
368*f1cec17aSPascal Paillet 			sizeof(p2a) + ret_nb * sizeof(struct scmi_perf_level);
369*f1cec17aSPascal Paillet 
370*f1cec17aSPascal Paillet 		return;
371*f1cec17aSPascal Paillet 	}
372*f1cec17aSPascal Paillet 
373*f1cec17aSPascal Paillet err:
374*f1cec17aSPascal Paillet 	assert(res);
375*f1cec17aSPascal Paillet 	scmi_status_response(msg, res);
376*f1cec17aSPascal Paillet }
377*f1cec17aSPascal Paillet 
378*f1cec17aSPascal Paillet static const scmi_msg_handler_t scmi_perf_handler_table[] = {
379*f1cec17aSPascal Paillet 	[SCMI_PROTOCOL_VERSION] = protocol_version,
380*f1cec17aSPascal Paillet 	[SCMI_PROTOCOL_ATTRIBUTES] = protocol_attributes,
381*f1cec17aSPascal Paillet 	[SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = protocol_message_attributes,
382*f1cec17aSPascal Paillet 	[SCMI_PERF_DOMAIN_ATTRIBUTES] = scmi_perf_domain_attributes,
383*f1cec17aSPascal Paillet 	[SCMI_PERF_DESCRIBE_LEVELS] = scmi_perf_describe_levels,
384*f1cec17aSPascal Paillet 	[SCMI_PERF_LEVEL_SET] = scmi_perf_level_set,
385*f1cec17aSPascal Paillet 	[SCMI_PERF_LEVEL_GET] = scmi_perf_level_get,
386*f1cec17aSPascal Paillet };
387*f1cec17aSPascal Paillet 
message_id_is_supported(size_t message_id)388*f1cec17aSPascal Paillet static bool message_id_is_supported(size_t message_id)
389*f1cec17aSPascal Paillet {
390*f1cec17aSPascal Paillet 	return message_id < ARRAY_SIZE(scmi_perf_handler_table) &&
391*f1cec17aSPascal Paillet 	       scmi_perf_handler_table[message_id];
392*f1cec17aSPascal Paillet }
393*f1cec17aSPascal Paillet 
scmi_msg_get_perf_handler(struct scmi_msg * msg)394*f1cec17aSPascal Paillet scmi_msg_handler_t scmi_msg_get_perf_handler(struct scmi_msg *msg)
395*f1cec17aSPascal Paillet {
396*f1cec17aSPascal Paillet 	const size_t array_size = ARRAY_SIZE(scmi_perf_handler_table);
397*f1cec17aSPascal Paillet 	unsigned int message_id = 0;
398*f1cec17aSPascal Paillet 
399*f1cec17aSPascal Paillet 	if (msg->message_id >= array_size) {
400*f1cec17aSPascal Paillet 		DMSG("handle not found %u", msg->message_id);
401*f1cec17aSPascal Paillet 		return NULL;
402*f1cec17aSPascal Paillet 	}
403*f1cec17aSPascal Paillet 
404*f1cec17aSPascal Paillet 	message_id = confine_array_index(msg->message_id, array_size);
405*f1cec17aSPascal Paillet 
406*f1cec17aSPascal Paillet 	return scmi_perf_handler_table[message_id];
407*f1cec17aSPascal Paillet }
408