Path: blob/master/arch/mips/cavium-octeon/executive/octeon-model.c
29537 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 <linux/string.h>28#include <asm/octeon/octeon.h>2930enum octeon_feature_bits __octeon_feature_bits __read_mostly;31EXPORT_SYMBOL_GPL(__octeon_feature_bits);3233/**34* Read a byte of fuse data35* @byte_addr: address to read36*37* Returns fuse value: 0 or 138*/39static uint8_t __init cvmx_fuse_read_byte(int byte_addr)40{41union cvmx_mio_fus_rcmd read_cmd;4243read_cmd.u64 = 0;44read_cmd.s.addr = byte_addr;45read_cmd.s.pend = 1;46cvmx_write_csr(CVMX_MIO_FUS_RCMD, read_cmd.u64);47while ((read_cmd.u64 = cvmx_read_csr(CVMX_MIO_FUS_RCMD))48&& read_cmd.s.pend)49;50return read_cmd.s.dat;51}5253/*54* Version of octeon_model_get_string() that takes buffer as argument,55* as running early in u-boot static/global variables don't work when56* running from flash.57*/58static const char *__init octeon_model_get_string_buffer(uint32_t chip_id,59char *buffer)60{61const char *family;62const char *core_model;63char pass[4];64int clock_mhz;65const char *suffix;66int num_cores;67union cvmx_mio_fus_dat2 fus_dat2;68union cvmx_mio_fus_dat3 fus_dat3;69char fuse_model[10];70uint32_t fuse_data = 0;71uint64_t l2d_fus3 = 0;7273if (OCTEON_IS_MODEL(OCTEON_CN3XXX) || OCTEON_IS_MODEL(OCTEON_CN5XXX))74l2d_fus3 = (cvmx_read_csr(CVMX_L2D_FUS3) >> 34) & 0x3;75fus_dat2.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT2);76fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3);77num_cores = cvmx_octeon_num_cores();7879/* Make sure the non existent devices look disabled */80switch ((chip_id >> 8) & 0xff) {81case 6: /* CN50XX */82case 2: /* CN30XX */83fus_dat3.s.nodfa_dte = 1;84fus_dat3.s.nozip = 1;85break;86case 4: /* CN57XX or CN56XX */87fus_dat3.s.nodfa_dte = 1;88break;89default:90break;91}9293/* Make a guess at the suffix */94/* NSP = everything */95/* EXP = No crypto */96/* SCP = No DFA, No zip */97/* CP = No DFA, No crypto, No zip */98if (fus_dat3.s.nodfa_dte) {99if (fus_dat2.s.nocrypto)100suffix = "CP";101else102suffix = "SCP";103} else if (fus_dat2.s.nocrypto)104suffix = "EXP";105else106suffix = "NSP";107108if (!fus_dat2.s.nocrypto)109__octeon_feature_bits |= OCTEON_HAS_CRYPTO;110111/*112* Assume pass number is encoded using <5:3><2:0>. Exceptions113* will be fixed later.114*/115sprintf(pass, "%d.%d", (int)((chip_id >> 3) & 7) + 1, (int)chip_id & 7);116117/*118* Use the number of cores to determine the last 2 digits of119* the model number. There are some exceptions that are fixed120* later.121*/122switch (num_cores) {123case 48:124core_model = "90";125break;126case 44:127core_model = "88";128break;129case 40:130core_model = "85";131break;132case 32:133core_model = "80";134break;135case 24:136core_model = "70";137break;138case 16:139core_model = "60";140break;141case 15:142core_model = "58";143break;144case 14:145core_model = "55";146break;147case 13:148core_model = "52";149break;150case 12:151core_model = "50";152break;153case 11:154core_model = "48";155break;156case 10:157core_model = "45";158break;159case 9:160core_model = "42";161break;162case 8:163core_model = "40";164break;165case 7:166core_model = "38";167break;168case 6:169core_model = "34";170break;171case 5:172core_model = "32";173break;174case 4:175core_model = "30";176break;177case 3:178core_model = "25";179break;180case 2:181core_model = "20";182break;183case 1:184core_model = "10";185break;186default:187core_model = "XX";188break;189}190191/* Now figure out the family, the first two digits */192switch ((chip_id >> 8) & 0xff) {193case 0: /* CN38XX, CN37XX or CN36XX */194if (l2d_fus3) {195/*196* For some unknown reason, the 16 core one is197* called 37 instead of 36.198*/199if (num_cores >= 16)200family = "37";201else202family = "36";203} else204family = "38";205/*206* This series of chips didn't follow the standard207* pass numbering.208*/209switch (chip_id & 0xf) {210case 0:211strscpy(pass, "1.X");212break;213case 1:214strscpy(pass, "2.X");215break;216case 3:217strscpy(pass, "3.X");218break;219default:220strscpy(pass, "X.X");221break;222}223break;224case 1: /* CN31XX or CN3020 */225if ((chip_id & 0x10) || l2d_fus3)226family = "30";227else228family = "31";229/*230* This series of chips didn't follow the standard231* pass numbering.232*/233switch (chip_id & 0xf) {234case 0:235strscpy(pass, "1.0");236break;237case 2:238strscpy(pass, "1.1");239break;240default:241strscpy(pass, "X.X");242break;243}244break;245case 2: /* CN3010 or CN3005 */246family = "30";247/* A chip with half cache is an 05 */248if (l2d_fus3)249core_model = "05";250/*251* This series of chips didn't follow the standard252* pass numbering.253*/254switch (chip_id & 0xf) {255case 0:256strscpy(pass, "1.0");257break;258case 2:259strscpy(pass, "1.1");260break;261default:262strscpy(pass, "X.X");263break;264}265break;266case 3: /* CN58XX */267family = "58";268/* Special case. 4 core, half cache (CP with half cache) */269if ((num_cores == 4) && l2d_fus3 && !strncmp(suffix, "CP", 2))270core_model = "29";271272/* Pass 1 uses different encodings for pass numbers */273if ((chip_id & 0xFF) < 0x8) {274switch (chip_id & 0x3) {275case 0:276strscpy(pass, "1.0");277break;278case 1:279strscpy(pass, "1.1");280break;281case 3:282strscpy(pass, "1.2");283break;284default:285strscpy(pass, "1.X");286break;287}288}289break;290case 4: /* CN57XX, CN56XX, CN55XX, CN54XX */291if (fus_dat2.cn56xx.raid_en) {292if (l2d_fus3)293family = "55";294else295family = "57";296if (fus_dat2.cn56xx.nocrypto)297suffix = "SP";298else299suffix = "SSP";300} else {301if (fus_dat2.cn56xx.nocrypto)302suffix = "CP";303else {304suffix = "NSP";305if (fus_dat3.s.nozip)306suffix = "SCP";307308if (fus_dat3.cn38xx.bar2_en)309suffix = "NSPB2";310}311if (l2d_fus3)312family = "54";313else314family = "56";315}316break;317case 6: /* CN50XX */318family = "50";319break;320case 7: /* CN52XX */321if (l2d_fus3)322family = "51";323else324family = "52";325break;326case 0x93: /* CN61XX */327family = "61";328if (fus_dat2.cn61xx.nocrypto && fus_dat2.cn61xx.dorm_crypto)329suffix = "AP";330if (fus_dat2.cn61xx.nocrypto)331suffix = "CP";332else if (fus_dat2.cn61xx.dorm_crypto)333suffix = "DAP";334else if (fus_dat3.cn61xx.nozip)335suffix = "SCP";336break;337case 0x90: /* CN63XX */338family = "63";339if (fus_dat3.s.l2c_crip == 2)340family = "62";341if (num_cores == 6) /* Other core counts match generic */342core_model = "35";343if (fus_dat2.cn63xx.nocrypto)344suffix = "CP";345else if (fus_dat2.cn63xx.dorm_crypto)346suffix = "DAP";347else if (fus_dat3.cn61xx.nozip)348suffix = "SCP";349else350suffix = "AAP";351break;352case 0x92: /* CN66XX */353family = "66";354if (num_cores == 6) /* Other core counts match generic */355core_model = "35";356if (fus_dat2.cn66xx.nocrypto && fus_dat2.cn66xx.dorm_crypto)357suffix = "AP";358if (fus_dat2.cn66xx.nocrypto)359suffix = "CP";360else if (fus_dat2.cn66xx.dorm_crypto)361suffix = "DAP";362else if (fus_dat3.cn61xx.nozip)363suffix = "SCP";364else365suffix = "AAP";366break;367case 0x91: /* CN68XX */368family = "68";369if (fus_dat2.cn68xx.nocrypto && fus_dat3.cn61xx.nozip)370suffix = "CP";371else if (fus_dat2.cn68xx.dorm_crypto)372suffix = "DAP";373else if (fus_dat3.cn61xx.nozip)374suffix = "SCP";375else if (fus_dat2.cn68xx.nocrypto)376suffix = "SP";377else378suffix = "AAP";379break;380case 0x94: /* CNF71XX */381family = "F71";382if (fus_dat3.cn61xx.nozip)383suffix = "SCP";384else385suffix = "AAP";386break;387case 0x95: /* CN78XX */388if (num_cores == 6) /* Other core counts match generic */389core_model = "35";390if (OCTEON_IS_MODEL(OCTEON_CN76XX))391family = "76";392else393family = "78";394if (fus_dat3.cn78xx.l2c_crip == 2)395family = "77";396if (fus_dat3.cn78xx.nozip397&& fus_dat3.cn78xx.nodfa_dte398&& fus_dat3.cn78xx.nohna_dte) {399if (fus_dat3.cn78xx.nozip &&400!fus_dat2.cn78xx.raid_en &&401fus_dat3.cn78xx.nohna_dte) {402suffix = "CP";403} else {404suffix = "SCP";405}406} else if (fus_dat2.cn78xx.raid_en == 0)407suffix = "HCP";408else409suffix = "AAP";410break;411case 0x96: /* CN70XX */412family = "70";413if (cvmx_read_csr(CVMX_MIO_FUS_PDF) & (0x1ULL << 32))414family = "71";415if (fus_dat2.cn70xx.nocrypto)416suffix = "CP";417else if (fus_dat3.cn70xx.nodfa_dte)418suffix = "SCP";419else420suffix = "AAP";421break;422case 0x97: /* CN73XX */423if (num_cores == 6) /* Other core counts match generic */424core_model = "35";425family = "73";426if (fus_dat3.cn73xx.l2c_crip == 2)427family = "72";428if (fus_dat3.cn73xx.nozip429&& fus_dat3.cn73xx.nodfa_dte430&& fus_dat3.cn73xx.nohna_dte) {431if (!fus_dat2.cn73xx.raid_en)432suffix = "CP";433else434suffix = "SCP";435} else436suffix = "AAP";437break;438case 0x98: /* CN75XX */439family = "F75";440if (fus_dat3.cn78xx.nozip441&& fus_dat3.cn78xx.nodfa_dte442&& fus_dat3.cn78xx.nohna_dte)443suffix = "SCP";444else445suffix = "AAP";446break;447default:448family = "XX";449core_model = "XX";450strscpy(pass, "X.X");451suffix = "XXX";452break;453}454455clock_mhz = octeon_get_clock_rate() / 1000000;456if (family[0] != '3') {457int fuse_base = 384 / 8;458if (family[0] == '6')459fuse_base = 832 / 8;460461/* Check for model in fuses, overrides normal decode */462/* This is _not_ valid for Octeon CN3XXX models */463fuse_data |= cvmx_fuse_read_byte(fuse_base + 3);464fuse_data = fuse_data << 8;465fuse_data |= cvmx_fuse_read_byte(fuse_base + 2);466fuse_data = fuse_data << 8;467fuse_data |= cvmx_fuse_read_byte(fuse_base + 1);468fuse_data = fuse_data << 8;469fuse_data |= cvmx_fuse_read_byte(fuse_base);470if (fuse_data & 0x7ffff) {471int model = fuse_data & 0x3fff;472int suffix = (fuse_data >> 14) & 0x1f;473if (suffix && model) {474/* Have both number and suffix in fuses, so both */475sprintf(fuse_model, "%d%c", model, 'A' + suffix - 1);476core_model = "";477family = fuse_model;478} else if (suffix && !model) {479/* Only have suffix, so add suffix to 'normal' model number */480sprintf(fuse_model, "%s%c", core_model, 'A' + suffix - 1);481core_model = fuse_model;482} else {483/* Don't have suffix, so just use model from fuses */484sprintf(fuse_model, "%d", model);485core_model = "";486family = fuse_model;487}488}489}490sprintf(buffer, "CN%s%sp%s-%d-%s", family, core_model, pass, clock_mhz, suffix);491return buffer;492}493494/**495* Given the chip processor ID from COP0, this function returns a496* string representing the chip model number. The string is of the497* form CNXXXXpX.X-FREQ-SUFFIX.498* - XXXX = The chip model number499* - X.X = Chip pass number500* - FREQ = Current frequency in Mhz501* - SUFFIX = NSP, EXP, SCP, SSP, or CP502*503* @chip_id: Chip ID504*505* Returns Model string506*/507const char *__init octeon_model_get_string(uint32_t chip_id)508{509static char buffer[32];510return octeon_model_get_string_buffer(chip_id, buffer);511}512513514