mirror of
https://github.com/torvalds/linux.git
synced 2024-12-14 23:25:54 +00:00
359 lines
7.8 KiB
C
359 lines
7.8 KiB
C
|
/***********************license start***************
|
||
|
* Author: Cavium Networks
|
||
|
*
|
||
|
* Contact: support@caviumnetworks.com
|
||
|
* This file is part of the OCTEON SDK
|
||
|
*
|
||
|
* Copyright (c) 2003-2008 Cavium Networks
|
||
|
*
|
||
|
* This file is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License, Version 2, as
|
||
|
* published by the Free Software Foundation.
|
||
|
*
|
||
|
* This file is distributed in the hope that it will be useful, but
|
||
|
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
|
||
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
|
||
|
* NONINFRINGEMENT. See the GNU General Public License for more
|
||
|
* details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this file; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
* or visit http://www.gnu.org/licenses/.
|
||
|
*
|
||
|
* This file may also be available under a different license from Cavium.
|
||
|
* Contact Cavium Networks for more information
|
||
|
***********************license end**************************************/
|
||
|
|
||
|
/*
|
||
|
* File defining functions for working with different Octeon
|
||
|
* models.
|
||
|
*/
|
||
|
#include <asm/octeon/octeon.h>
|
||
|
|
||
|
/**
|
||
|
* Given the chip processor ID from COP0, this function returns a
|
||
|
* string representing the chip model number. The string is of the
|
||
|
* form CNXXXXpX.X-FREQ-SUFFIX.
|
||
|
* - XXXX = The chip model number
|
||
|
* - X.X = Chip pass number
|
||
|
* - FREQ = Current frequency in Mhz
|
||
|
* - SUFFIX = NSP, EXP, SCP, SSP, or CP
|
||
|
*
|
||
|
* @chip_id: Chip ID
|
||
|
*
|
||
|
* Returns Model string
|
||
|
*/
|
||
|
const char *octeon_model_get_string(uint32_t chip_id)
|
||
|
{
|
||
|
static char buffer[32];
|
||
|
return octeon_model_get_string_buffer(chip_id, buffer);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Version of octeon_model_get_string() that takes buffer as argument,
|
||
|
* as running early in u-boot static/global variables don't work when
|
||
|
* running from flash.
|
||
|
*/
|
||
|
const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer)
|
||
|
{
|
||
|
const char *family;
|
||
|
const char *core_model;
|
||
|
char pass[4];
|
||
|
int clock_mhz;
|
||
|
const char *suffix;
|
||
|
union cvmx_l2d_fus3 fus3;
|
||
|
int num_cores;
|
||
|
union cvmx_mio_fus_dat2 fus_dat2;
|
||
|
union cvmx_mio_fus_dat3 fus_dat3;
|
||
|
char fuse_model[10];
|
||
|
uint32_t fuse_data = 0;
|
||
|
|
||
|
fus3.u64 = cvmx_read_csr(CVMX_L2D_FUS3);
|
||
|
fus_dat2.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT2);
|
||
|
fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3);
|
||
|
|
||
|
num_cores = cvmx_octeon_num_cores();
|
||
|
|
||
|
/* Make sure the non existant devices look disabled */
|
||
|
switch ((chip_id >> 8) & 0xff) {
|
||
|
case 6: /* CN50XX */
|
||
|
case 2: /* CN30XX */
|
||
|
fus_dat3.s.nodfa_dte = 1;
|
||
|
fus_dat3.s.nozip = 1;
|
||
|
break;
|
||
|
case 4: /* CN57XX or CN56XX */
|
||
|
fus_dat3.s.nodfa_dte = 1;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Make a guess at the suffix */
|
||
|
/* NSP = everything */
|
||
|
/* EXP = No crypto */
|
||
|
/* SCP = No DFA, No zip */
|
||
|
/* CP = No DFA, No crypto, No zip */
|
||
|
if (fus_dat3.s.nodfa_dte) {
|
||
|
if (fus_dat2.s.nocrypto)
|
||
|
suffix = "CP";
|
||
|
else
|
||
|
suffix = "SCP";
|
||
|
} else if (fus_dat2.s.nocrypto)
|
||
|
suffix = "EXP";
|
||
|
else
|
||
|
suffix = "NSP";
|
||
|
|
||
|
/*
|
||
|
* Assume pass number is encoded using <5:3><2:0>. Exceptions
|
||
|
* will be fixed later.
|
||
|
*/
|
||
|
sprintf(pass, "%u.%u", ((chip_id >> 3) & 7) + 1, chip_id & 7);
|
||
|
|
||
|
/*
|
||
|
* Use the number of cores to determine the last 2 digits of
|
||
|
* the model number. There are some exceptions that are fixed
|
||
|
* later.
|
||
|
*/
|
||
|
switch (num_cores) {
|
||
|
case 16:
|
||
|
core_model = "60";
|
||
|
break;
|
||
|
case 15:
|
||
|
core_model = "58";
|
||
|
break;
|
||
|
case 14:
|
||
|
core_model = "55";
|
||
|
break;
|
||
|
case 13:
|
||
|
core_model = "52";
|
||
|
break;
|
||
|
case 12:
|
||
|
core_model = "50";
|
||
|
break;
|
||
|
case 11:
|
||
|
core_model = "48";
|
||
|
break;
|
||
|
case 10:
|
||
|
core_model = "45";
|
||
|
break;
|
||
|
case 9:
|
||
|
core_model = "42";
|
||
|
break;
|
||
|
case 8:
|
||
|
core_model = "40";
|
||
|
break;
|
||
|
case 7:
|
||
|
core_model = "38";
|
||
|
break;
|
||
|
case 6:
|
||
|
core_model = "34";
|
||
|
break;
|
||
|
case 5:
|
||
|
core_model = "32";
|
||
|
break;
|
||
|
case 4:
|
||
|
core_model = "30";
|
||
|
break;
|
||
|
case 3:
|
||
|
core_model = "25";
|
||
|
break;
|
||
|
case 2:
|
||
|
core_model = "20";
|
||
|
break;
|
||
|
case 1:
|
||
|
core_model = "10";
|
||
|
break;
|
||
|
default:
|
||
|
core_model = "XX";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Now figure out the family, the first two digits */
|
||
|
switch ((chip_id >> 8) & 0xff) {
|
||
|
case 0: /* CN38XX, CN37XX or CN36XX */
|
||
|
if (fus3.cn38xx.crip_512k) {
|
||
|
/*
|
||
|
* For some unknown reason, the 16 core one is
|
||
|
* called 37 instead of 36.
|
||
|
*/
|
||
|
if (num_cores >= 16)
|
||
|
family = "37";
|
||
|
else
|
||
|
family = "36";
|
||
|
} else
|
||
|
family = "38";
|
||
|
/*
|
||
|
* This series of chips didn't follow the standard
|
||
|
* pass numbering.
|
||
|
*/
|
||
|
switch (chip_id & 0xf) {
|
||
|
case 0:
|
||
|
strcpy(pass, "1.X");
|
||
|
break;
|
||
|
case 1:
|
||
|
strcpy(pass, "2.X");
|
||
|
break;
|
||
|
case 3:
|
||
|
strcpy(pass, "3.X");
|
||
|
break;
|
||
|
default:
|
||
|
strcpy(pass, "X.X");
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case 1: /* CN31XX or CN3020 */
|
||
|
if ((chip_id & 0x10) || fus3.cn31xx.crip_128k)
|
||
|
family = "30";
|
||
|
else
|
||
|
family = "31";
|
||
|
/*
|
||
|
* This series of chips didn't follow the standard
|
||
|
* pass numbering.
|
||
|
*/
|
||
|
switch (chip_id & 0xf) {
|
||
|
case 0:
|
||
|
strcpy(pass, "1.0");
|
||
|
break;
|
||
|
case 2:
|
||
|
strcpy(pass, "1.1");
|
||
|
break;
|
||
|
default:
|
||
|
strcpy(pass, "X.X");
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case 2: /* CN3010 or CN3005 */
|
||
|
family = "30";
|
||
|
/* A chip with half cache is an 05 */
|
||
|
if (fus3.cn30xx.crip_64k)
|
||
|
core_model = "05";
|
||
|
/*
|
||
|
* This series of chips didn't follow the standard
|
||
|
* pass numbering.
|
||
|
*/
|
||
|
switch (chip_id & 0xf) {
|
||
|
case 0:
|
||
|
strcpy(pass, "1.0");
|
||
|
break;
|
||
|
case 2:
|
||
|
strcpy(pass, "1.1");
|
||
|
break;
|
||
|
default:
|
||
|
strcpy(pass, "X.X");
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case 3: /* CN58XX */
|
||
|
family = "58";
|
||
|
/* Special case. 4 core, no crypto */
|
||
|
if ((num_cores == 4) && fus_dat2.cn38xx.nocrypto)
|
||
|
core_model = "29";
|
||
|
|
||
|
/* Pass 1 uses different encodings for pass numbers */
|
||
|
if ((chip_id & 0xFF) < 0x8) {
|
||
|
switch (chip_id & 0x3) {
|
||
|
case 0:
|
||
|
strcpy(pass, "1.0");
|
||
|
break;
|
||
|
case 1:
|
||
|
strcpy(pass, "1.1");
|
||
|
break;
|
||
|
case 3:
|
||
|
strcpy(pass, "1.2");
|
||
|
break;
|
||
|
default:
|
||
|
strcpy(pass, "1.X");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 4: /* CN57XX, CN56XX, CN55XX, CN54XX */
|
||
|
if (fus_dat2.cn56xx.raid_en) {
|
||
|
if (fus3.cn56xx.crip_1024k)
|
||
|
family = "55";
|
||
|
else
|
||
|
family = "57";
|
||
|
if (fus_dat2.cn56xx.nocrypto)
|
||
|
suffix = "SP";
|
||
|
else
|
||
|
suffix = "SSP";
|
||
|
} else {
|
||
|
if (fus_dat2.cn56xx.nocrypto)
|
||
|
suffix = "CP";
|
||
|
else {
|
||
|
suffix = "NSP";
|
||
|
if (fus_dat3.s.nozip)
|
||
|
suffix = "SCP";
|
||
|
}
|
||
|
if (fus3.cn56xx.crip_1024k)
|
||
|
family = "54";
|
||
|
else
|
||
|
family = "56";
|
||
|
}
|
||
|
break;
|
||
|
case 6: /* CN50XX */
|
||
|
family = "50";
|
||
|
break;
|
||
|
case 7: /* CN52XX */
|
||
|
if (fus3.cn52xx.crip_256k)
|
||
|
family = "51";
|
||
|
else
|
||
|
family = "52";
|
||
|
break;
|
||
|
default:
|
||
|
family = "XX";
|
||
|
core_model = "XX";
|
||
|
strcpy(pass, "X.X");
|
||
|
suffix = "XXX";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
clock_mhz = octeon_get_clock_rate() / 1000000;
|
||
|
|
||
|
if (family[0] != '3') {
|
||
|
/* Check for model in fuses, overrides normal decode */
|
||
|
/* This is _not_ valid for Octeon CN3XXX models */
|
||
|
fuse_data |= cvmx_fuse_read_byte(51);
|
||
|
fuse_data = fuse_data << 8;
|
||
|
fuse_data |= cvmx_fuse_read_byte(50);
|
||
|
fuse_data = fuse_data << 8;
|
||
|
fuse_data |= cvmx_fuse_read_byte(49);
|
||
|
fuse_data = fuse_data << 8;
|
||
|
fuse_data |= cvmx_fuse_read_byte(48);
|
||
|
if (fuse_data & 0x7ffff) {
|
||
|
int model = fuse_data & 0x3fff;
|
||
|
int suffix = (fuse_data >> 14) & 0x1f;
|
||
|
if (suffix && model) {
|
||
|
/*
|
||
|
* Have both number and suffix in
|
||
|
* fuses, so both
|
||
|
*/
|
||
|
sprintf(fuse_model, "%d%c",
|
||
|
model, 'A' + suffix - 1);
|
||
|
core_model = "";
|
||
|
family = fuse_model;
|
||
|
} else if (suffix && !model) {
|
||
|
/*
|
||
|
* Only have suffix, so add suffix to
|
||
|
* 'normal' model number.
|
||
|
*/
|
||
|
sprintf(fuse_model, "%s%c", core_model,
|
||
|
'A' + suffix - 1);
|
||
|
core_model = fuse_model;
|
||
|
} else {
|
||
|
/*
|
||
|
* Don't have suffix, so just use
|
||
|
* model from fuses.
|
||
|
*/
|
||
|
sprintf(fuse_model, "%d", model);
|
||
|
core_model = "";
|
||
|
family = fuse_model;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
sprintf(buffer, "CN%s%sp%s-%d-%s",
|
||
|
family, core_model, pass, clock_mhz, suffix);
|
||
|
return buffer;
|
||
|
}
|