From 00072056a9b8ee8f9ac1ce0e3e9b0570a23a5a15 Mon Sep 17 00:00:00 2001 From: Ravi Kumar Date: Fri, 6 Apr 2018 08:36:50 -0400 Subject: [PATCH] net/axgbe: add workaround for ethernet training Signed-off-by: Ravi Kumar --- 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 64c7a7fa18..97a80f595c 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 61206a619c..a9a9fb5708 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 7bd290094b..b1cd2980b2 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 2296de7bf7..2721e5cc99 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 @@ again: 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 19bd4bec65..dfa908dd8e 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.20.1