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 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 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 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; 32490252e2aSEtienne Carriere remaining = nb_rates - in_args->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 370*d0d36df6SLudvig 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 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