Commit 6985157c authored by David S. Miller's avatar David S. Miller
Browse files

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 600003a3 ceaeaafc
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -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);

+35 −11
Original line number Diff line number Diff line
@@ -27,12 +27,28 @@ struct gmii2rgmii {
	struct mdio_device *mdio;
};

static int xgmiitorgmii_read_status(struct phy_device *phydev)
static void xgmiitorgmii_configure(struct gmii2rgmii *priv, int speed)
{
	struct gmii2rgmii *priv = mdiodev_get_drvdata(&phydev->mdio);
	struct mii_bus *bus = priv->mdio->bus;
	int addr = priv->mdio->addr;
	u16 val = 0;
	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);
	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;