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; 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 reg &= ~MII_BCM54XX_ECR_IM; 192 else 193 reg |= MII_BCM54XX_ECR_IM; 194 195 return phy_write(phydev, MII_BCM54XX_ECR, reg); 196} 197EXPORT_SYMBOL_GPL(bcm_phy_config_intr); 198 199int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow) 200{ 201 phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow)); 202 return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD)); 203} 204EXPORT_SYMBOL_GPL(bcm_phy_read_shadow); 205 206int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow, 207 u16 val) 208{ 209 return phy_write(phydev, MII_BCM54XX_SHD, 210 MII_BCM54XX_SHD_WRITE | 211 MII_BCM54XX_SHD_VAL(shadow) | 212 MII_BCM54XX_SHD_DATA(val)); 213} 214EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); 215 216int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 217{ 218 int val; 219 220 val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 221 if (val < 0) 222 return val; 223 224 return __phy_read(phydev, MII_BCM54XX_RDB_DATA); 225} 226EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb); 227 228int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb) 229{ 230 int ret; 231 232 phy_lock_mdio_bus(phydev); 233 ret = __bcm_phy_read_rdb(phydev, rdb); 234 phy_unlock_mdio_bus(phydev); 235 236 return ret; 237} 238EXPORT_SYMBOL_GPL(bcm_phy_read_rdb); 239 240int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 241{ 242 int ret; 243 244 ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 245 if (ret < 0) 246 return ret; 247 248 return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val); 249} 250EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb); 251 252int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val) 253{ 254 int ret; 255 256 phy_lock_mdio_bus(phydev); 257 ret = __bcm_phy_write_rdb(phydev, rdb, val); 258 phy_unlock_mdio_bus(phydev); 259 260 return ret; 261} 262EXPORT_SYMBOL_GPL(bcm_phy_write_rdb); 263 264int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 265{ 266 int new, ret; 267 268 ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb); 269 if (ret < 0) 270 return ret; 271 272 ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA); 273 if (ret < 0) 274 return ret; 275 276 new = (ret & ~mask) | set; 277 if (new == ret) 278 return 0; 279 280 return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new); 281} 282EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb); 283 284int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set) 285{ 286 int ret; 287 288 phy_lock_mdio_bus(phydev); 289 ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set); 290 phy_unlock_mdio_bus(phydev); 291 292 return ret; 293} 294EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb); 295 296int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) 297{ 298 int val; 299 300 if (dll_pwr_down) { 301 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3); 302 if (val < 0) 303 return val; 304 305 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS; 306 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val); 307 } 308 309 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD); 310 if (val < 0) 311 return val; 312 313 /* Clear APD bits */ 314 val &= BCM_APD_CLR_MASK; 315 316 if (phydev->autoneg == AUTONEG_ENABLE) 317 val |= BCM54XX_SHD_APD_EN; 318 else 319 val |= BCM_NO_ANEG_APD_EN; 320 321 /* Enable energy detect single link pulse for easy wakeup */ 322 val |= BCM_APD_SINGLELP_EN; 323 324 /* Enable Auto Power-Down (APD) for the PHY */ 325 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val); 326} 327EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); 328 329int bcm_phy_set_eee(struct phy_device *phydev, bool enable) 330{ 331 int val, mask = 0; 332 333 /* Enable EEE at PHY level */ 334 val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL); 335 if (val < 0) 336 return val; 337 338 if (enable) 339 val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; 340 else 341 val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X); 342 343 phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val); 344 345 /* Advertise EEE */ 346 val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV); 347 if (val < 0) 348 return val; 349 350 if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 351 phydev->supported)) 352 mask |= MDIO_EEE_1000T; 353 if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, 354 phydev->supported)) 355 mask |= MDIO_EEE_100TX; 356 357 if (enable) 358 val |= mask; 359 else 360 val &= ~mask; 361 362 phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val); 363 364 return 0; 365} 366EXPORT_SYMBOL_GPL(bcm_phy_set_eee); 367 368int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count) 369{ 370 int val; 371 372 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 373 if (val < 0) 374 return val; 375 376 /* Check if wirespeed is enabled or not */ 377 if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) { 378 *count = DOWNSHIFT_DEV_DISABLE; 379 return 0; 380 } 381 382 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 383 if (val < 0) 384 return val; 385 386 /* Downgrade after one link attempt */ 387 if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) { 388 *count = 1; 389 } else { 390 /* Downgrade after configured retry count */ 391 val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 392 val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK; 393 *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET; 394 } 395 396 return 0; 397} 398EXPORT_SYMBOL_GPL(bcm_phy_downshift_get); 399 400int bcm_phy_downshift_set(struct phy_device *phydev, u8 count) 401{ 402 int val = 0, ret = 0; 403 404 /* Range check the number given */ 405 if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET > 406 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK && 407 count != DOWNSHIFT_DEV_DEFAULT_COUNT) { 408 return -ERANGE; 409 } 410 411 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 412 if (val < 0) 413 return val; 414 415 /* Se the write enable bit */ 416 val |= MII_BCM54XX_AUXCTL_MISC_WREN; 417 418 if (count == DOWNSHIFT_DEV_DISABLE) { 419 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 420 return bcm54xx_auxctl_write(phydev, 421 MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 422 val); 423 } else { 424 val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 425 ret = bcm54xx_auxctl_write(phydev, 426 MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 427 val); 428 if (ret < 0) 429 return ret; 430 } 431 432 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 433 val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK << 434 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT | 435 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS); 436 437 switch (count) { 438 case 1: 439 val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS; 440 break; 441 case DOWNSHIFT_DEV_DEFAULT_COUNT: 442 val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 443 break; 444 default: 445 val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) << 446 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 447 break; 448 } 449 450 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val); 451} 452EXPORT_SYMBOL_GPL(bcm_phy_downshift_set); 453 454struct bcm_phy_hw_stat { 455 const char *string; 456 u8 reg; 457 u8 shift; 458 u8 bits; 459}; 460 461/* Counters freeze at either 0xffff or 0xff, better than nothing */ 462static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = { 463 { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 }, 464 { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 }, 465 { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 }, 466 { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 }, 467 { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 }, 468}; 469 470int bcm_phy_get_sset_count(struct phy_device *phydev) 471{ 472 return ARRAY_SIZE(bcm_phy_hw_stats); 473} 474EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count); 475 476void bcm_phy_get_strings(struct phy_device *phydev, u8 *data) 477{ 478 unsigned int i; 479 480 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 481 strlcpy(data + i * ETH_GSTRING_LEN, 482 bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN); 483} 484EXPORT_SYMBOL_GPL(bcm_phy_get_strings); 485 486/* Caller is supposed to provide appropriate storage for the library code to 487 * access the shadow copy 488 */ 489static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow, 490 unsigned int i) 491{ 492 struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i]; 493 int val; 494 u64 ret; 495 496 val = phy_read(phydev, stat.reg); 497 if (val < 0) { 498 ret = U64_MAX; 499 } else { 500 val >>= stat.shift; 501 val = val & ((1 << stat.bits) - 1); 502 shadow[i] += val; 503 ret = shadow[i]; 504 } 505 506 return ret; 507} 508 509void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, 510 struct ethtool_stats *stats, u64 *data) 511{ 512 unsigned int i; 513 514 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 515 data[i] = bcm_phy_get_stat(phydev, shadow, i); 516} 517EXPORT_SYMBOL_GPL(bcm_phy_get_stats); 518 519void bcm_phy_r_rc_cal_reset(struct phy_device *phydev) 520{ 521 /* Reset R_CAL/RC_CAL Engine */ 522 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010); 523 524 /* Disable Reset R_AL/RC_CAL Engine */ 525 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000); 526} 527EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset); 528 529int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev) 530{ 531 /* Increase VCO range to prevent unlocking problem of PLL at low 532 * temp 533 */ 534 bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 535 536 /* Change Ki to 011 */ 537 bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 538 539 /* Disable loading of TVCO buffer to bandgap, set bandgap trim 540 * to 111 541 */ 542 bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 543 544 /* Adjust bias current trim by -3 */ 545 bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); 546 547 /* Switch to CORE_BASE1E */ 548 phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); 549 550 bcm_phy_r_rc_cal_reset(phydev); 551 552 /* write AFE_RXCONFIG_0 */ 553 bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 554 555 /* write AFE_RXCONFIG_1 */ 556 bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 557 558 /* write AFE_RX_LP_COUNTER */ 559 bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 560 561 /* write AFE_HPF_TRIM_OTHERS */ 562 bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 563 564 /* write AFTE_TX_CONFIG */ 565 bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 566 567 return 0; 568} 569EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init); 570 571int bcm_phy_enable_jumbo(struct phy_device *phydev) 572{ 573 int ret; 574 575 ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL); 576 if (ret < 0) 577 return ret; 578 579 /* Enable extended length packet reception */ 580 ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, 581 ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN); 582 if (ret < 0) 583 return ret; 584 585 /* Enable the elastic FIFO for raising the transmission limit from 586 * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation 587 * latency. 588 */ 589 return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE); 590} 591EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo); 592 593static int __bcm_phy_enable_rdb_access(struct phy_device *phydev) 594{ 595 return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0); 596} 597 598static int __bcm_phy_enable_legacy_access(struct phy_device *phydev) 599{ 600 return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087, 601 BCM54XX_ACCESS_MODE_LEGACY_EN); 602} 603 604static int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb) 605{ 606 u16 mask, set; 607 int ret; 608 609 /* Auto-negotiation must be enabled for cable diagnostics to work, but 610 * don't advertise any capabilities. 611 */ 612 phy_write(phydev, MII_BMCR, BMCR_ANENABLE); 613 phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); 614 phy_write(phydev, MII_CTRL1000, 0); 615 616 phy_lock_mdio_bus(phydev); 617 if (is_rdb) { 618 ret = __bcm_phy_enable_legacy_access(phydev); 619 if (ret) 620 goto out; 621 } 622 623 mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK; 624 set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK | 625 FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK, 626 BCM54XX_ECD_CTRL_UNIT_CM); 627 628 ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set); 629 630out: 631 /* re-enable the RDB access even if there was an error */ 632 if (is_rdb) 633 ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 634 635 phy_unlock_mdio_bus(phydev); 636 637 return ret; 638} 639 640static int bcm_phy_cable_test_report_trans(int result) 641{ 642 switch (result) { 643 case BCM54XX_ECD_FAULT_TYPE_OK: 644 return ETHTOOL_A_CABLE_RESULT_CODE_OK; 645 case BCM54XX_ECD_FAULT_TYPE_OPEN: 646 return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 647 case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 648 return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 649 case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 650 return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; 651 case BCM54XX_ECD_FAULT_TYPE_INVALID: 652 case BCM54XX_ECD_FAULT_TYPE_BUSY: 653 default: 654 return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 655 } 656} 657 658static bool bcm_phy_distance_valid(int result) 659{ 660 switch (result) { 661 case BCM54XX_ECD_FAULT_TYPE_OPEN: 662 case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 663 case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 664 return true; 665 } 666 return false; 667} 668 669static int bcm_phy_report_length(struct phy_device *phydev, int pair) 670{ 671 int val; 672 673 val = __bcm_phy_read_exp(phydev, 674 BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair); 675 if (val < 0) 676 return val; 677 678 if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID) 679 return 0; 680 681 ethnl_cable_test_fault_length(phydev, pair, val); 682 683 return 0; 684} 685 686static int _bcm_phy_cable_test_get_status(struct phy_device *phydev, 687 bool *finished, bool is_rdb) 688{ 689 int pair_a, pair_b, pair_c, pair_d, ret; 690 691 *finished = false; 692 693 phy_lock_mdio_bus(phydev); 694 695 if (is_rdb) { 696 ret = __bcm_phy_enable_legacy_access(phydev); 697 if (ret) 698 goto out; 699 } 700 701 ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL); 702 if (ret < 0) 703 goto out; 704 705 if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) { 706 ret = 0; 707 goto out; 708 } 709 710 ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE); 711 if (ret < 0) 712 goto out; 713 714 pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret); 715 pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret); 716 pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret); 717 pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret); 718 719 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, 720 bcm_phy_cable_test_report_trans(pair_a)); 721 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, 722 bcm_phy_cable_test_report_trans(pair_b)); 723 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C, 724 bcm_phy_cable_test_report_trans(pair_c)); 725 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, 726 bcm_phy_cable_test_report_trans(pair_d)); 727 728 if (bcm_phy_distance_valid(pair_a)) 729 bcm_phy_report_length(phydev, 0); 730 if (bcm_phy_distance_valid(pair_b)) 731 bcm_phy_report_length(phydev, 1); 732 if (bcm_phy_distance_valid(pair_c)) 733 bcm_phy_report_length(phydev, 2); 734 if (bcm_phy_distance_valid(pair_d)) 735 bcm_phy_report_length(phydev, 3); 736 737 ret = 0; 738 *finished = true; 739out: 740 /* re-enable the RDB access even if there was an error */ 741 if (is_rdb) 742 ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 743 744 phy_unlock_mdio_bus(phydev); 745 746 return ret; 747} 748 749int bcm_phy_cable_test_start(struct phy_device *phydev) 750{ 751 return _bcm_phy_cable_test_start(phydev, false); 752} 753EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start); 754 755int bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished) 756{ 757 return _bcm_phy_cable_test_get_status(phydev, finished, false); 758} 759EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status); 760 761/* We assume that all PHYs which support RDB access can be switched to legacy 762 * mode. If, in the future, this is not true anymore, we have to re-implement 763 * this with RDB access. 764 */ 765int bcm_phy_cable_test_start_rdb(struct phy_device *phydev) 766{ 767 return _bcm_phy_cable_test_start(phydev, true); 768} 769EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb); 770 771int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev, 772 bool *finished) 773{ 774 return _bcm_phy_cable_test_get_status(phydev, finished, true); 775} 776EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb); 777 778MODULE_DESCRIPTION("Broadcom PHY Library"); 779MODULE_LICENSE("GPL v2"); 780MODULE_AUTHOR("Broadcom Corporation"); 781