diff options
author | Nicolas Royer <nroyer@baylibre.com> | 2020-09-27 17:37:49 +0200 |
---|---|---|
committer | nicola-mazzucato-arm <42373140+nicola-mazzucato-arm@users.noreply.github.com> | 2020-10-15 17:45:38 +0100 |
commit | 4c1cf6658adeeddcd85c4917380b6a96d2b749b1 (patch) | |
tree | 0faf42ffc40600160115c464c9f5eb150eba8a55 /product/rcar | |
parent | a8965c4d5cfc766f88f388cc6b93d1d95c62fb82 (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>
Diffstat (limited to 'product/rcar')
-rw-r--r-- | product/rcar/module/rcar_dvfs/include/mod_dvfs.h | 16 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/include/mod_rcar_dvfs.h | 268 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/src/Makefile | 15 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api.c | 266 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api_private.h | 16 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/src/mod_dvfs_event.c | 47 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/src/mod_dvfs_event_private.h | 18 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/src/mod_dvfs_module.c | 228 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/src/mod_dvfs_module_private.h | 42 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/src/mod_dvfs_private.h | 16 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/src/mod_dvfs_util.c | 66 | ||||
-rw-r--r-- | product/rcar/module/rcar_dvfs/src/mod_dvfs_util_private.h | 23 | ||||
-rw-r--r-- | product/rcar/scp_ramfw/config_rcar_dvfs.c | 89 | ||||
-rw-r--r-- | product/rcar/scp_ramfw/config_rcar_dvfs.h | 17 |
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, ¤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/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, ¤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/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 */ |