tscf_gen3_sim.c (17756B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * 8 * This software simulator can emulate basic register access for the 9 * TSC/Eagle SerDes PHY. 10 * 11 * The simulator suppor both IEEE clause 22/45 access and Broadcom 12 * proprietary SBUS access. 13 * 14 * Clause 22 address format: 15 * Bits [4:0] : Clause 22 register address 16 * Bits [31:5] : Unused 17 * 18 * Clause 45 address format: 19 * Bits [15:0] : Clause 45 register address 20 * Bits [20:16] : Clause 45 DEVAD 21 * Bits [23:21] : Clause 45 indicator (001b) 22 * Bits [31:24] : Unused 23 * 24 * SBUS address format: 25 * Bits [15:0] : Clause 45 register address 26 * Bits [18:16] : Lane control 27 * Bits [26:19] : Lane multicast (old format) 28 * Bits [31:27] : Clause 45 DEVAD 29 * 30 * The upper 16 bits if the SBUS address format is identical to the 31 * Broadcom Address Extension Register (AER) format. 32 * 33 * The clause 45 indicator serves two purposes which is to ensure that 34 * the upper 16 bits are never zero for a clause 45 address, but it 35 * also makes it possible for the PHY bus driver to distinguish 36 * between a clause 45 DEVAD and the old AER multicast format. 37 */ 38 39 #include <phymod/phymod_system.h> 40 #include <phymod/phymod_sim.h> 41 42 /* Convenience macro */ 43 #define DBG_VERB PHYMOD_DEBUG_VERBOSE 44 45 /* Bit field get/set macros */ 46 #define TSCF_GEN3_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift)) 47 #define TSCF_GEN3_BF_GET(_val, _mask, _shift) (((_val) >> (_shift)) & (_mask)) 48 49 /* 50 * Raw 32-bit address consists of AER value in upper 16 bits and 51 * clause 45 address in lower 16 bits. 52 */ 53 #define TSCF_GEN3_DEVAD_SHIFT 27 54 #define TSCF_GEN3_DEVAD_MASK 0x1f 55 #define TSCF_GEN3_DEVAD_GET(_addr) \ 56 TSCF_GEN3_BF_GET(_addr, TSCF_GEN3_DEVAD_MASK, TSCF_GEN3_DEVAD_SHIFT) 57 #define TSCF_GEN3_LANE_SHIFT 16 58 #define TSCF_GEN3_LANE_MASK 0x7 59 #define TSCF_GEN3_LANE_GET(_addr) \ 60 TSCF_GEN3_BF_GET(_addr, TSCF_GEN3_LANE_MASK, TSCF_GEN3_LANE_SHIFT) 61 #define TSCF_GEN3_REG_SHIFT 0 62 #define TSCF_GEN3_REG_MASK 0xffff 63 #define TSCF_GEN3_REG_GET(_addr) \ 64 TSCF_GEN3_BF_GET(_addr, TSCF_GEN3_REG_MASK, TSCF_GEN3_REG_SHIFT) 65 #define TSCF_GEN3_PLLIDX_SHIFT 24 66 #define TSCF_GEN3_PLLIDX_MASK 0x3 67 #define TSCF_GEN3_PLLIDX_GET(_addr) \ 68 TSCF_GEN3_BF_GET(_addr, TSCF_GEN3_PLLIDX_MASK, TSCF_GEN3_PLLIDX_SHIFT) 69 70 #define TSCF_GEN3_ADDR(_devad, _lane, _reg, _pllidx) \ 71 (((_devad) << TSCF_GEN3_DEVAD_SHIFT) + \ 72 ((_lane) << TSCF_GEN3_LANE_SHIFT) + \ 73 ((_reg) << TSCF_GEN3_REG_SHIFT)) + \ 74 ((_pllidx << TSCF_GEN3_PLLIDX_SHIFT)) 75 76 #define TSCF_GEN3_AER TSCF_GEN3_ADDR(0, 0, 0xffde, 0) 77 #define TSCF_GEN3_BLK TSCF_GEN3_ADDR(0, 0, 0x001f, 0) 78 79 80 /* 81 * The CL45 indicator is used to determine whether the upper 16 bits 82 * of the address is an AER value or a clause 45 DEVAD. 83 */ 84 #define TSCF_GEN3_CL45 (0x20 << 16) 85 #define TSCF_GEN3_CL45_MASK (0xe0 << 16) 86 87 #define TSCF_GEN3_ID0 0x600d 88 #define TSCF_GEN3_ID1 0x8770 89 #define TSCF_GEN3_SERDES_ID 0x02e4 90 #define MONTEREY_TSC_UCODE_IMAGE_CRC 0x7898 91 92 #define TSCF_GEN3_SIM_FW_LOAD_DONE_REG_ADDR (0x800d203) 93 94 /* Forward declarations */ 95 STATIC int 96 tscf_gen3_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data); 97 STATIC int 98 tscf_gen3_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data); 99 100 STATIC uint32_t 101 tscf_gen3_sim_default_data_get(uint32_t addr) 102 { 103 uint32_t devad, reg; 104 105 devad = TSCF_GEN3_DEVAD_GET(addr); 106 reg = TSCF_GEN3_REG_GET(addr); 107 108 /*force FW load success */ 109 if(addr == TSCF_GEN3_SIM_FW_LOAD_DONE_REG_ADDR) { 110 return 1; 111 } 112 113 if (devad == 0){ 114 switch (reg) { 115 case 0x0002: 116 return TSCF_GEN3_ID0; 117 case 0x0003: 118 return TSCF_GEN3_ID1; 119 case 0x900e: 120 return TSCF_GEN3_SERDES_ID; 121 default: 122 break; 123 } 124 } 125 if (devad == 1) { 126 switch (reg){ 127 case 0xd03d: 128 /* 129 * This is DSC_A_DSC_UC_CTRL for Falcon. Make sure 130 * The 'ready' bit is set. 131 */ 132 return 0x0080 ; 133 case 0xd10a: 134 /* DIG_COM_REVID1[15:12], Number of lanes */ 135 return 0x403e; 136 case 0xd188: 137 case 0xd0b9: 138 /* 139 * Reset check 140 */ 141 return 0x7; 142 case 0xd104: 143 return 0x8271; 144 case 0xd170: 145 return 0xb000; 146 default: 147 break; 148 149 } 150 } 151 return 0; 152 } 153 154 STATIC uint32_t 155 tscf_gen3_sim_reg_copies_get(uint32_t addr) 156 { 157 uint32_t devad, reg; 158 159 devad = TSCF_GEN3_DEVAD_GET(addr); 160 reg = TSCF_GEN3_REG_GET(addr); 161 162 if (reg == TSCF_GEN3_AER || reg == TSCF_GEN3_BLK) { 163 return 1; 164 } 165 166 if (devad == 0) { 167 if ((reg & 0xf000) == 0x9000) { 168 return 1; 169 } 170 if ((reg & 0xf000) == 0xa000) { 171 return 2; 172 } 173 return 4; 174 } else if (devad == 1) { 175 return 4; 176 } 177 return 0; 178 } 179 180 STATIC int 181 _tscf_gen3_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 182 { 183 int idx; 184 uint32_t mask; 185 186 phymod_sim_entry_t *pse; 187 188 if (pms_data == NULL || pms_data->entries == NULL) { 189 return PHYMOD_E_INIT; 190 } 191 192 /* Support optional write mask in upper 16 bits */ 193 mask = (data >> 16); 194 if (mask == 0) { 195 mask = 0xffff; 196 } 197 data &= mask; 198 199 /* Check if this register has been written already */ 200 for (idx = 0; idx < pms_data->entries_used; idx++) { 201 pse = &pms_data->entries[idx]; 202 if (pse->addr == addr) { 203 pse->data &= ~mask; 204 pse->data |= data; 205 DBG_VERB(("_tscf_gen3_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n", 206 addr, pse->data)); 207 return PHYMOD_E_NONE; 208 } 209 } 210 211 /* Check if database is full */ 212 if (pms_data->entries_used >= pms_data->num_entries) { 213 return PHYMOD_E_RESOURCE; 214 } 215 216 /* Check if new data matches default value */ 217 if (data == tscf_gen3_sim_default_data_get(addr)) { 218 return PHYMOD_E_NONE; 219 } 220 221 /* Add new register value */ 222 pse = &pms_data->entries[pms_data->entries_used++]; 223 pse->addr = addr; 224 pse->data = data; 225 226 DBG_VERB(("_tscf_gen3_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n", 227 addr, pse->data)); 228 229 return PHYMOD_E_NONE; 230 } 231 232 STATIC uint32_t 233 tscf_gen3_sim_write_adjust(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 234 { 235 uint32_t devad, reg, val; 236 uint32_t pllidx = 0; 237 238 devad = TSCF_GEN3_DEVAD_GET(addr); 239 reg = TSCF_GEN3_REG_GET(addr); 240 pllidx = TSCF_GEN3_PLLIDX_GET(addr); 241 242 if (devad == 0) { 243 switch (reg) { 244 case 0xc050: 245 /* Set SW_SPEED_CHANGE_DONE and SW_SPEED_CONFIG_VLD in status reg */ 246 tscf_gen3_sim_write(pms_data, addr + 1, 0x3); 247 /* Force link up in RX_X4_PCS_LIVE_STS register 248 offset 0x111 = 0xc161 - 0xc050 */ 249 tscf_gen3_sim_write(pms_data, addr + 0x111, 0x2); 250 /* Sync configured speed into final speed status register 251 * 0x20 = 0xc070 - 0xc050 */ 252 tscf_gen3_sim_read(pms_data, addr + 0x20, &val); 253 val = (val & 0xffff00ff) | ((data & 0xff)<<8); 254 tscf_gen3_sim_write(pms_data, addr + 0x20, val); 255 break; 256 case 0xc055: 257 /* Sync configured Lane Number into Lane Number status register 258 * 0x1B = 0xc070 - 0xc055 */ 259 tscf_gen3_sim_read(pms_data, addr + 0x1B, &val); 260 val = (val & 0xfffffff8) | (data & 0x7); 261 tscf_gen3_sim_write(pms_data, addr + 0x1B, val); 262 break; 263 default: 264 break; 265 } 266 } else if (devad == 1) { 267 switch (reg) { 268 case 0xd204: 269 case 0xd205: 270 case 0xd206: 271 case 0xd207: 272 /* Sync configured aMICRO_A_COM registers. Write and read access different register 273 * Write registers: 0xd204~0xd207; Read registers: 0xd208~0xd20b 274 * 0x4 = 0xd208(~0xd20b) - 0xd204(~0xd207)*/ 275 tscf_gen3_sim_write(pms_data, addr + 0x4, data); 276 break; 277 case 0xd0e1: 278 /* Sync configured SIGDET_CTL1 register. When a port is set disable, 279 * the port's RX_X4_PCS_LIVE_STS1 register deletes link status. 280 * RX_X4_PCS_LIVE_STS1 register is PCS register, so need set devad to 0. 281 * 0xF80 = 0xd0e1 - 0xc161*/ 282 tscf_gen3_sim_read(pms_data, (addr - 0xF80) & 0x7ffff, &val); 283 val = (val & 0xfffd) | (((data&0x80)==0x80)?0:0x2); 284 tscf_gen3_sim_write(pms_data, (addr - 0xF80) & 0x7ffff, val); 285 break; 286 case 0xd147: 287 /* Sync configured all lanes' PLL_CAL_CTL7 register. Only one PLL/VCO per core */ 288 /* Write lanes 0, 1, 2 and 3 */ 289 addr = TSCF_GEN3_ADDR(devad, 0, reg, pllidx); 290 (void)_tscf_gen3_sim_write(pms_data, addr, data); 291 292 addr = TSCF_GEN3_ADDR(devad, 1, reg, pllidx); 293 (void)_tscf_gen3_sim_write(pms_data, addr, data); 294 295 addr = TSCF_GEN3_ADDR(devad, 2, reg, pllidx); 296 (void)_tscf_gen3_sim_write(pms_data, addr, data); 297 298 addr = TSCF_GEN3_ADDR(devad, 3, reg, pllidx); 299 (void)_tscf_gen3_sim_write(pms_data, addr, data); 300 break; 301 case 0xd094: 302 case 0xd095: 303 /* Sync configured CL93N72 Control 2/3 registers. Write and read access different register 304 * Write registers: 0xd094/d095; Read registers: 0xd133/0xd134 305 * 0x9f = 0xd133(0xd134) - 0xd094(0xd095)*/ 306 tscf_gen3_sim_write(pms_data, addr + 0x9f, data); 307 break; 308 default: 309 break; 310 } 311 } 312 313 return data; 314 } 315 316 STATIC int 317 tscf_gen3_sim_init(phymod_sim_data_t *pms_data, 318 int num_entries, phymod_sim_entry_t *entries) 319 { 320 if (pms_data != NULL) { 321 PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data)); 322 pms_data->num_entries = num_entries; 323 pms_data->entries = entries; 324 } 325 return PHYMOD_E_NONE; 326 } 327 328 STATIC int 329 tscf_gen3_sim_reset(phymod_sim_data_t *pms_data) 330 { 331 uint32_t sim_size; 332 333 if (pms_data == NULL || pms_data->entries == NULL) { 334 return PHYMOD_E_INIT; 335 } 336 337 pms_data->entries_used = 0; 338 sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t); 339 PHYMOD_MEMSET(pms_data->entries, 0, sim_size); 340 341 return PHYMOD_E_NONE; 342 } 343 344 STATIC int 345 tscf_gen3_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data) 346 { 347 int idx; 348 uint32_t aer, blk, devad, reg, copies; 349 uint32_t lane = 0, pllidx = 0; 350 phymod_sim_entry_t *pse; 351 352 if (pms_data == NULL || pms_data->entries == NULL) { 353 return PHYMOD_E_INIT; 354 } 355 356 devad = 0; 357 358 if (addr < TSCF_GEN3_BLK) { 359 /* Assume clause 22 access */ 360 (void)tscf_gen3_sim_read(pms_data, TSCF_GEN3_BLK, &blk); 361 /* IEEE bit */ 362 if (addr & 0x10) { 363 blk |= 0x8000; 364 } else { 365 blk &= ~0x8000; 366 } 367 addr = (addr & 0xf) | (blk & 0xfff0); 368 if (addr != TSCF_GEN3_AER && addr != TSCF_GEN3_BLK) { 369 (void)tscf_gen3_sim_read(pms_data, TSCF_GEN3_AER, &aer); 370 addr |= (aer << 16); 371 } 372 } else { 373 /* Extract devad if clause 45 address format */ 374 if ((addr & TSCF_GEN3_CL45_MASK) == TSCF_GEN3_CL45) { 375 devad = (addr >> 16) & 0x1f; 376 addr &= 0xffff; 377 } 378 } 379 380 if (addr != TSCF_GEN3_AER && addr != TSCF_GEN3_BLK) { 381 /* Assume AER is in upper 16 bits */ 382 aer = (addr >> 16); 383 if (aer == 0) { 384 /* Try reading real AER instead */ 385 (void)tscf_gen3_sim_read(pms_data, TSCF_GEN3_AER, &aer); 386 } 387 /* Add clause 45 devad (if used) */ 388 if (devad) { 389 aer |= (devad << 11); 390 addr = (addr & 0xffff) | (aer << 16); 391 } 392 lane = (aer & 0x7); 393 if (lane > 3) { 394 /* Force lane 0 if lane is invalid */ 395 addr = TSCF_GEN3_ADDR(TSCF_GEN3_DEVAD_GET(addr), 0, TSCF_GEN3_REG_GET(addr), TSCF_GEN3_PLLIDX_GET(addr)); 396 } 397 } 398 399 /* Adjust lane according to number of copies */ 400 devad = TSCF_GEN3_DEVAD_GET(addr); 401 reg = TSCF_GEN3_REG_GET(addr); 402 pllidx = TSCF_GEN3_PLLIDX_GET(addr); 403 copies = tscf_gen3_sim_reg_copies_get(addr); 404 if (copies == 1) { 405 lane = 0; 406 } else if (copies == 2) { 407 lane &= ~0x1; 408 } 409 addr = TSCF_GEN3_ADDR(devad, lane, reg, pllidx); 410 411 /* for DSC_UC_CTRL always succeed */ 412 if(addr == 0x800d03d) { 413 *data = 0x80; 414 DBG_VERB(("tscf_gen3_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 415 addr, *data)); 416 return PHYMOD_E_NONE; 417 } else if (addr == 0x800d03e) { 418 *data = MONTEREY_TSC_UCODE_IMAGE_CRC; 419 return PHYMOD_E_NONE; 420 } else if (addr == 0x800d104) { 421 *data = 0x8271; 422 return PHYMOD_E_NONE; 423 } else if (addr == 0x800d201) { 424 *data = 0; 425 return PHYMOD_E_NONE; 426 } 427 428 /* Check if this register has been written already */ 429 for (idx = 0; idx < pms_data->entries_used; idx++) { 430 pse = &pms_data->entries[idx]; 431 if (pse->addr == addr) { 432 *data = pse->data; 433 DBG_VERB(("tscf_gen3_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 434 addr, *data)); 435 return PHYMOD_E_NONE; 436 } 437 } 438 439 /* Return default value if register was never written */ 440 *data = tscf_gen3_sim_default_data_get(addr); 441 442 DBG_VERB(("tscf_gen3_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n", 443 addr, *data)); 444 445 return PHYMOD_E_NONE; 446 } 447 448 STATIC int 449 tscf_gen3_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 450 { 451 uint32_t aer, blk, devad, reg, copies; 452 uint32_t lane = 0, pllidx = 0 ; 453 454 if (pms_data == NULL || pms_data->entries == NULL) { 455 return PHYMOD_E_INIT; 456 } 457 458 devad = 0; 459 460 if (addr < TSCF_GEN3_BLK) { 461 /* Assume clause 22 access */ 462 (void)tscf_gen3_sim_read(pms_data, TSCF_GEN3_BLK, &blk); 463 /* IEEE bit */ 464 if (addr & 0x10) { 465 blk |= 0x8000; 466 } else { 467 blk &= ~0x8000; 468 } 469 addr = (addr & 0xf) | (blk & 0xfff0); 470 if (addr != TSCF_GEN3_AER && addr != TSCF_GEN3_BLK) { 471 (void)tscf_gen3_sim_read(pms_data, TSCF_GEN3_AER, &aer); 472 addr |= (aer << 16); 473 } 474 } else { 475 /* Extract devad if clause 45 address format */ 476 if ((addr & TSCF_GEN3_CL45_MASK) == TSCF_GEN3_CL45) { 477 devad = (addr >> 16) & 0x1f; 478 addr &= 0xffff; 479 } 480 } 481 482 if (addr != TSCF_GEN3_AER && addr != TSCF_GEN3_BLK) { 483 /* Assume AER is in upper 16 bits */ 484 aer = (addr >> 16); 485 if (aer == 0) { 486 /* Try reading real AER instead */ 487 (void)tscf_gen3_sim_read(pms_data, TSCF_GEN3_AER, &aer); 488 } 489 /* Add clause 45 devad (if used) */ 490 if (devad) { 491 aer |= (devad << 11); 492 addr = (addr & 0xffff) | (aer << 16); 493 } 494 lane = (aer & 0x7); 495 if (lane > 6) { 496 return PHYMOD_E_PARAM; 497 } 498 if (lane > 3) { 499 /* 500 * Handle lane broadcast 501 * 502 * Note that we use lane 8 instead of lane 0 to prevent a 503 * broadcast loop. The value 8 will become 0 when masked 504 * with 0x7, but it prevents the AER in the upper 16 bits 505 * from being zero, which will cause the code above to 506 * obtain the AER value from register 0xffde. 507 */ 508 reg = TSCF_GEN3_REG_GET(addr); 509 devad = TSCF_GEN3_DEVAD_GET(addr); 510 pllidx = TSCF_GEN3_PLLIDX_GET(addr); 511 if (lane == 4 || lane == 6) { 512 /* Write lanes 0 and 1 */ 513 addr = TSCF_GEN3_ADDR(devad, 8, reg, pllidx); 514 (void)tscf_gen3_sim_write(pms_data, addr, data); 515 addr = TSCF_GEN3_ADDR(devad, 1, reg, pllidx); 516 (void)tscf_gen3_sim_write(pms_data, addr, data); 517 } 518 if (lane == 5 || lane == 6) { 519 /* Write lanes 2 and 3 */ 520 addr = TSCF_GEN3_ADDR(devad, 2, reg, pllidx); 521 (void)tscf_gen3_sim_write(pms_data, addr, data); 522 addr = TSCF_GEN3_ADDR(devad, 3, reg, pllidx); 523 (void)tscf_gen3_sim_write(pms_data, addr, data); 524 } 525 return PHYMOD_E_NONE; 526 } 527 } 528 529 /* Adjust data and/or related registers */ 530 data = tscf_gen3_sim_write_adjust(pms_data, addr, data); 531 532 /* Adjust lane according to number of copies */ 533 devad = TSCF_GEN3_DEVAD_GET(addr); 534 reg = TSCF_GEN3_REG_GET(addr); 535 copies = tscf_gen3_sim_reg_copies_get(addr); 536 if (copies == 1) { 537 lane = 0; 538 } else if (copies == 2) { 539 lane &= ~0x1; 540 } 541 pllidx = TSCF_GEN3_PLLIDX_GET(addr); 542 addr = TSCF_GEN3_ADDR(devad, lane, reg, pllidx); 543 544 return _tscf_gen3_sim_write(pms_data, addr, data); 545 } 546 547 STATIC int 548 tscf_gen3_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event) 549 { 550 if (pms_data == NULL || pms_data->entries == NULL) { 551 return PHYMOD_E_INIT; 552 } 553 554 return PHYMOD_E_NONE; 555 } 556 557 phymod_sim_drv_t tscf_gen3_sim_drv = { 558 tscf_gen3_sim_init, 559 tscf_gen3_sim_reset, 560 tscf_gen3_sim_read, 561 tscf_gen3_sim_write, 562 tscf_gen3_sim_event 563 }; 564