blob: ed673e49931536192ebb1ffb281fd0b6fcbc2d40 [file] [log] [blame]
Aaron Williams34658b12020-08-20 07:22:01 +02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018-2020 Marvell International Ltd.
4 */
5
6#include <env.h>
7#include <errno.h>
8
9#include <linux/compat.h>
10#include <linux/ctype.h>
11
12#include <mach/cvmx-regs.h>
13#include <mach/cvmx-coremask.h>
14#include <mach/cvmx-fuse.h>
15#include <mach/octeon-model.h>
16#include <mach/octeon-feature.h>
Stefan Roese1a035f82020-12-11 17:05:56 +010017#include <mach/cvmx-ciu-defs.h>
Aaron Williams34658b12020-08-20 07:22:01 +020018
19struct cvmx_coremask *get_coremask_override(struct cvmx_coremask *pcm)
20{
21 struct cvmx_coremask pcm_override = CVMX_COREMASK_MAX;
22 char *cptr;
23
24 /* The old code sets the number of cores to be to 16 in this case. */
25 cvmx_coremask_set_cores(pcm, 0, 16);
26
27 if (OCTEON_IS_OCTEON2() || OCTEON_IS_OCTEON3())
28 cvmx_coremask_copy(pcm, &pcm_override);
29
30 cptr = env_get("coremask_override");
31 if (cptr) {
32 if (cvmx_coremask_str2bmp(pcm, cptr) < 0)
33 return NULL;
34 }
35
36 return pcm;
37}
38
39/* Validate the coremask that is passed to a boot* function. */
40int validate_coremask(struct cvmx_coremask *pcm)
41{
42 struct cvmx_coremask coremask_override;
43 struct cvmx_coremask fuse_coremask;
44
45 if (!get_coremask_override(&coremask_override))
46 return -1;
47
48 octeon_get_available_coremask(&fuse_coremask);
49
50 if (!cvmx_coremask_is_subset(&fuse_coremask, pcm)) {
51 puts("ERROR: Can't boot cores that don't exist!\n");
52 puts("Available coremask:\n");
53 cvmx_coremask_print(&fuse_coremask);
54 return -1;
55 }
56
57 if (!cvmx_coremask_is_subset(&coremask_override, pcm)) {
58 struct cvmx_coremask print_cm;
59
60 puts("Notice: coremask changed from:\n");
61 cvmx_coremask_print(pcm);
62 puts("based on coremask_override of:\n");
63 cvmx_coremask_print(&coremask_override);
64 cvmx_coremask_and(&print_cm, pcm, &coremask_override);
65 puts("to:\n");
66 cvmx_coremask_print(&print_cm);
67 }
68
69 return 0;
70}
71
72/**
73 * In CIU_FUSE for the 78XX, odd and even cores are separated out.
74 * For example, a CIU_FUSE value of 0xfffffefffffe indicates that bits 0 and 1
75 * are set.
76 * This function converts the bit number in the CIU_FUSE register to a
77 * physical core number.
78 */
79static int convert_ciu_fuse_to_physical_core(int core, int max_cores)
80{
81 if (!octeon_has_feature(OCTEON_FEATURE_CIU3))
82 return core;
83 else if (!OCTEON_IS_MODEL(OCTEON_CN78XX))
84 return core;
85 else if (core < (max_cores / 2))
86 return core * 2;
87 else
88 return ((core - (max_cores / 2)) * 2) + 1;
89}
90
91/**
92 * Get the total number of fuses blown as well as the number blown per tad.
93 *
94 * @param coremask fuse coremask
95 * @param[out] tad_blown_count number of cores blown for each tad
96 * @param num_tads number of tads
97 * @param max_cores maximum number of cores
98 *
99 * @return void
100 */
101void fill_tad_corecount(u64 coremask, int tad_blown_count[], int num_tads,
102 int max_cores)
103{
104 int core, physical_core;
105
106 for (core = 0; core < max_cores; core++) {
107 if (!(coremask & (1ULL << core))) {
108 int tad;
109
110 physical_core =
111 convert_ciu_fuse_to_physical_core(core,
112 max_cores);
113 tad = physical_core % num_tads;
114 tad_blown_count[tad]++;
115 }
116 }
117}
118
119u64 get_core_pattern(int num_tads, int max_cores)
120{
121 u64 pattern = 1ULL;
122 int cnt;
123
124 for (cnt = 1; cnt < (max_cores / num_tads); cnt++)
125 pattern |= pattern << num_tads;
126
127 return pattern;
128}
129
130/**
131 * For CN78XX and CN68XX this function returns the logical coremask from the
132 * CIU_FUSE register value. For other models there is no difference.
133 *
134 * @param ciu_fuse_value fuse value from CIU_FUSE register
135 * @return logical coremask of CIU_FUSE value.
136 */
137u64 get_logical_coremask(u64 ciu_fuse_value)
138{
139 int tad_blown_count[MAX_CORE_TADS] = {0};
140 int tad;
141 u64 logical_coremask = 0;
142 u64 tad_mask, pattern;
143 int num_tads, max_cores;
144
145 if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
146 num_tads = 8;
147 max_cores = 48;
148 } else if (OCTEON_IS_MODEL(OCTEON_CN73XX) ||
149 OCTEON_IS_MODEL(OCTEON_CNF75XX)) {
150 num_tads = 4;
151 max_cores = 16;
152 } else if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
153 num_tads = 4;
154 max_cores = 32;
155 } else {
156 /* Most Octeon devices don't need any mapping. */
157 return ciu_fuse_value;
158 }
159
160 pattern = get_core_pattern(num_tads, max_cores);
161 fill_tad_corecount(ciu_fuse_value, tad_blown_count,
162 num_tads, max_cores);
163
164 for (tad = 0; tad < num_tads; tad++) {
165 tad_mask = pattern << tad;
166 logical_coremask |= tad_mask >> (tad_blown_count[tad] * num_tads);
167 }
168 return logical_coremask;
169}
170
171/**
172 * Returns the available coremask either from env or fuses.
173 * If the fuses are blown and locked, they are the definitive coremask.
174 *
175 * @param pcm pointer to coremask to fill in
176 * @return pointer to coremask
177 */
178struct cvmx_coremask *octeon_get_available_coremask(struct cvmx_coremask *pcm)
179{
180 u8 node_mask = 0x01; /* ToDo: Currently only one node is supported */
181 u64 ciu_fuse;
182 u64 cores;
183
184 cvmx_coremask_clear_all(pcm);
185
186 if (octeon_has_feature(OCTEON_FEATURE_CIU3)) {
187 int node;
188
189 cvmx_coremask_for_each_node(node, node_mask) {
190 ciu_fuse = (csr_rd(CVMX_CIU_FUSE) &
191 0x0000FFFFFFFFFFFFULL);
192
193 ciu_fuse = get_logical_coremask(ciu_fuse);
194 cvmx_coremask_set64_node(pcm, node, ciu_fuse);
195 }
196
197 return pcm;
198 }
199
200 ciu_fuse = (csr_rd(CVMX_CIU_FUSE) & 0x0000FFFFFFFFFFFFULL);
201 ciu_fuse = get_logical_coremask(ciu_fuse);
202
203 if (OCTEON_IS_MODEL(OCTEON_CN68XX))
204 cvmx_coremask_set64(pcm, ciu_fuse);
205
206 /* Get number of cores from fuse register, convert to coremask */
207 cores = __builtin_popcountll(ciu_fuse);
208
209 cvmx_coremask_set_cores(pcm, 0, cores);
210
211 return pcm;
212}
213
214int cvmx_coremask_str2bmp(struct cvmx_coremask *pcm, char *hexstr)
215{
216 int i, j;
217 int l; /* length of the hexstr in characters */
218 int lb; /* number of bits taken by hexstr */
219 int hldr_offset;/* holder's offset within the coremask */
220 int hldr_xsz; /* holder's size in the number of hex digits */
221 u64 h;
222 char c;
223
224#define MINUS_ONE (hexstr[0] == '-' && hexstr[1] == '1' && hexstr[2] == 0)
225 if (MINUS_ONE) {
226 cvmx_coremask_set_all(pcm);
227 return 0;
228 }
229
230 /* Skip '0x' from hexstr */
231 if (hexstr[0] == '0' && (hexstr[1] == 'x' || hexstr[1] == 'X'))
232 hexstr += 2;
233
234 if (!strlen(hexstr)) {
235 printf("%s: Error: hex string is empty\n", __func__);
236 return -2;
237 }
238
239 /* Trim leading zeros */
240 while (*hexstr == '0')
241 hexstr++;
242
243 cvmx_coremask_clear_all(pcm);
244 l = strlen(hexstr);
245
246 /* If length is 0 then the hex string must be all zeros */
247 if (l == 0)
248 return 0;
249
250 for (i = 0; i < l; i++) {
251 if (isxdigit((int)hexstr[i]) == 0) {
252 printf("%s: Non-hex digit within hexstr\n", __func__);
253 return -2;
254 }
255 }
256
257 lb = (l - 1) * 4;
258 if (hexstr[0] > '7')
259 lb += 4;
260 else if (hexstr[0] > '3')
261 lb += 3;
262 else if (hexstr[0] > '1')
263 lb += 2;
264 else
265 lb += 1;
266 if (lb > CVMX_MIPS_MAX_CORES) {
267 printf("%s: hexstr (%s) is too long\n", __func__, hexstr);
268 return -1;
269 }
270
271 hldr_offset = 0;
272 hldr_xsz = 2 * sizeof(u64);
273 for (i = l; i > 0; i -= hldr_xsz) {
274 c = hexstr[i];
275 hexstr[i] = 0;
276 j = i - hldr_xsz;
277 if (j < 0)
278 j = 0;
279 h = simple_strtoull(&hexstr[j], NULL, 16);
280 if (errno == EINVAL) {
281 printf("%s: strtou returns w/ EINVAL\n", __func__);
282 return -2;
283 }
284 pcm->coremask_bitmap[hldr_offset] = h;
285 hexstr[i] = c;
286 hldr_offset++;
287 }
288
289 return 0;
290}
291
292void cvmx_coremask_print(const struct cvmx_coremask *pcm)
293{
294 int i, j;
295 int start;
296 int found = 0;
297
298 /*
299 * Print one node per line. Since the bitmap is stored LSB to MSB
300 * we reverse the order when printing.
301 */
302 if (!octeon_has_feature(OCTEON_FEATURE_MULTINODE)) {
303 start = 0;
304 for (j = CVMX_COREMASK_MAX_CORES_PER_NODE -
305 CVMX_COREMASK_HLDRSZ;
306 j >= 0; j -= CVMX_COREMASK_HLDRSZ) {
307 if (pcm->coremask_bitmap[j / CVMX_COREMASK_HLDRSZ] != 0)
308 start = 1;
309 if (start) {
310 printf(" 0x%llx",
311 (u64)pcm->coremask_bitmap[j /
312 CVMX_COREMASK_HLDRSZ]);
313 }
314 }
315
316 if (start)
317 found = 1;
318
319 /*
320 * If the coremask is empty print <EMPTY> so it is not
321 * confusing
322 */
323 if (!found)
324 printf("<EMPTY>");
325 printf("\n");
326
327 return;
328 }
329
330 for (i = 0; i < CVMX_MAX_USED_CORES_BMP;
331 i += CVMX_COREMASK_MAX_CORES_PER_NODE) {
332 printf("%s node %d:", i > 0 ? "\n" : "",
333 cvmx_coremask_core_to_node(i));
334 start = 0;
335
336 for (j = i + CVMX_COREMASK_MAX_CORES_PER_NODE -
337 CVMX_COREMASK_HLDRSZ;
338 j >= i;
339 j -= CVMX_COREMASK_HLDRSZ) {
340 /* Don't start printing until we get a non-zero word. */
341 if (pcm->coremask_bitmap[j / CVMX_COREMASK_HLDRSZ] != 0)
342 start = 1;
343
344 if (start) {
345 printf(" 0x%llx", (u64)pcm->coremask_bitmap[j /
346 CVMX_COREMASK_HLDRSZ]);
347 }
348 }
349
350 if (start)
351 found = 1;
352 }
353
354 i /= CVMX_COREMASK_HLDRSZ;
355 for (; i < CVMX_COREMASK_BMPSZ; i++) {
356 if (pcm->coremask_bitmap[i]) {
357 printf(" EXTRA GARBAGE[%i]: %016llx\n", i,
358 (u64)pcm->coremask_bitmap[i]);
359 }
360 }
361
362 /* If the coremask is empty print <EMPTY> so it is not confusing */
363 if (!found)
364 printf("<EMPTY>");
365
366 printf("\n");
367}