Commit 96f4d430 authored by Tom Lendacky's avatar Tom Lendacky Committed by David S. Miller

amd-xgbe: Improve KR auto-negotiation and training

Update xgbe-phy-v2.c to make use of the auto-negotiation (AN) phy hooks
to improve the ability to successfully complete Clause 73 AN when running
at 10gbps.  Hardware can sometimes have issues with CDR lock when the
AN DME page exchange is being performed.

The AN and KR training hooks are used as follows:
- The pre AN hook is used to disable CDR tracking in the PHY so that the
  DME page exchange can be successfully and consistently completed.
- The post KR training hook is used to re-enable the CDR tracking so that
  KR training can successfully complete.
- The post AN hook is used to check for an unsuccessful AN which will
  increase a CDR tracking enablement delay (up to a maximum value).

Add two debugfs entries to allow control over use of the CDR tracking
workaround.  The debugfs entries allow the CDR tracking workaround to
be disabled and determine whether to re-enable CDR tracking before or
after link training has been initiated.

Also, with these changes the receiver reset cycle that is performed during
the link status check can be performed less often.
Signed-off-by: default avatarTom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4d945663
...@@ -1321,6 +1321,10 @@ ...@@ -1321,6 +1321,10 @@
#define MDIO_VEND2_AN_STAT 0x8002 #define MDIO_VEND2_AN_STAT 0x8002
#endif #endif
#ifndef MDIO_VEND2_PMA_CDR_CONTROL
#define MDIO_VEND2_PMA_CDR_CONTROL 0x8056
#endif
#ifndef MDIO_CTRL1_SPEED1G #ifndef MDIO_CTRL1_SPEED1G
#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100) #define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
#endif #endif
...@@ -1369,6 +1373,10 @@ ...@@ -1369,6 +1373,10 @@
#define XGBE_AN_CL37_TX_CONFIG_MASK 0x08 #define XGBE_AN_CL37_TX_CONFIG_MASK 0x08
#define XGBE_AN_CL37_MII_CTRL_8BIT 0x0100 #define XGBE_AN_CL37_MII_CTRL_8BIT 0x0100
#define XGBE_PMA_CDR_TRACK_EN_MASK 0x01
#define XGBE_PMA_CDR_TRACK_EN_OFF 0x00
#define XGBE_PMA_CDR_TRACK_EN_ON 0x01
/* Bit setting and getting macros /* Bit setting and getting macros
* The get macro will extract the current bit field value from within * The get macro will extract the current bit field value from within
* the variable * the variable
......
...@@ -519,6 +519,22 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata) ...@@ -519,6 +519,22 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
"debugfs_create_file failed\n"); "debugfs_create_file failed\n");
} }
if (pdata->vdata->an_cdr_workaround) {
pfile = debugfs_create_bool("an_cdr_workaround", 0600,
pdata->xgbe_debugfs,
&pdata->debugfs_an_cdr_workaround);
if (!pfile)
netdev_err(pdata->netdev,
"debugfs_create_bool failed\n");
pfile = debugfs_create_bool("an_cdr_track_early", 0600,
pdata->xgbe_debugfs,
&pdata->debugfs_an_cdr_track_early);
if (!pfile)
netdev_err(pdata->netdev,
"debugfs_create_bool failed\n");
}
kfree(buf); kfree(buf);
} }
......
...@@ -349,6 +349,7 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) ...@@ -349,6 +349,7 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata)
XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, UDP4TE, 1); XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, UDP4TE, 1);
/* Call MDIO/PHY initialization routine */ /* Call MDIO/PHY initialization routine */
pdata->debugfs_an_cdr_workaround = pdata->vdata->an_cdr_workaround;
ret = pdata->phy_if.phy_init(pdata); ret = pdata->phy_if.phy_init(pdata);
if (ret) if (ret)
return ret; return ret;
......
...@@ -432,6 +432,8 @@ static void xgbe_an73_disable(struct xgbe_prv_data *pdata) ...@@ -432,6 +432,8 @@ static void xgbe_an73_disable(struct xgbe_prv_data *pdata)
xgbe_an73_set(pdata, false, false); xgbe_an73_set(pdata, false, false);
xgbe_an73_disable_interrupts(pdata); xgbe_an73_disable_interrupts(pdata);
pdata->an_start = 0;
netif_dbg(pdata, link, pdata->netdev, "CL73 AN disabled\n"); netif_dbg(pdata, link, pdata->netdev, "CL73 AN disabled\n");
} }
...@@ -511,11 +513,11 @@ static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata, ...@@ -511,11 +513,11 @@ static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata,
XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL,
reg); reg);
if (pdata->phy_if.phy_impl.kr_training_post)
pdata->phy_if.phy_impl.kr_training_post(pdata);
netif_dbg(pdata, link, pdata->netdev, netif_dbg(pdata, link, pdata->netdev,
"KR training initiated\n"); "KR training initiated\n");
if (pdata->phy_if.phy_impl.kr_training_post)
pdata->phy_if.phy_impl.kr_training_post(pdata);
} }
return XGBE_AN_PAGE_RECEIVED; return XGBE_AN_PAGE_RECEIVED;
......
...@@ -456,6 +456,7 @@ static const struct xgbe_version_data xgbe_v2a = { ...@@ -456,6 +456,7 @@ static const struct xgbe_version_data xgbe_v2a = {
.irq_reissue_support = 1, .irq_reissue_support = 1,
.tx_desc_prefetch = 5, .tx_desc_prefetch = 5,
.rx_desc_prefetch = 5, .rx_desc_prefetch = 5,
.an_cdr_workaround = 1,
}; };
static const struct xgbe_version_data xgbe_v2b = { static const struct xgbe_version_data xgbe_v2b = {
...@@ -470,6 +471,7 @@ static const struct xgbe_version_data xgbe_v2b = { ...@@ -470,6 +471,7 @@ static const struct xgbe_version_data xgbe_v2b = {
.irq_reissue_support = 1, .irq_reissue_support = 1,
.tx_desc_prefetch = 5, .tx_desc_prefetch = 5,
.rx_desc_prefetch = 5, .rx_desc_prefetch = 5,
.an_cdr_workaround = 1,
}; };
static const struct pci_device_id xgbe_pci_table[] = { static const struct pci_device_id xgbe_pci_table[] = {
......
...@@ -147,6 +147,14 @@ ...@@ -147,6 +147,14 @@
/* Rate-change complete wait/retry count */ /* Rate-change complete wait/retry count */
#define XGBE_RATECHANGE_COUNT 500 #define XGBE_RATECHANGE_COUNT 500
/* CDR delay values for KR support (in usec) */
#define XGBE_CDR_DELAY_INIT 10000
#define XGBE_CDR_DELAY_INC 10000
#define XGBE_CDR_DELAY_MAX 100000
/* RRC frequency during link status check */
#define XGBE_RRC_FREQUENCY 10
enum xgbe_port_mode { enum xgbe_port_mode {
XGBE_PORT_MODE_RSVD = 0, XGBE_PORT_MODE_RSVD = 0,
XGBE_PORT_MODE_BACKPLANE, XGBE_PORT_MODE_BACKPLANE,
...@@ -355,6 +363,10 @@ struct xgbe_phy_data { ...@@ -355,6 +363,10 @@ struct xgbe_phy_data {
unsigned int redrv_addr; unsigned int redrv_addr;
unsigned int redrv_lane; unsigned int redrv_lane;
unsigned int redrv_model; unsigned int redrv_model;
/* KR AN support */
unsigned int phy_cdr_notrack;
unsigned int phy_cdr_delay;
}; };
/* I2C, MDIO and GPIO lines are muxed, so only one device at a time */ /* I2C, MDIO and GPIO lines are muxed, so only one device at a time */
...@@ -2361,7 +2373,7 @@ static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart) ...@@ -2361,7 +2373,7 @@ static int xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart)
return 1; return 1;
/* No link, attempt a receiver reset cycle */ /* No link, attempt a receiver reset cycle */
if (phy_data->rrc_count++) { if (phy_data->rrc_count++ > XGBE_RRC_FREQUENCY) {
phy_data->rrc_count = 0; phy_data->rrc_count = 0;
xgbe_phy_rrc(pdata); xgbe_phy_rrc(pdata);
} }
...@@ -2669,6 +2681,103 @@ static bool xgbe_phy_port_enabled(struct xgbe_prv_data *pdata) ...@@ -2669,6 +2681,103 @@ static bool xgbe_phy_port_enabled(struct xgbe_prv_data *pdata)
return true; return true;
} }
static void xgbe_phy_cdr_track(struct xgbe_prv_data *pdata)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
if (!pdata->debugfs_an_cdr_workaround)
return;
if (!phy_data->phy_cdr_notrack)
return;
usleep_range(phy_data->phy_cdr_delay,
phy_data->phy_cdr_delay + 500);
XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL,
XGBE_PMA_CDR_TRACK_EN_MASK,
XGBE_PMA_CDR_TRACK_EN_ON);
phy_data->phy_cdr_notrack = 0;
}
static void xgbe_phy_cdr_notrack(struct xgbe_prv_data *pdata)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
if (!pdata->debugfs_an_cdr_workaround)
return;
if (phy_data->phy_cdr_notrack)
return;
XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_VEND2_PMA_CDR_CONTROL,
XGBE_PMA_CDR_TRACK_EN_MASK,
XGBE_PMA_CDR_TRACK_EN_OFF);
xgbe_phy_rrc(pdata);
phy_data->phy_cdr_notrack = 1;
}
static void xgbe_phy_kr_training_post(struct xgbe_prv_data *pdata)
{
if (!pdata->debugfs_an_cdr_track_early)
xgbe_phy_cdr_track(pdata);
}
static void xgbe_phy_kr_training_pre(struct xgbe_prv_data *pdata)
{
if (pdata->debugfs_an_cdr_track_early)
xgbe_phy_cdr_track(pdata);
}
static void xgbe_phy_an_post(struct xgbe_prv_data *pdata)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73:
case XGBE_AN_MODE_CL73_REDRV:
if (phy_data->cur_mode != XGBE_MODE_KR)
break;
xgbe_phy_cdr_track(pdata);
switch (pdata->an_result) {
case XGBE_AN_READY:
case XGBE_AN_COMPLETE:
break;
default:
if (phy_data->phy_cdr_delay < XGBE_CDR_DELAY_MAX)
phy_data->phy_cdr_delay += XGBE_CDR_DELAY_INC;
else
phy_data->phy_cdr_delay = XGBE_CDR_DELAY_INIT;
break;
}
break;
default:
break;
}
}
static void xgbe_phy_an_pre(struct xgbe_prv_data *pdata)
{
struct xgbe_phy_data *phy_data = pdata->phy_data;
switch (pdata->an_mode) {
case XGBE_AN_MODE_CL73:
case XGBE_AN_MODE_CL73_REDRV:
if (phy_data->cur_mode != XGBE_MODE_KR)
break;
xgbe_phy_cdr_notrack(pdata);
break;
default:
break;
}
}
static void xgbe_phy_stop(struct xgbe_prv_data *pdata) static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
{ {
struct xgbe_phy_data *phy_data = pdata->phy_data; struct xgbe_phy_data *phy_data = pdata->phy_data;
...@@ -2680,6 +2789,9 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) ...@@ -2680,6 +2789,9 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
xgbe_phy_sfp_reset(phy_data); xgbe_phy_sfp_reset(phy_data);
xgbe_phy_sfp_mod_absent(pdata); xgbe_phy_sfp_mod_absent(pdata);
/* Reset CDR support */
xgbe_phy_cdr_track(pdata);
/* Power off the PHY */ /* Power off the PHY */
xgbe_phy_power_off(pdata); xgbe_phy_power_off(pdata);
...@@ -2712,6 +2824,9 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata) ...@@ -2712,6 +2824,9 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata)
/* Start in highest supported mode */ /* Start in highest supported mode */
xgbe_phy_set_mode(pdata, phy_data->start_mode); xgbe_phy_set_mode(pdata, phy_data->start_mode);
/* Reset CDR support */
xgbe_phy_cdr_track(pdata);
/* After starting the I2C controller, we can check for an SFP */ /* After starting the I2C controller, we can check for an SFP */
switch (phy_data->port_mode) { switch (phy_data->port_mode) {
case XGBE_PORT_MODE_SFP: case XGBE_PORT_MODE_SFP:
...@@ -3019,6 +3134,8 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) ...@@ -3019,6 +3134,8 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
} }
} }
phy_data->phy_cdr_delay = XGBE_CDR_DELAY_INIT;
/* Register for driving external PHYs */ /* Register for driving external PHYs */
mii = devm_mdiobus_alloc(pdata->dev); mii = devm_mdiobus_alloc(pdata->dev);
if (!mii) { if (!mii) {
...@@ -3071,4 +3188,10 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if) ...@@ -3071,4 +3188,10 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if)
phy_impl->an_advertising = xgbe_phy_an_advertising; phy_impl->an_advertising = xgbe_phy_an_advertising;
phy_impl->an_outcome = xgbe_phy_an_outcome; phy_impl->an_outcome = xgbe_phy_an_outcome;
phy_impl->an_pre = xgbe_phy_an_pre;
phy_impl->an_post = xgbe_phy_an_post;
phy_impl->kr_training_pre = xgbe_phy_kr_training_pre;
phy_impl->kr_training_post = xgbe_phy_kr_training_post;
} }
...@@ -994,6 +994,7 @@ struct xgbe_version_data { ...@@ -994,6 +994,7 @@ struct xgbe_version_data {
unsigned int irq_reissue_support; unsigned int irq_reissue_support;
unsigned int tx_desc_prefetch; unsigned int tx_desc_prefetch;
unsigned int rx_desc_prefetch; unsigned int rx_desc_prefetch;
unsigned int an_cdr_workaround;
}; };
struct xgbe_vxlan_data { struct xgbe_vxlan_data {
...@@ -1262,6 +1263,9 @@ struct xgbe_prv_data { ...@@ -1262,6 +1263,9 @@ struct xgbe_prv_data {
unsigned int debugfs_xprop_reg; unsigned int debugfs_xprop_reg;
unsigned int debugfs_xi2c_reg; unsigned int debugfs_xi2c_reg;
bool debugfs_an_cdr_workaround;
bool debugfs_an_cdr_track_early;
}; };
/* Function prototypes*/ /* Function prototypes*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment