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

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

amd-xgbe: Add support for clause 37 auto-negotiation



Add support to be able to use clause 37 auto-negotiation.

Signed-off-by: default avatarTom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a64def41
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -1027,6 +1027,10 @@
#define MDIO_PMA_10GBR_FECCTRL		0x00ab
#endif

#ifndef MDIO_PCS_DIG_CTRL
#define MDIO_PCS_DIG_CTRL		0x8000
#endif

#ifndef MDIO_AN_XNP
#define MDIO_AN_XNP			0x0016
#endif
@@ -1047,10 +1051,34 @@
#define MDIO_AN_INT			0x8002
#endif

#ifndef MDIO_VEND2_AN_ADVERTISE
#define MDIO_VEND2_AN_ADVERTISE		0x0004
#endif

#ifndef MDIO_VEND2_AN_LP_ABILITY
#define MDIO_VEND2_AN_LP_ABILITY	0x0005
#endif

#ifndef MDIO_VEND2_AN_CTRL
#define MDIO_VEND2_AN_CTRL		0x8001
#endif

#ifndef MDIO_VEND2_AN_STAT
#define MDIO_VEND2_AN_STAT		0x8002
#endif

#ifndef MDIO_CTRL1_SPEED1G
#define MDIO_CTRL1_SPEED1G		(MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
#endif

#ifndef MDIO_VEND2_CTRL1_AN_ENABLE
#define MDIO_VEND2_CTRL1_AN_ENABLE	BIT(12)
#endif

#ifndef MDIO_VEND2_CTRL1_AN_RESTART
#define MDIO_VEND2_CTRL1_AN_RESTART	BIT(9)
#endif

/* MDIO mask values */
#define XGBE_AN_CL73_INT_CMPLT		BIT(0)
#define XGBE_AN_CL73_INC_LINK		BIT(1)
@@ -1065,6 +1093,19 @@
#define XGBE_KR_TRAINING_START		BIT(0)
#define XGBE_KR_TRAINING_ENABLE		BIT(1)

#define XGBE_PCS_CL37_BP		BIT(12)

#define XGBE_AN_CL37_INT_CMPLT		BIT(0)
#define XGBE_AN_CL37_INT_MASK		0x01

#define XGBE_AN_CL37_HD_MASK		0x40
#define XGBE_AN_CL37_FD_MASK		0x20

#define XGBE_AN_CL37_PCS_MODE_MASK	0x06
#define XGBE_AN_CL37_PCS_MODE_BASEX	0x00
#define XGBE_AN_CL37_PCS_MODE_SGMII	0x04
#define XGBE_AN_CL37_TX_CONFIG_MASK	0x08

/* Bit setting and getting macros
 *  The get macro will extract the current bit field value from within
 *  the variable
+235 −7
Original line number Diff line number Diff line
@@ -125,6 +125,41 @@
#include "xgbe.h"
#include "xgbe-common.h"

static void xgbe_an37_clear_interrupts(struct xgbe_prv_data *pdata)
{
	int reg;

	reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT);
	reg &= ~XGBE_AN_CL37_INT_MASK;
	XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT, reg);
}

static void xgbe_an37_disable_interrupts(struct xgbe_prv_data *pdata)
{
	int reg;

	reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL);
	reg &= ~XGBE_AN_CL37_INT_MASK;
	XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg);

	reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL);
	reg &= ~XGBE_PCS_CL37_BP;
	XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL, reg);
}

static void xgbe_an37_enable_interrupts(struct xgbe_prv_data *pdata)
{
	int reg;

	reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL);
	reg |= XGBE_PCS_CL37_BP;
	XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_PCS_DIG_CTRL, reg);

	reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL);
	reg |= XGBE_AN_CL37_INT_MASK;
	XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg);
}

static void xgbe_an73_clear_interrupts(struct xgbe_prv_data *pdata)
{
	XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0);
@@ -146,11 +181,21 @@ static void xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata)
	case XGBE_AN_MODE_CL73:
		xgbe_an73_enable_interrupts(pdata);
		break;
	case XGBE_AN_MODE_CL37:
	case XGBE_AN_MODE_CL37_SGMII:
		xgbe_an37_enable_interrupts(pdata);
		break;
	default:
		break;
	}
}

static void xgbe_an_clear_interrupts_all(struct xgbe_prv_data *pdata)
{
	xgbe_an73_clear_interrupts(pdata);
	xgbe_an37_clear_interrupts(pdata);
}

static void xgbe_an73_enable_kr_training(struct xgbe_prv_data *pdata)
{
	unsigned int reg;
@@ -258,6 +303,39 @@ static bool xgbe_use_mode(struct xgbe_prv_data *pdata,
	return pdata->phy_if.phy_impl.use_mode(pdata, mode);
}

static void xgbe_an37_set(struct xgbe_prv_data *pdata, bool enable,
			  bool restart)
{
	unsigned int reg;

	reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_CTRL1);
	reg &= ~MDIO_VEND2_CTRL1_AN_ENABLE;

	if (enable)
		reg |= MDIO_VEND2_CTRL1_AN_ENABLE;

	if (restart)
		reg |= MDIO_VEND2_CTRL1_AN_RESTART;

	XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_CTRL1, reg);
}

static void xgbe_an37_restart(struct xgbe_prv_data *pdata)
{
	xgbe_an37_enable_interrupts(pdata);
	xgbe_an37_set(pdata, true, true);

	netif_dbg(pdata, link, pdata->netdev, "CL37 AN enabled/restarted\n");
}

static void xgbe_an37_disable(struct xgbe_prv_data *pdata)
{
	xgbe_an37_set(pdata, false, false);
	xgbe_an37_disable_interrupts(pdata);

	netif_dbg(pdata, link, pdata->netdev, "CL37 AN disabled\n");
}

static void xgbe_an73_set(struct xgbe_prv_data *pdata, bool enable,
			  bool restart)
{
@@ -297,6 +375,10 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata)
	case XGBE_AN_MODE_CL73:
		xgbe_an73_restart(pdata);
		break;
	case XGBE_AN_MODE_CL37:
	case XGBE_AN_MODE_CL37_SGMII:
		xgbe_an37_restart(pdata);
		break;
	default:
		break;
	}
@@ -308,11 +390,21 @@ static void xgbe_an_disable(struct xgbe_prv_data *pdata)
	case XGBE_AN_MODE_CL73:
		xgbe_an73_disable(pdata);
		break;
	case XGBE_AN_MODE_CL37:
	case XGBE_AN_MODE_CL37_SGMII:
		xgbe_an37_disable(pdata);
		break;
	default:
		break;
	}
}

static void xgbe_an_disable_all(struct xgbe_prv_data *pdata)
{
	xgbe_an73_disable(pdata);
	xgbe_an37_disable(pdata);
}

static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata,
					  enum xgbe_rx *state)
{
@@ -484,6 +576,30 @@ static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata)
	return XGBE_AN_INCOMPAT_LINK;
}

static void xgbe_an37_isr(struct xgbe_prv_data *pdata)
{
	unsigned int reg;

	/* Disable AN interrupts */
	xgbe_an37_disable_interrupts(pdata);

	/* Save the interrupt(s) that fired */
	reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT);
	pdata->an_int = reg & XGBE_AN_CL37_INT_MASK;
	pdata->an_status = reg & ~XGBE_AN_CL37_INT_MASK;

	if (pdata->an_int) {
		/* Clear the interrupt(s) that fired and process them */
		reg &= ~XGBE_AN_CL37_INT_MASK;
		XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_STAT, reg);

		queue_work(pdata->an_workqueue, &pdata->an_irq_work);
	} else {
		/* Enable AN interrupts */
		xgbe_an37_enable_interrupts(pdata);
	}
}

static void xgbe_an73_isr(struct xgbe_prv_data *pdata)
{
	/* Disable AN interrupts */
@@ -513,6 +629,10 @@ static irqreturn_t xgbe_an_isr(int irq, void *data)
	case XGBE_AN_MODE_CL73:
		xgbe_an73_isr(pdata);
		break;
	case XGBE_AN_MODE_CL37:
	case XGBE_AN_MODE_CL37_SGMII:
		xgbe_an37_isr(pdata);
		break;
	default:
		break;
	}
@@ -553,6 +673,64 @@ static const char *xgbe_state_as_string(enum xgbe_an state)
	}
}

static void xgbe_an37_state_machine(struct xgbe_prv_data *pdata)
{
	enum xgbe_an cur_state = pdata->an_state;

	if (!pdata->an_int)
		return;

	if (pdata->an_int & XGBE_AN_CL37_INT_CMPLT) {
		pdata->an_state = XGBE_AN_COMPLETE;
		pdata->an_int &= ~XGBE_AN_CL37_INT_CMPLT;

		/* If SGMII is enabled, check the link status */
		if ((pdata->an_mode == XGBE_AN_MODE_CL37_SGMII) &&
		    !(pdata->an_status & XGBE_SGMII_AN_LINK_STATUS))
			pdata->an_state = XGBE_AN_NO_LINK;
	}

	netif_dbg(pdata, link, pdata->netdev, "CL37 AN %s\n",
		  xgbe_state_as_string(pdata->an_state));

	cur_state = pdata->an_state;

	switch (pdata->an_state) {
	case XGBE_AN_READY:
		break;

	case XGBE_AN_COMPLETE:
		netif_dbg(pdata, link, pdata->netdev,
			  "Auto negotiation successful\n");
		break;

	case XGBE_AN_NO_LINK:
		break;

	default:
		pdata->an_state = XGBE_AN_ERROR;
	}

	if (pdata->an_state == XGBE_AN_ERROR) {
		netdev_err(pdata->netdev,
			   "error during auto-negotiation, state=%u\n",
			   cur_state);

		pdata->an_int = 0;
		xgbe_an37_clear_interrupts(pdata);
	}

	if (pdata->an_state >= XGBE_AN_COMPLETE) {
		pdata->an_result = pdata->an_state;
		pdata->an_state = XGBE_AN_READY;

		netif_dbg(pdata, link, pdata->netdev, "CL37 AN result: %s\n",
			  xgbe_state_as_string(pdata->an_result));
	}

	xgbe_an37_enable_interrupts(pdata);
}

static void xgbe_an73_state_machine(struct xgbe_prv_data *pdata)
{
	enum xgbe_an cur_state = pdata->an_state;
@@ -574,8 +752,6 @@ static void xgbe_an73_state_machine(struct xgbe_prv_data *pdata)
		pdata->an_state = XGBE_AN_ERROR;
	}

	pdata->an_result = pdata->an_state;

again:
	netif_dbg(pdata, link, pdata->netdev, "CL73 AN %s\n",
		  xgbe_state_as_string(pdata->an_state));
@@ -656,6 +832,10 @@ static void xgbe_an_state_machine(struct work_struct *work)
	case XGBE_AN_MODE_CL73:
		xgbe_an73_state_machine(pdata);
		break;
	case XGBE_AN_MODE_CL37:
	case XGBE_AN_MODE_CL37_SGMII:
		xgbe_an37_state_machine(pdata);
		break;
	default:
		break;
	}
@@ -663,6 +843,50 @@ static void xgbe_an_state_machine(struct work_struct *work)
	mutex_unlock(&pdata->an_mutex);
}

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

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

	if (pdata->phy.advertising & ADVERTISED_Asym_Pause)
		reg |= 0x80;
	else
		reg &= ~0x80;

	/* Full duplex, but not half */
	reg |= XGBE_AN_CL37_FD_MASK;
	reg &= ~XGBE_AN_CL37_HD_MASK;

	XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE, reg);

	/* Set up the Control register */
	reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL);
	reg &= XGBE_AN_CL37_TX_CONFIG_MASK;
	reg &= XGBE_AN_CL37_PCS_MODE_MASK;

	switch (pdata->an_mode) {
	case XGBE_AN_MODE_CL37:
		reg |= XGBE_AN_CL37_PCS_MODE_BASEX;
		break;
	case XGBE_AN_MODE_CL37_SGMII:
		reg |= XGBE_AN_CL37_PCS_MODE_SGMII;
		break;
	default:
		break;
	}

	XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg);

	netif_dbg(pdata, link, pdata->netdev, "CL37 AN (%s) initialized\n",
		  (pdata->an_mode == XGBE_AN_MODE_CL37) ? "BaseX" : "SGMII");
}

static void xgbe_an73_init(struct xgbe_prv_data *pdata)
{
	unsigned int reg;
@@ -719,6 +943,10 @@ static void xgbe_an_init(struct xgbe_prv_data *pdata)
	case XGBE_AN_MODE_CL73:
		xgbe_an73_init(pdata);
		break;
	case XGBE_AN_MODE_CL37:
	case XGBE_AN_MODE_CL37_SGMII:
		xgbe_an37_init(pdata);
		break;
	default:
		break;
	}
@@ -865,10 +1093,10 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
	}

	/* Disable and stop any in progress auto-negotiation */
	xgbe_an_disable(pdata);
	xgbe_an_disable_all(pdata);

	/* Clear any auto-negotitation interrupts */
	xgbe_an73_clear_interrupts(pdata);
	xgbe_an_clear_interrupts_all(pdata);

	pdata->an_result = XGBE_AN_READY;
	pdata->an_state = XGBE_AN_READY;
@@ -1007,7 +1235,7 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
	pdata->phy_started = 0;

	/* Disable auto-negotiation */
	xgbe_an_disable(pdata);
	xgbe_an_disable_all(pdata);

	devm_free_irq(pdata->dev, pdata->an_irq, pdata);

@@ -1078,10 +1306,10 @@ static int xgbe_phy_reset(struct xgbe_prv_data *pdata)
		return ret;

	/* Disable auto-negotiation for now */
	xgbe_an_disable(pdata);
	xgbe_an_disable_all(pdata);

	/* Clear auto-negotiation interrupts */
	xgbe_an73_clear_interrupts(pdata);
	xgbe_an_clear_interrupts_all(pdata);

	return 0;
}
+10 −1
Original line number Diff line number Diff line
@@ -261,7 +261,13 @@

/* Auto-negotiation */
#define XGBE_AN_MS_TIMEOUT		500
#define XGBE_LINK_TIMEOUT		10
#define XGBE_LINK_TIMEOUT		5

#define XGBE_SGMII_AN_LINK_STATUS	BIT(1)
#define XGBE_SGMII_AN_LINK_SPEED	(BIT(2) | BIT(3))
#define XGBE_SGMII_AN_LINK_SPEED_100	0x04
#define XGBE_SGMII_AN_LINK_SPEED_1000	0x08
#define XGBE_SGMII_AN_LINK_DUPLEX	BIT(4)

struct xgbe_prv_data;

@@ -467,6 +473,8 @@ enum xgbe_speed {

enum xgbe_an_mode {
	XGBE_AN_MODE_CL73 = 0,
	XGBE_AN_MODE_CL37,
	XGBE_AN_MODE_CL37_SGMII,
	XGBE_AN_MODE_NONE,
};

@@ -961,6 +969,7 @@ struct xgbe_prv_data {

	/* Auto-negotiation state machine support */
	unsigned int an_int;
	unsigned int an_status;
	struct mutex an_mutex;
	enum xgbe_an an_result;
	enum xgbe_an an_state;