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