1 /***********************license start*************** 2 * Author: Cavium Networks 3 * 4 * Contact: support@caviumnetworks.com 5 * This file is part of the OCTEON SDK 6 * 7 * Copyright (c) 2003-2010 Cavium Networks 8 * 9 * This file is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License, Version 2, as 11 * published by the Free Software Foundation. 12 * 13 * This file is distributed in the hope that it will be useful, but 14 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty 15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or 16 * NONINFRINGEMENT. See the GNU General Public License for more 17 * details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this file; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 * or visit http://www.gnu.org/licenses/. 23 * 24 * This file may also be available under a different license from Cavium. 25 * Contact Cavium Networks for more information 26 ***********************license end**************************************/ 27 28 #include <asm/octeon/octeon.h> 29 30 /** 31 * Read a byte of fuse data 32 * @byte_addr: address to read 33 * 34 * Returns fuse value: 0 or 1 35 */ 36 uint8_t cvmx_fuse_read_byte(int byte_addr) 37 { 38 union cvmx_mio_fus_rcmd read_cmd; 39 40 read_cmd.u64 = 0; 41 read_cmd.s.addr = byte_addr; 42 read_cmd.s.pend = 1; 43 cvmx_write_csr(CVMX_MIO_FUS_RCMD, read_cmd.u64); 44 while ((read_cmd.u64 = cvmx_read_csr(CVMX_MIO_FUS_RCMD)) 45 && read_cmd.s.pend) 46 ; 47 return read_cmd.s.dat; 48 } 49 50 /** 51 * Given the chip processor ID from COP0, this function returns a 52 * string representing the chip model number. The string is of the 53 * form CNXXXXpX.X-FREQ-SUFFIX. 54 * - XXXX = The chip model number 55 * - X.X = Chip pass number 56 * - FREQ = Current frequency in Mhz 57 * - SUFFIX = NSP, EXP, SCP, SSP, or CP 58 * 59 * @chip_id: Chip ID 60 * 61 * Returns Model string 62 */ 63 const char *octeon_model_get_string(uint32_t chip_id) 64 { 65 static char buffer[32]; 66 return octeon_model_get_string_buffer(chip_id, buffer); 67 } 68 69 /* 70 * Version of octeon_model_get_string() that takes buffer as argument, 71 * as running early in u-boot static/global variables don't work when 72 * running from flash. 73 */ 74 const char *octeon_model_get_string_buffer(uint32_t chip_id, char *buffer) 75 { 76 const char *family; 77 const char *core_model; 78 char pass[4]; 79 int clock_mhz; 80 const char *suffix; 81 union cvmx_l2d_fus3 fus3; 82 int num_cores; 83 union cvmx_mio_fus_dat2 fus_dat2; 84 union cvmx_mio_fus_dat3 fus_dat3; 85 char fuse_model[10]; 86 uint32_t fuse_data = 0; 87 88 fus3.u64 = 0; 89 if (!OCTEON_IS_MODEL(OCTEON_CN6XXX)) 90 fus3.u64 = cvmx_read_csr(CVMX_L2D_FUS3); 91 fus_dat2.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT2); 92 fus_dat3.u64 = cvmx_read_csr(CVMX_MIO_FUS_DAT3); 93 num_cores = cvmx_pop(cvmx_read_csr(CVMX_CIU_FUSE)); 94 95 /* Make sure the non existent devices look disabled */ 96 switch ((chip_id >> 8) & 0xff) { 97 case 6: /* CN50XX */ 98 case 2: /* CN30XX */ 99 fus_dat3.s.nodfa_dte = 1; 100 fus_dat3.s.nozip = 1; 101 break; 102 case 4: /* CN57XX or CN56XX */ 103 fus_dat3.s.nodfa_dte = 1; 104 break; 105 default: 106 break; 107 } 108 109 /* Make a guess at the suffix */ 110 /* NSP = everything */ 111 /* EXP = No crypto */ 112 /* SCP = No DFA, No zip */ 113 /* CP = No DFA, No crypto, No zip */ 114 if (fus_dat3.s.nodfa_dte) { 115 if (fus_dat2.s.nocrypto) 116 suffix = "CP"; 117 else 118 suffix = "SCP"; 119 } else if (fus_dat2.s.nocrypto) 120 suffix = "EXP"; 121 else 122 suffix = "NSP"; 123 124 /* 125 * Assume pass number is encoded using <5:3><2:0>. Exceptions 126 * will be fixed later. 127 */ 128 sprintf(pass, "%d.%d", (int)((chip_id >> 3) & 7) + 1, (int)chip_id & 7); 129 130 /* 131 * Use the number of cores to determine the last 2 digits of 132 * the model number. There are some exceptions that are fixed 133 * later. 134 */ 135 switch (num_cores) { 136 case 32: 137 core_model = "80"; 138 break; 139 case 24: 140 core_model = "70"; 141 break; 142 case 16: 143 core_model = "60"; 144 break; 145 case 15: 146 core_model = "58"; 147 break; 148 case 14: 149 core_model = "55"; 150 break; 151 case 13: 152 core_model = "52"; 153 break; 154 case 12: 155 core_model = "50"; 156 break; 157 case 11: 158 core_model = "48"; 159 break; 160 case 10: 161 core_model = "45"; 162 break; 163 case 9: 164 core_model = "42"; 165 break; 166 case 8: 167 core_model = "40"; 168 break; 169 case 7: 170 core_model = "38"; 171 break; 172 case 6: 173 core_model = "34"; 174 break; 175 case 5: 176 core_model = "32"; 177 break; 178 case 4: 179 core_model = "30"; 180 break; 181 case 3: 182 core_model = "25"; 183 break; 184 case 2: 185 core_model = "20"; 186 break; 187 case 1: 188 core_model = "10"; 189 break; 190 default: 191 core_model = "XX"; 192 break; 193 } 194 195 /* Now figure out the family, the first two digits */ 196 switch ((chip_id >> 8) & 0xff) { 197 case 0: /* CN38XX, CN37XX or CN36XX */ 198 if (fus3.cn38xx.crip_512k) { 199 /* 200 * For some unknown reason, the 16 core one is 201 * called 37 instead of 36. 202 */ 203 if (num_cores >= 16) 204 family = "37"; 205 else 206 family = "36"; 207 } else 208 family = "38"; 209 /* 210 * This series of chips didn't follow the standard 211 * pass numbering. 212 */ 213 switch (chip_id & 0xf) { 214 case 0: 215 strcpy(pass, "1.X"); 216 break; 217 case 1: 218 strcpy(pass, "2.X"); 219 break; 220 case 3: 221 strcpy(pass, "3.X"); 222 break; 223 default: 224 strcpy(pass, "X.X"); 225 break; 226 } 227 break; 228 case 1: /* CN31XX or CN3020 */ 229 if ((chip_id & 0x10) || fus3.cn31xx.crip_128k) 230 family = "30"; 231 else 232 family = "31"; 233 /* 234 * This series of chips didn't follow the standard 235 * pass numbering. 236 */ 237 switch (chip_id & 0xf) { 238 case 0: 239 strcpy(pass, "1.0"); 240 break; 241 case 2: 242 strcpy(pass, "1.1"); 243 break; 244 default: 245 strcpy(pass, "X.X"); 246 break; 247 } 248 break; 249 case 2: /* CN3010 or CN3005 */ 250 family = "30"; 251 /* A chip with half cache is an 05 */ 252 if (fus3.cn30xx.crip_64k) 253 core_model = "05"; 254 /* 255 * This series of chips didn't follow the standard 256 * pass numbering. 257 */ 258 switch (chip_id & 0xf) { 259 case 0: 260 strcpy(pass, "1.0"); 261 break; 262 case 2: 263 strcpy(pass, "1.1"); 264 break; 265 default: 266 strcpy(pass, "X.X"); 267 break; 268 } 269 break; 270 case 3: /* CN58XX */ 271 family = "58"; 272 /* Special case. 4 core, half cache (CP with half cache) */ 273 if ((num_cores == 4) && fus3.cn58xx.crip_1024k && !strncmp(suffix, "CP", 2)) 274 core_model = "29"; 275 276 /* Pass 1 uses different encodings for pass numbers */ 277 if ((chip_id & 0xFF) < 0x8) { 278 switch (chip_id & 0x3) { 279 case 0: 280 strcpy(pass, "1.0"); 281 break; 282 case 1: 283 strcpy(pass, "1.1"); 284 break; 285 case 3: 286 strcpy(pass, "1.2"); 287 break; 288 default: 289 strcpy(pass, "1.X"); 290 break; 291 } 292 } 293 break; 294 case 4: /* CN57XX, CN56XX, CN55XX, CN54XX */ 295 if (fus_dat2.cn56xx.raid_en) { 296 if (fus3.cn56xx.crip_1024k) 297 family = "55"; 298 else 299 family = "57"; 300 if (fus_dat2.cn56xx.nocrypto) 301 suffix = "SP"; 302 else 303 suffix = "SSP"; 304 } else { 305 if (fus_dat2.cn56xx.nocrypto) 306 suffix = "CP"; 307 else { 308 suffix = "NSP"; 309 if (fus_dat3.s.nozip) 310 suffix = "SCP"; 311 312 if (fus_dat3.s.bar2_en) 313 suffix = "NSPB2"; 314 } 315 if (fus3.cn56xx.crip_1024k) 316 family = "54"; 317 else 318 family = "56"; 319 } 320 break; 321 case 6: /* CN50XX */ 322 family = "50"; 323 break; 324 case 7: /* CN52XX */ 325 if (fus3.cn52xx.crip_256k) 326 family = "51"; 327 else 328 family = "52"; 329 break; 330 case 0x93: /* CN61XX */ 331 family = "61"; 332 if (fus_dat2.cn61xx.nocrypto && fus_dat2.cn61xx.dorm_crypto) 333 suffix = "AP"; 334 if (fus_dat2.cn61xx.nocrypto) 335 suffix = "CP"; 336 else if (fus_dat2.cn61xx.dorm_crypto) 337 suffix = "DAP"; 338 else if (fus_dat3.cn61xx.nozip) 339 suffix = "SCP"; 340 break; 341 case 0x90: /* CN63XX */ 342 family = "63"; 343 if (fus_dat3.s.l2c_crip == 2) 344 family = "62"; 345 if (num_cores == 6) /* Other core counts match generic */ 346 core_model = "35"; 347 if (fus_dat2.cn63xx.nocrypto) 348 suffix = "CP"; 349 else if (fus_dat2.cn63xx.dorm_crypto) 350 suffix = "DAP"; 351 else if (fus_dat3.cn63xx.nozip) 352 suffix = "SCP"; 353 else 354 suffix = "AAP"; 355 break; 356 case 0x92: /* CN66XX */ 357 family = "66"; 358 if (num_cores == 6) /* Other core counts match generic */ 359 core_model = "35"; 360 if (fus_dat2.cn66xx.nocrypto && fus_dat2.cn66xx.dorm_crypto) 361 suffix = "AP"; 362 if (fus_dat2.cn66xx.nocrypto) 363 suffix = "CP"; 364 else if (fus_dat2.cn66xx.dorm_crypto) 365 suffix = "DAP"; 366 else if (fus_dat3.cn66xx.nozip) 367 suffix = "SCP"; 368 else 369 suffix = "AAP"; 370 break; 371 case 0x91: /* CN68XX */ 372 family = "68"; 373 if (fus_dat2.cn68xx.nocrypto && fus_dat3.cn68xx.nozip) 374 suffix = "CP"; 375 else if (fus_dat2.cn68xx.dorm_crypto) 376 suffix = "DAP"; 377 else if (fus_dat3.cn68xx.nozip) 378 suffix = "SCP"; 379 else if (fus_dat2.cn68xx.nocrypto) 380 suffix = "SP"; 381 else 382 suffix = "AAP"; 383 break; 384 default: 385 family = "XX"; 386 core_model = "XX"; 387 strcpy(pass, "X.X"); 388 suffix = "XXX"; 389 break; 390 } 391 392 clock_mhz = octeon_get_clock_rate() / 1000000; 393 if (family[0] != '3') { 394 int fuse_base = 384 / 8; 395 if (family[0] == '6') 396 fuse_base = 832 / 8; 397 398 /* Check for model in fuses, overrides normal decode */ 399 /* This is _not_ valid for Octeon CN3XXX models */ 400 fuse_data |= cvmx_fuse_read_byte(fuse_base + 3); 401 fuse_data = fuse_data << 8; 402 fuse_data |= cvmx_fuse_read_byte(fuse_base + 2); 403 fuse_data = fuse_data << 8; 404 fuse_data |= cvmx_fuse_read_byte(fuse_base + 1); 405 fuse_data = fuse_data << 8; 406 fuse_data |= cvmx_fuse_read_byte(fuse_base); 407 if (fuse_data & 0x7ffff) { 408 int model = fuse_data & 0x3fff; 409 int suffix = (fuse_data >> 14) & 0x1f; 410 if (suffix && model) { 411 /* Have both number and suffix in fuses, so both */ 412 sprintf(fuse_model, "%d%c", model, 'A' + suffix - 1); 413 core_model = ""; 414 family = fuse_model; 415 } else if (suffix && !model) { 416 /* Only have suffix, so add suffix to 'normal' model number */ 417 sprintf(fuse_model, "%s%c", core_model, 'A' + suffix - 1); 418 core_model = fuse_model; 419 } else { 420 /* Don't have suffix, so just use model from fuses */ 421 sprintf(fuse_model, "%d", model); 422 core_model = ""; 423 family = fuse_model; 424 } 425 } 426 } 427 sprintf(buffer, "CN%s%sp%s-%d-%s", family, core_model, pass, clock_mhz, suffix); 428 return buffer; 429 } 430