phy5464.c (103289B)
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: phy5464.c 8 * Purpose: PHY driver for BCM5461, BCM5464 and BCM548x 9 * 10 * Supported BCM546X Family of PHY devices: 11 * 12 * Device Ports Media MAC Interface 13 * 5461 1 Copper GMII 14 * 5461S 1 Copper, Serdes GMII, SGMII 15 * 5464 4 Copper, Serdes GMII, SGMII 16 * 5464R 4 Copper, Serdes SGMII 17 * 5464S 4 Copper, Serdes, Passthru GMII, SGMII 18 * 5464SR 4 Copper, Serdes, Passthru SGMII 19 * 5466 4 Copper, Serdes GMII, SGMII 20 * 5466R 4 Copper, Serdes SGMII 21 * 5466S 4 Copper, Serdes, Passthru GMII, SGMII 22 * 5466SR 4 Copper, Serdes, Passthru SGMII 23 * 5488 8 Copper SGMII 24 * 54980 8 Copper SGMII 25 * 26 * The 5466 devices have a strapping option that will make them act 27 * identically to the corresponding 5464 device (including PHY ID 28 * register values). 29 * 30 * Other devices in this family, but not supported by this driver: 31 * 5464H 4 Copper RGMII(HSTL) 32 * 5465 4 Copper RGMII 33 * 34 * This driver works with both 569x switches that have an SGMII interface 35 * and with Strata and 5665 switches with a GMII interface. There is no 36 * support in this driver for RGMII, TBI, or RTBI interfaces. 37 * 38 * All of these PHY devices support a 100FX media mode, but currently this 39 * driver does not. 40 * 41 * If this driver is compiled without 569x support, then only GMII (Copper, 42 * Fiber) and SGMII (Copper) support will be enabled. 43 * 44 * SGMII support: 45 * The 5690, 5695, 56504 and 56601 switch devices provide a serial 46 * GMII (SGMII) MAC interface to a PHY device. The PHY devices 47 * above with an SGMII interface and no Passthru support are 48 * supported as copper only PHYs. Although the PHYs have a Serdes 49 * media interface suitable for fiber media, it shares the same 50 * electronics as the SGMII MAC interface. The 569x internal serdes 51 * is kept into passthru mode and the PHY performs all signaling. 52 * 53 * Those PHYs with a Passthru media interface can support both Copper 54 * (as above) and Fiber media (by putting the PHY into passthru mode 55 * and having the 569x serdes handle all signaling). There is no way 56 * for the driver to determine from the PHY device if it has a Passthru 57 * media interface, so properties must be set if Fiber media is to be 58 * used. The property "phy_5464S" or "phy_5464S_portXX" must be set if 59 * the PHY device is a 5464S or 5464SR. 60 * 61 * In all of the cases for 569x connections, the PHY's 1000BASE-X 62 * registers are never enabled. 63 * 64 * The 569x switches do not support SGMII auto-negotiation. They only 65 * support Serdes auto-negotiation. In Copper mode, 569x autoneg must 66 * be off and the PHY device autoneg on the SGMII mac interface must 67 * also be off. In Passthru (Fiber) mode, the 569x is used for Serdes 68 * autoneg and the 5464 is in passthru. 69 * 70 * GMII support: 71 * The Strata and 5665 family of switch devices provide a GMII MAC 72 * interface to a PHY device. The PHY devices with a GMII interface 73 * can be connected to these switches. Any PHY device with a Serdes 74 * media interface can be used for both Copper and Fiber media 75 * connections. Those PHY devices without a Serdes interface can only 76 * be used in Copper mode. 77 * 78 * The Passthru mode in any PHY devices is ignored. 79 * 80 * Auto-medium selection: 81 * Automatic selection of copper vs. fiber medium type is enabled when 82 * the "phy_automedium" property is 1 (default 0 for 5464 on 569x 83 * and 1 otherwise). If both copper and fiber signals are present, the 84 * driver consults the configuration property "phy_fiber_pref_portXX" 85 * (default 1) to determine whether copper or fiber is preferred. 86 * 87 * See sdk/doc/combo-ports.txt for more information. 88 * 89 * NOTE: auto-medium REQUIRES the fiber module energy detect pin to be 90 * wired to the LED4 pin on the 5464S. 91 * 92 * Workarounds: 93 * ERRATA_ENABLE: (default 0) 94 * The driver will deal with various errata for 5464 A0 and 5461 A0 95 * revision PHY devices. It is not enabled by default because 96 * production use of these PHYs should be using A1 or later revisions. 97 * 98 * LINK_DOWN_RESET_WAR: (default 1) 99 * The PHY will be reset each time link down is detected on copper. 100 * This is due to the 100TX_SWAP erratum on 5464 A0 revision parts. 101 */ 102 #include <shared/bsl.h> 103 104 #include <sal/types.h> 105 #include <sal/core/spl.h> 106 #include <soc/drv.h> 107 #include <soc/error.h> 108 #include <soc/phyreg.h> 109 110 #include <soc/phy.h> 111 #include <soc/phy/phyctrl.h> 112 #include <soc/phy/drv.h> 113 114 #include "phydefs.h" /* Must include before other phy related includes */ 115 116 #if defined(INCLUDE_PHY_5464_ESW) 117 #include "phyconfig.h" /* Must be the first phy include after phydefs.h */ 118 119 #include "phyident.h" 120 #include "phyreg.h" 121 #include "phyfege.h" 122 #include "phy5464.h" 123 124 /* 125 * Please see the errata sheet for the PHY devices for a description of 126 * the errata to decide which workarounds (WARs) to enable for your 127 * application. 128 */ 129 130 #define LINK_DOWN_RESET_WAR 1 /* Reset PHY on copper link down */ 131 #define ERRATA_ENABLE 0 /* rev A0 errata */ 132 133 #define AUTO_MDIX_WHEN_AN_DIS 0 /* Non-standard-compliant option */ 134 #define DISABLE_CLK125 0 /* Disable if unused to reduce */ 135 /* EMI emissions */ 136 137 #define DPRINTF(_s) LOG_INFO(BSL_LS_SOC_PHY, _s) 138 #define DPRINTF_VERBOSE(_s) LOG_VERBOSE(BSL_LS_SOC_PHY, _s) 139 140 #ifndef PHY_5464_CABLE_DIAG_HW 141 #define PHY_5464_CABLE_DIAG_HW 0 142 #endif 143 144 /* 145 * passthru mode is used for fiber support on 569x where the phy 146 * is put into serdes passthru mode and the serdes support in the 147 * 569x is used. 148 */ 149 150 #define ADVERT_ALL_COPPER \ 151 (SOC_PM_PAUSE | SOC_PM_10MB | SOC_PM_100MB | SOC_PM_1000MB) 152 #define ADVERT_ALL_FIBER \ 153 (SOC_PM_PAUSE | SOC_PM_1000MB_FD) 154 155 #define PHY_REV_A0 0 156 #define PHY_REV_A1 1 157 158 /* In the serial phy546x(includes phy5466), the fiber SD is required 159 * for supporting the combo medium solution. The SD might be active high or 160 * active low depends on HW designing in customer. 161 * SDK phy driver might indicate such definition by "config.bcm". And here we 162 * define HW_DEFAULT_SD_VALUE for such preference when no config.bcm definition 163 * been loaded. 164 * -------------------------------------------------- 165 * 1. bcm95324a0 : SD active low 166 * 2. bcm95324a1 : SD active low 167 * 3. ADD..... 168 */ 169 #define HW_DEFAULT_SD_VALUE -10 170 171 STATIC int phy_5464_duplex_set(int unit, soc_port_t port, int duplex); 172 STATIC int phy_5464_speed_set(int unit, soc_port_t port, int speed); 173 STATIC int phy_5464_master_set(int unit, soc_port_t port, int master); 174 STATIC int phy_5464_adv_local_set(int unit, soc_port_t port, 175 soc_port_mode_t mode); 176 STATIC int phy_5464_autoneg_set(int unit, soc_port_t port, int autoneg); 177 STATIC int phy_5464_mdix_set(int unit, soc_port_t port, soc_port_mdix_t mode); 178 STATIC int _phy_5464_medium_change(int unit, soc_port_t port, int force_update); 179 /*********************************************************************** 180 * 181 * HELPER FUNCTIONS 182 */ 183 /* 184 * Function: 185 * _phy_5464_medium_check 186 * Purpose: 187 * Determine if chip should operate in copper or fiber mode. 188 * Parameters: 189 * unit - StrataSwitch unit #. 190 * port - StrataSwitch port #. 191 * medium - (OUT) SOC_PORT_MEDIUM_XXX 192 * Returns: 193 * SOC_E_XXX 194 */ 195 196 STATIC int 197 _phy_5464_medium_check(int unit, soc_port_t port, int *medium) 198 { 199 phy_ctrl_t *pc; /* PHY state */ 200 uint16 tmp; /* Temp variable */ 201 int copper; /* Copper medium is active */ 202 203 pc = EXT_PHY_SW_STATE(unit, port); 204 /* Read Mode Register (0x1c shadow 11111) */ 205 SOC_IF_ERROR_RETURN 206 (READ_PHY5464_MODE_CTRLr(unit, pc, &tmp)); 207 if (PHY_FORCED_COPPER_MODE(unit, port)) { 208 copper = TRUE; 209 } else { 210 if (!pc->fiber.enable) { 211 copper = TRUE; 212 } else if (!pc->copper.enable) { 213 copper = FALSE; 214 } else { 215 if (pc->fiber.preferred) { 216 /* 0x10 Fiber Signal Detect 217 * 0x20 Copper Energy Detect 218 */ 219 copper = ((tmp & 0x30) == 0x20); 220 } else { 221 copper = ((tmp & 0x20) == 0x20); 222 } 223 } 224 } 225 *medium = copper ? SOC_PORT_MEDIUM_COPPER : SOC_PORT_MEDIUM_FIBER; 226 227 DPRINTF_VERBOSE((BSL_META_U(unit, 228 "_phy_5464_medium_check: " 229 "u=%d p=%d fiber_pref=%d 0x1c(11111)=%04x\n"), 230 unit, port, pc->fiber.preferred, tmp)); 231 DPRINTF_VERBOSE((BSL_META_U(unit, 232 "_phy_5464_medium_check: u=%d p=%d fiber=%d\n"), 233 unit, port, !copper)); 234 235 return SOC_E_NONE; 236 } 237 238 /* 239 * Function: 240 * phy_5464_medium_status 241 * Purpose: 242 * Indicate the current active medium 243 * Parameters: 244 * unit - StrataSwitch unit #. 245 * port - StrataSwitch port #. 246 * medium - (OUT) One of: 247 * SOC_PORT_MEDIUM_COPPER 248 * SOC_PORT_MEDIUM_FIBER 249 * Returns: 250 * SOC_E_NONE 251 */ 252 STATIC int 253 phy_5464_medium_status(int unit, soc_port_t port, soc_port_medium_t *medium) 254 { 255 if (PHY_COPPER_MODE(unit, port)) { 256 *medium = SOC_PORT_MEDIUM_COPPER; 257 } else { 258 *medium = SOC_PORT_MEDIUM_FIBER; 259 } 260 return SOC_E_NONE; 261 } 262 263 /* 264 * Function: 265 * _phy_5464_active_medium_config_update 266 * Purpose: 267 * Update the PHY with config parameters 268 * Parameters: 269 * unit - StrataSwitch unit #. 270 * port - StrataSwitch port #. 271 * cfg - Config structure. 272 * Returns: 273 * SOC_E_XXX 274 */ 275 STATIC int 276 _phy_5464_active_medium_config_update(int unit, soc_port_t port, 277 soc_phy_config_t *cfg) 278 { 279 SOC_IF_ERROR_RETURN 280 (phy_5464_speed_set(unit, port, cfg->force_speed)); 281 SOC_IF_ERROR_RETURN 282 (phy_5464_duplex_set(unit, port, cfg->force_duplex)); 283 SOC_IF_ERROR_RETURN 284 (phy_5464_master_set(unit, port, cfg->master)); 285 SOC_IF_ERROR_RETURN 286 (phy_5464_adv_local_set(unit, port, cfg->autoneg_advert)); 287 SOC_IF_ERROR_RETURN 288 (phy_5464_autoneg_set(unit, port, cfg->autoneg_enable)); 289 SOC_IF_ERROR_RETURN 290 (phy_5464_mdix_set(unit, port, cfg->mdix)); 291 292 return SOC_E_NONE; 293 } 294 295 /* 296 * Function: 297 * phy_5464_medium_config_set 298 * Purpose: 299 * Set the operating parameters that are automatically selected 300 * when medium switches type. 301 * Parameters: 302 * unit - StrataSwitch unit # 303 * port - Port number 304 * medium - SOC_PORT_MEDIUM_COPPER/FIBER 305 * cfg - Operating parameters 306 * Returns: 307 * SOC_E_XXX 308 */ 309 STATIC int 310 phy_5464_medium_config_set(int unit, soc_port_t port, 311 soc_port_medium_t medium, 312 soc_phy_config_t *cfg) 313 { 314 phy_ctrl_t *pc; 315 soc_phy_config_t *active_medium; /* Currently active medium */ 316 soc_phy_config_t *change_medium; /* Requested medium */ 317 soc_phy_config_t *other_medium; /* The other medium */ 318 int medium_update; 319 soc_port_mode_t advert_mask; 320 321 if (NULL == cfg) { 322 return SOC_E_PARAM; 323 } 324 325 pc = EXT_PHY_SW_STATE(unit, port); 326 medium_update = FALSE; 327 328 switch (medium) { 329 case SOC_PORT_MEDIUM_COPPER: 330 if (!pc->automedium && !PHY_COPPER_MODE(unit, port)) { 331 return SOC_E_UNAVAIL; 332 } 333 change_medium = &pc->copper; 334 other_medium = &pc->fiber; 335 advert_mask = ADVERT_ALL_COPPER; 336 break; 337 case SOC_PORT_MEDIUM_FIBER: 338 if (!pc->automedium && !PHY_FIBER_MODE(unit, port)) { 339 return SOC_E_UNAVAIL; 340 } 341 change_medium = &pc->fiber; 342 other_medium = &pc->copper; 343 advert_mask = ADVERT_ALL_FIBER; 344 break; 345 default: 346 return SOC_E_PARAM; 347 } 348 349 /* 350 * Changes take effect immediately if the target medium is active or 351 * the preferred medium changes. 352 */ 353 if (change_medium->enable != cfg->enable) { 354 medium_update = TRUE; 355 } 356 if (change_medium->preferred != cfg->preferred) { 357 /* Make sure that only one medium is preferred */ 358 other_medium->preferred = !cfg->preferred; 359 medium_update = TRUE; 360 } 361 362 sal_memcpy(change_medium, cfg, sizeof(*change_medium)); 363 change_medium->autoneg_advert &= advert_mask; 364 365 if (medium_update) { 366 /* The configuration may cause medium change. Check 367 * and update medium. 368 */ 369 SOC_IF_ERROR_RETURN 370 (_phy_5464_medium_change(unit, port,TRUE)); 371 } else { 372 active_medium = (PHY_COPPER_MODE(unit, port)) ? 373 &pc->copper : &pc->fiber; 374 if (active_medium == change_medium) { 375 /* If the medium to update is active, update the configuration */ 376 SOC_IF_ERROR_RETURN 377 (_phy_5464_active_medium_config_update(unit, port, 378 change_medium)); 379 } 380 } 381 return SOC_E_NONE; 382 } 383 384 /* 385 * Function: 386 * phy_5464_medium_config_get 387 * Purpose: 388 * Get the operating parameters that are automatically selected 389 * when medium switches type. 390 * Parameters: 391 * unit - StrataSwitch unit # 392 * port - Port number 393 * medium - SOC_PORT_MEDIUM_COPPER/FIBER 394 * cfg - (OUT) Operating parameters 395 * Returns: 396 * SOC_E_XXX 397 */ 398 399 STATIC int 400 phy_5464_medium_config_get(int unit, soc_port_t port, 401 soc_port_medium_t medium, 402 soc_phy_config_t *cfg) 403 { 404 int rv; 405 phy_ctrl_t *pc; 406 407 pc = EXT_PHY_SW_STATE(unit, port); 408 rv = SOC_E_NONE; 409 410 switch (medium) { 411 case SOC_PORT_MEDIUM_COPPER: 412 if (pc->automedium || PHY_COPPER_MODE(unit, port)) { 413 sal_memcpy(cfg, &pc->copper, sizeof (*cfg)); 414 } else { 415 rv = SOC_E_UNAVAIL; 416 } 417 break; 418 case SOC_PORT_MEDIUM_FIBER: 419 if (pc->automedium || PHY_FIBER_MODE(unit, port)) { 420 sal_memcpy(cfg, &pc->fiber, sizeof (*cfg)); 421 } else { 422 rv = SOC_E_UNAVAIL; 423 } 424 break; 425 default: 426 rv = SOC_E_PARAM; 427 break; 428 } 429 430 return rv; 431 } 432 433 /* 434 * Function: 435 * _phy_5464_reset_setup 436 * Purpose: 437 * Function to reset the PHY and set up initial operating registers. 438 * Parameters: 439 * unit - StrataSwitch unit #. 440 * port - StrataSwitch port #. 441 * Returns: 442 * SOC_E_XXX 443 * Notes: 444 * Configures for copper/fiber according to the current 445 * setting of PHY_FLAGS_FIBER. 446 */ 447 STATIC int 448 _phy_5464_reset_setup(int unit, soc_port_t port) 449 { 450 phy_ctrl_t *int_pc; 451 phy_ctrl_t *pc; 452 uint16 tmp; 453 454 DPRINTF((BSL_META_U(unit, 455 "_phy_5464_reset_setup: u=%d p=%d medium=%s\n"), 456 unit, port, 457 PHY_COPPER_MODE(unit, port) ? "COPPER" : "FIBER")); 458 459 SOC_IF_ERROR_RETURN(phy_ge_init(unit, port)); 460 461 462 pc = EXT_PHY_SW_STATE(unit, port); 463 int_pc = INT_PHY_SW_STATE(unit, port); 464 465 /* Configure interface to MAC */ 466 SOC_IF_ERROR_RETURN 467 (READ_PHY5464_1000X_MII_CTRLr(unit, pc, &tmp)); 468 469 /* phy_ge_init has reset the phy, powering down the unstrapped interface */ 470 /* make sure enabled interfaces are powered up */ 471 /* SGMII (passthrough fiber) or GMII fiber regs */ 472 if ((pc->fiber.enable) || (pc->interface == SOC_PORT_IF_SGMII)) { 473 tmp &= ~MII_CTRL_PD; /* remove power down */ 474 } else { 475 tmp |= MII_CTRL_PD; /* Keep the media in power down state if 476 * not use. 477 */ 478 } 479 480 if (PHY_SGMII_AUTONEG_MODE(unit, port)) { 481 /* 482 * Enable SGMII autonegotiation on the switch side so that the 483 * link status changes are reflected in the switch. 484 * On Bradley devices, LAG failover feature depends on the SerDes 485 * link staus to activate failover recovery. 486 */ 487 tmp |= MII_CTRL_AE; 488 } else { 489 tmp &= ~MII_CTRL_AE; /* disable SGMII autoneg. */ 490 } 491 492 SOC_IF_ERROR_RETURN 493 (WRITE_PHY5464_1000X_MII_CTRLr(unit, pc, tmp)); 494 495 /* copper regs */ 496 /* remove power down */ 497 if (pc->copper.enable) { 498 tmp = PHY_DISABLED_MODE(unit, port) ? MII_CTRL_PD : 0; 499 } else { 500 tmp = MII_CTRL_PD; 501 } 502 SOC_IF_ERROR_RETURN 503 (MODIFY_PHY5464_MII_CTRLr(unit, pc, tmp, MII_CTRL_PD)); 504 505 if (NULL != int_pc) { 506 SOC_IF_ERROR_RETURN 507 (PHY_INIT(int_pc->pd, unit, port)); 508 509 if (!PHY_SGMII_AUTONEG_MODE(unit, port)) { 510 if (PHY_COPPER_MODE(unit, port)) { 511 SOC_IF_ERROR_RETURN 512 (PHY_NOTIFY(int_pc->pd, unit, port, 513 phyEventStop, PHY_STOP_COPPER)); 514 } else { /* Fiber */ 515 if (pc->fiber.preferred) { 516 /* Disable copper transmitter */ 517 SOC_IF_ERROR_RETURN 518 (MODIFY_PHY5464_MII_ECRr(unit, pc, 0x2000, 0x2000)); 519 } 520 521 SOC_IF_ERROR_RETURN 522 (PHY_NOTIFY(int_pc->pd, unit, port, 523 phyEventResume, PHY_STOP_COPPER)); 524 } 525 } 526 } 527 #if ERRATA_ENABLE 528 /* 529 * Perform Errata workarounds for 5464SA0 silicon. 530 * NOTE: Assumes PHY internal port number 1 to 4 is ((port & 3) + 1). 531 */ 532 533 if (PHY_IS_BCM5464(pc) && pc->phy_rev == PHY_REV_A0 && 534 (port & 3) == 0) { 535 536 phy_ctrl_t *pc2; 537 538 pc2 = PHY_EXT_SW_STATE(unit, port + 1); 539 540 DPRINTF((BSL_META_U(unit, 541 "_phy_5464_reset_setup: u=%d p=%d ERRATA\n"), 542 unit, port)); 543 544 /* Internal Regulators Errata */ 545 546 SOC_IF_ERROR_RETURN 547 (WRITE_PHY_REG(unit, pc, 0x18, 0x0282)); 548 549 /* Twisted Pair Output Voltage Errata */ 550 551 SOC_IF_ERROR_RETURN 552 (WRITE_PHY_REG(unit, pc, 0x1e, 0x0012)); 553 SOC_IF_ERROR_RETURN 554 (WRITE_PHY_REG(unit, pc2, 0x18, 0x0007)); 555 SOC_IF_ERROR_RETURN 556 (READ_PHY_REG(unit, pc2, 0x18, &tmp)); 557 SOC_IF_ERROR_RETURN 558 (WRITE_PHY_REG(unit, pc2, 0x18, tmp | 0x0800)); 559 SOC_IF_ERROR_RETURN 560 (WRITE_PHY_REG(unit, pc2, 0x17, 0x000a)); 561 SOC_IF_ERROR_RETURN 562 (READ_PHY_REG(unit, pc2, 0x15, &tmp)); 563 SOC_IF_ERROR_RET 564 (WRITE_PHY_REG(unit, pc2, 0x15, tmp | 0x1000)); 565 } 566 567 if (PHY_IS_BCM5461(pc) && pc->phy_rev == PHY_REV_A0) { 568 /* Internal Regulators Erratum for 5461 (reg 0x18 shadow b'010) */ 569 570 SOC_IF_ERROR_RETURN 571 (WRITE_PHY_REG(unit, pc, 0x18, 0x0602)); 572 } 573 #endif /* ERRATA_ENABLE */ 574 575 /* Disable super-isolate */ 576 SOC_IF_ERROR_RETURN 577 (MODIFY_PHY5464_MII_POWER_CTRLr(unit, pc, 0x0, 1U<<5)); 578 579 580 #if DISABLE_CLK125 581 /* Reduce EMI emissions by disabling the CLK125 pin if not used */ 582 SOC_IF_ERROR_RETURN 583 (MODIFY_PHY5464_SPARE_CTRL_3r(unit, pc, 0, 1)); 584 #endif 585 586 if (pc->automedium || pc->fiber.preferred) { 587 if (pc->interface == SOC_PORT_IF_SGMII) { 588 /* passthru mode support only in sgmii mode */ 589 /* Enable SerDes Pass-Through */ 590 SOC_IF_ERROR_RETURN 591 (MODIFY_PHY5464_EXP_SERDES_PASSTHRU_ENr(unit, pc, 0x0001, 0x0001)); 592 593 if (!pc->automedium && pc->fiber.preferred) { 594 /* Set register 0x1c shadow "10111" bit 1 to force SerDes 595 * passthru */ 596 /* Misc 1000-X Control register (0x1c shadow 10111) */ 597 /* Medium Resolution override for SerDes passthru mode */ 598 SOC_IF_ERROR_RETURN 599 (MODIFY_PHY5464_MISC_1000Xr(unit, pc, 0x0002, 0x0002)); 600 } 601 } 602 603 if (PHY_IS_BCM5461(pc)) { 604 switch (pc->fiber_detect) { 605 case 1: /* PECL SD */ 606 case -1: 607 case 0: /* default */ 608 SOC_IF_ERROR_RETURN 609 (phy_reg_ge_write(unit, pc, 0x00, 0x000c, 0x1c, 0x0020)); 610 break; 611 case 10: /* EN_10B */ 612 case -10: 613 SOC_IF_ERROR_RETURN 614 (phy_reg_ge_write(unit, pc, 0x00, 0x000c, 0x1c, 0x0000)); 615 break; 616 default: 617 return SOC_E_CONFIG; 618 } 619 } else { /* multiport */ 620 switch (pc->fiber_detect) { 621 case 4: /* LED4 */ 622 case -4: 623 case 0: /* default */ 624 /* GPIO Control/Status Register (0x1c shadow 01111) */ 625 /* Change LED4 pin into an input */ 626 SOC_IF_ERROR_RETURN 627 (MODIFY_PHY5464_LED_GPIO_CTRLr(unit, pc, 0x0008, 0x0008)); 628 break; 629 default: 630 return SOC_E_CONFIG; 631 } 632 } 633 } 634 635 /* Configure Auto-detect Medium (0x1c shadow 11110) */ 636 /* In SGMII mode, the priority must be copper. 637 * 1) Optical modules generate a SD or LOS event if there is no activity. 638 * This would cause 5464S to falsely go into fiber mode when there is 639 * no link partner connected and cause 5464S to never link up a valid 640 * copper link partner. 641 * 2) When 5464S has a copper link, it cannot send out any signals on 642 * it PTOUT+/- because 5464S is in SSGMII to copper mode and not 643 * SerDes pass-through mode. This will cause link partner to never 644 * link up even if a fiber cable is connected between them because 645 * their PTOUT+/- pins are disabled. 646 */ 647 tmp = 0; 648 if (pc->automedium && 649 !(PHY_IS_BCM5464(pc) && pc->phy_rev == PHY_REV_A0)) { 650 /* Must NOT be set on 5464A0, and it will always auto-detect */ 651 tmp |= 0x01; /* Bit 0: Enable auto-detect */ 652 } 653 if (pc->fiber.preferred) { 654 tmp |= 0x06; /* Bit 1/2: always prefer SERDES */ 655 } else { 656 tmp |= 0x04; /* Bit 2: prefer SERDES if no medium active */ 657 } 658 659 /* 660 * Enable internal inversion of LED4/SD input pin. Used only if 661 * the fiber module provides RX_LOS instead of Signal Detect. 662 */ 663 if (pc->fiber_detect < 0) { 664 tmp |= 0x100; 665 } 666 667 SOC_IF_ERROR_RETURN 668 (MODIFY_PHY5464_AUTO_DETECT_MEDIUMr(unit, pc, tmp, 0x011f)); 669 670 if ((!pc->automedium) && 671 (pc->interface == SOC_PORT_IF_GMII)) { 672 /* Set operating mode only after auto-detect is disabled. 673 * Otherwise, the auto-detect will override the mode settings. 674 */ 675 /* Mode Control Register 676 * Mode Select = > 00: GMII Copper 677 * [2:1] 01: GMII Fiber 678 * 10: SGMII 679 * 11: GBIC(Media Converter) 680 */ 681 if (PHY_COPPER_MODE(unit, port)) { 682 tmp = (0 << 1); /* copper/GMII */ 683 } else { 684 tmp = (1 << 1); /* fiber/GMII */ 685 } 686 SOC_IF_ERROR_RETURN 687 (MODIFY_PHY5464_MODE_CTRLr(unit, pc, tmp, 0x0006)); 688 SOC_IF_ERROR_RETURN 689 (MODIFY_PHY5464_MII_ECRr(unit, pc, 0, (1 << 15))); 690 } 691 692 /* 693 * Configure Auxiliary 1000BASE-X control register to turn off 694 * carrier extension. The Intel 7131 NIC does not accept carrier 695 * extension and gets CRC errors. 696 */ 697 /* Disable carrier extension */ 698 SOC_IF_ERROR_RETURN 699 (MODIFY_PHY5464_AUX_1000X_CTRLr(unit, pc, 0x0040, 0x0040)); 700 701 /* Configure Extended Control Register */ 702 tmp = 0x0020; /* Enable LEDs to indicate traffic status */ 703 tmp |= 0x0001; /* Set high FIFO elasticity to support jumbo frames */ 704 SOC_IF_ERROR_RETURN 705 (MODIFY_PHY5464_MII_ECRr(unit, pc, tmp, tmp)); 706 707 if (PHY_IS_BCM54980(pc) || PHY_IS_BCM54980C(pc) || 708 PHY_IS_BCM54980V(pc) || PHY_IS_BCM54980VC(pc)) { 709 710 /* set 54980 elasticity fifo to support 13.5k frame size*/ 711 SOC_IF_ERROR_RETURN 712 (MODIFY_PHY5464_MISC_1000X_CTRL2r(unit,pc, 1, 1)); 713 SOC_IF_ERROR_RETURN 714 (MODIFY_PHY5464_AUX_1000X_CTRLr(unit,pc, 0, (1 << 1))); 715 } 716 717 #if AUTO_MDIX_WHEN_AN_DIS 718 /* Enable Auto-MDIX When autoneg disabled */ 719 SOC_IF_ERROR_RETURN 720 (MODIFY_PHY5464_MII_MISC_CTRLr(unit, pc, 0x0200, 0x0200)); 721 #endif 722 723 /* Enable extended packet length (4.5k through 25k) */ 724 SOC_IF_ERROR_RETURN 725 (MODIFY_PHY5464_MII_AUX_CTRLr(unit, pc, 0x4000, 0x4000)); 726 727 /* Configure LED selectors */ 728 tmp = ((pc->ledmode[1] & 0xf) << 4) | (pc->ledmode[0] & 0xf); 729 SOC_IF_ERROR_RETURN 730 (WRITE_PHY5464_LED_SELECTOR_1r(unit, pc, tmp)); 731 732 tmp = ((pc->ledmode[3] & 0xf) << 4) | (pc->ledmode[2] & 0xf); 733 SOC_IF_ERROR_RETURN 734 (WRITE_PHY5464_LED_SELECTOR_2r(unit, pc, tmp)); 735 736 tmp = (pc->ledctrl & 0x3ff); 737 SOC_IF_ERROR_RETURN 738 (WRITE_PHY5464_LED_CTRLr(unit, pc, tmp)); 739 740 SOC_IF_ERROR_RETURN 741 (WRITE_PHY5464_EXP_LED_SELECTORr(unit, pc, pc->ledselect)); 742 743 /* if using led link/activity mode, disable led traffic mode */ 744 if (pc->ledctrl & 0x10 || pc->ledselect == 0x01) { 745 SOC_IF_ERROR_RETURN 746 (MODIFY_PHY5464_MII_ECRr(unit, pc, 0, 0x0020)); 747 } 748 749 if (PHY_IS_BCM5478(pc) || PHY_IS_BCM5488(pc) || 750 PHY_IS_BCM54980(pc) || PHY_IS_BCM54980C(pc) || 751 PHY_IS_BCM54980V(pc) || PHY_IS_BCM54980VC(pc)) { 752 uint16 half, half1k; 753 uint32 power_mode; 754 755 tmp = 0; 756 half = 0; 757 half1k = 0; 758 if (pc->halfout) { 759 tmp &= (~0x40); /* Class A mode */ 760 if ((pc->halfout == 10) || (pc->halfout == 100)) { 761 half |= 0x8; /* Enable half amplitude for all speed */ 762 } else { 763 half1k |= 0x2; /* Enable half amplitude for Giga */ 764 } 765 } else { 766 tmp |= 0x40; /* Class A/B mode */ 767 half &= (~0x8); /* Disable half amplitude for Giga */ 768 half1k &= (~0x2); /* Disable half amplitude for all speed */ 769 770 /* For extended reach, default to highest performance mode 771 * (REF: 5488R-ES104-R) 772 */ 773 power_mode = soc_property_port_get(unit, port, 774 spn_PHY_LOW_POWER_MODE, 7); 775 tmp |= (power_mode & 0x7) << 13; /* Set power mode */ 776 } 777 778 /* Update Power/MII Control Register (0x18 shadow b'010) */ 779 SOC_IF_ERROR_RETURN 780 (MODIFY_PHY5464_MII_POWER_CTRLr(unit, pc, tmp, 0xe040)); 781 782 /* Update Test Register 1 (0x18 shadow b'100) */ 783 SOC_IF_ERROR_RETURN 784 (MODIFY_PHY5464_MII_MISC_TESTr(unit, pc, half, 0x0008)); 785 786 /* Update Expansion Register 0xF9 */ 787 SOC_IF_ERROR_RETURN 788 (MODIFY_PHY5464_EXP_PASSTHRU_LED_MODEr(unit, pc, half1k, 0x02)); 789 } 790 791 /* BCM54980 revB 10BASE-T LOW TRANSMITTER AMPLITUDE workaround. 792 * See 54980 errata 54980-ES105-R 793 */ 794 if (PHY_IS_BCM54980(pc) || PHY_IS_BCM54980C(pc) || 795 PHY_IS_BCM54980V(pc) || PHY_IS_BCM54980VC(pc)) { 796 797 /* only applies to B revisions */ 798 if ((pc->phy_id1 & 0xF) <= 2) { 799 SOC_IF_ERROR_RETURN 800 (WRITE_PHY5464_EXP_10BaseT_TxAMPr(unit,pc,0x3800)); 801 802 SOC_IF_ERROR_RETURN 803 (WRITE_PHY5464_TEST1r(unit,pc,0x0011)); 804 805 SOC_IF_ERROR_RETURN 806 (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0C00,0x0FFF)); 807 808 /* write 0xA to reg 0x17 and 0x90B to reg 15 */ 809 SOC_IF_ERROR_RETURN 810 (PHY5464_REG_WRITE(unit,pc,0,0xA,0x15,0x090B)); 811 812 SOC_IF_ERROR_RETURN 813 (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0400,0x0FFF)); 814 } 815 } 816 817 if (PHY_IS_BCM5488(pc) || PHY_IS_BCM5482(pc)) { 818 /* Adjust timing */ 819 SOC_IF_ERROR_RETURN 820 (MODIFY_PHY5464_SPARE_CTRLr(unit, pc, 0x0002, 0x0002)); 821 } 822 823 #ifdef BCM_HAWKEYE_SUPPORT 824 if (PHY_IS_BCM53314(pc)) { 825 /* Issue for For bcm53314_B0 : Unstable link issue with Marvell PHY 826 * Workaroud (PHY team confirmed workaround has been identified) : 827 * Set Exp register 0x08.bit[9]=0. 828 * (BIT9 is AUTO EARLY DAC WAKE power savings mode) 829 */ 830 uint16 dev_id = 0; 831 uint8 rev_id = 0; 832 833 soc_cm_get_id(unit, &dev_id, &rev_id); 834 if ((rev_id == BCM53312_B0_REV_ID) || 835 (rev_id == BCM53313_B0_REV_ID) || 836 (rev_id == BCM53314_B0_REV_ID)) { 837 SOC_IF_ERROR_RETURN 838 (MODIFY_PHY5464_10BT_CTRLr(unit, pc, 0x0000, 0x0200)); 839 } 840 } 841 #endif 842 843 return SOC_E_NONE; 844 } 845 846 /* 847 * Function: 848 * phy_5464_init 849 * Purpose: 850 * Init function for 5464 PHY. 851 * Parameters: 852 * unit - StrataSwitch unit #. 853 * port - StrataSwitch port #. 854 * Returns: 855 * SOC_E_NONE 856 */ 857 STATIC int 858 phy_5464_init(int unit, soc_port_t port) 859 { 860 phy_ctrl_t *pc; 861 phy_ctrl_t *int_pc; 862 int fiber_capable, fiber_preferred, type_s=0; 863 uint16 tmp; 864 uint8 phy_addr; 865 866 phy_addr = PORT_TO_PHY_ADDR(unit, port); 867 868 DPRINTF((BSL_META_U(unit, 869 "phy_5464_init: u=%d p=%d phy_addr=%d\n"), 870 unit, port, (int)phy_addr)); 871 872 pc = EXT_PHY_SW_STATE(unit, port); 873 int_pc = INT_PHY_SW_STATE(unit, port); 874 875 if (PHY_IS_BCM5462(pc)) { 876 pc->phy_model = PHY_BCM5461_MODEL; /* 5461D */ 877 } 878 879 if (int_pc == NULL) { 880 pc->interface = SOC_PORT_IF_GMII; 881 } else { 882 pc->interface = SOC_PORT_IF_SGMII; 883 } 884 885 #ifdef BCM_HAWKEYE_SUPPORT 886 if (PHY_IS_BCM53314(pc)){ 887 pc->interface = SOC_PORT_IF_GMII; 888 } 889 #endif 890 891 if (PHY_IS_BCM5461(pc)) { 892 SOC_IF_ERROR_RETURN 893 (READ_PHY5464_MODE_CTRLr(unit, pc, &tmp)); 894 type_s = (tmp & 0x8) ? 1 : 0; 895 } else if (PHY_IS_BCM5464(pc) || PHY_IS_BCM5466(pc)) { 896 /* 897 * On the 5464 family, we need the user to set a property to know 898 * if we are on an S part. See comments at top of file. 899 */ 900 type_s = soc_property_port_get(unit, port, spn_PHY_5464S, 0); 901 } 902 903 if (soc_feature(unit, soc_feature_dodeca_serdes) && int_pc != NULL) { 904 if (PHY_IS_BCM5461(pc)) { 905 if (!type_s) { 906 return SOC_E_FAIL; 907 } 908 fiber_capable = 0; 909 } else { 910 fiber_capable = type_s; 911 } 912 } else { 913 if (PHY_IS_BCM5461(pc)) { 914 fiber_capable = type_s; 915 } else { 916 fiber_capable = 1; 917 } 918 } 919 920 /* 921 * In automedium mode, the property phy_fiber_pref_port<X> is used 922 * to set a preference for fiber mode in the event both copper and 923 * fiber links are up. 924 * 925 * If NOT in automedium mode, phy_fiber_pref_port<X> is used to 926 * select which of fiber or copper is forced. 927 */ 928 929 if (!fiber_capable) { 930 fiber_preferred = 0; 931 pc->automedium = 0; 932 pc->fiber_detect = 0; 933 } else { 934 fiber_preferred = 935 soc_property_port_get(unit, port, spn_PHY_FIBER_PREF, 1); 936 pc->automedium = 937 soc_property_port_get(unit, port, spn_PHY_AUTOMEDIUM, 1); 938 pc->fiber_detect = 939 soc_property_port_get(unit, port, spn_PHY_FIBER_DETECT, 0); 940 } 941 942 DPRINTF((BSL_META_U(unit, 943 "phy_5464_init: " 944 "u=%d p=%d type=546%d%s automedium=%d " 945 "fiber_pref=%d detect=%d\n"), 946 unit, port, 947 pc->phy_model == PHY_BCM5461_MODEL ? 1 : 4, 948 type_s ? "S" : "", 949 pc->automedium, fiber_preferred, pc->fiber_detect)); 950 951 pc->copper.enable = TRUE; 952 pc->copper.preferred = !fiber_preferred; 953 pc->copper.autoneg_enable = TRUE; 954 pc->copper.autoneg_advert = ADVERT_ALL_COPPER; 955 pc->copper.force_speed = 1000; 956 pc->copper.force_duplex = TRUE; 957 pc->copper.master = SOC_PORT_MS_AUTO; 958 pc->copper.mdix = SOC_PORT_MDIX_AUTO; 959 960 pc->fiber.enable = fiber_capable; 961 pc->fiber.preferred = fiber_preferred; 962 pc->fiber.autoneg_enable = fiber_capable; 963 pc->fiber.autoneg_advert = ADVERT_ALL_FIBER; 964 pc->fiber.force_speed = 1000; 965 pc->fiber.force_duplex = TRUE; 966 pc->fiber.master = SOC_PORT_MS_NONE; 967 pc->fiber.mdix = SOC_PORT_MDIX_NORMAL; 968 969 /* Initially configure for the preferred medium. */ 970 971 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_COPPER); 972 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER); 973 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_PASSTHRU); 974 if (pc->fiber.preferred) { 975 PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER); 976 if (pc->interface == SOC_PORT_IF_SGMII) { 977 PHY_FLAGS_SET(unit, port, PHY_FLAGS_PASSTHRU); 978 } 979 } else { 980 PHY_FLAGS_SET(unit, port, PHY_FLAGS_COPPER); 981 } 982 983 /* Get Requested LED selectors (defaults are hardware defaults) */ 984 pc->ledmode[0] = soc_property_port_get(unit, port, spn_PHY_LED1_MODE, 0); 985 pc->ledmode[1] = soc_property_port_get(unit, port, spn_PHY_LED2_MODE, 1); 986 pc->ledmode[2] = soc_property_port_get(unit, port, spn_PHY_LED3_MODE, 3); 987 pc->ledmode[3] = soc_property_port_get(unit, port, spn_PHY_LED4_MODE, 6); 988 pc->ledctrl = soc_property_port_get(unit, port, spn_PHY_LED_CTRL, 0x8); 989 pc->ledselect = soc_property_port_get(unit, port, spn_PHY_LED_SELECT, 0); 990 991 /* Get half amplitude setting */ 992 pc->halfout = soc_property_port_get(unit, port, spn_PHY_HALF_POWER, 0); 993 994 SOC_IF_ERROR_RETURN 995 (_phy_5464_reset_setup(unit, port)); 996 997 SOC_IF_ERROR_RETURN 998 (_phy_5464_active_medium_config_update(unit, port, 999 PHY_COPPER_MODE(unit, port) ? 1000 &pc->copper : 1001 &pc->fiber)); 1002 1003 pc->power_mode = SOC_PHY_CONTROL_POWER_FULL; 1004 return SOC_E_NONE; 1005 } 1006 1007 /* 1008 * Function: 1009 * phy_5464_enable_set 1010 * Purpose: 1011 * Enable or disable the physical interface. 1012 * Parameters: 1013 * unit - StrataSwitch unit #. 1014 * port - StrataSwitch port #. 1015 * enable - Boolean, true = enable PHY, false = disable. 1016 * Returns: 1017 * SOC_E_XXX 1018 */ 1019 1020 STATIC int 1021 phy_5464_enable_set(int unit, soc_port_t port, int enable) 1022 { 1023 phy_ctrl_t *pc; 1024 uint16 power_down; 1025 1026 pc = EXT_PHY_SW_STATE(unit, port); 1027 1028 power_down = (enable) ? 0 : MII_CTRL_PD; 1029 1030 if ((pc->copper.enable) && 1031 (pc->automedium || PHY_COPPER_MODE(unit, port))) { 1032 SOC_IF_ERROR_RETURN 1033 (MODIFY_PHY5464_MII_CTRLr(unit, pc, power_down, MII_CTRL_PD)); 1034 } 1035 1036 if (pc->automedium || PHY_FIBER_MODE(unit, port)) { 1037 if (PHY_PASSTHRU_MODE(unit, port)) { 1038 phy_ctrl_t *int_pc; /* PHY software state */ 1039 1040 int_pc = INT_PHY_SW_STATE(unit, port); 1041 1042 if (NULL != int_pc) { 1043 /* In passthrough mode, call internal PHY driver */ 1044 SOC_IF_ERROR_RETURN 1045 (PHY_ENABLE_SET(int_pc->pd, unit, port, enable)); 1046 } else { 1047 return SOC_E_INTERNAL; 1048 } 1049 } else { /* GMII fiber */ 1050 if (pc->fiber.enable) { 1051 SOC_IF_ERROR_RETURN 1052 (MODIFY_PHY5464_1000X_MII_CTRLr(unit, pc, 1053 power_down, MII_CTRL_PD)); 1054 } 1055 } 1056 } 1057 1058 if (enable) { 1059 phy_ctrl_t *int_pc; /* PHY software state */ 1060 1061 int_pc = INT_PHY_SW_STATE(unit, port); 1062 1063 if (NULL != int_pc) { 1064 /* call internal PHY driver */ 1065 SOC_IF_ERROR_RETURN 1066 (PHY_ENABLE_SET(int_pc->pd, unit, port, enable)); 1067 } 1068 } 1069 1070 /* Update software state */ 1071 SOC_IF_ERROR_RETURN(phy_fe_ge_enable_set(unit, port, enable)); 1072 1073 return SOC_E_NONE; 1074 } 1075 1076 /* 1077 * Function: 1078 * phy_5464_enable_get 1079 * Purpose: 1080 * Enable or disable the physical interface for a 5464 device. 1081 * Parameters: 1082 * unit - StrataSwitch unit #. 1083 * port - StrataSwitch port #. 1084 * enable - (OUT) Boolean, true = enable PHY, false = disable. 1085 * Returns: 1086 * SOC_E_XXX 1087 */ 1088 1089 STATIC int 1090 phy_5464_enable_get(int unit, soc_port_t port, int *enable) 1091 { 1092 return phy_fe_ge_enable_get(unit, port, enable); 1093 } 1094 1095 STATIC int 1096 _phy_5464_fiber_link_get(int unit, soc_port_t port, int *link) 1097 { 1098 uint16 mii_stat; 1099 uint16 mii_ctrl; 1100 soc_timeout_t to; 1101 phy_ctrl_t *pc; 1102 1103 pc = EXT_PHY_SW_STATE(unit, port); 1104 1105 SOC_IF_ERROR_RETURN 1106 (READ_PHY5464_1000X_MII_STATr(unit, pc, &mii_stat)); 1107 1108 if (!(mii_stat & MII_STAT_LA) || (mii_stat == 0xffff)) { 1109 /* mii_stat == 0xffff check is to handle removable PHY daughter cards */ 1110 *link = FALSE; 1111 return SOC_E_NONE; 1112 } 1113 1114 /* Link appears to be up; we are done if autoneg is off. */ 1115 SOC_IF_ERROR_RETURN 1116 (READ_PHY5464_1000X_MII_CTRLr(unit, pc, &mii_ctrl)); 1117 1118 if (!(mii_ctrl & MII_CTRL_AE)) { 1119 *link = TRUE; 1120 return SOC_E_NONE; 1121 } 1122 1123 /* 1124 * If link appears to be up but autonegotiation is still in 1125 * progress, wait for it to complete. For BCM5228, autoneg can 1126 * still be busy up to about 200 usec after link is indicated. Also 1127 * continue to check link state in case it goes back down. 1128 */ 1129 soc_timeout_init(&to, PHY_AN_TIMEOUT(unit, port), 0); 1130 for (;;) { 1131 SOC_IF_ERROR_RETURN 1132 (READ_PHY5464_1000X_MII_STATr(unit, pc, &mii_stat)); 1133 1134 if (!(mii_stat & MII_STAT_LA)) { 1135 return SOC_E_NONE; 1136 } 1137 1138 if (mii_stat & MII_STAT_AN_DONE) { 1139 break; 1140 } 1141 1142 if (soc_timeout_check(&to)) { 1143 return SOC_E_BUSY; 1144 } 1145 } 1146 1147 /* Return link state at end of polling */ 1148 *link = ((mii_stat & MII_STAT_LA) != 0); 1149 1150 return SOC_E_NONE; 1151 } 1152 1153 STATIC int 1154 _phy_5464_medium_change(int unit, soc_port_t port, int force_update) 1155 { 1156 phy_ctrl_t *pc; 1157 int medium = SOC_PORT_MEDIUM_NONE; 1158 1159 pc = EXT_PHY_SW_STATE(unit, port); 1160 1161 SOC_IF_ERROR_RETURN 1162 (_phy_5464_medium_check(unit, port, &medium)); 1163 1164 /* 1165 * If the medium is changing, reset the PHY, change its 1166 * operating mode and 5690 SERDES operating mode, and reprogram 1167 * the persistent copper/fiber parameters. Also make a user 1168 * callback to inform them of medium status change. An 1169 * application could also use this to reprogram the port 1170 * parameters quickly after a medium status change. 1171 */ 1172 1173 if (medium == SOC_PORT_MEDIUM_COPPER) { 1174 if ((!PHY_COPPER_MODE(unit, port)) || force_update) { 1175 DPRINTF((BSL_META_U(unit, 1176 "_phy_5464_link_auto_detect: " 1177 "u=%d p=%d [F->C]\n"), 1178 unit, port)); 1179 1180 PHY_FLAGS_SET(unit, port, PHY_FLAGS_COPPER); 1181 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_FIBER); 1182 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_PASSTHRU); 1183 PHY_FLAGS_SET(unit, port, PHY_FLAGS_MEDIUM_CHANGE); 1184 SOC_IF_ERROR_RETURN 1185 (_phy_5464_reset_setup(unit, port)); 1186 if (pc->copper.enable) { 1187 SOC_IF_ERROR_RETURN 1188 (_phy_5464_active_medium_config_update(unit, port, 1189 &pc->copper)); 1190 } 1191 } else { 1192 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_MEDIUM_CHANGE); 1193 /* Configure Auto-detect Medium (0x1c shadow 11110) */ 1194 /* should not prefer SERDES */ 1195 SOC_IF_ERROR_RETURN 1196 (MODIFY_PHY5464_AUTO_DETECT_MEDIUMr(unit, pc, 0, 0x0006)); 1197 } 1198 } else { /* Fiber */ 1199 if (PHY_COPPER_MODE(unit, port) || force_update) { 1200 DPRINTF((BSL_META_U(unit, 1201 "_phy_5464_link_auto_detect: " 1202 "u=%d p=%d [C->F]\n"), 1203 unit, port)); 1204 1205 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_COPPER); 1206 PHY_FLAGS_SET(unit, port, PHY_FLAGS_FIBER); 1207 PHY_FLAGS_SET(unit, port, PHY_FLAGS_MEDIUM_CHANGE); 1208 if (pc->interface == SOC_PORT_IF_SGMII) { 1209 PHY_FLAGS_SET(unit, port, PHY_FLAGS_PASSTHRU); 1210 } 1211 SOC_IF_ERROR_RETURN 1212 (_phy_5464_reset_setup(unit, port)); 1213 1214 if (pc->fiber.enable) { 1215 SOC_IF_ERROR_RETURN 1216 (_phy_5464_active_medium_config_update(unit, port, 1217 &pc->fiber)); 1218 } 1219 } else { 1220 PHY_FLAGS_CLR(unit, port, PHY_FLAGS_MEDIUM_CHANGE); 1221 } 1222 } 1223 return SOC_E_NONE; 1224 } 1225 1226 /* 1227 * Function: 1228 * phy_5464_link_get 1229 * Purpose: 1230 * Determine the current link up/down status for a 5464 device. 1231 * Parameters: 1232 * unit - StrataSwitch unit #. 1233 * port - StrataSwitch port #. 1234 * link - (OUT) Boolean, true indicates link established. 1235 * Returns: 1236 * SOC_E_XXX 1237 * Notes: 1238 * If using automedium, also switches the mode. 1239 */ 1240 1241 STATIC int 1242 phy_5464_link_get(int unit, soc_port_t port, int *link) 1243 { 1244 phy_ctrl_t *pc; 1245 1246 pc = EXT_PHY_SW_STATE(unit, port); 1247 1248 *link = FALSE; /* Default return */ 1249 1250 if (PHY_FLAGS_TST(unit, port, PHY_FLAGS_DISABLE)) { 1251 return SOC_E_NONE; 1252 } 1253 1254 if (!pc->fiber.enable && !pc->copper.enable) { 1255 return SOC_E_NONE; 1256 } 1257 1258 if (pc->automedium) { 1259 SOC_IF_ERROR_RETURN 1260 (_phy_5464_medium_change(unit, port,FALSE)); 1261 } 1262 1263 if (PHY_COPPER_MODE(unit, port)) { 1264 SOC_IF_ERROR_RETURN 1265 (phy_fe_ge_link_get(unit, port, link)); 1266 } else { /* fiber mode */ 1267 if (PHY_PASSTHRU_MODE(unit, port)) { 1268 phy_ctrl_t *int_pc; /* PHY software state */ 1269 1270 int_pc = INT_PHY_SW_STATE(unit, port); 1271 1272 if (NULL != int_pc) { 1273 /* In passthrough mode, call internal PHY driver */ 1274 SOC_IF_ERROR_RETURN 1275 (PHY_LINK_GET(int_pc->pd, unit, port, link)); 1276 } else { 1277 return SOC_E_INTERNAL; 1278 } 1279 } else { /* GMII fiber mode */ 1280 /* PHYs that do not support passthrough mode must read 1281 * link status from 1000X status register if the port 1282 * is in fiber mode. 1283 */ 1284 SOC_IF_ERROR_RETURN 1285 (_phy_5464_fiber_link_get(unit, port, link)); 1286 } 1287 } 1288 if ((pc->automedium) && (pc->fiber.preferred)) { 1289 uint16 copper_transmit; 1290 1291 copper_transmit = 0; 1292 /* Disable/Enable Copper transmitter */ 1293 if (PHY_FIBER_MODE(unit, port)) { 1294 copper_transmit = (*link) ? 0x2000 : 0; 1295 } 1296 SOC_IF_ERROR_RETURN 1297 (MODIFY_PHY5464_MII_ECRr(unit, pc, copper_transmit, 0x2000)); 1298 } 1299 1300 DPRINTF_VERBOSE((BSL_META_U(unit, 1301 "phy_5464_link_get: " 1302 "u=%d p=%d mode=%s%s%s link=%d\n"), 1303 unit, port, 1304 PHY_COPPER_MODE(unit, port) ? "C" : "", 1305 PHY_FIBER_MODE(unit, port) ? "F" : "", 1306 PHY_PASSTHRU_MODE(unit, port) ? "P" : "", 1307 *link)); 1308 1309 return SOC_E_NONE; 1310 } 1311 1312 /* 1313 * Function: 1314 * phy_5464_duplex_set 1315 * Purpose: 1316 * Set the current duplex mode (forced). 1317 * Parameters: 1318 * unit - StrataSwitch unit #. 1319 * port - StrataSwitch port #. 1320 * duplex - Boolean, true indicates full duplex, false indicates half. 1321 * Returns: 1322 * SOC_E_NONE 1323 * SOC_E_UNAVAIL - Half duplex requested, and not supported. 1324 * Notes: 1325 * The duplex is set only for the ACTIVE medium. 1326 * No synchronization performed at this level. 1327 * Autonegotiation is not manipulated. 1328 */ 1329 1330 STATIC int 1331 phy_5464_duplex_set(int unit, soc_port_t port, int duplex) 1332 { 1333 phy_ctrl_t *pc = EXT_PHY_SW_STATE(unit, port); 1334 1335 DPRINTF((BSL_META_U(unit, 1336 "phy_5464_duplex_set: u=%d p=%d d=%d\n"), 1337 unit, port, duplex)); 1338 1339 if (PHY_COPPER_MODE(unit, port)) { 1340 /* Note: mac.c uses phy_5690_notify_duplex to update 5690 */ 1341 SOC_IF_ERROR_RETURN(phy_fe_ge_duplex_set(unit, port, duplex)); 1342 pc->copper.force_duplex = duplex; 1343 } else { /* Fiber */ 1344 if (!duplex) { 1345 return SOC_E_UNAVAIL; 1346 } 1347 } 1348 1349 return SOC_E_NONE; 1350 } 1351 1352 /* 1353 * Function: 1354 * phy_5464_duplex_get 1355 * Purpose: 1356 * Get the current operating duplex mode. If autoneg is enabled, 1357 * then operating mode is returned, otherwise forced mode is returned. 1358 * Parameters: 1359 * unit - StrataSwitch unit #. 1360 * port - StrataSwitch port #. 1361 * duplex - (OUT) Boolean, true indicates full duplex, false 1362 * indicates half. 1363 * Returns: 1364 * SOC_E_NONE 1365 * Notes: 1366 * The duplex is retrieved for the ACTIVE medium. 1367 * No synchronization performed at this level. Autonegotiation is 1368 * not manipulated. 1369 */ 1370 1371 STATIC int 1372 phy_5464_duplex_get(int unit, soc_port_t port, int *duplex) 1373 { 1374 1375 if (PHY_COPPER_MODE(unit, port)) { 1376 return phy_fe_ge_duplex_get(unit, port, duplex); 1377 } 1378 1379 *duplex = TRUE; 1380 1381 return SOC_E_NONE; 1382 } 1383 1384 /* 1385 * Function: 1386 * phy_5464_speed_set 1387 * Purpose: 1388 * Set the current operating speed (forced). 1389 * Parameters: 1390 * unit - StrataSwitch unit #. 1391 * port - StrataSwitch port #. 1392 * speed - Requested speed, only 1000 supported. 1393 * Returns: 1394 * SOC_E_XXX 1395 * Notes: 1396 * The speed is set only for the ACTIVE medium. 1397 */ 1398 1399 STATIC int 1400 phy_5464_speed_set(int unit, soc_port_t port, int speed) 1401 { 1402 int rv; 1403 phy_ctrl_t *pc = EXT_PHY_SW_STATE(unit, port); 1404 1405 DPRINTF((BSL_META_U(unit, 1406 "phy_5464_speed_set: u=%d p=%d s=%d fiber=%d\n"), 1407 unit, port, speed, PHY_FIBER_MODE(unit, port))); 1408 1409 rv = SOC_E_NONE; 1410 if (PHY_COPPER_MODE(unit, port)) { 1411 /* Note: mac.c uses phy_5690_notify_speed to update 5690 */ 1412 rv = (phy_fe_ge_speed_set(unit, port, speed)); 1413 if (SOC_SUCCESS(rv)) { 1414 pc->copper.force_speed = speed; 1415 } 1416 } else { /* Fiber */ 1417 if (PHY_PASSTHRU_MODE(unit, port)) { 1418 phy_ctrl_t *int_pc; /* PHY software state */ 1419 1420 int_pc = INT_PHY_SW_STATE(unit, port); 1421 1422 if (NULL != int_pc) { 1423 /* In passthrough mode, call internal PHY driver */ 1424 rv = PHY_SPEED_SET(int_pc->pd, unit, port, speed); 1425 } else { 1426 rv = SOC_E_INTERNAL; 1427 } 1428 1429 } else if (speed != 0 && speed != 1000) { 1430 rv = SOC_E_CONFIG; 1431 } 1432 1433 if (SOC_SUCCESS(rv)) { 1434 pc->fiber.force_speed = speed; 1435 } 1436 } 1437 1438 return rv; 1439 } 1440 1441 /* 1442 * Function: 1443 * phy_5464_speed_get 1444 * Purpose: 1445 * Get the current operating speed for a 5464 device. 1446 * Parameters: 1447 * unit - StrataSwitch unit #. 1448 * port - StrataSwitch port #. 1449 * duplex - (OUT) Boolean, true indicates full duplex, false 1450 * indicates half. 1451 * Returns: 1452 * SOC_E_NONE 1453 * Notes: 1454 * The speed is retrieved for the ACTIVE medium. 1455 */ 1456 1457 STATIC int 1458 phy_5464_speed_get(int unit, soc_port_t port, int *speed) 1459 { 1460 if (PHY_COPPER_MODE(unit, port)) { 1461 return phy_fe_ge_speed_get(unit, port, speed); 1462 } else { 1463 if (PHY_PASSTHRU_MODE(unit, port)) { 1464 /* If the PHY is in passthrough mode, phyctrl calls the inner 1465 * PHY driver speed_get(). 1466 */ 1467 return SOC_E_INTERNAL; 1468 } else { 1469 *speed = 1000; 1470 } 1471 } 1472 1473 return SOC_E_NONE; 1474 } 1475 1476 /* 1477 * Function: 1478 * phy_5464_master_set 1479 * Purpose: 1480 * Set the current master mode 1481 * Parameters: 1482 * unit - StrataSwitch unit #. 1483 * port - StrataSwitch port #. 1484 * master - SOC_PORT_MS_* 1485 * Returns: 1486 * SOC_E_XXX 1487 * Notes: 1488 * The master mode is set only for the ACTIVE medium. 1489 */ 1490 1491 STATIC int 1492 phy_5464_master_set(int unit, soc_port_t port, int master) 1493 { 1494 phy_ctrl_t *pc = EXT_PHY_SW_STATE(unit, port); 1495 1496 DPRINTF((BSL_META_U(unit, 1497 "phy_5464_master_set: u=%d p=%d master=%d fiber=%d\n"), 1498 unit, port, master, PHY_FIBER_MODE(unit, port))); 1499 1500 if (PHY_COPPER_MODE(unit, port)) { 1501 SOC_IF_ERROR_RETURN(phy_fe_ge_master_set(unit, port, master)); 1502 pc->copper.master = master; 1503 return SOC_E_NONE; 1504 } else { 1505 return SOC_E_NONE; 1506 } 1507 } 1508 1509 /* 1510 * Function: 1511 * phy_5464_master_get 1512 * Purpose: 1513 * Get the current master mode for a 5464 device. 1514 * Parameters: 1515 * unit - StrataSwitch unit #. 1516 * port - StrataSwitch port #. 1517 * master - (OUT) SOC_PORT_MS_* 1518 * Returns: 1519 * SOC_E_NONE 1520 * Notes: 1521 * The master mode is retrieved for the ACTIVE medium. 1522 */ 1523 1524 STATIC int 1525 phy_5464_master_get(int unit, soc_port_t port, int *master) 1526 { 1527 if (PHY_COPPER_MODE(unit, port)) { 1528 return phy_fe_ge_master_get(unit, port, master); 1529 } else { 1530 if (PHY_PASSTHRU_MODE(unit, port)) { 1531 /* If the PHY is in passthrough mode, phyctrl calls the inner 1532 * PHY driver master_get(). 1533 */ 1534 return SOC_E_INTERNAL; 1535 } else { 1536 *master = SOC_PORT_MS_NONE; 1537 } 1538 } 1539 return SOC_E_NONE; 1540 } 1541 1542 /* 1543 * Function: 1544 * phy_5464_autoneg_set 1545 * Purpose: 1546 * Enable or disable auto-negotiation on the specified port. 1547 * Parameters: 1548 * unit - StrataSwitch unit #. 1549 * port - StrataSwitch port #. 1550 * autoneg - Boolean, if true, auto-negotiation is enabled 1551 * (and/or restarted). If false, autonegotiation is disabled. 1552 * Returns: 1553 * SOC_E_XXX 1554 * Notes: 1555 * The autoneg mode is set only for the ACTIVE medium. 1556 */ 1557 1558 STATIC int 1559 phy_5464_autoneg_set(int unit, soc_port_t port, int autoneg) 1560 { 1561 uint16 autoneg_enable; 1562 int rv; 1563 phy_ctrl_t *pc; 1564 1565 pc = EXT_PHY_SW_STATE(unit, port); 1566 rv = SOC_E_NONE; 1567 autoneg_enable = 0; 1568 1569 if (PHY_COPPER_MODE(unit, port)) { 1570 /* Set auto-neg on PHY */ 1571 rv = phy_fe_ge_an_set(unit, port, autoneg); 1572 if (SOC_SUCCESS(rv)) { 1573 pc->copper.autoneg_enable = autoneg ? 1 : 0; 1574 } 1575 } else { /* Fiber mode */ 1576 if (PHY_PASSTHRU_MODE(unit, port)) { 1577 phy_ctrl_t *int_pc; /* PHY software state */ 1578 1579 int_pc = INT_PHY_SW_STATE(unit, port); 1580 1581 if (NULL != int_pc) { 1582 /* In passthrough mode, call internal PHY driver */ 1583 rv = PHY_AUTO_NEGOTIATE_SET(int_pc->pd, unit, port, autoneg); 1584 } else { 1585 rv = SOC_E_INTERNAL; 1586 } 1587 } else { 1588 autoneg_enable = (autoneg) ? MII_CTRL_AE | MII_CTRL_RAN: 0; 1589 rv = MODIFY_PHY5464_1000X_MII_CTRLr(unit, pc, autoneg_enable, 1590 MII_CTRL_AE | MII_CTRL_RAN); 1591 } 1592 if (SOC_SUCCESS(rv)) { 1593 pc->fiber.autoneg_enable = autoneg ? 1 : 0; 1594 } 1595 } 1596 1597 DPRINTF((BSL_META_U(unit, 1598 "phy_5464_autoneg_set: u=%d p=%d autoneg=%d " 1599 "auto=%d rv=%d\n"), 1600 unit, port, autoneg, pc->automedium, rv)); 1601 1602 return rv; 1603 } 1604 1605 STATIC int 1606 _phy_5464_fiber_an_get(int unit, soc_port_t port, 1607 int *autoneg, int *autoneg_done) 1608 { 1609 uint16 mii_ctrl; 1610 uint16 mii_stat; 1611 phy_ctrl_t *pc; 1612 1613 pc = EXT_PHY_SW_STATE(unit, port); 1614 1615 SOC_IF_ERROR_RETURN 1616 (READ_PHY5464_1000X_MII_CTRLr(unit, pc, &mii_ctrl)); 1617 1618 SOC_IF_ERROR_RETURN 1619 (READ_PHY5464_1000X_MII_STATr(unit, pc, &mii_stat)); 1620 1621 *autoneg = (mii_ctrl & MII_CTRL_AE) ? TRUE : FALSE; 1622 *autoneg_done = (mii_stat & MII_STAT_AN_DONE) ? TRUE : FALSE; 1623 1624 return(SOC_E_NONE); 1625 } 1626 1627 /* 1628 * Function: 1629 * phy_5464_autoneg_get 1630 * Purpose: 1631 * Get the current auto-negotiation status (enabled/busy). 1632 * Parameters: 1633 * unit - StrataSwitch unit #. 1634 * port - StrataSwitch port #. 1635 * autoneg - (OUT) if true, auto-negotiation is enabled. 1636 * autoneg_done - (OUT) if true, auto-negotiation is complete. This 1637 * value is undefined if autoneg == FALSE. 1638 * Returns: 1639 * SOC_E_XXX 1640 * Notes: 1641 * The autoneg mode is retrieved for the ACTIVE medium. 1642 */ 1643 1644 STATIC int 1645 phy_5464_autoneg_get(int unit, soc_port_t port, 1646 int *autoneg, int *autoneg_done) 1647 { 1648 int rv; 1649 1650 rv = SOC_E_NONE; 1651 1652 if (PHY_COPPER_MODE(unit, port)) { 1653 rv = phy_fe_ge_an_get(unit, port, autoneg, autoneg_done); 1654 } else { /* Fiber mode */ 1655 if (PHY_PASSTHRU_MODE(unit, port)) { /* switch SerDes passthrough PHY */ 1656 /* If the PHY is in passthrough mode, phyctrl calls the inner 1657 * PHY driver autoneg_set(). 1658 */ 1659 rv = SOC_E_INTERNAL; 1660 } else { /* switch GMII to SerDes optical module */ 1661 rv = _phy_5464_fiber_an_get(unit, port, autoneg, autoneg_done); 1662 } 1663 } 1664 return rv; 1665 } 1666 1667 STATIC int 1668 _phy_5464_fiber_adv_local_set(int unit, soc_port_t port, 1669 soc_port_mode_t mode) 1670 { 1671 uint16 mii_adv; /* Value to advertise */ 1672 phy_ctrl_t *pc; /* PHY software state */ 1673 1674 pc = EXT_PHY_SW_STATE(unit, port); 1675 1676 mii_adv = 0; 1677 if (mode & SOC_PM_1000MB_FD) mii_adv |= (1<<5); 1678 if (mode & SOC_PM_1000MB_HD) mii_adv |= (1<<6); 1679 switch (mode & SOC_PM_PAUSE) { 1680 case SOC_PM_PAUSE: 1681 mii_adv |= (0x1 << 7); 1682 break; 1683 case SOC_PM_PAUSE_TX: 1684 mii_adv |= (0x2 << 7); 1685 break; 1686 case SOC_PM_PAUSE_RX: 1687 mii_adv |= (0x3 << 7); 1688 break; 1689 } 1690 SOC_IF_ERROR_RETURN 1691 (WRITE_PHY5464_1000X_MII_ANAr(unit, pc, mii_adv)); 1692 1693 return SOC_E_NONE; 1694 } 1695 1696 /* 1697 * Function: 1698 * phy_5464_adv_local_set 1699 * Purpose: 1700 * Set the current advertisement for auto-negotiation. 1701 * Parameters: 1702 * unit - StrataSwitch unit #. 1703 * port - StrataSwitch port #. 1704 * mode - Port mode mask indicating supported options/speeds. 1705 * Returns: 1706 * SOC_E_XXX 1707 * Notes: 1708 * The advertisement is set only for the ACTIVE medium. 1709 * No synchronization performed at this level. 1710 */ 1711 1712 STATIC int 1713 phy_5464_adv_local_set(int unit, soc_port_t port, soc_port_mode_t mode) 1714 { 1715 int rv; /* Return value */ 1716 phy_ctrl_t *pc; /* PHY software state */ 1717 soc_port_ability_t ability; 1718 1719 pc = EXT_PHY_SW_STATE(unit, port); 1720 1721 rv = SOC_E_NONE; 1722 1723 if (PHY_COPPER_MODE(unit, port)) { 1724 rv = phy_ge_adv_local_set(unit, port, mode); 1725 if (SOC_SUCCESS(rv)) { 1726 pc->copper.autoneg_advert = mode & ADVERT_ALL_COPPER; 1727 } 1728 } else { /* PHY_FIBER_MODE */ 1729 if (PHY_PASSTHRU_MODE(unit, port)) { /* passthrough fiber mode */ 1730 phy_ctrl_t *int_pc; /* PHY software state */ 1731 1732 int_pc = INT_PHY_SW_STATE(unit, port); 1733 1734 if (NULL != int_pc) { 1735 /* In passthrough mode, call internal PHY driver */ 1736 rv = PHY_ADV_SET(int_pc->pd, unit, port, mode); 1737 if (SOC_E_UNAVAIL == rv) { 1738 rv = soc_port_mode_to_ability(mode, &ability); 1739 if (SOC_SUCCESS(rv)) { 1740 rv = (PHY_ABILITY_ADVERT_SET(int_pc->pd, unit, 1741 port, &ability)); 1742 } 1743 } 1744 } else { 1745 rv = SOC_E_INTERNAL; 1746 } 1747 } else { /* GMII Fiber mode */ 1748 rv = _phy_5464_fiber_adv_local_set(unit, port, mode); 1749 } 1750 1751 if (SOC_SUCCESS(rv)) { 1752 pc->fiber.autoneg_advert = mode & ADVERT_ALL_FIBER; 1753 } 1754 } 1755 1756 DPRINTF((BSL_META_U(unit, 1757 "phy_5464_adv_local_set: u=%d p=%d mode=0x%x rv=%d\n"), 1758 unit, port, mode, rv)); 1759 1760 return rv; 1761 } 1762 1763 STATIC int 1764 _phy_5464_fiber_adv_local_get(int unit, soc_port_t port, 1765 soc_port_mode_t *mode) 1766 { 1767 uint16 mii_adv; /* MII advertise register of the PHY */ 1768 phy_ctrl_t *pc; /* PHY software state */ 1769 1770 pc = EXT_PHY_SW_STATE(unit, port); 1771 1772 /* read the 1000BASE-X Auto-Neg Advertisement Reg( Address 0x04 ) */ 1773 SOC_IF_ERROR_RETURN 1774 (READ_PHY5464_1000X_MII_ANAr(unit, pc, &mii_adv)); 1775 1776 *mode = 0; 1777 if (mii_adv & (1 << 5)) *mode |= SOC_PM_1000MB_FD; 1778 if (mii_adv & (1 << 6)) *mode |= SOC_PM_1000MB_HD; 1779 switch ((mii_adv >> 7) & 0x3) { 1780 case 0x1: 1781 *mode |= SOC_PM_PAUSE; 1782 break; 1783 case 0x2: 1784 *mode |= SOC_PM_PAUSE_TX; 1785 break; 1786 case 0x3: 1787 *mode |= SOC_PM_PAUSE_RX; 1788 break; 1789 } 1790 1791 return SOC_E_NONE; 1792 } 1793 1794 /* 1795 * Function: 1796 * phy_5464_adv_local_get 1797 * Purpose: 1798 * Get the current advertisement for auto-negotiation. 1799 * Parameters: 1800 * unit - StrataSwitch unit #. 1801 * port - StrataSwitch port #. 1802 * mode - (OUT) Port mode mask indicating supported options/speeds. 1803 * Returns: 1804 * SOC_E_XXX 1805 * Notes: 1806 * The advertisement is retrieved for the ACTIVE medium. 1807 * No synchronization performed at this level. 1808 */ 1809 1810 STATIC int 1811 phy_5464_adv_local_get(int unit, soc_port_t port, soc_port_mode_t *mode) 1812 { 1813 int rv; /* Return value */ 1814 1815 rv = SOC_E_NONE; 1816 if (PHY_COPPER_MODE(unit, port)) { 1817 rv = phy_ge_adv_local_get(unit, port, mode); 1818 } else { /* PHY_FIBER_MODE */ 1819 if (PHY_PASSTHRU_MODE(unit, port)) { /* passthrough fiber mode */ 1820 /* If the PHY is in passthrough mode, phyctrl calls the inner 1821 * PHY driver adv_local_get. 1822 */ 1823 rv = SOC_E_INTERNAL; 1824 } else { /* GMII fiber mode */ 1825 rv = _phy_5464_fiber_adv_local_get(unit, port, mode); 1826 } 1827 } 1828 1829 return rv; 1830 } 1831 1832 STATIC int 1833 _phy_5464_fiber_adv_remote_get(int unit, soc_port_t port, 1834 soc_port_mode_t *mode) 1835 { 1836 uint16 mii_ctrl; /* MII control register */ 1837 uint16 mii_stat; /* MII status register */ 1838 uint16 mii_anp; /* MII link partner ability reg */ 1839 phy_ctrl_t *pc; /* PHY software state */ 1840 1841 pc = EXT_PHY_SW_STATE(unit, port); 1842 1843 /* read the 1000BASE-X Control Reg ( Address 0x00 ) */ 1844 SOC_IF_ERROR_RETURN 1845 (READ_PHY5464_1000X_MII_CTRLr(unit, pc, &mii_ctrl)); 1846 if (!(mii_ctrl & MII_CTRL_AE)) { 1847 return SOC_E_DISABLED; 1848 } 1849 1850 /* read the 1000Base-X Status Reg ( Address 0x01 ) */ 1851 SOC_IF_ERROR_RETURN 1852 (READ_PHY5464_1000X_MII_STATr(unit, pc, &mii_stat)); 1853 if (!(mii_stat & MII_STAT_AN_DONE)) { 1854 return SOC_E_NONE; 1855 } 1856 1857 /* read the auto-Neg Link Partner Ability Reg ( Address 0x05 ) */ 1858 SOC_IF_ERROR_RETURN 1859 (READ_PHY5464_1000X_MII_ANPr(unit, pc, &mii_anp)); 1860 1861 *mode = 0; 1862 if (mii_anp & (1 << 5)) *mode |= SOC_PM_1000MB_FD; 1863 if (mii_anp & (1 << 6)) *mode |= SOC_PM_1000MB_HD; 1864 switch ((mii_anp >> 7) & 0x3) { 1865 case 0x1: 1866 *mode |= SOC_PM_PAUSE; 1867 break; 1868 case 0x2: 1869 *mode |= SOC_PM_PAUSE_TX; 1870 break; 1871 case 0x3: 1872 *mode |= SOC_PM_PAUSE_RX; 1873 break; 1874 } 1875 return SOC_E_NONE; 1876 } 1877 1878 /* 1879 * Function: 1880 * phy_5464_adv_remote_get 1881 * Purpose: 1882 * Get partners current advertisement for auto-negotiation. 1883 * Parameters: 1884 * unit - StrataSwitch unit #. 1885 * port - StrataSwitch port #. 1886 * mode - (OUT) Port mode mask indicating supported options/speeds. 1887 * Returns: 1888 * SOC_E_XXX 1889 * Notes: 1890 * The remote advertisement is retrieved for the ACTIVE medium. 1891 * No synchronization performed at this level. If Autonegotiation is 1892 * disabled or in progress, this routine will return an error. 1893 */ 1894 1895 STATIC int 1896 phy_5464_adv_remote_get(int unit, soc_port_t port, soc_port_mode_t *mode) 1897 { 1898 int rv; 1899 1900 if (NULL == mode) { 1901 return SOC_E_PARAM; 1902 } 1903 1904 rv = SOC_E_NONE; 1905 if (PHY_COPPER_MODE(unit, port)) { 1906 rv = phy_ge_adv_remote_get(unit, port, mode); 1907 } else { /* PHY_FIBER_MODE */ 1908 if (PHY_PASSTHRU_MODE(unit, port)) { 1909 /* If the PHY is in passthrough mode, the inner PHY driver 1910 * adv_remote_get is called by phyctrl. 1911 */ 1912 rv = SOC_E_INTERNAL; 1913 } else { /* GMII fiber mode */ 1914 rv = _phy_5464_fiber_adv_remote_get(unit, port, mode); 1915 } 1916 } 1917 return rv; 1918 } 1919 1920 /* 1921 * Function: 1922 * phy_5464_lb_set 1923 * Purpose: 1924 * Set the local PHY loopback mode. 1925 * Parameters: 1926 * unit - StrataSwitch unit #. 1927 * port - StrataSwitch port #. 1928 * loopback - Boolean: true = enable loopback, false = disable. 1929 * Returns: 1930 * SOC_E_XXX 1931 * Notes: 1932 * The loopback mode is set only for the ACTIVE medium. 1933 * No synchronization performed at this level. 1934 */ 1935 1936 STATIC int 1937 phy_5464_lb_set(int unit, soc_port_t port, int enable) 1938 { 1939 int rv; /* Return value */ 1940 phy_ctrl_t *pc; /* PHY software state */ 1941 1942 rv = SOC_E_NONE; 1943 pc = EXT_PHY_SW_STATE(unit, port); 1944 1945 if (PHY_COPPER_MODE(unit, port)) { 1946 #if AUTO_MDIX_WHEN_AN_DIS 1947 { 1948 uint16 tmp; 1949 1950 /* Disable Auto-MDIX When autoneg disabled */ 1951 /* Enable Auto-MDIX When autoneg disabled */ 1952 tmp = (enable) ? 0x0000: 0x0200; 1953 SOC_IF_ERROR_RETURN 1954 (MODIFY_PHY5464_MII_MISC_CTRLr(unit, pc, tmp, 0x0200)); 1955 } 1956 #endif 1957 rv = phy_fe_ge_lb_set(unit, port, enable); 1958 #ifdef BCM_HAWKEYE_SUPPORT 1959 if (PHY_IS_BCM53314(pc)) { 1960 /* Force link bit in MII_AUX_MULTI_PHY register has to be set 1961 * so that the PHY is forced to show link up without 1962 * the link partner 1963 * WA for 10/100 Mbps in MAC/PHY loopback issue */ 1964 1965 uint16 mii_aux_multi_phy; 1966 SOC_IF_ERROR_RETURN 1967 (READ_PHYFEGE_MII_AUX_MULTI_PHYr(unit, pc, &mii_aux_multi_phy)); 1968 if(enable) { 1969 mii_aux_multi_phy = mii_aux_multi_phy | 0x1000; 1970 }else { 1971 mii_aux_multi_phy = mii_aux_multi_phy & (~0x1000); 1972 } 1973 SOC_IF_ERROR_RETURN 1974 (WRITE_PHYFEGE_MII_AUX_MULTI_PHYr(unit, pc, 1975 mii_aux_multi_phy)); 1976 } 1977 #endif 1978 1979 } else { /* Fiber mode */ 1980 if (PHY_PASSTHRU_MODE(unit, port)) { 1981 phy_ctrl_t *int_pc; /* PHY software state */ 1982 1983 int_pc = INT_PHY_SW_STATE(unit, port); 1984 1985 if (NULL != int_pc) { 1986 rv = (PHY_LOOPBACK_SET(int_pc->pd, unit, port, enable)); 1987 } else { 1988 rv = SOC_E_INTERNAL; 1989 } 1990 } else { /* GMII fiber mode */ 1991 uint16 lb; 1992 lb = (enable) ? MII_CTRL_LE : 0; 1993 rv = MODIFY_PHY5464_1000X_MII_CTRLr(unit, pc, lb, MII_CTRL_LE); 1994 } 1995 } 1996 1997 DPRINTF((BSL_META_U(unit, 1998 "phy_5464_lb_set: u=%d p=%d en=%d rv=%d\n"), 1999 unit, port, enable, rv)); 2000 return rv; 2001 } 2002 2003 /* 2004 * Function: 2005 * phy_5464_lb_get 2006 * Purpose: 2007 * Get the local PHY loopback mode. 2008 * Parameters: 2009 * unit - StrataSwitch unit #. 2010 * port - StrataSwitch port #. 2011 * loopback - (OUT) Boolean: true = enable loopback, false = disable. 2012 * Returns: 2013 * SOC_E_XXX 2014 * Notes: 2015 * The loopback mode is retrieved for the ACTIVE medium. 2016 */ 2017 2018 STATIC int 2019 phy_5464_lb_get(int unit, soc_port_t port, int *enable) 2020 { 2021 int rv; 2022 phy_ctrl_t *pc; 2023 2024 rv = SOC_E_NONE; 2025 pc = EXT_PHY_SW_STATE(unit, port); 2026 if (PHY_COPPER_MODE(unit, port)) { 2027 rv = phy_fe_ge_lb_get(unit, port, enable); 2028 } else { /* fiber mode */ 2029 if (PHY_PASSTHRU_MODE(unit, port)) { 2030 /* If the PHY is in passthrough mode, *\ 2031 \* phyctrl calls inner PHY driver loopback get() */ 2032 phy_ctrl_t *int_pc; /* PHY software state */ 2033 int_pc = INT_PHY_SW_STATE(unit, port); 2034 2035 if ( NULL != int_pc ) { 2036 rv = (PHY_LOOPBACK_GET(int_pc->pd, unit, port, enable)); 2037 } else { 2038 rv = SOC_E_INTERNAL; 2039 } 2040 } else { /* GMII fiber mode */ 2041 uint16 mii_ctrl; 2042 rv = READ_PHY5464_1000X_MII_CTRLr(unit, pc, &mii_ctrl); 2043 *enable = (mii_ctrl & MII_CTRL_LE) ? 1 : 0; 2044 } 2045 } 2046 return rv; 2047 } 2048 2049 /* 2050 * Function: 2051 * phy_5464_interface_set 2052 * Purpose: 2053 * Set the current operating mode of the PHY. 2054 * Parameters: 2055 * unit - StrataSwitch unit #. 2056 * port - StrataSwitch port #. 2057 * pif - one of SOC_PORT_IF_* 2058 * Returns: 2059 * SOC_E_NONE - success 2060 * SOC_E_UNAVAIL - unsupported interface 2061 */ 2062 2063 STATIC int 2064 phy_5464_interface_set(int unit, soc_port_t port, soc_port_if_t pif) 2065 { 2066 COMPILER_REFERENCE(unit); 2067 COMPILER_REFERENCE(port); 2068 2069 switch (pif) { 2070 case SOC_PORT_IF_GMII: 2071 case SOC_PORT_IF_SGMII: 2072 return SOC_E_NONE; 2073 default: 2074 return SOC_E_UNAVAIL; 2075 } 2076 } 2077 2078 /* 2079 * Function: 2080 * phy_5464_interface_get 2081 * Purpose: 2082 * Get the current operating mode of the PHY. 2083 * Parameters: 2084 * unit - StrataSwitch unit #. 2085 * port - StrataSwitch port #. 2086 * pif - (OUT) one of SOC_PORT_IF_* 2087 * Returns: 2088 * SOC_E_NONE 2089 */ 2090 2091 STATIC int 2092 phy_5464_interface_get(int unit, soc_port_t port, soc_port_if_t *pif) 2093 { 2094 phy_ctrl_t *pc; 2095 2096 COMPILER_REFERENCE(unit); 2097 COMPILER_REFERENCE(port); 2098 2099 pc = EXT_PHY_SW_STATE(unit, port); 2100 2101 *pif = pc->interface; 2102 2103 return SOC_E_NONE; 2104 } 2105 2106 /* 2107 * Function: 2108 * phy_5464_ability_get 2109 * Purpose: 2110 * Get the abilities of the local gigabit PHY. 2111 * Parameters: 2112 * unit - StrataSwitch unit #. 2113 * port - StrataSwitch port #. 2114 * mode - (OUT) Port mode mask indicating supported options/speeds. 2115 * Returns: 2116 * SOC_E_NONE 2117 * Notes: 2118 * The ability is retrieved only for the ACTIVE medium. 2119 */ 2120 2121 STATIC int 2122 phy_5464_ability_get(int unit, soc_port_t port, soc_port_mode_t *mode) 2123 { 2124 int rv; 2125 phy_ctrl_t *pc; 2126 2127 rv = SOC_E_NONE; 2128 if (PHY_COPPER_MODE(unit, port)) { 2129 rv = phy_ge_ability_get(unit, port, mode); 2130 } else { /* fiber mode */ 2131 *mode = (SOC_PM_AN | SOC_PM_LB_PHY | 2132 SOC_PM_PAUSE | SOC_PM_PAUSE_ASYMM | 2133 SOC_PM_GMII | SOC_PM_SGMII | SOC_PM_1000MB_FD); 2134 } 2135 2136 pc = EXT_PHY_SW_STATE(unit, port); 2137 if (pc->automedium) { 2138 *mode |= SOC_PM_COMBO; 2139 } 2140 2141 return rv; 2142 } 2143 2144 /* 2145 * Function: 2146 * phy_5464_mdix_set 2147 * Description: 2148 * Set the Auto-MDIX mode of a port/PHY 2149 * Parameters: 2150 * unit - Device number 2151 * port - Port number 2152 * mode - One of: 2153 * SOC_PORT_MDIX_AUTO 2154 * Enable auto-MDIX when autonegotiation is enabled 2155 * SOC_PORT_MDIX_FORCE_AUTO 2156 * Enable auto-MDIX always 2157 * SOC_PORT_MDIX_NORMAL 2158 * Disable auto-MDIX 2159 * SOC_PORT_MDIX_XOVER 2160 * Disable auto-MDIX, and swap cable pairs 2161 * Return Value: 2162 * SOC_E_XXX 2163 */ 2164 STATIC int 2165 phy_5464_mdix_set(int unit, soc_port_t port, soc_port_mdix_t mode) 2166 { 2167 phy_ctrl_t *pc; 2168 int speed; 2169 2170 pc = EXT_PHY_SW_STATE(unit, port); 2171 2172 if (PHY_COPPER_MODE(unit, port)) { 2173 switch (mode) { 2174 case SOC_PORT_MDIX_AUTO: 2175 /* Clear bit 14 for automatic MDI crossover */ 2176 SOC_IF_ERROR_RETURN 2177 (MODIFY_PHY5464_MII_ECRr(unit, pc, 0, 0x4000)); 2178 2179 /* 2180 * Write the result in the register 0x18, shadow copy 7 2181 */ 2182 /* Clear bit 9 to disable forced auto MDI xover */ 2183 SOC_IF_ERROR_RETURN 2184 (MODIFY_PHY5464_MII_MISC_CTRLr(unit, pc, 0, 0x0200)); 2185 break; 2186 2187 case SOC_PORT_MDIX_FORCE_AUTO: 2188 /* Clear bit 14 for automatic MDI crossover */ 2189 SOC_IF_ERROR_RETURN 2190 (MODIFY_PHY5464_MII_ECRr(unit, pc, 0, 0x4000)); 2191 2192 /* 2193 * Write the result in the register 0x18, shadow copy 7 2194 */ 2195 /* Set bit 9 to force automatic MDI crossover */ 2196 SOC_IF_ERROR_RETURN 2197 (MODIFY_PHY5464_MII_MISC_CTRLr(unit, pc, 0x0200, 0x0200)); 2198 break; 2199 2200 case SOC_PORT_MDIX_NORMAL: 2201 SOC_IF_ERROR_RETURN(phy_5464_speed_get(unit, port, &speed)); 2202 if (speed == 0 || speed == 10 || speed == 100) { 2203 /* Set bit 14 for manual MDI crossover */ 2204 SOC_IF_ERROR_RETURN 2205 (MODIFY_PHY5464_MII_ECRr(unit, pc, 0x4000, 0x4000)); 2206 2207 SOC_IF_ERROR_RETURN 2208 (WRITE_PHY5464_TEST1r(unit, pc, 0)); 2209 } else { 2210 return SOC_E_UNAVAIL; 2211 } 2212 break; 2213 2214 case SOC_PORT_MDIX_XOVER: 2215 SOC_IF_ERROR_RETURN(phy_5464_speed_get(unit, port, &speed)); 2216 if (speed == 0 || speed == 10 || speed == 100) { 2217 /* Set bit 14 for manual MDI crossover */ 2218 SOC_IF_ERROR_RETURN 2219 (MODIFY_PHY5464_MII_ECRr(unit, pc, 0x4000, 0x4000)); 2220 2221 SOC_IF_ERROR_RETURN 2222 (WRITE_PHY5464_TEST1r(unit, pc, 0x0080)); 2223 } else { 2224 return SOC_E_UNAVAIL; 2225 } 2226 break; 2227 2228 default: 2229 return SOC_E_PARAM; 2230 } 2231 2232 pc->copper.mdix = mode; 2233 return SOC_E_NONE; 2234 } else { /* fiber */ 2235 if (mode != SOC_PORT_MDIX_NORMAL) { 2236 return SOC_E_UNAVAIL; 2237 } 2238 return SOC_E_NONE; 2239 } 2240 } 2241 2242 #if defined(BCM_WARM_BOOT_SUPPORT) 2243 /* 2244 * Function: 2245 * phy_5464_mdix_wb_update 2246 * Description: 2247 * Read the hw, derive the mode and update the mdix mode in sw structure 2248 * during wb. 2249 * Parameters: 2250 * unit - (IN) Device number 2251 * port - (IN) Port number 2252 * mode - (OUT) :One of: 2253 * SOC_PORT_MDIX_AUTO 2254 * Enable auto-MDIX when autonegotiation is enabled 2255 * SOC_PORT_MDIX_FORCE_AUTO 2256 * Enable auto-MDIX always 2257 * SOC_PORT_MDIX_NORMAL 2258 * Disable auto-MDIX 2259 * SOC_PORT_MDIX_XOVER 2260 * Disable auto-MDIX, and swap cable pairs 2261 * Return Value: 2262 * SOC_E_XXX 2263 */ 2264 STATIC int 2265 phy_5464_mdix_wb_update(int unit, soc_port_t port) 2266 { 2267 phy_ctrl_t *pc; 2268 int speed; 2269 int mode = 0; 2270 uint16 val = 0; 2271 2272 pc = EXT_PHY_SW_STATE(unit, port); 2273 2274 if (PHY_COPPER_MODE(unit, port)) { 2275 /* Check bit 14 for automatic MDI crossover */ 2276 SOC_IF_ERROR_RETURN (READ_PHY5464_MII_ECRr(unit, pc, &val)); 2277 if(val & 0x4000) { 2278 SOC_IF_ERROR_RETURN(phy_5464_speed_get(unit, port, &speed)); 2279 if (speed == 0 || speed == 10 || speed == 100) { 2280 SOC_IF_ERROR_RETURN (READ_PHY5464_TEST1r(unit, pc, &val)); 2281 if (0 == val) { 2282 mode = SOC_PORT_MDIX_NORMAL; 2283 } else if (0x80 == val) { 2284 mode = SOC_PORT_MDIX_XOVER; 2285 } else { 2286 return SOC_E_UNAVAIL; 2287 } 2288 } else { 2289 return SOC_E_UNAVAIL; 2290 } 2291 } else { 2292 /* Check bit 9 forced auto MDI xover */ 2293 SOC_IF_ERROR_RETURN (READ_PHY5464_MII_MISC_CTRLr(unit, pc,&val)); 2294 if (val & 0x200) { 2295 /* bit 9 Set, forced auto MDI xover */ 2296 mode = SOC_PORT_MDIX_FORCE_AUTO; 2297 } else { 2298 /* bit 9 Clear, forced auto MDI xover */ 2299 mode = SOC_PORT_MDIX_AUTO; 2300 } 2301 } 2302 pc->copper.mdix = mode; 2303 } else { 2304 /* fiber */ 2305 mode = SOC_PORT_MDIX_NORMAL; 2306 } 2307 return SOC_E_NONE; 2308 } 2309 #endif /* defined(BCM_WARM_BOOT_SUPPORT) */ 2310 2311 /* 2312 * Function: 2313 * phy_5464_mdix_get 2314 * Description: 2315 * Get the Auto-MDIX mode of a port/PHY 2316 * Parameters: 2317 * unit - Device number 2318 * port - Port number 2319 * mode - (Out) One of: 2320 * SOC_PORT_MDIX_AUTO 2321 * Enable auto-MDIX when autonegotiation is enabled 2322 * SOC_PORT_MDIX_FORCE_AUTO 2323 * Enable auto-MDIX always 2324 * SOC_PORT_MDIX_NORMAL 2325 * Disable auto-MDIX 2326 * SOC_PORT_MDIX_XOVER 2327 * Disable auto-MDIX, and swap cable pairs 2328 * Return Value: 2329 * SOC_E_XXX 2330 */ 2331 STATIC int 2332 phy_5464_mdix_get(int unit, soc_port_t port, soc_port_mdix_t *mode) 2333 { 2334 phy_ctrl_t *pc = EXT_PHY_SW_STATE(unit, port); 2335 int speed; 2336 #if defined(BCM_WARM_BOOT_SUPPORT) 2337 int rv; 2338 #endif /* defined(BCM_WARM_BOOT_SUPPORT) */ 2339 2340 if (mode == NULL || pc == NULL) { 2341 return SOC_E_PARAM; 2342 } 2343 #if defined(BCM_WARM_BOOT_SUPPORT) 2344 if (SOC_WARM_BOOT(unit)) { 2345 rv = phy_5464_mdix_wb_update(unit, port); 2346 return rv; 2347 } 2348 #endif /* defined(BCM_WARM_BOOT_SUPPORT) */ 2349 if (PHY_COPPER_MODE(unit, port)) { 2350 SOC_IF_ERROR_RETURN(phy_5464_speed_get(unit, port, &speed)); 2351 if (speed == 1000) { 2352 *mode = SOC_PORT_MDIX_AUTO; 2353 } else { 2354 *mode = pc->copper.mdix; 2355 } 2356 } else { 2357 *mode = SOC_PORT_MDIX_NORMAL; 2358 } 2359 2360 return SOC_E_NONE; 2361 } 2362 2363 /* 2364 * Function: 2365 * phy_5464_mdix_status_get 2366 * Description: 2367 * Get the current MDIX status on a port/PHY 2368 * Parameters: 2369 * unit - Device number 2370 * port - Port number 2371 * status - (OUT) One of: 2372 * SOC_PORT_MDIX_STATUS_NORMAL 2373 * Straight connection 2374 * SOC_PORT_MDIX_STATUS_XOVER 2375 * Crossover has been performed 2376 * Return Value: 2377 * SOC_E_XXX 2378 */ 2379 STATIC int 2380 phy_5464_mdix_status_get(int unit, soc_port_t port, 2381 soc_port_mdix_status_t *status) 2382 { 2383 phy_ctrl_t *pc = EXT_PHY_SW_STATE(unit, port); 2384 uint16 tmp; 2385 2386 if (status == NULL || pc == NULL) { 2387 return SOC_E_PARAM; 2388 } 2389 2390 if (PHY_COPPER_MODE(unit, port)) { 2391 SOC_IF_ERROR_RETURN 2392 (READ_PHY5464_MII_ESRr(unit, pc, &tmp)); 2393 if (tmp & 0x2000) { 2394 *status = SOC_PORT_MDIX_STATUS_XOVER; 2395 } else { 2396 *status = SOC_PORT_MDIX_STATUS_NORMAL; 2397 } 2398 } else { 2399 *status = SOC_PORT_MDIX_STATUS_NORMAL; 2400 } 2401 2402 return SOC_E_NONE; 2403 } 2404 2405 /* 2406 * Helper defines for expansion register access 2407 * Used throughout the cable diagnostic code 2408 */ 2409 #define WRITE_EXP(_r, _v) \ 2410 SOC_IF_ERROR_RETURN \ 2411 (WRITE_PHY_REG(unit, pc, 0x17, 0x0f00 | _r)); \ 2412 SOC_IF_ERROR_RETURN \ 2413 (WRITE_PHY_REG(unit, pc, 0x15, _v)) 2414 2415 #define READ_EXP(_r, _v) \ 2416 SOC_IF_ERROR_RETURN \ 2417 (WRITE_PHY_REG(unit, pc, 0x17, 0x0f00 | _r)); \ 2418 SOC_IF_ERROR_RETURN \ 2419 (READ_PHY_REG(unit, pc, 0x15, _v)) 2420 2421 2422 /* saved state when running cable diagnostics */ 2423 typedef struct { 2424 uint16 reg_1c; 2425 uint16 reg_00; 2426 uint16 reg_18_0; 2427 uint16 reg_18_7; 2428 uint16 reg_17_0; 2429 uint16 reg_17_8100; 2430 } cdiag_setup_t; 2431 2432 /* 2433 * Function: 2434 * phy_5464_cable_diag_setup 2435 * Purpose: 2436 * Put phy into a state that it can run cable diagnostics. 2437 * Various internal state machines must be stopped. 2438 */ 2439 STATIC int 2440 phy_5464_cable_diag_setup(int unit, uint8 phy_addr, cdiag_setup_t *setup) 2441 { 2442 phy_ctrl_t *pc; 2443 uint16 tmp; 2444 soc_port_t port; 2445 2446 port = soc_phy_addr_to_port(unit, phy_addr); 2447 pc = EXT_PHY_SW_STATE(unit, port); 2448 2449 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x1c, 0x7c00)); 2450 SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x1c, &tmp)); 2451 setup->reg_1c = tmp; 2452 tmp &= ~0x0001; /* force copper regs */ 2453 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x1c, tmp | 0x8000)); 2454 2455 SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x00, &tmp)); 2456 setup->reg_00 = tmp; 2457 tmp = 0x0040; /* force 1G, no autoneg */ 2458 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x00, tmp)); 2459 2460 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, 0x0007)); 2461 SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x18, &tmp)); 2462 setup->reg_18_0 = tmp; 2463 tmp |= 0x0800; /* enable sm_dsp clock */ 2464 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, tmp)); 2465 2466 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, 0x7007)); 2467 SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x18, &tmp)); 2468 setup->reg_18_7 = tmp; 2469 tmp &= ~0x200; /* disable auto-mdix */ 2470 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, tmp | 0x8000)); 2471 2472 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x0000)); 2473 SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x15, &tmp)); 2474 setup->reg_17_0 = tmp; 2475 tmp = 0xf8b5; /* disable agc */ 2476 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, tmp)); 2477 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x2000)); 2478 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, tmp)); 2479 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x4000)); 2480 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, tmp)); 2481 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x6000)); 2482 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, tmp)); 2483 2484 /* freeze internal state machines */ 2485 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x8100)); 2486 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x16, 0x0002)); 2487 SOC_IF_ERROR_RETURN(READ_PHY_REG(unit, pc, 0x15, &tmp)); 2488 setup->reg_17_8100 = tmp; 2489 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, 0x0030)); 2490 2491 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x8200)); 2492 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x16, 0x0002)); 2493 2494 return SOC_E_NONE; 2495 } 2496 2497 #ifdef PHY_5464_CABLE_DIAG_UNSETUP 2498 /* 2499 * Function: 2500 * phy_5464_cable_diag_unsetup 2501 * Purpose: 2502 * Restore phy into a working state after cable diagnostics 2503 * have been run. 2504 */ 2505 STATIC int 2506 phy_5464_cable_diag_unsetup(int unit, uint8 phy_addr, cdiag_setup_t *setup) 2507 { 2508 phy_ctrl_t *pc; 2509 2510 pc = EXT_PHY_SW_STATE(unit, port); 2511 2512 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, setup->reg_18_0)); 2513 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x18, 2514 setup->reg_18_7 | 0x8000)); 2515 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x0000)); 2516 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, setup->reg_17_0)); 2517 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x2000)); 2518 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, setup->reg_17_0)); 2519 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x4000)); 2520 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, setup->reg_17_0)); 2521 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x6000)); 2522 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, setup->reg_17_0)); 2523 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x8100)); 2524 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x16, 0x0000)); 2525 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x15, 2526 setup->reg_17_8100)); 2527 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x17, 0x8200)); 2528 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x16, 0x0000)); 2529 2530 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x00, setup->reg_00)); 2531 SOC_IF_ERROR_RETURN(WRITE_PHY_REG(unit, pc, 0x1c, 2532 setup->reg_1c | 0x8000)); 2533 return SOC_E_NONE; 2534 } 2535 #endif /* PHY_5464_CABLE_DIAG_UNSETUP */ 2536 2537 /* cable diag pass info */ 2538 typedef struct { 2539 int skip_14_19; 2540 uint16 set_14; /* expansion regs to set */ 2541 uint16 set_15; 2542 uint16 set_16; 2543 uint16 set_17; 2544 uint16 set_18; 2545 uint16 set_19; 2546 uint16 set_1a; 2547 uint16 set_1b; 2548 uint16 set_1c; 2549 uint16 set_1d; 2550 uint16 set_10; 2551 2552 uint16 get_10; /* expansion reg results */ 2553 uint16 get_11; 2554 uint16 get_12; 2555 uint16 get_13; 2556 } cdiag_pass_t; 2557 2558 /* 2559 * Function: 2560 * phy_5464_cable_diag_pass 2561 * Purpose: 2562 * Run a cable diagnostic pass 2563 */ 2564 STATIC int 2565 phy_5464_cable_diag_pass(int unit, uint8 phy_addr, cdiag_pass_t *pass) 2566 { 2567 phy_ctrl_t *pc; 2568 uint16 tmp; 2569 int loop, maxloop; 2570 soc_port_t port; 2571 2572 port = soc_phy_addr_to_port(unit, phy_addr); 2573 pc = EXT_PHY_SW_STATE(unit, port); 2574 2575 maxloop = 50; /* usually takes about 5 */ 2576 2577 if (pass->skip_14_19 == 0) { 2578 WRITE_EXP(0x14, pass->set_14); 2579 WRITE_EXP(0x15, pass->set_15); 2580 WRITE_EXP(0x16, pass->set_16); 2581 WRITE_EXP(0x17, pass->set_17); 2582 if (PHY_IS_BCM5478(pc) || 2583 PHY_IS_BCM5488(pc)) { 2584 WRITE_EXP(0x1a, pass->set_18); 2585 WRITE_EXP(0x1b, pass->set_19); 2586 } else { 2587 WRITE_EXP(0x18, pass->set_18); 2588 WRITE_EXP(0x19, pass->set_19); 2589 } 2590 } 2591 if (PHY_IS_BCM5478(pc) || 2592 PHY_IS_BCM5488(pc)) { 2593 WRITE_EXP(0x1d, pass->set_1a); 2594 WRITE_EXP(0x1e, pass->set_1b); 2595 WRITE_EXP(0x1f, pass->set_1c); 2596 WRITE_EXP(0x10, pass->set_10 & 0x00ff); 2597 WRITE_EXP(0x28, pass->set_10 & 0xff00); 2598 } else { 2599 WRITE_EXP(0x1a, pass->set_1a); 2600 WRITE_EXP(0x1b, pass->set_1b); 2601 WRITE_EXP(0x1c, pass->set_1c); 2602 WRITE_EXP(0x1d, pass->set_1d); 2603 WRITE_EXP(0x10, pass->set_10); 2604 } 2605 2606 for (loop = 0; loop < maxloop; loop++) { 2607 sal_usleep(1000); 2608 if ((pc->phy_model == PHY_BCM5478_MODEL) || 2609 (pc->phy_model == PHY_BCM5488_MODEL)) { 2610 READ_EXP(0x10, &tmp); 2611 if (tmp & 0x0200) { 2612 pass->get_10 = tmp; 2613 READ_EXP(0x11, &tmp); 2614 break; 2615 } 2616 } else { 2617 READ_EXP(0x11, &tmp); 2618 if (tmp & 0x0001) { 2619 break; 2620 } 2621 } 2622 } 2623 if (loop >= maxloop) { 2624 return SOC_E_TIMEOUT; 2625 } 2626 2627 pass->get_11 = tmp; 2628 READ_EXP(0x12, &pass->get_12); 2629 READ_EXP(0x13, &pass->get_13); 2630 return SOC_E_NONE; 2631 } 2632 2633 #undef WRITE_EXP 2634 #undef READ_EXP 2635 2636 /* 2637 * Function: 2638 * phy_5464_cable_diag 2639 * Purpose: 2640 * Run 546x cable diagnostics 2641 * Parameters: 2642 * unit - device number 2643 * port - port number 2644 * status - (OUT) cable diagnotic status structure 2645 * Returns: 2646 * SOC_E_XXX 2647 */ 2648 extern int phy_5464_cable_diag_sw(int, soc_port_t, soc_port_cable_diag_t *); 2649 STATIC int 2650 phy_5464_cable_diag(int unit, soc_port_t port, 2651 soc_port_cable_diag_t *status) 2652 { 2653 phy_ctrl_t *pc; 2654 uint8 phy_addr; 2655 int rv, rv2, i; 2656 int bcm53314_speed = 0, bcm53314_an = 0, bcm53314_an_done = 0, bcm53314_link = 0; /* BCM53314 WAR Fix */ 2657 2658 pc = EXT_PHY_SW_STATE(unit, port); 2659 phy_addr = pc->phy_id; 2660 2661 if (status == NULL) { 2662 return SOC_E_PARAM; 2663 } 2664 2665 if ( ! PHY_IS_BCM5464(pc) && 2666 ! PHY_IS_BCM5461(pc) && 2667 ! PHY_IS_BCM5482(pc) && 2668 ! PHY_IS_BCM5488(pc) && 2669 ! PHY_IS_BCM54980(pc) && 2670 ! PHY_IS_BCM54980C(pc) && 2671 ! PHY_IS_BCM54980V(pc) && 2672 ! PHY_IS_BCM54980VC(pc) && 2673 ! PHY_IS_BCM53314(pc) && 2674 ! PHY_IS_BCM5462(pc) && 2675 ! PHY_IS_BCM5466(pc) && 2676 ! PHY_IS_BCM5488(pc) && 2677 ! PHY_IS_BCM5482(pc) ) { 2678 return SOC_E_UNAVAIL; 2679 } 2680 2681 status->state = SOC_PORT_CABLE_STATE_OK; 2682 status->npairs = 4; 2683 status->fuzz_len = 0; 2684 for (i = 0; i < 4; i++) { 2685 status->pair_state[i] = SOC_PORT_CABLE_STATE_OK; 2686 } 2687 2688 if (soc_property_get(unit, spn_CABLE_DIAG_HW, PHY_5464_CABLE_DIAG_HW)) { 2689 uint16 tmp, result; 2690 cdiag_setup_t setup_state; 2691 cdiag_pass_t pass; 2692 2693 if (!PHY_IS_BCM5464(pc) && !PHY_IS_BCM5461(pc)) { 2694 return SOC_E_UNAVAIL; 2695 } 2696 2697 SOC_IF_ERROR_RETURN 2698 (READ_PHY_REG(unit, pc, 0x11, &tmp)); 2699 2700 if (tmp & 0x0100) { /* link up? */ 2701 /* is linked at 10 or 100Mbit, diags will fail */ 2702 SOC_IF_ERROR_RETURN 2703 (READ_PHY_REG(unit, pc, 0x19, &tmp)); 2704 if ((tmp & 0x0600) != 0x0600) { 2705 status->npairs = 2; 2706 status->pair_len[0] = -1; 2707 status->pair_len[1] = -1; 2708 return SOC_E_NONE; 2709 } 2710 2711 /* run length checks for defect free cable */ 2712 pass.skip_14_19 = 1; 2713 pass.set_14 = 0; 2714 pass.set_15 = 0; 2715 pass.set_16 = 0; 2716 pass.set_17 = 0; 2717 pass.set_18 = 0; 2718 pass.set_19 = 0; 2719 pass.set_1a = 0x012c; 2720 pass.set_1b = 0x00c8; 2721 pass.set_1c = 0x00a0; 2722 pass.set_1d = 0x6096; 2723 pass.set_10 = 0x0207; 2724 rv = phy_5464_cable_diag_pass(unit, phy_addr, &pass); 2725 if (rv >= 0) { 2726 status->pair_len[0] = pass.get_12 & 0xff; 2727 status->pair_len[1] = (pass.get_12 >> 8) & 0xff; 2728 status->pair_len[2] = pass.get_13 & 0xff; 2729 status->pair_len[3] = (pass.get_13 >> 8) & 0xff; 2730 if (pc->phy_model == PHY_BCM5464_MODEL && 2731 pc->phy_rev == PHY_REV_A0) { 2732 /* A0 has a tendency to give pairs B, D a length of 152 */ 2733 if (status->pair_len[0] < 128 && status->pair_len[1] > 140) { 2734 status->pair_len[1] = status->pair_len[0]; 2735 } 2736 if (status->pair_len[1] < 128 && status->pair_len[0] > 140) { 2737 status->pair_len[0] = status->pair_len[1]; 2738 } 2739 if (status->pair_len[2] < 128 && status->pair_len[3] > 140) { 2740 status->pair_len[3] = status->pair_len[2]; 2741 } 2742 if (status->pair_len[3] < 128 && status->pair_len[2] > 140) { 2743 status->pair_len[2] = status->pair_len[3]; 2744 } 2745 } 2746 } 2747 return rv; 2748 } 2749 2750 /* link not up, so run defect checks */ 2751 2752 /* setup phy for cable diags: disable various automated things */ 2753 MIIM_LOCK(unit); /* this locks out linkscan, essentially */ 2754 rv = phy_5464_cable_diag_setup(unit, phy_addr, &setup_state); 2755 if (rv < 0) { 2756 MIIM_UNLOCK(unit); 2757 return rv; 2758 } 2759 2760 /* 2761 * perhaps some of these per pass magic numbers should come 2762 * from properties, but that's a lot of properties... 2763 */ 2764 2765 2766 /* pass 1 */ 2767 pass.skip_14_19 = 0; 2768 pass.set_14 = 0x5014; /* 0x4650 */ 2769 pass.set_15 = 0x0ed8; /* 0x0bb8 */ 2770 pass.set_16 = 0x0af0; /* 0x09c4 */ 2771 pass.set_17 = 0x047e; /* 0x03e8 */ 2772 pass.set_18 = 0x0a14; 2773 pass.set_19 = 0x3c00; 2774 pass.set_1a = 0x0640; 2775 pass.set_1b = 0x00fa; 2776 pass.set_1c = 0x012c; 2777 pass.set_1d = 0x0a60; 2778 pass.set_10 = 0x000b; 2779 rv = phy_5464_cable_diag_pass(unit, phy_addr, &pass); 2780 2781 if ((pc->phy_model == PHY_BCM5478_MODEL) || 2782 (pc->phy_model == PHY_BCM5488_MODEL)) { 2783 result = pass.get_10 & 0x0400; 2784 } else { 2785 result = pass.get_11 & 0x0002; 2786 } 2787 if (rv >= 0 && result == 0) { 2788 /* pass 2 (no errors found in pass 1) */ 2789 pass.set_14 = 0xffff; 2790 pass.set_15 = 0x04b0; 2791 pass.set_16 = 0x0320; 2792 pass.set_17 = 0x0320; 2793 pass.set_18 = 0x608c; 2794 pass.set_19 = 0xa000; 2795 pass.set_1a = 0x03e8; 2796 pass.set_1b = 0x0258; 2797 pass.set_1c = 0x00b4; 2798 pass.set_1d = 0x0a14; 2799 pass.set_10 = 0x000b; 2800 rv = phy_5464_cable_diag_pass(unit, phy_addr, &pass); 2801 } 2802 2803 if ((pc->phy_model == PHY_BCM5478_MODEL) || 2804 (pc->phy_model == PHY_BCM5488_MODEL)) { 2805 result = pass.get_10 & 0x0400; 2806 } else { 2807 result = pass.get_11 & 0x0002; 2808 } 2809 if (rv >= 0 && result == 0) { 2810 /* pass 3 (no errors found in pass 1 or 2) */ 2811 pass.set_14 = 0xffff; 2812 pass.set_15 = 0xffff; 2813 pass.set_16 = 0xffff; 2814 pass.set_17 = 0xffff; 2815 pass.set_18 = 0x608c; 2816 pass.set_19 = 0xa000; 2817 pass.set_1a = 0x03e8; 2818 pass.set_1b = 0x0258; 2819 pass.set_1c = 0x00b4; 2820 pass.set_1d = 0x0a14; 2821 pass.set_10 = 0x000b; 2822 rv = phy_5464_cable_diag_pass(unit, phy_addr, &pass); 2823 } 2824 2825 if (rv >= 0) { 2826 if ((pc->phy_model == PHY_BCM5478_MODEL) || 2827 (pc->phy_model == PHY_BCM5488_MODEL)) { 2828 if (pass.get_10 & 0x0400) { 2829 status->state = SOC_PORT_CABLE_STATE_OPENSHORT; 2830 } 2831 if (pass.get_11 & 0x0003) { 2832 status->pair_state[0] = SOC_PORT_CABLE_STATE_OPENSHORT; 2833 } else if (pass.get_11 & 0x1000) { 2834 status->pair_state[0] = SOC_PORT_CABLE_STATE_UNKNOWN; 2835 } 2836 if (pass.get_11 & 0x000c) { 2837 status->pair_state[1] = SOC_PORT_CABLE_STATE_OPENSHORT; 2838 } else if (pass.get_11 & 0x2000) { 2839 status->pair_state[1] = SOC_PORT_CABLE_STATE_UNKNOWN; 2840 } 2841 if (pass.get_11 & 0x0030) { 2842 status->pair_state[2] = SOC_PORT_CABLE_STATE_OPENSHORT; 2843 } else if (pass.get_11 & 0x4000) { 2844 status->pair_state[2] = SOC_PORT_CABLE_STATE_UNKNOWN; 2845 } 2846 if (pass.get_11 & 0x00c0) { 2847 status->pair_state[3] = SOC_PORT_CABLE_STATE_OPENSHORT; 2848 } else if (pass.get_11 & 0x8000) { 2849 status->pair_state[3] = SOC_PORT_CABLE_STATE_UNKNOWN; 2850 } 2851 } else { 2852 if (pass.get_11 & 0x0002) { 2853 status->state = SOC_PORT_CABLE_STATE_OPENSHORT; 2854 } 2855 if (pass.get_11 & 0x0100) { 2856 status->pair_state[0] = SOC_PORT_CABLE_STATE_OPENSHORT; 2857 } else if (pass.get_11 & 0x1000) { 2858 status->pair_state[0] = SOC_PORT_CABLE_STATE_UNKNOWN; 2859 } 2860 if (pass.get_11 & 0x0200) { 2861 status->pair_state[1] = SOC_PORT_CABLE_STATE_OPENSHORT; 2862 } else if (pass.get_11 & 0x2000) { 2863 status->pair_state[1] = SOC_PORT_CABLE_STATE_UNKNOWN; 2864 } 2865 if (pass.get_11 & 0x0400) { 2866 status->pair_state[2] = SOC_PORT_CABLE_STATE_OPENSHORT; 2867 } else if (pass.get_11 & 0x4000) { 2868 status->pair_state[2] = SOC_PORT_CABLE_STATE_UNKNOWN; 2869 } 2870 if (pass.get_11 & 0x0800) { 2871 status->pair_state[3] = SOC_PORT_CABLE_STATE_OPENSHORT; 2872 } else if (pass.get_11 & 0x8000) { 2873 status->pair_state[3] = SOC_PORT_CABLE_STATE_UNKNOWN; 2874 } 2875 } 2876 status->pair_len[0] = pass.get_12 & 0xff; 2877 status->pair_len[1] = (pass.get_12 >> 8) & 0xff; 2878 status->pair_len[2] = pass.get_13 & 0xff; 2879 status->pair_len[3] = (pass.get_13 >> 8) & 0xff; 2880 } 2881 2882 /* restore various control values */ 2883 #ifdef PHY_5464_CABLE_DIAG_UNSETUP 2884 rv2 = phy_5464_cable_diag_unsetup(unit, phy_addr, &setup_state); 2885 #endif 2886 MIIM_UNLOCK(unit); 2887 rv2 = _phy_5464_reset_setup(unit, port); 2888 2889 if (rv >= 0 && rv2 < 0) { 2890 return rv2; 2891 } 2892 return rv; 2893 } 2894 2895 /* BCM53314 WAR Fix */ 2896 if (PHY_IS_BCM53314(pc)) { 2897 SOC_IF_ERROR_RETURN(phy_5464_link_get(unit, port, &bcm53314_link)); 2898 SOC_IF_ERROR_RETURN(phy_5464_autoneg_get(unit, port, &bcm53314_an, &bcm53314_an_done)); 2899 SOC_IF_ERROR_RETURN(phy_5464_speed_get(unit, port, &bcm53314_speed)); 2900 } 2901 2902 MIIM_LOCK(unit); /* this locks out linkscan, essentially */ 2903 rv = phy_5464_cable_diag_sw(unit, port, status); 2904 MIIM_UNLOCK(unit); 2905 rv2 = 0; 2906 if (rv <= 0) { /* don't reset if > 0 -- link was up */ 2907 rv2 = _phy_5464_reset_setup(unit, port); 2908 } 2909 2910 /* BCM53314 WAR Fix */ 2911 if (bcm53314_an == TRUE && bcm53314_link == TRUE) { 2912 if (bcm53314_speed == 100 || bcm53314_speed == 10) { 2913 SOC_IF_ERROR_RETURN(phy_5464_autoneg_set(unit, port, TRUE)); 2914 } 2915 } 2916 2917 if (rv >= 0 && rv2 < 0) { 2918 return rv2; 2919 } 2920 return rv; 2921 } 2922 2923 STATIC int 2924 _phy_5464_power_mode_set (int unit, soc_port_t port, int mode) 2925 { 2926 phy_ctrl_t *pc; 2927 2928 pc = EXT_PHY_SW_STATE(unit, port); 2929 2930 if (pc->power_mode == mode) { 2931 return SOC_E_NONE; 2932 } 2933 2934 if (mode == SOC_PHY_CONTROL_POWER_LOW) { 2935 if (PHY_IS_BCM53314(pc)) { 2936 /* enable dsp clock */ 2937 SOC_IF_ERROR_RETURN 2938 (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0c00,0x0c00)); 2939 2940 /* enable low power 136 */ 2941 SOC_IF_ERROR_RETURN 2942 (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc,0x80,0x80)); 2943 2944 /* reduce tx bias current to -20% */ 2945 SOC_IF_ERROR_RETURN 2946 (WRITE_PHY_REG(unit, pc, 0x17, 0x0f75)); 2947 SOC_IF_ERROR_RETURN 2948 (WRITE_PHY_REG(unit, pc, 0x15, 0x1555)); 2949 2950 /* disable dsp clock */ 2951 SOC_IF_ERROR_RETURN 2952 (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0400,0x0c00)); 2953 pc->power_mode = mode; 2954 } else { 2955 return SOC_E_UNAVAIL; 2956 } 2957 } else if (mode == SOC_PHY_CONTROL_POWER_FULL) { 2958 2959 /* back to normal mode */ 2960 /* enable dsp clock */ 2961 SOC_IF_ERROR_RETURN 2962 (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0c00,0x0c00)); 2963 2964 /* disable low power 136 */ 2965 SOC_IF_ERROR_RETURN 2966 (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc,0x00,0x80)); 2967 2968 /* reduce tx bias current to -20% */ 2969 SOC_IF_ERROR_RETURN 2970 (WRITE_PHY_REG(unit, pc, 0x17, 0x0f75)); 2971 SOC_IF_ERROR_RETURN 2972 (WRITE_PHY_REG(unit, pc, 0x15, 0x0)); 2973 2974 /* disable dsp clock */ 2975 SOC_IF_ERROR_RETURN 2976 (MODIFY_PHY5464_MII_AUX_CTRLr(unit,pc,0x0400,0x0c00)); 2977 2978 /* disable the auto power mode */ 2979 SOC_IF_ERROR_RETURN 2980 (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc, 2981 0, 2982 PHY_5464_AUTO_PWRDWN_EN)); 2983 pc->power_mode = mode; 2984 } else if (mode == SOC_PHY_CONTROL_POWER_AUTO) { 2985 SOC_IF_ERROR_RETURN 2986 (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc, 2987 PHY_5464_AUTO_PWRDWN_EN, 2988 PHY_5464_AUTO_PWRDWN_EN)); 2989 pc->power_mode = mode; 2990 } 2991 return SOC_E_NONE; 2992 } 2993 2994 /* 2995 * Function: 2996 * phy_5464_control_set 2997 * Purpose: 2998 * Configure PHY device specific control fucntion. 2999 * Parameters: 3000 * unit - StrataSwitch unit #. 3001 * port - StrataSwitch port #. 3002 * type - Control to update 3003 * value - New setting for the control 3004 * Returns: 3005 * SOC_E_NONE 3006 */ 3007 STATIC int 3008 phy_5464_control_set(int unit, soc_port_t port, 3009 soc_phy_control_t type, uint32 value) 3010 { 3011 int rv; 3012 uint16 data16; 3013 phy_ctrl_t *pc; 3014 3015 PHY_CONTROL_TYPE_CHECK(type); 3016 3017 pc = EXT_PHY_SW_STATE(unit, port); 3018 rv = SOC_E_NONE; 3019 switch(type) { 3020 case SOC_PHY_CONTROL_POWER: 3021 rv = _phy_5464_power_mode_set(unit,port,value); 3022 break; 3023 3024 case SOC_PHY_CONTROL_POWER_AUTO_WAKE_TIME: 3025 if (value <= PHY_5464_AUTO_PWRDWN_WAKEUP_MAX) { 3026 3027 /* at least one unit */ 3028 if (value < PHY_5464_AUTO_PWRDWN_WAKEUP_UNIT) { 3029 value = PHY_5464_AUTO_PWRDWN_WAKEUP_UNIT; 3030 } 3031 } else { /* anything more then max, set to the max */ 3032 value = PHY_5464_AUTO_PWRDWN_WAKEUP_MAX; 3033 } 3034 SOC_IF_ERROR_RETURN 3035 (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc, 3036 value/PHY_5464_AUTO_PWRDWN_WAKEUP_UNIT, 3037 PHY_5464_AUTO_PWRDWN_WAKEUP_MASK)); 3038 break; 3039 3040 case SOC_PHY_CONTROL_POWER_AUTO_SLEEP_TIME: 3041 /* sleep time configuration is either 2.7s or 5.4 s, default is 2.7s */ 3042 if (value < PHY_5464_AUTO_PWRDWN_SLEEP_MAX) { 3043 data16 = 0; /* anything less than 5.4s, default to 2.7s */ 3044 } else { 3045 data16 = PHY_5464_AUTO_PWRDWN_SLEEP_MASK; 3046 } 3047 SOC_IF_ERROR_RETURN 3048 (MODIFY_PHY5464_AUTO_POWER_DOWNr(unit,pc, 3049 data16, 3050 PHY_5464_AUTO_PWRDWN_SLEEP_MASK)); 3051 break; 3052 3053 case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT: 3054 /* Not supported for 10/100/1000BASE-T interfaces */ 3055 rv = SOC_E_UNAVAIL; 3056 break; 3057 3058 default: 3059 rv = SOC_E_UNAVAIL; 3060 break; 3061 } 3062 return rv; 3063 } 3064 3065 /* 3066 * Function: 3067 * phy_5464_control_get 3068 * Purpose: 3069 * Get current control settign of the PHY. 3070 * Parameters: 3071 * unit - StrataSwitch unit #. 3072 * port - StrataSwitch port #. 3073 * type - Control to update 3074 * value - (OUT)Current setting for the control 3075 * Returns: 3076 * SOC_E_NONE 3077 */ 3078 STATIC int 3079 phy_5464_control_get(int unit, soc_port_t port, 3080 soc_phy_control_t type, uint32 *value) 3081 { 3082 int rv; 3083 phy_ctrl_t *pc; 3084 uint16 data16; 3085 3086 PHY_CONTROL_TYPE_CHECK(type); 3087 3088 pc = EXT_PHY_SW_STATE(unit, port); 3089 rv = SOC_E_NONE; 3090 switch(type) { 3091 case SOC_PHY_CONTROL_POWER: 3092 *value = pc->power_mode; 3093 break; 3094 case SOC_PHY_CONTROL_POWER_AUTO_SLEEP_TIME: 3095 SOC_IF_ERROR_RETURN 3096 (READ_PHY5464_AUTO_POWER_DOWNr(unit,pc, &data16)); 3097 3098 if (data16 & PHY_5464_AUTO_PWRDWN_SLEEP_MASK) { 3099 *value = PHY_5464_AUTO_PWRDWN_SLEEP_MAX; 3100 } else { 3101 *value = 2700; 3102 } 3103 break; 3104 3105 case SOC_PHY_CONTROL_POWER_AUTO_WAKE_TIME: 3106 SOC_IF_ERROR_RETURN 3107 (READ_PHY5464_AUTO_POWER_DOWNr(unit,pc, &data16)); 3108 3109 data16 &= PHY_5464_AUTO_PWRDWN_WAKEUP_MASK; 3110 *value = data16 * PHY_5464_AUTO_PWRDWN_WAKEUP_UNIT; 3111 break; 3112 3113 case SOC_PHY_CONTROL_LINKDOWN_TRANSMIT: 3114 /* Not supported for 10/100/1000BASE-T interfaces */ 3115 rv = SOC_E_UNAVAIL; 3116 break; 3117 3118 default: 3119 rv = SOC_E_UNAVAIL; 3120 break; 3121 } 3122 return rv; 3123 } 3124 3125 #if LINK_DOWN_RESET_WAR 3126 3127 /* 3128 * Function: 3129 * phy_5464_link_down 3130 * Purpose: 3131 * Perform reset work-around on link down (A0 silicon only) 3132 * Parameters: 3133 * unit - StrataSwitch unit #. 3134 * port - StrataSwitch port #. 3135 * Returns: 3136 * SOC_E_XXX 3137 */ 3138 3139 STATIC int 3140 phy_5464_link_down(int unit, soc_port_t port) 3141 { 3142 phy_ctrl_t *pc = EXT_PHY_SW_STATE(unit, port); 3143 3144 /* Only applies to copper link down on rev A0 */ 3145 3146 if (PHY_IS_BCM5464(pc) && pc->phy_rev == PHY_REV_A0 && 3147 PHY_COPPER_MODE(unit, port)) { 3148 3149 DPRINTF((BSL_META_U(unit, 3150 "5464_link_down: port=%d activated\n"), 3151 port)); 3152 3153 /* Reset the chip, then restore user settings. */ 3154 3155 SOC_IF_ERROR_RETURN 3156 (_phy_5464_reset_setup(unit, port)); 3157 3158 SOC_IF_ERROR_RETURN 3159 (_phy_5464_active_medium_config_update(unit, port, 3160 PHY_COPPER_MODE(unit, port) ? 3161 &pc->copper : 3162 &pc->fiber)); 3163 } 3164 3165 return SOC_E_NONE; 3166 } 3167 #else 3168 #define phy_5464_link_down NULL 3169 #endif /* LINK_DOWN_RESET_WAR */ 3170 3171 #ifdef BROADCOM_DEBUG 3172 3173 /* 3174 * Function: 3175 * phy_5464_shadow_dump 3176 * Purpose: 3177 * Debug routine to dump all shadow registers. 3178 * Parameters: 3179 * unit - StrataSwitch unit #. 3180 * port - StrataSwitch port #. 3181 */ 3182 3183 void 3184 phy_5464_shadow_dump(int unit, soc_port_t port) 3185 { 3186 uint16 tmp; 3187 int i; 3188 phy_ctrl_t *pc; 3189 3190 pc = EXT_PHY_SW_STATE(unit, port); 3191 3192 /* Register 0x18 Shadows */ 3193 for (i = 0; i <= 7; i++) { 3194 WRITE_PHY_REG(unit, pc, 0x18, (i << 12) | 0x7); 3195 READ_PHY_REG(unit, pc, 0x18, &tmp); 3196 if ((tmp & ~7) == 0x0000) { 3197 continue; 3198 } 3199 LOG_CLI((BSL_META_U(unit, 3200 "0x18[0x%x]=0x%04x\n"), i, tmp)); 3201 } 3202 3203 /* Register 0x1c Shadows */ 3204 for (i = 0; i <= 0x1f; i++) { 3205 WRITE_PHY_REG(unit, pc, 0x1c, i << 10); 3206 READ_PHY_REG(unit, pc, 0x1c, &tmp); 3207 if ((tmp & ~0x7c00) == 0x0000) { 3208 continue; 3209 } 3210 LOG_CLI((BSL_META_U(unit, 3211 "0x1c[0x%x]=0x%04x\n"), i, tmp)); 3212 } 3213 3214 /* Register 0x17/0x15 Shadows */ 3215 for (i = 0; i <= 0x13; i++) { 3216 WRITE_PHY_REG(unit, pc, 0x17, 0xf00 | i); 3217 READ_PHY_REG(unit, pc, 0x15, &tmp); 3218 if (tmp == 0x0000) { 3219 continue; 3220 } 3221 LOG_CLI((BSL_META_U(unit, 3222 "0x17[0x%x]=0x%04x\n"), i, tmp)); 3223 } 3224 } 3225 3226 #endif /* BROADCOM_DEBUG */ 3227 3228 /* 3229 * Variable: phy_5464drv_ge 3230 * Purpose: PHY driver for 5464 3231 */ 3232 3233 phy_driver_t phy_5464drv_ge = { 3234 "546X/548X Gigabit PHY Driver", 3235 phy_5464_init, 3236 phy_fe_ge_reset, 3237 phy_5464_link_get, 3238 phy_5464_enable_set, 3239 phy_5464_enable_get, 3240 phy_5464_duplex_set, 3241 phy_5464_duplex_get, 3242 phy_5464_speed_set, 3243 phy_5464_speed_get, 3244 phy_5464_master_set, 3245 phy_5464_master_get, 3246 phy_5464_autoneg_set, 3247 phy_5464_autoneg_get, 3248 phy_5464_adv_local_set, 3249 phy_5464_adv_local_get, 3250 phy_5464_adv_remote_get, 3251 phy_5464_lb_set, 3252 phy_5464_lb_get, 3253 phy_5464_interface_set, 3254 phy_5464_interface_get, 3255 phy_5464_ability_get, 3256 NULL, /* Link up event */ 3257 phy_5464_link_down, 3258 phy_5464_mdix_set, 3259 phy_5464_mdix_get, 3260 phy_5464_mdix_status_get, 3261 phy_5464_medium_config_set, 3262 phy_5464_medium_config_get, 3263 phy_5464_medium_status, 3264 phy_5464_cable_diag, 3265 NULL, /* phy_link_change */ 3266 phy_5464_control_set, /* phy_control_set */ 3267 phy_5464_control_get, /* phy_control_get */ 3268 phy_ge_reg_read, 3269 phy_ge_reg_write, 3270 phy_ge_reg_modify, 3271 NULL /* Phy notify event */ 3272 }; 3273 3274 #else /* INCLUDE_PHY_5464_ESW */ 3275 int _phy_5464_not_empty; 3276 #endif /* INCLUDE_PHY_5464_ESW */ 3277