Dan Handley | 610e7e1 | 2018-03-01 18:44:00 +0000 | [diff] [blame] | 1 | PSCI Library Integration guide for Armv8-A AArch32 systems |
Douglas Raillard | d7c21b7 | 2017-06-28 15:23:03 +0100 | [diff] [blame] | 2 | ========================================================== |
| 3 | |
| 4 | |
| 5 | .. section-numbering:: |
| 6 | :suffix: . |
| 7 | |
| 8 | .. contents:: |
| 9 | |
| 10 | -------------- |
| 11 | |
| 12 | Requirements |
| 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 | |
| 55 | Design |
| 56 | ------ |
| 57 | |
| 58 | Describing a power domain tree |
| 59 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 60 | |
| 61 | To fulfill requirement 1., the existing platform APIs |
| 62 | ``plat_get_aff_count()`` and ``plat_get_aff_state()`` have been |
| 63 | removed. 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 | |
| 81 | The following example power domain topology tree will be used to describe the |
| 82 | above text further. The leaf and non-leaf nodes in this tree have been numbered |
| 83 | separately. |
| 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 | |
| 118 | This 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 | |
| 129 | Removing assumptions about MPIDRs used in a platform |
| 130 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 131 | |
| 132 | To fulfill requirement 2., it is assumed that the platform assigns a |
| 133 | unique number (core index) between ``0`` and ``PLAT_CORE_COUNT - 1`` to each core |
| 134 | power domain. MPIDRs could be allocated in any manner and will not be used to |
| 135 | populate the tree. |
| 136 | |
| 137 | ``plat_core_pos_by_mpidr(mpidr)`` will return the core index for the core |
| 138 | corresponding to the MPIDR. It will return an error (-1) if an MPIDR is passed |
| 139 | which is not allocated or corresponds to an absent core. The semantics of this |
| 140 | platform API have changed since it is required to validate the passed MPIDR. It |
| 141 | has been made a mandatory API as a result. |
| 142 | |
| 143 | Another mandatory API, ``plat_my_core_pos()`` has been added to return the core |
| 144 | index for the calling core. This API provides a more lightweight mechanism to get |
| 145 | the index since there is no need to validate the MPIDR of the calling core. |
| 146 | |
| 147 | The platform should assign the core indices (as illustrated in the diagram above) |
| 148 | such that, if the core nodes are numbered from left to right, then the index |
| 149 | for 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 |
| 151 | relationship 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 |
| 153 | core in the array is the same as the return value from these APIs. |
| 154 | |
| 155 | Dealing with holes in MPIDR allocation |
| 156 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 157 | |
| 158 | For platforms where the number of allocated MPIDRs is equal to the number of |
| 159 | core power domains, for example, Juno and FVPs, the logic to convert an MPIDR to |
| 160 | a core index should remain unchanged. Both Juno and FVP use a simple collision |
| 161 | proof hash function to do this. |
| 162 | |
| 163 | It is possible that on some platforms, the allocation of MPIDRs is not |
| 164 | contiguous or certain cores have been disabled. This essentially means that the |
| 165 | MPIDRs have been sparsely allocated, that is, the size of the range of MPIDRs |
| 166 | used by the platform is not equal to the number of core power domains. |
| 167 | |
| 168 | The platform could adopt one of the following approaches to deal with this |
| 169 | scenario: |
| 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 | |
| 183 | Traversing through and distinguishing between core and non-core power domains |
| 184 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 185 | |
| 186 | To fulfill requirement 3 and 4, separate data structures have been defined |
| 187 | to 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 | |
| 227 | The power domain tree is implemented as a combination of the following data |
| 228 | structures. |
| 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 | |
| 235 | Populating the power domain tree |
| 236 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 237 | |
| 238 | The ``populate_power_domain_tree()`` function in ``psci_setup.c`` implements the |
| 239 | algorithm to parse the power domain descriptor exported by the platform to |
| 240 | populate the two arrays. It is essentially a breadth-first-search. The nodes for |
| 241 | each 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 | |
| 249 | For the example power domain tree illustrated above, the ``psci_cpu_pd_nodes`` |
| 250 | will be populated as follows. The value in each entry is the index of the parent |
| 251 | node. 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 | |
| 283 | The ``psci_non_cpu_pd_nodes`` array will be populated as follows. The value in |
| 284 | each 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 | |
| 304 | Each 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 |
| 306 | provides an MPIDR. The ``plat_core_pos_by_mpidr()`` function is used to validate |
| 307 | the MPIDR before using it to find the corresponding core node. The non-core power |
| 308 | domain nodes do not need to be identified. |
| 309 | |
| 310 | -------------- |
| 311 | |
Dan Handley | 610e7e1 | 2018-03-01 18:44:00 +0000 | [diff] [blame] | 312 | *Copyright (c) 2017-2018, Arm Limited and Contributors. All rights reserved.* |