xref: /optee_os/core/drivers/scmi-msg/clock.c (revision be501eb1e01962cd097582b4b85bba9ce83b0ffc)
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 
18659a1f88SEtienne Carriere size_t __weak plat_scmi_clock_count(unsigned int channel_id __unused)
19a7a9e3baSEtienne Carriere {
20a7a9e3baSEtienne Carriere 	return 0;
21a7a9e3baSEtienne Carriere }
22a7a9e3baSEtienne Carriere 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 RATES_ARRAY_SIZE_MAX	(SCMI_PLAYLOAD_MAX - \
244a7a9e3baSEtienne Carriere 				 sizeof(struct scmi_clock_describe_rates_p2a))
245a7a9e3baSEtienne Carriere 
246a7a9e3baSEtienne Carriere #define SCMI_RATES_BY_ARRAY(_nb_rates, _rem_rates) \
247a7a9e3baSEtienne Carriere 	SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS((_nb_rates), \
248a7a9e3baSEtienne Carriere 						SCMI_CLOCK_RATE_FORMAT_LIST, \
249a7a9e3baSEtienne Carriere 						(_rem_rates))
250a7a9e3baSEtienne Carriere #define SCMI_RATES_BY_STEP \
251a7a9e3baSEtienne Carriere 	SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS(3, \
252a7a9e3baSEtienne Carriere 						SCMI_CLOCK_RATE_FORMAT_RANGE, \
253a7a9e3baSEtienne Carriere 						0)
254a7a9e3baSEtienne Carriere 
255a7a9e3baSEtienne Carriere #define RATE_DESC_SIZE		sizeof(struct scmi_clock_rate)
256a7a9e3baSEtienne Carriere 
257a7a9e3baSEtienne Carriere static void write_rate_desc_array_in_buffer(char *dest, unsigned long *rates,
258a7a9e3baSEtienne Carriere 					    size_t nb_elt)
259a7a9e3baSEtienne Carriere {
260a7a9e3baSEtienne Carriere 	uint32_t *out = NULL;
261a7a9e3baSEtienne Carriere 	size_t n = 0;
262a7a9e3baSEtienne Carriere 
263*be501eb1SJorge Ramirez-Ortiz 	assert(IS_ALIGNED_WITH_TYPE(dest, uint32_t));
264a7a9e3baSEtienne Carriere 	out = (uint32_t *)(uintptr_t)dest;
265a7a9e3baSEtienne Carriere 
266a7a9e3baSEtienne Carriere 	for (n = 0; n < nb_elt; n++) {
267a7a9e3baSEtienne Carriere 		uint64_t rate = rates[n];
268a7a9e3baSEtienne Carriere 
269a7a9e3baSEtienne Carriere 		reg_pair_from_64(rate, out + 2 * n + 1, out + 2 * n);
270a7a9e3baSEtienne Carriere 	}
271a7a9e3baSEtienne Carriere }
272a7a9e3baSEtienne Carriere 
273a7a9e3baSEtienne Carriere static void scmi_clock_describe_rates(struct scmi_msg *msg)
274a7a9e3baSEtienne Carriere {
275a7a9e3baSEtienne Carriere 	const struct scmi_clock_describe_rates_a2p *in_args = (void *)msg->in;
276a7a9e3baSEtienne Carriere 	struct scmi_clock_describe_rates_p2a p2a = { };
277a7a9e3baSEtienne Carriere 	size_t nb_rates = 0;
278a7a9e3baSEtienne Carriere 	int32_t status = SCMI_GENERIC_ERROR;
279a7a9e3baSEtienne Carriere 	unsigned int clock_id = 0;
280a7a9e3baSEtienne Carriere 
281a7a9e3baSEtienne Carriere 	if (msg->in_size != sizeof(*in_args)) {
282a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_PROTOCOL_ERROR);
283a7a9e3baSEtienne Carriere 		return;
284a7a9e3baSEtienne Carriere 	}
285a7a9e3baSEtienne Carriere 
286659a1f88SEtienne Carriere 	if (in_args->clock_id >= plat_scmi_clock_count(msg->channel_id)) {
287a7a9e3baSEtienne Carriere 		scmi_status_response(msg, SCMI_INVALID_PARAMETERS);
288a7a9e3baSEtienne Carriere 		return;
289a7a9e3baSEtienne Carriere 	}
290a7a9e3baSEtienne Carriere 
291a7a9e3baSEtienne Carriere 	clock_id = confine_array_index(in_args->clock_id,
292659a1f88SEtienne Carriere 				       plat_scmi_clock_count(msg->channel_id));
293a7a9e3baSEtienne Carriere 
294a7a9e3baSEtienne Carriere 	/* Platform may support array rate description */
295659a1f88SEtienne Carriere 	status = plat_scmi_clock_rates_array(msg->channel_id, clock_id, 0, NULL,
296a7a9e3baSEtienne Carriere 					     &nb_rates);
297a7a9e3baSEtienne Carriere 	if (status == SCMI_SUCCESS) {
298a7a9e3baSEtienne Carriere 		/* Currently 12 cells mex, so it's affordable for the stack */
299a7a9e3baSEtienne Carriere 		unsigned long plat_rates[RATES_ARRAY_SIZE_MAX / RATE_DESC_SIZE];
300a7a9e3baSEtienne Carriere 		size_t max_nb = RATES_ARRAY_SIZE_MAX / RATE_DESC_SIZE;
301a7a9e3baSEtienne Carriere 		size_t ret_nb = MIN(nb_rates - in_args->rate_index, max_nb);
302a7a9e3baSEtienne Carriere 		size_t rem_nb = nb_rates - in_args->rate_index - ret_nb;
303a7a9e3baSEtienne Carriere 
304659a1f88SEtienne Carriere 		status =  plat_scmi_clock_rates_array(msg->channel_id, clock_id,
305d9be1b35SEtienne Carriere 						      in_args->rate_index,
306a7a9e3baSEtienne Carriere 						      plat_rates, &ret_nb);
307a7a9e3baSEtienne Carriere 		if (status == SCMI_SUCCESS) {
308a7a9e3baSEtienne Carriere 			write_rate_desc_array_in_buffer(msg->out + sizeof(p2a),
309a7a9e3baSEtienne Carriere 							plat_rates, ret_nb);
310a7a9e3baSEtienne Carriere 
311a7a9e3baSEtienne Carriere 			p2a.num_rates_flags = SCMI_RATES_BY_ARRAY(ret_nb,
312a7a9e3baSEtienne Carriere 								  rem_nb);
313a7a9e3baSEtienne Carriere 			p2a.status = SCMI_SUCCESS;
314a7a9e3baSEtienne Carriere 
315a7a9e3baSEtienne Carriere 			memcpy(msg->out, &p2a, sizeof(p2a));
316a7a9e3baSEtienne Carriere 			msg->out_size_out = sizeof(p2a) +
317a7a9e3baSEtienne Carriere 					    ret_nb * RATE_DESC_SIZE;
318a7a9e3baSEtienne Carriere 		}
319a7a9e3baSEtienne Carriere 	} else if (status == SCMI_NOT_SUPPORTED) {
320a7a9e3baSEtienne Carriere 		unsigned long triplet[3] = { 0, 0, 0 };
321a7a9e3baSEtienne Carriere 
322c4853b5cSClément Léger 		/* Platform may support min/max/step triplet description */
323659a1f88SEtienne Carriere 		status =  plat_scmi_clock_rates_by_step(msg->channel_id,
324659a1f88SEtienne Carriere 							clock_id, triplet);
325a7a9e3baSEtienne Carriere 		if (status == SCMI_SUCCESS) {
326a7a9e3baSEtienne Carriere 			write_rate_desc_array_in_buffer(msg->out + sizeof(p2a),
327a7a9e3baSEtienne Carriere 							triplet, 3);
328a7a9e3baSEtienne Carriere 
329a7a9e3baSEtienne Carriere 			p2a.num_rates_flags = SCMI_RATES_BY_STEP;
330a7a9e3baSEtienne Carriere 			p2a.status = SCMI_SUCCESS;
331a7a9e3baSEtienne Carriere 
332a7a9e3baSEtienne Carriere 			memcpy(msg->out, &p2a, sizeof(p2a));
333a7a9e3baSEtienne Carriere 			msg->out_size_out = sizeof(p2a) + (3 * RATE_DESC_SIZE);
334a7a9e3baSEtienne Carriere 		}
335a7a9e3baSEtienne Carriere 	} else {
336a7a9e3baSEtienne Carriere 		/* Fallthrough generic exit sequence below with error status */
337a7a9e3baSEtienne Carriere 	}
338a7a9e3baSEtienne Carriere 
339a7a9e3baSEtienne Carriere 	if (status) {
340a7a9e3baSEtienne Carriere 		scmi_status_response(msg, status);
341a7a9e3baSEtienne Carriere 	} else {
342a7a9e3baSEtienne Carriere 		/*
343a7a9e3baSEtienne Carriere 		 * Message payload is already writen to msg->out, and
344a7a9e3baSEtienne Carriere 		 * msg->out_size_out updated.
345a7a9e3baSEtienne Carriere 		 */
346a7a9e3baSEtienne Carriere 	}
347a7a9e3baSEtienne Carriere }
348a7a9e3baSEtienne Carriere 
349a7a9e3baSEtienne Carriere static const scmi_msg_handler_t scmi_clock_handler_table[] = {
350a7a9e3baSEtienne Carriere 	[SCMI_PROTOCOL_VERSION] = report_version,
351a7a9e3baSEtienne Carriere 	[SCMI_PROTOCOL_ATTRIBUTES] = report_attributes,
352a7a9e3baSEtienne Carriere 	[SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] = report_message_attributes,
353a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_ATTRIBUTES] = scmi_clock_attributes,
354a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_DESCRIBE_RATES] = scmi_clock_describe_rates,
355a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_RATE_SET] = scmi_clock_rate_set,
356a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_RATE_GET] = scmi_clock_rate_get,
357a7a9e3baSEtienne Carriere 	[SCMI_CLOCK_CONFIG_SET] = scmi_clock_config_set,
358a7a9e3baSEtienne Carriere };
359a7a9e3baSEtienne Carriere 
360a7a9e3baSEtienne Carriere static bool message_id_is_supported(size_t message_id)
361a7a9e3baSEtienne Carriere {
362a7a9e3baSEtienne Carriere 	return message_id < ARRAY_SIZE(scmi_clock_handler_table) &&
363a7a9e3baSEtienne Carriere 	       scmi_clock_handler_table[message_id];
364a7a9e3baSEtienne Carriere }
365a7a9e3baSEtienne Carriere 
366a7a9e3baSEtienne Carriere scmi_msg_handler_t scmi_msg_get_clock_handler(struct scmi_msg *msg)
367a7a9e3baSEtienne Carriere {
368a7a9e3baSEtienne Carriere 	const size_t array_size = ARRAY_SIZE(scmi_clock_handler_table);
369a7a9e3baSEtienne Carriere 	unsigned int message_id = 0;
370a7a9e3baSEtienne Carriere 
371a7a9e3baSEtienne Carriere 	if (msg->message_id >= array_size) {
372a7a9e3baSEtienne Carriere 		DMSG("Clock handle not found %u", msg->message_id);
373a7a9e3baSEtienne Carriere 		return NULL;
374a7a9e3baSEtienne Carriere 	}
375a7a9e3baSEtienne Carriere 
376a7a9e3baSEtienne Carriere 	message_id = confine_array_index(msg->message_id, array_size);
377a7a9e3baSEtienne Carriere 
378a7a9e3baSEtienne Carriere 	return scmi_clock_handler_table[message_id];
379a7a9e3baSEtienne Carriere }
380