.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2015-2017 Broadcom |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or |
---|
5 | | - * modify it under the terms of the GNU General Public License as |
---|
6 | | - * published by the Free Software Foundation version 2. |
---|
7 | | - * |
---|
8 | | - * This program is distributed "as is" WITHOUT ANY WARRANTY of any |
---|
9 | | - * kind, whether express or implied; without even the implied warranty |
---|
10 | | - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
11 | | - * GNU General Public License for more details. |
---|
12 | 4 | */ |
---|
13 | 5 | |
---|
14 | 6 | #include "bcm-phy-lib.h" |
---|
| 7 | +#include <linux/bitfield.h> |
---|
15 | 8 | #include <linux/brcmphy.h> |
---|
16 | 9 | #include <linux/export.h> |
---|
17 | 10 | #include <linux/mdio.h> |
---|
18 | 11 | #include <linux/module.h> |
---|
19 | 12 | #include <linux/phy.h> |
---|
20 | 13 | #include <linux/ethtool.h> |
---|
| 14 | +#include <linux/ethtool_netlink.h> |
---|
21 | 15 | |
---|
22 | 16 | #define MII_BCM_CHANNEL_WIDTH 0x2000 |
---|
23 | 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); |
---|
24 | 30 | |
---|
25 | 31 | int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val) |
---|
26 | 32 | { |
---|
27 | 33 | int rc; |
---|
28 | 34 | |
---|
29 | | - rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); |
---|
30 | | - if (rc < 0) |
---|
31 | | - return rc; |
---|
| 35 | + phy_lock_mdio_bus(phydev); |
---|
| 36 | + rc = __bcm_phy_write_exp(phydev, reg, val); |
---|
| 37 | + phy_unlock_mdio_bus(phydev); |
---|
32 | 38 | |
---|
33 | | - return phy_write(phydev, MII_BCM54XX_EXP_DATA, val); |
---|
| 39 | + return rc; |
---|
34 | 40 | } |
---|
35 | 41 | EXPORT_SYMBOL_GPL(bcm_phy_write_exp); |
---|
36 | 42 | |
---|
37 | | -int bcm_phy_read_exp(struct phy_device *phydev, u16 reg) |
---|
| 43 | +int __bcm_phy_read_exp(struct phy_device *phydev, u16 reg) |
---|
38 | 44 | { |
---|
39 | 45 | int val; |
---|
40 | 46 | |
---|
41 | | - val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); |
---|
| 47 | + val = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg); |
---|
42 | 48 | if (val < 0) |
---|
43 | 49 | return val; |
---|
44 | 50 | |
---|
45 | | - val = phy_read(phydev, MII_BCM54XX_EXP_DATA); |
---|
| 51 | + val = __phy_read(phydev, MII_BCM54XX_EXP_DATA); |
---|
46 | 52 | |
---|
47 | 53 | /* Restore default value. It's O.K. if this write fails. */ |
---|
48 | | - phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); |
---|
| 54 | + __phy_write(phydev, MII_BCM54XX_EXP_SEL, 0); |
---|
49 | 55 | |
---|
50 | 56 | return val; |
---|
51 | 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 | +} |
---|
52 | 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); |
---|
53 | 103 | |
---|
54 | 104 | int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum) |
---|
55 | 105 | { |
---|
.. | .. |
---|
163 | 213 | } |
---|
164 | 214 | EXPORT_SYMBOL_GPL(bcm_phy_write_shadow); |
---|
165 | 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 | + |
---|
166 | 296 | int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down) |
---|
167 | 297 | { |
---|
168 | 298 | int val; |
---|
.. | .. |
---|
217 | 347 | if (val < 0) |
---|
218 | 348 | return val; |
---|
219 | 349 | |
---|
220 | | - if (phydev->supported & SUPPORTED_1000baseT_Full) |
---|
| 350 | + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
---|
| 351 | + phydev->supported)) |
---|
221 | 352 | mask |= MDIO_EEE_1000T; |
---|
222 | | - if (phydev->supported & SUPPORTED_100baseT_Full) |
---|
| 353 | + if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, |
---|
| 354 | + phydev->supported)) |
---|
223 | 355 | mask |= MDIO_EEE_100TX; |
---|
224 | 356 | |
---|
225 | 357 | if (enable) |
---|
.. | .. |
---|
384 | 516 | } |
---|
385 | 517 | EXPORT_SYMBOL_GPL(bcm_phy_get_stats); |
---|
386 | 518 | |
---|
| 519 | +void 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 | +} |
---|
| 527 | +EXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset); |
---|
| 528 | + |
---|
| 529 | +int 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 | +} |
---|
| 569 | +EXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init); |
---|
| 570 | + |
---|
| 571 | +int 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 | +} |
---|
| 591 | +EXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo); |
---|
| 592 | + |
---|
| 593 | +static int __bcm_phy_enable_rdb_access(struct phy_device *phydev) |
---|
| 594 | +{ |
---|
| 595 | + return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0); |
---|
| 596 | +} |
---|
| 597 | + |
---|
| 598 | +static 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 | + |
---|
| 604 | +static 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 | + |
---|
| 630 | +out: |
---|
| 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 | + |
---|
| 640 | +static 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 | + |
---|
| 658 | +static 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 | + |
---|
| 669 | +static 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 | + |
---|
| 686 | +static 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; |
---|
| 739 | +out: |
---|
| 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 | + |
---|
| 749 | +int bcm_phy_cable_test_start(struct phy_device *phydev) |
---|
| 750 | +{ |
---|
| 751 | + return _bcm_phy_cable_test_start(phydev, false); |
---|
| 752 | +} |
---|
| 753 | +EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start); |
---|
| 754 | + |
---|
| 755 | +int 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 | +} |
---|
| 759 | +EXPORT_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 | + */ |
---|
| 765 | +int bcm_phy_cable_test_start_rdb(struct phy_device *phydev) |
---|
| 766 | +{ |
---|
| 767 | + return _bcm_phy_cable_test_start(phydev, true); |
---|
| 768 | +} |
---|
| 769 | +EXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb); |
---|
| 770 | + |
---|
| 771 | +int 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 | +} |
---|
| 776 | +EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb); |
---|
| 777 | + |
---|
387 | 778 | MODULE_DESCRIPTION("Broadcom PHY Library"); |
---|
388 | 779 | MODULE_LICENSE("GPL v2"); |
---|
389 | 780 | MODULE_AUTHOR("Broadcom Corporation"); |
---|