forked from mirrors/linux
		
	net: phy: DP83822: Add ability to advertise Fiber connection
The DP83822 can be configured to use a Fiber connection. The strap register is read to determine if the device has been configured to use a fiber connection. With the fiber connection the PHY can be configured to detect whether the fiber connection is active by either a high signal or a low signal. Fiber mode is only applicable to the DP83822 so rework the PHY match table so that non-fiber PHYs can still use the same driver but not call or use any of the fiber features. Signed-off-by: Dan Murphy <dmurphy@ti.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									7a63d769a8
								
							
						
					
					
						commit
						5dc39fd5ef
					
				
					 1 changed files with 218 additions and 7 deletions
				
			
		| 
						 | 
				
			
			@ -23,16 +23,31 @@
 | 
			
		|||
 | 
			
		||||
#define DP83822_DEVADDR		0x1f
 | 
			
		||||
 | 
			
		||||
#define MII_DP83822_CTRL_2	0x0a
 | 
			
		||||
#define MII_DP83822_PHYSTS	0x10
 | 
			
		||||
#define MII_DP83822_PHYSCR	0x11
 | 
			
		||||
#define MII_DP83822_MISR1	0x12
 | 
			
		||||
#define MII_DP83822_MISR2	0x13
 | 
			
		||||
#define MII_DP83822_FCSCR	0x14
 | 
			
		||||
#define MII_DP83822_RCSR	0x17
 | 
			
		||||
#define MII_DP83822_RESET_CTRL	0x1f
 | 
			
		||||
#define MII_DP83822_GENCFG	0x465
 | 
			
		||||
#define MII_DP83822_SOR1	0x467
 | 
			
		||||
 | 
			
		||||
/* GENCFG */
 | 
			
		||||
#define DP83822_SIG_DET_LOW	BIT(0)
 | 
			
		||||
 | 
			
		||||
/* Control Register 2 bits */
 | 
			
		||||
#define DP83822_FX_ENABLE	BIT(14)
 | 
			
		||||
 | 
			
		||||
#define DP83822_HW_RESET	BIT(15)
 | 
			
		||||
#define DP83822_SW_RESET	BIT(14)
 | 
			
		||||
 | 
			
		||||
/* PHY STS bits */
 | 
			
		||||
#define DP83822_PHYSTS_DUPLEX			BIT(2)
 | 
			
		||||
#define DP83822_PHYSTS_10			BIT(1)
 | 
			
		||||
#define DP83822_PHYSTS_LINK			BIT(0)
 | 
			
		||||
 | 
			
		||||
/* PHYSCR Register Fields */
 | 
			
		||||
#define DP83822_PHYSCR_INT_OE		BIT(0) /* Interrupt Output Enable */
 | 
			
		||||
#define DP83822_PHYSCR_INTEN		BIT(1) /* Interrupt Enable */
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +98,28 @@
 | 
			
		|||
#define DP83822_RX_CLK_SHIFT	BIT(12)
 | 
			
		||||
#define DP83822_TX_CLK_SHIFT	BIT(11)
 | 
			
		||||
 | 
			
		||||
/* SOR1 mode */
 | 
			
		||||
#define DP83822_STRAP_MODE1	0
 | 
			
		||||
#define DP83822_STRAP_MODE2	BIT(0)
 | 
			
		||||
#define DP83822_STRAP_MODE3	BIT(1)
 | 
			
		||||
#define DP83822_STRAP_MODE4	GENMASK(1, 0)
 | 
			
		||||
 | 
			
		||||
#define DP83822_COL_STRAP_MASK	GENMASK(11, 10)
 | 
			
		||||
#define DP83822_COL_SHIFT	10
 | 
			
		||||
#define DP83822_RX_ER_STR_MASK	GENMASK(9, 8)
 | 
			
		||||
#define DP83822_RX_ER_SHIFT	8
 | 
			
		||||
 | 
			
		||||
#define MII_DP83822_FIBER_ADVERTISE    (ADVERTISED_TP | ADVERTISED_MII | \
 | 
			
		||||
					ADVERTISED_FIBRE | ADVERTISED_BNC |  \
 | 
			
		||||
					ADVERTISED_Pause | ADVERTISED_Asym_Pause | \
 | 
			
		||||
					ADVERTISED_100baseT_Full)
 | 
			
		||||
 | 
			
		||||
struct dp83822_private {
 | 
			
		||||
	bool fx_signal_det_low;
 | 
			
		||||
	int fx_enabled;
 | 
			
		||||
	u16 fx_sd_enable;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int dp83822_ack_interrupt(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
| 
						 | 
				
			
			@ -197,6 +234,7 @@ static void dp83822_get_wol(struct phy_device *phydev,
 | 
			
		|||
 | 
			
		||||
static int dp83822_config_intr(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	struct dp83822_private *dp83822 = phydev->priv;
 | 
			
		||||
	int misr_status;
 | 
			
		||||
	int physcr_status;
 | 
			
		||||
	int err;
 | 
			
		||||
| 
						 | 
				
			
			@ -208,13 +246,16 @@ static int dp83822_config_intr(struct phy_device *phydev)
 | 
			
		|||
 | 
			
		||||
		misr_status |= (DP83822_RX_ERR_HF_INT_EN |
 | 
			
		||||
				DP83822_FALSE_CARRIER_HF_INT_EN |
 | 
			
		||||
				DP83822_ANEG_COMPLETE_INT_EN |
 | 
			
		||||
				DP83822_DUP_MODE_CHANGE_INT_EN |
 | 
			
		||||
				DP83822_SPEED_CHANGED_INT_EN |
 | 
			
		||||
				DP83822_LINK_STAT_INT_EN |
 | 
			
		||||
				DP83822_ENERGY_DET_INT_EN |
 | 
			
		||||
				DP83822_LINK_QUAL_INT_EN);
 | 
			
		||||
 | 
			
		||||
		if (!dp83822->fx_enabled)
 | 
			
		||||
			misr_status |= DP83822_ANEG_COMPLETE_INT_EN |
 | 
			
		||||
				       DP83822_DUP_MODE_CHANGE_INT_EN |
 | 
			
		||||
				       DP83822_SPEED_CHANGED_INT_EN;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		err = phy_write(phydev, MII_DP83822_MISR1, misr_status);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -224,14 +265,16 @@ static int dp83822_config_intr(struct phy_device *phydev)
 | 
			
		|||
			return misr_status;
 | 
			
		||||
 | 
			
		||||
		misr_status |= (DP83822_JABBER_DET_INT_EN |
 | 
			
		||||
				DP83822_WOL_PKT_INT_EN |
 | 
			
		||||
				DP83822_SLEEP_MODE_INT_EN |
 | 
			
		||||
				DP83822_MDI_XOVER_INT_EN |
 | 
			
		||||
				DP83822_LB_FIFO_INT_EN |
 | 
			
		||||
				DP83822_PAGE_RX_INT_EN |
 | 
			
		||||
				DP83822_ANEG_ERR_INT_EN |
 | 
			
		||||
				DP83822_EEE_ERROR_CHANGE_INT_EN);
 | 
			
		||||
 | 
			
		||||
		if (!dp83822->fx_enabled)
 | 
			
		||||
			misr_status |= DP83822_MDI_XOVER_INT_EN |
 | 
			
		||||
				       DP83822_ANEG_ERR_INT_EN |
 | 
			
		||||
				       DP83822_WOL_PKT_INT_EN;
 | 
			
		||||
 | 
			
		||||
		err = phy_write(phydev, MII_DP83822_MISR2, misr_status);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -270,13 +313,60 @@ static int dp8382x_disable_wol(struct phy_device *phydev)
 | 
			
		|||
				  MII_DP83822_WOL_CFG, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int dp83822_read_status(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	struct dp83822_private *dp83822 = phydev->priv;
 | 
			
		||||
	int status = phy_read(phydev, MII_DP83822_PHYSTS);
 | 
			
		||||
	int ctrl2;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (dp83822->fx_enabled) {
 | 
			
		||||
		if (status & DP83822_PHYSTS_LINK) {
 | 
			
		||||
			phydev->speed = SPEED_UNKNOWN;
 | 
			
		||||
			phydev->duplex = DUPLEX_UNKNOWN;
 | 
			
		||||
		} else {
 | 
			
		||||
			ctrl2 = phy_read(phydev, MII_DP83822_CTRL_2);
 | 
			
		||||
			if (ctrl2 < 0)
 | 
			
		||||
				return ctrl2;
 | 
			
		||||
 | 
			
		||||
			if (!(ctrl2 & DP83822_FX_ENABLE)) {
 | 
			
		||||
				ret = phy_write(phydev, MII_DP83822_CTRL_2,
 | 
			
		||||
						DP83822_FX_ENABLE | ctrl2);
 | 
			
		||||
				if (ret < 0)
 | 
			
		||||
					return ret;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = genphy_read_status(phydev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	if (status < 0)
 | 
			
		||||
		return status;
 | 
			
		||||
 | 
			
		||||
	if (status & DP83822_PHYSTS_DUPLEX)
 | 
			
		||||
		phydev->duplex = DUPLEX_FULL;
 | 
			
		||||
	else
 | 
			
		||||
		phydev->duplex = DUPLEX_HALF;
 | 
			
		||||
 | 
			
		||||
	if (status & DP83822_PHYSTS_10)
 | 
			
		||||
		phydev->speed = SPEED_10;
 | 
			
		||||
	else
 | 
			
		||||
		phydev->speed = SPEED_100;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int dp83822_config_init(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	struct dp83822_private *dp83822 = phydev->priv;
 | 
			
		||||
	struct device *dev = &phydev->mdio.dev;
 | 
			
		||||
	int rgmii_delay;
 | 
			
		||||
	s32 rx_int_delay;
 | 
			
		||||
	s32 tx_int_delay;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
	int bmcr;
 | 
			
		||||
 | 
			
		||||
	if (phy_interface_is_rgmii(phydev)) {
 | 
			
		||||
		rx_int_delay = phy_get_internal_delay(phydev, dev, NULL, 0,
 | 
			
		||||
| 
						 | 
				
			
			@ -302,6 +392,53 @@ static int dp83822_config_init(struct phy_device *phydev)
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dp83822->fx_enabled) {
 | 
			
		||||
		err = phy_modify(phydev, MII_DP83822_CTRL_2,
 | 
			
		||||
				 DP83822_FX_ENABLE, 1);
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
 | 
			
		||||
		/* Only allow advertising what this PHY supports */
 | 
			
		||||
		linkmode_and(phydev->advertising, phydev->advertising,
 | 
			
		||||
			     phydev->supported);
 | 
			
		||||
 | 
			
		||||
		linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
 | 
			
		||||
				 phydev->supported);
 | 
			
		||||
		linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
 | 
			
		||||
				 phydev->advertising);
 | 
			
		||||
 | 
			
		||||
		/* Auto neg is not supported in fiber mode */
 | 
			
		||||
		bmcr = phy_read(phydev, MII_BMCR);
 | 
			
		||||
		if (bmcr < 0)
 | 
			
		||||
			return bmcr;
 | 
			
		||||
 | 
			
		||||
		if (bmcr & BMCR_ANENABLE) {
 | 
			
		||||
			err =  phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
 | 
			
		||||
			if (err < 0)
 | 
			
		||||
				return err;
 | 
			
		||||
		}
 | 
			
		||||
		phydev->autoneg = AUTONEG_DISABLE;
 | 
			
		||||
		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 | 
			
		||||
				   phydev->supported);
 | 
			
		||||
		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
 | 
			
		||||
				   phydev->advertising);
 | 
			
		||||
 | 
			
		||||
		/* Setup fiber advertisement */
 | 
			
		||||
		err = phy_modify_changed(phydev, MII_ADVERTISE,
 | 
			
		||||
					 MII_DP83822_FIBER_ADVERTISE,
 | 
			
		||||
					 MII_DP83822_FIBER_ADVERTISE);
 | 
			
		||||
 | 
			
		||||
		if (err < 0)
 | 
			
		||||
			return err;
 | 
			
		||||
 | 
			
		||||
		if (dp83822->fx_signal_det_low) {
 | 
			
		||||
			err = phy_set_bits_mmd(phydev, DP83822_DEVADDR,
 | 
			
		||||
					       MII_DP83822_GENCFG,
 | 
			
		||||
					       DP83822_SIG_DET_LOW);
 | 
			
		||||
			if (err)
 | 
			
		||||
				return err;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return dp8382x_disable_wol(phydev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -314,13 +451,85 @@ static int dp83822_phy_reset(struct phy_device *phydev)
 | 
			
		|||
{
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_HW_RESET);
 | 
			
		||||
	err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_SW_RESET);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	return phydev->drv->config_init(phydev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_OF_MDIO
 | 
			
		||||
static int dp83822_of_init(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	struct dp83822_private *dp83822 = phydev->priv;
 | 
			
		||||
	struct device *dev = &phydev->mdio.dev;
 | 
			
		||||
 | 
			
		||||
	/* Signal detection for the PHY is only enabled if the FX_EN and the
 | 
			
		||||
	 * SD_EN pins are strapped. Signal detection can only enabled if FX_EN
 | 
			
		||||
	 * is strapped otherwise signal detection is disabled for the PHY.
 | 
			
		||||
	 */
 | 
			
		||||
	if (dp83822->fx_enabled && dp83822->fx_sd_enable)
 | 
			
		||||
		dp83822->fx_signal_det_low = device_property_present(dev,
 | 
			
		||||
								     "ti,link-loss-low");
 | 
			
		||||
	if (!dp83822->fx_enabled)
 | 
			
		||||
		dp83822->fx_enabled = device_property_present(dev,
 | 
			
		||||
							      "ti,fiber-mode");
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
static int dp83822_of_init(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
#endif /* CONFIG_OF_MDIO */
 | 
			
		||||
 | 
			
		||||
static int dp83822_read_straps(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	struct dp83822_private *dp83822 = phydev->priv;
 | 
			
		||||
	int fx_enabled, fx_sd_enable;
 | 
			
		||||
	int val;
 | 
			
		||||
 | 
			
		||||
	val = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_SOR1);
 | 
			
		||||
	if (val < 0)
 | 
			
		||||
		return val;
 | 
			
		||||
 | 
			
		||||
	fx_enabled = (val & DP83822_COL_STRAP_MASK) >> DP83822_COL_SHIFT;
 | 
			
		||||
	if (fx_enabled == DP83822_STRAP_MODE2 ||
 | 
			
		||||
	    fx_enabled == DP83822_STRAP_MODE3)
 | 
			
		||||
		dp83822->fx_enabled = 1;
 | 
			
		||||
 | 
			
		||||
	if (dp83822->fx_enabled) {
 | 
			
		||||
		fx_sd_enable = (val & DP83822_RX_ER_STR_MASK) >> DP83822_RX_ER_SHIFT;
 | 
			
		||||
		if (fx_sd_enable == DP83822_STRAP_MODE3 ||
 | 
			
		||||
		    fx_sd_enable == DP83822_STRAP_MODE4)
 | 
			
		||||
			dp83822->fx_sd_enable = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int dp83822_probe(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	struct dp83822_private *dp83822;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	dp83822 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83822),
 | 
			
		||||
			       GFP_KERNEL);
 | 
			
		||||
	if (!dp83822)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	phydev->priv = dp83822;
 | 
			
		||||
 | 
			
		||||
	ret = dp83822_read_straps(phydev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	dp83822_of_init(phydev);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int dp83822_suspend(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	int value;
 | 
			
		||||
| 
						 | 
				
			
			@ -352,8 +561,10 @@ static int dp83822_resume(struct phy_device *phydev)
 | 
			
		|||
		PHY_ID_MATCH_MODEL(_id),			\
 | 
			
		||||
		.name		= (_name),			\
 | 
			
		||||
		/* PHY_BASIC_FEATURES */			\
 | 
			
		||||
		.probe          = dp83822_probe,		\
 | 
			
		||||
		.soft_reset	= dp83822_phy_reset,		\
 | 
			
		||||
		.config_init	= dp83822_config_init,		\
 | 
			
		||||
		.read_status	= dp83822_read_status,		\
 | 
			
		||||
		.get_wol = dp83822_get_wol,			\
 | 
			
		||||
		.set_wol = dp83822_set_wol,			\
 | 
			
		||||
		.ack_interrupt = dp83822_ack_interrupt,		\
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue