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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'support-loopback-mode-speed-selection'

Gerhard Engleder says:

====================
Support loopback mode speed selection

Previously to commit 6ff3cddc365b ("net: phylib: do not disable autoneg
for fixed speeds >= 1G") it was possible to select the speed of the
loopback mode by configuring a fixed speed before enabling the loopback
mode. Now autoneg is always enabled for >= 1G and a fixed speed of >= 1G
requires successful autoneg. Thus, the speed of the loopback mode depends
on the link partner for >= 1G. There is no technical reason to depend on
the link partner for loopback mode. With this behavior the loopback mode
is less useful for testing.

Allow PHYs to support optional speed selection for the loopback mode.
This support is implemented for the generic loopback support and for PHY
drivers, which obviously support speed selection for loopback mode.
Additionally, loopback support according to the data sheet is added to
the KSZ9031 PHY.

Extend phy_loopback() to signal link up and down if speed changes,
because a new link speed requires link up signalling.

Use this loopback speed selection in the tsnep driver to select the
loopback mode speed depending the previously active speed. User space
tests with 100 Mbps and 1 Gbps loopback are possible again.
====================

Link: https://patch.msgid.link/20250312203010.47429-1-gerhard@engleder-embedded.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+212 -110
+10 -11
drivers/net/ethernet/engleder/tsnep_main.c
··· 221 221 222 222 static int tsnep_phy_loopback(struct tsnep_adapter *adapter, bool enable) 223 223 { 224 - int retval; 224 + int speed; 225 225 226 - retval = phy_loopback(adapter->phydev, enable); 227 - 228 - /* PHY link state change is not signaled if loopback is enabled, it 229 - * would delay a working loopback anyway, let's ensure that loopback 230 - * is working immediately by setting link mode directly 231 - */ 232 - if (!retval && enable) { 233 - netif_carrier_on(adapter->netdev); 234 - tsnep_set_link_mode(adapter); 226 + if (enable) { 227 + if (adapter->phydev->autoneg == AUTONEG_DISABLE && 228 + adapter->phydev->speed == SPEED_100) 229 + speed = SPEED_100; 230 + else 231 + speed = SPEED_1000; 232 + } else { 233 + speed = 0; 235 234 } 236 235 237 - return retval; 236 + return phy_loopback(adapter->phydev, enable, speed); 238 237 } 239 238 240 239 static int tsnep_phy_open(struct tsnep_adapter *adapter)
+2 -2
drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
··· 266 266 if (err) 267 267 goto out; 268 268 269 - err = phy_loopback(phy_dev, true); 269 + err = phy_loopback(phy_dev, true, 0); 270 270 } else { 271 - err = phy_loopback(phy_dev, false); 271 + err = phy_loopback(phy_dev, false, 0); 272 272 if (err) 273 273 goto out; 274 274
+2 -2
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
··· 7875 7875 if (ret) 7876 7876 return ret; 7877 7877 7878 - return phy_loopback(phydev, true); 7878 + return phy_loopback(phydev, true, 0); 7879 7879 } 7880 7880 7881 7881 static int hclge_disable_phy_loopback(struct hclge_dev *hdev, ··· 7883 7883 { 7884 7884 int ret; 7885 7885 7886 - ret = phy_loopback(phydev, false); 7886 + ret = phy_loopback(phydev, false, 0); 7887 7887 if (ret) 7888 7888 return ret; 7889 7889
+1 -1
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
··· 258 258 if (!phydev) 259 259 return; 260 260 261 - phy_loopback(phydev, false); 261 + phy_loopback(phydev, false, 0); 262 262 263 263 phy_start(phydev); 264 264 }
+4 -4
drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
··· 382 382 if (!priv->dev->phydev) 383 383 return -EOPNOTSUPP; 384 384 385 - ret = phy_loopback(priv->dev->phydev, true); 385 + ret = phy_loopback(priv->dev->phydev, true, 0); 386 386 if (ret) 387 387 return ret; 388 388 389 389 attr.dst = priv->dev->dev_addr; 390 390 ret = __stmmac_test_loopback(priv, &attr); 391 391 392 - phy_loopback(priv->dev->phydev, false); 392 + phy_loopback(priv->dev->phydev, false, 0); 393 393 return ret; 394 394 } 395 395 ··· 1985 1985 case STMMAC_LOOPBACK_PHY: 1986 1986 ret = -EOPNOTSUPP; 1987 1987 if (dev->phydev) 1988 - ret = phy_loopback(dev->phydev, true); 1988 + ret = phy_loopback(dev->phydev, true, 0); 1989 1989 if (!ret) 1990 1990 break; 1991 1991 fallthrough; ··· 2018 2018 case STMMAC_LOOPBACK_PHY: 2019 2019 ret = -EOPNOTSUPP; 2020 2020 if (dev->phydev) 2021 - ret = phy_loopback(dev->phydev, false); 2021 + ret = phy_loopback(dev->phydev, false, 0); 2022 2022 if (!ret) 2023 2023 break; 2024 2024 fallthrough;
+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 }
+37 -37
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 + u16 bmcr_ctl, mscr2_ctl = 0; 2136 2137 int err; 2137 2138 2138 - if (enable) { 2139 - u16 bmcr_ctl, mscr2_ctl = 0; 2139 + if (!enable) 2140 + return genphy_loopback(phydev, enable, 0); 2140 2141 2141 - bmcr_ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); 2142 + if (speed == SPEED_10 || speed == SPEED_100 || speed == SPEED_1000) 2143 + phydev->speed = speed; 2144 + else if (speed) 2145 + return -EINVAL; 2142 2146 2143 - err = phy_write(phydev, MII_BMCR, bmcr_ctl); 2144 - if (err < 0) 2145 - return err; 2147 + bmcr_ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); 2146 2148 2147 - if (phydev->speed == SPEED_1000) 2148 - mscr2_ctl = BMCR_SPEED1000; 2149 - else if (phydev->speed == SPEED_100) 2150 - mscr2_ctl = BMCR_SPEED100; 2151 - 2152 - err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE, 2153 - MII_88E1510_MSCR_2, BMCR_SPEED1000 | 2154 - BMCR_SPEED100, mscr2_ctl); 2155 - if (err < 0) 2156 - return err; 2157 - 2158 - /* Need soft reset to have speed configuration takes effect */ 2159 - err = genphy_soft_reset(phydev); 2160 - if (err < 0) 2161 - return err; 2162 - 2163 - err = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 2164 - BMCR_LOOPBACK); 2165 - 2166 - if (!err) { 2167 - /* It takes some time for PHY device to switch 2168 - * into/out-of loopback mode. 2169 - */ 2170 - msleep(1000); 2171 - } 2149 + err = phy_write(phydev, MII_BMCR, bmcr_ctl); 2150 + if (err < 0) 2172 2151 return err; 2173 - } else { 2174 - err = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 0); 2175 - if (err < 0) 2176 - return err; 2177 2152 2178 - return phy_config_aneg(phydev); 2153 + if (phydev->speed == SPEED_1000) 2154 + mscr2_ctl = BMCR_SPEED1000; 2155 + else if (phydev->speed == SPEED_100) 2156 + mscr2_ctl = BMCR_SPEED100; 2157 + 2158 + err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE, 2159 + MII_88E1510_MSCR_2, BMCR_SPEED1000 | 2160 + BMCR_SPEED100, mscr2_ctl); 2161 + if (err < 0) 2162 + return err; 2163 + 2164 + /* Need soft reset to have speed configuration takes effect */ 2165 + err = genphy_soft_reset(phydev); 2166 + if (err < 0) 2167 + return err; 2168 + 2169 + err = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 2170 + BMCR_LOOPBACK); 2171 + 2172 + if (!err) { 2173 + /* 2174 + * It takes some time for PHY device to switch into loopback 2175 + * mode. 2176 + */ 2177 + msleep(1000); 2179 2178 } 2179 + return err; 2180 2180 } 2181 2181 2182 2182 static int marvell_vct5_wait_complete(struct phy_device *phydev)
+24
drivers/net/phy/micrel.c
··· 1032 1032 #define MII_KSZ9031RN_EDPD 0x23 1033 1033 #define MII_KSZ9031RN_EDPD_ENABLE BIT(0) 1034 1034 1035 + static int ksz9031_set_loopback(struct phy_device *phydev, bool enable, 1036 + int speed) 1037 + { 1038 + u16 ctl = BMCR_LOOPBACK; 1039 + int val; 1040 + 1041 + if (!enable) 1042 + return genphy_loopback(phydev, enable, 0); 1043 + 1044 + if (speed == SPEED_10 || speed == SPEED_100 || speed == SPEED_1000) 1045 + phydev->speed = speed; 1046 + else if (speed) 1047 + return -EINVAL; 1048 + phydev->duplex = DUPLEX_FULL; 1049 + 1050 + ctl |= mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); 1051 + 1052 + phy_write(phydev, MII_BMCR, ctl); 1053 + 1054 + return phy_read_poll_timeout(phydev, MII_BMSR, val, val & BMSR_LSTATUS, 1055 + 5000, 500000, true); 1056 + } 1057 + 1035 1058 static int ksz9031_of_load_skew_values(struct phy_device *phydev, 1036 1059 const struct device_node *of_node, 1037 1060 u16 reg, size_t field_sz, ··· 5588 5565 .resume = kszphy_resume, 5589 5566 .cable_test_start = ksz9x31_cable_test_start, 5590 5567 .cable_test_get_status = ksz9x31_cable_test_get_status, 5568 + .set_loopback = ksz9031_set_loopback, 5591 5569 }, { 5592 5570 .phy_id = PHY_ID_LAN8814, 5593 5571 .phy_id_mask = MICREL_PHY_ID_MASK,
+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);
+87
drivers/net/phy/phy.c
··· 1708 1708 EXPORT_SYMBOL(phy_mac_interrupt); 1709 1709 1710 1710 /** 1711 + * phy_loopback - Configure loopback mode of PHY 1712 + * @phydev: target phy_device struct 1713 + * @enable: enable or disable loopback mode 1714 + * @speed: enable loopback mode with speed 1715 + * 1716 + * Configure loopback mode of PHY and signal link down and link up if speed is 1717 + * changing. 1718 + * 1719 + * Return: 0 on success, negative error code on failure. 1720 + */ 1721 + int phy_loopback(struct phy_device *phydev, bool enable, int speed) 1722 + { 1723 + bool link_up = false; 1724 + int ret = 0; 1725 + 1726 + if (!phydev->drv) 1727 + return -EIO; 1728 + 1729 + mutex_lock(&phydev->lock); 1730 + 1731 + if (enable && phydev->loopback_enabled) { 1732 + ret = -EBUSY; 1733 + goto out; 1734 + } 1735 + 1736 + if (!enable && !phydev->loopback_enabled) { 1737 + ret = -EINVAL; 1738 + goto out; 1739 + } 1740 + 1741 + if (enable) { 1742 + /* 1743 + * Link up is signaled with a defined speed. If speed changes, 1744 + * then first link down and after that link up needs to be 1745 + * signaled. 1746 + */ 1747 + if (phydev->link && phydev->state == PHY_RUNNING) { 1748 + /* link is up and signaled */ 1749 + if (speed && phydev->speed != speed) { 1750 + /* signal link down and up for new speed */ 1751 + phydev->link = false; 1752 + phydev->state = PHY_NOLINK; 1753 + phy_link_down(phydev); 1754 + 1755 + link_up = true; 1756 + } 1757 + } else { 1758 + /* link is not signaled */ 1759 + if (speed) { 1760 + /* signal link up for new speed */ 1761 + link_up = true; 1762 + } 1763 + } 1764 + } 1765 + 1766 + if (phydev->drv->set_loopback) 1767 + ret = phydev->drv->set_loopback(phydev, enable, speed); 1768 + else 1769 + ret = genphy_loopback(phydev, enable, speed); 1770 + 1771 + if (ret) { 1772 + if (enable) { 1773 + /* try to restore link if enabling loopback fails */ 1774 + if (phydev->drv->set_loopback) 1775 + phydev->drv->set_loopback(phydev, false, 0); 1776 + else 1777 + genphy_loopback(phydev, false, 0); 1778 + } 1779 + 1780 + goto out; 1781 + } 1782 + 1783 + if (link_up) { 1784 + phydev->link = true; 1785 + phydev->state = PHY_RUNNING; 1786 + phy_link_up(phydev); 1787 + } 1788 + 1789 + phydev->loopback_enabled = enable; 1790 + 1791 + out: 1792 + mutex_unlock(&phydev->lock); 1793 + return ret; 1794 + } 1795 + EXPORT_SYMBOL(phy_loopback); 1796 + 1797 + /** 1711 1798 * phy_eee_tx_clock_stop_capable() - indicate whether the MAC can stop tx clock 1712 1799 * @phydev: target phy_device struct 1713 1800 *
+7 -36
drivers/net/phy/phy_device.c
··· 1818 1818 } 1819 1819 EXPORT_SYMBOL(phy_resume); 1820 1820 1821 - int phy_loopback(struct phy_device *phydev, bool enable) 1822 - { 1823 - int ret = 0; 1824 - 1825 - if (!phydev->drv) 1826 - return -EIO; 1827 - 1828 - mutex_lock(&phydev->lock); 1829 - 1830 - if (enable && phydev->loopback_enabled) { 1831 - ret = -EBUSY; 1832 - goto out; 1833 - } 1834 - 1835 - if (!enable && !phydev->loopback_enabled) { 1836 - ret = -EINVAL; 1837 - goto out; 1838 - } 1839 - 1840 - if (phydev->drv->set_loopback) 1841 - ret = phydev->drv->set_loopback(phydev, enable); 1842 - else 1843 - ret = genphy_loopback(phydev, enable); 1844 - 1845 - if (ret) 1846 - goto out; 1847 - 1848 - phydev->loopback_enabled = enable; 1849 - 1850 - out: 1851 - mutex_unlock(&phydev->lock); 1852 - return ret; 1853 - } 1854 - EXPORT_SYMBOL(phy_loopback); 1855 - 1856 1821 /** 1857 1822 * phy_reset_after_clk_enable - perform a PHY reset if needed 1858 1823 * @phydev: target phy_device struct ··· 2575 2610 } 2576 2611 EXPORT_SYMBOL(genphy_resume); 2577 2612 2578 - int genphy_loopback(struct phy_device *phydev, bool enable) 2613 + int genphy_loopback(struct phy_device *phydev, bool enable, int speed) 2579 2614 { 2580 2615 if (enable) { 2581 2616 u16 ctl = BMCR_LOOPBACK; 2582 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; 2583 2624 2584 2625 ctl |= mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); 2585 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
+13 -5
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 */ ··· 1808 1800 int phy_suspend(struct phy_device *phydev); 1809 1801 int phy_resume(struct phy_device *phydev); 1810 1802 int __phy_resume(struct phy_device *phydev); 1811 - int phy_loopback(struct phy_device *phydev, bool enable); 1803 + int phy_loopback(struct phy_device *phydev, bool enable, int speed); 1812 1804 int phy_sfp_connect_phy(void *upstream, struct phy_device *phy); 1813 1805 void phy_sfp_disconnect_phy(void *upstream, struct phy_device *phy); 1814 1806 void phy_sfp_attach(void *upstream, struct sfp_bus *bus); ··· 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);
+2 -2
net/core/selftests.c
··· 299 299 if (!ndev->phydev) 300 300 return -EOPNOTSUPP; 301 301 302 - return phy_loopback(ndev->phydev, true); 302 + return phy_loopback(ndev->phydev, true, 0); 303 303 } 304 304 305 305 static int net_test_phy_loopback_disable(struct net_device *ndev) ··· 307 307 if (!ndev->phydev) 308 308 return -EOPNOTSUPP; 309 309 310 - return phy_loopback(ndev->phydev, false); 310 + return phy_loopback(ndev->phydev, false, 0); 311 311 } 312 312 313 313 static int net_test_phy_loopback_udp(struct net_device *ndev)