From f0f9b4ed23381d97cde2ac64248198bc43608e6d Mon Sep 17 00:00:00 2001 From: Lin Yun Sheng Date: Fri, 30 Jun 2017 17:44:15 +0800 Subject: [PATCH] net: phy: Add phy loopback support in net phy framework This patch add set_loopback in phy_driver, which is used by MAC driver to enable or disable phy loopback. it also add a generic genphy_loopback function, which use BMCR loopback bit to enable or disable loopback. Signed-off-by: Lin Yun Sheng Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 1 + drivers/net/phy/phy_device.c | 51 ++++++++++++++++++++++++++++++++++++ include/linux/phy.h | 5 ++++ 3 files changed, 57 insertions(+) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 8400403b3f62..5d314f143aea 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -2171,6 +2171,7 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, + .set_loopback = genphy_loopback, }, { .phy_id = MARVELL_PHY_ID_88E1540, diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index acf00f071c9a..1790f7fec125 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1136,6 +1136,39 @@ int phy_resume(struct phy_device *phydev) } EXPORT_SYMBOL(phy_resume); +int phy_loopback(struct phy_device *phydev, bool enable) +{ + struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); + int ret = 0; + + 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 && phydrv->set_loopback) + ret = phydrv->set_loopback(phydev, enable); + else + ret = -EOPNOTSUPP; + + if (ret) + goto out; + + phydev->loopback_enabled = enable; + +out: + mutex_unlock(&phydev->lock); + return ret; +} +EXPORT_SYMBOL(phy_loopback); + /* Generic PHY support and helper functions */ /** @@ -1584,6 +1617,23 @@ int genphy_resume(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_resume); +int genphy_loopback(struct phy_device *phydev, bool enable) +{ + int value; + + value = phy_read(phydev, MII_BMCR); + if (value < 0) + return value; + + if (enable) + value |= BMCR_LOOPBACK; + else + value &= ~BMCR_LOOPBACK; + + return phy_write(phydev, MII_BMCR, value); +} +EXPORT_SYMBOL(genphy_loopback); + static int __set_phy_supported(struct phy_device *phydev, u32 max_speed) { /* The default values for phydev->supported are provided by the PHY @@ -1829,6 +1879,7 @@ static struct phy_driver genphy_driver = { .read_status = genphy_read_status, .suspend = genphy_suspend, .resume = genphy_resume, + .set_loopback = genphy_loopback, }; static int __init phy_init(void) diff --git a/include/linux/phy.h b/include/linux/phy.h index 1d8d70193782..2a9567bb8186 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -372,6 +372,7 @@ struct phy_c45_device_ids { * has_fixups: Set to true if this phy has fixups/quirks. * suspended: Set to true if this phy has been suspended successfully. * sysfs_links: Internal boolean tracking sysfs symbolic links setup/removal. + * loopback_enabled: Set true if this phy has been loopbacked successfully. * state: state of the PHY for management purposes * dev_flags: Device-specific flags used by the PHY driver. * link_timeout: The number of timer firings to wait before the @@ -409,6 +410,7 @@ struct phy_device { bool has_fixups; bool suspended; bool sysfs_links; + bool loopback_enabled; enum phy_state state; @@ -648,6 +650,7 @@ struct phy_driver { int (*set_tunable)(struct phy_device *dev, struct ethtool_tunable *tuna, const void *data); + int (*set_loopback)(struct phy_device *dev, bool enable); }; #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ struct phy_driver, mdiodrv) @@ -793,6 +796,7 @@ void phy_device_remove(struct phy_device *phydev); int phy_init_hw(struct phy_device *phydev); int phy_suspend(struct phy_device *phydev); int phy_resume(struct phy_device *phydev); +int phy_loopback(struct phy_device *phydev, bool enable); struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, phy_interface_t interface); struct phy_device *phy_find_first(struct mii_bus *bus); @@ -847,6 +851,7 @@ int genphy_update_link(struct phy_device *phydev); int genphy_read_status(struct phy_device *phydev); int genphy_suspend(struct phy_device *phydev); int genphy_resume(struct phy_device *phydev); +int genphy_loopback(struct phy_device *phydev, bool enable); int genphy_soft_reset(struct phy_device *phydev); static inline int genphy_no_soft_reset(struct phy_device *phydev) { -- 2.20.1