[PATCH net-next v9 11/11] net: phy: ncn26000: Support for loopback

From: Selvamani Rajagopal via B4 Relay

Date: Fri Oct 09 2026 - 13:24:08 EST


From: Selvamani Rajagopal <Selvamani.Rajagopal@xxxxxxxxxx>

Adding loopback support for S2500 internal PHY to help running
loopback test through ethtool.

This instance requires only manipulating the loopback bit in
the PHY, without setting the speed. So, added a generic helper
function to modify loopback bit of the PHY only. This will
help the PHY drivers, like 10Base-T1S or any other PHYs that
want to handle loopback bit only.

Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@xxxxxxxxxx>

---
changes in v9
- replaced "fixed_speed" with "no_speed" for the API.
- Improved the comments to be specific about not setting the
speed.
changes in v8
- No code change. Changes were in two different patches.
Merged into one.
changes in v7
- No change
changes in v6
- No change
changes in v5
- No change
changes in v4
- Moved the onsemi implementation to a new, genphy API to
make it vendor agnostic.
changes in v3
- Addd loopback functionality for onsemi's PHY driver
- First patch
---
drivers/net/phy/dp83867.c | 11 +----------
drivers/net/phy/ncn26000.c | 1 +
drivers/net/phy/phy_device.c | 22 ++++++++++++++++++++++
include/linux/phy.h | 2 ++
4 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index a3cd5cb11ea0..a62667606422 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -1058,15 +1058,6 @@ static void dp83867_link_change_notify(struct phy_device *phydev)
}
}

-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);
-}
-
static int
dp83867_led_brightness_set(struct phy_device *phydev,
u8 index, enum led_brightness brightness)
@@ -1292,7 +1283,7 @@ static struct phy_driver dp83867_driver[] = {
.resume = dp83867_resume,

.link_change_notify = dp83867_link_change_notify,
- .set_loopback = dp83867_loopback,
+ .set_loopback = genphy_loopback_no_speed,

.led_brightness_set = dp83867_led_brightness_set,
.led_hw_is_supported = dp83867_led_hw_is_supported,
diff --git a/drivers/net/phy/ncn26000.c b/drivers/net/phy/ncn26000.c
index a33ebb2a7ece..de0fafda3876 100644
--- a/drivers/net/phy/ncn26000.c
+++ b/drivers/net/phy/ncn26000.c
@@ -178,6 +178,7 @@ static struct phy_driver ncn26000_driver[] = {
.config_aneg = ncn26000_config_aneg,
.read_status = ncn26000_read_status,
.handle_interrupt = ncn26000_handle_interrupt,
+ .set_loopback = genphy_loopback_no_speed,
.set_plca_cfg = ncn26000_c45_plca_set_cfg,
.get_plca_cfg = genphy_c45_plca_get_cfg,
.get_plca_status = genphy_c45_plca_get_status,
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index bfce8b893644..03419f645587 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -2791,6 +2791,28 @@ int genphy_resume(struct phy_device *phydev)
}
EXPORT_SYMBOL(genphy_resume);

+/**
+ * genphy_loopback_no_speed - Helper to modify the PHY loopback mode
+ * without setting speed. Some PHYs, like 10Base-T1S, don't support
+ * auto-negotiation and therefore setting the speed isn't supported.
+ *
+ * @phydev: The phy_device struct
+ * @enable: Flag to enable or disable the PHY level loopback.
+ * @speed: Speed setting. Not expected to be set. Error if it is set.
+ *
+ * Returns: 0 if successful, negative error code on failure.
+ */
+int genphy_loopback_no_speed(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);
+}
+EXPORT_SYMBOL(genphy_loopback_no_speed);
+
int genphy_loopback(struct phy_device *phydev, bool enable, int speed)
{
if (enable) {
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 7c5098a0dd6c..ea3505e49316 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -2307,6 +2307,8 @@ int genphy_read_status(struct phy_device *phydev);
int genphy_read_master_slave(struct phy_device *phydev);
int genphy_suspend(struct phy_device *phydev);
int genphy_resume(struct phy_device *phydev);
+int genphy_loopback_no_speed(struct phy_device *phydev, bool enable,
+ int speed);
int genphy_loopback(struct phy_device *phydev, bool enable, int speed);
int genphy_soft_reset(struct phy_device *phydev);
irqreturn_t genphy_handle_interrupt_no_ack(struct phy_device *phydev);

--
2.43.0