Skip to content

Commit

Permalink
Merge branch 'support-loopback-mode-speed-selection'
Browse files Browse the repository at this point in the history
Gerhard Engleder says:

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

Previously to commit 6ff3cdd ("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>
  • Loading branch information
Paolo Abeni committed Mar 20, 2025
2 parents 3d97da0 + 163d744 commit f0417e0
Show file tree
Hide file tree
Showing 16 changed files with 209 additions and 107 deletions.
21 changes: 10 additions & 11 deletions drivers/net/ethernet/engleder/tsnep_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,20 +221,19 @@ static void tsnep_phy_link_status_change(struct net_device *netdev)

static int tsnep_phy_loopback(struct tsnep_adapter *adapter, bool enable)
{
int retval;

retval = phy_loopback(adapter->phydev, enable);
int speed;

/* PHY link state change is not signaled if loopback is enabled, it
* would delay a working loopback anyway, let's ensure that loopback
* is working immediately by setting link mode directly
*/
if (!retval && enable) {
netif_carrier_on(adapter->netdev);
tsnep_set_link_mode(adapter);
if (enable) {
if (adapter->phydev->autoneg == AUTONEG_DISABLE &&
adapter->phydev->speed == SPEED_100)
speed = SPEED_100;
else
speed = SPEED_1000;
} else {
speed = 0;
}

return retval;
return phy_loopback(adapter->phydev, enable, speed);
}

static int tsnep_phy_open(struct tsnep_adapter *adapter)
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,9 @@ static int hns_nic_config_phy_loopback(struct phy_device *phy_dev, u8 en)
if (err)
goto out;

err = phy_loopback(phy_dev, true);
err = phy_loopback(phy_dev, true, 0);
} else {
err = phy_loopback(phy_dev, false);
err = phy_loopback(phy_dev, false, 0);
if (err)
goto out;

Expand Down
4 changes: 2 additions & 2 deletions drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7875,15 +7875,15 @@ static int hclge_enable_phy_loopback(struct hclge_dev *hdev,
if (ret)
return ret;

return phy_loopback(phydev, true);
return phy_loopback(phydev, true, 0);
}

static int hclge_disable_phy_loopback(struct hclge_dev *hdev,
struct phy_device *phydev)
{
int ret;

ret = phy_loopback(phydev, false);
ret = phy_loopback(phydev, false, 0);
if (ret)
return ret;

Expand Down
2 changes: 1 addition & 1 deletion drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ void hclge_mac_start_phy(struct hclge_dev *hdev)
if (!phydev)
return;

phy_loopback(phydev, false);
phy_loopback(phydev, false, 0);

phy_start(phydev);
}
Expand Down
8 changes: 4 additions & 4 deletions drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,14 +382,14 @@ static int stmmac_test_phy_loopback(struct stmmac_priv *priv)
if (!priv->dev->phydev)
return -EOPNOTSUPP;

ret = phy_loopback(priv->dev->phydev, true);
ret = phy_loopback(priv->dev->phydev, true, 0);
if (ret)
return ret;

attr.dst = priv->dev->dev_addr;
ret = __stmmac_test_loopback(priv, &attr);

phy_loopback(priv->dev->phydev, false);
phy_loopback(priv->dev->phydev, false, 0);
return ret;
}

Expand Down Expand Up @@ -1985,7 +1985,7 @@ void stmmac_selftest_run(struct net_device *dev,
case STMMAC_LOOPBACK_PHY:
ret = -EOPNOTSUPP;
if (dev->phydev)
ret = phy_loopback(dev->phydev, true);
ret = phy_loopback(dev->phydev, true, 0);
if (!ret)
break;
fallthrough;
Expand Down Expand Up @@ -2018,7 +2018,7 @@ void stmmac_selftest_run(struct net_device *dev,
case STMMAC_LOOPBACK_PHY:
ret = -EOPNOTSUPP;
if (dev->phydev)
ret = phy_loopback(dev->phydev, false);
ret = phy_loopback(dev->phydev, false, 0);
if (!ret)
break;
fallthrough;
Expand Down
5 changes: 4 additions & 1 deletion drivers/net/phy/adin1100.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,11 @@ static int adin_resume(struct phy_device *phydev)
return adin_set_powerdown_mode(phydev, false);
}

static int adin_set_loopback(struct phy_device *phydev, bool enable)
static int adin_set_loopback(struct phy_device *phydev, bool enable, int speed)
{
if (enable && speed)
return -EOPNOTSUPP;

if (enable)
return phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_10T1L_CTRL,
BMCR_LOOPBACK);
Expand Down
5 changes: 4 additions & 1 deletion drivers/net/phy/dp83867.c
Original file line number Diff line number Diff line change
Expand Up @@ -1009,8 +1009,11 @@ static void dp83867_link_change_notify(struct phy_device *phydev)
}
}

static int dp83867_loopback(struct phy_device *phydev, bool enable)
static int dp83867_loopback(struct phy_device *phydev, bool enable, int speed)
{
if (enable && speed)
return -EOPNOTSUPP;

return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
enable ? BMCR_LOOPBACK : 0);
}
Expand Down
68 changes: 34 additions & 34 deletions drivers/net/phy/marvell.c
Original file line number Diff line number Diff line change
Expand Up @@ -2131,52 +2131,52 @@ static void marvell_get_stats_simple(struct phy_device *phydev,
data[i] = marvell_get_stat_simple(phydev, i);
}

static int m88e1510_loopback(struct phy_device *phydev, bool enable)
static int m88e1510_loopback(struct phy_device *phydev, bool enable, int speed)
{
u16 bmcr_ctl, mscr2_ctl = 0;
int err;

if (enable) {
u16 bmcr_ctl, mscr2_ctl = 0;
if (!enable)
return genphy_loopback(phydev, enable, 0);

bmcr_ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);

err = phy_write(phydev, MII_BMCR, bmcr_ctl);
if (err < 0)
return err;
if (speed == SPEED_10 || speed == SPEED_100 || speed == SPEED_1000)
phydev->speed = speed;
else if (speed)
return -EINVAL;

if (phydev->speed == SPEED_1000)
mscr2_ctl = BMCR_SPEED1000;
else if (phydev->speed == SPEED_100)
mscr2_ctl = BMCR_SPEED100;
bmcr_ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);

err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
MII_88E1510_MSCR_2, BMCR_SPEED1000 |
BMCR_SPEED100, mscr2_ctl);
if (err < 0)
return err;
err = phy_write(phydev, MII_BMCR, bmcr_ctl);
if (err < 0)
return err;

/* Need soft reset to have speed configuration takes effect */
err = genphy_soft_reset(phydev);
if (err < 0)
return err;
if (phydev->speed == SPEED_1000)
mscr2_ctl = BMCR_SPEED1000;
else if (phydev->speed == SPEED_100)
mscr2_ctl = BMCR_SPEED100;

err = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
BMCR_LOOPBACK);
err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
MII_88E1510_MSCR_2, BMCR_SPEED1000 |
BMCR_SPEED100, mscr2_ctl);
if (err < 0)
return err;

if (!err) {
/* It takes some time for PHY device to switch
* into/out-of loopback mode.
*/
msleep(1000);
}
/* Need soft reset to have speed configuration takes effect */
err = genphy_soft_reset(phydev);
if (err < 0)
return err;
} else {
err = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 0);
if (err < 0)
return err;

return phy_config_aneg(phydev);
err = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
BMCR_LOOPBACK);

if (!err) {
/*
* It takes some time for PHY device to switch into loopback
* mode.
*/
msleep(1000);
}
return err;
}

static int marvell_vct5_wait_complete(struct phy_device *phydev)
Expand Down
24 changes: 24 additions & 0 deletions drivers/net/phy/micrel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,29 @@ static int ksz9021_config_init(struct phy_device *phydev)
#define MII_KSZ9031RN_EDPD 0x23
#define MII_KSZ9031RN_EDPD_ENABLE BIT(0)

static int ksz9031_set_loopback(struct phy_device *phydev, bool enable,
int speed)
{
u16 ctl = BMCR_LOOPBACK;
int val;

if (!enable)
return genphy_loopback(phydev, enable, 0);

if (speed == SPEED_10 || speed == SPEED_100 || speed == SPEED_1000)
phydev->speed = speed;
else if (speed)
return -EINVAL;
phydev->duplex = DUPLEX_FULL;

ctl |= mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);

phy_write(phydev, MII_BMCR, ctl);

return phy_read_poll_timeout(phydev, MII_BMSR, val, val & BMSR_LSTATUS,
5000, 500000, true);
}

static int ksz9031_of_load_skew_values(struct phy_device *phydev,
const struct device_node *of_node,
u16 reg, size_t field_sz,
Expand Down Expand Up @@ -5565,6 +5588,7 @@ static struct phy_driver ksphy_driver[] = {
.resume = kszphy_resume,
.cable_test_start = ksz9x31_cable_test_start,
.cable_test_get_status = ksz9x31_cable_test_get_status,
.set_loopback = ksz9031_set_loopback,
}, {
.phy_id = PHY_ID_LAN8814,
.phy_id_mask = MICREL_PHY_ID_MASK,
Expand Down
11 changes: 7 additions & 4 deletions drivers/net/phy/mxl-gpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ static void gpy_get_wol(struct phy_device *phydev,
wol->wolopts = priv->wolopts;
}

static int gpy_loopback(struct phy_device *phydev, bool enable)
static int gpy_loopback(struct phy_device *phydev, bool enable, int speed)
{
struct gpy_priv *priv = phydev->priv;
u16 set = 0;
Expand All @@ -822,6 +822,9 @@ static int gpy_loopback(struct phy_device *phydev, bool enable)
if (enable) {
u64 now = get_jiffies_64();

if (speed)
return -EOPNOTSUPP;

/* wait until 3 seconds from last disable */
if (time_before64(now, priv->lb_dis_to))
msleep(jiffies64_to_msecs(priv->lb_dis_to - now));
Expand All @@ -845,15 +848,15 @@ static int gpy_loopback(struct phy_device *phydev, bool enable)
return 0;
}

static int gpy115_loopback(struct phy_device *phydev, bool enable)
static int gpy115_loopback(struct phy_device *phydev, bool enable, int speed)
{
struct gpy_priv *priv = phydev->priv;

if (enable)
return gpy_loopback(phydev, enable);
return gpy_loopback(phydev, enable, speed);

if (priv->fw_minor > 0x76)
return gpy_loopback(phydev, 0);
return gpy_loopback(phydev, 0, 0);

return genphy_soft_reset(phydev);
}
Expand Down
5 changes: 4 additions & 1 deletion drivers/net/phy/phy-c45.c
Original file line number Diff line number Diff line change
Expand Up @@ -1228,8 +1228,11 @@ int gen10g_config_aneg(struct phy_device *phydev)
}
EXPORT_SYMBOL_GPL(gen10g_config_aneg);

int genphy_c45_loopback(struct phy_device *phydev, bool enable)
int genphy_c45_loopback(struct phy_device *phydev, bool enable, int speed)
{
if (enable && speed)
return -EOPNOTSUPP;

return phy_modify_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1,
MDIO_PCS_CTRL1_LOOPBACK,
enable ? MDIO_PCS_CTRL1_LOOPBACK : 0);
Expand Down
Loading

0 comments on commit f0417e0

Please sign in to comment.