aboutsummaryrefslogtreecommitdiff
path: root/module/scmi_clock/src/mod_scmi_clock.c
diff options
context:
space:
mode:
authorRonald Cron <ronald.cron@arm.com>2018-06-05 09:31:39 +0200
committerRonald Cron <ronald.cron@arm.com>2018-06-08 11:46:47 +0200
commitb151958dbb2f37383f4d9a1f7802c36008d9fef2 (patch)
treefe20ebfb8c10facbfd028edefe601462ae3ee64c /module/scmi_clock/src/mod_scmi_clock.c
parentfd3027b6fd17a4a33a685adb73f2acfcae9a2ced (diff)
Add support for SGM-775
Co-authored-by: Filipe Rinaldi <filipe.rinaldi@arm.com> Co-authored-by: Paul Beesley <paul.beesley@arm.com> Co-authored-by: Chris Kay <chris.kay@arm.com> Co-authored-by: Elieva Pignat <elieva.pignat@arm.com> Co-authored-by: Pedro Custodio <pedro.krewinkelcustodio@arm.com> Change-Id: Ic7524ad58a7c15d5b055e88a9719b2feee437f1d Signed-off-by: Ronald Cron <ronald.cron@arm.com>
Diffstat (limited to 'module/scmi_clock/src/mod_scmi_clock.c')
-rw-r--r--module/scmi_clock/src/mod_scmi_clock.c763
1 files changed, 763 insertions, 0 deletions
diff --git a/module/scmi_clock/src/mod_scmi_clock.c b/module/scmi_clock/src/mod_scmi_clock.c
new file mode 100644
index 00000000..0e95ea69
--- /dev/null
+++ b/module/scmi_clock/src/mod_scmi_clock.c
@@ -0,0 +1,763 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2018, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * SCMI Clock Management Protocol Support.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <fwk_errno.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <internal/scmi.h>
+#include <internal/scmi_clock.h>
+#include <mod_clock.h>
+#include <mod_scmi.h>
+#include <mod_scmi_clock.h>
+
+struct scmi_clock_ctx {
+ /*! SCMI Clock Module Configuration */
+ const struct mod_scmi_clock_config *config;
+
+ /*! Maximum supported number of pending, asynchronous clock rate changes */
+ uint8_t max_pending_transactions;
+
+ /*!
+ * Pointer to the table of agent descriptors, used to provide per-agent
+ * views of clocks in the system.
+ */
+ const struct mod_scmi_clock_agent *agent_table;
+
+ /* SCMI module API */
+ const struct mod_scmi_from_protocol_api *scmi_api;
+
+ /* Clock module API */
+ const struct mod_clock_api *clock_api;
+};
+
+static int scmi_clock_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_protocol_message_attributes_handler(
+ fwk_id_t service_id, const uint32_t *payload);
+static int scmi_clock_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_rate_get_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_rate_set_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_config_set_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+static int scmi_clock_describe_rates_handler(fwk_id_t service_id,
+ const uint32_t *payload);
+
+/*
+ * Internal variables.
+ */
+static struct scmi_clock_ctx scmi_clock_ctx;
+
+static int (* const handler_table[])(fwk_id_t, const uint32_t *) = {
+ [SCMI_PROTOCOL_VERSION] = scmi_clock_protocol_version_handler,
+ [SCMI_PROTOCOL_ATTRIBUTES] = scmi_clock_protocol_attributes_handler,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ scmi_clock_protocol_message_attributes_handler,
+ [SCMI_CLOCK_ATTRIBUTES] = scmi_clock_attributes_handler,
+ [SCMI_CLOCK_RATE_GET] = scmi_clock_rate_get_handler,
+ [SCMI_CLOCK_RATE_SET] = scmi_clock_rate_set_handler,
+ [SCMI_CLOCK_CONFIG_SET] = scmi_clock_config_set_handler,
+ [SCMI_CLOCK_DESCRIBE_RATES] = scmi_clock_describe_rates_handler,
+};
+
+static const unsigned int payload_size_table[] = {
+ [SCMI_PROTOCOL_VERSION] = 0,
+ [SCMI_PROTOCOL_ATTRIBUTES] = 0,
+ [SCMI_PROTOCOL_MESSAGE_ATTRIBUTES] =
+ sizeof(struct scmi_protocol_message_attributes_a2p),
+ [SCMI_CLOCK_ATTRIBUTES] = sizeof(struct scmi_clock_attributes_a2p),
+ [SCMI_CLOCK_RATE_GET] = sizeof(struct scmi_clock_rate_get_a2p),
+ [SCMI_CLOCK_RATE_SET] = sizeof(struct scmi_clock_rate_set_a2p),
+ [SCMI_CLOCK_CONFIG_SET] = sizeof(struct scmi_clock_config_set_a2p),
+ [SCMI_CLOCK_DESCRIBE_RATES] = sizeof(struct scmi_clock_describe_rates_a2p),
+};
+
+/*
+ * Given a service identifier, retrieve a pointer to its agent's
+ * \c mod_scmi_clock_agent structure within the agent table.
+ */
+static int get_agent_entry(
+ fwk_id_t service_id,
+ const struct mod_scmi_clock_agent **agent)
+{
+ int status;
+ unsigned int agent_id;
+
+ if (agent == NULL)
+ return FWK_E_PARAM;
+
+ status = scmi_clock_ctx.scmi_api->get_agent_id(service_id, &agent_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (agent_id >= scmi_clock_ctx.config->agent_count)
+ return FWK_E_PARAM;
+
+ *agent =
+ (struct mod_scmi_clock_agent *)&scmi_clock_ctx.agent_table[agent_id];
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Given a service identifier and a clock index, retrieve a pointer to the
+ * clock's \c mod_scmi_clock_device structure within the agent's device table.
+ * Optionally, a pointer to the agent may be retrieved as well.
+ */
+static int get_clock_device_entry(
+ fwk_id_t service_id,
+ unsigned int clock_idx,
+ const struct mod_scmi_clock_device **clock_device,
+ const struct mod_scmi_clock_agent **agent)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent_entry;
+
+ if (clock_device == NULL)
+ return FWK_E_PARAM;
+
+ status = get_agent_entry(service_id, &agent_entry);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (clock_idx >= agent_entry->device_count)
+ return FWK_E_RANGE;
+
+ *clock_device = &agent_entry->device_table[clock_idx];
+
+ assert((*clock_device)->permissions != MOD_SCMI_CLOCK_PERM_INVALID);
+ assert(fwk_module_is_valid_element_id((*clock_device)->element_id));
+
+ if (agent != NULL)
+ *agent = agent_entry;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Query the permissions for a service making a request to determine if
+ * the operation is permitted on the given clock device.
+ */
+static int check_service_permission(
+ const struct mod_scmi_clock_device *device,
+ enum mod_scmi_clock_permissions requested_permission,
+ bool *granted)
+{
+ if (granted == NULL)
+ return FWK_E_PARAM;
+
+ *granted = device->permissions & requested_permission;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Protocol Version
+ */
+static int scmi_clock_protocol_version_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ struct scmi_protocol_version_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .version = SCMI_PROTOCOL_VERSION_CLOCK,
+ };
+
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values,
+ sizeof(return_values));
+ return FWK_SUCCESS;
+}
+
+/*
+ * Protocol Attributes
+ */
+static int scmi_clock_protocol_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent;
+ struct scmi_protocol_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ };
+
+ status = get_agent_entry(service_id, &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_GENERIC_ERROR;
+ goto exit;
+ }
+
+ return_values.attributes = SCMI_CLOCK_PROTOCOL_ATTRIBUTES(
+ scmi_clock_ctx.max_pending_transactions,
+ agent->device_count
+ );
+
+exit:
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values,
+ sizeof(return_values));
+ return status;
+}
+
+/*
+ * Protocol Message Attributes
+ */
+static int scmi_clock_protocol_message_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ size_t response_size;
+ const struct scmi_protocol_message_attributes_a2p *parameters;
+ unsigned int message_id;
+ struct scmi_protocol_message_attributes_p2a return_values = {
+ .status = SCMI_SUCCESS,
+ .attributes = 0,
+ };
+
+ parameters = (const struct scmi_protocol_message_attributes_a2p*)
+ payload;
+ message_id = parameters->message_id;
+
+ if ((message_id >= FWK_ARRAY_SIZE(handler_table)) ||
+ (handler_table[message_id] == NULL)) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+exit:
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values, response_size);
+ return FWK_SUCCESS;
+}
+
+/*
+ * Clock Attributes
+ */
+static int scmi_clock_attributes_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent;
+ const struct mod_scmi_clock_device *clock_device;
+ bool service_permission_granted;
+ size_t response_size;
+ enum mod_clock_state clock_state;
+ const struct scmi_clock_attributes_a2p *parameters;
+ struct scmi_clock_attributes_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_clock_attributes_a2p*)payload;
+
+ status = get_clock_device_entry(service_id,
+ parameters->clock_id,
+ &clock_device,
+ &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = check_service_permission(clock_device,
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES, &service_permission_granted);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (!service_permission_granted) {
+ return_values.status = SCMI_DENIED;
+ goto exit;
+ }
+
+ status = scmi_clock_ctx.clock_api->get_state(clock_device->element_id,
+ &clock_state);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values = (struct scmi_clock_attributes_p2a) {
+ .status = SCMI_SUCCESS,
+ .attributes = SCMI_CLOCK_ATTRIBUTES(
+ clock_state == MOD_CLOCK_STATE_RUNNING),
+ };
+
+ strncpy(return_values.clock_name,
+ fwk_module_get_name(clock_device->element_id),
+ sizeof(return_values.clock_name) - 1);
+
+exit:
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values, response_size);
+ return status;
+}
+
+/*
+ * Clock Rate Get
+ */
+static int scmi_clock_rate_get_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent;
+ const struct mod_scmi_clock_device *clock_device;
+ bool service_permission_granted;
+ size_t response_size;
+ uint64_t rate;
+ const struct scmi_clock_rate_get_a2p *parameters;
+ struct scmi_clock_rate_get_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_clock_rate_get_a2p*)payload;
+
+ status = get_clock_device_entry(service_id,
+ parameters->clock_id,
+ &clock_device,
+ &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = check_service_permission(clock_device,
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES, &service_permission_granted);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (!service_permission_granted) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = scmi_clock_ctx.clock_api->get_rate(
+ clock_device->element_id, &rate);
+ return_values.rate[0] = (uint32_t)rate;
+ return_values.rate[1] = (uint32_t)(rate >> 32);
+
+ if (status == FWK_SUCCESS)
+ return_values.status = SCMI_SUCCESS;
+
+exit:
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values, response_size);
+ return status;
+}
+
+/*
+ * Clock Rate Set (Synchronous Only)
+ */
+static int scmi_clock_rate_set_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent;
+ const struct mod_scmi_clock_device *clock_device;
+ bool service_permission_granted;
+ size_t response_size;
+ uint64_t rate;
+ bool round_auto;
+ bool round_up;
+ bool asynchronous;
+ const struct scmi_clock_rate_set_a2p *parameters;
+ struct scmi_clock_rate_set_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_clock_rate_set_a2p*)payload;
+ round_up = parameters->flags & SCMI_CLOCK_RATE_SET_ROUND_UP_MASK;
+ round_auto = parameters->flags & SCMI_CLOCK_RATE_SET_ROUND_AUTO_MASK;
+ asynchronous = parameters->flags & SCMI_CLOCK_RATE_SET_ASYNC_MASK;
+ rate = (uint64_t)parameters->rate[0] +
+ (((uint64_t)parameters->rate[1]) << 32);
+
+ status = get_clock_device_entry(service_id,
+ parameters->clock_id,
+ &clock_device,
+ &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = check_service_permission(clock_device,
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES, &service_permission_granted);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (!service_permission_granted) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ if (asynchronous) {
+ /* Support for async clock set commands not yet implemented */
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = scmi_clock_ctx.clock_api->set_rate(clock_device->element_id, rate,
+ round_auto ? MOD_CLOCK_ROUND_MODE_NEAREST :
+ (round_up ? MOD_CLOCK_ROUND_MODE_UP : MOD_CLOCK_ROUND_MODE_DOWN));
+ if (status == FWK_E_RANGE) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ }
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values.status = SCMI_SUCCESS;
+
+exit:
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values, response_size);
+ return FWK_SUCCESS;
+}
+
+/*
+ * Clock Config Set
+ */
+static int scmi_clock_config_set_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ bool enable;
+ bool service_permission_granted;
+ size_t response_size;
+ const struct scmi_clock_config_set_a2p *parameters;
+ const struct mod_scmi_clock_agent *agent;
+ const struct mod_scmi_clock_device *clock_device;
+ struct scmi_clock_rate_set_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_clock_config_set_a2p*)payload;
+ enable = parameters->attributes & SCMI_CLOCK_CONFIG_SET_ENABLE_MASK;
+
+ status = get_clock_device_entry(service_id,
+ parameters->clock_id,
+ &clock_device,
+ &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = check_service_permission(clock_device,
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES, &service_permission_granted);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (!service_permission_granted) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = scmi_clock_ctx.clock_api->set_state(
+ clock_device->element_id,
+ enable ? MOD_CLOCK_STATE_RUNNING : MOD_CLOCK_STATE_STOPPED);
+ if (status == FWK_E_SUPPORT) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ return_values.status = SCMI_SUCCESS;
+
+exit:
+ response_size = (return_values.status == SCMI_SUCCESS) ?
+ sizeof(return_values) : sizeof(return_values.status);
+ scmi_clock_ctx.scmi_api->respond(service_id, &return_values, response_size);
+ return FWK_SUCCESS;
+}
+
+/*
+ * Clock Describe Rates
+ */
+static int scmi_clock_describe_rates_handler(fwk_id_t service_id,
+ const uint32_t *payload)
+{
+ int status;
+ const struct mod_scmi_clock_agent *agent;
+ const struct mod_scmi_clock_device *clock_device;
+ bool service_permission_granted;
+ unsigned int i;
+ size_t max_payload_size;
+ uint32_t payload_size;
+ uint32_t index;
+ unsigned int rate_count;
+ unsigned int remaining_rates;
+ uint64_t rate;
+ struct scmi_clock_rate scmi_rate;
+ struct scmi_clock_rate clock_range[3];
+ struct mod_clock_info info;
+ const struct scmi_clock_describe_rates_a2p *parameters;
+ struct scmi_clock_describe_rates_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR
+ };
+
+ parameters = (const struct scmi_clock_describe_rates_a2p*)payload;
+ index = parameters->rate_index;
+ payload_size = sizeof(return_values);
+
+ status = get_clock_device_entry(service_id,
+ parameters->clock_id,
+ &clock_device,
+ &agent);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_NOT_FOUND;
+ goto exit;
+ }
+
+ status = check_service_permission(clock_device,
+ MOD_SCMI_CLOCK_PERM_ATTRIBUTES, &service_permission_granted);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (!service_permission_granted) {
+ return_values.status = SCMI_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ /*
+ * Get the maximum payload size to determine how many clock rate entries can
+ * be returned in one response.
+ */
+ status = scmi_clock_ctx.scmi_api->get_max_payload_size(
+ service_id, &max_payload_size);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ status = scmi_clock_ctx.clock_api->get_info(clock_device->element_id,
+ &info);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ if (info.range.rate_type == MOD_CLOCK_RATE_TYPE_DISCRETE) {
+ /* The clock has a discrete list of frequencies */
+
+ if (index >= info.range.rate_count) {
+ return_values.status = SCMI_INVALID_PARAMETERS;
+ goto exit;
+ }
+
+ /* Can at least one entry be returned? */
+ if (SCMI_CLOCK_RATES_MAX(max_payload_size) == 0) {
+ status = FWK_E_SIZE;
+ goto exit;
+ }
+
+ /* The number of rates being returned in this payload is defined as the
+ * smaller of:
+ * - The clock rates that are available between the index and the
+ clock's maximum rate.
+ - The number of rates that can be returned in each payload.
+ */
+ rate_count = FWK_MIN(SCMI_CLOCK_RATES_MAX(max_payload_size),
+ info.range.rate_count - index);
+
+ /*
+ * Because the agent gives a starting index into the clock's rate list
+ * the number of rates remaining is calculated as the number of rates
+ * the clock supports minus the index, with the number of rates being
+ * returned in this payload subtracted.
+ */
+ remaining_rates = (info.range.rate_count - index) - rate_count;
+
+ /* Give the number of rates sent in the message payload */
+ return_values.num_rates_flags =
+ SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS(
+ rate_count,
+ SCMI_CLOCK_RATE_FORMAT_LIST,
+ remaining_rates
+ );
+
+ /* Set each rate entry in the payload to the associated frequency */
+ for (i = 0; i < rate_count; i++,
+ payload_size += sizeof(struct scmi_clock_rate)) {
+ status = scmi_clock_ctx.clock_api->get_rate_from_index(
+ clock_device->element_id,
+ index + i,
+ &rate);
+ if (status != FWK_SUCCESS)
+ goto exit;
+
+ scmi_rate.low = (uint32_t)rate;
+ scmi_rate.high = (uint32_t)(rate >> 32);
+
+ status = scmi_clock_ctx.scmi_api->write_payload(service_id,
+ payload_size, &scmi_rate, sizeof(scmi_rate));
+ if (status != FWK_SUCCESS)
+ goto exit;
+ }
+ } else {
+ /* The clock has a linear stepping */
+
+ /* Is the payload area large enough to return the complete triplet? */
+ if (SCMI_CLOCK_RATES_MAX(max_payload_size) < 3) {
+ status = FWK_E_SIZE;
+ goto exit;
+ }
+
+ return_values.num_rates_flags =
+ SCMI_CLOCK_DESCRIBE_RATES_NUM_RATES_FLAGS(
+ /* Only a single rate is returned */
+ 1,
+ SCMI_CLOCK_RATE_FORMAT_RANGE,
+ /* No further rates are available */
+ 0
+ );
+
+ /* Store the range data in the range entry in the payload */
+ clock_range[0].low = (uint32_t)info.range.min;
+ clock_range[0].high = (uint32_t)(info.range.min >> 32);
+ clock_range[1].low = (uint32_t)info.range.max;
+ clock_range[1].high = (uint32_t)(info.range.max >> 32);
+ clock_range[2].low = (uint32_t)info.range.step;
+ clock_range[2].high = (uint32_t)(info.range.step >> 32);
+
+ status = scmi_clock_ctx.scmi_api->write_payload(service_id,
+ payload_size, &clock_range, sizeof(clock_range));
+ if (status != FWK_SUCCESS)
+ goto exit;
+ payload_size += sizeof(clock_range);
+ }
+
+ return_values.status = SCMI_SUCCESS;
+ status = scmi_clock_ctx.scmi_api->write_payload(service_id, 0,
+ &return_values, sizeof(return_values));
+
+exit:
+ scmi_clock_ctx.scmi_api->respond(service_id,
+ (return_values.status == SCMI_SUCCESS) ?
+ NULL : &return_values.status,
+ (return_values.status == SCMI_SUCCESS) ?
+ payload_size : sizeof(return_values.status));
+ return status;
+}
+
+/*
+ * SCMI module -> SCMI clock module interface
+ */
+static int scmi_clock_get_scmi_protocol_id(fwk_id_t protocol_id,
+ uint8_t *scmi_protocol_id)
+{
+ int status;
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ *scmi_protocol_id = SCMI_PROTOCOL_ID_CLOCK;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_clock_message_handler(fwk_id_t protocol_id, fwk_id_t service_id,
+ const uint32_t *payload, size_t payload_size, unsigned int message_id)
+{
+ int status;
+ int32_t return_value;
+
+ static_assert(FWK_ARRAY_SIZE(handler_table) ==
+ FWK_ARRAY_SIZE(payload_size_table),
+ "[SCMI] Clock management protocol table sizes not consistent");
+ assert(payload != NULL);
+
+ status = fwk_module_check_call(protocol_id);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (message_id >= FWK_ARRAY_SIZE(handler_table)) {
+ return_value = SCMI_NOT_SUPPORTED;
+ goto error;
+ }
+
+ if (payload_size != payload_size_table[message_id]) {
+ return_value = SCMI_PROTOCOL_ERROR;
+ goto error;
+ }
+
+ return handler_table[message_id](service_id, payload);
+
+error:
+ scmi_clock_ctx.scmi_api->respond(service_id,
+ &return_value, sizeof(return_value));
+ return FWK_SUCCESS;
+}
+
+static struct mod_scmi_to_protocol_api scmi_clock_mod_scmi_to_protocol_api = {
+ .get_scmi_protocol_id = scmi_clock_get_scmi_protocol_id,
+ .message_handler = scmi_clock_message_handler
+};
+
+/*
+ * Framework handlers
+ */
+
+static int scmi_clock_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ const struct mod_scmi_clock_config *config =
+ (const struct mod_scmi_clock_config *)data;
+
+ if ((config == NULL) || (config->agent_table == NULL))
+ return FWK_E_PARAM;
+
+ scmi_clock_ctx.config = config;
+ scmi_clock_ctx.max_pending_transactions = config->max_pending_transactions;
+ scmi_clock_ctx.agent_table = config->agent_table;
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_clock_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+
+ if (round == 1)
+ return FWK_SUCCESS;
+
+ status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_SCMI),
+ FWK_ID_API(FWK_MODULE_IDX_SCMI, MOD_SCMI_API_IDX_PROTOCOL),
+ &scmi_clock_ctx.scmi_api);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_CLOCK),
+ FWK_ID_API(FWK_MODULE_IDX_CLOCK, 0), &scmi_clock_ctx.clock_api);
+}
+
+static int scmi_clock_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id, fwk_id_t api_id, const void **api)
+{
+ if (!fwk_id_is_equal(source_id, FWK_ID_MODULE(FWK_MODULE_IDX_SCMI)))
+ return FWK_E_ACCESS;
+
+ *api = &scmi_clock_mod_scmi_to_protocol_api;
+
+ return FWK_SUCCESS;
+}
+
+/* SCMI Clock Management Protocol Definition */
+const struct fwk_module module_scmi_clock = {
+ .name = "SCMI Clock Management Protocol",
+ .api_count = 1,
+ .type = FWK_MODULE_TYPE_PROTOCOL,
+ .init = scmi_clock_init,
+ .bind = scmi_clock_bind,
+ .process_bind_request = scmi_clock_process_bind_request,
+};