When setting a PHY's max speed using either the max-speed DT property
or ethtool, we should mask-out all non-compatible modes according to the
settings table, instead of just the 10/100BASET modes.

Signed-off-by: Maxime Chevallier <maxime.chevall...@bootlin.com>
Suggested-by: Russell King <rmk+ker...@armlinux.org.uk>
---
 drivers/net/phy/phy-core.c   | 45 ++++++++++++++++++++++++++++++
 drivers/net/phy/phy_device.c | 53 ------------------------------------
 include/linux/phy.h          |  1 +
 3 files changed, 46 insertions(+), 53 deletions(-)

diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index 7d6aad287f84..8e54fe396553 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -4,6 +4,7 @@
  */
 #include <linux/export.h>
 #include <linux/phy.h>
+#include <linux/of.h>
 
 const char *phy_speed_to_str(int speed)
 {
@@ -338,6 +339,50 @@ size_t phy_speeds(unsigned int *speeds, size_t size,
        return count;
 }
 
+static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
+{
+       const struct phy_setting *p;
+       int i;
+
+       for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
+               if (p->speed > max_speed)
+                       linkmode_clear_bit(p->bit, phydev->supported);
+               else
+                       break;
+       }
+
+       return 0;
+}
+
+int phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
+{
+       int err;
+
+       err = __set_phy_supported(phydev, max_speed);
+       if (err)
+               return err;
+
+       linkmode_copy(phydev->advertising, phydev->supported);
+
+       return 0;
+}
+EXPORT_SYMBOL(phy_set_max_speed);
+
+void of_set_phy_supported(struct phy_device *phydev)
+{
+       struct device_node *node = phydev->mdio.dev.of_node;
+       u32 max_speed;
+
+       if (!IS_ENABLED(CONFIG_OF_MDIO))
+               return;
+
+       if (!node)
+               return;
+
+       if (!of_property_read_u32(node, "max-speed", &max_speed))
+               __set_phy_supported(phydev, max_speed);
+}
+
 /**
  * phy_resolve_aneg_linkmode - resolve the advertisements into phy settings
  * @phydev: The phy_device struct
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 18a10565efd4..a4ab16992806 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -2023,44 +2023,6 @@ int genphy_loopback(struct phy_device *phydev, bool 
enable)
 }
 EXPORT_SYMBOL(genphy_loopback);
 
-static int __set_phy_supported(struct phy_device *phydev, u32 max_speed)
-{
-       switch (max_speed) {
-       case SPEED_10:
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
-                                  phydev->supported);
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
-                                  phydev->supported);
-               /* fall through */
-       case SPEED_100:
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
-                                  phydev->supported);
-               linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
-                                  phydev->supported);
-               break;
-       case SPEED_1000:
-               break;
-       default:
-               return -ENOTSUPP;
-       }
-
-       return 0;
-}
-
-int phy_set_max_speed(struct phy_device *phydev, u32 max_speed)
-{
-       int err;
-
-       err = __set_phy_supported(phydev, max_speed);
-       if (err)
-               return err;
-
-       linkmode_copy(phydev->advertising, phydev->supported);
-
-       return 0;
-}
-EXPORT_SYMBOL(phy_set_max_speed);
-
 /**
  * phy_remove_link_mode - Remove a supported link mode
  * @phydev: phy_device structure to remove link mode from
@@ -2191,21 +2153,6 @@ bool phy_validate_pause(struct phy_device *phydev,
 }
 EXPORT_SYMBOL(phy_validate_pause);
 
-static void of_set_phy_supported(struct phy_device *phydev)
-{
-       struct device_node *node = phydev->mdio.dev.of_node;
-       u32 max_speed;
-
-       if (!IS_ENABLED(CONFIG_OF_MDIO))
-               return;
-
-       if (!node)
-               return;
-
-       if (!of_property_read_u32(node, "max-speed", &max_speed))
-               __set_phy_supported(phydev, max_speed);
-}
-
 static void of_set_phy_eee_broken(struct phy_device *phydev)
 {
        struct device_node *node = phydev->mdio.dev.of_node;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 237dd035858a..cfdd3de38410 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -667,6 +667,7 @@ phy_lookup_setting(int speed, int duplex, const unsigned 
long *mask,
                   bool exact);
 size_t phy_speeds(unsigned int *speeds, size_t size,
                  unsigned long *mask);
+void of_set_phy_supported(struct phy_device *phydev);
 
 static inline bool __phy_is_started(struct phy_device *phydev)
 {
-- 
2.19.2

Reply via email to