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

phy53xxx.c (30659B)


      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:        serdes.c
      8  * Purpose:
      9  *	Fiber driver for 53xxx using internal QuadSerdes PHY.
     10  */
     11 #include <sal/types.h>
     12 #include <sal/core/spl.h>
     13 #include <shared/bsl.h>
     14 #include <soc/drv.h>
     15 #include <soc/debug.h>
     16 #include <soc/error.h>
     17 #include <soc/phyreg.h>
     18 
     19 #include <soc/phy.h>
     20 #include <soc/phy/phyctrl.h>
     21 #include <soc/phy/drv.h>
     22 
     23 #include "phydefs.h"      /* Must include before other phy related includes */
     24 
     25 #if defined(INCLUDE_PHY_53XXX)
     26 #include "phyconfig.h"    /* Must be the first phy include after phydefs.h */
     27 
     28 #include "phyident.h"
     29 #include "phyreg.h"
     30 #include "phynull.h"
     31 #include "serdes.h"
     32 #include "phy53xxx.h"
     33 #define ADVERT_ALL_FIBER \
     34             (SOC_PM_PAUSE | SOC_PM_1000MB_FD)
     35 
     36 
     37 /* Notify event forward declaration */
     38 STATIC int _phy_53xxx_notify_duplex(int unit, soc_port_t port, uint32 duplex);
     39 STATIC int _phy_53xxx_notify_speed(int unit, soc_port_t port, uint32 speed);
     40 STATIC int _phy_53xxx_notify_stop(int unit, soc_port_t port, uint32 flags);
     41 STATIC int _phy_53xxx_notify_resume(int unit, soc_port_t port, uint32 flags);
     42 STATIC int _phy_53xxx_notify_interface(int unit, soc_port_t port, uint32 intf);
     43 
     44 /***********************************************************************
     45  *
     46  * FIBER DRIVER ROUTINES
     47  */
     48 STATIC int
     49 phy_53xxx_init_no_reset(int unit, soc_port_t port)
     50 {
     51     uint16             data16, mask16;
     52     soc_timeout_t      to;
     53     phy_ctrl_t        *pc;
     54     int                rv;
     55 
     56     pc                       = INT_PHY_SW_STATE(unit, port);
     57     pc->fiber.enable         = PHY_FIBER_MODE(unit, port);
     58     pc->fiber.preferred      = PHY_FIBER_MODE(unit, port);
     59     pc->fiber.autoneg_enable = TRUE;
     60     pc->fiber.autoneg_advert = ADVERT_ALL_FIBER;
     61     pc->fiber.force_speed    = 1000;
     62     pc->fiber.force_duplex   = TRUE;
     63     pc->fiber.master         = SOC_PORT_MS_NONE;
     64     pc->fiber.mdix           = SOC_PORT_MDIX_NORMAL;
     65 
     66     /* Since phy53xxx driver reset function vector does not reset
     67      * the SerDes, reset the SerDes in initialization first. 
     68      * The internal SERDES PHY's reset bit is self-clearing.
     69      */
     70     SOC_IF_ERROR_RETURN
     71         (MODIFY_SERDES_MII_CTRLr(unit, pc,
     72                                MII_CTRL_RESET, MII_CTRL_RESET));
     73 
     74     soc_timeout_init(&to, 10000, 0);
     75     while (!soc_timeout_check(&to)) {
     76         rv = READ_SERDES_MII_CTRLr(unit, pc, &data16);
     77         if (((data16 & MII_CTRL_RESET) == 0) || SOC_FAILURE(rv)) {
     78             break;
     79         }
     80     }
     81     if ((data16 & MII_CTRL_RESET) != 0) {
     82         LOG_WARN(BSL_LS_SOC_PHY,
     83                  (BSL_META_U(unit,
     84                              "Combo SerDes reset failed: u=%d p=%d\n"),
     85                   unit, port));
     86     }
     87 
     88     data16 = 0;
     89     mask16 = QS_MII_CTRL_AN_ENABLE | QS_MII_CTRL_AN_RESTART;
     90     if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) ||
     91         PHY_PASSTHRU_MODE(unit, port) || 
     92         PHY_SGMII_AUTONEG_MODE(unit, port)) {
     93         data16 = QS_MII_CTRL_AN_ENABLE | QS_MII_CTRL_AN_RESTART;
     94     }
     95     SOC_IF_ERROR_RETURN
     96         (MODIFY_SERDES_MII_CTRLr(unit, pc, data16, mask16));
     97 
     98     data16 = QS_1000X_FALSE_LNK_DIS | QS_1000X_FLT_FORCE_EN;
     99     mask16 = QS_1000X_FALSE_LNK_DIS | QS_1000X_FLT_FORCE_EN;
    100     SOC_IF_ERROR_RETURN
    101         (MODIFY_SERDES_1000X_CTRL2r(unit, pc, data16, mask16));
    102 
    103     data16 = 0;
    104     mask16 = QS_1000X_AUTO_DETECT | QS_1000X_FIBER_MODE;
    105     /*
    106      * Configure signal auto-detection between SGMII and fiber.
    107      * This detection only works if auto-negotiation is enabled.
    108      */
    109     if (soc_property_port_get(unit, port, 
    110                               spn_SERDES_AUTOMEDIUM, FALSE)) {
    111         data16 |= QS_1000X_AUTO_DETECT;
    112     }
    113     /*
    114      * Put the Serdes in Fiber or SGMII mode
    115      */
    116     if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) ||
    117         PHY_PASSTHRU_MODE(unit, port)) {
    118         if (soc_property_port_get(unit, port,
    119                                   spn_SERDES_FIBER_PREF, TRUE)) {
    120             data16 |= QS_1000X_FIBER_MODE;
    121         }
    122     }
    123     SOC_IF_ERROR_RETURN
    124         (MODIFY_SERDES_1000X_CTRL1r(unit, pc, data16, mask16));
    125 
    126     if (PHY_COPPER_MODE(unit, port)) {
    127         /* Do not wait 10ms for sync status before a valid link is 
    128          * established when auto-negotiation is disabled.
    129          */
    130         SOC_IF_ERROR_RETURN
    131             (MODIFY_SERDES_1000X_CTRL2r(unit, pc, 0x0000, 0x0004));
    132     }
    133    
    134     LOG_INFO(BSL_LS_SOC_PHY,
    135              (BSL_META_U(unit,
    136                          "phy_53xxx_init: u=%d p=%d %s\n"),
    137               unit, port, 
    138               PHY_FIBER_MODE(unit, port) ? "Fiber" : "Copper"));
    139     return SOC_E_NONE;
    140 }
    141 
    142 
    143 /*
    144  * Function:
    145  *      phy_53xxx_init
    146  * Purpose:     
    147  *      Initialize 53xxx internal PHY driver
    148  * Parameters:
    149  *      unit - Switch unit #.
    150  *      port - Switch port #. 
    151  * Returns:     
    152  *      SOC_E_NONE
    153  * Notes:
    154  *      This function implements the bulk part of phy_53xxx_init and
    155  *      provides a method for initializing the SerDes when it is used
    156  *      as an intermediate PHY between the MAC and an external PHY. 
    157  *      In the latter case the PHY driver reset callback should be
    158  *      called only once, and this has already been done by the 
    159  *      external PHY driver init function.
    160  */
    161 
    162 STATIC int
    163 phy_53xxx_init(int unit, soc_port_t port)
    164 {
    165 
    166     LOG_INFO(BSL_LS_SOC_PHY,
    167              (BSL_META_U(unit,
    168                          "phy_53xxx_init: u=%d p=%d\n"),
    169               unit, port));
    170 
    171     if (!PHY_EXTERNAL_MODE(unit, port)) {
    172         /* PHY reset callbacks */
    173         SOC_IF_ERROR_RETURN(soc_phy_reset(unit, port));
    174 
    175         PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
    176     }
    177     SOC_IF_ERROR_RETURN
    178         (phy_53xxx_init_no_reset(unit, port));
    179 
    180     return SOC_E_NONE;
    181 }
    182 
    183 /*
    184  * Function:    
    185  *      phy_53xxx_enable_set
    186  * Purpose:     
    187  *      Enable or disable the physical interface.
    188  * Parameters:
    189  *      unit - Switch unit #.
    190  *      port - Switch port #. 
    191  *      enable - Boolean, true = enable PHY, false = disable.
    192  * Returns:     
    193  *      SOC_E_XXX
    194  * Notes:
    195  *      The PHY is held in reset when disabled.  Also, the
    196  *      MISC_CONTROL.IDDQ bit must be set to 1 so the remote partner
    197  *      sees link down.
    198  */
    199 
    200 STATIC int
    201 phy_53xxx_enable_set(int unit, soc_port_t port, int enable)
    202 {
    203     LOG_INFO(BSL_LS_SOC_PHY,
    204              (BSL_META_U(unit,
    205                          "phy_53xxx_enable_set: u=%d p=%d en=%d\n"),
    206               unit, port, enable));
    207     if (enable) {
    208         PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE);
    209     } else {
    210         PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE);
    211     }
    212 
    213     return SOC_E_NONE;
    214 }
    215 
    216 STATIC int
    217 _phy_53xxx_sgmii_speed_set(int unit, soc_port_t port, int speed)
    218 {
    219     uint16 mii_ctrl;
    220     phy_ctrl_t  *pc;
    221 
    222     pc = INT_PHY_SW_STATE(unit, port);
    223 
    224     switch (speed) {
    225         case 10:
    226             mii_ctrl = QS_MII_CTRL_SS_10;
    227             break;
    228         case 100:
    229             mii_ctrl = QS_MII_CTRL_SS_100;
    230             break;
    231         case 0:
    232         case 1000:  
    233             mii_ctrl = QS_MII_CTRL_SS_1000;
    234             break;
    235         default:
    236             return SOC_E_CONFIG;
    237     } 
    238     SOC_IF_ERROR_RETURN
    239         (MODIFY_SERDES_MII_CTRLr(unit, pc, mii_ctrl,
    240                                (QS_MII_CTRL_SS_LSB | QS_MII_CTRL_SS_MSB)));
    241     return SOC_E_NONE;
    242 }
    243 
    244 STATIC int
    245 _phy_53xxx_1000x_speed_set(int unit, soc_port_t port, int speed)
    246 {
    247     if ((speed !=0) && (speed != 1000)) {
    248         return SOC_E_CONFIG;
    249     } 
    250 
    251     return SOC_E_NONE;
    252 }
    253 
    254 /*
    255  * Function:    
    256  *      phy_53xxx_speed_set
    257  * Purpose:     
    258  *      Set the current operating speed (forced).
    259  * Parameters:
    260  *      unit - Switch unit #.
    261  *      port - Switch port #. 
    262  *      duplex - (OUT) Boolean, true indicates full duplex, false 
    263  *              indicates half.
    264  * Returns:     
    265  *      SOC_E_XXX
    266  */
    267 
    268 STATIC int
    269 phy_53xxx_speed_set(int unit, soc_port_t port, int speed)
    270 {
    271     uint16              fiber_status;
    272     phy_ctrl_t  *pc;
    273     int                 rv;
    274 
    275     pc = INT_PHY_SW_STATE(unit, port);
    276     rv = SOC_E_NONE;
    277     
    278     SOC_IF_ERROR_RETURN
    279         (READ_SERDES_1000X_STAT1r(unit, pc, &fiber_status));
    280 
    281     if (fiber_status & QS_1000X_STATUS1_SGMII_MODE) {
    282         rv = _phy_53xxx_sgmii_speed_set(unit, port, speed);
    283     } else {
    284         rv = _phy_53xxx_1000x_speed_set(unit, port, speed);
    285     }
    286 
    287     if (SOC_SUCCESS(rv)) {
    288         pc->fiber.force_speed = speed;
    289     }
    290 
    291     LOG_INFO(BSL_LS_SOC_PHY,
    292              (BSL_META_U(unit,
    293                          "phy_53xxx_speed_set: u=%d p=%d speed=%d rv=%d\n"),
    294               unit, port, speed, rv));
    295     
    296     return rv;
    297 }
    298 
    299 STATIC int 
    300 _phy_53xxx_sgmii_speed_get(int unit, soc_port_t port, int *speed)
    301 {
    302     uint16 mii_ctrl, mii_stat, mii_anp;
    303     phy_ctrl_t  *pc;
    304 
    305     pc = INT_PHY_SW_STATE(unit, port);
    306 
    307     SOC_IF_ERROR_RETURN
    308         (READ_SERDES_MII_CTRLr(unit, pc, &mii_ctrl));
    309     SOC_IF_ERROR_RETURN
    310         (READ_SERDES_MII_STATr(unit, pc, &mii_stat)); 
    311  
    312     if (mii_ctrl & QS_MII_CTRL_AN_ENABLE) { /*Auto-negotiation enabled */  
    313         if (!(mii_stat & MII_STAT_AN_DONE)) { /* Auto-neg NOT complete */
    314             *speed = 0;
    315             return SOC_E_NONE;
    316         } else { /* Read Advertise link partner */
    317             SOC_IF_ERROR_RETURN
    318                 (READ_SERDES_MII_ANPr(unit, pc, &mii_anp));
    319             if (mii_anp & QS_MII_ANP_SGMII) {
    320                 switch(mii_anp & QS_MII_ANP_SGMII_SPEED) {
    321                 case QS_MII_ANP_SGMII_SPEED_1000:
    322                     *speed = 1000;
    323                     break;
    324                 case QS_MII_ANP_SGMII_SPEED_100:
    325                     *speed = 100;
    326                     break;
    327                 case QS_MII_ANP_SGMII_SPEED_10:
    328                     *speed = 10;
    329                     break;
    330                 default:
    331                     return SOC_E_UNAVAIL;
    332                 }
    333             }
    334         }
    335     } else {  /* Forced speed */
    336         switch(mii_ctrl & (QS_MII_CTRL_SS_LSB | QS_MII_CTRL_SS_MSB)) {
    337         case QS_MII_CTRL_SS_10:
    338             *speed = 10; 
    339             break;
    340         case QS_MII_CTRL_SS_100:
    341             *speed = 100;
    342             break;
    343         case QS_MII_CTRL_SS_1000:
    344             *speed = 1000;
    345             break;
    346         default:
    347             return SOC_E_UNAVAIL;
    348         } 
    349     }
    350     return SOC_E_NONE;
    351 }
    352 
    353 STATIC int
    354 _phy_53xxx_1000x_speed_get(int unit, soc_port_t port, int *speed)
    355 {
    356     COMPILER_REFERENCE(unit);
    357     COMPILER_REFERENCE(port);
    358 
    359     *speed = 1000;
    360 
    361     return SOC_E_NONE;
    362 }
    363 
    364 /*
    365  * Function:    
    366  *      phy_53xxx_speed_get
    367  * Purpose:     
    368  *      Get the current operating speed. If autoneg is enabled, 
    369  *      then operating mode is returned, otherwise forced mode is returned.
    370  * Parameters:
    371  *      unit - Switch unit #.
    372  *      port - Switch port #. 
    373  *      duplex - (OUT) Boolean, true indicates full duplex, false 
    374  *              indicates half.
    375  * Returns:     
    376  *      SOC_E_XXX
    377  */
    378 
    379 STATIC int
    380 phy_53xxx_speed_get(int unit, soc_port_t port, int *speed)
    381 {
    382     uint16       fiber_status;
    383     phy_ctrl_t  *pc;
    384 
    385     pc = INT_PHY_SW_STATE(unit, port);
    386 
    387     *speed = 1000;
    388  
    389     SOC_IF_ERROR_RETURN
    390         (READ_SERDES_1000X_STAT1r(unit, pc, &fiber_status));
    391 
    392     if (fiber_status & QS_1000X_STATUS1_SGMII_MODE) {
    393         SOC_IF_ERROR_RETURN
    394             (_phy_53xxx_sgmii_speed_get(unit, port, speed));
    395     } else {
    396         SOC_IF_ERROR_RETURN
    397             (_phy_53xxx_1000x_speed_get(unit, port, speed));
    398     }
    399 
    400     return SOC_E_NONE;
    401 }
    402 
    403 /*
    404  * Function:    
    405  *      phy_53xxx_an_set
    406  * Purpose:     
    407  *      Enable or disabled auto-negotiation on the specified port.
    408  * Parameters:
    409  *      unit - Switch unit #.
    410  *      port - Switch port #. 
    411  *      an - Boolean, if true, auto-negotiation is enabled 
    412  *              (and/or restarted). If false, autonegotiation is disabled.
    413  * Returns:     
    414  *      SOC_E_XXX
    415  */
    416 
    417 STATIC int
    418 phy_53xxx_an_set(int unit, soc_port_t port, int an)
    419 {
    420     phy_ctrl_t  *pc;
    421     uint16       par_det;
    422     uint16       an_enable;
    423     uint16       auto_det;
    424 
    425     pc = INT_PHY_SW_STATE(unit, port);
    426 
    427     LOG_INFO(BSL_LS_SOC_PHY,
    428              (BSL_META_U(unit,
    429                          "phy_53xxx_an_set: u=%d p=%d an=%d\n"),
    430               unit, port, an));
    431     /*
    432      * Special case for BCM5421S being used in SGMII copper mode with
    433      * BCM53xxx.  Always have autoneg on.
    434      */
    435     if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_5421S)) {
    436         an = 1;
    437     }
    438 
    439     par_det   = 0;
    440     an_enable = 0; 
    441     auto_det  = 0;
    442     if (an) {
    443         par_det = QS_1000X_PAR_DET_EN;
    444         an_enable = QS_MII_CTRL_AN_ENABLE | QS_MII_CTRL_AN_RESTART;
    445         if (soc_property_port_get(unit, port,
    446                                   spn_SERDES_AUTOMEDIUM, FALSE)) {
    447             auto_det = QS_1000X_AUTO_DETECT; 
    448         }
    449     }
    450 
    451     SOC_IF_ERROR_RETURN
    452         (MODIFY_SERDES_1000X_CTRL2r(unit, pc,
    453                         par_det, QS_1000X_PAR_DET_EN));
    454 
    455     SOC_IF_ERROR_RETURN
    456         (MODIFY_SERDES_1000X_CTRL1r(unit, pc, 
    457                         auto_det, QS_1000X_AUTO_DETECT));
    458 
    459 
    460     /* Enable/Disable and Restart autonegotiation (self-clearing bit) */
    461     SOC_IF_ERROR_RETURN
    462         (MODIFY_SERDES_MII_CTRLr(unit, pc, an_enable,
    463                         QS_MII_CTRL_AN_ENABLE | QS_MII_CTRL_AN_RESTART));
    464 
    465     pc->fiber.autoneg_enable = an;
    466 
    467     return SOC_E_NONE;
    468 }
    469 
    470 /*
    471  * Function:    
    472  *      phy_53xxx_adv_local_set
    473  * Purpose:     
    474  *      Set the current advertisement for auto-negotiation.
    475  * Parameters:
    476  *      unit - Switch unit #.
    477  *      port - Switch port #. 
    478  *      mode - Port mode mask indicating supported options/speeds.
    479  * Returns:     
    480  *      SOC_E_XXX
    481  */
    482 STATIC int 
    483 phy_53xxx_adv_local_set(int unit, soc_port_t port, soc_port_mode_t mode)
    484 {
    485     return phy_serdes_adv_local_set(unit, port, mode);
    486 }
    487 
    488 /*
    489  * Function:    
    490  *      phy_53xxx_adv_local_get
    491  * Purpose:     
    492  *      Get the current advertisement for auto-negotiation.
    493  * Parameters:
    494  *      unit - Switch unit #.
    495  *      port - Switch port #. 
    496  *      mode - (OUT) Port mode mask indicating supported options/speeds.
    497  * Returns:     
    498  *      SOC_E_XXX
    499  */
    500 
    501 STATIC int
    502 phy_53xxx_adv_local_get(int unit, soc_port_t port, soc_port_mode_t *mode)
    503 {
    504     return phy_serdes_adv_local_get(unit, port, mode);
    505 }
    506 
    507 /*
    508  * Function:    
    509  *      phy_53xxx_adv_remote_get
    510  * Purpose:     
    511  *      Get partner's current advertisement for auto-negotiation.
    512  * Parameters:
    513  *      unit - Switch unit #.
    514  *      port - Switch port #. 
    515  *      mode - (OUT) Port mode mask indicating supported options/speeds.
    516  * Returns:
    517  *      SOC_E_XXX
    518  */
    519 
    520 STATIC int
    521 phy_53xxx_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode)
    522 { 
    523     return phy_serdes_adv_remote_get(unit, port, mode);
    524 }
    525 
    526 /*
    527  * Function:    
    528  *      phy_53xxx_lb_set
    529  * Purpose:     
    530  *      Set the internal PHY loopback mode.
    531  * Parameters:
    532  *      unit - Switch unit #.
    533  *      port - Switch port #. 
    534  *      loopback - Boolean: true = enable loopback, false = disable.
    535  * Returns:     
    536  *      SOC_E_XXX
    537  */
    538 
    539 STATIC int
    540 phy_53xxx_lb_set(int unit, soc_port_t port, int enable)
    541 {
    542     uint16       ctrl;
    543     phy_ctrl_t  *pc;
    544     int          rv;
    545 
    546     pc = INT_PHY_SW_STATE(unit, port);
    547 
    548     ctrl = (enable) ? QS_MII_CTRL_LOOPBACK : 0;
    549 
    550     rv = MODIFY_SERDES_MII_CTRLr(unit, pc, ctrl, QS_MII_CTRL_LOOPBACK);
    551 
    552     LOG_INFO(BSL_LS_SOC_PHY,
    553              (BSL_META_U(unit,
    554                          "phy_53xxx_lb_set: u=%d p=%d lb=%d rv=%d\n"),
    555               unit, port, enable, rv));
    556     return rv;
    557 }
    558 
    559 /*
    560  * Function:    
    561  *      phy_53xxx_ability_get
    562  * Purpose:     
    563  *      Get the abilities of the internal PHY.
    564  * Parameters:
    565  *      unit - Switch unit #.
    566  *      port - Switch port #. 
    567  *      mode - (OUT) Port mode mask indicating supported options/speeds.
    568  * Returns:     
    569  *      SOC_E_XXX
    570  */
    571 
    572 STATIC int
    573 phy_53xxx_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode)
    574 {
    575     uint16      fiber_status;
    576     phy_ctrl_t *pc;
    577     pc = INT_PHY_SW_STATE(unit, port);
    578 
    579     SOC_IF_ERROR_RETURN
    580         (READ_SERDES_1000X_STAT1r(unit, pc, &fiber_status));
    581 
    582     if (fiber_status & QS_1000X_STATUS1_SGMII_MODE) {
    583         *mode = (SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB_FD | 
    584                  SOC_PM_LB_PHY | SOC_PM_GMII);
    585     } else {
    586         *mode = (SOC_PM_1000MB_FD | SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM |
    587                  SOC_PM_AN | SOC_PM_LB_PHY | SOC_PM_GMII);
    588     }
    589 
    590     return SOC_E_NONE;
    591 }
    592 
    593 /*
    594  * Function:
    595  *      _phy_53xxx_medium_config_update
    596  * Purpose:
    597  *      Update the PHY with config parameters
    598  * Parameters:
    599  *      unit - Switch unit #.
    600  *      port - Switch port #.
    601  *      cfg - Config structure.
    602  * Returns:
    603  *      SOC_E_XXX
    604  */
    605 
    606 STATIC int
    607 _phy_53xxx_medium_config_update(int unit, soc_port_t port,
    608                                 soc_phy_config_t *cfg)
    609 {
    610     SOC_IF_ERROR_RETURN
    611         (phy_53xxx_speed_set(unit, port, cfg->force_speed));
    612     SOC_IF_ERROR_RETURN
    613         (phy_serdes_duplex_set(unit, port, cfg->force_duplex));
    614     SOC_IF_ERROR_RETURN
    615         (phy_53xxx_adv_local_set(unit, port, cfg->autoneg_advert));
    616     SOC_IF_ERROR_RETURN
    617         (phy_53xxx_an_set(unit, port, cfg->autoneg_enable));
    618 
    619     return SOC_E_NONE;
    620 }
    621 
    622 /*
    623  * Function:
    624  *      phy_53xxx_medium_config_set
    625  * Purpose:
    626  *      Set the operating parameters that are automatically selected
    627  *      when medium switches type.
    628  * Parameters:
    629  *      unit - Switch unit #
    630  *      port - Port number
    631  *      medium - SOC_PORT_MEDIUM_COPPER/FIBER
    632  *      cfg - Operating parameters
    633  * Returns:
    634  *      SOC_E_XXX
    635  */
    636 
    637 STATIC int
    638 phy_53xxx_medium_config_set(int unit, soc_port_t port, 
    639                            soc_port_medium_t  medium,
    640                            soc_phy_config_t  *cfg)
    641 {
    642     phy_ctrl_t    *pc;
    643 
    644     pc = INT_PHY_SW_STATE(unit, port);
    645 
    646     /*
    647      * Changes take effect immediately if the target medium is active.
    648      */
    649     switch (medium) {
    650     case SOC_PORT_MEDIUM_FIBER:
    651         /* Change the fiber modes */
    652         sal_memcpy(&pc->fiber, cfg, sizeof (pc->fiber));
    653 
    654         pc->fiber.autoneg_advert &= ADVERT_ALL_FIBER;
    655         if (PHY_FIBER_MODE(unit, port)) {
    656             SOC_IF_ERROR_RETURN
    657                 (_phy_53xxx_medium_config_update(unit, port, &pc->fiber));
    658         }
    659         return SOC_E_NONE;
    660     case SOC_PORT_MEDIUM_COPPER:
    661         return SOC_E_UNAVAIL;
    662     default:
    663         return SOC_E_PARAM;
    664     }
    665 }
    666 
    667 /*
    668  * Function:
    669  *      phy_53xxx_reg_read
    670  * Purpose:
    671  *      Routine to read PHY register
    672  * Parameters:
    673  *      uint         - BCM unit number
    674  *      pc           - PHY state
    675  *      flags        - Flags which specify the register type
    676  *      phy_reg_addr - Encoded register address
    677  *      phy_data     - (OUT) Value read from PHY register
    678  * Note:
    679  *      This register read function is not thread safe. Higher level
    680  * function that calls this function must obtain a per port lock
    681  * to avoid overriding register page mapping between threads.
    682  */
    683 STATIC int
    684 phy_53xxx_reg_read(int unit, soc_port_t port, uint32 flags,
    685                   uint32 phy_reg_addr, uint32 *phy_data)
    686 {
    687     uint16               reg_bank;
    688     uint8                reg_addr;
    689     uint16               data;     /* Temporary holder for phy_data */
    690     phy_ctrl_t          *pc;      /* PHY software state */
    691 
    692 
    693     pc = INT_PHY_SW_STATE(unit, port);
    694 
    695     reg_bank  = SOC_PHY_REG_BANK(phy_reg_addr);
    696     reg_addr  = SOC_PHY_REG_ADDR(phy_reg_addr);
    697 
    698     SOC_IF_ERROR_RETURN
    699         (phy_reg_serdes_read(unit, pc, reg_bank, reg_addr, &data));
    700 
    701    *phy_data = data;
    702 
    703     return SOC_E_NONE;
    704 }
    705 
    706 /*
    707  * Function:
    708  *      phy_53xxx_reg_write
    709  * Purpose:
    710  *      Routine to write PHY register
    711  * Parameters:
    712  *      uint         - BCM unit number
    713  *      pc           - PHY state
    714  *      flags        - Flags which specify the register type
    715  *      phy_reg_addr - Encoded register address
    716  *      phy_data     - Value write to PHY register
    717  * Note:
    718  *      This register read function is not thread safe. Higher level
    719  * function that calls this function must obtain a per port lock
    720  * to avoid overriding register page mapping between threads.
    721  */
    722 STATIC int
    723 phy_53xxx_reg_write(int unit, soc_port_t port, uint32 flags,
    724                    uint32 phy_reg_addr, uint32 phy_data)
    725 {
    726     uint16               reg_bank;
    727     uint8                reg_addr;
    728     uint16               data;     /* Temporary holder for phy_data */
    729     phy_ctrl_t          *pc;      /* PHY software state */
    730 
    731     pc = INT_PHY_SW_STATE(unit, port);
    732 
    733     data      = (uint16) (phy_data & 0x0000FFFF);
    734     reg_bank  = SOC_PHY_REG_BANK(phy_reg_addr);
    735     reg_addr  = SOC_PHY_REG_ADDR(phy_reg_addr);
    736 
    737     SOC_IF_ERROR_RETURN
    738         (phy_reg_serdes_write(unit, pc, reg_bank, reg_addr, data));
    739 
    740     return SOC_E_NONE;
    741 }
    742 
    743 /*
    744  * Function:
    745  *      phy_53xxx_reg_modify
    746  * Purpose:
    747  *      Routine to write PHY register
    748  * Parameters:
    749  *      uint         - BCM unit number
    750  *      pc           - PHY state
    751  *      flags        - Flags which specify the register type
    752  *      phy_reg_addr - Encoded register address
    753  *      phy_mo_data  - New value for the bits specified in phy_mo_mask
    754  *      phy_mo_mask  - Bit mask to modify
    755  * Note:
    756  *      This register read function is not thread safe. Higher level
    757  * function that calls this function must obtain a per port lock
    758  * to avoid overriding register page mapping between threads.
    759  */
    760 STATIC int
    761 phy_53xxx_reg_modify(int unit, soc_port_t port, uint32 flags,
    762                     uint32 phy_reg_addr, uint32 phy_data, uint32 phy_data_mask)
    763 {
    764     uint16         reg_bank;
    765     uint8          reg_addr;
    766     uint16         data;     /* Temporary holder for phy_data */
    767     uint16         mask;
    768     phy_ctrl_t    *pc;       /* PHY software state */
    769 
    770     pc = INT_PHY_SW_STATE(unit, port);
    771 
    772     data      = (uint16) (phy_data & 0x0000FFFF);
    773     mask      = (uint16) (phy_data_mask & 0x0000FFFF);
    774     reg_bank  = SOC_PHY_REG_BANK(phy_reg_addr);
    775     reg_addr  = SOC_PHY_REG_ADDR(phy_reg_addr);
    776 
    777     SOC_IF_ERROR_RETURN
    778         (phy_reg_serdes_modify(unit, pc, reg_bank, reg_addr, data, mask));
    779 
    780     return SOC_E_NONE;
    781 }
    782 
    783 STATIC int
    784 phy_53xxx_notify(int unit, soc_port_t port, 
    785                  soc_phy_event_t event, uint32 value)
    786 {
    787     int             rv;
    788 
    789     if (event >= phyEventCount) {
    790         return SOC_E_PARAM;
    791     }
    792 
    793     rv = SOC_E_NONE;
    794     switch(event) {
    795     case phyEventInterface:
    796         rv = (_phy_53xxx_notify_interface(unit, port, value));
    797         break;
    798     case phyEventDuplex:
    799         rv = (_phy_53xxx_notify_duplex(unit, port, value));
    800         break; 
    801     case phyEventSpeed:
    802         rv = (_phy_53xxx_notify_speed(unit, port, value));
    803         break; 
    804     case phyEventStop:
    805         rv = (_phy_53xxx_notify_stop(unit, port, value));
    806         break; 
    807     case phyEventResume:
    808         rv = (_phy_53xxx_notify_resume(unit, port, value));
    809         break; 
    810     default:
    811         rv = SOC_E_PARAM;
    812         break;
    813     }
    814 
    815     return rv;
    816 }
    817 
    818 
    819 /*
    820  * Variable:    phy_53xxxdrv_ge
    821  * Purpose:     PHY Driver for Dozen SerDes Serdes PHY
    822  */
    823 phy_driver_t phy_53xxxdrv_ge = {
    824     "53XXX Internal SERDES PHY Driver",
    825     phy_53xxx_init,
    826     phy_null_reset,
    827     phy_serdes_link_get, 
    828     phy_53xxx_enable_set, 
    829     phy_null_enable_get, 
    830     phy_serdes_duplex_set, 
    831     phy_null_one_get,              /* duplex_get */
    832     phy_53xxx_speed_set, 
    833     phy_53xxx_speed_get, 
    834     phy_serdes_master_set,          /* master_set */
    835     phy_serdes_master_get,          /* master_get */
    836     phy_53xxx_an_set,
    837     phy_serdes_an_get,
    838     phy_53xxx_adv_local_set,
    839     phy_53xxx_adv_local_get,
    840     phy_53xxx_adv_remote_get,
    841     phy_53xxx_lb_set,
    842     phy_serdes_lb_get,
    843     phy_serdes_interface_set, 
    844     phy_serdes_interface_get, 
    845     phy_53xxx_ability_get,
    846     NULL,
    847     NULL,
    848     phy_null_mdix_set,
    849     phy_null_mdix_get,
    850     phy_null_mdix_status_get,
    851     phy_53xxx_medium_config_set,
    852     phy_serdes_medium_config_get,
    853     phy_serdes_medium_status,
    854     NULL,                          /* phy_cable_diag  */
    855     NULL,                          /* phy_link_change */
    856     NULL,         /* phy_control_set */
    857     NULL,         /* phy_control_get */
    858     phy_53xxx_reg_read,
    859     phy_53xxx_reg_write,
    860     phy_53xxx_reg_modify,
    861     phy_53xxx_notify
    862 };
    863 
    864 /***********************************************************************
    865  *
    866  * PASS-THROUGH NOTIFY ROUTINES
    867  */
    868 
    869 /*
    870  * Function:
    871  *      phy_53xxx_notify_duplex
    872  * Purpose:     
    873  *      Program duplex if (and only if) 53xxx is an intermediate PHY.
    874  * Parameters:
    875  *      unit - unit #.
    876  *      port - port #. 
    877  *      duplex - Boolean, TRUE indicates full duplex, FALSE
    878  *              indicates half.
    879  * Returns:     
    880  *      SOC_E_XXX
    881  * Notes:
    882  *      If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to
    883  *      talk directly to an external fiber module.
    884  *
    885  *      If PHY_FLAGS_FIBER is clear, the PHY is being used in
    886  *      pass-through mode to talk to an external SGMII PHY.
    887  *
    888  *      When used in SGMII mode, autoneg must be turned off and
    889  *      the speed/duplex forced to match that of the external PHY.
    890  */
    891 STATIC int
    892 _phy_53xxx_notify_duplex(int unit, soc_port_t port, uint32 duplex)
    893 {
    894     int                 fiber;
    895     uint16              mii_ctrl;
    896     phy_ctrl_t  *pc;
    897 
    898     fiber = PHY_FIBER_MODE(unit, port);
    899     pc    = INT_PHY_SW_STATE(unit, port);
    900     LOG_INFO(BSL_LS_SOC_PHY,
    901              (BSL_META_U(unit,
    902                          "phy_53xxx_notify_duplex: u=%d p=%d duplex=%d fiber=%d\n"),
    903               unit, port, duplex, fiber));
    904 
    905     if (SAL_BOOT_SIMULATION) {
    906         return SOC_E_NONE;
    907     }
    908 
    909     if (fiber) {
    910         SOC_IF_ERROR_RETURN
    911             (MODIFY_SERDES_MII_CTRLr(unit, pc, 
    912                                    QS_MII_CTRL_FD, QS_MII_CTRL_FD));
    913         return SOC_E_NONE;
    914     }
    915 
    916     /* Put SERDES PHY in reset */
    917 
    918     SOC_IF_ERROR_RETURN
    919         (_phy_53xxx_notify_stop(unit, port, PHY_STOP_DUPLEX_CHG));
    920 
    921     /* Update duplexity */
    922     mii_ctrl = (duplex) ? QS_MII_CTRL_FD : 0;
    923     SOC_IF_ERROR_RETURN
    924         (MODIFY_SERDES_MII_CTRLr(unit, pc, mii_ctrl, QS_MII_CTRL_FD)); 
    925 
    926     /* Take SERDES PHY out of reset */
    927     SOC_IF_ERROR_RETURN
    928         (_phy_53xxx_notify_resume(unit, port, PHY_STOP_DUPLEX_CHG));
    929 
    930     /* Autonegotiation must be turned off to talk to external PHY if
    931      * SGMII autoneg is not enabled.
    932      */
    933     if (!PHY_SGMII_AUTONEG_MODE(unit, port)) {
    934         SOC_IF_ERROR_RETURN
    935             (phy_53xxx_an_set(unit, port, FALSE));
    936     }
    937     return SOC_E_NONE;
    938 }
    939 
    940 /*
    941  * Function:
    942  *      phy_53xxx_notify_speed
    943  * Purpose:     
    944  *      Program duplex if (and only if) 53xxx is an intermediate PHY.
    945  * Parameters:
    946  *      unit - unit #.
    947  *      port - port #. 
    948  *      speed - Speed to program.
    949  * Returns:     
    950  *      SOC_E_XXX
    951  * Notes:
    952  *      If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to
    953  *      talk directly to an external fiber module.
    954  *
    955  *      If PHY_FLAGS_FIBER is clear, the PHY is being used in
    956  *      pass-through mode to talk to an external SGMII PHY.
    957  *
    958  *      When used in pass-through mode, autoneg must be turned off and
    959  *      the speed/duplex forced to match that of the external PHY.
    960  */
    961 
    962 STATIC int
    963 _phy_53xxx_notify_speed(int unit, soc_port_t port, uint32 speed)
    964 {
    965     int          fiber;
    966     uint16       fiber_status;
    967     phy_ctrl_t  *pc;
    968 
    969     pc    = INT_PHY_SW_STATE(unit, port);
    970     fiber = PHY_FIBER_MODE(unit, port);
    971 
    972     if (SAL_BOOT_SIMULATION) {
    973         return SOC_E_NONE;
    974     }
    975  
    976     SOC_IF_ERROR_RETURN
    977         (READ_SERDES_1000X_STAT1r(unit, pc,
    978                          &fiber_status));
    979 
    980     if (fiber && !(fiber_status & QS_1000X_STATUS1_SGMII_MODE)) {
    981         if ((speed != 0) && (speed != 1000)) {
    982             return SOC_E_CONFIG;
    983         }
    984     }
    985 
    986     /* Put SERDES PHY in reset */
    987     SOC_IF_ERROR_RETURN
    988         (_phy_53xxx_notify_stop(unit, port, PHY_STOP_SPEED_CHG));
    989 
    990     /* Update speed */
    991     SOC_IF_ERROR_RETURN
    992         (_phy_53xxx_sgmii_speed_set(unit, port, (int)speed));
    993 
    994     /* Take SERDES PHY out of reset */
    995     SOC_IF_ERROR_RETURN
    996         (_phy_53xxx_notify_resume(unit, port, PHY_STOP_SPEED_CHG));
    997 
    998     /* Autonegotiation must be turned off to talk to external PHY */
    999     if (!PHY_SGMII_AUTONEG_MODE(unit, port) && !fiber) {
   1000         SOC_IF_ERROR_RETURN
   1001             (phy_53xxx_an_set(unit, port, FALSE));
   1002     }
   1003 
   1004     return SOC_E_NONE;
   1005 }
   1006 
   1007 /*
   1008  * Function:
   1009  *      _phy_53xxx_stop
   1010  * Purpose:
   1011  *      Put 53xxx SERDES in or out of reset depending on conditions
   1012  */
   1013 
   1014 STATIC int
   1015 _phy_53xxx_stop(int unit, soc_port_t port)
   1016 {
   1017     uint16              mii_ctrl;
   1018     int                 stop, copper;
   1019     phy_ctrl_t  *pc;
   1020 
   1021     pc = INT_PHY_SW_STATE(unit, port);
   1022 
   1023     copper = (pc->stop & 
   1024               PHY_STOP_COPPER) != 0;
   1025 
   1026     stop = ((pc->stop &
   1027              (PHY_STOP_PHY_DIS |
   1028               PHY_STOP_DRAIN)) != 0 ||
   1029             (copper &&
   1030              (pc->stop &
   1031               (PHY_STOP_MAC_DIS |
   1032                PHY_STOP_DUPLEX_CHG |
   1033                PHY_STOP_SPEED_CHG)) != 0));
   1034 
   1035     LOG_INFO(BSL_LS_SOC_PHY,
   1036              (BSL_META_U(unit,
   1037                          "phy_53xxx_stop: u=%d p=%d copper=%d stop=%d flg=0x%x\n"),
   1038               unit, port, copper, stop,
   1039               pc->stop));
   1040 
   1041     mii_ctrl = (stop) ? QS_MII_CTRL_PD : 0;
   1042     SOC_IF_ERROR_RETURN
   1043         (MODIFY_SERDES_MII_CTRLr(unit, pc, mii_ctrl, QS_MII_CTRL_PD));
   1044 
   1045     return SOC_E_NONE; 
   1046 }
   1047 
   1048 /*
   1049  * Function:
   1050  *      phy_53xxx_notify_stop
   1051  * Purpose:
   1052  *      Add a reason to put 53xxx PHY in reset.
   1053  * Parameters:
   1054  *      unit - unit #.
   1055  *      port - port #.
   1056  *      flags - Reason to stop
   1057  * Returns:
   1058  *      SOC_E_XXX
   1059  */
   1060 STATIC int
   1061 _phy_53xxx_notify_stop(int unit, soc_port_t port, uint32 flags)
   1062 {
   1063     phy_ctrl_t  *pc;
   1064 
   1065     pc = INT_PHY_SW_STATE(unit, port);
   1066 
   1067     pc->stop |= flags;
   1068 
   1069     return _phy_53xxx_stop(unit, port);
   1070 }
   1071 
   1072 /*
   1073  * Function:
   1074  *      phy_53xxx_notify_resume
   1075  * Purpose:
   1076  *      Remove a reason to put 53xxx PHY in reset.
   1077  * Parameters:
   1078  *      unit - unit #.
   1079  *      port - port #.
   1080  *      flags - Reason to stop
   1081  * Returns:
   1082  *      SOC_E_XXX
   1083  */
   1084 STATIC int
   1085 _phy_53xxx_notify_resume(int unit, soc_port_t port, uint32 flags)
   1086 {
   1087     phy_ctrl_t  *pc;
   1088 
   1089     pc = INT_PHY_SW_STATE(unit, port);
   1090 
   1091     pc->stop &= ~flags;
   1092 
   1093     return _phy_53xxx_stop(unit, port);
   1094 }
   1095 
   1096 
   1097 STATIC int 
   1098 _phy_53xxx_notify_interface(int unit, soc_port_t port, uint32 intf)
   1099 {
   1100     phy_ctrl_t  *pc;
   1101     uint16       data16;
   1102 
   1103     pc = INT_PHY_SW_STATE(unit, port);
   1104 
   1105     data16 = (intf == SOC_PORT_IF_SGMII) ? 0 : QS_1000X_FIBER_MODE; 
   1106     SOC_IF_ERROR_RETURN
   1107         (MODIFY_SERDES_1000X_CTRL1r(unit, pc, data16, QS_1000X_FIBER_MODE));
   1108 
   1109     return SOC_E_NONE;
   1110 }
   1111 
   1112 
   1113 #else /* INCLUDE_PHY_53XXX */
   1114 int _phy_53xxx_not_empty;
   1115 #endif /* INCLUDE_PHY_53XXX */
   1116