|
|
|
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.*
|