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