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

serdes100fx.c (45414B)


      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  * File:
      8  *      physerdes100fx.c
      9  * Purpose:
     10  *      Fiber driver for serdes100fx using internal Serdes PHY.
     11  *
     12  * For BCM56514, the internal GMACC core of the GPORT should always be
     13  * configured to run in SGMII mode.  TBI mode is not supported between
     14  * the GMACC and the internal SERDES module.
     15  * 
     16  * When operating with an external PHY, this driver is not used.
     17  * However the speed/duplex of the internal PHY must be programmed to
     18  * match the MAC and external PHY settings so the data can pass through.
     19  * This file supplies some routines to allow mac.c to accomplish this
     20  * (think of the internal PHY as part of the MAC in this case):
     21  *
     22  *      phy_serdes100fx_notify_duplex
     23  *      phy_serdes100fx_notify_speed
     24  *      phy_serdes100fx_notify_stop
     25  *      phy_serdes100fx_notify_resume
     26  *
     27  * CMIC MIIM operations can be performed to the internal register set
     28  * using internal MDIO address (PORT_TO_PHY_ADDR_INT), and an external
     29  * PHY register set (such as BCM5424/34/64) can be programmed using the
     30  * external MDIO address (PORT_TO_PHY_ADDR).
     31  *
     32  * MDIO accesses to the internal PHY are not modeled on Quickturn.
     33  */
     34 
     35 
     36 #include <sal/types.h>
     37 #include <sal/core/spl.h>
     38 #include <shared/bsl.h>
     39 #include <soc/drv.h>
     40 #include <soc/debug.h>
     41 #include <soc/error.h>
     42 #include <soc/phyreg.h>
     43 
     44 #include <soc/phy.h>
     45 #include <soc/phy/phyctrl.h>
     46 #include <soc/phy/drv.h>
     47 
     48 #include "phydefs.h"      /* Must include before other phy related includes */
     49 
     50 #if defined(INCLUDE_SERDES_100FX)
     51 #include "phyconfig.h"    /* Must be the first phy include after phydefs.h */
     52 #include "phyident.h"
     53 #include "phyreg.h"
     54 #include "phynull.h"
     55 #include "serdes.h"
     56 #include "serdes100fx.h"
     57 
     58 #define ADVERT_ALL_FIBER \
     59             (SOC_PM_PAUSE | SOC_PM_1000MB_FD)
     60 
     61 /* Notify event forward declaration */
     62 STATIC int 
     63 _phy_serdes100fx_notify_duplex(int unit, soc_port_t port, uint32 duplex);
     64 STATIC int 
     65 _phy_serdes100fx_notify_speed(int unit, soc_port_t port, uint32 speed);
     66 STATIC int 
     67 _phy_serdes100fx_notify_stop(int unit, soc_port_t port, uint32 flags);
     68 STATIC int 
     69 _phy_serdes100fx_notify_resume(int unit, soc_port_t port, uint32 flags);
     70 STATIC int
     71 _phy_serdes100fx_notify_interface(int unit, soc_port_t port, uint32 intf);
     72 STATIC int
     73 phy_serdes100fx_duplex_set(int unit, soc_port_t port, int duplex);
     74 
     75 /***********************************************************************
     76  *
     77  * HELPER FUNCTIONS
     78  */
     79 /*
     80  * Function:
     81  *      phy_serdes100fx_tx_control_set
     82  * Purpose:
     83  *      Set preemphasis, driver current, and predriver current values 
     84  * Parameters:
     85  *      unit - StrataSwitch unit #.
     86  *      pc   - PHY state object.
     87  *      port - StrataSwitch port #.
     88  * Returns:
     89  *      SOC_E_NONE
     90  */
     91 
     92 STATIC int
     93 _phy_serdes100fx_tx_control_set(int unit, phy_ctrl_t *pc,
     94                           soc_port_t port)
     95 {
     96     uint32 preemph;
     97     uint32 idriver;
     98     uint32 pdriver;
     99     uint16 data16;
    100     int    fiber;
    101 
    102     fiber = PHY_FIBER_MODE(unit, port);
    103 
    104     /* Default values for Dozen SerDes */
    105     if (fiber) {
    106         idriver = 0xf;
    107         pdriver = 0x7;
    108         preemph = 0x0;
    109     } else {
    110         idriver = 0xc;
    111         pdriver = 0x7;
    112         preemph = 0x0;
    113     }
    114 
    115     /* Read config override */
    116     preemph = soc_property_port_get(unit, port, 
    117                                     spn_SERDES_PREEMPHASIS, preemph);
    118     idriver = soc_property_port_get(unit, port,
    119                                     spn_SERDES_DRIVER_CURRENT, idriver);
    120     pdriver = soc_property_port_get(unit, port,
    121                                     spn_SERDES_PRE_DRIVER_CURRENT, pdriver);
    122 
    123     /* Dozen SerDes 
    124      * preemph[8:6], idriver[15:12], pdriver[11:9]
    125      */
    126     preemph = (preemph << 6) & 0x01c0;
    127     idriver = (idriver << 12) & 0xf000;
    128     pdriver = (pdriver << 9) & 0x0e00;
    129     data16  = (uint16)(preemph | idriver | pdriver);
    130 
    131     /* If amplitude override is not enabled, the amplitude value is 
    132      * automatically adjusted when interface changes between SGMII and 
    133      * fiber(1000X).
    134      */ 
    135     SOC_IF_ERROR_RETURN
    136         (MODIFY_SERDES100FX_1000X_CTRL1r(unit, pc,
    137               FX_1000X_TX_AMPLITUDE_OVRD, FX_1000X_TX_AMPLITUDE_OVRD)); 
    138 
    139     /* Set ANALOG_TX with recommended amplitude */ 
    140     SOC_IF_ERROR_RETURN
    141         (MODIFY_SERDES100FX_ANALOG_TXr(unit, pc, data16, 0xffc0));
    142     return SOC_E_NONE;
    143 }
    144 
    145 /***********************************************************************
    146  *
    147  * FIBER DRIVER ROUTINES
    148  */
    149 STATIC int
    150 _phy_serdes100fx_init_no_reset(int unit, soc_port_t port)
    151 {
    152     int                fiber;
    153     uint16             data16, mask16;
    154     phy_ctrl_t        *pc;
    155 
    156     pc                       = INT_PHY_SW_STATE(unit, port);
    157     fiber                    = PHY_FIBER_MODE(unit, port);
    158     pc->fiber.enable         = fiber;
    159     pc->fiber.preferred      = fiber;
    160     pc->fiber.autoneg_enable = 1;
    161     pc->fiber.autoneg_advert = ADVERT_ALL_FIBER;
    162     pc->fiber.force_speed    = 1000;
    163     pc->fiber.force_duplex   = TRUE;
    164     pc->fiber.master         = SOC_PORT_MS_NONE;
    165     pc->fiber.mdix           = SOC_PORT_MDIX_NORMAL;
    166 
    167     /* Since physerdes100fx driver reset function vector does not reset
    168      * the SerDes, reset the SerDes in initialization first. 
    169      * The internal SERDES PHY's reset bit is self-clearing.
    170      */
    171     SOC_IF_ERROR_RETURN
    172         (MODIFY_SERDES100FX_MII_CTRLr(unit, pc,
    173                                FX_MII_CTRL_RESET, FX_MII_CTRL_RESET));
    174 
    175     sal_usleep(10000);
    176 
    177     data16 = FX_MII_CTRL_AN_ENABLE | FX_MII_CTRL_AN_RESTART;
    178     mask16 = FX_MII_CTRL_AN_ENABLE | FX_MII_CTRL_AN_RESTART;
    179     if (PHY_COPPER_MODE(unit, port) && !PHY_SGMII_AUTONEG_MODE(unit, port)) {
    180         data16 = 0;
    181     }
    182     SOC_IF_ERROR_RETURN
    183         (MODIFY_SERDES100FX_MII_CTRLr(unit, pc, data16, mask16));
    184 
    185     /* Configure default preemphasis, predriver, idriver values */
    186     SOC_IF_ERROR_RETURN
    187         (_phy_serdes100fx_tx_control_set(unit, pc, port));
    188 
    189     SOC_IF_ERROR_RETURN
    190         (MODIFY_SERDES100FX_1000X_CTRL2r(unit, pc, 
    191                        (FX_1000X_FALSE_LNK_DIS | FX_1000X_FLT_FORCE_EN),
    192                        (FX_1000X_FALSE_LNK_DIS | FX_1000X_FLT_FORCE_EN)));
    193 
    194     data16 = 0;
    195     mask16 = FX_1000X_AUTO_DETECT | FX_1000X_FIBER_MODE;
    196     /*
    197      * Configure signal auto-detection between SGMII and fiber.
    198      * This detection only works if auto-negotiation is enabled.
    199      */
    200     if (soc_property_port_get(unit, port, 
    201                               spn_SERDES_AUTOMEDIUM, FALSE)) {
    202         data16 |= FX_1000X_AUTO_DETECT;
    203     }
    204     /*
    205      * Put the Serdes in Fiber or SGMII mode
    206      */
    207     if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) ||
    208         PHY_PASSTHRU_MODE(unit, port)) {
    209         if (soc_property_port_get(unit, port,
    210                                   spn_SERDES_FIBER_PREF, TRUE)) {
    211             data16 |= FX_1000X_FIBER_MODE;
    212         }
    213     }
    214     if (soc_property_port_get(unit, port, 
    215                               spn_SERDES_SGMII_MASTER, FALSE)) {
    216         data16 |= FX_1000X_SGMII_MASTER;
    217         mask16 |= FX_1000X_SGMII_MASTER;
    218     }
    219     SOC_IF_ERROR_RETURN
    220         (MODIFY_SERDES100FX_1000X_CTRL1r(unit, pc, data16, mask16));   
    221 
    222     data16 = FX_1000X_CLRAR_BER_CNTR | FX_1000X_FLT_FORCE_EN |
    223              FX_1000X_FALSE_LNK_DIS;
    224     mask16 = FX_1000X_CLRAR_BER_CNTR | FX_1000X_FLT_FORCE_EN |
    225              FX_1000X_FALSE_LNK_DIS | FX_1000X_PAR_DET_EN;
    226 
    227     SOC_IF_ERROR_RETURN
    228         (MODIFY_SERDES100FX_1000X_CTRL2r(unit, pc, data16, mask16));
    229 
    230     if (PHY_COPPER_MODE(unit, port)) {
    231         /* Do not wait 10ms for sync status before a valid link is 
    232          * established when auto-negotiation is disabled.
    233          */
    234         SOC_IF_ERROR_RETURN
    235             (MODIFY_SERDES100FX_1000X_CTRL2r(unit, pc, 0x0000, 0x0004));
    236     }
    237    
    238     /* Enable fast timer */
    239     SOC_IF_ERROR_RETURN
    240         (MODIFY_SERDES100FX_FX100_CTRL3r(unit, pc, 
    241                                          FX_FX100_CONTROL3_FAST_TIMER, 
    242                                          FX_FX100_CONTROL3_FAST_TIMER));
    243 
    244     /* Allow extended packet length ( > 9 kilo bytes) */
    245     /* NB: The link takes longer to link up if extended packet length
    246      *     is enabled.
    247      */  
    248     SOC_IF_ERROR_RETURN
    249         (WRITE_SERDES100FX_FX100_CTRL2r(unit, pc, 
    250                                         FX_FX100_CONTROL2_EXT_PKT_SZ));
    251 
    252     LOG_INFO(BSL_LS_SOC_PHY,
    253              (BSL_META_U(unit,
    254                          "phy_serdes100fx_init: u=%d p=%d %s\n"),
    255               unit, port, (fiber) ? "Fiber" : "Copper"));
    256 
    257     return SOC_E_NONE;
    258 }
    259 
    260 
    261 /*
    262  * Function:
    263  *      phy_serdes100fx_init
    264  * Purpose:     
    265  *      Initialize serdes100fx internal PHY driver
    266  * Parameters:
    267  *      unit - StrataSwitch unit #.
    268  *      port - StrataSwitch port #. 
    269  * Returns:     
    270  *      SOC_E_NONE
    271  * Notes:
    272  *      This function implements the bulk part of phy_serdes100fx_init and
    273  *      provides a method for initializing the SerDes when it is used
    274  *      as an intermediate PHY between the MAC and an external PHY. 
    275  *      In the latter case the PHY driver reset callback should be
    276  *      called only once, and this has already been done by the 
    277  *      external PHY driver init function.
    278  */
    279 
    280 STATIC int
    281 phy_serdes100fx_init(int unit, soc_port_t port)
    282 {
    283 
    284     LOG_INFO(BSL_LS_SOC_PHY,
    285              (BSL_META_U(unit,
    286                          "phy_serdes100fx_init: u=%d p=%d\n"),
    287               unit, port));
    288 
    289     if (!PHY_EXTERNAL_MODE(unit, port)) {
    290          /* PHY reset callbacks */
    291         SOC_IF_ERROR_RETURN(soc_phy_reset(unit, port));
    292 
    293         if (soc_property_port_get(unit, port,
    294                                   spn_SERDES_FIBER_PREF, 1)) {
    295             PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
    296         } else {
    297             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER);
    298         }
    299 
    300     }
    301     SOC_IF_ERROR_RETURN
    302         (_phy_serdes100fx_init_no_reset(unit, port));
    303 
    304     return SOC_E_NONE;
    305 }
    306 
    307 /*
    308  * Function:    
    309  *      phy_serdes100fx_enable_set
    310  * Purpose:     
    311  *      Enable or disable the physical interface.
    312  * Parameters:
    313  *      unit - StrataSwitch unit #.
    314  *      port - StrataSwitch port #. 
    315  *      enable - Boolean, true = enable PHY, false = disable.
    316  * Returns:     
    317  *      SOC_E_XXX
    318  * Notes:
    319  *      The PHY is held in reset when disabled.  Also, the
    320  *      MISC_CONTROL.IDDQ bit must be set to 1 so the remote partner
    321  *      sees link down.
    322  */
    323 
    324 STATIC int
    325 phy_serdes100fx_enable_set(int unit, soc_port_t port, int enable)
    326 {
    327     LOG_INFO(BSL_LS_SOC_PHY,
    328              (BSL_META_U(unit,
    329                          "phy_serdes100fx_enable_set: u=%d p=%d en=%d\n"),
    330               unit, port, enable));
    331     if (enable) {
    332         PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE);
    333 
    334         SOC_IF_ERROR_RETURN
    335             (_phy_serdes100fx_notify_resume(unit, port, PHY_STOP_PHY_DIS));
    336     } else {
    337         PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE);
    338 
    339         SOC_IF_ERROR_RETURN
    340             (_phy_serdes100fx_notify_stop(unit, port, PHY_STOP_PHY_DIS));
    341     }
    342 
    343     return SOC_E_NONE;
    344 }
    345 
    346 
    347 STATIC int
    348 _phy_serdes100fx_sgmii_speed_set(int unit, soc_port_t port, int speed)
    349 {
    350     uint16 mii_ctrl;
    351     phy_ctrl_t  *pc;
    352 
    353     pc = INT_PHY_SW_STATE(unit, port);
    354 
    355     switch (speed) {
    356         case 10:
    357             mii_ctrl = FX_MII_CTRL_SS_10;
    358             break;
    359         case 100:
    360             mii_ctrl = FX_MII_CTRL_SS_100;
    361             break;
    362         case 0:
    363         case 1000:  
    364             mii_ctrl = FX_MII_CTRL_SS_1000;
    365             break;
    366         default:
    367             return SOC_E_CONFIG;
    368     } 
    369     SOC_IF_ERROR_RETURN
    370         (MODIFY_SERDES100FX_MII_CTRLr(unit, pc, mii_ctrl,
    371                                (FX_MII_CTRL_SS_LSB | FX_MII_CTRL_SS_MSB)));
    372     return SOC_E_NONE;
    373 }
    374 
    375 STATIC int
    376 _phy_serdes100fx_1000x_speed_set(int unit, soc_port_t port, int speed)
    377 {
    378     phy_ctrl_t  *pc;
    379     uint16       data, mask;
    380 
    381     pc = INT_PHY_SW_STATE(unit, port);
    382 
    383 
    384     if ((speed != 0) && (speed != 100) && (speed != 1000)) { 
    385         return SOC_E_CONFIG;
    386     }
    387 
    388     /* Stop MAC clock  */
    389     data = mask = 0x0060;
    390     SOC_IF_ERROR_RETURN
    391         (MODIFY_SERDES100FX_FX100_CTRL2r(unit, pc, data, mask));
    392 
    393     mask = FX_FX100_CONTROL1_EN; 
    394     data = (speed == 100) ? FX_FX100_CONTROL1_EN : 0;
    395     SOC_IF_ERROR_RETURN
    396         (MODIFY_SERDES100FX_FX100_CTRL1r(unit, pc, data, mask));
    397 
    398     /* Reset analog clock */
    399     SOC_IF_ERROR_RETURN
    400         (WRITE_SERDES100FX_ANALOG_RX1r(unit, pc,
    401                     FX_ANALOG_RX1_RESET));
    402 
    403     SOC_IF_ERROR_RETURN
    404          (WRITE_SERDES100FX_ANALOG_RX1r(unit, pc,
    405                     FX_ANALOG_RX1_SIG_DET | FX_ANALOG_RX1_EDGE_SEL));
    406     sal_usleep(500);
    407 
    408     /* Allow extended packet length ( > 9 kilo bytes) */
    409     /* NB: The link takes longer to link up if extended packet length
    410      *     is enabled. Need to set this everytime speed changes.
    411      */  
    412     SOC_IF_ERROR_RETURN
    413         (WRITE_SERDES100FX_FX100_CTRL2r(unit, pc, 
    414                                         FX_FX100_CONTROL2_EXT_PKT_SZ));
    415     return SOC_E_NONE;
    416 }
    417 
    418 /*
    419  * Function:    
    420  *      phy_serdes100fx_speed_set
    421  * Purpose:     
    422  *      Set the current operating speed (forced).
    423  * Parameters:
    424  *      unit - StrataSwitch unit #.
    425  *      port - StrataSwitch port #. 
    426  *      duplex - (OUT) Boolean, true indicates full duplex, false 
    427  *              indicates half.
    428  * Returns:     
    429  *      SOC_E_XXX
    430  */
    431 
    432 STATIC int
    433 phy_serdes100fx_speed_set(int unit, soc_port_t port, int speed)
    434 {
    435     uint16              fiber_status;
    436     phy_ctrl_t  *pc;
    437     int                 rv;
    438 
    439     pc = INT_PHY_SW_STATE(unit, port);
    440     rv = SOC_E_NONE;
    441 
    442     SOC_IF_ERROR_RETURN
    443         (READ_SERDES100FX_1000X_STAT1r(unit, pc, &fiber_status));
    444 
    445     if (fiber_status & FX_1000X_STATUS1_SGMII_MODE) {
    446         rv = _phy_serdes100fx_sgmii_speed_set(unit, port, speed);
    447     } else {
    448         rv = _phy_serdes100fx_1000x_speed_set(unit, port, speed);
    449     }
    450 
    451     if (SOC_SUCCESS(rv)) {
    452         pc->fiber.force_speed = speed;
    453     }
    454 
    455     LOG_INFO(BSL_LS_SOC_PHY,
    456              (BSL_META_U(unit,
    457                          "phy_serdes100fx_speed_set: u=%d p=%d speed=%d rv=%d\n"),
    458               unit, port, speed, rv));
    459     
    460     return rv;
    461 }
    462 
    463 STATIC int 
    464 _phy_serdes100fx_sgmii_speed_get(int unit, soc_port_t port, int *speed)
    465 {
    466     uint16 mii_ctrl, mii_stat, mii_anp;
    467     phy_ctrl_t  *pc;
    468 
    469     pc = INT_PHY_SW_STATE(unit, port);
    470 
    471     SOC_IF_ERROR_RETURN
    472         (READ_SERDES100FX_MII_CTRLr(unit, pc, &mii_ctrl));
    473     SOC_IF_ERROR_RETURN
    474         (READ_SERDES100FX_MII_STATr(unit, pc, &mii_stat));
    475  
    476     if (mii_ctrl & FX_MII_CTRL_AN_ENABLE) { /*Auto-negotiation enabled */  
    477         if (!(mii_stat & MII_STAT_AN_DONE)) { /* Auto-neg NOT complete */
    478             *speed = 0;
    479             return SOC_E_NONE;
    480         } else { /* Read Advertise link partner */
    481             SOC_IF_ERROR_RETURN
    482                 (READ_SERDES100FX_MII_ANPr(unit, pc, &mii_anp));
    483             if (mii_anp & FX_MII_ANP_SGMII) {
    484                 switch(mii_anp & FX_MII_ANP_SGMII_SPEED) {
    485                 case FX_MII_ANP_SGMII_SPEED_1000:
    486                     *speed = 1000;
    487                     break;
    488                 case FX_MII_ANP_SGMII_SPEED_100:
    489                     *speed = 100;
    490                     break;
    491                 case FX_MII_ANP_SGMII_SPEED_10:
    492                     *speed = 10;
    493                     break;
    494                 default:
    495                     return SOC_E_UNAVAIL;
    496                 }
    497             }
    498         }
    499     } else {  /* Forced speed */
    500         switch(mii_ctrl & (FX_MII_CTRL_SS_LSB | FX_MII_CTRL_SS_MSB)) {
    501         case FX_MII_CTRL_SS_10:
    502             *speed = 10; 
    503             break;
    504         case FX_MII_CTRL_SS_100:
    505             *speed = 100;
    506             break;
    507         case FX_MII_CTRL_SS_1000:
    508             *speed = 1000;
    509             break;
    510         default:
    511             return SOC_E_UNAVAIL;
    512         } 
    513     }
    514     return SOC_E_NONE;
    515 }
    516 
    517 STATIC int
    518 _phy_serdes100fx_1000x_speed_get(int unit, soc_port_t port, int *speed)
    519 {
    520     phy_ctrl_t  *pc;
    521     uint16       fx100_control;
    522 
    523     pc = INT_PHY_SW_STATE(unit, port);
    524 
    525     *speed = 1000;
    526 
    527     SOC_IF_ERROR_RETURN 
    528         (READ_SERDES100FX_FX100_CTRL1r(unit, pc, &fx100_control));
    529     *speed = (fx100_control & FX_FX100_CONTROL1_EN) ? 100 : 1000; 
    530 
    531     return SOC_E_NONE;
    532 }
    533 
    534 /*
    535  * Function:    
    536  *      phy_serdes100fx_speed_get
    537  * Purpose:     
    538  *      Get the current operating speed. If autoneg is enabled, 
    539  *      then operating mode is returned, otherwise forced mode is returned.
    540  * Parameters:
    541  *      unit - StrataSwitch unit #.
    542  *      port - StrataSwitch port #. 
    543  *      duplex - (OUT) Boolean, true indicates full duplex, false 
    544  *              indicates half.
    545  * Returns:     
    546  *      SOC_E_XXX
    547  */
    548 
    549 STATIC int
    550 phy_serdes100fx_speed_get(int unit, soc_port_t port, int *speed)
    551 {
    552     uint16              fiber_status;
    553     phy_ctrl_t  *pc;
    554 
    555     pc = INT_PHY_SW_STATE(unit, port);
    556 
    557     *speed = 1000;
    558 
    559     SOC_IF_ERROR_RETURN
    560         (READ_SERDES100FX_1000X_STAT1r(unit, pc, &fiber_status));
    561 
    562     if (fiber_status & FX_1000X_STATUS1_SGMII_MODE) {
    563         SOC_IF_ERROR_RETURN
    564             (_phy_serdes100fx_sgmii_speed_get(unit, port, speed));
    565     } else {
    566         SOC_IF_ERROR_RETURN
    567             (_phy_serdes100fx_1000x_speed_get(unit, port, speed));
    568     }
    569 
    570     return SOC_E_NONE;
    571 }
    572 
    573 /*
    574  * Function:    
    575  *      phy_serdes100fx_an_set
    576  * Purpose:     
    577  *      Enable or disabled auto-negotiation on the specified port.
    578  * Parameters:
    579  *      unit - StrataSwitch unit #.
    580  *      port - StrataSwitch port #. 
    581  *      an - Boolean, if true, auto-negotiation is enabled 
    582  *              (and/or restarted). If false, autonegotiation is disabled.
    583  * Returns:     
    584  *      SOC_E_XXX
    585  */
    586 
    587 STATIC int
    588 phy_serdes100fx_an_set(int unit, soc_port_t port, int an)
    589 {
    590     phy_ctrl_t  *pc;
    591     uint16             par_det;
    592     uint16             an_enable;
    593     uint16             auto_det;
    594 
    595     pc = INT_PHY_SW_STATE(unit, port);
    596 
    597     LOG_INFO(BSL_LS_SOC_PHY,
    598              (BSL_META_U(unit,
    599                          "phy_serdes100fx_an_set: u=%d p=%d an=%d\n"),
    600               unit, port, an));
    601     /*
    602      * Special case for BCM5421S being used in SGMII copper mode with
    603      * BCMserdes100fx.  Always have autoneg on.
    604      */
    605     if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_5421S)) {
    606         an = 1;
    607     }
    608 
    609     par_det   = 0;
    610     an_enable = 0; 
    611     auto_det  = 0;
    612     if (an) {
    613         an_enable = FX_MII_CTRL_AN_ENABLE | FX_MII_CTRL_AN_RESTART;
    614         if (soc_property_port_get(unit, port,
    615                                   spn_SERDES_AUTOMEDIUM, FALSE)) {
    616             auto_det = FX_1000X_AUTO_DETECT; 
    617         }
    618     } else {
    619         /* Enable parallel detect if autoneg is disabled so that
    620          * parallel detect can assist in determining port speed */
    621         par_det = FX_1000X_PAR_DET_EN;
    622     }
    623 
    624     SOC_IF_ERROR_RETURN
    625         (MODIFY_SERDES100FX_1000X_CTRL2r(unit, pc,
    626                         par_det, FX_1000X_PAR_DET_EN));
    627 
    628     SOC_IF_ERROR_RETURN
    629         (MODIFY_SERDES100FX_1000X_CTRL1r(unit, pc, 
    630                        auto_det, FX_1000X_AUTO_DETECT));
    631 
    632     /* Enable/Disable and Restart autonegotiation (self-clearing bit) */
    633     SOC_IF_ERROR_RETURN
    634         (MODIFY_SERDES100FX_MII_CTRLr(unit, pc, an_enable,
    635                        FX_MII_CTRL_AN_ENABLE | FX_MII_CTRL_AN_RESTART));
    636 
    637     pc->fiber.autoneg_enable = an;
    638 
    639     return SOC_E_NONE;
    640 }
    641 
    642 /*
    643  * Function:    
    644  *      phy_serdes100fx_lb_set
    645  * Purpose:     
    646  *      Set the internal PHY loopback mode.
    647  * Parameters:
    648  *      unit - StrataSwitch unit #.
    649  *      port - StrataSwitch port #. 
    650  *      loopback - Boolean: true = enable loopback, false = disable.
    651  * Returns:     
    652  *      SOC_E_XXX
    653  */
    654 
    655 STATIC int
    656 phy_serdes100fx_lb_set(int unit, soc_port_t port, int enable)
    657 {
    658     uint16              ctrl;
    659     phy_ctrl_t  *pc;
    660     int                 rv;
    661 
    662     pc = INT_PHY_SW_STATE(unit, port);
    663 
    664     ctrl = (enable) ?FX_MII_CTRL_LOOPBACK : 0;
    665 
    666     rv = MODIFY_SERDES100FX_MII_CTRLr(unit, pc,
    667                                ctrl, FX_MII_CTRL_LOOPBACK);
    668 
    669     LOG_INFO(BSL_LS_SOC_PHY,
    670              (BSL_META_U(unit,
    671                          "phy_serdes100fx_lb_set: u=%d p=%d lb=%d rv=%d\n"),
    672               unit, port, enable, rv));
    673  
    674     return rv;
    675 }
    676 
    677 /*
    678  * Function:    
    679  *      phy_serdes100fx_lb_get
    680  * Purpose:     
    681  *      Get the local PHY loopback mode.
    682  * Parameters:
    683  *      unit - StrataSwitch unit #.
    684  *      port - StrataSwitch port #. 
    685  *      loopback - (OUT) Boolean: true = enable loopback, false = disable.
    686  * Returns:     
    687  *      SOC_E_XXX
    688  */
    689 
    690 STATIC int
    691 phy_serdes100fx_lb_get(int unit, soc_port_t port, int *enable)
    692 {
    693     uint16      ctrl;
    694     phy_ctrl_t  *pc;
    695 
    696     pc = INT_PHY_SW_STATE(unit, port);
    697     SOC_IF_ERROR_RETURN
    698         (READ_SERDES100FX_MII_CTRLr(unit, pc, &ctrl));
    699 
    700     *enable = (ctrl & FX_MII_CTRL_LOOPBACK) != 0;
    701 
    702     return SOC_E_NONE;
    703 }
    704 
    705 /*
    706  * Function:
    707  *      phy_serdes100fx_interface_set
    708  * Purpose:
    709  *      Set the current operating mode of the internal PHY.
    710  *      (Pertaining to the MAC/PHY interface, not the line interface).
    711  * Parameters:
    712  *      unit - StrataSwitch unit #.
    713  *      port - StrataSwitch port #. 
    714  *      pif - one of SOC_PORT_IF_*
    715  * Returns:
    716  *      SOC_E_XXX
    717  */
    718 
    719 STATIC int
    720 phy_serdes100fx_interface_set(int unit, soc_port_t port, soc_port_if_t pif)
    721 {
    722     int                 rv;
    723 
    724     switch (pif) {
    725     case SOC_PORT_IF_MII:
    726     case SOC_PORT_IF_GMII:
    727     case SOC_PORT_IF_SGMII:
    728     case SOC_PORT_IF_NOCXN:
    729         rv = SOC_E_NONE;
    730         break;
    731     default:
    732         rv = SOC_E_UNAVAIL;
    733         break;
    734     }
    735 
    736     LOG_INFO(BSL_LS_SOC_PHY,
    737              (BSL_META_U(unit,
    738                          "phy_serdes100fx_interface_set: u=%d p=%d pif=%d rv=%d\n"),
    739               unit, port, pif, rv));
    740 
    741     return rv;
    742 }
    743 
    744 /*
    745  * Function:
    746  *      phy_serdes100fx_interface_get
    747  * Purpose:
    748  *      Get the current operating mode of the internal PHY.
    749  *      (Pertaining to the MAC/PHY interface, not the line interface).
    750  * Parameters:
    751  *      unit - StrataSwitch unit #.
    752  *      port - StrataSwitch port #. 
    753  *      pif - (OUT) one of SOC_PORT_IF_*
    754  * Returns:
    755  *      SOC_E_XXX
    756  */
    757 
    758 STATIC int
    759 phy_serdes100fx_interface_get(int unit, soc_port_t port, soc_port_if_t *pif)
    760 {
    761     *pif = SOC_PORT_IF_GMII;
    762 
    763     return(SOC_E_NONE);
    764 }
    765 
    766 /*
    767  * Function:    
    768  *      phy_serdes100fx_ability_get
    769  * Purpose:     
    770  *      Get the abilities of the internal PHY.
    771  * Parameters:
    772  *      unit - StrataSwitch unit #.
    773  *      port - StrataSwitch port #. 
    774  *      mode - (OUT) Port mode mask indicating supported options/speeds.
    775  * Returns:     
    776  *      SOC_E_XXX
    777  */
    778 
    779 STATIC int
    780 phy_serdes100fx_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode)
    781 {
    782     if (PHY_FIBER_MODE(unit, port)) {
    783         *mode = SOC_PM_1000MB_FD | SOC_PM_PAUSE | SOC_PM_AN | 
    784                 SOC_PM_PAUSE_ASYMM |
    785                 SOC_PM_LB_PHY | SOC_PM_GMII | SOC_PM_100MB_FD;
    786     } else { 
    787         *mode = SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB_FD |
    788                 SOC_PM_PAUSE_ASYMM |
    789                 SOC_PM_PAUSE | SOC_PM_AN | SOC_PM_LB_PHY | SOC_PM_GMII;
    790     }
    791     return SOC_E_NONE;
    792 }
    793 
    794 /*
    795  * Function:
    796  *      _phy_serdes100fx_medium_config_update
    797  * Purpose:
    798  *      Update the PHY with config parameters
    799  * Parameters:
    800  *      unit - StrataSwitch unit #.
    801  *      port - StrataSwitch port #.
    802  *      cfg - Config structure.
    803  * Returns:
    804  *      SOC_E_XXX
    805  */
    806 
    807 STATIC int
    808 _phy_serdes100fx_medium_config_update(int unit, soc_port_t port,
    809                                 soc_phy_config_t *cfg)
    810 {
    811     SOC_IF_ERROR_RETURN
    812         (phy_serdes100fx_speed_set(unit, port, cfg->force_speed));
    813     SOC_IF_ERROR_RETURN
    814         (phy_serdes100fx_duplex_set(unit, port, cfg->force_duplex));
    815     SOC_IF_ERROR_RETURN
    816         (phy_serdes_adv_local_set(unit, port, cfg->autoneg_advert));
    817     SOC_IF_ERROR_RETURN
    818         (phy_serdes100fx_an_set(unit, port, cfg->autoneg_enable));
    819 
    820     return SOC_E_NONE;
    821 }
    822 
    823 /*
    824  * Function:
    825  *      phy_serdes100fx_medium_config_set
    826  * Purpose:
    827  *      Set the operating parameters that are automatically selected
    828  *      when medium switches type.
    829  * Parameters:
    830  *      unit - StrataSwitch unit #
    831  *      port - Port number
    832  *      medium - SOC_PORT_MEDIUM_COPPER/FIBER
    833  *      cfg - Operating parameters
    834  * Returns:
    835  *      SOC_E_XXX
    836  */
    837 
    838 STATIC int
    839 phy_serdes100fx_medium_config_set(int unit, soc_port_t port, 
    840                            soc_port_medium_t  medium,
    841                            soc_phy_config_t  *cfg)
    842 {
    843 
    844     phy_ctrl_t    *pc;
    845 
    846     pc = INT_PHY_SW_STATE(unit, port);
    847 
    848     /*
    849      * Changes take effect immediately if the target medium is active.
    850      */
    851     switch (medium) {
    852     case SOC_PORT_MEDIUM_FIBER:
    853         /* Change the fiber modes */
    854 
    855         sal_memcpy(&pc->fiber, cfg, sizeof (pc->fiber));
    856 
    857         pc->fiber.autoneg_advert &= ADVERT_ALL_FIBER;
    858         if (PHY_FIBER_MODE(unit, port)) {
    859             SOC_IF_ERROR_RETURN
    860                 (_phy_serdes100fx_medium_config_update(unit, port, &pc->fiber));
    861         }
    862         return SOC_E_NONE;
    863     case SOC_PORT_MEDIUM_COPPER:
    864         return SOC_E_UNAVAIL;
    865     default:
    866         return SOC_E_PARAM;
    867     }
    868 }
    869 
    870 
    871 STATIC int
    872 _phy_serdes100fx_control_linkdown_transmit_set(int unit, soc_port_t port, 
    873                                                uint32 value)
    874 {
    875     uint16        data;
    876     phy_ctrl_t    *pc;
    877 
    878     pc = INT_PHY_SW_STATE(unit, port);
    879 
    880     if (value) {
    881         data = FX_1000X_FORCE_XMIT_TX | FX_1000X_FLT_FORCE_EN |
    882                FX_1000X_FALSE_LNK_DIS;
    883         SOC_IF_ERROR_RETURN
    884             (WRITE_SERDES100FX_1000X_CTRL2r(unit, pc, data));
    885     } else {
    886         data = FX_1000X_FLT_FORCE_EN | FX_1000X_FALSE_LNK_DIS |
    887                FX_1000X_PAR_DET_EN;
    888         SOC_IF_ERROR_RETURN
    889             (WRITE_SERDES100FX_1000X_CTRL2r(unit, pc, data));
    890     }
    891 
    892     return SOC_E_NONE;
    893 }
    894 
    895 
    896 STATIC int
    897 _phy_serdes100fx_control_linkdown_transmit_get(int unit, soc_port_t port, 
    898                                                uint32 *value)
    899 {
    900     uint16        data;
    901     phy_ctrl_t    *pc;
    902 
    903     pc = INT_PHY_SW_STATE(unit, port);
    904 
    905     SOC_IF_ERROR_RETURN
    906         (READ_SERDES100FX_1000X_CTRL2r(unit, pc, &data));
    907 
    908     *value = (data & FX_1000X_FORCE_XMIT_TX) ? 1 : 0;
    909 
    910     return SOC_E_NONE;
    911 }
    912 
    913 STATIC int
    914 _phy_serdes100fx_control_tx_driver_set(int unit, soc_port_t port,
    915                                 soc_phy_control_t type, uint32 value)
    916 {
    917     uint16       data;  /* Temporary holder of reg value to be written */
    918     uint16       mask;  /* Bit mask of reg value to be updated */
    919     phy_ctrl_t  *pc;    /* PHY software state */
    920 
    921     pc = INT_PHY_SW_STATE(unit, port);
    922 
    923     /* Dozen SerDes */
    924     switch(type) {
    925     case SOC_PHY_CONTROL_PREEMPHASIS:
    926          data = (uint16)(value & 0x7);
    927          data = data << 6;
    928          mask = 0x01C0;
    929          break;
    930     case SOC_PHY_CONTROL_DRIVER_CURRENT:
    931          data = (uint16)(value & 0xF);
    932          data = data << 12;
    933          mask = 0xF000;
    934          break;
    935     case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
    936          data = (uint16)(value & 0x7);
    937          data = data << 9;
    938          mask = 0x0E00;
    939          break;
    940     default:
    941          /* should never get here */
    942          return SOC_E_PARAM;
    943     }
    944     SOC_IF_ERROR_RETURN
    945         (MODIFY_SERDES100FX_ANALOG_TXr(unit, pc, data, mask));
    946 
    947     return SOC_E_NONE;
    948 }
    949 
    950 STATIC int
    951 _phy_serdes100fx_control_tx_driver_get(int unit, soc_port_t port,
    952                                 soc_phy_control_t type, uint32 *value)
    953 {
    954     uint16         data16;   /* Temporary holder of a reg value */
    955     phy_ctrl_t    *pc;       /* PHY software state */
    956 
    957     pc = INT_PHY_SW_STATE(unit, port);
    958 
    959     data16 = 0;
    960 
    961     SOC_IF_ERROR_RETURN
    962         (READ_SERDES100FX_ANALOG_TXr(unit, pc, &data16));
    963     switch(type) {
    964     case SOC_PHY_CONTROL_PREEMPHASIS:
    965          *value = (data16 & 0x01C0) >> 6;
    966          break;
    967     case SOC_PHY_CONTROL_DRIVER_CURRENT:
    968          *value = (data16 & 0xF000) >> 12;
    969          break;
    970     case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
    971          *value = (data16 & 0x0E00) >> 9;
    972          break;
    973     default:
    974          /* should never get here */
    975          return SOC_E_PARAM;
    976     }
    977 
    978     return SOC_E_NONE;
    979 }
    980 
    981 /*
    982  * Function:
    983  *      phy_physerdes100fx_control_set
    984  * Purpose:
    985  *      Configure PHY device specific control fucntion.
    986  * Parameters:
    987  *      unit  - StrataSwitch unit #.
    988  *      port  - StrataSwitch port #.
    989  *      type  - Control to update
    990  *      value - New setting for the control
    991  * Returns:
    992  *      SOC_E_NONE
    993  */
    994 STATIC int
    995 phy_serdes100fx_control_set(int unit, soc_port_t port,
    996                      soc_phy_control_t type, uint32 value)
    997 {
    998     int rv;
    999     phy_ctrl_t        *pc;
   1000 
   1001     pc                       = INT_PHY_SW_STATE(unit, port);
   1002     /* coverity[mixed_enums] */
   1003     if ((type < 0) || (type >= SOC_PHY_CONTROL_COUNT)) {
   1004         return SOC_E_PARAM;
   1005     }
   1006   
   1007     rv = SOC_E_UNAVAIL;
   1008     switch(type) {
   1009     case SOC_PHY_CONTROL_PREEMPHASIS:
   1010         /* fall through */
   1011     case SOC_PHY_CONTROL_DRIVER_CURRENT:
   1012         /* fall through */
   1013     case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
   1014         rv = _phy_serdes100fx_control_tx_driver_set(unit, port, type, value);
   1015         break;
   1016     case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT:
   1017         rv = _phy_serdes100fx_control_linkdown_transmit_set(unit, port, value);
   1018         break;
   1019 
   1020     case SOC_PHY_CONTROL_PARALLEL_DETECTION:
   1021         /* enable 1000X parallel detect */
   1022         SOC_IF_ERROR_RETURN
   1023             (MODIFY_SERDES100FX_1000X_CTRL2r(unit, pc,
   1024                 value? FX_1000X_PAR_DET_EN:0,
   1025                FX_1000X_PAR_DET_EN));
   1026         rv = SOC_E_NONE;
   1027         break;
   1028 
   1029     default:
   1030         rv = SOC_E_UNAVAIL;
   1031         break;
   1032     }
   1033     return rv;
   1034 }
   1035 /*
   1036  * Function:
   1037  *      phy_serdes100fx_control_get
   1038  * Purpose:
   1039  *      Get current control settign of the PHY.
   1040  * Parameters:
   1041  *      unit  - StrataSwitch unit #.
   1042  *      port  - StrataSwitch port #.
   1043  *      type  - Control to update
   1044  *      value - (OUT)Current setting for the control
   1045  * Returns:    
   1046  *      SOC_E_NONE
   1047  */
   1048 STATIC int
   1049 phy_serdes100fx_control_get(int unit, soc_port_t port,
   1050                      soc_phy_control_t type, uint32 *value)
   1051 {
   1052     int rv;
   1053     phy_ctrl_t        *pc;
   1054     uint16 data16 = 0;
   1055 
   1056     pc                       = INT_PHY_SW_STATE(unit, port);
   1057 
   1058     if (NULL == value) {
   1059         return SOC_E_PARAM;
   1060     }
   1061     /* coverity[mixed_enums] */
   1062     if ((type < 0) || (type >= SOC_PHY_CONTROL_COUNT)) {
   1063         return SOC_E_PARAM;
   1064     }
   1065   
   1066     rv = SOC_E_UNAVAIL;
   1067     switch(type) {
   1068     case SOC_PHY_CONTROL_PREEMPHASIS:
   1069         /* fall through */
   1070     case SOC_PHY_CONTROL_DRIVER_CURRENT:
   1071         /* fall through */
   1072     case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
   1073         rv = _phy_serdes100fx_control_tx_driver_get(unit, port, type, value);
   1074         break;
   1075     case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT:
   1076         rv = _phy_serdes100fx_control_linkdown_transmit_get(unit, port, value);
   1077         break;
   1078 
   1079     case SOC_PHY_CONTROL_PARALLEL_DETECTION:
   1080         SOC_IF_ERROR_RETURN
   1081             (READ_SERDES100FX_1000X_CTRL2r(unit, pc,&data16));
   1082         *value = data16 & FX_1000X_PAR_DET_EN?  TRUE: FALSE;
   1083         rv = SOC_E_NONE;
   1084         break;
   1085 
   1086     default:
   1087         rv = SOC_E_UNAVAIL;
   1088         break;
   1089     }
   1090 
   1091     return rv;
   1092 }
   1093 
   1094 STATIC int
   1095 _phy_serdes100fx_tx_fifo_reset(int unit, soc_port_t port,uint32 speed)
   1096 {
   1097     uint16 data16;
   1098     phy_ctrl_t  *pc;
   1099     pc = INT_PHY_SW_STATE(unit, port);
   1100 
   1101     if (speed == 100) {
   1102         /* check if it is in 100fx mode */
   1103         SOC_IF_ERROR_RETURN
   1104             (READ_SERDES100FX_FX100_CTRL1r(unit,pc,&data16));
   1105 
   1106         if (data16 & FX_FX100_CONTROL1_EN) {
   1107             /* reset TX FIFO  */
   1108             SOC_IF_ERROR_RETURN
   1109                 (MODIFY_SERDES100FX_1000X_CTRL4r(unit,pc,
   1110                                  FX_1000X_DIG_RESET,
   1111                                  FX_1000X_DIG_RESET));
   1112             SOC_IF_ERROR_RETURN
   1113                 (MODIFY_SERDES100FX_1000X_CTRL4r(unit,pc,
   1114                                  0,
   1115                                  FX_1000X_DIG_RESET));
   1116         }
   1117     }
   1118     return SOC_E_NONE;
   1119 }
   1120 
   1121 STATIC int
   1122 phy_serdes100fx_notify(int unit, soc_port_t port,
   1123                  soc_phy_event_t event, uint32 value)
   1124 {
   1125     int             rv;
   1126 
   1127     if (event >= phyEventCount) {
   1128         return SOC_E_PARAM;
   1129     }
   1130 
   1131     rv = SOC_E_NONE;
   1132     switch(event) {
   1133     case phyEventInterface:
   1134         rv = (_phy_serdes100fx_notify_interface(unit, port, value));
   1135         break;
   1136     case phyEventDuplex:
   1137         rv = (_phy_serdes100fx_notify_duplex(unit, port, value));
   1138         break;
   1139     case phyEventSpeed:
   1140         rv = (_phy_serdes100fx_notify_speed(unit, port, value));
   1141         break;
   1142     case phyEventStop:
   1143         rv = (_phy_serdes100fx_notify_stop(unit, port, value));
   1144         break;
   1145     case phyEventResume:
   1146         rv = (_phy_serdes100fx_notify_resume(unit, port, value));
   1147         break;
   1148     case phyEventTxFifoReset:
   1149         rv = (_phy_serdes100fx_tx_fifo_reset(unit, port, value));
   1150         break;
   1151     default:
   1152         rv = SOC_E_PARAM;
   1153     }
   1154 
   1155     return rv;
   1156 }
   1157 
   1158 /*
   1159  * Function:
   1160  *      phy_serdes100fx_duplex_get
   1161  * Purpose:
   1162  *      Get the current operating duplex mode.
   1163  * Parameters:
   1164  *      unit - StrataSwitch unit #.
   1165  *      port - StrataSwitch port #.
   1166  *      duplex - (OUT) Boolean, true indicates full duplex, false
   1167  *              indicates half.
   1168  * Returns:
   1169  *      SOC_E_NONE
   1170  */
   1171 
   1172 STATIC int
   1173 phy_serdes100fx_duplex_get(int unit, soc_port_t port, int *duplex)
   1174 {
   1175     uint16       data16;
   1176     uint16       mii_ctrl;
   1177     phy_ctrl_t  *pc;
   1178                                                                                 
   1179     pc = INT_PHY_SW_STATE(unit, port);
   1180                                                                                 
   1181     *duplex = TRUE;
   1182                                                                                 
   1183     SOC_IF_ERROR_RETURN
   1184         (READ_SERDES100FX_1000X_STAT1r(unit, pc, &data16));
   1185 
   1186     if (!(data16 & FX_1000X_STATUS1_SGMII_MODE)) { /* fiber mode */
   1187         *duplex = (data16 & FX_1000X_STATUS1_FULL_DUPLEX)?
   1188                    TRUE:FALSE; 
   1189     } else {
   1190                                                                                 
   1191         /* retrieve the duplex setting in SGMII mode */
   1192                                                                                 
   1193         SOC_IF_ERROR_RETURN
   1194             (READ_SERDES100FX_MII_CTRLr(unit, pc, &mii_ctrl));
   1195                                                                                 
   1196         if (mii_ctrl & FX_MII_CTRL_AN_ENABLE) {
   1197             SOC_IF_ERROR_RETURN
   1198                 (READ_SERDES100FX_MII_ANPr(unit,pc,&data16));
   1199                                                                                 
   1200             /* make sure link partner is also in SGMII mode
   1201              * otherwise fall through to use the FD bit in MII_CTRL reg
   1202              */
   1203             if (data16 & FX_MII_ANP_SGMII) {
   1204                                                                                 
   1205                 /* test shows the duplex setting of external phy is
   1206                  * propagated to the AN link partner register
   1207                  */
   1208                 if (data16 & FX_MII_ANP_SGMII_DUPLEX) {
   1209                     *duplex = TRUE;
   1210                     return SOC_E_NONE;
   1211                 } else {
   1212                     *duplex = FALSE;
   1213                     return SOC_E_NONE;
   1214                 }
   1215             }
   1216         }
   1217         *duplex = (mii_ctrl & FX_MII_CTRL_FD) ? TRUE : FALSE;
   1218     }
   1219     return SOC_E_NONE;
   1220 }
   1221 
   1222 /*
   1223  * Function:
   1224  *      phy_serdes100fx_duplex_set  
   1225  * Purpose:
   1226  *      Set the current duplex mode (forced).
   1227  * Parameters:  
   1228  *      unit - StrataSwitch unit #.  
   1229  *      port - StrataSwitch port #.
   1230  *      duplex - Boolean, true indicates full duplex, false indicates half.
   1231  * Returns:
   1232  *      SOC_E_XXX
   1233  */
   1234 STATIC int
   1235 phy_serdes100fx_duplex_set(int unit, soc_port_t port, int duplex)
   1236 {
   1237     uint16       data16;
   1238     phy_ctrl_t  *pc;
   1239 
   1240     pc = INT_PHY_SW_STATE(unit, port);
   1241 
   1242     SOC_IF_ERROR_RETURN
   1243         (READ_SERDES100FX_1000X_STAT1r(unit, pc, &data16));
   1244 
   1245     if (!(data16 & FX_1000X_STATUS1_SGMII_MODE)) {  /* fiber mode */
   1246 
   1247         /* set only for 100FX. 1000X always fd */
   1248         SOC_IF_ERROR_RETURN
   1249             (READ_SERDES100FX_FX100_CTRL1r(unit, pc,&data16));
   1250 
   1251         if (data16 & FX_FX100_CONTROL1_EN) {
   1252             SOC_IF_ERROR_RETURN
   1253                 (MODIFY_SERDES100FX_FX100_CTRL1r(unit, pc,
   1254                    duplex? FX_FX100_CONTROL1_FD:0,
   1255                    FX_FX100_CONTROL1_FD));
   1256         }
   1257     } else {  /* SGMII mode */
   1258         SOC_IF_ERROR_RETURN
   1259             (MODIFY_SERDES100FX_MII_CTRLr(unit, pc,
   1260                             duplex? MII_CTRL_FD: 0,
   1261                             MII_CTRL_FD));
   1262     }
   1263     return SOC_E_NONE;
   1264 }
   1265 
   1266 
   1267 /*
   1268  * Variable:    phy_serdes100fxdrv_ge
   1269  * Purpose:     PHY Driver for Dozen SerDes Serdes PHY internal to Draco, 
   1270  */
   1271 phy_driver_t phy_serdes100fx_ge = {
   1272     "Internal SERDES(100FX) PHY Driver",
   1273     phy_serdes100fx_init,
   1274     phy_null_reset,
   1275     phy_serdes_link_get, 
   1276     phy_serdes100fx_enable_set, 
   1277     phy_null_enable_get, 
   1278     phy_serdes100fx_duplex_set, 
   1279     phy_serdes100fx_duplex_get,                    /* duplex_get */
   1280     phy_serdes100fx_speed_set, 
   1281     phy_serdes100fx_speed_get, 
   1282     phy_serdes_master_set,          /* master_set */
   1283     phy_serdes_master_get,          /* master_get */
   1284     phy_serdes100fx_an_set,
   1285     phy_serdes_an_get,
   1286     phy_serdes_adv_local_set,
   1287     phy_serdes_adv_local_get,
   1288     phy_serdes_adv_remote_get,
   1289     phy_serdes100fx_lb_set,
   1290     phy_serdes100fx_lb_get,
   1291     phy_serdes100fx_interface_set, 
   1292     phy_serdes100fx_interface_get, 
   1293     phy_serdes100fx_ability_get,
   1294     NULL,
   1295     NULL,
   1296     phy_null_mdix_set,
   1297     phy_null_mdix_get,
   1298     phy_null_mdix_status_get,
   1299     phy_serdes100fx_medium_config_set,
   1300     phy_serdes_medium_config_get,
   1301     phy_serdes_medium_status,
   1302     NULL,                          /* phy_cable_diag  */
   1303     NULL,                          /* phy_link_change */
   1304     phy_serdes100fx_control_set,         /* phy_control_set */
   1305     phy_serdes100fx_control_get,         /* phy_control_get */
   1306     phy_serdes_reg_read,
   1307     phy_serdes_reg_write,
   1308     phy_serdes_reg_modify,
   1309     phy_serdes100fx_notify
   1310 };
   1311 
   1312 /***********************************************************************
   1313  *
   1314  * PASS-THROUGH NOTIFY ROUTINES
   1315  */
   1316 
   1317 /*
   1318  * Function:
   1319  *      _phy_serdes100fx_notify_duplex
   1320  * Purpose:     
   1321  *      Program duplex if (and only if) serdes100fx is an intermediate PHY.
   1322  * Parameters:
   1323  *      unit - StrataSwitch unit #.
   1324  *      port - StrataSwitch port #. 
   1325  *      duplex - Boolean, TRUE indicates full duplex, FALSE
   1326  *              indicates half.
   1327  * Returns:     
   1328  *      SOC_E_XXX
   1329  * Notes:
   1330  *      If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to
   1331  *      talk directly to an external fiber module.
   1332  *
   1333  *      If PHY_FLAGS_FIBER is clear, the PHY is being used in
   1334  *      pass-through mode to talk to an external SGMII PHY.
   1335  *
   1336  *      When used in pass-through mode, autoneg must be turned off and
   1337  *      the speed/duplex forced to match that of the external PHY.
   1338  */
   1339 
   1340 STATIC int
   1341 _phy_serdes100fx_notify_duplex(int unit, soc_port_t port, uint32 duplex)
   1342 {
   1343     int                 fiber;
   1344     uint16              mii_ctrl;
   1345     phy_ctrl_t  *pc;
   1346 
   1347     fiber = PHY_FIBER_MODE(unit, port);
   1348     pc    = INT_PHY_SW_STATE(unit, port);
   1349     LOG_INFO(BSL_LS_SOC_PHY,
   1350              (BSL_META_U(unit,
   1351                          "phy_serdes100fx_notify_duplex: "
   1352                          "u=%d p=%d duplex=%d fiber=%d\n"),
   1353               unit, port, duplex, fiber));
   1354 
   1355     if (SAL_BOOT_SIMULATION) {
   1356         return SOC_E_NONE;
   1357     }
   1358 
   1359     if (fiber) {
   1360         SOC_IF_ERROR_RETURN
   1361             (MODIFY_SERDES100FX_MII_CTRLr(unit, pc, 
   1362                                   FX_MII_CTRL_FD, FX_MII_CTRL_FD));
   1363         return SOC_E_NONE;
   1364     }
   1365 
   1366     /* Put SERDES PHY in reset */
   1367 
   1368     SOC_IF_ERROR_RETURN
   1369         (_phy_serdes100fx_notify_stop(unit, port, PHY_STOP_DUPLEX_CHG));
   1370 
   1371     /* Update duplexity */
   1372     mii_ctrl = (duplex) ?FX_MII_CTRL_FD : 0;
   1373     SOC_IF_ERROR_RETURN
   1374         (MODIFY_SERDES100FX_MII_CTRLr(unit, pc, mii_ctrl, FX_MII_CTRL_FD));
   1375 
   1376     /* Take SERDES PHY out of reset */
   1377     SOC_IF_ERROR_RETURN
   1378         (_phy_serdes100fx_notify_resume(unit, port, PHY_STOP_DUPLEX_CHG));
   1379 
   1380     /* Autonegotiation must be turned off to talk to external PHY if
   1381      * SGMII autoneg is not enabled.
   1382      */
   1383     if (!PHY_SGMII_AUTONEG_MODE(unit, port)) {
   1384         SOC_IF_ERROR_RETURN
   1385             (phy_serdes100fx_an_set(unit, port, FALSE));
   1386     }
   1387     return SOC_E_NONE;
   1388 }
   1389 
   1390 /*
   1391  * Function:
   1392  *      _phy_serdes100fx_notify_speed
   1393  * Purpose:     
   1394  *      Program duplex if (and only if) serdes100fx is an intermediate PHY.
   1395  * Parameters:
   1396  *      unit - StrataSwitch unit #.
   1397  *      port - StrataSwitch port #. 
   1398  *      speed - Speed to program.
   1399  * Returns:     
   1400  *      SOC_E_XXX
   1401  * Notes:
   1402  *      If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to
   1403  *      talk directly to an external fiber module.
   1404  *
   1405  *      If PHY_FLAGS_FIBER is clear, the PHY is being used in
   1406  *      pass-through mode to talk to an external SGMII PHY.
   1407  *
   1408  *      When used in pass-through mode, autoneg must be turned off and
   1409  *      the speed/duplex forced to match that of the external PHY.
   1410  */
   1411 
   1412 STATIC int
   1413 _phy_serdes100fx_notify_speed(int unit, soc_port_t port, uint32 speed)
   1414 {
   1415     int          fiber;
   1416     uint16       fiber_status;
   1417     phy_ctrl_t  *pc;
   1418 
   1419     pc    = INT_PHY_SW_STATE(unit, port);
   1420     fiber = PHY_FIBER_MODE(unit, port);
   1421 
   1422     LOG_INFO(BSL_LS_SOC_PHY,
   1423              (BSL_META_U(unit,
   1424                          "_phy_serdes100fx_notify_speed: "
   1425                          "u=%d p=%d speed=%d fiber=%d\n"),
   1426               unit, port, speed, fiber));
   1427 
   1428     if (SAL_BOOT_SIMULATION) {
   1429         return SOC_E_NONE;
   1430     }
   1431  
   1432     SOC_IF_ERROR_RETURN
   1433         (READ_SERDES100FX_1000X_STAT1r(unit, pc, &fiber_status));
   1434 
   1435     if (fiber && !(fiber_status & FX_1000X_STATUS1_SGMII_MODE)) {
   1436         if ((speed != 0) && (speed != 100) && (speed != 1000)) {
   1437             return SOC_E_CONFIG;
   1438         }
   1439     }
   1440 
   1441     /* Put SERDES PHY in reset */
   1442     SOC_IF_ERROR_RETURN
   1443         (_phy_serdes100fx_notify_stop(unit, port, PHY_STOP_SPEED_CHG));
   1444 
   1445     /* Update speed */
   1446     SOC_IF_ERROR_RETURN
   1447         (_phy_serdes100fx_sgmii_speed_set(unit, port, speed));
   1448 
   1449     /* Take SERDES PHY out of reset */
   1450     SOC_IF_ERROR_RETURN
   1451         (_phy_serdes100fx_notify_resume(unit, port, PHY_STOP_SPEED_CHG));
   1452 
   1453     /* Autonegotiation must be turned off to talk to external PHY */
   1454     if (!PHY_SGMII_AUTONEG_MODE(unit, port) && !fiber) {
   1455         SOC_IF_ERROR_RETURN
   1456             (phy_serdes100fx_an_set(unit, port, FALSE));
   1457     }
   1458 
   1459     return SOC_E_NONE;
   1460 }
   1461 
   1462 /*
   1463  * Function:
   1464  *      _phy_serdes100fx_stop
   1465  * Purpose:
   1466  *      Put serdes100fx SERDES in or out of reset depending on conditions
   1467  */
   1468 
   1469 STATIC int
   1470 _phy_serdes100fx_stop(int unit, soc_port_t port)
   1471 {
   1472     uint16              mii_ctrl;
   1473     int                 stop, copper;
   1474     phy_ctrl_t  *pc;
   1475 
   1476     pc = INT_PHY_SW_STATE(unit, port);
   1477 
   1478     copper = (pc->stop & 
   1479               PHY_STOP_COPPER) != 0;
   1480 
   1481     stop = ((pc->stop &
   1482              (PHY_STOP_PHY_DIS |
   1483               PHY_STOP_DRAIN)) != 0 ||
   1484             (copper &&
   1485              (pc->stop &
   1486               (PHY_STOP_MAC_DIS |
   1487                PHY_STOP_DUPLEX_CHG |
   1488                PHY_STOP_SPEED_CHG)) != 0));
   1489 
   1490     LOG_INFO(BSL_LS_SOC_PHY,
   1491              (BSL_META_U(unit,
   1492                          "phy_serdes100fx_stop: u=%d p=%d copper=%d"
   1493                          " stop=%d flg=0x%x\n"),
   1494               unit, port, copper, stop,
   1495               pc->stop));
   1496 
   1497     mii_ctrl = (stop) ?FX_MII_CTRL_PD : 0;
   1498     SOC_IF_ERROR_RETURN
   1499         (MODIFY_SERDES100FX_MII_CTRLr(unit, pc, mii_ctrl, FX_MII_CTRL_PD));
   1500 
   1501     return SOC_E_NONE; 
   1502 }
   1503 
   1504 /*
   1505  * Function:
   1506  *      _phy_serdes100fx_notify_stop
   1507  * Purpose:     
   1508  *      Add a reason to put serdes100fx PHY in reset.
   1509  * Parameters:
   1510  *      unit - StrataSwitch unit #.
   1511  *      port - StrataSwitch port #. 
   1512  *      flags - Reason to stop
   1513  * Returns:     
   1514  *      SOC_E_XXX
   1515  */
   1516 
   1517 STATIC int
   1518 _phy_serdes100fx_notify_stop(int unit, soc_port_t port, uint32 flags)
   1519 {
   1520     INT_PHY_SW_STATE(unit, port)->stop |= flags;
   1521 
   1522     return _phy_serdes100fx_stop(unit, port);
   1523 }
   1524 
   1525 /*
   1526  * Function:
   1527  *      phy_serdes100fx_notify_resume
   1528  * Purpose:     
   1529  *      Remove a reason to put serdes100fx PHY in reset.
   1530  * Parameters:
   1531  *      unit - StrataSwitch unit #.
   1532  *      port - StrataSwitch port #. 
   1533  *      flags - Reason to stop
   1534  * Returns:     
   1535  *      SOC_E_XXX
   1536  */
   1537 
   1538 STATIC int
   1539 _phy_serdes100fx_notify_resume(int unit, soc_port_t port, uint32 flags)
   1540 {
   1541     INT_PHY_SW_STATE(unit, port)->stop &= ~flags;
   1542 
   1543     return _phy_serdes100fx_stop(unit, port);
   1544 }
   1545 
   1546 /*
   1547  * Function:
   1548  *      phy_serdes100fx_media_setup
   1549  * Purpose:     
   1550  *      Configure 
   1551  * Parameters:
   1552  *      unit - StrataSwitch unit #.
   1553  *      port - StrataSwitch port #. 
   1554  *      intf - Preferred interface (SGMII or 1000X) 
   1555  * Returns:     
   1556  *      SOC_E_XXX
   1557  */
   1558 STATIC int
   1559 _phy_serdes100fx_notify_interface(int unit, soc_port_t port, uint32 intf)
   1560 {
   1561     phy_ctrl_t  *pc;
   1562     uint16       data16;
   1563 
   1564     pc = INT_PHY_SW_STATE(unit, port);
   1565   
   1566     data16 = 0;
   1567     if (intf != SOC_PORT_IF_SGMII) {
   1568         data16 = FX_1000X_FIBER_MODE;
   1569     }
   1570     SOC_IF_ERROR_RETURN
   1571         (MODIFY_SERDES100FX_1000X_CTRL1r(unit, pc,
   1572                                          data16, FX_1000X_FIBER_MODE));
   1573 
   1574     return SOC_E_NONE;
   1575 }
   1576 
   1577 #else /* INCLUDE_SERDES_100FX */
   1578 int _phy_serdes100fx_not_empty;
   1579 #endif /* INCLUDE_SERDES_100FX */
   1580