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