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