phymod_tsc_iblk.c (21249B)
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 * XGS internal PHY read function with AER support. 8 * 9 * This function accepts a 32-bit PHY register address and will 10 * properly configure clause 45 DEVAD and XAUI lane access. 11 * Please see phymod_reg.h for additional information. 12 */ 13 14 #include <phymod/acc/phymod_tsc_iblk.h> 15 #include <phymod/phymod_debug.h> 16 17 /* 18 * Some bus drivers can handle both clause 22 and clause 45 access, so 19 * we use this bit to request clause 45 access. 20 */ 21 #define FORCE_CL45 0x20 22 #define LANE_IS_MEMBER(_lnmap, _ln) (((_lnmap) & (0x1 << (_ln))) ? 1 : 0) 23 #define LANE_MAX 16 /* QSGMII has multi-core, support more than 4 lanes */ 24 25 /* TSCBH has 8 lanes, lower 4 lanes belong to MPP0 and upper 4 lanes belong to MPP1 */ 26 #define TSCBH_LANE_IS_MPP1(_ln) (_ln > 3 ? 1: 0) 27 28 int 29 phymod_tsc_iblk_read(const phymod_access_t *pa, uint32_t addr, uint32_t *data) 30 { 31 int ioerr = 0; 32 uint32_t devad = (addr >> 16) & 0xf; 33 uint32_t blkaddr, regaddr; 34 uint32_t lane_map, lane; 35 uint32_t aer, add; 36 phymod_bus_t* bus; 37 uint8_t pll_index = 0; 38 39 add = addr ; 40 41 /* Work around for ARM compiler error [-Werror=unused-but-set-variable] */ 42 if ( add == 0 ) { 43 add = addr; 44 } 45 46 if (pa == NULL) { 47 PHYMOD_VDBG(DBG_ACC,pa,("iblk_rd add=%x pa=null\n", add)); 48 return -1; 49 } 50 51 bus = PHYMOD_ACC_BUS(pa); 52 pll_index = PHYMOD_ACC_PLLIDX(pa) & 0x3; 53 54 /* Do not attempt to read write-only registers */ 55 if (addr & PHYMOD_REG_ACC_TSC_IBLK_WR_ONLY) { 56 *data = 0; 57 PHYMOD_VDBG(DBG_ACC,pa,("iblk_rd add=%x WO=1\n", add)); 58 return 0; 59 } 60 61 /* Determine which lane to read from */ 62 lane = 0; 63 if (addr & PHYMOD_REG_ACC_AER_IBLK_FORCE_LANE) { 64 /* Forcing lane overrides default behavior */ 65 lane = (addr >> PHYMOD_REG_ACCESS_FLAGS_SHIFT) & 0x7; 66 } else { 67 /* Use first lane in lane map by default */ 68 lane_map = PHYMOD_ACC_LANE_MASK(pa); 69 if (lane_map & 0x1) { 70 lane = 0; 71 } else if (lane_map & 0x2) { 72 lane = 1; 73 } else if (lane_map & 0x4) { 74 lane = 2; 75 } else if (lane_map & 0x8) { 76 lane = 3; 77 } else if (lane_map & 0xfff0) { 78 lane = -1; 79 while (lane_map) { 80 lane++; 81 lane_map >>= 1; 82 } 83 } 84 } 85 86 /* Use default DEVAD if none specified */ 87 if(((pa->devad & PHYMOD_ACC_DEVAD_0_OVERRIDE_MASK) != 0) && (devad == 0)) { 88 devad = (pa->devad & PHYMOD_ACC_DEVAD_MASK); 89 /* Force DEVAD if want special address */ 90 } else if(pa->devad & PHYMOD_ACC_DEVAD_FORCE_MASK) { 91 devad = (pa->devad & PHYMOD_ACC_DEVAD_MASK); 92 } 93 94 /* Encode address extension */ 95 aer = lane | (devad << 11) | (pll_index << 8); 96 97 /* Mask raw register value */ 98 addr &= 0xffff; 99 100 /* If bus driver supports lane control, then we are done */ 101 if (PHYMOD_BUS_CAP_LANE_CTRL_GET(bus)) { 102 ioerr += PHYMOD_BUS_READ(pa, addr | (aer << 16), data); 103 PHYMOD_VDBG(DBG_ACC,pa,("iblk_rd sbus add=%x aer=%x adr=%x lm=%0x rtn=%0d d=%x\n", 104 add, aer, addr, pa->lane_mask, ioerr, *data)); 105 return ioerr; 106 } 107 108 /* Use clause 45 access if supported */ 109 if (PHYMOD_ACC_F_CLAUSE45_GET(pa)) { 110 devad |= FORCE_CL45; 111 ioerr += PHYMOD_BUS_WRITE(pa, 0xffde | (devad << 16), aer); 112 ioerr += PHYMOD_BUS_READ(pa, addr | (devad << 16), data); 113 PHYMOD_VDBG(DBG_ACC,pa,("iblk_rd cl45 add=%x dev=%x aer=%x adr=%x lm=%0x rtn=%0d d=%x\n", 114 add, devad, aer, addr, pa->lane_mask, ioerr, *data)); 115 return ioerr; 116 } 117 118 /* Write address extension register */ 119 ioerr += PHYMOD_BUS_WRITE(pa, 0x1f, 0xffd0); 120 ioerr += PHYMOD_BUS_WRITE(pa, 0x1e, aer); 121 122 /* Select block */ 123 blkaddr = addr & 0xfff0; 124 ioerr += PHYMOD_BUS_WRITE(pa, 0x1f, blkaddr); 125 126 /* Read register value */ 127 regaddr = addr & 0xf; 128 if (addr & 0x8000) { 129 regaddr |= 0x10; 130 } 131 ioerr += PHYMOD_BUS_READ(pa, regaddr, data); 132 PHYMOD_VDBG(DBG_ACC,pa,("iblk_rd cl22 add=%x aer=%x blk=%x adr=%x reg=%x lm=%0d rtn=%0d d=%x\n", 133 add, aer, blkaddr, addr, regaddr, pa->lane_mask, ioerr, *data)); 134 return ioerr; 135 } 136 137 138 139 int 140 phymod_tscbh_iblk_read(const phymod_access_t *pa, uint32_t addr, uint32_t *data) 141 { 142 int ioerr = 0; 143 uint32_t devad = (addr >> 16) & 0xf; 144 uint32_t blkaddr, regaddr; 145 uint32_t lane_map, lane; 146 uint32_t aer, add; 147 phymod_bus_t* bus; 148 uint8_t pll_index = 0, mpp_sel = 0; 149 150 add = addr ; 151 152 /* Work around for ARM compiler error [-Werror=unused-but-set-variable] */ 153 if ( add == 0 ) { 154 add = addr; 155 } 156 157 if (pa == NULL) { 158 PHYMOD_VDBG(DBG_ACC,pa,("iblk_rd add=%x pa=null\n", add)); 159 return -1; 160 } 161 162 bus = PHYMOD_ACC_BUS(pa); 163 pll_index = PHYMOD_ACC_PLLIDX(pa) & 0x3; 164 165 /* Do not attempt to read write-only registers */ 166 if (addr & PHYMOD_REG_ACC_TSC_IBLK_WR_ONLY) { 167 *data = 0; 168 PHYMOD_VDBG(DBG_ACC,pa,("iblk_rd add=%x WO=1\n", add)); 169 return 0; 170 } 171 172 /* Determine which lane to read from */ 173 lane = 0; 174 if (addr & PHYMOD_REG_ACC_AER_IBLK_FORCE_LANE) { 175 /* Forcing lane overrides default behavior */ 176 lane = (addr >> PHYMOD_REG_ACCESS_FLAGS_SHIFT) & 0x7; 177 } else { 178 /* Use first lane in lane map by default */ 179 lane_map = PHYMOD_ACC_LANE_MASK(pa); 180 if (lane_map & 0x1) { 181 lane = 0; 182 } else if (lane_map & 0x2) { 183 lane = 1; 184 } else if (lane_map & 0x4) { 185 lane = 2; 186 } else if (lane_map & 0x8) { 187 lane = 3; 188 } else if (lane_map & 0x10) { 189 lane = 4; 190 } else if (lane_map & 0x20) { 191 lane = 5; 192 } else if (lane_map & 0x40) { 193 lane = 6; 194 } else if (lane_map & 0x80) { 195 lane = 7; 196 } 197 } 198 199 /* Use default DEVAD if none specified */ 200 if(((pa->devad & PHYMOD_ACC_DEVAD_0_OVERRIDE_MASK) != 0) && (devad == 0)) { 201 devad = (pa->devad & PHYMOD_ACC_DEVAD_MASK); 202 /* Force DEVAD if want special address */ 203 } else if(pa->devad & PHYMOD_ACC_DEVAD_FORCE_MASK) { 204 devad = (pa->devad & PHYMOD_ACC_DEVAD_MASK); 205 } 206 207 /* Encode address extension based on if pcs or PMD */ 208 if (devad) { 209 /* PMD device read */ 210 aer = lane | (devad << 11) | (pll_index << 8); 211 } else { 212 /* PCS device */ 213 if (TSCBH_LANE_IS_MPP1(lane)) { 214 mpp_sel = PHYMOD_TSCBH_IBLK_MPP_1; 215 } else { 216 mpp_sel = PHYMOD_TSCBH_IBLK_MPP_0; 217 } 218 aer = (lane % 4) | (mpp_sel << PHYMOD_TSCBH_IBLK_MPP_SHIFT); 219 } 220 221 /* Mask raw register value */ 222 addr &= 0xffff; 223 224 /* If bus driver supports lane control, then we are done */ 225 if (PHYMOD_BUS_CAP_LANE_CTRL_GET(bus)) { 226 ioerr += PHYMOD_BUS_READ(pa, addr | (aer << 16), data); 227 PHYMOD_VDBG(DBG_ACC,pa,("iblk_rd sbus add=%x aer=%x adr=%x lm=%0x rtn=%0d d=%x\n", 228 add, aer, addr, pa->lane_mask, ioerr, *data)); 229 return ioerr; 230 } 231 232 /* Use clause 45 access if supported */ 233 if (PHYMOD_ACC_F_CLAUSE45_GET(pa)) { 234 devad |= FORCE_CL45; 235 ioerr += PHYMOD_BUS_WRITE(pa, 0xffde | (devad << 16), aer); 236 ioerr += PHYMOD_BUS_READ(pa, addr | (devad << 16), data); 237 PHYMOD_VDBG(DBG_ACC,pa,("iblk_rd cl45 add=%x dev=%x aer=%x adr=%x lm=%0x rtn=%0d d=%x\n", 238 add, devad, aer, addr, pa->lane_mask, ioerr, *data)); 239 return ioerr; 240 } 241 242 /* Write address extension register */ 243 ioerr += PHYMOD_BUS_WRITE(pa, 0x1f, 0xffd0); 244 ioerr += PHYMOD_BUS_WRITE(pa, 0x1e, aer); 245 246 /* Select block */ 247 blkaddr = addr & 0xfff0; 248 ioerr += PHYMOD_BUS_WRITE(pa, 0x1f, blkaddr); 249 250 /* Read register value */ 251 regaddr = addr & 0xf; 252 if (addr & 0x8000) { 253 regaddr |= 0x10; 254 } 255 ioerr += PHYMOD_BUS_READ(pa, regaddr, data); 256 PHYMOD_VDBG(DBG_ACC,pa,("iblk_rd cl22 add=%x aer=%x blk=%x adr=%x reg=%x lm=%0d rtn=%0d d=%x\n", 257 add, aer, blkaddr, addr, regaddr, pa->lane_mask, ioerr, *data)); 258 return ioerr; 259 } 260 261 262 static int 263 _tsc_iblk_write_lane( 264 const phymod_access_t *pa, 265 uint32_t lane, 266 uint32_t addr, 267 uint32_t data) 268 { 269 int ioerr = 0; 270 uint32_t devad = (addr >> 16) & 0xf; 271 uint32_t blkaddr, regaddr; 272 uint32_t aer, add; 273 uint32_t wr_mask, rdata; 274 phymod_bus_t* bus; 275 uint32_t is_write_disabled; 276 uint8_t pll_index = 0; 277 278 add = addr ; 279 280 /* Work around for ARM compiler error [-Werror=unused-but-set-variable] */ 281 if ( add == 0 ) { 282 add = addr; 283 } 284 285 if (pa == NULL) { 286 PHYMOD_VDBG(DBG_ACC,pa,("iblk_wr add=%x pa=null\n", addr)); 287 return -1; 288 } 289 290 bus = PHYMOD_ACC_BUS(pa); 291 pll_index = PHYMOD_ACC_PLLIDX(pa) & 0x3; 292 293 if (PHYMOD_IS_WRITE_DISABLED(pa, &is_write_disabled) == 0) { 294 if (is_write_disabled) { 295 return ioerr; 296 } 297 } 298 299 /* Use default DEVAD if none specified */ 300 if(((pa->devad & PHYMOD_ACC_DEVAD_0_OVERRIDE_MASK) != 0) && (devad == 0)) { 301 devad = (pa->devad & PHYMOD_ACC_DEVAD_MASK); 302 /* Force DEVAD if want special address */ 303 } else if(pa->devad & PHYMOD_ACC_DEVAD_FORCE_MASK) { 304 devad = (pa->devad & PHYMOD_ACC_DEVAD_MASK); 305 } 306 307 /* Check if write mask is specified */ 308 wr_mask = (data >> 16); 309 if (wr_mask) { 310 /* Read register if bus driver does not support write mask */ 311 if (PHYMOD_BUS_CAP_WR_MODIFY_GET(bus) == 0) { 312 ioerr += phymod_tsc_iblk_read(pa, addr, &rdata); 313 data = (rdata & ~wr_mask) | (data & wr_mask); 314 data &= 0xffff; 315 } 316 } 317 318 /* Encode address extension */ 319 aer = lane | (devad << 11) | (pll_index << 8); 320 321 /* Mask raw register value */ 322 addr &= 0xffff; 323 324 /* If bus driver supports lane control, then we are done */ 325 if (PHYMOD_BUS_CAP_LANE_CTRL_GET(bus)) { 326 ioerr += PHYMOD_BUS_WRITE(pa, addr | (aer << 16), data); 327 PHYMOD_VDBG(DBG_ACC,pa,("iblk_wr sbus add=%x aer=%x adr=%x lm=%0x rtn=%0d d=%x\n", 328 add, aer, addr, pa->lane_mask, ioerr, data)); 329 return ioerr; 330 } 331 332 /* Use clause 45 access if supported */ 333 if (PHYMOD_ACC_F_CLAUSE45_GET(pa)) { 334 addr &= 0xffff; 335 devad |= FORCE_CL45; 336 ioerr += PHYMOD_BUS_WRITE(pa, 0xffde | (devad << 16), aer); 337 ioerr += PHYMOD_BUS_WRITE(pa, addr | (devad << 16), data); 338 PHYMOD_VDBG(DBG_ACC,pa,("iblk_wr cl45 add=%x dev=%x aer=%x adr=%x lm=%0x rtn=%0d d=%x\n", 339 add, devad, aer, addr, pa->lane_mask, ioerr, data)); 340 return ioerr; 341 } 342 343 /* Write address extension register */ 344 ioerr += PHYMOD_BUS_WRITE(pa, 0x1f, 0xffd0); 345 ioerr += PHYMOD_BUS_WRITE(pa, 0x1e, aer); 346 347 /* Select block */ 348 blkaddr = addr & 0xfff0; 349 ioerr += PHYMOD_BUS_WRITE(pa, 0x1f, blkaddr); 350 351 /* Write register value */ 352 regaddr = addr & 0xf; 353 if (addr & 0x8000) { 354 regaddr |= 0x10; 355 } 356 ioerr += PHYMOD_BUS_WRITE(pa, regaddr, data); 357 PHYMOD_VDBG(DBG_ACC,pa,("iblk_wr cl22 add=%x aer=%x blk=%x reg=%x adr=%x lm=%0x rtn=%0d d=%x\n", 358 addr, aer, blkaddr, regaddr, addr, pa->lane_mask, ioerr, data)); 359 return ioerr; 360 } 361 362 static int 363 _tscbh_iblk_write_lane( 364 const phymod_access_t *pa, 365 uint32_t lane, 366 uint32_t addr, 367 uint32_t data) 368 { 369 int ioerr = 0; 370 uint32_t devad = (addr >> 16) & 0xf; 371 uint32_t blkaddr, regaddr; 372 uint32_t aer, add; 373 uint32_t wr_mask, rdata; 374 phymod_bus_t* bus; 375 uint32_t is_write_disabled; 376 uint8_t pll_index = 0; 377 378 add = addr ; 379 380 /* Work around for ARM compiler error [-Werror=unused-but-set-variable] */ 381 if ( add == 0 ) { 382 add = addr; 383 } 384 385 if (pa == NULL) { 386 PHYMOD_VDBG(DBG_ACC,pa,("iblk_wr add=%x pa=null\n", addr)); 387 return -1; 388 } 389 390 bus = PHYMOD_ACC_BUS(pa); 391 pll_index = PHYMOD_ACC_PLLIDX(pa) & 0x3; 392 393 if (PHYMOD_IS_WRITE_DISABLED(pa, &is_write_disabled) == 0) { 394 if (is_write_disabled) { 395 return ioerr; 396 } 397 } 398 399 /* Use default DEVAD if none specified */ 400 if(((pa->devad & PHYMOD_ACC_DEVAD_0_OVERRIDE_MASK) != 0) && (devad == 0)) { 401 devad = (pa->devad & PHYMOD_ACC_DEVAD_MASK); 402 /* Force DEVAD if want special address */ 403 } else if(pa->devad & PHYMOD_ACC_DEVAD_FORCE_MASK) { 404 devad = (pa->devad & PHYMOD_ACC_DEVAD_MASK); 405 } 406 407 /* Check if write mask is specified */ 408 wr_mask = (data >> 16); 409 if (wr_mask) { 410 /* Read register if bus driver does not support write mask */ 411 if (PHYMOD_BUS_CAP_WR_MODIFY_GET(bus) == 0) { 412 ioerr += phymod_tscbh_iblk_read(pa, addr, &rdata); 413 data = (rdata & ~wr_mask) | (data & wr_mask); 414 data &= 0xffff; 415 } 416 } 417 418 /* Encode address extension based on if pcs or PMD */ 419 if (devad) { /* PMD */ 420 aer = lane | (devad << 11) | (pll_index << 8); 421 } else { 422 aer = lane | (devad << 11); 423 } 424 425 /* Mask raw register value */ 426 addr &= 0xffff; 427 428 /* If bus driver supports lane control, then we are done */ 429 if (PHYMOD_BUS_CAP_LANE_CTRL_GET(bus)) { 430 ioerr += PHYMOD_BUS_WRITE(pa, addr | (aer << 16), data); 431 PHYMOD_VDBG(DBG_ACC,pa,("iblk_wr sbus add=%x aer=%x adr=%x lm=%0x rtn=%0d d=%x\n", 432 add, aer, addr, pa->lane_mask, ioerr, data)); 433 return ioerr; 434 } 435 436 /* Use clause 45 access if supported */ 437 if (PHYMOD_ACC_F_CLAUSE45_GET(pa)) { 438 addr &= 0xffff; 439 devad |= FORCE_CL45; 440 ioerr += PHYMOD_BUS_WRITE(pa, 0xffde | (devad << 16), aer); 441 ioerr += PHYMOD_BUS_WRITE(pa, addr | (devad << 16), data); 442 PHYMOD_VDBG(DBG_ACC,pa,("iblk_wr cl45 add=%x dev=%x aer=%x adr=%x lm=%0x rtn=%0d d=%x\n", 443 add, devad, aer, addr, pa->lane_mask, ioerr, data)); 444 return ioerr; 445 } 446 447 /* Write address extension register */ 448 ioerr += PHYMOD_BUS_WRITE(pa, 0x1f, 0xffd0); 449 ioerr += PHYMOD_BUS_WRITE(pa, 0x1e, aer); 450 451 /* Select block */ 452 blkaddr = addr & 0xfff0; 453 ioerr += PHYMOD_BUS_WRITE(pa, 0x1f, blkaddr); 454 455 /* Write register value */ 456 regaddr = addr & 0xf; 457 if (addr & 0x8000) { 458 regaddr |= 0x10; 459 } 460 ioerr += PHYMOD_BUS_WRITE(pa, regaddr, data); 461 PHYMOD_VDBG(DBG_ACC,pa,("iblk_wr cl22 add=%x aer=%x blk=%x reg=%x adr=%x lm=%0x rtn=%0d d=%x\n", 462 addr, aer, blkaddr, regaddr, addr, pa->lane_mask, ioerr, data)); 463 return ioerr; 464 } 465 466 467 int 468 phymod_tsc_iblk_write(const phymod_access_t *pa, uint32_t addr, uint32_t data) 469 { 470 int ioerr = 0; 471 uint32_t lane_map, hwlane, is_hwsupport = 1; 472 473 hwlane = 0; 474 if (addr & PHYMOD_REG_ACC_AER_IBLK_FORCE_LANE) { 475 /* Forcing lane overrides default behavior */ 476 hwlane = (addr >> PHYMOD_REG_ACCESS_FLAGS_SHIFT) & 0x7; 477 return _tsc_iblk_write_lane(pa, hwlane, addr, data); 478 } 479 lane_map = PHYMOD_ACC_LANE_MASK(pa) & 0xffff; 480 switch (lane_map) { 481 case 0x0: 482 hwlane = 0x0; 483 break; 484 case 0x1: 485 hwlane = 0x0; 486 break; 487 case 0x2: 488 hwlane = 0x1; 489 break; 490 case 0x4: 491 hwlane = 0x2; 492 break; 493 case 0x8: 494 hwlane = 0x3; 495 break; 496 case 0xf: 497 hwlane = PHYMOD_TSC_IBLK_BCAST; 498 break; 499 case 0x3: 500 hwlane = PHYMOD_TSC_IBLK_MCAST01; 501 break; 502 case 0xc: 503 hwlane = PHYMOD_TSC_IBLK_MCAST23; 504 break; 505 default: 506 is_hwsupport = 0; 507 hwlane = 0x0; 508 } 509 /* 510 * If the lane mask is supported by HW, i.e. a single lane or a 511 * supported multicast combination (0x3, 0xC or 0xF), then program 512 * directly by HW. 513 */ 514 if (is_hwsupport) { 515 return _tsc_iblk_write_lane(pa, hwlane, addr, data); 516 } 517 /* 518 * If the lane mask is not supported directly by HW, e.g. 0x9, then 519 * program one lane at a time. 520 */ 521 for (hwlane = 0; hwlane < LANE_MAX; hwlane++) { 522 if (LANE_IS_MEMBER(lane_map, hwlane)) { 523 ioerr = _tsc_iblk_write_lane(pa, hwlane, addr, data); 524 if (ioerr != 0) { 525 return ioerr; 526 } 527 } 528 } 529 return ioerr; 530 } 531 532 int 533 phymod_tscbh_iblk_write(const phymod_access_t *pa, uint32_t addr, uint32_t data) 534 { 535 int ioerr = 0; 536 uint32_t devad = (addr >> 16) & 0xf; 537 uint32_t lane_map, hwlane = 0, is_hwsupport = 1; 538 uint32_t mpp_sel = 0, lane = 0; 539 540 if (addr & PHYMOD_REG_ACC_AER_IBLK_FORCE_LANE) { 541 /* Forcing lane overrides default behavior */ 542 hwlane = (addr >> PHYMOD_REG_ACCESS_FLAGS_SHIFT) & 0x7; 543 return _tscbh_iblk_write_lane(pa, hwlane, addr, data); 544 } 545 lane_map = PHYMOD_ACC_LANE_MASK(pa) & 0xffff; 546 547 /* need to use different lane for pcs or PMD device */ 548 if (devad) { 549 /* for PMD device */ 550 switch (lane_map) { 551 case 0x0: 552 hwlane = 0x0; 553 break; 554 case 0x1: 555 hwlane = 0x0; 556 break; 557 case 0x2: 558 hwlane = 0x1; 559 break; 560 case 0x4: 561 hwlane = 0x2; 562 break; 563 case 0x8: 564 hwlane = 0x3; 565 break; 566 case 0x10: 567 hwlane = 0x4; 568 break; 569 case 0x20: 570 hwlane = 0x5; 571 break; 572 case 0x40: 573 hwlane = 0x6; 574 break; 575 case 0x80: 576 hwlane = 0x7; 577 break; 578 case 0xf: 579 hwlane = PHYMOD_BH_IBLK_MCAST0123; 580 break; 581 case 0xf0: 582 hwlane = PHYMOD_BH_IBLK_MCAST4567; 583 break; 584 case 0x3: 585 hwlane = PHYMOD_BH_IBLK_MCAST01; 586 break; 587 case 0xc: 588 hwlane = PHYMOD_BH_IBLK_MCAST23; 589 break; 590 case 0x30: 591 hwlane = PHYMOD_BH_IBLK_MCAST45; 592 break; 593 case 0xc0: 594 hwlane = PHYMOD_BH_IBLK_MCAST67; 595 break; 596 case 0xff: 597 hwlane = PHYMOD_BH_IBLK_BCAST; 598 break; 599 default: 600 is_hwsupport = 0; 601 hwlane = 0x0; 602 } 603 } else { 604 /* pcs device */ 605 switch (lane_map) { 606 case 0x0: 607 hwlane = 0x100; 608 break; 609 case 0x1: 610 hwlane = 0x100; 611 break; 612 case 0x2: 613 hwlane = 0x101; 614 break; 615 case 0x4: 616 hwlane = 0x102; 617 break; 618 case 0x8: 619 hwlane = 0x103; 620 break; 621 case 0x10: 622 hwlane = 0x200; 623 break; 624 case 0x20: 625 hwlane = 0x201; 626 break; 627 case 0x40: 628 hwlane = 0x202; 629 break; 630 case 0x80: 631 hwlane = 0x203; 632 break; 633 case 0xf: 634 hwlane = PHYMOD_TSCBH_IBLK_MCAST0123; 635 break; 636 case 0xff: 637 hwlane = PHYMOD_TSCBH_IBLK_BCAST; 638 break; 639 case 0x3: 640 hwlane = PHYMOD_TSCBH_IBLK_MCAST01; 641 break; 642 case 0xc: 643 hwlane = PHYMOD_TSCBH_IBLK_MCAST23; 644 break; 645 case 0x30: 646 hwlane = PHYMOD_TSCBH_IBLK_MCAST45; 647 break; 648 case 0xc0: 649 hwlane = PHYMOD_TSCBH_IBLK_MCAST67; 650 break; 651 default: 652 is_hwsupport = 0; 653 hwlane = 0x0; 654 } 655 } 656 /* 657 * If the lane mask is supported by HW, i.e. a single lane or a 658 * supported multicast combination (0x3, 0xC or 0xF), then program 659 * directly by HW. 660 */ 661 if (is_hwsupport) { 662 return _tscbh_iblk_write_lane(pa, hwlane, addr, data); 663 } 664 /* 665 * If the lane mask is not supported directly by HW, e.g. 0x9, then 666 * program one lane at a time. 667 */ 668 for (lane = 0; lane < LANE_MAX; lane++) { 669 if (LANE_IS_MEMBER(lane_map, lane)) { 670 /* if pcs device, then nbeed to form aer lane */ 671 if (!devad) { 672 if (TSCBH_LANE_IS_MPP1(lane)) { 673 mpp_sel = PHYMOD_TSCBH_IBLK_MPP_1; 674 } else { 675 mpp_sel = PHYMOD_TSCBH_IBLK_MPP_0; 676 } 677 hwlane = (lane % 4) | (mpp_sel << PHYMOD_TSCBH_IBLK_MPP_SHIFT); 678 } 679 ioerr = _tscbh_iblk_write_lane(pa, hwlane, addr, data); 680 if (ioerr != 0) { 681 return ioerr; 682 } 683 } 684 } 685 return ioerr; 686 }