From 3eef8689325e6358a0c51d2ef8c6a689b2f22d48 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 9 Aug 2019 20:43:04 +0200 Subject: [PATCH 1/4] net: phy: simplify genphy_config_advert by using the linkmode_adv_to_xxx_t functions Using linkmode_adv_to_mii_adv_t and linkmode_adv_to_mii_ctrl1000_t allows to simplify the code. In addition avoiding the conversion to the legacy u32 advertisement format allows to remove the warning. Signed-off-by: Heiner Kallweit Suggested-by: Andrew Lunn Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 7ddd91df99e3..a70a98dc9879 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1564,24 +1564,20 @@ EXPORT_SYMBOL(phy_reset_after_clk_enable); */ static int genphy_config_advert(struct phy_device *phydev) { - u32 advertise; - int bmsr, adv; - int err, changed = 0; + int err, bmsr, changed = 0; + u32 adv; /* Only allow advertising what this PHY supports */ linkmode_and(phydev->advertising, phydev->advertising, phydev->supported); - if (!ethtool_convert_link_mode_to_legacy_u32(&advertise, - phydev->advertising)) - phydev_warn(phydev, "PHY advertising (%*pb) more modes than genphy supports, some modes not advertised.\n", - __ETHTOOL_LINK_MODE_MASK_NBITS, - phydev->advertising); + + adv = linkmode_adv_to_mii_adv_t(phydev->advertising); /* Setup standard advertisement */ err = phy_modify_changed(phydev, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, - ethtool_adv_to_mii_adv_t(advertise)); + adv); if (err < 0) return err; if (err > 0) @@ -1598,13 +1594,7 @@ static int genphy_config_advert(struct phy_device *phydev) if (!(bmsr & BMSR_ESTATEN)) return changed; - /* Configure gigabit if it's supported */ - adv = 0; - if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, - phydev->supported) || - linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, - phydev->supported)) - adv = ethtool_adv_to_mii_ctrl1000_t(advertise); + adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); err = phy_modify_changed(phydev, MII_CTRL1000, ADVERTISE_1000FULL | ADVERTISE_1000HALF, From f4069cd7fa6583e7094001c6fce6f426d17a4c76 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 9 Aug 2019 20:43:50 +0200 Subject: [PATCH 2/4] net: phy: prepare phylib to deal with PHY's extending Clause 22 The integrated PHY in 2.5Gbps chip RTL8125 is the first (known to me) PHY that uses standard Clause 22 for all modes up to 1Gbps and adds 2.5Gbps control using vendor-specific registers. To use phylib for the standard part little extensions are needed: - Move most of genphy_config_aneg to a new function __genphy_config_aneg that takes a parameter whether restarting auto-negotiation is needed (depending on whether content of vendor-specific advertisement register changed). - Don't clear phydev->lp_advertising in genphy_read_status so that we can set non-C22 mode flags before. Basically both changes mimic the behavior of the equivalent Clause 45 functions. Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 27 ++++++++++++--------------- include/linux/phy.h | 8 +++++++- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index a70a98dc9879..b039632de73a 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1671,18 +1671,20 @@ int genphy_restart_aneg(struct phy_device *phydev) EXPORT_SYMBOL(genphy_restart_aneg); /** - * genphy_config_aneg - restart auto-negotiation or write BMCR + * __genphy_config_aneg - restart auto-negotiation or write BMCR * @phydev: target phy_device struct + * @changed: whether autoneg is requested * * Description: If auto-negotiation is enabled, we configure the * advertising, and then restart auto-negotiation. If it is not * enabled, then we write the BMCR. */ -int genphy_config_aneg(struct phy_device *phydev) +int __genphy_config_aneg(struct phy_device *phydev, bool changed) { - int err, changed; + int err; - changed = genphy_config_eee_advert(phydev); + if (genphy_config_eee_advert(phydev)) + changed = true; if (AUTONEG_ENABLE != phydev->autoneg) return genphy_setup_forced(phydev); @@ -1690,10 +1692,10 @@ int genphy_config_aneg(struct phy_device *phydev) err = genphy_config_advert(phydev); if (err < 0) /* error */ return err; + else if (err) + changed = true; - changed |= err; - - if (changed == 0) { + if (!changed) { /* Advertisement hasn't changed, but maybe aneg was never on to * begin with? Or maybe phy was isolated? */ @@ -1703,18 +1705,15 @@ int genphy_config_aneg(struct phy_device *phydev) return ctl; if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) - changed = 1; /* do restart aneg */ + changed = true; /* do restart aneg */ } /* Only restart aneg if we are advertising something different * than we were before. */ - if (changed > 0) - return genphy_restart_aneg(phydev); - - return 0; + return changed ? genphy_restart_aneg(phydev) : 0; } -EXPORT_SYMBOL(genphy_config_aneg); +EXPORT_SYMBOL(__genphy_config_aneg); /** * genphy_aneg_done - return auto-negotiation status @@ -1801,8 +1800,6 @@ int genphy_read_status(struct phy_device *phydev) phydev->pause = 0; phydev->asym_pause = 0; - linkmode_zero(phydev->lp_advertising); - if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { if (phydev->is_gigabit_capable) { lpagb = phy_read(phydev, MII_STAT1000); diff --git a/include/linux/phy.h b/include/linux/phy.h index 462b90b73f93..7117825ee57a 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -1069,7 +1069,7 @@ int genphy_read_abilities(struct phy_device *phydev); int genphy_setup_forced(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev); int genphy_config_eee_advert(struct phy_device *phydev); -int genphy_config_aneg(struct phy_device *phydev); +int __genphy_config_aneg(struct phy_device *phydev, bool changed); int genphy_aneg_done(struct phy_device *phydev); int genphy_update_link(struct phy_device *phydev); int genphy_read_status(struct phy_device *phydev); @@ -1077,6 +1077,12 @@ int genphy_suspend(struct phy_device *phydev); int genphy_resume(struct phy_device *phydev); int genphy_loopback(struct phy_device *phydev, bool enable); int genphy_soft_reset(struct phy_device *phydev); + +static inline int genphy_config_aneg(struct phy_device *phydev) +{ + return __genphy_config_aneg(phydev, false); +} + static inline int genphy_no_soft_reset(struct phy_device *phydev) { return 0; From bf22b343ca800aac076ccf986e762b28545aa6bb Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 9 Aug 2019 20:44:22 +0200 Subject: [PATCH 3/4] net: phy: add phy_modify_paged_changed Add helper function phy_modify_paged_changed, behavios is the same as for phy_modify_changed. Signed-off-by: Heiner Kallweit Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 31 +++++++++++++++++++++++++------ include/linux/phy.h | 2 ++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 16667fbac8bf..9ae3abb2daca 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -782,6 +782,29 @@ int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val) } EXPORT_SYMBOL(phy_write_paged); +/** + * phy_modify_paged_changed() - Function for modifying a paged register + * @phydev: a pointer to a &struct phy_device + * @page: the page for the phy + * @regnum: register number + * @mask: bit mask of bits to clear + * @set: bit mask of bits to set + * + * Returns negative errno, 0 if there was no change, and 1 in case of change + */ +int phy_modify_paged_changed(struct phy_device *phydev, int page, u32 regnum, + u16 mask, u16 set) +{ + int ret = 0, oldpage; + + oldpage = phy_select_page(phydev, page); + if (oldpage >= 0) + ret = __phy_modify_changed(phydev, regnum, mask, set); + + return phy_restore_page(phydev, oldpage, ret); +} +EXPORT_SYMBOL(phy_modify_paged_changed); + /** * phy_modify_paged() - Convenience function for modifying a paged register * @phydev: a pointer to a &struct phy_device @@ -795,12 +818,8 @@ EXPORT_SYMBOL(phy_write_paged); int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum, u16 mask, u16 set) { - int ret = 0, oldpage; + int ret = phy_modify_paged_changed(phydev, page, regnum, mask, set); - oldpage = phy_select_page(phydev, page); - if (oldpage >= 0) - ret = __phy_modify(phydev, regnum, mask, set); - - return phy_restore_page(phydev, oldpage, ret); + return ret < 0 ? ret : 0; } EXPORT_SYMBOL(phy_modify_paged); diff --git a/include/linux/phy.h b/include/linux/phy.h index 7117825ee57a..781f4810ceba 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -984,6 +984,8 @@ int phy_select_page(struct phy_device *phydev, int page); int phy_restore_page(struct phy_device *phydev, int oldpage, int ret); int phy_read_paged(struct phy_device *phydev, int page, u32 regnum); int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val); +int phy_modify_paged_changed(struct phy_device *phydev, int page, u32 regnum, + u16 mask, u16 set); int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum, u16 mask, u16 set); From 087f5b8758ae9f1b1968bc469bb3f5fae53e639b Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 9 Aug 2019 20:45:14 +0200 Subject: [PATCH 4/4] net: phy: realtek: add support for the 2.5Gbps PHY in RTL8125 This adds support for the integrated 2.5Gbps PHY in Realtek RTL8125. Advertisement of 2.5Gbps mode is done via a vendor-specific register. Same applies to reading NBase-T link partner advertisement. Unfortunately this 2.5Gbps PHY shares the PHY ID with the integrated 1Gbps PHY's in other Realtek network chips and so far no method is known to differentiate them. As a workaround use a dedicated fake PHY ID that is set by the network driver by intercepting the MDIO PHY ID read. v2: - Create dedicated PHY driver and use a fake PHY ID that is injected by the network driver. Suggested by Andrew Lunn. Signed-off-by: Heiner Kallweit Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/realtek.c | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index a669945eb829..5b466e80d956 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -39,6 +39,11 @@ #define RTL8366RB_POWER_SAVE 0x15 #define RTL8366RB_POWER_SAVE_ON BIT(12) +#define RTL_ADV_2500FULL BIT(7) +#define RTL_LPADV_10000FULL BIT(11) +#define RTL_LPADV_5000FULL BIT(6) +#define RTL_LPADV_2500FULL BIT(5) + MODULE_DESCRIPTION("Realtek PHY driver"); MODULE_AUTHOR("Johnson Leung"); MODULE_LICENSE("GPL"); @@ -256,6 +261,53 @@ static int rtl8366rb_config_init(struct phy_device *phydev) return ret; } +static int rtl8125_get_features(struct phy_device *phydev) +{ + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->supported); + + return genphy_read_abilities(phydev); +} + +static int rtl8125_config_aneg(struct phy_device *phydev) +{ + int ret = 0; + + if (phydev->autoneg == AUTONEG_ENABLE) { + u16 adv2500 = 0; + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->advertising)) + adv2500 = RTL_ADV_2500FULL; + + ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12, + RTL_ADV_2500FULL, adv2500); + if (ret < 0) + return ret; + } + + return __genphy_config_aneg(phydev, ret); +} + +static int rtl8125_read_status(struct phy_device *phydev) +{ + if (phydev->autoneg == AUTONEG_ENABLE) { + int lpadv = phy_read_paged(phydev, 0xa5d, 0x13); + + if (lpadv < 0) + return lpadv; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + phydev->lp_advertising, lpadv & RTL_LPADV_10000FULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, + phydev->lp_advertising, lpadv & RTL_LPADV_5000FULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL); + } + + return genphy_read_status(phydev); +} + static struct phy_driver realtek_drvs[] = { { PHY_ID_MATCH_EXACT(0x00008201), @@ -332,6 +384,16 @@ static struct phy_driver realtek_drvs[] = { .resume = genphy_resume, .read_page = rtl821x_read_page, .write_page = rtl821x_write_page, + }, { + PHY_ID_MATCH_EXACT(0x001cca50), + .name = "RTL8125 2.5Gbps internal", + .get_features = rtl8125_get_features, + .config_aneg = rtl8125_config_aneg, + .read_status = rtl8125_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, }, { PHY_ID_MATCH_EXACT(0x001cc961), .name = "RTL8366RB Gigabit Ethernet",