[dpdk-dev] [PATCH v5 17/18] net/axgbe: add workaround for axgbe ethernet training bug

Ravi Kumar Ravi1.kumar at amd.com
Fri Apr 6 14:36:50 CEST 2018


Signed-off-by: Ravi Kumar <Ravi1.kumar at amd.com>
---
 drivers/net/axgbe/axgbe_common.h   |   8 +++
 drivers/net/axgbe/axgbe_ethdev.c   |   2 +
 drivers/net/axgbe/axgbe_ethdev.h   |   6 ++
 drivers/net/axgbe/axgbe_mdio.c     |  13 ++++-
 drivers/net/axgbe/axgbe_phy_impl.c | 117 +++++++++++++++++++++++++++++++++++++
 5 files changed, 144 insertions(+), 2 deletions(-)

diff --git a/drivers/net/axgbe/axgbe_common.h b/drivers/net/axgbe/axgbe_common.h
index 64c7a7f..97a80f5 100644
--- a/drivers/net/axgbe/axgbe_common.h
+++ b/drivers/net/axgbe/axgbe_common.h
@@ -1247,6 +1247,10 @@
 #define MDIO_VEND2_AN_STAT		0x8002
 #endif
 
+#ifndef MDIO_VEND2_PMA_CDR_CONTROL
+#define MDIO_VEND2_PMA_CDR_CONTROL	0x8056
+#endif
+
 #ifndef MDIO_CTRL1_SPEED1G
 #define MDIO_CTRL1_SPEED1G		(MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
 #endif
@@ -1294,6 +1298,10 @@
 #define AXGBE_AN_CL37_PCS_MODE_SGMII	0x04
 #define AXGBE_AN_CL37_TX_CONFIG_MASK	0x08
 
+#define AXGBE_PMA_CDR_TRACK_EN_MASK	0x01
+#define AXGBE_PMA_CDR_TRACK_EN_OFF	0x00
+#define AXGBE_PMA_CDR_TRACK_EN_ON	0x01
+
 /*generic*/
 #define __iomem
 
diff --git a/drivers/net/axgbe/axgbe_ethdev.c b/drivers/net/axgbe/axgbe_ethdev.c
index 61a600c..096154c 100644
--- a/drivers/net/axgbe/axgbe_ethdev.c
+++ b/drivers/net/axgbe/axgbe_ethdev.c
@@ -50,6 +50,7 @@ static struct axgbe_version_data axgbe_v2a = {
 	.tx_tstamp_workaround		= 1,
 	.ecc_support			= 1,
 	.i2c_support			= 1,
+	.an_cdr_workaround		= 1,
 };
 
 static struct axgbe_version_data axgbe_v2b = {
@@ -61,6 +62,7 @@ static struct axgbe_version_data axgbe_v2b = {
 	.tx_tstamp_workaround		= 1,
 	.ecc_support			= 1,
 	.i2c_support			= 1,
+	.an_cdr_workaround		= 1,
 };
 
 static const struct rte_eth_desc_lim rx_desc_lim = {
diff --git a/drivers/net/axgbe/axgbe_ethdev.h b/drivers/net/axgbe/axgbe_ethdev.h
index 7bd2900..b1cd298 100644
--- a/drivers/net/axgbe/axgbe_ethdev.h
+++ b/drivers/net/axgbe/axgbe_ethdev.h
@@ -337,6 +337,10 @@ struct axgbe_phy_impl_if {
 	/* Process results of auto-negotiation */
 	enum axgbe_mode (*an_outcome)(struct axgbe_port *);
 
+	/* Pre/Post auto-negotiation support */
+	void (*an_pre)(struct axgbe_port *port);
+	void (*an_post)(struct axgbe_port *port);
+
 	/* Pre/Post KR training enablement support */
 	void (*kr_training_pre)(struct axgbe_port *);
 	void (*kr_training_post)(struct axgbe_port *);
@@ -431,6 +435,7 @@ struct axgbe_version_data {
 	unsigned int tx_tstamp_workaround;
 	unsigned int ecc_support;
 	unsigned int i2c_support;
+	unsigned int an_cdr_workaround;
 };
 
 /*
@@ -450,6 +455,7 @@ struct axgbe_port {
 	void *xprop_regs;	/* AXGBE property registers */
 	void *xi2c_regs;	/* AXGBE I2C CSRs */
 
+	bool cdr_track_early;
 	/* XPCS indirect addressing lock */
 	unsigned int xpcs_window_def_reg;
 	unsigned int xpcs_window_sel_reg;
diff --git a/drivers/net/axgbe/axgbe_mdio.c b/drivers/net/axgbe/axgbe_mdio.c
index 2296de7..2721e5c 100644
--- a/drivers/net/axgbe/axgbe_mdio.c
+++ b/drivers/net/axgbe/axgbe_mdio.c
@@ -287,10 +287,14 @@ static void axgbe_an73_disable(struct axgbe_port *pdata)
 {
 	axgbe_an73_set(pdata, false, false);
 	axgbe_an73_disable_interrupts(pdata);
+	pdata->an_start = 0;
 }
 
 static void axgbe_an_restart(struct axgbe_port *pdata)
 {
+	if (pdata->phy_if.phy_impl.an_pre)
+		pdata->phy_if.phy_impl.an_pre(pdata);
+
 	switch (pdata->an_mode) {
 	case AXGBE_AN_MODE_CL73:
 	case AXGBE_AN_MODE_CL73_REDRV:
@@ -307,6 +311,9 @@ static void axgbe_an_restart(struct axgbe_port *pdata)
 
 static void axgbe_an_disable(struct axgbe_port *pdata)
 {
+	if (pdata->phy_if.phy_impl.an_post)
+		pdata->phy_if.phy_impl.an_post(pdata);
+
 	switch (pdata->an_mode) {
 	case AXGBE_AN_MODE_CL73:
 	case AXGBE_AN_MODE_CL73_REDRV:
@@ -482,9 +489,9 @@ static enum axgbe_an axgbe_an73_incompat_link(struct axgbe_port *pdata)
 			return AXGBE_AN_NO_LINK;
 	}
 
-	axgbe_an73_disable(pdata);
+	axgbe_an_disable(pdata);
 	axgbe_switch_mode(pdata);
-	axgbe_an73_restart(pdata);
+	axgbe_an_restart(pdata);
 
 	return AXGBE_AN_INCOMPAT_LINK;
 }
@@ -553,6 +560,8 @@ static void axgbe_an73_state_machine(struct axgbe_port *pdata)
 		pdata->kr_state = AXGBE_RX_BPA;
 		pdata->kx_state = AXGBE_RX_BPA;
 		pdata->an_start = 0;
+		if (pdata->phy_if.phy_impl.an_post)
+			pdata->phy_if.phy_impl.an_post(pdata);
 	}
 
 	if (cur_state != pdata->an_state)
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 19bd4be..dfa908d 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -31,6 +31,11 @@
 /* Rate-change complete wait/retry count */
 #define AXGBE_RATECHANGE_COUNT		500
 
+/* CDR delay values for KR support (in usec) */
+#define AXGBE_CDR_DELAY_INIT		10000
+#define AXGBE_CDR_DELAY_INC		10000
+#define AXGBE_CDR_DELAY_MAX		100000
+
 enum axgbe_port_mode {
 	AXGBE_PORT_MODE_RSVD = 0,
 	AXGBE_PORT_MODE_BACKPLANE,
@@ -237,6 +242,10 @@ struct axgbe_phy_data {
 	unsigned int redrv_addr;
 	unsigned int redrv_lane;
 	unsigned int redrv_model;
+
+	/* KR AN support */
+	unsigned int phy_cdr_notrack;
+	unsigned int phy_cdr_delay;
 };
 
 static enum axgbe_an_mode axgbe_phy_an_mode(struct axgbe_port *pdata);
@@ -1766,6 +1775,100 @@ static bool axgbe_phy_port_enabled(struct axgbe_port *pdata)
 	return true;
 }
 
+static void axgbe_phy_cdr_track(struct axgbe_port *pdata)
+{
+	struct axgbe_phy_data *phy_data = pdata->phy_data;
+
+	if (!pdata->vdata->an_cdr_workaround)
+		return;
+
+	if (!phy_data->phy_cdr_notrack)
+		return;
+
+	rte_delay_us(phy_data->phy_cdr_delay + 400);
+
+	XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL,
+			 AXGBE_PMA_CDR_TRACK_EN_MASK,
+			 AXGBE_PMA_CDR_TRACK_EN_ON);
+
+	phy_data->phy_cdr_notrack = 0;
+}
+
+static void axgbe_phy_cdr_notrack(struct axgbe_port *pdata)
+{
+	struct axgbe_phy_data *phy_data = pdata->phy_data;
+
+	if (!pdata->vdata->an_cdr_workaround)
+		return;
+
+	if (phy_data->phy_cdr_notrack)
+		return;
+
+	XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL,
+			 AXGBE_PMA_CDR_TRACK_EN_MASK,
+			 AXGBE_PMA_CDR_TRACK_EN_OFF);
+
+	axgbe_phy_rrc(pdata);
+
+	phy_data->phy_cdr_notrack = 1;
+}
+
+static void axgbe_phy_kr_training_post(struct axgbe_port *pdata)
+{
+	if (!pdata->cdr_track_early)
+		axgbe_phy_cdr_track(pdata);
+}
+
+static void axgbe_phy_kr_training_pre(struct axgbe_port *pdata)
+{
+	if (pdata->cdr_track_early)
+		axgbe_phy_cdr_track(pdata);
+}
+
+static void axgbe_phy_an_post(struct axgbe_port *pdata)
+{
+	struct axgbe_phy_data *phy_data = pdata->phy_data;
+
+	switch (pdata->an_mode) {
+	case AXGBE_AN_MODE_CL73:
+	case AXGBE_AN_MODE_CL73_REDRV:
+		if (phy_data->cur_mode != AXGBE_MODE_KR)
+			break;
+
+		axgbe_phy_cdr_track(pdata);
+
+		switch (pdata->an_result) {
+		case AXGBE_AN_READY:
+		case AXGBE_AN_COMPLETE:
+			break;
+		default:
+			if (phy_data->phy_cdr_delay < AXGBE_CDR_DELAY_MAX)
+				phy_data->phy_cdr_delay += AXGBE_CDR_DELAY_INC;
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void axgbe_phy_an_pre(struct axgbe_port *pdata)
+{
+	struct axgbe_phy_data *phy_data = pdata->phy_data;
+
+	switch (pdata->an_mode) {
+	case AXGBE_AN_MODE_CL73:
+	case AXGBE_AN_MODE_CL73_REDRV:
+		if (phy_data->cur_mode != AXGBE_MODE_KR)
+			break;
+
+		axgbe_phy_cdr_notrack(pdata);
+		break;
+	default:
+		break;
+	}
+}
+
 static void axgbe_phy_stop(struct axgbe_port *pdata)
 {
 	struct axgbe_phy_data *phy_data = pdata->phy_data;
@@ -1774,6 +1877,9 @@ static void axgbe_phy_stop(struct axgbe_port *pdata)
 	axgbe_phy_sfp_reset(phy_data);
 	axgbe_phy_sfp_mod_absent(pdata);
 
+	/* Reset CDR support */
+	axgbe_phy_cdr_track(pdata);
+
 	/* Power off the PHY */
 	axgbe_phy_power_off(pdata);
 
@@ -1794,6 +1900,9 @@ static int axgbe_phy_start(struct axgbe_port *pdata)
 	/* Start in highest supported mode */
 	axgbe_phy_set_mode(pdata, phy_data->start_mode);
 
+	/* Reset CDR support */
+	axgbe_phy_cdr_track(pdata);
+
 	/* After starting the I2C controller, we can check for an SFP */
 	switch (phy_data->port_mode) {
 	case AXGBE_PORT_MODE_SFP:
@@ -2051,6 +2160,8 @@ static int axgbe_phy_init(struct axgbe_port *pdata)
 			return -EINVAL;
 		}
 	}
+
+	phy_data->phy_cdr_delay = AXGBE_CDR_DELAY_INIT;
 	return 0;
 }
 void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if)
@@ -2071,4 +2182,10 @@ void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if)
 	phy_impl->an_config		= axgbe_phy_an_config;
 	phy_impl->an_advertising	= axgbe_phy_an_advertising;
 	phy_impl->an_outcome		= axgbe_phy_an_outcome;
+
+	phy_impl->an_pre		= axgbe_phy_an_pre;
+	phy_impl->an_post		= axgbe_phy_an_post;
+
+	phy_impl->kr_training_pre	= axgbe_phy_kr_training_pre;
+	phy_impl->kr_training_post	= axgbe_phy_kr_training_post;
 }
-- 
2.7.4



More information about the dev mailing list