sfc: Add Siena PHY BIST and cable diagnostic support
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
c28884c574
commit
affaf485ca
|
@ -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,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue