diff options
Diffstat (limited to 'drivers/net/phy/microchip_t1.c')
| -rw-r--r-- | drivers/net/phy/microchip_t1.c | 171 | 
1 files changed, 171 insertions, 0 deletions
| diff --git a/drivers/net/phy/microchip_t1.c b/drivers/net/phy/microchip_t1.c index 001def4509c2..fed3e395f18e 100644 --- a/drivers/net/phy/microchip_t1.c +++ b/drivers/net/phy/microchip_t1.c @@ -3,9 +3,21 @@  #include <linux/kernel.h>  #include <linux/module.h> +#include <linux/delay.h>  #include <linux/mii.h>  #include <linux/phy.h> +/* External Register Control Register */ +#define LAN87XX_EXT_REG_CTL                     (0x14) +#define LAN87XX_EXT_REG_CTL_RD_CTL              (0x1000) +#define LAN87XX_EXT_REG_CTL_WR_CTL              (0x0800) + +/* External Register Read Data Register */ +#define LAN87XX_EXT_REG_RD_DATA                 (0x15) + +/* External Register Write Data Register */ +#define LAN87XX_EXT_REG_WR_DATA                 (0x16) +  /* Interrupt Source Register */  #define LAN87XX_INTERRUPT_SOURCE                (0x18) @@ -14,9 +26,160 @@  #define LAN87XX_MASK_LINK_UP                    (0x0004)  #define LAN87XX_MASK_LINK_DOWN                  (0x0002) +/* phyaccess nested types */ +#define	PHYACC_ATTR_MODE_READ		0 +#define	PHYACC_ATTR_MODE_WRITE		1 +#define	PHYACC_ATTR_MODE_MODIFY		2 + +#define	PHYACC_ATTR_BANK_SMI		0 +#define	PHYACC_ATTR_BANK_MISC		1 +#define	PHYACC_ATTR_BANK_PCS		2 +#define	PHYACC_ATTR_BANK_AFE		3 +#define	PHYACC_ATTR_BANK_MAX		7 +  #define DRIVER_AUTHOR	"Nisar Sayed <[email protected]>"  #define DRIVER_DESC	"Microchip LAN87XX T1 PHY driver" +struct access_ereg_val { +	u8  mode; +	u8  bank; +	u8  offset; +	u16 val; +	u16 mask; +}; + +static int access_ereg(struct phy_device *phydev, u8 mode, u8 bank, +		       u8 offset, u16 val) +{ +	u16 ereg = 0; +	int rc = 0; + +	if (mode > PHYACC_ATTR_MODE_WRITE || bank > PHYACC_ATTR_BANK_MAX) +		return -EINVAL; + +	if (bank == PHYACC_ATTR_BANK_SMI) { +		if (mode == PHYACC_ATTR_MODE_WRITE) +			rc = phy_write(phydev, offset, val); +		else +			rc = phy_read(phydev, offset); +		return rc; +	} + +	if (mode == PHYACC_ATTR_MODE_WRITE) { +		ereg = LAN87XX_EXT_REG_CTL_WR_CTL; +		rc = phy_write(phydev, LAN87XX_EXT_REG_WR_DATA, val); +		if (rc < 0) +			return rc; +	} else { +		ereg = LAN87XX_EXT_REG_CTL_RD_CTL; +	} + +	ereg |= (bank << 8) | offset; + +	rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, ereg); +	if (rc < 0) +		return rc; + +	if (mode == PHYACC_ATTR_MODE_READ) +		rc = phy_read(phydev, LAN87XX_EXT_REG_RD_DATA); + +	return rc; +} + +static int access_ereg_modify_changed(struct phy_device *phydev, +				      u8 bank, u8 offset, u16 val, u16 mask) +{ +	int new = 0, rc = 0; + +	if (bank > PHYACC_ATTR_BANK_MAX) +		return -EINVAL; + +	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, bank, offset, val); +	if (rc < 0) +		return rc; + +	new = val | (rc & (mask ^ 0xFFFF)); +	rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, bank, offset, new); + +	return rc; +} + +static int lan87xx_phy_init(struct phy_device *phydev) +{ +	static const struct access_ereg_val init[] = { +		/* TX Amplitude = 5 */ +		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_AFE, 0x0B, +		 0x000A, 0x001E}, +		/* Clear SMI interrupts */ +		{PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, 0x18, +		 0, 0}, +		/* Clear MISC interrupts */ +		{PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_MISC, 0x08, +		 0, 0}, +		/* Turn on TC10 Ring Oscillator (ROSC) */ +		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_MISC, 0x20, +		 0x0020, 0x0020}, +		/* WUR Detect Length to 1.2uS, LPC Detect Length to 1.09uS */ +		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_PCS, 0x20, +		 0x283C, 0}, +		/* Wake_In Debounce Length to 39uS, Wake_Out Length to 79uS */ +		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x21, +		 0x274F, 0}, +		/* Enable Auto Wake Forward to Wake_Out, ROSC on, Sleep, +		 * and Wake_In to wake PHY +		 */ +		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x20, +		 0x80A7, 0}, +		/* Enable WUP Auto Fwd, Enable Wake on MDI, Wakeup Debouncer +		 * to 128 uS +		 */ +		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x24, +		 0xF110, 0}, +		/* Enable HW Init */ +		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_SMI, 0x1A, +		 0x0100, 0x0100}, +	}; +	int rc, i; + +	/* Start manual initialization procedures in Managed Mode */ +	rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI, +					0x1a, 0x0000, 0x0100); +	if (rc < 0) +		return rc; + +	/* Soft Reset the SMI block */ +	rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI, +					0x00, 0x8000, 0x8000); +	if (rc < 0) +		return rc; + +	/* Check to see if the self-clearing bit is cleared */ +	usleep_range(1000, 2000); +	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, +			 PHYACC_ATTR_BANK_SMI, 0x00, 0); +	if (rc < 0) +		return rc; +	if ((rc & 0x8000) != 0) +		return -ETIMEDOUT; + +	/* PHY Initialization */ +	for (i = 0; i < ARRAY_SIZE(init); i++) { +		if (init[i].mode == PHYACC_ATTR_MODE_MODIFY) { +			rc = access_ereg_modify_changed(phydev, init[i].bank, +							init[i].offset, +							init[i].val, +							init[i].mask); +		} else { +			rc = access_ereg(phydev, init[i].mode, init[i].bank, +					 init[i].offset, init[i].val); +		} +		if (rc < 0) +			return rc; +	} + +	return 0; +} +  static int lan87xx_phy_config_intr(struct phy_device *phydev)  {  	int rc, val = 0; @@ -40,6 +203,13 @@ static int lan87xx_phy_ack_interrupt(struct phy_device *phydev)  	return rc < 0 ? rc : 0;  } +static int lan87xx_config_init(struct phy_device *phydev) +{ +	int rc = lan87xx_phy_init(phydev); + +	return rc < 0 ? rc : 0; +} +  static struct phy_driver microchip_t1_phy_driver[] = {  	{  		.phy_id         = 0x0007c150, @@ -48,6 +218,7 @@ static struct phy_driver microchip_t1_phy_driver[] = {  		.features       = PHY_BASIC_T1_FEATURES, +		.config_init	= lan87xx_config_init,  		.config_aneg    = genphy_config_aneg,  		.ack_interrupt  = lan87xx_phy_ack_interrupt, |