Skip to content

Commit 82dcc03

Browse files
soby-mathewachingupta
authored andcommitted
PSCI: Introduce new platform interface to describe topology
This patch removes the assumption in the current PSCI implementation that MPIDR based affinity levels map directly to levels in a power domain tree. This enables PSCI generic code to support complex power domain topologies as envisaged by PSCIv1.0 specification. The platform interface for querying the power domain topology has been changed such that: 1. The generic PSCI code does not generate MPIDRs and use them to query the platform about the number of power domains at a particular power level. The platform now provides a description of the power domain tree on the SoC through a data structure. The existing platform APIs to provide the same information have been removed. 2. The linear indices returned by plat_core_pos_by_mpidr() and plat_my_core_pos() are used to retrieve core power domain nodes from the power domain tree. Power domains above the core level are accessed using a 'parent' field in the tree node descriptors. The platform describes the power domain tree in an array of 'unsigned char's. The first entry in the array specifies the number of power domains at the highest power level implemented in the system. Each susbsequent entry corresponds to a power domain and contains the number of power domains that are its direct children. This array is exported to the generic PSCI implementation via the new `plat_get_power_domain_tree_desc()` platform API. The PSCI generic code uses this array to populate its internal power domain tree using the Breadth First Search like algorithm. The tree is split into two arrays: 1. An array that contains all the core power domain nodes 2. An array that contains all the other power domain nodes A separate array for core nodes allows certain core specific optimisations to be implemented e.g. remove the bakery lock, re-use per-cpu data framework for storing some information. Entries in the core power domain array are allocated such that the array index of the domain is equal to the linear index returned by plat_core_pos_by_mpidr() and plat_my_core_pos() for the MPIDR corresponding to that domain. This relationship is key to be able to use an MPIDR to find the corresponding core power domain node, traverse to higher power domain nodes and index into arrays that contain core specific information. An introductory document has been added to briefly describe the new interface. Change-Id: I4b444719e8e927ba391cae48a23558308447da13
1 parent 12d0d00 commit 82dcc03

File tree

11 files changed

+758
-661
lines changed

11 files changed

+758
-661
lines changed

docs/psci-pd-tree.md

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

include/bl31/services/psci1.0/psci.h

+20-11
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@
4343
#define PSCI_NUM_PWR_DOMAINS (2 * PLATFORM_CORE_COUNT)
4444
#endif
4545

46+
#define PSCI_NUM_NON_CPU_PWR_DOMAINS (PSCI_NUM_PWR_DOMAINS - \
47+
PLATFORM_CORE_COUNT)
48+
49+
/* This is the power level corresponding to a CPU */
50+
#define PSCI_CPU_PWR_LVL 0
51+
52+
/*
53+
* The maximum power level supported by PSCI. Since PSCI CPU_SUSPEND
54+
* uses the old power_state parameter format which has 2 bits to specify the
55+
* power level, this constant is defined to be 3.
56+
*/
57+
#define PSCI_MAX_PWR_LVL 3
58+
4659
/*******************************************************************************
4760
* Defines for runtime services func ids
4861
******************************************************************************/
@@ -137,16 +150,11 @@
137150
#define PSCI_E_NOT_PRESENT -7
138151
#define PSCI_E_DISABLED -8
139152

153+
#define PSCI_INVALID_MPIDR ~(0ULL)
154+
140155
/*******************************************************************************
141-
* PSCI power domain state related constants. A power domain instance could
142-
* be present or absent physically to cater for asymmetric topologies. If
143-
* present then it could be in one of the 4 further defined states.
156+
* PSCI power domain state related constants.
144157
******************************************************************************/
145-
#define PSCI_STATE_SHIFT 1
146-
#define PSCI_STATE_MASK 0xff
147-
148-
#define PSCI_PWR_DOMAIN_ABSENT 0x0
149-
#define PSCI_PWR_DOMAIN_PRESENT 0x1
150158
#define PSCI_STATE_ON 0x0
151159
#define PSCI_STATE_OFF 0x1
152160
#define PSCI_STATE_ON_PENDING 0x2
@@ -170,9 +178,10 @@
170178
* this information will not reside on a cache line shared with another cpu.
171179
******************************************************************************/
172180
typedef struct psci_cpu_data {
173-
uint32_t power_state;
181+
uint32_t power_state; /* The power state from CPU_SUSPEND */
182+
unsigned char psci_state; /* The state of this CPU as seen by PSCI */
174183
#if !USE_COHERENT_MEM
175-
bakery_info_t pcpu_bakery_info[PSCI_NUM_PWR_DOMAINS];
184+
bakery_info_t pcpu_bakery_info[PSCI_NUM_NON_CPU_PWR_DOMAINS];
176185
#endif
177186
} psci_cpu_data_t;
178187

@@ -230,7 +239,7 @@ void __dead2 psci_power_down_wfi(void);
230239
void psci_cpu_on_finish_entry(void);
231240
void psci_cpu_suspend_finish_entry(void);
232241
void psci_register_spd_pm_hook(const spd_pm_ops_t *);
233-
int psci_get_suspend_stateid_by_mpidr(unsigned long);
242+
int psci_get_suspend_stateid_by_idx(unsigned long);
234243
int psci_get_suspend_stateid(void);
235244
int psci_get_suspend_pwrlvl(void);
236245

include/plat/common/psci1.0/platform.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,7 @@ struct entry_point_info *bl31_plat_get_next_image_ep_info(uint32_t type);
183183
* Mandatory PSCI functions (BL3-1)
184184
******************************************************************************/
185185
int platform_setup_pm(const struct plat_pm_ops **);
186-
unsigned int plat_get_pwr_domain_count(unsigned int, unsigned long);
187-
unsigned int plat_get_pwr_domain_state(unsigned int, unsigned long);
186+
const unsigned char *plat_get_power_domain_tree_desc(void);
188187

189188
/*******************************************************************************
190189
* Optional BL3-1 functions (may be overridden)

0 commit comments

Comments
 (0)