phy5690.c (31011B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * File: 8 * phy5690.c 9 * Purpose: 10 * Fiber driver for 5690 using internal QuadSerdes PHY. 11 * 12 * For BCM5690, the internal GMACC core of the GPIC should always be 13 * configured to run in SGMII mode. TBI mode is not supported between 14 * the GMACC and the internal SERDES module. 15 * 16 * When operating with an external PHY, this driver is not used. 17 * However the speed/duplex of the internal PHY must be programmed to 18 * match the MAC and external PHY settings so the data can pass through. 19 * This file supplies some routines to allow mac.c to accomplish this 20 * (think of the internal PHY as part of the MAC in this case): 21 * 22 * _phy_5690_notify_duplex 23 * _phy_5690_notify_speed 24 * _phy_5690_notify_stop 25 * _phy_5690_notify_resume 26 * 27 * CMIC MIIM operations can be performed to the internal register set 28 * using internal MDIO address (PORT_TO_PHY_ADDR_INT), and an external 29 * PHY register set (such as BCM5424/34/64) can be programmed using the 30 * external MDIO address (PORT_TO_PHY_ADDR). 31 * 32 * MDIO accesses to the internal PHY are not modeled on Quickturn. 33 */ 34 35 #include <sal/types.h> 36 #include <sal/core/spl.h> 37 #include <shared/bsl.h> 38 #include <soc/drv.h> 39 #include <soc/debug.h> 40 #include <soc/error.h> 41 #include <soc/phyreg.h> 42 43 #include <soc/phy.h> 44 #include <soc/phy/phyctrl.h> 45 #include <soc/phy/drv.h> 46 47 #include "phyident.h" 48 #include "phyreg.h" 49 #include "phynull.h" 50 #include "phy5690.h" 51 52 #if defined(BCM_5690) && defined(BCM_ESW_SUPPORT) 53 54 #define PHY_5690_AN_STATE_ACCESS_INTERVAL 60000 /* 60000 Usec */ 55 56 #define PHY_5690_AN_TIME(_pc) \ 57 ((_pc)->an_state_time) 58 #define PHY_5690_AN_VALID(_pc) \ 59 ((_pc)->an_state_valid) 60 #define PHY_5690_SGMII_STATE(_pc) \ 61 ((_pc)->sgmii_an_state) 62 63 #define PHY_5690_AN_RECORD_TIME(_pc) \ 64 ((_pc)->an_state_time) = sal_time_usecs() 65 #define PHY_5690_AN_RECORD_STATE(_pc, _state) \ 66 ((_pc)->sgmii_an_state) = (_state) 67 #define PHY_5690_AN_VALID_SET(_pc) \ 68 ((_pc)->an_state_valid) = TRUE 69 #define PHY_5690_AN_VALID_RESET(_pc) \ 70 ((_pc)->an_state_valid) = FALSE 71 72 #define PHY_5690_AN_ACCESS_TIMEOUT(_pc) \ 73 ((uint32)SAL_USECS_SUB(sal_time_usecs(), PHY_5690_AN_TIME((_pc))) \ 74 >= PHY_5690_AN_STATE_ACCESS_INTERVAL) 75 76 STATIC int 77 _phy_5690_notify_resume(int unit, soc_port_t port, uint32 data); 78 STATIC int 79 _phy_5690_notify_stop(int unit, soc_port_t port, uint32 data); 80 STATIC int 81 _phy_5690_notify_interface(int unit, soc_port_t port, uint32 data); 82 STATIC int 83 _phy_5690_notify_duplex(int unit, soc_port_t port, uint32 data); 84 STATIC int 85 _phy_5690_notify_speed(int unit, soc_port_t port, uint32 data); 86 87 /*********************************************************************** 88 * 89 * FIBER DRIVER ROUTINES 90 */ 91 92 /* 93 * Function: 94 * phy_5690_init 95 * Purpose: 96 * Initialize 5690 internal PHY driver 97 * Parameters: 98 * unit - StrataSwitch unit #. 99 * port - StrataSwitch port #. 100 * Returns: 101 * SOC_E_NONE 102 */ 103 104 STATIC int 105 phy_5690_init(int unit, soc_port_t port) 106 { 107 uint16 ctrl; 108 phy_ctrl_t *pc; 109 110 pc = INT_PHY_SW_STATE(unit, port); 111 LOG_INFO(BSL_LS_SOC_PHY, 112 (BSL_META_U(unit, 113 "phy_5690_init: u=%d p=%d\n"), 114 unit, port)); 115 116 if (soc_feature(unit, soc_feature_phy_5690_link_war)) { 117 PHY_5690_AN_VALID_RESET(pc); 118 } 119 120 /* 121 * The internal SERDES PHY's reset bit must be held high for at 122 * least 1 usec and is not self-clearing. 123 */ 124 125 SOC_IF_ERROR_RETURN 126 (READ_PHY_REG(unit, pc, 127 QS_MII_CTRL_REG, &ctrl)); 128 129 ctrl |= QS_MII_CTRL_RESET; 130 131 SOC_IF_ERROR_RETURN 132 (WRITE_PHY_REG(unit, pc, QS_MII_CTRL_REG, ctrl)); 133 134 sal_usleep(1); 135 136 ctrl = QS_MII_CTRL_SS_1000 | QS_MII_CTRL_FD; 137 138 SOC_IF_ERROR_RETURN 139 (WRITE_PHY_REG(unit, pc, QS_MII_CTRL_REG, ctrl)); 140 141 sal_usleep(10000); 142 143 SOC_IF_ERROR_RETURN 144 (READ_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, &ctrl)); 145 ctrl &= ~QS_SGMII_TBI_LOOP; 146 SOC_IF_ERROR_RETURN 147 (WRITE_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, ctrl)); 148 149 SOC_IF_ERROR_RETURN 150 (WRITE_PHY_REG(unit, pc, QS_SGMII_CTRL2_REG, 151 (PHY_COPPER_MODE(unit, port) ? 0 : QS_SGMII_FIBER_MODE))); 152 153 return SOC_E_NONE; 154 } 155 156 157 /* 158 * Function: 159 * _phy_5690_war_link_get 160 * Purpose: 161 * Perform Link get work-around 162 * Parameters: 163 * unit - StrataSwitch unit #. 164 * port - StrataSwitch port #. 165 * link - (OUT) Boolean, true indicates link established. 166 * Returns: 167 * SOC_E_XXX 168 * Notes: 169 * This is the workaround for GNATS 3711, QS_SGMII_STAT_REG does 170 * not properly reflect the link up/down state. However, it is 171 * possible to divine the true link state using the autonegotiation 172 * state debug register, in combination with its latching behavior 173 * and certain timing requirements. 174 */ 175 176 STATIC int 177 _phy_5690_war_link_get(int unit, soc_port_t port, int *link) 178 { 179 uint16 sgmii_an_state; 180 uint16 sgmii_misc_ctrl; 181 uint16 sgmii_stat; 182 phy_ctrl_t *pc; 183 184 pc = INT_PHY_SW_STATE(unit, port); 185 186 if ((PHY_5690_AN_ACCESS_TIMEOUT(pc)) || 187 (!PHY_5690_AN_VALID(pc))) { 188 SOC_IF_ERROR_RETURN 189 (READ_PHY_REG(unit, pc, QS_SGMII_STAT_REG, &sgmii_stat)); 190 191 sgmii_an_state = sgmii_stat & 0x0ff0; 192 193 *link = ((sgmii_an_state == 0x0770 || sgmii_an_state == 0x0880) && 194 PHY_5690_AN_VALID(pc)); 195 196 /* Get a snapshot of AN state into QS_SGMII_STAT_REG */ 197 198 if (!(*link)) { 199 SOC_IF_ERROR_RETURN 200 (READ_PHY_REG(unit, pc, 201 QS_SGMII_MISC_CTRL_REG, &sgmii_misc_ctrl)); 202 sgmii_misc_ctrl &= ~0x0010; 203 SOC_IF_ERROR_RETURN 204 (WRITE_PHY_REG(unit, pc, 205 QS_SGMII_MISC_CTRL_REG, sgmii_misc_ctrl)); 206 sgmii_misc_ctrl |= 0x0010; 207 SOC_IF_ERROR_RETURN 208 (WRITE_PHY_REG(unit, pc, 209 QS_SGMII_MISC_CTRL_REG, sgmii_misc_ctrl)); 210 sgmii_an_state = 0; 211 } 212 213 PHY_5690_AN_RECORD_TIME(pc); 214 PHY_5690_AN_RECORD_STATE(pc, sgmii_an_state); 215 PHY_5690_AN_VALID_SET(pc); 216 } else { 217 sgmii_an_state = PHY_5690_SGMII_STATE(pc); 218 219 *link = (sgmii_an_state == 0x0770 || sgmii_an_state == 0x0880); 220 221 /* Check if link went down */ 222 223 if (*link) { 224 SOC_IF_ERROR_RETURN 225 (READ_PHY_REG(unit, pc, 226 QS_SGMII_STAT_REG, &sgmii_stat)); 227 228 sgmii_an_state = sgmii_stat & 0x0ff0; 229 230 *link = (sgmii_an_state == 0x0770 || sgmii_an_state == 0x0880); 231 232 PHY_5690_AN_RECORD_STATE(pc, sgmii_an_state); 233 } 234 } 235 236 return SOC_E_NONE; 237 } 238 239 240 /* 241 * Function: 242 * phy_5690_link_get 243 * Purpose: 244 * Determine the current link up/down status 245 * Parameters: 246 * unit - StrataSwitch unit #. 247 * port - StrataSwitch port #. 248 * link - (OUT) Boolean, true indicates link established. 249 * Returns: 250 * SOC_E_XXX 251 * Notes: 252 * MII_STATUS bit 2 reflects link state. 253 * This bit gets set immediately when the link is plugged in, 254 * before autoneg completes. 255 * Energy control (MISC_CONTROL bit 3) is not used. 256 */ 257 258 STATIC int 259 phy_5690_link_get(int unit, soc_port_t port, int *link) 260 { 261 phy_ctrl_t *pc; 262 uint16 mii_stat; 263 264 pc = INT_PHY_SW_STATE(unit, port); 265 266 if (soc_feature(unit, soc_feature_phy_5690_link_war)) { 267 return (_phy_5690_war_link_get(unit, port, link)); 268 } 269 270 SOC_IF_ERROR_RETURN 271 (READ_PHY_REG(unit, pc, QS_MII_STAT_REG, &mii_stat)); 272 273 *link = ((mii_stat & QS_MII_STAT_LA) != 0); 274 275 276 return SOC_E_NONE; 277 } 278 279 /* 280 * Function: 281 * phy_5690_enable_set 282 * Purpose: 283 * Enable or disable the physical interface. 284 * Parameters: 285 * unit - StrataSwitch unit #. 286 * port - StrataSwitch port #. 287 * enable - Boolean, true = enable PHY, false = disable. 288 * Returns: 289 * SOC_E_XXX 290 * Notes: 291 * The PHY is held in reset when disabled. Also, the 292 * MISC_CONTROL.IDDQ bit must be set to 1 so the remote partner 293 * sees link down. 294 */ 295 296 STATIC int 297 phy_5690_enable_set(int unit, soc_port_t port, int enable) 298 { 299 phy_ctrl_t *pc; 300 uint16 misc_ctrl; 301 302 pc = INT_PHY_SW_STATE(unit, port); 303 LOG_INFO(BSL_LS_SOC_PHY, 304 (BSL_META_U(unit, 305 "phy_5690_enable_set: u=%d p=%d en=%d\n"), 306 unit, port, enable)); 307 308 if (enable) { 309 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE); 310 SOC_IF_ERROR_RETURN 311 (READ_PHY_REG(unit, pc, 312 QS_SGMII_MISC_CTRL_REG, &misc_ctrl)); 313 misc_ctrl &= ~QS_SGMII_IDDQ_MODE; 314 SOC_IF_ERROR_RETURN 315 (WRITE_PHY_REG(unit, pc, 316 QS_SGMII_MISC_CTRL_REG, misc_ctrl)); 317 SOC_IF_ERROR_RETURN 318 (_phy_5690_notify_resume(unit, port, PHY_STOP_PHY_DIS)); 319 } else { 320 PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE); 321 SOC_IF_ERROR_RETURN 322 (_phy_5690_notify_stop(unit, port, PHY_STOP_PHY_DIS)); 323 SOC_IF_ERROR_RETURN 324 (READ_PHY_REG(unit, pc, 325 QS_SGMII_MISC_CTRL_REG, &misc_ctrl)); 326 misc_ctrl |= QS_SGMII_IDDQ_MODE; 327 SOC_IF_ERROR_RETURN 328 (WRITE_PHY_REG(unit, pc, 329 QS_SGMII_MISC_CTRL_REG, misc_ctrl)); 330 } 331 332 return SOC_E_NONE; 333 } 334 335 /* 336 * Function: 337 * phy_5690_enable_get 338 * Purpose: 339 * Return physical interface enable/disable state. 340 * Parameters: 341 * unit - StrataSwitch unit #. 342 * port - StrataSwitch port #. 343 * enable - (OUT) Boolean, true = enable PHY, false = disable. 344 * Returns: 345 * SOC_E_XXX 346 */ 347 348 STATIC int 349 phy_5690_enable_get(int unit, soc_port_t port, int *enable) 350 { 351 *enable = !PHY_FLAGS_TST(unit, port, PHY_FLAGS_DISABLE); 352 353 return(SOC_E_NONE); 354 } 355 356 /* 357 * Function: 358 * phy_5690_duplex_set 359 * Purpose: 360 * Set the current duplex mode (forced). 361 * Parameters: 362 * unit - StrataSwitch unit #. 363 * port - StrataSwitch port #. 364 * duplex - Boolean, TRUE indicates full duplex, FALSE indicates half. 365 * Returns: 366 * SOC_E_XXX 367 */ 368 369 STATIC int 370 phy_5690_duplex_set(int unit, soc_port_t port, int duplex) 371 { 372 COMPILER_REFERENCE(unit); 373 COMPILER_REFERENCE(port); 374 375 return (duplex ? SOC_E_NONE : SOC_E_UNAVAIL); 376 } 377 378 /* 379 * Function: 380 * phy_5690_duplex_get 381 * Purpose: 382 * Get the current operating duplex mode. If autoneg is enabled, 383 * then operating mode is returned, otherwise forced mode is returned. 384 * Parameters: 385 * unit - StrataSwitch unit #. 386 * port - StrataSwitch port #. 387 * duplex - (OUT) Boolean, true indicates full duplex, false 388 * indicates half. 389 * Returns: 390 * SOC_E_XXX 391 */ 392 393 STATIC int 394 phy_5690_duplex_get(int unit, soc_port_t port, int *duplex) 395 { 396 COMPILER_REFERENCE(unit); 397 COMPILER_REFERENCE(port); 398 399 *duplex = TRUE; 400 401 return SOC_E_NONE; 402 } 403 404 /* 405 * Function: 406 * phy_5690_speed_set 407 * Purpose: 408 * Set the current operating speed (forced). 409 * Parameters: 410 * unit - StrataSwitch unit #. 411 * port - StrataSwitch port #. 412 * duplex - (OUT) Boolean, true indicates full duplex, false 413 * indicates half. 414 * Returns: 415 * SOC_E_XXX 416 */ 417 418 STATIC int 419 phy_5690_speed_set(int unit, soc_port_t port, int speed) 420 { 421 COMPILER_REFERENCE(unit); 422 COMPILER_REFERENCE(port); 423 424 if (speed == 0 || speed == 1000) { 425 return SOC_E_NONE; 426 } 427 428 return SOC_E_CONFIG; 429 } 430 431 /* 432 * Function: 433 * phy_5690_speed_get 434 * Purpose: 435 * Get the current operating speed. If autoneg is enabled, 436 * then operating mode is returned, otherwise forced mode is returned. 437 * Parameters: 438 * unit - StrataSwitch unit #. 439 * port - StrataSwitch port #. 440 * duplex - (OUT) Boolean, true indicates full duplex, false 441 * indicates half. 442 * Returns: 443 * SOC_E_XXX 444 */ 445 446 STATIC int 447 phy_5690_speed_get(int unit, soc_port_t port, int *speed) 448 { 449 *speed = 1000; 450 451 return SOC_E_NONE; 452 } 453 454 /* 455 * Function: 456 * phy_5690_an_set 457 * Purpose: 458 * Enable or disabled auto-negotiation on the specified port. 459 * Parameters: 460 * unit - StrataSwitch unit #. 461 * port - StrataSwitch port #. 462 * an - Boolean, if true, auto-negotiation is enabled 463 * (and/or restarted). If false, autonegotiation is disabled. 464 * Returns: 465 * SOC_E_XXX 466 */ 467 468 STATIC int 469 phy_5690_an_set(int unit, soc_port_t port, int an) 470 { 471 phy_ctrl_t *pc; 472 uint16 sgmii_ctrl, misc_ctrl; 473 474 pc = INT_PHY_SW_STATE(unit, port); 475 SOC_IF_ERROR_RETURN 476 (READ_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, &sgmii_ctrl)); 477 SOC_IF_ERROR_RETURN 478 (READ_PHY_REG(unit, pc, QS_SGMII_MISC_CTRL_REG, &misc_ctrl)); 479 480 /* 481 * Special case for BCM5421S being used in SGMII copper mode with 482 * BCM5690. Always have autoneg on. 483 */ 484 485 if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_5421S)) { 486 an = 1; 487 } 488 489 if (an) { 490 sgmii_ctrl |= QS_SGMII_AN_SEL; /* IEEE clause 37 compliant */ 491 sgmii_ctrl &= ~QS_SGMII_AN_DISABLE; 492 misc_ctrl |= QS_SGMII_MAC_MODE; 493 } else { 494 sgmii_ctrl &= ~QS_SGMII_AN_SEL; 495 sgmii_ctrl |= QS_SGMII_AN_DISABLE; 496 misc_ctrl &= ~QS_SGMII_MAC_MODE; 497 } 498 499 SOC_IF_ERROR_RETURN 500 (WRITE_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, sgmii_ctrl)); 501 SOC_IF_ERROR_RETURN 502 (WRITE_PHY_REG(unit, pc, QS_SGMII_MISC_CTRL_REG, misc_ctrl)); 503 504 if (an) { 505 /* Restart autonegotiation (not a self-clearing bit) */ 506 507 SOC_IF_ERROR_RETURN 508 (WRITE_PHY_REG(unit, pc, 509 QS_SGMII_MISC_REG, QS_SGMII_ANA_RESTART)); 510 SOC_IF_ERROR_RETURN 511 (WRITE_PHY_REG(unit, pc, 512 QS_SGMII_MISC_REG, 0)); 513 } 514 515 return SOC_E_NONE; 516 } 517 518 /* 519 * Function: 520 * phy_5690_an_get 521 * Purpose: 522 * Get the current auto-negotiation status (enabled/busy) 523 * Parameters: 524 * unit - StrataSwitch unit #. 525 * port - StrataSwitch port #. 526 * an - (OUT) if true, auto-negotiation is enabled. 527 * an_done - (OUT) if true, auto-negotiation is complete. 528 * This value is undefined if an == false. 529 * Returns: 530 * SOC_E_XXX 531 */ 532 533 STATIC int 534 phy_5690_an_get(int unit, soc_port_t port, int *an, int *an_done) 535 { 536 phy_ctrl_t *pc; 537 uint16 sgmii_ctrl, sgmii_stat; 538 539 pc = INT_PHY_SW_STATE(unit, port); 540 541 SOC_IF_ERROR_RETURN 542 (READ_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, &sgmii_ctrl)); 543 544 *an = (sgmii_ctrl & QS_SGMII_AN_DISABLE) == 0; 545 546 SOC_IF_ERROR_RETURN 547 (READ_PHY_REG(unit, pc, QS_SGMII_STAT_REG, &sgmii_stat)); 548 549 *an_done = (sgmii_stat & QS_SGMII_ANA_COMPLETE) != 0; 550 551 return SOC_E_NONE; 552 } 553 554 /* 555 * Function: 556 * phy_5690_adv_local_set 557 * Purpose: 558 * Set the current advertisement for auto-negotiation. 559 * Parameters: 560 * unit - StrataSwitch unit #. 561 * port - StrataSwitch port #. 562 * mode - Port mode mask indicating supported options/speeds. 563 * Returns: 564 * SOC_E_XXX 565 */ 566 567 STATIC int 568 phy_5690_adv_local_set(int unit, soc_port_t port, soc_port_mode_t mode) 569 { 570 uint16 an_adv, sgmii_ctrl; 571 phy_ctrl_t *pc; 572 573 pc = INT_PHY_SW_STATE(unit, port); 574 575 mode &= SOC_PM_1000MB_FD | SOC_PM_PAUSE; 576 577 /* 578 * Set advertised duplex (only FD supported). 579 */ 580 581 SOC_IF_ERROR_RETURN 582 (READ_PHY_REG(unit, pc, QS_ANA_REG, &an_adv)); 583 584 an_adv &= ~(QS_MII_ANA_FD | QS_MII_ANA_HD); 585 586 if (mode & SOC_PM_1000MB_FD) { 587 an_adv |= QS_MII_ANA_FD; 588 } 589 590 SOC_IF_ERROR_RETURN 591 (WRITE_PHY_REG(unit, pc, QS_ANA_REG, an_adv)); 592 593 /* 594 * Set advertised pause bits in link code word. 595 */ 596 597 SOC_IF_ERROR_RETURN 598 (READ_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, &sgmii_ctrl)); 599 600 sgmii_ctrl &= ~(QS_SGMII_PAUSE_SYM | QS_SGMII_PAUSE_ASYM); 601 602 switch (mode & SOC_PM_PAUSE) { 603 case SOC_PM_PAUSE_TX: 604 sgmii_ctrl |= QS_SGMII_PAUSE_ASYM; 605 break; 606 case SOC_PM_PAUSE_RX: 607 sgmii_ctrl |= QS_SGMII_PAUSE_SYM | QS_SGMII_PAUSE_ASYM; 608 break; 609 case SOC_PM_PAUSE_TX | SOC_PM_PAUSE_RX: 610 sgmii_ctrl |= QS_SGMII_PAUSE_SYM; 611 break; 612 } 613 614 SOC_IF_ERROR_RETURN 615 (WRITE_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, sgmii_ctrl)); 616 617 return SOC_E_NONE; 618 } 619 620 /* 621 * Function: 622 * phy_5690_adv_local_get 623 * Purpose: 624 * Get the current advertisement for auto-negotiation. 625 * Parameters: 626 * unit - StrataSwitch unit #. 627 * port - StrataSwitch port #. 628 * mode - (OUT) Port mode mask indicating supported options/speeds. 629 * Returns: 630 * SOC_E_XXX 631 */ 632 633 STATIC int 634 phy_5690_adv_local_get(int unit, soc_port_t port, soc_port_mode_t *mode) 635 { 636 uint16 an_adv, sgmii_ctrl; 637 phy_ctrl_t *pc; 638 639 pc = INT_PHY_SW_STATE(unit, port); 640 641 *mode = 0; 642 643 SOC_IF_ERROR_RETURN 644 (READ_PHY_REG(unit, pc, QS_ANA_REG, &an_adv)); 645 646 if (an_adv & QS_MII_ANA_FD) { 647 *mode |= SOC_PM_1000MB_FD; 648 } 649 650 SOC_IF_ERROR_RETURN 651 (READ_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, &sgmii_ctrl)); 652 653 switch (sgmii_ctrl & (QS_SGMII_PAUSE_SYM | QS_SGMII_PAUSE_ASYM)) { 654 case QS_SGMII_PAUSE_SYM: 655 *mode |= SOC_PM_PAUSE_TX | SOC_PM_PAUSE_RX; 656 break; 657 case QS_SGMII_PAUSE_ASYM: 658 *mode |= SOC_PM_PAUSE_TX; 659 break; 660 case QS_SGMII_PAUSE_SYM | QS_SGMII_PAUSE_ASYM: 661 *mode |= SOC_PM_PAUSE_RX; 662 break; 663 } 664 665 return SOC_E_NONE; 666 } 667 668 /* 669 * Function: 670 * phy_5690_adv_remote_get 671 * Purpose: 672 * Get partner's current advertisement for auto-negotiation. 673 * Parameters: 674 * unit - StrataSwitch unit #. 675 * port - StrataSwitch port #. 676 * mode - (OUT) Port mode mask indicating supported options/speeds. 677 * Returns: 678 * SOC_E_XXX 679 */ 680 681 STATIC int 682 phy_5690_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode) 683 { 684 phy_ctrl_t *pc; 685 uint16 anlpa; 686 687 pc = INT_PHY_SW_STATE(unit, port); 688 689 *mode = 0; 690 691 SOC_IF_ERROR_RETURN 692 (READ_PHY_REG(unit, pc, QS_ANP_REG, &anlpa)); 693 694 if (anlpa & QS_MII_ANP_FIBER_FD) { 695 *mode |= SOC_PM_1000MB_FD; 696 } 697 698 if (anlpa & QS_MII_ANP_FIBER_HD) { 699 *mode |= SOC_PM_1000MB_HD; 700 } 701 702 switch (anlpa & 703 (QS_MII_ANP_FIBER_PAUSE_SYM | QS_MII_ANP_FIBER_PAUSE_ASYM)) { 704 case QS_MII_ANP_FIBER_PAUSE_SYM: 705 *mode |= SOC_PM_PAUSE_TX | SOC_PM_PAUSE_RX; 706 break; 707 case QS_MII_ANP_FIBER_PAUSE_ASYM: 708 *mode |= SOC_PM_PAUSE_TX; 709 break; 710 case QS_MII_ANP_FIBER_PAUSE_SYM | QS_MII_ANP_FIBER_PAUSE_ASYM: 711 *mode |= SOC_PM_PAUSE_RX; 712 break; 713 } 714 715 return SOC_E_NONE; 716 } 717 718 /* 719 * Function: 720 * phy_5690_master_set 721 * Purpose: 722 * Configure PHY master mode 723 * Parameters: 724 * unit - StrataSwitch unit #. 725 * port - StrataSwitch port #. 726 * master - PHY master mode. 727 * Returns: 728 * SOC_E_UNAVAIL 729 */ 730 STATIC int 731 phy_5690_master_set(int unit, soc_port_t port, int master) 732 { 733 COMPILER_REFERENCE(unit); 734 COMPILER_REFERENCE(port); 735 736 if (master == SOC_PORT_MS_NONE) { 737 return SOC_E_NONE; 738 } 739 return SOC_E_PARAM; 740 } 741 742 /* 743 * Function: 744 * phy_5690_master_get 745 * Purpose: 746 * Get current master mode setting 747 * Parameters: 748 * unit - StrataSwitch unit #. 749 * port - StrataSwitch port #. 750 * master - (OUT) BCM_PORT_MS_NONE 751 * Returns: 752 * SOC_E_NONE 753 */ 754 STATIC int 755 phy_5690_master_get(int unit, soc_port_t port, int *master) 756 { 757 *master = SOC_PORT_MS_NONE; 758 return SOC_E_NONE; 759 } 760 761 /* 762 * Function: 763 * phy_5690_lb_set 764 * Purpose: 765 * Set the internal PHY loopback mode. 766 * Parameters: 767 * unit - StrataSwitch unit #. 768 * port - StrataSwitch port #. 769 * loopback - Boolean: true = enable loopback, false = disable. 770 * Returns: 771 * SOC_E_XXX 772 */ 773 774 STATIC int 775 phy_5690_lb_set(int unit, soc_port_t port, int enable) 776 { 777 uint16 sgmii_ctrl; 778 phy_ctrl_t *pc; 779 780 pc = INT_PHY_SW_STATE(unit, port); 781 782 SOC_IF_ERROR_RETURN 783 (READ_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, &sgmii_ctrl)); 784 785 if (enable) { 786 sgmii_ctrl |= QS_SGMII_TBI_LOOP | QS_SGMII_REV_PHASE; 787 } else { 788 sgmii_ctrl &= ~(QS_SGMII_TBI_LOOP | QS_SGMII_REV_PHASE); 789 } 790 791 SOC_IF_ERROR_RETURN 792 (WRITE_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, sgmii_ctrl)); 793 794 return SOC_E_NONE; 795 } 796 797 /* 798 * Function: 799 * phy_5690_lb_get 800 * Purpose: 801 * Get the local PHY loopback mode. 802 * Parameters: 803 * unit - StrataSwitch unit #. 804 * port - StrataSwitch port #. 805 * loopback - (OUT) Boolean: true = enable loopback, false = disable. 806 * Returns: 807 * SOC_E_XXX 808 */ 809 810 STATIC int 811 phy_5690_lb_get(int unit, soc_port_t port, int *enable) 812 { 813 uint16 sgmii_ctrl; 814 phy_ctrl_t *pc; 815 816 pc = INT_PHY_SW_STATE(unit, port); 817 818 SOC_IF_ERROR_RETURN 819 (READ_PHY_REG(unit, pc, QS_SGMII_CTRL_REG, &sgmii_ctrl)); 820 821 *enable = (sgmii_ctrl & QS_SGMII_TBI_LOOP) != 0; 822 823 return SOC_E_NONE; 824 } 825 826 /* 827 * Function: 828 * phy_5690_interface_set 829 * Purpose: 830 * Set the current operating mode of the internal PHY. 831 * (Pertaining to the MAC/PHY interface, not the line interface). 832 * Parameters: 833 * unit - StrataSwitch unit #. 834 * port - StrataSwitch port #. 835 * pif - one of SOC_PORT_IF_* 836 * Returns: 837 * SOC_E_XXX 838 */ 839 840 STATIC int 841 phy_5690_interface_set(int unit, soc_port_t port, soc_port_if_t pif) 842 { 843 int rv; 844 845 switch (pif) { 846 case SOC_PORT_IF_MII: 847 case SOC_PORT_IF_GMII: 848 case SOC_PORT_IF_SGMII: 849 case SOC_PORT_IF_NOCXN: 850 rv = SOC_E_NONE; 851 break; 852 default: 853 rv = SOC_E_UNAVAIL; 854 break; 855 } 856 857 LOG_INFO(BSL_LS_SOC_PHY, 858 (BSL_META_U(unit, 859 "phy_5690_interface_set: u=%d p=%d pif=%d rv=%d\n"), 860 unit, port, pif, rv)); 861 862 return rv; 863 } 864 865 /* 866 * Function: 867 * phy_5690_interface_get 868 * Purpose: 869 * Get the current operating mode of the internal PHY. 870 * (Pertaining to the MAC/PHY interface, not the line interface). 871 * Parameters: 872 * unit - StrataSwitch unit #. 873 * port - StrataSwitch port #. 874 * pif - (OUT) one of SOC_PORT_IF_* 875 * Returns: 876 * SOC_E_XXX 877 */ 878 879 STATIC int 880 phy_5690_interface_get(int unit, soc_port_t port, soc_port_if_t *pif) 881 { 882 *pif = SOC_PORT_IF_GMII; 883 884 return(SOC_E_NONE); 885 } 886 887 /* 888 * Function: 889 * phy_5690_ability_get 890 * Purpose: 891 * Get the abilities of the internal PHY. 892 * Parameters: 893 * unit - StrataSwitch unit #. 894 * port - StrataSwitch port #. 895 * mode - (OUT) Port mode mask indicating supported options/speeds. 896 * Returns: 897 * SOC_E_XXX 898 */ 899 900 STATIC int 901 phy_5690_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode) 902 { 903 uint16 serdes_mode; 904 phy_ctrl_t *pc; 905 906 pc = INT_PHY_SW_STATE(unit, port); 907 908 SOC_IF_ERROR_RETURN 909 (READ_PHY_REG(unit, pc, QS_SGMII_CTRL2_REG, &serdes_mode)); 910 911 if (serdes_mode & QS_SGMII_FIBER_MODE) { 912 *mode = (SOC_PM_1000MB_FD | SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM | 913 SOC_PM_AN | SOC_PM_LB_PHY | SOC_PM_GMII); 914 } else { 915 *mode = (SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB_FD | 916 SOC_PM_LB_PHY | SOC_PM_GMII); 917 } 918 919 return SOC_E_NONE; 920 } 921 922 STATIC int 923 phy_5690_notify(int unit, soc_port_t port, 924 soc_phy_event_t event, uint32 value) 925 { 926 int rv; 927 928 if (event >= phyEventCount) { 929 return SOC_E_PARAM; 930 } 931 932 rv = SOC_E_NONE; 933 switch(event) { 934 case phyEventInterface: 935 rv = (_phy_5690_notify_interface(unit, port, value)); 936 break; 937 case phyEventDuplex: 938 rv = (_phy_5690_notify_duplex(unit, port, value)); 939 break; 940 case phyEventSpeed: 941 rv = (_phy_5690_notify_speed(unit, port, value)); 942 break; 943 case phyEventStop: 944 rv = (_phy_5690_notify_stop(unit, port, value)); 945 break; 946 case phyEventResume: 947 rv = (_phy_5690_notify_resume(unit, port, value)); 948 break; 949 default: 950 rv = SOC_E_PARAM; 951 } 952 953 return rv; 954 } 955 956 /* 957 * Variable: phy_5690drv_ge 958 * Purpose: PHY Driver for Quad Serdes PHY internal to Draco, 959 */ 960 961 phy_driver_t phy_5690drv_ge = { 962 "BCM5690 Internal SERDES PHY Driver", 963 phy_5690_init, 964 phy_null_reset, 965 phy_5690_link_get, 966 phy_5690_enable_set, 967 phy_5690_enable_get, 968 phy_5690_duplex_set, 969 phy_5690_duplex_get, 970 phy_5690_speed_set, 971 phy_5690_speed_get, 972 phy_5690_master_set, 973 phy_5690_master_get, 974 phy_5690_an_set, 975 phy_5690_an_get, 976 phy_5690_adv_local_set, 977 phy_5690_adv_local_get, 978 phy_5690_adv_remote_get, 979 phy_5690_lb_set, 980 phy_5690_lb_get, 981 phy_5690_interface_set, 982 phy_5690_interface_get, 983 phy_5690_ability_get, 984 NULL, 985 NULL, 986 phy_null_mdix_set, 987 phy_null_mdix_get, 988 phy_null_mdix_status_get, 989 NULL, 990 NULL, 991 phy_null_medium_get, 992 NULL, /* phy_cable_diag */ 993 NULL, /* phy_link_change */ 994 NULL, /* phy_control_set */ 995 NULL, /* phy_control_get */ 996 NULL, /* phy_reg_read */ 997 NULL, /* phy_reg_write */ 998 NULL, /* phy_reg_modify */ 999 phy_5690_notify 1000 }; 1001 1002 /*********************************************************************** 1003 * 1004 * PASS-THROUGH NOTIFY ROUTINES 1005 */ 1006 1007 /* 1008 * Function: 1009 * _phy_5690_notify_duplex 1010 * Purpose: 1011 * Program duplex if (and only if) 5690 is an intermediate PHY. 1012 * Parameters: 1013 * unit - StrataSwitch unit #. 1014 * port - StrataSwitch port #. 1015 * duplex - Boolean, TRUE indicates full duplex, FALSE 1016 * indicates half. 1017 * Returns: 1018 * SOC_E_XXX 1019 * Notes: 1020 * If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to 1021 * talk directly to an external fiber module. 1022 * 1023 * If PHY_FLAGS_FIBER is clear, the PHY is being used in 1024 * pass-through mode to talk to an external SGMII PHY. 1025 * 1026 * When used in pass-through mode, autoneg must be turned off and 1027 * the speed/duplex forced to match that of the external PHY. 1028 */ 1029 1030 STATIC int 1031 _phy_5690_notify_duplex(int unit, soc_port_t port, uint32 duplex) 1032 { 1033 int fiber = PHY_FLAGS_TST(unit, port, PHY_FLAGS_FIBER); 1034 uint16 mii_ctrl; 1035 phy_ctrl_t *pc; 1036 1037 pc = INT_PHY_SW_STATE(unit, port); 1038 1039 LOG_INFO(BSL_LS_SOC_PHY, 1040 (BSL_META_U(unit, 1041 "_phy_5690_notify_duplex: u=%d p=%d duplex=%d fiber=%d\n"), 1042 unit, port, duplex, fiber)); 1043 1044 if (SAL_BOOT_SIMULATION) { 1045 return SOC_E_NONE; 1046 } 1047 1048 if (fiber) { 1049 SOC_IF_ERROR_RETURN 1050 (READ_PHY_REG(unit, pc, QS_MII_CTRL_REG, &mii_ctrl)); 1051 1052 if (!(mii_ctrl & QS_MII_CTRL_FD)) { 1053 mii_ctrl |= QS_MII_CTRL_FD; 1054 SOC_IF_ERROR_RETURN 1055 (WRITE_PHY_REG(unit, pc, QS_MII_CTRL_REG, mii_ctrl)); 1056 } 1057 1058 return SOC_E_NONE; 1059 } 1060 1061 /* Put SERDES PHY in reset */ 1062 1063 SOC_IF_ERROR_RETURN 1064 (_phy_5690_notify_stop(unit, port, PHY_STOP_DUPLEX_CHG)); 1065 1066 /* Update duplexity */ 1067 1068 SOC_IF_ERROR_RETURN 1069 (READ_PHY_REG(unit, pc, QS_MII_CTRL_REG, &mii_ctrl)); 1070 1071 if (duplex) { 1072 mii_ctrl |= QS_MII_CTRL_FD; 1073 } else { 1074 mii_ctrl &= ~QS_MII_CTRL_FD; 1075 } 1076 1077 SOC_IF_ERROR_RETURN 1078 (WRITE_PHY_REG(unit, pc, QS_MII_CTRL_REG, mii_ctrl)); 1079 1080 /* Take SERDES PHY out of reset */ 1081 1082 SOC_IF_ERROR_RETURN 1083 (_phy_5690_notify_resume(unit, port, PHY_STOP_DUPLEX_CHG)); 1084 1085 /* Autonegotiation must be turned off to talk to external PHY */ 1086 1087 SOC_IF_ERROR_RETURN 1088 (phy_5690_an_set(unit, port, FALSE)); 1089 1090 return SOC_E_NONE; 1091 } 1092 1093 /* 1094 * Function: 1095 * _phy_5690_notify_speed 1096 * Purpose: 1097 * Program duplex if (and only if) 5690 is an intermediate PHY. 1098 * Parameters: 1099 * unit - StrataSwitch unit #. 1100 * port - StrataSwitch port #. 1101 * speed - Speed to program. 1102 * Returns: 1103 * SOC_E_XXX 1104 * Notes: 1105 * If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to 1106 * talk directly to an external fiber module. 1107 * 1108 * If PHY_FLAGS_FIBER is clear, the PHY is being used in 1109 * pass-through mode to talk to an external SGMII PHY. 1110 * 1111 * When used in pass-through mode, autoneg must be turned off and 1112 * the speed/duplex forced to match that of the external PHY. 1113 */ 1114 1115 STATIC int 1116 _phy_5690_notify_speed(int unit, soc_port_t port, uint32 speed) 1117 { 1118 int fiber = PHY_FLAGS_TST(unit, port, PHY_FLAGS_FIBER); 1119 uint16 mii_ctrl; 1120 int rv = SOC_E_NONE; 1121 phy_ctrl_t *pc; 1122 1123 pc = INT_PHY_SW_STATE(unit, port); 1124 1125 LOG_INFO(BSL_LS_SOC_PHY, 1126 (BSL_META_U(unit, 1127 "_phy_5690_notify_speed: u=%d p=%d speed=%d fiber=%d\n"), 1128 unit, port, speed, fiber)); 1129 1130 if (SAL_BOOT_SIMULATION) { 1131 return SOC_E_NONE; 1132 } 1133 1134 if (fiber) { 1135 SOC_IF_ERROR_RETURN 1136 (READ_PHY_REG(unit, pc, QS_MII_CTRL_REG, &mii_ctrl)); 1137 1138 if ((mii_ctrl & (QS_MII_CTRL_SS_LSB | QS_MII_CTRL_SS_MSB)) 1139 != QS_MII_CTRL_SS_1000) { 1140 mii_ctrl &= ~(QS_MII_CTRL_SS_LSB | QS_MII_CTRL_SS_MSB); 1141 mii_ctrl |= QS_MII_CTRL_SS_1000; 1142 SOC_IF_ERROR_RETURN 1143 (WRITE_PHY_REG(unit, pc, MII_CTRL_REG, mii_ctrl)); 1144 } 1145 return rv; 1146 } 1147 1148 /* Put SERDES PHY in reset */ 1149 1150 SOC_IF_ERROR_RETURN 1151 (_phy_5690_notify_stop(unit, port, PHY_STOP_SPEED_CHG)); 1152 1153 /* Update speed */ 1154 1155 SOC_IF_ERROR_RETURN 1156 (READ_PHY_REG(unit, pc, QS_MII_CTRL_REG, &mii_ctrl)); 1157 1158 mii_ctrl &= ~(QS_MII_CTRL_SS_LSB | QS_MII_CTRL_SS_MSB); 1159 1160 switch (speed) { 1161 case 10: 1162 mii_ctrl |= QS_MII_CTRL_SS_10; 1163 break; 1164 case 100: 1165 mii_ctrl |= QS_MII_CTRL_SS_100; 1166 break; 1167 case 0: 1168 case 1000: 1169 mii_ctrl |= QS_MII_CTRL_SS_1000; 1170 break; 1171 default: 1172 rv = SOC_E_CONFIG; 1173 break; 1174 } 1175 1176 SOC_IF_ERROR_RETURN 1177 (WRITE_PHY_REG(unit, pc, QS_MII_CTRL_REG, mii_ctrl)); 1178 1179 /* Take SERDES PHY out of reset */ 1180 1181 SOC_IF_ERROR_RETURN 1182 (_phy_5690_notify_resume(unit, port, PHY_STOP_SPEED_CHG)); 1183 1184 /* Autonegotiation must be turned off to talk to external PHY */ 1185 1186 SOC_IF_ERROR_RETURN 1187 (phy_5690_an_set(unit, port, FALSE)); 1188 1189 return rv; 1190 } 1191 1192 /* 1193 * Function: 1194 * _phy_5690_stop 1195 * Purpose: 1196 * Put 5690 SERDES in or out of reset depending on conditions 1197 */ 1198 1199 STATIC int 1200 _phy_5690_stop(int unit, soc_port_t port) 1201 { 1202 uint16 mii_ctrl; 1203 int stop, copper; 1204 phy_ctrl_t *pc; 1205 1206 pc = INT_PHY_SW_STATE(unit, port); 1207 1208 copper = (pc->stop & 1209 PHY_STOP_COPPER) != 0; 1210 1211 stop = ((pc->stop & 1212 (PHY_STOP_PHY_DIS | 1213 PHY_STOP_DRAIN)) != 0 || 1214 (copper && 1215 (pc->stop & 1216 (PHY_STOP_MAC_DIS | 1217 PHY_STOP_DUPLEX_CHG | 1218 PHY_STOP_SPEED_CHG)) != 0)); 1219 1220 LOG_INFO(BSL_LS_SOC_PHY, 1221 (BSL_META_U(unit, 1222 "phy_5690_stop: u=%d p=%d copper=%d stop=%d flg=0x%x\n"), 1223 unit, port, copper, stop, 1224 pc->stop)); 1225 1226 SOC_IF_ERROR_RETURN 1227 (READ_PHY_REG(unit, pc, QS_MII_CTRL_REG, &mii_ctrl)); 1228 1229 if (stop) { 1230 mii_ctrl |= MII_CTRL_RESET; 1231 } else { 1232 mii_ctrl &= ~MII_CTRL_RESET; 1233 } 1234 1235 SOC_IF_ERROR_RETURN 1236 (WRITE_PHY_REG(unit, pc, QS_MII_CTRL_REG, mii_ctrl)); 1237 1238 return SOC_E_NONE; 1239 } 1240 1241 /* 1242 * Function: 1243 * _phy_5690_notify_stop 1244 * Purpose: 1245 * Add a reason to put 5690 PHY in reset. 1246 * Parameters: 1247 * unit - StrataSwitch unit #. 1248 * port - StrataSwitch port #. 1249 * flags - Reason to stop 1250 * Returns: 1251 * SOC_E_XXX 1252 */ 1253 1254 STATIC int 1255 _phy_5690_notify_stop(int unit, soc_port_t port, uint32 flags) 1256 { 1257 phy_ctrl_t *pc; 1258 1259 pc = INT_PHY_SW_STATE(unit, port); 1260 pc->stop |= flags; 1261 1262 return _phy_5690_stop(unit, port); 1263 } 1264 1265 /* 1266 * Function: 1267 * _phy_5690_notify_resume 1268 * Purpose: 1269 * Remove a reason to put 5690 PHY in reset. 1270 * Parameters: 1271 * unit - StrataSwitch unit #. 1272 * port - StrataSwitch port #. 1273 * flags - Reason to stop 1274 * Returns: 1275 * SOC_E_XXX 1276 */ 1277 1278 STATIC int 1279 _phy_5690_notify_resume(int unit, soc_port_t port, uint32 flags) 1280 { 1281 phy_ctrl_t *pc; 1282 1283 pc = INT_PHY_SW_STATE(unit, port); 1284 pc->stop &= ~flags; 1285 1286 return _phy_5690_stop(unit, port); 1287 } 1288 1289 STATIC int 1290 _phy_5690_notify_interface(int unit, soc_port_t port, uint32 flags) 1291 { 1292 uint16 mode; 1293 phy_ctrl_t *pc; 1294 1295 pc = INT_PHY_SW_STATE(unit, port); 1296 1297 mode = (flags == SOC_PORT_IF_SGMII) ? 0 : QS_SGMII_FIBER_MODE; 1298 1299 SOC_IF_ERROR_RETURN 1300 (WRITE_PHY_REG(unit, pc, QS_SGMII_CTRL2_REG, mode)); 1301 1302 return SOC_E_NONE; 1303 } 1304 1305 #endif /* BCM_5690 */ 1306 1307 int _soc_phy_5690_not_empty;