Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: phy: Allow loopback speed selection for PHY drivers

PHY drivers support loopback mode, but it is not possible to select the
speed of the loopback mode. The speed is chosen by the set_loopback()
operation of the PHY driver. Same is valid for genphy_loopback().

There are PHYs that support loopback with different speeds. Extend
set_loopback() to make loopback speed selection possible.

Signed-off-by: Gerhard Engleder <gerhard@engleder-embedded.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20250312203010.47429-2-gerhard@engleder-embedded.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Gerhard Engleder and committed by
Paolo Abeni
45456e38 3d97da0e

+51 -18
+4 -1
drivers/net/phy/adin1100.c
··· 215 215 return adin_set_powerdown_mode(phydev, false); 216 216 } 217 217 218 - static int adin_set_loopback(struct phy_device *phydev, bool enable) 218 + static int adin_set_loopback(struct phy_device *phydev, bool enable, int speed) 219 219 { 220 + if (enable && speed) 221 + return -EOPNOTSUPP; 222 + 220 223 if (enable) 221 224 return phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_10T1L_CTRL, 222 225 BMCR_LOOPBACK);
+4 -1
drivers/net/phy/dp83867.c
··· 1009 1009 } 1010 1010 } 1011 1011 1012 - static int dp83867_loopback(struct phy_device *phydev, bool enable) 1012 + static int dp83867_loopback(struct phy_device *phydev, bool enable, int speed) 1013 1013 { 1014 + if (enable && speed) 1015 + return -EOPNOTSUPP; 1016 + 1014 1017 return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 1015 1018 enable ? BMCR_LOOPBACK : 0); 1016 1019 }
+7 -1
drivers/net/phy/marvell.c
··· 2131 2131 data[i] = marvell_get_stat_simple(phydev, i); 2132 2132 } 2133 2133 2134 - static int m88e1510_loopback(struct phy_device *phydev, bool enable) 2134 + static int m88e1510_loopback(struct phy_device *phydev, bool enable, int speed) 2135 2135 { 2136 2136 int err; 2137 2137 2138 2138 if (enable) { 2139 2139 u16 bmcr_ctl, mscr2_ctl = 0; 2140 + 2141 + if (speed == SPEED_10 || speed == SPEED_100 || 2142 + speed == SPEED_1000) 2143 + phydev->speed = speed; 2144 + else if (speed) 2145 + return -EINVAL; 2140 2146 2141 2147 bmcr_ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); 2142 2148
+7 -4
drivers/net/phy/mxl-gpy.c
··· 813 813 wol->wolopts = priv->wolopts; 814 814 } 815 815 816 - static int gpy_loopback(struct phy_device *phydev, bool enable) 816 + static int gpy_loopback(struct phy_device *phydev, bool enable, int speed) 817 817 { 818 818 struct gpy_priv *priv = phydev->priv; 819 819 u16 set = 0; ··· 821 821 822 822 if (enable) { 823 823 u64 now = get_jiffies_64(); 824 + 825 + if (speed) 826 + return -EOPNOTSUPP; 824 827 825 828 /* wait until 3 seconds from last disable */ 826 829 if (time_before64(now, priv->lb_dis_to)) ··· 848 845 return 0; 849 846 } 850 847 851 - static int gpy115_loopback(struct phy_device *phydev, bool enable) 848 + static int gpy115_loopback(struct phy_device *phydev, bool enable, int speed) 852 849 { 853 850 struct gpy_priv *priv = phydev->priv; 854 851 855 852 if (enable) 856 - return gpy_loopback(phydev, enable); 853 + return gpy_loopback(phydev, enable, speed); 857 854 858 855 if (priv->fw_minor > 0x76) 859 - return gpy_loopback(phydev, 0); 856 + return gpy_loopback(phydev, 0, 0); 860 857 861 858 return genphy_soft_reset(phydev); 862 859 }
+4 -1
drivers/net/phy/phy-c45.c
··· 1228 1228 } 1229 1229 EXPORT_SYMBOL_GPL(gen10g_config_aneg); 1230 1230 1231 - int genphy_c45_loopback(struct phy_device *phydev, bool enable) 1231 + int genphy_c45_loopback(struct phy_device *phydev, bool enable, int speed) 1232 1232 { 1233 + if (enable && speed) 1234 + return -EOPNOTSUPP; 1235 + 1233 1236 return phy_modify_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, 1234 1237 MDIO_PCS_CTRL1_LOOPBACK, 1235 1238 enable ? MDIO_PCS_CTRL1_LOOPBACK : 0);
+9 -3
drivers/net/phy/phy_device.c
··· 1838 1838 } 1839 1839 1840 1840 if (phydev->drv->set_loopback) 1841 - ret = phydev->drv->set_loopback(phydev, enable); 1841 + ret = phydev->drv->set_loopback(phydev, enable, 0); 1842 1842 else 1843 - ret = genphy_loopback(phydev, enable); 1843 + ret = genphy_loopback(phydev, enable, 0); 1844 1844 1845 1845 if (ret) 1846 1846 goto out; ··· 2610 2610 } 2611 2611 EXPORT_SYMBOL(genphy_resume); 2612 2612 2613 - int genphy_loopback(struct phy_device *phydev, bool enable) 2613 + int genphy_loopback(struct phy_device *phydev, bool enable, int speed) 2614 2614 { 2615 2615 if (enable) { 2616 2616 u16 ctl = BMCR_LOOPBACK; 2617 2617 int ret, val; 2618 + 2619 + if (speed == SPEED_10 || speed == SPEED_100 || 2620 + speed == SPEED_1000) 2621 + phydev->speed = speed; 2622 + else if (speed) 2623 + return -EINVAL; 2618 2624 2619 2625 ctl |= mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); 2620 2626
+4 -3
drivers/net/phy/xilinx_gmii2rgmii.c
··· 64 64 return 0; 65 65 } 66 66 67 - static int xgmiitorgmii_set_loopback(struct phy_device *phydev, bool enable) 67 + static int xgmiitorgmii_set_loopback(struct phy_device *phydev, bool enable, 68 + int speed) 68 69 { 69 70 struct gmii2rgmii *priv = mdiodev_get_drvdata(&phydev->mdio); 70 71 int err; 71 72 72 73 if (priv->phy_drv->set_loopback) 73 - err = priv->phy_drv->set_loopback(phydev, enable); 74 + err = priv->phy_drv->set_loopback(phydev, enable, speed); 74 75 else 75 - err = genphy_loopback(phydev, enable); 76 + err = genphy_loopback(phydev, enable, speed); 76 77 if (err < 0) 77 78 return err; 78 79
+12 -4
include/linux/phy.h
··· 1136 1136 int (*set_tunable)(struct phy_device *dev, 1137 1137 struct ethtool_tunable *tuna, 1138 1138 const void *data); 1139 - /** @set_loopback: Set the loopback mood of the PHY */ 1140 - int (*set_loopback)(struct phy_device *dev, bool enable); 1139 + /** 1140 + * @set_loopback: Set the loopback mode of the PHY 1141 + * enable selects if the loopback mode is enabled or disabled. If the 1142 + * loopback mode is enabled, then the speed of the loopback mode can be 1143 + * requested with the speed argument. If the speed argument is zero, 1144 + * then any speed can be selected. If the speed argument is > 0, then 1145 + * this speed shall be selected for the loopback mode or EOPNOTSUPP 1146 + * shall be returned if speed selection is not supported. 1147 + */ 1148 + int (*set_loopback)(struct phy_device *dev, bool enable, int speed); 1141 1149 /** @get_sqi: Get the signal quality indication */ 1142 1150 int (*get_sqi)(struct phy_device *dev); 1143 1151 /** @get_sqi_max: Get the maximum signal quality indication */ ··· 1923 1915 int genphy_read_master_slave(struct phy_device *phydev); 1924 1916 int genphy_suspend(struct phy_device *phydev); 1925 1917 int genphy_resume(struct phy_device *phydev); 1926 - int genphy_loopback(struct phy_device *phydev, bool enable); 1918 + int genphy_loopback(struct phy_device *phydev, bool enable, int speed); 1927 1919 int genphy_soft_reset(struct phy_device *phydev); 1928 1920 irqreturn_t genphy_handle_interrupt_no_ack(struct phy_device *phydev); 1929 1921 ··· 1965 1957 int genphy_c45_read_status(struct phy_device *phydev); 1966 1958 int genphy_c45_baset1_read_status(struct phy_device *phydev); 1967 1959 int genphy_c45_config_aneg(struct phy_device *phydev); 1968 - int genphy_c45_loopback(struct phy_device *phydev, bool enable); 1960 + int genphy_c45_loopback(struct phy_device *phydev, bool enable, int speed); 1969 1961 int genphy_c45_pma_resume(struct phy_device *phydev); 1970 1962 int genphy_c45_pma_suspend(struct phy_device *phydev); 1971 1963 int genphy_c45_fast_retrain(struct phy_device *phydev, bool enable);