1*91f16700SchasingluluPSCI Power Domain Tree Structure 2*91f16700Schasinglulu================================ 3*91f16700Schasinglulu 4*91f16700SchasingluluRequirements 5*91f16700Schasinglulu------------ 6*91f16700Schasinglulu 7*91f16700Schasinglulu#. A platform must export the ``plat_get_aff_count()`` and 8*91f16700Schasinglulu ``plat_get_aff_state()`` APIs to enable the generic PSCI code to 9*91f16700Schasinglulu populate a tree that describes the hierarchy of power domains in the 10*91f16700Schasinglulu system. This approach is inflexible because a change to the topology 11*91f16700Schasinglulu requires a change in the code. 12*91f16700Schasinglulu 13*91f16700Schasinglulu It would be much simpler for the platform to describe its power domain tree 14*91f16700Schasinglulu in a data structure. 15*91f16700Schasinglulu 16*91f16700Schasinglulu#. The generic PSCI code generates MPIDRs in order to populate the power domain 17*91f16700Schasinglulu tree. It also uses an MPIDR to find a node in the tree. The assumption that 18*91f16700Schasinglulu a platform will use exactly the same MPIDRs as generated by the generic PSCI 19*91f16700Schasinglulu code is not scalable. The use of an MPIDR also restricts the number of 20*91f16700Schasinglulu levels in the power domain tree to four. 21*91f16700Schasinglulu 22*91f16700Schasinglulu Therefore, there is a need to decouple allocation of MPIDRs from the 23*91f16700Schasinglulu mechanism used to populate the power domain topology tree. 24*91f16700Schasinglulu 25*91f16700Schasinglulu#. The current arrangement of the power domain tree requires a binary search 26*91f16700Schasinglulu over the sibling nodes at a particular level to find a specified power 27*91f16700Schasinglulu domain node. During a power management operation, the tree is traversed from 28*91f16700Schasinglulu a 'start' to an 'end' power level. The binary search is required to find the 29*91f16700Schasinglulu node at each level. The natural way to perform this traversal is to 30*91f16700Schasinglulu start from a leaf node and follow the parent node pointer to reach the end 31*91f16700Schasinglulu level. 32*91f16700Schasinglulu 33*91f16700Schasinglulu Therefore, there is a need to define data structures that implement the tree in 34*91f16700Schasinglulu a way which facilitates such a traversal. 35*91f16700Schasinglulu 36*91f16700Schasinglulu#. The attributes of a core power domain differ from the attributes of power 37*91f16700Schasinglulu domains at higher levels. For example, only a core power domain can be identified 38*91f16700Schasinglulu using an MPIDR. There is no requirement to perform state coordination while 39*91f16700Schasinglulu performing a power management operation on the core power domain. 40*91f16700Schasinglulu 41*91f16700Schasinglulu Therefore, there is a need to implement the tree in a way which facilitates this 42*91f16700Schasinglulu distinction between a leaf and non-leaf node and any associated 43*91f16700Schasinglulu optimizations. 44*91f16700Schasinglulu 45*91f16700Schasinglulu-------------- 46*91f16700Schasinglulu 47*91f16700SchasingluluDesign 48*91f16700Schasinglulu------ 49*91f16700Schasinglulu 50*91f16700SchasingluluDescribing a power domain tree 51*91f16700Schasinglulu~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 52*91f16700Schasinglulu 53*91f16700SchasingluluTo fulfill requirement 1., the existing platform APIs 54*91f16700Schasinglulu``plat_get_aff_count()`` and ``plat_get_aff_state()`` have been 55*91f16700Schasingluluremoved. A platform must define an array of unsigned chars such that: 56*91f16700Schasinglulu 57*91f16700Schasinglulu#. The first entry in the array specifies the number of power domains at the 58*91f16700Schasinglulu highest power level implemented in the platform. This caters for platforms 59*91f16700Schasinglulu where the power domain tree does not have a single root node, for example, 60*91f16700Schasinglulu the FVP has two cluster power domains at the highest level (1). 61*91f16700Schasinglulu 62*91f16700Schasinglulu#. Each subsequent entry corresponds to a power domain and contains the number 63*91f16700Schasinglulu of power domains that are its direct children. 64*91f16700Schasinglulu 65*91f16700Schasinglulu#. The size of the array minus the first entry will be equal to the number of 66*91f16700Schasinglulu non-leaf power domains. 67*91f16700Schasinglulu 68*91f16700Schasinglulu#. The value in each entry in the array is used to find the number of entries 69*91f16700Schasinglulu to consider at the next level. The sum of the values (number of children) of 70*91f16700Schasinglulu all the entries at a level specifies the number of entries in the array for 71*91f16700Schasinglulu the next level. 72*91f16700Schasinglulu 73*91f16700SchasingluluThe following example power domain topology tree will be used to describe the 74*91f16700Schasingluluabove text further. The leaf and non-leaf nodes in this tree have been numbered 75*91f16700Schasingluluseparately. 76*91f16700Schasinglulu 77*91f16700Schasinglulu:: 78*91f16700Schasinglulu 79*91f16700Schasinglulu +-+ 80*91f16700Schasinglulu |0| 81*91f16700Schasinglulu +-+ 82*91f16700Schasinglulu / \ 83*91f16700Schasinglulu / \ 84*91f16700Schasinglulu / \ 85*91f16700Schasinglulu / \ 86*91f16700Schasinglulu / \ 87*91f16700Schasinglulu / \ 88*91f16700Schasinglulu / \ 89*91f16700Schasinglulu / \ 90*91f16700Schasinglulu / \ 91*91f16700Schasinglulu / \ 92*91f16700Schasinglulu +-+ +-+ 93*91f16700Schasinglulu |1| |2| 94*91f16700Schasinglulu +-+ +-+ 95*91f16700Schasinglulu / \ / \ 96*91f16700Schasinglulu / \ / \ 97*91f16700Schasinglulu / \ / \ 98*91f16700Schasinglulu / \ / \ 99*91f16700Schasinglulu +-+ +-+ +-+ +-+ 100*91f16700Schasinglulu |3| |4| |5| |6| 101*91f16700Schasinglulu +-+ +-+ +-+ +-+ 102*91f16700Schasinglulu +---+-----+ +----+----| +----+----+ +----+-----+-----+ 103*91f16700Schasinglulu | | | | | | | | | | | | | 104*91f16700Schasinglulu | | | | | | | | | | | | | 105*91f16700Schasinglulu v v v v v v v v v v v v v 106*91f16700Schasinglulu +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +--+ 107*91f16700Schasinglulu |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |10| |11| |12| 108*91f16700Schasinglulu +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +--+ +--+ +--+ 109*91f16700Schasinglulu 110*91f16700SchasingluluThis tree is defined by the platform as the array described above as follows: 111*91f16700Schasinglulu 112*91f16700Schasinglulu.. code:: c 113*91f16700Schasinglulu 114*91f16700Schasinglulu #define PLAT_NUM_POWER_DOMAINS 20 115*91f16700Schasinglulu #define PLATFORM_CORE_COUNT 13 116*91f16700Schasinglulu #define PSCI_NUM_NON_CPU_PWR_DOMAINS \ 117*91f16700Schasinglulu (PLAT_NUM_POWER_DOMAINS - PLATFORM_CORE_COUNT) 118*91f16700Schasinglulu 119*91f16700Schasinglulu unsigned char plat_power_domain_tree_desc[] = { 1, 2, 2, 2, 3, 3, 3, 4}; 120*91f16700Schasinglulu 121*91f16700SchasingluluRemoving assumptions about MPIDRs used in a platform 122*91f16700Schasinglulu~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 123*91f16700Schasinglulu 124*91f16700SchasingluluTo fulfill requirement 2., it is assumed that the platform assigns a 125*91f16700Schasingluluunique number (core index) between ``0`` and ``PLAT_CORE_COUNT - 1`` to each core 126*91f16700Schasinglulupower domain. MPIDRs could be allocated in any manner and will not be used to 127*91f16700Schasinglulupopulate the tree. 128*91f16700Schasinglulu 129*91f16700Schasinglulu``plat_core_pos_by_mpidr(mpidr)`` will return the core index for the core 130*91f16700Schasinglulucorresponding to the MPIDR. It will return an error (-1) if an MPIDR is passed 131*91f16700Schasingluluwhich is not allocated or corresponds to an absent core. The semantics of this 132*91f16700Schasingluluplatform API have changed since it is required to validate the passed MPIDR. It 133*91f16700Schasingluluhas been made a mandatory API as a result. 134*91f16700Schasinglulu 135*91f16700SchasingluluAnother mandatory API, ``plat_my_core_pos()`` has been added to return the core 136*91f16700Schasingluluindex for the calling core. This API provides a more lightweight mechanism to get 137*91f16700Schasingluluthe index since there is no need to validate the MPIDR of the calling core. 138*91f16700Schasinglulu 139*91f16700SchasingluluThe platform should assign the core indices (as illustrated in the diagram above) 140*91f16700Schasinglulusuch that, if the core nodes are numbered from left to right, then the index 141*91f16700Schasinglulufor a core domain will be the same as the index returned by 142*91f16700Schasinglulu``plat_core_pos_by_mpidr()`` or ``plat_my_core_pos()`` for that core. This 143*91f16700Schasinglulurelationship allows the core nodes to be allocated in a separate array 144*91f16700Schasinglulu(requirement 4.) during ``psci_setup()`` in such an order that the index of the 145*91f16700Schasinglulucore in the array is the same as the return value from these APIs. 146*91f16700Schasinglulu 147*91f16700SchasingluluDealing with holes in MPIDR allocation 148*91f16700Schasinglulu^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 149*91f16700Schasinglulu 150*91f16700SchasingluluFor platforms where the number of allocated MPIDRs is equal to the number of 151*91f16700Schasinglulucore power domains, for example, Juno and FVPs, the logic to convert an MPIDR to 152*91f16700Schasinglulua core index should remain unchanged. Both Juno and FVP use a simple collision 153*91f16700Schasingluluproof hash function to do this. 154*91f16700Schasinglulu 155*91f16700SchasingluluIt is possible that on some platforms, the allocation of MPIDRs is not 156*91f16700Schasinglulucontiguous or certain cores have been disabled. This essentially means that the 157*91f16700SchasingluluMPIDRs have been sparsely allocated, that is, the size of the range of MPIDRs 158*91f16700Schasingluluused by the platform is not equal to the number of core power domains. 159*91f16700Schasinglulu 160*91f16700SchasingluluThe platform could adopt one of the following approaches to deal with this 161*91f16700Schasingluluscenario: 162*91f16700Schasinglulu 163*91f16700Schasinglulu#. Implement more complex logic to convert a valid MPIDR to a core index while 164*91f16700Schasinglulu maintaining the relationship described earlier. This means that the power 165*91f16700Schasinglulu domain tree descriptor will not describe any core power domains which are 166*91f16700Schasinglulu disabled or absent. Entries will not be allocated in the tree for these 167*91f16700Schasinglulu domains. 168*91f16700Schasinglulu 169*91f16700Schasinglulu#. Treat unallocated MPIDRs and disabled cores as absent but still describe them 170*91f16700Schasinglulu in the power domain descriptor, that is, the number of core nodes described 171*91f16700Schasinglulu is equal to the size of the range of MPIDRs allocated. This approach will 172*91f16700Schasinglulu lead to memory wastage since entries will be allocated in the tree but will 173*91f16700Schasinglulu allow use of a simpler logic to convert an MPIDR to a core index. 174*91f16700Schasinglulu 175*91f16700SchasingluluTraversing through and distinguishing between core and non-core power domains 176*91f16700Schasinglulu~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 177*91f16700Schasinglulu 178*91f16700SchasingluluTo fulfill requirement 3 and 4, separate data structures have been defined 179*91f16700Schasingluluto represent leaf and non-leaf power domain nodes in the tree. 180*91f16700Schasinglulu 181*91f16700Schasinglulu.. code:: c 182*91f16700Schasinglulu 183*91f16700Schasinglulu /******************************************************************************* 184*91f16700Schasinglulu * The following two data structures implement the power domain tree. The tree 185*91f16700Schasinglulu * is used to track the state of all the nodes i.e. power domain instances 186*91f16700Schasinglulu * described by the platform. The tree consists of nodes that describe CPU power 187*91f16700Schasinglulu * domains i.e. leaf nodes and all other power domains which are parents of a 188*91f16700Schasinglulu * CPU power domain i.e. non-leaf nodes. 189*91f16700Schasinglulu ******************************************************************************/ 190*91f16700Schasinglulu typedef struct non_cpu_pwr_domain_node { 191*91f16700Schasinglulu /* 192*91f16700Schasinglulu * Index of the first CPU power domain node level 0 which has this node 193*91f16700Schasinglulu * as its parent. 194*91f16700Schasinglulu */ 195*91f16700Schasinglulu unsigned int cpu_start_idx; 196*91f16700Schasinglulu 197*91f16700Schasinglulu /* 198*91f16700Schasinglulu * Number of CPU power domains which are siblings of the domain indexed 199*91f16700Schasinglulu * by 'cpu_start_idx' i.e. all the domains in the range 'cpu_start_idx 200*91f16700Schasinglulu * -> cpu_start_idx + ncpus' have this node as their parent. 201*91f16700Schasinglulu */ 202*91f16700Schasinglulu unsigned int ncpus; 203*91f16700Schasinglulu 204*91f16700Schasinglulu /* Index of the parent power domain node */ 205*91f16700Schasinglulu unsigned int parent_node; 206*91f16700Schasinglulu 207*91f16700Schasinglulu ----- 208*91f16700Schasinglulu } non_cpu_pd_node_t; 209*91f16700Schasinglulu 210*91f16700Schasinglulu typedef struct cpu_pwr_domain_node { 211*91f16700Schasinglulu u_register_t mpidr; 212*91f16700Schasinglulu 213*91f16700Schasinglulu /* Index of the parent power domain node */ 214*91f16700Schasinglulu unsigned int parent_node; 215*91f16700Schasinglulu 216*91f16700Schasinglulu ----- 217*91f16700Schasinglulu } cpu_pd_node_t; 218*91f16700Schasinglulu 219*91f16700SchasingluluThe power domain tree is implemented as a combination of the following data 220*91f16700Schasinglulustructures. 221*91f16700Schasinglulu 222*91f16700Schasinglulu.. code:: c 223*91f16700Schasinglulu 224*91f16700Schasinglulu non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS]; 225*91f16700Schasinglulu cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT]; 226*91f16700Schasinglulu 227*91f16700SchasingluluPopulating the power domain tree 228*91f16700Schasinglulu~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 229*91f16700Schasinglulu 230*91f16700SchasingluluThe ``populate_power_domain_tree()`` function in ``psci_setup.c`` implements the 231*91f16700Schasinglulualgorithm to parse the power domain descriptor exported by the platform to 232*91f16700Schasinglulupopulate the two arrays. It is essentially a breadth-first-search. The nodes for 233*91f16700Schasinglulueach level starting from the root are laid out one after another in the 234*91f16700Schasinglulu``psci_non_cpu_pd_nodes`` and ``psci_cpu_pd_nodes`` arrays as follows: 235*91f16700Schasinglulu 236*91f16700Schasinglulu:: 237*91f16700Schasinglulu 238*91f16700Schasinglulu psci_non_cpu_pd_nodes -> [[Level 3 nodes][Level 2 nodes][Level 1 nodes]] 239*91f16700Schasinglulu psci_cpu_pd_nodes -> [Level 0 nodes] 240*91f16700Schasinglulu 241*91f16700SchasingluluFor the example power domain tree illustrated above, the ``psci_cpu_pd_nodes`` 242*91f16700Schasingluluwill be populated as follows. The value in each entry is the index of the parent 243*91f16700Schasinglulunode. Other fields have been ignored for simplicity. 244*91f16700Schasinglulu 245*91f16700Schasinglulu:: 246*91f16700Schasinglulu 247*91f16700Schasinglulu +-------------+ ^ 248*91f16700Schasinglulu CPU0 | 3 | | 249*91f16700Schasinglulu +-------------+ | 250*91f16700Schasinglulu CPU1 | 3 | | 251*91f16700Schasinglulu +-------------+ | 252*91f16700Schasinglulu CPU2 | 3 | | 253*91f16700Schasinglulu +-------------+ | 254*91f16700Schasinglulu CPU3 | 4 | | 255*91f16700Schasinglulu +-------------+ | 256*91f16700Schasinglulu CPU4 | 4 | | 257*91f16700Schasinglulu +-------------+ | 258*91f16700Schasinglulu CPU5 | 4 | | PLATFORM_CORE_COUNT 259*91f16700Schasinglulu +-------------+ | 260*91f16700Schasinglulu CPU6 | 5 | | 261*91f16700Schasinglulu +-------------+ | 262*91f16700Schasinglulu CPU7 | 5 | | 263*91f16700Schasinglulu +-------------+ | 264*91f16700Schasinglulu CPU8 | 5 | | 265*91f16700Schasinglulu +-------------+ | 266*91f16700Schasinglulu CPU9 | 6 | | 267*91f16700Schasinglulu +-------------+ | 268*91f16700Schasinglulu CPU10 | 6 | | 269*91f16700Schasinglulu +-------------+ | 270*91f16700Schasinglulu CPU11 | 6 | | 271*91f16700Schasinglulu +-------------+ | 272*91f16700Schasinglulu CPU12 | 6 | v 273*91f16700Schasinglulu +-------------+ 274*91f16700Schasinglulu 275*91f16700SchasingluluThe ``psci_non_cpu_pd_nodes`` array will be populated as follows. The value in 276*91f16700Schasinglulueach entry is the index of the parent node. 277*91f16700Schasinglulu 278*91f16700Schasinglulu:: 279*91f16700Schasinglulu 280*91f16700Schasinglulu +-------------+ ^ 281*91f16700Schasinglulu PD0 | -1 | | 282*91f16700Schasinglulu +-------------+ | 283*91f16700Schasinglulu PD1 | 0 | | 284*91f16700Schasinglulu +-------------+ | 285*91f16700Schasinglulu PD2 | 0 | | 286*91f16700Schasinglulu +-------------+ | 287*91f16700Schasinglulu PD3 | 1 | | PLAT_NUM_POWER_DOMAINS - 288*91f16700Schasinglulu +-------------+ | PLATFORM_CORE_COUNT 289*91f16700Schasinglulu PD4 | 1 | | 290*91f16700Schasinglulu +-------------+ | 291*91f16700Schasinglulu PD5 | 2 | | 292*91f16700Schasinglulu +-------------+ | 293*91f16700Schasinglulu PD6 | 2 | | 294*91f16700Schasinglulu +-------------+ v 295*91f16700Schasinglulu 296*91f16700SchasingluluEach core can find its node in the ``psci_cpu_pd_nodes`` array using the 297*91f16700Schasinglulu``plat_my_core_pos()`` function. When a core is turned on, the normal world 298*91f16700Schasingluluprovides an MPIDR. The ``plat_core_pos_by_mpidr()`` function is used to validate 299*91f16700Schasingluluthe MPIDR before using it to find the corresponding core node. The non-core power 300*91f16700Schasingluludomain nodes do not need to be identified. 301*91f16700Schasinglulu 302*91f16700Schasinglulu-------------- 303*91f16700Schasinglulu 304*91f16700Schasinglulu*Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.* 305