tsce16_sim.c (16699B)
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/Merlin16 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 #include <phymod/chip/bcmi_tsce16_xgxs_defs.h> 42 43 /* Convenience macro */ 44 #define DBG_VERB PHYMOD_DEBUG_VERBOSE 45 46 /* Bit field get/set macros */ 47 #define TSCE16_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift)) 48 #define TSCE16_BF_GET(_val, _mask, _shift) (((_val) >> (_shift)) & (_mask)) 49 50 /* 51 * Raw 32-bit address consists of AER value in upper 16 bits and 52 * clause 45 address in lower 16 bits. 53 */ 54 #define TSCE16_DEVAD_SHIFT 27 55 #define TSCE16_DEVAD_MASK 0x1f 56 #define TSCE16_DEVAD_GET(_addr) \ 57 TSCE16_BF_GET(_addr, TSCE16_DEVAD_MASK, TSCE16_DEVAD_SHIFT) 58 #define TSCE16_LANE_SHIFT 16 59 #define TSCE16_LANE_MASK 0x7 60 #define TSCE16_LANE_GET(_addr) \ 61 TSCE16_BF_GET(_addr, TSCE16_LANE_MASK, TSCE16_LANE_SHIFT) 62 #define TSCE16_REG_SHIFT 0 63 #define TSCE16_REG_MASK 0xffff 64 #define TSCE16_REG_GET(_addr) \ 65 TSCE16_BF_GET(_addr, TSCE16_REG_MASK, TSCE16_REG_SHIFT) 66 67 #define TSCE16_ADDR(_devad, _lane, _reg) \ 68 (((_devad) << TSCE16_DEVAD_SHIFT) + \ 69 ((_lane) << TSCE16_LANE_SHIFT) + \ 70 ((_reg) << TSCE16_REG_SHIFT)) 71 72 #define TSCE16_AER TSCE16_ADDR(0, 0, 0xffde) 73 #define TSCE16_BLK TSCE16_ADDR(0, 0, 0x001f) 74 75 /* 76 * The CL45 indicator is used to determine whether the upper 16 bits 77 * of the address is an AER value or a clause 45 DEVAD. 78 */ 79 #define TSCE16_CL45 (0x20 << 16) 80 #define TSCE16_CL45_MASK (0xe0 << 16) 81 82 #define TSCE16_ID0 0x600d 83 #define TSCE16_ID1 0x8770 84 #define TSCE16_SERDES_ID 0x0312 85 86 #define TSCE16_SIM_FW_LOAD_DONE_REG_ADDR (0x0800d205) 87 #define TSCE16_SIM_DSC_UC_CTRL_REG_ADDR (0x0800d00d) 88 89 /* Forward declarations */ 90 STATIC int 91 tsce16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data); 92 STATIC int 93 tsce16_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data); 94 95 STATIC uint32_t 96 tsce16_sim_default_data_get(uint32_t addr) 97 { 98 uint32_t reg; 99 100 reg = TSCE16_REG_GET(addr); 101 102 /*force FW load success */ 103 if(addr == TSCE16_SIM_FW_LOAD_DONE_REG_ADDR) { 104 return (1 << 15); 105 } 106 107 switch (reg) { 108 case 0x0002: 109 return TSCE16_ID0; 110 case 0x0003: 111 return TSCE16_ID1; 112 case 0x900e: 113 return TSCE16_SERDES_ID; 114 case 0x9003: 115 return 0xe4e4; 116 case 0x925a: 117 return 0x0002; 118 case 0x925b: 119 return 0x006b; 120 case 0x925d: 121 return 0x3b5f; 122 case 0x925e: 123 return 0x006b; 124 case 0xa000: 125 return 0xfffc; 126 case 0xa001: 127 return 0x8030; 128 case 0xc070: 129 return 0xff00; 130 case 0xc111: 131 return 0x0800; 132 case 0xc113: 133 return 0x01c8; 134 case 0xc134: 135 return 0x2870; 136 case 0xc185: 137 return 0x02a0; 138 case 0xc330: 139 return 0x0002; 140 case 0xd089: 141 return 0x0007; 142 case 0xd0a2: 143 return 0x5250; 144 case 0xd0b1: 145 return 0x0007; 146 case 0xd0b4: 147 return 0x0500; 148 case 0xd0b8: 149 return 0x4042; 150 case 0xd0c1: 151 return 0x0008; 152 case 0xd0d2: 153 return 0x000e; 154 case 0xd0e2: 155 return 0x0002; 156 case 0xd0f4: 157 /* this is not the default value. value is changed to pass through 158 * core int. 159 */ 160 return 0x8271; 161 case 0xd0f8: 162 return 0x0007; 163 case 0xd111: 164 return 0x0020; 165 case 0xd189: 166 return 0x0007; 167 case 0xd203: 168 /* this is not the default value. value is changed to pass through 169 * core init. 170 */ 171 return 0x1; 172 default: 173 return 0; 174 } 175 return 0; 176 } 177 178 STATIC uint32_t 179 tsce16_sim_reg_copies_get(uint32_t addr) 180 { 181 uint32_t devad, reg; 182 183 devad = TSCE16_DEVAD_GET(addr); 184 reg = TSCE16_REG_GET(addr); 185 186 if (reg == TSCE16_AER || reg == TSCE16_BLK) { 187 return 1; 188 } 189 190 if (devad == 0) { 191 if ((reg & 0xf000) == 0x9000) { 192 return 1; 193 } 194 if ((reg & 0xf000) == 0xa000) { 195 return 2; 196 } 197 return 4; 198 } else if (devad == 1) { 199 return 4; 200 } 201 return 0; 202 } 203 204 STATIC int 205 _tsce16_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 206 { 207 int idx; 208 uint32_t mask; 209 210 phymod_sim_entry_t *pse; 211 212 if (pms_data == NULL || pms_data->entries == NULL) { 213 return PHYMOD_E_INIT; 214 } 215 216 /* Support optional write mask in upper 16 bits */ 217 mask = (data >> 16); 218 if (mask == 0) { 219 mask = 0xffff; 220 } 221 data &= mask; 222 223 /* Check if this register has been written already */ 224 for (idx = 0; idx < pms_data->entries_used; idx++) { 225 pse = &pms_data->entries[idx]; 226 if (pse->addr == addr) { 227 pse->data &= ~mask; 228 pse->data |= data; 229 DBG_VERB(("_tsce16_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n", 230 addr, pse->data)); 231 return PHYMOD_E_NONE; 232 } 233 } 234 235 /* Check if database is full */ 236 if (pms_data->entries_used >= pms_data->num_entries) { 237 return PHYMOD_E_RESOURCE; 238 } 239 240 /* Check if new data matches default value */ 241 if (data == tsce16_sim_default_data_get(addr)) { 242 return PHYMOD_E_NONE; 243 } 244 245 /* Add new register value */ 246 pse = &pms_data->entries[pms_data->entries_used++]; 247 pse->addr = addr; 248 pse->data = data; 249 250 DBG_VERB(("_tsce16_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n", 251 addr, pse->data)); 252 253 return PHYMOD_E_NONE; 254 } 255 256 STATIC uint32_t 257 tsce16_sim_write_adjust(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 258 { 259 uint32_t devad, reg, val; 260 261 devad = TSCE16_DEVAD_GET(addr); 262 reg = TSCE16_REG_GET(addr); 263 264 if (devad == 0) { 265 switch (reg) { 266 case 0xc050: 267 /* Set SW_SPEED_CHANGE_DONE and SW_SPEED_CONFIG_VLD in status reg */ 268 tsce16_sim_write(pms_data, addr + 1, 0x3); 269 /* Force link up in RX_X4_PCS_LIVE_STS register 270 offset 0x104 = 0xc154 - 0xc050 */ 271 tsce16_sim_write(pms_data, addr + 0x104, 0x1b); 272 /* Sync configured speed into final speed status register 0x20 = 0xc070 - 0xc050 */ 273 tsce16_sim_read(pms_data, addr + 0x20, &val); 274 val = (val & 0xffff00ff) | ((data & 0xff)<<8); 275 tsce16_sim_write(pms_data, addr + 0x20, val); 276 break; 277 case 0xc055: 278 /* Sync configured Lane Number into Lane Number status register 0x1B = 0xc070 - 0xc055 */ 279 tsce16_sim_read(pms_data, addr + 0x1B, &val); 280 val = (val & 0xfffffff8) | (data & 0x7); 281 tsce16_sim_write(pms_data, addr + 0x1B, val); 282 break; 283 default: 284 break; 285 } 286 } else if (devad == 1) { 287 switch (reg) { 288 case 0xd127: 289 /* Sync configured all lanes' PLL_CAL_CTL7 register. Only one PLL/VCO per core */ 290 /* Write lanes 0, 1, 2 and 3 */ 291 addr = TSCE16_ADDR(devad, 0, reg); 292 (void)_tsce16_sim_write(pms_data, addr, data); 293 294 addr = TSCE16_ADDR(devad, 1, reg); 295 (void)_tsce16_sim_write(pms_data, addr, data); 296 297 addr = TSCE16_ADDR(devad, 2, reg); 298 (void)_tsce16_sim_write(pms_data, addr, data); 299 300 addr = TSCE16_ADDR(devad, 3, reg); 301 (void)_tsce16_sim_write(pms_data, addr, data); 302 break; 303 case 0xd203: 304 /* write data register to UC_RAM_WRDATA(0xd203) 305 * read data register from UC_RAM_RDDATA(0xd204) 306 */ 307 _tsce16_sim_write(pms_data, addr + 1, data); 308 break; 309 case 0xd0c1: 310 /* Sync configured SIGDET_CTL1 register. When a port is set disable, 311 * the port's RX_X4_PCS_LIVE_STS register deletes link status. 312 * RX_X4_PCS_LIVE_STS register is PCS register, so need set devad to 0. 313 * 0xF6D = 0xd0c1 - 0xc154*/ 314 tsce16_sim_read(pms_data, (addr - 0xF6D) & 0x7ffff, &val); 315 val = (val & 0xfffd) | (((data&0x80)==0x80)?0:0x2); 316 tsce16_sim_write(pms_data, (addr - 0xF6D) & 0x7ffff, val); 317 break; 318 default: 319 break; 320 } 321 } 322 323 return data; 324 } 325 326 STATIC int 327 tsce16_sim_init(phymod_sim_data_t *pms_data, 328 int num_entries, phymod_sim_entry_t *entries) 329 { 330 if (pms_data != NULL) { 331 PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data)); 332 pms_data->num_entries = num_entries; 333 pms_data->entries = entries; 334 } 335 return PHYMOD_E_NONE; 336 } 337 338 STATIC int 339 tsce16_sim_reset(phymod_sim_data_t *pms_data) 340 { 341 uint32_t sim_size; 342 343 if (pms_data == NULL || pms_data->entries == NULL) { 344 return PHYMOD_E_INIT; 345 } 346 347 pms_data->entries_used = 0; 348 sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t); 349 PHYMOD_MEMSET(pms_data->entries, 0, sim_size); 350 351 return PHYMOD_E_NONE; 352 } 353 354 STATIC int 355 tsce16_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data) 356 { 357 int idx; 358 uint32_t aer, blk, devad, reg, copies; 359 uint32_t lane = 0; 360 phymod_sim_entry_t *pse; 361 362 if (pms_data == NULL || pms_data->entries == NULL) { 363 return PHYMOD_E_INIT; 364 } 365 366 devad = 0; 367 368 if (addr < TSCE16_BLK) { 369 /* Assume clause 22 access */ 370 (void)tsce16_sim_read(pms_data, TSCE16_BLK, &blk); 371 /* IEEE bit */ 372 if (addr & 0x10) { 373 blk |= 0x8000; 374 } else { 375 blk &= ~0x8000; 376 } 377 addr = (addr & 0xf) | (blk & 0xfff0); 378 if (addr != TSCE16_AER && addr != TSCE16_BLK) { 379 (void)tsce16_sim_read(pms_data, TSCE16_AER, &aer); 380 addr |= (aer << 16); 381 } 382 } else { 383 /* Extract devad if clause 45 address format */ 384 if ((addr & TSCE16_CL45_MASK) == TSCE16_CL45) { 385 devad = (addr >> 16) & 0x1f; 386 addr &= 0xffff; 387 } 388 } 389 390 if (addr != TSCE16_AER && addr != TSCE16_BLK) { 391 /* Assume AER is in upper 16 bits */ 392 aer = (addr >> 16); 393 if (aer == 0) { 394 /* Try reading real AER instead */ 395 (void)tsce16_sim_read(pms_data, TSCE16_AER, &aer); 396 } 397 /* Add clause 45 devad (if used) */ 398 if (devad) { 399 aer |= (devad << 11); 400 addr = (addr & 0xffff) | (aer << 16); 401 } 402 lane = (aer & 0x7); 403 if (lane > 3) { 404 /* Force lane 0 if lane is invalid */ 405 addr = TSCE16_ADDR(TSCE16_DEVAD_GET(addr), 0, TSCE16_REG_GET(addr)); 406 } 407 } 408 409 /* Adjust lane according to number of copies */ 410 devad = TSCE16_DEVAD_GET(addr); 411 reg = TSCE16_REG_GET(addr); 412 copies = tsce16_sim_reg_copies_get(addr); 413 if (copies == 1) { 414 lane = 0; 415 } else if (copies == 2) { 416 lane &= ~0x1; 417 } 418 addr = TSCE16_ADDR(devad, lane, reg); 419 420 /* for DSC_UC_CTRL always succeed */ 421 if(addr == TSCE16_SIM_DSC_UC_CTRL_REG_ADDR) { 422 *data = 0x80; 423 DBG_VERB(("tsce16_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 424 addr, *data)); 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(("tsce16_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 = tsce16_sim_default_data_get(addr); 441 442 DBG_VERB(("tsce16_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n", 443 addr, *data)); 444 445 return PHYMOD_E_NONE; 446 } 447 448 STATIC int 449 tsce16_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; 453 454 if (pms_data == NULL || pms_data->entries == NULL) { 455 return PHYMOD_E_INIT; 456 } 457 458 devad = 0; 459 460 if (addr < TSCE16_BLK) { 461 /* Assume clause 22 access */ 462 (void)tsce16_sim_read(pms_data, TSCE16_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 != TSCE16_AER && addr != TSCE16_BLK) { 471 (void)tsce16_sim_read(pms_data, TSCE16_AER, &aer); 472 addr |= (aer << 16); 473 } 474 } else { 475 /* Extract devad if clause 45 address format */ 476 if ((addr & TSCE16_CL45_MASK) == TSCE16_CL45) { 477 devad = (addr >> 16) & 0x1f; 478 addr &= 0xffff; 479 } 480 } 481 482 if (addr != TSCE16_AER && addr != TSCE16_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)tsce16_sim_read(pms_data, TSCE16_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 = TSCE16_REG_GET(addr); 509 devad = TSCE16_DEVAD_GET(addr); 510 if (lane == 4 || lane == 6) { 511 /* Write lanes 0 and 1 */ 512 addr = TSCE16_ADDR(devad, 8, reg); 513 (void)tsce16_sim_write(pms_data, addr, data); 514 addr = TSCE16_ADDR(devad, 1, reg); 515 (void)tsce16_sim_write(pms_data, addr, data); 516 } 517 if (lane == 5 || lane == 6) { 518 /* Write lanes 2 and 3 */ 519 addr = TSCE16_ADDR(devad, 2, reg); 520 (void)tsce16_sim_write(pms_data, addr, data); 521 addr = TSCE16_ADDR(devad, 3, reg); 522 (void)tsce16_sim_write(pms_data, addr, data); 523 } 524 return PHYMOD_E_NONE; 525 } 526 } 527 528 /* Adjust data and/or related registers */ 529 data = tsce16_sim_write_adjust(pms_data, addr, data); 530 531 /* Adjust lane according to number of copies */ 532 devad = TSCE16_DEVAD_GET(addr); 533 reg = TSCE16_REG_GET(addr); 534 copies = tsce16_sim_reg_copies_get(addr); 535 if (copies == 1) { 536 lane = 0; 537 } else if (copies == 2) { 538 lane &= ~0x1; 539 } 540 addr = TSCE16_ADDR(devad, lane, reg); 541 542 return _tsce16_sim_write(pms_data, addr, data); 543 } 544 545 STATIC int 546 tsce16_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event) 547 { 548 if (pms_data == NULL || pms_data->entries == NULL) { 549 return PHYMOD_E_INIT; 550 } 551 552 return PHYMOD_E_NONE; 553 } 554 555 phymod_sim_drv_t tsce16_sim_drv = { 556 tsce16_sim_init, 557 tsce16_sim_reset, 558 tsce16_sim_read, 559 tsce16_sim_write, 560 tsce16_sim_event 561 }; 562