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