EFSYS_OPT_LOOPBACK should be enabled to use it.

>From Solarflare Communications Inc.

Signed-off-by: Andrew Rybchenko <arybchenko at solarflare.com>
---
 drivers/net/sfc/base/ef10_impl.h  |  13 +++
 drivers/net/sfc/base/ef10_mac.c   |  36 ++++++++
 drivers/net/sfc/base/ef10_nic.c   |   9 ++
 drivers/net/sfc/base/ef10_phy.c   |  45 +++++++++
 drivers/net/sfc/base/efx.h        |  74 +++++++++++++++
 drivers/net/sfc/base/efx_check.h  |   7 ++
 drivers/net/sfc/base/efx_impl.h   |   8 ++
 drivers/net/sfc/base/efx_mac.c    |   6 ++
 drivers/net/sfc/base/efx_mcdi.h   |   6 ++
 drivers/net/sfc/base/efx_nic.c    | 188 ++++++++++++++++++++++++++++++++++++++
 drivers/net/sfc/base/efx_port.c   | 101 ++++++++++++++++++++
 drivers/net/sfc/base/siena_impl.h |  13 +++
 drivers/net/sfc/base/siena_mac.c  |  36 ++++++++
 drivers/net/sfc/base/siena_nic.c  |   9 ++
 drivers/net/sfc/base/siena_phy.c  |  42 +++++++++
 15 files changed, 593 insertions(+)

diff --git a/drivers/net/sfc/base/ef10_impl.h b/drivers/net/sfc/base/ef10_impl.h
index 0e5561e..01c48c8 100644
--- a/drivers/net/sfc/base/ef10_impl.h
+++ b/drivers/net/sfc/base/ef10_impl.h
@@ -259,6 +259,16 @@ extern                     void
 ef10_mac_filter_default_rxq_clear(
        __in            efx_nic_t *enp);

+#if EFSYS_OPT_LOOPBACK
+
+extern __checkReturn   efx_rc_t
+ef10_mac_loopback_set(
+       __in            efx_nic_t *enp,
+       __in            efx_link_mode_t link_mode,
+       __in            efx_loopback_type_t loopback_type);
+
+#endif /* EFSYS_OPT_LOOPBACK */
+
 #if EFSYS_OPT_MAC_STATS

 extern __checkReturn                   efx_rc_t
@@ -337,6 +347,9 @@ typedef struct ef10_link_state_s {
        uint32_t                els_lp_cap_mask;
        unsigned int            els_fcntl;
        efx_link_mode_t         els_link_mode;
+#if EFSYS_OPT_LOOPBACK
+       efx_loopback_type_t     els_loopback;
+#endif
        boolean_t               els_mac_up;
 } ef10_link_state_t;

diff --git a/drivers/net/sfc/base/ef10_mac.c b/drivers/net/sfc/base/ef10_mac.c
index 477d0e7..488633f 100644
--- a/drivers/net/sfc/base/ef10_mac.c
+++ b/drivers/net/sfc/base/ef10_mac.c
@@ -443,6 +443,42 @@ ef10_mac_filter_default_rxq_clear(
 }


+#if EFSYS_OPT_LOOPBACK
+
+       __checkReturn   efx_rc_t
+ef10_mac_loopback_set(
+       __in            efx_nic_t *enp,
+       __in            efx_link_mode_t link_mode,
+       __in            efx_loopback_type_t loopback_type)
+{
+       efx_port_t *epp = &(enp->en_port);
+       const efx_phy_ops_t *epop = epp->ep_epop;
+       efx_loopback_type_t old_loopback_type;
+       efx_link_mode_t old_loopback_link_mode;
+       efx_rc_t rc;
+
+       /* The PHY object handles this on EF10 */
+       old_loopback_type = epp->ep_loopback_type;
+       old_loopback_link_mode = epp->ep_loopback_link_mode;
+       epp->ep_loopback_type = loopback_type;
+       epp->ep_loopback_link_mode = link_mode;
+
+       if ((rc = epop->epo_reconfigure(enp)) != 0)
+               goto fail1;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       epp->ep_loopback_type = old_loopback_type;
+       epp->ep_loopback_link_mode = old_loopback_link_mode;
+
+       return (rc);
+}
+
+#endif /* EFSYS_OPT_LOOPBACK */
+
 #if EFSYS_OPT_MAC_STATS

        __checkReturn                   efx_rc_t
diff --git a/drivers/net/sfc/base/ef10_nic.c b/drivers/net/sfc/base/ef10_nic.c
index f28edd2..fec6a69 100644
--- a/drivers/net/sfc/base/ef10_nic.c
+++ b/drivers/net/sfc/base/ef10_nic.c
@@ -1377,10 +1377,19 @@ ef10_nic_probe(
                goto fail5;
 #endif

+#if EFSYS_OPT_LOOPBACK
+       if ((rc = efx_mcdi_get_loopback_modes(enp)) != 0)
+               goto fail6;
+#endif
+
        encp->enc_features = enp->en_features;

        return (0);

+#if EFSYS_OPT_LOOPBACK
+fail6:
+       EFSYS_PROBE(fail6);
+#endif
 #if EFSYS_OPT_MAC_STATS
 fail5:
        EFSYS_PROBE(fail5);
diff --git a/drivers/net/sfc/base/ef10_phy.c b/drivers/net/sfc/base/ef10_phy.c
index cc00250..81309f2 100644
--- a/drivers/net/sfc/base/ef10_phy.c
+++ b/drivers/net/sfc/base/ef10_phy.c
@@ -235,6 +235,30 @@ ef10_phy_get_link(
                            MCDI_OUT_DWORD(req, GET_LINK_OUT_FCNTL),
                            &elsp->els_link_mode, &elsp->els_fcntl);

+#if EFSYS_OPT_LOOPBACK
+       /* Assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespace agree */
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_NONE == EFX_LOOPBACK_OFF);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_DATA == EFX_LOOPBACK_DATA);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMAC == EFX_LOOPBACK_GMAC);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGMII == EFX_LOOPBACK_XGMII);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGXS == EFX_LOOPBACK_XGXS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI == EFX_LOOPBACK_XAUI);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII == EFX_LOOPBACK_GMII);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII == EFX_LOOPBACK_SGMII);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGBR == EFX_LOOPBACK_XGBR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI == EFX_LOOPBACK_XFI);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_FAR == EFX_LOOPBACK_XAUI_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII_FAR == EFX_LOOPBACK_GMII_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII_FAR == EFX_LOOPBACK_SGMII_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_FAR == EFX_LOOPBACK_XFI_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GPHY == EFX_LOOPBACK_GPHY);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PHYXS == EFX_LOOPBACK_PHY_XS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PCS == EFX_LOOPBACK_PCS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMAPMD == EFX_LOOPBACK_PMA_PMD);
+
+       elsp->els_loopback = MCDI_OUT_DWORD(req, GET_LINK_OUT_LOOPBACK_MODE);
+#endif /* EFSYS_OPT_LOOPBACK */
+
        elsp->els_mac_up = MCDI_OUT_DWORD(req, GET_LINK_OUT_MAC_FAULT) == 0;

        return (0);
@@ -289,8 +313,29 @@ ef10_phy_reconfigure(
        MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP,
            PHY_CAP_40000FDX, (cap_mask >> EFX_PHY_CAP_40000FDX) & 0x1);

+#if EFSYS_OPT_LOOPBACK
+       MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE,
+                   epp->ep_loopback_type);
+       switch (epp->ep_loopback_link_mode) {
+       case EFX_LINK_100FDX:
+               speed = 100;
+               break;
+       case EFX_LINK_1000FDX:
+               speed = 1000;
+               break;
+       case EFX_LINK_10000FDX:
+               speed = 10000;
+               break;
+       case EFX_LINK_40000FDX:
+               speed = 40000;
+               break;
+       default:
+               speed = 0;
+       }
+#else
        MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE, MC_CMD_LOOPBACK_NONE);
        speed = 0;
+#endif /* EFSYS_OPT_LOOPBACK */
        MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_SPEED, speed);

 #if EFSYS_OPT_PHY_FLAGS
diff --git a/drivers/net/sfc/base/efx.h b/drivers/net/sfc/base/efx.h
index f140a89..40a7e56 100644
--- a/drivers/net/sfc/base/efx.h
+++ b/drivers/net/sfc/base/efx.h
@@ -646,6 +646,77 @@ extern     __checkReturn   efx_rc_t
 efx_port_init(
        __in            efx_nic_t *enp);

+#if EFSYS_OPT_LOOPBACK
+
+typedef enum efx_loopback_type_e {
+       EFX_LOOPBACK_OFF = 0,
+       EFX_LOOPBACK_DATA = 1,
+       EFX_LOOPBACK_GMAC = 2,
+       EFX_LOOPBACK_XGMII = 3,
+       EFX_LOOPBACK_XGXS = 4,
+       EFX_LOOPBACK_XAUI = 5,
+       EFX_LOOPBACK_GMII = 6,
+       EFX_LOOPBACK_SGMII = 7,
+       EFX_LOOPBACK_XGBR = 8,
+       EFX_LOOPBACK_XFI = 9,
+       EFX_LOOPBACK_XAUI_FAR = 10,
+       EFX_LOOPBACK_GMII_FAR = 11,
+       EFX_LOOPBACK_SGMII_FAR = 12,
+       EFX_LOOPBACK_XFI_FAR = 13,
+       EFX_LOOPBACK_GPHY = 14,
+       EFX_LOOPBACK_PHY_XS = 15,
+       EFX_LOOPBACK_PCS = 16,
+       EFX_LOOPBACK_PMA_PMD = 17,
+       EFX_LOOPBACK_XPORT = 18,
+       EFX_LOOPBACK_XGMII_WS = 19,
+       EFX_LOOPBACK_XAUI_WS = 20,
+       EFX_LOOPBACK_XAUI_WS_FAR = 21,
+       EFX_LOOPBACK_XAUI_WS_NEAR = 22,
+       EFX_LOOPBACK_GMII_WS = 23,
+       EFX_LOOPBACK_XFI_WS = 24,
+       EFX_LOOPBACK_XFI_WS_FAR = 25,
+       EFX_LOOPBACK_PHYXS_WS = 26,
+       EFX_LOOPBACK_PMA_INT = 27,
+       EFX_LOOPBACK_SD_NEAR = 28,
+       EFX_LOOPBACK_SD_FAR = 29,
+       EFX_LOOPBACK_PMA_INT_WS = 30,
+       EFX_LOOPBACK_SD_FEP2_WS = 31,
+       EFX_LOOPBACK_SD_FEP1_5_WS = 32,
+       EFX_LOOPBACK_SD_FEP_WS = 33,
+       EFX_LOOPBACK_SD_FES_WS = 34,
+       EFX_LOOPBACK_NTYPES
+} efx_loopback_type_t;
+
+typedef enum efx_loopback_kind_e {
+       EFX_LOOPBACK_KIND_OFF = 0,
+       EFX_LOOPBACK_KIND_ALL,
+       EFX_LOOPBACK_KIND_MAC,
+       EFX_LOOPBACK_KIND_PHY,
+       EFX_LOOPBACK_NKINDS
+} efx_loopback_kind_t;
+
+extern                 void
+efx_loopback_mask(
+       __in    efx_loopback_kind_t loopback_kind,
+       __out   efx_qword_t *maskp);
+
+extern __checkReturn   efx_rc_t
+efx_port_loopback_set(
+       __in    efx_nic_t *enp,
+       __in    efx_link_mode_t link_mode,
+       __in    efx_loopback_type_t type);
+
+#if EFSYS_OPT_NAMES
+
+extern __checkReturn   const char *
+efx_loopback_type_name(
+       __in            efx_nic_t *enp,
+       __in            efx_loopback_type_t type);
+
+#endif /* EFSYS_OPT_NAMES */
+
+#endif /* EFSYS_OPT_LOOPBACK */
+
 extern __checkReturn   efx_rc_t
 efx_port_poll(
        __in            efx_nic_t *enp,
@@ -921,6 +992,9 @@ typedef struct efx_nic_cfg_s {
        uint32_t                enc_rx_prefix_size;
        uint32_t                enc_rx_buf_align_start;
        uint32_t                enc_rx_buf_align_end;
+#if EFSYS_OPT_LOOPBACK
+       efx_qword_t             enc_loopback_types[EFX_LINK_NMODES];
+#endif /* EFSYS_OPT_LOOPBACK */
 #if EFSYS_OPT_PHY_FLAGS
        uint32_t                enc_phy_flags_mask;
 #endif /* EFSYS_OPT_PHY_FLAGS */
diff --git a/drivers/net/sfc/base/efx_check.h b/drivers/net/sfc/base/efx_check.h
index ac248b3..22cf892 100644
--- a/drivers/net/sfc/base/efx_check.h
+++ b/drivers/net/sfc/base/efx_check.h
@@ -90,6 +90,13 @@
 # endif
 #endif /* EFSYS_OPT_HUNTINGTON */

+#if EFSYS_OPT_LOOPBACK
+/* Support hardware loopback modes */
+# if !(EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD)
+#  error "LOOPBACK requires SIENA or HUNTINGTON or MEDFORD"
+# endif
+#endif /* EFSYS_OPT_LOOPBACK */
+
 #ifdef EFSYS_OPT_MAC_FALCON_GMAC
 # error "MAC_FALCON_GMAC is obsolete and is not supported."
 #endif
diff --git a/drivers/net/sfc/base/efx_impl.h b/drivers/net/sfc/base/efx_impl.h
index e88e8a9..12a8a4f 100644
--- a/drivers/net/sfc/base/efx_impl.h
+++ b/drivers/net/sfc/base/efx_impl.h
@@ -186,6 +186,10 @@ typedef struct efx_mac_ops_s {
        efx_rc_t        (*emo_filter_default_rxq_set)(efx_nic_t *,
                                                      efx_rxq_t *, boolean_t);
        void            (*emo_filter_default_rxq_clear)(efx_nic_t *);
+#if EFSYS_OPT_LOOPBACK
+       efx_rc_t        (*emo_loopback_set)(efx_nic_t *, efx_link_mode_t,
+                                           efx_loopback_type_t);
+#endif /* EFSYS_OPT_LOOPBACK */
 #if EFSYS_OPT_MAC_STATS
        efx_rc_t        (*emo_stats_get_mask)(efx_nic_t *, uint32_t *, size_t);
        efx_rc_t        (*emo_stats_upload)(efx_nic_t *, efsys_mem_t *);
@@ -261,6 +265,10 @@ typedef struct efx_port_s {
        uint8_t                 ep_mulcst_addr_list[EFX_MAC_ADDR_LEN *
                                                    EFX_MAC_MULTICAST_LIST_MAX];
        uint32_t                ep_mulcst_addr_count;
+#if EFSYS_OPT_LOOPBACK
+       efx_loopback_type_t     ep_loopback_type;
+       efx_link_mode_t         ep_loopback_link_mode;
+#endif /* EFSYS_OPT_LOOPBACK */
 #if EFSYS_OPT_PHY_FLAGS
        uint32_t                ep_phy_flags;
 #endif /* EFSYS_OPT_PHY_FLAGS */
diff --git a/drivers/net/sfc/base/efx_mac.c b/drivers/net/sfc/base/efx_mac.c
index 840b7db..c1d81ef 100644
--- a/drivers/net/sfc/base/efx_mac.c
+++ b/drivers/net/sfc/base/efx_mac.c
@@ -50,6 +50,9 @@ static const efx_mac_ops_t    __efx_siena_mac_ops = {
        siena_mac_multicast_list_set,           /* emo_multicast_list_set */
        NULL,                                   /* emo_filter_set_default_rxq */
        NULL,                           /* emo_filter_default_rxq_clear */
+#if EFSYS_OPT_LOOPBACK
+       siena_mac_loopback_set,                 /* emo_loopback_set */
+#endif /* EFSYS_OPT_LOOPBACK */
 #if EFSYS_OPT_MAC_STATS
        siena_mac_stats_get_mask,               /* emo_stats_get_mask */
        efx_mcdi_mac_stats_upload,              /* emo_stats_upload */
@@ -71,6 +74,9 @@ static const efx_mac_ops_t    __efx_ef10_mac_ops = {
        ef10_mac_filter_default_rxq_set,        /* emo_filter_default_rxq_set */
        ef10_mac_filter_default_rxq_clear,
                                        /* emo_filter_default_rxq_clear */
+#if EFSYS_OPT_LOOPBACK
+       ef10_mac_loopback_set,                  /* emo_loopback_set */
+#endif /* EFSYS_OPT_LOOPBACK */
 #if EFSYS_OPT_MAC_STATS
        ef10_mac_stats_get_mask,                /* emo_stats_get_mask */
        efx_mcdi_mac_stats_upload,              /* emo_stats_upload */
diff --git a/drivers/net/sfc/base/efx_mcdi.h b/drivers/net/sfc/base/efx_mcdi.h
index 6e24313..814f3f4 100644
--- a/drivers/net/sfc/base/efx_mcdi.h
+++ b/drivers/net/sfc/base/efx_mcdi.h
@@ -220,6 +220,12 @@ efx_mcdi_mac_stats_periodic(
        __in            boolean_t events);


+#if EFSYS_OPT_LOOPBACK
+extern __checkReturn   efx_rc_t
+efx_mcdi_get_loopback_modes(
+       __in            efx_nic_t *enp);
+#endif /* EFSYS_OPT_LOOPBACK */
+
 extern __checkReturn   efx_rc_t
 efx_mcdi_phy_module_get_info(
        __in                    efx_nic_t *enp,
diff --git a/drivers/net/sfc/base/efx_nic.c b/drivers/net/sfc/base/efx_nic.c
index 047d4a0..d4b5d08 100644
--- a/drivers/net/sfc/base/efx_nic.c
+++ b/drivers/net/sfc/base/efx_nic.c
@@ -775,6 +775,194 @@ efx_nic_test_tables(

 #endif /* EFSYS_OPT_DIAG */

+#if EFSYS_OPT_LOOPBACK
+
+extern                 void
+efx_loopback_mask(
+       __in    efx_loopback_kind_t loopback_kind,
+       __out   efx_qword_t *maskp)
+{
+       efx_qword_t mask;
+
+       EFSYS_ASSERT3U(loopback_kind, <, EFX_LOOPBACK_NKINDS);
+       EFSYS_ASSERT(maskp != NULL);
+
+       /* Assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespace agree */
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_NONE == EFX_LOOPBACK_OFF);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_DATA == EFX_LOOPBACK_DATA);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMAC == EFX_LOOPBACK_GMAC);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGMII == EFX_LOOPBACK_XGMII);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGXS == EFX_LOOPBACK_XGXS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI == EFX_LOOPBACK_XAUI);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII == EFX_LOOPBACK_GMII);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII == EFX_LOOPBACK_SGMII);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGBR == EFX_LOOPBACK_XGBR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI == EFX_LOOPBACK_XFI);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_FAR == EFX_LOOPBACK_XAUI_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII_FAR == EFX_LOOPBACK_GMII_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII_FAR == EFX_LOOPBACK_SGMII_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_FAR == EFX_LOOPBACK_XFI_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GPHY == EFX_LOOPBACK_GPHY);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PHYXS == EFX_LOOPBACK_PHY_XS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PCS == EFX_LOOPBACK_PCS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMAPMD == EFX_LOOPBACK_PMA_PMD);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XPORT == EFX_LOOPBACK_XPORT);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGMII_WS == EFX_LOOPBACK_XGMII_WS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_WS == EFX_LOOPBACK_XAUI_WS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_WS_FAR ==
+           EFX_LOOPBACK_XAUI_WS_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_WS_NEAR ==
+           EFX_LOOPBACK_XAUI_WS_NEAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII_WS == EFX_LOOPBACK_GMII_WS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_WS == EFX_LOOPBACK_XFI_WS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_WS_FAR ==
+           EFX_LOOPBACK_XFI_WS_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PHYXS_WS == EFX_LOOPBACK_PHYXS_WS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMA_INT == EFX_LOOPBACK_PMA_INT);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_NEAR == EFX_LOOPBACK_SD_NEAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FAR == EFX_LOOPBACK_SD_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMA_INT_WS ==
+           EFX_LOOPBACK_PMA_INT_WS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FEP2_WS ==
+           EFX_LOOPBACK_SD_FEP2_WS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FEP1_5_WS ==
+           EFX_LOOPBACK_SD_FEP1_5_WS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FEP_WS == EFX_LOOPBACK_SD_FEP_WS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FES_WS == EFX_LOOPBACK_SD_FES_WS);
+
+       /* Build bitmask of possible loopback types */
+       EFX_ZERO_QWORD(mask);
+
+       if ((loopback_kind == EFX_LOOPBACK_KIND_OFF) ||
+           (loopback_kind == EFX_LOOPBACK_KIND_ALL)) {
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_OFF);
+       }
+
+       if ((loopback_kind == EFX_LOOPBACK_KIND_MAC) ||
+           (loopback_kind == EFX_LOOPBACK_KIND_ALL)) {
+               /*
+                * The "MAC" grouping has historically been used by drivers to
+                * mean loopbacks supported by on-chip hardware. Keep that
+                * meaning here, and include on-chip PHY layer loopbacks.
+                */
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_DATA);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMAC);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGMII);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGXS);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XAUI);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMII);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SGMII);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGBR);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XFI);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XAUI_FAR);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMII_FAR);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SGMII_FAR);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XFI_FAR);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PMA_INT);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SD_NEAR);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SD_FAR);
+       }
+
+       if ((loopback_kind == EFX_LOOPBACK_KIND_PHY) ||
+           (loopback_kind == EFX_LOOPBACK_KIND_ALL)) {
+               /*
+                * The "PHY" grouping has historically been used by drivers to
+                * mean loopbacks supported by off-chip hardware. Keep that
+                * meaning here.
+                */
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GPHY);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PHY_XS);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PCS);
+               EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PMA_PMD);
+       }
+
+       *maskp = mask;
+}
+
+       __checkReturn   efx_rc_t
+efx_mcdi_get_loopback_modes(
+       __in            efx_nic_t *enp)
+{
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       efx_mcdi_req_t req;
+       uint8_t payload[MAX(MC_CMD_GET_LOOPBACK_MODES_IN_LEN,
+                           MC_CMD_GET_LOOPBACK_MODES_OUT_LEN)];
+       efx_qword_t mask;
+       efx_qword_t modes;
+       efx_rc_t rc;
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_GET_LOOPBACK_MODES;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_GET_LOOPBACK_MODES_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_GET_LOOPBACK_MODES_OUT_LEN;
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       if (req.emr_out_length_used <
+           MC_CMD_GET_LOOPBACK_MODES_OUT_SUGGESTED_OFST +
+           MC_CMD_GET_LOOPBACK_MODES_OUT_SUGGESTED_LEN) {
+               rc = EMSGSIZE;
+               goto fail2;
+       }
+
+       /*
+        * We assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespaces agree
+        * in efx_loopback_mask() and in siena_phy.c:siena_phy_get_link().
+        */
+       efx_loopback_mask(EFX_LOOPBACK_KIND_ALL, &mask);
+
+       EFX_AND_QWORD(mask,
+           *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_SUGGESTED));
+
+       modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_100M);
+       EFX_AND_QWORD(modes, mask);
+       encp->enc_loopback_types[EFX_LINK_100FDX] = modes;
+
+       modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_1G);
+       EFX_AND_QWORD(modes, mask);
+       encp->enc_loopback_types[EFX_LINK_1000FDX] = modes;
+
+       modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_10G);
+       EFX_AND_QWORD(modes, mask);
+       encp->enc_loopback_types[EFX_LINK_10000FDX] = modes;
+
+       if (req.emr_out_length_used >=
+           MC_CMD_GET_LOOPBACK_MODES_OUT_40G_OFST +
+           MC_CMD_GET_LOOPBACK_MODES_OUT_40G_LEN) {
+               /* Response includes 40G loopback modes */
+               modes =
+                   *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_40G);
+               EFX_AND_QWORD(modes, mask);
+               encp->enc_loopback_types[EFX_LINK_40000FDX] = modes;
+       }
+
+       EFX_ZERO_QWORD(modes);
+       EFX_SET_QWORD_BIT(modes, EFX_LOOPBACK_OFF);
+       EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_100FDX]);
+       EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_1000FDX]);
+       EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_10000FDX]);
+       EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_40000FDX]);
+       encp->enc_loopback_types[EFX_LINK_UNKNOWN] = modes;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+#endif /* EFSYS_OPT_LOOPBACK */
+
        __checkReturn   efx_rc_t
 efx_nic_calculate_pcie_link_bandwidth(
        __in            uint32_t pcie_link_width,
diff --git a/drivers/net/sfc/base/efx_port.c b/drivers/net/sfc/base/efx_port.c
index 291a8e9..518c2a2 100644
--- a/drivers/net/sfc/base/efx_port.c
+++ b/drivers/net/sfc/base/efx_port.c
@@ -125,6 +125,107 @@ efx_port_poll(
        return (rc);
 }

+#if EFSYS_OPT_LOOPBACK
+
+       __checkReturn   efx_rc_t
+efx_port_loopback_set(
+       __in            efx_nic_t *enp,
+       __in            efx_link_mode_t link_mode,
+       __in            efx_loopback_type_t loopback_type)
+{
+       efx_port_t *epp = &(enp->en_port);
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       const efx_mac_ops_t *emop = epp->ep_emop;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PORT);
+       EFSYS_ASSERT(emop != NULL);
+
+       EFSYS_ASSERT(link_mode < EFX_LINK_NMODES);
+
+       if (EFX_TEST_QWORD_BIT(encp->enc_loopback_types[link_mode],
+               loopback_type) == 0) {
+               rc = ENOTSUP;
+               goto fail1;
+       }
+
+       if (epp->ep_loopback_type == loopback_type &&
+           epp->ep_loopback_link_mode == link_mode)
+               return (0);
+
+       if ((rc = emop->emo_loopback_set(enp, link_mode, loopback_type)) != 0)
+               goto fail2;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+#if EFSYS_OPT_NAMES
+
+static const char * const __efx_loopback_type_name[] = {
+       "OFF",
+       "DATA",
+       "GMAC",
+       "XGMII",
+       "XGXS",
+       "XAUI",
+       "GMII",
+       "SGMII",
+       "XGBR",
+       "XFI",
+       "XAUI_FAR",
+       "GMII_FAR",
+       "SGMII_FAR",
+       "XFI_FAR",
+       "GPHY",
+       "PHY_XS",
+       "PCS",
+       "PMA_PMD",
+       "XPORT",
+       "XGMII_WS",
+       "XAUI_WS",
+       "XAUI_WS_FAR",
+       "XAUI_WS_NEAR",
+       "GMII_WS",
+       "XFI_WS",
+       "XFI_WS_FAR",
+       "PHYXS_WS",
+       "PMA_INT",
+       "SD_NEAR",
+       "SD_FAR",
+       "PMA_INT_WS",
+       "SD_FEP2_WS",
+       "SD_FEP1_5_WS",
+       "SD_FEP_WS",
+       "SD_FES_WS",
+};
+
+       __checkReturn   const char *
+efx_loopback_type_name(
+       __in            efx_nic_t *enp,
+       __in            efx_loopback_type_t type)
+{
+       EFX_STATIC_ASSERT(EFX_ARRAY_SIZE(__efx_loopback_type_name) ==
+           EFX_LOOPBACK_NTYPES);
+
+       _NOTE(ARGUNUSED(enp))
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(type, <, EFX_LOOPBACK_NTYPES);
+
+       return (__efx_loopback_type_name[type]);
+}
+
+#endif /* EFSYS_OPT_NAMES */
+
+#endif /* EFSYS_OPT_LOOPBACK */
+
                        void
 efx_port_fini(
        __in            efx_nic_t *enp)
diff --git a/drivers/net/sfc/base/siena_impl.h 
b/drivers/net/sfc/base/siena_impl.h
index 98fd2c5..da03098 100644
--- a/drivers/net/sfc/base/siena_impl.h
+++ b/drivers/net/sfc/base/siena_impl.h
@@ -138,6 +138,9 @@ typedef struct siena_link_state_s {
        uint32_t                sls_lp_cap_mask;
        unsigned int            sls_fcntl;
        efx_link_mode_t         sls_link_mode;
+#if EFSYS_OPT_LOOPBACK
+       efx_loopback_type_t     sls_loopback;
+#endif
        boolean_t               sls_mac_up;
 } siena_link_state_t;

@@ -232,6 +235,16 @@ siena_mac_pdu_get(
        __in    efx_nic_t *enp,
        __out   size_t *pdu);

+#if EFSYS_OPT_LOOPBACK
+
+extern __checkReturn   efx_rc_t
+siena_mac_loopback_set(
+       __in            efx_nic_t *enp,
+       __in            efx_link_mode_t link_mode,
+       __in            efx_loopback_type_t loopback_type);
+
+#endif /* EFSYS_OPT_LOOPBACK */
+
 #if EFSYS_OPT_MAC_STATS

 extern __checkReturn                   efx_rc_t
diff --git a/drivers/net/sfc/base/siena_mac.c b/drivers/net/sfc/base/siena_mac.c
index dbe9c6f..29bbff8 100644
--- a/drivers/net/sfc/base/siena_mac.c
+++ b/drivers/net/sfc/base/siena_mac.c
@@ -194,6 +194,42 @@ siena_mac_reconfigure(
        return (rc);
 }

+#if EFSYS_OPT_LOOPBACK
+
+       __checkReturn   efx_rc_t
+siena_mac_loopback_set(
+       __in            efx_nic_t *enp,
+       __in            efx_link_mode_t link_mode,
+       __in            efx_loopback_type_t loopback_type)
+{
+       efx_port_t *epp = &(enp->en_port);
+       const efx_phy_ops_t *epop = epp->ep_epop;
+       efx_loopback_type_t old_loopback_type;
+       efx_link_mode_t old_loopback_link_mode;
+       efx_rc_t rc;
+
+       /* The PHY object handles this on Siena */
+       old_loopback_type = epp->ep_loopback_type;
+       old_loopback_link_mode = epp->ep_loopback_link_mode;
+       epp->ep_loopback_type = loopback_type;
+       epp->ep_loopback_link_mode = link_mode;
+
+       if ((rc = epop->epo_reconfigure(enp)) != 0)
+               goto fail1;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       epp->ep_loopback_type = old_loopback_type;
+       epp->ep_loopback_link_mode = old_loopback_link_mode;
+
+       return (rc);
+}
+
+#endif /* EFSYS_OPT_LOOPBACK */
+
 #if EFSYS_OPT_MAC_STATS

        __checkReturn                   efx_rc_t
diff --git a/drivers/net/sfc/base/siena_nic.c b/drivers/net/sfc/base/siena_nic.c
index c77393a..d28d1d2 100644
--- a/drivers/net/sfc/base/siena_nic.c
+++ b/drivers/net/sfc/base/siena_nic.c
@@ -216,10 +216,19 @@ siena_nic_probe(
                goto fail10;
 #endif

+#if EFSYS_OPT_LOOPBACK
+       if ((rc = efx_mcdi_get_loopback_modes(enp)) != 0)
+               goto fail11;
+#endif
+
        encp->enc_features = enp->en_features;

        return (0);

+#if EFSYS_OPT_LOOPBACK
+fail11:
+       EFSYS_PROBE(fail11);
+#endif
 #if EFSYS_OPT_MAC_STATS
 fail10:
        EFSYS_PROBE(fail10);
diff --git a/drivers/net/sfc/base/siena_phy.c b/drivers/net/sfc/base/siena_phy.c
index 9aeef23..b90ccab 100644
--- a/drivers/net/sfc/base/siena_phy.c
+++ b/drivers/net/sfc/base/siena_phy.c
@@ -226,6 +226,30 @@ siena_phy_get_link(
                            MCDI_OUT_DWORD(req, GET_LINK_OUT_FCNTL),
                            &slsp->sls_link_mode, &slsp->sls_fcntl);

+#if EFSYS_OPT_LOOPBACK
+       /* Assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespace agree */
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_NONE == EFX_LOOPBACK_OFF);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_DATA == EFX_LOOPBACK_DATA);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMAC == EFX_LOOPBACK_GMAC);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGMII == EFX_LOOPBACK_XGMII);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGXS == EFX_LOOPBACK_XGXS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI == EFX_LOOPBACK_XAUI);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII == EFX_LOOPBACK_GMII);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII == EFX_LOOPBACK_SGMII);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGBR == EFX_LOOPBACK_XGBR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI == EFX_LOOPBACK_XFI);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_FAR == EFX_LOOPBACK_XAUI_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII_FAR == EFX_LOOPBACK_GMII_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII_FAR == EFX_LOOPBACK_SGMII_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_FAR == EFX_LOOPBACK_XFI_FAR);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GPHY == EFX_LOOPBACK_GPHY);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PHYXS == EFX_LOOPBACK_PHY_XS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PCS == EFX_LOOPBACK_PCS);
+       EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMAPMD == EFX_LOOPBACK_PMA_PMD);
+
+       slsp->sls_loopback = MCDI_OUT_DWORD(req, GET_LINK_OUT_LOOPBACK_MODE);
+#endif /* EFSYS_OPT_LOOPBACK */
+
        slsp->sls_mac_up = MCDI_OUT_DWORD(req, GET_LINK_OUT_MAC_FAULT) == 0;

        return (0);
@@ -273,8 +297,26 @@ siena_phy_reconfigure(
                PHY_CAP_ASYM, (cap_mask >> EFX_PHY_CAP_ASYM) & 0x1,
                PHY_CAP_AN, (cap_mask >> EFX_PHY_CAP_AN) & 0x1);

+#if EFSYS_OPT_LOOPBACK
+       MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE,
+                   epp->ep_loopback_type);
+       switch (epp->ep_loopback_link_mode) {
+       case EFX_LINK_100FDX:
+               speed = 100;
+               break;
+       case EFX_LINK_1000FDX:
+               speed = 1000;
+               break;
+       case EFX_LINK_10000FDX:
+               speed = 10000;
+               break;
+       default:
+               speed = 0;
+       }
+#else
        MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE, MC_CMD_LOOPBACK_NONE);
        speed = 0;
+#endif /* EFSYS_OPT_LOOPBACK */
        MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_SPEED, speed);

 #if EFSYS_OPT_PHY_FLAGS
-- 
2.5.5

Reply via email to