aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Royer <nroyer@baylibre.com>2020-09-27 17:37:49 +0200
committernicola-mazzucato-arm <42373140+nicola-mazzucato-arm@users.noreply.github.com>2020-10-15 17:45:38 +0100
commit4c1cf6658adeeddcd85c4917380b6a96d2b749b1 (patch)
tree0faf42ffc40600160115c464c9f5eb150eba8a55
parenta8965c4d5cfc766f88f388cc6b93d1d95c62fb82 (diff)
rcar/module: add rcar dvfs module and config data
Change-Id: I6118b0ca36937fb02fb8101a152071f4cbfb74a8 Signed-off-by: Tsutomu Muroya <tsutomu.muroya.jy@bp.renesas.com> Signed-off-by: Nicolas Royer <nroyer@baylibre.com>
-rw-r--r--product/rcar/module/rcar_dvfs/include/mod_dvfs.h16
-rw-r--r--product/rcar/module/rcar_dvfs/include/mod_rcar_dvfs.h268
-rw-r--r--product/rcar/module/rcar_dvfs/src/Makefile15
-rw-r--r--product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api.c266
-rw-r--r--product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api_private.h16
-rw-r--r--product/rcar/module/rcar_dvfs/src/mod_dvfs_event.c47
-rw-r--r--product/rcar/module/rcar_dvfs/src/mod_dvfs_event_private.h18
-rw-r--r--product/rcar/module/rcar_dvfs/src/mod_dvfs_module.c228
-rw-r--r--product/rcar/module/rcar_dvfs/src/mod_dvfs_module_private.h42
-rw-r--r--product/rcar/module/rcar_dvfs/src/mod_dvfs_private.h16
-rw-r--r--product/rcar/module/rcar_dvfs/src/mod_dvfs_util.c66
-rw-r--r--product/rcar/module/rcar_dvfs/src/mod_dvfs_util_private.h23
-rw-r--r--product/rcar/scp_ramfw/config_rcar_dvfs.c89
-rw-r--r--product/rcar/scp_ramfw/config_rcar_dvfs.h17
14 files changed, 1127 insertions, 0 deletions
diff --git a/product/rcar/module/rcar_dvfs/include/mod_dvfs.h b/product/rcar/module/rcar_dvfs/include/mod_dvfs.h
new file mode 100644
index 00000000..a59355b2
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/include/mod_dvfs.h
@@ -0,0 +1,16 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_H
+#define MOD_DVFS_H
+
+#define FWK_MODULE_IDX_DVFS FWK_MODULE_IDX_RCAR_DVFS
+#define fwk_module_id_dvfs fwk_module_id_rcar_dvfs
+
+#include "mod_rcar_dvfs.h"
+
+#endif /* MOD_DVFS_H */
diff --git a/product/rcar/module/rcar_dvfs/include/mod_rcar_dvfs.h b/product/rcar/module/rcar_dvfs/include/mod_rcar_dvfs.h
new file mode 100644
index 00000000..e29bbcf6
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/include/mod_rcar_dvfs.h
@@ -0,0 +1,268 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_RCAR_DVFS_H
+#define MOD_RCAR_DVFS_H
+
+#include <fwk_id.h>
+#include <fwk_module_idx.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*!
+ * \ingroup GroupRCARModule RCAR Product Modules
+ * \defgroup GroupRCARDvfs Dynamic Voltage and Frequency Scaling (DVFS)
+ * \{
+ */
+
+/*!
+ * \defgroup GroupRCARDvfsTypes Types
+ * \{
+ */
+
+/*!
+ * \brief Frequency limits.
+ */
+struct mod_dvfs_frequency_limits {
+ uint64_t minimum; /*!< Minimum permitted rate */
+ uint64_t maximum; /*!< Maximum permitted rate */
+};
+
+/*!
+ * \brief Operating Performance Point (OPP).
+ */
+struct mod_dvfs_opp {
+ uint64_t voltage; /*!< Power supply voltage in millivolts (mV) */
+ uint64_t frequency; /*!< Clock rate in Hertz (Hz) */
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupRCARDvfsConfig Configuration
+ * \{
+ */
+
+/*!
+ * \brief Domain configuration.
+ */
+struct mod_dvfs_domain_config {
+ /*!
+ * \brief Power supply identifier.
+ *
+ * \warning This identifier must refer to an element of the \c psu module.
+ */
+ fwk_id_t psu_id;
+
+ /*!
+ * \brief Clock identifier.
+ *
+ * \warning This identifier must refer to an element of the \c clock module.
+ */
+ fwk_id_t clock_id;
+
+ /*! Worst-case transition latency in microseconds */
+ uint16_t latency;
+
+ /*! Sustained operating point index */
+ size_t sustained_idx;
+
+ /*!
+ * \brief Operating points.
+ *
+ * \note The frequencies of these operating points must be in ascending
+ * order.
+ */
+ struct mod_dvfs_opp *opps;
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupRCARDvfsApis APIs
+ * \{
+ */
+
+/*!
+ * \brief Domain API.
+ */
+struct mod_dvfs_domain_api {
+ /*!
+ * \brief Get the current operating point of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param [out] opp Current operating point.
+ */
+ int (*get_current_opp)(fwk_id_t domain_id, struct mod_dvfs_opp *opp);
+
+ /*!
+ * \brief Get the sustained operating point of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param [out] opp Sustained operating point.
+ */
+ int (*get_sustained_opp)(fwk_id_t domain_id, struct mod_dvfs_opp *opp);
+
+ /*!
+ * \brief Get an operating point from its index.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param n Index of the operating point to retrieve.
+ * \param [out] opp Requested operating point.
+ */
+ int (*get_nth_opp)(fwk_id_t domain_id, size_t n, struct mod_dvfs_opp *opp);
+
+ /*!
+ * \brief Get the number of operating points of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param [out] opp_count Number of operating points.
+ */
+ int (*get_opp_count)(fwk_id_t domain_id, size_t *opp_count);
+
+ /*!
+ * \brief Get the worst-case transition latency of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param [out] latency Worst-case transition latency.
+ */
+ int (*get_latency)(fwk_id_t domain_id, uint16_t *latency);
+
+ /*!
+ * \brief Set the frequency of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param idx Index of the operating point to transition to.
+ */
+ 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.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param [out] limits Current frequency limits.
+ */
+ int (*get_frequency_limits)(
+ fwk_id_t domain_id,
+ struct mod_dvfs_frequency_limits *limits);
+
+ /*!
+ * \brief Set the frequency of a domain.
+ *
+ * \param domain_id Element identifier of the domain.
+ * \param limits Pointer to the new limits.
+ */
+ 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);
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupRCARDvfsEvents Events
+ * \{
+ */
+
+/*!
+ * \brief <tt>Set operating point</tt> event response parameters.
+ */
+struct mod_dvfs_event_params_set_frequency_response {
+ int status; /*!< Status of the request */
+};
+
+/*!
+ * \brief <tt>Set limits</tt> event response parameters.
+ */
+struct mod_dvfs_event_params_set_frequency_limits_response {
+ int status; /*!< Status of the request */
+};
+
+/*!
+ * \}
+ */
+
+/*!
+ * \defgroup GroupRCARDvfsIds Identifiers
+ * \{
+ */
+
+/*!
+ * \brief API indices.
+ */
+enum mod_dvfs_api_idx {
+ MOD_DVFS_API_IDX_DVFS, /*!< API index for mod_dvfs_api_id_dvfs() */
+ MOD_DVFS_API_IDX_COUNT /*!< Number of defined APIs */
+};
+
+/*! Module API identifier */
+static const fwk_id_t mod_dvfs_api_id_dvfs =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_DVFS, MOD_DVFS_API_IDX_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
+};
+
+/*! <tt>Set operating point</tt> 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);
+
+/*! <tt>Set frequency limits</tt> 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);
+
+/*!
+ * \}
+ */
+
+/*!
+ * \}
+ */
+
+#endif /* MOD_RCAR_DVFS_H */
diff --git a/product/rcar/module/rcar_dvfs/src/Makefile b/product/rcar/module/rcar_dvfs/src/Makefile
new file mode 100644
index 00000000..f85b5675
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/src/Makefile
@@ -0,0 +1,15 @@
+#
+# Renesas SCP/MCP Software
+# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := rcar_dvfs
+BS_LIB_SOURCES := \
+ mod_dvfs_domain_api.c \
+ mod_dvfs_event.c \
+ mod_dvfs_module.c \
+ mod_dvfs_util.c \
+
+include $(BS_DIR)/lib.mk
diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api.c b/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api.c
new file mode 100644
index 00000000..bcc11dc8
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api.c
@@ -0,0 +1,266 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <mod_dvfs_private.h>
+
+#include <mod_clock.h>
+#include <mod_rcar_pmic.h>
+
+#include <fwk_assert.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+
+#include <stdbool.h>
+
+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, &current_opp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ new_opp = adjust_opp_for_new_limits(ctx, &current_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/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api_private.h b/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api_private.h
new file mode 100644
index 00000000..5026bc29
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api_private.h
@@ -0,0 +1,16 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_DOMAIN_API_PRIVATE_H
+#define MOD_DVFS_DOMAIN_API_PRIVATE_H
+
+#include <mod_dvfs.h>
+
+/* Module API implementation */
+extern const struct mod_dvfs_domain_api __mod_dvfs_domain_api;
+
+#endif /* MOD_DVFS_DOMAIN_API_PRIVATE_H */
diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_event.c b/product/rcar/module/rcar_dvfs/src/mod_dvfs_event.c
new file mode 100644
index 00000000..f9317112
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_event.c
@@ -0,0 +1,47 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <mod_dvfs_private.h>
+
+#include <fwk_macros.h>
+
+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/product/rcar/module/rcar_dvfs/src/mod_dvfs_event_private.h b/product/rcar/module/rcar_dvfs/src/mod_dvfs_event_private.h
new file mode 100644
index 00000000..c8e0b324
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_event_private.h
@@ -0,0 +1,18 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_EVENT_PRIVATE_H
+#define MOD_DVFS_EVENT_PRIVATE_H
+
+#include <fwk_event.h>
+
+/* 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/product/rcar/module/rcar_dvfs/src/mod_dvfs_module.c b/product/rcar/module/rcar_dvfs/src/mod_dvfs_module.c
new file mode 100644
index 00000000..2d5b99fa
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_module.c
@@ -0,0 +1,228 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <mod_dvfs_private.h>
+
+#include <mod_clock.h>
+#include <mod_power_domain.h>
+#include <mod_rcar_pmic.h>
+
+#include <fwk_assert.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_notification.h>
+
+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_rcar_pmic_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_rcar_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/product/rcar/module/rcar_dvfs/src/mod_dvfs_module_private.h b/product/rcar/module/rcar_dvfs/src/mod_dvfs_module_private.h
new file mode 100644
index 00000000..0ec3d455
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_module_private.h
@@ -0,0 +1,42 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_MODULE_PRIVATE_H
+#define MOD_DVFS_MODULE_PRIVATE_H
+
+#include <mod_clock.h>
+#include <mod_rcar_pmic.h>
+
+#include <fwk_id.h>
+
+/* Domain context */
+struct mod_dvfs_domain_ctx {
+ /* Domain configuration */
+ const struct mod_dvfs_domain_config *config;
+
+ struct {
+ /* Power supply API */
+ const struct mod_rcar_pmic_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/product/rcar/module/rcar_dvfs/src/mod_dvfs_private.h b/product/rcar/module/rcar_dvfs/src/mod_dvfs_private.h
new file mode 100644
index 00000000..6eebcac6
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_private.h
@@ -0,0 +1,16 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_DVFS_PRIVATE_H
+#define MOD_DVFS_PRIVATE_H
+
+#include <mod_dvfs_domain_api_private.h>
+#include <mod_dvfs_event_private.h>
+#include <mod_dvfs_module_private.h>
+#include <mod_dvfs_util_private.h>
+
+#endif /* MOD_DVFS_PRIVATE_H */
diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_util.c b/product/rcar/module/rcar_dvfs/src/mod_dvfs_util.c
new file mode 100644
index 00000000..c3c8df82
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_util.c
@@ -0,0 +1,66 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <mod_dvfs_private.h>
+
+#include <fwk_module.h>
+
+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, &current_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/product/rcar/module/rcar_dvfs/src/mod_dvfs_util_private.h b/product/rcar/module/rcar_dvfs/src/mod_dvfs_util_private.h
new file mode 100644
index 00000000..8bc33562
--- /dev/null
+++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_util_private.h
@@ -0,0 +1,23 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2017-2020, 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 <mod_dvfs_domain_api_private.h>
+
+#include <mod_dvfs.h>
+
+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/product/rcar/scp_ramfw/config_rcar_dvfs.c b/product/rcar/scp_ramfw/config_rcar_dvfs.c
new file mode 100644
index 00000000..fc1cc952
--- /dev/null
+++ b/product/rcar/scp_ramfw/config_rcar_dvfs.c
@@ -0,0 +1,89 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <config_rcar_dvfs.h>
+
+#include <mod_dvfs.h>
+
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+
+static const struct mod_dvfs_domain_config cpu_group_little = {
+ .psu_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_PMIC, 0),
+ .clock_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, 0),
+ .latency = 1200,
+ .sustained_idx = 2,
+ .opps = (struct mod_dvfs_opp[]){ {
+ .frequency = 800 * FWK_MHZ,
+ .voltage = 820000,
+ },
+ {
+ .frequency = 1000 * FWK_MHZ,
+ .voltage = 820000,
+ },
+ {
+ .frequency = 1200 * FWK_MHZ,
+ .voltage = 820000,
+ },
+ { 0 } }
+};
+
+static const struct mod_dvfs_domain_config cpu_group_big = {
+ .psu_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_PMIC, 1),
+ .clock_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, 1),
+ .latency = 1200,
+ .sustained_idx = 2,
+ .opps = (struct mod_dvfs_opp[]){ {
+ .frequency = 500 * FWK_MHZ,
+ .voltage = 830000,
+ },
+ {
+ .frequency = 1000 * FWK_MHZ,
+ .voltage = 830000,
+ },
+ {
+ .frequency = 1500 * FWK_MHZ,
+ .voltage = 830000,
+ },
+#if 0 /* The prototype does not support boost mode. */
+ {
+ .frequency = 1600 * FWK_MHZ,
+ .voltage = 900000,
+ },
+ {
+ .frequency = 1700 * FWK_MHZ,
+ .voltage = 960000,
+ },
+#endif
+ { 0 } }
+};
+
+static const struct fwk_element element_table[] = {
+ [DVFS_ELEMENT_IDX_LITTLE] =
+ {
+ .name = "CPU_GROUP_LITTLE",
+ .data = &cpu_group_little,
+ },
+ [DVFS_ELEMENT_IDX_BIG] =
+ {
+ .name = "CPU_GROUP_BIG",
+ .data = &cpu_group_big,
+ },
+ { 0 }
+};
+
+static const struct fwk_element *dvfs_get_element_table(fwk_id_t module_id)
+{
+ return element_table;
+}
+
+struct fwk_module_config config_rcar_dvfs = {
+ .elements = FWK_MODULE_DYNAMIC_ELEMENTS(dvfs_get_element_table),
+ .data = NULL,
+};
diff --git a/product/rcar/scp_ramfw/config_rcar_dvfs.h b/product/rcar/scp_ramfw/config_rcar_dvfs.h
new file mode 100644
index 00000000..e97ab608
--- /dev/null
+++ b/product/rcar/scp_ramfw/config_rcar_dvfs.h
@@ -0,0 +1,17 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CONFIG_RCAR_DVFS_H
+#define CONFIG_RCAR_DVFS_H
+
+enum dvfs_element_idx {
+ DVFS_ELEMENT_IDX_LITTLE,
+ DVFS_ELEMENT_IDX_BIG,
+ DVFS_ELEMENT_IDX_COUNT
+};
+
+#endif /* CONFIG_RCAR_DVFS_H */