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