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

phy54640.c (183548B)


      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:        phy54640.c
      8  * Purpose:     PHY driver for BCM54640
      9  *
     10  * Supported BCM54640 PHY:
     11  *
     12  *      Device  Ports   Media              MAC Interface
     13  *      54640   4       Copper, Serdes     SGMII
     14  *
     15  * Workarounds:
     16  *
     17  * References:
     18  * 54640-DS02-R.pdf          BCM54640 Data Sheet
     19  *     
     20  *    +----------+   +---------------------------+
     21  *    |          |   |            +->Copper      |<--> Magnetic
     22  *    |  SGMII   |<->| SGMII    <-+              |
     23  *    | (SerDes) |   |            |              |
     24  *    |          |   |            +->SGMII/      |<--> (PHY)SFP (10/100/1000)
     25  *    +----------+   |               1000BASE-X/ |<--> 1000BASE-X SFP
     26  *        MAC        |               100BASE-FX  |<--> 100BASE-FX SFP
     27  *                   |                           |
     28  *                   |                           |
     29  *                   +---------------------------+
     30  *                               54640
     31  *
     32  * Notes:
     33  */ 
     34 
     35 #include <sal/types.h>
     36 #include <sal/core/spl.h>
     37 #include <shared/bsl.h>
     38 #include <soc/drv.h>
     39 #include <soc/debug.h>
     40 #include <soc/error.h>
     41 #include <soc/phyreg.h>
     42 
     43 #include <soc/phy.h>
     44 #include <soc/phy/phyctrl.h>
     45 #include <soc/phy/drv.h>
     46 
     47 #include "phydefs.h"      /* Must include before other phy related includes */
     48 
     49 #if defined(INCLUDE_PHY_54640) || defined(INCLUDE_PHY_54640E)
     50 #include "phyconfig.h"    /* Must be the first phy include after phydefs.h */
     51 #include "phyident.h"
     52 #include "phyreg.h"
     53 #include "phyfege.h"
     54 #include "phy54640.h"
     55 
     56 #define AUTO_MDIX_WHEN_AN_DIS       0       /* Non-standard-compliant option */
     57 
     58 #define ECD_ENABLE 0
     59 
     60 #define ADVERT_ALL_COPPER \
     61         (SOC_PM_PAUSE | SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB)
     62 #define ADVERT_ALL_FIBER \
     63         (SOC_PM_PAUSE | SOC_PM_1000MB_FD)
     64 
     65 #define PHY_IS_BCM54640E(_pc)   (PHY_MODEL_CHECK((_pc), \
     66                                  PHY_BCM54680E_OUI, \
     67                                  PHY_BCM54680E_MODEL) \
     68                                  && (((_pc)->phy_rev & 0x8) == 0))
     69 
     70 #define PHY_IS_BCM54640E_A0(_pc) (PHY_MODEL_CHECK((_pc), \
     71                                  PHY_BCM54680E_OUI, \
     72                                  PHY_BCM54680E_MODEL) \
     73                                  && ((_pc)->phy_rev == 0x0 ))
     74 
     75 #define PHY_IS_BCM54640E_A1(_pc) (PHY_MODEL_CHECK((_pc), \
     76                                  PHY_BCM54680E_OUI, \
     77                                  PHY_BCM54680E_MODEL) \
     78                                  && ((_pc)->phy_rev == 0x1 ))
     79 
     80 #define PHY_IS_BCM54640E_A0A1(_pc) (PHY_MODEL_CHECK((_pc), \
     81                                  PHY_BCM54680E_OUI, \
     82                                  PHY_BCM54680E_MODEL) \
     83                                  && (((_pc)->phy_rev & 0xe)== 0x0 ))
     84 
     85 #define SOC_IF_ERROR_BREAK(op, __rv__) \
     86         if (((__rv__) = (op)) < 0) {   \
     87             break;                     \
     88         }    
     89 
     90 STATIC int _phy_54640_no_reset_setup(int unit, soc_port_t port);
     91 STATIC int _phy_54640_medium_change(int unit, soc_port_t port, int force_update);
     92 STATIC int phy_54640_duplex_set(int unit, soc_port_t port, int duplex);
     93 STATIC int phy_54640_speed_set(int unit, soc_port_t port, int speed);
     94 STATIC int phy_54640_master_set(int unit, soc_port_t port, int master);
     95 STATIC int phy_54640_ability_advert_set(int unit, soc_port_t port, soc_port_ability_t *ability);
     96 STATIC int phy_54640_ability_local_get(int unit, soc_port_t port, soc_port_ability_t *ability);
     97 STATIC int _phy_54640_ability_cu_local_get(int unit, soc_port_t port, 
     98                                        soc_port_ability_t *ability);
     99 STATIC int _phy_54640_ability_fiber_local_get(int unit, soc_port_t port, 
    100                                        soc_port_ability_t *ability);
    101 STATIC int phy_54640_autoneg_set(int unit, soc_port_t port, int autoneg);
    102 STATIC int phy_54640_mdix_set(int unit, soc_port_t port, soc_port_mdix_t mode);
    103 STATIC int phy_54640_speed_get(int unit, soc_port_t port, int *speed);
    104 STATIC int phy_54640_control_set(int unit, soc_port_t port, soc_phy_control_t type, uint32 value);
    105 STATIC int phy_54640_control_get(int unit, soc_port_t port, soc_phy_control_t type, uint32 *value);
    106                     
    107 extern int
    108 phy_ecd_cable_diag_init(int unit, soc_port_t port);
    109 
    110 extern int
    111 phy_ecd_cable_diag(int unit, soc_port_t port,
    112             soc_port_cable_diag_t *status);
    113 
    114 /***********************************************************************
    115  *
    116  * HELPER FUNCTIONS
    117  */
    118 /*
    119  * Function:
    120  *      _phy_54640e_cl45_reg_read
    121  * Purpose:
    122  *      Read Clause 45 Register using Clause 22 register access
    123  * Parameters:
    124  *      unit      - StrataSwitch unit #.
    125  *      pc        - Phy control
    126  *      flags     - Flags
    127  *      dev_addr  - Clause 45 Device
    128  *      reg_addr  - Register Address to read
    129  *      val       - Value Read
    130  * Returns:
    131  *      SOC_E_XXX
    132  */
    133 
    134 STATIC int
    135 _phy_54640e_cl45_reg_read(int unit, phy_ctrl_t *pc, uint32 flags, 
    136                          uint8 dev_addr, uint16 reg_addr, uint16 *val)
    137 {
    138 
    139     /* Write Device Address to register 0x0D (Set Function field to Address)*/
    140     SOC_IF_ERROR_RETURN
    141         (PHY54640_REG_WRITE(unit, pc, flags, 0x00, 0x0D, (dev_addr & 0x001f)));
    142 
    143     /* Select the register by writing to register address to register 0x0E */
    144     SOC_IF_ERROR_RETURN
    145         (PHY54640_REG_WRITE(unit, pc, flags, 0x00, 0x0E, reg_addr));
    146 
    147     /* Write Device Address to register 0x0D (Set Function field to Data)*/
    148     SOC_IF_ERROR_RETURN
    149         (PHY54640_REG_WRITE(unit, pc, flags, 0x00, 0x0D, 
    150                            ((0x4000) | (dev_addr & 0x001f))));
    151 
    152     /* Read register 0x0E to get the value */
    153     SOC_IF_ERROR_RETURN
    154         (PHY54640_REG_READ(unit, pc, flags, 0x00, 0x0E, val));
    155 
    156     return SOC_E_NONE;
    157 }
    158 
    159 /*
    160  * Function:
    161  *      _phy_54640e_cl45_reg_write
    162  * Purpose:
    163  *      Write Clause 45 Register content using Clause 22 register access
    164  * Parameters:
    165  *      unit      - StrataSwitch unit #.
    166  *      pc        - Phy control
    167  *      flags     - Flags
    168  *      dev_addr  - Clause 45 Device
    169  *      reg_addr  - Register Address to read
    170  *      val       - Value to be written
    171  * Returns:
    172  *      SOC_E_XXX
    173  */
    174 
    175 STATIC int
    176 _phy_54640e_cl45_reg_write(int unit, phy_ctrl_t *pc, uint32 flags, 
    177                          uint8 dev_addr, uint16 reg_addr, uint16 val)
    178 {
    179 
    180     /* Write Device Address to register 0x0D (Set Function field to Address)*/
    181     SOC_IF_ERROR_RETURN
    182         (PHY54640_REG_WRITE(unit, pc, flags, 0x00, 0x0D, (dev_addr & 0x001f)));
    183 
    184     /* Select the register by writing to register address to register 0x0E */
    185     SOC_IF_ERROR_RETURN
    186         (PHY54640_REG_WRITE(unit, pc, flags, 0x00, 0x0E, reg_addr));
    187 
    188     /* Write Device Address to register 0x0D (Set Function field to Data)*/
    189     SOC_IF_ERROR_RETURN
    190         (PHY54640_REG_WRITE(unit, pc, flags, 0x00, 0x0D, 
    191                            ((0x4000) | (dev_addr & 0x001f))));
    192 
    193     /* Write register 0x0E to write the value */
    194     SOC_IF_ERROR_RETURN
    195         (PHY54640_REG_WRITE(unit, pc, flags, 0x00, 0x0E, val));
    196 
    197     return SOC_E_NONE;
    198 }
    199 
    200 /*
    201  * Function:
    202  *      _phy_54640e_cl45_reg_modify
    203  * Purpose:
    204  *      Modify Clause 45 Register contents using Clause 22 register access
    205  * Parameters:
    206  *      unit      - StrataSwitch unit #.
    207  *      pc        - Phy control
    208  *      flags     - Flags
    209  *      reg_bank  - Register bank
    210  *      dev_addr  - Clause 45 Device
    211  *      reg_addr  - Register Address to read
    212  *      val       - Value 
    213  *      mask      - Mask for modifying the contents of the register
    214  * Returns:
    215  *      SOC_E_XXX
    216  */
    217 
    218 STATIC int
    219 _phy_54640e_cl45_reg_modify(int unit, phy_ctrl_t *pc, uint32 flags, 
    220                           uint8 dev_addr, uint16 reg_addr, uint16 val, 
    221                           uint16 mask)
    222 {
    223 
    224     uint16 value = 0;
    225 
    226     SOC_IF_ERROR_RETURN
    227         (PHY54640E_CL45_REG_READ(unit, pc, flags, dev_addr, reg_addr, &value));
    228 
    229     value = (val & mask) | (value & ~mask);
    230 
    231     SOC_IF_ERROR_RETURN
    232         (PHY54640E_CL45_REG_WRITE(unit, pc, flags, dev_addr, reg_addr, value));
    233 
    234     return SOC_E_NONE;
    235 }
    236 
    237 /*
    238  * Function:
    239  *      _phy_54640e_blk_top_lvl_reg_read
    240  * Purpose:
    241  *      New mechanism to Read Top Level Registers for EEE and 1588.
    242  * Parameters:
    243  *      unit       - StrataSwitch unit #.
    244  *      pc         - Phy control
    245  *      flags      - Flags
    246  *      reg_offset - Register Address to read
    247  *      val        - Value Read
    248  * Returns:
    249  *      SOC_E_XXX
    250  */
    251 
    252 STATIC int
    253 _phy_54640e_blk_top_lvl_reg_read(int unit, phy_ctrl_t *pc, uint32 flags, 
    254                          uint16 reg_offset, uint16 *val)
    255 {
    256     /* Write register 0x17 with the top level reg offset to read (handled in PHY54640_REG_READ) */
    257     /* Read register 0x15 to get the value */
    258     SOC_IF_ERROR_RETURN
    259         (PHY54640_REG_READ(unit, pc, flags, (0x0D00|(reg_offset & 0xff)), 0x15, val));
    260 
    261     return SOC_E_NONE;
    262 }
    263 
    264 /*
    265  * Function:
    266  *      _phy_54640e_blk_top_lvl_reg_write
    267  * Purpose:
    268  *      New mechanism to Write Top Level Registers for EEE and 1588
    269  * Parameters:
    270  *      unit      - StrataSwitch unit #.
    271  *      pc        - Phy control
    272  *      flags     - Flags
    273  *      reg_offset  - Register Address to read
    274  *      val       - Value to be written
    275  * Returns:
    276  *      SOC_E_XXX
    277  */
    278 
    279 STATIC int
    280 _phy_54640e_blk_top_lvl_reg_write(int unit, phy_ctrl_t *pc, uint32 flags, 
    281                                   uint16 reg_offset, uint16 val)
    282 {
    283     /* Write register 0x17 with the top level reg offset to write (handled in PHY54640_REG_WRITE) */
    284     /* Write register 0x15 the value to write to the top level register offset */
    285     SOC_IF_ERROR_RETURN
    286         (PHY54640_REG_WRITE(unit, pc, flags, (0x0D00|(reg_offset & 0xff)), 0x15, val));
    287 
    288     return SOC_E_NONE;
    289 }
    290 
    291 /*
    292  * Function:
    293  *      _phy_54640e_blk_top_lvl_reg_modify
    294  * Purpose:
    295  *      Modify Clause 45 Register contents using Clause 22 register access
    296  * Parameters:
    297  *      unit      - StrataSwitch unit #.
    298  *      pc        - Phy control
    299  *      flags     - Flags
    300  *      reg_bank  - Register bank
    301  *      dev_addr  - Clause 45 Device
    302  *      reg_addr  - Register Address to read
    303  *      val       - Value 
    304  *      mask      - Mask for modifying the contents of the register
    305  * Returns:
    306  *      SOC_E_XXX
    307  */
    308 
    309 STATIC int
    310 _phy_54640e_blk_top_lvl_reg_modify(int unit, phy_ctrl_t *pc, uint32 flags,
    311                           uint16 reg_offset, uint16 val, uint16 mask)
    312 {
    313 
    314     uint16 value = 0;
    315 
    316     /* Write register 0x17 with the top level reg offset to read */
    317     SOC_IF_ERROR_RETURN
    318         (_phy_54640e_blk_top_lvl_reg_read(unit, pc, flags, 
    319                                           reg_offset, &value));
    320 
    321     value = (val & mask) | (value & ~mask);
    322 
    323     /* Write register 0x17 with the top level reg offset to read */
    324 
    325     SOC_IF_ERROR_RETURN
    326         (_phy_54640e_blk_top_lvl_reg_write(unit, pc, flags, 
    327                                           reg_offset, value));
    328 
    329     return SOC_E_NONE;
    330 }
    331 
    332 /*
    333  * Function:
    334  *      _phy_54640_medium_check
    335  * Purpose:
    336  *      Determine if chip should operate in copper or fiber mode.
    337  * Parameters:
    338  *      unit - StrataSwitch unit #.
    339  *      port - StrataSwitch port #.
    340  *      medium - (OUT) SOC_PORT_MEDIUM_XXX
    341  * Returns:
    342  *      SOC_E_XXX
    343  */
    344 STATIC int
    345 _phy_54640_medium_check(int unit, soc_port_t port, int *medium)
    346 {
    347     phy_ctrl_t    *pc = EXT_PHY_SW_STATE(unit, port);
    348     uint16         tmp = 0xffff;
    349 
    350     *medium = SOC_PORT_MEDIUM_COPPER;
    351 
    352     if (pc->automedium) {
    353         /* Read Mode Register (0x1c shadow 11111) */
    354         SOC_IF_ERROR_RETURN
    355             (READ_PHY54640_MODE_CTRLr(unit, pc, &tmp));
    356 
    357         if (pc->fiber.preferred) {
    358             /* 0x10 Fiber Signal Detect
    359              * 0x20 Copper Energy Detect
    360              */
    361             if ((tmp & 0x30) == 0x20) {
    362                 /* Only copper is link up */
    363                 *medium = SOC_PORT_MEDIUM_COPPER;
    364             } else {
    365                 *medium = SOC_PORT_MEDIUM_FIBER;
    366             }
    367         } else {
    368             if ((tmp & 0x30) == 0x10) {
    369                 /* Only fiber is link up */
    370                 *medium = SOC_PORT_MEDIUM_FIBER;
    371             } else {
    372                 *medium = SOC_PORT_MEDIUM_COPPER;
    373             }
    374         }
    375     } else {
    376         *medium = pc->fiber.preferred ? SOC_PORT_MEDIUM_FIBER:
    377                   SOC_PORT_MEDIUM_COPPER;
    378     }
    379    
    380     LOG_VERBOSE(BSL_LS_SOC_PHY,
    381                 (BSL_META_U(unit,
    382                             "_phy_54640_medium_check: "
    383                             "u=%d p=%d fiber_pref=%d 0x1c(11111)=%04x fiber=%d\n"),
    384                  unit, port, pc->fiber.preferred, tmp, 
    385                  (*medium == SOC_PORT_MEDIUM_FIBER) ? 1 : 0));
    386 
    387     return SOC_E_NONE;
    388 }
    389 
    390 STATIC int
    391 _phy_bcm54640_serdes_update(int unit, soc_port_t port)
    392 {
    393     phy_ctrl_t  *pc;
    394     pc = EXT_PHY_SW_STATE(unit, port);
    395 
    396     /* check if ( Copper_disabled ) or ( fiber enabled and preferred ) */
    397     if ( (!(pc->copper.enable)) ||
    398           ((pc->fiber.enable) && (pc->fiber.preferred)) ) {
    399         /* turn on the passive external SerDes 100FX if fiber 100FX mode */
    400         uint16 serdes_mode = (pc->fiber.force_speed == 100)
    401                            ? PHY_BCM54640_EXT_SERDES_FX : 0;
    402         /* External Serdes Control Reg., DIGX_SHD_1C_14, RDB_0x234 */
    403         SOC_IF_ERROR_RETURN
    404             (MODIFY_PHY54640_EXT_SERDES_CTRLr(unit, pc, serdes_mode,
    405                                         PHY_BCM54640_EXT_SERDES_FX_MASK));
    406     }
    407     return SOC_E_NONE;
    408 }
    409 
    410 /*
    411  * Function:
    412  *      phy_54640_medium_status
    413  * Purpose:
    414  *      Indicate the current active medium
    415  * Parameters:
    416  *      unit - StrataSwitch unit #.
    417  *      port - StrataSwitch port #.
    418  *      medium - (OUT) One of:
    419  *              SOC_PORT_MEDIUM_COPPER
    420  *              SOC_PORT_MEDIUM_FIBER
    421  * Returns:
    422  *      SOC_E_NONE
    423  */
    424 
    425 STATIC int
    426 phy_54640_medium_status(int unit, soc_port_t port, soc_port_medium_t *medium)
    427 {
    428     if (PHY_COPPER_MODE(unit, port)) {
    429         *medium = SOC_PORT_MEDIUM_COPPER;
    430     } else {
    431         *medium = SOC_PORT_MEDIUM_FIBER;
    432     }
    433     return SOC_E_NONE;
    434 }
    435 
    436 /*
    437  * Function:
    438  *      _phy_54640_medium_config_update
    439  * Purpose:
    440  *      Update the PHY with config parameters
    441  * Parameters:
    442  *      unit - StrataSwitch unit #.
    443  *      port - StrataSwitch port #.
    444  *      cfg - Config structure.
    445  * Returns:
    446  *      SOC_E_XXX
    447  */
    448 
    449 STATIC int
    450 _phy_54640_medium_config_update(int unit, soc_port_t port,
    451                                 soc_phy_config_t *cfg)
    452 {
    453     SOC_IF_ERROR_RETURN
    454         (phy_54640_speed_set(unit, port, cfg->force_speed));
    455     SOC_IF_ERROR_RETURN
    456         (phy_54640_duplex_set(unit, port, cfg->force_duplex));
    457     SOC_IF_ERROR_RETURN
    458         (phy_54640_master_set(unit, port, cfg->master));
    459     SOC_IF_ERROR_RETURN
    460         (phy_54640_ability_advert_set(unit, port, &cfg->advert_ability));
    461     SOC_IF_ERROR_RETURN
    462         (phy_54640_autoneg_set(unit, port, cfg->autoneg_enable));
    463     SOC_IF_ERROR_RETURN
    464         (phy_54640_mdix_set(unit, port, cfg->mdix));
    465 
    466     return SOC_E_NONE;
    467 }
    468 
    469 /***********************************************************************
    470  *
    471  * PHY54640 DRIVER ROUTINES
    472  */
    473 
    474 /*
    475  * Function:
    476  *      phy_54640_medium_config_set
    477  * Purpose:
    478  *      Set the operating parameters that are automatically selected
    479  *      when medium switches type.
    480  * Parameters:
    481  *      unit - StrataSwitch unit #
    482  *      port - Port number
    483  *      medium - SOC_PORT_MEDIUM_COPPER/FIBER
    484  *      cfg - Operating parameters
    485  * Returns:
    486  *      SOC_E_XXX
    487  */
    488 
    489 STATIC int
    490 phy_54640_medium_config_set(int unit, soc_port_t port, 
    491                            soc_port_medium_t  medium,
    492                            soc_phy_config_t  *cfg)
    493 {
    494     phy_ctrl_t    *pc;
    495     soc_phy_config_t *active_medium;  /* Currently active medium */
    496     soc_phy_config_t *change_medium;  /* Requested medium */
    497     soc_phy_config_t *other_medium;   /* The other medium */
    498     int               medium_update;
    499     soc_port_mode_t   advert_mask;
    500 
    501     if (NULL == cfg) {
    502         return SOC_E_PARAM;
    503     }
    504 
    505     pc            = EXT_PHY_SW_STATE(unit, port);
    506     medium_update = FALSE;
    507 
    508     switch (medium) {
    509     case SOC_PORT_MEDIUM_COPPER:
    510         if (!pc->automedium) {
    511             if (!PHY_COPPER_MODE(unit, port)) {
    512                 return SOC_E_UNAVAIL;
    513             }
    514             /* check if device is fiber capable before switching */
    515             if (cfg->preferred == 0) {
    516                 uint16 data;
    517 
    518                 SOC_IF_ERROR_RETURN
    519                     (READ_PHY54640_MODE_CTRLr(unit, pc, &data));
    520  
    521                 /* return if not fiber capable*/
    522                 if (!(data & 0x8)) {
    523                     return SOC_E_UNAVAIL;
    524                 }
    525             }
    526         }
    527 
    528         change_medium  = &pc->copper;
    529         other_medium   = &pc->fiber;
    530         advert_mask    = ADVERT_ALL_COPPER;
    531         break;
    532     case SOC_PORT_MEDIUM_FIBER:
    533         if (!pc->automedium && !PHY_FIBER_MODE(unit, port)) {
    534             return SOC_E_UNAVAIL;
    535         }
    536         change_medium  = &pc->fiber;
    537         other_medium   = &pc->copper;
    538         advert_mask    = ADVERT_ALL_FIBER;
    539         break;
    540     default:
    541         return SOC_E_PARAM;
    542     }
    543 
    544     /*
    545      * Changes take effect immediately if the target medium is active or
    546      * the preferred medium changes.
    547      */
    548     if (change_medium->enable != cfg->enable) {
    549         medium_update = TRUE;
    550     }
    551     if (change_medium->preferred != cfg->preferred) {
    552         /* Make sure that only one medium is preferred */
    553         other_medium->preferred = !cfg->preferred;
    554         medium_update = TRUE;
    555     }
    556 
    557     sal_memcpy(change_medium, cfg, sizeof(*change_medium));
    558     change_medium->autoneg_advert &= advert_mask;
    559 
    560     SOC_IF_ERROR_RETURN
    561         (_phy_bcm54640_serdes_update(unit, port));
    562 
    563     if (medium_update) {
    564         /* The new configuration may cause medium change. Check
    565          * and update medium.
    566          */
    567         SOC_IF_ERROR_RETURN
    568             (_phy_54640_medium_change(unit, port, TRUE));
    569     } else {
    570         active_medium = (PHY_COPPER_MODE(unit, port)) ?  
    571                             &pc->copper : &pc->fiber;
    572         if (active_medium == change_medium) {
    573             /* If the medium to update is active, update the configuration */
    574             SOC_IF_ERROR_RETURN
    575                 (_phy_54640_medium_config_update(unit, port, change_medium));
    576         }
    577     }
    578     return SOC_E_NONE;
    579 }
    580 
    581 /*
    582  * Function:
    583  *      phy_54640_medium_config_get
    584  * Purpose:
    585  *      Get the operating parameters that are automatically selected
    586  *      when medium switches type.
    587  * Parameters:
    588  *      unit - StrataSwitch unit #
    589  *      port - Port number
    590  *      medium - SOC_PORT_MEDIUM_COPPER/FIBER
    591  *      cfg - (OUT) Operating parameters
    592  * Returns:
    593  *      SOC_E_XXX
    594  */
    595 
    596 STATIC int
    597 phy_54640_medium_config_get(int unit, soc_port_t port, 
    598                            soc_port_medium_t medium,
    599                            soc_phy_config_t *cfg)
    600 {
    601     int            rv;
    602     phy_ctrl_t    *pc;
    603 
    604     pc = EXT_PHY_SW_STATE(unit, port);
    605     rv = SOC_E_NONE;
    606 
    607     switch (medium) {
    608     case SOC_PORT_MEDIUM_COPPER:
    609         if (pc->automedium || PHY_COPPER_MODE(unit, port)) {
    610             sal_memcpy(cfg, &pc->copper, sizeof (*cfg));
    611         } else {
    612             rv = SOC_E_UNAVAIL;
    613         }
    614         break;
    615     case SOC_PORT_MEDIUM_FIBER:
    616         if (pc->automedium || PHY_FIBER_MODE(unit, port)) {
    617             sal_memcpy(cfg, &pc->fiber, sizeof (*cfg));
    618         } else {
    619             rv = SOC_E_UNAVAIL;
    620         }
    621         break;
    622     default:
    623         rv = SOC_E_PARAM;
    624         break;
    625     }
    626 
    627     return rv;
    628 }
    629 
    630 /*
    631  * Function:
    632  *      _phy_54640_reset_setup
    633  * Purpose:
    634  *      Function to reset the PHY and set up initial operating registers.
    635  * Parameters:
    636  *      unit - StrataSwitch unit #.
    637  *      port - StrataSwitch port #.
    638  * Returns:
    639  *      SOC_E_XXX
    640  * Notes:
    641  *      Configures for copper/fiber according to the current
    642  *      setting of PHY_FLAGS_FIBER.
    643  */
    644 STATIC int
    645 _phy_54640_reset_setup(int unit, soc_port_t port)
    646 {
    647 
    648     phy_ctrl_t    *pc;
    649     soc_port_t     primary_port;
    650     int            index;
    651 
    652     pc = EXT_PHY_SW_STATE(unit, port);
    653 
    654     /* Reset the PHY with previously registered callback function. 
    655      */
    656     SOC_IF_ERROR_RETURN(soc_phy_reset(unit, port));
    657 
    658     if ( soc_phy_primary_and_offset_get(unit, port, &primary_port, &index)
    659                                        == SOC_E_NONE ) {
    660         /* determine the order of logical ports on a PHY chip */
    661         if ( index & 0x80 ) {
    662             PHY54640_FLAGS(pc) |= ( PHY54640_PHYA_REV);    /* reverse order */
    663         } else {
    664             PHY54640_FLAGS(pc) &= (~PHY54640_PHYA_REV);    /* normal  order */
    665         }
    666     } else {
    667         /* config variable 'phy_port_primary_and_offset' not specified  *\
    668         |* Assign default values in a 4-alignment basis :               *|
    669         |*    e.g.         port =  2 3 4 5 6 7 8 9                      *|
    670         |*                index =  0 1 2 3 0 1 2 3                      *|
    671         \*         primary_port =  2 2 2 2 6 6 6 6                      */
    672         index        = pc->phy_id & ( PHY54640_DEV_PHY_SLICE_MASK);
    673         primary_port = ((port - index) <= 0) ?  0 :  (port - index);
    674         PHY54640_FLAGS(pc) &= (~PHY54640_PHYA_REV);        /* normal  order */
    675         LOG_WARN(BSL_LS_SOC_PHY,
    676                  (BSL_META_U(unit,
    677                              "Config property 'phy_port_primary_and_offset' "
    678                              "not set for unit %d, port %d.  "
    679                              "Default to slice=0x%x, base=0x%x\n"),
    680                              unit, port, index, primary_port));
    681     }
    682 
    683     /* keep the original MDIO address.  Set slice ID & base PHY address */
    684     /*   Do not change the order to the following four lines */
    685     index &= ~0x80;    /* clear reverse bit (PHYA_REV) */
    686     PHY54640_DEV_PHY_ID_ORIG(pc) = pc->phy_id;
    687     PHY54640_DEV_PHY_SLICE(pc)   = index;
    688     PHY54640_DEV_SET_BASE_ADDR(pc);   /* phy address of port 0 of this chip */
    689 
    690     SOC_IF_ERROR_RETURN
    691         (phy_54640_control_set( unit, port, SOC_PHY_CONTROL_PORT_PRIMARY,
    692                                             primary_port));
    693     SOC_IF_ERROR_RETURN
    694         (phy_54640_control_set( unit, port, SOC_PHY_CONTROL_PORT_OFFSET,
    695                                             index ));
    696     if ( index == 0 ) {
    697         pc->flags  |= PHYCTRL_IS_PORT0;
    698         /* Switch to MDI registers */
    699         SOC_IF_ERROR_RETURN(
    700             PHY54640_REG_WRITE(unit, pc, 0, 0x0D01, 0x15, 0x0001));
    701     }
    702 
    703     /* Disable super-isolate */
    704     SOC_IF_ERROR_RETURN
    705         (MODIFY_PHY54640_MII_POWER_CTRLr(unit, pc, 0x0, 1U<<5));
    706 
    707     /* BCM54640E is EEE capable */
    708     if ( PHY_IS_BCM54640E(pc) ) {
    709         PHY_FLAGS_SET(unit, port, PHY_FLAGS_EEE_CAPABLE);
    710         SOC_IF_ERROR_RETURN
    711             (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 7,
    712                                                 0x0000, 0x0003));
    713         SOC_IF_ERROR_RETURN
    714             (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0F7F,0x15, 0x0, 0x7)); /* exp 7f */
    715     }
    716 
    717     if ( ECD_ENABLE && ((PHY_IS_BCM54640E(pc))
    718                     && ((pc->phy_rev & 0x7) > 0x2)) ) {
    719         SOC_IF_ERROR_RETURN(
    720             phy_ecd_cable_diag_init(unit, port));
    721     }
    722 
    723     SOC_IF_ERROR_RETURN
    724         (_phy_54640_no_reset_setup(unit, port));
    725 
    726     return SOC_E_NONE;
    727 }
    728 
    729 STATIC int
    730 _phy_54640_no_reset_setup(int unit, soc_port_t port)
    731 {
    732     phy_ctrl_t    *pc;
    733     uint16         data, mask;
    734 
    735     LOG_INFO(BSL_LS_SOC_PHY,
    736              (BSL_META_U(unit,
    737                          "_phy_54640_reset_setup: u=%d p=%d medium=%s\n"),
    738                          unit, port,
    739               PHY_COPPER_MODE(unit, port) ? "COPPER" : "FIBER"));
    740 
    741     pc      = EXT_PHY_SW_STATE(unit, port);
    742 
    743     /* copper regs */
    744     if (!pc->copper.enable || (!pc->automedium && pc->fiber.preferred)) {
    745         /* Copper interface is not used. Powered down. */
    746         SOC_IF_ERROR_RETURN
    747             (MODIFY_PHY54640_MII_CTRLr(unit, pc, MII_CTRL_PD, MII_CTRL_PD));
    748     } else {
    749         SOC_IF_ERROR_RETURN
    750             (MODIFY_PHY54640_MII_CTRLr(unit, pc, 0, MII_CTRL_PD));
    751         SOC_IF_ERROR_RETURN
    752             (WRITE_PHY54640_MII_GB_CTRLr(unit, pc,  
    753                             MII_GB_CTRL_ADV_1000FD | MII_GB_CTRL_PT));
    754         SOC_IF_ERROR_RETURN
    755             (WRITE_PHY54640_MII_CTRLr(unit, pc, 
    756                 MII_CTRL_FD | MII_CTRL_SS_10 | 
    757                 MII_CTRL_SS_100 | MII_CTRL_AE));
    758     }
    759 
    760     /* Adjust timing and enable link speed led mode */
    761     SOC_IF_ERROR_RETURN
    762         (MODIFY_PHY54640_SPARE_CTRLr(unit, pc, 0x0006, 0x0006));
    763 
    764     if (PHY_IS_BCM54640(pc)) {
    765         /* Invert 10BT TX clock */
    766         SOC_IF_ERROR_RETURN
    767             (MODIFY_PHY54640_MII_10BASE_Tr(unit, pc, 1U<<11, 1U<<11));
    768     }
    769 
    770 
    771     /* Power down SerDes */
    772     SOC_IF_ERROR_RETURN
    773         (MODIFY_PHY54640_1000X_MII_CTRLr(unit, pc, MII_CTRL_PD, 
    774                                                MII_CTRL_PD));
    775     /* Configure Auto-detect Medium (0x1c shadow 11110) */
    776     mask = 0x33f;               /* Auto-detect bit mask */
    777     data = 0;
    778     if (pc->automedium) {
    779         data |= 1 << 0;    /* Enable auto-detect medium */
    780     }
    781 
    782     /* When no link partner exists, fiber should be default medium */
    783     /* When both link partners exipc, fiber should be prefered medium
    784      * for the following reasons.
    785      * 1. Most fiber module generates signal detect when the fiber cable is
    786      *    plugged in regardless of any activity even if the link partner is
    787      *    powered down.
    788      * 2. Fiber mode consume much lesser power than copper mode.
    789      */
    790     if (pc->fiber.preferred) {
    791         data |= 1 << 1;    /* Fiber selected when both media are active */
    792         data |= 1 << 2;    /* Fiber selected when no medium is active */
    793     }
    794 
    795     /* Qulalify fiber link with SYNC from PCS. */
    796     data |= 1 << 5;
    797 
    798     /*
    799      * Enable internal inversion of LED2/SD input pin.  Used only if
    800      * the fiber module provides RX_LOS instead of Signal Detect.
    801      */
    802     if (pc->fiber_detect < 0) {
    803          data |= 1 << 8;    /* Fiber signal detect is active low from pin */
    804     }
    805 
    806     SOC_IF_ERROR_RETURN
    807         (MODIFY_PHY54640_AUTO_DETECT_MEDIUMr(unit, pc, data, mask));
    808 
    809 
    810     if (pc->fiber.enable && (pc->automedium || pc->fiber.preferred)) {
    811         /* remove power down of SerDes */
    812         SOC_IF_ERROR_RETURN
    813             (MODIFY_PHY54640_1000X_MII_CTRLr(unit, pc, 0, MII_CTRL_PD));
    814 
    815         /* set the advertisement of serdes */
    816         /* Disable half-duplex, full-duplex capable */
    817         /* SGMII AutoNeg Advertis Reg., Fiber Reg. 0x4 */
    818         SOC_IF_ERROR_RETURN
    819             (MODIFY_PHY54640_1000X_MII_ANAr(unit, pc, MII_ANA_C37_FD, 
    820                                     (MII_ANA_C37_HD | MII_ANA_C37_FD)));
    821         /* SGMII Slave Reg.,  Reg. 0x1C, shadow 0x15 */
    822         SOC_IF_ERROR_RETURN
    823             (MODIFY_PHY54640_SGMII_SLAVEr(unit, pc, 0, 0x1));
    824         /* Restart SerDes autonegotiation */
    825         SOC_IF_ERROR_RETURN
    826             (WRITE_PHY54640_1000X_MII_CTRLr(unit, pc,
    827                 MII_CTRL_FD | MII_CTRL_SS_1000 | MII_CTRL_AE));
    828         
    829         switch (pc->fiber_detect) {
    830             case  2:         /* LED 2 */
    831             case -2:
    832             case  4:         /* LED 4 : kept for backward compatibility */
    833             case -4:
    834             case  10:        /* EN_10B for signal detect */
    835             case -10:
    836             case 0:         /* default */
    837                 /* GPIO Control/Status Register (0x1c shadow 01111) */
    838                 /* Change LED2 pin into an input */
    839                 SOC_IF_ERROR_RETURN
    840                     (MODIFY_PHY54640_LED_GPIO_CTRLr(unit, pc, 0x08, 0x08));
    841                 break;
    842             case  1:         /* PECL , no such pin in 54640 data sheet */
    843             case -1:
    844             default:
    845                 return SOC_E_CONFIG;
    846         }
    847         SOC_IF_ERROR_RETURN
    848             (MODIFY_PHY54640_MISC_1000X_CONTROLr(unit, pc, 0x0000, 0x0020));
    849         SOC_IF_ERROR_RETURN
    850             (MODIFY_PHY54640_MODE_CTRLr(unit, pc, 0x0002, 0x0006));        
    851     }
    852     else {
    853         /* SGMII to copper mode */
    854         SOC_IF_ERROR_RETURN
    855             (MODIFY_PHY54640_MODE_CTRLr(unit, pc, 0x0, 0x0006));        
    856     }
    857 
    858 #if AUTO_MDIX_WHEN_AN_DIS
    859     /* Enable Auto-MDIX When autoneg disabled */
    860     SOC_IF_ERROR_RETURN
    861         (MODIFY_PHY54640_MII_MISC_CTRLr(unit, pc, 0x200, 0x200));
    862 #endif
    863 
    864     /* Configure extended control register for led mode and jumbo frame support
    865      */
    866     mask  = (1 << 5) | (1 << 0);
    867 
    868     /* if using led link/activity mode, disable led traffic mode */
    869     if (pc->ledctrl & 0x10 || pc->ledselect == 0x01) {
    870         data = 0;
    871     } else {
    872         data  = 1 << 5;      /* Enable LEDs to indicate traffic status */
    873     }
    874     /* Configure Extended Control Register for Jumbo frames support */
    875     data |= 1 << 0;      /* Set high FIFO elasticity to support jumbo frames */
    876     SOC_IF_ERROR_RETURN
    877         (MODIFY_PHY54640_MII_ECRr(unit, pc, data, mask));
    878 
    879     /* Enable extended packet length (4.5k through 25k) */
    880     SOC_IF_ERROR_RETURN
    881         (MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, 0x4000, 0x4000));
    882 
    883     SOC_IF_ERROR_RETURN
    884         (MODIFY_PHY54640_MISC_1000X_CTRL2r(unit, pc, 0x0001, 0x0001));
    885 
    886     SOC_IF_ERROR_RETURN
    887         (MODIFY_PHY54640_AUX_CTRLr(unit, pc, 0x0002, 0x0002));
    888 
    889     /* Configure LED selectors */
    890     data = ((pc->ledmode[1] & 0xf) << 4) | (pc->ledmode[0] & 0xf);
    891     SOC_IF_ERROR_RETURN
    892         (WRITE_PHY54640_LED_SELECTOR_1r(unit, pc, data));
    893 
    894     data = ((pc->ledmode[3] & 0xf) << 4) | (pc->ledmode[2] & 0xf);
    895     SOC_IF_ERROR_RETURN
    896         (WRITE_PHY54640_LED_SELECTOR_2r(unit, pc, data));
    897 
    898     data = (pc->ledctrl & 0x3ff);
    899     SOC_IF_ERROR_RETURN
    900         (WRITE_PHY54640_LED_CTRLr(unit, pc, data));
    901 
    902     SOC_IF_ERROR_RETURN
    903         (WRITE_PHY54640_EXP_LED_SELECTORr(unit, pc, pc->ledselect));
    904 
    905     if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
    906         return SOC_E_NONE;
    907     }
    908 
    909     /* 100BASE-TX initialization change for EEE and autogreen mode */
    910     SOC_IF_ERROR_RETURN
    911         (MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, 0x0c00, 0x0c00));
    912     SOC_IF_ERROR_RETURN(
    913         WRITE_PHY_REG(unit, pc, 0x17, 0x4022));
    914     SOC_IF_ERROR_RETURN(
    915         WRITE_PHY_REG(unit, pc, 0x15, 0x017b));
    916     SOC_IF_ERROR_RETURN
    917         (MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, 0x0400, 0x0c00));
    918 
    919 
    920     return SOC_E_NONE;
    921 }
    922 
    923 /*
    924  * Function:
    925  *      phy_54640_init
    926  * Purpose:
    927  *      Init function for 54640 PHY.
    928  * Parameters:
    929  *      unit - StrataSwitch unit #.
    930  *      port - StrataSwitch port #.
    931  * Returns:
    932  *      SOC_E_NONE
    933  */
    934 
    935 STATIC int
    936 phy_54640_init(int unit, soc_port_t port)
    937 {
    938     phy_ctrl_t        *pc;
    939     int                fiber_capable;
    940     int                fiber_preferred;
    941 
    942     LOG_INFO(BSL_LS_SOC_PHY,
    943              (BSL_META_U(unit,
    944                          "phy_54640_init: u=%d p=%d\n"),
    945                          unit, port));
    946 
    947     /* Only SGMII interface to switch MAC is supported */ 
    948     if (IS_GMII_PORT(unit, port)) {
    949         return SOC_E_CONFIG; 
    950     }
    951 
    952     pc = EXT_PHY_SW_STATE(unit, port);
    953 
    954     pc->interface = SOC_PORT_IF_SGMII;
    955 
    956     fiber_capable = 1;
    957 
    958     fiber_preferred =
    959         soc_property_port_get(unit, port, spn_PHY_FIBER_PREF, 0);
    960     pc->automedium =
    961         soc_property_port_get(unit, port, spn_PHY_AUTOMEDIUM, 0);
    962     pc->fiber_detect =
    963         soc_property_port_get(unit, port, spn_PHY_FIBER_DETECT, -4);
    964 
    965     LOG_INFO(BSL_LS_SOC_PHY,
    966              (BSL_META_U(unit,
    967                          "phy_54640_init: "
    968                          "u=%d p=%d type=54640%s automedium=%d fiber_pref=%d detect=%d\n"),
    969               unit, port, fiber_capable ? "S" : "",
    970               pc->automedium, fiber_preferred, pc->fiber_detect));
    971 
    972     pc->copper.enable = TRUE;
    973     pc->copper.preferred = !fiber_preferred;
    974     pc->copper.autoneg_enable = TRUE;
    975     pc->copper.autoneg_advert = ADVERT_ALL_COPPER;
    976     SOC_IF_ERROR_RETURN
    977         (_phy_54640_ability_cu_local_get(unit, port, &(pc->copper.advert_ability)));
    978     pc->copper.advert_ability.medium = SOC_PA_MEDIUM_COPPER;
    979     pc->copper.force_speed = 1000;
    980     pc->copper.force_duplex = TRUE;
    981     pc->copper.master = SOC_PORT_MS_AUTO;
    982     pc->copper.mdix = SOC_PORT_MDIX_AUTO;
    983 
    984     pc->fiber.enable = fiber_capable;
    985     pc->fiber.preferred = fiber_preferred;
    986     pc->fiber.autoneg_enable = fiber_capable;
    987     pc->fiber.autoneg_advert = ADVERT_ALL_FIBER;
    988     SOC_IF_ERROR_RETURN
    989         (_phy_54640_ability_fiber_local_get(unit, port, &(pc->fiber.advert_ability)));
    990     pc->copper.advert_ability.medium = SOC_PA_MEDIUM_FIBER;
    991     pc->fiber.force_speed = 1000;
    992     pc->fiber.force_duplex = TRUE;
    993     pc->fiber.master = SOC_PORT_MS_NONE;
    994     pc->fiber.mdix = SOC_PORT_MDIX_NORMAL;
    995 
    996     /* Initially configure for the preferred medium. */
    997     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_COPPER);
    998     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER);
    999     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_PASSTHRU);
   1000     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_100FX);
   1001     PHY_FLAGS_CLR(unit, pc->port, PHY_FLAGS_EEE_ENABLED);
   1002     PHY_FLAGS_CLR(unit, pc->port, PHY_FLAGS_EEE_MODE);
   1003 
   1004     if (pc->fiber.preferred) {
   1005         PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
   1006     } else {
   1007         PHY_FLAGS_SET(unit, port, PHY_FLAGS_COPPER);
   1008     }
   1009 
   1010     /* Get Requested LED selectors (defaults are hardware defaults) */
   1011     pc->ledmode[0] = soc_property_port_get(unit, port, spn_PHY_LED1_MODE, 0);
   1012     pc->ledmode[1] = soc_property_port_get(unit, port, spn_PHY_LED2_MODE, 1);
   1013     pc->ledmode[2] = soc_property_port_get(unit, port, spn_PHY_LED3_MODE, 3);
   1014     pc->ledmode[3] = soc_property_port_get(unit, port, spn_PHY_LED4_MODE, 6);
   1015     pc->ledctrl = soc_property_port_get(unit, port, spn_PHY_LED_CTRL, 0x8);
   1016     pc->ledselect = soc_property_port_get(unit, port, spn_PHY_LED_SELECT, 0);
   1017 
   1018 #ifdef BCM_WARM_BOOT_SUPPORT
   1019     if (SOC_WARM_BOOT(unit)) {
   1020         uint16 data;
   1021         SOC_IF_ERROR_RETURN
   1022                 (READ_PHY54640_SERDES_100FX_CTRLr(unit, pc, &data));
   1023         if (data & 0x1) {
   1024             PHY_FLAGS_SET(unit, port, PHY_FLAGS_100FX);
   1025             pc->fiber.force_speed = 100;
   1026             pc->fiber.autoneg_enable = FALSE;
   1027             if (!(data & 0x2)) {
   1028                 pc->fiber.force_duplex = FALSE;
   1029             }
   1030         } else {
   1031             SOC_IF_ERROR_RETURN
   1032                 (READ_PHY54640_1000X_MII_CTRLr(unit, pc, &data));
   1033             if (!(data & MII_CTRL_FD)) {
   1034                 pc->fiber.force_duplex = FALSE;
   1035             }
   1036             if (!(data & MII_CTRL_AE)) {
   1037                 pc->fiber.autoneg_enable = FALSE;
   1038             }
   1039         }
   1040     }
   1041 #endif
   1042 
   1043     SOC_IF_ERROR_RETURN
   1044         (_phy_54640_reset_setup(unit, port));
   1045 
   1046     SOC_IF_ERROR_RETURN
   1047         (_phy_54640_medium_config_update(unit, port,
   1048                                         PHY_COPPER_MODE(unit, port) ?
   1049                                         &pc->copper :
   1050                                         &pc->fiber));
   1051     return SOC_E_NONE;
   1052 }
   1053 
   1054 /*
   1055  * Function:
   1056  *      phy_54640_enable_set
   1057  * Purpose:
   1058  *      Enable or disable the physical interface.
   1059  * Parameters:
   1060  *      unit - StrataSwitch unit #.
   1061  *      port - StrataSwitch port #.
   1062  *      enable - Boolean, true = enable PHY, false = disable.
   1063  * Returns:
   1064  *      SOC_E_XXX
   1065  */
   1066 
   1067 STATIC int
   1068 phy_54640_enable_set(int unit, soc_port_t port, int enable)
   1069 {
   1070     uint16         power, mii_stat;
   1071     soc_timeout_t  to;
   1072     phy_ctrl_t    *pc;
   1073 
   1074     pc     = EXT_PHY_SW_STATE(unit, port);
   1075     power  = (enable) ? 0 : MII_CTRL_PD;
   1076 
   1077     /* Do not incorrectly Power up the interface if medium is disabled and 
   1078      * global enable = true
   1079      */
   1080     if (pc->copper.enable && (pc->automedium || PHY_COPPER_MODE(unit, port))) {
   1081         SOC_IF_ERROR_RETURN
   1082             (MODIFY_PHY54640_MII_CTRLr(unit, pc, power, MII_CTRL_PD));
   1083 
   1084         if (!enable) {
   1085             if ((PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) &&
   1086                ((pc->phy_rev & 0x4) == 0x0) ){ /* A0,A1,B0,B1 */
   1087 
   1088                 soc_timeout_init(&to, 2000000, 0);
   1089                 do {
   1090                     SOC_IF_ERROR_RETURN
   1091                         (READ_PHY54640_MII_STATr(unit, pc, &mii_stat));
   1092 
   1093                     if (soc_timeout_check(&to)) {
   1094                         LOG_WARN(BSL_LS_SOC_PHY,
   1095                                  (BSL_META_U(unit,
   1096                                              "phy54640.c: copper link didn't go down after power down: u=%d p=%d\n"),
   1097                                   unit, port));
   1098                         break;
   1099                     }
   1100                 } while(mii_stat & MII_STAT_LA);
   1101             }
   1102         }
   1103 
   1104         LOG_INFO(BSL_LS_SOC_PHY,
   1105                  (BSL_META_U(unit,
   1106                              "phy_54640_enable_set: "
   1107                              "Power %s copper medium\n"), (enable) ? "up" : "down"));
   1108     }
   1109 
   1110     if (pc->fiber.enable && (pc->automedium || PHY_FIBER_MODE(unit, port))) {
   1111         phy_ctrl_t  *int_pc;
   1112         SOC_IF_ERROR_RETURN
   1113             (MODIFY_PHY54640_1000X_MII_CTRLr(unit, pc, power, 
   1114                                                    MII_CTRL_PD));
   1115 
   1116         int_pc = INT_PHY_SW_STATE(unit, port);
   1117         if (NULL != int_pc) {
   1118             SOC_IF_ERROR_RETURN
   1119                 (PHY_ENABLE_SET(int_pc->pd, unit, port, enable));
   1120         }
   1121 
   1122         /*
   1123          * When enable/disable the fiber port,
   1124          * the Tx disable bit of Expansion register 0x52 should be set also.
   1125          * Otherwise the following issue happens.
   1126          * After the fiber port is powered down
   1127          * (verified bit 11 MII cotnrol register 0 is set to 1),
   1128          * the link partner still shows linkup
   1129          */
   1130         SOC_IF_ERROR_RETURN
   1131             (MODIFY_PHY54640_EXP_SERDES_SGMII_RX_CTRLr(unit, pc, enable?0:1, 0x1));
   1132         SOC_IF_ERROR_RETURN
   1133             (MODIFY_PHY54640_EXP_SERDES_SGMII_CTRLr(unit, pc, enable?0:1, 0x1));
   1134 
   1135         if (!enable) {
   1136             soc_timeout_init(&to, 2000000, 0);
   1137             do {
   1138                 SOC_IF_ERROR_RETURN
   1139                     (READ_PHY54640_1000X_MII_STATr(unit, pc, &mii_stat));
   1140 
   1141                 if (soc_timeout_check(&to)) {
   1142                     LOG_WARN(BSL_LS_SOC_PHY,
   1143                              (BSL_META_U(unit,
   1144                                          "phy54640.c: fiber link didn't go down after power down: u=%d p=%d\n"),
   1145                               unit, port));
   1146                     break;
   1147                 }
   1148             } while(mii_stat & MII_STAT_LA);
   1149         }
   1150     
   1151         LOG_INFO(BSL_LS_SOC_PHY,
   1152                  (BSL_META_U(unit,
   1153                              "phy_54640_enable_set: "
   1154                              "Power %s fiber medium\n"), (enable) ? "up" : "down"));
   1155     }
   1156 
   1157     /* Update software state */
   1158     SOC_IF_ERROR_RETURN(phy_fe_ge_enable_set(unit, port, enable));
   1159 
   1160     return SOC_E_NONE;
   1161 }
   1162 
   1163 /*
   1164  * Function:
   1165  *      phy_54640_enable_get
   1166  * Purpose:
   1167  *      Enable or disable the physical interface for a 54640 device.
   1168  * Parameters:
   1169  *      unit - StrataSwitch unit #.
   1170  *      port - StrataSwitch port #.
   1171  *      enable - (OUT) Boolean, true = enable PHY, false = disable.
   1172  * Returns:
   1173  *      SOC_E_XXX
   1174  */
   1175 
   1176 STATIC int
   1177 phy_54640_enable_get(int unit, soc_port_t port, int *enable)
   1178 {
   1179     return phy_fe_ge_enable_get(unit, port, enable);
   1180 }
   1181 
   1182 /*
   1183  * Function:
   1184  *      _phy_54640_fiber_100fx_setup
   1185  * Purpose:
   1186  *      Switch from 1000X mode to 100FX.  
   1187  * Parameters:
   1188  *      unit - StrataSwitch unit #.
   1189  *      port - StrataSwitch port #.
   1190  * Returns:
   1191  *      SOC_E_XXX
   1192  */
   1193 STATIC int
   1194 _phy_54640_fiber_100fx_setup(int unit, soc_port_t port) 
   1195 {
   1196     phy_ctrl_t    *pc;
   1197 
   1198     pc          = EXT_PHY_SW_STATE(unit, port);
   1199 
   1200     LOG_VERBOSE(BSL_LS_SOC_PHY,
   1201                 (BSL_META_U(unit,
   1202                             "_phy_54640_1000x_to_100fx: u=%d p=%d \n"),
   1203                  unit, port));
   1204     /* Operate SerDes in 100FX  */
   1205 
   1206     /* SerDes 100FX control register - register 1CH shadow 13H 
   1207       * BIT0 : 100FX link SerDes enable
   1208       * BIT1 : 100FX SerDes full duplex
   1209       */
   1210     if(pc->fiber.force_duplex) {
   1211         SOC_IF_ERROR_RETURN
   1212                 (MODIFY_PHY54640_SERDES_100FX_CTRLr(unit, pc, 0x3, 0x3));
   1213     } else {
   1214         SOC_IF_ERROR_RETURN
   1215                 (MODIFY_PHY54640_SERDES_100FX_CTRLr(unit, pc, 0x1, 0x3));
   1216     }
   1217 
   1218     /* Power up the SerDes      */ 
   1219     SOC_IF_ERROR_RETURN
   1220         (WRITE_PHY54640_1000X_MII_CTRLr(unit, pc, 
   1221                         MII_CTRL_FD | MII_CTRL_SS_100 | 
   1222                         (PHY_FLAGS_TST(unit, port, PHY_FLAGS_DISABLE) ? MII_CTRL_PD : 0)));
   1223 
   1224     PHY_FLAGS_SET(unit, port, PHY_FLAGS_100FX);
   1225     pc->fiber.autoneg_enable = FALSE;
   1226 
   1227     return SOC_E_NONE;
   1228 }
   1229 
   1230 /*
   1231  * Function:
   1232  *      _phy_54640_fiber_1000x_setup
   1233  * Purpose:
   1234  *      Switch from 100FX mode to 1000X.  
   1235  * Parameters:
   1236  *      unit - StrataSwitch unit #.
   1237  *      port - StrataSwitch port #.
   1238  * Returns:
   1239  *      SOC_E_XXX
   1240  */
   1241 STATIC int
   1242 _phy_54640_fiber_1000x_setup(int unit, soc_port_t port) 
   1243 {
   1244     uint16         data;
   1245     phy_ctrl_t    *pc;
   1246 
   1247     pc          = EXT_PHY_SW_STATE(unit, port);
   1248 
   1249     LOG_VERBOSE(BSL_LS_SOC_PHY,
   1250                 (BSL_META_U(unit,
   1251                             "_phy_54640_fiber_1000x_setup: u=%d p=%d \n"),
   1252                  unit, port));
   1253 
   1254     /* Operate SerDes in 1000X  */
   1255     SOC_IF_ERROR_RETURN
   1256             (MODIFY_PHY54640_SERDES_100FX_CTRLr(unit, pc, 0x0, 0x1));
   1257 
   1258     /* Power up the SerDes      */ 
   1259     if (pc->fiber.autoneg_enable) {
   1260             data = MII_CTRL_AE | MII_CTRL_RAN | MII_CTRL_FD | MII_CTRL_SS_1000;
   1261     } else {
   1262             data = MII_CTRL_RAN | MII_CTRL_FD | MII_CTRL_SS_1000;
   1263     }
   1264 
   1265     if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_DISABLE)) { 
   1266             data |= MII_CTRL_PD; 
   1267     } 
   1268 
   1269     SOC_IF_ERROR_RETURN
   1270         (WRITE_PHY54640_1000X_MII_CTRLr(unit, pc, data));
   1271 
   1272     /* When speed is 1000Mbps, set duplex to HD mode.
   1273      * This is because our MAC does not support 1G half duplex.
   1274      */
   1275     pc->fiber.force_duplex = TRUE;
   1276     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_100FX);
   1277 
   1278     return SOC_E_NONE;
   1279 }
   1280 
   1281 STATIC int
   1282 _phy_54640_medium_change(int unit, soc_port_t port, int force_update)
   1283 {
   1284     phy_ctrl_t    *pc;
   1285     int            medium;
   1286 
   1287     pc    = EXT_PHY_SW_STATE(unit, port);
   1288 
   1289     SOC_IF_ERROR_RETURN
   1290         (_phy_54640_medium_check(unit, port, &medium));
   1291 
   1292     if (medium == SOC_PORT_MEDIUM_COPPER) {
   1293         if ((!PHY_COPPER_MODE(unit, port)) || force_update) { /* Was fiber */ 
   1294             PHY_FLAGS_SET(unit, port, PHY_FLAGS_COPPER);
   1295             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER);
   1296             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_100FX);
   1297             PHY_FLAGS_SET(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
   1298 
   1299             SOC_IF_ERROR_RETURN
   1300                 (_phy_54640_no_reset_setup(unit, port));
   1301             SOC_IF_ERROR_RETURN
   1302                 (_phy_bcm54640_serdes_update(unit, port));
   1303 
   1304             /* Do not power up the interface if medium is disabled. */
   1305             if (pc->copper.enable) {
   1306                 SOC_IF_ERROR_RETURN
   1307                     (_phy_54640_medium_config_update(unit, port, &pc->copper));
   1308             }
   1309             LOG_INFO(BSL_LS_SOC_PHY,
   1310                      (BSL_META_U(unit,
   1311                                  "_phy_54640_link_auto_detect: u=%d p=%d [F->C]\n"),
   1312                       unit, port));
   1313         }
   1314 
   1315     } else {        /* Fiber */
   1316         if (PHY_COPPER_MODE(unit, port) || force_update) { /* Was copper */
   1317             PHY_FLAGS_CLR(unit, port, PHY_FLAGS_COPPER);
   1318             PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER);
   1319             PHY_FLAGS_SET(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
   1320 
   1321             SOC_IF_ERROR_RETURN
   1322                 (_phy_54640_no_reset_setup(unit, port));
   1323             SOC_IF_ERROR_RETURN
   1324                 (_phy_bcm54640_serdes_update(unit, port));
   1325 
   1326             /*
   1327              * When enable/disable the fiber port,
   1328              * the Tx disable bit of Expansion register 0x52 should be set also.
   1329              * Otherwise the following issue happens.
   1330              * After the fiber port is powered down
   1331              * (verified bit 11 MII cotnrol register 0 is set to 1),
   1332              * the link partner still shows linkup
   1333              */
   1334             SOC_IF_ERROR_RETURN
   1335                 (MODIFY_PHY54640_EXP_SERDES_SGMII_RX_CTRLr(unit, pc,
   1336                                                  pc->fiber.enable?0:1, 0x1));
   1337             SOC_IF_ERROR_RETURN
   1338                 (MODIFY_PHY54640_EXP_SERDES_SGMII_CTRLr(unit, pc,
   1339                                                  pc->fiber.enable?0:1, 0x1));
   1340 
   1341             if (pc->fiber.enable) {
   1342                 SOC_IF_ERROR_RETURN
   1343                     (_phy_54640_medium_config_update(unit, port, &pc->fiber));
   1344             }
   1345             LOG_INFO(BSL_LS_SOC_PHY,
   1346                      (BSL_META_U(unit,
   1347                                  "_phy_54640_link_auto_detect: u=%d p=%d [C->F]\n"),
   1348                       unit, port));
   1349         }
   1350     }
   1351     return SOC_E_NONE;
   1352 }
   1353 
   1354 /*
   1355  * Function:
   1356  *      phy_54640_link_get
   1357  * Purpose:
   1358  *      Determine the current link up/down status for a 54640 device.
   1359  * Parameters:
   1360  *      unit - StrataSwitch unit #.
   1361  *      port - StrataSwitch port #.
   1362  *      link - (OUT) Boolean, true indicates link established.
   1363  * Returns:
   1364  *      SOC_E_XXX
   1365  * Notes:
   1366  *      If using automedium, also switches the mode.
   1367  */
   1368 STATIC int
   1369 phy_54640_link_get(int unit, soc_port_t port, int *link)
   1370 {
   1371     phy_ctrl_t    *pc;
   1372     uint16         data; 
   1373     uint16         mask;
   1374 
   1375     pc    = EXT_PHY_SW_STATE(unit, port);
   1376     *link = FALSE;      /* Default return */
   1377 
   1378     if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_DISABLE)) {
   1379         return SOC_E_NONE;
   1380     }
   1381 
   1382     if (!pc->fiber.enable && !pc->copper.enable) {
   1383         return SOC_E_NONE;
   1384     }
   1385 
   1386     SOC_IF_ERROR_RETURN
   1387         (READ_PHY54640_EXP_OPT_MODE_STATr(unit, pc, &data));
   1388 
   1389     if (!(data & (1U << 5))) { /* no sync */
   1390         SOC_IF_ERROR_RETURN
   1391             (READ_PHY54640_1000X_MII_ANPr(unit, pc, &data));
   1392 
   1393         if (data & MII_ANP_SGMII_MODE) { /* Link partner is SGMII master */
   1394             /* Disable auto-detection between SGMII-slave and 1000BASE-X and set mode to 1000BASE-X */
   1395             SOC_IF_ERROR_RETURN
   1396                    (MODIFY_PHY54640_SGMII_SLAVEr(unit, pc, 0, 0x3));
   1397         }
   1398     }
   1399 
   1400     PHY_FLAGS_CLR(unit, port, PHY_FLAGS_MEDIUM_CHANGE);
   1401     if (pc->automedium) {
   1402         /* Check for medium change and update HW/SW accordingly. */
   1403         SOC_IF_ERROR_RETURN
   1404             (_phy_54640_medium_change(unit, port,FALSE));
   1405     }
   1406     if (PHY_COPPER_MODE(unit, port)) {
   1407         SOC_IF_ERROR_RETURN(phy_fe_ge_link_get(unit, port, link));
   1408     } else {
   1409         SOC_IF_ERROR_RETURN
   1410             (READ_PHY54640_1000X_MII_STATr(unit, pc, &data));
   1411         *link = (data & MII_STAT_LA) ? TRUE : FALSE;
   1412     }
   1413 
   1414     /* If preferred medium is up, disable the other medium 
   1415      * to prevent false link. */
   1416     if (pc->automedium) {
   1417         mask = MII_CTRL_PD;
   1418         if (pc->copper.preferred) {
   1419             if (pc->fiber.enable) {
   1420                 /* power down the serdes */
   1421                 data = (*link && PHY_COPPER_MODE(unit, port)) ? MII_CTRL_PD : 0;
   1422             } else {
   1423                 data = MII_CTRL_PD;
   1424             }
   1425             SOC_IF_ERROR_RETURN
   1426                 (MODIFY_PHY54640_1000X_MII_CTRLr(unit, pc, data, mask));
   1427         } else {  /* pc->fiber.preferred */
   1428             if (pc->copper.enable) {
   1429                 data = (*link && PHY_FIBER_MODE(unit, port)) ? MII_CTRL_PD : 0;
   1430             } else {
   1431                 data = MII_CTRL_PD;
   1432             }
   1433             SOC_IF_ERROR_RETURN
   1434                 (MODIFY_PHY54640_MII_CTRLr(unit, pc, data, mask));
   1435         }
   1436     }
   1437 
   1438     LOG_VERBOSE(BSL_LS_SOC_PHY,
   1439                 (BSL_META_U(unit,
   1440                             "phy_54640_link_get: u=%d p=%d mode=%s%s link=%d\n"),
   1441                  unit, port,
   1442                  PHY_COPPER_MODE(unit, port) ? "C" : "F",
   1443                  PHY_FIBER_100FX_MODE(unit, port)? "(100FX)" : "",
   1444                  *link));
   1445 
   1446     return SOC_E_NONE;
   1447 }
   1448 
   1449 /*
   1450  * Function:
   1451  *      phy_54640_duplex_set
   1452  * Purpose:
   1453  *      Set the current duplex mode (forced).
   1454  * Parameters:
   1455  *      unit - StrataSwitch unit #.
   1456  *      port - StrataSwitch port #.
   1457  *      duplex - Boolean, true indicates full duplex, false indicates half.
   1458  * Returns:
   1459  *      SOC_E_NONE
   1460  *      SOC_E_UNAVAIL - Half duplex requested, and not supported.
   1461  * Notes:
   1462  *      The duplex is set only for the ACTIVE medium.
   1463  *      No synchronization performed at this level.
   1464  *      Autonegotiation is not manipulated.
   1465  */
   1466 
   1467 STATIC int
   1468 phy_54640_duplex_set(int unit, soc_port_t port, int duplex)
   1469 {
   1470     int                  rv;
   1471     phy_ctrl_t    *pc;
   1472 
   1473     pc = EXT_PHY_SW_STATE(unit, port);
   1474     rv = SOC_E_NONE;
   1475     if (PHY_COPPER_MODE(unit, port)) {
   1476         /* Note: mac.c uses phy_5690_notify_duplex to update 5690 */
   1477         rv = phy_fe_ge_duplex_set(unit, port, duplex);
   1478         if (SOC_SUCCESS(rv)) {
   1479             pc->copper.force_duplex = duplex;
   1480         }
   1481     } else {    /* Fiber */
   1482         if (PHY_FIBER_100FX_MODE(unit, port)) {
   1483             SOC_IF_ERROR_RETURN
   1484                 (MODIFY_PHY54640_SERDES_100FX_CTRLr(unit, pc, duplex? 0x2:0x0, 0x2));
   1485             pc->fiber.force_duplex = duplex;
   1486         } else {
   1487             if(!duplex) {
   1488                 return SOC_E_UNAVAIL;
   1489             }
   1490         }
   1491     }
   1492 
   1493     LOG_INFO(BSL_LS_SOC_PHY,
   1494              (BSL_META_U(unit,
   1495                          "phy_54640_duplex_set: u=%d p=%d d=%d rv=%d\n"),
   1496               unit, port, duplex, rv));
   1497 
   1498     return rv;
   1499 }
   1500 
   1501 /*
   1502  * Function:
   1503  *      phy_54640_duplex_get
   1504  * Purpose:
   1505  *      Get the current operating duplex mode. If autoneg is enabled,
   1506  *      then operating mode is returned, otherwise forced mode is returned.
   1507  * Parameters:
   1508  *      unit - StrataSwitch unit #.
   1509  *      port - StrataSwitch port #.
   1510  *      duplex - (OUT) Boolean, true indicates full duplex, false
   1511  *              indicates half.
   1512  * Returns:
   1513  *      SOC_E_NONE
   1514  * Notes:
   1515  *      The duplex is retrieved for the ACTIVE medium.
   1516  *      No synchronization performed at this level. Autonegotiation is
   1517  *      not manipulated.
   1518  */
   1519 
   1520 STATIC int
   1521 phy_54640_duplex_get(int unit, soc_port_t port, int *duplex)
   1522 {
   1523     phy_ctrl_t    *pc;
   1524     uint16         opt_mode;
   1525 
   1526     pc = EXT_PHY_SW_STATE(unit, port);
   1527 
   1528     if (PHY_COPPER_MODE(unit, port)) {
   1529         return phy_fe_ge_duplex_get(unit, port, duplex);    
   1530     } else {    /* Fiber (1000BaseX, 100BaseFX, SGMII-Slave */
   1531         SOC_IF_ERROR_RETURN
   1532             (READ_PHY54640_EXP_OPT_MODE_STATr(unit, pc, &opt_mode));
   1533         /* The bit 12 of register EXP_OPT_MODE_STAT is serdes duplex */
   1534         *duplex = (opt_mode & 0x1000) ? TRUE : FALSE;
   1535     }
   1536         
   1537     return SOC_E_NONE;
   1538 }
   1539 
   1540 /*
   1541  * Function:
   1542  *      phy_54640_speed_set
   1543  * Purpose:
   1544  *      Set the current operating speed (forced).
   1545  * Parameters:
   1546  *      unit - StrataSwitch unit #.
   1547  *      port - StrataSwitch port #.
   1548  *      speed - Requested speed, only 1000 supported.
   1549  * Returns:
   1550  *      SOC_E_XXX
   1551  * Notes:
   1552  *      The speed is set only for the ACTIVE medium.
   1553  */
   1554 
   1555 STATIC int
   1556 phy_54640_speed_set(int unit, soc_port_t port, int speed)
   1557 {
   1558     int            rv; 
   1559     phy_ctrl_t    *pc;
   1560 
   1561     pc = EXT_PHY_SW_STATE(unit, port);
   1562     rv = SOC_E_NONE;
   1563     if (PHY_COPPER_MODE(unit, port)) {
   1564         /* Note: mac.c uses phy_5690_notify_speed to update 5690 */
   1565         rv = phy_fe_ge_speed_set(unit, port, speed);
   1566         if (SOC_SUCCESS(rv)) {
   1567             pc->copper.force_speed = speed;
   1568         }
   1569     } else {    /* Fiber */
   1570         if (speed == 100) {
   1571             rv = _phy_54640_fiber_100fx_setup(unit, port);
   1572         } else  if (speed == 0 || speed == 1000) {
   1573             rv = _phy_54640_fiber_1000x_setup(unit, port);
   1574         } else {
   1575             rv = SOC_E_CONFIG;
   1576         }
   1577         if (SOC_SUCCESS(rv)) {
   1578             pc->fiber.force_speed = speed;
   1579         }
   1580     }
   1581 
   1582     LOG_INFO(BSL_LS_SOC_PHY,
   1583              (BSL_META_U(unit,
   1584                          "phy_54640_speed_set: u=%d p=%d s=%d fiber=%d rv=%d\n"),
   1585               unit, port, speed, PHY_FIBER_MODE(unit, port), rv));
   1586 
   1587     return rv;
   1588 }
   1589 
   1590 /*
   1591  * Function:
   1592  *      phy_54640_speed_get
   1593  * Purpose:
   1594  *      Get the current operating speed for a 54640 device.
   1595  * Parameters:
   1596  *      unit - StrataSwitch unit #.
   1597  *      port - StrataSwitch port #.
   1598  *      duplex - (OUT) Boolean, true indicates full duplex, false
   1599  *              indicates half.
   1600  * Returns:
   1601  *      SOC_E_NONE
   1602  * Notes:
   1603  *      The speed is retrieved for the ACTIVE medium.
   1604  */
   1605 
   1606 STATIC int
   1607 phy_54640_speed_get(int unit, soc_port_t port, int *speed)
   1608 {
   1609     phy_ctrl_t    *pc;
   1610     uint16         opt_mode;
   1611 
   1612     pc = EXT_PHY_SW_STATE(unit, port);
   1613 
   1614     if (PHY_COPPER_MODE(unit, port)) {
   1615         return phy_fe_ge_speed_get(unit, port, speed);
   1616     } else {
   1617         *speed = 1000;
   1618         SOC_IF_ERROR_RETURN
   1619             (READ_PHY54640_EXP_OPT_MODE_STATr(unit, pc, &opt_mode));
   1620         switch(opt_mode & (0x3 << 13)) {
   1621         case (0 << 13):
   1622             *speed = 10;
   1623             break;
   1624         case (1 << 13):
   1625             *speed = 100;
   1626             break;
   1627         case (2 << 13):
   1628             *speed = 1000;
   1629             break;
   1630         default:
   1631             LOG_WARN(BSL_LS_SOC_PHY,
   1632                      (BSL_META_U(unit,
   1633                                  "phy_54640_speed_get: u=%d p=%d invalid speed\n"),
   1634                       unit, port));
   1635         }
   1636     }
   1637 
   1638     return SOC_E_NONE;
   1639 }
   1640 
   1641 /*
   1642  * Function:
   1643  *      phy_54640_master_set
   1644  * Purpose:
   1645  *      Set the current master mode
   1646  * Parameters:
   1647  *      unit - StrataSwitch unit #.
   1648  *      port - StrataSwitch port #.
   1649  *      master - SOC_PORT_MS_*
   1650  * Returns:
   1651  *      SOC_E_XXX
   1652  * Notes:
   1653  *      The master mode is set only for the ACTIVE medium.
   1654  */
   1655 STATIC int
   1656 phy_54640_master_set(int unit, soc_port_t port, int master)
   1657 {
   1658     int                  rv;
   1659     phy_ctrl_t    *pc;
   1660 
   1661     pc = EXT_PHY_SW_STATE(unit, port);
   1662     rv = SOC_E_NONE;
   1663     if (PHY_COPPER_MODE(unit, port)) {
   1664         rv = phy_fe_ge_master_set(unit, port, master);
   1665         if (SOC_SUCCESS(rv)) {
   1666             pc->copper.master = master;
   1667         }
   1668     }
   1669     LOG_INFO(BSL_LS_SOC_PHY,
   1670              (BSL_META_U(unit,
   1671                          "phy_54640_master_set: u=%d p=%d master=%d fiber=%d rv=%d\n"),
   1672               unit, port, master, PHY_FIBER_MODE(unit, port), rv));
   1673     return rv;
   1674 }
   1675 
   1676 /*
   1677  * Function:
   1678  *      phy_54640_master_get
   1679  * Purpose:
   1680  *      Get the current master mode for a 54640 device.
   1681  * Parameters:
   1682  *      unit - StrataSwitch unit #.
   1683  *      port - StrataSwitch port #.
   1684  *      master - (OUT) SOC_PORT_MS_*
   1685  * Returns:
   1686  *      SOC_E_NONE
   1687  * Notes:
   1688  *      The master mode is retrieved for the ACTIVE medium.
   1689  */
   1690 
   1691 STATIC int
   1692 phy_54640_master_get(int unit, soc_port_t port, int *master)
   1693 {
   1694     if (PHY_COPPER_MODE(unit, port)) {
   1695         return phy_fe_ge_master_get(unit, port, master);
   1696     } else {
   1697         *master = SOC_PORT_MS_NONE;
   1698         return SOC_E_NONE;
   1699     }
   1700 }
   1701 /*
   1702  * Function:
   1703  *      phy_54640_autoneg_set
   1704  * Purpose:
   1705  *      Enable or disable auto-negotiation on the specified port.
   1706  * Parameters:
   1707  *      unit - StrataSwitch unit #.
   1708  *      port - StrataSwitch port #.
   1709  *      autoneg - Boolean, if true, auto-negotiation is enabled
   1710  *              (and/or restarted). If false, autonegotiation is disabled.
   1711  * Returns:
   1712  *      SOC_E_XXX
   1713  * Notes:
   1714  *      The autoneg mode is set only for the ACTIVE medium.
   1715  */
   1716 
   1717 STATIC int
   1718 phy_54640_autoneg_set(int unit, soc_port_t port, int autoneg)
   1719 {
   1720     int          rv;
   1721     uint16       data, mask;
   1722     phy_ctrl_t  *pc;
   1723 
   1724     pc = EXT_PHY_SW_STATE(unit, port);
   1725     rv = SOC_E_NONE;
   1726     if ( PHY_COPPER_MODE(unit, port) ) {  /* copper mode */
   1727         rv = phy_fe_ge_an_set(unit, port, autoneg);
   1728         if (SOC_SUCCESS(rv)) {
   1729             pc->copper.autoneg_enable = autoneg ? TRUE : FALSE;
   1730         }
   1731     } else {                              /* fiber mode */
   1732         if (autoneg) {
   1733             /* When enabling autoneg, set the default speed to 1000Mbps first.
   1734              * PHY will not enable autoneg if the PHY is in 100FX mode.
   1735              */
   1736             SOC_IF_ERROR_RETURN
   1737                 (phy_54640_speed_set(unit, port, 1000));
   1738         }
   1739 
   1740         /* Disable/Enable auto-detection between SGMII-slave and 1000BASE-X */
   1741         /* SGMII Slave Reg.,  Reg. 0x1C, shadow 0x15 */
   1742         SOC_IF_ERROR_RETURN
   1743             (MODIFY_PHY54640_SGMII_SLAVEr(unit, pc, 0, 0x1));
   1744         SOC_IF_ERROR_RETURN
   1745             (phy_54640_ability_advert_set(unit, port, &(pc->fiber.advert_ability)));
   1746 
   1747         data = MII_CTRL_FD;
   1748         data |= (autoneg) ? MII_CTRL_AE | MII_CTRL_RAN : 0;
   1749         mask = MII_CTRL_AE | MII_CTRL_RAN | MII_CTRL_FD;
   1750         SOC_IF_ERROR_RETURN
   1751             (MODIFY_PHY54640_1000X_MII_CTRLr(unit, pc, data, mask));
   1752 
   1753         pc->fiber.autoneg_enable = autoneg ? TRUE : FALSE;
   1754     }
   1755  
   1756     LOG_INFO(BSL_LS_SOC_PHY,
   1757              (BSL_META_U(unit,
   1758                          "phy_54640_autoneg_set: u=%d p=%d autoneg=%d rv=%d\n"),
   1759               unit, port, autoneg, rv));
   1760 
   1761     return rv;
   1762 }
   1763 
   1764 /*
   1765  * Function:
   1766  *      phy_54640_autoneg_get
   1767  * Purpose:
   1768  *      Get the current auto-negotiation status (enabled/busy).
   1769  * Parameters:
   1770  *      unit - StrataSwitch unit #.
   1771  *      port - StrataSwitch port #.
   1772  *      autoneg - (OUT) if true, auto-negotiation is enabled.
   1773  *      autoneg_done - (OUT) if true, auto-negotiation is complete. This
   1774  *              value is undefined if autoneg == FALSE.
   1775  * Returns:
   1776  *      SOC_E_XXX
   1777  * Notes:
   1778  *      The autoneg mode is retrieved for the ACTIVE medium.
   1779  */
   1780 STATIC int
   1781 phy_54640_autoneg_get(int unit, soc_port_t port,
   1782                      int *autoneg, int *autoneg_done)
   1783 {
   1784     int           rv;
   1785     uint16        data;
   1786     phy_ctrl_t   *pc;
   1787 
   1788     pc = EXT_PHY_SW_STATE(unit, port);
   1789 
   1790     rv            = SOC_E_NONE;
   1791     *autoneg      = FALSE;
   1792     *autoneg_done = FALSE;
   1793     if (PHY_COPPER_MODE(unit, port)) {
   1794         rv = phy_fe_ge_an_get(unit, port, autoneg, autoneg_done);
   1795     } else {
   1796         /* autoneg is not enabled in 100FX mode */
   1797         if (PHY_FIBER_100FX_MODE(unit, port)) {
   1798             *autoneg = FALSE;
   1799             *autoneg_done = TRUE;
   1800             return rv;
   1801         }
   1802 
   1803         SOC_IF_ERROR_RETURN
   1804             (READ_PHY54640_1000X_MII_CTRLr(unit, pc, &data));
   1805         if (data & MII_CTRL_AE) {
   1806             *autoneg = TRUE;
   1807 
   1808             SOC_IF_ERROR_RETURN
   1809                 (READ_PHY54640_1000X_MII_STATr(unit, pc, &data));
   1810             if (data & MII_STAT_AN_DONE) {
   1811                 *autoneg_done = TRUE;
   1812             }
   1813         }
   1814     }
   1815     return rv;
   1816 }
   1817 
   1818 /*
   1819  * Function:
   1820  *      phy_54640_ability_advert_set
   1821  * Purpose:
   1822  *      Set the current advertisement for auto-negotiation.
   1823  * Parameters:
   1824  *      unit    - StrataSwitch unit #.
   1825  *      port    - StrataSwitch port #.
   1826  *      ability - Port ability indicating supported options/speeds.
   1827  * Returns:
   1828  *      SOC_E_XXX
   1829  */
   1830 STATIC int
   1831 phy_54640_ability_advert_set(int unit, soc_port_t port, soc_port_ability_t *ability)
   1832 {
   1833     int          rv;
   1834     phy_ctrl_t   *pc;
   1835     uint16       mii_adv, mii_gb_ctrl, eee_ability = 0;
   1836 
   1837     if (NULL == ability) {
   1838         return SOC_E_PARAM;
   1839     }
   1840 
   1841     pc = EXT_PHY_SW_STATE(unit, port);
   1842     rv = SOC_E_NONE;
   1843 
   1844     if ( PHY_COPPER_MODE(unit, port) ) {
   1845         mii_adv     = MII_ANA_ASF_802_3;
   1846         SOC_IF_ERROR_RETURN
   1847             (READ_PHY54640_MII_GB_CTRLr(unit, pc,  &mii_gb_ctrl));
   1848         mii_gb_ctrl &= ~(MII_GB_CTRL_ADV_1000HD | MII_GB_CTRL_ADV_1000FD);
   1849         mii_gb_ctrl |= MII_GB_CTRL_PT; /* repeater / switch port */
   1850 
   1851         if (ability->speed_half_duplex & SOC_PA_SPEED_10MB)  {
   1852             mii_adv |= MII_ANA_HD_10;
   1853         }
   1854         if (ability->speed_half_duplex & SOC_PA_SPEED_100MB) {
   1855             mii_adv |= MII_ANA_HD_100;
   1856         }
   1857         if (ability->speed_half_duplex & SOC_PA_SPEED_1000MB) {
   1858             mii_gb_ctrl |= MII_GB_CTRL_ADV_1000HD;
   1859         }
   1860         if (ability->speed_full_duplex & SOC_PA_SPEED_10MB)  {
   1861             mii_adv |= MII_ANA_FD_10;
   1862         }
   1863         if (ability->speed_full_duplex & SOC_PA_SPEED_100MB) {
   1864             mii_adv |= MII_ANA_FD_100;
   1865         }
   1866         if (ability->speed_full_duplex & SOC_PA_SPEED_1000MB) {
   1867             mii_gb_ctrl |= MII_GB_CTRL_ADV_1000FD;
   1868         }
   1869 
   1870         switch (ability->pause & (SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX)) {
   1871         case SOC_PA_PAUSE_TX:
   1872             mii_adv |= MII_ANA_ASYM_PAUSE;
   1873             break;
   1874         case SOC_PA_PAUSE_RX:
   1875             mii_adv |= MII_ANA_PAUSE | MII_ANA_ASYM_PAUSE;
   1876             break;
   1877         case SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX:
   1878             mii_adv |= MII_ANA_PAUSE;
   1879             break;
   1880         }
   1881 
   1882         /* EEE settings */
   1883         if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   1884             eee_ability = 0;
   1885             if (ability->eee & SOC_PA_EEE_1GB_BASET) {
   1886                 eee_ability |= 0x4;
   1887             }
   1888             if (ability->eee & SOC_PA_EEE_100MB_BASETX) {
   1889                 eee_ability |= 0x2;
   1890             }
   1891             if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_ENABLED)) {
   1892                 SOC_IF_ERROR_RETURN
   1893                     (MODIFY_PHY54640_EEE_ADVr(unit, pc, eee_ability, 0x0006));
   1894             } else {
   1895                 SOC_IF_ERROR_RETURN
   1896                     (MODIFY_PHY54640_EEE_ADVr(unit, pc, 0x0000, 0x0006));
   1897             }
   1898         }
   1899 
   1900         SOC_IF_ERROR_RETURN
   1901             (MODIFY_PHY54640_MII_ANAr(unit, pc, mii_adv,
   1902             MII_ANA_PAUSE|MII_ANA_ASYM_PAUSE|MII_ANA_FD_100|MII_ANA_HD_100|
   1903             MII_ANA_FD_10|MII_ANA_HD_10|MII_ANA_ASF_802_3));
   1904         SOC_IF_ERROR_RETURN
   1905             (MODIFY_PHY54640_MII_GB_CTRLr(unit, pc, mii_gb_ctrl,
   1906             MII_GB_CTRL_PT|MII_GB_CTRL_ADV_1000FD|MII_GB_CTRL_ADV_1000HD));
   1907 
   1908         /* update the PHY property software table (phy_ctrl_t) */
   1909         sal_memcpy( &(pc->copper.advert_ability), ability,
   1910                     sizeof(soc_port_ability_t) );
   1911 
   1912     } else { 
   1913         uint16  mii_adv, data;
   1914 
   1915         SOC_IF_ERROR_RETURN
   1916             (READ_PHY54640_1000X_MII_ANPr(unit, pc, &data));
   1917         if (data & MII_ANP_SGMII_MODE) { /* Link partner is SGMII master */
   1918             return SOC_E_NONE;
   1919         }
   1920 
   1921         mii_adv = MII_ANA_C37_FD;  /* Always advertise 1000X full duplex */
   1922         switch (ability->pause & (SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX)) {
   1923         case SOC_PA_PAUSE_TX:
   1924             mii_adv |= MII_ANA_C37_ASYM_PAUSE;
   1925             break;
   1926         case SOC_PA_PAUSE_RX:
   1927             mii_adv |= (MII_ANA_C37_ASYM_PAUSE | MII_ANA_C37_PAUSE);
   1928             break;
   1929         case SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX:
   1930             mii_adv |= MII_ANA_C37_PAUSE;
   1931             break;
   1932         }
   1933         /* Write to SerDes Auto-Neg Advertisement Reg */
   1934         rv = WRITE_PHY54640_1000X_MII_ANAr(unit, pc, mii_adv);
   1935 
   1936         /* update the PHY property software table (phy_ctrl_t) */
   1937         sal_memcpy( &(pc->fiber.advert_ability), ability,
   1938                     sizeof(soc_port_ability_t) );
   1939     }
   1940     return rv;
   1941 }
   1942 
   1943 /*
   1944  * Function:
   1945  *      phy_54640_ability_advert_get
   1946  * Purpose:
   1947  *      Get the current advertisement for auto-negotiation.
   1948  * Parameters:
   1949  *      unit    - StrataSwitch unit #.
   1950  *      port    - StrataSwitch port #.
   1951  *      ability - Port ability indicating supported options/speeds.
   1952  * Returns:
   1953  *      SOC_E_XXX
   1954  * Notes:
   1955  *      The advertisement is retrieved for the ACTIVE medium.
   1956  *      No synchronization performed at this level.
   1957  */
   1958 STATIC int
   1959 phy_54640_ability_advert_get(int unit, soc_port_t port, soc_port_ability_t *ability)
   1960 {
   1961     phy_ctrl_t *pc;
   1962     uint16      mii_ana;
   1963     uint16      mii_gb_ctrl;
   1964     uint16      eee_ability = 0;
   1965 
   1966     if (NULL == ability) {
   1967         return (SOC_E_PARAM);
   1968     }
   1969     sal_memset(ability, 0, sizeof(*ability));
   1970 
   1971     pc = EXT_PHY_SW_STATE(unit, port);
   1972 
   1973     if (PHY_COPPER_MODE(unit, port)) {
   1974 
   1975         SOC_IF_ERROR_RETURN
   1976             (READ_PHY54640_MII_ANAr(unit, pc, &mii_ana));
   1977         SOC_IF_ERROR_RETURN
   1978             (READ_PHY54640_MII_GB_CTRLr(unit, pc, &mii_gb_ctrl));
   1979 
   1980         sal_memset(ability, 0, sizeof(*ability));
   1981 
   1982         if (mii_ana & MII_ANA_HD_10) {
   1983            ability->speed_half_duplex |= SOC_PA_SPEED_10MB;
   1984         }
   1985         if (mii_ana & MII_ANA_HD_100) {
   1986           ability->speed_half_duplex |= SOC_PA_SPEED_100MB;
   1987         }
   1988         if (mii_ana & MII_ANA_FD_10) {
   1989           ability->speed_full_duplex |= SOC_PA_SPEED_10MB;
   1990         }
   1991         if (mii_ana & MII_ANA_FD_100) {
   1992           ability->speed_full_duplex |= SOC_PA_SPEED_100MB;
   1993         }
   1994 
   1995         switch (mii_ana & (MII_ANA_PAUSE | MII_ANA_ASYM_PAUSE)) {
   1996         case MII_ANA_PAUSE:
   1997             ability->pause = SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX;
   1998             break;
   1999         case MII_ANA_ASYM_PAUSE:
   2000             ability->pause = SOC_PA_PAUSE_TX;
   2001             break;
   2002         case MII_ANA_PAUSE | MII_ANA_ASYM_PAUSE:
   2003             ability->pause = SOC_PA_PAUSE_RX;
   2004             break;
   2005         }
   2006 
   2007         /* GE Specific values */
   2008 
   2009         if (mii_gb_ctrl & MII_GB_CTRL_ADV_1000HD) {
   2010            ability->speed_half_duplex |= SOC_PA_SPEED_1000MB;
   2011         }
   2012         if (mii_gb_ctrl & MII_GB_CTRL_ADV_1000FD) {
   2013            ability->speed_full_duplex |= SOC_PA_SPEED_1000MB;
   2014         }
   2015 
   2016         /* EEE settings */
   2017         if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   2018             SOC_IF_ERROR_RETURN
   2019                 (READ_PHY54640_EEE_ADVr(unit, pc, &eee_ability));
   2020             if (eee_ability & 0x04) {
   2021                 ability->eee |= SOC_PA_EEE_1GB_BASET;
   2022             }
   2023             if (eee_ability & 0x02) {
   2024                 ability->eee |= SOC_PA_EEE_100MB_BASETX;
   2025             }
   2026         }
   2027     } else {    /* PHY_FIBER_MODE */
   2028         uint16  mii_adv, data;
   2029         SOC_IF_ERROR_RETURN
   2030             (READ_PHY54640_1000X_MII_ANPr(unit, pc, &data));
   2031         if (data & MII_ANP_SGMII_MODE) { /* Link partner is SGMII master */
   2032             ability->speed_full_duplex = SOC_PA_SPEED_1000MB | SOC_PA_SPEED_100MB | SOC_PA_SPEED_10MB;
   2033             ability->speed_half_duplex = SOC_PA_SPEED_100MB | SOC_PA_SPEED_10MB;
   2034             ability->pause = SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX;
   2035         } else {
   2036             /* read the 1000BASE-X Auto-Neg Advertisement Reg ( Address 0x04 ) */
   2037             SOC_IF_ERROR_RETURN
   2038                 (READ_PHY54640_1000X_MII_ANAr(unit, pc, &mii_adv));
   2039 
   2040             if (mii_adv & MII_ANA_C37_FD) {
   2041                 ability->speed_full_duplex |= SOC_PA_SPEED_1000MB;
   2042             }
   2043             if (mii_adv & MII_ANA_C37_HD) {
   2044                 ability->speed_half_duplex |= SOC_PA_SPEED_1000MB;
   2045             }
   2046             switch (mii_adv & (MII_ANA_C37_PAUSE | MII_ANA_C37_ASYM_PAUSE)) {
   2047             case MII_ANA_C37_PAUSE:
   2048                 ability->pause = SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX;
   2049                 break;
   2050             case MII_ANA_C37_ASYM_PAUSE:
   2051                 ability->pause = SOC_PA_PAUSE_TX;
   2052                 break;
   2053             case (MII_ANA_C37_PAUSE | MII_ANA_C37_ASYM_PAUSE):
   2054                 ability->pause = SOC_PA_PAUSE_RX;
   2055                 break;
   2056             }
   2057         }
   2058     }
   2059     return SOC_E_NONE;
   2060 }
   2061 
   2062 /*
   2063  * Function:
   2064  *      phy_54640_ability_remote_get
   2065  * Purpose:
   2066  *      Get partners current advertisement for auto-negotiation.
   2067  * Parameters:
   2068  *      unit    - StrataSwitch unit #.
   2069  *      port    - StrataSwitch port #.
   2070  *      ability - Port ability indicating supported options/speeds.
   2071  * Returns:
   2072  *      SOC_E_XXX
   2073  * Notes:
   2074  *      The remote advertisement is retrieved for the ACTIVE medium.
   2075  *      No synchronization performed at this level. If Autonegotiation is
   2076  *      disabled or in progress, this routine will return an error.
   2077  */
   2078 
   2079 STATIC int
   2080 phy_54640_ability_remote_get(int unit, soc_port_t port, soc_port_ability_t *ability)
   2081 {
   2082     phy_ctrl_t       *pc;
   2083     uint16            mii_stat;
   2084     uint16            mii_anp;
   2085     uint16            mii_gb_stat;
   2086     uint16            eee_resolution;
   2087     uint16            data;
   2088 
   2089     if (NULL == ability) {
   2090         return (SOC_E_PARAM);
   2091     }
   2092     sal_memset(ability, 0, sizeof(*ability));
   2093 
   2094     pc = EXT_PHY_SW_STATE(unit, port);
   2095     if (PHY_COPPER_MODE(unit, port)) {
   2096         SOC_IF_ERROR_RETURN
   2097             (READ_PHY54640_MII_STATr(unit, pc, &mii_stat));
   2098 
   2099         if ((mii_stat & MII_STAT_AN_DONE & MII_STAT_LA)
   2100                    == (MII_STAT_AN_DONE & MII_STAT_LA)) {
   2101             /* Decode remote advertisement only when link is up and autoneg is
   2102              * completed.
   2103              */
   2104             SOC_IF_ERROR_RETURN
   2105                 (READ_PHY54640_MII_ANPr(unit, pc, &mii_anp));
   2106             SOC_IF_ERROR_RETURN
   2107                 (READ_PHY54640_MII_GB_STATr(unit, pc, &mii_gb_stat));
   2108 
   2109             if (mii_anp & MII_ANA_HD_10) {
   2110                 ability->speed_half_duplex |= SOC_PA_SPEED_10MB;
   2111             }
   2112             if (mii_anp & MII_ANA_HD_100) {
   2113                 ability->speed_half_duplex |= SOC_PA_SPEED_100MB;
   2114             }
   2115             if (mii_anp & MII_ANA_FD_10) {
   2116                ability->speed_full_duplex |= SOC_PA_SPEED_10MB;
   2117             }
   2118             if (mii_anp & MII_ANA_FD_100) {
   2119                ability->speed_full_duplex |= SOC_PA_SPEED_100MB;
   2120             }
   2121 
   2122             switch (mii_anp & (MII_ANA_PAUSE | MII_ANA_ASYM_PAUSE)) {
   2123             case MII_ANA_PAUSE:
   2124                 ability->pause = SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX;
   2125                 break;
   2126             case MII_ANA_ASYM_PAUSE:
   2127                 ability->pause = SOC_PA_PAUSE_TX;
   2128                 break;
   2129             case MII_ANA_PAUSE | MII_ANA_ASYM_PAUSE:
   2130                 ability->pause = SOC_PA_PAUSE_RX;
   2131                 break;
   2132            }
   2133 
   2134             /* GE Specific values */
   2135 
   2136             if (mii_gb_stat & MII_GB_STAT_LP_1000HD) {
   2137             ability->speed_half_duplex |= SOC_PA_SPEED_1000MB;
   2138             }
   2139             if (mii_gb_stat & MII_GB_STAT_LP_1000FD) {
   2140                 ability->speed_full_duplex |= SOC_PA_SPEED_1000MB;
   2141             }
   2142             /* EEE settings */
   2143             if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   2144                 SOC_IF_ERROR_RETURN
   2145                 (READ_PHY54640_EEE_RESOLUTION_STATr(unit, pc, &eee_resolution));
   2146                 if (eee_resolution & 0x04) {
   2147                     ability->eee |= SOC_PA_EEE_1GB_BASET;
   2148                 }
   2149                 if (eee_resolution & 0x02) {
   2150                     ability->eee |= SOC_PA_EEE_100MB_BASETX;
   2151                 }
   2152             }
   2153 
   2154         } else {
   2155             /* Simply return local abilities */
   2156             phy_54640_ability_advert_get(unit, port, ability);
   2157         }
   2158     } else {    /* PHY_FIBER_MODE */
   2159 
   2160         /* read the 1000BASE-X     Control Reg ( Address 0x00 ),
   2161          *   Auto-Neg Link Partner Ability Reg ( Address 0x05 )
   2162          */
   2163         SOC_IF_ERROR_RETURN
   2164             (READ_PHY54640_1000X_MII_CTRLr(unit, pc, &data));
   2165         if (!(data & MII_CTRL_AE)) {
   2166             return SOC_E_DISABLED;
   2167         }
   2168 
   2169         SOC_IF_ERROR_RETURN
   2170             (READ_PHY54640_1000X_MII_ANPr(unit, pc, &data));
   2171 
   2172         if (data & MII_ANP_SGMII_MODE) {
   2173             /* Unable to determine remote link partner's advertisement */ 
   2174             return SOC_E_NONE;
   2175         }
   2176         if (data & MII_ANP_C37_FD) {
   2177             ability->speed_full_duplex |= SOC_PA_SPEED_1000MB;
   2178         }
   2179         if (data & MII_ANP_C37_HD) {
   2180             ability->speed_half_duplex |= SOC_PA_SPEED_1000MB;
   2181         }
   2182         switch (data & (MII_ANP_C37_PAUSE | MII_ANP_C37_ASYM_PAUSE)) {
   2183         case MII_ANP_C37_PAUSE:
   2184             ability->pause = SOC_PA_PAUSE_TX | SOC_PA_PAUSE_RX;
   2185             break;
   2186         case MII_ANP_C37_ASYM_PAUSE:
   2187             ability->pause = SOC_PA_PAUSE_TX;
   2188             break;
   2189         case (MII_ANP_C37_PAUSE | MII_ANP_C37_ASYM_PAUSE):
   2190             ability->pause = SOC_PA_PAUSE_RX;
   2191             break;
   2192         }
   2193     }
   2194     LOG_INFO(BSL_LS_SOC_PHY,
   2195              (BSL_META_U(unit,
   2196                          "phy_54640_ability_remote_get:unit=%d p=%d pause=%08x sp=%08x\n"),
   2197               unit, port, ability->pause, ability->speed_full_duplex));
   2198 
   2199     return SOC_E_NONE;
   2200 }
   2201 
   2202 /*
   2203  * Function:
   2204  *      phy_54640_ability_local_get
   2205  * Purpose:
   2206  *      Get the device's complete abilities.
   2207  * Parameters:
   2208  *      unit - StrataSwitch unit #.
   2209  *      port - StrataSwitch port #.
   2210  *      ability - return device's abilities.
   2211  * Returns:
   2212  *      SOC_E_XXX
   2213  */
   2214 
   2215 STATIC int
   2216 _phy_54640_ability_cu_local_get(int unit, soc_port_t port, soc_port_ability_t *ability)
   2217 {
   2218     phy_ctrl_t *pc;
   2219 
   2220     pc = EXT_PHY_SW_STATE(unit, port);
   2221 
   2222     ability->speed_half_duplex  = SOC_PA_SPEED_100MB | SOC_PA_SPEED_10MB;
   2223     ability->speed_full_duplex  = SOC_PA_SPEED_1000MB |SOC_PA_SPEED_100MB | 
   2224                                   SOC_PA_SPEED_10MB;
   2225                                   
   2226     ability->pause     = SOC_PA_PAUSE | SOC_PA_PAUSE_ASYMM;
   2227     ability->interface = SOC_PA_INTF_SGMII;
   2228     ability->medium    = SOC_PA_MEDIUM_COPPER; 
   2229     ability->loopback  = SOC_PA_LB_PHY;
   2230     ability->flags     = SOC_PA_AUTONEG;
   2231     
   2232     /* EEE settings */
   2233     if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   2234             ability->eee |= SOC_PA_EEE_1GB_BASET | SOC_PA_EEE_100MB_BASETX;
   2235     }
   2236 
   2237     if (pc->automedium) {
   2238         ability->flags     |= SOC_PA_COMBO;
   2239     }
   2240     return SOC_E_NONE;
   2241 }
   2242 
   2243 STATIC int
   2244 _phy_54640_ability_fiber_local_get(int unit, soc_port_t port, soc_port_ability_t *ability)
   2245 {
   2246     phy_ctrl_t *pc;
   2247     uint16  data;
   2248 
   2249     pc = EXT_PHY_SW_STATE(unit, port);
   2250     
   2251 
   2252     SOC_IF_ERROR_RETURN
   2253         (READ_PHY54640_1000X_MII_ANPr(unit, pc, &data));
   2254     if (data & MII_ANP_SGMII_MODE) { /* Link partner is SGMII master */
   2255         ability->speed_full_duplex = SOC_PA_SPEED_1000MB | SOC_PA_SPEED_100MB | SOC_PA_SPEED_10MB;
   2256         ability->speed_half_duplex = SOC_PA_SPEED_100MB | SOC_PA_SPEED_10MB;
   2257     } else {
   2258         ability->speed_half_duplex  = SOC_PA_SPEED_100MB;
   2259         ability->speed_full_duplex  = SOC_PA_SPEED_1000MB |SOC_PA_SPEED_100MB;
   2260     }
   2261     ability->pause     = SOC_PA_PAUSE | SOC_PA_PAUSE_ASYMM;
   2262     ability->interface = SOC_PA_INTF_SGMII;
   2263     ability->medium    = SOC_PA_MEDIUM_FIBER; 
   2264     ability->loopback  = SOC_PA_LB_PHY;
   2265     ability->flags     = SOC_PA_AUTONEG;
   2266 
   2267     if (pc->automedium) {
   2268         ability->flags     |= SOC_PA_COMBO;
   2269     }
   2270     return SOC_E_NONE;
   2271 }
   2272 
   2273 
   2274 STATIC int
   2275 phy_54640_ability_local_get(int unit, soc_port_t port, soc_port_ability_t *ability)
   2276 {
   2277     if (NULL == ability) {
   2278         return (SOC_E_PARAM);
   2279     }
   2280     sal_memset(ability, 0, sizeof(*ability));
   2281 
   2282     if (PHY_COPPER_MODE(unit, port)) {
   2283         SOC_IF_ERROR_RETURN
   2284             (_phy_54640_ability_cu_local_get(unit, port, ability));
   2285     } else {
   2286         SOC_IF_ERROR_RETURN
   2287             (_phy_54640_ability_fiber_local_get(unit, port, ability));
   2288     }
   2289     return SOC_E_NONE;
   2290 }
   2291 
   2292 /*
   2293  * Function:
   2294  *      phy_54640_lb_set
   2295  * Purpose:
   2296  *      Set the local PHY loopback mode.
   2297  * Parameters:
   2298  *      unit - StrataSwitch unit #.
   2299  *      port - StrataSwitch port #.
   2300  *      loopback - Boolean: true = enable loopback, false = disable.
   2301  * Returns:
   2302  *      SOC_E_XXX
   2303  * Notes:
   2304  *      The loopback mode is set only for the ACTIVE medium.
   2305  *      No synchronization performed at this level.
   2306  */
   2307 
   2308 STATIC int
   2309 phy_54640_lb_set(int unit, soc_port_t port, int enable)
   2310 {
   2311     int            rv;
   2312     uint16         data;
   2313     phy_ctrl_t    *pc;
   2314 
   2315     pc = EXT_PHY_SW_STATE(unit, port);
   2316     rv = SOC_E_NONE;
   2317 
   2318 #if AUTO_MDIX_WHEN_AN_DIS
   2319     {
   2320         /* Disable Auto-MDIX When autoneg disabled */
   2321         /* Enable Auto-MDIX When autoneg disabled */
   2322         data = (enable) ? 0x0000 : 0x0200;
   2323         SOC_IF_ERROR_RETURN
   2324             (MODIFY_PHY_54640_MII_MISC_CTRLr(unit, pc, data, 0x0200)); 
   2325     }
   2326 #endif
   2327 
   2328     if (PHY_COPPER_MODE(unit, port)) {
   2329         rv = phy_fe_ge_lb_set(unit, port, enable);
   2330     } else { 
   2331         data = (enable) ? MII_CTRL_LE : 0;
   2332         rv   = MODIFY_PHY54640_1000X_MII_CTRLr
   2333                   (unit, pc, data, MII_CTRL_LE);
   2334         if (enable && SOC_SUCCESS(rv)) {
   2335             uint16        stat;
   2336             int           link;
   2337             soc_timeout_t to;
   2338 
   2339             /* Wait up to 5000 msec for link up */
   2340             soc_timeout_init(&to, 5000000, 0);
   2341             link = 0;
   2342             while (!soc_timeout_check(&to)) {
   2343                 rv = READ_PHY54640_1000X_MII_STATr(unit, pc, &stat);
   2344                 link = stat & MII_STAT_LA;
   2345                 if (link || SOC_FAILURE(rv)) {
   2346                     break;
   2347                 }
   2348             }
   2349             if (!link) {
   2350                 LOG_WARN(BSL_LS_SOC_PHY,
   2351                          (BSL_META_U(unit,
   2352                                      "phy_54640_lb_set: u=%d p=%d TIMEOUT\n"),
   2353                           unit, port));
   2354 
   2355                 rv = SOC_E_TIMEOUT;
   2356             }
   2357         }
   2358     }
   2359     LOG_INFO(BSL_LS_SOC_PHY,
   2360              (BSL_META_U(unit,
   2361                          "phy_54640_lb_set: u=%d p=%d en=%d rv=%d\n"), 
   2362               unit, port, enable, rv));
   2363 
   2364     return rv; 
   2365 }
   2366 
   2367 /*
   2368  * Function:
   2369  *      phy_54640_lb_get
   2370  * Purpose:
   2371  *      Get the local PHY loopback mode.
   2372  * Parameters:
   2373  *      unit - StrataSwitch unit #.
   2374  *      port - StrataSwitch port #.
   2375  *      loopback - (OUT) Boolean: true = enable loopback, false = disable.
   2376  * Returns:
   2377  *      SOC_E_XXX
   2378  * Notes:
   2379  *      The loopback mode is retrieved for the ACTIVE medium.
   2380  */
   2381 
   2382 STATIC int
   2383 phy_54640_lb_get(int unit, soc_port_t port, int *enable)
   2384 {
   2385     uint16               data;
   2386     phy_ctrl_t    *pc;
   2387 
   2388     pc = EXT_PHY_SW_STATE(unit, port);
   2389 
   2390     if (PHY_COPPER_MODE(unit, port)) {
   2391         SOC_IF_ERROR_RETURN
   2392             (phy_fe_ge_lb_get(unit, port, enable));
   2393     } else {
   2394        SOC_IF_ERROR_RETURN
   2395            (READ_PHY54640_1000X_MII_CTRLr(unit, pc, &data));
   2396         *enable = (data & MII_CTRL_LE) ? 1 : 0;
   2397     }
   2398 
   2399     return SOC_E_NONE; 
   2400 }
   2401 
   2402 /*
   2403  * Function:
   2404  *      phy_54640_interface_set
   2405  * Purpose:
   2406  *      Set the current operating mode of the PHY.
   2407  * Parameters:
   2408  *      unit - StrataSwitch unit #.
   2409  *      port - StrataSwitch port #.
   2410  *      pif - one of SOC_PORT_IF_*
   2411  * Returns:
   2412  *      SOC_E_NONE - success
   2413  *      SOC_E_UNAVAIL - unsupported interface
   2414  */
   2415 
   2416 STATIC int
   2417 phy_54640_interface_set(int unit, soc_port_t port, soc_port_if_t pif)
   2418 {
   2419     COMPILER_REFERENCE(unit);
   2420     COMPILER_REFERENCE(port);
   2421 
   2422     switch (pif) {
   2423     case SOC_PORT_IF_SGMII:
   2424     case SOC_PORT_IF_GMII:
   2425         return SOC_E_NONE;
   2426     default:
   2427         return SOC_E_UNAVAIL;
   2428     }
   2429 }
   2430 
   2431 /*
   2432  * Function:
   2433  *      phy_54640_interface_get
   2434  * Purpose:
   2435  *      Get the current operating mode of the PHY.
   2436  * Parameters:
   2437  *      unit - StrataSwitch unit #.
   2438  *      port - StrataSwitch port #.
   2439  *      pif - (OUT) one of SOC_PORT_IF_*
   2440  * Returns:
   2441  *      SOC_E_NONE
   2442  */
   2443 
   2444 STATIC int
   2445 phy_54640_interface_get(int unit, soc_port_t port, soc_port_if_t *pif)
   2446 {
   2447     COMPILER_REFERENCE(unit);
   2448     COMPILER_REFERENCE(port);
   2449 
   2450     *pif = SOC_PORT_IF_SGMII;
   2451 
   2452     return SOC_E_NONE;
   2453 }
   2454 
   2455 
   2456 /*
   2457  * Function:
   2458  *      phy_54640_mdix_set
   2459  * Description:
   2460  *      Set the Auto-MDIX mode of a port/PHY
   2461  * Parameters:
   2462  *      unit - Device number
   2463  *      port - Port number
   2464  *      mode - One of:
   2465  *              SOC_PORT_MDIX_AUTO
   2466  *                      Enable auto-MDIX when autonegotiation is enabled
   2467  *              SOC_PORT_MDIX_FORCE_AUTO
   2468  *                      Enable auto-MDIX always
   2469  *              SOC_PORT_MDIX_NORMAL
   2470  *                      Disable auto-MDIX
   2471  *              SOC_PORT_MDIX_XOVER
   2472  *                      Disable auto-MDIX, and swap cable pairs
   2473  * Return Value:
   2474  *      SOC_E_XXX
   2475  */
   2476 STATIC int
   2477 phy_54640_mdix_set(int unit, soc_port_t port, soc_port_mdix_t mode)
   2478 {
   2479     phy_ctrl_t    *pc;
   2480     int                  speed;
   2481 
   2482     pc = EXT_PHY_SW_STATE(unit, port);
   2483     if (PHY_COPPER_MODE(unit, port)) {
   2484         switch (mode) {
   2485         case SOC_PORT_MDIX_AUTO:
   2486             /* Clear bit 14 for automatic MDI crossover */
   2487             SOC_IF_ERROR_RETURN
   2488                 (MODIFY_PHY54640_MII_ECRr(unit, pc, 0, 0x4000));
   2489 
   2490             /* Clear bit 9 to disable forced auto MDI xover */
   2491             SOC_IF_ERROR_RETURN
   2492                 (MODIFY_PHY54640_MII_MISC_CTRLr(unit, pc, 0, 0x0200));
   2493             break;
   2494 
   2495         case SOC_PORT_MDIX_FORCE_AUTO:
   2496             /* Clear bit 14 for automatic MDI crossover */
   2497             SOC_IF_ERROR_RETURN
   2498                 (MODIFY_PHY54640_MII_ECRr(unit, pc, 0, 0x4000));
   2499 
   2500             /* Set bit 9 to disable forced auto MDI xover */
   2501             SOC_IF_ERROR_RETURN
   2502                 (MODIFY_PHY54640_MII_MISC_CTRLr(unit, pc, 0x0200, 0x0200));
   2503             break;
   2504 
   2505         case SOC_PORT_MDIX_NORMAL:
   2506             SOC_IF_ERROR_RETURN(phy_54640_speed_get(unit, port, &speed));
   2507             if (speed == 0 || speed == 10 || speed == 100) {
   2508                 /* Set bit 14 for automatic MDI crossover */
   2509                 SOC_IF_ERROR_RETURN
   2510                     (MODIFY_PHY54640_MII_ECRr(unit, pc, 0x4000, 0x4000));
   2511 
   2512                 SOC_IF_ERROR_RETURN
   2513                     (WRITE_PHY54640_TEST1r(unit, pc, 0));
   2514             } else {
   2515                 return SOC_E_UNAVAIL;
   2516             }
   2517             break;
   2518 
   2519         case SOC_PORT_MDIX_XOVER:
   2520             SOC_IF_ERROR_RETURN(phy_54640_speed_get(unit, port, &speed));
   2521             if (speed == 0 || speed == 10 || speed == 100) {
   2522                 /* Set bit 14 for automatic MDI crossover */
   2523                 SOC_IF_ERROR_RETURN
   2524                     (MODIFY_PHY54640_MII_ECRr(unit, pc, 0x4000, 0x4000));
   2525 
   2526                 SOC_IF_ERROR_RETURN
   2527                     (WRITE_PHY54640_TEST1r(unit, pc, 0x0080));
   2528             } else {
   2529                 return SOC_E_UNAVAIL;
   2530             }
   2531             break;
   2532 
   2533         default:
   2534             return SOC_E_PARAM;
   2535         }
   2536         pc->copper.mdix = mode;
   2537     } else {    /* fiber */
   2538         if (mode != SOC_PORT_MDIX_NORMAL) {
   2539             return SOC_E_UNAVAIL;
   2540         }
   2541     }
   2542     return SOC_E_NONE;
   2543 }        
   2544 
   2545 /*
   2546  * Function:
   2547  *      phy_54640_mdix_get
   2548  * Description:
   2549  *      Get the Auto-MDIX mode of a port/PHY
   2550  * Parameters:
   2551  *      unit - Device number
   2552  *      port - Port number
   2553  *      mode - (Out) One of:
   2554  *              SOC_PORT_MDIX_AUTO
   2555  *                      Enable auto-MDIX when autonegotiation is enabled
   2556  *              SOC_PORT_MDIX_FORCE_AUTO
   2557  *                      Enable auto-MDIX always
   2558  *              SOC_PORT_MDIX_NORMAL
   2559  *                      Disable auto-MDIX
   2560  *              SOC_PORT_MDIX_XOVER
   2561  *                      Disable auto-MDIX, and swap cable pairs
   2562  * Return Value:
   2563  *      SOC_E_XXX
   2564  */
   2565 STATIC int
   2566 phy_54640_mdix_get(int unit, soc_port_t port, soc_port_mdix_t *mode)
   2567 {
   2568     phy_ctrl_t    *pc;
   2569     int            speed;
   2570 
   2571     if (mode == NULL) {
   2572         return SOC_E_PARAM;
   2573     }
   2574 
   2575     pc = EXT_PHY_SW_STATE(unit, port);
   2576 
   2577     if (PHY_COPPER_MODE(unit, port)) {
   2578         SOC_IF_ERROR_RETURN(phy_54640_speed_get(unit, port, &speed));
   2579         if (speed == 1000) {
   2580            *mode = SOC_PORT_MDIX_AUTO;
   2581         } else {
   2582             *mode = pc->copper.mdix;
   2583         }
   2584     } else {
   2585         *mode = SOC_PORT_MDIX_NORMAL;
   2586     }
   2587 
   2588     return SOC_E_NONE;
   2589 }    
   2590 
   2591 /*
   2592  * Function:
   2593  *      phy_54640_mdix_status_get
   2594  * Description:
   2595  *      Get the current MDIX status on a port/PHY
   2596  * Parameters:
   2597  *      unit    - Device number
   2598  *      port    - Port number
   2599  *      status  - (OUT) One of:
   2600  *              SOC_PORT_MDIX_STATUS_NORMAL
   2601  *                      Straight connection
   2602  *              SOC_PORT_MDIX_STATUS_XOVER
   2603  *                      Crossover has been performed
   2604  * Return Value:
   2605  *      SOC_E_XXX
   2606  */
   2607 STATIC int
   2608 phy_54640_mdix_status_get(int unit, soc_port_t port, 
   2609                          soc_port_mdix_status_t *status)
   2610 {
   2611     phy_ctrl_t    *pc;
   2612     uint16          data;
   2613 
   2614     if (status == NULL) {
   2615         return SOC_E_PARAM;
   2616     }
   2617 
   2618     pc = EXT_PHY_SW_STATE(unit, port);
   2619 
   2620     if (PHY_COPPER_MODE(unit, port)) {
   2621         SOC_IF_ERROR_RETURN
   2622             (READ_PHY54640_MII_ESRr(unit, pc, &data));
   2623         if (data & 0x2000) {
   2624             *status = SOC_PORT_MDIX_STATUS_XOVER;
   2625         } else {
   2626             *status = SOC_PORT_MDIX_STATUS_NORMAL;
   2627         }
   2628     } else {
   2629         *status = SOC_PORT_MDIX_STATUS_NORMAL;
   2630     }
   2631 
   2632     return SOC_E_NONE;
   2633 }    
   2634 
   2635 STATIC int
   2636 _phy_54640_power_mode_set (int unit, soc_port_t port, int mode)
   2637 {
   2638     phy_ctrl_t    *pc;
   2639 
   2640     pc       = EXT_PHY_SW_STATE(unit, port);
   2641 
   2642     if (pc->power_mode == mode) {
   2643         return SOC_E_NONE;
   2644     }
   2645 
   2646     if (mode == SOC_PHY_CONTROL_POWER_LOW) {
   2647         /* enable dsp clock */
   2648         SOC_IF_ERROR_RETURN
   2649             (MODIFY_PHY54640_MII_AUX_CTRLr(unit,pc,0x0c00,0x0c00));
   2650 
   2651         /* enable low power 136 */
   2652         SOC_IF_ERROR_RETURN
   2653             (MODIFY_PHY54640_AUTO_POWER_DOWNr(unit,pc,0x80,0x80));
   2654 
   2655         /* reduce tx bias current to -20% */
   2656         SOC_IF_ERROR_RETURN
   2657             (WRITE_PHY_REG(unit, pc, 0x17, 0x0f75));
   2658         SOC_IF_ERROR_RETURN
   2659             (WRITE_PHY_REG(unit, pc, 0x15, 0x1555));
   2660 
   2661         /* disable dsp clock */
   2662         SOC_IF_ERROR_RETURN
   2663             (MODIFY_PHY54640_MII_AUX_CTRLr(unit,pc,0x0400,0x0c00));
   2664         pc->power_mode = mode;
   2665 
   2666     } else if (mode == SOC_PHY_CONTROL_POWER_FULL) {
   2667 
   2668         /* back to normal mode */
   2669         /* enable dsp clock */
   2670         SOC_IF_ERROR_RETURN
   2671             (MODIFY_PHY54640_MII_AUX_CTRLr(unit,pc,0x0c00,0x0c00));
   2672 
   2673         /* disable low power 136 */
   2674         SOC_IF_ERROR_RETURN
   2675             (MODIFY_PHY54640_AUTO_POWER_DOWNr(unit,pc,0x00,0x80));
   2676 
   2677         /* set tx bias current to nominal */
   2678         SOC_IF_ERROR_RETURN
   2679             (WRITE_PHY_REG(unit, pc, 0x17, 0x0f75));
   2680         SOC_IF_ERROR_RETURN
   2681             (WRITE_PHY_REG(unit, pc, 0x15, 0x0));
   2682 
   2683         /* disable dsp clock */
   2684         SOC_IF_ERROR_RETURN
   2685             (MODIFY_PHY54640_MII_AUX_CTRLr(unit,pc,0x0400,0x0c00));
   2686         pc->power_mode = mode;
   2687     }
   2688     return SOC_E_NONE;
   2689 }
   2690 
   2691 /*
   2692  * Function:
   2693  *      _phy_54640_eee_enable
   2694  * Purpose:
   2695  *      Enable or disable EEE (Native)
   2696  * Parameters:
   2697  *      unit   - StrataSwitch unit #.
   2698  *      port   - StrataSwitch port #.
   2699  *      enable - Enable Native EEE
   2700  * Returns:
   2701  *      SOC_E_NONE
   2702  */
   2703 STATIC int
   2704 _phy_54640_eee_enable(int unit, soc_port_t port, int enable)
   2705 {
   2706     phy_ctrl_t *pc;
   2707     
   2708     if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   2709         return SOC_E_FAIL;
   2710     }
   2711     pc = EXT_PHY_SW_STATE(unit, port);
   2712 
   2713     if (enable == 1) {
   2714         if (PHY_IS_BCM54640E_A0A1(pc)) {
   2715             SOC_IF_ERROR_RETURN
   2716                 (MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, 0x0c00, 0x0c00));
   2717 
   2718             SOC_IF_ERROR_RETURN(
   2719                 WRITE_PHY_REG(unit, pc, 0x17, 0x0ffe));
   2720             SOC_IF_ERROR_RETURN(
   2721                 WRITE_PHY_REG(unit, pc, 0x15, 0x0100));
   2722 
   2723             SOC_IF_ERROR_RETURN(
   2724                 WRITE_PHY_REG(unit, pc, 0x17, 0x0fff));
   2725             SOC_IF_ERROR_RETURN(
   2726                 WRITE_PHY_REG(unit, pc, 0x15, 0x4000));
   2727 
   2728             SOC_IF_ERROR_RETURN(
   2729                 WRITE_PHY_REG(unit, pc, 0x17, 0x2022));
   2730             SOC_IF_ERROR_RETURN(
   2731                 WRITE_PHY_REG(unit, pc, 0x15, 0x01f1));
   2732 
   2733             SOC_IF_ERROR_RETURN(
   2734                 WRITE_PHY_REG(unit, pc, 0x17, 0x4021));
   2735             SOC_IF_ERROR_RETURN(
   2736                 WRITE_PHY_REG(unit, pc, 0x15, 0x0887));
   2737 
   2738             SOC_IF_ERROR_RETURN(
   2739                 WRITE_PHY_REG(unit, pc, 0x17, 0x2021));
   2740             SOC_IF_ERROR_RETURN(
   2741                 WRITE_PHY_REG(unit, pc, 0x15, 0x8983));
   2742 
   2743             SOC_IF_ERROR_RETURN(
   2744                 WRITE_PHY_REG(unit, pc, 0x17, 0x0021));
   2745             SOC_IF_ERROR_RETURN(
   2746                 WRITE_PHY_REG(unit, pc, 0x15, 0x4600));
   2747 
   2748             SOC_IF_ERROR_RETURN(
   2749                 WRITE_PHY_REG(unit, pc, 0x17, 0x0e40));
   2750             SOC_IF_ERROR_RETURN(
   2751                 WRITE_PHY_REG(unit, pc, 0x15, 0x0000));
   2752 
   2753             SOC_IF_ERROR_RETURN
   2754                 (MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, 0x0400, 0x0c00));
   2755 
   2756             SOC_IF_ERROR_RETURN
   2757                 (MODIFY_PHY54640_EEE_TEST_CTRL_Br(unit, pc, 0xc000, 0xc000));
   2758             SOC_IF_ERROR_RETURN
   2759                 (MODIFY_PHY54640_EEE_TEST_CTRL_Ar(unit, pc, 0xf300, 0xf300));
   2760         } else {
   2761             SOC_IF_ERROR_RETURN
   2762                 (MODIFY_PHY54640_EEE_803Dr(unit, pc, 0xc000, 0xc000)); /* 7.803d */
   2763         }
   2764         SOC_IF_ERROR_RETURN
   2765             (MODIFY_PHY54640_EEE_ADVr(unit, pc, 0x0006, 0x0006));
   2766         pc->copper.advert_ability.eee |= (SOC_PA_EEE_1GB_BASET | SOC_PA_EEE_100MB_BASETX);
   2767 
   2768         SOC_IF_ERROR_RETURN
   2769             (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 0x1, 0x1)); /* exp af */
   2770 
   2771     } else {  /* enable != 1 */
   2772         if (PHY_IS_BCM54640E_A0A1(pc)) {
   2773             SOC_IF_ERROR_RETURN
   2774                 (MODIFY_PHY54640_EEE_TEST_CTRL_Br(unit, pc, 0x0000, 0xc000));
   2775             SOC_IF_ERROR_RETURN
   2776                 (MODIFY_PHY54640_EEE_TEST_CTRL_Ar(unit, pc, 0x0000, 0xf300));
   2777         } else {
   2778             SOC_IF_ERROR_RETURN
   2779                 (MODIFY_PHY54640_EEE_803Dr(unit, pc, 0x0000, 0xc000)); /* 7.803d */
   2780         }
   2781         SOC_IF_ERROR_RETURN
   2782             (MODIFY_PHY54640_EEE_ADVr(unit, pc, 0x0000, 0x0006));
   2783         pc->copper.advert_ability.eee &= ~(SOC_PA_EEE_1GB_BASET | SOC_PA_EEE_100MB_BASETX);
   2784 
   2785         SOC_IF_ERROR_RETURN
   2786             (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 0x0, 0x1)); /* exp af */
   2787     }
   2788 
   2789     return SOC_E_NONE;
   2790 }
   2791 
   2792 /*
   2793  * Function:
   2794  *      _phy_54640_eee_auto_enable
   2795  * Purpose:
   2796  *      Enable or disable EEE (Native)
   2797  * Parameters:
   2798  *      unit   - StrataSwitch unit #.
   2799  *      port   - StrataSwitch port #.
   2800  *      enable - Enable Native EEE
   2801  * Returns:
   2802  *      SOC_E_NONE
   2803  */
   2804 STATIC int
   2805 _phy_54640_eee_auto_enable(int unit, soc_port_t port, int enable)
   2806 {
   2807     phy_ctrl_t *pc;
   2808     
   2809     if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   2810         return SOC_E_FAIL;
   2811     }
   2812     pc = EXT_PHY_SW_STATE(unit, port);
   2813 
   2814     if (enable == 1) {
   2815         if (PHY_IS_BCM54640E_A0A1(pc)) {
   2816             SOC_IF_ERROR_RETURN
   2817                 (MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, 0x0c00, 0x0c00));
   2818 
   2819             SOC_IF_ERROR_RETURN(
   2820                 WRITE_PHY_REG(unit, pc, 0x17, 0x0ffe));
   2821             SOC_IF_ERROR_RETURN(
   2822                 WRITE_PHY_REG(unit, pc, 0x15, 0x0900));
   2823 
   2824             SOC_IF_ERROR_RETURN(
   2825                 WRITE_PHY_REG(unit, pc, 0x17, 0x0fff));
   2826             SOC_IF_ERROR_RETURN(
   2827                 WRITE_PHY_REG(unit, pc, 0x15, 0x4000));
   2828 
   2829             SOC_IF_ERROR_RETURN(
   2830                 WRITE_PHY_REG(unit, pc, 0x17, 0x2022));
   2831             SOC_IF_ERROR_RETURN(
   2832                 WRITE_PHY_REG(unit, pc, 0x15, 0x01f1));
   2833 
   2834             SOC_IF_ERROR_RETURN(
   2835                 WRITE_PHY_REG(unit, pc, 0x17, 0x2021));
   2836             SOC_IF_ERROR_RETURN(
   2837                 WRITE_PHY_REG(unit, pc, 0x15, 0x8983));
   2838 
   2839             SOC_IF_ERROR_RETURN(
   2840                 WRITE_PHY_REG(unit, pc, 0x17, 0x4021));
   2841             SOC_IF_ERROR_RETURN(
   2842                 WRITE_PHY_REG(unit, pc, 0x15, 0x0887));
   2843 
   2844             SOC_IF_ERROR_RETURN(
   2845                 WRITE_PHY_REG(unit, pc, 0x17, 0x0021));
   2846             SOC_IF_ERROR_RETURN(
   2847                 WRITE_PHY_REG(unit, pc, 0x15, 0x4600));
   2848 
   2849             SOC_IF_ERROR_RETURN
   2850                 (MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, 0x0400, 0x0c00));
   2851 
   2852             SOC_IF_ERROR_RETURN
   2853                 (MODIFY_PHY54640_SGMII_SLAVEr(unit, pc, 0x02, 0x02));
   2854 
   2855             SOC_IF_ERROR_RETURN
   2856                 (MODIFY_PHY54640_MODE_CTRLr(unit, pc, 0x00, 0x06));
   2857 
   2858             SOC_IF_ERROR_RETURN(
   2859                 WRITE_PHY_REG(unit, pc, 0x17, 0x0faf));
   2860             SOC_IF_ERROR_RETURN(
   2861                 WRITE_PHY_REG(unit, pc, 0x15, 0x0001));
   2862 
   2863             SOC_IF_ERROR_RETURN
   2864                 (MODIFY_PHY54640_EEE_TEST_CTRL_Br(unit, pc, 0xc000, 0xc000));
   2865 
   2866             SOC_IF_ERROR_RETURN
   2867                 (MODIFY_PHY54640_EEE_TEST_CTRL_Ar(unit, pc, 0xf300, 0x1300));
   2868 
   2869         } else {  /* ! PHY_IS_BCM54640E_A0A1 */
   2870             SOC_IF_ERROR_RETURN
   2871                 (MODIFY_PHY54640_EEE_803Dr(unit, pc, 0xc000, 0xc000)); /* 7.803d */
   2872         }
   2873         SOC_IF_ERROR_RETURN
   2874             (MODIFY_PHY54640_EEE_ADVr(unit, pc, 0x0006, 0x0006));
   2875         pc->copper.advert_ability.eee |= (SOC_PA_EEE_1GB_BASET | SOC_PA_EEE_100MB_BASETX);
   2876 
   2877         SOC_IF_ERROR_RETURN
   2878            (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 7,
   2879                                                    0x0001, 0x0003));
   2880         SOC_IF_ERROR_RETURN
   2881             (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 0x1, 0x3)); /* exp af */
   2882 
   2883     } else {  /* enable != 1 */
   2884         SOC_IF_ERROR_RETURN
   2885             (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 0x0, 0x3)); /* exp af */
   2886         SOC_IF_ERROR_RETURN
   2887             (MODIFY_PHY54640_EEE_ADVr(unit, pc, 0x0000, 0x0006));
   2888         pc->copper.advert_ability.eee &= ~(SOC_PA_EEE_1GB_BASET | SOC_PA_EEE_100MB_BASETX);
   2889 
   2890         if (PHY_IS_BCM54640E_A0A1(pc)) {
   2891             SOC_IF_ERROR_RETURN
   2892                 (MODIFY_PHY54640_EEE_TEST_CTRL_Br(unit, pc, 0x0000, 0xc000)); /* 7.8031 */
   2893             SOC_IF_ERROR_RETURN
   2894                 (MODIFY_PHY54640_EEE_TEST_CTRL_Ar(unit, pc, 0x0000, 0xf300)); /* 7.8030 2 ops */
   2895         } else {
   2896             SOC_IF_ERROR_RETURN
   2897                 (MODIFY_PHY54640_EEE_803Dr(unit, pc, 0x0000, 0xc000)); /* 7.803d */
   2898         }
   2899     }
   2900 
   2901     return SOC_E_NONE;
   2902 }
   2903 
   2904 /*
   2905  * Function:
   2906  *      phy_54640_toplvl_reg_read
   2907  * Purpose:
   2908  *      Read a top level register from a supporting chip
   2909  * Parameters:
   2910  *      unit - SOC Unit #.
   2911  *      port - Port number.
   2912  *      primary_port - Primary Port number.
   2913  *      reg_offset  - Offset to the reg
   2914  *      data  - Pointer to data returned
   2915  * Returns:
   2916  *      SOC_E_XXX
   2917  */
   2918 int
   2919 phy_54640_toplvl_reg_read(int unit, soc_port_t port, soc_port_t primary_port, 
   2920                           uint8 reg_offset, uint16 *data)
   2921 
   2922 {
   2923     phy_ctrl_t    *pc, *pc_port1, *pc_port3;
   2924     uint16 reg_data, status;
   2925     soc_phy_chip_info_t *chip_info;
   2926     int         rv = SOC_E_NONE;
   2927 
   2928     if ((primary_port == -1) || (SOC_PHY_INFO(unit, primary_port).chip_info == NULL)) {
   2929         return SOC_E_FAIL;
   2930     }
   2931 
   2932     chip_info = SOC_PHY_INFO(unit, primary_port).chip_info;
   2933 
   2934     if ((chip_info->offset_to_port[0] == -1) ||
   2935         (chip_info->offset_to_port[2] == -1)) {
   2936 
   2937         return SOC_E_FAIL;
   2938     }
   2939 
   2940     pc       = EXT_PHY_SW_STATE(unit, port);
   2941     pc_port1 = EXT_PHY_SW_STATE(unit, chip_info->offset_to_port[0]);
   2942     pc_port3 = EXT_PHY_SW_STATE(unit, chip_info->offset_to_port[2]);
   2943 
   2944     if ((!pc) || (!pc_port1) || (!pc_port3)) {
   2945         return SOC_E_FAIL;
   2946     }
   2947 
   2948     INT_MCU_LOCK(unit);
   2949     /* Switch to MAC SGMII */
   2950    
   2951     if (PHY_IS_BCM54640E(pc)) {
   2952         /* Write register 0x17 with the top level reg offset to write (handled in PHY54640_REG_WRITE) */
   2953         /* Write register 0x15 the value to write to the top level register offset */
   2954         rv = PHY54640_REG_WRITE(unit, pc_port1, 0, 0x0D01, 0x15, 0x0003);
   2955         if (SOC_FAILURE(rv)) {
   2956 	        INT_MCU_UNLOCK(unit);
   2957 
   2958             return rv;
   2959         }
   2960     } else {
   2961         rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xd040);
   2962         if (SOC_FAILURE(rv)) {
   2963             INT_MCU_UNLOCK(unit);
   2964             return rv;
   2965         }
   2966 
   2967         rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xd020);
   2968         if (SOC_FAILURE(rv)) {
   2969             INT_MCU_UNLOCK(unit);
   2970             return rv;
   2971         }
   2972     }
   2973 
   2974     reg_data = 0xAC00 | (reg_offset & 0x7f);
   2975 
   2976     rv = pc->write(unit, pc_port1->phy_id, 0x1c, reg_data);
   2977     if (SOC_FAILURE(rv)) {
   2978         INT_MCU_UNLOCK(unit);
   2979         return rv;
   2980     }
   2981 
   2982     /* Switch back to MDI */
   2983     if (PHY_IS_BCM54640E(pc)) {
   2984         /* Write register 0x17 with the top level reg offset to write (handled in PHY54640_REG_WRITE) */
   2985         /* Write register 0x15 the value to write to the top level register offset */
   2986         rv = PHY54640_REG_WRITE(unit, pc_port1, 0, 0x0D01, 0x15, 0x0001);
   2987         if (SOC_FAILURE(rv)) {
   2988             INT_MCU_UNLOCK(unit);
   2989 
   2990             return rv;
   2991         }
   2992     } else {
   2993         rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xd04f);
   2994         if (SOC_FAILURE(rv)) {
   2995             INT_MCU_UNLOCK(unit);
   2996             return rv;
   2997         }
   2998 
   2999         rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xd00f);
   3000         if (SOC_FAILURE(rv)) {
   3001             INT_MCU_UNLOCK(unit);
   3002             return rv;
   3003         }
   3004 
   3005         rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xd000);
   3006         if (SOC_FAILURE(rv)) {
   3007             INT_MCU_UNLOCK(unit);
   3008             return rv;
   3009         }
   3010     }
   3011 
   3012     /* Read data from Top level MII Status register(0x15h) */
   3013     rv = pc->write(unit, pc_port3->phy_id, 0x17, 0x0F0B);
   3014     if (SOC_FAILURE(rv)) {
   3015         INT_MCU_UNLOCK(unit);
   3016         return rv;
   3017     }
   3018     rv = pc->read(unit, pc_port3->phy_id, 0x15, &status);
   3019     INT_MCU_UNLOCK(unit);
   3020     if (SOC_FAILURE(rv)) {
   3021         return rv;
   3022     }
   3023     *data = (status & 0xff);
   3024 
   3025     return SOC_E_NONE;
   3026 }
   3027 
   3028 /*
   3029  * Function:
   3030  *      phy_54640_toplvl_reg_write
   3031  * Purpose:
   3032  *      Write to a top level register from a supporting chip
   3033  * Parameters:
   3034  *      unit - SOC Unit #.
   3035  *      port - Port number.
   3036  *      primary_port - Primary Port number.
   3037  *      reg_offset  - Offset to the reg
   3038  *      data  - Data to be written
   3039  * Returns:
   3040  *      SOC_E_XXX
   3041  */
   3042 int
   3043 phy_54640_toplvl_reg_write(int unit, soc_port_t port, soc_port_t primary_port,
   3044                            uint8 reg_offset, uint16 data)
   3045 
   3046 {
   3047     phy_ctrl_t    *pc, *pc_port1, *pc_port2;
   3048     uint16 reg_data;
   3049     soc_phy_chip_info_t *chip_info;
   3050     int         rv = SOC_E_NONE;
   3051 
   3052     if ((primary_port == -1) || (SOC_PHY_INFO(unit, primary_port).chip_info == NULL)) {
   3053         return SOC_E_FAIL;
   3054     }
   3055 
   3056     chip_info = SOC_PHY_INFO(unit, primary_port).chip_info;
   3057 
   3058     if ((chip_info->offset_to_port[0] == -1) ||
   3059         (chip_info->offset_to_port[1] == -1)) {
   3060 
   3061         return SOC_E_FAIL;
   3062     }
   3063 
   3064     pc       = EXT_PHY_SW_STATE(unit, port);
   3065     pc_port1 = EXT_PHY_SW_STATE(unit, chip_info->offset_to_port[0]);
   3066     pc_port2 = EXT_PHY_SW_STATE(unit, chip_info->offset_to_port[1]);
   3067 
   3068     if ((!pc) || (!pc_port1) || (!pc_port2)) {
   3069         return SOC_E_FAIL;
   3070     }
   3071 
   3072     /* Write Data to port2, register 0x1C, shadow 0x0c */
   3073     INT_MCU_LOCK(unit);
   3074 
   3075     /* Switch to MAC SGMII */
   3076     if (PHY_IS_BCM54640E(pc)) {
   3077         /* Write register 0x17 with the top level reg offset to write (handled in PHY54640_REG_WRITE) */
   3078         /* Write register 0x15 the value to write to the top level register offset */
   3079         rv = PHY54640_REG_WRITE(unit, pc_port1, 0, 0x0D01, 0x15, 0x0003);
   3080         if (SOC_FAILURE(rv)) {
   3081 	        INT_MCU_UNLOCK(unit);
   3082             return rv;
   3083         }
   3084     } else {
   3085         rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xd040);
   3086         if (SOC_FAILURE(rv)) {
   3087             INT_MCU_UNLOCK(unit);
   3088             return rv;
   3089         }
   3090 
   3091         rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xd020);
   3092         if (SOC_FAILURE(rv)) {
   3093             INT_MCU_UNLOCK(unit);
   3094             return rv;
   3095         }
   3096     }
   3097 
   3098     reg_data = (0xB000 | (data & 0xff));
   3099     rv = pc->write(unit, pc_port2->phy_id, 0x1c, reg_data);
   3100     if (SOC_FAILURE(rv)) {
   3101         INT_MCU_UNLOCK(unit);
   3102         return rv;
   3103     }
   3104 
   3105     /* Write Reg address to Port 1's register 0x1C, shadow 0x0B */
   3106     /* Enable Write ( Port 1's register 0x1C, shadow 0x0B) Bit 7 = 1 */
   3107     reg_data = (0xAC80 | reg_offset);
   3108     rv = pc->write(unit, pc_port1->phy_id, 0x1c, reg_data);
   3109     if (SOC_FAILURE(rv)) {
   3110         INT_MCU_UNLOCK(unit);
   3111         return rv;
   3112     }
   3113 
   3114     /* Disable Write ( Port 1's register 0x1C, shadow 0x0B) Bit 7 = 0 */
   3115     rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xAC00);
   3116     if (SOC_FAILURE(rv)) {
   3117         INT_MCU_UNLOCK(unit);
   3118         return rv;
   3119     }
   3120 
   3121     /* Switch to MDI SGMII */
   3122     if (PHY_IS_BCM54640E(pc)) {
   3123         /* Write register 0x17 with the top level reg offset to write (handled in PHY54640_REG_WRITE) */
   3124         /* Write register 0x15 the value to write to the top level register offset */
   3125         rv = PHY54640_REG_WRITE(unit, pc_port1, 0, 0x0D01, 0x15, 0x0001);
   3126         if (SOC_FAILURE(rv)) {
   3127 	        INT_MCU_UNLOCK(unit);
   3128             return rv;
   3129         }
   3130     } else {
   3131         rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xd04f);
   3132         if (SOC_FAILURE(rv)) {
   3133             INT_MCU_UNLOCK(unit);
   3134             return rv;
   3135         }
   3136 
   3137         rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xd00f);
   3138         if (SOC_FAILURE(rv)) {
   3139             INT_MCU_UNLOCK(unit);
   3140             return rv;
   3141         }
   3142 
   3143         rv = pc->write(unit, pc_port1->phy_id, 0x1c, 0xd000);
   3144     }
   3145 
   3146     INT_MCU_UNLOCK(unit);
   3147 
   3148     return rv;
   3149 }
   3150 
   3151 
   3152 STATIC int
   3153 phy_54640_control_linkdown_transmit_set(int unit, soc_port_t port, 
   3154                                        uint32 value)
   3155 {
   3156     phy_ctrl_t  *pc;
   3157     int         speed;             
   3158     int         rv = SOC_E_NONE;
   3159 
   3160     if (PHY_COPPER_MODE(unit, port)) {
   3161         return SOC_E_UNAVAIL;
   3162     }
   3163 
   3164     SOC_IF_ERROR_RETURN
   3165         (phy_54640_speed_get(unit, port, &speed));
   3166 
   3167     pc = EXT_PHY_SW_STATE(unit, port);
   3168 
   3169     switch (speed) {
   3170         
   3171     case 1000:
   3172 
   3173         if (value) {
   3174             SOC_IF_ERROR_RETURN
   3175                 (MODIFY_PHY54640_1000X_MII_CTRLr(unit, pc, 0, MII_CTRL_AE));
   3176             /* clear bits 1,2,3,5 and 6 */
   3177             SOC_IF_ERROR_RETURN
   3178                 (MODIFY_PHY54640_MISC_1000X_CTRL2r(unit, pc, 0x0000, 0x006e));
   3179             SOC_IF_ERROR_RETURN
   3180                 (MODIFY_PHY54640_1000X_MII_CTRLr(unit, pc, (1U << 5), (1U << 5)));
   3181         } else {
   3182             SOC_IF_ERROR_RETURN
   3183                 (MODIFY_PHY54640_1000X_MII_CTRLr(unit, pc, 0, (1U << 5)));
   3184             SOC_IF_ERROR_RETURN
   3185                 (WRITE_PHY54640_1000X_MII_CTRLr(unit, pc, 0x1140));
   3186             SOC_IF_ERROR_RETURN
   3187                 (WRITE_PHY54640_1000X_MII_CTRLr(unit, pc, 0x1940));
   3188             /* set bits 1,2,3 and 5 while clearing bit 6 */
   3189             SOC_IF_ERROR_RETURN
   3190                 (MODIFY_PHY54640_MISC_1000X_CTRL2r(unit, pc, 0x002e, 0x006e));
   3191             if (!pc->fiber.autoneg_enable) {
   3192                 SOC_IF_ERROR_RETURN
   3193                     (MODIFY_PHY54640_1000X_MII_CTRLr(unit, pc, 0, MII_CTRL_AE));
   3194             }
   3195         }
   3196         break;
   3197         
   3198     default:
   3199         rv = SOC_E_UNAVAIL;
   3200     }
   3201 
   3202     return rv;
   3203 }
   3204 
   3205 STATIC int
   3206 phy_54640_control_linkdown_transmit_get(int unit, soc_port_t port, 
   3207                                        uint32 *value)
   3208 {
   3209     phy_ctrl_t  *pc;
   3210     int         speed;             
   3211     uint16      data;
   3212     int         rv = SOC_E_NONE;
   3213 
   3214     if (PHY_COPPER_MODE(unit, port)) {
   3215         return SOC_E_UNAVAIL;
   3216     }
   3217 
   3218     SOC_IF_ERROR_RETURN
   3219         (phy_54640_speed_get(unit, port, &speed));
   3220 
   3221     pc = EXT_PHY_SW_STATE(unit, port);
   3222 
   3223     switch (speed) {
   3224 
   3225     case 1000:
   3226         /* Check 1000-X Uni-Tx bit */
   3227         SOC_IF_ERROR_RETURN
   3228             (READ_PHY54640_1000X_MII_CTRLr(unit, pc, &data));
   3229         *value = (data & (1U << 5)) ? 1 : 0;
   3230         break;
   3231 
   3232     default:
   3233         rv = SOC_E_UNAVAIL;
   3234     }
   3235 
   3236     return rv;
   3237 }
   3238 
   3239 
   3240 /*
   3241  * Function:
   3242  *      phy_54640_control_set
   3243  * Purpose:
   3244  *      Configure PHY device specific control fucntion.
   3245  * Parameters:
   3246  *      unit  - StrataSwitch unit #.
   3247  *      port  - StrataSwitch port #.
   3248  *      type  - Control to update
   3249  *      value - New setting for the control
   3250  * Returns:
   3251  *      SOC_E_NONE
   3252  */
   3253 STATIC int
   3254 phy_54640_control_set(int unit, soc_port_t port,
   3255                      soc_phy_control_t type, uint32 value)
   3256 {
   3257     int rv;
   3258     phy_ctrl_t *pc;
   3259     uint16 data;
   3260     soc_port_t primary;
   3261     int offset, saved_autoneg;
   3262 
   3263     PHY_CONTROL_TYPE_CHECK(type);
   3264 
   3265     pc = EXT_PHY_SW_STATE(unit, port);
   3266     rv = SOC_E_NONE;
   3267     switch(type) {
   3268 
   3269         case SOC_PHY_CONTROL_POWER:
   3270             rv = _phy_54640_power_mode_set(unit,port,value);
   3271         break;
   3272 
   3273         case SOC_PHY_CONTROL_PORT_PRIMARY:
   3274                 SOC_IF_ERROR_RETURN
   3275                     (soc_phyctrl_primary_set(unit, port, (soc_port_t)value));
   3276                 break;
   3277 
   3278         case SOC_PHY_CONTROL_PORT_OFFSET:
   3279                 SOC_IF_ERROR_RETURN
   3280                     (soc_phyctrl_offset_set(unit, port, (int)value));
   3281             break;
   3282 
   3283         case SOC_PHY_CONTROL_CLOCK_ENABLE:
   3284         SOC_IF_ERROR_RETURN
   3285             (soc_phyctrl_primary_get(unit, port, &primary));
   3286         SOC_IF_ERROR_RETURN
   3287             (soc_phyctrl_offset_get(unit, port, &offset));
   3288         SOC_IF_ERROR_RETURN
   3289             (phy_54640_toplvl_reg_read(unit, port, primary, 0x0, &data));
   3290         if ( value ) {
   3291             SOC_IF_ERROR_RETURN
   3292                 (phy_54640_toplvl_reg_write(unit, port, primary, 0x0,
   3293                      ( data & 0xf0 ) | ( offset & 0x7 )));
   3294         } else {
   3295             SOC_IF_ERROR_RETURN
   3296                 (phy_54640_toplvl_reg_write(unit, port, primary, 0x0,
   3297                      ( data & 0xf0 ) | 0x8 ));
   3298         }
   3299         break;
   3300 
   3301         case SOC_PHY_CONTROL_CLOCK_SECONDARY_ENABLE:
   3302         SOC_IF_ERROR_RETURN
   3303             (soc_phyctrl_primary_get(unit, port, &primary));
   3304         SOC_IF_ERROR_RETURN
   3305             (soc_phyctrl_offset_get(unit, port, &offset));
   3306         SOC_IF_ERROR_RETURN
   3307             (phy_54640_toplvl_reg_read(unit, port, primary, 0x0, &data));
   3308         if ( value ) {
   3309             SOC_IF_ERROR_RETURN
   3310                 (phy_54640_toplvl_reg_write(unit, port, primary, 0x0,
   3311                      ( data & 0x0f ) | (( offset & 0x7 )<<4 )));
   3312         } else {
   3313             SOC_IF_ERROR_RETURN
   3314                 (phy_54640_toplvl_reg_write(unit, port, primary, 0x0,
   3315                      ( data & 0x0f ) | (0x8<<4) ));
   3316         }
   3317         break;
   3318 
   3319         case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT:
   3320             rv = phy_54640_control_linkdown_transmit_set(unit, port, value);
   3321             break;
   3322 
   3323         case SOC_PHY_CONTROL_EEE:
   3324             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3325                 rv = SOC_E_UNAVAIL;
   3326             }
   3327 
   3328             if (value == 1) {
   3329                 /* Enable Native EEE */
   3330                 SOC_IF_ERROR_RETURN
   3331                     (_phy_54640_eee_enable(unit, port, 1));
   3332                 saved_autoneg = pc->copper.autoneg_enable;
   3333                 /* Initiate AN */
   3334                 SOC_IF_ERROR_RETURN
   3335                     (phy_54640_autoneg_set(unit, port, 1));
   3336                 pc->copper.autoneg_enable = saved_autoneg;
   3337                 PHY_FLAGS_SET(unit, pc->port, PHY_FLAGS_EEE_ENABLED);
   3338                 PHY_FLAGS_CLR(unit, pc->port, PHY_FLAGS_EEE_MODE);
   3339             } else {
   3340                 SOC_IF_ERROR_RETURN
   3341                     (_phy_54640_eee_enable(unit, port, 0));
   3342                 /* Initiate AN if administratively enabled */
   3343                 SOC_IF_ERROR_RETURN
   3344                     (phy_54640_autoneg_set(unit, port, pc->copper.autoneg_enable ? 1 : 0));
   3345                 PHY_FLAGS_CLR(unit, pc->port, PHY_FLAGS_EEE_ENABLED);
   3346             }
   3347 
   3348             break;
   3349 
   3350         case SOC_PHY_CONTROL_EEE_AUTO:
   3351             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3352                 rv = SOC_E_UNAVAIL;
   3353             }
   3354             if (value == 1) {
   3355                 /* Disable Native EEE */
   3356                 SOC_IF_ERROR_RETURN
   3357                     (_phy_54640_eee_enable(unit, port, 0));
   3358                 /* Enable Auto EEE */
   3359                 SOC_IF_ERROR_RETURN
   3360                     (_phy_54640_eee_auto_enable(unit, port, 1));
   3361                 saved_autoneg = pc->copper.autoneg_enable;
   3362                 /* Initiate AN */
   3363                 SOC_IF_ERROR_RETURN
   3364                     (phy_54640_autoneg_set(unit, port, 1));
   3365                 pc->copper.autoneg_enable = saved_autoneg;
   3366                 PHY_FLAGS_SET(unit, pc->port, PHY_FLAGS_EEE_ENABLED);
   3367                 PHY_FLAGS_SET(unit, pc->port, PHY_FLAGS_EEE_MODE);
   3368             } else {
   3369                 /* Disable Auto EEE */
   3370                 SOC_IF_ERROR_RETURN
   3371                     (_phy_54640_eee_auto_enable(unit, port, 0));
   3372                 /* Initiate AN if administratively enabled */
   3373                 SOC_IF_ERROR_RETURN
   3374                     (phy_54640_autoneg_set(unit, port, pc->copper.autoneg_enable ? 1 : 0));
   3375                 PHY_FLAGS_CLR(unit, pc->port, PHY_FLAGS_EEE_ENABLED);
   3376             }
   3377             break;
   3378 
   3379         case SOC_PHY_CONTROL_EEE_AUTO_IDLE_THRESHOLD:
   3380             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3381                 rv = SOC_E_UNAVAIL;
   3382             }
   3383 
   3384             if (value > 7) { /* Values needs to between 0 to 7 (inclusive) */
   3385                 rv = SOC_E_CONFIG;
   3386             }
   3387 
   3388             SOC_IF_ERROR_RETURN
   3389                (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 7,
   3390                                                        value<<8, 0x0700));
   3391             /* Setting GPHY core, MII buffer register as well */
   3392             SOC_IF_ERROR_RETURN
   3393                (MODIFY_PHY54640_EXP_MII_BUF_CNTL_STAT1r(unit, pc, 
   3394                                                         value<<8, 0x0700));
   3395             break;
   3396 
   3397 
   3398         case SOC_PHY_CONTROL_EEE_AUTO_FIXED_LATENCY:
   3399             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3400                 rv = SOC_E_UNAVAIL;
   3401             }
   3402             /* Fixed latency can have only 0 or 1 value  */
   3403             if ((value != FALSE) && (value != TRUE)) {
   3404                 return SOC_E_PARAM;
   3405             }
   3406             if (value == FALSE) { 
   3407                 /* Sets Variable Latency */
   3408                 SOC_IF_ERROR_RETURN
   3409                    (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 7,
   3410                                                        0x0004, 0x0004));
   3411                 /* Setting GPHY core, MII buffer register as well */
   3412                 SOC_IF_ERROR_RETURN
   3413                    (MODIFY_PHY54640_EXP_MII_BUF_CNTL_STAT1r(unit, pc,
   3414                                                             0x0004, 0x0004));
   3415             } else {
   3416                 /* Sets Fixed Latency */
   3417                 SOC_IF_ERROR_RETURN
   3418                    (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 7,
   3419                                                        0x0000, 0x0004));
   3420                 /* Setting GPHY core, MII buffer register as well */
   3421                 SOC_IF_ERROR_RETURN
   3422                    (MODIFY_PHY54640_EXP_MII_BUF_CNTL_STAT1r(unit, pc, 
   3423                                                             0x0000, 0x0004));
   3424             } 
   3425             break;
   3426 
   3427 
   3428         case SOC_PHY_CONTROL_EEE_AUTO_BUFFER_LIMIT:
   3429             /* Buffer limit modification is not allowed */
   3430 
   3431         case SOC_PHY_CONTROL_EEE_TRANSMIT_WAKE_TIME:
   3432         case SOC_PHY_CONTROL_EEE_RECEIVE_WAKE_TIME:
   3433         case SOC_PHY_CONTROL_EEE_TRANSMIT_SLEEP_TIME:
   3434         case SOC_PHY_CONTROL_EEE_RECEIVE_SLEEP_TIME:
   3435         case SOC_PHY_CONTROL_EEE_TRANSMIT_QUIET_TIME:
   3436         case SOC_PHY_CONTROL_EEE_RECEIVE_QUIET_TIME:
   3437         case SOC_PHY_CONTROL_EEE_TRANSMIT_REFRESH_TIME:
   3438             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3439                 rv = SOC_E_UNAVAIL;
   3440             }
   3441 
   3442             /* Time modification not allowed, IEEE EEE spec constants */
   3443             rv = SOC_E_UNAVAIL;
   3444             break;
   3445 
   3446         case SOC_PHY_CONTROL_EEE_STATISTICS_CLEAR:
   3447             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3448                 rv = SOC_E_UNAVAIL;
   3449             }
   3450             
   3451             if (value == 1) {
   3452                 /* Clearing the stats: Enable clear on Read  */
   3453                 SOC_IF_ERROR_RETURN
   3454                    (MODIFY_PHY54640_EXP_MII_BUF_CNTL_STAT1r(unit, pc, 
   3455                                                             0x1000, 0x1000));
   3456 
   3457                 /* Read all the stats to clear */
   3458                 SOC_IF_ERROR_RETURN
   3459                     (READ_PHY54640_EXP_EEE_TX_EVENTSr(unit, pc, &data));
   3460                 SOC_IF_ERROR_RETURN
   3461                     (READ_PHY54640_EXP_EEE_TX_EVENTSr(unit, pc, &data));
   3462                 SOC_IF_ERROR_RETURN
   3463                     (READ_PHY54640_EXP_EEE_TX_DURATIONr(unit, pc, &data));
   3464                 SOC_IF_ERROR_RETURN
   3465                     (READ_PHY54640_EXP_EEE_TX_DURATIONr(unit, pc, &data));
   3466                 SOC_IF_ERROR_RETURN
   3467                     (READ_PHY54640_EXP_EEE_RX_EVENTSr(unit, pc, &data));
   3468                 SOC_IF_ERROR_RETURN
   3469                     (READ_PHY54640_EXP_EEE_RX_EVENTSr(unit, pc, &data));
   3470                 SOC_IF_ERROR_RETURN
   3471                     (READ_PHY54640_EXP_EEE_RX_DURATIONr(unit, pc, &data));
   3472                 SOC_IF_ERROR_RETURN
   3473                     (READ_PHY54640_EXP_EEE_RX_DURATIONr(unit, pc, &data));
   3474 
   3475                 /* Disable Clear on Read  */
   3476                 SOC_IF_ERROR_RETURN
   3477                    (MODIFY_PHY54640_EXP_MII_BUF_CNTL_STAT1r(unit, pc, 
   3478                                                             0x0000, 0x1000));
   3479             }
   3480 
   3481             break;
   3482     case SOC_PHY_CONTROL_LOOPBACK_EXTERNAL:
   3483         if (value) {
   3484             int saved_autoneg, saved_speed;
   3485 
   3486             saved_autoneg = pc->copper.autoneg_enable;
   3487             SOC_IF_ERROR_RETURN
   3488                 (phy_54640_autoneg_set(unit, port, 0));
   3489             pc->copper.autoneg_enable = saved_autoneg;
   3490             
   3491             saved_speed = pc->copper.force_speed;
   3492             SOC_IF_ERROR_RETURN
   3493                 (phy_54640_speed_set(unit, port, 1000));
   3494             pc->copper.force_speed = saved_speed;
   3495 
   3496             SOC_IF_ERROR_RETURN
   3497                 (MODIFY_PHY54640_MII_GB_CTRLr(unit, pc, (1U << 12 | 1U << 11), (1U << 12 | 1U << 11)));
   3498             SOC_IF_ERROR_RETURN
   3499                 (MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, (1U << 15 | 1U << 10), (1U << 15 | 1U << 10)));
   3500         } else {
   3501             SOC_IF_ERROR_RETURN
   3502                 (MODIFY_PHY54640_MII_GB_CTRLr(unit, pc, 0, (1U << 12 | 1U << 11)));
   3503             SOC_IF_ERROR_RETURN
   3504                 (MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, 0, (1U << 15 | 1U << 10)));
   3505             SOC_IF_ERROR_RETURN
   3506                 (phy_54640_speed_set(unit, port, pc->copper.force_speed));
   3507             SOC_IF_ERROR_RETURN
   3508                 (phy_54640_autoneg_set(unit, port, pc->copper.autoneg_enable ? 1 : 0));
   3509         }
   3510         break;
   3511     default:
   3512         rv = SOC_E_UNAVAIL;
   3513         break;
   3514     }
   3515     return rv;
   3516 }
   3517 /*
   3518  * Function:
   3519  *      phy_54640_control_get
   3520  * Purpose:
   3521  *      Get current control settign of the PHY.
   3522  * Parameters:
   3523  *      unit  - StrataSwitch unit #.
   3524  *      port  - StrataSwitch port #.
   3525  *      type  - Control to update
   3526  *      value - (OUT)Current setting for the control
   3527  * Returns:
   3528  *      SOC_E_NONE
   3529  */
   3530 STATIC int
   3531 phy_54640_control_get(int unit, soc_port_t port,
   3532                      soc_phy_control_t type, uint32 *value)
   3533 {
   3534     int rv;
   3535     phy_ctrl_t *pc;
   3536     uint16 data;
   3537     uint32 temp;
   3538     soc_port_t primary;
   3539     int offset;
   3540 
   3541 
   3542     PHY_CONTROL_TYPE_CHECK(type);
   3543 
   3544     pc = EXT_PHY_SW_STATE(unit, port);
   3545     rv = SOC_E_NONE;
   3546     switch(type) {
   3547         case SOC_PHY_CONTROL_POWER:
   3548             *value = pc->power_mode;
   3549         break;
   3550 
   3551         case SOC_PHY_CONTROL_PORT_PRIMARY:
   3552                 SOC_IF_ERROR_RETURN
   3553                     (soc_phyctrl_primary_get(unit, port, &primary));
   3554                 *value = (uint32) primary;
   3555             break;
   3556 
   3557         case SOC_PHY_CONTROL_PORT_OFFSET:
   3558                 SOC_IF_ERROR_RETURN
   3559                     (soc_phyctrl_offset_get(unit, port, &offset));
   3560                 *value = (uint32) offset;
   3561             break;
   3562 
   3563         case SOC_PHY_CONTROL_CLOCK_ENABLE:
   3564         SOC_IF_ERROR_RETURN
   3565             (soc_phyctrl_primary_get(unit, port, &primary));
   3566         SOC_IF_ERROR_RETURN
   3567             (soc_phyctrl_offset_get(unit, port, &offset));
   3568         if (phy_54640_toplvl_reg_read(unit, port, primary, 0x0, &data) == SOC_E_NONE) {
   3569             if (( data & 0x8 ) || (( data & 0x7 ) != offset)) {
   3570                 *value = FALSE;
   3571             } else {
   3572                 *value = TRUE;
   3573             }
   3574         }
   3575         break;
   3576 
   3577         case SOC_PHY_CONTROL_CLOCK_SECONDARY_ENABLE:
   3578         SOC_IF_ERROR_RETURN
   3579             (soc_phyctrl_primary_get(unit, port, &primary));
   3580         SOC_IF_ERROR_RETURN
   3581             (soc_phyctrl_offset_get(unit, port, &offset));
   3582         if (phy_54640_toplvl_reg_read(unit, port, primary, 0x0, &data) == SOC_E_NONE) {
   3583             if (( data & (0x8<<4) ) || (( data & (0x7<<4) ) != (offset<<4) )) {
   3584                 *value = FALSE;
   3585             } else {
   3586                 *value = TRUE;
   3587             }
   3588         }
   3589         break;
   3590 
   3591         case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT:
   3592             rv = phy_54640_control_linkdown_transmit_get(unit, port, value);
   3593             break;
   3594 
   3595         case SOC_PHY_CONTROL_EEE:
   3596             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3597                 rv = SOC_E_UNAVAIL;
   3598             }
   3599             *value = (PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_ENABLED) &&
   3600                  !PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_MODE)) ? 1 : 0;
   3601             break;
   3602 
   3603         case SOC_PHY_CONTROL_EEE_AUTO:
   3604             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3605                 rv = SOC_E_UNAVAIL;
   3606             }
   3607             *value = (PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_ENABLED) &&
   3608                   PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_MODE)) ? 1 : 0;
   3609             break;
   3610 
   3611         case SOC_PHY_CONTROL_EEE_AUTO_FIXED_LATENCY:
   3612             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3613                 rv = SOC_E_UNAVAIL;
   3614             }
   3615             SOC_IF_ERROR_RETURN
   3616                 (READ_PHY54640_EXP_MII_BUF_CNTL_STAT1r(unit, pc, &data));
   3617             *value = ((data & 0x0004) == 0);
   3618             break;
   3619 
   3620         case SOC_PHY_CONTROL_EEE_AUTO_IDLE_THRESHOLD:
   3621             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3622                 rv = SOC_E_UNAVAIL;
   3623             }
   3624             SOC_IF_ERROR_RETURN
   3625                 (READ_PHY54640_EXP_MII_BUF_CNTL_STAT1r(unit, pc, &data));
   3626             *value = ((data & 0x0700) >> 8);
   3627             break;
   3628 
   3629         case SOC_PHY_CONTROL_EEE_TRANSMIT_EVENTS:
   3630             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3631                 rv = SOC_E_UNAVAIL;
   3632             }
   3633             SOC_IF_ERROR_RETURN
   3634                 (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 0x0, 1U<<14)); /* exp af */
   3635             SOC_IF_ERROR_RETURN
   3636                 (READ_PHY54640_EXP_EEE_TX_EVENTSr(unit, pc, &data));
   3637             temp = data;
   3638             SOC_IF_ERROR_RETURN
   3639                 (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 1U<<14, 1U<<14)); /* exp af */
   3640             SOC_IF_ERROR_RETURN
   3641                 (READ_PHY54640_EXP_EEE_TX_EVENTSr(unit, pc, &data));
   3642             temp |= (data << 16);
   3643             *value = temp;
   3644             break;
   3645 
   3646         case SOC_PHY_CONTROL_EEE_TRANSMIT_DURATION:
   3647             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3648                 rv = SOC_E_UNAVAIL;
   3649             }
   3650             SOC_IF_ERROR_RETURN
   3651                 (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 0x0, 1U<<14)); /* exp af */
   3652             SOC_IF_ERROR_RETURN
   3653                 (READ_PHY54640_EXP_EEE_TX_DURATIONr(unit, pc, &data));
   3654             temp = data;
   3655             SOC_IF_ERROR_RETURN
   3656                 (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 1U<<14, 1U<<14)); /* exp af */
   3657             SOC_IF_ERROR_RETURN
   3658                 (READ_PHY54640_EXP_EEE_TX_DURATIONr(unit, pc, &data));
   3659             temp |= (data << 16);
   3660             *value = temp;
   3661             break;
   3662 
   3663         case SOC_PHY_CONTROL_EEE_RECEIVE_EVENTS:
   3664             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3665                 rv = SOC_E_UNAVAIL;
   3666             }
   3667             SOC_IF_ERROR_RETURN
   3668                 (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 0x0, 1U<<14)); /* exp af */
   3669             SOC_IF_ERROR_RETURN
   3670                 (READ_PHY54640_EXP_EEE_RX_EVENTSr(unit, pc, &data));
   3671             temp = data;
   3672             SOC_IF_ERROR_RETURN
   3673                 (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 1U<<14, 1U<<14)); /* exp af */
   3674             SOC_IF_ERROR_RETURN
   3675                 (READ_PHY54640_EXP_EEE_RX_EVENTSr(unit, pc, &data));
   3676             temp |= (data << 16);
   3677             *value = temp;
   3678             break;
   3679 
   3680         case SOC_PHY_CONTROL_EEE_RECEIVE_DURATION:
   3681             if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) {
   3682                 rv = SOC_E_UNAVAIL;
   3683             }
   3684             SOC_IF_ERROR_RETURN
   3685                 (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 0x0, 1U<<14)); /* exp af */
   3686             SOC_IF_ERROR_RETURN
   3687                 (READ_PHY54640_EXP_EEE_RX_DURATIONr(unit, pc, &data));
   3688             temp = data;
   3689             SOC_IF_ERROR_RETURN
   3690                 (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FAF,0x15, 1U<<14, 1U<<14)); /* exp af */
   3691             SOC_IF_ERROR_RETURN
   3692                 (READ_PHY54640_EXP_EEE_RX_DURATIONr(unit, pc, &data));
   3693             temp |= (data << 16);
   3694             *value = temp;
   3695 
   3696             break;
   3697 
   3698     case SOC_PHY_CONTROL_LOOPBACK_EXTERNAL:
   3699          SOC_IF_ERROR_RETURN
   3700              (READ_PHY54640_MII_AUX_CTRLr(unit, pc, &data));
   3701          *value = data & (1U<<15) ? 1 : 0;
   3702          break;
   3703 
   3704     default:
   3705             rv = SOC_E_UNAVAIL;
   3706             break;
   3707     }
   3708     return rv;
   3709 }
   3710 
   3711 /*
   3712  * Function:
   3713  *      phy_54640_cable_diag
   3714  * Purpose:
   3715  *      Run 546x cable diagnostics
   3716  * Parameters:
   3717  *      unit - device number
   3718  *      port - port number
   3719  *      status - (OUT) cable diagnotic status structure
   3720  * Returns:     
   3721  *      SOC_E_XXX
   3722  */
   3723 STATIC int
   3724 phy_54640_cable_diag(int unit, soc_port_t port,
   3725                     soc_port_cable_diag_t *status)
   3726 {
   3727     int rv, rv2, i;
   3728     phy_ctrl_t *pc;
   3729 
   3730     extern int phy_5464_cable_diag_sw(int, soc_port_t , 
   3731                                       soc_port_cable_diag_t *);
   3732 
   3733     pc = EXT_PHY_SW_STATE(unit, port);
   3734 
   3735     if (status == NULL) {
   3736         return SOC_E_PARAM;
   3737     }
   3738 
   3739     status->state = SOC_PORT_CABLE_STATE_OK;
   3740     status->npairs = 4;
   3741     status->fuzz_len = 0;
   3742     for (i = 0; i < 4; i++) {
   3743         status->pair_state[i] = SOC_PORT_CABLE_STATE_OK;
   3744     }
   3745 
   3746     MIIM_LOCK(unit);    /* this locks out linkscan, essentially */
   3747     rv = phy_5464_cable_diag_sw(unit,port, status);
   3748     MIIM_UNLOCK(unit);
   3749     rv2 = 0;
   3750     if (rv <= 0) {      /* don't reset if > 0 -- link was up */
   3751         rv2 = _phy_54640_reset_setup(unit, port);
   3752         if (SOC_SUCCESS(rv2)) {
   3753             rv2 = _phy_54640_medium_config_update(unit, port, &pc->copper);
   3754         }
   3755     }
   3756     if (rv >= 0 && rv2 < 0) {
   3757         return rv2;
   3758     }
   3759     return rv;
   3760 }
   3761 
   3762 STATIC int
   3763 phy_54640_cable_diag_dispatch(int unit, soc_port_t port,
   3764             soc_port_cable_diag_t *status)
   3765 {
   3766     phy_ctrl_t *pc;
   3767 
   3768     pc = EXT_PHY_SW_STATE(unit, port);
   3769 
   3770     if (ECD_ENABLE && ((PHY_IS_BCM54640E(pc)) && ((pc->phy_rev & 0x7) > 0x2)))  {
   3771         SOC_IF_ERROR_RETURN(
   3772             phy_ecd_cable_diag(unit, port, status));
   3773         SOC_IF_ERROR_RETURN(
   3774             _phy_54640_reset_setup(unit, port));
   3775     } else {
   3776         SOC_IF_ERROR_RETURN(
   3777             phy_54640_cable_diag(unit, port, status));
   3778     }
   3779 
   3780     return SOC_E_NONE;
   3781 }
   3782 
   3783 /*
   3784  * Function:
   3785  *      phy_54640_probe
   3786  * Purpose:
   3787  *      Complement the generic phy probe routine to identify this phy when its
   3788  *      phy id0 and id1 is same as some other phy's.
   3789  * Parameters:
   3790  *      unit - StrataSwitch unit #.
   3791  *      pc   - phy ctrl descriptor.
   3792  * Returns:
   3793  *      SOC_E_NONE,SOC_E_NOT_FOUND and SOC_E_<error>
   3794  */
   3795 STATIC int
   3796 phy_54640_probe(int unit, phy_ctrl_t *pc)
   3797 {
   3798     uint16 id0, id1;
   3799     int rv = SOC_E_NOT_FOUND;
   3800 
   3801     if (READ_PHY54640_MII_PHY_ID0r(unit, pc, &id0) < 0) {
   3802         return SOC_E_NOT_FOUND;
   3803     }
   3804     if (READ_PHY54640_MII_PHY_ID1r(unit, pc, &id1) < 0) {
   3805         return SOC_E_NOT_FOUND;
   3806     }
   3807 
   3808     switch (PHY_MODEL(id0, id1)) {
   3809 
   3810     case PHY_BCM54640_MODEL:
   3811             rv = SOC_E_NONE;
   3812     break;
   3813 
   3814     case PHY_BCM54680E_MODEL:
   3815         if ((id1 & 0x8) == 0) {
   3816             rv = SOC_E_NONE;
   3817         }
   3818     break;
   3819 
   3820     default:
   3821     break;
   3822 
   3823     }
   3824     pc->size = sizeof(PHY54640_DEV_DESC_t);
   3825     return rv;
   3826 }
   3827 
   3828 STATIC int
   3829 phy_54640_link_up(int unit, soc_port_t port)
   3830 {
   3831     phy_ctrl_t *pc;
   3832     int speed;
   3833     uint16 value;
   3834 
   3835 
   3836     pc = EXT_PHY_SW_STATE(unit, port);
   3837 
   3838     SOC_IF_ERROR_RETURN
   3839         (phy_54640_speed_get(unit, port, &speed));
   3840 
   3841     if (speed == 100) {
   3842         SOC_IF_ERROR_RETURN(
   3843             READ_PHY_REG(unit, pc, 0x11, &value));
   3844         if(value & (1U << 13)) {
   3845                 SOC_IF_ERROR_RETURN
   3846                     (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FF5,0x15, 0x0<<10, 0x3<<10)); /* exp reg f5 */
   3847         } else {
   3848                 SOC_IF_ERROR_RETURN
   3849                     (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FF5,0x15, 0x1<<10, 0x3<<10)); /* exp reg f5 */
   3850         }
   3851     }
   3852     
   3853     if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) { /*non EEE */
   3854         /* do nothing */
   3855     } else if ((pc->phy_rev & 0x6) == 0x0) {
   3856     /* revs A0 and A1 of supported PHYs */
   3857         uint32 eee_auto;
   3858 
   3859         SOC_IF_ERROR_RETURN
   3860             (phy_54640_control_get(unit, port,
   3861                      SOC_PHY_CONTROL_EEE_AUTO, &eee_auto));
   3862         if ((speed == 100) && (eee_auto == 1)) {
   3863             SOC_IF_ERROR_RETURN(
   3864                 PHY54640_REG_WRITE(unit, pc, 0, 0x0D10, 0x15, 0x00ff));
   3865 
   3866         } else {
   3867             SOC_IF_ERROR_RETURN(
   3868                 PHY54640_REG_WRITE(unit, pc, 0, 0x0D10, 0x15, 0x0000));
   3869         }
   3870     } else if ((pc->phy_rev & 0x6) == 0x2) {
   3871         /* revs B0 and B1 of supported PHYs */
   3872         sal_usleep(10000);
   3873         SOC_IF_ERROR_RETURN(
   3874             MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, (1U << 11), (1U << 11)));
   3875         SOC_IF_ERROR_RETURN(
   3876             WRITE_PHY_REG(unit, pc, 0x17, 0x001a));
   3877         SOC_IF_ERROR_RETURN(
   3878             WRITE_PHY_REG(unit, pc, 0x15, 0x0003));
   3879         SOC_IF_ERROR_RETURN(
   3880             MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, 0, (1U << 11)));
   3881     }
   3882 
   3883     /* Notify interface to internal PHY */
   3884     SOC_IF_ERROR_RETURN
   3885         (soc_phyctrl_notify(unit, port, phyEventInterface, SOC_PORT_IF_SGMII));
   3886 
   3887     /* Situation observed with Saber devices XGXS16g1l+BCM54640E
   3888        Linkscan is not updating speed for internal phy xgxs16g1l. 
   3889        It was observed that when speed is set to 100M the int phy 
   3890        is still set to 1G and traffic does not flow through untill 
   3891        int phy speed is also update to 100M. This applies to other 
   3892        speeds as well. 
   3893        Explicitly send speed notify to int phy from ext phy.
   3894     */
   3895     SOC_IF_ERROR_RETURN
   3896         (soc_phyctrl_notify(unit, port, phyEventSpeed, speed));
   3897 
   3898     return SOC_E_NONE;
   3899 }
   3900 
   3901 STATIC int
   3902 phy_54640_link_down(int unit, soc_port_t port)
   3903 {
   3904     phy_ctrl_t *pc;
   3905 
   3906     if (!PHY_FLAGS_TST(unit, port, PHY_FLAGS_EEE_CAPABLE)) { /*non EEE */
   3907         return SOC_E_NONE;
   3908     }
   3909 
   3910     pc = EXT_PHY_SW_STATE(unit, port);
   3911 
   3912     if ((pc->phy_rev & 0x6) == 0x2) {
   3913         SOC_IF_ERROR_RETURN(
   3914             MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, (1U << 11), (1U << 11)));
   3915         SOC_IF_ERROR_RETURN(
   3916             WRITE_PHY_REG(unit, pc, 0x17, 0x001a));
   3917         SOC_IF_ERROR_RETURN(
   3918             WRITE_PHY_REG(unit, pc, 0x15, 0x0007));
   3919         SOC_IF_ERROR_RETURN(
   3920             MODIFY_PHY54640_MII_AUX_CTRLr(unit, pc, 0, (1U << 11)));
   3921     }
   3922 
   3923     return (SOC_E_NONE);
   3924 }
   3925 
   3926 
   3927 void _phy_54640e_encode_egress_message_mode(soc_port_phy_timesync_event_message_egress_mode_t mode,
   3928                                             int offset, uint16 *value)
   3929 {
   3930 
   3931     switch (mode) {
   3932     case SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_EGRESS_MODE_NONE:
   3933         *value |= (0x0 << offset);
   3934         break;
   3935     case SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_EGRESS_MODE_UPDATE_CORRECTIONFIELD:
   3936         *value |= (0x1 << offset);
   3937         break;
   3938     case SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_EGRESS_MODE_REPLACE_CORRECTIONFIELD_ORIGIN:
   3939         *value |= (0x2 << offset);
   3940         break;
   3941     case SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_EGRESS_MODE_CAPTURE_TIMESTAMP:
   3942         *value |= (0x3 << offset);
   3943         break;
   3944     default:
   3945         break;
   3946     }
   3947 
   3948 }
   3949 
   3950 void _phy_54640e_encode_ingress_message_mode(soc_port_phy_timesync_event_message_ingress_mode_t mode,
   3951                                             int offset, uint16 *value)
   3952 {
   3953 
   3954     switch (mode) {
   3955     case SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_INGRESS_MODE_NONE:
   3956         *value |= (0x0 << offset);
   3957         break;
   3958     case SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_INGRESS_MODE_UPDATE_CORRECTIONFIELD:
   3959         *value |= (0x1 << offset);
   3960         break;
   3961     case SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_INGRESS_MODE_INSERT_TIMESTAMP:
   3962         *value |= (0x2 << offset);
   3963         break;
   3964     case SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_INGRESS_MODE_INSERT_DELAYTIME:
   3965         *value |= (0x3 << offset);
   3966         break;
   3967     default:
   3968         break;
   3969     }
   3970 
   3971 }
   3972 
   3973 void _phy_54640e_decode_egress_message_mode(uint16 value, int offset,
   3974                                             soc_port_phy_timesync_event_message_egress_mode_t *mode)
   3975 {
   3976 
   3977     switch ((value >> offset) & 0x3) {
   3978     case 0x0:
   3979         *mode = SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_EGRESS_MODE_NONE;
   3980         break;
   3981     case 0x1:
   3982         *mode = SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_EGRESS_MODE_UPDATE_CORRECTIONFIELD;
   3983         break;
   3984     case 0x2:
   3985         *mode = SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_EGRESS_MODE_REPLACE_CORRECTIONFIELD_ORIGIN;
   3986         break;
   3987     case 0x3:
   3988         *mode = SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_EGRESS_MODE_CAPTURE_TIMESTAMP;
   3989         break;
   3990     /*
   3991      * COVERITY  
   3992      *
   3993      * This default is unreachable. It is kept intentionally as a defensive default for future development.
   3994      */
   3995     /* coverity[dead_error_begin]  */
   3996     default:
   3997         break;
   3998     }
   3999 
   4000 }
   4001 
   4002 void _phy_54640e_decode_ingress_message_mode(uint16 value, int offset,
   4003                                             soc_port_phy_timesync_event_message_ingress_mode_t *mode)
   4004 {
   4005 
   4006     switch ((value >> offset) & 0x3) {
   4007     case 0x0:
   4008         *mode = SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_INGRESS_MODE_NONE;
   4009         break;
   4010     case 0x1:
   4011         *mode = SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_INGRESS_MODE_UPDATE_CORRECTIONFIELD;
   4012         break;
   4013     case 0x2:
   4014         *mode = SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_INGRESS_MODE_INSERT_TIMESTAMP;
   4015         break;
   4016     case 0x3:
   4017         *mode = SOC_PORT_PHY_TIMESYNC_EVENT_MESSAGE_INGRESS_MODE_INSERT_DELAYTIME;
   4018         break;
   4019     /*
   4020      * COVERITY
   4021      *
   4022      * This default is unreachable. It is kept intentionally as a defensive default for future development.
   4023      */
   4024     /* coverity[dead_error_begin]  */
   4025     default:
   4026         break;
   4027     }
   4028 
   4029 }
   4030 
   4031 void _phy_54640e_encode_gmode(soc_port_phy_timesync_global_mode_t mode,
   4032                                             uint16 *value)
   4033 {
   4034 
   4035     switch (mode) {
   4036     case SOC_PORT_PHY_TIMESYNC_MODE_FREE:
   4037         *value = 0x1;
   4038         break;
   4039     case SOC_PORT_PHY_TIMESYNC_MODE_SYNCIN:
   4040         *value = 0x2;
   4041         break;
   4042     case SOC_PORT_PHY_TIMESYNC_MODE_CPU:
   4043         *value = 0x3;
   4044         break;
   4045     default:
   4046         break;
   4047     }
   4048 
   4049 }
   4050 
   4051 void _phy_54640e_decode_gmode(uint16 value,
   4052                                             soc_port_phy_timesync_global_mode_t *mode)
   4053 {
   4054 
   4055     switch (value & 0x3) {
   4056     case 0x1:
   4057         *mode = SOC_PORT_PHY_TIMESYNC_MODE_FREE;
   4058         break;
   4059     case 0x2:
   4060         *mode = SOC_PORT_PHY_TIMESYNC_MODE_SYNCIN;
   4061         break;
   4062     case 0x3:
   4063         *mode = SOC_PORT_PHY_TIMESYNC_MODE_CPU;
   4064         break;
   4065     default:
   4066         break;
   4067     }
   4068 
   4069 }
   4070 
   4071 void _phy_54640e_encode_framesync_mode(soc_port_phy_timesync_framesync_mode_t mode,
   4072                                             uint16 *value)
   4073 {
   4074 
   4075     switch (mode) {
   4076     case SOC_PORT_PHY_TIMESYNC_FRAMESYNC_SYNCIN0:
   4077         *value = 1U << 0;
   4078         break;
   4079     case SOC_PORT_PHY_TIMESYNC_FRAMESYNC_SYNCIN1:
   4080         *value = 1U << 1;
   4081         break;
   4082     case SOC_PORT_PHY_TIMESYNC_FRAMESYNC_SYNCOUT:
   4083         *value = 1U << 2;
   4084         break;
   4085     case SOC_PORT_PHY_TIMESYNC_FRAMESYNC_CPU:
   4086         *value = 1U << 3;
   4087         break;
   4088     default:
   4089         break;
   4090     }
   4091 
   4092 }
   4093 
   4094 void _phy_54640e_decode_framesync_mode(uint16 value,
   4095                                             soc_port_phy_timesync_framesync_mode_t *mode)
   4096 {
   4097 
   4098     switch (value & 0xf) {
   4099     case 1U << 0:
   4100         *mode = SOC_PORT_PHY_TIMESYNC_FRAMESYNC_SYNCIN0;
   4101         break;
   4102     case 1U << 1:
   4103         *mode = SOC_PORT_PHY_TIMESYNC_FRAMESYNC_SYNCIN1;
   4104         break;
   4105     case 1U << 2:
   4106         *mode = SOC_PORT_PHY_TIMESYNC_FRAMESYNC_SYNCOUT;
   4107         break;
   4108     case 1U << 3:
   4109         *mode = SOC_PORT_PHY_TIMESYNC_FRAMESYNC_CPU;
   4110         break;
   4111     default:
   4112         break;
   4113     }
   4114 
   4115 }
   4116 
   4117 void _phy_54640e_encode_syncout_mode(soc_port_phy_timesync_syncout_mode_t mode,
   4118                                             uint16 *value)
   4119 {
   4120 
   4121     switch (mode) {
   4122     case SOC_PORT_PHY_TIMESYNC_SYNCOUT_DISABLE:
   4123         *value = 0x0;
   4124         break;
   4125     case SOC_PORT_PHY_TIMESYNC_SYNCOUT_ONE_TIME:
   4126         *value = 0x1;
   4127         break;
   4128     case SOC_PORT_PHY_TIMESYNC_SYNCOUT_PULSE_TRAIN:
   4129         *value = 0x2;
   4130         break;
   4131     case SOC_PORT_PHY_TIMESYNC_SYNCOUT_PULSE_TRAIN_WITH_SYNC:
   4132         *value = 0x3;
   4133         break;
   4134     default:
   4135         break;
   4136     }
   4137 
   4138 }
   4139 
   4140 void _phy_54640e_decode_syncout_mode(uint16 value,
   4141                                             soc_port_phy_timesync_syncout_mode_t *mode)
   4142 {
   4143 
   4144     switch (value & 0x3) {
   4145     case 0x0:
   4146         *mode = SOC_PORT_PHY_TIMESYNC_SYNCOUT_DISABLE;
   4147         break;
   4148     case 0x1:
   4149         *mode = SOC_PORT_PHY_TIMESYNC_SYNCOUT_ONE_TIME;
   4150         break;
   4151     case 0x2:
   4152         *mode = SOC_PORT_PHY_TIMESYNC_SYNCOUT_PULSE_TRAIN;
   4153         break;
   4154     case 0x3:
   4155         *mode = SOC_PORT_PHY_TIMESYNC_SYNCOUT_PULSE_TRAIN_WITH_SYNC;
   4156         break;
   4157     /*
   4158      * COVERITY
   4159      *
   4160      * This default is unreachable. It is kept intentionally as a defensive default for future development.
   4161      */
   4162     /* coverity[dead_error_begin]  */
   4163     default:
   4164         break;
   4165     }
   4166 
   4167 }
   4168 
   4169 
   4170 STATIC int
   4171 phy_54640_timesync_config_set(int unit, soc_port_t port, soc_port_phy_timesync_config_t *conf)
   4172 {
   4173     phy_ctrl_t *pc;
   4174     int offset, rv = SOC_E_NONE;
   4175     uint16 rx_control_reg = 0, tx_control_reg = 0, rx_crc_control_reg = 0,
   4176            en_control_reg = 0, tx_capture_en_reg = 0, rx_capture_en_reg = 0,
   4177            nse_nco_6 = 0, nse_nco_2c = 0, value, mask,
   4178            gmode = 0, framesync_mode = 0, syncout_mode = 0,
   4179            pulse_1_length, pulse_2_length, length_threshold, event_offset;
   4180     uint32 interval;
   4181 
   4182     pc = EXT_PHY_SW_STATE(unit, port);
   4183 
   4184     if ((!PHY_IS_BCM54640E(pc)) || PHY_IS_BCM54640E_A0A1(pc)) {
   4185         return SOC_E_UNAVAIL;
   4186     }
   4187 
   4188     offset = PHY54640_DEV_PHY_SLICE(pc);
   4189 
   4190     do {
   4191         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_FLAGS) {
   4192 
   4193             if (conf->flags & SOC_PORT_PHY_TIMESYNC_ENABLE) {
   4194             en_control_reg |= ((1U << (offset + 8)) | (1U << offset));
   4195             nse_nco_6 |= (1U << 12); /* NSE init */
   4196 
   4197             SOC_IF_ERROR_BREAK
   4198                 (MODIFY_PHY54640_SGMII_SLAVEr(unit, pc, 0x02, 0x02), rv);
   4199 
   4200             SOC_IF_ERROR_BREAK
   4201                 (MODIFY_PHY54640_EXT_SERDES_CTRLr(unit, pc, 0x00, 0x01), rv);
   4202 
   4203             SOC_IF_ERROR_BREAK
   4204                 (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FF5,0x15, 1U<<0, 1U<<0), rv); /* exp reg f5 */
   4205             } else {
   4206 
   4207             SOC_IF_ERROR_BREAK
   4208                 (PHY54640_REG_MODIFY(unit, pc, 0x00, 0x0FF5,0x15, 0, 1U<<0), rv); /* exp reg f5 */
   4209 
   4210             }
   4211 
   4212             /* pc->phy_id =  PHY54640_SLICE_ADDR(pc, 0); */
   4213             pc->phy_id =  PHY54640_DEV_PHY_ID_BASE(pc);
   4214 
   4215             SOC_IF_ERROR_BREAK
   4216                 (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x10, en_control_reg, 
   4217                            ((1U << (offset + 8)) | (1U << offset))), rv);
   4218 
   4219             SOC_IF_ERROR_BREAK
   4220                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x10, &en_control_reg), rv);
   4221 
   4222             if (conf->flags & SOC_PORT_PHY_TIMESYNC_CAPTURE_TS_ENABLE) {
   4223             tx_capture_en_reg |= (1U << offset);
   4224             rx_capture_en_reg |= (1U << (offset + 8));
   4225             }
   4226 
   4227             SOC_IF_ERROR_BREAK
   4228                 (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x12, tx_capture_en_reg, 
   4229                            (1U << offset)), rv);
   4230 
   4231             SOC_IF_ERROR_BREAK
   4232                 (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x13, rx_capture_en_reg, 
   4233                            (1U << (offset + 8))), rv);
   4234 
   4235             if (conf->flags & SOC_PORT_PHY_TIMESYNC_HEARTBEAT_TS_ENABLE) {
   4236             nse_nco_6 |= (1U << 13);
   4237             }
   4238 
   4239             SOC_IF_ERROR_BREAK
   4240                 (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x4f, nse_nco_6, 
   4241                            (1U << 13)), rv);
   4242 
   4243             if (conf->flags & SOC_PORT_PHY_TIMESYNC_RX_CRC_ENABLE) {
   4244             rx_crc_control_reg |= (1U << 3);
   4245             }
   4246 
   4247             SOC_IF_ERROR_BREAK
   4248                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x34, rx_crc_control_reg), rv);
   4249 
   4250             if (conf->flags & SOC_PORT_PHY_TIMESYNC_8021AS_ENABLE) {
   4251             rx_control_reg |= (1U << 3);
   4252             tx_control_reg |= (1U << 3);
   4253             }
   4254 
   4255             if (conf->flags & SOC_PORT_PHY_TIMESYNC_L2_ENABLE) {
   4256             rx_control_reg |= (1U << 2);
   4257             tx_control_reg |= (1U << 2);
   4258             }
   4259 
   4260             if (conf->flags & SOC_PORT_PHY_TIMESYNC_IP4_ENABLE) {
   4261             rx_control_reg |= (1U << 1);
   4262             tx_control_reg |= (1U << 1);
   4263             }
   4264 
   4265             if (conf->flags & SOC_PORT_PHY_TIMESYNC_IP6_ENABLE) {
   4266             rx_control_reg |= (1U << 0);
   4267             tx_control_reg |= (1U << 0);
   4268             }
   4269 
   4270             SOC_IF_ERROR_BREAK
   4271                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x32, tx_control_reg), rv);
   4272                               
   4273             SOC_IF_ERROR_BREAK
   4274                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x33, rx_control_reg), rv);
   4275                               
   4276             if (conf->flags & SOC_PORT_PHY_TIMESYNC_CLOCK_SRC_EXT) {
   4277             nse_nco_2c &= (uint16) ~(1U << 14);
   4278             } else {
   4279             nse_nco_2c |= (1U << 14);
   4280             }
   4281 
   4282             SOC_IF_ERROR_BREAK
   4283                 (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x47, nse_nco_2c, 
   4284                                (1U << 14)), rv);
   4285         }
   4286 
   4287         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_ITPID) {
   4288             /* VLAN TAG register */
   4289             SOC_IF_ERROR_BREAK
   4290             (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x35, conf->itpid), rv);
   4291         }
   4292 
   4293         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_OTPID) {
   4294             /* OUTER VLAN TAG register */
   4295             SOC_IF_ERROR_BREAK
   4296                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x36, conf->otpid), rv);
   4297         }
   4298 
   4299         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_OTPID2) {
   4300             /* INNER VLAN TAG register */
   4301             SOC_IF_ERROR_BREAK
   4302                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x52, conf->otpid2), rv);
   4303         }
   4304 
   4305         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TS_DIVIDER) {
   4306             SOC_IF_ERROR_BREAK
   4307                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x4b, conf->ts_divider & 0xfff), rv);
   4308         }
   4309 
   4310 
   4311         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_LINK_DELAY) {
   4312             pc->phy_id =  PHY54640_DEV_PHY_ID_ORIG(pc);
   4313 
   4314             SOC_IF_ERROR_BREAK
   4315                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x6f, conf->rx_link_delay & 0xffff), rv);
   4316             SOC_IF_ERROR_BREAK
   4317                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x70, (conf->rx_link_delay >> 16) & 0xffff), rv);
   4318 
   4319             /* pc->phy_id =  PHY54640_SLICE_ADDR(pc, 0); */
   4320             pc->phy_id =  PHY54640_DEV_PHY_ID_BASE(pc);
   4321         }
   4322 
   4323 
   4324         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_ORIGINAL_TIMECODE) {
   4325             SOC_IF_ERROR_BREAK
   4326                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x25, (uint16)(conf->original_timecode.nanoseconds & 0xffff)), rv);
   4327 
   4328             SOC_IF_ERROR_BREAK
   4329                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x26, (uint16)((conf->original_timecode.nanoseconds >> 16) & 0xffff)), rv);
   4330 
   4331             SOC_IF_ERROR_BREAK
   4332                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x27, (uint16)(COMPILER_64_LO(conf->original_timecode.seconds) & 0xffff)), rv);
   4333 
   4334             SOC_IF_ERROR_BREAK
   4335                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x28, (uint16)((COMPILER_64_LO(conf->original_timecode.seconds) >> 16) & 0xffff)), rv);
   4336 
   4337             SOC_IF_ERROR_BREAK
   4338                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x29, (uint16)(COMPILER_64_HI(conf->original_timecode.seconds) & 0xffff)), rv);
   4339         }
   4340         mask = 0;
   4341 
   4342         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_GMODE) {
   4343             _phy_54640e_encode_gmode(conf->gmode,&gmode);
   4344             mask |= (0x3 << 14);
   4345         }
   4346 
   4347         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_FRAMESYNC_MODE) {
   4348             _phy_54640e_encode_framesync_mode(conf->framesync.mode, &framesync_mode);
   4349             mask |= (0xf << 2) | (0x1 << 11);
   4350 
   4351             /* Divide by 8 */
   4352             length_threshold = conf->framesync.length_threshold >> 3;
   4353             event_offset = conf->framesync.event_offset >> 3;
   4354 
   4355             SOC_IF_ERROR_BREAK
   4356                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x51, length_threshold), rv);
   4357             SOC_IF_ERROR_BREAK
   4358                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x50, event_offset), rv);
   4359         }
   4360 
   4361         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_SYNCOUT_MODE) {
   4362             _phy_54640e_encode_syncout_mode(conf->syncout.mode, &syncout_mode);
   4363             mask |= (0x3 << 0);
   4364 
   4365             /* Divide by 8 */
   4366             interval = conf->syncout.interval >> 3;
   4367             pulse_1_length = conf->syncout.pulse_1_length >> 3;
   4368             pulse_2_length = conf->syncout.pulse_2_length >> 3;
   4369 
   4370             SOC_IF_ERROR_BREAK
   4371                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x48, interval & 0xffff), rv);
   4372             SOC_IF_ERROR_BREAK
   4373                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x49, 
   4374                     ((pulse_2_length & 0x3) << 14) | ((interval >> 16) & 0x3fff)), rv);
   4375             SOC_IF_ERROR_BREAK
   4376                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x4a, 
   4377                     ((pulse_1_length & 0x1ff) << 7) | ((pulse_2_length >> 2) & 0x7f)), rv);
   4378 
   4379             SOC_IF_ERROR_BREAK
   4380                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x4e, 
   4381                     (uint16)(COMPILER_64_LO(conf->syncout.syncout_ts) & 0xffff)), rv);
   4382             SOC_IF_ERROR_BREAK
   4383                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x4d, 
   4384                     (uint16)((COMPILER_64_LO(conf->syncout.syncout_ts) >> 16) & 0xffff)), rv);
   4385             SOC_IF_ERROR_BREAK
   4386                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x4c, 
   4387                     (uint16)(COMPILER_64_HI(conf->syncout.syncout_ts) & 0xffff)), rv);
   4388         }
   4389 
   4390         SOC_IF_ERROR_BREAK
   4391             (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x4f, (gmode << 14) |
   4392                 ((framesync_mode & 0x2) ? (1U << 11) : (1U << 6)) | /* if bits 1 or 2 are set, turn off syncout */
   4393                 (framesync_mode << 2) | (syncout_mode << 0), mask), rv);
   4394 
   4395         /* tx_timestamp_offset */
   4396         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TX_TIMESTAMP_OFFSET) {
   4397             SOC_IF_ERROR_BREAK
   4398                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x15 + offset, (uint16)(conf->tx_timestamp_offset & 0xffff)), rv);
   4399 
   4400             if (offset < 4 ) {
   4401             SOC_IF_ERROR_BREAK
   4402                 (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x63, 
   4403                     ((conf->tx_timestamp_offset & 0xf0000) >> ((4 - offset) << 2)), 0xf << (offset << 2)), rv);
   4404             } else {
   4405             uint16 offset1 = offset - 4;
   4406             SOC_IF_ERROR_BREAK
   4407                 (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x64, 
   4408                     ((conf->tx_timestamp_offset & 0xf0000) >> ((4 - offset1) << 2)), 0xf << (offset1 << 2)), rv);
   4409             }
   4410         }
   4411 
   4412         /* rx_timestamp_offset */
   4413         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_TIMESTAMP_OFFSET) {
   4414             SOC_IF_ERROR_BREAK
   4415                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x1d + offset, (uint16)(conf->rx_timestamp_offset & 0xffff)), rv);
   4416 
   4417             if (offset < 4 ) {
   4418             SOC_IF_ERROR_BREAK
   4419                 (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x65, 
   4420                     ((conf->rx_timestamp_offset & 0xf0000) >> ((4 - offset) << 2)), 0xf << (offset << 2)), rv);
   4421             } else {
   4422             uint16 offset1 = offset - 4;
   4423             SOC_IF_ERROR_BREAK
   4424                 (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x66, 
   4425                     ((conf->rx_timestamp_offset & 0xf0000) >> ((4 - offset1) << 2)), 0xf << (offset1 << 2)), rv);
   4426             }
   4427         }
   4428 
   4429         value = 0;
   4430         mask = 0;
   4431 
   4432         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TX_SYNC_MODE) {
   4433             _phy_54640e_encode_egress_message_mode(conf->tx_sync_mode, 0, &value);
   4434             mask |= 0x3 << 0;
   4435         }
   4436 
   4437         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TX_DELAY_REQUEST_MODE) {
   4438             _phy_54640e_encode_egress_message_mode(conf->tx_delay_request_mode, 2, &value);
   4439             mask |= 0x3 << 2;
   4440         }
   4441 
   4442         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TX_PDELAY_REQUEST_MODE) {
   4443             _phy_54640e_encode_egress_message_mode(conf->tx_pdelay_request_mode, 4, &value);
   4444             mask |= 0x3 << 4;
   4445         }
   4446 
   4447         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TX_PDELAY_RESPONSE_MODE) {
   4448             _phy_54640e_encode_egress_message_mode(conf->tx_pdelay_response_mode, 6, &value);
   4449             mask |= 0x3 << 6;
   4450         }
   4451                             
   4452         pc->phy_id =  PHY54640_DEV_PHY_ID_ORIG(pc);
   4453 
   4454         SOC_IF_ERROR_BREAK
   4455             (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x11, value, mask), rv); 
   4456 
   4457         value = 0;
   4458         mask = 0;
   4459 
   4460         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_SYNC_MODE) {
   4461             _phy_54640e_encode_ingress_message_mode(conf->rx_sync_mode, 0, &value);
   4462             mask |= 0x3 << 0;
   4463         }
   4464 
   4465         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_DELAY_REQUEST_MODE) {
   4466             _phy_54640e_encode_ingress_message_mode(conf->rx_delay_request_mode, 2, &value);
   4467             mask |= 0x3 << 2;
   4468         }
   4469 
   4470         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_PDELAY_REQUEST_MODE) {
   4471             _phy_54640e_encode_ingress_message_mode(conf->rx_pdelay_request_mode, 4, &value);
   4472             mask |= 0x3 << 4;
   4473         }
   4474 
   4475         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_PDELAY_RESPONSE_MODE) {
   4476             _phy_54640e_encode_ingress_message_mode(conf->rx_pdelay_response_mode, 6, &value);
   4477             mask |= 0x3 << 6;
   4478         }
   4479                             
   4480                             
   4481         SOC_IF_ERROR_BREAK
   4482             (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x12, value << 8, mask << 8), rv); 
   4483 
   4484         /* pc->phy_id =  PHY54640_SLICE_ADDR(pc, 0); */
   4485         pc->phy_id =  PHY54640_DEV_PHY_ID_BASE(pc);
   4486 
   4487         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_REF_PHASE) {
   4488             /* Initial ref phase [15:0] */
   4489             SOC_IF_ERROR_BREAK
   4490                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x38, (uint16)(COMPILER_64_LO(conf->phy_1588_dpll_ref_phase) & 0xffff)), rv);
   4491 
   4492             /* Initial ref phase [31:16]  */
   4493             SOC_IF_ERROR_BREAK
   4494                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x39, (uint16)((COMPILER_64_LO(conf->phy_1588_dpll_ref_phase) >> 16) & 0xffff)), rv);
   4495 
   4496             /*  Initial ref phase [47:32] */
   4497             SOC_IF_ERROR_BREAK
   4498                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x3a, (uint16)(COMPILER_64_HI(conf->phy_1588_dpll_ref_phase) & 0xffff)), rv);
   4499         }
   4500 
   4501         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_REF_PHASE_DELTA) {
   4502             /* Ref phase delta [15:0] */
   4503             SOC_IF_ERROR_BREAK
   4504                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x3b, (uint16)(conf->phy_1588_dpll_ref_phase_delta & 0xffff)), rv);
   4505 
   4506             /* Ref phase delta [31:16]  */
   4507             SOC_IF_ERROR_BREAK
   4508                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x3c, (uint16)((conf->phy_1588_dpll_ref_phase_delta >> 16) & 0xffff)), rv);
   4509         }
   4510 
   4511         /* DPLL K1 & K2 */
   4512         mask = 0;
   4513 
   4514         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_K1) {
   4515             mask |= 0xff << 8;
   4516         }
   4517 
   4518         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_K2) {
   4519             mask |= 0xff;
   4520         }
   4521         SOC_IF_ERROR_BREAK
   4522             (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x3d, (conf->phy_1588_dpll_k1 & 0xff)<<8 | (conf->phy_1588_dpll_k2 & 0xff), mask ), rv);
   4523 
   4524         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_K3) {
   4525             /* DPLL K3 */
   4526             SOC_IF_ERROR_BREAK
   4527                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x3e, conf->phy_1588_dpll_k3 & 0xff), rv);
   4528         }
   4529 
   4530 
   4531         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_LOOP_FILTER) {
   4532             /* Initial loop filter[15:0] */
   4533             SOC_IF_ERROR_BREAK
   4534                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x3f, (uint16)(COMPILER_64_LO(conf->phy_1588_dpll_loop_filter) & 0xffff)), rv);
   4535 
   4536             /* Initial loop filter[31:16]  */
   4537             SOC_IF_ERROR_BREAK
   4538                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x40, (uint16)((COMPILER_64_LO(conf->phy_1588_dpll_loop_filter) >> 16) & 0xffff)), rv);
   4539 
   4540             /*  Initial loop filter[47:32] */
   4541             SOC_IF_ERROR_BREAK
   4542                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x41, (uint16)(COMPILER_64_HI(conf->phy_1588_dpll_loop_filter) & 0xffff)), rv);
   4543 
   4544             /* Initial loop filter[63:48]  */
   4545             SOC_IF_ERROR_BREAK
   4546                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x42, (uint16)((COMPILER_64_HI(conf->phy_1588_dpll_loop_filter) >> 16) & 0xffff)), rv);
   4547         }
   4548 
   4549     } while (0);
   4550 
   4551     pc->phy_id =  PHY54640_DEV_PHY_ID_ORIG(pc);
   4552 
   4553     return rv;
   4554 }
   4555 
   4556 STATIC int
   4557 phy_54640_timesync_config_get(int unit, soc_port_t port, soc_port_phy_timesync_config_t *conf)
   4558 {
   4559     phy_ctrl_t *pc;
   4560     int offset, rv = SOC_E_NONE;
   4561     uint16 tx_control_reg = 0, rx_crc_control_reg = 0,
   4562            en_control_reg = 0, tx_capture_en_reg = 0,
   4563            nse_nco_6 = 0, nse_nco_2c = 0, temp1, temp2, temp3, temp4, value;
   4564     soc_port_phy_timesync_global_mode_t gmode = SOC_PORT_PHY_TIMESYNC_MODE_FREE;
   4565     soc_port_phy_timesync_framesync_mode_t framesync_mode = SOC_PORT_PHY_TIMESYNC_FRAMESYNC_NONE;
   4566     soc_port_phy_timesync_syncout_mode_t syncout_mode = SOC_PORT_PHY_TIMESYNC_SYNCOUT_DISABLE;
   4567 
   4568     pc = EXT_PHY_SW_STATE(unit, port);
   4569 
   4570     offset = PHY54640_DEV_PHY_SLICE(pc);
   4571 
   4572     if ((!PHY_IS_BCM54640E(pc)) || PHY_IS_BCM54640E_A0A1(pc)) {
   4573         return SOC_E_UNAVAIL;
   4574     }
   4575 
   4576     conf->flags = 0;
   4577 
   4578     do {
   4579         /* pc->phy_id =  PHY54640_SLICE_ADDR(pc, 0); */
   4580         pc->phy_id =  PHY54640_DEV_PHY_ID_BASE(pc);
   4581 
   4582         SOC_IF_ERROR_BREAK
   4583             (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x4f, &nse_nco_6), rv);
   4584 
   4585         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_FLAGS) {
   4586             SOC_IF_ERROR_BREAK
   4587                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x10, &en_control_reg), rv); 
   4588 
   4589             if (en_control_reg & (1U << offset)) {
   4590             conf->flags |= SOC_PORT_PHY_TIMESYNC_ENABLE;
   4591             }
   4592 
   4593             SOC_IF_ERROR_BREAK
   4594                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x12, &tx_capture_en_reg), rv); 
   4595 
   4596             if (tx_capture_en_reg & (1U << offset)) {
   4597             conf->flags |= SOC_PORT_PHY_TIMESYNC_CAPTURE_TS_ENABLE;
   4598             }
   4599 
   4600             /*
   4601             SOC_IF_ERROR_BREAK
   4602                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x4f, &nse_nco_6), rv);
   4603              */
   4604 
   4605             if (nse_nco_6 & (1U << 13)) {
   4606             conf->flags |= SOC_PORT_PHY_TIMESYNC_HEARTBEAT_TS_ENABLE;
   4607             }
   4608 
   4609             SOC_IF_ERROR_BREAK
   4610             (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x34, &rx_crc_control_reg), rv);
   4611 
   4612             if (rx_crc_control_reg & (1U << 3)) {
   4613             conf->flags |= SOC_PORT_PHY_TIMESYNC_RX_CRC_ENABLE;
   4614             }
   4615 
   4616             SOC_IF_ERROR_BREAK
   4617                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x32, &tx_control_reg), rv);
   4618                               
   4619             if (tx_control_reg & (1U << 3)) {
   4620             conf->flags |= SOC_PORT_PHY_TIMESYNC_8021AS_ENABLE;
   4621             }
   4622 
   4623             if (tx_control_reg & (1U << 2)) {
   4624             conf->flags |= SOC_PORT_PHY_TIMESYNC_L2_ENABLE;
   4625             }
   4626 
   4627             if (tx_control_reg & (1U << 1)) {
   4628             conf->flags |= SOC_PORT_PHY_TIMESYNC_IP4_ENABLE;
   4629             }
   4630 
   4631             if (tx_control_reg & (1U << 0)) {
   4632             conf->flags |= SOC_PORT_PHY_TIMESYNC_IP6_ENABLE;
   4633             }
   4634 
   4635             SOC_IF_ERROR_BREAK
   4636                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x47, &nse_nco_2c), rv);
   4637 
   4638             if (!(nse_nco_2c & (1U << 14))) {
   4639             conf->flags |= SOC_PORT_PHY_TIMESYNC_CLOCK_SRC_EXT;
   4640             }
   4641         }
   4642 
   4643         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_GMODE) {
   4644             _phy_54640e_decode_gmode((nse_nco_6  >> 14),&gmode);
   4645             conf->gmode = gmode;
   4646         }
   4647 
   4648         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_FRAMESYNC_MODE) {
   4649             _phy_54640e_decode_framesync_mode((nse_nco_6  >> 2), &framesync_mode);
   4650             conf->framesync.mode = framesync_mode;
   4651 
   4652             SOC_IF_ERROR_BREAK
   4653                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x51, &temp1), rv);
   4654             SOC_IF_ERROR_BREAK
   4655                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x50, &temp2), rv);
   4656 
   4657             /* Multiply by 8 */
   4658             conf->framesync.length_threshold = temp1 << 3;
   4659             conf->framesync.event_offset = temp2 << 3;
   4660         }
   4661 
   4662         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_SYNCOUT_MODE) {
   4663             _phy_54640e_decode_syncout_mode((nse_nco_6  >> 0), &syncout_mode);
   4664             conf->syncout.mode = syncout_mode;
   4665 
   4666             SOC_IF_ERROR_BREAK
   4667                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x48, &temp1), rv);
   4668             SOC_IF_ERROR_BREAK
   4669                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x49, &temp2), rv);
   4670             SOC_IF_ERROR_BREAK
   4671                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x4a, &temp3), rv);
   4672 
   4673             /* Multiply by 8 */
   4674             conf->syncout.pulse_1_length = ((temp3 >> 7) & 0x1ff) << 3;
   4675             conf->syncout.pulse_2_length =  (((temp3 & 0x7f) << 2) | ((temp2 >> 14) & 0x3)) << 3;
   4676             conf->syncout.interval =  (((temp2 & 0x3fff) << 16) | temp1) << 3;
   4677 
   4678             SOC_IF_ERROR_BREAK
   4679                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x4e, &temp1), rv);
   4680             SOC_IF_ERROR_BREAK
   4681                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x4d, &temp2), rv);
   4682             SOC_IF_ERROR_BREAK
   4683                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x4c, &temp3), rv);
   4684 
   4685             COMPILER_64_SET(conf->syncout.syncout_ts, ((uint32)temp3),  (((uint32)temp2<<16)|((uint32)temp1)));
   4686         }
   4687 
   4688         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_ITPID) {
   4689             SOC_IF_ERROR_BREAK
   4690                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x35, &conf->itpid), rv);
   4691         }
   4692 
   4693         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_OTPID) {
   4694             SOC_IF_ERROR_BREAK
   4695                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x36, &conf->otpid), rv);
   4696         }
   4697 
   4698         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_OTPID2) {
   4699             SOC_IF_ERROR_BREAK
   4700                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x52, &conf->otpid2), rv);
   4701         }
   4702 
   4703         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TS_DIVIDER) {
   4704             SOC_IF_ERROR_BREAK
   4705                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x4b, &conf->ts_divider), rv);
   4706         }
   4707 
   4708         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_LINK_DELAY) {
   4709             pc->phy_id =  PHY54640_DEV_PHY_ID_ORIG(pc);
   4710 
   4711             SOC_IF_ERROR_BREAK
   4712                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x6f, &temp1), rv);
   4713             SOC_IF_ERROR_BREAK
   4714                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x70, &temp2), rv);
   4715 
   4716             conf->rx_link_delay = ((uint32)temp2 << 16) | temp1; 
   4717 
   4718             /* pc->phy_id =  PHY54640_SLICE_ADDR(pc, 0); */
   4719             pc->phy_id =  PHY54640_DEV_PHY_ID_BASE(pc);
   4720         }
   4721 
   4722 
   4723         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_ORIGINAL_TIMECODE) {
   4724             SOC_IF_ERROR_BREAK
   4725                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x25, &temp1), rv);
   4726 
   4727             SOC_IF_ERROR_BREAK
   4728                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x26, &temp2), rv);
   4729 
   4730             conf->original_timecode.nanoseconds = ((uint32)temp2 << 16) | temp1; 
   4731 
   4732             SOC_IF_ERROR_BREAK
   4733                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x27, &temp1), rv);
   4734 
   4735             SOC_IF_ERROR_BREAK
   4736                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x28, &temp2), rv);
   4737 
   4738             SOC_IF_ERROR_BREAK
   4739                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x29, &temp3), rv);
   4740 
   4741             /* conf->original_timecode.seconds = ((uint64)temp3 << 32) | ((uint32)temp2 << 16) | temp1; */
   4742 
   4743             COMPILER_64_SET(conf->original_timecode.seconds, ((uint32)temp3),  (((uint32)temp2<<16)|((uint32)temp1)));
   4744         }
   4745 
   4746         /* tx_timestamp_offset */
   4747         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TX_TIMESTAMP_OFFSET) {
   4748             uint32 temp32;
   4749 
   4750             SOC_IF_ERROR_BREAK
   4751                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x15 + offset, &temp1), rv);
   4752             if (offset < 4 ) {
   4753             SOC_IF_ERROR_BREAK
   4754                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x63, &temp2), rv);
   4755             temp32 = (temp2 & (0xf << (offset << 2))) << ((4 - offset) << 2);
   4756             } else {
   4757             uint16 offset1 = offset - 4;
   4758             SOC_IF_ERROR_BREAK
   4759                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x64, &temp2), rv);
   4760             temp32 = (temp2 & (0xf << (offset1 << 2))) << ((4 - offset1) << 2);
   4761             }
   4762             conf->tx_timestamp_offset = temp32 | temp1;
   4763         }
   4764 
   4765         /* rx_timestamp_offset */
   4766         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_TIMESTAMP_OFFSET) {
   4767             uint32 temp32;
   4768 
   4769             SOC_IF_ERROR_BREAK
   4770                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x1d + offset, &temp1), rv);
   4771             if (offset < 4 ) {
   4772             SOC_IF_ERROR_BREAK
   4773                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x65, &temp2), rv);
   4774             temp32 = (temp2 & (0xf << (offset << 2))) << ((4 - offset) << 2);
   4775             } else {
   4776             uint16 offset1 = offset - 4;
   4777             SOC_IF_ERROR_BREAK
   4778                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x66, &temp2), rv);
   4779             temp32 = (temp2 & (0xf << (offset1 << 2))) << ((4 - offset1) << 2);
   4780             }
   4781             conf->rx_timestamp_offset = temp32 | temp1;
   4782         }
   4783 
   4784         pc->phy_id =  PHY54640_DEV_PHY_ID_ORIG(pc);
   4785 
   4786         SOC_IF_ERROR_BREAK
   4787             (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x11, &value), rv); 
   4788 
   4789         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TX_SYNC_MODE) {
   4790             _phy_54640e_decode_egress_message_mode(value, 0, &conf->tx_sync_mode);
   4791         }
   4792 
   4793         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TX_DELAY_REQUEST_MODE) {
   4794             _phy_54640e_decode_egress_message_mode(value, 2, &conf->tx_delay_request_mode);
   4795         }
   4796 
   4797         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TX_PDELAY_REQUEST_MODE) {
   4798             _phy_54640e_decode_egress_message_mode(value, 4, &conf->tx_pdelay_request_mode);
   4799         }
   4800 
   4801         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_TX_PDELAY_RESPONSE_MODE) {
   4802             _phy_54640e_decode_egress_message_mode(value, 6, &conf->tx_pdelay_response_mode);
   4803         }
   4804 
   4805         SOC_IF_ERROR_BREAK
   4806             (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x12, &value), rv); 
   4807 
   4808         value >>= 8;
   4809 
   4810         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_SYNC_MODE) {
   4811             _phy_54640e_decode_ingress_message_mode(value, 0, &conf->rx_sync_mode);
   4812         }
   4813 
   4814         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_DELAY_REQUEST_MODE) {
   4815             _phy_54640e_decode_ingress_message_mode(value, 2, &conf->rx_delay_request_mode);
   4816         }
   4817 
   4818         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_PDELAY_REQUEST_MODE) {
   4819             _phy_54640e_decode_ingress_message_mode(value, 4, &conf->rx_pdelay_request_mode);
   4820         }
   4821 
   4822         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_RX_PDELAY_RESPONSE_MODE) {
   4823             _phy_54640e_decode_ingress_message_mode(value, 6, &conf->rx_pdelay_response_mode);
   4824         }
   4825 
   4826         /* pc->phy_id =  PHY54640_SLICE_ADDR(pc, 0); */
   4827         pc->phy_id =  PHY54640_DEV_PHY_ID_BASE(pc);
   4828 
   4829         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_REF_PHASE) {
   4830             /* Initial ref phase [15:0] */
   4831             SOC_IF_ERROR_BREAK
   4832                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x38, &temp1), rv);
   4833 
   4834             /* Initial ref phase [31:16]  */
   4835             SOC_IF_ERROR_BREAK
   4836                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x39, &temp2), rv);
   4837 
   4838             /*  Initial ref phase [47:32] */
   4839             SOC_IF_ERROR_BREAK
   4840                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x3a, &temp3), rv);
   4841 
   4842             COMPILER_64_SET(conf->phy_1588_dpll_ref_phase, ((uint32)temp3), (((uint32)temp2<<16)|((uint32)temp1)));
   4843         }
   4844 
   4845         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_REF_PHASE_DELTA) {
   4846             /* Ref phase delta [15:0] */
   4847             SOC_IF_ERROR_BREAK
   4848                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x3b, &temp1), rv);
   4849 
   4850             /* Ref phase delta [31:16]  */
   4851             SOC_IF_ERROR_BREAK
   4852                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x3c, &temp2), rv);
   4853 
   4854             conf->phy_1588_dpll_ref_phase_delta = (temp2 << 16) | temp1;
   4855         }
   4856 
   4857         /* DPLL K1 & K2 */
   4858         SOC_IF_ERROR_BREAK
   4859             (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x3d, &temp1 ), rv);
   4860 
   4861         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_K1) {
   4862             conf->phy_1588_dpll_k1 = (temp1 >> 8 ) & 0xff;
   4863         }
   4864 
   4865         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_K2) {
   4866             conf->phy_1588_dpll_k2 = temp1 & 0xff;
   4867         }
   4868 
   4869         /* DPLL K3 */
   4870         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_K3) {
   4871             SOC_IF_ERROR_BREAK
   4872                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x3e, &temp1), rv);
   4873                     conf->phy_1588_dpll_k3 = temp1 & 0xff;
   4874         }
   4875 
   4876         if (conf->validity_mask & SOC_PORT_PHY_TIMESYNC_VALID_PHY_1588_DPLL_LOOP_FILTER) {
   4877             /* Initial loop filter[15:0] */
   4878             SOC_IF_ERROR_BREAK
   4879                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x3f, &temp1), rv);
   4880 
   4881             /* Initial loop filter[31:16]  */
   4882             SOC_IF_ERROR_BREAK
   4883                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x40, &temp2), rv);
   4884 
   4885             /*  Initial loop filter[47:32] */
   4886             SOC_IF_ERROR_BREAK
   4887                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x41, &temp3), rv);
   4888 
   4889             /* Initial loop filter[63:48]  */
   4890             SOC_IF_ERROR_BREAK
   4891                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x42, &temp4), rv);
   4892 
   4893             COMPILER_64_SET(conf->phy_1588_dpll_loop_filter, (((uint32)temp4<<16)|((uint32)temp3)), (((uint32)temp2<<16)|((uint32)temp1)));
   4894         }
   4895     } while (0);
   4896 
   4897     pc->phy_id =  PHY54640_DEV_PHY_ID_ORIG(pc);
   4898                                             
   4899     return rv;
   4900 }
   4901 
   4902 STATIC int
   4903 phy_54640_timesync_control_set(int unit, soc_port_t port, soc_port_control_phy_timesync_t type, uint64 value)
   4904 {
   4905     phy_ctrl_t *pc;
   4906     uint16 temp1, temp2;
   4907     uint32 value0;
   4908     int offset, rv = SOC_E_NONE;
   4909 
   4910     pc = EXT_PHY_SW_STATE(unit, port);
   4911 
   4912     offset = PHY54640_DEV_PHY_SLICE(pc);
   4913 
   4914     if ((!PHY_IS_BCM54640E(pc)) || PHY_IS_BCM54640E_A0A1(pc)) {
   4915         return SOC_E_UNAVAIL;
   4916     }
   4917 
   4918     do {
   4919         /* pc->phy_id =  PHY54640_SLICE_ADDR(pc, 0); */
   4920         pc->phy_id =  PHY54640_DEV_PHY_ID_BASE(pc);
   4921 
   4922         switch (type) {
   4923         case SOC_PORT_CONTROL_PHY_TIMESYNC_CAPTURE_TIMESTAMP:
   4924         case SOC_PORT_CONTROL_PHY_TIMESYNC_HEARTBEAT_TIMESTAMP:
   4925             return SOC_E_FAIL;
   4926 
   4927         case SOC_PORT_CONTROL_PHY_TIMESYNC_NCOADDEND:
   4928             SOC_IF_ERROR_BREAK
   4929                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x44, (uint16)COMPILER_64_LO(value)), rv);
   4930             break;
   4931 
   4932         case SOC_PORT_CONTROL_PHY_TIMESYNC_FRAMESYNC:
   4933            SOC_IF_ERROR_BREAK
   4934                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x4f, &temp1), rv);
   4935             temp2 = ((temp1 | (0x3 << 14) | (0x1 << 12)) & ~(0xf << 2)) | (0x1 << 5);
   4936             SOC_IF_ERROR_BREAK
   4937                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x4f, temp2), rv);
   4938             sal_udelay(1);
   4939             temp2 &= ~((0x1 << 5) | (0x1 << 12));
   4940             SOC_IF_ERROR_BREAK
   4941                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x4f, temp2), rv);
   4942             sal_udelay(1);
   4943             SOC_IF_ERROR_BREAK
   4944                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x4f, temp1), rv);
   4945             break;
   4946 
   4947         case SOC_PORT_CONTROL_PHY_TIMESYNC_LOCAL_TIME:
   4948             COMPILER_64_SHR(value, 4);
   4949             SOC_IF_ERROR_BREAK
   4950                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x45, (uint16)(COMPILER_64_LO(value) & 0xffff)), rv);
   4951             SOC_IF_ERROR_BREAK
   4952                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x46, (uint16)((COMPILER_64_LO(value) >> 16) & 0xffff)), rv);
   4953             SOC_IF_ERROR_BREAK
   4954                 (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x47, (uint16)(COMPILER_64_HI(value) & 0xfff), 0xfff), rv);
   4955             break;
   4956 
   4957         case SOC_PORT_CONTROL_PHY_TIMESYNC_LOAD_CONTROL:
   4958             temp1 = 0;
   4959             temp2 = 0;
   4960             value0 = COMPILER_64_LO(value);
   4961             if (value0 &  SOC_PORT_PHY_TIMESYNC_TN_LOAD) {
   4962                 temp1 |= 1U << 11;
   4963             }
   4964             if (value0 &  SOC_PORT_PHY_TIMESYNC_TN_ALWAYS_LOAD) {
   4965                 temp2 |= 1U << 11;
   4966             }
   4967             if (value0 &  SOC_PORT_PHY_TIMESYNC_TIMECODE_LOAD) {
   4968                 temp1 |= 1U << 10;
   4969             }
   4970             if (value0 &  SOC_PORT_PHY_TIMESYNC_TIMECODE_ALWAYS_LOAD) {
   4971                 temp2 |= 1U << 10;
   4972             }
   4973             if (value0 &  SOC_PORT_PHY_TIMESYNC_SYNCOUT_LOAD) {
   4974                 temp1 |= 1U << 9;
   4975             }
   4976             if (value0 &  SOC_PORT_PHY_TIMESYNC_SYNCOUT_ALWAYS_LOAD) {
   4977                 temp2 |= 1U << 9;
   4978             }
   4979             if (value0 &  SOC_PORT_PHY_TIMESYNC_NCO_DIVIDER_LOAD) {
   4980                 temp1 |= 1U << 8;
   4981             }
   4982             if (value0 &  SOC_PORT_PHY_TIMESYNC_NCO_DIVIDER_ALWAYS_LOAD) {
   4983                 temp2 |= 1U << 8;
   4984             }
   4985             if (value0 &  SOC_PORT_PHY_TIMESYNC_LOCAL_TIME_LOAD) {
   4986                 temp1 |= 1U << 7;
   4987             }
   4988             if (value0 &  SOC_PORT_PHY_TIMESYNC_LOCAL_TIME_ALWAYS_LOAD) {
   4989                 temp2 |= 1U << 7;
   4990             }
   4991             if (value0 &  SOC_PORT_PHY_TIMESYNC_NCO_ADDEND_LOAD) {
   4992                 temp1 |= 1U << 6;
   4993             }
   4994             if (value0 &  SOC_PORT_PHY_TIMESYNC_NCO_ADDEND_ALWAYS_LOAD) {
   4995                 temp2 |= 1U << 6;
   4996             }
   4997             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_LOOP_FILTER_LOAD) {
   4998                 temp1 |= 1U << 5;
   4999             }
   5000             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_LOOP_FILTER_ALWAYS_LOAD) {
   5001                 temp2 |= 1U << 5;
   5002             }
   5003             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_REF_PHASE_LOAD) {
   5004                 temp1 |= 1U << 4;
   5005             }
   5006             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_REF_PHASE_ALWAYS_LOAD) {
   5007                 temp2 |= 1U << 4;
   5008             }
   5009             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_REF_PHASE_DELTA_LOAD) {
   5010                 temp1 |= 1U << 3;
   5011             }
   5012             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_REF_PHASE_DELTA_ALWAYS_LOAD) {
   5013                 temp2 |= 1U << 3;
   5014             }
   5015             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_K3_LOAD) {
   5016                 temp1 |= 1U << 2;
   5017             }
   5018             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_K3_ALWAYS_LOAD) {
   5019                 temp2 |= 1U << 2;
   5020             }
   5021             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_K2_LOAD) {
   5022                 temp1 |= 1U << 1;
   5023             }
   5024             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_K2_ALWAYS_LOAD) {
   5025                 temp2 |= 1U << 1;
   5026             }
   5027             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_K1_LOAD) {
   5028                 temp1 |= 1U << 0;
   5029             }
   5030             if (value0 &  SOC_PORT_PHY_TIMESYNC_DPLL_K1_ALWAYS_LOAD) {
   5031                 temp2 |= 1U << 0;
   5032             }
   5033             SOC_IF_ERROR_BREAK
   5034                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x2e, temp1), rv);
   5035             SOC_IF_ERROR_BREAK
   5036                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x2f, temp2), rv);
   5037             break;
   5038 
   5039         case SOC_PORT_CONTROL_PHY_TIMESYNC_INTERRUPT_MASK:
   5040             temp1 = 0;
   5041 
   5042             value0 = COMPILER_64_LO(value);
   5043 
   5044             if (value0 &  SOC_PORT_PHY_TIMESYNC_TIMESTAMP_INTERRUPT_MASK) {
   5045                 temp1 |= 1U << 1;
   5046             }
   5047             if (value0 &  SOC_PORT_PHY_TIMESYNC_FRAMESYNC_INTERRUPT_MASK) {
   5048                 temp1 |= 1U << 0;
   5049             }
   5050             SOC_IF_ERROR_BREAK
   5051                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x30, temp1), rv);
   5052             break;
   5053 
   5054         case SOC_PORT_CONTROL_PHY_TIMESYNC_TX_TIMESTAMP_OFFSET:
   5055             value0 = COMPILER_64_LO(value);
   5056 
   5057             /* tx_timestamp_offset */
   5058             SOC_IF_ERROR_BREAK
   5059                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x15 + offset, (uint16)(value0 & 0xffff)), rv);
   5060 
   5061             if (offset < 4 ) {
   5062                 SOC_IF_ERROR_BREAK
   5063                     (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x63, 
   5064                         ((value0 & 0xf0000) >> ((4 - offset) << 2)), 0xf << (offset << 2)), rv);
   5065             } else {
   5066                 uint16 offset1 = offset - 4;
   5067                 SOC_IF_ERROR_BREAK
   5068                     (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x64, 
   5069                         ((value0 & 0xf0000) >> ((4 - offset1) << 2)), 0xf << (offset1 << 2)), rv);
   5070             }
   5071             break;
   5072 
   5073         case SOC_PORT_CONTROL_PHY_TIMESYNC_RX_TIMESTAMP_OFFSET:
   5074             value0 = COMPILER_64_LO(value);
   5075 
   5076             /* rx_timestamp_offset */
   5077             SOC_IF_ERROR_BREAK
   5078                 (_phy_54640e_blk_top_lvl_reg_write(unit, pc, 0, 0x1d + offset, (uint16)(value0 & 0xffff)), rv);
   5079 
   5080             if (offset < 4 ) {
   5081                 SOC_IF_ERROR_BREAK
   5082                     (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x65, 
   5083                         ((value0 & 0xf0000) >> ((4 - offset) << 2)), 0xf << (offset << 2)), rv);
   5084             } else {
   5085                 uint16 offset1 = offset - 4;
   5086                 SOC_IF_ERROR_BREAK
   5087                     (_phy_54640e_blk_top_lvl_reg_modify(unit, pc, 0, 0x66, 
   5088                         ((value0 & 0xf0000) >> ((4 - offset1) << 2)), 0xf << (offset1 << 2)), rv);
   5089             }
   5090             break;
   5091 
   5092 
   5093         default:
   5094             pc->phy_id =  PHY54640_DEV_PHY_ID_ORIG(pc);
   5095             return SOC_E_FAIL;
   5096         }
   5097     } while(0);
   5098 
   5099     pc->phy_id =  PHY54640_DEV_PHY_ID_ORIG(pc);
   5100 
   5101     return rv;
   5102 }
   5103 
   5104 STATIC int
   5105 phy_54640_timesync_control_get(int unit, soc_port_t port, soc_port_control_phy_timesync_t type, uint64 *value)
   5106 {
   5107     phy_ctrl_t *pc;
   5108     uint16 value0;
   5109     uint16 value1;
   5110     uint16 value2;
   5111     uint32 temp32;
   5112     int offset, rv = SOC_E_NONE;
   5113 
   5114     pc = EXT_PHY_SW_STATE(unit, port);
   5115 
   5116     offset = PHY54640_DEV_PHY_SLICE(pc);
   5117 
   5118     if ((!PHY_IS_BCM54640E(pc)) || PHY_IS_BCM54640E_A0A1(pc)) {
   5119         return SOC_E_UNAVAIL;
   5120     }
   5121 
   5122     do {
   5123         /* pc->phy_id =  PHY54640_SLICE_ADDR(pc, 0); */
   5124         pc->phy_id =  PHY54640_DEV_PHY_ID_BASE(pc);
   5125 
   5126         switch (type) {
   5127         case SOC_PORT_CONTROL_PHY_TIMESYNC_HEARTBEAT_TIMESTAMP:
   5128             SOC_IF_ERROR_BREAK
   5129                 (WRITE_PHY54640_EXP_READ_START_END_CTRLr(unit, pc, 0x1), rv);
   5130 
   5131             SOC_IF_ERROR_BREAK
   5132                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x56, &value0), rv);
   5133             SOC_IF_ERROR_BREAK
   5134                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x57, &value1), rv);
   5135             SOC_IF_ERROR_BREAK
   5136                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x58, &value2), rv);
   5137 
   5138             SOC_IF_ERROR_BREAK
   5139                 (WRITE_PHY54640_EXP_READ_START_END_CTRLr(unit, pc, 0x2), rv);
   5140             SOC_IF_ERROR_BREAK
   5141                 (WRITE_PHY54640_EXP_READ_START_END_CTRLr(unit, pc, 0x0), rv);
   5142 
   5143             COMPILER_64_SET((*value), ((uint32)value2),  (((uint32)value1<<16)|((uint32)value0)));
   5144         /*    *value =  (((uint64)value2) << 32) | (((uint64)value1) << 16) | ((uint64)value0); */
   5145             break;
   5146 
   5147         case SOC_PORT_CONTROL_PHY_TIMESYNC_CAPTURE_TIMESTAMP:
   5148             SOC_IF_ERROR_BREAK
   5149                 (WRITE_PHY54640_EXP_READ_START_END_CTRLr(unit, pc, 0x4), rv);
   5150 
   5151             SOC_IF_ERROR_BREAK
   5152                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x53, &value0), rv);
   5153             SOC_IF_ERROR_BREAK
   5154                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x54, &value1), rv);
   5155             SOC_IF_ERROR_BREAK
   5156                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x55, &value2), rv);
   5157 
   5158             SOC_IF_ERROR_BREAK
   5159                 (WRITE_PHY54640_EXP_READ_START_END_CTRLr(unit, pc, 0x8), rv);
   5160             SOC_IF_ERROR_BREAK
   5161                 (WRITE_PHY54640_EXP_READ_START_END_CTRLr(unit, pc, 0x0), rv);
   5162 
   5163             COMPILER_64_SET((*value), ((uint32)value2),  (((uint32)value1<<16)|((uint32)value0)));
   5164         /*   *value = (((uint64)value2) << 32) | (((uint64)value1) << 16) | ((uint64)value0); */
   5165             break;
   5166 
   5167         case SOC_PORT_CONTROL_PHY_TIMESYNC_NCOADDEND:
   5168             SOC_IF_ERROR_BREAK
   5169                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x44, &value0), rv);
   5170             /* *value = value0; */
   5171             COMPILER_64_SET((*value), 0,  (uint32)value0);
   5172             break;
   5173 
   5174         case SOC_PORT_CONTROL_PHY_TIMESYNC_LOCAL_TIME:
   5175             SOC_IF_ERROR_BREAK
   5176                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x45, &value0), rv);
   5177             SOC_IF_ERROR_BREAK
   5178                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x46, &value1), rv);
   5179             SOC_IF_ERROR_BREAK
   5180                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x47, &value2), rv);
   5181 
   5182             COMPILER_64_SET((*value), (value2 & 0x0fff), ((uint32)value1 << 16) | value0 );
   5183             COMPILER_64_SHL((*value), 4);
   5184             break;
   5185 
   5186         case SOC_PORT_CONTROL_PHY_TIMESYNC_LOAD_CONTROL:
   5187             SOC_IF_ERROR_BREAK
   5188                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x2e, &value1), rv);
   5189             SOC_IF_ERROR_BREAK
   5190                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x2f, &value2), rv);
   5191             value0 = 0;
   5192             if (value1 & (1U << 11)) {
   5193                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_TN_LOAD;
   5194             }
   5195             if (value2 & (1U << 11)) {
   5196                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_TN_ALWAYS_LOAD;
   5197             }
   5198             if (value1 & (1U << 10)) {
   5199                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_TIMECODE_LOAD;
   5200             }
   5201             if (value2 & (1U << 10)) {
   5202                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_TIMECODE_ALWAYS_LOAD;
   5203             }
   5204             if (value1 & (1U << 9)) {
   5205                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_SYNCOUT_LOAD;
   5206             }
   5207             if (value2 & (1U << 9)) {
   5208                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_SYNCOUT_ALWAYS_LOAD;
   5209             }
   5210             if (value1 & (1U << 8)) {
   5211                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_NCO_DIVIDER_LOAD;
   5212             }
   5213             if (value1 & (1U << 8)) {
   5214                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_NCO_DIVIDER_ALWAYS_LOAD;
   5215             }
   5216             if (value1 & (1U << 7)) {
   5217                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_LOCAL_TIME_LOAD;
   5218             }
   5219             if (value1 & (1U << 7)) {
   5220                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_LOCAL_TIME_ALWAYS_LOAD;
   5221             }
   5222             if (value1 & (1U << 6)) {
   5223                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_NCO_ADDEND_LOAD;
   5224             }
   5225             if (value2 & (1U << 6)) {
   5226                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_NCO_ADDEND_ALWAYS_LOAD;
   5227             }
   5228             if (value1 & (1U << 5)) {
   5229                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_LOOP_FILTER_LOAD;
   5230             }
   5231             if (value2 & (1U << 5)) {
   5232                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_LOOP_FILTER_ALWAYS_LOAD;
   5233             }
   5234             if (value1 & (1U << 4)) {
   5235                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_REF_PHASE_LOAD;
   5236             }
   5237             if (value2 & (1U << 4)) {
   5238                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_REF_PHASE_ALWAYS_LOAD;
   5239             }
   5240             if (value1 & (1U << 3)) {
   5241                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_REF_PHASE_DELTA_LOAD;
   5242             }
   5243             if (value2 & (1U << 3)) {
   5244                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_REF_PHASE_DELTA_ALWAYS_LOAD;
   5245             }
   5246             if (value1 & (1U << 2)) {
   5247                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_K3_LOAD;
   5248             }
   5249             if (value2 & (1U << 2)) {
   5250                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_K3_ALWAYS_LOAD;
   5251             }
   5252             if (value1 & (1U << 1)) {
   5253                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_K2_LOAD;
   5254             }
   5255             if (value2 & (1U << 1)) {
   5256                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_K2_ALWAYS_LOAD;
   5257             }
   5258             if (value1 & (1U << 0)) {
   5259                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_K1_LOAD;
   5260             }
   5261             if (value2 & (1U << 0)) {
   5262                 value0 |= (uint16) SOC_PORT_PHY_TIMESYNC_DPLL_K1_ALWAYS_LOAD;
   5263             }
   5264             COMPILER_64_SET((*value), 0, (uint32)value0);
   5265             break;
   5266 
   5267         case SOC_PORT_CONTROL_PHY_TIMESYNC_INTERRUPT:
   5268             SOC_IF_ERROR_BREAK
   5269                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x31, &value1), rv);
   5270             value0 = 0;
   5271             if (value1 & (1U << 1)) {
   5272                 value0 |= SOC_PORT_PHY_TIMESYNC_TIMESTAMP_INTERRUPT;
   5273             }
   5274             if (value1 & (1U << 0)) {
   5275                 value0 |= SOC_PORT_PHY_TIMESYNC_FRAMESYNC_INTERRUPT;
   5276             }
   5277             COMPILER_64_SET((*value), 0, (uint32)value0);
   5278             break;
   5279 
   5280         case SOC_PORT_CONTROL_PHY_TIMESYNC_INTERRUPT_MASK:
   5281             SOC_IF_ERROR_BREAK
   5282                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x30, &value1), rv);
   5283             value0 = 0;
   5284             if (value1 & (1U << 1)) {
   5285                 value0 |= SOC_PORT_PHY_TIMESYNC_TIMESTAMP_INTERRUPT_MASK;
   5286             }
   5287             if (value1 & (1U << 0)) {
   5288                 value0 |= SOC_PORT_PHY_TIMESYNC_FRAMESYNC_INTERRUPT_MASK;
   5289             }
   5290             COMPILER_64_SET((*value), 0, (uint32)value0);
   5291             break;
   5292 
   5293         case SOC_PORT_CONTROL_PHY_TIMESYNC_TX_TIMESTAMP_OFFSET:
   5294         /* tx_timestamp_offset */
   5295             SOC_IF_ERROR_BREAK
   5296                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x15 + offset, &value1), rv);
   5297             if (offset < 4 ) {
   5298                 SOC_IF_ERROR_BREAK
   5299                     (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x63, &value2), rv);
   5300                 temp32 = (value2 & (0xf << (offset << 2))) << ((4 - offset) << 2);
   5301             } else {
   5302                 uint16 offset1 = offset - 4;
   5303                 SOC_IF_ERROR_BREAK
   5304                     (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x64, &value2), rv);
   5305                 temp32 = (value2 & (0xf << (offset1 << 2))) << ((4 - offset1) << 2);
   5306             }
   5307             /* *value = temp32 | value1; */
   5308             COMPILER_64_SET((*value), 0, (uint32)(temp32 | value1));
   5309             break;
   5310 
   5311         case SOC_PORT_CONTROL_PHY_TIMESYNC_RX_TIMESTAMP_OFFSET:
   5312         /* rx_timestamp_offset */
   5313             SOC_IF_ERROR_BREAK
   5314                 (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x1d + offset, &value1), rv);
   5315             if (offset < 4 ) {
   5316                 SOC_IF_ERROR_BREAK
   5317                     (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x65, &value2), rv);
   5318                 temp32 = (value2 & (0xf << (offset << 2))) << ((4 - offset) << 2);
   5319             } else {
   5320                 uint16 offset1 = offset - 4;
   5321                 SOC_IF_ERROR_BREAK
   5322                     (_phy_54640e_blk_top_lvl_reg_read(unit, pc, 0, 0x66, &value2), rv);
   5323                 temp32 = (value2 & (0xf << (offset1 << 2))) << ((4 - offset1) << 2);
   5324             }
   5325             /* *value = temp32 | value1; */
   5326             COMPILER_64_SET((*value), 0, (uint32)(temp32 | value1));
   5327             break;
   5328 
   5329         default:
   5330             pc->phy_id =  PHY54640_DEV_PHY_ID_ORIG(pc);
   5331             return SOC_E_FAIL;
   5332         }
   5333     } while(0);
   5334 
   5335     pc->phy_id =  PHY54640_DEV_PHY_ID_ORIG(pc);
   5336 
   5337     return rv;
   5338 }
   5339 
   5340 #ifdef BROADCOM_DEBUG
   5341 
   5342 /*
   5343  * Function:
   5344  *      phy_54640_shadow_dump
   5345  * Purpose:
   5346  *      Debug routine to dump all shadow registers.
   5347  * Parameters:
   5348  *      unit - StrataSwitch unit #.
   5349  *      port - StrataSwitch port #.
   5350  */
   5351 
   5352 void
   5353 phy_54640_shadow_dump(int unit, soc_port_t port)
   5354 {
   5355     phy_ctrl_t *pc;
   5356     uint16      tmp;
   5357     int         i;
   5358 
   5359     pc       = EXT_PHY_SW_STATE(unit, port);
   5360 
   5361     /* Register 0x18 Shadows */
   5362     for (i = 0; i <= 7; i++) {
   5363         WRITE_PHY_REG(unit, pc, 0x18, (i << 12) | 0x7);
   5364         READ_PHY_REG(unit, pc, 0x18, &tmp);
   5365         if ((tmp & ~7) == 0x0000) {
   5366             continue;
   5367         }
   5368         LOG_CLI((BSL_META_U(unit,
   5369                             "0x18[0x%x]=0x%04x\n"), i, tmp));
   5370     }
   5371 
   5372     /* Register 0x1c Shadows */
   5373     for (i = 0; i <= 0x1f; i++) {
   5374         WRITE_PHY_REG(unit, pc, 0x1c, i << 10);
   5375         READ_PHY_REG(unit, pc, 0x1c, &tmp);
   5376         if ((tmp & ~0x7c00) == 0x0000) {
   5377             continue;
   5378         }
   5379         LOG_CLI((BSL_META_U(unit,
   5380                             "0x1c[0x%x]=0x%04x\n"), i, tmp));
   5381     }
   5382 
   5383     /* Register 0x17/0x15 Shadows */
   5384     for (i = 0; i <= 0x13; i++) {
   5385         WRITE_PHY_REG(unit, pc, 0x17, 0xf00 | i);
   5386         READ_PHY_REG(unit, pc, 0x15, &tmp);
   5387         if (tmp  == 0x0000) {
   5388             continue;
   5389         }
   5390         LOG_CLI((BSL_META_U(unit,
   5391                             "0x17[0x%x]=0x%04x\n"), i, tmp));
   5392     }
   5393 }
   5394 
   5395 #endif /* BROADCOM_DEBUG */
   5396 
   5397 
   5398 /*
   5399  * Variable:    phy_54640drv_ge
   5400  * Purpose:     PHY driver for 54640
   5401  */
   5402 
   5403 phy_driver_t phy_54640drv_ge = {
   5404     "54640 Gigabit PHY Driver",
   5405     phy_54640_init,
   5406     phy_fe_ge_reset,
   5407     phy_54640_link_get,
   5408     phy_54640_enable_set,
   5409     phy_54640_enable_get,
   5410     phy_54640_duplex_set,
   5411     phy_54640_duplex_get,
   5412     phy_54640_speed_set,
   5413     phy_54640_speed_get,
   5414     phy_54640_master_set,
   5415     phy_54640_master_get,
   5416     phy_54640_autoneg_set,
   5417     phy_54640_autoneg_get,
   5418     NULL,
   5419     NULL,
   5420     NULL,
   5421     phy_54640_lb_set,
   5422     phy_54640_lb_get,
   5423     phy_54640_interface_set,
   5424     phy_54640_interface_get,
   5425     NULL,
   5426     phy_54640_link_up,           /* Phy link up event */
   5427     phy_54640_link_down,         /* Phy link down event */
   5428     phy_54640_mdix_set,
   5429     phy_54640_mdix_get,
   5430     phy_54640_mdix_status_get,
   5431     phy_54640_medium_config_set,
   5432     phy_54640_medium_config_get,
   5433     phy_54640_medium_status,
   5434     phy_54640_cable_diag_dispatch,
   5435     NULL,                       /* phy_link_change */
   5436     phy_54640_control_set,      /* phy_control_set */ 
   5437     phy_54640_control_get,      /* phy_control_get */
   5438     phy_ge_reg_read,
   5439     phy_ge_reg_write,
   5440     phy_ge_reg_modify,
   5441     NULL,                        /* Phy event notify */ 
   5442     phy_54640_probe,             /* pd_probe */
   5443     phy_54640_ability_advert_set,
   5444     phy_54640_ability_advert_get,
   5445     phy_54640_ability_remote_get,
   5446     phy_54640_ability_local_get,
   5447     NULL,                        /* f/w set */
   5448     phy_54640_timesync_config_set,
   5449     phy_54640_timesync_config_get,
   5450     phy_54640_timesync_control_set,
   5451     phy_54640_timesync_control_get
   5452 };
   5453 #else /* INCLUDE_PHY_54640*/
   5454 int _soc_phy_54640_not_empty;
   5455 #endif /* INCLUDE_PHY_54640*/
   5456