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