123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- PSCI Power Domain Tree Structure
- ================================
- Requirements
- ------------
- #. A platform must export the ``plat_get_aff_count()`` and
- ``plat_get_aff_state()`` APIs to enable the generic PSCI code to
- populate a tree that describes the hierarchy of power domains in the
- system. This approach is inflexible because a change to the topology
- requires a change in the code.
- It would be much simpler for the platform to describe its power domain tree
- in a data structure.
- #. The generic PSCI code generates MPIDRs in order to populate the power domain
- tree. It also uses an MPIDR to find a node in the tree. The assumption that
- a platform will use exactly the same MPIDRs as generated by the generic PSCI
- code is not scalable. The use of an MPIDR also restricts the number of
- levels in the power domain tree to four.
- Therefore, there is a need to decouple allocation of MPIDRs from the
- mechanism used to populate the power domain topology tree.
- #. The current arrangement of the power domain tree requires a binary search
- over the sibling nodes at a particular level to find a specified power
- domain node. During a power management operation, the tree is traversed from
- a 'start' to an 'end' power level. The binary search is required to find the
- node at each level. The natural way to perform this traversal is to
- start from a leaf node and follow the parent node pointer to reach the end
- level.
- Therefore, there is a need to define data structures that implement the tree in
- a way which facilitates such a traversal.
- #. The attributes of a core power domain differ from the attributes of power
- domains at higher levels. For example, only a core power domain can be identified
- using an MPIDR. There is no requirement to perform state coordination while
- performing a power management operation on the core power domain.
- Therefore, there is a need to implement the tree in a way which facilitates this
- distinction between a leaf and non-leaf node and any associated
- optimizations.
- --------------
- Design
- ------
- Describing a power domain tree
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- To fulfill requirement 1., the existing platform APIs
- ``plat_get_aff_count()`` and ``plat_get_aff_state()`` have been
- removed. A platform must define an array of unsigned chars such that:
- #. The first entry in the array specifies the number of power domains at the
- highest power level implemented in the platform. This caters for platforms
- where the power domain tree does not have a single root node, for example,
- the FVP has two cluster power domains at the highest level (1).
- #. Each subsequent entry corresponds to a power domain and contains the number
- of power domains that are its direct children.
- #. The size of the array minus the first entry will be equal to the number of
- non-leaf power domains.
- #. The value in each entry in the array is used to find the number of entries
- to consider at the next level. The sum of the values (number of children) of
- all the entries at a level specifies the number of entries in the array for
- the next level.
- The following example power domain topology tree will be used to describe the
- above text further. The leaf and non-leaf nodes in this tree have been numbered
- separately.
- ::
- +-+
- |0|
- +-+
- / \
- / \
- / \
- / \
- / \
- / \
- / \
- / \
- / \
- / \
- +-+ +-+
- |1| |2|
- +-+ +-+
- / \ / \
- / \ / \
- / \ / \
- / \ / \
- +-+ +-+ +-+ +-+
- |3| |4| |5| |6|
- +-+ +-+ +-+ +-+
- +---+-----+ +----+----| +----+----+ +----+-----+-----+
- | | | | | | | | | | | | |
- | | | | | | | | | | | | |
- v v v v v v v v v v v v v
- +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +--+
- |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |10| |11| |12|
- +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +--+
- This tree is defined by the platform as the array described above as follows:
- .. code:: c
- #define PLAT_NUM_POWER_DOMAINS 20
- #define PLATFORM_CORE_COUNT 13
- #define PSCI_NUM_NON_CPU_PWR_DOMAINS \
- (PLAT_NUM_POWER_DOMAINS - PLATFORM_CORE_COUNT)
- unsigned char plat_power_domain_tree_desc[] = { 1, 2, 2, 2, 3, 3, 3, 4};
- Removing assumptions about MPIDRs used in a platform
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- To fulfill requirement 2., it is assumed that the platform assigns a
- unique number (core index) between ``0`` and ``PLAT_CORE_COUNT - 1`` to each core
- power domain. MPIDRs could be allocated in any manner and will not be used to
- populate the tree.
- ``plat_core_pos_by_mpidr(mpidr)`` will return the core index for the core
- corresponding to the MPIDR. It will return an error (-1) if an MPIDR is passed
- which is not allocated or corresponds to an absent core. The semantics of this
- platform API have changed since it is required to validate the passed MPIDR. It
- has been made a mandatory API as a result.
- Another mandatory API, ``plat_my_core_pos()`` has been added to return the core
- index for the calling core. This API provides a more lightweight mechanism to get
- the index since there is no need to validate the MPIDR of the calling core.
- The platform should assign the core indices (as illustrated in the diagram above)
- such that, if the core nodes are numbered from left to right, then the index
- for a core domain will be the same as the index returned by
- ``plat_core_pos_by_mpidr()`` or ``plat_my_core_pos()`` for that core. This
- relationship allows the core nodes to be allocated in a separate array
- (requirement 4.) during ``psci_setup()`` in such an order that the index of the
- core in the array is the same as the return value from these APIs.
- Dealing with holes in MPIDR allocation
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- For platforms where the number of allocated MPIDRs is equal to the number of
- core power domains, for example, Juno and FVPs, the logic to convert an MPIDR to
- a core index should remain unchanged. Both Juno and FVP use a simple collision
- proof hash function to do this.
- It is possible that on some platforms, the allocation of MPIDRs is not
- contiguous or certain cores have been disabled. This essentially means that the
- MPIDRs have been sparsely allocated, that is, the size of the range of MPIDRs
- used by the platform is not equal to the number of core power domains.
- The platform could adopt one of the following approaches to deal with this
- scenario:
- #. Implement more complex logic to convert a valid MPIDR to a core index while
- maintaining the relationship described earlier. This means that the power
- domain tree descriptor will not describe any core power domains which are
- disabled or absent. Entries will not be allocated in the tree for these
- domains.
- #. Treat unallocated MPIDRs and disabled cores as absent but still describe them
- in the power domain descriptor, that is, the number of core nodes described
- is equal to the size of the range of MPIDRs allocated. This approach will
- lead to memory wastage since entries will be allocated in the tree but will
- allow use of a simpler logic to convert an MPIDR to a core index.
- Traversing through and distinguishing between core and non-core power domains
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- To fulfill requirement 3 and 4, separate data structures have been defined
- to represent leaf and non-leaf power domain nodes in the tree.
- .. code:: c
- /*******************************************************************************
- * The following two data structures implement the power domain tree. The tree
- * is used to track the state of all the nodes i.e. power domain instances
- * described by the platform. The tree consists of nodes that describe CPU power
- * domains i.e. leaf nodes and all other power domains which are parents of a
- * CPU power domain i.e. non-leaf nodes.
- ******************************************************************************/
- typedef struct non_cpu_pwr_domain_node {
- /*
- * Index of the first CPU power domain node level 0 which has this node
- * as its parent.
- */
- unsigned int cpu_start_idx;
- /*
- * Number of CPU power domains which are siblings of the domain indexed
- * by 'cpu_start_idx' i.e. all the domains in the range 'cpu_start_idx
- * -> cpu_start_idx + ncpus' have this node as their parent.
- */
- unsigned int ncpus;
- /* Index of the parent power domain node */
- unsigned int parent_node;
- -----
- } non_cpu_pd_node_t;
- typedef struct cpu_pwr_domain_node {
- u_register_t mpidr;
- /* Index of the parent power domain node */
- unsigned int parent_node;
- -----
- } cpu_pd_node_t;
- The power domain tree is implemented as a combination of the following data
- structures.
- .. code:: c
- non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS];
- cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT];
- Populating the power domain tree
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- The ``populate_power_domain_tree()`` function in ``psci_setup.c`` implements the
- algorithm to parse the power domain descriptor exported by the platform to
- populate the two arrays. It is essentially a breadth-first-search. The nodes for
- each level starting from the root are laid out one after another in the
- ``psci_non_cpu_pd_nodes`` and ``psci_cpu_pd_nodes`` arrays as follows:
- ::
- psci_non_cpu_pd_nodes -> [[Level 3 nodes][Level 2 nodes][Level 1 nodes]]
- psci_cpu_pd_nodes -> [Level 0 nodes]
- For the example power domain tree illustrated above, the ``psci_cpu_pd_nodes``
- will be populated as follows. The value in each entry is the index of the parent
- node. Other fields have been ignored for simplicity.
- ::
- +-------------+ ^
- CPU0 | 3 | |
- +-------------+ |
- CPU1 | 3 | |
- +-------------+ |
- CPU2 | 3 | |
- +-------------+ |
- CPU3 | 4 | |
- +-------------+ |
- CPU4 | 4 | |
- +-------------+ |
- CPU5 | 4 | | PLATFORM_CORE_COUNT
- +-------------+ |
- CPU6 | 5 | |
- +-------------+ |
- CPU7 | 5 | |
- +-------------+ |
- CPU8 | 5 | |
- +-------------+ |
- CPU9 | 6 | |
- +-------------+ |
- CPU10 | 6 | |
- +-------------+ |
- CPU11 | 6 | |
- +-------------+ |
- CPU12 | 6 | v
- +-------------+
- The ``psci_non_cpu_pd_nodes`` array will be populated as follows. The value in
- each entry is the index of the parent node.
- ::
- +-------------+ ^
- PD0 | -1 | |
- +-------------+ |
- PD1 | 0 | |
- +-------------+ |
- PD2 | 0 | |
- +-------------+ |
- PD3 | 1 | | PLAT_NUM_POWER_DOMAINS -
- +-------------+ | PLATFORM_CORE_COUNT
- PD4 | 1 | |
- +-------------+ |
- PD5 | 2 | |
- +-------------+ |
- PD6 | 2 | |
- +-------------+ v
- Each core can find its node in the ``psci_cpu_pd_nodes`` array using the
- ``plat_my_core_pos()`` function. When a core is turned on, the normal world
- provides an MPIDR. The ``plat_core_pos_by_mpidr()`` function is used to validate
- the MPIDR before using it to find the corresponding core node. The non-core power
- domain nodes do not need to be identified.
- --------------
- *Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.*
|