diff options
Diffstat (limited to 'drivers/net/phy/icplus.c')
| -rw-r--r-- | drivers/net/phy/icplus.c | 170 | 
1 files changed, 153 insertions, 17 deletions
diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c index c1d2d251fe8..97bf58bf493 100644 --- a/drivers/net/phy/icplus.c +++ b/drivers/net/phy/icplus.c @@ -30,48 +30,59 @@  #include <asm/irq.h>  #include <asm/uaccess.h> -MODULE_DESCRIPTION("ICPlus IP175C PHY driver"); +MODULE_DESCRIPTION("ICPlus IP175C/IP101A/IP101G/IC1001 PHY drivers");  MODULE_AUTHOR("Michael Barkowski");  MODULE_LICENSE("GPL"); +/* IP101A/G - IP1001 */ +#define IP10XX_SPEC_CTRL_STATUS		16	/* Spec. Control Register */ +#define IP1001_RXPHASE_SEL		(1<<0)	/* Add delay on RX_CLK */ +#define IP1001_TXPHASE_SEL		(1<<1)	/* Add delay on TX_CLK */ +#define IP1001_SPEC_CTRL_STATUS_2	20	/* IP1001 Spec. Control Reg 2 */ +#define IP1001_APS_ON			11	/* IP1001 APS Mode  bit */ +#define IP101A_G_APS_ON			2	/* IP101A/G APS Mode bit */ +#define IP101A_G_IRQ_CONF_STATUS	0x11	/* Conf Info IRQ & Status Reg */ +#define	IP101A_G_IRQ_PIN_USED		(1<<15) /* INTR pin used */ +#define	IP101A_G_IRQ_DEFAULT		IP101A_G_IRQ_PIN_USED +  static int ip175c_config_init(struct phy_device *phydev)  {  	int err, i; -	static int full_reset_performed = 0; +	static int full_reset_performed;  	if (full_reset_performed == 0) {  		/* master reset */ -		err = phydev->bus->write(phydev->bus, 30, 0, 0x175c); +		err = mdiobus_write(phydev->bus, 30, 0, 0x175c);  		if (err < 0)  			return err;  		/* ensure no bus delays overlap reset period */ -		err = phydev->bus->read(phydev->bus, 30, 0); +		err = mdiobus_read(phydev->bus, 30, 0);  		/* data sheet specifies reset period is 2 msec */  		mdelay(2);  		/* enable IP175C mode */ -		err = phydev->bus->write(phydev->bus, 29, 31, 0x175c); +		err = mdiobus_write(phydev->bus, 29, 31, 0x175c);  		if (err < 0)  			return err;  		/* Set MII0 speed and duplex (in PHY mode) */ -		err = phydev->bus->write(phydev->bus, 29, 22, 0x420); +		err = mdiobus_write(phydev->bus, 29, 22, 0x420);  		if (err < 0)  			return err;  		/* reset switch ports */  		for (i = 0; i < 5; i++) { -			err = phydev->bus->write(phydev->bus, i, -						 MII_BMCR, BMCR_RESET); +			err = mdiobus_write(phydev->bus, i, +					    MII_BMCR, BMCR_RESET);  			if (err < 0)  				return err;  		}  		for (i = 0; i < 5; i++) -			err = phydev->bus->read(phydev->bus, i, MII_BMCR); +			err = mdiobus_read(phydev->bus, i, MII_BMCR);  		mdelay(2); @@ -89,6 +100,91 @@ static int ip175c_config_init(struct phy_device *phydev)  	return 0;  } +static int ip1xx_reset(struct phy_device *phydev) +{ +	int bmcr; + +	/* Software Reset PHY */ +	bmcr = phy_read(phydev, MII_BMCR); +	if (bmcr < 0) +		return bmcr; +	bmcr |= BMCR_RESET; +	bmcr = phy_write(phydev, MII_BMCR, bmcr); +	if (bmcr < 0) +		return bmcr; + +	do { +		bmcr = phy_read(phydev, MII_BMCR); +		if (bmcr < 0) +			return bmcr; +	} while (bmcr & BMCR_RESET); + +	return 0; +} + +static int ip1001_config_init(struct phy_device *phydev) +{ +	int c; + +	c = ip1xx_reset(phydev); +	if (c < 0) +		return c; + +	/* Enable Auto Power Saving mode */ +	c = phy_read(phydev, IP1001_SPEC_CTRL_STATUS_2); +	if (c < 0) +		return c; +	c |= IP1001_APS_ON; +	c = phy_write(phydev, IP1001_SPEC_CTRL_STATUS_2, c); +	if (c < 0) +		return c; + +	if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || +	    (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || +	    (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || +	    (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { + +		c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); +		if (c < 0) +			return c; + +		c &= ~(IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); + +		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) +			c |= (IP1001_RXPHASE_SEL | IP1001_TXPHASE_SEL); +		else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) +			c |= IP1001_RXPHASE_SEL; +		else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) +			c |= IP1001_TXPHASE_SEL; + +		c = phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); +		if (c < 0) +			return c; +	} + +	return 0; +} + +static int ip101a_g_config_init(struct phy_device *phydev) +{ +	int c; + +	c = ip1xx_reset(phydev); +	if (c < 0) +		return c; + +	/* INTR pin used: speed/link/duplex will cause an interrupt */ +	c = phy_write(phydev, IP101A_G_IRQ_CONF_STATUS, IP101A_G_IRQ_DEFAULT); +	if (c < 0) +		return c; + +	/* Enable Auto Power Saving mode */ +	c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); +	c |= IP101A_G_APS_ON; + +	return phy_write(phydev, IP10XX_SPEC_CTRL_STATUS, c); +} +  static int ip175c_read_status(struct phy_device *phydev)  {  	if (phydev->addr == 4) /* WAN port */ @@ -108,7 +204,17 @@ static int ip175c_config_aneg(struct phy_device *phydev)  	return 0;  } -static struct phy_driver ip175c_driver = { +static int ip101a_g_ack_interrupt(struct phy_device *phydev) +{ +	int err = phy_read(phydev, IP101A_G_IRQ_CONF_STATUS); +	if (err < 0) +		return err; + +	return 0; +} + +static struct phy_driver icplus_driver[] = { +{  	.phy_id		= 0x02430d80,  	.name		= "ICPlus IP175C",  	.phy_id_mask	= 0x0ffffff0, @@ -119,23 +225,53 @@ static struct phy_driver ip175c_driver = {  	.suspend	= genphy_suspend,  	.resume		= genphy_resume,  	.driver		= { .owner = THIS_MODULE,}, -}; +}, { +	.phy_id		= 0x02430d90, +	.name		= "ICPlus IP1001", +	.phy_id_mask	= 0x0ffffff0, +	.features	= PHY_GBIT_FEATURES | SUPPORTED_Pause | +			  SUPPORTED_Asym_Pause, +	.config_init	= &ip1001_config_init, +	.config_aneg	= &genphy_config_aneg, +	.read_status	= &genphy_read_status, +	.suspend	= genphy_suspend, +	.resume		= genphy_resume, +	.driver		= { .owner = THIS_MODULE,}, +}, { +	.phy_id		= 0x02430c54, +	.name		= "ICPlus IP101A/G", +	.phy_id_mask	= 0x0ffffff0, +	.features	= PHY_BASIC_FEATURES | SUPPORTED_Pause | +			  SUPPORTED_Asym_Pause, +	.flags		= PHY_HAS_INTERRUPT, +	.ack_interrupt	= ip101a_g_ack_interrupt, +	.config_init	= &ip101a_g_config_init, +	.config_aneg	= &genphy_config_aneg, +	.read_status	= &genphy_read_status, +	.suspend	= genphy_suspend, +	.resume		= genphy_resume, +	.driver		= { .owner = THIS_MODULE,}, +} }; -static int __init ip175c_init(void) +static int __init icplus_init(void)  { -	return phy_driver_register(&ip175c_driver); +	return phy_drivers_register(icplus_driver, +		ARRAY_SIZE(icplus_driver));  } -static void __exit ip175c_exit(void) +static void __exit icplus_exit(void)  { -	phy_driver_unregister(&ip175c_driver); +	phy_drivers_unregister(icplus_driver, +		ARRAY_SIZE(icplus_driver));  } -module_init(ip175c_init); -module_exit(ip175c_exit); +module_init(icplus_init); +module_exit(icplus_exit);  static struct mdio_device_id __maybe_unused icplus_tbl[] = {  	{ 0x02430d80, 0x0ffffff0 }, +	{ 0x02430d90, 0x0ffffff0 }, +	{ 0x02430c54, 0x0ffffff0 },  	{ }  };  | 
