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 19 int __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 } 29 EXPORT_SYMBOL_GPL(__bcm_phy_write_exp); 30 31 int 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 } 41 EXPORT_SYMBOL_GPL(bcm_phy_write_exp); 42 43 int __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 } 58 EXPORT_SYMBOL_GPL(__bcm_phy_read_exp); 59 60 int 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 } 70 EXPORT_SYMBOL_GPL(bcm_phy_read_exp); 71 72 int __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 } 90 EXPORT_SYMBOL_GPL(__bcm_phy_modify_exp); 91 92 int 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 } 102 EXPORT_SYMBOL_GPL(bcm_phy_modify_exp); 103 104 int 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 } 113 EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read); 114 115 int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val) 116 { 117 return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val); 118 } 119 EXPORT_SYMBOL(bcm54xx_auxctl_write); 120 121 int 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 } 143 EXPORT_SYMBOL_GPL(bcm_phy_write_misc); 144 145 int 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 } 167 EXPORT_SYMBOL_GPL(bcm_phy_read_misc); 168 169 int 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 } 180 EXPORT_SYMBOL_GPL(bcm_phy_ack_intr); 181 182 int 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 } 197 EXPORT_SYMBOL_GPL(bcm_phy_config_intr); 198 199 int 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 } 204 EXPORT_SYMBOL_GPL(bcm_phy_read_shadow); 205 206 int 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 } 214 EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); 215 216 int __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 } 226 EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb); 227 228 int 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 } 238 EXPORT_SYMBOL_GPL(bcm_phy_read_rdb); 239 240 int __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 } 250 EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb); 251 252 int 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 } 262 EXPORT_SYMBOL_GPL(bcm_phy_write_rdb); 263 264 int __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 } 282 EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb); 283 284 int 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 } 294 EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb); 295 296 int 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 } 327 EXPORT_SYMBOL_GPL(bcm_phy_enable_apd); 328 329 int bcm_phy_set_eee(struct phy_device *phydev, bool enable) 330 { 331 int val; 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 (enable) 351 val |= (MDIO_EEE_100TX | MDIO_EEE_1000T); 352 else 353 val &= ~(MDIO_EEE_100TX | MDIO_EEE_1000T); 354 355 phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val); 356 357 return 0; 358 } 359 EXPORT_SYMBOL_GPL(bcm_phy_set_eee); 360 361 int bcm_phy_downshift_get(struct phy_device *phydev, u8 *count) 362 { 363 int val; 364 365 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 366 if (val < 0) 367 return val; 368 369 /* Check if wirespeed is enabled or not */ 370 if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) { 371 *count = DOWNSHIFT_DEV_DISABLE; 372 return 0; 373 } 374 375 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 376 if (val < 0) 377 return val; 378 379 /* Downgrade after one link attempt */ 380 if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) { 381 *count = 1; 382 } else { 383 /* Downgrade after configured retry count */ 384 val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 385 val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK; 386 *count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET; 387 } 388 389 return 0; 390 } 391 EXPORT_SYMBOL_GPL(bcm_phy_downshift_get); 392 393 int bcm_phy_downshift_set(struct phy_device *phydev, u8 count) 394 { 395 int val = 0, ret = 0; 396 397 /* Range check the number given */ 398 if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET > 399 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK && 400 count != DOWNSHIFT_DEV_DEFAULT_COUNT) { 401 return -ERANGE; 402 } 403 404 val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); 405 if (val < 0) 406 return val; 407 408 /* Se the write enable bit */ 409 val |= MII_BCM54XX_AUXCTL_MISC_WREN; 410 411 if (count == DOWNSHIFT_DEV_DISABLE) { 412 val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 413 return bcm54xx_auxctl_write(phydev, 414 MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 415 val); 416 } else { 417 val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN; 418 ret = bcm54xx_auxctl_write(phydev, 419 MII_BCM54XX_AUXCTL_SHDWSEL_MISC, 420 val); 421 if (ret < 0) 422 return ret; 423 } 424 425 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2); 426 val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK << 427 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT | 428 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS); 429 430 switch (count) { 431 case 1: 432 val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS; 433 break; 434 case DOWNSHIFT_DEV_DEFAULT_COUNT: 435 val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 436 break; 437 default: 438 val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) << 439 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT; 440 break; 441 } 442 443 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val); 444 } 445 EXPORT_SYMBOL_GPL(bcm_phy_downshift_set); 446 447 struct bcm_phy_hw_stat { 448 const char *string; 449 u8 reg; 450 u8 shift; 451 u8 bits; 452 }; 453 454 /* Counters freeze at either 0xffff or 0xff, better than nothing */ 455 static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = { 456 { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 }, 457 { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 }, 458 { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 }, 459 { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 }, 460 { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 }, 461 }; 462 463 int bcm_phy_get_sset_count(struct phy_device *phydev) 464 { 465 return ARRAY_SIZE(bcm_phy_hw_stats); 466 } 467 EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count); 468 469 void bcm_phy_get_strings(struct phy_device *phydev, u8 *data) 470 { 471 unsigned int i; 472 473 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 474 strlcpy(data + i * ETH_GSTRING_LEN, 475 bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN); 476 } 477 EXPORT_SYMBOL_GPL(bcm_phy_get_strings); 478 479 /* Caller is supposed to provide appropriate storage for the library code to 480 * access the shadow copy 481 */ 482 static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow, 483 unsigned int i) 484 { 485 struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i]; 486 int val; 487 u64 ret; 488 489 val = phy_read(phydev, stat.reg); 490 if (val < 0) { 491 ret = U64_MAX; 492 } else { 493 val >>= stat.shift; 494 val = val & ((1 << stat.bits) - 1); 495 shadow[i] += val; 496 ret = shadow[i]; 497 } 498 499 return ret; 500 } 501 502 void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, 503 struct ethtool_stats *stats, u64 *data) 504 { 505 unsigned int i; 506 507 for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) 508 data[i] = bcm_phy_get_stat(phydev, shadow, i); 509 } 510 EXPORT_SYMBOL_GPL(bcm_phy_get_stats); 511 512 void bcm_phy_r_rc_cal_reset(struct phy_device *phydev) 513 { 514 /* Reset R_CAL/RC_CAL Engine */ 515 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010); 516 517 /* Disable Reset R_AL/RC_CAL Engine */ 518 bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000); 519 } 520 EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset); 521 522 int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev) 523 { 524 /* Increase VCO range to prevent unlocking problem of PLL at low 525 * temp 526 */ 527 bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048); 528 529 /* Change Ki to 011 */ 530 bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b); 531 532 /* Disable loading of TVCO buffer to bandgap, set bandgap trim 533 * to 111 534 */ 535 bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20); 536 537 /* Adjust bias current trim by -3 */ 538 bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b); 539 540 /* Switch to CORE_BASE1E */ 541 phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd); 542 543 bcm_phy_r_rc_cal_reset(phydev); 544 545 /* write AFE_RXCONFIG_0 */ 546 bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19); 547 548 /* write AFE_RXCONFIG_1 */ 549 bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f); 550 551 /* write AFE_RX_LP_COUNTER */ 552 bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); 553 554 /* write AFE_HPF_TRIM_OTHERS */ 555 bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b); 556 557 /* write AFTE_TX_CONFIG */ 558 bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800); 559 560 return 0; 561 } 562 EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init); 563 564 int bcm_phy_enable_jumbo(struct phy_device *phydev) 565 { 566 int ret; 567 568 ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL); 569 if (ret < 0) 570 return ret; 571 572 /* Enable extended length packet reception */ 573 ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL, 574 ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN); 575 if (ret < 0) 576 return ret; 577 578 /* Enable the elastic FIFO for raising the transmission limit from 579 * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation 580 * latency. 581 */ 582 return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE); 583 } 584 EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo); 585 586 static int __bcm_phy_enable_rdb_access(struct phy_device *phydev) 587 { 588 return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0); 589 } 590 591 static int __bcm_phy_enable_legacy_access(struct phy_device *phydev) 592 { 593 return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087, 594 BCM54XX_ACCESS_MODE_LEGACY_EN); 595 } 596 597 static int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb) 598 { 599 u16 mask, set; 600 int ret; 601 602 /* Auto-negotiation must be enabled for cable diagnostics to work, but 603 * don't advertise any capabilities. 604 */ 605 phy_write(phydev, MII_BMCR, BMCR_ANENABLE); 606 phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA); 607 phy_write(phydev, MII_CTRL1000, 0); 608 609 phy_lock_mdio_bus(phydev); 610 if (is_rdb) { 611 ret = __bcm_phy_enable_legacy_access(phydev); 612 if (ret) 613 goto out; 614 } 615 616 mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK; 617 set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK | 618 FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK, 619 BCM54XX_ECD_CTRL_UNIT_CM); 620 621 ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set); 622 623 out: 624 /* re-enable the RDB access even if there was an error */ 625 if (is_rdb) 626 ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 627 628 phy_unlock_mdio_bus(phydev); 629 630 return ret; 631 } 632 633 static int bcm_phy_cable_test_report_trans(int result) 634 { 635 switch (result) { 636 case BCM54XX_ECD_FAULT_TYPE_OK: 637 return ETHTOOL_A_CABLE_RESULT_CODE_OK; 638 case BCM54XX_ECD_FAULT_TYPE_OPEN: 639 return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 640 case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 641 return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 642 case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 643 return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT; 644 case BCM54XX_ECD_FAULT_TYPE_INVALID: 645 case BCM54XX_ECD_FAULT_TYPE_BUSY: 646 default: 647 return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 648 } 649 } 650 651 static bool bcm_phy_distance_valid(int result) 652 { 653 switch (result) { 654 case BCM54XX_ECD_FAULT_TYPE_OPEN: 655 case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT: 656 case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT: 657 return true; 658 } 659 return false; 660 } 661 662 static int bcm_phy_report_length(struct phy_device *phydev, int pair) 663 { 664 int val; 665 666 val = __bcm_phy_read_exp(phydev, 667 BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair); 668 if (val < 0) 669 return val; 670 671 if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID) 672 return 0; 673 674 ethnl_cable_test_fault_length(phydev, pair, val); 675 676 return 0; 677 } 678 679 static int _bcm_phy_cable_test_get_status(struct phy_device *phydev, 680 bool *finished, bool is_rdb) 681 { 682 int pair_a, pair_b, pair_c, pair_d, ret; 683 684 *finished = false; 685 686 phy_lock_mdio_bus(phydev); 687 688 if (is_rdb) { 689 ret = __bcm_phy_enable_legacy_access(phydev); 690 if (ret) 691 goto out; 692 } 693 694 ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL); 695 if (ret < 0) 696 goto out; 697 698 if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) { 699 ret = 0; 700 goto out; 701 } 702 703 ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE); 704 if (ret < 0) 705 goto out; 706 707 pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret); 708 pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret); 709 pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret); 710 pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret); 711 712 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, 713 bcm_phy_cable_test_report_trans(pair_a)); 714 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B, 715 bcm_phy_cable_test_report_trans(pair_b)); 716 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C, 717 bcm_phy_cable_test_report_trans(pair_c)); 718 ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D, 719 bcm_phy_cable_test_report_trans(pair_d)); 720 721 if (bcm_phy_distance_valid(pair_a)) 722 bcm_phy_report_length(phydev, 0); 723 if (bcm_phy_distance_valid(pair_b)) 724 bcm_phy_report_length(phydev, 1); 725 if (bcm_phy_distance_valid(pair_c)) 726 bcm_phy_report_length(phydev, 2); 727 if (bcm_phy_distance_valid(pair_d)) 728 bcm_phy_report_length(phydev, 3); 729 730 ret = 0; 731 *finished = true; 732 out: 733 /* re-enable the RDB access even if there was an error */ 734 if (is_rdb) 735 ret = __bcm_phy_enable_rdb_access(phydev) ? : ret; 736 737 phy_unlock_mdio_bus(phydev); 738 739 return ret; 740 } 741 742 int bcm_phy_cable_test_start(struct phy_device *phydev) 743 { 744 return _bcm_phy_cable_test_start(phydev, false); 745 } 746 EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start); 747 748 int bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished) 749 { 750 return _bcm_phy_cable_test_get_status(phydev, finished, false); 751 } 752 EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status); 753 754 /* We assume that all PHYs which support RDB access can be switched to legacy 755 * mode. If, in the future, this is not true anymore, we have to re-implement 756 * this with RDB access. 757 */ 758 int bcm_phy_cable_test_start_rdb(struct phy_device *phydev) 759 { 760 return _bcm_phy_cable_test_start(phydev, true); 761 } 762 EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb); 763 764 int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev, 765 bool *finished) 766 { 767 return _bcm_phy_cable_test_get_status(phydev, finished, true); 768 } 769 EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb); 770 771 MODULE_DESCRIPTION("Broadcom PHY Library"); 772 MODULE_LICENSE("GPL v2"); 773 MODULE_AUTHOR("Broadcom Corporation"); 774