Commit a23a1e57 authored by Piergiorgio Beruto's avatar Piergiorgio Beruto Committed by David S. Miller
Browse files

drivers/net/phy: add connection between ethtool and phylib for PLCA



This patch adds the required connection between netlink ethtool and
phylib to resolve PLCA get/set config and get status messages.

Signed-off-by: default avatarPiergiorgio Beruto <piergiorgio.beruto@gmail.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 16178c8e
Loading
Loading
Loading
Loading
+192 −0
Original line number Diff line number Diff line
@@ -543,6 +543,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
 *
+3 −0
Original line number Diff line number Diff line
@@ -3283,6 +3283,9 @@ static const struct ethtool_phy_ops phy_ethtool_phy_ops = {
	.get_sset_count		= phy_ethtool_get_sset_count,
	.get_strings		= phy_ethtool_get_strings,
	.get_stats		= phy_ethtool_get_stats,
	.get_plca_cfg		= phy_ethtool_get_plca_cfg,
	.set_plca_cfg		= phy_ethtool_set_plca_cfg,
	.get_plca_status	= phy_ethtool_get_plca_status,
	.start_cable_test	= phy_start_cable_test,
	.start_cable_test_tdr	= phy_start_cable_test_tdr,
};
+7 −0
Original line number Diff line number Diff line
@@ -1851,6 +1851,13 @@ int phy_ethtool_get_strings(struct phy_device *phydev, u8 *data);
int phy_ethtool_get_sset_count(struct phy_device *phydev);
int phy_ethtool_get_stats(struct phy_device *phydev,
			  struct ethtool_stats *stats, u64 *data);
int phy_ethtool_get_plca_cfg(struct phy_device *phydev,
			     struct phy_plca_cfg *plca_cfg);
int phy_ethtool_set_plca_cfg(struct phy_device *phydev,
			     const struct phy_plca_cfg *plca_cfg,
			     struct netlink_ext_ack *extack);
int phy_ethtool_get_plca_status(struct phy_device *phydev,
				struct phy_plca_status *plca_st);

static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
{