aboutsummaryrefslogtreecommitdiff
path: root/product/rcar
diff options
context:
space:
mode:
authorNicolas Royer <nroyer@baylibre.com>2020-09-27 17:37:25 +0200
committernicola-mazzucato-arm <42373140+nicola-mazzucato-arm@users.noreply.github.com>2020-10-15 17:45:38 +0100
commita8965c4d5cfc766f88f388cc6b93d1d95c62fb82 (patch)
treeade288fafbb44fceeac6077c332b40fb07dd99c7 /product/rcar
parent6d92e073c3df3881f0d4c79bcb5be1fef400b579 (diff)
rcar/module: add rcar clock module and config data
Change-Id: I942313cc1e17053597df7c5543758defd768eff4 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_clock/include/mod_rcar_clock.h413
-rw-r--r--product/rcar/module/rcar_clock/src/Makefile11
-rw-r--r--product/rcar/module/rcar_clock/src/mod_rcar_clock.c1164
-rw-r--r--product/rcar/scp_ramfw/config_rcar_clock.c151
4 files changed, 1739 insertions, 0 deletions
diff --git a/product/rcar/module/rcar_clock/include/mod_rcar_clock.h b/product/rcar/module/rcar_clock/include/mod_rcar_clock.h
new file mode 100644
index 00000000..1bbc1bd5
--- /dev/null
+++ b/product/rcar/module/rcar_clock/include/mod_rcar_clock.h
@@ -0,0 +1,413 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOD_RCAR_CLOCK_H
+#define MOD_RCAR_CLOCK_H
+
+#include <rcar_mmap.h>
+
+#include <mod_clock.h>
+
+#include <fwk_element.h>
+
+#include <stdint.h>
+
+/*!
+ * \addtogroup GroupRCARModule RCAR Product Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupRCARClock Clock
+ * @{
+ */
+
+/*!
+ * \brief Clock driver interface.
+ */
+struct mod_rcar_clock_drv_api {
+ /*! Name of the driver */
+ const char *name;
+
+ /*!
+ * \brief Set a new clock rate by providing a frequency in Hertz (Hz).
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param rate The desired frequency in Hertz.
+ *
+ * \param round_mode The type of rounding to perform, if required, to
+ * achieve the given rate.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*set_rate)(
+ fwk_id_t clock_id,
+ uint64_t rate,
+ enum mod_clock_round_mode round_mode);
+
+ /*!
+ * \brief Get the current rate of a clock in Hertz (Hz).
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param [out] rate The current clock rate in Hertz.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_rate)(fwk_id_t clock_id, uint64_t *rate);
+
+ /*!
+ * \brief Get a clock rate in Hertz from an index into the clock's range.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param rate_index The index into the clock's range to get the rate of.
+ *
+ * \param[out] rate The rate, in Hertz, corresponding to the index.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_rate_from_index)(
+ fwk_id_t clock_id,
+ unsigned int rate_index,
+ uint64_t *rate);
+
+ /*!
+ * \brief Set the running state of a clock.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param state One of the valid clock states.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*set_state)(fwk_id_t clock_id, enum mod_clock_state state);
+
+ /*!
+ * \brief Get the running state of a clock.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param[out] state The current clock state.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_state)(fwk_id_t clock_id, enum mod_clock_state *state);
+
+ /*!
+ * \brief Get the range of rates that the clock supports.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param[out] range The clock range structure.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_range)(fwk_id_t clock_id, struct mod_clock_range *range);
+
+ /*!
+ * \brief Handle the condition where the state of a clock's power domain is
+ * about to change.
+ *
+ * \details This function will be called prior to the change in power
+ * state occurring so that the clock driver implementing this API is
+ * able to perform any required preparatory work beforehand.
+ *
+ * \note This function is optional. If the driver does not control any
+ * clocks that require power state awareness then the pointer may be set
+ * to NULL.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param current_state The current power state that the clock's power
+ * domain will transition away from.
+ *
+ * \param new_state The power state that the clock's power domain will
+ * transition to.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*process_pending_power_transition)(
+ fwk_id_t clock_id,
+ unsigned int current_state,
+ unsigned int new_state);
+
+ /*!
+ * \brief Handle the condition where the state of a clock's power domain
+ * has changed.
+ *
+ * \details This function will be called after the change in power state
+ * has occurred. The driver can take any appropriate actions that are
+ * required to accommodate the new state. The transition can be to a
+ * deeper power state (e.g. ON->OFF) or to a shallower power state
+ * (e.g. OFF->ON).
+ *
+ * \note This function is optional. If the driver does not control any
+ * clocks that require power state awareness then the pointer may be set
+ * to NULL.
+ *
+ * \param clock_id Clock device identifier.
+ *
+ * \param state The power state that the clock's power domain transitioned
+ * to.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*process_power_transition)(fwk_id_t clock_id, unsigned int state);
+
+ /*!
+ * \brief Handle the resume of a clock's power domain.
+ *
+ * \retval FWK_SUCCESS The operation succeeded.
+ * \return One of the standard framework error codes.
+ */
+ int (*resume)(void);
+};
+
+/*!
+ * \brief APIs provided by the driver.
+ */
+enum mod_rcar_clock_api_type {
+ /*! An implementation of the Clock HAL module's clock driver API */
+ MOD_RCAR_CLOCK_API_TYPE_CLOCK,
+ /*! A low-level API for direct control of CSS clocks */
+ MOD_RCAR_CLOCK_API_TYPE_CSS,
+ MOD_RCAR_CLOCK_API_COUNT,
+};
+
+/*!
+ * \brief Sub-types of rcar clock.
+ */
+enum mod_rcar_clock_type {
+ /*! A clock with a fixed source. Only its divider can be changed. */
+ MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE,
+ /*! A clock with multiple, selectable sources and at least one divider. */
+ MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE,
+ /*!
+ * A clock with multiple, selectable sources, at least one divider, and
+ * support for modulation.
+ */
+ MOD_RCAR_CLOCK_TYPE_CLUSTER,
+};
+
+/*!
+ * \brief Divider register types.
+ */
+enum mod_rcar_clock_msclock_divider {
+ /*! Divider affecting the A57 PLL clock source. */
+ MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT = 1,
+ /*! Divider affecting the A53 PLL clock source. */
+ MOD_RCAR_CLOCK_A53_DIVIDER_DIV_EXT = 2,
+};
+
+/*!
+ * \brief Selectable clock sources for V8.2 cluster clocks.
+ */
+enum mod_clusclock_source {
+ /*! The clock source is set to a private cluster PLL */
+ MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0 = 0,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL1 = 1,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL2 = 2,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL3 = 3,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL4 = 4,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL5 = 5,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL6 = 6,
+ /*! The clock source is set to a private cluster PLL */
+ MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL7 = 7,
+ /*! Number of valid clock sources */
+ MOD_RCAR_CLOCK_CLUSCLK_SOURCE_MAX
+};
+
+/*!
+ * \brief Rate lookup entry.
+ */
+struct mod_rcar_clock_rate {
+ /*! Rate in Hertz. */
+ uint64_t rate;
+ /*! Clock source used to obtain the rate (multi-source clocks only). */
+ uint8_t source;
+ /*! The divider register to use (multi-source clocks only). */
+ enum mod_rcar_clock_msclock_divider divider_reg;
+ /*! Divider used to obtain the rate. */
+ uint8_t divider;
+};
+
+/*!
+ * \brief Ext clock rate lookup entry.
+ */
+struct mod_ext_clock_rate {
+ /*! Rate in Hertz. */
+ uint32_t ext_clk_rate;
+};
+
+/*!
+ * \brief Subsystem clock device configuration.
+ */
+struct mod_rcar_clock_dev_config {
+ /*! The type of the clock device. */
+ enum mod_rcar_clock_type type;
+
+ /*!
+ * \brief Indicates whether the clock is part of a CSS clock group (\c true)
+ * or operating as an independent clock (\c false).
+ */
+ bool is_group_member;
+
+ /*! Pointer to the clock's control register. */
+ volatile uint32_t *const control_reg;
+
+ /*! Pointer to the clock's modulator register, if any. */
+ volatile uint32_t *const modulator_reg;
+
+ /*! Pointer to the clock's DIV_SYS divider register, if any. */
+ volatile uint32_t *const divsys_reg;
+
+ /*! Pointer to the clock's DIV_EXT divider register, if any. */
+ volatile uint32_t *const divext_reg;
+
+ /*! Pointer to the clock's rate lookup table. */
+ const struct mod_rcar_clock_rate *rate_table;
+
+ /*! The number of rates in the rate lookup table. */
+ uint32_t rate_count;
+
+ /*! The rate, in Hz, to set during module initialization. */
+ uint64_t initial_rate;
+
+ /*!
+ * If \c true, the driver will not attempt to set a default frequency, or to
+ * otherwise configure the PLL during the pre-runtime phase. The PLL is
+ * expected to be initialized later in response to a notification or other
+ * event.
+ */
+ const bool defer_initialization;
+};
+
+/*!
+ * @cond
+ */
+
+/* Device context */
+struct rcar_clock_dev_ctx {
+ bool initialized;
+ uint64_t current_rate;
+ enum mod_clock_state current_state;
+ const struct mod_rcar_clock_dev_config *config;
+};
+
+/* Module context */
+struct rcar_clock_ctx {
+ struct rcar_clock_dev_ctx *dev_ctx_table;
+ unsigned int dev_count;
+ uint32_t extal_clk;
+};
+
+struct op_points {
+ unsigned long freq; /* Hz */
+ unsigned long volt; /* uV */
+};
+
+#define PLL_BASE_CLOCK (16640000UL)
+#define CPG_FRQCRB (CPG_BASE + 0x0004)
+#define CPG_FRQCRB_KICK 0x80000000
+#define CPG_FRQCRC (CPG_BASE + 0x00e0)
+#define CPG_FRQCRC_ZFC_A57_MASK 0x00001F00
+#define CPG_FRQCRC_ZFC_A57_SHIFT 8
+#define CPG_FRQCRC_ZFC_A53_MASK 0x0000001F
+#define CPG_FRQCRC_ZFC_A53_SHIFT 0
+#ifndef CPG_PLL0CR
+# define CPG_PLL0CR (CPG_BASE + 0x00d8)
+#endif
+#define CPG_PLL0CR_ZFC_MASK 0x7F000000
+#define CPG_PLL0CR_ZFC_SHIFT 24
+#ifndef CPG_PLL2CR
+# define CPG_PLL2CR (CPG_BASE + 0x002c)
+#endif
+#define CPG_PLL2CR_ZFC_MASK 0x7F000000
+#define CPG_PLL2CR_ZFC_SHIFT 24
+
+#define min(x, y) \
+ ({ \
+ __typeof__(x) _x = (x); \
+ __typeof__(y) _y = (y); \
+ _x < _y ? _x : _y; \
+ })
+
+#define max(x, y) \
+ ({ \
+ __typeof__(x) _x = (x); \
+ __typeof__(y) _y = (y); \
+ _x > _y ? _x : _y; \
+ })
+
+#define clamp(val, lo, hi) min((__typeof__(val))max(val, lo), hi)
+
+#define A57_DOMAIN 0
+#define A53_DOMAIN 1
+#define NR_H3_A57_OPP 5
+#define NR_M3_A57_OPP 6
+#define NR_H3_A53_OPP 3
+#define NR_M3_A53_OPP 4
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#define DIV_ROUND(n, d) (((n) + (d) / 2) / (d))
+
+/* CPG base address */
+#define CPG_PLLECR 0x00D0
+
+/* Implementation for customized clocks (Z-clk, Z2-clk, PLL0-clk) for CPUFreq */
+#define CPG_PLLECR_PLL0ST BIT(8)
+#define CPG_PLLECR_PLL2ST BIT(10)
+
+/* Define for PLL0 clk driver */
+#define CPG_PLLCR_STC_MASK 0x7f000000
+#define CPG_PLLCR_STC_SHIFT 24
+
+/* Modify for Z-clock and Z2-clock
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable. clk->rate = parent->rate * mult / 32
+ * parent - fixed parent. No clk_set_parent support
+ */
+#define CPG_FRQCRC_ZFC_MASK (0x1f << 8)
+#define CPG_FRQCRC_ZFC_SHIFT 8
+#define CPG_FRQCRC_Z2FC_MASK 0x1f
+#define Z_CLK_MAX_THRESHOLD 1500000000U
+#define Z2_CLK_MAX_THRESHOLD 1200000000U
+
+extern int32_t rcar_iic_dvfs_receive(uint8_t slave, uint8_t reg, uint8_t *data);
+extern int32_t rcar_iic_dvfs_send(uint8_t slave, uint8_t regr, uint8_t data);
+
+/*!
+ * @endcond
+ */
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_RCAR_CLOCK_H */
diff --git a/product/rcar/module/rcar_clock/src/Makefile b/product/rcar/module/rcar_clock/src/Makefile
new file mode 100644
index 00000000..b773cf6b
--- /dev/null
+++ b/product/rcar/module/rcar_clock/src/Makefile
@@ -0,0 +1,11 @@
+#
+# Renesas SCP/MCP Software
+# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := Clock HAL
+BS_LIB_SOURCES := mod_rcar_clock.c
+
+include $(BS_DIR)/lib.mk
diff --git a/product/rcar/module/rcar_clock/src/mod_rcar_clock.c b/product/rcar/module/rcar_clock/src/mod_rcar_clock.c
new file mode 100644
index 00000000..46d9112c
--- /dev/null
+++ b/product/rcar/module/rcar_clock/src/mod_rcar_clock.c
@@ -0,0 +1,1164 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <mmio.h>
+
+#include <mod_clock.h>
+#include <mod_power_domain.h>
+#include <mod_rcar_clock.h>
+
+#include <fwk_element.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_status.h>
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+static struct rcar_clock_ctx module_ctx;
+static int current_a57_opp_limit = 0;
+static const struct op_points *current_a57_opp_table;
+static int current_a53_opp_limit = 0;
+static const struct op_points *current_a53_opp_table;
+static int dvfs_inited = 0;
+
+/* TODO These should be taken from avs_driver.c */
+#define EFUSE_AVS0 (0U)
+#define EFUSE_AVS_NUM (8U)
+static uint32_t efuse_avs = EFUSE_AVS0;
+
+/* Describe OPPs exactly how they are described in the device-tree */
+static const struct op_points rcar_h3_a57_op_points[EFUSE_AVS_NUM]
+ [NR_H3_A57_OPP] = {
+ {
+ {
+ 500000000,
+ 830000,
+ },
+ {
+ 1000000000,
+ 830000,
+ },
+ {
+ 1500000000,
+ 830000,
+ },
+ {
+ 1600000000,
+ 900000,
+ },
+ {
+ 1700000000,
+ 960000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 820000,
+ },
+ {
+ 1000000000,
+ 820000,
+ },
+ {
+ 1500000000,
+ 820000,
+ },
+ {
+ 1600000000,
+ 890000,
+ },
+ {
+ 1700000000,
+ 950000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 810000,
+ },
+ {
+ 1000000000,
+ 810000,
+ },
+ {
+ 1500000000,
+ 810000,
+ },
+ {
+ 1600000000,
+ 880000,
+ },
+ {
+ 1700000000,
+ 930000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 800000,
+ },
+ {
+ 1000000000,
+ 800000,
+ },
+ {
+ 1500000000,
+ 800000,
+ },
+ {
+ 1600000000,
+ 870000,
+ },
+ {
+ 1700000000,
+ 910000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 790000,
+ },
+ {
+ 1000000000,
+ 790000,
+ },
+ {
+ 1500000000,
+ 790000,
+ },
+ {
+ 1600000000,
+ 860000,
+ },
+ {
+ 1700000000,
+ 890000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 780000,
+ },
+ {
+ 1000000000,
+ 780000,
+ },
+ {
+ 1500000000,
+ 780000,
+ },
+ {
+ 1600000000,
+ 850000,
+ },
+ {
+ 1700000000,
+ 880000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 770000,
+ },
+ {
+ 1000000000,
+ 770000,
+ },
+ {
+ 1500000000,
+ 770000,
+ },
+ {
+ 1600000000,
+ 840000,
+ },
+ {
+ 1700000000,
+ 870000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 760000,
+ },
+ {
+ 1000000000,
+ 760000,
+ },
+ {
+ 1500000000,
+ 760000,
+ },
+ {
+ 1600000000,
+ 830000,
+ },
+ {
+ 1700000000,
+ 860000,
+ },
+ },
+ };
+
+static const struct op_points rcar_h3_a53_op_points[NR_H3_A53_OPP] = {
+ {
+ 800000000,
+ 820000,
+ },
+ {
+ 1000000000,
+ 820000,
+ },
+ {
+ 1200000000,
+ 820000,
+ },
+};
+
+static const struct op_points rcar_m3_a57_op_points[EFUSE_AVS_NUM]
+ [NR_M3_A57_OPP] = {
+ {
+ {
+ 500000000,
+ 830000,
+ },
+ {
+ 1000000000,
+ 830000,
+ },
+ {
+ 1500000000,
+ 830000,
+ },
+ {
+ 1600000000,
+ 900000,
+ },
+ {
+ 1700000000,
+ 900000,
+ },
+ {
+ 1800000000,
+ 960000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 820000,
+ },
+ {
+ 1000000000,
+ 820000,
+ },
+ {
+ 1500000000,
+ 820000,
+ },
+ {
+ 1600000000,
+ 890000,
+ },
+ {
+ 1700000000,
+ 890000,
+ },
+ {
+ 1800000000,
+ 950000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 810000,
+ },
+ {
+ 1000000000,
+ 810000,
+ },
+ {
+ 1500000000,
+ 810000,
+ },
+ {
+ 1600000000,
+ 880000,
+ },
+ {
+ 1700000000,
+ 880000,
+ },
+ {
+ 1800000000,
+ 930000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 800000,
+ },
+ {
+ 1000000000,
+ 800000,
+ },
+ {
+ 1500000000,
+ 800000,
+ },
+ {
+ 1600000000,
+ 870000,
+ },
+ {
+ 1700000000,
+ 870000,
+ },
+ {
+ 1800000000,
+ 910000,
+ },
+ },
+
+ {
+ {
+ 500000000,
+ 790000,
+ },
+ {
+ 1000000000,
+ 790000,
+ },
+ {
+ 1500000000,
+ 790000,
+ },
+ {
+ 1600000000,
+ 860000,
+ },
+ {
+ 1700000000,
+ 860000,
+ },
+ {
+ 1800000000,
+ 890000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 780000,
+ },
+ {
+ 1000000000,
+ 780000,
+ },
+ {
+ 1500000000,
+ 780000,
+ },
+ {
+ 1600000000,
+ 850000,
+ },
+ {
+ 1700000000,
+ 850000,
+ },
+ {
+ 1800000000,
+ 880000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 770000,
+ },
+ {
+ 1000000000,
+ 770000,
+ },
+ {
+ 1500000000,
+ 770000,
+ },
+ {
+ 1600000000,
+ 840000,
+ },
+ {
+ 1700000000,
+ 840000,
+ },
+ {
+ 1800000000,
+ 870000,
+ },
+ },
+ {
+ {
+ 500000000,
+ 760000,
+ },
+ {
+ 1000000000,
+ 760000,
+ },
+ {
+ 1500000000,
+ 760000,
+ },
+ {
+ 1600000000,
+ 830000,
+ },
+ {
+ 1700000000,
+ 830000,
+ },
+ {
+ 1800000000,
+ 860000,
+ },
+ },
+ };
+
+static const struct op_points rcar_m3_a53_op_points[NR_M3_A53_OPP] = {
+ {
+ 800000000,
+ 820000,
+ },
+ {
+ 1000000000,
+ 820000,
+ },
+ {
+ 1200000000,
+ 820000,
+ },
+ {
+ 1300000000,
+ 820000,
+ },
+};
+
+uint32_t rcar_dvfs_get_opp_voltage(int domain, int oppnr)
+{
+ if (domain == A57_DOMAIN) {
+ if (oppnr < 0 || oppnr >= current_a57_opp_limit)
+ return ~0;
+
+ /* Protocol requires voltage to be in mV */
+ return current_a57_opp_table[oppnr].volt / 1000;
+ } else if (domain == A53_DOMAIN) {
+ if (oppnr < 0 || oppnr >= current_a53_opp_limit)
+ return ~0;
+
+ /* Protocol requires voltage to be in mV */
+ return current_a53_opp_table[oppnr].volt / 1000;
+ }
+
+ return ~0;
+}
+
+uint32_t rcar_dvfs_get_opp_frequency(int domain, int oppnr)
+{
+ if (domain == A57_DOMAIN) {
+ if (oppnr < 0 || oppnr >= current_a57_opp_limit)
+ return ~0;
+
+ /* Protocol requires frequency to be in Hz */
+ return current_a57_opp_table[oppnr].freq;
+ } else if (domain == A53_DOMAIN) {
+ if (oppnr < 0 || oppnr >= current_a53_opp_limit)
+ return ~0;
+
+ /* Protocol requires frequency to be in Hz */
+ return current_a53_opp_table[oppnr].freq;
+ }
+
+ return ~0;
+}
+
+static unsigned long pll_clk_parent_rate(void)
+{
+ static const unsigned long extal_freq[] = {
+ 16660000U, /* MD14_MD13_TYPE_0 */
+ 20000000U, /* MD14_MD13_TYPE_1 */
+ 25000000U, /* MD14_MD13_TYPE_2 */
+ 33330000U, /* MD14_MD13_TYPE_3 */
+ };
+ unsigned long rate;
+ int idx;
+
+ idx = (mmio_read_32(RCAR_MODEMR) & MODEMR_BOOT_PLL_MASK) >>
+ MODEMR_BOOT_PLL_SHIFT;
+
+ rate = extal_freq[idx];
+ /* Divider setting of EXTAL input is 1/2 when MD14=1 MD13=1 */
+ if (idx == MD14_MD13_TYPE_3)
+ rate = DIV_ROUND(rate, 2);
+
+ return rate;
+}
+
+static unsigned long pll0_clk_round_rate(unsigned long rate)
+{
+ unsigned long parent_rate = pll_clk_parent_rate();
+ unsigned int mult;
+
+ if (rate < Z_CLK_MAX_THRESHOLD)
+ rate = Z_CLK_MAX_THRESHOLD; /* Set lowest value: 1.5GHz */
+
+ mult = DIV_ROUND(rate, parent_rate);
+ mult = max(mult, 90U); /* Lowest value is 1.5GHz (stc == 90) */
+ mult = min(mult, 108U);
+
+ rate = parent_rate * mult;
+ /* Round to closest value at 100MHz unit */
+ rate = 100000000 * DIV_ROUND(rate, 100000000);
+
+ return rate;
+}
+
+static unsigned long pll0_clk_recalc_rate(void)
+{
+ unsigned long parent_rate = pll_clk_parent_rate();
+ unsigned int val;
+ unsigned long rate;
+
+ val =
+ (mmio_read_32(CPG_PLL0CR) & CPG_PLLCR_STC_MASK) >> CPG_PLLCR_STC_SHIFT;
+
+ rate = parent_rate * (val + 1);
+ /* Round to closest value at 100MHz unit */
+ rate = 100000000 * DIV_ROUND(rate, 100000000);
+
+ return rate;
+}
+
+static int pll0_clk_set_rate(unsigned long rate)
+{
+ unsigned long parent_rate = pll_clk_parent_rate();
+ unsigned int stc_val;
+ uint32_t val;
+
+ stc_val = DIV_ROUND(rate, parent_rate);
+ stc_val = max(stc_val, 90U); /* Lowest value is 1.5GHz (stc == 90) */
+ stc_val = min(stc_val, 108U);
+
+ stc_val -= 1;
+ val = mmio_read_32(CPG_PLL0CR);
+ val &= ~CPG_PLLCR_STC_MASK;
+ val |= stc_val << CPG_PLLCR_STC_SHIFT;
+ mmio_write_32(CPG_PLL0CR, val);
+
+ while (!(mmio_read_32(CPG_BASE + CPG_PLLECR) & CPG_PLLECR_PLL0ST))
+ continue;
+
+ return 0;
+}
+
+static unsigned long pll2_clk_round_rate(unsigned long rate)
+{
+ unsigned long parent_rate = pll_clk_parent_rate();
+ unsigned int mult;
+
+ if (rate < Z2_CLK_MAX_THRESHOLD)
+ rate = Z2_CLK_MAX_THRESHOLD; /* Set lowest value: 1.2GHz */
+
+ mult = DIV_ROUND(rate, parent_rate);
+ mult = max(mult, 72U); /* Lowest value is 1.2GHz (stc == 72) */
+ mult = min(mult, 78U);
+
+ rate = parent_rate * mult;
+ /* Round to closest value at 100MHz unit */
+ rate = 100000000 * DIV_ROUND(rate, 100000000);
+
+ return rate;
+}
+
+static unsigned long pll2_clk_recalc_rate(void)
+{
+ unsigned long parent_rate = pll_clk_parent_rate();
+ unsigned int val;
+ unsigned long rate;
+
+ val =
+ (mmio_read_32(CPG_PLL2CR) & CPG_PLLCR_STC_MASK) >> CPG_PLLCR_STC_SHIFT;
+
+ rate = parent_rate * (val + 1);
+ /* Round to closest value at 100MHz unit */
+ rate = 100000000 * DIV_ROUND(rate, 100000000);
+
+ return rate;
+}
+
+static int pll2_clk_set_rate(unsigned long rate)
+{
+ unsigned long parent_rate = pll_clk_parent_rate();
+ unsigned int stc_val;
+ uint32_t val;
+
+ stc_val = DIV_ROUND(rate, parent_rate);
+ stc_val = max(stc_val, 72U); /* Lowest value is 1.2GHz (stc == 72) */
+ stc_val = min(stc_val, 78U);
+
+ stc_val -= 1;
+ val = mmio_read_32(CPG_PLL2CR);
+ val &= ~CPG_PLLCR_STC_MASK;
+ val |= stc_val << CPG_PLLCR_STC_SHIFT;
+ mmio_write_32(CPG_PLL2CR, val);
+
+ while (!(mmio_read_32(CPG_BASE + CPG_PLLECR) & CPG_PLLECR_PLL2ST))
+ continue;
+
+ return 0;
+}
+
+static unsigned long z_clk_round_rate(
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long prate = *parent_rate;
+ unsigned int mult;
+
+ if (!prate)
+ prate = 1;
+
+ if (rate <= Z_CLK_MAX_THRESHOLD) { /* Focus on changing z-clock */
+ prate = Z_CLK_MAX_THRESHOLD; /* Set parent to: 1.5GHz */
+ mult = DIV_ROUND(rate * 32, prate);
+ } else {
+ /* Focus on changing parent. Fix z-clock divider is 32/32 */
+ mult = 32;
+ }
+ mult = max(mult, 1U);
+ mult = min(mult, 32U);
+
+ /* Re-calculate the parent_rate to propagate new rate for it */
+ prate = DIV_ROUND(rate * 32, mult);
+ prate = 100000000 * DIV_ROUND(prate, 100000000);
+ rate = 100000000 * DIV_ROUND(prate / 32 * mult, 100000000);
+ *parent_rate = prate;
+
+ return rate;
+}
+
+static unsigned long z_clk_recalc_rate(unsigned long parent_rate)
+{
+ unsigned int mult;
+ unsigned int val;
+ unsigned long rate;
+
+ val = (mmio_read_32(CPG_FRQCRC) & CPG_FRQCRC_ZFC_MASK) >>
+ CPG_FRQCRC_ZFC_SHIFT;
+ mult = 32 - val;
+
+ rate = DIV_ROUND(parent_rate * mult, 32);
+ /* Round to closest value at 100MHz unit */
+ rate = 100000000 * DIV_ROUND(rate, 100000000);
+
+ return rate;
+}
+
+static int z_clk_set_rate(unsigned long rate, unsigned long parent_rate)
+{
+ unsigned int mult;
+ uint32_t val, kick;
+ unsigned int i;
+
+ if (rate <= Z_CLK_MAX_THRESHOLD) { /* Focus on changing z-clock */
+ parent_rate = Z_CLK_MAX_THRESHOLD; /* Set parent to: 1.5GHz */
+ mult = DIV_ROUND(rate * 32, parent_rate);
+ } else {
+ mult = 32;
+ }
+ mult = max(mult, 1U);
+ mult = min(mult, 32U);
+
+ if (mmio_read_32(CPG_FRQCRB) & CPG_FRQCRB_KICK)
+ return -1;
+
+ val = mmio_read_32(CPG_FRQCRC);
+ val &= ~CPG_FRQCRC_ZFC_MASK;
+ val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT;
+ mmio_write_32(CPG_FRQCRC, val);
+
+ /*
+ * Set KICK bit in FRQCRB to update hardware setting and wait for
+ * clock change completion.
+ */
+ kick = mmio_read_32(CPG_FRQCRB);
+ kick |= CPG_FRQCRB_KICK;
+ mmio_write_32(CPG_FRQCRB, kick);
+
+ /*
+ * Note: There is no HW information about the worst case latency.
+ *
+ * Using experimental measurements, it seems that no more than
+ * ~10 iterations are needed, independently of the CPU rate.
+ * Since this value might be dependent of external xtal rate, pll1
+ * rate or even the other emulation clocks rate, use 1000 as a
+ * "super" safe value.
+ */
+ for (i = 1000; i; i--) {
+ if (!(mmio_read_32(CPG_FRQCRB) & CPG_FRQCRB_KICK)) {
+ return 0;
+ }
+
+ /*cpu_relax();*/
+ }
+
+ return -1;
+}
+
+static unsigned long z2_clk_round_rate(
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long prate = *parent_rate;
+ unsigned int mult;
+
+ if (!prate)
+ prate = 1;
+
+ if (rate <= Z2_CLK_MAX_THRESHOLD) { /* Focus on changing z2-clock */
+ prate = Z2_CLK_MAX_THRESHOLD; /* Set parent to: 1.2GHz */
+ mult = DIV_ROUND(rate * 32, prate);
+ } else {
+ /* Focus on changing parent. Fix z2-clock divider is 32/32 */
+ mult = 32;
+ }
+ mult = max(mult, 1U);
+ mult = min(mult, 32U);
+
+ /* Re-calculate the parent_rate to propagate new rate for it */
+ prate = DIV_ROUND(rate * 32, mult);
+ prate = 100000000 * DIV_ROUND(prate, 100000000);
+ rate = 100000000 * DIV_ROUND(prate / 32 * mult, 100000000);
+ *parent_rate = prate;
+
+ return rate;
+}
+
+static unsigned long z2_clk_recalc_rate(unsigned long parent_rate)
+{
+ unsigned int mult;
+ unsigned int val;
+ unsigned long rate;
+
+ val = mmio_read_32(CPG_FRQCRC) & CPG_FRQCRC_Z2FC_MASK;
+ mult = 32 - val;
+
+ rate = DIV_ROUND(parent_rate * mult, 32);
+ /* Round to closest value at 100MHz unit */
+ rate = 100000000 * DIV_ROUND(rate, 100000000);
+
+ return rate;
+}
+
+static int z2_clk_set_rate(unsigned long rate, unsigned long parent_rate)
+{
+ unsigned int mult;
+ uint32_t val, kick;
+ unsigned int i;
+
+ if (rate <= Z2_CLK_MAX_THRESHOLD) { /* Focus on changing z2-clock */
+ parent_rate = Z2_CLK_MAX_THRESHOLD; /* Set parent to: 1.2GHz */
+ mult = DIV_ROUND(rate * 32, parent_rate);
+ } else {
+ mult = 32;
+ }
+ mult = max(mult, 1U);
+ mult = min(mult, 32U);
+
+ if (mmio_read_32(CPG_FRQCRB) & CPG_FRQCRB_KICK)
+ return -1;
+
+ val = mmio_read_32(CPG_FRQCRC);
+ val &= ~CPG_FRQCRC_Z2FC_MASK;
+ val |= 32 - mult;
+ mmio_write_32(CPG_FRQCRC, val);
+
+ /*
+ * Set KICK bit in FRQCRB to update hardware setting and wait for
+ * clock change completion.
+ */
+ kick = mmio_read_32(CPG_FRQCRB);
+ kick |= CPG_FRQCRB_KICK;
+ mmio_write_32(CPG_FRQCRB, kick);
+
+ /*
+ * Note: There is no HW information about the worst case latency.
+ *
+ * Using experimental measurements, it seems that no more than
+ * ~10 iterations are needed, independently of the CPU rate.
+ * Since this value might be dependent of external xtal rate, pll1
+ * rate or even the other emulation clocks rate, use 1000 as a
+ * "super" safe value.
+ */
+ for (i = 1000; i; i--) {
+ if (!(mmio_read_32(CPG_FRQCRB) & CPG_FRQCRB_KICK)) {
+ return 0;
+ }
+
+ /*cpu_relax();*/
+ }
+
+ return -1;
+}
+
+#if 0
+static int set_voltage(unsigned long volt)
+{
+/* TODO : Not supported because it conflicts with I2C control from the kernel */
+ uint8_t val;
+ int ret;
+
+ if (volt < BD9571MWV_MIN_MV * 1000 || volt > BD9571MWV_MAX_MV * 1000)
+ return -1;
+
+ val = DIV_ROUND(volt, BD9571MWV_STEP_MV * 1000);
+ val &= REG_DATA_DVFS_SetVID_MASK;
+
+ ret = rcar_iic_dvfs_send(SLAVE_ADDR_PMIC, REG_ADDR_DVFS_SetVID, val);
+ if (ret) {
+ return ret;
+ }
+ return 0;
+}
+#endif
+
+#if 0
+static unsigned long get_voltage(void)
+{
+ uint8_t val;
+ unsigned long volt;
+ int ret;
+
+ ret = rcar_iic_dvfs_receive(SLAVE_ADDR_PMIC, REG_ADDR_DVFS_SetVID, &val);
+ if (ret) {
+ return ret;
+ }
+
+ val &= REG_DATA_DVFS_SetVID_MASK;
+ volt = (unsigned long)val * BD9571MWV_STEP_MV * 1000;
+
+ return volt;
+}
+#endif
+
+static const struct op_points *find_opp(int domain, unsigned long freq)
+{
+ int i;
+
+ if (domain == A57_DOMAIN) {
+ for (i = 0; i < current_a57_opp_limit; i++) {
+ if (current_a57_opp_table[i].freq == freq)
+ return &current_a57_opp_table[i];
+ }
+ } else if (domain == A53_DOMAIN) {
+ for (i = 0; i < current_a53_opp_limit; i++) {
+ if (current_a53_opp_table[i].freq == freq)
+ return &current_a53_opp_table[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int set_a57_opp(unsigned long target_freq)
+{
+ unsigned long freq, old_freq, prate, old_prate;
+ const struct op_points *opp;
+ int ret;
+
+ prate = 0;
+ freq = z_clk_round_rate(target_freq, &prate);
+
+ old_prate = pll0_clk_recalc_rate();
+ old_freq = z_clk_recalc_rate(old_prate);
+
+ /* Return early if nothing to do */
+ if (old_freq == freq) {
+ return 0;
+ }
+
+ opp = find_opp(A57_DOMAIN, freq);
+ if (!opp) {
+ return -1;
+ }
+
+ /* Scaling up? Scale voltage before frequency */
+ prate = pll0_clk_round_rate(prate);
+ if (old_prate != prate)
+ pll0_clk_set_rate(prate);
+
+ ret = z_clk_set_rate(freq, prate);
+ if (ret) {
+ /* Restore voltage */
+ if (old_prate != prate)
+ pll0_clk_set_rate(old_prate);
+ }
+
+ return ret;
+}
+
+static int set_a53_opp(unsigned long target_freq)
+{
+ unsigned long freq, old_freq, prate, old_prate;
+ const struct op_points *opp;
+ int ret = 0;
+
+ prate = 0;
+ freq = z2_clk_round_rate(target_freq, &prate);
+
+ old_prate = pll2_clk_recalc_rate();
+ old_freq = z2_clk_recalc_rate(old_prate);
+
+ /* Return early if nothing to do */
+ if (old_freq == freq) {
+ return 0;
+ }
+
+ opp = find_opp(A53_DOMAIN, freq);
+ if (!opp) {
+ return -1;
+ }
+
+ prate = pll2_clk_round_rate(prate);
+ if (old_prate != prate)
+ pll2_clk_set_rate(prate);
+
+ ret = z2_clk_set_rate(freq, prate);
+
+ return ret;
+}
+
+int rcar_dvfs_get_nr_opp(int domain)
+{
+ if (domain == A57_DOMAIN)
+ return current_a57_opp_limit;
+ else if (domain == A53_DOMAIN)
+ return current_a53_opp_limit;
+
+ return 0;
+}
+
+int rcar_dvfs_opp_init(void)
+{
+ uint32_t product;
+
+ if (dvfs_inited)
+ return 0;
+
+ product = mmio_read_32(RCAR_PRR) & RCAR_PRODUCT_MASK;
+
+ if (product == RCAR_PRODUCT_H3) {
+ current_a57_opp_limit = ARRAY_SIZE(rcar_h3_a57_op_points[efuse_avs]);
+ current_a57_opp_table = &rcar_h3_a57_op_points[efuse_avs][0];
+
+ current_a53_opp_limit = ARRAY_SIZE(rcar_h3_a53_op_points);
+ current_a53_opp_table = &rcar_h3_a53_op_points[0];
+ } else if (product == RCAR_PRODUCT_M3) {
+ current_a57_opp_limit = ARRAY_SIZE(rcar_m3_a57_op_points[efuse_avs]);
+ current_a57_opp_table = &rcar_m3_a57_op_points[efuse_avs][0];
+
+ current_a53_opp_limit = ARRAY_SIZE(rcar_m3_a53_op_points);
+ current_a53_opp_table = &rcar_m3_a53_op_points[0];
+ } else
+ return -1;
+
+ dvfs_inited = 1;
+
+ return 0;
+}
+
+/*
+ * Static helper functions
+ */
+
+static int do_rcar_clock_set_rate(
+ fwk_id_t dev_id,
+ uint64_t rate,
+ enum mod_clock_round_mode round_mode)
+{
+ struct rcar_clock_dev_ctx *ctx;
+ int ret = 0;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ /* base clock */
+ switch (ctx->config->rate_table->divider_reg) {
+ case MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT:
+ ret = set_a57_opp(rate);
+ break;
+ case MOD_RCAR_CLOCK_A53_DIVIDER_DIV_EXT:
+ ret = set_a53_opp(rate);
+ break;
+ default:
+ return FWK_E_SUPPORT;
+ }
+ ctx->current_rate = rate;
+ return ret;
+}
+
+/*
+ * Clock driver API functions
+ */
+
+static int rcar_clock_set_rate(
+ fwk_id_t dev_id,
+ uint64_t rate,
+ enum mod_clock_round_mode round_mode)
+{
+ struct rcar_clock_dev_ctx *ctx;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+
+ if (!ctx->initialized)
+ return FWK_E_INIT;
+
+ if (ctx->current_state == MOD_CLOCK_STATE_STOPPED)
+ return FWK_E_PWRSTATE;
+
+ return do_rcar_clock_set_rate(dev_id, rate, round_mode);
+}
+
+static int rcar_clock_get_rate(fwk_id_t dev_id, uint64_t *rate)
+{
+ struct rcar_clock_dev_ctx *ctx;
+
+ if (rate == NULL)
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id);
+ *rate = ctx->current_rate;
+
+ return FWK_SUCCESS;
+}
+
+static const struct mod_rcar_clock_drv_api api_clock = {
+ .set_rate = rcar_clock_set_rate,
+ .get_rate = rcar_clock_get_rate,
+};
+
+/*
+ * Framework handler functions
+ */
+
+static int rcar_clock_init(
+ fwk_id_t module_id,
+ unsigned int element_count,
+ const void *data)
+{
+ struct mod_ext_clock_rate *ext;
+ module_ctx.dev_count = element_count;
+
+ if (element_count == 0)
+ return FWK_SUCCESS;
+
+ module_ctx.dev_ctx_table =
+ fwk_mm_calloc(element_count, sizeof(struct rcar_clock_dev_ctx));
+ if (module_ctx.dev_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ ext = (struct mod_ext_clock_rate *)data;
+ module_ctx.extal_clk = ext->ext_clk_rate;
+
+ return FWK_SUCCESS;
+}
+
+static int rcar_clock_element_init(
+ fwk_id_t element_id,
+ unsigned int sub_element_count,
+ const void *data)
+{
+ unsigned int i = 0;
+ uint64_t current_rate;
+ uint64_t last_rate = 0;
+ struct rcar_clock_dev_ctx *ctx;
+ const struct mod_rcar_clock_dev_config *dev_config = data;
+
+ if (!fwk_module_is_valid_element_id(element_id))
+ return FWK_E_PARAM;
+
+ ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id);
+
+ /* Verify that the rate entries in the device's lookup table are ordered */
+ while (i < dev_config->rate_count) {
+ current_rate = dev_config->rate_table[i].rate;
+
+ /* The rate entries must be in ascending order */
+ if (current_rate < last_rate)
+ return FWK_E_DATA;
+
+ last_rate = current_rate;
+ i++;
+ }
+
+ ctx->config = dev_config;
+
+ if (ctx->config->defer_initialization)
+ return FWK_SUCCESS;
+
+ ctx->current_state = MOD_CLOCK_STATE_RUNNING;
+ ctx->initialized = true;
+
+ /*
+ * Clock devices that are members of a clock group must skip initialization
+ * at this time since they will be set to a specific rate by the CSS Clock
+ * driver during the start stage or in response to a notification.
+ */
+ if (ctx->config->is_group_member)
+ return FWK_SUCCESS;
+
+ return do_rcar_clock_set_rate(
+ element_id, dev_config->initial_rate, MOD_CLOCK_ROUND_MODE_NONE);
+}
+
+static int rcar_clock_process_bind_request(
+ fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ *api = &api_clock;
+ return FWK_SUCCESS;
+}
+static int rcar_clock_start(fwk_id_t id)
+{
+ rcar_dvfs_opp_init();
+ return FWK_SUCCESS;
+}
+
+const struct fwk_module module_rcar_clock = {
+ .name = "RCAR Clock Driver",
+ .type = FWK_MODULE_TYPE_DRIVER,
+ .api_count = MOD_RCAR_CLOCK_API_COUNT,
+ .event_count = 0,
+ .init = rcar_clock_init,
+ .element_init = rcar_clock_element_init,
+ .process_bind_request = rcar_clock_process_bind_request,
+ .start = rcar_clock_start,
+};
diff --git a/product/rcar/scp_ramfw/config_rcar_clock.c b/product/rcar/scp_ramfw/config_rcar_clock.c
new file mode 100644
index 00000000..ae065366
--- /dev/null
+++ b/product/rcar/scp_ramfw/config_rcar_clock.c
@@ -0,0 +1,151 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <system_clock.h>
+
+#include <mod_rcar_clock.h>
+
+#include <fwk_element.h>
+#include <fwk_id.h>
+#include <fwk_macros.h>
+#include <fwk_module.h>
+
+/*
+ * Rate lookup tables
+ */
+
+static struct mod_rcar_clock_rate rate_table_cpu_a53[] = {
+ {
+ .rate = 800 * FWK_MHZ,
+ .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL2,
+ .divider_reg = MOD_RCAR_CLOCK_A53_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+ {
+ .rate = 1000 * FWK_MHZ,
+ .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL2,
+ .divider_reg = MOD_RCAR_CLOCK_A53_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+ {
+ .rate = 1200 * FWK_MHZ,
+ .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL2,
+ .divider_reg = MOD_RCAR_CLOCK_A53_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+};
+
+static struct mod_rcar_clock_rate rate_table_cpu_a57[] = {
+ {
+ .rate = 500 * FWK_MHZ,
+ .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .divider_reg = MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+ {
+ .rate = 1000 * FWK_MHZ,
+ .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .divider_reg = MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+ {
+ .rate = 1500 * FWK_MHZ,
+ .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .divider_reg = MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+ {
+ .rate = 1600 * FWK_MHZ,
+ .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .divider_reg = MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+ {
+ .rate = 1700 * FWK_MHZ,
+ .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0,
+ .divider_reg = MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT,
+ .divider = 1, /* Rate adjusted via CPU PLL */
+ },
+};
+
+static const struct fwk_element rcar_clock_element_table[] = {
+ /*
+ * A53 CPUS
+ */
+ {
+ .name = "CLUS0_CPU0",
+ .data = &((struct mod_rcar_clock_dev_config){
+ .type = MOD_RCAR_CLOCK_TYPE_CLUSTER,
+ .is_group_member = false,
+ .rate_table = rate_table_cpu_a53,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a53),
+ .initial_rate = 1200 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ /*
+ * A57 CPUS
+ */
+ {
+ .name = "CLUS0_CPU4",
+ .data = &((struct mod_rcar_clock_dev_config){
+ .type = MOD_RCAR_CLOCK_TYPE_CLUSTER,
+ .is_group_member = false,
+ .rate_table = rate_table_cpu_a57,
+ .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a57),
+ .initial_rate = 1500 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ /*
+ * VPU
+ */
+ {
+ .name = "VPU",
+ .data = &((struct mod_rcar_clock_dev_config){
+ .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = true,
+ .initial_rate = 600 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ /*
+ * DPU
+ */
+ {
+ .name = "ACLKDP",
+ .data = &((struct mod_rcar_clock_dev_config){
+ .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = true,
+ .initial_rate = (CLOCK_RATE_SYSPLLCLK / 3),
+ .defer_initialization = false,
+ }),
+ },
+ {
+ .name = "DPU_M0",
+ .data = &((struct mod_rcar_clock_dev_config){
+ .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE,
+ .is_group_member = true,
+ .initial_rate = 260 * FWK_MHZ,
+ .defer_initialization = false,
+ }),
+ },
+ {}, /* Termination description. */
+};
+
+static const struct fwk_element *rcar_clock_get_element_table(
+ fwk_id_t module_id)
+{
+ return rcar_clock_element_table;
+}
+
+struct fwk_module_config config_rcar_clock = {
+ .elements = FWK_MODULE_DYNAMIC_ELEMENTS(rcar_clock_get_element_table),
+ .data = &((struct mod_ext_clock_rate){
+ .ext_clk_rate = PLL_BASE_CLOCK,
+ }),
+};