root/drivers/net/ethernet/huawei/hinic3/hinic3_cmdq.h
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. */

#ifndef _HINIC3_CMDQ_H_
#define _HINIC3_CMDQ_H_

#include <linux/dmapool.h>

#include "hinic3_hw_intf.h"
#include "hinic3_wq.h"

#define CMDQ_DEPTH  4096

struct cmdq_db {
        __le32 db_head;
        __le32 db_info;
};

/* hw defined cmdq wqe header */
struct cmdq_header {
        __le32 header_info;
        __le32 saved_data;
};

struct cmdq_lcmd_bufdesc {
        struct hinic3_sge sge;
        __le64            rsvd2;
        __le64            rsvd3;
};

struct cmdq_status {
        __le32 status_info;
};

struct cmdq_ctrl {
        __le32 ctrl_info;
};

struct cmdq_direct_resp {
        __le64 val;
        __le64 rsvd;
};

struct cmdq_completion {
        union {
                struct hinic3_sge       sge;
                struct cmdq_direct_resp direct;
        } resp;
};

struct cmdq_wqe_scmd {
        struct cmdq_header     header;
        __le64                 rsvd3;
        struct cmdq_status     status;
        struct cmdq_ctrl       ctrl;
        struct cmdq_completion completion;
        __le32                 rsvd10[6];
};

struct cmdq_wqe_lcmd {
        struct cmdq_header       header;
        struct cmdq_status       status;
        struct cmdq_ctrl         ctrl;
        struct cmdq_completion   completion;
        struct cmdq_lcmd_bufdesc buf_desc;
};

struct cmdq_wqe {
        union {
                struct cmdq_wqe_scmd wqe_scmd;
                struct cmdq_wqe_lcmd wqe_lcmd;
        };
};

static_assert(sizeof(struct cmdq_wqe) == 64);

enum hinic3_cmdq_type {
        HINIC3_CMDQ_SYNC      = 0,
        HINIC3_MAX_CMDQ_TYPES = 4
};

enum hinic3_cmdq_status {
        HINIC3_CMDQ_ENABLE = BIT(0),
};

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,
};

struct hinic3_cmd_buf {
        void       *buf;
        dma_addr_t dma_addr;
        __le16     size;
        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;
        int                       *errcode;
        /* completion code */
        int                       *cmpt_code;
        __le64                    *direct_resp;
        u64                       cmdq_msg_id;
        struct hinic3_cmd_buf     *buf_in;
        struct hinic3_cmd_buf     *buf_out;
};

struct hinic3_cmdq {
        struct hinic3_wq            wq;
        enum hinic3_cmdq_type       cmdq_type;
        u8                          wrapped;
        /* synchronize command submission with completions via event queue */
        spinlock_t                  cmdq_lock;
        struct hinic3_cmdq_cmd_info *cmd_infos;
        struct hinic3_hwdev         *hwdev;
};

struct hinic3_cmdqs {
        struct hinic3_hwdev *hwdev;
        struct hinic3_cmdq  cmdq[HINIC3_MAX_CMDQ_TYPES];
        struct dma_pool     *cmd_buf_pool;
        /* doorbell area */
        u8 __iomem          *cmdqs_db_base;

        /* When command queue uses multiple memory pages (1-level CLA), this
         * block will hold aggregated indirection table for all command queues
         * of cmdqs. Not used for small cmdq (0-level CLA).
         */
        dma_addr_t          wq_block_paddr;
        void                *wq_block_vaddr;

        u32                 status;
        u32                 disable_flag;
        u8                  cmdq_num;
};

int hinic3_cmdqs_init(struct hinic3_hwdev *hwdev);
void hinic3_cmdqs_free(struct hinic3_hwdev *hwdev);

struct hinic3_cmd_buf *hinic3_alloc_cmd_buf(struct hinic3_hwdev *hwdev);
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);
bool hinic3_cmdq_idle(struct hinic3_cmdq *cmdq);

#endif