| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From fa6e98cee558622565c97924e922b97340aeabd8 Mon Sep 17 00:00:00 2001 |
| 2 | From: Heiner Kallweit <hkallweit1@gmail.com> |
| 3 | Date: Tue, 22 Oct 2019 11:31:07 -0700 |
| 4 | Subject: [PATCH] net: phy: add support for clause 37 auto-negotiation |
| 5 | MIME-Version: 1.0 |
| 6 | Content-Type: text/plain; charset=UTF-8 |
| 7 | Content-Transfer-Encoding: 8bit |
| 8 | |
| 9 | This patch adds support for clause 37 1000Base-X auto-negotiation. |
| 10 | |
| 11 | Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> |
| 12 | Signed-off-by: Tao Ren <taoren@fb.com> |
| 13 | Tested-by: René van Dorst <opensource@vdorst.com> |
| 14 | Reviewed-by: Andrew Lunn <andrew@lunn.ch> |
| 15 | Signed-off-by: David S. Miller <davem@davemloft.net> |
| 16 | --- |
| 17 | drivers/net/phy/phy_device.c | 139 +++++++++++++++++++++++++++++++++++ |
| 18 | include/linux/phy.h | 4 + |
| 19 | 2 files changed, 143 insertions(+) |
| 20 | |
| 21 | --- a/drivers/net/phy/phy_device.c |
| 22 | +++ b/drivers/net/phy/phy_device.c |
| 23 | @@ -1683,6 +1683,40 @@ static int genphy_config_advert(struct p |
| 24 | } |
| 25 | |
| 26 | /** |
| 27 | + * genphy_c37_config_advert - sanitize and advertise auto-negotiation parameters |
| 28 | + * @phydev: target phy_device struct |
| 29 | + * |
| 30 | + * Description: Writes MII_ADVERTISE with the appropriate values, |
| 31 | + * after sanitizing the values to make sure we only advertise |
| 32 | + * what is supported. Returns < 0 on error, 0 if the PHY's advertisement |
| 33 | + * hasn't changed, and > 0 if it has changed. This function is intended |
| 34 | + * for Clause 37 1000Base-X mode. |
| 35 | + */ |
| 36 | +static int genphy_c37_config_advert(struct phy_device *phydev) |
| 37 | +{ |
| 38 | + u16 adv = 0; |
| 39 | + |
| 40 | + /* Only allow advertising what this PHY supports */ |
| 41 | + linkmode_and(phydev->advertising, phydev->advertising, |
| 42 | + phydev->supported); |
| 43 | + |
| 44 | + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, |
| 45 | + phydev->advertising)) |
| 46 | + adv |= ADVERTISE_1000XFULL; |
| 47 | + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, |
| 48 | + phydev->advertising)) |
| 49 | + adv |= ADVERTISE_1000XPAUSE; |
| 50 | + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
| 51 | + phydev->advertising)) |
| 52 | + adv |= ADVERTISE_1000XPSE_ASYM; |
| 53 | + |
| 54 | + return phy_modify_changed(phydev, MII_ADVERTISE, |
| 55 | + ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE | |
| 56 | + ADVERTISE_1000XHALF | ADVERTISE_1000XPSE_ASYM, |
| 57 | + adv); |
| 58 | +} |
| 59 | + |
| 60 | +/** |
| 61 | * genphy_config_eee_advert - disable unwanted eee mode advertisement |
| 62 | * @phydev: target phy_device struct |
| 63 | * |
| 64 | @@ -1791,6 +1825,54 @@ int __genphy_config_aneg(struct phy_devi |
| 65 | EXPORT_SYMBOL(__genphy_config_aneg); |
| 66 | |
| 67 | /** |
| 68 | + * genphy_c37_config_aneg - restart auto-negotiation or write BMCR |
| 69 | + * @phydev: target phy_device struct |
| 70 | + * |
| 71 | + * Description: If auto-negotiation is enabled, we configure the |
| 72 | + * advertising, and then restart auto-negotiation. If it is not |
| 73 | + * enabled, then we write the BMCR. This function is intended |
| 74 | + * for use with Clause 37 1000Base-X mode. |
| 75 | + */ |
| 76 | +int genphy_c37_config_aneg(struct phy_device *phydev) |
| 77 | +{ |
| 78 | + int err, changed; |
| 79 | + |
| 80 | + if (phydev->autoneg != AUTONEG_ENABLE) |
| 81 | + return genphy_setup_forced(phydev); |
| 82 | + |
| 83 | + err = phy_modify(phydev, MII_BMCR, BMCR_SPEED1000 | BMCR_SPEED100, |
| 84 | + BMCR_SPEED1000); |
| 85 | + if (err) |
| 86 | + return err; |
| 87 | + |
| 88 | + changed = genphy_c37_config_advert(phydev); |
| 89 | + if (changed < 0) /* error */ |
| 90 | + return changed; |
| 91 | + |
| 92 | + if (!changed) { |
| 93 | + /* Advertisement hasn't changed, but maybe aneg was never on to |
| 94 | + * begin with? Or maybe phy was isolated? |
| 95 | + */ |
| 96 | + int ctl = phy_read(phydev, MII_BMCR); |
| 97 | + |
| 98 | + if (ctl < 0) |
| 99 | + return ctl; |
| 100 | + |
| 101 | + if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) |
| 102 | + changed = 1; /* do restart aneg */ |
| 103 | + } |
| 104 | + |
| 105 | + /* Only restart aneg if we are advertising something different |
| 106 | + * than we were before. |
| 107 | + */ |
| 108 | + if (changed > 0) |
| 109 | + return genphy_restart_aneg(phydev); |
| 110 | + |
| 111 | + return 0; |
| 112 | +} |
| 113 | +EXPORT_SYMBOL(genphy_c37_config_aneg); |
| 114 | + |
| 115 | +/** |
| 116 | * genphy_aneg_done - return auto-negotiation status |
| 117 | * @phydev: target phy_device struct |
| 118 | * |
| 119 | @@ -1963,6 +2045,63 @@ int genphy_read_status(struct phy_device |
| 120 | EXPORT_SYMBOL(genphy_read_status); |
| 121 | |
| 122 | /** |
| 123 | + * genphy_c37_read_status - check the link status and update current link state |
| 124 | + * @phydev: target phy_device struct |
| 125 | + * |
| 126 | + * Description: Check the link, then figure out the current state |
| 127 | + * by comparing what we advertise with what the link partner |
| 128 | + * advertises. This function is for Clause 37 1000Base-X mode. |
| 129 | + */ |
| 130 | +int genphy_c37_read_status(struct phy_device *phydev) |
| 131 | +{ |
| 132 | + int lpa, err, old_link = phydev->link; |
| 133 | + |
| 134 | + /* Update the link, but return if there was an error */ |
| 135 | + err = genphy_update_link(phydev); |
| 136 | + if (err) |
| 137 | + return err; |
| 138 | + |
| 139 | + /* why bother the PHY if nothing can have changed */ |
| 140 | + if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) |
| 141 | + return 0; |
| 142 | + |
| 143 | + phydev->duplex = DUPLEX_UNKNOWN; |
| 144 | + phydev->pause = 0; |
| 145 | + phydev->asym_pause = 0; |
| 146 | + |
| 147 | + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { |
| 148 | + lpa = phy_read(phydev, MII_LPA); |
| 149 | + if (lpa < 0) |
| 150 | + return lpa; |
| 151 | + |
| 152 | + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, |
| 153 | + phydev->lp_advertising, lpa & LPA_LPACK); |
| 154 | + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, |
| 155 | + phydev->lp_advertising, lpa & LPA_1000XFULL); |
| 156 | + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, |
| 157 | + phydev->lp_advertising, lpa & LPA_1000XPAUSE); |
| 158 | + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, |
| 159 | + phydev->lp_advertising, |
| 160 | + lpa & LPA_1000XPAUSE_ASYM); |
| 161 | + |
| 162 | + phy_resolve_aneg_linkmode(phydev); |
| 163 | + } else if (phydev->autoneg == AUTONEG_DISABLE) { |
| 164 | + int bmcr = phy_read(phydev, MII_BMCR); |
| 165 | + |
| 166 | + if (bmcr < 0) |
| 167 | + return bmcr; |
| 168 | + |
| 169 | + if (bmcr & BMCR_FULLDPLX) |
| 170 | + phydev->duplex = DUPLEX_FULL; |
| 171 | + else |
| 172 | + phydev->duplex = DUPLEX_HALF; |
| 173 | + } |
| 174 | + |
| 175 | + return 0; |
| 176 | +} |
| 177 | +EXPORT_SYMBOL(genphy_c37_read_status); |
| 178 | + |
| 179 | +/** |
| 180 | * genphy_soft_reset - software reset the PHY via BMCR_RESET bit |
| 181 | * @phydev: target phy_device struct |
| 182 | * |
| 183 | --- a/include/linux/phy.h |
| 184 | +++ b/include/linux/phy.h |
| 185 | @@ -1120,6 +1120,10 @@ int genphy_read_mmd_unsupported(struct p |
| 186 | int genphy_write_mmd_unsupported(struct phy_device *phdev, int devnum, |
| 187 | u16 regnum, u16 val); |
| 188 | |
| 189 | +/* Clause 37 */ |
| 190 | +int genphy_c37_config_aneg(struct phy_device *phydev); |
| 191 | +int genphy_c37_read_status(struct phy_device *phydev); |
| 192 | + |
| 193 | /* Clause 45 PHY */ |
| 194 | int genphy_c45_restart_aneg(struct phy_device *phydev); |
| 195 | int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart); |