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

phy5464.c (103289B)


      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:        phy5464.c
      8  * Purpose:     PHY driver for BCM5461, BCM5464 and BCM548x
      9  *
     10  * Supported BCM546X Family of PHY devices:
     11  *
     12  *      Device  Ports   Media                           MAC Interface
     13  *      5461    1       Copper                          GMII
     14  *      5461S   1       Copper, Serdes                  GMII, SGMII
     15  *      5464    4       Copper, Serdes                  GMII, SGMII
     16  *      5464R   4       Copper, Serdes                  SGMII
     17  *      5464S   4       Copper, Serdes, Passthru        GMII, SGMII
     18  *      5464SR  4       Copper, Serdes, Passthru        SGMII
     19  *      5466    4       Copper, Serdes                  GMII, SGMII
     20  *      5466R   4       Copper, Serdes                  SGMII
     21  *      5466S   4       Copper, Serdes, Passthru        GMII, SGMII
     22  *      5466SR  4       Copper, Serdes, Passthru        SGMII
     23  *      5488    8       Copper                          SGMII
     24  *      54980   8       Copper                          SGMII
     25  *
     26  * The 5466 devices have a strapping option that will make them act
     27  * identically to the corresponding 5464 device (including PHY ID
     28  * register values).
     29  *
     30  * Other devices in this family, but not supported by this driver:
     31  *      5464H   4       Copper                          RGMII(HSTL)
     32  *      5465    4       Copper                          RGMII
     33  *
     34  * This driver works with both 569x switches that have an SGMII interface
     35  * and with Strata and 5665 switches with a GMII interface.  There is no
     36  * support in this driver for RGMII, TBI, or RTBI interfaces.
     37  *
     38  * All of these PHY devices support a 100FX media mode, but currently this
     39  * driver does not.
     40  *
     41  * If this driver is compiled without 569x support, then only GMII (Copper,
     42  * Fiber) and SGMII (Copper) support will be enabled.
     43  *
     44  * SGMII support:
     45  *      The 5690, 5695, 56504 and 56601 switch devices provide a serial
     46  *      GMII (SGMII) MAC interface to a PHY device.  The PHY devices
     47  *      above with an SGMII interface and no Passthru support are
     48  *      supported as copper only PHYs.  Although the PHYs have a Serdes
     49  *      media interface suitable for fiber media, it shares the same
     50  *      electronics as the SGMII MAC interface.  The 569x internal serdes
     51  *      is kept into passthru mode and the PHY performs all signaling.
     52  *
     53  *      Those PHYs with a Passthru media interface can support both Copper
     54  *      (as above) and Fiber media (by putting the PHY into passthru mode
     55  *      and having the 569x serdes handle all signaling).  There is no way
     56  *      for the driver to determine from the PHY device if it has a Passthru
     57  *      media interface, so properties must be set if Fiber media is to be
     58  *      used.  The property "phy_5464S" or "phy_5464S_portXX" must be set if
     59  *      the PHY device is a 5464S or 5464SR.
     60  *
     61  *      In all of the cases for 569x connections, the PHY's 1000BASE-X
     62  *      registers are never enabled.
     63  *
     64  *      The 569x switches do not support SGMII auto-negotiation.  They only
     65  *      support Serdes auto-negotiation.  In Copper mode, 569x autoneg must
     66  *      be off and the PHY device autoneg on the SGMII mac interface must
     67  *      also be off.  In Passthru (Fiber) mode, the 569x is used for Serdes
     68  *      autoneg and the 5464 is in passthru.
     69  *
     70  * GMII support:
     71  *      The Strata and 5665 family of switch devices provide a GMII MAC
     72  *      interface to a PHY device.  The PHY devices with a GMII interface
     73  *      can be connected to these switches.  Any PHY device with a Serdes
     74  *      media interface can be used for both Copper and Fiber media
     75  *      connections.  Those PHY devices without a Serdes interface can only
     76  *      be used in Copper mode.
     77  *
     78  *      The Passthru mode in any PHY devices is ignored.
     79  *
     80  * Auto-medium selection:
     81  *      Automatic selection of copper vs. fiber medium type is enabled when
     82  *      the "phy_automedium" property is 1 (default 0 for 5464 on 569x
     83  *      and 1 otherwise).  If both copper and fiber signals are present, the
     84  *      driver consults the configuration property "phy_fiber_pref_portXX"
     85  *      (default 1) to determine whether copper or fiber is preferred.
     86  *
     87  *      See sdk/doc/combo-ports.txt for more information.
     88  *
     89  *      NOTE: auto-medium REQUIRES the fiber module energy detect pin to be
     90  *      wired to the LED4 pin on the 5464S.
     91  *
     92  * Workarounds:
     93  *      ERRATA_ENABLE: (default 0)
     94  *      The driver will deal with various errata for 5464 A0 and 5461 A0
     95  *      revision PHY devices.  It is not enabled by default because
     96  *      production use of these PHYs should be using A1 or later revisions.
     97  *
     98  *      LINK_DOWN_RESET_WAR: (default 1)
     99  *      The PHY will be reset each time link down is detected on copper.
    100  *      This is due to the 100TX_SWAP erratum on 5464 A0 revision parts.
    101  */
    102 #include <shared/bsl.h>
    103 
    104 #include <sal/types.h>
    105 #include <sal/core/spl.h>
    106 #include <soc/drv.h>
    107 #include <soc/error.h>
    108 #include <soc/phyreg.h>
    109 
    110 #include <soc/phy.h>
    111 #include <soc/phy/phyctrl.h>
    112 #include <soc/phy/drv.h>
    113 
    114 #include "phydefs.h"      /* Must include before other phy related includes */
    115 
    116 #if defined(INCLUDE_PHY_5464_ESW)
    117 #include "phyconfig.h"    /* Must be the first phy include after phydefs.h */
    118 
    119 #include "phyident.h"
    120 #include "phyreg.h"
    121 #include "phyfege.h"
    122 #include "phy5464.h"
    123 
    124 /*
    125  * Please see the errata sheet for the PHY devices for a description of
    126  * the errata to decide which workarounds (WARs) to enable for your
    127  * application.
    128  */
    129 
    130 #define LINK_DOWN_RESET_WAR     1       /* Reset PHY on copper link down */
    131 #define ERRATA_ENABLE           0       /* rev A0 errata */
    132 
    133 #define AUTO_MDIX_WHEN_AN_DIS   0       /* Non-standard-compliant option */
    134 #define DISABLE_CLK125          0       /* Disable if unused to reduce */
    135                                         /*  EMI emissions */
    136 
    137 #define DPRINTF(_s)             LOG_INFO(BSL_LS_SOC_PHY, _s)
    138 #define DPRINTF_VERBOSE(_s)     LOG_VERBOSE(BSL_LS_SOC_PHY, _s)
    139 
    140 #ifndef PHY_5464_CABLE_DIAG_HW
    141 #define PHY_5464_CABLE_DIAG_HW  0
    142 #endif
    143 
    144 /*
    145  * passthru mode is used for fiber support on 569x where the phy
    146  * is put into serdes passthru mode and the serdes support in the
    147  * 569x is used.
    148  */
    149 
    150 #define ADVERT_ALL_COPPER \
    151         (SOC_PM_PAUSE | SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB)
    152 #define ADVERT_ALL_FIBER \
    153         (SOC_PM_PAUSE | SOC_PM_1000MB_FD)
    154 
    155 #define PHY_REV_A0              0
    156 #define PHY_REV_A1              1
    157 
    158 /* In the serial phy546x(includes phy5466), the fiber SD is required
    159  * for supporting the combo medium solution. The SD might be active high or
    160  * active low depends on HW designing in customer.
    161  *   SDK phy driver might indicate such definition by "config.bcm". And here we
    162  * define HW_DEFAULT_SD_VALUE for such preference when no config.bcm definition
    163  * been loaded.
    164  * --------------------------------------------------
    165  *  1. bcm95324a0 : SD active low
    166  *  2. bcm95324a1 : SD active low
    167  *  3. ADD.....
    168  */
    169 #define HW_DEFAULT_SD_VALUE         -10
    170 
    171 STATIC int phy_5464_duplex_set(int unit, soc_port_t port, int duplex);
    172 STATIC int phy_5464_speed_set(int unit, soc_port_t port, int speed);
    173 STATIC int phy_5464_master_set(int unit, soc_port_t port, int master);
    174 STATIC int phy_5464_adv_local_set(int unit, soc_port_t port,
    175                                   soc_port_mode_t mode);
    176 STATIC int phy_5464_autoneg_set(int unit, soc_port_t port, int autoneg);
    177 STATIC int phy_5464_mdix_set(int unit, soc_port_t port, soc_port_mdix_t mode);
    178 STATIC int _phy_5464_medium_change(int unit, soc_port_t port, int force_update);
    179 /*********************************************************************** 
    180  * 
    181  * HELPER FUNCTIONS 
    182  */
    183 /*
    184  * Function:
    185  *      _phy_5464_medium_check
    186  * Purpose:
    187  *      Determine if chip should operate in copper or fiber mode.
    188  * Parameters:
    189  *      unit - StrataSwitch unit #.
    190  *      port - StrataSwitch port #.
    191  *      medium - (OUT) SOC_PORT_MEDIUM_XXX
    192  * Returns:
    193  *      SOC_E_XXX
    194  */
    195 
    196 STATIC int
    197 _phy_5464_medium_check(int unit, soc_port_t port, int *medium)
    198 {
    199     phy_ctrl_t        *pc;    /* PHY state */
    200     uint16             tmp;    /* Temp variable */
    201     int                copper; /* Copper medium is active */
    202 
    203     pc    = EXT_PHY_SW_STATE(unit, port);
    204     /* Read Mode Register (0x1c shadow 11111) */
    205     SOC_IF_ERROR_RETURN
    206         (READ_PHY5464_MODE_CTRLr(unit, pc, &tmp));
    207     if (PHY_FORCED_COPPER_MODE(unit, port)) {
    208         copper = TRUE;
    209     } else {
    210         if (!pc->fiber.enable) {
    211             copper = TRUE;
    212         } else if (!pc->copper.enable) {
    213             copper = FALSE;
    214         } else {
    215             if (pc->fiber.preferred) {
    216                 /* 0x10 Fiber Signal Detect
    217                  * 0x20 Copper Energy Detect
    218                  */
    219                 copper = ((tmp & 0x30) == 0x20); 
    220             } else {
    221                 copper = ((tmp & 0x20) == 0x20);
    222             }
    223         }
    224     }
    225     *medium = copper ? SOC_PORT_MEDIUM_COPPER : SOC_PORT_MEDIUM_FIBER;
    226 
    227     DPRINTF_VERBOSE((BSL_META_U(unit,
    228                                 "_phy_5464_medium_check: "
    229                                 "u=%d p=%d fiber_pref=%d 0x1c(11111)=%04x\n"),
    230                      unit, port, pc->fiber.preferred, tmp));
    231     DPRINTF_VERBOSE((BSL_META_U(unit,
    232                                 "_phy_5464_medium_check: u=%d p=%d fiber=%d\n"),
    233                     unit, port, !copper));
    234 
    235     return SOC_E_NONE;
    236 }
    237 
    238 /*
    239  * Function:
    240  *      phy_5464_medium_status
    241  * Purpose:
    242  *      Indicate the current active medium
    243  * Parameters:
    244  *      unit - StrataSwitch unit #.
    245  *      port - StrataSwitch port #.
    246  *      medium - (OUT) One of:
    247  *              SOC_PORT_MEDIUM_COPPER
    248  *              SOC_PORT_MEDIUM_FIBER
    249  * Returns:
    250  *      SOC_E_NONE
    251  */
    252 STATIC int
    253 phy_5464_medium_status(int unit, soc_port_t port, soc_port_medium_t *medium)
    254 {
    255     if (PHY_COPPER_MODE(unit, port)) {
    256         *medium = SOC_PORT_MEDIUM_COPPER;
    257     } else {
    258         *medium = SOC_PORT_MEDIUM_FIBER;
    259     }
    260     return SOC_E_NONE;
    261 }
    262 
    263 /*
    264  * Function:
    265  *      _phy_5464_active_medium_config_update
    266  * Purpose:
    267  *      Update the PHY with config parameters
    268  * Parameters:
    269  *      unit - StrataSwitch unit #.
    270  *      port - StrataSwitch port #.
    271  *      cfg - Config structure.
    272  * Returns:
    273  *      SOC_E_XXX
    274  */
    275 STATIC int
    276 _phy_5464_active_medium_config_update(int unit, soc_port_t port,
    277                                 soc_phy_config_t *cfg)
    278 {
    279     SOC_IF_ERROR_RETURN
    280         (phy_5464_speed_set(unit, port, cfg->force_speed));
    281     SOC_IF_ERROR_RETURN
    282         (phy_5464_duplex_set(unit, port, cfg->force_duplex));
    283     SOC_IF_ERROR_RETURN
    284         (phy_5464_master_set(unit, port, cfg->master));
    285     SOC_IF_ERROR_RETURN
    286         (phy_5464_adv_local_set(unit, port, cfg->autoneg_advert));
    287     SOC_IF_ERROR_RETURN
    288         (phy_5464_autoneg_set(unit, port, cfg->autoneg_enable));
    289     SOC_IF_ERROR_RETURN
    290         (phy_5464_mdix_set(unit, port, cfg->mdix));
    291 
    292     return SOC_E_NONE;
    293 }
    294 
    295 /*
    296  * Function:
    297  *      phy_5464_medium_config_set
    298  * Purpose:
    299  *      Set the operating parameters that are automatically selected
    300  *      when medium switches type.
    301  * Parameters:
    302  *      unit - StrataSwitch unit #
    303  *      port - Port number
    304  *      medium - SOC_PORT_MEDIUM_COPPER/FIBER
    305  *      cfg - Operating parameters
    306  * Returns:
    307  *      SOC_E_XXX
    308  */
    309 STATIC int
    310 phy_5464_medium_config_set(int unit, soc_port_t port, 
    311                            soc_port_medium_t  medium,
    312                            soc_phy_config_t  *cfg)
    313 {
    314     phy_ctrl_t       *pc;
    315     soc_phy_config_t *active_medium;  /* Currently active medium */
    316     soc_phy_config_t *change_medium;  /* Requested medium */
    317     soc_phy_config_t *other_medium;   /* The other medium */
    318     int               medium_update;
    319     soc_port_mode_t   advert_mask;
    320 
    321     if (NULL == cfg) {
    322         return SOC_E_PARAM;
    323     }
    324     
    325     pc            = EXT_PHY_SW_STATE(unit, port);
    326     medium_update = FALSE;
    327 
    328     switch (medium) {
    329     case SOC_PORT_MEDIUM_COPPER:
    330         if (!pc->automedium && !PHY_COPPER_MODE(unit, port)) {
    331              return SOC_E_UNAVAIL;
    332         }
    333         change_medium  = &pc->copper;
    334         other_medium   = &pc->fiber;
    335         advert_mask    = ADVERT_ALL_COPPER;
    336         break;
    337     case SOC_PORT_MEDIUM_FIBER:
    338         if (!pc->automedium && !PHY_FIBER_MODE(unit, port)) {
    339             return SOC_E_UNAVAIL;
    340         }
    341         change_medium  = &pc->fiber;
    342         other_medium   = &pc->copper;
    343         advert_mask    = ADVERT_ALL_FIBER;
    344         break;
    345     default:
    346         return SOC_E_PARAM;
    347     }
    348 
    349     /*
    350      * Changes take effect immediately if the target medium is active or 
    351      * the preferred medium changes.
    352      */
    353     if (change_medium->enable != cfg->enable) {
    354         medium_update = TRUE;
    355     }
    356     if (change_medium->preferred != cfg->preferred) {
    357         /* Make sure that only one medium is preferred */
    358         other_medium->preferred = !cfg->preferred;
    359         medium_update = TRUE;
    360     }
    361 
    362     sal_memcpy(change_medium, cfg, sizeof(*change_medium));
    363     change_medium->autoneg_advert &= advert_mask;
    364 
    365     if (medium_update) {
    366         /* The configuration may cause medium change. Check
    367          * and update medium.
    368          */
    369         SOC_IF_ERROR_RETURN
    370             (_phy_5464_medium_change(unit, port,TRUE));
    371     } else {
    372         active_medium = (PHY_COPPER_MODE(unit, port)) ?  
    373                           &pc->copper : &pc->fiber;
    374         if (active_medium == change_medium) {
    375             /* If the medium to update is active, update the configuration */
    376             SOC_IF_ERROR_RETURN
    377                 (_phy_5464_active_medium_config_update(unit, port, 
    378                                                        change_medium));
    379         }
    380     }
    381     return SOC_E_NONE;
    382 }
    383 
    384 /*
    385  * Function:
    386  *      phy_5464_medium_config_get
    387  * Purpose:
    388  *      Get the operating parameters that are automatically selected
    389  *      when medium switches type.
    390  * Parameters:
    391  *      unit - StrataSwitch unit #
    392  *      port - Port number
    393  *      medium - SOC_PORT_MEDIUM_COPPER/FIBER
    394  *      cfg - (OUT) Operating parameters
    395  * Returns:
    396  *      SOC_E_XXX
    397  */
    398 
    399 STATIC int
    400 phy_5464_medium_config_get(int unit, soc_port_t port, 
    401                            soc_port_medium_t medium,
    402                            soc_phy_config_t *cfg)
    403 {
    404     int            rv;
    405     phy_ctrl_t    *pc;
    406 
    407     pc = EXT_PHY_SW_STATE(unit, port);
    408     rv = SOC_E_NONE;
    409 
    410     switch (medium) {
    411     case SOC_PORT_MEDIUM_COPPER:
    412         if (pc->automedium || PHY_COPPER_MODE(unit, port)) {
    413             sal_memcpy(cfg, &pc->copper, sizeof (*cfg));
    414         } else {
    415             rv = SOC_E_UNAVAIL;
    416         }
    417         break;
    418     case SOC_PORT_MEDIUM_FIBER:
    419         if (pc->automedium || PHY_FIBER_MODE(unit, port)) {
    420             sal_memcpy(cfg, &pc->fiber, sizeof (*cfg));
    421         } else {
    422             rv = SOC_E_UNAVAIL;
    423         }
    424         break;
    425     default:
    426         rv = SOC_E_PARAM;
    427         break;
    428     }
    429 
    430     return rv;
    431 }
    432 
    433 /*
    434  * Function:
    435  *      _phy_5464_reset_setup
    436  * Purpose:
    437  *      Function to reset the PHY and set up initial operating registers.
    438  * Parameters:
    439  *      unit - StrataSwitch unit #.
    440  *      port - StrataSwitch port #.
    441  * Returns:
    442  *      SOC_E_XXX
    443  * Notes:
    444  *      Configures for copper/fiber according to the current
    445  *      setting of PHY_FLAGS_FIBER.
    446  */
    447 STATIC int
    448 _phy_5464_reset_setup(int unit, soc_port_t port)
    449 {
    450     phy_ctrl_t        *int_pc;
    451     phy_ctrl_t        *pc;
    452     uint16             tmp;
    453 
    454     DPRINTF((BSL_META_U(unit,
    455                         "_phy_5464_reset_setup: u=%d p=%d medium=%s\n"),
    456              unit, port,
    457              PHY_COPPER_MODE(unit, port) ? "COPPER" : "FIBER"));
    458 
    459     SOC_IF_ERROR_RETURN(phy_ge_init(unit, port));
    460 
    461 
    462     pc     = EXT_PHY_SW_STATE(unit, port);
    463     int_pc = INT_PHY_SW_STATE(unit, port);
    464 
    465     /* Configure interface to MAC */    
    466     SOC_IF_ERROR_RETURN
    467         (READ_PHY5464_1000X_MII_CTRLr(unit, pc, &tmp));
    468 
    469     /* phy_ge_init has reset the phy, powering down the unstrapped interface */
    470     /* make sure enabled interfaces are powered up */
    471     /* SGMII (passthrough fiber) or GMII fiber regs */
    472     if ((pc->fiber.enable) || (pc->interface == SOC_PORT_IF_SGMII)) {
    473         tmp &= ~MII_CTRL_PD;     /* remove power down */
    474     } else {
    475         tmp |= MII_CTRL_PD;      /* Keep the media in power down state if
    476                                   * not use. 
    477                                   */
    478     }
    479 
    480     if (PHY_SGMII_AUTONEG_MODE(unit, port)) {
    481         /*
    482          * Enable SGMII autonegotiation on the switch side so that the
    483          * link status changes are reflected in the switch. 
    484          * On Bradley devices, LAG failover feature depends on the SerDes
    485          * link staus to activate failover recovery.
    486          */ 
    487         tmp |= MII_CTRL_AE;
    488     } else {
    489         tmp &= ~MII_CTRL_AE;    /* disable SGMII autoneg. */
    490     }
    491 
    492     SOC_IF_ERROR_RETURN
    493         (WRITE_PHY5464_1000X_MII_CTRLr(unit, pc, tmp));
    494    
    495     /* copper regs */
    496     /* remove power down */
    497     if (pc->copper.enable) {
    498         tmp = PHY_DISABLED_MODE(unit, port) ? MII_CTRL_PD : 0;
    499     } else {
    500         tmp = MII_CTRL_PD;
    501     }
    502     SOC_IF_ERROR_RETURN
    503         (MODIFY_PHY5464_MII_CTRLr(unit, pc, tmp, MII_CTRL_PD));
    504 
    505     if (NULL != int_pc) {
    506         SOC_IF_ERROR_RETURN
    507             (PHY_INIT(int_pc->pd, unit, port));
    508 
    509         if (!PHY_SGMII_AUTONEG_MODE(unit, port)) {
    510             if (PHY_COPPER_MODE(unit, port)) {
    511                 SOC_IF_ERROR_RETURN
    512                     (PHY_NOTIFY(int_pc->pd, unit, port, 
    513                                 phyEventStop, PHY_STOP_COPPER));
    514             } else {        /* Fiber */
    515                 if (pc->fiber.preferred) {
    516                     /* Disable copper transmitter */
    517                     SOC_IF_ERROR_RETURN
    518                         (MODIFY_PHY5464_MII_ECRr(unit, pc, 0x2000, 0x2000));
    519                 }
    520  
    521                 SOC_IF_ERROR_RETURN
    522                     (PHY_NOTIFY(int_pc->pd, unit, port,
    523                                 phyEventResume, PHY_STOP_COPPER));
    524             }
    525         }
    526     } 
    527 #if ERRATA_ENABLE
    528     /*
    529      * Perform Errata workarounds for 5464SA0 silicon.
    530      * NOTE: Assumes PHY internal port number 1 to 4 is ((port & 3) + 1).
    531      */
    532 
    533     if (PHY_IS_BCM5464(pc) && pc->phy_rev == PHY_REV_A0 &&
    534         (port & 3) == 0) {
    535 
    536         phy_ctrl_t     *pc2;
    537 
    538         pc2 = PHY_EXT_SW_STATE(unit, port + 1);
    539 
    540         DPRINTF((BSL_META_U(unit,
    541                             "_phy_5464_reset_setup: u=%d p=%d ERRATA\n"),
    542                  unit, port));
    543 
    544         /* Internal Regulators Errata */
    545 
    546         SOC_IF_ERROR_RETURN
    547             (WRITE_PHY_REG(unit, pc, 0x18, 0x0282));
    548 
    549         /* Twisted Pair Output Voltage Errata */
    550 
    551         SOC_IF_ERROR_RETURN
    552             (WRITE_PHY_REG(unit, pc, 0x1e, 0x0012));
    553         SOC_IF_ERROR_RETURN
    554             (WRITE_PHY_REG(unit, pc2, 0x18, 0x0007));
    555         SOC_IF_ERROR_RETURN
    556             (READ_PHY_REG(unit, pc2, 0x18, &tmp));
    557         SOC_IF_ERROR_RETURN
    558             (WRITE_PHY_REG(unit, pc2, 0x18, tmp | 0x0800));
    559         SOC_IF_ERROR_RETURN
    560             (WRITE_PHY_REG(unit, pc2, 0x17, 0x000a));
    561         SOC_IF_ERROR_RETURN
    562             (READ_PHY_REG(unit, pc2, 0x15, &tmp));
    563         SOC_IF_ERROR_RET
    564             (WRITE_PHY_REG(unit, pc2, 0x15, tmp | 0x1000));
    565     }
    566 
    567     if (PHY_IS_BCM5461(pc) && pc->phy_rev == PHY_REV_A0) {
    568         /* Internal Regulators Erratum for 5461 (reg 0x18 shadow b'010) */
    569 
    570         SOC_IF_ERROR_RETURN
    571             (WRITE_PHY_REG(unit, pc, 0x18, 0x0602));
    572     }
    573 #endif /* ERRATA_ENABLE */
    574 
    575     /* Disable super-isolate */
    576     SOC_IF_ERROR_RETURN
    577         (MODIFY_PHY5464_MII_POWER_CTRLr(unit, pc, 0x0, 1U<<5));
    578 
    579 
    580 #if DISABLE_CLK125
    581     /* Reduce EMI emissions by disabling the CLK125 pin if not used */
    582     SOC_IF_ERROR_RETURN
    583         (MODIFY_PHY5464_SPARE_CTRL_3r(unit, pc, 0, 1));
    584 #endif
    585 
    586     if (pc->automedium || pc->fiber.preferred) {
    587         if (pc->interface == SOC_PORT_IF_SGMII) {
    588             /* passthru mode support only in sgmii mode */
    589             /* Enable SerDes Pass-Through */
    590             SOC_IF_ERROR_RETURN
    591                 (MODIFY_PHY5464_EXP_SERDES_PASSTHRU_ENr(unit, pc, 0x0001, 0x0001));
    592 
    593             if (!pc->automedium && pc->fiber.preferred) {
    594                 /* Set register 0x1c shadow "10111" bit 1 to force SerDes 
    595                  * passthru */
    596                 /* Misc 1000-X Control register (0x1c shadow 10111) */
    597                 /* Medium Resolution override for SerDes passthru mode */
    598                 SOC_IF_ERROR_RETURN
    599                     (MODIFY_PHY5464_MISC_1000Xr(unit, pc, 0x0002, 0x0002));
    600             }
    601         }
    602         
    603         if (PHY_IS_BCM5461(pc)) {
    604             switch (pc->fiber_detect) {
    605             case 1:             /* PECL SD */
    606             case -1:
    607             case 0:             /* default */
    608                 SOC_IF_ERROR_RETURN
    609                     (phy_reg_ge_write(unit, pc, 0x00, 0x000c, 0x1c, 0x0020));
    610                 break;
    611             case 10:            /* EN_10B */
    612             case -10:
    613                 SOC_IF_ERROR_RETURN
    614                     (phy_reg_ge_write(unit, pc, 0x00, 0x000c, 0x1c, 0x0000));
    615                 break;
    616             default:
    617                 return SOC_E_CONFIG;
    618             }
    619         } else {        /* multiport */
    620             switch (pc->fiber_detect) {
    621             case 4:             /* LED4 */
    622             case -4:
    623             case 0:             /* default */
    624                 /* GPIO Control/Status Register (0x1c shadow 01111) */
    625                 /* Change LED4 pin into an input */
    626                 SOC_IF_ERROR_RETURN
    627                     (MODIFY_PHY5464_LED_GPIO_CTRLr(unit, pc, 0x0008, 0x0008));
    628                 break;
    629             default:
    630                 return SOC_E_CONFIG;
    631             }
    632         }
    633     }
    634 
    635     /* Configure Auto-detect Medium (0x1c shadow 11110) */
    636     /* In SGMII mode, the priority must be copper.
    637      * 1) Optical modules generate a SD or LOS event if there is no activity.
    638      *    This would cause 5464S to falsely go into fiber mode when there is
    639      *    no link partner connected and cause 5464S to never link up a valid
    640      *    copper link partner.
    641      * 2) When 5464S has a copper link, it cannot send out any signals on
    642      *    it PTOUT+/- because 5464S is in SSGMII to copper mode and not
    643      *    SerDes pass-through mode. This will cause link partner to never
    644      *    link up even if a fiber cable is connected between them because
    645      *    their PTOUT+/- pins are disabled.
    646      */
    647     tmp = 0;
    648     if (pc->automedium &&
    649         !(PHY_IS_BCM5464(pc) && pc->phy_rev == PHY_REV_A0)) {
    650         /* Must NOT be set on 5464A0, and it will always auto-detect */
    651         tmp |= 0x01;    /* Bit 0: Enable auto-detect */
    652     }
    653     if (pc->fiber.preferred) {
    654         tmp |= 0x06;    /* Bit 1/2: always prefer SERDES */
    655     } else {
    656         tmp |= 0x04;    /* Bit 2: prefer SERDES if no medium active */
    657     }
    658 
    659     /*
    660      * Enable internal inversion of LED4/SD input pin.  Used only if
    661      * the fiber module provides RX_LOS instead of Signal Detect.
    662      */
    663     if (pc->fiber_detect < 0) {
    664         tmp |= 0x100;
    665     }
    666 
    667     SOC_IF_ERROR_RETURN
    668         (MODIFY_PHY5464_AUTO_DETECT_MEDIUMr(unit, pc, tmp, 0x011f));
    669 
    670     if ((!pc->automedium) && 
    671         (pc->interface == SOC_PORT_IF_GMII)) {
    672         /* Set operating mode only after auto-detect is disabled.
    673          * Otherwise, the auto-detect will override the mode settings.
    674          */
    675         /* Mode Control Register
    676          * Mode Select = >  00:     GMII Copper
    677          *    [2:1]         01:     GMII Fiber
    678          *                  10:     SGMII
    679          *                  11:     GBIC(Media Converter)
    680          */
    681         if (PHY_COPPER_MODE(unit, port)) {
    682             tmp = (0 << 1);         /* copper/GMII */
    683         } else {
    684             tmp = (1 << 1);         /* fiber/GMII */
    685         }
    686         SOC_IF_ERROR_RETURN
    687             (MODIFY_PHY5464_MODE_CTRLr(unit, pc, tmp, 0x0006));
    688         SOC_IF_ERROR_RETURN
    689             (MODIFY_PHY5464_MII_ECRr(unit, pc, 0, (1 << 15)));
    690     }
    691 
    692     /*
    693      * Configure Auxiliary 1000BASE-X control register to turn off
    694      * carrier extension.  The Intel 7131 NIC does not accept carrier
    695      * extension and gets CRC errors.
    696      */
    697     /* Disable carrier extension */
    698     SOC_IF_ERROR_RETURN
    699         (MODIFY_PHY5464_AUX_1000X_CTRLr(unit, pc, 0x0040, 0x0040));
    700 
    701     /* Configure Extended Control Register */
    702     tmp  = 0x0020;      /* Enable LEDs to indicate traffic status */
    703     tmp |= 0x0001;      /* Set high FIFO elasticity to support jumbo frames */
    704     SOC_IF_ERROR_RETURN
    705         (MODIFY_PHY5464_MII_ECRr(unit, pc, tmp, tmp));
    706 
    707     if (PHY_IS_BCM54980(pc) || PHY_IS_BCM54980C(pc) ||
    708         PHY_IS_BCM54980V(pc) || PHY_IS_BCM54980VC(pc)) {
    709 
    710         /* set 54980 elasticity fifo to support 13.5k frame size*/
    711         SOC_IF_ERROR_RETURN
    712             (MODIFY_PHY5464_MISC_1000X_CTRL2r(unit,pc, 1, 1));
    713         SOC_IF_ERROR_RETURN
    714             (MODIFY_PHY5464_AUX_1000X_CTRLr(unit,pc, 0, (1 << 1)));
    715     }
    716 
    717 #if AUTO_MDIX_WHEN_AN_DIS
    718     /* Enable Auto-MDIX When autoneg disabled */
    719     SOC_IF_ERROR_RETURN
    720         (MODIFY_PHY5464_MII_MISC_CTRLr(unit, pc, 0x0200, 0x0200));
    721 #endif
    722 
    723     /* Enable extended packet length (4.5k through 25k) */
    724     SOC_IF_ERROR_RETURN
    725         (MODIFY_PHY5464_MII_AUX_CTRLr(unit, pc, 0x4000, 0x4000));
    726 
    727     /* Configure LED selectors */
    728     tmp = ((pc->ledmode[1] & 0xf) << 4) | (pc->ledmode[0] & 0xf);
    729     SOC_IF_ERROR_RETURN
    730         (WRITE_PHY5464_LED_SELECTOR_1r(unit, pc, tmp));
    731 
    732     tmp = ((pc->ledmode[3] & 0xf) << 4) | (pc->ledmode[2] & 0xf);
    733     SOC_IF_ERROR_RETURN
    734         (WRITE_PHY5464_LED_SELECTOR_2r(unit, pc, tmp));
    735 
    736     tmp = (pc->ledctrl & 0x3ff);
    737     SOC_IF_ERROR_RETURN
    738         (WRITE_PHY5464_LED_CTRLr(unit, pc, tmp));
    739 
    740     SOC_IF_ERROR_RETURN
    741         (WRITE_PHY5464_EXP_LED_SELECTORr(unit, pc, pc->ledselect));
    742 
    743     /* if using led link/activity mode, disable led traffic mode */
    744     if (pc->ledctrl & 0x10 || pc->ledselect == 0x01) {
    745         SOC_IF_ERROR_RETURN
    746             (MODIFY_PHY5464_MII_ECRr(unit, pc, 0, 0x0020));
    747     }
    748 
    749     if (PHY_IS_BCM5478(pc)  || PHY_IS_BCM5488(pc)  ||
    750         PHY_IS_BCM54980(pc) || PHY_IS_BCM54980C(pc) ||
    751         PHY_IS_BCM54980V(pc) || PHY_IS_BCM54980VC(pc)) {
    752         uint16 half, half1k;
    753         uint32 power_mode; 
    754   
    755         tmp    = 0;
    756         half   = 0;
    757         half1k = 0;
    758         if (pc->halfout) {
    759             tmp &= (~0x40);     /* Class A mode */
    760             if ((pc->halfout == 10) || (pc->halfout == 100)) { 
    761                 half |= 0x8;    /* Enable half amplitude for all speed */
    762             } else {
    763                 half1k |= 0x2;  /* Enable half amplitude for Giga */
    764             }
    765         } else {
    766             tmp |= 0x40;        /* Class A/B mode */ 
    767             half &= (~0x8);     /* Disable half amplitude for Giga */
    768             half1k &= (~0x2);   /* Disable half amplitude for all speed */
    769 
    770             /* For extended reach, default to highest performance mode 
    771              * (REF: 5488R-ES104-R)
    772              */
    773             power_mode = soc_property_port_get(unit, port, 
    774                                                spn_PHY_LOW_POWER_MODE, 7);
    775             tmp |= (power_mode & 0x7) << 13; /* Set power mode */
    776         }
    777 
    778         /* Update Power/MII Control Register (0x18 shadow b'010) */
    779         SOC_IF_ERROR_RETURN
    780             (MODIFY_PHY5464_MII_POWER_CTRLr(unit, pc, tmp, 0xe040));
    781 
    782         /* Update Test Register 1 (0x18 shadow b'100) */
    783         SOC_IF_ERROR_RETURN
    784             (MODIFY_PHY5464_MII_MISC_TESTr(unit, pc, half, 0x0008));
    785 
    786         /* Update Expansion Register 0xF9 */
    787         SOC_IF_ERROR_RETURN
    788             (MODIFY_PHY5464_EXP_PASSTHRU_LED_MODEr(unit, pc, half1k, 0x02));
    789     }
    790 
    791     /* BCM54980 revB 10BASE-T LOW TRANSMITTER AMPLITUDE workaround. 
    792      * See 54980 errata 54980-ES105-R 
    793      */
    794     if (PHY_IS_BCM54980(pc) || PHY_IS_BCM54980C(pc) ||
    795         PHY_IS_BCM54980V(pc) || PHY_IS_BCM54980VC(pc)) { 
    796 
    797         /* only applies to B revisions */
    798         if ((pc->phy_id1 & 0xF) <= 2) {
    799             SOC_IF_ERROR_RETURN
    800                 (WRITE_PHY5464_EXP_10BaseT_TxAMPr(unit,pc,0x3800));
    801                         
    802             SOC_IF_ERROR_RETURN
    803                 (WRITE_PHY5464_TEST1r(unit,pc,0x0011));
    804 
    805             SOC_IF_ERROR_RETURN
    806                 (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0C00,0x0FFF));
    807 
    808             /* write 0xA to reg 0x17 and 0x90B to reg 15 */
    809             SOC_IF_ERROR_RETURN
    810                 (PHY5464_REG_WRITE(unit,pc,0,0xA,0x15,0x090B));
    811 
    812             SOC_IF_ERROR_RETURN
    813                 (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0400,0x0FFF));
    814         }
    815     }
    816 
    817     if (PHY_IS_BCM5488(pc) || PHY_IS_BCM5482(pc)) {
    818         /* Adjust timing */
    819         SOC_IF_ERROR_RETURN
    820             (MODIFY_PHY5464_SPARE_CTRLr(unit, pc, 0x0002, 0x0002));
    821     }
    822 
    823 #ifdef BCM_HAWKEYE_SUPPORT
    824     if (PHY_IS_BCM53314(pc)) {
    825         /* Issue for For bcm53314_B0 : Unstable link issue with Marvell PHY
    826          * Workaroud (PHY team confirmed workaround has been identified) :
    827          * Set Exp register 0x08.bit[9]=0.
    828          * (BIT9 is AUTO EARLY DAC WAKE power savings mode)
    829          */
    830         uint16 dev_id = 0;   
    831         uint8 rev_id = 0;   
    832      
    833         soc_cm_get_id(unit, &dev_id, &rev_id);   
    834         if ((rev_id == BCM53312_B0_REV_ID) ||   
    835             (rev_id == BCM53313_B0_REV_ID) ||   
    836             (rev_id == BCM53314_B0_REV_ID)) { 
    837             SOC_IF_ERROR_RETURN
    838                 (MODIFY_PHY5464_10BT_CTRLr(unit, pc, 0x0000, 0x0200));    
    839         }
    840     }
    841 #endif
    842 
    843     return SOC_E_NONE;
    844 }
    845 
    846 /*
    847  * Function:
    848  *      phy_5464_init
    849  * Purpose:
    850  *      Init function for 5464 PHY.
    851  * Parameters:
    852  *      unit - StrataSwitch unit #.
    853  *      port - StrataSwitch port #.
    854  * Returns:
    855  *      SOC_E_NONE
    856  */
    857 STATIC int
    858 phy_5464_init(int unit, soc_port_t port)
    859 {
    860     phy_ctrl_t         *pc;
    861     phy_ctrl_t         *int_pc;
    862     int                 fiber_capable, fiber_preferred, type_s=0;
    863     uint16              tmp;
    864     uint8               phy_addr;
    865 
    866     phy_addr = PORT_TO_PHY_ADDR(unit, port);
    867 
    868     DPRINTF((BSL_META_U(unit,
    869                         "phy_5464_init: u=%d p=%d phy_addr=%d\n"),
    870              unit, port, (int)phy_addr));
    871 
    872     pc     = EXT_PHY_SW_STATE(unit, port);
    873     int_pc = INT_PHY_SW_STATE(unit, port);
    874 
    875     if (PHY_IS_BCM5462(pc)) {
    876         pc->phy_model = PHY_BCM5461_MODEL;      /* 5461D */
    877     }
    878  
    879     if (int_pc == NULL) {
    880         pc->interface = SOC_PORT_IF_GMII;
    881     } else {
    882         pc->interface = SOC_PORT_IF_SGMII;
    883     }
    884 
    885 #ifdef BCM_HAWKEYE_SUPPORT 
    886     if (PHY_IS_BCM53314(pc)){
    887         pc->interface = SOC_PORT_IF_GMII;
    888     }
    889 #endif    
    890 
    891     if (PHY_IS_BCM5461(pc)) {
    892         SOC_IF_ERROR_RETURN
    893             (READ_PHY5464_MODE_CTRLr(unit, pc, &tmp));
    894         type_s = (tmp & 0x8) ? 1 : 0;
    895     } else if (PHY_IS_BCM5464(pc) || PHY_IS_BCM5466(pc)) {
    896         /*
    897          * On the 5464 family, we need the user to set a property to know
    898          * if we are on an S part.  See comments at top of file.
    899          */
    900         type_s = soc_property_port_get(unit, port, spn_PHY_5464S, 0);
    901     }
    902 
    903     if (soc_feature(unit, soc_feature_dodeca_serdes) && int_pc != NULL) {
    904         if (PHY_IS_BCM5461(pc)) {
    905             if (!type_s) {
    906                 return SOC_E_FAIL;
    907             }
    908             fiber_capable = 0;
    909         } else {
    910             fiber_capable = type_s;
    911         }
    912     } else {
    913         if (PHY_IS_BCM5461(pc)) {
    914             fiber_capable = type_s;
    915         } else {
    916             fiber_capable = 1;
    917         }
    918     }
    919 
    920     /*
    921      * In automedium mode, the property phy_fiber_pref_port<X> is used
    922      * to set a preference for fiber mode in the event both copper and
    923      * fiber links are up.
    924      *
    925      * If NOT in automedium mode, phy_fiber_pref_port<X> is used to
    926      * select which of fiber or copper is forced.
    927      */
    928 
    929     if (!fiber_capable) {
    930         fiber_preferred = 0;
    931         pc->automedium = 0;
    932         pc->fiber_detect = 0;
    933     } else {
    934         fiber_preferred =
    935             soc_property_port_get(unit, port, spn_PHY_FIBER_PREF, 1);
    936         pc->automedium =
    937             soc_property_port_get(unit, port, spn_PHY_AUTOMEDIUM, 1);
    938         pc->fiber_detect =
    939             soc_property_port_get(unit, port, spn_PHY_FIBER_DETECT, 0);
    940     }
    941 
    942     DPRINTF((BSL_META_U(unit,
    943                         "phy_5464_init: "
    944                         "u=%d p=%d type=546%d%s automedium=%d "
    945                         "fiber_pref=%d detect=%d\n"),
    946              unit, port,
    947              pc->phy_model == PHY_BCM5461_MODEL ? 1 : 4,
    948              type_s ? "S" : "",
    949              pc->automedium, fiber_preferred, pc->fiber_detect));
    950 
    951     pc->copper.enable = TRUE;
    952     pc->copper.preferred = !fiber_preferred;
    953     pc->copper.autoneg_enable = TRUE;
    954     pc->copper.autoneg_advert = ADVERT_ALL_COPPER;
    955     pc->copper.force_speed = 1000;
    956     pc->copper.force_duplex = TRUE;
    957     pc->copper.master = SOC_PORT_MS_AUTO;
    958     pc->copper.mdix = SOC_PORT_MDIX_AUTO;
    959 
    960     pc->fiber.enable = fiber_capable;
    961     pc->fiber.preferred = fiber_preferred;
    962     pc->fiber.autoneg_enable = fiber_capable;
    963     pc->fiber.autoneg_advert = ADVERT_ALL_FIBER;
    964     pc->fiber.force_speed = 1000;
    965     pc->fiber.force_duplex = TRUE;
    966     pc->fiber.master = SOC_PORT_MS_NONE;
    967     pc->fiber.mdix = SOC_PORT_MDIX_NORMAL;
    968 
    969     /* Initially configure for the preferred medium. */
    970 
    971     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_COPPER);
    972     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER);
    973     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_PASSTHRU);
    974     if (pc->fiber.preferred) {
    975         PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
    976         if (pc->interface == SOC_PORT_IF_SGMII) {
    977             PHY_FLAGS_SET(unit, port, PHY_FLAGS_PASSTHRU);
    978         }
    979     } else {
    980         PHY_FLAGS_SET(unit, port, PHY_FLAGS_COPPER);
    981     }
    982 
    983     /* Get Requested LED selectors (defaults are hardware defaults) */
    984     pc->ledmode[0] = soc_property_port_get(unit, port, spn_PHY_LED1_MODE, 0);
    985     pc->ledmode[1] = soc_property_port_get(unit, port, spn_PHY_LED2_MODE, 1);
    986     pc->ledmode[2] = soc_property_port_get(unit, port, spn_PHY_LED3_MODE, 3);
    987     pc->ledmode[3] = soc_property_port_get(unit, port, spn_PHY_LED4_MODE, 6);
    988     pc->ledctrl    = soc_property_port_get(unit, port, spn_PHY_LED_CTRL, 0x8);
    989     pc->ledselect  = soc_property_port_get(unit, port, spn_PHY_LED_SELECT, 0);
    990 
    991     /* Get half amplitude setting */
    992     pc->halfout = soc_property_port_get(unit, port, spn_PHY_HALF_POWER, 0);
    993     
    994     SOC_IF_ERROR_RETURN
    995         (_phy_5464_reset_setup(unit, port));
    996 
    997     SOC_IF_ERROR_RETURN
    998         (_phy_5464_active_medium_config_update(unit, port,
    999                                         PHY_COPPER_MODE(unit, port) ?
   1000                                         &pc->copper :
   1001                                         &pc->fiber));
   1002 
   1003     pc->power_mode = SOC_PHY_CONTROL_POWER_FULL;   
   1004     return SOC_E_NONE;
   1005 }
   1006 
   1007 /*
   1008  * Function:
   1009  *      phy_5464_enable_set
   1010  * Purpose:
   1011  *      Enable or disable the physical interface.
   1012  * Parameters:
   1013  *      unit - StrataSwitch unit #.
   1014  *      port - StrataSwitch port #.
   1015  *      enable - Boolean, true = enable PHY, false = disable.
   1016  * Returns:
   1017  *      SOC_E_XXX
   1018  */
   1019 
   1020 STATIC int
   1021 phy_5464_enable_set(int unit, soc_port_t port, int enable)
   1022 {
   1023     phy_ctrl_t    *pc;
   1024     uint16 power_down;
   1025 
   1026     pc = EXT_PHY_SW_STATE(unit, port);
   1027 
   1028     power_down = (enable) ? 0 : MII_CTRL_PD;
   1029 
   1030     if ((pc->copper.enable) && 
   1031         (pc->automedium || PHY_COPPER_MODE(unit, port))) {
   1032         SOC_IF_ERROR_RETURN
   1033             (MODIFY_PHY5464_MII_CTRLr(unit, pc, power_down, MII_CTRL_PD));
   1034     }
   1035 
   1036     if (pc->automedium || PHY_FIBER_MODE(unit, port)) {
   1037         if (PHY_PASSTHRU_MODE(unit, port)) {
   1038             phy_ctrl_t    *int_pc;   /* PHY software state */
   1039 
   1040             int_pc = INT_PHY_SW_STATE(unit, port);
   1041 
   1042             if (NULL != int_pc) {
   1043                 /* In passthrough mode, call internal PHY driver */
   1044                 SOC_IF_ERROR_RETURN
   1045                     (PHY_ENABLE_SET(int_pc->pd, unit, port, enable));
   1046             } else {
   1047                 return SOC_E_INTERNAL;
   1048             }
   1049         } else { /* GMII fiber */
   1050             if (pc->fiber.enable) {
   1051                 SOC_IF_ERROR_RETURN
   1052                     (MODIFY_PHY5464_1000X_MII_CTRLr(unit, pc, 
   1053                                                 power_down, MII_CTRL_PD));
   1054             }
   1055         } 
   1056     }
   1057 
   1058     if (enable) {
   1059         phy_ctrl_t    *int_pc;   /* PHY software state */
   1060 
   1061         int_pc = INT_PHY_SW_STATE(unit, port);
   1062 
   1063         if (NULL != int_pc) {
   1064             /* call internal PHY driver */
   1065             SOC_IF_ERROR_RETURN
   1066                 (PHY_ENABLE_SET(int_pc->pd, unit, port, enable));
   1067         }
   1068     }
   1069 
   1070     /* Update software state */
   1071     SOC_IF_ERROR_RETURN(phy_fe_ge_enable_set(unit, port, enable));
   1072 
   1073     return SOC_E_NONE;
   1074 }
   1075 
   1076 /*
   1077  * Function:
   1078  *      phy_5464_enable_get
   1079  * Purpose:
   1080  *      Enable or disable the physical interface for a 5464 device.
   1081  * Parameters:
   1082  *      unit - StrataSwitch unit #.
   1083  *      port - StrataSwitch port #.
   1084  *      enable - (OUT) Boolean, true = enable PHY, false = disable.
   1085  * Returns:
   1086  *      SOC_E_XXX
   1087  */
   1088 
   1089 STATIC int
   1090 phy_5464_enable_get(int unit, soc_port_t port, int *enable)
   1091 {
   1092     return phy_fe_ge_enable_get(unit, port, enable);
   1093 }
   1094 
   1095 STATIC int
   1096 _phy_5464_fiber_link_get(int unit, soc_port_t port, int *link)
   1097 {
   1098     uint16              mii_stat;
   1099     uint16              mii_ctrl;
   1100     soc_timeout_t       to;
   1101     phy_ctrl_t    *pc;
   1102 
   1103     pc = EXT_PHY_SW_STATE(unit, port);
   1104 
   1105     SOC_IF_ERROR_RETURN
   1106         (READ_PHY5464_1000X_MII_STATr(unit, pc, &mii_stat));
   1107 
   1108     if (!(mii_stat & MII_STAT_LA) || (mii_stat == 0xffff)) {
   1109         /* mii_stat == 0xffff check is to handle removable PHY daughter cards */
   1110         *link = FALSE;
   1111         return SOC_E_NONE;
   1112     }
   1113 
   1114     /* Link appears to be up; we are done if autoneg is off. */
   1115     SOC_IF_ERROR_RETURN
   1116         (READ_PHY5464_1000X_MII_CTRLr(unit, pc, &mii_ctrl));
   1117 
   1118     if (!(mii_ctrl & MII_CTRL_AE)) {
   1119         *link = TRUE;
   1120         return SOC_E_NONE;
   1121     }
   1122 
   1123     /*
   1124      * If link appears to be up but autonegotiation is still in
   1125      * progress, wait for it to complete.  For BCM5228, autoneg can
   1126      * still be busy up to about 200 usec after link is indicated.  Also
   1127      * continue to check link state in case it goes back down.
   1128      */
   1129     soc_timeout_init(&to, PHY_AN_TIMEOUT(unit, port), 0);
   1130     for (;;) {
   1131         SOC_IF_ERROR_RETURN
   1132             (READ_PHY5464_1000X_MII_STATr(unit, pc, &mii_stat));
   1133 
   1134         if (!(mii_stat & MII_STAT_LA)) {
   1135             return SOC_E_NONE;
   1136         }
   1137 
   1138         if (mii_stat & MII_STAT_AN_DONE) {
   1139             break;
   1140         }
   1141 
   1142         if (soc_timeout_check(&to)) {
   1143             return SOC_E_BUSY;
   1144         }
   1145     }
   1146 
   1147     /* Return link state at end of polling */
   1148     *link = ((mii_stat & MII_STAT_LA) != 0);
   1149 
   1150     return SOC_E_NONE;
   1151 }
   1152 
   1153 STATIC int
   1154 _phy_5464_medium_change(int unit, soc_port_t port, int force_update)
   1155 {
   1156     phy_ctrl_t    *pc;
   1157     int            medium = SOC_PORT_MEDIUM_NONE;
   1158 
   1159     pc = EXT_PHY_SW_STATE(unit, port);
   1160 
   1161     SOC_IF_ERROR_RETURN
   1162         (_phy_5464_medium_check(unit, port, &medium));
   1163 
   1164     /*
   1165      * If the medium is changing, reset the PHY, change its
   1166      * operating mode and 5690 SERDES operating mode, and reprogram
   1167      * the persistent copper/fiber parameters.  Also make a user
   1168      * callback to inform them of medium status change.  An
   1169      * application could also use this to reprogram the port
   1170      * parameters quickly after a medium status change.
   1171      */
   1172 
   1173     if (medium == SOC_PORT_MEDIUM_COPPER) {
   1174         if ((!PHY_COPPER_MODE(unit, port)) || force_update) {
   1175             DPRINTF((BSL_META_U(unit,
   1176                                 "_phy_5464_link_auto_detect: "
   1177                                 "u=%d p=%d [F->C]\n"),
   1178                      unit, port));
   1179 
   1180             PHY_FLAGS_SET(unit, port, PHY_FLAGS_COPPER);
   1181             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER);
   1182             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_PASSTHRU);
   1183             PHY_FLAGS_SET(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
   1184             SOC_IF_ERROR_RETURN
   1185                 (_phy_5464_reset_setup(unit, port));
   1186             if (pc->copper.enable) {
   1187                 SOC_IF_ERROR_RETURN
   1188                     (_phy_5464_active_medium_config_update(unit, port, 
   1189                                             &pc->copper));
   1190             }
   1191         } else {
   1192             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
   1193             /* Configure Auto-detect Medium (0x1c shadow 11110) */
   1194             /* should not prefer SERDES */
   1195             SOC_IF_ERROR_RETURN
   1196                 (MODIFY_PHY5464_AUTO_DETECT_MEDIUMr(unit, pc, 0, 0x0006));
   1197         }
   1198     } else {        /* Fiber */
   1199         if (PHY_COPPER_MODE(unit, port) || force_update) {
   1200             DPRINTF((BSL_META_U(unit,
   1201                                 "_phy_5464_link_auto_detect: "
   1202                                 "u=%d p=%d [C->F]\n"),
   1203                      unit, port));
   1204 
   1205             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_COPPER);
   1206             PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
   1207             PHY_FLAGS_SET(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
   1208             if (pc->interface == SOC_PORT_IF_SGMII) {
   1209                 PHY_FLAGS_SET(unit, port, PHY_FLAGS_PASSTHRU);
   1210             }
   1211             SOC_IF_ERROR_RETURN
   1212                 (_phy_5464_reset_setup(unit, port));
   1213 
   1214             if (pc->fiber.enable) {
   1215                 SOC_IF_ERROR_RETURN
   1216                     (_phy_5464_active_medium_config_update(unit, port,
   1217                                   &pc->fiber));
   1218             }
   1219         } else {
   1220             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
   1221         } 
   1222     }
   1223     return SOC_E_NONE;
   1224 }
   1225 
   1226 /*
   1227  * Function:
   1228  *      phy_5464_link_get
   1229  * Purpose:
   1230  *      Determine the current link up/down status for a 5464 device.
   1231  * Parameters:
   1232  *      unit - StrataSwitch unit #.
   1233  *      port - StrataSwitch port #.
   1234  *      link - (OUT) Boolean, true indicates link established.
   1235  * Returns:
   1236  *      SOC_E_XXX
   1237  * Notes:
   1238  *      If using automedium, also switches the mode.
   1239  */
   1240 
   1241 STATIC int
   1242 phy_5464_link_get(int unit, soc_port_t port, int *link)
   1243 {
   1244     phy_ctrl_t    *pc;
   1245 
   1246     pc = EXT_PHY_SW_STATE(unit, port);
   1247 
   1248     *link = FALSE;      /* Default return */
   1249 
   1250     if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_DISABLE)) {
   1251         return SOC_E_NONE;
   1252     }
   1253 
   1254     if (!pc->fiber.enable && !pc->copper.enable) {
   1255         return SOC_E_NONE;
   1256     }
   1257 
   1258     if (pc->automedium) {
   1259        SOC_IF_ERROR_RETURN
   1260            (_phy_5464_medium_change(unit, port,FALSE));
   1261     }
   1262 
   1263     if (PHY_COPPER_MODE(unit, port)) {
   1264         SOC_IF_ERROR_RETURN
   1265             (phy_fe_ge_link_get(unit, port, link));
   1266     } else { /* fiber mode */
   1267         if (PHY_PASSTHRU_MODE(unit, port)) {
   1268             phy_ctrl_t    *int_pc;   /* PHY software state */
   1269 
   1270             int_pc = INT_PHY_SW_STATE(unit, port);
   1271 
   1272             if (NULL != int_pc) {
   1273                 /* In passthrough mode, call internal PHY driver */
   1274                 SOC_IF_ERROR_RETURN
   1275                     (PHY_LINK_GET(int_pc->pd, unit, port, link));
   1276             } else {
   1277                 return SOC_E_INTERNAL;
   1278             }
   1279         } else { /* GMII fiber mode */
   1280             /* PHYs that do not support passthrough mode must read
   1281              * link status from 1000X status register if the port
   1282              * is in fiber mode.
   1283              */ 
   1284             SOC_IF_ERROR_RETURN
   1285                 (_phy_5464_fiber_link_get(unit, port, link));
   1286         }
   1287     }
   1288     if ((pc->automedium) && (pc->fiber.preferred)) {
   1289         uint16      copper_transmit;
   1290 
   1291         copper_transmit = 0;
   1292         /* Disable/Enable Copper transmitter */
   1293         if (PHY_FIBER_MODE(unit, port)) {
   1294             copper_transmit = (*link) ? 0x2000 : 0; 
   1295         }
   1296         SOC_IF_ERROR_RETURN
   1297             (MODIFY_PHY5464_MII_ECRr(unit, pc, copper_transmit, 0x2000));
   1298     }
   1299 
   1300     DPRINTF_VERBOSE((BSL_META_U(unit,
   1301                                 "phy_5464_link_get: "
   1302                                 "u=%d p=%d mode=%s%s%s link=%d\n"),
   1303                     unit, port,
   1304                     PHY_COPPER_MODE(unit, port) ? "C" : "",
   1305                     PHY_FIBER_MODE(unit, port) ? "F" : "",
   1306                     PHY_PASSTHRU_MODE(unit, port) ? "P" : "",
   1307                     *link));
   1308 
   1309     return SOC_E_NONE;
   1310 }
   1311 
   1312 /*
   1313  * Function:
   1314  *      phy_5464_duplex_set
   1315  * Purpose:
   1316  *      Set the current duplex mode (forced).
   1317  * Parameters:
   1318  *      unit - StrataSwitch unit #.
   1319  *      port - StrataSwitch port #.
   1320  *      duplex - Boolean, true indicates full duplex, false indicates half.
   1321  * Returns:
   1322  *      SOC_E_NONE
   1323  *      SOC_E_UNAVAIL - Half duplex requested, and not supported.
   1324  * Notes:
   1325  *      The duplex is set only for the ACTIVE medium.
   1326  *      No synchronization performed at this level.
   1327  *      Autonegotiation is not manipulated.
   1328  */
   1329 
   1330 STATIC int
   1331 phy_5464_duplex_set(int unit, soc_port_t port, int duplex)
   1332 {
   1333     phy_ctrl_t    *pc = EXT_PHY_SW_STATE(unit, port);
   1334 
   1335     DPRINTF((BSL_META_U(unit,
   1336                         "phy_5464_duplex_set: u=%d p=%d d=%d\n"),
   1337              unit, port, duplex));
   1338 
   1339     if (PHY_COPPER_MODE(unit, port)) {
   1340         /* Note: mac.c uses phy_5690_notify_duplex to update 5690 */
   1341         SOC_IF_ERROR_RETURN(phy_fe_ge_duplex_set(unit, port, duplex));
   1342         pc->copper.force_duplex = duplex;
   1343     } else {    /* Fiber */
   1344         if (!duplex) {
   1345             return SOC_E_UNAVAIL;
   1346         }
   1347     }
   1348 
   1349     return SOC_E_NONE;
   1350 }
   1351 
   1352 /*
   1353  * Function:
   1354  *      phy_5464_duplex_get
   1355  * Purpose:
   1356  *      Get the current operating duplex mode. If autoneg is enabled,
   1357  *      then operating mode is returned, otherwise forced mode is returned.
   1358  * Parameters:
   1359  *      unit - StrataSwitch unit #.
   1360  *      port - StrataSwitch port #.
   1361  *      duplex - (OUT) Boolean, true indicates full duplex, false
   1362  *              indicates half.
   1363  * Returns:
   1364  *      SOC_E_NONE
   1365  * Notes:
   1366  *      The duplex is retrieved for the ACTIVE medium.
   1367  *      No synchronization performed at this level. Autonegotiation is
   1368  *      not manipulated.
   1369  */
   1370 
   1371 STATIC int
   1372 phy_5464_duplex_get(int unit, soc_port_t port, int *duplex)
   1373 {
   1374 
   1375     if (PHY_COPPER_MODE(unit, port)) {
   1376         return phy_fe_ge_duplex_get(unit, port, duplex);    
   1377     }
   1378     
   1379     *duplex = TRUE;
   1380         
   1381     return SOC_E_NONE;
   1382 }
   1383 
   1384 /*
   1385  * Function:
   1386  *      phy_5464_speed_set
   1387  * Purpose:
   1388  *      Set the current operating speed (forced).
   1389  * Parameters:
   1390  *      unit - StrataSwitch unit #.
   1391  *      port - StrataSwitch port #.
   1392  *      speed - Requested speed, only 1000 supported.
   1393  * Returns:
   1394  *      SOC_E_XXX
   1395  * Notes:
   1396  *      The speed is set only for the ACTIVE medium.
   1397  */
   1398 
   1399 STATIC int
   1400 phy_5464_speed_set(int unit, soc_port_t port, int speed)
   1401 {
   1402     int            rv;
   1403     phy_ctrl_t    *pc = EXT_PHY_SW_STATE(unit, port);
   1404 
   1405     DPRINTF((BSL_META_U(unit,
   1406                         "phy_5464_speed_set: u=%d p=%d s=%d fiber=%d\n"),
   1407              unit, port, speed, PHY_FIBER_MODE(unit, port)));
   1408 
   1409     rv = SOC_E_NONE;
   1410     if (PHY_COPPER_MODE(unit, port)) {
   1411         /* Note: mac.c uses phy_5690_notify_speed to update 5690 */
   1412         rv = (phy_fe_ge_speed_set(unit, port, speed));
   1413         if (SOC_SUCCESS(rv)) {
   1414             pc->copper.force_speed = speed;
   1415         }
   1416     } else {    /* Fiber */
   1417         if (PHY_PASSTHRU_MODE(unit, port)) {
   1418             phy_ctrl_t    *int_pc;   /* PHY software state */
   1419 
   1420             int_pc = INT_PHY_SW_STATE(unit, port);
   1421 
   1422             if (NULL != int_pc) {
   1423                 /* In passthrough mode, call internal PHY driver */
   1424                 rv = PHY_SPEED_SET(int_pc->pd, unit, port, speed);
   1425             } else {
   1426                 rv = SOC_E_INTERNAL;
   1427             }
   1428  
   1429         } else if (speed != 0 && speed != 1000) {
   1430             rv = SOC_E_CONFIG;
   1431         }
   1432 
   1433         if (SOC_SUCCESS(rv)) {
   1434             pc->fiber.force_speed = speed;
   1435         }
   1436     }
   1437 
   1438     return rv;
   1439 }
   1440 
   1441 /*
   1442  * Function:
   1443  *      phy_5464_speed_get
   1444  * Purpose:
   1445  *      Get the current operating speed for a 5464 device.
   1446  * Parameters:
   1447  *      unit - StrataSwitch unit #.
   1448  *      port - StrataSwitch port #.
   1449  *      duplex - (OUT) Boolean, true indicates full duplex, false
   1450  *              indicates half.
   1451  * Returns:
   1452  *      SOC_E_NONE
   1453  * Notes:
   1454  *      The speed is retrieved for the ACTIVE medium.
   1455  */
   1456 
   1457 STATIC int
   1458 phy_5464_speed_get(int unit, soc_port_t port, int *speed)
   1459 {
   1460     if (PHY_COPPER_MODE(unit, port)) {
   1461         return phy_fe_ge_speed_get(unit, port, speed);
   1462     } else {
   1463         if (PHY_PASSTHRU_MODE(unit, port)) {
   1464             /* If the PHY is in passthrough mode, phyctrl calls the inner 
   1465              * PHY driver speed_get().
   1466              */
   1467             return SOC_E_INTERNAL;
   1468         } else {
   1469             *speed = 1000;
   1470         }
   1471     }
   1472     
   1473     return SOC_E_NONE;
   1474 }
   1475 
   1476 /*
   1477  * Function:
   1478  *      phy_5464_master_set
   1479  * Purpose:
   1480  *      Set the current master mode
   1481  * Parameters:
   1482  *      unit - StrataSwitch unit #.
   1483  *      port - StrataSwitch port #.
   1484  *      master - SOC_PORT_MS_*
   1485  * Returns:
   1486  *      SOC_E_XXX
   1487  * Notes:
   1488  *      The master mode is set only for the ACTIVE medium.
   1489  */
   1490 
   1491 STATIC int
   1492 phy_5464_master_set(int unit, soc_port_t port, int master)
   1493 {
   1494     phy_ctrl_t    *pc = EXT_PHY_SW_STATE(unit, port);
   1495 
   1496     DPRINTF((BSL_META_U(unit,
   1497                         "phy_5464_master_set: u=%d p=%d master=%d fiber=%d\n"),
   1498              unit, port, master, PHY_FIBER_MODE(unit, port)));
   1499 
   1500     if (PHY_COPPER_MODE(unit, port)) {
   1501         SOC_IF_ERROR_RETURN(phy_fe_ge_master_set(unit, port, master));
   1502         pc->copper.master = master;
   1503         return SOC_E_NONE;
   1504     } else {
   1505         return SOC_E_NONE;
   1506     }
   1507 }
   1508 
   1509 /*
   1510  * Function:
   1511  *      phy_5464_master_get
   1512  * Purpose:
   1513  *      Get the current master mode for a 5464 device.
   1514  * Parameters:
   1515  *      unit - StrataSwitch unit #.
   1516  *      port - StrataSwitch port #.
   1517  *      master - (OUT) SOC_PORT_MS_*
   1518  * Returns:
   1519  *      SOC_E_NONE
   1520  * Notes:
   1521  *      The master mode is retrieved for the ACTIVE medium.
   1522  */
   1523 
   1524 STATIC int
   1525 phy_5464_master_get(int unit, soc_port_t port, int *master)
   1526 {
   1527     if (PHY_COPPER_MODE(unit, port)) {
   1528         return phy_fe_ge_master_get(unit, port, master);
   1529     } else {
   1530         if (PHY_PASSTHRU_MODE(unit, port)) {
   1531             /* If the PHY is in passthrough mode, phyctrl calls the inner 
   1532              * PHY driver master_get().
   1533              */
   1534             return SOC_E_INTERNAL;
   1535         } else {
   1536             *master = SOC_PORT_MS_NONE;
   1537         }
   1538     }
   1539     return SOC_E_NONE;
   1540 }
   1541 
   1542 /*
   1543  * Function:
   1544  *      phy_5464_autoneg_set
   1545  * Purpose:
   1546  *      Enable or disable auto-negotiation on the specified port.
   1547  * Parameters:
   1548  *      unit - StrataSwitch unit #.
   1549  *      port - StrataSwitch port #.
   1550  *      autoneg - Boolean, if true, auto-negotiation is enabled
   1551  *              (and/or restarted). If false, autonegotiation is disabled.
   1552  * Returns:
   1553  *      SOC_E_XXX
   1554  * Notes:
   1555  *      The autoneg mode is set only for the ACTIVE medium.
   1556  */
   1557 
   1558 STATIC int
   1559 phy_5464_autoneg_set(int unit, soc_port_t port, int autoneg)
   1560 {
   1561     uint16               autoneg_enable;
   1562     int                  rv;
   1563     phy_ctrl_t    *pc;
   1564 
   1565     pc             = EXT_PHY_SW_STATE(unit, port);
   1566     rv             = SOC_E_NONE;
   1567     autoneg_enable = 0;
   1568 
   1569     if (PHY_COPPER_MODE(unit, port)) {
   1570         /* Set auto-neg on PHY */
   1571         rv = phy_fe_ge_an_set(unit, port, autoneg);
   1572         if (SOC_SUCCESS(rv)) {
   1573             pc->copper.autoneg_enable = autoneg ? 1 : 0;
   1574         }
   1575     } else { /* Fiber mode */
   1576         if (PHY_PASSTHRU_MODE(unit, port)) {
   1577             phy_ctrl_t    *int_pc;   /* PHY software state */
   1578 
   1579             int_pc = INT_PHY_SW_STATE(unit, port);
   1580 
   1581             if (NULL != int_pc) {
   1582                 /* In passthrough mode, call internal PHY driver */
   1583                 rv = PHY_AUTO_NEGOTIATE_SET(int_pc->pd, unit, port, autoneg);
   1584             } else {
   1585                 rv = SOC_E_INTERNAL;
   1586             }
   1587         } else {
   1588             autoneg_enable = (autoneg) ? MII_CTRL_AE | MII_CTRL_RAN: 0;
   1589             rv = MODIFY_PHY5464_1000X_MII_CTRLr(unit, pc, autoneg_enable,
   1590                                                 MII_CTRL_AE | MII_CTRL_RAN);
   1591         }
   1592         if (SOC_SUCCESS(rv)) {
   1593             pc->fiber.autoneg_enable = autoneg ? 1 : 0;
   1594         }  
   1595     }
   1596 
   1597     DPRINTF((BSL_META_U(unit,
   1598                         "phy_5464_autoneg_set: u=%d p=%d autoneg=%d "
   1599                         "auto=%d rv=%d\n"),
   1600              unit, port, autoneg, pc->automedium, rv));
   1601 
   1602     return rv;
   1603 }
   1604 
   1605 STATIC int
   1606 _phy_5464_fiber_an_get(int unit, soc_port_t port,
   1607                        int *autoneg, int *autoneg_done)
   1608 {
   1609     uint16               mii_ctrl;
   1610     uint16               mii_stat;
   1611     phy_ctrl_t    *pc;
   1612 
   1613     pc = EXT_PHY_SW_STATE(unit, port);
   1614 
   1615     SOC_IF_ERROR_RETURN
   1616         (READ_PHY5464_1000X_MII_CTRLr(unit, pc, &mii_ctrl));
   1617 
   1618     SOC_IF_ERROR_RETURN
   1619         (READ_PHY5464_1000X_MII_STATr(unit, pc, &mii_stat));
   1620 
   1621     *autoneg      = (mii_ctrl & MII_CTRL_AE) ? TRUE : FALSE;
   1622     *autoneg_done = (mii_stat & MII_STAT_AN_DONE) ? TRUE : FALSE;
   1623 
   1624     return(SOC_E_NONE);
   1625 }
   1626 
   1627 /*
   1628  * Function:
   1629  *      phy_5464_autoneg_get
   1630  * Purpose:
   1631  *      Get the current auto-negotiation status (enabled/busy).
   1632  * Parameters:
   1633  *      unit - StrataSwitch unit #.
   1634  *      port - StrataSwitch port #.
   1635  *      autoneg - (OUT) if true, auto-negotiation is enabled.
   1636  *      autoneg_done - (OUT) if true, auto-negotiation is complete. This
   1637  *              value is undefined if autoneg == FALSE.
   1638  * Returns:
   1639  *      SOC_E_XXX
   1640  * Notes:
   1641  *      The autoneg mode is retrieved for the ACTIVE medium.
   1642  */
   1643 
   1644 STATIC int
   1645 phy_5464_autoneg_get(int unit, soc_port_t port,
   1646                      int *autoneg, int *autoneg_done)
   1647 {
   1648     int rv;
   1649 
   1650     rv = SOC_E_NONE;
   1651 
   1652     if (PHY_COPPER_MODE(unit, port)) {
   1653         rv = phy_fe_ge_an_get(unit, port, autoneg, autoneg_done);
   1654     } else { /* Fiber mode */
   1655         if (PHY_PASSTHRU_MODE(unit, port)) { /* switch SerDes passthrough PHY */
   1656             /* If the PHY is in passthrough mode, phyctrl calls the inner
   1657              * PHY driver autoneg_set().
   1658              */
   1659             rv = SOC_E_INTERNAL;
   1660         } else { /* switch GMII to SerDes optical module */ 
   1661             rv = _phy_5464_fiber_an_get(unit, port, autoneg, autoneg_done);
   1662         }
   1663     }
   1664     return rv;
   1665 }
   1666 
   1667 STATIC int 
   1668 _phy_5464_fiber_adv_local_set(int unit, soc_port_t port, 
   1669                               soc_port_mode_t mode)
   1670 {
   1671     uint16               mii_adv;  /* Value to advertise */
   1672     phy_ctrl_t    *pc;       /* PHY software state */
   1673 
   1674     pc = EXT_PHY_SW_STATE(unit, port);
   1675 
   1676     mii_adv = 0;
   1677     if (mode & SOC_PM_1000MB_FD)    mii_adv |= (1<<5);
   1678     if (mode & SOC_PM_1000MB_HD)    mii_adv |= (1<<6);
   1679     switch (mode & SOC_PM_PAUSE) {
   1680     case SOC_PM_PAUSE:
   1681         mii_adv |= (0x1 << 7);
   1682         break;
   1683     case SOC_PM_PAUSE_TX:
   1684         mii_adv |= (0x2 << 7);
   1685         break;
   1686     case SOC_PM_PAUSE_RX:
   1687         mii_adv |= (0x3 << 7);
   1688         break;
   1689     }
   1690     SOC_IF_ERROR_RETURN
   1691         (WRITE_PHY5464_1000X_MII_ANAr(unit, pc, mii_adv));
   1692 
   1693     return SOC_E_NONE;
   1694 }
   1695 
   1696 /*
   1697  * Function:
   1698  *      phy_5464_adv_local_set
   1699  * Purpose:
   1700  *      Set the current advertisement for auto-negotiation.
   1701  * Parameters:
   1702  *      unit - StrataSwitch unit #.
   1703  *      port - StrataSwitch port #.
   1704  *      mode - Port mode mask indicating supported options/speeds.
   1705  * Returns:
   1706  *      SOC_E_XXX
   1707  * Notes:
   1708  *      The advertisement is set only for the ACTIVE medium.
   1709  *      No synchronization performed at this level.
   1710  */
   1711 
   1712 STATIC int
   1713 phy_5464_adv_local_set(int unit, soc_port_t port, soc_port_mode_t mode)
   1714 {
   1715     int            rv;   /* Return value */
   1716     phy_ctrl_t    *pc;       /* PHY software state */
   1717     soc_port_ability_t    ability;
   1718 
   1719     pc = EXT_PHY_SW_STATE(unit, port);
   1720 
   1721     rv = SOC_E_NONE;
   1722 
   1723     if (PHY_COPPER_MODE(unit, port)) {
   1724         rv = phy_ge_adv_local_set(unit, port, mode);
   1725         if (SOC_SUCCESS(rv)) {
   1726             pc->copper.autoneg_advert = mode & ADVERT_ALL_COPPER;
   1727         }
   1728     } else {    /* PHY_FIBER_MODE */
   1729         if (PHY_PASSTHRU_MODE(unit, port)) { /* passthrough fiber mode */
   1730             phy_ctrl_t    *int_pc;   /* PHY software state */
   1731 
   1732             int_pc = INT_PHY_SW_STATE(unit, port);
   1733 
   1734             if (NULL != int_pc) {
   1735                 /* In passthrough mode, call internal PHY driver */
   1736                 rv = PHY_ADV_SET(int_pc->pd, unit, port, mode);
   1737                 if (SOC_E_UNAVAIL == rv) {
   1738                     rv = soc_port_mode_to_ability(mode, &ability);
   1739                     if (SOC_SUCCESS(rv)) {
   1740                         rv = (PHY_ABILITY_ADVERT_SET(int_pc->pd, unit, 
   1741                                                 port, &ability));
   1742                     }
   1743                 }
   1744             } else {
   1745                 rv = SOC_E_INTERNAL;
   1746             }
   1747         } else { /* GMII Fiber mode */ 
   1748                 rv = _phy_5464_fiber_adv_local_set(unit, port, mode);
   1749         }
   1750 
   1751         if (SOC_SUCCESS(rv)) {
   1752             pc->fiber.autoneg_advert = mode & ADVERT_ALL_FIBER;
   1753         } 
   1754     }
   1755 
   1756     DPRINTF((BSL_META_U(unit,
   1757                         "phy_5464_adv_local_set: u=%d p=%d mode=0x%x rv=%d\n"),
   1758              unit, port, mode, rv));
   1759 
   1760     return rv;
   1761 }
   1762 
   1763 STATIC int
   1764 _phy_5464_fiber_adv_local_get(int unit, soc_port_t port, 
   1765                               soc_port_mode_t *mode)
   1766 {
   1767     uint16              mii_adv;   /* MII advertise register of the PHY */ 
   1768     phy_ctrl_t    *pc;       /* PHY software state */
   1769         
   1770     pc = EXT_PHY_SW_STATE(unit, port);
   1771 
   1772     /* read the 1000BASE-X Auto-Neg Advertisement Reg( Address 0x04 ) */
   1773     SOC_IF_ERROR_RETURN
   1774         (READ_PHY5464_1000X_MII_ANAr(unit, pc, &mii_adv));
   1775 
   1776     *mode = 0;
   1777     if (mii_adv & (1 << 5))         *mode |= SOC_PM_1000MB_FD;
   1778     if (mii_adv & (1 << 6))         *mode |= SOC_PM_1000MB_HD;
   1779     switch ((mii_adv >> 7) & 0x3) {
   1780     case 0x1:
   1781         *mode |= SOC_PM_PAUSE;
   1782         break;
   1783     case 0x2:
   1784         *mode |= SOC_PM_PAUSE_TX;
   1785         break;
   1786     case 0x3:
   1787         *mode |= SOC_PM_PAUSE_RX;
   1788         break;
   1789     }
   1790 
   1791     return SOC_E_NONE;
   1792 }
   1793  
   1794 /*
   1795  * Function:
   1796  *      phy_5464_adv_local_get
   1797  * Purpose:
   1798  *      Get the current advertisement for auto-negotiation.
   1799  * Parameters:
   1800  *      unit - StrataSwitch unit #.
   1801  *      port - StrataSwitch port #.
   1802  *      mode - (OUT) Port mode mask indicating supported options/speeds.
   1803  * Returns:
   1804  *      SOC_E_XXX
   1805  * Notes:
   1806  *      The advertisement is retrieved for the ACTIVE medium.
   1807  *      No synchronization performed at this level.
   1808  */
   1809 
   1810 STATIC int
   1811 phy_5464_adv_local_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   1812 {
   1813     int     rv;    /* Return value */
   1814 
   1815     rv = SOC_E_NONE;
   1816     if (PHY_COPPER_MODE(unit, port)) {
   1817         rv = phy_ge_adv_local_get(unit, port, mode);
   1818     } else {    /* PHY_FIBER_MODE */
   1819         if (PHY_PASSTHRU_MODE(unit, port)) { /* passthrough fiber mode */
   1820             /* If the PHY is in passthrough mode, phyctrl calls the inner
   1821              * PHY driver adv_local_get.
   1822              */
   1823             rv = SOC_E_INTERNAL;
   1824         } else { /* GMII fiber mode */
   1825             rv = _phy_5464_fiber_adv_local_get(unit, port, mode);
   1826         }
   1827     }
   1828 
   1829     return rv;
   1830 }
   1831 
   1832 STATIC int
   1833 _phy_5464_fiber_adv_remote_get(int unit, soc_port_t port,
   1834                                soc_port_mode_t *mode)
   1835 {
   1836     uint16               mii_ctrl;  /* MII control register */ 
   1837     uint16               mii_stat;  /* MII status register */
   1838     uint16               mii_anp;   /* MII link partner ability reg */ 
   1839     phy_ctrl_t    *pc;        /* PHY software state */
   1840 
   1841     pc = EXT_PHY_SW_STATE(unit, port);
   1842 
   1843     /* read the 1000BASE-X Control Reg ( Address 0x00 ) */
   1844     SOC_IF_ERROR_RETURN
   1845         (READ_PHY5464_1000X_MII_CTRLr(unit, pc, &mii_ctrl));
   1846     if (!(mii_ctrl & MII_CTRL_AE)) {
   1847         return SOC_E_DISABLED;
   1848     } 
   1849 
   1850     /* read the 1000Base-X Status Reg  ( Address 0x01 ) */
   1851     SOC_IF_ERROR_RETURN
   1852         (READ_PHY5464_1000X_MII_STATr(unit, pc, &mii_stat));
   1853     if (!(mii_stat & MII_STAT_AN_DONE)) {
   1854         return SOC_E_NONE;
   1855     }
   1856 
   1857     /* read the auto-Neg Link Partner Ability Reg ( Address 0x05 ) */
   1858     SOC_IF_ERROR_RETURN
   1859         (READ_PHY5464_1000X_MII_ANPr(unit, pc, &mii_anp));
   1860 
   1861     *mode = 0;
   1862     if (mii_anp & (1 << 5))         *mode |= SOC_PM_1000MB_FD;
   1863     if (mii_anp & (1 << 6))         *mode |= SOC_PM_1000MB_HD;
   1864     switch ((mii_anp >> 7) & 0x3) {
   1865     case 0x1:
   1866         *mode |= SOC_PM_PAUSE;
   1867         break;
   1868     case 0x2:
   1869         *mode |= SOC_PM_PAUSE_TX;
   1870         break;
   1871     case 0x3:
   1872         *mode |= SOC_PM_PAUSE_RX;
   1873         break;
   1874     }
   1875     return SOC_E_NONE;
   1876 }
   1877 
   1878 /*
   1879  * Function:
   1880  *      phy_5464_adv_remote_get
   1881  * Purpose:
   1882  *      Get partners current advertisement for auto-negotiation.
   1883  * Parameters:
   1884  *      unit - StrataSwitch unit #.
   1885  *      port - StrataSwitch port #.
   1886  *      mode - (OUT) Port mode mask indicating supported options/speeds.
   1887  * Returns:
   1888  *      SOC_E_XXX
   1889  * Notes:
   1890  *      The remote advertisement is retrieved for the ACTIVE medium.
   1891  *      No synchronization performed at this level. If Autonegotiation is
   1892  *      disabled or in progress, this routine will return an error.
   1893  */
   1894 
   1895 STATIC int
   1896 phy_5464_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   1897 {
   1898     int                  rv;
   1899 
   1900     if (NULL == mode) {
   1901         return SOC_E_PARAM;
   1902     }
   1903 
   1904     rv = SOC_E_NONE;
   1905     if (PHY_COPPER_MODE(unit, port)) {
   1906         rv = phy_ge_adv_remote_get(unit, port, mode);
   1907     } else {    /* PHY_FIBER_MODE */
   1908         if (PHY_PASSTHRU_MODE(unit, port)) {
   1909             /* If the PHY is in passthrough mode, the inner PHY driver
   1910              * adv_remote_get is called by phyctrl. 
   1911              */
   1912             rv = SOC_E_INTERNAL;
   1913         } else { /* GMII fiber mode */
   1914             rv = _phy_5464_fiber_adv_remote_get(unit, port, mode);
   1915         }
   1916     }
   1917     return rv;
   1918 }
   1919 
   1920 /*
   1921  * Function:
   1922  *      phy_5464_lb_set
   1923  * Purpose:
   1924  *      Set the local PHY loopback mode.
   1925  * Parameters:
   1926  *      unit - StrataSwitch unit #.
   1927  *      port - StrataSwitch port #.
   1928  *      loopback - Boolean: true = enable loopback, false = disable.
   1929  * Returns:
   1930  *      SOC_E_XXX
   1931  * Notes:
   1932  *      The loopback mode is set only for the ACTIVE medium.
   1933  *      No synchronization performed at this level.
   1934  */
   1935 
   1936 STATIC int
   1937 phy_5464_lb_set(int unit, soc_port_t port, int enable)
   1938 {
   1939     int            rv;   /* Return value */
   1940     phy_ctrl_t    *pc;   /* PHY software state */
   1941 
   1942     rv = SOC_E_NONE;
   1943     pc = EXT_PHY_SW_STATE(unit, port);
   1944 
   1945     if (PHY_COPPER_MODE(unit, port)) {
   1946 #if AUTO_MDIX_WHEN_AN_DIS
   1947         {
   1948         uint16          tmp;
   1949 
   1950         /* Disable Auto-MDIX When autoneg disabled */
   1951         /* Enable Auto-MDIX When autoneg disabled */
   1952         tmp = (enable) ? 0x0000: 0x0200;
   1953         SOC_IF_ERROR_RETURN
   1954             (MODIFY_PHY5464_MII_MISC_CTRLr(unit, pc, tmp, 0x0200));
   1955         }
   1956 #endif
   1957         rv = phy_fe_ge_lb_set(unit, port, enable);
   1958 #ifdef BCM_HAWKEYE_SUPPORT
   1959         if (PHY_IS_BCM53314(pc)) {
   1960             /* Force link bit in MII_AUX_MULTI_PHY register has to be set
   1961              * so that the PHY is forced to show link up without
   1962              * the link partner
   1963              * WA for 10/100 Mbps in MAC/PHY loopback issue */
   1964 
   1965             uint16 mii_aux_multi_phy;
   1966             SOC_IF_ERROR_RETURN
   1967                 (READ_PHYFEGE_MII_AUX_MULTI_PHYr(unit, pc, &mii_aux_multi_phy));
   1968             if(enable) {
   1969                 mii_aux_multi_phy = mii_aux_multi_phy | 0x1000;
   1970             }else {
   1971                 mii_aux_multi_phy = mii_aux_multi_phy & (~0x1000);
   1972             }
   1973             SOC_IF_ERROR_RETURN
   1974                 (WRITE_PHYFEGE_MII_AUX_MULTI_PHYr(unit, pc,
   1975                             mii_aux_multi_phy));
   1976         }
   1977 #endif
   1978 
   1979     } else { /* Fiber mode */
   1980         if (PHY_PASSTHRU_MODE(unit, port)) {
   1981             phy_ctrl_t    *int_pc;   /* PHY software state */
   1982 
   1983             int_pc = INT_PHY_SW_STATE(unit, port);
   1984 
   1985             if (NULL != int_pc) {
   1986                 rv = (PHY_LOOPBACK_SET(int_pc->pd, unit, port, enable));
   1987             } else {
   1988                 rv = SOC_E_INTERNAL;
   1989             }
   1990         } else {  /* GMII fiber mode */ 
   1991             uint16 lb;
   1992             lb = (enable) ? MII_CTRL_LE : 0;
   1993             rv = MODIFY_PHY5464_1000X_MII_CTRLr(unit, pc, lb, MII_CTRL_LE);
   1994         }
   1995     }
   1996 
   1997     DPRINTF((BSL_META_U(unit,
   1998                         "phy_5464_lb_set: u=%d p=%d en=%d rv=%d\n"),
   1999              unit, port, enable, rv));
   2000     return rv;
   2001 }
   2002 
   2003 /*
   2004  * Function:
   2005  *      phy_5464_lb_get
   2006  * Purpose:
   2007  *      Get the local PHY loopback mode.
   2008  * Parameters:
   2009  *      unit - StrataSwitch unit #.
   2010  *      port - StrataSwitch port #.
   2011  *      loopback - (OUT) Boolean: true = enable loopback, false = disable.
   2012  * Returns:
   2013  *      SOC_E_XXX
   2014  * Notes:
   2015  *      The loopback mode is retrieved for the ACTIVE medium.
   2016  */
   2017 
   2018 STATIC int
   2019 phy_5464_lb_get(int unit, soc_port_t port, int *enable)
   2020 {
   2021     int            rv;
   2022     phy_ctrl_t    *pc;
   2023 
   2024     rv = SOC_E_NONE;
   2025     pc = EXT_PHY_SW_STATE(unit, port);
   2026     if (PHY_COPPER_MODE(unit, port)) {
   2027         rv = phy_fe_ge_lb_get(unit, port, enable);
   2028     } else { /* fiber mode */
   2029         if (PHY_PASSTHRU_MODE(unit, port)) {
   2030             /* If the PHY is in passthrough mode,             *\
   2031             \* phyctrl calls inner PHY driver loopback get()  */
   2032             phy_ctrl_t    *int_pc;      /* PHY software state */
   2033             int_pc = INT_PHY_SW_STATE(unit, port);
   2034 
   2035             if ( NULL != int_pc ) {
   2036                 rv = (PHY_LOOPBACK_GET(int_pc->pd, unit, port, enable));
   2037             } else {
   2038                 rv = SOC_E_INTERNAL;
   2039             }
   2040         } else { /* GMII fiber mode */
   2041             uint16 mii_ctrl;
   2042             rv = READ_PHY5464_1000X_MII_CTRLr(unit, pc, &mii_ctrl);
   2043             *enable = (mii_ctrl & MII_CTRL_LE) ? 1 : 0;
   2044         }
   2045     }
   2046     return rv;  
   2047 }
   2048 
   2049 /*
   2050  * Function:
   2051  *      phy_5464_interface_set
   2052  * Purpose:
   2053  *      Set the current operating mode of the PHY.
   2054  * Parameters:
   2055  *      unit - StrataSwitch unit #.
   2056  *      port - StrataSwitch port #.
   2057  *      pif - one of SOC_PORT_IF_*
   2058  * Returns:
   2059  *      SOC_E_NONE - success
   2060  *      SOC_E_UNAVAIL - unsupported interface
   2061  */
   2062 
   2063 STATIC int
   2064 phy_5464_interface_set(int unit, soc_port_t port, soc_port_if_t pif)
   2065 {
   2066     COMPILER_REFERENCE(unit);
   2067     COMPILER_REFERENCE(port);
   2068 
   2069     switch (pif) {
   2070     case SOC_PORT_IF_GMII:
   2071     case SOC_PORT_IF_SGMII:
   2072         return SOC_E_NONE;
   2073     default:
   2074         return SOC_E_UNAVAIL;
   2075     }
   2076 }
   2077 
   2078 /*
   2079  * Function:
   2080  *      phy_5464_interface_get
   2081  * Purpose:
   2082  *      Get the current operating mode of the PHY.
   2083  * Parameters:
   2084  *      unit - StrataSwitch unit #.
   2085  *      port - StrataSwitch port #.
   2086  *      pif - (OUT) one of SOC_PORT_IF_*
   2087  * Returns:
   2088  *      SOC_E_NONE
   2089  */
   2090 
   2091 STATIC int
   2092 phy_5464_interface_get(int unit, soc_port_t port, soc_port_if_t *pif)
   2093 {
   2094     phy_ctrl_t    *pc;
   2095 
   2096     COMPILER_REFERENCE(unit);
   2097     COMPILER_REFERENCE(port);
   2098 
   2099     pc = EXT_PHY_SW_STATE(unit, port);
   2100 
   2101     *pif = pc->interface;
   2102 
   2103     return SOC_E_NONE;
   2104 }
   2105 
   2106 /*
   2107  * Function:
   2108  *      phy_5464_ability_get
   2109  * Purpose:
   2110  *      Get the abilities of the local gigabit PHY.
   2111  * Parameters:
   2112  *      unit - StrataSwitch unit #.
   2113  *      port - StrataSwitch port #.
   2114  *      mode - (OUT) Port mode mask indicating supported options/speeds.
   2115  * Returns:
   2116  *      SOC_E_NONE
   2117  * Notes:
   2118  *      The ability is retrieved only for the ACTIVE medium.
   2119  */
   2120 
   2121 STATIC int
   2122 phy_5464_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   2123 {
   2124     int         rv;
   2125     phy_ctrl_t *pc;
   2126 
   2127     rv = SOC_E_NONE;
   2128     if (PHY_COPPER_MODE(unit, port)) {
   2129         rv = phy_ge_ability_get(unit, port, mode);
   2130     } else { /* fiber mode */
   2131         *mode = (SOC_PM_AN | SOC_PM_LB_PHY | 
   2132                  SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM |
   2133                  SOC_PM_GMII | SOC_PM_SGMII | SOC_PM_1000MB_FD);
   2134     }
   2135 
   2136     pc = EXT_PHY_SW_STATE(unit, port);
   2137     if (pc->automedium) {
   2138         *mode |= SOC_PM_COMBO;
   2139     }
   2140 
   2141     return rv;
   2142 }
   2143  
   2144 /*
   2145  * Function:
   2146  *      phy_5464_mdix_set
   2147  * Description:
   2148  *      Set the Auto-MDIX mode of a port/PHY
   2149  * Parameters:
   2150  *      unit - Device number
   2151  *      port - Port number
   2152  *      mode - One of:
   2153  *              SOC_PORT_MDIX_AUTO
   2154  *                      Enable auto-MDIX when autonegotiation is enabled
   2155  *              SOC_PORT_MDIX_FORCE_AUTO
   2156  *                      Enable auto-MDIX always
   2157  *              SOC_PORT_MDIX_NORMAL
   2158  *                      Disable auto-MDIX
   2159  *              SOC_PORT_MDIX_XOVER
   2160  *                      Disable auto-MDIX, and swap cable pairs
   2161  * Return Value:
   2162  *      SOC_E_XXX
   2163  */
   2164 STATIC int
   2165 phy_5464_mdix_set(int unit, soc_port_t port, soc_port_mdix_t mode)
   2166 {
   2167     phy_ctrl_t    *pc;
   2168     int            speed;
   2169 
   2170     pc = EXT_PHY_SW_STATE(unit, port);
   2171 
   2172     if (PHY_COPPER_MODE(unit, port)) {
   2173         switch (mode) {
   2174         case SOC_PORT_MDIX_AUTO:
   2175             /* Clear bit 14 for automatic MDI crossover */
   2176             SOC_IF_ERROR_RETURN
   2177                 (MODIFY_PHY5464_MII_ECRr(unit, pc, 0, 0x4000));
   2178 
   2179             /*
   2180              * Write the result in the register 0x18, shadow copy 7
   2181              */
   2182             /* Clear bit 9 to disable forced auto MDI xover */
   2183             SOC_IF_ERROR_RETURN
   2184                 (MODIFY_PHY5464_MII_MISC_CTRLr(unit, pc, 0, 0x0200));
   2185             break;
   2186 
   2187         case SOC_PORT_MDIX_FORCE_AUTO:
   2188             /* Clear bit 14 for automatic MDI crossover */
   2189             SOC_IF_ERROR_RETURN
   2190                 (MODIFY_PHY5464_MII_ECRr(unit, pc, 0, 0x4000));
   2191 
   2192             /*
   2193              * Write the result in the register 0x18, shadow copy 7
   2194              */
   2195             /* Set bit 9 to force automatic MDI crossover */
   2196             SOC_IF_ERROR_RETURN
   2197                 (MODIFY_PHY5464_MII_MISC_CTRLr(unit, pc, 0x0200, 0x0200));
   2198             break;
   2199 
   2200         case SOC_PORT_MDIX_NORMAL:
   2201             SOC_IF_ERROR_RETURN(phy_5464_speed_get(unit, port, &speed));
   2202             if (speed == 0 || speed == 10 || speed == 100) {
   2203                 /* Set bit 14 for manual MDI crossover */
   2204                 SOC_IF_ERROR_RETURN
   2205                     (MODIFY_PHY5464_MII_ECRr(unit, pc, 0x4000, 0x4000));
   2206 
   2207                 SOC_IF_ERROR_RETURN
   2208                     (WRITE_PHY5464_TEST1r(unit, pc, 0));
   2209             } else {
   2210                 return SOC_E_UNAVAIL;
   2211             }
   2212             break;
   2213 
   2214         case SOC_PORT_MDIX_XOVER:
   2215             SOC_IF_ERROR_RETURN(phy_5464_speed_get(unit, port, &speed));
   2216             if (speed == 0 || speed == 10 || speed == 100) {
   2217                  /* Set bit 14 for manual MDI crossover */
   2218                 SOC_IF_ERROR_RETURN
   2219                     (MODIFY_PHY5464_MII_ECRr(unit, pc, 0x4000, 0x4000));
   2220 
   2221                 SOC_IF_ERROR_RETURN
   2222                     (WRITE_PHY5464_TEST1r(unit, pc, 0x0080));
   2223             } else {
   2224                 return SOC_E_UNAVAIL;
   2225             }
   2226             break;
   2227 
   2228         default:
   2229             return SOC_E_PARAM;
   2230         }
   2231 
   2232         pc->copper.mdix = mode;
   2233         return SOC_E_NONE;
   2234     } else {    /* fiber */
   2235         if (mode != SOC_PORT_MDIX_NORMAL) {
   2236             return SOC_E_UNAVAIL;
   2237         }
   2238         return SOC_E_NONE;
   2239     }
   2240 }        
   2241 
   2242 #if defined(BCM_WARM_BOOT_SUPPORT)
   2243 /*
   2244  * Function:
   2245  *      phy_5464_mdix_wb_update
   2246  * Description:
   2247  *      Read the hw, derive the mode and update the mdix mode in sw structure
   2248  *      during wb.
   2249  * Parameters:
   2250  *      unit - (IN) Device number
   2251  *      port - (IN) Port number
   2252  *      mode - (OUT) :One of:
   2253  *              SOC_PORT_MDIX_AUTO
   2254  *                      Enable auto-MDIX when autonegotiation is enabled
   2255  *              SOC_PORT_MDIX_FORCE_AUTO
   2256  *                      Enable auto-MDIX always
   2257  *              SOC_PORT_MDIX_NORMAL
   2258  *                      Disable auto-MDIX
   2259  *              SOC_PORT_MDIX_XOVER
   2260  *                      Disable auto-MDIX, and swap cable pairs
   2261  * Return Value:
   2262  *      SOC_E_XXX
   2263  */
   2264 STATIC int
   2265 phy_5464_mdix_wb_update(int unit, soc_port_t port)
   2266 {
   2267     phy_ctrl_t    *pc;
   2268     int            speed;
   2269     int            mode = 0;
   2270     uint16         val = 0;
   2271 
   2272     pc = EXT_PHY_SW_STATE(unit, port);
   2273 
   2274     if (PHY_COPPER_MODE(unit, port)) {
   2275         /* Check bit 14 for automatic MDI crossover */
   2276         SOC_IF_ERROR_RETURN (READ_PHY5464_MII_ECRr(unit, pc, &val));
   2277         if(val & 0x4000) {
   2278             SOC_IF_ERROR_RETURN(phy_5464_speed_get(unit, port, &speed));
   2279             if (speed == 0 || speed == 10 || speed == 100) {
   2280                 SOC_IF_ERROR_RETURN (READ_PHY5464_TEST1r(unit, pc, &val));
   2281                 if (0 == val) {
   2282                     mode = SOC_PORT_MDIX_NORMAL;
   2283                 } else if (0x80 == val) {
   2284                     mode = SOC_PORT_MDIX_XOVER;
   2285                 } else {
   2286                     return SOC_E_UNAVAIL;
   2287                 }
   2288             } else {
   2289                 return SOC_E_UNAVAIL;
   2290             }
   2291         } else {
   2292             /* Check bit 9 forced auto MDI xover */
   2293             SOC_IF_ERROR_RETURN (READ_PHY5464_MII_MISC_CTRLr(unit, pc,&val));
   2294             if (val & 0x200) {
   2295                 /* bit 9 Set, forced auto MDI xover */
   2296                 mode = SOC_PORT_MDIX_FORCE_AUTO;
   2297             } else {
   2298                 /* bit 9 Clear, forced auto MDI xover */
   2299                 mode = SOC_PORT_MDIX_AUTO;
   2300             }
   2301         }
   2302         pc->copper.mdix = mode;
   2303     } else {
   2304         /* fiber */
   2305         mode = SOC_PORT_MDIX_NORMAL;
   2306     }
   2307     return SOC_E_NONE;
   2308 }
   2309 #endif /* defined(BCM_WARM_BOOT_SUPPORT) */
   2310 
   2311 /*
   2312  * Function:
   2313  *      phy_5464_mdix_get
   2314  * Description:
   2315  *      Get the Auto-MDIX mode of a port/PHY
   2316  * Parameters:
   2317  *      unit - Device number
   2318  *      port - Port number
   2319  *      mode - (Out) One of:
   2320  *              SOC_PORT_MDIX_AUTO
   2321  *                      Enable auto-MDIX when autonegotiation is enabled
   2322  *              SOC_PORT_MDIX_FORCE_AUTO
   2323  *                      Enable auto-MDIX always
   2324  *              SOC_PORT_MDIX_NORMAL
   2325  *                      Disable auto-MDIX
   2326  *              SOC_PORT_MDIX_XOVER
   2327  *                      Disable auto-MDIX, and swap cable pairs
   2328  * Return Value:
   2329  *      SOC_E_XXX
   2330  */
   2331 STATIC int
   2332 phy_5464_mdix_get(int unit, soc_port_t port, soc_port_mdix_t *mode)
   2333 {
   2334     phy_ctrl_t    *pc = EXT_PHY_SW_STATE(unit, port);
   2335     int                  speed;
   2336 #if defined(BCM_WARM_BOOT_SUPPORT)
   2337     int                  rv;
   2338 #endif /* defined(BCM_WARM_BOOT_SUPPORT)  */
   2339 
   2340     if (mode == NULL || pc == NULL) {
   2341         return SOC_E_PARAM;
   2342     }
   2343 #if defined(BCM_WARM_BOOT_SUPPORT)
   2344     if (SOC_WARM_BOOT(unit)) {
   2345         rv = phy_5464_mdix_wb_update(unit, port);
   2346         return rv;
   2347     }
   2348 #endif /* defined(BCM_WARM_BOOT_SUPPORT) */
   2349     if (PHY_COPPER_MODE(unit, port)) {
   2350         SOC_IF_ERROR_RETURN(phy_5464_speed_get(unit, port, &speed));
   2351         if (speed == 1000) {
   2352            *mode = SOC_PORT_MDIX_AUTO;
   2353         } else {
   2354             *mode = pc->copper.mdix;
   2355         }
   2356     } else {
   2357         *mode = SOC_PORT_MDIX_NORMAL;
   2358     }
   2359 
   2360     return SOC_E_NONE;
   2361 }    
   2362 
   2363 /*
   2364  * Function:
   2365  *      phy_5464_mdix_status_get
   2366  * Description:
   2367  *      Get the current MDIX status on a port/PHY
   2368  * Parameters:
   2369  *      unit    - Device number
   2370  *      port    - Port number
   2371  *      status  - (OUT) One of:
   2372  *              SOC_PORT_MDIX_STATUS_NORMAL
   2373  *                      Straight connection
   2374  *              SOC_PORT_MDIX_STATUS_XOVER
   2375  *                      Crossover has been performed
   2376  * Return Value:
   2377  *      SOC_E_XXX
   2378  */
   2379 STATIC int
   2380 phy_5464_mdix_status_get(int unit, soc_port_t port, 
   2381                          soc_port_mdix_status_t *status)
   2382 {
   2383     phy_ctrl_t    *pc = EXT_PHY_SW_STATE(unit, port);
   2384     uint16               tmp;
   2385 
   2386     if (status == NULL || pc == NULL) {
   2387         return SOC_E_PARAM;
   2388     }
   2389 
   2390     if (PHY_COPPER_MODE(unit, port)) {
   2391         SOC_IF_ERROR_RETURN
   2392             (READ_PHY5464_MII_ESRr(unit, pc, &tmp));
   2393         if (tmp & 0x2000) {
   2394             *status = SOC_PORT_MDIX_STATUS_XOVER;
   2395         } else {
   2396             *status = SOC_PORT_MDIX_STATUS_NORMAL;
   2397         }
   2398     } else {
   2399         *status = SOC_PORT_MDIX_STATUS_NORMAL;
   2400     }
   2401 
   2402     return SOC_E_NONE;
   2403 }    
   2404 
   2405 /*
   2406  * Helper defines for expansion register access
   2407  * Used throughout the cable diagnostic code
   2408  */
   2409 #define WRITE_EXP(_r, _v) \
   2410         SOC_IF_ERROR_RETURN \
   2411                 (WRITE_PHY_REG(unit, pc, 0x17, 0x0f00 | _r)); \
   2412         SOC_IF_ERROR_RETURN \
   2413                 (WRITE_PHY_REG(unit, pc, 0x15, _v))
   2414 
   2415 #define READ_EXP(_r, _v) \
   2416         SOC_IF_ERROR_RETURN \
   2417                 (WRITE_PHY_REG(unit, pc, 0x17, 0x0f00 | _r)); \
   2418         SOC_IF_ERROR_RETURN \
   2419                 (READ_PHY_REG(unit, pc, 0x15, _v))
   2420 
   2421 
   2422 /* saved state when running cable diagnostics */
   2423 typedef struct {
   2424     uint16      reg_1c;
   2425     uint16      reg_00;
   2426     uint16      reg_18_0;
   2427     uint16      reg_18_7;
   2428     uint16      reg_17_0;
   2429     uint16      reg_17_8100;
   2430 } cdiag_setup_t;
   2431 
   2432 /*
   2433  * Function:
   2434  *      phy_5464_cable_diag_setup
   2435  * Purpose:
   2436  *      Put phy into a state that it can run cable diagnostics.
   2437  *      Various internal state machines must be stopped.
   2438  */
   2439 STATIC int
   2440 phy_5464_cable_diag_setup(int unit, uint8 phy_addr, cdiag_setup_t *setup)
   2441 {
   2442     phy_ctrl_t  *pc;
   2443     uint16       tmp;
   2444     soc_port_t   port;
   2445 
   2446     port = soc_phy_addr_to_port(unit, phy_addr); 
   2447     pc   = EXT_PHY_SW_STATE(unit, port);
   2448 
   2449     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x1c, 0x7c00));
   2450     SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x1c, &tmp));
   2451     setup->reg_1c = tmp;
   2452     tmp &= ~0x0001;     /* force copper regs */
   2453     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x1c, tmp | 0x8000));
   2454 
   2455     SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x00, &tmp));
   2456     setup->reg_00 = tmp;
   2457     tmp = 0x0040;       /* force 1G, no autoneg */
   2458     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x00, tmp));
   2459 
   2460     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, 0x0007));
   2461     SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x18, &tmp));
   2462     setup->reg_18_0 = tmp;
   2463     tmp |= 0x0800;      /* enable sm_dsp clock */
   2464     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, tmp));
   2465 
   2466     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, 0x7007));
   2467     SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x18, &tmp));
   2468     setup->reg_18_7 = tmp;
   2469     tmp &= ~0x200;      /* disable auto-mdix */
   2470     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, tmp | 0x8000));
   2471 
   2472     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x0000));
   2473     SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x15, &tmp));
   2474     setup->reg_17_0 = tmp;
   2475     tmp = 0xf8b5;       /* disable agc */
   2476     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, tmp));
   2477     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x2000));
   2478     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, tmp));
   2479     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x4000));
   2480     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, tmp));
   2481     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x6000));
   2482     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, tmp));
   2483 
   2484     /* freeze internal state machines */
   2485     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x8100));
   2486     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x16, 0x0002));
   2487     SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x15, &tmp));
   2488     setup->reg_17_8100 = tmp;
   2489     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, 0x0030));
   2490 
   2491     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x8200));
   2492     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x16, 0x0002));
   2493 
   2494     return SOC_E_NONE;
   2495 }
   2496 
   2497 #ifdef PHY_5464_CABLE_DIAG_UNSETUP
   2498 /*
   2499  * Function:
   2500  *      phy_5464_cable_diag_unsetup
   2501  * Purpose:
   2502  *      Restore phy into a working state after cable diagnostics
   2503  *      have been run.
   2504  */
   2505 STATIC int
   2506 phy_5464_cable_diag_unsetup(int unit, uint8 phy_addr, cdiag_setup_t *setup)
   2507 {
   2508     phy_ctrl_t *pc;
   2509 
   2510     pc = EXT_PHY_SW_STATE(unit, port);
   2511 
   2512     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, setup->reg_18_0));
   2513     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18,
   2514                                        setup->reg_18_7 | 0x8000));
   2515     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x0000));
   2516     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, setup->reg_17_0));
   2517     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x2000));
   2518     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, setup->reg_17_0));
   2519     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x4000));
   2520     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, setup->reg_17_0));
   2521     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x6000));
   2522     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, setup->reg_17_0));
   2523     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x8100));
   2524     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x16, 0x0000));
   2525     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15,
   2526                                        setup->reg_17_8100));
   2527     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x8200));
   2528     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x16, 0x0000));
   2529 
   2530     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x00, setup->reg_00));
   2531     SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x1c,
   2532                                        setup->reg_1c | 0x8000));
   2533     return SOC_E_NONE;
   2534 }
   2535 #endif /* PHY_5464_CABLE_DIAG_UNSETUP */
   2536 
   2537 /* cable diag pass info */
   2538 typedef struct {
   2539     int         skip_14_19;
   2540     uint16      set_14;         /* expansion regs to set */
   2541     uint16      set_15;
   2542     uint16      set_16;
   2543     uint16      set_17;
   2544     uint16      set_18;
   2545     uint16      set_19;
   2546     uint16      set_1a;
   2547     uint16      set_1b;
   2548     uint16      set_1c;
   2549     uint16      set_1d;
   2550     uint16      set_10;
   2551 
   2552     uint16      get_10;         /* expansion reg results */
   2553     uint16      get_11;        
   2554     uint16      get_12;
   2555     uint16      get_13;
   2556 } cdiag_pass_t;
   2557 
   2558 /*
   2559  * Function:
   2560  *      phy_5464_cable_diag_pass
   2561  * Purpose:
   2562  *      Run a cable diagnostic pass
   2563  */
   2564 STATIC int
   2565 phy_5464_cable_diag_pass(int unit, uint8 phy_addr, cdiag_pass_t *pass)
   2566 {
   2567     phy_ctrl_t *pc;
   2568     uint16      tmp;
   2569     int         loop, maxloop;
   2570     soc_port_t  port;
   2571 
   2572     port = soc_phy_addr_to_port(unit, phy_addr); 
   2573     pc = EXT_PHY_SW_STATE(unit, port);
   2574 
   2575     maxloop = 50;       /* usually takes about 5 */
   2576 
   2577     if (pass->skip_14_19 == 0) {
   2578         WRITE_EXP(0x14, pass->set_14);
   2579         WRITE_EXP(0x15, pass->set_15);
   2580         WRITE_EXP(0x16, pass->set_16);
   2581         WRITE_EXP(0x17, pass->set_17);
   2582         if (PHY_IS_BCM5478(pc) || 
   2583             PHY_IS_BCM5488(pc)) {
   2584             WRITE_EXP(0x1a, pass->set_18);
   2585             WRITE_EXP(0x1b, pass->set_19);
   2586         } else {
   2587             WRITE_EXP(0x18, pass->set_18);
   2588             WRITE_EXP(0x19, pass->set_19);
   2589         }
   2590     }
   2591     if (PHY_IS_BCM5478(pc) || 
   2592         PHY_IS_BCM5488(pc)) {
   2593         WRITE_EXP(0x1d, pass->set_1a);
   2594         WRITE_EXP(0x1e, pass->set_1b);
   2595         WRITE_EXP(0x1f, pass->set_1c);
   2596         WRITE_EXP(0x10, pass->set_10 & 0x00ff);
   2597         WRITE_EXP(0x28, pass->set_10 & 0xff00);
   2598     } else {
   2599         WRITE_EXP(0x1a, pass->set_1a);
   2600         WRITE_EXP(0x1b, pass->set_1b);
   2601         WRITE_EXP(0x1c, pass->set_1c);
   2602         WRITE_EXP(0x1d, pass->set_1d);
   2603         WRITE_EXP(0x10, pass->set_10);
   2604     }
   2605 
   2606     for (loop = 0; loop < maxloop; loop++) {
   2607         sal_usleep(1000);
   2608         if ((pc->phy_model == PHY_BCM5478_MODEL) || 
   2609             (pc->phy_model == PHY_BCM5488_MODEL)) {
   2610             READ_EXP(0x10, &tmp);
   2611             if (tmp & 0x0200) {
   2612                 pass->get_10 = tmp;
   2613                 READ_EXP(0x11, &tmp);
   2614                 break;
   2615             }
   2616         } else {
   2617             READ_EXP(0x11, &tmp);
   2618             if (tmp & 0x0001) {
   2619                 break;
   2620             }
   2621         }
   2622     }
   2623     if (loop >= maxloop) {
   2624         return SOC_E_TIMEOUT;
   2625     }
   2626 
   2627     pass->get_11 = tmp;
   2628     READ_EXP(0x12, &pass->get_12);
   2629     READ_EXP(0x13, &pass->get_13);
   2630     return SOC_E_NONE;
   2631 }
   2632 
   2633 #undef  WRITE_EXP
   2634 #undef  READ_EXP
   2635 
   2636 /*
   2637  * Function:
   2638  *      phy_5464_cable_diag
   2639  * Purpose:
   2640  *      Run 546x cable diagnostics
   2641  * Parameters:
   2642  *      unit - device number
   2643  *      port - port number
   2644  *      status - (OUT) cable diagnotic status structure
   2645  * Returns:     
   2646  *      SOC_E_XXX
   2647  */
   2648 extern int phy_5464_cable_diag_sw(int, soc_port_t, soc_port_cable_diag_t *);
   2649 STATIC int
   2650 phy_5464_cable_diag(int unit, soc_port_t port,
   2651                     soc_port_cable_diag_t *status)
   2652 {
   2653     phy_ctrl_t    *pc;
   2654     uint8          phy_addr;
   2655     int            rv, rv2, i;
   2656     int   bcm53314_speed = 0, bcm53314_an = 0, bcm53314_an_done = 0, bcm53314_link = 0;  /* BCM53314 WAR Fix */
   2657 
   2658     pc       = EXT_PHY_SW_STATE(unit, port);
   2659     phy_addr = pc->phy_id;
   2660 
   2661     if (status == NULL) {
   2662         return SOC_E_PARAM;
   2663     }
   2664 
   2665     if ( ! PHY_IS_BCM5464(pc) &&
   2666          ! PHY_IS_BCM5461(pc) &&
   2667          ! PHY_IS_BCM5482(pc) &&
   2668          ! PHY_IS_BCM5488(pc) &&
   2669          ! PHY_IS_BCM54980(pc) &&
   2670          ! PHY_IS_BCM54980C(pc) &&
   2671          ! PHY_IS_BCM54980V(pc) &&
   2672          ! PHY_IS_BCM54980VC(pc) &&
   2673          ! PHY_IS_BCM53314(pc) &&
   2674          ! PHY_IS_BCM5462(pc) &&
   2675          ! PHY_IS_BCM5466(pc) &&
   2676          ! PHY_IS_BCM5488(pc) &&
   2677          ! PHY_IS_BCM5482(pc) ) {
   2678         return SOC_E_UNAVAIL;
   2679     }
   2680 
   2681     status->state = SOC_PORT_CABLE_STATE_OK;
   2682     status->npairs = 4;
   2683     status->fuzz_len = 0;
   2684     for (i = 0; i < 4; i++) {
   2685         status->pair_state[i] = SOC_PORT_CABLE_STATE_OK;
   2686     }
   2687 
   2688     if (soc_property_get(unit, spn_CABLE_DIAG_HW, PHY_5464_CABLE_DIAG_HW)) {
   2689         uint16              tmp, result;
   2690         cdiag_setup_t       setup_state;
   2691         cdiag_pass_t        pass;
   2692 
   2693         if (!PHY_IS_BCM5464(pc) && !PHY_IS_BCM5461(pc)) {
   2694             return SOC_E_UNAVAIL;
   2695         }
   2696  
   2697         SOC_IF_ERROR_RETURN
   2698             (READ_PHY_REG(unit, pc, 0x11, &tmp));
   2699 
   2700         if (tmp & 0x0100) {         /* link up? */
   2701             /* is linked at 10 or 100Mbit, diags will fail */
   2702             SOC_IF_ERROR_RETURN
   2703                 (READ_PHY_REG(unit, pc, 0x19, &tmp));
   2704             if ((tmp & 0x0600) != 0x0600) {
   2705                 status->npairs = 2;
   2706                 status->pair_len[0] = -1;
   2707                 status->pair_len[1] = -1;
   2708                 return SOC_E_NONE;
   2709             }
   2710 
   2711             /* run length checks for defect free cable */
   2712             pass.skip_14_19 = 1;
   2713             pass.set_14 = 0;
   2714             pass.set_15 = 0;
   2715             pass.set_16 = 0;
   2716             pass.set_17 = 0;
   2717             pass.set_18 = 0;
   2718             pass.set_19 = 0;
   2719             pass.set_1a = 0x012c;
   2720             pass.set_1b = 0x00c8;
   2721             pass.set_1c = 0x00a0;
   2722             pass.set_1d = 0x6096;
   2723             pass.set_10 = 0x0207;
   2724             rv = phy_5464_cable_diag_pass(unit, phy_addr, &pass);
   2725             if (rv >= 0) {
   2726                 status->pair_len[0] = pass.get_12 & 0xff;
   2727                 status->pair_len[1] = (pass.get_12 >> 8) & 0xff;
   2728                 status->pair_len[2] = pass.get_13 & 0xff;
   2729                 status->pair_len[3] = (pass.get_13 >> 8) & 0xff;
   2730                 if (pc->phy_model == PHY_BCM5464_MODEL &&
   2731                     pc->phy_rev == PHY_REV_A0) {
   2732                     /* A0 has a tendency to give pairs B, D a length of 152 */
   2733                     if (status->pair_len[0] < 128 && status->pair_len[1] > 140) {
   2734                         status->pair_len[1] = status->pair_len[0];
   2735                     }
   2736                     if (status->pair_len[1] < 128 && status->pair_len[0] > 140) {
   2737                         status->pair_len[0] = status->pair_len[1];
   2738                     }
   2739                     if (status->pair_len[2] < 128 && status->pair_len[3] > 140) {
   2740                         status->pair_len[3] = status->pair_len[2];
   2741                     }
   2742                     if (status->pair_len[3] < 128 && status->pair_len[2] > 140) {
   2743                         status->pair_len[2] = status->pair_len[3];
   2744                     }
   2745                 }
   2746             }
   2747             return rv;
   2748         }
   2749 
   2750         /* link not up, so run defect checks */
   2751     
   2752         /* setup phy for cable diags: disable various automated things */
   2753         MIIM_LOCK(unit);    /* this locks out linkscan, essentially */
   2754         rv = phy_5464_cable_diag_setup(unit, phy_addr, &setup_state);
   2755         if (rv < 0) {
   2756             MIIM_UNLOCK(unit);
   2757             return rv;
   2758         }
   2759 
   2760         /*
   2761          * perhaps some of these per pass magic numbers should come
   2762          * from properties, but that's a lot of properties...
   2763          */
   2764         
   2765 
   2766         /* pass 1 */
   2767         pass.skip_14_19 = 0;
   2768         pass.set_14 = 0x5014;       /* 0x4650 */
   2769         pass.set_15 = 0x0ed8;       /* 0x0bb8 */
   2770         pass.set_16 = 0x0af0;       /* 0x09c4 */
   2771         pass.set_17 = 0x047e;       /* 0x03e8 */
   2772         pass.set_18 = 0x0a14;
   2773         pass.set_19 = 0x3c00;
   2774         pass.set_1a = 0x0640;
   2775         pass.set_1b = 0x00fa;
   2776         pass.set_1c = 0x012c;
   2777         pass.set_1d = 0x0a60;
   2778         pass.set_10 = 0x000b;
   2779         rv = phy_5464_cable_diag_pass(unit, phy_addr, &pass);
   2780 
   2781         if ((pc->phy_model == PHY_BCM5478_MODEL) || 
   2782             (pc->phy_model == PHY_BCM5488_MODEL)) {
   2783             result = pass.get_10 & 0x0400;
   2784         } else {
   2785             result = pass.get_11 & 0x0002;
   2786         }
   2787         if (rv >= 0 && result == 0) {
   2788             /* pass 2 (no errors found in pass 1) */
   2789             pass.set_14 = 0xffff;
   2790             pass.set_15 = 0x04b0;
   2791             pass.set_16 = 0x0320;
   2792             pass.set_17 = 0x0320;
   2793             pass.set_18 = 0x608c;
   2794             pass.set_19 = 0xa000;
   2795             pass.set_1a = 0x03e8;
   2796             pass.set_1b = 0x0258;
   2797             pass.set_1c = 0x00b4;
   2798             pass.set_1d = 0x0a14;
   2799             pass.set_10 = 0x000b;
   2800             rv = phy_5464_cable_diag_pass(unit, phy_addr, &pass);
   2801         }
   2802 
   2803         if ((pc->phy_model == PHY_BCM5478_MODEL) || 
   2804             (pc->phy_model == PHY_BCM5488_MODEL)) {
   2805             result = pass.get_10 & 0x0400;
   2806         } else {
   2807             result = pass.get_11 & 0x0002;
   2808         }
   2809         if (rv >= 0 && result == 0) {
   2810             /* pass 3 (no errors found in pass 1 or 2) */
   2811             pass.set_14 = 0xffff;
   2812             pass.set_15 = 0xffff;
   2813             pass.set_16 = 0xffff;
   2814             pass.set_17 = 0xffff;
   2815             pass.set_18 = 0x608c;
   2816             pass.set_19 = 0xa000;
   2817             pass.set_1a = 0x03e8;
   2818             pass.set_1b = 0x0258;
   2819             pass.set_1c = 0x00b4;
   2820             pass.set_1d = 0x0a14;
   2821             pass.set_10 = 0x000b;
   2822             rv = phy_5464_cable_diag_pass(unit, phy_addr, &pass);
   2823         }
   2824 
   2825         if (rv >= 0) {
   2826             if ((pc->phy_model == PHY_BCM5478_MODEL) || 
   2827                 (pc->phy_model == PHY_BCM5488_MODEL)) {
   2828                 if (pass.get_10 & 0x0400) {
   2829                     status->state = SOC_PORT_CABLE_STATE_OPENSHORT;
   2830                 }
   2831                 if (pass.get_11 & 0x0003) {
   2832                     status->pair_state[0] = SOC_PORT_CABLE_STATE_OPENSHORT;
   2833                 } else if (pass.get_11 & 0x1000) {
   2834                     status->pair_state[0] = SOC_PORT_CABLE_STATE_UNKNOWN;
   2835                 }
   2836                 if (pass.get_11 & 0x000c) {
   2837                     status->pair_state[1] = SOC_PORT_CABLE_STATE_OPENSHORT;
   2838                 } else if (pass.get_11 & 0x2000) {
   2839                     status->pair_state[1] = SOC_PORT_CABLE_STATE_UNKNOWN;
   2840                 }
   2841                 if (pass.get_11 & 0x0030) {
   2842                     status->pair_state[2] = SOC_PORT_CABLE_STATE_OPENSHORT;
   2843                 } else if (pass.get_11 & 0x4000) {
   2844                     status->pair_state[2] = SOC_PORT_CABLE_STATE_UNKNOWN;
   2845                 }
   2846                 if (pass.get_11 & 0x00c0) {
   2847                     status->pair_state[3] = SOC_PORT_CABLE_STATE_OPENSHORT;
   2848                 } else if (pass.get_11 & 0x8000) {
   2849                     status->pair_state[3] = SOC_PORT_CABLE_STATE_UNKNOWN;
   2850                 }
   2851             } else {
   2852                 if (pass.get_11 & 0x0002) {
   2853                     status->state = SOC_PORT_CABLE_STATE_OPENSHORT;
   2854                 }
   2855                 if (pass.get_11 & 0x0100) {
   2856                     status->pair_state[0] = SOC_PORT_CABLE_STATE_OPENSHORT;
   2857                 } else if (pass.get_11 & 0x1000) {
   2858                     status->pair_state[0] = SOC_PORT_CABLE_STATE_UNKNOWN;
   2859                 }
   2860                 if (pass.get_11 & 0x0200) {
   2861                     status->pair_state[1] = SOC_PORT_CABLE_STATE_OPENSHORT;
   2862                 } else if (pass.get_11 & 0x2000) {
   2863                     status->pair_state[1] = SOC_PORT_CABLE_STATE_UNKNOWN;
   2864                 }
   2865                 if (pass.get_11 & 0x0400) {
   2866                     status->pair_state[2] = SOC_PORT_CABLE_STATE_OPENSHORT;
   2867                 } else if (pass.get_11 & 0x4000) {
   2868                     status->pair_state[2] = SOC_PORT_CABLE_STATE_UNKNOWN;
   2869                 }
   2870                 if (pass.get_11 & 0x0800) {
   2871                     status->pair_state[3] = SOC_PORT_CABLE_STATE_OPENSHORT;
   2872                 } else if (pass.get_11 & 0x8000) {
   2873                     status->pair_state[3] = SOC_PORT_CABLE_STATE_UNKNOWN;
   2874                 }
   2875             }
   2876             status->pair_len[0] = pass.get_12 & 0xff;
   2877             status->pair_len[1] = (pass.get_12 >> 8) & 0xff;
   2878             status->pair_len[2] = pass.get_13 & 0xff;
   2879             status->pair_len[3] = (pass.get_13 >> 8) & 0xff;
   2880         }
   2881 
   2882         /* restore various control values */
   2883 #ifdef PHY_5464_CABLE_DIAG_UNSETUP
   2884         rv2 = phy_5464_cable_diag_unsetup(unit, phy_addr, &setup_state);
   2885 #endif
   2886         MIIM_UNLOCK(unit);
   2887         rv2 = _phy_5464_reset_setup(unit, port);
   2888     
   2889         if (rv >= 0 && rv2 < 0) {
   2890             return rv2;
   2891         }
   2892         return rv;
   2893     }
   2894 
   2895 /* BCM53314 WAR Fix */
   2896     if (PHY_IS_BCM53314(pc)) {
   2897        SOC_IF_ERROR_RETURN(phy_5464_link_get(unit, port, &bcm53314_link));
   2898        SOC_IF_ERROR_RETURN(phy_5464_autoneg_get(unit, port, &bcm53314_an, &bcm53314_an_done));
   2899        SOC_IF_ERROR_RETURN(phy_5464_speed_get(unit, port, &bcm53314_speed));
   2900     }
   2901 
   2902     MIIM_LOCK(unit);    /* this locks out linkscan, essentially */
   2903     rv = phy_5464_cable_diag_sw(unit, port, status);
   2904     MIIM_UNLOCK(unit);
   2905     rv2 = 0;
   2906     if (rv <= 0) {      /* don't reset if > 0 -- link was up */
   2907         rv2 = _phy_5464_reset_setup(unit, port);
   2908     }
   2909 
   2910 /* BCM53314 WAR Fix */
   2911     if (bcm53314_an == TRUE && bcm53314_link == TRUE) {
   2912        if (bcm53314_speed == 100 || bcm53314_speed == 10) {
   2913           SOC_IF_ERROR_RETURN(phy_5464_autoneg_set(unit, port, TRUE));
   2914        }
   2915     }
   2916 
   2917     if (rv >= 0 && rv2 < 0) {
   2918         return rv2;
   2919     }
   2920     return rv;
   2921 }
   2922 
   2923 STATIC int
   2924 _phy_5464_power_mode_set (int unit, soc_port_t port, int mode)
   2925 {
   2926     phy_ctrl_t    *pc;
   2927 
   2928     pc       = EXT_PHY_SW_STATE(unit, port);
   2929 
   2930     if (pc->power_mode == mode) {
   2931         return SOC_E_NONE;
   2932     }
   2933 
   2934     if (mode == SOC_PHY_CONTROL_POWER_LOW) {
   2935         if (PHY_IS_BCM53314(pc)) {
   2936             /* enable dsp clock */
   2937             SOC_IF_ERROR_RETURN
   2938                 (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0c00,0x0c00));
   2939 
   2940             /* enable low power 136 */
   2941             SOC_IF_ERROR_RETURN
   2942                 (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc,0x80,0x80));
   2943 
   2944             /* reduce tx bias current to -20% */
   2945             SOC_IF_ERROR_RETURN
   2946                 (WRITE_PHY_REG(unit, pc, 0x17, 0x0f75));
   2947             SOC_IF_ERROR_RETURN
   2948                 (WRITE_PHY_REG(unit, pc, 0x15, 0x1555));
   2949 
   2950             /* disable dsp clock */
   2951             SOC_IF_ERROR_RETURN
   2952                 (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0400,0x0c00));
   2953             pc->power_mode = mode;
   2954         } else {
   2955             return SOC_E_UNAVAIL;
   2956         }
   2957     } else if (mode == SOC_PHY_CONTROL_POWER_FULL) {
   2958 
   2959         /* back to normal mode */
   2960         /* enable dsp clock */
   2961         SOC_IF_ERROR_RETURN
   2962             (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0c00,0x0c00));
   2963 
   2964         /* disable low power 136 */
   2965         SOC_IF_ERROR_RETURN
   2966             (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc,0x00,0x80));
   2967 
   2968         /* reduce tx bias current to -20% */
   2969         SOC_IF_ERROR_RETURN
   2970             (WRITE_PHY_REG(unit, pc, 0x17, 0x0f75));
   2971         SOC_IF_ERROR_RETURN
   2972             (WRITE_PHY_REG(unit, pc, 0x15, 0x0));
   2973 
   2974         /* disable dsp clock */
   2975         SOC_IF_ERROR_RETURN
   2976             (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0400,0x0c00));
   2977   
   2978         /* disable the auto power mode */
   2979         SOC_IF_ERROR_RETURN
   2980             (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc,
   2981                         0,
   2982                         PHY_5464_AUTO_PWRDWN_EN));
   2983         pc->power_mode = mode;
   2984     } else if (mode == SOC_PHY_CONTROL_POWER_AUTO) {
   2985         SOC_IF_ERROR_RETURN
   2986             (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc,
   2987                         PHY_5464_AUTO_PWRDWN_EN,
   2988                         PHY_5464_AUTO_PWRDWN_EN));
   2989         pc->power_mode = mode;
   2990     }
   2991     return SOC_E_NONE;
   2992 }
   2993 
   2994 /*
   2995  * Function:
   2996  *      phy_5464_control_set
   2997  * Purpose:
   2998  *      Configure PHY device specific control fucntion.
   2999  * Parameters:
   3000  *      unit  - StrataSwitch unit #.
   3001  *      port  - StrataSwitch port #.
   3002  *      type  - Control to update
   3003  *      value - New setting for the control
   3004  * Returns:
   3005  *      SOC_E_NONE
   3006  */
   3007 STATIC int
   3008 phy_5464_control_set(int unit, soc_port_t port,
   3009                      soc_phy_control_t type, uint32 value)
   3010 {
   3011     int rv;
   3012     uint16 data16;
   3013     phy_ctrl_t *pc;
   3014 
   3015     PHY_CONTROL_TYPE_CHECK(type);
   3016 
   3017     pc = EXT_PHY_SW_STATE(unit, port);
   3018     rv = SOC_E_NONE;
   3019     switch(type) {
   3020     case SOC_PHY_CONTROL_POWER:
   3021         rv = _phy_5464_power_mode_set(unit,port,value);
   3022         break;
   3023 
   3024     case SOC_PHY_CONTROL_POWER_AUTO_WAKE_TIME:
   3025         if (value <= PHY_5464_AUTO_PWRDWN_WAKEUP_MAX) {
   3026 
   3027             /* at least one unit */
   3028             if (value < PHY_5464_AUTO_PWRDWN_WAKEUP_UNIT) {
   3029                 value = PHY_5464_AUTO_PWRDWN_WAKEUP_UNIT;
   3030             }
   3031         } else { /* anything more then max, set to the max */
   3032             value = PHY_5464_AUTO_PWRDWN_WAKEUP_MAX;
   3033         }
   3034         SOC_IF_ERROR_RETURN 
   3035             (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc,
   3036                       value/PHY_5464_AUTO_PWRDWN_WAKEUP_UNIT, 
   3037                       PHY_5464_AUTO_PWRDWN_WAKEUP_MASK)); 
   3038         break;
   3039 
   3040     case SOC_PHY_CONTROL_POWER_AUTO_SLEEP_TIME:
   3041         /* sleep time configuration is either 2.7s or 5.4 s, default is 2.7s */
   3042         if (value < PHY_5464_AUTO_PWRDWN_SLEEP_MAX) {
   3043             data16 = 0; /* anything less than 5.4s, default to 2.7s */
   3044         } else { 
   3045             data16 = PHY_5464_AUTO_PWRDWN_SLEEP_MASK;
   3046         }
   3047         SOC_IF_ERROR_RETURN 
   3048             (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc,
   3049                       data16,
   3050                       PHY_5464_AUTO_PWRDWN_SLEEP_MASK));
   3051         break;
   3052 
   3053     case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT:
   3054         /* Not supported for 10/100/1000BASE-T interfaces */
   3055         rv = SOC_E_UNAVAIL;
   3056         break;
   3057 
   3058     default:
   3059         rv = SOC_E_UNAVAIL;
   3060         break;
   3061     }
   3062     return rv;
   3063 }
   3064 
   3065 /*
   3066  * Function:
   3067  *      phy_5464_control_get
   3068  * Purpose:
   3069  *      Get current control settign of the PHY.
   3070  * Parameters:
   3071  *      unit  - StrataSwitch unit #.
   3072  *      port  - StrataSwitch port #.
   3073  *      type  - Control to update
   3074  *      value - (OUT)Current setting for the control
   3075  * Returns:
   3076  *      SOC_E_NONE
   3077  */
   3078 STATIC int
   3079 phy_5464_control_get(int unit, soc_port_t port,
   3080                      soc_phy_control_t type, uint32 *value)
   3081 {
   3082     int rv;
   3083     phy_ctrl_t *pc;
   3084     uint16 data16;
   3085 
   3086     PHY_CONTROL_TYPE_CHECK(type);
   3087 
   3088     pc = EXT_PHY_SW_STATE(unit, port);
   3089     rv = SOC_E_NONE;
   3090     switch(type) {
   3091     case SOC_PHY_CONTROL_POWER:
   3092         *value = pc->power_mode;
   3093         break;
   3094     case SOC_PHY_CONTROL_POWER_AUTO_SLEEP_TIME:
   3095         SOC_IF_ERROR_RETURN 
   3096             (READ_PHY5464_AUTO_POWER_DOWNr(unit,pc, &data16));
   3097 
   3098         if (data16 & PHY_5464_AUTO_PWRDWN_SLEEP_MASK) {
   3099             *value = PHY_5464_AUTO_PWRDWN_SLEEP_MAX;
   3100         } else {
   3101             *value = 2700;
   3102         }
   3103         break;
   3104 
   3105     case SOC_PHY_CONTROL_POWER_AUTO_WAKE_TIME:
   3106         SOC_IF_ERROR_RETURN 
   3107             (READ_PHY5464_AUTO_POWER_DOWNr(unit,pc, &data16));
   3108 
   3109         data16 &= PHY_5464_AUTO_PWRDWN_WAKEUP_MASK;
   3110         *value = data16 * PHY_5464_AUTO_PWRDWN_WAKEUP_UNIT;
   3111         break;
   3112         
   3113     case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT:
   3114         /* Not supported for 10/100/1000BASE-T interfaces */
   3115         rv = SOC_E_UNAVAIL;
   3116         break;
   3117 
   3118     default:
   3119         rv = SOC_E_UNAVAIL;
   3120         break;
   3121     }
   3122     return rv;
   3123 }
   3124 
   3125 #if LINK_DOWN_RESET_WAR
   3126 
   3127 /*
   3128  * Function:
   3129  *      phy_5464_link_down
   3130  * Purpose:
   3131  *      Perform reset work-around on link down (A0 silicon only)
   3132  * Parameters:
   3133  *      unit - StrataSwitch unit #.
   3134  *      port - StrataSwitch port #.
   3135  * Returns:
   3136  *      SOC_E_XXX
   3137  */
   3138 
   3139 STATIC int
   3140 phy_5464_link_down(int unit, soc_port_t port)
   3141 {
   3142     phy_ctrl_t    *pc = EXT_PHY_SW_STATE(unit, port);
   3143 
   3144     /* Only applies to copper link down on rev A0 */
   3145 
   3146     if (PHY_IS_BCM5464(pc) && pc->phy_rev == PHY_REV_A0 &&
   3147         PHY_COPPER_MODE(unit, port)) {
   3148 
   3149         DPRINTF((BSL_META_U(unit,
   3150                             "5464_link_down: port=%d activated\n"),
   3151                  port));
   3152                     
   3153         /* Reset the chip, then restore user settings. */
   3154 
   3155         SOC_IF_ERROR_RETURN
   3156             (_phy_5464_reset_setup(unit, port));
   3157 
   3158         SOC_IF_ERROR_RETURN
   3159             (_phy_5464_active_medium_config_update(unit, port,
   3160                                             PHY_COPPER_MODE(unit, port) ?
   3161                                             &pc->copper :
   3162                                             &pc->fiber));
   3163     }
   3164 
   3165     return SOC_E_NONE;
   3166 }
   3167 #else
   3168 #define phy_5464_link_down NULL
   3169 #endif /* LINK_DOWN_RESET_WAR */
   3170 
   3171 #ifdef BROADCOM_DEBUG
   3172 
   3173 /*
   3174  * Function:
   3175  *      phy_5464_shadow_dump
   3176  * Purpose:
   3177  *      Debug routine to dump all shadow registers.
   3178  * Parameters:
   3179  *      unit - StrataSwitch unit #.
   3180  *      port - StrataSwitch port #.
   3181  */
   3182 
   3183 void
   3184 phy_5464_shadow_dump(int unit, soc_port_t port)
   3185 {
   3186     uint16      tmp;
   3187     int         i;
   3188     phy_ctrl_t *pc;
   3189 
   3190     pc       = EXT_PHY_SW_STATE(unit, port);
   3191 
   3192     /* Register 0x18 Shadows */
   3193     for (i = 0; i <= 7; i++) {
   3194         WRITE_PHY_REG(unit, pc, 0x18, (i << 12) | 0x7);
   3195         READ_PHY_REG(unit, pc, 0x18, &tmp);
   3196         if ((tmp & ~7) == 0x0000) {
   3197             continue;
   3198         }
   3199         LOG_CLI((BSL_META_U(unit,
   3200                             "0x18[0x%x]=0x%04x\n"), i, tmp));
   3201     }
   3202 
   3203     /* Register 0x1c Shadows */
   3204     for (i = 0; i <= 0x1f; i++) {
   3205         WRITE_PHY_REG(unit, pc, 0x1c, i << 10);
   3206         READ_PHY_REG(unit, pc, 0x1c, &tmp);
   3207         if ((tmp & ~0x7c00) == 0x0000) {
   3208             continue;
   3209         }
   3210         LOG_CLI((BSL_META_U(unit,
   3211                             "0x1c[0x%x]=0x%04x\n"), i, tmp));
   3212     }
   3213 
   3214     /* Register 0x17/0x15 Shadows */
   3215     for (i = 0; i <= 0x13; i++) {
   3216         WRITE_PHY_REG(unit, pc, 0x17, 0xf00 | i);
   3217         READ_PHY_REG(unit, pc, 0x15, &tmp);
   3218         if (tmp  == 0x0000) {
   3219             continue;
   3220         }
   3221         LOG_CLI((BSL_META_U(unit,
   3222                             "0x17[0x%x]=0x%04x\n"), i, tmp));
   3223     }
   3224 }
   3225 
   3226 #endif /* BROADCOM_DEBUG */
   3227 
   3228 /*
   3229  * Variable:    phy_5464drv_ge
   3230  * Purpose:     PHY driver for 5464
   3231  */
   3232 
   3233 phy_driver_t phy_5464drv_ge = {
   3234     "546X/548X Gigabit PHY Driver",
   3235     phy_5464_init,
   3236     phy_fe_ge_reset,
   3237     phy_5464_link_get,
   3238     phy_5464_enable_set,
   3239     phy_5464_enable_get,
   3240     phy_5464_duplex_set,
   3241     phy_5464_duplex_get,
   3242     phy_5464_speed_set,
   3243     phy_5464_speed_get,
   3244     phy_5464_master_set,
   3245     phy_5464_master_get,
   3246     phy_5464_autoneg_set,
   3247     phy_5464_autoneg_get,
   3248     phy_5464_adv_local_set,
   3249     phy_5464_adv_local_get,
   3250     phy_5464_adv_remote_get,
   3251     phy_5464_lb_set,
   3252     phy_5464_lb_get,
   3253     phy_5464_interface_set,
   3254     phy_5464_interface_get,
   3255     phy_5464_ability_get,
   3256     NULL,                       /* Link up event */
   3257     phy_5464_link_down,
   3258     phy_5464_mdix_set,
   3259     phy_5464_mdix_get,
   3260     phy_5464_mdix_status_get,
   3261     phy_5464_medium_config_set,
   3262     phy_5464_medium_config_get,
   3263     phy_5464_medium_status,
   3264     phy_5464_cable_diag,
   3265     NULL,                       /* phy_link_change */
   3266     phy_5464_control_set,       /* phy_control_set */ 
   3267     phy_5464_control_get,       /* phy_control_get */
   3268     phy_ge_reg_read,
   3269     phy_ge_reg_write,
   3270     phy_ge_reg_modify,
   3271     NULL                        /* Phy notify event */   
   3272 };
   3273 
   3274 #else /* INCLUDE_PHY_5464_ESW */
   3275 int _phy_5464_not_empty;
   3276 #endif /* INCLUDE_PHY_5464_ESW */
   3277