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

serdescombo5601x.c (56239B)


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