igc: add support to eeprom, registers and link self-tests
Introduced igc_diag.c and igc_diag.h, these files have the diagnostics functionality of igc driver. For the time being these files are being used by ethtool self-test callbacks. Which mean that eeprom, registers and link self-tests for ethtool were implemented. Signed-off-by: Vitaly Lifshits <vitaly.lifshits@intel.com> Reported-by: kbuild test robot <lkp@intel.com> Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Tested-by: Aaron Brown <aaron.f.brown@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
parent
25f06eff75
commit
f026d8ca29
|
@ -8,4 +8,4 @@
|
|||
obj-$(CONFIG_IGC) += igc.o
|
||||
|
||||
igc-objs := igc_main.o igc_mac.o igc_i225.o igc_base.o igc_nvm.o igc_phy.o \
|
||||
igc_ethtool.o igc_ptp.o igc_dump.o igc_tsn.o
|
||||
igc_diag.o igc_ethtool.o igc_ptp.o igc_dump.o igc_tsn.o
|
||||
|
|
|
@ -198,6 +198,8 @@ struct igc_adapter {
|
|||
unsigned long link_check_timeout;
|
||||
struct igc_info ei;
|
||||
|
||||
u32 test_icr;
|
||||
|
||||
struct ptp_clock *ptp_clock;
|
||||
struct ptp_clock_info ptp_caps;
|
||||
struct work_struct ptp_tx_work;
|
||||
|
@ -215,6 +217,8 @@ struct igc_adapter {
|
|||
|
||||
void igc_up(struct igc_adapter *adapter);
|
||||
void igc_down(struct igc_adapter *adapter);
|
||||
int igc_open(struct net_device *netdev);
|
||||
int igc_close(struct net_device *netdev);
|
||||
int igc_setup_tx_resources(struct igc_ring *ring);
|
||||
int igc_setup_rx_resources(struct igc_ring *ring);
|
||||
void igc_free_tx_resources(struct igc_ring *ring);
|
||||
|
|
186
drivers/net/ethernet/intel/igc/igc_diag.c
Normal file
186
drivers/net/ethernet/intel/igc/igc_diag.c
Normal file
|
@ -0,0 +1,186 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Intel Corporation */
|
||||
|
||||
#include "igc.h"
|
||||
#include "igc_diag.h"
|
||||
|
||||
static struct igc_reg_test reg_test[] = {
|
||||
{ IGC_FCAL, 1, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
|
||||
{ IGC_FCAH, 1, PATTERN_TEST, 0x0000FFFF, 0xFFFFFFFF },
|
||||
{ IGC_FCT, 1, PATTERN_TEST, 0x0000FFFF, 0xFFFFFFFF },
|
||||
{ IGC_RDBAH(0), 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
|
||||
{ IGC_RDBAL(0), 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFF80 },
|
||||
{ IGC_RDLEN(0), 4, PATTERN_TEST, 0x000FFF80, 0x000FFFFF },
|
||||
{ IGC_RDT(0), 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
|
||||
{ IGC_FCRTH, 1, PATTERN_TEST, 0x0003FFF0, 0x0003FFF0 },
|
||||
{ IGC_FCTTV, 1, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
|
||||
{ IGC_TIPG, 1, PATTERN_TEST, 0x3FFFFFFF, 0x3FFFFFFF },
|
||||
{ IGC_TDBAH(0), 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
|
||||
{ IGC_TDBAL(0), 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFF80 },
|
||||
{ IGC_TDLEN(0), 4, PATTERN_TEST, 0x000FFF80, 0x000FFFFF },
|
||||
{ IGC_TDT(0), 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
|
||||
{ IGC_RCTL, 1, SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
|
||||
{ IGC_RCTL, 1, SET_READ_TEST, 0x04CFB2FE, 0x003FFFFB },
|
||||
{ IGC_RCTL, 1, SET_READ_TEST, 0x04CFB2FE, 0xFFFFFFFF },
|
||||
{ IGC_TCTL, 1, SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
|
||||
{ IGC_RA, 16, TABLE64_TEST_LO,
|
||||
0xFFFFFFFF, 0xFFFFFFFF },
|
||||
{ IGC_RA, 16, TABLE64_TEST_HI,
|
||||
0x900FFFFF, 0xFFFFFFFF },
|
||||
{ IGC_MTA, 128, TABLE32_TEST,
|
||||
0xFFFFFFFF, 0xFFFFFFFF },
|
||||
{ 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static bool reg_pattern_test(struct igc_adapter *adapter, u64 *data, int reg,
|
||||
u32 mask, u32 write)
|
||||
{
|
||||
struct igc_hw *hw = &adapter->hw;
|
||||
u32 pat, val, before;
|
||||
static const u32 test_pattern[] = {
|
||||
0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
for (pat = 0; pat < ARRAY_SIZE(test_pattern); pat++) {
|
||||
before = rd32(reg);
|
||||
wr32(reg, test_pattern[pat] & write);
|
||||
val = rd32(reg);
|
||||
if (val != (test_pattern[pat] & write & mask)) {
|
||||
netdev_err(adapter->netdev,
|
||||
"pattern test reg %04X failed: got 0x%08X expected 0x%08X",
|
||||
reg, val, test_pattern[pat] & write & mask);
|
||||
*data = reg;
|
||||
wr32(reg, before);
|
||||
return false;
|
||||
}
|
||||
wr32(reg, before);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool reg_set_and_check(struct igc_adapter *adapter, u64 *data, int reg,
|
||||
u32 mask, u32 write)
|
||||
{
|
||||
struct igc_hw *hw = &adapter->hw;
|
||||
u32 val, before;
|
||||
|
||||
before = rd32(reg);
|
||||
wr32(reg, write & mask);
|
||||
val = rd32(reg);
|
||||
if ((write & mask) != (val & mask)) {
|
||||
netdev_err(adapter->netdev,
|
||||
"set/check reg %04X test failed: got 0x%08X expected 0x%08X",
|
||||
reg, (val & mask), (write & mask));
|
||||
*data = reg;
|
||||
wr32(reg, before);
|
||||
return false;
|
||||
}
|
||||
wr32(reg, before);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool igc_reg_test(struct igc_adapter *adapter, u64 *data)
|
||||
{
|
||||
struct igc_reg_test *test = reg_test;
|
||||
struct igc_hw *hw = &adapter->hw;
|
||||
u32 value, before, after;
|
||||
u32 i, toggle, b = false;
|
||||
|
||||
/* Because the status register is such a special case,
|
||||
* we handle it separately from the rest of the register
|
||||
* tests. Some bits are read-only, some toggle, and some
|
||||
* are writeable.
|
||||
*/
|
||||
toggle = 0x6800D3;
|
||||
before = rd32(IGC_STATUS);
|
||||
value = before & toggle;
|
||||
wr32(IGC_STATUS, toggle);
|
||||
after = rd32(IGC_STATUS) & toggle;
|
||||
if (value != after) {
|
||||
netdev_err(adapter->netdev,
|
||||
"failed STATUS register test got: 0x%08X expected: 0x%08X",
|
||||
after, value);
|
||||
*data = 1;
|
||||
return false;
|
||||
}
|
||||
/* restore previous status */
|
||||
wr32(IGC_STATUS, before);
|
||||
|
||||
/* Perform the remainder of the register test, looping through
|
||||
* the test table until we either fail or reach the null entry.
|
||||
*/
|
||||
while (test->reg) {
|
||||
for (i = 0; i < test->array_len; i++) {
|
||||
switch (test->test_type) {
|
||||
case PATTERN_TEST:
|
||||
b = reg_pattern_test(adapter, data,
|
||||
test->reg + (i * 0x40),
|
||||
test->mask,
|
||||
test->write);
|
||||
break;
|
||||
case SET_READ_TEST:
|
||||
b = reg_set_and_check(adapter, data,
|
||||
test->reg + (i * 0x40),
|
||||
test->mask,
|
||||
test->write);
|
||||
break;
|
||||
case TABLE64_TEST_LO:
|
||||
b = reg_pattern_test(adapter, data,
|
||||
test->reg + (i * 8),
|
||||
test->mask,
|
||||
test->write);
|
||||
break;
|
||||
case TABLE64_TEST_HI:
|
||||
b = reg_pattern_test(adapter, data,
|
||||
test->reg + 4 + (i * 8),
|
||||
test->mask,
|
||||
test->write);
|
||||
break;
|
||||
case TABLE32_TEST:
|
||||
b = reg_pattern_test(adapter, data,
|
||||
test->reg + (i * 4),
|
||||
test->mask,
|
||||
test->write);
|
||||
break;
|
||||
}
|
||||
if (!b)
|
||||
return false;
|
||||
}
|
||||
test++;
|
||||
}
|
||||
*data = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool igc_eeprom_test(struct igc_adapter *adapter, u64 *data)
|
||||
{
|
||||
struct igc_hw *hw = &adapter->hw;
|
||||
|
||||
*data = 0;
|
||||
|
||||
if (hw->nvm.ops.validate(hw) != IGC_SUCCESS) {
|
||||
*data = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool igc_link_test(struct igc_adapter *adapter, u64 *data)
|
||||
{
|
||||
bool link_up;
|
||||
|
||||
*data = 0;
|
||||
|
||||
/* add delay to give enough time for autonegotioation to finish */
|
||||
if (adapter->hw.mac.autoneg)
|
||||
ssleep(5);
|
||||
|
||||
link_up = igc_has_link(adapter);
|
||||
if (!link_up) {
|
||||
*data = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
30
drivers/net/ethernet/intel/igc/igc_diag.h
Normal file
30
drivers/net/ethernet/intel/igc/igc_diag.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2020 Intel Corporation */
|
||||
|
||||
bool igc_reg_test(struct igc_adapter *adapter, u64 *data);
|
||||
bool igc_eeprom_test(struct igc_adapter *adapter, u64 *data);
|
||||
bool igc_link_test(struct igc_adapter *adapter, u64 *data);
|
||||
|
||||
struct igc_reg_test {
|
||||
u16 reg;
|
||||
u8 array_len;
|
||||
u8 test_type;
|
||||
u32 mask;
|
||||
u32 write;
|
||||
};
|
||||
|
||||
/* In the hardware, registers are laid out either singly, in arrays
|
||||
* spaced 0x40 bytes apart, or in contiguous tables. We assume
|
||||
* most tests take place on arrays or single registers (handled
|
||||
* as a single-element array) and special-case the tables.
|
||||
* Table tests are always pattern tests.
|
||||
*
|
||||
* We also make provision for some required setup steps by specifying
|
||||
* registers to be written without any read-back testing.
|
||||
*/
|
||||
|
||||
#define PATTERN_TEST 1
|
||||
#define SET_READ_TEST 2
|
||||
#define TABLE32_TEST 3
|
||||
#define TABLE64_TEST_LO 4
|
||||
#define TABLE64_TEST_HI 5
|
|
@ -6,6 +6,7 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "igc.h"
|
||||
#include "igc_diag.h"
|
||||
|
||||
/* forward declaration */
|
||||
struct igc_stats {
|
||||
|
@ -1896,6 +1897,64 @@ static int igc_set_link_ksettings(struct net_device *netdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void igc_diag_test(struct net_device *netdev,
|
||||
struct ethtool_test *eth_test, u64 *data)
|
||||
{
|
||||
struct igc_adapter *adapter = netdev_priv(netdev);
|
||||
bool if_running = netif_running(netdev);
|
||||
|
||||
if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
|
||||
netdev_info(adapter->netdev, "offline testing starting");
|
||||
set_bit(__IGC_TESTING, &adapter->state);
|
||||
|
||||
/* Link test performed before hardware reset so autoneg doesn't
|
||||
* interfere with test result
|
||||
*/
|
||||
if (!igc_link_test(adapter, &data[TEST_LINK]))
|
||||
eth_test->flags |= ETH_TEST_FL_FAILED;
|
||||
|
||||
if (if_running)
|
||||
igc_close(netdev);
|
||||
else
|
||||
igc_reset(adapter);
|
||||
|
||||
netdev_info(adapter->netdev, "register testing starting");
|
||||
if (!igc_reg_test(adapter, &data[TEST_REG]))
|
||||
eth_test->flags |= ETH_TEST_FL_FAILED;
|
||||
|
||||
igc_reset(adapter);
|
||||
|
||||
netdev_info(adapter->netdev, "eeprom testing starting");
|
||||
if (!igc_eeprom_test(adapter, &data[TEST_EEP]))
|
||||
eth_test->flags |= ETH_TEST_FL_FAILED;
|
||||
|
||||
igc_reset(adapter);
|
||||
|
||||
/* loopback and interrupt tests
|
||||
* will be implemented in the future
|
||||
*/
|
||||
data[TEST_LOOP] = 0;
|
||||
data[TEST_IRQ] = 0;
|
||||
|
||||
clear_bit(__IGC_TESTING, &adapter->state);
|
||||
if (if_running)
|
||||
igc_open(netdev);
|
||||
} else {
|
||||
netdev_info(adapter->netdev, "online testing starting");
|
||||
|
||||
/* register, eeprom, intr and loopback tests not run online */
|
||||
data[TEST_REG] = 0;
|
||||
data[TEST_EEP] = 0;
|
||||
data[TEST_IRQ] = 0;
|
||||
data[TEST_LOOP] = 0;
|
||||
|
||||
if (!igc_link_test(adapter, &data[TEST_LINK]))
|
||||
eth_test->flags |= ETH_TEST_FL_FAILED;
|
||||
}
|
||||
|
||||
msleep_interruptible(4 * 1000);
|
||||
}
|
||||
|
||||
static const struct ethtool_ops igc_ethtool_ops = {
|
||||
.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
|
||||
.get_drvinfo = igc_get_drvinfo,
|
||||
|
@ -1933,6 +1992,7 @@ static const struct ethtool_ops igc_ethtool_ops = {
|
|||
.complete = igc_ethtool_complete,
|
||||
.get_link_ksettings = igc_get_link_ksettings,
|
||||
.set_link_ksettings = igc_set_link_ksettings,
|
||||
.self_test = igc_diag_test,
|
||||
};
|
||||
|
||||
void igc_set_ethtool_ops(struct net_device *netdev)
|
||||
|
|
|
@ -4380,7 +4380,7 @@ static int __igc_open(struct net_device *netdev, bool resuming)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int igc_open(struct net_device *netdev)
|
||||
int igc_open(struct net_device *netdev)
|
||||
{
|
||||
return __igc_open(netdev, false);
|
||||
}
|
||||
|
@ -4422,7 +4422,7 @@ static int __igc_close(struct net_device *netdev, bool suspending)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int igc_close(struct net_device *netdev)
|
||||
int igc_close(struct net_device *netdev)
|
||||
{
|
||||
if (netif_device_present(netdev) || netdev->dismantle)
|
||||
return __igc_close(netdev, false);
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#define IGC_FACTPS 0x05B30
|
||||
|
||||
/* Interrupt Register Description */
|
||||
#define IGC_EICR 0x01580 /* Ext. Interrupt Cause read - W0 */
|
||||
#define IGC_EICS 0x01520 /* Ext. Interrupt Cause Set - W0 */
|
||||
#define IGC_EIMS 0x01524 /* Ext. Interrupt Mask Set/Read - RW */
|
||||
#define IGC_EIMC 0x01528 /* Ext. Interrupt Mask Clear - WO */
|
||||
|
@ -119,6 +120,7 @@
|
|||
#define IGC_RLPML 0x05004 /* Rx Long Packet Max Length */
|
||||
#define IGC_RFCTL 0x05008 /* Receive Filter Control*/
|
||||
#define IGC_MTA 0x05200 /* Multicast Table Array - RW Array */
|
||||
#define IGC_RA 0x05400 /* Receive Address - RW Array */
|
||||
#define IGC_UTA 0x0A000 /* Unicast Table Array - RW */
|
||||
#define IGC_RAL(_n) (0x05400 + ((_n) * 0x08))
|
||||
#define IGC_RAH(_n) (0x05404 + ((_n) * 0x08))
|
||||
|
|
Loading…
Reference in New Issue
Block a user