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

phy8040.c (42778B)


      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:    phy8040.c
      8  * Purpose: Phy Driver supports for Broadcom 8040 multiplexer/retimer
      9  * Note:    The driver is written in a way to support the mux configuration.
     10  *          It always assumes one port connects to the switch side.
     11  *          other three ports (called mux port) may or may not connect to a phy
     12  *          device on the line side. 
     13  *          Only one link(switch side port <---> line side port) is active in
     14  *          any given time. 
     15  */
     16 
     17 #include <sal/types.h>
     18 #include <shared/bsl.h>
     19 #include <soc/drv.h>
     20 #include <soc/debug.h>
     21 #include <soc/error.h>
     22 #include <soc/phyreg.h>
     23 
     24 #include <soc/phy.h>
     25 #include <soc/phy/phyctrl.h>
     26 #include <soc/phy/drv.h>
     27 
     28 #if defined(INCLUDE_PHY_8040)
     29 #include "phydefs.h"      /* Must include before other phy related includes */
     30 #include "phyconfig.h"    /* Must be the first phy include after phydefs.h */
     31 #include "phyident.h"
     32 #include "phyreg.h"
     33 #include "phynull.h"
     34 #include "phyxehg.h"
     35 #include "phy8040.h"
     36 
     37 #define MAX_MPORTS      3  /* max number of mux ports on the device*/
     38 #define MAX_8040_PORTS  4  /* number of total xgxs ports on the device */
     39 #define PHY8040_MUX_PORT_INVALID   0x7f
     40 #define PHY_ADDR_NONE  0xFF
     41 #define PHY87X6_ID1_REVA   0x6034
     42 #define PHY87X6_ID1_REVB   0x6035
     43 #define PHY87X6_ID0        0x20
     44 
     45 /* 8040 device specific data descriptor. */ 
     46 #define PHY8040_DEV_DESC(pc)  ((phy8040_ctrl_t *)((pc)->driver_data))
     47  
     48 #define PHYDRV_CALL_NOARG(pc,dc,name,rv) \
     49     do { \
     50        if ((dc)->lp[(dc)->inx].nxt_pc) { \
     51 	phy_ctrl_t * tmp_pc = EXT_PHY_SW_STATE((pc)->unit, (pc)->port); \
     52 	EXT_PHY_SW_STATE((pc)->unit,(pc)->port)=(dc)->lp[(dc)->inx].nxt_pc; \
     53         rv = name((dc)->lp[(dc)->inx].nxt_pc->pd,(pc)->unit,(pc)->port); \
     54 	EXT_PHY_SW_STATE((pc)->unit, (pc)->port) = tmp_pc; \
     55        } \
     56     } while(0)
     57 
     58 #define PHYDRV_CALL_ARG1(pc,dc,name,arg0,rv) \
     59     do { \
     60        if ((dc)->lp[(dc)->inx].nxt_pc) { \
     61         phy_ctrl_t * tmp_pc = EXT_PHY_SW_STATE((pc)->unit, (pc)->port); \
     62         EXT_PHY_SW_STATE((pc)->unit,(pc)->port)=(dc)->lp[(dc)->inx].nxt_pc; \
     63         rv = name((dc)->lp[(dc)->inx].nxt_pc->pd,(pc)->unit,(pc)->port,arg0); \
     64         EXT_PHY_SW_STATE((pc)->unit, (pc)->port) = tmp_pc; \
     65        } \
     66     } while(0)
     67 
     68 #define PHYDRV_CALL_ARG2(pc,dc,name,arg0,arg1,rv) \
     69     do { \
     70        if ((dc)->lp[(dc)->inx].nxt_pc) { \
     71         phy_ctrl_t * tmp_pc = EXT_PHY_SW_STATE((pc)->unit, (pc)->port); \
     72         EXT_PHY_SW_STATE((pc)->unit,(pc)->port)=(dc)->lp[(dc)->inx].nxt_pc; \
     73         rv = name((dc)->lp[(dc)->inx].nxt_pc->pd,(pc)->unit,(pc)->port, \
     74 			arg0,arg1); \
     75         EXT_PHY_SW_STATE((pc)->unit, (pc)->port) = tmp_pc; \
     76        } \
     77     } while(0)
     78 
     79 #define PHYDRV_CALL_ARG3(pc,dc,name,arg0,arg1,arg2,rv) \
     80     do { \
     81        if ((dc)->lp[(dc)->inx].nxt_pc) { \
     82         phy_ctrl_t * tmp_pc = EXT_PHY_SW_STATE((pc)->unit, (pc)->port); \
     83         EXT_PHY_SW_STATE((pc)->unit,(pc)->port)=(dc)->lp[(dc)->inx].nxt_pc; \
     84         rv = name((dc)->lp[(dc)->inx].nxt_pc->pd,(pc)->unit,(pc)->port, \
     85                         arg0,arg1,arg2); \
     86         EXT_PHY_SW_STATE((pc)->unit, (pc)->port) = tmp_pc; \
     87        } \
     88     } while(0)
     89 
     90 /* mux_port_desc_t describes each mux port configuration. The value of each 
     91  * element must match the actual hardware configuration. For example, if the
     92  * mux port 0 connects a 8726 phy device with the mdio address 0x56, then the
     93  * port should be 0 and phy_addr should be 0x56.
     94  * The port is configured through spn_PHY_8040_MUX_PORT[x], x=0-2. 
     95  * The phy_addr is configured through spn_PORT_PHY_ADDR1.
     96  * Make sure all the configuration values match up the actual hardware configuration.
     97  */ 
     98 typedef struct {
     99     phy_ctrl_t *nxt_pc;  /* the driver to handle the connected phy device */
    100     uint16 port;             /* the 8040 port connecting to the phy device */
    101     uint8  phy_addr;         /* the mdio address of the phy device */
    102 } mux_port_desc_t;
    103  
    104 /* device specific info descriptor */
    105 
    106 typedef struct {
    107     /* The logical port map. Array index represents the logic port number. 
    108      * Each entry describes each mux port configuration. See comment on
    109      * mux_port_desc_t. 
    110      */ 
    111     mux_port_desc_t lp[MAX_MPORTS];
    112     uint16 switch_port;     /* switch port */
    113     uint16 num_mports;      /* number of mux port used */
    114     uint8 devids[MAX_8040_PORTS];   /* device ids, index is physical port number */
    115     uint8 inx;    /* active logic port number */
    116     uint8 sp_10g[MAX_MPORTS]; /* logical port speed */ 
    117 } phy8040_ctrl_t;
    118 
    119 /* default MDIO addresses of phy devices on the mux ports, per port based*/
    120 static uint8 ext_phy_dft_addr1[][MAX_MPORTS] = {
    121     /* first phy     second phy     third phy       */
    122     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 0  */
    123     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 1  */
    124     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 2  */
    125     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 3  */
    126     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 4  */
    127     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 5  */
    128     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 6  */
    129     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 7  */
    130     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 8  */
    131     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 9  */
    132     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 10  */
    133     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 11  */
    134     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 12  */
    135     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 13  */
    136     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 14  */
    137     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 15  */
    138     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 16  */
    139     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 17  */
    140     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 18  */
    141     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 19  */
    142     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 20  */
    143     {0x56,          PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 21  */
    144     {0x57,          PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 22  */
    145     {0x58,          PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 23  */
    146     {0x59,          PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 24  */
    147     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 25  */
    148     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 26  */
    149     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 27  */
    150     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 28  */
    151     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 29  */
    152     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 30  */
    153     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 31  */
    154     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 32  */
    155     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 33  */
    156     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 34  */
    157     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 35  */
    158     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 36  */
    159     {PHY_ADDR_NONE, PHY_ADDR_NONE, PHY_ADDR_NONE},  /* 37  */
    160 };
    161 
    162 #define MAX_DFT_ADDR_ENTRY \
    163   ((sizeof(ext_phy_dft_addr1)/sizeof(ext_phy_dft_addr1[0][0]))/MAX_MPORTS)
    164 
    165 /* local function prototypes */
    166 STATIC int _phy8040_speed_set(int unit, soc_port_t port, int speed);
    167 STATIC int _phy8040_speed_get(int unit, soc_port_t port, int *speed);
    168 
    169 
    170 static int
    171 _phy_8040_mux(int unit, soc_port_t port)
    172 {
    173     int              switch_port;
    174     int              active_mport;
    175     phy8040_ctrl_t * dc;
    176     phy_ctrl_t     *pc;
    177 
    178     pc = EXT_PHY_SW_STATE(unit, port);
    179                                                                                 
    180     dc = PHY8040_DEV_DESC(pc);
    181 
    182     active_mport = dc->lp[dc->inx].port;
    183     switch_port  = dc->switch_port;
    184 
    185     LOG_INFO(BSL_LS_SOC_PHY,
    186              (BSL_META_U(unit,
    187                          "8040: u=%d port=%d primary=%d secondary=%d.\n"), 
    188                          pc->unit, pc->port, switch_port, active_mport));
    189 
    190     /* Power down other two blocks. */
    191     SOC_IF_ERROR_RETURN
    192         (WRITE_PHY8040_PWR_CTRLr(pc->unit, pc, 
    193                             (PHY8040_PWRCTRL_RSVD| 
    194                              (0xf & 
    195                               ~((1<<switch_port)|
    196                                 (1<<active_mport))))));
    197     
    198     /* Set source for primary <---> secondary ports */
    199     SOC_IF_ERROR_RETURN
    200         (MODIFY_PHY8040_XGXSn_CTRLr(pc->unit, pc, switch_port,
    201                                PHY8040_XGXS_CTRL_SRC(active_mport),
    202                                PHY8040_XGXS_CTRL_SRC(0xf)));
    203     SOC_IF_ERROR_RETURN
    204         (MODIFY_PHY8040_XGXSn_CTRLr(pc->unit, pc, active_mport,
    205                                PHY8040_XGXS_CTRL_SRC(switch_port),
    206                                PHY8040_XGXS_CTRL_SRC(0xf)));
    207     
    208     /* Soft reset primary & secondary ports */
    209     SOC_IF_ERROR_RETURN
    210         (MODIFY_PHY8040_XGXSn_REG(pc->unit, pc, dc->devids[switch_port],
    211                              MII_CTRL_REG, MII_CTRL_RESET, MII_CTRL_RESET));
    212     SOC_IF_ERROR_RETURN
    213         (MODIFY_PHY8040_XGXSn_REG(pc->unit, pc, dc->devids[active_mport],
    214                              MII_CTRL_REG, MII_CTRL_RESET, MII_CTRL_RESET));
    215 
    216     return SOC_E_NONE;
    217 }
    218 
    219 /*
    220  * Function:
    221  *    phy_8040_init
    222  * Purpose:    
    223  *    Initialize 8040 phys. Configure each XGXS block with a unique device ID.
    224  *    The MDIO addressing is to use a single MDIO address and a different device
    225  *    id for each of the five device blocks. The top block's device ID is hardwired.
    226  *    The driver is written to only support the MDIO clause 45 protocol.
    227  *    Set up a valid link(switch port <---> mux port) and disable the unused mux 
    228  *    ports.
    229  * 
    230  * Parameters:
    231  *    unit - StrataSwitch unit #.
    232  *    port - StrataSwitch port #.
    233  * Returns:    
    234  *    SOC_E_NONE
    235  */
    236 STATIC int
    237 phy_8040_init(int unit, soc_port_t port)
    238 {
    239     phy8040_ctrl_t * dc;
    240     phy8040_ctrl_t * new_dc;
    241     int i;
    242     int rv = SOC_E_NONE;
    243     phy_ctrl_t     *pc;
    244     phy_ctrl_t *new_pc;
    245     uint32 mdio_adrs;
    246     void *tmp_holder;  /* temporary place holder */
    247     int size;
    248     int num_pc;
    249     soc_phy_info_t phy_info;
    250     extern int _ext_phy_probe(int unit, soc_port_t port,soc_phy_info_t *pi,
    251                               phy_ctrl_t *ext_pc);
    252 
    253     pc = EXT_PHY_SW_STATE(unit, port);
    254 
    255     /* allocate a temp storage to hold fixed number of control structures */
    256     size = sizeof(phy8040_ctrl_t) + MAX_MPORTS * sizeof (phy_ctrl_t);
    257     tmp_holder = sal_alloc(size, "PHY8040");
    258     if (tmp_holder == NULL) {
    259         return SOC_E_MEMORY;
    260     }
    261     sal_memset(tmp_holder, 0, size);
    262 
    263     dc = (phy8040_ctrl_t *)tmp_holder;    
    264  
    265     dc->devids[0] =PHY8040_C45_DEV_XGXS0;
    266     dc->devids[1] =PHY8040_C45_DEV_XGXS1;
    267     dc->devids[2] =PHY8040_C45_DEV_XGXS2;
    268     dc->devids[3] =PHY8040_C45_DEV_XGXS3;
    269 
    270     dc->switch_port   = soc_property_port_get(pc->unit, pc->port,
    271                                            spn_PHY_8040_SWITCH_PORT,
    272                                            PHY8040_DFLT_SWITCH_PORT);
    273     dc->lp[0].port = soc_property_port_get(pc->unit, pc->port,
    274                                            spn_PHY_8040_MUX_PORT0,
    275                                            PHY8040_DFLT_MUX_PORT0);
    276     dc->lp[1].port = soc_property_port_get(pc->unit, pc->port,
    277                                            spn_PHY_8040_MUX_PORT1,
    278                                            PHY8040_DFLT_MUX_PORT1);
    279     dc->lp[2].port = soc_property_port_get(pc->unit, pc->port,
    280                                            spn_PHY_8040_MUX_PORT2,
    281                                            PHY8040_MUX_PORT_INVALID);
    282 
    283     /* spn_PORT_PHY_ADDR1 configures 3 additional MDIO addresses for
    284      * the mux port with 8040 type phy 
    285      * bit       31 -- 24 23 -- 16 15 -- 8 7 -- 0
    286      * mdioAdrs           phy 3    phy 2   phy 1
    287      * phy 1 --> mux port 0
    288      * phy 2 --> mux port 1
    289      * phy 3 --> mux port 2
    290      */ 
    291     mdio_adrs = soc_property_port_get(pc->unit, pc->port,
    292                                            spn_PORT_PHY_ADDR1, 0);
    293 
    294     for (i = 0; i < MAX_MPORTS; i++) {
    295         if ((mdio_adrs >> (i * 8)) & 0xFF) {  /* use configured value */
    296             dc->lp[i].phy_addr = (mdio_adrs >> (i * 8)) & 0xFF;
    297         } else { /* use default value */
    298             if (pc->port < MAX_DFT_ADDR_ENTRY) {
    299                 dc->lp[i].phy_addr = ext_phy_dft_addr1[pc->port][i];
    300             }
    301         }
    302     }
    303     
    304     for (i = 0; i < MAX_MPORTS; i++) {
    305         if (dc->lp[i].port == PHY8040_MUX_PORT_INVALID) {
    306             break;
    307         }
    308     }
    309     dc->num_mports = i;
    310     
    311     /* probe the mux ports for the connected phys. */
    312 
    313     new_pc = (phy_ctrl_t *)(dc + 1);
    314     for (num_pc = 0,i = 0; i < dc->num_mports; i++) {
    315         new_pc->unit = unit;
    316         new_pc->port = port;
    317         new_pc->speed_max = pc->speed_max;
    318         new_pc->read = pc->read; /* use same MDIO read  as this device's */
    319         new_pc->write = pc->write; /*use same MDIO write as this device's */
    320         new_pc->phy_id = dc->lp[i].phy_addr;
    321 
    322         /* probe the next device at the given address */
    323         SOC_IF_ERROR_RETURN
    324             (_ext_phy_probe(unit, port, &phy_info, new_pc));
    325 
    326         if (new_pc->pd != NULL) {
    327             dc->lp[i].nxt_pc = new_pc;
    328             num_pc += 1;
    329             LOG_INFO(BSL_LS_SOC_PHY,
    330                      (BSL_META_U(unit,
    331                                  "_phy_8040_init: found next phy device"
    332                                  " u=%d p=%d id0=0x%x id1=0x%x\n"),
    333                       unit, port,new_pc->phy_id0,new_pc->phy_id1));
    334         }
    335         new_pc += 1;
    336     }
    337 
    338     /* device probe done, allocate memory for control structures */
    339 
    340     size = sizeof(phy8040_ctrl_t) + num_pc * sizeof (phy_ctrl_t);
    341     pc->driver_data = (void *)sal_alloc (size, pc->pd->drv_name);
    342     if (pc->driver_data == NULL) {
    343         sal_free(tmp_holder);
    344         return SOC_E_MEMORY;
    345     }
    346 
    347     /* copy control descriptors from temporary storage */
    348     new_dc = (phy8040_ctrl_t *)pc->driver_data;
    349     sal_memcpy(new_dc, dc, sizeof(phy8040_ctrl_t));
    350     new_pc = (phy_ctrl_t *)(new_dc + 1); 
    351     for (i = 0; i < dc->num_mports; i++) {
    352         if (dc->lp[i].nxt_pc) {
    353             sal_memcpy(new_pc, dc->lp[i].nxt_pc, sizeof(phy_ctrl_t));
    354             new_dc->lp[i].nxt_pc = new_pc;
    355             new_pc += 1;
    356         }
    357     }
    358 
    359     /* done copy, free temp storage */
    360     sal_free(tmp_holder);
    361 
    362     PHY_FLAGS_SET(pc->unit, pc->port,  PHY_FLAGS_FIBER | PHY_FLAGS_C45);
    363 
    364     /* Set our 4 internal XGXS block phy and dev addresses */
    365     for (i=0; i<MAX_8040_PORTS; i++)
    366       {
    367       SOC_IF_ERROR_RETURN
    368           (MODIFY_PHY8040_XGXSn_CTRLr(pc->unit, pc, i,
    369                     PHY8040_XGXS_CTRL_DEVID(new_dc->devids[i])|
    370                     PHY8040_XGXS_CTRL_PRTAD(pc->phy_id),
    371                     PHY8040_XGXS_CTRL_DEVID(0x1f)|
    372                     PHY8040_XGXS_CTRL_PRTAD(0x1f)));
    373       }
    374 
    375     /* determine the default active phy_ctrl desc */
    376     new_dc->inx = 0;
    377     
    378     _phy_8040_mux(pc->unit,pc->port);
    379 
    380     /* all logical ports default speed to 10G */
    381     for (i = 0; i < new_dc->num_mports; i++) {
    382         new_dc->sp_10g[i] = TRUE;
    383     }
    384     PHYDRV_CALL_NOARG(pc,new_dc,PHY_INIT,rv);
    385     return rv;
    386 }
    387 
    388 
    389 /*
    390  * Function:
    391  *    phy_8040_link_get
    392  * Purpose:
    393  *    Get layer2 connection status.
    394  * Parameters:
    395  *    unit - StrataSwitch unit #.
    396  *    port - StrataSwitch port #.
    397  *    link - address of memory to store link up/down state.
    398  * Returns:    
    399  *    SOC_E_NONE
    400  */
    401 
    402 STATIC int
    403 phy_8040_link_get(int unit, soc_port_t port, int *link)
    404 {
    405     int              switch_port;
    406     int              active_mport;
    407     uint16           switch_port_stat, mux_port_stat;
    408     uint16           sp_sync_stat, mp_sync_stat,rx_status;
    409     phy8040_ctrl_t * dc;
    410     phy_ctrl_t     *pc;
    411     int rv = SOC_E_NONE;
    412     int an = 0;
    413     int an_done = 0;
    414     int speed = 0;
    415     int speed_local = 0;
    416 
    417     pc = EXT_PHY_SW_STATE(unit, port);
    418                                                                                 
    419     dc = PHY8040_DEV_DESC(pc);
    420 
    421     if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_DISABLE)) {
    422         *link = FALSE;
    423         return SOC_E_NONE;
    424     }
    425 
    426     switch_port = dc->switch_port;
    427     active_mport = dc->lp[dc->inx].port;
    428 
    429     *link=FALSE;
    430 
    431     if (NULL != dc->lp[dc->inx].nxt_pc) {
    432     /* When the connected phy is in AN mode, its negotiated speed needs to be 
    433      * propagated to this device. Sync up here since this function is periodically
    434      * called by a dedicated linkscan task.  
    435      */
    436         PHYDRV_CALL_ARG1(pc,dc,PHY_LINK_GET,link,rv);
    437    
    438         if (*link == TRUE) {
    439             /* check if in an mode */ 
    440             PHYDRV_CALL_ARG2(pc,dc,PHY_AUTO_NEGOTIATE_GET,&an,&an_done,rv);
    441      
    442             if (an == TRUE && an_done == TRUE) {
    443                 /* get the speed */
    444                 PHYDRV_CALL_ARG1(pc,dc,PHY_SPEED_GET,&speed,rv);
    445                 _phy8040_speed_get(unit, port, &speed_local);
    446       
    447                 if (speed && (speed != speed_local)) {
    448                     /* set this device's speed accordingly */
    449                     _phy8040_speed_set(unit,port,speed);
    450                 }
    451             }
    452         } else {
    453             return rv;
    454         }
    455     }
    456 
    457     if (dc->sp_10g[dc->inx] == FALSE) {
    458         /* check 1G linkup status */
    459         SOC_IF_ERROR_RETURN
    460             (READ_PHY8040_XGXSn_REG(pc->unit, pc, dc->devids[active_mport],
    461                              PHY8040_RX_STATUS_REG,&rx_status));
    462 
    463         if ((rx_status & PHY8040_1G_LINKUP_MASK) == PHY8040_1G_LINKUP_MASK) {
    464             *link=TRUE;
    465         }
    466         SOC_IF_ERROR_RETURN
    467             (READ_PHY8040_XGXSn_REG(pc->unit, pc, dc->devids[switch_port],
    468                              PHY8040_RX_STATUS_REG,&rx_status));
    469         if ((rx_status & PHY8040_1G_LINKUP_MASK) == PHY8040_1G_LINKUP_MASK) {
    470             *link=TRUE;
    471         }
    472     } else {  
    473        /* check 10G link */
    474  
    475         SOC_IF_ERROR_RETURN
    476             (READ_PHY8040_XGXSn_REG(pc->unit, pc, 
    477                            dc->devids[switch_port],
    478                            MII_STAT_REG, &switch_port_stat));
    479         SOC_IF_ERROR_RETURN
    480             (READ_PHY8040_XGXSn_REG(pc->unit, pc, 
    481                            dc->devids[active_mport],
    482                            MII_STAT_REG, &mux_port_stat));
    483     
    484         *link = MII_STAT_LA & (switch_port_stat & mux_port_stat);
    485     
    486         SOC_IF_ERROR_RETURN
    487             (READ_PHY8040_XGXSn_REG(pc->unit, pc, 
    488                            dc->devids[switch_port],
    489                            PHY8040_LANE_STATUS_REG, 
    490                            &sp_sync_stat));
    491     
    492         SOC_IF_ERROR_RETURN
    493             (READ_PHY8040_XGXSn_REG(pc->unit, pc, 
    494                            dc->devids[active_mport],
    495                            PHY8040_LANE_STATUS_REG,
    496                            &mp_sync_stat));
    497         if (*link) {
    498             *link &= (((sp_sync_stat & mp_sync_stat) & 0xf) == 0xf) ? 
    499                   MII_STAT_LA : 0;
    500             if (!*link) {
    501                 LOG_INFO(BSL_LS_SOC_PHY,
    502                          (BSL_META_U(unit,
    503                                      "8040: Link set but no sync.\n"
    504                                      "      u=%d port=%d primary lane status=%X secondary lane"
    505                                      " status=%X.\n"), 
    506                           pc->unit, pc->port, sp_sync_stat, mp_sync_stat));
    507             }
    508         } else {
    509             LOG_VERBOSE(BSL_LS_SOC_PHY,
    510                         (BSL_META_U(unit,
    511                                     "8040: u=%d port=%d switch port=%X mux port=%X.\n"), 
    512                          pc->unit, pc->port, switch_port_stat, mux_port_stat));
    513         }
    514     }
    515 
    516     /* check the connected device */
    517     if (*link) {
    518         PHYDRV_CALL_ARG1(pc,dc,PHY_LINK_GET,link,rv);
    519     }
    520     return rv;
    521 }
    522 
    523 /*
    524  * Function:
    525  *    phy_8040_enable_set
    526  * Purpose:
    527  *    Enable/Disable phy 
    528  * Parameters:
    529  *    unit - StrataSwitch unit #.
    530  *    port - StrataSwitch port #.
    531  *    enable - on/off state to set
    532  * Returns:    
    533  *    SOC_E_NONE
    534  */
    535 
    536 STATIC int
    537 phy_8040_enable_set(int unit, soc_port_t port, int enable)
    538 {
    539     phy8040_ctrl_t * dc;
    540     uint16 data;
    541     int rv = SOC_E_NONE;
    542     phy_ctrl_t     *pc;
    543   
    544     pc = EXT_PHY_SW_STATE(unit, port);
    545     dc = PHY8040_DEV_DESC(pc);
    546 
    547     if (enable) {
    548         PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE);
    549     } else {
    550         PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE);
    551     }
    552 
    553     data = enable ? 0:MII_CTRL_PD;
    554 
    555     SOC_IF_ERROR_RETURN
    556         (MODIFY_PHY8040_XGXSn_REG(pc->unit, pc, dc->devids[dc->lp[dc->inx].port],
    557                              MII_CTRL_REG, data, MII_CTRL_PD));
    558 
    559     PHYDRV_CALL_ARG1(pc,dc,PHY_ENABLE_SET,enable,rv);
    560     
    561     return (rv);
    562 }
    563 
    564 STATIC int
    565 _phy8040_speed_set(int unit, soc_port_t port, int speed)
    566 {
    567     phy8040_ctrl_t * dc;
    568     int rv = SOC_E_NONE;
    569     phy_ctrl_t     *pc;
    570     uint8 devid[2];
    571     int i;
    572  
    573     pc = EXT_PHY_SW_STATE(unit, port);
    574     dc = PHY8040_DEV_DESC(pc);
    575 
    576     devid[0] = dc->devids[dc->lp[dc->inx].port];
    577     devid[1] = dc->devids[dc->switch_port];
    578 
    579     if (speed == 10000) { /* 10G */
    580         dc->sp_10g[dc->inx] = TRUE;
    581 
    582         /* various register configuration for 10G speed, powerup default values */
    583         for (i = 0; i < 2; i++) {
    584             SOC_IF_ERROR_RETURN
    585                 (WRITE_PHY8040_XGXSn_REG(pc->unit, pc, devid[i], 
    586                                       PHY8040_TX0A_CTRL1_REG, 0x0dd0));
    587         
    588             SOC_IF_ERROR_RETURN
    589                 (WRITE_PHY8040_XGXSn_REG(pc->unit, pc, devid[i], 
    590                                       PHY8040_XGXS_CTRL_REG,0x202e));
    591             SOC_IF_ERROR_RETURN
    592                 (WRITE_PHY8040_XGXSn_REG(pc->unit, pc, devid[i], 
    593                                       PHY8040_LANE_CTRL1_REG,0x0000));
    594 
    595             SOC_IF_ERROR_RETURN
    596                 (WRITE_PHY8040_XGXSn_REG(pc->unit, pc, devid[i], 
    597                                       PHY8040_LANE_CTRL2_REG,0xf000));
    598 
    599             SOC_IF_ERROR_RETURN
    600                 (WRITE_PHY8040_XGXSn_REG(pc->unit, pc, devid[i], 
    601                                       PHY8040_RX_CTRL_REG,0x1c0a));
    602         }
    603     } else if (speed == 1000) {  /* 1G */
    604 
    605         dc->sp_10g[dc->inx] = FALSE;
    606         /* various register configuration for 1G speed */
    607         for (i = 0; i < 2; i++) {
    608             SOC_IF_ERROR_RETURN
    609                 (WRITE_PHY8040_XGXSn_REG(pc->unit, pc, devid[i], 
    610                                         PHY8040_TX0A_CTRL1_REG,0x0dd0));
    611                                                                                      
    612             SOC_IF_ERROR_RETURN
    613                 (WRITE_PHY8040_XGXSn_REG(pc->unit, pc, devid[i], 
    614                                         PHY8040_XGXS_CTRL_REG, 0x263a));
    615             SOC_IF_ERROR_RETURN
    616                 (WRITE_PHY8040_XGXSn_REG(pc->unit, pc, devid[i], 
    617                                         PHY8040_LANE_CTRL1_REG,0xffff));
    618                                                                                      
    619             SOC_IF_ERROR_RETURN
    620                 (WRITE_PHY8040_XGXSn_REG(pc->unit, pc, devid[i], 
    621                                         PHY8040_LANE_CTRL2_REG,0x0000));
    622                                                                                      
    623             SOC_IF_ERROR_RETURN
    624                 (WRITE_PHY8040_XGXSn_REG(pc->unit, pc, devid[i], 
    625                                         PHY8040_RX_CTRL_REG,0x1c00));
    626         }
    627     }
    628 
    629     return (rv);
    630 }
    631 
    632 /*
    633  * Function:
    634  *      phy_8040_speed_set
    635  * Purpose:
    636  *      Set PHY speed
    637  * Parameters:
    638  *      unit - StrataSwitch unit #.
    639  *      port - StrataSwitch port #.
    640  *      speed - link speed in Mbps
    641  * Returns:
    642  *      SOC_E_NONE
    643  */
    644                                                                                      
    645 STATIC int
    646 phy_8040_speed_set(int unit, soc_port_t port, int speed)
    647 {
    648     phy8040_ctrl_t * dc;
    649     int rv = SOC_E_NONE;
    650     phy_ctrl_t     *pc;
    651 
    652     pc = EXT_PHY_SW_STATE(unit, port);
    653     dc = PHY8040_DEV_DESC(pc);
    654 
    655     _phy8040_speed_set(unit,port,speed);
    656 
    657     rv = SOC_E_UNAVAIL;
    658     PHYDRV_CALL_ARG1(pc,dc,PHY_SPEED_SET,speed,rv);
    659                                                                                      
    660     if (rv == SOC_E_UNAVAIL) {
    661         phy_ctrl_t  *int_pc;
    662         int_pc = INT_PHY_SW_STATE(unit, port);
    663                                                                                      
    664         if (NULL != int_pc) {
    665         rv = PHY_SPEED_SET(int_pc->pd, unit, port, speed);
    666         }
    667     }
    668     return (rv);
    669 }
    670 
    671 STATIC int
    672 _phy8040_speed_get(int unit, soc_port_t port, int *speed)
    673 {
    674     phy8040_ctrl_t * dc;
    675     int rv = SOC_E_NONE;
    676     phy_ctrl_t     *pc;
    677                                                                                      
    678     pc = EXT_PHY_SW_STATE(unit, port);
    679     dc = PHY8040_DEV_DESC(pc);
    680     if (dc->sp_10g[dc->inx] == FALSE) {
    681         *speed = 1000;
    682     } else {
    683         *speed = 10000;
    684     }
    685 
    686     return (rv);
    687 }
    688 
    689 /*
    690  * Function:
    691  *      phy_8040_speed_get
    692  * Purpose:
    693  *      Get PHY speed
    694  * Parameters:
    695  *      unit - StrataSwitch unit #.
    696  *      port - StrataSwitch port #.
    697  *      speed - current link speed in Mbps
    698  * Returns:
    699  *      SOC_E_NONE
    700  */
    701 
    702 STATIC int
    703 phy_8040_speed_get(int unit, soc_port_t port, int *speed)
    704 {
    705     phy8040_ctrl_t * dc;
    706     int rv = SOC_E_NONE;
    707     phy_ctrl_t     *pc;
    708 
    709     pc = EXT_PHY_SW_STATE(unit, port);
    710     dc = PHY8040_DEV_DESC(pc);
    711 
    712     _phy8040_speed_get(unit, port, speed);
    713     PHYDRV_CALL_ARG1(pc,dc,PHY_SPEED_GET,speed,rv);
    714 
    715     return (rv);
    716 }
    717 
    718 /*
    719  * Function:
    720  *      phy_8040_an_get
    721  * Purpose:
    722  *      Get the current auto-negotiation status (enabled/busy)
    723  * Parameters:
    724  *      unit - StrataSwitch unit #.
    725  *      port - StrataSwitch port #.
    726  *      an   - (OUT) if true, auto-negotiation is enabled.
    727  *      an_done - (OUT) if true, auto-negotiation is complete. This
    728  *              value is undefined if an == false.
    729  * Returns:
    730  *      SOC_E_XXX
    731  */
    732 
    733 STATIC int
    734 phy_8040_an_get(int unit, soc_port_t port, int *an, int *an_done)
    735 {
    736     phy8040_ctrl_t * dc;
    737     int rv = SOC_E_NONE;
    738     phy_ctrl_t     *pc;
    739 
    740     pc = EXT_PHY_SW_STATE(unit, port);
    741     dc = PHY8040_DEV_DESC(pc);
    742     *an= 0;
    743     *an_done = 0;
    744     PHYDRV_CALL_ARG2(pc,dc,PHY_AUTO_NEGOTIATE_GET,an,an_done,rv);
    745     return (rv); 
    746 }
    747 
    748 /*
    749  * Function:
    750  *      phy_8040_an_set
    751  * Purpose:
    752  *      Enable or disabled auto-negotiation on the specified port.
    753  * Parameters:
    754  *      unit - StrataSwitch unit #.
    755  *      port - StrataSwitch port #.
    756  *      an   - Boolean, if true, auto-negotiation is enabled
    757  *              (and/or restarted). If false, autonegotiation is disabled.
    758  * Returns:
    759  *      SOC_E_XXX
    760  */
    761 
    762 STATIC int
    763 phy_8040_an_set(int unit, soc_port_t port, int an)
    764 {
    765     phy8040_ctrl_t * dc;
    766     int rv = SOC_E_NONE;
    767     phy_ctrl_t     *pc;
    768 
    769     pc = EXT_PHY_SW_STATE(unit, port);
    770     dc = PHY8040_DEV_DESC(pc);
    771     PHYDRV_CALL_ARG1(pc,dc,PHY_AUTO_NEGOTIATE_SET,an,rv);
    772     return (rv);
    773 }
    774 
    775 /*
    776  * Function:
    777  *    phy_8040_lb_set
    778  * Purpose:
    779  *    Put 8040 in PHY PCS/PMA/PMD loopback
    780  * Parameters:
    781  *      unit - StrataSwitch unit #.
    782  *      port - StrataSwitch port #.
    783  *      enable - binary value for on/off (1/0)
    784  * Returns:    
    785  *    SOC_E_NONE
    786  */
    787 
    788 STATIC int
    789 phy_8040_lb_set(int unit, soc_port_t port, int enable)
    790 {
    791     uint16     tmp;
    792     phy8040_ctrl_t * dc;
    793     phy_ctrl_t     *pc;
    794     int rv = SOC_E_UNAVAIL;
    795     pc = EXT_PHY_SW_STATE(unit, port);
    796                                                                                 
    797     dc = PHY8040_DEV_DESC(pc);
    798 
    799     /* do the loopback from outmost phy if available */
    800     PHYDRV_CALL_ARG1(pc,dc,PHY_LOOPBACK_SET,enable,rv);
    801 
    802     if (rv != SOC_E_NONE) {
    803         tmp = enable ? MII_CTRL_LE : 0;
    804 
    805         SOC_IF_ERROR_RETURN
    806             (MODIFY_PHY8040_XGXSn_REG(pc->unit, pc, dc->devids[dc->lp[dc->inx].port],
    807                              MII_CTRL_REG, tmp, MII_CTRL_LE));
    808     }
    809 
    810     return SOC_E_NONE;
    811 }
    812 
    813 /*
    814  * Function:
    815  *    phy_8040_lb_get
    816  * Purpose:
    817  *    Get 8040 PHY loopback state
    818  * Parameters:
    819  *      unit - StrataSwitch unit #.
    820  *      port - StrataSwitch port #.
    821  *      enable - address of location to store binary value for on/off (1/0)
    822  * Returns:    
    823  *    SOC_E_NONE
    824  */
    825 
    826 STATIC int
    827 phy_8040_lb_get(int unit, soc_port_t port, int *enable)
    828 {
    829     uint16 tmp;
    830     int rv = SOC_E_UNAVAIL;
    831     phy8040_ctrl_t * dc;
    832     phy_ctrl_t     *pc;
    833 
    834     pc = EXT_PHY_SW_STATE(unit, port);
    835 
    836     dc = PHY8040_DEV_DESC(pc);
    837 
    838     /* check the loopback from outmost phy if available */
    839     PHYDRV_CALL_ARG1(pc,dc,PHY_LOOPBACK_GET,enable,rv);
    840 
    841     if (rv != SOC_E_NONE) {
    842         SOC_IF_ERROR_RETURN
    843             (READ_PHY8040_XGXSn_REG(pc->unit, pc, dc->devids[dc->lp[dc->inx].port],
    844                    MII_CTRL_REG,&tmp));
    845 
    846         *enable = (tmp & MII_CTRL_LE) ? TRUE : FALSE;
    847     }
    848     return SOC_E_NONE;
    849 }
    850 
    851 /*
    852  * Function:
    853  *      phy_8040_control_set
    854  * Purpose:
    855  *      Configure PHY device specific control fucntion.
    856  * Parameters:
    857  *      unit - StrataSwitch unit #.
    858  *      port - StrataSwitch port #.
    859  *      type  - Control to update
    860  *      value - New setting for the control
    861  *      type SOC_PHY_CONTROL_INTERFACE controls which mux port to use. 
    862  *      The associated value specifies the logical mux port number 0,1 and 2 
    863  * Returns:
    864  *      SOC_E_NONE
    865  */
    866 STATIC int
    867 phy_8040_control_set(int unit, soc_port_t port,
    868                      soc_phy_control_t type, uint32 value)
    869 {
    870     int         rv;
    871     phy8040_ctrl_t * dc;
    872     int logic_port;
    873     phy_ctrl_t     *pc;
    874     int speed;
    875 
    876     pc = EXT_PHY_SW_STATE(unit, port);
    877 
    878     dc = PHY8040_DEV_DESC(pc);
    879 
    880     if ((type < 0) || (type >= SOC_PHY_CONTROL_COUNT)) {
    881         return SOC_E_PARAM;
    882     }
    883                                                                                      
    884     rv = SOC_E_NONE;
    885     
    886     switch(type) {
    887         /* chose which physical port, value contains the selected 8040 port */
    888         case SOC_PHY_CONTROL_INTERFACE:
    889             logic_port = value;
    890             if (logic_port != dc->inx) {
    891 
    892                 if (logic_port < dc->num_mports) {
    893                     /* before changing to the new selected port, disable the 
    894                      * the current active port to prevent the false link
    895                      */
    896                     phy_8040_enable_set(pc->unit, pc->port, FALSE);
    897                     speed = dc->sp_10g[dc->inx];  /* remember previous speed*/
    898 
    899                     /* activate the new mux port */
    900                     dc->inx = logic_port;
    901 
    902                     _phy_8040_mux(pc->unit,pc->port);
    903                     phy_8040_enable_set(pc->unit, pc->port, TRUE);
    904                  
    905                     /* the switch port speed may be changed in the previous
    906                      * link. Make sure it has the right speed
    907                      */
    908                     if (speed != dc->sp_10g[dc->inx]) {
    909                         phy_ctrl_t  *int_pc;
    910 
    911                         speed = dc->sp_10g[dc->inx]? 10000:1000;
    912                         _phy8040_speed_set(unit,port,speed);
    913 
    914                         int_pc = INT_PHY_SW_STATE(unit, port);
    915                         
    916                         /* in case the linkscan doesn't detect the linkup event
    917                          * and set the serdes speed accordingly. we do it here
    918                          */                                                  
    919                         if (NULL != int_pc) {
    920                             rv = PHY_SPEED_SET(int_pc->pd, unit, port, speed);
    921                         }
    922                     }
    923                 }
    924             }
    925             break;
    926 
    927         case SOC_PHY_CONTROL_PREEMPHASIS:
    928             /* Write PRE-EMPHASIS */
    929             SOC_IF_ERROR_RETURN
    930                 (MODIFY_PHY8040_XGXSn_REG(pc->unit, pc, 
    931                                      dc->devids[dc->lp[dc->inx].port],
    932                                      PHY8040_PRE_EMPHASIS_REG,
    933                                      PHY8040_PRE_EMPHASIS(value),
    934                                      PHY8040_PRE_EMPHASIS(0xf)));
    935             break;
    936         case SOC_PHY_CONTROL_DRIVER_CURRENT:
    937             /* Write DRIVER Current */
    938             SOC_IF_ERROR_RETURN
    939                 (MODIFY_PHY8040_XGXSn_REG(pc->unit, pc, 
    940                                      dc->devids[dc->lp[dc->inx].port],
    941                                      PHY8040_PRE_EMPHASIS_REG,
    942                                      PHY8040_DRIVER_STRENGTH(value),
    943                                      PHY8040_DRIVER_STRENGTH(0xf)));
    944             break;            
    945         default:
    946             rv = SOC_E_UNAVAIL;
    947             break;
    948     }
    949     if (((soc_phy_control_t)type != SOC_PHY_CONTROL_INTERFACE) && 
    950         ((soc_phy_control_t)type != SOC_PHY_CONTROL_INTERFACE_MAX)) {
    951         PHYDRV_CALL_ARG2(pc,dc,PHY_CONTROL_SET,type,value,rv);
    952     }
    953     return rv;
    954 }
    955 /*
    956  * Function:
    957  *      phy_8040_control_get
    958  * Purpose:
    959  *      Get current control settign of the PHY.
    960  * Parameters:
    961  *      unit - StrataSwitch unit #.
    962  *      port - StrataSwitch port #.
    963  *      type  - Control to update
    964  *      value - (OUT)Current setting for the control
    965  * Returns:
    966  *      SOC_E_NONE
    967  */
    968 STATIC int
    969 phy_8040_control_get(int unit, soc_port_t port,
    970                      soc_phy_control_t type, uint32 *value)
    971 {
    972     int rv;
    973     uint16      val;
    974     phy8040_ctrl_t * dc;
    975     phy_ctrl_t     *pc;
    976 
    977     pc = EXT_PHY_SW_STATE(unit, port);
    978     dc = PHY8040_DEV_DESC(pc);
    979     
    980     if ((type < 0) || (type >= SOC_PHY_CONTROL_COUNT)) {
    981         return SOC_E_PARAM;
    982     }
    983 
    984     rv = SOC_E_NONE;
    985     
    986     switch(type) {
    987         case SOC_PHY_CONTROL_PREEMPHASIS:
    988             /* Read PRE-EMPHASIS */
    989             SOC_IF_ERROR_RETURN
    990                 (READ_PHY8040_XGXSn_REG(pc->unit, pc, 
    991                                    dc->devids[dc->lp[dc->inx].port],
    992                                    PHY8040_PRE_EMPHASIS_REG,
    993                                    &val));
    994             *value = (val >> 12) & 0xf;
    995             break;
    996         case SOC_PHY_CONTROL_DRIVER_CURRENT:
    997             /* Read DRIVER Current */
    998             SOC_IF_ERROR_RETURN
    999                 (READ_PHY8040_XGXSn_REG(pc->unit, pc, 
   1000                                    dc->devids[dc->lp[dc->inx].port],
   1001                                    PHY8040_PRE_EMPHASIS_REG,
   1002                                    &val));
   1003             *value = (val >> 8) & 0xf;
   1004             break;            
   1005         case SOC_PHY_CONTROL_INTERFACE:
   1006             *value = dc->inx;
   1007             break;
   1008 
   1009         case SOC_PHY_CONTROL_INTERFACE_MAX:
   1010             *value = dc->num_mports - 1;
   1011             break;
   1012         default:
   1013             rv = SOC_E_UNAVAIL;
   1014             break;
   1015     }
   1016 
   1017     if (((soc_phy_control_t)type != SOC_PHY_CONTROL_INTERFACE) && 
   1018         ((soc_phy_control_t)type != SOC_PHY_CONTROL_INTERFACE_MAX)) {
   1019         PHYDRV_CALL_ARG2(pc,dc,PHY_CONTROL_GET,type,value,rv);
   1020     }
   1021     return rv;
   1022 }
   1023 
   1024 /*
   1025  * Function:
   1026  *      phy_8040_interface_get
   1027  * Purpose:
   1028  *      Get the device's interface.
   1029  * Parameters:
   1030  *      unit - StrataSwitch unit #.
   1031  *      port - StrataSwitch port #.
   1032  *      pif - return device's interface.
   1033  * Returns:
   1034  *      SOC_E_XXX
   1035  */ 
   1036 STATIC int
   1037 phy_8040_interface_get(int unit, soc_port_t port, soc_port_if_t *pif)
   1038 {
   1039     int rv = SOC_E_UNAVAIL;
   1040     phy8040_ctrl_t * dc;
   1041     phy_ctrl_t     *pc;
   1042 
   1043     pc = EXT_PHY_SW_STATE(unit, port);
   1044     dc = PHY8040_DEV_DESC(pc);
   1045 
   1046     /* check the outmost phy first */
   1047     PHYDRV_CALL_ARG1(pc,dc,PHY_INTERFACE_GET,pif,rv);
   1048     if (rv != SOC_E_NONE) {
   1049         *pif = SOC_PORT_IF_XAUI;
   1050     }                                                                                
   1051     return SOC_E_NONE;
   1052 }
   1053                                                                                 
   1054 /*
   1055  * Function:
   1056  *      phy_8040_ability_local_get
   1057  * Purpose:  
   1058  *      Get the device's complete abilities.
   1059  * Parameters: 
   1060  *      unit - StrataSwitch unit #.
   1061  *      port - StrataSwitch port #.
   1062  *      ability - return device's abilities. 
   1063  * Returns: 
   1064  *      SOC_E_XXX
   1065  */                                                                                      
   1066 STATIC 
   1067 int phy_8040_ability_local_get(int unit, soc_port_t port, soc_port_ability_t *ability)
   1068 {
   1069     int rv = SOC_E_UNAVAIL;
   1070     phy8040_ctrl_t * dc;
   1071     phy_ctrl_t     *pc;
   1072 
   1073     pc = EXT_PHY_SW_STATE(unit, port);
   1074     dc = PHY8040_DEV_DESC(pc);
   1075 
   1076     LOG_INFO(BSL_LS_SOC_PHY,
   1077              (BSL_META_U(unit,
   1078                          "phy_8040_ability_local_get: u=%d p=%d\n"),
   1079               pc->unit, pc->port));                                                                                      
   1080     if (NULL == ability) {    
   1081         return SOC_E_PARAM;     
   1082     }                                                                                      
   1083     /* check the outmost phy first */
   1084     PHYDRV_CALL_ARG1(pc,dc,PHY_ABILITY_LOCAL_GET,ability,rv);
   1085     if (rv != SOC_E_NONE) {
   1086         ability->speed_half_duplex  = SOC_PA_ABILITY_NONE;
   1087         ability->speed_full_duplex  = SOC_PA_SPEED_10GB | SOC_PA_SPEED_1000MB;
   1088         ability->pause     = 0;
   1089         ability->interface = SOC_PA_INTF_XGMII;
   1090         ability->medium    = SOC_PA_MEDIUM_FIBER;
   1091         ability->loopback  = SOC_PA_LB_PHY;
   1092         ability->flags     = 0;
   1093         LOG_INFO(BSL_LS_SOC_PHY,
   1094                  (BSL_META_U(unit,
   1095                              "phy_8040_ability_local_get: u=%d p=%d speed=0x%x\n"),
   1096                   pc->unit, pc->port, ability->speed_full_duplex));
   1097     }
   1098     return (SOC_E_NONE);
   1099 }
   1100 
   1101 /*
   1102  * Function:
   1103  *      phy_8040_ability_advert_get
   1104  * Purpose:
   1105  *      Get the current advertisement for auto-negotiation.
   1106  * Parameters:
   1107  *      unit - StrataSwitch unit #.
   1108  *      port - StrataSwitch port #.
   1109  *      mode - (OUT) Port mode mask indicating supported options/speeds.
   1110  * Returns:
   1111  *      SOC_E_XXX
   1112  * Notes:
   1113  *      The advertisement is retrieved for the ACTIVE medium.
   1114  *      No synchronization performed at this level.
   1115  */
   1116 
   1117 STATIC int
   1118 phy_8040_ability_advert_get(int unit, soc_port_t port, soc_port_ability_t *ability)
   1119 {
   1120     int rv = SOC_E_UNAVAIL;
   1121     phy8040_ctrl_t * dc;
   1122     phy_ctrl_t     *pc;
   1123 
   1124     pc = EXT_PHY_SW_STATE(unit, port);
   1125     dc = PHY8040_DEV_DESC(pc);
   1126 
   1127     PHYDRV_CALL_ARG1(pc,dc,PHY_ABILITY_ADVERT_GET,ability,rv);
   1128     if (rv != SOC_E_NONE) {
   1129         ability->speed_full_duplex = 0;
   1130     }
   1131     return (SOC_E_NONE);
   1132 }
   1133 
   1134 /*
   1135  * Function:
   1136  *      phy_8040_ability_advert_remote_get
   1137  * Purpose:
   1138  *      Get the remote device's current advertisement for auto-negotiation.
   1139  * Parameters:
   1140  *      unit - StrataSwitch unit #.
   1141  *      port - StrataSwitch port #.
   1142  *      mode - (OUT) Port mode mask indicating supported options/speeds.
   1143  * Returns:
   1144  *      SOC_E_XXX
   1145  * Notes:
   1146  *      The advertisement is retrieved for the ACTIVE medium.
   1147  *      No synchronization performed at this level.
   1148  */
   1149 
   1150 STATIC int
   1151 phy_8040_ability_advert_remote_get(int unit, soc_port_t port, soc_port_ability_t *ability)
   1152 {
   1153     int rv = SOC_E_UNAVAIL;
   1154     phy8040_ctrl_t * dc;
   1155     phy_ctrl_t     *pc;
   1156 
   1157     pc = EXT_PHY_SW_STATE(unit, port);
   1158     dc = PHY8040_DEV_DESC(pc);
   1159 
   1160     PHYDRV_CALL_ARG1(pc,dc,PHY_ABILITY_REMOTE_GET,ability,rv);
   1161     if (rv != SOC_E_NONE) {
   1162         ability->speed_full_duplex = 0;
   1163     }
   1164     return (SOC_E_NONE);
   1165 }
   1166 
   1167 /*
   1168  * Function:
   1169  *      phy_8040_ability_advert_set
   1170  * Purpose:
   1171  *      Set the current advertisement for auto-negotiation.
   1172  * Parameters:
   1173  *      unit - StrataSwitch unit #.
   1174  *      port - StrataSwitch port #.
   1175  *      mode - Port mode mask indicating supported options/speeds.
   1176  * Returns:
   1177  *      SOC_E_XXX
   1178  * Notes:
   1179  *      The advertisement is set only for the ACTIVE medium.
   1180  *      No synchronization performed at this level.
   1181  */
   1182 
   1183 STATIC int
   1184 phy_8040_ability_advert_set(int unit, soc_port_t port, soc_port_ability_t *ability)
   1185 {
   1186     int rv = SOC_E_NONE;
   1187     phy8040_ctrl_t * dc;
   1188     phy_ctrl_t     *pc;
   1189 
   1190     pc = EXT_PHY_SW_STATE(unit, port);
   1191     dc = PHY8040_DEV_DESC(pc);
   1192 
   1193     PHYDRV_CALL_ARG1(pc,dc,PHY_ABILITY_ADVERT_SET,ability,rv);
   1194     return (rv);
   1195 }
   1196 
   1197 /*
   1198  * Function:
   1199  *      phy_8481_firmware_set
   1200  * Purpose:
   1201  *      program the given firmware into the SPI-ROM
   1202  * Parameters:
   1203  *      unit - StrataSwitch unit #.
   1204  *      port - StrataSwitch port #.
   1205  *      offset - offset to the data stream
   1206  *      array  - the given data
   1207  *      datalen- the data length
   1208  * Returns:
   1209  *      SOC_E_NONE
   1210  */
   1211 
   1212 STATIC int
   1213 phy_8040_firmware_set(int unit, int port, int offset, uint8 *data,int len)
   1214 {
   1215     phy8040_ctrl_t * dc;
   1216     phy_ctrl_t     *pc;
   1217     int rv = SOC_E_NONE;
   1218 
   1219     pc = EXT_PHY_SW_STATE(unit, port);
   1220     dc = PHY8040_DEV_DESC(pc);
   1221 
   1222     if (NULL != dc->lp[dc->inx].nxt_pc) {
   1223         PHYDRV_CALL_ARG3(pc,dc,PHY_FIRMWARE_SET,offset,data,len,rv);
   1224         return rv;
   1225     }
   1226     return SOC_E_UNAVAIL;
   1227 }
   1228 
   1229 /*
   1230  * Variable:
   1231  *    phy_8040_drv
   1232  * Purpose:
   1233  *    Phy Driver for 10G (XAUI x 4) Serdes PHY. 
   1234  */
   1235 
   1236 phy_driver_t phy_8040drv_xe = {
   1237     "8040 10-Gigabit PHY Driver",
   1238     phy_8040_init,        /* Init */
   1239     phy_null_reset,       /* Reset (dummy) */
   1240     phy_8040_link_get,    /* Link get: XXX: talk to Bob */
   1241     phy_8040_enable_set,  /* Enable set: : XXX: talk to Bob    */
   1242     phy_null_enable_get,  /* Enable get */
   1243     phy_null_set,         /* Duplex set */
   1244     phy_null_one_get,     /* Duplex get */
   1245     phy_8040_speed_set,   /* Speed set  */
   1246     phy_8040_speed_get,   /* Speed get  */
   1247     phy_null_set,         /* Master set */
   1248     phy_null_zero_get,    /* Master get */
   1249     phy_8040_an_set,         /* ANA set */
   1250     phy_8040_an_get,      /* ANA get */
   1251     phy_null_mode_set,    /* Local Advert set */
   1252     phy_null_mode_get,    /* Local Advert get */
   1253     phy_null_mode_get,    /* Remote Advert get */
   1254     phy_8040_lb_set,      /* PHY loopback set: XXX: talk to Bob    */
   1255     phy_8040_lb_get,      /* PHY loopback set: XXX: talk to Bob    */
   1256     phy_null_interface_set, /* IO Interface set */
   1257     phy_8040_interface_get, /* IO Interface get */
   1258     NULL,   /* PHY abilities mask */
   1259     NULL,
   1260     NULL,
   1261     phy_null_mdix_set,
   1262     phy_null_mdix_get,
   1263     phy_null_mdix_status_get,
   1264     NULL,
   1265     NULL,
   1266     phy_null_medium_get,
   1267     NULL,                    /* phy_cable_diag  */
   1268     NULL,                    /* phy_link_change */
   1269     phy_8040_control_set,    /* phy_control_set */
   1270     phy_8040_control_get,    /* phy_control_get */
   1271     NULL,                    /* phy_reg_read */
   1272     NULL,                    /* phy_reg_write */
   1273     NULL,                    /* phy_reg_modify */
   1274     NULL,                    /* phy_notify */
   1275     NULL,                    /* pd_probe */
   1276     phy_8040_ability_advert_set,  /* pd_ability_advert_set */
   1277     phy_8040_ability_advert_get,  /* pd_ability_advert_get */
   1278     phy_8040_ability_advert_remote_get,     /* pd_ability_remote_get */
   1279     phy_8040_ability_local_get,   /* pd_ability_local_get  */
   1280     phy_8040_firmware_set 
   1281 };
   1282 #else /* INCLUDE_PHY_8040 */
   1283 int _phy_8040_not_empty;
   1284 #endif /* INCLUDE_PHY_8040 */
   1285