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