Paul Beesley | fc9ee36 | 2019-03-07 15:47:15 +0000 | [diff] [blame] | 1 | PSCI Power Domain Tree Structure |
| 2 | ================================ |
Douglas Raillard | d7c21b7 | 2017-06-28 15:23:03 +0100 | [diff] [blame] | 3 | |
Douglas Raillard | d7c21b7 | 2017-06-28 15:23:03 +0100 | [diff] [blame] | 4 | Requirements |
| 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 | |
| 47 | Design |
| 48 | ------ |
| 49 | |
| 50 | Describing a power domain tree |
| 51 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 52 | |
| 53 | To fulfill requirement 1., the existing platform APIs |
| 54 | ``plat_get_aff_count()`` and ``plat_get_aff_state()`` have been |
| 55 | removed. 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 | |
| 73 | The following example power domain topology tree will be used to describe the |
| 74 | above text further. The leaf and non-leaf nodes in this tree have been numbered |
| 75 | separately. |
| 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 | |
| 110 | This tree is defined by the platform as the array described above as follows: |
| 111 | |
Paul Beesley | 493e349 | 2019-03-13 15:11:04 +0000 | [diff] [blame] | 112 | .. code:: c |
Douglas Raillard | d7c21b7 | 2017-06-28 15:23:03 +0100 | [diff] [blame] | 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 | |
| 121 | Removing assumptions about MPIDRs used in a platform |
| 122 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 123 | |
| 124 | To fulfill requirement 2., it is assumed that the platform assigns a |
| 125 | unique number (core index) between ``0`` and ``PLAT_CORE_COUNT - 1`` to each core |
| 126 | power domain. MPIDRs could be allocated in any manner and will not be used to |
| 127 | populate the tree. |
| 128 | |
| 129 | ``plat_core_pos_by_mpidr(mpidr)`` will return the core index for the core |
| 130 | corresponding to the MPIDR. It will return an error (-1) if an MPIDR is passed |
| 131 | which is not allocated or corresponds to an absent core. The semantics of this |
| 132 | platform API have changed since it is required to validate the passed MPIDR. It |
| 133 | has been made a mandatory API as a result. |
| 134 | |
| 135 | Another mandatory API, ``plat_my_core_pos()`` has been added to return the core |
| 136 | index for the calling core. This API provides a more lightweight mechanism to get |
| 137 | the index since there is no need to validate the MPIDR of the calling core. |
| 138 | |
| 139 | The platform should assign the core indices (as illustrated in the diagram above) |
| 140 | such that, if the core nodes are numbered from left to right, then the index |
| 141 | for 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 |
| 143 | relationship 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 |
| 145 | core in the array is the same as the return value from these APIs. |
| 146 | |
| 147 | Dealing with holes in MPIDR allocation |
| 148 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 149 | |
| 150 | For platforms where the number of allocated MPIDRs is equal to the number of |
| 151 | core power domains, for example, Juno and FVPs, the logic to convert an MPIDR to |
| 152 | a core index should remain unchanged. Both Juno and FVP use a simple collision |
| 153 | proof hash function to do this. |
| 154 | |
| 155 | It is possible that on some platforms, the allocation of MPIDRs is not |
| 156 | contiguous or certain cores have been disabled. This essentially means that the |
| 157 | MPIDRs have been sparsely allocated, that is, the size of the range of MPIDRs |
| 158 | used by the platform is not equal to the number of core power domains. |
| 159 | |
| 160 | The platform could adopt one of the following approaches to deal with this |
| 161 | scenario: |
| 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 | |
| 175 | Traversing through and distinguishing between core and non-core power domains |
| 176 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 177 | |
| 178 | To fulfill requirement 3 and 4, separate data structures have been defined |
| 179 | to 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 | |
| 219 | The power domain tree is implemented as a combination of the following data |
| 220 | structures. |
| 221 | |
Paul Beesley | 493e349 | 2019-03-13 15:11:04 +0000 | [diff] [blame] | 222 | .. code:: c |
Douglas Raillard | d7c21b7 | 2017-06-28 15:23:03 +0100 | [diff] [blame] | 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 | |
| 227 | Populating the power domain tree |
| 228 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 229 | |
| 230 | The ``populate_power_domain_tree()`` function in ``psci_setup.c`` implements the |
| 231 | algorithm to parse the power domain descriptor exported by the platform to |
| 232 | populate the two arrays. It is essentially a breadth-first-search. The nodes for |
| 233 | each 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 | |
| 241 | For the example power domain tree illustrated above, the ``psci_cpu_pd_nodes`` |
| 242 | will be populated as follows. The value in each entry is the index of the parent |
| 243 | node. 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 | |
| 275 | The ``psci_non_cpu_pd_nodes`` array will be populated as follows. The value in |
| 276 | each 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 | |
| 296 | Each 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 |
| 298 | provides an MPIDR. The ``plat_core_pos_by_mpidr()`` function is used to validate |
| 299 | the MPIDR before using it to find the corresponding core node. The non-core power |
| 300 | domain nodes do not need to be identified. |
| 301 | |
| 302 | -------------- |
| 303 | |
Dan Handley | 610e7e1 | 2018-03-01 18:44:00 +0000 | [diff] [blame] | 304 | *Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.* |