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

phy5421S.c (62838B)


      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:        phy5421S.c
      8  *
      9  * Please see sdk/doc/combo-ports.txt for more information.
     10  *
     11  * The BCM5421S connects to virtually any MAC interface and dynamically
     12  * supports both fiber and copper line interfaces.
     13  *
     14  * This driver allows the StrataSwitch to talk to the 5421S using either
     15  * the GMII interface or TBI (Ten Bit Interface).  Upon initialization,
     16  * one interface or the other is statically configured based on the
     17  * if_tbi_portXX property.  The MDIO must be connected for either mode.
     18  * The default is GMII and is recommended for both fiber and copper.
     19  *
     20  * When used with BCM5690, this driver switches to SGMII mode.  There is
     21  * also a special check in phy5690.c to enable autonegotiation for
     22  * SGMII.  WARNING: this configuration is not represented in any
     23  * Broadcom SDK, so proper operation cannot be guaranteed.
     24  *
     25  * This driver allows the 5421S to use either the copper or fiber line
     26  * interface.  If the phy_automedium property is true, the mode is
     27  * selected dynamically on link up using the energy/signal detect.
     28  * If both copper and fiber signals are detected, the driver will
     29  * consult the configuration property phy_fiber_pref<_portXX>.  It will
     30  * select fiber if the property is 1, and copper if it is 0.  The
     31  * default value of the property is 1, since fiber is more likely to be
     32  * preferred.
     33  *
     34  * If the phy_automedium property is false, then the mode is fixed
     35  * according to the phy_fiber_pref<_portXX> property.
     36  *
     37  * This driver does not support autonegotiation with TBI+copper.
     38  *
     39  * If LINK_DOWN_RESET_WAR is 1, then the PHY will be reset each time
     40  * link down is detected on copper.  BCM5421SA2 has difficulty obtaining
     41  * link with certain other gigabit PHYs if not reset when link goes down
     42  * (see errata).
     43  *
     44  * Fiber de-glitch: some GBICs may cause a brief fiber energy detect
     45  * when inserted, even without a link.  This could cause the copper link
     46  * to be dropped, so an energy detect de-glitch is provided.  The
     47  * property phy_fiber_deglitch_usecs is the de-glitch time in usec.
     48  *
     49  * Autoneg defaults may be different for copper and fiber.  The routines
     50  * phy_5421S_medium_config_set change the default port modes for a
     51  * specific medium type.  The default port modes are installed as soon
     52  * as possible after signal detect indicates a change of medium.  The
     53  * standard PHY set/get routines operate only on the currently active
     54  * medium.
     55  */
     56 
     57 #ifdef INCLUDE_PHY_5421S
     58 
     59 #include <sal/types.h>
     60 #include <sal/core/spl.h>
     61 #include <shared/bsl.h>
     62 #include <soc/drv.h>
     63 #include <soc/debug.h>
     64 #include <soc/error.h>
     65 #include <soc/phy.h>
     66 #include <soc/phyreg.h>
     67 
     68 #include <soc/phy.h>
     69 #include <soc/phy/phyctrl.h>
     70 #include <soc/phy/drv.h>
     71 #include "phyident.h"
     72 #include "phyreg.h"
     73 #include "phyfege.h"
     74 #include "phy5421S.h"
     75 
     76 #ifndef INCLUDE_PHY_SERDES
     77 #error "SERDES support required."
     78 #endif
     79 
     80 #define LINK_DOWN_RESET_WAR        1    /* Needed for A0, A1, A2 */
     81 #define REGULATOR_GAMMA_WAR        1    /* Needed if using external regulators */
     82 #define SERDES_COPPER_AMP_WAR        0    /* Needed for A0 only, NOT A1/A2 */
     83 #define ADC_PERF_WAR                1    /* Needed for A0, A1, A2 */
     84 
     85 #define DPRINTF(_s)             LOG_INFO(BSL_LS_SOC_PHY, _s)
     86 
     87 #define GMII_MODE(unit, port)        (!PHY_TBI_MODE(unit, port))
     88 
     89 #define ADVERT_ALL_COPPER \
     90         (SOC_PM_PAUSE | SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB)
     91 #define ADVERT_ALL_FIBER \
     92         (SOC_PM_PAUSE | SOC_PM_1000MB_FD)
     93 
     94 /*
     95  * See comments above about fiber energy de-glitch.  De-glitch time is
     96  * specified in microseconds but precision is limited to the linkscan
     97  * period.  A de-glitch time of 0 disables de-glitching.
     98  */
     99 
    100 STATIC int phy_5421S_interface_set(int unit, soc_port_t port,
    101                                    soc_port_if_t pif);
    102 
    103 STATIC int _phy_5421S_medium_config_update(int unit, soc_port_t port,
    104                                            soc_phy_config_t *cfg);
    105 
    106 STATIC int phy_5421S_duplex_set(int unit, soc_port_t port, int duplex);
    107 STATIC int phy_5421S_speed_set(int unit, soc_port_t port, int speed);
    108 STATIC int phy_5421S_master_set(int unit, soc_port_t port, int master);
    109 STATIC int phy_5421S_adv_local_set(int unit, soc_port_t port,
    110                                    soc_port_mode_t mode);
    111 STATIC int phy_5421S_autoneg_set(int unit, soc_port_t port, int an);
    112 
    113 STATIC int phy_5421S_link_down(int unit, soc_port_t port);
    114 STATIC int phy_5421S_mdix_set(int unit, soc_port_t port, soc_port_mdix_t mode);
    115 
    116 /*
    117  * Function:
    118  *        _phy_5421S_medium_check
    119  * Purpose:
    120  *        Returns preferred medium based on current signal detects
    121  * Parameters:
    122  *        unit - StrataSwitch unit #.
    123  *        port - StrataSwitch port #. 
    124  *        medium - (OUT) SOC_PORT_MEDIUM_XXX
    125  * Returns:        
    126  *        SOC_E_XXX
    127  * Notes:
    128  *        Optionally implements fiber signal de-glitch.
    129  *        Uses the preferred medium property and mode enables,
    130  *        combined with the energy signals that are present.
    131  */
    132 
    133 STATIC int
    134 _phy_5421S_medium_check(int unit, soc_port_t port, int *medium)
    135 {
    136     phy_ctrl_t     *pc = EXT_PHY_SW_STATE(unit, port);
    137     uint16          tmp;
    138     int             fiber_detect, copper_detect;
    139     int             elapsed_time;
    140 
    141     /*
    142      * If medium automedium is not disabled, ignore the energy signals
    143      * and return a fixed medium type according to phy_fiber_pref.
    144      */
    145 
    146     if (!pc->automedium) {
    147         if (pc->fiber.preferred) {
    148             *medium = SOC_PORT_MEDIUM_FIBER;
    149         } else {
    150             *medium = SOC_PORT_MEDIUM_COPPER;
    151         }
    152 
    153         return SOC_E_NONE;
    154     }
    155 
    156     *medium = SOC_PORT_MEDIUM_NONE;
    157 
    158     SOC_IF_ERROR_RETURN
    159         (READ_PHY5421_SGMII_1000X_STATr(unit, pc, &tmp));
    160 
    161     copper_detect = pc->copper.enable && ((tmp & 0x8000) != 0);
    162     fiber_detect  = pc->fiber.enable  && ((tmp & 0x2000) != 0);
    163 
    164     /* De-glitch fiber detect */
    165 
    166     if (!fiber_detect) {
    167         pc->fiber_plug_wait = 0;
    168     } else if (!pc->fiber_plug_wait) {
    169         /* Fiber just now detected; start timer how long to ignore it */
    170         if (pc->fiber_plug_dely > 0) {
    171             pc->fiber_plug_wait = 1;
    172             pc->fiber_plug_time = sal_time_usecs();
    173             pc->fiber_plug_done = 0;
    174             fiber_detect = FALSE;
    175         }
    176     } else if (!pc->fiber_plug_done) {
    177         /* Ignoring fiber until timer expiry */
    178         elapsed_time = SAL_USECS_SUB(sal_time_usecs(), pc->fiber_plug_time);
    179         if ((elapsed_time < 0) ||
    180             (elapsed_time >= pc->fiber_plug_dely)) {
    181             pc->fiber_plug_done = 1;
    182         } else {
    183             fiber_detect = FALSE;
    184         }
    185     }
    186 
    187     /* Detect which medium should be active, if any */
    188 
    189     if (fiber_detect && copper_detect) {
    190         *medium = (pc->fiber.preferred ?
    191                    SOC_PORT_MEDIUM_FIBER :
    192                    SOC_PORT_MEDIUM_COPPER);
    193     } else if (fiber_detect) {
    194         *medium = SOC_PORT_MEDIUM_FIBER;
    195     } else if (copper_detect) {
    196         *medium = SOC_PORT_MEDIUM_COPPER;
    197     } else {
    198         *medium = SOC_PORT_MEDIUM_NONE;
    199     }
    200 
    201     return SOC_E_NONE;
    202 }
    203 
    204 /*
    205  * Function:
    206  *        _phy_5421S_medium_select
    207  * Purpose:
    208  *        Program the PHY to copper or fiber mode based on specified medium.
    209  * Parameters:
    210  *        unit - StrataSwitch unit #.
    211  *        port - StrataSwitch port #. 
    212  *        medium - One of:
    213  *                SOC_PORT_MEDIUM_COPPER
    214  *                SOC_PORT_MEDIUM_FIBER
    215  *                SOC_PORT_MEDIUM_NONE
    216  * Returns:        
    217  *        SOC_E_XXX
    218  */
    219 
    220 STATIC int
    221 _phy_5421S_medium_select(int unit, soc_port_t port, int medium)
    222 {
    223     phy_ctrl_t     *pc = EXT_PHY_SW_STATE(unit, port);
    224     uint16          tmp;
    225     int             allow_mode_switch;
    226 
    227     DPRINTF((BSL_META_U(unit,
    228                         "5421S_medium_select: port=%d medium=%d fp=%d\n"),
    229              port, medium, pc->fiber.preferred));
    230 
    231     /*
    232      * Inform outside world of the current mode, including phy5690.c for
    233      * notify routines and bcm/link.c for printing the link type.
    234      */
    235 
    236     if (medium == SOC_PORT_MEDIUM_FIBER) {
    237         PHY_FLAGS_CLR(unit, port, PHY_FLAGS_COPPER);
    238         PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
    239     } else {
    240         PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER);
    241         PHY_FLAGS_SET(unit, port, PHY_FLAGS_COPPER);
    242     }
    243 
    244     /*
    245      * Program medium.  If no neither medium is active, put the PHY in
    246      * fiber mode because it's lower power.
    247      */
    248 
    249     if (medium == SOC_PORT_MEDIUM_COPPER) {
    250         /*
    251          * Clear SGMII enable when using GMII, as is the case for
    252          * StrataSwitches, and set it when using SGMII, which is the
    253          * case for BCM5690 only (shadow 010, power control).
    254          */
    255         tmp = 0;
    256         SOC_IF_ERROR_RETURN
    257             (MODIFY_PHY5421_POWER_CTRLr(unit, pc, tmp, 0x0800));
    258 
    259         /* Set full duplex */
    260         SOC_IF_ERROR_RETURN
    261             (MODIFY_PHY5421_MII_CTRLr(unit, pc, MII_CTRL_FD, MII_CTRL_FD));
    262 
    263         if (PHY_TBI_MODE(unit, port)) {
    264             /* Partial TBI+COPPER support - ANCTL.ANEN must be 0 */
    265             phy_ctrl_t  *int_pc;
    266 
    267             int_pc = INT_PHY_SW_STATE(unit, port);
    268             SOC_IF_ERROR_RETURN
    269                 (PHYCTRL_AUTO_NEGOTIATE_SET(int_pc, unit, port, FALSE));
    270         }
    271     } else {        /* SOC_PORT_MEDIUM_FIBER/NONE */
    272         /* Set SGMII enable (shadow 010, power control) */
    273         SOC_IF_ERROR_RETURN
    274             (MODIFY_PHY5421_POWER_CTRLr(unit, pc, 0x0800, 0x0800));
    275 
    276 
    277         /*
    278          * Set Fiber mode (shadow 00100, spare control register 2).
    279          * This can remain even in copper mode.  However, it may be set
    280          * ONLY after setting SGMII enable or link will be messed up.
    281          */
    282         SOC_IF_ERROR_RETURN
    283             (MODIFY_PHY5421_SPARE_CTRL2r(unit, pc, 0x0020, 0x0020));
    284     }
    285 
    286     /*
    287      * Decide whether to enable the non-selected medium to send link
    288      * pulses, which could cause certain partners to assume link is up.
    289      * Link pulses should ONLY be sent on the non-selected medium if it
    290      * is the preferred medium, or if neither medium has signal.
    291      */
    292 
    293     switch (medium) {
    294     case SOC_PORT_MEDIUM_FIBER:
    295         allow_mode_switch = !pc->fiber.preferred;
    296         break;
    297     case SOC_PORT_MEDIUM_COPPER:
    298         allow_mode_switch = pc->fiber.preferred;
    299         break;
    300     default:
    301         allow_mode_switch = TRUE;
    302         break;
    303     }
    304 
    305     /* Spare Control Register 3 */
    306     if (allow_mode_switch) {
    307         tmp = 0;                /* Enable pulses on non-selected medium */
    308     } else {
    309         tmp = 0x08;                /* Disable pulses on non-selected medium */
    310     }
    311     SOC_IF_ERROR_RETURN
    312         (MODIFY_PHY5421_SPARE_CTRL3r(unit, pc, tmp, 0x0008));
    313 
    314     /*
    315      * Before link comes up, set the PHY parameters including autoneg
    316      * enable and advertisement, according to saved defaults for this
    317      * port type (copper/fiber).
    318      */
    319 
    320     if (medium == SOC_PORT_MEDIUM_COPPER) {
    321         SOC_IF_ERROR_RETURN
    322             (_phy_5421S_medium_config_update(unit, port, &pc->copper));
    323     }
    324 
    325     if (medium == SOC_PORT_MEDIUM_FIBER) {
    326         SOC_IF_ERROR_RETURN
    327             (_phy_5421S_medium_config_update(unit, port, &pc->fiber));
    328     }
    329 
    330     return SOC_E_NONE;
    331 }
    332 
    333 /*
    334  * Function:
    335  *        phy_5421S_medium_status
    336  * Purpose:
    337  *        Indicate the current active medium
    338  * Parameters:
    339  *        unit - StrataSwitch unit #.
    340  *        port - StrataSwitch port #. 
    341  *        medium - (OUT) One of:
    342  *                SOC_PORT_MEDIUM_COPPER
    343  *                SOC_PORT_MEDIUM_FIBER
    344  *                SOC_PORT_MEDIUM_NONE
    345  * Returns:        
    346  *        SOC_E_NONE
    347  */
    348 
    349 STATIC int
    350 phy_5421S_medium_status(int unit, soc_port_t port, soc_port_medium_t *medium)
    351 {
    352     *medium = EXT_PHY_SW_STATE(unit, port)->medium;
    353     return SOC_E_NONE;
    354 }
    355 
    356 /*
    357  * Function:
    358  *        _phy_5421S_medium_config_update
    359  * Purpose:
    360  *        Update the PHY with config parameters
    361  * Parameters:
    362  *        unit - StrataSwitch unit #.
    363  *        port - StrataSwitch port #. 
    364  *        cfg - Config structure.
    365  * Returns:        
    366  *        SOC_E_XXX
    367  */
    368 
    369 STATIC int
    370 _phy_5421S_medium_config_update(int unit, soc_port_t port,
    371                                 soc_phy_config_t *cfg)
    372 {
    373     SOC_IF_ERROR_RETURN
    374         (phy_5421S_speed_set(unit, port, cfg->force_speed));
    375     SOC_IF_ERROR_RETURN
    376         (phy_5421S_duplex_set(unit, port, cfg->force_duplex));
    377     SOC_IF_ERROR_RETURN
    378         (phy_5421S_master_set(unit, port, cfg->master));
    379     SOC_IF_ERROR_RETURN
    380         (phy_5421S_adv_local_set(unit, port, cfg->autoneg_advert));
    381     SOC_IF_ERROR_RETURN
    382         (phy_5421S_autoneg_set(unit, port, cfg->autoneg_enable));
    383     SOC_IF_ERROR_RETURN
    384         (phy_5421S_mdix_set(unit, port, cfg->mdix));
    385 
    386     return SOC_E_NONE;
    387 }
    388 
    389 /*
    390  * Function:
    391  *        phy_5421S_medium_config_set
    392  * Purpose:
    393  *        Set the operating parameters that are automatically selected
    394  *        when medium switches type.
    395  * Parameters:
    396  *        unit - StrataSwitch unit #
    397  *        port - Port number
    398  *        medium - SOC_PORT_MEDIUM_COPPER/FIBER
    399  *        cfg - Operating parameters
    400  * Returns:
    401  *        SOC_E_XXX
    402  */
    403 
    404 STATIC int
    405 phy_5421S_medium_config_set(int unit, soc_port_t port, 
    406                             soc_port_medium_t medium,
    407                             soc_phy_config_t *cfg)
    408 {
    409     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
    410 
    411     /*
    412      * Changes take effect immediately if the target medium is active.
    413      */
    414 
    415     if (medium == SOC_PORT_MEDIUM_COPPER) {
    416         if (!pc->automedium && !PHY_COPPER_MODE(unit, port)) {
    417             return SOC_E_UNAVAIL;
    418         }
    419         sal_memcpy(&pc->copper, cfg, sizeof (pc->copper));
    420 
    421         pc->copper.autoneg_advert &= ADVERT_ALL_COPPER;
    422 
    423         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
    424             SOC_IF_ERROR_RETURN
    425                 (_phy_5421S_medium_config_update(unit, port, &pc->copper));
    426         }
    427     } else if (medium == SOC_PORT_MEDIUM_FIBER) {
    428         if (!pc->automedium && !PHY_FIBER_MODE(unit, port)) {
    429             return SOC_E_UNAVAIL;
    430         }
    431         sal_memcpy(&pc->fiber, cfg, sizeof (pc->fiber));
    432 
    433         pc->fiber.autoneg_advert &= ADVERT_ALL_FIBER;
    434 
    435         if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
    436             SOC_IF_ERROR_RETURN
    437                 (_phy_5421S_medium_config_update(unit, port, &pc->fiber));
    438         }
    439     } else {
    440         return SOC_E_PARAM;
    441     }
    442 
    443     return SOC_E_NONE;
    444 }
    445 
    446 /*
    447  * Function:
    448  *        phy_5421S_medium_config_get
    449  * Purpose:
    450  *        Get the operating parameters that are automatically selected
    451  *        when medium switches type.
    452  * Parameters:
    453  *        unit - StrataSwitch unit #
    454  *        port - Port number
    455  *        medium - SOC_PORT_MEDIUM_COPPER/FIBER
    456  *        cfg - (OUT) Operating parameters
    457  * Returns:
    458  *        SOC_E_XXX
    459  */
    460 
    461 STATIC int
    462 phy_5421S_medium_config_get(int unit, soc_port_t port, 
    463                             soc_port_medium_t medium,
    464                             soc_phy_config_t *cfg)
    465 {
    466     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
    467 
    468     if (medium == SOC_PORT_MEDIUM_COPPER) {
    469         if (!pc->automedium && !PHY_COPPER_MODE(unit, port)) {
    470             return SOC_E_UNAVAIL;
    471         }
    472         sal_memcpy(cfg, &pc->copper, sizeof (*cfg));
    473     } else if (medium == SOC_PORT_MEDIUM_FIBER) {
    474         if (!pc->automedium && !PHY_FIBER_MODE(unit, port)) {
    475             return SOC_E_UNAVAIL;
    476         }
    477         sal_memcpy(cfg, &pc->fiber, sizeof (*cfg));
    478     } else {
    479         return SOC_E_PARAM;
    480     }
    481 
    482     return SOC_E_NONE;
    483 }
    484 
    485 /*
    486  * Function:         
    487  *        _phy_5421S_reset_setup
    488  * Purpose:        
    489  *        Function to reset the PHY and set up initial operating registers.
    490  * Parameters:
    491  *        unit - StrataSwitch unit #.
    492  *        port - StrataSwitch port #. 
    493  * Returns:        
    494  *        SOC_E_XXX
    495  */
    496 
    497 STATIC int
    498 _phy_5421S_reset_setup(int unit, soc_port_t port)
    499 {
    500     uint16          tmp;
    501     soc_port_if_t   pif;
    502     int             rv;
    503     phy_ctrl_t     *pc;
    504     phy_ctrl_t     *int_pc;
    505 
    506     pc     = EXT_PHY_SW_STATE(unit, port);
    507     int_pc = INT_PHY_SW_STATE(unit, port);
    508 
    509     DPRINTF((BSL_META_U(unit,
    510                         "_phy_5421S_reset_setup: u=%d p=%d\n"),
    511              unit, port));
    512 
    513     /* Reset PHY */
    514     SOC_IF_ERROR_RETURN(soc_phy_reset(unit, port));
    515 
    516     /* Enable SGMII registers */
    517     SOC_IF_ERROR_RETURN
    518         (WRITE_PHY5421_SGMII_REG_ENBLEr(unit, pc, 0xfc01));
    519 
    520     /* Put chip in TBI or SGMII mode (retain current PHY_FLAG_10B setting) */
    521     if (PHY_TBI_MODE(unit, port)) {
    522         pif = SOC_PORT_IF_TBI;
    523     } else {
    524         pif = SOC_PORT_IF_GMII;
    525     }
    526 
    527     SOC_IF_ERROR_RETURN(phy_5421S_interface_set(unit, port, pif));
    528 
    529     /* Initialize the switch MAC PCS in case TBI mode is used now or later */
    530     rv = PHYCTRL_INIT(int_pc, unit, port);
    531     /* If the SOC chip does not support TBI, and the system is      */
    532     /* configured for TBI, return the error code. If the system     */
    533     /* is not configured for TBI, ignore the phy_serdes_init error  */
    534     /* and save the TBI capability status for use elsewhere.        */
    535     if (rv < 0) { 
    536         EXT_PHY_SW_STATE(unit, port)->tbi_capable = FALSE;
    537         if (PHY_TBI_MODE(unit, port)) {
    538             
    539 #if 0 /* Should we take the PHY out of TBI mode? */ 
    540             SOC_IF_ERROR_RETURN(phy_5421S_interface_set(unit, port, 
    541                                                         SOC_PORT_IF_GMII)); 
    542 #endif
    543             return (rv);
    544         }
    545     }
    546 
    547 #if SERDES_COPPER_AMP_WAR
    548     /*
    549      * Supply Sensitivity Errata for BCM5421SA0; reduces amplitude out
    550      * of compliance if used on BCM5421SA1 or later parts.
    551      */
    552     SOC_IF_ERROR_RETURN
    553         (phy_reg_ge_write(unit, pc, 0x00, 0x0000, 0x18, 0x0c00));
    554     SOC_IF_ERROR_RETURN
    555         (phy_reg_ge_write(unit, pc, 0x00, 0x003e, 0x15, 0x0380));
    556 #endif
    557 
    558 #if ADC_PERF_WAR
    559     /*
    560      * Proprietary fix for unusual board/cabling arrangement.
    561      * Can compromise cable length performance if used unnecessarily.
    562      */
    563     SOC_IF_ERROR_RETURN
    564         (phy_reg_ge_write(unit, pc, 0x00, 0x0000, 0x18, 0x0c00));
    565     SOC_IF_ERROR_RETURN
    566         (phy_reg_ge_write(unit, pc, 0x00, 0x201f, 0x15, 0x2aaa));
    567 #endif
    568 
    569 #if REGULATOR_GAMMA_WAR
    570     /*
    571      * Gamma fix needed if not using internal regulators;
    572      * does not hurt if used either way.
    573      */
    574     SOC_IF_ERROR_RETURN
    575         (phy_reg_ge_write(unit, pc, 0x00, 0x0000, 0x18, 0x0c00));
    576     SOC_IF_ERROR_RETURN
    577         (phy_reg_ge_write(unit, pc, 0x00, 0x000a, 0x15, 0x0323));
    578 #endif
    579 
    580     /* Clear RGMII enable (shadow 111, misc control) */
    581     SOC_IF_ERROR_RETURN
    582         (MODIFY_PHY5421_MISC_CTRLr(unit, pc, 0, 0x0080));
    583 
    584     /*
    585      * Configure Spare Control Register 3.
    586      * Enabling LEDs to work in fiber mode is important because this
    587      * signal is used to indicate link in _phy_5421S_link_gmii_fiber().
    588      */
    589     tmp  = 0x20;        /* Enable signal/energy detect interrupt */
    590     tmp |= 0x40;        /* Enable LEDs to work in fiber mode */
    591     SOC_IF_ERROR_RETURN
    592         (MODIFY_PHY5421_SPARE_CTRL3r(unit, pc, tmp, 0x0060));
    593 
    594     /* Configure Extended Control Register */
    595     tmp  = 0x0020;        /* Enable LEDs to indicate traffic status */
    596     tmp |= 0x0001;        /* Set high FIFO elasticity to support jumbo frames */
    597     SOC_IF_ERROR_RETURN
    598         (MODIFY_PHY5421_MII_ECRr(unit, pc, tmp, 0x0021));
    599 
    600     /* Enable extended packet length (4.5k through 25k) */
    601     SOC_IF_ERROR_RETURN
    602         (MODIFY_PHY5421_MISC_CTRLr(unit, pc, 0x4000, 0x4000));
    603 
    604     return(SOC_E_NONE);
    605 }
    606 
    607 /*
    608  * Function:         
    609  *        phy_5421S_init
    610  * Purpose:        
    611  *        Init function for 5421S PHY.
    612  * Parameters:
    613  *        unit - StrataSwitch unit #.
    614  *        port - StrataSwitch port #. 
    615  * Returns:        
    616  *        SOC_E_XXX
    617  */
    618 
    619 STATIC int
    620 phy_5421S_init(int unit, soc_port_t port)
    621 {
    622     phy_ctrl_t     *pc;
    623     int             fiber_preferred;
    624 
    625     DPRINTF((BSL_META_U(unit,
    626                         "phy_5421S_init: u=%d p=%d\n"),
    627              unit, port));
    628 
    629     pc = EXT_PHY_SW_STATE(unit, port);
    630 
    631     /* phyident.c has already set PHY_FLAGS_10B if required */
    632 
    633     PHY_FLAGS_SET(unit, port, PHY_FLAGS_5421S);
    634 
    635     /*
    636      * Set the default settings for copper and fiber.  The upper layer
    637      * application can change these using phy_5421S_medium_config_set.
    638      *
    639      * The settings are also updated by individual calls such as
    640      * phy_5421S_adv_local_set, phy_5421S_speed_set, etc.  In this case,
    641      * they operate on the currently-selected medium type.
    642      *
    643      * The property phy_fiber_pref_port<X> is used to set a preference
    644      * for fiber mode in the event both copper and fiber links are up.
    645      * Default is to prefer fiber.
    646      */
    647 
    648     pc->automedium =
    649         soc_property_port_get(unit, port,
    650                               spn_PHY_AUTOMEDIUM, 1);
    651 
    652     fiber_preferred =
    653         soc_property_port_get(unit, port, spn_PHY_FIBER_PREF, 1);
    654 
    655     pc->fiber_plug_dely =
    656         soc_property_port_get(unit, port,
    657                               spn_PHY_FIBER_DEGLITCH_USECS, 0);
    658 
    659     pc->copper.enable = TRUE;
    660     pc->copper.preferred = !fiber_preferred;
    661     pc->copper.autoneg_enable = TRUE;
    662     pc->copper.autoneg_advert = ADVERT_ALL_COPPER;
    663     pc->copper.force_speed = 1000;
    664     pc->copper.force_duplex = TRUE;
    665     pc->copper.master = SOC_PORT_MS_AUTO;
    666     pc->copper.mdix = SOC_PORT_MDIX_AUTO;
    667 
    668     pc->fiber.enable = TRUE;
    669     pc->fiber.preferred = fiber_preferred;
    670     pc->fiber.autoneg_enable = !PHY_TBI_MODE(unit, port);
    671     pc->fiber.autoneg_advert = ADVERT_ALL_FIBER;
    672     pc->fiber.force_speed = 1000;
    673     pc->fiber.force_duplex = TRUE;
    674     pc->fiber.master = SOC_PORT_MS_NONE;
    675     pc->fiber.mdix = SOC_PORT_MDIX_NORMAL;
    676 
    677     /* This PHY is TBI capable (although the SOC */
    678     /* chip may ultimately turn out not to be).  */
    679     pc->tbi_capable = TRUE; 
    680 
    681     /* Start with no medium */
    682 
    683     pc->medium = SOC_PORT_MEDIUM_NONE;
    684 
    685     /* Hardware initialization */
    686 
    687     SOC_IF_ERROR_RETURN(_phy_5421S_reset_setup(unit, port));
    688 
    689     return SOC_E_NONE;
    690 }
    691 
    692 /*
    693  * Function:         
    694  *        phy_5421S_enable_set
    695  * Purpose:        
    696  *        Enable or disable the physical interface.
    697  * Parameters:
    698  *        unit - StrataSwitch unit #.
    699  *        port - StrataSwitch port #. 
    700  *        enable - Boolean, true = enable PHY, false = disable.
    701  * Returns:        
    702  *        SOC_E_XXX
    703  * Notes:
    704  *        This is a software enable/disable for the entire PHY.
    705  *        See phy_fe_ge_enable_set for an explanation.
    706  */
    707 
    708 STATIC int
    709 phy_5421S_enable_set(int unit, soc_port_t port, int enable)
    710 {
    711     if (GMII_MODE(unit, port)) {
    712         if (enable) {
    713             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE);
    714         } else {
    715             PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE);
    716         }
    717         return SOC_E_NONE;
    718     } else {
    719         phy_ctrl_t *int_pc;
    720 
    721         int_pc = INT_PHY_SW_STATE(unit, port);
    722         return PHYCTRL_ENABLE_SET(int_pc, unit, port, enable);
    723     }
    724 }
    725 
    726 /*
    727  * Function:         
    728  *        phy_5421S_enable_get
    729  * Purpose:        
    730  *        Enable or disable the physical interface for a 5421S device.
    731  * Parameters:
    732  *        unit - StrataSwitch unit #.
    733  *        port - StrataSwitch port #. 
    734  *        enable - (OUT) Boolean, true = enable PHY, false = disable.
    735  * Returns:        
    736  *        SOC_E_XXX
    737  */
    738 
    739 STATIC int
    740 phy_5421S_enable_get(int unit, soc_port_t port, int *enable)
    741 {
    742     if (GMII_MODE(unit, port)) {
    743         *enable = !PHY_FLAGS_TST(unit, port, PHY_FLAGS_DISABLE);
    744         return SOC_E_NONE;
    745     } else {
    746         phy_ctrl_t *int_pc;
    747 
    748         int_pc = INT_PHY_SW_STATE(unit, port);
    749         return PHYCTRL_ENABLE_GET(int_pc, unit, port, enable);
    750     }
    751 }
    752 
    753 /*
    754  * Function:
    755  *        _phy_5421S_link_gmii_fiber (internal)
    756  * Purpose:
    757  *        Determine the current link up/down status for a 5421S device when
    758  *        operating in GMII+FIBER mode.
    759  * Parameters:
    760  *      unit - StrataSwitch unit #.
    761  *      port - StrataSwitch port #.
    762  *      link - (OUT) Boolean, true indicates link established.
    763  * Returns:
    764  *        SOC_E_XXX
    765  */
    766 STATIC int
    767 _phy_5421S_link_gmii_fiber(int unit, soc_port_t port, int *link)
    768 {
    769     uint16          tmp;
    770     int             autoneg_enabled, autoneg_complete;
    771     int             autoneg_local_fault, autoneg_remote_fault;
    772     int             led_link;
    773     phy_ctrl_t    *pc = EXT_PHY_SW_STATE(unit, port);
    774 
    775     /*
    776      * Read LED link status (shadow 01000, LED status register).
    777      * This works because the init routine has configured the Fiber LED
    778      * mode.  It provides real time indication of true link, whether or
    779      * not autonegotiation is enabled.
    780      */
    781     SOC_IF_ERROR_RETURN
    782         (READ_PHY5421_LED_STATr(unit, pc, &tmp));
    783 
    784     led_link = ((tmp & 0x20) == 0);
    785 
    786     SOC_IF_ERROR_RETURN
    787         (READ_PHY5421_SGMII_1000X_CTRLr(unit, pc, &tmp));
    788 
    789     autoneg_enabled = ((tmp & 0x4) == 0);
    790 
    791     if (!autoneg_enabled) {
    792         /* If autonegotiation is off, go by LED signal */
    793         *link = led_link;
    794         return SOC_E_NONE;
    795     }
    796 
    797     SOC_IF_ERROR_RETURN
    798         (READ_PHY5421_SGMII_1000X_STATr(unit, pc, &tmp));
    799 
    800     autoneg_complete = ((tmp & 0x2) != 0);
    801 
    802     if (!autoneg_complete) {
    803         *link = FALSE;
    804         return SOC_E_NONE;
    805     }
    806 
    807     autoneg_local_fault = ((tmp & 1) != 0);        /* Autoneg Error */
    808 
    809     if (autoneg_local_fault) {
    810         autoneg_remote_fault = 0;
    811     } else {
    812         SOC_IF_ERROR_RETURN
    813             (READ_PHY5421_1000X_ANPr(unit, pc, &tmp));
    814         autoneg_remote_fault = ((tmp & 0x60) != 0);
    815     }
    816 
    817     if (autoneg_local_fault || autoneg_remote_fault) {
    818         /* Restart autonegotiation */
    819         SOC_IF_ERROR_RETURN
    820             (WRITE_PHY5421_SGMII_1000X_MISCr(unit, pc, 0x0001));
    821         *link = FALSE;
    822         return SOC_E_NONE;
    823     }
    824 
    825     *link = TRUE;
    826 
    827     return SOC_E_NONE;
    828 }
    829 
    830 /*
    831  * Function:         
    832  *        phy_5421S_link_get
    833  * Purpose:        
    834  *        Determine the current link up/down status for a 5421S device.
    835  * Parameters:
    836  *        unit - StrataSwitch unit #.
    837  *        port - StrataSwitch port #. 
    838  *        link - (OUT) Boolean, true indicates link established.
    839  * Returns:        
    840  *        SOC_E_XXX
    841  * Notes: 
    842  *        No synchronization performed at this level.
    843  */
    844 
    845 STATIC int
    846 phy_5421S_link_get(int unit, soc_port_t port, int *link)
    847 {
    848     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
    849     int                medium;
    850 
    851     SOC_IF_ERROR_RETURN(_phy_5421S_medium_check(unit, port, &medium));
    852 
    853     if (medium != pc->medium) {
    854         PHY_FLAGS_SET(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
    855         DPRINTF((BSL_META_U(unit,
    856                             "phy_5421S_link_get: u=%d p=%d: medium %d-->%d\n"),
    857                  unit, port, pc->medium, medium));
    858 
    859         pc->medium = medium;
    860 
    861         SOC_IF_ERROR_RETURN(_phy_5421S_medium_select(unit, port, medium));
    862 
    863         /* Ensure linkscan sees link down at least once */
    864 
    865         *link = FALSE;
    866         return SOC_E_NONE;
    867     } else {
    868         PHY_FLAGS_CLR(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
    869     }
    870 
    871     /*
    872      * Use the appropriate method to check for link up.
    873      */
    874 
    875     if (GMII_MODE(unit, port)) {
    876         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
    877             return phy_fe_ge_link_get(unit, port, link);
    878         } else if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
    879             return _phy_5421S_link_gmii_fiber(unit, port, link);
    880         } else {
    881             *link = FALSE;
    882             return SOC_E_NONE;
    883         }
    884     } else {
    885         phy_ctrl_t *int_pc;
    886 
    887         int_pc = INT_PHY_SW_STATE(unit, port);
    888         return PHYCTRL_LINK_GET(int_pc, unit, port, link);
    889     }
    890 }
    891 
    892 /*
    893  * Function:         
    894  *        phy_5421S_duplex_set
    895  * Purpose:        
    896  *        Set the current duplex mode (forced).
    897  * Parameters:
    898  *        unit - StrataSwitch unit #.
    899  *        port - StrataSwitch port #. 
    900  *        duplex - Boolean, true indicates full duplex, false indicates half.
    901  * Returns:        
    902  *        SOC_E_NONE
    903  *        SOC_E_UNAVAIL - Half duplex requested, and not supported.
    904  *        SOC_E_XXX
    905  * Notes: 
    906  *        The duplex is set only for the ACTIVE medium.
    907  *        No synchronization performed at this level.
    908  *        Autonegotiation is not manipulated.
    909  */
    910 
    911 STATIC int
    912 phy_5421S_duplex_set(int unit, soc_port_t port, int duplex)
    913 {
    914     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
    915 
    916     DPRINTF((BSL_META_U(unit,
    917                         "phy_5421S_duplex_set: u=%d p=%d d=%d m=%d\n"),
    918              unit, port, duplex, pc->medium));
    919 
    920     if (GMII_MODE(unit, port)) {
    921         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
    922             SOC_IF_ERROR_RETURN(phy_fe_ge_duplex_set(unit, port, duplex));
    923             pc->copper.force_duplex = duplex;
    924         }
    925 
    926         if (pc->medium == SOC_PORT_MEDIUM_FIBER && !duplex) {
    927             return SOC_E_UNAVAIL;
    928         }
    929     } else {
    930         phy_ctrl_t *int_pc;
    931 
    932         int_pc = INT_PHY_SW_STATE(unit, port);
    933 
    934         SOC_IF_ERROR_RETURN
    935             (PHYCTRL_DUPLEX_SET(int_pc, unit, port, duplex));
    936 
    937         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
    938             pc->copper.force_duplex = duplex;
    939         } 
    940 
    941         if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
    942             pc->fiber.force_duplex = duplex;
    943         }
    944     }
    945 
    946     return SOC_E_NONE;
    947 }
    948 
    949 /*
    950  * Function:         
    951  *        phy_5421S_duplex_get
    952  * Purpose:        
    953  *        Get the current operating duplex mode. If autoneg is enabled, 
    954  *        then operating mode is returned, otherwise forced mode is returned.
    955  * Parameters:
    956  *        unit - StrataSwitch unit #.
    957  *        port - StrataSwitch port #. 
    958  *        duplex - (OUT) Boolean, true indicates full duplex, false 
    959  *                indicates half.
    960  * Returns:        
    961  *        SOC_E_NONE
    962  * Notes: 
    963  *        The duplex is retrieved for the ACTIVE medium.
    964  *        No synchronization performed at this level. Autonegotiation is 
    965  *        not manipulated. 
    966  */
    967 
    968 STATIC int
    969 phy_5421S_duplex_get(int unit, soc_port_t port, int *duplex)
    970 {
    971     phy_ctrl_t        *pc;
    972 
    973     if (GMII_MODE(unit, port)) {
    974         pc = EXT_PHY_SW_STATE(unit, port);
    975         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
    976             SOC_IF_ERROR_RETURN(phy_fe_ge_duplex_get(unit, port, duplex));
    977         } else {
    978             *duplex = TRUE;
    979         }
    980         return SOC_E_NONE;
    981     } else {
    982         pc = INT_PHY_SW_STATE(unit, port);
    983 
    984         return PHYCTRL_DUPLEX_GET(pc, unit, port, duplex);
    985     }
    986 }
    987 
    988 /*
    989  * Function:         
    990  *        phy_5421S_speed_set
    991  * Purpose:        
    992  *        Set the current operating speed (forced).
    993  * Parameters:
    994  *        unit - StrataSwitch unit #.
    995  *        port - StrataSwitch port #. 
    996  *        speed - Requested speed, only 1000 supported.
    997  * Returns:        
    998  *        SOC_E_XXX
    999  * Notes:
   1000  *        The speed is set only for the ACTIVE medium.
   1001  */
   1002 
   1003 STATIC int
   1004 phy_5421S_speed_set(int unit, soc_port_t port, int speed)
   1005 {
   1006     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
   1007 
   1008     DPRINTF((BSL_META_U(unit,
   1009                         "phy_5421S_speed_set: u=%d p=%d s=%d m=%d\n"),
   1010              unit, port, speed, pc->medium));
   1011 
   1012     if (GMII_MODE(unit, port)) {
   1013         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1014             SOC_IF_ERROR_RETURN(phy_fe_ge_speed_set(unit, port, speed));
   1015             pc->copper.force_speed = speed;
   1016         }
   1017 
   1018         if (pc->medium == SOC_PORT_MEDIUM_FIBER && speed != 1000) {
   1019             return SOC_E_CONFIG;
   1020         }
   1021     } else {
   1022         phy_ctrl_t  *int_pc;
   1023 
   1024         int_pc = INT_PHY_SW_STATE(unit, port);
   1025 
   1026         SOC_IF_ERROR_RETURN
   1027             (PHYCTRL_SPEED_SET(int_pc, unit, port, speed));
   1028         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1029             pc->copper.force_speed = speed;
   1030         }
   1031 
   1032         if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
   1033             pc->fiber.force_speed = speed;
   1034         }
   1035     }
   1036 
   1037     return SOC_E_NONE;
   1038 }
   1039 
   1040 /*
   1041  * Function:         
   1042  *        phy_5421S_speed_get
   1043  * Purpose:        
   1044  *        Get the current operating speed for a 5421S device.
   1045  * Parameters:
   1046  *        unit - StrataSwitch unit #.
   1047  *        port - StrataSwitch port #. 
   1048  *        speed - (OUT) Boolean, true indicates full duplex, false 
   1049  *                indicates half.
   1050  * Returns:        
   1051  *        SOC_E_XXX
   1052  * Notes:
   1053  *        The speed is retrieved for the ACTIVE medium, 0 if none.
   1054  */
   1055 
   1056 STATIC int
   1057 phy_5421S_speed_get(int unit, soc_port_t port, int *speed)
   1058 {
   1059     phy_ctrl_t        *pc;
   1060 
   1061     if (GMII_MODE(unit, port)) {
   1062         pc = EXT_PHY_SW_STATE(unit, port);
   1063         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1064             SOC_IF_ERROR_RETURN(phy_fe_ge_speed_get(unit, port, speed));
   1065         } else if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
   1066             *speed = 1000;
   1067         } else {
   1068             *speed = 0;
   1069         }
   1070         return SOC_E_NONE;
   1071     } else {
   1072         pc = INT_PHY_SW_STATE(unit, port);
   1073         return PHYCTRL_SPEED_GET(pc, unit, port, speed);
   1074     }
   1075 }
   1076 
   1077 /*
   1078  * Function:         
   1079  *        phy_5421S_master_set
   1080  * Purpose:        
   1081  *        Set the current master mode
   1082  * Parameters:
   1083  *        unit - StrataSwitch unit #.
   1084  *        port - StrataSwitch port #. 
   1085  *        master - SOC_PORT_MS_*
   1086  * Returns:        
   1087  *        SOC_E_XXX
   1088  * Notes:
   1089  *        The master mode is set only for the ACTIVE medium.
   1090  */
   1091 
   1092 STATIC int
   1093 phy_5421S_master_set(int unit, soc_port_t port, int master)
   1094 {
   1095     phy_ctrl_t        *pc;
   1096 
   1097     if (GMII_MODE(unit, port)) {
   1098         pc = EXT_PHY_SW_STATE(unit, port);
   1099         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1100             SOC_IF_ERROR_RETURN(phy_fe_ge_master_set(unit, port, master));
   1101             pc->copper.master = master;
   1102         }
   1103     } else {
   1104         pc = INT_PHY_SW_STATE(unit, port);
   1105 
   1106         return (PHYCTRL_MASTER_SET(pc, unit, port, master));
   1107     }
   1108 
   1109     return SOC_E_NONE;
   1110 }
   1111 
   1112 /*
   1113  * Function:         
   1114  *        phy_5421S_master_get
   1115  * Purpose:        
   1116  *        Get the current master mode for a 5421S device.
   1117  * Parameters:
   1118  *        unit - StrataSwitch unit #.
   1119  *        port - StrataSwitch port #. 
   1120  *        master - (OUT) SOC_PORT_MS_*
   1121  * Returns:        
   1122  *        SOC_E_XXX
   1123  * Notes:
   1124  *        The master mode is retrieved for the ACTIVE medium.
   1125  */
   1126 
   1127 STATIC int
   1128 phy_5421S_master_get(int unit, soc_port_t port, int *master)
   1129 {
   1130     phy_ctrl_t        *pc;
   1131 
   1132     if (GMII_MODE(unit, port)) {
   1133         pc = EXT_PHY_SW_STATE(unit, port);
   1134         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1135             SOC_IF_ERROR_RETURN(phy_fe_ge_master_get(unit, port, master));
   1136         } else {
   1137             *master = SOC_PORT_MS_NONE;
   1138         }
   1139     } else {
   1140         pc = INT_PHY_SW_STATE(unit, port);
   1141 
   1142         return (PHYCTRL_MASTER_GET(pc, unit, port, master));
   1143     }
   1144 
   1145     return SOC_E_NONE;
   1146 }
   1147 
   1148 /*
   1149  * Function:         
   1150  *        phy_5421S_autoneg_set
   1151  * Purpose:        
   1152  *        Enable or disable auto-negotiation on the specified port.
   1153  * Parameters:
   1154  *        unit - StrataSwitch unit #.
   1155  *        port - StrataSwitch port #. 
   1156  *        an   - Boolean, if true, auto-negotiation is enabled 
   1157  *                (and/or restarted). If false, autonegotiation is disabled.
   1158  * Returns:        
   1159  *        SOC_E_XXX
   1160  * Notes:
   1161  *        The autoneg mode is set only for the ACTIVE medium.
   1162  */
   1163 
   1164 STATIC int
   1165 phy_5421S_autoneg_set(int unit, soc_port_t port, int autoneg)
   1166 {
   1167     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
   1168 
   1169     DPRINTF((BSL_META_U(unit,
   1170                         "phy_5421S_autoneg_set: u=%d p=%d autoneg=%d m=%d\n"),
   1171              unit, port, autoneg, pc->medium));
   1172 
   1173     if (GMII_MODE(unit, port)) {
   1174         uint16                tmp;
   1175 
   1176         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1177             SOC_IF_ERROR_RETURN(phy_fe_ge_an_set(unit, port, autoneg));
   1178             pc->copper.autoneg_enable = autoneg ? 1 : 0;
   1179         }
   1180 
   1181         if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
   1182             if (autoneg) {
   1183                 tmp = 0;      /* Clear AUTONEG DISABLE */
   1184             } else {
   1185                 tmp = 0x4;    /* Set AUTONEG DISABLE */
   1186             }
   1187 
   1188             SOC_IF_ERROR_RETURN
   1189                 (MODIFY_PHY5421_SGMII_1000X_CTRLr(unit, pc, tmp, 0x0004));
   1190 
   1191             /* Restart autoneg */
   1192             SOC_IF_ERROR_RETURN
   1193                 (WRITE_PHY5421_SGMII_1000X_MISCr(unit, pc, 0x0001));
   1194 
   1195             pc->fiber.autoneg_enable = autoneg ? 1 : 0;
   1196         }
   1197     } else {
   1198         phy_ctrl_t *int_pc;
   1199         int_pc = INT_PHY_SW_STATE(unit, port);
   1200         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1201             /* Partial TBI+COPPER support - ANCTL.ANEN must be 0 */
   1202 
   1203             SOC_IF_ERROR_RETURN
   1204                 (PHYCTRL_AUTO_NEGOTIATE_SET(int_pc, unit, port, FALSE));
   1205 
   1206             pc->copper.autoneg_enable = FALSE;
   1207         }
   1208 
   1209         if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
   1210             SOC_IF_ERROR_RETURN
   1211                 (PHYCTRL_AUTO_NEGOTIATE_SET(int_pc, unit, port, autoneg));
   1212 
   1213             pc->fiber.autoneg_enable = autoneg ? 1 : 0;
   1214         }
   1215     }
   1216 
   1217     return SOC_E_NONE;
   1218 }
   1219 
   1220 /*
   1221  * Function:         
   1222  *        phy_5421S_autoneg_get
   1223  * Purpose:        
   1224  *        Get the current auto-negotiation status (enabled/busy).
   1225  * Parameters:
   1226  *        unit - StrataSwitch unit #.
   1227  *        port - StrataSwitch port #. 
   1228  *        autoneg - (OUT) if true, auto-negotiation is enabled.
   1229  *        autoneg_done - (OUT) if true, auto-negotiation is complete. This
   1230  *                     value is undefined if autoneg == FALSE. 
   1231  * Returns:        
   1232  *        SOC_E_XXX
   1233  * Notes:
   1234  *        The autoneg mode is retrieved for the ACTIVE medium.
   1235  */
   1236 
   1237 STATIC int
   1238 phy_5421S_autoneg_get(int unit, soc_port_t port,
   1239                       int *autoneg, int *autoneg_done)
   1240 {
   1241     phy_ctrl_t        *pc;
   1242 
   1243     if (GMII_MODE(unit, port)) {
   1244         pc = EXT_PHY_SW_STATE(unit, port);
   1245         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1246             SOC_IF_ERROR_RETURN
   1247                 (phy_fe_ge_an_get(unit, port, autoneg, autoneg_done));
   1248         } else if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
   1249             uint16        tmp;
   1250 
   1251             SOC_IF_ERROR_RETURN
   1252                 (READ_PHY5421_SGMII_1000X_CTRLr(unit, pc, &tmp)); 
   1253             *autoneg = ((tmp & 0x4) == 0);      /* AUTONEG DISABLE bit clear */
   1254 
   1255             SOC_IF_ERROR_RETURN
   1256                 (READ_PHY5421_SGMII_1000X_STATr(unit, pc, &tmp));
   1257 
   1258             *autoneg_done = ((tmp & 0x2) != 0);   /* AUTONEG DONE */
   1259         } else {
   1260             *autoneg = FALSE;
   1261             *autoneg_done = FALSE;
   1262         }
   1263     } else {
   1264         pc = INT_PHY_SW_STATE(unit, port);
   1265         SOC_IF_ERROR_RETURN
   1266             (PHYCTRL_AUTO_NEGOTIATE_GET(pc, unit, port, autoneg, autoneg_done));
   1267     }
   1268 
   1269     return SOC_E_NONE;
   1270 }
   1271 
   1272 /*
   1273  * Function:         
   1274  *        phy_5421S_adv_local_set
   1275  * Purpose:        
   1276  *        Set the current advertisement for auto-negotiation.
   1277  * Parameters:
   1278  *        unit - StrataSwitch unit #.
   1279  *        port - StrataSwitch port #. 
   1280  *        mode - Port mode mask indicating supported options/speeds.
   1281  * Returns:        
   1282  *        SOC_E_XXX
   1283  * Notes: 
   1284  *        The advertisement is set only for the ACTIVE medium.
   1285  *        No synchronization performed at this level.
   1286  */
   1287 
   1288 STATIC int
   1289 phy_5421S_adv_local_set(int unit, soc_port_t port, soc_port_mode_t mode)
   1290 {
   1291     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
   1292 
   1293     DPRINTF((BSL_META_U(unit,
   1294                         "phy_5421S_adv_local_set: u=%d p=%d mode=0x%x m=%d\n"),
   1295              unit, port, mode, pc->medium));
   1296 
   1297     if (GMII_MODE(unit, port)) {
   1298         uint16             tmp;
   1299         int                autoneg;
   1300 
   1301         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1302             SOC_IF_ERROR_RETURN(phy_ge_adv_local_set(unit, port, mode));
   1303             pc->copper.autoneg_advert = mode;
   1304         }
   1305 
   1306         if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
   1307 
   1308             SOC_IF_ERROR_RETURN
   1309                 (READ_PHY5421_SGMII_1000X_CTRLr(unit, pc, &tmp));
   1310 
   1311             autoneg = ((tmp & 0x4) == 0);
   1312 
   1313             tmp &= ~0x6000;            /* Transmit "No-Failure" to partner */
   1314 
   1315             switch (mode & SOC_PM_PAUSE) {
   1316             default:
   1317                 tmp &= ~(0x1000 | 0x0800);     /* ASYMM=0 and PAUSE=0 */
   1318                 break;
   1319             case SOC_PM_PAUSE_TX:
   1320                 tmp |= 0x1000;                 /* ASYMM=1 and PAUSE(dir)=0 */
   1321                 tmp &= ~0x0800;
   1322                 break;
   1323             case SOC_PM_PAUSE_RX:
   1324                 tmp |= (0x1000 | 0x0800);      /* ASYMM=1 and PAUSE(dir)=1 */
   1325                 break;
   1326             case SOC_PM_PAUSE_TX | SOC_PM_PAUSE_RX:
   1327                 tmp &= ~0x1000;                /* ASYMM=0 and PAUSE=1 */
   1328                 tmp |= 0x0800;
   1329                 break;
   1330             }
   1331 
   1332             SOC_IF_ERROR_RETURN
   1333                 (WRITE_PHY5421_SGMII_1000X_CTRLr(unit, pc, tmp));
   1334 
   1335             if (autoneg) {
   1336                 /* Restart autonegotiation */
   1337                 SOC_IF_ERROR_RETURN
   1338                     (WRITE_PHY5421_SGMII_1000X_MISCr(unit, pc, 0x0001));
   1339             }
   1340 
   1341             pc->fiber.autoneg_advert = mode;
   1342         }
   1343     } else {
   1344         phy_ctrl_t *int_pc;
   1345 
   1346         int_pc = INT_PHY_SW_STATE(unit, port);
   1347         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1348             SOC_IF_ERROR_RETURN
   1349                 (PHYCTRL_ADV_SET(int_pc, unit, port, mode));
   1350             pc->copper.autoneg_advert = mode;
   1351         }
   1352         if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
   1353             SOC_IF_ERROR_RETURN
   1354                 (PHYCTRL_ADV_SET(int_pc, unit, port, mode));
   1355             pc->fiber.autoneg_advert = mode;
   1356         }
   1357     }
   1358 
   1359     return SOC_E_NONE;
   1360 }
   1361 
   1362 /*
   1363  * Function:         
   1364  *        phy_5421S_adv_local_get
   1365  * Purpose:        
   1366  *        Get the current advertisement for auto-negotiation.
   1367  * Parameters:
   1368  *        unit - StrataSwitch unit #.
   1369  *        port - StrataSwitch port #. 
   1370  *        mode - (OUT) Port mode mask indicating supported options/speeds.
   1371  * Returns:        
   1372  *        SOC_E_XXX
   1373  * Notes: 
   1374  *        The advertisement is retrieved for the ACTIVE medium.
   1375  *        No synchronization performed at this level.
   1376  */
   1377 
   1378 STATIC int
   1379 phy_5421S_adv_local_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   1380 {
   1381     phy_ctrl_t        *pc;
   1382 
   1383     if (GMII_MODE(unit, port)) {
   1384         pc = EXT_PHY_SW_STATE(unit, port);
   1385         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1386             SOC_IF_ERROR_RETURN(phy_ge_adv_local_get(unit, port, mode));
   1387         } else if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
   1388             uint16        tmp;
   1389 
   1390             SOC_IF_ERROR_RETURN
   1391                 (READ_PHY5421_SGMII_1000X_CTRLr(unit, pc, &tmp));
   1392 
   1393             *mode = SOC_PM_1000MB_FD;
   1394 
   1395             switch (tmp & 0x1800) {
   1396             case 0x0000:
   1397                 break;
   1398             case 0x0800:
   1399                 *mode |= SOC_PM_PAUSE_TX | SOC_PM_PAUSE_RX;
   1400                 break;
   1401             case 0x1000:
   1402                 *mode |= SOC_PM_PAUSE_TX;
   1403                 break;
   1404             case 0x1800:
   1405                 *mode |= SOC_PM_PAUSE_RX;
   1406                 break;
   1407             }
   1408         } else {
   1409             *mode = 0;
   1410         }
   1411     } else {
   1412         pc = INT_PHY_SW_STATE(unit, port);
   1413         SOC_IF_ERROR_RETURN
   1414             (PHYCTRL_ADV_GET(pc, unit, port, mode));
   1415     }
   1416 
   1417     return SOC_E_NONE;
   1418 }
   1419 
   1420 /*
   1421  * Function:         
   1422  *        phy_5421S_adv_remote_get
   1423  * Purpose:        
   1424  *        Get partner's current advertisement for auto-negotiation.
   1425  * Parameters:
   1426  *        unit - StrataSwitch unit #.
   1427  *        port - StrataSwitch port #. 
   1428  *        mode - (OUT) Port mode mask indicating supported options/speeds.
   1429  * Returns:        
   1430  *        SOC_E_XXX
   1431  * Notes: 
   1432  *        The remote advertisement is retrieved for the ACTIVE medium.
   1433  *        No synchronization performed at this level. If Autonegotiation is
   1434  *        disabled or in progress, this routine will return an error.
   1435  */
   1436 
   1437 STATIC int
   1438 phy_5421S_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   1439 {
   1440     phy_ctrl_t        *pc;
   1441 
   1442     if (GMII_MODE(unit, port)) {
   1443         pc = EXT_PHY_SW_STATE(unit, port);
   1444         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1445             SOC_IF_ERROR_RETURN(phy_ge_adv_remote_get(unit, port, mode));
   1446         } else if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
   1447             uint16        tmp;
   1448             int           link;
   1449 
   1450             *mode = 0;
   1451 
   1452             SOC_IF_ERROR_RETURN
   1453                 (READ_PHY5421_SGMII_1000X_CTRLr(unit, pc, &tmp));
   1454 
   1455             if (tmp & 0x4) {
   1456                 return SOC_E_DISABLED;
   1457             }
   1458 
   1459             SOC_IF_ERROR_RETURN
   1460                 (_phy_5421S_link_gmii_fiber(unit, port, &link));
   1461 
   1462             if (!link) {
   1463                 return SOC_E_BUSY;
   1464             }
   1465 
   1466             SOC_IF_ERROR_RETURN
   1467                 (READ_PHY5421_1000X_ANPr(unit, pc, &tmp)); 
   1468 
   1469             if (tmp & 0x10) {
   1470                 *mode |= SOC_PM_PAUSE_ASYMM;
   1471             }
   1472 
   1473             if (tmp & 0x8) {
   1474                 *mode |= SOC_PM_PAUSE;
   1475             }
   1476 
   1477             if (tmp & 0x4) {
   1478                 *mode |= SOC_PM_1000MB_HD;
   1479             }
   1480 
   1481             if (tmp & 0x2) {
   1482                 *mode |= SOC_PM_1000MB_FD;
   1483             }
   1484         } else {
   1485             *mode = 0;
   1486         }
   1487     } else {
   1488         pc = INT_PHY_SW_STATE(unit, port);
   1489         return (PHYCTRL_ADV_REMOTE_GET(pc, unit, port, mode));
   1490 
   1491     }
   1492 
   1493     return SOC_E_NONE;
   1494 }
   1495 
   1496 /*
   1497  * Function:         
   1498  *        phy_5421S_lb_set
   1499  * Purpose:        
   1500  *        Set the local PHY loopback mode.
   1501  * Parameters:
   1502  *        unit - StrataSwitch unit #.
   1503  *        port - StrataSwitch port #. 
   1504  *        loopback - Boolean: true = enable loopback, false = disable.
   1505  * Returns:        
   1506  *        SOC_E_XXX
   1507  * Notes: 
   1508  *        The loopback mode is set only for the ACTIVE medium.
   1509  *        No synchronization performed at this level.
   1510  */
   1511 
   1512 STATIC int
   1513 phy_5421S_lb_set(int unit, soc_port_t port, int enable)
   1514 {
   1515     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
   1516 
   1517     DPRINTF((BSL_META_U(unit,
   1518                         "phy_5421S_lb_set: u=%d p=%d en=%d m=%d\n"),
   1519              unit, port, enable, pc->medium));
   1520 
   1521     if (GMII_MODE(unit, port) && pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1522         SOC_IF_ERROR_RETURN(phy_fe_ge_lb_set(unit, port, enable));
   1523     } else {
   1524         uint16                tmp;
   1525 
   1526         SOC_IF_ERROR_RETURN
   1527             (READ_PHY5421_SGMII_1000X_CTRLr(unit, pc, &tmp));
   1528 
   1529         if (enable) {
   1530             tmp |= 1;
   1531         } else {
   1532             tmp &= ~1;
   1533         }
   1534 
   1535         SOC_IF_ERROR_RETURN
   1536             (WRITE_PHY5421_SGMII_1000X_CTRLr(unit, pc, tmp));
   1537     }
   1538 
   1539     return SOC_E_NONE;
   1540 }
   1541 
   1542 /*
   1543  * Function:         
   1544  *        phy_5421S_lb_get
   1545  * Purpose:        
   1546  *        Get the local PHY loopback mode.
   1547  * Parameters:
   1548  *        unit - StrataSwitch unit #.
   1549  *        port - StrataSwitch port #. 
   1550  *        loopback - (OUT) Boolean: true = enable loopback, false = disable.
   1551  * Returns:        
   1552  *        SOC_E_XXX
   1553  * Notes:
   1554  *        The loopback mode is retrieved for the ACTIVE medium.
   1555  */
   1556 
   1557 STATIC int
   1558 phy_5421S_lb_get(int unit, soc_port_t port, int *enable)
   1559 {
   1560     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
   1561 
   1562     if (GMII_MODE(unit, port) && pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1563         SOC_IF_ERROR_RETURN(phy_fe_ge_lb_get(unit, port, enable));
   1564     } else {
   1565         uint16                tmp;
   1566 
   1567         SOC_IF_ERROR_RETURN
   1568             (READ_PHY5421_SGMII_1000X_CTRLr(unit, pc, &tmp));
   1569 
   1570         *enable = ((tmp & 1)) != 0;
   1571     }
   1572 
   1573     return SOC_E_NONE;
   1574 }
   1575 
   1576 /*
   1577  * Function:
   1578  *        phy_5421S_interface_set
   1579  * Purpose:
   1580  *        Set the current operating mode of the PHY (TBI or MII/GMII)
   1581  * Parameters:
   1582  *        unit - StrataSwitch unit #.
   1583  *        port - StrataSwitch port #. 
   1584  *        pif - One of SOC_PORT_IF_*
   1585  * Returns:
   1586  *        SOC_E_XXX
   1587  */
   1588 
   1589 STATIC int
   1590 phy_5421S_interface_set(int unit, soc_port_t port, soc_port_if_t pif)
   1591 {
   1592 #if defined(BCM_ESW_SUPPORT)
   1593     uint32              gpcsc;
   1594 #endif    
   1595     uint16              mii_ecr;
   1596     int                 tbi;
   1597     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
   1598 
   1599     DPRINTF((BSL_META_U(unit,
   1600                         "phy_5421S_interface_set: u=%d p=%d pif=%d\n"),
   1601              unit, port, pif));
   1602 
   1603     /*
   1604      * On SDKs with 5421S daughterboards, for even N, the port N EWRAP
   1605      * line is used to select TBI mode on port N and N+1, and port N+1
   1606      * EWRAP is ignored.  MII management registers are used to select
   1607      * loopback.
   1608      */
   1609 
   1610     tbi = (pif == SOC_PORT_IF_TBI);
   1611 
   1612     if (tbi) {
   1613         if (EXT_PHY_SW_STATE(unit, port)->tbi_capable == TRUE) {
   1614             PHY_FLAGS_SET(unit, port, PHY_FLAGS_10B);
   1615         } else {
   1616             return SOC_E_UNAVAIL;
   1617         }
   1618     } else {
   1619         PHY_FLAGS_CLR(unit, port, PHY_FLAGS_10B);
   1620     }
   1621 
   1622 #if defined(BCM_ESW_SUPPORT)
   1623     if (SOC_IS_ESW(unit)) {
   1624        SOC_IF_ERROR_RETURN(READ_GPCSCr(unit, port, &gpcsc));
   1625        soc_reg_field_set(unit, GPCSCr, &gpcsc, EWRAPf, tbi);
   1626        SOC_IF_ERROR_RETURN(WRITE_GPCSCr(unit, port, gpcsc));
   1627     }
   1628 #endif /* BCM_ESW_SUPPORT */
   1629     /*
   1630      * There is a strange "TBI Toggle" bit in this PHY that must be
   1631      * written if the PHY comes up in GMII mode.  The initial mode is
   1632      * latched from a pin and may vary depending if link is present.
   1633      */
   1634 
   1635     SOC_IF_ERROR_RETURN
   1636         (READ_PHY5421_MII_ECRr(unit, pc, &mii_ecr));
   1637 
   1638     DPRINTF((BSL_META_U(unit,
   1639                         "5421S_if_set: port=%d tbi=%d reg 0x10=0x%x\n"),
   1640              port, tbi, mii_ecr));
   1641 
   1642     if ((tbi && !(mii_ecr & MII_ECR_10B)) ||
   1643         (!tbi && (mii_ecr & MII_ECR_10B))) {
   1644 
   1645         /* Update ECR */
   1646         mii_ecr ^= MII_ECR_10B;
   1647 
   1648         DPRINTF((BSL_META_U(unit,
   1649                             "5421S_if_set: port=%d set reg 0x10=0x%x "
   1650                             "and toggle TBI\n"),
   1651                  port, mii_ecr));
   1652 
   1653         SOC_IF_ERROR_RETURN
   1654             (WRITE_PHY5421_MII_ECRr(unit, pc, mii_ecr));
   1655 
   1656         /* Write 1 to TBI Toggle (shadow 00100, spare control register 2) */
   1657         SOC_IF_ERROR_RETURN
   1658             (WRITE_PHY5421_SPARE_CTRL2r(unit, pc, 0x0040));
   1659     }
   1660 
   1661     return SOC_E_NONE;
   1662 }
   1663 
   1664 /*
   1665  * Function:
   1666  *        phy_5421S_interface_get
   1667  * Purpose:
   1668  *        Get the current operating mode of the PHY (TBI or GMII).
   1669  * Parameters:
   1670  *        unit - StrataSwitch unit #.
   1671  *        port - StrataSwitch port #. 
   1672  *        mii - (OUT) TRUE --> MII/GMII, FALSE --> TBI.
   1673  * Returns:
   1674  *        SOC_E_XXX
   1675  */
   1676 
   1677 STATIC int
   1678 phy_5421S_interface_get(int unit, soc_port_t port, soc_port_if_t *pif)
   1679 {
   1680     if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_10B)) {
   1681         *pif = SOC_PORT_IF_TBI;
   1682     } else {
   1683         *pif = SOC_PORT_IF_GMII;
   1684     }
   1685 
   1686     return SOC_E_NONE;
   1687 }
   1688 
   1689 /*
   1690  * Function:         
   1691  *        phy_5421S_ability_get
   1692  * Purpose:        
   1693  *        Get the abilities of the local gigabit PHY.
   1694  * Parameters:
   1695  *        unit - StrataSwitch unit #.
   1696  *        port - StrataSwitch port #. 
   1697  *        mode - (OUT) Port mode mask indicating supported options/speeds.
   1698  * Returns:        
   1699  *        SOC_E_NONE
   1700  * Notes:
   1701  *        The ability is retrieved only for the ACTIVE medium.
   1702  */
   1703 
   1704 STATIC int 
   1705 phy_5421S_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode)
   1706 {
   1707     phy_ctrl_t        *pc;
   1708 
   1709     /*
   1710      * This should return both GMII and TBI abilities, but with the
   1711      * current code that causes problems in _bcm_port_mode_setup().
   1712      */
   1713 
   1714     if (GMII_MODE(unit, port)) {
   1715         pc = EXT_PHY_SW_STATE(unit, port);
   1716         if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1717             SOC_IF_ERROR_RETURN(phy_ge_ability_get(unit, port, mode));
   1718         } else if (pc->medium == SOC_PORT_MEDIUM_FIBER) {
   1719             *mode = (SOC_PM_AN | SOC_PM_LB_PHY | 
   1720                      SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM |
   1721                      SOC_PM_GMII | SOC_PM_1000MB_FD);
   1722         } else {
   1723             *mode = SOC_PM_GMII;
   1724         }
   1725     } else {
   1726         pc = INT_PHY_SW_STATE(unit, port);
   1727         SOC_IF_ERROR_RETURN
   1728             (PHYCTRL_ABILITY_GET(pc, unit, port, mode));
   1729 
   1730     }
   1731 
   1732     if (pc->automedium) {
   1733         *mode |= SOC_PM_COMBO;
   1734     }
   1735 
   1736     if (PHY_TBI_MODE(unit, port)) {
   1737         *mode |= SOC_PM_TBI;
   1738     }
   1739 
   1740     DPRINTF((BSL_META_U(unit,
   1741                         "phy_5421S_ability_get: u=%d p=%d mode=0x%x\n"),
   1742              unit, port, *mode));
   1743 
   1744     return(SOC_E_NONE);
   1745 }
   1746 
   1747 /*
   1748  * Function:
   1749  *        phy_5421S_mdix_set
   1750  * Description:
   1751  *        Set the Auto-MDIX mode of a port/PHY
   1752  * Parameters:
   1753  *        unit - Device number
   1754  *        port - Port number
   1755  *        mode - One of:
   1756  *                BCM_PORT_MDIX_AUTO
   1757  *                        Enable auto-MDIX when autonegotiation is enabled
   1758  *                BCM_PORT_MDIX_FORCE_AUTO
   1759  *                        Enable auto-MDIX always
   1760  *                BCM_PORT_MDIX_NORMAL
   1761  *                        Disable auto-MDIX
   1762  *                BCM_PORT_MDIX_XOVER
   1763  *                        Disable auto-MDIX, and swap cable pairs
   1764  * Return Value:
   1765  *        SOC_E_UNAVAIL - feature unsupported by hardware
   1766  *        SOC_E_XXX - other error
   1767  */
   1768 STATIC int
   1769 phy_5421S_mdix_set(int unit, soc_port_t port, soc_port_mdix_t mode)
   1770 {
   1771     phy_ctrl_t         *pc = EXT_PHY_SW_STATE(unit, port);
   1772     int                 speed;
   1773     int                 rv = SOC_E_NONE;
   1774 
   1775     if (pc == NULL) {
   1776         return SOC_E_PARAM;
   1777     }
   1778 
   1779     switch (pc->medium) {
   1780     case SOC_PORT_MEDIUM_FIBER:
   1781         if (mode != SOC_PORT_MDIX_NORMAL) {
   1782             rv = SOC_E_UNAVAIL;
   1783         }
   1784 
   1785         if (rv == SOC_E_NONE) {
   1786             pc->fiber.mdix = mode;
   1787         }
   1788         break;
   1789 
   1790     case SOC_PORT_MEDIUM_COPPER:
   1791         switch (mode) {
   1792         case SOC_PORT_MDIX_AUTO:
   1793             /* Clear bit 14 for automatic MDI crossover */
   1794             SOC_IF_ERROR_RETURN  
   1795                 (MODIFY_PHY5421_MII_ECRr(unit, pc, 0, 0x4000));
   1796             break;
   1797 
   1798         case SOC_PORT_MDIX_FORCE_AUTO:
   1799             rv = SOC_E_UNAVAIL;
   1800             break;
   1801 
   1802         case SOC_PORT_MDIX_NORMAL:
   1803             SOC_IF_ERROR_RETURN(phy_5421S_speed_get(unit, port, &speed));
   1804             if (speed == 10 || speed == 100) {
   1805                 /* Set bit 14 for manual MDI crossover */
   1806                 SOC_IF_ERROR_RETURN  
   1807                     (MODIFY_PHY5421_MII_ECRr(unit, pc, 0x4000, 0x4000));
   1808 
   1809                 SOC_IF_ERROR_RETURN
   1810                     (WRITE_PHY5421_TEST1r(unit, pc, 0x0000));
   1811             } else {
   1812                 return SOC_E_UNAVAIL;
   1813             }
   1814             break;
   1815 
   1816         case SOC_PORT_MDIX_XOVER:
   1817             SOC_IF_ERROR_RETURN(phy_5421S_speed_get(unit, port, &speed));
   1818             if (speed == 10 || speed == 100) {
   1819                 /* Set bit 14 for manual MDI crossover */
   1820                  SOC_IF_ERROR_RETURN  
   1821                     (MODIFY_PHY5421_MII_ECRr(unit, pc, 0x4000, 0x4000));
   1822 
   1823                 SOC_IF_ERROR_RETURN
   1824                     (WRITE_PHY5421_TEST1r(unit, pc, 0x0080));
   1825             } else {
   1826                 return SOC_E_PARAM;
   1827             }
   1828             break;
   1829         default:
   1830             rv = SOC_E_UNAVAIL;
   1831         }
   1832 
   1833         if (rv == SOC_E_NONE) {
   1834             pc->copper.mdix = mode;
   1835         }
   1836 
   1837         break;
   1838 
   1839     default:
   1840         if (mode != SOC_PORT_MDIX_NORMAL) {
   1841             rv = SOC_E_UNAVAIL;
   1842         }
   1843         break;
   1844     }
   1845 
   1846     return (rv);
   1847 }        
   1848 
   1849 /*
   1850  * Function:
   1851  *        phy_5421S_mdix_get
   1852  * Description:
   1853  *        Get the Auto-MDIX mode of a port/PHY
   1854  * Parameters:
   1855  *        unit - Device number
   1856  *        port - Port number
   1857  *        mode - (Out) One of:
   1858  *                BCM_PORT_MDIX_AUTO
   1859  *                        Enable auto-MDIX when autonegotiation is enabled
   1860  *                BCM_PORT_MDIX_FORCE_AUTO
   1861  *                        Enable auto-MDIX always
   1862  *                BCM_PORT_MDIX_NORMAL
   1863  *                        Disable auto-MDIX
   1864  *                BCM_PORT_MDIX_XOVER
   1865  *                        Disable auto-MDIX, and swap cable pairs
   1866  * Return Value:
   1867  *        SOC_E_UNAVAIL - feature unsupported by hardware
   1868  *        SOC_E_XXX - other error
   1869  */
   1870 STATIC int
   1871 phy_5421S_mdix_get(int unit, soc_port_t port, soc_port_mdix_t *mode)
   1872 {
   1873     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
   1874 
   1875     if (mode == NULL || pc == NULL) {
   1876         return SOC_E_PARAM;
   1877     }
   1878 
   1879     if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1880         *mode = pc->copper.mdix;
   1881     } else {
   1882         *mode = SOC_PORT_MDIX_NORMAL;
   1883     }
   1884 
   1885     return SOC_E_NONE;
   1886 }    
   1887 
   1888 /*
   1889  * Function:
   1890  *        phy_5421S_mdix_status_get
   1891  * Description:
   1892  *        Get the current MDIX status on a port/PHY
   1893  * Parameters:
   1894  *        unit    - Device number
   1895  *        port    - Port number
   1896  *        status  - (OUT) One of:
   1897  *                BCM_PORT_MDIX_STATUS_NORMAL
   1898  *                        Straight connection
   1899  *                BCM_PORT_MDIX_STATUS_XOVER
   1900  *                        Crossover has been performed
   1901  * Return Value:
   1902  *        SOC_E_UNAVAIL - feature unsupported by hardware
   1903  *        SOC_E_XXX - other error
   1904  */
   1905 STATIC int
   1906 phy_5421S_mdix_status_get(int unit, soc_port_t port, 
   1907                          soc_port_mdix_status_t *status)
   1908 {
   1909     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
   1910     uint16             tmp;
   1911 
   1912     if (status == NULL || pc == NULL) {
   1913         return SOC_E_PARAM;
   1914     }
   1915 
   1916     if (pc->medium == SOC_PORT_MEDIUM_COPPER) {
   1917         SOC_IF_ERROR_RETURN
   1918             (READ_PHY5421_MII_ESRr(unit, pc, &tmp));
   1919         if (tmp & 0x2000) {
   1920             *status = SOC_PORT_MDIX_STATUS_XOVER;
   1921         } else {
   1922             *status = SOC_PORT_MDIX_STATUS_NORMAL;
   1923         }
   1924     } else {
   1925         *status = SOC_PORT_MDIX_STATUS_NORMAL;
   1926     }
   1927 
   1928     return SOC_E_NONE;
   1929 }    
   1930 
   1931 #if LINK_DOWN_RESET_WAR
   1932 
   1933 /*
   1934  * Function:         
   1935  *        phy_5421S_link_down
   1936  * Purpose:        
   1937  *        Perform reset work-around on link down.
   1938  * Parameters:
   1939  *        unit - StrataSwitch unit #.
   1940  *        port - StrataSwitch port #. 
   1941  * Returns:        
   1942  *        SOC_E_XXX
   1943  * Notes:
   1944  *        This work-around is necessary to ensure interoperability with
   1945  *        different PHYs.
   1946  */
   1947 
   1948 STATIC int 
   1949 phy_5421S_link_down(int unit, soc_port_t port)
   1950 {
   1951     phy_ctrl_t        *pc = EXT_PHY_SW_STATE(unit, port);
   1952     uint16             mii_ctrl, mii_gb_ctrl, mii_advert, mii_fiber;
   1953 
   1954     DPRINTF((BSL_META_U(unit,
   1955                         "5421S_link_down: port=%d m=%d\n"),
   1956              port, pc->medium));
   1957 
   1958     /* Only applies to copper link down */
   1959 
   1960     if (pc->medium != SOC_PORT_MEDIUM_COPPER) {
   1961         return SOC_E_NONE;
   1962     }
   1963 
   1964     /* Save settings managed by the driver. */
   1965     SOC_IF_ERROR_RETURN
   1966         (READ_PHY5421_MII_CTRLr(unit, pc, &mii_ctrl));
   1967     SOC_IF_ERROR_RETURN
   1968         (READ_PHY5421_MII_ANAr(unit, pc, &mii_advert));
   1969     SOC_IF_ERROR_RETURN
   1970         (READ_PHY5421_MII_GB_CTRLr(unit, pc, &mii_gb_ctrl));
   1971     SOC_IF_ERROR_RETURN
   1972         (READ_PHY5421_SGMII_1000X_CTRLr(unit, pc, &mii_fiber));
   1973 
   1974     /* Perform normal reset and initialization. */
   1975 
   1976     SOC_IF_ERROR_RETURN(_phy_5421S_reset_setup(unit, port));
   1977 
   1978     /* Restore settings managed by the driver. */
   1979     SOC_IF_ERROR_RETURN
   1980         (WRITE_PHY5421_MII_CTRLr(unit, pc, mii_ctrl));
   1981     SOC_IF_ERROR_RETURN
   1982         (WRITE_PHY5421_MII_ANAr(unit, pc, mii_advert));
   1983     SOC_IF_ERROR_RETURN
   1984         (WRITE_PHY5421_MII_GB_CTRLr(unit, pc, mii_gb_ctrl));
   1985     SOC_IF_ERROR_RETURN
   1986         (WRITE_PHY5421_SGMII_1000X_CTRLr(unit, pc, mii_fiber));
   1987 
   1988     return SOC_E_NONE;
   1989 }
   1990 
   1991 #else
   1992 #define phy_5421S_link_down NULL
   1993 #endif /* LINK_DOWN_RESET_WAR */
   1994 
   1995 #ifdef BROADCOM_DEBUG
   1996 
   1997 /*
   1998  * Function:
   1999  *        phy_5421S_shadow_dump
   2000  * Purpose:
   2001  *        Debug routine to dump all shadow registers.
   2002  * Parameters:
   2003  *        unit - StrataSwitch unit #.
   2004  *        port - StrataSwitch port #. 
   2005  */
   2006 
   2007 void
   2008 phy_5421S_shadow_dump(int unit, soc_port_t port)
   2009 {
   2010     uint16               tmp;
   2011     phy_ctrl_t          *pc;
   2012 
   2013     pc        = EXT_PHY_SW_STATE(unit, port);
   2014     /* Hideous Register 0x18 */
   2015 
   2016     WRITE_PHY_REG(unit, pc, 0x18, 0x0007);
   2017     READ_PHY_REG(unit, pc, 0x18, &tmp);
   2018     LOG_CLI((BSL_META_U(unit,
   2019                         "0x18[000]=0x%04x\n"), tmp));
   2020     WRITE_PHY_REG(unit, pc, 0x18, 0x1007);
   2021     READ_PHY_REG(unit, pc, 0x18, &tmp);
   2022     LOG_CLI((BSL_META_U(unit,
   2023                         "0x18[001]=0x%04x\n"), tmp));
   2024     WRITE_PHY_REG(unit, pc, 0x18, 0x2007);
   2025     READ_PHY_REG(unit, pc, 0x18, &tmp);
   2026     LOG_CLI((BSL_META_U(unit,
   2027                         "0x18[010]=0x%04x\n"), tmp));
   2028     WRITE_PHY_REG(unit, pc, 0x18, 0x4007);
   2029     READ_PHY_REG(unit, pc, 0x18, &tmp);
   2030     LOG_CLI((BSL_META_U(unit,
   2031                         "0x18[100]=0x%04x\n"), tmp));
   2032     WRITE_PHY_REG(unit, pc, 0x18, 0x7007);
   2033     READ_PHY_REG(unit, pc, 0x18, &tmp);
   2034     LOG_CLI((BSL_META_U(unit,
   2035                         "0x18[111]=0x%04x\n"), tmp));
   2036 
   2037     /* Hideous Register 0x1c */
   2038 
   2039     WRITE_PHY_REG(unit, pc, 0x1c, 0x0800);
   2040     READ_PHY_REG(unit, pc, 0x1c, &tmp);
   2041     LOG_CLI((BSL_META_U(unit,
   2042                         "0x1c[00010]=0x%04x\n"), tmp));
   2043     WRITE_PHY_REG(unit, pc, 0x1c, 0x1000);
   2044     READ_PHY_REG(unit, pc, 0x1c, &tmp);
   2045     LOG_CLI((BSL_META_U(unit,
   2046                         "0x1c[00100]=0x%04x\n"), tmp));
   2047     WRITE_PHY_REG(unit, pc, 0x1c, 0x1400);
   2048     READ_PHY_REG(unit, pc, 0x1c, &tmp);
   2049     LOG_CLI((BSL_META_U(unit,
   2050                         "0x1c[00101]=0x%04x\n"), tmp));
   2051     WRITE_PHY_REG(unit, pc, 0x1c, 0x2000);
   2052     READ_PHY_REG(unit, pc, 0x1c, &tmp);
   2053     LOG_CLI((BSL_META_U(unit,
   2054                         "0x1c[01000]=0x%04x\n"), tmp));
   2055     WRITE_PHY_REG(unit, pc, 0x1c, 0x2400);
   2056     READ_PHY_REG(unit, pc, 0x1c, &tmp);
   2057     LOG_CLI((BSL_META_U(unit,
   2058                         "0x1c[01001]=0x%04x\n"), tmp));
   2059     WRITE_PHY_REG(unit, pc, 0x1c, 0x2800);
   2060     READ_PHY_REG(unit, pc, 0x1c, &tmp);
   2061     LOG_CLI((BSL_META_U(unit,
   2062                         "0x1c[01010]=0x%04x\n"), tmp));
   2063     WRITE_PHY_REG(unit, pc, 0x1c, 0x7c00);
   2064     READ_PHY_REG(unit, pc, 0x1c, &tmp);
   2065     LOG_CLI((BSL_META_U(unit,
   2066                         "0x1c[11111]=0x%04x\n"), tmp));
   2067 }
   2068 
   2069 #endif /* BROADCOM_DEBUG */
   2070 
   2071 
   2072 
   2073 
   2074 /*
   2075  * Variable:        phy_5421Sdrv_ge
   2076  * Purpose:        PHY driver for 5421S
   2077  */
   2078 phy_driver_t phy_5421Sdrv_ge = {
   2079     "5421S Gigabit PHY Driver",
   2080     phy_5421S_init,
   2081     phy_fe_ge_reset,
   2082     phy_5421S_link_get, 
   2083     phy_5421S_enable_set, 
   2084     phy_5421S_enable_get, 
   2085     phy_5421S_duplex_set, 
   2086     phy_5421S_duplex_get, 
   2087     phy_5421S_speed_set, 
   2088     phy_5421S_speed_get, 
   2089     phy_5421S_master_set, 
   2090     phy_5421S_master_get, 
   2091     phy_5421S_autoneg_set, 
   2092     phy_5421S_autoneg_get, 
   2093     phy_5421S_adv_local_set,
   2094     phy_5421S_adv_local_get, 
   2095     phy_5421S_adv_remote_get,
   2096     phy_5421S_lb_set,
   2097     phy_5421S_lb_get,
   2098     phy_5421S_interface_set, 
   2099     phy_5421S_interface_get, 
   2100     phy_5421S_ability_get,
   2101     NULL,                       /* Link up event */
   2102     phy_5421S_link_down,
   2103     phy_5421S_mdix_set,
   2104     phy_5421S_mdix_get,
   2105     phy_5421S_mdix_status_get,
   2106     phy_5421S_medium_config_set,
   2107     phy_5421S_medium_config_get,
   2108     phy_5421S_medium_status,
   2109     NULL,                       /* phy_cable_diag  */
   2110     NULL,                       /* phy_link_change */
   2111     NULL,                       /* phy_control_set */
   2112     NULL,                       /* phy_control_get */
   2113     phy_ge_reg_read,
   2114     phy_ge_reg_write, 
   2115     phy_ge_reg_modify,
   2116     NULL                        /* Phy event notify */
   2117 };
   2118 
   2119 #endif /* INCLUDE_PHY_5421S */
   2120 
   2121 int _soc_phy_5421S_not_empty;