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