phy56xxx5601x.c (53954B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * File: 8 * phy56xxx_5601x.c 9 * Purpose: 10 * Fiber driver for 56xxx_5601x using internal Serdes PHY with shadow 11 * registers. 12 * 13 * For BCM56504, the internal GMACC core of the GPORT should always be 14 * configured to run in SGMII mode. TBI mode is not supported between 15 * the GMACC and the internal SERDES module. 16 * 17 * When operating with an external PHY, this driver is not used. 18 * However the speed/duplex of the internal PHY must be programmed to 19 * match the MAC and external PHY settings so the data can pass through. 20 * This file supplies some routines to allow mac.c to accomplish this 21 * (think of the internal PHY as part of the MAC in this case): 22 * 23 * phy_56xxx_5601x_notify_duplex 24 * phy_56xxx_5601x_notify_speed 25 * phy_56xxx_5601x_notify_stop 26 * phy_56xxx_5601x_notify_resume 27 * 28 * CMIC MIIM operations can be performed to the internal register set 29 * using internal MDIO address (PORT_TO_PHY_ADDR_INT), and an external 30 * PHY register set (such as BCM5424/34/64) can be programmed using the 31 * external MDIO address (PORT_TO_PHY_ADDR). 32 * 33 * MDIO accesses to the internal PHY are not modeled on Quickturn. 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_PHY_56XXX) 51 #include "phyconfig.h" /* Must be the first phy include after phydefs.h */ 52 53 #include "phyident.h" 54 #include "phyreg.h" 55 #include "phynull.h" 56 #include "serdes.h" 57 #include "physr.h" 58 #include "phy56xxx5601x.h" 59 60 #ifdef INCLUDE_PHY_XGXS6 61 #include "xgxs6.h" 62 #endif /*INCLUDE_PHY_XGXS6 */ 63 64 #define ADVERT_ALL_FIBER \ 65 (SOC_PM_PAUSE | SOC_PM_1000MB_FD) 66 67 /* Notify event forward declaration */ 68 STATIC int _phy_56xxx_5601x_notify_duplex(int unit, soc_port_t port, uint32 duplex); 69 STATIC int _phy_56xxx_5601x_notify_speed(int unit, soc_port_t port, uint32 speed); 70 STATIC int _phy_56xxx_5601x_notify_stop(int unit, soc_port_t port, uint32 flags); 71 STATIC int _phy_56xxx_5601x_notify_resume(int unit, soc_port_t port, uint32 flags); 72 STATIC int _phy_56xxx_5601x_notify_interface(int unit, soc_port_t port, uint32 intf); 73 74 75 STATIC int 76 _phy_56xxx_5601x_sregs_init(phy_ctrl_t *pc) 77 { 78 serdes_5601x_sregs_t *sr; 79 if (pc->driver_data == NULL) { 80 return SOC_E_INTERNAL; 81 } 82 sr = pc->driver_data; 83 84 sr->mii_ctrl = 0x1140; 85 sr->mii_ana = 0x01a0; 86 sr->r1000x_ctrl1 = 0x0181; 87 sr->r1000x_ctrl2 = 0x0000; 88 89 return SOC_E_NONE; 90 } 91 92 STATIC int 93 phy_serdes_5601x_reg_read(int unit, phy_ctrl_t *pc, uint16 reg_bank, 94 uint8 reg_addr, uint16 *phy_rd_data) 95 { 96 serdes_5601x_sregs_t *sr; 97 uint16 data = 0; 98 int rv = SOC_E_NONE; 99 int shadow_read = 1; 100 101 #ifdef INCLUDE_PHY_XGXS6 102 if (soc_feature(unit, soc_feature_xgxs_v6)) { 103 if ((reg_bank != 0) || (reg_addr >= 0x0010)) { 104 reg_bank = 0x300 + (reg_bank << 4); 105 } 106 } 107 #endif /* INCLUDE_PHY_XGXS6 */ 108 109 /* Read shadow registers if shadowed register */ 110 sr = pc->driver_data; 111 if (sr == NULL) { 112 return SOC_E_INTERNAL; 113 } 114 switch (reg_bank) { 115 case 0: 116 switch (reg_addr) { 117 case 0: 118 data = sr->mii_ctrl; 119 break; 120 case 4: 121 data = sr->mii_ana; 122 break; 123 case 0x10: 124 data = sr->r1000x_ctrl1; 125 break; 126 case 0x11: 127 data = sr->r1000x_ctrl2; 128 break; 129 default: 130 shadow_read = 0; 131 break; 132 } 133 break; 134 default: 135 /* Invalid block */ 136 LOG_INFO(BSL_LS_SOC_PHY, 137 (BSL_META_U(unit, 138 "phy_serdes_5601x_reg_read: unexpected read from %x*%x: %04x\n"), 139 reg_bank, reg_addr, data)); 140 break; 141 } 142 143 if (! shadow_read) { 144 /* Read real registers */ 145 rv = WRITE_PHY_REG(unit, pc, 0x1f, reg_bank); 146 if (SOC_SUCCESS(rv)) { 147 rv = READ_PHY_REG(unit, pc, reg_addr, &data); 148 } 149 LOG_INFO(BSL_LS_SOC_PHY, 150 (BSL_META_U(unit, 151 "phy_serdes_5601x_reg_read: %x*%x: %04x\n"), 152 reg_bank, reg_addr, data)); 153 } else { 154 LOG_INFO(BSL_LS_SOC_PHY, 155 (BSL_META_U(unit, 156 "phy_serdes_5601x_reg_read: shadow %x*%x: %04x\n"), 157 reg_bank, reg_addr, data)); 158 } 159 160 161 *phy_rd_data = data; 162 163 return rv; 164 } 165 166 STATIC int 167 phy_serdes_5601x_reg_write(int unit, phy_ctrl_t *pc, uint16 reg_bank, 168 uint8 reg_addr, uint16 phy_wr_data) 169 { 170 serdes_5601x_sregs_t *sr; 171 int rv = SOC_E_NONE; 172 int shadow_write = 1; 173 174 #ifdef INCLUDE_PHY_XGXS6 175 if (soc_feature(unit, soc_feature_xgxs_v6)) { 176 if ((reg_bank != 0) || (reg_addr >= 0x0010)) { 177 reg_bank = 0x300 + (reg_bank << 4); 178 } 179 } 180 #endif /* INCLUDE_PHY_XGXS6 */ 181 182 sr = pc->driver_data; 183 if (sr == NULL) { 184 return SOC_E_INTERNAL; 185 } 186 187 /* Write to shadow if shadowed register*/ 188 switch (reg_bank) { 189 case 0: 190 switch (reg_addr) { 191 case 0: 192 /* 193 * Do not write the reset bit and restart autoneg bits 194 * to the shadow as these are self-cleared bits 195 */ 196 sr->mii_ctrl = phy_wr_data & ~(MII_CTRL_RESET | MII_CTRL_RAN); 197 break; 198 case 4: 199 sr->mii_ana = phy_wr_data; 200 break; 201 case 0x10: 202 sr->r1000x_ctrl1 = phy_wr_data; 203 break; 204 case 0x11: 205 sr->r1000x_ctrl2 = phy_wr_data; 206 break; 207 default: 208 shadow_write = 0; 209 break; 210 } 211 break; 212 default: 213 /* Invalid block */ 214 LOG_INFO(BSL_LS_SOC_PHY, 215 (BSL_META_U(unit, 216 "phy_serdes_5601x_reg_write: unexpected write to %x*%x: %04x\n"), 217 reg_bank, reg_addr, phy_wr_data)); 218 break; 219 } 220 221 if (shadow_write) { 222 LOG_INFO(BSL_LS_SOC_PHY, 223 (BSL_META_U(unit, 224 "phy_serdes_5601x_reg_write: shadow %x*%x: %04x\n"), 225 reg_bank, reg_addr, phy_wr_data)); 226 } else { 227 LOG_INFO(BSL_LS_SOC_PHY, 228 (BSL_META_U(unit, 229 "phy_serdes_5601x_reg_write: %x*%x: %04x\n"), 230 reg_bank, reg_addr, phy_wr_data)); 231 } 232 233 /* Write through to real device */ 234 rv = WRITE_PHY_REG(unit, pc, 0x1f, reg_bank); 235 if (SOC_SUCCESS(rv)) { 236 rv = WRITE_PHY_REG(unit, pc, reg_addr, phy_wr_data); 237 } 238 239 return rv; 240 } 241 242 STATIC int 243 phy_serdes_5601x_reg_modify(int unit, phy_ctrl_t *pc, 244 uint16 reg_bank, uint8 reg_addr, 245 uint16 phy_mo_data, uint16 phy_mo_mask) 246 { 247 uint16 tmp, otmp; 248 249 #ifdef INCLUDE_PHY_XGXS6 250 if (soc_feature(unit, soc_feature_xgxs_v6)) { 251 if ((reg_bank != 0) || (reg_addr >= 0x0010)) { 252 reg_bank = 0x300 + (reg_bank << 4); 253 } 254 } 255 #endif /* INCLUDE_PHY_XGXS6 */ 256 257 phy_mo_data &= phy_mo_mask; 258 SOC_IF_ERROR_RETURN 259 (phy_serdes_5601x_reg_read(unit, pc, reg_bank, reg_addr, &tmp)); 260 otmp = tmp; 261 tmp &= ~(phy_mo_mask); 262 tmp |= phy_mo_data; 263 264 if (tmp != otmp) { 265 SOC_IF_ERROR_RETURN 266 (phy_serdes_5601x_reg_write(unit, pc, reg_bank, 267 reg_addr, tmp)); 268 } 269 270 return SOC_E_NONE; 271 } 272 273 274 /*********************************************************************** 275 * 276 * HELPER FUNCTIONS 277 */ 278 279 /* 280 * Function: 281 * phy_56xxx_5601x_fiber_status_get 282 * Purpose: 283 * Retrieves whether port is in fiber or SGMII 284 * Parameters: 285 * unit - StrataSwitch unit #. 286 * pc - PHY state object. 287 * fiber_status - TRUE if fiber, else SGMII 288 * Returns: 289 * SOC_E_XXX 290 */ 291 292 STATIC int 293 _phy_56xxx_5601x_fiber_status_get(int unit, phy_ctrl_t *pc, uint16 *fiber_status) 294 { 295 uint16 fiber; 296 297 SOC_IF_ERROR_RETURN 298 (READ_SERDES_5601X_1000X_CTRL1r(unit, pc, &fiber)); 299 if (fiber & DDS_1000X_AUTO_DETECT) { 300 /* Get status from 1000X_STAT1r */ 301 SOC_IF_ERROR_RETURN 302 (READ_SERDES_5601X_1000X_STAT1r(unit, pc, &fiber)); 303 *fiber_status = !(fiber & DDS_1000X_STATUS1_SGMII_MODE); 304 } else { 305 /* Get from forced mode */ 306 *fiber_status = (fiber & DDS_1000X_FIBER_MODE); 307 } 308 309 return SOC_E_NONE; 310 } 311 312 313 /* 314 * Function: 315 * phy_56xxx_5601x_tx_control_set 316 * Purpose: 317 * Set preemphasis, driver current, and predriver current values 318 * Parameters: 319 * unit - StrataSwitch unit #. 320 * pc - PHY state object. 321 * port - StrataSwitch port #. 322 * Returns: 323 * SOC_E_NONE 324 */ 325 326 STATIC int 327 _phy_56xxx_5601x_tx_control_set(int unit, phy_ctrl_t *pc, 328 soc_port_t port) 329 { 330 uint32 preemph; 331 uint32 idriver; 332 uint32 pdriver; 333 uint16 data16; 334 int fiber; 335 336 fiber = PHY_FIBER_MODE(unit, port); 337 338 #ifdef INCLUDE_PHY_XGXS6 339 if (soc_feature(unit, soc_feature_xgxs_v6)) { 340 /* Unicore */ 341 /* Preemphasis overrided in gxmac.c */ 342 return SOC_E_NONE; 343 } else 344 #endif /* INCLUDE_PHY_XGXS6 */ 345 { 346 /* Default values for Dozen SerDes */ 347 if (fiber) { 348 idriver = 0xf; 349 pdriver = 0x7; 350 preemph = 0x0; 351 } else { 352 idriver = 0xc; 353 pdriver = 0x7; 354 preemph = 0x0; 355 } 356 } 357 358 /* Read config override */ 359 preemph = soc_property_port_get(unit, port, 360 spn_SERDES_PREEMPHASIS, preemph); 361 idriver = soc_property_port_get(unit, port, 362 spn_SERDES_DRIVER_CURRENT, idriver); 363 pdriver = soc_property_port_get(unit, port, 364 spn_SERDES_PRE_DRIVER_CURRENT, pdriver); 365 366 /* Dozen SerDes 367 * preemph[8:6], idriver[15:12], pdriver[11:9] 368 */ 369 preemph = (preemph << 6) & 0x01c0; 370 idriver = (idriver << 12) & 0xf000; 371 pdriver = (pdriver << 9) & 0x0e00; 372 data16 = (uint16)(preemph | idriver | pdriver); 373 374 /* If amplitude override is not enabled, the amplitude value is 375 * automatically adjusted when interface changes between SGMII and 376 * fiber(1000X). 377 */ 378 SOC_IF_ERROR_RETURN 379 (MODIFY_SERDES_5601X_1000X_CTRL1r(unit, pc, 380 DDS_1000X_TX_AMPLITUDE_OVRD, DDS_1000X_TX_AMPLITUDE_OVRD)); 381 382 /* Set ANALOG_TX with recommended amplitude */ 383 SOC_IF_ERROR_RETURN 384 (MODIFY_SERDES_5601X_ANALOG_TXr(unit, pc, data16, 0xffc0)); 385 386 return SOC_E_NONE; 387 } 388 389 /*********************************************************************** 390 * 391 * FIBER DRIVER ROUTINES 392 */ 393 STATIC int 394 phy_56xxx_5601x_init_no_reset(int unit, soc_port_t port) 395 { 396 uint16 data16, mask16; 397 soc_timeout_t to; 398 phy_ctrl_t *pc; 399 int rv; 400 401 pc = INT_PHY_SW_STATE(unit, port); 402 pc->fiber.enable = PHY_FIBER_MODE(unit, port); 403 pc->fiber.preferred = PHY_FIBER_MODE(unit, port); 404 pc->fiber.autoneg_enable = TRUE; 405 pc->fiber.autoneg_advert = ADVERT_ALL_FIBER; 406 pc->fiber.force_speed = 1000; 407 pc->fiber.force_duplex = TRUE; 408 pc->fiber.master = SOC_PORT_MS_NONE; 409 pc->fiber.mdix = SOC_PORT_MDIX_NORMAL; 410 411 SOC_IF_ERROR_RETURN(_phy_56xxx_5601x_sregs_init(pc)); 412 413 /* Since phy56xxx_5601x driver reset function vector does not reset 414 * the SerDes, reset the SerDes in initialization first. 415 * The internal SERDES PHY's reset bit is self-clearing. 416 */ 417 SOC_IF_ERROR_RETURN 418 (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, 419 MII_CTRL_RESET, MII_CTRL_RESET)); 420 421 soc_timeout_init(&to, 10000, 0); 422 while (!soc_timeout_check(&to)) { 423 rv = READ_SERDES_5601X_MII_CTRLr(unit, pc, &data16); 424 if (((data16 & MII_CTRL_RESET) == 0) || SOC_FAILURE(rv)) { 425 break; 426 } 427 } 428 if ((data16 & MII_CTRL_RESET) != 0) { 429 LOG_WARN(BSL_LS_SOC_PHY, 430 (BSL_META_U(unit, 431 "Combo SerDes reset failed: u=%d p=%d\n"), 432 unit, port)); 433 } 434 435 SOC_IF_ERROR_RETURN(_phy_56xxx_5601x_sregs_init(pc)); 436 437 data16 = 0; 438 mask16 = DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART; 439 if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) || 440 PHY_PASSTHRU_MODE(unit, port) || 441 PHY_SGMII_AUTONEG_MODE(unit, port)) { 442 data16 = DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART; 443 } 444 SOC_IF_ERROR_RETURN 445 (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, data16, mask16)); 446 447 /* Configure default preemphasis, predriver, idriver values */ 448 SOC_IF_ERROR_RETURN 449 (_phy_56xxx_5601x_tx_control_set(unit, pc, port)); 450 451 data16 = DDS_1000X_FALSE_LNK_DIS | DDS_1000X_FLT_FORCE_EN; 452 mask16 = DDS_1000X_FALSE_LNK_DIS | DDS_1000X_FLT_FORCE_EN; 453 SOC_IF_ERROR_RETURN 454 (MODIFY_SERDES_5601X_1000X_CTRL2r(unit, pc, data16, mask16)); 455 456 data16 = 0; 457 mask16 = DDS_1000X_AUTO_DETECT | DDS_1000X_FIBER_MODE; 458 /* 459 * Configure signal auto-detection between SGMII and fiber. 460 * This detection only works if auto-negotiation is enabled. 461 */ 462 if (soc_property_port_get(unit, port, 463 spn_SERDES_AUTOMEDIUM, FALSE)) { 464 data16 |= DDS_1000X_AUTO_DETECT; 465 } 466 /* 467 * Put the Serdes in Fiber or SGMII mode 468 */ 469 if ((PHY_FIBER_MODE(unit, port) && !PHY_EXTERNAL_MODE(unit, port)) || 470 PHY_PASSTHRU_MODE(unit, port)) { 471 if (soc_property_port_get(unit, port, 472 spn_SERDES_FIBER_PREF, TRUE)) { 473 data16 |= DDS_1000X_FIBER_MODE; 474 } 475 } 476 if (soc_property_port_get(unit, port, 477 spn_SERDES_SGMII_MASTER, FALSE)) { 478 data16 |= DDS_1000X_SGMII_MASTER; 479 mask16 |= DDS_1000X_SGMII_MASTER; 480 } 481 SOC_IF_ERROR_RETURN 482 (MODIFY_SERDES_5601X_1000X_CTRL1r(unit, pc, data16, mask16)); 483 484 if (PHY_COPPER_MODE(unit, port)) { 485 /* Do not wait 10ms for sync status before a valid link is 486 * established when auto-negotiation is disabled. 487 */ 488 SOC_IF_ERROR_RETURN 489 (MODIFY_SERDES_5601X_1000X_CTRL2r(unit, pc, 0x0000, 0x0004)); 490 } 491 492 LOG_INFO(BSL_LS_SOC_PHY, 493 (BSL_META_U(unit, 494 "phy_56xxx_5601x_init: u=%d p=%d %s\n"), 495 unit, port, 496 PHY_FIBER_MODE(unit, port) ? "Fiber" : "Copper")); 497 return SOC_E_NONE; 498 } 499 500 501 /* 502 * Function: 503 * phy_56xxx_5601x_init 504 * Purpose: 505 * Initialize 56xxx_5601x internal PHY driver 506 * Parameters: 507 * unit - StrataSwitch unit #. 508 * port - StrataSwitch port #. 509 * Returns: 510 * SOC_E_NONE 511 * Notes: 512 * This function implements the bulk part of phy_56xxx_5601x_init and 513 * provides a method for initializing the SerDes when it is used 514 * as an intermediate PHY between the MAC and an external PHY. 515 * In the latter case the PHY driver reset callback should be 516 * called only once, and this has already been done by the 517 * external PHY driver init function. 518 */ 519 520 STATIC int 521 phy_56xxx_5601x_init(int unit, soc_port_t port) 522 { 523 524 LOG_INFO(BSL_LS_SOC_PHY, 525 (BSL_META_U(unit, 526 "phy_56xxx_5601x_init: u=%d p=%d\n"), 527 unit, port)); 528 529 if (!PHY_EXTERNAL_MODE(unit, port)) { 530 /* PHY reset callbacks */ 531 SOC_IF_ERROR_RETURN(soc_phy_reset(unit, port)); 532 533 if (soc_property_port_get(unit, port, 534 spn_SERDES_FIBER_PREF, 1)) { 535 PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER); 536 } else { 537 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER); 538 } 539 540 } 541 SOC_IF_ERROR_RETURN 542 (phy_56xxx_5601x_init_no_reset(unit, port)); 543 544 return SOC_E_NONE; 545 } 546 547 /* 548 * Function: 549 * phy_56xxx_5601x_enable_set 550 * Purpose: 551 * Enable or disable the physical interface. 552 * Parameters: 553 * unit - StrataSwitch unit #. 554 * port - StrataSwitch port #. 555 * enable - Boolean, true = enable PHY, false = disable. 556 * Returns: 557 * SOC_E_XXX 558 * Notes: 559 * The PHY is held in reset when disabled. Also, the 560 * MISC_CONTROL.IDDQ bit must be set to 1 so the remote partner 561 * sees link down. 562 */ 563 564 STATIC int 565 phy_56xxx_5601x_enable_set(int unit, soc_port_t port, int enable) 566 { 567 LOG_INFO(BSL_LS_SOC_PHY, 568 (BSL_META_U(unit, 569 "phy_56xxx_5601x_enable_set: u=%d p=%d en=%d\n"), 570 unit, port, enable)); 571 if (enable) { 572 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_DISABLE); 573 574 SOC_IF_ERROR_RETURN 575 (_phy_56xxx_5601x_notify_resume(unit, port, PHY_STOP_PHY_DIS)); 576 } else { 577 PHY_FLAGS_SET(unit, port, PHY_FLAGS_DISABLE); 578 579 SOC_IF_ERROR_RETURN 580 (_phy_56xxx_5601x_notify_stop(unit, port, PHY_STOP_PHY_DIS)); 581 } 582 583 return SOC_E_NONE; 584 } 585 586 STATIC int 587 _phy_56xxx_5601x_sgmii_speed_set(int unit, soc_port_t port, int speed) 588 { 589 uint16 mii_ctrl; 590 phy_ctrl_t *pc; 591 592 pc = INT_PHY_SW_STATE(unit, port); 593 594 switch (speed) { 595 case 10: 596 mii_ctrl = DDS_MII_CTRL_SS_10; 597 break; 598 case 100: 599 mii_ctrl = DDS_MII_CTRL_SS_100; 600 break; 601 case 0: 602 case 1000: 603 mii_ctrl = DDS_MII_CTRL_SS_1000; 604 break; 605 default: 606 return SOC_E_CONFIG; 607 } 608 SOC_IF_ERROR_RETURN 609 (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, mii_ctrl, 610 (DDS_MII_CTRL_SS_LSB | DDS_MII_CTRL_SS_MSB))); 611 return SOC_E_NONE; 612 } 613 614 STATIC int 615 _phy_56xxx_5601x_1000x_speed_set(int unit, soc_port_t port, int speed) 616 { 617 if ((speed !=0) && (speed != 1000)) { 618 return SOC_E_CONFIG; 619 } 620 621 return SOC_E_NONE; 622 } 623 624 /* 625 * Function: 626 * phy_56xxx_5601x_speed_set 627 * Purpose: 628 * Set the current operating speed (forced). 629 * Parameters: 630 * unit - StrataSwitch unit #. 631 * port - StrataSwitch port #. 632 * duplex - (OUT) Boolean, true indicates full duplex, false 633 * indicates half. 634 * Returns: 635 * SOC_E_XXX 636 */ 637 638 STATIC int 639 phy_56xxx_5601x_speed_set(int unit, soc_port_t port, int speed) 640 { 641 uint16 fiber_status; 642 int rv; 643 phy_ctrl_t *pc; 644 645 pc = INT_PHY_SW_STATE(unit, port); 646 rv = SOC_E_NONE; 647 648 #ifdef INCLUDE_PHY_XGXS6 649 if (soc_feature(unit, soc_feature_xgxs_v6)) { 650 SOC_IF_ERROR_RETURN 651 (phy_xgxs6_hg.pd_speed_set(unit, port, speed)); 652 pc->fiber.force_speed = speed; 653 return SOC_E_NONE; 654 } 655 #endif /* INCLUDE_PHY_XGXS6 */ 656 657 SOC_IF_ERROR_RETURN 658 (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status)); 659 if (fiber_status == 0) { 660 rv = _phy_56xxx_5601x_sgmii_speed_set(unit, port, speed); 661 } else { 662 rv = _phy_56xxx_5601x_1000x_speed_set(unit, port, speed); 663 } 664 665 if (SOC_SUCCESS(rv)) { 666 pc->fiber.force_speed = speed; 667 } 668 669 LOG_INFO(BSL_LS_SOC_PHY, 670 (BSL_META_U(unit, 671 "phy_56xxx_5601x_speed_set: u=%d p=%d speed=%d rv=%d\n"), 672 unit, port, speed, rv)); 673 674 return rv; 675 } 676 677 STATIC int 678 _phy_56xxx_5601x_sgmii_speed_get(int unit, soc_port_t port, int *speed) 679 { 680 uint16 mii_ctrl, mii_stat, mii_anp; 681 phy_ctrl_t *pc; 682 683 pc = INT_PHY_SW_STATE(unit, port); 684 685 SOC_IF_ERROR_RETURN 686 (READ_SERDES_5601X_MII_CTRLr(unit, pc, &mii_ctrl)); 687 SOC_IF_ERROR_RETURN 688 (READ_SERDES_5601X_MII_STATr(unit, pc, &mii_stat)); 689 690 if (mii_ctrl & DDS_MII_CTRL_AN_ENABLE) { /*Auto-negotiation enabled */ 691 if (!(mii_stat & MII_STAT_AN_DONE)) { /* Auto-neg NOT complete */ 692 *speed = 0; 693 return SOC_E_NONE; 694 } else { /* Read Advertise link partner */ 695 SOC_IF_ERROR_RETURN 696 (READ_SERDES_5601X_MII_ANPr(unit, pc, &mii_anp)); 697 if (mii_anp & DDS_MII_ANP_SGMII) { 698 switch(mii_anp & DDS_MII_ANP_SGMII_SPEED) { 699 case DDS_MII_ANP_SGMII_SPEED_1000: 700 *speed = 1000; 701 break; 702 case DDS_MII_ANP_SGMII_SPEED_100: 703 *speed = 100; 704 break; 705 case DDS_MII_ANP_SGMII_SPEED_10: 706 *speed = 10; 707 break; 708 default: 709 return SOC_E_UNAVAIL; 710 } 711 } 712 } 713 } else { /* Forced speed */ 714 switch(mii_ctrl & (DDS_MII_CTRL_SS_LSB | DDS_MII_CTRL_SS_MSB)) { 715 case DDS_MII_CTRL_SS_10: 716 *speed = 10; 717 break; 718 case DDS_MII_CTRL_SS_100: 719 *speed = 100; 720 break; 721 case DDS_MII_CTRL_SS_1000: 722 *speed = 1000; 723 break; 724 default: 725 return SOC_E_UNAVAIL; 726 } 727 } 728 return SOC_E_NONE; 729 } 730 731 STATIC int 732 _phy_56xxx_5601x_1000x_speed_get(int unit, soc_port_t port, int *speed) 733 { 734 COMPILER_REFERENCE(unit); 735 COMPILER_REFERENCE(port); 736 737 *speed = 1000; 738 739 return SOC_E_NONE; 740 } 741 742 /* 743 * Function: 744 * phy_56xxx_5601x_speed_get 745 * Purpose: 746 * Get the current operating speed. If autoneg is enabled, 747 * then operating mode is returned, otherwise forced mode is returned. 748 * Parameters: 749 * unit - StrataSwitch unit #. 750 * port - StrataSwitch port #. 751 * duplex - (OUT) Boolean, true indicates full duplex, false 752 * indicates half. 753 * Returns: 754 * SOC_E_XXX 755 */ 756 757 STATIC int 758 phy_56xxx_5601x_speed_get(int unit, soc_port_t port, int *speed) 759 { 760 uint16 fiber_status; 761 phy_ctrl_t *pc; 762 763 pc = INT_PHY_SW_STATE(unit, port); 764 765 *speed = 1000; 766 767 #ifdef INCLUDE_PHY_XGXS6 768 if (soc_feature(unit, soc_feature_xgxs_v6)) { 769 return phy_xgxs6_hg.pd_speed_get(unit, port, speed); 770 } 771 #endif /* INCLUDE_PHY_XGXS6 */ 772 773 SOC_IF_ERROR_RETURN 774 (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status)); 775 if (fiber_status == 0) { 776 SOC_IF_ERROR_RETURN 777 (_phy_56xxx_5601x_sgmii_speed_get(unit, port, speed)); 778 } else { 779 SOC_IF_ERROR_RETURN 780 (_phy_56xxx_5601x_1000x_speed_get(unit, port, speed)); 781 } 782 783 return SOC_E_NONE; 784 } 785 786 /* 787 * Function: 788 * phy_56xxx_5601x_an_set 789 * Purpose: 790 * Enable or disabled auto-negotiation on the specified port. 791 * Parameters: 792 * unit - StrataSwitch unit #. 793 * port - StrataSwitch port #. 794 * an - Boolean, if true, auto-negotiation is enabled 795 * (and/or restarted). If false, autonegotiation is disabled. 796 * Returns: 797 * SOC_E_XXX 798 */ 799 800 STATIC int 801 phy_56xxx_5601x_an_set(int unit, soc_port_t port, int an) 802 { 803 phy_ctrl_t *pc; 804 uint16 par_det; 805 uint16 an_enable; 806 uint16 auto_det; 807 808 pc = INT_PHY_SW_STATE(unit, port); 809 810 LOG_INFO(BSL_LS_SOC_PHY, 811 (BSL_META_U(unit, 812 "phy_56xxx_5601x_an_set: u=%d p=%d an=%d\n"), 813 unit, port, an)); 814 /* 815 * Special case for BCM5421S being used in SGMII copper mode with 816 * BCM56xxx_5601x. Always have autoneg on. 817 */ 818 if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_5421S)) { 819 an = 1; 820 } 821 822 par_det = 0; 823 an_enable = 0; 824 auto_det = 0; 825 if (an) { 826 par_det = DDS_1000X_PAR_DET_EN; 827 an_enable = DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART; 828 if (soc_property_port_get(unit, port, 829 spn_SERDES_AUTOMEDIUM, FALSE)) { 830 auto_det = DDS_1000X_AUTO_DETECT; 831 } 832 } 833 834 SOC_IF_ERROR_RETURN 835 (MODIFY_SERDES_5601X_1000X_CTRL2r(unit, pc, 836 par_det, DDS_1000X_PAR_DET_EN)); 837 838 SOC_IF_ERROR_RETURN 839 (MODIFY_SERDES_5601X_1000X_CTRL1r(unit, pc, 840 auto_det, DDS_1000X_AUTO_DETECT)); 841 842 843 /* Enable/Disable and Restart autonegotiation (self-clearing bit) */ 844 SOC_IF_ERROR_RETURN 845 (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, an_enable, 846 DDS_MII_CTRL_AN_ENABLE | DDS_MII_CTRL_AN_RESTART)); 847 848 pc->fiber.autoneg_enable = an; 849 850 return SOC_E_NONE; 851 } 852 853 /* 854 * Function: 855 * phy_56xxx_5601x_adv_local_set 856 * Purpose: 857 * Set the current advertisement for auto-negotiation. 858 * Parameters: 859 * unit - StrataSwitch unit #. 860 * port - StrataSwitch port #. 861 * mode - Port mode mask indicating supported options/speeds. 862 * Returns: 863 * SOC_E_XXX 864 */ 865 STATIC int 866 phy_56xxx_5601x_adv_local_set(int unit, soc_port_t port, soc_port_mode_t mode) 867 { 868 #ifdef INCLUDE_PHY_XGXS6 869 if (soc_feature(unit, soc_feature_xgxs_v6)) { 870 mode &= SOC_PM_2500MB_FD | SOC_PM_1000MB_FD | 871 SOC_PM_PAUSE_TX | SOC_PM_PAUSE_RX; 872 return (phy_xgxs6_hg.pd_adv_local_set(unit, port, mode)); 873 } 874 #endif 875 876 return phy_serdes_adv_local_set(unit, port, mode); 877 } 878 879 /* 880 * Function: 881 * phy_56xxx_5601x_adv_local_get 882 * Purpose: 883 * Get the current advertisement for auto-negotiation. 884 * Parameters: 885 * unit - StrataSwitch unit #. 886 * port - StrataSwitch port #. 887 * mode - (OUT) Port mode mask indicating supported options/speeds. 888 * Returns: 889 * SOC_E_XXX 890 */ 891 892 STATIC int 893 phy_56xxx_5601x_adv_local_get(int unit, soc_port_t port, soc_port_mode_t *mode) 894 { 895 #ifdef INCLUDE_PHY_XGXS6 896 if (soc_feature(unit, soc_feature_xgxs_v6)) { 897 return phy_xgxs6_hg.pd_adv_local_get(unit, port, mode); 898 } 899 #endif /* INCLUDE_PHY_XGXS6 */ 900 901 return phy_serdes_adv_local_get(unit, port, mode); 902 } 903 904 STATIC int 905 _phy_56xxx5601x_sgmii_adv_remote_get(int unit, soc_port_t port, 906 soc_port_mode_t *mode) 907 { 908 uint16 anlpa; 909 soc_port_mode_t duplex; 910 phy_ctrl_t *pc; 911 912 pc = INT_PHY_SW_STATE(unit, port); 913 914 *mode = 0; 915 916 SOC_IF_ERROR_RETURN 917 (READ_SERDES_5601X_MII_ANPr(unit, pc, &anlpa)); 918 919 switch (anlpa & DDS_MII_ANP_SGMII_SPEED) { 920 case DDS_MII_ANP_SGMII_SPEED_10: 921 *mode = SOC_PM_10MB; 922 break; 923 case DDS_MII_ANP_SGMII_SPEED_100: 924 *mode = SOC_PM_100MB; 925 break; 926 case DDS_MII_ANP_SGMII_SPEED_1000: 927 *mode = SOC_PM_1000MB; 928 break; 929 } 930 931 duplex = (anlpa & DDS_MII_ANP_SGMII_DUPLEX) ? SOC_PM_FD : SOC_PM_HD; 932 933 *mode = (*mode) & duplex; 934 935 return SOC_E_NONE; 936 } 937 938 STATIC int 939 _phy_56xxx5601x_1000x_adv_remote_get(int unit, soc_port_t port, 940 soc_port_mode_t *mode) 941 { 942 uint16 anlpa; 943 phy_ctrl_t *pc; 944 945 pc = INT_PHY_SW_STATE(unit, port); 946 947 *mode = 0; 948 949 SOC_IF_ERROR_RETURN 950 (READ_SERDES_5601X_MII_ANPr(unit, pc, &anlpa)); 951 952 if (anlpa & DDS_MII_ANP_FIBER_FD) { 953 *mode |= SOC_PM_1000MB_FD; 954 } 955 956 if (anlpa & DDS_MII_ANP_FIBER_HD) { 957 *mode |= SOC_PM_1000MB_HD; 958 } 959 960 switch (anlpa & 961 (DDS_MII_ANP_FIBER_PAUSE_SYM | DDS_MII_ANP_FIBER_PAUSE_ASYM)) { 962 case DDS_MII_ANP_FIBER_PAUSE_SYM: 963 *mode |= SOC_PM_PAUSE_TX | SOC_PM_PAUSE_RX; 964 break; 965 case DDS_MII_ANP_FIBER_PAUSE_ASYM: 966 *mode |= SOC_PM_PAUSE_TX; 967 break; 968 case DDS_MII_ANP_FIBER_PAUSE_SYM | DDS_MII_ANP_FIBER_PAUSE_ASYM: 969 *mode |= SOC_PM_PAUSE_RX; 970 break; 971 } 972 973 return SOC_E_NONE; 974 } 975 976 /* 977 * Function: 978 * _phy_56xxx_5601x_adv_remote_get 979 * Purpose: 980 * Get partner's current advertisement for auto-negotiation. 981 * Parameters: 982 * unit - StrataSwitch unit #. 983 * port - StrataSwitch port #. 984 * mode - (OUT) Port mode mask indicating supported options/speeds. 985 * Returns: 986 * SOC_E_XXX 987 */ 988 989 STATIC int 990 _phy_56xxx_5601x_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode) 991 { 992 uint16 fiber_status; 993 phy_ctrl_t *pc; 994 995 pc = INT_PHY_SW_STATE(unit, port); 996 997 SOC_IF_ERROR_RETURN 998 (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status)); 999 if (fiber_status == 0) { 1000 SOC_IF_ERROR_RETURN 1001 (_phy_56xxx5601x_sgmii_adv_remote_get(unit, port, mode)); 1002 } else { 1003 SOC_IF_ERROR_RETURN 1004 (_phy_56xxx5601x_1000x_adv_remote_get(unit, port, mode)); 1005 } 1006 1007 LOG_INFO(BSL_LS_SOC_PHY, 1008 (BSL_META_U(unit, 1009 "phy_serdes_adv_remote_get: u=%d p=%d adv=%s%s%s\n"), 1010 unit, port, 1011 (*mode & SOC_PM_1000MB_FD) ? "1000MB " : "", 1012 (*mode & SOC_PM_PAUSE_TX) ? "PAUSE_TX " : "", 1013 (*mode & SOC_PM_PAUSE_RX) ? "PAUSE_TX " : "")); 1014 1015 return SOC_E_NONE; 1016 } 1017 1018 /* 1019 * Function: 1020 * phy_56xxx_5601x_adv_remote_get 1021 * Purpose: 1022 * Get partner's current advertisement for auto-negotiation. 1023 * Parameters: 1024 * unit - StrataSwitch unit #. 1025 * port - StrataSwitch port #. 1026 * mode - (OUT) Port mode mask indicating supported options/speeds. 1027 * Returns: 1028 * SOC_E_XXX 1029 */ 1030 1031 STATIC int 1032 phy_56xxx_5601x_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode) 1033 { 1034 #ifdef INCLUDE_PHY_XGXS6 1035 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1036 return phy_xgxs6_hg.pd_adv_remote_get(unit, port, mode); 1037 } 1038 #endif /* INCLUDE_PHY_XGXS6 */ 1039 1040 return _phy_56xxx_5601x_adv_remote_get(unit, port, mode); 1041 } 1042 1043 /* 1044 * Function: 1045 * phy_56xxx_5601x_lb_set 1046 * Purpose: 1047 * Set the internal PHY loopback mode. 1048 * Parameters: 1049 * unit - StrataSwitch unit #. 1050 * port - StrataSwitch port #. 1051 * loopback - Boolean: true = enable loopback, false = disable. 1052 * Returns: 1053 * SOC_E_XXX 1054 */ 1055 1056 STATIC int 1057 phy_56xxx_5601x_lb_set(int unit, soc_port_t port, int enable) 1058 { 1059 uint16 ctrl; 1060 phy_ctrl_t *pc; 1061 int rv; 1062 1063 #ifdef INCLUDE_PHY_XGXS6 1064 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1065 return phy_xgxs6_hg.pd_lb_set(unit, port, enable); 1066 } 1067 #endif /* INCLUDE_PHY_XGXS6 */ 1068 1069 pc = INT_PHY_SW_STATE(unit, port); 1070 1071 ctrl = (enable) ? DDS_MII_CTRL_LOOPBACK : 0; 1072 1073 rv = MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, ctrl, DDS_MII_CTRL_LOOPBACK); 1074 1075 LOG_INFO(BSL_LS_SOC_PHY, 1076 (BSL_META_U(unit, 1077 "phy_56xxx_5601x_lb_set: u=%d p=%d lb=%d rv=%d\n"), 1078 unit, port, enable, rv)); 1079 return rv; 1080 } 1081 1082 /* 1083 * Function: 1084 * phy_56xxx_5601x_ability_get 1085 * Purpose: 1086 * Get the abilities of the internal PHY. 1087 * Parameters: 1088 * unit - StrataSwitch unit #. 1089 * port - StrataSwitch port #. 1090 * mode - (OUT) Port mode mask indicating supported options/speeds. 1091 * Returns: 1092 * SOC_E_XXX 1093 */ 1094 1095 STATIC int 1096 phy_56xxx_5601x_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode) 1097 { 1098 uint16 fiber_status; 1099 phy_ctrl_t *pc; 1100 pc = INT_PHY_SW_STATE(unit, port); 1101 1102 SOC_IF_ERROR_RETURN 1103 (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status)); 1104 if (fiber_status == 0) { 1105 *mode = (SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB_FD | 1106 SOC_PM_LB_PHY | SOC_PM_GMII); 1107 } else { 1108 *mode = (SOC_PM_1000MB_FD | SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM | 1109 SOC_PM_AN | SOC_PM_LB_PHY | SOC_PM_GMII); 1110 } 1111 1112 #ifdef INCLUDE_PHY_XGXS6 1113 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1114 *mode |= SOC_PM_2500MB_FD; 1115 } 1116 #endif /* INCLUDE_PHY_XGXS6 */ 1117 1118 1119 return SOC_E_NONE; 1120 } 1121 1122 /* 1123 * Function: 1124 * _phy_56xxx_5601x_medium_config_update 1125 * Purpose: 1126 * Update the PHY with config parameters 1127 * Parameters: 1128 * unit - StrataSwitch unit #. 1129 * port - StrataSwitch port #. 1130 * cfg - Config structure. 1131 * Returns: 1132 * SOC_E_XXX 1133 */ 1134 1135 STATIC int 1136 _phy_56xxx_5601x_medium_config_update(int unit, soc_port_t port, 1137 soc_phy_config_t *cfg) 1138 { 1139 SOC_IF_ERROR_RETURN 1140 (phy_56xxx_5601x_speed_set(unit, port, cfg->force_speed)); 1141 SOC_IF_ERROR_RETURN 1142 (phy_serdes_duplex_set(unit, port, cfg->force_duplex)); 1143 SOC_IF_ERROR_RETURN 1144 (phy_56xxx_5601x_adv_local_set(unit, port, cfg->autoneg_advert)); 1145 SOC_IF_ERROR_RETURN 1146 (phy_56xxx_5601x_an_set(unit, port, cfg->autoneg_enable)); 1147 1148 return SOC_E_NONE; 1149 } 1150 1151 /* 1152 * Function: 1153 * phy_56xxx_5601x_medium_config_set 1154 * Purpose: 1155 * Set the operating parameters that are automatically selected 1156 * when medium switches type. 1157 * Parameters: 1158 * unit - StrataSwitch unit # 1159 * port - Port number 1160 * medium - SOC_PORT_MEDIUM_COPPER/FIBER 1161 * cfg - Operating parameters 1162 * Returns: 1163 * SOC_E_XXX 1164 */ 1165 1166 STATIC int 1167 phy_56xxx_5601x_medium_config_set(int unit, soc_port_t port, 1168 soc_port_medium_t medium, 1169 soc_phy_config_t *cfg) 1170 { 1171 phy_ctrl_t *pc; 1172 1173 pc = INT_PHY_SW_STATE(unit, port); 1174 1175 /* 1176 * Changes take effect immediately if the target medium is active. 1177 */ 1178 switch (medium) { 1179 case SOC_PORT_MEDIUM_FIBER: 1180 /* Change the fiber modes */ 1181 sal_memcpy(&pc->fiber, cfg, sizeof (pc->fiber)); 1182 1183 pc->fiber.autoneg_advert &= ADVERT_ALL_FIBER; 1184 if (PHY_FIBER_MODE(unit, port)) { 1185 SOC_IF_ERROR_RETURN 1186 (_phy_56xxx_5601x_medium_config_update(unit, port, &pc->fiber)); 1187 } 1188 return SOC_E_NONE; 1189 case SOC_PORT_MEDIUM_COPPER: 1190 return SOC_E_UNAVAIL; 1191 default: 1192 return SOC_E_PARAM; 1193 } 1194 } 1195 1196 STATIC int 1197 _phy_56xxx_5601x_control_tx_driver_set(int unit, soc_port_t port, 1198 soc_phy_control_t type, uint32 value) 1199 { 1200 uint16 data; /* Temporary holder of reg value to be written */ 1201 uint16 mask; /* Bit mask of reg value to be updated */ 1202 phy_ctrl_t *pc; /* PHY software state */ 1203 1204 pc = INT_PHY_SW_STATE(unit, port); 1205 1206 /* Dozen SerDes */ 1207 switch(type) { 1208 case SOC_PHY_CONTROL_PREEMPHASIS: 1209 data = (uint16)(value & 0x7); 1210 data = data << 6; 1211 mask = 0x01C0; 1212 break; 1213 case SOC_PHY_CONTROL_DRIVER_CURRENT: 1214 data = (uint16)(value & 0xF); 1215 data = data << 12; 1216 mask = 0xF000; 1217 break; 1218 case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT: 1219 data = (uint16)(value & 0x7); 1220 data = data << 9; 1221 mask = 0x0E00; 1222 break; 1223 default: 1224 /* should never get here */ 1225 return SOC_E_PARAM; 1226 } 1227 SOC_IF_ERROR_RETURN 1228 (MODIFY_SERDES_5601X_ANALOG_TXr(unit, pc, data, mask)); 1229 1230 return SOC_E_NONE; 1231 } 1232 1233 STATIC int 1234 _phy_56xxx_5601x_control_tx_driver_get(int unit, soc_port_t port, 1235 soc_phy_control_t type, uint32 *value) 1236 { 1237 uint16 data16; /* Temporary holder of a reg value */ 1238 phy_ctrl_t *pc; /* PHY software state */ 1239 1240 pc = INT_PHY_SW_STATE(unit, port); 1241 1242 data16 = 0; 1243 /* Dozen SerDes */ 1244 SOC_IF_ERROR_RETURN 1245 (READ_SERDES_5601X_ANALOG_TXr(unit, pc, &data16)); 1246 switch(type) { 1247 case SOC_PHY_CONTROL_PREEMPHASIS: 1248 *value = (data16 & 0x01C0) >> 6; 1249 break; 1250 case SOC_PHY_CONTROL_DRIVER_CURRENT: 1251 *value = (data16 & 0xF000) >> 12; 1252 break; 1253 case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT: 1254 *value = (data16 & 0x0E00) >> 9; 1255 break; 1256 default: 1257 /* should never get here */ 1258 return SOC_E_PARAM; 1259 } 1260 1261 return SOC_E_NONE; 1262 } 1263 1264 /* 1265 * Function: 1266 * phy_phy56xxx_5601x_control_set 1267 * Purpose: 1268 * Configure PHY device specific control fucntion. 1269 * Parameters: 1270 * unit - StrataSwitch unit #. 1271 * port - StrataSwitch port #. 1272 * type - Control to update 1273 * value - New setting for the control 1274 * Returns: 1275 * SOC_E_NONE 1276 */ 1277 STATIC int 1278 phy_56xxx_5601x_control_set(int unit, soc_port_t port, 1279 soc_phy_control_t type, uint32 value) 1280 { 1281 int rv; 1282 1283 #ifdef INCLUDE_PHY_XGXS6 1284 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1285 return (phy_xgxs6_hg.pd_control_set(unit, port, type, value)); 1286 } 1287 #endif 1288 1289 PHY_CONTROL_TYPE_CHECK(type); 1290 1291 rv = SOC_E_UNAVAIL; 1292 switch(type) { 1293 case SOC_PHY_CONTROL_PREEMPHASIS: 1294 /* fall through */ 1295 case SOC_PHY_CONTROL_DRIVER_CURRENT: 1296 /* fall through */ 1297 case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT: 1298 rv = _phy_56xxx_5601x_control_tx_driver_set(unit, port, type, value); 1299 break; 1300 default: 1301 rv = SOC_E_UNAVAIL; 1302 break; 1303 } 1304 return rv; 1305 } 1306 /* 1307 * Function: 1308 * phy_56xxx_5601x_control_get 1309 * Purpose: 1310 * Get current control settign of the PHY. 1311 * Parameters: 1312 * unit - StrataSwitch unit #. 1313 * port - StrataSwitch port #. 1314 * type - Control to update 1315 * value - (OUT)Current setting for the control 1316 * Returns: 1317 * SOC_E_NONE 1318 */ 1319 STATIC int 1320 phy_56xxx_5601x_control_get(int unit, soc_port_t port, 1321 soc_phy_control_t type, uint32 *value) 1322 { 1323 int rv; 1324 1325 if (NULL == value) { 1326 return SOC_E_PARAM; 1327 } 1328 1329 #ifdef INCLUDE_PHY_XGXS6 1330 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1331 return (phy_xgxs6_hg.pd_control_get(unit, port, type, value)); 1332 } 1333 #endif /* INCLUDE_PHY_XGXS6 */ 1334 1335 PHY_CONTROL_TYPE_CHECK(type); 1336 1337 rv = SOC_E_UNAVAIL; 1338 switch(type) { 1339 case SOC_PHY_CONTROL_PREEMPHASIS: 1340 /* fall through */ 1341 case SOC_PHY_CONTROL_DRIVER_CURRENT: 1342 /* fall through */ 1343 case SOC_PHY_CONTROL_PRE_DRIVER_CURRENT: 1344 rv = _phy_56xxx_5601x_control_tx_driver_get(unit, port, type, value); 1345 break; 1346 default: 1347 rv = SOC_E_UNAVAIL; 1348 break; 1349 } 1350 1351 return rv; 1352 } 1353 1354 /* 1355 * Function: 1356 * phy_56xxx_5601x_reg_read 1357 * Purpose: 1358 * Routine to read PHY register 1359 * Parameters: 1360 * uint - BCM unit number 1361 * pc - PHY state 1362 * flags - Flags which specify the register type 1363 * phy_reg_addr - Encoded register address 1364 * phy_data - (OUT) Value read from PHY register 1365 * Note: 1366 * This register read function is not thread safe. Higher level 1367 * function that calls this function must obtain a per port lock 1368 * to avoid overriding register page mapping between threads. 1369 */ 1370 STATIC int 1371 phy_56xxx_5601x_reg_read(int unit, soc_port_t port, uint32 flags, 1372 uint32 phy_reg_addr, uint32 *phy_data) 1373 { 1374 uint16 reg_bank; 1375 uint8 reg_addr; 1376 uint16 data; /* Temporary holder for phy_data */ 1377 phy_ctrl_t *pc; /* PHY software state */ 1378 1379 #ifdef INCLUDE_PHY_XGXS6 1380 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1381 return (phy_xgxs6_hg.pd_reg_read(unit, port, flags, 1382 phy_reg_addr, phy_data)); 1383 } 1384 #endif /* INCLUDE_PHY_XGXS6 */ 1385 1386 pc = INT_PHY_SW_STATE(unit, port); 1387 1388 reg_bank = SOC_PHY_REG_BANK(phy_reg_addr); 1389 reg_addr = SOC_PHY_REG_ADDR(phy_reg_addr); 1390 1391 SOC_IF_ERROR_RETURN 1392 (phy_reg_serdes_read(unit, pc, reg_bank, reg_addr, &data)); 1393 1394 *phy_data = data; 1395 1396 return SOC_E_NONE; 1397 } 1398 1399 /* 1400 * Function: 1401 * phy_56xxx_5601x_reg_write 1402 * Purpose: 1403 * Routine to write PHY register 1404 * Parameters: 1405 * uint - BCM unit number 1406 * pc - PHY state 1407 * flags - Flags which specify the register type 1408 * phy_reg_addr - Encoded register address 1409 * phy_data - Value write to PHY register 1410 * Note: 1411 * This register read function is not thread safe. Higher level 1412 * function that calls this function must obtain a per port lock 1413 * to avoid overriding register page mapping between threads. 1414 */ 1415 STATIC int 1416 phy_56xxx_5601x_reg_write(int unit, soc_port_t port, uint32 flags, 1417 uint32 phy_reg_addr, uint32 phy_data) 1418 { 1419 uint16 reg_bank; 1420 uint8 reg_addr; 1421 uint16 data; /* Temporary holder for phy_data */ 1422 phy_ctrl_t *pc; /* PHY software state */ 1423 1424 #ifdef INCLUDE_PHY_XGXS6 1425 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1426 return (phy_xgxs6_hg.pd_reg_write(unit, port, flags, 1427 phy_reg_addr, phy_data)); 1428 } 1429 #endif /* INCLUDE_PHY_XGXS6 */ 1430 1431 pc = INT_PHY_SW_STATE(unit, port); 1432 1433 data = (uint16) (phy_data & 0x0000FFFF); 1434 reg_bank = SOC_PHY_REG_BANK(phy_reg_addr); 1435 reg_addr = SOC_PHY_REG_ADDR(phy_reg_addr); 1436 1437 SOC_IF_ERROR_RETURN 1438 (phy_reg_serdes_write(unit, pc, reg_bank, reg_addr, data)); 1439 1440 return SOC_E_NONE; 1441 } 1442 1443 /* 1444 * Function: 1445 * phy_56xxx_5601x_reg_modify 1446 * Purpose: 1447 * Routine to write PHY register 1448 * Parameters: 1449 * uint - BCM unit number 1450 * pc - PHY state 1451 * flags - Flags which specify the register type 1452 * phy_reg_addr - Encoded register address 1453 * phy_mo_data - New value for the bits specified in phy_mo_mask 1454 * phy_mo_mask - Bit mask to modify 1455 * Note: 1456 * This register read function is not thread safe. Higher level 1457 * function that calls this function must obtain a per port lock 1458 * to avoid overriding register page mapping between threads. 1459 */ 1460 STATIC int 1461 phy_56xxx_5601x_reg_modify(int unit, soc_port_t port, uint32 flags, 1462 uint32 phy_reg_addr, uint32 phy_data, uint32 phy_data_mask) 1463 { 1464 uint16 reg_bank; 1465 uint8 reg_addr; 1466 uint16 data; /* Temporary holder for phy_data */ 1467 uint16 mask; 1468 phy_ctrl_t *pc; /* PHY software state */ 1469 1470 #ifdef INCLUDE_PHY_XGXS6 1471 if (soc_feature(unit, soc_feature_xgxs_v6)) { 1472 return (phy_xgxs6_hg.pd_reg_modify(unit, port, flags, 1473 phy_reg_addr, phy_data, phy_data_mask)); 1474 } 1475 #endif /* INCLUDE_PHY_XGXS6 */ 1476 1477 pc = INT_PHY_SW_STATE(unit, port); 1478 1479 data = (uint16) (phy_data & 0x0000FFFF); 1480 mask = (uint16) (phy_data_mask & 0x0000FFFF); 1481 reg_bank = SOC_PHY_REG_BANK(phy_reg_addr); 1482 reg_addr = SOC_PHY_REG_ADDR(phy_reg_addr); 1483 1484 SOC_IF_ERROR_RETURN 1485 (phy_reg_serdes_modify(unit, pc, reg_bank, reg_addr, data, mask)); 1486 1487 return SOC_E_NONE; 1488 } 1489 1490 STATIC int 1491 phy_56xxx_5601x_notify(int unit, soc_port_t port, 1492 soc_phy_event_t event, uint32 value) 1493 { 1494 int rv; 1495 1496 if (event >= phyEventCount) { 1497 return SOC_E_PARAM; 1498 } 1499 1500 rv = SOC_E_NONE; 1501 switch(event) { 1502 case phyEventInterface: 1503 rv = (_phy_56xxx_5601x_notify_interface(unit, port, value)); 1504 break; 1505 case phyEventDuplex: 1506 rv = (_phy_56xxx_5601x_notify_duplex(unit, port, value)); 1507 break; 1508 case phyEventSpeed: 1509 rv = (_phy_56xxx_5601x_notify_speed(unit, port, value)); 1510 break; 1511 case phyEventStop: 1512 rv = (_phy_56xxx_5601x_notify_stop(unit, port, value)); 1513 break; 1514 case phyEventResume: 1515 rv = (_phy_56xxx_5601x_notify_resume(unit, port, value)); 1516 break; 1517 default: 1518 rv = SOC_E_PARAM; 1519 break; 1520 } 1521 1522 return rv; 1523 } 1524 1525 STATIC int 1526 phy_56xxx_5601x_duplex_get(int unit, soc_port_t port, int *duplex) 1527 { 1528 uint16 reg0_16; 1529 uint16 mii_ctrl; 1530 uint16 fiber_status; 1531 phy_ctrl_t *pc; 1532 1533 pc = INT_PHY_SW_STATE(unit, port); 1534 1535 *duplex = TRUE; 1536 1537 SOC_IF_ERROR_RETURN 1538 (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status)); 1539 if (fiber_status == 0) { 1540 /* retrieve the duplex setting in SGMII mode */ 1541 1542 SOC_IF_ERROR_RETURN 1543 (READ_SERDES_5601X_MII_CTRLr(unit, pc, &mii_ctrl)); 1544 1545 if (mii_ctrl & DDS_MII_CTRL_AN_ENABLE) { 1546 SOC_IF_ERROR_RETURN 1547 (READ_SERDES_5601X_MII_ANPr(unit,pc,®0_16)); 1548 1549 /* make sure link partner is also in SGMII mode 1550 * otherwise fall through to use the FD bit in MII_CTRL reg 1551 */ 1552 if (reg0_16 & DDS_MII_ANP_SGMII) { 1553 1554 /* test shows the duplex setting of external phy is 1555 * propagated to the AN link partner register 1556 */ 1557 if (reg0_16 & DDS_MII_ANP_SGMII_DUPLEX) { 1558 *duplex = TRUE; 1559 return SOC_E_NONE; 1560 } else { 1561 *duplex = FALSE; 1562 return SOC_E_NONE; 1563 } 1564 } 1565 } 1566 1567 *duplex = (mii_ctrl & DDS_MII_CTRL_FD) ? TRUE : FALSE; 1568 } 1569 1570 return SOC_E_NONE; 1571 } 1572 1573 STATIC int 1574 phy_56xxx_5601x_an_get(int unit, soc_port_t port, int *an, int *an_done) 1575 { 1576 uint16 ctrl, stat; 1577 phy_ctrl_t *pc; 1578 1579 pc = INT_PHY_SW_STATE(unit, port); 1580 1581 SOC_IF_ERROR_RETURN 1582 (READ_SERDES_5601X_MII_CTRLr(unit, pc, &ctrl)); 1583 1584 *an = (ctrl & MII_CTRL_AE) != 0; 1585 1586 SOC_IF_ERROR_RETURN 1587 (READ_SERDES_5601X_MII_STATr(unit, pc, &stat)); 1588 1589 *an_done = (stat & MII_STAT_AN_DONE) != 0; 1590 1591 LOG_INFO(BSL_LS_SOC_PHY, 1592 (BSL_META_U(unit, 1593 "phy_phy56xxx_5601x_an_get: u=%d p=%d an=%d an_done=%d\n"), 1594 unit, port, *an, *an_done)); 1595 1596 return SOC_E_NONE; 1597 } 1598 1599 STATIC int 1600 phy_56xxx_5601x_lb_get(int unit, soc_port_t port, int *enable) 1601 { 1602 uint16 ctrl; 1603 phy_ctrl_t *pc; 1604 1605 pc = INT_PHY_SW_STATE(unit, port); 1606 SOC_IF_ERROR_RETURN 1607 (READ_SERDES_5601X_MII_CTRLr(unit, pc, &ctrl)); 1608 1609 *enable = (ctrl & MII_CTRL_LE) != 0; 1610 1611 return SOC_E_NONE; 1612 } 1613 1614 1615 /* 1616 * Variable: phy_56xxx_5601xdrv_ge 1617 * Purpose: PHY Driver for Dozen SerDes Serdes PHY internal to Draco, 1618 */ 1619 phy_driver_t phy_56xxx_5601x_drv_ge = { 1620 "56XXX_5601X Internal SERDES PHY Driver", 1621 phy_56xxx_5601x_init, 1622 phy_null_reset, 1623 phy_serdes_link_get, 1624 phy_56xxx_5601x_enable_set, 1625 phy_null_enable_get, 1626 phy_serdes_duplex_set, 1627 phy_56xxx_5601x_duplex_get, /* duplex_get */ 1628 phy_56xxx_5601x_speed_set, 1629 phy_56xxx_5601x_speed_get, 1630 phy_serdes_master_set, 1631 phy_serdes_master_get, 1632 phy_56xxx_5601x_an_set, /* local */ 1633 phy_56xxx_5601x_an_get, /* local */ 1634 phy_56xxx_5601x_adv_local_set, 1635 phy_56xxx_5601x_adv_local_get, 1636 phy_56xxx_5601x_adv_remote_get, 1637 phy_56xxx_5601x_lb_set, 1638 phy_56xxx_5601x_lb_get, 1639 phy_serdes_interface_set, 1640 phy_serdes_interface_get, 1641 phy_56xxx_5601x_ability_get, 1642 NULL, 1643 NULL, 1644 phy_null_mdix_set, 1645 phy_null_mdix_get, 1646 phy_null_mdix_status_get, 1647 phy_56xxx_5601x_medium_config_set, 1648 phy_serdes_medium_config_get, 1649 phy_serdes_medium_status, 1650 NULL, /* phy_cable_diag */ 1651 NULL, /* phy_link_change */ 1652 phy_56xxx_5601x_control_set, /* phy_control_set */ 1653 phy_56xxx_5601x_control_get, /* phy_control_get */ 1654 phy_56xxx_5601x_reg_read, 1655 phy_56xxx_5601x_reg_write, 1656 phy_56xxx_5601x_reg_modify, 1657 phy_56xxx_5601x_notify 1658 }; 1659 1660 /*********************************************************************** 1661 * 1662 * PASS-THROUGH NOTIFY ROUTINES 1663 */ 1664 1665 /* 1666 * Function: 1667 * phy_56xxx_5601x_notify_duplex 1668 * Purpose: 1669 * Program duplex if (and only if) 56xxx_5601x is an intermediate PHY. 1670 * Parameters: 1671 * unit - StrataSwitch unit #. 1672 * port - StrataSwitch port #. 1673 * duplex - Boolean, TRUE indicates full duplex, FALSE 1674 * indicates half. 1675 * Returns: 1676 * SOC_E_XXX 1677 * Notes: 1678 * If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to 1679 * talk directly to an external fiber module. 1680 * 1681 * If PHY_FLAGS_FIBER is clear, the PHY is being used in 1682 * pass-through mode to talk to an external SGMII PHY. 1683 * 1684 * When used in SGMII mode, autoneg must be turned off and 1685 * the speed/duplex forced to match that of the external PHY. 1686 */ 1687 STATIC int 1688 _phy_56xxx_5601x_notify_duplex(int unit, soc_port_t port, uint32 duplex) 1689 { 1690 int fiber; 1691 uint16 mii_ctrl; 1692 phy_ctrl_t *pc; 1693 1694 fiber = PHY_FIBER_MODE(unit, port); 1695 pc = INT_PHY_SW_STATE(unit, port); 1696 LOG_INFO(BSL_LS_SOC_PHY, 1697 (BSL_META_U(unit, 1698 "phy_56xxx_5601x_notify_duplex: u=%d p=%d duplex=%d fiber=%d\n"), 1699 unit, port, duplex, fiber)); 1700 1701 if (SAL_BOOT_SIMULATION) { 1702 return SOC_E_NONE; 1703 } 1704 1705 if (fiber) { 1706 SOC_IF_ERROR_RETURN 1707 (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, 1708 DDS_MII_CTRL_FD, DDS_MII_CTRL_FD)); 1709 return SOC_E_NONE; 1710 } 1711 1712 /* Put SERDES PHY in reset */ 1713 1714 SOC_IF_ERROR_RETURN 1715 (_phy_56xxx_5601x_notify_stop(unit, port, PHY_STOP_DUPLEX_CHG)); 1716 1717 /* Update duplexity */ 1718 mii_ctrl = (duplex) ? DDS_MII_CTRL_FD : 0; 1719 SOC_IF_ERROR_RETURN 1720 (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, mii_ctrl, DDS_MII_CTRL_FD)); 1721 1722 /* Take SERDES PHY out of reset */ 1723 SOC_IF_ERROR_RETURN 1724 (_phy_56xxx_5601x_notify_resume(unit, port, PHY_STOP_DUPLEX_CHG)); 1725 1726 /* Autonegotiation must be turned off to talk to external PHY if 1727 * SGMII autoneg is not enabled. 1728 */ 1729 if (!PHY_SGMII_AUTONEG_MODE(unit, port)) { 1730 SOC_IF_ERROR_RETURN 1731 (phy_56xxx_5601x_an_set(unit, port, FALSE)); 1732 } 1733 return SOC_E_NONE; 1734 } 1735 1736 /* 1737 * Function: 1738 * phy_56xxx_5601x_notify_speed 1739 * Purpose: 1740 * Program duplex if (and only if) 56xxx_5601x is an intermediate PHY. 1741 * Parameters: 1742 * unit - StrataSwitch unit #. 1743 * port - StrataSwitch port #. 1744 * speed - Speed to program. 1745 * Returns: 1746 * SOC_E_XXX 1747 * Notes: 1748 * If PHY_FLAGS_FIBER is set, it indicates the PHY is being used to 1749 * talk directly to an external fiber module. 1750 * 1751 * If PHY_FLAGS_FIBER is clear, the PHY is being used in 1752 * pass-through mode to talk to an external SGMII PHY. 1753 * 1754 * When used in pass-through mode, autoneg must be turned off and 1755 * the speed/duplex forced to match that of the external PHY. 1756 */ 1757 1758 STATIC int 1759 _phy_56xxx_5601x_notify_speed(int unit, soc_port_t port, uint32 speed) 1760 { 1761 int fiber; 1762 uint16 fiber_status; 1763 phy_ctrl_t *pc; 1764 1765 pc = INT_PHY_SW_STATE(unit, port); 1766 fiber = PHY_FIBER_MODE(unit, port); 1767 1768 if (SAL_BOOT_SIMULATION) { 1769 return SOC_E_NONE; 1770 } 1771 SOC_IF_ERROR_RETURN 1772 (_phy_56xxx_5601x_fiber_status_get(unit, pc, &fiber_status)); 1773 if (fiber && fiber_status) { 1774 if ((speed != 0) && (speed != 1000)) { 1775 return SOC_E_CONFIG; 1776 } 1777 } 1778 1779 /* Put SERDES PHY in reset */ 1780 SOC_IF_ERROR_RETURN 1781 (_phy_56xxx_5601x_notify_stop(unit, port, PHY_STOP_SPEED_CHG)); 1782 1783 /* Update speed */ 1784 SOC_IF_ERROR_RETURN 1785 (_phy_56xxx_5601x_sgmii_speed_set(unit, port, (int)speed)); 1786 1787 /* Take SERDES PHY out of reset */ 1788 SOC_IF_ERROR_RETURN 1789 (_phy_56xxx_5601x_notify_resume(unit, port, PHY_STOP_SPEED_CHG)); 1790 1791 /* Autonegotiation must be turned off to talk to external PHY */ 1792 if (!PHY_SGMII_AUTONEG_MODE(unit, port) && !fiber) { 1793 SOC_IF_ERROR_RETURN 1794 (phy_56xxx_5601x_an_set(unit, port, FALSE)); 1795 } 1796 1797 return SOC_E_NONE; 1798 } 1799 1800 /* 1801 * Function: 1802 * _phy_56xxx_5601x_stop 1803 * Purpose: 1804 * Put 56xxx_5601x SERDES in or out of reset depending on conditions 1805 */ 1806 1807 STATIC int 1808 _phy_56xxx_5601x_stop(int unit, soc_port_t port) 1809 { 1810 uint16 mii_ctrl; 1811 int stop, copper; 1812 phy_ctrl_t *pc; 1813 1814 pc = INT_PHY_SW_STATE(unit, port); 1815 1816 copper = (pc->stop & 1817 PHY_STOP_COPPER) != 0; 1818 1819 stop = ((pc->stop & 1820 (PHY_STOP_PHY_DIS | 1821 PHY_STOP_DRAIN)) != 0 || 1822 (copper && 1823 (pc->stop & 1824 (PHY_STOP_MAC_DIS | 1825 PHY_STOP_DUPLEX_CHG | 1826 PHY_STOP_SPEED_CHG)) != 0)); 1827 1828 LOG_INFO(BSL_LS_SOC_PHY, 1829 (BSL_META_U(unit, 1830 "phy_56xxx_5601x_stop: u=%d p=%d copper=%d stop=%d flg=0x%x\n"), 1831 unit, port, copper, stop, 1832 pc->stop)); 1833 1834 mii_ctrl = (stop) ? DDS_MII_CTRL_PD : 0; 1835 SOC_IF_ERROR_RETURN 1836 (MODIFY_SERDES_5601X_MII_CTRLr(unit, pc, mii_ctrl, DDS_MII_CTRL_PD)); 1837 1838 return SOC_E_NONE; 1839 } 1840 1841 /* 1842 * Function: 1843 * phy_56xxx_5601x_notify_stop 1844 * Purpose: 1845 * Add a reason to put 56xxx_5601x PHY in reset. 1846 * Parameters: 1847 * unit - StrataSwitch unit #. 1848 * port - StrataSwitch port #. 1849 * flags - Reason to stop 1850 * Returns: 1851 * SOC_E_XXX 1852 */ 1853 STATIC int 1854 _phy_56xxx_5601x_notify_stop(int unit, soc_port_t port, uint32 flags) 1855 { 1856 phy_ctrl_t *pc; 1857 1858 pc = INT_PHY_SW_STATE(unit, port); 1859 1860 pc->stop |= flags; 1861 1862 return _phy_56xxx_5601x_stop(unit, port); 1863 } 1864 1865 /* 1866 * Function: 1867 * phy_56xxx_5601x_notify_resume 1868 * Purpose: 1869 * Remove a reason to put 56xxx_5601x PHY in reset. 1870 * Parameters: 1871 * unit - StrataSwitch unit #. 1872 * port - StrataSwitch port #. 1873 * flags - Reason to stop 1874 * Returns: 1875 * SOC_E_XXX 1876 */ 1877 STATIC int 1878 _phy_56xxx_5601x_notify_resume(int unit, soc_port_t port, uint32 flags) 1879 { 1880 phy_ctrl_t *pc; 1881 1882 pc = INT_PHY_SW_STATE(unit, port); 1883 1884 pc->stop &= ~flags; 1885 1886 return _phy_56xxx_5601x_stop(unit, port); 1887 } 1888 1889 1890 STATIC int 1891 _phy_56xxx_5601x_notify_interface(int unit, soc_port_t port, uint32 intf) 1892 { 1893 phy_ctrl_t *pc; 1894 uint16 data16; 1895 1896 pc = INT_PHY_SW_STATE(unit, port); 1897 1898 data16 = (intf == SOC_PORT_IF_SGMII) ? 0 : DDS_1000X_FIBER_MODE; 1899 SOC_IF_ERROR_RETURN 1900 (MODIFY_SERDES_5601X_1000X_CTRL1r(unit, pc, data16, DDS_1000X_FIBER_MODE)); 1901 1902 return SOC_E_NONE; 1903 } 1904 #else /* INCLUDE_PHY_56XXX_5601X */ 1905 int _phy_56xxx_5601x_not_empty; 1906 #endif /* INCLUDE_PHY_56XXX_5601X */ 1907