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

serdes65lp.c (47305B)


      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  *      physerdes65lp.c
      9  * Purpose:
     10  *      Fiber driver for serdes 65nm Low Power using internal Serdes PHY.
     11  *
     12  * When operating with an external PHY, this driver is not used.
     13  * However the speed/duplex of the internal PHY must be programmed to
     14  * match the MAC and external PHY settings so the data can pass through.
     15  * This file supplies some routines to allow mac.c to accomplish this
     16  * (think of the internal PHY as part of the MAC in this case):
     17  *
     18  *      phy_serdes65lp_notify_duplex
     19  *      phy_serdes65lp_notify_speed
     20  *      phy_serdes65lp_notify_stop
     21  *      phy_serdes65lp_notify_resume
     22  */
     23 
     24 #include <sal/types.h>
     25 #include <sal/core/spl.h>
     26 #include <shared/bsl.h>
     27 #include <soc/drv.h>
     28 #include <soc/debug.h>
     29 #include <soc/error.h>
     30 #include <soc/phy.h>
     31 #include <soc/phyreg.h>
     32 
     33 #include <soc/phy.h>
     34 #include <soc/phy/phyctrl.h>
     35 #include <soc/phy/drv.h>
     36 
     37 #include "phydefs.h"      /* Must include before other phy related includes */
     38 
     39 #if defined(INCLUDE_SERDES_65LP)
     40 #include "phyconfig.h"    /* Must be the first phy include after phydefs.h */
     41 #include "phyident.h"
     42 #include "phyreg.h"
     43 #include "phynull.h"
     44 #include "serdes.h"
     45 #include "serdes65lp.h"
     46 
     47 #define ADVERT_ALL_FIBER \
     48             (SOC_PM_PAUSE | SOC_PM_1000MB_FD)
     49 
     50 #define PORT_ENABLE 0x0  /* Enable ports */
     51 #define PORT_DISABLE 0x1 /* Disable ports */
     52 /* Notify event forward declaration */
     53 STATIC int 
     54 _phy_serdes65lp_notify_duplex(int unit, soc_port_t port, uint32 duplex);
     55 STATIC int 
     56 _phy_serdes65lp_notify_speed(int unit, soc_port_t port, uint32 speed);
     57 STATIC int 
     58 _phy_serdes65lp_notify_stop(int unit, soc_port_t port, uint32 flags);
     59 STATIC int 
     60 _phy_serdes65lp_notify_resume(int unit, soc_port_t port, uint32 flags);
     61 STATIC int
     62 _phy_serdes65lp_notify_interface(int unit, soc_port_t port, uint32 intf);
     63 STATIC int
     64 phy_serdes65lp_duplex_set(int unit, soc_port_t port, int duplex);
     65 
     66 /***********************************************************************
     67  *
     68  * FIBER DRIVER ROUTINES
     69  */
     70 STATIC int
     71 _phy_serdes65lp_tx_control_set(int unit, phy_ctrl_t *pc,
     72                           soc_port_t port)
     73 {
     74     uint32 preemph;
     75     uint32 idriver;
     76     uint32 pdriver;
     77     uint16 data;
     78     uint16 mask;
     79 
     80     SOC_IF_ERROR_RETURN
     81         (READ_SERDES65LP_1000X_STAT1r(unit, pc, &data));
     82 
     83     if (data & LP_1000X_STATUS1_SGMII_MODE) {
     84         /* SGMII mode */
     85         idriver = 0xa;
     86         pdriver = 0x7;
     87     } else {
     88         /* 1000X mode */
     89         idriver = 0xe;
     90         pdriver = 0xe;
     91     }
     92     preemph = 0x0;
     93 
     94     /* Read config override */
     95     preemph = soc_property_port_get(unit, port,
     96                                     spn_SERDES_PREEMPHASIS, preemph);
     97     idriver = soc_property_port_get(unit, port,
     98                                     spn_SERDES_DRIVER_CURRENT, idriver);
     99     pdriver = soc_property_port_get(unit, port,
    100                                     spn_SERDES_PRE_DRIVER_CURRENT, pdriver);
    101 
    102     if (data & LP_1000X_STATUS1_SGMII_MODE) {
    103         /* SGMII mode */
    104         data = ((idriver & 0xf) << 12) | ((preemph & 0xf) << 7);
    105         mask = 0xf780;
    106         SOC_IF_ERROR_RETURN
    107             (MODIFY_SERDES65LP_ANALOG_TX1r(unit, pc, data, mask));
    108 
    109         data = (pdriver & 0xf);
    110         mask = 0xf;
    111         SOC_IF_ERROR_RETURN
    112             (MODIFY_SERDES65LP_ANALOG_TX2r(unit, pc, data, mask));
    113     } else {
    114         /* 1000X mode */
    115         data = (preemph & 0xf) << 7;
    116         mask = 0x0780;
    117         SOC_IF_ERROR_RETURN
    118             (MODIFY_SERDES65LP_ANALOG_TX1r(unit, pc, data, mask));
    119 
    120         data = ((pdriver & 0xf) << 5) | ((idriver & 0xf) << 1);
    121         mask = 0x1fe;
    122         SOC_IF_ERROR_RETURN
    123             (MODIFY_SERDES65LP_ANALOG_TX_AMPr(unit, pc, data, mask));
    124     }
    125     return SOC_E_NONE;
    126 }
    127 
    128 STATIC int
    129 _phy_serdes65lp_init_no_reset(int unit, soc_port_t port)
    130 {
    131     uint16             data16, mask16;
    132     uint16             mode_1000x;
    133     phy_ctrl_t        *pc;
    134 
    135     pc                       = INT_PHY_SW_STATE(unit, port);
    136     pc->fiber.enable         = PHY_FIBER_MODE(unit, port);
    137     pc->fiber.preferred      = PHY_FIBER_MODE(unit, port);
    138     pc->fiber.autoneg_enable = 1;
    139     pc->fiber.autoneg_advert = ADVERT_ALL_FIBER;
    140     pc->fiber.force_speed    = 1000;
    141     pc->fiber.force_duplex   = TRUE;
    142     pc->fiber.master         = SOC_PORT_MS_NONE;
    143     pc->fiber.mdix           = SOC_PORT_MDIX_NORMAL;
    144 
    145     /* reset data path */
    146  
    147     SOC_IF_ERROR_RETURN
    148         (MODIFY_SERDES65LP_1000X_CTRL4r(unit,pc,LP_1000X_DIG_RESET,
    149                                         LP_1000X_DIG_RESET));
    150 
    151     /* some delays defore clearing the reset */ 
    152     SOC_IF_ERROR_RETURN
    153         (READ_SERDES65LP_1000X_CTRL4r(unit,pc, &data16));
    154     SOC_IF_ERROR_RETURN
    155         (MODIFY_SERDES65LP_1000X_CTRL4r(unit,pc,0,
    156                                     LP_1000X_DIG_RESET));
    157    
    158     /* Disable autodetect between 100FX and 1000X mode */
    159     data16 = LP_FX100_CONTROL1_FD;
    160     mask16 = LP_FX100_CONTROL1_FD | LP_FX100_CONTROL1_AUTO_DET |
    161              LP_FX100_CONTROL1_EN;
    162     SOC_IF_ERROR_RETURN
    163         (MODIFY_SERDES65LP_FX100_CTRL1r(unit, pc, data16, mask16)); 
    164 
    165     /* Select between external and internal clock */
    166     if (soc_property_get(unit, spn_SERDES_LCPLL, 0)) {
    167        /* Select 156.25MHz LCPLL */
    168        SOC_IF_ERROR_RETURN
    169            (MODIFY_SERDES65LP_ANALOG_PLL2r(unit, pc, 0x0d88, 0x0d88));
    170     }
    171 
    172     /* Set the default speed/duplex/autoneg */
    173     data16 = MII_CTRL_FD | MII_CTRL_SS_1000;
    174     if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) ||
    175         PHY_PASSTHRU_MODE(unit, port) ||
    176         PHY_SGMII_AUTONEG_MODE(unit, port)) {
    177         data16 |= MII_CTRL_AE | MII_CTRL_RAN;
    178     }
    179 
    180     /* If enabling autoneg, make sure that the default speed and duplex
    181      * are set correctly.
    182      */
    183     if (data16 & MII_CTRL_AE) {
    184         /* Advertise default speed and duplex */
    185         SOC_IF_ERROR_RETURN
    186             (WRITE_SERDES65LP_MII_ANAr(unit, pc, MII_ANA_C37_FD | 
    187                                                  MII_ANA_C37_PAUSE |
    188                                                  MII_ANA_C37_ASYM_PAUSE));
    189     }
    190     SOC_IF_ERROR_RETURN
    191         (WRITE_SERDES65LP_MII_CTRLr(unit, pc, data16));
    192 
    193     /* Configure auto-detect and fiber vs. sgmii mode */
    194     data16 = LP_1000X_PLL_PWRDWN;
    195     mask16 = LP_1000X_PLL_PWRDWN | 
    196              LP_1000X_AUTO_DETECT | LP_1000X_FIBER_MODE |
    197              LP_1000X_SIGNAL_DETECT_EN;
    198 
    199     if (soc_property_port_get(unit, port,spn_SERDES_RX_LOS, 0)) {
    200         /* Configure the signal detect from pin */
    201         data16 |= LP_1000X_SIGNAL_DETECT_EN;
    202     }
    203 
    204     /*
    205      * Configure signal auto-detection between SGMII and fiber.
    206      * This detection only works if auto-negotiation is enabled.
    207      */
    208     if (soc_property_port_get(unit, port, 
    209                               spn_SERDES_AUTOMEDIUM, FALSE)) {
    210         data16 |= LP_1000X_AUTO_DETECT;
    211     }
    212 
    213     /*
    214      * Put the Serdes in Fiber or SGMII mode
    215      */
    216     mode_1000x = 0;
    217     if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) ||
    218         PHY_PASSTHRU_MODE(unit, port)) {
    219         mode_1000x = LP_1000X_FIBER_MODE;
    220     }
    221     /* Allow property to override */
    222     if (soc_property_port_get(unit, port,
    223                               spn_SERDES_FIBER_PREF, mode_1000x)) {
    224         data16 |= LP_1000X_FIBER_MODE;
    225     } else {
    226         data16 &= ~LP_1000X_FIBER_MODE;
    227     }
    228     if (soc_property_port_get(unit, port, 
    229                               spn_SERDES_SGMII_MASTER, FALSE)) {
    230         data16 |= LP_1000X_SGMII_MASTER;
    231         mask16 |= LP_1000X_SGMII_MASTER;
    232     }
    233 
    234     SOC_IF_ERROR_RETURN
    235         (MODIFY_SERDES65LP_1000X_CTRL1r(unit, pc, data16, mask16));   
    236 
    237     data16 = 0; 
    238     mask16 = LP_1000X_FILTER_FORCE_EN | 
    239              LP_1000X_FORCE_XMIT_DATA_ON_TXSIDE |
    240              LP_1000X_PAR_DET_EN;
    241     if ((PHY_EXTERNAL_MODE(unit, port) && PHY_PASSTHRU_MODE(unit, port)) ||
    242         (!PHY_EXTERNAL_MODE(unit, port) && PHY_FIBER_MODE(unit, port))) {
    243         /* Wait 10ms for sync status before a valid link is 
    244          * established when auto-negotiation is disabled.
    245          */
    246         data16 |= LP_1000X_FILTER_FORCE_EN;
    247     } 
    248    
    249     SOC_IF_ERROR_RETURN
    250         (MODIFY_SERDES65LP_1000X_CTRL2r(unit, pc, data16, mask16));
    251 
    252     /* Allow extended packet length ( > 9 kilo bytes) */
    253     /* NB: The link takes longer to link up if extended packet length
    254      *     is enabled.
    255      */  
    256     SOC_IF_ERROR_RETURN
    257         (WRITE_SERDES65LP_FX100_CTRL2r(unit, pc, 
    258                                         LP_FX100_CONTROL2_EXT_PKT_SZ));
    259 
    260     if (soc_property_port_get(unit, port, "serdes_fifo_low_latency", 0)) {
    261         data16 = LP_1000X_FREQ_LOCK_ELASTICITY_RX |
    262                  LP_1000X_FREQ_LOCK_ELASTICITY_TX;
    263         mask16 = LP_1000X_FIFO_ELASTICITY_MASK |
    264                  LP_1000X_FREQ_LOCK_ELASTICITY_RX |
    265                  LP_1000X_FREQ_LOCK_ELASTICITY_TX;
    266         SOC_IF_ERROR_RETURN
    267             (MODIFY_SERDES65LP_1000X_CTRL3r(unit, pc, data16, mask16));
    268     }
    269     SOC_IF_ERROR_RETURN
    270         (_phy_serdes65lp_tx_control_set(unit, pc, port));
    271 
    272     LOG_INFO(BSL_LS_SOC_PHY,
    273              (BSL_META_U(unit,
    274                          "phy_serdes65lp_init: u=%d p=%d %s\n"),
    275               unit, port, 
    276               PHY_FIBER_MODE(unit, port)? "Fiber" : "Copper"));
    277 
    278     return SOC_E_NONE;
    279 }
    280 
    281 
    282 /*
    283  * Function:
    284  *      phy_serdes65lp_init
    285  * Purpose:     
    286  *      Initialize serdes65lp internal PHY driver
    287  * Parameters:
    288  *      unit - StrataSwitch unit #.
    289  *      port - StrataSwitch port #. 
    290  * Returns:     
    291  *      SOC_E_NONE
    292  * Notes:
    293  *      This function implements the bulk part of phy_serdes65lp_init and
    294  *      provides a method for initializing the SerDes when it is used
    295  *      as an intermediate PHY between the MAC and an external PHY. 
    296  *      In the latter case the PHY driver reset callback should be
    297  *      called only once, and this has already been done by the 
    298  *      external PHY driver init function.
    299  */
    300 
    301 STATIC int
    302 phy_serdes65lp_init(int unit, soc_port_t port)
    303 {
    304 
    305     LOG_INFO(BSL_LS_SOC_PHY,
    306              (BSL_META_U(unit,
    307                          "phy_serdes65lp_init: u=%d p=%d\n"),
    308               unit, port));
    309 
    310     if (!PHY_EXTERNAL_MODE(unit, port)) {
    311          /* PHY reset callbacks */
    312         SOC_IF_ERROR_RETURN(soc_phy_reset(unit, port));
    313 
    314         /* No external PHY. SerDes is always in fiber mode. */
    315         if (soc_property_port_get(unit, port,
    316                                   spn_SERDES_FIBER_PREF, 1)) {
    317             PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
    318         } else {
    319             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER);
    320         }
    321 
    322     }
    323     SOC_IF_ERROR_RETURN
    324         (_phy_serdes65lp_init_no_reset(unit, port));
    325 
    326     return SOC_E_NONE;
    327 }
    328 
    329 /*
    330  * Function:    
    331  *      phy_serdes65lp_enable_set
    332  * Purpose:     
    333  *      Enable or disable the physical interface.
    334  * Parameters:
    335  *      unit - StrataSwitch unit #.
    336  *      port - StrataSwitch port #. 
    337  *      enable - Boolean, true = enable PHY, false = disable.
    338  * Returns:     
    339  *      SOC_E_XXX
    340  * Notes:
    341  *      The PHY is held in reset when disabled.  Also, the
    342  *      MISC_CONTROL.IDDQ bit must be set to 1 so the remote partner
    343  *      sees link down.
    344  */
    345 
    346 STATIC int
    347 phy_serdes65lp_enable_set(int unit, soc_port_t port, int enable)
    348 {
    349     phy_ctrl_t  *pc = INT_PHY_SW_STATE(unit, port); /* PHY software state */
    350     LOG_INFO(BSL_LS_SOC_PHY,
    351              (BSL_META_U(unit,
    352                          "phy_serdes65lp_enable_set: u=%d p=%d en=%d\n"),
    353               unit, port, enable));
    354     if (enable) {
    355         PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE);
    356         SOC_IF_ERROR_RETURN
    357             (MODIFY_SERDES65LP_ANALOG_TX1r(unit, pc, PORT_ENABLE, 0x1));
    358         SOC_IF_ERROR_RETURN
    359             (_phy_serdes65lp_notify_resume(unit, port, PHY_STOP_PHY_DIS));
    360     } else {
    361         PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE);
    362 
    363         SOC_IF_ERROR_RETURN
    364             (_phy_serdes65lp_notify_stop(unit, port, PHY_STOP_PHY_DIS));
    365         SOC_IF_ERROR_RETURN
    366             (MODIFY_SERDES65LP_ANALOG_TX1r(unit, pc, PORT_DISABLE, 0x1));
    367     }
    368 
    369     return SOC_E_NONE;
    370 }
    371 
    372 
    373 STATIC int
    374 _phy_serdes65lp_sgmii_speed_set(int unit, soc_port_t port, int speed)
    375 {
    376     uint16 mii_ctrl;
    377     phy_ctrl_t  *pc;
    378 
    379     pc = INT_PHY_SW_STATE(unit, port);
    380 
    381     switch (speed) {
    382         case 10:
    383             mii_ctrl = MII_CTRL_SS_10;
    384             break;
    385         case 100:
    386             mii_ctrl = MII_CTRL_SS_100;
    387             break;
    388         case 0:
    389         case 1000:  
    390             mii_ctrl = MII_CTRL_SS_1000;
    391             break;
    392         default:
    393             return SOC_E_CONFIG;
    394     } 
    395     SOC_IF_ERROR_RETURN
    396         (MODIFY_SERDES65LP_MII_CTRLr(unit, pc, mii_ctrl, MII_CTRL_SS_MASK));
    397     return SOC_E_NONE;
    398 }
    399 
    400 STATIC int
    401 _phy_serdes65lp_1000x_speed_set(int unit, soc_port_t port, int speed)
    402 {
    403     phy_ctrl_t  *pc;
    404     uint16       data, mask;
    405 
    406     pc = INT_PHY_SW_STATE(unit, port);
    407 
    408     if ((speed != 0) && (speed != 100) && (speed != 1000)) { 
    409         return SOC_E_CONFIG;
    410     }
    411 
    412     /* Disable autodetect between 100FX and 1000X mode */
    413     mask = LP_FX100_CONTROL1_EN; 
    414     data = (speed == 100) ? LP_FX100_CONTROL1_EN : 0;
    415     SOC_IF_ERROR_RETURN
    416         (MODIFY_SERDES65LP_FX100_CTRL1r(unit, pc, data, mask));
    417 
    418     /* Allow extended packet length ( > 9 kilo bytes) */
    419     /* NB: The link takes longer to link up if extended packet length
    420      *     is enabled. Need to set this everytime speed changes.
    421      */  
    422     SOC_IF_ERROR_RETURN
    423         (WRITE_SERDES65LP_FX100_CTRL2r(unit, pc, 
    424                                         LP_FX100_CONTROL2_EXT_PKT_SZ));
    425     return SOC_E_NONE;
    426 }
    427 
    428 /*
    429  * Function:    
    430  *      phy_serdes65lp_speed_set
    431  * Purpose:     
    432  *      Set the current operating speed (forced).
    433  * Parameters:
    434  *      unit - StrataSwitch unit #.
    435  *      port - StrataSwitch port #. 
    436  *      speed - Speed to set.
    437  * Returns:     
    438  *      SOC_E_XXX
    439  */
    440 
    441 STATIC int
    442 phy_serdes65lp_speed_set(int unit, soc_port_t port, int speed)
    443 {
    444     uint16              fiber_status;
    445     phy_ctrl_t  *pc;
    446     int                 rv;
    447 
    448     pc = INT_PHY_SW_STATE(unit, port);
    449     rv = SOC_E_NONE;
    450 
    451     SOC_IF_ERROR_RETURN
    452         (READ_SERDES65LP_1000X_STAT1r(unit, pc, &fiber_status));
    453 
    454     if (fiber_status & LP_1000X_STATUS1_SGMII_MODE) {
    455         rv = _phy_serdes65lp_sgmii_speed_set(unit, port, speed);
    456     } else {
    457         rv = _phy_serdes65lp_1000x_speed_set(unit, port, speed);
    458     }
    459 
    460     if (SOC_SUCCESS(rv)) {
    461         pc->fiber.force_speed = speed;
    462     }
    463 
    464     LOG_INFO(BSL_LS_SOC_PHY,
    465              (BSL_META_U(unit,
    466                          "phy_serdes65lp_speed_set: u=%d p=%d speed=%d rv=%d\n"),
    467               unit, port, speed, rv));
    468     
    469     return rv;
    470 }
    471 
    472 STATIC int 
    473 _phy_serdes65lp_sgmii_speed_get(int unit, soc_port_t port, int *speed)
    474 {
    475     uint16 mii_ctrl, mii_stat, mii_anp;
    476     phy_ctrl_t  *pc;
    477 
    478     pc = INT_PHY_SW_STATE(unit, port);
    479 
    480     SOC_IF_ERROR_RETURN
    481         (READ_SERDES65LP_MII_CTRLr(unit, pc, &mii_ctrl));
    482     SOC_IF_ERROR_RETURN
    483         (READ_SERDES65LP_MII_STATr(unit, pc, &mii_stat));
    484  
    485     if (mii_ctrl & MII_CTRL_AE) { /*Auto-negotiation enabled */  
    486         if (!(mii_stat & MII_STAT_AN_DONE)) { /* Auto-neg NOT complete */
    487             *speed = 0;
    488             return SOC_E_NONE;
    489         } else { /* Read Advertise link partner */
    490             SOC_IF_ERROR_RETURN
    491                 (READ_SERDES65LP_MII_ANPr(unit, pc, &mii_anp));
    492             if (mii_anp & MII_ANP_SGMII_MODE) {
    493                 switch(mii_anp & MII_ANP_SGMII_SS_MASK) {
    494                 case MII_ANP_SGMII_SS_1000:
    495                     *speed = 1000;
    496                     break;
    497                 case MII_ANP_SGMII_SS_100:
    498                     *speed = 100;
    499                     break;
    500                 case MII_ANP_SGMII_SS_10:
    501                     *speed = 10;
    502                     break;
    503                 default:
    504                     return SOC_E_INTERNAL;
    505                 }
    506             }
    507         }
    508     } else {  /* Forced speed */
    509         switch(mii_ctrl & MII_CTRL_SS_MASK) {
    510         case MII_CTRL_SS_10:
    511             *speed = 10; 
    512             break;
    513         case MII_CTRL_SS_100:
    514             *speed = 100;
    515             break;
    516         case MII_CTRL_SS_1000:
    517             *speed = 1000;
    518             break;
    519         default:
    520             return SOC_E_INTERNAL;
    521         } 
    522     }
    523     return SOC_E_NONE;
    524 }
    525 
    526 STATIC int
    527 _phy_serdes65lp_1000x_speed_get(int unit, soc_port_t port, int *speed)
    528 {
    529     phy_ctrl_t  *pc;
    530     uint16       fx100_control;
    531 
    532     pc = INT_PHY_SW_STATE(unit, port);
    533 
    534     *speed = 1000;
    535 
    536     SOC_IF_ERROR_RETURN 
    537         (READ_SERDES65LP_FX100_CTRL1r(unit, pc, &fx100_control));
    538     *speed = (fx100_control & LP_FX100_CONTROL1_EN) ? 100 : 1000; 
    539 
    540     return SOC_E_NONE;
    541 }
    542 
    543 /*
    544  * Function:    
    545  *      phy_serdes65lp_speed_get
    546  * Purpose:     
    547  *      Get the current operating speed. If autoneg is enabled, 
    548  *      then operating mode is returned, otherwise forced mode is returned.
    549  * Parameters:
    550  *      unit - StrataSwitch unit #.
    551  *      port - StrataSwitch port #. 
    552  *      speed - Current port speed. 
    553  * Returns:     
    554  *      SOC_E_XXX
    555  */
    556 
    557 STATIC int
    558 phy_serdes65lp_speed_get(int unit, soc_port_t port, int *speed)
    559 {
    560     uint16              fiber_status;
    561     phy_ctrl_t  *pc;
    562 
    563     pc = INT_PHY_SW_STATE(unit, port);
    564 
    565     *speed = 1000;
    566 
    567     SOC_IF_ERROR_RETURN
    568         (READ_SERDES65LP_1000X_STAT1r(unit, pc, &fiber_status));
    569 
    570     if (fiber_status & LP_1000X_STATUS1_SGMII_MODE) {
    571         SOC_IF_ERROR_RETURN
    572             (_phy_serdes65lp_sgmii_speed_get(unit, port, speed));
    573     } else {
    574         SOC_IF_ERROR_RETURN
    575             (_phy_serdes65lp_1000x_speed_get(unit, port, speed));
    576     }
    577 
    578     return SOC_E_NONE;
    579 }
    580 
    581 /*
    582  * Function:    
    583  *      phy_serdes65lp_an_set
    584  * Purpose:     
    585  *      Enable or disabled auto-negotiation on the specified port.
    586  * Parameters:
    587  *      unit - StrataSwitch unit #.
    588  *      port - StrataSwitch port #. 
    589  *      an - Boolean, if true, auto-negotiation is enabled 
    590  *              (and/or restarted). If false, autonegotiation is disabled.
    591  * Returns:     
    592  *      SOC_E_XXX
    593  */
    594 
    595 STATIC int
    596 phy_serdes65lp_an_set(int unit, soc_port_t port, int an)
    597 {
    598     phy_ctrl_t  *pc;
    599     uint16             an_enable;
    600     uint16             auto_det;
    601 
    602     pc = INT_PHY_SW_STATE(unit, port);
    603 
    604     LOG_INFO(BSL_LS_SOC_PHY,
    605              (BSL_META_U(unit,
    606                          "phy_serdes65lp_an_set: u=%d p=%d an=%d\n"),
    607               unit, port, an));
    608 
    609     an_enable = 0; 
    610     auto_det  = 0;
    611     if (an) {
    612         an_enable = MII_CTRL_AE | MII_CTRL_RAN;
    613         if (soc_property_port_get(unit, port,
    614                                   spn_SERDES_AUTOMEDIUM, FALSE)) {
    615             auto_det = LP_1000X_AUTO_DETECT; 
    616         }
    617 
    618         /* Make sure SerDes is not in forced 100FX mode when enabling autoneg.
    619          */
    620         SOC_IF_ERROR_RETURN
    621             (MODIFY_SERDES65LP_FX100_CTRL1r(unit, pc, 0, LP_FX100_CONTROL1_EN));
    622 
    623     }
    624 
    625     SOC_IF_ERROR_RETURN
    626         (MODIFY_SERDES65LP_1000X_CTRL1r(unit, pc, 
    627                        auto_det, LP_1000X_AUTO_DETECT));
    628 
    629     /* Enable/Disable and Restart autonegotiation (self-clearing bit) */
    630     SOC_IF_ERROR_RETURN
    631         (MODIFY_SERDES65LP_MII_CTRLr(unit, pc, an_enable,
    632                        MII_CTRL_AE | MII_CTRL_RAN));
    633 
    634     pc->fiber.autoneg_enable = an;
    635 
    636     return SOC_E_NONE;
    637 }
    638 
    639 /*
    640  * Function:    
    641  *      phy_serdes65lp_ability_get
    642  * Purpose:     
    643  *      Get the abilities of the internal PHY.
    644  * Parameters:
    645  *      unit - StrataSwitch unit #.
    646  *      port - StrataSwitch port #. 
    647  *      mode - (OUT) Port mode mask indicating supported options/speeds.
    648  * Returns:     
    649  *      SOC_E_XXX
    650  */
    651 
    652 STATIC int
    653 phy_serdes65lp_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode)
    654 {
    655     uint16      fiber_status;
    656     phy_ctrl_t *pc;
    657 
    658     pc = INT_PHY_SW_STATE(unit, port);
    659     SOC_IF_ERROR_RETURN
    660         (READ_SERDES65LP_1000X_STAT1r(unit, pc, &fiber_status));
    661 
    662     if (fiber_status & LP_1000X_STATUS1_SGMII_MODE) {
    663         *mode = (SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB_FD |
    664                  SOC_PM_LB_PHY | SOC_PM_GMII);
    665     } else {
    666         *mode = (SOC_PM_1000MB_FD | SOC_PM_100MB_FD |
    667                  SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM | SOC_PM_AN | 
    668                  SOC_PM_LB_PHY | SOC_PM_GMII);
    669     }
    670 
    671 
    672     
    673     return SOC_E_NONE;
    674 }
    675 
    676 /*
    677  * Function:
    678  *      _phy_serdes65lp_medium_config_update
    679  * Purpose:
    680  *      Update the PHY with config parameters
    681  * Parameters:
    682  *      unit - StrataSwitch unit #.
    683  *      port - StrataSwitch port #.
    684  *      cfg - Config structure.
    685  * Returns:
    686  *      SOC_E_XXX
    687  */
    688 
    689 STATIC int
    690 _phy_serdes65lp_medium_config_update(int unit, soc_port_t port,
    691                                 soc_phy_config_t *cfg)
    692 {
    693     SOC_IF_ERROR_RETURN
    694         (phy_serdes65lp_speed_set(unit, port, cfg->force_speed));
    695     SOC_IF_ERROR_RETURN
    696         (phy_serdes65lp_duplex_set(unit, port, cfg->force_duplex));
    697     SOC_IF_ERROR_RETURN
    698         (phy_serdes_adv_local_set(unit, port, cfg->autoneg_advert));
    699     SOC_IF_ERROR_RETURN
    700         (phy_serdes65lp_an_set(unit, port, cfg->autoneg_enable));
    701 
    702     return SOC_E_NONE;
    703 }
    704 
    705 /*
    706  * Function:
    707  *      phy_serdes65lp_medium_config_set
    708  * Purpose:
    709  *      Set the operating parameters that are automatically selected
    710  *      when medium switches type.
    711  * Parameters:
    712  *      unit - StrataSwitch unit #
    713  *      port - Port number
    714  *      medium - SOC_PORT_MEDIUM_COPPER/FIBER
    715  *      cfg - Operating parameters
    716  * Returns:
    717  *      SOC_E_XXX
    718  */
    719 
    720 STATIC int
    721 phy_serdes65lp_medium_config_set(int unit, soc_port_t port, 
    722                            soc_port_medium_t  medium,
    723                            soc_phy_config_t  *cfg)
    724 {
    725 
    726     phy_ctrl_t    *pc;
    727 
    728     pc = INT_PHY_SW_STATE(unit, port);
    729 
    730     /*
    731      * Changes take effect immediately if the target medium is active.
    732      */
    733     switch (medium) {
    734     case SOC_PORT_MEDIUM_FIBER:
    735         /* Change the fiber modes */
    736 
    737         sal_memcpy(&pc->fiber, cfg, sizeof (pc->fiber));
    738 
    739         pc->fiber.autoneg_advert &= ADVERT_ALL_FIBER;
    740         if (PHY_FIBER_MODE(unit, port)) {
    741             SOC_IF_ERROR_RETURN
    742                 (_phy_serdes65lp_medium_config_update(unit, port, &pc->fiber));
    743         }
    744         return SOC_E_NONE;
    745     case SOC_PORT_MEDIUM_COPPER:
    746         return SOC_E_UNAVAIL;
    747     default:
    748         return SOC_E_PARAM;
    749     }
    750 }
    751 
    752 STATIC int
    753 _phy_serdes65lp_control_tx_driver_set(int unit, soc_port_t port,
    754                                 soc_phy_control_t type, uint32 value)
    755 {
    756     uint16       data;  /* Temporary holder of reg value to be written */
    757     uint16       mask;  /* Bit mask of reg value to be updated */
    758     phy_ctrl_t  *pc;    /* PHY software state */
    759 
    760     pc = INT_PHY_SW_STATE(unit, port);
    761 
    762     SOC_IF_ERROR_RETURN
    763         (READ_SERDES65LP_1000X_STAT1r(unit, pc, &data));
    764 
    765     if (data & LP_1000X_STATUS1_SGMII_MODE) {
    766         /* SGMII mode */
    767         switch(type) {
    768         case SOC_PHY_CONTROL_PREEMPHASIS:
    769              data = (uint16)(value & 0xf);
    770              data = data << 7;
    771              mask = 0x0780;
    772              SOC_IF_ERROR_RETURN
    773                  (MODIFY_SERDES65LP_ANALOG_TX1r(unit, pc, data, mask));
    774              break;
    775         case SOC_PHY_CONTROL_DRIVER_CURRENT:
    776              data = (uint16)(value & 0xF);
    777              data = data << 12;
    778              mask = 0xf000;
    779              SOC_IF_ERROR_RETURN
    780                  (MODIFY_SERDES65LP_ANALOG_TX1r(unit, pc, data, mask));
    781              break;
    782         case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
    783              data = (uint16)(value & 0xf);
    784              mask = 0x000f;
    785              SOC_IF_ERROR_RETURN
    786                 (MODIFY_SERDES65LP_ANALOG_TX2r(unit, pc, data, mask));
    787              break;
    788         default:
    789              /* should never get here */
    790              return SOC_E_PARAM;
    791         }
    792     } else {
    793         /* 1000X mode */
    794         switch(type) {
    795         case SOC_PHY_CONTROL_PREEMPHASIS:
    796              data = (uint16)(value & 0xf);
    797              data = data << 7;
    798              mask = 0x0780;
    799              SOC_IF_ERROR_RETURN
    800                 (MODIFY_SERDES65LP_ANALOG_TX1r(unit, pc, data, mask));
    801              break;
    802         case SOC_PHY_CONTROL_DRIVER_CURRENT:
    803              data = (uint16)(value & 0xf);
    804              data = data << 1; 
    805              mask = 0x001e;
    806              SOC_IF_ERROR_RETURN
    807                 (MODIFY_SERDES65LP_ANALOG_TX_AMPr(unit, pc, data, mask));
    808              break;
    809         case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
    810              data = (uint16)(value & 0xf);
    811              data = data << 5;
    812              mask = 0x01e0;
    813              SOC_IF_ERROR_RETURN
    814                 (MODIFY_SERDES65LP_ANALOG_TX_AMPr(unit, pc, data, mask));
    815              break;
    816         default:
    817              /* should never get here */
    818              return SOC_E_PARAM;
    819         }
    820     }
    821 
    822     return SOC_E_NONE;
    823 }
    824 
    825 STATIC int
    826 _phy_serdes65lp_control_tx_driver_get(int unit, soc_port_t port,
    827                                 soc_phy_control_t type, uint32 *value)
    828 {
    829     uint16         data;   /* Temporary holder of a reg value */
    830     phy_ctrl_t    *pc;     /* PHY software state */
    831 
    832     pc = INT_PHY_SW_STATE(unit, port);
    833 
    834     SOC_IF_ERROR_RETURN
    835         (READ_SERDES65LP_1000X_STAT1r(unit, pc, &data));
    836 
    837     if (data & LP_1000X_STATUS1_SGMII_MODE) {
    838         /* SGMII mode */
    839         switch(type) {
    840         case SOC_PHY_CONTROL_PREEMPHASIS:
    841              SOC_IF_ERROR_RETURN
    842                  (READ_SERDES65LP_ANALOG_TX1r(unit, pc, &data));
    843              *value = (data & 0x0780) >> 7;
    844              break;
    845         case SOC_PHY_CONTROL_DRIVER_CURRENT:
    846              SOC_IF_ERROR_RETURN
    847                  (READ_SERDES65LP_ANALOG_TX1r(unit, pc, &data));
    848              *value = (data & 0xf000) >> 12;
    849              break;
    850         case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
    851              SOC_IF_ERROR_RETURN
    852                 (READ_SERDES65LP_ANALOG_TX2r(unit, pc, &data));
    853              *value = (data & 0x000f);
    854              break;
    855         default:
    856              /* should never get here */
    857              return SOC_E_PARAM;
    858         }
    859     } else {
    860         /* 1000X mode */
    861         switch(type) {
    862         case SOC_PHY_CONTROL_PREEMPHASIS:
    863              SOC_IF_ERROR_RETURN
    864                 (READ_SERDES65LP_ANALOG_TX1r(unit, pc, &data));
    865              *value = (data & 0x0780) >> 7;
    866              break;
    867         case SOC_PHY_CONTROL_DRIVER_CURRENT:
    868              SOC_IF_ERROR_RETURN
    869                 (READ_SERDES65LP_ANALOG_TX_AMPr(unit, pc, &data));
    870              *value = (data & 0x001e) >> 1;
    871              break;
    872         case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
    873              SOC_IF_ERROR_RETURN
    874                 (READ_SERDES65LP_ANALOG_TX_AMPr(unit, pc, &data));
    875              *value = (data & 0x01e0) >> 5;
    876              break;
    877         default:
    878              /* should never get here */
    879              return SOC_E_PARAM;
    880         }
    881     }
    882 
    883     return SOC_E_NONE;
    884 }
    885 
    886 STATIC int
    887 _phy_serdes65lp_control_linkdown_transmit_set(int unit, soc_port_t port, 
    888                                                uint32 value)
    889 {
    890     uint16        data;
    891     phy_ctrl_t    *pc;
    892 
    893     pc = INT_PHY_SW_STATE(unit, port);
    894 
    895     if (value) {
    896         data = LP_1000X_FORCE_XMIT_DATA_ON_TXSIDE | LP_1000X_FILTER_FORCE_EN |
    897                LP_1000X_FALSE_LNK_DIS;
    898         SOC_IF_ERROR_RETURN
    899             (WRITE_SERDES65LP_1000X_CTRL2r(unit, pc, data));
    900     } else {
    901         data = LP_1000X_FILTER_FORCE_EN | LP_1000X_FALSE_LNK_DIS |
    902                LP_1000X_PAR_DET_EN;
    903         SOC_IF_ERROR_RETURN
    904             (WRITE_SERDES65LP_1000X_CTRL2r(unit, pc, data));
    905     }
    906 
    907     return SOC_E_NONE;
    908 }
    909 
    910 
    911 STATIC int
    912 _phy_serdes65lp_control_linkdown_transmit_get(int unit, soc_port_t port, 
    913                                                uint32 *value)
    914 {
    915     uint16        data;
    916     phy_ctrl_t    *pc;
    917 
    918     pc = INT_PHY_SW_STATE(unit, port);
    919 
    920     SOC_IF_ERROR_RETURN
    921         (READ_SERDES65LP_1000X_CTRL2r(unit, pc, &data));
    922 
    923     *value = (data & LP_1000X_FORCE_XMIT_DATA_ON_TXSIDE) ? 1 : 0;
    924 
    925     return SOC_E_NONE;
    926 }
    927 
    928 
    929 
    930 /*
    931  * Function:
    932  *      phy_physerdes65lp_control_set
    933  * Purpose:
    934  *      Configure PHY device specific control fucntion.
    935  * Parameters:
    936  *      unit  - StrataSwitch unit #.
    937  *      port  - StrataSwitch port #.
    938  *      type  - Control to update
    939  *      value - New setting for the control
    940  * Returns:
    941  *      SOC_E_NONE
    942  */
    943 STATIC int
    944 phy_serdes65lp_control_set(int unit, soc_port_t port,
    945                      soc_phy_control_t type, uint32 value)
    946 {
    947     int rv;
    948     phy_ctrl_t  *pc;    /* PHY software state */
    949     /* coverity[mixed_enums] */
    950     if ((type < 0) || (type >= SOC_PHY_CONTROL_COUNT)) {
    951         return SOC_E_PARAM;
    952     }
    953 
    954     pc = INT_PHY_SW_STATE(unit, port);
    955   
    956     rv = SOC_E_NONE;
    957     switch(type) {
    958     case SOC_PHY_CONTROL_PREEMPHASIS:
    959         /* fall through */
    960     case SOC_PHY_CONTROL_DRIVER_CURRENT:
    961         /* fall through */
    962     case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
    963         rv = _phy_serdes65lp_control_tx_driver_set(unit, port, type, value);
    964         break;
    965 
    966     case SOC_PHY_CONTROL_POWER:
    967         if (value == SOC_PHY_CONTROL_POWER_AUTO) {
    968             SOC_IF_ERROR_RETURN
    969                 (MODIFY_SERDES65LP_FX100_CTRL1r(unit,pc,
    970                           LP_FX100_CTRL1_AUTO_PWRDWN_EN,
    971                           LP_FX100_CTRL1_AUTO_PWRDWN_EN));
    972         } else if (value == SOC_PHY_CONTROL_POWER_FULL) {
    973             SOC_IF_ERROR_RETURN
    974                 (MODIFY_SERDES65LP_FX100_CTRL1r(unit,pc,
    975                           0,
    976                           LP_FX100_CTRL1_AUTO_PWRDWN_EN));
    977         } else {
    978             rv = SOC_E_PARAM;
    979         }
    980         break;
    981 
    982     case SOC_PHY_CONTROL_POWER_AUTO_SLEEP_TIME:
    983         /* sleep time configuration either 3 or 5 seconds, default 5 seconds */
    984         if ((value == 0) || (value >= 5000)) {
    985             SOC_IF_ERROR_RETURN
    986                 (MODIFY_SERDES65LP_FX100_CTRL1r(unit,pc,
    987                           0,
    988                           LP_FX100_CTRL1_PWRDWN_PERIOD_3SEC));
    989         } else {  /* config for 3 seconds if less than 5 seconds */
    990             SOC_IF_ERROR_RETURN
    991                 (MODIFY_SERDES65LP_FX100_CTRL1r(unit,pc,
    992                           LP_FX100_CTRL1_PWRDWN_PERIOD_3SEC,
    993                           LP_FX100_CTRL1_PWRDWN_PERIOD_3SEC));
    994         }      
    995         break;
    996 
    997     case SOC_PHY_CONTROL_POWER_AUTO_WAKE_TIME:
    998         /* wake time configuration either 42ms or 250ms, default 42ms */
    999         if ((value == 0) || (value < 250)) {
   1000             SOC_IF_ERROR_RETURN
   1001                 (MODIFY_SERDES65LP_FX100_CTRL1r(unit,pc,
   1002                           0,
   1003                           LP_FX100_CTRL1_PWRDWN_WAKEUP_250ms));
   1004         } else {  /* config for 250ms if >= 250ms  */
   1005             SOC_IF_ERROR_RETURN
   1006                 (MODIFY_SERDES65LP_FX100_CTRL1r(unit,pc,
   1007                           LP_FX100_CTRL1_PWRDWN_WAKEUP_250ms,
   1008                           LP_FX100_CTRL1_PWRDWN_WAKEUP_250ms));
   1009         }      
   1010         break;
   1011 
   1012      case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT:
   1013         rv = _phy_serdes65lp_control_linkdown_transmit_set(unit, port, value);
   1014         break;
   1015 
   1016     case SOC_PHY_CONTROL_PARALLEL_DETECTION:
   1017         /* enable 1000X parallel detect */
   1018         SOC_IF_ERROR_RETURN
   1019             (MODIFY_SERDES65LP_1000X_CTRL2r(unit, pc,
   1020                value? LP_1000X_PAR_DET_EN:0,
   1021                LP_1000X_PAR_DET_EN));
   1022         rv = SOC_E_NONE;
   1023         break;
   1024 
   1025     default:
   1026         rv = SOC_E_UNAVAIL;
   1027         break;
   1028     }
   1029     return rv;
   1030 }
   1031 
   1032 /*
   1033  * Function:
   1034  *      phy_serdes65lp_control_get
   1035  * Purpose:
   1036  *      Get current control settign of the PHY.
   1037  * Parameters:
   1038  *      unit  - StrataSwitch unit #.
   1039  *      port  - StrataSwitch port #.
   1040  *      type  - Control to update
   1041  *      value - (OUT)Current setting for the control
   1042  * Returns:    
   1043  *      SOC_E_NONE
   1044  */
   1045 STATIC int
   1046 phy_serdes65lp_control_get(int unit, soc_port_t port,
   1047                      soc_phy_control_t type, uint32 *value)
   1048 {
   1049     int rv;
   1050     uint16 reg_data;
   1051     phy_ctrl_t  *pc;    /* PHY software state */
   1052   
   1053     if (NULL == value) {
   1054         return SOC_E_PARAM;
   1055     }
   1056     /* coverity[mixed_enums] */
   1057     if ((type < 0) || (type >= SOC_PHY_CONTROL_COUNT)) {
   1058         return SOC_E_PARAM;
   1059     }
   1060   
   1061     pc = INT_PHY_SW_STATE(unit, port);
   1062     rv = SOC_E_NONE;
   1063 
   1064     switch(type) {
   1065     case SOC_PHY_CONTROL_PREEMPHASIS:
   1066         /* fall through */
   1067     case SOC_PHY_CONTROL_DRIVER_CURRENT:
   1068         /* fall through */
   1069     case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT:
   1070         rv = _phy_serdes65lp_control_tx_driver_get(unit, port, type, value);
   1071         break;
   1072 
   1073     case SOC_PHY_CONTROL_POWER:
   1074         SOC_IF_ERROR_RETURN
   1075             (READ_SERDES65LP_FX100_CTRL1r(unit,pc, &reg_data));
   1076 
   1077         if (reg_data & LP_FX100_CTRL1_AUTO_PWRDWN_EN) {
   1078             *value = SOC_PHY_CONTROL_POWER_AUTO;
   1079         } else {
   1080             *value = SOC_PHY_CONTROL_POWER_FULL;
   1081         }
   1082         break;
   1083 
   1084     case SOC_PHY_CONTROL_POWER_AUTO_SLEEP_TIME:
   1085         SOC_IF_ERROR_RETURN
   1086             (READ_SERDES65LP_FX100_CTRL1r(unit,pc, &reg_data));
   1087 
   1088         if (reg_data & LP_FX100_CTRL1_PWRDWN_PERIOD_3SEC) {
   1089             *value = 3000;
   1090         } else {
   1091             *value = 5000;
   1092         }
   1093         break;
   1094 
   1095     case SOC_PHY_CONTROL_POWER_AUTO_WAKE_TIME:
   1096         SOC_IF_ERROR_RETURN
   1097             (READ_SERDES65LP_FX100_CTRL1r(unit,pc, &reg_data));
   1098 
   1099         if (reg_data & LP_FX100_CTRL1_PWRDWN_WAKEUP_250ms) {
   1100             *value = 250;
   1101         } else {
   1102             *value = 42;
   1103         }
   1104         break;
   1105 
   1106     case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT:
   1107         rv = _phy_serdes65lp_control_linkdown_transmit_get(unit, port, value);
   1108         break;
   1109 
   1110     case SOC_PHY_CONTROL_PARALLEL_DETECTION:
   1111         SOC_IF_ERROR_RETURN
   1112             (READ_SERDES65LP_1000X_CTRL2r(unit, pc,&reg_data));
   1113         *value = reg_data & LP_1000X_PAR_DET_EN?  TRUE: FALSE;
   1114         rv = SOC_E_NONE;
   1115         break;
   1116 
   1117     default:
   1118         rv = SOC_E_UNAVAIL;
   1119         break;
   1120     }
   1121 
   1122     return rv;
   1123 }
   1124 
   1125 STATIC int
   1126 _phy_serdes65lp_tx_fifo_reset(int unit, soc_port_t port,uint32 speed)
   1127 {
   1128     uint16 data16;
   1129     phy_ctrl_t  *pc;
   1130     pc = INT_PHY_SW_STATE(unit, port);
   1131 
   1132     if (speed == 100) {
   1133         /* check if it is in 100fx mode */
   1134         SOC_IF_ERROR_RETURN
   1135             (READ_SERDES65LP_FX100_CTRL1r(unit,pc,&data16));
   1136 
   1137         if (data16 & LP_FX100_CONTROL1_EN) {
   1138             /* reset TX FIFO  */
   1139             SOC_IF_ERROR_RETURN
   1140                 (MODIFY_SERDES65LP_1000X_CTRL4r(unit,pc,
   1141                                  LP_1000X_DIG_RESET,
   1142                                  LP_1000X_DIG_RESET));
   1143             SOC_IF_ERROR_RETURN
   1144                 (MODIFY_SERDES65LP_1000X_CTRL4r(unit,pc,
   1145                                  0,
   1146                                  LP_1000X_DIG_RESET));
   1147         }
   1148     }
   1149     return SOC_E_NONE;
   1150 }
   1151 
   1152 STATIC int
   1153 phy_serdes65lp_notify(int unit, soc_port_t port,
   1154                  soc_phy_event_t event, uint32 value)
   1155 {
   1156     int             rv;
   1157 
   1158     if (event >= phyEventCount) {
   1159         return SOC_E_PARAM;
   1160     }
   1161 
   1162     rv = SOC_E_NONE;
   1163     switch(event) {
   1164     case phyEventInterface:
   1165         rv = (_phy_serdes65lp_notify_interface(unit, port, value));
   1166         break;
   1167     case phyEventDuplex:
   1168         rv = (_phy_serdes65lp_notify_duplex(unit, port, value));
   1169         break;
   1170     case phyEventSpeed:
   1171         rv = (_phy_serdes65lp_notify_speed(unit, port, value));
   1172         break;
   1173     case phyEventStop:
   1174         rv = (_phy_serdes65lp_notify_stop(unit, port, value));
   1175         break;
   1176     case phyEventResume:
   1177         rv = (_phy_serdes65lp_notify_resume(unit, port, value));
   1178         break;
   1179     case phyEventAutoneg:
   1180         rv = (phy_serdes65lp_an_set(unit, port, value));
   1181         break;
   1182     case phyEventTxFifoReset:
   1183         rv = (_phy_serdes65lp_tx_fifo_reset(unit, port, value));
   1184         break;
   1185     default:
   1186         rv = SOC_E_PARAM;
   1187         break;
   1188     }
   1189 
   1190     return rv;
   1191 }
   1192 
   1193 /*
   1194  * Function:
   1195  *      phy_serdes65lp_duplex_get
   1196  * Purpose:
   1197  *      Get the current operating duplex mode. 
   1198  * Parameters:
   1199  *      unit - StrataSwitch unit #.
   1200  *      port - StrataSwitch port #.
   1201  *      duplex - (OUT) Boolean, true indicates full duplex, false
   1202  *              indicates half.
   1203  * Returns:
   1204  *      SOC_E_NONE
   1205  */
   1206 
   1207 STATIC int
   1208 phy_serdes65lp_duplex_get(int unit, soc_port_t port, int *duplex)
   1209 {
   1210     uint16       data16;
   1211     uint16       mii_ctrl;
   1212     phy_ctrl_t  *pc;
   1213 
   1214     pc = INT_PHY_SW_STATE(unit, port);
   1215     *duplex = TRUE;
   1216 
   1217     SOC_IF_ERROR_RETURN
   1218         (READ_SERDES65LP_1000X_STAT1r(unit, pc, &data16));
   1219 
   1220 
   1221     if (!(data16 & LP_1000X_STATUS1_SGMII_MODE)) {   /* fiber mode */
   1222         *duplex = (data16 & LP_1000X_STATUS1_FULL_DUPLEX)?
   1223                   TRUE:FALSE;
   1224     } else { 
   1225         /* retrieve the duplex setting in SGMII mode */
   1226 
   1227         SOC_IF_ERROR_RETURN
   1228             (READ_SERDES65LP_MII_CTRLr(unit, pc, &mii_ctrl));
   1229 
   1230         if (mii_ctrl & MII_CTRL_AE) {
   1231             SOC_IF_ERROR_RETURN
   1232                 (READ_SERDES65LP_MII_ANPr(unit,pc,&data16));
   1233 
   1234             /* make sure link partner is also in SGMII mode
   1235              * otherwise fall through to use the FD bit in MII_CTRL reg
   1236              */
   1237             if (data16 & MII_ANP_SGMII_MODE) {
   1238                 if (data16 & MII_ANP_SGMII_FD) {
   1239                     *duplex = TRUE;
   1240                 } else {
   1241                     *duplex = FALSE;
   1242                 }
   1243                 return SOC_E_NONE;
   1244             }
   1245         }
   1246         *duplex = (mii_ctrl & MII_CTRL_FD) ? TRUE : FALSE;
   1247     } 
   1248     return SOC_E_NONE;
   1249 }
   1250 
   1251 /*
   1252  * Function:
   1253  *      phy_serdes65lp_duplex_set
   1254  * Purpose:
   1255  *      Set the current duplex mode (forced).
   1256  * Parameters:
   1257  *      unit - StrataSwitch unit #.
   1258  *      port - StrataSwitch port #.
   1259  *      duplex - Boolean, true indicates full duplex, false indicates half.
   1260  * Returns:
   1261  *      SOC_E_XXX
   1262  */
   1263 STATIC int
   1264 phy_serdes65lp_duplex_set(int unit, soc_port_t port, int duplex)
   1265 {
   1266     uint16       data16;
   1267     phy_ctrl_t  *pc;
   1268 
   1269     pc = INT_PHY_SW_STATE(unit, port);
   1270 
   1271     SOC_IF_ERROR_RETURN
   1272         (READ_SERDES65LP_1000X_STAT1r(unit, pc, &data16));
   1273 
   1274     if (!(data16 & LP_1000X_STATUS1_SGMII_MODE)) {  /* fiber mode */
   1275 
   1276         /* set only for 100FX. 1000X always fd */
   1277         SOC_IF_ERROR_RETURN
   1278             (READ_SERDES65LP_FX100_CTRL1r(unit, pc,&data16));
   1279 
   1280         if (data16 & LP_FX100_CONTROL1_EN) {
   1281             SOC_IF_ERROR_RETURN
   1282                 (MODIFY_SERDES65LP_FX100_CTRL1r(unit, pc, 
   1283                    duplex? LP_FX100_CONTROL1_FD:0,
   1284                    LP_FX100_CONTROL1_FD));
   1285         } 
   1286     } else {  /* SGMII mode */  
   1287         SOC_IF_ERROR_RETURN
   1288             (MODIFY_SERDES65LP_MII_CTRLr(unit, pc, 
   1289                             duplex? MII_CTRL_FD: 0, 
   1290                             MII_CTRL_FD));
   1291     }
   1292     return SOC_E_NONE;
   1293 }
   1294 
   1295 /*
   1296  * Variable:    phy_serdes65lpdrv_ge
   1297  * Purpose:     PHY Driver for Dozen SerDes Serdes PHY internal to Draco, 
   1298  */
   1299 phy_driver_t phy_serdes65lp_ge = {
   1300     "Internal 65nm SERDES PHY Driver",
   1301     phy_serdes65lp_init,
   1302     phy_null_reset,
   1303     phy_serdes_link_get, 
   1304     phy_serdes65lp_enable_set, 
   1305     phy_null_enable_get, 
   1306     phy_serdes65lp_duplex_set,          /* duplex_set */
   1307     phy_serdes65lp_duplex_get,          /* duplex_get */
   1308     phy_serdes65lp_speed_set, 
   1309     phy_serdes65lp_speed_get, 
   1310     phy_serdes_master_set,              /* master_set */
   1311     phy_serdes_master_get,              /* master_get */
   1312     phy_serdes65lp_an_set,
   1313     phy_serdes_an_get,
   1314     phy_serdes_adv_local_set,
   1315     phy_serdes_adv_local_get,
   1316     phy_serdes_adv_remote_get,
   1317     phy_serdes_lb_set,
   1318     phy_serdes_lb_get,
   1319     phy_serdes_interface_set, 
   1320     phy_serdes_interface_get, 
   1321     phy_serdes65lp_ability_get,
   1322     NULL,
   1323     NULL,
   1324     phy_null_mdix_set,
   1325     phy_null_mdix_get,
   1326     phy_null_mdix_status_get,
   1327     phy_serdes65lp_medium_config_set,
   1328     phy_serdes_medium_config_get,
   1329     phy_serdes_medium_status,
   1330     NULL,                               /* phy_cable_diag  */
   1331     NULL,                               /* phy_link_change */
   1332     phy_serdes65lp_control_set,         /* phy_control_set */
   1333     phy_serdes65lp_control_get,         /* phy_control_get */
   1334     phy_serdes_reg_read,
   1335     phy_serdes_reg_write,
   1336     phy_serdes_reg_modify,
   1337     phy_serdes65lp_notify
   1338 };
   1339 
   1340 /***********************************************************************
   1341  *
   1342  * PASS-THROUGH NOTIFY ROUTINES
   1343  */
   1344 
   1345 /*
   1346  * Function:
   1347  *      _phy_serdes65lp_notify_duplex
   1348  * Purpose:     
   1349  *      Program duplex if (and only if) serdes65lp is an intermediate PHY.
   1350  * Parameters:
   1351  *      unit - StrataSwitch unit #.
   1352  *      port - StrataSwitch port #. 
   1353  *      duplex - Boolean, TRUE indicates full duplex, FALSE
   1354  *              indicates half.
   1355  * Returns:     
   1356  *      SOC_E_XXX
   1357  * Notes:
   1358  *      If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to
   1359  *      talk directly to an external fiber module.
   1360  *
   1361  *      If PHY_FLAGS_FIBER is clear, the PHY is being used in
   1362  *      pass-through mode to talk to an external SGMII PHY.
   1363  *
   1364  *      When used in pass-through mode, autoneg must be turned off and
   1365  *      the speed/duplex forced to match that of the external PHY.
   1366  */
   1367 
   1368 STATIC int
   1369 _phy_serdes65lp_notify_duplex(int unit, soc_port_t port, uint32 duplex)
   1370 {
   1371     int                 fiber;
   1372     uint16              mii_ctrl;
   1373     phy_ctrl_t  *pc;
   1374 
   1375     fiber = PHY_FIBER_MODE(unit, port);
   1376     pc    = INT_PHY_SW_STATE(unit, port);
   1377     LOG_INFO(BSL_LS_SOC_PHY,
   1378              (BSL_META_U(unit,
   1379                          "phy_serdes65lp_notify_duplex: "
   1380                          "u=%d p=%d duplex=%d fiber=%d\n"),
   1381               unit, port, duplex, fiber));
   1382 
   1383     if (SAL_BOOT_SIMULATION) {
   1384         return SOC_E_NONE;
   1385     }
   1386 
   1387     if (fiber) {
   1388         SOC_IF_ERROR_RETURN
   1389             (MODIFY_SERDES65LP_MII_CTRLr(unit, pc, 
   1390                                   MII_CTRL_FD, MII_CTRL_FD));
   1391         return SOC_E_NONE;
   1392     }
   1393 
   1394     /* Put SERDES PHY in reset */
   1395 
   1396     SOC_IF_ERROR_RETURN
   1397         (_phy_serdes65lp_notify_stop(unit, port, PHY_STOP_DUPLEX_CHG));
   1398 
   1399     /* Update duplexity */
   1400     mii_ctrl = (duplex) ?MII_CTRL_FD : 0;
   1401     SOC_IF_ERROR_RETURN
   1402         (MODIFY_SERDES65LP_MII_CTRLr(unit, pc, mii_ctrl, MII_CTRL_FD));
   1403 
   1404     /* Take SERDES PHY out of reset */
   1405     SOC_IF_ERROR_RETURN
   1406         (_phy_serdes65lp_notify_resume(unit, port, PHY_STOP_DUPLEX_CHG));
   1407 
   1408     /* Autonegotiation must be turned off to talk to external PHY if
   1409      * SGMII autoneg is not enabled.
   1410      */
   1411     if (!PHY_SGMII_AUTONEG_MODE(unit, port)) {
   1412         SOC_IF_ERROR_RETURN
   1413             (phy_serdes65lp_an_set(unit, port, FALSE));
   1414     }
   1415     return SOC_E_NONE;
   1416 }
   1417 
   1418 /*
   1419  * Function:
   1420  *      _phy_serdes65lp_notify_speed
   1421  * Purpose:     
   1422  *      Program duplex if (and only if) serdes65lp is an intermediate PHY.
   1423  * Parameters:
   1424  *      unit - StrataSwitch unit #.
   1425  *      port - StrataSwitch port #. 
   1426  *      speed - Speed to program.
   1427  * Returns:     
   1428  *      SOC_E_XXX
   1429  * Notes:
   1430  *      If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to
   1431  *      talk directly to an external fiber module.
   1432  *
   1433  *      If PHY_FLAGS_FIBER is clear, the PHY is being used in
   1434  *      pass-through mode to talk to an external SGMII PHY.
   1435  *
   1436  *      When used in pass-through mode, autoneg must be turned off and
   1437  *      the speed/duplex forced to match that of the external PHY.
   1438  */
   1439 
   1440 STATIC int
   1441 _phy_serdes65lp_notify_speed(int unit, soc_port_t port, uint32 speed)
   1442 {
   1443     int          fiber;
   1444     uint16       fiber_status;
   1445     phy_ctrl_t  *pc;
   1446 
   1447     pc    = INT_PHY_SW_STATE(unit, port);
   1448     fiber = PHY_FIBER_MODE(unit, port);
   1449 
   1450     LOG_INFO(BSL_LS_SOC_PHY,
   1451              (BSL_META_U(unit,
   1452                          "_phy_serdes65lp_notify_speed: "
   1453                          "u=%d p=%d speed=%d fiber=%d\n"),
   1454               unit, port, speed, fiber));
   1455 
   1456     if (SAL_BOOT_SIMULATION) {
   1457         return SOC_E_NONE;
   1458     }
   1459  
   1460     SOC_IF_ERROR_RETURN
   1461         (READ_SERDES65LP_1000X_STAT1r(unit, pc, &fiber_status));
   1462 
   1463     if (fiber && !(fiber_status & LP_1000X_STATUS1_SGMII_MODE)) {
   1464         if ((speed != 0) && (speed != 100) && (speed != 1000)) {
   1465             return SOC_E_CONFIG;
   1466         }
   1467     }
   1468 
   1469     /* Put SERDES PHY in reset */
   1470     SOC_IF_ERROR_RETURN
   1471         (_phy_serdes65lp_notify_stop(unit, port, PHY_STOP_SPEED_CHG));
   1472 
   1473     /* Update speed */
   1474     SOC_IF_ERROR_RETURN
   1475         (_phy_serdes65lp_sgmii_speed_set(unit, port, speed));
   1476 
   1477     /* Take SERDES PHY out of reset */
   1478     SOC_IF_ERROR_RETURN
   1479         (_phy_serdes65lp_notify_resume(unit, port, PHY_STOP_SPEED_CHG));
   1480 
   1481     /* Autonegotiation must be turned off to talk to external PHY */
   1482     if (!PHY_SGMII_AUTONEG_MODE(unit, port) && !fiber) {
   1483         SOC_IF_ERROR_RETURN
   1484             (phy_serdes65lp_an_set(unit, port, FALSE));
   1485     }
   1486 
   1487     return SOC_E_NONE;
   1488 }
   1489 
   1490 /*
   1491  * Function:
   1492  *      _phy_serdes65lp_stop
   1493  * Purpose:
   1494  *      Put serdes65lp SERDES in or out of reset depending on conditions
   1495  */
   1496 
   1497 STATIC int
   1498 _phy_serdes65lp_stop(int unit, soc_port_t port)
   1499 {
   1500     uint16              mii_ctrl;
   1501     int                 stop, copper;
   1502     phy_ctrl_t  *pc;
   1503 
   1504     pc = INT_PHY_SW_STATE(unit, port);
   1505 
   1506     copper = (pc->stop & 
   1507               PHY_STOP_COPPER) != 0;
   1508 
   1509     stop = ((pc->stop &
   1510              (PHY_STOP_PHY_DIS |
   1511               PHY_STOP_DRAIN)) != 0 ||
   1512             (copper &&
   1513              (pc->stop &
   1514               (PHY_STOP_MAC_DIS |
   1515                PHY_STOP_DUPLEX_CHG |
   1516                PHY_STOP_SPEED_CHG)) != 0));
   1517 
   1518     LOG_INFO(BSL_LS_SOC_PHY,
   1519              (BSL_META_U(unit,
   1520                          "phy_serdes65lp_stop: u=%d p=%d copper=%d"
   1521                          " stop=%d flg=0x%x\n"),
   1522               unit, port, copper, stop,
   1523               pc->stop));
   1524 
   1525     mii_ctrl = (stop) ?MII_CTRL_PD : 0;
   1526     SOC_IF_ERROR_RETURN
   1527         (MODIFY_SERDES65LP_MII_CTRLr(unit, pc, mii_ctrl, MII_CTRL_PD));
   1528 
   1529     return SOC_E_NONE; 
   1530 }
   1531 
   1532 /*
   1533  * Function:
   1534  *      _phy_serdes65lp_notify_stop
   1535  * Purpose:     
   1536  *      Add a reason to put serdes65lp PHY in reset.
   1537  * Parameters:
   1538  *      unit - StrataSwitch unit #.
   1539  *      port - StrataSwitch port #. 
   1540  *      flags - Reason to stop
   1541  * Returns:     
   1542  *      SOC_E_XXX
   1543  */
   1544 
   1545 STATIC int
   1546 _phy_serdes65lp_notify_stop(int unit, soc_port_t port, uint32 flags)
   1547 {
   1548     INT_PHY_SW_STATE(unit, port)->stop |= flags;
   1549 
   1550     return _phy_serdes65lp_stop(unit, port);
   1551 }
   1552 
   1553 /*
   1554  * Function:
   1555  *      phy_serdes65lp_notify_resume
   1556  * Purpose:     
   1557  *      Remove a reason to put serdes65lp PHY in reset.
   1558  * Parameters:
   1559  *      unit - StrataSwitch unit #.
   1560  *      port - StrataSwitch port #. 
   1561  *      flags - Reason to stop
   1562  * Returns:     
   1563  *      SOC_E_XXX
   1564  */
   1565 
   1566 STATIC int
   1567 _phy_serdes65lp_notify_resume(int unit, soc_port_t port, uint32 flags)
   1568 {
   1569     INT_PHY_SW_STATE(unit, port)->stop &= ~flags;
   1570 
   1571     return _phy_serdes65lp_stop(unit, port);
   1572 }
   1573 
   1574 /*
   1575  * Function:
   1576  *      phy_serdes65lp_media_setup
   1577  * Purpose:     
   1578  *      Configure 
   1579  * Parameters:
   1580  *      unit - StrataSwitch unit #.
   1581  *      port - StrataSwitch port #. 
   1582  *      PHY_FIBER_MODE - Configure for fiber mode
   1583  *      fiber_pref - Fiber preferrred (if fiber mode)
   1584  * Returns:     
   1585  *      SOC_E_XXX
   1586  */
   1587 STATIC int
   1588 _phy_serdes65lp_notify_interface(int unit, soc_port_t port, uint32 intf)
   1589 {
   1590     phy_ctrl_t  *pc;
   1591     uint16       data16;
   1592 
   1593     pc = INT_PHY_SW_STATE(unit, port);
   1594   
   1595     data16 = 0;
   1596     if (intf != SOC_PORT_IF_SGMII) {
   1597         data16 = LP_1000X_FIBER_MODE;
   1598     }
   1599     SOC_IF_ERROR_RETURN
   1600         (MODIFY_SERDES65LP_1000X_CTRL1r(unit, pc,
   1601                                          data16, LP_1000X_FIBER_MODE));
   1602 
   1603     return SOC_E_NONE;
   1604 }
   1605 #else /* INCLUDE_SERDES_65LP */
   1606 int _phy_serdes65lp_not_empty;
   1607 #endif /* INCLUDE_SERDES_65LP */