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

serdescombo.c (45433B)


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