net/sfc/base: import built-in selftest
authorAndrew Rybchenko <arybchenko@solarflare.com>
Tue, 29 Nov 2016 16:18:45 +0000 (16:18 +0000)
committerFerruh Yigit <ferruh.yigit@intel.com>
Tue, 17 Jan 2017 18:39:26 +0000 (19:39 +0100)
EFSYS_OPT_BIST should be enabled to use it.

From Solarflare Communications Inc.

Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
drivers/net/sfc/base/ef10_impl.h
drivers/net/sfc/base/ef10_phy.c
drivers/net/sfc/base/efx.h
drivers/net/sfc/base/efx_check.h
drivers/net/sfc/base/efx_impl.h
drivers/net/sfc/base/efx_mcdi.c
drivers/net/sfc/base/efx_mcdi.h
drivers/net/sfc/base/efx_phy.c
drivers/net/sfc/base/siena_impl.h
drivers/net/sfc/base/siena_phy.c

index 6afe0bc..1cc6a72 100644 (file)
@@ -345,6 +345,35 @@ ef10_phy_oui_get(
        __in            efx_nic_t *enp,
        __out           uint32_t *ouip);
 
+#if EFSYS_OPT_BIST
+
+extern __checkReturn           efx_rc_t
+ef10_bist_enable_offline(
+       __in                    efx_nic_t *enp);
+
+extern __checkReturn           efx_rc_t
+ef10_bist_start(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type);
+
+extern __checkReturn           efx_rc_t
+ef10_bist_poll(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type,
+       __out                   efx_bist_result_t *resultp,
+       __out_opt __drv_when(count > 0, __notnull)
+       uint32_t        *value_maskp,
+       __out_ecount_opt(count) __drv_when(count > 0, __notnull)
+       unsigned long   *valuesp,
+       __in                    size_t count);
+
+extern                         void
+ef10_bist_stop(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type);
+
+#endif /* EFSYS_OPT_BIST */
+
 /* TX */
 
 extern __checkReturn   efx_rc_t
index 36e2603..9e1b9c2 100644 (file)
@@ -390,4 +390,157 @@ ef10_phy_oui_get(
        return (ENOTSUP);
 }
 
+#if EFSYS_OPT_BIST
+
+       __checkReturn           efx_rc_t
+ef10_bist_enable_offline(
+       __in                    efx_nic_t *enp)
+{
+       efx_rc_t rc;
+
+       if ((rc = efx_mcdi_bist_enable_offline(enp)) != 0)
+               goto fail1;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn           efx_rc_t
+ef10_bist_start(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type)
+{
+       efx_rc_t rc;
+
+       if ((rc = efx_mcdi_bist_start(enp, type)) != 0)
+               goto fail1;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn           efx_rc_t
+ef10_bist_poll(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type,
+       __out                   efx_bist_result_t *resultp,
+       __out_opt __drv_when(count > 0, __notnull)
+       uint32_t *value_maskp,
+       __out_ecount_opt(count) __drv_when(count > 0, __notnull)
+       unsigned long *valuesp,
+       __in                    size_t count)
+{
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       efx_mcdi_req_t req;
+       uint8_t payload[MAX(MC_CMD_POLL_BIST_IN_LEN,
+                           MCDI_CTL_SDU_LEN_MAX)];
+       uint32_t value_mask = 0;
+       uint32_t result;
+       efx_rc_t rc;
+
+       _NOTE(ARGUNUSED(type))
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_POLL_BIST;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_POLL_BIST_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MCDI_CTL_SDU_LEN_MAX;
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       if (req.emr_out_length_used < MC_CMD_POLL_BIST_OUT_RESULT_OFST + 4) {
+               rc = EMSGSIZE;
+               goto fail2;
+       }
+
+       if (count > 0)
+               (void) memset(valuesp, '\0', count * sizeof (unsigned long));
+
+       result = MCDI_OUT_DWORD(req, POLL_BIST_OUT_RESULT);
+
+       if (result == MC_CMD_POLL_BIST_FAILED &&
+           req.emr_out_length >= MC_CMD_POLL_BIST_OUT_MEM_LEN &&
+           count > EFX_BIST_MEM_ECC_FATAL) {
+               if (valuesp != NULL) {
+                       valuesp[EFX_BIST_MEM_TEST] =
+                           MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_TEST);
+                       valuesp[EFX_BIST_MEM_ADDR] =
+                           MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ADDR);
+                       valuesp[EFX_BIST_MEM_BUS] =
+                           MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_BUS);
+                       valuesp[EFX_BIST_MEM_EXPECT] =
+                           MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_EXPECT);
+                       valuesp[EFX_BIST_MEM_ACTUAL] =
+                           MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ACTUAL);
+                       valuesp[EFX_BIST_MEM_ECC] =
+                           MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ECC);
+                       valuesp[EFX_BIST_MEM_ECC_PARITY] =
+                           MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ECC_PARITY);
+                       valuesp[EFX_BIST_MEM_ECC_FATAL] =
+                           MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ECC_FATAL);
+               }
+               value_mask |= (1 << EFX_BIST_MEM_TEST) |
+                   (1 << EFX_BIST_MEM_ADDR) |
+                   (1 << EFX_BIST_MEM_BUS) |
+                   (1 << EFX_BIST_MEM_EXPECT) |
+                   (1 << EFX_BIST_MEM_ACTUAL) |
+                   (1 << EFX_BIST_MEM_ECC) |
+                   (1 << EFX_BIST_MEM_ECC_PARITY) |
+                   (1 << EFX_BIST_MEM_ECC_FATAL);
+       } else if (result == MC_CMD_POLL_BIST_FAILED &&
+           encp->enc_phy_type == EFX_PHY_XFI_FARMI &&
+           req.emr_out_length >= MC_CMD_POLL_BIST_OUT_MRSFP_LEN &&
+           count > EFX_BIST_FAULT_CODE) {
+               if (valuesp != NULL)
+                       valuesp[EFX_BIST_FAULT_CODE] =
+                           MCDI_OUT_DWORD(req, POLL_BIST_OUT_MRSFP_TEST);
+               value_mask |= 1 << EFX_BIST_FAULT_CODE;
+       }
+
+       if (value_maskp != NULL)
+               *value_maskp = value_mask;
+
+       EFSYS_ASSERT(resultp != NULL);
+       if (result == MC_CMD_POLL_BIST_RUNNING)
+               *resultp = EFX_BIST_RESULT_RUNNING;
+       else if (result == MC_CMD_POLL_BIST_PASSED)
+               *resultp = EFX_BIST_RESULT_PASSED;
+       else
+               *resultp = EFX_BIST_RESULT_FAILED;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+                       void
+ef10_bist_stop(
+       __in            efx_nic_t *enp,
+       __in            efx_bist_type_t type)
+{
+       /* There is no way to stop BIST on EF10. */
+       _NOTE(ARGUNUSED(enp, type))
+}
+
+#endif /* EFSYS_OPT_BIST */
+
 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
index c7108a9..84d60b6 100644 (file)
@@ -549,6 +549,83 @@ efx_phy_module_get_info(
        __out_bcount(len)               uint8_t *data);
 
 
+#if EFSYS_OPT_BIST
+
+typedef enum efx_bist_type_e {
+       EFX_BIST_TYPE_UNKNOWN,
+       EFX_BIST_TYPE_PHY_NORMAL,
+       EFX_BIST_TYPE_PHY_CABLE_SHORT,
+       EFX_BIST_TYPE_PHY_CABLE_LONG,
+       EFX_BIST_TYPE_MC_MEM,   /* Test the MC DMEM and IMEM */
+       EFX_BIST_TYPE_SAT_MEM,  /* Test the DMEM and IMEM of satellite cpus*/
+       EFX_BIST_TYPE_REG,      /* Test the register memories */
+       EFX_BIST_TYPE_NTYPES,
+} efx_bist_type_t;
+
+typedef enum efx_bist_result_e {
+       EFX_BIST_RESULT_UNKNOWN,
+       EFX_BIST_RESULT_RUNNING,
+       EFX_BIST_RESULT_PASSED,
+       EFX_BIST_RESULT_FAILED,
+} efx_bist_result_t;
+
+typedef enum efx_phy_cable_status_e {
+       EFX_PHY_CABLE_STATUS_OK,
+       EFX_PHY_CABLE_STATUS_INVALID,
+       EFX_PHY_CABLE_STATUS_OPEN,
+       EFX_PHY_CABLE_STATUS_INTRAPAIRSHORT,
+       EFX_PHY_CABLE_STATUS_INTERPAIRSHORT,
+       EFX_PHY_CABLE_STATUS_BUSY,
+} efx_phy_cable_status_t;
+
+typedef enum efx_bist_value_e {
+       EFX_BIST_PHY_CABLE_LENGTH_A,
+       EFX_BIST_PHY_CABLE_LENGTH_B,
+       EFX_BIST_PHY_CABLE_LENGTH_C,
+       EFX_BIST_PHY_CABLE_LENGTH_D,
+       EFX_BIST_PHY_CABLE_STATUS_A,
+       EFX_BIST_PHY_CABLE_STATUS_B,
+       EFX_BIST_PHY_CABLE_STATUS_C,
+       EFX_BIST_PHY_CABLE_STATUS_D,
+       EFX_BIST_FAULT_CODE,
+       /* Memory BIST specific values. These match to the MC_CMD_BIST_POLL
+        * response. */
+       EFX_BIST_MEM_TEST,
+       EFX_BIST_MEM_ADDR,
+       EFX_BIST_MEM_BUS,
+       EFX_BIST_MEM_EXPECT,
+       EFX_BIST_MEM_ACTUAL,
+       EFX_BIST_MEM_ECC,
+       EFX_BIST_MEM_ECC_PARITY,
+       EFX_BIST_MEM_ECC_FATAL,
+       EFX_BIST_NVALUES,
+} efx_bist_value_t;
+
+extern __checkReturn           efx_rc_t
+efx_bist_enable_offline(
+       __in                    efx_nic_t *enp);
+
+extern __checkReturn           efx_rc_t
+efx_bist_start(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type);
+
+extern __checkReturn           efx_rc_t
+efx_bist_poll(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type,
+       __out                   efx_bist_result_t *resultp,
+       __out_opt               uint32_t *value_maskp,
+       __out_ecount_opt(count) unsigned long *valuesp,
+       __in                    size_t count);
+
+extern                         void
+efx_bist_stop(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type);
+
+#endif /* EFSYS_OPT_BIST */
+
 #define        EFX_FEATURE_IPV6                0x00000001
 #define        EFX_FEATURE_LFSR_HASH_INSERT    0x00000002
 #define        EFX_FEATURE_LINK_EVENTS         0x00000004
@@ -594,6 +671,9 @@ typedef struct efx_nic_cfg_s {
 #if EFSYS_OPT_MCDI
        uint8_t                 enc_mcdi_mdio_channel;
 #endif /* EFSYS_OPT_MCDI */
+#if EFSYS_OPT_BIST
+       uint32_t                enc_bist_mask;
+#endif /* EFSYS_OPT_BIST */
 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
        uint32_t                enc_pf;
        uint32_t                enc_vf;
index feaccd0..c78c5b6 100644 (file)
 #  error "MCAST_FILTER_LIST is obsolete and is not supported"
 #endif
 
+#if EFSYS_OPT_BIST
+/* Support BIST */
+# if !(EFSYS_OPT_SIENA || EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD)
+#  error "BIST requires SIENA or HUNTINGTON or MEDFORD"
+# endif
+#endif /* EFSYS_OPT_BIST */
+
 #if EFSYS_OPT_ALLOW_UNCONFIGURED_NIC
 /* Support adapters with missing static config (for factory use only) */
 # if !EFSYS_OPT_MEDFORD
index a7c6b29..a6853b3 100644 (file)
@@ -175,6 +175,14 @@ typedef struct efx_phy_ops_s {
        efx_rc_t        (*epo_reconfigure)(efx_nic_t *);
        efx_rc_t        (*epo_verify)(efx_nic_t *);
        efx_rc_t        (*epo_oui_get)(efx_nic_t *, uint32_t *);
+#if EFSYS_OPT_BIST
+       efx_rc_t        (*epo_bist_enable_offline)(efx_nic_t *);
+       efx_rc_t        (*epo_bist_start)(efx_nic_t *, efx_bist_type_t);
+       efx_rc_t        (*epo_bist_poll)(efx_nic_t *, efx_bist_type_t,
+                                        efx_bist_result_t *, uint32_t *,
+                                        unsigned long *, size_t);
+       void            (*epo_bist_stop)(efx_nic_t *, efx_bist_type_t);
+#endif /* EFSYS_OPT_BIST */
 } efx_phy_ops_t;
 
 #if EFSYS_OPT_FILTER
@@ -230,6 +238,9 @@ typedef struct efx_port_s {
        uint32_t                ep_phy_cap_mask;
        boolean_t               ep_mac_drain;
        boolean_t               ep_mac_stats_pending;
+#if EFSYS_OPT_BIST
+       efx_bist_type_t         ep_current_bist;
+#endif
        const efx_mac_ops_t     *ep_emop;
        const efx_phy_ops_t     *ep_epop;
 } efx_port_t;
index 8d91812..b14cba1 100644 (file)
@@ -1432,6 +1432,19 @@ efx_mcdi_get_phy_cfg(
        encp->enc_mcdi_mdio_channel =
                (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_CHANNEL);
 
+#if EFSYS_OPT_BIST
+       encp->enc_bist_mask = 0;
+       if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
+           GET_PHY_CFG_OUT_BIST_CABLE_SHORT))
+               encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_SHORT);
+       if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
+           GET_PHY_CFG_OUT_BIST_CABLE_LONG))
+               encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_LONG);
+       if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
+           GET_PHY_CFG_OUT_BIST))
+               encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_NORMAL);
+#endif  /* EFSYS_OPT_BIST */
+
        return (0);
 
 fail2:
@@ -1542,6 +1555,108 @@ fail1:
        return (rc);
 }
 
+#if EFSYS_OPT_BIST
+
+#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
+/*
+ * Enter bist offline mode. This is a fw mode which puts the NIC into a state
+ * where memory BIST tests can be run and not much else can interfere or happen.
+ * A reboot is required to exit this mode.
+ */
+       __checkReturn           efx_rc_t
+efx_mcdi_bist_enable_offline(
+       __in                    efx_nic_t *enp)
+{
+       efx_mcdi_req_t req;
+       efx_rc_t rc;
+
+       EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_IN_LEN == 0);
+       EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_OUT_LEN == 0);
+
+       req.emr_cmd = MC_CMD_ENABLE_OFFLINE_BIST;
+       req.emr_in_buf = NULL;
+       req.emr_in_length = 0;
+       req.emr_out_buf = NULL;
+       req.emr_out_length = 0;
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
+
+       __checkReturn           efx_rc_t
+efx_mcdi_bist_start(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type)
+{
+       efx_mcdi_req_t req;
+       uint8_t payload[MAX(MC_CMD_START_BIST_IN_LEN,
+                           MC_CMD_START_BIST_OUT_LEN)];
+       efx_rc_t rc;
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_START_BIST;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_START_BIST_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_START_BIST_OUT_LEN;
+
+       switch (type) {
+       case EFX_BIST_TYPE_PHY_NORMAL:
+               MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, MC_CMD_PHY_BIST);
+               break;
+       case EFX_BIST_TYPE_PHY_CABLE_SHORT:
+               MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
+                   MC_CMD_PHY_BIST_CABLE_SHORT);
+               break;
+       case EFX_BIST_TYPE_PHY_CABLE_LONG:
+               MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
+                   MC_CMD_PHY_BIST_CABLE_LONG);
+               break;
+       case EFX_BIST_TYPE_MC_MEM:
+               MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
+                   MC_CMD_MC_MEM_BIST);
+               break;
+       case EFX_BIST_TYPE_SAT_MEM:
+               MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
+                   MC_CMD_PORT_MEM_BIST);
+               break;
+       case EFX_BIST_TYPE_REG:
+               MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
+                   MC_CMD_REG_BIST);
+               break;
+       default:
+               EFSYS_ASSERT(0);
+       }
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+#endif /* EFSYS_OPT_BIST */
+
 
 /* Enable logging of some events (e.g. link state changes) */
        __checkReturn   efx_rc_t
index a62e921..6e24313 100644 (file)
@@ -180,6 +180,18 @@ efx_mcdi_mac_spoofing_supported(
        __out                   boolean_t *supportedp);
 
 
+#if EFSYS_OPT_BIST
+#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
+extern __checkReturn           efx_rc_t
+efx_mcdi_bist_enable_offline(
+       __in                    efx_nic_t *enp);
+#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
+extern __checkReturn           efx_rc_t
+efx_mcdi_bist_start(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type);
+#endif /* EFSYS_OPT_BIST */
+
 extern __checkReturn           efx_rc_t
 efx_mcdi_get_resource_limits(
        __in                    efx_nic_t *enp,
index e7e915e..f07f127 100644 (file)
@@ -39,6 +39,12 @@ static const efx_phy_ops_t   __efx_phy_siena_ops = {
        siena_phy_reconfigure,          /* epo_reconfigure */
        siena_phy_verify,               /* epo_verify */
        siena_phy_oui_get,              /* epo_oui_get */
+#if EFSYS_OPT_BIST
+       NULL,                           /* epo_bist_enable_offline */
+       siena_phy_bist_start,           /* epo_bist_start */
+       siena_phy_bist_poll,            /* epo_bist_poll */
+       siena_phy_bist_stop,            /* epo_bist_stop */
+#endif /* EFSYS_OPT_BIST */
 };
 #endif /* EFSYS_OPT_SIENA */
 
@@ -49,6 +55,12 @@ static const efx_phy_ops_t   __efx_phy_ef10_ops = {
        ef10_phy_reconfigure,           /* epo_reconfigure */
        ef10_phy_verify,                /* epo_verify */
        ef10_phy_oui_get,               /* epo_oui_get */
+#if EFSYS_OPT_BIST
+       ef10_bist_enable_offline,       /* epo_bist_enable_offline */
+       ef10_bist_start,                /* epo_bist_start */
+       ef10_bist_poll,                 /* epo_bist_poll */
+       ef10_bist_stop,                 /* epo_bist_stop */
+#endif /* EFSYS_OPT_BIST */
 };
 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
 
@@ -266,6 +278,134 @@ fail1:
 }
 
 
+#if EFSYS_OPT_BIST
+
+       __checkReturn           efx_rc_t
+efx_bist_enable_offline(
+       __in                    efx_nic_t *enp)
+{
+       efx_port_t *epp = &(enp->en_port);
+       const efx_phy_ops_t *epop = epp->ep_epop;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+
+       if (epop->epo_bist_enable_offline == NULL) {
+               rc = ENOTSUP;
+               goto fail1;
+       }
+
+       if ((rc = epop->epo_bist_enable_offline(enp)) != 0)
+               goto fail2;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+
+}
+
+       __checkReturn           efx_rc_t
+efx_bist_start(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type)
+{
+       efx_port_t *epp = &(enp->en_port);
+       const efx_phy_ops_t *epop = epp->ep_epop;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+
+       EFSYS_ASSERT3U(type, !=, EFX_BIST_TYPE_UNKNOWN);
+       EFSYS_ASSERT3U(type, <, EFX_BIST_TYPE_NTYPES);
+       EFSYS_ASSERT3U(epp->ep_current_bist, ==, EFX_BIST_TYPE_UNKNOWN);
+
+       if (epop->epo_bist_start == NULL) {
+               rc = ENOTSUP;
+               goto fail1;
+       }
+
+       if ((rc = epop->epo_bist_start(enp, type)) != 0)
+               goto fail2;
+
+       epp->ep_current_bist = type;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn           efx_rc_t
+efx_bist_poll(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type,
+       __out                   efx_bist_result_t *resultp,
+       __out_opt               uint32_t *value_maskp,
+       __out_ecount_opt(count) unsigned long *valuesp,
+       __in                    size_t count)
+{
+       efx_port_t *epp = &(enp->en_port);
+       const efx_phy_ops_t *epop = epp->ep_epop;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+
+       EFSYS_ASSERT3U(type, !=, EFX_BIST_TYPE_UNKNOWN);
+       EFSYS_ASSERT3U(type, <, EFX_BIST_TYPE_NTYPES);
+       EFSYS_ASSERT3U(epp->ep_current_bist, ==, type);
+
+       EFSYS_ASSERT(epop->epo_bist_poll != NULL);
+       if (epop->epo_bist_poll == NULL) {
+               rc = ENOTSUP;
+               goto fail1;
+       }
+
+       if ((rc = epop->epo_bist_poll(enp, type, resultp, value_maskp,
+           valuesp, count)) != 0)
+               goto fail2;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+                       void
+efx_bist_stop(
+       __in            efx_nic_t *enp,
+       __in            efx_bist_type_t type)
+{
+       efx_port_t *epp = &(enp->en_port);
+       const efx_phy_ops_t *epop = epp->ep_epop;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+
+       EFSYS_ASSERT3U(type, !=, EFX_BIST_TYPE_UNKNOWN);
+       EFSYS_ASSERT3U(type, <, EFX_BIST_TYPE_NTYPES);
+       EFSYS_ASSERT3U(epp->ep_current_bist, ==, type);
+
+       EFSYS_ASSERT(epop->epo_bist_stop != NULL);
+
+       if (epop->epo_bist_stop != NULL)
+               epop->epo_bist_stop(enp, type);
+
+       epp->ep_current_bist = EFX_BIST_TYPE_UNKNOWN;
+}
+
+#endif /* EFSYS_OPT_BIST */
                        void
 efx_phy_unprobe(
        __in    efx_nic_t *enp)
index c316867..bdaa4a3 100644 (file)
@@ -170,6 +170,31 @@ siena_phy_oui_get(
        __in            efx_nic_t *enp,
        __out           uint32_t *ouip);
 
+#if EFSYS_OPT_BIST
+
+extern __checkReturn           efx_rc_t
+siena_phy_bist_start(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type);
+
+extern __checkReturn           efx_rc_t
+siena_phy_bist_poll(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type,
+       __out                   efx_bist_result_t *resultp,
+       __out_opt __drv_when(count > 0, __notnull)
+       uint32_t        *value_maskp,
+       __out_ecount_opt(count) __drv_when(count > 0, __notnull)
+       unsigned long   *valuesp,
+       __in                    size_t count);
+
+extern                         void
+siena_phy_bist_stop(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type);
+
+#endif /* EFSYS_OPT_BIST */
+
 extern __checkReturn   efx_rc_t
 siena_mac_poll(
        __in            efx_nic_t *enp,
index 0e3fc34..d7e7d77 100644 (file)
@@ -372,4 +372,209 @@ siena_phy_oui_get(
        return (ENOTSUP);
 }
 
+#if EFSYS_OPT_BIST
+
+       __checkReturn           efx_rc_t
+siena_phy_bist_start(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type)
+{
+       efx_rc_t rc;
+
+       if ((rc = efx_mcdi_bist_start(enp, type)) != 0)
+               goto fail1;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+static __checkReturn           unsigned long
+siena_phy_sft9001_bist_status(
+       __in                    uint16_t code)
+{
+       switch (code) {
+       case MC_CMD_POLL_BIST_SFT9001_PAIR_BUSY:
+               return (EFX_PHY_CABLE_STATUS_BUSY);
+       case MC_CMD_POLL_BIST_SFT9001_INTER_PAIR_SHORT:
+               return (EFX_PHY_CABLE_STATUS_INTERPAIRSHORT);
+       case MC_CMD_POLL_BIST_SFT9001_INTRA_PAIR_SHORT:
+               return (EFX_PHY_CABLE_STATUS_INTRAPAIRSHORT);
+       case MC_CMD_POLL_BIST_SFT9001_PAIR_OPEN:
+               return (EFX_PHY_CABLE_STATUS_OPEN);
+       case MC_CMD_POLL_BIST_SFT9001_PAIR_OK:
+               return (EFX_PHY_CABLE_STATUS_OK);
+       default:
+               return (EFX_PHY_CABLE_STATUS_INVALID);
+       }
+}
+
+       __checkReturn           efx_rc_t
+siena_phy_bist_poll(
+       __in                    efx_nic_t *enp,
+       __in                    efx_bist_type_t type,
+       __out                   efx_bist_result_t *resultp,
+       __out_opt __drv_when(count > 0, __notnull)
+       uint32_t *value_maskp,
+       __out_ecount_opt(count) __drv_when(count > 0, __notnull)
+       unsigned long *valuesp,
+       __in                    size_t count)
+{
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       uint8_t payload[MAX(MC_CMD_POLL_BIST_IN_LEN,
+                           MCDI_CTL_SDU_LEN_MAX)];
+       uint32_t value_mask = 0;
+       efx_mcdi_req_t req;
+       uint32_t result;
+       efx_rc_t rc;
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_POLL_BIST;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_POLL_BIST_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MCDI_CTL_SDU_LEN_MAX;
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       if (req.emr_out_length_used < MC_CMD_POLL_BIST_OUT_RESULT_OFST + 4) {
+               rc = EMSGSIZE;
+               goto fail2;
+       }
+
+       if (count > 0)
+               (void) memset(valuesp, '\0', count * sizeof (unsigned long));
+
+       result = MCDI_OUT_DWORD(req, POLL_BIST_OUT_RESULT);
+
+       /* Extract PHY specific results */
+       if (result == MC_CMD_POLL_BIST_PASSED &&
+           encp->enc_phy_type == EFX_PHY_SFT9001B &&
+           req.emr_out_length_used >= MC_CMD_POLL_BIST_OUT_SFT9001_LEN &&
+           (type == EFX_BIST_TYPE_PHY_CABLE_SHORT ||
+           type == EFX_BIST_TYPE_PHY_CABLE_LONG)) {
+               uint16_t word;
+
+               if (count > EFX_BIST_PHY_CABLE_LENGTH_A) {
+                       if (valuesp != NULL)
+                               valuesp[EFX_BIST_PHY_CABLE_LENGTH_A] =
+                                   MCDI_OUT_DWORD(req,
+                                   POLL_BIST_OUT_SFT9001_CABLE_LENGTH_A);
+                       value_mask |= (1 << EFX_BIST_PHY_CABLE_LENGTH_A);
+               }
+
+               if (count > EFX_BIST_PHY_CABLE_LENGTH_B) {
+                       if (valuesp != NULL)
+                               valuesp[EFX_BIST_PHY_CABLE_LENGTH_B] =
+                                   MCDI_OUT_DWORD(req,
+                                   POLL_BIST_OUT_SFT9001_CABLE_LENGTH_B);
+                       value_mask |= (1 << EFX_BIST_PHY_CABLE_LENGTH_B);
+               }
+
+               if (count > EFX_BIST_PHY_CABLE_LENGTH_C) {
+                       if (valuesp != NULL)
+                               valuesp[EFX_BIST_PHY_CABLE_LENGTH_C] =
+                                   MCDI_OUT_DWORD(req,
+                                   POLL_BIST_OUT_SFT9001_CABLE_LENGTH_C);
+                       value_mask |= (1 << EFX_BIST_PHY_CABLE_LENGTH_C);
+               }
+
+               if (count > EFX_BIST_PHY_CABLE_LENGTH_D) {
+                       if (valuesp != NULL)
+                               valuesp[EFX_BIST_PHY_CABLE_LENGTH_D] =
+                                   MCDI_OUT_DWORD(req,
+                                   POLL_BIST_OUT_SFT9001_CABLE_LENGTH_D);
+                       value_mask |= (1 << EFX_BIST_PHY_CABLE_LENGTH_D);
+               }
+
+               if (count > EFX_BIST_PHY_CABLE_STATUS_A) {
+                       if (valuesp != NULL) {
+                               word = MCDI_OUT_WORD(req,
+                                   POLL_BIST_OUT_SFT9001_CABLE_STATUS_A);
+                               valuesp[EFX_BIST_PHY_CABLE_STATUS_A] =
+                                   siena_phy_sft9001_bist_status(word);
+                       }
+                       value_mask |= (1 << EFX_BIST_PHY_CABLE_STATUS_A);
+               }
+
+               if (count > EFX_BIST_PHY_CABLE_STATUS_B) {
+                       if (valuesp != NULL) {
+                               word = MCDI_OUT_WORD(req,
+                                   POLL_BIST_OUT_SFT9001_CABLE_STATUS_B);
+                               valuesp[EFX_BIST_PHY_CABLE_STATUS_B] =
+                                   siena_phy_sft9001_bist_status(word);
+                       }
+                       value_mask |= (1 << EFX_BIST_PHY_CABLE_STATUS_B);
+               }
+
+               if (count > EFX_BIST_PHY_CABLE_STATUS_C) {
+                       if (valuesp != NULL) {
+                               word = MCDI_OUT_WORD(req,
+                                   POLL_BIST_OUT_SFT9001_CABLE_STATUS_C);
+                               valuesp[EFX_BIST_PHY_CABLE_STATUS_C] =
+                                   siena_phy_sft9001_bist_status(word);
+                       }
+                       value_mask |= (1 << EFX_BIST_PHY_CABLE_STATUS_C);
+               }
+
+               if (count > EFX_BIST_PHY_CABLE_STATUS_D) {
+                       if (valuesp != NULL) {
+                               word = MCDI_OUT_WORD(req,
+                                   POLL_BIST_OUT_SFT9001_CABLE_STATUS_D);
+                               valuesp[EFX_BIST_PHY_CABLE_STATUS_D] =
+                                   siena_phy_sft9001_bist_status(word);
+                       }
+                       value_mask |= (1 << EFX_BIST_PHY_CABLE_STATUS_D);
+               }
+
+       } else if (result == MC_CMD_POLL_BIST_FAILED &&
+                   encp->enc_phy_type == EFX_PHY_QLX111V &&
+                   req.emr_out_length >= MC_CMD_POLL_BIST_OUT_MRSFP_LEN &&
+                   count > EFX_BIST_FAULT_CODE) {
+               if (valuesp != NULL)
+                       valuesp[EFX_BIST_FAULT_CODE] =
+                           MCDI_OUT_DWORD(req, POLL_BIST_OUT_MRSFP_TEST);
+               value_mask |= 1 << EFX_BIST_FAULT_CODE;
+       }
+
+       if (value_maskp != NULL)
+               *value_maskp = value_mask;
+
+       EFSYS_ASSERT(resultp != NULL);
+       if (result == MC_CMD_POLL_BIST_RUNNING)
+               *resultp = EFX_BIST_RESULT_RUNNING;
+       else if (result == MC_CMD_POLL_BIST_PASSED)
+               *resultp = EFX_BIST_RESULT_PASSED;
+       else
+               *resultp = EFX_BIST_RESULT_FAILED;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+                       void
+siena_phy_bist_stop(
+       __in            efx_nic_t *enp,
+       __in            efx_bist_type_t type)
+{
+       /* There is no way to stop BIST on Siena */
+       _NOTE(ARGUNUSED(enp, type))
+}
+
+#endif /* EFSYS_OPT_BIST */
+
 #endif /* EFSYS_OPT_SIENA */