summaryrefslogtreecommitdiff
path: root/arch/mips/mach-octeon/cvmx-coremask.c
blob: ed673e49931536192ebb1ffb281fd0b6fcbc2d40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2018-2020 Marvell International Ltd.
 */

#include <env.h>
#include <errno.h>

#include <linux/compat.h>
#include <linux/ctype.h>

#include <mach/cvmx-regs.h>
#include <mach/cvmx-coremask.h>
#include <mach/cvmx-fuse.h>
#include <mach/octeon-model.h>
#include <mach/octeon-feature.h>
#include <mach/cvmx-ciu-defs.h>

struct cvmx_coremask *get_coremask_override(struct cvmx_coremask *pcm)
{
	struct cvmx_coremask pcm_override = CVMX_COREMASK_MAX;
	char *cptr;

	/* The old code sets the number of cores to be to 16 in this case. */
	cvmx_coremask_set_cores(pcm, 0, 16);

	if (OCTEON_IS_OCTEON2() || OCTEON_IS_OCTEON3())
		cvmx_coremask_copy(pcm, &pcm_override);

	cptr = env_get("coremask_override");
	if (cptr) {
		if (cvmx_coremask_str2bmp(pcm, cptr) < 0)
			return NULL;
	}

	return pcm;
}

/* Validate the coremask that is passed to a boot* function. */
int validate_coremask(struct cvmx_coremask *pcm)
{
	struct cvmx_coremask coremask_override;
	struct cvmx_coremask fuse_coremask;

	if (!get_coremask_override(&coremask_override))
		return -1;

	octeon_get_available_coremask(&fuse_coremask);

	if (!cvmx_coremask_is_subset(&fuse_coremask, pcm)) {
		puts("ERROR: Can't boot cores that don't exist!\n");
		puts("Available coremask:\n");
		cvmx_coremask_print(&fuse_coremask);
		return -1;
	}

	if (!cvmx_coremask_is_subset(&coremask_override, pcm)) {
		struct cvmx_coremask print_cm;

		puts("Notice: coremask changed from:\n");
		cvmx_coremask_print(pcm);
		puts("based on coremask_override of:\n");
		cvmx_coremask_print(&coremask_override);
		cvmx_coremask_and(&print_cm, pcm, &coremask_override);
		puts("to:\n");
		cvmx_coremask_print(&print_cm);
	}

	return 0;
}

/**
 * In CIU_FUSE for the 78XX, odd and even cores are separated out.
 * For example, a CIU_FUSE value of 0xfffffefffffe indicates that bits 0 and 1
 * are set.
 * This function converts the bit number in the CIU_FUSE register to a
 * physical core number.
 */
static int convert_ciu_fuse_to_physical_core(int core, int max_cores)
{
	if (!octeon_has_feature(OCTEON_FEATURE_CIU3))
		return core;
	else if (!OCTEON_IS_MODEL(OCTEON_CN78XX))
		return core;
	else if (core < (max_cores / 2))
		return core * 2;
	else
		return ((core - (max_cores / 2)) * 2) + 1;
}

/**
 * Get the total number of fuses blown as well as the number blown per tad.
 *
 * @param	coremask	fuse coremask
 * @param[out]	tad_blown_count	number of cores blown for each tad
 * @param	num_tads	number of tads
 * @param	max_cores	maximum number of cores
 *
 * @return	void
 */
void fill_tad_corecount(u64 coremask, int tad_blown_count[], int num_tads,
			int max_cores)
{
	int core, physical_core;

	for (core = 0; core < max_cores; core++) {
		if (!(coremask & (1ULL << core))) {
			int tad;

			physical_core =
				convert_ciu_fuse_to_physical_core(core,
								  max_cores);
			tad = physical_core % num_tads;
			tad_blown_count[tad]++;
		}
	}
}

u64 get_core_pattern(int num_tads, int max_cores)
{
	u64 pattern = 1ULL;
	int cnt;

	for (cnt = 1; cnt < (max_cores / num_tads); cnt++)
		pattern |= pattern << num_tads;

	return pattern;
}

/**
 * For CN78XX and CN68XX this function returns the logical coremask from the
 * CIU_FUSE register value. For other models there is no difference.
 *
 * @param ciu_fuse_value	fuse value from CIU_FUSE register
 * @return logical coremask of CIU_FUSE value.
 */
u64 get_logical_coremask(u64 ciu_fuse_value)
{
	int tad_blown_count[MAX_CORE_TADS] = {0};
	int tad;
	u64 logical_coremask = 0;
	u64 tad_mask, pattern;
	int num_tads, max_cores;

	if (OCTEON_IS_MODEL(OCTEON_CN78XX)) {
		num_tads = 8;
		max_cores = 48;
	} else if (OCTEON_IS_MODEL(OCTEON_CN73XX) ||
		   OCTEON_IS_MODEL(OCTEON_CNF75XX)) {
		num_tads = 4;
		max_cores = 16;
	} else if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
		num_tads = 4;
		max_cores = 32;
	} else {
		/* Most Octeon devices don't need any mapping. */
		return ciu_fuse_value;
	}

	pattern = get_core_pattern(num_tads, max_cores);
	fill_tad_corecount(ciu_fuse_value, tad_blown_count,
			   num_tads, max_cores);

	for (tad = 0; tad < num_tads; tad++) {
		tad_mask = pattern << tad;
		logical_coremask |= tad_mask >> (tad_blown_count[tad] * num_tads);
	}
	return logical_coremask;
}

/**
 * Returns the available coremask either from env or fuses.
 * If the fuses are blown and locked, they are the definitive coremask.
 *
 * @param pcm	pointer to coremask to fill in
 * @return pointer to coremask
 */
struct cvmx_coremask *octeon_get_available_coremask(struct cvmx_coremask *pcm)
{
	u8 node_mask = 0x01;	/* ToDo: Currently only one node is supported */
	u64 ciu_fuse;
	u64 cores;

	cvmx_coremask_clear_all(pcm);

	if (octeon_has_feature(OCTEON_FEATURE_CIU3)) {
		int node;

		cvmx_coremask_for_each_node(node, node_mask) {
			ciu_fuse = (csr_rd(CVMX_CIU_FUSE) &
				    0x0000FFFFFFFFFFFFULL);

			ciu_fuse = get_logical_coremask(ciu_fuse);
			cvmx_coremask_set64_node(pcm, node, ciu_fuse);
		}

		return pcm;
	}

	ciu_fuse = (csr_rd(CVMX_CIU_FUSE) & 0x0000FFFFFFFFFFFFULL);
	ciu_fuse = get_logical_coremask(ciu_fuse);

	if (OCTEON_IS_MODEL(OCTEON_CN68XX))
		cvmx_coremask_set64(pcm, ciu_fuse);

	/* Get number of cores from fuse register, convert to coremask */
	cores = __builtin_popcountll(ciu_fuse);

	cvmx_coremask_set_cores(pcm, 0, cores);

	return pcm;
}

int cvmx_coremask_str2bmp(struct cvmx_coremask *pcm, char *hexstr)
{
	int i, j;
	int l;		/* length of the hexstr in characters */
	int lb;		/* number of bits taken by hexstr */
	int hldr_offset;/* holder's offset within the coremask */
	int hldr_xsz;	/* holder's size in the number of hex digits */
	u64 h;
	char c;

#define MINUS_ONE (hexstr[0] == '-' && hexstr[1] == '1' && hexstr[2] == 0)
	if (MINUS_ONE) {
		cvmx_coremask_set_all(pcm);
		return 0;
	}

	/* Skip '0x' from hexstr */
	if (hexstr[0] == '0' && (hexstr[1] == 'x' || hexstr[1] == 'X'))
		hexstr += 2;

	if (!strlen(hexstr)) {
		printf("%s: Error: hex string is empty\n", __func__);
		return -2;
	}

	/* Trim leading zeros */
	while (*hexstr == '0')
		hexstr++;

	cvmx_coremask_clear_all(pcm);
	l = strlen(hexstr);

	/* If length is 0 then the hex string must be all zeros */
	if (l == 0)
		return 0;

	for (i = 0; i < l; i++) {
		if (isxdigit((int)hexstr[i]) == 0) {
			printf("%s: Non-hex digit within hexstr\n", __func__);
			return -2;
		}
	}

	lb = (l - 1) * 4;
	if (hexstr[0] > '7')
		lb += 4;
	else if (hexstr[0] > '3')
		lb += 3;
	else if (hexstr[0] > '1')
		lb += 2;
	else
		lb += 1;
	if (lb > CVMX_MIPS_MAX_CORES) {
		printf("%s: hexstr (%s) is too long\n", __func__, hexstr);
		return -1;
	}

	hldr_offset = 0;
	hldr_xsz = 2 * sizeof(u64);
	for (i = l; i > 0; i -= hldr_xsz) {
		c = hexstr[i];
		hexstr[i] = 0;
		j = i - hldr_xsz;
		if (j < 0)
			j = 0;
		h = simple_strtoull(&hexstr[j], NULL, 16);
		if (errno == EINVAL) {
			printf("%s: strtou returns w/ EINVAL\n", __func__);
			return -2;
		}
		pcm->coremask_bitmap[hldr_offset] = h;
		hexstr[i] = c;
		hldr_offset++;
	}

	return 0;
}

void cvmx_coremask_print(const struct cvmx_coremask *pcm)
{
	int i, j;
	int start;
	int found = 0;

	/*
	 * Print one node per line. Since the bitmap is stored LSB to MSB
	 * we reverse the order when printing.
	 */
	if (!octeon_has_feature(OCTEON_FEATURE_MULTINODE)) {
		start = 0;
		for (j = CVMX_COREMASK_MAX_CORES_PER_NODE -
			     CVMX_COREMASK_HLDRSZ;
		     j >= 0; j -= CVMX_COREMASK_HLDRSZ) {
			if (pcm->coremask_bitmap[j / CVMX_COREMASK_HLDRSZ] != 0)
				start = 1;
			if (start) {
				printf(" 0x%llx",
				       (u64)pcm->coremask_bitmap[j /
						CVMX_COREMASK_HLDRSZ]);
			}
		}

		if (start)
			found = 1;

		/*
		 * If the coremask is empty print <EMPTY> so it is not
		 * confusing
		 */
		if (!found)
			printf("<EMPTY>");
		printf("\n");

		return;
	}

	for (i = 0; i < CVMX_MAX_USED_CORES_BMP;
	     i += CVMX_COREMASK_MAX_CORES_PER_NODE) {
		printf("%s  node %d:", i > 0 ? "\n" : "",
		       cvmx_coremask_core_to_node(i));
		start = 0;

		for (j = i + CVMX_COREMASK_MAX_CORES_PER_NODE -
			     CVMX_COREMASK_HLDRSZ;
		     j >= i;
		     j -= CVMX_COREMASK_HLDRSZ) {
			/* Don't start printing until we get a non-zero word. */
			if (pcm->coremask_bitmap[j / CVMX_COREMASK_HLDRSZ] != 0)
				start = 1;

			if (start) {
				printf(" 0x%llx", (u64)pcm->coremask_bitmap[j /
							CVMX_COREMASK_HLDRSZ]);
			}
		}

		if (start)
			found = 1;
	}

	i /= CVMX_COREMASK_HLDRSZ;
	for (; i < CVMX_COREMASK_BMPSZ; i++) {
		if (pcm->coremask_bitmap[i]) {
			printf("  EXTRA GARBAGE[%i]: %016llx\n", i,
			       (u64)pcm->coremask_bitmap[i]);
		}
	}

	/* If the coremask is empty print <EMPTY> so it is not confusing */
	if (!found)
		printf("<EMPTY>");

	printf("\n");
}