eagle_sim.c (11083B)
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 43 /* Convenience macro */ 44 #define DBG_VERB PHYMOD_DEBUG_VERBOSE 45 46 /* Bit field get/set macros */ 47 #define EAGLE_BF_SET(_val, _mask, _shift) _val |= ((_mask) << (_shift)) 48 #define EAGLE_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 EAGLE_DEVAD_SHIFT 27 55 #define EAGLE_DEVAD_MASK 0x1f 56 #define EAGLE_DEVAD_GET(_addr) \ 57 EAGLE_BF_GET(_addr, EAGLE_DEVAD_MASK, EAGLE_DEVAD_SHIFT) 58 #define EAGLE_LANE_SHIFT 16 59 #define EAGLE_LANE_MASK 0x7 60 #define EAGLE_LANE_GET(_addr) \ 61 EAGLE_BF_GET(_addr, EAGLE_LANE_MASK, EAGLE_LANE_SHIFT) 62 #define EAGLE_REG_SHIFT 0 63 #define EAGLE_REG_MASK 0xffff 64 #define EAGLE_REG_GET(_addr) \ 65 EAGLE_BF_GET(_addr, EAGLE_REG_MASK, EAGLE_REG_SHIFT) 66 67 #define EAGLE_ADDR(_devad, _lane, _reg) \ 68 (((_devad) << EAGLE_DEVAD_SHIFT) + \ 69 ((_lane) << EAGLE_LANE_SHIFT) + \ 70 ((_reg) << EAGLE_REG_SHIFT)) 71 72 #define EAGLE_AER EAGLE_ADDR(0, 0, 0xffde) 73 #define EAGLE_BLK EAGLE_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 EAGLE_CL45 (0x20 << 16) 80 #define EAGLE_CL45_MASK (0xe0 << 16) 81 82 STATIC uint32_t 83 eagle_sim_default_data_get(uint32_t addr) 84 { 85 uint32_t devad, reg; 86 87 devad = EAGLE_DEVAD_GET(addr); 88 reg = EAGLE_REG_GET(addr); 89 90 if (devad == 0) { 91 switch (reg) { 92 case 0x0002: 93 return 0; 94 case 0x0003: 95 return 0; 96 case 0x900e: 97 return 0x02d2; 98 case 0xd0f0: 99 return 0x02da; 100 default: 101 break; 102 } 103 } else { 104 /*revid_model*/ 105 if (devad == 1 && reg == 0xd0f0) { 106 return 0x1b; 107 } else { 108 return 0; 109 } 110 } 111 112 return 0; 113 114 } 115 116 STATIC uint32_t 117 eagle_sim_reg_copies_get(uint32_t addr) 118 { 119 uint32_t reg; 120 121 reg = EAGLE_REG_GET(addr); 122 123 if (reg == EAGLE_AER || reg == EAGLE_BLK) { 124 return 1; 125 } 126 127 return 4; 128 } 129 130 STATIC int 131 eagle_sim_init(phymod_sim_data_t *pms_data, 132 int num_entries, phymod_sim_entry_t *entries) 133 { 134 if (pms_data != NULL) { 135 PHYMOD_MEMSET(pms_data, 0, sizeof(*pms_data)); 136 pms_data->num_entries = num_entries; 137 pms_data->entries = entries; 138 } 139 return PHYMOD_E_NONE; 140 } 141 142 STATIC int 143 eagle_sim_reset(phymod_sim_data_t *pms_data) 144 { 145 uint32_t sim_size; 146 147 if (pms_data == NULL || pms_data->entries == NULL) { 148 return PHYMOD_E_INIT; 149 } 150 151 pms_data->entries_used = 0; 152 sim_size = pms_data->num_entries * sizeof(phymod_sim_entry_t); 153 PHYMOD_MEMSET(pms_data->entries, 0, sim_size); 154 155 return PHYMOD_E_NONE; 156 } 157 158 STATIC int 159 eagle_sim_read(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t *data) 160 { 161 int idx; 162 uint32_t aer, blk, devad, reg, copies; 163 uint32_t lane = 0; 164 phymod_sim_entry_t *pse; 165 166 if (pms_data == NULL || pms_data->entries == NULL) { 167 return PHYMOD_E_INIT; 168 } 169 170 devad = 0; 171 172 if (addr < EAGLE_BLK) { 173 /* Assume clause 22 access */ 174 (void)eagle_sim_read(pms_data, EAGLE_BLK, &blk); 175 if (addr & 0x10) { 176 /* IEEE bit */ 177 blk |= 0x8000; 178 } 179 addr = (addr & 0xf) | (blk & 0xfff0); 180 if (addr != EAGLE_AER && addr != EAGLE_BLK) { 181 (void)eagle_sim_read(pms_data, EAGLE_AER, &aer); 182 addr |= (aer << 16); 183 } 184 } else { 185 /* Extract devad if clause 45 address format */ 186 if ((addr & EAGLE_CL45_MASK) == EAGLE_CL45) { 187 devad = (addr >> 16) & 0x1f; 188 addr &= 0xffff; 189 } 190 } 191 192 if (addr != EAGLE_AER && addr != EAGLE_BLK) { 193 /* Assume AER is in upper 16 bits */ 194 aer = (addr >> 16); 195 if (aer == 0) { 196 /* Try reading real AER instead */ 197 (void)eagle_sim_read(pms_data, EAGLE_AER, &aer); 198 } 199 /* Add clause 45 devad (if used) */ 200 if (devad) { 201 aer |= (devad << 11); 202 addr = (addr & 0xffff) | (aer << 16); 203 } 204 lane = (aer & 0x7); 205 if (lane > 3) { 206 /* Force lane 0 if lane is invalid */ 207 addr = EAGLE_ADDR(EAGLE_DEVAD_GET(addr), 0, EAGLE_REG_GET(addr)); 208 } 209 } 210 211 /* Adjust lane according to number of copies */ 212 devad = EAGLE_DEVAD_GET(addr); 213 reg = EAGLE_REG_GET(addr); 214 copies = eagle_sim_reg_copies_get(addr); 215 if (copies == 1) { 216 lane = 0; 217 } else if (copies == 2) { 218 lane &= ~0x1; 219 } 220 addr = EAGLE_ADDR(devad, lane, reg); 221 222 /* Check if this register has been written already */ 223 for (idx = 0; idx < pms_data->entries_used; idx++) { 224 pse = &pms_data->entries[idx]; 225 if (pse->addr == addr) { 226 *data = pse->data; 227 DBG_VERB(("eagle_sim_read 0x%08"PRIx32" = 0x%04"PRIx32"\n", 228 addr, *data)); 229 return PHYMOD_E_NONE; 230 } 231 } 232 233 /* Return default value if register was never written */ 234 *data = eagle_sim_default_data_get(addr); 235 236 DBG_VERB(("eagle_sim_read 0x%08"PRIx32" = [0x%04"PRIx32"]\n", 237 addr, *data)); 238 239 return PHYMOD_E_NONE; 240 } 241 242 STATIC int 243 eagle_sim_write(phymod_sim_data_t *pms_data, uint32_t addr, uint32_t data) 244 { 245 int idx; 246 uint32_t aer, blk, devad, reg, copies, mask; 247 uint32_t lane = 0; 248 phymod_sim_entry_t *pse; 249 250 if (pms_data == NULL || pms_data->entries == NULL) { 251 return PHYMOD_E_INIT; 252 } 253 254 devad = 0; 255 256 if (addr < EAGLE_BLK) { 257 /* Assume clause 22 access */ 258 (void)eagle_sim_read(pms_data, EAGLE_BLK, &blk); 259 if (addr & 0x10) { 260 /* IEEE bit */ 261 blk |= 0x8000; 262 } 263 addr = (addr & 0xf) | (blk & 0xfff0); 264 if (addr != EAGLE_AER && addr != EAGLE_BLK) { 265 (void)eagle_sim_read(pms_data, EAGLE_AER, &aer); 266 addr |= (aer << 16); 267 } 268 } else { 269 /* Extract devad if clause 45 address format */ 270 if ((addr & EAGLE_CL45_MASK) == EAGLE_CL45) { 271 devad = (addr >> 16) & 0x1f; 272 addr &= 0xffff; 273 } 274 } 275 276 if (addr != EAGLE_AER && addr != EAGLE_BLK) { 277 /* Assume AER is in upper 16 bits */ 278 aer = (addr >> 16); 279 if (aer == 0) { 280 /* Try reading real AER instead */ 281 (void)eagle_sim_read(pms_data, EAGLE_AER, &aer); 282 } 283 /* Add clause 45 devad (if used) */ 284 if (devad) { 285 aer |= (devad << 11); 286 addr = (addr & 0xffff) | (aer << 16); 287 } 288 lane = (aer & 0x7); 289 if (lane > 6) { 290 return PHYMOD_E_PARAM; 291 } 292 if (lane > 3) { 293 /* 294 * Handle lane broadcast 295 * 296 * Note that we use lane 8 instead of lane 0 to prevent a 297 * broadcast loop. The value 8 will become 0 when masked 298 * with 0x7, but it prevents the AER in the upper 16 bits 299 * from being zero, which will cause the code above to 300 * obtain the AER value from register 0xffde. 301 */ 302 reg = EAGLE_REG_GET(addr); 303 devad = EAGLE_DEVAD_GET(addr); 304 if (lane == 4 || lane == 6) { 305 /* Write lanes 0 and 1 */ 306 addr = EAGLE_ADDR(devad, 8, reg); 307 (void)eagle_sim_write(pms_data, addr, data); 308 addr = EAGLE_ADDR(devad, 1, reg); 309 (void)eagle_sim_write(pms_data, addr, data); 310 } 311 if (lane == 5 || lane == 6) { 312 /* Write lanes 2 and 3 */ 313 addr = EAGLE_ADDR(devad, 2, reg); 314 (void)eagle_sim_write(pms_data, addr, data); 315 addr = EAGLE_ADDR(devad, 3, reg); 316 (void)eagle_sim_write(pms_data, addr, data); 317 } 318 return PHYMOD_E_NONE; 319 } 320 } 321 322 /* Adjust lane according to number of copies */ 323 devad = EAGLE_DEVAD_GET(addr); 324 reg = EAGLE_REG_GET(addr); 325 copies = eagle_sim_reg_copies_get(addr); 326 if (copies == 1) { 327 lane = 0; 328 } else if (copies == 2) { 329 lane &= ~0x1; 330 } 331 addr = EAGLE_ADDR(devad, lane, reg); 332 333 /* Support optional write mask in upper 16 bits */ 334 mask = (data >> 16); 335 if (mask == 0) { 336 mask = 0xffff; 337 } 338 data &= mask; 339 340 /* Check if this register has been written already */ 341 for (idx = 0; idx < pms_data->entries_used; idx++) { 342 pse = &pms_data->entries[idx]; 343 if (pse->addr == addr) { 344 pse->data &= ~mask; 345 pse->data |= data; 346 DBG_VERB(("eagle_sim_write 0x%08"PRIx32" = 0x%04"PRIx32"\n", 347 addr, pse->data)); 348 return PHYMOD_E_NONE; 349 } 350 } 351 352 /* Check if database is full */ 353 if (pms_data->entries_used >= pms_data->num_entries) { 354 return PHYMOD_E_RESOURCE; 355 } 356 357 /* Check if new data matches default value */ 358 if (data == eagle_sim_default_data_get(addr)) { 359 return PHYMOD_E_NONE; 360 } 361 362 /* Add new register value */ 363 pse = &pms_data->entries[pms_data->entries_used++]; 364 pse->addr = addr; 365 pse->data = data; 366 367 DBG_VERB(("eagle_sim_write 0x%08"PRIx32" = 0x%04"PRIx32" (new)\n", 368 addr, pse->data)); 369 370 return PHYMOD_E_NONE; 371 } 372 373 STATIC int 374 eagle_sim_event(phymod_sim_data_t *pms_data, phymod_sim_event_t event) 375 { 376 if (pms_data == NULL || pms_data->entries == NULL) { 377 return PHYMOD_E_INIT; 378 } 379 380 return PHYMOD_E_NONE; 381 } 382 383 phymod_sim_drv_t eagle_sim_drv = { 384 eagle_sim_init, 385 eagle_sim_reset, 386 eagle_sim_read, 387 eagle_sim_write, 388 eagle_sim_event 389 }; 390