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