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

phy56xxx.c (43866B)


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