[PATCH net-next v01 01/15] hinic3: Add command queue detailed-respone interfaces
From: Fan Gong
Date: Tue Feb 24 2026 - 08:16:24 EST
Co-developed-by: Zhu Yikai <zhuyikai1@xxxxxxxxxxxxxx>
Signed-off-by: Zhu Yikai <zhuyikai1@xxxxxxxxxxxxxx>
Signed-off-by: Fan Gong <gongfan1@xxxxxxxxxx>
---
.../net/ethernet/huawei/hinic3/hinic3_cmdq.c | 196 +++++++++++++++++-
.../net/ethernet/huawei/hinic3/hinic3_cmdq.h | 15 ++
2 files changed, 209 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.c b/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.c
index 86720bb119e9..15c9aa247bae 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.c
@@ -61,6 +61,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 +88,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,
@@ -166,6 +174,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 +202,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)
{
@@ -257,6 +284,11 @@ void hinic3_cmdq_ceq_handler(struct hinic3_hwdev *hwdev, __le32 ceqe_data)
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 +315,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",
+ buf_in->size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int wait_cmdqs_enable(struct hinic3_cmdqs *cmdqs)
{
unsigned long end;
@@ -356,6 +400,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 +431,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,
@@ -557,11 +607,118 @@ static int cmdq_sync_cmd_direct_resp(struct hinic3_cmdq *cmdq, u8 mod, u8 cmd,
return err ? err : errcode;
}
+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)
+{
+ struct hinic3_cmdq_cmd_info *cmd_info, saved_cmd_info;
+ struct cmdq_wqe *curr_wqe, wqe = {};
+ int cmpt_code = CMDQ_SEND_CMPT_CODE;
+ struct hinic3_wq *wq = &cmdq->wq;
+ u16 curr_prod_idx, next_prod_idx;
+ struct completion done;
+ u64 curr_msg_id;
+ int errcode;
+ u8 wrapped;
+ int err;
+
+ spin_lock_bh(&cmdq->cmdq_lock);
+ curr_wqe = cmdq_get_wqe(wq, &curr_prod_idx);
+ if (!curr_wqe) {
+ spin_unlock_bh(&cmdq->cmdq_lock);
+ return -EBUSY;
+ }
+
+ wrapped = cmdq->wrapped;
+
+ next_prod_idx = curr_prod_idx + CMDQ_WQE_NUM_WQEBBS;
+ if (next_prod_idx >= wq->q_depth) {
+ cmdq->wrapped ^= 1;
+ next_prod_idx -= wq->q_depth;
+ }
+
+ cmd_info = &cmdq->cmd_infos[curr_prod_idx];
+
+ init_completion(&done);
+ refcount_inc(&buf_in->ref_cnt);
+ refcount_inc(&buf_out->ref_cnt);
+ cmd_info->cmd_type = HINIC3_CMD_TYPE_SGE_RESP;
+ 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;
+ cmd_info->buf_out = buf_out;
+
+ saved_cmd_info = *cmd_info;
+ cmdq_set_lcmd_wqe(&wqe, CMDQ_CMD_SGE_RESP, 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, 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) {
+ 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,
+ "Force stop cmdq cmd, mod: %u, cmd: %u\n",
+ mod, cmd);
+ err = -EAGAIN;
+ }
+
+ smp_rmb(); /* read error code after completion */
+
+ return err ? err : errcode;
+}
+
+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;
+ 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);
@@ -573,6 +730,39 @@ int hinic3_cmdq_direct_resp(struct hinic3_hwdev *hwdev, u8 mod, u8 cmd,
err = cmdq_sync_cmd_direct_resp(&cmdqs->cmdq[HINIC3_CMDQ_SYNC],
mod, cmd, buf_in, out_param);
+ 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;
+
+ 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;
}
@@ -759,7 +949,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;
@@ -786,7 +977,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);
--
2.43.0