1 // SPDX-License-Identifier: BSD-3-Clause
2 /*
3  * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
4  * Copyright (c) 2020, Linaro Limited
5  */
6 #include <assert.h>
7 #include <config.h>
8 #include <confine_array_index.h>
9 #include <drivers/scmi-msg.h>
10 #include <drivers/scmi.h>
11 #include <string.h>
12 #include <util.h>
13 
14 #include "common.h"
15 #include "voltage_domain.h"
16 
17 static bool message_id_is_supported(unsigned int message_id);
18 
plat_scmi_voltd_count(unsigned int channel_id __unused)19 size_t __weak plat_scmi_voltd_count(unsigned int channel_id __unused)
20 {
21 	return 0;
22 }
23 
plat_scmi_voltd_get_name(unsigned int channel_id __unused,unsigned int scmi_id __unused)24 const char __weak *plat_scmi_voltd_get_name(unsigned int channel_id __unused,
25 					    unsigned int scmi_id __unused)
26 {
27 	return NULL;
28 }
29 
plat_scmi_voltd_levels_array(unsigned int channel_id __unused,unsigned int scmi_id __unused,size_t start_index __unused,long * levels __unused,size_t * nb_elts __unused)30 int32_t __weak plat_scmi_voltd_levels_array(unsigned int channel_id __unused,
31 					    unsigned int scmi_id __unused,
32 					    size_t start_index __unused,
33 					    long *levels __unused,
34 					    size_t *nb_elts __unused)
35 {
36 	return SCMI_NOT_SUPPORTED;
37 }
38 
plat_scmi_voltd_levels_by_step(unsigned int channel_id __unused,unsigned int scmi_id __unused,long * steps __unused)39 int32_t __weak plat_scmi_voltd_levels_by_step(unsigned int channel_id __unused,
40 					      unsigned int scmi_id __unused,
41 					      long *steps __unused)
42 {
43 	return SCMI_NOT_SUPPORTED;
44 }
45 
plat_scmi_voltd_get_level(unsigned int channel_id __unused,unsigned int scmi_id __unused,long * level __unused)46 int32_t __weak plat_scmi_voltd_get_level(unsigned int channel_id __unused,
47 					 unsigned int scmi_id __unused,
48 					 long *level __unused)
49 {
50 	return SCMI_NOT_SUPPORTED;
51 }
52 
plat_scmi_voltd_set_level(unsigned int channel_id __unused,unsigned int scmi_id __unused,long microvolt __unused)53 int32_t __weak plat_scmi_voltd_set_level(unsigned int channel_id __unused,
54 					 unsigned int scmi_id __unused,
55 					 long microvolt __unused)
56 {
57 	return SCMI_NOT_SUPPORTED;
58 }
59 
plat_scmi_voltd_get_config(unsigned int channel_id __unused,unsigned int scmi_id __unused,uint32_t * config __unused)60 int32_t __weak plat_scmi_voltd_get_config(unsigned int channel_id __unused,
61 					  unsigned int scmi_id __unused,
62 					  uint32_t *config __unused)
63 {
64 	return SCMI_NOT_SUPPORTED;
65 }
66 
plat_scmi_voltd_set_config(unsigned int channel_id __unused,unsigned int scmi_id __unused,uint32_t config __unused)67 int32_t __weak plat_scmi_voltd_set_config(unsigned int channel_id __unused,
68 					  unsigned int scmi_id __unused,
69 					  uint32_t config __unused)
70 {
71 	return SCMI_NOT_SUPPORTED;
72 }
73 
report_version(struct scmi_msg * msg)74 static void report_version(struct scmi_msg *msg)
75 {
76 	struct scmi_protocol_version_p2a out_args = {
77 		.status = SCMI_SUCCESS,
78 		.version = SCMI_PROTOCOL_VERSION_VOLTAGE_DOMAIN,
79 	};
80 
81 	if (IS_ENABLED(CFG_SCMI_MSG_STRICT_ABI) && msg->in_size) {
82 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
83 		return;
84 	}
85 
86 	scmi_write_response(msg, &out_args, sizeof(out_args));
87 }
88 
report_attributes(struct scmi_msg * msg)89 static void report_attributes(struct scmi_msg *msg)
90 {
91 	size_t domain_count = plat_scmi_voltd_count(msg->channel_id);
92 	struct scmi_protocol_attributes_p2a out_args = {
93 		.status = SCMI_SUCCESS,
94 		.attributes = domain_count,
95 	};
96 
97 	assert(!(domain_count & ~SCMI_VOLTAGE_DOMAIN_COUNT_MASK));
98 
99 	if (IS_ENABLED(CFG_SCMI_MSG_STRICT_ABI) && msg->in_size) {
100 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
101 		return;
102 	}
103 
104 	scmi_write_response(msg, &out_args, sizeof(out_args));
105 }
106 
report_message_attributes(struct scmi_msg * msg)107 static void report_message_attributes(struct scmi_msg *msg)
108 {
109 	struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in;
110 	struct scmi_protocol_message_attributes_p2a out_args = {
111 		.status = SCMI_SUCCESS,
112 		/* For this protocol, attributes shall be zero */
113 		.attributes = 0,
114 	};
115 
116 	if (msg->in_size != sizeof(*in_args)) {
117 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
118 		return;
119 	}
120 
121 	if (!message_id_is_supported(in_args->message_id)) {
122 		scmi_status_response(msg, SCMI_NOT_FOUND);
123 		return;
124 	}
125 
126 	scmi_write_response(msg, &out_args, sizeof(out_args));
127 }
128 
scmi_voltd_domain_attributes(struct scmi_msg * msg)129 static void scmi_voltd_domain_attributes(struct scmi_msg *msg)
130 {
131 	const struct scmi_voltd_attributes_a2p *in_args = (void *)msg->in;
132 	struct scmi_voltd_attributes_p2a out_args = {
133 		.status = SCMI_SUCCESS,
134 	};
135 	const char *name = NULL;
136 	unsigned int domain_id = 0;
137 
138 	if (msg->in_size != sizeof(*in_args)) {
139 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
140 		return;
141 	}
142 
143 	if (in_args->domain_id >= plat_scmi_voltd_count(msg->channel_id)) {
144 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
145 		return;
146 	}
147 
148 	domain_id = confine_array_index(in_args->domain_id,
149 					plat_scmi_voltd_count(msg->channel_id));
150 
151 	name = plat_scmi_voltd_get_name(msg->channel_id, domain_id);
152 	if (!name) {
153 		scmi_status_response(msg, SCMI_NOT_FOUND);
154 		return;
155 	}
156 
157 	COPY_NAME_IDENTIFIER(out_args.name, name);
158 
159 	scmi_write_response(msg, &out_args, sizeof(out_args));
160 }
161 
162 #define LEVELS_BY_ARRAY(_nb_rates, _rem_rates) \
163 	SCMI_VOLTAGE_DOMAIN_LEVELS_FLAGS((_nb_rates), \
164 					 SCMI_VOLTD_LEVELS_FORMAT_LIST, \
165 					 (_rem_rates))
166 
167 #define LEVELS_BY_STEP \
168 	SCMI_VOLTAGE_DOMAIN_LEVELS_FLAGS(3, SCMI_VOLTD_LEVELS_FORMAT_RANGE, 0)
169 
170 #define LEVEL_DESC_SIZE		sizeof(int32_t)
171 
scmi_voltd_describe_levels(struct scmi_msg * msg)172 static void scmi_voltd_describe_levels(struct scmi_msg *msg)
173 {
174 	const struct scmi_voltd_describe_levels_a2p *in_args = (void *)msg->in;
175 	struct scmi_voltd_describe_levels_p2a out_args = { };
176 	int32_t status = SCMI_GENERIC_ERROR;
177 	unsigned int out_count = 0;
178 	unsigned int domain_id = 0;
179 	int32_t *out_levels = NULL;
180 	size_t nb_levels = 0;
181 
182 	if (msg->in_size != sizeof(*in_args)) {
183 		status = SCMI_PROTOCOL_ERROR;
184 		goto out;
185 	}
186 
187 	if (in_args->domain_id >= plat_scmi_voltd_count(msg->channel_id)) {
188 		status = SCMI_INVALID_PARAMETERS;
189 		goto out;
190 	}
191 
192 	if (msg->out_size < sizeof(out_args)) {
193 		status = SCMI_INVALID_PARAMETERS;
194 		goto out;
195 	}
196 	assert(IS_ALIGNED_WITH_TYPE(msg->out + sizeof(out_args), int32_t));
197 	out_levels = (int32_t *)(uintptr_t)(msg->out + sizeof(out_args));
198 
199 	domain_id = confine_array_index(in_args->domain_id,
200 					plat_scmi_voltd_count(msg->channel_id));
201 
202 	/* Platform may support array rate description */
203 	status = plat_scmi_voltd_levels_array(msg->channel_id, domain_id, 0,
204 					      NULL, &nb_levels);
205 	if (status == SCMI_SUCCESS) {
206 		size_t avail_sz = msg->out_size - sizeof(out_args);
207 		unsigned int level_index = in_args->level_index;
208 		unsigned int remaining = 0;
209 
210 		if (avail_sz < LEVEL_DESC_SIZE && nb_levels) {
211 			status = SCMI_PROTOCOL_ERROR;
212 			goto out;
213 		}
214 
215 		while (avail_sz >= LEVEL_DESC_SIZE && level_index < nb_levels) {
216 			long plat_level = 0;
217 			size_t cnt = 1;
218 
219 			status = plat_scmi_voltd_levels_array(msg->channel_id,
220 							      domain_id,
221 							      level_index,
222 							      &plat_level,
223 							      &cnt);
224 			if (status)
225 				goto out;
226 
227 			*out_levels = plat_level;
228 
229 			avail_sz -= LEVEL_DESC_SIZE;
230 			out_levels++;
231 			level_index++;
232 		}
233 
234 		remaining = nb_levels - in_args->level_index;
235 		out_count = level_index - in_args->level_index;
236 		out_args.flags = LEVELS_BY_ARRAY(out_count, remaining);
237 	} else if (status == SCMI_NOT_SUPPORTED) {
238 		long triplet[3] = { 0, 0, 0 };
239 
240 		if (msg->out_size < sizeof(out_args) + 3 * LEVEL_DESC_SIZE) {
241 			status = SCMI_PROTOCOL_ERROR;
242 			goto out;
243 		}
244 
245 		/* Platform may support min/max/step triplet description */
246 		status =  plat_scmi_voltd_levels_by_step(msg->channel_id,
247 							 domain_id, triplet);
248 		if (status)
249 			goto out;
250 
251 		out_levels[0] = triplet[0];
252 		out_levels[1] = triplet[1];
253 		out_levels[2] = triplet[2];
254 
255 		out_count = 3;
256 		out_args.flags = LEVELS_BY_STEP;
257 	}
258 
259 out:
260 	if (status) {
261 		scmi_status_response(msg, status);
262 	} else {
263 		out_args.status = SCMI_SUCCESS;
264 		memcpy(msg->out, &out_args, sizeof(out_args));
265 		msg->out_size_out = sizeof(out_args) +
266 				    out_count * LEVEL_DESC_SIZE;
267 	}
268 }
269 
scmi_voltd_config_set(struct scmi_msg * msg)270 static void scmi_voltd_config_set(struct scmi_msg *msg)
271 {
272 	const struct scmi_voltd_config_set_a2p *in_args = (void *)msg->in;
273 	unsigned int domain_id = 0;
274 	unsigned long config = 0;
275 	int32_t status = SCMI_GENERIC_ERROR;
276 
277 	if (msg->in_size != sizeof(*in_args)) {
278 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
279 		return;
280 	}
281 
282 	if (in_args->domain_id >= plat_scmi_voltd_count(msg->channel_id)) {
283 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
284 		return;
285 	}
286 
287 	domain_id = confine_array_index(in_args->domain_id,
288 					plat_scmi_voltd_count(msg->channel_id));
289 
290 	config = in_args->config & SCMI_VOLTAGE_DOMAIN_CONFIG_MASK;
291 
292 	status = plat_scmi_voltd_set_config(msg->channel_id, domain_id, config);
293 
294 	scmi_status_response(msg, status);
295 }
296 
scmi_voltd_config_get(struct scmi_msg * msg)297 static void scmi_voltd_config_get(struct scmi_msg *msg)
298 {
299 	const struct scmi_voltd_config_get_a2p *in_args = (void *)msg->in;
300 	struct scmi_voltd_config_get_p2a out_args = { };
301 	unsigned int domain_id = 0;
302 
303 	if (msg->in_size != sizeof(*in_args)) {
304 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
305 		return;
306 	}
307 
308 	if (in_args->domain_id >= plat_scmi_voltd_count(msg->channel_id)) {
309 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
310 		return;
311 	}
312 
313 	domain_id = confine_array_index(in_args->domain_id,
314 					plat_scmi_voltd_count(msg->channel_id));
315 
316 	if (plat_scmi_voltd_get_config(msg->channel_id, domain_id,
317 				       &out_args.config)) {
318 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
319 		return;
320 	}
321 
322 	scmi_write_response(msg, &out_args, sizeof(out_args));
323 }
324 
scmi_voltd_level_set(struct scmi_msg * msg)325 static void scmi_voltd_level_set(struct scmi_msg *msg)
326 {
327 	const struct scmi_voltd_level_set_a2p *in_args = (void *)msg->in;
328 	int32_t status = SCMI_GENERIC_ERROR;
329 	unsigned int domain_id = 0;
330 
331 	if (msg->in_size != sizeof(*in_args)) {
332 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
333 		return;
334 	}
335 
336 	if (in_args->domain_id >= plat_scmi_voltd_count(msg->channel_id)) {
337 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
338 		return;
339 	}
340 
341 	domain_id = confine_array_index(in_args->domain_id,
342 					plat_scmi_voltd_count(msg->channel_id));
343 
344 	status = plat_scmi_voltd_set_level(msg->channel_id, domain_id,
345 					   in_args->voltage_level);
346 
347 	scmi_status_response(msg, status);
348 }
349 
scmi_voltd_level_get(struct scmi_msg * msg)350 static void scmi_voltd_level_get(struct scmi_msg *msg)
351 {
352 	const struct scmi_voltd_level_get_a2p *in_args = (void *)msg->in;
353 	struct scmi_voltd_level_get_p2a out_args = {
354 		.status = SCMI_SUCCESS,
355 	};
356 	unsigned int domain_id = 0;
357 	long level = 0;
358 
359 	if (msg->in_size != sizeof(*in_args)) {
360 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
361 		return;
362 	}
363 
364 	if (in_args->domain_id >= plat_scmi_voltd_count(msg->channel_id)) {
365 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
366 		return;
367 	}
368 
369 	domain_id = confine_array_index(in_args->domain_id,
370 					plat_scmi_voltd_count(msg->channel_id));
371 
372 	out_args.status = plat_scmi_voltd_get_level(msg->channel_id, domain_id,
373 						    &level);
374 	out_args.voltage_level = level;
375 
376 	scmi_write_response(msg, &out_args, sizeof(out_args));
377 }
378 
379 static const scmi_msg_handler_t handler_array[] = {
380 	[SCMI_PROTOCOL_VERSION] = report_version,
381 	[SCMI_PROTOCOL_ATTRIBUTES] = report_attributes,
382 	[SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes,
383 	[SCMI_VOLTAGE_DOMAIN_ATTRIBUTES] = scmi_voltd_domain_attributes,
384 	[SCMI_VOLTAGE_DESCRIBE_LEVELS] = scmi_voltd_describe_levels,
385 	[SCMI_VOLTAGE_CONFIG_SET] = scmi_voltd_config_set,
386 	[SCMI_VOLTAGE_CONFIG_GET] = scmi_voltd_config_get,
387 	[SCMI_VOLTAGE_LEVEL_SET] = scmi_voltd_level_set,
388 	[SCMI_VOLTAGE_LEVEL_GET] = scmi_voltd_level_get,
389 };
390 
message_id_is_supported(unsigned int id)391 static bool message_id_is_supported(unsigned int id)
392 {
393 	return id < ARRAY_SIZE(handler_array) && handler_array[id];
394 }
395 
scmi_msg_get_voltd_handler(struct scmi_msg * msg)396 scmi_msg_handler_t scmi_msg_get_voltd_handler(struct scmi_msg *msg)
397 {
398 	const size_t array_size = ARRAY_SIZE(handler_array);
399 	unsigned int message_id = 0;
400 
401 	if (msg->message_id >= array_size) {
402 		DMSG("Voltage domain handle not found %u", msg->message_id);
403 		return NULL;
404 	}
405 
406 	message_id = confine_array_index(msg->message_id, array_size);
407 
408 	return handler_array[message_id];
409 }
410