diff options
author | David S. Miller <davem@davemloft.net> | 2023-02-13 11:12:31 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2023-02-13 11:12:31 +0000 |
commit | 9b0bf4f77162e5423bc08aff7be0cb80134a2884 (patch) | |
tree | d61fefc26cd17e3c78ee29f75c641422c67e8242 | |
parent | 79cdf17e5131ccdee0792f6f25d3db0e34861998 (diff) | |
parent | 8b68710a3121e0475b123a20c4220f66a728770e (diff) |
Merge branch 'ksz9477-eee-support'
Oleksij Rempel says:
====================
net: add EEE support for KSZ9477 switch family
changes v8:
- fix comment for linkmode_to_mii_eee_cap1_t() function
- add Acked-by: Arun Ramadoss <arun.ramadoss@microchip.com>
- add Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
changes v7:
- update documentation for genphy_c45_eee_is_active()
- address review comments on "net: dsa: microchip: enable EEE support"
patch
changes v6:
- split patch set and send only first 9 patches
- Add Reviewed-by: Andrew Lunn <andrew@lunn.ch>
- use 0xffff instead of GENMASK
- Document @supported_eee
- use "()" with function name in comments
changes v5:
- spell fixes
- move part of genphy_c45_read_eee_abilities() to
genphy_c45_read_eee_cap1()
- validate MDIO_PCS_EEE_ABLE register against 0xffff val.
- rename *eee_100_10000* to *eee_cap1*
- use linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)
instead of !linkmode_empty()
- add documentation to linkmode/register helpers
changes v4:
- remove following helpers:
mmd_eee_cap_to_ethtool_sup_t
mmd_eee_adv_to_ethtool_adv_t
ethtool_adv_to_mmd_eee_adv_t
and port drivers from this helpers to linkmode helpers.
- rebase against latest net-next
- port phy_init_eee() to genphy_c45_eee_is_active()
changes v3:
- rework some parts of EEE infrastructure and move it to c45 code.
- add supported_eee storage and start using it in EEE code and by the
micrel driver.
- add EEE support for ar8035 PHY
- add SmartEEE support to FEC i.MX series.
changes v2:
- use phydev->supported instead of reading MII_BMSR regiaster
- fix @get_eee > @set_eee
With this patch series we provide EEE control for KSZ9477 family of
switches and
AR8035 with i.MX6 configuration.
According to my tests, on a system with KSZ8563 switch and 100Mbit idle
link,
we consume 0,192W less power per port if EEE is enabled.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 66 | ||||
-rw-r--r-- | drivers/net/phy/micrel.c | 21 | ||||
-rw-r--r-- | drivers/net/phy/phy-c45.c | 319 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 153 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 26 | ||||
-rw-r--r-- | include/linux/mdio.h | 84 | ||||
-rw-r--r-- | include/linux/phy.h | 14 | ||||
-rw-r--r-- | include/uapi/linux/mdio.h | 8 |
8 files changed, 554 insertions, 137 deletions
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 46becc0382d6..729b36eeb2c4 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -2673,6 +2673,70 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port) return -EOPNOTSUPP; } +static int ksz_validate_eee(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->info->internal_phy[port]) + return -EOPNOTSUPP; + + switch (dev->chip_id) { + case KSZ8563_CHIP_ID: + case KSZ9477_CHIP_ID: + case KSZ9563_CHIP_ID: + case KSZ9567_CHIP_ID: + case KSZ9893_CHIP_ID: + case KSZ9896_CHIP_ID: + case KSZ9897_CHIP_ID: + return 0; + } + + return -EOPNOTSUPP; +} + +static int ksz_get_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + int ret; + + ret = ksz_validate_eee(ds, port); + if (ret) + return ret; + + /* There is no documented control of Tx LPI configuration. */ + e->tx_lpi_enabled = true; + + /* There is no documented control of Tx LPI timer. According to tests + * Tx LPI timer seems to be set by default to minimal value. + */ + e->tx_lpi_timer = 0; + + return 0; +} + +static int ksz_set_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + struct ksz_device *dev = ds->priv; + int ret; + + ret = ksz_validate_eee(ds, port); + if (ret) + return ret; + + if (!e->tx_lpi_enabled) { + dev_err(dev->dev, "Disabling EEE Tx LPI is not supported\n"); + return -EINVAL; + } + + if (e->tx_lpi_timer) { + dev_err(dev->dev, "Setting EEE Tx LPI timer is not supported\n"); + return -EINVAL; + } + + return 0; +} + static void ksz_set_xmii(struct ksz_device *dev, int port, phy_interface_t interface) { @@ -3130,6 +3194,8 @@ static const struct dsa_switch_ops ksz_switch_ops = { .port_txtstamp = ksz_port_txtstamp, .port_rxtstamp = ksz_port_rxtstamp, .port_setup_tc = ksz_setup_tc, + .get_mac_eee = ksz_get_mac_eee, + .set_mac_eee = ksz_set_mac_eee, }; struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 330c76328da0..2c84fccef4f6 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -1392,6 +1392,26 @@ static int ksz9131_config_aneg(struct phy_device *phydev) return genphy_config_aneg(phydev); } +static int ksz9477_get_features(struct phy_device *phydev) +{ + int ret; + + ret = genphy_read_abilities(phydev); + if (ret) + return ret; + + /* The "EEE control and capability 1" (Register 3.20) seems to be + * influenced by the "EEE advertisement 1" (Register 7.60). Changes + * on the 7.60 will affect 3.20. So, we need to construct our own list + * of caps. + * KSZ8563R should have 100BaseTX/Full only. + */ + linkmode_and(phydev->supported_eee, phydev->supported, + PHY_EEE_CAP1_FEATURES); + + return 0; +} + #define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6) #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4) @@ -4172,6 +4192,7 @@ static struct phy_driver ksphy_driver[] = { .handle_interrupt = kszphy_handle_interrupt, .suspend = genphy_suspend, .resume = genphy_resume, + .get_features = ksz9477_get_features, } }; module_phy_driver(ksphy_driver); diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index 9f9565a4819d..ef36582adbeb 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -262,7 +262,11 @@ int genphy_c45_an_config_aneg(struct phy_device *phydev) linkmode_and(phydev->advertising, phydev->advertising, phydev->supported); - changed = genphy_config_eee_advert(phydev); + ret = genphy_c45_write_eee_adv(phydev, phydev->supported_eee); + if (ret < 0) + return ret; + else if (ret) + changed = true; if (genphy_c45_baset1_able(phydev)) return genphy_c45_baset1_an_config_aneg(phydev); @@ -662,6 +666,199 @@ int genphy_c45_read_mdix(struct phy_device *phydev) EXPORT_SYMBOL_GPL(genphy_c45_read_mdix); /** + * genphy_c45_write_eee_adv - write advertised EEE link modes + * @phydev: target phy_device struct + * @adv: the linkmode advertisement settings + */ +int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv) +{ + int val, changed; + + if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) { + val = linkmode_to_mii_eee_cap1_t(adv); + + /* In eee_broken_modes are stored MDIO_AN_EEE_ADV specific raw + * register values. + */ + val &= ~phydev->eee_broken_modes; + + /* IEEE 802.3-2018 45.2.7.13 EEE advertisement 1 + * (Register 7.60) + */ + val = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, + MDIO_AN_EEE_ADV, + MDIO_EEE_100TX | MDIO_EEE_1000T | + MDIO_EEE_10GT | MDIO_EEE_1000KX | + MDIO_EEE_10GKX4 | MDIO_EEE_10GKR, + val); + if (val < 0) + return val; + if (val > 0) + changed = 1; + } + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, + phydev->supported_eee)) { + val = linkmode_adv_to_mii_10base_t1_t(adv); + /* IEEE 802.3cg-2019 45.2.7.25 10BASE-T1 AN control register + * (Register 7.526) + */ + val = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, + MDIO_AN_10BT1_AN_CTRL, + MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L, + val); + if (val < 0) + return val; + if (val > 0) + changed = 1; + } + + return changed; +} + +/** + * genphy_c45_read_eee_adv - read advertised EEE link modes + * @phydev: target phy_device struct + * @adv: the linkmode advertisement status + */ +static int genphy_c45_read_eee_adv(struct phy_device *phydev, + unsigned long *adv) +{ + int val; + + if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) { + /* IEEE 802.3-2018 45.2.7.13 EEE advertisement 1 + * (Register 7.60) + */ + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); + if (val < 0) + return val; + + mii_eee_cap1_mod_linkmode_t(adv, val); + } + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, + phydev->supported_eee)) { + /* IEEE 802.3cg-2019 45.2.7.25 10BASE-T1 AN control register + * (Register 7.526) + */ + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10BT1_AN_CTRL); + if (val < 0) + return val; + + mii_10base_t1_adv_mod_linkmode_t(adv, val); + } + + return 0; +} + +/** + * genphy_c45_read_eee_lpa - read advertised LP EEE link modes + * @phydev: target phy_device struct + * @lpa: the linkmode LP advertisement status + */ +static int genphy_c45_read_eee_lpa(struct phy_device *phydev, + unsigned long *lpa) +{ + int val; + + if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) { + /* IEEE 802.3-2018 45.2.7.14 EEE link partner ability 1 + * (Register 7.61) + */ + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); + if (val < 0) + return val; + + mii_eee_cap1_mod_linkmode_t(lpa, val); + } + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, + phydev->supported_eee)) { + /* IEEE 802.3cg-2019 45.2.7.26 10BASE-T1 AN status register + * (Register 7.527) + */ + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10BT1_AN_STAT); + if (val < 0) + return val; + + mii_10base_t1_adv_mod_linkmode_t(lpa, val); + } + + return 0; +} + +/** + * genphy_c45_read_eee_cap1 - read supported EEE link modes from register 3.20 + * @phydev: target phy_device struct + */ +static int genphy_c45_read_eee_cap1(struct phy_device *phydev) +{ + int val; + + /* IEEE 802.3-2018 45.2.3.10 EEE control and capability 1 + * (Register 3.20) + */ + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); + if (val < 0) + return val; + + /* The 802.3 2018 standard says the top 2 bits are reserved and should + * read as 0. Also, it seems unlikely anybody will build a PHY which + * supports 100GBASE-R deep sleep all the way down to 100BASE-TX EEE. + * If MDIO_PCS_EEE_ABLE is 0xffff assume EEE is not supported. + */ + if (val == 0xffff) + return 0; + + mii_eee_cap1_mod_linkmode_t(phydev->supported_eee, val); + + /* Some buggy devices indicate EEE link modes in MDIO_PCS_EEE_ABLE + * which they don't support as indicated by BMSR, ESTATUS etc. + */ + linkmode_and(phydev->supported_eee, phydev->supported_eee, + phydev->supported); + + return 0; +} + +/** + * genphy_c45_read_eee_abilities - read supported EEE link modes + * @phydev: target phy_device struct + */ +int genphy_c45_read_eee_abilities(struct phy_device *phydev) +{ + int val; + + /* There is not indicator whether optional register + * "EEE control and capability 1" (3.20) is supported. Read it only + * on devices with appropriate linkmodes. + */ + if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) { + val = genphy_c45_read_eee_cap1(phydev); + if (val) + return val; + } + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, + phydev->supported)) { + /* IEEE 802.3cg-2019 45.2.1.186b 10BASE-T1L PMA status register + * (Register 1.2295) + */ + val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10T1L_STAT); + if (val < 0) + return val; + + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, + phydev->supported_eee, + val & MDIO_PMA_10T1L_STAT_EEE); + } + + return 0; +} +EXPORT_SYMBOL_GPL(genphy_c45_read_eee_abilities); + +/** * genphy_c45_pma_read_abilities - read supported link modes from PMA * @phydev: target phy_device struct * @@ -775,6 +972,11 @@ int genphy_c45_pma_read_abilities(struct phy_device *phydev) } } + /* This is optional functionality. If not supported, we may get an error + * which should be ignored. + */ + genphy_c45_read_eee_abilities(phydev); + return 0; } EXPORT_SYMBOL_GPL(genphy_c45_pma_read_abilities); @@ -1124,6 +1326,121 @@ int genphy_c45_plca_get_status(struct phy_device *phydev, } EXPORT_SYMBOL_GPL(genphy_c45_plca_get_status); +/** + * genphy_c45_eee_is_active - get EEE status + * @phydev: target phy_device struct + * @adv: variable to store advertised linkmodes + * @lp: variable to store LP advertised linkmodes + * @is_enabled: variable to store EEE enabled/disabled configuration value + * + * Description: this function will read local and link partner PHY + * advertisements. Compare them return current EEE state. + */ +int genphy_c45_eee_is_active(struct phy_device *phydev, unsigned long *adv, + unsigned long *lp, bool *is_enabled) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_adv) = {}; + __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_lp) = {}; + __ETHTOOL_DECLARE_LINK_MODE_MASK(common); + bool eee_enabled, eee_active; + int ret; + + ret = genphy_c45_read_eee_adv(phydev, tmp_adv); + if (ret) + return ret; + + ret = genphy_c45_read_eee_lpa(phydev, tmp_lp); + if (ret) + return ret; + + eee_enabled = !linkmode_empty(tmp_adv); + linkmode_and(common, tmp_adv, tmp_lp); + if (eee_enabled && !linkmode_empty(common)) + eee_active = phy_check_valid(phydev->speed, phydev->duplex, + common); + else + eee_active = false; + + if (adv) + linkmode_copy(adv, tmp_adv); + if (lp) + linkmode_copy(lp, tmp_lp); + if (is_enabled) + *is_enabled = eee_enabled; + + return eee_active; +} +EXPORT_SYMBOL(genphy_c45_eee_is_active); + +/** + * genphy_c45_ethtool_get_eee - get EEE supported and status + * @phydev: target phy_device struct + * @data: ethtool_eee data + * + * Description: it reports the Supported/Advertisement/LP Advertisement + * capabilities. + */ +int genphy_c45_ethtool_get_eee(struct phy_device *phydev, + struct ethtool_eee *data) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {}; + __ETHTOOL_DECLARE_LINK_MODE_MASK(lp) = {}; + bool overflow = false, is_enabled; + int ret; + + ret = genphy_c45_eee_is_active(phydev, adv, lp, &is_enabled); + if (ret < 0) + return ret; + + data->eee_enabled = is_enabled; + data->eee_active = ret; + + if (!ethtool_convert_link_mode_to_legacy_u32(&data->supported, + phydev->supported_eee)) + overflow = true; + if (!ethtool_convert_link_mode_to_legacy_u32(&data->advertised, adv)) + overflow = true; + if (!ethtool_convert_link_mode_to_legacy_u32(&data->lp_advertised, lp)) + overflow = true; + + if (overflow) + phydev_warn(phydev, "Not all supported or advertised EEE link modes were passed to the user space\n"); + + return 0; +} +EXPORT_SYMBOL(genphy_c45_ethtool_get_eee); + +/** + * genphy_c45_ethtool_set_eee - get EEE supported and status + * @phydev: target phy_device struct + * @data: ethtool_eee data + * + * Description: it reportes the Supported/Advertisement/LP Advertisement + * capabilities. + */ +int genphy_c45_ethtool_set_eee(struct phy_device *phydev, + struct ethtool_eee *data) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {}; + int ret; + + if (data->eee_enabled) { + if (data->advertised) + adv[0] = data->advertised; + else + linkmode_copy(adv, phydev->supported_eee); + } + + ret = genphy_c45_write_eee_adv(phydev, adv); + if (ret < 0) + return ret; + if (ret > 0) + return phy_restart_aneg(phydev); + + return 0; +} +EXPORT_SYMBOL(genphy_c45_ethtool_set_eee); + struct phy_driver genphy_c45_driver = { .phy_id = 0xffffffff, .phy_id_mask = 0xffffffff, diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 3378ca4f49b6..2f1041a7211e 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -242,11 +242,11 @@ unsigned int phy_supported_speeds(struct phy_device *phy, * * Description: Returns true if there is a valid setting, false otherwise. */ -static inline bool phy_check_valid(int speed, int duplex, - unsigned long *features) +bool phy_check_valid(int speed, int duplex, unsigned long *features) { return !!phy_lookup_setting(speed, duplex, features, true); } +EXPORT_SYMBOL(phy_check_valid); /** * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex @@ -1457,30 +1457,6 @@ void phy_mac_interrupt(struct phy_device *phydev) } EXPORT_SYMBOL(phy_mac_interrupt); -static void mmd_eee_adv_to_linkmode(unsigned long *advertising, u16 eee_adv) -{ - linkmode_zero(advertising); - - if (eee_adv & MDIO_EEE_100TX) - linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, - advertising); - if (eee_adv & MDIO_EEE_1000T) - linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, - advertising); - if (eee_adv & MDIO_EEE_10GT) - linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, - advertising); - if (eee_adv & MDIO_EEE_1000KX) - linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, - advertising); - if (eee_adv & MDIO_EEE_10GKX4) - linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, - advertising); - if (eee_adv & MDIO_EEE_10GKR) - linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, - advertising); -} - /** * phy_init_eee - init and check the EEE feature * @phydev: target phy_device struct @@ -1493,62 +1469,25 @@ static void mmd_eee_adv_to_linkmode(unsigned long *advertising, u16 eee_adv) */ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) { + int ret; + if (!phydev->drv) return -EIO; - /* According to 802.3az,the EEE is supported only in full duplex-mode. - */ - if (phydev->duplex == DUPLEX_FULL) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(common); - __ETHTOOL_DECLARE_LINK_MODE_MASK(lp); - __ETHTOOL_DECLARE_LINK_MODE_MASK(adv); - int eee_lp, eee_cap, eee_adv; - int status; - u32 cap; - - /* Read phy status to properly get the right settings */ - status = phy_read_status(phydev); - if (status) - return status; - - /* First check if the EEE ability is supported */ - eee_cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); - if (eee_cap <= 0) - goto eee_exit_err; - - cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap); - if (!cap) - goto eee_exit_err; - - /* Check which link settings negotiated and verify it in - * the EEE advertising registers. - */ - eee_lp = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); - if (eee_lp <= 0) - goto eee_exit_err; - - eee_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); - if (eee_adv <= 0) - goto eee_exit_err; - - mmd_eee_adv_to_linkmode(adv, eee_adv); - mmd_eee_adv_to_linkmode(lp, eee_lp); - linkmode_and(common, adv, lp); - - if (!phy_check_valid(phydev->speed, phydev->duplex, common)) - goto eee_exit_err; + ret = genphy_c45_eee_is_active(phydev, NULL, NULL, NULL); + if (ret < 0) + return ret; + if (!ret) + return -EPROTONOSUPPORT; - if (clk_stop_enable) - /* Configure the PHY to stop receiving xMII - * clock while it is signaling LPI. - */ - phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, - MDIO_PCS_CTRL1_CLKSTOP_EN); + if (clk_stop_enable) + /* Configure the PHY to stop receiving xMII + * clock while it is signaling LPI. + */ + ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, + MDIO_PCS_CTRL1_CLKSTOP_EN); - return 0; /* EEE supported */ - } -eee_exit_err: - return -EPROTONOSUPPORT; + return ret < 0 ? ret : 0; } EXPORT_SYMBOL(phy_init_eee); @@ -1578,33 +1517,10 @@ EXPORT_SYMBOL(phy_get_eee_err); */ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) { - int val; - if (!phydev->drv) return -EIO; - /* Get Supported EEE */ - val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); - if (val < 0) - return val; - data->supported = mmd_eee_cap_to_ethtool_sup_t(val); - - /* Get advertisement EEE */ - val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); - if (val < 0) - return val; - data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); - data->eee_enabled = !!data->advertised; - - /* Get LP advertisement EEE */ - val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); - if (val < 0) - return val; - data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); - - data->eee_active = !!(data->advertised & data->lp_advertised); - - return 0; + return genphy_c45_ethtool_get_eee(phydev, data); } EXPORT_SYMBOL(phy_ethtool_get_eee); @@ -1617,43 +1533,10 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); */ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) { - int cap, old_adv, adv = 0, ret; - if (!phydev->drv) return -EIO; - /* Get Supported EEE */ - cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); - if (cap < 0) - return cap; - - old_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); - if (old_adv < 0) - return old_adv; - - if (data->eee_enabled) { - adv = !data->advertised ? cap : - ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap; - /* Mask prohibited EEE modes */ - adv &= ~phydev->eee_broken_modes; - } - - if (old_adv != adv) { - ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); - if (ret < 0) - return ret; - - /* Restart autonegotiation so the new modes get sent to the - * link partner. - */ - if (phydev->autoneg == AUTONEG_ENABLE) { - ret = phy_restart_aneg(phydev); - if (ret < 0) - return ret; - } - } - - return 0; + return genphy_c45_ethtool_set_eee(phydev, data); } EXPORT_SYMBOL(phy_ethtool_set_eee); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index a3917c7acbd3..8d927c5e3bf8 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -132,6 +132,18 @@ static const int phy_10gbit_full_features_array[] = { ETHTOOL_LINK_MODE_10000baseT_Full_BIT, }; +static const int phy_eee_cap1_features_array[] = { + ETHTOOL_LINK_MODE_100baseT_Full_BIT, + ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, +}; + +__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap1_features) __ro_after_init; +EXPORT_SYMBOL_GPL(phy_eee_cap1_features); + static void features_init(void) { /* 10/100 half/full*/ @@ -213,6 +225,10 @@ static void features_init(void) linkmode_set_bit_array(phy_10gbit_fec_features_array, ARRAY_SIZE(phy_10gbit_fec_features_array), phy_10gbit_fec_features); + linkmode_set_bit_array(phy_eee_cap1_features_array, + ARRAY_SIZE(phy_eee_cap1_features_array), + phy_eee_cap1_features); + } void phy_device_free(struct phy_device *phydev) @@ -2215,7 +2231,10 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed) { int err; - if (genphy_config_eee_advert(phydev)) + err = genphy_c45_write_eee_adv(phydev, phydev->supported_eee); + if (err < 0) + return err; + else if (err) changed = true; err = genphy_setup_master_slave(phydev); @@ -2637,6 +2656,11 @@ int genphy_read_abilities(struct phy_device *phydev) phydev->supported, val & ESTATUS_1000_XFULL); } + /* This is optional functionality. If not supported, we may get an error + * which should be ignored. + */ + genphy_c45_read_eee_abilities(phydev); + return 0; } EXPORT_SYMBOL(genphy_read_abilities); diff --git a/include/linux/mdio.h b/include/linux/mdio.h index c0da30d63b1d..27013d6bf24a 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -402,6 +402,90 @@ static inline u32 linkmode_adv_to_mii_t1_adv_m_t(unsigned long *advertising) return result; } +/** + * mii_eee_cap1_mod_linkmode_t() + * @adv: target the linkmode advertisement settings + * @val: register value + * + * A function that translates value of following registers to the linkmode: + * IEEE 802.3-2018 45.2.3.10 "EEE control and capability 1" register (3.20) + * IEEE 802.3-2018 45.2.7.13 "EEE advertisement 1" register (7.60) + * IEEE 802.3-2018 45.2.7.14 "EEE "link partner ability 1 register (7.61) + */ +static inline void mii_eee_cap1_mod_linkmode_t(unsigned long *adv, u32 val) +{ + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, + adv, val & MDIO_EEE_100TX); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + adv, val & MDIO_EEE_1000T); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + adv, val & MDIO_EEE_10GT); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + adv, val & MDIO_EEE_1000KX); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + adv, val & MDIO_EEE_10GKX4); + linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + adv, val & MDIO_EEE_10GKR); +} + +/** + * linkmode_to_mii_eee_cap1_t() + * @adv: the linkmode advertisement settings + * + * A function that translates linkmode to value for IEEE 802.3-2018 45.2.7.13 + * "EEE advertisement 1" register (7.60) + */ +static inline u32 linkmode_to_mii_eee_cap1_t(unsigned long *adv) +{ + u32 result = 0; + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, adv)) + result |= MDIO_EEE_100TX; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, adv)) + result |= MDIO_EEE_1000T; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, adv)) + result |= MDIO_EEE_10GT; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, adv)) + result |= MDIO_EEE_1000KX; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, adv)) + result |= MDIO_EEE_10GKX4; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, adv)) + result |= MDIO_EEE_10GKR; + + return result; +} + +/** + * mii_10base_t1_adv_mod_linkmode_t() + * @adv: linkmode advertisement settings + * @val: register value + * + * A function that translates IEEE 802.3cg-2019 45.2.7.26 "10BASE-T1 AN status" + * register (7.527) value to the linkmode. + */ +static inline void mii_10base_t1_adv_mod_linkmode_t(unsigned long *adv, u16 val) +{ + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, + adv, val & MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L); +} + +/** + * linkmode_adv_to_mii_10base_t1_t() + * @adv: linkmode advertisement settings + * + * A function that translates the linkmode to IEEE 802.3cg-2019 45.2.7.25 + * "10BASE-T1 AN control" register (7.526) value. + */ +static inline u32 linkmode_adv_to_mii_10base_t1_t(unsigned long *adv) +{ + u32 result = 0; + + if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, adv)) + result |= MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L; + + 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); int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, diff --git a/include/linux/phy.h b/include/linux/phy.h index fbeba4fee8d4..727bff531a14 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -52,6 +52,7 @@ extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_all_ports_features) __ro_after_ extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_features) __ro_after_init; extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_fec_features) __ro_after_init; extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_init; +extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap1_features) __ro_after_init; #define PHY_BASIC_FEATURES ((unsigned long *)&phy_basic_features) #define PHY_BASIC_T1_FEATURES ((unsigned long *)&phy_basic_t1_features) @@ -62,6 +63,7 @@ extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_ini #define PHY_10GBIT_FEATURES ((unsigned long *)&phy_10gbit_features) #define PHY_10GBIT_FEC_FEATURES ((unsigned long *)&phy_10gbit_fec_features) #define PHY_10GBIT_FULL_FEATURES ((unsigned long *)&phy_10gbit_full_features) +#define PHY_EEE_CAP1_FEATURES ((unsigned long *)&phy_eee_cap1_features) extern const int phy_basic_ports_array[3]; extern const int phy_fibre_port_array[1]; @@ -572,6 +574,7 @@ struct macsec_ops; * @supported: Combined MAC/PHY supported linkmodes * @advertising: Currently advertised linkmodes * @adv_old: Saved advertised while power saving for WoL + * @supported_eee: supported PHY EEE linkmodes * @lp_advertising: Current link partner advertised linkmodes * @host_interfaces: PHY interface modes supported by host * @eee_broken_modes: Energy efficient ethernet modes which should be prohibited @@ -676,6 +679,8 @@ struct phy_device { __ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising); /* used with phy_speed_down */ __ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old); + /* used for eee validation */ + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported_eee); /* Host supported PHY interface types. Should be ignored if empty. */ DECLARE_PHY_INTERFACE_MASK(host_interfaces); @@ -1614,6 +1619,7 @@ int phy_start_aneg(struct phy_device *phydev); int phy_aneg_done(struct phy_device *phydev); int phy_speed_down(struct phy_device *phydev, bool sync); int phy_speed_up(struct phy_device *phydev); +bool phy_check_valid(int speed, int duplex, unsigned long *features); int phy_restart_aneg(struct phy_device *phydev); int phy_reset_after_clk_enable(struct phy_device *phydev); @@ -1737,6 +1743,7 @@ 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); +int genphy_c45_read_eee_abilities(struct phy_device *phydev); int genphy_c45_pma_baset1_read_master_slave(struct phy_device *phydev); int genphy_c45_read_status(struct phy_device *phydev); int genphy_c45_baset1_read_status(struct phy_device *phydev); @@ -1751,6 +1758,13 @@ int genphy_c45_plca_set_cfg(struct phy_device *phydev, const struct phy_plca_cfg *plca_cfg); int genphy_c45_plca_get_status(struct phy_device *phydev, struct phy_plca_status *plca_st); +int genphy_c45_eee_is_active(struct phy_device *phydev, unsigned long *adv, + unsigned long *lp, bool *is_enabled); +int genphy_c45_ethtool_get_eee(struct phy_device *phydev, + struct ethtool_eee *data); +int genphy_c45_ethtool_set_eee(struct phy_device *phydev, + struct ethtool_eee *data); +int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv); /* Generic C45 PHY driver */ extern struct phy_driver genphy_c45_driver; diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h index 75b7257a51e1..256b463e47a6 100644 --- a/include/uapi/linux/mdio.h +++ b/include/uapi/linux/mdio.h @@ -79,6 +79,8 @@ #define MDIO_AN_T1_LP_L 517 /* BASE-T1 AN LP Base Page ability register [15:0] */ #define MDIO_AN_T1_LP_M 518 /* BASE-T1 AN LP Base Page ability register [31:16] */ #define MDIO_AN_T1_LP_H 519 /* BASE-T1 AN LP Base Page ability register [47:32] */ +#define MDIO_AN_10BT1_AN_CTRL 526 /* 10BASE-T1 AN control register */ +#define MDIO_AN_10BT1_AN_STAT 527 /* 10BASE-T1 AN status register */ #define MDIO_PMA_PMD_BT1_CTRL 2100 /* BASE-T1 PMA/PMD control register */ /* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */ @@ -340,6 +342,12 @@ #define MDIO_AN_T1_LP_H_10L_TX_HI_REQ 0x1000 /* 10BASE-T1L High Level LP Transmit Request */ #define MDIO_AN_T1_LP_H_10L_TX_HI 0x2000 /* 10BASE-T1L High Level LP Transmit Ability */ +/* 10BASE-T1 AN control register */ +#define MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L 0x4000 /* 10BASE-T1L EEE ability advertisement */ + +/* 10BASE-T1 AN status register */ +#define MDIO_AN_10BT1_AN_STAT_LPA_EEE_T1L 0x4000 /* 10BASE-T1L LP EEE ability advertisement */ + /* BASE-T1 PMA/PMD control register */ #define MDIO_PMA_PMD_BT1_CTRL_CFG_MST 0x4000 /* MASTER-SLAVE config value */ |