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

link.c (67767B)


      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  * BCM Link Scan Module
      8  *
      9  * Linkscan should always run for all chips in a system. It manages
     10  * the current chip LINK state (EPC_LINK or equivalent), and programs
     11  * MACs to match auto-negotiated links. 
     12  *
     13  * Linkscan is also responsible for determining the link up/down
     14  * status for a port. Since link down events signaled by most PHYs are
     15  * latched low and cleared on read, it is important that accesses to
     16  * to the PHY itself be managed carefully. Linkscan is intended to be
     17  * the only process that reads the actual PHY (using
     18  * _bcm_port_link_get). All other calls to retrieve link status
     19  * results in calls to _bcm_link_get which returns the linkscan view
     20  * of the link status. This ensures linkscan is the only process that
     21  * reads the actual PHYs.
     22  *
     23  * All modifications to the linkscan state are protected by LS_LOCK.
     24  *
     25  * Linkscan maintains the following port bitmaps
     26  *
     27  *     pbm_link:
     28  *                 Current physical link up/down status. When a bit
     29  *                 in this mask is set, a link up or link down event
     30  *                 is recognized and signaled to any registered
     31  *                 handlers.
     32  *
     33  *     pbm_link_change:
     34  *                 Mask of ports that need to recognize a link
     35  *                 down event. Ports are added to this mask by the
     36  *                 function bcm_link_change. 
     37  *
     38  *     pbm_override_ports:
     39  *                 Bitmap of ports that are currently
     40  *                 being explicitly set to a value. These actual value
     41  *                 is determined by pbm_override_link. Ports are
     42  *                 added and removed from this mode by the routine
     43  *                 _bcm_link_force. 
     44  *
     45  *                 Ports that are forced to an UP state do NOT result
     46  *                 in a call to bcm_port_update. It is the
     47  *                 responsibility of the caller to configure the
     48  *                 correct MAC and PHY state.
     49  *
     50  *     pbm_override_link:
     51  *                 Bitmap indicating the link-up/link-down
     52  *                 status for those ports with override set.
     53  *
     54  *     pbm_sgmii_autoneg:
     55  *                 Bitmap of the port that is configured in SGMII
     56  *                 autoneg mode based on spn_PHY_SGMII_AUTONEG.
     57  *                 Maintaining this bitmap avoids the overhead
     58  *                 of the soc_property_port_get call. 
     59  *
     60  *     pbm_remote_fault:
     61  *                 Bit map indicating the remote fault status of
     62  *                 a link-up port.
     63  *     pbm_local_fault:
     64  *                 Bit map indicating the local fault status of
     65  *                 a link-up port.
     66  *
     67  * Calls to _bcm_link_get always returns the current status as
     68  * indicated by pbm_link. 
     69  *
     70  * NOTE:
     71  * Original file is src/bcm/esw/link.c, version 1.49.
     72  * That file should eventually be removed, and XGS specific code
     73  * should be placed in corresponding routines to be attached to the
     74  * 'driver' in 'link_control' during module initialization
     75  * 'soc_linkctrl_init()'.
     76  */
     77 
     78 
     79 #include <shared/bsl.h>
     80 
     81 #include <sal/types.h>
     82 #include <shared/alloc.h>
     83 #include <sal/core/spl.h>
     84 #include <soc/drv.h>
     85 #include <soc/scache.h>
     86 #include <soc/phyctrl.h>
     87 #include <soc/linkctrl.h>
     88 
     89 #include <bcm/error.h>
     90 #include <bcm/port.h>
     91 
     92 #include <bcm/link.h>
     93 
     94 #include <bcm_int/common/lock.h>
     95 #include <bcm_int/common/link.h>
     96 
     97 #ifdef BCM_WARM_BOOT_SUPPORT
     98 #include <bcm/module.h>
     99 #endif
    100 
    101 #ifdef BCM_CMICX_SUPPORT
    102 #include <shared/cmicfw/iproc_m0ssq.h>
    103 #endif
    104 
    105 #include <soc/dnxc/multithread_analyzer.h>
    106 
    107 #if defined (BCM_DNX_SUPPORT) || defined (BCM_DNXF_SUPPORT)
    108 #include <soc/dnxc/dnxc_ha.h>
    109 #ifdef BCM_DNX_SUPPORT
    110 #include <soc/dnx/dnx_err_recovery_manager_utils.h>
    111 #endif
    112 #endif
    113 
    114 /*
    115  * Handler to registered callbacks for link changes
    116  */
    117 typedef struct _ls_handler_cb_s {
    118     struct _ls_handler_cb_s  *next;
    119     bcm_linkscan_handler_t   cb_f;
    120 } _ls_handler_cb_t;
    121 
    122 /*
    123  * Linkscan error state
    124  */
    125 typedef struct _ls_errstate_s {
    126     int          limit;    /* # errors to enter error state */
    127     int          delay;    /* Length of error state in seconds */
    128     int          count;    /* # of errors so far */
    129     int          wait;     /* Boolean, TRUE when in error state */
    130     sal_usecs_t  time;     /* Time error state was entered */
    131 } _ls_errstate_t;
    132 
    133 /*
    134  * Linkscan Module control structure
    135  */
    136 typedef struct _ls_control_s {
    137     const _bcm_ls_driver_t  *driver;            /* Device specific routines */
    138     sal_mutex_t       lock;               /* Synchronization */
    139     char              taskname[16];       /* Linkscan thread name */
    140     VOL sal_thread_t  thread_id;          /* Linkscan thread id */
    141     VOL int           interval_us;        /* Time between scans (us) */
    142     sal_sem_t         sema;               /* Linkscan semaphore */
    143     pbmp_t            pbm_hw;             /* Hardware link scan ports */
    144     pbmp_t            pbm_sw;             /* Software link scan ports */
    145     pbmp_t            pbm_hw_upd;         /* Ports requiring HW link re-scan */
    146     pbmp_t            pbm_sgmii_autoneg;  /* Ports with SGMII autoneg */
    147     pbmp_t            pbm_link;           /* Ports currently up */
    148     pbmp_t            pbm_link_change;    /* Ports needed to recognize down */
    149     pbmp_t            pbm_override_ports; /* Force up/Down ports */
    150     pbmp_t            pbm_override_link;  /* Force up/Down status */
    151     pbmp_t            pbm_newly_enabled;  /* indicate linkscan is newly enabled for ports*/
    152     pbmp_t            pbm_remote_fault;   /* Ports receiving remote fault */
    153     int               link_fault_chk_en;  /* Enable Link remote fault checking in Linkscan thread */
    154     pbmp_t            pbm_local_fault;    /* Ports receiving local fault */
    155     int               link_local_fault_chk_en;  /* Enable Link local fault checking in Linkscan thread */
    156     int               hw_change;          /* HW Link state has changed */
    157     _ls_errstate_t    error[NUM_PORTS];   /* Link error state */
    158     _ls_handler_cb_t  *handler_cb;        /* Handler to list of callbacks */
    159     bcm_linkscan_port_handler_t
    160                       port_link_f[NUM_PORTS]; /* Port link fn */
    161 } _ls_control_t;
    162 
    163 _ls_control_t           *_linkscan_control[BCM_LOCAL_UNITS_MAX];
    164 
    165 #define LS_CONTROL(unit)        (_linkscan_control[unit])
    166 
    167 #define LS_CONTROL_DRV(unit)   (_linkscan_control[unit]->driver)
    168 
    169 #define _LS_CALL(_ld, _lf, _la) \
    170     ((_ld) == 0 ? BCM_E_PARAM : \
    171      ((_ld)->_lf == 0 ? BCM_E_UNAVAIL : (_ld)->_lf _la))
    172 
    173 #define LS_HW_INTERRUPT(_u, _b) \
    174     do { \
    175         if (LS_CONTROL_DRV(_u) && (LS_CONTROL_DRV(_u)->ld_hw_interrupt)) { \
    176             LS_CONTROL_DRV(_u)->ld_hw_interrupt((_u), (_b)); \
    177         } \
    178     } while (0)
    179 
    180 #define LS_PORT_LINK_GET(_u, _p, _h, _l) \
    181     _LS_CALL(LS_CONTROL_DRV(_u), ld_port_link_get, ((_u), (_p), (_h), (_l)))
    182 
    183 #define LS_INTERNAL_SELECT(_u, _p) \
    184     _LS_CALL(LS_CONTROL_DRV(_u), ld_internal_select, ((_u), (_p)))
    185 
    186 #define LS_UPDATE_ASF(_u, _p, _l, _s, _d) \
    187     _LS_CALL(LS_CONTROL_DRV(_u), ld_update_asf, ((_u), (_p), (_l), (_s), (_d)))
    188 
    189 #define LS_TRUNK_SW_FAILOVER_TRIGGER(_u, _a, _s) \
    190     _LS_CALL(LS_CONTROL_DRV(_u), ld_trunk_sw_failover_trigger, \
    191              ((_u), (_a), (_s)))
    192 
    193 
    194 /*
    195  * Define:
    196  *     LS_LOCK/LS_UNLOCK
    197  * Purpose:
    198  *     Serialization Macros for access to _linkscan_control structure.
    199  */
    200 
    201 #define LS_LOCK(unit) \
    202         sal_mutex_take(_linkscan_control[unit]->lock, sal_mutex_FOREVER)
    203 
    204 #define LS_UNLOCK(unit) \
    205         sal_mutex_give(_linkscan_control[unit]->lock)
    206 
    207 /*
    208  * General util macros
    209  */
    210 #define UNIT_VALID_CHECK(unit) \
    211     if (((unit) < 0) || ((unit) >= BCM_LOCAL_UNITS_MAX)) { return BCM_E_UNIT; }
    212 
    213 #define UNIT_INIT_CHECK(unit) \
    214     do { \
    215         UNIT_VALID_CHECK(unit); \
    216         if (_linkscan_control[unit] == NULL) { return BCM_E_INIT; } \
    217     } while (0)
    218 
    219 #ifdef BCM_WARM_BOOT_SUPPORT
    220 #define LINK_WB_VERSION_1_0                SOC_SCACHE_VERSION(1,0)
    221 #define LINK_WB_CURRENT_VERSION            LINK_WB_VERSION_1_0
    222 #endif /* BCM_WARM_BOOT_SUPPORT */
    223 
    224 /* Local Function Declaration */
    225 int bcm_common_linkscan_enable_set(int unit, int us);
    226 
    227 /*
    228  * Function:
    229  *     _bcm_linkscan_pbm_init
    230  * Purpose:
    231  *     Initialize the port bitmaps in the link_control structure.
    232  * Parameters:
    233  *     unit - Device unit number
    234  * Returns:
    235  *     None
    236  * Notes:
    237  *     Assumes: valid unit number, lock is held.
    238  */
    239 STATIC void
    240 _bcm_linkscan_pbm_init(int unit)
    241 {
    242     _ls_control_t  *lc = LS_CONTROL(unit);
    243     /*
    244      * Force initial scan by setting link change while pretending link
    245      * was initially up.
    246      */
    247     BCM_PBMP_ASSIGN(lc->pbm_link, PBMP_ALL(unit));
    248     BCM_PBMP_ASSIGN(lc->pbm_link_change, PBMP_PORT_ALL(unit));
    249 
    250     BCM_PBMP_CLEAR(lc->pbm_override_ports);
    251     BCM_PBMP_CLEAR(lc->pbm_override_link);
    252     BCM_PBMP_CLEAR(lc->pbm_newly_enabled);
    253     BCM_PBMP_CLEAR(lc->pbm_remote_fault);
    254     BCM_PBMP_CLEAR(lc->pbm_local_fault);
    255 
    256     BCM_PBMP_ASSIGN(lc->pbm_hw_upd, PBMP_ALL(unit));
    257 }
    258 
    259 /*
    260  * Function:
    261  *      _bcm_link_fault_get
    262  * Purpose:
    263  *      Check for remote link which is already up.
    264  * Parameters:
    265  *      unit - unit.
    266  *      port - Port to process.
    267  *      remote_fault - (IN/OUT) Remote fault status.
    268  *      local_fault - (IN/OUT) Local fault status.
    269  * Returns:
    270  *      BCM_E_XXX
    271  */
    272 STATIC int
    273 _bcm_link_fault_get(int unit, int port, int *remote_fault, int *local_fault)
    274 {
    275     int rv, speed;
    276     uint32 flags;
    277 #ifdef BCM_DNX_SUPPORT
    278     if (SOC_IS_DNX(unit)) {
    279         if (IS_IL_PORT(unit, port) || IS_SFI_PORT(unit, port)) {
    280             return BCM_E_UNAVAIL;
    281         }
    282     } else
    283 #endif
    284 #ifdef BCM_DNXF_SUPPORT
    285     if (SOC_IS_DNXF(unit)) {
    286         return BCM_E_UNAVAIL;
    287     } else
    288 #endif
    289     if (IS_HG_PORT(unit, port) || IS_XE_PORT(unit, port) ||
    290         IS_CE_PORT(unit, port) || IS_XL_PORT(unit, port) ||
    291         IS_CL_PORT(unit, port)) {
    292         rv = bcm_port_speed_get(unit, port, &speed);
    293         if (BCM_FAILURE(rv)) {
    294             return rv;
    295         }
    296         /* check if the port has no fault status to retrieve */
    297         if (speed < 5000) {
    298             return BCM_E_UNAVAIL;
    299         }
    300     } else {
    301         return BCM_E_UNAVAIL;
    302     }
    303     /* get the indications */
    304     rv = bcm_port_fault_get(unit, port, &flags);
    305     if (BCM_FAILURE(rv)) {
    306         return rv;
    307     }
    308     if (flags & BCM_PORT_FAULT_REMOTE) {
    309         *remote_fault = TRUE;
    310     }
    311     if (flags & BCM_PORT_FAULT_LOCAL) {
    312         *local_fault = TRUE;
    313     }
    314     /* clear the indications */
    315     rv = bcm_port_control_set(unit,
    316                               port,
    317                               bcmPortControlLinkFaultLocal, 0);
    318     if (BCM_FAILURE(rv)) {
    319         return rv;
    320     }
    321     rv = bcm_port_control_set(unit,
    322                               port,
    323                               bcmPortControlLinkFaultRemote, 0);
    324     if (BCM_FAILURE(rv)) {
    325         return rv;
    326     }
    327     return rv;
    328 }
    329 
    330 /*
    331  * Function:    
    332  *     _bcm_linkscan_update_port
    333  * Purpose:     
    334  *     Check for and process a link event on one port.
    335  * Parameters:  
    336  *     unit - Device unit number
    337  *     port - Device port to process
    338  * Returns:
    339  *     BCM_E_XXX
    340  * Notes:
    341  *     Assumes: valid unit number, lock is held.
    342  */
    343 STATIC int
    344 _bcm_linkscan_update_port(int unit, int port)
    345 {
    346     _ls_control_t       *lc = LS_CONTROL(unit);
    347     int                 cur_link, change, new_link = FALSE;
    348     bcm_port_info_t     info;
    349     _ls_handler_cb_t    *lh, *lh_next = NULL;
    350     int                 rv;
    351     int                 cur_remote_fault = FALSE, new_remote_fault = FALSE;
    352     int                 cur_local_fault = FALSE, new_local_fault = FALSE;
    353     soc_pbmp_t          pbm_link_fwd;
    354 
    355     assert(SOC_PORT_VALID(unit, port)); 
    356     /*
    357      * Current link status is calculated in the following order:
    358      *   1) If link status is in an override state, the override
    359      *      state is used.
    360      *   2) If link is required to recognize a link down event, the
    361      *      current scan will recognize the link down (if the link
    362      *      was previously down, this will result in no action
    363      *      being taken)
    364      *   3) Use real detected link status.
    365      *        a) If using Hardware link scanning, use captured H/W
    366      *           value since the H/W scan will clear the latched
    367      *           link down event.
    368      *        b) If using S/W link scanning, retrieve the current
    369      *           link status from either:
    370      *           - registered port link routine (registered
    371      *             with 'bcm_linkscan_port_register'),
    372      *           - or the PHY.
    373      */
    374 
    375     cur_link = BCM_PBMP_MEMBER(lc->pbm_link, port);
    376     cur_remote_fault = SOC_PBMP_MEMBER(lc->pbm_remote_fault, port);
    377     cur_local_fault = SOC_PBMP_MEMBER(lc->pbm_local_fault, port);
    378     change = BCM_PBMP_MEMBER(lc->pbm_link_change, port);
    379     BCM_PBMP_PORT_REMOVE(lc->pbm_link_change, port);
    380 
    381     if (change) {                                                  /* 2) */
    382         new_link = FALSE;
    383         rv = BCM_E_NONE;
    384 
    385     } else if (BCM_PBMP_MEMBER(lc->pbm_override_ports, port)) {    /* 1) */
    386         new_link = BCM_PBMP_MEMBER(lc->pbm_override_link, port);
    387         rv = BCM_E_NONE;
    388 
    389     } else if (BCM_PBMP_MEMBER(lc->pbm_hw, port)) {                /* 3a) */
    390         new_link = BCM_PBMP_MEMBER(lc->pbm_hw_upd, port);
    391         /*
    392          * If link up, we should read the link status from the PHY because
    393          * the PHY may link up but autneg may not have completed.
    394          */
    395         rv = BCM_E_NONE;
    396 #ifdef BCM_CMICX_SUPPORT
    397         if (!soc_feature(unit, soc_feature_cmicx))
    398 #endif /* BCM_CMICX_SUPPORT */
    399         {
    400             if (new_link ||
    401                 BCM_PBMP_MEMBER(lc->pbm_sgmii_autoneg, port)) {
    402                 /* Fall back on SW linkscan to detect possible medium change */
    403                 rv = LS_PORT_LINK_GET(unit, port, 1, &new_link);
    404             }
    405         }
    406     } else if (BCM_PBMP_MEMBER(lc->pbm_sw, port)) {                /* 3b) */
    407         if (lc->port_link_f[port]) {    /* Use registered port link function */
    408             int state;
    409 
    410             rv = lc->port_link_f[port](unit, port, &state);
    411             if (rv == BCM_E_NONE) {
    412                 new_link = state ? TRUE : FALSE;
    413             } else if (rv == BCM_E_UNAVAIL) {  /* Retrieve PHY link */
    414                 rv = LS_PORT_LINK_GET(unit, port, 0, &new_link);
    415             }
    416         } else {
    417             rv = LS_PORT_LINK_GET(unit, port, 0, &new_link);
    418         }
    419         LOG_VERBOSE(BSL_LS_BCM_LINK,
    420                     (BSL_META_U(unit,
    421                                 "SW link p=%d %s\n"),
    422                      port, new_link ? "up" : "down"));
    423     } else {
    424         return BCM_E_NONE;    /* Port not being scanned */
    425     }
    426 
    427     if (BCM_FAILURE(rv)) {
    428         LOG_ERROR(BSL_LS_BCM_LINK,
    429                   (BSL_META_U(unit,
    430                               "Port %s: Failed to recover link status: %s\n"), 
    431                    SOC_PORT_NAME(unit, port), bcm_errmsg(rv)));
    432         return rv;
    433     }
    434 
    435     if (new_link && (lc->link_fault_chk_en == 1 || lc->link_local_fault_chk_en == 1)
    436         && BCM_PBMP_MEMBER(lc->pbm_sw, port)) {
    437         rv = _bcm_link_fault_get(unit, port, &new_remote_fault, &new_local_fault);
    438         if (rv == BCM_E_UNAVAIL) {
    439             rv = BCM_E_NONE;
    440         } else if (BCM_FAILURE(rv)) {
    441             LOG_ERROR(BSL_LS_BCM_LINK,
    442                       (BSL_META_U(unit,
    443                        "Unit %d, Port %s: Failed to read fault status: %s\n"),
    444                        unit, SOC_PORT_NAME(unit, port), bcm_errmsg(rv)));
    445             return rv;
    446         }
    447         /* Set remote fault to be false if not enabling remote fault checking */
    448         if (!lc->link_fault_chk_en) {
    449             new_remote_fault = FALSE;
    450         }
    451         /* Set local fault to be false if not enabling local fault checking */
    452         if (!lc->link_local_fault_chk_en) {
    453             new_local_fault = FALSE;
    454         }
    455     }
    456 
    457     if ((((cur_link) && (new_link) && (cur_remote_fault == new_remote_fault) &&
    458         (cur_local_fault == new_local_fault)) ||
    459         ((!cur_link) && (!new_link))) && !(BCM_PBMP_MEMBER(lc->pbm_newly_enabled, port))) {
    460         /* No change and is not newly enabled*/
    461         return BCM_E_NONE;
    462     }
    463 
    464     /*
    465      * If disabling, stop ingresses from sending any more traffic to
    466      * this port.
    467      */
    468 
    469     if (!new_link) {
    470         BCM_PBMP_PORT_REMOVE(lc->pbm_link, port);
    471         SOC_PBMP_ASSIGN(pbm_link_fwd, lc->pbm_link);
    472         SOC_PBMP_PORT_REMOVE(lc->pbm_remote_fault, port);
    473         SOC_PBMP_REMOVE(pbm_link_fwd, lc->pbm_remote_fault);
    474         SOC_PBMP_PORT_REMOVE(lc->pbm_local_fault, port);
    475         SOC_PBMP_REMOVE(pbm_link_fwd, lc->pbm_local_fault);
    476         rv = soc_linkctrl_link_fwd_set(unit, pbm_link_fwd);
    477 
    478         if (BCM_FAILURE(rv)) {
    479             LOG_ERROR(BSL_LS_BCM_LINK,
    480                       (BSL_META_U(unit,
    481                                   "Port %s: soc_linkctrl_link_fwd_set failed: %s\n"),
    482                        SOC_PORT_NAME(unit, port), bcm_errmsg(rv)));
    483             return rv;
    484         }
    485     }
    486 
    487     /* Program MACs (only if port is not forced) */
    488 
    489     /*
    490      * For AradPlus devices, as per SDK-51076 when link is up,
    491      * USE_EXTERNAL_FAULT_FOR_TX should be cleared, otherwise
    492      * remote fault will be sent from AradPlus. When link is
    493      * down, that field should be set.
    494      */
    495     if ((!BCM_PBMP_MEMBER(lc->pbm_override_ports, port) ||
    496         SOC_IS_ARADPLUS_A0(unit)) &&
    497         (cur_link != new_link)) {
    498         rv = bcm_port_update(unit, port, new_link);
    499 
    500         if (BCM_FAILURE(rv)) {
    501             LOG_ERROR(BSL_LS_BCM_LINK,
    502                       (BSL_META_U(unit,
    503                                   "Port %s: bcm_port_update failed: %s\n"),
    504                        SOC_PORT_NAME(unit, port), bcm_errmsg(rv)));
    505             return rv;
    506         }
    507     }
    508 
    509     /*
    510      * If enabling, allow traffic to go to this port.
    511      */
    512 
    513     if (new_link) {
    514         BCM_PBMP_PORT_ADD(lc->pbm_link, port);
    515         SOC_PBMP_ASSIGN(pbm_link_fwd, lc->pbm_link);
    516 
    517         if (!new_remote_fault) {
    518             SOC_PBMP_PORT_REMOVE(lc->pbm_remote_fault, port);
    519         } else {
    520             SOC_PBMP_PORT_ADD(lc->pbm_remote_fault, port);
    521         }
    522         if (!new_local_fault) {
    523             SOC_PBMP_PORT_REMOVE(lc->pbm_local_fault, port);
    524         } else {
    525             SOC_PBMP_PORT_ADD(lc->pbm_local_fault, port);
    526         }
    527         SOC_PBMP_REMOVE(pbm_link_fwd, lc->pbm_remote_fault);
    528         SOC_PBMP_REMOVE(pbm_link_fwd, lc->pbm_local_fault);
    529 
    530         rv = soc_linkctrl_link_fwd_set(unit, pbm_link_fwd);
    531 
    532         if (BCM_FAILURE(rv)) {
    533             LOG_ERROR(BSL_LS_BCM_LINK,
    534                       (BSL_META_U(unit,
    535                                   "Port %s: soc_linkctrl_link_fwd_set failed: %s\n"),
    536                        SOC_PORT_NAME(unit, port), bcm_errmsg(rv)));
    537             return rv;
    538         }
    539     }
    540 
    541     /*
    542      * Call registered handlers with complete link info.
    543      * Display link status message, if requested.
    544      *
    545      * In case link status changed again for bcm_port_info_get,
    546      * overwrite the linkstatus field with new_link.  This ensures
    547      * the handler is presented with a consistent alternating
    548      * sequence of link up/down.
    549      */
    550 
    551     if(soc_feature(unit, soc_feature_linkscan_remove_port_info)){
    552         bcm_port_info_t_init(&info);
    553         info.action_mask = BCM_PORT_ATTR_LINKSTAT_MASK;
    554     } else {
    555         rv = bcm_port_info_get(unit, port, &info);
    556         if (BCM_FAILURE(rv)) {
    557             LOG_ERROR(BSL_LS_BCM_LINK,
    558                       (BSL_META_U(unit,
    559                                   "Port %s: bcm_port_info_get failed: %s\n"),
    560                        SOC_PORT_NAME(unit, port), bcm_errmsg(rv)));
    561             return rv;
    562         }
    563     }
    564 
    565     if (soc_feature(unit, soc_feature_asf)) {
    566         rv = LS_UPDATE_ASF(unit, port, new_link, info.speed, info.duplex);
    567         if (BCM_FAILURE(rv)) {
    568             LOG_ERROR(BSL_LS_BCM_LINK,
    569                       (BSL_META_U(unit,
    570                                   "Port %s: linkscan ASF update failed: %s\n"),
    571                        SOC_PORT_NAME(unit, port), bcm_errmsg(rv)));
    572             return rv;
    573         }
    574     }
    575 
    576     if (new_link) {
    577         if(soc_feature(unit, soc_feature_linkscan_remove_port_info)){
    578             LOG_VERBOSE(BSL_LS_BCM_LINK,
    579                         (BSL_META_U(unit,
    580                                     "Port %s: link up\n"),
    581                          SOC_PORT_NAME(unit, port)));
    582         } else {
    583             LOG_VERBOSE(BSL_LS_BCM_LINK,
    584                     (BSL_META_U(unit,
    585                                 "Port %s: link up (%dMb %s %s)\n"),
    586                      SOC_PORT_NAME(unit, port),
    587                      info.speed,
    588                      info.duplex ? "Full Duplex" : "Half Duplex",
    589                      PHY_FIBER_MODE(unit, port) ?
    590                      "Fiber" : "Copper"));
    591         }
    592     } else {
    593         LOG_VERBOSE(BSL_LS_BCM_LINK,
    594                     (BSL_META_U(unit,
    595                                 "Port %s: link down\n"),
    596                      SOC_PORT_NAME(unit, port)));
    597     }
    598 
    599     /* coverity[dead_error_line] */
    600     info.linkstatus = new_remote_fault ? (new_local_fault ? BCM_PORT_LINK_STATUS_LOCAL_AND_REMOTE_FAULT :
    601                       BCM_PORT_LINK_STATUS_REMOTE_FAULT ) : (new_local_fault ?
    602                       BCM_PORT_LINK_STATUS_LOCAL_FAULT : new_link);
    603 
    604     for (lh = lc->handler_cb; lh; lh = lh_next) {
    605         /*
    606          * save the next linkscan handler first, in case current handler
    607          * unregister itself inside the handler function
    608          */
    609         lh_next = lh->next;        
    610         lh->cb_f(unit, port, &info);
    611     }
    612     if (BCM_PBMP_MEMBER(lc->pbm_newly_enabled, port)) {
    613         BCM_PBMP_PORT_REMOVE(lc->pbm_newly_enabled, port);
    614     }
    615 
    616     return BCM_E_NONE;
    617 }
    618 
    619 /*
    620  * Function:
    621  *     _bcm_linkscan_update
    622  * Purpose:     
    623  *     Check for a change in link status on each link.  If a change
    624  *     is detected, call bcm_port_update to program the MACs for that
    625  *     link, and call the list of registered handlers.
    626  * Parameters:  
    627  *     unit - Device unit number
    628  *     pbm  - Bitmap of ports to scan
    629  * Returns:
    630  *     Nothing.
    631  * Notes:
    632  *     Assumes: valid unit number.
    633  */
    634 STATIC void
    635 _bcm_linkscan_update(int unit, pbmp_t pbm)
    636 {
    637     _ls_control_t  *lc = LS_CONTROL(unit);
    638     pbmp_t         save_link_change;
    639     int            rv;
    640     bcm_port_t     port;
    641 
    642     LS_LOCK(unit);
    643 
    644     /*
    645      * Suspend hardware link scan here to avoid overhead of pause/resume
    646      * on MDIO accesses. Ideally this would not be required if the source
    647      * of the interrupt is the signal from the internal Serdes but we still
    648      * need to do this due to work-arounds in phy drivers where we need to
    649      * rely on SW linkscan to detect link up.
    650      */
    651     soc_linkctrl_linkscan_pause(unit);           /* Suspend linkscan */
    652 
    653     PBMP_ITER(pbm, port) {
    654         
    655         _ls_errstate_t *err = &lc->error[port];
    656 
    657         /* This check is done in case of a scenario of removing a port(dynamic port provisioning)
    658            right after the initiliaztion of the valid ports bitmap, causing for _bcm_linkscan_update
    659            call with an invalid port */
    660         if (!SOC_PORT_VALID(unit, port)) {
    661             continue;
    662         }
    663 
    664         if (err->wait) {    /* Port waiting in error state */
    665             if (SAL_USECS_SUB(sal_time_usecs(), err->time) >= err->delay) {
    666                 err->wait = 0;    /* Exit error state */
    667                 err->count = 0;
    668 
    669                 LOG_WARN(BSL_LS_BCM_LINK,
    670                          (BSL_META_U(unit,
    671                                      "Port %s: restored\n"),
    672                           SOC_PORT_NAME(unit, port)));
    673             } else {
    674                 continue;
    675             }
    676         }
    677 
    678         save_link_change = lc->pbm_link_change;
    679 
    680         rv = _bcm_linkscan_update_port(unit, port);
    681 
    682         if (BCM_FAILURE(rv)) {
    683 
    684             lc->pbm_link_change = save_link_change;
    685 
    686             if (++err->count >= err->limit && err->limit > 0) {
    687                 /* Enter error state */
    688                 LOG_ERROR(BSL_LS_BCM_LINK,
    689                           (BSL_META_U(unit,
    690                                       "Port %s: temporarily removed from linkscan\n"),
    691                            SOC_PORT_NAME(unit, port)));
    692 
    693                 err->time = sal_time_usecs();
    694                 err->wait = 1;
    695             }
    696         } else if (err->count > 0) {
    697             err->count--;    /* Reprieve */
    698         }
    699 
    700     }
    701 
    702     soc_linkctrl_linkscan_continue(unit);        /* Restart H/W link scan */
    703 
    704     LS_UNLOCK(unit);
    705 }
    706 
    707 /*
    708  * Function:    
    709  *     _bcm_linkscan_hw_interrupt
    710  * Purpose:     
    711  *     Link scan interrupt handler.
    712  * Parameters:  
    713  *     unit - Device unit number
    714  * Returns:     
    715  *     Nothing
    716  * Notes:
    717  *     Assumes: valid unit number.
    718  */
    719 STATIC void
    720 _bcm_linkscan_hw_interrupt(int unit)
    721 {
    722     _ls_control_t  *lc = LS_CONTROL(unit);
    723 
    724     if ((NULL != lc) && (NULL != lc->sema)) {
    725         lc->hw_change = 1;
    726         sal_sem_give(lc->sema);
    727     }
    728     LOG_VERBOSE(BSL_LS_BCM_LINK,
    729                 (BSL_META_U(unit,
    730                             "Linkscan interrupt unit %d\n"), unit));
    731 }
    732 
    733 /*
    734  * Function:    
    735  *     _bcm_linkscan_thread
    736  * Purpose:     
    737  *     Scan the ports on the specified unit for link status
    738  *     changes and process them as they occur.
    739  * Parameters:  
    740  *     unit - Device unit number
    741  * Returns:     
    742  *     Nothing
    743  * Notes:
    744  *     Assumes: valid unit number.
    745  */
    746 STATIC void
    747 _bcm_linkscan_thread(int unit)
    748 {
    749     _ls_control_t  *lc = LS_CONTROL(unit);
    750     sal_usecs_t    interval;
    751     int            rv;
    752     soc_port_t     port;
    753     char           thread_name[SAL_THREAD_NAME_MAX_LEN];
    754     sal_thread_t   thread;
    755 
    756     DNXC_MTA(dnxc_multithread_analyzer_declare_api_in_play(unit, __FUNCTION__, MTA_FLAG_THREAD_MAIN_FUNC, TRUE));
    757 
    758 #ifdef BCM_DNX_SUPPORT
    759     if(SOC_IS_DNX(unit))
    760     {
    761         DNX_ERR_RECOVERY_UTILS_EXCLUDED_THREAD_ADD(unit);
    762     }
    763 #endif
    764 
    765     LOG_INFO(BSL_LS_BCM_LINK,
    766              (BSL_META_U(unit,
    767                          "Linkscan starting on unit %d\n"),
    768               unit));
    769     thread = sal_thread_self();
    770     thread_name[0] = 0;
    771     sal_thread_name(thread, thread_name, sizeof (thread_name));
    772     /* Do not clear the pbm_override_ports and pbm_override_link.
    773      * If a port in Loopback mode forces link up before enabling linkscan,
    774      * the port status should be still forced to up after enabling linkscan.
    775      */
    776 
    777     BCM_PBMP_ASSIGN(lc->pbm_link, PBMP_ALL(unit));
    778 
    779     sal_memset(lc->error, 0, sizeof (lc->error));
    780 
    781     PBMP_ITER(PBMP_PORT_ALL(unit), port) {
    782         lc->error[port].limit = soc_property_port_get(unit, port,
    783                                          spn_BCM_LINKSCAN_MAXERR, 5);
    784         lc->error[port].delay = soc_property_port_get(unit, port,
    785                                          spn_BCM_LINKSCAN_ERRDELAY, 10000000);
    786     }
    787 
    788     /* Clear initial value of forwarding ports. */
    789     rv = soc_linkctrl_link_fwd_set(unit, lc->pbm_link);
    790 
    791     if (BCM_FAILURE(rv)) {
    792         LOG_ERROR(BSL_LS_BCM_LINK,
    793                   (BSL_META_U(unit,
    794                               "AbnormalThreadExit:%s, Failed to clear forwarding ports: %s\n"), 
    795                    thread_name, bcm_errmsg(rv)));
    796         DNXC_MTA(dnxc_multithread_analyzer_declare_api_in_play(unit, __FUNCTION__, MTA_FLAG_THREAD_MAIN_FUNC, FALSE));
    797         sal_thread_exit(0);
    798     }
    799 
    800     /* Register for hardware linkscan interrupt. */
    801     rv = soc_linkctrl_linkscan_register(unit, _bcm_linkscan_hw_interrupt);
    802 
    803     if (BCM_FAILURE(rv)) {
    804         LOG_ERROR(BSL_LS_BCM_LINK,
    805                   (BSL_META_U(unit,
    806                               "AbnormalThreadExit:%s, Failed to register handler: %s\n"),
    807                    thread_name, bcm_errmsg(rv)));
    808         DNXC_MTA(dnxc_multithread_analyzer_declare_api_in_play(unit, __FUNCTION__, MTA_FLAG_THREAD_MAIN_FUNC, FALSE));
    809         sal_thread_exit(0);
    810     }
    811 
    812     lc->thread_id = sal_thread_self();
    813 
    814     while ((interval = lc->interval_us) != 0) {
    815         pbmp_t  change;
    816         pbmp_t  hw_link, hw_update;
    817         pbmp_t  all_update;
    818 
    819         if (BCM_PBMP_IS_NULL(lc->pbm_sw) &&
    820             BCM_PBMP_IS_NULL(lc->pbm_remote_fault) &&
    821             BCM_PBMP_IS_NULL(lc->pbm_local_fault)) {
    822             interval = sal_sem_FOREVER;
    823         }
    824 
    825         /* all port changed */
    826         BCM_PBMP_ASSIGN(all_update, PBMP_PORT_ALL(unit));
    827         /* sample changed */
    828         BCM_PBMP_ASSIGN(change, lc->pbm_link_change);
    829 
    830         if (lc->hw_change) {
    831             soc_linkctrl_linkscan_pause(unit);
    832             lc->hw_change = 0;
    833 
    834             (void)soc_linkctrl_hw_link_get(unit, &hw_link);
    835             BCM_PBMP_AND(hw_link, lc->pbm_hw);
    836 #ifdef BCM_TRUNK_FAILOVER_SUPPORT
    837             LS_TRUNK_SW_FAILOVER_TRIGGER(unit, lc->pbm_hw, lc->pbm_hw_upd);
    838 #endif /* BCM_TRUNK_FAILOVER_SUPPORT */
    839 
    840             /* Make sure that only valid ports are scaned */
    841             BCM_PBMP_ASSIGN(hw_update, hw_link);
    842 
    843             /* In _bcm_linkscan_update_port, new_link is retrieved from pbm_hw_upd */
    844             BCM_PBMP_ASSIGN(lc->pbm_hw_upd, hw_link);
    845 
    846             BCM_PBMP_XOR(hw_update, lc->pbm_link);
    847 
    848             BCM_PBMP_AND(hw_update, lc->pbm_hw);
    849             
    850             /* Make sure that only valid ports are scaned */
    851             _bcm_linkscan_update(unit, hw_update);
    852 
    853             /* Make sure that only valid ports are scaned */
    854             BCM_PBMP_AND(change, PBMP_PORT_ALL(unit));
    855             _bcm_linkscan_update(unit, change);
    856             soc_linkctrl_linkscan_continue(unit);
    857 #ifdef BCM_CMICX_SUPPORT
    858             if (soc_feature(unit, soc_feature_cmicx)) {
    859                 BCM_PBMP_REMOVE(all_update, lc->pbm_hw);
    860             }
    861 #endif /* BCM_CMICX_SUPPORT */
    862         }
    863 
    864         /* After processing the link status changes of the ports
    865          * indicated by interrupt handler (mainly to trigger swfailover),
    866          * scan all the ports again to make sure that the link status
    867          * is stable.
    868          * For some PHYs such as 5228, hardware linkscan may say the
    869          * link is up while the PHY is actually not quite done
    870          * autonegotiating. Rescanning make sure that the PHY link is
    871          * in sync with switch HW link state.
    872          */ 
    873 
    874         _bcm_linkscan_update(unit, all_update);
    875 
    876 
    877         if (!BCM_PBMP_IS_NULL(change)) {
    878             /* Re-scan due to hardware force */
    879             continue;
    880         }
    881 
    882         (void)sal_sem_take(lc->sema, interval);
    883     }
    884 
    885     (void)soc_linkctrl_linkscan_register(unit, NULL);
    886 
    887     /*
    888      * Before exiting, re-enable all ports that were being scanned.
    889      *
    890      * For administrative reloads, application can enter reload
    891      * mode to avoid this disturbing of ports.
    892      */
    893 
    894 #if defined (BCM_DNX_SUPPORT) || defined (BCM_DNXF_SUPPORT)
    895     if (SOC_IS_DNX(unit) || SOC_IS_DNXF(unit)) {
    896         (void)dnxc_ha_tmp_allow_access_enable(unit, DNXC_HA_ALLOW_SCHAN);
    897     }
    898 #endif
    899     BCM_PBMP_ITER(lc->pbm_sw, port) {
    900         int enable;
    901 
    902         if (BCM_SUCCESS(bcm_port_enable_get(unit, port, &enable))) {
    903             (void)bcm_port_update(unit, port, enable);
    904         }
    905     }
    906 
    907     BCM_PBMP_ITER(lc->pbm_hw, port) {
    908         int enable;
    909 
    910         if (BCM_SUCCESS(bcm_port_enable_get(unit, port, &enable))) {
    911             (void)bcm_port_update(unit, port, enable);
    912         }
    913     }
    914 #if defined (BCM_DNX_SUPPORT) || defined (BCM_DNXF_SUPPORT)
    915     if (SOC_IS_DNX(unit) || SOC_IS_DNXF(unit)) {
    916         (void)dnxc_ha_tmp_allow_access_disable(unit, DNXC_HA_ALLOW_SCHAN);
    917     }
    918 #endif
    919     LOG_INFO(BSL_LS_BCM_LINK,
    920              (BSL_META_U(unit,
    921                          "Linkscan exiting\n")));
    922 
    923     lc->thread_id = NULL;
    924 
    925     DNXC_MTA(dnxc_multithread_analyzer_declare_api_in_play(unit, __FUNCTION__, MTA_FLAG_THREAD_MAIN_FUNC, FALSE));
    926 
    927 #ifdef BCM_DNX_SUPPORT
    928     if(SOC_IS_DNX(unit))
    929     {
    930         DNX_ERR_RECOVERY_UTILS_EXCLUDED_THREAD_REMOVE(unit);
    931     }
    932 #endif
    933 
    934     sal_thread_exit(0);
    935 }
    936 
    937 
    938 /*
    939  * Internal BCM link routines
    940  */
    941 
    942 /*
    943  * Function:    
    944  *     _bcm_link_get
    945  * Purpose:
    946  *     Return linkscan's current link status for the given port.
    947  * Parameters:  
    948  *     unit - Device unit number
    949  *     port - Device port number
    950  *     link - (OUT) Current link status
    951  * Returns:
    952  *     BCM_E_NONE     - Success
    953  *     BCM_E_DISABLED - Port not being scanned
    954  * Note:
    955  *     This routine does not acquire the LS_LOCK, as it only reads a 
    956  *     snapshot of the link bitmaps.  It also cannot hold the LS_LOCK 
    957  *     since it is called indirectly from the linkscan thread 
    958  *     when requesting port info.
    959  */
    960 int
    961 _bcm_link_get(int unit, bcm_port_t port, int *link)
    962 {
    963     int            rv = BCM_E_NONE;
    964     _ls_control_t  *lc;
    965     UNIT_VALID_CHECK(unit);
    966 
    967     if ((lc = LS_CONTROL(unit)) == NULL) {
    968         return BCM_E_DISABLED;
    969     }
    970 
    971     if (BCM_PBMP_MEMBER(lc->pbm_override_ports, port)) {
    972         *link = BCM_PBMP_MEMBER(lc->pbm_override_link, port);
    973         return BCM_E_NONE;
    974     }
    975 
    976     rv = bcm_linkscan_enable_port_get(unit, port);
    977     if (BCM_SUCCESS(rv)) {
    978         *link = (SOC_PBMP_MEMBER(lc->pbm_link, port) &&
    979                  !SOC_PBMP_MEMBER(lc->pbm_remote_fault, port) &&
    980                  !SOC_PBMP_MEMBER(lc->pbm_local_fault, port)) ?
    981                  BCM_PORT_LINK_STATUS_UP : BCM_PORT_LINK_STATUS_DOWN;
    982 
    983 
    984     }
    985 
    986     return rv;
    987 }
    988 
    989 /*
    990  * Function:    
    991  *     _bcm_link_force
    992  * Purpose:
    993  *     Set linkscan's current link status for a port.
    994  * Parameters:  
    995  *     unit  - Device unit number
    996  *     port  - Device port number
    997  *     force - If TRUE, link status is forced to new link status;
    998  *             if FALSE, link status is no longer forced
    999  *     link  - New link status
   1000  * Returns:
   1001  *     BCM_E_NONE - Success
   1002  *     BCM_E_INIT - Not initialized.
   1003  * Notes:
   1004  *     When a link is forced up or down, linkscan will stop scanning
   1005  *     that port and _bcm_link_get will always return the forced status.
   1006  *     This is used for purposes such as when a link is placed in MAC
   1007  *     loopback.  If software forces a link up, it is responsible for
   1008  *     configuring that port.
   1009  */
   1010 int
   1011 _bcm_link_force(int unit, bcm_port_t port, int force, int link)
   1012 {
   1013     _ls_control_t  *lc;
   1014     pbmp_t         pbm;
   1015 
   1016     UNIT_INIT_CHECK(unit);
   1017 
   1018     if (!SOC_PORT_VALID(unit, port) || !IS_PORT(unit, port)) {
   1019         return BCM_E_PORT;
   1020     }
   1021 
   1022     LS_LOCK(unit);
   1023     lc = LS_CONTROL(unit);
   1024 
   1025     if (force) {
   1026         BCM_PBMP_PORT_REMOVE(lc->pbm_override_link, port);
   1027         if (link) {
   1028             BCM_PBMP_PORT_ADD(lc->pbm_override_link, port);
   1029         }
   1030         BCM_PBMP_PORT_ADD(lc->pbm_override_ports, port);
   1031     } else {
   1032         BCM_PBMP_PORT_REMOVE(lc->pbm_override_ports, port);
   1033         BCM_PBMP_PORT_REMOVE(lc->pbm_override_link, port);
   1034         BCM_PBMP_PORT_ADD(lc->pbm_link_change, port);
   1035     }
   1036 
   1037     /*
   1038      * Force immediate update to just this port - this allows loopback 
   1039      * forces to take effect immediately.
   1040      */
   1041     BCM_PBMP_CLEAR(pbm);
   1042     BCM_PBMP_PORT_ADD(pbm, port);
   1043     _bcm_linkscan_update(unit, pbm);
   1044 
   1045     LS_UNLOCK(unit);
   1046 
   1047     /*
   1048      * Wake up master thread to notice changes - required if using hardware
   1049      * link scanning.
   1050      */
   1051     if (lc->sema != NULL) {
   1052         sal_sem_give(lc->sema);
   1053     }
   1054 
   1055     return BCM_E_NONE;
   1056 }
   1057 
   1058 /*
   1059  * Function:
   1060  *     bcm_linkscan_init
   1061  * Purpose:
   1062  *     Initialize the linkscan software module.
   1063  * Parameters:
   1064  *     unit   - Device unit number
   1065  *     driver - Device specific link routines
   1066  * Returns:
   1067  *     BCM_E_XXX
   1068  * Notes:
   1069  *     Device specific bcm_linkscan_init() must call this routine
   1070  *     in order to initialize BCM linkscan common module.
   1071  */
   1072 int
   1073 _bcm_linkscan_init(int unit, const _bcm_ls_driver_t *driver)
   1074 {
   1075     _ls_control_t   *lc;
   1076     bcm_port_if_t   intf;
   1077     int             rv = BCM_E_NONE;
   1078     uint32          size;
   1079     soc_port_t      port;
   1080 #ifdef BCM_WARM_BOOT_SUPPORT
   1081     uint32          scache_handle;
   1082     uint8           *scache_ptr = NULL;
   1083     uint16          default_ver = LINK_WB_CURRENT_VERSION;
   1084     uint16          recovered_ver = LINK_WB_CURRENT_VERSION;
   1085     _ls_control_t   *free_lc = NULL;
   1086 #endif
   1087 
   1088     UNIT_VALID_CHECK(unit);
   1089 
   1090     if (_linkscan_control[unit] != NULL) {
   1091         BCM_IF_ERROR_RETURN(bcm_linkscan_detach(unit));
   1092     }
   1093 
   1094     size = sizeof(_ls_control_t);
   1095     if ((lc = sal_alloc(size, "link_control")) == NULL) {
   1096         return BCM_E_MEMORY;
   1097     }
   1098     sal_memset(lc, 0, size);
   1099 
   1100 #ifdef BCM_WARM_BOOT_SUPPORT
   1101     SOC_SCACHE_HANDLE_SET(scache_handle, unit, BCM_MODULE_LINKSCAN, 0);
   1102 
   1103     /* on cold boot, setup scache */
   1104     rv = soc_versioned_scache_ptr_get(unit, scache_handle,
   1105                                       (SOC_WARM_BOOT(unit) ? FALSE : TRUE),
   1106                                       &size, &scache_ptr,
   1107                                       default_ver, &recovered_ver);
   1108     if (BCM_FAILURE(rv) && (rv != SOC_E_NOT_FOUND)) {
   1109         LOG_ERROR(BSL_LS_BCM_LINK,
   1110                   (BSL_META_U(unit,
   1111                               "Error(%s) reading scache. scache_ptr:%p and len:%d\n"),
   1112                    soc_errmsg(rv), scache_ptr, size));
   1113         /* If warmboot initialization fails, skip using warmboot for linkscan */
   1114         rv = SOC_E_NOT_FOUND;
   1115         goto skip_wb;
   1116     }
   1117 
   1118     LOG_VERBOSE(BSL_LS_BCM_LINK,
   1119                 (BSL_META_U(unit,
   1120                             "LINKSCAN: allocating 0x%x (%d) bytes of scache:"),
   1121                  size, size));
   1122 
   1123     rv = soc_scache_handle_used_set(unit, scache_handle, size);
   1124     
   1125     /* Get the pointer for the Level 2 cache */
   1126     free_lc = lc;
   1127     
   1128     if (scache_ptr) {
   1129         /* if supporting warmboot, use scache */
   1130         sal_free(lc);
   1131         free_lc = NULL;
   1132         lc = (_ls_control_t*) scache_ptr;
   1133 
   1134         if (SOC_WARM_BOOT(unit)) {
   1135             /* clear out non-recoverable resources */
   1136             lc->thread_id = NULL;
   1137             lc->sema = NULL;
   1138             lc->lock = NULL;
   1139             lc->driver = NULL;
   1140             lc->handler_cb = NULL;
   1141             sal_memset(lc->port_link_f, 0, sizeof(lc->port_link_f));
   1142         } else {
   1143             sal_memset(lc, 0, size);
   1144         }
   1145     }
   1146 
   1147 skip_wb:
   1148 #endif /* BCM_WARM_BOOT_SUPPORT */
   1149 
   1150     if (BCM_SUCCESS(rv) || (rv == SOC_E_NOT_FOUND)) {
   1151         lc->lock = sal_mutex_create("bcm_link_LOCK");
   1152         if (lc->lock == NULL) {
   1153             rv = BCM_E_MEMORY;
   1154         }
   1155     }
   1156 
   1157     if (BCM_SUCCESS(rv) || (rv == SOC_E_NOT_FOUND)) {
   1158         lc->sema = sal_sem_create("bcm_link_SLEEP", 
   1159                                   sal_sem_BINARY, 0);
   1160         if (lc->sema == NULL) {
   1161             sal_mutex_destroy(lc->lock);
   1162             rv = BCM_E_MEMORY;
   1163         }
   1164     }
   1165 
   1166     if (BCM_FAILURE(rv) && (rv != SOC_E_NOT_FOUND)) {
   1167 #ifdef BCM_WARM_BOOT_SUPPORT
   1168         if (free_lc) {
   1169             sal_free(free_lc);
   1170         } else
   1171 #endif
   1172         {
   1173             sal_free(lc);
   1174         }
   1175 
   1176         return rv;
   1177     }
   1178 
   1179     _linkscan_control[unit] = lc;
   1180     _linkscan_control[unit]->driver = driver;
   1181 
   1182     /*
   1183      * Initialize link_control port bitmaps so bcm_port_update works
   1184      * reasonably even if the linkscan thread is never started.
   1185      */
   1186     if (!SOC_WARM_BOOT(unit)) {
   1187         _bcm_linkscan_pbm_init(unit);
   1188     }
   1189 
   1190     /* 1. Select between C45 and C22 for HW linkscan
   1191      * 2. Select appropriate MDIO Bus
   1192      * 3. Select between MDIO scan vs. link status from internal PHY
   1193      * 4. Initialize HW linkscan PHY address map.
   1194      */
   1195     if (!(soc_feature(unit, soc_feature_cmicx) && SAL_BOOT_PLISIM)) {
   1196         BCM_IF_ERROR_RETURN(soc_linkctrl_linkscan_hw_init(unit));
   1197     }
   1198     /*
   1199      * Select the source of the CMIC link status interrupt
   1200      * to be the Internal Serdes on SGMII ports
   1201      */ 
   1202     BCM_PBMP_CLEAR(lc->pbm_sgmii_autoneg);
   1203     if (soc_feature(unit, soc_feature_sgmii_autoneg)) {
   1204         PBMP_ITER(PBMP_PORT_ALL(unit), port) {
   1205             if (soc_property_port_get(unit, port,
   1206                                       spn_PHY_SGMII_AUTONEG, FALSE)) {
   1207                 rv = bcm_port_interface_get(unit, port, &intf);
   1208                 if ( BCM_SUCCESS(rv) && (intf == BCM_PORT_IF_SGMII)){
   1209                     BCM_PBMP_PORT_ADD(lc->pbm_sgmii_autoneg, port);
   1210                     LS_INTERNAL_SELECT(unit, port);
   1211                 }
   1212             }
   1213         }
   1214     }
   1215 
   1216     return BCM_E_NONE;
   1217 }
   1218 
   1219 /* This function locks the LS thread, it's used outside the scope of the module in order to protect dynamic port
   1220    provioning from removing a port while the thread is active, which causes a failure in port validity check */
   1221 int
   1222 _bcm_linkscan_pause(int unit)
   1223 {
   1224     UNIT_VALID_CHECK(unit);
   1225 
   1226     /*  check if Linkscan is active */
   1227     if (LS_CONTROL(unit)) {
   1228         /* Lock the mutex */
   1229         LS_LOCK(unit);
   1230     }
   1231 
   1232     return BCM_E_NONE;
   1233 }
   1234 
   1235 
   1236 /* This function unlocks the LS thread, it's used outside the scope of the module in order to protect dynamic port
   1237    provioning from removing a port while the thread is active, which causes a failure in port validity check */
   1238 int
   1239 _bcm_linkscan_continue(int unit)
   1240 {
   1241     UNIT_VALID_CHECK(unit);
   1242 
   1243     /*  check if Linkscan is active */
   1244     if (LS_CONTROL(unit)) {
   1245         /* Lock the mutex */
   1246         LS_UNLOCK(unit);
   1247     }
   1248 
   1249     return BCM_E_NONE;
   1250 }
   1251 
   1252 /*
   1253  * Function:
   1254  *     _bcm_linkscan_available
   1255  * Purpose:
   1256  *     To check if linkscan module is initialized.
   1257  * Parameters:
   1258  *     unit   - Device unit number
   1259  * Returns:
   1260  *     BCM_E_XXX
   1261  */
   1262 int
   1263 _bcm_linkscan_available(int unit)
   1264 {
   1265     _ls_control_t  *lc;
   1266     UNIT_VALID_CHECK(unit);
   1267 
   1268     if ((lc = LS_CONTROL(unit)) == NULL) {
   1269         return BCM_E_DISABLED;
   1270     }
   1271 
   1272     return BCM_E_NONE;
   1273 }
   1274 
   1275 /*
   1276  * Function:
   1277  *     _bcm_linkscan_port_init
   1278  * Purpose:
   1279  *     Add the port to bitmaps in the link_control structure.
   1280  * Parameters:
   1281  *     unit - Device unit number
   1282  *     port - Logical port ID
   1283  * Returns:
   1284  *     BCM_E_XXX
   1285  * Notes:
   1286  *     Used for dynamic port provisioning.
   1287  */
   1288 int
   1289 _bcm_linkscan_port_init(int unit, int port)
   1290 {
   1291     _ls_control_t  *lc = LS_CONTROL(unit);
   1292 
   1293     UNIT_VALID_CHECK(unit);
   1294 
   1295     if (!SOC_PORT_VALID(unit, port) || !IS_PORT(unit, port)) {
   1296         return BCM_E_PORT;
   1297     }
   1298 
   1299     /*
   1300      * Force initial scan by setting link change while pretending link
   1301      * was initially up.
   1302      */
   1303     LS_LOCK(unit);
   1304     BCM_PBMP_PORT_ADD(lc->pbm_link, port);
   1305     BCM_PBMP_PORT_ADD(lc->pbm_link_change, port);
   1306     BCM_PBMP_PORT_ADD(lc->pbm_hw_upd, port);
   1307     LS_UNLOCK(unit);
   1308 
   1309     return BCM_E_NONE;
   1310 }
   1311 
   1312 #ifdef BCM_WARM_BOOT_SUPPORT
   1313 /* This function compresses the info in _ls_control_t and stores it
   1314  * to stable memory.
   1315  * Input param: sync --> indicates whether to sync scache to Persistent memory
   1316  */
   1317 int
   1318 bcm_linkscan_sync(int unit, int sync)
   1319 {
   1320     int                     rv = BCM_E_NONE;
   1321     uint8                   *scache_ptr = NULL;
   1322     uint32                  scache_len = 0;
   1323     soc_scache_handle_t     scache_handle;
   1324     uint16                  default_ver = LINK_WB_CURRENT_VERSION;
   1325     uint16                  recovered_ver = LINK_WB_CURRENT_VERSION;
   1326 
   1327     if (SOC_WARM_BOOT(unit)) {
   1328         LOG_ERROR(BSL_LS_BCM_COMMON,
   1329                   (BSL_META_U(unit,
   1330                               "Cannot write to SCACHE during WarmBoot\n")));
   1331         return SOC_E_INTERNAL;
   1332     }
   1333 
   1334     SOC_SCACHE_HANDLE_SET(scache_handle, unit, BCM_MODULE_LINKSCAN, 0);
   1335 
   1336     rv = soc_versioned_scache_ptr_get(unit, scache_handle,
   1337                                       FALSE, &scache_len, &scache_ptr,
   1338                                       default_ver, &recovered_ver);
   1339     if (SOC_FAILURE(rv) && (rv != SOC_E_NOT_FOUND)) {
   1340         LOG_ERROR(BSL_LS_BCM_LINK,
   1341                   (BSL_META_U(unit,
   1342                               "Error(%s) reading scache. scache_ptr:%p and len:%d\n"),
   1343                    soc_errmsg(rv), scache_ptr, scache_len));
   1344         return rv;
   1345     }
   1346 
   1347     rv = soc_scache_handle_used_set(unit, scache_handle, sizeof(_ls_control_t));
   1348 
   1349     if (sync) {
   1350         rv = soc_scache_commit(unit);
   1351         if (rv != SOC_E_NONE) {
   1352             LOG_ERROR(BSL_LS_BCM_LINK,
   1353                       (BSL_META_U(unit,
   1354                                   "Error(%s) sync'ing scache to Persistent memory. \n"),
   1355                        soc_errmsg(rv)));
   1356             return rv;
   1357         }
   1358     }
   1359     return BCM_E_NONE;
   1360 }
   1361 #endif /* BCM_WARM_BOOT_SUPPORT */
   1362 
   1363 /************************************************************************
   1364  *********                                                      *********
   1365  *********         Start of BCM API Exported Routines           *********
   1366  */
   1367 
   1368 /*
   1369  * Function:
   1370  *     bcm_linkscan_detach
   1371  * Purpose:
   1372  *     Prepare linkscan module to detach specified unit.
   1373  * Parameters:
   1374  *     unit - Device unit number
   1375  * Returns:
   1376  *     BCM_E_NONE - Detach successful
   1377  *     BCM_E_XXX  - Detach failed
   1378  * Notes:
   1379  *     This is safe to call at any time, but linkscan should only be
   1380  *     initialized or detached from the main application thread.
   1381  */
   1382 int
   1383 bcm_common_linkscan_detach(int unit)
   1384 {
   1385     _ls_control_t     *lc;
   1386     _ls_handler_cb_t  *lh;
   1387     pbmp_t            empty_pbm;
   1388 #ifdef BCM_WARM_BOOT_SUPPORT
   1389     int               size, rv;
   1390     uint32            scache_handle;
   1391     uint8            *ptr;
   1392     soc_wb_cache_t   *wb_cache = NULL;
   1393 #endif
   1394 
   1395     UNIT_VALID_CHECK(unit);
   1396 
   1397     lc = LS_CONTROL(unit);
   1398     if (lc == NULL) {
   1399         return BCM_E_NONE;
   1400     }
   1401 
   1402     BCM_PBMP_CLEAR(empty_pbm);
   1403 
   1404     SOC_IF_ERROR_RETURN(soc_linkctrl_linkscan_config(unit,
   1405                                                      empty_pbm, empty_pbm));
   1406 
   1407     BCM_IF_ERROR_RETURN(bcm_common_linkscan_enable_set(unit, 0));
   1408 
   1409 #ifdef BCM_CMICX_SUPPORT
   1410     if (soc_feature(unit, soc_feature_cmicx) && !SAL_BOOT_PLISIM)
   1411     {
   1412         /* Iproc M0 exit */
   1413         soc_iproc_m0_exit(unit);
   1414     }
   1415 #endif /* BCM_CMICX_SUPPORT */
   1416 
   1417     /* Clean up list of handlers */
   1418 
   1419     while (lc->handler_cb != NULL) {
   1420         lh = lc->handler_cb;
   1421         lc->handler_cb = lh->next;
   1422         sal_free(lh);
   1423     }
   1424 
   1425     /* Mark and not initialized and free mutex */
   1426 
   1427     if (lc->sema != NULL) {
   1428         sal_sem_destroy(lc->sema);
   1429         lc->sema = NULL;
   1430     }
   1431 
   1432     if (lc->lock != NULL) {
   1433         sal_mutex_destroy(lc->lock);
   1434         lc->lock = NULL;
   1435     }
   1436 
   1437     LS_CONTROL(unit) = NULL;
   1438 
   1439 #ifdef BCM_WARM_BOOT_SUPPORT
   1440     SOC_SCACHE_HANDLE_SET(scache_handle, unit, BCM_MODULE_LINKSCAN, 0);
   1441     rv = soc_stable_size_get(unit, &size);
   1442     if (BCM_SUCCESS(rv) && size) {
   1443         rv = soc_scache_ptr_get(unit, scache_handle, &ptr, (uint32*)&size);
   1444         wb_cache = (soc_wb_cache_t*)ptr;
   1445     }
   1446 
   1447     /* lc was allocated when Warm Boot is compiled and 
   1448      * the scache is not configured */
   1449     if (BCM_SUCCESS(rv) && (wb_cache == NULL)) {
   1450         sal_free(lc);
   1451     }
   1452     
   1453 #else
   1454     sal_free(lc);
   1455 #endif
   1456 
   1457     return BCM_E_NONE;
   1458 }
   1459 
   1460 /*
   1461  * Function:    
   1462  *     bcm_linkscan_enable_set
   1463  * Purpose:
   1464  *     Enable or disable the link scan feature.
   1465  * Parameters:
   1466  *     unit - Device unit number
   1467  *     us   - Specifies the software polling interval in micro-seconds;
   1468  *            the value 0 disables linkscan
   1469  * Returns:
   1470  *     BCM_E_XXX
   1471  */
   1472 int
   1473 bcm_common_linkscan_enable_set(int unit, int us)
   1474 {
   1475     _ls_control_t   *lc;
   1476     int             rv = BCM_E_NONE;
   1477     soc_timeout_t   to;
   1478     pbmp_t          empty_pbm;
   1479     sal_thread_t    tid;
   1480 
   1481     UNIT_VALID_CHECK(unit);
   1482 
   1483     lc = LS_CONTROL(unit);
   1484     if (!us && lc == NULL) {    /* No error to disable if not initialized */
   1485         return BCM_E_NONE;
   1486     }
   1487 
   1488     UNIT_INIT_CHECK(unit);
   1489 
   1490     sal_snprintf(lc->taskname,
   1491                  sizeof (lc->taskname),
   1492                  "bcmLINK.%d",
   1493                  unit);
   1494 
   1495     BCM_PBMP_CLEAR(empty_pbm);
   1496 
   1497     if (us) {
   1498         if (us < BCM_LINKSCAN_INTERVAL_MIN) {
   1499             us = BCM_LINKSCAN_INTERVAL_MIN;
   1500         }
   1501 
   1502         lc->interval_us = us;
   1503 
   1504         if (lc->thread_id != NULL) {
   1505             /* Linkscan is already running; just update the period */
   1506             sal_sem_give(lc->sema);
   1507             return BCM_E_NONE;
   1508         }
   1509 
   1510         tid = sal_thread_create(lc->taskname,
   1511                               SAL_THREAD_STKSZ,
   1512                               soc_property_get(unit,
   1513                                                spn_LINKSCAN_THREAD_PRI,
   1514                                                50),
   1515                               (void (*)(void*))_bcm_linkscan_thread,
   1516                               INT_TO_PTR(unit));
   1517 
   1518         if (tid == SAL_THREAD_ERROR) {
   1519             lc->interval_us = 0;
   1520             rv = BCM_E_MEMORY;
   1521         } else {
   1522             soc_timeout_init(&to, 3000000, 0);
   1523 
   1524             while (lc->thread_id == NULL) {
   1525                 if (soc_timeout_check(&to)) {
   1526                     LOG_ERROR(BSL_LS_BCM_LINK,
   1527                               (BSL_META_U(unit,
   1528                                           "%s: Thread did not start\n"),
   1529                               lc->taskname));
   1530                     lc->interval_us = 0;
   1531                     rv = BCM_E_INTERNAL;
   1532                     break;
   1533                 }
   1534             }
   1535             if (BCM_SUCCESS(rv)) {
   1536                 /* Make sure HW linkscanning is enabled on HW linkscan ports */
   1537                 rv = soc_linkctrl_linkscan_config(unit,
   1538                                                   lc->pbm_hw, empty_pbm);
   1539             }
   1540 
   1541             sal_sem_give(lc->sema);
   1542         }
   1543     } else if (lc->thread_id != NULL) {
   1544         lc->interval_us = 0;
   1545 
   1546         /* To prevent getting HW linkscan interrupt after linkscan is disabled,
   1547          * HW linkscanning must be disabled. */ 
   1548         rv = soc_linkctrl_linkscan_config(unit, empty_pbm, empty_pbm);
   1549 
   1550         sal_sem_give(lc->sema);
   1551 
   1552         soc_timeout_init(&to, 10000000, 0);   /* Enough time for Quickturn */
   1553 
   1554         while (lc->thread_id != NULL) {
   1555             if (soc_timeout_check(&to)) {
   1556                 LOG_ERROR(BSL_LS_BCM_LINK,
   1557                           (BSL_META_U(unit,
   1558                                       "%s: Thread did not exit\n"),
   1559                            lc->taskname));
   1560                 rv = BCM_E_INTERNAL;
   1561                 break;
   1562             }
   1563         }
   1564     }
   1565 
   1566     return rv;
   1567 }
   1568 
   1569 /*
   1570  * Function:    
   1571  *     bcm_linkscan_enable_get
   1572  * Purpose:
   1573  *     Retrieve the current linkscan mode.
   1574  * Parameters:
   1575  *     unit - Device unit number
   1576  *     us   - (OUT) Pointer to microsecond scan time for software scanning, 
   1577  *             0 if not enabled.
   1578  * Returns:
   1579  *     BCM_E_XXX
   1580  */
   1581 int
   1582 bcm_common_linkscan_enable_get(int unit, int *us)
   1583 {
   1584     UNIT_INIT_CHECK(unit);
   1585 
   1586     *us = LS_CONTROL(unit)->interval_us;
   1587 
   1588     return BCM_E_NONE;
   1589 }
   1590 
   1591 /*
   1592  * Function:
   1593  *     bcm_linkscan_enable_port_get
   1594  * Purpose:
   1595  *     Determine whether or not linkscan is managing a given port.
   1596  * Parameters:
   1597  *     unit - Device unit number
   1598  *     port - Device port to check
   1599  * Returns:
   1600  *     BCM_E_NONE     - Port being scanned
   1601  *     BCM_E_DISABLED - Port not being scanned
   1602  */
   1603 int
   1604 bcm_common_linkscan_enable_port_get(int unit, bcm_port_t port)
   1605 {
   1606     int            rv = BCM_E_NONE;
   1607     _ls_control_t  *lc;
   1608 
   1609     UNIT_VALID_CHECK(unit);
   1610 
   1611     if (BCM_GPORT_IS_SET(port)) {
   1612         BCM_IF_ERROR_RETURN(bcm_port_local_get(unit, port, &port));
   1613     }
   1614 
   1615     if (!SOC_PORT_VALID(unit, port) || !IS_PORT(unit, port)) {
   1616         return BCM_E_PORT;
   1617     }
   1618 
   1619     if ((lc = LS_CONTROL(unit)) == NULL) {
   1620         return BCM_E_DISABLED;
   1621     }
   1622 
   1623     if (lc->interval_us == 0 ||
   1624         (!BCM_PBMP_MEMBER(lc->pbm_sw, port) &&
   1625          !BCM_PBMP_MEMBER(lc->pbm_hw, port) &&
   1626          !BCM_PBMP_MEMBER(lc->pbm_override_ports, port))) {
   1627         rv = BCM_E_DISABLED;
   1628     }
   1629     return rv;
   1630 }
   1631 
   1632 /*
   1633  * Function:    
   1634  *     bcm_linkscan_mode_set
   1635  * Purpose:
   1636  *     Set the current scanning mode for the specified port.
   1637  * Parameters:
   1638  *     unit - Device unit number
   1639  *     port - Device port number
   1640  *     mode - New scan mode for specified port
   1641  * Returns:
   1642  *     BCM_E_XXX
   1643  */
   1644 int
   1645 bcm_common_linkscan_mode_set(int unit, bcm_port_t port, int mode)
   1646 {
   1647     _ls_control_t   *lc;
   1648     int             rv = BCM_E_NONE;
   1649     pbmp_t          empty_pbm;
   1650     int             added = 0, sw_member = 0;
   1651 
   1652     UNIT_INIT_CHECK(unit);
   1653 
   1654     if (BCM_GPORT_IS_SET(port)) {
   1655         BCM_IF_ERROR_RETURN(bcm_port_local_get(unit, port, &port));
   1656     }
   1657 
   1658     if (!SOC_PORT_VALID(unit, port) || !IS_PORT(unit, port)) {
   1659         return BCM_E_PORT;
   1660     }
   1661 
   1662     BCM_PBMP_CLEAR(empty_pbm);
   1663 
   1664     LS_LOCK(unit);
   1665 
   1666     lc = LS_CONTROL(unit);
   1667 
   1668     /* First, remove from current configuration */
   1669     if (BCM_PBMP_MEMBER(lc->pbm_sw, port)) {
   1670         sw_member = 1;
   1671     }
   1672  
   1673     BCM_PBMP_PORT_REMOVE(lc->pbm_sw, port);
   1674     BCM_PBMP_PORT_REMOVE(lc->pbm_hw, port);
   1675 
   1676     /* Now add back to proper map */
   1677     switch (mode) {
   1678     case BCM_LINKSCAN_MODE_NONE:
   1679         SOC_PBMP_PORT_REMOVE(lc->pbm_remote_fault, port);
   1680         SOC_PBMP_PORT_REMOVE(lc->pbm_local_fault, port);
   1681         break;
   1682     case BCM_LINKSCAN_MODE_SW:
   1683         BCM_PBMP_PORT_ADD(lc->pbm_sw, port);
   1684         BCM_PBMP_PORT_ADD(lc->pbm_newly_enabled, port);
   1685         added = 1;
   1686         break;
   1687     case BCM_LINKSCAN_MODE_HW:
   1688         if (lc->link_fault_chk_en == 1 ||
   1689             lc->link_local_fault_chk_en == 1) {
   1690             LOG_ERROR(BSL_LS_BCM_LINK,
   1691                      (BSL_META_U(unit,
   1692                               "HW link scan and link fault are not supported\n")));
   1693             if(sw_member) {
   1694                 BCM_PBMP_PORT_ADD(lc->pbm_sw, port);
   1695             }
   1696 
   1697             LS_UNLOCK(unit);
   1698             return BCM_E_PARAM;
   1699         }
   1700         BCM_PBMP_PORT_ADD(lc->pbm_hw, port);
   1701 
   1702         if (BCM_PBMP_MEMBER(lc->pbm_sgmii_autoneg, port)) {
   1703             /* Need to run SW link scan as well on ports where the source 
   1704              * of the link status is the internal Serdes - only SGMII ports */
   1705             BCM_PBMP_PORT_ADD(lc->pbm_sw, port);
   1706         }
   1707         BCM_PBMP_PORT_ADD(lc->pbm_newly_enabled, port);
   1708         added = 1;
   1709         lc->hw_change = 1;
   1710         break;
   1711     default:
   1712         return BCM_E_PARAM;
   1713     }
   1714 
   1715     /* Reconfigure HW linkscan in case changed */
   1716     rv = soc_linkctrl_linkscan_config(unit, lc->pbm_hw, empty_pbm);
   1717 
   1718     /* Prime the HW linkscan pump */
   1719     if (SOC_PBMP_NOT_NULL(lc->pbm_hw)) {
   1720         lc->hw_change = 1;
   1721         _bcm_linkscan_hw_interrupt(unit);
   1722     }
   1723 
   1724     if (rv == BCM_E_UNAVAIL && added && sw_member) {
   1725         BCM_PBMP_PORT_ADD(lc->pbm_sw, port);
   1726         BCM_PBMP_PORT_REMOVE(lc->pbm_hw, port); 
   1727     }
   1728 
   1729     LS_UNLOCK(unit);
   1730 
   1731     if (lc->sema != NULL) {
   1732         sal_sem_give(lc->sema);    /* register change now */
   1733     }
   1734 
   1735     /* When no longer scanning a port, return it to the enabled state. */
   1736     if (BCM_SUCCESS(rv) && !added) {
   1737         int enable;
   1738 
   1739         rv = bcm_port_enable_get(unit, port, &enable);
   1740         if (BCM_SUCCESS(rv)) {
   1741             rv = bcm_port_update(unit, port, enable);
   1742         }
   1743     }
   1744 
   1745     return rv;
   1746 }
   1747 
   1748 /*
   1749  * Function:    
   1750  *     bcm_linkscan_mode_set_pbm
   1751  * Purpose:
   1752  *     Set the current scanning mode for the specified ports.
   1753  * Parameters:
   1754  *     unit - Device unit number
   1755  *     pbm  - Port bitmap indicating port to set
   1756  *     mode - New scan mode for specified ports
   1757  * Returns:
   1758  *     BCM_E_XXX
   1759  */
   1760 int
   1761 bcm_common_linkscan_mode_set_pbm(int unit, pbmp_t pbm, int mode)
   1762 {
   1763     bcm_port_t  port;
   1764 
   1765     UNIT_INIT_CHECK(unit);
   1766 
   1767     PBMP_ITER(pbm, port) {
   1768         BCM_IF_ERROR_RETURN
   1769             (bcm_linkscan_mode_set(unit, port, mode));
   1770     }
   1771 
   1772     return BCM_E_NONE;
   1773 }
   1774 
   1775 /*
   1776  * Function:    
   1777  *     bcm_linkscan_mode_get
   1778  * Purpose:
   1779  *     Recover the current scanning mode for the specified port.
   1780  * Parameters:
   1781  *     unit - Device unit number
   1782  *     port - Device port number
   1783  *     mode - (OUT) current scan mode for specified port
   1784  * Returns:
   1785  *     BCM_E_XXX
   1786  */
   1787 int
   1788 bcm_common_linkscan_mode_get(int unit, bcm_port_t port, int *mode)
   1789 {
   1790     _ls_control_t  *lc;
   1791 
   1792     UNIT_INIT_CHECK(unit);
   1793 
   1794     lc = LS_CONTROL(unit);
   1795 
   1796     if (BCM_GPORT_IS_SET(port)) {
   1797         BCM_IF_ERROR_RETURN(bcm_port_local_get(unit, port, &port));
   1798     }
   1799 
   1800     if (!SOC_PORT_VALID(unit, port) || !IS_PORT(unit, port)) {
   1801         return BCM_E_PORT;
   1802     }
   1803 
   1804     if (PBMP_MEMBER(lc->pbm_hw, port)) {
   1805         *mode = BCM_LINKSCAN_MODE_HW;
   1806     } else if (PBMP_MEMBER(lc->pbm_sw, port)) {
   1807         *mode = BCM_LINKSCAN_MODE_SW;
   1808     } else {
   1809         *mode = BCM_LINKSCAN_MODE_NONE;
   1810     }
   1811 
   1812     return BCM_E_NONE;
   1813 }
   1814 
   1815 /*
   1816  * Function:    
   1817  *     bcm_linkscan_update
   1818  * Purpose:     
   1819  *     Check for a change in link status on each link.  If a change
   1820  *     is detected, call bcm_port_update to program the MACs for that
   1821  *     link, and call the list of registered handlers.
   1822  * Parameters:  
   1823  *     unit - Device unit number
   1824  *     pbm  - Bitmap of ports to scan
   1825  * Returns:
   1826  *     BCM_E_XXX
   1827  */
   1828 int 
   1829 bcm_common_linkscan_update(int unit, bcm_pbmp_t pbm)
   1830 {                       
   1831     UNIT_INIT_CHECK(unit);
   1832 
   1833     if (BCM_PBMP_IS_NULL(pbm)) {
   1834         return BCM_E_NONE;
   1835     }
   1836 
   1837     _bcm_linkscan_update(unit, pbm);
   1838 
   1839     return BCM_E_NONE;
   1840 }
   1841 
   1842 /*
   1843  * Function:    
   1844  *     bcm_linkscan_register
   1845  * Purpose:
   1846  *     Register a handler to be called when a link status change is noticed.
   1847  * Parameters:
   1848  *     unit - Device unit number
   1849  *     f    - Pointer to function to call when link status change is seen
   1850  * Returns:
   1851  *     BCM_E_XXX
   1852  */
   1853 int
   1854 bcm_common_linkscan_register(int unit, bcm_linkscan_handler_t f)
   1855 {
   1856     _ls_control_t     *lc;
   1857     _ls_handler_cb_t  *lh;
   1858     int               found = FALSE;
   1859 
   1860     UNIT_INIT_CHECK(unit);
   1861     
   1862     if (f == NULL){
   1863         return BCM_E_PARAM;
   1864     }
   1865     
   1866     LS_LOCK(unit);
   1867 
   1868     lc = LS_CONTROL(unit);
   1869 
   1870     /* First, see if this handler already registered */
   1871     for (lh = lc->handler_cb; lh != NULL; lh = lh->next) {
   1872         if (lh->cb_f == f) {
   1873             found = TRUE;
   1874             break;
   1875         }
   1876     }
   1877 
   1878     if (found) {
   1879         LS_UNLOCK(unit);
   1880         return BCM_E_NONE;
   1881     }
   1882 
   1883     if ((lh = sal_alloc(sizeof(*lh), "bcm_linkscan_register")) == NULL) {
   1884         LS_UNLOCK(unit);
   1885         return BCM_E_MEMORY;
   1886     }
   1887 
   1888     lh->next = lc->handler_cb;
   1889     lh->cb_f = f;
   1890     lc->handler_cb = lh;
   1891 
   1892     LS_UNLOCK(unit);
   1893 
   1894     return BCM_E_NONE;
   1895 }
   1896 
   1897 /*
   1898  * Function:
   1899  *     bcm_linkscan_unregister
   1900  * Purpose:
   1901  *     Remove a previously registered handler from the callout list.
   1902  * Parameters:
   1903  *     unit - Device unit number
   1904  *     f    - Pointer to function registered in call to 
   1905  *            bcm_linkscan_register
   1906  * Returns:
   1907  *     BCM_E_NONE      - Success
   1908  *     BCM_E_NOT_FOUND - Could not find matching handler
   1909  */
   1910 int
   1911 bcm_common_linkscan_unregister(int unit, bcm_linkscan_handler_t f)
   1912 {
   1913     _ls_control_t     *lc;
   1914     _ls_handler_cb_t  *lh, *p;
   1915 
   1916     UNIT_INIT_CHECK(unit);
   1917 
   1918     LS_LOCK(unit);
   1919 
   1920     lc = LS_CONTROL(unit);
   1921 
   1922     for (p = NULL, lh = lc->handler_cb; lh; p = lh, lh = lh->next) { 
   1923         if (lh->cb_f == f) {
   1924             if (p == NULL) {
   1925                 lc->handler_cb = lh->next;
   1926             } else {
   1927                 p->next = lh->next;
   1928             }
   1929             break;
   1930         }
   1931     }
   1932 
   1933     LS_UNLOCK(unit);
   1934 
   1935     if (lh == NULL) {
   1936         return BCM_E_NOT_FOUND;
   1937     }        
   1938     
   1939     sal_free(lh);
   1940 
   1941     return BCM_E_NONE;
   1942 }
   1943 
   1944 /*
   1945  * Function:
   1946  *     bcm_linkscan_port_register
   1947  * Purpose:
   1948  *     Register a handler to be called when a link status
   1949  *     is to be determined by a caller provided function.
   1950  * Parameters:
   1951  *     unit - Device unit number
   1952  *     port - Device port number
   1953  *     f    - Pointer to function to call for true link status
   1954  * Returns:
   1955  *     BCM_E_XXX
   1956  * Notes:
   1957  *     This function works with software linkscan only.  
   1958  */
   1959 int
   1960 bcm_common_linkscan_port_register(int unit, bcm_port_t port,
   1961                                   bcm_linkscan_port_handler_t f)
   1962 {
   1963     _ls_control_t  *lc;
   1964   
   1965     UNIT_INIT_CHECK(unit);
   1966 
   1967     if (BCM_GPORT_IS_SET(port)) {
   1968         BCM_IF_ERROR_RETURN(bcm_port_local_get(unit, port, &port));
   1969     }
   1970 
   1971     if (!SOC_PORT_VALID(unit, port) || !IS_PORT(unit, port)) {
   1972         return BCM_E_PORT;
   1973     }
   1974 
   1975     LS_LOCK(unit);
   1976     lc = LS_CONTROL(unit);
   1977     lc->port_link_f[port] = f;
   1978     LS_UNLOCK(unit);
   1979 
   1980     return BCM_E_NONE;
   1981 }
   1982 
   1983 /*
   1984  * Function:
   1985  *     bcm_linkscan_port_unregister
   1986  * Purpose:
   1987  *     Remove a previously registered handler that is used
   1988  *     for setting link status. 
   1989  * Parameters:
   1990  *     unit - Device unit number
   1991  *     port - Device port number
   1992  *     f    - Pointer to function registered in call to 
   1993  *            bcm_linkscan_port_register
   1994  * Returns:
   1995  *     BCM_E_XXX
   1996  */
   1997 int
   1998 bcm_common_linkscan_port_unregister(int unit, bcm_port_t port,
   1999                                     bcm_linkscan_port_handler_t f)
   2000 {
   2001     _ls_control_t  *lc;
   2002 
   2003     UNIT_INIT_CHECK(unit);
   2004 
   2005     if (BCM_GPORT_IS_SET(port)) {
   2006         BCM_IF_ERROR_RETURN(bcm_port_local_get(unit, port, &port));
   2007     }
   2008 
   2009     if (!SOC_PORT_VALID(unit, port) || !IS_PORT(unit, port)) {
   2010         return BCM_E_PORT;
   2011     }
   2012 
   2013     LS_LOCK(unit);
   2014     lc = LS_CONTROL(unit);
   2015     lc->port_link_f[port] = NULL;
   2016     LS_UNLOCK(unit);
   2017 
   2018     return BCM_E_NONE;
   2019 }
   2020 
   2021 /*
   2022  * Function:    
   2023  *     bcm_link_wait
   2024  * Purpose:
   2025  *     Wait for all links in the mask to be "link up".
   2026  * Parameters:  
   2027  *     unit - Device unit number
   2028  *     pbm  - (IN/OUT) Port bitmap to wait for, mask of those link up on 
   2029  *            return
   2030  *     us   - Number of microseconds to wait
   2031  * Returns:
   2032  *     BCM_E_NONE     - all links are ready.
   2033  *     BCM_E_TIMEOUT  - not all links ready in specified time.
   2034  *     BCM_E_DISABLED - linkscan not running on one or more of the ports.
   2035  */
   2036 int
   2037 bcm_common_link_wait(int unit, pbmp_t *pbm, sal_usecs_t us)
   2038 {
   2039     _ls_control_t   *lc;
   2040     soc_timeout_t   to;
   2041     pbmp_t          sofar_pbm;
   2042     soc_port_t      port;
   2043 
   2044     UNIT_VALID_CHECK(unit);
   2045 
   2046     BCM_PBMP_ITER(*pbm, port) {
   2047         BCM_IF_ERROR_RETURN
   2048             (bcm_linkscan_enable_port_get(unit, port));
   2049     }
   2050 
   2051     /*
   2052      * If a port was just configured, it may have gone down but
   2053      * pbm_link may not reflect that until the next time linkscan
   2054      * runs.  This is avoided by forcing an update of pbm_link.
   2055      */
   2056     _bcm_linkscan_update(unit, *pbm);
   2057 
   2058     lc = LS_CONTROL(unit);
   2059 
   2060     soc_timeout_init(&to, us, 0);
   2061 
   2062     for (;;) {
   2063         BCM_PBMP_ASSIGN(sofar_pbm, lc->pbm_link);
   2064         BCM_PBMP_REMOVE(sofar_pbm, lc->pbm_remote_fault);
   2065         BCM_PBMP_REMOVE(sofar_pbm, lc->pbm_local_fault);
   2066         BCM_PBMP_AND(sofar_pbm, *pbm);
   2067         if (BCM_PBMP_EQ(sofar_pbm, *pbm)) {
   2068             break;
   2069         }
   2070 
   2071         if (soc_timeout_check(&to)) {
   2072             BCM_PBMP_AND(*pbm, lc->pbm_link);
   2073             SOC_PBMP_REMOVE(*pbm, lc->pbm_remote_fault);
   2074             SOC_PBMP_REMOVE(*pbm, lc->pbm_local_fault);
   2075             return BCM_E_TIMEOUT;
   2076         }
   2077 
   2078         sal_usleep(lc->interval_us / 4);
   2079     }
   2080 
   2081     return BCM_E_NONE;
   2082 }
   2083 
   2084 /*
   2085  * Function:    
   2086  *     bcm_link_change
   2087  * Purpose:
   2088  *     Force a transient link down event to be recognized,
   2089  *     regardless of the current physical up/down state of the
   2090  *     port.  This does not affect the physical link status. 
   2091  * Parameters:  
   2092  *     unit - Device unit number
   2093  *     pbm  - Bitmap of ports to operate on
   2094  * Returns:
   2095  *     BCM_E_XXX
   2096  */
   2097 int
   2098 bcm_common_link_change(int unit, pbmp_t pbm)
   2099 {
   2100     _ls_control_t  *lc;
   2101 
   2102     UNIT_INIT_CHECK(unit);
   2103 
   2104     LS_LOCK(unit);
   2105 
   2106     lc = LS_CONTROL(unit);
   2107     BCM_PBMP_AND(pbm, PBMP_PORT_ALL(unit));
   2108     BCM_PBMP_OR(lc->pbm_link_change, pbm);
   2109 
   2110     LS_UNLOCK(unit);
   2111 
   2112     /*
   2113      * Wake up master thread to notice changes - required if using hardware
   2114      * link scanning.
   2115      */
   2116     if (lc->sema != NULL) {
   2117         sal_sem_give(lc->sema);
   2118     }
   2119 
   2120     return BCM_E_NONE;
   2121 }
   2122 
   2123 /*
   2124  * Function:
   2125  *     bcm_common_linkscan_trigger_event_set
   2126  * Purpose:
   2127  *     Set Link event to linkscan thread.
   2128  *
   2129  * Parameters:
   2130  *     unit - Device unit number
   2131  *     port - Port number, only -1 is acceptable.
   2132  *     flags - flags
   2133  *     trigger_event - Event type (BCM_LINKSCAN_TRIGGER_EVENT_XXX)
   2134  *     enable - enable the event.
   2135  * Returns:
   2136  *     BCM_E_XXX
   2137  */
   2138 
   2139 int bcm_common_linkscan_trigger_event_set(
   2140       int unit,
   2141       bcm_port_t port,
   2142       uint32 flags,
   2143       bcm_linkscan_trigger_event_t trigger_event,
   2144       int enable)
   2145 {
   2146     int rv = BCM_E_NONE;
   2147     _ls_control_t  *lc;
   2148 
   2149     UNIT_INIT_CHECK(unit);
   2150 
   2151     LS_LOCK(unit);
   2152 
   2153     lc = LS_CONTROL(unit);
   2154     switch (trigger_event) {
   2155         /* If trigger_event is BCM_LINKSCAN_TRIGGER_EVENT_REMOTE_FAULT, also reach this case
   2156          * In the enum definition,
   2157          * BCM_LINKSCAN_TRIGGER_EVENT_REMOTE_FAULT = BCM_LINKSCAN_TRIGGER_EVENT_FAULT
   2158          */
   2159         case BCM_LINKSCAN_TRIGGER_EVENT_FAULT:
   2160             if(BCM_PBMP_IS_NULL(lc->pbm_hw)) {
   2161                 lc->link_fault_chk_en = 1;
   2162             } else {
   2163                 LOG_ERROR(BSL_LS_BCM_LINK,
   2164                          (BSL_META_U(unit,
   2165                                   "HW link scan and link fault are not supported\n")));
   2166                 rv = BCM_E_PARAM;
   2167             }
   2168             break;
   2169         case BCM_LINKSCAN_TRIGGER_EVENT_LOCAL_FAULT:
   2170             if(BCM_PBMP_IS_NULL(lc->pbm_hw)) {
   2171                 lc->link_local_fault_chk_en = 1;
   2172             } else {
   2173                 LOG_ERROR(BSL_LS_BCM_LINK,
   2174                          (BSL_META_U(unit,
   2175                                   "HW link scan and link fault are not supported\n")));
   2176                 rv = BCM_E_PARAM;
   2177             }
   2178             break;
   2179         default:
   2180             rv = BCM_E_PARAM;
   2181             break;
   2182     }
   2183 
   2184     LS_UNLOCK(unit);
   2185 
   2186     return rv;
   2187 }
   2188 
   2189 /*
   2190  * Function:
   2191  *     bcm_common_linkscan_trigger_event_get
   2192  * Purpose:
   2193  *     Get Link event status in linkscan thread.
   2194  *
   2195  * Parameters:
   2196  *     unit - Device unit number
   2197  *     port - Port number, only -1 is acceptable.
   2198  *     flags - flags
   2199  *     trigger_event - Event type (BCM_LINKSCAN_TRIGGER_EVENT_XXX)
   2200  *     enable - event status.
   2201  * Returns:
   2202  *     BCM_E_XXX
   2203  */
   2204 
   2205 int bcm_common_linkscan_trigger_event_get(
   2206       int unit,
   2207       bcm_port_t port,
   2208       uint32 flags,
   2209       bcm_linkscan_trigger_event_t trigger_event,
   2210       int *enable)
   2211 {
   2212     _ls_control_t  *lc;
   2213 
   2214     UNIT_INIT_CHECK(unit);
   2215 
   2216     lc = LS_CONTROL(unit);
   2217     switch (trigger_event) {
   2218         /* If trigger_event is BCM_LINKSCAN_TRIGGER_EVENT_REMOTE_FAULT, also reach this case
   2219          * In the enum definition,
   2220          * BCM_LINKSCAN_TRIGGER_EVENT_REMOTE_FAULT = BCM_LINKSCAN_TRIGGER_EVENT_FAULT
   2221          */
   2222         case BCM_LINKSCAN_TRIGGER_EVENT_FAULT:
   2223             *enable = lc->link_fault_chk_en;
   2224             break;
   2225         case BCM_LINKSCAN_TRIGGER_EVENT_LOCAL_FAULT:
   2226             *enable = lc->link_local_fault_chk_en;
   2227             break;
   2228         default:
   2229             return BCM_E_PARAM;
   2230     }
   2231     return BCM_E_NONE;
   2232 }
   2233 
   2234 #if defined(BROADCOM_DEBUG)
   2235 int
   2236 bcm_common_linkscan_dump(int unit)
   2237 {
   2238     _ls_handler_cb_t  *ent;
   2239 
   2240     if (LS_CONTROL(unit) == NULL) {
   2241         LOG_CLI((BSL_META_U(unit,
   2242                             "BCM linkscan not initialized for unit %d\n"), unit));
   2243         return BCM_E_PARAM;
   2244     }
   2245 
   2246     LOG_INFO(BSL_LS_BCM_LINK,
   2247              (BSL_META_U(unit,
   2248                          "BCM linkscan callbacks for unit %d\n"),
   2249               unit));
   2250     for (ent = LS_CONTROL(unit)->handler_cb; ent != NULL;
   2251          ent = ent->next) {
   2252 #if !defined(__PEDANTIC__)
   2253         LOG_INFO(BSL_LS_BCM_LINK,
   2254                  (BSL_META_U(unit,
   2255                              "    Fn %p\n"),
   2256                   (void *)ent->cb_f));
   2257 #else /* !defined(__PEDANTIC__) */
   2258         LOG_INFO(BSL_LS_BCM_LINK,
   2259                  (BSL_META_U(unit,
   2260                              "    Function pointer unprintable\n")));
   2261 #endif /* !defined(__PEDANTIC__) */
   2262     }
   2263 
   2264     return BCM_E_NONE;
   2265 }
   2266 #endif  /* BROADCOM_DEBUG */
   2267 
   2268 #if defined(BCM_CMICX_SUPPORT) || defined (BCM_DNXF_SUPPORT) || defined (BCM_DNX_SUPPORT)
   2269 /*
   2270  * Function:
   2271  *     cmicx_common_linkscan_hw_interrupt
   2272  * Purpose:
   2273  *     Link scan interrupt handler.
   2274  * Parameters:
   2275  *     unit - Device unit number
   2276  * Returns:
   2277  *     Nothing
   2278  * Notes:
   2279  *     Assumes: valid unit number.
   2280  */
   2281 void
   2282 cmicx_common_linkscan_hw_interrupt(int unit)
   2283 {
   2284     _ls_control_t  *lc = LS_CONTROL(unit);
   2285 
   2286     if ((NULL != lc) && (NULL != lc->sema)) {
   2287         lc->hw_change = 1;
   2288         sal_sem_give(lc->sema);
   2289     }
   2290     LOG_VERBOSE(BSL_LS_BCM_LINK,
   2291                 (BSL_META_U(unit,
   2292                             "Linkscan interrupt unit %d\n"), unit));
   2293 }
   2294 #endif /* BCM_CMICX_SUPPORT || BCM_DNXF_SUPPORT */