diff options
| author | Dmitry Torokhov <[email protected]> | 2023-05-01 15:20:08 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <[email protected]> | 2023-05-01 15:20:08 -0700 | 
| commit | 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e (patch) | |
| tree | d57f3a63479a07b4e0cece029886e76e04feb984 /drivers/net/phy/phy.c | |
| parent | 5dc63e56a9cf8df0b59c234a505a1653f1bdf885 (diff) | |
| parent | 53bea86b5712c7491bb3dae12e271666df0a308c (diff) | |
Merge branch 'next' into for-linus
Prepare input updates for 6.4 merge window.
Diffstat (limited to 'drivers/net/phy/phy.c')
| -rw-r--r-- | drivers/net/phy/phy.c | 417 | 
1 files changed, 270 insertions, 147 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index e5b6cb1a77f9..b33e55a7364e 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -242,11 +242,11 @@ unsigned int phy_supported_speeds(struct phy_device *phy,   *   * Description: Returns true if there is a valid setting, false otherwise.   */ -static inline bool phy_check_valid(int speed, int duplex, -				   unsigned long *features) +bool phy_check_valid(int speed, int duplex, unsigned long *features)  {  	return !!phy_lookup_setting(speed, duplex, features, true);  } +EXPORT_SYMBOL(phy_check_valid);  /**   * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex @@ -544,6 +544,198 @@ int phy_ethtool_get_stats(struct phy_device *phydev,  EXPORT_SYMBOL(phy_ethtool_get_stats);  /** + * phy_ethtool_get_plca_cfg - Get PLCA RS configuration + * @phydev: the phy_device struct + * @plca_cfg: where to store the retrieved configuration + * + * Retrieve the PLCA configuration from the PHY. Return 0 on success or a + * negative value if an error occurred. + */ +int phy_ethtool_get_plca_cfg(struct phy_device *phydev, +			     struct phy_plca_cfg *plca_cfg) +{ +	int ret; + +	if (!phydev->drv) { +		ret = -EIO; +		goto out; +	} + +	if (!phydev->drv->get_plca_cfg) { +		ret = -EOPNOTSUPP; +		goto out; +	} + +	mutex_lock(&phydev->lock); +	ret = phydev->drv->get_plca_cfg(phydev, plca_cfg); + +	mutex_unlock(&phydev->lock); +out: +	return ret; +} + +/** + * plca_check_valid - Check PLCA configuration before enabling + * @phydev: the phy_device struct + * @plca_cfg: current PLCA configuration + * @extack: extack for reporting useful error messages + * + * Checks whether the PLCA and PHY configuration are consistent and it is safe + * to enable PLCA. Returns 0 on success or a negative value if the PLCA or PHY + * configuration is not consistent. + */ +static int plca_check_valid(struct phy_device *phydev, +			    const struct phy_plca_cfg *plca_cfg, +			    struct netlink_ext_ack *extack) +{ +	int ret = 0; + +	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, +			       phydev->advertising)) { +		ret = -EOPNOTSUPP; +		NL_SET_ERR_MSG(extack, +			       "Point to Multi-Point mode is not enabled"); +	} else if (plca_cfg->node_id >= 255) { +		NL_SET_ERR_MSG(extack, "PLCA node ID is not set"); +		ret = -EINVAL; +	} + +	return ret; +} + +/** + * phy_ethtool_set_plca_cfg - Set PLCA RS configuration + * @phydev: the phy_device struct + * @plca_cfg: new PLCA configuration to apply + * @extack: extack for reporting useful error messages + * + * Sets the PLCA configuration in the PHY. Return 0 on success or a + * negative value if an error occurred. + */ +int phy_ethtool_set_plca_cfg(struct phy_device *phydev, +			     const struct phy_plca_cfg *plca_cfg, +			     struct netlink_ext_ack *extack) +{ +	struct phy_plca_cfg *curr_plca_cfg; +	int ret; + +	if (!phydev->drv) { +		ret = -EIO; +		goto out; +	} + +	if (!phydev->drv->set_plca_cfg || +	    !phydev->drv->get_plca_cfg) { +		ret = -EOPNOTSUPP; +		goto out; +	} + +	curr_plca_cfg = kmalloc(sizeof(*curr_plca_cfg), GFP_KERNEL); +	if (!curr_plca_cfg) { +		ret = -ENOMEM; +		goto out; +	} + +	mutex_lock(&phydev->lock); + +	ret = phydev->drv->get_plca_cfg(phydev, curr_plca_cfg); +	if (ret) +		goto out_drv; + +	if (curr_plca_cfg->enabled < 0 && plca_cfg->enabled >= 0) { +		NL_SET_ERR_MSG(extack, +			       "PHY does not support changing the PLCA 'enable' attribute"); +		ret = -EINVAL; +		goto out_drv; +	} + +	if (curr_plca_cfg->node_id < 0 && plca_cfg->node_id >= 0) { +		NL_SET_ERR_MSG(extack, +			       "PHY does not support changing the PLCA 'local node ID' attribute"); +		ret = -EINVAL; +		goto out_drv; +	} + +	if (curr_plca_cfg->node_cnt < 0 && plca_cfg->node_cnt >= 0) { +		NL_SET_ERR_MSG(extack, +			       "PHY does not support changing the PLCA 'node count' attribute"); +		ret = -EINVAL; +		goto out_drv; +	} + +	if (curr_plca_cfg->to_tmr < 0 && plca_cfg->to_tmr >= 0) { +		NL_SET_ERR_MSG(extack, +			       "PHY does not support changing the PLCA 'TO timer' attribute"); +		ret = -EINVAL; +		goto out_drv; +	} + +	if (curr_plca_cfg->burst_cnt < 0 && plca_cfg->burst_cnt >= 0) { +		NL_SET_ERR_MSG(extack, +			       "PHY does not support changing the PLCA 'burst count' attribute"); +		ret = -EINVAL; +		goto out_drv; +	} + +	if (curr_plca_cfg->burst_tmr < 0 && plca_cfg->burst_tmr >= 0) { +		NL_SET_ERR_MSG(extack, +			       "PHY does not support changing the PLCA 'burst timer' attribute"); +		ret = -EINVAL; +		goto out_drv; +	} + +	// if enabling PLCA, perform a few sanity checks +	if (plca_cfg->enabled > 0) { +		// allow setting node_id concurrently with enabled +		if (plca_cfg->node_id >= 0) +			curr_plca_cfg->node_id = plca_cfg->node_id; + +		ret = plca_check_valid(phydev, curr_plca_cfg, extack); +		if (ret) +			goto out_drv; +	} + +	ret = phydev->drv->set_plca_cfg(phydev, plca_cfg); + +out_drv: +	kfree(curr_plca_cfg); +	mutex_unlock(&phydev->lock); +out: +	return ret; +} + +/** + * phy_ethtool_get_plca_status - Get PLCA RS status information + * @phydev: the phy_device struct + * @plca_st: where to store the retrieved status information + * + * Retrieve the PLCA status information from the PHY. Return 0 on success or a + * negative value if an error occurred. + */ +int phy_ethtool_get_plca_status(struct phy_device *phydev, +				struct phy_plca_status *plca_st) +{ +	int ret; + +	if (!phydev->drv) { +		ret = -EIO; +		goto out; +	} + +	if (!phydev->drv->get_plca_status) { +		ret = -EOPNOTSUPP; +		goto out; +	} + +	mutex_lock(&phydev->lock); +	ret = phydev->drv->get_plca_status(phydev, plca_st); + +	mutex_unlock(&phydev->lock); +out: +	return ret; +} + +/**   * phy_start_cable_test - Start a cable test   *   * @phydev: the phy_device struct @@ -877,27 +1069,35 @@ EXPORT_SYMBOL(phy_ethtool_ksettings_set);  int phy_speed_down(struct phy_device *phydev, bool sync)  {  	__ETHTOOL_DECLARE_LINK_MODE_MASK(adv_tmp); -	int ret; +	int ret = 0; + +	mutex_lock(&phydev->lock);  	if (phydev->autoneg != AUTONEG_ENABLE) -		return 0; +		goto out;  	linkmode_copy(adv_tmp, phydev->advertising);  	ret = phy_speed_down_core(phydev);  	if (ret) -		return ret; +		goto out;  	linkmode_copy(phydev->adv_old, adv_tmp); -	if (linkmode_equal(phydev->advertising, adv_tmp)) -		return 0; +	if (linkmode_equal(phydev->advertising, adv_tmp)) { +		ret = 0; +		goto out; +	}  	ret = phy_config_aneg(phydev);  	if (ret) -		return ret; +		goto out; + +	ret = sync ? phy_poll_aneg_done(phydev) : 0; +out: +	mutex_unlock(&phydev->lock); -	return sync ? phy_poll_aneg_done(phydev) : 0; +	return ret;  }  EXPORT_SYMBOL_GPL(phy_speed_down); @@ -910,21 +1110,28 @@ EXPORT_SYMBOL_GPL(phy_speed_down);  int phy_speed_up(struct phy_device *phydev)  {  	__ETHTOOL_DECLARE_LINK_MODE_MASK(adv_tmp); +	int ret = 0; + +	mutex_lock(&phydev->lock);  	if (phydev->autoneg != AUTONEG_ENABLE) -		return 0; +		goto out;  	if (linkmode_empty(phydev->adv_old)) -		return 0; +		goto out;  	linkmode_copy(adv_tmp, phydev->advertising);  	linkmode_copy(phydev->advertising, phydev->adv_old);  	linkmode_zero(phydev->adv_old);  	if (linkmode_equal(phydev->advertising, adv_tmp)) -		return 0; +		goto out; + +	ret = phy_config_aneg(phydev); +out: +	mutex_unlock(&phydev->lock); -	return phy_config_aneg(phydev); +	return ret;  }  EXPORT_SYMBOL_GPL(phy_speed_up); @@ -1265,30 +1472,6 @@ void phy_mac_interrupt(struct phy_device *phydev)  }  EXPORT_SYMBOL(phy_mac_interrupt); -static void mmd_eee_adv_to_linkmode(unsigned long *advertising, u16 eee_adv) -{ -	linkmode_zero(advertising); - -	if (eee_adv & MDIO_EEE_100TX) -		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, -				 advertising); -	if (eee_adv & MDIO_EEE_1000T) -		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, -				 advertising); -	if (eee_adv & MDIO_EEE_10GT) -		linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, -				 advertising); -	if (eee_adv & MDIO_EEE_1000KX) -		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, -				 advertising); -	if (eee_adv & MDIO_EEE_10GKX4) -		linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, -				 advertising); -	if (eee_adv & MDIO_EEE_10GKR) -		linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, -				 advertising); -} -  /**   * phy_init_eee - init and check the EEE feature   * @phydev: target phy_device struct @@ -1301,62 +1484,25 @@ static void mmd_eee_adv_to_linkmode(unsigned long *advertising, u16 eee_adv)   */  int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)  { +	int ret; +  	if (!phydev->drv)  		return -EIO; -	/* According to 802.3az,the EEE is supported only in full duplex-mode. -	 */ -	if (phydev->duplex == DUPLEX_FULL) { -		__ETHTOOL_DECLARE_LINK_MODE_MASK(common); -		__ETHTOOL_DECLARE_LINK_MODE_MASK(lp); -		__ETHTOOL_DECLARE_LINK_MODE_MASK(adv); -		int eee_lp, eee_cap, eee_adv; -		int status; -		u32 cap; - -		/* Read phy status to properly get the right settings */ -		status = phy_read_status(phydev); -		if (status) -			return status; - -		/* First check if the EEE ability is supported */ -		eee_cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); -		if (eee_cap <= 0) -			goto eee_exit_err; - -		cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap); -		if (!cap) -			goto eee_exit_err; - -		/* Check which link settings negotiated and verify it in -		 * the EEE advertising registers. -		 */ -		eee_lp = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); -		if (eee_lp <= 0) -			goto eee_exit_err; - -		eee_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); -		if (eee_adv <= 0) -			goto eee_exit_err; - -		mmd_eee_adv_to_linkmode(adv, eee_adv); -		mmd_eee_adv_to_linkmode(lp, eee_lp); -		linkmode_and(common, adv, lp); - -		if (!phy_check_valid(phydev->speed, phydev->duplex, common)) -			goto eee_exit_err; +	ret = genphy_c45_eee_is_active(phydev, NULL, NULL, NULL); +	if (ret < 0) +		return ret; +	if (!ret) +		return -EPROTONOSUPPORT; -		if (clk_stop_enable) -			/* Configure the PHY to stop receiving xMII -			 * clock while it is signaling LPI. -			 */ -			phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, -					 MDIO_PCS_CTRL1_CLKSTOP_EN); +	if (clk_stop_enable) +		/* Configure the PHY to stop receiving xMII +		 * clock while it is signaling LPI. +		 */ +		ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, +				       MDIO_PCS_CTRL1_CLKSTOP_EN); -		return 0; /* EEE supported */ -	} -eee_exit_err: -	return -EPROTONOSUPPORT; +	return ret < 0 ? ret : 0;  }  EXPORT_SYMBOL(phy_init_eee); @@ -1369,10 +1515,16 @@ EXPORT_SYMBOL(phy_init_eee);   */  int phy_get_eee_err(struct phy_device *phydev)  { +	int ret; +  	if (!phydev->drv)  		return -EIO; -	return phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_WK_ERR); +	mutex_lock(&phydev->lock); +	ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_WK_ERR); +	mutex_unlock(&phydev->lock); + +	return ret;  }  EXPORT_SYMBOL(phy_get_eee_err); @@ -1386,33 +1538,16 @@ EXPORT_SYMBOL(phy_get_eee_err);   */  int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)  { -	int val; +	int ret;  	if (!phydev->drv)  		return -EIO; -	/* Get Supported EEE */ -	val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); -	if (val < 0) -		return val; -	data->supported = mmd_eee_cap_to_ethtool_sup_t(val); - -	/* Get advertisement EEE */ -	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); -	if (val < 0) -		return val; -	data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); -	data->eee_enabled = !!data->advertised; - -	/* Get LP advertisement EEE */ -	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); -	if (val < 0) -		return val; -	data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); - -	data->eee_active = !!(data->advertised & data->lp_advertised); +	mutex_lock(&phydev->lock); +	ret = genphy_c45_ethtool_get_eee(phydev, data); +	mutex_unlock(&phydev->lock); -	return 0; +	return ret;  }  EXPORT_SYMBOL(phy_ethtool_get_eee); @@ -1425,43 +1560,16 @@ EXPORT_SYMBOL(phy_ethtool_get_eee);   */  int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)  { -	int cap, old_adv, adv = 0, ret; +	int ret;  	if (!phydev->drv)  		return -EIO; -	/* Get Supported EEE */ -	cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); -	if (cap < 0) -		return cap; - -	old_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); -	if (old_adv < 0) -		return old_adv; - -	if (data->eee_enabled) { -		adv = !data->advertised ? cap : -		      ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap; -		/* Mask prohibited EEE modes */ -		adv &= ~phydev->eee_broken_modes; -	} - -	if (old_adv != adv) { -		ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); -		if (ret < 0) -			return ret; - -		/* Restart autonegotiation so the new modes get sent to the -		 * link partner. -		 */ -		if (phydev->autoneg == AUTONEG_ENABLE) { -			ret = phy_restart_aneg(phydev); -			if (ret < 0) -				return ret; -		} -	} +	mutex_lock(&phydev->lock); +	ret = genphy_c45_ethtool_set_eee(phydev, data); +	mutex_unlock(&phydev->lock); -	return 0; +	return ret;  }  EXPORT_SYMBOL(phy_ethtool_set_eee); @@ -1473,8 +1581,15 @@ EXPORT_SYMBOL(phy_ethtool_set_eee);   */  int phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)  { -	if (phydev->drv && phydev->drv->set_wol) -		return phydev->drv->set_wol(phydev, wol); +	int ret; + +	if (phydev->drv && phydev->drv->set_wol) { +		mutex_lock(&phydev->lock); +		ret = phydev->drv->set_wol(phydev, wol); +		mutex_unlock(&phydev->lock); + +		return ret; +	}  	return -EOPNOTSUPP;  } @@ -1488,8 +1603,11 @@ EXPORT_SYMBOL(phy_ethtool_set_wol);   */  void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)  { -	if (phydev->drv && phydev->drv->get_wol) +	if (phydev->drv && phydev->drv->get_wol) { +		mutex_lock(&phydev->lock);  		phydev->drv->get_wol(phydev, wol); +		mutex_unlock(&phydev->lock); +	}  }  EXPORT_SYMBOL(phy_ethtool_get_wol); @@ -1526,6 +1644,7 @@ EXPORT_SYMBOL(phy_ethtool_set_link_ksettings);  int phy_ethtool_nway_reset(struct net_device *ndev)  {  	struct phy_device *phydev = ndev->phydev; +	int ret;  	if (!phydev)  		return -ENODEV; @@ -1533,6 +1652,10 @@ int phy_ethtool_nway_reset(struct net_device *ndev)  	if (!phydev->drv)  		return -EIO; -	return phy_restart_aneg(phydev); +	mutex_lock(&phydev->lock); +	ret = phy_restart_aneg(phydev); +	mutex_unlock(&phydev->lock); + +	return ret;  }  EXPORT_SYMBOL(phy_ethtool_nway_reset);  |