diff options
Diffstat (limited to 'drivers/net/phy/lxt.c')
| -rw-r--r-- | drivers/net/phy/lxt.c | 127 | 
1 files changed, 127 insertions, 0 deletions
diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c index 6d1e3fcc43e..ec40ba882f6 100644 --- a/drivers/net/phy/lxt.c +++ b/drivers/net/phy/lxt.c @@ -122,6 +122,123 @@ static int lxt971_config_intr(struct phy_device *phydev)  	return err;  } +/* + * A2 version of LXT973 chip has an ERRATA: it randomly return the contents + * of the previous even register when you read a odd register regularly + */ + +static int lxt973a2_update_link(struct phy_device *phydev) +{ +	int status; +	int control; +	int retry = 8; /* we try 8 times */ + +	/* Do a fake read */ +	status = phy_read(phydev, MII_BMSR); + +	if (status < 0) +		return status; + +	control = phy_read(phydev, MII_BMCR); +	if (control < 0) +		return control; + +	do { +		/* Read link and autonegotiation status */ +		status = phy_read(phydev, MII_BMSR); +	} while (status >= 0 && retry-- && status == control); + +	if (status < 0) +		return status; + +	if ((status & BMSR_LSTATUS) == 0) +		phydev->link = 0; +	else +		phydev->link = 1; + +	return 0; +} + +int lxt973a2_read_status(struct phy_device *phydev) +{ +	int adv; +	int err; +	int lpa; +	int lpagb = 0; + +	/* Update the link, but return if there was an error */ +	err = lxt973a2_update_link(phydev); +	if (err) +		return err; + +	if (AUTONEG_ENABLE == phydev->autoneg) { +		int retry = 1; + +		adv = phy_read(phydev, MII_ADVERTISE); + +		if (adv < 0) +			return adv; + +		do { +			lpa = phy_read(phydev, MII_LPA); + +			if (lpa < 0) +				return lpa; + +			/* If both registers are equal, it is suspect but not +			* impossible, hence a new try +			*/ +		} while (lpa == adv && retry--); + +		lpa &= adv; + +		phydev->speed = SPEED_10; +		phydev->duplex = DUPLEX_HALF; +		phydev->pause = phydev->asym_pause = 0; + +		if (lpagb & (LPA_1000FULL | LPA_1000HALF)) { +			phydev->speed = SPEED_1000; + +			if (lpagb & LPA_1000FULL) +				phydev->duplex = DUPLEX_FULL; +		} else if (lpa & (LPA_100FULL | LPA_100HALF)) { +			phydev->speed = SPEED_100; + +			if (lpa & LPA_100FULL) +				phydev->duplex = DUPLEX_FULL; +		} else { +			if (lpa & LPA_10FULL) +				phydev->duplex = DUPLEX_FULL; +		} + +		if (phydev->duplex == DUPLEX_FULL) { +			phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; +			phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; +		} +	} else { +		int bmcr = phy_read(phydev, MII_BMCR); + +		if (bmcr < 0) +			return bmcr; + +		if (bmcr & BMCR_FULLDPLX) +			phydev->duplex = DUPLEX_FULL; +		else +			phydev->duplex = DUPLEX_HALF; + +		if (bmcr & BMCR_SPEED1000) +			phydev->speed = SPEED_1000; +		else if (bmcr & BMCR_SPEED100) +			phydev->speed = SPEED_100; +		else +			phydev->speed = SPEED_10; + +		phydev->pause = phydev->asym_pause = 0; +	} + +	return 0; +} +  static int lxt973_probe(struct phy_device *phydev)  {  	int val = phy_read(phydev, MII_LXT973_PCR); @@ -175,6 +292,16 @@ static struct phy_driver lxt97x_driver[] = {  	.driver		= { .owner = THIS_MODULE,},  }, {  	.phy_id		= 0x00137a10, +	.name		= "LXT973-A2", +	.phy_id_mask	= 0xffffffff, +	.features	= PHY_BASIC_FEATURES, +	.flags		= 0, +	.probe		= lxt973_probe, +	.config_aneg	= lxt973_config_aneg, +	.read_status	= lxt973a2_read_status, +	.driver		= { .owner = THIS_MODULE,}, +}, { +	.phy_id		= 0x00137a10,  	.name		= "LXT973",  	.phy_id_mask	= 0xfffffff0,  	.features	= PHY_BASIC_FEATURES,  |