Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04cc8cac authored by Ben Hutchings's avatar Ben Hutchings Committed by David S. Miller
Browse files

sfc: Implement auto-negotiation



Add infrastructure for auto-negotiation of speed, duplex and flow
control.

When using 10Xpress, auto-negotiate flow control.  While we're
at it, clean up the code to warn when partner is not 10GBASE-T
capable.

Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 177dfcd8
Loading
Loading
Loading
Loading
+48 −9
Original line number Original line Diff line number Diff line
@@ -12,11 +12,13 @@
#include <linux/ethtool.h>
#include <linux/ethtool.h>
#include <linux/rtnetlink.h>
#include <linux/rtnetlink.h>
#include "net_driver.h"
#include "net_driver.h"
#include "workarounds.h"
#include "selftest.h"
#include "selftest.h"
#include "efx.h"
#include "efx.h"
#include "ethtool.h"
#include "ethtool.h"
#include "falcon.h"
#include "falcon.h"
#include "spi.h"
#include "spi.h"
#include "mdio_10g.h"


const char *efx_loopback_mode_names[] = {
const char *efx_loopback_mode_names[] = {
	[LOOPBACK_NONE]		= "NONE",
	[LOOPBACK_NONE]		= "NONE",
@@ -674,14 +676,51 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
				      struct ethtool_pauseparam *pause)
				      struct ethtool_pauseparam *pause)
{
{
	struct efx_nic *efx = netdev_priv(net_dev);
	struct efx_nic *efx = netdev_priv(net_dev);
	enum efx_fc_type flow_control = efx->flow_control;
	enum efx_fc_type wanted_fc;
	bool reset;


	flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO);
	wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) |
	flow_control |= pause->rx_pause ? EFX_FC_RX : 0;
		     (pause->tx_pause ? EFX_FC_TX : 0) |
	flow_control |= pause->tx_pause ? EFX_FC_TX : 0;
		     (pause->autoneg ? EFX_FC_AUTO : 0));
	flow_control |= pause->autoneg ? EFX_FC_AUTO : 0;

	if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) {
		EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n");
		return -EINVAL;
	}

	if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) &&
	    (wanted_fc & EFX_FC_AUTO)) {
		EFX_LOG(efx, "PHY does not support flow control "
			"autonegotiation\n");
		return -EINVAL;
	}

	/* TX flow control may automatically turn itself off if the
	 * link partner (intermittently) stops responding to pause
	 * frames. There isn't any indication that this has happened,
	 * so the best we do is leave it up to the user to spot this
	 * and fix it be cycling transmit flow control on this end. */
	reset = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX);
	if (EFX_WORKAROUND_11482(efx) && reset) {
		if (falcon_rev(efx) >= FALCON_REV_B0) {
			/* Recover by resetting the EM block */
			if (efx->link_up)
				falcon_drain_tx_fifo(efx);
		} else {
			/* Schedule a reset to recover */
			efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
		}
	}

	/* Try to push the pause parameters */
	mutex_lock(&efx->mac_lock);

	efx->wanted_fc = wanted_fc;
	mdio_clause45_set_pause(efx);
	__efx_reconfigure_port(efx);

	mutex_unlock(&efx->mac_lock);


	efx_reconfigure_port(efx);
	return 0;
	return 0;
}
}


@@ -690,9 +729,9 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
{
{
	struct efx_nic *efx = netdev_priv(net_dev);
	struct efx_nic *efx = netdev_priv(net_dev);


	pause->rx_pause = !!(efx->flow_control & EFX_FC_RX);
	pause->rx_pause = !!(efx->wanted_fc & EFX_FC_RX);
	pause->tx_pause = !!(efx->flow_control & EFX_FC_TX);
	pause->tx_pause = !!(efx->wanted_fc & EFX_FC_TX);
	pause->autoneg = !!(efx->flow_control & EFX_FC_AUTO);
	pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO);
}
}




+3 −3
Original line number Original line Diff line number Diff line
@@ -1998,7 +1998,7 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx)
	/* Transmission of pause frames when RX crosses the threshold is
	/* Transmission of pause frames when RX crosses the threshold is
	 * covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL.
	 * covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL.
	 * Action on receipt of pause frames is controller by XM_DIS_FCNTL */
	 * Action on receipt of pause frames is controller by XM_DIS_FCNTL */
	tx_fc = !!(efx->flow_control & EFX_FC_TX);
	tx_fc = !!(efx->link_fc & EFX_FC_TX);
	falcon_read(efx, &reg, RX_CFG_REG_KER);
	falcon_read(efx, &reg, RX_CFG_REG_KER);
	EFX_SET_OWORD_FIELD_VER(efx, reg, RX_XOFF_MAC_EN, tx_fc);
	EFX_SET_OWORD_FIELD_VER(efx, reg, RX_XOFF_MAC_EN, tx_fc);


@@ -2328,9 +2328,9 @@ int falcon_probe_port(struct efx_nic *efx)


	/* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */
	/* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */
	if (falcon_rev(efx) >= FALCON_REV_B0)
	if (falcon_rev(efx) >= FALCON_REV_B0)
		efx->flow_control = EFX_FC_RX | EFX_FC_TX;
		efx->wanted_fc = EFX_FC_RX | EFX_FC_TX;
	else
	else
		efx->flow_control = EFX_FC_RX;
		efx->wanted_fc = EFX_FC_RX;


	/* Allocate buffer for stats */
	/* Allocate buffer for stats */
	rc = falcon_alloc_buffer(efx, &efx->stats_buffer,
	rc = falcon_alloc_buffer(efx, &efx->stats_buffer,
+2 −2
Original line number Original line Diff line number Diff line
@@ -31,8 +31,8 @@ static void falcon_reconfigure_gmac(struct efx_nic *efx)
	efx_oword_t reg;
	efx_oword_t reg;


	/* Configuration register 1 */
	/* Configuration register 1 */
	tx_fc = (efx->flow_control & EFX_FC_TX) || !efx->link_fd;
	tx_fc = (efx->link_fc & EFX_FC_TX) || !efx->link_fd;
	rx_fc = !!(efx->flow_control & EFX_FC_RX);
	rx_fc = !!(efx->link_fc & EFX_FC_RX);
	loopback = (efx->loopback_mode == LOOPBACK_GMAC);
	loopback = (efx->loopback_mode == LOOPBACK_GMAC);
	bytemode = (efx->link_speed == 1000);
	bytemode = (efx->link_speed == 1000);


+1 −1
Original line number Original line Diff line number Diff line
@@ -142,7 +142,7 @@ static void falcon_reconfigure_xmac_core(struct efx_nic *efx)
{
{
	unsigned int max_frame_len;
	unsigned int max_frame_len;
	efx_oword_t reg;
	efx_oword_t reg;
	bool rx_fc = !!(efx->flow_control & EFX_FC_RX);
	bool rx_fc = !!(efx->link_fc & EFX_FC_RX);


	/* Configure MAC  - cut-thru mode is hard wired on */
	/* Configure MAC  - cut-thru mode is hard wired on */
	EFX_POPULATE_DWORD_3(reg,
	EFX_POPULATE_DWORD_3(reg,
+277 −71
Original line number Original line Diff line number Diff line
@@ -47,14 +47,17 @@ static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd,
	if (LOOPBACK_INTERNAL(efx))
	if (LOOPBACK_INTERNAL(efx))
		return 0;
		return 0;


	if (mmd != MDIO_MMD_AN) {
		/* Read MMD STATUS2 to check it is responding. */
		/* Read MMD STATUS2 to check it is responding. */
	status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2);
		status = mdio_clause45_read(efx, phy_id, mmd,
					    MDIO_MMDREG_STAT2);
		if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) &
		if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) &
		     ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) !=
		     ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) !=
		    MDIO_MMDREG_STAT2_PRESENT_VAL) {
		    MDIO_MMDREG_STAT2_PRESENT_VAL) {
			EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd);
			EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd);
			return -EIO;
			return -EIO;
		}
		}
	}


	/* Read MMD STATUS 1 to check for fault. */
	/* Read MMD STATUS 1 to check for fault. */
	status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT1);
	status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT1);
@@ -179,12 +182,15 @@ bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
	else if (efx->loopback_mode == LOOPBACK_PHYXS)
	else if (efx->loopback_mode == LOOPBACK_PHYXS)
		mmd_mask &= ~(MDIO_MMDREG_DEVS_PHYXS |
		mmd_mask &= ~(MDIO_MMDREG_DEVS_PHYXS |
			      MDIO_MMDREG_DEVS_PCS |
			      MDIO_MMDREG_DEVS_PCS |
			      MDIO_MMDREG_DEVS_PMAPMD);
			      MDIO_MMDREG_DEVS_PMAPMD |
			      MDIO_MMDREG_DEVS_AN);
	else if (efx->loopback_mode == LOOPBACK_PCS)
	else if (efx->loopback_mode == LOOPBACK_PCS)
		mmd_mask &= ~(MDIO_MMDREG_DEVS_PCS |
		mmd_mask &= ~(MDIO_MMDREG_DEVS_PCS |
			      MDIO_MMDREG_DEVS_PMAPMD);
			      MDIO_MMDREG_DEVS_PMAPMD |
			      MDIO_MMDREG_DEVS_AN);
	else if (efx->loopback_mode == LOOPBACK_PMAPMD)
	else if (efx->loopback_mode == LOOPBACK_PMAPMD)
		mmd_mask &= ~MDIO_MMDREG_DEVS_PMAPMD;
		mmd_mask &= ~(MDIO_MMDREG_DEVS_PMAPMD |
			      MDIO_MMDREG_DEVS_AN);


	while (mmd_mask) {
	while (mmd_mask) {
		if (mmd_mask & 1) {
		if (mmd_mask & 1) {
@@ -244,6 +250,7 @@ void mdio_clause45_set_mmds_lpower(struct efx_nic *efx,
				   int low_power, unsigned int mmd_mask)
				   int low_power, unsigned int mmd_mask)
{
{
	int mmd = 0;
	int mmd = 0;
	mmd_mask &= ~MDIO_MMDREG_DEVS_AN;
	while (mmd_mask) {
	while (mmd_mask) {
		if (mmd_mask & 1)
		if (mmd_mask & 1)
			mdio_clause45_set_mmd_lpower(efx, low_power, mmd);
			mdio_clause45_set_mmd_lpower(efx, low_power, mmd);
@@ -252,103 +259,302 @@ void mdio_clause45_set_mmds_lpower(struct efx_nic *efx,
	}
	}
}
}


static u32 mdio_clause45_get_an(struct efx_nic *efx, u16 addr, u32 xnp)
{
	int phy_id = efx->mii.phy_id;
	u32 result = 0;
	int reg;

	reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, addr);
	if (reg & ADVERTISE_10HALF)
		result |= ADVERTISED_10baseT_Half;
	if (reg & ADVERTISE_10FULL)
		result |= ADVERTISED_10baseT_Full;
	if (reg & ADVERTISE_100HALF)
		result |= ADVERTISED_100baseT_Half;
	if (reg & ADVERTISE_100FULL)
		result |= ADVERTISED_100baseT_Full;
	if (reg & LPA_RESV)
		result |= xnp;

	return result;
}

/**
/**
 * mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO.
 * mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO.
 * @efx:		Efx NIC
 * @efx:		Efx NIC
 * @ecmd: 		Buffer for settings
 * @ecmd: 		Buffer for settings
 *
 *
 * On return the 'port', 'speed', 'supported' and 'advertising' fields of
 * On return the 'port', 'speed', 'supported' and 'advertising' fields of
 * ecmd have been filled out based on the PMA type.
 * ecmd have been filled out.
 */
 */
void mdio_clause45_get_settings(struct efx_nic *efx,
void mdio_clause45_get_settings(struct efx_nic *efx,
				struct ethtool_cmd *ecmd)
				struct ethtool_cmd *ecmd)
{
{
	int pma_type;
	mdio_clause45_get_settings_ext(efx, ecmd, 0, 0);

	/* If no PMA is present we are presumably talking something XAUI-ish
	 * like CX4. Which we report as FIBRE (see below) */
	if ((efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)) == 0) {
		ecmd->speed = SPEED_10000;
		ecmd->port = PORT_FIBRE;
		ecmd->supported = SUPPORTED_FIBRE;
		ecmd->advertising = ADVERTISED_FIBRE;
		return;
}
}


	pma_type = mdio_clause45_read(efx, efx->mii.phy_id,
/**
				      MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2);
 * mdio_clause45_get_settings_ext - Read (some of) the PHY settings over MDIO.
	pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK;
 * @efx:		Efx NIC
 * @ecmd: 		Buffer for settings
 * @xnp:		Advertised Extended Next Page state
 * @xnp_lpa:		Link Partner's advertised XNP state
 *
 * On return the 'port', 'speed', 'supported' and 'advertising' fields of
 * ecmd have been filled out.
 */
void mdio_clause45_get_settings_ext(struct efx_nic *efx,
				    struct ethtool_cmd *ecmd,
				    u32 xnp, u32 xnp_lpa)
{
	int phy_id = efx->mii.phy_id;
	int reg;


	switch (pma_type) {
	ecmd->transceiver = XCVR_INTERNAL;
		/* We represent CX4 as fibre in the absence of anything
	ecmd->phy_address = phy_id;
		   better. */

	case MDIO_PMAPMD_CTRL2_10G_CX4:
	reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
		ecmd->speed = SPEED_10000;
				 MDIO_MMDREG_CTRL2);
		ecmd->port = PORT_FIBRE;
	switch (reg & MDIO_PMAPMD_CTRL2_TYPE_MASK) {
		ecmd->supported = SUPPORTED_FIBRE;
		ecmd->advertising = ADVERTISED_FIBRE;
		break;
		/* 10G Base-T */
	case MDIO_PMAPMD_CTRL2_10G_BT:
	case MDIO_PMAPMD_CTRL2_10G_BT:
		ecmd->speed = SPEED_10000;
		ecmd->port = PORT_TP;
		ecmd->supported = SUPPORTED_TP | SUPPORTED_10000baseT_Full;
		ecmd->advertising = (ADVERTISED_FIBRE
				     | ADVERTISED_10000baseT_Full);
		break;
	case MDIO_PMAPMD_CTRL2_1G_BT:
	case MDIO_PMAPMD_CTRL2_1G_BT:
		ecmd->speed = SPEED_1000;
		ecmd->port = PORT_TP;
		ecmd->supported = SUPPORTED_TP | SUPPORTED_1000baseT_Full;
		ecmd->advertising = (ADVERTISED_FIBRE
				     | ADVERTISED_1000baseT_Full);
		break;
	case MDIO_PMAPMD_CTRL2_100_BT:
	case MDIO_PMAPMD_CTRL2_100_BT:
		ecmd->speed = SPEED_100;
		ecmd->port = PORT_TP;
		ecmd->supported = SUPPORTED_TP | SUPPORTED_100baseT_Full;
		ecmd->advertising = (ADVERTISED_FIBRE
				     | ADVERTISED_100baseT_Full);
		break;
	case MDIO_PMAPMD_CTRL2_10_BT:
	case MDIO_PMAPMD_CTRL2_10_BT:
		ecmd->speed = SPEED_10;
		ecmd->port = PORT_TP;
		ecmd->port = PORT_TP;
		ecmd->supported = SUPPORTED_TP | SUPPORTED_10baseT_Full;
		ecmd->supported = SUPPORTED_TP;
		ecmd->advertising = ADVERTISED_FIBRE | ADVERTISED_10baseT_Full;
		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
					 MDIO_MMDREG_SPEED);
		if (reg & (1 << MDIO_MMDREG_SPEED_10G_LBN))
			ecmd->supported |= SUPPORTED_10000baseT_Full;
		if (reg & (1 << MDIO_MMDREG_SPEED_1000M_LBN))
			ecmd->supported |= (SUPPORTED_1000baseT_Full |
					    SUPPORTED_1000baseT_Half);
		if (reg & (1 << MDIO_MMDREG_SPEED_100M_LBN))
			ecmd->supported |= (SUPPORTED_100baseT_Full |
					    SUPPORTED_100baseT_Half);
		if (reg & (1 << MDIO_MMDREG_SPEED_10M_LBN))
			ecmd->supported |= (SUPPORTED_10baseT_Full |
					    SUPPORTED_10baseT_Half);
		ecmd->advertising = ADVERTISED_TP;
		break;
		break;
	/* All the other defined modes are flavours of

	 * 10G optical */
	/* We represent CX4 as fibre in the absence of anything better */
	case MDIO_PMAPMD_CTRL2_10G_CX4:
	/* All the other defined modes are flavours of optical */
	default:
	default:
		ecmd->speed = SPEED_10000;
		ecmd->port = PORT_FIBRE;
		ecmd->port = PORT_FIBRE;
		ecmd->supported = SUPPORTED_FIBRE;
		ecmd->supported = SUPPORTED_FIBRE;
		ecmd->advertising = ADVERTISED_FIBRE;
		ecmd->advertising = ADVERTISED_FIBRE;
		break;
		break;
	}
	}

	if (efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) {
		ecmd->supported |= SUPPORTED_Autoneg;
		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
					 MDIO_MMDREG_CTRL1);
		if (reg & BMCR_ANENABLE) {
			ecmd->autoneg = AUTONEG_ENABLE;
			ecmd->advertising |=
				ADVERTISED_Autoneg |
				mdio_clause45_get_an(efx,
						     MDIO_AN_ADVERTISE, xnp);
		} else
			ecmd->autoneg = AUTONEG_DISABLE;
	} else
		ecmd->autoneg = AUTONEG_DISABLE;

	/* If AN is enabled and complete, report best common mode */
	if (ecmd->autoneg &&
	    (mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, MDIO_MMDREG_STAT1) &
	     (1 << MDIO_AN_STATUS_AN_DONE_LBN))) {
		u32 common, lpa;
		lpa = mdio_clause45_get_an(efx, MDIO_AN_LPA, xnp_lpa);
		common = ecmd->advertising & lpa;
		if (common & ADVERTISED_10000baseT_Full) {
			ecmd->speed = SPEED_10000;
			ecmd->duplex = DUPLEX_FULL;
		} else if (common & (ADVERTISED_1000baseT_Full |
				     ADVERTISED_1000baseT_Half)) {
			ecmd->speed = SPEED_1000;
			ecmd->duplex = !!(common & ADVERTISED_1000baseT_Full);
		} else if (common & (ADVERTISED_100baseT_Full |
				     ADVERTISED_100baseT_Half)) {
			ecmd->speed = SPEED_100;
			ecmd->duplex = !!(common & ADVERTISED_100baseT_Full);
		} else {
			ecmd->speed = SPEED_10;
			ecmd->duplex = !!(common & ADVERTISED_10baseT_Full);
		}
	} else {
		/* Report forced settings */
		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
					 MDIO_MMDREG_CTRL1);
		ecmd->speed = (((reg & BMCR_SPEED1000) ? 100 : 1) *
			       ((reg & BMCR_SPEED100) ? 100 : 10));
		ecmd->duplex = (reg & BMCR_FULLDPLX ||
				ecmd->speed == SPEED_10000);
	}
}
}


/**
/**
 * mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO.
 * mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO.
 * @efx:		Efx NIC
 * @efx:		Efx NIC
 * @ecmd: 		New settings
 * @ecmd: 		New settings
 *
 * Currently this just enforces that we are _not_ changing the
 * 'port', 'speed', 'supported' or 'advertising' settings as these
 * cannot be changed on any currently supported PHY.
 */
 */
int mdio_clause45_set_settings(struct efx_nic *efx,
int mdio_clause45_set_settings(struct efx_nic *efx,
			       struct ethtool_cmd *ecmd)
			       struct ethtool_cmd *ecmd)
{
{
	struct ethtool_cmd tmpcmd;
	int phy_id = efx->mii.phy_id;
	mdio_clause45_get_settings(efx, &tmpcmd);
	struct ethtool_cmd prev;
	/* None of the current PHYs support more than one mode
	u32 required;
	 * of operation (and only 10GBT ever will), so keep things
	int ctrl1_bits, reg;
	 * simple for now */

	if ((ecmd->speed == tmpcmd.speed) && (ecmd->port == tmpcmd.port) &&
	efx->phy_op->get_settings(efx, &prev);
	    (ecmd->supported == tmpcmd.supported) &&

	    (ecmd->advertising == tmpcmd.advertising))
	if (ecmd->advertising == prev.advertising &&
	    ecmd->speed == prev.speed &&
	    ecmd->duplex == prev.duplex &&
	    ecmd->port == prev.port &&
	    ecmd->autoneg == prev.autoneg)
		return 0;

	/* We can only change these settings for -T PHYs */
	if (prev.port != PORT_TP || ecmd->port != PORT_TP)
		return -EINVAL;

	/* Check that PHY supports these settings and work out the
	 * basic control bits */
	if (ecmd->duplex) {
		switch (ecmd->speed) {
		case SPEED_10:
			ctrl1_bits = BMCR_FULLDPLX;
			required = SUPPORTED_10baseT_Full;
			break;
		case SPEED_100:
			ctrl1_bits = BMCR_SPEED100 | BMCR_FULLDPLX;
			required = SUPPORTED_100baseT_Full;
			break;
		case SPEED_1000:
			ctrl1_bits = BMCR_SPEED1000 | BMCR_FULLDPLX;
			required = SUPPORTED_1000baseT_Full;
			break;
		case SPEED_10000:
			ctrl1_bits = (BMCR_SPEED1000 | BMCR_SPEED100 |
				      BMCR_FULLDPLX);
			required = SUPPORTED_10000baseT_Full;
			break;
		default:
			return -EINVAL;
		}
	} else {
		switch (ecmd->speed) {
		case SPEED_10:
			ctrl1_bits = 0;
			required = SUPPORTED_10baseT_Half;
			break;
		case SPEED_100:
			ctrl1_bits = BMCR_SPEED100;
			required = SUPPORTED_100baseT_Half;
			break;
		case SPEED_1000:
			ctrl1_bits = BMCR_SPEED1000;
			required = SUPPORTED_1000baseT_Half;
			break;
		default:
			return -EINVAL;
		}
	}
	if (ecmd->autoneg)
		required |= SUPPORTED_Autoneg;
	required |= ecmd->advertising;
	if (required & ~prev.supported)
		return -EINVAL;

	/* Set the basic control bits */
	reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
				 MDIO_MMDREG_CTRL1);
	reg &= ~(BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_FULLDPLX | 0x003c);
	reg |= ctrl1_bits;
	mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL1,
			    reg);

	/* Set the AN registers */
	if (ecmd->autoneg != prev.autoneg ||
	    ecmd->advertising != prev.advertising) {
		bool xnp = false;

		if (efx->phy_op->set_xnp_advertise)
			xnp = efx->phy_op->set_xnp_advertise(efx,
							     ecmd->advertising);

		if (ecmd->autoneg) {
			reg = 0;
			if (ecmd->advertising & ADVERTISED_10baseT_Half)
				reg |= ADVERTISE_10HALF;
			if (ecmd->advertising & ADVERTISED_10baseT_Full)
				reg |= ADVERTISE_10FULL;
			if (ecmd->advertising & ADVERTISED_100baseT_Half)
				reg |= ADVERTISE_100HALF;
			if (ecmd->advertising & ADVERTISED_100baseT_Full)
				reg |= ADVERTISE_100FULL;
			if (xnp)
				reg |= ADVERTISE_RESV;
			mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
					    MDIO_AN_ADVERTISE, reg);
		}

		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
					 MDIO_MMDREG_CTRL1);
		if (ecmd->autoneg)
			reg |= BMCR_ANENABLE | BMCR_ANRESTART;
		else
			reg &= ~BMCR_ANENABLE;
		if (xnp)
			reg |= 1 << MDIO_AN_CTRL_XNP_LBN;
		else
			reg &= ~(1 << MDIO_AN_CTRL_XNP_LBN);
		mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
				    MDIO_MMDREG_CTRL1, reg);
	}

	return 0;
	return 0;
	return -EOPNOTSUPP;
}

void mdio_clause45_set_pause(struct efx_nic *efx)
{
	int phy_id = efx->mii.phy_id;
	int reg;

	if (efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) {
		/* Set pause capability advertising */
		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
					 MDIO_AN_ADVERTISE);
		reg &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
		reg |= efx_fc_advertise(efx->wanted_fc);
		mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
				    MDIO_AN_ADVERTISE, reg);

		/* Restart auto-negotiation */
		reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
					 MDIO_MMDREG_CTRL1);
		if (reg & BMCR_ANENABLE) {
			reg |= BMCR_ANRESTART;
			mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
					    MDIO_MMDREG_CTRL1, reg);
		}
	}
}

enum efx_fc_type mdio_clause45_get_pause(struct efx_nic *efx)
{
	int phy_id = efx->mii.phy_id;
	int lpa;

	if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)))
		return efx->wanted_fc;
	lpa = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, MDIO_AN_LPA);
	return efx_fc_resolve(efx->wanted_fc, lpa);
}
}


void mdio_clause45_set_flag(struct efx_nic *efx, u8 prt, u8 dev,
void mdio_clause45_set_flag(struct efx_nic *efx, u8 prt, u8 dev,
Loading