aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Royer <nroyer@baylibre.com>2020-09-27 17:41:19 +0200
committernicola-mazzucato-arm <42373140+nicola-mazzucato-arm@users.noreply.github.com>2020-10-15 17:45:38 +0100
commite88b03fd71f44a24bdf0cc72838f276404de7b0b (patch)
tree2e61a057816b264221bb4e50e8b2d55fb65879bc
parent9407c9b53bc33339c6d28a68fc1d4a1fd9951f73 (diff)
rcar/module: add rcar power_domain module and config data
Change-Id: I44796fe0ea4c66e90d77c6cd93843914a7978f3b 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_power_domain/include/mod_power_domain.h16
-rw-r--r--product/rcar/module/rcar_power_domain/include/mod_rcar_power_domain.h1149
-rw-r--r--product/rcar/module/rcar_power_domain/src/Makefile11
-rw-r--r--product/rcar/module/rcar_power_domain/src/mod_rcar_power_domain.c1567
-rw-r--r--product/rcar/scp_ramfw/config_rcar_power_domain.c423
-rw-r--r--product/rcar/scp_ramfw/config_rcar_power_domain.h38
6 files changed, 3204 insertions, 0 deletions
diff --git a/product/rcar/module/rcar_power_domain/include/mod_power_domain.h b/product/rcar/module/rcar_power_domain/include/mod_power_domain.h
new file mode 100644
index 00000000..1ee6e9a7
--- /dev/null
+++ b/product/rcar/module/rcar_power_domain/include/mod_power_domain.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_POWER_DOMAIN_H
+#define MOD_POWER_DOMAIN_H
+
+#define FWK_MODULE_IDX_POWER_DOMAIN FWK_MODULE_IDX_RCAR_POWER_DOMAIN
+#define fwk_module_id_power_domain fwk_module_id_rcar_power_domain
+
+#include "mod_rcar_power_domain.h"
+
+#endif /* MOD_POWER_DOMAIN_H */
diff --git a/product/rcar/module/rcar_power_domain/include/mod_rcar_power_domain.h b/product/rcar/module/rcar_power_domain/include/mod_rcar_power_domain.h
new file mode 100644
index 00000000..03b8e7e3
--- /dev/null
+++ b/product/rcar/module/rcar_power_domain/include/mod_rcar_power_domain.h
@@ -0,0 +1,1149 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2015-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_RCAR_POWER_DOMAIN_H
+#define MOD_RCAR_POWER_DOMAIN_H
+
+#include <fwk_element.h>
+#include <fwk_id.h>
+#include <fwk_module_idx.h>
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/*!
+ * \addtogroup GroupRCARModule RCAR Product Modules
+ * @{
+ */
+
+/*!
+ * \defgroup GroupRCARPowerDomain Power Domains
+ *
+ * \details Support for querying and setting the power state of power domains
+ * such as CPU, cluster and GPU power domains.
+ *
+ * @{
+ */
+
+/*!
+ * Maximum number of states for a power domain.
+ */
+#define MOD_PD_STATE_COUNT_MAX 16
+
+/*!
+ * \brief Types of power domain.
+ */
+enum mod_pd_type {
+ /*! Processor. */
+ MOD_PD_TYPE_CORE,
+ /*! Processor cluster. */
+ MOD_PD_TYPE_CLUSTER,
+ /*! Generic device. */
+ MOD_PD_TYPE_DEVICE,
+ /*! Debug device. */
+ MOD_PD_TYPE_DEVICE_DEBUG,
+ /*! System. */
+ MOD_PD_TYPE_SYSTEM,
+ /*! Number of power domain types. */
+ MOD_PD_TYPE_COUNT
+};
+
+/*!
+ * \brief Bit width of power domain type within attributes.
+ */
+#define MOD_PD_TYPE_BITS_WIDTH \
+ ((sizeof(unsigned int) * CHAR_BIT) - __builtin_clz(MOD_PD_TYPE_COUNT - 1))
+
+/*!
+ * \brief Attributes of a power domain.
+ */
+struct mod_pd_attributes {
+ /*! Type of the power domain. */
+ enum mod_pd_type pd_type : MOD_PD_TYPE_BITS_WIDTH;
+
+ /*! \internal */
+ unsigned int not_used
+ : ((sizeof(unsigned int) * CHAR_BIT) - MOD_PD_TYPE_BITS_WIDTH);
+};
+
+/*!
+ * \brief Identifiers for the power levels.
+ */
+enum mod_pd_level {
+ /*! Level 0. */
+ MOD_PD_LEVEL_0,
+ /*! Level 1. */
+ MOD_PD_LEVEL_1,
+ /*! Level 2. */
+ MOD_PD_LEVEL_2,
+ /*! Level 3. */
+ MOD_PD_LEVEL_3,
+ /*! Number of power domain levels. */
+ MOD_PD_LEVEL_COUNT
+};
+
+/*!
+ * \brief Number of bits used for each level within a power domain tree
+ * position.
+ */
+#define MOD_PD_TREE_POS_BITS_PER_LEVEL 8
+
+/*!
+ * \brief Shifts for the power level fields within a power domain tree position.
+ */
+enum {
+ /*! Number of bits to shift for the power level 0 field. */
+ MOD_PD_TREE_POS_LEVEL_0_SHIFT =
+ MOD_PD_LEVEL_0 * MOD_PD_TREE_POS_BITS_PER_LEVEL,
+ /*! Number of bits to shift for the power level 1 field. */
+ MOD_PD_TREE_POS_LEVEL_1_SHIFT =
+ MOD_PD_LEVEL_1 * MOD_PD_TREE_POS_BITS_PER_LEVEL,
+ /*! Number of bits to shift for the power level 2 field. */
+ MOD_PD_TREE_POS_LEVEL_2_SHIFT =
+ MOD_PD_LEVEL_2 * MOD_PD_TREE_POS_BITS_PER_LEVEL,
+ /*! Number of bits to shift for the power level 3 field. */
+ MOD_PD_TREE_POS_LEVEL_3_SHIFT =
+ MOD_PD_LEVEL_3 * MOD_PD_TREE_POS_BITS_PER_LEVEL,
+ MOD_PD_TREE_POS_LEVEL_SHIFT =
+ MOD_PD_LEVEL_COUNT * MOD_PD_TREE_POS_BITS_PER_LEVEL,
+};
+
+/*!
+ * \brief Mask for the power level fields within a power domain tree position.
+ */
+#define MOD_PD_TREE_POS_LEVEL_MASK UINT64_C(0xFF)
+
+/*!
+ * \brief Build the power domain tree position of a power domain.
+ */
+#define MOD_PD_TREE_POS(LEVEL, LEVEL_3, LEVEL_2, LEVEL_1, LEVEL_0) \
+ ((((uint64_t)((LEVEL)&MOD_PD_TREE_POS_LEVEL_MASK)) \
+ << MOD_PD_TREE_POS_LEVEL_SHIFT) | \
+ (((uint64_t)((LEVEL_3)&MOD_PD_TREE_POS_LEVEL_MASK)) \
+ << MOD_PD_TREE_POS_LEVEL_3_SHIFT) | \
+ (((uint64_t)((LEVEL_2)&MOD_PD_TREE_POS_LEVEL_MASK)) \
+ << MOD_PD_TREE_POS_LEVEL_2_SHIFT) | \
+ (((uint64_t)((LEVEL_1)&MOD_PD_TREE_POS_LEVEL_MASK)) \
+ << MOD_PD_TREE_POS_LEVEL_1_SHIFT) | \
+ (((uint64_t)((LEVEL_0)&MOD_PD_TREE_POS_LEVEL_MASK)) \
+ << MOD_PD_TREE_POS_LEVEL_0_SHIFT))
+
+/*!
+ * \brief Representation of the invalid tree position. Used when checking that
+ * power domains are declared in increasing order of their tree position.
+ */
+#define MOD_PD_INVALID_TREE_POS MOD_PD_TREE_POS(MOD_PD_LEVEL_COUNT, 0, 0, 0, 0)
+
+/*!
+ * \brief Power domain module configuration.
+ */
+struct mod_power_domain_config {
+ /*!
+ * Identifiers of the modules and elements which are granted access to the
+ * restricted interface of the module.
+ */
+ fwk_id_t *authorized_id_table;
+
+ /*! Number of identifiers in the "authorized_id_table" table. */
+ size_t authorized_id_table_size;
+};
+
+/*!
+ * \brief Configuration data for a power domain.
+ *
+ * \details The configuration data of a power domain describes the power
+ * domain and its relationship with other power domains in the system.
+ * Assumptions about the organisation of the power domains, underlying the
+ * way in which power domains are described here, are:
+ * - Each power domain can be represented as a node in a power domain
+ * topology tree.
+ * - Sibling power domains are mutually exclusive.
+ * - Parent power domains are shared by the children.
+ *
+ * For more information, refer to the "Arm Power State Coordination
+ * Interface Platform Design Document", available here:
+ * http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_
+ * Coordination_Interface_PDD_v1_1_DEN0022D.pdf
+ */
+struct mod_power_domain_element_config {
+ /*!
+ * \brief Defines the position of the power domain within the power domain
+ * tree.
+ *
+ * \details Each child of a power domain is assigned a number ranging from 0
+ * to 255. Compute the position of a power domain from the position of
+ * its parent 'parent_pos' (the number assigned to the power domain as
+ * a child of its parent is 'child_pos') as follows:
+ *
+ * tree_pos = (parent_pos - (1 << MOD_PD_TREE_POS_LEVEL_SHIFT)) +
+ * (child_pos << (8*pd_level))
+ *
+ * The position of the top-level domain is defined as:
+ * (level of the top-level domain) << MOD_PD_TREE_POS_LEVEL_SHIFT
+ *
+ * If the power domain hierarchy maps to the core hierarchy (based on
+ * MPIDR levels of affinity), derive the position of the core power
+ * domains from the Aff0, Aff1, Aff2 and Aff3 fields of the MPIDR
+ * registers of the cores as follows:
+ *
+ * core power domain position = (Aff3 << MOD_PD_TREE_LEVEL_3_SHIFT) +
+ * (Aff2 << MOD_PD_TREE_LEVEL_2_SHIFT) +
+ * (Aff1 << MOD_PD_TREE_LEVEL_1_SHIFT) +
+ * (Aff0 << MOD_PD_TREE_LEVEL_0_SHIFT)
+ *
+ * In the module configuration data, the power domains have to be in
+ * increasing order of their power domain position. Thus, the power
+ * domains with the lowest power level have to be first and the system
+ * power domain has to be last. This table must contain at least one
+ * element, the system power domain.
+ */
+ uint64_t tree_pos;
+
+ /*!
+ * Identifier of the module or element providing the driver for the power
+ * domain.
+ */
+ fwk_id_t driver_id;
+
+ /*! Identifier of the driver API. */
+ fwk_id_t api_id;
+
+ /*! Attributes of the power domain. */
+ struct mod_pd_attributes attributes;
+
+ /*!
+ * Mask of the allowed power states for the power domain, for each possible
+ * power state of its parent. Table of allowed state masks. The bit 'i' of
+ * the entry 'j' is equal to one if and only if the state 'i' for the power
+ * domain is allowed when its parent is in state 'j'. The number of bits of
+ * each entry of this table has to be greater or equal than
+ * MOD_PD_STATE_COUNT_MAX.
+ */
+ const uint32_t *allowed_state_mask_table;
+
+ /*!
+ * Power state names. The power state names are used only for debug
+ * purposes and this table is optional. If not provided then the default
+ * power state names (OFF, ON, SLEEP, 3, 4, ...) are used for the power
+ * domain states.
+ */
+ const char **state_name_table;
+
+ /*! Size of the table of allowed state masks */
+ size_t allowed_state_mask_table_size;
+
+ /*! Size of the table of allowed state masks */
+ size_t state_name_table_size;
+};
+
+/*!
+ * \brief Types of system shutdown
+ */
+enum mod_pd_system_shutdown {
+ /*! System shutdown */
+ MOD_PD_SYSTEM_SHUTDOWN,
+
+ /*! System cold reset */
+ MOD_PD_SYSTEM_COLD_RESET,
+
+ /*! System warm reset */
+ MOD_PD_SYSTEM_WARM_RESET,
+
+ /*! Sub-system reset */
+ MOD_PD_SYSTEM_SUB_SYSTEM_RESET,
+
+ /*! Forced system shutdown */
+ MOD_PD_SYSTEM_FORCED_SHUTDOWN,
+
+ /*! Number of shutdown types */
+ MOD_PD_SYSTEM_COUNT,
+};
+
+/*!
+ * \brief Identifiers of the power domain states. The other states are defined
+ * by the platform code for more flexibility. The power states defined by
+ * the platform must be ordered from the shallowest to the deepest state.
+ */
+enum mod_pd_state {
+ /*! \c OFF power state */
+ MOD_PD_STATE_OFF,
+
+ /*! \c ON power state */
+ MOD_PD_STATE_ON,
+
+ /*! \c SLEEP power state */
+ MOD_PD_STATE_SLEEP,
+
+ /*! Number of power states */
+ MOD_PD_STATE_COUNT
+};
+
+/*!
+ * \brief Masks for the power domain states.
+ */
+enum mod_pd_state_mask {
+ MOD_PD_STATE_OFF_MASK = 1 << MOD_PD_STATE_OFF,
+ MOD_PD_STATE_ON_MASK = 1 << MOD_PD_STATE_ON,
+ MOD_PD_STATE_SLEEP_MASK = 1 << MOD_PD_STATE_SLEEP,
+};
+
+/*!
+ * \brief Number of bits for each level state in a composite power state.
+ */
+#define MOD_PD_CS_STATE_BITS_PER_LEVEL 4
+
+/*!
+ * \brief Mask for a composite power state.
+ */
+#define MOD_PD_CS_STATE_MASK ((1 << MOD_PD_CS_STATE_BITS_PER_LEVEL) - 1)
+
+/*!
+ * \brief Shifts for the states and child policies fields in a composite
+ * power state.
+ */
+enum {
+ MOD_PD_CS_LEVEL_0_STATE_SHIFT =
+ MOD_PD_LEVEL_0 * MOD_PD_CS_STATE_BITS_PER_LEVEL,
+ MOD_PD_CS_LEVEL_1_STATE_SHIFT =
+ MOD_PD_LEVEL_1 * MOD_PD_CS_STATE_BITS_PER_LEVEL,
+ MOD_PD_CS_LEVEL_2_STATE_SHIFT =
+ MOD_PD_LEVEL_2 * MOD_PD_CS_STATE_BITS_PER_LEVEL,
+ MOD_PD_CS_LEVEL_3_STATE_SHIFT =
+ MOD_PD_LEVEL_3 * MOD_PD_CS_STATE_BITS_PER_LEVEL,
+ MOD_PD_CS_LEVEL_SHIFT = MOD_PD_LEVEL_COUNT * MOD_PD_CS_STATE_BITS_PER_LEVEL,
+
+ MOD_PD_CS_VALID_BITS =
+ (1 << (MOD_PD_CS_LEVEL_SHIFT + MOD_PD_CS_STATE_BITS_PER_LEVEL)) - 1
+};
+
+/*!
+ * \brief Compute a composite power domain state.
+ */
+#define MOD_PD_COMPOSITE_STATE( \
+ HIGHEST_LEVEL, LEVEL_3_STATE, LEVEL_2_STATE, LEVEL_1_STATE, LEVEL_0_STATE) \
+ (((HIGHEST_LEVEL) << MOD_PD_CS_LEVEL_SHIFT) | \
+ ((LEVEL_3_STATE) << MOD_PD_CS_LEVEL_3_STATE_SHIFT) | \
+ ((LEVEL_2_STATE) << MOD_PD_CS_LEVEL_2_STATE_SHIFT) | \
+ ((LEVEL_1_STATE) << MOD_PD_CS_LEVEL_1_STATE_SHIFT) | \
+ ((LEVEL_0_STATE) << MOD_PD_CS_LEVEL_0_STATE_SHIFT))
+
+/*!
+ * \brief Power domain driver interface.
+ *
+ * \details The interface the power domain module relies on to perform
+ * actions on a power domain. Examples include powering a domain on or off,
+ * resetting the domain and preparing for/reacting to a system shutdown or
+ * reset.
+ */
+struct mod_pd_driver_api {
+ /*!
+ * \brief Set the power domain identified by \p dev_id to the \p state power
+ * state.
+ *
+ * \details The power domain module calls this function when processing a
+ * <tt>set state</tt> request. The function is called once the module
+ * has checked that the power domain's children and parent are in a
+ * proper state for the power domain to be transitioned to the \p state
+ * power state. This function must not return until the transition has
+ * completed.
+ *
+ * \warning In the case of a <tt>shutdown</tt> request, if provided, \ref
+ * shutdown will be called instead.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ * \param state Power state the power domain has to be put into.
+ *
+ * \retval FWK_SUCCESS The power state has been successfully set.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \return One of the other specific error codes described by the driver
+ * module.
+ */
+ int (*set_state)(fwk_id_t dev_id, unsigned int state);
+
+ /*!
+ * \brief Get the current power state of the power domain identified by
+ * \p dev_id.
+ *
+ * \details The power domain module calls this function when processing a
+ * <tt>get state</tt> or <tt>get composite state</tt> request.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ * \param state Pointer to storage for the power state of the power domain.
+ *
+ * \retval FWK_SUCCESS The power state was successfully returned.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ */
+ int (*get_state)(fwk_id_t dev_id, unsigned int *state);
+
+ /*!
+ * \brief Reset the power domain identified by \p dev_id.
+ *
+ * \details The power domain module calls this function when processing a
+ * <tt>reset</tt> request. The module will ensure that any children of
+ * the power domain are powered off before attempting a reset.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ *
+ * \retval FWK_SUCCESS The power domain was successfully reset.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \return One of the other specific error codes described by the module.
+ */
+ int (*reset)(fwk_id_t dev_id);
+
+ /*!
+ * \brief Check whether a state transition request should be denied.
+ *
+ * \note This function is optional (may be \c NULL) and it is expected that
+ * most drivers will not implement it.
+ *
+ * \details The power domain module calls this function when it wishes to
+ * know whether it should permit or deny a state transition request.
+ * It is intended to provide a device a denial mechanism that can make
+ * judgements based on information unavailable to the power domain
+ * module, such as dependencies between power domains beyond the power
+ * domain tree model.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ * \param state Power state the power domain is intended to be put into.
+ *
+ * \retval true The transition should be denied.
+ * \retval false The power domain can transition to the power state \p
+ * state.
+ */
+ bool (*deny)(fwk_id_t dev_id, unsigned int state);
+
+ /*!
+ * \brief Prepare the last standing core for a system suspend.
+ *
+ * \details The function prepares the last standing core for entering the
+ * \ref MOD_PD_STATE_OFF state (powered off, no wake-up interrupt) when
+ * it will execute WFI. The function should also ensure that when the
+ * core is powered off a state transition report is sent by means of
+ * the \ref mod_pd_driver_input_api::report_power_state_transition
+ * driver input interface function indicating that the core power
+ * domain state should be updated. This function is mandatory for core
+ * power domains, but is otherwise unused.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ *
+ * \retval FWK_SUCCESS The core has been successfully prepared.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ */
+ int (*prepare_core_for_system_suspend)(fwk_id_t dev_id);
+
+ /*!
+ * \brief Shutdown a power domain as part of a system shutdown.
+ *
+ * \note This function is optional (may be \c NULL).
+ *
+ * \details The power domain module calls this function when processing a
+ * <tt>shutdown</tt> request. The power domain must be in the
+ * \ref MOD_PD_STATE_OFF state when it returns.
+ *
+ * \param dev_id Driver identifier of the power domain.
+ * \param state State the system will be suspended to. The definition
+ * of those states is platform specific.
+ *
+ * \retval FWK_SUCCESS The core has been successfully shutdown.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ */
+ int (*shutdown)(
+ fwk_id_t dev_id,
+ enum mod_pd_system_shutdown system_shutdown);
+};
+
+/*!
+ * \brief Power domain module public interface.
+ *
+ * \details The interface the power domain module exposes to the other modules
+ * and their elements without any restriction.
+ */
+
+struct mod_pd_public_api {
+ /*!
+ * \brief Get the type of a power domain.
+ *
+ * \param pd_id Identifier of the power domain that the type is being
+ * queried for.
+ * \param[out] type Type of the power domain.
+ *
+ * \retval FWK_SUCCESS The type of the power domain was returned.
+ * \retval FWK_E_INIT The module has not been initialized.
+ * \retval FWK_E_STATE The power domain module is in an invalid state.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'type' is equal to NULL.
+ */
+ int (*get_domain_type)(fwk_id_t pd_id, enum mod_pd_type *type);
+
+ /*!
+ * \brief Get the identifier of the parent of a power domain.
+ *
+ * \note The function returns \ref FWK_ID_NONE in the case of the root power
+ * domain which does not have any parent.
+ *
+ * \param pd_id Identifier of the power domain.
+ * \param[out] parent_pd_id Identifier of the parent power domain.
+ *
+ * \retval FWK_SUCCESS The identifier of the parent power domain was
+ * returned.
+ * \retval FWK_E_INIT The module has not been initialized.
+ * \retval FWK_E_STATE The power domain module is in an invalid state.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'parent_pd_id' is equal to NULL.
+ */
+ int (*get_domain_parent_id)(fwk_id_t pd_id, fwk_id_t *parent_pd_id);
+};
+
+/*!
+ * \brief Power domain module restricted interface.
+ *
+ * \details The interface the power domain module exposes to a restricted set
+ * of modules and/or elements. The set of modules and/or elements that are
+ * allowed to access this interface is defined by the module configuration
+ * data.
+ */
+struct mod_pd_restricted_api {
+ /*!
+ * \brief Get the type of a power domain.
+ *
+ * \param pd_id Identifier of the power domain that the type is being
+ * queried for.
+ * \param[out] type Type of the power domain.
+ *
+ * \retval FWK_SUCCESS The type of the power domain was returned.
+ * \retval FWK_E_INIT The module has not been initialized.
+ * \retval FWK_E_STATE The power domain module is in an invalid state.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'type' is equal to NULL.
+ */
+ int (*get_domain_type)(fwk_id_t pd_id, enum mod_pd_type *type);
+
+ /*!
+ * \brief Get the identifier of the parent of a power domain.
+ *
+ * \note The function returns \ref FWK_ID_NONE in the case of the root power
+ * domain which does not have any parent.
+ *
+ * \param pd_id Identifier of the power domain.
+ * \param[out] parent_pd_id The identifier of the parent power domain.
+ *
+ * \retval FWK_SUCCESS The identifier of the parent power domain was
+ * returned.
+ * \retval FWK_E_INIT The module has not been initialized.
+ * \retval FWK_E_STATE The power domain module is in an invalid state.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'parent_pd_id' is equal to NULL.
+ */
+ int (*get_domain_parent_id)(fwk_id_t pd_id, fwk_id_t *parent_pd_id);
+
+ /*!
+ * \brief Set the state of a power domain and possibly of one or several of
+ * its ancestors.
+ *
+ * \note The function sets the state of the power domain identified by
+ * 'pd_id' and possibly of one or several of its ancestors. When the
+ * function returns the state transition is completed.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be set.
+ *
+ * \param state State the power domain has to be put into and
+ * possibly the state(s) its ancestor(s) has(have) to be put into. The
+ * module will ensure that, for each power state transition, the parent
+ * and the children of the power domain involved are in a state where
+ * the transition can be completed.
+ *
+ * \retval FWK_SUCCESS The power state transition was completed.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_state)(fwk_id_t pd_id, uint32_t state);
+
+ /*!
+ * \brief Request an asynchronous power state transition.
+ *
+ * \warning Successful completion of this function does not indicate
+ * completion of a transition, but instead that a request has been
+ * submitted.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be set.
+ *
+ * \param resp_requested True if the caller wants to be notified with an
+ * event response at the end of the request processing.
+ *
+ * \param composite State the power domain has to be put into and
+ * possibly the state(s) its ancestor(s) has(have) to be put into. The
+ * module will ensure that, for each power state transition, the parent
+ * and the children of the power domain involved are in a state where
+ * the transition can be completed.
+ *
+ * \retval FWK_SUCCESS The power state transition was submitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_state_async)(fwk_id_t pd_id, bool resp_requested, uint32_t state);
+
+ /*!
+ * \brief Get the state of a given power domain.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be
+ * retrieved.
+ * \param[out] state The power domain state.
+ *
+ * \retval FWK_SUCCESS The power state was returned.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ * \retval FWK_E_PARAM The pointer 'state' is equal to NULL.
+ */
+ int (*get_state)(fwk_id_t pd_id, unsigned int *state);
+
+ /*!
+ * \brief Reset of a power domain.
+ *
+ * \note The function resets the power domain identified by 'pd_id'. When
+ * the function returns the power domain reset is completed.
+ *
+ * \param pd_id Identifier of the power domain to reset.
+ *
+ * \retval FWK_SUCCESS Power state retrieving request transmitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_NOMEM Failed to allocate a request descriptor.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ */
+ int (*reset)(fwk_id_t pd_id);
+
+ /*!
+ * \brief Suspend the system.
+ *
+ * \note The function initiates the suspension of the system. On call, all
+ * but one core power domain must be in the MOD_PD_STATE_OFF state.
+ * When the function returns the power down of the last standing core
+ * is programmed to occur as soon as the core executes WFI.
+ *
+ * \param state State the system has to be suspended to. The definition
+ * of those states is platform specific.
+ *
+ * \retval FWK_SUCCESS The system suspension has been initiated
+ * successfully.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_NOMEM Failed to allocate a request descriptor.
+ * \retval FWK_E_PARAM Invalid state.
+ * \retval FWK_E_STATE The system is not in the proper state to be
+ * suspended.
+ */
+ int (*system_suspend)(unsigned int state);
+
+ /*!
+ * \brief Shutdown the system.
+ *
+ * \note The function shutdowns the system whatever its current state. If
+ * the shutdown is successful, the function does not return.
+ *
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_PENDING Request aknowledged. A response event will not be
+ * sent to the caller.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_NOMEM Failed to allocate a request descriptor.
+ */
+ int (*system_shutdown)(enum mod_pd_system_shutdown system_shutdown);
+};
+
+/*!
+ * \brief Power domain module driver input API.
+ *
+ * \details The interface the power domain module exposes to its module drivers
+ * to be able to ask for power state transitions or report power state
+ * transitions following the occurrence of interrupts.
+ */
+struct mod_pd_driver_input_api {
+ /*!
+ * \brief Request an asynchronous composite power state transition.
+ *
+ * \warning Successful completion of this function does not indicate
+ * completion of a transition, but instead that a request has been
+ * submitted.
+ *
+ * \param pd_id Identifier of the power domain whose state has to be set.
+ *
+ * \param resp_requested True if the caller wants to be notified with an
+ * event response at the end of the request processing.
+ *
+ * \param composite_state State the power domain has to be put into and
+ * possibly the state(s) its ancestor(s) has(have) to be put into. The
+ * module will ensure that, for each power state transition, the parent
+ * and the children of the power domain involved are in a state where
+ * the transition can be completed.
+ *
+ * \retval FWK_SUCCESS The composite power state transition was submitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_state_async)(
+ fwk_id_t pd_id,
+ bool resp_requested,
+ uint32_t composite_state);
+
+ /*!
+ * \brief Request for a power domain to be reset.
+ *
+ * \note The function queues a reset request. When the function returns the
+ * power domain has not been reset, the reset has just been requested
+ * to the power domain module.
+ *
+ * \param pd_id Identifier of the power domain to reset.
+ *
+ * \param resp_requested True if the caller wants to be notified with an
+ * event response at the end of the request processing.
+ *
+ * \retval FWK_SUCCESS Reset request transmitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the
+ * call to the API.
+ * \retval FWK_E_HANDLER The function is not called from a thread.
+ * \retval FWK_E_NOMEM Failed to allocate a request descriptor.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ */
+ int (*reset_async)(fwk_id_t pd_id, bool resp_requested);
+
+ /*!
+ * \brief Report a power domain state transition.
+ *
+ * \warning Failure to call this function on a power state transition may
+ * leave the power domain module with an outdated power state for the
+ * power domain.
+ *
+ * \param pd_id Identifier of the power domain, a power state transition
+ * report is is sent to.
+ *
+ * \retval FWK_SUCCESS Report transmitted.
+ * \retval FWK_E_ACCESS Invalid access, the framework has rejected the call
+ * to the API.
+ * \retval FWK_E_NOMEM Failed to allocate a report event.
+ * \retval FWK_E_PARAM The power domain identifier is unknown.
+ */
+ int (*report_power_state_transition)(fwk_id_t pd_id, unsigned int state);
+
+ /*!
+ * \brief Get the power domain identifier of the last core online before or
+ * during system suspend.
+ *
+ * \details When an agent calls for system suspend, the power domain module
+ * ensures that all cores are turned off except for one, which is the
+ * calling entity. Because any core can call system suspend, it may be
+ * necessary for a driver to know which core called system suspend.
+ * With this information the driver can, for example, safely resume the
+ * system from the same core which was turned off lastly.
+ *
+ * \note The power domain module does not perform any checks to ensure that
+ * this function is called during a system suspend sequence.
+ *
+ * \param[out] last_core_pd_id Identifier of the last core.
+ *
+ * \retval FWK_E_PARAM The pointer to the identifier is not valid.
+ * \retval FWK_SUCCESS The request was successful.
+ * \return One of the standard framework error codes.
+ */
+ int (*get_last_core_pd_id)(fwk_id_t *last_core_pd_id);
+};
+
+/*!
+ * \brief Parameters of a power state pre-transition notification.
+ */
+struct mod_pd_power_state_pre_transition_notification_params {
+ /*! Current power state */
+ unsigned int current_state;
+
+ /*! Target power state */
+ unsigned int target_state;
+};
+
+/*!
+ * \brief Parameters of the response to a power state pre-transition
+ * notification.
+ */
+struct mod_pd_power_state_pre_transition_notification_resp_params {
+ /*!
+ * Status of the transition for the entity being notified.
+ */
+ int status;
+};
+
+/*!
+ * \brief Parameters of a power domain transition notification.
+ */
+struct mod_pd_power_state_transition_notification_params {
+ /*! Power state the power domain has transitioned to */
+ unsigned int state;
+};
+
+/*!
+ * \defgroup GroupPowerDomainIds Identifiers
+ * \{
+ */
+
+/*!
+ * \brief API indices
+ */
+enum mod_pd_api_idx {
+ MOD_PD_API_IDX_PUBLIC,
+ MOD_PD_API_IDX_RESTRICTED,
+ MOD_PD_API_IDX_DRIVER_INPUT,
+ MOD_PD_API_IDX_COUNT,
+};
+
+/*! Public API identifier */
+static const fwk_id_t mod_pd_api_id_public =
+ FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, MOD_PD_API_IDX_PUBLIC);
+
+/*! Restricted API identifier */
+static const fwk_id_t mod_pd_api_id_restricted = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN,
+ MOD_PD_API_IDX_RESTRICTED);
+
+/*! Driver input API identifier */
+static const fwk_id_t mod_pd_api_id_driver_input = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN,
+ MOD_PD_API_IDX_DRIVER_INPUT);
+
+/*!
+ * \brief Notification indices.
+ */
+enum mod_pd_notification_idx {
+ /*! Power state transition */
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_TRANSITION,
+
+ /*! Power state pre-transition */
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_PRE_TRANSITION,
+
+ /*! Number of notifications defined by the power domain module */
+ MOD_PD_NOTIFICATION_COUNT,
+};
+
+/*!
+ * \brief Notification identifiers.
+ */
+/*! Identifier of the power state transition notification */
+static const fwk_id_t mod_pd_notification_id_power_state_transition =
+ FWK_ID_NOTIFICATION_INIT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN,
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_TRANSITION);
+/*! Identifier of the power state pre-transition notification */
+static const fwk_id_t mod_pd_notification_id_power_state_pre_transition =
+ FWK_ID_NOTIFICATION_INIT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN,
+ MOD_PD_NOTIFICATION_IDX_POWER_STATE_PRE_TRANSITION);
+
+/*!
+ * @cond
+ */
+
+#define CPU_PWR_OFF U(0x00000003)
+#define MODE_L2_DOWN (0x00000002U)
+
+/*
+ * Module and power domain contexts
+ */
+
+struct response_ctx {
+ /* Pending response flag. */
+ bool pending;
+
+ /* Cookie of the event to respond to. */
+ uint32_t cookie;
+};
+
+/* Context for the power state transition notification */
+struct power_state_transition_notification_ctx {
+ /* Number of pending notification responses */
+ unsigned int pending_responses;
+
+ /*
+ * Power state the power domain has transitioned to.
+ */
+ unsigned int state;
+};
+
+/* Context for the power state pre-transition notification */
+struct power_state_pre_transition_notification_ctx {
+ /* Number of pending notification responses */
+ unsigned int pending_responses;
+
+ /* Target power state */
+ unsigned int state;
+
+ /*
+ * Status of the responses received so far. Either FWK_SUCCESS if all the
+ * responses received so far have indicated success, or FWK_E_DEVICE
+ * otherwise.
+ */
+ int response_status;
+
+ /*
+ * Validity flag. Set to true when a notification is sent and reset to
+ * false when the requested state for the power domain is changed.
+ */
+ bool valid;
+};
+
+struct pd_ctx {
+ /* Identifier of the power domain */
+ fwk_id_t id;
+
+ /* Driver interface */
+ struct mod_pd_driver_api *driver_api;
+
+ /* Driver's identifier of the power domain */
+ fwk_id_t driver_id;
+
+ /* Power domain configuration data */
+ const struct mod_power_domain_element_config *config;
+
+ /*
+ * Mask of the valid states. Bit \c n in \ref valid_states_mask is equal
+ * to one if and only if the state \c n is a valid state for the power
+ * domain. The number of bits of this field has to be greater or equal than
+ * MOD_PD_STATE_COUNT_MAX.
+ */
+ uint32_t valid_state_mask;
+
+ /*
+ * Table of allowed state masks. Bit \c n of the entry \c m is equal to
+ * one if and only if the state \c n for the power domain is allowed when
+ * its parent is in state \c m. The number of bits of each entry of this
+ * table has to be greater or equal than MOD_PD_STATE_COUNT_MAX.
+ */
+ const uint32_t *allowed_state_mask_table;
+
+ /* Size of the table of allowed state masks */
+ size_t allowed_state_mask_table_size;
+
+ /* Pointer to the power domain's parent context */
+ struct pd_ctx *parent;
+
+ /*
+ * Pointer to the context of the power domain's first child. This
+ * field is equal to NULL if the power domain does not have any children.
+ */
+ struct pd_ctx *first_child;
+
+ /*
+ * Pointer to the context of the power domain sibling in the chain of the
+ * children power domains of their parent.
+ */
+ struct pd_ctx *sibling;
+
+ /* Requested power state for the power domain */
+ unsigned int requested_state;
+
+ /* Last power state requested to the driver for the power domain */
+ unsigned int state_requested_to_driver;
+
+ /*
+ * Current state of the power domain. When a transition is in progress, the
+ * current state is different from the last requested state.
+ */
+ unsigned int current_state;
+
+ /* Pending response context */
+ struct response_ctx response;
+
+ /* Context for the power state transition notification */
+ struct power_state_transition_notification_ctx
+ power_state_transition_notification_ctx;
+
+ /* Context for the power state pre-transition notification */
+ struct power_state_pre_transition_notification_ctx
+ power_state_pre_transition_notification_ctx;
+};
+
+struct system_suspend_ctx {
+ /* Flag indicating if a system suspend is ongoing (true) or not (false) */
+ bool ongoing;
+
+ /* Last standing core context */
+ struct pd_ctx *last_core_pd;
+
+ /* Suspend state for the system power domain */
+ unsigned int state;
+};
+
+struct mod_pd_ctx {
+ /* Module configuration data */
+ struct mod_power_domain_config *config;
+
+ /* Table of power domain contexts */
+ struct pd_ctx *pd_ctx_table;
+
+ /* Number of power domains */
+ unsigned int pd_count;
+
+ /* Log module API */
+ const struct mod_log_api *log_api;
+
+ /* Context of the system power domain */
+ struct pd_ctx *system_pd_ctx;
+
+ /* System suspend context */
+ struct system_suspend_ctx system_suspend;
+};
+
+/*
+ * Power domain module events
+ */
+
+/* Power module event indexes */
+enum pd_event_idx {
+ PD_EVENT_IDX_SET_STATE,
+ PD_EVENT_IDX_GET_STATE,
+ PD_EVENT_IDX_RESET,
+ PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION,
+ PD_EVENT_IDX_SYSTEM_SUSPEND,
+ PD_EVENT_IDX_SYSTEM_SHUTDOWN,
+ PD_EVENT_COUNT
+};
+
+/* Standard parameter of a response event */
+struct pd_response {
+ /* Status of the event processing */
+ int status;
+};
+
+/*
+ * PD_EVENT_IDX_SET_STATE
+ * Parameters of the set state request event
+ */
+struct pd_set_state_request {
+ /*
+ * The composite state that defines the power state that the power domain,
+ * target of the request, has to be put into and possibly the power states
+ * the ancestors of the power domain have to be put into.
+ */
+ uint32_t composite_state;
+ uint32_t domain_id;
+};
+
+/* Parameters of the set state response event */
+struct pd_set_state_response {
+ /* Status of the set state request event processing */
+ int status;
+
+ /*
+ * The composite state that defines the power state that the power domain,
+ * target of the request, had to be put into and possibly the power states
+ * the ancestors of the power domain had to be put into.
+ */
+ uint32_t composite_state;
+};
+
+/*
+ * PD_EVENT_IDX_GET_STATE
+ * Parameters of the get state request event
+ */
+struct pd_get_state_request {
+ /*
+ * Flag indicating if the composite state of the power domain and its
+ * ancestors has to be returned (composite=true) or just the power domain
+ * state (composite=false).
+ */
+ bool composite;
+};
+
+/* Parameters of the get state response event */
+struct pd_get_state_response {
+ /* Status of the get state request event processing */
+ int status;
+
+ /* Copy of the "composite" request parameter */
+ bool composite;
+
+ /*
+ * The power state of the power domain target of the request or the
+ * composite state of the power domain and its ancestors depending on the
+ * value of the "composite" request parameter.
+ */
+ uint32_t state;
+};
+
+/*
+ * PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION
+ * Parameters of the power state transition report event
+ */
+struct pd_power_state_transition_report {
+ /* The new power state of the power domain */
+ uint32_t state;
+};
+
+/*
+ * PD_EVENT_IDX_SYSTEM_SUSPEND
+ * Parameters of the system suspend request event
+ */
+struct pd_system_suspend_request {
+ /* System suspend state, platform specific */
+ unsigned int state;
+};
+
+/*
+ * PD_EVENT_IDX_SYSTEM_SHUTDOWN
+ * Parameters of the system shutdown request event
+ */
+struct pd_system_shutdown_request {
+ /* System shutdown type */
+ enum mod_pd_system_shutdown system_shutdown;
+};
+
+/*
+ * For each power level, shift in a composite state of the state for the power
+ * level.
+ */
+static const unsigned int mod_pd_cs_level_state_shift[MOD_PD_LEVEL_COUNT] = {
+ MOD_PD_CS_LEVEL_0_STATE_SHIFT,
+ MOD_PD_CS_LEVEL_1_STATE_SHIFT,
+ MOD_PD_CS_LEVEL_2_STATE_SHIFT,
+ MOD_PD_CS_LEVEL_3_STATE_SHIFT
+};
+
+/*
+ * Internal variables
+ */
+static const char *const default_state_name_table[] = {
+ "OFF", "ON", "SLEEP", "3", "4", "5", "6", "7",
+ "8", "9", "10", "11", "12", "13", "14", "15"
+};
+
+/*!
+ * @endcond
+ */
+
+/*!
+ * \}
+ */
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* MOD_RCAR_POWER_DOMAIN_H */
diff --git a/product/rcar/module/rcar_power_domain/src/Makefile b/product/rcar/module/rcar_power_domain/src/Makefile
new file mode 100644
index 00000000..18ec7212
--- /dev/null
+++ b/product/rcar/module/rcar_power_domain/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 := Power domain
+BS_LIB_SOURCES := mod_rcar_power_domain.c
+
+include $(BS_DIR)/lib.mk
diff --git a/product/rcar/module/rcar_power_domain/src/mod_rcar_power_domain.c b/product/rcar/module/rcar_power_domain/src/mod_rcar_power_domain.c
new file mode 100644
index 00000000..ea355263
--- /dev/null
+++ b/product/rcar/module/rcar_power_domain/src/mod_rcar_power_domain.c
@@ -0,0 +1,1567 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <config_rcar_power_domain.h>
+#include <mmio.h>
+#include <rcar_core.h>
+#include <rcar_scmi_id.h>
+
+#include <mod_rcar_power_domain.h>
+
+#include <fwk_assert.h>
+#include <fwk_element.h>
+#include <fwk_id.h>
+#include <fwk_log.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+#include <fwk_multi_thread.h>
+#include <fwk_notification.h>
+#include <fwk_status.h>
+#include <fwk_thread.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * Internal variables
+ */
+static struct mod_pd_ctx mod_pd_ctx;
+
+/*
+ * Utility functions
+ */
+
+/* Functions related to power domain positions in the power domain tree */
+static bool is_valid_tree_pos(uint64_t tree_pos)
+{
+ return (tree_pos < MOD_PD_TREE_POS(MOD_PD_LEVEL_COUNT, 0, 0, 0, 0));
+}
+
+static enum mod_pd_level get_level_from_tree_pos(uint64_t tree_pos)
+{
+ return (enum mod_pd_level)(
+ (tree_pos >> MOD_PD_TREE_POS_LEVEL_SHIFT) & MOD_PD_TREE_POS_LEVEL_MASK);
+}
+
+/* State related utility functions */
+static bool is_valid_state(const struct pd_ctx *pd, unsigned int state)
+{
+ return (state < MOD_PD_STATE_COUNT_MAX) &&
+ ((pd->valid_state_mask & (1 << state)) != 0);
+}
+
+static unsigned int normalize_state(unsigned int state)
+{
+ switch (state) {
+ case MOD_PD_STATE_OFF:
+ return (MOD_PD_STATE_COUNT_MAX + 1);
+
+ case MOD_PD_STATE_SLEEP:
+ return MOD_PD_STATE_COUNT_MAX;
+
+ default:
+ return state;
+ }
+}
+
+static bool is_deeper_state(
+ unsigned int state,
+ unsigned int state_to_compare_to)
+{
+ return normalize_state(state) > normalize_state(state_to_compare_to);
+}
+
+static bool is_shallower_state(
+ unsigned int state,
+ unsigned int state_to_compare_to)
+{
+ return normalize_state(state) < normalize_state(state_to_compare_to);
+}
+
+static bool is_allowed_by_child(
+ const struct pd_ctx *child,
+ unsigned int parent_state,
+ unsigned int child_state)
+{
+ if (parent_state >= child->allowed_state_mask_table_size)
+ return false;
+
+ return (
+ (child->allowed_state_mask_table[parent_state] & (1 << child_state)) !=
+ 0);
+}
+
+static const char *get_state_name(const struct pd_ctx *pd, unsigned int state)
+{
+ static char const unknown_name[] = "Unknown";
+
+ if (state < pd->config->state_name_table_size)
+ return pd->config->state_name_table[state];
+ else if (state < FWK_ARRAY_SIZE(default_state_name_table))
+ return default_state_name_table[state];
+ else
+ return unknown_name;
+}
+
+/* Functions related to a composite state */
+static unsigned int get_level_state_from_composite_state(
+ uint32_t composite_state,
+ enum mod_pd_level level)
+{
+ return (composite_state >> mod_pd_cs_level_state_shift[level]) &
+ MOD_PD_CS_STATE_MASK;
+}
+
+static enum mod_pd_level get_highest_level_from_composite_state(
+ uint32_t composite_state)
+{
+ return (enum mod_pd_level)(
+ (composite_state >> MOD_PD_CS_LEVEL_SHIFT) & MOD_PD_CS_STATE_MASK);
+}
+
+static bool is_valid_composite_state(
+ struct pd_ctx *target_pd,
+ uint32_t composite_state)
+{
+ enum mod_pd_level level;
+ enum mod_pd_level highest_level;
+ unsigned int state, child_state = MOD_PD_STATE_OFF;
+ struct pd_ctx *pd = target_pd;
+ struct pd_ctx *child = NULL;
+
+ assert(target_pd != NULL);
+
+ if (composite_state & (~MOD_PD_CS_VALID_BITS))
+ goto error;
+
+ level = get_level_from_tree_pos(pd->config->tree_pos);
+ highest_level = get_highest_level_from_composite_state(composite_state);
+
+ if ((highest_level < level) || (highest_level >= MOD_PD_LEVEL_COUNT))
+ goto error;
+
+ for (; level <= highest_level; level++) {
+ if (pd == NULL)
+ goto error;
+
+ state = get_level_state_from_composite_state(composite_state, level);
+ if (!is_valid_state(pd, state))
+ goto error;
+
+ if ((child != NULL) && !is_allowed_by_child(child, state, child_state))
+ goto error;
+
+ child = pd;
+ child_state = state;
+ pd = pd->parent;
+ }
+
+ return true;
+
+error:
+ FWK_LOG_ERR(
+ "[PD] ERROR: Invalid composite state for %s: 0x%08x.",
+ fwk_module_get_name(target_pd->id),
+ composite_state);
+
+ return false;
+}
+
+/*
+ * Check whether a transition to a given power state for a power domain is
+ * possible given the current state of its parent and children (if any).
+ *
+ * \param pd Description of the power domain to check the power state transition
+ * for.
+ * \param state Power state.
+ */
+static bool is_allowed_by_parent_and_children(
+ struct pd_ctx *pd,
+ unsigned int state)
+{
+ struct pd_ctx *parent, *child;
+
+ parent = pd->parent;
+ if (parent != NULL) {
+ if (!is_allowed_by_child(pd, parent->current_state, state))
+ return false;
+ }
+
+ child = pd->first_child;
+ while (child != NULL) {
+ if (!is_allowed_by_child(child, state, child->current_state))
+ return false;
+ child = child->sibling;
+ }
+
+ return true;
+}
+
+/*
+ * Check whether a power state pre-transition notification must be sent.
+ *
+ * \param pd Description of the power domain
+ * \param state Power state the power domain has to transit to
+ *
+ * \retval true A power state pre-transition notification must be sent.
+ * \retval false A power state pre-transition notification doesn't have to be
+ * sent.
+ */
+static bool check_power_state_pre_transition_notification(
+ struct pd_ctx *pd,
+ unsigned int state)
+{
+ if (!is_deeper_state(state, pd->state_requested_to_driver))
+ return false;
+
+ if ((state == pd->power_state_pre_transition_notification_ctx.state) &&
+ pd->power_state_pre_transition_notification_ctx.valid) {
+ return (
+ pd->power_state_pre_transition_notification_ctx.response_status !=
+ FWK_SUCCESS);
+ }
+
+ return true;
+}
+
+/*
+ * Initiate a power state pre-transition notification if necessary.
+ *
+ * \param pd Description of the power domain to initiate the notification
+ * for.
+ *
+ * \retval true Waiting for notification responses.
+ * \retval false Not waiting for any notification response.
+ */
+static bool initiate_power_state_pre_transition_notification(struct pd_ctx *pd)
+{
+ unsigned int state;
+ struct fwk_event notification_event = {
+ .id = mod_pd_notification_id_power_state_pre_transition,
+ .response_requested = true
+ };
+ struct mod_pd_power_state_pre_transition_notification_params *params;
+
+ state = pd->requested_state;
+ if (!check_power_state_pre_transition_notification(pd, state))
+ return false;
+
+ /*
+ * If still waiting for some responses on the previous power state
+ * pre-transition notification, wait for them before to issue the next one.
+ */
+ if (pd->power_state_pre_transition_notification_ctx.pending_responses != 0)
+ return true;
+
+ params = (struct mod_pd_power_state_pre_transition_notification_params *)
+ notification_event.params;
+ params->current_state = pd->current_state;
+ params->target_state = state;
+ fwk_notification_notify(
+ &notification_event,
+ &pd->power_state_pre_transition_notification_ctx.pending_responses);
+
+ pd->power_state_pre_transition_notification_ctx.state = state;
+ pd->power_state_pre_transition_notification_ctx.response_status =
+ FWK_SUCCESS;
+ pd->power_state_pre_transition_notification_ctx.valid = true;
+
+ return (
+ pd->power_state_pre_transition_notification_ctx.pending_responses != 0);
+}
+
+/*
+ * Initiate the transition to a power state for a power domain.
+ *
+ * \param pd Description of the power domain to initiate the state transition
+ * for.
+ *
+ * \retval FWK_SUCCESS The power state transition was initiated.
+ * \retval FWK_E_DEVICE The power state transition was denied by the driver.
+ * \return One of the other driver-defined error codes.
+ */
+static int initiate_power_state_transition(struct pd_ctx *pd)
+{
+ int status;
+ unsigned int state = pd->requested_state;
+
+ status = pd->driver_api->set_state(pd->driver_id, state);
+ pd->current_state = state;
+
+ FWK_LOG_TRACE(
+ "[PD] %s: %s->%s, %d.",
+ fwk_module_get_name(pd->id),
+ get_state_name(pd, pd->state_requested_to_driver),
+ get_state_name(pd, state),
+ status);
+
+ pd->state_requested_to_driver = state;
+
+ return status;
+}
+
+/*
+ * Respond to a request.
+ *
+ * \param pd Description of the power domain in charge of the response
+ * \param status Response status
+ */
+static void respond(struct pd_ctx *pd, int status)
+{
+ struct fwk_event resp_event;
+ const struct pd_set_state_request *req_params =
+ (struct pd_set_state_request *)(&resp_event.params);
+ struct pd_set_state_response *resp_params =
+ (struct pd_set_state_response *)(&resp_event.params);
+
+ if (!pd->response.pending)
+ return;
+
+ status = fwk_thread_get_delayed_response(
+ pd->id, pd->response.cookie, &resp_event);
+ pd->response.pending = false;
+
+ if (status != FWK_SUCCESS)
+ return;
+
+ resp_params->composite_state = req_params->composite_state;
+ resp_params->status = status;
+
+ fwk_thread_put_event(&resp_event);
+}
+
+/*
+ * Determine whether a composite state requires that the transition begins
+ * with the highest or lowest level.
+ *
+ * \param lowest_pd Target of the composite state transition request.
+ * \param uint32_t composite_state Target composite state.
+ * \retval true The power state transition must propagate upwards.
+ * \retval false The power state transition must propagate downwards.
+ */
+static bool is_upwards_transition_propagation(
+ const struct pd_ctx *lowest_pd,
+ uint32_t composite_state)
+{
+ enum mod_pd_level lowest_level, highest_level, level;
+ const struct pd_ctx *pd;
+ unsigned int state;
+
+ lowest_level = get_level_from_tree_pos(lowest_pd->config->tree_pos);
+ highest_level = get_highest_level_from_composite_state(composite_state);
+
+ for (level = lowest_level, pd = lowest_pd; level <= highest_level;
+ level++, pd = pd->parent) {
+ state = get_level_state_from_composite_state(composite_state, level);
+ if (state == pd->requested_state)
+ continue;
+
+ return is_deeper_state(state, pd->requested_state);
+ }
+
+ return false;
+}
+static bool is_allowed_by_children(const struct pd_ctx *pd, unsigned int state)
+{
+ const struct pd_ctx *child;
+
+ for (child = pd->first_child; child != NULL; child = child->sibling) {
+ if (!is_allowed_by_child(child, state, child->requested_state))
+ return false;
+ }
+
+ return true;
+}
+
+static void process_set_state_request(
+ struct pd_ctx *lowest_pd,
+ struct pd_set_state_request *req_params,
+ struct fwk_event *resp_event)
+{
+ int status;
+ struct pd_set_state_response *resp_params =
+ (struct pd_set_state_response *)resp_event->params;
+ uint32_t composite_state;
+ bool up;
+ enum mod_pd_level lowest_level, highest_level, level;
+ unsigned int nb_pds;
+ struct pd_ctx *pd;
+ const struct pd_ctx *parent;
+ unsigned int pd_index;
+ unsigned int state;
+ bool first_power_state_transition_initiated = false;
+
+ /* A set state request cancels any pending system suspend. */
+ mod_pd_ctx.system_suspend.ongoing = false;
+
+ composite_state = req_params->composite_state;
+ up = is_upwards_transition_propagation(lowest_pd, composite_state);
+
+ /*
+ * It has already been tested as part of the composite state validation that
+ * 'highest_level >= lowest_level' and 'highest_level' is lower
+ * than the highest power level.
+ */
+ lowest_level = get_level_from_tree_pos(lowest_pd->config->tree_pos);
+ highest_level = get_highest_level_from_composite_state(composite_state);
+ nb_pds = highest_level - lowest_level + 1;
+
+ status = FWK_SUCCESS;
+ pd = lowest_pd;
+ for (pd_index = 0; pd_index < nb_pds; pd_index++, pd = pd->parent) {
+ if (up)
+ level = lowest_level + pd_index;
+ else {
+ /*
+ * When walking down the power domain tree, get the context of the
+ * next power domain to process as well as its level.
+ */
+ pd = lowest_pd;
+ for (level = lowest_level; level < (highest_level - pd_index);
+ level++)
+ pd = pd->parent;
+ }
+
+ state = get_level_state_from_composite_state(composite_state, level);
+ if (state == pd->requested_state) {
+ continue;
+ }
+
+ /*
+ * Check that the requested power state is compatible with the states
+ * currently requested for the parent and children of the power domain.
+ */
+ parent = pd->parent;
+ if ((parent != NULL) &&
+ (!is_allowed_by_child(pd, parent->requested_state, state))) {
+ status = FWK_E_PWRSTATE;
+ break;
+ }
+
+ if (!is_allowed_by_children(pd, state))
+ continue;
+
+ /*
+ * A new valid power state is requested for the power domain. Send any
+ * pending response concerning the previous requested power state.
+ */
+ pd->requested_state = state;
+ pd->power_state_pre_transition_notification_ctx.valid = false;
+ respond(pd, FWK_E_OVERWRITTEN);
+
+ if (pd->state_requested_to_driver == state)
+ continue;
+
+ /*
+ * If a power state transition has already been initiated for an
+ * ancestor or descendant, we don't initiate the power state transition
+ * now. It will be initiated on completion of the transition of one
+ * of its ancestor or descendant.
+ */
+ if (first_power_state_transition_initiated)
+ continue;
+
+ /*
+ * If the parent or a child is not currently in a power state
+ * compatible with the new requested state for the power domain, do not
+ * initiate the transition now as well. It will be initiated when the
+ * parent and the children are in a proper state.
+ */
+ if (!is_allowed_by_parent_and_children(pd, state))
+ continue;
+
+ /*
+ * Defer the power state transition if power state pre-transition
+ * notification responses need to be waited for.
+ */
+ if (initiate_power_state_pre_transition_notification(pd))
+ continue;
+
+ status = initiate_power_state_transition(pd);
+ first_power_state_transition_initiated = (status == FWK_SUCCESS);
+ }
+
+ if (!resp_event->response_requested)
+ return;
+
+ resp_params->status = status;
+ resp_params->composite_state = composite_state;
+}
+
+/*
+ * Complete a system suspend
+ *
+ * Following the shutdown of the last standing core put all of its ancestors
+ * in the MOD_PD_STATE_OFF state but the system power domain which is put
+ * into the state that has been asked for.
+ *
+ * target_pd Description of the power domain target of the 'set composite state'
+ * request to suspend the system in the desired state.
+ */
+static int complete_system_suspend(struct pd_ctx *target_pd)
+{
+ enum mod_pd_level level;
+ unsigned int composite_state = 0;
+ struct pd_ctx *pd = target_pd;
+ struct fwk_event resp_event;
+ struct pd_set_state_response *resp_params =
+ (struct pd_set_state_response *)(&resp_event.params);
+
+ /*
+ * Traverse the PD tree bottom-up from current power domain to the top
+ * to build the composite state with MOD_PD_STATE_OFF power state for all
+ * levels but the last one.
+ */
+ level = get_level_from_tree_pos(target_pd->config->tree_pos);
+ do {
+ composite_state |=
+ ((pd->parent != NULL) ? MOD_PD_STATE_OFF :
+ mod_pd_ctx.system_suspend.state)
+ << mod_pd_cs_level_state_shift[level++];
+ pd = pd->parent;
+ } while (pd != NULL);
+
+ /*
+ * Finally, we need to update the highest valid level in the composite
+ * state.
+ */
+ composite_state |= (--level) << MOD_PD_CS_LEVEL_SHIFT;
+
+ resp_event = (struct fwk_event){ 0 };
+
+ process_set_state_request(
+ target_pd,
+ &((struct pd_set_state_request){
+ .composite_state = composite_state,
+ }),
+ &resp_event);
+
+ return resp_params->status;
+}
+
+/*
+ * Process a 'get composite state' request.
+ *
+ * pd Description of the target of the 'get state' request
+ * req_params Parameters of the 'get state' request
+ * resp_params Parameters of the 'get state' request response to be filled in
+ */
+static void process_get_state_request(
+ struct pd_ctx *pd,
+ const struct pd_get_state_request *req_params,
+ struct pd_get_state_response *resp_params)
+{
+ enum mod_pd_level level = get_level_from_tree_pos(pd->config->tree_pos);
+ unsigned int composite_state = 0;
+ if (!req_params->composite) {
+ resp_params->state = pd->current_state;
+ } else {
+ /*
+ * Traverse the PD tree bottom-up from current power domain to the top,
+ * collecting node's states and placing them in the correct position in
+ * the composite state.
+ */
+ do {
+ composite_state |= pd->current_state
+ << mod_pd_cs_level_state_shift[level++];
+ pd = pd->parent;
+ } while (pd != NULL);
+
+ /*
+ * Finally, we need to update the highest valid level in
+ * the composite state.
+ */
+ composite_state |= (--level) << MOD_PD_CS_LEVEL_SHIFT;
+
+ resp_params->state = composite_state;
+ }
+
+ resp_params->status = FWK_SUCCESS;
+}
+
+/*
+ * Process a 'reset' request.
+ *
+ * pd Description of the target of the 'reset' request
+ * resp_params Parameters of the 'reset' request response to be filled in
+ */
+static void process_reset_request(
+ struct pd_ctx *pd,
+ struct pd_response *resp_params)
+{
+ struct pd_ctx *child;
+
+ if (pd->requested_state == MOD_PD_STATE_OFF) {
+ resp_params->status = FWK_E_PWRSTATE;
+ return;
+ }
+
+ child = pd->first_child;
+ while (child != NULL) {
+ if ((child->requested_state != MOD_PD_STATE_OFF) ||
+ (child->current_state != MOD_PD_STATE_OFF)) {
+ resp_params->status = FWK_E_PWRSTATE;
+ return;
+ }
+ child = child->sibling;
+ }
+
+ resp_params->status = pd->driver_api->reset(pd->driver_id);
+}
+
+/*
+ * Process a power state transition report describing a transition to a deeper
+ * state.
+ *
+ * \param pd Target power domain context
+ */
+static void process_power_state_transition_report_deeper_state(
+ struct pd_ctx *pd)
+{
+ struct pd_ctx *parent = pd->parent;
+ unsigned int requested_state = parent->requested_state;
+
+ if (parent == NULL)
+ return;
+
+ if (parent->state_requested_to_driver == requested_state)
+ return;
+
+ if (!is_allowed_by_parent_and_children(parent, requested_state))
+ return;
+
+ if (!initiate_power_state_pre_transition_notification(parent))
+ initiate_power_state_transition(parent);
+}
+
+/*
+ * Process a power state transition report describing a transition to a
+ * shallower state.
+ *
+ * \param pd Target power domain context
+ */
+static void process_power_state_transition_report_shallower_state(
+ struct pd_ctx *pd)
+{
+ struct pd_ctx *child;
+ unsigned int requested_state;
+
+ for (child = pd->first_child; child != NULL; child = child->sibling) {
+ requested_state = child->requested_state;
+ if (child->state_requested_to_driver == requested_state)
+ continue;
+
+ if (!is_allowed_by_parent_and_children(child, requested_state))
+ return;
+
+ if (!initiate_power_state_pre_transition_notification(child))
+ initiate_power_state_transition(child);
+ }
+}
+
+/*
+ * Process a power state transition report
+ *
+ * \param pd Description of the target of the power state transition report
+ * \param report_params Parameters of the power state transition report
+ */
+static void process_power_state_transition_report(
+ struct pd_ctx *pd,
+ const struct pd_power_state_transition_report *report_params)
+{
+ unsigned int new_state = report_params->state;
+ unsigned int previous_state;
+ struct fwk_event notification_event = {
+ .id = mod_pd_notification_id_power_state_transition,
+ .response_requested = true
+ };
+ struct mod_pd_power_state_transition_notification_params *params;
+
+ if (new_state == pd->requested_state)
+ respond(pd, FWK_SUCCESS);
+
+ previous_state = pd->current_state;
+ pd->current_state = new_state;
+
+ if (pd->power_state_transition_notification_ctx.pending_responses == 0) {
+ params = (struct mod_pd_power_state_transition_notification_params *)
+ notification_event.params;
+ params->state = new_state;
+ pd->power_state_transition_notification_ctx.state = new_state;
+ fwk_notification_notify(
+ &notification_event,
+ &pd->power_state_transition_notification_ctx.pending_responses);
+ }
+
+ if ((mod_pd_ctx.system_suspend.ongoing) &&
+ (pd == mod_pd_ctx.system_suspend.last_core_pd)) {
+ mod_pd_ctx.system_suspend.ongoing = false;
+ complete_system_suspend(pd);
+ }
+
+ if (is_deeper_state(new_state, previous_state))
+ process_power_state_transition_report_deeper_state(pd);
+ else if (is_shallower_state(new_state, previous_state))
+ process_power_state_transition_report_shallower_state(pd);
+}
+
+/*
+ * Process a 'system suspend' request
+ *
+ * req_params Parameters of the 'system suspend' request
+ * resp_params Parameters of the 'system suspend' request response to be filled
+ * in
+ */
+static void process_system_suspend_request(
+ const struct pd_system_suspend_request *req_params,
+ struct pd_response *resp_params)
+{
+ int status;
+ unsigned int pd_idx;
+ struct pd_ctx *pd;
+ struct pd_ctx *last_core_pd = NULL;
+ struct pd_ctx *last_cluster_pd = NULL;
+
+ /*
+ * All core related power domains have to be in the MOD_PD_STATE_OFF state
+ * but one core and its ancestors.
+ */
+ for (pd_idx = 0; pd_idx < mod_pd_ctx.pd_count; pd_idx++) {
+ pd = &mod_pd_ctx.pd_ctx_table[pd_idx];
+ if ((pd->requested_state == MOD_PD_STATE_OFF) &&
+ (pd->current_state == MOD_PD_STATE_OFF))
+ continue;
+
+ if (pd->config->attributes.pd_type == MOD_PD_TYPE_CORE) {
+ if (last_core_pd != NULL) {
+ resp_params->status = FWK_E_STATE;
+ return;
+ }
+ last_core_pd = pd;
+ } else if (pd->config->attributes.pd_type == MOD_PD_TYPE_CLUSTER) {
+ if (last_cluster_pd != NULL) {
+ if (PD_RCAR_CLUSTER1 != pd_idx) {
+ resp_params->status = FWK_E_STATE;
+ return;
+ }
+ }
+ last_cluster_pd = pd;
+ }
+ }
+
+ if (last_core_pd == NULL) {
+ status = complete_system_suspend(
+ (last_cluster_pd != NULL) ? last_cluster_pd :
+ mod_pd_ctx.system_pd_ctx);
+ } else {
+ status = last_core_pd->driver_api->prepare_core_for_system_suspend(
+ last_core_pd->driver_id);
+ if (status == FWK_SUCCESS) {
+ mod_pd_ctx.system_suspend.ongoing = true;
+ mod_pd_ctx.system_suspend.last_core_pd = last_core_pd;
+ mod_pd_ctx.system_suspend.state = req_params->state;
+ last_core_pd->requested_state =
+ last_core_pd->state_requested_to_driver = MOD_PD_STATE_OFF;
+ }
+ }
+
+ resp_params->status = status;
+}
+
+/*
+ * Process a 'system shutdown' request
+ *
+ * req_params Parameters of the 'system shutdown' request
+ * resp_params Parameters of the 'system shutdown' request response to be filled
+ * in
+ */
+static void process_system_shutdown_request(
+ const struct pd_system_shutdown_request *req_params,
+ struct pd_response *resp_params)
+{
+ int status;
+ unsigned int pd_idx;
+ struct pd_ctx *pd;
+ fwk_id_t pd_id;
+
+ for (pd_idx = 0; pd_idx < mod_pd_ctx.pd_count; pd_idx++) {
+ pd = &mod_pd_ctx.pd_ctx_table[pd_idx];
+ pd_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, pd_idx);
+
+ FWK_LOG_TRACE("[PD] Shutting down %s.", fwk_module_get_name(pd_id));
+
+ if (pd->driver_api->shutdown != NULL) {
+ status = pd->driver_api->shutdown(
+ pd->driver_id, req_params->system_shutdown);
+ } else
+ status = pd->driver_api->set_state(pd->driver_id, MOD_PD_STATE_OFF);
+
+ if (status != FWK_SUCCESS)
+ FWK_LOG_ERR(
+ "[PD] ERROR: Shutdown of %s returned %d.",
+ fwk_module_get_name(pd_id),
+ status);
+ else
+ FWK_LOG_TRACE("[PD] %s shutdown.", fwk_module_get_name(pd_id));
+
+ pd->requested_state = pd->state_requested_to_driver =
+ pd->current_state = MOD_PD_STATE_OFF;
+ }
+
+ resp_params->status = FWK_E_PANIC;
+}
+
+/*
+ * API functions
+ */
+
+/* Functions common to the public and restricted API */
+static int pd_get_domain_type(fwk_id_t pd_id, enum mod_pd_type *type)
+{
+ struct pd_ctx *pd;
+
+ if (type == NULL)
+ return FWK_E_PARAM;
+
+ if (!fwk_module_is_valid_element_id(pd_id))
+ return FWK_E_PARAM;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ *type = pd->config->attributes.pd_type;
+
+ return FWK_SUCCESS;
+}
+
+static int pd_get_domain_parent_id(fwk_id_t pd_id, fwk_id_t *parent_pd_id)
+{
+ const struct pd_ctx *pd;
+
+ if (parent_pd_id == NULL)
+ return FWK_E_PARAM;
+
+ if (!fwk_module_is_valid_element_id(pd_id))
+ return FWK_E_PARAM;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ *parent_pd_id = (pd->parent != NULL) ? pd->parent->id : FWK_ID_NONE;
+
+ return FWK_SUCCESS;
+}
+
+/* Functions specific to the restricted API */
+
+static int pd_set_state(fwk_id_t pd_id, unsigned int state)
+{
+ int status;
+ struct pd_ctx *pd;
+ enum mod_pd_level level;
+ struct fwk_event req;
+ struct fwk_event resp;
+ struct pd_set_state_request *req_params =
+ (struct pd_set_state_request *)(&req.params);
+ struct pd_set_state_response *resp_params =
+ (struct pd_set_state_response *)(&resp.params);
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ if (!is_valid_state(pd, state))
+ return FWK_E_PARAM;
+
+ level = get_level_from_tree_pos(pd->config->tree_pos);
+
+ req = (struct fwk_event){
+ .id = FWK_ID_EVENT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN, PD_EVENT_IDX_SET_STATE),
+ .target_id = pd_id,
+ };
+
+ req_params->composite_state = (level << MOD_PD_CS_LEVEL_SHIFT) |
+ (state << mod_pd_cs_level_state_shift[level]);
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS) {
+ return status;
+ }
+
+ return resp_params->status;
+}
+
+#if 0
+static int pd_set_state_async(fwk_id_t pd_id,
+ bool response_requested, unsigned int state)
+{
+ return FWK_SUCCESS;
+}
+#endif
+
+static int pd_set_composite_state(fwk_id_t pd_id, uint32_t composite_state)
+{
+ int status;
+ struct pd_ctx *pd;
+ struct fwk_event req;
+ struct fwk_event resp;
+ struct pd_set_state_request *req_params =
+ (struct pd_set_state_request *)(&req.params);
+ struct pd_set_state_response *resp_params =
+ (struct pd_set_state_response *)(&resp.params);
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ if (!is_valid_composite_state(pd, composite_state))
+ return FWK_E_PARAM;
+
+ req = (struct fwk_event){
+ .id = FWK_ID_EVENT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN, PD_EVENT_IDX_SET_STATE),
+ .source_id = pd->driver_id,
+ .target_id = pd_id,
+ };
+
+ req_params->composite_state = composite_state;
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return resp_params->status;
+}
+
+static int pd_set_composite_state_async(
+ fwk_id_t pd_id,
+ bool response_requested,
+ uint32_t composite_state)
+{
+ return pd_set_composite_state(pd_id, composite_state);
+}
+
+static int pd_get_state(fwk_id_t pd_id, unsigned int *state)
+{
+ int status;
+ struct fwk_event req;
+ struct fwk_event resp;
+ struct pd_get_state_request *req_params =
+ (struct pd_get_state_request *)(&req.params);
+ struct pd_get_state_response *resp_params =
+ (struct pd_get_state_response *)(&resp.params);
+
+ if (state == NULL)
+ return FWK_E_PARAM;
+
+ req = (struct fwk_event){
+ .id = FWK_ID_EVENT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN, PD_EVENT_IDX_GET_STATE),
+ .target_id = pd_id,
+ };
+
+ req_params->composite = false;
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (resp_params->status != FWK_SUCCESS)
+ return resp_params->status;
+
+ *state = resp_params->state;
+
+ return FWK_SUCCESS;
+}
+
+#if 0
+static int pd_get_composite_state(fwk_id_t pd_id, unsigned int *composite_state)
+{
+ int status;
+ struct fwk_event req;
+ struct fwk_event resp;
+ struct pd_get_state_request *req_params =
+ (struct pd_get_state_request *)(&req.params);
+ struct pd_get_state_response *resp_params =
+ (struct pd_get_state_response *)(&resp.params);
+
+ if (composite_state == NULL)
+ return FWK_E_PARAM;
+
+ req = (struct fwk_event) {
+ .id = FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN,
+ PD_EVENT_IDX_GET_STATE),
+ .target_id = pd_id,
+ };
+
+ req_params->composite = true;
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ if (resp_params->status != FWK_SUCCESS)
+ return resp_params->status;
+
+ *composite_state = resp_params->state;
+
+ return FWK_SUCCESS;
+}
+#endif
+
+static int pd_reset(fwk_id_t pd_id)
+{
+ int status;
+ struct fwk_event req;
+ struct fwk_event resp;
+ struct pd_response *resp_params = (struct pd_response *)(&resp.params);
+
+ req = (struct fwk_event){
+ .id =
+ FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, PD_EVENT_IDX_RESET),
+ .target_id = pd_id,
+ };
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return resp_params->status;
+}
+
+static int pd_system_suspend(unsigned int state)
+{
+ int status;
+ struct fwk_event req;
+ struct fwk_event resp;
+ struct pd_system_suspend_request *req_params =
+ (struct pd_system_suspend_request *)(&req.params);
+ struct pd_response *resp_params = (struct pd_response *)(&resp.params);
+
+ req = (struct fwk_event){
+ .id = FWK_ID_EVENT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN, PD_EVENT_IDX_SYSTEM_SUSPEND),
+ .target_id = fwk_module_id_rcar_power_domain,
+ };
+
+ req_params->state = state;
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return resp_params->status;
+}
+
+static int pd_system_shutdown(enum mod_pd_system_shutdown system_shutdown)
+{
+ int status;
+ struct fwk_event req;
+ struct fwk_event resp;
+ struct pd_system_shutdown_request *req_params =
+ (struct pd_system_shutdown_request *)(&req.params);
+ struct pd_response *resp_params = (struct pd_response *)(&resp.params);
+
+ req = (struct fwk_event){
+ .id = FWK_ID_EVENT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN, PD_EVENT_IDX_SYSTEM_SHUTDOWN),
+ .target_id = fwk_module_id_rcar_power_domain,
+ };
+
+ req_params->system_shutdown = system_shutdown;
+
+ status = fwk_thread_put_event_and_wait(&req, &resp);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ return resp_params->status;
+}
+
+/* Functions specific to the driver input API */
+
+static int pd_reset_async(fwk_id_t pd_id, bool response_requested)
+{
+ struct fwk_event req;
+ struct pd_ctx *pd;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ req = (struct fwk_event){
+ .id =
+ FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, PD_EVENT_IDX_RESET),
+ .source_id = pd->driver_id,
+ .target_id = pd_id,
+ .response_requested = response_requested,
+ };
+
+ return fwk_thread_put_event(&req);
+}
+
+static int report_power_state_transition(
+ const struct pd_ctx *pd,
+ unsigned int state)
+{
+ struct fwk_event report;
+ struct pd_power_state_transition_report *report_params =
+ (struct pd_power_state_transition_report *)(&report.params);
+
+ report =
+ (struct fwk_event){ .source_id = pd->driver_id,
+ .target_id = pd->id,
+ .id = FWK_ID_EVENT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN,
+ PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION) };
+ report_params->state = state;
+
+ return fwk_thread_put_event(&report);
+}
+
+static int pd_report_power_state_transition(fwk_id_t pd_id, unsigned int state)
+{
+ const struct pd_ctx *pd;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ return report_power_state_transition(pd, state);
+}
+
+static int pd_get_last_core_pd_id(fwk_id_t *last_core_pd_id)
+{
+ if (last_core_pd_id == NULL)
+ return FWK_E_PARAM;
+
+ *last_core_pd_id = mod_pd_ctx.system_suspend.last_core_pd->id;
+
+ return FWK_SUCCESS;
+}
+
+/* Module APIs */
+
+static const struct mod_pd_public_api pd_public_api = {
+ .get_domain_type = pd_get_domain_type,
+ .get_domain_parent_id = pd_get_domain_parent_id,
+};
+
+static const struct mod_pd_restricted_api pd_restricted_api = {
+ .get_domain_type = pd_get_domain_type,
+ .get_domain_parent_id = pd_get_domain_parent_id,
+
+ .set_state = pd_set_state,
+ .set_state_async = pd_set_composite_state_async,
+ .get_state = pd_get_state,
+ .reset = pd_reset,
+ .system_suspend = pd_system_suspend,
+ .system_shutdown = pd_system_shutdown
+};
+
+static const struct mod_pd_driver_input_api pd_driver_input_api = {
+ .set_state_async = pd_set_composite_state_async,
+ .reset_async = pd_reset_async,
+ .report_power_state_transition = pd_report_power_state_transition,
+ .get_last_core_pd_id = pd_get_last_core_pd_id,
+};
+
+/*
+ * Framework handlers
+ */
+static int pd_init(fwk_id_t module_id, unsigned int dev_count, const void *data)
+{
+ if ((data == NULL) || (dev_count == 0))
+ return FWK_E_PARAM;
+
+ mod_pd_ctx.config = (struct mod_power_domain_config *)data;
+
+ if ((mod_pd_ctx.config->authorized_id_table == NULL) &&
+ (mod_pd_ctx.config->authorized_id_table_size != 0))
+ return FWK_E_PARAM;
+
+ mod_pd_ctx.pd_ctx_table = fwk_mm_calloc(dev_count, sizeof(struct pd_ctx));
+ if (mod_pd_ctx.pd_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ mod_pd_ctx.pd_count = dev_count;
+ mod_pd_ctx.system_pd_ctx = &mod_pd_ctx.pd_ctx_table[dev_count - 1];
+
+ return FWK_SUCCESS;
+}
+
+static int pd_power_domain_init(
+ fwk_id_t pd_id,
+ unsigned int unused,
+ const void *config)
+{
+ static uint64_t max_tree_pos = MOD_PD_INVALID_TREE_POS;
+
+ const struct mod_power_domain_element_config *pd_config =
+ (const struct mod_power_domain_element_config *)config;
+ struct pd_ctx *pd;
+ unsigned int state;
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)];
+
+ if (!is_valid_tree_pos(pd_config->tree_pos))
+ return FWK_E_PARAM;
+
+ if (pd_config->attributes.pd_type >= MOD_PD_TYPE_COUNT)
+ return FWK_E_PARAM;
+
+ /*
+ * Check that the power domains are declared by increasing order of their
+ * tree position.
+ */
+ if ((max_tree_pos != MOD_PD_INVALID_TREE_POS) &&
+ (pd_config->tree_pos <= max_tree_pos))
+ return FWK_E_PARAM;
+ max_tree_pos = pd_config->tree_pos;
+
+ if ((pd_config->allowed_state_mask_table == NULL) ||
+ (pd_config->allowed_state_mask_table_size == 0))
+ return FWK_E_PARAM;
+
+ pd->allowed_state_mask_table = pd_config->allowed_state_mask_table;
+ pd->allowed_state_mask_table_size =
+ pd_config->allowed_state_mask_table_size;
+
+ for (state = 0; state < pd->allowed_state_mask_table_size; state++)
+ pd->valid_state_mask |= pd->allowed_state_mask_table[state];
+
+ pd->id = pd_id;
+ pd->config = pd_config;
+
+ return FWK_SUCCESS;
+}
+
+static int pd_post_init(fwk_id_t module_id)
+{
+ return FWK_SUCCESS;
+}
+
+static int pd_bind(fwk_id_t id, unsigned int round)
+{
+ int status;
+ struct pd_ctx *pd;
+ const struct mod_power_domain_element_config *config;
+ struct mod_pd_driver_api *driver_api = NULL;
+
+ /* Nothing to do but during the first round of calls */
+ if (round != 0) {
+ return FWK_SUCCESS;
+ }
+
+#if 0
+ if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
+ return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG),
+ FWK_ID_API(FWK_MODULE_IDX_LOG, 0), &mod_pd_ctx.log_api);
+ }
+#endif
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(id)];
+ config = pd->config;
+
+ status = fwk_module_bind(config->driver_id, config->api_id, &driver_api);
+ if (status != FWK_SUCCESS) {
+ return status;
+ }
+
+ pd->driver_id = config->driver_id;
+ if ((driver_api->set_state == NULL) || (driver_api->get_state == NULL) ||
+ (driver_api->reset == NULL) ||
+ ((config->attributes.pd_type == MOD_PD_TYPE_CORE) &&
+ (driver_api->prepare_core_for_system_suspend == NULL))) {
+ return FWK_E_PARAM;
+ }
+
+ pd->driver_api = driver_api;
+
+ return FWK_SUCCESS;
+}
+
+static int pd_start(fwk_id_t id)
+{
+ struct pd_ctx *pd;
+ int index;
+ struct fwk_event notification_event = {
+ .source_id = FWK_ID_ELEMENT(
+ FWK_MODULE_IDX_RCAR_POWER_DOMAIN,
+ CONFIG_POWER_DOMAIN_CHILD_COUNT + rcar_core_get_count()),
+ .id = mod_pd_notification_id_power_state_transition,
+ .response_requested = true,
+ };
+ struct mod_pd_power_state_transition_notification_params *params;
+ unsigned int notification_count;
+
+ /* Nothing to do for elements */
+ if (fwk_module_is_valid_element_id(id))
+ return FWK_SUCCESS;
+
+ for (index = mod_pd_ctx.pd_count - 1; index >= 0; index--) {
+ pd = &mod_pd_ctx.pd_ctx_table[index];
+ pd->requested_state = MOD_PD_STATE_OFF;
+ pd->state_requested_to_driver = MOD_PD_STATE_OFF;
+ pd->current_state = MOD_PD_STATE_OFF;
+ }
+
+ /* CPU0 is always on. */
+ pd = &mod_pd_ctx.pd_ctx_table[PD_RCAR_CLUS0CORE0];
+ pd->current_state = MOD_PD_STATE_ON;
+ /* CLUSTER0 is always on. */
+ pd = &mod_pd_ctx.pd_ctx_table[PD_RCAR_CLUSTER0];
+ pd->current_state = MOD_PD_STATE_ON;
+ /* CLUSTER1 is always on. */
+ pd = &mod_pd_ctx.pd_ctx_table[PD_RCAR_CLUSTER1];
+ pd->current_state = MOD_PD_STATE_ON;
+
+ params = (struct mod_pd_power_state_transition_notification_params *)
+ notification_event.params;
+ params->state = MOD_PD_STATE_ON;
+ fwk_notification_notify(&notification_event, &notification_count);
+
+ return FWK_SUCCESS;
+}
+
+static int pd_process_bind_request(
+ fwk_id_t source_id,
+ fwk_id_t target_id,
+ fwk_id_t api_id,
+ const void **api)
+{
+ struct pd_ctx *pd;
+ unsigned int id_idx;
+
+ switch (fwk_id_get_api_idx(api_id)) {
+ case MOD_PD_API_IDX_PUBLIC:
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE))
+ return FWK_E_ACCESS;
+ *api = &pd_public_api;
+ break;
+
+ case MOD_PD_API_IDX_RESTRICTED:
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE))
+ return FWK_E_ACCESS;
+ if (mod_pd_ctx.config->authorized_id_table_size == 0) {
+ *api = &pd_restricted_api;
+ return FWK_SUCCESS;
+ }
+ for (id_idx = 0; id_idx < mod_pd_ctx.config->authorized_id_table_size;
+ id_idx++) {
+ if (fwk_id_is_equal(
+ source_id,
+ mod_pd_ctx.config->authorized_id_table[id_idx])) {
+ *api = &pd_restricted_api;
+ return FWK_SUCCESS;
+ }
+ }
+ return FWK_E_ACCESS;
+
+ case MOD_PD_API_IDX_DRIVER_INPUT:
+ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT))
+ return FWK_E_ACCESS;
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(target_id)];
+ if (!fwk_id_is_equal(source_id, pd->driver_id))
+ return FWK_E_ACCESS;
+ *api = &pd_driver_input_api;
+ break;
+
+ default:
+ return FWK_E_PARAM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int pd_process_event(
+ const struct fwk_event *event,
+ struct fwk_event *resp)
+{
+ struct pd_ctx *pd = NULL;
+
+ if (fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT))
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(event->target_id)];
+
+ switch (fwk_id_get_event_idx(event->id)) {
+ case PD_EVENT_IDX_SET_STATE:
+ assert(pd != NULL);
+
+ process_set_state_request(
+ pd, (struct pd_set_state_request *)event->params, resp);
+
+ return FWK_SUCCESS;
+
+ case PD_EVENT_IDX_GET_STATE:
+ assert(pd != NULL);
+
+ process_get_state_request(
+ pd,
+ (struct pd_get_state_request *)event->params,
+ (struct pd_get_state_response *)resp->params);
+
+ return FWK_SUCCESS;
+
+ case PD_EVENT_IDX_RESET:
+ assert(pd != NULL);
+
+ process_reset_request(pd, (struct pd_response *)resp->params);
+
+ return FWK_SUCCESS;
+
+ case PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION:
+ assert(pd != NULL);
+
+ process_power_state_transition_report(
+ pd, (struct pd_power_state_transition_report *)event->params);
+
+ return FWK_SUCCESS;
+
+ case PD_EVENT_IDX_SYSTEM_SUSPEND:
+ process_system_suspend_request(
+ (struct pd_system_suspend_request *)event->params,
+ (struct pd_response *)resp->params);
+
+ return FWK_SUCCESS;
+
+ case PD_EVENT_IDX_SYSTEM_SHUTDOWN:
+ process_system_shutdown_request(
+ (struct pd_system_shutdown_request *)event->params,
+ (struct pd_response *)resp->params);
+
+ return FWK_SUCCESS;
+
+ default:
+ FWK_LOG_ERR(
+ "[PD] ERROR: Invalid power state request: <%d>.", event->id.value);
+
+ return FWK_E_PARAM;
+ }
+}
+
+static int process_power_state_pre_transition_notification_response(
+ struct pd_ctx *pd,
+ struct mod_pd_power_state_pre_transition_notification_resp_params *params)
+{
+ if (pd->power_state_pre_transition_notification_ctx.pending_responses ==
+ 0) {
+ assert(false);
+ return FWK_E_PANIC;
+ }
+
+ if (params->status != FWK_SUCCESS) {
+ pd->power_state_pre_transition_notification_ctx.response_status =
+ FWK_E_DEVICE;
+ }
+
+ pd->power_state_pre_transition_notification_ctx.pending_responses--;
+ if (pd->power_state_pre_transition_notification_ctx.pending_responses != 0)
+ return FWK_SUCCESS;
+
+ if (pd->power_state_pre_transition_notification_ctx.valid == true) {
+ /*
+ * All the notification responses have been received, the requested
+ * state for the power domain has not changed in the
+ * meantime and all the notified entities agreed on the power state
+ * transition, proceed with it.
+ */
+ if (pd->power_state_pre_transition_notification_ctx.response_status ==
+ FWK_SUCCESS)
+ initiate_power_state_transition(pd);
+ } else {
+ /*
+ * All the notification responses have been received but the
+ * requested state for the power domain has changed, start the
+ * processings for the new requested state.
+ */
+ if ((pd->requested_state == pd->state_requested_to_driver) ||
+ (!is_allowed_by_parent_and_children(pd, pd->requested_state)))
+ return FWK_SUCCESS;
+
+ if (!initiate_power_state_pre_transition_notification(pd))
+ initiate_power_state_transition(pd);
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int process_power_state_transition_notification_response(
+ struct pd_ctx *pd)
+{
+ struct fwk_event notification_event;
+ struct mod_pd_power_state_transition_notification_params *params;
+
+ if (pd->power_state_transition_notification_ctx.pending_responses == 0) {
+ assert(false);
+ return FWK_E_PANIC;
+ }
+
+ pd->power_state_transition_notification_ctx.pending_responses--;
+ if (pd->power_state_transition_notification_ctx.pending_responses != 0)
+ return FWK_SUCCESS;
+
+ if (pd->power_state_transition_notification_ctx.state == pd->current_state)
+ return FWK_SUCCESS;
+
+ /*
+ * While receiving the responses, the power state of the power domain
+ * has changed. Send a notification for the current power state.
+ */
+ notification_event.id = mod_pd_notification_id_power_state_transition;
+ notification_event.response_requested = true;
+ params = (struct mod_pd_power_state_transition_notification_params *)
+ notification_event.params;
+ params->state = pd->current_state;
+
+ pd->power_state_transition_notification_ctx.state = pd->current_state;
+ fwk_notification_notify(
+ &notification_event,
+ &pd->power_state_transition_notification_ctx.pending_responses);
+
+ return FWK_SUCCESS;
+}
+
+static int pd_process_notification(
+ const struct fwk_event *event,
+ struct fwk_event *resp)
+{
+ struct pd_ctx *pd;
+
+ /* Only responses are expected. */
+ if (!event->is_response) {
+ assert(false);
+ return FWK_E_SUPPORT;
+ }
+
+ if (!fwk_module_is_valid_element_id(event->target_id)) {
+ assert(false);
+ return FWK_E_PARAM;
+ }
+
+ pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(event->target_id)];
+
+ if (fwk_id_is_equal(
+ event->id, mod_pd_notification_id_power_state_transition))
+ return process_power_state_transition_notification_response(pd);
+
+ return process_power_state_pre_transition_notification_response(
+ pd,
+ (struct mod_pd_power_state_pre_transition_notification_resp_params *)
+ event->params);
+}
+
+/* Module definition */
+const struct fwk_module module_rcar_power_domain = {
+ .name = "POWER DOMAIN",
+ .type = FWK_MODULE_TYPE_HAL,
+ .api_count = MOD_PD_API_IDX_COUNT,
+ .event_count = PD_EVENT_COUNT,
+ .notification_count = MOD_PD_NOTIFICATION_COUNT,
+ .init = pd_init,
+ .element_init = pd_power_domain_init,
+ .post_init = pd_post_init,
+ .bind = pd_bind,
+ .start = pd_start,
+ .process_bind_request = pd_process_bind_request,
+ .process_event = pd_process_event,
+ .process_notification = pd_process_notification
+};
diff --git a/product/rcar/scp_ramfw/config_rcar_power_domain.c b/product/rcar/scp_ramfw/config_rcar_power_domain.c
new file mode 100644
index 00000000..75558351
--- /dev/null
+++ b/product/rcar/scp_ramfw/config_rcar_power_domain.c
@@ -0,0 +1,423 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <config_rcar_pd_core.h>
+#include <config_rcar_pd_sysc.h>
+#include <config_rcar_power_domain.h>
+#include <rcar_core.h>
+
+#include <mod_rcar_pd_core.h>
+#include <mod_rcar_power_domain.h>
+#include <mod_system_power.h>
+
+#include <fwk_element.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <fwk_module.h>
+#include <fwk_module_idx.h>
+
+#include <stdint.h>
+#include <string.h>
+
+static const char *core_pd_name_table[RCAR_CORE_PER_CLUSTER_MAX] = {
+ "CLUS0CORE0", "CLUS0CORE1", "CLUS0CORE2", "CLUS0CORE3",
+ "CLUS1CORE0", "CLUS1CORE1", "CLUS1CORE2", "CLUS1CORE3",
+};
+
+/* Mask of the allowed states for the systop power domain */
+static const uint32_t systop_allowed_state_mask_table[] = {
+ [0] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK |
+ (1 << MOD_SYSTEM_POWER_POWER_STATE_SLEEP0) |
+ (1 << MOD_SYSTEM_POWER_POWER_STATE_SLEEP1)
+};
+/*
+ * Mask of the allowed states for the top level power domains
+ * (but the cluster power domains) depending on the system states.
+ */
+static const uint32_t toplevel_allowed_state_mask_table[] = {
+ [MOD_PD_STATE_OFF] = MOD_PD_STATE_OFF_MASK,
+ [MOD_PD_STATE_ON] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK,
+ [MOD_SYSTEM_POWER_POWER_STATE_SLEEP0] = MOD_PD_STATE_OFF_MASK,
+ [MOD_SYSTEM_POWER_POWER_STATE_SLEEP1] = MOD_PD_STATE_OFF_MASK
+};
+/*
+ * Mask of the allowed states for the cluster power domain depending on the
+ * system states.
+ */
+static const uint32_t cluster_pd_allowed_state_mask_table[] = {
+ [MOD_PD_STATE_OFF] = MOD_PD_STATE_OFF_MASK,
+ [MOD_PD_STATE_ON] =
+ MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK | MOD_PD_STATE_SLEEP_MASK,
+ [MOD_SYSTEM_POWER_POWER_STATE_SLEEP0] = MOD_PD_STATE_OFF_MASK,
+ [MOD_SYSTEM_POWER_POWER_STATE_SLEEP1] = MOD_PD_STATE_OFF_MASK
+};
+
+/* Mask of the allowed states for a core depending on the cluster states. */
+static const uint32_t core_pd_allowed_state_mask_table[] = {
+ [MOD_PD_STATE_OFF] = MOD_PD_STATE_OFF_MASK,
+ [MOD_PD_STATE_ON] =
+ MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK | MOD_PD_STATE_SLEEP_MASK,
+ [MOD_PD_STATE_SLEEP] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_SLEEP_MASK,
+};
+
+/* Power module specific configuration data (none) */
+static const struct mod_power_domain_config rcar_power_domain_config = {};
+
+static struct
+ fwk_element
+ rcar_power_domain_static_element_table
+ [] =
+ {
+ [CONFIG_POWER_DOMAIN_CHILD_CLUSTER0] =
+ {
+ .name = "CLUS0",
+ .data =
+ &(
+ (struct mod_power_domain_element_config){
+ .attributes.pd_type =
+ MOD_PD_TYPE_CLUSTER,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_CLUSTER0,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_RCAR_PD_CORE,
+ RCAR_PD_CORE_ELEMENT_IDX_CLU0),
+ .api_id =
+ FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_CORE,
+ MOD_RCAR_PD_SYSC_API_IDX_POWER_DOMAIN_DRIVER),
+ .allowed_state_mask_table = cluster_pd_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(
+ cluster_pd_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_CLUSTER1] =
+ {
+ .name = "CLUS1",
+ .data = &((struct mod_power_domain_element_config){ .attributes
+ .pd_type = MOD_PD_TYPE_CLUSTER,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_CLUSTER1,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_RCAR_PD_CORE,
+ RCAR_PD_CORE_ELEMENT_IDX_CLU1),
+ .api_id = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_CORE,
+ MOD_RCAR_PD_SYSC_API_IDX_POWER_DOMAIN_DRIVER),
+ .allowed_state_mask_table = cluster_pd_allowed_state_mask_table,
+ .allowed_state_mask_table_size = FWK_ARRAY_SIZE(
+ cluster_pd_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_A3IR] =
+ {
+ .name = "a3ir",
+ .data = &((struct mod_power_domain_element_config){ .attributes
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_A3IR,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ RCAR_PD_SYSC_ELEMENT_IDX_A3IR),
+ .api_id = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ 0),
+ .allowed_state_mask_table = toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size = FWK_ARRAY_SIZE(
+ toplevel_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_3DGE] =
+ {
+ .name = "3dg-e",
+ .data = &((struct mod_power_domain_element_config){ .attributes
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_3DGE,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ RCAR_PD_SYSC_ELEMENT_IDX_3DGE),
+ .api_id = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ 0),
+ .allowed_state_mask_table = toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size = FWK_ARRAY_SIZE(
+ toplevel_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_3DGD] =
+ {
+ .name = "3dg-d",
+ .data = &(
+ (struct
+ mod_power_domain_element_config){ .attributes.pd_type = MOD_PD_TYPE_DEVICE, .tree_pos = MOD_PD_TREE_POS(MOD_PD_LEVEL_2, 0, 0, CONFIG_POWER_DOMAIN_CHILD_3DGD, 0), .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_3DGD), .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), .allowed_state_mask_table = toplevel_allowed_state_mask_table, .allowed_state_mask_table_size = FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_3DGC] =
+ {
+ .name = "3dg-c",
+ .data = &(
+ (struct
+ mod_power_domain_element_config){ .attributes
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_3DGC,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ RCAR_PD_SYSC_ELEMENT_IDX_3DGC),
+ .api_id = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ 0),
+ .allowed_state_mask_table =
+ toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(
+ toplevel_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_3DGB] =
+ {
+ .name = "3dg-b",
+ .data = &(
+ (struct mod_power_domain_element_config){
+ .attributes.pd_type =
+ MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_3DGB,
+ 0),
+ .driver_id =
+ FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ RCAR_PD_SYSC_ELEMENT_IDX_3DGB),
+ .api_id =
+ FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ 0),
+ .allowed_state_mask_table =
+ toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(
+ toplevel_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_3DGA] =
+ {
+ .name = "3dg-a",
+ .data = &(
+ (struct mod_power_domain_element_config){
+ .attributes.pd_type =
+ MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_3DGA,
+ 0),
+ .driver_id =
+ FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ RCAR_PD_SYSC_ELEMENT_IDX_3DGA),
+ .api_id =
+ FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ 0),
+ .allowed_state_mask_table =
+ toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(
+ toplevel_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_A2VC1] =
+ {
+ .name = "a2vc1",
+ .data = &((struct mod_power_domain_element_config){ .attributes
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_A2VC1,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ RCAR_PD_SYSC_ELEMENT_IDX_A2VC1),
+ .api_id = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ 0),
+ .allowed_state_mask_table =
+ toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(
+ toplevel_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_A3VC] =
+ {
+ .name = "a3vc",
+ .data = &((struct mod_power_domain_element_config){ .attributes
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_A3VC,
+ 0),
+ .driver_id =
+ FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_A3VC),
+ .api_id = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ 0),
+ .allowed_state_mask_table =
+ toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(
+ toplevel_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_CR7] =
+ {
+ .name = "cr7",
+ .data = &((struct mod_power_domain_element_config){ .attributes
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_CR7,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ RCAR_PD_SYSC_ELEMENT_IDX_CR7),
+ .api_id = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ 0),
+ .allowed_state_mask_table =
+ toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(
+ toplevel_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_A3VP] =
+ {
+ .name = "a3vp",
+ .data = &((struct mod_power_domain_element_config){ .attributes
+ .pd_type = MOD_PD_TYPE_DEVICE,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ CONFIG_POWER_DOMAIN_CHILD_A3VP,
+ 0),
+ .driver_id = FWK_ID_ELEMENT_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ RCAR_PD_SYSC_ELEMENT_IDX_A3VP),
+ .api_id = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_RCAR_PD_SYSC,
+ 0),
+ .allowed_state_mask_table =
+ toplevel_allowed_state_mask_table,
+ .allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(
+ toplevel_allowed_state_mask_table) }),
+ },
+ [CONFIG_POWER_DOMAIN_CHILD_COUNT] =
+ {
+ .name = "SYSTOP",
+ .data = &((struct mod_power_domain_element_config){ .attributes
+ .pd_type = MOD_PD_TYPE_SYSTEM,
+ .tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2,
+ 0,
+ 0,
+ 0,
+ 0),
+ .driver_id =
+ FWK_ID_MODULE_INIT(
+ FWK_MODULE_IDX_SYSTEM_POWER),
+ .api_id = FWK_ID_API_INIT(
+ FWK_MODULE_IDX_SYSTEM_POWER,
+ MOD_SYSTEM_POWER_API_IDX_PD_DRIVER),
+ .allowed_state_mask_table =
+ systop_allowed_state_mask_table,
+ .allowed_state_mask_table_size = FWK_ARRAY_SIZE(
+ systop_allowed_state_mask_table) }),
+ },
+ };
+
+/*
+ * Function definitions with internal linkage
+ */
+static const struct fwk_element *rcar_power_domain_get_element_table(
+ fwk_id_t module_id)
+{
+ struct fwk_element *element_table, *element;
+ struct mod_power_domain_element_config *pd_config_table, *pd_config;
+ unsigned int core_idx;
+
+ element_table = fwk_mm_calloc(
+ rcar_core_get_count() +
+ FWK_ARRAY_SIZE(rcar_power_domain_static_element_table) +
+ 1, /* Terminator */
+ sizeof(struct fwk_element));
+ if (element_table == NULL)
+ return NULL;
+
+ pd_config_table = fwk_mm_calloc(
+ rcar_core_get_count(), sizeof(struct mod_power_domain_element_config));
+ if (pd_config_table == NULL)
+ return NULL;
+
+ for (core_idx = 0; core_idx < rcar_core_get_count(); core_idx++) {
+ element = &element_table[core_idx];
+ pd_config = &pd_config_table[core_idx];
+
+ element->name = core_pd_name_table[core_idx];
+ element->data = pd_config;
+
+ pd_config->attributes.pd_type = MOD_PD_TYPE_CORE,
+ pd_config->tree_pos = MOD_PD_TREE_POS(
+ MOD_PD_LEVEL_2, 0, 0, CONFIG_POWER_DOMAIN_CHILD_CLUSTER0, core_idx),
+ pd_config->driver_id =
+ FWK_ID_ELEMENT(FWK_MODULE_IDX_RCAR_PD_CORE, core_idx),
+ pd_config->api_id = FWK_ID_API(
+ FWK_MODULE_IDX_RCAR_PD_CORE,
+ MOD_RCAR_PD_SYSC_API_IDX_POWER_DOMAIN_DRIVER),
+ pd_config->allowed_state_mask_table = core_pd_allowed_state_mask_table,
+ pd_config->allowed_state_mask_table_size =
+ FWK_ARRAY_SIZE(core_pd_allowed_state_mask_table);
+ }
+
+ memcpy(
+ element_table + rcar_core_get_count(),
+ rcar_power_domain_static_element_table,
+ sizeof(rcar_power_domain_static_element_table));
+
+ return element_table;
+}
+
+/*
+ * Power module configuration data
+ */
+struct fwk_module_config config_rcar_power_domain = {
+ .elements =
+ FWK_MODULE_DYNAMIC_ELEMENTS(rcar_power_domain_get_element_table),
+ .data = &rcar_power_domain_config,
+};
diff --git a/product/rcar/scp_ramfw/config_rcar_power_domain.h b/product/rcar/scp_ramfw/config_rcar_power_domain.h
new file mode 100644
index 00000000..39987ca8
--- /dev/null
+++ b/product/rcar/scp_ramfw/config_rcar_power_domain.h
@@ -0,0 +1,38 @@
+/*
+ * Renesas SCP/MCP Software
+ * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CONFIG_RCAR_POWER_DOMAIN_H
+#define CONFIG_RCAR_POWER_DOMAIN_H
+
+#include <rcar_scmi_id.h>
+
+#define CONFIG_POWER_DOMAIN_CORE_CLUS0CORE0 PD_RCAR_CLUS0CORE0
+#define CONFIG_POWER_DOMAIN_CORE_CLUS0CORE1 PD_RCAR_CLUS0CORE1
+#define CONFIG_POWER_DOMAIN_CORE_CLUS0CORE2 PD_RCAR_CLUS0CORE2
+#define CONFIG_POWER_DOMAIN_CORE_CLUS0CORE3 PD_RCAR_CLUS0CORE3
+#define CONFIG_POWER_DOMAIN_CORE_CLUS1CORE0 PD_RCAR_CLUS1CORE0
+#define CONFIG_POWER_DOMAIN_CORE_CLUS1CORE1 PD_RCAR_CLUS1CORE1
+#define CONFIG_POWER_DOMAIN_CORE_CLUS1CORE2 PD_RCAR_CLUS1CORE2
+#define CONFIG_POWER_DOMAIN_CORE_CLUS1CORE3 PD_RCAR_CLUS1CORE3
+
+enum rcar_powerdomain_child_index {
+ CONFIG_POWER_DOMAIN_CHILD_CLUSTER0,
+ CONFIG_POWER_DOMAIN_CHILD_CLUSTER1,
+ CONFIG_POWER_DOMAIN_CHILD_A3IR,
+ CONFIG_POWER_DOMAIN_CHILD_3DGE,
+ CONFIG_POWER_DOMAIN_CHILD_3DGD,
+ CONFIG_POWER_DOMAIN_CHILD_3DGC,
+ CONFIG_POWER_DOMAIN_CHILD_3DGB,
+ CONFIG_POWER_DOMAIN_CHILD_3DGA,
+ CONFIG_POWER_DOMAIN_CHILD_A2VC1,
+ CONFIG_POWER_DOMAIN_CHILD_A3VC,
+ CONFIG_POWER_DOMAIN_CHILD_CR7,
+ CONFIG_POWER_DOMAIN_CHILD_A3VP,
+ CONFIG_POWER_DOMAIN_CHILD_COUNT
+};
+
+#endif /* CONFIG_RCAR_POWER_DOMAIN_H */