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

linkctrl.c (11826B)


      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  * Hardware Linkscan module
      8  *
      9  * For legacy devices (pre-Arad and pre-XGS5) Hardware linkscan is not recommended
     10  *
     11  * If hardware linkscan is used, each MII operation must temporarily
     12  * disable it and wait for the current scan to complete, increasing the
     13  * latency.  PHY status register 1 may contain clear-on-read bits that
     14  * will be cleared by hardware linkscan and not seen later.  Special
     15  * support is provided for the Serdes MAC.
     16  *
     17  * NOTE:
     18  * Original file is src/soc/common/link.c, version 1.7.
     19  * That file should eventually be removed, and XGS specific routines
     20  * should be implemented and set for the 'driver' during module
     21  * initialization in 'soc_linkctrl_init()'.
     22  */
     23 #include <soc/error.h>
     24 #include <soc/drv.h>
     25 #include <soc/linkctrl.h>
     26 
     27 typedef struct _soc_linkctrl_s {
     28     pbmp_t                 link_fwd;
     29     pbmp_t                 link_mask;
     30     int                    link_pause;  /* Link scan pause count */
     31     CONST soc_linkctrl_driver_t  *driver;
     32 } _soc_linkctrl_t;
     33 
     34 static _soc_linkctrl_t    _link_control[SOC_MAX_NUM_DEVICES];
     35 
     36 /*
     37  * General util macros
     38  */
     39 #define UNIT_VALID_CHECK(_unit) \
     40     if (((_unit) < 0) || ((_unit) >= SOC_MAX_NUM_DEVICES)) { \
     41         return SOC_E_UNIT; \
     42     }
     43 
     44 #define UNIT_INIT_CHECK(_unit) \
     45     do { \
     46         UNIT_VALID_CHECK(_unit); \
     47         if (LINKCTRL_DRV(_unit) == NULL) { return SOC_E_INIT; } \
     48     } while (0)
     49 
     50 #define PARAM_NULL_CHECK(_param) \
     51     if ((_param) == NULL) { return SOC_E_PARAM; }
     52 
     53 
     54 #define LINKCTRL(_unit)         (_link_control[_unit])
     55 #define LINKCTRL_DRV(_unit)     (_link_control[_unit].driver)
     56 
     57 #define LINK_MIIM_PORT_INFO(_unit)  (LINKCTRL_DRV(_unit)->port_info)
     58 
     59 #define _LC_CALL(_ld, _lf, _la) \
     60     ((_ld) == 0 ? SOC_E_PARAM : \
     61      ((_ld)->_lf == 0 ? SOC_E_UNAVAIL : (_ld)->_lf _la))
     62 
     63 #define LINKSCAN_HW_INIT(_u) \
     64     _LC_CALL(LINKCTRL_DRV(_u), ld_linkscan_hw_init, (_u))
     65 
     66 #define LINKSCAN_CONFIG(_u, _mii, _dir) \
     67     _LC_CALL(LINKCTRL_DRV(_u), ld_linkscan_config, ((_u), (_mii), (_dir)))
     68 
     69 #define LINKSCAN_PAUSE(_u) \
     70     _LC_CALL(LINKCTRL_DRV(_u), ld_linkscan_pause, (_u))
     71 
     72 #define LINKSCAN_CONTINUE(_u) \
     73     _LC_CALL(LINKCTRL_DRV(_u), ld_linkscan_continue, (_u))
     74 
     75 #define LINK_UPDATE(_u) \
     76     _LC_CALL(LINKCTRL_DRV(_u), ld_update, (_u))
     77 
     78 #define LINKSCAN_HW_LINK_GET(_u, _hw_link) \
     79     _LC_CALL(LINKCTRL_DRV(_u), ld_hw_link_get, ((_u), (_hw_link)))
     80 
     81 /*
     82  * Function:
     83  *     soc_linkctrl_linkscan_hw_init
     84  * Purpose:
     85  *     Initialize device hardware linkscan.
     86  * Parameters:
     87  *     unit - Device unit number
     88  * Returns:
     89  *     SOC_E_XXX
     90  */
     91 int
     92 soc_linkctrl_linkscan_hw_init(int unit)
     93 {
     94     UNIT_INIT_CHECK(unit);
     95 
     96     return LINKSCAN_HW_INIT(unit);
     97 }
     98 
     99 /*
    100  * Function:
    101  *     soc_linkctrl_init
    102  * Purpose:
    103  *     Initialize SOC link control module.
    104  * Parameters:
    105  *     unit   - Device unit number
    106  *     driver - Device functions
    107  * Returns:
    108  *     SOC_E_XXX
    109  */
    110 int
    111 soc_linkctrl_init(int unit, CONST soc_linkctrl_driver_t *driver)
    112 {
    113     UNIT_VALID_CHECK(unit);
    114     PARAM_NULL_CHECK(driver);
    115 
    116 #ifdef BCM_LINKSCAN_LOCK_PER_UNIT
    117     if (soc_feature(unit, soc_feature_linkscan_lock_per_unit)) {
    118         SOC_CONTROL(unit)->linkscan_mutex = sal_mutex_create("Linkscan Lock");
    119     }
    120 #endif
    121 
    122     SOC_PBMP_CLEAR(LINKCTRL(unit).link_fwd);
    123     LINKCTRL(unit).link_mask  = PBMP_ALL(unit);
    124     LINKCTRL(unit).link_pause = 0;
    125     LINKCTRL(unit).driver     = driver;
    126 
    127     return SOC_E_NONE;
    128 }
    129 
    130 int
    131 soc_linkctrl_deinit(int unit)
    132 {
    133 
    134 #ifdef BCM_LINKSCAN_LOCK_PER_UNIT
    135     if (soc_feature(unit, soc_feature_linkscan_lock_per_unit)) {
    136         if(NULL != SOC_CONTROL(unit)->linkscan_mutex) {
    137             sal_mutex_destroy(SOC_CONTROL(unit)->linkscan_mutex);
    138             SOC_CONTROL(unit)->linkscan_mutex = NULL;
    139         }
    140     }
    141 #endif
    142 
    143     return SOC_E_NONE;
    144 
    145 }
    146 
    147 
    148 /*
    149  * Function:
    150  *     soc_linkctrl_link_fwd_set
    151  * Purpose:
    152  *     Set link forward for given bitmap.
    153  * Parameters:
    154  *     unit - Device unit number
    155  *     pbmp - Link forward bitmap
    156  * Returns:
    157  *     SOC_E_XXX
    158  * Notes:
    159  *     Link bitmap should be manipulated only through this routine and
    160  *     soc_linkctrl_link_mask_set.
    161  */
    162 int
    163 soc_linkctrl_link_fwd_set(int unit, pbmp_t fwd)
    164 {
    165     UNIT_INIT_CHECK(unit);
    166 
    167     LINKCTRL(unit).link_fwd = fwd;
    168 
    169     return LINK_UPDATE(unit);
    170 }
    171 
    172 /*
    173  * Function:
    174  *     soc_linkctrl_link_fwd_get
    175  * Purpose:
    176  *     Get link forward bitmap.
    177  * Parameters:
    178  *     unit - Device unit number
    179  *     pbmp - (OUT) Link forward bitmap
    180  * Returns:
    181  *     SOC_E_XXX
    182  */
    183 int
    184 soc_linkctrl_link_fwd_get(int unit, pbmp_t *fwd)
    185 {
    186     UNIT_INIT_CHECK(unit);
    187     PARAM_NULL_CHECK(fwd);
    188 
    189     *fwd = LINKCTRL(unit).link_fwd;
    190 
    191     return SOC_E_NONE;
    192 }
    193 
    194 /*
    195  * Function:
    196  *     soc_linkctrl_link_mask_set
    197  * Purpose:
    198  *     Mask bits in link bitmap independent of link_fwd value.
    199  * Parameters:
    200  *     unit - Device unit number
    201  *     mask - Link mask bitmap
    202  * Returns:
    203  *     SOC_E_XXX
    204  * Notes:
    205  *     This routine is used to clear bits in the link bitmap to support
    206  *     the mac_fe/ge_enable_set() calls.
    207  */
    208 int
    209 soc_linkctrl_link_mask_set(int unit, pbmp_t mask)
    210 {
    211     UNIT_INIT_CHECK(unit);
    212 
    213     LINKCTRL(unit).link_mask = mask;
    214 
    215     return LINK_UPDATE(unit);
    216 }
    217 
    218 /*
    219  * Function:
    220  *     soc_linkctrl_link_mask_get
    221  * Purpose:
    222  *     Counterpart to soc_linkctrl_mask_set
    223  * Parameters:
    224  *     unit - Device unit number
    225  *     mask - (OUT) Link mask bitmap
    226  * Returns:
    227  *     SOC_E_XXX
    228  */
    229 int
    230 soc_linkctrl_link_mask_get(int unit, pbmp_t *mask)
    231 {
    232     UNIT_INIT_CHECK(unit);
    233     PARAM_NULL_CHECK(mask);
    234 
    235     *mask = LINKCTRL(unit).link_mask;
    236 
    237     return SOC_E_NONE;
    238 }
    239 
    240 /*
    241  * Function:
    242  *     soc_linkctrl_linkscan_register
    243  * Purpose:
    244  *     Provide a callout made when link scanning detects a link change.
    245  * Parameters:
    246  *     unit - Device unit number
    247  *     f    - Function called when link status change is detected
    248  * Returns:
    249  *     SOC_E_XXX
    250  * Notes:
    251  *     Handler called in interrupt context.
    252  */
    253 int
    254 soc_linkctrl_linkscan_register(int unit, void (*f)(int))
    255 {
    256     soc_control_t  *soc;
    257 
    258     UNIT_INIT_CHECK(unit);
    259 
    260     soc = SOC_CONTROL(unit);    
    261     if (f != NULL && soc->soc_link_callout != NULL) {
    262         return SOC_E_EXISTS;
    263     }
    264 
    265     soc->soc_link_callout = f;
    266 
    267     return SOC_E_NONE;
    268 }
    269 
    270 /*
    271  * Function:
    272  *     soc_linkctrl_linkscan_config
    273  * Purpose:
    274  *     Set ports to scan in CMIC.
    275  * Parameters:
    276  *     unit       - Device unit number
    277  *     mii_pbm    - Port bitmap of ports to scan with MIIM registers
    278  *     direct_pbm - Port bitmap of ports to scan using NON MII
    279  * Returns:
    280  *     SOC_E_XXX
    281  */
    282 int
    283 soc_linkctrl_linkscan_config(int unit, pbmp_t hw_mii_pbm, pbmp_t hw_direct_pbm)
    284 {
    285     int     rv;
    286     int     s, has_mge, has_dge;
    287     pbmp_t  pbm;
    288 
    289     UNIT_INIT_CHECK(unit);
    290 
    291     SOC_PBMP_ASSIGN(pbm, hw_mii_pbm);
    292     SOC_PBMP_AND(pbm, hw_direct_pbm);
    293     if (SOC_PBMP_NOT_NULL(pbm)) {    /* !(hw_mii_pbm & hw_direct_pbm) */
    294         return SOC_E_CONFIG;
    295     }
    296 
    297     /*
    298      * Hardware (direct) scanning is NOT supported on 10/100 ports.
    299      */
    300     SOC_PBMP_ASSIGN(pbm, hw_direct_pbm);
    301     SOC_PBMP_AND(pbm, PBMP_FE_ALL(unit));
    302     if (SOC_PBMP_NOT_NULL(pbm)) {
    303         return SOC_E_UNAVAIL;
    304     }
    305 
    306     /*
    307      * The LINK_SCAN_GIG control affects ALL ports. Thus, all ports
    308      * being scanned by H/W must be either MIIM scanned or scanned
    309      * using the direct connection.
    310      */
    311     SOC_PBMP_ASSIGN(pbm, PBMP_GE_ALL(unit));
    312     SOC_PBMP_AND(pbm, hw_mii_pbm);
    313     has_mge = SOC_PBMP_NOT_NULL(pbm);
    314     SOC_PBMP_ASSIGN(pbm, PBMP_GE_ALL(unit));
    315     SOC_PBMP_AND(pbm, hw_direct_pbm);
    316     has_dge = SOC_PBMP_NOT_NULL(pbm);
    317     if (has_mge && has_dge) {
    318         return SOC_E_UNAVAIL;
    319     }
    320 
    321     /*
    322      * soc_linkctrl_linkscan_pause/continue combination will result in the
    323      * registers being setup and started properly if we are enabling for
    324      * the first time.
    325      */
    326 
    327     SOC_LINKSCAN_LOCK(unit, s);
    328 
    329     soc_linkctrl_linkscan_pause(unit);
    330 
    331     rv = LINKSCAN_CONFIG(unit, hw_mii_pbm, hw_direct_pbm);
    332     
    333     soc_linkctrl_linkscan_continue(unit);
    334 
    335     SOC_LINKSCAN_UNLOCK(unit, s);
    336 
    337     return rv;
    338 }
    339 
    340 /*
    341  * Function:
    342  *     soc_linkctrl_linkscan_pause
    343  * Purpose:
    344  *     Pauses link scanning, without disabling it.
    345  *     This call is used to pause scanning temporarily.
    346  * Parameters:
    347  *     unit - Device unit number
    348  * Returns:
    349  *     SOC_E_NONE
    350  * Notes:
    351  *     Nesting pauses is provided for.
    352  *     Software must ensure every pause is accompanied by a continue
    353  *     or linkscan will never resume.
    354  */
    355 int
    356 soc_linkctrl_linkscan_pause(int unit)
    357 {
    358     int            rv = SOC_E_NONE;
    359     int            s;
    360     soc_control_t  *soc;
    361 
    362     UNIT_INIT_CHECK(unit);
    363 
    364     soc = SOC_CONTROL(unit);    
    365     SOC_LINKSCAN_LOCK(unit, s);    /* Manipulate flags & regs atomically */
    366 
    367     if ((LINKCTRL(unit).link_pause++ == 0) &&
    368         (soc->soc_flags & SOC_F_LSE)) {
    369         /* Stop link scan and wait for current pass to finish */
    370         rv = LINKSCAN_PAUSE(unit);
    371     }
    372 
    373     SOC_LINKSCAN_UNLOCK(unit, s);
    374 
    375     return rv;
    376 }
    377 
    378 /*
    379  * Function:
    380  *     soc_linkctrl_linkscan_continue
    381  * Purpose:
    382  *     Continue link scanning after it has been paused.
    383  * Parameters:
    384  *     unit - Device unit number
    385  * Returns:
    386  *     SOC_E_NONE
    387  * Notes:
    388  *     This routine is designed so if soc_linkctrl_linkscan_config is called,
    389  *     it won't be confused whether or not a pause is in effect.
    390  */
    391 int
    392 soc_linkctrl_linkscan_continue(int unit)
    393 {
    394     int            rv = SOC_E_NONE;
    395     int            s;
    396     soc_control_t  *soc;
    397 
    398     UNIT_INIT_CHECK(unit);
    399 
    400     soc = SOC_CONTROL(unit);    
    401     SOC_LINKSCAN_LOCK(unit, s);    /* Manipulate flags & regs atomically */
    402 
    403     if (LINKCTRL(unit).link_pause <= 0) {
    404         SOC_LINKSCAN_UNLOCK(unit, s);
    405         return SOC_E_INTERNAL;    /* Continue not preceded by a pause */
    406     }
    407 
    408     if ((--LINKCTRL(unit).link_pause == 0) &&
    409         (soc->soc_flags & SOC_F_LSE)) {
    410         rv = LINKSCAN_CONTINUE(unit);
    411     }
    412 
    413     SOC_LINKSCAN_UNLOCK(unit, s);
    414 
    415     return rv;
    416 }
    417 
    418 /*
    419  * Function:
    420  *     soc_linkctrl_miim_port_get
    421  * Purpose:
    422  *     Return the MIIM port number for a given port.
    423  * Parameters:
    424  *     unit      - Device unit number
    425  *     port      - Device port number
    426  *     miim_port - Returns port number in MIIM
    427  * Returns:
    428  *     SOC_E_XXX
    429  * Notes:
    430  *     When dealing with ports, the MIIM has a fixed port number
    431  *     assigned to a physical port.  Since the user can remap
    432  *     the port numbers in some devices,
    433  *     it is necessary to translate the 'user' port number to
    434  *     the actual device physical port.
    435  */
    436 int
    437 soc_linkctrl_miim_port_get(int unit, soc_port_t port, int *miim_port)
    438 {
    439     soc_linkctrl_port_info_t  *port_info;
    440     int                       block_type;
    441     int                       block_number;
    442     int                       block_index;
    443 
    444     UNIT_INIT_CHECK(unit);
    445 
    446     port_info = LINK_MIIM_PORT_INFO(unit);
    447     if (port_info == NULL) {
    448         *miim_port = port;
    449         return SOC_E_NONE;
    450     }
    451 
    452     /* Get physical block type, number and port offset */
    453     block_type   = SOC_PORT_BLOCK_TYPE(unit, port);
    454     block_number = SOC_PORT_BLOCK_NUMBER(unit, port);
    455     block_index  = SOC_PORT_BLOCK_INDEX(unit, port);
    456 
    457     while (port_info->block_type != -1) {
    458         if ((port_info->block_type   == block_type)   &&
    459             (port_info->block_number == block_number) &&
    460             (port_info->block_index  == block_index)) {
    461             /* Found */
    462             *miim_port = port_info->port;
    463             return SOC_E_NONE;
    464         }
    465 
    466         port_info++;
    467     }
    468 
    469     return SOC_E_NOT_FOUND;
    470 }
    471 
    472 int
    473 soc_linkctrl_hw_link_get(int unit, soc_pbmp_t *hw_link)
    474 {
    475 
    476     UNIT_INIT_CHECK(unit);
    477 
    478     return LINKSCAN_HW_LINK_GET(unit, hw_link);
    479 
    480 }