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