tsce_sim.c (15209B)
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 #include <phymod/chip/bcmi_tsce_xgxs_resetval.h> 42 43 /* Convenience macro */ 44 #define DBG_VERB PHYMOD_DEBUG_VERBOSE 45 46 /* Bit field get/set macros */ 47 #define TSCE_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift)) 48 #define TSCE_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_DEVAD_SHIFT 27 55 #define TSCE_DEVAD_MASK 0x1f 56 #define TSCE_DEVAD_GET(_addr) \ 57 TSCE_BF_GET(_addr, TSCE_DEVAD_MASK, TSCE_DEVAD_SHIFT) 58 #define TSCE_LANE_SHIFT 16 59 #define TSCE_LANE_MASK 0x7 60 #define TSCE_LANE_GET(_addr) \ 61 TSCE_BF_GET(_addr, TSCE_LANE_MASK, TSCE_LANE_SHIFT) 62 #define TSCE_REG_SHIFT 0 63 #define TSCE_REG_MASK 0xffff 64 #define TSCE_REG_GET(_addr) \ 65 TSCE_BF_GET(_addr, TSCE_REG_MASK, TSCE_REG_SHIFT) 66 #define TSCE_PLLIDX_SHIF 24 67 #define TSCE_PLLIDX_MASK 0x3 68 #define TSCE_PLLIDX_GET(_addr) \ 69 TSCE_BF_GET(_addr, TSCE_PLLIDX_MASK, TSCE_PLLIDX_SHIF) 70 71 #define TSCE_ADDR(_devad, _lane, _reg, _pllidx) \ 72 (((_devad) << TSCE_DEVAD_SHIFT) + \ 73 ((_lane) << TSCE_LANE_SHIFT) + \ 74 ((_reg) << TSCE_REG_SHIFT)) + \ 75 ((_pllidx << TSCE_PLLIDX_SHIF)) 76 77 #define TSCE_AER TSCE_ADDR(0, 0, 0xffde, 0) 78 #define TSCE_BLK TSCE_ADDR(0, 0, 0x001f, 0) 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 TSCE_CL45 (0x20 << 16) 85 #define TSCE_CL45_MASK (0xe0 << 16) 86 87 #define TSCE_SIM_FW_LOAD_DONE_REG_ADDR (0x0800d205) 88 #define TSCE_SIM_DSC_UC_CTRL_REG_ADDR (0x0800d00d) 89 90 /* Forward declarations */ 91 STATIC int 92 tsce_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data); 93 STATIC int 94 tsce_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data); 95 96 STATIC uint32_t 97 tsce_sim_default_data_get(uint32_t addr) 98 { 99 uint32_t devad, reg; 100 101 devad = TSCE_DEVAD_GET(addr); 102 reg = TSCE_REG_GET(addr); 103 104 /*force FW load success */ 105 if(addr == TSCE_SIM_FW_LOAD_DONE_REG_ADDR) { 106 return (1 << 15); 107 } 108 109 return bcmi_tsce_xgxs_resetval_get(devad, reg); 110 } 111 112 STATIC uint32_t 113 tsce_sim_reg_copies_get(uint32_t addr) 114 { 115 uint32_t devad, reg; 116 117 devad = TSCE_DEVAD_GET(addr); 118 reg = TSCE_REG_GET(addr); 119 120 if (reg == TSCE_AER || reg == TSCE_BLK) { 121 return 1; 122 } 123 124 if (devad == 0) { 125 if ((reg & 0xf000) == 0x9000) { 126 return 1; 127 } 128 if ((reg & 0xf000) == 0xa000) { 129 return 2; 130 } 131 return 4; 132 } else if (devad == 1) { 133 return 4; 134 } 135 return 0; 136 } 137 138 STATIC int 139 _tsce_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 140 { 141 int idx; 142 uint32_t mask; 143 144 phymod_sim_entry_t *pse; 145 146 if (pms_data == NULL || pms_data->entries == NULL) { 147 return PHYMOD_E_INIT; 148 } 149 150 /* Support optional write mask in upper 16 bits */ 151 mask = (data >> 16); 152 if (mask == 0) { 153 mask = 0xffff; 154 } 155 data &= mask; 156 157 /* Check if this register has been written already */ 158 for (idx = 0; idx < pms_data->entries_used; idx++) { 159 pse = &pms_data->entries[idx]; 160 if (pse->addr == addr) { 161 pse->data &= ~mask; 162 pse->data |= data; 163 DBG_VERB(("_tsce_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n", 164 addr, pse->data)); 165 return PHYMOD_E_NONE; 166 } 167 } 168 169 /* Check if database is full */ 170 if (pms_data->entries_used >= pms_data->num_entries) { 171 return PHYMOD_E_RESOURCE; 172 } 173 174 /* Check if new data matches default value */ 175 if (data == tsce_sim_default_data_get(addr)) { 176 return PHYMOD_E_NONE; 177 } 178 179 /* Add new register value */ 180 pse = &pms_data->entries[pms_data->entries_used++]; 181 pse->addr = addr; 182 pse->data = data; 183 184 DBG_VERB(("_tsce_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n", 185 addr, pse->data)); 186 187 return PHYMOD_E_NONE; 188 } 189 190 STATIC uint32_t 191 tsce_sim_write_adjust(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 192 { 193 uint32_t devad, reg, val; 194 uint32_t pllidx = 0; 195 196 devad = TSCE_DEVAD_GET(addr); 197 reg = TSCE_REG_GET(addr); 198 pllidx = TSCE_PLLIDX_GET(addr); 199 200 if (devad == 0) { 201 switch (reg) { 202 case 0xc050: 203 /* Set SW_SPEED_CHANGE_DONE and SW_SPEED_CONFIG_VLD in status reg */ 204 tsce_sim_write(pms_data, addr + 1, 0x3); 205 /* Force link up in RX_X4_PCS_LIVE_STS register 206 offset 0x104 = 0xc154 - 0xc050 */ 207 tsce_sim_write(pms_data, addr + 0x104, 0x1b); 208 /* Sync configured speed into final speed status register 0x20 = 0xc070 - 0xc050 */ 209 tsce_sim_read(pms_data, addr + 0x20, &val); 210 val = (val & 0xffff00ff) | ((data & 0xff)<<8); 211 tsce_sim_write(pms_data, addr + 0x20, val); 212 break; 213 case 0xc055: 214 /* Sync configured Lane Number into Lane Number status register 0x1B = 0xc070 - 0xc055 */ 215 tsce_sim_read(pms_data, addr + 0x1B, &val); 216 val = (val & 0xfffffff8) | (data & 0x7); 217 tsce_sim_write(pms_data, addr + 0x1B, val); 218 break; 219 default: 220 break; 221 } 222 } else if (devad == 1) { 223 switch (reg) { 224 case 0xd127: 225 /* Sync configured all lanes' PLL_CAL_CTL7 register. Only one PLL/VCO per core */ 226 /* Write lanes 0, 1, 2 and 3 */ 227 addr = TSCE_ADDR(devad, 0, reg, pllidx); 228 (void)_tsce_sim_write(pms_data, addr, data); 229 230 addr = TSCE_ADDR(devad, 1, reg, pllidx); 231 (void)_tsce_sim_write(pms_data, addr, data); 232 233 addr = TSCE_ADDR(devad, 2, reg, pllidx); 234 (void)_tsce_sim_write(pms_data, addr, data); 235 236 addr = TSCE_ADDR(devad, 3, reg, pllidx); 237 (void)_tsce_sim_write(pms_data, addr, data); 238 break; 239 case 0xd203: 240 /* write data register to UC_RAM_WRDATA(0xd203) 241 * read data register from UC_RAM_RDDATA(0xd204) 242 */ 243 _tsce_sim_write(pms_data, addr + 1, data); 244 break; 245 case 0xd0c1: 246 /* Sync configured SIGDET_CTL1 register. When a port is set disable, 247 * the port's RX_X4_PCS_LIVE_STS register deletes link status. 248 * RX_X4_PCS_LIVE_STS register is PCS register, so need set devad to 0. 249 * 0xF6D = 0xd0c1 - 0xc154*/ 250 tsce_sim_read(pms_data, (addr - 0xF6D) & 0x7ffff, &val); 251 val = (val & 0xfffd) | (((data&0x80)==0x80)?0:0x2); 252 tsce_sim_write(pms_data, (addr - 0xF6D) & 0x7ffff, val); 253 break; 254 default: 255 break; 256 } 257 } 258 259 return data; 260 } 261 262 STATIC int 263 tsce_sim_init(phymod_sim_data_t *pms_data, 264 int num_entries, phymod_sim_entry_t *entries) 265 { 266 if (pms_data != NULL) { 267 PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data)); 268 pms_data->num_entries = num_entries; 269 pms_data->entries = entries; 270 } 271 return PHYMOD_E_NONE; 272 } 273 274 STATIC int 275 tsce_sim_reset(phymod_sim_data_t *pms_data) 276 { 277 uint32_t sim_size; 278 279 if (pms_data == NULL || pms_data->entries == NULL) { 280 return PHYMOD_E_INIT; 281 } 282 283 pms_data->entries_used = 0; 284 sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t); 285 PHYMOD_MEMSET(pms_data->entries, 0, sim_size); 286 287 return PHYMOD_E_NONE; 288 } 289 290 STATIC int 291 tsce_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data) 292 { 293 int idx; 294 uint32_t aer, blk, devad, reg, copies; 295 uint32_t lane = 0, pllidx = 0; 296 phymod_sim_entry_t *pse; 297 298 if (pms_data == NULL || pms_data->entries == NULL) { 299 return PHYMOD_E_INIT; 300 } 301 302 devad = 0; 303 304 if (addr < TSCE_BLK) { 305 /* Assume clause 22 access */ 306 (void)tsce_sim_read(pms_data, TSCE_BLK, &blk); 307 /* IEEE bit */ 308 if (addr & 0x10) { 309 blk |= 0x8000; 310 } else { 311 blk &= ~0x8000; 312 } 313 addr = (addr & 0xf) | (blk & 0xfff0); 314 if (addr != TSCE_AER && addr != TSCE_BLK) { 315 (void)tsce_sim_read(pms_data, TSCE_AER, &aer); 316 addr |= (aer << 16); 317 } 318 } else { 319 /* Extract devad if clause 45 address format */ 320 if ((addr & TSCE_CL45_MASK) == TSCE_CL45) { 321 devad = (addr >> 16) & 0x1f; 322 addr &= 0xffff; 323 } 324 } 325 326 if (addr != TSCE_AER && addr != TSCE_BLK) { 327 /* Assume AER is in upper 16 bits */ 328 aer = (addr >> 16); 329 if (aer == 0) { 330 /* Try reading real AER instead */ 331 (void)tsce_sim_read(pms_data, TSCE_AER, &aer); 332 } 333 /* Add clause 45 devad (if used) */ 334 if (devad) { 335 aer |= (devad << 11); 336 addr = (addr & 0xffff) | (aer << 16); 337 } 338 lane = (aer & 0x7); 339 if (lane > 3) { 340 /* Force lane 0 if lane is invalid */ 341 addr = TSCE_ADDR(TSCE_DEVAD_GET(addr), 0, TSCE_REG_GET(addr), TSCE_PLLIDX_GET(addr)); 342 } 343 } 344 345 /* Adjust lane according to number of copies */ 346 devad = TSCE_DEVAD_GET(addr); 347 reg = TSCE_REG_GET(addr); 348 pllidx = TSCE_PLLIDX_GET(addr); 349 copies = tsce_sim_reg_copies_get(addr); 350 if (copies == 1) { 351 lane = 0; 352 } else if (copies == 2) { 353 lane &= ~0x1; 354 } 355 addr = TSCE_ADDR(devad, lane, reg, pllidx); 356 357 /* for DSC_UC_CTRL always succeed */ 358 if(addr == TSCE_SIM_DSC_UC_CTRL_REG_ADDR) { 359 *data = 0x80; 360 DBG_VERB(("tsce_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 361 addr, *data)); 362 return PHYMOD_E_NONE; 363 } 364 365 /* Check if this register has been written already */ 366 for (idx = 0; idx < pms_data->entries_used; idx++) { 367 pse = &pms_data->entries[idx]; 368 if (pse->addr == addr) { 369 *data = pse->data; 370 DBG_VERB(("tsce_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 371 addr, *data)); 372 return PHYMOD_E_NONE; 373 } 374 } 375 376 /* Return default value if register was never written */ 377 *data = tsce_sim_default_data_get(addr); 378 379 DBG_VERB(("tsce_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n", 380 addr, *data)); 381 382 return PHYMOD_E_NONE; 383 } 384 385 STATIC int 386 tsce_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 387 { 388 uint32_t aer, blk, devad, reg, copies; 389 uint32_t lane = 0, pllidx = 0; 390 391 if (pms_data == NULL || pms_data->entries == NULL) { 392 return PHYMOD_E_INIT; 393 } 394 395 devad = 0; 396 397 if (addr < TSCE_BLK) { 398 /* Assume clause 22 access */ 399 (void)tsce_sim_read(pms_data, TSCE_BLK, &blk); 400 /* IEEE bit */ 401 if (addr & 0x10) { 402 blk |= 0x8000; 403 } else { 404 blk &= ~0x8000; 405 } 406 addr = (addr & 0xf) | (blk & 0xfff0); 407 if (addr != TSCE_AER && addr != TSCE_BLK) { 408 (void)tsce_sim_read(pms_data, TSCE_AER, &aer); 409 addr |= (aer << 16); 410 } 411 } else { 412 /* Extract devad if clause 45 address format */ 413 if ((addr & TSCE_CL45_MASK) == TSCE_CL45) { 414 devad = (addr >> 16) & 0x1f; 415 addr &= 0xffff; 416 } 417 } 418 419 if (addr != TSCE_AER && addr != TSCE_BLK) { 420 /* Assume AER is in upper 16 bits */ 421 aer = (addr >> 16); 422 if (aer == 0) { 423 /* Try reading real AER instead */ 424 (void)tsce_sim_read(pms_data, TSCE_AER, &aer); 425 } 426 /* Add clause 45 devad (if used) */ 427 if (devad) { 428 aer |= (devad << 11); 429 addr = (addr & 0xffff) | (aer << 16); 430 } 431 lane = (aer & 0x7); 432 if (lane > 6) { 433 return PHYMOD_E_PARAM; 434 } 435 if (lane > 3) { 436 /* 437 * Handle lane broadcast 438 * 439 * Note that we use lane 8 instead of lane 0 to prevent a 440 * broadcast loop. The value 8 will become 0 when masked 441 * with 0x7, but it prevents the AER in the upper 16 bits 442 * from being zero, which will cause the code above to 443 * obtain the AER value from register 0xffde. 444 */ 445 reg = TSCE_REG_GET(addr); 446 devad = TSCE_DEVAD_GET(addr); 447 pllidx = TSCE_PLLIDX_GET(addr); 448 if (lane == 4 || lane == 6) { 449 /* Write lanes 0 and 1 */ 450 addr = TSCE_ADDR(devad, 8, reg, pllidx); 451 (void)tsce_sim_write(pms_data, addr, data); 452 addr = TSCE_ADDR(devad, 1, reg, pllidx); 453 (void)tsce_sim_write(pms_data, addr, data); 454 } 455 if (lane == 5 || lane == 6) { 456 /* Write lanes 2 and 3 */ 457 addr = TSCE_ADDR(devad, 2, reg, pllidx); 458 (void)tsce_sim_write(pms_data, addr, data); 459 addr = TSCE_ADDR(devad, 3, reg, pllidx); 460 (void)tsce_sim_write(pms_data, addr, data); 461 } 462 return PHYMOD_E_NONE; 463 } 464 } 465 466 /* Adjust data and/or related registers */ 467 data = tsce_sim_write_adjust(pms_data, addr, data); 468 469 /* Adjust lane according to number of copies */ 470 devad = TSCE_DEVAD_GET(addr); 471 reg = TSCE_REG_GET(addr); 472 copies = tsce_sim_reg_copies_get(addr); 473 if (copies == 1) { 474 lane = 0; 475 } else if (copies == 2) { 476 lane &= ~0x1; 477 } 478 pllidx = TSCE_PLLIDX_GET(addr); 479 addr = TSCE_ADDR(devad, lane, reg, pllidx); 480 481 return _tsce_sim_write(pms_data, addr, data); 482 } 483 484 STATIC int 485 tsce_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event) 486 { 487 if (pms_data == NULL || pms_data->entries == NULL) { 488 return PHYMOD_E_INIT; 489 } 490 491 return PHYMOD_E_NONE; 492 } 493 494 phymod_sim_drv_t tsce_sim_drv = { 495 tsce_sim_init, 496 tsce_sim_reset, 497 tsce_sim_read, 498 tsce_sim_write, 499 tsce_sim_event 500 }; 501