sfc: Disable VF queues during register self-test
Currently VF queues and drivers may remain active during this test. This could cause memory corruption or spurious test failures. Therefore we reset the port/function before running these tests on Siena. On Falcon this doesn't work: we have to do some additional initialisation before some blocks will work again. So refactor the reset/register-test sequence into an efx_nic_type method so efx_selftest() doesn't have to consider such quirks. In the process, fix another minor bug: Siena does not have an 'invisible' reset and the self-test currently fails to push the PHY configuration after resetting. Passing RESET_TYPE_ALL to efx_reset_{down,up}() fixes this. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
This commit is contained in:
parent
0f1e54ae52
commit
d4f2cecce1
5 changed files with 76 additions and 60 deletions
|
@ -25,9 +25,12 @@
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "phy.h"
|
#include "phy.h"
|
||||||
#include "workarounds.h"
|
#include "workarounds.h"
|
||||||
|
#include "selftest.h"
|
||||||
|
|
||||||
/* Hardware control for SFC4000 (aka Falcon). */
|
/* Hardware control for SFC4000 (aka Falcon). */
|
||||||
|
|
||||||
|
static int falcon_reset_hw(struct efx_nic *efx, enum reset_type method);
|
||||||
|
|
||||||
static const unsigned int
|
static const unsigned int
|
||||||
/* "Large" EEPROM device: Atmel AT25640 or similar
|
/* "Large" EEPROM device: Atmel AT25640 or similar
|
||||||
* 8 KB, 16-bit address, 32 B write block */
|
* 8 KB, 16-bit address, 32 B write block */
|
||||||
|
@ -1034,10 +1037,34 @@ static const struct efx_nic_register_test falcon_b0_register_tests[] = {
|
||||||
EFX_OWORD32(0x0003FF0F, 0x00000000, 0x00000000, 0x00000000) },
|
EFX_OWORD32(0x0003FF0F, 0x00000000, 0x00000000, 0x00000000) },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int falcon_b0_test_registers(struct efx_nic *efx)
|
static int
|
||||||
|
falcon_b0_test_chip(struct efx_nic *efx, struct efx_self_tests *tests)
|
||||||
{
|
{
|
||||||
return efx_nic_test_registers(efx, falcon_b0_register_tests,
|
enum reset_type reset_method = RESET_TYPE_INVISIBLE;
|
||||||
ARRAY_SIZE(falcon_b0_register_tests));
|
int rc, rc2;
|
||||||
|
|
||||||
|
mutex_lock(&efx->mac_lock);
|
||||||
|
if (efx->loopback_modes) {
|
||||||
|
/* We need the 312 clock from the PHY to test the XMAC
|
||||||
|
* registers, so move into XGMII loopback if available */
|
||||||
|
if (efx->loopback_modes & (1 << LOOPBACK_XGMII))
|
||||||
|
efx->loopback_mode = LOOPBACK_XGMII;
|
||||||
|
else
|
||||||
|
efx->loopback_mode = __ffs(efx->loopback_modes);
|
||||||
|
}
|
||||||
|
__efx_reconfigure_port(efx);
|
||||||
|
mutex_unlock(&efx->mac_lock);
|
||||||
|
|
||||||
|
efx_reset_down(efx, reset_method);
|
||||||
|
|
||||||
|
tests->registers =
|
||||||
|
efx_nic_test_registers(efx, falcon_b0_register_tests,
|
||||||
|
ARRAY_SIZE(falcon_b0_register_tests))
|
||||||
|
? -1 : 1;
|
||||||
|
|
||||||
|
rc = falcon_reset_hw(efx, reset_method);
|
||||||
|
rc2 = efx_reset_up(efx, reset_method, rc == 0);
|
||||||
|
return rc ? rc : rc2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
|
@ -1818,7 +1845,7 @@ const struct efx_nic_type falcon_b0_nic_type = {
|
||||||
.get_wol = falcon_get_wol,
|
.get_wol = falcon_get_wol,
|
||||||
.set_wol = falcon_set_wol,
|
.set_wol = falcon_set_wol,
|
||||||
.resume_wol = efx_port_dummy_op_void,
|
.resume_wol = efx_port_dummy_op_void,
|
||||||
.test_registers = falcon_b0_test_registers,
|
.test_chip = falcon_b0_test_chip,
|
||||||
.test_nvram = falcon_test_nvram,
|
.test_nvram = falcon_test_nvram,
|
||||||
|
|
||||||
.revision = EFX_REV_FALCON_B0,
|
.revision = EFX_REV_FALCON_B0,
|
||||||
|
|
|
@ -68,6 +68,8 @@
|
||||||
#define EFX_TXQ_TYPES 4
|
#define EFX_TXQ_TYPES 4
|
||||||
#define EFX_MAX_TX_QUEUES (EFX_TXQ_TYPES * EFX_MAX_CHANNELS)
|
#define EFX_MAX_TX_QUEUES (EFX_TXQ_TYPES * EFX_MAX_CHANNELS)
|
||||||
|
|
||||||
|
struct efx_self_tests;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct efx_special_buffer - An Efx special buffer
|
* struct efx_special_buffer - An Efx special buffer
|
||||||
* @addr: CPU base address of the buffer
|
* @addr: CPU base address of the buffer
|
||||||
|
@ -901,7 +903,8 @@ static inline unsigned int efx_port_num(struct efx_nic *efx)
|
||||||
* @get_wol: Get WoL configuration from driver state
|
* @get_wol: Get WoL configuration from driver state
|
||||||
* @set_wol: Push WoL configuration to the NIC
|
* @set_wol: Push WoL configuration to the NIC
|
||||||
* @resume_wol: Synchronise WoL state between driver and MC (e.g. after resume)
|
* @resume_wol: Synchronise WoL state between driver and MC (e.g. after resume)
|
||||||
* @test_registers: Test read/write functionality of control registers
|
* @test_chip: Test registers. Should use efx_nic_test_registers(), and is
|
||||||
|
* expected to reset the NIC.
|
||||||
* @test_nvram: Test validity of NVRAM contents
|
* @test_nvram: Test validity of NVRAM contents
|
||||||
* @revision: Hardware architecture revision
|
* @revision: Hardware architecture revision
|
||||||
* @mem_map_size: Memory BAR mapped size
|
* @mem_map_size: Memory BAR mapped size
|
||||||
|
@ -946,7 +949,7 @@ struct efx_nic_type {
|
||||||
void (*get_wol)(struct efx_nic *efx, struct ethtool_wolinfo *wol);
|
void (*get_wol)(struct efx_nic *efx, struct ethtool_wolinfo *wol);
|
||||||
int (*set_wol)(struct efx_nic *efx, u32 type);
|
int (*set_wol)(struct efx_nic *efx, u32 type);
|
||||||
void (*resume_wol)(struct efx_nic *efx);
|
void (*resume_wol)(struct efx_nic *efx);
|
||||||
int (*test_registers)(struct efx_nic *efx);
|
int (*test_chip)(struct efx_nic *efx, struct efx_self_tests *tests);
|
||||||
int (*test_nvram)(struct efx_nic *efx);
|
int (*test_nvram)(struct efx_nic *efx);
|
||||||
|
|
||||||
int revision;
|
int revision;
|
||||||
|
|
|
@ -124,9 +124,6 @@ int efx_nic_test_registers(struct efx_nic *efx,
|
||||||
unsigned address = 0, i, j;
|
unsigned address = 0, i, j;
|
||||||
efx_oword_t mask, imask, original, reg, buf;
|
efx_oword_t mask, imask, original, reg, buf;
|
||||||
|
|
||||||
/* Falcon should be in loopback to isolate the XMAC from the PHY */
|
|
||||||
WARN_ON(!LOOPBACK_INTERNAL(efx));
|
|
||||||
|
|
||||||
for (i = 0; i < n_regs; ++i) {
|
for (i = 0; i < n_regs; ++i) {
|
||||||
address = regs[i].address;
|
address = regs[i].address;
|
||||||
mask = imask = regs[i].mask;
|
mask = imask = regs[i].mask;
|
||||||
|
|
|
@ -120,19 +120,6 @@ static int efx_test_nvram(struct efx_nic *efx, struct efx_self_tests *tests)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int efx_test_chip(struct efx_nic *efx, struct efx_self_tests *tests)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
/* Test register access */
|
|
||||||
if (efx->type->test_registers) {
|
|
||||||
rc = efx->type->test_registers(efx);
|
|
||||||
tests->registers = rc ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
*
|
*
|
||||||
* Interrupt and event queue testing
|
* Interrupt and event queue testing
|
||||||
|
@ -699,8 +686,7 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
|
||||||
{
|
{
|
||||||
enum efx_loopback_mode loopback_mode = efx->loopback_mode;
|
enum efx_loopback_mode loopback_mode = efx->loopback_mode;
|
||||||
int phy_mode = efx->phy_mode;
|
int phy_mode = efx->phy_mode;
|
||||||
enum reset_type reset_method = RESET_TYPE_INVISIBLE;
|
int rc_test = 0, rc_reset, rc;
|
||||||
int rc_test = 0, rc_reset = 0, rc;
|
|
||||||
|
|
||||||
efx_selftest_async_cancel(efx);
|
efx_selftest_async_cancel(efx);
|
||||||
|
|
||||||
|
@ -737,44 +723,26 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
|
||||||
*/
|
*/
|
||||||
netif_device_detach(efx->net_dev);
|
netif_device_detach(efx->net_dev);
|
||||||
|
|
||||||
mutex_lock(&efx->mac_lock);
|
if (efx->type->test_chip) {
|
||||||
if (efx->loopback_modes) {
|
rc_reset = efx->type->test_chip(efx, tests);
|
||||||
/* We need the 312 clock from the PHY to test the XMAC
|
if (rc_reset) {
|
||||||
* registers, so move into XGMII loopback if available */
|
netif_err(efx, hw, efx->net_dev,
|
||||||
if (efx->loopback_modes & (1 << LOOPBACK_XGMII))
|
"Unable to recover from chip test\n");
|
||||||
efx->loopback_mode = LOOPBACK_XGMII;
|
efx_schedule_reset(efx, RESET_TYPE_DISABLE);
|
||||||
else
|
return rc_reset;
|
||||||
efx->loopback_mode = __ffs(efx->loopback_modes);
|
}
|
||||||
|
|
||||||
|
if ((tests->registers < 0) && !rc_test)
|
||||||
|
rc_test = -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
__efx_reconfigure_port(efx);
|
|
||||||
mutex_unlock(&efx->mac_lock);
|
|
||||||
|
|
||||||
/* free up all consumers of SRAM (including all the queues) */
|
|
||||||
efx_reset_down(efx, reset_method);
|
|
||||||
|
|
||||||
rc = efx_test_chip(efx, tests);
|
|
||||||
if (rc && !rc_test)
|
|
||||||
rc_test = rc;
|
|
||||||
|
|
||||||
/* reset the chip to recover from the register test */
|
|
||||||
rc_reset = efx->type->reset(efx, reset_method);
|
|
||||||
|
|
||||||
/* Ensure that the phy is powered and out of loopback
|
/* Ensure that the phy is powered and out of loopback
|
||||||
* for the bist and loopback tests */
|
* for the bist and loopback tests */
|
||||||
|
mutex_lock(&efx->mac_lock);
|
||||||
efx->phy_mode &= ~PHY_MODE_LOW_POWER;
|
efx->phy_mode &= ~PHY_MODE_LOW_POWER;
|
||||||
efx->loopback_mode = LOOPBACK_NONE;
|
efx->loopback_mode = LOOPBACK_NONE;
|
||||||
|
__efx_reconfigure_port(efx);
|
||||||
rc = efx_reset_up(efx, reset_method, rc_reset == 0);
|
mutex_unlock(&efx->mac_lock);
|
||||||
if (rc && !rc_reset)
|
|
||||||
rc_reset = rc;
|
|
||||||
|
|
||||||
if (rc_reset) {
|
|
||||||
netif_err(efx, drv, efx->net_dev,
|
|
||||||
"Unable to recover from chip test\n");
|
|
||||||
efx_schedule_reset(efx, RESET_TYPE_DISABLE);
|
|
||||||
return rc_reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = efx_test_phy(efx, tests, flags);
|
rc = efx_test_phy(efx, tests, flags);
|
||||||
if (rc && !rc_test)
|
if (rc && !rc_test)
|
||||||
|
|
|
@ -25,10 +25,12 @@
|
||||||
#include "workarounds.h"
|
#include "workarounds.h"
|
||||||
#include "mcdi.h"
|
#include "mcdi.h"
|
||||||
#include "mcdi_pcol.h"
|
#include "mcdi_pcol.h"
|
||||||
|
#include "selftest.h"
|
||||||
|
|
||||||
/* Hardware control for SFC9000 family including SFL9021 (aka Siena). */
|
/* Hardware control for SFC9000 family including SFL9021 (aka Siena). */
|
||||||
|
|
||||||
static void siena_init_wol(struct efx_nic *efx);
|
static void siena_init_wol(struct efx_nic *efx);
|
||||||
|
static int siena_reset_hw(struct efx_nic *efx, enum reset_type method);
|
||||||
|
|
||||||
|
|
||||||
static void siena_push_irq_moderation(struct efx_channel *channel)
|
static void siena_push_irq_moderation(struct efx_channel *channel)
|
||||||
|
@ -154,10 +156,29 @@ static const struct efx_nic_register_test siena_register_tests[] = {
|
||||||
EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0x00000007, 0x00000000) },
|
EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0x00000007, 0x00000000) },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int siena_test_registers(struct efx_nic *efx)
|
static int siena_test_chip(struct efx_nic *efx, struct efx_self_tests *tests)
|
||||||
{
|
{
|
||||||
return efx_nic_test_registers(efx, siena_register_tests,
|
enum reset_type reset_method = reset_method;
|
||||||
ARRAY_SIZE(siena_register_tests));
|
int rc, rc2;
|
||||||
|
|
||||||
|
efx_reset_down(efx, reset_method);
|
||||||
|
|
||||||
|
/* Reset the chip immediately so that it is completely
|
||||||
|
* quiescent regardless of what any VF driver does.
|
||||||
|
*/
|
||||||
|
rc = siena_reset_hw(efx, reset_method);
|
||||||
|
if (rc)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
tests->registers =
|
||||||
|
efx_nic_test_registers(efx, siena_register_tests,
|
||||||
|
ARRAY_SIZE(siena_register_tests))
|
||||||
|
? -1 : 1;
|
||||||
|
|
||||||
|
rc = siena_reset_hw(efx, reset_method);
|
||||||
|
out:
|
||||||
|
rc2 = efx_reset_up(efx, reset_method, rc == 0);
|
||||||
|
return rc ? rc : rc2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
|
@ -649,7 +670,7 @@ const struct efx_nic_type siena_a0_nic_type = {
|
||||||
.get_wol = siena_get_wol,
|
.get_wol = siena_get_wol,
|
||||||
.set_wol = siena_set_wol,
|
.set_wol = siena_set_wol,
|
||||||
.resume_wol = siena_init_wol,
|
.resume_wol = siena_init_wol,
|
||||||
.test_registers = siena_test_registers,
|
.test_chip = siena_test_chip,
|
||||||
.test_nvram = efx_mcdi_nvram_test_all,
|
.test_nvram = efx_mcdi_nvram_test_all,
|
||||||
|
|
||||||
.revision = EFX_REV_SIENA_A0,
|
.revision = EFX_REV_SIENA_A0,
|
||||||
|
|
Loading…
Add table
Reference in a new issue