blob: 49f7daf4c817e1182c8470a995c15c491ec4cfab [file] [log] [blame]
Achin Gupta4f6ad662013-10-25 09:08:21 +01001/*
Dan Handleye83b0ca2014-01-14 18:17:09 +00002 * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
Achin Gupta4f6ad662013-10-25 09:08:21 +01003 *
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
Achin Gupta4f6ad662013-10-25 09:08:21 +010031#include <assert.h>
Dan Handleyed6ff952014-05-14 17:44:19 +010032#include <platform_def.h>
Achin Gupta4f6ad662013-10-25 09:08:21 +010033/* TODO: Reusing psci error codes & state information. Get our own! */
34#include <psci.h>
Dan Handley4d2e49d2014-04-11 11:52:12 +010035#include "drivers/pwrc/fvp_pwrc.h"
Achin Gupta4f6ad662013-10-25 09:08:21 +010036
37/* We treat '255' as an invalid affinity instance */
38#define AFFINST_INVAL 0xff
39
40/*******************************************************************************
41 * We support 3 flavours of the FVP: Foundation, Base AEM & Base Cortex. Each
42 * flavour has a different topology. The common bit is that there can be a max.
43 * of 2 clusters (affinity 1) and 4 cpus (affinity 0) per cluster. So we define
44 * a tree like data structure which caters to these maximum bounds. It simply
45 * marks the absent affinity level instances as PSCI_AFF_ABSENT e.g. there is no
46 * cluster 1 on the Foundation FVP. The 'data' field is currently unused.
47 ******************************************************************************/
Dan Handleye2712bc2014-04-10 15:37:22 +010048typedef struct affinity_info {
Achin Gupta4f6ad662013-10-25 09:08:21 +010049 unsigned char sibling;
50 unsigned char child;
51 unsigned char state;
52 unsigned int data;
Dan Handleye2712bc2014-04-10 15:37:22 +010053} affinity_info_t;
Achin Gupta4f6ad662013-10-25 09:08:21 +010054
55/*******************************************************************************
56 * The following two data structures store the topology tree for the fvp. There
57 * is a separate array for each affinity level i.e. cpus and clusters. The child
58 * and sibling references allow traversal inside and in between the two arrays.
59 ******************************************************************************/
Dan Handleye2712bc2014-04-10 15:37:22 +010060static affinity_info_t fvp_aff1_topology_map[PLATFORM_CLUSTER_COUNT];
61static affinity_info_t fvp_aff0_topology_map[PLATFORM_CORE_COUNT];
Achin Gupta4f6ad662013-10-25 09:08:21 +010062
63/* Simple global variable to safeguard us from stupidity */
64static unsigned int topology_setup_done;
65
66/*******************************************************************************
67 * This function implements a part of the critical interface between the psci
68 * generic layer and the platform to allow the former to detect the platform
69 * topology. psci queries the platform to determine how many affinity instances
70 * are present at a particular level for a given mpidr e.g. consider a dual
71 * cluster platform where each cluster has 4 cpus. A call to this function with
72 * (0, 0x100) will return the number of cpus implemented under cluster 1 i.e. 4.
73 * Similarly a call with (1, 0x100) will return 2 i.e. the number of clusters.
74 * This is 'cause we are effectively asking how many affinity level 1 instances
75 * are implemented under affinity level 2 instance 0.
76 ******************************************************************************/
77unsigned int plat_get_aff_count(unsigned int aff_lvl,
78 unsigned long mpidr)
79{
80 unsigned int aff_count = 1, ctr;
81 unsigned char parent_aff_id;
82
83 assert(topology_setup_done == 1);
84
85 switch (aff_lvl) {
86 case 3:
87 case 2:
88 /*
89 * Assert if the parent affinity instance is not 0.
90 * This also takes care of level 3 in an obfuscated way
91 */
92 parent_aff_id = (mpidr >> MPIDR_AFF3_SHIFT) & MPIDR_AFFLVL_MASK;
93 assert(parent_aff_id == 0);
94
95 /*
96 * Report that we implement a single instance of
97 * affinity levels 2 & 3 which are AFF_ABSENT
98 */
99 break;
100 case 1:
101 /* Assert if the parent affinity instance is not 0. */
102 parent_aff_id = (mpidr >> MPIDR_AFF2_SHIFT) & MPIDR_AFFLVL_MASK;
103 assert(parent_aff_id == 0);
104
105 /* Fetch the starting index in the aff1 array */
106 for (ctr = 0;
107 fvp_aff1_topology_map[ctr].sibling != AFFINST_INVAL;
108 ctr = fvp_aff1_topology_map[ctr].sibling) {
109 aff_count++;
110 }
111
112 break;
113 case 0:
114 /* Assert if the cluster id is anything apart from 0 or 1 */
115 parent_aff_id = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK;
116 assert(parent_aff_id < PLATFORM_CLUSTER_COUNT);
117
118 /* Fetch the starting index in the aff0 array */
119 for (ctr = fvp_aff1_topology_map[parent_aff_id].child;
120 fvp_aff0_topology_map[ctr].sibling != AFFINST_INVAL;
121 ctr = fvp_aff0_topology_map[ctr].sibling) {
122 aff_count++;
123 }
124
125 break;
126 default:
127 assert(0);
128 }
129
130 return aff_count;
131}
132
133/*******************************************************************************
134 * This function implements a part of the critical interface between the psci
135 * generic layer and the platform to allow the former to detect the state of a
136 * affinity instance in the platform topology. psci queries the platform to
137 * determine whether an affinity instance is present or absent. This caters for
138 * topologies where an intermediate affinity level instance is missing e.g.
139 * consider a platform which implements a single cluster with 4 cpus and there
140 * is another cpu sitting directly on the interconnect along with the cluster.
141 * The mpidrs of the cluster would range from 0x0-0x3. The mpidr of the single
142 * cpu would be 0x100 to highlight that it does not belong to cluster 0. Cluster
143 * 1 is however missing but needs to be accounted to reach this single cpu in
144 * the topology tree. Hence it will be marked as PSCI_AFF_ABSENT. This is not
145 * applicable to the FVP but depicted as an example.
146 ******************************************************************************/
147unsigned int plat_get_aff_state(unsigned int aff_lvl,
148 unsigned long mpidr)
149{
150 unsigned int aff_state = PSCI_AFF_ABSENT, idx;
151 idx = (mpidr >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK;
152
153 assert(topology_setup_done == 1);
154
155 switch (aff_lvl) {
156 case 3:
157 case 2:
158 /* Report affinity levels 2 & 3 as absent */
159 break;
160 case 1:
161 aff_state = fvp_aff1_topology_map[idx].state;
162 break;
163 case 0:
164 /*
165 * First get start index of the aff0 in its array & then add
166 * to it the affinity id that we want the state of
167 */
168 idx = fvp_aff1_topology_map[idx].child;
169 idx += (mpidr >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK;
170 aff_state = fvp_aff0_topology_map[idx].state;
171 break;
172 default:
173 assert(0);
174 }
175
176 return aff_state;
177}
178
179/*******************************************************************************
180 * Handy optimization to prevent the psci implementation from traversing through
181 * affinity levels which are not present while detecting the platform topology.
182 ******************************************************************************/
Juan Castillo2d552402014-06-13 17:05:10 +0100183int plat_get_max_afflvl(void)
Achin Gupta4f6ad662013-10-25 09:08:21 +0100184{
185 return MPIDR_AFFLVL1;
186}
187
188/*******************************************************************************
189 * This function populates the FVP specific topology information depending upon
190 * the FVP flavour its running on. We construct all the mpidrs we can handle
191 * and rely on the PWRC.PSYSR to flag absent cpus when their status is queried.
192 ******************************************************************************/
Juan Castillo2d552402014-06-13 17:05:10 +0100193int fvp_setup_topology(void)
Achin Gupta4f6ad662013-10-25 09:08:21 +0100194{
195 unsigned char aff0, aff1, aff_state, aff0_offset = 0;
196 unsigned long mpidr;
197
198 topology_setup_done = 0;
199
200 for (aff1 = 0; aff1 < PLATFORM_CLUSTER_COUNT; aff1++) {
201
202 fvp_aff1_topology_map[aff1].child = aff0_offset;
203 fvp_aff1_topology_map[aff1].sibling = aff1 + 1;
204
205 for (aff0 = 0; aff0 < PLATFORM_MAX_CPUS_PER_CLUSTER; aff0++) {
206
207 mpidr = aff1 << MPIDR_AFF1_SHIFT;
208 mpidr |= aff0 << MPIDR_AFF0_SHIFT;
209
210 if (fvp_pwrc_read_psysr(mpidr) != PSYSR_INVALID) {
211 /*
212 * Presence of even a single aff0 indicates
213 * presence of parent aff1 on the FVP.
214 */
215 aff_state = PSCI_AFF_PRESENT;
216 fvp_aff1_topology_map[aff1].state =
217 PSCI_AFF_PRESENT;
218 } else {
219 aff_state = PSCI_AFF_ABSENT;
220 }
221
222 fvp_aff0_topology_map[aff0_offset].child = AFFINST_INVAL;
223 fvp_aff0_topology_map[aff0_offset].state = aff_state;
224 fvp_aff0_topology_map[aff0_offset].sibling =
225 aff0_offset + 1;
226
227 /* Increment the absolute number of aff0s traversed */
228 aff0_offset++;
229 }
230
231 /* Tie-off the last aff0 sibling to -1 to avoid overflow */
232 fvp_aff0_topology_map[aff0_offset - 1].sibling = AFFINST_INVAL;
233 }
234
235 /* Tie-off the last aff1 sibling to AFFINST_INVAL to avoid overflow */
236 fvp_aff1_topology_map[aff1 - 1].sibling = AFFINST_INVAL;
237
238 topology_setup_done = 1;
239 return 0;
240}