Backplane CR/KR link training on the E56 PHY is unreliable and does
not recover after a link event: the completion poll can block for a
second on a condition that never triggers, the page exchange is only
run once and its result is assumed to stay valid, and the FFE init
mode and calibration ordering are also wrong.

- Poll the AN FSM (0x78010, value 0x9) for CL72 completion with a
  1 ms step and a 400 ms budget, replacing the ephy 0x163c mask-0xe
  poll that tested the wrong condition; the per-lane TX-FFE dumps
  move into txgbe_e56_get_txffe(), called once AN completes.
- Re-run the page exchange on every AN next-page interrupt from the
  event handler (50 x 1 ms poll, explicit next-page handshake,
  -ETIMEDOUT on expiry) and only enter training after it succeeds;
  the inline exchange in the AN73 flow is removed and a CL72 failure
  no longer aborts the flow, so every link event re-runs the
  exchange instead of assuming the previous one is still valid.
- Initialize the FFE init mode bits in cfg_40g()/cfg_10g(), run
  txgbe_e56_set_rxs_ufine_le_max() after the RXS osc init, and write
  0x78001 = 0x7 in the AN programming sequence.
- Clear hw->bp_link_mode in txgbe_set_link_to_sfi() so a port that
  trained on the backplane does not keep a stale backplane mode
  after switching to SFI.

Fixes: 234ce0d1fa9d ("net/txgbe: fix link stability for Amber-Lite backplane 
mode")
Cc: [email protected]

Signed-off-by: Zaiyu Wang <[email protected]>
---
 drivers/net/txgbe/base/txgbe_e56.c    |   3 +
 drivers/net/txgbe/base/txgbe_e56.h    |   1 +
 drivers/net/txgbe/base/txgbe_e56_bp.c | 161 +++++++++++++++-----------
 drivers/net/txgbe/base/txgbe_e56_bp.h |   2 +
 drivers/net/txgbe/base/txgbe_phy.c    |   3 +
 drivers/net/txgbe/txgbe_ethdev.c      |  28 ++++-
 6 files changed, 129 insertions(+), 69 deletions(-)

diff --git a/drivers/net/txgbe/base/txgbe_e56.c 
b/drivers/net/txgbe/base/txgbe_e56.c
index 2711863f2c..8e986daf96 100644
--- a/drivers/net/txgbe/base/txgbe_e56.c
+++ b/drivers/net/txgbe/base/txgbe_e56.c
@@ -551,6 +551,7 @@ u32 txgbe_e56_cfg_40g(struct txgbe_hw *hw)
 
        addr  = E56PHY_FETX_FFE_TRAIN_CFG_0_ADDR;
        rdata = rd32_ephy(hw, addr);
+       set_fields_e56(&rdata, 
E56PHY_FETX_FFE_TRAIN_CFG_0_KRT_FETX_INIT_FFE_CFG_0, 0x2);
        set_fields_e56(&rdata, 
E56PHY_FETX_FFE_TRAIN_CFG_0_KRT_FETX_INIT_FFE_CFG_2, 0x2);
        wr32_ephy(hw, addr, rdata);
 
@@ -1377,6 +1378,8 @@ txgbe_e56_cfg_10g(struct txgbe_hw *hw)
 
        addr = E56PHY_FETX_FFE_TRAIN_CFG_0_ADDR;
        rdata = rd32_ephy(hw, addr);
+       set_fields_e56(&rdata, 
E56PHY_FETX_FFE_TRAIN_CFG_0_KRT_FETX_INIT_FFE_CFG_0,
+                      0x2);
        set_fields_e56(&rdata, 
E56PHY_FETX_FFE_TRAIN_CFG_0_KRT_FETX_INIT_FFE_CFG_2,
                       0x2);
        wr32_ephy(hw, addr, rdata);
diff --git a/drivers/net/txgbe/base/txgbe_e56.h 
b/drivers/net/txgbe/base/txgbe_e56.h
index fe9a83ae01..32d95b61b9 100644
--- a/drivers/net/txgbe/base/txgbe_e56.h
+++ b/drivers/net/txgbe/base/txgbe_e56.h
@@ -435,6 +435,7 @@ typedef union {
 #define E56PHY_KRT_TFSM_CFGKRT_TFSM_HOLDOFF_TIMER_X256K 23, 16
 
 #define E56PHY_FETX_FFE_TRAIN_CFG_0_ADDR   (E56PHY_PMD_BASE_ADDR + 0x2BC)
+#define E56PHY_FETX_FFE_TRAIN_CFG_0_KRT_FETX_INIT_FFE_CFG_0 1, 0
 #define E56PHY_FETX_FFE_TRAIN_CFG_0_KRT_FETX_INIT_FFE_CFG_2 9, 8
 #define E56PHY_FETX_FFE_TRAIN_CFG_0_KRT_FETX_INIT_FFE_CFG_3 13, 12
 
diff --git a/drivers/net/txgbe/base/txgbe_e56_bp.c 
b/drivers/net/txgbe/base/txgbe_e56_bp.c
index d376d918df..f840e6b5dd 100644
--- a/drivers/net/txgbe/base/txgbe_e56_bp.c
+++ b/drivers/net/txgbe/base/txgbe_e56_bp.c
@@ -813,6 +813,7 @@ static int txgbe_e56_phy_rxs_calib_adapt_seq(struct 
txgbe_hw *hw,
                status |= txgbe_e56_ctle_bypass_seq(hw, bp_link_mode);
 
        status |= txgbe_e56_rxs_osc_init_for_temp_track_range(hw, bp_link_mode);
+       txgbe_e56_set_rxs_ufine_le_max(hw, bp_link_mode);
 
        /* Wait an fsm_rx_sts 25G */
        BP_LOG("Wait CTRL_FSM_RX_STAT[0]::ctrl_fsm_rx0_st to be ready ...\n");
@@ -2123,6 +2124,7 @@ int txgbe_e56_set_phy_link_mode(struct txgbe_hw *hw,
        set_fields_e56(&rdata, 12, 12, 0x1);
        wr32_epcs(hw, 0x070000, rdata);
        wr32_epcs(hw, 0x078002, 0x0000);
+       wr32_epcs(hw, 0x78001, 0x7);
        /* pcs case fec en to work around first */
        wr32_epcs(hw, 0x100ab, 1);
 
@@ -2351,24 +2353,29 @@ static int chk_bkp_ability(struct txgbe_hw *hw,
        return 0;
 }
 
-static int txgbe_e56_exchange_page(struct txgbe_hw *hw)
+int txgbe_e56_exchange_page(struct txgbe_hw *hw)
 {
        struct txgbe_backplane_ability local_ability = {0}, lp_ability = {0};
        u32 an_int, base_page = 0;
-       int count = 0;
+       int count = 0, count2 = 0;
 
        an_int = rd32_epcs(hw, 0x78002);
-       /* 500ms timeout */
        if (!(an_int & VR_AN_INTR_PG_RCV))
                return -EINVAL;
 
-       for (count = 0; count < 500; count++) {
+       /* 50ms timeout */
+       for (count = 0; count < 50; count++) {
                u32 fsm = rd32_epcs(hw, 0x78010);
-               u32 rdata = rd32_epcs(hw, 0x78002);
+               u32 next_page = 0;
+               u32 rdata;
+
+               count2++;
 
                BP_LOG("-----count----- %d - fsm: %x\n", count, fsm);
-               BP_LOG("read 78002 data %0x and clear pacv\n", rdata);
+               rdata = rd32_epcs(hw, 0x78002);
+               /* clear an pacv int */
                an_int = rdata;
+               BP_LOG("read 78002 data %0x and clear pacv\n", rdata);
                set_fields_e56(&rdata, 2, 2, 0x0);
                wr32_epcs(hw, 0x78002, rdata);
                if (an_int & VR_AN_INTR_PG_RCV) {
@@ -2383,31 +2390,84 @@ static int txgbe_e56_exchange_page(struct txgbe_hw *hw)
                                        wr32_epcs(hw, 0x70016, 0x2001);
                                        BP_LOG("write 70016 0x%0x\n",
                                               0x2001);
+                                       next_page = 1;
+                                       count = 0; /* reset count to wait next 
page */
+                               } else {
+                                       next_page = 0;
                                }
                                base_page = 1;
                        }
                }
-               if ((fsm & 0x8) == 0x8) {
-                       hw->fsm = 0x8;
-                       goto check_ability;
+               if (!next_page) {
+                       if ((fsm & 0x8) == 0x8) {
+                               hw->fsm = 0x8;
+                               goto check_ability;
+                       }
                }
-               usec_delay(100);
+               usec_delay(1000);
        }
 
 check_ability:
+       if (count == 50) {
+               BP_LOG("Wait for next page timeout\n");
+               return -ETIMEDOUT;
+       }
+       BP_LOG("AN exchange page done in %d ms\n", count2);
        return chk_bkp_ability(hw, local_ability, lp_ability);
 }
 
+void txgbe_e56_get_txffe(struct txgbe_hw *hw)
+{
+       /* 21. read txffe to check kr training status */
+       u32 rdata = 0, pmd_ctrl = 0, lane_idx = 0, lane_num = 0, txffe = 0;
+
+       switch (hw->bp_link_mode) {
+       case 10:
+               lane_num = 1;
+               break;
+       case 40:
+               lane_num = 4;
+               break;
+       case 25:
+               lane_num = 1;
+               break;
+       default:
+               BP_LOG("%s %d :Invalid speed\n", __func__, __LINE__);
+               return;
+       }
+
+       BP_LOG("%dG phy kr training check.... fsm: %x\n",
+              hw->bp_link_mode, rd32_epcs(hw, 0x78010));
+       rdata = rd32_ephy(hw, 0x163c) & GENMASK(lane_num, 1);
+       pmd_ctrl = rd32_ephy(hw, 0x1644);
+       BP_LOG("KR TRAINNING CHECK = %x. pmd_ctrl:%lx-%lx-%lx-%lx\n",
+              rdata,
+              FIELD_GET_M(GENMASK(3, 0), pmd_ctrl),
+              FIELD_GET_M(GENMASK(7, 4), pmd_ctrl),
+              FIELD_GET_M(GENMASK(11, 8), pmd_ctrl),
+              FIELD_GET_M(GENMASK(15, 12), pmd_ctrl));
+       BP_LOG("before: %x-%x-%x-%x\n",
+              rd32_ephy(hw, 0x141c), rd32_ephy(hw, 0x1420),
+              rd32_ephy(hw, 0x1424), rd32_ephy(hw, 0x1428));
+       for (lane_idx = 0; lane_idx < lane_num; lane_idx++) {
+               txffe = rd32_ephy(hw, 0x828 + lane_idx * 0x100);
+               BP_LOG("after[%x]: %lx-%lx-%lx-%lx\n", lane_idx,
+                      FIELD_GET_M(GENMASK(6, 0), txffe),
+                      FIELD_GET_M(GENMASK(21, 16), txffe),
+                      FIELD_GET_M(GENMASK(29, 24), txffe),
+                      FIELD_GET_M(GENMASK(13, 8), txffe));
+       }
+}
+
 static int txgbe_e56_cl72_training(struct txgbe_hw *hw)
 {
        u32 bylinkmode = hw->bp_link_mode;
        u8 bypass_ctle = hw->bypass_ctle;
        int status = 0, temp_data = 0;
-       u32 lane_num = 0, lane_idx = 0;
-       u32 __rte_unused pmd_ctrl = 0, txffe = 0;
+       u32 lane_num = 0;
+       u32 __rte_unused pmd_ctrl = 0;
        int ret = 0;
        u32 rdata;
-
        u8 pll_en_cfg = 0;
        u8 pmd_mode = 0;
 
@@ -2463,52 +2523,48 @@ static int txgbe_e56_cl72_training(struct txgbe_hw *hw)
 
        /* 18 */
        /* 19. rxs calibration and adaptation sequence */
-       BP_LOG("2.4 Wait %dG RXS.... fsm: %x\n",
-              bylinkmode, rd32_epcs(hw, 0x78010));
+       BP_LOG("2.4 Wait %dG RXS.... fsm: %x, an_int: %x\n",
+              bylinkmode, rd32_epcs(hw, 0x78010),
+              rd32_epcs(hw, 0x78002));
        status = txgbe_e56_phy_rxs_calib_adapt_seq(hw, bylinkmode, bypass_ctle);
        ret |= status;
        /* 20 */
-       BP_LOG("2.5 Wait %dG phy calibration.... fsm: %x\n",
-              bylinkmode, rd32_epcs(hw, 0x78010));
-       txgbe_e56_set_rxs_ufine_le_max(hw, bylinkmode);
+       BP_LOG("2.5 Wait %dG phy calibration.... fsm: %x, an_int: %x\n",
+              bylinkmode, rd32_epcs(hw, 0x78010),
+              rd32_epcs(hw, 0x78002));
        status = txgbe_e56_get_temp(hw, &temp_data);
        if (bylinkmode == 40)
                status = txgbe_temp_track_seq_40g(hw, 
TXGBE_LINK_SPEED_40GB_FULL);
        else
                status = txgbe_e56_rxs_post_cdr_lock_temp_track_seq(hw, 
bylinkmode);
+
+       ret |= status;
        /* 21 */
-       BP_LOG("2.6 Wait %dG phy kr training check.... fsm: %x\n",
-              bylinkmode, rd32_epcs(hw, 0x78010));
-       status = kr_read_poll(rd32_ephy, rdata,
-                                 ((rdata & 0xe) & GENMASK(lane_num, 1)) ==
-                                 (0xe & GENMASK(lane_num, 1)), 100,
-                                  10000, hw, 0x163c);
+       BP_LOG("2.6 Wait %dG phy kr fsm check : %x, an_int: %x\n",
+              bylinkmode, rd32_epcs(hw, 0x78010),
+              rd32_epcs(hw, 0x78002));
+       status = kr_read_poll(rd32_epcs, rdata,
+                             (rdata & 0x9) == 0x9, 1000,
+                             400, hw, 0x78010);
        pmd_ctrl = rd32_ephy(hw, 0x1644);
-       BP_LOG("KR TRAINING CHECK = %x, %s. pmd_ctrl:%lx-%lx-%lx-%lx\n",
+       BP_LOG("KR FSM CHECK = %x, %s. pmd_ctrl:%lx-%lx-%lx-%lx\n",
               rdata, status ? "FAILED" : "SUCCESS",
               FIELD_GET_M(GENMASK(3, 0), pmd_ctrl),
               FIELD_GET_M(GENMASK(7, 4), pmd_ctrl),
               FIELD_GET_M(GENMASK(11, 8), pmd_ctrl),
               FIELD_GET_M(GENMASK(15, 12), pmd_ctrl));
        ret |= status;
-       BP_LOG("before: %x-%x-%x-%x\n",
-              rd32_ephy(hw, 0x141c), rd32_ephy(hw, 0x1420),
-              rd32_ephy(hw, 0x1424), rd32_ephy(hw, 0x1428));
-
-       for (lane_idx = 0; lane_idx < lane_num; lane_idx++) {
-               txffe = rd32_ephy(hw, 0x828 + lane_idx * 0x100);
-               BP_LOG("after[%x]: %lx-%lx-%lx-%lx\n", lane_idx,
-                      FIELD_GET_M(GENMASK(6, 0), txffe),
-                      FIELD_GET_M(GENMASK(21, 16), txffe),
-                      FIELD_GET_M(GENMASK(29, 24), txffe),
-                      FIELD_GET_M(GENMASK(13, 8), txffe));
-       }
 
        /* 22 */
-       BP_LOG("2.7 Wait %dG phy Rx adc.... fsm:%x\n",
-              bylinkmode, rd32_epcs(hw, 0x78010));
+       BP_LOG("2.7 Wait %dG phy Rx adc.... fsm:%x, an_int: %x\n",
+              bylinkmode, rd32_epcs(hw, 0x78010),
+              rd32_epcs(hw, 0x78002));
        status = txgbe_e56_rxs_adc_adapt_seq(hw, bypass_ctle);
 
+       BP_LOG("2.8 ===end ret : %d.... fsm:%x, an_int: %x\n",
+              ret, rd32_epcs(hw, 0x78010),
+              rd32_epcs(hw, 0x78002));
+
        return ret;
 }
 
@@ -2517,27 +2573,7 @@ int txgbe_handle_e56_bkp_an73_flow(struct txgbe_hw *hw)
        int status = 0;
        u32 rdata;
 
-       BP_LOG("2.1 Wait page changed ....\n");
-       status = txgbe_e56_exchange_page(hw);
-       if (status) {
-               BP_LOG("Exchange page failed\n");
-               return status;
-       }
-
-       BP_LOG("2.2 Wait page changed ..done..\n");
-       wr32_epcs(hw, 0x100ab, 0);
-       if (AN_TRAINING_MODE) {
-               rdata = rd32_epcs(hw, 0x70000);
-               BP_LOG("read 0x70000 data %0x\n", rdata);
-               wr32_epcs(hw, 0x70000, 0);
-               BP_LOG("write 0x70000 0x%0x\n", 0);
-       }
-
-       rdata = rd32_epcs(hw, 0x78002);
-       BP_LOG("read 78002 data %0x and clear page int\n", rdata);
-       set_fields_e56(&rdata, 2, 2, 0x0);
-       wr32_epcs(hw, 0x78002, rdata);
-
+       /* 10  RXS_DISABLE - TXS_DISABLE - CMS_DISABLE */
        /* dis phy tx/rx lane */
        rdata = rd32_ephy(hw, 0x1400);
        set_fields_e56(&rdata, 19, 16, 0x0);
@@ -2583,11 +2619,6 @@ int txgbe_handle_e56_bkp_an73_flow(struct txgbe_hw *hw)
        }
 
        status = txgbe_e56_cl72_training(hw);
-       if (status) {
-               BP_LOG("CL72 training failed, status = %d\n", status);
-               return status;
-       }
-
        rdata = rd32_ephy(hw, E56PHY_RXS_IDLE_DETECT_1_ADDR);
        set_fields_e56(&rdata, E56PHY_RXS_IDLE_DETECT_1_IDLE_TH_ADC_PEAK_MAX, 
0x28);
        set_fields_e56(&rdata, E56PHY_RXS_IDLE_DETECT_1_IDLE_TH_ADC_PEAK_MIN, 
0xa);
diff --git a/drivers/net/txgbe/base/txgbe_e56_bp.h 
b/drivers/net/txgbe/base/txgbe_e56_bp.h
index d2c49c2fce..da0d02b79f 100644
--- a/drivers/net/txgbe/base/txgbe_e56_bp.h
+++ b/drivers/net/txgbe/base/txgbe_e56_bp.h
@@ -279,4 +279,6 @@ typedef union {
 int txgbe_e56_set_phy_link_mode(struct txgbe_hw *hw,
                                u8 bp_link_mode, u32 need_restart);
 int txgbe_handle_e56_bkp_an73_flow(struct txgbe_hw *hw);
+int txgbe_e56_exchange_page(struct txgbe_hw *hw);
+void txgbe_e56_get_txffe(struct txgbe_hw *hw);
 #endif
diff --git a/drivers/net/txgbe/base/txgbe_phy.c 
b/drivers/net/txgbe/base/txgbe_phy.c
index 8b653f0aeb..10df23afae 100644
--- a/drivers/net/txgbe/base/txgbe_phy.c
+++ b/drivers/net/txgbe/base/txgbe_phy.c
@@ -1947,6 +1947,9 @@ txgbe_set_link_to_sfi(struct txgbe_hw *hw,
        s32 err = 0;
        u32 value = 0;
 
+       /* Switching to SFI mode clears backplane link mode. */
+       hw->bp_link_mode = 0;
+
        /* Set the module link speed */
        hw->mac.set_rate_select_speed(hw, speed);
        /* 1. Wait xpcs power-up good */
diff --git a/drivers/net/txgbe/txgbe_ethdev.c b/drivers/net/txgbe/txgbe_ethdev.c
index ef30e0c40a..269e0aadd9 100644
--- a/drivers/net/txgbe/txgbe_ethdev.c
+++ b/drivers/net/txgbe/txgbe_ethdev.c
@@ -3025,6 +3025,7 @@ void txgbe_dev_e56_check_bp_event(void *param)
        u32 __rte_unused an_int = 0;
        int ret = 0;
        bool need_link_update = false;
+       bool exchange_done = false;
 
        if (!hw)
                return;
@@ -3043,8 +3044,10 @@ void txgbe_dev_e56_check_bp_event(void *param)
                hw->phy.sfp_type = txgbe_sfp_type_not_present;
        }
 
-       if (!(txgbe_xpcs_an_enabled(hw)))
+       if (!(txgbe_xpcs_an_enabled(hw))) {
+               BP_LOG("%s %d\n", __func__, __LINE__);
                return;
+       }
 
        if (!hw->devarg.auto_neg)
                return;
@@ -3063,6 +3066,7 @@ void txgbe_dev_e56_check_bp_event(void *param)
                need_link_update = true;
                value &= ~VR_AN_INTR_CMPLT;
                wr32_epcs(hw, VR_AN_INTR, value);
+               txgbe_e56_get_txffe(hw);
        }
 
        if (value & VR_AN_INTR_LINK) {
@@ -3080,7 +3084,21 @@ void txgbe_dev_e56_check_bp_event(void *param)
        }
 
        if (value & VR_AN_INTR_PG_RCV) {
-               BP_LOG("%d Enter training\n", hw->port_id);
+               BP_LOG("%d 2.1 *** Wait page changed ....\n", hw->port_id);
+               ret = txgbe_e56_exchange_page(hw);
+               if (ret) {
+                       BP_LOG("%d 2.2 *** Exchange page failed\n", 
hw->port_id);
+                       goto an_status;
+               } else {
+                       BP_LOG("%d 2.2 *** Wait page changed ..done..\n",
+                              hw->port_id);
+                       wr32_epcs(hw, 0x100ab, 0);
+                       exchange_done = true;
+               }
+       }
+
+       if (exchange_done) {
+               BP_LOG("%d 2.2.2 *** Enter training\n", hw->port_id);
                ret = txgbe_handle_e56_bkp_an73_flow(hw);
                if (!AN_TRAINING_MODE) {
                        fsm = rd32_epcs(hw, 0x78010);
@@ -3783,8 +3801,10 @@ txgbe_dev_interrupt_get_status(struct rte_eth_dev *dev,
        if (eicr & TXGBE_ICRMISC_LSC)
                intr->flags |= TXGBE_FLAG_NEED_LINK_UPDATE;
 
-       if (eicr & TXGBE_ICRMISC_ANDONE)
-               intr->flags |= TXGBE_FLAG_NEED_AN_CONFIG;
+       if (eicr & TXGBE_ICRMISC_ANDONE) {
+               PMD_DRV_LOG(DEBUG, "an int eicr=0x%08x", eicr);
+               intr->flags |= TXGBE_FLAG_NEED_AN_CONFIG;//aml40-to-do
+       }
 
        if (eicr & TXGBE_ICRMISC_VFMBX)
                intr->flags |= TXGBE_FLAG_MAILBOX;
-- 
2.55.0.windows.2

Reply via email to