1b4734308SPeng Fan // SPDX-License-Identifier: BSD-3-Clause
2b4734308SPeng Fan /*
3b4734308SPeng Fan * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved.
4b4734308SPeng Fan * Copyright (c) 2019-2020, Linaro Limited
5b4734308SPeng Fan */
6b4734308SPeng Fan #include <cdefs.h>
7b4734308SPeng Fan #include <string.h>
8b4734308SPeng Fan
9b4734308SPeng Fan #include <drivers/scmi-msg.h>
10b4734308SPeng Fan #include <drivers/scmi.h>
11b4734308SPeng Fan #include <lib/utils_def.h>
12b4734308SPeng Fan
13b4734308SPeng Fan #include "common.h"
14b4734308SPeng Fan
15b4734308SPeng Fan #pragma weak plat_scmi_clock_count
16b4734308SPeng Fan #pragma weak plat_scmi_clock_get_name
17*684952d1SKamlesh Gurudasani #pragma weak plat_scmi_clock_get_enable_delay
18b4734308SPeng Fan #pragma weak plat_scmi_clock_rates_array
19b4734308SPeng Fan #pragma weak plat_scmi_clock_rates_by_step
20*684952d1SKamlesh Gurudasani #pragma weak plat_scmi_clock_get_possible_parents
21*684952d1SKamlesh Gurudasani #pragma weak plat_scmi_clock_get_parent
22*684952d1SKamlesh Gurudasani #pragma weak plat_scmi_clock_set_parent
23b4734308SPeng Fan #pragma weak plat_scmi_clock_get_rate
24b4734308SPeng Fan #pragma weak plat_scmi_clock_set_rate
25b4734308SPeng Fan #pragma weak plat_scmi_clock_get_state
26b4734308SPeng Fan #pragma weak plat_scmi_clock_set_state
27*684952d1SKamlesh Gurudasani #pragma weak plat_scmi_clock_get_extended_config
28*684952d1SKamlesh Gurudasani #pragma weak plat_scmi_clock_set_extended_config
29b4734308SPeng Fan
30b4734308SPeng Fan static bool message_id_is_supported(unsigned int message_id);
31b4734308SPeng Fan
plat_scmi_clock_count(unsigned int agent_id __unused)32b4734308SPeng Fan size_t plat_scmi_clock_count(unsigned int agent_id __unused)
33b4734308SPeng Fan {
34b4734308SPeng Fan return 0U;
35b4734308SPeng Fan }
36b4734308SPeng Fan
plat_scmi_clock_get_name(unsigned int agent_id __unused,unsigned int scmi_id __unused)37b4734308SPeng Fan const char *plat_scmi_clock_get_name(unsigned int agent_id __unused,
38b4734308SPeng Fan unsigned int scmi_id __unused)
39b4734308SPeng Fan {
40b4734308SPeng Fan return NULL;
41b4734308SPeng Fan }
42b4734308SPeng Fan
plat_scmi_clock_get_enable_delay(unsigned int agent_id __unused,unsigned int scmi_id __unused)43*684952d1SKamlesh Gurudasani uint32_t plat_scmi_clock_get_enable_delay(unsigned int agent_id __unused,
44*684952d1SKamlesh Gurudasani unsigned int scmi_id __unused)
45*684952d1SKamlesh Gurudasani {
46*684952d1SKamlesh Gurudasani return 0U;
47*684952d1SKamlesh Gurudasani }
48*684952d1SKamlesh Gurudasani
plat_scmi_clock_rates_array(unsigned int agent_id __unused,unsigned int scmi_id __unused,unsigned long * rates __unused,size_t * nb_elts __unused,uint32_t start_idx __unused)49b4734308SPeng Fan int32_t plat_scmi_clock_rates_array(unsigned int agent_id __unused,
50b4734308SPeng Fan unsigned int scmi_id __unused,
51b4734308SPeng Fan unsigned long *rates __unused,
52ca9d6edcSXiaoDong Huang size_t *nb_elts __unused,
53ca9d6edcSXiaoDong Huang uint32_t start_idx __unused)
54b4734308SPeng Fan {
55b4734308SPeng Fan return SCMI_NOT_SUPPORTED;
56b4734308SPeng Fan }
57b4734308SPeng Fan
plat_scmi_clock_rates_by_step(unsigned int agent_id __unused,unsigned int scmi_id __unused,unsigned long * steps __unused)58b4734308SPeng Fan int32_t plat_scmi_clock_rates_by_step(unsigned int agent_id __unused,
59b4734308SPeng Fan unsigned int scmi_id __unused,
60b4734308SPeng Fan unsigned long *steps __unused)
61b4734308SPeng Fan {
62b4734308SPeng Fan return SCMI_NOT_SUPPORTED;
63b4734308SPeng Fan }
64b4734308SPeng Fan
plat_scmi_clock_get_possible_parents(unsigned int agent_id,unsigned int scmi_id,unsigned int * plat_possible_parents,size_t * nb_elts,unsigned int skip_parents)65*684952d1SKamlesh Gurudasani int32_t plat_scmi_clock_get_possible_parents(unsigned int agent_id,
66*684952d1SKamlesh Gurudasani unsigned int scmi_id,
67*684952d1SKamlesh Gurudasani unsigned int *plat_possible_parents,
68*684952d1SKamlesh Gurudasani size_t *nb_elts,
69*684952d1SKamlesh Gurudasani unsigned int skip_parents)
70*684952d1SKamlesh Gurudasani {
71*684952d1SKamlesh Gurudasani return SCMI_NOT_SUPPORTED;
72*684952d1SKamlesh Gurudasani }
73*684952d1SKamlesh Gurudasani
plat_scmi_clock_get_parent(unsigned int agent_id,unsigned int scmi_id,unsigned int * parent_id)74*684952d1SKamlesh Gurudasani int32_t plat_scmi_clock_get_parent(unsigned int agent_id,
75*684952d1SKamlesh Gurudasani unsigned int scmi_id,
76*684952d1SKamlesh Gurudasani unsigned int *parent_id)
77*684952d1SKamlesh Gurudasani {
78*684952d1SKamlesh Gurudasani return SCMI_NOT_SUPPORTED;
79*684952d1SKamlesh Gurudasani }
80*684952d1SKamlesh Gurudasani
plat_scmi_clock_set_parent(unsigned int agent_id,unsigned int scmi_id,unsigned int parent_id)81*684952d1SKamlesh Gurudasani int32_t plat_scmi_clock_set_parent(unsigned int agent_id,
82*684952d1SKamlesh Gurudasani unsigned int scmi_id,
83*684952d1SKamlesh Gurudasani unsigned int parent_id)
84*684952d1SKamlesh Gurudasani {
85*684952d1SKamlesh Gurudasani return SCMI_NOT_SUPPORTED;
86*684952d1SKamlesh Gurudasani }
87*684952d1SKamlesh Gurudasani
plat_scmi_clock_get_rate(unsigned int agent_id __unused,unsigned int scmi_id __unused)88b4734308SPeng Fan unsigned long plat_scmi_clock_get_rate(unsigned int agent_id __unused,
89b4734308SPeng Fan unsigned int scmi_id __unused)
90b4734308SPeng Fan {
91b4734308SPeng Fan return 0U;
92b4734308SPeng Fan }
93b4734308SPeng Fan
plat_scmi_clock_set_rate(unsigned int agent_id __unused,unsigned int scmi_id __unused,unsigned long rate __unused)94b4734308SPeng Fan int32_t plat_scmi_clock_set_rate(unsigned int agent_id __unused,
95b4734308SPeng Fan unsigned int scmi_id __unused,
96b4734308SPeng Fan unsigned long rate __unused)
97b4734308SPeng Fan {
98b4734308SPeng Fan return SCMI_NOT_SUPPORTED;
99b4734308SPeng Fan }
100b4734308SPeng Fan
plat_scmi_clock_get_state(unsigned int agent_id __unused,unsigned int scmi_id __unused)101b4734308SPeng Fan int32_t plat_scmi_clock_get_state(unsigned int agent_id __unused,
102b4734308SPeng Fan unsigned int scmi_id __unused)
103b4734308SPeng Fan {
104b4734308SPeng Fan return SCMI_NOT_SUPPORTED;
105b4734308SPeng Fan }
106b4734308SPeng Fan
plat_scmi_clock_set_state(unsigned int agent_id __unused,unsigned int scmi_id __unused,bool enable_not_disable __unused)107b4734308SPeng Fan int32_t plat_scmi_clock_set_state(unsigned int agent_id __unused,
108b4734308SPeng Fan unsigned int scmi_id __unused,
109b4734308SPeng Fan bool enable_not_disable __unused)
110b4734308SPeng Fan {
111b4734308SPeng Fan return SCMI_NOT_SUPPORTED;
112b4734308SPeng Fan }
113b4734308SPeng Fan
plat_scmi_clock_get_extended_config(unsigned int agent_id __unused,unsigned int scmi_id __unused,unsigned char extended_config_type __unused,unsigned int * extended_config_val __unused)114*684952d1SKamlesh Gurudasani int32_t plat_scmi_clock_get_extended_config(unsigned int agent_id __unused,
115*684952d1SKamlesh Gurudasani unsigned int scmi_id __unused,
116*684952d1SKamlesh Gurudasani unsigned char extended_config_type __unused,
117*684952d1SKamlesh Gurudasani unsigned int *extended_config_val __unused)
118*684952d1SKamlesh Gurudasani {
119*684952d1SKamlesh Gurudasani return SCMI_NOT_SUPPORTED;
120*684952d1SKamlesh Gurudasani }
121*684952d1SKamlesh Gurudasani
plat_scmi_clock_set_extended_config(unsigned int agent_id __unused,unsigned int scmi_id __unused,unsigned char extended_config_type __unused,unsigned int extended_config_val __unused)122*684952d1SKamlesh Gurudasani int32_t plat_scmi_clock_set_extended_config(unsigned int agent_id __unused,
123*684952d1SKamlesh Gurudasani unsigned int scmi_id __unused,
124*684952d1SKamlesh Gurudasani unsigned char extended_config_type __unused,
125*684952d1SKamlesh Gurudasani unsigned int extended_config_val __unused)
126*684952d1SKamlesh Gurudasani {
127*684952d1SKamlesh Gurudasani return SCMI_NOT_SUPPORTED;
128*684952d1SKamlesh Gurudasani }
129*684952d1SKamlesh Gurudasani
report_version(struct scmi_msg * msg)130b4734308SPeng Fan static void report_version(struct scmi_msg *msg)
131b4734308SPeng Fan {
132b4734308SPeng Fan struct scmi_protocol_version_p2a return_values = {
133b4734308SPeng Fan .status = SCMI_SUCCESS,
134b4734308SPeng Fan .version = SCMI_PROTOCOL_VERSION_CLOCK,
135b4734308SPeng Fan };
136b4734308SPeng Fan
137b4734308SPeng Fan if (msg->in_size != 0) {
138b4734308SPeng Fan scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
139b4734308SPeng Fan return;
140b4734308SPeng Fan }
141b4734308SPeng Fan
142b4734308SPeng Fan scmi_write_response(msg, &return_values, sizeof(return_values));
143b4734308SPeng Fan }
144b4734308SPeng Fan
report_attributes(struct scmi_msg * msg)145b4734308SPeng Fan static void report_attributes(struct scmi_msg *msg)
146b4734308SPeng Fan {
147b4734308SPeng Fan size_t agent_count = plat_scmi_clock_count(msg->agent_id);
148b4734308SPeng Fan struct scmi_protocol_attributes_p2a return_values = {
149b4734308SPeng Fan .status = SCMI_SUCCESS,
150b4734308SPeng Fan .attributes = SCMI_CLOCK_PROTOCOL_ATTRIBUTES(1U, agent_count),
151b4734308SPeng Fan };
152b4734308SPeng Fan
153b4734308SPeng Fan if (msg->in_size != 0) {
154b4734308SPeng Fan scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
155b4734308SPeng Fan return;
156b4734308SPeng Fan }
157b4734308SPeng Fan
158b4734308SPeng Fan scmi_write_response(msg, &return_values, sizeof(return_values));
159b4734308SPeng Fan }
160b4734308SPeng Fan
report_message_attributes(struct scmi_msg * msg)161b4734308SPeng Fan static void report_message_attributes(struct scmi_msg *msg)
162b4734308SPeng Fan {
163b4734308SPeng Fan struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in;
164b4734308SPeng Fan struct scmi_protocol_message_attributes_p2a return_values = {
165b4734308SPeng Fan .status = SCMI_SUCCESS,
166b4734308SPeng Fan /* For this protocol, attributes shall be zero */
167b4734308SPeng Fan .attributes = 0U,
168b4734308SPeng Fan };
169b4734308SPeng Fan
170b4734308SPeng Fan if (msg->in_size != sizeof(*in_args)) {
171b4734308SPeng Fan scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
172b4734308SPeng Fan return;
173b4734308SPeng Fan }
174b4734308SPeng Fan
175b4734308SPeng Fan if (!message_id_is_supported(in_args->message_id)) {
176b4734308SPeng Fan scmi_status_response(msg, SCMI_NOT_FOUND);
177b4734308SPeng Fan return;
178b4734308SPeng Fan }
179b4734308SPeng Fan
180b4734308SPeng Fan scmi_write_response(msg, &return_values, sizeof(return_values));
181b4734308SPeng Fan }
182b4734308SPeng Fan
scmi_clock_attributes(struct scmi_msg * msg)183b4734308SPeng Fan static void scmi_clock_attributes(struct scmi_msg *msg)
184b4734308SPeng Fan {
185b4734308SPeng Fan const struct scmi_clock_attributes_a2p *in_args = (void *)msg->in;
186b4734308SPeng Fan struct scmi_clock_attributes_p2a return_values = {
187b4734308SPeng Fan .status = SCMI_SUCCESS,
188b4734308SPeng Fan };
189b4734308SPeng Fan const char *name = NULL;
190b4734308SPeng Fan unsigned int clock_id = 0U;
191b4734308SPeng Fan
192b4734308SPeng Fan if (msg->in_size != sizeof(*in_args)) {
193b4734308SPeng Fan scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
194b4734308SPeng Fan return;
195b4734308SPeng Fan }
196b4734308SPeng Fan
197b4734308SPeng Fan clock_id = SPECULATION_SAFE_VALUE(in_args->clock_id);
198b4734308SPeng Fan
199b4734308SPeng Fan if (clock_id >= plat_scmi_clock_count(msg->agent_id)) {
20048ec8d33Sscaria scmi_status_response(msg, SCMI_NOT_FOUND);
201b4734308SPeng Fan return;
202b4734308SPeng Fan }
203b4734308SPeng Fan
204b4734308SPeng Fan
205b4734308SPeng Fan name = plat_scmi_clock_get_name(msg->agent_id, clock_id);
206b4734308SPeng Fan if (name == NULL) {
207b4734308SPeng Fan scmi_status_response(msg, SCMI_NOT_FOUND);
208b4734308SPeng Fan return;
209b4734308SPeng Fan }
210b4734308SPeng Fan
211b4734308SPeng Fan COPY_NAME_IDENTIFIER(return_values.clock_name, name);
212b4734308SPeng Fan
213b4734308SPeng Fan return_values.attributes = plat_scmi_clock_get_state(msg->agent_id,
214b4734308SPeng Fan clock_id);
215b4734308SPeng Fan
216*684952d1SKamlesh Gurudasani return_values.attributes |= BIT(SCMI_CLOCK_EXTENDED_CONFIG_SUPPORT_POS);
217*684952d1SKamlesh Gurudasani return_values.attributes |= BIT(SCMI_CLOCK_PARENT_IDENTIFIER_SUPPORT_POS);
218*684952d1SKamlesh Gurudasani
219*684952d1SKamlesh Gurudasani return_values.clock_enable_delay = plat_scmi_clock_get_enable_delay(msg->agent_id,
220*684952d1SKamlesh Gurudasani clock_id);
221*684952d1SKamlesh Gurudasani
222b4734308SPeng Fan scmi_write_response(msg, &return_values, sizeof(return_values));
223b4734308SPeng Fan }
224b4734308SPeng Fan
225*684952d1SKamlesh Gurudasani
226*684952d1SKamlesh Gurudasani
227*684952d1SKamlesh Gurudasani #define PARENTS_ARRAY_SIZE_MAX (SCMI_PLAYLOAD_MAX - \
228*684952d1SKamlesh Gurudasani sizeof(struct scmi_clock_possible_parents_get_p2a))
229*684952d1SKamlesh Gurudasani
230*684952d1SKamlesh Gurudasani /* Protocol limits the maximum number of parent clock identifiers that are
231*684952d1SKamlesh Gurudasani * remaining or returned by this call
232*684952d1SKamlesh Gurudasani */
233*684952d1SKamlesh Gurudasani #define PARENT_CLOCK_MAX_COUNT 255U
234*684952d1SKamlesh Gurudasani
235*684952d1SKamlesh Gurudasani #define PARENT_CLOCK_IDENTIFIER_SIZE sizeof(uint32_t)
236*684952d1SKamlesh Gurudasani
237*684952d1SKamlesh Gurudasani #define PARENTS_ARRAY_ELEMENTS_NUMBER_MAX ((uint32_t)(PARENTS_ARRAY_SIZE_MAX / \
238*684952d1SKamlesh Gurudasani PARENT_CLOCK_IDENTIFIER_SIZE))
239*684952d1SKamlesh Gurudasani
write_possible_parents_array_in_buffer(char * dest,unsigned int * possible_parents,size_t nb_elt)240*684952d1SKamlesh Gurudasani static void write_possible_parents_array_in_buffer(char *dest, unsigned int *possible_parents,
241*684952d1SKamlesh Gurudasani size_t nb_elt)
242*684952d1SKamlesh Gurudasani {
243*684952d1SKamlesh Gurudasani uint32_t *out = (uint32_t *)(uintptr_t)dest;
244*684952d1SKamlesh Gurudasani size_t n;
245*684952d1SKamlesh Gurudasani
246*684952d1SKamlesh Gurudasani ASSERT_SYM_PTR_ALIGN(out);
247*684952d1SKamlesh Gurudasani
248*684952d1SKamlesh Gurudasani for (n = 0U; n < nb_elt; n++) {
249*684952d1SKamlesh Gurudasani out[n] = (uint32_t)possible_parents[n];
250*684952d1SKamlesh Gurudasani }
251*684952d1SKamlesh Gurudasani }
252*684952d1SKamlesh Gurudasani
scmi_clock_possible_parents_get(struct scmi_msg * msg)253*684952d1SKamlesh Gurudasani static void scmi_clock_possible_parents_get(struct scmi_msg *msg)
254*684952d1SKamlesh Gurudasani {
255*684952d1SKamlesh Gurudasani const struct scmi_clock_possible_parents_get_a2p *in_args = (void *)msg->in;
256*684952d1SKamlesh Gurudasani struct scmi_clock_possible_parents_get_p2a p2a = {
257*684952d1SKamlesh Gurudasani .status = SCMI_SUCCESS,
258*684952d1SKamlesh Gurudasani .flags = 0,
259*684952d1SKamlesh Gurudasani };
260*684952d1SKamlesh Gurudasani
261*684952d1SKamlesh Gurudasani unsigned int clock_id = 0U;
262*684952d1SKamlesh Gurudasani size_t nb_possible_parents;
263*684952d1SKamlesh Gurudasani int32_t status;
264*684952d1SKamlesh Gurudasani
265*684952d1SKamlesh Gurudasani if (msg->in_size != sizeof(*in_args)) {
266*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
267*684952d1SKamlesh Gurudasani return;
268*684952d1SKamlesh Gurudasani }
269*684952d1SKamlesh Gurudasani
270*684952d1SKamlesh Gurudasani clock_id = SPECULATION_SAFE_VALUE(in_args->clock_id);
271*684952d1SKamlesh Gurudasani
272*684952d1SKamlesh Gurudasani if (clock_id >= plat_scmi_clock_count(msg->agent_id)) {
273*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_NOT_FOUND);
274*684952d1SKamlesh Gurudasani return;
275*684952d1SKamlesh Gurudasani }
276*684952d1SKamlesh Gurudasani /* Get number of possible parents */
277*684952d1SKamlesh Gurudasani status = plat_scmi_clock_get_possible_parents(msg->agent_id, clock_id, NULL,
278*684952d1SKamlesh Gurudasani &nb_possible_parents, 0);
279*684952d1SKamlesh Gurudasani if (status == SCMI_SUCCESS) {
280*684952d1SKamlesh Gurudasani
281*684952d1SKamlesh Gurudasani if (in_args->skip_parents >= nb_possible_parents) {
282*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
283*684952d1SKamlesh Gurudasani return;
284*684952d1SKamlesh Gurudasani }
285*684952d1SKamlesh Gurudasani
286*684952d1SKamlesh Gurudasani /* Currently 20 cells max, so it's affordable for the stack */
287*684952d1SKamlesh Gurudasani unsigned int plat_possible_parents[PARENTS_ARRAY_ELEMENTS_NUMBER_MAX];
288*684952d1SKamlesh Gurudasani size_t max_nb = MIN(PARENTS_ARRAY_ELEMENTS_NUMBER_MAX,
289*684952d1SKamlesh Gurudasani PARENT_CLOCK_MAX_COUNT);
290*684952d1SKamlesh Gurudasani size_t ret_nb = MIN(nb_possible_parents - in_args->skip_parents, max_nb);
291*684952d1SKamlesh Gurudasani size_t rem_nb = nb_possible_parents - in_args->skip_parents - ret_nb;
292*684952d1SKamlesh Gurudasani
293*684952d1SKamlesh Gurudasani rem_nb = MIN((uint32_t)rem_nb, PARENT_CLOCK_MAX_COUNT);
294*684952d1SKamlesh Gurudasani
295*684952d1SKamlesh Gurudasani status = plat_scmi_clock_get_possible_parents(msg->agent_id, clock_id,
296*684952d1SKamlesh Gurudasani plat_possible_parents, &ret_nb,
297*684952d1SKamlesh Gurudasani in_args->skip_parents);
298*684952d1SKamlesh Gurudasani if (status == SCMI_SUCCESS) {
299*684952d1SKamlesh Gurudasani ret_nb = MIN(ret_nb, max_nb);
300*684952d1SKamlesh Gurudasani write_possible_parents_array_in_buffer(msg->out + sizeof(p2a),
301*684952d1SKamlesh Gurudasani plat_possible_parents, ret_nb);
302*684952d1SKamlesh Gurudasani
303*684952d1SKamlesh Gurudasani p2a.flags = ((uint8_t)rem_nb) << 24;
304*684952d1SKamlesh Gurudasani p2a.flags |= (uint8_t)ret_nb;
305*684952d1SKamlesh Gurudasani p2a.status = SCMI_SUCCESS;
306*684952d1SKamlesh Gurudasani
307*684952d1SKamlesh Gurudasani memcpy(msg->out, &p2a, sizeof(p2a));
308*684952d1SKamlesh Gurudasani msg->out_size_out = sizeof(p2a) +
309*684952d1SKamlesh Gurudasani ret_nb * sizeof(unsigned int);
310*684952d1SKamlesh Gurudasani }
311*684952d1SKamlesh Gurudasani
312*684952d1SKamlesh Gurudasani } else {
313*684952d1SKamlesh Gurudasani /* Fallthrough generic exit sequence below with error status */
314*684952d1SKamlesh Gurudasani }
315*684952d1SKamlesh Gurudasani
316*684952d1SKamlesh Gurudasani if (status != SCMI_SUCCESS) {
317*684952d1SKamlesh Gurudasani scmi_status_response(msg, status);
318*684952d1SKamlesh Gurudasani } else {
319*684952d1SKamlesh Gurudasani /*
320*684952d1SKamlesh Gurudasani * Message payload is already written to msg->out, and
321*684952d1SKamlesh Gurudasani * msg->out_size_out updated.
322*684952d1SKamlesh Gurudasani */
323*684952d1SKamlesh Gurudasani }
324*684952d1SKamlesh Gurudasani }
325*684952d1SKamlesh Gurudasani
scmi_clock_parent_get(struct scmi_msg * msg)326*684952d1SKamlesh Gurudasani static void scmi_clock_parent_get(struct scmi_msg *msg)
327*684952d1SKamlesh Gurudasani {
328*684952d1SKamlesh Gurudasani const struct scmi_clock_parent_get_a2p *in_args = (void *)msg->in;
329*684952d1SKamlesh Gurudasani
330*684952d1SKamlesh Gurudasani struct scmi_clock_parent_get_p2a return_values = {
331*684952d1SKamlesh Gurudasani .status = SCMI_SUCCESS,
332*684952d1SKamlesh Gurudasani };
333*684952d1SKamlesh Gurudasani unsigned int clock_id = 0U;
334*684952d1SKamlesh Gurudasani
335*684952d1SKamlesh Gurudasani if (msg->in_size != sizeof(*in_args)) {
336*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
337*684952d1SKamlesh Gurudasani return;
338*684952d1SKamlesh Gurudasani }
339*684952d1SKamlesh Gurudasani
340*684952d1SKamlesh Gurudasani clock_id = SPECULATION_SAFE_VALUE(in_args->clock_id);
341*684952d1SKamlesh Gurudasani
342*684952d1SKamlesh Gurudasani if (clock_id >= plat_scmi_clock_count(msg->agent_id)) {
343*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_NOT_FOUND);
344*684952d1SKamlesh Gurudasani return;
345*684952d1SKamlesh Gurudasani }
346*684952d1SKamlesh Gurudasani
347*684952d1SKamlesh Gurudasani return_values.status = plat_scmi_clock_get_parent(msg->agent_id, clock_id,
348*684952d1SKamlesh Gurudasani &return_values.parent_id);
349*684952d1SKamlesh Gurudasani
350*684952d1SKamlesh Gurudasani scmi_write_response(msg, &return_values, sizeof(return_values));
351*684952d1SKamlesh Gurudasani }
352*684952d1SKamlesh Gurudasani
scmi_clock_parent_set(struct scmi_msg * msg)353*684952d1SKamlesh Gurudasani static void scmi_clock_parent_set(struct scmi_msg *msg)
354*684952d1SKamlesh Gurudasani {
355*684952d1SKamlesh Gurudasani const struct scmi_clock_parent_set_a2p *in_args = (void *)msg->in;
356*684952d1SKamlesh Gurudasani
357*684952d1SKamlesh Gurudasani int32_t status = 0;
358*684952d1SKamlesh Gurudasani unsigned int clock_id = 0U;
359*684952d1SKamlesh Gurudasani unsigned int parent_id = 0U;
360*684952d1SKamlesh Gurudasani unsigned int clock_count = 0U;
361*684952d1SKamlesh Gurudasani
362*684952d1SKamlesh Gurudasani if (msg->in_size != sizeof(*in_args)) {
363*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
364*684952d1SKamlesh Gurudasani return;
365*684952d1SKamlesh Gurudasani }
366*684952d1SKamlesh Gurudasani
367*684952d1SKamlesh Gurudasani clock_id = SPECULATION_SAFE_VALUE(in_args->clock_id);
368*684952d1SKamlesh Gurudasani parent_id = SPECULATION_SAFE_VALUE(in_args->parent_id);
369*684952d1SKamlesh Gurudasani
370*684952d1SKamlesh Gurudasani clock_count = plat_scmi_clock_count(msg->agent_id);
371*684952d1SKamlesh Gurudasani if (clock_id >= clock_count || parent_id >= clock_count) {
372*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_NOT_FOUND);
373*684952d1SKamlesh Gurudasani return;
374*684952d1SKamlesh Gurudasani }
375*684952d1SKamlesh Gurudasani
376*684952d1SKamlesh Gurudasani status = plat_scmi_clock_set_parent(msg->agent_id, clock_id, parent_id);
377*684952d1SKamlesh Gurudasani
378*684952d1SKamlesh Gurudasani scmi_status_response(msg, status);
379*684952d1SKamlesh Gurudasani }
380*684952d1SKamlesh Gurudasani
scmi_clock_rate_get(struct scmi_msg * msg)381b4734308SPeng Fan static void scmi_clock_rate_get(struct scmi_msg *msg)
382b4734308SPeng Fan {
383b4734308SPeng Fan const struct scmi_clock_rate_get_a2p *in_args = (void *)msg->in;
384b4734308SPeng Fan unsigned long rate = 0U;
385b4734308SPeng Fan struct scmi_clock_rate_get_p2a return_values = {
386b4734308SPeng Fan .status = SCMI_SUCCESS,
387b4734308SPeng Fan };
388b4734308SPeng Fan unsigned int clock_id = 0U;
389b4734308SPeng Fan
390b4734308SPeng Fan if (msg->in_size != sizeof(*in_args)) {
391b4734308SPeng Fan scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
392b4734308SPeng Fan return;
393b4734308SPeng Fan }
394b4734308SPeng Fan
395b4734308SPeng Fan clock_id = SPECULATION_SAFE_VALUE(in_args->clock_id);
396b4734308SPeng Fan
397b4734308SPeng Fan if (clock_id >= plat_scmi_clock_count(msg->agent_id)) {
39848ec8d33Sscaria scmi_status_response(msg, SCMI_NOT_FOUND);
399b4734308SPeng Fan return;
400b4734308SPeng Fan }
401b4734308SPeng Fan
402b4734308SPeng Fan rate = plat_scmi_clock_get_rate(msg->agent_id, clock_id);
403b4734308SPeng Fan
404b4734308SPeng Fan return_values.rate[0] = (uint32_t)rate;
405b4734308SPeng Fan return_values.rate[1] = (uint32_t)((uint64_t)rate >> 32);
406b4734308SPeng Fan
407b4734308SPeng Fan scmi_write_response(msg, &return_values, sizeof(return_values));
408b4734308SPeng Fan }
409b4734308SPeng Fan
scmi_clock_rate_set(struct scmi_msg * msg)410b4734308SPeng Fan static void scmi_clock_rate_set(struct scmi_msg *msg)
411b4734308SPeng Fan {
412b4734308SPeng Fan const struct scmi_clock_rate_set_a2p *in_args = (void *)msg->in;
413b4734308SPeng Fan unsigned long rate = 0U;
414b4734308SPeng Fan int32_t status = 0;
415b4734308SPeng Fan unsigned int clock_id = 0U;
416b4734308SPeng Fan
417b4734308SPeng Fan if (msg->in_size != sizeof(*in_args)) {
418b4734308SPeng Fan scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
419b4734308SPeng Fan return;
420b4734308SPeng Fan }
421b4734308SPeng Fan
422b4734308SPeng Fan clock_id = SPECULATION_SAFE_VALUE(in_args->clock_id);
423b4734308SPeng Fan
424b4734308SPeng Fan if (clock_id >= plat_scmi_clock_count(msg->agent_id)) {
42548ec8d33Sscaria scmi_status_response(msg, SCMI_NOT_FOUND);
426b4734308SPeng Fan return;
427b4734308SPeng Fan }
428b4734308SPeng Fan
429b4734308SPeng Fan rate = (unsigned long)(((uint64_t)in_args->rate[1] << 32) |
430b4734308SPeng Fan in_args->rate[0]);
431b4734308SPeng Fan
432b4734308SPeng Fan status = plat_scmi_clock_set_rate(msg->agent_id, clock_id, rate);
433b4734308SPeng Fan
434b4734308SPeng Fan scmi_status_response(msg, status);
435b4734308SPeng Fan }
436b4734308SPeng Fan
scmi_clock_config_get(struct scmi_msg * msg)437*684952d1SKamlesh Gurudasani static void scmi_clock_config_get(struct scmi_msg *msg)
438*684952d1SKamlesh Gurudasani {
439*684952d1SKamlesh Gurudasani const struct scmi_clock_config_get_a2p *in_args = (void *)msg->in;
440*684952d1SKamlesh Gurudasani struct scmi_clock_config_get_p2a p2a = {
441*684952d1SKamlesh Gurudasani .status = SCMI_SUCCESS,
442*684952d1SKamlesh Gurudasani };
443*684952d1SKamlesh Gurudasani unsigned int extended_config_type = 0U;
444*684952d1SKamlesh Gurudasani unsigned int clock_id = 0U;
445*684952d1SKamlesh Gurudasani
446*684952d1SKamlesh Gurudasani if (msg->in_size != sizeof(*in_args)) {
447*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
448*684952d1SKamlesh Gurudasani return;
449*684952d1SKamlesh Gurudasani }
450*684952d1SKamlesh Gurudasani
451*684952d1SKamlesh Gurudasani clock_id = SPECULATION_SAFE_VALUE(in_args->clock_id);
452*684952d1SKamlesh Gurudasani
453*684952d1SKamlesh Gurudasani if (clock_id >= plat_scmi_clock_count(msg->agent_id)) {
454*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_NOT_FOUND);
455*684952d1SKamlesh Gurudasani return;
456*684952d1SKamlesh Gurudasani }
457*684952d1SKamlesh Gurudasani
458*684952d1SKamlesh Gurudasani p2a.config = plat_scmi_clock_get_state(msg->agent_id, clock_id);
459*684952d1SKamlesh Gurudasani
460*684952d1SKamlesh Gurudasani extended_config_type = in_args->flags & SCMI_CLOCK_EXTENDED_CONFIG_GET_TYPE_MASK;
461*684952d1SKamlesh Gurudasani if (extended_config_type != 0U) {
462*684952d1SKamlesh Gurudasani p2a.status = plat_scmi_clock_get_extended_config(msg->agent_id,
463*684952d1SKamlesh Gurudasani clock_id,
464*684952d1SKamlesh Gurudasani extended_config_type,
465*684952d1SKamlesh Gurudasani &p2a.extended_config_val);
466*684952d1SKamlesh Gurudasani }
467*684952d1SKamlesh Gurudasani
468*684952d1SKamlesh Gurudasani scmi_write_response(msg, &p2a, sizeof(p2a));
469*684952d1SKamlesh Gurudasani }
470*684952d1SKamlesh Gurudasani
scmi_clock_config_set(struct scmi_msg * msg)471b4734308SPeng Fan static void scmi_clock_config_set(struct scmi_msg *msg)
472b4734308SPeng Fan {
473b4734308SPeng Fan const struct scmi_clock_config_set_a2p *in_args = (void *)msg->in;
474b4734308SPeng Fan int32_t status = SCMI_GENERIC_ERROR;
475*684952d1SKamlesh Gurudasani uint8_t enable = 0U;
476b4734308SPeng Fan unsigned int clock_id = 0U;
477*684952d1SKamlesh Gurudasani uint8_t extended_config_type = 0U;
478b4734308SPeng Fan
479b4734308SPeng Fan if (msg->in_size != sizeof(*in_args)) {
480b4734308SPeng Fan scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
481b4734308SPeng Fan return;
482b4734308SPeng Fan }
483b4734308SPeng Fan
484b4734308SPeng Fan clock_id = SPECULATION_SAFE_VALUE(in_args->clock_id);
485b4734308SPeng Fan
486b4734308SPeng Fan if (clock_id >= plat_scmi_clock_count(msg->agent_id)) {
48748ec8d33Sscaria scmi_status_response(msg, SCMI_NOT_FOUND);
488b4734308SPeng Fan return;
489b4734308SPeng Fan }
490b4734308SPeng Fan
491b4734308SPeng Fan enable = in_args->attributes & SCMI_CLOCK_CONFIG_SET_ENABLE_MASK;
492b4734308SPeng Fan
493*684952d1SKamlesh Gurudasani extended_config_type = in_args->attributes & SCMI_CLOCK_EXTENDED_CONFIG_SET_TYPE_MASK;
494*684952d1SKamlesh Gurudasani
495*684952d1SKamlesh Gurudasani if ((extended_config_type == 0U)
496*684952d1SKamlesh Gurudasani && (enable == SCMI_CLOCK_CONFIG_SET_UNCHANGED_STATE)) {
497*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
498*684952d1SKamlesh Gurudasani return;
499*684952d1SKamlesh Gurudasani }
500*684952d1SKamlesh Gurudasani
501*684952d1SKamlesh Gurudasani if (enable == SCMI_CLOCK_CONFIG_SET_RESERVED_STATE) {
502*684952d1SKamlesh Gurudasani scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
503*684952d1SKamlesh Gurudasani return;
504*684952d1SKamlesh Gurudasani }
505*684952d1SKamlesh Gurudasani
506*684952d1SKamlesh Gurudasani if (enable != SCMI_CLOCK_CONFIG_SET_UNCHANGED_STATE) {
507*684952d1SKamlesh Gurudasani status = plat_scmi_clock_set_state(msg->agent_id, clock_id, (bool)enable);
508*684952d1SKamlesh Gurudasani if (status != SCMI_SUCCESS) {
509*684952d1SKamlesh Gurudasani scmi_status_response(msg, status);
510*684952d1SKamlesh Gurudasani return;
511*684952d1SKamlesh Gurudasani }
512*684952d1SKamlesh Gurudasani }
513*684952d1SKamlesh Gurudasani
514*684952d1SKamlesh Gurudasani if (extended_config_type != 0U) {
515*684952d1SKamlesh Gurudasani status = plat_scmi_clock_set_extended_config(msg->agent_id, clock_id,
516*684952d1SKamlesh Gurudasani extended_config_type,
517*684952d1SKamlesh Gurudasani in_args->extended_config_val);
518*684952d1SKamlesh Gurudasani }
519b4734308SPeng Fan
520b4734308SPeng Fan scmi_status_response(msg, status);
521b4734308SPeng Fan }
522b4734308SPeng Fan
523b4734308SPeng Fan #define RATES_ARRAY_SIZE_MAX (SCMI_PLAYLOAD_MAX - \
524b4734308SPeng Fan sizeof(struct scmi_clock_describe_rates_p2a))
525b4734308SPeng Fan
526b4734308SPeng Fan #define SCMI_RATES_BY_ARRAY(_nb_rates, _rem_rates) \
527b4734308SPeng Fan SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS((_nb_rates), \
528b4734308SPeng Fan SCMI_CLOCK_RATE_FORMAT_LIST, \
529b4734308SPeng Fan (_rem_rates))
530b4734308SPeng Fan #define SCMI_RATES_BY_STEP \
531b4734308SPeng Fan SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS(3U, \
532b4734308SPeng Fan SCMI_CLOCK_RATE_FORMAT_RANGE, \
533b4734308SPeng Fan 0U)
534b4734308SPeng Fan
535b4734308SPeng Fan #define RATE_DESC_SIZE sizeof(struct scmi_clock_rate)
536b4734308SPeng Fan
write_rate_desc_array_in_buffer(char * dest,unsigned long * rates,size_t nb_elt)537b4734308SPeng Fan static void write_rate_desc_array_in_buffer(char *dest, unsigned long *rates,
538b4734308SPeng Fan size_t nb_elt)
539b4734308SPeng Fan {
540b4734308SPeng Fan uint32_t *out = (uint32_t *)(uintptr_t)dest;
541b4734308SPeng Fan size_t n;
542b4734308SPeng Fan
543b4734308SPeng Fan ASSERT_SYM_PTR_ALIGN(out);
544b4734308SPeng Fan
545b4734308SPeng Fan for (n = 0U; n < nb_elt; n++) {
546b4734308SPeng Fan out[2 * n] = (uint32_t)rates[n];
547b4734308SPeng Fan out[2 * n + 1] = (uint32_t)((uint64_t)rates[n] >> 32);
548b4734308SPeng Fan }
549b4734308SPeng Fan }
550b4734308SPeng Fan
scmi_clock_describe_rates(struct scmi_msg * msg)551b4734308SPeng Fan static void scmi_clock_describe_rates(struct scmi_msg *msg)
552b4734308SPeng Fan {
553b4734308SPeng Fan const struct scmi_clock_describe_rates_a2p *in_args = (void *)msg->in;
554b4734308SPeng Fan struct scmi_clock_describe_rates_p2a p2a = {
555b4734308SPeng Fan .status = SCMI_SUCCESS,
556b4734308SPeng Fan };
557b4734308SPeng Fan size_t nb_rates;
558b4734308SPeng Fan int32_t status;
559b4734308SPeng Fan unsigned int clock_id;
560b4734308SPeng Fan
561b4734308SPeng Fan if (msg->in_size != sizeof(*in_args)) {
562b4734308SPeng Fan scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
563b4734308SPeng Fan return;
564b4734308SPeng Fan }
565b4734308SPeng Fan
566b4734308SPeng Fan clock_id = SPECULATION_SAFE_VALUE(in_args->clock_id);
567b4734308SPeng Fan
568b4734308SPeng Fan if (clock_id >= plat_scmi_clock_count(msg->agent_id)) {
56948ec8d33Sscaria scmi_status_response(msg, SCMI_NOT_FOUND);
570b4734308SPeng Fan return;
571b4734308SPeng Fan }
572b4734308SPeng Fan
573b4734308SPeng Fan /* Platform may support array rate description */
574b4734308SPeng Fan status = plat_scmi_clock_rates_array(msg->agent_id, clock_id, NULL,
575ca9d6edcSXiaoDong Huang &nb_rates, 0);
576b4734308SPeng Fan if (status == SCMI_SUCCESS) {
577b4734308SPeng Fan /* Currently 12 cells mex, so it's affordable for the stack */
578b4734308SPeng Fan unsigned long plat_rates[RATES_ARRAY_SIZE_MAX / RATE_DESC_SIZE];
579b4734308SPeng Fan size_t max_nb = RATES_ARRAY_SIZE_MAX / RATE_DESC_SIZE;
580b4734308SPeng Fan size_t ret_nb = MIN(nb_rates - in_args->rate_index, max_nb);
581b4734308SPeng Fan size_t rem_nb = nb_rates - in_args->rate_index - ret_nb;
582b4734308SPeng Fan
583b4734308SPeng Fan status = plat_scmi_clock_rates_array(msg->agent_id, clock_id,
584ca9d6edcSXiaoDong Huang plat_rates, &ret_nb,
585ca9d6edcSXiaoDong Huang in_args->rate_index);
586b4734308SPeng Fan if (status == SCMI_SUCCESS) {
587b4734308SPeng Fan write_rate_desc_array_in_buffer(msg->out + sizeof(p2a),
588b4734308SPeng Fan plat_rates, ret_nb);
589b4734308SPeng Fan
590b4734308SPeng Fan p2a.num_rates_flags = SCMI_RATES_BY_ARRAY(ret_nb,
591b4734308SPeng Fan rem_nb);
592b4734308SPeng Fan p2a.status = SCMI_SUCCESS;
593b4734308SPeng Fan
594b4734308SPeng Fan memcpy(msg->out, &p2a, sizeof(p2a));
595b4734308SPeng Fan msg->out_size_out = sizeof(p2a) +
596b4734308SPeng Fan ret_nb * RATE_DESC_SIZE;
597b4734308SPeng Fan }
598b4734308SPeng Fan } else if (status == SCMI_NOT_SUPPORTED) {
599b4734308SPeng Fan unsigned long triplet[3] = { 0U, 0U, 0U };
600b4734308SPeng Fan
601b4734308SPeng Fan /* Platform may support min§max/step triplet description */
602b4734308SPeng Fan status = plat_scmi_clock_rates_by_step(msg->agent_id, clock_id,
603b4734308SPeng Fan triplet);
604b4734308SPeng Fan if (status == SCMI_SUCCESS) {
605b4734308SPeng Fan write_rate_desc_array_in_buffer(msg->out + sizeof(p2a),
606b4734308SPeng Fan triplet, 3U);
607b4734308SPeng Fan
608b4734308SPeng Fan p2a.num_rates_flags = SCMI_RATES_BY_STEP;
609b4734308SPeng Fan p2a.status = SCMI_SUCCESS;
610b4734308SPeng Fan
611b4734308SPeng Fan memcpy(msg->out, &p2a, sizeof(p2a));
612b4734308SPeng Fan msg->out_size_out = sizeof(p2a) + (3U * RATE_DESC_SIZE);
613b4734308SPeng Fan }
614b4734308SPeng Fan } else {
615b4734308SPeng Fan /* Fallthrough generic exit sequence below with error status */
616b4734308SPeng Fan }
617b4734308SPeng Fan
618b4734308SPeng Fan if (status != SCMI_SUCCESS) {
619b4734308SPeng Fan scmi_status_response(msg, status);
620b4734308SPeng Fan } else {
621b4734308SPeng Fan /*
6221b491eeaSElyes Haouas * Message payload is already written to msg->out, and
623b4734308SPeng Fan * msg->out_size_out updated.
624b4734308SPeng Fan */
625b4734308SPeng Fan }
626b4734308SPeng Fan }
627b4734308SPeng Fan
628b4734308SPeng Fan static const scmi_msg_handler_t scmi_clock_handler_table[] = {
629b4734308SPeng Fan [SCMI_PROTOCOL_VERSION] = report_version,
630b4734308SPeng Fan [SCMI_PROTOCOL_ATTRIBUTES] = report_attributes,
631b4734308SPeng Fan [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes,
632b4734308SPeng Fan [SCMI_CLOCK_ATTRIBUTES] = scmi_clock_attributes,
633b4734308SPeng Fan [SCMI_CLOCK_DESCRIBE_RATES] = scmi_clock_describe_rates,
634b4734308SPeng Fan [SCMI_CLOCK_RATE_SET] = scmi_clock_rate_set,
635b4734308SPeng Fan [SCMI_CLOCK_RATE_GET] = scmi_clock_rate_get,
636b4734308SPeng Fan [SCMI_CLOCK_CONFIG_SET] = scmi_clock_config_set,
637*684952d1SKamlesh Gurudasani [SCMI_CLOCK_CONFIG_GET] = scmi_clock_config_get,
638*684952d1SKamlesh Gurudasani [SCMI_CLOCK_POSSIBLE_PARENTS_GET] = scmi_clock_possible_parents_get,
639*684952d1SKamlesh Gurudasani [SCMI_CLOCK_PARENT_SET] = scmi_clock_parent_set,
640*684952d1SKamlesh Gurudasani [SCMI_CLOCK_PARENT_GET] = scmi_clock_parent_get,
641b4734308SPeng Fan };
642b4734308SPeng Fan
message_id_is_supported(unsigned int message_id)6432355ebffSSchspa Shi static bool message_id_is_supported(unsigned int message_id)
644b4734308SPeng Fan {
645b4734308SPeng Fan return (message_id < ARRAY_SIZE(scmi_clock_handler_table)) &&
646b4734308SPeng Fan (scmi_clock_handler_table[message_id] != NULL);
647b4734308SPeng Fan }
648b4734308SPeng Fan
scmi_msg_get_clock_handler(struct scmi_msg * msg)649b4734308SPeng Fan scmi_msg_handler_t scmi_msg_get_clock_handler(struct scmi_msg *msg)
650b4734308SPeng Fan {
651b4734308SPeng Fan const size_t array_size = ARRAY_SIZE(scmi_clock_handler_table);
652b4734308SPeng Fan unsigned int message_id = SPECULATION_SAFE_VALUE(msg->message_id);
653b4734308SPeng Fan
654b4734308SPeng Fan if (message_id >= array_size) {
655b4734308SPeng Fan VERBOSE("Clock handle not found %u", msg->message_id);
656b4734308SPeng Fan return NULL;
657b4734308SPeng Fan }
658b4734308SPeng Fan
659b4734308SPeng Fan return scmi_clock_handler_table[message_id];
660b4734308SPeng Fan }
661