sfc: SFT9001: Add cable diagnostics

The SFT9001 firmware implements cable diagnostics; run those and
include their results in a self-test.  In case of a cable fault, do
not fail the self-test as a whole; only faults in the NIC should cause
that.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Ben Hutchings 2008-12-26 13:48:00 -08:00 committed by David S. Miller
parent 1796721a5a
commit 307505e9a4
1 changed files with 107 additions and 8 deletions

View File

@ -106,6 +106,25 @@
#define PMA_PMD_SPEED_LBN 4 #define PMA_PMD_SPEED_LBN 4
#define PMA_PMD_SPEED_WIDTH 4 #define PMA_PMD_SPEED_WIDTH 4
/* Cable diagnostics - SFT9001 only */
#define PMA_PMD_CDIAG_CTRL_REG 49213
#define CDIAG_CTRL_IMMED_LBN 15
#define CDIAG_CTRL_BRK_LINK_LBN 12
#define CDIAG_CTRL_IN_PROG_LBN 11
#define CDIAG_CTRL_LEN_UNIT_LBN 10
#define CDIAG_CTRL_LEN_METRES 1
#define PMA_PMD_CDIAG_RES_REG 49174
#define CDIAG_RES_A_LBN 12
#define CDIAG_RES_B_LBN 8
#define CDIAG_RES_C_LBN 4
#define CDIAG_RES_D_LBN 0
#define CDIAG_RES_WIDTH 4
#define CDIAG_RES_OPEN 2
#define CDIAG_RES_OK 1
#define CDIAG_RES_INVALID 0
/* Set of 4 registers for pairs A-D */
#define PMA_PMD_CDIAG_LEN_REG 49175
/* Serdes control registers - SFT9001 only */ /* Serdes control registers - SFT9001 only */
#define PMA_PMD_CSERDES_CTRL_REG 64258 #define PMA_PMD_CSERDES_CTRL_REG 64258
/* Set the 156.25 MHz output to 312.5 MHz to drive Falcon's XMAC */ /* Set the 156.25 MHz output to 312.5 MHz to drive Falcon's XMAC */
@ -654,12 +673,12 @@ void tenxpress_phy_blink(struct efx_nic *efx, bool blink)
PMA_PMD_LED_OVERR_REG, reg); PMA_PMD_LED_OVERR_REG, reg);
} }
static const char *const tenxpress_test_names[] = { static const char *const sfx7101_test_names[] = {
"bist" "bist"
}; };
static int static int
tenxpress_run_tests(struct efx_nic *efx, int *results, unsigned flags) sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags)
{ {
int rc; int rc;
@ -672,6 +691,86 @@ tenxpress_run_tests(struct efx_nic *efx, int *results, unsigned flags)
return rc; return rc;
} }
static const char *const sft9001_test_names[] = {
"bist",
"cable.pairA.status",
"cable.pairB.status",
"cable.pairC.status",
"cable.pairD.status",
"cable.pairA.length",
"cable.pairB.length",
"cable.pairC.length",
"cable.pairD.length",
};
static int sft9001_run_tests(struct efx_nic *efx, int *results, unsigned flags)
{
struct ethtool_cmd ecmd;
int phy_id = efx->mii.phy_id;
int rc = 0, rc2, i, res_reg;
if (!(flags & ETH_TEST_FL_OFFLINE))
return 0;
efx->phy_op->get_settings(efx, &ecmd);
/* Initialise cable diagnostic results to unknown failure */
for (i = 1; i < 9; ++i)
results[i] = -1;
/* Run cable diagnostics; wait up to 5 seconds for them to complete.
* A cable fault is not a self-test failure, but a timeout is. */
mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
PMA_PMD_CDIAG_CTRL_REG,
(1 << CDIAG_CTRL_IMMED_LBN) |
(1 << CDIAG_CTRL_BRK_LINK_LBN) |
(CDIAG_CTRL_LEN_METRES << CDIAG_CTRL_LEN_UNIT_LBN));
i = 0;
while (mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
PMA_PMD_CDIAG_CTRL_REG) &
(1 << CDIAG_CTRL_IN_PROG_LBN)) {
if (++i == 50) {
rc = -ETIMEDOUT;
goto reset;
}
msleep(100);
}
res_reg = mdio_clause45_read(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
PMA_PMD_CDIAG_RES_REG);
for (i = 0; i < 4; i++) {
int pair_res =
(res_reg >> (CDIAG_RES_A_LBN - i * CDIAG_RES_WIDTH))
& ((1 << CDIAG_RES_WIDTH) - 1);
int len_reg = mdio_clause45_read(efx, efx->mii.phy_id,
MDIO_MMD_PMAPMD,
PMA_PMD_CDIAG_LEN_REG + i);
if (pair_res == CDIAG_RES_OK)
results[1 + i] = 1;
else if (pair_res == CDIAG_RES_INVALID)
results[1 + i] = -1;
else
results[1 + i] = -pair_res;
if (pair_res != CDIAG_RES_INVALID &&
pair_res != CDIAG_RES_OPEN &&
len_reg != 0xffff)
results[5 + i] = len_reg;
}
/* We must reset to exit cable diagnostic mode. The BIST will
* also run when we do this. */
reset:
rc2 = tenxpress_special_reset(efx);
results[0] = rc2 ? -1 : 1;
if (!rc)
rc = rc2;
rc2 = efx->phy_op->set_settings(efx, &ecmd);
if (!rc)
rc = rc2;
return rc;
}
static u32 tenxpress_get_xnp_lpa(struct efx_nic *efx) static u32 tenxpress_get_xnp_lpa(struct efx_nic *efx)
{ {
int phy = efx->mii.phy_id; int phy = efx->mii.phy_id;
@ -784,9 +883,9 @@ struct efx_phy_operations falcon_sfx7101_phy_ops = {
.clear_interrupt = efx_port_dummy_op_void, .clear_interrupt = efx_port_dummy_op_void,
.get_settings = sfx7101_get_settings, .get_settings = sfx7101_get_settings,
.set_settings = mdio_clause45_set_settings, .set_settings = mdio_clause45_set_settings,
.num_tests = ARRAY_SIZE(tenxpress_test_names), .num_tests = ARRAY_SIZE(sfx7101_test_names),
.test_names = tenxpress_test_names, .test_names = sfx7101_test_names,
.run_tests = tenxpress_run_tests, .run_tests = sfx7101_run_tests,
.mmds = TENXPRESS_REQUIRED_DEVS, .mmds = TENXPRESS_REQUIRED_DEVS,
.loopbacks = SFX7101_LOOPBACKS, .loopbacks = SFX7101_LOOPBACKS,
}; };
@ -801,9 +900,9 @@ struct efx_phy_operations falcon_sft9001_phy_ops = {
.get_settings = sft9001_get_settings, .get_settings = sft9001_get_settings,
.set_settings = sft9001_set_settings, .set_settings = sft9001_set_settings,
.set_xnp_advertise = sft9001_set_xnp_advertise, .set_xnp_advertise = sft9001_set_xnp_advertise,
.num_tests = ARRAY_SIZE(tenxpress_test_names), .num_tests = ARRAY_SIZE(sft9001_test_names),
.test_names = tenxpress_test_names, .test_names = sft9001_test_names,
.run_tests = tenxpress_run_tests, .run_tests = sft9001_run_tests,
.mmds = TENXPRESS_REQUIRED_DEVS, .mmds = TENXPRESS_REQUIRED_DEVS,
.loopbacks = SFT9001_LOOPBACKS, .loopbacks = SFT9001_LOOPBACKS,
}; };