From fc281d78b6863ba86baec220651fef815341d3a0 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 2 Apr 2023 17:11:40 +0200 Subject: [PATCH 1/7] net: phy: smsc: rename flag energy_enable Rename the flag to edpd_enable, as we're not enabling energy but edpd (energy detect power down) mode. In addition change the type to a bit field member in preparation of adding further flags. Signed-off-by: Heiner Kallweit Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 730964b856ab..928cf6d8b2e6 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -44,7 +44,7 @@ static struct smsc_hw_stat smsc_hw_stats[] = { }; struct smsc_phy_priv { - bool energy_enable; + unsigned int edpd_enable:1; }; static int smsc_phy_ack_interrupt(struct phy_device *phydev) @@ -102,7 +102,7 @@ int smsc_phy_config_init(struct phy_device *phydev) { struct smsc_phy_priv *priv = phydev->priv; - if (!priv || !priv->energy_enable || phydev->irq != PHY_POLL) + if (!priv || !priv->edpd_enable || phydev->irq != PHY_POLL) return 0; /* Enable energy detect power down mode */ @@ -198,7 +198,7 @@ int lan87xx_read_status(struct phy_device *phydev) if (err) return err; - if (!phydev->link && priv && priv->energy_enable && + if (!phydev->link && priv && priv->edpd_enable && phydev->irq == PHY_POLL) { /* Disable EDPD to wake up PHY */ int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); @@ -284,10 +284,10 @@ int smsc_phy_probe(struct phy_device *phydev) if (!priv) return -ENOMEM; - priv->energy_enable = true; + priv->edpd_enable = true; if (device_property_present(dev, "smsc,disable-energy-detect")) - priv->energy_enable = false; + priv->edpd_enable = false; phydev->priv = priv; From 89946e31ff4f7f43b3cf4837eb5b9f141eb6f1d6 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 2 Apr 2023 17:12:20 +0200 Subject: [PATCH 2/7] net: phy: smsc: add helper smsc_phy_config_edpd Add helper smsc_phy_config_edpd() and explicitly clear bit MII_LAN83C185_EDPWRDOWN is edpd_enable isn't set. Boot loader may have left whatever value. Signed-off-by: Heiner Kallweit Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 928cf6d8b2e6..1b588366e075 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -77,6 +77,18 @@ int smsc_phy_config_intr(struct phy_device *phydev) } EXPORT_SYMBOL_GPL(smsc_phy_config_intr); +static int smsc_phy_config_edpd(struct phy_device *phydev) +{ + struct smsc_phy_priv *priv = phydev->priv; + + if (priv->edpd_enable) + return phy_set_bits(phydev, MII_LAN83C185_CTRL_STATUS, + MII_LAN83C185_EDPWRDOWN); + else + return phy_clear_bits(phydev, MII_LAN83C185_CTRL_STATUS, + MII_LAN83C185_EDPWRDOWN); +} + irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev) { int irq_status; @@ -105,9 +117,7 @@ int smsc_phy_config_init(struct phy_device *phydev) if (!priv || !priv->edpd_enable || phydev->irq != PHY_POLL) return 0; - /* Enable energy detect power down mode */ - return phy_set_bits(phydev, MII_LAN83C185_CTRL_STATUS, - MII_LAN83C185_EDPWRDOWN); + return smsc_phy_config_edpd(phydev); } EXPORT_SYMBOL_GPL(smsc_phy_config_init); From d56417ad1133fc41752bb9fe37da7ae3187395a4 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 2 Apr 2023 17:13:05 +0200 Subject: [PATCH 3/7] net: phy: smsc: clear edpd_enable if interrupt mode is used Clear edpd_enable if interupt mode is used, this avoids having to check for PHY_POLL multiple times. Signed-off-by: Heiner Kallweit Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 1b588366e075..f5ecd8bea0e3 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -114,9 +114,12 @@ int smsc_phy_config_init(struct phy_device *phydev) { struct smsc_phy_priv *priv = phydev->priv; - if (!priv || !priv->edpd_enable || phydev->irq != PHY_POLL) + if (!priv) return 0; + if (phydev->irq != PHY_POLL) + priv->edpd_enable = false; + return smsc_phy_config_edpd(phydev); } EXPORT_SYMBOL_GPL(smsc_phy_config_init); @@ -208,8 +211,7 @@ int lan87xx_read_status(struct phy_device *phydev) if (err) return err; - if (!phydev->link && priv && priv->edpd_enable && - phydev->irq == PHY_POLL) { + if (!phydev->link && priv && priv->edpd_enable) { /* Disable EDPD to wake up PHY */ int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) From a620511080960a72a40c58c55945e41cc3052f6f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 2 Apr 2023 17:13:35 +0200 Subject: [PATCH 4/7] net: phy: smsc: add flag edpd_mode_set_by_user Add flag edpd_mode_set_by_user in preparation of adding edpd phy tunable support. This flag will allow users to override the default behavior of edpd being disabled if interrupt mode is used. Signed-off-by: Heiner Kallweit Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index f5ecd8bea0e3..25b9cd474e83 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -45,6 +45,7 @@ static struct smsc_hw_stat smsc_hw_stats[] = { struct smsc_phy_priv { unsigned int edpd_enable:1; + unsigned int edpd_mode_set_by_user:1; }; static int smsc_phy_ack_interrupt(struct phy_device *phydev) @@ -117,7 +118,8 @@ int smsc_phy_config_init(struct phy_device *phydev) if (!priv) return 0; - if (phydev->irq != PHY_POLL) + /* don't use EDPD in irq mode except overridden by user */ + if (!priv->edpd_mode_set_by_user && phydev->irq != PHY_POLL) priv->edpd_enable = false; return smsc_phy_config_edpd(phydev); From 1ce658693b089ee753260eae78a3ea572bc4b3fb Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 2 Apr 2023 17:14:35 +0200 Subject: [PATCH 5/7] net: phy: smsc: prepare for making edpd wait period configurable Add a member edpd_max_wait_ms to the private data structure in preparation of making the wait period configurable by supporting the edpd phy tunable. v2: - rename constant to EDPD_MAX_WAIT_DFLT_MS Signed-off-by: Heiner Kallweit Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 25b9cd474e83..659a3ab10d82 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -33,6 +33,8 @@ #define SPECIAL_CTRL_STS_AMDIX_ENABLE_ 0x4000 #define SPECIAL_CTRL_STS_AMDIX_STATE_ 0x2000 +#define EDPD_MAX_WAIT_DFLT_MS 640 + struct smsc_hw_stat { const char *string; u8 reg; @@ -46,6 +48,7 @@ static struct smsc_hw_stat smsc_hw_stats[] = { struct smsc_phy_priv { unsigned int edpd_enable:1; unsigned int edpd_mode_set_by_user:1; + unsigned int edpd_max_wait_ms; }; static int smsc_phy_ack_interrupt(struct phy_device *phydev) @@ -213,9 +216,13 @@ int lan87xx_read_status(struct phy_device *phydev) if (err) return err; - if (!phydev->link && priv && priv->edpd_enable) { + if (!phydev->link && priv && priv->edpd_enable && + priv->edpd_max_wait_ms) { + unsigned int max_wait = priv->edpd_max_wait_ms * 1000; + int rc; + /* Disable EDPD to wake up PHY */ - int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); + rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc; @@ -229,7 +236,7 @@ int lan87xx_read_status(struct phy_device *phydev) */ read_poll_timeout(phy_read, rc, rc & MII_LAN83C185_ENERGYON || rc < 0, - 10000, 640000, true, phydev, + 10000, max_wait, true, phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc; @@ -299,6 +306,7 @@ int smsc_phy_probe(struct phy_device *phydev) return -ENOMEM; priv->edpd_enable = true; + priv->edpd_max_wait_ms = EDPD_MAX_WAIT_DFLT_MS; if (device_property_present(dev, "smsc,disable-energy-detect")) priv->edpd_enable = false; From 657de1cf258dbe2489906c81bd91e4af536de255 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 2 Apr 2023 17:16:56 +0200 Subject: [PATCH 6/7] net: phy: smsc: add support for edpd tunable This adds support for the EDPD PHY tunable. Per default EDPD is disabled in interrupt mode, the tunable can be used to override this, e.g. if the link partner doesn't use EDPD. The interval to check for energy can be chosen between 1000ms and 2000ms. Note that this value consists of the 1000ms phylib interval for state machine runs plus the time to wait for energy being detected. v2: - consider that phylib core holds phydev->lock when calling the phy tunable hooks Signed-off-by: Heiner Kallweit Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 75 +++++++++++++++++++++++++++++++++++++++++ include/linux/smscphy.h | 4 +++ 2 files changed, 79 insertions(+) diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 659a3ab10d82..0eba69ad5745 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -34,6 +34,8 @@ #define SPECIAL_CTRL_STS_AMDIX_STATE_ 0x2000 #define EDPD_MAX_WAIT_DFLT_MS 640 +/* interval between phylib state machine runs in ms */ +#define PHY_STATE_MACH_MS 1000 struct smsc_hw_stat { const char *string; @@ -295,6 +297,79 @@ static void smsc_get_stats(struct phy_device *phydev, data[i] = smsc_get_stat(phydev, i); } +static int smsc_phy_get_edpd(struct phy_device *phydev, u16 *edpd) +{ + struct smsc_phy_priv *priv = phydev->priv; + + if (!priv) + return -EOPNOTSUPP; + + if (!priv->edpd_enable) + *edpd = ETHTOOL_PHY_EDPD_DISABLE; + else if (!priv->edpd_max_wait_ms) + *edpd = ETHTOOL_PHY_EDPD_NO_TX; + else + *edpd = PHY_STATE_MACH_MS + priv->edpd_max_wait_ms; + + return 0; +} + +static int smsc_phy_set_edpd(struct phy_device *phydev, u16 edpd) +{ + struct smsc_phy_priv *priv = phydev->priv; + + if (!priv) + return -EOPNOTSUPP; + + switch (edpd) { + case ETHTOOL_PHY_EDPD_DISABLE: + priv->edpd_enable = false; + break; + case ETHTOOL_PHY_EDPD_NO_TX: + priv->edpd_enable = true; + priv->edpd_max_wait_ms = 0; + break; + case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS: + edpd = PHY_STATE_MACH_MS + EDPD_MAX_WAIT_DFLT_MS; + fallthrough; + default: + if (phydev->irq != PHY_POLL) + return -EOPNOTSUPP; + if (edpd < PHY_STATE_MACH_MS || edpd > PHY_STATE_MACH_MS + 1000) + return -EINVAL; + priv->edpd_enable = true; + priv->edpd_max_wait_ms = edpd - PHY_STATE_MACH_MS; + } + + priv->edpd_mode_set_by_user = true; + + return smsc_phy_config_edpd(phydev); +} + +int smsc_phy_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_EDPD: + return smsc_phy_get_edpd(phydev, data); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(smsc_phy_get_tunable); + +int smsc_phy_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, const void *data) +{ + switch (tuna->id) { + case ETHTOOL_PHY_EDPD: + return smsc_phy_set_edpd(phydev, *(u16 *)data); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(smsc_phy_set_tunable); + int smsc_phy_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; diff --git a/include/linux/smscphy.h b/include/linux/smscphy.h index 80f37c1dba58..e1c88627755a 100644 --- a/include/linux/smscphy.h +++ b/include/linux/smscphy.h @@ -32,6 +32,10 @@ int smsc_phy_config_intr(struct phy_device *phydev); irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev); int smsc_phy_config_init(struct phy_device *phydev); int lan87xx_read_status(struct phy_device *phydev); +int smsc_phy_get_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, void *data); +int smsc_phy_set_tunable(struct phy_device *phydev, + struct ethtool_tunable *tuna, const void *data); int smsc_phy_probe(struct phy_device *phydev); #endif /* __LINUX_SMSCPHY_H__ */ From 3c4c3b3e6d414c50f6858f027a2ca2206e1ce583 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 2 Apr 2023 17:17:35 +0200 Subject: [PATCH 7/7] net: phy: smsc: enable edpd tunable support Enable EDPD PHY tunable support for all drivers using lan87xx_read_status. Signed-off-by: Heiner Kallweit Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 0eba69ad5745..692930750215 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -469,6 +469,9 @@ static struct phy_driver smsc_phy_driver[] = { .get_strings = smsc_get_strings, .get_stats = smsc_get_stats, + .get_tunable = smsc_phy_get_tunable, + .set_tunable = smsc_phy_set_tunable, + .suspend = genphy_suspend, .resume = genphy_resume, }, { @@ -513,6 +516,9 @@ static struct phy_driver smsc_phy_driver[] = { .get_strings = smsc_get_strings, .get_stats = smsc_get_stats, + .get_tunable = smsc_phy_get_tunable, + .set_tunable = smsc_phy_set_tunable, + .suspend = genphy_suspend, .resume = genphy_resume, }, { @@ -539,6 +545,9 @@ static struct phy_driver smsc_phy_driver[] = { .get_strings = smsc_get_strings, .get_stats = smsc_get_stats, + .get_tunable = smsc_phy_get_tunable, + .set_tunable = smsc_phy_set_tunable, + .suspend = genphy_suspend, .resume = genphy_resume, }, { @@ -569,6 +578,9 @@ static struct phy_driver smsc_phy_driver[] = { .get_strings = smsc_get_strings, .get_stats = smsc_get_stats, + .get_tunable = smsc_phy_get_tunable, + .set_tunable = smsc_phy_set_tunable, + .suspend = genphy_suspend, .resume = genphy_resume, } };