qtce_sim.c (14165B)
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 * Viper 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 QTCE_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift)) 47 #define QTCE_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 QTCE_DEVAD_SHIFT 27 54 #define QTCE_DEVAD_MASK 0x1f 55 #define QTCE_DEVAD_GET(_addr) \ 56 QTCE_BF_GET(_addr, QTCE_DEVAD_MASK, QTCE_DEVAD_SHIFT) 57 #define QTCE_LANE_SHIFT 16 58 #define QTCE_LANE_MASK 0x7 59 #define QTCE_LANE_GET(_addr) \ 60 QTCE_BF_GET(_addr, QTCE_LANE_MASK, QTCE_LANE_SHIFT) 61 #define QTCE_REG_SHIFT 0 62 #define QTCE_REG_MASK 0xffff 63 #define QTCE_REG_GET(_addr) \ 64 QTCE_BF_GET(_addr, QTCE_REG_MASK, QTCE_REG_SHIFT) 65 66 #define QTCE_ADDR(_devad, _lane, _reg) \ 67 (((_devad) << QTCE_DEVAD_SHIFT) + \ 68 ((_lane) << QTCE_LANE_SHIFT) + \ 69 ((_reg) << QTCE_REG_SHIFT)) 70 71 #define QTCE_AER QTCE_ADDR(0, 0, 0xffde) 72 #define QTCE_BLK QTCE_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 QTCE_CL45 (0x20 << 16) 79 #define QTCE_CL45_MASK (0xe0 << 16) 80 81 /* Forward declarations */ 82 STATIC int 83 qtce_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data); 84 STATIC int 85 qtce_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data); 86 87 STATIC uint32_t 88 qtce_sim_default_data_get(uint32_t addr) 89 { 90 uint32_t devad, reg; 91 92 devad = QTCE_DEVAD_GET(addr); 93 reg = QTCE_REG_GET(addr); 94 95 if (devad == 0) { 96 switch (reg) { 97 case 0x0002: 98 return 0x600d; 99 case 0x0003: 100 return 0x8770; 101 case 0x9007: /* MAIN_SERDESID */ 102 return 0x02d5; 103 case 0xc041: /* sc_x4_final_config_status_sp(0)_A */ 104 case 0xc051: /* sc_x4_final_config_status_sp(1)_A */ 105 case 0xc061: /* sc_x4_final_config_status_sp(2)_A */ 106 case 0xc071: /* sc_x4_final_config_status_sp(3)_A */ 107 return 0x20; 108 case 0xc020: /* sc_x4_control_control */ 109 return 0x1ff; 110 case 0xc179: 111 return 0xff; 112 default: 113 break; 114 } 115 } else if (devad == 1) { 116 switch (reg) { 117 case 0xd205: /* UC_DOWNLOAD_STS */ 118 return 0x0080; 119 case 0xd089: 120 return 0x0007; 121 case 0xd0f8: 122 return 0x0007; 123 default: 124 break; 125 } 126 } 127 128 return 0; 129 } 130 131 STATIC uint32_t 132 qtce_sim_reg_copies_get(uint32_t addr) 133 { 134 uint32_t devad, reg; 135 136 devad = QTCE_DEVAD_GET(addr); 137 reg = QTCE_REG_GET(addr); 138 139 if (reg == QTCE_AER || reg == QTCE_BLK) { 140 return 1; 141 } 142 143 if (devad == 0) { 144 if ((reg & 0xf000) == 0x9000) { 145 return 1; 146 } 147 if ((reg & 0xf000) == 0xa000) { 148 return 2; 149 } 150 return 4; 151 } else if (devad == 1) { 152 return 4; 153 } 154 return 0; 155 } 156 157 STATIC uint32_t 158 qtce_sim_write_adjust(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 159 { 160 uint32_t devad, reg; 161 uint32_t addr_tmp, data_tmp, data_r; 162 uint32_t i, mask; 163 164 devad = QTCE_DEVAD_GET(addr); 165 reg = QTCE_REG_GET(addr); 166 167 if (devad == 0) { 168 switch (reg) { 169 case 0xc020: 170 /* Set SW_SPEED_CHANGE_DONE and SW_SPEED_CONFIG_VLD in status reg */ 171 qtce_sim_write(pms_data, addr + 2, 0x3); 172 /* Sync speed to 0xc040 sc_x4_final_config_status_sp0_resolved_speed */ 173 qtce_sim_write(pms_data, addr + 0x20, data & 0Xff); 174 /* Set 0xc179 RX_X4_Status0_pcs_live_status and 0xc178 RX_X4_Status0_pcs_latched_status. force all ports link up */ 175 qtce_sim_write(pms_data, addr + 0x158, 0x0); 176 qtce_sim_write(pms_data, addr + 0x159, 0xff); 177 break; 178 case 0xc021: 179 mask = (data >> 16); 180 if (mask == 0) { 181 mask = 0xffff; 182 } 183 184 qtce_sim_read(pms_data, addr, &data_r); 185 data_r &= ~mask; 186 data |= data_r; 187 188 for (i = 0; i < 4; i++) { 189 addr_tmp = addr + 0x20 + i*0x10; /* register 0xc041, 0xc051, 0xc061, 0xc071 */ 190 data_tmp = (data >> i*2) & 0x3; /* 0: 1G, 1: 100M, 2: 10M for 0xc021*/ 191 data_tmp = data_tmp == 0 ? 2 : data_tmp == 1 ? 0 : 1; /* 0: 100M, 1: 10M, 2: 1G for 0xc041 */ 192 qtce_sim_read(pms_data, addr_tmp, &data_r); 193 qtce_sim_write(pms_data, addr_tmp, ((data_r & 0xffcf) | (data_tmp << 4))); 194 } 195 break; 196 default: 197 break; 198 } 199 } else if (devad == 1) { 200 switch (reg) { 201 default: 202 break; 203 } 204 } 205 206 return data; 207 } 208 209 STATIC int 210 qtce_sim_init(phymod_sim_data_t *pms_data, 211 int num_entries, phymod_sim_entry_t *entries) 212 { 213 if (pms_data != NULL) { 214 PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data)); 215 pms_data->num_entries = num_entries; 216 pms_data->entries = entries; 217 } 218 return PHYMOD_E_NONE; 219 } 220 221 STATIC int 222 qtce_sim_reset(phymod_sim_data_t *pms_data) 223 { 224 uint32_t sim_size; 225 226 if (pms_data == NULL || pms_data->entries == NULL) { 227 return PHYMOD_E_INIT; 228 } 229 230 pms_data->entries_used = 0; 231 sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t); 232 PHYMOD_MEMSET(pms_data->entries, 0, sim_size); 233 234 return PHYMOD_E_NONE; 235 } 236 237 STATIC int 238 qtce_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data) 239 { 240 int idx; 241 uint32_t aer, blk, devad, reg, copies; 242 uint32_t lane = 0; 243 phymod_sim_entry_t *pse; 244 245 if (pms_data == NULL || pms_data->entries == NULL) { 246 return PHYMOD_E_INIT; 247 } 248 249 devad = 0; 250 251 if (addr < QTCE_BLK) { 252 /* Assume clause 22 access */ 253 (void)qtce_sim_read(pms_data, QTCE_BLK, &blk); 254 /* IEEE bit */ 255 if (addr & 0x10) { 256 blk |= 0x8000; 257 } else { 258 blk &= ~0x8000; 259 } 260 addr = (addr & 0xf) | (blk & 0xfff0); 261 if (addr != QTCE_AER && addr != QTCE_BLK) { 262 (void)qtce_sim_read(pms_data, QTCE_AER, &aer); 263 addr |= (aer << 16); 264 } 265 } else { 266 /* Extract devad if clause 45 address format */ 267 if ((addr & QTCE_CL45_MASK) == QTCE_CL45) { 268 devad = (addr >> 16) & 0x1f; 269 addr &= 0xffff; 270 } 271 } 272 273 if (addr != QTCE_AER && addr != QTCE_BLK) { 274 /* Assume AER is in upper 16 bits */ 275 aer = (addr >> 16); 276 if (aer == 0) { 277 /* Try reading real AER instead */ 278 (void)qtce_sim_read(pms_data, QTCE_AER, &aer); 279 } 280 /* Add clause 45 devad (if used) */ 281 if (devad) { 282 aer |= (devad << 11); 283 addr = (addr & 0xffff) | (aer << 16); 284 } 285 lane = (aer & 0x7); 286 if (lane > 3) { 287 /* Force lane 0 if lane is invalid */ 288 addr = QTCE_ADDR(QTCE_DEVAD_GET(addr), 0, QTCE_REG_GET(addr)); 289 } 290 } 291 292 /* Adjust lane according to number of copies */ 293 devad = QTCE_DEVAD_GET(addr); 294 reg = QTCE_REG_GET(addr); 295 copies = qtce_sim_reg_copies_get(addr); 296 if (copies == 1) { 297 lane = 0; 298 } else if (copies == 2) { 299 lane &= ~0x1; 300 } 301 addr = QTCE_ADDR(devad, lane, reg); 302 303 if (addr == 0x800d00d) { /* DSC_UC_CTL:UC_DSC_READY_FOR_CMD */ 304 *data = 0x80; 305 return PHYMOD_E_NONE; 306 } else if (addr == 0x800d00e) { /* DSC_SCRATCH */ 307 *data = 0xf6c0; /* expected CRC */ 308 return PHYMOD_E_NONE; 309 } 310 311 /* Check if this register has been written already */ 312 for (idx = 0; idx < pms_data->entries_used; idx++) { 313 pse = &pms_data->entries[idx]; 314 if (pse->addr == addr) { 315 *data = pse->data; 316 DBG_VERB(("qtce_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 317 addr, *data)); 318 return PHYMOD_E_NONE; 319 } 320 } 321 322 /* Return default value if register was never written */ 323 *data = qtce_sim_default_data_get(addr); 324 325 DBG_VERB(("qtce_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n", 326 addr, *data)); 327 328 return PHYMOD_E_NONE; 329 } 330 331 STATIC int 332 qtce_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 333 { 334 int idx; 335 uint32_t aer, blk, devad, reg, copies, mask; 336 uint32_t lane = 0; 337 phymod_sim_entry_t *pse; 338 339 if (pms_data == NULL || pms_data->entries == NULL) { 340 return PHYMOD_E_INIT; 341 } 342 343 devad = 0; 344 345 if (addr < QTCE_BLK) { 346 /* Assume clause 22 access */ 347 (void)qtce_sim_read(pms_data, QTCE_BLK, &blk); 348 /* IEEE bit */ 349 if (addr & 0x10) { 350 blk |= 0x8000; 351 } else { 352 blk &= ~0x8000; 353 } 354 addr = (addr & 0xf) | (blk & 0xfff0); 355 if (addr != QTCE_AER && addr != QTCE_BLK) { 356 (void)qtce_sim_read(pms_data, QTCE_AER, &aer); 357 addr |= (aer << 16); 358 } 359 } else { 360 /* Extract devad if clause 45 address format */ 361 if ((addr & QTCE_CL45_MASK) == QTCE_CL45) { 362 devad = (addr >> 16) & 0x1f; 363 addr &= 0xffff; 364 } 365 } 366 367 if (addr != QTCE_AER && addr != QTCE_BLK) { 368 /* Assume AER is in upper 16 bits */ 369 aer = (addr >> 16); 370 if (aer == 0) { 371 /* Try reading real AER instead */ 372 (void)qtce_sim_read(pms_data, QTCE_AER, &aer); 373 } 374 /* Add clause 45 devad (if used) */ 375 if (devad) { 376 aer |= (devad << 11); 377 addr = (addr & 0xffff) | (aer << 16); 378 } 379 lane = (aer & 0x7); 380 if (lane > 6) { 381 return PHYMOD_E_PARAM; 382 } 383 if (lane > 3) { 384 /* 385 * Handle lane broadcast 386 * 387 * Note that we use lane 8 instead of lane 0 to prevent a 388 * broadcast loop. The value 8 will become 0 when masked 389 * with 0x7, but it prevents the AER in the upper 16 bits 390 * from being zero, which will cause the code above to 391 * obtain the AER value from register 0xffde. 392 */ 393 reg = QTCE_REG_GET(addr); 394 devad = QTCE_DEVAD_GET(addr); 395 if (lane == 4 || lane == 6) { 396 /* Write lanes 0 and 1 */ 397 addr = QTCE_ADDR(devad, 8, reg); 398 (void)qtce_sim_write(pms_data, addr, data); 399 addr = QTCE_ADDR(devad, 1, reg); 400 (void)qtce_sim_write(pms_data, addr, data); 401 } 402 if (lane == 5 || lane == 6) { 403 /* Write lanes 2 and 3 */ 404 addr = QTCE_ADDR(devad, 2, reg); 405 (void)qtce_sim_write(pms_data, addr, data); 406 addr = QTCE_ADDR(devad, 3, reg); 407 (void)qtce_sim_write(pms_data, addr, data); 408 } 409 return PHYMOD_E_NONE; 410 } 411 } 412 413 /* Adjust data and/or related registers */ 414 data = qtce_sim_write_adjust(pms_data, addr, data); 415 416 /* Adjust lane according to number of copies */ 417 devad = QTCE_DEVAD_GET(addr); 418 reg = QTCE_REG_GET(addr); 419 copies = qtce_sim_reg_copies_get(addr); 420 if (copies == 1) { 421 lane = 0; 422 } else if (copies == 2) { 423 lane &= ~0x1; 424 } 425 addr = QTCE_ADDR(devad, lane, reg); 426 427 /* Support optional write mask in upper 16 bits */ 428 mask = (data >> 16); 429 if (mask == 0) { 430 mask = 0xffff; 431 } 432 data &= mask; 433 434 /* Check if this register has been written already */ 435 for (idx = 0; idx < pms_data->entries_used; idx++) { 436 pse = &pms_data->entries[idx]; 437 if (pse->addr == addr) { 438 pse->data &= ~mask; 439 pse->data |= data; 440 DBG_VERB(("qtce_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n", 441 addr, pse->data)); 442 return PHYMOD_E_NONE; 443 } 444 } 445 446 /* Check if database is full */ 447 if (pms_data->entries_used >= pms_data->num_entries) { 448 return PHYMOD_E_RESOURCE; 449 } 450 451 /* Check if new data matches default value */ 452 if (data == qtce_sim_default_data_get(addr)) { 453 return PHYMOD_E_NONE; 454 } 455 456 /* Add new register value */ 457 pse = &pms_data->entries[pms_data->entries_used++]; 458 pse->addr = addr; 459 pse->data = data; 460 461 DBG_VERB(("qtce_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n", 462 addr, pse->data)); 463 464 return PHYMOD_E_NONE; 465 } 466 467 STATIC int 468 qtce_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event) 469 { 470 if (pms_data == NULL || pms_data->entries == NULL) { 471 return PHYMOD_E_INIT; 472 } 473 474 return PHYMOD_E_NONE; 475 } 476 477 phymod_sim_drv_t qtce_sim_drv = { 478 qtce_sim_init, 479 qtce_sim_reset, 480 qtce_sim_read, 481 qtce_sim_write, 482 qtce_sim_event 483 }; 484