[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250213035529.2402283-3-shaojijie@huawei.com>
Date: Thu, 13 Feb 2025 11:55:24 +0800
From: Jijie Shao <shaojijie@...wei.com>
To: <davem@...emloft.net>, <edumazet@...gle.com>, <kuba@...nel.org>,
<pabeni@...hat.com>, <andrew+netdev@...n.ch>, <horms@...nel.org>
CC: <shenjian15@...wei.com>, <wangpeiyang1@...wei.com>,
<liuyonglong@...wei.com>, <chenhao418@...wei.com>, <sudongming1@...wei.com>,
<xujunsheng@...wei.com>, <shiyongbang@...wei.com>, <libaihan@...wei.com>,
<jonathan.cameron@...wei.com>, <shameerali.kolothum.thodi@...wei.com>,
<salil.mehta@...wei.com>, <netdev@...r.kernel.org>,
<linux-kernel@...r.kernel.org>, <shaojijie@...wei.com>
Subject: [PATCH net-next 2/7] net: hibmcge: Add self test supported in this module
This patch supports many self test: Mac, SerDes and Phy.
To implement self test, this patch implements a simple packet sending and
receiving function in the driver. By sending a packet in a specific format,
driver considers that the test is successful if the packet is received.
Otherwise, the test fails.
The SerDes hardware is on the BMC side, Therefore, when the SerDes loopback
need enabled, driver notifies the BMC through an event message.
Signed-off-by: Jijie Shao <shaojijie@...wei.com>
---
.../ethernet/hisilicon/hibmcge/hbg_common.h | 10 +
.../ethernet/hisilicon/hibmcge/hbg_ethtool.c | 192 +++++++++++++++++-
.../net/ethernet/hisilicon/hibmcge/hbg_hw.c | 6 +
.../net/ethernet/hisilicon/hibmcge/hbg_hw.h | 1 +
.../net/ethernet/hisilicon/hibmcge/hbg_reg.h | 1 +
.../net/ethernet/hisilicon/hibmcge/hbg_txrx.c | 6 +
6 files changed, 208 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
index 920514a8e29a..761137861c8d 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
@@ -81,6 +81,9 @@ enum hbg_hw_event_type {
HBG_HW_EVENT_NONE = 0,
HBG_HW_EVENT_INIT, /* driver is loading */
HBG_HW_EVENT_RESET,
+
+ HBG_HW_EVENT_SERDES_LOOPBACK_ENABLE = 4,
+ HBG_HW_EVENT_SERDES_LOOPBACK_DISABLE = 5,
};
struct hbg_dev_specs {
@@ -249,8 +252,13 @@ struct hbg_stats {
u64 tx_timeout_cnt;
u64 tx_dma_err_cnt;
+
+ u64 self_test_rx_pkt_cnt;
};
+typedef void (*self_test_pkt_recv)(struct net_device *ndev,
+ struct sk_buff *skb);
+
struct hbg_priv {
struct net_device *netdev;
struct pci_dev *pdev;
@@ -266,6 +274,8 @@ struct hbg_priv {
struct hbg_user_def user_def;
struct hbg_stats stats;
struct delayed_work service_task;
+
+ self_test_pkt_recv self_test_pkt_recv_fn;
};
#endif
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c
index f5be8d0ef611..f3980e5bec2e 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c
@@ -8,6 +8,17 @@
#include "hbg_err.h"
#include "hbg_ethtool.h"
#include "hbg_hw.h"
+#include "hbg_txrx.h"
+
+#define HBG_LOOP_DATA_SIZE 0x100
+#define HBG_LOOP_MAX_WAIT_TIME 2000
+#define HBG_LOOP_WAIT_TIME_STEP 100
+
+struct hibmcge_self_test {
+ char desc[ETH_GSTRING_LEN];
+ int (*enable)(struct hbg_priv *priv, bool enable);
+ int (*fn)(struct net_device *ndev);
+};
struct hbg_ethtool_stats {
char name[ETH_GSTRING_LEN];
@@ -309,6 +320,167 @@ static int hbg_ethtool_reset(struct net_device *netdev, u32 *flags)
return hbg_reset(priv);
}
+static int hbg_test_mac_loopback_enable(struct hbg_priv *priv, bool enable)
+{
+ hbg_hw_loop_enable(priv, enable);
+ return 0;
+}
+
+static int hbg_test_serdes_loopback_enable(struct hbg_priv *priv, bool enable)
+{
+ u32 event = enable ? HBG_HW_EVENT_SERDES_LOOPBACK_ENABLE :
+ HBG_HW_EVENT_SERDES_LOOPBACK_DISABLE;
+
+ return hbg_hw_event_notify(priv, event);
+}
+
+static int hbg_test_phy_loopback_enable(struct hbg_priv *priv, bool enable)
+{
+ return phy_loopback(priv->mac.phydev, enable);
+}
+
+static int hbg_self_test_pkt_send(struct net_device *ndev)
+{
+ struct hbg_priv *priv = netdev_priv(ndev);
+ struct sk_buff *skb = NULL;
+ int ret;
+ u32 i;
+
+ skb = netdev_alloc_skb(priv->netdev, HBG_LOOP_DATA_SIZE + NET_SKB_PAD);
+ if (!skb)
+ return -ENOMEM;
+ skb_reset_mac_header(skb);
+
+ skb_put(skb, HBG_LOOP_DATA_SIZE);
+ for (i = 0; i < HBG_LOOP_DATA_SIZE; i++)
+ skb->data[i] = i & 0xFF;
+
+ ret = hbg_net_start_xmit(skb, ndev);
+ if (ret != NETDEV_TX_OK) {
+ dev_kfree_skb_any(skb);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void hbg_self_test_pkt_recv(struct net_device *ndev, struct sk_buff *skb)
+{
+ struct hbg_priv *priv = netdev_priv(ndev);
+ u32 i;
+
+ skb_reserve(skb, HBG_PACKET_HEAD_SIZE + NET_IP_ALIGN);
+ if (skb_tailroom(skb) < HBG_LOOP_DATA_SIZE)
+ return;
+
+ skb_put(skb, HBG_LOOP_DATA_SIZE);
+
+ for (i = 0; i < HBG_LOOP_DATA_SIZE; i++)
+ if (skb->data[i] != (i & 0xFF))
+ return;
+
+ priv->stats.self_test_rx_pkt_cnt++;
+}
+
+static int hbg_test_loopback(struct net_device *ndev)
+{
+ struct hbg_priv *priv = netdev_priv(ndev);
+ u32 wait_time = 0;
+ int ret;
+
+ priv->stats.self_test_rx_pkt_cnt = 0;
+ ret = hbg_self_test_pkt_send(priv->netdev);
+ if (ret)
+ return ret;
+
+ do {
+ if (priv->stats.self_test_rx_pkt_cnt)
+ return 0;
+
+ msleep(HBG_LOOP_WAIT_TIME_STEP);
+ wait_time += HBG_LOOP_WAIT_TIME_STEP;
+ } while (wait_time < HBG_LOOP_MAX_WAIT_TIME);
+
+ return -ETIMEDOUT;
+}
+
+static int hbg_test_netif_carrier(struct net_device *ndev)
+{
+ return netif_carrier_ok(ndev) ? 0 : -ENOLINK;
+}
+
+/* Only support test in full duplex mode. */
+static int hbg_test_full_duplex(struct net_device *ndev)
+{
+ return ndev->phydev->duplex == DUPLEX_FULL ? 0 : -EINVAL;
+}
+
+const struct hibmcge_self_test hbg_selftests[] = {
+ {
+ .desc = "Carrier ",
+ .fn = hbg_test_netif_carrier,
+ }, {
+ .desc = "Full Duplex ",
+ .fn = hbg_test_full_duplex,
+ }, {
+ .desc = "MAC loopback ",
+ .enable = hbg_test_mac_loopback_enable,
+ .fn = hbg_test_loopback,
+ }, {
+ .desc = "Serdes loopback ",
+ .enable = hbg_test_serdes_loopback_enable,
+ .fn = hbg_test_loopback,
+ }, {
+ .desc = "Phy loopback ",
+ .enable = hbg_test_phy_loopback_enable,
+ .fn = hbg_test_loopback,
+ },
+};
+
+static void hbg_ethtool_self_test(struct net_device *netdev,
+ struct ethtool_test *etest, u64 *buf)
+{
+ struct hbg_priv *priv = netdev_priv(netdev);
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(hbg_selftests); i++)
+ buf[i] = -ENOEXEC;
+
+ if (!(etest->flags & ETH_TEST_FL_OFFLINE)) {
+ netdev_err(netdev, "Only offline tests are supported\n");
+ etest->flags |= ETH_TEST_FL_FAILED;
+ return;
+ }
+
+ if (!netif_running(netdev)) {
+ netdev_err(netdev, "port is down, can not do test\n");
+ etest->flags |= ETH_TEST_FL_FAILED;
+ return;
+ }
+
+ hbg_hw_set_mac_filter_enable(priv, false);
+ priv->self_test_pkt_recv_fn = hbg_self_test_pkt_recv;
+ for (i = 0; i < ARRAY_SIZE(hbg_selftests); i++) {
+ if (hbg_selftests[i].enable) {
+ buf[i] = hbg_selftests[i].enable(priv, true);
+ if (buf[i]) {
+ etest->flags |= ETH_TEST_FL_FAILED;
+ continue;
+ }
+ }
+
+ buf[i] = hbg_selftests[i].fn(netdev);
+ if (buf[i])
+ etest->flags |= ETH_TEST_FL_FAILED;
+
+ if (hbg_selftests[i].enable)
+ hbg_selftests[i].enable(priv, false);
+ }
+
+ priv->self_test_pkt_recv_fn = NULL;
+ hbg_hw_set_mac_filter_enable(priv, priv->filter.enabled);
+}
+
static void hbg_update_stats_by_info(struct hbg_priv *priv,
const struct hbg_ethtool_stats *info,
u32 info_len)
@@ -340,10 +512,12 @@ void hbg_update_stats(struct hbg_priv *priv)
static int hbg_ethtool_get_sset_count(struct net_device *netdev, int stringset)
{
- if (stringset != ETH_SS_STATS)
- return -EOPNOTSUPP;
+ if (stringset == ETH_SS_STATS)
+ return ARRAY_SIZE(hbg_ethtool_stats_info);
+ else if (stringset == ETH_SS_TEST)
+ return ARRAY_SIZE(hbg_selftests);
- return ARRAY_SIZE(hbg_ethtool_stats_info);
+ return -EOPNOTSUPP;
}
static void hbg_ethtool_get_strings(struct net_device *netdev,
@@ -351,11 +525,12 @@ static void hbg_ethtool_get_strings(struct net_device *netdev,
{
u32 i;
- if (stringset != ETH_SS_STATS)
- return;
-
- for (i = 0; i < ARRAY_SIZE(hbg_ethtool_stats_info); i++)
- ethtool_puts(&data, hbg_ethtool_stats_info[i].name);
+ if (stringset == ETH_SS_STATS)
+ for (i = 0; i < ARRAY_SIZE(hbg_ethtool_stats_info); i++)
+ ethtool_puts(&data, hbg_ethtool_stats_info[i].name);
+ else if (stringset == ETH_SS_TEST)
+ for (i = 0; i < ARRAY_SIZE(hbg_selftests); i++)
+ ethtool_puts(&data, hbg_selftests[i].desc);
}
static void hbg_ethtool_get_stats(struct net_device *netdev,
@@ -487,6 +662,7 @@ static const struct ethtool_ops hbg_ethtool_ops = {
.get_eth_mac_stats = hbg_ethtool_get_eth_mac_stats,
.get_eth_ctrl_stats = hbg_ethtool_get_eth_ctrl_stats,
.get_rmon_stats = hbg_ethtool_get_rmon_stats,
+ .self_test = hbg_ethtool_self_test,
};
void hbg_ethtool_set_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
index e7798f213645..d978535e06a0 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
@@ -177,6 +177,12 @@ void hbg_hw_set_mtu(struct hbg_priv *priv, u16 mtu)
hbg_hw_set_mac_max_frame_len(priv, frame_len);
}
+void hbg_hw_loop_enable(struct hbg_priv *priv, u32 enable)
+{
+ hbg_reg_write_field(priv, HBG_REG_LOOP_REG_ADDR,
+ HBG_REG_CF_CG2MI_LP_EN_B, enable);
+}
+
void hbg_hw_mac_enable(struct hbg_priv *priv, u32 enable)
{
hbg_reg_write_field(priv, HBG_REG_PORT_ENABLE_ADDR,
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
index a4a049b5121d..fc216fcfae06 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.h
@@ -50,6 +50,7 @@ void hbg_hw_irq_clear(struct hbg_priv *priv, u32 mask);
bool hbg_hw_irq_is_enabled(struct hbg_priv *priv, u32 mask);
void hbg_hw_irq_enable(struct hbg_priv *priv, u32 mask, bool enable);
void hbg_hw_set_mtu(struct hbg_priv *priv, u16 mtu);
+void hbg_hw_loop_enable(struct hbg_priv *priv, u32 enable);
void hbg_hw_mac_enable(struct hbg_priv *priv, u32 enable);
void hbg_hw_set_uc_addr(struct hbg_priv *priv, u64 mac_addr, u32 index);
u32 hbg_hw_get_fifo_used_num(struct hbg_priv *priv, enum hbg_dir dir);
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
index 106d0e0408ba..d6d91fbe220c 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
@@ -108,6 +108,7 @@
#define HBG_REG_MODE_CHANGE_EN_ADDR (HBG_REG_SGMII_BASE + 0x01B4)
#define HBG_REG_MODE_CHANGE_EN_B BIT(0)
#define HBG_REG_LOOP_REG_ADDR (HBG_REG_SGMII_BASE + 0x01DC)
+#define HBG_REG_CF_CG2MI_LP_EN_B BIT(2)
#define HBG_REG_RECV_CTRL_ADDR (HBG_REG_SGMII_BASE + 0x01E0)
#define HBG_REG_RECV_CTRL_STRIP_PAD_EN_B BIT(3)
#define HBG_REG_VLAN_CODE_ADDR (HBG_REG_SGMII_BASE + 0x01E8)
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
index 35dd3512d00e..8c631a9bcb6b 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
@@ -413,6 +413,12 @@ static int hbg_napi_rx_poll(struct napi_struct *napi, int budget)
rx_desc = (struct hbg_rx_desc *)buffer->skb->data;
pkt_len = FIELD_GET(HBG_RX_DESC_W2_PKT_LEN_M, rx_desc->word2);
+ if (unlikely(priv->self_test_pkt_recv_fn)) {
+ priv->self_test_pkt_recv_fn(priv->netdev, buffer->skb);
+ hbg_buffer_free(buffer);
+ goto next_buffer;
+ }
+
if (unlikely(!hbg_rx_pkt_check(priv, rx_desc))) {
hbg_buffer_free(buffer);
goto next_buffer;
--
2.33.0
Powered by blists - more mailing lists