From 744e458aebf8dc7f33eee9af61aeb0145de921a6 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 17 Feb 2019 10:28:33 +0100 Subject: [PATCH 1/4] net: phy: add helper linkmode_adv_to_mii_10gbt_adv_t Add a helper linkmode_adv_to_mii_10gbt_adv_t(), similar to linkmode_adv_to_mii_adv_t. Signed-off-by: Heiner Kallweit Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/linux/mdio.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/linux/mdio.h b/include/linux/mdio.h index bfa7114167d71..dd46828b4c476 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -261,6 +261,31 @@ static inline u16 ethtool_adv_to_mmd_eee_adv_t(u32 adv) return reg; } +/** + * linkmode_adv_to_mii_10gbt_adv_t + * @advertising: the linkmode advertisement settings + * + * A small helper function that translates linkmode advertisement + * settings to phy autonegotiation advertisements for the C45 + * 10GBASE-T AN CONTROL (7.32) register. + */ +static inline u32 linkmode_adv_to_mii_10gbt_adv_t(unsigned long *advertising) +{ + u32 result = 0; + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + advertising)) + result |= MDIO_AN_10GBT_CTRL_ADV2_5G; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, + advertising)) + result |= MDIO_AN_10GBT_CTRL_ADV5G; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + advertising)) + result |= MDIO_AN_10GBT_CTRL_ADV10G; + + return result; +} + int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); From 9a5dc8af441668a3db7fdcd927cb288be62c0a2e Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 17 Feb 2019 10:29:19 +0100 Subject: [PATCH 2/4] net: phy: add genphy_c45_an_config_aneg C45 configuration of 10/100 and multi-giga bit auto negotiation advertisement is standardized. Configuration of 1000Base-T however appears to be vendor specific. Move the generic code out of the Marvell driver into the common phy-c45.c file. v2: - change function name to genphy_c45_an_config_aneg Signed-off-by: Andrew Lunn [hkallweit1@gmail.com: use new helper linkmode_adv_to_mii_10gbt_adv_t and split patch] Signed-off-by: Heiner Kallweit Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy-c45.c | 44 +++++++++++++++++++++++++++++++++++++++ include/linux/phy.h | 1 + 2 files changed, 45 insertions(+) diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index 7af5fa81daf67..e17672bd180eb 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -74,6 +74,50 @@ int genphy_c45_pma_setup_forced(struct phy_device *phydev) } EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced); +/** + * genphy_c45_an_config_aneg - configure advertisement registers + * @phydev: target phy_device struct + * + * Configure advertisement registers based on modes set in phydev->advertising + * + * Returns negative errno code on failure, 0 if advertisement didn't change, + * or 1 if advertised modes changed. + */ +int genphy_c45_an_config_aneg(struct phy_device *phydev) +{ + int changed = 0, ret; + u32 adv; + + linkmode_and(phydev->advertising, phydev->advertising, + phydev->supported); + + adv = linkmode_adv_to_mii_adv_t(phydev->advertising); + + ret = phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_100BASE4 | + ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, + adv); + if (ret < 0) + return ret; + if (ret > 0) + changed = 1; + + adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising); + + ret = phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV10G | + MDIO_AN_10GBT_CTRL_ADV5G | + MDIO_AN_10GBT_CTRL_ADV2_5G, + adv); + if (ret < 0) + return ret; + if (ret > 0) + changed = 1; + + return changed; +} +EXPORT_SYMBOL_GPL(genphy_c45_an_config_aneg); + /** * genphy_c45_an_disable_aneg - disable auto-negotiation * @phydev: target phy_device struct diff --git a/include/linux/phy.h b/include/linux/phy.h index bf1070c2a53b4..3db507e68191f 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -1101,6 +1101,7 @@ int genphy_c45_read_link(struct phy_device *phydev); int genphy_c45_read_lpa(struct phy_device *phydev); int genphy_c45_read_pma(struct phy_device *phydev); int genphy_c45_pma_setup_forced(struct phy_device *phydev); +int genphy_c45_an_config_aneg(struct phy_device *phydev); int genphy_c45_an_disable_aneg(struct phy_device *phydev); int genphy_c45_read_mdix(struct phy_device *phydev); int genphy_c45_pma_read_abilities(struct phy_device *phydev); From 3de97f3c630850caf0fff67555bbe3f7ba61c308 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 17 Feb 2019 10:30:45 +0100 Subject: [PATCH 3/4] net: phy: marvell10g: use genphy_c45_an_config_aneg Use new function genphy_c45_config_aneg() in mv3310_config_aneg(). v2: - add a comment regarding 1000BaseT vendor registers v3: - rebased Signed-off-by: Andrew Lunn [hkallweit1@gmail.com: patch splitted] Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/phy/marvell10g.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 496805c0ddfea..8955740832cc0 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -279,18 +279,15 @@ static int mv3310_config_aneg(struct phy_device *phydev) return genphy_c45_an_disable_aneg(phydev); } - linkmode_and(phydev->advertising, phydev->advertising, - phydev->supported); - - ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, - ADVERTISE_ALL | ADVERTISE_100BASE4 | - ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, - linkmode_adv_to_mii_adv_t(phydev->advertising)); + ret = genphy_c45_an_config_aneg(phydev); if (ret < 0) return ret; if (ret > 0) changed = true; + /* Clause 45 has no standardized support for 1000BaseT, therefore + * use vendor registers for this mode. + */ reg = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MV_AN_CTRL1000, ADVERTISE_1000FULL | ADVERTISE_1000HALF, reg); @@ -299,20 +296,6 @@ static int mv3310_config_aneg(struct phy_device *phydev) if (ret > 0) changed = true; - /* 10G control register */ - if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, - phydev->advertising)) - reg = MDIO_AN_10GBT_CTRL_ADV10G; - else - reg = 0; - - ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, - MDIO_AN_10GBT_CTRL_ADV10G, reg); - if (ret < 0) - return ret; - if (ret > 0) - changed = true; - if (changed) ret = genphy_c45_restart_aneg(phydev); From 3ce2a027ae4e3ede876da6913641ad37c33b698b Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 17 Feb 2019 10:32:29 +0100 Subject: [PATCH 4/4] net: phy: marvell10g: check for newly set aneg Even if the advertisement registers content didn't change, we may have just switched to aneg, and therefore have to trigger an aneg restart. This matches the behavior of genphy_config_aneg(). Signed-off-by: Heiner Kallweit Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/marvell10g.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 8955740832cc0..b83eb19cf8bb9 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -296,6 +296,16 @@ static int mv3310_config_aneg(struct phy_device *phydev) if (ret > 0) changed = true; + if (!changed) { + /* Configure and restart aneg if it wasn't set before */ + ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); + if (ret < 0) + return ret; + + if (!(ret & MDIO_AN_CTRL1_ENABLE)) + changed = 1; + } + if (changed) ret = genphy_c45_restart_aneg(phydev);