tsce_dpll_sim.c (16851B)
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_tsce_dpll_xgxs_defs.h> 42 43 /* Convenience macro */ 44 #define DBG_VERB PHYMOD_DEBUG_VERBOSE 45 46 /* Bit field get/set macros */ 47 #define TSCE_DPLL_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift)) 48 #define TSCE_DPLL_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 TSCE_DPLL_DEVAD_SHIFT 27 55 #define TSCE_DPLL_DEVAD_MASK 0x1f 56 #define TSCE_DPLL_DEVAD_GET(_addr) \ 57 TSCE_DPLL_BF_GET(_addr, TSCE_DPLL_DEVAD_MASK, TSCE_DPLL_DEVAD_SHIFT) 58 #define TSCE_DPLL_LANE_SHIFT 16 59 #define TSCE_DPLL_LANE_MASK 0x7 60 #define TSCE_DPLL_LANE_GET(_addr) \ 61 TSCE_DPLL_BF_GET(_addr, TSCE_DPLL_LANE_MASK, TSCE_DPLL_LANE_SHIFT) 62 #define TSCE_DPLL_REG_SHIFT 0 63 #define TSCE_DPLL_REG_MASK 0xffff 64 #define TSCE_DPLL_REG_GET(_addr) \ 65 TSCE_DPLL_BF_GET(_addr, TSCE_DPLL_REG_MASK, TSCE_DPLL_REG_SHIFT) 66 67 #define TSCE_DPLL_ADDR(_devad, _lane, _reg) \ 68 (((_devad) << TSCE_DPLL_DEVAD_SHIFT) + \ 69 ((_lane) << TSCE_DPLL_LANE_SHIFT) + \ 70 ((_reg) << TSCE_DPLL_REG_SHIFT)) 71 72 #define TSCE_DPLL_AER TSCE_DPLL_ADDR(0, 0, 0xffde) 73 #define TSCE_DPLL_BLK TSCE_DPLL_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 TSCE_DPLL_CL45 (0x20 << 16) 80 #define TSCE_DPLL_CL45_MASK (0xe0 << 16) 81 82 #define TSCE_DPLL_ID0 0x600d 83 #define TSCE_DPLL_ID1 0x8770 84 #define TSCE_DPLL_SERDES_ID 0x02e3 85 86 #define TSCE_DPLL_SIM_FW_LOAD_DONE_REG_ADDR (0x0800d205) 87 #define TSCE_DPLL_SIM_DSC_UC_CTRL_REG_ADDR (0x0800d00d) 88 89 /* Forward declarations */ 90 STATIC int 91 tsce_dpll_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data); 92 STATIC int 93 tsce_dpll_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data); 94 95 STATIC uint32_t 96 tsce_dpll_sim_default_data_get(uint32_t addr) 97 { 98 uint32_t reg; 99 100 reg = TSCE_DPLL_REG_GET(addr); 101 102 /*force FW load success */ 103 if(addr == TSCE_DPLL_SIM_FW_LOAD_DONE_REG_ADDR) { 104 return (1 << 15); 105 } 106 107 switch (reg) { 108 case 0x0002: 109 return TSCE_DPLL_ID0; 110 case 0x0003: 111 return TSCE_DPLL_ID1; 112 case 0x900e: 113 return TSCE_DPLL_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 0x01c4; 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 0x000c; 144 case 0xd0b1: 145 return 0x0007; 146 case 0xd0b4: 147 return 0x0077; 148 case 0xd0c1: 149 return 0xa008; 150 case 0xd0d2: 151 return 0x0006; 152 case 0xd0e2: 153 return 0x0002; 154 case 0xd0f4: 155 /* this is not the default value. value is changed to pass through 156 * core int. 157 */ 158 return 0x8271; 159 case 0xd111: 160 return 0x002b; 161 case 0xd148: 162 return 0x0007; 163 default: 164 return 0; 165 } 166 return 0; 167 } 168 169 STATIC uint32_t 170 tsce_dpll_sim_reg_copies_get(uint32_t addr) 171 { 172 uint32_t devad, reg; 173 174 devad = TSCE_DPLL_DEVAD_GET(addr); 175 reg = TSCE_DPLL_REG_GET(addr); 176 177 if (reg == TSCE_DPLL_AER || reg == TSCE_DPLL_BLK) { 178 return 1; 179 } 180 181 if (devad == 0) { 182 if ((reg & 0xf000) == 0x9000) { 183 return 1; 184 } 185 if ((reg & 0xf000) == 0xa000) { 186 return 2; 187 } 188 return 4; 189 } else if (devad == 1) { 190 return 4; 191 } 192 return 0; 193 } 194 195 STATIC int 196 _tsce_dpll_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 197 { 198 int idx; 199 uint32_t mask; 200 201 phymod_sim_entry_t *pse; 202 203 if (pms_data == NULL || pms_data->entries == NULL) { 204 return PHYMOD_E_INIT; 205 } 206 207 /* Support optional write mask in upper 16 bits */ 208 mask = (data >> 16); 209 if (mask == 0) { 210 mask = 0xffff; 211 } 212 data &= mask; 213 214 /* Check if this register has been written already */ 215 for (idx = 0; idx < pms_data->entries_used; idx++) { 216 pse = &pms_data->entries[idx]; 217 if (pse->addr == addr) { 218 pse->data &= ~mask; 219 pse->data |= data; 220 DBG_VERB(("_tsce_dpll_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n", 221 addr, pse->data)); 222 return PHYMOD_E_NONE; 223 } 224 } 225 226 /* Check if database is full */ 227 if (pms_data->entries_used >= pms_data->num_entries) { 228 return PHYMOD_E_RESOURCE; 229 } 230 231 /* Check if new data matches default value */ 232 if (data == tsce_dpll_sim_default_data_get(addr)) { 233 return PHYMOD_E_NONE; 234 } 235 236 /* Add new register value */ 237 pse = &pms_data->entries[pms_data->entries_used++]; 238 pse->addr = addr; 239 pse->data = data; 240 241 DBG_VERB(("_tsce_dpll_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n", 242 addr, pse->data)); 243 244 return PHYMOD_E_NONE; 245 } 246 247 STATIC uint32_t 248 tsce_dpll_sim_write_adjust(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 249 { 250 uint32_t devad, reg, val; 251 252 devad = TSCE_DPLL_DEVAD_GET(addr); 253 reg = TSCE_DPLL_REG_GET(addr); 254 255 if (devad == 0) { 256 switch (reg) { 257 case 0xc050: 258 /* Set SW_SPEED_CHANGE_DONE and SW_SPEED_CONFIG_VLD in status reg */ 259 tsce_dpll_sim_write(pms_data, addr + 1, 0x3); 260 /* Force link up in RX_X4_PCS_LIVE_STS register 261 offset 0x104 = 0xc154 - 0xc050 */ 262 tsce_dpll_sim_write(pms_data, addr + 0x104, 0x1b); 263 /* Sync configured speed into final speed status register 0x20 = 0xc070 - 0xc050 */ 264 tsce_dpll_sim_read(pms_data, addr + 0x20, &val); 265 val = (val & 0xffff00ff) | ((data & 0xff)<<8); 266 tsce_dpll_sim_write(pms_data, addr + 0x20, val); 267 break; 268 case 0xc055: 269 /* Sync configured Lane Number into Lane Number status register 0x1B = 0xc070 - 0xc055 */ 270 tsce_dpll_sim_read(pms_data, addr + 0x1B, &val); 271 val = (val & 0xfffffff8) | (data & 0x7); 272 tsce_dpll_sim_write(pms_data, addr + 0x1B, val); 273 break; 274 default: 275 break; 276 } 277 } else if (devad == 1) { 278 switch (reg) { 279 case 0xd127: 280 /* Sync configured all lanes' PLL_CAL_CTL7 register. Only one PLL/VCO per core */ 281 /* Write lanes 0, 1, 2 and 3 */ 282 addr = TSCE_DPLL_ADDR(devad, 0, reg); 283 (void)_tsce_dpll_sim_write(pms_data, addr, data); 284 285 addr = TSCE_DPLL_ADDR(devad, 1, reg); 286 (void)_tsce_dpll_sim_write(pms_data, addr, data); 287 288 addr = TSCE_DPLL_ADDR(devad, 2, reg); 289 (void)_tsce_dpll_sim_write(pms_data, addr, data); 290 291 addr = TSCE_DPLL_ADDR(devad, 3, reg); 292 (void)_tsce_dpll_sim_write(pms_data, addr, data); 293 break; 294 case 0xd203: 295 /* write data register to UC_RAM_WRDATA(0xd203) 296 * read data register from UC_RAM_RDDATA(0xd204) 297 */ 298 _tsce_dpll_sim_write(pms_data, addr + 1, data); 299 break; 300 case 0xd0c1: 301 /* Sync configured SIGDET_CTL1 register. When a port is set disable, 302 * the port's RX_X4_PCS_LIVE_STS register deletes link status. 303 * RX_X4_PCS_LIVE_STS register is PCS register, so need set devad to 0. 304 * 0xF6D = 0xd0c1 - 0xc154*/ 305 tsce_dpll_sim_read(pms_data, (addr - 0xF6D) & 0x7ffff, &val); 306 val = (val & 0xfffd) | (((data&0x80)==0x80)?0:0x2); 307 tsce_dpll_sim_write(pms_data, (addr - 0xF6D) & 0x7ffff, val); 308 break; 309 default: 310 break; 311 } 312 } 313 314 return data; 315 } 316 317 STATIC int 318 tsce_dpll_sim_init(phymod_sim_data_t *pms_data, 319 int num_entries, phymod_sim_entry_t *entries) 320 { 321 if (pms_data != NULL) { 322 PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data)); 323 pms_data->num_entries = num_entries; 324 pms_data->entries = entries; 325 } 326 return PHYMOD_E_NONE; 327 } 328 329 STATIC int 330 tsce_dpll_sim_reset(phymod_sim_data_t *pms_data) 331 { 332 uint32_t sim_size; 333 334 if (pms_data == NULL || pms_data->entries == NULL) { 335 return PHYMOD_E_INIT; 336 } 337 338 pms_data->entries_used = 0; 339 sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t); 340 PHYMOD_MEMSET(pms_data->entries, 0, sim_size); 341 342 return PHYMOD_E_NONE; 343 } 344 345 STATIC int 346 tsce_dpll_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data) 347 { 348 int idx; 349 uint32_t aer, blk, devad, reg, copies; 350 uint32_t lane = 0; 351 phymod_sim_entry_t *pse; 352 353 if (pms_data == NULL || pms_data->entries == NULL) { 354 return PHYMOD_E_INIT; 355 } 356 357 devad = 0; 358 359 if (addr < TSCE_DPLL_BLK) { 360 /* Assume clause 22 access */ 361 (void)tsce_dpll_sim_read(pms_data, TSCE_DPLL_BLK, &blk); 362 /* IEEE bit */ 363 if (addr & 0x10) { 364 blk |= 0x8000; 365 } else { 366 blk &= ~0x8000; 367 } 368 addr = (addr & 0xf) | (blk & 0xfff0); 369 if (addr != TSCE_DPLL_AER && addr != TSCE_DPLL_BLK) { 370 (void)tsce_dpll_sim_read(pms_data, TSCE_DPLL_AER, &aer); 371 addr |= (aer << 16); 372 } 373 } else { 374 /* Extract devad if clause 45 address format */ 375 if ((addr & TSCE_DPLL_CL45_MASK) == TSCE_DPLL_CL45) { 376 devad = (addr >> 16) & 0x1f; 377 addr &= 0xffff; 378 } 379 } 380 381 if (addr != TSCE_DPLL_AER && addr != TSCE_DPLL_BLK) { 382 /* Assume AER is in upper 16 bits */ 383 aer = (addr >> 16); 384 if (aer == 0) { 385 /* Try reading real AER instead */ 386 (void)tsce_dpll_sim_read(pms_data, TSCE_DPLL_AER, &aer); 387 } 388 /* Add clause 45 devad (if used) */ 389 if (devad) { 390 aer |= (devad << 11); 391 addr = (addr & 0xffff) | (aer << 16); 392 } 393 lane = (aer & 0x7); 394 if (lane > 3) { 395 /* Force lane 0 if lane is invalid */ 396 addr = TSCE_DPLL_ADDR(TSCE_DPLL_DEVAD_GET(addr), 0, TSCE_DPLL_REG_GET(addr)); 397 } 398 } 399 400 /* Adjust lane according to number of copies */ 401 devad = TSCE_DPLL_DEVAD_GET(addr); 402 reg = TSCE_DPLL_REG_GET(addr); 403 copies = tsce_dpll_sim_reg_copies_get(addr); 404 if (copies == 1) { 405 lane = 0; 406 } else if (copies == 2) { 407 lane &= ~0x1; 408 } 409 addr = TSCE_DPLL_ADDR(devad, lane, reg); 410 411 /* for DSC_UC_CTRL always succeed */ 412 if(addr == TSCE_DPLL_SIM_DSC_UC_CTRL_REG_ADDR) { 413 *data = 0x80; 414 DBG_VERB(("tsce_dpll_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 415 addr, *data)); 416 return PHYMOD_E_NONE; 417 } 418 419 /* Check if this register has been written already */ 420 for (idx = 0; idx < pms_data->entries_used; idx++) { 421 pse = &pms_data->entries[idx]; 422 if (pse->addr == addr) { 423 *data = pse->data; 424 DBG_VERB(("tsce_dpll_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 425 addr, *data)); 426 return PHYMOD_E_NONE; 427 } 428 } 429 430 /* Return default value if register was never written */ 431 *data = tsce_dpll_sim_default_data_get(addr); 432 433 DBG_VERB(("tsce_dpll_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n", 434 addr, *data)); 435 436 return PHYMOD_E_NONE; 437 } 438 439 STATIC int 440 tsce_dpll_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 441 { 442 uint32_t aer, blk, devad, reg, copies; 443 uint32_t lane = 0; 444 445 if (pms_data == NULL || pms_data->entries == NULL) { 446 return PHYMOD_E_INIT; 447 } 448 449 devad = 0; 450 451 if (addr < TSCE_DPLL_BLK) { 452 /* Assume clause 22 access */ 453 (void)tsce_dpll_sim_read(pms_data, TSCE_DPLL_BLK, &blk); 454 /* IEEE bit */ 455 if (addr & 0x10) { 456 blk |= 0x8000; 457 } else { 458 blk &= ~0x8000; 459 } 460 addr = (addr & 0xf) | (blk & 0xfff0); 461 if (addr != TSCE_DPLL_AER && addr != TSCE_DPLL_BLK) { 462 (void)tsce_dpll_sim_read(pms_data, TSCE_DPLL_AER, &aer); 463 addr |= (aer << 16); 464 } 465 } else { 466 /* Extract devad if clause 45 address format */ 467 if ((addr & TSCE_DPLL_CL45_MASK) == TSCE_DPLL_CL45) { 468 devad = (addr >> 16) & 0x1f; 469 addr &= 0xffff; 470 } 471 } 472 473 if (addr != TSCE_DPLL_AER && addr != TSCE_DPLL_BLK) { 474 /* Assume AER is in upper 16 bits */ 475 aer = (addr >> 16); 476 if (aer == 0) { 477 /* Try reading real AER instead */ 478 (void)tsce_dpll_sim_read(pms_data, TSCE_DPLL_AER, &aer); 479 } 480 /* Add clause 45 devad (if used) */ 481 if (devad) { 482 aer |= (devad << 11); 483 addr = (addr & 0xffff) | (aer << 16); 484 } 485 lane = (aer & 0x7); 486 if (lane > 6) { 487 return PHYMOD_E_PARAM; 488 } 489 if (lane > 3) { 490 /* 491 * Handle lane broadcast 492 * 493 * Note that we use lane 8 instead of lane 0 to prevent a 494 * broadcast loop. The value 8 will become 0 when masked 495 * with 0x7, but it prevents the AER in the upper 16 bits 496 * from being zero, which will cause the code above to 497 * obtain the AER value from register 0xffde. 498 */ 499 reg = TSCE_DPLL_REG_GET(addr); 500 devad = TSCE_DPLL_DEVAD_GET(addr); 501 if (lane == 4 || lane == 6) { 502 /* Write lanes 0 and 1 */ 503 addr = TSCE_DPLL_ADDR(devad, 8, reg); 504 (void)tsce_dpll_sim_write(pms_data, addr, data); 505 addr = TSCE_DPLL_ADDR(devad, 1, reg); 506 (void)tsce_dpll_sim_write(pms_data, addr, data); 507 } 508 if (lane == 5 || lane == 6) { 509 /* Write lanes 2 and 3 */ 510 addr = TSCE_DPLL_ADDR(devad, 2, reg); 511 (void)tsce_dpll_sim_write(pms_data, addr, data); 512 addr = TSCE_DPLL_ADDR(devad, 3, reg); 513 (void)tsce_dpll_sim_write(pms_data, addr, data); 514 } 515 return PHYMOD_E_NONE; 516 } 517 } 518 519 /* Adjust data and/or related registers */ 520 data = tsce_dpll_sim_write_adjust(pms_data, addr, data); 521 522 /* Adjust lane according to number of copies */ 523 devad = TSCE_DPLL_DEVAD_GET(addr); 524 reg = TSCE_DPLL_REG_GET(addr); 525 copies = tsce_dpll_sim_reg_copies_get(addr); 526 if (copies == 1) { 527 lane = 0; 528 } else if (copies == 2) { 529 lane &= ~0x1; 530 } 531 addr = TSCE_DPLL_ADDR(devad, lane, reg); 532 533 return _tsce_dpll_sim_write(pms_data, addr, data); 534 } 535 536 STATIC int 537 tsce_dpll_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event) 538 { 539 if (pms_data == NULL || pms_data->entries == NULL) { 540 return PHYMOD_E_INIT; 541 } 542 543 return PHYMOD_E_NONE; 544 } 545 546 phymod_sim_drv_t tsce_dpll_sim_drv = { 547 tsce_dpll_sim_init, 548 tsce_dpll_sim_reset, 549 tsce_dpll_sim_read, 550 tsce_dpll_sim_write, 551 tsce_dpll_sim_event 552 }; 553