diff options
Diffstat (limited to 'docs/design/psci-pd-tree.rst')
-rw-r--r-- | docs/design/psci-pd-tree.rst | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/docs/design/psci-pd-tree.rst b/docs/design/psci-pd-tree.rst new file mode 100644 index 0000000..56a6d6f --- /dev/null +++ b/docs/design/psci-pd-tree.rst @@ -0,0 +1,304 @@ +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.* |