summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2026-03-12 14:13:50 +0300
committerPaolo Abeni <pabeni@redhat.com>2026-03-12 14:13:50 +0300
commit52e4d5da6db788c3abc5bbb0e044e761b3540a30 (patch)
treefa85034d35df564a9223c7337f2b40fe2dceb3a6
parent0de607dc4fd80ede3b2a35e8a72f99c7a0bbc321 (diff)
parent00608d02ddf04b49c14801f4b0581b1b937bc766 (diff)
downloadlinux-52e4d5da6db788c3abc5bbb0e044e761b3540a30.tar.xz
Merge branch 'net-hinic3-pf-initialization'
Fan Gong says: ==================== net: hinic3: PF initialization This is [2/3] part of hinic3 Ethernet driver second submission. With this patch hinic3 becomes a complete Ethernet driver with pf and vf. Add cmdq detailed-response interfaces. Add dump interfaces for cmdq, aeq, ceq and mailbox. Add msg_send_lock for message sending concurrency. Add PF device support and chip_present_flag to check cards. Add rx vlan offload support. Add PF FLR wait and timeout handling. Add 5 ethtool ops for information of driver and link. v1: https://lore.kernel.org/netdev/cover.1771916043.git.zhuyikai1@h-partners.com/ v2: https://lore.kernel.org/netdev/cover.1772697509.git.zhuyikai1@h-partners.com/ ==================== Link: https://patch.msgid.link/cover.1773062356.git.zhuyikai1@h-partners.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-rw-r--r--drivers/net/ethernet/huawei/hinic3/Makefile1
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.c195
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.h15
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_common.c6
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_common.h1
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_csr.h2
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_eqs.c65
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_eqs.h5
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c425
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.c47
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.h8
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c69
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h2
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h12
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c9
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h1
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c23
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_lld.c1
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_main.c3
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c39
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_mbox.h4
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h16
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c16
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c77
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h110
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h3
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_pci_id_tbl.h1
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_rss.c2
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_rx.c15
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_rx.h3
30 files changed, 1146 insertions, 30 deletions
diff --git a/drivers/net/ethernet/huawei/hinic3/Makefile b/drivers/net/ethernet/huawei/hinic3/Makefile
index 26c05ecf31c9..4ebad3a4f943 100644
--- a/drivers/net/ethernet/huawei/hinic3/Makefile
+++ b/drivers/net/ethernet/huawei/hinic3/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_HINIC3) += hinic3.o
hinic3-objs := hinic3_cmdq.o \
hinic3_common.o \
hinic3_eqs.o \
+ hinic3_ethtool.o \
hinic3_filter.o \
hinic3_hw_cfg.o \
hinic3_hw_comm.o \
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.c b/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.c
index bc5b80c31693..1b0ba244a68f 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.c
@@ -6,12 +6,14 @@
#include <linux/dma-mapping.h>
#include "hinic3_cmdq.h"
+#include "hinic3_eqs.h"
#include "hinic3_hwdev.h"
#include "hinic3_hwif.h"
#include "hinic3_mbox.h"
#define CMDQ_BUF_SIZE 2048
#define CMDQ_WQEBB_SIZE 64
+#define CMDQ_WQE_HEAD_LEN 32
#define CMDQ_CMD_TIMEOUT 5000
#define CMDQ_ENABLE_WAIT_TIMEOUT 300
@@ -61,6 +63,10 @@
#define CMDQ_DB_HEAD_SET(val, member) \
FIELD_PREP(CMDQ_DB_HEAD_##member##_MASK, val)
+#define SAVED_DATA_ARM_MASK BIT(31)
+#define SAVED_DATA_SET(val, member) \
+ FIELD_PREP(SAVED_DATA_##member##_MASK, val)
+
#define CMDQ_CEQE_TYPE_MASK GENMASK(2, 0)
#define CMDQ_CEQE_GET(val, member) \
FIELD_GET(CMDQ_CEQE_##member##_MASK, le32_to_cpu(val))
@@ -84,6 +90,10 @@ enum cmdq_data_format {
CMDQ_DATA_DIRECT = 1,
};
+enum cmdq_scmd_type {
+ CMDQ_SET_ARM_CMD = 2,
+};
+
enum cmdq_ctrl_sect_len {
CMDQ_CTRL_SECT_LEN = 1,
CMDQ_CTRL_DIRECT_SECT_LEN = 2,
@@ -106,6 +116,20 @@ enum cmdq_cmd_type {
#define CMDQ_WQE_NUM_WQEBBS 1
+static void hinic3_dump_cmdq_wqe_head(struct hinic3_hwdev *hwdev,
+ struct cmdq_wqe *wqe)
+{
+ u32 *data = (u32 *)wqe;
+ u32 i;
+
+ for (i = 0; i < (CMDQ_WQE_HEAD_LEN / sizeof(u32)); i += 0x4) {
+ dev_dbg(hwdev->dev,
+ "wqe data: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
+ *(data + i), *(data + i + 0x1), *(data + i + 0x2),
+ *(data + i + 0x3));
+ }
+}
+
static struct cmdq_wqe *cmdq_read_wqe(struct hinic3_wq *wq, u16 *ci)
{
if (hinic3_wq_get_used(wq) == 0)
@@ -166,6 +190,11 @@ static void cmdq_clear_cmd_buf(struct hinic3_cmdq_cmd_info *cmd_info,
hinic3_free_cmd_buf(hwdev, cmd_info->buf_in);
cmd_info->buf_in = NULL;
}
+
+ if (cmd_info->buf_out) {
+ hinic3_free_cmd_buf(hwdev, cmd_info->buf_out);
+ cmd_info->buf_out = NULL;
+ }
}
static void clear_wqe_complete_bit(struct hinic3_cmdq *cmdq,
@@ -189,6 +218,20 @@ static void clear_wqe_complete_bit(struct hinic3_cmdq *cmdq,
hinic3_wq_put_wqebbs(&cmdq->wq, CMDQ_WQE_NUM_WQEBBS);
}
+static int cmdq_arm_ceq_handler(struct hinic3_cmdq *cmdq,
+ struct cmdq_wqe *wqe, u16 ci)
+{
+ struct cmdq_ctrl *ctrl = &wqe->wqe_scmd.ctrl;
+ __le32 ctrl_info = ctrl->ctrl_info;
+
+ if (!CMDQ_WQE_COMPLETED(ctrl_info))
+ return -EBUSY;
+
+ clear_wqe_complete_bit(cmdq, wqe, ci);
+
+ return 0;
+}
+
static void cmdq_update_cmd_status(struct hinic3_cmdq *cmdq, u16 prod_idx,
struct cmdq_wqe *wqe)
{
@@ -252,11 +295,17 @@ void hinic3_cmdq_ceq_handler(struct hinic3_hwdev *hwdev, __le32 ceqe_data)
case HINIC3_CMD_TYPE_TIMEOUT:
dev_warn(hwdev->dev, "Cmdq timeout, q_id: %u, ci: %u\n",
cmdq_type, ci);
+ hinic3_dump_cmdq_wqe_head(hwdev, wqe);
fallthrough;
case HINIC3_CMD_TYPE_FAKE_TIMEOUT:
cmdq_clear_cmd_buf(cmd_info, hwdev);
clear_wqe_complete_bit(cmdq, wqe, ci);
break;
+ case HINIC3_CMD_TYPE_SET_ARM:
+ /* arm_bit was set until here */
+ if (cmdq_arm_ceq_handler(cmdq, wqe, ci))
+ return;
+ break;
default:
/* only arm bit is using scmd wqe,
* the other wqe is lcmd
@@ -283,6 +332,18 @@ void hinic3_cmdq_ceq_handler(struct hinic3_hwdev *hwdev, __le32 ceqe_data)
}
}
+static int cmdq_params_valid(const struct hinic3_hwdev *hwdev,
+ const struct hinic3_cmd_buf *buf_in)
+{
+ if (le16_to_cpu(buf_in->size) > CMDQ_BUF_SIZE) {
+ dev_err(hwdev->dev, "Invalid CMDQ buffer size: 0x%x\n",
+ le16_to_cpu(buf_in->size));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int wait_cmdqs_enable(struct hinic3_cmdqs *cmdqs)
{
unsigned long end;
@@ -292,7 +353,8 @@ static int wait_cmdqs_enable(struct hinic3_cmdqs *cmdqs)
if (cmdqs->status & HINIC3_CMDQ_ENABLE)
return 0;
usleep_range(1000, 2000);
- } while (time_before(jiffies, end) && !cmdqs->disable_flag);
+ } while (time_before(jiffies, end) && !cmdqs->disable_flag &&
+ cmdqs->hwdev->chip_present_flag);
cmdqs->disable_flag = 1;
@@ -356,6 +418,7 @@ static void cmdq_prepare_wqe_ctrl(struct cmdq_wqe *wqe, u8 wrapped,
enum cmdq_bufdesc_len buf_len)
{
struct cmdq_header *hdr = CMDQ_WQE_HEADER(wqe);
+ __le32 saved_data = hdr->saved_data;
enum cmdq_ctrl_sect_len ctrl_len;
struct cmdq_wqe_lcmd *wqe_lcmd;
struct cmdq_wqe_scmd *wqe_scmd;
@@ -386,6 +449,11 @@ static void cmdq_prepare_wqe_ctrl(struct cmdq_wqe *wqe, u8 wrapped,
CMDQ_WQE_HDR_SET(3, COMPLETE_SECT_LEN) |
CMDQ_WQE_HDR_SET(ctrl_len, CTRL_LEN) |
CMDQ_WQE_HDR_SET(wrapped, HW_BUSY_BIT));
+
+ saved_data &= ~cpu_to_le32(SAVED_DATA_ARM_MASK);
+ if (cmd == CMDQ_SET_ARM_CMD && mod == MGMT_MOD_COMM)
+ saved_data |= cpu_to_le32(SAVED_DATA_SET(1, ARM));
+ hdr->saved_data = saved_data;
}
static void cmdq_set_lcmd_wqe(struct cmdq_wqe *wqe,
@@ -485,12 +553,15 @@ static int wait_cmdq_sync_cmd_completion(struct hinic3_cmdq *cmdq,
clear_cmd_info(cmd_info, saved_cmd_info);
spin_unlock_bh(&cmdq->cmdq_lock);
+ hinic3_dump_ceq_info(cmdq->hwdev);
+
return err;
}
-static int cmdq_sync_cmd_direct_resp(struct hinic3_cmdq *cmdq, u8 mod, u8 cmd,
- struct hinic3_cmd_buf *buf_in,
- __le64 *out_param)
+static int cmdq_sync_cmd_exec(struct hinic3_cmdq *cmdq, u8 mod, u8 cmd,
+ struct hinic3_cmd_buf *buf_in,
+ struct hinic3_cmd_buf *buf_out,
+ __le64 *out_param, u8 cmd_type, u8 wqe_cmd)
{
struct hinic3_cmdq_cmd_info *cmd_info, saved_cmd_info;
int cmpt_code = CMDQ_SEND_CMPT_CODE;
@@ -520,31 +591,35 @@ static int cmdq_sync_cmd_direct_resp(struct hinic3_cmdq *cmdq, u8 mod, u8 cmd,
cmd_info = &cmdq->cmd_infos[curr_prod_idx];
init_completion(&done);
refcount_inc(&buf_in->ref_cnt);
- cmd_info->cmd_type = HINIC3_CMD_TYPE_DIRECT_RESP;
+ if (buf_out)
+ refcount_inc(&buf_out->ref_cnt);
+
+ cmd_info->cmd_type = cmd_type;
cmd_info->done = &done;
cmd_info->errcode = &errcode;
cmd_info->direct_resp = out_param;
cmd_info->cmpt_code = &cmpt_code;
cmd_info->buf_in = buf_in;
+ if (buf_out)
+ cmd_info->buf_out = buf_out;
+
saved_cmd_info = *cmd_info;
- cmdq_set_lcmd_wqe(&wqe, CMDQ_CMD_DIRECT_RESP, buf_in, NULL,
+ cmdq_set_lcmd_wqe(&wqe, wqe_cmd, buf_in, buf_out,
wrapped, mod, cmd, curr_prod_idx);
cmdq_wqe_fill(curr_wqe, &wqe);
(cmd_info->cmdq_msg_id)++;
curr_msg_id = cmd_info->cmdq_msg_id;
- cmdq_set_db(cmdq, HINIC3_CMDQ_SYNC, next_prod_idx);
+ cmdq_set_db(cmdq, cmdq->cmdq_type, next_prod_idx);
spin_unlock_bh(&cmdq->cmdq_lock);
err = wait_cmdq_sync_cmd_completion(cmdq, cmd_info, &saved_cmd_info,
curr_msg_id, curr_prod_idx,
curr_wqe, CMDQ_CMD_TIMEOUT);
- if (err) {
+ if (err)
dev_err(cmdq->hwdev->dev,
"Cmdq sync command timeout, mod: %u, cmd: %u, prod idx: 0x%x\n",
mod, cmd, curr_prod_idx);
- err = -ETIMEDOUT;
- }
if (cmpt_code == CMDQ_FORCE_STOP_CMPT_CODE) {
dev_dbg(cmdq->hwdev->dev,
@@ -557,22 +632,114 @@ static int cmdq_sync_cmd_direct_resp(struct hinic3_cmdq *cmdq, u8 mod, u8 cmd,
return err ? err : errcode;
}
+static int cmdq_sync_cmd_direct_resp(struct hinic3_cmdq *cmdq, u8 mod, u8 cmd,
+ struct hinic3_cmd_buf *buf_in,
+ __le64 *out_param)
+{
+ return cmdq_sync_cmd_exec(cmdq, mod, cmd, buf_in, NULL, out_param,
+ HINIC3_CMD_TYPE_DIRECT_RESP,
+ CMDQ_CMD_DIRECT_RESP);
+}
+
+static int cmdq_sync_cmd_detail_resp(struct hinic3_cmdq *cmdq, u8 mod, u8 cmd,
+ struct hinic3_cmd_buf *buf_in,
+ struct hinic3_cmd_buf *buf_out,
+ __le64 *out_param)
+{
+ return cmdq_sync_cmd_exec(cmdq, mod, cmd, buf_in, buf_out, out_param,
+ HINIC3_CMD_TYPE_SGE_RESP,
+ CMDQ_CMD_SGE_RESP);
+}
+
+int hinic3_cmd_buf_pair_init(struct hinic3_hwdev *hwdev,
+ struct hinic3_cmd_buf_pair *pair)
+{
+ pair->in = hinic3_alloc_cmd_buf(hwdev);
+ if (!pair->in)
+ goto err_out;
+
+ pair->out = hinic3_alloc_cmd_buf(hwdev);
+ if (!pair->out)
+ goto err_free_cmd_buf_in;
+
+ return 0;
+
+err_free_cmd_buf_in:
+ hinic3_free_cmd_buf(hwdev, pair->in);
+err_out:
+ return -ENOMEM;
+}
+
+void hinic3_cmd_buf_pair_uninit(struct hinic3_hwdev *hwdev,
+ struct hinic3_cmd_buf_pair *pair)
+{
+ hinic3_free_cmd_buf(hwdev, pair->in);
+ hinic3_free_cmd_buf(hwdev, pair->out);
+}
+
int hinic3_cmdq_direct_resp(struct hinic3_hwdev *hwdev, u8 mod, u8 cmd,
struct hinic3_cmd_buf *buf_in, __le64 *out_param)
{
struct hinic3_cmdqs *cmdqs;
int err;
+ if (!hwdev->chip_present_flag)
+ return -ETIMEDOUT;
+
+ err = cmdq_params_valid(hwdev, buf_in);
+ if (err) {
+ dev_err(hwdev->dev, "Invalid CMDQ parameters\n");
+ goto err_out;
+ }
+
cmdqs = hwdev->cmdqs;
err = wait_cmdqs_enable(cmdqs);
if (err) {
dev_err(hwdev->dev, "Cmdq is disabled\n");
- return err;
+ goto err_out;
}
err = cmdq_sync_cmd_direct_resp(&cmdqs->cmdq[HINIC3_CMDQ_SYNC],
mod, cmd, buf_in, out_param);
+ if (err)
+ goto err_out;
+
+ return 0;
+err_out:
+ return err;
+}
+
+int hinic3_cmdq_detail_resp(struct hinic3_hwdev *hwdev, u8 mod, u8 cmd,
+ struct hinic3_cmd_buf *buf_in,
+ struct hinic3_cmd_buf *buf_out, __le64 *out_param)
+{
+ struct hinic3_cmdqs *cmdqs;
+ int err;
+
+ if (!hwdev->chip_present_flag)
+ return -ETIMEDOUT;
+
+ err = cmdq_params_valid(hwdev, buf_in);
+ if (err)
+ goto err_out;
+
+ cmdqs = hwdev->cmdqs;
+
+ err = wait_cmdqs_enable(cmdqs);
+ if (err) {
+ dev_err(hwdev->dev, "Cmdq is disabled\n");
+ goto err_out;
+ }
+
+ err = cmdq_sync_cmd_detail_resp(&cmdqs->cmdq[HINIC3_CMDQ_SYNC],
+ mod, cmd, buf_in, buf_out, out_param);
+ if (err)
+ goto err_out;
+
+ return 0;
+
+err_out:
return err;
}
@@ -758,7 +925,8 @@ static int init_cmdqs(struct hinic3_hwdev *hwdev)
static void cmdq_flush_sync_cmd(struct hinic3_cmdq_cmd_info *cmd_info)
{
- if (cmd_info->cmd_type != HINIC3_CMD_TYPE_DIRECT_RESP)
+ if (cmd_info->cmd_type != HINIC3_CMD_TYPE_DIRECT_RESP &&
+ cmd_info->cmd_type != HINIC3_CMD_TYPE_SGE_RESP)
return;
cmd_info->cmd_type = HINIC3_CMD_TYPE_FORCE_STOP;
@@ -785,7 +953,8 @@ static void hinic3_cmdq_flush_cmd(struct hinic3_cmdq *cmdq)
while (cmdq_read_wqe(&cmdq->wq, &ci)) {
hinic3_wq_put_wqebbs(&cmdq->wq, CMDQ_WQE_NUM_WQEBBS);
cmd_info = &cmdq->cmd_infos[ci];
- if (cmd_info->cmd_type == HINIC3_CMD_TYPE_DIRECT_RESP)
+ if (cmd_info->cmd_type == HINIC3_CMD_TYPE_DIRECT_RESP ||
+ cmd_info->cmd_type == HINIC3_CMD_TYPE_SGE_RESP)
cmdq_flush_sync_cmd(cmd_info);
}
spin_unlock_bh(&cmdq->cmdq_lock);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.h b/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.h
index f99c386a2780..29bad2fae7ba 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.h
@@ -85,7 +85,9 @@ enum hinic3_cmdq_status {
enum hinic3_cmdq_cmd_type {
HINIC3_CMD_TYPE_NONE,
+ HINIC3_CMD_TYPE_SET_ARM,
HINIC3_CMD_TYPE_DIRECT_RESP,
+ HINIC3_CMD_TYPE_SGE_RESP,
HINIC3_CMD_TYPE_FAKE_TIMEOUT,
HINIC3_CMD_TYPE_TIMEOUT,
HINIC3_CMD_TYPE_FORCE_STOP,
@@ -98,6 +100,11 @@ struct hinic3_cmd_buf {
refcount_t ref_cnt;
};
+struct hinic3_cmd_buf_pair {
+ struct hinic3_cmd_buf *in;
+ struct hinic3_cmd_buf *out;
+};
+
struct hinic3_cmdq_cmd_info {
enum hinic3_cmdq_cmd_type cmd_type;
struct completion *done;
@@ -107,6 +114,7 @@ struct hinic3_cmdq_cmd_info {
__le64 *direct_resp;
u64 cmdq_msg_id;
struct hinic3_cmd_buf *buf_in;
+ struct hinic3_cmd_buf *buf_out;
};
struct hinic3_cmdq {
@@ -146,8 +154,15 @@ void hinic3_free_cmd_buf(struct hinic3_hwdev *hwdev,
struct hinic3_cmd_buf *cmd_buf);
void hinic3_cmdq_ceq_handler(struct hinic3_hwdev *hwdev, __le32 ceqe_data);
+int hinic3_cmd_buf_pair_init(struct hinic3_hwdev *hwdev,
+ struct hinic3_cmd_buf_pair *pair);
+void hinic3_cmd_buf_pair_uninit(struct hinic3_hwdev *hwdev,
+ struct hinic3_cmd_buf_pair *pair);
int hinic3_cmdq_direct_resp(struct hinic3_hwdev *hwdev, u8 mod, u8 cmd,
struct hinic3_cmd_buf *buf_in, __le64 *out_param);
+int hinic3_cmdq_detail_resp(struct hinic3_hwdev *hwdev, u8 mod, u8 cmd,
+ struct hinic3_cmd_buf *buf_in,
+ struct hinic3_cmd_buf *buf_out, __le64 *out_param);
void hinic3_cmdq_flush_sync_cmd(struct hinic3_hwdev *hwdev);
int hinic3_reinit_cmdq_ctxts(struct hinic3_hwdev *hwdev);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_common.c b/drivers/net/ethernet/huawei/hinic3/hinic3_common.c
index fe4778d152cf..b28576debdc8 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_common.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_common.c
@@ -59,10 +59,14 @@ int hinic3_wait_for_timeout(void *priv_data, wait_cpl_handler handler,
enum hinic3_wait_return ret;
int err;
- err = read_poll_timeout(handler, ret, ret == HINIC3_WAIT_PROCESS_CPL,
+ err = read_poll_timeout(handler, ret,
+ !(ret & HINIC3_WAIT_PROCESS_WAITING),
wait_once_us, wait_total_ms * USEC_PER_MSEC,
false, priv_data);
+ if (ret == HINIC3_WAIT_PROCESS_ERR)
+ return -EIO;
+
return err;
}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_common.h b/drivers/net/ethernet/huawei/hinic3/hinic3_common.h
index a8fabfae90fb..c892439fa3cd 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_common.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_common.h
@@ -21,6 +21,7 @@ struct hinic3_dma_addr_align {
enum hinic3_wait_return {
HINIC3_WAIT_PROCESS_CPL = 0,
HINIC3_WAIT_PROCESS_WAITING = 1,
+ HINIC3_WAIT_PROCESS_ERR = 2,
};
struct hinic3_sge {
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_csr.h b/drivers/net/ethernet/huawei/hinic3/hinic3_csr.h
index f7083a6e7df9..0e32ff34919e 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_csr.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_csr.h
@@ -76,9 +76,11 @@
#define HINIC3_CSR_AEQ_CTRL_0_ADDR (HINIC3_CFG_REGS_FLAG + 0x200)
#define HINIC3_CSR_AEQ_CTRL_1_ADDR (HINIC3_CFG_REGS_FLAG + 0x204)
+#define HINIC3_CSR_AEQ_CONS_IDX_ADDR (HINIC3_CFG_REGS_FLAG + 0x208)
#define HINIC3_CSR_AEQ_PROD_IDX_ADDR (HINIC3_CFG_REGS_FLAG + 0x20C)
#define HINIC3_CSR_AEQ_CI_SIMPLE_INDIR_ADDR (HINIC3_CFG_REGS_FLAG + 0x50)
+#define HINIC3_CSR_CEQ_CONS_IDX_ADDR (HINIC3_CFG_REGS_FLAG + 0x288)
#define HINIC3_CSR_CEQ_PROD_IDX_ADDR (HINIC3_CFG_REGS_FLAG + 0x28c)
#define HINIC3_CSR_CEQ_CI_SIMPLE_INDIR_ADDR (HINIC3_CFG_REGS_FLAG + 0x54)
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_eqs.c b/drivers/net/ethernet/huawei/hinic3/hinic3_eqs.c
index 13a0c6b07660..b8ac1d7bd82b 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_eqs.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_eqs.c
@@ -56,6 +56,10 @@
#define EQ_CI_SIMPLE_INDIR_SET(val, member) \
FIELD_PREP(EQ_CI_SIMPLE_INDIR_##member##_MASK, val)
+#define EQ_CONS_IDX_REG_ADDR(eq) \
+ (((eq)->type == HINIC3_AEQ) ? \
+ HINIC3_CSR_AEQ_CONS_IDX_ADDR : HINIC3_CSR_CEQ_CONS_IDX_ADDR)
+
#define EQ_CI_SIMPLE_INDIR_REG_ADDR(eq) \
(((eq)->type == HINIC3_AEQ) ? \
HINIC3_CSR_AEQ_CI_SIMPLE_INDIR_ADDR : \
@@ -353,6 +357,7 @@ static irqreturn_t ceq_interrupt(int irq, void *data)
struct hinic3_eq *ceq = data;
int err;
+ ceq->soft_intr_jif = jiffies;
/* clear resend timer counters */
hinic3_msix_intr_clear_resend_bit(ceq->hwdev, ceq->msix_entry_idx,
EQ_MSIX_RESEND_TIMER_CLEAR);
@@ -713,6 +718,39 @@ void hinic3_aeqs_free(struct hinic3_hwdev *hwdev)
kfree(aeqs);
}
+void hinic3_dump_aeq_info(struct hinic3_hwdev *hwdev)
+{
+ const struct hinic3_aeq_elem *aeqe_pos;
+ u32 addr, ci, pi, ctrl0, idx;
+ struct hinic3_eq *eq;
+ int q_id;
+
+ for (q_id = 0; q_id < hwdev->aeqs->num_aeqs; q_id++) {
+ eq = &hwdev->aeqs->aeq[q_id];
+ /* Indirect access should set q_id first */
+ hinic3_hwif_write_reg(eq->hwdev->hwif,
+ HINIC3_EQ_INDIR_IDX_ADDR(eq->type),
+ eq->q_id);
+
+ addr = HINIC3_CSR_AEQ_CTRL_0_ADDR;
+
+ ctrl0 = hinic3_hwif_read_reg(hwdev->hwif, addr);
+
+ idx = hinic3_hwif_read_reg(hwdev->hwif,
+ HINIC3_EQ_INDIR_IDX_ADDR(eq->type));
+
+ addr = EQ_CONS_IDX_REG_ADDR(eq);
+ ci = hinic3_hwif_read_reg(hwdev->hwif, addr);
+ addr = EQ_PROD_IDX_REG_ADDR(eq);
+ pi = hinic3_hwif_read_reg(hwdev->hwif, addr);
+ aeqe_pos = get_curr_aeq_elem(eq);
+ dev_err(hwdev->dev,
+ "Aeq id: %d, idx: %u, ctrl0: 0x%08x, ci: 0x%08x, pi: 0x%x, work_state: 0x%x, wrap: %u, desc: 0x%x swci:0x%x\n",
+ q_id, idx, ctrl0, ci, pi, work_busy(&eq->aeq_work),
+ eq->wrapped, be32_to_cpu(aeqe_pos->desc), eq->cons_idx);
+ }
+}
+
int hinic3_ceqs_init(struct hinic3_hwdev *hwdev, u16 num_ceqs,
struct msix_entry *msix_entries)
{
@@ -773,3 +811,30 @@ void hinic3_ceqs_free(struct hinic3_hwdev *hwdev)
kfree(ceqs);
}
+
+void hinic3_dump_ceq_info(struct hinic3_hwdev *hwdev)
+{
+ struct hinic3_eq *eq;
+ u32 addr, ci, pi;
+ int q_id;
+
+ for (q_id = 0; q_id < hwdev->ceqs->num_ceqs; q_id++) {
+ eq = &hwdev->ceqs->ceq[q_id];
+ /* Indirect access should set q_id first */
+ hinic3_hwif_write_reg(eq->hwdev->hwif,
+ HINIC3_EQ_INDIR_IDX_ADDR(eq->type),
+ eq->q_id);
+
+ addr = EQ_CONS_IDX_REG_ADDR(eq);
+ ci = hinic3_hwif_read_reg(hwdev->hwif, addr);
+ addr = EQ_PROD_IDX_REG_ADDR(eq);
+ pi = hinic3_hwif_read_reg(hwdev->hwif, addr);
+ dev_err(hwdev->dev,
+ "Ceq id: %d, ci: 0x%08x, sw_ci: 0x%08x, pi: 0x%x, wrap: %u, ceqe: 0x%x\n",
+ q_id, ci, eq->cons_idx, pi,
+ eq->wrapped, be32_to_cpu(*get_curr_ceq_elem(eq)));
+
+ dev_err(hwdev->dev, "Ceq last response soft interrupt time: %u\n",
+ jiffies_to_msecs(jiffies - eq->soft_intr_jif));
+ }
+}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_eqs.h b/drivers/net/ethernet/huawei/hinic3/hinic3_eqs.h
index 005a6e0745b3..c0fa237b270b 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_eqs.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_eqs.h
@@ -56,6 +56,8 @@ struct hinic3_eq {
u16 msix_entry_idx;
char irq_name[HINIC3_EQ_IRQ_NAME_LEN];
struct work_struct aeq_work;
+
+ u64 soft_intr_jif;
};
struct hinic3_aeq_elem {
@@ -110,6 +112,8 @@ int hinic3_aeq_register_cb(struct hinic3_hwdev *hwdev,
hinic3_aeq_event_cb hwe_cb);
void hinic3_aeq_unregister_cb(struct hinic3_hwdev *hwdev,
enum hinic3_aeq_type event);
+void hinic3_dump_aeq_info(struct hinic3_hwdev *hwdev);
+
int hinic3_ceqs_init(struct hinic3_hwdev *hwdev, u16 num_ceqs,
struct msix_entry *msix_entries);
void hinic3_ceqs_free(struct hinic3_hwdev *hwdev);
@@ -118,5 +122,6 @@ int hinic3_ceq_register_cb(struct hinic3_hwdev *hwdev,
hinic3_ceq_event_cb callback);
void hinic3_ceq_unregister_cb(struct hinic3_hwdev *hwdev,
enum hinic3_ceq_event event);
+void hinic3_dump_ceq_info(struct hinic3_hwdev *hwdev);
#endif
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c b/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c
new file mode 100644
index 000000000000..90fc16288de9
--- /dev/null
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+
+#include "hinic3_lld.h"
+#include "hinic3_hw_comm.h"
+#include "hinic3_nic_dev.h"
+#include "hinic3_nic_cfg.h"
+
+#define HINIC3_MGMT_VERSION_MAX_LEN 32
+
+static void hinic3_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *info)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ u8 mgmt_ver[HINIC3_MGMT_VERSION_MAX_LEN];
+ struct pci_dev *pdev = nic_dev->pdev;
+ int err;
+
+ strscpy(info->driver, HINIC3_NIC_DRV_NAME, sizeof(info->driver));
+ strscpy(info->bus_info, pci_name(pdev), sizeof(info->bus_info));
+
+ err = hinic3_get_mgmt_version(nic_dev->hwdev, mgmt_ver,
+ HINIC3_MGMT_VERSION_MAX_LEN);
+ if (err) {
+ netdev_err(netdev, "Failed to get fw version\n");
+ return;
+ }
+
+ snprintf(info->fw_version, sizeof(info->fw_version), "%s", mgmt_ver);
+}
+
+static u32 hinic3_get_msglevel(struct net_device *netdev)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+ return nic_dev->msg_enable;
+}
+
+static void hinic3_set_msglevel(struct net_device *netdev, u32 data)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+ nic_dev->msg_enable = data;
+
+ netdev_dbg(netdev, "Set message level: 0x%x\n", data);
+}
+
+static const u32 hinic3_link_mode_ge[] = {
+ ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_10ge_base_r[] = {
+ ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
+ ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
+ ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_25ge_base_r[] = {
+ ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_40ge_base_r4[] = {
+ ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_50ge_base_r[] = {
+ ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_50ge_base_r2[] = {
+ ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_100ge_base_r[] = {
+ ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_100ge_base_r2[] = {
+ ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_100ge_base_r4[] = {
+ ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_200ge_base_r2[] = {
+ ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_200ge_base_r4[] = {
+ ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
+ ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
+};
+
+struct hw2ethtool_link_mode {
+ const u32 *link_mode_bit_arr;
+ u32 arr_size;
+ u32 speed;
+};
+
+static const struct hw2ethtool_link_mode
+ hw2ethtool_link_mode_table[LINK_MODE_MAX_NUMBERS] = {
+ [LINK_MODE_GE] = {
+ .link_mode_bit_arr = hinic3_link_mode_ge,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_ge),
+ .speed = SPEED_1000,
+ },
+ [LINK_MODE_10GE_BASE_R] = {
+ .link_mode_bit_arr = hinic3_link_mode_10ge_base_r,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_10ge_base_r),
+ .speed = SPEED_10000,
+ },
+ [LINK_MODE_25GE_BASE_R] = {
+ .link_mode_bit_arr = hinic3_link_mode_25ge_base_r,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_25ge_base_r),
+ .speed = SPEED_25000,
+ },
+ [LINK_MODE_40GE_BASE_R4] = {
+ .link_mode_bit_arr = hinic3_link_mode_40ge_base_r4,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_40ge_base_r4),
+ .speed = SPEED_40000,
+ },
+ [LINK_MODE_50GE_BASE_R] = {
+ .link_mode_bit_arr = hinic3_link_mode_50ge_base_r,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_50ge_base_r),
+ .speed = SPEED_50000,
+ },
+ [LINK_MODE_50GE_BASE_R2] = {
+ .link_mode_bit_arr = hinic3_link_mode_50ge_base_r2,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_50ge_base_r2),
+ .speed = SPEED_50000,
+ },
+ [LINK_MODE_100GE_BASE_R] = {
+ .link_mode_bit_arr = hinic3_link_mode_100ge_base_r,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_100ge_base_r),
+ .speed = SPEED_100000,
+ },
+ [LINK_MODE_100GE_BASE_R2] = {
+ .link_mode_bit_arr = hinic3_link_mode_100ge_base_r2,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_100ge_base_r2),
+ .speed = SPEED_100000,
+ },
+ [LINK_MODE_100GE_BASE_R4] = {
+ .link_mode_bit_arr = hinic3_link_mode_100ge_base_r4,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_100ge_base_r4),
+ .speed = SPEED_100000,
+ },
+ [LINK_MODE_200GE_BASE_R2] = {
+ .link_mode_bit_arr = hinic3_link_mode_200ge_base_r2,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_200ge_base_r2),
+ .speed = SPEED_200000,
+ },
+ [LINK_MODE_200GE_BASE_R4] = {
+ .link_mode_bit_arr = hinic3_link_mode_200ge_base_r4,
+ .arr_size = ARRAY_SIZE(hinic3_link_mode_200ge_base_r4),
+ .speed = SPEED_200000,
+ },
+};
+
+#define GET_SUPPORTED_MODE 0
+#define GET_ADVERTISED_MODE 1
+
+struct hinic3_link_settings {
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
+
+ u32 speed;
+ u8 duplex;
+ u8 port;
+ u8 autoneg;
+};
+
+#define HINIC3_ADD_SUPPORTED_LINK_MODE(ecmd, mode) \
+ set_bit(ETHTOOL_LINK_##mode##_BIT, (ecmd)->supported)
+#define HINIC3_ADD_ADVERTISED_LINK_MODE(ecmd, mode) \
+ set_bit(ETHTOOL_LINK_##mode##_BIT, (ecmd)->advertising)
+
+static void hinic3_add_speed_link_mode(unsigned long *bitmap, u32 mode)
+{
+ u32 i;
+
+ for (i = 0; i < hw2ethtool_link_mode_table[mode].arr_size; i++) {
+ if (hw2ethtool_link_mode_table[mode].link_mode_bit_arr[i] >=
+ __ETHTOOL_LINK_MODE_MASK_NBITS)
+ continue;
+
+ set_bit(hw2ethtool_link_mode_table[mode].link_mode_bit_arr[i],
+ bitmap);
+ }
+}
+
+/* Related to enum mag_cmd_port_speed */
+static const u32 hw_to_ethtool_speed[] = {
+ (u32)SPEED_UNKNOWN, SPEED_10, SPEED_100, SPEED_1000, SPEED_10000,
+ SPEED_25000, SPEED_40000, SPEED_50000, SPEED_100000, SPEED_200000
+};
+
+static void
+hinic3_add_ethtool_link_mode(struct hinic3_link_settings *link_settings,
+ u32 hw_link_mode, u32 name)
+{
+ unsigned long *advertising_mask = link_settings->advertising;
+ unsigned long *supported_mask = link_settings->supported;
+ u32 link_mode;
+
+ for (link_mode = 0; link_mode < LINK_MODE_MAX_NUMBERS; link_mode++) {
+ if (hw_link_mode & BIT(link_mode)) {
+ if (name == GET_SUPPORTED_MODE)
+ hinic3_add_speed_link_mode(supported_mask,
+ link_mode);
+ else
+ hinic3_add_speed_link_mode(advertising_mask,
+ link_mode);
+ }
+ }
+}
+
+static void
+hinic3_link_speed_set(struct net_device *netdev,
+ struct hinic3_link_settings *link_settings,
+ struct hinic3_nic_port_info *port_info)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ bool link_status_up;
+ int err;
+
+ if (port_info->supported_mode != LINK_MODE_UNKNOWN)
+ hinic3_add_ethtool_link_mode(link_settings,
+ port_info->supported_mode,
+ GET_SUPPORTED_MODE);
+ if (port_info->advertised_mode != LINK_MODE_UNKNOWN)
+ hinic3_add_ethtool_link_mode(link_settings,
+ port_info->advertised_mode,
+ GET_ADVERTISED_MODE);
+
+ err = hinic3_get_link_status(nic_dev->hwdev, &link_status_up);
+ if (!err && link_status_up) {
+ link_settings->speed =
+ port_info->speed < ARRAY_SIZE(hw_to_ethtool_speed) ?
+ hw_to_ethtool_speed[port_info->speed] :
+ (u32)SPEED_UNKNOWN;
+
+ link_settings->duplex = port_info->duplex;
+ } else {
+ link_settings->speed = (u32)SPEED_UNKNOWN;
+ link_settings->duplex = DUPLEX_UNKNOWN;
+ }
+}
+
+static void
+hinic3_link_port_type_set(struct hinic3_link_settings *link_settings,
+ u8 port_type)
+{
+ switch (port_type) {
+ case MAG_CMD_WIRE_TYPE_ELECTRIC:
+ HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_TP);
+ HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_TP);
+ link_settings->port = PORT_TP;
+ break;
+
+ case MAG_CMD_WIRE_TYPE_AOC:
+ case MAG_CMD_WIRE_TYPE_MM:
+ case MAG_CMD_WIRE_TYPE_SM:
+ HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_FIBRE);
+ HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_FIBRE);
+ link_settings->port = PORT_FIBRE;
+ break;
+
+ case MAG_CMD_WIRE_TYPE_COPPER:
+ HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_FIBRE);
+ HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_FIBRE);
+ link_settings->port = PORT_DA;
+ break;
+
+ case MAG_CMD_WIRE_TYPE_BACKPLANE:
+ HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_Backplane);
+ HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Backplane);
+ link_settings->port = PORT_NONE;
+ break;
+
+ default:
+ link_settings->port = PORT_OTHER;
+ break;
+ }
+}
+
+static int
+hinic3_get_link_pause_settings(struct net_device *netdev,
+ struct hinic3_link_settings *link_settings)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct hinic3_nic_pause_config nic_pause = {};
+ int err;
+
+ err = hinic3_get_pause_info(nic_dev, &nic_pause);
+ if (err) {
+ netdev_err(netdev, "Failed to get pause param from hw\n");
+ return err;
+ }
+
+ HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_Pause);
+ if (nic_pause.rx_pause && nic_pause.tx_pause) {
+ HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Pause);
+ } else if (nic_pause.tx_pause) {
+ HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings,
+ MODE_Asym_Pause);
+ } else if (nic_pause.rx_pause) {
+ HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Pause);
+ HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings,
+ MODE_Asym_Pause);
+ }
+
+ return 0;
+}
+
+static int
+hinic3_get_link_settings(struct net_device *netdev,
+ struct hinic3_link_settings *link_settings)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct hinic3_nic_port_info port_info = {};
+ int err;
+
+ err = hinic3_get_port_info(nic_dev->hwdev, &port_info);
+ if (err) {
+ netdev_err(netdev, "Failed to get port info\n");
+ return err;
+ }
+
+ hinic3_link_speed_set(netdev, link_settings, &port_info);
+
+ hinic3_link_port_type_set(link_settings, port_info.port_type);
+
+ link_settings->autoneg = port_info.autoneg_state == PORT_CFG_AN_ON ?
+ AUTONEG_ENABLE : AUTONEG_DISABLE;
+ if (port_info.autoneg_cap)
+ HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_Autoneg);
+ if (port_info.autoneg_state == PORT_CFG_AN_ON)
+ HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Autoneg);
+
+ if (!HINIC3_IS_VF(nic_dev->hwdev)) {
+ err = hinic3_get_link_pause_settings(netdev, link_settings);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+hinic3_get_link_ksettings(struct net_device *netdev,
+ struct ethtool_link_ksettings *link_settings)
+{
+ struct ethtool_link_settings *base = &link_settings->base;
+ struct hinic3_link_settings settings = {};
+ int err;
+
+ ethtool_link_ksettings_zero_link_mode(link_settings, supported);
+ ethtool_link_ksettings_zero_link_mode(link_settings, advertising);
+
+ err = hinic3_get_link_settings(netdev, &settings);
+ if (err)
+ return err;
+
+ bitmap_copy(link_settings->link_modes.supported, settings.supported,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+ bitmap_copy(link_settings->link_modes.advertising, settings.advertising,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+
+ base->autoneg = settings.autoneg;
+ base->speed = settings.speed;
+ base->duplex = settings.duplex;
+ base->port = settings.port;
+
+ return 0;
+}
+
+static const struct ethtool_ops hinic3_ethtool_ops = {
+ .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
+ ETHTOOL_COALESCE_PKT_RATE_RX_USECS,
+ .get_link_ksettings = hinic3_get_link_ksettings,
+ .get_drvinfo = hinic3_get_drvinfo,
+ .get_msglevel = hinic3_get_msglevel,
+ .set_msglevel = hinic3_set_msglevel,
+ .get_link = ethtool_op_get_link,
+};
+
+void hinic3_set_ethtool_ops(struct net_device *netdev)
+{
+ netdev->ethtool_ops = &hinic3_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.c
index ca60cf4b6282..2dfa4fb39627 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.c
@@ -17,6 +17,17 @@ static void hinic3_parse_pub_res_cap(struct hinic3_hwdev *hwdev,
{
cap->port_id = dev_cap->port_id;
cap->supp_svcs_bitmap = dev_cap->svc_cap_en;
+
+ cap->cos_valid_bitmap = dev_cap->valid_cos_bitmap;
+ cap->port_cos_valid_bitmap = dev_cap->port_cos_valid_bitmap;
+
+ if (type != HINIC3_FUNC_TYPE_VF)
+ cap->max_vf = dev_cap->max_vf;
+ else
+ cap->max_vf = 0;
+
+ dev_dbg(hwdev->dev, "Port_id: 0x%x, cos_bitmap: 0x%x, Max_vf: 0x%x\n",
+ cap->port_id, cap->cos_valid_bitmap, cap->max_vf);
}
static void hinic3_parse_l2nic_res_cap(struct hinic3_hwdev *hwdev,
@@ -28,6 +39,13 @@ static void hinic3_parse_l2nic_res_cap(struct hinic3_hwdev *hwdev,
nic_svc_cap->max_sqs = min(dev_cap->nic_max_sq_id + 1,
HINIC3_CFG_MAX_QP);
+
+ nic_svc_cap->max_rqs = min(dev_cap->nic_max_rq_id + 1,
+ HINIC3_CFG_MAX_QP);
+ nic_svc_cap->default_num_queues = dev_cap->nic_default_num_queues;
+
+ dev_dbg(hwdev->dev, "L2nic resource capbility, max_sqs: 0x%x, max_rqs: 0x%x\n",
+ nic_svc_cap->max_sqs, nic_svc_cap->max_rqs);
}
static void hinic3_parse_dev_cap(struct hinic3_hwdev *hwdev,
@@ -44,8 +62,8 @@ static void hinic3_parse_dev_cap(struct hinic3_hwdev *hwdev,
hinic3_parse_l2nic_res_cap(hwdev, cap, dev_cap, type);
}
-static int get_cap_from_fw(struct hinic3_hwdev *hwdev,
- enum hinic3_func_type type)
+static int hinic3_get_cap_from_fw(struct hinic3_hwdev *hwdev,
+ enum hinic3_func_type type)
{
struct mgmt_msg_params msg_params = {};
struct cfg_cmd_dev_cap dev_cap = {};
@@ -69,6 +87,29 @@ static int get_cap_from_fw(struct hinic3_hwdev *hwdev,
return 0;
}
+static int hinic3_get_dev_cap(struct hinic3_hwdev *hwdev)
+{
+ enum hinic3_func_type type = HINIC3_FUNC_TYPE(hwdev);
+ int err;
+
+ switch (type) {
+ case HINIC3_FUNC_TYPE_PF:
+ case HINIC3_FUNC_TYPE_VF:
+ err = hinic3_get_cap_from_fw(hwdev, type);
+ if (err) {
+ dev_err(hwdev->dev, "Failed to get FW capability\n");
+ return err;
+ }
+ break;
+ default:
+ dev_err(hwdev->dev, "Unsupported PCI Function type: %d\n",
+ type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int hinic3_init_irq_info(struct hinic3_hwdev *hwdev)
{
struct hinic3_cfg_mgmt_info *cfg_mgmt = hwdev->cfg_mgmt;
@@ -215,7 +256,7 @@ void hinic3_free_irq(struct hinic3_hwdev *hwdev, u32 irq_id)
int hinic3_init_capability(struct hinic3_hwdev *hwdev)
{
- return get_cap_from_fw(hwdev, HINIC3_FUNC_TYPE_VF);
+ return hinic3_get_dev_cap(hwdev);
}
bool hinic3_support_nic(struct hinic3_hwdev *hwdev)
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.h
index 58806199bf54..361c0b6a70f0 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.h
@@ -26,6 +26,8 @@ struct hinic3_irq_info {
struct hinic3_nic_service_cap {
u16 max_sqs;
+ u16 max_rqs;
+ u16 default_num_queues;
};
/* Device capabilities */
@@ -34,6 +36,12 @@ struct hinic3_dev_cap {
u16 supp_svcs_bitmap;
/* Physical port */
u8 port_id;
+
+ u8 cos_valid_bitmap;
+ u8 port_cos_valid_bitmap;
+ /* max number of VFs that PF supports */
+ u16 max_vf;
+
struct hinic3_nic_service_cap nic_svc_cap;
};
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
index ecfe6265954e..0dbf9cbe7f9a 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
@@ -292,6 +292,32 @@ int hinic3_set_cmdq_depth(struct hinic3_hwdev *hwdev, u16 cmdq_depth)
return 0;
}
+#define HINIC3_FLR_TIMEOUT 1000
+
+static enum hinic3_wait_return hinic3_check_flr_finish_handler(void *priv_data)
+{
+ struct hinic3_hwdev *hwdev = priv_data;
+ struct hinic3_hwif *hwif = hwdev->hwif;
+ enum hinic3_pf_status status;
+
+ if (!hwdev->chip_present_flag)
+ return HINIC3_WAIT_PROCESS_ERR;
+
+ status = hinic3_get_pf_status(hwif);
+ if (status == HINIC3_PF_STATUS_FLR_FINISH_FLAG) {
+ hinic3_set_pf_status(hwif, HINIC3_PF_STATUS_ACTIVE_FLAG);
+ return HINIC3_WAIT_PROCESS_CPL;
+ }
+
+ return HINIC3_WAIT_PROCESS_WAITING;
+}
+
+static int hinic3_wait_for_flr_finish(struct hinic3_hwdev *hwdev)
+{
+ return hinic3_wait_for_timeout(hwdev, hinic3_check_flr_finish_handler,
+ HINIC3_FLR_TIMEOUT, 0xa * USEC_PER_MSEC);
+}
+
#define HINIC3_WAIT_CMDQ_IDLE_TIMEOUT 5000
static enum hinic3_wait_return check_cmdq_stop_handler(void *priv_data)
@@ -300,6 +326,10 @@ static enum hinic3_wait_return check_cmdq_stop_handler(void *priv_data)
enum hinic3_cmdq_type cmdq_type;
struct hinic3_cmdqs *cmdqs;
+ /* Stop waiting when card unpresent */
+ if (!hwdev->chip_present_flag)
+ return HINIC3_WAIT_PROCESS_ERR;
+
cmdqs = hwdev->cmdqs;
for (cmdq_type = 0; cmdq_type < cmdqs->cmdq_num; cmdq_type++) {
if (!hinic3_cmdq_idle(&cmdqs->cmdq[cmdq_type]))
@@ -347,6 +377,9 @@ int hinic3_func_rx_tx_flush(struct hinic3_hwdev *hwdev)
int ret = 0;
int err;
+ if (!hwdev->chip_present_flag)
+ return 0;
+
err = wait_cmdq_stop(hwdev);
if (err) {
dev_warn(hwdev->dev, "CMDQ is still working, CMDQ timeout value is unreasonable\n");
@@ -382,6 +415,14 @@ int hinic3_func_rx_tx_flush(struct hinic3_hwdev *hwdev)
ret = err;
}
+ if (HINIC3_FUNC_TYPE(hwdev) != HINIC3_FUNC_TYPE_VF) {
+ err = hinic3_wait_for_flr_finish(hwdev);
+ if (err) {
+ dev_warn(hwdev->dev, "Wait firmware FLR timeout\n");
+ ret = err;
+ }
+ }
+
hinic3_toggle_doorbell(hwif, ENABLE_DOORBELL);
err = hinic3_reinit_cmdq_ctxts(hwdev);
@@ -539,3 +580,31 @@ int hinic3_clean_root_ctxt(struct hinic3_hwdev *hwdev)
return 0;
}
+
+#define HINIC3_FW_VER_TYPE_MPU 1
+
+int hinic3_get_mgmt_version(struct hinic3_hwdev *hwdev, u8 *mgmt_ver,
+ u8 version_size)
+{
+ struct comm_cmd_get_fw_version fw_ver = {};
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ fw_ver.fw_type = HINIC3_FW_VER_TYPE_MPU;
+
+ mgmt_msg_params_init_default(&msg_params, &fw_ver, sizeof(fw_ver));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
+ COMM_CMD_GET_FW_VERSION, &msg_params);
+
+ if (err || fw_ver.head.status) {
+ dev_err(hwdev->dev,
+ "Failed to get fw version, err: %d, status: 0x%x\n",
+ err, fw_ver.head.status);
+ return -EFAULT;
+ }
+
+ snprintf(mgmt_ver, version_size, "%s", fw_ver.ver);
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
index 8e4737c486b7..e672f9af5cb1 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
@@ -49,5 +49,7 @@ void hinic3_sync_time_to_fw(struct hinic3_hwdev *hwdev);
int hinic3_set_root_ctxt(struct hinic3_hwdev *hwdev, u32 rq_depth, u32 sq_depth,
int rx_buf_sz);
int hinic3_clean_root_ctxt(struct hinic3_hwdev *hwdev);
+int hinic3_get_mgmt_version(struct hinic3_hwdev *hwdev, u8 *mgmt_ver,
+ u8 version_size);
#endif
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
index 329a9c464ff9..cfc9daa3034f 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
@@ -114,6 +114,7 @@ enum comm_cmd {
COMM_CMD_SET_DMA_ATTR = 25,
/* Commands for obtaining information */
+ COMM_CMD_GET_FW_VERSION = 60,
COMM_CMD_SYNC_TIME = 62,
COMM_CMD_SEND_BDF_INFO = 64,
};
@@ -275,6 +276,17 @@ struct comm_cmd_bdf_info {
u8 rsvd2[5];
};
+#define COMM_FW_VERSION_LEN 16
+#define COMM_FW_COMPILE_TIME_LEN 20
+struct comm_cmd_get_fw_version {
+ struct mgmt_msg_head head;
+
+ u16 fw_type;
+ u16 rsvd1;
+ u8 ver[COMM_FW_VERSION_LEN];
+ u8 time[COMM_FW_COMPILE_TIME_LEN];
+};
+
/* Services supported by HW. HW uses these values when delivering events.
* HW supports multiple services that are not yet supported by driver
* (e.g. RoCE).
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
index 0074d0c6dbaf..f44b3064ab2e 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c
@@ -32,6 +32,9 @@
#define HINIC3_PCIE_PH_DISABLE 0
#define HINIC3_PCIE_MSIX_ATTR_ENTRY 0
+#define HINIC3_CHIP_PRESENT 1
+#define HINIC3_CHIP_ABSENT 0
+
#define HINIC3_DEFAULT_EQ_MSIX_PENDING_LIMIT 0
#define HINIC3_DEFAULT_EQ_MSIX_COALESC_TIMER_CFG 0xFF
#define HINIC3_DEFAULT_EQ_MSIX_RESEND_TIMER_CFG 7
@@ -431,6 +434,10 @@ err_free_ceqs:
static void hinic3_free_cmdqs_channel(struct hinic3_hwdev *hwdev)
{
hinic3_comm_cmdqs_free(hwdev);
+
+ hinic3_set_wq_page_size(hwdev, hinic3_global_func_id(hwdev),
+ HINIC3_MIN_PAGE_SIZE);
+
hinic3_ceqs_free(hwdev);
}
@@ -545,6 +552,7 @@ int hinic3_init_hwdev(struct pci_dev *pdev)
dev_err(hwdev->dev, "Failed to init hwif\n");
goto err_free_hwdev;
}
+ hwdev->chip_present_flag = HINIC3_CHIP_PRESENT;
hwdev->workq = alloc_workqueue(HINIC3_HWDEV_WQ_NAME, WQ_MEM_RECLAIM | WQ_PERCPU,
HINIC3_WQ_MAX_REQ);
@@ -621,6 +629,7 @@ void hinic3_set_api_stop(struct hinic3_hwdev *hwdev)
struct hinic3_recv_msg *recv_resp_msg;
struct hinic3_mbox *mbox;
+ hwdev->chip_present_flag = HINIC3_CHIP_ABSENT;
spin_lock_bh(&hwdev->channel_lock);
if (HINIC3_IS_PF(hwdev) &&
test_bit(HINIC3_HWDEV_MGMT_INITED, &hwdev->func_state)) {
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
index 9686c2600b46..4276ac136464 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h
@@ -76,6 +76,7 @@ struct hinic3_hwdev {
u32 wq_page_size;
u8 max_cmdq;
ulong func_state;
+ int chip_present_flag;
};
struct hinic3_event_info {
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c b/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c
index 771883174b3b..90feaa225080 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c
@@ -70,11 +70,19 @@
#define HINIC3_PPF_ELECTION_GET(val, member) \
FIELD_GET(HINIC3_PPF_ELECTION_##member##_MASK, val)
+#define HINIC3_GET_REG_FLAG(reg) ((reg) & (~(HINIC3_REGS_FLAG_MASK)))
#define HINIC3_GET_REG_ADDR(reg) ((reg) & (HINIC3_REGS_FLAG_MASK))
static void __iomem *hinic3_reg_addr(struct hinic3_hwif *hwif, u32 reg)
{
- return hwif->cfg_regs_base + HINIC3_GET_REG_ADDR(reg);
+ void __iomem *addr;
+
+ if (HINIC3_GET_REG_FLAG(reg) == HINIC3_MGMT_REGS_FLAG)
+ addr = hwif->mgmt_regs_base + HINIC3_GET_REG_ADDR(reg);
+ else
+ addr = hwif->cfg_regs_base + HINIC3_GET_REG_ADDR(reg);
+
+ return addr;
}
u32 hinic3_hwif_read_reg(struct hinic3_hwif *hwif, u32 reg)
@@ -97,6 +105,8 @@ static enum hinic3_wait_return check_hwif_ready_handler(void *priv_data)
u32 attr1;
attr1 = hinic3_hwif_read_reg(hwdev->hwif, HINIC3_CSR_FUNC_ATTR1_ADDR);
+ if (attr1 == HINIC3_PCIE_LINK_DOWN)
+ return HINIC3_WAIT_PROCESS_ERR;
return HINIC3_AF1_GET(attr1, MGMT_INIT_STATUS) ?
HINIC3_WAIT_PROCESS_CPL : HINIC3_WAIT_PROCESS_WAITING;
@@ -135,6 +145,7 @@ static void set_hwif_attr(struct hinic3_func_attr *attr, u32 attr0, u32 attr1,
static int init_hwif_attr(struct hinic3_hwdev *hwdev)
{
u32 attr0, attr1, attr2, attr3, attr6;
+ struct hinic3_func_attr *attr;
struct hinic3_hwif *hwif;
hwif = hwdev->hwif;
@@ -158,7 +169,15 @@ static int init_hwif_attr(struct hinic3_hwdev *hwdev)
if (attr6 == HINIC3_PCIE_LINK_DOWN)
return -EFAULT;
- set_hwif_attr(&hwif->attr, attr0, attr1, attr2, attr3, attr6);
+ attr = &hwif->attr;
+ set_hwif_attr(attr, attr0, attr1, attr2, attr3, attr6);
+
+ if (attr->func_type != HINIC3_FUNC_TYPE_VF &&
+ attr->func_type != HINIC3_FUNC_TYPE_PF) {
+ dev_err(hwdev->dev, "unexpected func_type %u\n",
+ attr->func_type);
+ return -EINVAL;
+ }
if (!hwif->attr.num_ceqs) {
dev_err(hwdev->dev, "Ceq num cfg in fw is zero\n");
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c b/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c
index a8b89eeed753..f83d5bd1041e 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c
@@ -426,6 +426,7 @@ static void hinic3_remove(struct pci_dev *pdev)
}
static const struct pci_device_id hinic3_pci_table[] = {
+ {PCI_VDEVICE(HUAWEI, PCI_DEV_ID_HINIC3_PF), 0},
{PCI_VDEVICE(HUAWEI, PCI_DEV_ID_HINIC3_VF), 0},
{0, 0}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
index 6039fcf3c1dd..0a888fe4c975 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
@@ -18,6 +18,7 @@
#define HINIC3_NIC_DRV_DESC "Intelligent Network Interface Card Driver"
+#define HINIC3_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_LINK)
#define HINIC3_RX_BUF_LEN 2048
#define HINIC3_LRO_REPLENISH_THLD 256
#define HINIC3_NIC_DEV_WQ_NAME "hinic3_nic_dev_wq"
@@ -143,6 +144,7 @@ static int hinic3_init_nic_dev(struct net_device *netdev,
nic_dev->hwdev = hwdev;
nic_dev->pdev = pdev;
+ nic_dev->msg_enable = HINIC3_DEFAULT_MSG_ENABLE;
nic_dev->rx_buf_len = HINIC3_RX_BUF_LEN;
nic_dev->lro_replenish_thld = HINIC3_LRO_REPLENISH_THLD;
nic_dev->vlan_bitmap = kzalloc(HINIC3_VLAN_BITMAP_SIZE(nic_dev),
@@ -241,6 +243,7 @@ static void hinic3_sw_uninit(struct net_device *netdev)
static void hinic3_assign_netdev_ops(struct net_device *netdev)
{
hinic3_set_netdev_ops(netdev);
+ hinic3_set_ethtool_ops(netdev);
}
static void netdev_feature_init(struct net_device *netdev)
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
index 826fa8879a11..c1bc64a50dd3 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.c
@@ -5,6 +5,7 @@
#include "hinic3_common.h"
#include "hinic3_csr.h"
+#include "hinic3_eqs.h"
#include "hinic3_hwdev.h"
#include "hinic3_hwif.h"
#include "hinic3_mbox.h"
@@ -50,9 +51,9 @@
#define MBOX_WB_STATUS_NOT_FINISHED 0x00
#define MBOX_STATUS_FINISHED(wb) \
- ((FIELD_PREP(MBOX_WB_STATUS_MASK, (wb))) != MBOX_WB_STATUS_NOT_FINISHED)
+ ((FIELD_GET(MBOX_WB_STATUS_MASK, (wb))) != MBOX_WB_STATUS_NOT_FINISHED)
#define MBOX_STATUS_SUCCESS(wb) \
- ((FIELD_PREP(MBOX_WB_STATUS_MASK, (wb))) == \
+ ((FIELD_GET(MBOX_WB_STATUS_MASK, (wb))) == \
MBOX_WB_STATUS_FINISHED_SUCCESS)
#define MBOX_STATUS_ERRCODE(wb) \
((wb) & MBOX_WB_ERROR_CODE_MASK)
@@ -395,6 +396,7 @@ static int hinic3_mbox_pre_init(struct hinic3_hwdev *hwdev,
{
mbox->hwdev = hwdev;
mutex_init(&mbox->mbox_send_lock);
+ mutex_init(&mbox->mbox_seg_send_lock);
spin_lock_init(&mbox->mbox_lock);
mbox->workq = create_singlethread_workqueue(HINIC3_MBOX_WQ_NAME);
@@ -460,7 +462,8 @@ void hinic3_free_mbox(struct hinic3_hwdev *hwdev)
destroy_workqueue(mbox->workq);
free_mbox_wb_status(mbox);
- hinic3_uninit_func_mbox_msg_channel(hwdev);
+ if (HINIC3_IS_VF(hwdev))
+ hinic3_uninit_func_mbox_msg_channel(hwdev);
uninit_mgmt_msg_channel(mbox);
kfree(mbox);
}
@@ -616,6 +619,18 @@ static void write_mbox_msg_attr(struct hinic3_mbox *mbox,
mbox_ctrl);
}
+static void hinic3_dump_mbox_reg(struct hinic3_hwdev *hwdev)
+{
+ u32 val;
+
+ val = hinic3_hwif_read_reg(hwdev->hwif,
+ HINIC3_FUNC_CSR_MAILBOX_CONTROL_OFF);
+ dev_err(hwdev->dev, "Mailbox control reg: 0x%x\n", val);
+ val = hinic3_hwif_read_reg(hwdev->hwif,
+ HINIC3_FUNC_CSR_MAILBOX_INT_OFF);
+ dev_err(hwdev->dev, "Mailbox interrupt offset: 0x%x\n", val);
+}
+
static u16 get_mbox_status(const struct hinic3_send_mbox *mbox)
{
__be64 *wb_status = mbox->wb_vaddr;
@@ -633,6 +648,9 @@ static enum hinic3_wait_return check_mbox_wb_status(void *priv_data)
struct hinic3_mbox *mbox = priv_data;
u16 wb_status;
+ if (!mbox->hwdev->chip_present_flag)
+ return HINIC3_WAIT_PROCESS_ERR;
+
wb_status = get_mbox_status(&mbox->send_mbox);
return MBOX_STATUS_FINISHED(wb_status) ?
@@ -670,6 +688,7 @@ static int send_mbox_seg(struct hinic3_mbox *mbox, __le64 header,
if (err) {
dev_err(hwdev->dev, "Send mailbox segment timeout, wb status: 0x%x\n",
wb_status);
+ hinic3_dump_mbox_reg(hwdev);
return err;
}
@@ -706,6 +725,8 @@ static int send_mbox_msg(struct hinic3_mbox *mbox, u8 mod, u16 cmd,
else
rsp_aeq_id = 0;
+ mutex_lock(&mbox->mbox_seg_send_lock);
+
if (dst_func == MBOX_MGMT_FUNC_ID &&
!(hwdev->features[0] & MBOX_COMM_F_MBOX_SEGMENT)) {
err = mbox_prepare_dma_msg(mbox, ack_type, &dma_msg,
@@ -759,6 +780,8 @@ static int send_mbox_msg(struct hinic3_mbox *mbox, u8 mod, u16 cmd,
}
err_send:
+ mutex_unlock(&mbox->mbox_seg_send_lock);
+
return err;
}
@@ -774,6 +797,9 @@ static enum hinic3_wait_return check_mbox_msg_finish(void *priv_data)
{
struct hinic3_mbox *mbox = priv_data;
+ if (!mbox->hwdev->chip_present_flag)
+ return HINIC3_WAIT_PROCESS_ERR;
+
return (mbox->event_flag == MBOX_EVENT_SUCCESS) ?
HINIC3_WAIT_PROCESS_CPL : HINIC3_WAIT_PROCESS_WAITING;
}
@@ -805,6 +831,9 @@ int hinic3_send_mbox_to_mgmt(struct hinic3_hwdev *hwdev, u8 mod, u16 cmd,
u32 msg_len;
int err;
+ if (!hwdev->chip_present_flag)
+ return -EPERM;
+
/* expect response message */
msg_desc = get_mbox_msg_desc(mbox, MBOX_MSG_RESP, MBOX_MGMT_FUNC_ID);
mutex_lock(&mbox->mbox_send_lock);
@@ -825,6 +854,7 @@ int hinic3_send_mbox_to_mgmt(struct hinic3_hwdev *hwdev, u8 mod, u16 cmd,
if (wait_mbox_msg_completion(mbox, msg_params->timeout_ms)) {
dev_err(hwdev->dev,
"Send mbox msg timeout, msg_id: %u\n", msg_info.msg_id);
+ hinic3_dump_aeq_info(mbox->hwdev);
err = -ETIMEDOUT;
goto err_send;
}
@@ -882,6 +912,9 @@ int hinic3_send_mbox_to_mgmt_no_ack(struct hinic3_hwdev *hwdev, u8 mod, u16 cmd,
struct mbox_msg_info msg_info = {};
int err;
+ if (!hwdev->chip_present_flag)
+ return -EPERM;
+
mutex_lock(&mbox->mbox_send_lock);
err = send_mbox_msg(mbox, mod, cmd, msg_params->buf_in,
msg_params->in_size, MBOX_MGMT_FUNC_ID,
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.h
index e26f22d1d564..30de0c129503 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mbox.h
@@ -114,6 +114,10 @@ struct hinic3_mbox {
struct hinic3_hwdev *hwdev;
/* lock for send mbox message and ack message */
struct mutex mbox_send_lock;
+ /* lock for send message transmission.
+ * The lock hierarchy is mbox_send_lock -> mbox_seg_send_lock.
+ */
+ struct mutex mbox_seg_send_lock;
struct hinic3_send_mbox send_mbox;
struct mbox_dma_queue sync_msg_queue;
struct mbox_dma_queue async_msg_queue;
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
index c0c87a8c2198..c5bca3c4af96 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
@@ -183,7 +183,18 @@ struct l2nic_cmd_lro_timer {
/* IEEE 802.1Qaz std */
#define L2NIC_DCB_COS_MAX 0x8
-struct l2nic_cmd_set_rss_ctx_tbl {
+struct l2nic_cmd_pause_config {
+ struct mgmt_msg_head msg_head;
+ u8 port_id;
+ u8 opcode;
+ u16 rsvd1;
+ u8 auto_neg;
+ u8 rx_pause;
+ u8 tx_pause;
+ u8 rsvd2[5];
+};
+
+struct l2nic_cmd_rss_ctx_tbl {
struct mgmt_msg_head msg_head;
u16 func_id;
u16 rsvd1;
@@ -238,6 +249,7 @@ enum l2nic_cmd {
L2NIC_CMD_CFG_RSS_HASH_KEY = 63,
L2NIC_CMD_CFG_RSS_HASH_ENGINE = 64,
L2NIC_CMD_SET_RSS_CTX_TBL = 65,
+ L2NIC_CMD_CFG_PAUSE_INFO = 101,
L2NIC_CMD_QOS_DCB_STATE = 110,
L2NIC_CMD_FORCE_PKT_DROP = 113,
L2NIC_CMD_MAX = 256,
@@ -259,6 +271,8 @@ enum l2nic_ucode_cmd {
enum mag_cmd {
MAG_CMD_SET_PORT_ENABLE = 6,
MAG_CMD_GET_LINK_STATUS = 7,
+
+ MAG_CMD_GET_PORT_INFO = 153,
};
/* firmware also use this cmd report link event to driver */
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
index cabb8523f246..da73811641a9 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
@@ -415,13 +415,17 @@ static void hinic3_vport_down(struct net_device *netdev)
netif_carrier_off(netdev);
netif_tx_disable(netdev);
- glb_func_id = hinic3_global_func_id(nic_dev->hwdev);
- hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, false);
+ if (nic_dev->hwdev->chip_present_flag) {
+ hinic3_maybe_set_port_state(netdev, false);
- hinic3_flush_txqs(netdev);
- /* wait to guarantee that no packets will be sent to host */
- msleep(100);
- hinic3_flush_qps_res(nic_dev->hwdev);
+ glb_func_id = hinic3_global_func_id(nic_dev->hwdev);
+ hinic3_set_vport_enable(nic_dev->hwdev, glb_func_id, false);
+
+ hinic3_flush_txqs(netdev);
+ /* wait to guarantee that no packets will be sent to host */
+ msleep(100);
+ hinic3_flush_qps_res(nic_dev->hwdev);
+ }
}
static int hinic3_open(struct net_device *netdev)
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
index 44abccf9cb29..de5a7984d2cb 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
@@ -639,6 +639,39 @@ int hinic3_get_link_status(struct hinic3_hwdev *hwdev, bool *link_status_up)
return 0;
}
+int hinic3_get_port_info(struct hinic3_hwdev *hwdev,
+ struct hinic3_nic_port_info *port_info)
+{
+ struct mag_cmd_get_port_info port_msg = {};
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ port_msg.port_id = hinic3_physical_port_id(hwdev);
+
+ mgmt_msg_params_init_default(&msg_params, &port_msg, sizeof(port_msg));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_HILINK,
+ MAG_CMD_GET_PORT_INFO, &msg_params);
+
+ if (err || port_msg.head.status) {
+ dev_err(hwdev->dev,
+ "Failed to get port info, err: %d, status: 0x%x\n",
+ err, port_msg.head.status);
+ return -EFAULT;
+ }
+
+ port_info->autoneg_cap = port_msg.an_support;
+ port_info->autoneg_state = port_msg.an_en;
+ port_info->duplex = port_msg.duplex;
+ port_info->port_type = port_msg.wire_type;
+ port_info->speed = port_msg.speed;
+ port_info->fec = port_msg.fec;
+ port_info->supported_mode = port_msg.supported_mode;
+ port_info->advertised_mode = port_msg.advertised_mode;
+
+ return 0;
+}
+
int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
bool enable)
{
@@ -661,3 +694,47 @@ int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
return 0;
}
+
+static int hinic3_cfg_hw_pause(struct hinic3_hwdev *hwdev, u8 opcode,
+ struct hinic3_nic_pause_config *nic_pause)
+{
+ struct l2nic_cmd_pause_config pause_info = {};
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ pause_info.port_id = hinic3_physical_port_id(hwdev);
+ pause_info.opcode = opcode;
+ if (opcode == MGMT_MSG_CMD_OP_SET) {
+ pause_info.auto_neg = nic_pause->auto_neg;
+ pause_info.rx_pause = nic_pause->rx_pause;
+ pause_info.tx_pause = nic_pause->tx_pause;
+ }
+
+ mgmt_msg_params_init_default(&msg_params, &pause_info,
+ sizeof(pause_info));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+ L2NIC_CMD_CFG_PAUSE_INFO, &msg_params);
+
+ if (err || pause_info.msg_head.status) {
+ dev_err(hwdev->dev, "Failed to %s pause info, err: %d, status: 0x%x\n",
+ opcode == MGMT_MSG_CMD_OP_SET ? "set" : "get",
+ err, pause_info.msg_head.status);
+ return -EFAULT;
+ }
+
+ if (opcode == MGMT_MSG_CMD_OP_GET) {
+ nic_pause->auto_neg = pause_info.auto_neg;
+ nic_pause->rx_pause = pause_info.rx_pause;
+ nic_pause->tx_pause = pause_info.tx_pause;
+ }
+
+ return 0;
+}
+
+int hinic3_get_pause_info(struct hinic3_nic_dev *nic_dev,
+ struct hinic3_nic_pause_config *nic_pause)
+{
+ return hinic3_cfg_hw_pause(nic_dev->hwdev, MGMT_MSG_CMD_OP_GET,
+ nic_pause);
+}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
index c32eaa886e17..5d52202a8d4e 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
@@ -6,6 +6,7 @@
#include <linux/types.h>
+#include "hinic3_hwif.h"
#include "hinic3_hw_intf.h"
#include "hinic3_mgmt_interface.h"
@@ -35,6 +36,49 @@ struct hinic3_sq_attr {
u64 ci_dma_base;
};
+enum mag_cmd_port_an {
+ PORT_CFG_AN_ON = 1,
+};
+
+/* mag supported/advertised link mode bitmap */
+enum mag_cmd_link_mode {
+ LINK_MODE_GE = 0,
+ LINK_MODE_10GE_BASE_R = 1,
+ LINK_MODE_25GE_BASE_R = 2,
+ LINK_MODE_40GE_BASE_R4 = 3,
+ LINK_MODE_50GE_BASE_R = 4,
+ LINK_MODE_50GE_BASE_R2 = 5,
+ LINK_MODE_100GE_BASE_R = 6,
+ LINK_MODE_100GE_BASE_R2 = 7,
+ LINK_MODE_100GE_BASE_R4 = 8,
+ LINK_MODE_200GE_BASE_R2 = 9,
+ LINK_MODE_200GE_BASE_R4 = 10,
+ LINK_MODE_MAX_NUMBERS,
+
+ LINK_MODE_UNKNOWN = 0xFFFF
+};
+
+struct mag_cmd_get_port_info {
+ struct mgmt_msg_head head;
+
+ u8 port_id;
+ u8 rsvd0[3];
+
+ u8 wire_type;
+ u8 an_support;
+ u8 an_en;
+ u8 duplex;
+
+ u8 speed;
+ u8 fec;
+ u8 lanes;
+ u8 rsvd1;
+
+ u32 supported_mode;
+ u32 advertised_mode;
+ u8 rsvd2[8];
+};
+
#define MAG_CMD_PORT_DISABLE 0x0
#define MAG_CMD_TX_ENABLE 0x1
#define MAG_CMD_RX_ENABLE 0x2
@@ -52,6 +96,39 @@ struct mag_cmd_set_port_enable {
u8 rsvd1[3];
};
+/* xsfp wire type, refers to cmis protocol definition */
+enum mag_wire_type {
+ MAG_CMD_WIRE_TYPE_UNKNOWN = 0x0,
+ MAG_CMD_WIRE_TYPE_MM = 0x1,
+ MAG_CMD_WIRE_TYPE_SM = 0x2,
+ MAG_CMD_WIRE_TYPE_COPPER = 0x3,
+ MAG_CMD_WIRE_TYPE_ACC = 0x4,
+ MAG_CMD_WIRE_TYPE_BASET = 0x5,
+ MAG_CMD_WIRE_TYPE_AOC = 0x40,
+ MAG_CMD_WIRE_TYPE_ELECTRIC = 0x41,
+ MAG_CMD_WIRE_TYPE_BACKPLANE = 0x42
+};
+
+#define XSFP_INFO_MAX_SIZE 640
+struct mag_cmd_get_xsfp_info {
+ struct mgmt_msg_head head;
+
+ u8 port_id;
+ u8 wire_type;
+ u16 out_len;
+ u32 rsvd;
+ u8 sfp_info[XSFP_INFO_MAX_SIZE];
+};
+
+struct mag_cmd_get_xsfp_present {
+ struct mgmt_msg_head head;
+
+ u8 port_id;
+ /* 0:present, 1:absent */
+ u8 abs_status;
+ u8 rsvd[2];
+};
+
enum link_err_type {
LINK_ERR_MODULE_UNRECOGENIZED,
LINK_ERR_NUM,
@@ -69,6 +146,34 @@ struct hinic3_port_module_event {
enum link_err_type err_type;
};
+struct hinic3_nic_port_info {
+ u8 port_type;
+ u8 autoneg_cap;
+ u8 autoneg_state;
+ u8 duplex;
+ u8 speed;
+ u8 fec;
+ u32 supported_mode;
+ u32 advertised_mode;
+};
+
+struct hinic3_nic_pause_config {
+ u8 auto_neg;
+ u8 rx_pause;
+ u8 tx_pause;
+};
+
+struct hinic3_nic_cfg {
+ /* Valid when pfc is disabled */
+ bool pause_set;
+ struct hinic3_nic_pause_config nic_pause;
+
+ u8 pfc_en;
+ u8 pfc_bitmap;
+
+ struct hinic3_nic_port_info port_info;
+};
+
int hinic3_get_nic_feature_from_hw(struct hinic3_nic_dev *nic_dev);
int hinic3_set_nic_feature_to_hw(struct hinic3_nic_dev *nic_dev);
bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
@@ -100,9 +205,14 @@ int hinic3_set_rx_mode(struct hinic3_hwdev *hwdev, u32 rx_mode);
int hinic3_sync_dcb_state(struct hinic3_hwdev *hwdev, u8 op_code, u8 state);
int hinic3_set_port_enable(struct hinic3_hwdev *hwdev, bool enable);
int hinic3_get_link_status(struct hinic3_hwdev *hwdev, bool *link_status_up);
+int hinic3_get_port_info(struct hinic3_hwdev *hwdev,
+ struct hinic3_nic_port_info *port_info);
int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
bool enable);
int hinic3_add_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);
int hinic3_del_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);
+int hinic3_get_pause_info(struct hinic3_nic_dev *nic_dev,
+ struct hinic3_nic_pause_config *nic_pause);
+
#endif
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
index 29189241f446..9502293ff710 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
@@ -101,6 +101,7 @@ struct hinic3_nic_dev {
struct hinic3_hwdev *hwdev;
struct hinic3_nic_io *nic_io;
+ u32 msg_enable;
u16 max_qps;
u16 rx_buf_len;
u32 lro_replenish_thld;
@@ -148,4 +149,6 @@ void hinic3_qps_irq_uninit(struct net_device *netdev);
void hinic3_set_rx_mode_work(struct work_struct *work);
void hinic3_clean_mac_list_filter(struct net_device *netdev);
+void hinic3_set_ethtool_ops(struct net_device *netdev);
+
#endif
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_pci_id_tbl.h b/drivers/net/ethernet/huawei/hinic3/hinic3_pci_id_tbl.h
index 86c88d0bb4bd..02b2b0fbecc7 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_pci_id_tbl.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_pci_id_tbl.h
@@ -4,6 +4,7 @@
#ifndef _HINIC3_PCI_ID_TBL_H_
#define _HINIC3_PCI_ID_TBL_H_
+#define PCI_DEV_ID_HINIC3_PF 0x0222
#define PCI_DEV_ID_HINIC3_VF 0x375F
#endif
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c b/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c
index 4ff1b2f79838..25db74d8c7dd 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c
@@ -132,7 +132,7 @@ static int hinic3_rss_set_indir_tbl(struct hinic3_hwdev *hwdev,
static int hinic3_set_rss_type(struct hinic3_hwdev *hwdev,
struct hinic3_rss_type rss_type)
{
- struct l2nic_cmd_set_rss_ctx_tbl ctx_tbl = {};
+ struct l2nic_cmd_rss_ctx_tbl ctx_tbl = {};
struct mgmt_msg_params msg_params = {};
u32 ctx;
int err;
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c
index 1236ec233b7f..309ab5901379 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c
@@ -328,6 +328,7 @@ static void hinic3_rx_csum(struct hinic3_rxq *rxq, u32 offload_type,
u32 ip_type = RQ_CQE_OFFOLAD_TYPE_GET(offload_type, IP_TYPE);
u32 csum_err = RQ_CQE_STATUS_GET(status, CSUM_ERR);
struct net_device *netdev = rxq->netdev;
+ bool l2_tunnel;
if (!(netdev->features & NETIF_F_RXCSUM))
return;
@@ -350,6 +351,12 @@ static void hinic3_rx_csum(struct hinic3_rxq *rxq, u32 offload_type,
case HINIC3_RX_UDP_PKT:
case HINIC3_RX_SCTP_PKT:
skb->ip_summed = CHECKSUM_UNNECESSARY;
+ l2_tunnel = HINIC3_GET_RX_TUNNEL_PKT_FORMAT(offload_type) ==
+ HINIC3_RX_PKT_FORMAT_VXLAN ? 1 : 0;
+ if (l2_tunnel) {
+ /* If we checked the outer header let the stack know */
+ skb->csum_level = 1;
+ }
break;
default:
skb->ip_summed = CHECKSUM_NONE;
@@ -390,6 +397,14 @@ static int recv_one_pkt(struct hinic3_rxq *rxq, struct hinic3_rq_cqe *rx_cqe,
offload_type = le32_to_cpu(rx_cqe->offload_type);
hinic3_rx_csum(rxq, offload_type, status, skb);
+ if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+ RQ_CQE_OFFOLAD_TYPE_GET(offload_type, VLAN_EN)) {
+ u16 vid = RQ_CQE_SGE_GET(vlan_len, VLAN);
+
+ /* if the packet is a vlan pkt, the vid may be 0 */
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
+ }
+
num_lro = RQ_CQE_STATUS_GET(status, NUM_LRO);
if (num_lro)
hinic3_lro_set_gso_params(skb, num_lro);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
index 31622e0a63d0..06d1b3299e7c 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
@@ -15,6 +15,9 @@
#define RQ_CQE_OFFOLAD_TYPE_GET(val, member) \
FIELD_GET(RQ_CQE_OFFOLAD_TYPE_##member##_MASK, val)
+#define HINIC3_GET_RX_TUNNEL_PKT_FORMAT(offload_type) \
+ RQ_CQE_OFFOLAD_TYPE_GET(offload_type, TUNNEL_PKT_FORMAT)
+
#define RQ_CQE_SGE_VLAN_MASK GENMASK(15, 0)
#define RQ_CQE_SGE_LEN_MASK GENMASK(31, 16)
#define RQ_CQE_SGE_GET(val, member) \