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