mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-08 14:14:32 +00:00
367 lines
8.4 KiB
C
367 lines
8.4 KiB
C
|
// 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>
|
||
|
|
||
|
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");
|
||
|
}
|