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

phy56xxx5601x.c (53954B)


      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_5601x.c
      9  * Purpose:
     10  *      Fiber driver for 56xxx_5601x using internal Serdes PHY with shadow
     11  *      registers.
     12  *
     13  * For BCM56504, the internal GMACC core of the GPORT should always be
     14  * configured to run in SGMII mode.  TBI mode is not supported between
     15  * the GMACC and the internal SERDES module.
     16  * 
     17  * When operating with an external PHY, this driver is not used.
     18  * However the speed/duplex of the internal PHY must be programmed to
     19  * match the MAC and external PHY settings so the data can pass through.
     20  * This file supplies some routines to allow mac.c to accomplish this
     21  * (think of the internal PHY as part of the MAC in this case):
     22  *
     23  *      phy_56xxx_5601x_notify_duplex
     24  *      phy_56xxx_5601x_notify_speed
     25  *      phy_56xxx_5601x_notify_stop
     26  *      phy_56xxx_5601x_notify_resume
     27  *
     28  * CMIC MIIM operations can be performed to the internal register set
     29  * using internal MDIO address (PORT_TO_PHY_ADDR_INT), and an external
     30  * PHY register set (such as BCM5424/34/64) can be programmed using the
     31  * external MDIO address (PORT_TO_PHY_ADDR).
     32  *
     33  * MDIO accesses to the internal PHY are not modeled on Quickturn.
     34  */
     35 
     36 #include <sal/types.h>
     37 #include <sal/core/spl.h>
     38 #include <shared/bsl.h>
     39 #include <soc/drv.h>
     40 #include <soc/debug.h>
     41 #include <soc/error.h>
     42 #include <soc/phyreg.h>
     43 
     44 #include <soc/phy.h>
     45 #include <soc/phy/phyctrl.h>
     46 #include <soc/phy/drv.h>
     47 
     48 #include "phydefs.h"      /* Must include before other phy related includes */
     49 
     50 #if defined(INCLUDE_PHY_56XXX)
     51 #include "phyconfig.h"    /* Must be the first phy include after phydefs.h */
     52 
     53 #include "phyident.h"
     54 #include "phyreg.h"
     55 #include "phynull.h"
     56 #include "serdes.h"
     57 #include "physr.h"
     58 #include "phy56xxx5601x.h"
     59 
     60 #ifdef INCLUDE_PHY_XGXS6
     61 #include "xgxs6.h"
     62 #endif /*INCLUDE_PHY_XGXS6 */
     63 
     64 #define ADVERT_ALL_FIBER \
     65             (SOC_PM_PAUSE | SOC_PM_1000MB_FD)
     66 
     67 /* Notify event forward declaration */
     68 STATIC int _phy_56xxx_5601x_notify_duplex(int unit, soc_port_t port, uint32 duplex);
     69 STATIC int _phy_56xxx_5601x_notify_speed(int unit, soc_port_t port, uint32 speed);
     70 STATIC int _phy_56xxx_5601x_notify_stop(int unit, soc_port_t port, uint32 flags);
     71 STATIC int _phy_56xxx_5601x_notify_resume(int unit, soc_port_t port, uint32 flags);
     72 STATIC int _phy_56xxx_5601x_notify_interface(int unit, soc_port_t port, uint32 intf);
     73 
     74 
     75 STATIC int
     76 _phy_56xxx_5601x_sregs_init(phy_ctrl_t *pc)
     77 {
     78     serdes_5601x_sregs_t *sr;
     79     if (pc->driver_data == NULL) {
     80         return SOC_E_INTERNAL;
     81     }
     82     sr = pc->driver_data;
     83 
     84     sr->mii_ctrl       = 0x1140;
     85     sr->mii_ana        = 0x01a0;
     86     sr->r1000x_ctrl1   = 0x0181;
     87     sr->r1000x_ctrl2   = 0x0000;
     88 
     89     return SOC_E_NONE;
     90 }
     91 
     92 STATIC int
     93 phy_serdes_5601x_reg_read(int unit, phy_ctrl_t *pc,  uint16 reg_bank,
     94                          uint8 reg_addr, uint16 *phy_rd_data)
     95 {
     96     serdes_5601x_sregs_t  *sr;
     97     uint16              data = 0;
     98     int                 rv = SOC_E_NONE;
     99     int                 shadow_read = 1;
    100 
    101 #ifdef INCLUDE_PHY_XGXS6
    102     if (soc_feature(unit, soc_feature_xgxs_v6)) {
    103         if ((reg_bank != 0) || (reg_addr >= 0x0010)) {
    104             reg_bank = 0x300 + (reg_bank << 4);
    105         }
    106     }
    107 #endif /* INCLUDE_PHY_XGXS6 */
    108 
    109     /* Read shadow registers if shadowed register */
    110     sr = pc->driver_data;
    111     if (sr == NULL) {
    112         return SOC_E_INTERNAL;
    113     }
    114     switch (reg_bank) {
    115     case 0:
    116         switch (reg_addr) {
    117         case 0:
    118             data = sr->mii_ctrl;
    119             break;
    120         case 4:
    121             data = sr->mii_ana;
    122             break;
    123         case 0x10:
    124             data = sr->r1000x_ctrl1;
    125             break;
    126         case 0x11:
    127             data = sr->r1000x_ctrl2;
    128             break;
    129         default:
    130             shadow_read = 0;
    131             break;
    132         }
    133         break;
    134     default:
    135         /* Invalid block */
    136         LOG_INFO(BSL_LS_SOC_PHY,
    137                  (BSL_META_U(unit,
    138                              "phy_serdes_5601x_reg_read: unexpected read from %x*%x: %04x\n"),
    139                   reg_bank, reg_addr, data));
    140         break;
    141     }
    142 
    143     if (! shadow_read) { 
    144         /* Read real registers */
    145         rv = WRITE_PHY_REG(unit, pc, 0x1f, reg_bank); 
    146         if (SOC_SUCCESS(rv)) { 
    147             rv = READ_PHY_REG(unit, pc, reg_addr, &data);  
    148         }
    149         LOG_INFO(BSL_LS_SOC_PHY,
    150                  (BSL_META_U(unit,
    151                              "phy_serdes_5601x_reg_read: %x*%x: %04x\n"),
    152                   reg_bank, reg_addr, data));
    153     } else {
    154         LOG_INFO(BSL_LS_SOC_PHY,
    155                  (BSL_META_U(unit,
    156                              "phy_serdes_5601x_reg_read: shadow %x*%x: %04x\n"),
    157                   reg_bank, reg_addr, data));
    158     }  
    159 
    160 
    161     *phy_rd_data = data;
    162 
    163     return rv;
    164 }
    165 
    166 STATIC int
    167 phy_serdes_5601x_reg_write(int unit, phy_ctrl_t *pc, uint16 reg_bank,
    168                           uint8 reg_addr, uint16 phy_wr_data)
    169 {
    170     serdes_5601x_sregs_t  *sr;
    171     int                 rv = SOC_E_NONE;
    172     int                 shadow_write = 1;
    173 
    174 #ifdef INCLUDE_PHY_XGXS6
    175     if (soc_feature(unit, soc_feature_xgxs_v6)) {
    176         if ((reg_bank != 0) || (reg_addr >= 0x0010)) {
    177             reg_bank = 0x300 + (reg_bank << 4);
    178         }
    179     }
    180 #endif /* INCLUDE_PHY_XGXS6 */
    181 
    182     sr = pc->driver_data;
    183     if (sr == NULL) {
    184         return SOC_E_INTERNAL;
    185     }
    186 
    187     /* Write to shadow if shadowed register*/
    188     switch (reg_bank) {
    189     case 0:
    190         switch (reg_addr) {
    191         case 0:
    192             /*                                           
    193              * Do not write the reset bit and restart autoneg bits
    194              * to the shadow as these are self-cleared bits
    195              */
    196             sr->mii_ctrl = phy_wr_data & ~(MII_CTRL_RESET | MII_CTRL_RAN);
    197             break;
    198         case 4:
    199             sr->mii_ana = phy_wr_data;
    200             break;
    201         case 0x10:
    202             sr->r1000x_ctrl1 = phy_wr_data;
    203             break;
    204         case 0x11:
    205             sr->r1000x_ctrl2 = phy_wr_data;
    206             break;
    207         default:
    208             shadow_write = 0;
    209             break;
    210         }
    211         break;
    212     default:
    213         /* Invalid block */
    214         LOG_INFO(BSL_LS_SOC_PHY,
    215                  (BSL_META_U(unit,
    216                              "phy_serdes_5601x_reg_write: unexpected write to %x*%x: %04x\n"),
    217                   reg_bank, reg_addr, phy_wr_data));
    218         break;
    219     }
    220 
    221     if (shadow_write) {
    222         LOG_INFO(BSL_LS_SOC_PHY,
    223                  (BSL_META_U(unit,
    224                              "phy_serdes_5601x_reg_write: shadow %x*%x: %04x\n"),
    225                   reg_bank, reg_addr, phy_wr_data));
    226     } else {
    227         LOG_INFO(BSL_LS_SOC_PHY,
    228                  (BSL_META_U(unit,
    229                              "phy_serdes_5601x_reg_write: %x*%x: %04x\n"),
    230                   reg_bank, reg_addr, phy_wr_data));
    231     }
    232 
    233     /* Write through to real device */
    234     rv = WRITE_PHY_REG(unit, pc, 0x1f, reg_bank);
    235     if (SOC_SUCCESS(rv)) {
    236         rv = WRITE_PHY_REG(unit, pc, reg_addr, phy_wr_data);
    237     }
    238 
    239     return rv;
    240 }
    241 
    242 STATIC int
    243 phy_serdes_5601x_reg_modify(int unit, phy_ctrl_t *pc,
    244                            uint16 reg_bank, uint8 reg_addr,
    245                            uint16 phy_mo_data, uint16 phy_mo_mask)
    246 {
    247     uint16  tmp, otmp;
    248 
    249 #ifdef INCLUDE_PHY_XGXS6
    250     if (soc_feature(unit, soc_feature_xgxs_v6)) {
    251         if ((reg_bank != 0) || (reg_addr >= 0x0010)) {
    252             reg_bank = 0x300 + (reg_bank << 4);
    253         }
    254     }
    255 #endif /* INCLUDE_PHY_XGXS6 */
    256 
    257     phy_mo_data &=  phy_mo_mask;
    258     SOC_IF_ERROR_RETURN
    259         (phy_serdes_5601x_reg_read(unit, pc, reg_bank, reg_addr, &tmp));
    260     otmp = tmp;
    261     tmp &= ~(phy_mo_mask);
    262     tmp |= phy_mo_data;
    263 
    264     if (tmp != otmp) {
    265         SOC_IF_ERROR_RETURN
    266             (phy_serdes_5601x_reg_write(unit, pc, reg_bank,
    267                                        reg_addr, tmp));
    268     }
    269 
    270     return SOC_E_NONE;
    271 }
    272 
    273 
    274 /***********************************************************************
    275  *
    276  * HELPER FUNCTIONS
    277  */
    278 
    279 /*
    280  * Function:
    281  *      phy_56xxx_5601x_fiber_status_get
    282  * Purpose:
    283  *      Retrieves whether port is in fiber or SGMII
    284  * Parameters:
    285  *      unit - StrataSwitch unit #.
    286  *      pc   - PHY state object.
    287  *      fiber_status - TRUE if fiber, else SGMII
    288  * Returns:
    289  *      SOC_E_XXX
    290  */
    291 
    292 STATIC int
    293 _phy_56xxx_5601x_fiber_status_get(int unit, phy_ctrl_t *pc, uint16 *fiber_status)
    294 {
    295     uint16 fiber;
    296 
    297     SOC_IF_ERROR_RETURN
    298         (READ_SERDES_5601X_1000X_CTRL1r(unit, pc, &fiber));
    299     if (fiber & DDS_1000X_AUTO_DETECT) {
    300         /* Get status from 1000X_STAT1r */
    301         SOC_IF_ERROR_RETURN
    302             (READ_SERDES_5601X_1000X_STAT1r(unit, pc, &fiber));
    303         *fiber_status = !(fiber & DDS_1000X_STATUS1_SGMII_MODE);
    304     } else {
    305         /* Get from forced mode */
    306         *fiber_status = (fiber & DDS_1000X_FIBER_MODE);
    307     }
    308 
    309     return SOC_E_NONE;
    310 }
    311 
    312 
    313 /*
    314  * Function:
    315  *      phy_56xxx_5601x_tx_control_set
    316  * Purpose:
    317  *      Set preemphasis, driver current, and predriver current values 
    318  * Parameters:
    319  *      unit - StrataSwitch unit #.
    320  *      pc   - PHY state object.
    321  *      port - StrataSwitch port #.
    322  * Returns:
    323  *      SOC_E_NONE
    324  */
    325 
    326 STATIC int
    327 _phy_56xxx_5601x_tx_control_set(int unit, phy_ctrl_t *pc,
    328                           soc_port_t port)
    329 {
    330     uint32 preemph;
    331     uint32 idriver;
    332     uint32 pdriver;
    333     uint16 data16;
    334     int    fiber;
    335 
    336     fiber = PHY_FIBER_MODE(unit, port);
    337 
    338 #ifdef INCLUDE_PHY_XGXS6
    339     if (soc_feature(unit, soc_feature_xgxs_v6)) {
    340          /* Unicore */
    341          /* Preemphasis overrided in gxmac.c */
    342         return SOC_E_NONE;
    343     } else 
    344 #endif /* INCLUDE_PHY_XGXS6 */
    345     {
    346         /* Default values for Dozen SerDes */
    347         if (fiber) {
    348             idriver = 0xf;
    349             pdriver = 0x7;
    350             preemph = 0x0;
    351         } else {
    352             idriver = 0xc;
    353             pdriver = 0x7;
    354             preemph = 0x0;
    355         }
    356     }
    357 
    358     /* Read config override */
    359     preemph = soc_property_port_get(unit, port, 
    360                                     spn_SERDES_PREEMPHASIS, preemph);
    361     idriver = soc_property_port_get(unit, port,
    362                                     spn_SERDES_DRIVER_CURRENT, idriver);
    363     pdriver = soc_property_port_get(unit, port,
    364                                     spn_SERDES_PRE_DRIVER_CURRENT, pdriver);
    365 
    366     /* Dozen SerDes 
    367      * preemph[8:6], idriver[15:12], pdriver[11:9]
    368      */
    369     preemph = (preemph << 6) & 0x01c0;
    370     idriver = (idriver << 12) & 0xf000;
    371     pdriver = (pdriver << 9) & 0x0e00;
    372     data16  = (uint16)(preemph | idriver | pdriver);
    373 
    374     /* If amplitude override is not enabled, the amplitude value is 
    375      * automatically adjusted when interface changes between SGMII and 
    376      * fiber(1000X).
    377      */ 
    378     SOC_IF_ERROR_RETURN
    379         (MODIFY_SERDES_5601X_1000X_CTRL1r(unit, pc,
    380               DDS_1000X_TX_AMPLITUDE_OVRD, DDS_1000X_TX_AMPLITUDE_OVRD)); 
    381 
    382     /* Set ANALOG_TX with recommended amplitude */ 
    383     SOC_IF_ERROR_RETURN
    384         (MODIFY_SERDES_5601X_ANALOG_TXr(unit, pc, data16, 0xffc0));
    385 
    386     return SOC_E_NONE;
    387 }
    388 
    389 /***********************************************************************
    390  *
    391  * FIBER DRIVER ROUTINES
    392  */
    393 STATIC int
    394 phy_56xxx_5601x_init_no_reset(int unit, soc_port_t port)
    395 {
    396     uint16             data16, mask16;
    397     soc_timeout_t      to;
    398     phy_ctrl_t        *pc;
    399     int                rv;
    400 
    401     pc                       = INT_PHY_SW_STATE(unit, port);
    402     pc->fiber.enable         = PHY_FIBER_MODE(unit, port);
    403     pc->fiber.preferred      = PHY_FIBER_MODE(unit, port);
    404     pc->fiber.autoneg_enable = TRUE;
    405     pc->fiber.autoneg_advert = ADVERT_ALL_FIBER;
    406     pc->fiber.force_speed    = 1000;
    407     pc->fiber.force_duplex   = TRUE;
    408     pc->fiber.master         = SOC_PORT_MS_NONE;
    409     pc->fiber.mdix           = SOC_PORT_MDIX_NORMAL;
    410 
    411    SOC_IF_ERROR_RETURN(_phy_56xxx_5601x_sregs_init(pc));
    412 
    413     /* Since phy56xxx_5601x driver reset function vector does not reset
    414      * the SerDes, reset the SerDes in initialization first. 
    415      * The internal SERDES PHY's reset bit is self-clearing.
    416      */
    417     SOC_IF_ERROR_RETURN
    418         (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc,
    419                                MII_CTRL_RESET, MII_CTRL_RESET));
    420 
    421     soc_timeout_init(&to, 10000, 0);
    422     while (!soc_timeout_check(&to)) {
    423         rv = READ_SERDES_5601X_MII_CTRLr(unit, pc, &data16);
    424         if (((data16 & MII_CTRL_RESET) == 0) || SOC_FAILURE(rv)) {
    425             break;
    426         }
    427     }
    428     if ((data16 & MII_CTRL_RESET) != 0) {
    429         LOG_WARN(BSL_LS_SOC_PHY,
    430                  (BSL_META_U(unit,
    431                              "Combo SerDes reset failed: u=%d p=%d\n"),
    432                   unit, port));
    433     }
    434 
    435    SOC_IF_ERROR_RETURN(_phy_56xxx_5601x_sregs_init(pc));
    436 
    437     data16 = 0;
    438     mask16 = DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART;
    439     if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) ||
    440         PHY_PASSTHRU_MODE(unit, port) || 
    441         PHY_SGMII_AUTONEG_MODE(unit, port)) {
    442         data16 = DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART;
    443     }
    444     SOC_IF_ERROR_RETURN
    445         (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, data16, mask16));
    446 
    447     /* Configure default preemphasis, predriver, idriver values */
    448     SOC_IF_ERROR_RETURN
    449         (_phy_56xxx_5601x_tx_control_set(unit, pc, port));
    450 
    451     data16 = DDS_1000X_FALSE_LNK_DIS | DDS_1000X_FLT_FORCE_EN;
    452     mask16 = DDS_1000X_FALSE_LNK_DIS | DDS_1000X_FLT_FORCE_EN;
    453     SOC_IF_ERROR_RETURN
    454         (MODIFY_SERDES_5601X_1000X_CTRL2r(unit, pc, data16, mask16));
    455 
    456     data16 = 0;
    457     mask16 = DDS_1000X_AUTO_DETECT | DDS_1000X_FIBER_MODE;
    458     /*
    459      * Configure signal auto-detection between SGMII and fiber.
    460      * This detection only works if auto-negotiation is enabled.
    461      */
    462     if (soc_property_port_get(unit, port, 
    463                               spn_SERDES_AUTOMEDIUM, FALSE)) {
    464         data16 |= DDS_1000X_AUTO_DETECT;
    465     }
    466     /*
    467      * Put the Serdes in Fiber or SGMII mode
    468      */
    469     if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) ||
    470         PHY_PASSTHRU_MODE(unit, port)) {
    471         if (soc_property_port_get(unit, port,
    472                                   spn_SERDES_FIBER_PREF, TRUE)) {
    473             data16 |= DDS_1000X_FIBER_MODE;
    474         }
    475     }
    476     if (soc_property_port_get(unit, port, 
    477                               spn_SERDES_SGMII_MASTER, FALSE)) {
    478         data16 |= DDS_1000X_SGMII_MASTER;
    479         mask16 |= DDS_1000X_SGMII_MASTER;
    480     }
    481     SOC_IF_ERROR_RETURN
    482         (MODIFY_SERDES_5601X_1000X_CTRL1r(unit, pc, data16, mask16));
    483 
    484     if (PHY_COPPER_MODE(unit, port)) {
    485         /* Do not wait 10ms for sync status before a valid link is 
    486          * established when auto-negotiation is disabled.
    487          */
    488         SOC_IF_ERROR_RETURN
    489             (MODIFY_SERDES_5601X_1000X_CTRL2r(unit, pc, 0x0000, 0x0004));
    490     }
    491    
    492     LOG_INFO(BSL_LS_SOC_PHY,
    493              (BSL_META_U(unit,
    494                          "phy_56xxx_5601x_init: u=%d p=%d %s\n"),
    495               unit, port, 
    496               PHY_FIBER_MODE(unit, port) ? "Fiber" : "Copper"));
    497     return SOC_E_NONE;
    498 }
    499 
    500 
    501 /*
    502  * Function:
    503  *      phy_56xxx_5601x_init
    504  * Purpose:     
    505  *      Initialize 56xxx_5601x internal PHY driver
    506  * Parameters:
    507  *      unit - StrataSwitch unit #.
    508  *      port - StrataSwitch port #. 
    509  * Returns:     
    510  *      SOC_E_NONE
    511  * Notes:
    512  *      This function implements the bulk part of phy_56xxx_5601x_init and
    513  *      provides a method for initializing the SerDes when it is used
    514  *      as an intermediate PHY between the MAC and an external PHY. 
    515  *      In the latter case the PHY driver reset callback should be
    516  *      called only once, and this has already been done by the 
    517  *      external PHY driver init function.
    518  */
    519 
    520 STATIC int
    521 phy_56xxx_5601x_init(int unit, soc_port_t port)
    522 {
    523 
    524     LOG_INFO(BSL_LS_SOC_PHY,
    525              (BSL_META_U(unit,
    526                          "phy_56xxx_5601x_init: u=%d p=%d\n"),
    527               unit, port));
    528 
    529     if (!PHY_EXTERNAL_MODE(unit, port)) {
    530         /* PHY reset callbacks */
    531         SOC_IF_ERROR_RETURN(soc_phy_reset(unit, port));
    532 
    533         if (soc_property_port_get(unit, port,
    534                                   spn_SERDES_FIBER_PREF, 1)) {
    535             PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
    536         } else {
    537             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER);
    538         }
    539 
    540     }
    541     SOC_IF_ERROR_RETURN
    542         (phy_56xxx_5601x_init_no_reset(unit, port));
    543 
    544     return SOC_E_NONE;
    545 }
    546 
    547 /*
    548  * Function:    
    549  *      phy_56xxx_5601x_enable_set
    550  * Purpose:     
    551  *      Enable or disable the physical interface.
    552  * Parameters:
    553  *      unit - StrataSwitch unit #.
    554  *      port - StrataSwitch port #. 
    555  *      enable - Boolean, true = enable PHY, false = disable.
    556  * Returns:     
    557  *      SOC_E_XXX
    558  * Notes:
    559  *      The PHY is held in reset when disabled.  Also, the
    560  *      MISC_CONTROL.IDDQ bit must be set to 1 so the remote partner
    561  *      sees link down.
    562  */
    563 
    564 STATIC int
    565 phy_56xxx_5601x_enable_set(int unit, soc_port_t port, int enable)
    566 {
    567     LOG_INFO(BSL_LS_SOC_PHY,
    568              (BSL_META_U(unit,
    569                          "phy_56xxx_5601x_enable_set: u=%d p=%d en=%d\n"),
    570               unit, port, enable));
    571     if (enable) {
    572         PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE);
    573 
    574         SOC_IF_ERROR_RETURN
    575             (_phy_56xxx_5601x_notify_resume(unit, port, PHY_STOP_PHY_DIS));
    576     } else {
    577         PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE);
    578 
    579         SOC_IF_ERROR_RETURN
    580             (_phy_56xxx_5601x_notify_stop(unit, port, PHY_STOP_PHY_DIS));
    581     }
    582 
    583     return SOC_E_NONE;
    584 }
    585 
    586 STATIC int
    587 _phy_56xxx_5601x_sgmii_speed_set(int unit, soc_port_t port, int speed)
    588 {
    589     uint16 mii_ctrl;
    590     phy_ctrl_t  *pc;
    591 
    592     pc = INT_PHY_SW_STATE(unit, port);
    593 
    594     switch (speed) {
    595         case 10:
    596             mii_ctrl = DDS_MII_CTRL_SS_10;
    597             break;
    598         case 100:
    599             mii_ctrl = DDS_MII_CTRL_SS_100;
    600             break;
    601         case 0:
    602         case 1000:  
    603             mii_ctrl = DDS_MII_CTRL_SS_1000;
    604             break;
    605         default:
    606             return SOC_E_CONFIG;
    607     } 
    608     SOC_IF_ERROR_RETURN
    609         (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, mii_ctrl,
    610                                (DDS_MII_CTRL_SS_LSB | DDS_MII_CTRL_SS_MSB)));
    611     return SOC_E_NONE;
    612 }
    613 
    614 STATIC int
    615 _phy_56xxx_5601x_1000x_speed_set(int unit, soc_port_t port, int speed)
    616 {
    617     if ((speed !=0) && (speed != 1000)) {
    618         return SOC_E_CONFIG;
    619     } 
    620 
    621     return SOC_E_NONE;
    622 }
    623 
    624 /*
    625  * Function:    
    626  *      phy_56xxx_5601x_speed_set
    627  * Purpose:     
    628  *      Set the current operating speed (forced).
    629  * Parameters:
    630  *      unit - StrataSwitch unit #.
    631  *      port - StrataSwitch port #. 
    632  *      duplex - (OUT) Boolean, true indicates full duplex, false 
    633  *              indicates half.
    634  * Returns:     
    635  *      SOC_E_XXX
    636  */
    637 
    638 STATIC int
    639 phy_56xxx_5601x_speed_set(int unit, soc_port_t port, int speed)
    640 {
    641     uint16       fiber_status;
    642     int          rv;
    643     phy_ctrl_t  *pc;
    644 
    645     pc = INT_PHY_SW_STATE(unit, port);
    646     rv = SOC_E_NONE;
    647 
    648 #ifdef INCLUDE_PHY_XGXS6
    649     if (soc_feature(unit, soc_feature_xgxs_v6)) {
    650         SOC_IF_ERROR_RETURN
    651             (phy_xgxs6_hg.pd_speed_set(unit, port, speed));
    652         pc->fiber.force_speed = speed;
    653         return SOC_E_NONE;
    654     }
    655 #endif /* INCLUDE_PHY_XGXS6 */
    656 
    657     SOC_IF_ERROR_RETURN
    658         (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status));
    659     if (fiber_status == 0) {
    660         rv = _phy_56xxx_5601x_sgmii_speed_set(unit, port, speed);
    661     } else {
    662         rv = _phy_56xxx_5601x_1000x_speed_set(unit, port, speed);
    663     }
    664 
    665     if (SOC_SUCCESS(rv)) {
    666         pc->fiber.force_speed = speed;
    667     }
    668 
    669     LOG_INFO(BSL_LS_SOC_PHY,
    670              (BSL_META_U(unit,
    671                          "phy_56xxx_5601x_speed_set: u=%d p=%d speed=%d rv=%d\n"),
    672               unit, port, speed, rv));
    673     
    674     return rv;
    675 }
    676 
    677 STATIC int 
    678 _phy_56xxx_5601x_sgmii_speed_get(int unit, soc_port_t port, int *speed)
    679 {
    680     uint16 mii_ctrl, mii_stat, mii_anp;
    681     phy_ctrl_t  *pc;
    682 
    683     pc = INT_PHY_SW_STATE(unit, port);
    684 
    685     SOC_IF_ERROR_RETURN
    686         (READ_SERDES_5601X_MII_CTRLr(unit, pc, &mii_ctrl));
    687     SOC_IF_ERROR_RETURN
    688         (READ_SERDES_5601X_MII_STATr(unit, pc, &mii_stat)); 
    689  
    690     if (mii_ctrl & DDS_MII_CTRL_AN_ENABLE) { /*Auto-negotiation enabled */  
    691         if (!(mii_stat & MII_STAT_AN_DONE)) { /* Auto-neg NOT complete */
    692             *speed = 0;
    693             return SOC_E_NONE;
    694         } else { /* Read Advertise link partner */
    695             SOC_IF_ERROR_RETURN
    696                 (READ_SERDES_5601X_MII_ANPr(unit, pc, &mii_anp));
    697             if (mii_anp & DDS_MII_ANP_SGMII) {
    698                 switch(mii_anp & DDS_MII_ANP_SGMII_SPEED) {
    699                 case DDS_MII_ANP_SGMII_SPEED_1000:
    700                     *speed = 1000;
    701                     break;
    702                 case DDS_MII_ANP_SGMII_SPEED_100:
    703                     *speed = 100;
    704                     break;
    705                 case DDS_MII_ANP_SGMII_SPEED_10:
    706                     *speed = 10;
    707                     break;
    708                 default:
    709                     return SOC_E_UNAVAIL;
    710                 }
    711             }
    712         }
    713     } else {  /* Forced speed */
    714         switch(mii_ctrl & (DDS_MII_CTRL_SS_LSB | DDS_MII_CTRL_SS_MSB)) {
    715         case DDS_MII_CTRL_SS_10:
    716             *speed = 10; 
    717             break;
    718         case DDS_MII_CTRL_SS_100:
    719             *speed = 100;
    720             break;
    721         case DDS_MII_CTRL_SS_1000:
    722             *speed = 1000;
    723             break;
    724         default:
    725             return SOC_E_UNAVAIL;
    726         } 
    727     }
    728     return SOC_E_NONE;
    729 }
    730 
    731 STATIC int
    732 _phy_56xxx_5601x_1000x_speed_get(int unit, soc_port_t port, int *speed)
    733 {
    734     COMPILER_REFERENCE(unit);
    735     COMPILER_REFERENCE(port);
    736 
    737     *speed = 1000;
    738 
    739     return SOC_E_NONE;
    740 }
    741 
    742 /*
    743  * Function:    
    744  *      phy_56xxx_5601x_speed_get
    745  * Purpose:     
    746  *      Get the current operating speed. If autoneg is enabled, 
    747  *      then operating mode is returned, otherwise forced mode is returned.
    748  * Parameters:
    749  *      unit - StrataSwitch unit #.
    750  *      port - StrataSwitch port #. 
    751  *      duplex - (OUT) Boolean, true indicates full duplex, false 
    752  *              indicates half.
    753  * Returns:     
    754  *      SOC_E_XXX
    755  */
    756 
    757 STATIC int
    758 phy_56xxx_5601x_speed_get(int unit, soc_port_t port, int *speed)
    759 {
    760     uint16       fiber_status;
    761     phy_ctrl_t  *pc;
    762 
    763     pc = INT_PHY_SW_STATE(unit, port);
    764 
    765     *speed = 1000;
    766 
    767 #ifdef INCLUDE_PHY_XGXS6
    768     if (soc_feature(unit, soc_feature_xgxs_v6)) {
    769         return  phy_xgxs6_hg.pd_speed_get(unit, port, speed);
    770     }
    771 #endif /* INCLUDE_PHY_XGXS6 */
    772  
    773     SOC_IF_ERROR_RETURN
    774         (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status));
    775     if (fiber_status == 0) {
    776         SOC_IF_ERROR_RETURN
    777             (_phy_56xxx_5601x_sgmii_speed_get(unit, port, speed));
    778     } else {
    779         SOC_IF_ERROR_RETURN
    780             (_phy_56xxx_5601x_1000x_speed_get(unit, port, speed));
    781     }
    782 
    783     return SOC_E_NONE;
    784 }
    785 
    786 /*
    787  * Function:    
    788  *      phy_56xxx_5601x_an_set
    789  * Purpose:     
    790  *      Enable or disabled auto-negotiation on the specified port.
    791  * Parameters:
    792  *      unit - StrataSwitch unit #.
    793  *      port - StrataSwitch port #. 
    794  *      an - Boolean, if true, auto-negotiation is enabled 
    795  *              (and/or restarted). If false, autonegotiation is disabled.
    796  * Returns:     
    797  *      SOC_E_XXX
    798  */
    799 
    800 STATIC int
    801 phy_56xxx_5601x_an_set(int unit, soc_port_t port, int an)
    802 {
    803     phy_ctrl_t  *pc;
    804     uint16       par_det;
    805     uint16       an_enable;
    806     uint16       auto_det;
    807 
    808     pc = INT_PHY_SW_STATE(unit, port);
    809 
    810     LOG_INFO(BSL_LS_SOC_PHY,
    811              (BSL_META_U(unit,
    812                          "phy_56xxx_5601x_an_set: u=%d p=%d an=%d\n"),
    813               unit, port, an));
    814     /*
    815      * Special case for BCM5421S being used in SGMII copper mode with
    816      * BCM56xxx_5601x.  Always have autoneg on.
    817      */
    818     if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_5421S)) {
    819         an = 1;
    820     }
    821 
    822     par_det   = 0;
    823     an_enable = 0; 
    824     auto_det  = 0;
    825     if (an) {
    826         par_det = DDS_1000X_PAR_DET_EN;
    827         an_enable = DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART;
    828         if (soc_property_port_get(unit, port,
    829                                   spn_SERDES_AUTOMEDIUM, FALSE)) {
    830             auto_det = DDS_1000X_AUTO_DETECT; 
    831         }
    832     }
    833 
    834     SOC_IF_ERROR_RETURN
    835         (MODIFY_SERDES_5601X_1000X_CTRL2r(unit, pc,
    836                         par_det, DDS_1000X_PAR_DET_EN));
    837 
    838     SOC_IF_ERROR_RETURN
    839         (MODIFY_SERDES_5601X_1000X_CTRL1r(unit, pc, 
    840                         auto_det, DDS_1000X_AUTO_DETECT));
    841 
    842 
    843     /* Enable/Disable and Restart autonegotiation (self-clearing bit) */
    844     SOC_IF_ERROR_RETURN
    845         (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, an_enable,
    846                         DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART));
    847 
    848     pc->fiber.autoneg_enable = an;
    849 
    850     return SOC_E_NONE;
    851 }
    852 
    853 /*
    854  * Function:    
    855  *      phy_56xxx_5601x_adv_local_set
    856  * Purpose:     
    857  *      Set the current advertisement for auto-negotiation.
    858  * Parameters:
    859  *      unit - StrataSwitch unit #.
    860  *      port - StrataSwitch port #. 
    861  *      mode - Port mode mask indicating supported options/speeds.
    862  * Returns:     
    863  *      SOC_E_XXX
    864  */
    865 STATIC int 
    866 phy_56xxx_5601x_adv_local_set(int unit, soc_port_t port, soc_port_mode_t mode)
    867 {
    868 #ifdef INCLUDE_PHY_XGXS6
    869     if (soc_feature(unit, soc_feature_xgxs_v6)) {
    870         mode &= SOC_PM_2500MB_FD | SOC_PM_1000MB_FD |
    871                 SOC_PM_PAUSE_TX | SOC_PM_PAUSE_RX;
    872         return (phy_xgxs6_hg.pd_adv_local_set(unit, port, mode));
    873     }
    874 #endif
    875 
    876     return phy_serdes_adv_local_set(unit, port, mode);
    877 }
    878 
    879 /*
    880  * Function:    
    881  *      phy_56xxx_5601x_adv_local_get
    882  * Purpose:     
    883  *      Get the current advertisement for auto-negotiation.
    884  * Parameters:
    885  *      unit - StrataSwitch unit #.
    886  *      port - StrataSwitch port #. 
    887  *      mode - (OUT) Port mode mask indicating supported options/speeds.
    888  * Returns:     
    889  *      SOC_E_XXX
    890  */
    891 
    892 STATIC int
    893 phy_56xxx_5601x_adv_local_get(int unit, soc_port_t port, soc_port_mode_t *mode)
    894 {
    895 #ifdef INCLUDE_PHY_XGXS6
    896     if (soc_feature(unit, soc_feature_xgxs_v6)) {
    897         return  phy_xgxs6_hg.pd_adv_local_get(unit, port, mode);
    898     }
    899 #endif /* INCLUDE_PHY_XGXS6 */
    900  
    901     return phy_serdes_adv_local_get(unit, port, mode);
    902 }
    903 
    904 STATIC int
    905 _phy_56xxx5601x_sgmii_adv_remote_get(int unit, soc_port_t port, 
    906                                 soc_port_mode_t *mode)
    907 {
    908     uint16              anlpa;
    909     soc_port_mode_t     duplex;
    910     phy_ctrl_t         *pc;
    911 
    912     pc = INT_PHY_SW_STATE(unit, port);
    913 
    914     *mode = 0;
    915 
    916     SOC_IF_ERROR_RETURN
    917         (READ_SERDES_5601X_MII_ANPr(unit, pc, &anlpa));
    918 
    919     switch (anlpa & DDS_MII_ANP_SGMII_SPEED) {
    920     case DDS_MII_ANP_SGMII_SPEED_10:
    921         *mode = SOC_PM_10MB;
    922         break;
    923     case DDS_MII_ANP_SGMII_SPEED_100:
    924         *mode = SOC_PM_100MB;
    925         break;
    926     case DDS_MII_ANP_SGMII_SPEED_1000:
    927         *mode = SOC_PM_1000MB;
    928         break;
    929     } 
    930 
    931     duplex = (anlpa & DDS_MII_ANP_SGMII_DUPLEX) ? SOC_PM_FD : SOC_PM_HD;
    932 
    933     *mode = (*mode) & duplex; 
    934 
    935     return SOC_E_NONE;
    936 }
    937 
    938 STATIC int
    939 _phy_56xxx5601x_1000x_adv_remote_get(int unit, soc_port_t port,
    940                                 soc_port_mode_t *mode)
    941 {
    942     uint16              anlpa;
    943     phy_ctrl_t  *pc;
    944 
    945     pc = INT_PHY_SW_STATE(unit, port);
    946 
    947     *mode = 0;
    948 
    949     SOC_IF_ERROR_RETURN
    950         (READ_SERDES_5601X_MII_ANPr(unit, pc, &anlpa));
    951 
    952     if (anlpa & DDS_MII_ANP_FIBER_FD) {
    953         *mode |= SOC_PM_1000MB_FD;
    954     }
    955 
    956     if (anlpa & DDS_MII_ANP_FIBER_HD) {
    957         *mode |= SOC_PM_1000MB_HD;
    958     }
    959 
    960     switch (anlpa &
    961             (DDS_MII_ANP_FIBER_PAUSE_SYM | DDS_MII_ANP_FIBER_PAUSE_ASYM)) {
    962     case DDS_MII_ANP_FIBER_PAUSE_SYM:
    963         *mode |= SOC_PM_PAUSE_TX | SOC_PM_PAUSE_RX;
    964         break;
    965     case DDS_MII_ANP_FIBER_PAUSE_ASYM:
    966         *mode |= SOC_PM_PAUSE_TX;
    967         break;
    968     case DDS_MII_ANP_FIBER_PAUSE_SYM | DDS_MII_ANP_FIBER_PAUSE_ASYM:
    969         *mode |= SOC_PM_PAUSE_RX;
    970         break;
    971     }
    972     
    973     return SOC_E_NONE;
    974 }
    975 
    976 /*
    977  * Function:
    978  *      _phy_56xxx_5601x_adv_remote_get
    979  * Purpose:
    980  *      Get partner's current advertisement for auto-negotiation.
    981  * Parameters:
    982  *      unit - StrataSwitch unit #.
    983  *      port - StrataSwitch port #.
    984  *      mode - (OUT) Port mode mask indicating supported options/speeds.
    985  * Returns:
    986  *      SOC_E_XXX
    987  */
    988 
    989 STATIC int
    990 _phy_56xxx_5601x_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode)
    991 {
    992     uint16              fiber_status;
    993     phy_ctrl_t  *pc;
    994 
    995     pc = INT_PHY_SW_STATE(unit, port);
    996 
    997     SOC_IF_ERROR_RETURN
    998         (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status));
    999     if (fiber_status == 0) {
   1000         SOC_IF_ERROR_RETURN
   1001             (_phy_56xxx5601x_sgmii_adv_remote_get(unit, port, mode));
   1002     } else {
   1003         SOC_IF_ERROR_RETURN
   1004             (_phy_56xxx5601x_1000x_adv_remote_get(unit, port, mode));
   1005     }
   1006 
   1007     LOG_INFO(BSL_LS_SOC_PHY,
   1008              (BSL_META_U(unit,
   1009                          "phy_serdes_adv_remote_get: u=%d p=%d adv=%s%s%s\n"),
   1010               unit, port,
   1011               (*mode & SOC_PM_1000MB_FD) ? "1000MB " : "",
   1012               (*mode & SOC_PM_PAUSE_TX)  ? "PAUSE_TX " : "",
   1013               (*mode & SOC_PM_PAUSE_RX)  ? "PAUSE_TX " : ""));
   1014 
   1015     return SOC_E_NONE;
   1016 }
   1017 
   1018 /*
   1019  * Function:    
   1020  *      phy_56xxx_5601x_adv_remote_get
   1021  * Purpose:     
   1022  *      Get partner's current advertisement for auto-negotiation.
   1023  * Parameters:
   1024  *      unit - StrataSwitch unit #.
   1025  *      port - StrataSwitch port #. 
   1026  *      mode - (OUT) Port mode mask indicating supported options/speeds.
   1027  * Returns:
   1028  *      SOC_E_XXX
   1029  */
   1030 
   1031 STATIC int
   1032 phy_56xxx_5601x_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   1033 {
   1034 #ifdef INCLUDE_PHY_XGXS6
   1035     if (soc_feature(unit, soc_feature_xgxs_v6)) {
   1036         return  phy_xgxs6_hg.pd_adv_remote_get(unit, port, mode);
   1037     }
   1038 #endif /* INCLUDE_PHY_XGXS6 */
   1039  
   1040     return _phy_56xxx_5601x_adv_remote_get(unit, port, mode);
   1041 }
   1042 
   1043 /*
   1044  * Function:    
   1045  *      phy_56xxx_5601x_lb_set
   1046  * Purpose:     
   1047  *      Set the internal PHY loopback mode.
   1048  * Parameters:
   1049  *      unit - StrataSwitch unit #.
   1050  *      port - StrataSwitch port #. 
   1051  *      loopback - Boolean: true = enable loopback, false = disable.
   1052  * Returns:     
   1053  *      SOC_E_XXX
   1054  */
   1055 
   1056 STATIC int
   1057 phy_56xxx_5601x_lb_set(int unit, soc_port_t port, int enable)
   1058 {
   1059     uint16       ctrl;
   1060     phy_ctrl_t  *pc;
   1061     int          rv;
   1062 
   1063 #ifdef INCLUDE_PHY_XGXS6
   1064     if (soc_feature(unit, soc_feature_xgxs_v6)) {
   1065         return phy_xgxs6_hg.pd_lb_set(unit, port, enable);
   1066     }
   1067 #endif /* INCLUDE_PHY_XGXS6 */
   1068 
   1069     pc = INT_PHY_SW_STATE(unit, port);
   1070 
   1071     ctrl = (enable) ? DDS_MII_CTRL_LOOPBACK : 0;
   1072 
   1073     rv = MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, ctrl, DDS_MII_CTRL_LOOPBACK);
   1074 
   1075     LOG_INFO(BSL_LS_SOC_PHY,
   1076              (BSL_META_U(unit,
   1077                          "phy_56xxx_5601x_lb_set: u=%d p=%d lb=%d rv=%d\n"),
   1078               unit, port, enable, rv));
   1079     return rv;
   1080 }
   1081 
   1082 /*
   1083  * Function:    
   1084  *      phy_56xxx_5601x_ability_get
   1085  * Purpose:     
   1086  *      Get the abilities of the internal PHY.
   1087  * Parameters:
   1088  *      unit - StrataSwitch unit #.
   1089  *      port - StrataSwitch port #. 
   1090  *      mode - (OUT) Port mode mask indicating supported options/speeds.
   1091  * Returns:     
   1092  *      SOC_E_XXX
   1093  */
   1094 
   1095 STATIC int
   1096 phy_56xxx_5601x_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   1097 {
   1098     uint16      fiber_status;
   1099     phy_ctrl_t *pc;
   1100     pc = INT_PHY_SW_STATE(unit, port);
   1101 
   1102     SOC_IF_ERROR_RETURN
   1103         (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status));
   1104     if (fiber_status == 0) {
   1105         *mode = (SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB_FD | 
   1106                  SOC_PM_LB_PHY | SOC_PM_GMII);
   1107     } else {
   1108         *mode = (SOC_PM_1000MB_FD | SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM |
   1109                  SOC_PM_AN | SOC_PM_LB_PHY | SOC_PM_GMII);
   1110     }
   1111 
   1112 #ifdef INCLUDE_PHY_XGXS6
   1113     if (soc_feature(unit, soc_feature_xgxs_v6)) {
   1114         *mode |= SOC_PM_2500MB_FD;
   1115     }
   1116 #endif /* INCLUDE_PHY_XGXS6 */
   1117  
   1118 
   1119     return SOC_E_NONE;
   1120 }
   1121 
   1122 /*
   1123  * Function:
   1124  *      _phy_56xxx_5601x_medium_config_update
   1125  * Purpose:
   1126  *      Update the PHY with config parameters
   1127  * Parameters:
   1128  *      unit - StrataSwitch unit #.
   1129  *      port - StrataSwitch port #.
   1130  *      cfg - Config structure.
   1131  * Returns:
   1132  *      SOC_E_XXX
   1133  */
   1134 
   1135 STATIC int
   1136 _phy_56xxx_5601x_medium_config_update(int unit, soc_port_t port,
   1137                                 soc_phy_config_t *cfg)
   1138 {
   1139     SOC_IF_ERROR_RETURN
   1140         (phy_56xxx_5601x_speed_set(unit, port, cfg->force_speed));
   1141     SOC_IF_ERROR_RETURN
   1142         (phy_serdes_duplex_set(unit, port, cfg->force_duplex));
   1143     SOC_IF_ERROR_RETURN
   1144         (phy_56xxx_5601x_adv_local_set(unit, port, cfg->autoneg_advert));
   1145     SOC_IF_ERROR_RETURN
   1146         (phy_56xxx_5601x_an_set(unit, port, cfg->autoneg_enable));
   1147 
   1148     return SOC_E_NONE;
   1149 }
   1150 
   1151 /*
   1152  * Function:
   1153  *      phy_56xxx_5601x_medium_config_set
   1154  * Purpose:
   1155  *      Set the operating parameters that are automatically selected
   1156  *      when medium switches type.
   1157  * Parameters:
   1158  *      unit - StrataSwitch unit #
   1159  *      port - Port number
   1160  *      medium - SOC_PORT_MEDIUM_COPPER/FIBER
   1161  *      cfg - Operating parameters
   1162  * Returns:
   1163  *      SOC_E_XXX
   1164  */
   1165 
   1166 STATIC int
   1167 phy_56xxx_5601x_medium_config_set(int unit, soc_port_t port, 
   1168                            soc_port_medium_t  medium,
   1169                            soc_phy_config_t  *cfg)
   1170 {
   1171     phy_ctrl_t    *pc;
   1172 
   1173     pc = INT_PHY_SW_STATE(unit, port);
   1174 
   1175     /*
   1176      * Changes take effect immediately if the target medium is active.
   1177      */
   1178     switch (medium) {
   1179     case SOC_PORT_MEDIUM_FIBER:
   1180         /* Change the fiber modes */
   1181         sal_memcpy(&pc->fiber, cfg, sizeof (pc->fiber));
   1182 
   1183         pc->fiber.autoneg_advert &= ADVERT_ALL_FIBER;
   1184         if (PHY_FIBER_MODE(unit, port)) {
   1185             SOC_IF_ERROR_RETURN
   1186                 (_phy_56xxx_5601x_medium_config_update(unit, port, &pc->fiber));
   1187         }
   1188         return SOC_E_NONE;
   1189     case SOC_PORT_MEDIUM_COPPER:
   1190         return SOC_E_UNAVAIL;
   1191     default:
   1192         return SOC_E_PARAM;
   1193     }
   1194 }
   1195 
   1196 STATIC int
   1197 _phy_56xxx_5601x_control_tx_driver_set(int unit, soc_port_t port,
   1198                                 soc_phy_control_t type, uint32 value)
   1199 {
   1200     uint16              data;  /* Temporary holder of reg value to be written */
   1201     uint16              mask;  /* Bit mask of reg value to be updated */
   1202     phy_ctrl_t  *pc;    /* PHY software state */
   1203 
   1204     pc = INT_PHY_SW_STATE(unit, port);
   1205 
   1206     /* Dozen SerDes */
   1207     switch(type) {
   1208     case SOC_PHY_CONTROL_PREEMPHASIS:
   1209          data = (uint16)(value & 0x7);
   1210          data = data << 6;
   1211          mask = 0x01C0;
   1212          break;
   1213     case SOC_PHY_CONTROL_DRIVER_CURRENT:
   1214          data = (uint16)(value & 0xF);
   1215          data = data << 12;
   1216          mask = 0xF000;
   1217          break;
   1218     case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
   1219          data = (uint16)(value & 0x7);
   1220          data = data << 9;
   1221          mask = 0x0E00;
   1222          break;
   1223     default:
   1224          /* should never get here */
   1225          return SOC_E_PARAM;
   1226     }
   1227     SOC_IF_ERROR_RETURN
   1228         (MODIFY_SERDES_5601X_ANALOG_TXr(unit, pc, data, mask));
   1229 
   1230     return SOC_E_NONE;
   1231 }
   1232 
   1233 STATIC int
   1234 _phy_56xxx_5601x_control_tx_driver_get(int unit, soc_port_t port,
   1235                                 soc_phy_control_t type, uint32 *value)
   1236 {
   1237     uint16                data16;   /* Temporary holder of a reg value */
   1238     phy_ctrl_t    *pc;       /* PHY software state */
   1239 
   1240     pc = INT_PHY_SW_STATE(unit, port);
   1241 
   1242     data16 = 0;
   1243     /* Dozen SerDes */
   1244     SOC_IF_ERROR_RETURN
   1245         (READ_SERDES_5601X_ANALOG_TXr(unit, pc, &data16));
   1246     switch(type) {
   1247     case SOC_PHY_CONTROL_PREEMPHASIS:
   1248          *value = (data16 & 0x01C0) >> 6;
   1249          break;
   1250     case SOC_PHY_CONTROL_DRIVER_CURRENT:
   1251          *value = (data16 & 0xF000) >> 12;
   1252          break;
   1253     case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
   1254          *value = (data16 & 0x0E00) >> 9;
   1255          break;
   1256     default:
   1257          /* should never get here */
   1258          return SOC_E_PARAM;
   1259     }
   1260 
   1261     return SOC_E_NONE;
   1262 }
   1263 
   1264 /*
   1265  * Function:
   1266  *      phy_phy56xxx_5601x_control_set
   1267  * Purpose:
   1268  *      Configure PHY device specific control fucntion.
   1269  * Parameters:
   1270  *      unit  - StrataSwitch unit #.
   1271  *      port  - StrataSwitch port #.
   1272  *      type  - Control to update
   1273  *      value - New setting for the control
   1274  * Returns:
   1275  *      SOC_E_NONE
   1276  */
   1277 STATIC int
   1278 phy_56xxx_5601x_control_set(int unit, soc_port_t port,
   1279                      soc_phy_control_t type, uint32 value)
   1280 {
   1281     int rv;
   1282 
   1283 #ifdef INCLUDE_PHY_XGXS6
   1284     if (soc_feature(unit, soc_feature_xgxs_v6)) {
   1285         return (phy_xgxs6_hg.pd_control_set(unit, port, type, value));
   1286     }
   1287 #endif
   1288 
   1289     PHY_CONTROL_TYPE_CHECK(type);
   1290   
   1291     rv = SOC_E_UNAVAIL;
   1292     switch(type) {
   1293     case SOC_PHY_CONTROL_PREEMPHASIS:
   1294         /* fall through */
   1295     case SOC_PHY_CONTROL_DRIVER_CURRENT:
   1296         /* fall through */
   1297     case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
   1298         rv = _phy_56xxx_5601x_control_tx_driver_set(unit, port, type, value);
   1299         break;
   1300     default:
   1301         rv = SOC_E_UNAVAIL;
   1302         break;
   1303     }
   1304     return rv;
   1305 }
   1306 /*
   1307  * Function:
   1308  *      phy_56xxx_5601x_control_get
   1309  * Purpose:
   1310  *      Get current control settign of the PHY.
   1311  * Parameters:
   1312  *      unit  - StrataSwitch unit #.
   1313  *      port  - StrataSwitch port #.
   1314  *      type  - Control to update
   1315  *      value - (OUT)Current setting for the control
   1316  * Returns:    
   1317  *      SOC_E_NONE
   1318  */
   1319 STATIC int
   1320 phy_56xxx_5601x_control_get(int unit, soc_port_t port,
   1321                      soc_phy_control_t type, uint32 *value)
   1322 {
   1323     int rv;
   1324 
   1325     if (NULL == value) {
   1326         return SOC_E_PARAM;
   1327     }
   1328 
   1329 #ifdef INCLUDE_PHY_XGXS6
   1330     if (soc_feature(unit, soc_feature_xgxs_v6)) {
   1331         return (phy_xgxs6_hg.pd_control_get(unit, port, type, value));
   1332     } 
   1333 #endif /* INCLUDE_PHY_XGXS6 */
   1334 
   1335     PHY_CONTROL_TYPE_CHECK(type);
   1336   
   1337     rv = SOC_E_UNAVAIL;
   1338     switch(type) {
   1339     case SOC_PHY_CONTROL_PREEMPHASIS:
   1340         /* fall through */
   1341     case SOC_PHY_CONTROL_DRIVER_CURRENT:
   1342         /* fall through */
   1343     case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
   1344         rv = _phy_56xxx_5601x_control_tx_driver_get(unit, port, type, value);
   1345         break;
   1346     default:
   1347         rv = SOC_E_UNAVAIL;
   1348         break;
   1349     }
   1350 
   1351     return rv;
   1352 }
   1353 
   1354 /*
   1355  * Function:
   1356  *      phy_56xxx_5601x_reg_read
   1357  * Purpose:
   1358  *      Routine to read PHY register
   1359  * Parameters:
   1360  *      uint         - BCM unit number
   1361  *      pc           - PHY state
   1362  *      flags        - Flags which specify the register type
   1363  *      phy_reg_addr - Encoded register address
   1364  *      phy_data     - (OUT) Value read from PHY register
   1365  * Note:
   1366  *      This register read function is not thread safe. Higher level
   1367  * function that calls this function must obtain a per port lock
   1368  * to avoid overriding register page mapping between threads.
   1369  */
   1370 STATIC int
   1371 phy_56xxx_5601x_reg_read(int unit, soc_port_t port, uint32 flags,
   1372                   uint32 phy_reg_addr, uint32 *phy_data)
   1373 {
   1374     uint16               reg_bank;
   1375     uint8                reg_addr;
   1376     uint16               data;     /* Temporary holder for phy_data */
   1377     phy_ctrl_t          *pc;      /* PHY software state */
   1378 
   1379 #ifdef INCLUDE_PHY_XGXS6
   1380     if (soc_feature(unit, soc_feature_xgxs_v6)) {
   1381         return (phy_xgxs6_hg.pd_reg_read(unit, port, flags, 
   1382                                          phy_reg_addr, phy_data));
   1383     } 
   1384 #endif /* INCLUDE_PHY_XGXS6 */
   1385 
   1386     pc = INT_PHY_SW_STATE(unit, port);
   1387 
   1388     reg_bank  = SOC_PHY_REG_BANK(phy_reg_addr);
   1389     reg_addr  = SOC_PHY_REG_ADDR(phy_reg_addr);
   1390 
   1391     SOC_IF_ERROR_RETURN
   1392         (phy_reg_serdes_read(unit, pc, reg_bank, reg_addr, &data));
   1393 
   1394    *phy_data = data;
   1395 
   1396     return SOC_E_NONE;
   1397 }
   1398 
   1399 /*
   1400  * Function:
   1401  *      phy_56xxx_5601x_reg_write
   1402  * Purpose:
   1403  *      Routine to write PHY register
   1404  * Parameters:
   1405  *      uint         - BCM unit number
   1406  *      pc           - PHY state
   1407  *      flags        - Flags which specify the register type
   1408  *      phy_reg_addr - Encoded register address
   1409  *      phy_data     - Value write to PHY register
   1410  * Note:
   1411  *      This register read function is not thread safe. Higher level
   1412  * function that calls this function must obtain a per port lock
   1413  * to avoid overriding register page mapping between threads.
   1414  */
   1415 STATIC int
   1416 phy_56xxx_5601x_reg_write(int unit, soc_port_t port, uint32 flags,
   1417                    uint32 phy_reg_addr, uint32 phy_data)
   1418 {
   1419     uint16               reg_bank;
   1420     uint8                reg_addr;
   1421     uint16               data;     /* Temporary holder for phy_data */
   1422     phy_ctrl_t          *pc;      /* PHY software state */
   1423 
   1424 #ifdef INCLUDE_PHY_XGXS6
   1425     if (soc_feature(unit, soc_feature_xgxs_v6)) {
   1426         return (phy_xgxs6_hg.pd_reg_write(unit, port, flags, 
   1427                                          phy_reg_addr, phy_data));
   1428     } 
   1429 #endif /* INCLUDE_PHY_XGXS6 */
   1430 
   1431     pc = INT_PHY_SW_STATE(unit, port);
   1432 
   1433     data      = (uint16) (phy_data & 0x0000FFFF);
   1434     reg_bank  = SOC_PHY_REG_BANK(phy_reg_addr);
   1435     reg_addr  = SOC_PHY_REG_ADDR(phy_reg_addr);
   1436 
   1437     SOC_IF_ERROR_RETURN
   1438         (phy_reg_serdes_write(unit, pc, reg_bank, reg_addr, data));
   1439 
   1440     return SOC_E_NONE;
   1441 }
   1442 
   1443 /*
   1444  * Function:
   1445  *      phy_56xxx_5601x_reg_modify
   1446  * Purpose:
   1447  *      Routine to write PHY register
   1448  * Parameters:
   1449  *      uint         - BCM unit number
   1450  *      pc           - PHY state
   1451  *      flags        - Flags which specify the register type
   1452  *      phy_reg_addr - Encoded register address
   1453  *      phy_mo_data  - New value for the bits specified in phy_mo_mask
   1454  *      phy_mo_mask  - Bit mask to modify
   1455  * Note:
   1456  *      This register read function is not thread safe. Higher level
   1457  * function that calls this function must obtain a per port lock
   1458  * to avoid overriding register page mapping between threads.
   1459  */
   1460 STATIC int
   1461 phy_56xxx_5601x_reg_modify(int unit, soc_port_t port, uint32 flags,
   1462                     uint32 phy_reg_addr, uint32 phy_data, uint32 phy_data_mask)
   1463 {
   1464     uint16         reg_bank;
   1465     uint8          reg_addr;
   1466     uint16         data;     /* Temporary holder for phy_data */
   1467     uint16         mask;
   1468     phy_ctrl_t    *pc;       /* PHY software state */
   1469 
   1470 #ifdef INCLUDE_PHY_XGXS6
   1471     if (soc_feature(unit, soc_feature_xgxs_v6)) {
   1472         return (phy_xgxs6_hg.pd_reg_modify(unit, port, flags, 
   1473                                        phy_reg_addr, phy_data, phy_data_mask));
   1474     } 
   1475 #endif /* INCLUDE_PHY_XGXS6 */
   1476 
   1477     pc = INT_PHY_SW_STATE(unit, port);
   1478 
   1479     data      = (uint16) (phy_data & 0x0000FFFF);
   1480     mask      = (uint16) (phy_data_mask & 0x0000FFFF);
   1481     reg_bank  = SOC_PHY_REG_BANK(phy_reg_addr);
   1482     reg_addr  = SOC_PHY_REG_ADDR(phy_reg_addr);
   1483 
   1484     SOC_IF_ERROR_RETURN
   1485         (phy_reg_serdes_modify(unit, pc, reg_bank, reg_addr, data, mask));
   1486 
   1487     return SOC_E_NONE;
   1488 }
   1489 
   1490 STATIC int
   1491 phy_56xxx_5601x_notify(int unit, soc_port_t port, 
   1492                  soc_phy_event_t event, uint32 value)
   1493 {
   1494     int             rv;
   1495 
   1496     if (event >= phyEventCount) {
   1497         return SOC_E_PARAM;
   1498     }
   1499 
   1500     rv = SOC_E_NONE;
   1501     switch(event) {
   1502     case phyEventInterface:
   1503         rv = (_phy_56xxx_5601x_notify_interface(unit, port, value));
   1504         break;
   1505     case phyEventDuplex:
   1506         rv = (_phy_56xxx_5601x_notify_duplex(unit, port, value));
   1507         break; 
   1508     case phyEventSpeed:
   1509         rv = (_phy_56xxx_5601x_notify_speed(unit, port, value));
   1510         break; 
   1511     case phyEventStop:
   1512         rv = (_phy_56xxx_5601x_notify_stop(unit, port, value));
   1513         break; 
   1514     case phyEventResume:
   1515         rv = (_phy_56xxx_5601x_notify_resume(unit, port, value));
   1516         break; 
   1517     default:
   1518         rv = SOC_E_PARAM;
   1519         break;
   1520     }
   1521 
   1522     return rv;
   1523 }
   1524 
   1525 STATIC int 
   1526 phy_56xxx_5601x_duplex_get(int unit, soc_port_t port, int *duplex)
   1527 {
   1528     uint16       reg0_16;
   1529     uint16       mii_ctrl;
   1530     uint16       fiber_status;
   1531     phy_ctrl_t  *pc;
   1532 
   1533     pc = INT_PHY_SW_STATE(unit, port);
   1534 
   1535     *duplex = TRUE;
   1536 
   1537     SOC_IF_ERROR_RETURN
   1538         (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status));
   1539     if (fiber_status == 0) {
   1540         /* retrieve the duplex setting in SGMII mode */
   1541 
   1542         SOC_IF_ERROR_RETURN
   1543             (READ_SERDES_5601X_MII_CTRLr(unit, pc, &mii_ctrl));
   1544 
   1545         if (mii_ctrl & DDS_MII_CTRL_AN_ENABLE) {
   1546             SOC_IF_ERROR_RETURN
   1547                 (READ_SERDES_5601X_MII_ANPr(unit,pc,&reg0_16));
   1548 
   1549             /* make sure link partner is also in SGMII mode
   1550              * otherwise fall through to use the FD bit in MII_CTRL reg
   1551              */
   1552             if (reg0_16 & DDS_MII_ANP_SGMII) {
   1553 
   1554                 /* test shows the duplex setting of external phy is 
   1555                  * propagated to the AN link partner register 
   1556                  */                
   1557                 if (reg0_16 & DDS_MII_ANP_SGMII_DUPLEX) {
   1558                     *duplex = TRUE;
   1559                     return SOC_E_NONE;
   1560                 } else {
   1561                     *duplex = FALSE;
   1562                     return SOC_E_NONE;
   1563                 } 
   1564             }
   1565         }
   1566 
   1567         *duplex = (mii_ctrl & DDS_MII_CTRL_FD) ? TRUE : FALSE;
   1568     }
   1569 
   1570     return SOC_E_NONE;
   1571 }
   1572 
   1573 STATIC int
   1574 phy_56xxx_5601x_an_get(int unit, soc_port_t port, int *an, int *an_done)
   1575 {
   1576     uint16      ctrl, stat;
   1577     phy_ctrl_t  *pc;
   1578 
   1579     pc = INT_PHY_SW_STATE(unit, port);
   1580 
   1581     SOC_IF_ERROR_RETURN
   1582         (READ_SERDES_5601X_MII_CTRLr(unit, pc, &ctrl));
   1583 
   1584     *an = (ctrl & MII_CTRL_AE) != 0;
   1585 
   1586     SOC_IF_ERROR_RETURN
   1587         (READ_SERDES_5601X_MII_STATr(unit, pc, &stat));
   1588 
   1589     *an_done = (stat & MII_STAT_AN_DONE) != 0;
   1590 
   1591     LOG_INFO(BSL_LS_SOC_PHY,
   1592              (BSL_META_U(unit,
   1593                          "phy_phy56xxx_5601x_an_get: u=%d p=%d an=%d an_done=%d\n"),
   1594               unit, port, *an, *an_done));
   1595 
   1596     return SOC_E_NONE;
   1597 }
   1598 
   1599 STATIC int
   1600 phy_56xxx_5601x_lb_get(int unit, soc_port_t port, int *enable)
   1601 {
   1602     uint16      ctrl;
   1603     phy_ctrl_t  *pc;
   1604 
   1605     pc = INT_PHY_SW_STATE(unit, port);
   1606     SOC_IF_ERROR_RETURN
   1607         (READ_SERDES_5601X_MII_CTRLr(unit, pc, &ctrl));
   1608 
   1609     *enable = (ctrl & MII_CTRL_LE) != 0;
   1610 
   1611     return SOC_E_NONE;
   1612 }
   1613 
   1614 
   1615 /*
   1616  * Variable:    phy_56xxx_5601xdrv_ge
   1617  * Purpose:     PHY Driver for Dozen SerDes Serdes PHY internal to Draco, 
   1618  */
   1619 phy_driver_t phy_56xxx_5601x_drv_ge = {
   1620     "56XXX_5601X Internal SERDES PHY Driver",
   1621     phy_56xxx_5601x_init,
   1622     phy_null_reset,
   1623     phy_serdes_link_get, 
   1624     phy_56xxx_5601x_enable_set, 
   1625     phy_null_enable_get, 
   1626     phy_serdes_duplex_set, 
   1627     phy_56xxx_5601x_duplex_get,          /* duplex_get */
   1628     phy_56xxx_5601x_speed_set, 
   1629     phy_56xxx_5601x_speed_get, 
   1630     phy_serdes_master_set,          
   1631     phy_serdes_master_get,          
   1632     phy_56xxx_5601x_an_set,              /* local */
   1633     phy_56xxx_5601x_an_get,              /* local */
   1634     phy_56xxx_5601x_adv_local_set,
   1635     phy_56xxx_5601x_adv_local_get,
   1636     phy_56xxx_5601x_adv_remote_get,
   1637     phy_56xxx_5601x_lb_set,
   1638     phy_56xxx_5601x_lb_get,
   1639     phy_serdes_interface_set, 
   1640     phy_serdes_interface_get, 
   1641     phy_56xxx_5601x_ability_get,
   1642     NULL,
   1643     NULL,
   1644     phy_null_mdix_set,
   1645     phy_null_mdix_get,
   1646     phy_null_mdix_status_get,
   1647     phy_56xxx_5601x_medium_config_set,
   1648     phy_serdes_medium_config_get,
   1649     phy_serdes_medium_status,
   1650     NULL,                          /* phy_cable_diag  */
   1651     NULL,                          /* phy_link_change */
   1652     phy_56xxx_5601x_control_set,         /* phy_control_set */
   1653     phy_56xxx_5601x_control_get,         /* phy_control_get */
   1654     phy_56xxx_5601x_reg_read,
   1655     phy_56xxx_5601x_reg_write,
   1656     phy_56xxx_5601x_reg_modify,
   1657     phy_56xxx_5601x_notify
   1658 };
   1659 
   1660 /***********************************************************************
   1661  *
   1662  * PASS-THROUGH NOTIFY ROUTINES
   1663  */
   1664 
   1665 /*
   1666  * Function:
   1667  *      phy_56xxx_5601x_notify_duplex
   1668  * Purpose:     
   1669  *      Program duplex if (and only if) 56xxx_5601x is an intermediate PHY.
   1670  * Parameters:
   1671  *      unit - StrataSwitch unit #.
   1672  *      port - StrataSwitch port #. 
   1673  *      duplex - Boolean, TRUE indicates full duplex, FALSE
   1674  *              indicates half.
   1675  * Returns:     
   1676  *      SOC_E_XXX
   1677  * Notes:
   1678  *      If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to
   1679  *      talk directly to an external fiber module.
   1680  *
   1681  *      If PHY_FLAGS_FIBER is clear, the PHY is being used in
   1682  *      pass-through mode to talk to an external SGMII PHY.
   1683  *
   1684  *      When used in SGMII mode, autoneg must be turned off and
   1685  *      the speed/duplex forced to match that of the external PHY.
   1686  */
   1687 STATIC int
   1688 _phy_56xxx_5601x_notify_duplex(int unit, soc_port_t port, uint32 duplex)
   1689 {
   1690     int                 fiber;
   1691     uint16              mii_ctrl;
   1692     phy_ctrl_t  *pc;
   1693 
   1694     fiber = PHY_FIBER_MODE(unit, port);
   1695     pc    = INT_PHY_SW_STATE(unit, port);
   1696     LOG_INFO(BSL_LS_SOC_PHY,
   1697              (BSL_META_U(unit,
   1698                          "phy_56xxx_5601x_notify_duplex: u=%d p=%d duplex=%d fiber=%d\n"),
   1699               unit, port, duplex, fiber));
   1700 
   1701     if (SAL_BOOT_SIMULATION) {
   1702         return SOC_E_NONE;
   1703     }
   1704 
   1705     if (fiber) {
   1706         SOC_IF_ERROR_RETURN
   1707             (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, 
   1708                                    DDS_MII_CTRL_FD, DDS_MII_CTRL_FD));
   1709         return SOC_E_NONE;
   1710     }
   1711 
   1712     /* Put SERDES PHY in reset */
   1713 
   1714     SOC_IF_ERROR_RETURN
   1715         (_phy_56xxx_5601x_notify_stop(unit, port, PHY_STOP_DUPLEX_CHG));
   1716 
   1717     /* Update duplexity */
   1718     mii_ctrl = (duplex) ? DDS_MII_CTRL_FD : 0;
   1719     SOC_IF_ERROR_RETURN
   1720         (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, mii_ctrl, DDS_MII_CTRL_FD)); 
   1721 
   1722     /* Take SERDES PHY out of reset */
   1723     SOC_IF_ERROR_RETURN
   1724         (_phy_56xxx_5601x_notify_resume(unit, port, PHY_STOP_DUPLEX_CHG));
   1725 
   1726     /* Autonegotiation must be turned off to talk to external PHY if
   1727      * SGMII autoneg is not enabled.
   1728      */
   1729     if (!PHY_SGMII_AUTONEG_MODE(unit, port)) {
   1730         SOC_IF_ERROR_RETURN
   1731             (phy_56xxx_5601x_an_set(unit, port, FALSE));
   1732     }
   1733     return SOC_E_NONE;
   1734 }
   1735 
   1736 /*
   1737  * Function:
   1738  *      phy_56xxx_5601x_notify_speed
   1739  * Purpose:     
   1740  *      Program duplex if (and only if) 56xxx_5601x is an intermediate PHY.
   1741  * Parameters:
   1742  *      unit - StrataSwitch unit #.
   1743  *      port - StrataSwitch port #. 
   1744  *      speed - Speed to program.
   1745  * Returns:     
   1746  *      SOC_E_XXX
   1747  * Notes:
   1748  *      If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to
   1749  *      talk directly to an external fiber module.
   1750  *
   1751  *      If PHY_FLAGS_FIBER is clear, the PHY is being used in
   1752  *      pass-through mode to talk to an external SGMII PHY.
   1753  *
   1754  *      When used in pass-through mode, autoneg must be turned off and
   1755  *      the speed/duplex forced to match that of the external PHY.
   1756  */
   1757 
   1758 STATIC int
   1759 _phy_56xxx_5601x_notify_speed(int unit, soc_port_t port, uint32 speed)
   1760 {
   1761     int          fiber;
   1762     uint16       fiber_status;
   1763     phy_ctrl_t  *pc;
   1764 
   1765     pc    = INT_PHY_SW_STATE(unit, port);
   1766     fiber = PHY_FIBER_MODE(unit, port);
   1767 
   1768     if (SAL_BOOT_SIMULATION) {
   1769         return SOC_E_NONE;
   1770     }
   1771     SOC_IF_ERROR_RETURN
   1772         (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status));
   1773     if (fiber && fiber_status) {
   1774         if ((speed != 0) && (speed != 1000)) {
   1775             return SOC_E_CONFIG;
   1776         }
   1777     }
   1778 
   1779     /* Put SERDES PHY in reset */
   1780     SOC_IF_ERROR_RETURN
   1781         (_phy_56xxx_5601x_notify_stop(unit, port, PHY_STOP_SPEED_CHG));
   1782 
   1783     /* Update speed */
   1784     SOC_IF_ERROR_RETURN
   1785         (_phy_56xxx_5601x_sgmii_speed_set(unit, port, (int)speed));
   1786 
   1787     /* Take SERDES PHY out of reset */
   1788     SOC_IF_ERROR_RETURN
   1789         (_phy_56xxx_5601x_notify_resume(unit, port, PHY_STOP_SPEED_CHG));
   1790 
   1791     /* Autonegotiation must be turned off to talk to external PHY */
   1792     if (!PHY_SGMII_AUTONEG_MODE(unit, port) && !fiber) {
   1793         SOC_IF_ERROR_RETURN
   1794             (phy_56xxx_5601x_an_set(unit, port, FALSE));
   1795     }
   1796 
   1797     return SOC_E_NONE;
   1798 }
   1799 
   1800 /*
   1801  * Function:
   1802  *      _phy_56xxx_5601x_stop
   1803  * Purpose:
   1804  *      Put 56xxx_5601x SERDES in or out of reset depending on conditions
   1805  */
   1806 
   1807 STATIC int
   1808 _phy_56xxx_5601x_stop(int unit, soc_port_t port)
   1809 {
   1810     uint16              mii_ctrl;
   1811     int                 stop, copper;
   1812     phy_ctrl_t  *pc;
   1813 
   1814     pc = INT_PHY_SW_STATE(unit, port);
   1815 
   1816     copper = (pc->stop & 
   1817               PHY_STOP_COPPER) != 0;
   1818 
   1819     stop = ((pc->stop &
   1820              (PHY_STOP_PHY_DIS |
   1821               PHY_STOP_DRAIN)) != 0 ||
   1822             (copper &&
   1823              (pc->stop &
   1824               (PHY_STOP_MAC_DIS |
   1825                PHY_STOP_DUPLEX_CHG |
   1826                PHY_STOP_SPEED_CHG)) != 0));
   1827 
   1828     LOG_INFO(BSL_LS_SOC_PHY,
   1829              (BSL_META_U(unit,
   1830                          "phy_56xxx_5601x_stop: u=%d p=%d copper=%d stop=%d flg=0x%x\n"),
   1831               unit, port, copper, stop,
   1832               pc->stop));
   1833 
   1834     mii_ctrl = (stop) ? DDS_MII_CTRL_PD : 0;
   1835     SOC_IF_ERROR_RETURN
   1836         (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, mii_ctrl, DDS_MII_CTRL_PD));
   1837 
   1838     return SOC_E_NONE; 
   1839 }
   1840 
   1841 /*
   1842  * Function:
   1843  *      phy_56xxx_5601x_notify_stop
   1844  * Purpose:
   1845  *      Add a reason to put 56xxx_5601x PHY in reset.
   1846  * Parameters:
   1847  *      unit - StrataSwitch unit #.
   1848  *      port - StrataSwitch port #.
   1849  *      flags - Reason to stop
   1850  * Returns:
   1851  *      SOC_E_XXX
   1852  */
   1853 STATIC int
   1854 _phy_56xxx_5601x_notify_stop(int unit, soc_port_t port, uint32 flags)
   1855 {
   1856     phy_ctrl_t  *pc;
   1857 
   1858     pc = INT_PHY_SW_STATE(unit, port);
   1859 
   1860     pc->stop |= flags;
   1861 
   1862     return _phy_56xxx_5601x_stop(unit, port);
   1863 }
   1864 
   1865 /*
   1866  * Function:
   1867  *      phy_56xxx_5601x_notify_resume
   1868  * Purpose:
   1869  *      Remove a reason to put 56xxx_5601x PHY in reset.
   1870  * Parameters:
   1871  *      unit - StrataSwitch unit #.
   1872  *      port - StrataSwitch port #.
   1873  *      flags - Reason to stop
   1874  * Returns:
   1875  *      SOC_E_XXX
   1876  */
   1877 STATIC int
   1878 _phy_56xxx_5601x_notify_resume(int unit, soc_port_t port, uint32 flags)
   1879 {
   1880     phy_ctrl_t  *pc;
   1881 
   1882     pc = INT_PHY_SW_STATE(unit, port);
   1883 
   1884     pc->stop &= ~flags;
   1885 
   1886     return _phy_56xxx_5601x_stop(unit, port);
   1887 }
   1888 
   1889 
   1890 STATIC int 
   1891 _phy_56xxx_5601x_notify_interface(int unit, soc_port_t port, uint32 intf)
   1892 {
   1893     phy_ctrl_t  *pc;
   1894     uint16       data16;
   1895 
   1896     pc = INT_PHY_SW_STATE(unit, port);
   1897 
   1898     data16 = (intf == SOC_PORT_IF_SGMII) ? 0 : DDS_1000X_FIBER_MODE; 
   1899     SOC_IF_ERROR_RETURN
   1900         (MODIFY_SERDES_5601X_1000X_CTRL1r(unit, pc, data16, DDS_1000X_FIBER_MODE));
   1901 
   1902     return SOC_E_NONE;
   1903 }
   1904 #else /* INCLUDE_PHY_56XXX_5601X */
   1905 int _phy_56xxx_5601x_not_empty;
   1906 #endif /* INCLUDE_PHY_56XXX_5601X */
   1907