Merge tag 'mips-pull-2020-10-07' of https://gitlab.denx.de/u-boot/custodians/u-boot...
[platform/kernel/u-boot.git] / arch / mips / mach-octeon / cvmx-coremask.c
1 // 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
18 struct 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. */
39 int 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  */
78 static 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  */
100 void 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
118 u64 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  */
136 u64 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  */
177 struct 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
213 int 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
291 void 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 }