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

phy54684.c (83764B)


      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:        phy54684.c
      8  * Purpose:     PHY driver for BCM54684
      9  *
     10  * Supported BCM546X Family of PHY devices:
     11  *
     12  *      Device  Ports   Media                           MAC Interface
     13  *      54684   8       Copper, Serdes                  SGMII
     14  *
     15  * Workarounds:
     16  *
     17  * References:
     18  * 54684-DS00-RDS.pdf        BCM54684 Data Sheet
     19  * MCS54684-ES100-RDS.pdf    Octal 1000/100/10BASE-T Gigabit Ethernet Transceiver
     20  *     
     21  *    +----------+   +---------------------------+
     22  *    |          |   |            +->Copper      |<--> Magnetic
     23  *    |  SGMII   |<->| SGMII    <-+              |
     24  *    | (SerDes) |   |            |              |
     25  *    |          |   |            +->SGMII/      |<--> (PHY)SFP (10/100/1000)
     26  *    +----------+   |               1000BASE-X/ |<--> 1000BASE-X SFP
     27  *        MAC        |               100BASE-FX  |<--> 100BASE-FX SFP
     28  *                   |                           |
     29  *                   |                           |
     30  *                   +---------------------------+
     31  *                               54684
     32  *
     33  * Notes:
     34  */ 
     35 
     36 #include <sal/types.h>
     37 #include <sal/core/spl.h>
     38 #include <shared/bsl.h>
     39 #include <soc/drv.h>
     40 #include <soc/debug.h>
     41 #include <soc/error.h>
     42 #include <soc/phyreg.h>
     43 
     44 #include <soc/phy.h>
     45 #include <soc/phy/phyctrl.h>
     46 #include <soc/phy/drv.h>
     47 
     48 #include "phydefs.h"      /* Must include before other phy related includes */
     49 
     50 #if defined(INCLUDE_PHY_54684)
     51 #include "phyconfig.h"    /* Must be the first phy include after phydefs.h */
     52 #include "phyident.h"
     53 #include "phyreg.h"
     54 #include "phyfege.h"
     55 #include "phy54684.h"
     56 
     57 #define AUTO_MDIX_WHEN_AN_DIS   0       /* Non-standard-compliant option */
     58 #define WORKAROUND_FIX          1
     59 
     60 #define ADVERT_ALL_COPPER \
     61         (SOC_PM_PAUSE | SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB)
     62 #define ADVERT_ALL_FIBER \
     63         (SOC_PM_PAUSE | SOC_PM_1000MB_FD)
     64 
     65 STATIC int _phy_54684_no_reset_setup(int unit, soc_port_t port);
     66 STATIC int _phy_54684_medium_change(int unit, soc_port_t port, int force_update);
     67 STATIC int phy_54684_duplex_set(int unit, soc_port_t port, int duplex);
     68 STATIC int phy_54684_speed_set(int unit, soc_port_t port, int speed);
     69 STATIC int phy_54684_master_set(int unit, soc_port_t port, int master);
     70 STATIC int phy_54684_adv_local_set(int unit, soc_port_t port,
     71                                   soc_port_mode_t mode);
     72 STATIC int phy_54684_autoneg_set(int unit, soc_port_t port, int autoneg);
     73 STATIC int phy_54684_mdix_set(int unit, soc_port_t port, soc_port_mdix_t mode);
     74 STATIC int phy_54684_speed_get(int unit, soc_port_t port, int *speed);
     75 
     76 /***********************************************************************
     77  *
     78  * HELPER FUNCTIONS
     79  */
     80 /*
     81  * Function:
     82  *      _phy_54684_medium_check
     83  * Purpose:
     84  *      Determine if chip should operate in copper or fiber mode.
     85  * Parameters:
     86  *      unit - StrataSwitch unit #.
     87  *      port - StrataSwitch port #.
     88  *      medium - (OUT) SOC_PORT_MEDIUM_XXX
     89  * Returns:
     90  *      SOC_E_XXX
     91  */
     92 STATIC int
     93 _phy_54684_medium_check(int unit, soc_port_t port, int *medium)
     94 {
     95     phy_ctrl_t    *pc = EXT_PHY_SW_STATE(unit, port);
     96     uint16         tmp = 0xffff;
     97 
     98     *medium = SOC_PORT_MEDIUM_COPPER;
     99 
    100     if (pc->automedium) {
    101         /* Read Mode Register (0x1c shadow 11111) */
    102         SOC_IF_ERROR_RETURN
    103             (READ_PHY54684_MODE_CTRLr(unit, pc, &tmp));
    104 
    105         if (pc->fiber.preferred) {
    106             /* 0x10 Fiber Signal Detect
    107              * 0x20 Copper Energy Detect
    108              */
    109             if ((tmp & 0x30) == 0x20) {
    110                 /* Only copper is link up */
    111                 *medium = SOC_PORT_MEDIUM_COPPER;
    112             } else {
    113                 *medium = SOC_PORT_MEDIUM_FIBER;
    114             }
    115         } else {
    116             if ((tmp & 0x30) == 0x10) {
    117                 /* Only fiber is link up */
    118                 *medium = SOC_PORT_MEDIUM_FIBER;
    119             } else {
    120                 *medium = SOC_PORT_MEDIUM_COPPER;
    121             }
    122         }
    123     } else {
    124         *medium = pc->fiber.preferred ? SOC_PORT_MEDIUM_FIBER:
    125                   SOC_PORT_MEDIUM_COPPER;
    126     }
    127    
    128     LOG_VERBOSE(BSL_LS_SOC_PHY,
    129                 (BSL_META_U(unit,
    130                             "_phy_54684_medium_check: "
    131                             "u=%d p=%d fiber_pref=%d 0x1c(11111)=%04x fiber=%d\n"),
    132                  unit, port, pc->fiber.preferred, tmp, 
    133                  (*medium == SOC_PORT_MEDIUM_FIBER) ? 1 : 0));
    134 
    135     return SOC_E_NONE;
    136 }
    137 
    138 /*
    139  * Function:
    140  *      phy_54684_medium_status
    141  * Purpose:
    142  *      Indicate the current active medium
    143  * Parameters:
    144  *      unit - StrataSwitch unit #.
    145  *      port - StrataSwitch port #.
    146  *      medium - (OUT) One of:
    147  *              SOC_PORT_MEDIUM_COPPER
    148  *              SOC_PORT_MEDIUM_FIBER
    149  * Returns:
    150  *      SOC_E_NONE
    151  */
    152 
    153 STATIC int
    154 phy_54684_medium_status(int unit, soc_port_t port, soc_port_medium_t *medium)
    155 {
    156     if (PHY_COPPER_MODE(unit, port)) {
    157         *medium = SOC_PORT_MEDIUM_COPPER;
    158     } else {
    159         *medium = SOC_PORT_MEDIUM_FIBER;
    160     }
    161     return SOC_E_NONE;
    162 }
    163 
    164 /*
    165  * Function:
    166  *      _phy_54684_medium_config_update
    167  * Purpose:
    168  *      Update the PHY with config parameters
    169  * Parameters:
    170  *      unit - StrataSwitch unit #.
    171  *      port - StrataSwitch port #.
    172  *      cfg - Config structure.
    173  * Returns:
    174  *      SOC_E_XXX
    175  */
    176 
    177 STATIC int
    178 _phy_54684_medium_config_update(int unit, soc_port_t port,
    179                                 soc_phy_config_t *cfg)
    180 {
    181     SOC_IF_ERROR_RETURN
    182         (phy_54684_speed_set(unit, port, cfg->force_speed));
    183     SOC_IF_ERROR_RETURN
    184         (phy_54684_duplex_set(unit, port, cfg->force_duplex));
    185     SOC_IF_ERROR_RETURN
    186         (phy_54684_master_set(unit, port, cfg->master));
    187     SOC_IF_ERROR_RETURN
    188         (phy_54684_adv_local_set(unit, port, cfg->autoneg_advert));
    189     SOC_IF_ERROR_RETURN
    190         (phy_54684_autoneg_set(unit, port, cfg->autoneg_enable));
    191     SOC_IF_ERROR_RETURN
    192         (phy_54684_mdix_set(unit, port, cfg->mdix));
    193 
    194     return SOC_E_NONE;
    195 }
    196 
    197 /***********************************************************************
    198  *
    199  * PHY54684 DRIVER ROUTINES
    200  */
    201 
    202 /*
    203  * Function:
    204  *      phy_54684_medium_config_set
    205  * Purpose:
    206  *      Set the operating parameters that are automatically selected
    207  *      when medium switches type.
    208  * Parameters:
    209  *      unit - StrataSwitch unit #
    210  *      port - Port number
    211  *      medium - SOC_PORT_MEDIUM_COPPER/FIBER
    212  *      cfg - Operating parameters
    213  * Returns:
    214  *      SOC_E_XXX
    215  */
    216 
    217 STATIC int
    218 phy_54684_medium_config_set(int unit, soc_port_t port, 
    219                            soc_port_medium_t  medium,
    220                            soc_phy_config_t  *cfg)
    221 {
    222     phy_ctrl_t    *pc;
    223     soc_phy_config_t *active_medium;  /* Currently active medium */
    224     soc_phy_config_t *change_medium;  /* Requested medium */
    225     soc_phy_config_t *other_medium;   /* The other medium */
    226     int               medium_update;
    227     soc_port_mode_t   advert_mask;
    228 
    229     if (NULL == cfg) {
    230         return SOC_E_PARAM;
    231     }
    232 
    233     pc            = EXT_PHY_SW_STATE(unit, port);
    234     medium_update = FALSE;
    235 
    236    switch (medium) {
    237     case SOC_PORT_MEDIUM_COPPER:
    238         if (!pc->automedium) {
    239             if (!PHY_COPPER_MODE(unit, port)) {
    240                 return SOC_E_UNAVAIL;
    241             }
    242             /* check if device is fiber capable before switching */
    243             if (cfg->preferred == 0) {
    244                 uint16 data;
    245 
    246                 SOC_IF_ERROR_RETURN
    247                     (READ_PHY54684_MODE_CTRLr(unit, pc, &data));
    248  
    249                 /* return if not fiber capable*/
    250                 if (!(data & 0x8)) {
    251                     return SOC_E_UNAVAIL;
    252                 }
    253             }
    254         }
    255 
    256         change_medium  = &pc->copper;
    257         other_medium   = &pc->fiber;
    258         advert_mask    = ADVERT_ALL_COPPER;
    259         break;
    260     case SOC_PORT_MEDIUM_FIBER:
    261         if (!pc->automedium && !PHY_FIBER_MODE(unit, port)) {
    262             return SOC_E_UNAVAIL;
    263         }
    264         change_medium  = &pc->fiber;
    265         other_medium   = &pc->copper;
    266         advert_mask    = ADVERT_ALL_FIBER;
    267         break;
    268     default:
    269         return SOC_E_PARAM;
    270     }
    271 
    272     /*
    273      * Changes take effect immediately if the target medium is active or
    274      * the preferred medium changes.
    275      */
    276     if (change_medium->enable != cfg->enable) {
    277         medium_update = TRUE;
    278     }
    279     if (change_medium->preferred != cfg->preferred) {
    280         /* Make sure that only one medium is preferred */
    281         other_medium->preferred = !cfg->preferred;
    282         medium_update = TRUE;
    283     }
    284 
    285     sal_memcpy(change_medium, cfg, sizeof(*change_medium));
    286     change_medium->autoneg_advert &= advert_mask;
    287 
    288     if (medium_update) {
    289         /* The new configuration may cause medium change. Check
    290          * and update medium.
    291          */
    292         SOC_IF_ERROR_RETURN
    293             (_phy_54684_medium_change(unit, port, TRUE));
    294     } else {
    295         active_medium = (PHY_COPPER_MODE(unit, port)) ?  
    296                             &pc->copper : &pc->fiber;
    297         if (active_medium == change_medium) {
    298             /* If the medium to update is active, update the configuration */
    299             SOC_IF_ERROR_RETURN
    300                 (_phy_54684_medium_config_update(unit, port, change_medium));
    301         }
    302     }
    303     return SOC_E_NONE;
    304 }
    305 
    306 /*
    307  * Function:
    308  *      phy_54684_medium_config_get
    309  * Purpose:
    310  *      Get the operating parameters that are automatically selected
    311  *      when medium switches type.
    312  * Parameters:
    313  *      unit - StrataSwitch unit #
    314  *      port - Port number
    315  *      medium - SOC_PORT_MEDIUM_COPPER/FIBER
    316  *      cfg - (OUT) Operating parameters
    317  * Returns:
    318  *      SOC_E_XXX
    319  */
    320 
    321 STATIC int
    322 phy_54684_medium_config_get(int unit, soc_port_t port, 
    323                            soc_port_medium_t medium,
    324                            soc_phy_config_t *cfg)
    325 {
    326     int            rv;
    327     phy_ctrl_t    *pc;
    328 
    329     pc = EXT_PHY_SW_STATE(unit, port);
    330     rv = SOC_E_NONE;
    331 
    332     switch (medium) {
    333     case SOC_PORT_MEDIUM_COPPER:
    334         if (pc->automedium || PHY_COPPER_MODE(unit, port)) {
    335             sal_memcpy(cfg, &pc->copper, sizeof (*cfg));
    336         } else {
    337             rv = SOC_E_UNAVAIL;
    338         }
    339         break;
    340     case SOC_PORT_MEDIUM_FIBER:
    341         if (pc->automedium || PHY_FIBER_MODE(unit, port)) {
    342             sal_memcpy(cfg, &pc->fiber, sizeof (*cfg));
    343         } else {
    344             rv = SOC_E_UNAVAIL;
    345         }
    346         break;
    347     default:
    348         rv = SOC_E_PARAM;
    349         break;
    350     }
    351 
    352     return rv;
    353 }
    354 
    355 /*
    356  * Function:
    357  *      _phy_54684_reset_setup
    358  * Purpose:
    359  *      Function to reset the PHY and set up initial operating registers.
    360  * Parameters:
    361  *      unit - StrataSwitch unit #.
    362  *      port - StrataSwitch port #.
    363  * Returns:
    364  *      SOC_E_XXX
    365  * Notes:
    366  *      Configures for copper/fiber according to the current
    367  *      setting of PHY_FLAGS_FIBER.
    368  */
    369 
    370 STATIC int
    371 _phy_54684_reset_setup(int unit, soc_port_t port)
    372 {
    373 
    374     phy_ctrl_t    *pc;
    375     soc_port_t     primary_port;
    376     int            index;
    377 
    378     pc = EXT_PHY_SW_STATE(unit, port);
    379 
    380     if (soc_phy_primary_and_offset_get(unit, port, &primary_port, &index) == SOC_E_NONE) {
    381         pc->lane_num = index;
    382     }
    383 
    384     /* Reset the PHY with previously registered callback function. 
    385      */
    386     SOC_IF_ERROR_RETURN(soc_phy_reset(unit, port));
    387 
    388     /* Disable super-isolate */
    389     SOC_IF_ERROR_RETURN
    390         (MODIFY_PHY54684_MII_POWER_CTRLr(unit, pc, 0x0, 1U<<5));
    391 
    392     SOC_IF_ERROR_RETURN
    393         (_phy_54684_no_reset_setup(unit, port));
    394 
    395     return SOC_E_NONE;
    396 }
    397 
    398 STATIC int
    399 _phy_54684_no_reset_setup(int unit, soc_port_t port)
    400 {
    401     phy_ctrl_t    *int_pc;
    402     phy_ctrl_t    *pc;
    403     uint16         data, mask;
    404 
    405     LOG_INFO(BSL_LS_SOC_PHY,
    406              (BSL_META_U(unit,
    407                          "_phy_54684_reset_setup: u=%d p=%d medium=%s\n"),
    408                          unit, port,
    409               PHY_COPPER_MODE(unit, port) ? "COPPER" : "FIBER"));
    410 
    411     pc      = EXT_PHY_SW_STATE(unit, port);
    412     int_pc  = INT_PHY_SW_STATE(unit, port);
    413 
    414     /*
    415      * Workaround for BCM54684 B0 chip
    416      * (not necessary for B1 and C0 chip)
    417      * Description: BCM54684 defaults into the SGMII/QSGMII-to-copper split 
    418      *    mode after reset. The BCM54684 device mode should default into the 
    419      *    QSMII-to-Copper mode after reset.
    420      */
    421     if(PHY_REVISION_CHECK(pc, PHY_BCM54684_MODEL, PHY_BCM54684_OUI, 0x0)) {
    422         SOC_IF_ERROR_RETURN
    423             (MODIFY_PHY54684_MODE_CTRLr(unit, pc, 0x0000, 0x0006));
    424     }
    425 
    426     /*
    427      * Workaround for BCM54684 B0 chip
    428      * Description: disable signal detect from pin to make fiber mode work
    429      */
    430     if(PHY_REVISION_CHECK(pc, PHY_BCM54684_MODEL, PHY_BCM54684_OUI, 0x0)) {
    431         SOC_IF_ERROR_RETURN
    432             (MODIFY_PHY54684_MISC_1000X_CONTROLr(unit, pc, 0x0000, 0x0020));
    433     } 
    434 
    435    /* SGMII interface to MAC */
    436     /* soc_phy_reset has reset the phy, powering down the unstrapped interface.
    437      * Make sure enabled interfaces are powered up. 
    438      */
    439     SOC_IF_ERROR_RETURN
    440         (MODIFY_PHY54684_1000X_MII_CTRLr(unit, pc, 0, MII_CTRL_PD));
    441 
    442     /* 1000Base-X FullDuplex will be clear after the phy been 
    443      * reset in phy54684
    444      */
    445     data = MII_CTRL_FD | MII_CTRL_SS_1000 | MII_CTRL_RAN | MII_CTRL_AE;
    446 
    447     SOC_IF_ERROR_RETURN
    448         (WRITE_PHY54684_1000X_MII_CTRLr(unit, pc, data));
    449    
    450     /* copper regs */
    451     if (!pc->copper.enable || (!pc->automedium && pc->fiber.preferred)) {
    452         /* Copper interface is not used. Powered down. */
    453         SOC_IF_ERROR_RETURN
    454             (MODIFY_PHY54684_MII_CTRLr(unit, pc, MII_CTRL_PD, MII_CTRL_PD));
    455     } else {
    456         SOC_IF_ERROR_RETURN
    457             (MODIFY_PHY54684_MII_CTRLr(unit, pc, 0, MII_CTRL_PD));
    458         SOC_IF_ERROR_RETURN
    459             (WRITE_PHY54684_MII_GB_CTRLr(unit, pc,  
    460                             MII_GB_CTRL_ADV_1000FD | MII_GB_CTRL_PT));
    461         SOC_IF_ERROR_RETURN
    462             (WRITE_PHY54684_MII_CTRLr(unit, pc, 
    463                 MII_CTRL_FD | MII_CTRL_SS_10 | 
    464                 MII_CTRL_SS_100 | MII_CTRL_AE));
    465     }
    466           
    467     /* Adjust timing and enable link speed led mode */
    468     SOC_IF_ERROR_RETURN
    469         (MODIFY_PHY54684_SPARE_CTRLr(unit, pc, 0x0006, 0x0006));
    470 
    471     if (NULL != int_pc) {
    472         SOC_IF_ERROR_RETURN
    473             (PHY_INIT(int_pc->pd, unit, port));
    474 
    475         if (PHY_FIBER_MODE(unit, port)) {
    476            /* Interface to MAC or internal SerDes is always SGMII mode.
    477             * Typical internal SerDes driver set the interface to 1000BASE-X,
    478             * if the media is fiber. Therefore, we need to force the interface 
    479             * of internal SerDes Driver to SGMII. */
    480            SOC_IF_ERROR_RETURN
    481                (PHY_NOTIFY(int_pc->pd, unit, port, phyEventInterface,
    482                            SOC_PORT_IF_SGMII));
    483         } 
    484 
    485         if (PHY_COPPER_MODE(unit, port) && 
    486             !PHY_SGMII_AUTONEG_MODE(unit, port)) {
    487             /* Make sure internal SerDes is in power down state 
    488              * until the MAC is ready. */
    489             SOC_IF_ERROR_RETURN
    490                 (PHY_NOTIFY(int_pc->pd, unit, port, 
    491                             phyEventStop, PHY_STOP_COPPER));
    492         }
    493     }
    494 
    495     /* Power down SerDes */
    496     SOC_IF_ERROR_RETURN
    497         (MODIFY_PHY54684_1000X_MII_CTRLr(unit, pc, MII_CTRL_PD, 
    498                                                MII_CTRL_PD));
    499 
    500     if (pc->fiber.enable && (pc->automedium || pc->fiber.preferred)) {
    501         /* remove power down of SerDes */
    502         SOC_IF_ERROR_RETURN
    503             (MODIFY_PHY54684_1000X_MII_CTRLr(unit, pc, 0, MII_CTRL_PD));
    504 
    505         /* set the advertisement of serdes */
    506         /* Disable half-duplex, full-duplex capable */
    507         SOC_IF_ERROR_RETURN
    508             (MODIFY_PHY54684_1000X_MII_ANAr(unit, pc, (1 << 5), 
    509                                                 (1 << 5) | (1 << 6)));
    510 
    511         /* Enable auto-detection between SGMII-slave and 1000BASE-X */
    512         SOC_IF_ERROR_RETURN
    513             (MODIFY_PHY54684_SGMII_SLAVEr(unit, pc, 1, 1));
    514 
    515         /* Restart SerDes autonegotiation */
    516         SOC_IF_ERROR_RETURN
    517             (WRITE_PHY54684_1000X_MII_CTRLr(unit, pc,
    518                 MII_CTRL_FD | MII_CTRL_SS_1000 | MII_CTRL_AE));
    519         
    520         switch (pc->fiber_detect) {
    521             case  2:         /* LED 2 */
    522             case -2:
    523             case  4:         /* LED 4 : kept for backward compatibility */
    524             case -4:
    525             case  10:        /* EN_10B for signal detect */
    526             case -10:
    527             case 0:         /* default */
    528                 /* GPIO Control/Status Register (0x1c shadow 01111) */
    529                 /* Change LED2 pin into an input */
    530                 SOC_IF_ERROR_RETURN
    531                     (MODIFY_PHY54684_LED_GPIO_CTRLr(unit, pc, 0x08, 0x08));
    532                 break;
    533             case  1:         /* PECL , no such pin in 54684 data sheet */
    534             case -1:
    535             default:
    536                 return SOC_E_CONFIG;
    537         }
    538         SOC_IF_ERROR_RETURN
    539             (MODIFY_PHY54684_MODE_CTRLr(unit, pc, 0x0002, 0x0006));        
    540     }
    541 
    542     /* Configure Auto-detect Medium (0x1c shadow 11110) */
    543     mask = 0x33f;               /* Auto-detect bit mask */
    544     data = 0;
    545     if (pc->automedium) {
    546         data |= 1 << 0;    /* Enable auto-detect medium */
    547     }
    548     /* When no link partner exists, fiber should be default medium */
    549     /* When both link partners exipc, fiber should be prefered medium
    550      * for the following reasons.
    551      * 1. Most fiber module generates signal detect when the fiber cable is
    552      *    plugged in regardless of any activity even if the link partner is
    553      *    powered down.
    554      * 2. Fiber mode consume much lesser power than copper mode.
    555      */
    556     if (pc->fiber.preferred) {
    557         data |= 1 << 1;    /* Fiber selected when both media are active */
    558         data |= 1 << 2;    /* Fiber selected when no medium is active */
    559     }
    560 
    561     /*
    562      * Enable internal inversion of LED2/SD input pin.  Used only if
    563      * the fiber module provides RX_LOS instead of Signal Detect.
    564      */
    565     if (pc->fiber_detect < 0) {
    566         data |= 1 << 8;    /* Fiber signal detect is active low from pin */
    567     }
    568     SOC_IF_ERROR_RETURN
    569         (MODIFY_PHY54684_AUTO_DETECT_MEDIUMr(unit, pc, data, mask));
    570 
    571 #if AUTO_MDIX_WHEN_AN_DIS
    572     /* Enable Auto-MDIX When autoneg disabled */
    573     SOC_IF_ERROR_RETURN
    574         (MODIFY_PHY54684_MII_MISC_CTRLr(unit, pc, 0x200, 0x200));
    575 #endif
    576 
    577     /*
    578      * Configure 1000X control2 register (0x8301 Bit[7] = 1) to turn off
    579      * carrier extension.  The Intel 7131 NIC does not accept carrier
    580      * extension and gets CRC errors.
    581      */
    582     if(pc->lane_num == 0){
    583          /* The phy address of QSGMII is the phy address pins + 8 */
    584         pc->phy_id =  pc->phy_id + 8;
    585         SOC_IF_ERROR_RETURN   
    586            (PHY54684_REG_WRITE(unit, pc, SOC_PHY_REG_RESERVE_ACCESS,   
    587                                      0x0000, 0x1f, 0xffd0));   
    588         SOC_IF_ERROR_RETURN   
    589            (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0000, 0x1e, 0x1f));   
    590         SOC_IF_ERROR_RETURN   
    591            (PHY54684_REG_WRITE(unit, pc, SOC_PHY_REG_RESERVE_ACCESS,   
    592                                      0x0000, 0x1f, 0x8300));   
    593         SOC_IF_ERROR_RETURN   
    594            (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x11, 0x0080, 0x0080));
    595 
    596         /* Revert the phy_id */
    597         pc->phy_id = pc->phy_id - 8;
    598     }
    599 
    600     /* Configure extended control register for led mode and jumbo frame support
    601      */
    602     mask  = (1 << 5) | (1 << 0);
    603 
    604     /* if using led link/activity mode, disable led traffic mode */
    605     if (pc->ledctrl & 0x10 || pc->ledselect == 0x01) {
    606         data = 0;
    607     } else {
    608         data  = 1 << 5;      /* Enable LEDs to indicate traffic status */
    609     }
    610     /* Configure Extended Control Register for Jumbo frames support */
    611     data |= 1 << 0;      /* Set high FIFO elasticity to support jumbo frames */
    612     SOC_IF_ERROR_RETURN
    613         (MODIFY_PHY54684_MII_ECRr(unit, pc, data, mask));
    614 
    615     /* Enable extended packet length (4.5k through 25k) */
    616     SOC_IF_ERROR_RETURN
    617         (MODIFY_PHY54684_MII_AUX_CTRLr(unit, pc, 0x4000, 0x4000));
    618 
    619     /* Configure LED selectors */
    620     data = ((pc->ledmode[1] & 0xf) << 4) | (pc->ledmode[0] & 0xf);
    621     SOC_IF_ERROR_RETURN
    622         (WRITE_PHY54684_LED_SELECTOR_1r(unit, pc, data));
    623 
    624     data = ((pc->ledmode[3] & 0xf) << 4) | (pc->ledmode[2] & 0xf);
    625     SOC_IF_ERROR_RETURN
    626         (WRITE_PHY54684_LED_SELECTOR_2r(unit, pc, data));
    627 
    628     data = (pc->ledctrl & 0x3ff);
    629     SOC_IF_ERROR_RETURN
    630         (WRITE_PHY54684_LED_CTRLr(unit, pc, data));
    631 
    632     SOC_IF_ERROR_RETURN
    633         (WRITE_PHY54684_EXP_LED_SELECTORr(unit, pc, pc->ledselect));
    634 
    635 #ifdef BCM_HURRICANE1_SUPPORT
    636 
    637 	if (SOC_IS_HURRICANE(unit))
    638 	{
    639 	    if (!pc->fiber.preferred)
    640 	    {
    641 	        /* LED initialization sequence for Hurricane*/
    642 	        /* Disable current LED mode. Write 1s to bits[3:0] of 
    643 	           GPIO control register (reg 1Ch, Shadow Fh).*/
    644 	        SOC_IF_ERROR_RETURN
    645 	            (MODIFY_PHY54684_LED_GPIO_CTRLr(unit, pc, 0xbc0f, 0xbc0f));
    646 	        /* Enable multimode LED mode. Write AAh to bits[7:0] of
    647 	           LED selectro register (reg 1Ch, Shadow Dh). */
    648 	        SOC_IF_ERROR_RETURN
    649 	            (MODIFY_PHY54684_LED_SELECTOR_1r(unit, pc, 0xb4aa, 0xb4aa));
    650 	        SOC_IF_ERROR_RETURN
    651 	            (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x17, 0x0f04, 0x0f04));
    652 	        SOC_IF_ERROR_RETURN
    653 	            (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x15, 0x0128, 0x0128));
    654 	        
    655 	    }
    656     }
    657 #endif
    658 
    659 #if WORKAROUND_FIX
    660     if(PHY_REVISION_CHECK(pc, PHY_BCM54684_MODEL, PHY_BCM54684_OUI, 0x0)) {
    661         /* 
    662          * Workaround for BCM54684 B0 chip 
    663          * (not necessary for B1 and C0 chip)
    664          * Description Device does not initialized to correct value.
    665          * Workaround The following script code should run to 
    666          * initialize the device after reset before normal operation.
    667          * Step 1) Perform the following write operations for 
    668          *         port 1 through port 8
    669          *     Write register 18h = 0C00h
    670          *     Write register 17h = 001fh
    671          *     Write register 15h = 0300h
    672          *     Write register 17h = 0601fh
    673          *     Write register 15h = 0002h
    674          *     Write register 17h = 0f75h
    675          *     Write register 15h = 0028h
    676          * Step 2) Perform the following write operations to port 4 only
    677          *     Write register 1C = AC60h
    678          *     Write register 1C = 0002h
    679          * Step 3) Perform the following write operations to port 3 only
    680          *     Write register 1C = AC5Eh
    681          * Step 4) Perform the following write operations to port 8 only
    682          *     Write register 1Ch = AC60h
    683          *     Write register 1Ch = B012h
    684          * Step 5) Perform the following write operations to port 7 only
    685          *     Write register 1Ch = AC5Eh 
    686          */            
    687 
    688         /* Step 1 */
    689         SOC_IF_ERROR_RETURN
    690             (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x18, 
    691                                      0x0C00, 0x0C00));
    692         SOC_IF_ERROR_RETURN
    693             (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x17, 
    694                                      0x001F, 0x001F));
    695         SOC_IF_ERROR_RETURN
    696             (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x15, 
    697                                      0x0300, 0x0300));
    698         SOC_IF_ERROR_RETURN
    699             (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x17, 
    700                                      0x601F, 0x601F));
    701         SOC_IF_ERROR_RETURN
    702             (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x15, 
    703                                      0x0002, 0x0002));
    704         SOC_IF_ERROR_RETURN
    705             (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x17, 
    706                                      0x0F75, 0x0F75));
    707         SOC_IF_ERROR_RETURN
    708             (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x15, 
    709                                      0x0028, 0x0028));
    710 
    711         if(pc->lane_num == 3) {
    712             /* Step 2 */
    713             SOC_IF_ERROR_RETURN
    714                 (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x000B, 0x1C, 
    715                                          0x0060, 0x0060));
    716             SOC_IF_ERROR_RETURN
    717                 (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x1C, 
    718                                          0x0002, 0x0002));
    719         }
    720 
    721         if(pc->lane_num == 2) {
    722             /* Step 3 */
    723             SOC_IF_ERROR_RETURN
    724                 (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x000B, 0x1C, 
    725                                          0x005E, 0x005E));
    726         }
    727 
    728         if(pc->lane_num == 7) {
    729             /* Step 4 */
    730             SOC_IF_ERROR_RETURN
    731                 (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x000B, 0x1C, 
    732                                          0x0060, 0x0060));
    733             SOC_IF_ERROR_RETURN
    734                 (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x000C, 0x1C, 
    735                                          0x0012, 0x0012));
    736         }
    737 
    738         if(pc->lane_num == 6) {
    739             /* Step 5 */
    740             SOC_IF_ERROR_RETURN
    741                 (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x000B, 0x1C, 
    742                                          0x005E, 0x005E));
    743         }
    744     }
    745 
    746     if(PHY_REVISION_CHECK(pc, PHY_BCM54684_MODEL, PHY_BCM54684_OUI, 0x1)) {
    747         /* 
    748          * Apply workaround for BCM54684 C0. 
    749          * Perform the following writes to power up the dual 1.25 Gig SerDes.
    750          */
    751         if((pc->lane_num == 6) || (pc->lane_num == 7)){
    752             SOC_IF_ERROR_RETURN
    753                 (MODIFY_PHY54684_EXP_SERDES_SGMII_RX_CTRLr(unit, pc, 0x0, 0x1));
    754             SOC_IF_ERROR_RETURN
    755                 (MODIFY_PHY54684_EXP_SERDES_SGMII_CTRLr(unit, pc, 0x0, 0x1));
    756         }
    757         if(pc->lane_num == 4) {
    758             SOC_IF_ERROR_RETURN
    759                 (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0f55, 0x15, 0x0, 0x1));
    760         }
    761     }
    762 
    763     if(PHY_REVISION_CHECK(pc, PHY_BCM54684_MODEL, PHY_BCM54684_OUI, 0x0)) {
    764         /* Workaround for BCM54684 B0 chip : 
    765             Operate at -20% TX-bias current versus the +20% setting.  
    766             This will drop the power by 40 mW/port but use shorter cable < 100m.
    767             How to reduce tx bias current to 20% : 
    768             Perform a modify to VDACCTRL<7:0> of Expansion Register 0x75 
    769             Set the VDACCTRL<7:0>="00010100" to get -20% TX  */
    770         SOC_IF_ERROR_RETURN
    771             (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0f75, 0x15, 0x14, 0xff));
    772     }
    773 
    774     if(PHY_REVISION_CHECK(pc, PHY_BCM54684_MODEL, PHY_BCM54684_OUI, 0x2)) {
    775         /* For bcm54684_D0 only.
    776          *
    777          *  Workaround for two tests:
    778          *  1. IEEE symmetry test
    779          *  2. EMI test
    780          */
    781         SOC_IF_ERROR_RETURN
    782             (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0f76, 0x15, 0x100));
    783         SOC_IF_ERROR_RETURN
    784             (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0f75, 0x15, 0x29));
    785         SOC_IF_ERROR_RETURN
    786             (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0f74, 0x15, 0xd087));
    787         SOC_IF_ERROR_RETURN
    788             (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0000, 0x18, 0x0800, 0x0800));
    789         SOC_IF_ERROR_RETURN
    790             (PHY54684_REG_WRITE(unit, pc, 0x00, 0x601f, 0x15, 0x0110));
    791 
    792         /* Issue for bcm54684_D0: Unstable link issue with Marvell PHY
    793          * Workaroud (PHY team confirmed workaround has been identified) :
    794          * Set Exp register 0x08.bit[9]=0. 
    795          * (BIT9 is AUTO EARLY DAC WAKE power savings mode)
    796          */
    797         SOC_IF_ERROR_RETURN
    798             (MODIFY_PHY54684_10BT_CTRLr(unit, pc, 0x0000, 0x0200));
    799     }
    800 
    801     if (soc_property_get(unit, spn_PHY_53314_CLK156, 1)) {
    802         /* Workaroud :
    803          * If the bit9 of GPHY port5 (port0--7) Exp Reg55h is 0,
    804          * the fiber will be operating 25% faster.
    805          * Thus it will cause link problem.
    806          * So we set the bit9 of GPHY port5 (port0--7) Exp Reg55h to 1 here
    807          * It is for 156.25MHz only.
    808          */
    809         if(pc->lane_num == 5) {        
    810              /* The phy address of QSGMII is the phy address pins + 8 */
    811             SOC_IF_ERROR_RETURN
    812             (PHY54684_REG_MODIFY(unit, pc, 0x00, 0x0F55, 0x15,
    813                 0x0200, 0x0200));
    814         }
    815     }
    816 
    817     if (soc_feature(unit, soc_feature_hawkeye_a0_war)) {
    818         /* Workaroud : Fix predriver and driver settings 
    819          *             in TX DFS registers of QSGMII 
    820          *             (Enhancement based on analog designer feedback)
    821          * change the TX driver register to value 170H 
    822          * for revision A0 of HAWKEYE and PHY 54684
    823          * (Addr : 0x8067)
    824          */
    825         if(pc->lane_num == 0) {
    826             /* The phy address of QSGMII is the phy address pins + 8 */
    827             pc->phy_id =  pc->phy_id + 8;
    828             SOC_IF_ERROR_RETURN   
    829                 (PHY54684_REG_WRITE(unit, pc, SOC_PHY_REG_RESERVE_ACCESS,   
    830                                          0x0000, 0x1f, 0xffd0));   
    831             SOC_IF_ERROR_RETURN   
    832                (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0000, 0x1e, 0x1f));   
    833             SOC_IF_ERROR_RETURN   
    834                 (PHY54684_REG_WRITE(unit, pc, SOC_PHY_REG_RESERVE_ACCESS,   
    835                                          0x0000, 0x1f, 0x8060));   
    836             SOC_IF_ERROR_RETURN   
    837                 (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0000, 0x17, 0x170));   
    838             /* Revert the phy_id */
    839             pc->phy_id = pc->phy_id - 8;
    840         }   
    841 
    842         /* Workaroud :
    843          * Make disparity error check configuration in 
    844          * A0 and B0 to reflect the behavior in C0
    845          * write 0xF000 to reg address 0x8105 for all AERs
    846          * write 0x00D0 to reg address 0x80BA for all AERs
    847          */
    848         if(pc->lane_num == 0){
    849              /* The phy address of QSGMII is the phy address pins + 8 */
    850             pc->phy_id =  pc->phy_id + 8;
    851             SOC_IF_ERROR_RETURN   
    852                (PHY54684_REG_WRITE(unit, pc, SOC_PHY_REG_RESERVE_ACCESS,   
    853                                          0x0000, 0x1f, 0xffd0));   
    854             SOC_IF_ERROR_RETURN   
    855                (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0000, 0x1e, 0x1f));   
    856             SOC_IF_ERROR_RETURN   
    857                (PHY54684_REG_WRITE(unit, pc, SOC_PHY_REG_RESERVE_ACCESS,   
    858                                          0x0000, 0x1f, 0x80b0));   
    859             SOC_IF_ERROR_RETURN   
    860                (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0000, 0x1a, 0x00d0));   
    861             SOC_IF_ERROR_RETURN   
    862                (PHY54684_REG_WRITE(unit, pc, SOC_PHY_REG_RESERVE_ACCESS,   
    863                                          0x0000, 0x1f, 0x8100));   
    864             SOC_IF_ERROR_RETURN   
    865                (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0000, 0x15, 0xf000));   
    866             /* Revert the phy_id */
    867             pc->phy_id = pc->phy_id - 8;
    868         }   
    869     }
    870 
    871     /* Issue : TX/RX can't work on 10/100 HD mode after cable plug/unplug.
    872      * Workaroud :
    873      * One of the "Remote PHY" registers in the QSGMII block 
    874      * of the BCM54684 PHY was modified to fix this issue.
    875      * The register is located at address 0x833C. Bit 8 of that register 
    876      * was cleared to use the legacy TX FIFO of the QSGMII block of BCM54684 PHY.
    877      */
    878     if(pc->lane_num == 0){
    879          /* The phy address of QSGMII is the phy address pins + 8 */
    880         pc->phy_id =  pc->phy_id + 8;
    881         SOC_IF_ERROR_RETURN   
    882            (PHY54684_REG_WRITE(unit, pc, SOC_PHY_REG_RESERVE_ACCESS,   
    883                                      0x0000, 0x1f, 0xffd0));   
    884         SOC_IF_ERROR_RETURN   
    885            (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0000, 0x1e, 0x1f));   
    886         SOC_IF_ERROR_RETURN   
    887            (PHY54684_REG_WRITE(unit, pc, SOC_PHY_REG_RESERVE_ACCESS,   
    888                                      0x0000, 0x1f, 0x8330));   
    889         SOC_IF_ERROR_RETURN   
    890            (PHY54684_REG_WRITE(unit, pc, 0x00, 0x0000, 0x1c, 0x0080));   
    891 
    892         /* Revert the phy_id */
    893         pc->phy_id = pc->phy_id - 8;
    894     }
    895 #endif    
    896     return SOC_E_NONE;
    897 }
    898 
    899 /*
    900  * Function:
    901  *      phy_54684_init
    902  * Purpose:
    903  *      Init function for 54684 PHY.
    904  * Parameters:
    905  *      unit - StrataSwitch unit #.
    906  *      port - StrataSwitch port #.
    907  * Returns:
    908  *      SOC_E_NONE
    909  */
    910 
    911 STATIC int
    912 phy_54684_init(int unit, soc_port_t port)
    913 {
    914     phy_ctrl_t        *pc;
    915     int                 fiber_capable;
    916     int                 fiber_preferred;
    917 
    918     LOG_INFO(BSL_LS_SOC_PHY,
    919              (BSL_META_U(unit,
    920                          "phy_54684_init: u=%d p=%d\n"),
    921                          unit, port));
    922 
    923     /* Only SGMII interface to switch MAC is supported */ 
    924     if (IS_GMII_PORT(unit, port)) {
    925         return SOC_E_CONFIG; 
    926     }
    927 
    928     pc = EXT_PHY_SW_STATE(unit, port);
    929     pc->interface = SOC_PORT_IF_SGMII;
    930 
    931     fiber_capable = 1;
    932 
    933     fiber_preferred =
    934         soc_property_port_get(unit, port, spn_PHY_FIBER_PREF, 0);
    935     pc->automedium =
    936         soc_property_port_get(unit, port, spn_PHY_AUTOMEDIUM, 0);
    937     pc->fiber_detect =
    938         soc_property_port_get(unit, port, spn_PHY_FIBER_DETECT, -4);
    939 
    940     LOG_INFO(BSL_LS_SOC_PHY,
    941              (BSL_META_U(unit,
    942                          "phy_54684_init: "
    943                          "u=%d p=%d type=54684%s automedium=%d fiber_pref=%d detect=%d\n"),
    944               unit, port, fiber_capable ? "S" : "",
    945               pc->automedium, fiber_preferred, pc->fiber_detect));
    946 
    947     pc->copper.enable = TRUE;
    948     pc->copper.preferred = !fiber_preferred;
    949     pc->copper.autoneg_enable = TRUE;
    950     pc->copper.autoneg_advert = ADVERT_ALL_COPPER;
    951     pc->copper.force_speed = 1000;
    952     pc->copper.force_duplex = TRUE;
    953     pc->copper.master = SOC_PORT_MS_AUTO;
    954     pc->copper.mdix = SOC_PORT_MDIX_AUTO;
    955 
    956     pc->fiber.enable = fiber_capable;
    957     pc->fiber.preferred = fiber_preferred;
    958     pc->fiber.autoneg_enable = fiber_capable;
    959     pc->fiber.autoneg_advert = ADVERT_ALL_FIBER;
    960     pc->fiber.force_speed = 1000;
    961     pc->fiber.force_duplex = TRUE;
    962     pc->fiber.master = SOC_PORT_MS_NONE;
    963     pc->fiber.mdix = SOC_PORT_MDIX_NORMAL;
    964 
    965     /* Initially configure for the preferred medium. */
    966     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_COPPER);
    967     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER);
    968     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_PASSTHRU);
    969     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_100FX);
    970     if (pc->fiber.preferred) {
    971         PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
    972     } else {
    973         PHY_FLAGS_SET(unit, port, PHY_FLAGS_COPPER);
    974     }
    975 
    976     /* Get Requested LED selectors (defaults are hardware defaults) */
    977     pc->ledmode[0] = soc_property_port_get(unit, port, spn_PHY_LED1_MODE, 0);
    978     pc->ledmode[1] = soc_property_port_get(unit, port, spn_PHY_LED2_MODE, 1);
    979     pc->ledmode[2] = soc_property_port_get(unit, port, spn_PHY_LED3_MODE, 3);
    980     pc->ledmode[3] = soc_property_port_get(unit, port, spn_PHY_LED4_MODE, 6);
    981     pc->ledctrl = soc_property_port_get(unit, port, spn_PHY_LED_CTRL, 0x8);
    982     pc->ledselect = soc_property_port_get(unit, port, spn_PHY_LED_SELECT, 0);
    983 
    984     SOC_IF_ERROR_RETURN
    985         (_phy_54684_reset_setup(unit, port));
    986 
    987     SOC_IF_ERROR_RETURN
    988         (_phy_54684_medium_config_update(unit, port,
    989                                         PHY_COPPER_MODE(unit, port) ?
    990                                         &pc->copper :
    991                                         &pc->fiber));
    992     return SOC_E_NONE;
    993 }
    994 
    995 /*
    996  * Function:
    997  *      phy_54684_enable_set
    998  * Purpose:
    999  *      Enable or disable the physical interface.
   1000  * Parameters:
   1001  *      unit - StrataSwitch unit #.
   1002  *      port - StrataSwitch port #.
   1003  *      enable - Boolean, true = enable PHY, false = disable.
   1004  * Returns:
   1005  *      SOC_E_XXX
   1006  */
   1007 
   1008 STATIC int
   1009 phy_54684_enable_set(int unit, soc_port_t port, int enable)
   1010 {
   1011     uint16         power;
   1012     phy_ctrl_t    *pc;
   1013 
   1014     pc     = EXT_PHY_SW_STATE(unit, port);
   1015     power  = (enable) ? 0 : MII_CTRL_PD;
   1016 
   1017     /* Do not incorrectly Power up the interface if medium is disabled and 
   1018      * global enable = true
   1019      */
   1020     if (pc->copper.enable && (pc->automedium || PHY_COPPER_MODE(unit, port))) {
   1021         SOC_IF_ERROR_RETURN
   1022             (MODIFY_PHY54684_MII_CTRLr(unit, pc, power, MII_CTRL_PD));
   1023 
   1024         LOG_INFO(BSL_LS_SOC_PHY,
   1025                  (BSL_META_U(unit,
   1026                              "phy_54684_enable_set: "
   1027                              "Power %s copper medium\n"), (enable) ? "up" : "down"));
   1028     }
   1029 
   1030     if (pc->fiber.enable && (pc->automedium || PHY_FIBER_MODE(unit, port))) {
   1031         phy_ctrl_t  *int_pc;
   1032         SOC_IF_ERROR_RETURN
   1033             (MODIFY_PHY54684_1000X_MII_CTRLr(unit, pc, power, 
   1034                                                    MII_CTRL_PD));
   1035 
   1036         int_pc = INT_PHY_SW_STATE(unit, port);
   1037         if (NULL != int_pc) {
   1038             SOC_IF_ERROR_RETURN
   1039                 (PHY_ENABLE_SET(int_pc->pd, unit, port, enable));
   1040         }
   1041 
   1042         /*
   1043          * When enable/disable the fiber port,
   1044          * the Tx disable bit of Expansion register 0x52 should be set also.
   1045          * Otherwise the following issue happens.
   1046          * After the fiber port is powered down
   1047          * (verified bit 11 MII cotnrol register 0 is set to 1),
   1048          * the link partner still shows linkup
   1049          */
   1050         SOC_IF_ERROR_RETURN
   1051             (MODIFY_PHY54684_EXP_SERDES_SGMII_RX_CTRLr(unit, pc, enable?0:1, 0x1));
   1052         SOC_IF_ERROR_RETURN
   1053             (MODIFY_PHY54684_EXP_SERDES_SGMII_CTRLr(unit, pc, enable?0:1, 0x1));
   1054     
   1055         LOG_INFO(BSL_LS_SOC_PHY,
   1056                  (BSL_META_U(unit,
   1057                              "phy_54684_enable_set: "
   1058                              "Power %s fiber medium\n"), (enable) ? "up" : "down"));
   1059     }
   1060 
   1061     /* Update software state */
   1062     SOC_IF_ERROR_RETURN(phy_fe_ge_enable_set(unit, port, enable));
   1063 
   1064     return SOC_E_NONE;
   1065 }
   1066 
   1067 /*
   1068  * Function:
   1069  *      phy_54684_enable_get
   1070  * Purpose:
   1071  *      Enable or disable the physical interface for a 54684 device.
   1072  * Parameters:
   1073  *      unit - StrataSwitch unit #.
   1074  *      port - StrataSwitch port #.
   1075  *      enable - (OUT) Boolean, true = enable PHY, false = disable.
   1076  * Returns:
   1077  *      SOC_E_XXX
   1078  */
   1079 
   1080 STATIC int
   1081 phy_54684_enable_get(int unit, soc_port_t port, int *enable)
   1082 {
   1083     return phy_fe_ge_enable_get(unit, port, enable);
   1084 }
   1085 
   1086 /*
   1087  * Function:
   1088  *      _phy_54684_fiber_100fx_setup
   1089  * Purpose:
   1090  *      Switch from 1000X mode to 100FX.  
   1091  * Parameters:
   1092  *      unit - StrataSwitch unit #.
   1093  *      port - StrataSwitch port #.
   1094  * Returns:
   1095  *      SOC_E_XXX
   1096  */
   1097 STATIC int
   1098 _phy_54684_fiber_100fx_setup(int unit, soc_port_t port) 
   1099 {
   1100     phy_ctrl_t    *pc;
   1101 
   1102     pc          = EXT_PHY_SW_STATE(unit, port);
   1103 
   1104     LOG_VERBOSE(BSL_LS_SOC_PHY,
   1105                 (BSL_META_U(unit,
   1106                             "_phy_54684_1000x_to_100fx: u=%d p=%d \n"),
   1107                  unit, port));
   1108     /* Operate SerDes in 100FX  */
   1109 
   1110     /* SerDes 100FX control register - register 1CH shadow 13H 
   1111       * BIT0 : 100FX link SerDes enable
   1112       * BIT1 : 100FX SerDes full duplex
   1113       */
   1114     if(pc->fiber.force_duplex) {
   1115         SOC_IF_ERROR_RETURN
   1116                 (MODIFY_PHY54684_SERDES_100FX_CTRLr(unit, pc, 0x3, 0x3));
   1117     } else {
   1118         SOC_IF_ERROR_RETURN
   1119                 (MODIFY_PHY54684_SERDES_100FX_CTRLr(unit, pc, 0x1, 0x3));
   1120     }
   1121 
   1122     /* Power up the SerDes      */ 
   1123     SOC_IF_ERROR_RETURN
   1124         (WRITE_PHY54684_1000X_MII_CTRLr(unit, pc, 
   1125                         MII_CTRL_AE | MII_CTRL_FD | MII_CTRL_SS_100));
   1126 
   1127     PHY_FLAGS_SET(unit, port, PHY_FLAGS_100FX);
   1128     pc->fiber.autoneg_enable = FALSE;
   1129 
   1130     return SOC_E_NONE;
   1131 }
   1132 
   1133 /*
   1134  * Function:
   1135  *      _phy_54684_fiber_1000x_setup
   1136  * Purpose:
   1137  *      Switch from 100FX mode to 1000X.  
   1138  * Parameters:
   1139  *      unit - StrataSwitch unit #.
   1140  *      port - StrataSwitch port #.
   1141  * Returns:
   1142  *      SOC_E_XXX
   1143  */
   1144 STATIC int
   1145 _phy_54684_fiber_1000x_setup(int unit, soc_port_t port) 
   1146 {
   1147     uint16         data;
   1148     phy_ctrl_t    *pc;
   1149 
   1150     pc          = EXT_PHY_SW_STATE(unit, port);
   1151 
   1152     LOG_VERBOSE(BSL_LS_SOC_PHY,
   1153                 (BSL_META_U(unit,
   1154                             "_phy_54684_fiber_1000x_setup: u=%d p=%d \n"),
   1155                  unit, port));
   1156 
   1157     /* Operate SerDes in 1000X  */
   1158     SOC_IF_ERROR_RETURN
   1159             (MODIFY_PHY54684_SERDES_100FX_CTRLr(unit, pc, 0x0, 0x1));
   1160 
   1161     /* Power up the SerDes      */ 
   1162     if (pc->fiber.autoneg_enable) {
   1163             data = MII_CTRL_AE | MII_CTRL_RAN | MII_CTRL_FD | MII_CTRL_SS_1000;
   1164     } else {
   1165             data = MII_CTRL_RAN | MII_CTRL_FD | MII_CTRL_SS_1000;
   1166     }
   1167 
   1168     SOC_IF_ERROR_RETURN
   1169         (WRITE_PHY54684_1000X_MII_CTRLr(unit, pc, data));
   1170 
   1171     /* When speed is 1000Mbps, set duplex to HD mode.
   1172      * This is because our MAC does not support 1G half duplex.
   1173      */
   1174     pc->fiber.force_duplex = TRUE;
   1175     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_100FX);
   1176 
   1177     return SOC_E_NONE;
   1178 }
   1179 
   1180 STATIC int
   1181 _phy_54684_medium_change(int unit, soc_port_t port, int force_update)
   1182 {
   1183     phy_ctrl_t    *pc;
   1184     int            medium;
   1185 
   1186     pc    = EXT_PHY_SW_STATE(unit, port);
   1187 
   1188     SOC_IF_ERROR_RETURN
   1189         (_phy_54684_medium_check(unit, port, &medium));
   1190 
   1191     if (medium == SOC_PORT_MEDIUM_COPPER) {
   1192         if ((!PHY_COPPER_MODE(unit, port)) || force_update) { /* Was fiber */ 
   1193             PHY_FLAGS_SET(unit, port, PHY_FLAGS_COPPER);
   1194             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER);
   1195             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_100FX);
   1196             PHY_FLAGS_SET(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
   1197             SOC_IF_ERROR_RETURN
   1198                 (_phy_54684_no_reset_setup(unit, port));
   1199 
   1200             /* Do not power up the interface if medium is disabled. */
   1201             if (pc->copper.enable) {
   1202                 SOC_IF_ERROR_RETURN
   1203                     (_phy_54684_medium_config_update(unit, port, &pc->copper));
   1204             }
   1205             LOG_INFO(BSL_LS_SOC_PHY,
   1206                      (BSL_META_U(unit,
   1207                                  "_phy_54684_link_auto_detect: u=%d p=%d [F->C]\n"),
   1208                       unit, port));
   1209         }
   1210     } else {        /* Fiber */
   1211         if (PHY_COPPER_MODE(unit, port) || force_update) { /* Was copper */
   1212             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_COPPER);
   1213             PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
   1214             PHY_FLAGS_SET(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
   1215             SOC_IF_ERROR_RETURN
   1216                 (_phy_54684_no_reset_setup(unit, port));
   1217 
   1218             /*
   1219              * When enable/disable the fiber port,
   1220              * the Tx disable bit of Expansion register 0x52 should be set also.
   1221              * Otherwise the following issue happens.
   1222              * After the fiber port is powered down
   1223              * (verified bit 11 MII cotnrol register 0 is set to 1),
   1224              * the link partner still shows linkup
   1225              */
   1226             SOC_IF_ERROR_RETURN
   1227                 (MODIFY_PHY54684_EXP_SERDES_SGMII_RX_CTRLr(unit, pc,
   1228                                                  pc->fiber.enable?0:1, 0x1));
   1229             SOC_IF_ERROR_RETURN
   1230                 (MODIFY_PHY54684_EXP_SERDES_SGMII_CTRLr(unit, pc,
   1231                                                  pc->fiber.enable?0:1, 0x1));
   1232 
   1233             if (pc->fiber.enable) {
   1234                 SOC_IF_ERROR_RETURN
   1235                     (_phy_54684_medium_config_update(unit, port, &pc->fiber));
   1236             }
   1237             LOG_INFO(BSL_LS_SOC_PHY,
   1238                      (BSL_META_U(unit,
   1239                                  "_phy_54684_link_auto_detect: u=%d p=%d [C->F]\n"),
   1240                       unit, port));
   1241         }
   1242     }
   1243     return SOC_E_NONE;
   1244 }
   1245 
   1246 /*
   1247  * Function:
   1248  *      phy_54684_link_get
   1249  * Purpose:
   1250  *      Determine the current link up/down status for a 54684 device.
   1251  * Parameters:
   1252  *      unit - StrataSwitch unit #.
   1253  *      port - StrataSwitch port #.
   1254  *      link - (OUT) Boolean, true indicates link established.
   1255  * Returns:
   1256  *      SOC_E_XXX
   1257  * Notes:
   1258  *      If using automedium, also switches the mode.
   1259  */
   1260 STATIC int
   1261 phy_54684_link_get(int unit, soc_port_t port, int *link)
   1262 {
   1263     phy_ctrl_t    *pc;
   1264     uint16         data; 
   1265     uint16         mask;
   1266 
   1267     pc    = EXT_PHY_SW_STATE(unit, port);
   1268     *link = FALSE;      /* Default return */
   1269 
   1270     if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_DISABLE)) {
   1271         return SOC_E_NONE;
   1272     }
   1273 
   1274     if (!pc->fiber.enable && !pc->copper.enable) {
   1275         return SOC_E_NONE;
   1276     }
   1277 
   1278     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
   1279     if (pc->automedium) {
   1280         /* Check for medium change and update HW/SW accordingly. */
   1281         SOC_IF_ERROR_RETURN
   1282             (_phy_54684_medium_change(unit, port,FALSE));
   1283     }
   1284     if (PHY_COPPER_MODE(unit, port)) {
   1285         SOC_IF_ERROR_RETURN(phy_fe_ge_link_get(unit, port, link));
   1286     } else {
   1287         SOC_IF_ERROR_RETURN
   1288             (READ_PHY54684_1000X_MII_STATr(unit, pc, &data));
   1289         *link = (data & MII_STAT_LA) ? TRUE : FALSE;
   1290     }
   1291 
   1292     /* If preferred medium is up, disable the other medium 
   1293      * to prevent false link. */
   1294     if (pc->automedium) {
   1295         mask = MII_CTRL_PD;
   1296         if (pc->copper.preferred) {
   1297             if (pc->fiber.enable) {
   1298                 /* power down the serdes */
   1299                 data = (*link && PHY_COPPER_MODE(unit, port)) ? MII_CTRL_PD : 0;
   1300             } else {
   1301                 data = MII_CTRL_PD;
   1302             }
   1303             SOC_IF_ERROR_RETURN
   1304                 (MODIFY_PHY54684_1000X_MII_CTRLr(unit, pc, data, mask));
   1305         } else {  /* pc->fiber.preferred */
   1306             if (pc->copper.enable) {
   1307                 data = (*link && PHY_FIBER_MODE(unit, port)) ? MII_CTRL_PD : 0;
   1308             } else {
   1309                 data = MII_CTRL_PD;
   1310             }
   1311             SOC_IF_ERROR_RETURN
   1312                 (MODIFY_PHY54684_MII_CTRLr(unit, pc, data, mask));
   1313         }
   1314     }
   1315 
   1316     LOG_VERBOSE(BSL_LS_SOC_PHY,
   1317                 (BSL_META_U(unit,
   1318                             "phy_54684_link_get: u=%d p=%d mode=%s%s link=%d\n"),
   1319                  unit, port,
   1320                  PHY_COPPER_MODE(unit, port) ? "C" : "F",
   1321                  PHY_FIBER_100FX_MODE(unit, port)? "(100FX)" : "",
   1322                  *link));
   1323 
   1324     return SOC_E_NONE;
   1325 }
   1326 
   1327 /*
   1328  * Function:
   1329  *      phy_54684_duplex_set
   1330  * Purpose:
   1331  *      Set the current duplex mode (forced).
   1332  * Parameters:
   1333  *      unit - StrataSwitch unit #.
   1334  *      port - StrataSwitch port #.
   1335  *      duplex - Boolean, true indicates full duplex, false indicates half.
   1336  * Returns:
   1337  *      SOC_E_NONE
   1338  *      SOC_E_UNAVAIL - Half duplex requested, and not supported.
   1339  * Notes:
   1340  *      The duplex is set only for the ACTIVE medium.
   1341  *      No synchronization performed at this level.
   1342  *      Autonegotiation is not manipulated.
   1343  */
   1344 
   1345 STATIC int
   1346 phy_54684_duplex_set(int unit, soc_port_t port, int duplex)
   1347 {
   1348     int                  rv;
   1349     phy_ctrl_t    *pc;
   1350 
   1351     pc = EXT_PHY_SW_STATE(unit, port);
   1352     rv = SOC_E_NONE;
   1353     if (PHY_COPPER_MODE(unit, port)) {
   1354         /* Note: mac.c uses phy_5690_notify_duplex to update 5690 */
   1355         rv = phy_fe_ge_duplex_set(unit, port, duplex);
   1356         if (SOC_SUCCESS(rv)) {
   1357             pc->copper.force_duplex = duplex;
   1358         }
   1359     } else {    /* Fiber */
   1360         if (PHY_FIBER_100FX_MODE(unit, port)) {
   1361             SOC_IF_ERROR_RETURN
   1362                 (MODIFY_PHY54684_SERDES_100FX_CTRLr(unit, pc, duplex? 0x2:0x0, 0x2));
   1363             pc->fiber.force_duplex = duplex;
   1364         } else {
   1365             if(!duplex) {
   1366                 return SOC_E_UNAVAIL;
   1367             }
   1368         }
   1369     }
   1370 
   1371     LOG_INFO(BSL_LS_SOC_PHY,
   1372              (BSL_META_U(unit,
   1373                          "phy_54684_duplex_set: u=%d p=%d d=%d rv=%d\n"),
   1374               unit, port, duplex, rv));
   1375 
   1376     return rv;
   1377 }
   1378 
   1379 /*
   1380  * Function:
   1381  *      phy_54684_duplex_get
   1382  * Purpose:
   1383  *      Get the current operating duplex mode. If autoneg is enabled,
   1384  *      then operating mode is returned, otherwise forced mode is returned.
   1385  * Parameters:
   1386  *      unit - StrataSwitch unit #.
   1387  *      port - StrataSwitch port #.
   1388  *      duplex - (OUT) Boolean, true indicates full duplex, false
   1389  *              indicates half.
   1390  * Returns:
   1391  *      SOC_E_NONE
   1392  * Notes:
   1393  *      The duplex is retrieved for the ACTIVE medium.
   1394  *      No synchronization performed at this level. Autonegotiation is
   1395  *      not manipulated.
   1396  */
   1397 
   1398 STATIC int
   1399 phy_54684_duplex_get(int unit, soc_port_t port, int *duplex)
   1400 {
   1401     phy_ctrl_t    *pc;
   1402     uint16         opt_mode;
   1403 
   1404     pc = EXT_PHY_SW_STATE(unit, port);
   1405 
   1406     if (PHY_COPPER_MODE(unit, port)) {
   1407         return phy_fe_ge_duplex_get(unit, port, duplex);    
   1408     } else {    /* Fiber */
   1409         if (PHY_FIBER_100FX_MODE(unit, port)) {
   1410             SOC_IF_ERROR_RETURN
   1411                 (READ_PHY54684_EXP_OPT_MODE_STATr(unit, pc, &opt_mode));
   1412             /* The bit 12 of register EXP_OPT_MODE_STAT is serdes duplex */
   1413             if(opt_mode & 0x1000) {
   1414                 *duplex = TRUE;
   1415             } else {
   1416                 *duplex = FALSE;
   1417             }
   1418         } else { /* 1000X */
   1419             *duplex = TRUE;
   1420         }
   1421     }
   1422         
   1423     return SOC_E_NONE;
   1424 }
   1425 
   1426 /*
   1427  * Function:
   1428  *      phy_54684_speed_set
   1429  * Purpose:
   1430  *      Set the current operating speed (forced).
   1431  * Parameters:
   1432  *      unit - StrataSwitch unit #.
   1433  *      port - StrataSwitch port #.
   1434  *      speed - Requested speed, only 1000 supported.
   1435  * Returns:
   1436  *      SOC_E_XXX
   1437  * Notes:
   1438  *      The speed is set only for the ACTIVE medium.
   1439  */
   1440 
   1441 STATIC int
   1442 phy_54684_speed_set(int unit, soc_port_t port, int speed)
   1443 {
   1444     int            rv; 
   1445     phy_ctrl_t    *pc;
   1446 
   1447     pc = EXT_PHY_SW_STATE(unit, port);
   1448     rv = SOC_E_NONE;
   1449     if (PHY_COPPER_MODE(unit, port)) {
   1450         /* Note: mac.c uses phy_5690_notify_speed to update 5690 */
   1451         rv = phy_fe_ge_speed_set(unit, port, speed);
   1452         if (SOC_SUCCESS(rv)) {
   1453             pc->copper.force_speed = speed;
   1454         }
   1455     } else {    /* Fiber */
   1456         if (speed == 100) {
   1457             rv = _phy_54684_fiber_100fx_setup(unit, port);
   1458         } else  if (speed == 0 || speed == 1000) {
   1459             rv = _phy_54684_fiber_1000x_setup(unit, port);
   1460         } else {
   1461             rv = SOC_E_CONFIG;
   1462         }
   1463         if (SOC_SUCCESS(rv)) {
   1464             pc->fiber.force_speed = speed;
   1465         }
   1466     }
   1467 
   1468     LOG_INFO(BSL_LS_SOC_PHY,
   1469              (BSL_META_U(unit,
   1470                          "phy_54684_speed_set: u=%d p=%d s=%d fiber=%d rv=%d\n"),
   1471               unit, port, speed, PHY_FIBER_MODE(unit, port), rv));
   1472 
   1473     return rv;
   1474 }
   1475 
   1476 /*
   1477  * Function:
   1478  *      phy_54684_speed_get
   1479  * Purpose:
   1480  *      Get the current operating speed for a 54684 device.
   1481  * Parameters:
   1482  *      unit - StrataSwitch unit #.
   1483  *      port - StrataSwitch port #.
   1484  *      duplex - (OUT) Boolean, true indicates full duplex, false
   1485  *              indicates half.
   1486  * Returns:
   1487  *      SOC_E_NONE
   1488  * Notes:
   1489  *      The speed is retrieved for the ACTIVE medium.
   1490  */
   1491 
   1492 STATIC int
   1493 phy_54684_speed_get(int unit, soc_port_t port, int *speed)
   1494 {
   1495     phy_ctrl_t    *pc;
   1496     uint16         opt_mode;
   1497 
   1498     pc = EXT_PHY_SW_STATE(unit, port);
   1499 
   1500     if (PHY_COPPER_MODE(unit, port)) {
   1501         return phy_fe_ge_speed_get(unit, port, speed);
   1502     } else {
   1503         *speed = 1000;
   1504         SOC_IF_ERROR_RETURN
   1505             (READ_PHY54684_EXP_OPT_MODE_STATr(unit, pc, &opt_mode));
   1506         switch(opt_mode & (0x3 << 13)) {
   1507         case (0 << 13):
   1508             *speed = 10;
   1509             break;
   1510         case (1 << 13):
   1511             *speed = 100;
   1512             break;
   1513         case (2 << 13):
   1514             *speed = 1000;
   1515             break;
   1516         default:
   1517             LOG_WARN(BSL_LS_SOC_PHY,
   1518                      (BSL_META_U(unit,
   1519                                  "phy_54684_speed_get: u=%d p=%d invalid speed\n"),
   1520                       unit, port));
   1521         }
   1522     }
   1523 
   1524     return SOC_E_NONE;
   1525 }
   1526 
   1527 /*
   1528  * Function:
   1529  *      phy_54684_master_set
   1530  * Purpose:
   1531  *      Set the current master mode
   1532  * Parameters:
   1533  *      unit - StrataSwitch unit #.
   1534  *      port - StrataSwitch port #.
   1535  *      master - SOC_PORT_MS_*
   1536  * Returns:
   1537  *      SOC_E_XXX
   1538  * Notes:
   1539  *      The master mode is set only for the ACTIVE medium.
   1540  */
   1541 STATIC int
   1542 phy_54684_master_set(int unit, soc_port_t port, int master)
   1543 {
   1544     int                  rv;
   1545     phy_ctrl_t    *pc;
   1546 
   1547     pc = EXT_PHY_SW_STATE(unit, port);
   1548     rv = SOC_E_NONE;
   1549     if (PHY_COPPER_MODE(unit, port)) {
   1550         rv = phy_fe_ge_master_set(unit, port, master);
   1551         if (SOC_SUCCESS(rv)) {
   1552             pc->copper.master = master;
   1553         }
   1554     }
   1555     LOG_INFO(BSL_LS_SOC_PHY,
   1556              (BSL_META_U(unit,
   1557                          "phy_54684_master_set: u=%d p=%d master=%d fiber=%d rv=%d\n"),
   1558               unit, port, master, PHY_FIBER_MODE(unit, port), rv));
   1559     return rv;
   1560 }
   1561 
   1562 /*
   1563  * Function:
   1564  *      phy_54684_master_get
   1565  * Purpose:
   1566  *      Get the current master mode for a 54684 device.
   1567  * Parameters:
   1568  *      unit - StrataSwitch unit #.
   1569  *      port - StrataSwitch port #.
   1570  *      master - (OUT) SOC_PORT_MS_*
   1571  * Returns:
   1572  *      SOC_E_NONE
   1573  * Notes:
   1574  *      The master mode is retrieved for the ACTIVE medium.
   1575  */
   1576 
   1577 STATIC int
   1578 phy_54684_master_get(int unit, soc_port_t port, int *master)
   1579 {
   1580     if (PHY_COPPER_MODE(unit, port)) {
   1581         return phy_fe_ge_master_get(unit, port, master);
   1582     } else {
   1583         *master = SOC_PORT_MS_NONE;
   1584         return SOC_E_NONE;
   1585     }
   1586 }
   1587 
   1588 /*
   1589  * Function:
   1590  *      phy_54684_autoneg_set
   1591  * Purpose:
   1592  *      Enable or disable auto-negotiation on the specified port.
   1593  * Parameters:
   1594  *      unit - StrataSwitch unit #.
   1595  *      port - StrataSwitch port #.
   1596  *      autoneg - Boolean, if true, auto-negotiation is enabled
   1597  *              (and/or restarted). If false, autonegotiation is disabled.
   1598  * Returns:
   1599  *      SOC_E_XXX
   1600  * Notes:
   1601  *      The autoneg mode is set only for the ACTIVE medium.
   1602  */
   1603 
   1604 STATIC int
   1605 phy_54684_autoneg_set(int unit, soc_port_t port, int autoneg)
   1606 {
   1607     int                 rv;
   1608     uint16              data, mask;
   1609     phy_ctrl_t   *pc;
   1610 
   1611     pc = EXT_PHY_SW_STATE(unit, port);
   1612     rv = SOC_E_NONE;
   1613     if (PHY_COPPER_MODE(unit, port)) {
   1614         rv = phy_fe_ge_an_set(unit, port, autoneg);
   1615         if (SOC_SUCCESS(rv)) {
   1616             pc->copper.autoneg_enable = autoneg ? 1 : 0;
   1617         }
   1618     } else { 
   1619         if (autoneg) {
   1620             /* When enabling autoneg, set the default speed to 1000Mbps first.
   1621              * PHY will not enable autoneg if the PHY is in 100FX mode.
   1622              */
   1623             SOC_IF_ERROR_RETURN
   1624                 (phy_54684_speed_set(unit, port, 1000));
   1625         }
   1626 
   1627         /* Disable/Enable auto-detection between SGMII-slave and 1000BASE-X */
   1628         SOC_IF_ERROR_RETURN
   1629             (MODIFY_PHY54684_SGMII_SLAVEr(unit, pc, autoneg ?1:0, 1));
   1630 
   1631         SOC_IF_ERROR_RETURN
   1632             (phy_54684_adv_local_set(unit, port, pc->fiber.autoneg_advert));
   1633 
   1634         data = MII_CTRL_FD;
   1635         data |= (autoneg) ? MII_CTRL_AE | MII_CTRL_RAN : 0;
   1636         mask = MII_CTRL_AE | MII_CTRL_RAN | MII_CTRL_FD;
   1637         SOC_IF_ERROR_RETURN
   1638             (MODIFY_PHY54684_1000X_MII_CTRLr(unit, pc, data, mask));
   1639 
   1640 
   1641         pc->fiber.autoneg_enable = autoneg ? TRUE : FALSE;
   1642     }
   1643  
   1644     LOG_INFO(BSL_LS_SOC_PHY,
   1645              (BSL_META_U(unit,
   1646                          "phy_54684_autoneg_set: u=%d p=%d autoneg=%d rv=%d\n"),
   1647               unit, port, autoneg, rv));
   1648 
   1649     return rv;
   1650 }
   1651 
   1652 /*
   1653  * Function:
   1654  *      phy_54684_autoneg_get
   1655  * Purpose:
   1656  *      Get the current auto-negotiation status (enabled/busy).
   1657  * Parameters:
   1658  *      unit - StrataSwitch unit #.
   1659  *      port - StrataSwitch port #.
   1660  *      autoneg - (OUT) if true, auto-negotiation is enabled.
   1661  *      autoneg_done - (OUT) if true, auto-negotiation is complete. This
   1662  *              value is undefined if autoneg == FALSE.
   1663  * Returns:
   1664  *      SOC_E_XXX
   1665  * Notes:
   1666  *      The autoneg mode is retrieved for the ACTIVE medium.
   1667  */
   1668 
   1669 STATIC int
   1670 phy_54684_autoneg_get(int unit, soc_port_t port,
   1671                      int *autoneg, int *autoneg_done)
   1672 {
   1673     int           rv;
   1674     uint16        data;
   1675     phy_ctrl_t   *pc;
   1676 
   1677     pc = EXT_PHY_SW_STATE(unit, port);
   1678 
   1679     rv            = SOC_E_NONE;
   1680     *autoneg      = FALSE;
   1681     *autoneg_done = FALSE;
   1682     if (PHY_COPPER_MODE(unit, port)) {
   1683         rv = phy_fe_ge_an_get(unit, port, autoneg, autoneg_done);
   1684     } else {
   1685         /* autoneg is not enabled in 100FX mode */
   1686         if (PHY_FIBER_100FX_MODE(unit, port)) {
   1687             *autoneg = FALSE;
   1688             *autoneg_done = TRUE;
   1689             return rv;
   1690         }
   1691 
   1692         SOC_IF_ERROR_RETURN
   1693             (READ_PHY54684_1000X_MII_CTRLr(unit, pc, &data));
   1694         if (data & MII_CTRL_AE) {
   1695             *autoneg = TRUE;
   1696 
   1697             SOC_IF_ERROR_RETURN
   1698                 (READ_PHY54684_1000X_MII_STATr(unit, pc, &data));
   1699             if (data & MII_STAT_AN_DONE) {
   1700                 *autoneg_done = TRUE;
   1701             }
   1702         }
   1703         
   1704     }
   1705 
   1706     return rv;
   1707 }
   1708 
   1709 /*
   1710  * Function:
   1711  *      phy_54684_adv_local_set
   1712  * Purpose:
   1713  *      Set the current advertisement for auto-negotiation.
   1714  * Parameters:
   1715  *      unit - StrataSwitch unit #.
   1716  *      port - StrataSwitch port #.
   1717  *      mode - Port mode mask indicating supported options/speeds.
   1718  * Returns:
   1719  *      SOC_E_XXX
   1720  * Notes:
   1721  *      The advertisement is set only for the ACTIVE medium.
   1722  *      No synchronization performed at this level.
   1723  */
   1724 
   1725 STATIC int
   1726 phy_54684_adv_local_set(int unit, soc_port_t port, soc_port_mode_t mode)
   1727 {
   1728     int                  rv;
   1729     phy_ctrl_t    *pc;
   1730 
   1731     pc = EXT_PHY_SW_STATE(unit, port);
   1732     rv = SOC_E_NONE;
   1733     if (PHY_COPPER_MODE(unit, port)) {
   1734         rv = phy_ge_adv_local_set(unit, port, mode);
   1735         if (SOC_SUCCESS(rv)) {
   1736             pc->copper.autoneg_advert = mode & ADVERT_ALL_COPPER;
   1737         }
   1738     } else { 
   1739         uint16  mii_adv;
   1740     
   1741         mii_adv = MII_ANA_C37_FD;  /* Always advertise 1000X full duplex */
   1742         switch (mode & SOC_PM_PAUSE) {
   1743         case SOC_PM_PAUSE:
   1744             mii_adv |= MII_ANA_C37_PAUSE;
   1745             break;
   1746         case SOC_PM_PAUSE_TX:
   1747             mii_adv |= MII_ANA_C37_ASYM_PAUSE;
   1748             break;
   1749         case SOC_PM_PAUSE_RX:
   1750             mii_adv |= (MII_ANA_C37_ASYM_PAUSE | MII_ANA_C37_PAUSE);
   1751             break;
   1752         }
   1753         /* Write to SerDes Auto-Neg Advertisement Reg */
   1754         rv = WRITE_PHY54684_1000X_MII_ANAr(unit, pc, mii_adv);
   1755         if (SOC_SUCCESS(rv)) {
   1756             pc->fiber.autoneg_advert = mode & ADVERT_ALL_FIBER;
   1757         }
   1758     }
   1759     LOG_INFO(BSL_LS_SOC_PHY,
   1760              (BSL_META_U(unit,
   1761                          "phy_54684_adv_local_set: u=%d p=%d mode=0x%x, rv=%d\n"),
   1762               unit, port, mode, rv));
   1763     return rv;
   1764 }
   1765 
   1766 /*
   1767  * Function:
   1768  *      phy_54684_adv_local_get
   1769  * Purpose:
   1770  *      Get the current advertisement for auto-negotiation.
   1771  * Parameters:
   1772  *      unit - StrataSwitch unit #.
   1773  *      port - StrataSwitch port #.
   1774  *      mode - (OUT) Port mode mask indicating supported options/speeds.
   1775  * Returns:
   1776  *      SOC_E_XXX
   1777  * Notes:
   1778  *      The advertisement is retrieved for the ACTIVE medium.
   1779  *      No synchronization performed at this level.
   1780  */
   1781 
   1782 STATIC int
   1783 phy_54684_adv_local_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   1784 {
   1785     uint16         mii_adv;
   1786     phy_ctrl_t    *pc;
   1787 
   1788     pc = EXT_PHY_SW_STATE(unit, port);
   1789 
   1790     if (PHY_COPPER_MODE(unit, port)) {
   1791         return phy_ge_adv_local_get(unit, port, mode);
   1792     } else {    /* PHY_FIBER_MODE */
   1793         /* read the 1000BASE-X Auto-Neg Advertisement Reg ( Address 0x04 ) */
   1794         SOC_IF_ERROR_RETURN
   1795             (READ_PHY54684_1000X_MII_ANAr(unit, pc, &mii_adv));
   1796 
   1797         *mode = 0;
   1798         *mode |= (mii_adv & MII_ANA_C37_FD) ? SOC_PM_1000MB_FD : 0;
   1799         *mode |= (mii_adv & MII_ANA_C37_HD) ? SOC_PM_1000MB_HD : 0;
   1800         switch (mii_adv & (MII_ANA_C37_PAUSE | MII_ANA_C37_ASYM_PAUSE)) {
   1801         case MII_ANA_C37_PAUSE:
   1802             *mode |= SOC_PM_PAUSE;
   1803             break;
   1804         case MII_ANA_C37_ASYM_PAUSE:
   1805             *mode |= SOC_PM_PAUSE_TX;
   1806             break;
   1807         case (MII_ANA_C37_PAUSE | MII_ANA_C37_ASYM_PAUSE):
   1808             *mode |= SOC_PM_PAUSE_RX;
   1809             break;
   1810         }
   1811     }
   1812     return SOC_E_NONE;
   1813 }
   1814 
   1815 /*
   1816  * Function:
   1817  *      phy_54684_adv_remote_get
   1818  * Purpose:
   1819  *      Get partners current advertisement for auto-negotiation.
   1820  * Parameters:
   1821  *      unit - StrataSwitch unit #.
   1822  *      port - StrataSwitch port #.
   1823  *      mode - (OUT) Port mode mask indicating supported options/speeds.
   1824  * Returns:
   1825  *      SOC_E_XXX
   1826  * Notes:
   1827  *      The remote advertisement is retrieved for the ACTIVE medium.
   1828  *      No synchronization performed at this level. If Autonegotiation is
   1829  *      disabled or in progress, this routine will return an error.
   1830  */
   1831 
   1832 STATIC int
   1833 phy_54684_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   1834 {
   1835     uint16               data;
   1836     phy_ctrl_t    *pc;
   1837 
   1838     pc = EXT_PHY_SW_STATE(unit, port);
   1839     if (PHY_COPPER_MODE(unit, port)) {
   1840         return phy_ge_adv_remote_get(unit, port, mode);
   1841     } else {    /* PHY_FIBER_MODE */
   1842 
   1843         *mode = 0;
   1844         /* read the 1000BASE-X     Control Reg ( Address 0x00 ),
   1845          *   Auto-Neg Link Partner Ability Reg ( Address 0x05 )
   1846          */
   1847         SOC_IF_ERROR_RETURN
   1848             (READ_PHY54684_1000X_MII_CTRLr(unit, pc, &data));
   1849         if (!(data & MII_CTRL_AE)) {
   1850             return SOC_E_DISABLED;
   1851         }
   1852 
   1853         SOC_IF_ERROR_RETURN
   1854             (READ_PHY54684_1000X_MII_ANPr(unit, pc, &data));
   1855         if (data & MII_ANP_SGMII_MODE) {
   1856             /* Link partner is SGMII master */
   1857             switch(data & MII_ANP_SGMII_SS_MASK) {
   1858             case MII_ANP_SGMII_SS_10:
   1859                 *mode = SOC_PM_10MB;
   1860                 break;
   1861             case MII_ANP_SGMII_SS_100:
   1862                 *mode = SOC_PM_100MB;
   1863                 break;
   1864             case MII_ANP_SGMII_SS_1000:
   1865                 *mode = SOC_PM_1000MB;
   1866                 break;
   1867             }
   1868 
   1869             *mode &= (data & MII_ANP_SGMII_FD) ? SOC_PM_FD : SOC_PM_HD;
   1870         } else {
   1871             *mode |= (data & MII_ANP_C37_FD) ? SOC_PM_1000MB_FD : 0;
   1872             *mode |= (data & MII_ANP_C37_HD) ? SOC_PM_1000MB_HD : 0;
   1873             switch (data & (MII_ANP_C37_PAUSE | MII_ANP_C37_ASYM_PAUSE)) {
   1874             case MII_ANP_C37_PAUSE:
   1875                 *mode |= SOC_PM_PAUSE;
   1876                 break;
   1877             case MII_ANP_C37_ASYM_PAUSE:
   1878                 *mode |= SOC_PM_PAUSE_TX;
   1879                 break;
   1880             case (MII_ANP_C37_PAUSE | MII_ANP_C37_ASYM_PAUSE):
   1881                 *mode |= SOC_PM_PAUSE_RX;
   1882                 break;
   1883             }
   1884         }
   1885     }
   1886     return SOC_E_NONE;
   1887 }
   1888 
   1889 /*
   1890  * Function:
   1891  *      phy_54684_lb_set
   1892  * Purpose:
   1893  *      Set the local PHY loopback mode.
   1894  * Parameters:
   1895  *      unit - StrataSwitch unit #.
   1896  *      port - StrataSwitch port #.
   1897  *      loopback - Boolean: true = enable loopback, false = disable.
   1898  * Returns:
   1899  *      SOC_E_XXX
   1900  * Notes:
   1901  *      The loopback mode is set only for the ACTIVE medium.
   1902  *      No synchronization performed at this level.
   1903  */
   1904 
   1905 STATIC int
   1906 phy_54684_lb_set(int unit, soc_port_t port, int enable)
   1907 {
   1908     int            rv;
   1909     uint16         data;
   1910     phy_ctrl_t    *pc;
   1911 
   1912     pc = EXT_PHY_SW_STATE(unit, port);
   1913     rv = SOC_E_NONE;
   1914 
   1915 #if AUTO_MDIX_WHEN_AN_DIS
   1916     {
   1917         /* Disable Auto-MDIX When autoneg disabled */
   1918         /* Enable Auto-MDIX When autoneg disabled */
   1919         data = (enable) ? 0x0000 : 0x0200;
   1920         SOC_IF_ERROR_RETURN
   1921             (MODIFY_PHY_54684_MII_MISC_CTRLr(unit, pc, data, 0x0200)); 
   1922     }
   1923 #endif
   1924 
   1925     if (PHY_COPPER_MODE(unit, port)) {
   1926         rv = phy_fe_ge_lb_set(unit, port, enable);
   1927     } else { 
   1928         data = (enable) ? MII_CTRL_LE : 0;
   1929         rv   = MODIFY_PHY54684_1000X_MII_CTRLr
   1930                   (unit, pc, data, MII_CTRL_LE);
   1931         if (enable && SOC_SUCCESS(rv)) {
   1932             uint16        stat;
   1933             int           link;
   1934             soc_timeout_t to;
   1935 
   1936             /* Wait up to 5000 msec for link up */
   1937             soc_timeout_init(&to, 5000000, 0);
   1938             link = 0;
   1939             while (!soc_timeout_check(&to)) {
   1940                 rv = READ_PHY54684_1000X_MII_STATr(unit, pc, &stat);
   1941                 link = stat & MII_STAT_LA;
   1942                 if (link || SOC_FAILURE(rv)) {
   1943                     break;
   1944                 }
   1945             }
   1946             if (!link) {
   1947                 LOG_WARN(BSL_LS_SOC_PHY,
   1948                          (BSL_META_U(unit,
   1949                                      "phy_54684_lb_set: u=%d p=%d TIMEOUT\n"),
   1950                           unit, port));
   1951 
   1952                 rv = SOC_E_TIMEOUT;
   1953             }
   1954         }
   1955     }
   1956     LOG_INFO(BSL_LS_SOC_PHY,
   1957              (BSL_META_U(unit,
   1958                          "phy_54684_lb_set: u=%d p=%d en=%d rv=%d\n"), 
   1959               unit, port, enable, rv));
   1960 
   1961     return rv; 
   1962 }
   1963 
   1964 /*
   1965  * Function:
   1966  *      phy_54684_lb_get
   1967  * Purpose:
   1968  *      Get the local PHY loopback mode.
   1969  * Parameters:
   1970  *      unit - StrataSwitch unit #.
   1971  *      port - StrataSwitch port #.
   1972  *      loopback - (OUT) Boolean: true = enable loopback, false = disable.
   1973  * Returns:
   1974  *      SOC_E_XXX
   1975  * Notes:
   1976  *      The loopback mode is retrieved for the ACTIVE medium.
   1977  */
   1978 
   1979 STATIC int
   1980 phy_54684_lb_get(int unit, soc_port_t port, int *enable)
   1981 {
   1982     uint16               data;
   1983     phy_ctrl_t    *pc;
   1984 
   1985     pc = EXT_PHY_SW_STATE(unit, port);
   1986 
   1987     if (PHY_COPPER_MODE(unit, port)) {
   1988         SOC_IF_ERROR_RETURN
   1989             (phy_fe_ge_lb_get(unit, port, enable));
   1990     } else {
   1991        SOC_IF_ERROR_RETURN
   1992            (READ_PHY54684_1000X_MII_CTRLr(unit, pc, &data));
   1993         *enable = (data & MII_CTRL_LE) ? 1 : 0;
   1994     }
   1995 
   1996     return SOC_E_NONE; 
   1997 }
   1998 
   1999 /*
   2000  * Function:
   2001  *      phy_54684_interface_set
   2002  * Purpose:
   2003  *      Set the current operating mode of the PHY.
   2004  * Parameters:
   2005  *      unit - StrataSwitch unit #.
   2006  *      port - StrataSwitch port #.
   2007  *      pif - one of SOC_PORT_IF_*
   2008  * Returns:
   2009  *      SOC_E_NONE - success
   2010  *      SOC_E_UNAVAIL - unsupported interface
   2011  */
   2012 
   2013 STATIC int
   2014 phy_54684_interface_set(int unit, soc_port_t port, soc_port_if_t pif)
   2015 {
   2016     COMPILER_REFERENCE(unit);
   2017     COMPILER_REFERENCE(port);
   2018 
   2019     switch (pif) {
   2020     case SOC_PORT_IF_SGMII:
   2021     case SOC_PORT_IF_GMII:
   2022         return SOC_E_NONE;
   2023     default:
   2024         return SOC_E_UNAVAIL;
   2025     }
   2026 }
   2027 
   2028 /*
   2029  * Function:
   2030  *      phy_54684_interface_get
   2031  * Purpose:
   2032  *      Get the current operating mode of the PHY.
   2033  * Parameters:
   2034  *      unit - StrataSwitch unit #.
   2035  *      port - StrataSwitch port #.
   2036  *      pif - (OUT) one of SOC_PORT_IF_*
   2037  * Returns:
   2038  *      SOC_E_NONE
   2039  */
   2040 
   2041 STATIC int
   2042 phy_54684_interface_get(int unit, soc_port_t port, soc_port_if_t *pif)
   2043 {
   2044     COMPILER_REFERENCE(unit);
   2045     COMPILER_REFERENCE(port);
   2046 
   2047     *pif = SOC_PORT_IF_SGMII;
   2048 
   2049     return SOC_E_NONE;
   2050 }
   2051 
   2052 /*
   2053  * Function:
   2054  *      phy_54684_ability_get
   2055  * Purpose:
   2056  *      Get the abilities of the local gigabit PHY.
   2057  * Parameters:
   2058  *      unit - StrataSwitch unit #.
   2059  *      port - StrataSwitch port #.
   2060  *      mode - (OUT) Port mode mask indicating supported options/speeds.
   2061  * Returns:
   2062  *      SOC_E_NONE
   2063  * Notes:
   2064  *      The ability is retrieved only for the ACTIVE medium.
   2065  */
   2066 
   2067 STATIC int
   2068 phy_54684_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   2069 {
   2070     int         rv;
   2071     phy_ctrl_t *pc;
   2072 
   2073     rv = SOC_E_NONE;
   2074     if (PHY_COPPER_MODE(unit, port)) {
   2075         rv = phy_ge_ability_get(unit, port, mode);
   2076     } else { /* fiber mode */
   2077         *mode = (SOC_PM_AN | SOC_PM_LB_PHY | 
   2078                  SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM |
   2079                  SOC_PM_GMII | SOC_PM_SGMII | SOC_PM_1000MB_FD |
   2080                  SOC_PM_100MB_FD | SOC_PM_100MB_HD);
   2081     }
   2082 
   2083     pc = EXT_PHY_SW_STATE(unit, port);
   2084     if (pc->automedium) {
   2085         *mode |= SOC_PM_COMBO;
   2086     }
   2087 
   2088     return rv;
   2089 }
   2090 
   2091 /*
   2092  * Function:
   2093  *      phy_54684_mdix_set
   2094  * Description:
   2095  *      Set the Auto-MDIX mode of a port/PHY
   2096  * Parameters:
   2097  *      unit - Device number
   2098  *      port - Port number
   2099  *      mode - One of:
   2100  *              SOC_PORT_MDIX_AUTO
   2101  *                      Enable auto-MDIX when autonegotiation is enabled
   2102  *              SOC_PORT_MDIX_FORCE_AUTO
   2103  *                      Enable auto-MDIX always
   2104  *              SOC_PORT_MDIX_NORMAL
   2105  *                      Disable auto-MDIX
   2106  *              SOC_PORT_MDIX_XOVER
   2107  *                      Disable auto-MDIX, and swap cable pairs
   2108  * Return Value:
   2109  *      SOC_E_XXX
   2110  */
   2111 STATIC int
   2112 phy_54684_mdix_set(int unit, soc_port_t port, soc_port_mdix_t mode)
   2113 {
   2114     phy_ctrl_t    *pc;
   2115     int                  speed;
   2116 
   2117     pc = EXT_PHY_SW_STATE(unit, port);
   2118     if (PHY_COPPER_MODE(unit, port)) {
   2119         switch (mode) {
   2120         case SOC_PORT_MDIX_AUTO:
   2121             /* Clear bit 14 for automatic MDI crossover */
   2122             SOC_IF_ERROR_RETURN
   2123                 (MODIFY_PHY54684_MII_ECRr(unit, pc, 0, 0x4000));
   2124 
   2125             /* Clear bit 9 to disable forced auto MDI xover */
   2126             SOC_IF_ERROR_RETURN
   2127                 (MODIFY_PHY54684_MII_MISC_CTRLr(unit, pc, 0, 0x0200));
   2128             break;
   2129 
   2130         case SOC_PORT_MDIX_FORCE_AUTO:
   2131             /* Clear bit 14 for automatic MDI crossover */
   2132             SOC_IF_ERROR_RETURN
   2133                 (MODIFY_PHY54684_MII_ECRr(unit, pc, 0, 0x4000));
   2134 
   2135             /* Set bit 9 to disable forced auto MDI xover */
   2136             SOC_IF_ERROR_RETURN
   2137                 (MODIFY_PHY54684_MII_MISC_CTRLr(unit, pc, 0x0200, 0x0200));
   2138             break;
   2139 
   2140         case SOC_PORT_MDIX_NORMAL:
   2141             SOC_IF_ERROR_RETURN(phy_54684_speed_get(unit, port, &speed));
   2142             if (speed == 0 || speed == 10 || speed == 100) {
   2143                 /* Set bit 14 for automatic MDI crossover */
   2144                 SOC_IF_ERROR_RETURN
   2145                     (MODIFY_PHY54684_MII_ECRr(unit, pc, 0x4000, 0x4000));
   2146 
   2147                 SOC_IF_ERROR_RETURN
   2148                     (WRITE_PHY54684_TEST1r(unit, pc, 0));
   2149             } else {
   2150                 return SOC_E_UNAVAIL;
   2151             }
   2152             break;
   2153 
   2154         case SOC_PORT_MDIX_XOVER:
   2155             SOC_IF_ERROR_RETURN(phy_54684_speed_get(unit, port, &speed));
   2156             if (speed == 0 || speed == 10 || speed == 100) {
   2157                 /* Set bit 14 for automatic MDI crossover */
   2158                 SOC_IF_ERROR_RETURN
   2159                     (MODIFY_PHY54684_MII_ECRr(unit, pc, 0x4000, 0x4000));
   2160 
   2161                 SOC_IF_ERROR_RETURN
   2162                     (WRITE_PHY54684_TEST1r(unit, pc, 0x0080));
   2163             } else {
   2164                 return SOC_E_UNAVAIL;
   2165             }
   2166             break;
   2167 
   2168         default:
   2169             return SOC_E_PARAM;
   2170         }
   2171         pc->copper.mdix = mode;
   2172     } else {    /* fiber */
   2173         if (mode != SOC_PORT_MDIX_NORMAL) {
   2174             return SOC_E_UNAVAIL;
   2175         }
   2176     }
   2177     return SOC_E_NONE;
   2178 }        
   2179 
   2180 #if defined(BCM_WARM_BOOT_SUPPORT)
   2181 /*
   2182  * Function:
   2183  *      _phy_54684_mdix_wb_update
   2184  * Description:
   2185  *      Read the hw, derive the mode and update the mdix mode in sw structure
   2186  *      during wb.
   2187  * Parameters:
   2188  *      unit - (IN) Device number
   2189  *      port - (IN) Port number
   2190  *      mode - (OUT) :One of:
   2191  *              SOC_PORT_MDIX_AUTO
   2192  *                      Enable auto-MDIX when autonegotiation is enabled
   2193  *              SOC_PORT_MDIX_FORCE_AUTO
   2194  *                      Enable auto-MDIX always
   2195  *              SOC_PORT_MDIX_NORMAL
   2196  *                      Disable auto-MDIX
   2197  *              SOC_PORT_MDIX_XOVER
   2198  *                      Disable auto-MDIX, and swap cable pairs
   2199  * Return Value:
   2200  *      SOC_E_XXX
   2201  */
   2202 STATIC int
   2203 _phy_54684_mdix_wb_update(int unit, soc_port_t port)
   2204 {
   2205     phy_ctrl_t    *pc;
   2206     int            speed;
   2207     int            mode = 0;
   2208     uint16         val = 0;
   2209 
   2210     pc = EXT_PHY_SW_STATE(unit, port);
   2211 
   2212     /* Check bit 14 for automatic MDI crossover */
   2213     SOC_IF_ERROR_RETURN (READ_PHY54684_MII_ECRr(unit, pc, &val));
   2214     if(val & 0x4000) {
   2215         SOC_IF_ERROR_RETURN(phy_54684_speed_get(unit, port, &speed));
   2216         if (speed == 0 || speed == 10 || speed == 100) {
   2217             SOC_IF_ERROR_RETURN (READ_PHY54684_TEST1r(unit, pc, &val));
   2218             if (0 == val) {
   2219                 mode = SOC_PORT_MDIX_NORMAL;
   2220             } else if (0x80 == val) {
   2221                 mode = SOC_PORT_MDIX_XOVER;
   2222             } else {
   2223                 return SOC_E_UNAVAIL;
   2224             }
   2225         } else {
   2226             return SOC_E_UNAVAIL;
   2227         }
   2228     } else {
   2229         /* Check bit 9 forced auto MDI xover */
   2230         SOC_IF_ERROR_RETURN (READ_PHY54684_MII_MISC_CTRLr(unit, pc,&val));
   2231         if (val & 0x200) {
   2232             /* bit 9 Set, forced auto MDI xover */
   2233             mode = SOC_PORT_MDIX_FORCE_AUTO;
   2234         } else {
   2235             /* bit 9 Clear, forced auto MDI xover */
   2236             mode = SOC_PORT_MDIX_AUTO;
   2237         }
   2238     }
   2239     pc->copper.mdix = mode;
   2240 
   2241     return SOC_E_NONE;
   2242 }
   2243 #endif /* defined(BCM_WARM_BOOT_SUPPORT) */
   2244 
   2245 /*
   2246  * Function:
   2247  *      phy_54684_mdix_get
   2248  * Description:
   2249  *      Get the Auto-MDIX mode of a port/PHY
   2250  * Parameters:
   2251  *      unit - Device number
   2252  *      port - Port number
   2253  *      mode - (Out) One of:
   2254  *              SOC_PORT_MDIX_AUTO
   2255  *                      Enable auto-MDIX when autonegotiation is enabled
   2256  *              SOC_PORT_MDIX_FORCE_AUTO
   2257  *                      Enable auto-MDIX always
   2258  *              SOC_PORT_MDIX_NORMAL
   2259  *                      Disable auto-MDIX
   2260  *              SOC_PORT_MDIX_XOVER
   2261  *                      Disable auto-MDIX, and swap cable pairs
   2262  * Return Value:
   2263  *      SOC_E_XXX
   2264  */
   2265 STATIC int
   2266 phy_54684_mdix_get(int unit, soc_port_t port, soc_port_mdix_t *mode)
   2267 {
   2268     phy_ctrl_t    *pc;
   2269     int            speed;
   2270 
   2271     if (mode == NULL) {
   2272         return SOC_E_PARAM;
   2273     }
   2274 
   2275 #if defined(BCM_WARM_BOOT_SUPPORT)
   2276     if (SOC_WARM_BOOT(unit)) {
   2277         return _phy_54684_mdix_wb_update(unit, port);
   2278     }
   2279 #endif /* defined(BCM_WARM_BOOT_SUPPORT) */
   2280 
   2281     pc = EXT_PHY_SW_STATE(unit, port);
   2282 
   2283     if (PHY_COPPER_MODE(unit, port)) {
   2284         SOC_IF_ERROR_RETURN(phy_54684_speed_get(unit, port, &speed));
   2285         if (speed == 1000) {
   2286            *mode = SOC_PORT_MDIX_AUTO;
   2287         } else {
   2288             *mode = pc->copper.mdix;
   2289         }
   2290     } else {
   2291         *mode = SOC_PORT_MDIX_NORMAL;
   2292     }
   2293 
   2294     return SOC_E_NONE;
   2295 }    
   2296 
   2297 /*
   2298  * Function:
   2299  *      phy_54684_mdix_status_get
   2300  * Description:
   2301  *      Get the current MDIX status on a port/PHY
   2302  * Parameters:
   2303  *      unit    - Device number
   2304  *      port    - Port number
   2305  *      status  - (OUT) One of:
   2306  *              SOC_PORT_MDIX_STATUS_NORMAL
   2307  *                      Straight connection
   2308  *              SOC_PORT_MDIX_STATUS_XOVER
   2309  *                      Crossover has been performed
   2310  * Return Value:
   2311  *      SOC_E_XXX
   2312  */
   2313 STATIC int
   2314 phy_54684_mdix_status_get(int unit, soc_port_t port, 
   2315                          soc_port_mdix_status_t *status)
   2316 {
   2317     phy_ctrl_t    *pc;
   2318     uint16          data;
   2319 
   2320     if (status == NULL) {
   2321         return SOC_E_PARAM;
   2322     }
   2323 
   2324     pc = EXT_PHY_SW_STATE(unit, port);
   2325 
   2326     if (PHY_COPPER_MODE(unit, port)) {
   2327         SOC_IF_ERROR_RETURN
   2328             (READ_PHY54684_MII_ESRr(unit, pc, &data));
   2329         if (data & 0x2000) {
   2330             *status = SOC_PORT_MDIX_STATUS_XOVER;
   2331         } else {
   2332             *status = SOC_PORT_MDIX_STATUS_NORMAL;
   2333         }
   2334     } else {
   2335         *status = SOC_PORT_MDIX_STATUS_NORMAL;
   2336     }
   2337 
   2338     return SOC_E_NONE;
   2339 }    
   2340 
   2341 STATIC int
   2342 _phy_54684_power_mode_set (int unit, soc_port_t port, int mode)
   2343 {
   2344     phy_ctrl_t    *pc;
   2345 
   2346     pc       = EXT_PHY_SW_STATE(unit, port);
   2347 
   2348     if (pc->power_mode == mode) {
   2349         return SOC_E_NONE;
   2350     }
   2351 
   2352     if (mode == SOC_PHY_CONTROL_POWER_LOW) {
   2353         /* enable dsp clock */
   2354         SOC_IF_ERROR_RETURN
   2355             (MODIFY_PHY54684_MII_AUX_CTRLr(unit,pc,0x0c00,0x0c00));
   2356 
   2357         /* enable low power 136 */
   2358         SOC_IF_ERROR_RETURN
   2359             (MODIFY_PHY54684_AUTO_POWER_DOWNr(unit,pc,0x80,0x80));
   2360 
   2361         /* reduce tx bias current to -20% */
   2362         SOC_IF_ERROR_RETURN
   2363             (WRITE_PHY_REG(unit, pc, 0x17, 0x0f75));
   2364         SOC_IF_ERROR_RETURN
   2365             (WRITE_PHY_REG(unit, pc, 0x15, 0x1555));
   2366 
   2367         /* disable dsp clock */
   2368         SOC_IF_ERROR_RETURN
   2369             (MODIFY_PHY54684_MII_AUX_CTRLr(unit,pc,0x0400,0x0c00));
   2370         pc->power_mode = mode;
   2371 
   2372     } else if (mode == SOC_PHY_CONTROL_POWER_FULL) {
   2373 
   2374         /* back to normal mode */
   2375         /* enable dsp clock */
   2376         SOC_IF_ERROR_RETURN
   2377             (MODIFY_PHY54684_MII_AUX_CTRLr(unit,pc,0x0c00,0x0c00));
   2378 
   2379         /* disable low power 136 */
   2380         SOC_IF_ERROR_RETURN
   2381             (MODIFY_PHY54684_AUTO_POWER_DOWNr(unit,pc,0x00,0x80));
   2382 
   2383         /* set tx bias current to nominal */
   2384         SOC_IF_ERROR_RETURN
   2385             (WRITE_PHY_REG(unit, pc, 0x17, 0x0f75));
   2386         SOC_IF_ERROR_RETURN
   2387             (WRITE_PHY_REG(unit, pc, 0x15, 0x0));
   2388 
   2389         /* disable dsp clock */
   2390         SOC_IF_ERROR_RETURN
   2391             (MODIFY_PHY54684_MII_AUX_CTRLr(unit,pc,0x0400,0x0c00));
   2392 
   2393         /* disable the auto power mode */
   2394         SOC_IF_ERROR_RETURN
   2395             (MODIFY_PHY54684_AUTO_POWER_DOWNr(unit,pc,
   2396                         0,
   2397                         PHY_54684_AUTO_PWRDWN_EN));
   2398         pc->power_mode = mode;
   2399     } else if (mode == SOC_PHY_CONTROL_POWER_AUTO) {
   2400         SOC_IF_ERROR_RETURN
   2401             (MODIFY_PHY54684_AUTO_POWER_DOWNr(unit,pc,
   2402                         PHY_54684_AUTO_PWRDWN_EN,
   2403                         PHY_54684_AUTO_PWRDWN_EN));
   2404         pc->power_mode = mode;
   2405     }
   2406 
   2407     return SOC_E_NONE;
   2408 }
   2409 
   2410 /*
   2411  * Function:
   2412  *      phy_54684_control_set
   2413  * Purpose:
   2414  *      Configure PHY device specific control fucntion.
   2415  * Parameters:
   2416  *      unit  - StrataSwitch unit #.
   2417  *      port  - StrataSwitch port #.
   2418  *      type  - Control to update
   2419  *      value - New setting for the control
   2420  * Returns:
   2421  *      SOC_E_NONE
   2422  */
   2423 STATIC int
   2424 phy_54684_control_set(int unit, soc_port_t port,
   2425                      soc_phy_control_t type, uint32 value)
   2426 {
   2427     int rv;
   2428     phy_ctrl_t *pc;
   2429     uint16 data;
   2430 
   2431     PHY_CONTROL_TYPE_CHECK(type);
   2432 
   2433     pc = EXT_PHY_SW_STATE(unit, port);
   2434     rv = SOC_E_NONE;
   2435     switch(type) {
   2436         case SOC_PHY_CONTROL_POWER:
   2437             rv = _phy_54684_power_mode_set(unit,port,value);
   2438         break;
   2439 
   2440     case SOC_PHY_CONTROL_POWER_AUTO_WAKE_TIME:
   2441         if (value <= PHY_54684_AUTO_PWRDWN_WAKEUP_MAX) {
   2442 
   2443             /* at least one unit */
   2444             if (value < PHY_54684_AUTO_PWRDWN_WAKEUP_UNIT) {
   2445                 value = PHY_54684_AUTO_PWRDWN_WAKEUP_UNIT;
   2446             }
   2447         } else { /* anything more then max, set to the max */
   2448             value = PHY_54684_AUTO_PWRDWN_WAKEUP_MAX;
   2449         }
   2450         SOC_IF_ERROR_RETURN
   2451             (MODIFY_PHY54684_AUTO_POWER_DOWNr(unit,pc,
   2452                       value/PHY_54684_AUTO_PWRDWN_WAKEUP_UNIT,
   2453                       PHY_54684_AUTO_PWRDWN_WAKEUP_MASK));
   2454         break;
   2455 
   2456     case SOC_PHY_CONTROL_POWER_AUTO_SLEEP_TIME:
   2457         /* sleep time configuration is either 2.7s or 5.4 s, default is 2.7s */
   2458         if (value < PHY_54684_AUTO_PWRDWN_SLEEP_MAX) {
   2459             data = 0; /* anything less than 5.4s, default to 2.7s */
   2460         } else {
   2461             data = PHY_54684_AUTO_PWRDWN_SLEEP_MASK;
   2462         }
   2463         SOC_IF_ERROR_RETURN
   2464             (MODIFY_PHY54684_AUTO_POWER_DOWNr(unit,pc,
   2465                       data,
   2466                       PHY_54684_AUTO_PWRDWN_SLEEP_MASK));
   2467         break;
   2468 
   2469     default:
   2470         rv = SOC_E_UNAVAIL;
   2471         break;
   2472     }
   2473     return rv;
   2474 }
   2475 /*
   2476  * Function:
   2477  *      phy_54684_control_get
   2478  * Purpose:
   2479  *      Get current control settign of the PHY.
   2480  * Parameters:
   2481  *      unit  - StrataSwitch unit #.
   2482  *      port  - StrataSwitch port #.
   2483  *      type  - Control to update
   2484  *      value - (OUT)Current setting for the control
   2485  * Returns:
   2486  *      SOC_E_NONE
   2487  */
   2488 STATIC int
   2489 phy_54684_control_get(int unit, soc_port_t port,
   2490                      soc_phy_control_t type, uint32 *value)
   2491 {
   2492     int rv;
   2493     phy_ctrl_t *pc;
   2494     uint16 data;
   2495 
   2496     PHY_CONTROL_TYPE_CHECK(type);
   2497 
   2498     pc = EXT_PHY_SW_STATE(unit, port);
   2499     rv = SOC_E_NONE;
   2500     switch(type) {
   2501         case SOC_PHY_CONTROL_POWER:
   2502             *value = pc->power_mode;
   2503         break;
   2504 
   2505     case SOC_PHY_CONTROL_POWER_AUTO_SLEEP_TIME:
   2506         SOC_IF_ERROR_RETURN
   2507             (READ_PHY54684_AUTO_POWER_DOWNr(unit,pc, &data));
   2508 
   2509         if (data & PHY_54684_AUTO_PWRDWN_SLEEP_MASK) {
   2510             *value = PHY_54684_AUTO_PWRDWN_SLEEP_MAX;
   2511         } else {
   2512             *value = 2700;
   2513         }
   2514         break;
   2515 
   2516     case SOC_PHY_CONTROL_POWER_AUTO_WAKE_TIME:
   2517         SOC_IF_ERROR_RETURN
   2518             (READ_PHY54684_AUTO_POWER_DOWNr(unit,pc, &data));
   2519 
   2520         data &= PHY_54684_AUTO_PWRDWN_WAKEUP_MASK;
   2521         *value = data * PHY_54684_AUTO_PWRDWN_WAKEUP_UNIT;
   2522         break;
   2523 
   2524     default:
   2525         rv = SOC_E_UNAVAIL;
   2526         break;
   2527     }
   2528     return rv;
   2529 }
   2530 
   2531 /*
   2532  * Function:
   2533  *      phy_54684_cable_diag
   2534  * Purpose:
   2535  *      Run 546x cable diagnostics
   2536  * Parameters:
   2537  *      unit - device number
   2538  *      port - port number
   2539  *      status - (OUT) cable diagnotic status structure
   2540  * Returns:     
   2541  *      SOC_E_XXX
   2542  */
   2543 STATIC int
   2544 phy_54684_cable_diag(int unit, soc_port_t port,
   2545                     soc_port_cable_diag_t *status)
   2546 {
   2547     int                 rv=SOC_E_NONE, rv2, i;
   2548 
   2549     extern int phy_5464_cable_diag_sw(int, soc_port_t , 
   2550                                       soc_port_cable_diag_t *);
   2551 
   2552     if (status == NULL) {
   2553         return SOC_E_PARAM;
   2554     }
   2555 
   2556     status->state = SOC_PORT_CABLE_STATE_OK;
   2557     status->npairs = 4;
   2558     status->fuzz_len = 0;
   2559     for (i = 0; i < 4; i++) {
   2560         status->pair_state[i] = SOC_PORT_CABLE_STATE_OK;
   2561     }
   2562 
   2563     MIIM_LOCK(unit);    /* this locks out linkscan, essentially */
   2564     rv = phy_5464_cable_diag_sw(unit,port, status);
   2565     MIIM_UNLOCK(unit);
   2566     rv2 = 0;
   2567     if (rv <= 0) {      /* don't reset if > 0 -- link was up */
   2568         rv2 = _phy_54684_reset_setup(unit, port);
   2569     }
   2570     if (rv >= 0 && rv2 < 0) {
   2571         return rv2;
   2572     }
   2573     return rv;
   2574 }
   2575 
   2576 #ifdef BROADCOM_DEBUG
   2577 
   2578 /*
   2579  * Function:
   2580  *      phy_54684_shadow_dump
   2581  * Purpose:
   2582  *      Debug routine to dump all shadow registers.
   2583  * Parameters:
   2584  *      unit - StrataSwitch unit #.
   2585  *      port - StrataSwitch port #.
   2586  */
   2587 
   2588 void
   2589 phy_54684_shadow_dump(int unit, soc_port_t port)
   2590 {
   2591     phy_ctrl_t *pc;
   2592     uint16      tmp;
   2593     int         i;
   2594 
   2595     pc       = EXT_PHY_SW_STATE(unit, port);
   2596 
   2597     /* Register 0x18 Shadows */
   2598     for (i = 0; i <= 7; i++) {
   2599         WRITE_PHY_REG(unit, pc, 0x18, (i << 12) | 0x7);
   2600         READ_PHY_REG(unit, pc, 0x18, &tmp);
   2601         if ((tmp & ~7) == 0x0000) {
   2602             continue;
   2603         }
   2604         LOG_CLI((BSL_META_U(unit,
   2605                             "0x18[0x%x]=0x%04x\n"), i, tmp));
   2606     }
   2607 
   2608     /* Register 0x1c Shadows */
   2609     for (i = 0; i <= 0x1f; i++) {
   2610         WRITE_PHY_REG(unit, pc, 0x1c, i << 10);
   2611         READ_PHY_REG(unit, pc, 0x1c, &tmp);
   2612         if ((tmp & ~0x7c00) == 0x0000) {
   2613             continue;
   2614         }
   2615         LOG_CLI((BSL_META_U(unit,
   2616                             "0x1c[0x%x]=0x%04x\n"), i, tmp));
   2617     }
   2618 
   2619     /* Register 0x17/0x15 Shadows */
   2620     for (i = 0; i <= 0x13; i++) {
   2621         WRITE_PHY_REG(unit, pc, 0x17, 0xf00 | i);
   2622         READ_PHY_REG(unit, pc, 0x15, &tmp);
   2623         if (tmp  == 0x0000) {
   2624             continue;
   2625         }
   2626         LOG_CLI((BSL_META_U(unit,
   2627                             "0x17[0x%x]=0x%04x\n"), i, tmp));
   2628     }
   2629 }
   2630 
   2631 #endif /* BROADCOM_DEBUG */
   2632 
   2633 /*
   2634  * Variable:    phy_54684drv_ge
   2635  * Purpose:     PHY driver for 54684
   2636  */
   2637 
   2638 phy_driver_t phy_54684drv_ge = {
   2639     "54684 Gigabit PHY Driver",
   2640     phy_54684_init,
   2641     phy_fe_ge_reset,
   2642     phy_54684_link_get,
   2643     phy_54684_enable_set,
   2644     phy_54684_enable_get,
   2645     phy_54684_duplex_set,
   2646     phy_54684_duplex_get,
   2647     phy_54684_speed_set,
   2648     phy_54684_speed_get,
   2649     phy_54684_master_set,
   2650     phy_54684_master_get,
   2651     phy_54684_autoneg_set,
   2652     phy_54684_autoneg_get,
   2653     phy_54684_adv_local_set,
   2654     phy_54684_adv_local_get,
   2655     phy_54684_adv_remote_get,
   2656     phy_54684_lb_set,
   2657     phy_54684_lb_get,
   2658     phy_54684_interface_set,
   2659     phy_54684_interface_get,
   2660     phy_54684_ability_get,
   2661     NULL,                        /* Phy link up event */
   2662     NULL,                        /* Phy link down event */
   2663     phy_54684_mdix_set,
   2664     phy_54684_mdix_get,
   2665     phy_54684_mdix_status_get,
   2666     phy_54684_medium_config_set,
   2667     phy_54684_medium_config_get,
   2668     phy_54684_medium_status,
   2669     phy_54684_cable_diag,
   2670     NULL,                       /* phy_link_change */
   2671     phy_54684_control_set,      /* phy_control_set */ 
   2672     phy_54684_control_get,      /* phy_control_get */
   2673     phy_ge_reg_read,
   2674     phy_ge_reg_write,
   2675     phy_ge_reg_modify,
   2676     NULL                        /* Phy event notify */ 
   2677 };
   2678 
   2679 #else /* INCLUDE_PHY_54684 */
   2680 int _soc_phy_54684_not_empty;
   2681 #endif /* INCLUDE_PHY_54684 */
   2682