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