Contributors: 1
Author Tokens Token Proportion Commits Commit Proportion
Unknown 2008 100.00% 7 100.00%
Total 2008 7


// SPDX-License-Identifier: GPL-2.0
// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.

#include <linux/delay.h>

#include "hinic3_cmdq.h"
#include "hinic3_hw_comm.h"
#include "hinic3_hwdev.h"
#include "hinic3_hwif.h"
#include "hinic3_mbox.h"

int hinic3_set_interrupt_cfg_direct(struct hinic3_hwdev *hwdev,
				    const struct hinic3_interrupt_info *info)
{
	struct comm_cmd_cfg_msix_ctrl_reg msix_cfg = {};
	struct mgmt_msg_params msg_params = {};
	int err;

	msix_cfg.func_id = hinic3_global_func_id(hwdev);
	msix_cfg.msix_index = info->msix_index;
	msix_cfg.opcode = MGMT_MSG_CMD_OP_SET;

	msix_cfg.lli_credit_cnt = info->lli_credit_limit;
	msix_cfg.lli_timer_cnt = info->lli_timer_cfg;
	msix_cfg.pending_cnt = info->pending_limit;
	msix_cfg.coalesce_timer_cnt = info->coalesc_timer_cfg;
	msix_cfg.resend_timer_cnt = info->resend_timer_cfg;

	mgmt_msg_params_init_default(&msg_params, &msix_cfg, sizeof(msix_cfg));

	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_CFG_MSIX_CTRL_REG, &msg_params);
	if (err || msix_cfg.head.status) {
		dev_err(hwdev->dev,
			"Failed to set interrupt config, err: %d, status: 0x%x\n",
			err, msix_cfg.head.status);
		return -EINVAL;
	}

	return 0;
}

int hinic3_func_reset(struct hinic3_hwdev *hwdev, u16 func_id, u64 reset_flag)
{
	struct comm_cmd_func_reset func_reset = {};
	struct mgmt_msg_params msg_params = {};
	int err;

	func_reset.func_id = func_id;
	func_reset.reset_flag = reset_flag;

	mgmt_msg_params_init_default(&msg_params, &func_reset,
				     sizeof(func_reset));

	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_FUNC_RESET, &msg_params);
	if (err || func_reset.head.status) {
		dev_err(hwdev->dev, "Failed to reset func resources, reset_flag 0x%llx, err: %d, status: 0x%x\n",
			reset_flag, err, func_reset.head.status);
		return -EIO;
	}

	return 0;
}

static int hinic3_comm_features_nego(struct hinic3_hwdev *hwdev, u8 opcode,
				     u64 *s_feature, u16 size)
{
	struct comm_cmd_feature_nego feature_nego = {};
	struct mgmt_msg_params msg_params = {};
	int err;

	feature_nego.func_id = hinic3_global_func_id(hwdev);
	feature_nego.opcode = opcode;
	if (opcode == MGMT_MSG_CMD_OP_SET)
		memcpy(feature_nego.s_feature, s_feature,
		       array_size(size, sizeof(u64)));

	mgmt_msg_params_init_default(&msg_params, &feature_nego,
				     sizeof(feature_nego));

	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_FEATURE_NEGO, &msg_params);
	if (err || feature_nego.head.status) {
		dev_err(hwdev->dev, "Failed to negotiate feature, err: %d, status: 0x%x\n",
			err, feature_nego.head.status);
		return -EINVAL;
	}

	if (opcode == MGMT_MSG_CMD_OP_GET)
		memcpy(s_feature, feature_nego.s_feature,
		       array_size(size, sizeof(u64)));

	return 0;
}

int hinic3_get_comm_features(struct hinic3_hwdev *hwdev, u64 *s_feature,
			     u16 size)
{
	return hinic3_comm_features_nego(hwdev, MGMT_MSG_CMD_OP_GET, s_feature,
					 size);
}

int hinic3_set_comm_features(struct hinic3_hwdev *hwdev, u64 *s_feature,
			     u16 size)
{
	return hinic3_comm_features_nego(hwdev, MGMT_MSG_CMD_OP_SET, s_feature,
					 size);
}

int hinic3_get_global_attr(struct hinic3_hwdev *hwdev,
			   struct comm_global_attr *attr)
{
	struct comm_cmd_get_glb_attr get_attr = {};
	struct mgmt_msg_params msg_params = {};
	int err;

	mgmt_msg_params_init_default(&msg_params, &get_attr, sizeof(get_attr));

	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_GET_GLOBAL_ATTR, &msg_params);
	if (err || get_attr.head.status) {
		dev_err(hwdev->dev,
			"Failed to get global attribute, err: %d, status: 0x%x\n",
			err, get_attr.head.status);
		return -EIO;
	}

	memcpy(attr, &get_attr.attr, sizeof(*attr));

	return 0;
}

int hinic3_set_func_svc_used_state(struct hinic3_hwdev *hwdev, u16 svc_type,
				   u8 state)
{
	struct comm_cmd_set_func_svc_used_state used_state = {};
	struct mgmt_msg_params msg_params = {};
	int err;

	used_state.func_id = hinic3_global_func_id(hwdev);
	used_state.svc_type = svc_type;
	used_state.used_state = state;

	mgmt_msg_params_init_default(&msg_params, &used_state,
				     sizeof(used_state));

	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_SET_FUNC_SVC_USED_STATE,
				       &msg_params);
	if (err || used_state.head.status) {
		dev_err(hwdev->dev,
			"Failed to set func service used state, err: %d, status: 0x%x\n",
			err, used_state.head.status);
		return -EIO;
	}

	return 0;
}

int hinic3_set_dma_attr_tbl(struct hinic3_hwdev *hwdev, u8 entry_idx, u8 st,
			    u8 at, u8 ph, u8 no_snooping, u8 tph_en)
{
	struct comm_cmd_set_dma_attr dma_attr = {};
	struct mgmt_msg_params msg_params = {};
	int err;

	dma_attr.func_id = hinic3_global_func_id(hwdev);
	dma_attr.entry_idx = entry_idx;
	dma_attr.st = st;
	dma_attr.at = at;
	dma_attr.ph = ph;
	dma_attr.no_snooping = no_snooping;
	dma_attr.tph_en = tph_en;

	mgmt_msg_params_init_default(&msg_params, &dma_attr, sizeof(dma_attr));

	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_SET_DMA_ATTR, &msg_params);
	if (err || dma_attr.head.status) {
		dev_err(hwdev->dev, "Failed to set dma attr, err: %d, status: 0x%x\n",
			err, dma_attr.head.status);
		return -EIO;
	}

	return 0;
}

int hinic3_set_wq_page_size(struct hinic3_hwdev *hwdev, u16 func_idx,
			    u32 page_size)
{
	struct comm_cmd_cfg_wq_page_size page_size_info = {};
	struct mgmt_msg_params msg_params = {};
	int err;

	page_size_info.func_id = func_idx;
	page_size_info.page_size = ilog2(page_size / HINIC3_MIN_PAGE_SIZE);
	page_size_info.opcode = MGMT_MSG_CMD_OP_SET;

	mgmt_msg_params_init_default(&msg_params, &page_size_info,
				     sizeof(page_size_info));

	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_CFG_PAGESIZE, &msg_params);
	if (err || page_size_info.head.status) {
		dev_err(hwdev->dev,
			"Failed to set wq page size, err: %d, status: 0x%x\n",
			err, page_size_info.head.status);
		return -EFAULT;
	}

	return 0;
}

int hinic3_set_cmdq_depth(struct hinic3_hwdev *hwdev, u16 cmdq_depth)
{
	struct comm_cmd_set_root_ctxt root_ctxt = {};
	struct mgmt_msg_params msg_params = {};
	int err;

	root_ctxt.func_id = hinic3_global_func_id(hwdev);

	root_ctxt.set_cmdq_depth = 1;
	root_ctxt.cmdq_depth = ilog2(cmdq_depth);

	mgmt_msg_params_init_default(&msg_params, &root_ctxt,
				     sizeof(root_ctxt));

	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_SET_VAT, &msg_params);
	if (err || root_ctxt.head.status) {
		dev_err(hwdev->dev,
			"Failed to set cmdq depth, err: %d, status: 0x%x\n",
			err, root_ctxt.head.status);
		return -EFAULT;
	}

	return 0;
}

#define HINIC3_WAIT_CMDQ_IDLE_TIMEOUT    5000

static enum hinic3_wait_return check_cmdq_stop_handler(void *priv_data)
{
	struct hinic3_hwdev *hwdev = priv_data;
	enum hinic3_cmdq_type cmdq_type;
	struct hinic3_cmdqs *cmdqs;

	cmdqs = hwdev->cmdqs;
	for (cmdq_type = 0; cmdq_type < cmdqs->cmdq_num; cmdq_type++) {
		if (!hinic3_cmdq_idle(&cmdqs->cmdq[cmdq_type]))
			return HINIC3_WAIT_PROCESS_WAITING;
	}

	return HINIC3_WAIT_PROCESS_CPL;
}

static int wait_cmdq_stop(struct hinic3_hwdev *hwdev)
{
	struct hinic3_cmdqs *cmdqs = hwdev->cmdqs;
	enum hinic3_cmdq_type cmdq_type;
	int err;

	if (!(cmdqs->status & HINIC3_CMDQ_ENABLE))
		return 0;

	cmdqs->status &= ~HINIC3_CMDQ_ENABLE;
	err = hinic3_wait_for_timeout(hwdev, check_cmdq_stop_handler,
				      HINIC3_WAIT_CMDQ_IDLE_TIMEOUT,
				      USEC_PER_MSEC);

	if (err)
		goto err_reenable_cmdq;

	return 0;

err_reenable_cmdq:
	for (cmdq_type = 0; cmdq_type < cmdqs->cmdq_num; cmdq_type++) {
		if (!hinic3_cmdq_idle(&cmdqs->cmdq[cmdq_type]))
			dev_err(hwdev->dev, "Cmdq %d is busy\n", cmdq_type);
	}
	cmdqs->status |= HINIC3_CMDQ_ENABLE;

	return err;
}

int hinic3_func_rx_tx_flush(struct hinic3_hwdev *hwdev)
{
	struct comm_cmd_clear_resource clear_db = {};
	struct comm_cmd_clear_resource clr_res = {};
	struct hinic3_hwif *hwif = hwdev->hwif;
	struct mgmt_msg_params msg_params = {};
	int ret = 0;
	int err;

	err = wait_cmdq_stop(hwdev);
	if (err) {
		dev_warn(hwdev->dev, "CMDQ is still working, CMDQ timeout value is unreasonable\n");
		ret = err;
	}

	hinic3_toggle_doorbell(hwif, DISABLE_DOORBELL);

	clear_db.func_id = hwif->attr.func_global_idx;
	mgmt_msg_params_init_default(&msg_params, &clear_db, sizeof(clear_db));
	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_FLUSH_DOORBELL, &msg_params);
	if (err || clear_db.head.status) {
		dev_warn(hwdev->dev, "Failed to flush doorbell, err: %d, status: 0x%x\n",
			 err, clear_db.head.status);
		if (err)
			ret = err;
		else
			ret = -EFAULT;
	}

	clr_res.func_id = hwif->attr.func_global_idx;
	msg_params.buf_in = &clr_res;
	msg_params.in_size = sizeof(clr_res);
	err = hinic3_send_mbox_to_mgmt_no_ack(hwdev, MGMT_MOD_COMM,
					      COMM_CMD_START_FLUSH,
					      &msg_params);
	if (err) {
		dev_warn(hwdev->dev, "Failed to notice flush message, err: %d\n",
			 err);
		ret = err;
	}

	hinic3_toggle_doorbell(hwif, ENABLE_DOORBELL);

	err = hinic3_reinit_cmdq_ctxts(hwdev);
	if (err) {
		dev_warn(hwdev->dev, "Failed to reinit cmdq\n");
		ret = err;
	}

	return ret;
}

static int get_hw_rx_buf_size_idx(int rx_buf_sz, u16 *buf_sz_idx)
{
	/* Supported RX buffer sizes in bytes. Configured by array index. */
	static const int supported_sizes[16] = {
		[0] = 32,     [1] = 64,     [2] = 96,     [3] = 128,
		[4] = 192,    [5] = 256,    [6] = 384,    [7] = 512,
		[8] = 768,    [9] = 1024,   [10] = 1536,  [11] = 2048,
		[12] = 3072,  [13] = 4096,  [14] = 8192,  [15] = 16384,
	};
	u16 idx;

	/* Scan from biggest to smallest. Choose supported size that is equal or
	 * smaller. For smaller value HW will under-utilize posted buffers. For
	 * bigger value HW may overrun posted buffers.
	 */
	idx = ARRAY_SIZE(supported_sizes);
	while (idx > 0) {
		idx--;
		if (supported_sizes[idx] <= rx_buf_sz) {
			*buf_sz_idx = idx;
			return 0;
		}
	}

	return -EINVAL;
}

int hinic3_set_root_ctxt(struct hinic3_hwdev *hwdev, u32 rq_depth, u32 sq_depth,
			 int rx_buf_sz)
{
	struct comm_cmd_set_root_ctxt root_ctxt = {};
	struct mgmt_msg_params msg_params = {};
	u16 buf_sz_idx;
	int err;

	err = get_hw_rx_buf_size_idx(rx_buf_sz, &buf_sz_idx);
	if (err)
		return err;

	root_ctxt.func_id = hinic3_global_func_id(hwdev);

	root_ctxt.set_cmdq_depth = 0;
	root_ctxt.cmdq_depth = 0;

	root_ctxt.lro_en = 1;

	root_ctxt.rq_depth  = ilog2(rq_depth);
	root_ctxt.rx_buf_sz = buf_sz_idx;
	root_ctxt.sq_depth  = ilog2(sq_depth);

	mgmt_msg_params_init_default(&msg_params, &root_ctxt,
				     sizeof(root_ctxt));

	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_SET_VAT, &msg_params);
	if (err || root_ctxt.head.status) {
		dev_err(hwdev->dev,
			"Failed to set root context, err: %d, status: 0x%x\n",
			err, root_ctxt.head.status);
		return -EFAULT;
	}

	return 0;
}

int hinic3_clean_root_ctxt(struct hinic3_hwdev *hwdev)
{
	struct comm_cmd_set_root_ctxt root_ctxt = {};
	struct mgmt_msg_params msg_params = {};
	int err;

	root_ctxt.func_id = hinic3_global_func_id(hwdev);

	mgmt_msg_params_init_default(&msg_params, &root_ctxt,
				     sizeof(root_ctxt));

	err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
				       COMM_CMD_SET_VAT, &msg_params);
	if (err || root_ctxt.head.status) {
		dev_err(hwdev->dev,
			"Failed to set root context, err: %d, status: 0x%x\n",
			err, root_ctxt.head.status);
		return -EFAULT;
	}

	return 0;
}