falcon_sim.c (15978B)
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 FALCON_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift)) 47 #define FALCON_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 FALCON_DEVAD_SHIFT 27 54 #define FALCON_DEVAD_MASK 0x1f 55 #define FALCON_DEVAD_GET(_addr) \ 56 FALCON_BF_GET(_addr, FALCON_DEVAD_MASK, FALCON_DEVAD_SHIFT) 57 #define FALCON_LANE_SHIFT 16 58 #define FALCON_LANE_MASK 0x7 59 #define FALCON_LANE_GET(_addr) \ 60 FALCON_BF_GET(_addr, FALCON_LANE_MASK, FALCON_LANE_SHIFT) 61 #define FALCON_REG_SHIFT 0 62 #define FALCON_REG_MASK 0xffff 63 #define FALCON_REG_GET(_addr) \ 64 FALCON_BF_GET(_addr, FALCON_REG_MASK, FALCON_REG_SHIFT) 65 66 #define FALCON_ADDR(_devad, _lane, _reg) \ 67 (((_devad) << FALCON_DEVAD_SHIFT) + \ 68 ((_lane) << FALCON_LANE_SHIFT) + \ 69 ((_reg) << FALCON_REG_SHIFT)) 70 71 #define FALCON_AER FALCON_ADDR(0, 0, 0xffde) 72 #define FALCON_BLK FALCON_ADDR(0, 0, 0x001f) 73 74 /*RAM sim*/ 75 #define FALCON_RAM_WR_ADDR_REG_MS (0xd204) 76 #define FALCON_RAM_WR_ADDR_REG_LS (0xd205) 77 #define FALCON_RAM_WR_DATA_REG_MS (0xd206) 78 #define FALCON_RAM_WR_DATA_REG_LS (0xd207) 79 #define FALCON_RAM_RD_ADDR_REG_MS (0xd208) 80 #define FALCON_RAM_RD_ADDR_REG_LS (0xd209) 81 #define FALCON_RAM_RD_DATA_REG_MS (0xd20a) 82 #define FALCON_RAM_RD_DATA_REG_LS (0xd20b) 83 #define FALCON_IS_RAM_ADDR_REG(reg) (reg == FALCON_RAM_WR_ADDR_REG_MS || reg == FALCON_RAM_WR_ADDR_REG_LS || reg == FALCON_RAM_RD_ADDR_REG_MS || reg == FALCON_RAM_RD_ADDR_REG_LS) 84 #define FALCON_IS_RAM_DATA_REG(reg) (reg == FALCON_RAM_WR_DATA_REG_MS || reg == FALCON_RAM_WR_DATA_REG_LS || reg == FALCON_RAM_RD_DATA_REG_MS || reg == FALCON_RAM_RD_DATA_REG_LS) 85 86 #define FALCON_SIM_ENTRY_F_RAM_LS_DATA_ENTRY (0x1) 87 #define FALCON_SIM_ENTRY_F_RAM_MS_DATA_ENTRY (0x2) 88 #define FALCON_SIM_ENTRY_F_RAM_LS_ADDR_ENTRY (0x4) 89 #define FALCON_SIM_ENTRY_F_RAM_MS_ADDR_ENTRY (0x8) 90 91 92 93 /* 94 * The CL45 indicator is used to determine whether the upper 16 bits 95 * of the address is an AER value or a clause 45 DEVAD. 96 */ 97 #define FALCON_CL45 (0x20 << 16) 98 #define FALCON_CL45_MASK (0xe0 << 16) 99 100 STATIC uint32_t 101 falcon_sim_default_data_get(uint32_t addr) 102 { 103 uint32_t devad, reg; 104 105 devad = FALCON_DEVAD_GET(addr); 106 reg = FALCON_REG_GET(addr); 107 108 if (devad == 0) { 109 switch (reg) { 110 case 0x0002: 111 return 0; 112 case 0x0003: 113 return 0; 114 case 0xd100: 115 return 0x02db; 116 case 0xd108: 117 case 0xd0b9: 118 /* 119 * Reset check 120 */ 121 return 0x7; 122 default: 123 break; 124 } 125 }else if (devad == 1) { 126 switch (reg) { 127 case 0xd100: 128 return 0x02db; 129 default: 130 break; 131 } 132 } 133 return 0; 134 } 135 136 STATIC uint32_t 137 falcon_sim_reg_copies_get(uint32_t addr) 138 { 139 uint32_t devad, reg; 140 141 devad = FALCON_DEVAD_GET(addr); 142 reg = FALCON_REG_GET(addr); 143 144 if (reg == FALCON_AER || reg == FALCON_BLK) { 145 return 1; 146 } 147 148 if (devad == 0) { 149 if ((reg & 0xf000) == 0x9000) { 150 return 1; 151 } 152 if ((reg & 0xf000) == 0xa000) { 153 return 2; 154 } 155 return 4; 156 } 157 return 0; 158 } 159 160 STATIC int 161 falcon_sim_init(phymod_sim_data_t *pms_data, 162 int num_entries, phymod_sim_entry_t *entries) 163 { 164 if (pms_data != NULL) { 165 PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data)); 166 pms_data->num_entries = num_entries; 167 pms_data->entries = entries; 168 } 169 return PHYMOD_E_NONE; 170 } 171 172 STATIC int 173 falcon_sim_reset(phymod_sim_data_t *pms_data) 174 { 175 uint32_t sim_size; 176 177 if (pms_data == NULL || pms_data->entries == NULL) { 178 return PHYMOD_E_INIT; 179 } 180 181 pms_data->entries_used = 0; 182 sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t); 183 PHYMOD_MEMSET(pms_data->entries, 0, sim_size); 184 185 return PHYMOD_E_NONE; 186 } 187 188 STATIC int 189 falcon_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data) 190 { 191 int idx; 192 uint32_t aer, blk, devad, reg, copies; 193 uint32_t lane = 0; 194 phymod_sim_entry_t *pse; 195 uint32_t pse_flag = 0; 196 uint32_t addr_ms, addr_ls; 197 198 if (pms_data == NULL || pms_data->entries == NULL) { 199 return PHYMOD_E_INIT; 200 } 201 202 devad = 0; 203 204 205 if (addr < FALCON_BLK) { 206 /* Assume clause 22 access */ 207 (void)falcon_sim_read(pms_data, FALCON_BLK, &blk); 208 if (addr & 0x10) { 209 /* IEEE bit */ 210 blk |= 0x8000; 211 } 212 addr = (addr & 0xf) | (blk & 0xfff0); 213 if (addr != FALCON_AER && addr != FALCON_BLK) { 214 (void)falcon_sim_read(pms_data, FALCON_AER, &aer); 215 addr |= (aer << 16); 216 } 217 } else { 218 /* Extract devad if clause 45 address format */ 219 if ((addr & FALCON_CL45_MASK) == FALCON_CL45) { 220 devad = (addr >> 16) & 0x1f; 221 addr &= 0xffff; 222 } 223 } 224 225 if (addr != FALCON_AER && addr != FALCON_BLK) { 226 /* Assume AER is in upper 16 bits */ 227 aer = (addr >> 16); 228 if (aer == 0) { 229 /* Try reading real AER instead */ 230 (void)falcon_sim_read(pms_data, FALCON_AER, &aer); 231 } 232 /* Add clause 45 devad (if used) */ 233 if (devad) { 234 aer |= (devad << 11); 235 addr = (addr & 0xffff) | (aer << 16); 236 } 237 lane = (aer & 0x7); 238 if (lane > 3) { 239 /* Force lane 0 if lane is invalid */ 240 addr = FALCON_ADDR(FALCON_DEVAD_GET(addr), 0, FALCON_REG_GET(addr)); 241 } 242 } 243 244 /* Adjust lane according to number of copies */ 245 devad = FALCON_DEVAD_GET(addr); 246 reg = FALCON_REG_GET(addr); 247 copies = falcon_sim_reg_copies_get(addr); 248 if (copies == 1) { 249 lane = 0; 250 } else if (copies == 2) { 251 lane &= ~0x1; 252 } 253 254 255 /*handle ram read/write*/ 256 if (FALCON_IS_RAM_DATA_REG(reg) || FALCON_IS_RAM_ADDR_REG(reg)) { 257 258 if (FALCON_IS_RAM_DATA_REG(reg)) { 259 (void)falcon_sim_read(pms_data, FALCON_ADDR(devad, lane, FALCON_RAM_RD_ADDR_REG_LS), &addr_ls); 260 (void)falcon_sim_read(pms_data, FALCON_ADDR(devad, lane, FALCON_RAM_RD_ADDR_REG_MS), &addr_ms); 261 addr = addr_ls | (addr_ms << 16); 262 if (reg == FALCON_RAM_RD_DATA_REG_LS || reg == FALCON_RAM_WR_DATA_REG_LS) { 263 pse_flag |= FALCON_SIM_ENTRY_F_RAM_LS_DATA_ENTRY; 264 } else { 265 pse_flag |= FALCON_SIM_ENTRY_F_RAM_MS_DATA_ENTRY; 266 } 267 } else { /*FALCON_IS_RAM_ADDR_REG*/ 268 addr = FALCON_ADDR(devad, lane, 0 /*dummy*/); 269 if (reg == FALCON_RAM_RD_ADDR_REG_LS || reg == FALCON_RAM_WR_ADDR_REG_LS) { 270 pse_flag |= FALCON_SIM_ENTRY_F_RAM_LS_DATA_ENTRY; 271 } else { 272 pse_flag |= FALCON_SIM_ENTRY_F_RAM_MS_DATA_ENTRY; 273 } 274 } 275 276 /* Check if this register has been written already */ 277 for (idx = 0; idx < pms_data->entries_used; idx++) { 278 pse = &pms_data->entries[idx]; 279 if ((pse->addr == addr) && (pse->flags == pse_flag)) { 280 *data = pse->data; 281 DBG_VERB(("falcon_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 282 addr, *data)); 283 return PHYMOD_E_NONE; 284 } 285 } 286 287 *data = 0; 288 return PHYMOD_E_NONE; 289 } 290 291 292 293 addr = FALCON_ADDR(devad, lane, reg); 294 295 /* Check if this register has been written already */ 296 for (idx = 0; idx < pms_data->entries_used; idx++) { 297 pse = &pms_data->entries[idx]; 298 if ((pse->addr == addr) && (pse->flags == pse_flag)) { 299 *data = pse->data; 300 DBG_VERB(("falcon_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 301 addr, *data)); 302 return PHYMOD_E_NONE; 303 } 304 } 305 306 /* Return default value if register was never written */ 307 *data = falcon_sim_default_data_get(addr); 308 309 DBG_VERB(("falcon_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n", 310 addr, *data)); 311 312 return PHYMOD_E_NONE; 313 } 314 315 STATIC int 316 falcon_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 317 { 318 int idx; 319 uint32_t aer, blk, devad, reg, copies, mask; 320 uint32_t lane = 0; 321 phymod_sim_entry_t *pse; 322 uint32_t pse_flag = 0; 323 uint32_t addr_ms, addr_ls; 324 325 if (pms_data == NULL || pms_data->entries == NULL) { 326 return PHYMOD_E_INIT; 327 } 328 329 devad = 0; 330 331 if (addr < FALCON_BLK) { 332 /* Assume clause 22 access */ 333 (void)falcon_sim_read(pms_data, FALCON_BLK, &blk); 334 if (addr & 0x10) { 335 /* IEEE bit */ 336 blk |= 0x8000; 337 } 338 addr = (addr & 0xf) | (blk & 0xfff0); 339 if (addr != FALCON_AER && addr != FALCON_BLK) { 340 (void)falcon_sim_read(pms_data, FALCON_AER, &aer); 341 addr |= (aer << 16); 342 } 343 } else { 344 /* Extract devad if clause 45 address format */ 345 if ((addr & FALCON_CL45_MASK) == FALCON_CL45) { 346 devad = (addr >> 16) & 0x1f; 347 addr &= 0xffff; 348 } 349 } 350 351 if (addr != FALCON_AER && addr != FALCON_BLK) { 352 /* Assume AER is in upper 16 bits */ 353 aer = (addr >> 16); 354 if (aer == 0) { 355 /* Try reading real AER instead */ 356 (void)falcon_sim_read(pms_data, FALCON_AER, &aer); 357 } 358 /* Add clause 45 devad (if used) */ 359 if (devad) { 360 aer |= (devad << 11); 361 addr = (addr & 0xffff) | (aer << 16); 362 } 363 lane = (aer & 0x7); 364 if (lane > 6) { 365 return PHYMOD_E_PARAM; 366 } 367 if (lane > 3) { 368 /* 369 * Handle lane broadcast 370 * 371 * Note that we use lane 8 instead of lane 0 to prevent a 372 * broadcast loop. The value 8 will become 0 when masked 373 * with 0x7, but it prevents the AER in the upper 16 bits 374 * from being zero, which will cause the code above to 375 * obtain the AER value from register 0xffde. 376 */ 377 reg = FALCON_REG_GET(addr); 378 devad = FALCON_DEVAD_GET(addr); 379 if (lane == 4 || lane == 6) { 380 /* Write lanes 0 and 1 */ 381 addr = FALCON_ADDR(devad, 8, reg); 382 (void)falcon_sim_write(pms_data, addr, data); 383 addr = FALCON_ADDR(devad, 1, reg); 384 (void)falcon_sim_write(pms_data, addr, data); 385 } 386 if (lane == 5 || lane == 6) { 387 /* Write lanes 2 and 3 */ 388 addr = FALCON_ADDR(devad, 2, reg); 389 (void)falcon_sim_write(pms_data, addr, data); 390 addr = FALCON_ADDR(devad, 3, reg); 391 (void)falcon_sim_write(pms_data, addr, data); 392 } 393 return PHYMOD_E_NONE; 394 } 395 } 396 397 /* Adjust lane according to number of copies */ 398 devad = FALCON_DEVAD_GET(addr); 399 reg = FALCON_REG_GET(addr); 400 copies = falcon_sim_reg_copies_get(addr); 401 if (copies == 1) { 402 lane = 0; 403 } else if (copies == 2) { 404 lane &= ~0x1; 405 } 406 407 if (FALCON_IS_RAM_DATA_REG(reg) || FALCON_IS_RAM_ADDR_REG(reg)) { 408 409 if (FALCON_IS_RAM_DATA_REG(reg)) { 410 (void)falcon_sim_read(pms_data, FALCON_ADDR(devad, lane, FALCON_RAM_RD_ADDR_REG_LS), &addr_ls); 411 (void)falcon_sim_read(pms_data, FALCON_ADDR(devad, lane, FALCON_RAM_RD_ADDR_REG_MS), &addr_ms); 412 addr = addr_ls | (addr_ms << 16); 413 if (reg == FALCON_RAM_RD_DATA_REG_LS || reg == FALCON_RAM_WR_DATA_REG_LS) { 414 pse_flag |= FALCON_SIM_ENTRY_F_RAM_LS_DATA_ENTRY; 415 } else { 416 pse_flag |= FALCON_SIM_ENTRY_F_RAM_MS_DATA_ENTRY; 417 } 418 } else { /*FALCON_IS_RAM_ADDR_REG*/ 419 addr = FALCON_ADDR(devad, lane, 0/*dummy*/); 420 if (reg == FALCON_RAM_RD_ADDR_REG_LS || reg == FALCON_RAM_WR_ADDR_REG_LS) { 421 pse_flag |= FALCON_SIM_ENTRY_F_RAM_LS_DATA_ENTRY; 422 } else { 423 pse_flag |= FALCON_SIM_ENTRY_F_RAM_MS_DATA_ENTRY; 424 } 425 } 426 427 /* Check if this register has been written already */ 428 for (idx = 0; idx < pms_data->entries_used; idx++) { 429 pse = &pms_data->entries[idx]; 430 if ((pse->addr == addr) && (pse->flags == pse_flag)) { 431 pse->data = data; 432 DBG_VERB(("falcon_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" - flag = %u \n", 433 addr, pse->data, pse_flag)); 434 return PHYMOD_E_NONE; 435 } 436 } 437 438 pse = &pms_data->entries[pms_data->entries_used++]; 439 pse->addr = addr; 440 pse->data = data; 441 pse->flags = pse_flag; 442 443 DBG_VERB(("falcon_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" - flag = %u (new)\n", 444 addr, pse->data, pse->flags)); 445 446 return PHYMOD_E_NONE; 447 } 448 449 addr = FALCON_ADDR(devad, lane, reg); 450 451 /* Support optional write mask in upper 16 bits */ 452 mask = (data >> 16); 453 if (mask == 0) { 454 mask = 0xffff; 455 } 456 data &= mask; 457 458 459 460 /* Check if this register has been written already */ 461 for (idx = 0; idx < pms_data->entries_used; idx++) { 462 pse = &pms_data->entries[idx]; 463 if ((pse->addr == addr) && (pse->flags == pse_flag)) { 464 pse->data &= ~mask; 465 pse->data |= data; 466 DBG_VERB(("falcon_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n", 467 addr, pse->data)); 468 return PHYMOD_E_NONE; 469 } 470 } 471 472 /* Check if database is full */ 473 if (pms_data->entries_used >= pms_data->num_entries) { 474 return PHYMOD_E_RESOURCE; 475 } 476 477 /* Check if new data matches default value */ 478 if (data == falcon_sim_default_data_get(addr)) { 479 return PHYMOD_E_NONE; 480 } 481 482 /* Add new register value */ 483 pse = &pms_data->entries[pms_data->entries_used++]; 484 pse->addr = addr; 485 pse->data = data; 486 pse->flags = pse_flag; 487 488 DBG_VERB(("falcon_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n", 489 addr, pse->data)); 490 491 return PHYMOD_E_NONE; 492 } 493 494 STATIC int 495 falcon_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event) 496 { 497 if (pms_data == NULL || pms_data->entries == NULL) { 498 return PHYMOD_E_INIT; 499 } 500 501 return PHYMOD_E_NONE; 502 } 503 504 phymod_sim_drv_t falcon_sim_drv = { 505 falcon_sim_init, 506 falcon_sim_reset, 507 falcon_sim_read, 508 falcon_sim_write, 509 falcon_sim_event 510 }; 511