Merge branch 'gmii2rgmii-loopback'

Gerhard Engleder says:

====================
Add Xilinx GMII2RGMII loopback support

The Xilinx GMII2RGMII driver overrides PHY driver functions in order to
configure the device according to the link speed of the PHY attached to
it. This is implemented for a normal link but not for loopback.

Andrew told me to use phy_loopback and this changes make phy_loopback
work in combination with Xilinx GMII2RGMII.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2021-08-20 14:31:46 +01:00
commit 6985157ce8
2 changed files with 40 additions and 19 deletions

View File

@ -233,11 +233,9 @@ static DEFINE_MUTEX(phy_fixup_lock);
static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
{
struct device_driver *drv = phydev->mdio.dev.driver;
struct phy_driver *phydrv = to_phy_driver(drv);
struct net_device *netdev = phydev->attached_dev;
if (!drv || !phydrv->suspend)
if (!phydev->drv->suspend)
return false;
/* PHY not attached? May suspend if the PHY has not already been
@ -1821,11 +1819,10 @@ 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;
if (!phydrv)
return -ENODEV;
if (!phydev->drv)
return -EIO;
mutex_lock(&phydev->lock);
@ -1839,8 +1836,8 @@ int phy_loopback(struct phy_device *phydev, bool enable)
goto out;
}
if (phydrv->set_loopback)
ret = phydrv->set_loopback(phydev, enable);
if (phydev->drv->set_loopback)
ret = phydev->drv->set_loopback(phydev, enable);
else
ret = genphy_loopback(phydev, enable);

View File

@ -27,12 +27,28 @@ struct gmii2rgmii {
struct mdio_device *mdio;
};
static void xgmiitorgmii_configure(struct gmii2rgmii *priv, int speed)
{
struct mii_bus *bus = priv->mdio->bus;
int addr = priv->mdio->addr;
u16 val;
val = mdiobus_read(bus, addr, XILINX_GMII2RGMII_REG);
val &= ~XILINX_GMII2RGMII_SPEED_MASK;
if (speed == SPEED_1000)
val |= BMCR_SPEED1000;
else if (speed == SPEED_100)
val |= BMCR_SPEED100;
else
val |= BMCR_SPEED10;
mdiobus_write(bus, addr, XILINX_GMII2RGMII_REG, val);
}
static int xgmiitorgmii_read_status(struct phy_device *phydev)
{
struct gmii2rgmii *priv = mdiodev_get_drvdata(&phydev->mdio);
struct mii_bus *bus = priv->mdio->bus;
int addr = priv->mdio->addr;
u16 val = 0;
int err;
if (priv->phy_drv->read_status)
@ -42,17 +58,24 @@ static int xgmiitorgmii_read_status(struct phy_device *phydev)
if (err < 0)
return err;
val = mdiobus_read(bus, addr, XILINX_GMII2RGMII_REG);
val &= ~XILINX_GMII2RGMII_SPEED_MASK;
xgmiitorgmii_configure(priv, phydev->speed);
if (phydev->speed == SPEED_1000)
val |= BMCR_SPEED1000;
else if (phydev->speed == SPEED_100)
val |= BMCR_SPEED100;
return 0;
}
static int xgmiitorgmii_set_loopback(struct phy_device *phydev, bool enable)
{
struct gmii2rgmii *priv = mdiodev_get_drvdata(&phydev->mdio);
int err;
if (priv->phy_drv->set_loopback)
err = priv->phy_drv->set_loopback(phydev, enable);
else
val |= BMCR_SPEED10;
err = genphy_loopback(phydev, enable);
if (err < 0)
return err;
mdiobus_write(bus, addr, XILINX_GMII2RGMII_REG, val);
xgmiitorgmii_configure(priv, phydev->speed);
return 0;
}
@ -90,6 +113,7 @@ static int xgmiitorgmii_probe(struct mdio_device *mdiodev)
memcpy(&priv->conv_phy_drv, priv->phy_dev->drv,
sizeof(struct phy_driver));
priv->conv_phy_drv.read_status = xgmiitorgmii_read_status;
priv->conv_phy_drv.set_loopback = xgmiitorgmii_set_loopback;
mdiodev_set_drvdata(&priv->phy_dev->mdio, priv);
priv->phy_dev->drv = &priv->conv_phy_drv;