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