phy53xxx.c (30659B)
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: serdes.c 8 * Purpose: 9 * Fiber driver for 53xxx using internal QuadSerdes PHY. 10 */ 11 #include <sal/types.h> 12 #include <sal/core/spl.h> 13 #include <shared/bsl.h> 14 #include <soc/drv.h> 15 #include <soc/debug.h> 16 #include <soc/error.h> 17 #include <soc/phyreg.h> 18 19 #include <soc/phy.h> 20 #include <soc/phy/phyctrl.h> 21 #include <soc/phy/drv.h> 22 23 #include "phydefs.h" /* Must include before other phy related includes */ 24 25 #if defined(INCLUDE_PHY_53XXX) 26 #include "phyconfig.h" /* Must be the first phy include after phydefs.h */ 27 28 #include "phyident.h" 29 #include "phyreg.h" 30 #include "phynull.h" 31 #include "serdes.h" 32 #include "phy53xxx.h" 33 #define ADVERT_ALL_FIBER \ 34 (SOC_PM_PAUSE | SOC_PM_1000MB_FD) 35 36 37 /* Notify event forward declaration */ 38 STATIC int _phy_53xxx_notify_duplex(int unit, soc_port_t port, uint32 duplex); 39 STATIC int _phy_53xxx_notify_speed(int unit, soc_port_t port, uint32 speed); 40 STATIC int _phy_53xxx_notify_stop(int unit, soc_port_t port, uint32 flags); 41 STATIC int _phy_53xxx_notify_resume(int unit, soc_port_t port, uint32 flags); 42 STATIC int _phy_53xxx_notify_interface(int unit, soc_port_t port, uint32 intf); 43 44 /*********************************************************************** 45 * 46 * FIBER DRIVER ROUTINES 47 */ 48 STATIC int 49 phy_53xxx_init_no_reset(int unit, soc_port_t port) 50 { 51 uint16 data16, mask16; 52 soc_timeout_t to; 53 phy_ctrl_t *pc; 54 int rv; 55 56 pc = INT_PHY_SW_STATE(unit, port); 57 pc->fiber.enable = PHY_FIBER_MODE(unit, port); 58 pc->fiber.preferred = PHY_FIBER_MODE(unit, port); 59 pc->fiber.autoneg_enable = TRUE; 60 pc->fiber.autoneg_advert = ADVERT_ALL_FIBER; 61 pc->fiber.force_speed = 1000; 62 pc->fiber.force_duplex = TRUE; 63 pc->fiber.master = SOC_PORT_MS_NONE; 64 pc->fiber.mdix = SOC_PORT_MDIX_NORMAL; 65 66 /* Since phy53xxx driver reset function vector does not reset 67 * the SerDes, reset the SerDes in initialization first. 68 * The internal SERDES PHY's reset bit is self-clearing. 69 */ 70 SOC_IF_ERROR_RETURN 71 (MODIFY_SERDES_MII_CTRLr(unit, pc, 72 MII_CTRL_RESET, MII_CTRL_RESET)); 73 74 soc_timeout_init(&to, 10000, 0); 75 while (!soc_timeout_check(&to)) { 76 rv = READ_SERDES_MII_CTRLr(unit, pc, &data16); 77 if (((data16 & MII_CTRL_RESET) == 0) || SOC_FAILURE(rv)) { 78 break; 79 } 80 } 81 if ((data16 & MII_CTRL_RESET) != 0) { 82 LOG_WARN(BSL_LS_SOC_PHY, 83 (BSL_META_U(unit, 84 "Combo SerDes reset failed: u=%d p=%d\n"), 85 unit, port)); 86 } 87 88 data16 = 0; 89 mask16 = QS_MII_CTRL_AN_ENABLE | QS_MII_CTRL_AN_RESTART; 90 if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) || 91 PHY_PASSTHRU_MODE(unit, port) || 92 PHY_SGMII_AUTONEG_MODE(unit, port)) { 93 data16 = QS_MII_CTRL_AN_ENABLE | QS_MII_CTRL_AN_RESTART; 94 } 95 SOC_IF_ERROR_RETURN 96 (MODIFY_SERDES_MII_CTRLr(unit, pc, data16, mask16)); 97 98 data16 = QS_1000X_FALSE_LNK_DIS | QS_1000X_FLT_FORCE_EN; 99 mask16 = QS_1000X_FALSE_LNK_DIS | QS_1000X_FLT_FORCE_EN; 100 SOC_IF_ERROR_RETURN 101 (MODIFY_SERDES_1000X_CTRL2r(unit, pc, data16, mask16)); 102 103 data16 = 0; 104 mask16 = QS_1000X_AUTO_DETECT | QS_1000X_FIBER_MODE; 105 /* 106 * Configure signal auto-detection between SGMII and fiber. 107 * This detection only works if auto-negotiation is enabled. 108 */ 109 if (soc_property_port_get(unit, port, 110 spn_SERDES_AUTOMEDIUM, FALSE)) { 111 data16 |= QS_1000X_AUTO_DETECT; 112 } 113 /* 114 * Put the Serdes in Fiber or SGMII mode 115 */ 116 if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) || 117 PHY_PASSTHRU_MODE(unit, port)) { 118 if (soc_property_port_get(unit, port, 119 spn_SERDES_FIBER_PREF, TRUE)) { 120 data16 |= QS_1000X_FIBER_MODE; 121 } 122 } 123 SOC_IF_ERROR_RETURN 124 (MODIFY_SERDES_1000X_CTRL1r(unit, pc, data16, mask16)); 125 126 if (PHY_COPPER_MODE(unit, port)) { 127 /* Do not wait 10ms for sync status before a valid link is 128 * established when auto-negotiation is disabled. 129 */ 130 SOC_IF_ERROR_RETURN 131 (MODIFY_SERDES_1000X_CTRL2r(unit, pc, 0x0000, 0x0004)); 132 } 133 134 LOG_INFO(BSL_LS_SOC_PHY, 135 (BSL_META_U(unit, 136 "phy_53xxx_init: u=%d p=%d %s\n"), 137 unit, port, 138 PHY_FIBER_MODE(unit, port) ? "Fiber" : "Copper")); 139 return SOC_E_NONE; 140 } 141 142 143 /* 144 * Function: 145 * phy_53xxx_init 146 * Purpose: 147 * Initialize 53xxx internal PHY driver 148 * Parameters: 149 * unit - Switch unit #. 150 * port - Switch port #. 151 * Returns: 152 * SOC_E_NONE 153 * Notes: 154 * This function implements the bulk part of phy_53xxx_init and 155 * provides a method for initializing the SerDes when it is used 156 * as an intermediate PHY between the MAC and an external PHY. 157 * In the latter case the PHY driver reset callback should be 158 * called only once, and this has already been done by the 159 * external PHY driver init function. 160 */ 161 162 STATIC int 163 phy_53xxx_init(int unit, soc_port_t port) 164 { 165 166 LOG_INFO(BSL_LS_SOC_PHY, 167 (BSL_META_U(unit, 168 "phy_53xxx_init: u=%d p=%d\n"), 169 unit, port)); 170 171 if (!PHY_EXTERNAL_MODE(unit, port)) { 172 /* PHY reset callbacks */ 173 SOC_IF_ERROR_RETURN(soc_phy_reset(unit, port)); 174 175 PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER); 176 } 177 SOC_IF_ERROR_RETURN 178 (phy_53xxx_init_no_reset(unit, port)); 179 180 return SOC_E_NONE; 181 } 182 183 /* 184 * Function: 185 * phy_53xxx_enable_set 186 * Purpose: 187 * Enable or disable the physical interface. 188 * Parameters: 189 * unit - Switch unit #. 190 * port - Switch port #. 191 * enable - Boolean, true = enable PHY, false = disable. 192 * Returns: 193 * SOC_E_XXX 194 * Notes: 195 * The PHY is held in reset when disabled. Also, the 196 * MISC_CONTROL.IDDQ bit must be set to 1 so the remote partner 197 * sees link down. 198 */ 199 200 STATIC int 201 phy_53xxx_enable_set(int unit, soc_port_t port, int enable) 202 { 203 LOG_INFO(BSL_LS_SOC_PHY, 204 (BSL_META_U(unit, 205 "phy_53xxx_enable_set: u=%d p=%d en=%d\n"), 206 unit, port, enable)); 207 if (enable) { 208 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE); 209 } else { 210 PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE); 211 } 212 213 return SOC_E_NONE; 214 } 215 216 STATIC int 217 _phy_53xxx_sgmii_speed_set(int unit, soc_port_t port, int speed) 218 { 219 uint16 mii_ctrl; 220 phy_ctrl_t *pc; 221 222 pc = INT_PHY_SW_STATE(unit, port); 223 224 switch (speed) { 225 case 10: 226 mii_ctrl = QS_MII_CTRL_SS_10; 227 break; 228 case 100: 229 mii_ctrl = QS_MII_CTRL_SS_100; 230 break; 231 case 0: 232 case 1000: 233 mii_ctrl = QS_MII_CTRL_SS_1000; 234 break; 235 default: 236 return SOC_E_CONFIG; 237 } 238 SOC_IF_ERROR_RETURN 239 (MODIFY_SERDES_MII_CTRLr(unit, pc, mii_ctrl, 240 (QS_MII_CTRL_SS_LSB | QS_MII_CTRL_SS_MSB))); 241 return SOC_E_NONE; 242 } 243 244 STATIC int 245 _phy_53xxx_1000x_speed_set(int unit, soc_port_t port, int speed) 246 { 247 if ((speed !=0) && (speed != 1000)) { 248 return SOC_E_CONFIG; 249 } 250 251 return SOC_E_NONE; 252 } 253 254 /* 255 * Function: 256 * phy_53xxx_speed_set 257 * Purpose: 258 * Set the current operating speed (forced). 259 * Parameters: 260 * unit - Switch unit #. 261 * port - Switch port #. 262 * duplex - (OUT) Boolean, true indicates full duplex, false 263 * indicates half. 264 * Returns: 265 * SOC_E_XXX 266 */ 267 268 STATIC int 269 phy_53xxx_speed_set(int unit, soc_port_t port, int speed) 270 { 271 uint16 fiber_status; 272 phy_ctrl_t *pc; 273 int rv; 274 275 pc = INT_PHY_SW_STATE(unit, port); 276 rv = SOC_E_NONE; 277 278 SOC_IF_ERROR_RETURN 279 (READ_SERDES_1000X_STAT1r(unit, pc, &fiber_status)); 280 281 if (fiber_status & QS_1000X_STATUS1_SGMII_MODE) { 282 rv = _phy_53xxx_sgmii_speed_set(unit, port, speed); 283 } else { 284 rv = _phy_53xxx_1000x_speed_set(unit, port, speed); 285 } 286 287 if (SOC_SUCCESS(rv)) { 288 pc->fiber.force_speed = speed; 289 } 290 291 LOG_INFO(BSL_LS_SOC_PHY, 292 (BSL_META_U(unit, 293 "phy_53xxx_speed_set: u=%d p=%d speed=%d rv=%d\n"), 294 unit, port, speed, rv)); 295 296 return rv; 297 } 298 299 STATIC int 300 _phy_53xxx_sgmii_speed_get(int unit, soc_port_t port, int *speed) 301 { 302 uint16 mii_ctrl, mii_stat, mii_anp; 303 phy_ctrl_t *pc; 304 305 pc = INT_PHY_SW_STATE(unit, port); 306 307 SOC_IF_ERROR_RETURN 308 (READ_SERDES_MII_CTRLr(unit, pc, &mii_ctrl)); 309 SOC_IF_ERROR_RETURN 310 (READ_SERDES_MII_STATr(unit, pc, &mii_stat)); 311 312 if (mii_ctrl & QS_MII_CTRL_AN_ENABLE) { /*Auto-negotiation enabled */ 313 if (!(mii_stat & MII_STAT_AN_DONE)) { /* Auto-neg NOT complete */ 314 *speed = 0; 315 return SOC_E_NONE; 316 } else { /* Read Advertise link partner */ 317 SOC_IF_ERROR_RETURN 318 (READ_SERDES_MII_ANPr(unit, pc, &mii_anp)); 319 if (mii_anp & QS_MII_ANP_SGMII) { 320 switch(mii_anp & QS_MII_ANP_SGMII_SPEED) { 321 case QS_MII_ANP_SGMII_SPEED_1000: 322 *speed = 1000; 323 break; 324 case QS_MII_ANP_SGMII_SPEED_100: 325 *speed = 100; 326 break; 327 case QS_MII_ANP_SGMII_SPEED_10: 328 *speed = 10; 329 break; 330 default: 331 return SOC_E_UNAVAIL; 332 } 333 } 334 } 335 } else { /* Forced speed */ 336 switch(mii_ctrl & (QS_MII_CTRL_SS_LSB | QS_MII_CTRL_SS_MSB)) { 337 case QS_MII_CTRL_SS_10: 338 *speed = 10; 339 break; 340 case QS_MII_CTRL_SS_100: 341 *speed = 100; 342 break; 343 case QS_MII_CTRL_SS_1000: 344 *speed = 1000; 345 break; 346 default: 347 return SOC_E_UNAVAIL; 348 } 349 } 350 return SOC_E_NONE; 351 } 352 353 STATIC int 354 _phy_53xxx_1000x_speed_get(int unit, soc_port_t port, int *speed) 355 { 356 COMPILER_REFERENCE(unit); 357 COMPILER_REFERENCE(port); 358 359 *speed = 1000; 360 361 return SOC_E_NONE; 362 } 363 364 /* 365 * Function: 366 * phy_53xxx_speed_get 367 * Purpose: 368 * Get the current operating speed. If autoneg is enabled, 369 * then operating mode is returned, otherwise forced mode is returned. 370 * Parameters: 371 * unit - Switch unit #. 372 * port - Switch port #. 373 * duplex - (OUT) Boolean, true indicates full duplex, false 374 * indicates half. 375 * Returns: 376 * SOC_E_XXX 377 */ 378 379 STATIC int 380 phy_53xxx_speed_get(int unit, soc_port_t port, int *speed) 381 { 382 uint16 fiber_status; 383 phy_ctrl_t *pc; 384 385 pc = INT_PHY_SW_STATE(unit, port); 386 387 *speed = 1000; 388 389 SOC_IF_ERROR_RETURN 390 (READ_SERDES_1000X_STAT1r(unit, pc, &fiber_status)); 391 392 if (fiber_status & QS_1000X_STATUS1_SGMII_MODE) { 393 SOC_IF_ERROR_RETURN 394 (_phy_53xxx_sgmii_speed_get(unit, port, speed)); 395 } else { 396 SOC_IF_ERROR_RETURN 397 (_phy_53xxx_1000x_speed_get(unit, port, speed)); 398 } 399 400 return SOC_E_NONE; 401 } 402 403 /* 404 * Function: 405 * phy_53xxx_an_set 406 * Purpose: 407 * Enable or disabled auto-negotiation on the specified port. 408 * Parameters: 409 * unit - Switch unit #. 410 * port - Switch port #. 411 * an - Boolean, if true, auto-negotiation is enabled 412 * (and/or restarted). If false, autonegotiation is disabled. 413 * Returns: 414 * SOC_E_XXX 415 */ 416 417 STATIC int 418 phy_53xxx_an_set(int unit, soc_port_t port, int an) 419 { 420 phy_ctrl_t *pc; 421 uint16 par_det; 422 uint16 an_enable; 423 uint16 auto_det; 424 425 pc = INT_PHY_SW_STATE(unit, port); 426 427 LOG_INFO(BSL_LS_SOC_PHY, 428 (BSL_META_U(unit, 429 "phy_53xxx_an_set: u=%d p=%d an=%d\n"), 430 unit, port, an)); 431 /* 432 * Special case for BCM5421S being used in SGMII copper mode with 433 * BCM53xxx. Always have autoneg on. 434 */ 435 if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_5421S)) { 436 an = 1; 437 } 438 439 par_det = 0; 440 an_enable = 0; 441 auto_det = 0; 442 if (an) { 443 par_det = QS_1000X_PAR_DET_EN; 444 an_enable = QS_MII_CTRL_AN_ENABLE | QS_MII_CTRL_AN_RESTART; 445 if (soc_property_port_get(unit, port, 446 spn_SERDES_AUTOMEDIUM, FALSE)) { 447 auto_det = QS_1000X_AUTO_DETECT; 448 } 449 } 450 451 SOC_IF_ERROR_RETURN 452 (MODIFY_SERDES_1000X_CTRL2r(unit, pc, 453 par_det, QS_1000X_PAR_DET_EN)); 454 455 SOC_IF_ERROR_RETURN 456 (MODIFY_SERDES_1000X_CTRL1r(unit, pc, 457 auto_det, QS_1000X_AUTO_DETECT)); 458 459 460 /* Enable/Disable and Restart autonegotiation (self-clearing bit) */ 461 SOC_IF_ERROR_RETURN 462 (MODIFY_SERDES_MII_CTRLr(unit, pc, an_enable, 463 QS_MII_CTRL_AN_ENABLE | QS_MII_CTRL_AN_RESTART)); 464 465 pc->fiber.autoneg_enable = an; 466 467 return SOC_E_NONE; 468 } 469 470 /* 471 * Function: 472 * phy_53xxx_adv_local_set 473 * Purpose: 474 * Set the current advertisement for auto-negotiation. 475 * Parameters: 476 * unit - Switch unit #. 477 * port - Switch port #. 478 * mode - Port mode mask indicating supported options/speeds. 479 * Returns: 480 * SOC_E_XXX 481 */ 482 STATIC int 483 phy_53xxx_adv_local_set(int unit, soc_port_t port, soc_port_mode_t mode) 484 { 485 return phy_serdes_adv_local_set(unit, port, mode); 486 } 487 488 /* 489 * Function: 490 * phy_53xxx_adv_local_get 491 * Purpose: 492 * Get the current advertisement for auto-negotiation. 493 * Parameters: 494 * unit - Switch unit #. 495 * port - Switch port #. 496 * mode - (OUT) Port mode mask indicating supported options/speeds. 497 * Returns: 498 * SOC_E_XXX 499 */ 500 501 STATIC int 502 phy_53xxx_adv_local_get(int unit, soc_port_t port, soc_port_mode_t *mode) 503 { 504 return phy_serdes_adv_local_get(unit, port, mode); 505 } 506 507 /* 508 * Function: 509 * phy_53xxx_adv_remote_get 510 * Purpose: 511 * Get partner's current advertisement for auto-negotiation. 512 * Parameters: 513 * unit - Switch unit #. 514 * port - Switch port #. 515 * mode - (OUT) Port mode mask indicating supported options/speeds. 516 * Returns: 517 * SOC_E_XXX 518 */ 519 520 STATIC int 521 phy_53xxx_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode) 522 { 523 return phy_serdes_adv_remote_get(unit, port, mode); 524 } 525 526 /* 527 * Function: 528 * phy_53xxx_lb_set 529 * Purpose: 530 * Set the internal PHY loopback mode. 531 * Parameters: 532 * unit - Switch unit #. 533 * port - Switch port #. 534 * loopback - Boolean: true = enable loopback, false = disable. 535 * Returns: 536 * SOC_E_XXX 537 */ 538 539 STATIC int 540 phy_53xxx_lb_set(int unit, soc_port_t port, int enable) 541 { 542 uint16 ctrl; 543 phy_ctrl_t *pc; 544 int rv; 545 546 pc = INT_PHY_SW_STATE(unit, port); 547 548 ctrl = (enable) ? QS_MII_CTRL_LOOPBACK : 0; 549 550 rv = MODIFY_SERDES_MII_CTRLr(unit, pc, ctrl, QS_MII_CTRL_LOOPBACK); 551 552 LOG_INFO(BSL_LS_SOC_PHY, 553 (BSL_META_U(unit, 554 "phy_53xxx_lb_set: u=%d p=%d lb=%d rv=%d\n"), 555 unit, port, enable, rv)); 556 return rv; 557 } 558 559 /* 560 * Function: 561 * phy_53xxx_ability_get 562 * Purpose: 563 * Get the abilities of the internal PHY. 564 * Parameters: 565 * unit - Switch unit #. 566 * port - Switch port #. 567 * mode - (OUT) Port mode mask indicating supported options/speeds. 568 * Returns: 569 * SOC_E_XXX 570 */ 571 572 STATIC int 573 phy_53xxx_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode) 574 { 575 uint16 fiber_status; 576 phy_ctrl_t *pc; 577 pc = INT_PHY_SW_STATE(unit, port); 578 579 SOC_IF_ERROR_RETURN 580 (READ_SERDES_1000X_STAT1r(unit, pc, &fiber_status)); 581 582 if (fiber_status & QS_1000X_STATUS1_SGMII_MODE) { 583 *mode = (SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB_FD | 584 SOC_PM_LB_PHY | SOC_PM_GMII); 585 } else { 586 *mode = (SOC_PM_1000MB_FD | SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM | 587 SOC_PM_AN | SOC_PM_LB_PHY | SOC_PM_GMII); 588 } 589 590 return SOC_E_NONE; 591 } 592 593 /* 594 * Function: 595 * _phy_53xxx_medium_config_update 596 * Purpose: 597 * Update the PHY with config parameters 598 * Parameters: 599 * unit - Switch unit #. 600 * port - Switch port #. 601 * cfg - Config structure. 602 * Returns: 603 * SOC_E_XXX 604 */ 605 606 STATIC int 607 _phy_53xxx_medium_config_update(int unit, soc_port_t port, 608 soc_phy_config_t *cfg) 609 { 610 SOC_IF_ERROR_RETURN 611 (phy_53xxx_speed_set(unit, port, cfg->force_speed)); 612 SOC_IF_ERROR_RETURN 613 (phy_serdes_duplex_set(unit, port, cfg->force_duplex)); 614 SOC_IF_ERROR_RETURN 615 (phy_53xxx_adv_local_set(unit, port, cfg->autoneg_advert)); 616 SOC_IF_ERROR_RETURN 617 (phy_53xxx_an_set(unit, port, cfg->autoneg_enable)); 618 619 return SOC_E_NONE; 620 } 621 622 /* 623 * Function: 624 * phy_53xxx_medium_config_set 625 * Purpose: 626 * Set the operating parameters that are automatically selected 627 * when medium switches type. 628 * Parameters: 629 * unit - Switch unit # 630 * port - Port number 631 * medium - SOC_PORT_MEDIUM_COPPER/FIBER 632 * cfg - Operating parameters 633 * Returns: 634 * SOC_E_XXX 635 */ 636 637 STATIC int 638 phy_53xxx_medium_config_set(int unit, soc_port_t port, 639 soc_port_medium_t medium, 640 soc_phy_config_t *cfg) 641 { 642 phy_ctrl_t *pc; 643 644 pc = INT_PHY_SW_STATE(unit, port); 645 646 /* 647 * Changes take effect immediately if the target medium is active. 648 */ 649 switch (medium) { 650 case SOC_PORT_MEDIUM_FIBER: 651 /* Change the fiber modes */ 652 sal_memcpy(&pc->fiber, cfg, sizeof (pc->fiber)); 653 654 pc->fiber.autoneg_advert &= ADVERT_ALL_FIBER; 655 if (PHY_FIBER_MODE(unit, port)) { 656 SOC_IF_ERROR_RETURN 657 (_phy_53xxx_medium_config_update(unit, port, &pc->fiber)); 658 } 659 return SOC_E_NONE; 660 case SOC_PORT_MEDIUM_COPPER: 661 return SOC_E_UNAVAIL; 662 default: 663 return SOC_E_PARAM; 664 } 665 } 666 667 /* 668 * Function: 669 * phy_53xxx_reg_read 670 * Purpose: 671 * Routine to read PHY register 672 * Parameters: 673 * uint - BCM unit number 674 * pc - PHY state 675 * flags - Flags which specify the register type 676 * phy_reg_addr - Encoded register address 677 * phy_data - (OUT) Value read from PHY register 678 * Note: 679 * This register read function is not thread safe. Higher level 680 * function that calls this function must obtain a per port lock 681 * to avoid overriding register page mapping between threads. 682 */ 683 STATIC int 684 phy_53xxx_reg_read(int unit, soc_port_t port, uint32 flags, 685 uint32 phy_reg_addr, uint32 *phy_data) 686 { 687 uint16 reg_bank; 688 uint8 reg_addr; 689 uint16 data; /* Temporary holder for phy_data */ 690 phy_ctrl_t *pc; /* PHY software state */ 691 692 693 pc = INT_PHY_SW_STATE(unit, port); 694 695 reg_bank = SOC_PHY_REG_BANK(phy_reg_addr); 696 reg_addr = SOC_PHY_REG_ADDR(phy_reg_addr); 697 698 SOC_IF_ERROR_RETURN 699 (phy_reg_serdes_read(unit, pc, reg_bank, reg_addr, &data)); 700 701 *phy_data = data; 702 703 return SOC_E_NONE; 704 } 705 706 /* 707 * Function: 708 * phy_53xxx_reg_write 709 * Purpose: 710 * Routine to write PHY register 711 * Parameters: 712 * uint - BCM unit number 713 * pc - PHY state 714 * flags - Flags which specify the register type 715 * phy_reg_addr - Encoded register address 716 * phy_data - Value write to PHY register 717 * Note: 718 * This register read function is not thread safe. Higher level 719 * function that calls this function must obtain a per port lock 720 * to avoid overriding register page mapping between threads. 721 */ 722 STATIC int 723 phy_53xxx_reg_write(int unit, soc_port_t port, uint32 flags, 724 uint32 phy_reg_addr, uint32 phy_data) 725 { 726 uint16 reg_bank; 727 uint8 reg_addr; 728 uint16 data; /* Temporary holder for phy_data */ 729 phy_ctrl_t *pc; /* PHY software state */ 730 731 pc = INT_PHY_SW_STATE(unit, port); 732 733 data = (uint16) (phy_data & 0x0000FFFF); 734 reg_bank = SOC_PHY_REG_BANK(phy_reg_addr); 735 reg_addr = SOC_PHY_REG_ADDR(phy_reg_addr); 736 737 SOC_IF_ERROR_RETURN 738 (phy_reg_serdes_write(unit, pc, reg_bank, reg_addr, data)); 739 740 return SOC_E_NONE; 741 } 742 743 /* 744 * Function: 745 * phy_53xxx_reg_modify 746 * Purpose: 747 * Routine to write PHY register 748 * Parameters: 749 * uint - BCM unit number 750 * pc - PHY state 751 * flags - Flags which specify the register type 752 * phy_reg_addr - Encoded register address 753 * phy_mo_data - New value for the bits specified in phy_mo_mask 754 * phy_mo_mask - Bit mask to modify 755 * Note: 756 * This register read function is not thread safe. Higher level 757 * function that calls this function must obtain a per port lock 758 * to avoid overriding register page mapping between threads. 759 */ 760 STATIC int 761 phy_53xxx_reg_modify(int unit, soc_port_t port, uint32 flags, 762 uint32 phy_reg_addr, uint32 phy_data, uint32 phy_data_mask) 763 { 764 uint16 reg_bank; 765 uint8 reg_addr; 766 uint16 data; /* Temporary holder for phy_data */ 767 uint16 mask; 768 phy_ctrl_t *pc; /* PHY software state */ 769 770 pc = INT_PHY_SW_STATE(unit, port); 771 772 data = (uint16) (phy_data & 0x0000FFFF); 773 mask = (uint16) (phy_data_mask & 0x0000FFFF); 774 reg_bank = SOC_PHY_REG_BANK(phy_reg_addr); 775 reg_addr = SOC_PHY_REG_ADDR(phy_reg_addr); 776 777 SOC_IF_ERROR_RETURN 778 (phy_reg_serdes_modify(unit, pc, reg_bank, reg_addr, data, mask)); 779 780 return SOC_E_NONE; 781 } 782 783 STATIC int 784 phy_53xxx_notify(int unit, soc_port_t port, 785 soc_phy_event_t event, uint32 value) 786 { 787 int rv; 788 789 if (event >= phyEventCount) { 790 return SOC_E_PARAM; 791 } 792 793 rv = SOC_E_NONE; 794 switch(event) { 795 case phyEventInterface: 796 rv = (_phy_53xxx_notify_interface(unit, port, value)); 797 break; 798 case phyEventDuplex: 799 rv = (_phy_53xxx_notify_duplex(unit, port, value)); 800 break; 801 case phyEventSpeed: 802 rv = (_phy_53xxx_notify_speed(unit, port, value)); 803 break; 804 case phyEventStop: 805 rv = (_phy_53xxx_notify_stop(unit, port, value)); 806 break; 807 case phyEventResume: 808 rv = (_phy_53xxx_notify_resume(unit, port, value)); 809 break; 810 default: 811 rv = SOC_E_PARAM; 812 break; 813 } 814 815 return rv; 816 } 817 818 819 /* 820 * Variable: phy_53xxxdrv_ge 821 * Purpose: PHY Driver for Dozen SerDes Serdes PHY 822 */ 823 phy_driver_t phy_53xxxdrv_ge = { 824 "53XXX Internal SERDES PHY Driver", 825 phy_53xxx_init, 826 phy_null_reset, 827 phy_serdes_link_get, 828 phy_53xxx_enable_set, 829 phy_null_enable_get, 830 phy_serdes_duplex_set, 831 phy_null_one_get, /* duplex_get */ 832 phy_53xxx_speed_set, 833 phy_53xxx_speed_get, 834 phy_serdes_master_set, /* master_set */ 835 phy_serdes_master_get, /* master_get */ 836 phy_53xxx_an_set, 837 phy_serdes_an_get, 838 phy_53xxx_adv_local_set, 839 phy_53xxx_adv_local_get, 840 phy_53xxx_adv_remote_get, 841 phy_53xxx_lb_set, 842 phy_serdes_lb_get, 843 phy_serdes_interface_set, 844 phy_serdes_interface_get, 845 phy_53xxx_ability_get, 846 NULL, 847 NULL, 848 phy_null_mdix_set, 849 phy_null_mdix_get, 850 phy_null_mdix_status_get, 851 phy_53xxx_medium_config_set, 852 phy_serdes_medium_config_get, 853 phy_serdes_medium_status, 854 NULL, /* phy_cable_diag */ 855 NULL, /* phy_link_change */ 856 NULL, /* phy_control_set */ 857 NULL, /* phy_control_get */ 858 phy_53xxx_reg_read, 859 phy_53xxx_reg_write, 860 phy_53xxx_reg_modify, 861 phy_53xxx_notify 862 }; 863 864 /*********************************************************************** 865 * 866 * PASS-THROUGH NOTIFY ROUTINES 867 */ 868 869 /* 870 * Function: 871 * phy_53xxx_notify_duplex 872 * Purpose: 873 * Program duplex if (and only if) 53xxx is an intermediate PHY. 874 * Parameters: 875 * unit - unit #. 876 * port - port #. 877 * duplex - Boolean, TRUE indicates full duplex, FALSE 878 * indicates half. 879 * Returns: 880 * SOC_E_XXX 881 * Notes: 882 * If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to 883 * talk directly to an external fiber module. 884 * 885 * If PHY_FLAGS_FIBER is clear, the PHY is being used in 886 * pass-through mode to talk to an external SGMII PHY. 887 * 888 * When used in SGMII mode, autoneg must be turned off and 889 * the speed/duplex forced to match that of the external PHY. 890 */ 891 STATIC int 892 _phy_53xxx_notify_duplex(int unit, soc_port_t port, uint32 duplex) 893 { 894 int fiber; 895 uint16 mii_ctrl; 896 phy_ctrl_t *pc; 897 898 fiber = PHY_FIBER_MODE(unit, port); 899 pc = INT_PHY_SW_STATE(unit, port); 900 LOG_INFO(BSL_LS_SOC_PHY, 901 (BSL_META_U(unit, 902 "phy_53xxx_notify_duplex: u=%d p=%d duplex=%d fiber=%d\n"), 903 unit, port, duplex, fiber)); 904 905 if (SAL_BOOT_SIMULATION) { 906 return SOC_E_NONE; 907 } 908 909 if (fiber) { 910 SOC_IF_ERROR_RETURN 911 (MODIFY_SERDES_MII_CTRLr(unit, pc, 912 QS_MII_CTRL_FD, QS_MII_CTRL_FD)); 913 return SOC_E_NONE; 914 } 915 916 /* Put SERDES PHY in reset */ 917 918 SOC_IF_ERROR_RETURN 919 (_phy_53xxx_notify_stop(unit, port, PHY_STOP_DUPLEX_CHG)); 920 921 /* Update duplexity */ 922 mii_ctrl = (duplex) ? QS_MII_CTRL_FD : 0; 923 SOC_IF_ERROR_RETURN 924 (MODIFY_SERDES_MII_CTRLr(unit, pc, mii_ctrl, QS_MII_CTRL_FD)); 925 926 /* Take SERDES PHY out of reset */ 927 SOC_IF_ERROR_RETURN 928 (_phy_53xxx_notify_resume(unit, port, PHY_STOP_DUPLEX_CHG)); 929 930 /* Autonegotiation must be turned off to talk to external PHY if 931 * SGMII autoneg is not enabled. 932 */ 933 if (!PHY_SGMII_AUTONEG_MODE(unit, port)) { 934 SOC_IF_ERROR_RETURN 935 (phy_53xxx_an_set(unit, port, FALSE)); 936 } 937 return SOC_E_NONE; 938 } 939 940 /* 941 * Function: 942 * phy_53xxx_notify_speed 943 * Purpose: 944 * Program duplex if (and only if) 53xxx is an intermediate PHY. 945 * Parameters: 946 * unit - unit #. 947 * port - port #. 948 * speed - Speed to program. 949 * Returns: 950 * SOC_E_XXX 951 * Notes: 952 * If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to 953 * talk directly to an external fiber module. 954 * 955 * If PHY_FLAGS_FIBER is clear, the PHY is being used in 956 * pass-through mode to talk to an external SGMII PHY. 957 * 958 * When used in pass-through mode, autoneg must be turned off and 959 * the speed/duplex forced to match that of the external PHY. 960 */ 961 962 STATIC int 963 _phy_53xxx_notify_speed(int unit, soc_port_t port, uint32 speed) 964 { 965 int fiber; 966 uint16 fiber_status; 967 phy_ctrl_t *pc; 968 969 pc = INT_PHY_SW_STATE(unit, port); 970 fiber = PHY_FIBER_MODE(unit, port); 971 972 if (SAL_BOOT_SIMULATION) { 973 return SOC_E_NONE; 974 } 975 976 SOC_IF_ERROR_RETURN 977 (READ_SERDES_1000X_STAT1r(unit, pc, 978 &fiber_status)); 979 980 if (fiber && !(fiber_status & QS_1000X_STATUS1_SGMII_MODE)) { 981 if ((speed != 0) && (speed != 1000)) { 982 return SOC_E_CONFIG; 983 } 984 } 985 986 /* Put SERDES PHY in reset */ 987 SOC_IF_ERROR_RETURN 988 (_phy_53xxx_notify_stop(unit, port, PHY_STOP_SPEED_CHG)); 989 990 /* Update speed */ 991 SOC_IF_ERROR_RETURN 992 (_phy_53xxx_sgmii_speed_set(unit, port, (int)speed)); 993 994 /* Take SERDES PHY out of reset */ 995 SOC_IF_ERROR_RETURN 996 (_phy_53xxx_notify_resume(unit, port, PHY_STOP_SPEED_CHG)); 997 998 /* Autonegotiation must be turned off to talk to external PHY */ 999 if (!PHY_SGMII_AUTONEG_MODE(unit, port) && !fiber) { 1000 SOC_IF_ERROR_RETURN 1001 (phy_53xxx_an_set(unit, port, FALSE)); 1002 } 1003 1004 return SOC_E_NONE; 1005 } 1006 1007 /* 1008 * Function: 1009 * _phy_53xxx_stop 1010 * Purpose: 1011 * Put 53xxx SERDES in or out of reset depending on conditions 1012 */ 1013 1014 STATIC int 1015 _phy_53xxx_stop(int unit, soc_port_t port) 1016 { 1017 uint16 mii_ctrl; 1018 int stop, copper; 1019 phy_ctrl_t *pc; 1020 1021 pc = INT_PHY_SW_STATE(unit, port); 1022 1023 copper = (pc->stop & 1024 PHY_STOP_COPPER) != 0; 1025 1026 stop = ((pc->stop & 1027 (PHY_STOP_PHY_DIS | 1028 PHY_STOP_DRAIN)) != 0 || 1029 (copper && 1030 (pc->stop & 1031 (PHY_STOP_MAC_DIS | 1032 PHY_STOP_DUPLEX_CHG | 1033 PHY_STOP_SPEED_CHG)) != 0)); 1034 1035 LOG_INFO(BSL_LS_SOC_PHY, 1036 (BSL_META_U(unit, 1037 "phy_53xxx_stop: u=%d p=%d copper=%d stop=%d flg=0x%x\n"), 1038 unit, port, copper, stop, 1039 pc->stop)); 1040 1041 mii_ctrl = (stop) ? QS_MII_CTRL_PD : 0; 1042 SOC_IF_ERROR_RETURN 1043 (MODIFY_SERDES_MII_CTRLr(unit, pc, mii_ctrl, QS_MII_CTRL_PD)); 1044 1045 return SOC_E_NONE; 1046 } 1047 1048 /* 1049 * Function: 1050 * phy_53xxx_notify_stop 1051 * Purpose: 1052 * Add a reason to put 53xxx PHY in reset. 1053 * Parameters: 1054 * unit - unit #. 1055 * port - port #. 1056 * flags - Reason to stop 1057 * Returns: 1058 * SOC_E_XXX 1059 */ 1060 STATIC int 1061 _phy_53xxx_notify_stop(int unit, soc_port_t port, uint32 flags) 1062 { 1063 phy_ctrl_t *pc; 1064 1065 pc = INT_PHY_SW_STATE(unit, port); 1066 1067 pc->stop |= flags; 1068 1069 return _phy_53xxx_stop(unit, port); 1070 } 1071 1072 /* 1073 * Function: 1074 * phy_53xxx_notify_resume 1075 * Purpose: 1076 * Remove a reason to put 53xxx PHY in reset. 1077 * Parameters: 1078 * unit - unit #. 1079 * port - port #. 1080 * flags - Reason to stop 1081 * Returns: 1082 * SOC_E_XXX 1083 */ 1084 STATIC int 1085 _phy_53xxx_notify_resume(int unit, soc_port_t port, uint32 flags) 1086 { 1087 phy_ctrl_t *pc; 1088 1089 pc = INT_PHY_SW_STATE(unit, port); 1090 1091 pc->stop &= ~flags; 1092 1093 return _phy_53xxx_stop(unit, port); 1094 } 1095 1096 1097 STATIC int 1098 _phy_53xxx_notify_interface(int unit, soc_port_t port, uint32 intf) 1099 { 1100 phy_ctrl_t *pc; 1101 uint16 data16; 1102 1103 pc = INT_PHY_SW_STATE(unit, port); 1104 1105 data16 = (intf == SOC_PORT_IF_SGMII) ? 0 : QS_1000X_FIBER_MODE; 1106 SOC_IF_ERROR_RETURN 1107 (MODIFY_SERDES_1000X_CTRL1r(unit, pc, data16, QS_1000X_FIBER_MODE)); 1108 1109 return SOC_E_NONE; 1110 } 1111 1112 1113 #else /* INCLUDE_PHY_53XXX */ 1114 int _phy_53xxx_not_empty; 1115 #endif /* INCLUDE_PHY_53XXX */ 1116