2023-01-30 08:07:56

by Oleksij Rempel

[permalink] [raw]
Subject: [PATCH net-next v3 00/15] net: add EEE support for KSZ9477 and AR8035 with i.MX6

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.

Oleksij Rempel (15):
net: dsa: microchip: enable EEE support
net: phy: add genphy_c45_read_eee_abilities() function
net: phy: micrel: add ksz9477_get_features()
net: phy: export phy_check_valid() function
net: phy: add genphy_c45_ethtool_get/set_eee() support
net: phy: c22: migrate to genphy_c45_write_eee_adv()
net: phy: c45: migrate to genphy_c45_write_eee_adv()
net: phy: start using genphy_c45_ethtool_get/set_eee()
net: phy: add driver specific get/set_eee support
net: phy: at803x: implement ethtool access to SmartEEE functionality
net: phy: at803x: ar8035: fix EEE support for half duplex links
net: phy: add PHY specifica flag to signal SmartEEE support
net: phy: at803x: add PHY_SMART_EEE flag to AR8035
net: phy: add phy_has_smarteee() helper
net: fec: add support for PHYs with SmartEEE support

drivers/net/dsa/microchip/ksz_common.c | 66 ++++++
drivers/net/ethernet/freescale/fec_main.c | 22 +-
drivers/net/phy/at803x.c | 142 +++++++++++-
drivers/net/phy/micrel.c | 21 ++
drivers/net/phy/phy-c45.c | 255 +++++++++++++++++++++-
drivers/net/phy/phy.c | 66 +-----
drivers/net/phy/phy_device.c | 26 ++-
include/linux/mdio.h | 53 +++++
include/linux/phy.h | 26 +++
include/uapi/linux/mdio.h | 8 +
10 files changed, 615 insertions(+), 70 deletions(-)

--
2.30.2



2023-01-30 08:07:59

by Oleksij Rempel

[permalink] [raw]
Subject: [PATCH net-next v3 09/15] net: phy: add driver specific get/set_eee support

Not all PHYs can be handled by generic phy_ethtool_get/set_eee()
functions. So, add driver specific get/set_eee support.

Signed-off-by: Oleksij Rempel <[email protected]>
---
drivers/net/phy/phy.c | 6 ++++++
include/linux/phy.h | 5 +++++
2 files changed, 11 insertions(+)

diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 54b62a604238..1e0e81510f10 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1581,6 +1581,9 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
if (!phydev->drv)
return -EIO;

+ if (phydev->drv->get_eee)
+ return phydev->drv->get_eee(phydev, data);
+
return genphy_c45_ethtool_get_eee(phydev, data);
}
EXPORT_SYMBOL(phy_ethtool_get_eee);
@@ -1597,6 +1600,9 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
if (!phydev->drv)
return -EIO;

+ if (phydev->drv->set_eee)
+ return phydev->drv->set_eee(phydev, data);
+
return genphy_c45_ethtool_set_eee(phydev, data);
}
EXPORT_SYMBOL(phy_ethtool_set_eee);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index d7eacb150eac..f6619f4aaa4b 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1059,6 +1059,11 @@ struct phy_driver {
/** @get_plca_status: Return the current PLCA status info */
int (*get_plca_status)(struct phy_device *dev,
struct phy_plca_status *plca_st);
+
+ /** @get_eee: Return the current EEE configuration */
+ int (*get_eee)(struct phy_device *phydev, struct ethtool_eee *e);
+ /** @set_eee: Set the EEE configuration */
+ int (*set_eee)(struct phy_device *phydev, struct ethtool_eee *e);
};
#define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
struct phy_driver, mdiodrv)
--
2.30.2


2023-01-30 08:08:02

by Oleksij Rempel

[permalink] [raw]
Subject: [PATCH net-next v3 07/15] net: phy: c45: migrate to genphy_c45_write_eee_adv()

Migrate from genphy_config_eee_advert() to genphy_c45_write_eee_adv().

It should work as before except write operation to the EEE adv registers
will be done only if some EEE abilities was detected.

If some driver will have a regression, related driver should provide own
.get_features callback. See micrel.c:ksz9477_get_features() as example.

Signed-off-by: Oleksij Rempel <[email protected]>
---
drivers/net/phy/phy-c45.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index d89627ee0ec8..3a990e0a8d51 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);
@@ -945,6 +949,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);
--
2.30.2


2023-01-30 08:08:05

by Oleksij Rempel

[permalink] [raw]
Subject: [PATCH net-next v3 11/15] net: phy: at803x: ar8035: fix EEE support for half duplex links

If AR8035 is running with enabled EEE and LPI, it will not be able to
establish an 100BaseTX/Half or 1000BaseT/Half link. Similar issue we
will have with 100BaseTX/Full and LPI TX timer configured to less then
80msec.

To avoid this issue, we need to keep LPI disabled before link is
establish and enable it only we detected supported link configuration.

Signed-off-by: Oleksij Rempel <[email protected]>
---
drivers/net/phy/at803x.c | 41 +++++++++++++++++++++++++++++++++++-----
1 file changed, 36 insertions(+), 5 deletions(-)

diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index 9eb4439b0afc..5ab43eb63581 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -313,6 +313,7 @@ struct at803x_priv {
u8 smarteee_lpi_tw_100m;
bool is_fiber;
bool is_1000basex;
+ bool tx_lpi_on;
struct regulator_dev *vddio_rdev;
struct regulator_dev *vddh_rdev;
struct regulator *vddio;
@@ -970,6 +971,8 @@ static int at803x_smarteee_config(struct phy_device *phydev, bool enable,
u16 mask = 0, val = 0;
int ret;

+ priv->tx_lpi_on = enable;
+
if (priv->flags & AT803X_DISABLE_SMARTEEE || !enable)
return phy_modify_mmd(phydev, MDIO_MMD_PCS,
AT803X_MMD3_SMARTEEE_CTL3,
@@ -1010,10 +1013,15 @@ static int at803x_smarteee_config(struct phy_device *phydev, bool enable,
if (ret)
return ret;

- val = AT803X_MMD3_SMARTEEE_CTL3_LPI_EN |
- FIELD_PREP(AT803X_MMD3_SMARTEEE_LPI_TIME_HIGH,
- FIELD_GET(AT803X_MMD3_SMARTEEE_LPI_TIME_23_16,
- tx_lpi_timer_raw));
+ val = FIELD_PREP(AT803X_MMD3_SMARTEEE_LPI_TIME_HIGH,
+ FIELD_GET(AT803X_MMD3_SMARTEEE_LPI_TIME_23_16,
+ tx_lpi_timer_raw));
+
+ if (phydev->state == PHY_RUNNING &&
+ phy_check_valid(phydev->speed, phydev->duplex,
+ phydev->supported_eee)) {
+ val |= AT803X_MMD3_SMARTEEE_CTL3_LPI_EN;
+ }

return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3,
AT803X_MMD3_SMARTEEE_CTL3_LPI_EN |
@@ -1682,7 +1690,7 @@ static int at803x_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
tx_timer_ns = tx_timer_raw * AT803X_MMD3_SMARTEEE_LPI_TIME_RESOL_NS;
data->tx_lpi_timer = DIV_ROUND_CLOSEST_ULL(tx_timer_ns, NSEC_PER_USEC);

- data->tx_lpi_enabled = !!(ret & AT803X_MMD3_SMARTEEE_CTL3_LPI_EN);
+ data->tx_lpi_enabled = priv->tx_lpi_on;

return genphy_c45_ethtool_get_eee(phydev, data);
}
@@ -1709,6 +1717,28 @@ static int at803x_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
return genphy_c45_ethtool_set_eee(phydev, data);
}

+static void at8035_link_change_notify(struct phy_device *phydev)
+{
+ struct at803x_priv *priv = phydev->priv;
+
+ if (priv->flags & AT803X_DISABLE_SMARTEEE)
+ return;
+
+ if (phydev->state == PHY_RUNNING) {
+ if (priv->tx_lpi_on && phy_check_valid(phydev->speed,
+ phydev->duplex,
+ phydev->supported_eee))
+ phy_set_bits_mmd(phydev, MDIO_MMD_PCS,
+ AT803X_MMD3_SMARTEEE_CTL3,
+ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN);
+ } else {
+ if (priv->tx_lpi_on)
+ phy_clear_bits_mmd(phydev, MDIO_MMD_PCS,
+ AT803X_MMD3_SMARTEEE_CTL3,
+ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN);
+ }
+}
+
static int qca83xx_config_init(struct phy_device *phydev)
{
u8 switch_revision;
@@ -2137,6 +2167,7 @@ static struct phy_driver at803x_driver[] = {
.cable_test_get_status = at803x_cable_test_get_status,
.get_eee = at803x_get_eee,
.set_eee = at803x_set_eee,
+ .link_change_notify = at8035_link_change_notify,
}, {
/* Qualcomm Atheros AR8030 */
.phy_id = ATH8030_PHY_ID,
--
2.30.2


2023-01-30 08:08:08

by Oleksij Rempel

[permalink] [raw]
Subject: [PATCH net-next v3 12/15] net: phy: add PHY specifica flag to signal SmartEEE support

Typical EEE support need cooperation of MAC and PHY, so both parts should
be able to do EEE. But, there also PHYs compatible with normal 802.3az
standard working with legacy MAC without EEE ability, acting as a complete
EEE power saving system.

To identify this PHYs we need a PHY specific flag. Since the PHY
specification implementing this functionality calls it SmartEEE, use
the same flag name - PHY_SMART_EEE.

Signed-off-by: Oleksij Rempel <[email protected]>
---
include/linux/phy.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/include/linux/phy.h b/include/linux/phy.h
index f6619f4aaa4b..90d7b90fa1f4 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -85,6 +85,7 @@ extern const int phy_10gbit_features_array[1];
#define PHY_IS_INTERNAL 0x00000001
#define PHY_RST_AFTER_CLK_EN 0x00000002
#define PHY_POLL_CABLE_TEST 0x00000004
+#define PHY_SMART_EEE 0x00000008 /* EEE done by PHY without MAC */
#define MDIO_DEVICE_IS_PHY 0x80000000

/**
--
2.30.2


2023-01-30 08:08:13

by Oleksij Rempel

[permalink] [raw]
Subject: [PATCH net-next v3 01/15] net: dsa: microchip: enable EEE support

Some of KSZ9477 family switches provides EEE support. To enable it, we
just need to register set_mac_eee/set_mac_eee handlers and validate
supported chip version and port.

Signed-off-by: Oleksij Rempel <[email protected]>
---
drivers/net/dsa/microchip/ksz_common.c | 66 ++++++++++++++++++++++++++
1 file changed, 66 insertions(+)

diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 5e1e5bd555d2..bef36e2fa257 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2645,6 +2645,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 testes
+ * 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)
{
@@ -3006,6 +3070,8 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.port_hwtstamp_set = ksz_hwtstamp_set,
.port_txtstamp = ksz_port_txtstamp,
.port_rxtstamp = ksz_port_rxtstamp,
+ .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)
--
2.30.2


2023-01-30 08:08:19

by Oleksij Rempel

[permalink] [raw]
Subject: [PATCH net-next v3 02/15] net: phy: add genphy_c45_read_eee_abilities() function

Add generic function for EEE abilities defined by IEEE 802.3
specification. For now following registers are supported:
- IEEE 802.3-2018 45.2.3.10 EEE control and capability 1 (Register 3.20)
- IEEE 802.3cg-2019 45.2.1.186b 10BASE-T1L PMA status register
(Register 1.2295)

Since I was not able to find any flag signaling support of this
registers, we should detect link mode abilities first and then based on
this abilities doing EEE link modes detection.

Results of EEE ability detection will be stored in to new variable
phydev->supported_eee.

Signed-off-by: Oleksij Rempel <[email protected]>
---
drivers/net/phy/phy-c45.c | 49 ++++++++++++++++++++++++++++++++++++
drivers/net/phy/phy_device.c | 16 ++++++++++++
include/linux/mdio.h | 17 +++++++++++++
include/linux/phy.h | 5 ++++
4 files changed, 87 insertions(+)

diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index cff83220595c..03af19796848 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -661,6 +661,55 @@ int genphy_c45_read_mdix(struct phy_device *phydev)
}
EXPORT_SYMBOL_GPL(genphy_c45_read_mdix);

+/**
+ * genphy_c45_read_eee_abilities - read supported EEE link modes
+ * @phydev: target phy_device struct
+ *
+ * Read supported EEE link modes.
+ */
+int genphy_c45_read_eee_abilities(struct phy_device *phydev)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
+ int val;
+
+ linkmode_and(common, phydev->supported, PHY_EEE_100_10000_FEATURES);
+ /* There is not indicator if optional register
+ * "EEE control and capability 1" (3.20) is supported. Read it only
+ * on devices with appropriate linkmodes.
+ */
+ if (!linkmode_empty(common)) {
+ /* 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;
+
+ mii_eee_100_10000_adv_mod_linkmode_t(phydev->supported_eee, val);
+
+ /* Some buggy devices claim not supported EEE link modes */
+ linkmode_and(phydev->supported_eee, phydev->supported_eee,
+ phydev->supported);
+ }
+
+ 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
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 0d371a0a49f2..698549ef2981 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_100_10000_features_array[6] = {
+ 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_100_10000_features) __ro_after_init;
+EXPORT_SYMBOL_GPL(phy_eee_100_10000_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_100_10000_features_array,
+ ARRAY_SIZE(phy_eee_100_10000_features_array),
+ phy_eee_100_10000_features);
+
}

void phy_device_free(struct phy_device *phydev)
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index 220f3ca8702d..ea4f7d08d1a6 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -410,6 +410,23 @@ static inline u32 linkmode_adv_to_mii_t1_adv_m_t(unsigned long *advertising)
return result;
}

+static inline void mii_eee_100_10000_adv_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);
+}
+
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 b3cf1e08e880..42a4a5d66472 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_100_10000_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_100_10000_FEATURES ((unsigned long *)&phy_eee_100_10000_features)

extern const int phy_basic_ports_array[3];
extern const int phy_fibre_port_array[1];
@@ -684,6 +686,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);
@@ -1745,6 +1749,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);
--
2.30.2


2023-01-30 08:08:22

by Oleksij Rempel

[permalink] [raw]
Subject: [PATCH net-next v3 05/15] net: phy: add genphy_c45_ethtool_get/set_eee() support

Add replacement for phy_ethtool_get/set_eee() functions.

Current phy_ethtool_get/set_eee() implementation is great and it is
possible to make it even better:
- this functionality is for devices implementing parts of IEEE 802.3
specification beyond Clause 22. The better place for this code is
phy-c45.c
- currently it is able to do read/write operations on PHYs with
different abilities to not existing registers. It is better to
use stored supported_eee abilities to avoid false read/write
operations.
- the eee_active detection will provide wrong results on not supported
link modes. It is better to validate speed/duplex properties against
supported EEE link modes.
- it is able to support only limited amount of link modes. We have more
EEE link modes...

By refactoring this code I address most of this point except of the last
one. Adding additional EEE link modes will need more work.

Signed-off-by: Oleksij Rempel <[email protected]>
---
drivers/net/phy/phy-c45.c | 195 ++++++++++++++++++++++++++++++++++++++
include/linux/mdio.h | 36 +++++++
include/linux/phy.h | 5 +
include/uapi/linux/mdio.h | 8 ++
4 files changed, 244 insertions(+)

diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 03af19796848..d89627ee0ec8 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -661,6 +661,127 @@ int genphy_c45_read_mdix(struct phy_device *phydev)
}
EXPORT_SYMBOL_GPL(genphy_c45_read_mdix);

+/**
+ * genphy_c45_write_eee_adv - read advertised EEE link modes
+ * @phydev: target phy_device struct
+ */
+int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
+ int val, changed;
+
+ linkmode_and(common, phydev->supported_eee, PHY_EEE_100_10000_FEATURES);
+ if (!linkmode_empty(common)) {
+ val = linkmode_adv_to_mii_eee_100_10000_adv_t(adv);
+
+ /* In eee_broken_modes are stored MDIO_AN_EEE_ADV specific raw
+ * register values.
+ */
+ val &= ~phydev->eee_broken_modes;
+
+ 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);
+
+ 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
+ */
+static int genphy_c45_read_eee_adv(struct phy_device *phydev,
+ unsigned long *adv)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
+ int val;
+
+ linkmode_and(common, phydev->supported_eee, PHY_EEE_100_10000_FEATURES);
+ if (!linkmode_empty(common)) {
+ /* 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_100_10000_adv_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
+ */
+static int genphy_c45_read_eee_lpa(struct phy_device *phydev,
+ unsigned long *lpa)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
+ int val;
+
+ linkmode_and(common, phydev->supported_eee, PHY_EEE_100_10000_FEATURES);
+ if (!linkmode_empty(common)) {
+ /* 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_100_10000_adv_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_abilities - read supported EEE link modes
* @phydev: target phy_device struct
@@ -1173,6 +1294,80 @@ int genphy_c45_plca_get_status(struct phy_device *phydev,
}
EXPORT_SYMBOL_GPL(genphy_c45_plca_get_status);

+/**
+ * genphy_c45_ethtool_get_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_get_eee(struct phy_device *phydev,
+ struct ethtool_eee *data)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(lp) = {};
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
+ int ret;
+
+ ret = genphy_c45_read_eee_adv(phydev, adv);
+ if (ret)
+ return ret;
+
+ ret = genphy_c45_read_eee_lpa(phydev, lp);
+ if (ret)
+ return ret;
+
+ data->eee_enabled = !linkmode_empty(adv);
+ linkmode_and(common, adv, lp);
+ if (data->eee_enabled && !linkmode_empty(common))
+ data->eee_active = phy_check_valid(phydev->speed,
+ phydev->duplex, common);
+ else
+ data->eee_active = false;
+
+ /* FIXME: EEE ethtool interface currently do not support full set of
+ * possible EEE link modes.
+ */
+ data->supported = phydev->supported_eee[0];
+ data->advertised = adv[0];
+ data->lp_advertised = lp[0];
+
+ 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/include/linux/mdio.h b/include/linux/mdio.h
index ea4f7d08d1a6..231cf02671a7 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -427,6 +427,42 @@ static inline void mii_eee_100_10000_adv_mod_linkmode_t(unsigned long *adv,
adv, val & MDIO_EEE_10GKR);
}

+static inline u32 linkmode_adv_to_mii_eee_100_10000_adv_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;
+}
+
+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);
+}
+
+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 950e5ee0d8f2..d7eacb150eac 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1765,6 +1765,11 @@ 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_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 */

--
2.30.2


2023-01-30 08:08:26

by Oleksij Rempel

[permalink] [raw]
Subject: [PATCH net-next v3 06/15] net: phy: c22: migrate to genphy_c45_write_eee_adv()

Migrate from genphy_config_eee_advert() to genphy_c45_write_eee_adv().

It should work as before except write operation to the EEE adv registers
will be done only if some EEE abilities was detected.

If some driver will have a regression, related driver should provide own
.get_features callback. See micrel.c:ksz9477_get_features() as example.

Signed-off-by: Oleksij Rempel <[email protected]>
---
drivers/net/phy/phy_device.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 698549ef2981..abe6124eb9dc 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -2231,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);
@@ -2653,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);
--
2.30.2


2023-01-30 17:59:19

by Vladimir Oltean

[permalink] [raw]
Subject: Re: [PATCH net-next v3 01/15] net: dsa: microchip: enable EEE support

On Mon, Jan 30, 2023 at 09:07:00AM +0100, Oleksij Rempel wrote:
> @@ -3006,6 +3070,8 @@ static const struct dsa_switch_ops ksz_switch_ops = {
> .port_hwtstamp_set = ksz_hwtstamp_set,
> .port_txtstamp = ksz_port_txtstamp,
> .port_rxtstamp = ksz_port_rxtstamp,
> + .get_mac_eee = ksz_get_mac_eee,
> + .set_mac_eee = ksz_set_mac_eee,
> };

<<<<<<< head
.port_setup_tc = ksz_setup_tc,
=======
.get_mac_eee = ksz_get_mac_eee,
.set_mac_eee = ksz_set_mac_eee,
>>>>>>> net: dsa: microchip: enable eee support

with commit 71d7920fb2d1 ("net: dsa: microchip: add support for credit
based shaper").

2023-01-30 21:55:33

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 02/15] net: phy: add genphy_c45_read_eee_abilities() function

> +static inline void mii_eee_100_10000_adv_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);
> +}

This and mmd_eee_cap_to_ethtool_sup_t() are very similar. Could you
try to remove the duplication. Maybe
ethtool_convert_link_mode_to_legacy_u32() could be used?

Andrew

2023-01-30 22:06:40

by Andrew Lunn

[permalink] [raw]
Subject: Re: [PATCH net-next v3 05/15] net: phy: add genphy_c45_ethtool_get/set_eee() support

> +/**
> + * genphy_c45_write_eee_adv - read advertised EEE link modes

s/read/write

> + * @phydev: target phy_device struct
> + */
> +int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv)
> +{
> + __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
> + int val, changed;
> +
> + linkmode_and(common, phydev->supported_eee, PHY_EEE_100_10000_FEATURES);
> + if (!linkmode_empty(common)) {
> + val = linkmode_adv_to_mii_eee_100_10000_adv_t(adv);
> +
> + /* In eee_broken_modes are stored MDIO_AN_EEE_ADV specific raw
> + * register values.
> + */
> + val &= ~phydev->eee_broken_modes;
> +
> + 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);
> +
> + 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
> + */
> +static int genphy_c45_read_eee_adv(struct phy_device *phydev,
> + unsigned long *adv)
> +{
> + __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
> + int val;
> +
> + linkmode_and(common, phydev->supported_eee, PHY_EEE_100_10000_FEATURES);
> + if (!linkmode_empty(common)) {
> + /* 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_100_10000_adv_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
> + */
> +static int genphy_c45_read_eee_lpa(struct phy_device *phydev,
> + unsigned long *lpa)
> +{
> + __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
> + int val;
> +
> + linkmode_and(common, phydev->supported_eee, PHY_EEE_100_10000_FEATURES);
> + if (!linkmode_empty(common)) {
> + /* 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_100_10000_adv_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_abilities - read supported EEE link modes
> * @phydev: target phy_device struct
> @@ -1173,6 +1294,80 @@ int genphy_c45_plca_get_status(struct phy_device *phydev,
> }
> EXPORT_SYMBOL_GPL(genphy_c45_plca_get_status);
>
> +/**
> + * genphy_c45_ethtool_get_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_get_eee(struct phy_device *phydev,
> + struct ethtool_eee *data)
> +{
> + __ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
> + __ETHTOOL_DECLARE_LINK_MODE_MASK(lp) = {};
> + __ETHTOOL_DECLARE_LINK_MODE_MASK(common);
> + int ret;
> +
> + ret = genphy_c45_read_eee_adv(phydev, adv);
> + if (ret)
> + return ret;
> +
> + ret = genphy_c45_read_eee_lpa(phydev, lp);
> + if (ret)
> + return ret;
> +
> + data->eee_enabled = !linkmode_empty(adv);
> + linkmode_and(common, adv, lp);
> + if (data->eee_enabled && !linkmode_empty(common))
> + data->eee_active = phy_check_valid(phydev->speed,
> + phydev->duplex, common);
> + else
> + data->eee_active = false;
> +
> + /* FIXME: EEE ethtool interface currently do not support full set of
> + * possible EEE link modes.
> + */
> + data->supported = phydev->supported_eee[0];
> + data->advertised = adv[0];
> + data->lp_advertised = lp[0];
> +
> + 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/include/linux/mdio.h b/include/linux/mdio.h
> index ea4f7d08d1a6..231cf02671a7 100644
> --- a/include/linux/mdio.h
> +++ b/include/linux/mdio.h
> @@ -427,6 +427,42 @@ static inline void mii_eee_100_10000_adv_mod_linkmode_t(unsigned long *adv,
> adv, val & MDIO_EEE_10GKR);
> }
>
> +static inline u32 linkmode_adv_to_mii_eee_100_10000_adv_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;

Please could you remove the duplication with ethtool_adv_to_mmd_eee_adv_t().

Andrew