xref: /optee_os/core/drivers/scmi-msg/clock.c (revision a7a9e3ba71dd908aafdc4c5ed9b29b15faa9692d)
1*a7a9e3baSEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
2*a7a9e3baSEtienne Carriere /*
3*a7a9e3baSEtienne Carriere  * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
4*a7a9e3baSEtienne Carriere  * Copyright (c) 2019, Linaro Limited
5*a7a9e3baSEtienne Carriere  */
6*a7a9e3baSEtienne Carriere #include <assert.h>
7*a7a9e3baSEtienne Carriere #include <confine_array_index.h>
8*a7a9e3baSEtienne Carriere #include <drivers/scmi-msg.h>
9*a7a9e3baSEtienne Carriere #include <drivers/scmi.h>
10*a7a9e3baSEtienne Carriere #include <string.h>
11*a7a9e3baSEtienne Carriere #include <util.h>
12*a7a9e3baSEtienne Carriere 
13*a7a9e3baSEtienne Carriere #include "clock.h"
14*a7a9e3baSEtienne Carriere #include "common.h"
15*a7a9e3baSEtienne Carriere 
16*a7a9e3baSEtienne Carriere static bool message_id_is_supported(unsigned int message_id);
17*a7a9e3baSEtienne Carriere 
18*a7a9e3baSEtienne Carriere size_t __weak plat_scmi_clock_count(unsigned int agent_id __unused)
19*a7a9e3baSEtienne Carriere {
20*a7a9e3baSEtienne Carriere 	return 0;
21*a7a9e3baSEtienne Carriere }
22*a7a9e3baSEtienne Carriere 
23*a7a9e3baSEtienne Carriere const char __weak *plat_scmi_clock_get_name(unsigned int agent_id __unused,
24*a7a9e3baSEtienne Carriere 					    unsigned int scmi_id __unused)
25*a7a9e3baSEtienne Carriere {
26*a7a9e3baSEtienne Carriere 	return NULL;
27*a7a9e3baSEtienne Carriere }
28*a7a9e3baSEtienne Carriere 
29*a7a9e3baSEtienne Carriere int32_t __weak plat_scmi_clock_rates_array(unsigned int agent_id __unused,
30*a7a9e3baSEtienne Carriere 					   unsigned int scmi_id __unused,
31*a7a9e3baSEtienne Carriere 					   unsigned long *rates __unused,
32*a7a9e3baSEtienne Carriere 					   size_t *nb_elts __unused)
33*a7a9e3baSEtienne Carriere {
34*a7a9e3baSEtienne Carriere 	return SCMI_NOT_SUPPORTED;
35*a7a9e3baSEtienne Carriere }
36*a7a9e3baSEtienne Carriere 
37*a7a9e3baSEtienne Carriere int32_t __weak plat_scmi_clock_rates_by_step(unsigned int agent_id __unused,
38*a7a9e3baSEtienne Carriere 					     unsigned int scmi_id __unused,
39*a7a9e3baSEtienne Carriere 					     unsigned long *steps __unused)
40*a7a9e3baSEtienne Carriere {
41*a7a9e3baSEtienne Carriere 	return SCMI_NOT_SUPPORTED;
42*a7a9e3baSEtienne Carriere }
43*a7a9e3baSEtienne Carriere 
44*a7a9e3baSEtienne Carriere unsigned long __weak plat_scmi_clock_get_rate(unsigned int agent_id __unused,
45*a7a9e3baSEtienne Carriere 					      unsigned int scmi_id __unused)
46*a7a9e3baSEtienne Carriere {
47*a7a9e3baSEtienne Carriere 	return 0;
48*a7a9e3baSEtienne Carriere }
49*a7a9e3baSEtienne Carriere 
50*a7a9e3baSEtienne Carriere int32_t __weak plat_scmi_clock_set_rate(unsigned int agent_id __unused,
51*a7a9e3baSEtienne Carriere 					unsigned int scmi_id __unused,
52*a7a9e3baSEtienne Carriere 					unsigned long rate __unused)
53*a7a9e3baSEtienne Carriere {
54*a7a9e3baSEtienne Carriere 	return SCMI_NOT_SUPPORTED;
55*a7a9e3baSEtienne Carriere }
56*a7a9e3baSEtienne Carriere 
57*a7a9e3baSEtienne Carriere int32_t __weak plat_scmi_clock_get_state(unsigned int agent_id __unused,
58*a7a9e3baSEtienne Carriere 					 unsigned int scmi_id __unused)
59*a7a9e3baSEtienne Carriere {
60*a7a9e3baSEtienne Carriere 	return SCMI_NOT_SUPPORTED;
61*a7a9e3baSEtienne Carriere }
62*a7a9e3baSEtienne Carriere 
63*a7a9e3baSEtienne Carriere int32_t __weak plat_scmi_clock_set_state(unsigned int agent_id __unused,
64*a7a9e3baSEtienne Carriere 					 unsigned int scmi_id __unused,
65*a7a9e3baSEtienne Carriere 					 bool enable_not_disable __unused)
66*a7a9e3baSEtienne Carriere {
67*a7a9e3baSEtienne Carriere 	return SCMI_NOT_SUPPORTED;
68*a7a9e3baSEtienne Carriere }
69*a7a9e3baSEtienne Carriere 
70*a7a9e3baSEtienne Carriere static void report_version(struct scmi_msg *msg)
71*a7a9e3baSEtienne Carriere {
72*a7a9e3baSEtienne Carriere 	struct scmi_protocol_version_p2a return_values = {
73*a7a9e3baSEtienne Carriere 		.status = SCMI_SUCCESS,
74*a7a9e3baSEtienne Carriere 		.version = SCMI_PROTOCOL_VERSION_CLOCK,
75*a7a9e3baSEtienne Carriere 	};
76*a7a9e3baSEtienne Carriere 
77*a7a9e3baSEtienne Carriere 	if (msg->in_size) {
78*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
79*a7a9e3baSEtienne Carriere 		return;
80*a7a9e3baSEtienne Carriere 	}
81*a7a9e3baSEtienne Carriere 
82*a7a9e3baSEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
83*a7a9e3baSEtienne Carriere }
84*a7a9e3baSEtienne Carriere 
85*a7a9e3baSEtienne Carriere static void report_attributes(struct scmi_msg *msg)
86*a7a9e3baSEtienne Carriere {
87*a7a9e3baSEtienne Carriere 	size_t agent_count = plat_scmi_clock_count(msg->agent_id);
88*a7a9e3baSEtienne Carriere 	struct scmi_protocol_attributes_p2a return_values = {
89*a7a9e3baSEtienne Carriere 		.status = SCMI_SUCCESS,
90*a7a9e3baSEtienne Carriere 		.attributes = SCMI_CLOCK_PROTOCOL_ATTRIBUTES(1, agent_count),
91*a7a9e3baSEtienne Carriere 	};
92*a7a9e3baSEtienne Carriere 
93*a7a9e3baSEtienne Carriere 	if (msg->in_size) {
94*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
95*a7a9e3baSEtienne Carriere 		return;
96*a7a9e3baSEtienne Carriere 	}
97*a7a9e3baSEtienne Carriere 
98*a7a9e3baSEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
99*a7a9e3baSEtienne Carriere }
100*a7a9e3baSEtienne Carriere 
101*a7a9e3baSEtienne Carriere static void report_message_attributes(struct scmi_msg *msg)
102*a7a9e3baSEtienne Carriere {
103*a7a9e3baSEtienne Carriere 	struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in;
104*a7a9e3baSEtienne Carriere 	struct scmi_protocol_message_attributes_p2a return_values = {
105*a7a9e3baSEtienne Carriere 		.status = SCMI_SUCCESS,
106*a7a9e3baSEtienne Carriere 		/* For this protocol, attributes shall be zero */
107*a7a9e3baSEtienne Carriere 		.attributes = 0,
108*a7a9e3baSEtienne Carriere 	};
109*a7a9e3baSEtienne Carriere 
110*a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
111*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
112*a7a9e3baSEtienne Carriere 		return;
113*a7a9e3baSEtienne Carriere 	}
114*a7a9e3baSEtienne Carriere 
115*a7a9e3baSEtienne Carriere 	if (!message_id_is_supported(in_args->message_id)) {
116*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_NOT_FOUND);
117*a7a9e3baSEtienne Carriere 		return;
118*a7a9e3baSEtienne Carriere 	}
119*a7a9e3baSEtienne Carriere 
120*a7a9e3baSEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
121*a7a9e3baSEtienne Carriere }
122*a7a9e3baSEtienne Carriere 
123*a7a9e3baSEtienne Carriere static void scmi_clock_attributes(struct scmi_msg *msg)
124*a7a9e3baSEtienne Carriere {
125*a7a9e3baSEtienne Carriere 	const struct scmi_clock_attributes_a2p *in_args = (void *)msg->in;
126*a7a9e3baSEtienne Carriere 	struct scmi_clock_attributes_p2a return_values = {
127*a7a9e3baSEtienne Carriere 		.status = SCMI_SUCCESS,
128*a7a9e3baSEtienne Carriere 	};
129*a7a9e3baSEtienne Carriere 	const char *name = NULL;
130*a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
131*a7a9e3baSEtienne Carriere 
132*a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
133*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
134*a7a9e3baSEtienne Carriere 		return;
135*a7a9e3baSEtienne Carriere 	}
136*a7a9e3baSEtienne Carriere 
137*a7a9e3baSEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->agent_id)) {
138*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
139*a7a9e3baSEtienne Carriere 		return;
140*a7a9e3baSEtienne Carriere 	}
141*a7a9e3baSEtienne Carriere 
142*a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
143*a7a9e3baSEtienne Carriere 				       plat_scmi_clock_count(msg->agent_id));
144*a7a9e3baSEtienne Carriere 
145*a7a9e3baSEtienne Carriere 	name = plat_scmi_clock_get_name(msg->agent_id, clock_id);
146*a7a9e3baSEtienne Carriere 	if (!name) {
147*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_NOT_FOUND);
148*a7a9e3baSEtienne Carriere 		return;
149*a7a9e3baSEtienne Carriere 	}
150*a7a9e3baSEtienne Carriere 
151*a7a9e3baSEtienne Carriere 	COPY_NAME_IDENTIFIER(return_values.clock_name, name);
152*a7a9e3baSEtienne Carriere 
153*a7a9e3baSEtienne Carriere 	return_values.attributes = plat_scmi_clock_get_state(msg->agent_id,
154*a7a9e3baSEtienne Carriere 							     clock_id);
155*a7a9e3baSEtienne Carriere 
156*a7a9e3baSEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
157*a7a9e3baSEtienne Carriere }
158*a7a9e3baSEtienne Carriere 
159*a7a9e3baSEtienne Carriere static void scmi_clock_rate_get(struct scmi_msg *msg)
160*a7a9e3baSEtienne Carriere {
161*a7a9e3baSEtienne Carriere 	const struct scmi_clock_rate_get_a2p *in_args = (void *)msg->in;
162*a7a9e3baSEtienne Carriere 	unsigned long rate = 0;
163*a7a9e3baSEtienne Carriere 	struct scmi_clock_rate_get_p2a return_values = { };
164*a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
165*a7a9e3baSEtienne Carriere 
166*a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
167*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
168*a7a9e3baSEtienne Carriere 		return;
169*a7a9e3baSEtienne Carriere 	}
170*a7a9e3baSEtienne Carriere 
171*a7a9e3baSEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->agent_id)) {
172*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
173*a7a9e3baSEtienne Carriere 		return;
174*a7a9e3baSEtienne Carriere 	}
175*a7a9e3baSEtienne Carriere 
176*a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
177*a7a9e3baSEtienne Carriere 				       plat_scmi_clock_count(msg->agent_id));
178*a7a9e3baSEtienne Carriere 
179*a7a9e3baSEtienne Carriere 	rate = plat_scmi_clock_get_rate(msg->agent_id, clock_id);
180*a7a9e3baSEtienne Carriere 
181*a7a9e3baSEtienne Carriere 	reg_pair_from_64(rate, return_values.rate + 1, return_values.rate);
182*a7a9e3baSEtienne Carriere 
183*a7a9e3baSEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
184*a7a9e3baSEtienne Carriere }
185*a7a9e3baSEtienne Carriere 
186*a7a9e3baSEtienne Carriere static void scmi_clock_rate_set(struct scmi_msg *msg)
187*a7a9e3baSEtienne Carriere {
188*a7a9e3baSEtienne Carriere 	const struct scmi_clock_rate_set_a2p *in_args = (void *)msg->in;
189*a7a9e3baSEtienne Carriere 	uint64_t rate_64 = 0;
190*a7a9e3baSEtienne Carriere 	unsigned long rate = 0;
191*a7a9e3baSEtienne Carriere 	int32_t status = 0;
192*a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
193*a7a9e3baSEtienne Carriere 
194*a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
195*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
196*a7a9e3baSEtienne Carriere 		return;
197*a7a9e3baSEtienne Carriere 	}
198*a7a9e3baSEtienne Carriere 
199*a7a9e3baSEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->agent_id)) {
200*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
201*a7a9e3baSEtienne Carriere 		return;
202*a7a9e3baSEtienne Carriere 	}
203*a7a9e3baSEtienne Carriere 
204*a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
205*a7a9e3baSEtienne Carriere 				       plat_scmi_clock_count(msg->agent_id));
206*a7a9e3baSEtienne Carriere 
207*a7a9e3baSEtienne Carriere 	rate_64 = reg_pair_to_64(in_args->rate[1], in_args->rate[0]);
208*a7a9e3baSEtienne Carriere 	rate = rate_64;
209*a7a9e3baSEtienne Carriere 
210*a7a9e3baSEtienne Carriere 	status = plat_scmi_clock_set_rate(msg->agent_id, clock_id, rate);
211*a7a9e3baSEtienne Carriere 
212*a7a9e3baSEtienne Carriere 	scmi_status_response(msg, status);
213*a7a9e3baSEtienne Carriere }
214*a7a9e3baSEtienne Carriere 
215*a7a9e3baSEtienne Carriere static void scmi_clock_config_set(struct scmi_msg *msg)
216*a7a9e3baSEtienne Carriere {
217*a7a9e3baSEtienne Carriere 	const struct scmi_clock_config_set_a2p *in_args = (void *)msg->in;
218*a7a9e3baSEtienne Carriere 	int32_t status = SCMI_GENERIC_ERROR;
219*a7a9e3baSEtienne Carriere 	bool enable = false;
220*a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
221*a7a9e3baSEtienne Carriere 
222*a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
223*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
224*a7a9e3baSEtienne Carriere 		return;
225*a7a9e3baSEtienne Carriere 	}
226*a7a9e3baSEtienne Carriere 
227*a7a9e3baSEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->agent_id)) {
228*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
229*a7a9e3baSEtienne Carriere 		return;
230*a7a9e3baSEtienne Carriere 	}
231*a7a9e3baSEtienne Carriere 
232*a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
233*a7a9e3baSEtienne Carriere 				       plat_scmi_clock_count(msg->agent_id));
234*a7a9e3baSEtienne Carriere 
235*a7a9e3baSEtienne Carriere 	enable = in_args->attributes & SCMI_CLOCK_CONFIG_SET_ENABLE_MASK;
236*a7a9e3baSEtienne Carriere 
237*a7a9e3baSEtienne Carriere 	status = plat_scmi_clock_set_state(msg->agent_id, clock_id, enable);
238*a7a9e3baSEtienne Carriere 
239*a7a9e3baSEtienne Carriere 	scmi_status_response(msg, status);
240*a7a9e3baSEtienne Carriere }
241*a7a9e3baSEtienne Carriere 
242*a7a9e3baSEtienne Carriere #define RATES_ARRAY_SIZE_MAX	(SCMI_PLAYLOAD_MAX - \
243*a7a9e3baSEtienne Carriere 				 sizeof(struct scmi_clock_describe_rates_p2a))
244*a7a9e3baSEtienne Carriere 
245*a7a9e3baSEtienne Carriere #define SCMI_RATES_BY_ARRAY(_nb_rates, _rem_rates) \
246*a7a9e3baSEtienne Carriere 	SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS((_nb_rates), \
247*a7a9e3baSEtienne Carriere 						SCMI_CLOCK_RATE_FORMAT_LIST, \
248*a7a9e3baSEtienne Carriere 						(_rem_rates))
249*a7a9e3baSEtienne Carriere #define SCMI_RATES_BY_STEP \
250*a7a9e3baSEtienne Carriere 	SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS(3, \
251*a7a9e3baSEtienne Carriere 						SCMI_CLOCK_RATE_FORMAT_RANGE, \
252*a7a9e3baSEtienne Carriere 						0)
253*a7a9e3baSEtienne Carriere 
254*a7a9e3baSEtienne Carriere #define RATE_DESC_SIZE		sizeof(struct scmi_clock_rate)
255*a7a9e3baSEtienne Carriere 
256*a7a9e3baSEtienne Carriere static void write_rate_desc_array_in_buffer(char *dest, unsigned long *rates,
257*a7a9e3baSEtienne Carriere 					    size_t nb_elt)
258*a7a9e3baSEtienne Carriere {
259*a7a9e3baSEtienne Carriere 	uint32_t *out = NULL;
260*a7a9e3baSEtienne Carriere 	size_t n = 0;
261*a7a9e3baSEtienne Carriere 
262*a7a9e3baSEtienne Carriere 	assert(ALIGNMENT_IS_OK(dest, uint32_t));
263*a7a9e3baSEtienne Carriere 	out = (uint32_t *)(uintptr_t)dest;
264*a7a9e3baSEtienne Carriere 
265*a7a9e3baSEtienne Carriere 	for (n = 0; n < nb_elt; n++) {
266*a7a9e3baSEtienne Carriere 		uint64_t rate = rates[n];
267*a7a9e3baSEtienne Carriere 
268*a7a9e3baSEtienne Carriere 		reg_pair_from_64(rate, out + 2 * n + 1, out + 2 * n);
269*a7a9e3baSEtienne Carriere 	}
270*a7a9e3baSEtienne Carriere }
271*a7a9e3baSEtienne Carriere 
272*a7a9e3baSEtienne Carriere static void scmi_clock_describe_rates(struct scmi_msg *msg)
273*a7a9e3baSEtienne Carriere {
274*a7a9e3baSEtienne Carriere 	const struct scmi_clock_describe_rates_a2p *in_args = (void *)msg->in;
275*a7a9e3baSEtienne Carriere 	struct scmi_clock_describe_rates_p2a p2a = { };
276*a7a9e3baSEtienne Carriere 	size_t nb_rates = 0;
277*a7a9e3baSEtienne Carriere 	int32_t status = SCMI_GENERIC_ERROR;
278*a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
279*a7a9e3baSEtienne Carriere 
280*a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
281*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
282*a7a9e3baSEtienne Carriere 		return;
283*a7a9e3baSEtienne Carriere 	}
284*a7a9e3baSEtienne Carriere 
285*a7a9e3baSEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->agent_id)) {
286*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
287*a7a9e3baSEtienne Carriere 		return;
288*a7a9e3baSEtienne Carriere 	}
289*a7a9e3baSEtienne Carriere 
290*a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
291*a7a9e3baSEtienne Carriere 				       plat_scmi_clock_count(msg->agent_id));
292*a7a9e3baSEtienne Carriere 
293*a7a9e3baSEtienne Carriere 	/* Platform may support array rate description */
294*a7a9e3baSEtienne Carriere 	status = plat_scmi_clock_rates_array(msg->agent_id, clock_id, NULL,
295*a7a9e3baSEtienne Carriere 					     &nb_rates);
296*a7a9e3baSEtienne Carriere 	if (status == SCMI_SUCCESS) {
297*a7a9e3baSEtienne Carriere 		/* Currently 12 cells mex, so it's affordable for the stack */
298*a7a9e3baSEtienne Carriere 		unsigned long plat_rates[RATES_ARRAY_SIZE_MAX / RATE_DESC_SIZE];
299*a7a9e3baSEtienne Carriere 		size_t max_nb = RATES_ARRAY_SIZE_MAX / RATE_DESC_SIZE;
300*a7a9e3baSEtienne Carriere 		size_t ret_nb = MIN(nb_rates - in_args->rate_index, max_nb);
301*a7a9e3baSEtienne Carriere 		size_t rem_nb = nb_rates - in_args->rate_index - ret_nb;
302*a7a9e3baSEtienne Carriere 
303*a7a9e3baSEtienne Carriere 		status =  plat_scmi_clock_rates_array(msg->agent_id, clock_id,
304*a7a9e3baSEtienne Carriere 						      plat_rates, &ret_nb);
305*a7a9e3baSEtienne Carriere 		if (status == SCMI_SUCCESS) {
306*a7a9e3baSEtienne Carriere 			write_rate_desc_array_in_buffer(msg->out + sizeof(p2a),
307*a7a9e3baSEtienne Carriere 							plat_rates, ret_nb);
308*a7a9e3baSEtienne Carriere 
309*a7a9e3baSEtienne Carriere 			p2a.num_rates_flags = SCMI_RATES_BY_ARRAY(ret_nb,
310*a7a9e3baSEtienne Carriere 								  rem_nb);
311*a7a9e3baSEtienne Carriere 			p2a.status = SCMI_SUCCESS;
312*a7a9e3baSEtienne Carriere 
313*a7a9e3baSEtienne Carriere 			memcpy(msg->out, &p2a, sizeof(p2a));
314*a7a9e3baSEtienne Carriere 			msg->out_size_out = sizeof(p2a) +
315*a7a9e3baSEtienne Carriere 					    ret_nb * RATE_DESC_SIZE;
316*a7a9e3baSEtienne Carriere 		}
317*a7a9e3baSEtienne Carriere 	} else if (status == SCMI_NOT_SUPPORTED) {
318*a7a9e3baSEtienne Carriere 		unsigned long triplet[3] = { 0, 0, 0 };
319*a7a9e3baSEtienne Carriere 
320*a7a9e3baSEtienne Carriere 		/* Platform may support min§max/step triplet description */
321*a7a9e3baSEtienne Carriere 		status =  plat_scmi_clock_rates_by_step(msg->agent_id, clock_id,
322*a7a9e3baSEtienne Carriere 							triplet);
323*a7a9e3baSEtienne Carriere 		if (status == SCMI_SUCCESS) {
324*a7a9e3baSEtienne Carriere 			write_rate_desc_array_in_buffer(msg->out + sizeof(p2a),
325*a7a9e3baSEtienne Carriere 							triplet, 3);
326*a7a9e3baSEtienne Carriere 
327*a7a9e3baSEtienne Carriere 			p2a.num_rates_flags = SCMI_RATES_BY_STEP;
328*a7a9e3baSEtienne Carriere 			p2a.status = SCMI_SUCCESS;
329*a7a9e3baSEtienne Carriere 
330*a7a9e3baSEtienne Carriere 			memcpy(msg->out, &p2a, sizeof(p2a));
331*a7a9e3baSEtienne Carriere 			msg->out_size_out = sizeof(p2a) + (3 * RATE_DESC_SIZE);
332*a7a9e3baSEtienne Carriere 		}
333*a7a9e3baSEtienne Carriere 	} else {
334*a7a9e3baSEtienne Carriere 		/* Fallthrough generic exit sequence below with error status */
335*a7a9e3baSEtienne Carriere 	}
336*a7a9e3baSEtienne Carriere 
337*a7a9e3baSEtienne Carriere 	if (status) {
338*a7a9e3baSEtienne Carriere 		scmi_status_response(msg, status);
339*a7a9e3baSEtienne Carriere 	} else {
340*a7a9e3baSEtienne Carriere 		/*
341*a7a9e3baSEtienne Carriere 		 * Message payload is already writen to msg->out, and
342*a7a9e3baSEtienne Carriere 		 * msg->out_size_out updated.
343*a7a9e3baSEtienne Carriere 		 */
344*a7a9e3baSEtienne Carriere 	}
345*a7a9e3baSEtienne Carriere }
346*a7a9e3baSEtienne Carriere 
347*a7a9e3baSEtienne Carriere static const scmi_msg_handler_t scmi_clock_handler_table[] = {
348*a7a9e3baSEtienne Carriere 	[SCMI_PROTOCOL_VERSION] = report_version,
349*a7a9e3baSEtienne Carriere 	[SCMI_PROTOCOL_ATTRIBUTES] = report_attributes,
350*a7a9e3baSEtienne Carriere 	[SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes,
351*a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_ATTRIBUTES] = scmi_clock_attributes,
352*a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_DESCRIBE_RATES] = scmi_clock_describe_rates,
353*a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_RATE_SET] = scmi_clock_rate_set,
354*a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_RATE_GET] = scmi_clock_rate_get,
355*a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_CONFIG_SET] = scmi_clock_config_set,
356*a7a9e3baSEtienne Carriere };
357*a7a9e3baSEtienne Carriere 
358*a7a9e3baSEtienne Carriere static bool message_id_is_supported(size_t message_id)
359*a7a9e3baSEtienne Carriere {
360*a7a9e3baSEtienne Carriere 	return message_id < ARRAY_SIZE(scmi_clock_handler_table) &&
361*a7a9e3baSEtienne Carriere 	       scmi_clock_handler_table[message_id];
362*a7a9e3baSEtienne Carriere }
363*a7a9e3baSEtienne Carriere 
364*a7a9e3baSEtienne Carriere scmi_msg_handler_t scmi_msg_get_clock_handler(struct scmi_msg *msg)
365*a7a9e3baSEtienne Carriere {
366*a7a9e3baSEtienne Carriere 	const size_t array_size = ARRAY_SIZE(scmi_clock_handler_table);
367*a7a9e3baSEtienne Carriere 	unsigned int message_id = 0;
368*a7a9e3baSEtienne Carriere 
369*a7a9e3baSEtienne Carriere 	if (msg->message_id >= array_size) {
370*a7a9e3baSEtienne Carriere 		DMSG("Clock handle not found %u", msg->message_id);
371*a7a9e3baSEtienne Carriere 		return NULL;
372*a7a9e3baSEtienne Carriere 	}
373*a7a9e3baSEtienne Carriere 
374*a7a9e3baSEtienne Carriere 	message_id = confine_array_index(msg->message_id, array_size);
375*a7a9e3baSEtienne Carriere 
376*a7a9e3baSEtienne Carriere 	return scmi_clock_handler_table[message_id];
377*a7a9e3baSEtienne Carriere }
378