When the client driver starts the port, it invokes 'efx_port_init' API. At
this stage, libefx has no way of knowing whether the driver will prefer to
keep the existing link or revise, say, FEC mode. So if the current link is
up, it is better to leave it alone. This way, if the driver does not tweak
FEC mode with 'efx_phy_adv_cap_set' later on, the link will likely stay up.

Save the last FEC choice (select 'AUTO' on attach) and apply it by default.

Signed-off-by: Ivan Malov <ivan.ma...@arknetworks.am>
---
 drivers/common/sfc_efx/base/efx_impl.h     |  6 ++++
 drivers/common/sfc_efx/base/efx_np.c       | 42 ++++++++++++++++++----
 drivers/common/sfc_efx/base/efx_phy.c      |  4 +++
 drivers/common/sfc_efx/base/efx_port.c     |  1 +
 drivers/common/sfc_efx/base/medford4_phy.c |  1 +
 5 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/drivers/common/sfc_efx/base/efx_impl.h 
b/drivers/common/sfc_efx/base/efx_impl.h
index 69268546d2..c91fbbb61b 100644
--- a/drivers/common/sfc_efx/base/efx_impl.h
+++ b/drivers/common/sfc_efx/base/efx_impl.h
@@ -398,6 +398,10 @@ typedef struct efx_port_s {
        efx_np_stat_t           ep_np_mac_stat_lut[EFX_MAC_NSTATS];
        /* Client-requested lane count for the physical link. */
        efx_phy_lane_count_t    ep_np_lane_count_req;
+       /* If 'B_TRUE', the client has not invoked 'efx_phy_adv_cap_set' yet. */
+       boolean_t               ep_np_keep_prev_fec_ctrl;
+       /* It's 'AUTO' on driver attach. Updated on each 'LINK_CTRL' call. */
+       uint8_t                 ep_np_prev_fec_ctrl;
 } efx_port_t;
 
 typedef struct efx_mon_ops_s {
@@ -1957,6 +1961,8 @@ efx_np_link_ctrl(
        __in            efx_link_mode_t loopback_link_mode,
        __in            efx_loopback_type_t loopback_mode,
        __in            efx_phy_lane_count_t lane_count,
+       __in            boolean_t keep_prev_fec_ctrl,
+       __inout         uint8_t *prev_fec_ctrlp,
        __in            uint32_t cap_mask_sw,
        __in            boolean_t fcntl_an);
 
diff --git a/drivers/common/sfc_efx/base/efx_np.c 
b/drivers/common/sfc_efx/base/efx_np.c
index 54305ee61e..327981f0db 100644
--- a/drivers/common/sfc_efx/base/efx_np.c
+++ b/drivers/common/sfc_efx/base/efx_np.c
@@ -1026,6 +1026,12 @@ efx_np_attach(
        /* For faster link up, use autoneg. flow control by default. */
        epp->ep_fcntl_autoneg = B_TRUE;
 
+       /*
+        * We have no way of knowing which 'FEC_MODE' choice the previous
+        * client driver passed via 'LINK_CTRL'. Assume 'AUTO' by default.
+        */
+       epp->ep_np_prev_fec_ctrl = MC_CMD_FEC_AUTO;
+
        /* Subscribe to link change events. */
        rc = efx_np_set_event_mask(enp, epp->ep_np_handle, B_TRUE);
        if (rc != 0)
@@ -1277,6 +1283,8 @@ efx_np_link_ctrl(
        __in            efx_link_mode_t loopback_link_mode,
        __in            efx_loopback_type_t loopback_mode,
        __in            efx_phy_lane_count_t lane_count,
+       __in            boolean_t keep_prev_fec_ctrl,
+       __inout         uint8_t *prev_fec_ctrlp,
        __in            uint32_t cap_mask_sw,
        __in            boolean_t fcntl_an)
 {
@@ -1380,13 +1388,34 @@ efx_np_link_ctrl(
         *
         * No requested FEC bits in the original mask gives supported=TRUE.
         */
-       EFX_NP_CAP_SW_MASK_TO_HW_ENUM(efx_np_cap_map_fec_req,
-           ETH_AN_FIELDS_FEC_REQ, cap_data_raw, cap_mask_sw,
-           NULL, NULL, &supported, &cap_enum_hw);
+       if (keep_prev_fec_ctrl == B_FALSE) {
+               /*
+                * Enforce what the user has asked for. If the mask has got
+                * no 'FEC_REQUESTED' bits, use 'NONE' or 'AUTO' from above.
+                */
+               EFX_NP_CAP_SW_MASK_TO_HW_ENUM(efx_np_cap_map_fec_req,
+                   ETH_AN_FIELDS_FEC_REQ, cap_data_raw, cap_mask_sw,
+                   NULL, NULL, &supported, &cap_enum_hw);
 
-       if ((cap_mask_sw & EFX_PHY_CAP_FEC_MASK) != 0 && supported == B_FALSE) {
-               rc = ENOTSUP;
-               goto fail5;
+               if ((cap_mask_sw & EFX_PHY_CAP_FEC_MASK) != 0
+                   && supported == B_FALSE) {
+                       rc = ENOTSUP;
+                       goto fail5;
+               }
+       } else {
+               /*
+                * At this stage, the FEC mask does not draw any input from
+                * the client driver. If the active link was autonegotiated,
+                * it might contain 'FEC_REQUESTED' bits translated earlier
+                * from 'ADVERTISED_ABILITIES_FEC_REQ' of 'LINK_STATE' MCDI.
+                * If the link is fixed, it does not have any of these bits.
+                *
+                * Either way, translating it into a fixed enum will result
+                * in a fixed FEC choice. If that does not match the choice
+                * of the previous user, a lengthy link renegotiation might
+                * follow. Pass previous choice to avoid long link-up times.
+                */
+               cap_enum_hw = *prev_fec_ctrlp;
        }
 
        EFSYS_ASSERT(cap_enum_hw <= UINT8_MAX);
@@ -1404,6 +1433,7 @@ efx_np_link_ctrl(
                goto fail6;
        }
 
+       *prev_fec_ctrlp = fec;
        return (0);
 
 fail6:
diff --git a/drivers/common/sfc_efx/base/efx_phy.c 
b/drivers/common/sfc_efx/base/efx_phy.c
index 1f99c72e62..1f1552ce1f 100644
--- a/drivers/common/sfc_efx/base/efx_phy.c
+++ b/drivers/common/sfc_efx/base/efx_phy.c
@@ -251,6 +251,7 @@ efx_phy_adv_cap_set(
 {
        efx_port_t *epp = &(enp->en_port);
        const efx_phy_ops_t *epop = epp->ep_epop;
+       boolean_t old_np_keep_prev_fec_ctrl;
        uint32_t old_mask;
        efx_rc_t rc;
 
@@ -271,6 +272,8 @@ efx_phy_adv_cap_set(
        if (epp->ep_adv_cap_mask == mask)
                goto done;
 
+       old_np_keep_prev_fec_ctrl = epp->ep_np_keep_prev_fec_ctrl;
+       epp->ep_np_keep_prev_fec_ctrl = B_FALSE;
        old_mask = epp->ep_adv_cap_mask;
        epp->ep_adv_cap_mask = mask;
 
@@ -283,6 +286,7 @@ efx_phy_adv_cap_set(
 fail3:
        EFSYS_PROBE(fail3);
 
+       epp->ep_np_keep_prev_fec_ctrl = old_np_keep_prev_fec_ctrl;
        epp->ep_adv_cap_mask = old_mask;
        /* Reconfigure for robustness */
        if (epop->epo_reconfigure(enp) != 0) {
diff --git a/drivers/common/sfc_efx/base/efx_port.c 
b/drivers/common/sfc_efx/base/efx_port.c
index 7e514e92dd..5ae5e144bd 100644
--- a/drivers/common/sfc_efx/base/efx_port.c
+++ b/drivers/common/sfc_efx/base/efx_port.c
@@ -27,6 +27,7 @@ efx_port_init(
        enp->en_mod_flags |= EFX_MOD_PORT;
 
        epp->ep_np_lane_count_req = EFX_PHY_LANE_COUNT_DEFAULT;
+       epp->ep_np_keep_prev_fec_ctrl = B_TRUE;
        epp->ep_mac_type = EFX_MAC_INVALID;
        epp->ep_link_mode = EFX_LINK_UNKNOWN;
        epp->ep_mac_drain = B_TRUE;
diff --git a/drivers/common/sfc_efx/base/medford4_phy.c 
b/drivers/common/sfc_efx/base/medford4_phy.c
index 9ba6dfbc10..ef7e4e5bac 100644
--- a/drivers/common/sfc_efx/base/medford4_phy.c
+++ b/drivers/common/sfc_efx/base/medford4_phy.c
@@ -117,6 +117,7 @@ medford4_phy_reconfigure(
 
        rc = efx_np_link_ctrl(enp, epp->ep_np_handle, epp->ep_np_cap_data_raw,
                    loopback_link_mode, loopback, epp->ep_np_lane_count_req,
+                   epp->ep_np_keep_prev_fec_ctrl, &epp->ep_np_prev_fec_ctrl,
                    epp->ep_adv_cap_mask, epp->ep_fcntl_autoneg);
        if (rc != 0)
                goto fail2;
-- 
2.47.2

Reply via email to