bcm-phy-lib.c (20005B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2015-2017 Broadcom 4 */ 5 6#include "bcm-phy-lib.h" 7#include <linux/bitfield.h> 8#include <linux/brcmphy.h> 9#include <linux/export.h> 10#include <linux/mdio.h> 11#include <linux/module.h> 12#include <linux/phy.h> 13#include <linux/ethtool.h> 14#include <linux/ethtool_netlink.h> 15 16#define MII_BCM_CHANNEL_WIDTH 0x2000 17#define BCM_CL45VEN_EEE_ADV 0x3c 18 19int __bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) 20{ 21 int rc; 22 23 rc = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 24 if (rc < 0) 25 return rc; 26 27 return __phy_write(phydev, MII_BCM54XX_EXP_DATA, val); 28} 29EXPORT_SYMBOL_GPL(__bcm_phy_write_exp); 30 31int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) 32{ 33 int rc; 34 35 phy_lock_mdio_bus(phydev); 36 rc = __bcm_phy_write_exp(phydev, reg, val); 37 phy_unlock_mdio_bus(phydev); 38 39 return rc; 40} 41EXPORT_SYMBOL_GPL(bcm_phy_write_exp); 42 43int __bcm_phy_read_exp(struct phy_device *phydev, u16 reg) 44{ 45 int val; 46 47 val = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 48 if (val < 0) 49 return val; 50 51 val = __phy_read(phydev, MII_BCM54XX_EXP_DATA); 52 53 /* Restore default value. It's O.K. if this write fails. */ 54 __phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); 55 56 return val; 57} 58EXPORT_SYMBOL_GPL(__bcm_phy_read_exp); 59 60int bcm_phy_read_exp(struct phy_device *phydev, u16 reg) 61{ 62 int rc; 63 64 phy_lock_mdio_bus(phydev); 65 rc = __bcm_phy_read_exp(phydev, reg); 66 phy_unlock_mdio_bus(phydev); 67 68 return rc; 69} 70EXPORT_SYMBOL_GPL(bcm_phy_read_exp); 71 72int __bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set) 73{ 74 int new, ret; 75 76 ret = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); 77 if (ret < 0) 78 return ret; 79 80 ret = __phy_read(phydev, MII_BCM54XX_EXP_DATA); 81 if (ret < 0) 82 return ret; 83 84 new = (ret & ~mask) | set; 85 if (new == ret) 86 return 0; 87 88 return __phy_write(phydev, MII_BCM54XX_EXP_DATA, new); 89} 90EXPORT_SYMBOL_GPL(__bcm_phy_modify_exp); 91 92int bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set) 93{ 94 int ret; 95 96 phy_lock_mdio_bus(phydev); 97 ret = __bcm_phy_modify_exp(phydev, reg, mask, set); 98 phy_unlock_mdio_bus(phydev); 99 100 return ret; 101} 102EXPORT_SYMBOL_GPL(bcm_phy_modify_exp); 103 104int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum) 105{ 106 /* The register must be written to both the Shadow Register Select and 107 * the Shadow Read Register Selector 108 */ 109 phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MASK | 110 regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT); 111 return phy_read(phydev, MII_BCM54XX_AUX_CTL); 112} 113EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read); 114 115int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) 116{ 117 return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); 118} 119EXPORT_SYMBOL(bcm54xx_auxctl_write); 120 121int bcm_phy_write_misc(struct phy_device *phydev, 122 u16 reg, u16 chl, u16 val) 123{ 124 int rc; 125 int tmp; 126 127 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 128 MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 129 if (rc < 0) 130 return rc; 131 132 tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 133 tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 134 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 135 if (rc < 0) 136 return rc; 137 138 tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; 139 rc = bcm_phy_write_exp(phydev, tmp, val); 140 141 return rc; 142} 143EXPORT_SYMBOL_GPL(bcm_phy_write_misc); 144 145int bcm_phy_read_misc(struct phy_device *phydev, 146 u16 reg, u16 chl) 147{ 148 int rc; 149 int tmp; 150 151 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 152 MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 153 if (rc < 0) 154 return rc; 155 156 tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL); 157 tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA; 158 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp); 159 if (rc < 0) 160 return rc; 161 162 tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg; 163 rc = bcm_phy_read_exp(phydev, tmp); 164 165 return rc; 166} 167EXPORT_SYMBOL_GPL(bcm_phy_read_misc); 168 169int bcm_phy_ack_intr(struct phy_device *phydev) 170{ 171 int reg; 172 173 /* Clear pending interrupts. */ 174 reg = phy_read(phydev, MII_BCM54XX_ISR); 175 if (reg < 0) 176 return reg; 177 178 return 0; 179} 180EXPORT_SYMBOL_GPL(bcm_phy_ack_intr); 181 182int bcm_phy_config_intr(struct phy_device *phydev) 183{ 184 int reg, err; 185 186 reg = phy_read(phydev, MII_BCM54XX_ECR); 187 if (reg < 0) 188 return reg; 189 190 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 191 err = bcm_phy_ack_intr(phydev); 192 if (err) 193 return err; 194 195 reg &= ~MII_BCM54XX_ECR_IM; 196 err = phy_write(phydev, MII_BCM54XX_ECR, reg); 197 } else { 198 reg |= MII_BCM54XX_ECR_IM; 199 err = phy_write(phydev, MII_BCM54XX_ECR, reg); 200 if (err) 201 return err; 202 203 err = bcm_phy_ack_intr(phydev); 204 } 205 return err; 206} 207EXPORT_SYMBOL_GPL(bcm_phy_config_intr); 208 209irqreturn_t bcm_phy_handle_interrupt(struct phy_device *phydev) 210{ 211 int irq_status, irq_mask; 212 213 irq_status = phy_read(phydev, MII_BCM54XX_ISR); 214 if (irq_status < 0) { 215 phy_error(phydev); 216 return IRQ_NONE; 217 } 218 219 /* If a bit from the Interrupt Mask register is set, the corresponding 220 * bit from the Interrupt Status register is masked. So read the IMR 221 * and then flip the bits to get the list of possible interrupt 222 * sources. 223 */ 224 irq_mask = phy_read(phydev, MII_BCM54XX_IMR); 225 if (irq_mask < 0) { 226 phy_error(phydev); 227 return IRQ_NONE; 228 } 229 irq_mask = ~irq_mask; 230 231 if (!(irq_status & irq_mask)) 232 return IRQ_NONE; 233 234 phy_trigger_machine(phydev); 235 236 return IRQ_HANDLED; 237} 238EXPORT_SYMBOL_GPL(bcm_phy_handle_interrupt); 239 240int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow) 241{ 242 phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); 243 return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); 244} 245EXPORT_SYMBOL_GPL(bcm_phy_read_shadow); 246 247int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, 248 u16 val) 249{ 250 return phy_write(phydev, MII_BCM54XX_SHD, 251 MII_BCM54XX_SHD_WRITE | 252 MII_BCM54XX_SHD_VAL(shadow) | 253 MII_BCM54XX_SHD_DATA(val)); 254} 255EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); 256 257int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 258{ 259 int val; 260 261 val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 262 if (val < 0) 263 return val; 264 265 return __phy_read(phydev, MII_BCM54XX_RDB_DATA); 266} 267EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb); 268 269int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 270{ 271 int ret; 272 273 phy_lock_mdio_bus(phydev); 274 ret = __bcm_phy_read_rdb(phydev, rdb); 275 phy_unlock_mdio_bus(phydev); 276 277 return ret; 278} 279EXPORT_SYMBOL_GPL(bcm_phy_read_rdb); 280 281int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 282{ 283 int ret; 284 285 ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 286 if (ret < 0) 287 return ret; 288 289 return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val); 290} 291EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb); 292 293int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 294{ 295 int ret; 296 297 phy_lock_mdio_bus(phydev); 298 ret = __bcm_phy_write_rdb(phydev, rdb, val); 299 phy_unlock_mdio_bus(phydev); 300 301 return ret; 302} 303EXPORT_SYMBOL_GPL(bcm_phy_write_rdb); 304 305int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 306{ 307 int new, ret; 308 309 ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 310 if (ret < 0) 311 return ret; 312 313 ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA); 314 if (ret < 0) 315 return ret; 316 317 new = (ret & ~mask) | set; 318 if (new == ret) 319 return 0; 320 321 return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new); 322} 323EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb); 324 325int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 326{ 327 int ret; 328 329 phy_lock_mdio_bus(phydev); 330 ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set); 331 phy_unlock_mdio_bus(phydev); 332 333 return ret; 334} 335EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb); 336 337int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) 338{ 339 int val; 340 341 if (dll_pwr_down) { 342 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); 343 if (val < 0) 344 return val; 345 346 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; 347 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); 348 } 349 350 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); 351 if (val < 0) 352 return val; 353 354 /* Clear APD bits */ 355 val &= BCM_APD_CLR_MASK; 356 357 if (phydev->autoneg == AUTONEG_ENABLE) 358 val |= BCM54XX_SHD_APD_EN; 359 else 360 val |= BCM_NO_ANEG_APD_EN; 361 362 /* Enable energy detect single link pulse for easy wakeup */ 363 val |= BCM_APD_SINGLELP_EN; 364 365 /* Enable Auto Power-Down (APD) for the PHY */ 366 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); 367} 368EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); 369 370int bcm_phy_set_eee(struct phy_device *phydev, bool enable) 371{ 372 int val, mask = 0; 373 374 /* Enable EEE at PHY level */ 375 val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL); 376 if (val < 0) 377 return val; 378 379 if (enable) 380 val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; 381 else 382 val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X); 383 384 phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val); 385 386 /* Advertise EEE */ 387 val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV); 388 if (val < 0) 389 return val; 390 391 if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 392 phydev->supported)) 393 mask |= MDIO_EEE_1000T; 394 if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, 395 phydev->supported)) 396 mask |= MDIO_EEE_100TX; 397 398 if (enable) 399 val |= mask; 400 else 401 val &= ~mask; 402 403 phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val); 404 405 return 0; 406} 407EXPORT_SYMBOL_GPL(bcm_phy_set_eee); 408 409int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count) 410{ 411 int val; 412 413 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 414 if (val < 0) 415 return val; 416 417 /* Check if wirespeed is enabled or not */ 418 if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) { 419 *count = DOWNSHIFT_DEV_DISABLE; 420 return 0; 421 } 422 423 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 424 if (val < 0) 425 return val; 426 427 /* Downgrade after one link attempt */ 428 if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) { 429 *count = 1; 430 } else { 431 /* Downgrade after configured retry count */ 432 val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 433 val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK; 434 *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET; 435 } 436 437 return 0; 438} 439EXPORT_SYMBOL_GPL(bcm_phy_downshift_get); 440 441int bcm_phy_downshift_set(struct phy_device *phydev, u8 count) 442{ 443 int val = 0, ret = 0; 444 445 /* Range check the number given */ 446 if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET > 447 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK && 448 count != DOWNSHIFT_DEV_DEFAULT_COUNT) { 449 return -ERANGE; 450 } 451 452 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 453 if (val < 0) 454 return val; 455 456 /* Se the write enable bit */ 457 val |= MII_BCM54XX_AUXCTL_MISC_WREN; 458 459 if (count == DOWNSHIFT_DEV_DISABLE) { 460 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 461 return bcm54xx_auxctl_write(phydev, 462 MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 463 val); 464 } else { 465 val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 466 ret = bcm54xx_auxctl_write(phydev, 467 MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 468 val); 469 if (ret < 0) 470 return ret; 471 } 472 473 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 474 val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK << 475 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT | 476 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS); 477 478 switch (count) { 479 case 1: 480 val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS; 481 break; 482 case DOWNSHIFT_DEV_DEFAULT_COUNT: 483 val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 484 break; 485 default: 486 val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) << 487 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 488 break; 489 } 490 491 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val); 492} 493EXPORT_SYMBOL_GPL(bcm_phy_downshift_set); 494 495struct bcm_phy_hw_stat { 496 const char *string; 497 u8 reg; 498 u8 shift; 499 u8 bits; 500}; 501 502/* Counters freeze at either 0xffff or 0xff, better than nothing */ 503static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = { 504 { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 }, 505 { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 }, 506 { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 }, 507 { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 }, 508 { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 }, 509}; 510 511int bcm_phy_get_sset_count(struct phy_device *phydev) 512{ 513 return ARRAY_SIZE(bcm_phy_hw_stats); 514} 515EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count); 516 517void bcm_phy_get_strings(struct phy_device *phydev, u8 *data) 518{ 519 unsigned int i; 520 521 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 522 strlcpy(data + i * ETH_GSTRING_LEN, 523 bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN); 524} 525EXPORT_SYMBOL_GPL(bcm_phy_get_strings); 526 527/* Caller is supposed to provide appropriate storage for the library code to 528 * access the shadow copy 529 */ 530static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow, 531 unsigned int i) 532{ 533 struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i]; 534 int val; 535 u64 ret; 536 537 val = phy_read(phydev, stat.reg); 538 if (val < 0) { 539 ret = U64_MAX; 540 } else { 541 val >>= stat.shift; 542 val = val & ((1 << stat.bits) - 1); 543 shadow[i] += val; 544 ret = shadow[i]; 545 } 546 547 return ret; 548} 549 550void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, 551 struct ethtool_stats *stats, u64 *data) 552{ 553 unsigned int i; 554 555 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 556 data[i] = bcm_phy_get_stat(phydev, shadow, i); 557} 558EXPORT_SYMBOL_GPL(bcm_phy_get_stats); 559 560void bcm_phy_r_rc_cal_reset(struct phy_device *phydev) 561{ 562 /* Reset R_CAL/RC_CAL Engine */ 563 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010); 564 565 /* Disable Reset R_AL/RC_CAL Engine */ 566 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000); 567} 568EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset); 569 570int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev) 571{ 572 /* Increase VCO range to prevent unlocking problem of PLL at low 573 * temp 574 */ 575 bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 576 577 /* Change Ki to 011 */ 578 bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 579 580 /* Disable loading of TVCO buffer to bandgap, set bandgap trim 581 * to 111 582 */ 583 bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 584 585 /* Adjust bias current trim by -3 */ 586 bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); 587 588 /* Switch to CORE_BASE1E */ 589 phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); 590 591 bcm_phy_r_rc_cal_reset(phydev); 592 593 /* write AFE_RXCONFIG_0 */ 594 bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 595 596 /* write AFE_RXCONFIG_1 */ 597 bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 598 599 /* write AFE_RX_LP_COUNTER */ 600 bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 601 602 /* write AFE_HPF_TRIM_OTHERS */ 603 bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 604 605 /* write AFTE_TX_CONFIG */ 606 bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 607 608 return 0; 609} 610EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init); 611 612int bcm_phy_enable_jumbo(struct phy_device *phydev) 613{ 614 int ret; 615 616 ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL); 617 if (ret < 0) 618 return ret; 619 620 /* Enable extended length packet reception */ 621 ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, 622 ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN); 623 if (ret < 0) 624 return ret; 625 626 /* Enable the elastic FIFO for raising the transmission limit from 627 * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation 628 * latency. 629 */ 630 return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE); 631} 632EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo); 633 634static int __bcm_phy_enable_rdb_access(struct phy_device *phydev) 635{ 636 return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0); 637} 638 639static int __bcm_phy_enable_legacy_access(struct phy_device *phydev) 640{ 641 return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087, 642 BCM54XX_ACCESS_MODE_LEGACY_EN); 643} 644 645static int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb) 646{ 647 u16 mask, set; 648 int ret; 649 650 /* Auto-negotiation must be enabled for cable diagnostics to work, but 651 * don't advertise any capabilities. 652 */ 653 phy_write(phydev, MII_BMCR, BMCR_ANENABLE); 654 phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); 655 phy_write(phydev, MII_CTRL1000, 0); 656 657 phy_lock_mdio_bus(phydev); 658 if (is_rdb) { 659 ret = __bcm_phy_enable_legacy_access(phydev); 660 if (ret) 661 goto out; 662 } 663 664 mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK; 665 set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK | 666 FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK, 667 BCM54XX_ECD_CTRL_UNIT_CM); 668 669 ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set); 670 671out: 672 /* re-enable the RDB access even if there was an error */ 673 if (is_rdb) 674 ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 675 676 phy_unlock_mdio_bus(phydev); 677 678 return ret; 679} 680 681static int bcm_phy_cable_test_report_trans(int result) 682{ 683 switch (result) { 684 case BCM54XX_ECD_FAULT_TYPE_OK: 685 return ETHTOOL_A_CABLE_RESULT_CODE_OK; 686 case BCM54XX_ECD_FAULT_TYPE_OPEN: 687 return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 688 case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 689 return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 690 case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 691 return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; 692 case BCM54XX_ECD_FAULT_TYPE_INVALID: 693 case BCM54XX_ECD_FAULT_TYPE_BUSY: 694 default: 695 return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 696 } 697} 698 699static bool bcm_phy_distance_valid(int result) 700{ 701 switch (result) { 702 case BCM54XX_ECD_FAULT_TYPE_OPEN: 703 case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 704 case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 705 return true; 706 } 707 return false; 708} 709 710static int bcm_phy_report_length(struct phy_device *phydev, int pair) 711{ 712 int val; 713 714 val = __bcm_phy_read_exp(phydev, 715 BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair); 716 if (val < 0) 717 return val; 718 719 if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID) 720 return 0; 721 722 ethnl_cable_test_fault_length(phydev, pair, val); 723 724 return 0; 725} 726 727static int _bcm_phy_cable_test_get_status(struct phy_device *phydev, 728 bool *finished, bool is_rdb) 729{ 730 int pair_a, pair_b, pair_c, pair_d, ret; 731 732 *finished = false; 733 734 phy_lock_mdio_bus(phydev); 735 736 if (is_rdb) { 737 ret = __bcm_phy_enable_legacy_access(phydev); 738 if (ret) 739 goto out; 740 } 741 742 ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL); 743 if (ret < 0) 744 goto out; 745 746 if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) { 747 ret = 0; 748 goto out; 749 } 750 751 ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE); 752 if (ret < 0) 753 goto out; 754 755 pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret); 756 pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret); 757 pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret); 758 pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret); 759 760 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, 761 bcm_phy_cable_test_report_trans(pair_a)); 762 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, 763 bcm_phy_cable_test_report_trans(pair_b)); 764 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C, 765 bcm_phy_cable_test_report_trans(pair_c)); 766 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, 767 bcm_phy_cable_test_report_trans(pair_d)); 768 769 if (bcm_phy_distance_valid(pair_a)) 770 bcm_phy_report_length(phydev, 0); 771 if (bcm_phy_distance_valid(pair_b)) 772 bcm_phy_report_length(phydev, 1); 773 if (bcm_phy_distance_valid(pair_c)) 774 bcm_phy_report_length(phydev, 2); 775 if (bcm_phy_distance_valid(pair_d)) 776 bcm_phy_report_length(phydev, 3); 777 778 ret = 0; 779 *finished = true; 780out: 781 /* re-enable the RDB access even if there was an error */ 782 if (is_rdb) 783 ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 784 785 phy_unlock_mdio_bus(phydev); 786 787 return ret; 788} 789 790int bcm_phy_cable_test_start(struct phy_device *phydev) 791{ 792 return _bcm_phy_cable_test_start(phydev, false); 793} 794EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start); 795 796int bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished) 797{ 798 return _bcm_phy_cable_test_get_status(phydev, finished, false); 799} 800EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status); 801 802/* We assume that all PHYs which support RDB access can be switched to legacy 803 * mode. If, in the future, this is not true anymore, we have to re-implement 804 * this with RDB access. 805 */ 806int bcm_phy_cable_test_start_rdb(struct phy_device *phydev) 807{ 808 return _bcm_phy_cable_test_start(phydev, true); 809} 810EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb); 811 812int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev, 813 bool *finished) 814{ 815 return _bcm_phy_cable_test_get_status(phydev, finished, true); 816} 817EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb); 818 819MODULE_DESCRIPTION("Broadcom PHY Library"); 820MODULE_LICENSE("GPL v2"); 821MODULE_AUTHOR("Broadcom Corporation");