diff --git a/module/dvfs/include/mod_dvfs.h b/module/dvfs/include/mod_dvfs.h
index 2c4a672ec67de2bb446aeb309aea3b284debf5db..964445d056358e0b52f231bdd178c35b851e557b 100644
--- a/module/dvfs/include/mod_dvfs.h
+++ b/module/dvfs/include/mod_dvfs.h
@@ -67,6 +67,16 @@ struct mod_dvfs_domain_config {
*/
fwk_id_t clock_id;
+ /*!
+ * \brief Alarm identifier.
+ *
+ * \warning This identifier must refer to an alarm of the \c timer module.
+ */
+ fwk_id_t alarm_id;
+
+ /*! Delay in milliseconds before retrying a request */
+ uint16_t retry_ms;
+
/*! Worst-case transition latency in microseconds */
uint16_t latency;
@@ -149,16 +159,6 @@ struct mod_dvfs_domain_api {
*/
int (*set_frequency)(fwk_id_t domain_id, uint64_t frequency);
- /*!
- * \brief Set the frequency of a domain.
- *
- * \note This function is asynchronous.
- *
- * \param domain_id Element identifier of the domain.
- * \param idx Index of the operating point to transition to.
- */
- int (*set_frequency_async)(fwk_id_t domain_id, uint64_t frequency);
-
/*!
* \brief Get the frequency of a domain.
*
@@ -178,18 +178,6 @@ struct mod_dvfs_domain_api {
int (*set_frequency_limits)(
fwk_id_t domain_id,
const struct mod_dvfs_frequency_limits *limits);
-
- /*!
- * \brief Set the frequency of a domain.
- *
- * \note This function is asynchronous.
- *
- * \param domain_id Element identifier of the domain.
- * \param limits Pointer to the new limits.
- */
- int (*set_frequency_limits_async)(
- fwk_id_t domain_id,
- const struct mod_dvfs_frequency_limits *limits);
};
/*!
@@ -202,23 +190,19 @@ struct mod_dvfs_domain_api {
*/
/*!
- * \brief Set operating point event response parameters.
+ * \brief Get current OPP event response parameters.
*/
-struct mod_dvfs_event_params_set_frequency_response {
- int status; /*!< Status of the request */
-};
+struct mod_dvfs_params_response {
+ /*! Event response status */
+ int status;
-/*!
- * \brief Set limits event response parameters.
- */
-struct mod_dvfs_event_params_set_frequency_limits_response {
- int status; /*!< Status of the request */
+ /*! Event response frequency */
+ uint64_t performance_level;
};
/*!
* \}
*/
-
/*!
* \defgroup GroupDvfsIds Identifiers
* \{
@@ -240,25 +224,18 @@ static const fwk_id_t mod_dvfs_api_id_dvfs =
* \brief Event indices.
*/
enum mod_dvfs_event_idx {
- /*! Event index for mod_dvfs_event_id_set_frequency() */
- MOD_DVFS_EVENT_IDX_SET_FREQUENCY,
-
- /*! Event index for mod_dvfs_event_id_set_frequency_limits() */
- MOD_DVFS_EVENT_IDX_SET_FREQUENCY_LIMITS,
-
- /*! Number of defined events */
- MOD_DVFS_EVENT_IDX_COUNT
+ MOD_DVFS_EVENT_IDX_SET, /*!< Set level/limits */
+ MOD_DVFS_EVENT_IDX_GET_OPP, /*!< Get frequency */
+ MOD_DVFS_EVENT_IDX_COUNT, /*!< event count */
};
-/*! Set operating point event identifier */
-static const fwk_id_t mod_dvfs_event_id_set_frequency =
- FWK_ID_EVENT_INIT(FWK_MODULE_IDX_DVFS, MOD_DVFS_EVENT_IDX_SET_FREQUENCY);
+/*! Set operating point/limits event identifier */
+static const fwk_id_t mod_dvfs_event_id_set =
+ FWK_ID_EVENT_INIT(FWK_MODULE_IDX_DVFS, MOD_DVFS_EVENT_IDX_SET);
-/*! Set frequency limits event identifier */
-static const fwk_id_t mod_dvfs_event_id_set_frequency_limits =
- FWK_ID_EVENT_INIT(
- FWK_MODULE_IDX_DVFS,
- MOD_DVFS_EVENT_IDX_SET_FREQUENCY_LIMITS);
+/*! Get current OPP event identifier */
+static const fwk_id_t mod_dvfs_event_id_get_opp =
+ FWK_ID_EVENT_INIT(FWK_MODULE_IDX_DVFS, MOD_DVFS_EVENT_IDX_GET_OPP);
/*!
* \}
diff --git a/module/dvfs/src/Makefile b/module/dvfs/src/Makefile
index 433bbcc3159cc73cf9bc0abe9186a64de1c04dd2..9e63ba5d9cdf7cc8c09878eb0ba79dc9e9b02580 100644
--- a/module/dvfs/src/Makefile
+++ b/module/dvfs/src/Makefile
@@ -6,10 +6,6 @@
#
BS_LIB_NAME := dvfs
-BS_LIB_SOURCES := \
- mod_dvfs_domain_api.c \
- mod_dvfs_event.c \
- mod_dvfs_module.c \
- mod_dvfs_util.c \
+BS_LIB_SOURCES := mod_dvfs.c
include $(BS_DIR)/lib.mk
diff --git a/module/dvfs/src/mod_dvfs.c b/module/dvfs/src/mod_dvfs.c
new file mode 100644
index 0000000000000000000000000000000000000000..dc3b2eca989f5f8902b103ee6d687dfc3dea3783
--- /dev/null
+++ b/module/dvfs/src/mod_dvfs.c
@@ -0,0 +1,1110 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef BUILD_HAS_MULTITHREADING
+#include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * Maximum number of attempts to complete a request
+ */
+#define DVFS_MAX_RETRIES 4
+
+enum mod_dvfs_internal_event_idx {
+ /* retry request */
+ MOD_DVFS_INTERNAL_EVENT_IDX_RETRY = MOD_DVFS_EVENT_IDX_COUNT,
+ MOD_DVFS_INTERNAL_EVENT_IDX_COUNT,
+};
+
+/* Retry request event identifier */
+static const fwk_id_t mod_dvfs_event_id_retry =
+ FWK_ID_EVENT_INIT(FWK_MODULE_IDX_DVFS,
+ MOD_DVFS_INTERNAL_EVENT_IDX_RETRY);
+
+/*!
+ * \brief Domain states for GET_OPP/SET_OPP.
+ */
+enum mod_dvfs_domain_state {
+ DVFS_DOMAIN_STATE_IDLE = 0,
+
+ /* Waiting for GET_OPP request */
+ DVFS_DOMAIN_GET_OPP,
+
+ /* Waiting for SET_OPP request */
+ DVFS_DOMAIN_SET_OPP,
+
+ /* set_rate() in progress, set_voltage() next */
+ DVFS_DOMAIN_SET_VOLTAGE,
+
+ /* set_voltage() in progress, set_frequency() next */
+ DVFS_DOMAIN_SET_FREQUENCY,
+
+ /* waiting for SET_OPP to complete */
+ DVFS_DOMAIN_SET_OPP_DONE,
+
+ /* waiting for alarm callback to start a retry */
+ DVFS_DOMAIN_STATE_RETRY,
+};
+
+/*!
+ * \brief Request for SET_OPP.
+ */
+struct mod_dvfs_request {
+ /* New operating point data for the request */
+ struct mod_dvfs_opp new_opp;
+
+ /* Response expected for this request */
+ bool response_required;
+
+ /* Retry the request on failure */
+ bool retry_request;
+
+ /* This request requires the event source_id to be set */
+ bool set_source_id;
+
+ /* Retry count */
+ uint8_t num_retries;
+};
+
+/*!
+ * \brief Domain context.
+ */
+struct mod_dvfs_domain_ctx {
+ /* Context Domain ID */
+ fwk_id_t domain_id;
+
+ /* Domain configuration */
+ const struct mod_dvfs_domain_config *config;
+
+ struct {
+ /* Power supply API */
+ const struct mod_psu_device_api *psu;
+
+ /* Clock API */
+ const struct mod_clock_api *clock;
+
+ /* Alarm API for pending requests */
+ const struct mod_timer_alarm_api *alarm_api;
+
+ } apis;
+
+ /* Number of operating points */
+ size_t opp_count;
+
+ /* Current operating point */
+ struct mod_dvfs_opp current_opp;
+
+ /* Current frequency limits */
+ struct mod_dvfs_frequency_limits frequency_limits;
+
+ /* Current request details */
+ struct mod_dvfs_request request;
+
+ /* State */
+ enum mod_dvfs_domain_state state;
+
+ /* Cookie for deferred request response */
+ uint32_t cookie;
+
+ /* Pending request details */
+ struct mod_dvfs_request pending_request;
+
+ /* SET_OPP Request is pending for this domain */
+ bool request_pending;
+};
+
+struct mod_dvfs_ctx {
+ /* Number of DVFS domains */
+ uint32_t dvfs_domain_element_count;
+
+ /* DVFS device context table */
+ struct mod_dvfs_domain_ctx (*domain_ctx)[];
+} dvfs_ctx;
+
+/*
+ * DVFS Helper Functions
+ */
+static struct mod_dvfs_domain_ctx *get_domain_ctx(fwk_id_t domain_id)
+{
+ uint32_t idx = fwk_id_get_element_idx(domain_id);
+
+ if (idx < dvfs_ctx.dvfs_domain_element_count)
+ return &(*dvfs_ctx.domain_ctx)[idx];
+ else
+ return NULL;
+}
+
+static int count_opps(const struct mod_dvfs_opp *opps)
+{
+ const struct mod_dvfs_opp *opp = &opps[0];
+
+ while ((opp->voltage != 0) && (opp->frequency != 0))
+ opp++;
+
+ return opp - &opps[0];
+}
+
+static const struct mod_dvfs_opp *get_opp_for_values(
+ const struct mod_dvfs_domain_ctx *ctx,
+ uint64_t frequency,
+ uint64_t voltage)
+{
+ size_t opp_idx;
+ const struct mod_dvfs_opp *opp;
+
+ /*
+ * If both parameters are zero we have nothing to do.
+ */
+ if ((frequency == 0) && (voltage == 0))
+ return NULL;
+
+ for (opp_idx = 0; opp_idx < ctx->opp_count; opp_idx++) {
+ opp = &ctx->config->opps[opp_idx];
+
+ /* Only check the frequency if requested */
+ if ((frequency != 0) && (opp->frequency != frequency))
+ continue;
+
+ /* Only check the voltage if requested */
+ if ((voltage != 0) && (opp->voltage != voltage))
+ continue;
+
+ return opp;
+ }
+
+ return NULL;
+}
+
+static bool is_opp_within_limits(const struct mod_dvfs_opp *opp,
+ const struct mod_dvfs_frequency_limits *limits)
+{
+ return (opp->frequency >= limits->minimum) &&
+ (opp->frequency <= limits->maximum);
+}
+
+static bool are_limits_valid(const struct mod_dvfs_domain_ctx *ctx,
+ const struct mod_dvfs_frequency_limits *limits)
+{
+ if (limits->minimum > limits->maximum)
+ return false;
+
+ if (get_opp_for_values(ctx, limits->minimum, 0) == NULL)
+ return false;
+
+ if (get_opp_for_values(ctx, limits->maximum, 0) == NULL)
+ return false;
+
+ return true;
+}
+
+static const struct mod_dvfs_opp *adjust_opp_for_new_limits(
+ const struct mod_dvfs_domain_ctx *ctx,
+ const struct mod_dvfs_opp *opp,
+ const struct mod_dvfs_frequency_limits *limits)
+{
+ uint64_t needle;
+
+ if (opp->frequency < limits->minimum)
+ needle = limits->minimum;
+ else if (opp->frequency > limits->maximum)
+ needle = limits->maximum;
+ else {
+ /* No transition necessary */
+ return opp;
+ }
+
+ return get_opp_for_values(ctx, needle, 0);
+}
+
+/*
+ * Helper to create events to process requests asynchronously
+ */
+static int put_event_request(struct mod_dvfs_domain_ctx *ctx,
+ fwk_id_t event_id,
+ enum mod_dvfs_domain_state state)
+{
+ struct fwk_event req;
+
+ req = (struct fwk_event) {
+ .target_id = ctx->domain_id,
+ .id = event_id,
+ .response_requested = ctx->request.response_required,
+ };
+
+ if (ctx->request.set_source_id)
+ req.source_id = ctx->domain_id;
+
+ ctx->state = state;
+
+ return fwk_thread_put_event(&req);
+}
+
+static int dvfs_set_frequency_start(struct mod_dvfs_domain_ctx *ctx,
+ const struct mod_dvfs_opp *new_opp,
+ bool retry_request)
+{
+ ctx->request.new_opp = *new_opp;
+ ctx->request.retry_request = retry_request;
+ ctx->request.response_required = false;
+
+ return put_event_request(ctx, mod_dvfs_event_id_set,
+ DVFS_DOMAIN_SET_OPP);
+}
+
+/*
+ * Handle pending requests
+ */
+static void dvfs_flush_pending_request(struct mod_dvfs_domain_ctx *ctx)
+{
+ if (ctx->request_pending) {
+ ctx->request_pending = false;
+ dvfs_set_frequency_start(ctx, &ctx->pending_request.new_opp,
+ ctx->pending_request.retry_request);
+ }
+ ctx->pending_request = (struct mod_dvfs_request){ 0 };
+}
+
+static void alarm_callback(uintptr_t param)
+{
+ struct fwk_event req;
+ struct mod_dvfs_domain_ctx *ctx =
+ (struct mod_dvfs_domain_ctx *)param;
+
+ req = (struct fwk_event) {
+ .target_id = ctx->domain_id,
+ .source_id = ctx->domain_id,
+ .id = mod_dvfs_event_id_retry,
+ .response_requested = ctx->pending_request.response_required,
+ };
+
+ fwk_thread_put_event(&req);
+}
+
+static int dvfs_handle_pending_request(struct mod_dvfs_domain_ctx *ctx)
+{
+ int status = FWK_SUCCESS;
+
+ if (ctx->state == DVFS_DOMAIN_STATE_RETRY)
+ return FWK_SUCCESS;
+
+ if (ctx->config->retry_ms > 0) {
+ status = ctx->apis.alarm_api->start(ctx->config->alarm_id,
+ ctx->config->retry_ms, MOD_TIMER_ALARM_TYPE_ONCE,
+ alarm_callback, (uintptr_t)ctx);
+ if (status == FWK_SUCCESS)
+ ctx->state = DVFS_DOMAIN_STATE_RETRY;
+ } else {
+ /*
+ * If this domain does not have a timeout configured we start
+ * processing the request immediately.
+ */
+ dvfs_flush_pending_request(ctx);
+ }
+ return status;
+}
+
+static int dvfs_create_pending_level_request(struct mod_dvfs_domain_ctx *ctx,
+ const struct mod_dvfs_opp *new_opp, bool retry_request)
+{
+
+ if (ctx->request_pending) {
+ if ((new_opp->frequency == ctx->pending_request.new_opp.frequency) &&
+ (new_opp->voltage == ctx->pending_request.new_opp.voltage))
+ return FWK_SUCCESS;
+ } else {
+ if ((new_opp->frequency == ctx->current_opp.frequency) &&
+ (new_opp->voltage == ctx->current_opp.voltage)) {
+ return FWK_SUCCESS;
+
+ ctx->pending_request.num_retries = 0;
+ ctx->pending_request.set_source_id = false;
+ ctx->pending_request.response_required = false;
+ ctx->request_pending = true;
+ }
+ }
+
+ ctx->pending_request.new_opp = *new_opp;
+
+ /*
+ * Set the retry flag only if we have to retry the request. Otherwise don't
+ * overwrite it.
+ */
+ if (retry_request)
+ ctx->pending_request.retry_request = retry_request;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * DVFS module synchronous API functions
+ */
+static int dvfs_get_sustained_opp(fwk_id_t domain_id, struct mod_dvfs_opp *opp)
+{
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ ctx = get_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ if (ctx->config->sustained_idx >= ctx->opp_count)
+ return FWK_E_PARAM;
+
+ *opp = ctx->config->opps[ctx->config->sustained_idx];
+
+ return FWK_SUCCESS;
+}
+
+static int dvfs_get_nth_opp(fwk_id_t domain_id,
+ size_t n,
+ struct mod_dvfs_opp *opp)
+{
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ if (opp == NULL)
+ return FWK_E_PARAM;
+
+ ctx = get_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ if (n >= ctx->opp_count)
+ return FWK_E_PARAM;
+
+ *opp = ctx->config->opps[n];
+
+ return FWK_SUCCESS;
+}
+
+static int dvfs_get_opp_count(fwk_id_t domain_id, size_t *opp_count)
+{
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ if (opp_count == NULL)
+ return FWK_E_PARAM;
+
+ ctx = get_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ *opp_count = ctx->opp_count;
+
+ return FWK_SUCCESS;
+}
+
+static int dvfs_get_latency(fwk_id_t domain_id, uint16_t *latency)
+{
+ const struct mod_dvfs_domain_ctx *ctx;
+
+ if (latency == NULL)
+ return FWK_E_PARAM;
+
+ ctx = get_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ *latency = ctx->config->latency;
+
+ return FWK_SUCCESS;
+}
+
+static int dvfs_get_frequency_limits(fwk_id_t domain_id,
+ struct mod_dvfs_frequency_limits *limits)
+{
+ struct mod_dvfs_domain_ctx *ctx;
+
+ if (limits == NULL)
+ return FWK_E_PARAM;
+
+ ctx = get_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ *limits = ctx->frequency_limits;
+ return FWK_SUCCESS;
+}
+
+/*
+ * dvfs_get_current_opp() may be either synchronous or asynchronous
+ */
+static int dvfs_get_current_opp(fwk_id_t domain_id, struct mod_dvfs_opp *opp)
+{
+ int status;
+ struct mod_dvfs_domain_ctx *ctx;
+
+ if (opp == NULL)
+ return FWK_E_PARAM;
+
+ ctx = get_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ if (ctx->current_opp.frequency != 0) {
+ opp->frequency = ctx->current_opp.frequency;
+ opp->voltage = ctx->current_opp.voltage;
+ return FWK_SUCCESS;
+ }
+
+ if (ctx->state != DVFS_DOMAIN_STATE_IDLE)
+ return FWK_E_BUSY;
+
+ ctx->request.response_required = true;
+ status = put_event_request(ctx, mod_dvfs_event_id_get_opp,
+ DVFS_DOMAIN_GET_OPP);
+ if (status == FWK_SUCCESS) {
+ /*
+ * We return FWK_PENDING here to indicate to the caller that the
+ * result of the request is pending and will arrive later through
+ * an event.
+ */
+ return FWK_PENDING;
+ }
+
+ return status;
+}
+
+/*
+ * DVFS module asynchronous API functions
+ */
+static int dvfs_set_frequency(fwk_id_t domain_id, uint64_t frequency)
+{
+ struct mod_dvfs_domain_ctx *ctx;
+ const struct mod_dvfs_opp *new_opp;
+
+ ctx = get_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ /* Only accept frequencies that exist in the operating point table */
+ new_opp = get_opp_for_values(ctx, frequency, 0);
+ if (new_opp == NULL)
+ return FWK_E_RANGE;
+
+ if (!is_opp_within_limits(new_opp, &ctx->frequency_limits))
+ return FWK_E_RANGE;
+
+ if (ctx->state != DVFS_DOMAIN_STATE_IDLE)
+ return dvfs_create_pending_level_request(ctx, new_opp, false);
+
+ if ((new_opp->frequency == ctx->current_opp.frequency) &&
+ (new_opp->voltage == ctx->current_opp.voltage))
+ return FWK_SUCCESS;
+
+ return dvfs_set_frequency_start(ctx, new_opp, false);
+}
+
+static int dvfs_set_frequency_limits(fwk_id_t domain_id,
+ const struct mod_dvfs_frequency_limits *limits)
+{
+ struct mod_dvfs_domain_ctx *ctx;
+ const struct mod_dvfs_opp *new_opp;
+
+ ctx = get_domain_ctx(domain_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ if (!are_limits_valid(ctx, limits))
+ return FWK_E_PARAM;
+
+ new_opp = adjust_opp_for_new_limits(ctx, &ctx->current_opp, limits);
+ if (new_opp == NULL)
+ return FWK_E_PARAM;
+
+ ctx->frequency_limits = *limits;
+
+ if ((new_opp->frequency == ctx->current_opp.frequency) &&
+ (new_opp->voltage == ctx->current_opp.voltage)) {
+ return FWK_SUCCESS;
+ }
+
+ if (ctx->state != DVFS_DOMAIN_STATE_IDLE)
+ return dvfs_create_pending_level_request(ctx, new_opp, true);
+
+ return dvfs_set_frequency_start(ctx, new_opp, true);
+}
+
+static const struct mod_dvfs_domain_api mod_dvfs_domain_api = {
+ .get_current_opp = dvfs_get_current_opp,
+ .get_sustained_opp = dvfs_get_sustained_opp,
+ .get_nth_opp = dvfs_get_nth_opp,
+ .get_opp_count = dvfs_get_opp_count,
+ .get_latency = dvfs_get_latency,
+ .set_frequency = dvfs_set_frequency,
+ .get_frequency_limits = dvfs_get_frequency_limits,
+ .set_frequency_limits = dvfs_set_frequency_limits,
+};
+
+/*
+ * DVFS utility functions
+ */
+
+/*
+ * DVFS Request Complete handling.
+ */
+static void dvfs_complete_respond(struct mod_dvfs_domain_ctx *ctx,
+ struct fwk_event *resp_event,
+ int req_status)
+{
+ int status;
+ struct fwk_event read_req_event;
+ struct mod_dvfs_params_response *resp_params;
+ bool return_opp;
+
+ if ((ctx->state == DVFS_DOMAIN_GET_OPP) && (req_status == FWK_SUCCESS))
+ return_opp = true;
+
+ if (ctx->cookie != 0) {
+ /*
+ * The request was handled asynchronously, retrieve
+ * the delayed_response and return it to the caller
+ * with the data.
+ */
+ resp_params = (struct mod_dvfs_params_response *)
+ &read_req_event.params;
+ status = fwk_thread_get_delayed_response(ctx->domain_id,
+ ctx->cookie,
+ &read_req_event);
+
+ if (status == FWK_SUCCESS) {
+ resp_params->status = req_status;
+ if (return_opp)
+ resp_params->performance_level = ctx->current_opp.frequency;
+ fwk_thread_put_event(&read_req_event);
+ }
+ ctx->cookie = 0;
+ } else if (resp_event != NULL) {
+ /*
+ * The request is being handled synchronously, return
+ * the data to the caller in the resp_event.
+ */
+ resp_params =
+ (struct mod_dvfs_params_response *)resp_event->params;
+ resp_params->status = req_status;
+ if (return_opp)
+ resp_params->performance_level = ctx->current_opp.frequency;
+ }
+}
+
+static int dvfs_complete(struct mod_dvfs_domain_ctx *ctx,
+ struct fwk_event *resp_event,
+ int req_status)
+{
+ int status = req_status;
+
+ if (ctx->request.response_required) {
+ /*
+ * If the DVFS request requires a response we send it now, no retries
+ * are attempted.
+ */
+ dvfs_complete_respond(ctx, resp_event, req_status);
+
+ } else if ((req_status != FWK_SUCCESS) && ctx->request.retry_request) {
+ /*
+ * No response required, request has failed, a retry is necessary.
+ */
+ if (ctx->request.num_retries++ < DVFS_MAX_RETRIES) {
+ ctx->pending_request.retry_request = ctx->request.retry_request;
+ ctx->pending_request.num_retries = ctx->request.num_retries;
+ if (!ctx->request_pending) {
+ ctx->pending_request.new_opp = ctx->request.new_opp;
+ ctx->request_pending = true;
+ }
+ }
+ }
+
+ /*
+ * Now we need to start processing the pending request if any,
+ * note that we do not set the state to DOMAIN_STATE_IDLE
+ * here to prevent another request being processed ahead of this one.
+ */
+ if (ctx->request_pending) {
+ dvfs_handle_pending_request(ctx);
+ } else {
+ /*
+ * The request has completed and there are no pending requests.
+ * Clean up the domain ctx here.
+ */
+ ctx->pending_request = (struct mod_dvfs_request){ 0 };
+ ctx->request = (struct mod_dvfs_request){ 0 };
+ ctx->state = DVFS_DOMAIN_STATE_IDLE;
+ }
+
+ return status;
+}
+
+/*
+ * The SET_OPP() request has successfully completed the first step,
+ * reading the voltage.
+ */
+static int dvfs_handle_set_opp(struct mod_dvfs_domain_ctx *ctx,
+ uint64_t voltage)
+{
+ int status = FWK_SUCCESS;
+
+ if (ctx->request.new_opp.voltage > voltage) {
+ /*
+ * Current < request, increase voltage then set frequency
+ */
+ status = ctx->apis.psu->set_voltage(
+ ctx->config->psu_id, ctx->request.new_opp.voltage);
+
+ if (status == FWK_PENDING) {
+ ctx->state = DVFS_DOMAIN_SET_FREQUENCY;
+ return status;
+ }
+
+ if (status != FWK_SUCCESS)
+ return dvfs_complete(ctx, NULL, status);
+
+ /*
+ * Voltage set successsfully, continue to set the frequency
+ */
+ status = ctx->apis.clock->set_rate(
+ ctx->config->clock_id,
+ ctx->request.new_opp.frequency,
+ MOD_CLOCK_ROUND_MODE_NONE);
+
+ if (status == FWK_PENDING) {
+ ctx->state = DVFS_DOMAIN_SET_OPP_DONE;
+ return status;
+ }
+ } else if (ctx->request.new_opp.voltage < voltage) {
+ /*
+ * Current > request, decrease frequency then set voltage
+ */
+ status = ctx->apis.clock->set_rate(
+ ctx->config->clock_id,
+ ctx->request.new_opp.frequency,
+ MOD_CLOCK_ROUND_MODE_NONE);
+
+ if (status == FWK_PENDING) {
+ ctx->state = DVFS_DOMAIN_SET_VOLTAGE;
+ return status;
+ }
+
+ if (status != FWK_SUCCESS)
+ return dvfs_complete(ctx, NULL, status);
+
+ /*
+ * Clock set_rate() completed successfully, continue to set_voltage()
+ */
+ status = ctx->apis.psu->set_voltage(
+ ctx->config->psu_id, ctx->request.new_opp.voltage);
+
+ if (status == FWK_PENDING) {
+ ctx->state = DVFS_DOMAIN_SET_OPP_DONE;
+ return status;
+ }
+ } else if (ctx->current_opp.frequency == 0) {
+ /*
+ * At startup the voltage may be set without the frequency having
+ * been set. In this case we must set the frequency regardless of
+ * the voltage.
+ */
+ status = ctx->apis.clock->set_rate(
+ ctx->config->clock_id,
+ ctx->request.new_opp.frequency,
+ MOD_CLOCK_ROUND_MODE_NONE);
+
+ if (status == FWK_PENDING) {
+ ctx->state = DVFS_DOMAIN_SET_OPP_DONE;
+ return status;
+ }
+ }
+
+ /*
+ * SET_OPP() completed, return to caller.
+ */
+ if (status == FWK_SUCCESS)
+ ctx->current_opp = ctx->request.new_opp;
+
+ return dvfs_complete(ctx, NULL, status);
+}
+
+/*
+ * The current voltage has been read. This is the first step of a SET_OPP()
+ * request and the only step of a GET_OPP() request. It may have been handled
+ * synchronously or asynchronously. Note that resp_event will only be set
+ * by a GET_OPP(), it will always be NULL for SET_OPP().
+ */
+static int dvfs_handle_psu_get_voltage_resp(struct mod_dvfs_domain_ctx *ctx,
+ struct fwk_event *resp_event,
+ int req_status,
+ uint64_t voltage)
+{
+ const struct mod_dvfs_opp *opp;
+
+ if (req_status != FWK_SUCCESS)
+ return dvfs_complete(ctx, resp_event, req_status);
+
+ if (ctx->state == DVFS_DOMAIN_SET_OPP)
+ return dvfs_handle_set_opp(ctx, voltage);
+
+ /*
+ * We have the actual voltage, get the frequency from the
+ * corresponding OPP in the domain context table.
+ */
+ opp = get_opp_for_values(ctx, 0, voltage);
+ if (opp == NULL)
+ return dvfs_complete(ctx, resp_event, FWK_E_DEVICE);
+
+ /*
+ * We have successfully found the frequency, save it in the domain context.
+ */
+ ctx->current_opp.voltage = voltage;
+ ctx->current_opp.frequency = opp->frequency;
+
+ /*
+ * This is a GET_OPP(), we are done, return the frequency to caller
+ */
+ return dvfs_complete(ctx, resp_event, FWK_SUCCESS);
+}
+
+/*
+ * Note that dvfs_handle_psu_set_voltage_resp() is only called after an
+ * asynchronous set_voltage() operation.
+ */
+static int dvfs_handle_psu_set_voltage_resp(struct mod_dvfs_domain_ctx *ctx,
+ const struct fwk_event *event)
+{
+ int status = FWK_SUCCESS;
+ struct mod_psu_driver_response *psu_response =
+ (struct mod_psu_driver_response *)event->params;
+
+ if (psu_response->status != FWK_SUCCESS)
+ return dvfs_complete(ctx, NULL, psu_response->status);
+
+ if (ctx->state == DVFS_DOMAIN_SET_FREQUENCY) {
+ status = ctx->apis.clock->set_rate(
+ ctx->config->clock_id,
+ ctx->request.new_opp.frequency,
+ MOD_CLOCK_ROUND_MODE_NONE);
+ if (status == FWK_PENDING) {
+ ctx->state = DVFS_DOMAIN_SET_OPP_DONE;
+ return status;
+ }
+ } else if (ctx->state == DVFS_DOMAIN_SET_OPP_DONE) {
+ status = FWK_SUCCESS;
+ } else
+ status = FWK_E_DEVICE;
+
+ /*
+ * SET_OPP() completed, return to caller.
+ */
+ if (status == FWK_SUCCESS)
+ ctx->current_opp = ctx->request.new_opp;
+
+ return dvfs_complete(ctx, NULL, status);
+}
+
+/*
+ * Note that dvfs_handle_clk_set_freq_resp() is only called after an
+ * asynchronous set_rate() operation.
+ */
+static int dvfs_handle_clk_set_freq_resp(struct mod_dvfs_domain_ctx *ctx,
+ const struct fwk_event *event)
+{
+ int status;
+ struct mod_clock_driver_resp_params *clock_response =
+ (struct mod_clock_driver_resp_params *)event->params;
+
+ if (clock_response->status != FWK_SUCCESS)
+ return dvfs_complete(ctx, NULL, clock_response->status);
+
+ if (ctx->state == DVFS_DOMAIN_SET_VOLTAGE) {
+ /*
+ * Clock set_rate() completed successfully, continue to set_voltage()
+ */
+ status = ctx->apis.psu->set_voltage(
+ ctx->config->psu_id,
+ ctx->request.new_opp.voltage);
+ if (status == FWK_PENDING) {
+ ctx->state = DVFS_DOMAIN_SET_OPP_DONE;
+ return status;
+ }
+
+ } else if (ctx->state == DVFS_DOMAIN_SET_OPP_DONE) {
+ status = FWK_SUCCESS;
+ } else
+ status = FWK_E_DEVICE;
+
+ /*
+ * SET_OPP() completed, return to caller.
+ */
+ if (status == FWK_SUCCESS)
+ ctx->current_opp = ctx->request.new_opp;
+
+ return dvfs_complete(ctx, NULL, status);
+}
+
+/*
+ * DVFS Module Framework Support
+ */
+static int mod_dvfs_process_event(const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ int status = FWK_SUCCESS;
+ struct mod_dvfs_domain_ctx *ctx;
+ struct mod_psu_driver_response *psu_response;
+ uint64_t voltage;
+
+ ctx = get_domain_ctx(event->target_id);
+ if (ctx == NULL)
+ return FWK_E_PARAM;
+
+ /*
+ * local DVFS event from dvfs_get_current_opp()
+ */
+ if (fwk_id_is_equal(event->id, mod_dvfs_event_id_get_opp)) {
+ status = ctx->apis.psu->get_voltage(ctx->config->psu_id,
+ &ctx->request.new_opp.voltage);
+ if (status == FWK_PENDING) {
+ ctx->cookie = event->cookie;
+ resp_event->is_delayed_response = true;
+ return FWK_SUCCESS;
+ }
+
+ /*
+ * Handle get_voltage() synchronously
+ */
+ return dvfs_handle_psu_get_voltage_resp(ctx,
+ resp_event, status, ctx->request.new_opp.voltage);
+ }
+
+ /*
+ * local DVFS event from dvfs_set_frequency()
+ */
+ if (fwk_id_is_equal(event->id, mod_dvfs_event_id_set)) {
+ if (ctx->current_opp.voltage != 0) {
+ voltage = ctx->current_opp.voltage;
+ status = FWK_SUCCESS;
+ } else {
+ status = ctx->apis.psu->get_voltage(ctx->config->psu_id,
+ &voltage);
+ if (status == FWK_PENDING)
+ return FWK_SUCCESS;
+ }
+
+ /*
+ * Handle get_voltage() synchronously
+ */
+ return dvfs_handle_psu_get_voltage_resp(ctx, NULL,
+ status, voltage);
+ }
+
+ /*
+ * local DVFS event from dvfs_flush_pending_request()
+ */
+ if (fwk_id_is_equal(event->id, mod_dvfs_event_id_retry)) {
+ ctx->request.set_source_id = false;
+ return dvfs_set_frequency_start(ctx, &ctx->pending_request.new_opp,
+ ctx->pending_request.retry_request);
+ }
+
+ /*
+ * response event from PSU get_voltage()
+ */
+ if (fwk_id_is_equal(event->id, mod_psu_event_id_get_voltage)) {
+ /*
+ * Handle get_voltage() asynchronously, cookie will have been saved
+ * above so we can safely discard the resp_event.
+ */
+ psu_response = (struct mod_psu_driver_response *)event->params;
+ return dvfs_handle_psu_get_voltage_resp(ctx, NULL,
+ psu_response->status, psu_response->voltage);
+ }
+
+ /*
+ * response event from SET_OPP() PSU set_voltage()
+ */
+ if (fwk_id_is_equal(event->id, mod_psu_event_id_set_voltage)) {
+ /*
+ * Handle set_voltage() asynchronously, no response required for
+ * a SET_OPP() request so resp_event discarded.
+ */
+ return dvfs_handle_psu_set_voltage_resp(ctx, event);
+ }
+
+ /*
+ * response event from SET_OPP() Clock set_rate()
+ */
+ if (fwk_id_is_equal(event->id, mod_clock_event_id_request)) {
+ /*
+ * Handle set_frequency() asynchronously, no response required for
+ * a SET_OPP() request so resp_event discarded.
+ */
+ return dvfs_handle_clk_set_freq_resp(ctx, event);
+ }
+
+ return FWK_E_PARAM;
+}
+
+/*
+ * Module framework support
+ */
+
+/*
+ * The DVFS voltage/frequency must be set to the sustained OPP at startup
+ * for each domain.
+ */
+static int dvfs_start(fwk_id_t id)
+{
+ int status;
+ struct mod_dvfs_opp sustained_opp;
+ struct mod_dvfs_domain_ctx *ctx;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return FWK_SUCCESS;
+
+ status = dvfs_get_sustained_opp(id, &sustained_opp);
+ if (status == FWK_SUCCESS) {
+ ctx = get_domain_ctx(id);
+ ctx->request.set_source_id = true;
+ ctx->request.num_retries = 0;
+ dvfs_set_frequency_start(ctx, &sustained_opp, true);
+ }
+
+ return status;
+}
+
+static int dvfs_init(fwk_id_t module_id, unsigned int element_count,
+ const void *data)
+{
+ dvfs_ctx.domain_ctx = fwk_mm_calloc(element_count,
+ sizeof((*dvfs_ctx.domain_ctx)[0]));
+ if (dvfs_ctx.domain_ctx == NULL)
+ return FWK_E_NOMEM;
+
+ dvfs_ctx.dvfs_domain_element_count = element_count;
+
+ return FWK_SUCCESS;
+}
+
+static int
+dvfs_element_init(fwk_id_t domain_id,
+ unsigned int sub_element_count,
+ const void *data)
+{
+ struct mod_dvfs_domain_ctx *ctx = get_domain_ctx(domain_id);
+
+ fwk_assert(sub_element_count == 0);
+
+ ctx->domain_id = domain_id;
+
+ /* Initialize the configuration */
+ ctx->config = data;
+ fwk_assert(ctx->config->opps != NULL);
+
+ /* Initialize the context */
+ ctx->opp_count = count_opps(ctx->config->opps);
+ fwk_assert(ctx->opp_count > 0);
+
+ /* Frequency limits default to the minimum and maximum available */
+ ctx->frequency_limits = (struct mod_dvfs_frequency_limits) {
+ .minimum = ctx->config->opps[0].frequency,
+ .maximum = ctx->config->opps[ctx->opp_count - 1].frequency,
+ };
+
+ return FWK_SUCCESS;
+}
+
+static int
+dvfs_bind_element(fwk_id_t domain_id, unsigned int round)
+{
+ int status;
+ const struct mod_dvfs_domain_ctx *ctx = get_domain_ctx(domain_id);
+
+ /* Only handle the first round */
+ if (round > 0)
+ return FWK_SUCCESS;
+
+ /* Bind to the power supply module */
+ status = fwk_module_bind(ctx->config->psu_id, mod_psu_api_id_device,
+ &ctx->apis.psu);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ /* Bind to the clock module */
+ status = fwk_module_bind(ctx->config->clock_id,
+ FWK_ID_API(FWK_MODULE_IDX_CLOCK, 0), &ctx->apis.clock);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ /* Bind to the alarm HAL if required */
+ if (ctx->config->retry_ms > 0) {
+ status = fwk_module_bind(ctx->config->alarm_id, MOD_TIMER_API_ID_ALARM,
+ &ctx->apis.alarm_api);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int
+dvfs_bind(fwk_id_t id, unsigned int round)
+{
+ /* Bind our elements */
+ if (fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return dvfs_bind_element(id, round);
+
+ return FWK_SUCCESS;
+}
+
+static int
+dvfs_process_bind_request_module(fwk_id_t source_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ /* Only expose the module API */
+ if (!fwk_id_is_equal(api_id, mod_dvfs_api_id_dvfs))
+ return FWK_E_PARAM;
+
+ /* We don't do any permissions management */
+ *api = &mod_dvfs_domain_api;
+
+ return FWK_SUCCESS;
+}
+
+static int
+dvfs_process_bind_request(fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ /* Only allow binding to the module */
+ if (!fwk_id_is_equal(target_id, fwk_module_id_dvfs))
+ return FWK_E_PARAM;
+
+ return dvfs_process_bind_request_module(source_id, api_id, api);
+}
+
+/* Module description */
+const struct fwk_module module_dvfs = {
+ .name = "DVFS",
+ .type = FWK_MODULE_TYPE_HAL,
+ .init = dvfs_init,
+ .element_init = dvfs_element_init,
+ .start = dvfs_start,
+ .bind = dvfs_bind,
+ .process_bind_request = dvfs_process_bind_request,
+ .process_event = mod_dvfs_process_event,
+ .api_count = MOD_DVFS_API_IDX_COUNT,
+ .event_count = MOD_DVFS_INTERNAL_EVENT_IDX_COUNT,
+};
diff --git a/module/dvfs/src/mod_dvfs_domain_api.c b/module/dvfs/src/mod_dvfs_domain_api.c
deleted file mode 100644
index 7e76442358feb1a39a25e0195e2c8e6697a25cff..0000000000000000000000000000000000000000
--- a/module/dvfs/src/mod_dvfs_domain_api.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Arm SCP/MCP Software
- * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-static const struct mod_dvfs_opp *get_opp_for_values(
- const struct mod_dvfs_domain_ctx *ctx,
- uint64_t frequency,
- uint64_t voltage)
-{
- size_t opp_idx;
- const struct mod_dvfs_opp *opp;
-
- /* A value of zero indicates the parameter should be ignored */
- assert((frequency != 0) || (voltage != 0));
-
- for (opp_idx = 0; opp_idx < ctx->opp_count; opp_idx++) {
- opp = &ctx->config->opps[opp_idx];
-
- /* Only check the frequency if requested */
- if ((frequency != 0) && (opp->frequency != frequency))
- continue;
-
- /* Only check the voltage if requested */
- if ((voltage != 0) && (opp->voltage != voltage))
- continue;
-
- return opp;
- }
-
- return NULL;
-}
-
-static bool is_opp_within_limits(
- const struct mod_dvfs_opp *opp,
- const struct mod_dvfs_frequency_limits *limits)
-{
- return (opp->frequency >= limits->minimum) &&
- (opp->frequency <= limits->maximum);
-}
-
-static bool are_limits_valid(
- const struct mod_dvfs_domain_ctx *ctx,
- const struct mod_dvfs_frequency_limits *limits)
-{
- if (limits->minimum > limits->maximum)
- return false;
-
- if (get_opp_for_values(ctx, limits->minimum, 0) == NULL)
- return false;
-
- if (get_opp_for_values(ctx, limits->maximum, 0) == NULL)
- return false;
-
- return true;
-}
-
-static const struct mod_dvfs_opp *adjust_opp_for_new_limits(
- const struct mod_dvfs_domain_ctx *ctx,
- const struct mod_dvfs_opp *opp,
- const struct mod_dvfs_frequency_limits *limits)
-{
- uint64_t needle;
-
- if (opp->frequency < limits->minimum)
- needle = limits->minimum;
- else if (opp->frequency > limits->maximum)
- needle = limits->maximum;
- else {
- /* No transition necessary */
- return opp;
- }
-
- return get_opp_for_values(ctx, needle, 0);
-}
-
-static int api_get_current_opp(fwk_id_t domain_id, struct mod_dvfs_opp *opp)
-{
- int status;
- const struct mod_dvfs_domain_ctx *ctx;
-
- assert(opp != NULL);
-
- ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
- if (ctx == NULL)
- return FWK_E_PARAM;
-
- status = __mod_dvfs_get_current_opp(ctx, opp);
- if (status != FWK_SUCCESS)
- return status;
-
- return FWK_SUCCESS;
-}
-
-int api_get_sustained_opp(fwk_id_t domain_id, struct mod_dvfs_opp *opp)
-{
- const struct mod_dvfs_domain_ctx *ctx;
-
- assert(opp != NULL);
-
- ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
- if (ctx == NULL)
- return FWK_E_PARAM;
-
- *opp = ctx->config->opps[ctx->config->sustained_idx];
-
- return FWK_SUCCESS;
-}
-
-int api_get_nth_opp(fwk_id_t domain_id,
- size_t n,
- struct mod_dvfs_opp *opp)
-{
- const struct mod_dvfs_domain_ctx *ctx;
-
- assert(opp != NULL);
-
- ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
- if (ctx == NULL)
- return FWK_E_PARAM;
-
- if (n >= ctx->opp_count)
- return FWK_E_PARAM;
-
- *opp = ctx->config->opps[n];
-
- return FWK_SUCCESS;
-}
-
-static int api_get_opp_count(fwk_id_t domain_id, size_t *opp_count)
-{
- const struct mod_dvfs_domain_ctx *ctx;
-
- assert(opp_count != NULL);
-
- ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
- if (ctx == NULL)
- return FWK_E_PARAM;
-
- *opp_count = ctx->opp_count;
-
- return FWK_SUCCESS;
-}
-
-int api_get_latency(fwk_id_t domain_id, uint16_t *latency)
-{
- const struct mod_dvfs_domain_ctx *ctx;
-
- assert(latency != NULL);
-
- ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
- if (ctx == NULL)
- return FWK_E_PARAM;
-
- *latency = ctx->config->latency;
-
- return FWK_SUCCESS;
-}
-
-static int api_set_frequency(fwk_id_t domain_id, uint64_t frequency)
-{
- int status;
- const struct mod_dvfs_domain_ctx *ctx;
- const struct mod_dvfs_opp *new_opp;
-
- ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
- if (ctx == NULL)
- return FWK_E_PARAM;
-
- /* Only accept frequencies that exist in the operating point table */
- new_opp = get_opp_for_values(ctx, frequency, 0);
- if (new_opp == NULL)
- return FWK_E_RANGE;
-
- if (!is_opp_within_limits(new_opp, &ctx->frequency_limits))
- return FWK_E_RANGE;
-
- status = __mod_dvfs_set_opp(ctx, new_opp);
- if (status != FWK_SUCCESS)
- return status;
-
- return FWK_SUCCESS;
-}
-
-static int api_set_frequency_async(fwk_id_t domain_id, uint64_t frequency)
-{
- return FWK_E_SUPPORT;
-}
-
-int api_get_frequency_limits(
- fwk_id_t domain_id,
- struct mod_dvfs_frequency_limits *limits)
-{
- const struct mod_dvfs_domain_ctx *ctx;
-
- ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
- if (ctx == NULL)
- return FWK_E_PARAM;
-
- *limits = ctx->frequency_limits;
-
- return FWK_SUCCESS;
-}
-
-static int api_set_frequency_limits(
- fwk_id_t domain_id,
- const struct mod_dvfs_frequency_limits *limits)
-{
- int status;
- struct mod_dvfs_domain_ctx *ctx;
- struct mod_dvfs_opp current_opp;
- const struct mod_dvfs_opp *new_opp;
-
- ctx = __mod_dvfs_get_valid_domain_ctx(domain_id);
- if (ctx == NULL)
- return FWK_E_PARAM;
-
- if (!are_limits_valid(ctx, limits))
- return FWK_E_PARAM;
-
- status = __mod_dvfs_get_current_opp(ctx, ¤t_opp);
- if (status != FWK_SUCCESS)
- return status;
-
- new_opp = adjust_opp_for_new_limits(ctx, ¤t_opp, limits);
- status = __mod_dvfs_set_opp(ctx, new_opp);
- if (status != FWK_SUCCESS)
- return status;
-
- ctx->frequency_limits = *limits;
-
- return FWK_SUCCESS;
-}
-
-static int api_set_frequency_limits_async(
- fwk_id_t domain_id,
- const struct mod_dvfs_frequency_limits *limits)
-{
- return FWK_E_SUPPORT;
-}
-
-const struct mod_dvfs_domain_api __mod_dvfs_domain_api = {
- .get_current_opp = api_get_current_opp,
- .get_sustained_opp = api_get_sustained_opp,
- .get_nth_opp = api_get_nth_opp,
- .get_opp_count = api_get_opp_count,
- .get_latency = api_get_latency,
- .set_frequency = api_set_frequency,
- .set_frequency_async = api_set_frequency_async,
- .get_frequency_limits = api_get_frequency_limits,
- .set_frequency_limits = api_set_frequency_limits,
- .set_frequency_limits_async = api_set_frequency_limits_async,
-};
diff --git a/module/dvfs/src/mod_dvfs_domain_api_private.h b/module/dvfs/src/mod_dvfs_domain_api_private.h
deleted file mode 100644
index 5c1b28187bfae3c82c398632342b05a772b4e9db..0000000000000000000000000000000000000000
--- a/module/dvfs/src/mod_dvfs_domain_api_private.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Arm SCP/MCP Software
- * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef MOD_DVFS_DOMAIN_API_PRIVATE_H
-#define MOD_DVFS_DOMAIN_API_PRIVATE_H
-
-#include
-
-/* Module API implementation */
-extern const struct mod_dvfs_domain_api __mod_dvfs_domain_api;
-
-#endif /* MOD_DVFS_DOMAIN_API_PRIVATE_H */
diff --git a/module/dvfs/src/mod_dvfs_event.c b/module/dvfs/src/mod_dvfs_event.c
deleted file mode 100644
index f0224d99e6a8f1832e2ee652766d5961b19fc384..0000000000000000000000000000000000000000
--- a/module/dvfs/src/mod_dvfs_event.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Arm SCP/MCP Software
- * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include
-#include
-
-static int event_set_opp(
- const struct fwk_event *event,
- struct fwk_event *response)
-{
- return FWK_E_SUPPORT;
-}
-
-static int event_set_frequency_limits(
- const struct fwk_event *event,
- struct fwk_event *response)
-{
- return FWK_E_SUPPORT;
-}
-
-int __mod_dvfs_process_event(
- const struct fwk_event *event,
- struct fwk_event *response)
-{
- typedef int (*handler_t)(
- const struct fwk_event *event,
- struct fwk_event *response);
-
- static const handler_t handlers[] = {
- [MOD_DVFS_EVENT_IDX_SET_FREQUENCY] = event_set_opp,
- [MOD_DVFS_EVENT_IDX_SET_FREQUENCY_LIMITS] = event_set_frequency_limits,
- };
-
- handler_t handler;
-
- /* Ensure we have a handler implemented for this event */
- handler = handlers[fwk_id_get_event_idx(event->id)];
- if (handler == NULL)
- return FWK_E_PARAM;
-
- /* Delegate event handling to the relevant handler */
- return handler(event, response);
-}
diff --git a/module/dvfs/src/mod_dvfs_event_private.h b/module/dvfs/src/mod_dvfs_event_private.h
deleted file mode 100644
index cb6c30c949349ab02d27f6b5280b3191f785d3ec..0000000000000000000000000000000000000000
--- a/module/dvfs/src/mod_dvfs_event_private.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Arm SCP/MCP Software
- * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef MOD_DVFS_EVENT_PRIVATE_H
-#define MOD_DVFS_EVENT_PRIVATE_H
-
-#include
-
-/* Event handler */
-int __mod_dvfs_process_event(
- const struct fwk_event *event,
- struct fwk_event *response);
-
-#endif /* MOD_DVFS_EVENT_PRIVATE_H */
diff --git a/module/dvfs/src/mod_dvfs_module.c b/module/dvfs/src/mod_dvfs_module.c
deleted file mode 100644
index e2de6a60be4f325d08e14414764466b4fd16551b..0000000000000000000000000000000000000000
--- a/module/dvfs/src/mod_dvfs_module.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Arm SCP/MCP Software
- * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-static struct mod_dvfs_domain_ctx (*domain_ctx)[];
-
-static int count_opps(const struct mod_dvfs_opp *opps)
-{
- const struct mod_dvfs_opp *opp = &opps[0];
-
- while ((opp->voltage != 0) && (opp->frequency != 0))
- opp++;
-
- return opp - &opps[0];
-}
-
-static struct mod_dvfs_domain_ctx *get_domain_ctx(fwk_id_t domain_id)
-{
- unsigned int element_idx = fwk_id_get_element_idx(domain_id);
-
- return &(*domain_ctx)[element_idx];
-}
-
-static int dvfs_init(
- fwk_id_t module_id,
- unsigned int element_count,
- const void *data)
-{
- domain_ctx = fwk_mm_calloc(
- element_count,
- sizeof((*domain_ctx)[0]));
- if (domain_ctx == NULL)
- return FWK_E_NOMEM;
-
- return FWK_SUCCESS;
-}
-
-static int dvfs_element_init(
- fwk_id_t domain_id,
- unsigned int sub_element_count,
- const void *data)
-{
- struct mod_dvfs_domain_ctx *ctx = get_domain_ctx(domain_id);
-
- assert(sub_element_count == 0);
-
- /* Initialize the configuration */
- ctx->config = data;
- assert(ctx->config->opps != NULL);
-
- /* Initialize the context */
- ctx->opp_count = count_opps(ctx->config->opps);
- assert(ctx->opp_count > 0);
-
- /* Frequency limits default to the minimum and maximum available */
- ctx->frequency_limits = (struct mod_dvfs_frequency_limits) {
- .minimum = ctx->config->opps[0].frequency,
- .maximum = ctx->config->opps[ctx->opp_count - 1].frequency,
- };
-
- ctx->suspended_opp = ctx->config->opps[ctx->config->sustained_idx];
-
- return FWK_SUCCESS;
-}
-
-static int dvfs_bind_element(fwk_id_t domain_id, unsigned int round)
-{
- int status;
- const struct mod_dvfs_domain_ctx *ctx =
- get_domain_ctx(domain_id);
-
- /* Only handle the first round */
- if (round > 0)
- return FWK_SUCCESS;
-
- /* Bind to the power supply module */
- status = fwk_module_bind(
- ctx->config->psu_id, mod_psu_api_id_device, &ctx->apis.psu);
- if (status != FWK_SUCCESS)
- return FWK_E_PANIC;
-
- /* Bind to the clock module */
- status = fwk_module_bind(
- ctx->config->clock_id,
- FWK_ID_API(FWK_MODULE_IDX_CLOCK, 0),
- &ctx->apis.clock);
- if (status != FWK_SUCCESS)
- return FWK_E_PANIC;
-
- return FWK_SUCCESS;
-}
-
-static int dvfs_bind(fwk_id_t id, unsigned int round)
-{
- /* We only need to handle binding our elements */
- if (fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
- return dvfs_bind_element(id, round);
-
- return FWK_SUCCESS;
-}
-
-static int dvfs_process_bind_request_module(
- fwk_id_t source_id,
- fwk_id_t api_id,
- const void **api)
-{
- /* Only expose the module API */
- if (!fwk_id_is_equal(api_id, mod_dvfs_api_id_dvfs))
- return FWK_E_PARAM;
-
- /* We don't do any permissions management */
- *api = &__mod_dvfs_domain_api;
-
- return FWK_SUCCESS;
-}
-
-static int dvfs_process_bind_request(
- fwk_id_t source_id,
- fwk_id_t target_id,
- fwk_id_t api_id,
- const void **api)
-{
- /* Only allow binding to the module */
- if (!fwk_id_is_equal(target_id, fwk_module_id_dvfs))
- return FWK_E_PARAM;
-
- return dvfs_process_bind_request_module(source_id, api_id, api);
-}
-
-static int dvfs_start(fwk_id_t id)
-{
- int status;
- const struct mod_dvfs_domain_ctx *ctx;
-
- if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
- return FWK_SUCCESS;
-
- ctx = get_domain_ctx(id);
-
- /* Register for clock state notifications */
- status = fwk_notification_subscribe(
- mod_clock_notification_id_state_changed,
- ctx->config->clock_id,
- id);
- if (status != FWK_SUCCESS)
- return status;
-
- return fwk_notification_subscribe(
- mod_clock_notification_id_state_change_pending,
- ctx->config->clock_id,
- id);
-}
-
-static int dvfs_notify_system_state_transition_suspend(fwk_id_t domain_id)
-{
- struct mod_dvfs_domain_ctx *ctx =
- get_domain_ctx(domain_id);
-
- return __mod_dvfs_get_current_opp(ctx, &ctx->suspended_opp);
-}
-
-static int dvfs_notify_system_state_transition_resume(fwk_id_t domain_id)
-{
- const struct mod_dvfs_domain_ctx *ctx =
- get_domain_ctx(domain_id);
-
- return __mod_dvfs_set_opp(ctx, &ctx->suspended_opp);
-}
-
-static int dvfs_process_notification(
- const struct fwk_event *event,
- struct fwk_event *resp_event)
-{
- struct clock_notification_params *params;
- struct clock_state_change_pending_resp_params *resp_params;
-
- assert(
- fwk_id_is_equal(
- event->id,
- mod_clock_notification_id_state_changed) ||
- fwk_id_is_equal(
- event->id,
- mod_clock_notification_id_state_change_pending));
- assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT));
-
- params = (struct clock_notification_params *)event->params;
-
- if (fwk_id_is_equal(event->id, mod_clock_notification_id_state_changed)) {
- if (params->new_state == MOD_CLOCK_STATE_RUNNING)
- return dvfs_notify_system_state_transition_resume(event->target_id);
- } else if (params->new_state == MOD_CLOCK_STATE_STOPPED) {
- /* DVFS has received the pending change notification */
- resp_params =
- (struct clock_state_change_pending_resp_params *)resp_event->params;
- resp_params->status = FWK_SUCCESS;
-
- return dvfs_notify_system_state_transition_suspend(event->target_id);
- }
-
- return FWK_SUCCESS;
-}
-
-struct mod_dvfs_domain_ctx *__mod_dvfs_get_valid_domain_ctx(fwk_id_t domain_id)
-{
- return get_domain_ctx(domain_id);
-}
-
-/* Module description */
-const struct fwk_module module_dvfs = {
- .name = "DVFS",
- .type = FWK_MODULE_TYPE_HAL,
- .init = dvfs_init,
- .element_init = dvfs_element_init,
- .bind = dvfs_bind,
- .process_bind_request = dvfs_process_bind_request,
- .process_event = __mod_dvfs_process_event,
- .start = dvfs_start,
- .process_notification = dvfs_process_notification,
- .api_count = MOD_DVFS_API_IDX_COUNT,
- .event_count = MOD_DVFS_EVENT_IDX_COUNT,
-};
diff --git a/module/dvfs/src/mod_dvfs_module_private.h b/module/dvfs/src/mod_dvfs_module_private.h
deleted file mode 100644
index 6dc39ed83ad1c84a022fcc08b614463bb935a90c..0000000000000000000000000000000000000000
--- a/module/dvfs/src/mod_dvfs_module_private.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Arm SCP/MCP Software
- * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef MOD_DVFS_MODULE_PRIVATE_H
-#define MOD_DVFS_MODULE_PRIVATE_H
-
-#include
-#include
-#include
-
-/* Domain context */
-struct mod_dvfs_domain_ctx {
- /* Domain configuration */
- const struct mod_dvfs_domain_config *config;
-
- struct {
- /* Power supply API */
- const struct mod_psu_device_api *psu;
-
- /* Clock API */
- const struct mod_clock_api *clock;
- } apis;
-
- /* Number of operating points */
- size_t opp_count;
-
- /* Operating point prior to domain suspension */
- struct mod_dvfs_opp suspended_opp;
-
- /* Current operating point limits */
- struct mod_dvfs_frequency_limits frequency_limits;
-};
-
-struct mod_dvfs_domain_ctx *__mod_dvfs_get_valid_domain_ctx(fwk_id_t domain_id);
-
-#endif /* MOD_DVFS_MODULE_PRIVATE_H */
diff --git a/module/dvfs/src/mod_dvfs_private.h b/module/dvfs/src/mod_dvfs_private.h
deleted file mode 100644
index bdd6ceb0bbcc343532041aaef55e0d2c8d12d6a6..0000000000000000000000000000000000000000
--- a/module/dvfs/src/mod_dvfs_private.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Arm SCP/MCP Software
- * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef MOD_DVFS_PRIVATE_H
-#define MOD_DVFS_PRIVATE_H
-
-#include
-#include
-#include
-#include
-
-#endif /* MOD_DVFS_PRIVATE_H */
diff --git a/module/dvfs/src/mod_dvfs_util.c b/module/dvfs/src/mod_dvfs_util.c
deleted file mode 100644
index a9982dffbc023cc1f41bb111b8586402fdae7757..0000000000000000000000000000000000000000
--- a/module/dvfs/src/mod_dvfs_util.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Arm SCP/MCP Software
- * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include
-#include
-
-int __mod_dvfs_set_opp(
- const struct mod_dvfs_domain_ctx *ctx,
- const struct mod_dvfs_opp *new_opp)
-{
- int status;
- struct mod_dvfs_opp current_opp;
-
- status = __mod_dvfs_get_current_opp(ctx, ¤t_opp);
- if (status != FWK_SUCCESS)
- return status;
-
- if (new_opp->voltage > current_opp.voltage) {
- /* Raise the voltage before raising the frequency */
- status = ctx->apis.psu->set_voltage(
- ctx->config->psu_id,
- new_opp->voltage);
- if (status != FWK_SUCCESS)
- return FWK_E_DEVICE;
- }
-
- if (new_opp->frequency != current_opp.frequency) {
- status = ctx->apis.clock->set_rate(
- ctx->config->clock_id,
- new_opp->frequency,
- MOD_CLOCK_ROUND_MODE_NONE);
- if (status != FWK_SUCCESS)
- return FWK_E_DEVICE;
- }
-
- if (new_opp->voltage < current_opp.voltage) {
- /* Lower the voltage after lowering the frequency */
- status = ctx->apis.psu->set_voltage(
- ctx->config->psu_id,
- new_opp->voltage);
- if (status != FWK_SUCCESS)
- return FWK_E_DEVICE;
- }
-
- return FWK_SUCCESS;
-}
-
-int __mod_dvfs_get_current_opp(
- const struct mod_dvfs_domain_ctx *ctx,
- struct mod_dvfs_opp *opp)
-{
- int status;
-
- status = ctx->apis.clock->get_rate(
- ctx->config->clock_id,
- &opp->frequency);
- if (status != FWK_SUCCESS)
- return FWK_E_DEVICE;
-
- status = ctx->apis.psu->get_voltage(
- ctx->config->psu_id,
- &opp->voltage);
- if (status != FWK_SUCCESS)
- return FWK_E_DEVICE;
-
- return FWK_SUCCESS;
-}
diff --git a/module/dvfs/src/mod_dvfs_util_private.h b/module/dvfs/src/mod_dvfs_util_private.h
deleted file mode 100644
index 7d8d37d0eaf715c235dffee9af9565540f3b25ba..0000000000000000000000000000000000000000
--- a/module/dvfs/src/mod_dvfs_util_private.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Arm SCP/MCP Software
- * Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef MOD_DVFS_UTIL_PRIVATE_H
-#define MOD_DVFS_UTIL_PRIVATE_H
-
-#include
-#include
-
-int __mod_dvfs_set_opp(
- const struct mod_dvfs_domain_ctx *ctx,
- const struct mod_dvfs_opp *new_opp);
-
-int __mod_dvfs_get_current_opp(
- const struct mod_dvfs_domain_ctx *ctx,
- struct mod_dvfs_opp *opp);
-
-#endif /* MOD_DVFS_PRIVATE_H */
diff --git a/module/scmi_perf/src/mod_scmi_perf.c b/module/scmi_perf/src/mod_scmi_perf.c
index 9edf57860f084cc2baf89603b2b61d2cc5d86174..e990b5b608eb7d66221cc381a81318ff12816c92 100644
--- a/module/scmi_perf/src/mod_scmi_perf.c
+++ b/module/scmi_perf/src/mod_scmi_perf.c
@@ -11,8 +11,12 @@
#include
#include
#include
+#include
#include
#include
+#ifdef BUILD_HAS_MULTITHREADING
+#include
+#endif
#include
#include
#include
@@ -20,20 +24,6 @@
#include
#include
-struct scmi_perf_ctx {
- /* SCMI Performance Module Configuration */
- const struct mod_scmi_perf_config *config;
-
- /* Number of power domains */
- unsigned int domain_count;
-
- /* SCMI module API */
- const struct mod_scmi_from_protocol_api *scmi_api;
-
- /* DVFS module API */
- const struct mod_dvfs_domain_api *dvfs_api;
-};
-
static int scmi_perf_protocol_version_handler(
fwk_id_t service_id, const uint32_t *payload);
static int scmi_perf_protocol_attributes_handler(
@@ -53,8 +43,6 @@ static int scmi_perf_limits_set_handler(
static int scmi_perf_limits_get_handler(
fwk_id_t service_id, const uint32_t *payload);
-static struct scmi_perf_ctx scmi_perf_ctx;
-
static int (*handler_table[])(fwk_id_t, const uint32_t *) = {
[SCMI_PROTOCOL_VERSION] =
scmi_perf_protocol_version_handler,
@@ -95,6 +83,53 @@ static unsigned int payload_size_table[] = {
sizeof(struct scmi_perf_limits_get_a2p),
};
+struct perf_operations {
+ /*
+ * Service identifier currently requesting operation.
+ * A 'none' value means that there is no pending request.
+ */
+ fwk_id_t service_id;
+};
+
+
+struct scmi_perf_ctx {
+ /* SCMI Performance Module Configuration */
+ const struct mod_scmi_perf_config *config;
+
+ /* Number of power domains */
+ uint32_t domain_count;
+
+ /* SCMI module API */
+ const struct mod_scmi_from_protocol_api *scmi_api;
+
+ /* DVFS module API */
+ const struct mod_dvfs_domain_api *dvfs_api;
+
+ /* Pointer to a table of operations */
+ struct perf_operations *perf_ops_table;
+};
+
+static struct scmi_perf_ctx scmi_perf_ctx;
+
+/* Event indices */
+enum scmi_perf_event_idx {
+ SCMI_PERF_EVENT_IDX_LEVEL_GET_REQUEST,
+ SCMI_PERF_EVENT_IDX_LIMITS_GET_REQUEST,
+ SCMI_PERF_EVENT_IDX_COUNT,
+};
+
+struct scmi_perf_event_parameters {
+ fwk_id_t domain_id;
+};
+
+static const fwk_id_t scmi_perf_get_level =
+ FWK_ID_EVENT_INIT(FWK_MODULE_IDX_SCMI_PERF,
+ SCMI_PERF_EVENT_IDX_LEVEL_GET_REQUEST);
+
+static const fwk_id_t scmi_perf_get_limits =
+ FWK_ID_EVENT_INIT(FWK_MODULE_IDX_SCMI_PERF,
+ SCMI_PERF_EVENT_IDX_LIMITS_GET_REQUEST);
+
/*
* Protocol command handlers
*/
@@ -135,15 +170,13 @@ static int scmi_perf_protocol_message_attributes_handler(fwk_id_t service_id,
const uint32_t *payload)
{
const struct scmi_protocol_message_attributes_a2p *parameters;
- unsigned int message_id;
struct scmi_protocol_message_attributes_p2a return_values;
- parameters =
- (const struct scmi_protocol_message_attributes_a2p *)payload;
- message_id = parameters->message_id;
+ parameters = (const struct scmi_protocol_message_attributes_a2p *)
+ payload;
- if ((message_id < FWK_ARRAY_SIZE(handler_table)) &&
- (handler_table[message_id] != NULL)) {
+ if ((parameters->message_id < FWK_ARRAY_SIZE(handler_table)) &&
+ (handler_table[parameters->message_id] != NULL)) {
return_values = (struct scmi_protocol_message_attributes_p2a) {
.status = SCMI_SUCCESS,
.attributes = 0, /* All commands have an attributes value of 0 */
@@ -165,15 +198,16 @@ static int scmi_perf_domain_attributes_handler(fwk_id_t service_id,
unsigned int agent_id;
const struct mod_scmi_perf_domain_config *domain;
const struct scmi_perf_domain_attributes_a2p *parameters;
- struct scmi_perf_domain_attributes_p2a return_values;
uint32_t permissions;
fwk_id_t domain_id;
struct mod_dvfs_opp opp;
+ struct scmi_perf_domain_attributes_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ };
- return_values.status = SCMI_GENERIC_ERROR;
+ parameters = (const struct scmi_perf_domain_attributes_a2p *)payload;
/* Validate the domain identifier */
- parameters = (const struct scmi_perf_domain_attributes_a2p *)payload;
if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
status = FWK_SUCCESS;
return_values.status = SCMI_NOT_FOUND;
@@ -188,7 +222,7 @@ static int scmi_perf_domain_attributes_handler(fwk_id_t service_id,
domain = &(*scmi_perf_ctx.config->domains)[parameters->domain_id];
permissions = (*domain->permissions)[agent_id];
- domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id),
+ domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id);
status = scmi_perf_ctx.dvfs_api->get_sustained_opp(domain_id, &opp);
if (status != FWK_SUCCESS)
goto exit;
@@ -206,8 +240,8 @@ static int scmi_perf_domain_attributes_handler(fwk_id_t service_id,
};
/* Copy the domain name into the mailbox */
- strncpy((char *)return_values.name, fwk_module_get_name(domain_id),
- sizeof(return_values.name) - 1);
+ strncpy((char *)return_values.name,
+ fwk_module_get_name(domain_id), sizeof(return_values.name) - 1);
exit:
scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
@@ -223,7 +257,6 @@ static int scmi_perf_describe_levels_handler(fwk_id_t service_id,
int status;
size_t max_payload_size;
const struct scmi_perf_describe_levels_a2p *parameters;
- struct scmi_perf_describe_levels_p2a return_values;
fwk_id_t domain_id;
struct scmi_perf_level perf_level;
unsigned int num_levels, level_index, level_index_max;
@@ -231,8 +264,10 @@ static int scmi_perf_describe_levels_handler(fwk_id_t service_id,
size_t opp_count;
struct mod_dvfs_opp opp;
uint16_t latency;
+ struct scmi_perf_describe_levels_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ };
- return_values.status = SCMI_GENERIC_ERROR;
payload_size = sizeof(return_values);
status = scmi_perf_ctx.scmi_api->get_max_payload_size(service_id,
@@ -242,13 +277,12 @@ static int scmi_perf_describe_levels_handler(fwk_id_t service_id,
status = (SCMI_PERF_LEVELS_MAX(max_payload_size) > 0) ?
FWK_SUCCESS : FWK_E_SIZE;
- assert(status == FWK_SUCCESS);
if (status != FWK_SUCCESS)
goto exit;
+ parameters = (const struct scmi_perf_describe_levels_a2p *)payload;
/* Validate the domain identifier */
- parameters = (const struct scmi_perf_describe_levels_a2p *)payload;
if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
return_values.status = SCMI_NOT_FOUND;
@@ -326,12 +360,13 @@ static int scmi_perf_limits_set_handler(fwk_id_t service_id,
unsigned int agent_id;
const struct mod_scmi_perf_domain_config *domain;
const struct scmi_perf_limits_set_a2p *parameters;
- struct scmi_perf_limits_set_p2a return_values;
uint32_t permissions;
-
- return_values.status = SCMI_GENERIC_ERROR;
+ struct scmi_perf_limits_set_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ };
parameters = (const struct scmi_perf_limits_set_a2p *)payload;
+
if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
status = FWK_SUCCESS;
return_values.status = SCMI_NOT_FOUND;
@@ -358,23 +393,21 @@ static int scmi_perf_limits_set_handler(fwk_id_t service_id,
goto exit;
}
- /* Execute the transition asynchronously */
status = scmi_perf_ctx.dvfs_api->set_frequency_limits(
FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id),
&((struct mod_dvfs_frequency_limits) {
- .minimum = parameters->range_min,
- .maximum = parameters->range_max
- }));
- if (status != FWK_SUCCESS) {
- status = FWK_SUCCESS;
- return_values.status = SCMI_OUT_OF_RANGE;
+ .minimum = parameters->range_min,
+ .maximum = parameters->range_max
+ }));
- goto exit;
- }
+ /*
+ * Return immediately to the caller, fire-and-forget.
+ */
- return_values = (struct scmi_perf_limits_set_p2a) {
- .status = SCMI_SUCCESS,
- };
+ if ((status == FWK_SUCCESS) || (status == FWK_PENDING))
+ return_values.status = SCMI_SUCCESS;
+ else
+ return_values.status = SCMI_OUT_OF_RANGE;
exit:
scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
@@ -388,11 +421,12 @@ static int scmi_perf_limits_get_handler(fwk_id_t service_id,
const uint32_t *payload)
{
int status;
+ fwk_id_t domain_id;
const struct scmi_perf_limits_get_a2p *parameters;
- struct scmi_perf_limits_get_p2a return_values;
- struct mod_dvfs_frequency_limits limits;
-
- return_values.status = SCMI_GENERIC_ERROR;
+ struct scmi_perf_event_parameters *evt_params;
+ struct scmi_perf_limits_get_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ };
parameters = (const struct scmi_perf_limits_get_a2p *)payload;
if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
@@ -402,21 +436,42 @@ static int scmi_perf_limits_get_handler(fwk_id_t service_id,
goto exit;
}
- status = scmi_perf_ctx.dvfs_api->get_frequency_limits(
- FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id), &limits);
- if (status != FWK_SUCCESS)
+ domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id);
+
+ /* Check if there is already a request pending for this domain */
+ if (!fwk_id_is_equal(
+ scmi_perf_ctx.perf_ops_table[parameters->domain_id].service_id,
+ FWK_ID_NONE)){
+ return_values.status = SCMI_BUSY;
+ status = FWK_SUCCESS;
+
goto exit;
+ }
- return_values = (struct scmi_perf_limits_get_p2a) {
- .status = SCMI_SUCCESS,
- .range_min = (uint32_t)limits.minimum,
- .range_max = (uint32_t)limits.maximum
+ /* The get_limits request is processed within the event being generated */
+ struct fwk_event event = {
+ .target_id = fwk_module_id_scmi_perf,
+ .id = scmi_perf_get_limits,
};
+ evt_params = (struct scmi_perf_event_parameters *)event.params;
+ evt_params->domain_id = domain_id;
+
+ status = fwk_thread_put_event(&event);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_GENERIC_ERROR;
+
+ goto exit;
+ }
+
+ /* Store service identifier to indicate there is a pending request */
+ scmi_perf_ctx.perf_ops_table[parameters->domain_id].service_id = service_id;
+
+ return FWK_SUCCESS;
+
exit:
scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
- (return_values.status == SCMI_SUCCESS) ?
- sizeof(return_values) : sizeof(return_values.status));
+ sizeof(return_values.status));
return status;
}
@@ -428,12 +483,13 @@ static int scmi_perf_level_set_handler(fwk_id_t service_id,
unsigned int agent_id;
const struct mod_scmi_perf_domain_config *domain;
const struct scmi_perf_level_set_a2p *parameters;
- struct scmi_perf_level_set_p2a return_values;
uint32_t permissions;
-
- return_values.status = SCMI_GENERIC_ERROR;
+ struct scmi_perf_level_set_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ };
parameters = (const struct scmi_perf_level_set_a2p *)payload;
+
if (parameters->domain_id >= scmi_perf_ctx.domain_count) {
status = FWK_SUCCESS;
return_values.status = SCMI_NOT_FOUND;
@@ -454,21 +510,17 @@ static int scmi_perf_level_set_handler(fwk_id_t service_id,
goto exit;
}
- /* Execute the transition synchronously */
status = scmi_perf_ctx.dvfs_api->set_frequency(
- FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id),
- parameters->performance_level);
-
- if (status == FWK_E_RANGE) {
- status = FWK_SUCCESS;
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id),
+ (uint64_t)parameters->performance_level);
+
+ /*
+ * Return immediately to the caller, fire-and-forget.
+ */
+ if ((status == FWK_SUCCESS) || (status == FWK_PENDING))
+ return_values.status = SCMI_SUCCESS;
+ else if (status == FWK_E_RANGE)
return_values.status = SCMI_OUT_OF_RANGE;
- } else if (status != FWK_SUCCESS) {
- return_values.status = SCMI_GENERIC_ERROR;
- } else {
- return_values = (struct scmi_perf_level_set_p2a) {
- .status = SCMI_SUCCESS,
- };
- }
exit:
scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
@@ -483,8 +535,10 @@ static int scmi_perf_level_get_handler(fwk_id_t service_id,
{
int status;
const struct scmi_perf_level_get_a2p *parameters;
- struct scmi_perf_level_get_p2a return_values;
- struct mod_dvfs_opp opp;
+ struct scmi_perf_event_parameters *evt_params;
+ struct scmi_perf_level_get_p2a return_values = {
+ .status = SCMI_GENERIC_ERROR,
+ };
return_values.status = SCMI_GENERIC_ERROR;
@@ -496,17 +550,38 @@ static int scmi_perf_level_get_handler(fwk_id_t service_id,
goto exit;
}
- status = scmi_perf_ctx.dvfs_api->get_current_opp(
- FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS, parameters->domain_id), &opp);
- if (status != FWK_SUCCESS)
+ /* Check if there is already a request pending for this domain */
+ if (!fwk_id_is_equal(
+ scmi_perf_ctx.perf_ops_table[parameters->domain_id].service_id,
+ FWK_ID_NONE)){
+ return_values.status = SCMI_BUSY;
+ status = FWK_SUCCESS;
+
goto exit;
+ }
- /* Return the frequency as the performance level */
- return_values = (struct scmi_perf_level_get_p2a) {
- .status = SCMI_SUCCESS,
- .performance_level = (uint32_t)opp.frequency,
+ /* The get_level request is processed within the event being generated */
+ struct fwk_event event = {
+ .target_id = fwk_module_id_scmi_perf,
+ .id = scmi_perf_get_level,
};
+ evt_params = (struct scmi_perf_event_parameters *)event.params;
+ evt_params->domain_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_DVFS,
+ parameters->domain_id);
+
+ status = fwk_thread_put_event(&event);
+ if (status != FWK_SUCCESS) {
+ return_values.status = SCMI_GENERIC_ERROR;
+
+ goto exit;
+ }
+
+ /* Store service identifier to indicate there is a pending request */
+ scmi_perf_ctx.perf_ops_table[parameters->domain_id].service_id = service_id;
+
+ return FWK_SUCCESS;
+
exit:
scmi_perf_ctx.scmi_api->respond(service_id, &return_values,
(return_values.status == SCMI_SUCCESS) ?
@@ -561,6 +636,33 @@ static struct mod_scmi_to_protocol_api scmi_perf_mod_scmi_to_protocol_api = {
.message_handler = scmi_perf_message_handler
};
+/*
+ * Static helpers for responding to SCMI.
+ */
+static void scmi_perf_respond(
+ void *return_values,
+ fwk_id_t domain_id,
+ int size)
+{
+ int idx = fwk_id_get_element_idx(domain_id);
+ fwk_id_t service_id;
+
+ /*
+ * The service identifier used for the response is retrieved from the
+ * domain operations table.
+ */
+ service_id = scmi_perf_ctx.perf_ops_table[idx].service_id;
+
+ scmi_perf_ctx.scmi_api->respond(service_id,
+ return_values, size);
+
+ /*
+ * Set the service identifier to 'none' to indicate the domain is
+ * available again.
+ */
+ scmi_perf_ctx.perf_ops_table[idx].service_id = FWK_ID_NONE;
+}
+
/*
* Framework handlers
*/
@@ -568,6 +670,7 @@ static int scmi_perf_init(fwk_id_t module_id, unsigned int element_count,
const void *data)
{
int return_val;
+ int i;
const struct mod_scmi_perf_config *config =
(const struct mod_scmi_perf_config *)data;
@@ -579,9 +682,18 @@ static int scmi_perf_init(fwk_id_t module_id, unsigned int element_count,
if (return_val <= 0)
return FWK_E_SUPPORT;
+ scmi_perf_ctx.perf_ops_table = fwk_mm_calloc(return_val,
+ sizeof(struct perf_operations));
+ if (scmi_perf_ctx.perf_ops_table == NULL)
+ return FWK_E_NOMEM;
+
scmi_perf_ctx.config = config;
scmi_perf_ctx.domain_count = return_val;
+ /* Initialize table */
+ for (i = 0; i < return_val; i++)
+ scmi_perf_ctx.perf_ops_table[i].service_id = FWK_ID_NONE;
+
return FWK_SUCCESS;
}
@@ -612,12 +724,135 @@ static int scmi_perf_process_bind_request(fwk_id_t source_id,
return FWK_SUCCESS;
}
+
+/*
+ * Handle a request for get_level/limits.
+ */
+static int process_request_event(const struct fwk_event *event)
+{
+ int status;
+ struct mod_dvfs_frequency_limits limits;
+ struct scmi_perf_event_parameters *params;
+ struct scmi_perf_level_get_p2a return_values_level;
+ struct scmi_perf_limits_get_p2a return_values_limits;
+ struct mod_dvfs_opp opp;
+
+ /* request event to DVFS HAL */
+ if (fwk_id_is_equal(event->id, scmi_perf_get_level)) {
+ params = (struct scmi_perf_event_parameters *)event->params;
+
+ status = scmi_perf_ctx.dvfs_api->get_current_opp(params->domain_id,
+ &opp);
+ if (status == FWK_SUCCESS) {
+ /* DVFS value is ready */
+ return_values_level = (struct scmi_perf_level_get_p2a) {
+ .status = SCMI_SUCCESS,
+ .performance_level = (uint32_t)opp.frequency,
+ };
+
+ scmi_perf_respond(&return_values_level, params->domain_id,
+ sizeof(return_values_level));
+
+ return status;
+ } else if (status == FWK_PENDING) {
+ /* DVFS value will be provided through a response event */
+ return FWK_SUCCESS;
+ } else {
+ return_values_level = (struct scmi_perf_level_get_p2a) {
+ .status = SCMI_HARDWARE_ERROR,
+ };
+
+ scmi_perf_respond(&return_values_level, params->domain_id,
+ sizeof(return_values_level.status));
+
+ return FWK_E_DEVICE;
+ }
+ }
+
+ if (fwk_id_is_equal(event->id, scmi_perf_get_limits)) {
+ params = (struct scmi_perf_event_parameters *)event->params;
+
+ status = scmi_perf_ctx.dvfs_api->get_frequency_limits(params->domain_id,
+ &limits);
+ if (status == FWK_SUCCESS) {
+ /* DVFS value is ready */
+ return_values_limits = (struct scmi_perf_limits_get_p2a) {
+ .status = SCMI_SUCCESS,
+ .range_min = limits.minimum,
+ .range_max = limits.maximum,
+ };
+
+ scmi_perf_respond(&return_values_limits, params->domain_id,
+ sizeof(return_values_limits));
+
+ return status;
+ } else if (status == FWK_PENDING) {
+ /* DVFS value will be provided through a response event */
+ return FWK_SUCCESS;
+ } else {
+ return_values_limits = (struct scmi_perf_limits_get_p2a) {
+ .status = SCMI_HARDWARE_ERROR,
+ };
+
+ scmi_perf_respond(&return_values_limits, params->domain_id,
+ sizeof(return_values_limits.status));
+
+ return FWK_E_DEVICE;
+ }
+ }
+
+ return FWK_E_PARAM;
+}
+
+/*
+ * Handle a response event from the HAL which indicates that the
+ * requested operation has completed.
+ */
+static int process_response_event(const struct fwk_event *event)
+{
+ struct mod_dvfs_params_response *params_level;
+ struct scmi_perf_level_get_p2a return_values_level;
+
+ if (fwk_id_is_equal(event->id, mod_dvfs_event_id_get_opp)) {
+ params_level = (struct mod_dvfs_params_response *)
+ event->params;
+ return_values_level = (struct scmi_perf_level_get_p2a) {
+ .status = params_level->status,
+ .performance_level = params_level->performance_level,
+ };
+ scmi_perf_respond(&return_values_level, event->source_id,
+ (return_values_level.status == SCMI_SUCCESS) ?
+ sizeof(return_values_level) :
+ sizeof(return_values_level.status));
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int scmi_perf_process_event(const struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ /* Request events from SCMI */
+ if (fwk_id_get_module_idx(event->source_id) ==
+ fwk_id_get_module_idx(fwk_module_id_scmi))
+ return process_request_event(event);
+
+ /* Response events from DVFS */
+ if (fwk_id_get_module_idx(event->source_id) ==
+ fwk_id_get_module_idx(fwk_module_id_dvfs))
+ return process_response_event(event);
+
+ return FWK_E_PARAM;
+}
+
/* SCMI Performance Management Protocol Definition */
const struct fwk_module module_scmi_perf = {
.name = "SCMI Performance Management Protocol",
.api_count = 1,
+ .event_count = SCMI_PERF_EVENT_IDX_COUNT,
.type = FWK_MODULE_TYPE_PROTOCOL,
.init = scmi_perf_init,
.bind = scmi_perf_bind,
.process_bind_request = scmi_perf_process_bind_request,
+ .process_event = scmi_perf_process_event,
};
diff --git a/product/juno/include/juno_alarm_idx.h b/product/juno/include/juno_alarm_idx.h
index 26bffa3af5c2278a717bc1b77721859f8ecdda5d..0c71e9e07ef5b451545f85f73d202cc39fc77e29 100644
--- a/product/juno/include/juno_alarm_idx.h
+++ b/product/juno/include/juno_alarm_idx.h
@@ -17,7 +17,15 @@ enum juno_xrp7724_alarm_idx {
JUNO_XRP7724_ALARM_IDX_COUNT,
};
+/* Alarm indices for DVFS */
+enum juno_dvfs_alarm_idx {
+ JUNO_DVFS_ALARM_VLITTLE_IDX = JUNO_XRP7724_ALARM_IDX_COUNT,
+ JUNO_DVFS_ALARM_BIG_IDX,
+ JUNO_DVFS_ALARM_GPU_IDX,
+ JUNO_DVFS_ALARM_IDX_CNT,
+};
+
/* Total count of alarms */
-#define JUNO_ALARM_IDX_COUNT JUNO_XRP7724_ALARM_IDX_COUNT
+#define JUNO_ALARM_IDX_COUNT JUNO_DVFS_ALARM_IDX_CNT
#endif /* JUNO_ALARM_IDX_H */
diff --git a/product/juno/module/juno_cdcel937/src/juno_cdcel937.h b/product/juno/module/juno_cdcel937/src/juno_cdcel937.h
index 288d3eb59e564f187d12f7db8e68336ff500f9f3..e9044fc462f4268c2bac70f354009c29c4ba753d 100644
--- a/product/juno/module/juno_cdcel937/src/juno_cdcel937.h
+++ b/product/juno/module/juno_cdcel937/src/juno_cdcel937.h
@@ -241,7 +241,6 @@ struct juno_cdcel937_dev_ctx {
struct pll_cfg_reg pll_config;
struct cfg_reg_y1 y1_config;
uint64_t rate;
- uint64_t *rate_addr;
int index;
};
diff --git a/product/juno/module/juno_cdcel937/src/mod_juno_cdcel937.c b/product/juno/module/juno_cdcel937/src/mod_juno_cdcel937.c
index 17f5697d8b30abb8a62bd014d333c7cb56b5a427..fdf37a2a4102548e342e29a4c34da75f9b906b6e 100644
--- a/product/juno/module/juno_cdcel937/src/mod_juno_cdcel937.c
+++ b/product/juno/module/juno_cdcel937/src/mod_juno_cdcel937.c
@@ -714,7 +714,6 @@ static int juno_cdcel937_get_rate(fwk_id_t clock_id,
ctx = &ctx_table[fwk_id_get_element_idx(clock_id)];
if (ctx->rate_set == false) {
- ctx->rate_addr = rate;
status = create_get_rate_request(clock_id, ctx);
if ((status != FWK_PENDING) && (status != FWK_SUCCESS))
return FWK_E_DEVICE;
@@ -1110,10 +1109,6 @@ static int juno_cdcel937_process_event(const struct fwk_event *event,
module_ctx.state = JUNO_CDCEL937_DEVICE_IDLE;
response_param.status = status;
response_param.value.rate = ctx->rate_hz;
- if (ctx->rate_addr) {
- *(ctx->rate_addr) = ctx->rate;
- ctx->rate_addr = NULL;
- }
ctx->driver_response_api->request_complete(ctx->config->clock_hal_id,
&response_param);
diff --git a/product/juno/module/juno_hdlcd/src/mod_juno_hdlcd.c b/product/juno/module/juno_hdlcd/src/mod_juno_hdlcd.c
index 89a94a0d86e26fb80140b85b06c01dafae284e29..42820fea176db3dee2ae313f6e36bd8560281ba1 100644
--- a/product/juno/module/juno_hdlcd/src/mod_juno_hdlcd.c
+++ b/product/juno/module/juno_hdlcd/src/mod_juno_hdlcd.c
@@ -142,6 +142,8 @@ void juno_hdlcd_request_complete(fwk_id_t dev_id,
ctx->driver_response_api->request_complete(ctx->config->clock_hal_id,
response_param);
+
+ module_ctx.request_clock_id = FWK_ID_NONE;
}
static const struct mod_clock_driver_response_api hdlcd_driver_response_api = {
@@ -171,6 +173,11 @@ static int juno_hdlcd_set_rate(fwk_id_t clock_id, uint64_t rate,
(rounded_rate > ctx->config->max_rate))
return FWK_E_RANGE;
+ if (platform == JUNO_IDX_PLATFORM_RTL) {
+ if (!fwk_id_is_equal(module_ctx.request_clock_id, FWK_ID_NONE))
+ return FWK_E_BUSY;
+ }
+
/*
* Clock rate is always twice the pixel clock rate. This is because Juno has
* an implicit "divide by 2" stage.
@@ -233,7 +240,8 @@ static int juno_hdlcd_set_rate(fwk_id_t clock_id, uint64_t rate,
"[HDLCD] Failed to set board clock\n");
return FWK_E_DEVICE;
}
- module_ctx.request_clock_id = clock_id;
+ if (status == FWK_PENDING)
+ module_ctx.request_clock_id = clock_id;
return status;
}
@@ -438,6 +446,7 @@ static int juno_hdlcd_start(fwk_id_t id)
module_ctx.current_pll_rate = ((uint64_t)(PXL_REF_CLK_RATE) * nf) /
(nr * od);
+ module_ctx.request_clock_id = FWK_ID_NONE;
return FWK_SUCCESS;
}
diff --git a/product/juno/module/juno_xrp7724/include/mod_juno_xrp7724.h b/product/juno/module/juno_xrp7724/include/mod_juno_xrp7724.h
index 13de7a28c3fae8ee7d11a62ada130782a7753fa7..b9eb37870e1214cf181c7951b482a5e77002cc0d 100644
--- a/product/juno/module/juno_xrp7724/include/mod_juno_xrp7724.h
+++ b/product/juno/module/juno_xrp7724/include/mod_juno_xrp7724.h
@@ -27,12 +27,25 @@
struct mod_juno_xrp7724_config {
/*! Slave address of the I2C device */
unsigned int slave_address;
+
/*! Identifier of the I2C HAL */
fwk_id_t i2c_hal_id;
+
/*! Identifier of the timer */
fwk_id_t timer_hal_id;
+
+ /*!
+ * \brief Identifier of the alarm.
+ *
+ * \note When setting the voltage, it is necessary to wait for the PSU to
+ * \note stabilize at the new voltage. We use the Timer HAL to insert a
+ * \note short delay for this.
+ */
+ fwk_id_t alarm_hal_id;
+
/*! Identifier of the GPIO for the assert command */
fwk_id_t gpio_assert_id;
+
/*! Identifier of the GPIO for the system mode selection */
fwk_id_t gpio_mode_id;
};
@@ -43,11 +56,12 @@ struct mod_juno_xrp7724_config {
enum mod_juno_xrp7724_element_type {
MOD_JUNO_XRP7724_ELEMENT_TYPE_GPIO,
MOD_JUNO_XRP7724_ELEMENT_TYPE_SENSOR,
+ MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU,
MOD_JUNO_XRP7724_ELEMENT_TYPE_COUNT,
};
/*!
- * \brief Element configuration.
+ * \brief Element configuration
*/
struct mod_juno_xrp7724_dev_config {
/*! Identifier of the element for the driver response */
@@ -57,7 +71,21 @@ struct mod_juno_xrp7724_dev_config {
fwk_id_t driver_response_api_id;
/*!
- * \brief Sensor information.
+ * \brief Identifier of the ADC sensor associated with the PSU channel.
+ *
+ * \note Only provided for a PSU element
+ */
+ fwk_id_t psu_adc_id;
+
+ /*!
+ * \brief Index of the bus associated to the PSU.
+ *
+ * \note Only provided for a PSU element
+ */
+ uint8_t psu_bus_idx;
+
+ /*!
+ * \brief Sensor information
*
* \note Only provided for a sensor element
*/
@@ -90,6 +118,7 @@ struct mod_juno_xrp7724_api_system_mode {
enum mod_juno_xrp7724_api_idx {
MOD_JUNO_XRP7724_API_IDX_SENSOR,
MOD_JUNO_XRP7724_API_IDX_SYSTEM_MODE,
+ MOD_JUNO_XRP7724_API_IDX_PSU,
MOD_JUNO_XRP7724_API_IDX_COUNT,
};
@@ -101,6 +130,10 @@ static const fwk_id_t mod_juno_xrp7724_api_id_system_mode = FWK_ID_API_INIT(
static const fwk_id_t mod_juno_xrp7724_api_id_sensor = FWK_ID_API_INIT(
FWK_MODULE_IDX_JUNO_XRP7724, MOD_JUNO_XRP7724_API_IDX_SENSOR);
+/*! Identifier of the PSU driver API */
+static const fwk_id_t mod_juno_xrp7724_api_id_psu = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_JUNO_XRP7724, MOD_JUNO_XRP7724_API_IDX_PSU);
+
/*!
* \}
*/
diff --git a/product/juno/module/juno_xrp7724/src/mod_juno_xrp7724.c b/product/juno/module/juno_xrp7724/src/mod_juno_xrp7724.c
index 4373ad10d6df06cb5007cf5c6ab6bc641492a9b1..3a54072f00e773a49173d9ba78166da25f42bb61 100644
--- a/product/juno/module/juno_xrp7724/src/mod_juno_xrp7724.c
+++ b/product/juno/module/juno_xrp7724/src/mod_juno_xrp7724.c
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -39,6 +40,40 @@
/* Read temperature command */
#define SENSOR_READ_TEMP 0x15
+/*
+ * PSU Properties
+ */
+#define PSU_VOUT_PRESCALE_UV 2500 /* Low range */
+#define PSU_VOUT_STEP_COARSE_UV (5 * PSU_VOUT_PRESCALE_UV)
+#define PSU_VOUT_STEP_FINE_UV PSU_VOUT_PRESCALE_UV
+#define PSU_MVOUT_SCALE_READ 15
+#define PSU_FINE_ADJUST_POS 12
+#define PSU_MAX_FINE_ADJUST 0x7
+#define PSU_CHANNEL_COUNT 4
+
+#define PSU_MAX_VOUT_MV 1100
+
+/* Ramping/Settling time when changing voltage in ms */
+#define PSU_RAMP_DELAY_SET_MS 1
+
+/* Ramping/Settling time when enabling a channel in ms */
+#define PSU_RAMP_DELAY_ENABLE_MS 2
+
+#define PSU_TARGET_MARGIN_MV 20
+
+/*
+ * Power Command Set
+ */
+#define PSU_PWR_GET_VOLTAGE_CHx 0x10
+#define PSU_PWR_ENABLE_SUPPLY 0x1E
+#define PSU_PWR_SET_VOLTAGE_CHx 0x20
+
+#define PSU_WRITE_LENGTH 3
+#define PSU_READ_LENGTH 2
+
+#define PSU_CHANNEL_ENABLED 0x1
+#define PSU_CHANNEL_DISABLED 0x0
+
enum juno_xrp7724_event_idx {
JUNO_XRP7724_EVENT_IDX_REQUEST,
JUNO_XRP7724_EVENT_IDX_COUNT
@@ -58,14 +93,52 @@ enum juno_xrp7724_sensor_request {
JUNO_XRP7724_SENSOR_REQUEST_CONVERT_VALUE,
};
+enum juno_xrp7724_psu_request {
+ JUNO_XRP7724_PSU_REQUEST_IDLE,
+ JUNO_XRP7724_PSU_REQUEST_READ_VOLTAGE,
+ JUNO_XRP7724_PSU_REQUEST_CONVERT_VOLTAGE,
+ JUNO_XRP7724_PSU_REQUEST_SET_VOLTAGE,
+ JUNO_XRP7724_PSU_REQUEST_WAIT_FOR_VOLTAGE,
+ JUNO_XRP7724_PSU_REQUEST_COMPARE_VOLTAGE,
+ JUNO_XRP7724_PSU_REQUEST_SET_ENABLED,
+ JUNO_XRP7724_PSU_REQUEST_WAIT_FOR_ENABLED,
+ JUNO_XRP7724_PSU_REQUEST_DONE_ENABLED,
+};
+
+struct psu_set_enabled_param {
+ bool enabled;
+};
+
+struct psu_set_voltage_param {
+ uint64_t voltage;
+ uint16_t set_value;
+};
+
struct juno_xrp7724_ctx {
const struct mod_juno_xrp7724_config *config;
const struct mod_sensor_driver_response_api *sensor_driver_response_api;
+ const struct mod_psu_driver_response_api *psu_driver_response_api;
const struct mod_i2c_api *i2c_api;
const struct mod_timer_api *timer_api;
+ const struct mod_timer_alarm_api *alarm_api;
+ const struct mod_sensor_api *adc_api;
enum juno_xrp7724_gpio_request gpio_request;
enum juno_xrp7724_sensor_request sensor_request;
- bool driver_skipped;
+
+ /*
+ * Note that as all PSUs are accessible via a single I2C bus only one
+ * request may be active at any time.
+ */
+ enum juno_xrp7724_psu_request psu_request;
+};
+
+struct juno_xrp7724_dev_psu_ctx {
+ /* Indicate for a PSU element whether the channel is enabled */
+ bool is_psu_channel_enabled;
+ uint32_t wait_event_cookie;
+ fwk_id_t element_id;
+ bool psu_set_enabled;
+ uint64_t psu_set_voltage;
};
struct juno_xrp7724_dev_ctx {
@@ -73,6 +146,8 @@ struct juno_xrp7724_dev_ctx {
uint32_t cookie;
uint8_t transmit_data[TRANSMIT_DATA_MAX];
uint8_t receive_data[RECEIVE_DATA_MAX];
+ /* PSU data for the device */
+ struct juno_xrp7724_dev_psu_ctx juno_xrp7724_dev_psu;
};
static const fwk_id_t juno_xrp7724_event_id_request =
@@ -87,8 +162,6 @@ static int set_gpio(fwk_id_t id, struct juno_xrp7724_dev_ctx *ctx)
{
int status;
- fwk_assert(fwk_module_is_valid_sub_element_id(id));
-
ctx->transmit_data[0] = GPIO_WRITE_CMD;
ctx->transmit_data[1] = fwk_id_get_sub_element_idx(id);
ctx->transmit_data[2] = SET_GPIO_MASK;
@@ -114,6 +187,24 @@ static uint64_t temperature_to_millidegree_celsius(uint16_t temp)
return (temp * 5000ULL) - 272150;
}
+/* Helper function for the PSU API */
+static void alarm_callback(uintptr_t param)
+{
+ int status;
+ struct fwk_event event;
+ const struct juno_xrp7724_dev_ctx *ctx =
+ (struct juno_xrp7724_dev_ctx *)param;
+
+ event = (struct fwk_event) {
+ .source_id = ctx->juno_xrp7724_dev_psu.element_id,
+ .target_id = ctx->juno_xrp7724_dev_psu.element_id,
+ .id = juno_xrp7724_event_id_request,
+ };
+
+ status = fwk_thread_put_event(&event);
+ fwk_assert(status == FWK_SUCCESS);
+}
+
/*
* Functions for the system mode API
*/
@@ -179,7 +270,6 @@ static int juno_xrp7724_sensor_get_value(fwk_id_t id, uint64_t *value)
fwk_assert(fwk_module_is_valid_element_id(id));
-
if (module_ctx.sensor_request != JUNO_XRP7724_SENSOR_REQUEST_IDLE)
return FWK_E_BUSY;
@@ -207,7 +297,6 @@ static int juno_xrp7724_sensor_get_info(fwk_id_t id,
{
const struct juno_xrp7724_dev_ctx *ctx;
- fwk_assert(fwk_module_is_valid_element_id(id));
fwk_assert(info != NULL);
ctx = &ctx_table[fwk_id_get_element_idx(id)];
@@ -223,6 +312,151 @@ static const struct mod_sensor_driver_api sensor_driver_api = {
.get_info = juno_xrp7724_sensor_get_info,
};
+/*
+ * Driver functions for the PSU API
+ */
+static int juno_xrp7724_set_enabled(fwk_id_t id, bool enabled)
+{
+ int status;
+ struct fwk_event event;
+ const struct juno_xrp7724_dev_ctx *ctx;
+ struct psu_set_enabled_param *param =
+ (struct psu_set_enabled_param *)event.params;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(id)];
+ fwk_assert(ctx->config->type == MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU);
+
+ /*
+ * We only have a single I2C bus so only one I2C request is allowed
+ * at any one time.
+ */
+ if (module_ctx.psu_request != JUNO_XRP7724_PSU_REQUEST_IDLE)
+ return FWK_E_BUSY;
+
+ event = (struct fwk_event) {
+ .target_id = id,
+ .id = juno_xrp7724_event_id_request,
+ };
+
+ param->enabled = enabled;
+
+ status = fwk_thread_put_event(&event);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ module_ctx.psu_request = JUNO_XRP7724_PSU_REQUEST_SET_ENABLED;
+
+ return FWK_PENDING;
+}
+
+static int juno_xrp7724_get_enabled(fwk_id_t id, bool *enabled)
+{
+ const struct juno_xrp7724_dev_ctx *ctx;
+
+ if (enabled == NULL)
+ return FWK_E_PARAM;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(id)];
+ fwk_assert(ctx->config->type == MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU);
+
+ *enabled = ctx->juno_xrp7724_dev_psu.is_psu_channel_enabled;
+
+ return FWK_SUCCESS;
+}
+
+static int juno_xrp7724_set_voltage(fwk_id_t id, uint64_t voltage)
+{
+ int status;
+ struct fwk_event event;
+ uint16_t set_value;
+ uint32_t mvref;
+ uint8_t fine_adj;
+ uint32_t coarse_val;
+ const struct juno_xrp7724_dev_ctx *ctx;
+ struct psu_set_voltage_param *param =
+ (struct psu_set_voltage_param *)event.params;
+
+ /* Platform voltage cap */
+ if (voltage > PSU_MAX_VOUT_MV)
+ return FWK_E_RANGE;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(id)];
+ fwk_assert(ctx->config->type == MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU);
+
+ if (module_ctx.psu_request != JUNO_XRP7724_PSU_REQUEST_IDLE)
+ return FWK_E_BUSY;
+
+ /* Compute the number of coarse voltage steps */
+ coarse_val = (voltage * 1000) / PSU_VOUT_STEP_COARSE_UV;
+
+ /* Compute the resulting voltage in mV */
+ mvref = (coarse_val * PSU_VOUT_STEP_COARSE_UV) / 1000;
+
+ /*
+ * Compute the number of fine steps required to reduce the error.
+ * The truncation means in the worst case, we get just under 2.5mV of
+ * undervoltage.
+ */
+ fine_adj = ((voltage - mvref) * 1000) / PSU_VOUT_STEP_FINE_UV;
+
+ if (fine_adj > PSU_MAX_FINE_ADJUST)
+ fine_adj = PSU_MAX_FINE_ADJUST;
+
+ set_value = (fine_adj << PSU_FINE_ADJUST_POS) | coarse_val;
+
+ event = (struct fwk_event) {
+ .target_id = id,
+ .id = juno_xrp7724_event_id_request,
+ };
+
+ param->set_value = set_value;
+ param->voltage = voltage;
+
+ status = fwk_thread_put_event(&event);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ module_ctx.psu_request = JUNO_XRP7724_PSU_REQUEST_SET_VOLTAGE;
+
+ return FWK_PENDING;
+}
+
+static int juno_xrp7724_get_voltage(fwk_id_t id, uint64_t *voltage)
+{
+ int status;
+ struct fwk_event event;
+ const struct juno_xrp7724_dev_ctx *ctx;
+
+ if (voltage == NULL)
+ return FWK_E_PARAM;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(id)];
+ fwk_assert(ctx->config->type == MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU);
+
+ if (module_ctx.psu_request != JUNO_XRP7724_PSU_REQUEST_IDLE)
+ return FWK_E_BUSY;
+
+ event = (struct fwk_event) {
+ .target_id = id,
+ .id = juno_xrp7724_event_id_request,
+ };
+
+ status = fwk_thread_put_event(&event);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ module_ctx.psu_request = JUNO_XRP7724_PSU_REQUEST_READ_VOLTAGE;
+
+ return FWK_PENDING;
+}
+
+static struct mod_psu_driver_api psu_driver_api = {
+ .set_enabled = juno_xrp7724_set_enabled,
+ .get_enabled = juno_xrp7724_get_enabled,
+ .set_voltage = juno_xrp7724_set_voltage,
+ .get_voltage = juno_xrp7724_get_voltage,
+};
+
/*
* Framework handlers
*/
@@ -230,9 +464,6 @@ static int juno_xrp7724_init(fwk_id_t module_id,
unsigned int element_count,
const void *data)
{
- int status;
- enum juno_idx_platform platform_id;
-
fwk_assert(data != NULL);
module_ctx.config = (struct mod_juno_xrp7724_config *)data;
@@ -242,15 +473,6 @@ static int juno_xrp7724_init(fwk_id_t module_id,
if (ctx_table == NULL)
return FWK_E_NOMEM;
- status = juno_id_get_platform(&platform_id);
- if (!fwk_expect(status == FWK_SUCCESS))
- return FWK_E_PANIC;
-
- if (platform_id == JUNO_IDX_PLATFORM_FVP) {
- /* XRP7724 hardware is only available in the board */
- module_ctx.driver_skipped = true;
- }
-
return FWK_SUCCESS;
}
@@ -270,6 +492,13 @@ static int juno_xrp7724_element_init(fwk_id_t element_id,
return FWK_E_DATA;
}
+ if (ctx->config->type == MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU) {
+ if (ctx->config->psu_bus_idx >= PSU_CHANNEL_COUNT)
+ return FWK_E_DATA;
+ ctx->juno_xrp7724_dev_psu.is_psu_channel_enabled = true;
+ ctx->juno_xrp7724_dev_psu.element_id = element_id;
+ }
+
return FWK_SUCCESS;
}
@@ -277,27 +506,31 @@ static int juno_xrp7724_bind(fwk_id_t id, unsigned int round)
{
int status;
const struct mod_juno_xrp7724_config *config = module_ctx.config;
- struct juno_xrp7724_dev_ctx *ctx;
+ const struct juno_xrp7724_dev_ctx *ctx;
- /* Only bind in first round of calls and if the driver is available */
- if ((round > 0) || (module_ctx.driver_skipped))
+ /* Only bind in first round of calls */
+ if (round > 0)
return FWK_SUCCESS;
if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
/* Bind to I2C HAL */
- status = fwk_module_bind(config->i2c_hal_id,
- FWK_ID_API(FWK_MODULE_IDX_I2C, MOD_I2C_API_IDX_I2C),
+ status = fwk_module_bind(config->i2c_hal_id, mod_i2c_api_id_i2c,
&module_ctx.i2c_api);
if (status != FWK_SUCCESS)
return FWK_E_HANDLER;
/* Bind to timer HAL */
- status = fwk_module_bind(config->timer_hal_id,
- FWK_ID_API(FWK_MODULE_IDX_TIMER, MOD_TIMER_API_IDX_TIMER),
+ status = fwk_module_bind(config->timer_hal_id, MOD_TIMER_API_ID_TIMER,
&module_ctx.timer_api);
if (status != FWK_SUCCESS)
return FWK_E_HANDLER;
+ /* Bind to the alarm HAL */
+ status = fwk_module_bind(config->alarm_hal_id, MOD_TIMER_API_ID_ALARM,
+ &module_ctx.alarm_api);
+ if (status != FWK_SUCCESS)
+ return FWK_E_HANDLER;
+
return FWK_SUCCESS;
}
@@ -311,6 +544,19 @@ static int juno_xrp7724_bind(fwk_id_t id, unsigned int round)
return FWK_E_HANDLER;
}
+ if (ctx->config->type == MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU) {
+ status = fwk_module_bind(ctx->config->driver_response_id,
+ ctx->config->driver_response_api_id,
+ &module_ctx.psu_driver_response_api);
+ if (status != FWK_SUCCESS)
+ return FWK_E_HANDLER;
+
+ status = fwk_module_bind(ctx->config->psu_adc_id,
+ mod_sensor_api_id_sensor, &module_ctx.adc_api);
+ if (status != FWK_SUCCESS)
+ return FWK_E_HANDLER;
+ }
+
return FWK_SUCCESS;
}
@@ -319,13 +565,12 @@ static int juno_xrp7724_process_bind_request(fwk_id_t source_id,
fwk_id_t api_id,
const void **api)
{
- if (module_ctx.driver_skipped)
- return FWK_E_ACCESS;
-
if (fwk_id_is_equal(api_id, mod_juno_xrp7724_api_id_system_mode))
*api = &system_mode_api;
else if (fwk_id_is_equal(api_id, mod_juno_xrp7724_api_id_sensor))
*api = &sensor_driver_api;
+ else if (fwk_id_is_equal(api_id, mod_juno_xrp7724_api_id_psu))
+ *api = &psu_driver_api;
else
return FWK_E_PARAM;
@@ -342,8 +587,6 @@ static int juno_xrp7724_gpio_process_request(fwk_id_t id, int response_status)
struct juno_xrp7724_dev_ctx *ctx;
const struct mod_juno_xrp7724_config *config = module_ctx.config;
- fwk_assert(fwk_module_is_valid_element_id(id));
-
ctx = ctx_table + fwk_id_get_element_idx(id);
switch (module_ctx.gpio_request) {
@@ -433,7 +676,6 @@ static int juno_xrp7724_sensor_process_request(fwk_id_t id, int status)
{
struct juno_xrp7724_dev_ctx *ctx;
uint64_t temp = 0;
- int request_status = status;
struct mod_sensor_driver_resp_params resp_params;
const struct mod_juno_xrp7724_config *module_config = module_ctx.config;
@@ -441,27 +683,29 @@ static int juno_xrp7724_sensor_process_request(fwk_id_t id, int status)
switch (module_ctx.sensor_request) {
case JUNO_XRP7724_SENSOR_REQUEST_READ_VALUE:
- module_ctx.sensor_request = JUNO_XRP7724_SENSOR_REQUEST_CONVERT_VALUE;
-
ctx->transmit_data[0] = SENSOR_READ_TEMP;
-
- request_status =
+ status =
module_ctx.i2c_api->transmit_then_receive_as_master(
module_config->i2c_hal_id, module_config->slave_address,
ctx->transmit_data, ctx->receive_data, SENSOR_WRITE_LENGTH,
SENSOR_READ_LENGTH);
- if (request_status == FWK_PENDING)
- return FWK_SUCCESS;
+ if (status == FWK_PENDING) {
+ module_ctx.sensor_request =
+ JUNO_XRP7724_SENSOR_REQUEST_CONVERT_VALUE;
+ return FWK_SUCCESS;
+ }
- break;
+ if (status != FWK_SUCCESS)
+ break;
+ /* FALLTHRU */
case JUNO_XRP7724_SENSOR_REQUEST_CONVERT_VALUE:
/*
- * The request_status parameter contains the I2C transaction status.
+ * The status parameter contains the I2C transaction status.
* The conversion is done if the read of the sensor value has been
* successful.
*/
- if (request_status == FWK_SUCCESS) {
+ if (status == FWK_SUCCESS) {
temp = temperature_to_millidegree_celsius(
((uint16_t)ctx->receive_data[0] << 8) | ctx->receive_data[1]);
}
@@ -469,12 +713,12 @@ static int juno_xrp7724_sensor_process_request(fwk_id_t id, int status)
break;
default:
- request_status = FWK_E_PARAM;
+ status = FWK_E_PARAM;
}
module_ctx.sensor_request = JUNO_XRP7724_SENSOR_REQUEST_IDLE;
- resp_params.status = request_status;
+ resp_params.status = status;
resp_params.value = temp;
module_ctx.sensor_driver_response_api->reading_complete(
@@ -484,6 +728,181 @@ static int juno_xrp7724_sensor_process_request(fwk_id_t id, int status)
return FWK_SUCCESS;
}
+
+static int juno_xrp7724_psu_process_request(fwk_id_t id,
+ const uint8_t *event_params, int status)
+{
+ uint16_t set_value;
+ uint64_t adc_val;
+ struct juno_xrp7724_dev_ctx *ctx;
+ struct mod_psu_driver_response driver_response;
+ const struct mod_juno_xrp7724_config *module_config = module_ctx.config;
+
+ ctx = &ctx_table[fwk_id_get_element_idx(id)];
+
+ switch (module_ctx.psu_request) {
+ case JUNO_XRP7724_PSU_REQUEST_READ_VOLTAGE:
+ module_ctx.psu_request = JUNO_XRP7724_PSU_REQUEST_CONVERT_VOLTAGE;
+
+ ctx->transmit_data[0] = PSU_PWR_GET_VOLTAGE_CHx +
+ ctx->config->psu_bus_idx;
+
+ status = module_ctx.i2c_api->transmit_then_receive_as_master(
+ module_config->i2c_hal_id, module_config->slave_address,
+ ctx->transmit_data, ctx->receive_data, 1, PSU_READ_LENGTH);
+ if (status == FWK_PENDING)
+ return FWK_SUCCESS;
+
+ if (status != FWK_SUCCESS)
+ break;
+
+ /* FALLTHRU */
+
+ case JUNO_XRP7724_PSU_REQUEST_CONVERT_VOLTAGE:
+ /*
+ * If the I2C transaction completed successfully convert the voltage
+ */
+ if (status == FWK_SUCCESS) {
+ driver_response.voltage = (((uint16_t)ctx->receive_data[0] << 8) |
+ ctx->receive_data[1]) * PSU_MVOUT_SCALE_READ;
+ }
+
+ break;
+
+ case JUNO_XRP7724_PSU_REQUEST_SET_VOLTAGE:
+ module_ctx.psu_request = JUNO_XRP7724_PSU_REQUEST_WAIT_FOR_VOLTAGE;
+
+ set_value = ((struct psu_set_voltage_param *)event_params)->set_value;
+ ctx->juno_xrp7724_dev_psu.psu_set_voltage =
+ ((struct psu_set_voltage_param *)event_params)->voltage;
+
+ ctx->transmit_data[0] = PSU_PWR_SET_VOLTAGE_CHx +
+ ctx->config->psu_bus_idx;
+ ctx->transmit_data[1] = (uint8_t)(set_value >> 8);
+ ctx->transmit_data[2] = (uint8_t)(set_value & 0xFF);
+
+ status = module_ctx.i2c_api->transmit_as_master(
+ module_config->i2c_hal_id, module_config->slave_address,
+ ctx->transmit_data, PSU_WRITE_LENGTH);
+ if (status == FWK_PENDING)
+ return FWK_SUCCESS;
+
+ if (status != FWK_SUCCESS)
+ break;
+
+ /* FALLTHRU */
+
+ case JUNO_XRP7724_PSU_REQUEST_WAIT_FOR_VOLTAGE:
+ /* Check I2C response status */
+ if (status != FWK_SUCCESS)
+ break;
+
+ /*
+ * If the channel is currently enabled then the voltage will ramp to the
+ * new value and it is necessary to wait for it to stabilize before
+ * checking the final output voltage using the board's ADC.
+ *
+ * If the channel is not enabled then there is no need to wait or read
+ * back the output voltage.
+ */
+ if (ctx->juno_xrp7724_dev_psu.is_psu_channel_enabled) {
+ status = module_ctx.alarm_api->start(module_config->alarm_hal_id,
+ PSU_RAMP_DELAY_SET_MS, MOD_TIMER_ALARM_TYPE_ONCE,
+ alarm_callback, (uintptr_t)ctx);
+ if (status != FWK_SUCCESS)
+ break;
+
+ module_ctx.psu_request = JUNO_XRP7724_PSU_REQUEST_COMPARE_VOLTAGE;
+
+ return FWK_SUCCESS;
+ }
+
+ /*
+ * If channel is not enabled there is nothing more to do.
+ */
+ status = FWK_SUCCESS;
+ break;
+
+ case JUNO_XRP7724_PSU_REQUEST_COMPARE_VOLTAGE:
+ status = module_ctx.adc_api->get_value(ctx->config->psu_adc_id,
+ &adc_val);
+ if (status != FWK_SUCCESS)
+ break;
+
+ if (((adc_val + PSU_TARGET_MARGIN_MV) <
+ ctx->juno_xrp7724_dev_psu.psu_set_voltage) ||
+ ((adc_val - PSU_TARGET_MARGIN_MV) >
+ ctx->juno_xrp7724_dev_psu.psu_set_voltage))
+ status = FWK_E_DEVICE;
+
+ break;
+
+ case JUNO_XRP7724_PSU_REQUEST_SET_ENABLED:
+ module_ctx.psu_request = JUNO_XRP7724_PSU_REQUEST_WAIT_FOR_ENABLED;
+
+ ctx->juno_xrp7724_dev_psu.psu_set_enabled =
+ ((struct psu_set_enabled_param *)event_params)->enabled;
+ ctx->transmit_data[0] = PSU_PWR_ENABLE_SUPPLY,
+ ctx->transmit_data[1] = ctx->config->psu_bus_idx;
+ ctx->transmit_data[2] = ctx->juno_xrp7724_dev_psu.psu_set_enabled ?
+ PSU_CHANNEL_ENABLED : PSU_CHANNEL_DISABLED;
+
+ status = module_ctx.i2c_api->transmit_as_master(
+ module_config->i2c_hal_id, module_config->slave_address,
+ ctx->transmit_data, PSU_WRITE_LENGTH);
+ if (status == FWK_PENDING)
+ return FWK_SUCCESS;
+
+ if (status != FWK_SUCCESS)
+ break;
+
+ /* FALLTHRU */
+
+ case JUNO_XRP7724_PSU_REQUEST_WAIT_FOR_ENABLED:
+ /* Check I2C response status */
+ if (status == FWK_SUCCESS) {
+ /*
+ * The transaction was successful so the stored channel
+ * status can be updated.
+ */
+ ctx->juno_xrp7724_dev_psu.is_psu_channel_enabled =
+ ctx->juno_xrp7724_dev_psu.psu_set_enabled;
+
+ /*
+ * Wait a fixed time for the voltage to stabilize.
+ */
+ if (ctx->juno_xrp7724_dev_psu.is_psu_channel_enabled) {
+ status = module_ctx.alarm_api->start(
+ module_config->alarm_hal_id, PSU_RAMP_DELAY_ENABLE_MS,
+ MOD_TIMER_ALARM_TYPE_ONCE, alarm_callback, (uintptr_t)ctx);
+ if (status != FWK_SUCCESS)
+ break;
+
+ module_ctx.psu_request = JUNO_XRP7724_PSU_REQUEST_DONE_ENABLED;
+ return FWK_SUCCESS;
+ } else
+ status = FWK_SUCCESS;
+ }
+
+ break;
+
+ case JUNO_XRP7724_PSU_REQUEST_DONE_ENABLED:
+ status = FWK_SUCCESS;
+ break;
+
+ default:
+ return FWK_E_SUPPORT;
+ }
+
+ driver_response.status = status;
+ module_ctx.psu_request = JUNO_XRP7724_PSU_REQUEST_IDLE;
+
+ module_ctx.psu_driver_response_api->respond(ctx->config->driver_response_id,
+ driver_response);
+
+ return FWK_SUCCESS;
+}
+
/*
* This function is called when a request event is received for the XRP7724 and
* when an I2C transaction is completed.
@@ -496,8 +915,6 @@ static int juno_xrp7724_process_event(const struct fwk_event *event,
struct mod_i2c_event_param *param =
(struct mod_i2c_event_param *)event->params;
- fwk_assert(fwk_module_is_valid_element_id(event->target_id));
-
ctx = ctx_table + fwk_id_get_element_idx(event->target_id);
switch (ctx->config->type) {
@@ -518,6 +935,9 @@ static int juno_xrp7724_process_event(const struct fwk_event *event,
return juno_xrp7724_sensor_process_request(event->target_id,
param->status);
+ case MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU:
+ return juno_xrp7724_psu_process_request(event->target_id,
+ event->params, param->status);
default:
return FWK_E_PARAM;
}
diff --git a/product/juno/scp_ramfw/config_juno_xrp7724.c b/product/juno/scp_ramfw/config_juno_xrp7724.c
index 1e1ec69e6771e5415360a74ce1aed45fe821af21..dc99a00f64bed58db0e57629ad1384e4a10160cd 100644
--- a/product/juno/scp_ramfw/config_juno_xrp7724.c
+++ b/product/juno/scp_ramfw/config_juno_xrp7724.c
@@ -11,10 +11,13 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
+#include
enum mod_juno_xrp7724_gpio_idx {
MOD_JUNO_XRP7724_GPIO_IDX_ASSERT,
@@ -50,7 +53,59 @@ static const struct fwk_element juno_xrp7724_element_table[] = {
.data = &(const struct mod_juno_xrp7724_dev_config) {
.type = MOD_JUNO_XRP7724_ELEMENT_TYPE_GPIO,
},
- },
+ },
+ [MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VSYS] = {
+ .name = "VSYS",
+ .data = &(const struct mod_juno_xrp7724_dev_config) {
+ .driver_response_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PSU,
+ MOD_PSU_ELEMENT_IDX_VSYS),
+ .driver_response_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PSU,
+ MOD_PSU_API_IDX_DRIVER_RESPONSE),
+ .psu_adc_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SENSOR,
+ MOD_JUNO_SENSOR_VOLT_SYS_IDX),
+ .psu_bus_idx = 0,
+ .type = MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU,
+ },
+ },
+ [MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VBIG] = {
+ .name = "VBIG",
+ .data = &(const struct mod_juno_xrp7724_dev_config) {
+ .driver_response_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PSU,
+ MOD_PSU_ELEMENT_IDX_VBIG),
+ .driver_response_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PSU,
+ MOD_PSU_API_IDX_DRIVER_RESPONSE),
+ .psu_adc_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SENSOR,
+ MOD_JUNO_SENSOR_VOLT_BIG_IDX),
+ .psu_bus_idx = 1,
+ .type = MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU,
+ },
+ },
+ [MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VLITTLE] = {
+ .name = "VLITTLE",
+ .data = &(const struct mod_juno_xrp7724_dev_config) {
+ .driver_response_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PSU,
+ MOD_PSU_ELEMENT_IDX_VLITTLE),
+ .driver_response_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PSU,
+ MOD_PSU_API_IDX_DRIVER_RESPONSE),
+ .psu_adc_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SENSOR,
+ MOD_JUNO_SENSOR_VOLT_LITTLE_IDX),
+ .psu_bus_idx = 2,
+ .type = MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU,
+ },
+ },
+ [MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VGPU] = {
+ .name = "VGPU",
+ .data = &(const struct mod_juno_xrp7724_dev_config) {
+ .driver_response_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_PSU,
+ MOD_PSU_ELEMENT_IDX_VGPU),
+ .driver_response_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_PSU,
+ MOD_PSU_API_IDX_DRIVER_RESPONSE),
+ .psu_adc_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_SENSOR,
+ MOD_JUNO_SENSOR_VOLT_GPU_IDX),
+ .psu_bus_idx = 3,
+ .type = MOD_JUNO_XRP7724_ELEMENT_TYPE_PSU,
+ },
+ },
[MOD_JUNO_XRP7724_ELEMENT_IDX_COUNT] = { 0 },
};
@@ -67,6 +122,8 @@ const struct fwk_module_config config_juno_xrp7724 = {
.slave_address = 0x28,
.i2c_hal_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_I2C, 0),
.timer_hal_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_TIMER, 0),
+ .alarm_hal_id = FWK_ID_SUB_ELEMENT_INIT(FWK_MODULE_IDX_TIMER, 0,
+ JUNO_XRP7724_ALARM_IDX),
.gpio_assert_id = FWK_ID_SUB_ELEMENT_INIT(FWK_MODULE_IDX_JUNO_XRP7724,
MOD_JUNO_XRP7724_ELEMENT_IDX_GPIO,
MOD_JUNO_XRP7724_GPIO_IDX_ASSERT),
diff --git a/product/juno/scp_ramfw/config_juno_xrp7724.h b/product/juno/scp_ramfw/config_juno_xrp7724.h
index 78e6714eea70771e8baf760ed97eedb12f273d35..917a4801549f750cb18024f94b488c5b240d8a78 100644
--- a/product/juno/scp_ramfw/config_juno_xrp7724.h
+++ b/product/juno/scp_ramfw/config_juno_xrp7724.h
@@ -12,6 +12,10 @@
enum mod_juno_xrp7724_element_idx {
MOD_JUNO_XRP7724_ELEMENT_IDX_SENSOR,
MOD_JUNO_XRP7724_ELEMENT_IDX_GPIO,
+ MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VSYS,
+ MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VBIG,
+ MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VLITTLE,
+ MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VGPU,
MOD_JUNO_XRP7724_ELEMENT_IDX_COUNT
};
diff --git a/product/juno/scp_ramfw/config_psu.c b/product/juno/scp_ramfw/config_psu.c
new file mode 100644
index 0000000000000000000000000000000000000000..b6849730f62442da10edd689154fcb3bbfd6f591
--- /dev/null
+++ b/product/juno/scp_ramfw/config_psu.c
@@ -0,0 +1,63 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static struct fwk_element psu_dev_desc_table[] = {
+ [MOD_PSU_ELEMENT_IDX_VSYS] = {
+ .name = "VSYS",
+ .data = &(const struct mod_psu_element_cfg) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_JUNO_XRP7724,
+ MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VSYS),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_JUNO_XRP7724,
+ MOD_JUNO_XRP7724_API_IDX_PSU),
+ },
+ },
+ [MOD_PSU_ELEMENT_IDX_VBIG] = {
+ .name = "VBIG",
+ .data = &(const struct mod_psu_element_cfg) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_JUNO_XRP7724,
+ MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VBIG),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_JUNO_XRP7724,
+ MOD_JUNO_XRP7724_API_IDX_PSU),
+ },
+ },
+ [MOD_PSU_ELEMENT_IDX_VLITTLE] = {
+ .name = "VLITLLE",
+ .data = &(const struct mod_psu_element_cfg) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_JUNO_XRP7724,
+ MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VLITTLE),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_JUNO_XRP7724,
+ MOD_JUNO_XRP7724_API_IDX_PSU),
+ },
+ },
+ [MOD_PSU_ELEMENT_IDX_VGPU] = {
+ .name = "VGPU",
+ .data = &(const struct mod_psu_element_cfg) {
+ .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_JUNO_XRP7724,
+ MOD_JUNO_XRP7724_ELEMENT_IDX_PSU_VGPU),
+ .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_JUNO_XRP7724,
+ MOD_JUNO_XRP7724_API_IDX_PSU),
+ },
+ },
+ [MOD_PSU_ELEMENT_IDX_COUNT] = { 0 },
+};
+
+static const struct fwk_element *psu_get_dev_desc_table(fwk_id_t module_id)
+{
+ return psu_dev_desc_table;
+}
+
+struct fwk_module_config config_psu = {
+ .get_element_table = psu_get_dev_desc_table,
+};
diff --git a/product/juno/scp_ramfw/config_psu.h b/product/juno/scp_ramfw/config_psu.h
new file mode 100644
index 0000000000000000000000000000000000000000..a231c83ca4d9087e063a8dcc5ba414a79fcce59b
--- /dev/null
+++ b/product/juno/scp_ramfw/config_psu.h
@@ -0,0 +1,22 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CONFIG_PSU_H
+#define CONFIG_PSU_H
+
+/* PSU indices for Juno */
+enum mod_juno_psu_element_idx {
+ MOD_PSU_ELEMENT_IDX_VSYS,
+ MOD_PSU_ELEMENT_IDX_VBIG,
+ MOD_PSU_ELEMENT_IDX_VLITTLE,
+ MOD_PSU_ELEMENT_IDX_VGPU,
+
+ /* Number of sensors */
+ MOD_PSU_ELEMENT_IDX_COUNT
+};
+
+#endif /* CONFIG_PSU_H */
diff --git a/product/juno/scp_ramfw/firmware.mk b/product/juno/scp_ramfw/firmware.mk
index 1e079f6d679ee90143f3248e8eb8ab088f0908d3..205e14805f5744ec267e624d1ebee83213d28591 100644
--- a/product/juno/scp_ramfw/firmware.mk
+++ b/product/juno/scp_ramfw/firmware.mk
@@ -42,7 +42,8 @@ BS_FIRMWARE_MODULES := \
juno_adc \
juno_xrp7724 \
sensor \
- reg_sensor
+ reg_sensor \
+ psu
BS_FIRMWARE_SOURCES := \
rtx_config.c \
@@ -70,6 +71,7 @@ BS_FIRMWARE_SOURCES := \
config_juno_adc.c \
config_juno_xrp7724.c \
config_sensor.c \
- config_reg_sensor.c
+ config_reg_sensor.c \
+ config_psu.c
include $(BS_DIR)/firmware.mk