tscf16_sim.c (16503B)
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 TSCF16_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift)) 47 #define TSCF16_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 TSCF16_DEVAD_SHIFT 27 54 #define TSCF16_DEVAD_MASK 0x1f 55 #define TSCF16_DEVAD_GET(_addr) \ 56 TSCF16_BF_GET(_addr, TSCF16_DEVAD_MASK, TSCF16_DEVAD_SHIFT) 57 #define TSCF16_LANE_SHIFT 16 58 #define TSCF16_LANE_MASK 0x7 59 #define TSCF16_LANE_GET(_addr) \ 60 TSCF16_BF_GET(_addr, TSCF16_LANE_MASK, TSCF16_LANE_SHIFT) 61 #define TSCF16_REG_SHIFT 0 62 #define TSCF16_REG_MASK 0xffff 63 #define TSCF16_REG_GET(_addr) \ 64 TSCF16_BF_GET(_addr, TSCF16_REG_MASK, TSCF16_REG_SHIFT) 65 66 #define TSCF16_ADDR(_devad, _lane, _reg) \ 67 (((_devad) << TSCF16_DEVAD_SHIFT) + \ 68 ((_lane) << TSCF16_LANE_SHIFT) + \ 69 ((_reg) << TSCF16_REG_SHIFT)) 70 71 #define TSCF16_AER TSCF16_ADDR(0, 0, 0xffde) 72 #define TSCF16_BLK TSCF16_ADDR(0, 0, 0x001f) 73 74 /* 75 * The CL45 indicator is used to determine whether the upper 16 bits 76 * of the address is an AER value or a clause 45 DEVAD. 77 */ 78 #define TSCF16_CL45 (0x20 << 16) 79 #define TSCF16_CL45_MASK (0xe0 << 16) 80 81 #define TSCF16_ID0 0x600d 82 #define TSCF16_ID1 0x8770 83 #define TSCF16_SERDES_ID 0x0315 84 #define FALCON16_TSC_UCODE_IMAGE_CRC 0xe60a 85 86 #define TSCF16_SIM_FW_LOAD_DONE_REG_ADDR (0x800d203) 87 88 /* Forward declarations */ 89 STATIC int 90 tscf16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data); 91 STATIC int 92 tscf16_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data); 93 94 STATIC uint32_t 95 tscf16_sim_default_data_get(uint32_t addr) 96 { 97 uint32_t reg; 98 99 reg = TSCF16_REG_GET(addr); 100 101 /*force FW load success */ 102 if(addr == TSCF16_SIM_FW_LOAD_DONE_REG_ADDR) { 103 return 1; 104 } 105 106 switch (reg) { 107 case 0x0002: 108 return TSCF16_ID0; 109 case 0x0003: 110 return TSCF16_ID1; 111 case 0x900e: 112 return TSCF16_SERDES_ID; 113 case 0xd03d: 114 /* 115 * This is DSC_A_DSC_UC_CTRL for Falcon. Make sure 116 * The 'ready' bit is set. 117 */ 118 return 0x0080 ; 119 case 0xd10a: 120 /* DIG_COM_REVID1[15:12], Number of lanes */ 121 return 0x403e; 122 case 0xd108: 123 case 0xd0b9: 124 /* 125 * Reset check 126 */ 127 return 0x7; 128 case 0xd1b9: 129 return 0x7; 130 case 0xd104: 131 return 0x8271; 132 case 0xd170: 133 return 0xb000; 134 default: 135 break; 136 } 137 138 return 0; 139 } 140 STATIC uint32_t 141 tscf16_sim_reg_copies_get(uint32_t addr) 142 { 143 uint32_t devad, reg; 144 145 devad = TSCF16_DEVAD_GET(addr); 146 reg = TSCF16_REG_GET(addr); 147 148 if (reg == TSCF16_AER || reg == TSCF16_BLK) { 149 return 1; 150 } 151 152 if (devad == 0) { 153 if ((reg & 0xf000) == 0x9000) { 154 return 1; 155 } 156 if ((reg & 0xf000) == 0xa000) { 157 return 2; 158 } 159 return 4; 160 } else if (devad == 1) { 161 return 4; 162 } 163 return 0; 164 } 165 166 STATIC int 167 _tscf16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 168 { 169 int idx; 170 uint32_t mask; 171 172 phymod_sim_entry_t *pse; 173 174 if (pms_data == NULL || pms_data->entries == NULL) { 175 return PHYMOD_E_INIT; 176 } 177 178 /* Support optional write mask in upper 16 bits */ 179 mask = (data >> 16); 180 if (mask == 0) { 181 mask = 0xffff; 182 } 183 data &= mask; 184 185 /* Check if this register has been written already */ 186 for (idx = 0; idx < pms_data->entries_used; idx++) { 187 pse = &pms_data->entries[idx]; 188 if (pse->addr == addr) { 189 pse->data &= ~mask; 190 pse->data |= data; 191 DBG_VERB(("_tscf16_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n", 192 addr, pse->data)); 193 return PHYMOD_E_NONE; 194 } 195 } 196 197 /* Check if database is full */ 198 if (pms_data->entries_used >= pms_data->num_entries) { 199 return PHYMOD_E_RESOURCE; 200 } 201 202 /* Check if new data matches default value */ 203 if (data == tscf16_sim_default_data_get(addr)) { 204 return PHYMOD_E_NONE; 205 } 206 207 /* Add new register value */ 208 pse = &pms_data->entries[pms_data->entries_used++]; 209 pse->addr = addr; 210 pse->data = data; 211 212 DBG_VERB(("_tscf16_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n", 213 addr, pse->data)); 214 215 return PHYMOD_E_NONE; 216 } 217 218 STATIC uint32_t 219 tscf16_sim_write_adjust(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 220 { 221 uint32_t devad, reg, val; 222 223 devad = TSCF16_DEVAD_GET(addr); 224 reg = TSCF16_REG_GET(addr); 225 226 if (devad == 0) { 227 switch (reg) { 228 case 0xc050: 229 /* Set SW_SPEED_CHANGE_DONE and SW_SPEED_CONFIG_VLD in status reg */ 230 tscf16_sim_write(pms_data, addr + 1, 0x3); 231 /* Force link up in RX_X4_PCS_LIVE_STS register 232 offset 0x111 = 0xc161 - 0xc050 */ 233 tscf16_sim_write(pms_data, addr + 0x111, 0x2); 234 /* Sync configured speed into final speed status register 235 * 0x20 = 0xc070 - 0xc050 */ 236 tscf16_sim_read(pms_data, addr + 0x20, &val); 237 val = (val & 0xffff00ff) | ((data & 0xff)<<8); 238 tscf16_sim_write(pms_data, addr + 0x20, val); 239 break; 240 case 0xc055: 241 /* Sync configured Lane Number into Lane Number status register 242 * 0x1B = 0xc070 - 0xc055 */ 243 tscf16_sim_read(pms_data, addr + 0x1B, &val); 244 val = (val & 0xfffffff8) | (data & 0x7); 245 tscf16_sim_write(pms_data, addr + 0x1B, val); 246 break; 247 default: 248 break; 249 } 250 } else if (devad == 1) { 251 switch (reg) { 252 case 0xd204: 253 case 0xd205: 254 case 0xd206: 255 case 0xd207: 256 /* Sync configured aMICRO_A_COM registers. Write and read access different register 257 * Write registers: 0xd204~0xd207; Read registers: 0xd208~0xd20b 258 * 0x4 = 0xd208(~0xd20b) - 0xd204(~0xd207)*/ 259 tscf16_sim_write(pms_data, addr + 0x4, data); 260 break; 261 case 0xd0e1: 262 /* Sync configured SIGDET_CTL1 register. When a port is set disable, 263 * the port's RX_X4_PCS_LIVE_STS1 register deletes link status. 264 * RX_X4_PCS_LIVE_STS1 register is PCS register, so need set devad to 0. 265 * 0xF80 = 0xd0e1 - 0xc161*/ 266 tscf16_sim_read(pms_data, (addr - 0xF80) & 0x7ffff, &val); 267 val = (val & 0xfffd) | (((data&0x80)==0x80)?0:0x2); 268 tscf16_sim_write(pms_data, (addr - 0xF80) & 0x7ffff, val); 269 break; 270 case 0xd147: 271 /* Sync configured all lanes' PLL_CAL_CTL7 register. Only one PLL/VCO per core */ 272 /* Write lanes 0, 1, 2 and 3 */ 273 addr = TSCF16_ADDR(devad, 0, reg); 274 (void)_tscf16_sim_write(pms_data, addr, data); 275 276 addr = TSCF16_ADDR(devad, 1, reg); 277 (void)_tscf16_sim_write(pms_data, addr, data); 278 279 addr = TSCF16_ADDR(devad, 2, reg); 280 (void)_tscf16_sim_write(pms_data, addr, data); 281 282 addr = TSCF16_ADDR(devad, 3, reg); 283 (void)_tscf16_sim_write(pms_data, addr, data); 284 break; 285 case 0xd094: 286 case 0xd095: 287 /* Sync configured CL93N72 Control 2/3 registers. Write and read access different register 288 * Write registers: 0xd094/d095; Read registers: 0xd133/0xd134 289 * 0x9f = 0xd133(0xd134) - 0xd094(0xd095)*/ 290 tscf16_sim_write(pms_data, addr + 0x9f, data); 291 break; 292 default: 293 break; 294 } 295 } 296 297 return data; 298 } 299 300 STATIC int 301 tscf16_sim_init(phymod_sim_data_t *pms_data, 302 int num_entries, phymod_sim_entry_t *entries) 303 { 304 if (pms_data != NULL) { 305 PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data)); 306 pms_data->num_entries = num_entries; 307 pms_data->entries = entries; 308 } 309 return PHYMOD_E_NONE; 310 } 311 312 STATIC int 313 tscf16_sim_reset(phymod_sim_data_t *pms_data) 314 { 315 uint32_t sim_size; 316 317 if (pms_data == NULL || pms_data->entries == NULL) { 318 return PHYMOD_E_INIT; 319 } 320 321 pms_data->entries_used = 0; 322 sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t); 323 PHYMOD_MEMSET(pms_data->entries, 0, sim_size); 324 325 return PHYMOD_E_NONE; 326 } 327 328 STATIC int 329 tscf16_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data) 330 { 331 int idx; 332 uint32_t aer, blk, devad, reg, copies; 333 uint32_t lane = 0; 334 phymod_sim_entry_t *pse; 335 336 if (pms_data == NULL || pms_data->entries == NULL) { 337 return PHYMOD_E_INIT; 338 } 339 340 devad = 0; 341 342 if (addr < TSCF16_BLK) { 343 /* Assume clause 22 access */ 344 (void)tscf16_sim_read(pms_data, TSCF16_BLK, &blk); 345 /* IEEE bit */ 346 if (addr & 0x10) { 347 blk |= 0x8000; 348 } else { 349 blk &= ~0x8000; 350 } 351 addr = (addr & 0xf) | (blk & 0xfff0); 352 if (addr != TSCF16_AER && addr != TSCF16_BLK) { 353 (void)tscf16_sim_read(pms_data, TSCF16_AER, &aer); 354 addr |= (aer << 16); 355 } 356 } else { 357 /* Extract devad if clause 45 address format */ 358 if ((addr & TSCF16_CL45_MASK) == TSCF16_CL45) { 359 devad = (addr >> 16) & 0x1f; 360 addr &= 0xffff; 361 } 362 } 363 364 if (addr != TSCF16_AER && addr != TSCF16_BLK) { 365 /* Assume AER is in upper 16 bits */ 366 aer = (addr >> 16); 367 if (aer == 0) { 368 /* Try reading real AER instead */ 369 (void)tscf16_sim_read(pms_data, TSCF16_AER, &aer); 370 } 371 /* Add clause 45 devad (if used) */ 372 if (devad) { 373 aer |= (devad << 11); 374 addr = (addr & 0xffff) | (aer << 16); 375 } 376 lane = (aer & 0x7); 377 if (lane > 3) { 378 /* Force lane 0 if lane is invalid */ 379 addr = TSCF16_ADDR(TSCF16_DEVAD_GET(addr), 0, TSCF16_REG_GET(addr)); 380 } 381 } 382 383 /* Adjust lane according to number of copies */ 384 devad = TSCF16_DEVAD_GET(addr); 385 reg = TSCF16_REG_GET(addr); 386 copies = tscf16_sim_reg_copies_get(addr); 387 if (copies == 1) { 388 lane = 0; 389 } else if (copies == 2) { 390 lane &= ~0x1; 391 } 392 addr = TSCF16_ADDR(devad, lane, reg); 393 394 /* for DSC_UC_CTRL always succeed */ 395 if(addr == 0x800d03d) { 396 *data = 0x80; 397 DBG_VERB(("tscf16_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 398 addr, *data)); 399 return PHYMOD_E_NONE; 400 } else if (addr == 0x800d03e) { 401 *data = FALCON16_TSC_UCODE_IMAGE_CRC; 402 return PHYMOD_E_NONE; 403 } else if (reg == 0xd104) { 404 *data = 0x8271; 405 return PHYMOD_E_NONE; 406 } else if (addr == 0x800d201) { 407 *data = 0; 408 return PHYMOD_E_NONE; 409 } 410 411 /* Check if this register has been written already */ 412 for (idx = 0; idx < pms_data->entries_used; idx++) { 413 pse = &pms_data->entries[idx]; 414 if (pse->addr == addr) { 415 *data = pse->data; 416 DBG_VERB(("tscf16_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 417 addr, *data)); 418 return PHYMOD_E_NONE; 419 } 420 } 421 422 /* Return default value if register was never written */ 423 *data = tscf16_sim_default_data_get(addr); 424 425 DBG_VERB(("tscf16_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n", 426 addr, *data)); 427 428 return PHYMOD_E_NONE; 429 } 430 431 STATIC int 432 tscf16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 433 { 434 uint32_t aer, blk, devad, reg, copies; 435 uint32_t lane = 0; 436 437 if (pms_data == NULL || pms_data->entries == NULL) { 438 return PHYMOD_E_INIT; 439 } 440 441 devad = 0; 442 443 if (addr < TSCF16_BLK) { 444 /* Assume clause 22 access */ 445 (void)tscf16_sim_read(pms_data, TSCF16_BLK, &blk); 446 /* IEEE bit */ 447 if (addr & 0x10) { 448 blk |= 0x8000; 449 } else { 450 blk &= ~0x8000; 451 } 452 addr = (addr & 0xf) | (blk & 0xfff0); 453 if (addr != TSCF16_AER && addr != TSCF16_BLK) { 454 (void)tscf16_sim_read(pms_data, TSCF16_AER, &aer); 455 addr |= (aer << 16); 456 } 457 } else { 458 /* Extract devad if clause 45 address format */ 459 if ((addr & TSCF16_CL45_MASK) == TSCF16_CL45) { 460 devad = (addr >> 16) & 0x1f; 461 addr &= 0xffff; 462 } 463 } 464 465 if (addr != TSCF16_AER && addr != TSCF16_BLK) { 466 /* Assume AER is in upper 16 bits */ 467 aer = (addr >> 16); 468 if (aer == 0) { 469 /* Try reading real AER instead */ 470 (void)tscf16_sim_read(pms_data, TSCF16_AER, &aer); 471 } 472 /* Add clause 45 devad (if used) */ 473 if (devad) { 474 aer |= (devad << 11); 475 addr = (addr & 0xffff) | (aer << 16); 476 } 477 lane = (aer & 0x7); 478 if (lane > 6) { 479 return PHYMOD_E_PARAM; 480 } 481 if (lane > 3) { 482 /* 483 * Handle lane broadcast 484 * 485 * Note that we use lane 8 instead of lane 0 to prevent a 486 * broadcast loop. The value 8 will become 0 when masked 487 * with 0x7, but it prevents the AER in the upper 16 bits 488 * from being zero, which will cause the code above to 489 * obtain the AER value from register 0xffde. 490 */ 491 reg = TSCF16_REG_GET(addr); 492 devad = TSCF16_DEVAD_GET(addr); 493 if (lane == 4 || lane == 6) { 494 /* Write lanes 0 and 1 */ 495 addr = TSCF16_ADDR(devad, 8, reg); 496 (void)tscf16_sim_write(pms_data, addr, data); 497 addr = TSCF16_ADDR(devad, 1, reg); 498 (void)tscf16_sim_write(pms_data, addr, data); 499 } 500 if (lane == 5 || lane == 6) { 501 /* Write lanes 2 and 3 */ 502 addr = TSCF16_ADDR(devad, 2, reg); 503 (void)tscf16_sim_write(pms_data, addr, data); 504 addr = TSCF16_ADDR(devad, 3, reg); 505 (void)tscf16_sim_write(pms_data, addr, data); 506 } 507 return PHYMOD_E_NONE; 508 } 509 } 510 511 /* Adjust data and/or related registers */ 512 data = tscf16_sim_write_adjust(pms_data, addr, data); 513 514 /* Adjust lane according to number of copies */ 515 devad = TSCF16_DEVAD_GET(addr); 516 reg = TSCF16_REG_GET(addr); 517 copies = tscf16_sim_reg_copies_get(addr); 518 if (copies == 1) { 519 lane = 0; 520 } else if (copies == 2) { 521 lane &= ~0x1; 522 } 523 addr = TSCF16_ADDR(devad, lane, reg); 524 525 return _tscf16_sim_write(pms_data, addr, data); 526 } 527 528 STATIC int 529 tscf16_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event) 530 { 531 if (pms_data == NULL || pms_data->entries == NULL) { 532 return PHYMOD_E_INIT; 533 } 534 535 return PHYMOD_E_NONE; 536 } 537 538 phymod_sim_drv_t tscf16_sim_drv = { 539 tscf16_sim_init, 540 tscf16_sim_reset, 541 tscf16_sim_read, 542 tscf16_sim_write, 543 tscf16_sim_event 544 }; 545