Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: phy: bcm-phy-lib: Implement BroadR-Reach link modes

Implement single-pair BroadR-Reach modes on bcm5481x PHY by Broadcom.
Create set of functions alternative to IEEE 802.3 to handle
configuration of these modes on compatible Broadcom PHYs.
There is only subset of capabilities supported because of limited
collection of hardware available for the development.
For BroadR-Reach capable PHYs, the LRE (Long Reach Ethernet)
alternative register set is handled. Only bcm54811 PHY is verified,
for bcm54810, there is some support possible but untested. There
is no auto-negotiation of the link parameters (called LDS in the
Broadcom terminology, Long-Distance Signaling) for bcm54811.
It should be possible to enable LDS for bcm54810.

Signed-off-by: Kamil Horák (2N) <kamilh@axis.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
Link: https://patch.msgid.link/20240712150709.3134474-5-kamilh@axis.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Kamil Horák (2N) and committed by
Jakub Kicinski
03ab6c24 775631d7

+501 -35
+115
drivers/net/phy/bcm-phy-lib.c
··· 794 794 return ret; 795 795 } 796 796 797 + static int bcm_setup_lre_forced(struct phy_device *phydev) 798 + { 799 + u16 ctl = 0; 800 + 801 + phydev->pause = 0; 802 + phydev->asym_pause = 0; 803 + 804 + if (phydev->speed == SPEED_100) 805 + ctl |= LRECR_SPEED100; 806 + 807 + if (phydev->duplex != DUPLEX_FULL) 808 + return -EOPNOTSUPP; 809 + 810 + return phy_modify(phydev, MII_BCM54XX_LRECR, LRECR_SPEED100, ctl); 811 + } 812 + 813 + /** 814 + * bcm_linkmode_adv_to_lre_adv_t - translate linkmode advertisement to LDS 815 + * @advertising: the linkmode advertisement settings 816 + * Return: LDS Auto-Negotiation Advertised Ability register value 817 + * 818 + * A small helper function that translates linkmode advertisement 819 + * settings to phy LDS autonegotiation advertisements for the 820 + * MII_BCM54XX_LREANAA register of Broadcom PHYs capable of LDS 821 + */ 822 + static u32 bcm_linkmode_adv_to_lre_adv_t(unsigned long *advertising) 823 + { 824 + u32 result = 0; 825 + 826 + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT, 827 + advertising)) 828 + result |= LREANAA_10_1PAIR; 829 + if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, 830 + advertising)) 831 + result |= LREANAA_100_1PAIR; 832 + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising)) 833 + result |= LRELPA_PAUSE; 834 + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertising)) 835 + result |= LRELPA_PAUSE_ASYM; 836 + 837 + return result; 838 + } 839 + 797 840 int bcm_phy_cable_test_start(struct phy_device *phydev) 798 841 { 799 842 return _bcm_phy_cable_test_start(phydev, false); ··· 1108 1065 return bcm_phy_write_shadow(phydev, reg, ret); 1109 1066 } 1110 1067 EXPORT_SYMBOL_GPL(bcm_phy_led_brightness_set); 1068 + 1069 + int bcm_setup_lre_master_slave(struct phy_device *phydev) 1070 + { 1071 + u16 ctl = 0; 1072 + 1073 + switch (phydev->master_slave_set) { 1074 + case MASTER_SLAVE_CFG_MASTER_PREFERRED: 1075 + case MASTER_SLAVE_CFG_MASTER_FORCE: 1076 + ctl = LRECR_MASTER; 1077 + break; 1078 + case MASTER_SLAVE_CFG_SLAVE_PREFERRED: 1079 + case MASTER_SLAVE_CFG_SLAVE_FORCE: 1080 + break; 1081 + case MASTER_SLAVE_CFG_UNKNOWN: 1082 + case MASTER_SLAVE_CFG_UNSUPPORTED: 1083 + return 0; 1084 + default: 1085 + phydev_warn(phydev, "Unsupported Master/Slave mode\n"); 1086 + return -EOPNOTSUPP; 1087 + } 1088 + 1089 + return phy_modify_changed(phydev, MII_BCM54XX_LRECR, LRECR_MASTER, ctl); 1090 + } 1091 + EXPORT_SYMBOL_GPL(bcm_setup_lre_master_slave); 1092 + 1093 + int bcm_config_lre_aneg(struct phy_device *phydev, bool changed) 1094 + { 1095 + int err; 1096 + 1097 + if (genphy_config_eee_advert(phydev)) 1098 + changed = true; 1099 + 1100 + err = bcm_setup_lre_master_slave(phydev); 1101 + if (err < 0) 1102 + return err; 1103 + else if (err) 1104 + changed = true; 1105 + 1106 + if (phydev->autoneg != AUTONEG_ENABLE) 1107 + return bcm_setup_lre_forced(phydev); 1108 + 1109 + err = bcm_config_lre_advert(phydev); 1110 + if (err < 0) 1111 + return err; 1112 + else if (err) 1113 + changed = true; 1114 + 1115 + return genphy_check_and_restart_aneg(phydev, changed); 1116 + } 1117 + EXPORT_SYMBOL_GPL(bcm_config_lre_aneg); 1118 + 1119 + /** 1120 + * bcm_config_lre_advert - sanitize and advertise Long-Distance Signaling 1121 + * auto-negotiation parameters 1122 + * @phydev: target phy_device struct 1123 + * Return: 0 if the PHY's advertisement hasn't changed, < 0 on error, 1124 + * > 0 if it has changed 1125 + * 1126 + * Writes MII_BCM54XX_LREANAA with the appropriate values. The values are to be 1127 + * sanitized before, to make sure we only advertise what is supported. 1128 + * The sanitization is done already in phy_ethtool_ksettings_set() 1129 + */ 1130 + int bcm_config_lre_advert(struct phy_device *phydev) 1131 + { 1132 + u32 adv = bcm_linkmode_adv_to_lre_adv_t(phydev->advertising); 1133 + 1134 + /* Setup BroadR-Reach mode advertisement */ 1135 + return phy_modify_changed(phydev, MII_BCM54XX_LREANAA, 1136 + LRE_ADVERTISE_ALL | LREANAA_PAUSE | 1137 + LREANAA_PAUSE_ASYM, adv); 1138 + } 1139 + EXPORT_SYMBOL_GPL(bcm_config_lre_advert); 1111 1140 1112 1141 MODULE_DESCRIPTION("Broadcom PHY Library"); 1113 1142 MODULE_LICENSE("GPL v2");
+4
drivers/net/phy/bcm-phy-lib.h
··· 121 121 int bcm_phy_led_brightness_set(struct phy_device *phydev, 122 122 u8 index, enum led_brightness value); 123 123 124 + int bcm_setup_lre_master_slave(struct phy_device *phydev); 125 + int bcm_config_lre_aneg(struct phy_device *phydev, bool changed); 126 + int bcm_config_lre_advert(struct phy_device *phydev); 127 + 124 128 #endif /* _LINUX_BCM_PHY_LIB_H */
+382 -35
drivers/net/phy/broadcom.c
··· 5 5 * Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet 6 6 * transceivers. 7 7 * 8 + * Broadcom BCM54810, BCM54811 BroadR-Reach transceivers. 9 + * 8 10 * Copyright (c) 2006 Maciej W. Rozycki 9 11 * 10 12 * Inspired by code written by Amy Fong. ··· 38 36 struct bcm_ptp_private *ptp; 39 37 int wake_irq; 40 38 bool wake_irq_enabled; 39 + bool brr_mode; 40 + }; 41 + 42 + /* Link modes for BCM58411 PHY */ 43 + static const int bcm54811_linkmodes[] = { 44 + ETHTOOL_LINK_MODE_100baseT1_Full_BIT, 45 + ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT, 46 + ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 47 + ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 48 + ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 49 + ETHTOOL_LINK_MODE_100baseT_Full_BIT, 50 + ETHTOOL_LINK_MODE_100baseT_Half_BIT, 51 + ETHTOOL_LINK_MODE_10baseT_Full_BIT, 52 + ETHTOOL_LINK_MODE_10baseT_Half_BIT 53 + }; 54 + 55 + /* Long-Distance Signaling (BroadR-Reach mode aneg) relevant linkmode bits */ 56 + static const int lds_br_bits[] = { 57 + ETHTOOL_LINK_MODE_Autoneg_BIT, 58 + ETHTOOL_LINK_MODE_Pause_BIT, 59 + ETHTOOL_LINK_MODE_Asym_Pause_BIT, 60 + ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT, 61 + ETHTOOL_LINK_MODE_100baseT1_Full_BIT 41 62 }; 42 63 43 64 static bool bcm54xx_phy_can_wakeup(struct phy_device *phydev) ··· 372 347 bcm_ptp_config_init(phydev); 373 348 } 374 349 350 + static int bcm5481x_set_brrmode(struct phy_device *phydev, bool on) 351 + { 352 + int reg; 353 + int err; 354 + u16 val; 355 + 356 + reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL); 357 + 358 + if (reg < 0) 359 + return reg; 360 + 361 + if (on) 362 + reg |= BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; 363 + else 364 + reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; 365 + 366 + err = bcm_phy_write_exp(phydev, 367 + BCM54810_EXP_BROADREACH_LRE_MISC_CTL, reg); 368 + if (err) 369 + return err; 370 + 371 + /* Ensure LRE or IEEE register set is accessed according to the brr 372 + * on/off, thus set the override 373 + */ 374 + val = BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_EN; 375 + if (!on) 376 + val |= BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_OVERRIDE_VAL; 377 + 378 + return bcm_phy_write_exp(phydev, 379 + BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL, val); 380 + } 381 + 382 + static int bcm54811_config_init(struct phy_device *phydev) 383 + { 384 + struct bcm54xx_phy_priv *priv = phydev->priv; 385 + int err, reg; 386 + 387 + /* Enable CLK125 MUX on LED4 if ref clock is enabled. */ 388 + if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) { 389 + reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0); 390 + if (reg < 0) 391 + return reg; 392 + err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0, 393 + BCM54612E_LED4_CLK125OUT_EN | reg); 394 + if (err < 0) 395 + return err; 396 + } 397 + 398 + /* With BCM54811, BroadR-Reach implies no autoneg */ 399 + if (priv->brr_mode) 400 + phydev->autoneg = 0; 401 + 402 + return bcm5481x_set_brrmode(phydev, priv->brr_mode); 403 + } 404 + 375 405 static int bcm54xx_config_init(struct phy_device *phydev) 376 406 { 377 407 int reg, err, val; ··· 478 398 err = bcm_phy_write_exp(phydev, 479 399 BCM54810_EXP_BROADREACH_LRE_MISC_CTL, 480 400 val); 401 + break; 402 + case PHY_ID_BCM54811: 403 + err = bcm54811_config_init(phydev); 481 404 break; 482 405 } 483 406 if (err) ··· 636 553 return -EOPNOTSUPP; 637 554 } 638 555 639 - static int bcm54811_config_init(struct phy_device *phydev) 640 - { 641 - int err, reg; 642 556 643 - /* Disable BroadR-Reach function. */ 644 - reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL); 645 - reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; 646 - err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL, 647 - reg); 648 - if (err < 0) 649 - return err; 650 - 651 - err = bcm54xx_config_init(phydev); 652 - 653 - /* Enable CLK125 MUX on LED4 if ref clock is enabled. */ 654 - if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) { 655 - reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0); 656 - err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0, 657 - BCM54612E_LED4_CLK125OUT_EN | reg); 658 - if (err < 0) 659 - return err; 660 - } 661 - 662 - return err; 663 - } 664 - 665 - static int bcm5481_config_aneg(struct phy_device *phydev) 557 + /** 558 + * bcm5481x_read_abilities - read PHY abilities from LRESR or Clause 22 559 + * (BMSR) registers, based on whether the PHY is in BroadR-Reach or IEEE mode 560 + * @phydev: target phy_device struct 561 + * 562 + * Description: Reads the PHY's abilities and populates phydev->supported 563 + * accordingly. The register to read the abilities from is determined by 564 + * the brr mode setting of the PHY as read from the device tree. 565 + * Note that the LRE and IEEE sets of abilities are disjunct, in other words, 566 + * not only the link modes differ, but also the auto-negotiation and 567 + * master-slave setup is controlled differently. 568 + * 569 + * Returns: 0 on success, < 0 on failure 570 + */ 571 + static int bcm5481x_read_abilities(struct phy_device *phydev) 666 572 { 667 573 struct device_node *np = phydev->mdio.dev.of_node; 668 - int ret; 574 + struct bcm54xx_phy_priv *priv = phydev->priv; 575 + int i, val, err; 669 576 670 - /* Aneg firstly. */ 671 - ret = genphy_config_aneg(phydev); 577 + for (i = 0; i < ARRAY_SIZE(bcm54811_linkmodes); i++) 578 + linkmode_clear_bit(bcm54811_linkmodes[i], phydev->supported); 672 579 673 - /* Then we can set up the delay. */ 580 + priv->brr_mode = of_property_read_bool(np, "brr-mode"); 581 + 582 + /* Set BroadR-Reach mode as configured in the DT. */ 583 + err = bcm5481x_set_brrmode(phydev, priv->brr_mode); 584 + if (err) 585 + return err; 586 + 587 + if (priv->brr_mode) { 588 + linkmode_set_bit_array(phy_basic_ports_array, 589 + ARRAY_SIZE(phy_basic_ports_array), 590 + phydev->supported); 591 + 592 + val = phy_read(phydev, MII_BCM54XX_LRESR); 593 + if (val < 0) 594 + return val; 595 + 596 + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 597 + phydev->supported, 598 + val & LRESR_LDSABILITY); 599 + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, 600 + phydev->supported, 601 + val & LRESR_100_1PAIR); 602 + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT, 603 + phydev->supported, 604 + val & LRESR_10_1PAIR); 605 + return 0; 606 + } 607 + 608 + return genphy_read_abilities(phydev); 609 + } 610 + 611 + static int bcm5481x_config_delay_swap(struct phy_device *phydev) 612 + { 613 + struct device_node *np = phydev->mdio.dev.of_node; 614 + 615 + /* Set up the delay. */ 674 616 bcm54xx_config_clock_delay(phydev); 675 617 676 618 if (of_property_read_bool(np, "enet-phy-lane-swap")) { 677 619 /* Lane Swap - Undocumented register...magic! */ 678 - ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9, 679 - 0x11B); 620 + int ret = bcm_phy_write_exp(phydev, 621 + MII_BCM54XX_EXP_SEL_ER + 0x9, 622 + 0x11B); 680 623 if (ret < 0) 681 624 return ret; 682 625 } 683 626 684 - return ret; 627 + return 0; 628 + } 629 + 630 + static int bcm5481_config_aneg(struct phy_device *phydev) 631 + { 632 + struct bcm54xx_phy_priv *priv = phydev->priv; 633 + int ret; 634 + 635 + /* Aneg firstly. */ 636 + if (priv->brr_mode) 637 + ret = bcm_config_lre_aneg(phydev, false); 638 + else 639 + ret = genphy_config_aneg(phydev); 640 + 641 + if (ret) 642 + return ret; 643 + 644 + /* Then we can set up the delay and swap. */ 645 + return bcm5481x_config_delay_swap(phydev); 646 + } 647 + 648 + static int bcm54811_config_aneg(struct phy_device *phydev) 649 + { 650 + struct bcm54xx_phy_priv *priv = phydev->priv; 651 + int ret; 652 + 653 + /* Aneg firstly. */ 654 + if (priv->brr_mode) { 655 + /* BCM54811 is only capable of autonegotiation in IEEE mode */ 656 + phydev->autoneg = 0; 657 + ret = bcm_config_lre_aneg(phydev, false); 658 + } else { 659 + ret = genphy_config_aneg(phydev); 660 + } 661 + 662 + if (ret) 663 + return ret; 664 + 665 + /* Then we can set up the delay and swap. */ 666 + return bcm5481x_config_delay_swap(phydev); 685 667 } 686 668 687 669 struct bcm54616s_phy_priv { ··· 1210 1062 bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, ret); 1211 1063 } 1212 1064 1065 + static int lre_read_master_slave(struct phy_device *phydev) 1066 + { 1067 + int cfg = MASTER_SLAVE_CFG_UNKNOWN, state; 1068 + int val; 1069 + 1070 + /* In BroadR-Reach mode we are always capable of master-slave 1071 + * and there is no preferred master or slave configuration 1072 + */ 1073 + phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN; 1074 + phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; 1075 + 1076 + val = phy_read(phydev, MII_BCM54XX_LRECR); 1077 + if (val < 0) 1078 + return val; 1079 + 1080 + if ((val & LRECR_LDSEN) == 0) { 1081 + if (val & LRECR_MASTER) 1082 + cfg = MASTER_SLAVE_CFG_MASTER_FORCE; 1083 + else 1084 + cfg = MASTER_SLAVE_CFG_SLAVE_FORCE; 1085 + } 1086 + 1087 + val = phy_read(phydev, MII_BCM54XX_LRELDSE); 1088 + if (val < 0) 1089 + return val; 1090 + 1091 + if (val & LDSE_MASTER) 1092 + state = MASTER_SLAVE_STATE_MASTER; 1093 + else 1094 + state = MASTER_SLAVE_STATE_SLAVE; 1095 + 1096 + phydev->master_slave_get = cfg; 1097 + phydev->master_slave_state = state; 1098 + 1099 + return 0; 1100 + } 1101 + 1102 + /* Read LDS Link Partner Ability in BroadR-Reach mode */ 1103 + static int lre_read_lpa(struct phy_device *phydev) 1104 + { 1105 + int i, lrelpa; 1106 + 1107 + if (phydev->autoneg != AUTONEG_ENABLE) { 1108 + if (!phydev->autoneg_complete) { 1109 + /* aneg not yet done, reset all relevant bits */ 1110 + for (i = 0; i < ARRAY_SIZE(lds_br_bits); i++) 1111 + linkmode_clear_bit(lds_br_bits[i], 1112 + phydev->lp_advertising); 1113 + 1114 + return 0; 1115 + } 1116 + 1117 + /* Long-Distance Signaling Link Partner Ability */ 1118 + lrelpa = phy_read(phydev, MII_BCM54XX_LRELPA); 1119 + if (lrelpa < 0) 1120 + return lrelpa; 1121 + 1122 + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, 1123 + phydev->lp_advertising, 1124 + lrelpa & LRELPA_PAUSE_ASYM); 1125 + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, 1126 + phydev->lp_advertising, 1127 + lrelpa & LRELPA_PAUSE); 1128 + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, 1129 + phydev->lp_advertising, 1130 + lrelpa & LRELPA_100_1PAIR); 1131 + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT, 1132 + phydev->lp_advertising, 1133 + lrelpa & LRELPA_10_1PAIR); 1134 + } else { 1135 + linkmode_zero(phydev->lp_advertising); 1136 + } 1137 + 1138 + return 0; 1139 + } 1140 + 1141 + static int lre_read_status_fixed(struct phy_device *phydev) 1142 + { 1143 + int lrecr = phy_read(phydev, MII_BCM54XX_LRECR); 1144 + 1145 + if (lrecr < 0) 1146 + return lrecr; 1147 + 1148 + phydev->duplex = DUPLEX_FULL; 1149 + 1150 + if (lrecr & LRECR_SPEED100) 1151 + phydev->speed = SPEED_100; 1152 + else 1153 + phydev->speed = SPEED_10; 1154 + 1155 + return 0; 1156 + } 1157 + 1158 + /** 1159 + * lre_update_link - update link status in @phydev 1160 + * @phydev: target phy_device struct 1161 + * Return: 0 on success, < 0 on error 1162 + * 1163 + * Description: Update the value in phydev->link to reflect the 1164 + * current link value. In order to do this, we need to read 1165 + * the status register twice, keeping the second value. 1166 + * This is a genphy_update_link modified to work on LRE registers 1167 + * of BroadR-Reach PHY 1168 + */ 1169 + static int lre_update_link(struct phy_device *phydev) 1170 + { 1171 + int status = 0, lrecr; 1172 + 1173 + lrecr = phy_read(phydev, MII_BCM54XX_LRECR); 1174 + if (lrecr < 0) 1175 + return lrecr; 1176 + 1177 + /* Autoneg is being started, therefore disregard BMSR value and 1178 + * report link as down. 1179 + */ 1180 + if (lrecr & BMCR_ANRESTART) 1181 + goto done; 1182 + 1183 + /* The link state is latched low so that momentary link 1184 + * drops can be detected. Do not double-read the status 1185 + * in polling mode to detect such short link drops except 1186 + * the link was already down. 1187 + */ 1188 + if (!phy_polling_mode(phydev) || !phydev->link) { 1189 + status = phy_read(phydev, MII_BCM54XX_LRESR); 1190 + if (status < 0) 1191 + return status; 1192 + else if (status & LRESR_LSTATUS) 1193 + goto done; 1194 + } 1195 + 1196 + /* Read link and autonegotiation status */ 1197 + status = phy_read(phydev, MII_BCM54XX_LRESR); 1198 + if (status < 0) 1199 + return status; 1200 + done: 1201 + phydev->link = status & LRESR_LSTATUS ? 1 : 0; 1202 + phydev->autoneg_complete = status & LRESR_LDSCOMPLETE ? 1 : 0; 1203 + 1204 + /* Consider the case that autoneg was started and "aneg complete" 1205 + * bit has been reset, but "link up" bit not yet. 1206 + */ 1207 + if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) 1208 + phydev->link = 0; 1209 + 1210 + return 0; 1211 + } 1212 + 1213 + /* Get the status in BroadRReach mode just like genphy_read_status does 1214 + * in normal mode 1215 + */ 1216 + static int bcm54811_lre_read_status(struct phy_device *phydev) 1217 + { 1218 + int err, old_link = phydev->link; 1219 + 1220 + /* Update the link, but return if there was an error */ 1221 + err = lre_update_link(phydev); 1222 + if (err) 1223 + return err; 1224 + 1225 + /* why bother the PHY if nothing can have changed */ 1226 + if (phydev->autoneg == 1227 + AUTONEG_ENABLE && old_link && phydev->link) 1228 + return 0; 1229 + 1230 + phydev->speed = SPEED_UNKNOWN; 1231 + phydev->duplex = DUPLEX_UNKNOWN; 1232 + phydev->pause = 0; 1233 + phydev->asym_pause = 0; 1234 + 1235 + err = lre_read_master_slave(phydev); 1236 + if (err < 0) 1237 + return err; 1238 + 1239 + /* Read LDS Link Partner Ability */ 1240 + err = lre_read_lpa(phydev); 1241 + if (err < 0) 1242 + return err; 1243 + 1244 + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) 1245 + phy_resolve_aneg_linkmode(phydev); 1246 + else if (phydev->autoneg == AUTONEG_DISABLE) 1247 + err = lre_read_status_fixed(phydev); 1248 + 1249 + return err; 1250 + } 1251 + 1252 + static int bcm54811_read_status(struct phy_device *phydev) 1253 + { 1254 + struct bcm54xx_phy_priv *priv = phydev->priv; 1255 + 1256 + if (priv->brr_mode) 1257 + return bcm54811_lre_read_status(phydev); 1258 + 1259 + return genphy_read_status(phydev); 1260 + } 1261 + 1213 1262 static struct phy_driver broadcom_drivers[] = { 1214 1263 { 1215 1264 .phy_id = PHY_ID_BCM5411, ··· 1556 1211 .get_strings = bcm_phy_get_strings, 1557 1212 .get_stats = bcm54xx_get_stats, 1558 1213 .probe = bcm54xx_phy_probe, 1559 - .config_init = bcm54811_config_init, 1560 - .config_aneg = bcm5481_config_aneg, 1214 + .config_init = bcm54xx_config_init, 1215 + .config_aneg = bcm54811_config_aneg, 1561 1216 .config_intr = bcm_phy_config_intr, 1562 1217 .handle_interrupt = bcm_phy_handle_interrupt, 1218 + .read_status = bcm54811_read_status, 1219 + .get_features = bcm5481x_read_abilities, 1563 1220 .suspend = bcm54xx_suspend, 1564 1221 .resume = bcm54xx_resume, 1565 1222 .link_change_notify = bcm54xx_link_change_notify,