Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 307505e9 authored by Ben Hutchings's avatar Ben Hutchings Committed by David S. Miller
Browse files

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: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1796721a
Loading
Loading
Loading
Loading
+107 −8
Original line number Diff line number Diff line
@@ -106,6 +106,25 @@
#define PMA_PMD_SPEED_LBN        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 */
#define PMA_PMD_CSERDES_CTRL_REG 64258
/* 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);
}

static const char *const tenxpress_test_names[] = {
static const char *const sfx7101_test_names[] = {
	"bist"
};

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;

@@ -672,6 +691,86 @@ tenxpress_run_tests(struct efx_nic *efx, int *results, unsigned flags)
	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)
{
	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,
	.get_settings	  = sfx7101_get_settings,
	.set_settings	  = mdio_clause45_set_settings,
	.num_tests	  = ARRAY_SIZE(tenxpress_test_names),
	.test_names	  = tenxpress_test_names,
	.run_tests	  = tenxpress_run_tests,
	.num_tests	  = ARRAY_SIZE(sfx7101_test_names),
	.test_names	  = sfx7101_test_names,
	.run_tests	  = sfx7101_run_tests,
	.mmds             = TENXPRESS_REQUIRED_DEVS,
	.loopbacks        = SFX7101_LOOPBACKS,
};
@@ -801,9 +900,9 @@ struct efx_phy_operations falcon_sft9001_phy_ops = {
	.get_settings	  = sft9001_get_settings,
	.set_settings	  = sft9001_set_settings,
	.set_xnp_advertise = sft9001_set_xnp_advertise,
	.num_tests	  = ARRAY_SIZE(tenxpress_test_names),
	.test_names	  = tenxpress_test_names,
	.run_tests	  = tenxpress_run_tests,
	.num_tests	  = ARRAY_SIZE(sft9001_test_names),
	.test_names	  = sft9001_test_names,
	.run_tests	  = sft9001_run_tests,
	.mmds             = TENXPRESS_REQUIRED_DEVS,
	.loopbacks        = SFT9001_LOOPBACKS,
};