Path: blob/master/arch/mips/cavium-octeon/executive/octeon-model.c
26481 views
/***********************license start***************1* Author: Cavium Networks2*3* Contact: [email protected]4* This file is part of the OCTEON SDK5*6* Copyright (c) 2003-2017 Cavium, Inc.7*8* This file is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License, Version 2, as10* published by the Free Software Foundation.11*12* This file is distributed in the hope that it will be useful, but13* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty14* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or15* NONINFRINGEMENT. See the GNU General Public License for more16* details.17*18* You should have received a copy of the GNU General Public License19* along with this file; if not, write to the Free Software20* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA21* or visit http://www.gnu.org/licenses/.22*23* This file may also be available under a different license from Cavium.24* Contact Cavium Networks for more information25***********************license end**************************************/2627#include <asm/octeon/octeon.h>2829enum octeon_feature_bits __octeon_feature_bits __read_mostly;30EXPORT_SYMBOL_GPL(__octeon_feature_bits);3132/**33* Read a byte of fuse data34* @byte_addr: address to read35*36* Returns fuse value: 0 or 137*/38static uint8_t __init cvmx_fuse_read_byte(int byte_addr)39{40union cvmx_mio_fus_rcmd read_cmd;4142read_cmd.u64 = 0;43read_cmd.s.addr = byte_addr;44read_cmd.s.pend = 1;45cvmx_write_csr(CVMX_MIO_FUS_RCMD, read_cmd.u64);46while ((read_cmd.u64 = cvmx_read_csr(CVMX_MIO_FUS_RCMD))47&& read_cmd.s.pend)48;49return read_cmd.s.dat;50}5152/*53* Version of octeon_model_get_string() that takes buffer as argument,54* as running early in u-boot static/global variables don't work when55* running from flash.56*/57static const char *__init octeon_model_get_string_buffer(uint32_t chip_id,58char *buffer)59{60const char *family;61const char *core_model;62char pass[4];63int clock_mhz;64const char *suffix;65int num_cores;66union cvmx_mio_fus_dat2 fus_dat2;67union cvmx_mio_fus_dat3 fus_dat3;68char fuse_model[10];69uint32_t fuse_data = 0;70uint64_t l2d_fus3 = 0;7172if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN5XXX))73l2d_fus3 = (cvmx_read_csr(CVMX_L2D_FUS3) >> 34) & 0x3;74fus_dat2.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT2);75fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3);76num_cores = cvmx_octeon_num_cores();7778/* Make sure the non existent devices look disabled */79switch ((chip_id >> 8) & 0xff) {80case 6: /* CN50XX */81case 2: /* CN30XX */82fus_dat3.s.nodfa_dte = 1;83fus_dat3.s.nozip = 1;84break;85case 4: /* CN57XX or CN56XX */86fus_dat3.s.nodfa_dte = 1;87break;88default:89break;90}9192/* Make a guess at the suffix */93/* NSP = everything */94/* EXP = No crypto */95/* SCP = No DFA, No zip */96/* CP = No DFA, No crypto, No zip */97if (fus_dat3.s.nodfa_dte) {98if (fus_dat2.s.nocrypto)99suffix = "CP";100else101suffix = "SCP";102} else if (fus_dat2.s.nocrypto)103suffix = "EXP";104else105suffix = "NSP";106107if (!fus_dat2.s.nocrypto)108__octeon_feature_bits |= OCTEON_HAS_CRYPTO;109110/*111* Assume pass number is encoded using <5:3><2:0>. Exceptions112* will be fixed later.113*/114sprintf(pass, "%d.%d", (int)((chip_id >> 3) & 7) + 1, (int)chip_id & 7);115116/*117* Use the number of cores to determine the last 2 digits of118* the model number. There are some exceptions that are fixed119* later.120*/121switch (num_cores) {122case 48:123core_model = "90";124break;125case 44:126core_model = "88";127break;128case 40:129core_model = "85";130break;131case 32:132core_model = "80";133break;134case 24:135core_model = "70";136break;137case 16:138core_model = "60";139break;140case 15:141core_model = "58";142break;143case 14:144core_model = "55";145break;146case 13:147core_model = "52";148break;149case 12:150core_model = "50";151break;152case 11:153core_model = "48";154break;155case 10:156core_model = "45";157break;158case 9:159core_model = "42";160break;161case 8:162core_model = "40";163break;164case 7:165core_model = "38";166break;167case 6:168core_model = "34";169break;170case 5:171core_model = "32";172break;173case 4:174core_model = "30";175break;176case 3:177core_model = "25";178break;179case 2:180core_model = "20";181break;182case 1:183core_model = "10";184break;185default:186core_model = "XX";187break;188}189190/* Now figure out the family, the first two digits */191switch ((chip_id >> 8) & 0xff) {192case 0: /* CN38XX, CN37XX or CN36XX */193if (l2d_fus3) {194/*195* For some unknown reason, the 16 core one is196* called 37 instead of 36.197*/198if (num_cores >= 16)199family = "37";200else201family = "36";202} else203family = "38";204/*205* This series of chips didn't follow the standard206* pass numbering.207*/208switch (chip_id & 0xf) {209case 0:210strcpy(pass, "1.X");211break;212case 1:213strcpy(pass, "2.X");214break;215case 3:216strcpy(pass, "3.X");217break;218default:219strcpy(pass, "X.X");220break;221}222break;223case 1: /* CN31XX or CN3020 */224if ((chip_id & 0x10) || l2d_fus3)225family = "30";226else227family = "31";228/*229* This series of chips didn't follow the standard230* pass numbering.231*/232switch (chip_id & 0xf) {233case 0:234strcpy(pass, "1.0");235break;236case 2:237strcpy(pass, "1.1");238break;239default:240strcpy(pass, "X.X");241break;242}243break;244case 2: /* CN3010 or CN3005 */245family = "30";246/* A chip with half cache is an 05 */247if (l2d_fus3)248core_model = "05";249/*250* This series of chips didn't follow the standard251* pass numbering.252*/253switch (chip_id & 0xf) {254case 0:255strcpy(pass, "1.0");256break;257case 2:258strcpy(pass, "1.1");259break;260default:261strcpy(pass, "X.X");262break;263}264break;265case 3: /* CN58XX */266family = "58";267/* Special case. 4 core, half cache (CP with half cache) */268if ((num_cores == 4) && l2d_fus3 && !strncmp(suffix, "CP", 2))269core_model = "29";270271/* Pass 1 uses different encodings for pass numbers */272if ((chip_id & 0xFF) < 0x8) {273switch (chip_id & 0x3) {274case 0:275strcpy(pass, "1.0");276break;277case 1:278strcpy(pass, "1.1");279break;280case 3:281strcpy(pass, "1.2");282break;283default:284strcpy(pass, "1.X");285break;286}287}288break;289case 4: /* CN57XX, CN56XX, CN55XX, CN54XX */290if (fus_dat2.cn56xx.raid_en) {291if (l2d_fus3)292family = "55";293else294family = "57";295if (fus_dat2.cn56xx.nocrypto)296suffix = "SP";297else298suffix = "SSP";299} else {300if (fus_dat2.cn56xx.nocrypto)301suffix = "CP";302else {303suffix = "NSP";304if (fus_dat3.s.nozip)305suffix = "SCP";306307if (fus_dat3.cn38xx.bar2_en)308suffix = "NSPB2";309}310if (l2d_fus3)311family = "54";312else313family = "56";314}315break;316case 6: /* CN50XX */317family = "50";318break;319case 7: /* CN52XX */320if (l2d_fus3)321family = "51";322else323family = "52";324break;325case 0x93: /* CN61XX */326family = "61";327if (fus_dat2.cn61xx.nocrypto && fus_dat2.cn61xx.dorm_crypto)328suffix = "AP";329if (fus_dat2.cn61xx.nocrypto)330suffix = "CP";331else if (fus_dat2.cn61xx.dorm_crypto)332suffix = "DAP";333else if (fus_dat3.cn61xx.nozip)334suffix = "SCP";335break;336case 0x90: /* CN63XX */337family = "63";338if (fus_dat3.s.l2c_crip == 2)339family = "62";340if (num_cores == 6) /* Other core counts match generic */341core_model = "35";342if (fus_dat2.cn63xx.nocrypto)343suffix = "CP";344else if (fus_dat2.cn63xx.dorm_crypto)345suffix = "DAP";346else if (fus_dat3.cn61xx.nozip)347suffix = "SCP";348else349suffix = "AAP";350break;351case 0x92: /* CN66XX */352family = "66";353if (num_cores == 6) /* Other core counts match generic */354core_model = "35";355if (fus_dat2.cn66xx.nocrypto && fus_dat2.cn66xx.dorm_crypto)356suffix = "AP";357if (fus_dat2.cn66xx.nocrypto)358suffix = "CP";359else if (fus_dat2.cn66xx.dorm_crypto)360suffix = "DAP";361else if (fus_dat3.cn61xx.nozip)362suffix = "SCP";363else364suffix = "AAP";365break;366case 0x91: /* CN68XX */367family = "68";368if (fus_dat2.cn68xx.nocrypto && fus_dat3.cn61xx.nozip)369suffix = "CP";370else if (fus_dat2.cn68xx.dorm_crypto)371suffix = "DAP";372else if (fus_dat3.cn61xx.nozip)373suffix = "SCP";374else if (fus_dat2.cn68xx.nocrypto)375suffix = "SP";376else377suffix = "AAP";378break;379case 0x94: /* CNF71XX */380family = "F71";381if (fus_dat3.cn61xx.nozip)382suffix = "SCP";383else384suffix = "AAP";385break;386case 0x95: /* CN78XX */387if (num_cores == 6) /* Other core counts match generic */388core_model = "35";389if (OCTEON_IS_MODEL(OCTEON_CN76XX))390family = "76";391else392family = "78";393if (fus_dat3.cn78xx.l2c_crip == 2)394family = "77";395if (fus_dat3.cn78xx.nozip396&& fus_dat3.cn78xx.nodfa_dte397&& fus_dat3.cn78xx.nohna_dte) {398if (fus_dat3.cn78xx.nozip &&399!fus_dat2.cn78xx.raid_en &&400fus_dat3.cn78xx.nohna_dte) {401suffix = "CP";402} else {403suffix = "SCP";404}405} else if (fus_dat2.cn78xx.raid_en == 0)406suffix = "HCP";407else408suffix = "AAP";409break;410case 0x96: /* CN70XX */411family = "70";412if (cvmx_read_csr(CVMX_MIO_FUS_PDF) & (0x1ULL << 32))413family = "71";414if (fus_dat2.cn70xx.nocrypto)415suffix = "CP";416else if (fus_dat3.cn70xx.nodfa_dte)417suffix = "SCP";418else419suffix = "AAP";420break;421case 0x97: /* CN73XX */422if (num_cores == 6) /* Other core counts match generic */423core_model = "35";424family = "73";425if (fus_dat3.cn73xx.l2c_crip == 2)426family = "72";427if (fus_dat3.cn73xx.nozip428&& fus_dat3.cn73xx.nodfa_dte429&& fus_dat3.cn73xx.nohna_dte) {430if (!fus_dat2.cn73xx.raid_en)431suffix = "CP";432else433suffix = "SCP";434} else435suffix = "AAP";436break;437case 0x98: /* CN75XX */438family = "F75";439if (fus_dat3.cn78xx.nozip440&& fus_dat3.cn78xx.nodfa_dte441&& fus_dat3.cn78xx.nohna_dte)442suffix = "SCP";443else444suffix = "AAP";445break;446default:447family = "XX";448core_model = "XX";449strcpy(pass, "X.X");450suffix = "XXX";451break;452}453454clock_mhz = octeon_get_clock_rate() / 1000000;455if (family[0] != '3') {456int fuse_base = 384 / 8;457if (family[0] == '6')458fuse_base = 832 / 8;459460/* Check for model in fuses, overrides normal decode */461/* This is _not_ valid for Octeon CN3XXX models */462fuse_data |= cvmx_fuse_read_byte(fuse_base + 3);463fuse_data = fuse_data << 8;464fuse_data |= cvmx_fuse_read_byte(fuse_base + 2);465fuse_data = fuse_data << 8;466fuse_data |= cvmx_fuse_read_byte(fuse_base + 1);467fuse_data = fuse_data << 8;468fuse_data |= cvmx_fuse_read_byte(fuse_base);469if (fuse_data & 0x7ffff) {470int model = fuse_data & 0x3fff;471int suffix = (fuse_data >> 14) & 0x1f;472if (suffix && model) {473/* Have both number and suffix in fuses, so both */474sprintf(fuse_model, "%d%c", model, 'A' + suffix - 1);475core_model = "";476family = fuse_model;477} else if (suffix && !model) {478/* Only have suffix, so add suffix to 'normal' model number */479sprintf(fuse_model, "%s%c", core_model, 'A' + suffix - 1);480core_model = fuse_model;481} else {482/* Don't have suffix, so just use model from fuses */483sprintf(fuse_model, "%d", model);484core_model = "";485family = fuse_model;486}487}488}489sprintf(buffer, "CN%s%sp%s-%d-%s", family, core_model, pass, clock_mhz, suffix);490return buffer;491}492493/**494* Given the chip processor ID from COP0, this function returns a495* string representing the chip model number. The string is of the496* form CNXXXXpX.X-FREQ-SUFFIX.497* - XXXX = The chip model number498* - X.X = Chip pass number499* - FREQ = Current frequency in Mhz500* - SUFFIX = NSP, EXP, SCP, SSP, or CP501*502* @chip_id: Chip ID503*504* Returns Model string505*/506const char *__init octeon_model_get_string(uint32_t chip_id)507{508static char buffer[32];509return octeon_model_get_string_buffer(chip_id, buffer);510}511512513