amd-xgbe: Improve KR auto-negotiation and training
authorTom Lendacky <thomas.lendacky@amd.com>
Mon, 23 Apr 2018 16:43:17 +0000 (11:43 -0500)
committerDavid S. Miller <davem@davemloft.net>
Tue, 24 Apr 2018 01:24:22 +0000 (21:24 -0400)
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: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/amd/xgbe/xgbe-common.h
drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
drivers/net/ethernet/amd/xgbe/xgbe-main.c
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
drivers/net/ethernet/amd/xgbe/xgbe-pci.c
drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
drivers/net/ethernet/amd/xgbe/xgbe.h

index 7ea72ef..d272dc6 100644 (file)
 #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
 #define XGBE_AN_CL37_TX_CONFIG_MASK    0x08
 #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
  *  The get macro will extract the current bit field value from within
  *  the variable
index 7d128be..b911439 100644 (file)
@@ -519,6 +519,22 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
                                   "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);
 }
 
index 795e556..441d097 100644 (file)
@@ -349,6 +349,7 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata)
        XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, UDP4TE, 1);
 
        /* Call MDIO/PHY initialization routine */
+       pdata->debugfs_an_cdr_workaround = pdata->vdata->an_cdr_workaround;
        ret = pdata->phy_if.phy_init(pdata);
        if (ret)
                return ret;
index e3d361e..1b45cd7 100644 (file)
@@ -432,6 +432,8 @@ static void xgbe_an73_disable(struct xgbe_prv_data *pdata)
        xgbe_an73_set(pdata, false, false);
        xgbe_an73_disable_interrupts(pdata);
 
+       pdata->an_start = 0;
+
        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,
                XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL,
                            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,
                          "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;
index eb23f9b..82d1f41 100644 (file)
@@ -456,6 +456,7 @@ static const struct xgbe_version_data xgbe_v2a = {
        .irq_reissue_support            = 1,
        .tx_desc_prefetch               = 5,
        .rx_desc_prefetch               = 5,
+       .an_cdr_workaround              = 1,
 };
 
 static const struct xgbe_version_data xgbe_v2b = {
@@ -470,6 +471,7 @@ static const struct xgbe_version_data xgbe_v2b = {
        .irq_reissue_support            = 1,
        .tx_desc_prefetch               = 5,
        .rx_desc_prefetch               = 5,
+       .an_cdr_workaround              = 1,
 };
 
 static const struct pci_device_id xgbe_pci_table[] = {
index 3304a29..b48efc0 100644 (file)
 /* Rate-change complete wait/retry count */
 #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 {
        XGBE_PORT_MODE_RSVD = 0,
        XGBE_PORT_MODE_BACKPLANE,
@@ -355,6 +363,10 @@ struct xgbe_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;
 };
 
 /* 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)
                return 1;
 
        /* 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;
                xgbe_phy_rrc(pdata);
        }
@@ -2669,6 +2681,103 @@ static bool xgbe_phy_port_enabled(struct xgbe_prv_data *pdata)
        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)
 {
        struct xgbe_phy_data *phy_data = pdata->phy_data;
@@ -2680,6 +2789,9 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
        xgbe_phy_sfp_reset(phy_data);
        xgbe_phy_sfp_mod_absent(pdata);
 
+       /* Reset CDR support */
+       xgbe_phy_cdr_track(pdata);
+
        /* Power off the PHY */
        xgbe_phy_power_off(pdata);
 
@@ -2712,6 +2824,9 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata)
        /* Start in highest supported 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 */
        switch (phy_data->port_mode) {
        case XGBE_PORT_MODE_SFP:
@@ -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 */
        mii = devm_mdiobus_alloc(pdata->dev);
        if (!mii) {
@@ -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_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;
 }
index fa0b51e..95d4b56 100644 (file)
@@ -994,6 +994,7 @@ struct xgbe_version_data {
        unsigned int irq_reissue_support;
        unsigned int tx_desc_prefetch;
        unsigned int rx_desc_prefetch;
+       unsigned int an_cdr_workaround;
 };
 
 struct xgbe_vxlan_data {
@@ -1262,6 +1263,9 @@ struct xgbe_prv_data {
        unsigned int debugfs_xprop_reg;
 
        unsigned int debugfs_xi2c_reg;
+
+       bool debugfs_an_cdr_workaround;
+       bool debugfs_an_cdr_track_early;
 };
 
 /* Function prototypes*/