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 */