xref: /optee_os/core/drivers/scmi-msg/clock.c (revision 229670c3ef41b54b307e17fb841baaecc49648f6)
1a7a9e3baSEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
2a7a9e3baSEtienne Carriere /*
3a7a9e3baSEtienne Carriere  * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
4a7a9e3baSEtienne Carriere  * Copyright (c) 2019, Linaro Limited
5a7a9e3baSEtienne Carriere  */
6a7a9e3baSEtienne Carriere #include <assert.h>
7a7a9e3baSEtienne Carriere #include <confine_array_index.h>
8a7a9e3baSEtienne Carriere #include <drivers/scmi-msg.h>
9a7a9e3baSEtienne Carriere #include <drivers/scmi.h>
10a7a9e3baSEtienne Carriere #include <string.h>
11a7a9e3baSEtienne Carriere #include <util.h>
12a7a9e3baSEtienne Carriere 
13a7a9e3baSEtienne Carriere #include "clock.h"
14a7a9e3baSEtienne Carriere #include "common.h"
15a7a9e3baSEtienne Carriere 
16a7a9e3baSEtienne Carriere static bool message_id_is_supported(unsigned int message_id);
17a7a9e3baSEtienne Carriere 
plat_scmi_clock_count(unsigned int channel_id __unused)18659a1f88SEtienne Carriere size_t __weak plat_scmi_clock_count(unsigned int channel_id __unused)
19a7a9e3baSEtienne Carriere {
20a7a9e3baSEtienne Carriere 	return 0;
21a7a9e3baSEtienne Carriere }
22a7a9e3baSEtienne Carriere 
plat_scmi_clock_get_name(unsigned int channel_id __unused,unsigned int scmi_id __unused)23659a1f88SEtienne Carriere const char __weak *plat_scmi_clock_get_name(unsigned int channel_id __unused,
24a7a9e3baSEtienne Carriere 					    unsigned int scmi_id __unused)
25a7a9e3baSEtienne Carriere {
26a7a9e3baSEtienne Carriere 	return NULL;
27a7a9e3baSEtienne Carriere }
28a7a9e3baSEtienne Carriere 
plat_scmi_clock_rates_array(unsigned int channel_id __unused,unsigned int scmi_id __unused,size_t start_index __unused,unsigned long * rates __unused,size_t * nb_elts __unused)29659a1f88SEtienne Carriere int32_t __weak plat_scmi_clock_rates_array(unsigned int channel_id __unused,
30a7a9e3baSEtienne Carriere 					   unsigned int scmi_id __unused,
31d9be1b35SEtienne Carriere 					   size_t start_index __unused,
32a7a9e3baSEtienne Carriere 					   unsigned long *rates __unused,
33a7a9e3baSEtienne Carriere 					   size_t *nb_elts __unused)
34a7a9e3baSEtienne Carriere {
35a7a9e3baSEtienne Carriere 	return SCMI_NOT_SUPPORTED;
36a7a9e3baSEtienne Carriere }
37a7a9e3baSEtienne Carriere 
plat_scmi_clock_rates_by_step(unsigned int channel_id __unused,unsigned int scmi_id __unused,unsigned long * steps __unused)38659a1f88SEtienne Carriere int32_t __weak plat_scmi_clock_rates_by_step(unsigned int channel_id __unused,
39a7a9e3baSEtienne Carriere 					     unsigned int scmi_id __unused,
40a7a9e3baSEtienne Carriere 					     unsigned long *steps __unused)
41a7a9e3baSEtienne Carriere {
42a7a9e3baSEtienne Carriere 	return SCMI_NOT_SUPPORTED;
43a7a9e3baSEtienne Carriere }
44a7a9e3baSEtienne Carriere 
plat_scmi_clock_get_rate(unsigned int channel_id __unused,unsigned int scmi_id __unused)45659a1f88SEtienne Carriere unsigned long __weak plat_scmi_clock_get_rate(unsigned int channel_id __unused,
46a7a9e3baSEtienne Carriere 					      unsigned int scmi_id __unused)
47a7a9e3baSEtienne Carriere {
48a7a9e3baSEtienne Carriere 	return 0;
49a7a9e3baSEtienne Carriere }
50a7a9e3baSEtienne Carriere 
plat_scmi_clock_set_rate(unsigned int channel_id __unused,unsigned int scmi_id __unused,unsigned long rate __unused)51659a1f88SEtienne Carriere int32_t __weak plat_scmi_clock_set_rate(unsigned int channel_id __unused,
52a7a9e3baSEtienne Carriere 					unsigned int scmi_id __unused,
53a7a9e3baSEtienne Carriere 					unsigned long rate __unused)
54a7a9e3baSEtienne Carriere {
55a7a9e3baSEtienne Carriere 	return SCMI_NOT_SUPPORTED;
56a7a9e3baSEtienne Carriere }
57a7a9e3baSEtienne Carriere 
plat_scmi_clock_get_state(unsigned int channel_id __unused,unsigned int scmi_id __unused)58659a1f88SEtienne Carriere int32_t __weak plat_scmi_clock_get_state(unsigned int channel_id __unused,
59a7a9e3baSEtienne Carriere 					 unsigned int scmi_id __unused)
60a7a9e3baSEtienne Carriere {
61a7a9e3baSEtienne Carriere 	return SCMI_NOT_SUPPORTED;
62a7a9e3baSEtienne Carriere }
63a7a9e3baSEtienne Carriere 
plat_scmi_clock_set_state(unsigned int channel_id __unused,unsigned int scmi_id __unused,bool enable_not_disable __unused)64659a1f88SEtienne Carriere int32_t __weak plat_scmi_clock_set_state(unsigned int channel_id __unused,
65a7a9e3baSEtienne Carriere 					 unsigned int scmi_id __unused,
66a7a9e3baSEtienne Carriere 					 bool enable_not_disable __unused)
67a7a9e3baSEtienne Carriere {
68a7a9e3baSEtienne Carriere 	return SCMI_NOT_SUPPORTED;
69a7a9e3baSEtienne Carriere }
70a7a9e3baSEtienne Carriere 
report_version(struct scmi_msg * msg)71a7a9e3baSEtienne Carriere static void report_version(struct scmi_msg *msg)
72a7a9e3baSEtienne Carriere {
73a7a9e3baSEtienne Carriere 	struct scmi_protocol_version_p2a return_values = {
74a7a9e3baSEtienne Carriere 		.status = SCMI_SUCCESS,
75a7a9e3baSEtienne Carriere 		.version = SCMI_PROTOCOL_VERSION_CLOCK,
76a7a9e3baSEtienne Carriere 	};
77a7a9e3baSEtienne Carriere 
78a7a9e3baSEtienne Carriere 	if (msg->in_size) {
79a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
80a7a9e3baSEtienne Carriere 		return;
81a7a9e3baSEtienne Carriere 	}
82a7a9e3baSEtienne Carriere 
83a7a9e3baSEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
84a7a9e3baSEtienne Carriere }
85a7a9e3baSEtienne Carriere 
report_attributes(struct scmi_msg * msg)86a7a9e3baSEtienne Carriere static void report_attributes(struct scmi_msg *msg)
87a7a9e3baSEtienne Carriere {
88659a1f88SEtienne Carriere 	size_t clk_count = plat_scmi_clock_count(msg->channel_id);
89a7a9e3baSEtienne Carriere 	struct scmi_protocol_attributes_p2a return_values = {
90a7a9e3baSEtienne Carriere 		.status = SCMI_SUCCESS,
91659a1f88SEtienne Carriere 		.attributes = SCMI_CLOCK_PROTOCOL_ATTRIBUTES(1, clk_count),
92a7a9e3baSEtienne Carriere 	};
93a7a9e3baSEtienne Carriere 
94a7a9e3baSEtienne Carriere 	if (msg->in_size) {
95a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
96a7a9e3baSEtienne Carriere 		return;
97a7a9e3baSEtienne Carriere 	}
98a7a9e3baSEtienne Carriere 
99a7a9e3baSEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
100a7a9e3baSEtienne Carriere }
101a7a9e3baSEtienne Carriere 
report_message_attributes(struct scmi_msg * msg)102a7a9e3baSEtienne Carriere static void report_message_attributes(struct scmi_msg *msg)
103a7a9e3baSEtienne Carriere {
104a7a9e3baSEtienne Carriere 	struct scmi_protocol_message_attributes_a2p *in_args = (void *)msg->in;
105a7a9e3baSEtienne Carriere 	struct scmi_protocol_message_attributes_p2a return_values = {
106a7a9e3baSEtienne Carriere 		.status = SCMI_SUCCESS,
107a7a9e3baSEtienne Carriere 		/* For this protocol, attributes shall be zero */
108a7a9e3baSEtienne Carriere 		.attributes = 0,
109a7a9e3baSEtienne Carriere 	};
110a7a9e3baSEtienne Carriere 
111a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
112a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
113a7a9e3baSEtienne Carriere 		return;
114a7a9e3baSEtienne Carriere 	}
115a7a9e3baSEtienne Carriere 
116a7a9e3baSEtienne Carriere 	if (!message_id_is_supported(in_args->message_id)) {
117a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_NOT_FOUND);
118a7a9e3baSEtienne Carriere 		return;
119a7a9e3baSEtienne Carriere 	}
120a7a9e3baSEtienne Carriere 
121a7a9e3baSEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
122a7a9e3baSEtienne Carriere }
123a7a9e3baSEtienne Carriere 
scmi_clock_attributes(struct scmi_msg * msg)124a7a9e3baSEtienne Carriere static void scmi_clock_attributes(struct scmi_msg *msg)
125a7a9e3baSEtienne Carriere {
126a7a9e3baSEtienne Carriere 	const struct scmi_clock_attributes_a2p *in_args = (void *)msg->in;
127a7a9e3baSEtienne Carriere 	struct scmi_clock_attributes_p2a return_values = {
128a7a9e3baSEtienne Carriere 		.status = SCMI_SUCCESS,
129a7a9e3baSEtienne Carriere 	};
130a7a9e3baSEtienne Carriere 	const char *name = NULL;
131a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
132a7a9e3baSEtienne Carriere 
133a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
134a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
135a7a9e3baSEtienne Carriere 		return;
136a7a9e3baSEtienne Carriere 	}
137a7a9e3baSEtienne Carriere 
138659a1f88SEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->channel_id)) {
139a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
140a7a9e3baSEtienne Carriere 		return;
141a7a9e3baSEtienne Carriere 	}
142a7a9e3baSEtienne Carriere 
143a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
144659a1f88SEtienne Carriere 				       plat_scmi_clock_count(msg->channel_id));
145a7a9e3baSEtienne Carriere 
146659a1f88SEtienne Carriere 	name = plat_scmi_clock_get_name(msg->channel_id, clock_id);
147a7a9e3baSEtienne Carriere 	if (!name) {
148a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_NOT_FOUND);
149a7a9e3baSEtienne Carriere 		return;
150a7a9e3baSEtienne Carriere 	}
151a7a9e3baSEtienne Carriere 
152a7a9e3baSEtienne Carriere 	COPY_NAME_IDENTIFIER(return_values.clock_name, name);
153a7a9e3baSEtienne Carriere 
154659a1f88SEtienne Carriere 	return_values.attributes = plat_scmi_clock_get_state(msg->channel_id,
155a7a9e3baSEtienne Carriere 							     clock_id);
156a7a9e3baSEtienne Carriere 
157a7a9e3baSEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
158a7a9e3baSEtienne Carriere }
159a7a9e3baSEtienne Carriere 
scmi_clock_rate_get(struct scmi_msg * msg)160a7a9e3baSEtienne Carriere static void scmi_clock_rate_get(struct scmi_msg *msg)
161a7a9e3baSEtienne Carriere {
162a7a9e3baSEtienne Carriere 	const struct scmi_clock_rate_get_a2p *in_args = (void *)msg->in;
163a7a9e3baSEtienne Carriere 	unsigned long rate = 0;
164a7a9e3baSEtienne Carriere 	struct scmi_clock_rate_get_p2a return_values = { };
165a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
166a7a9e3baSEtienne Carriere 
167a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
168a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
169a7a9e3baSEtienne Carriere 		return;
170a7a9e3baSEtienne Carriere 	}
171a7a9e3baSEtienne Carriere 
172659a1f88SEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->channel_id)) {
173a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
174a7a9e3baSEtienne Carriere 		return;
175a7a9e3baSEtienne Carriere 	}
176a7a9e3baSEtienne Carriere 
177a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
178659a1f88SEtienne Carriere 				       plat_scmi_clock_count(msg->channel_id));
179a7a9e3baSEtienne Carriere 
180659a1f88SEtienne Carriere 	rate = plat_scmi_clock_get_rate(msg->channel_id, clock_id);
181a7a9e3baSEtienne Carriere 
182a7a9e3baSEtienne Carriere 	reg_pair_from_64(rate, return_values.rate + 1, return_values.rate);
183a7a9e3baSEtienne Carriere 
184a7a9e3baSEtienne Carriere 	scmi_write_response(msg, &return_values, sizeof(return_values));
185a7a9e3baSEtienne Carriere }
186a7a9e3baSEtienne Carriere 
scmi_clock_rate_set(struct scmi_msg * msg)187a7a9e3baSEtienne Carriere static void scmi_clock_rate_set(struct scmi_msg *msg)
188a7a9e3baSEtienne Carriere {
189a7a9e3baSEtienne Carriere 	const struct scmi_clock_rate_set_a2p *in_args = (void *)msg->in;
190a7a9e3baSEtienne Carriere 	uint64_t rate_64 = 0;
191a7a9e3baSEtienne Carriere 	unsigned long rate = 0;
192a7a9e3baSEtienne Carriere 	int32_t status = 0;
193a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
194a7a9e3baSEtienne Carriere 
195a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
196a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
197a7a9e3baSEtienne Carriere 		return;
198a7a9e3baSEtienne Carriere 	}
199a7a9e3baSEtienne Carriere 
200659a1f88SEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->channel_id)) {
201a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
202a7a9e3baSEtienne Carriere 		return;
203a7a9e3baSEtienne Carriere 	}
204a7a9e3baSEtienne Carriere 
205a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
206659a1f88SEtienne Carriere 				       plat_scmi_clock_count(msg->channel_id));
207a7a9e3baSEtienne Carriere 
208a7a9e3baSEtienne Carriere 	rate_64 = reg_pair_to_64(in_args->rate[1], in_args->rate[0]);
209a7a9e3baSEtienne Carriere 	rate = rate_64;
210a7a9e3baSEtienne Carriere 
211659a1f88SEtienne Carriere 	status = plat_scmi_clock_set_rate(msg->channel_id, clock_id, rate);
212a7a9e3baSEtienne Carriere 
213a7a9e3baSEtienne Carriere 	scmi_status_response(msg, status);
214a7a9e3baSEtienne Carriere }
215a7a9e3baSEtienne Carriere 
scmi_clock_config_set(struct scmi_msg * msg)216a7a9e3baSEtienne Carriere static void scmi_clock_config_set(struct scmi_msg *msg)
217a7a9e3baSEtienne Carriere {
218a7a9e3baSEtienne Carriere 	const struct scmi_clock_config_set_a2p *in_args = (void *)msg->in;
219a7a9e3baSEtienne Carriere 	int32_t status = SCMI_GENERIC_ERROR;
220a7a9e3baSEtienne Carriere 	bool enable = false;
221a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
222a7a9e3baSEtienne Carriere 
223a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
224a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
225a7a9e3baSEtienne Carriere 		return;
226a7a9e3baSEtienne Carriere 	}
227a7a9e3baSEtienne Carriere 
228659a1f88SEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->channel_id)) {
229a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
230a7a9e3baSEtienne Carriere 		return;
231a7a9e3baSEtienne Carriere 	}
232a7a9e3baSEtienne Carriere 
233a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
234659a1f88SEtienne Carriere 				       plat_scmi_clock_count(msg->channel_id));
235a7a9e3baSEtienne Carriere 
236a7a9e3baSEtienne Carriere 	enable = in_args->attributes & SCMI_CLOCK_CONFIG_SET_ENABLE_MASK;
237a7a9e3baSEtienne Carriere 
238659a1f88SEtienne Carriere 	status = plat_scmi_clock_set_state(msg->channel_id, clock_id, enable);
239a7a9e3baSEtienne Carriere 
240a7a9e3baSEtienne Carriere 	scmi_status_response(msg, status);
241a7a9e3baSEtienne Carriere }
242a7a9e3baSEtienne Carriere 
243a7a9e3baSEtienne Carriere #define SCMI_RATES_BY_ARRAY(_nb_rates, _rem_rates) \
244a7a9e3baSEtienne Carriere 	SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS((_nb_rates), \
245a7a9e3baSEtienne Carriere 						SCMI_CLOCK_RATE_FORMAT_LIST, \
246a7a9e3baSEtienne Carriere 						(_rem_rates))
247a7a9e3baSEtienne Carriere #define SCMI_RATES_BY_STEP \
248a7a9e3baSEtienne Carriere 	SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS(3, \
249a7a9e3baSEtienne Carriere 						SCMI_CLOCK_RATE_FORMAT_RANGE, \
250a7a9e3baSEtienne Carriere 						0)
251a7a9e3baSEtienne Carriere 
252a7a9e3baSEtienne Carriere #define RATE_DESC_SIZE		sizeof(struct scmi_clock_rate)
253a7a9e3baSEtienne Carriere 
write_rate_desc_array_in_buffer(char * dest,unsigned long * rates,size_t nb_elt)254a7a9e3baSEtienne Carriere static void write_rate_desc_array_in_buffer(char *dest, unsigned long *rates,
255a7a9e3baSEtienne Carriere 					    size_t nb_elt)
256a7a9e3baSEtienne Carriere {
257a7a9e3baSEtienne Carriere 	uint32_t *out = NULL;
258a7a9e3baSEtienne Carriere 	size_t n = 0;
259a7a9e3baSEtienne Carriere 
260be501eb1SJorge Ramirez-Ortiz 	assert(IS_ALIGNED_WITH_TYPE(dest, uint32_t));
261a7a9e3baSEtienne Carriere 	out = (uint32_t *)(uintptr_t)dest;
262a7a9e3baSEtienne Carriere 
263a7a9e3baSEtienne Carriere 	for (n = 0; n < nb_elt; n++) {
264a7a9e3baSEtienne Carriere 		uint64_t rate = rates[n];
265a7a9e3baSEtienne Carriere 
266a7a9e3baSEtienne Carriere 		reg_pair_from_64(rate, out + 2 * n + 1, out + 2 * n);
267a7a9e3baSEtienne Carriere 	}
268a7a9e3baSEtienne Carriere }
269a7a9e3baSEtienne Carriere 
scmi_clock_describe_rates(struct scmi_msg * msg)270a7a9e3baSEtienne Carriere static void scmi_clock_describe_rates(struct scmi_msg *msg)
271a7a9e3baSEtienne Carriere {
272a7a9e3baSEtienne Carriere 	const struct scmi_clock_describe_rates_a2p *in_args = (void *)msg->in;
273a7a9e3baSEtienne Carriere 	struct scmi_clock_describe_rates_p2a p2a = { };
274a7a9e3baSEtienne Carriere 	size_t nb_rates = 0;
275a7a9e3baSEtienne Carriere 	int32_t status = SCMI_GENERIC_ERROR;
276a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
27790252e2aSEtienne Carriere 	unsigned int out_count = 0;
278a7a9e3baSEtienne Carriere 
279a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
280a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
281a7a9e3baSEtienne Carriere 		return;
282a7a9e3baSEtienne Carriere 	}
283a7a9e3baSEtienne Carriere 
284659a1f88SEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->channel_id)) {
285a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
286a7a9e3baSEtienne Carriere 		return;
287a7a9e3baSEtienne Carriere 	}
288a7a9e3baSEtienne Carriere 
289a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
290659a1f88SEtienne Carriere 				       plat_scmi_clock_count(msg->channel_id));
291a7a9e3baSEtienne Carriere 
292a7a9e3baSEtienne Carriere 	/* Platform may support array rate description */
293659a1f88SEtienne Carriere 	status = plat_scmi_clock_rates_array(msg->channel_id, clock_id, 0, NULL,
294a7a9e3baSEtienne Carriere 					     &nb_rates);
295a7a9e3baSEtienne Carriere 	if (status == SCMI_SUCCESS) {
29690252e2aSEtienne Carriere 		unsigned int rate_index = in_args->rate_index;
29790252e2aSEtienne Carriere 		unsigned int remaining = 0;
29890252e2aSEtienne Carriere 		size_t avail_sz = msg->out_size - sizeof(p2a);
29990252e2aSEtienne Carriere 		char *out_rates = msg->out + sizeof(p2a);
300a7a9e3baSEtienne Carriere 
30190252e2aSEtienne Carriere 		if (avail_sz < RATE_DESC_SIZE && nb_rates) {
30290252e2aSEtienne Carriere 			status = SCMI_PROTOCOL_ERROR;
30390252e2aSEtienne Carriere 			goto out;
304a7a9e3baSEtienne Carriere 		}
30590252e2aSEtienne Carriere 
30690252e2aSEtienne Carriere 		while (avail_sz >= RATE_DESC_SIZE && rate_index < nb_rates) {
30790252e2aSEtienne Carriere 			unsigned long rate = 0;
30890252e2aSEtienne Carriere 			size_t cnt = 1;
30990252e2aSEtienne Carriere 
31090252e2aSEtienne Carriere 			status = plat_scmi_clock_rates_array(msg->channel_id,
31190252e2aSEtienne Carriere 							     clock_id,
31290252e2aSEtienne Carriere 							     rate_index,
31390252e2aSEtienne Carriere 							     &rate, &cnt);
31490252e2aSEtienne Carriere 			if (status)
31590252e2aSEtienne Carriere 				goto out;
31690252e2aSEtienne Carriere 
31790252e2aSEtienne Carriere 			write_rate_desc_array_in_buffer(out_rates, &rate, cnt);
31890252e2aSEtienne Carriere 			avail_sz -= RATE_DESC_SIZE;
31990252e2aSEtienne Carriere 			out_rates += RATE_DESC_SIZE;
32090252e2aSEtienne Carriere 			rate_index++;
32190252e2aSEtienne Carriere 		}
32290252e2aSEtienne Carriere 
32390252e2aSEtienne Carriere 		out_count = rate_index - in_args->rate_index;
324*229670c3SEtienne Carriere 		remaining = nb_rates - rate_index;
32590252e2aSEtienne Carriere 		p2a.num_rates_flags = SCMI_RATES_BY_ARRAY(out_count, remaining);
326a7a9e3baSEtienne Carriere 	} else if (status == SCMI_NOT_SUPPORTED) {
327a7a9e3baSEtienne Carriere 		unsigned long triplet[3] = { 0, 0, 0 };
328a7a9e3baSEtienne Carriere 
32990252e2aSEtienne Carriere 		if (msg->out_size < sizeof(p2a) + 3 * RATE_DESC_SIZE) {
33090252e2aSEtienne Carriere 			status = SCMI_PROTOCOL_ERROR;
33190252e2aSEtienne Carriere 			goto out;
33290252e2aSEtienne Carriere 		}
33390252e2aSEtienne Carriere 
334c4853b5cSClément Léger 		/* Platform may support min/max/step triplet description */
335659a1f88SEtienne Carriere 		status =  plat_scmi_clock_rates_by_step(msg->channel_id,
336659a1f88SEtienne Carriere 							clock_id, triplet);
33790252e2aSEtienne Carriere 		if (status)
33890252e2aSEtienne Carriere 			goto out;
33990252e2aSEtienne Carriere 
340a7a9e3baSEtienne Carriere 		write_rate_desc_array_in_buffer(msg->out + sizeof(p2a),
341a7a9e3baSEtienne Carriere 						triplet, 3);
342a7a9e3baSEtienne Carriere 
34390252e2aSEtienne Carriere 		out_count = 3;
344a7a9e3baSEtienne Carriere 		p2a.num_rates_flags = SCMI_RATES_BY_STEP;
345a7a9e3baSEtienne Carriere 	} else {
346a7a9e3baSEtienne Carriere 		/* Fallthrough generic exit sequence below with error status */
347a7a9e3baSEtienne Carriere 	}
348a7a9e3baSEtienne Carriere 
34990252e2aSEtienne Carriere out:
350a7a9e3baSEtienne Carriere 	if (status) {
351a7a9e3baSEtienne Carriere 		scmi_status_response(msg, status);
352a7a9e3baSEtienne Carriere 	} else {
35390252e2aSEtienne Carriere 		p2a.status = SCMI_SUCCESS;
35490252e2aSEtienne Carriere 		memcpy(msg->out, &p2a, sizeof(p2a));
35590252e2aSEtienne Carriere 		msg->out_size_out = sizeof(p2a) + out_count * RATE_DESC_SIZE;
356a7a9e3baSEtienne Carriere 	}
357a7a9e3baSEtienne Carriere }
358a7a9e3baSEtienne Carriere 
359a7a9e3baSEtienne Carriere static const scmi_msg_handler_t scmi_clock_handler_table[] = {
360a7a9e3baSEtienne Carriere 	[SCMI_PROTOCOL_VERSION] = report_version,
361a7a9e3baSEtienne Carriere 	[SCMI_PROTOCOL_ATTRIBUTES] = report_attributes,
362a7a9e3baSEtienne Carriere 	[SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes,
363a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_ATTRIBUTES] = scmi_clock_attributes,
364a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_DESCRIBE_RATES] = scmi_clock_describe_rates,
365a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_RATE_SET] = scmi_clock_rate_set,
366a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_RATE_GET] = scmi_clock_rate_get,
367a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_CONFIG_SET] = scmi_clock_config_set,
368a7a9e3baSEtienne Carriere };
369a7a9e3baSEtienne Carriere 
message_id_is_supported(unsigned int message_id)370d0d36df6SLudvig Pärsson static bool message_id_is_supported(unsigned int message_id)
371a7a9e3baSEtienne Carriere {
372a7a9e3baSEtienne Carriere 	return message_id < ARRAY_SIZE(scmi_clock_handler_table) &&
373a7a9e3baSEtienne Carriere 	       scmi_clock_handler_table[message_id];
374a7a9e3baSEtienne Carriere }
375a7a9e3baSEtienne Carriere 
scmi_msg_get_clock_handler(struct scmi_msg * msg)376a7a9e3baSEtienne Carriere scmi_msg_handler_t scmi_msg_get_clock_handler(struct scmi_msg *msg)
377a7a9e3baSEtienne Carriere {
378a7a9e3baSEtienne Carriere 	const size_t array_size = ARRAY_SIZE(scmi_clock_handler_table);
379a7a9e3baSEtienne Carriere 	unsigned int message_id = 0;
380a7a9e3baSEtienne Carriere 
381a7a9e3baSEtienne Carriere 	if (msg->message_id >= array_size) {
382a7a9e3baSEtienne Carriere 		DMSG("Clock handle not found %u", msg->message_id);
383a7a9e3baSEtienne Carriere 		return NULL;
384a7a9e3baSEtienne Carriere 	}
385a7a9e3baSEtienne Carriere 
386a7a9e3baSEtienne Carriere 	message_id = confine_array_index(msg->message_id, array_size);
387a7a9e3baSEtienne Carriere 
388a7a9e3baSEtienne Carriere 	return scmi_clock_handler_table[message_id];
389a7a9e3baSEtienne Carriere }
390