return len;
}
+
+/* CSR register test data
+ *
+ * The register test will be used to verify hardware is behaving as expected.
+ *
+ * The test itself will have us writing to registers that should have no
+ * side effects due to us resetting after the test has been completed.
+ * While the test is being run the interface should be offline.
+ */
+struct fbnic_csr_reg_test_data {
+ int reg;
+ u16 reg_offset;
+ u8 array_len;
+ u32 read;
+ u32 write;
+};
+
+#define FBNIC_QUEUE_REG_TEST(_name, _read, _write) { \
+ .reg = FBNIC_QUEUE(0) + FBNIC_QUEUE_##_name, \
+ .reg_offset = FBNIC_QUEUE_STRIDE, \
+ .array_len = 64, \
+ .read = _read, \
+ .write = _write \
+}
+
+static const struct fbnic_csr_reg_test_data pattern_test[] = {
+ FBNIC_QUEUE_REG_TEST(TWQ0_CTL, FBNIC_QUEUE_TWQ_CTL_RESET,
+ FBNIC_QUEUE_TWQ_CTL_RESET),
+ FBNIC_QUEUE_REG_TEST(TWQ0_PTRS, 0, ~0),
+ FBNIC_QUEUE_REG_TEST(TWQ0_SIZE, FBNIC_QUEUE_TWQ_SIZE_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(TWQ0_BAL, FBNIC_QUEUE_BAL_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(TWQ0_BAH, ~0, ~0),
+ FBNIC_QUEUE_REG_TEST(TWQ1_CTL, FBNIC_QUEUE_TWQ_CTL_RESET,
+ FBNIC_QUEUE_TWQ_CTL_RESET),
+ FBNIC_QUEUE_REG_TEST(TWQ1_PTRS, 0, ~0),
+ FBNIC_QUEUE_REG_TEST(TWQ1_SIZE, FBNIC_QUEUE_TWQ_SIZE_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(TWQ1_BAL, FBNIC_QUEUE_BAL_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(TWQ1_BAH, ~0, ~0),
+ FBNIC_QUEUE_REG_TEST(TCQ_CTL, FBNIC_QUEUE_TCQ_CTL_RESET,
+ FBNIC_QUEUE_TCQ_CTL_RESET),
+ FBNIC_QUEUE_REG_TEST(TCQ_PTRS, 0, ~0),
+ FBNIC_QUEUE_REG_TEST(TCQ_SIZE, FBNIC_QUEUE_TCQ_SIZE_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(TCQ_BAL, FBNIC_QUEUE_BAL_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(TCQ_BAH, ~0, ~0),
+ FBNIC_QUEUE_REG_TEST(RCQ_CTL, FBNIC_QUEUE_RCQ_CTL_RESET,
+ FBNIC_QUEUE_RCQ_CTL_RESET),
+ FBNIC_QUEUE_REG_TEST(RCQ_PTRS, 0, ~0),
+ FBNIC_QUEUE_REG_TEST(RCQ_SIZE, FBNIC_QUEUE_RCQ_SIZE_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(RCQ_BAL, FBNIC_QUEUE_BAL_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(RCQ_BAH, ~0, ~0),
+ FBNIC_QUEUE_REG_TEST(BDQ_CTL, FBNIC_QUEUE_BDQ_CTL_RESET,
+ FBNIC_QUEUE_BDQ_CTL_RESET),
+ FBNIC_QUEUE_REG_TEST(BDQ_HPQ_PTRS, 0, ~0),
+ FBNIC_QUEUE_REG_TEST(BDQ_HPQ_SIZE, FBNIC_QUEUE_BDQ_SIZE_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(BDQ_HPQ_BAL, FBNIC_QUEUE_BAL_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(BDQ_HPQ_BAH, ~0, ~0),
+ FBNIC_QUEUE_REG_TEST(BDQ_PPQ_PTRS, 0, ~0),
+ FBNIC_QUEUE_REG_TEST(BDQ_PPQ_SIZE, FBNIC_QUEUE_BDQ_SIZE_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(BDQ_PPQ_BAL, FBNIC_QUEUE_BAL_MASK, ~0),
+ FBNIC_QUEUE_REG_TEST(BDQ_PPQ_BAH, ~0, ~0),
+};
+
+static enum fbnic_reg_self_test_codes
+fbnic_csr_reg_pattern_test(struct fbnic_dev *fbd, int index,
+ const struct fbnic_csr_reg_test_data *test_data)
+{
+ static const u32 pattern[] = { ~0, 0x5A5A5A5A, 0xA5A5A5A5, 0};
+ enum fbnic_reg_self_test_codes reg;
+ int i;
+
+ reg = test_data->reg + test_data->reg_offset * index;
+ for (i = 0; i < ARRAY_SIZE(pattern); i++) {
+ u32 val = pattern[i] & test_data->write;
+ u32 result;
+
+ wr32(fbd, reg, val);
+ result = rd32(fbd, reg);
+ val &= test_data->read;
+
+ if (result == val)
+ continue;
+
+ dev_err(fbd->dev,
+ "%s: reg 0x%06X failed, expected 0x%08X received 0x%08X\n",
+ __func__, reg, val, result);
+
+ /* Note that FBNIC_INTR_STATUS(0) could be tested and fail
+ * and the result would not be reported since the register
+ * offset is 0. However as that register isn't included in
+ * the register test that isn't an issue.
+ */
+ return reg;
+ }
+
+ return FBNIC_REG_TEST_SUCCESS;
+}
+
+/**
+ * fbnic_csr_regs_test() - Verify behavior of NIC registers
+ * @fbd: device to test
+ *
+ * This function is meant to test the bit values of various registers in
+ * the NIC device. Specifically this test will verify which bits are
+ * writable and which ones are not. It will write varying patterns of bits
+ * to the registers testing for sticky bits, or bits that are writable but
+ * should not be.
+ *
+ * Return: FBNIC_REG_TEST_SUCCESS on success, register number on failure
+ **/
+enum fbnic_reg_self_test_codes fbnic_csr_regs_test(struct fbnic_dev *fbd)
+{
+ const struct fbnic_csr_reg_test_data *test_data;
+
+ for (test_data = pattern_test;
+ test_data < pattern_test + ARRAY_SIZE(pattern_test); test_data++) {
+ u32 i;
+
+ for (i = 0; i < test_data->array_len; i++) {
+ enum fbnic_reg_self_test_codes reg =
+ fbnic_csr_reg_pattern_test(fbd, i, test_data);
+
+ if (reg)
+ return reg;
+ }
+ }
+
+ return FBNIC_REG_TEST_SUCCESS;
+}
#define FBNIC_STATS_LEN \
(FBNIC_HW_STATS_LEN + FBNIC_XDP_STATS_LEN * FBNIC_MAX_XDPQS)
+enum fbnic_self_test_results {
+ TEST_REG = 0,
+};
+
+static const char fbnic_gstrings_self_test[][ETH_GSTRING_LEN] = {
+ [TEST_REG] = "Register test (offline)",
+};
+
+#define FBNIC_TEST_LEN ARRAY_SIZE(fbnic_gstrings_self_test)
+
static void
fbnic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
{
for (i = 0; i < FBNIC_MAX_XDPQS; i++)
fbnic_get_xdp_queue_strings(&data, i);
break;
+ case ETH_SS_TEST:
+ memcpy(data, fbnic_gstrings_self_test,
+ sizeof(fbnic_gstrings_self_test));
+ break;
}
}
switch (sset) {
case ETH_SS_STATS:
return FBNIC_STATS_LEN;
+ case ETH_SS_TEST:
+ return FBNIC_TEST_LEN;
default:
return -EOPNOTSUPP;
}
return 0;
}
+static int fbnic_ethtool_regs_test(struct net_device *netdev, u64 *data)
+{
+ struct fbnic_net *fbn = netdev_priv(netdev);
+ struct fbnic_dev *fbd = fbn->fbd;
+
+ *data = fbnic_csr_regs_test(fbd);
+
+ return !!*data;
+}
+
+static void fbnic_self_test(struct net_device *netdev,
+ struct ethtool_test *eth_test, u64 *data)
+{
+ bool if_running = netif_running(netdev);
+
+ if (!(eth_test->flags & ETH_TEST_FL_OFFLINE)) {
+ data[TEST_REG] = 0;
+ return;
+ }
+
+ if (if_running)
+ netif_close(netdev);
+
+ if (fbnic_ethtool_regs_test(netdev, &data[TEST_REG]))
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+
+ if (if_running && netif_open(netdev, NULL)) {
+ netdev_err(netdev,
+ "Failed to re-initialize hardware following test\n");
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+ }
+}
+
static void fbnic_get_channels(struct net_device *netdev,
struct ethtool_channels *ch)
{
.get_pause_stats = fbnic_get_pause_stats,
.get_pauseparam = fbnic_phylink_get_pauseparam,
.set_pauseparam = fbnic_phylink_set_pauseparam,
+ .self_test = fbnic_self_test,
.get_strings = fbnic_get_strings,
.get_ethtool_stats = fbnic_get_ethtool_stats,
.get_sset_count = fbnic_get_sset_count,