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