diff options
author | Gerhard Engleder <gerhard@engleder-embedded.com> | 2025-03-12 21:30:07 +0100 |
---|---|---|
committer | Paolo Abeni <pabeni@redhat.com> | 2025-03-20 08:45:08 +0100 |
commit | 0d60fd50328a96a901b09ed653704ce7f41d15ce (patch) | |
tree | 597298dd516246bde48b7ea7384d9bffaa7d7b2e /drivers/net/phy | |
parent | 45456e38c44eda2f1285601398fd289b3cec7002 (diff) | |
download | lwn-0d60fd50328a96a901b09ed653704ce7f41d15ce.tar.gz lwn-0d60fd50328a96a901b09ed653704ce7f41d15ce.zip |
net: phy: Support speed selection for PHY loopback
phy_loopback() leaves it to the PHY driver to select the speed of the
loopback mode. Thus, the speed of the loopback mode depends on the PHY
driver in use.
Add support for speed selection to phy_loopback() to enable loopback
with defined speeds. Ensure that link up is signaled if speed changes
as speed is not allowed to change during link up. Link down and up is
necessary for a new speed.
Signed-off-by: Gerhard Engleder <gerhard@engleder-embedded.com>
Link: https://patch.msgid.link/20250312203010.47429-3-gerhard@engleder-embedded.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/phy.c | 87 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 35 |
2 files changed, 87 insertions, 35 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 562acde89224..13df28445f02 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1708,6 +1708,93 @@ void phy_mac_interrupt(struct phy_device *phydev) EXPORT_SYMBOL(phy_mac_interrupt); /** + * phy_loopback - Configure loopback mode of PHY + * @phydev: target phy_device struct + * @enable: enable or disable loopback mode + * @speed: enable loopback mode with speed + * + * Configure loopback mode of PHY and signal link down and link up if speed is + * changing. + * + * Return: 0 on success, negative error code on failure. + */ +int phy_loopback(struct phy_device *phydev, bool enable, int speed) +{ + bool link_up = false; + int ret = 0; + + if (!phydev->drv) + return -EIO; + + mutex_lock(&phydev->lock); + + if (enable && phydev->loopback_enabled) { + ret = -EBUSY; + goto out; + } + + if (!enable && !phydev->loopback_enabled) { + ret = -EINVAL; + goto out; + } + + if (enable) { + /* + * Link up is signaled with a defined speed. If speed changes, + * then first link down and after that link up needs to be + * signaled. + */ + if (phydev->link && phydev->state == PHY_RUNNING) { + /* link is up and signaled */ + if (speed && phydev->speed != speed) { + /* signal link down and up for new speed */ + phydev->link = false; + phydev->state = PHY_NOLINK; + phy_link_down(phydev); + + link_up = true; + } + } else { + /* link is not signaled */ + if (speed) { + /* signal link up for new speed */ + link_up = true; + } + } + } + + if (phydev->drv->set_loopback) + ret = phydev->drv->set_loopback(phydev, enable, speed); + else + ret = genphy_loopback(phydev, enable, speed); + + if (ret) { + if (enable) { + /* try to restore link if enabling loopback fails */ + if (phydev->drv->set_loopback) + phydev->drv->set_loopback(phydev, false, 0); + else + genphy_loopback(phydev, false, 0); + } + + goto out; + } + + if (link_up) { + phydev->link = true; + phydev->state = PHY_RUNNING; + phy_link_up(phydev); + } + + phydev->loopback_enabled = enable; + +out: + mutex_unlock(&phydev->lock); + return ret; +} +EXPORT_SYMBOL(phy_loopback); + +/** * phy_eee_tx_clock_stop_capable() - indicate whether the MAC can stop tx clock * @phydev: target phy_device struct * diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 09864c9635ac..675fbd225378 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1818,41 +1818,6 @@ int phy_resume(struct phy_device *phydev) } EXPORT_SYMBOL(phy_resume); -int phy_loopback(struct phy_device *phydev, bool enable) -{ - int ret = 0; - - if (!phydev->drv) - return -EIO; - - mutex_lock(&phydev->lock); - - if (enable && phydev->loopback_enabled) { - ret = -EBUSY; - goto out; - } - - if (!enable && !phydev->loopback_enabled) { - ret = -EINVAL; - goto out; - } - - if (phydev->drv->set_loopback) - ret = phydev->drv->set_loopback(phydev, enable, 0); - else - ret = genphy_loopback(phydev, enable, 0); - - if (ret) - goto out; - - phydev->loopback_enabled = enable; - -out: - mutex_unlock(&phydev->lock); - return ret; -} -EXPORT_SYMBOL(phy_loopback); - /** * phy_reset_after_clk_enable - perform a PHY reset if needed * @phydev: target phy_device struct |