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