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