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

net: usb: smsc95xx: add support for ethtool pause parameters

Implement ethtool .get_pauseparam and .set_pauseparam handlers for
configuring flow control on smsc95xx. The driver now supports enabling
or disabling transmit and receive pause frames, with or without
autonegotiation. Pause settings are applied during link-up based on
current PHY state and user configuration.

Previously, the driver used phy_get_pause() during link-up handling,
but lacked initialization and an ethtool interface to configure pause
modes. As a result, flow control support was effectively non-functional.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20250718075157.297923-1-o.rempel@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Oleksij Rempel and committed by
Jakub Kicinski
c521b8c9 0b3b3ba1

+69 -3
+69 -3
drivers/net/usb/smsc95xx.c
··· 63 63 u32 hash_hi; 64 64 u32 hash_lo; 65 65 u32 wolopts; 66 + bool pause_rx; 67 + bool pause_tx; 68 + bool pause_autoneg; 66 69 spinlock_t mac_cr_lock; 67 70 u8 features; 68 71 u8 suspend_flags; ··· 540 537 541 538 static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev) 542 539 { 543 - u32 flow = 0, afc_cfg; 544 540 struct smsc95xx_priv *pdata = dev->driver_priv; 545 - bool tx_pause, rx_pause; 541 + u32 flow = 0, afc_cfg; 546 542 547 543 int ret = smsc95xx_read_reg(dev, AFC_CFG, &afc_cfg); 548 544 if (ret < 0) 549 545 return ret; 550 546 551 547 if (pdata->phydev->duplex == DUPLEX_FULL) { 552 - phy_get_pause(pdata->phydev, &tx_pause, &rx_pause); 548 + bool tx_pause, rx_pause; 549 + 550 + if (pdata->phydev->autoneg == AUTONEG_ENABLE && 551 + pdata->pause_autoneg) { 552 + phy_get_pause(pdata->phydev, &tx_pause, &rx_pause); 553 + } else { 554 + tx_pause = pdata->pause_tx; 555 + rx_pause = pdata->pause_rx; 556 + } 553 557 554 558 if (rx_pause) 555 559 flow = 0xFFFF0002; ··· 782 772 } 783 773 } 784 774 775 + static void smsc95xx_get_pauseparam(struct net_device *ndev, 776 + struct ethtool_pauseparam *pause) 777 + { 778 + struct smsc95xx_priv *pdata; 779 + struct usbnet *dev; 780 + 781 + dev = netdev_priv(ndev); 782 + pdata = dev->driver_priv; 783 + 784 + pause->autoneg = pdata->pause_autoneg; 785 + pause->rx_pause = pdata->pause_rx; 786 + pause->tx_pause = pdata->pause_tx; 787 + } 788 + 789 + static int smsc95xx_set_pauseparam(struct net_device *ndev, 790 + struct ethtool_pauseparam *pause) 791 + { 792 + bool pause_autoneg_rx, pause_autoneg_tx; 793 + struct smsc95xx_priv *pdata; 794 + struct phy_device *phydev; 795 + struct usbnet *dev; 796 + 797 + dev = netdev_priv(ndev); 798 + pdata = dev->driver_priv; 799 + phydev = ndev->phydev; 800 + 801 + if (!phydev) 802 + return -ENODEV; 803 + 804 + pdata->pause_rx = pause->rx_pause; 805 + pdata->pause_tx = pause->tx_pause; 806 + pdata->pause_autoneg = pause->autoneg; 807 + 808 + if (pause->autoneg) { 809 + pause_autoneg_rx = pause->rx_pause; 810 + pause_autoneg_tx = pause->tx_pause; 811 + } else { 812 + pause_autoneg_rx = false; 813 + pause_autoneg_tx = false; 814 + } 815 + 816 + phy_set_asym_pause(ndev->phydev, pause_autoneg_rx, pause_autoneg_tx); 817 + if (phydev->link && (!pause->autoneg || 818 + phydev->autoneg == AUTONEG_DISABLE)) 819 + smsc95xx_mac_update_fullduplex(dev); 820 + 821 + return 0; 822 + } 823 + 785 824 static const struct ethtool_ops smsc95xx_ethtool_ops = { 786 825 .get_link = smsc95xx_get_link, 787 826 .nway_reset = phy_ethtool_nway_reset, ··· 850 791 .self_test = net_selftest, 851 792 .get_strings = smsc95xx_ethtool_get_strings, 852 793 .get_sset_count = smsc95xx_ethtool_get_sset_count, 794 + .get_pauseparam = smsc95xx_get_pauseparam, 795 + .set_pauseparam = smsc95xx_set_pauseparam, 853 796 }; 854 797 855 798 static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) ··· 1287 1226 dev->net->min_mtu = ETH_MIN_MTU; 1288 1227 dev->net->max_mtu = ETH_DATA_LEN; 1289 1228 dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; 1229 + 1230 + pdata->pause_tx = true; 1231 + pdata->pause_rx = true; 1232 + pdata->pause_autoneg = true; 1233 + phy_support_asym_pause(pdata->phydev); 1290 1234 1291 1235 ret = phy_connect_direct(dev->net, pdata->phydev, 1292 1236 &smsc95xx_handle_link_change,