|
@@ -17,6 +17,8 @@
|
|
|
#include "mcdi.h"
|
|
|
#include "mcdi_pcol.h"
|
|
|
#include "mdio_10g.h"
|
|
|
+#include "nic.h"
|
|
|
+#include "selftest.h"
|
|
|
|
|
|
struct efx_mcdi_phy_cfg {
|
|
|
u32 flags;
|
|
@@ -594,6 +596,146 @@ static int efx_mcdi_phy_test_alive(struct efx_nic *efx)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static const char *const mcdi_sft9001_cable_diag_names[] = {
|
|
|
+ "cable.pairA.length",
|
|
|
+ "cable.pairB.length",
|
|
|
+ "cable.pairC.length",
|
|
|
+ "cable.pairD.length",
|
|
|
+ "cable.pairA.status",
|
|
|
+ "cable.pairB.status",
|
|
|
+ "cable.pairC.status",
|
|
|
+ "cable.pairD.status",
|
|
|
+};
|
|
|
+
|
|
|
+static int efx_mcdi_bist(struct efx_nic *efx, unsigned int bist_mode,
|
|
|
+ int *results)
|
|
|
+{
|
|
|
+ unsigned int retry, i, count = 0;
|
|
|
+ size_t outlen;
|
|
|
+ u32 status;
|
|
|
+ u8 *buf, *ptr;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ buf = kzalloc(0x100, GFP_KERNEL);
|
|
|
+ if (buf == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ BUILD_BUG_ON(MC_CMD_START_BIST_OUT_LEN != 0);
|
|
|
+ MCDI_SET_DWORD(buf, START_BIST_IN_TYPE, bist_mode);
|
|
|
+ rc = efx_mcdi_rpc(efx, MC_CMD_START_BIST, buf, MC_CMD_START_BIST_IN_LEN,
|
|
|
+ NULL, 0, NULL);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* Wait up to 10s for BIST to finish */
|
|
|
+ for (retry = 0; retry < 100; ++retry) {
|
|
|
+ BUILD_BUG_ON(MC_CMD_POLL_BIST_IN_LEN != 0);
|
|
|
+ rc = efx_mcdi_rpc(efx, MC_CMD_POLL_BIST, NULL, 0,
|
|
|
+ buf, 0x100, &outlen);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ status = MCDI_DWORD(buf, POLL_BIST_OUT_RESULT);
|
|
|
+ if (status != MC_CMD_POLL_BIST_RUNNING)
|
|
|
+ goto finished;
|
|
|
+
|
|
|
+ msleep(100);
|
|
|
+ }
|
|
|
+
|
|
|
+ rc = -ETIMEDOUT;
|
|
|
+ goto out;
|
|
|
+
|
|
|
+finished:
|
|
|
+ results[count++] = (status == MC_CMD_POLL_BIST_PASSED) ? 1 : -1;
|
|
|
+
|
|
|
+ /* SFT9001 specific cable diagnostics output */
|
|
|
+ if (efx->phy_type == PHY_TYPE_SFT9001B &&
|
|
|
+ (bist_mode == MC_CMD_PHY_BIST_CABLE_SHORT ||
|
|
|
+ bist_mode == MC_CMD_PHY_BIST_CABLE_LONG)) {
|
|
|
+ ptr = MCDI_PTR(buf, POLL_BIST_OUT_SFT9001_CABLE_LENGTH_A);
|
|
|
+ if (status == MC_CMD_POLL_BIST_PASSED &&
|
|
|
+ outlen >= MC_CMD_POLL_BIST_OUT_SFT9001_LEN) {
|
|
|
+ for (i = 0; i < 8; i++) {
|
|
|
+ results[count + i] =
|
|
|
+ EFX_DWORD_FIELD(((efx_dword_t *)ptr)[i],
|
|
|
+ EFX_DWORD_0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ count += 8;
|
|
|
+ }
|
|
|
+ rc = count;
|
|
|
+
|
|
|
+out:
|
|
|
+ kfree(buf);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static int efx_mcdi_phy_run_tests(struct efx_nic *efx, int *results,
|
|
|
+ unsigned flags)
|
|
|
+{
|
|
|
+ struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data;
|
|
|
+ u32 mode;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_BIST_LBN)) {
|
|
|
+ rc = efx_mcdi_bist(efx, MC_CMD_PHY_BIST, results);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ results += rc;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If we support both LONG and SHORT, then run each in response to
|
|
|
+ * break or not. Otherwise, run the one we support */
|
|
|
+ mode = 0;
|
|
|
+ if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_BIST_CABLE_SHORT_LBN)) {
|
|
|
+ if ((flags & ETH_TEST_FL_OFFLINE) &&
|
|
|
+ (phy_cfg->flags &
|
|
|
+ (1 << MC_CMD_GET_PHY_CFG_BIST_CABLE_LONG_LBN)))
|
|
|
+ mode = MC_CMD_PHY_BIST_CABLE_LONG;
|
|
|
+ else
|
|
|
+ mode = MC_CMD_PHY_BIST_CABLE_SHORT;
|
|
|
+ } else if (phy_cfg->flags &
|
|
|
+ (1 << MC_CMD_GET_PHY_CFG_BIST_CABLE_LONG_LBN))
|
|
|
+ mode = MC_CMD_PHY_BIST_CABLE_LONG;
|
|
|
+
|
|
|
+ if (mode != 0) {
|
|
|
+ rc = efx_mcdi_bist(efx, mode, results);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+ results += rc;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+const char *efx_mcdi_phy_test_name(struct efx_nic *efx, unsigned int index)
|
|
|
+{
|
|
|
+ struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data;
|
|
|
+
|
|
|
+ if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_BIST_LBN)) {
|
|
|
+ if (index == 0)
|
|
|
+ return "bist";
|
|
|
+ --index;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (phy_cfg->flags & ((1 << MC_CMD_GET_PHY_CFG_BIST_CABLE_SHORT_LBN) |
|
|
|
+ (1 << MC_CMD_GET_PHY_CFG_BIST_CABLE_LONG_LBN))) {
|
|
|
+ if (index == 0)
|
|
|
+ return "cable";
|
|
|
+ --index;
|
|
|
+
|
|
|
+ if (efx->phy_type == PHY_TYPE_SFT9001B) {
|
|
|
+ if (index < ARRAY_SIZE(mcdi_sft9001_cable_diag_names))
|
|
|
+ return mcdi_sft9001_cable_diag_names[index];
|
|
|
+ index -= ARRAY_SIZE(mcdi_sft9001_cable_diag_names);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
struct efx_phy_operations efx_mcdi_phy_ops = {
|
|
|
.probe = efx_mcdi_phy_probe,
|
|
|
.init = efx_port_dummy_op_int,
|
|
@@ -604,6 +746,6 @@ struct efx_phy_operations efx_mcdi_phy_ops = {
|
|
|
.get_settings = efx_mcdi_phy_get_settings,
|
|
|
.set_settings = efx_mcdi_phy_set_settings,
|
|
|
.test_alive = efx_mcdi_phy_test_alive,
|
|
|
- .run_tests = NULL,
|
|
|
- .test_name = NULL,
|
|
|
+ .run_tests = efx_mcdi_phy_run_tests,
|
|
|
+ .test_name = efx_mcdi_phy_test_name,
|
|
|
};
|