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