blob: 6ae686d8dba1a5407bbfebcc8c49896a7377a42a [file] [log] [blame] [view]
Soby Mathew82dcc032015-04-08 17:42:06 +01001------------
2Requirements
3------------
4
51. 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
142. 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
233. 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
344. 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------
45Design
46------
47
48### Describing a power domain tree
49
50To fulfill requirement 1., the existing platform APIs
51`plat_get_aff_count()` and `plat_get_aff_state()` have been
52removed. A platform must define an array of unsigned chars such that:
53
541. 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
592. Each subsequent entry corresponds to a power domain and contains the number
60 of power domains that are its direct children.
61
623. The size of the array minus the first entry will be equal to the number of
63 non-leaf power domains.
64
654. 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
70The following example power domain topology tree will be used to describe the
71above text further. The leaf and non-leaf nodes in this tree have been numbered
72separately.
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
108This 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
121To fulfill requirement 2., it is assumed that the platform assigns a
122unique number (core index) between `0` and `PLAT_CORE_COUNT - 1` to each core
123power domain. MPIDRs could be allocated in any manner and will not be used to
124populate the tree.
125
126`plat_core_pos_by_mpidr(mpidr)` will return the core index for the core
127corresponding to the MPIDR. It will return an error (-1) if an MPIDR is passed
128which is not allocated or corresponds to an absent core. The semantics of this
129platform API have changed since it is required to validate the passed MPIDR. It
130has been made a mandatory API as a result.
131
132Another mandatory API, `plat_my_core_pos()` has been added to return the core
133index for the calling core. This API provides a more lightweight mechanism to get
134the index since there is no need to validate the MPIDR of the calling core.
135
136The platform should assign the core indices (as illustrated in the diagram above)
137such that, if the core nodes are numbered from left to right, then the index
138for 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
140relationship 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
142core in the array is the same as the return value from these APIs.
143
144#### Dealing with holes in MPIDR allocation
145
146For platforms where the number of allocated MPIDRs is equal to the number of
147core power domains, for example, Juno and FVPs, the logic to convert an MPIDR to
148a core index should remain unchanged. Both Juno and FVP use a simple collision
149proof hash function to do this.
150
151It is possible that on some platforms, the allocation of MPIDRs is not
152contiguous or certain cores have been disabled. This essentially means that the
153MPIDRs have been sparsely allocated, that is, the size of the range of MPIDRs
154used by the platform is not equal to the number of core power domains.
155
156The platform could adopt one of the following approaches to deal with this
157scenario:
158
1591. 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
1652. 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
174To fulfill requirement 3 and 4, separate data structures have been defined
175to 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 ******************************************************************************/
185typedef 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
205typedef 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
215The power domain tree is implemented as a combination of the following data
216structures.
217
218```
219non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS];
220cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT];
221```
222
223### Populating the power domain tree
224
225The `populate_power_domain_tree()` function in `psci_setup.c` implements the
226algorithm to parse the power domain descriptor exported by the platform to
227populate the two arrays. It is essentially a breadth-first-search. The nodes for
228each 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```
232psci_non_cpu_pd_nodes -> [[Level 3 nodes][Level 2 nodes][Level 1 nodes]]
233psci_cpu_pd_nodes -> [Level 0 nodes]
234```
235
236For the example power domain tree illustrated above, the `psci_cpu_pd_nodes`
237will be populated as follows. The value in each entry is the index of the parent
238node. 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
270The `psci_non_cpu_pd_nodes` array will be populated as follows. The value in
271each 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
291Each 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
293provides an MPIDR. The `plat_core_pos_by_mpidr()` function is used to validate
294the MPIDR before using it to find the corresponding core node. The non-core power
295domain nodes do not need to be identified.