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