openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

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 }