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

Commit d7445d1f authored by Lendacky, Thomas's avatar Lendacky, Thomas Committed by David S. Miller
Browse files

amd-xgbe: Add support for a KR redriver



This patch provides support for the presence of a KR redriver chip in
between the device PCS and an external PHY.  When a redriver chip is
present the device must perform clause 73 auto-negotiation in order to
set the redriver chip for the downstream connection.

Signed-off-by: default avatarTom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 732f2ab7
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1062,6 +1062,16 @@
#define XP_PROP_4_MUX_ADDR_LO_WIDTH		3
#define XP_PROP_4_MUX_CHAN_INDEX		4
#define XP_PROP_4_MUX_CHAN_WIDTH		3
#define XP_PROP_4_REDRV_ADDR_INDEX		16
#define XP_PROP_4_REDRV_ADDR_WIDTH		7
#define XP_PROP_4_REDRV_IF_INDEX		23
#define XP_PROP_4_REDRV_IF_WIDTH		1
#define XP_PROP_4_REDRV_LANE_INDEX		24
#define XP_PROP_4_REDRV_LANE_WIDTH		3
#define XP_PROP_4_REDRV_MODEL_INDEX		28
#define XP_PROP_4_REDRV_MODEL_WIDTH		3
#define XP_PROP_4_REDRV_PRESENT_INDEX		31
#define XP_PROP_4_REDRV_PRESENT_WIDTH		1

/* I2C Control register offsets */
#define IC_CON					0x0000
+32 −13
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ static void xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata)
{
	switch (pdata->an_mode) {
	case XGBE_AN_MODE_CL73:
	case XGBE_AN_MODE_CL73_REDRV:
		xgbe_an73_enable_interrupts(pdata);
		break;
	case XGBE_AN_MODE_CL37:
@@ -254,6 +255,10 @@ static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata)

static void xgbe_sfi_mode(struct xgbe_prv_data *pdata)
{
	/* If a KR re-driver is present, change to KR mode instead */
	if (pdata->kr_redrv)
		return xgbe_kr_mode(pdata);

	/* Disable KR training */
	xgbe_an73_disable_kr_training(pdata);

@@ -433,6 +438,7 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata)
{
	switch (pdata->an_mode) {
	case XGBE_AN_MODE_CL73:
	case XGBE_AN_MODE_CL73_REDRV:
		xgbe_an73_restart(pdata);
		break;
	case XGBE_AN_MODE_CL37:
@@ -448,6 +454,7 @@ static void xgbe_an_disable(struct xgbe_prv_data *pdata)
{
	switch (pdata->an_mode) {
	case XGBE_AN_MODE_CL73:
	case XGBE_AN_MODE_CL73_REDRV:
		xgbe_an73_disable(pdata);
		break;
	case XGBE_AN_MODE_CL37:
@@ -687,6 +694,7 @@ static irqreturn_t xgbe_an_isr(int irq, void *data)

	switch (pdata->an_mode) {
	case XGBE_AN_MODE_CL73:
	case XGBE_AN_MODE_CL73_REDRV:
		xgbe_an73_isr(pdata);
		break;
	case XGBE_AN_MODE_CL37:
@@ -895,6 +903,7 @@ static void xgbe_an_state_machine(struct work_struct *work)

	switch (pdata->an_mode) {
	case XGBE_AN_MODE_CL73:
	case XGBE_AN_MODE_CL73_REDRV:
		xgbe_an73_state_machine(pdata);
		break;
	case XGBE_AN_MODE_CL37:
@@ -910,16 +919,18 @@ static void xgbe_an_state_machine(struct work_struct *work)

static void xgbe_an37_init(struct xgbe_prv_data *pdata)
{
	unsigned int reg;
	unsigned int advertising, reg;

	advertising = pdata->phy_if.phy_impl.an_advertising(pdata);

	/* Set up Advertisement register */
	reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE);
	if (pdata->phy.advertising & ADVERTISED_Pause)
	if (advertising & ADVERTISED_Pause)
		reg |= 0x100;
	else
		reg &= ~0x100;

	if (pdata->phy.advertising & ADVERTISED_Asym_Pause)
	if (advertising & ADVERTISED_Asym_Pause)
		reg |= 0x80;
	else
		reg &= ~0x80;
@@ -954,11 +965,13 @@ static void xgbe_an37_init(struct xgbe_prv_data *pdata)

static void xgbe_an73_init(struct xgbe_prv_data *pdata)
{
	unsigned int reg;
	unsigned int advertising, reg;

	advertising = pdata->phy_if.phy_impl.an_advertising(pdata);

	/* Set up Advertisement register 3 first */
	reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
	if (pdata->phy.advertising & ADVERTISED_10000baseR_FEC)
	if (advertising & ADVERTISED_10000baseR_FEC)
		reg |= 0xc000;
	else
		reg &= ~0xc000;
@@ -967,13 +980,13 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata)

	/* Set up Advertisement register 2 next */
	reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
	if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full)
	if (advertising & ADVERTISED_10000baseKR_Full)
		reg |= 0x80;
	else
		reg &= ~0x80;

	if ((pdata->phy.advertising & ADVERTISED_1000baseKX_Full) ||
	    (pdata->phy.advertising & ADVERTISED_2500baseX_Full))
	if ((advertising & ADVERTISED_1000baseKX_Full) ||
	    (advertising & ADVERTISED_2500baseX_Full))
		reg |= 0x20;
	else
		reg &= ~0x20;
@@ -982,12 +995,12 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata)

	/* Set up Advertisement register 1 last */
	reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
	if (pdata->phy.advertising & ADVERTISED_Pause)
	if (advertising & ADVERTISED_Pause)
		reg |= 0x400;
	else
		reg &= ~0x400;

	if (pdata->phy.advertising & ADVERTISED_Asym_Pause)
	if (advertising & ADVERTISED_Asym_Pause)
		reg |= 0x800;
	else
		reg &= ~0x800;
@@ -1006,6 +1019,7 @@ static void xgbe_an_init(struct xgbe_prv_data *pdata)
	pdata->an_mode = pdata->phy_if.phy_impl.an_mode(pdata);
	switch (pdata->an_mode) {
	case XGBE_AN_MODE_CL73:
	case XGBE_AN_MODE_CL73_REDRV:
		xgbe_an73_init(pdata);
		break;
	case XGBE_AN_MODE_CL37:
@@ -1149,10 +1163,15 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
	if (ret)
		return ret;

	if (pdata->phy.autoneg != AUTONEG_ENABLE)
		return xgbe_phy_config_fixed(pdata);
	if (pdata->phy.autoneg != AUTONEG_ENABLE) {
		ret = xgbe_phy_config_fixed(pdata);
		if (ret || !pdata->kr_redrv)
			return ret;

		netif_dbg(pdata, link, pdata->netdev, "AN redriver support\n");
	} else {
		netif_dbg(pdata, link, pdata->netdev, "AN PHY configuration\n");
	}

	/* Disable auto-negotiation interrupt */
	disable_irq(pdata->an_irq);
+7 −0
Original line number Diff line number Diff line
@@ -295,6 +295,11 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
	return mode;
}

static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata)
{
	return pdata->phy.advertising;
}

static int xgbe_phy_an_config(struct xgbe_prv_data *pdata)
{
	/* Nothing uniquely required for an configuration */
@@ -831,6 +836,8 @@ void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if)

	phy_impl->an_config		= xgbe_phy_an_config;

	phy_impl->an_advertising	= xgbe_phy_an_advertising;

	phy_impl->an_outcome		= xgbe_phy_an_outcome;

	phy_impl->kr_training_pre	= xgbe_phy_kr_training_pre;
+403 −24
Original line number Diff line number Diff line
@@ -277,6 +277,26 @@ enum xgbe_mdio_reset {
	XGBE_MDIO_RESET_MAX,
};

/* Re-driver related definitions */
enum xgbe_phy_redrv_if {
	XGBE_PHY_REDRV_IF_MDIO = 0,
	XGBE_PHY_REDRV_IF_I2C,
	XGBE_PHY_REDRV_IF_MAX,
};

enum xgbe_phy_redrv_model {
	XGBE_PHY_REDRV_MODEL_4223 = 0,
	XGBE_PHY_REDRV_MODEL_4227,
	XGBE_PHY_REDRV_MODEL_MAX,
};

enum xgbe_phy_redrv_mode {
	XGBE_PHY_REDRV_MODE_CX = 5,
	XGBE_PHY_REDRV_MODE_SR = 9,
};

#define XGBE_PHY_REDRV_MODE_REG	0x12b0

/* PHY related configuration information */
struct xgbe_phy_data {
	enum xgbe_port_mode port_mode;
@@ -327,6 +347,13 @@ struct xgbe_phy_data {
	enum xgbe_mdio_reset mdio_reset;
	unsigned int mdio_reset_addr;
	unsigned int mdio_reset_gpio;

	/* Re-driver support */
	unsigned int redrv;
	unsigned int redrv_if;
	unsigned int redrv_addr;
	unsigned int redrv_lane;
	unsigned int redrv_model;
};

/* I2C, MDIO and GPIO lines are muxed, so only one device at a time */
@@ -346,6 +373,68 @@ static int xgbe_phy_i2c_xfer(struct xgbe_prv_data *pdata,
	return pdata->i2c_if.i2c_xfer(pdata, i2c_op);
}

static int xgbe_phy_redrv_write(struct xgbe_prv_data *pdata, unsigned int reg,
				unsigned int val)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	struct xgbe_i2c_op i2c_op;
	__be16 *redrv_val;
	u8 redrv_data[5], csum;
	unsigned int i, retry;
	int ret;

	/* High byte of register contains read/write indicator */
	redrv_data[0] = ((reg >> 8) & 0xff) << 1;
	redrv_data[1] = reg & 0xff;
	redrv_val = (__be16 *)&redrv_data[2];
	*redrv_val = cpu_to_be16(val);

	/* Calculate 1 byte checksum */
	csum = 0;
	for (i = 0; i < 4; i++) {
		csum += redrv_data[i];
		if (redrv_data[i] > csum)
			csum++;
	}
	redrv_data[4] = ~csum;

	retry = 1;
again1:
	i2c_op.cmd = XGBE_I2C_CMD_WRITE;
	i2c_op.target = phy_data->redrv_addr;
	i2c_op.len = sizeof(redrv_data);
	i2c_op.buf = redrv_data;
	ret = xgbe_phy_i2c_xfer(pdata, &i2c_op);
	if (ret) {
		if ((ret == -EAGAIN) && retry--)
			goto again1;

		return ret;
	}

	retry = 1;
again2:
	i2c_op.cmd = XGBE_I2C_CMD_READ;
	i2c_op.target = phy_data->redrv_addr;
	i2c_op.len = 1;
	i2c_op.buf = redrv_data;
	ret = xgbe_phy_i2c_xfer(pdata, &i2c_op);
	if (ret) {
		if ((ret == -EAGAIN) && retry--)
			goto again2;

		return ret;
	}

	if (redrv_data[0] != 0xff) {
		netif_dbg(pdata, drv, pdata->netdev,
			  "Redriver write checksum error\n");
		ret = -EIO;
	}

	return ret;
}

static int xgbe_phy_i2c_write(struct xgbe_prv_data *pdata, unsigned int target,
			      void *val, unsigned int val_len)
{
@@ -1144,31 +1233,31 @@ static void xgbe_phy_sfp_detect(struct xgbe_prv_data *pdata)
	xgbe_phy_put_comm_ownership(pdata);
}

static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata)
static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	enum xgbe_mode mode;

	pdata->phy.lp_advertising |= ADVERTISED_Autoneg;
	pdata->phy.lp_advertising |= ADVERTISED_TP;

	if (pdata->phy.pause_autoneg && phy_data->phydev) {
		/* Flow control is obtained from the attached PHY */
	u16 lcl_adv = 0, rmt_adv = 0;
	u8 fc;

	pdata->phy.tx_pause = 0;
	pdata->phy.rx_pause = 0;

	if (!phy_data->phydev)
		return;

	if (phy_data->phydev->advertising & ADVERTISED_Pause)
		lcl_adv |= ADVERTISE_PAUSE_CAP;
	if (phy_data->phydev->advertising & ADVERTISED_Asym_Pause)
		lcl_adv |= ADVERTISE_PAUSE_ASYM;

		if (phy_data->phydev->pause)
	if (phy_data->phydev->pause) {
		pdata->phy.lp_advertising |= ADVERTISED_Pause;
		rmt_adv |= LPA_PAUSE_CAP;
		if (phy_data->phydev->asym_pause)
	}
	if (phy_data->phydev->asym_pause) {
		pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause;
		rmt_adv |= LPA_PAUSE_ASYM;
	}

	fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
	if (fc & FLOW_CTRL_TX)
@@ -1177,6 +1266,17 @@ static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata)
		pdata->phy.rx_pause = 1;
}

static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata)
{
	enum xgbe_mode mode;

	pdata->phy.lp_advertising |= ADVERTISED_Autoneg;
	pdata->phy.lp_advertising |= ADVERTISED_TP;

	/* Use external PHY to determine flow control */
	if (pdata->phy.pause_autoneg)
		xgbe_phy_phydev_flowctrl(pdata);

	switch (pdata->an_status & XGBE_SGMII_AN_LINK_SPEED) {
	case XGBE_SGMII_AN_LINK_SPEED_100:
		if (pdata->an_status & XGBE_SGMII_AN_LINK_DUPLEX) {
@@ -1249,6 +1349,83 @@ static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata)
	return mode;
}

static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	enum xgbe_mode mode;
	unsigned int ad_reg, lp_reg;

	pdata->phy.lp_advertising |= ADVERTISED_Autoneg;
	pdata->phy.lp_advertising |= ADVERTISED_Backplane;

	/* Use external PHY to determine flow control */
	if (pdata->phy.pause_autoneg)
		xgbe_phy_phydev_flowctrl(pdata);

	/* Compare Advertisement and Link Partner register 2 */
	ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
	lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1);
	if (lp_reg & 0x80)
		pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full;
	if (lp_reg & 0x20)
		pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full;

	ad_reg &= lp_reg;
	if (ad_reg & 0x80) {
		switch (phy_data->port_mode) {
		case XGBE_PORT_MODE_BACKPLANE:
			mode = XGBE_MODE_KR;
			break;
		default:
			mode = XGBE_MODE_SFI;
			break;
		}
	} else if (ad_reg & 0x20) {
		switch (phy_data->port_mode) {
		case XGBE_PORT_MODE_BACKPLANE:
			mode = XGBE_MODE_KX_1000;
			break;
		case XGBE_PORT_MODE_1000BASE_X:
			mode = XGBE_MODE_X;
			break;
		case XGBE_PORT_MODE_SFP:
			switch (phy_data->sfp_base) {
			case XGBE_SFP_BASE_1000_T:
				if (phy_data->phydev &&
				    (phy_data->phydev->speed == SPEED_100))
					mode = XGBE_MODE_SGMII_100;
				else
					mode = XGBE_MODE_SGMII_1000;
				break;
			case XGBE_SFP_BASE_1000_SX:
			case XGBE_SFP_BASE_1000_LX:
			case XGBE_SFP_BASE_1000_CX:
			default:
				mode = XGBE_MODE_X;
				break;
			}
			break;
		default:
			if (phy_data->phydev &&
			    (phy_data->phydev->speed == SPEED_100))
				mode = XGBE_MODE_SGMII_100;
			else
				mode = XGBE_MODE_SGMII_1000;
			break;
		}
	} else {
		mode = XGBE_MODE_UNKNOWN;
	}

	/* Compare Advertisement and Link Partner register 3 */
	ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
	lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2);
	if (lp_reg & 0xc000)
		pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC;

	return mode;
}

static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata)
{
	enum xgbe_mode mode;
@@ -1311,6 +1488,8 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
	switch (pdata->an_mode) {
	case XGBE_AN_MODE_CL73:
		return xgbe_phy_an73_outcome(pdata);
	case XGBE_AN_MODE_CL73_REDRV:
		return xgbe_phy_an73_redrv_outcome(pdata);
	case XGBE_AN_MODE_CL37:
		return xgbe_phy_an37_outcome(pdata);
	case XGBE_AN_MODE_CL37_SGMII:
@@ -1320,6 +1499,63 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
	}
}

static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	unsigned int advertising;

	/* Without a re-driver, just return current advertising */
	if (!phy_data->redrv)
		return pdata->phy.advertising;

	/* With the KR re-driver we need to advertise a single speed */
	advertising = pdata->phy.advertising;
	advertising &= ~ADVERTISED_1000baseKX_Full;
	advertising &= ~ADVERTISED_10000baseKR_Full;

	switch (phy_data->port_mode) {
	case XGBE_PORT_MODE_BACKPLANE:
		advertising |= ADVERTISED_10000baseKR_Full;
		break;
	case XGBE_PORT_MODE_BACKPLANE_2500:
		advertising |= ADVERTISED_1000baseKX_Full;
		break;
	case XGBE_PORT_MODE_1000BASE_T:
	case XGBE_PORT_MODE_1000BASE_X:
	case XGBE_PORT_MODE_NBASE_T:
		advertising |= ADVERTISED_1000baseKX_Full;
		break;
	case XGBE_PORT_MODE_10GBASE_T:
		if (phy_data->phydev &&
		    (phy_data->phydev->speed == SPEED_10000))
			advertising |= ADVERTISED_10000baseKR_Full;
		else
			advertising |= ADVERTISED_1000baseKX_Full;
		break;
	case XGBE_PORT_MODE_10GBASE_R:
		advertising |= ADVERTISED_10000baseKR_Full;
		break;
	case XGBE_PORT_MODE_SFP:
		switch (phy_data->sfp_base) {
		case XGBE_SFP_BASE_1000_T:
		case XGBE_SFP_BASE_1000_SX:
		case XGBE_SFP_BASE_1000_LX:
		case XGBE_SFP_BASE_1000_CX:
			advertising |= ADVERTISED_1000baseKX_Full;
			break;
		default:
			advertising |= ADVERTISED_10000baseKR_Full;
			break;
		}
		break;
	default:
		advertising |= ADVERTISED_10000baseKR_Full;
		break;
	}

	return advertising;
}

static int xgbe_phy_an_config(struct xgbe_prv_data *pdata)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;
@@ -1364,6 +1600,10 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;

	/* A KR re-driver will always require CL73 AN */
	if (phy_data->redrv)
		return XGBE_AN_MODE_CL73_REDRV;

	switch (phy_data->port_mode) {
	case XGBE_PORT_MODE_BACKPLANE:
		return XGBE_AN_MODE_CL73;
@@ -1386,6 +1626,61 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata)
	}
}

static int xgbe_phy_set_redrv_mode_mdio(struct xgbe_prv_data *pdata,
					enum xgbe_phy_redrv_mode mode)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	u16 redrv_reg, redrv_val;

	redrv_reg = XGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000);
	redrv_val = (u16)mode;

	return pdata->hw_if.write_ext_mii_regs(pdata, phy_data->redrv_addr,
					       redrv_reg, redrv_val);
}

static int xgbe_phy_set_redrv_mode_i2c(struct xgbe_prv_data *pdata,
				       enum xgbe_phy_redrv_mode mode)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	unsigned int redrv_reg;
	int ret;

	/* Calculate the register to write */
	redrv_reg = XGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000);

	ret = xgbe_phy_redrv_write(pdata, redrv_reg, mode);

	return ret;
}

static void xgbe_phy_set_redrv_mode(struct xgbe_prv_data *pdata)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	enum xgbe_phy_redrv_mode mode;
	int ret;

	if (!phy_data->redrv)
		return;

	mode = XGBE_PHY_REDRV_MODE_CX;
	if ((phy_data->port_mode == XGBE_PORT_MODE_SFP) &&
	    (phy_data->sfp_base != XGBE_SFP_BASE_1000_CX) &&
	    (phy_data->sfp_base != XGBE_SFP_BASE_10000_CR))
		mode = XGBE_PHY_REDRV_MODE_SR;

	ret = xgbe_phy_get_comm_ownership(pdata);
	if (ret)
		return;

	if (phy_data->redrv_if)
		xgbe_phy_set_redrv_mode_i2c(pdata, mode);
	else
		xgbe_phy_set_redrv_mode_mdio(pdata, mode);

	xgbe_phy_put_comm_ownership(pdata);
}

static void xgbe_phy_start_ratechange(struct xgbe_prv_data *pdata)
{
	if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS))
@@ -1457,6 +1752,8 @@ static void xgbe_phy_sfi_mode(struct xgbe_prv_data *pdata)
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	unsigned int s0;

	xgbe_phy_set_redrv_mode(pdata);

	xgbe_phy_start_ratechange(pdata);

	/* 10G/SFI */
@@ -1492,6 +1789,8 @@ static void xgbe_phy_x_mode(struct xgbe_prv_data *pdata)
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	unsigned int s0;

	xgbe_phy_set_redrv_mode(pdata);

	xgbe_phy_start_ratechange(pdata);

	/* 1G/X */
@@ -1516,6 +1815,8 @@ static void xgbe_phy_sgmii_1000_mode(struct xgbe_prv_data *pdata)
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	unsigned int s0;

	xgbe_phy_set_redrv_mode(pdata);

	xgbe_phy_start_ratechange(pdata);

	/* 1G/SGMII */
@@ -1540,6 +1841,8 @@ static void xgbe_phy_sgmii_100_mode(struct xgbe_prv_data *pdata)
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	unsigned int s0;

	xgbe_phy_set_redrv_mode(pdata);

	xgbe_phy_start_ratechange(pdata);

	/* 1G/SGMII */
@@ -1564,6 +1867,8 @@ static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata)
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	unsigned int s0;

	xgbe_phy_set_redrv_mode(pdata);

	xgbe_phy_start_ratechange(pdata);

	/* 10G/KR */
@@ -1588,6 +1893,8 @@ static void xgbe_phy_kx_2500_mode(struct xgbe_prv_data *pdata)
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	unsigned int s0;

	xgbe_phy_set_redrv_mode(pdata);

	xgbe_phy_start_ratechange(pdata);

	/* 2.5G/KX */
@@ -1612,6 +1919,8 @@ static void xgbe_phy_kx_1000_mode(struct xgbe_prv_data *pdata)
	struct xgbe_phy_data *phy_data = pdata->phy_data;
	unsigned int s0;

	xgbe_phy_set_redrv_mode(pdata);

	xgbe_phy_start_ratechange(pdata);

	/* 1G/KX */
@@ -2232,6 +2541,30 @@ static int xgbe_phy_mdio_reset(struct xgbe_prv_data *pdata)
	return ret;
}

static bool xgbe_phy_redrv_error(struct xgbe_phy_data *phy_data)
{
	if (!phy_data->redrv)
		return false;

	if (phy_data->redrv_if >= XGBE_PHY_REDRV_IF_MAX)
		return true;

	switch (phy_data->redrv_model) {
	case XGBE_PHY_REDRV_MODEL_4223:
		if (phy_data->redrv_lane > 3)
			return true;
		break;
	case XGBE_PHY_REDRV_MODEL_4227:
		if (phy_data->redrv_lane > 1)
			return true;
		break;
	default:
		return true;
	}

	return false;
}

static int xgbe_phy_mdio_reset_setup(struct xgbe_prv_data *pdata)
{
	struct xgbe_phy_data *phy_data = pdata->phy_data;
@@ -2481,6 +2814,20 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
		dev_dbg(pdata->dev, "mdio addr=%u\n", phy_data->mdio_addr);
	}

	reg = XP_IOREAD(pdata, XP_PROP_4);
	phy_data->redrv = XP_GET_BITS(reg, XP_PROP_4, REDRV_PRESENT);
	phy_data->redrv_if = XP_GET_BITS(reg, XP_PROP_4, REDRV_IF);
	phy_data->redrv_addr = XP_GET_BITS(reg, XP_PROP_4, REDRV_ADDR);
	phy_data->redrv_lane = XP_GET_BITS(reg, XP_PROP_4, REDRV_LANE);
	phy_data->redrv_model = XP_GET_BITS(reg, XP_PROP_4, REDRV_MODEL);
	if (phy_data->redrv && netif_msg_probe(pdata)) {
		dev_dbg(pdata->dev, "redrv present\n");
		dev_dbg(pdata->dev, "redrv i/f=%u\n", phy_data->redrv_if);
		dev_dbg(pdata->dev, "redrv addr=%#x\n", phy_data->redrv_addr);
		dev_dbg(pdata->dev, "redrv lane=%u\n", phy_data->redrv_lane);
		dev_dbg(pdata->dev, "redrv model=%u\n", phy_data->redrv_model);
	}

	/* Validate the connection requested */
	if (xgbe_phy_conn_type_mismatch(pdata)) {
		dev_err(pdata->dev, "phy mode/connection mismatch (%#x/%#x)\n",
@@ -2499,6 +2846,13 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
	if (ret)
		return ret;

	/* Validate the re-driver information */
	if (xgbe_phy_redrv_error(phy_data)) {
		dev_err(pdata->dev, "phy re-driver settings error\n");
		return -EINVAL;
	}
	pdata->kr_redrv = phy_data->redrv;

	/* Indicate current mode is unknown */
	phy_data->cur_mode = XGBE_MODE_UNKNOWN;

@@ -2651,6 +3005,29 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
		dev_dbg(pdata->dev, "phy supported=%#x\n",
			pdata->phy.supported);

	if ((phy_data->conn_type & XGBE_CONN_TYPE_MDIO) &&
	    (phy_data->phydev_mode != XGBE_MDIO_MODE_NONE)) {
		ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->mdio_addr,
						    phy_data->phydev_mode);
		if (ret) {
			dev_err(pdata->dev,
				"mdio port/clause not compatible (%d/%u)\n",
				phy_data->mdio_addr, phy_data->phydev_mode);
			return -EINVAL;
		}
	}

	if (phy_data->redrv && !phy_data->redrv_if) {
		ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->redrv_addr,
						    XGBE_MDIO_MODE_CL22);
		if (ret) {
			dev_err(pdata->dev,
				"redriver mdio port not compatible (%u)\n",
				phy_data->redrv_addr);
			return -EINVAL;
		}
	}

	/* Register for driving external PHYs */
	mii = devm_mdiobus_alloc(pdata->dev);
	if (!mii) {
@@ -2700,5 +3077,7 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if)

	phy_impl->an_config		= xgbe_phy_an_config;

	phy_impl->an_advertising	= xgbe_phy_an_advertising;

	phy_impl->an_outcome		= xgbe_phy_an_outcome;
}
+6 −0
Original line number Diff line number Diff line
@@ -508,6 +508,7 @@ enum xgbe_xpcs_access {

enum xgbe_an_mode {
	XGBE_AN_MODE_CL73 = 0,
	XGBE_AN_MODE_CL73_REDRV,
	XGBE_AN_MODE_CL37,
	XGBE_AN_MODE_CL37_SGMII,
	XGBE_AN_MODE_NONE,
@@ -807,6 +808,9 @@ struct xgbe_phy_impl_if {
	/* Configure auto-negotiation settings */
	int (*an_config)(struct xgbe_prv_data *);

	/* Set/override auto-negotiation advertisement settings */
	unsigned int (*an_advertising)(struct xgbe_prv_data *);

	/* Process results of auto-negotiation */
	enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *);

@@ -1124,6 +1128,8 @@ struct xgbe_prv_data {
	unsigned long link_check;
	struct completion mdio_complete;

	unsigned int kr_redrv;

	char an_name[IFNAMSIZ + 32];
	struct workqueue_struct *an_workqueue;