phy56xxx.c (43866B)
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 * phy56xxx.c 9 * Purpose: 10 * Fiber driver for 56xxx using internal Serdes PHY. 11 * 12 * For BCM56504, the internal GMACC core of the GPORT 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_56xxx_notify_duplex 23 * phy_56xxx_notify_speed 24 * phy_56xxx_notify_stop 25 * phy_56xxx_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 "phydefs.h" /* Must include before other phy related includes */ 48 49 #if defined(INCLUDE_PHY_56XXX) 50 #include "phyconfig.h" /* Must be the first phy include after phydefs.h */ 51 52 #include "phyident.h" 53 #include "phyreg.h" 54 #include "phynull.h" 55 #include "serdes.h" 56 #include "phy56xxx.h" 57 #ifdef INCLUDE_PHY_XGXS6 58 #include "xgxs6.h" 59 #endif /*INCLUDE_PHY_XGXS6 */ 60 61 #define ADVERT_ALL_FIBER \ 62 (SOC_PM_PAUSE | SOC_PM_1000MB_FD) 63 64 /* Notify event forward declaration */ 65 STATIC int _phy_56xxx_notify_duplex(int unit, soc_port_t port, uint32 duplex); 66 STATIC int _phy_56xxx_notify_speed(int unit, soc_port_t port, uint32 speed); 67 STATIC int _phy_56xxx_notify_stop(int unit, soc_port_t port, uint32 flags); 68 STATIC int _phy_56xxx_notify_resume(int unit, soc_port_t port, uint32 flags); 69 STATIC int _phy_56xxx_notify_interface(int unit, soc_port_t port, uint32 intf); 70 71 /*********************************************************************** 72 * 73 * HELPER FUNCTIONS 74 */ 75 /* 76 * Function: 77 * phy_56xxx_tx_control_set 78 * Purpose: 79 * Set preemphasis, driver current, and predriver current values 80 * Parameters: 81 * unit - StrataSwitch unit #. 82 * pc - PHY state object. 83 * port - StrataSwitch port #. 84 * Returns: 85 * SOC_E_NONE 86 */ 87 88 STATIC int 89 _phy_56xxx_tx_control_set(int unit, phy_ctrl_t *pc, 90 soc_port_t port) 91 { 92 uint32 preemph; 93 uint32 idriver; 94 uint32 pdriver; 95 uint16 data16; 96 int fiber; 97 98 fiber = PHY_FIBER_MODE(unit, port); 99 100 #ifdef INCLUDE_PHY_XGXS6 101 if (soc_feature(unit, soc_feature_xgxs_v6)) { 102 /* Unicore */ 103 /* Preemphasis overrided in gxmac.c */ 104 return SOC_E_NONE; 105 } else 106 #endif /* INCLUDE_PHY_XGXS6 */ 107 { 108 /* Default values for Dozen SerDes */ 109 if (fiber) { 110 idriver = 0xf; 111 pdriver = 0x7; 112 preemph = 0x0; 113 } else { 114 idriver = 0xc; 115 pdriver = 0x7; 116 preemph = 0x0; 117 } 118 } 119 120 /* Read config override */ 121 preemph = soc_property_port_get(unit, port, 122 spn_SERDES_PREEMPHASIS, preemph); 123 idriver = soc_property_port_get(unit, port, 124 spn_SERDES_DRIVER_CURRENT, idriver); 125 pdriver = soc_property_port_get(unit, port, 126 spn_SERDES_PRE_DRIVER_CURRENT, pdriver); 127 128 /* Dozen SerDes 129 * preemph[8:6], idriver[15:12], pdriver[11:9] 130 */ 131 preemph = (preemph << 6) & 0x01c0; 132 idriver = (idriver << 12) & 0xf000; 133 pdriver = (pdriver << 9) & 0x0e00; 134 data16 = (uint16)(preemph | idriver | pdriver); 135 136 /* If amplitude override is not enabled, the amplitude value is 137 * automatically adjusted when interface changes between SGMII and 138 * fiber(1000X). 139 */ 140 SOC_IF_ERROR_RETURN 141 (MODIFY_SERDESDDS_1000X_CTRL1r(unit, pc, 142 DDS_1000X_TX_AMPLITUDE_OVRD, DDS_1000X_TX_AMPLITUDE_OVRD)); 143 144 /* Set ANALOG_TX with recommended amplitude */ 145 SOC_IF_ERROR_RETURN 146 (MODIFY_SERDESDDS_ANALOG_TXr(unit, pc, data16, 0xffc0)); 147 148 return SOC_E_NONE; 149 } 150 151 /*********************************************************************** 152 * 153 * FIBER DRIVER ROUTINES 154 */ 155 STATIC int 156 phy_56xxx_init_no_reset(int unit, soc_port_t port) 157 { 158 uint16 data16, mask16; 159 soc_timeout_t to; 160 phy_ctrl_t *pc; 161 int rv; 162 163 pc = INT_PHY_SW_STATE(unit, port); 164 pc->fiber.enable = PHY_FIBER_MODE(unit, port); 165 pc->fiber.preferred = PHY_FIBER_MODE(unit, port); 166 pc->fiber.autoneg_enable = TRUE; 167 pc->fiber.autoneg_advert = ADVERT_ALL_FIBER; 168 pc->fiber.force_speed = 1000; 169 pc->fiber.force_duplex = TRUE; 170 pc->fiber.master = SOC_PORT_MS_NONE; 171 pc->fiber.mdix = SOC_PORT_MDIX_NORMAL; 172 173 /* Since phy56xxx driver reset function vector does not reset 174 * the SerDes, reset the SerDes in initialization first. 175 * The internal SERDES PHY's reset bit is self-clearing. 176 */ 177 SOC_IF_ERROR_RETURN 178 (MODIFY_SERDESDDS_MII_CTRLr(unit, pc, 179 MII_CTRL_RESET, MII_CTRL_RESET)); 180 181 soc_timeout_init(&to, 10000, 0); 182 while (!soc_timeout_check(&to)) { 183 rv = READ_SERDESDDS_MII_CTRLr(unit, pc, &data16); 184 if (((data16 & MII_CTRL_RESET) == 0) || SOC_FAILURE(rv)) { 185 break; 186 } 187 } 188 if ((data16 & MII_CTRL_RESET) != 0) { 189 LOG_WARN(BSL_LS_SOC_PHY, 190 (BSL_META_U(unit, 191 "Combo SerDes reset failed: u=%d p=%d\n"), 192 unit, port)); 193 } 194 195 data16 = 0; 196 mask16 = DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART; 197 if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) || 198 PHY_PASSTHRU_MODE(unit, port) || 199 PHY_SGMII_AUTONEG_MODE(unit, port)) { 200 data16 = DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART; 201 } 202 SOC_IF_ERROR_RETURN 203 (MODIFY_SERDESDDS_MII_CTRLr(unit, pc, data16, mask16)); 204 205 /* Configure default preemphasis, predriver, idriver values */ 206 SOC_IF_ERROR_RETURN 207 (_phy_56xxx_tx_control_set(unit, pc, port)); 208 209 data16 = DDS_1000X_FALSE_LNK_DIS | DDS_1000X_FLT_FORCE_EN; 210 mask16 = DDS_1000X_FALSE_LNK_DIS | DDS_1000X_FLT_FORCE_EN; 211 SOC_IF_ERROR_RETURN 212 (MODIFY_SERDESDDS_1000X_CTRL2r(unit, pc, data16, mask16)); 213 214 data16 = 0; 215 mask16 = DDS_1000X_AUTO_DETECT | DDS_1000X_FIBER_MODE; 216 /* 217 * Configure signal auto-detection between SGMII and fiber. 218 * This detection only works if auto-negotiation is enabled. 219 */ 220 if (soc_property_port_get(unit, port, 221 spn_SERDES_AUTOMEDIUM, FALSE)) { 222 data16 |= DDS_1000X_AUTO_DETECT; 223 } 224 /* 225 * Put the Serdes in Fiber or SGMII mode 226 */ 227 if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) || 228 PHY_PASSTHRU_MODE(unit, port)) { 229 if (soc_property_port_get(unit, port, 230 spn_SERDES_FIBER_PREF, TRUE)) { 231 data16 |= DDS_1000X_FIBER_MODE; 232 } 233 } 234 if (soc_property_port_get(unit, port, 235 spn_SERDES_SGMII_MASTER, FALSE)) { 236 data16 |= DDS_1000X_SGMII_MASTER; 237 mask16 |= DDS_1000X_SGMII_MASTER; 238 } 239 SOC_IF_ERROR_RETURN 240 (MODIFY_SERDESDDS_1000X_CTRL1r(unit, pc, data16, mask16)); 241 242 if (PHY_COPPER_MODE(unit, port)) { 243 /* Do not wait 10ms for sync status before a valid link is 244 * established when auto-negotiation is disabled. 245 */ 246 SOC_IF_ERROR_RETURN 247 (MODIFY_SERDESDDS_1000X_CTRL2r(unit, pc, 0x0000, 0x0004)); 248 } 249 250 LOG_INFO(BSL_LS_SOC_PHY, 251 (BSL_META_U(unit, 252 "phy_56xxx_init: u=%d p=%d %s\n"), 253 unit, port, 254 PHY_FIBER_MODE(unit, port) ? "Fiber" : "Copper")); 255 return SOC_E_NONE; 256 } 257 258 259 /* 260 * Function: 261 * phy_56xxx_init 262 * Purpose: 263 * Initialize 56xxx internal PHY driver 264 * Parameters: 265 * unit - StrataSwitch unit #. 266 * port - StrataSwitch port #. 267 * Returns: 268 * SOC_E_NONE 269 * Notes: 270 * This function implements the bulk part of phy_56xxx_init and 271 * provides a method for initializing the SerDes when it is used 272 * as an intermediate PHY between the MAC and an external PHY. 273 * In the latter case the PHY driver reset callback should be 274 * called only once, and this has already been done by the 275 * external PHY driver init function. 276 */ 277 278 STATIC int 279 phy_56xxx_init(int unit, soc_port_t port) 280 { 281 282 LOG_INFO(BSL_LS_SOC_PHY, 283 (BSL_META_U(unit, 284 "phy_56xxx_init: u=%d p=%d\n"), 285 unit, port)); 286 287 if (!PHY_EXTERNAL_MODE(unit, port)) { 288 /* PHY reset callbacks */ 289 SOC_IF_ERROR_RETURN(soc_phy_reset(unit, port)); 290 291 if (soc_property_port_get(unit, port, 292 spn_SERDES_FIBER_PREF, 1)) { 293 PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER); 294 } else { 295 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER); 296 } 297 298 } 299 SOC_IF_ERROR_RETURN 300 (phy_56xxx_init_no_reset(unit, port)); 301 302 return SOC_E_NONE; 303 } 304 305 /* 306 * Function: 307 * phy_56xxx_enable_set 308 * Purpose: 309 * Enable or disable the physical interface. 310 * Parameters: 311 * unit - StrataSwitch unit #. 312 * port - StrataSwitch port #. 313 * enable - Boolean, true = enable PHY, false = disable. 314 * Returns: 315 * SOC_E_XXX 316 * Notes: 317 * The PHY is held in reset when disabled. Also, the 318 * MISC_CONTROL.IDDQ bit must be set to 1 so the remote partner 319 * sees link down. 320 */ 321 322 STATIC int 323 phy_56xxx_enable_set(int unit, soc_port_t port, int enable) 324 { 325 LOG_INFO(BSL_LS_SOC_PHY, 326 (BSL_META_U(unit, 327 "phy_56xxx_enable_set: u=%d p=%d en=%d\n"), 328 unit, port, enable)); 329 if (enable) { 330 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE); 331 332 SOC_IF_ERROR_RETURN 333 (_phy_56xxx_notify_resume(unit, port, PHY_STOP_PHY_DIS)); 334 } else { 335 PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE); 336 337 SOC_IF_ERROR_RETURN 338 (_phy_56xxx_notify_stop(unit, port, PHY_STOP_PHY_DIS)); 339 } 340 341 return SOC_E_NONE; 342 } 343 344 STATIC int 345 _phy_56xxx_sgmii_speed_set(int unit, soc_port_t port, int speed) 346 { 347 uint16 mii_ctrl; 348 phy_ctrl_t *pc; 349 350 pc = INT_PHY_SW_STATE(unit, port); 351 352 switch (speed) { 353 case 10: 354 mii_ctrl = DDS_MII_CTRL_SS_10; 355 break; 356 case 100: 357 mii_ctrl = DDS_MII_CTRL_SS_100; 358 break; 359 case 0: 360 case 1000: 361 mii_ctrl = DDS_MII_CTRL_SS_1000; 362 break; 363 default: 364 return SOC_E_CONFIG; 365 } 366 SOC_IF_ERROR_RETURN 367 (MODIFY_SERDESDDS_MII_CTRLr(unit, pc, mii_ctrl, 368 (DDS_MII_CTRL_SS_LSB | DDS_MII_CTRL_SS_MSB))); 369 return SOC_E_NONE; 370 } 371 372 STATIC int 373 _phy_56xxx_1000x_speed_set(int unit, soc_port_t port, int speed) 374 { 375 if ((speed !=0) && (speed != 1000)) { 376 return SOC_E_CONFIG; 377 } 378 379 return SOC_E_NONE; 380 } 381 382 /* 383 * Function: 384 * phy_56xxx_speed_set 385 * Purpose: 386 * Set the current operating speed (forced). 387 * Parameters: 388 * unit - StrataSwitch unit #. 389 * port - StrataSwitch port #. 390 * duplex - (OUT) Boolean, true indicates full duplex, false 391 * indicates half. 392 * Returns: 393 * SOC_E_XXX 394 */ 395 396 STATIC int 397 phy_56xxx_speed_set(int unit, soc_port_t port, int speed) 398 { 399 uint16 fiber_status; 400 phy_ctrl_t *pc; 401 int rv; 402 403 pc = INT_PHY_SW_STATE(unit, port); 404 rv = SOC_E_NONE; 405 406 #ifdef INCLUDE_PHY_XGXS6 407 if (soc_feature(unit, soc_feature_xgxs_v6)) { 408 SOC_IF_ERROR_RETURN 409 (phy_xgxs6_hg.pd_speed_set(unit, port, speed)); 410 pc->fiber.force_speed = speed; 411 return SOC_E_NONE; 412 } 413 #endif /* INCLUDE_PHY_XGXS6 */ 414 415 SOC_IF_ERROR_RETURN 416 (READ_SERDESDDS_1000X_STAT1r(unit, pc, &fiber_status)); 417 418 if (fiber_status & DDS_1000X_STATUS1_SGMII_MODE) { 419 rv = _phy_56xxx_sgmii_speed_set(unit, port, speed); 420 } else { 421 rv = _phy_56xxx_1000x_speed_set(unit, port, speed); 422 } 423 424 if (SOC_SUCCESS(rv)) { 425 pc->fiber.force_speed = speed; 426 } 427 428 LOG_INFO(BSL_LS_SOC_PHY, 429 (BSL_META_U(unit, 430 "phy_56xxx_speed_set: u=%d p=%d speed=%d rv=%d\n"), 431 unit, port, speed, rv)); 432 433 return rv; 434 } 435 436 STATIC int 437 _phy_56xxx_sgmii_speed_get(int unit, soc_port_t port, int *speed) 438 { 439 uint16 mii_ctrl, mii_stat, mii_anp; 440 phy_ctrl_t *pc; 441 442 pc = INT_PHY_SW_STATE(unit, port); 443 444 SOC_IF_ERROR_RETURN 445 (READ_SERDESDDS_MII_CTRLr(unit, pc, &mii_ctrl)); 446 SOC_IF_ERROR_RETURN 447 (READ_SERDESDDS_MII_STATr(unit, pc, &mii_stat)); 448 449 if (mii_ctrl & DDS_MII_CTRL_AN_ENABLE) { /*Auto-negotiation enabled */ 450 if (!(mii_stat & MII_STAT_AN_DONE)) { /* Auto-neg NOT complete */ 451 *speed = 0; 452 return SOC_E_NONE; 453 } else { /* Read Advertise link partner */ 454 SOC_IF_ERROR_RETURN 455 (READ_SERDESDDS_MII_ANPr(unit, pc, &mii_anp)); 456 if (mii_anp & DDS_MII_ANP_SGMII) { 457 switch(mii_anp & DDS_MII_ANP_SGMII_SPEED) { 458 case DDS_MII_ANP_SGMII_SPEED_1000: 459 *speed = 1000; 460 break; 461 case DDS_MII_ANP_SGMII_SPEED_100: 462 *speed = 100; 463 break; 464 case DDS_MII_ANP_SGMII_SPEED_10: 465 *speed = 10; 466 break; 467 default: 468 return SOC_E_UNAVAIL; 469 } 470 } 471 } 472 } else { /* Forced speed */ 473 switch(mii_ctrl & (DDS_MII_CTRL_SS_LSB | DDS_MII_CTRL_SS_MSB)) { 474 case DDS_MII_CTRL_SS_10: 475 *speed = 10; 476 break; 477 case DDS_MII_CTRL_SS_100: 478 *speed = 100; 479 break; 480 case DDS_MII_CTRL_SS_1000: 481 *speed = 1000; 482 break; 483 default: 484 return SOC_E_UNAVAIL; 485 } 486 } 487 return SOC_E_NONE; 488 } 489 490 STATIC int 491 _phy_56xxx_1000x_speed_get(int unit, soc_port_t port, int *speed) 492 { 493 COMPILER_REFERENCE(unit); 494 COMPILER_REFERENCE(port); 495 496 *speed = 1000; 497 498 return SOC_E_NONE; 499 } 500 501 /* 502 * Function: 503 * phy_56xxx_speed_get 504 * Purpose: 505 * Get the current operating speed. If autoneg is enabled, 506 * then operating mode is returned, otherwise forced mode is returned. 507 * Parameters: 508 * unit - StrataSwitch unit #. 509 * port - StrataSwitch port #. 510 * duplex - (OUT) Boolean, true indicates full duplex, false 511 * indicates half. 512 * Returns: 513 * SOC_E_XXX 514 */ 515 516 STATIC int 517 phy_56xxx_speed_get(int unit, soc_port_t port, int *speed) 518 { 519 uint16 fiber_status; 520 phy_ctrl_t *pc; 521 522 pc = INT_PHY_SW_STATE(unit, port); 523 524 *speed = 1000; 525 526 #ifdef INCLUDE_PHY_XGXS6 527 if (soc_feature(unit, soc_feature_xgxs_v6)) { 528 return phy_xgxs6_hg.pd_speed_get(unit, port, speed); 529 } 530 #endif /* INCLUDE_PHY_XGXS6 */ 531 532 SOC_IF_ERROR_RETURN 533 (READ_SERDESDDS_1000X_STAT1r(unit, pc, &fiber_status)); 534 535 if (fiber_status & DDS_1000X_STATUS1_SGMII_MODE) { 536 SOC_IF_ERROR_RETURN 537 (_phy_56xxx_sgmii_speed_get(unit, port, speed)); 538 } else { 539 SOC_IF_ERROR_RETURN 540 (_phy_56xxx_1000x_speed_get(unit, port, speed)); 541 } 542 543 return SOC_E_NONE; 544 } 545 546 /* 547 * Function: 548 * phy_56xxx_an_set 549 * Purpose: 550 * Enable or disabled auto-negotiation on the specified port. 551 * Parameters: 552 * unit - StrataSwitch unit #. 553 * port - StrataSwitch port #. 554 * an - Boolean, if true, auto-negotiation is enabled 555 * (and/or restarted). If false, autonegotiation is disabled. 556 * Returns: 557 * SOC_E_XXX 558 */ 559 560 STATIC int 561 phy_56xxx_an_set(int unit, soc_port_t port, int an) 562 { 563 phy_ctrl_t *pc; 564 uint16 par_det; 565 uint16 an_enable; 566 uint16 auto_det; 567 568 pc = INT_PHY_SW_STATE(unit, port); 569 570 LOG_INFO(BSL_LS_SOC_PHY, 571 (BSL_META_U(unit, 572 "phy_56xxx_an_set: u=%d p=%d an=%d\n"), 573 unit, port, an)); 574 par_det = 0; 575 an_enable = 0; 576 auto_det = 0; 577 if (an) { 578 an_enable = DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART; 579 if (soc_property_port_get(unit, port, 580 spn_SERDES_AUTOMEDIUM, FALSE)) { 581 auto_det = DDS_1000X_AUTO_DETECT; 582 } 583 } else { 584 par_det = DDS_1000X_PAR_DET_EN; 585 } 586 587 SOC_IF_ERROR_RETURN 588 (MODIFY_SERDESDDS_1000X_CTRL2r(unit, pc, 589 par_det, DDS_1000X_PAR_DET_EN)); 590 591 SOC_IF_ERROR_RETURN 592 (MODIFY_SERDESDDS_1000X_CTRL1r(unit, pc, 593 auto_det, DDS_1000X_AUTO_DETECT)); 594 595 596 /* Enable/Disable and Restart autonegotiation (self-clearing bit) */ 597 SOC_IF_ERROR_RETURN 598 (MODIFY_SERDESDDS_MII_CTRLr(unit, pc, an_enable, 599 DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART)); 600 601 pc->fiber.autoneg_enable = an; 602 603 return SOC_E_NONE; 604 } 605 606 /* 607 * Function: 608 * phy_56xxx_adv_local_set 609 * Purpose: 610 * Set the current advertisement for auto-negotiation. 611 * Parameters: 612 * unit - StrataSwitch unit #. 613 * port - StrataSwitch port #. 614 * mode - Port mode mask indicating supported options/speeds. 615 * Returns: 616 * SOC_E_XXX 617 */ 618 STATIC int 619 phy_56xxx_adv_local_set(int unit, soc_port_t port, soc_port_mode_t mode) 620 { 621 #ifdef INCLUDE_PHY_XGXS6 622 if (soc_feature(unit, soc_feature_xgxs_v6)) { 623 mode &= SOC_PM_2500MB_FD | SOC_PM_1000MB_FD | 624 SOC_PM_PAUSE_TX | SOC_PM_PAUSE_RX; 625 return (phy_xgxs6_hg.pd_adv_local_set(unit, port, mode)); 626 } 627 #endif 628 629 return phy_serdes_adv_local_set(unit, port, mode); 630 } 631 632 /* 633 * Function: 634 * phy_56xxx_adv_local_get 635 * Purpose: 636 * Get the current advertisement for auto-negotiation. 637 * Parameters: 638 * unit - StrataSwitch unit #. 639 * port - StrataSwitch port #. 640 * mode - (OUT) Port mode mask indicating supported options/speeds. 641 * Returns: 642 * SOC_E_XXX 643 */ 644 645 STATIC int 646 phy_56xxx_adv_local_get(int unit, soc_port_t port, soc_port_mode_t *mode) 647 { 648 #ifdef INCLUDE_PHY_XGXS6 649 if (soc_feature(unit, soc_feature_xgxs_v6)) { 650 return phy_xgxs6_hg.pd_adv_local_get(unit, port, mode); 651 } 652 #endif /* INCLUDE_PHY_XGXS6 */ 653 654 return phy_serdes_adv_local_get(unit, port, mode); 655 } 656 657 /* 658 * Function: 659 * phy_56xxx_adv_remote_get 660 * Purpose: 661 * Get partner's current advertisement for auto-negotiation. 662 * Parameters: 663 * unit - StrataSwitch unit #. 664 * port - StrataSwitch port #. 665 * mode - (OUT) Port mode mask indicating supported options/speeds. 666 * Returns: 667 * SOC_E_XXX 668 */ 669 670 STATIC int 671 phy_56xxx_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode) 672 { 673 #ifdef INCLUDE_PHY_XGXS6 674 if (soc_feature(unit, soc_feature_xgxs_v6)) { 675 return phy_xgxs6_hg.pd_adv_remote_get(unit, port, mode); 676 } 677 #endif /* INCLUDE_PHY_XGXS6 */ 678 679 return phy_serdes_adv_remote_get(unit, port, mode); 680 } 681 682 /* 683 * Function: 684 * phy_56xxx_lb_set 685 * Purpose: 686 * Set the internal PHY loopback mode. 687 * Parameters: 688 * unit - StrataSwitch unit #. 689 * port - StrataSwitch port #. 690 * loopback - Boolean: true = enable loopback, false = disable. 691 * Returns: 692 * SOC_E_XXX 693 */ 694 695 STATIC int 696 phy_56xxx_lb_set(int unit, soc_port_t port, int enable) 697 { 698 uint16 ctrl; 699 phy_ctrl_t *pc; 700 int rv; 701 702 #ifdef INCLUDE_PHY_XGXS6 703 if (soc_feature(unit, soc_feature_xgxs_v6)) { 704 return phy_xgxs6_hg.pd_lb_set(unit, port, enable); 705 } 706 #endif /* INCLUDE_PHY_XGXS6 */ 707 708 pc = INT_PHY_SW_STATE(unit, port); 709 710 ctrl = (enable) ? DDS_MII_CTRL_LOOPBACK : 0; 711 712 rv = MODIFY_SERDESDDS_MII_CTRLr(unit, pc, ctrl, DDS_MII_CTRL_LOOPBACK); 713 714 LOG_INFO(BSL_LS_SOC_PHY, 715 (BSL_META_U(unit, 716 "phy_56xxx_lb_set: u=%d p=%d lb=%d rv=%d\n"), 717 unit, port, enable, rv)); 718 return rv; 719 } 720 721 /* 722 * Function: 723 * phy_56xxx_ability_get 724 * Purpose: 725 * Get the abilities of the internal PHY. 726 * Parameters: 727 * unit - StrataSwitch unit #. 728 * port - StrataSwitch port #. 729 * mode - (OUT) Port mode mask indicating supported options/speeds. 730 * Returns: 731 * SOC_E_XXX 732 */ 733 734 STATIC int 735 phy_56xxx_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode) 736 { 737 uint16 fiber_status; 738 phy_ctrl_t *pc; 739 pc = INT_PHY_SW_STATE(unit, port); 740 741 SOC_IF_ERROR_RETURN 742 (READ_SERDESDDS_1000X_STAT1r(unit, pc, &fiber_status)); 743 744 if (fiber_status & DDS_1000X_STATUS1_SGMII_MODE) { 745 *mode = (SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB_FD | 746 SOC_PM_LB_PHY | SOC_PM_GMII); 747 } else { 748 *mode = (SOC_PM_1000MB_FD | SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM | 749 SOC_PM_AN | SOC_PM_LB_PHY | SOC_PM_GMII); 750 } 751 752 #ifdef INCLUDE_PHY_XGXS6 753 if (soc_feature(unit, soc_feature_xgxs_v6)) { 754 *mode |= SOC_PM_2500MB_FD; 755 } 756 #endif /* INCLUDE_PHY_XGXS6 */ 757 758 759 return SOC_E_NONE; 760 } 761 762 /* 763 * Function: 764 * _phy_56xxx_medium_config_update 765 * Purpose: 766 * Update the PHY with config parameters 767 * Parameters: 768 * unit - StrataSwitch unit #. 769 * port - StrataSwitch port #. 770 * cfg - Config structure. 771 * Returns: 772 * SOC_E_XXX 773 */ 774 775 STATIC int 776 _phy_56xxx_medium_config_update(int unit, soc_port_t port, 777 soc_phy_config_t *cfg) 778 { 779 SOC_IF_ERROR_RETURN 780 (phy_56xxx_speed_set(unit, port, cfg->force_speed)); 781 SOC_IF_ERROR_RETURN 782 (phy_serdes_duplex_set(unit, port, cfg->force_duplex)); 783 SOC_IF_ERROR_RETURN 784 (phy_56xxx_adv_local_set(unit, port, cfg->autoneg_advert)); 785 SOC_IF_ERROR_RETURN 786 (phy_56xxx_an_set(unit, port, cfg->autoneg_enable)); 787 788 return SOC_E_NONE; 789 } 790 791 /* 792 * Function: 793 * phy_56xxx_medium_config_set 794 * Purpose: 795 * Set the operating parameters that are automatically selected 796 * when medium switches type. 797 * Parameters: 798 * unit - StrataSwitch unit # 799 * port - Port number 800 * medium - SOC_PORT_MEDIUM_COPPER/FIBER 801 * cfg - Operating parameters 802 * Returns: 803 * SOC_E_XXX 804 */ 805 806 STATIC int 807 phy_56xxx_medium_config_set(int unit, soc_port_t port, 808 soc_port_medium_t medium, 809 soc_phy_config_t *cfg) 810 { 811 phy_ctrl_t *pc; 812 813 pc = INT_PHY_SW_STATE(unit, port); 814 815 /* 816 * Changes take effect immediately if the target medium is active. 817 */ 818 switch (medium) { 819 case SOC_PORT_MEDIUM_FIBER: 820 /* Change the fiber modes */ 821 sal_memcpy(&pc->fiber, cfg, sizeof (pc->fiber)); 822 823 pc->fiber.autoneg_advert &= ADVERT_ALL_FIBER; 824 if (PHY_FIBER_MODE(unit, port)) { 825 SOC_IF_ERROR_RETURN 826 (_phy_56xxx_medium_config_update(unit, port, &pc->fiber)); 827 } 828 return SOC_E_NONE; 829 case SOC_PORT_MEDIUM_COPPER: 830 return SOC_E_UNAVAIL; 831 default: 832 return SOC_E_PARAM; 833 } 834 } 835 836 STATIC int 837 _phy_56xxx_control_linkdown_transmit_set(int unit, soc_port_t port, 838 uint32 value) 839 { 840 uint16 data; 841 phy_ctrl_t *pc; 842 843 pc = INT_PHY_SW_STATE(unit, port); 844 if (value) { 845 data = DDS_1000X_FORCE_XMIT_TX | DDS_1000X_FLT_FORCE_EN | 846 DDS_1000X_FALSE_LNK_DIS; 847 SOC_IF_ERROR_RETURN 848 (WRITE_SERDESDDS_1000X_CTRL2r(unit, pc, data)); 849 } else { 850 data = DDS_1000X_FLT_FORCE_EN | DDS_1000X_FALSE_LNK_DIS | 851 DDS_1000X_PAR_DET_EN; 852 SOC_IF_ERROR_RETURN 853 (WRITE_SERDESDDS_1000X_CTRL2r(unit, pc, data)); 854 855 } 856 857 return SOC_E_NONE; 858 } 859 860 861 STATIC int 862 _phy_56xxx_control_linkdown_transmit_get(int unit, soc_port_t port, 863 uint32 *value) 864 { 865 uint16 data; 866 phy_ctrl_t *pc; 867 868 pc = INT_PHY_SW_STATE(unit, port); 869 870 SOC_IF_ERROR_RETURN 871 (READ_SERDESDDS_1000X_CTRL2r(unit, pc, &data)); 872 873 *value = (data & DDS_1000X_FORCE_XMIT_TX) ? 1 : 0; 874 875 return SOC_E_NONE; 876 } 877 878 879 STATIC int 880 _phy_56xxx_control_tx_driver_set(int unit, soc_port_t port, 881 soc_phy_control_t type, uint32 value) 882 { 883 uint16 data; /* Temporary holder of reg value to be written */ 884 uint16 mask; /* Bit mask of reg value to be updated */ 885 phy_ctrl_t *pc; /* PHY software state */ 886 887 pc = INT_PHY_SW_STATE(unit, port); 888 889 /* Dozen SerDes */ 890 switch(type) { 891 case SOC_PHY_CONTROL_PREEMPHASIS: 892 data = (uint16)(value & 0x7); 893 data = data << 6; 894 mask = 0x01C0; 895 break; 896 case SOC_PHY_CONTROL_DRIVER_CURRENT: 897 data = (uint16)(value & 0xF); 898 data = data << 12; 899 mask = 0xF000; 900 break; 901 case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT: 902 data = (uint16)(value & 0x7); 903 data = data << 9; 904 mask = 0x0E00; 905 break; 906 default: 907 /* should never get here */ 908 return SOC_E_PARAM; 909 } 910 SOC_IF_ERROR_RETURN 911 (MODIFY_SERDESDDS_ANALOG_TXr(unit, pc, data, mask)); 912 913 return SOC_E_NONE; 914 } 915 916 STATIC int 917 _phy_56xxx_control_tx_driver_get(int unit, soc_port_t port, 918 soc_phy_control_t type, uint32 *value) 919 { 920 uint16 data16; /* Temporary holder of a reg value */ 921 phy_ctrl_t *pc; /* PHY software state */ 922 923 pc = INT_PHY_SW_STATE(unit, port); 924 925 data16 = 0; 926 /* Dozen SerDes */ 927 SOC_IF_ERROR_RETURN 928 (READ_SERDESDDS_ANALOG_TXr(unit, pc, &data16)); 929 switch(type) { 930 case SOC_PHY_CONTROL_PREEMPHASIS: 931 *value = (data16 & 0x01C0) >> 6; 932 break; 933 case SOC_PHY_CONTROL_DRIVER_CURRENT: 934 *value = (data16 & 0xF000) >> 12; 935 break; 936 case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT: 937 *value = (data16 & 0x0E00) >> 9; 938 break; 939 default: 940 /* should never get here */ 941 return SOC_E_PARAM; 942 } 943 944 return SOC_E_NONE; 945 } 946 947 /* 948 * Function: 949 * phy_phy56xxx_control_set 950 * Purpose: 951 * Configure PHY device specific control fucntion. 952 * Parameters: 953 * unit - StrataSwitch unit #. 954 * port - StrataSwitch port #. 955 * type - Control to update 956 * value - New setting for the control 957 * Returns: 958 * SOC_E_NONE 959 */ 960 STATIC int 961 phy_56xxx_control_set(int unit, soc_port_t port, 962 soc_phy_control_t type, uint32 value) 963 { 964 int rv; 965 966 #ifdef INCLUDE_PHY_XGXS6 967 if (soc_feature(unit, soc_feature_xgxs_v6)) { 968 return (phy_xgxs6_hg.pd_control_set(unit, port, type, value)); 969 } 970 #endif 971 972 PHY_CONTROL_TYPE_CHECK(type); 973 974 rv = SOC_E_UNAVAIL; 975 switch(type) { 976 case SOC_PHY_CONTROL_PREEMPHASIS: 977 /* fall through */ 978 case SOC_PHY_CONTROL_DRIVER_CURRENT: 979 /* fall through */ 980 case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT: 981 rv = _phy_56xxx_control_tx_driver_set(unit, port, type, value); 982 break; 983 case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT: 984 rv = _phy_56xxx_control_linkdown_transmit_set(unit, port, value); 985 break; 986 default: 987 rv = SOC_E_UNAVAIL; 988 break; 989 } 990 return rv; 991 } 992 /* 993 * Function: 994 * phy_56xxx_control_get 995 * Purpose: 996 * Get current control settign of the PHY. 997 * Parameters: 998 * unit - StrataSwitch unit #. 999 * port - StrataSwitch port #. 1000 * type - Control to update 1001 * value - (OUT)Current setting for the control 1002 * Returns: 1003 * SOC_E_NONE 1004 */ 1005 STATIC int 1006 phy_56xxx_control_get(int unit, soc_port_t port, 1007 soc_phy_control_t type, uint32 *value) 1008 { 1009 int rv; 1010 1011 if (NULL == value) { 1012 return SOC_E_PARAM; 1013 } 1014 1015 #ifdef INCLUDE_PHY_XGXS6 1016 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1017 return (phy_xgxs6_hg.pd_control_get(unit, port, type, value)); 1018 } 1019 #endif /* INCLUDE_PHY_XGXS6 */ 1020 1021 PHY_CONTROL_TYPE_CHECK(type); 1022 1023 rv = SOC_E_UNAVAIL; 1024 switch(type) { 1025 case SOC_PHY_CONTROL_PREEMPHASIS: 1026 /* fall through */ 1027 case SOC_PHY_CONTROL_DRIVER_CURRENT: 1028 /* fall through */ 1029 case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT: 1030 rv = _phy_56xxx_control_tx_driver_get(unit, port, type, value); 1031 break; 1032 case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT: 1033 rv = _phy_56xxx_control_linkdown_transmit_get(unit, port, value); 1034 break; 1035 default: 1036 rv = SOC_E_UNAVAIL; 1037 break; 1038 } 1039 1040 return rv; 1041 } 1042 1043 /* 1044 * Function: 1045 * phy_56xxx_reg_read 1046 * Purpose: 1047 * Routine to read PHY register 1048 * Parameters: 1049 * uint - BCM unit number 1050 * pc - PHY state 1051 * flags - Flags which specify the register type 1052 * phy_reg_addr - Encoded register address 1053 * phy_data - (OUT) Value read from PHY register 1054 * Note: 1055 * This register read function is not thread safe. Higher level 1056 * function that calls this function must obtain a per port lock 1057 * to avoid overriding register page mapping between threads. 1058 */ 1059 STATIC int 1060 phy_56xxx_reg_read(int unit, soc_port_t port, uint32 flags, 1061 uint32 phy_reg_addr, uint32 *phy_data) 1062 { 1063 uint16 reg_bank; 1064 uint8 reg_addr; 1065 uint16 data; /* Temporary holder for phy_data */ 1066 phy_ctrl_t *pc; /* PHY software state */ 1067 1068 #ifdef INCLUDE_PHY_XGXS6 1069 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1070 return (phy_xgxs6_hg.pd_reg_read(unit, port, flags, 1071 phy_reg_addr, phy_data)); 1072 } 1073 #endif /* INCLUDE_PHY_XGXS6 */ 1074 1075 pc = INT_PHY_SW_STATE(unit, port); 1076 1077 reg_bank = SOC_PHY_REG_BANK(phy_reg_addr); 1078 reg_addr = SOC_PHY_REG_ADDR(phy_reg_addr); 1079 1080 SOC_IF_ERROR_RETURN 1081 (phy_reg_serdes_read(unit, pc, reg_bank, reg_addr, &data)); 1082 1083 *phy_data = data; 1084 1085 return SOC_E_NONE; 1086 } 1087 1088 /* 1089 * Function: 1090 * phy_56xxx_reg_write 1091 * Purpose: 1092 * Routine to write PHY register 1093 * Parameters: 1094 * uint - BCM unit number 1095 * pc - PHY state 1096 * flags - Flags which specify the register type 1097 * phy_reg_addr - Encoded register address 1098 * phy_data - Value write to PHY register 1099 * Note: 1100 * This register read function is not thread safe. Higher level 1101 * function that calls this function must obtain a per port lock 1102 * to avoid overriding register page mapping between threads. 1103 */ 1104 STATIC int 1105 phy_56xxx_reg_write(int unit, soc_port_t port, uint32 flags, 1106 uint32 phy_reg_addr, uint32 phy_data) 1107 { 1108 uint16 reg_bank; 1109 uint8 reg_addr; 1110 uint16 data; /* Temporary holder for phy_data */ 1111 phy_ctrl_t *pc; /* PHY software state */ 1112 1113 #ifdef INCLUDE_PHY_XGXS6 1114 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1115 return (phy_xgxs6_hg.pd_reg_write(unit, port, flags, 1116 phy_reg_addr, phy_data)); 1117 } 1118 #endif /* INCLUDE_PHY_XGXS6 */ 1119 1120 pc = INT_PHY_SW_STATE(unit, port); 1121 1122 data = (uint16) (phy_data & 0x0000FFFF); 1123 reg_bank = SOC_PHY_REG_BANK(phy_reg_addr); 1124 reg_addr = SOC_PHY_REG_ADDR(phy_reg_addr); 1125 1126 SOC_IF_ERROR_RETURN 1127 (phy_reg_serdes_write(unit, pc, reg_bank, reg_addr, data)); 1128 1129 return SOC_E_NONE; 1130 } 1131 1132 /* 1133 * Function: 1134 * phy_56xxx_reg_modify 1135 * Purpose: 1136 * Routine to write PHY register 1137 * Parameters: 1138 * uint - BCM unit number 1139 * pc - PHY state 1140 * flags - Flags which specify the register type 1141 * phy_reg_addr - Encoded register address 1142 * phy_mo_data - New value for the bits specified in phy_mo_mask 1143 * phy_mo_mask - Bit mask to modify 1144 * Note: 1145 * This register read function is not thread safe. Higher level 1146 * function that calls this function must obtain a per port lock 1147 * to avoid overriding register page mapping between threads. 1148 */ 1149 STATIC int 1150 phy_56xxx_reg_modify(int unit, soc_port_t port, uint32 flags, 1151 uint32 phy_reg_addr, uint32 phy_data, uint32 phy_data_mask) 1152 { 1153 uint16 reg_bank; 1154 uint8 reg_addr; 1155 uint16 data; /* Temporary holder for phy_data */ 1156 uint16 mask; 1157 phy_ctrl_t *pc; /* PHY software state */ 1158 1159 #ifdef INCLUDE_PHY_XGXS6 1160 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1161 return (phy_xgxs6_hg.pd_reg_modify(unit, port, flags, 1162 phy_reg_addr, phy_data, phy_data_mask)); 1163 } 1164 #endif /* INCLUDE_PHY_XGXS6 */ 1165 1166 pc = INT_PHY_SW_STATE(unit, port); 1167 1168 data = (uint16) (phy_data & 0x0000FFFF); 1169 mask = (uint16) (phy_data_mask & 0x0000FFFF); 1170 reg_bank = SOC_PHY_REG_BANK(phy_reg_addr); 1171 reg_addr = SOC_PHY_REG_ADDR(phy_reg_addr); 1172 1173 SOC_IF_ERROR_RETURN 1174 (phy_reg_serdes_modify(unit, pc, reg_bank, reg_addr, data, mask)); 1175 1176 return SOC_E_NONE; 1177 } 1178 1179 STATIC int 1180 phy_56xxx_notify(int unit, soc_port_t port, 1181 soc_phy_event_t event, uint32 value) 1182 { 1183 int rv; 1184 1185 if (event >= phyEventCount) { 1186 return SOC_E_PARAM; 1187 } 1188 1189 rv = SOC_E_NONE; 1190 switch(event) { 1191 case phyEventInterface: 1192 rv = (_phy_56xxx_notify_interface(unit, port, value)); 1193 break; 1194 case phyEventDuplex: 1195 rv = (_phy_56xxx_notify_duplex(unit, port, value)); 1196 break; 1197 case phyEventSpeed: 1198 rv = (_phy_56xxx_notify_speed(unit, port, value)); 1199 break; 1200 case phyEventStop: 1201 rv = (_phy_56xxx_notify_stop(unit, port, value)); 1202 break; 1203 case phyEventResume: 1204 rv = (_phy_56xxx_notify_resume(unit, port, value)); 1205 break; 1206 default: 1207 rv = SOC_E_PARAM; 1208 break; 1209 } 1210 1211 return rv; 1212 } 1213 1214 STATIC int 1215 phy_56xxx_duplex_get(int unit, soc_port_t port, int *duplex) 1216 { 1217 uint16 reg0_16; 1218 uint16 mii_ctrl; 1219 phy_ctrl_t *pc; 1220 1221 pc = INT_PHY_SW_STATE(unit, port); 1222 1223 *duplex = TRUE; 1224 1225 SOC_IF_ERROR_RETURN 1226 (READ_SERDESDDS_1000X_STAT1r(unit, pc, ®0_16)); 1227 1228 if (reg0_16 & DDS_1000X_STATUS1_SGMII_MODE) { 1229 1230 /* retrieve the duplex setting in SGMII mode */ 1231 1232 SOC_IF_ERROR_RETURN 1233 (READ_SERDESDDS_MII_CTRLr(unit, pc, &mii_ctrl)); 1234 1235 if (mii_ctrl & DDS_MII_CTRL_AN_ENABLE) { 1236 SOC_IF_ERROR_RETURN 1237 (READ_SERDESDDS_MII_ANPr(unit,pc,®0_16)); 1238 1239 /* make sure link partner is also in SGMII mode 1240 * otherwise fall through to use the FD bit in MII_CTRL reg 1241 */ 1242 if (reg0_16 & DDS_MII_ANP_SGMII) { 1243 1244 /* test shows the duplex setting of external phy is 1245 * propagated to the AN link partner register 1246 */ 1247 if (reg0_16 & DDS_MII_ANP_SGMII_DUPLEX) { 1248 *duplex = TRUE; 1249 return SOC_E_NONE; 1250 } else { 1251 *duplex = FALSE; 1252 return SOC_E_NONE; 1253 } 1254 } 1255 } 1256 1257 *duplex = (mii_ctrl & DDS_MII_CTRL_FD) ? TRUE : FALSE; 1258 } 1259 1260 return SOC_E_NONE; 1261 } 1262 1263 /* 1264 * Variable: phy_56xxxdrv_ge 1265 * Purpose: PHY Driver for Dozen SerDes Serdes PHY internal to Draco, 1266 */ 1267 phy_driver_t phy_56xxxdrv_ge = { 1268 "56XXX Internal SERDES PHY Driver", 1269 phy_56xxx_init, 1270 phy_null_reset, 1271 phy_serdes_link_get, 1272 phy_56xxx_enable_set, 1273 phy_null_enable_get, 1274 phy_serdes_duplex_set, 1275 phy_56xxx_duplex_get, /* duplex_get */ 1276 phy_56xxx_speed_set, 1277 phy_56xxx_speed_get, 1278 phy_serdes_master_set, /* master_set */ 1279 phy_serdes_master_get, /* master_get */ 1280 phy_56xxx_an_set, 1281 phy_serdes_an_get, 1282 phy_56xxx_adv_local_set, 1283 phy_56xxx_adv_local_get, 1284 phy_56xxx_adv_remote_get, 1285 phy_56xxx_lb_set, 1286 phy_serdes_lb_get, 1287 phy_serdes_interface_set, 1288 phy_serdes_interface_get, 1289 phy_56xxx_ability_get, 1290 NULL, 1291 NULL, 1292 phy_null_mdix_set, 1293 phy_null_mdix_get, 1294 phy_null_mdix_status_get, 1295 phy_56xxx_medium_config_set, 1296 phy_serdes_medium_config_get, 1297 phy_serdes_medium_status, 1298 NULL, /* phy_cable_diag */ 1299 NULL, /* phy_link_change */ 1300 phy_56xxx_control_set, /* phy_control_set */ 1301 phy_56xxx_control_get, /* phy_control_get */ 1302 phy_56xxx_reg_read, 1303 phy_56xxx_reg_write, 1304 phy_56xxx_reg_modify, 1305 phy_56xxx_notify 1306 }; 1307 1308 /*********************************************************************** 1309 * 1310 * PASS-THROUGH NOTIFY ROUTINES 1311 */ 1312 1313 /* 1314 * Function: 1315 * phy_56xxx_notify_duplex 1316 * Purpose: 1317 * Program duplex if (and only if) 56xxx is an intermediate PHY. 1318 * Parameters: 1319 * unit - StrataSwitch unit #. 1320 * port - StrataSwitch port #. 1321 * duplex - Boolean, TRUE indicates full duplex, FALSE 1322 * indicates half. 1323 * Returns: 1324 * SOC_E_XXX 1325 * Notes: 1326 * If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to 1327 * talk directly to an external fiber module. 1328 * 1329 * If PHY_FLAGS_FIBER is clear, the PHY is being used in 1330 * pass-through mode to talk to an external SGMII PHY. 1331 * 1332 * When used in SGMII mode, autoneg must be turned off and 1333 * the speed/duplex forced to match that of the external PHY. 1334 */ 1335 STATIC int 1336 _phy_56xxx_notify_duplex(int unit, soc_port_t port, uint32 duplex) 1337 { 1338 int fiber; 1339 uint16 mii_ctrl; 1340 phy_ctrl_t *pc; 1341 1342 fiber = PHY_FIBER_MODE(unit, port); 1343 pc = INT_PHY_SW_STATE(unit, port); 1344 LOG_INFO(BSL_LS_SOC_PHY, 1345 (BSL_META_U(unit, 1346 "phy_56xxx_notify_duplex: u=%d p=%d duplex=%d fiber=%d\n"), 1347 unit, port, duplex, fiber)); 1348 1349 if (SAL_BOOT_SIMULATION) { 1350 return SOC_E_NONE; 1351 } 1352 1353 if (fiber) { 1354 SOC_IF_ERROR_RETURN 1355 (MODIFY_SERDESDDS_MII_CTRLr(unit, pc, 1356 DDS_MII_CTRL_FD, DDS_MII_CTRL_FD)); 1357 return SOC_E_NONE; 1358 } 1359 1360 /* Put SERDES PHY in reset */ 1361 1362 SOC_IF_ERROR_RETURN 1363 (_phy_56xxx_notify_stop(unit, port, PHY_STOP_DUPLEX_CHG)); 1364 1365 /* Update duplexity */ 1366 mii_ctrl = (duplex) ? DDS_MII_CTRL_FD : 0; 1367 SOC_IF_ERROR_RETURN 1368 (MODIFY_SERDESDDS_MII_CTRLr(unit, pc, mii_ctrl, DDS_MII_CTRL_FD)); 1369 1370 /* Take SERDES PHY out of reset */ 1371 SOC_IF_ERROR_RETURN 1372 (_phy_56xxx_notify_resume(unit, port, PHY_STOP_DUPLEX_CHG)); 1373 1374 /* Autonegotiation must be turned off to talk to external PHY if 1375 * SGMII autoneg is not enabled. 1376 */ 1377 if (!PHY_SGMII_AUTONEG_MODE(unit, port)) { 1378 SOC_IF_ERROR_RETURN 1379 (phy_56xxx_an_set(unit, port, FALSE)); 1380 } 1381 return SOC_E_NONE; 1382 } 1383 1384 /* 1385 * Function: 1386 * phy_56xxx_notify_speed 1387 * Purpose: 1388 * Program duplex if (and only if) 56xxx is an intermediate PHY. 1389 * Parameters: 1390 * unit - StrataSwitch unit #. 1391 * port - StrataSwitch port #. 1392 * speed - Speed to program. 1393 * Returns: 1394 * SOC_E_XXX 1395 * Notes: 1396 * If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to 1397 * talk directly to an external fiber module. 1398 * 1399 * If PHY_FLAGS_FIBER is clear, the PHY is being used in 1400 * pass-through mode to talk to an external SGMII PHY. 1401 * 1402 * When used in pass-through mode, autoneg must be turned off and 1403 * the speed/duplex forced to match that of the external PHY. 1404 */ 1405 1406 STATIC int 1407 _phy_56xxx_notify_speed(int unit, soc_port_t port, uint32 speed) 1408 { 1409 int fiber; 1410 uint16 fiber_status; 1411 phy_ctrl_t *pc; 1412 1413 pc = INT_PHY_SW_STATE(unit, port); 1414 fiber = PHY_FIBER_MODE(unit, port); 1415 1416 if (SAL_BOOT_SIMULATION) { 1417 return SOC_E_NONE; 1418 } 1419 1420 SOC_IF_ERROR_RETURN 1421 (READ_SERDESDDS_1000X_STAT1r(unit, pc, 1422 &fiber_status)); 1423 1424 if (fiber && !(fiber_status & DDS_1000X_STATUS1_SGMII_MODE)) { 1425 if ((speed != 0) && (speed != 1000)) { 1426 return SOC_E_CONFIG; 1427 } 1428 } 1429 1430 /* Put SERDES PHY in reset */ 1431 SOC_IF_ERROR_RETURN 1432 (_phy_56xxx_notify_stop(unit, port, PHY_STOP_SPEED_CHG)); 1433 1434 /* Update speed */ 1435 SOC_IF_ERROR_RETURN 1436 (_phy_56xxx_sgmii_speed_set(unit, port, (int)speed)); 1437 1438 /* Take SERDES PHY out of reset */ 1439 SOC_IF_ERROR_RETURN 1440 (_phy_56xxx_notify_resume(unit, port, PHY_STOP_SPEED_CHG)); 1441 1442 /* Autonegotiation must be turned off to talk to external PHY */ 1443 if (!PHY_SGMII_AUTONEG_MODE(unit, port) && !fiber) { 1444 SOC_IF_ERROR_RETURN 1445 (phy_56xxx_an_set(unit, port, FALSE)); 1446 } 1447 1448 return SOC_E_NONE; 1449 } 1450 1451 /* 1452 * Function: 1453 * _phy_56xxx_stop 1454 * Purpose: 1455 * Put 56xxx SERDES in or out of reset depending on conditions 1456 */ 1457 1458 STATIC int 1459 _phy_56xxx_stop(int unit, soc_port_t port) 1460 { 1461 uint16 mii_ctrl; 1462 int stop, copper; 1463 phy_ctrl_t *pc; 1464 1465 pc = INT_PHY_SW_STATE(unit, port); 1466 1467 copper = (pc->stop & 1468 PHY_STOP_COPPER) != 0; 1469 1470 stop = ((pc->stop & 1471 (PHY_STOP_PHY_DIS | 1472 PHY_STOP_DRAIN)) != 0 || 1473 (copper && 1474 (pc->stop & 1475 (PHY_STOP_MAC_DIS | 1476 PHY_STOP_DUPLEX_CHG | 1477 PHY_STOP_SPEED_CHG)) != 0)); 1478 1479 LOG_INFO(BSL_LS_SOC_PHY, 1480 (BSL_META_U(unit, 1481 "phy_56xxx_stop: u=%d p=%d copper=%d stop=%d flg=0x%x\n"), 1482 unit, port, copper, stop, 1483 pc->stop)); 1484 1485 mii_ctrl = (stop) ? DDS_MII_CTRL_PD : 0; 1486 SOC_IF_ERROR_RETURN 1487 (MODIFY_SERDESDDS_MII_CTRLr(unit, pc, mii_ctrl, DDS_MII_CTRL_PD)); 1488 1489 return SOC_E_NONE; 1490 } 1491 1492 /* 1493 * Function: 1494 * phy_56xxx_notify_stop 1495 * Purpose: 1496 * Add a reason to put 56xxx PHY in reset. 1497 * Parameters: 1498 * unit - StrataSwitch unit #. 1499 * port - StrataSwitch port #. 1500 * flags - Reason to stop 1501 * Returns: 1502 * SOC_E_XXX 1503 */ 1504 STATIC int 1505 _phy_56xxx_notify_stop(int unit, soc_port_t port, uint32 flags) 1506 { 1507 phy_ctrl_t *pc; 1508 1509 pc = INT_PHY_SW_STATE(unit, port); 1510 1511 pc->stop |= flags; 1512 1513 return _phy_56xxx_stop(unit, port); 1514 } 1515 1516 /* 1517 * Function: 1518 * phy_56xxx_notify_resume 1519 * Purpose: 1520 * Remove a reason to put 56xxx PHY in reset. 1521 * Parameters: 1522 * unit - StrataSwitch unit #. 1523 * port - StrataSwitch port #. 1524 * flags - Reason to stop 1525 * Returns: 1526 * SOC_E_XXX 1527 */ 1528 STATIC int 1529 _phy_56xxx_notify_resume(int unit, soc_port_t port, uint32 flags) 1530 { 1531 phy_ctrl_t *pc; 1532 1533 pc = INT_PHY_SW_STATE(unit, port); 1534 1535 pc->stop &= ~flags; 1536 1537 return _phy_56xxx_stop(unit, port); 1538 } 1539 1540 1541 STATIC int 1542 _phy_56xxx_notify_interface(int unit, soc_port_t port, uint32 intf) 1543 { 1544 phy_ctrl_t *pc; 1545 uint16 data16; 1546 1547 pc = INT_PHY_SW_STATE(unit, port); 1548 1549 data16 = (intf == SOC_PORT_IF_SGMII) ? 0 : DDS_1000X_FIBER_MODE; 1550 SOC_IF_ERROR_RETURN 1551 (MODIFY_SERDESDDS_1000X_CTRL1r(unit, pc, data16, DDS_1000X_FIBER_MODE)); 1552 1553 return SOC_E_NONE; 1554 } 1555 #else /* INCLUDE_PHY_56XXX */ 1556 int _phy_56xxx_not_empty; 1557 #endif /* INCLUDE_PHY_56XXX */ 1558