1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2018-2023, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * SCMI Core Configuration Protocol Support.
9 */
10
11 #include <internal/scmi_apcore.h>
12
13 #include <mod_scmi.h>
14 #include <mod_scmi_apcore.h>
15
16 #include <fwk_assert.h>
17 #include <fwk_id.h>
18 #include <fwk_log.h>
19 #include <fwk_macros.h>
20 #include <fwk_module.h>
21 #include <fwk_module_idx.h>
22 #include <fwk_status.h>
23
24 #include <stdbool.h>
25 #include <stdint.h>
26
27 struct scmi_apcore_ctx {
28 /* Module Configuration */
29 const struct mod_scmi_apcore_config *config;
30
31 /* SCMI module API */
32 const struct mod_scmi_from_protocol_api *scmi_api;
33
34 /*
35 * Tracks whether an agent has requested that the configuration be locked.
36 * \c true if the configuration is locked and the reset address of the CPUs
37 * can no longer be altered, \c false otherwise.
38 */
39 bool locked;
40 };
41
42 static int scmi_apcore_protocol_version_handler(fwk_id_t service_id,
43 const uint32_t *payload);
44 static int scmi_apcore_protocol_attributes_handler(fwk_id_t service_id,
45 const uint32_t *payload);
46 static int scmi_apcore_protocol_message_attributes_handler(
47 fwk_id_t service_id, const uint32_t *payload);
48 static int scmi_apcore_reset_address_set_handler(fwk_id_t service_id,
49 const uint32_t *payload);
50 static int scmi_apcore_reset_address_get_handler(fwk_id_t service_id,
51 const uint32_t *payload);
52
53 /*
54 * Internal variables.
55 */
56 static struct scmi_apcore_ctx scmi_apcore_ctx;
57
58 static int (*const handler_table[])(fwk_id_t, const uint32_t *) = {
59 [MOD_SCMI_PROTOCOL_VERSION] = scmi_apcore_protocol_version_handler,
60 [MOD_SCMI_PROTOCOL_ATTRIBUTES] = scmi_apcore_protocol_attributes_handler,
61 [MOD_SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
62 scmi_apcore_protocol_message_attributes_handler,
63 [MOD_SCMI_APCORE_RESET_ADDRESS_SET] = scmi_apcore_reset_address_set_handler,
64 [MOD_SCMI_APCORE_RESET_ADDRESS_GET] = scmi_apcore_reset_address_get_handler,
65 };
66
67 static const unsigned int payload_size_table[] = {
68 [MOD_SCMI_PROTOCOL_VERSION] = 0,
69 [MOD_SCMI_PROTOCOL_ATTRIBUTES] = 0,
70 [MOD_SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
71 (unsigned int)sizeof(struct scmi_protocol_message_attributes_a2p),
72 [MOD_SCMI_APCORE_RESET_ADDRESS_SET] =
73 (unsigned int)sizeof(struct scmi_apcore_reset_address_set_a2p),
74 [MOD_SCMI_APCORE_RESET_ADDRESS_GET] = 0,
75 };
76
77 /*
78 * Static, Helper Functions
79 */
set_reset_address(uint32_t address_low,uint32_t address_high)80 static int set_reset_address(uint32_t address_low, uint32_t address_high)
81 {
82 uint64_t address_composite;
83 unsigned int grp_idx;
84 unsigned int reg_idx;
85 const struct mod_scmi_apcore_reset_register_group *reg_group;
86 uintptr_t reset_reg;
87
88 address_composite = ((uint64_t)address_high << 32) | address_low;
89
90 /* Iterate over the reset register group structures */
91 for (grp_idx = 0;
92 grp_idx < scmi_apcore_ctx.config->reset_register_group_count;
93 grp_idx++) {
94
95 reg_group =
96 &scmi_apcore_ctx.config->reset_register_group_table[grp_idx];
97 fwk_assert(reg_group->base_register != 0);
98
99 /* Begin with the first register in the group */
100 reset_reg = reg_group->base_register;
101
102 /* Program each reset vector register within the group */
103 for (reg_idx = 0; reg_idx < reg_group->register_count; reg_idx++) {
104 if (scmi_apcore_ctx.config->reset_register_width ==
105 MOD_SCMI_APCORE_REG_WIDTH_32) {
106 /* Treat the register as 32-bit */
107 *(uint32_t *)reset_reg = address_low;
108 reset_reg += sizeof(uint32_t);
109 } else {
110 /* Treat the register as 64-bit */
111 *(uint64_t *)reset_reg = address_composite;
112 reset_reg += sizeof(uint64_t);
113 }
114 }
115 }
116
117 return FWK_SUCCESS;
118 }
119
120 /*
121 * Protocol Version
122 */
scmi_apcore_protocol_version_handler(fwk_id_t service_id,const uint32_t * payload)123 static int scmi_apcore_protocol_version_handler(fwk_id_t service_id,
124 const uint32_t *payload)
125 {
126 struct scmi_protocol_version_p2a return_values = {
127 .status = (int32_t)SCMI_SUCCESS,
128 .version = MOD_SCMI_PROTOCOL_VERSION_APCORE,
129 };
130
131 return scmi_apcore_ctx.scmi_api->respond(
132 service_id, &return_values, sizeof(return_values));
133 }
134
135 /*
136 * Protocol Attributes
137 */
scmi_apcore_protocol_attributes_handler(fwk_id_t service_id,const uint32_t * payload)138 static int scmi_apcore_protocol_attributes_handler(fwk_id_t service_id,
139 const uint32_t *payload)
140 {
141 struct scmi_protocol_attributes_p2a return_values = {
142 .status = (int32_t)SCMI_SUCCESS,
143 .attributes = 0,
144 };
145
146 if (scmi_apcore_ctx.config->reset_register_width ==
147 MOD_SCMI_APCORE_REG_WIDTH_64) {
148 return_values.attributes |=
149 MOD_SCMI_APCORE_PROTOCOL_ATTRIBUTES_64BIT_MASK;
150 }
151
152 return scmi_apcore_ctx.scmi_api->respond(
153 service_id, &return_values, sizeof(return_values));
154 }
155
156 /*
157 * Protocol Message Attributes
158 */
scmi_apcore_protocol_message_attributes_handler(fwk_id_t service_id,const uint32_t * payload)159 static int scmi_apcore_protocol_message_attributes_handler(fwk_id_t service_id,
160 const uint32_t *payload)
161 {
162 size_t response_size;
163 const struct scmi_protocol_message_attributes_a2p *parameters;
164 unsigned int message_id;
165 struct scmi_protocol_message_attributes_p2a return_values = {
166 .status = (int32_t)SCMI_SUCCESS,
167 .attributes = 0,
168 };
169
170 parameters = (const struct scmi_protocol_message_attributes_a2p *)
171 payload;
172 message_id = parameters->message_id;
173
174 if ((message_id >= FWK_ARRAY_SIZE(handler_table)) ||
175 (handler_table[message_id] == NULL)) {
176 return_values.status = (int32_t)SCMI_NOT_FOUND;
177 }
178
179 response_size = (return_values.status == SCMI_SUCCESS) ?
180 sizeof(return_values) : sizeof(return_values.status);
181
182 return scmi_apcore_ctx.scmi_api->respond(
183 service_id, &return_values, response_size);
184 }
185
186 /*
187 * Reset Address Set
188 */
scmi_apcore_reset_address_set_handler(fwk_id_t service_id,const uint32_t * payload)189 static int scmi_apcore_reset_address_set_handler(fwk_id_t service_id,
190 const uint32_t *payload)
191 {
192 int status;
193 unsigned int agent_id;
194 enum scmi_agent_type agent_type;
195 const struct scmi_apcore_reset_address_set_a2p *parameters;
196 struct scmi_apcore_reset_address_set_p2a return_values = {
197 .status = (int32_t)SCMI_GENERIC_ERROR
198 };
199
200 parameters = (const struct scmi_apcore_reset_address_set_a2p *)payload;
201
202 status = scmi_apcore_ctx.scmi_api->get_agent_id(service_id, &agent_id);
203 if (status != FWK_SUCCESS) {
204 goto exit;
205 }
206
207 status = scmi_apcore_ctx.scmi_api->get_agent_type(agent_id, &agent_type);
208 if (status != FWK_SUCCESS) {
209 goto exit;
210 }
211
212 /* Only the PSCI agent may set the reset address */
213 if (agent_type != SCMI_AGENT_TYPE_PSCI) {
214 return_values.status = (int32_t)SCMI_DENIED;
215 goto exit;
216 }
217
218 /* An agent previously requested that the configuration be locked */
219 if (scmi_apcore_ctx.locked) {
220 return_values.status = (int32_t)SCMI_DENIED;
221 goto exit;
222 }
223
224 /*
225 * Ensure that the platform has 64-bit reset vector registers if a reset
226 * address utilizing more that 32 bits has been provided.
227 */
228 if ((parameters->reset_address_high != 0) &&
229 (scmi_apcore_ctx.config->reset_register_width ==
230 MOD_SCMI_APCORE_REG_WIDTH_32)) {
231 return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
232 goto exit;
233 }
234
235 /* Check for alignment */
236 if ((parameters->reset_address_low % 4) != 0) {
237 return_values.status = (int32_t)SCMI_INVALID_PARAMETERS;
238 goto exit;
239 }
240
241 status = set_reset_address(
242 parameters->reset_address_low, parameters->reset_address_high);
243 if (status != FWK_SUCCESS) {
244 goto exit;
245 }
246
247 return_values.status = (int32_t)SCMI_SUCCESS;
248
249 /* Lock the configuration if requested */
250 if (parameters->attributes & MOD_SCMI_APCORE_RESET_ADDRESS_SET_LOCK_MASK) {
251 scmi_apcore_ctx.locked = true;
252 }
253
254 exit:
255 return scmi_apcore_ctx.scmi_api->respond(
256 service_id, &return_values, sizeof(return_values));
257 }
258
259 /*
260 * Reset Address Get
261 */
scmi_apcore_reset_address_get_handler(fwk_id_t service_id,const uint32_t * payload)262 static int scmi_apcore_reset_address_get_handler(fwk_id_t service_id,
263 const uint32_t *payload)
264 {
265 int status, respond_status;
266 unsigned int agent_id;
267 const struct mod_scmi_apcore_reset_register_group *reg_group;
268 uint64_t reset_address;
269 enum scmi_agent_type agent_type;
270 struct scmi_apcore_reset_address_get_p2a return_values = {
271 .status = (int32_t)SCMI_GENERIC_ERROR
272 };
273
274 status = scmi_apcore_ctx.scmi_api->get_agent_id(service_id, &agent_id);
275 if (status != FWK_SUCCESS) {
276 goto exit;
277 }
278
279 status = scmi_apcore_ctx.scmi_api->get_agent_type(agent_id, &agent_type);
280 if (status != FWK_SUCCESS) {
281 goto exit;
282 }
283
284 /* Only the PSCI agent may get the current reset address */
285 if (agent_type != SCMI_AGENT_TYPE_PSCI) {
286 return_values.status = (int32_t)SCMI_DENIED;
287 goto exit;
288 }
289
290 /* The reset address is common across all reset address registers */
291 reg_group = &scmi_apcore_ctx.config->reset_register_group_table[0];
292
293 if (scmi_apcore_ctx.config->reset_register_width ==
294 MOD_SCMI_APCORE_REG_WIDTH_32) {
295 reset_address = *(uint32_t *)reg_group->base_register;
296 return_values.reset_address_high = 0;
297 } else {
298 reset_address = *(uint64_t *)reg_group->base_register;
299 return_values.reset_address_high =
300 (uint32_t)((reset_address >> 32) & UINT32_MAX);
301 }
302
303 return_values.reset_address_low = (uint32_t)reset_address;
304
305 return_values.attributes |=
306 (scmi_apcore_ctx.locked << MOD_SCMI_APCORE_RESET_ADDRESS_GET_LOCK_POS);
307 return_values.status = (int32_t)SCMI_SUCCESS;
308
309 exit:
310 respond_status = scmi_apcore_ctx.scmi_api->respond(
311 service_id, &return_values, sizeof(return_values));
312
313 if (respond_status != FWK_SUCCESS) {
314 FWK_LOG_DEBUG("[SCMI-APCORE] %s @%d", __func__, __LINE__);
315 }
316
317 return status;
318 }
319
320 /*
321 * SCMI module -> SCMI AP Core Configuration module interface
322 */
scmi_apcore_get_scmi_protocol_id(fwk_id_t protocol_id,uint8_t * scmi_protocol_id)323 static int scmi_apcore_get_scmi_protocol_id(fwk_id_t protocol_id,
324 uint8_t *scmi_protocol_id)
325 {
326 *scmi_protocol_id = (uint8_t)MOD_SCMI_PROTOCOL_ID_APCORE;
327
328 return FWK_SUCCESS;
329 }
330
scmi_apcore_message_handler(fwk_id_t protocol_id,fwk_id_t service_id,const uint32_t * payload,size_t payload_size,unsigned int message_id)331 static int scmi_apcore_message_handler(
332 fwk_id_t protocol_id,
333 fwk_id_t service_id,
334 const uint32_t *payload,
335 size_t payload_size,
336 unsigned int message_id)
337 {
338 int32_t return_value;
339
340 static_assert(FWK_ARRAY_SIZE(handler_table) ==
341 FWK_ARRAY_SIZE(payload_size_table),
342 "[SCMI] Core configuration protocol table sizes not consistent");
343 fwk_assert(payload != NULL);
344
345 if (message_id >= FWK_ARRAY_SIZE(handler_table)) {
346 return_value = (int32_t)SCMI_NOT_FOUND;
347 goto error;
348 }
349
350 if (payload_size != payload_size_table[message_id]) {
351 return_value = (int32_t)SCMI_PROTOCOL_ERROR;
352 goto error;
353 }
354
355 return handler_table[message_id](service_id, payload);
356
357 error:
358 return scmi_apcore_ctx.scmi_api->respond(
359 service_id, &return_value, sizeof(return_value));
360 }
361
362 static struct mod_scmi_to_protocol_api scmi_apcore_mod_scmi_to_protocol_api = {
363 .get_scmi_protocol_id = scmi_apcore_get_scmi_protocol_id,
364 .message_handler = scmi_apcore_message_handler
365 };
366
367 /*
368 * Framework handlers
369 */
370
scmi_apcore_init(fwk_id_t module_id,unsigned int element_count,const void * data)371 static int scmi_apcore_init(fwk_id_t module_id, unsigned int element_count,
372 const void *data)
373 {
374 const struct mod_scmi_apcore_config *config =
375 (const struct mod_scmi_apcore_config *)data;
376
377 if (config == NULL) {
378 return FWK_E_PARAM;
379 }
380 if (config->reset_register_group_table == NULL) {
381 return FWK_E_PARAM;
382 }
383 if (config->reset_register_group_count == 0) {
384 return FWK_E_PARAM;
385 }
386 if (config->reset_register_width >= MOD_SCMI_APCORE_REG_WIDTH_COUNT) {
387 return FWK_E_PARAM;
388 }
389
390 scmi_apcore_ctx.config = config;
391
392 return FWK_SUCCESS;
393 }
394
scmi_apcore_bind(fwk_id_t id,unsigned int round)395 static int scmi_apcore_bind(fwk_id_t id, unsigned int round)
396 {
397 if (round == 1) {
398 return FWK_SUCCESS;
399 }
400
401 /* Bind to the SCMI module, storing an API pointer for later use. */
402 return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
403 FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),
404 &scmi_apcore_ctx.scmi_api);
405 }
406
scmi_apcore_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)407 static int scmi_apcore_process_bind_request(fwk_id_t source_id,
408 fwk_id_t target_id, fwk_id_t api_id, const void **api)
409 {
410 /* Only accept binding requests from the SCMI module. */
411 if (!fwk_id_is_equal(source_id, FWK_ID_MODULE(FWK_MODULE_IDX_SCMI))) {
412 return FWK_E_ACCESS;
413 }
414
415 *api = &scmi_apcore_mod_scmi_to_protocol_api;
416
417 return FWK_SUCCESS;
418 }
419
420 /* SCMI Clock Management Protocol Definition */
421 const struct fwk_module module_scmi_apcore = {
422 .api_count = 1,
423 .type = FWK_MODULE_TYPE_PROTOCOL,
424 .init = scmi_apcore_init,
425 .bind = scmi_apcore_bind,
426 .process_bind_request = scmi_apcore_process_bind_request,
427 };
428