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