diff options
Diffstat (limited to 'drivers/net/phy/phy_device.c')
| -rw-r--r-- | drivers/net/phy/phy_device.c | 208 | 
1 files changed, 149 insertions, 59 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 77068c545de0..dcc93a873174 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -225,7 +225,7 @@ static void phy_mdio_device_remove(struct mdio_device *mdiodev)  }  static struct phy_driver genphy_driver; -extern struct phy_driver genphy_10g_driver; +extern struct phy_driver genphy_c45_driver;  static LIST_HEAD(phy_fixup_list);  static DEFINE_MUTEX(phy_fixup_lock); @@ -1174,7 +1174,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,  	 */  	if (!d->driver) {  		if (phydev->is_c45) -			d->driver = &genphy_10g_driver.mdiodrv.driver; +			d->driver = &genphy_c45_driver.mdiodrv.driver;  		else  			d->driver = &genphy_driver.mdiodrv.driver; @@ -1335,7 +1335,7 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy);  bool phy_driver_is_genphy_10g(struct phy_device *phydev)  {  	return phy_driver_is_genphy_kind(phydev, -					 &genphy_10g_driver.mdiodrv.driver); +					 &genphy_c45_driver.mdiodrv.driver);  }  EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g); @@ -1710,23 +1710,19 @@ int genphy_update_link(struct phy_device *phydev)  	 */  	if (!phy_polling_mode(phydev)) {  		status = phy_read(phydev, MII_BMSR); -		if (status < 0) { +		if (status < 0)  			return status; -		} else if (status & BMSR_LSTATUS) { -			phydev->link = 1; -			return 0; -		} +		else if (status & BMSR_LSTATUS) +			goto done;  	}  	/* Read link and autonegotiation status */  	status = phy_read(phydev, MII_BMSR);  	if (status < 0)  		return status; - -	if ((status & BMSR_LSTATUS) == 0) -		phydev->link = 0; -	else -		phydev->link = 1; +done: +	phydev->link = status & BMSR_LSTATUS ? 1 : 0; +	phydev->autoneg_complete = status & BMSR_ANEGCOMPLETE ? 1 : 0;  	return 0;  } @@ -1743,23 +1739,26 @@ EXPORT_SYMBOL(genphy_update_link);   */  int genphy_read_status(struct phy_device *phydev)  { -	int adv; -	int err; -	int lpa; -	int lpagb = 0; +	int adv, lpa, lpagb, err, old_link = phydev->link;  	/* Update the link, but return if there was an error */  	err = genphy_update_link(phydev);  	if (err)  		return err; +	/* why bother the PHY if nothing can have changed */ +	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) +		return 0; + +	phydev->speed = SPEED_UNKNOWN; +	phydev->duplex = DUPLEX_UNKNOWN; +	phydev->pause = 0; +	phydev->asym_pause = 0; +  	linkmode_zero(phydev->lp_advertising); -	if (AUTONEG_ENABLE == phydev->autoneg) { -		if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, -				      phydev->supported) || -		    linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, -				      phydev->supported)) { +	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { +		if (phydev->is_gigabit_capable) {  			lpagb = phy_read(phydev, MII_STAT1000);  			if (lpagb < 0)  				return lpagb; @@ -1785,14 +1784,8 @@ int genphy_read_status(struct phy_device *phydev)  			return lpa;  		mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa); - -		phydev->speed = SPEED_UNKNOWN; -		phydev->duplex = DUPLEX_UNKNOWN; -		phydev->pause = 0; -		phydev->asym_pause = 0; -  		phy_resolve_aneg_linkmode(phydev); -	} else { +	} else if (phydev->autoneg == AUTONEG_DISABLE) {  		int bmcr = phy_read(phydev, MII_BMCR);  		if (bmcr < 0) @@ -1809,9 +1802,6 @@ int genphy_read_status(struct phy_device *phydev)  			phydev->speed = SPEED_100;  		else  			phydev->speed = SPEED_10; - -		phydev->pause = 0; -		phydev->asym_pause = 0;  	}  	return 0; @@ -1829,13 +1819,25 @@ EXPORT_SYMBOL(genphy_read_status);   */  int genphy_soft_reset(struct phy_device *phydev)  { +	u16 res = BMCR_RESET;  	int ret; -	ret = phy_set_bits(phydev, MII_BMCR, BMCR_RESET); +	if (phydev->autoneg == AUTONEG_ENABLE) +		res |= BMCR_ANRESTART; + +	ret = phy_modify(phydev, MII_BMCR, BMCR_ISOLATE, res);  	if (ret < 0)  		return ret; -	return phy_poll_reset(phydev); +	ret = phy_poll_reset(phydev); +	if (ret) +		return ret; + +	/* BMCR may be reset to defaults */ +	if (phydev->autoneg == AUTONEG_DISABLE) +		ret = genphy_setup_forced(phydev); + +	return ret;  }  EXPORT_SYMBOL(genphy_soft_reset); @@ -1887,6 +1889,54 @@ int genphy_config_init(struct phy_device *phydev)  }  EXPORT_SYMBOL(genphy_config_init); +/** + * genphy_read_abilities - read PHY abilities from Clause 22 registers + * @phydev: target phy_device struct + * + * Description: Reads the PHY's abilities and populates + * phydev->supported accordingly. + * + * Returns: 0 on success, < 0 on failure + */ +int genphy_read_abilities(struct phy_device *phydev) +{ +	int val; + +	linkmode_set_bit_array(phy_basic_ports_array, +			       ARRAY_SIZE(phy_basic_ports_array), +			       phydev->supported); + +	val = phy_read(phydev, MII_BMSR); +	if (val < 0) +		return val; + +	linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported, +			 val & BMSR_ANEGCAPABLE); + +	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported, +			 val & BMSR_100FULL); +	linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported, +			 val & BMSR_100HALF); +	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, phydev->supported, +			 val & BMSR_10FULL); +	linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, phydev->supported, +			 val & BMSR_10HALF); + +	if (val & BMSR_ESTATEN) { +		val = phy_read(phydev, MII_ESTATUS); +		if (val < 0) +			return val; + +		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, +				 phydev->supported, val & ESTATUS_1000_TFULL); +		linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, +				 phydev->supported, val & ESTATUS_1000_THALF); +	} + +	return 0; +} +EXPORT_SYMBOL(genphy_read_abilities); +  /* This is used for the phy device which doesn't support the MMD extended   * register access, but it does have side effect when we are trying to access   * the MMD register via indirect method. @@ -1935,10 +1985,35 @@ EXPORT_SYMBOL(genphy_loopback);  void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode)  {  	linkmode_clear_bit(link_mode, phydev->supported); -	linkmode_copy(phydev->advertising, phydev->supported); +	phy_advertise_supported(phydev);  }  EXPORT_SYMBOL(phy_remove_link_mode); +static void phy_copy_pause_bits(unsigned long *dst, unsigned long *src) +{ +	linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, dst, +		linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, src)); +	linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, dst, +		linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, src)); +} + +/** + * phy_advertise_supported - Advertise all supported modes + * @phydev: target phy_device struct + * + * Description: Called to advertise all supported modes, doesn't touch + * pause mode advertising. + */ +void phy_advertise_supported(struct phy_device *phydev) +{ +	__ETHTOOL_DECLARE_LINK_MODE_MASK(new); + +	linkmode_copy(new, phydev->supported); +	phy_copy_pause_bits(new, phydev->advertising); +	linkmode_copy(phydev->advertising, new); +} +EXPORT_SYMBOL(phy_advertise_supported); +  /**   * phy_support_sym_pause - Enable support of symmetrical pause   * @phydev: target phy_device struct @@ -1949,8 +2024,7 @@ EXPORT_SYMBOL(phy_remove_link_mode);  void phy_support_sym_pause(struct phy_device *phydev)  {  	linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); -	linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported); -	linkmode_copy(phydev->advertising, phydev->supported); +	phy_copy_pause_bits(phydev->advertising, phydev->supported);  }  EXPORT_SYMBOL(phy_support_sym_pause); @@ -1962,9 +2036,7 @@ EXPORT_SYMBOL(phy_support_sym_pause);   */  void phy_support_asym_pause(struct phy_device *phydev)  { -	linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported); -	linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); -	linkmode_copy(phydev->advertising, phydev->supported); +	phy_copy_pause_bits(phydev->advertising, phydev->supported);  }  EXPORT_SYMBOL(phy_support_asym_pause); @@ -2044,11 +2116,14 @@ bool phy_validate_pause(struct phy_device *phydev,  			struct ethtool_pauseparam *pp)  {  	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, -			       phydev->supported) || -	    (!linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, -				phydev->supported) && -	     pp->rx_pause != pp->tx_pause)) +			       phydev->supported) && pp->rx_pause) +		return false; + +	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, +			       phydev->supported) && +	    pp->rx_pause != pp->tx_pause)  		return false; +  	return true;  }  EXPORT_SYMBOL(phy_validate_pause); @@ -2104,14 +2179,30 @@ static int phy_probe(struct device *dev)  	 */  	if (phydrv->features) {  		linkmode_copy(phydev->supported, phydrv->features); -	} else { +	} else if (phydrv->get_features) {  		err = phydrv->get_features(phydev); -		if (err) -			goto out; +	} else if (phydev->is_c45) { +		err = genphy_c45_pma_read_abilities(phydev); +	} else { +		err = genphy_read_abilities(phydev);  	} +	if (err) +		goto out; + +	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, +			       phydev->supported)) +		phydev->autoneg = 0; + +	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, +			      phydev->supported)) +		phydev->is_gigabit_capable = 1; +	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, +			      phydev->supported)) +		phydev->is_gigabit_capable = 1; +  	of_set_phy_supported(phydev); -	linkmode_copy(phydev->advertising, phydev->supported); +	phy_advertise_supported(phydev);  	/* Get the EEE modes we want to prohibit. We will ask  	 * the PHY stop advertising these mode later on @@ -2177,11 +2268,11 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner)  	int retval;  	/* Either the features are hard coded, or dynamically -	 * determine. It cannot be both or neither +	 * determined. It cannot be both.  	 */ -	if (WARN_ON((!new_driver->features && !new_driver->get_features) || -		    (new_driver->features && new_driver->get_features))) { -		pr_err("%s: Driver features are missing\n", new_driver->name); +	if (WARN_ON(new_driver->features && new_driver->get_features)) { +		pr_err("%s: features and get_features must not both be set\n", +		       new_driver->name);  		return -EINVAL;  	} @@ -2243,8 +2334,7 @@ static struct phy_driver genphy_driver = {  	.phy_id_mask	= 0xffffffff,  	.name		= "Generic PHY",  	.soft_reset	= genphy_no_soft_reset, -	.config_init	= genphy_config_init, -	.features	= PHY_GBIT_ALL_PORTS_FEATURES, +	.get_features	= genphy_read_abilities,  	.aneg_done	= genphy_aneg_done,  	.suspend	= genphy_suspend,  	.resume		= genphy_resume, @@ -2261,14 +2351,14 @@ static int __init phy_init(void)  	features_init(); -	rc = phy_driver_register(&genphy_10g_driver, THIS_MODULE); +	rc = phy_driver_register(&genphy_c45_driver, THIS_MODULE);  	if (rc) -		goto err_10g; +		goto err_c45;  	rc = phy_driver_register(&genphy_driver, THIS_MODULE);  	if (rc) { -		phy_driver_unregister(&genphy_10g_driver); -err_10g: +		phy_driver_unregister(&genphy_c45_driver); +err_c45:  		mdio_bus_exit();  	} @@ -2277,7 +2367,7 @@ err_10g:  static void __exit phy_exit(void)  { -	phy_driver_unregister(&genphy_10g_driver); +	phy_driver_unregister(&genphy_c45_driver);  	phy_driver_unregister(&genphy_driver);  	mdio_bus_exit();  }  |