summaryrefslogtreecommitdiff
path: root/drivers/net/phy/phy_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/phy_device.c')
-rw-r--r--drivers/net/phy/phy_device.c52
1 files changed, 48 insertions, 4 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index cc38e326405a..0a2d8bedf73d 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -273,6 +273,9 @@ static __maybe_unused int mdio_bus_phy_suspend(struct device *dev)
{
struct phy_device *phydev = to_phy_device(dev);
+ if (phydev->mac_managed_pm)
+ return 0;
+
/* We must stop the state machine manually, otherwise it stops out of
* control, possibly with the phydev->lock held. Upon resume, netdev
* may call phy routines that try to grab the same lock, and that may
@@ -294,6 +297,9 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev)
struct phy_device *phydev = to_phy_device(dev);
int ret;
+ if (phydev->mac_managed_pm)
+ return 0;
+
if (!phydev->suspended_by_mdio_bus)
goto no_resume;
@@ -512,10 +518,21 @@ phy_has_fixups_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(phy_has_fixups);
+static ssize_t phy_dev_flags_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return sprintf(buf, "0x%08x\n", phydev->dev_flags);
+}
+static DEVICE_ATTR_RO(phy_dev_flags);
+
static struct attribute *phy_dev_attrs[] = {
&dev_attr_phy_id.attr,
&dev_attr_phy_interface.attr,
&dev_attr_phy_has_fixups.attr,
+ &dev_attr_phy_dev_flags.attr,
NULL,
};
ATTRIBUTE_GROUPS(phy_dev);
@@ -1760,6 +1777,9 @@ int phy_loopback(struct phy_device *phydev, bool enable)
struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
int ret = 0;
+ if (!phydrv)
+ return -ENODEV;
+
mutex_lock(&phydev->lock);
if (enable && phydev->loopback_enabled) {
@@ -1772,10 +1792,10 @@ int phy_loopback(struct phy_device *phydev, bool enable)
goto out;
}
- if (phydev->drv && phydrv->set_loopback)
+ if (phydrv->set_loopback)
ret = phydrv->set_loopback(phydev, enable);
else
- ret = -EOPNOTSUPP;
+ ret = genphy_loopback(phydev, enable);
if (ret)
goto out;
@@ -2545,8 +2565,32 @@ EXPORT_SYMBOL(genphy_resume);
int genphy_loopback(struct phy_device *phydev, bool enable)
{
- return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
- enable ? BMCR_LOOPBACK : 0);
+ if (enable) {
+ u16 val, ctl = BMCR_LOOPBACK;
+ int ret;
+
+ if (phydev->speed == SPEED_1000)
+ ctl |= BMCR_SPEED1000;
+ else if (phydev->speed == SPEED_100)
+ ctl |= BMCR_SPEED100;
+
+ if (phydev->duplex == DUPLEX_FULL)
+ ctl |= BMCR_FULLDPLX;
+
+ phy_modify(phydev, MII_BMCR, ~0, ctl);
+
+ ret = phy_read_poll_timeout(phydev, MII_BMSR, val,
+ val & BMSR_LSTATUS,
+ 5000, 500000, true);
+ if (ret)
+ return ret;
+ } else {
+ phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 0);
+
+ phy_config_aneg(phydev);
+ }
+
+ return 0;
}
EXPORT_SYMBOL(genphy_loopback);