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