blob: f65ad9d37056f86d0d3faa654a01470931666de7 [file] [log] [blame]
Soby Mathew49473e42015-06-10 13:49:59 +01001/*
2 * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of ARM nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <arch_helpers.h>
32#include <assert.h>
33#include <platform.h>
34#include <platform_def.h>
35#include <psci.h>
36
37/* The power domain tree descriptor */
38static unsigned char power_domain_tree_desc
39 [PLATFORM_NUM_AFFS - PLATFORM_CORE_COUNT + 1];
40
41/*******************************************************************************
42 * Simple routine to set the id of an affinity instance at a given level
43 * in the mpidr. The assumption is that the affinity level and the power
44 * domain level are the same.
45 ******************************************************************************/
46unsigned long mpidr_set_aff_inst(unsigned long mpidr,
47 unsigned char aff_inst,
48 int aff_lvl)
49{
50 unsigned long aff_shift;
51
52 assert(aff_lvl <= MPIDR_AFFLVL3);
53
54 /*
55 * Decide the number of bits to shift by depending upon
56 * the power level
57 */
58 aff_shift = get_afflvl_shift(aff_lvl);
59
60 /* Clear the existing power instance & set the new one*/
61 mpidr &= ~((unsigned long)MPIDR_AFFLVL_MASK << aff_shift);
62 mpidr |= (unsigned long)aff_inst << aff_shift;
63
64 return mpidr;
65}
66
67/******************************************************************************
68 * This function uses insertion sort to sort a given list of mpidr's in the
69 * ascending order of the index returned by platform_get_core_pos.
70 *****************************************************************************/
71void sort_mpidr_by_cpu_idx(unsigned int aff_count, unsigned long mpidr_list[])
72{
73 int i, j;
74 unsigned long temp_mpidr;
75
76 for (i = 1; i < aff_count; i++) {
77 temp_mpidr = mpidr_list[i];
78
79 for (j = i;
80 j > 0 &&
81 platform_get_core_pos(mpidr_list[j-1]) >
82 platform_get_core_pos(temp_mpidr);
83 j--)
84 mpidr_list[j] = mpidr_list[j-1];
85
86 mpidr_list[j] = temp_mpidr;
87 }
88}
89
90/*******************************************************************************
91 * The compatibility routine to construct the power domain tree description.
92 * The assumption made is that the power domains correspond to affinity
93 * instances on the platform. This routine's aim is to traverse to the target
94 * affinity level and populate the number of siblings at that level in
95 * 'power_domain_tree_desc' array. It uses the current affinity level to keep
96 * track of how many levels from the root of the tree have been traversed.
97 * If the current affinity level != target affinity level, then the platform
98 * is asked to return the number of children that each affinity instance has
99 * at the current affinity level. Traversal is then done for each child at the
100 * next lower level i.e. current affinity level - 1.
101 *
102 * The power domain description needs to be constructed in such a way that
103 * affinity instances containing CPUs with lower cpu indices need to be
104 * described first. Hence when traversing the power domain levels, the list
105 * of mpidrs at that power domain level is sorted in the ascending order of CPU
106 * indices before the lower levels are recursively described.
107 *
108 * CAUTION: This routine assumes that affinity instance ids are allocated in a
109 * monotonically increasing manner at each affinity level in a mpidr starting
110 * from 0. If the platform breaks this assumption then this code will have to
111 * be reworked accordingly.
112 ******************************************************************************/
113static unsigned int init_pwr_domain_tree_desc(unsigned long mpidr,
114 unsigned int affmap_idx,
115 int cur_afflvl,
116 int tgt_afflvl)
117{
118 unsigned int ctr, aff_count;
119
120 /*
121 * Temporary list to hold the MPIDR list at a particular power domain
122 * level so as to sort them.
123 */
124 unsigned long mpidr_list[PLATFORM_CORE_COUNT];
125
126 assert(cur_afflvl >= tgt_afflvl);
127
128 /*
129 * Find the number of siblings at the current power level &
130 * assert if there are none 'cause then we have been invoked with
131 * an invalid mpidr.
132 */
133 aff_count = plat_get_aff_count(cur_afflvl, mpidr);
134 assert(aff_count);
135
136 if (tgt_afflvl < cur_afflvl) {
137 for (ctr = 0; ctr < aff_count; ctr++) {
138 mpidr_list[ctr] = mpidr_set_aff_inst(mpidr, ctr,
139 cur_afflvl);
140 }
141
142 /* Need to sort mpidr list according to CPU index */
143 sort_mpidr_by_cpu_idx(aff_count, mpidr_list);
144 for (ctr = 0; ctr < aff_count; ctr++) {
145 affmap_idx = init_pwr_domain_tree_desc(mpidr_list[ctr],
146 affmap_idx,
147 cur_afflvl - 1,
148 tgt_afflvl);
149 }
150 } else {
151 power_domain_tree_desc[affmap_idx++] = aff_count;
152 }
153 return affmap_idx;
154}
155
156
157/*******************************************************************************
158 * This function constructs the topology tree description at runtime
159 * and returns it. The assumption made is that the power domains correspond
160 * to affinity instances on the platform.
161 ******************************************************************************/
162const unsigned char *plat_get_power_domain_tree_desc(void)
163{
164 int afflvl, affmap_idx;
165
166 /*
167 * We assume that the platform allocates affinity instance ids from
168 * 0 onwards at each affinity level in the mpidr. FIRST_MPIDR = 0.0.0.0
169 */
170 affmap_idx = 0;
171 for (afflvl = PLATFORM_MAX_AFFLVL; afflvl >= MPIDR_AFFLVL0; afflvl--) {
172 affmap_idx = init_pwr_domain_tree_desc(FIRST_MPIDR,
173 affmap_idx,
174 PLATFORM_MAX_AFFLVL,
175 afflvl);
176 }
177
178 assert(affmap_idx == (PLATFORM_NUM_AFFS - PLATFORM_CORE_COUNT + 1));
179
180 return power_domain_tree_desc;
181}
182
183/******************************************************************************
184 * The compatibility helper function for plat_core_pos_by_mpidr(). It
185 * validates the 'mpidr' by making sure that it is within acceptable bounds
186 * for the platform and queries the platform layer whether the CPU specified
187 * by the mpidr is present or not. If present, it returns the index of the
188 * core corresponding to the 'mpidr'. Else it returns -1.
189 *****************************************************************************/
190int plat_core_pos_by_mpidr(u_register_t mpidr)
191{
192 unsigned long shift, aff_inst;
193 int i;
194
195 /* Ignore the Reserved bits and U bit in MPIDR */
196 mpidr &= MPIDR_AFFINITY_MASK;
197
198 /*
199 * Check if any affinity field higher than
200 * the PLATFORM_MAX_AFFLVL is set.
201 */
202 shift = get_afflvl_shift(PLATFORM_MAX_AFFLVL + 1);
203 if (mpidr >> shift)
204 return -1;
205
206 for (i = PLATFORM_MAX_AFFLVL; i >= 0; i--) {
207 shift = get_afflvl_shift(i);
208 aff_inst = ((mpidr &
209 ((unsigned long)MPIDR_AFFLVL_MASK << shift)) >> shift);
210 if (aff_inst >= plat_get_aff_count(i, mpidr))
211 return -1;
212 }
213
214 if (plat_get_aff_state(0, mpidr) == PSCI_AFF_ABSENT)
215 return -1;
216
217 return platform_get_core_pos(mpidr);
218}