pax_global_header00006660000000000000000000000064150202733250014511gustar00rootroot0000000000000052 comment=f71d7ee3c03870250a9224f0bee2700e3740f56e hinic-dkms-0+git20250605+6.6.0-95.0.0/000077500000000000000000000000001502027332500160355ustar00rootroot00000000000000hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/000077500000000000000000000000001502027332500171275ustar00rootroot00000000000000hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/Kconfig000066400000000000000000000006061502027332500204340ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-only # # Huawei driver configuration # config HINIC tristate "Huawei Intelligent PCIE Network Interface Card" depends on (PCI_MSI && (X86 || ARM64)) select NET_DEVLINK help This driver supports HiNIC PCIE Ethernet cards. To compile this driver as part of the kernel, choose Y here. If unsure, choose N. The default is compiled as module. hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/Makefile000066400000000000000000000010631502027332500205670ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_HINIC) += hinic.o hinic-y := hinic_nic_cfg.o hinic_nic_io.o hinic_nic_dbg.o \ hinic_hwif.o hinic_msix_attr.o hinic_eqs.o \ hinic_mbox.o hinic_api_cmd.o hinic_mgmt.o \ hinic_wq.o hinic_cmdq.o hinic_hwdev.o hinic_cfg.o \ hinic_sml_counter.o hinic_sml_lt.o \ hinic_multi_host_mgmt.o hinic_main.o hinic_lld.o \ hinic_qp.o hinic_rx.o hinic_tx.o hinic_dbgtool_knl.o \ hinic_nictool.o hinic_sriov.o hinic_dcb.o\ hinic_ethtool.o hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_api_cmd.c000066400000000000000000000744401502027332500220520ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwdev.h" #include "hinic_csr.h" #include "hinic_hwif.h" #include "hinic_api_cmd.h" #define API_CMD_CHAIN_CELL_SIZE_SHIFT 6U #define API_CMD_CELL_DESC_SIZE 8 #define API_CMD_CELL_DATA_ADDR_SIZE 8 #define API_CHAIN_NUM_CELLS 32 #define API_CHAIN_CELL_SIZE 128 #define API_CHAIN_RSP_DATA_SIZE 128 #define API_CMD_CELL_WB_ADDR_SIZE 8 #define API_CHAIN_CELL_ALIGNMENT 8 #define API_CMD_TIMEOUT 10000 #define API_CMD_STATUS_TIMEOUT 100000 #define API_CMD_BUF_SIZE 2048ULL #define API_CMD_NODE_ALIGN_SIZE 512ULL #define API_PAYLOAD_ALIGN_SIZE 64ULL #define API_CHAIN_RESP_ALIGNMENT 64ULL #define COMPLETION_TIMEOUT_DEFAULT 1000UL #define POLLING_COMPLETION_TIMEOUT_DEFAULT 1000U #define API_CMD_RESPONSE_DATA_PADDR(val) be64_to_cpu(*((u64 *)(val))) #define READ_API_CMD_PRIV_DATA(id, token) (((id) << 16) + (token)) #define WRITE_API_CMD_PRIV_DATA(id) (((u8)id) << 16) #define MASKED_IDX(chain, idx) ((idx) & ((chain)->num_cells - 1)) #define SIZE_4BYTES(size) (ALIGN((u32)(size), 4U) >> 2) #define SIZE_8BYTES(size) (ALIGN((u32)(size), 8U) >> 3) enum api_cmd_data_format { SGL_DATA = 1, }; enum api_cmd_type { API_CMD_WRITE_TYPE = 0, API_CMD_READ_TYPE = 1, }; enum api_cmd_bypass { NOT_BYPASS = 0, BYPASS = 1, }; enum api_cmd_resp_aeq { NOT_TRIGGER = 0, TRIGGER = 1, }; static u8 xor_chksum_set(void *data) { int idx; u8 checksum = 0; u8 *val = data; for (idx = 0; idx < 7; idx++) checksum ^= val[idx]; return checksum; } static void set_prod_idx(struct hinic_api_cmd_chain *chain) { enum hinic_api_cmd_chain_type chain_type = chain->chain_type; struct hinic_hwif *hwif = chain->hwdev->hwif; u32 hw_prod_idx_addr = HINIC_CSR_API_CMD_CHAIN_PI_ADDR(chain_type); u32 prod_idx = chain->prod_idx; hinic_hwif_write_reg(hwif, hw_prod_idx_addr, prod_idx); } static u32 get_hw_cons_idx(struct hinic_api_cmd_chain *chain) { u32 addr, val; addr = HINIC_CSR_API_CMD_STATUS_0_ADDR(chain->chain_type); val = hinic_hwif_read_reg(chain->hwdev->hwif, addr); return HINIC_API_CMD_STATUS_GET(val, CONS_IDX); } static void dump_api_chain_reg(struct hinic_api_cmd_chain *chain) { void *dev = chain->hwdev->dev_hdl; u32 addr, val; addr = HINIC_CSR_API_CMD_STATUS_0_ADDR(chain->chain_type); val = hinic_hwif_read_reg(chain->hwdev->hwif, addr); sdk_err(dev, "Chain type: 0x%x, cpld error: 0x%x, check error: 0x%x, current fsm: 0x%x\n", chain->chain_type, HINIC_API_CMD_STATUS_GET(val, CPLD_ERR), HINIC_API_CMD_STATUS_GET(val, CHKSUM_ERR), HINIC_API_CMD_STATUS_GET(val, FSM)); sdk_err(dev, "Chain hw current ci: 0x%x\n", HINIC_API_CMD_STATUS_GET(val, CONS_IDX)); addr = HINIC_CSR_API_CMD_CHAIN_PI_ADDR(chain->chain_type); val = hinic_hwif_read_reg(chain->hwdev->hwif, addr); sdk_err(dev, "Chain hw current pi: 0x%x\n", val); } /** * chain_busy - check if the chain is still processing last requests * @chain: chain to check * Return: 0 - success, negative - failure */ static int chain_busy(struct hinic_api_cmd_chain *chain) { void *dev = chain->hwdev->dev_hdl; struct hinic_api_cmd_cell_ctxt *ctxt; u64 resp_header; ctxt = &chain->cell_ctxt[chain->prod_idx]; switch (chain->chain_type) { case HINIC_API_CMD_MULTI_READ: case HINIC_API_CMD_POLL_READ: resp_header = be64_to_cpu(ctxt->resp->header); if (ctxt->status && !HINIC_API_CMD_RESP_HEADER_VALID(resp_header)) { sdk_err(dev, "Context(0x%x) busy, pi: %d, resp_header: 0x%08x%08x\n", ctxt->status, chain->prod_idx, upper_32_bits(resp_header), lower_32_bits(resp_header)); dump_api_chain_reg(chain); return -EBUSY; } break; case HINIC_API_CMD_POLL_WRITE: case HINIC_API_CMD_WRITE_TO_MGMT_CPU: case HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU: chain->cons_idx = get_hw_cons_idx(chain); if (chain->cons_idx == MASKED_IDX(chain, chain->prod_idx + 1)) { sdk_err(dev, "API CMD chain %d is busy, cons_idx = %d, prod_idx = %d\n", chain->chain_type, chain->cons_idx, chain->prod_idx); dump_api_chain_reg(chain); return -EBUSY; } break; default: sdk_err(dev, "Unknown Chain type %d\n", chain->chain_type); return -EINVAL; } return 0; } /** * get_cell_data_size - get the data size of specific cell type * @type: chain type * @cmd_size: the command size * Return: cell_data_size */ static u16 get_cell_data_size(enum hinic_api_cmd_chain_type type, u16 cmd_size) { u16 cell_data_size = 0; switch (type) { case HINIC_API_CMD_POLL_READ: cell_data_size = ALIGN(API_CMD_CELL_DESC_SIZE + API_CMD_CELL_WB_ADDR_SIZE + API_CMD_CELL_DATA_ADDR_SIZE, API_CHAIN_CELL_ALIGNMENT); break; case HINIC_API_CMD_WRITE_TO_MGMT_CPU: case HINIC_API_CMD_POLL_WRITE: case HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU: cell_data_size = ALIGN(API_CMD_CELL_DESC_SIZE + API_CMD_CELL_DATA_ADDR_SIZE, API_CHAIN_CELL_ALIGNMENT); break; default: break; } return cell_data_size; } /** * prepare_cell_ctrl - prepare the ctrl of the cell for the command * @cell_ctrl: the control of the cell to set the control into it * @cell_len: the size of the cell */ static void prepare_cell_ctrl(u64 *cell_ctrl, u16 cell_len) { u64 ctrl; u8 chksum; ctrl = HINIC_API_CMD_CELL_CTRL_SET(SIZE_8BYTES(cell_len), CELL_LEN) | HINIC_API_CMD_CELL_CTRL_SET(0ULL, RD_DMA_ATTR_OFF) | HINIC_API_CMD_CELL_CTRL_SET(0ULL, WR_DMA_ATTR_OFF); chksum = xor_chksum_set(&ctrl); ctrl |= HINIC_API_CMD_CELL_CTRL_SET(chksum, XOR_CHKSUM); /* The data in the HW should be in Big Endian Format */ *cell_ctrl = cpu_to_be64(ctrl); } /** * prepare_api_cmd - prepare API CMD command * @chain: chain for the command * @cell: the cell of the command * @dest: destination node on the card that will receive the command * @cmd: command data * @cmd_size: the command size */ static void prepare_api_cmd(struct hinic_api_cmd_chain *chain, struct hinic_api_cmd_cell *cell, enum hinic_node_id dest, const void *cmd, u16 cmd_size) { struct hinic_api_cmd_cell_ctxt *cell_ctxt; u32 priv; cell_ctxt = &chain->cell_ctxt[chain->prod_idx]; switch (chain->chain_type) { case HINIC_API_CMD_POLL_READ: priv = READ_API_CMD_PRIV_DATA(chain->chain_type, cell_ctxt->saved_prod_idx); cell->desc = HINIC_API_CMD_DESC_SET(SGL_DATA, API_TYPE) | HINIC_API_CMD_DESC_SET(API_CMD_READ_TYPE, RD_WR) | HINIC_API_CMD_DESC_SET(BYPASS, MGMT_BYPASS) | HINIC_API_CMD_DESC_SET(NOT_TRIGGER, RESP_AEQE_EN) | HINIC_API_CMD_DESC_SET(priv, PRIV_DATA); break; case HINIC_API_CMD_POLL_WRITE: priv = WRITE_API_CMD_PRIV_DATA(chain->chain_type); cell->desc = HINIC_API_CMD_DESC_SET(SGL_DATA, API_TYPE) | HINIC_API_CMD_DESC_SET(API_CMD_WRITE_TYPE, RD_WR) | HINIC_API_CMD_DESC_SET(BYPASS, MGMT_BYPASS) | HINIC_API_CMD_DESC_SET(NOT_TRIGGER, RESP_AEQE_EN) | HINIC_API_CMD_DESC_SET(priv, PRIV_DATA); break; case HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU: case HINIC_API_CMD_WRITE_TO_MGMT_CPU: priv = WRITE_API_CMD_PRIV_DATA(chain->chain_type); cell->desc = HINIC_API_CMD_DESC_SET(SGL_DATA, API_TYPE) | HINIC_API_CMD_DESC_SET(API_CMD_WRITE_TYPE, RD_WR) | HINIC_API_CMD_DESC_SET(NOT_BYPASS, MGMT_BYPASS) | HINIC_API_CMD_DESC_SET(TRIGGER, RESP_AEQE_EN) | HINIC_API_CMD_DESC_SET(priv, PRIV_DATA); break; default: sdk_err(chain->hwdev->dev_hdl, "Unknown Chain type: %d\n", chain->chain_type); return; } cell->desc |= HINIC_API_CMD_DESC_SET(dest, DEST) | HINIC_API_CMD_DESC_SET(SIZE_4BYTES(cmd_size), SIZE); cell->desc |= HINIC_API_CMD_DESC_SET(xor_chksum_set(&cell->desc), XOR_CHKSUM); /* The data in the HW should be in Big Endian Format */ cell->desc = cpu_to_be64(cell->desc); memcpy(cell_ctxt->api_cmd_vaddr, cmd, cmd_size); } /** * prepare_cell - prepare cell ctrl and cmd in the current producer cell * @chain: chain for the command * @dest: destination node on the card that will receive the command * @cmd: command data * @cmd_size: the command size */ static void prepare_cell(struct hinic_api_cmd_chain *chain, enum hinic_node_id dest, void *cmd, u16 cmd_size) { struct hinic_api_cmd_cell *curr_node; u16 cell_size; curr_node = chain->curr_node; cell_size = get_cell_data_size(chain->chain_type, cmd_size); prepare_cell_ctrl(&curr_node->ctrl, cell_size); prepare_api_cmd(chain, curr_node, dest, cmd, cmd_size); } static inline void cmd_chain_prod_idx_inc(struct hinic_api_cmd_chain *chain) { chain->prod_idx = MASKED_IDX(chain, chain->prod_idx + 1); } static void issue_api_cmd(struct hinic_api_cmd_chain *chain) { set_prod_idx(chain); } /** * api_cmd_status_update - update the status of the chain * @chain: chain to update */ static void api_cmd_status_update(struct hinic_api_cmd_chain *chain) { struct hinic_api_cmd_status *wb_status; enum hinic_api_cmd_chain_type chain_type; u64 status_header; u32 buf_desc; wb_status = chain->wb_status; buf_desc = be32_to_cpu(wb_status->buf_desc); if (HINIC_API_CMD_STATUS_GET(buf_desc, CHKSUM_ERR)) return; status_header = be64_to_cpu(wb_status->header); chain_type = HINIC_API_CMD_STATUS_HEADER_GET(status_header, CHAIN_ID); if (chain_type >= HINIC_API_CMD_MAX) return; if (chain_type != chain->chain_type) return; chain->cons_idx = HINIC_API_CMD_STATUS_GET(buf_desc, CONS_IDX); } /** * wait_for_status_poll - wait for write to mgmt command to complete * @chain: the chain of the command * Return: 0 - success, negative - failure */ static int wait_for_status_poll(struct hinic_api_cmd_chain *chain) { int err = -ETIMEDOUT; u32 cnt = 0; while (cnt < API_CMD_STATUS_TIMEOUT && chain->hwdev->chip_present_flag) { api_cmd_status_update(chain); /* SYNC API CMD cmd should start after prev cmd finished */ if (chain->cons_idx == chain->prod_idx) { err = 0; break; } usleep_range(50, 100); cnt++; } return err; } static void copy_resp_data(struct hinic_api_cmd_cell_ctxt *ctxt, void *ack, u16 ack_size) { struct hinic_api_cmd_resp_fmt *resp = ctxt->resp; memcpy(ack, &resp->resp_data, ack_size); ctxt->status = 0; } /** * prepare_cell - polling for respense data of the read api-command * @ctxt: pointer to api cmd cell ctxt * * Return: 0 - success, negative - failure */ static int wait_for_resp_polling(struct hinic_api_cmd_cell_ctxt *ctxt) { u64 resp_header; int ret = -ETIMEDOUT; u32 cnt = 0; while (cnt < POLLING_COMPLETION_TIMEOUT_DEFAULT) { resp_header = be64_to_cpu(ctxt->resp->header); rmb(); /* read the latest header */ if (HINIC_API_CMD_RESP_HEADER_VALID(resp_header)) { ret = 0; break; } usleep_range(100, 1000); cnt++; } if (ret) pr_err("Wait for api chain response timeout\n"); return ret; } /** * wait_for_api_cmd_completion - wait for command to complete * @chain: chain for the command * @ctxt: pointer to api cmd cell ctxt * @ack: pointer to ack message * @ack_size: the size of ack message * Return: 0 - success, negative - failure */ static int wait_for_api_cmd_completion(struct hinic_api_cmd_chain *chain, struct hinic_api_cmd_cell_ctxt *ctxt, void *ack, u16 ack_size) { void *dev = chain->hwdev->dev_hdl; int err = 0; switch (chain->chain_type) { case HINIC_API_CMD_POLL_READ: err = wait_for_resp_polling(ctxt); if (!err) copy_resp_data(ctxt, ack, ack_size); break; case HINIC_API_CMD_POLL_WRITE: case HINIC_API_CMD_WRITE_TO_MGMT_CPU: err = wait_for_status_poll(chain); if (err) { sdk_err(dev, "API CMD Poll status timeout, chain type: %d\n", chain->chain_type); break; } break; case HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU: /* No need to wait */ break; default: sdk_err(dev, "Unknown API CMD Chain type: %d\n", chain->chain_type); err = -EINVAL; break; } if (err) dump_api_chain_reg(chain); return err; } static inline void update_api_cmd_ctxt(struct hinic_api_cmd_chain *chain, struct hinic_api_cmd_cell_ctxt *ctxt) { ctxt->status = 1; ctxt->saved_prod_idx = chain->prod_idx; if (ctxt->resp) { ctxt->resp->header = 0; /* make sure "header" was cleared */ wmb(); } } /** * api_cmd - API CMD command * @chain: chain for the command * @dest: destination node on the card that will receive the command * @cmd: command data * @cmd_size: the command size * @ack: the buffer for ack * @ack_size: the size of ack * Return: 0 - success, negative - failure */ static int api_cmd(struct hinic_api_cmd_chain *chain, enum hinic_node_id dest, void *cmd, u16 cmd_size, void *ack, u16 ack_size) { struct hinic_api_cmd_cell_ctxt *ctxt; if (chain->chain_type == HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU) spin_lock(&chain->async_lock); else down(&chain->sem); ctxt = &chain->cell_ctxt[chain->prod_idx]; if (chain_busy(chain)) { if (chain->chain_type == HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU) spin_unlock(&chain->async_lock); else up(&chain->sem); return -EBUSY; } update_api_cmd_ctxt(chain, ctxt); prepare_cell(chain, dest, cmd, cmd_size); cmd_chain_prod_idx_inc(chain); wmb(); /* issue the command */ issue_api_cmd(chain); /* incremented prod idx, update ctxt */ chain->curr_node = chain->cell_ctxt[chain->prod_idx].cell_vaddr; if (chain->chain_type == HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU) spin_unlock(&chain->async_lock); else up(&chain->sem); return wait_for_api_cmd_completion(chain, ctxt, ack, ack_size); } /** * hinic_api_cmd_write - Write API CMD command * @chain: chain for write command * @dest: destination node on the card that will receive the command * @cmd: command data * @size: the command size * Return: 0 - success, negative - failure */ int hinic_api_cmd_write(struct hinic_api_cmd_chain *chain, enum hinic_node_id dest, void *cmd, u16 size) { /* Verify the chain type */ return api_cmd(chain, dest, cmd, size, NULL, 0); } int hinic_api_cmd_read(struct hinic_api_cmd_chain *chain, enum hinic_node_id dest, void *cmd, u16 size, void *ack, u16 ack_size) { return api_cmd(chain, dest, cmd, size, ack, ack_size); } /** * api_cmd_hw_restart - restart the chain in the HW * @cmd_chain: the API CMD specific chain to restart */ static int api_cmd_hw_restart(struct hinic_api_cmd_chain *cmd_chain) { struct hinic_hwif *hwif = cmd_chain->hwdev->hwif; u32 reg_addr, val; int err; u32 cnt = 0; /* Read Modify Write */ reg_addr = HINIC_CSR_API_CMD_CHAIN_REQ_ADDR(cmd_chain->chain_type); val = hinic_hwif_read_reg(hwif, reg_addr); val = HINIC_API_CMD_CHAIN_REQ_CLEAR(val, RESTART); val |= HINIC_API_CMD_CHAIN_REQ_SET(1, RESTART); hinic_hwif_write_reg(hwif, reg_addr, val); err = -ETIMEDOUT; while (cnt < API_CMD_TIMEOUT) { val = hinic_hwif_read_reg(hwif, reg_addr); if (!HINIC_API_CMD_CHAIN_REQ_GET(val, RESTART)) { err = 0; break; } usleep_range(900, 1000); cnt++; } return err; } /** * api_cmd_ctrl_init - set the control register of a chain * @chain: the API CMD specific chain to set control register for */ static void api_cmd_ctrl_init(struct hinic_api_cmd_chain *chain) { struct hinic_hwif *hwif = chain->hwdev->hwif; u32 reg_addr, ctrl; u32 size; /* Read Modify Write */ reg_addr = HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(chain->chain_type); size = (u32)ilog2(chain->cell_size >> API_CMD_CHAIN_CELL_SIZE_SHIFT); ctrl = hinic_hwif_read_reg(hwif, reg_addr); ctrl = HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, AEQE_EN) & HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, CELL_SIZE); ctrl |= HINIC_API_CMD_CHAIN_CTRL_SET(0, AEQE_EN) | HINIC_API_CMD_CHAIN_CTRL_SET(size, CELL_SIZE); hinic_hwif_write_reg(hwif, reg_addr, ctrl); } /** * api_cmd_set_status_addr - set the status address of a chain in the HW * @chain: the API CMD specific chain to set status address for */ static void api_cmd_set_status_addr(struct hinic_api_cmd_chain *chain) { struct hinic_hwif *hwif = chain->hwdev->hwif; u32 addr, val; addr = HINIC_CSR_API_CMD_STATUS_HI_ADDR(chain->chain_type); val = upper_32_bits(chain->wb_status_paddr); hinic_hwif_write_reg(hwif, addr, val); addr = HINIC_CSR_API_CMD_STATUS_LO_ADDR(chain->chain_type); val = lower_32_bits(chain->wb_status_paddr); hinic_hwif_write_reg(hwif, addr, val); } /** * api_cmd_set_num_cells - set the number cells of a chain in the HW * @chain: the API CMD specific chain to set the number of cells for */ static void api_cmd_set_num_cells(struct hinic_api_cmd_chain *chain) { struct hinic_hwif *hwif = chain->hwdev->hwif; u32 addr, val; addr = HINIC_CSR_API_CMD_CHAIN_NUM_CELLS_ADDR(chain->chain_type); val = chain->num_cells; hinic_hwif_write_reg(hwif, addr, val); } /** * api_cmd_head_init - set the head cell of a chain in the HW * @chain: the API CMD specific chain to set the head for */ static void api_cmd_head_init(struct hinic_api_cmd_chain *chain) { struct hinic_hwif *hwif = chain->hwdev->hwif; u32 addr, val; addr = HINIC_CSR_API_CMD_CHAIN_HEAD_HI_ADDR(chain->chain_type); val = upper_32_bits(chain->head_cell_paddr); hinic_hwif_write_reg(hwif, addr, val); addr = HINIC_CSR_API_CMD_CHAIN_HEAD_LO_ADDR(chain->chain_type); val = lower_32_bits(chain->head_cell_paddr); hinic_hwif_write_reg(hwif, addr, val); } /** * wait_for_ready_chain - wait for the chain to be ready * @chain: the API CMD specific chain to wait for * Return: 0 - success, negative - failure */ static int wait_for_ready_chain(struct hinic_api_cmd_chain *chain) { struct hinic_hwif *hwif = chain->hwdev->hwif; u32 addr, val; u32 hw_cons_idx; u32 cnt = 0; int err; addr = HINIC_CSR_API_CMD_STATUS_0_ADDR(chain->chain_type); err = -ETIMEDOUT; while (cnt < API_CMD_TIMEOUT) { val = hinic_hwif_read_reg(hwif, addr); hw_cons_idx = HINIC_API_CMD_STATUS_GET(val, CONS_IDX); /* wait for HW cons idx to be updated */ if (hw_cons_idx == chain->cons_idx) { err = 0; break; } usleep_range(900, 1000); cnt++; } return err; } /** * api_cmd_chain_hw_clean - clean the HW * @chain: the API CMD specific chain */ static void api_cmd_chain_hw_clean(struct hinic_api_cmd_chain *chain) { struct hinic_hwif *hwif = chain->hwdev->hwif; u32 addr, ctrl; addr = HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(chain->chain_type); ctrl = hinic_hwif_read_reg(hwif, addr); ctrl = HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, RESTART_EN) & HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, XOR_ERR) & HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, AEQE_EN) & HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, XOR_CHK_EN) & HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, CELL_SIZE); hinic_hwif_write_reg(hwif, addr, ctrl); } /** * api_cmd_chain_hw_init - initialize the chain in the HW * @chain: the API CMD specific chain to initialize in HW * Return: 0 - success, negative - failure */ static int api_cmd_chain_hw_init(struct hinic_api_cmd_chain *chain) { api_cmd_chain_hw_clean(chain); api_cmd_set_status_addr(chain); if (api_cmd_hw_restart(chain)) { sdk_err(chain->hwdev->dev_hdl, "Failed to restart api_cmd_hw\n"); return -EBUSY; } api_cmd_ctrl_init(chain); api_cmd_set_num_cells(chain); api_cmd_head_init(chain); return wait_for_ready_chain(chain); } /** * alloc_cmd_buf - allocate a dma buffer for API CMD command * @chain: the API CMD specific chain for the cmd * @cell: the cell in the HW for the cmd * @cell_idx: the index of the cell * Return: 0 - success, negative - failure */ static int alloc_cmd_buf(struct hinic_api_cmd_chain *chain, struct hinic_api_cmd_cell *cell, u32 cell_idx) { struct hinic_api_cmd_cell_ctxt *cell_ctxt; void *dev = chain->hwdev->dev_hdl; void *buf_vaddr; u64 buf_paddr; int err = 0; buf_vaddr = (u8 *)((u64)chain->buf_vaddr_base + chain->buf_size_align * cell_idx); buf_paddr = chain->buf_paddr_base + chain->buf_size_align * cell_idx; cell_ctxt = &chain->cell_ctxt[cell_idx]; cell_ctxt->api_cmd_vaddr = buf_vaddr; /* set the cmd DMA address in the cell */ switch (chain->chain_type) { case HINIC_API_CMD_POLL_READ: cell->read.hw_cmd_paddr = cpu_to_be64(buf_paddr); break; case HINIC_API_CMD_WRITE_TO_MGMT_CPU: case HINIC_API_CMD_POLL_WRITE: case HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU: /* The data in the HW should be in Big Endian Format */ cell->write.hw_cmd_paddr = cpu_to_be64(buf_paddr); break; default: sdk_err(dev, "Unknown API CMD Chain type: %d\n", chain->chain_type); err = -EINVAL; break; } return err; } static void alloc_resp_buf(struct hinic_api_cmd_chain *chain, struct hinic_api_cmd_cell *cell, u32 cell_idx) { struct hinic_api_cmd_cell_ctxt *cell_ctxt; void *resp_vaddr; u64 resp_paddr; resp_vaddr = (u8 *)((u64)chain->rsp_vaddr_base + chain->rsp_size_align * cell_idx); resp_paddr = chain->rsp_paddr_base + chain->rsp_size_align * cell_idx; cell_ctxt = &chain->cell_ctxt[cell_idx]; cell_ctxt->resp = resp_vaddr; cell->read.hw_wb_resp_paddr = cpu_to_be64(resp_paddr); } static int hinic_alloc_api_cmd_cell_buf(struct hinic_api_cmd_chain *chain, u32 cell_idx, struct hinic_api_cmd_cell *node) { void *dev = chain->hwdev->dev_hdl; int err; /* For read chain, we should allocate buffer for the response data */ if (chain->chain_type == HINIC_API_CMD_MULTI_READ || chain->chain_type == HINIC_API_CMD_POLL_READ) alloc_resp_buf(chain, node, cell_idx); switch (chain->chain_type) { case HINIC_API_CMD_WRITE_TO_MGMT_CPU: case HINIC_API_CMD_POLL_WRITE: case HINIC_API_CMD_POLL_READ: case HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU: err = alloc_cmd_buf(chain, node, cell_idx); if (err) { sdk_err(dev, "Failed to allocate cmd buffer\n"); goto alloc_cmd_buf_err; } break; /* For api command write and api command read, the data section * is directly inserted in the cell, so no need to allocate. */ case HINIC_API_CMD_MULTI_READ: chain->cell_ctxt[cell_idx].api_cmd_vaddr = &node->read.hw_cmd_paddr; break; default: sdk_err(dev, "Unsupported API CMD chain type\n"); err = -EINVAL; goto alloc_cmd_buf_err; } return 0; alloc_cmd_buf_err: return err; } /** * api_cmd_create_cell - create API CMD cell of specific chain * @chain: the API CMD specific chain to create its cell * @cell_idx: the cell index to create * @pre_node: previous cell * @node_vaddr: the virt addr of the cell * Return: 0 - success, negative - failure */ static int api_cmd_create_cell(struct hinic_api_cmd_chain *chain, u32 cell_idx, struct hinic_api_cmd_cell *pre_node, struct hinic_api_cmd_cell **node_vaddr) { struct hinic_api_cmd_cell_ctxt *cell_ctxt; struct hinic_api_cmd_cell *node; void *cell_vaddr; u64 cell_paddr; int err; cell_vaddr = (void *)((u64)chain->cell_vaddr_base + chain->cell_size_align * cell_idx); cell_paddr = chain->cell_paddr_base + chain->cell_size_align * cell_idx; cell_ctxt = &chain->cell_ctxt[cell_idx]; cell_ctxt->cell_vaddr = cell_vaddr; node = cell_ctxt->cell_vaddr; if (!pre_node) { chain->head_node = cell_vaddr; chain->head_cell_paddr = cell_paddr; } else { /* The data in the HW should be in Big Endian Format */ pre_node->next_cell_paddr = cpu_to_be64(cell_paddr); } /* Driver software should make sure that there is an empty API * command cell at the end the chain */ node->next_cell_paddr = 0; err = hinic_alloc_api_cmd_cell_buf(chain, cell_idx, node); if (err) return err; *node_vaddr = node; return 0; } /** * api_cmd_create_cells - create API CMD cells for specific chain * @chain: the API CMD specific chain * Return: 0 - success, negative - failure */ static int api_cmd_create_cells(struct hinic_api_cmd_chain *chain) { struct hinic_api_cmd_cell *node = NULL, *pre_node = NULL; void *dev = chain->hwdev->dev_hdl; u32 cell_idx; int err; for (cell_idx = 0; cell_idx < chain->num_cells; cell_idx++) { err = api_cmd_create_cell(chain, cell_idx, pre_node, &node); if (err) { sdk_err(dev, "Failed to create API CMD cell\n"); return err; } pre_node = node; } if (!node) return -EFAULT; /* set the Final node to point on the start */ node->next_cell_paddr = cpu_to_be64(chain->head_cell_paddr); /* set the current node to be the head */ chain->curr_node = chain->head_node; return 0; } /** * api_chain_init - initialize API CMD specific chain * @chain: the API CMD specific chain to initialize * @attr: attributes to set in the chain * Return: 0 - success, negative - failure */ static int api_chain_init(struct hinic_api_cmd_chain *chain, struct hinic_api_cmd_chain_attr *attr) { void *dev = chain->hwdev->dev_hdl; size_t cell_ctxt_size; size_t cells_buf_size; int err; chain->chain_type = attr->chain_type; chain->num_cells = attr->num_cells; chain->cell_size = attr->cell_size; chain->rsp_size = attr->rsp_size; chain->prod_idx = 0; chain->cons_idx = 0; if (chain->chain_type == HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU) spin_lock_init(&chain->async_lock); else sema_init(&chain->sem, 1); cell_ctxt_size = chain->num_cells * sizeof(*chain->cell_ctxt); if (!cell_ctxt_size) { sdk_err(dev, "Api chain cell size cannot be zero\n"); return -EINVAL; } chain->cell_ctxt = kzalloc(cell_ctxt_size, GFP_KERNEL); if (!chain->cell_ctxt) return -ENOMEM; chain->wb_status = dma_alloc_coherent(dev, sizeof(*chain->wb_status), &chain->wb_status_paddr, GFP_KERNEL); if (!chain->wb_status) { sdk_err(dev, "Failed to allocate DMA wb status\n"); err = -ENOMEM; goto alloc_wb_status_err; } chain->cell_size_align = ALIGN((u64)chain->cell_size, API_CMD_NODE_ALIGN_SIZE); chain->rsp_size_align = ALIGN((u64)chain->rsp_size, API_CHAIN_RESP_ALIGNMENT); chain->buf_size_align = ALIGN(API_CMD_BUF_SIZE, API_PAYLOAD_ALIGN_SIZE); cells_buf_size = (chain->cell_size_align + chain->rsp_size_align + chain->buf_size_align) * chain->num_cells; err = hinic_dma_alloc_coherent_align(dev, cells_buf_size, API_CMD_NODE_ALIGN_SIZE, GFP_KERNEL, &chain->cells_addr); if (err) { sdk_err(dev, "Failed to allocate API CMD cells buffer\n"); goto alloc_cells_buf_err; } chain->cell_vaddr_base = chain->cells_addr.align_vaddr; chain->cell_paddr_base = chain->cells_addr.align_paddr; chain->rsp_vaddr_base = (u8 *)((u64)chain->cell_vaddr_base + chain->cell_size_align * chain->num_cells); chain->rsp_paddr_base = chain->cell_paddr_base + chain->cell_size_align * chain->num_cells; chain->buf_vaddr_base = (u8 *)((u64)chain->rsp_vaddr_base + chain->rsp_size_align * chain->num_cells); chain->buf_paddr_base = chain->rsp_paddr_base + chain->rsp_size_align * chain->num_cells; return 0; alloc_cells_buf_err: dma_free_coherent(dev, sizeof(*chain->wb_status), chain->wb_status, chain->wb_status_paddr); alloc_wb_status_err: kfree(chain->cell_ctxt); return err; } /** * api_chain_free - free API CMD specific chain * @chain: the API CMD specific chain to free */ static void api_chain_free(struct hinic_api_cmd_chain *chain) { void *dev = chain->hwdev->dev_hdl; hinic_dma_free_coherent_align(dev, &chain->cells_addr); dma_free_coherent(dev, sizeof(*chain->wb_status), chain->wb_status, chain->wb_status_paddr); kfree(chain->cell_ctxt); } /** * api_cmd_create_chain - create API CMD specific chain * @chain: the API CMD specific chain to create * @attr: attributes to set in the chain * Return: 0 - success, negative - failure */ static int api_cmd_create_chain(struct hinic_api_cmd_chain **cmd_chain, struct hinic_api_cmd_chain_attr *attr) { struct hinic_hwdev *hwdev = attr->hwdev; struct hinic_api_cmd_chain *chain; int err; if (attr->num_cells & (attr->num_cells - 1)) { sdk_err(hwdev->dev_hdl, "Invalid number of cells, must be power of 2\n"); return -EINVAL; } chain = kzalloc(sizeof(*chain), GFP_KERNEL); if (!chain) return -ENOMEM; chain->hwdev = hwdev; err = api_chain_init(chain, attr); if (err) { sdk_err(hwdev->dev_hdl, "Failed to initialize chain\n"); goto chain_init_err; } err = api_cmd_create_cells(chain); if (err) { sdk_err(hwdev->dev_hdl, "Failed to create cells for API CMD chain\n"); goto create_cells_err; } err = api_cmd_chain_hw_init(chain); if (err) { sdk_err(hwdev->dev_hdl, "Failed to initialize chain HW\n"); goto chain_hw_init_err; } *cmd_chain = chain; return 0; chain_hw_init_err: create_cells_err: api_chain_free(chain); chain_init_err: kfree(chain); return err; } /** * api_cmd_destroy_chain - destroy API CMD specific chain * @chain: the API CMD specific chain to destroy */ static void api_cmd_destroy_chain(struct hinic_api_cmd_chain *chain) { api_chain_free(chain); kfree(chain); } /** * hinic_api_cmd_init - Initialize all the API CMD chains * @hwdev: the pointer to hw device * @chain: the API CMD chains that will be initialized * Return: 0 - success, negative - failure */ int hinic_api_cmd_init(struct hinic_hwdev *hwdev, struct hinic_api_cmd_chain **chain) { void *dev = hwdev->dev_hdl; struct hinic_api_cmd_chain_attr attr; enum hinic_api_cmd_chain_type chain_type, i; int err; attr.hwdev = hwdev; attr.num_cells = API_CHAIN_NUM_CELLS; attr.cell_size = API_CHAIN_CELL_SIZE; attr.rsp_size = API_CHAIN_RSP_DATA_SIZE; chain_type = HINIC_API_CMD_WRITE_TO_MGMT_CPU; for (; chain_type < HINIC_API_CMD_MAX; chain_type++) { attr.chain_type = chain_type; err = api_cmd_create_chain(&chain[chain_type], &attr); if (err) { sdk_err(dev, "Failed to create chain %d\n", chain_type); goto create_chain_err; } } return 0; create_chain_err: i = HINIC_API_CMD_WRITE_TO_MGMT_CPU; for (; i < chain_type; i++) api_cmd_destroy_chain(chain[i]); return err; } /** * hinic_api_cmd_free - free the API CMD chains * @chain: the API CMD chains that will be freed */ void hinic_api_cmd_free(struct hinic_api_cmd_chain **chain) { enum hinic_api_cmd_chain_type chain_type; chain_type = HINIC_API_CMD_WRITE_TO_MGMT_CPU; for (; chain_type < HINIC_API_CMD_MAX; chain_type++) api_cmd_destroy_chain(chain[chain_type]); } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_api_cmd.h000066400000000000000000000221521502027332500220500ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_API_CMD_H_ #define HINIC_API_CMD_H_ #define HINIC_API_CMD_CELL_CTRL_CELL_LEN_SHIFT 0 #define HINIC_API_CMD_CELL_CTRL_RD_DMA_ATTR_OFF_SHIFT 16 #define HINIC_API_CMD_CELL_CTRL_WR_DMA_ATTR_OFF_SHIFT 24 #define HINIC_API_CMD_CELL_CTRL_XOR_CHKSUM_SHIFT 56 #define HINIC_API_CMD_CELL_CTRL_CELL_LEN_MASK 0x3FU #define HINIC_API_CMD_CELL_CTRL_RD_DMA_ATTR_OFF_MASK 0x3FU #define HINIC_API_CMD_CELL_CTRL_WR_DMA_ATTR_OFF_MASK 0x3FU #define HINIC_API_CMD_CELL_CTRL_XOR_CHKSUM_MASK 0xFFU #define HINIC_API_CMD_CELL_CTRL_SET(val, member) \ ((((u64)val) & HINIC_API_CMD_CELL_CTRL_##member##_MASK) << \ HINIC_API_CMD_CELL_CTRL_##member##_SHIFT) #define HINIC_API_CMD_DESC_API_TYPE_SHIFT 0 #define HINIC_API_CMD_DESC_RD_WR_SHIFT 1 #define HINIC_API_CMD_DESC_MGMT_BYPASS_SHIFT 2 #define HINIC_API_CMD_DESC_RESP_AEQE_EN_SHIFT 3 #define HINIC_API_CMD_DESC_PRIV_DATA_SHIFT 8 #define HINIC_API_CMD_DESC_DEST_SHIFT 32 #define HINIC_API_CMD_DESC_SIZE_SHIFT 40 #define HINIC_API_CMD_DESC_XOR_CHKSUM_SHIFT 56 #define HINIC_API_CMD_DESC_API_TYPE_MASK 0x1U #define HINIC_API_CMD_DESC_RD_WR_MASK 0x1U #define HINIC_API_CMD_DESC_MGMT_BYPASS_MASK 0x1U #define HINIC_API_CMD_DESC_RESP_AEQE_EN_MASK 0x1U #define HINIC_API_CMD_DESC_DEST_MASK 0x1FU #define HINIC_API_CMD_DESC_SIZE_MASK 0x7FFU #define HINIC_API_CMD_DESC_XOR_CHKSUM_MASK 0xFFU #define HINIC_API_CMD_DESC_PRIV_DATA_MASK 0xFFFFFFU #define HINIC_API_CMD_DESC_SET(val, member) \ ((((u64)val) & HINIC_API_CMD_DESC_##member##_MASK) << \ HINIC_API_CMD_DESC_##member##_SHIFT) #define HINIC_API_CMD_STATUS_HEADER_VALID_SHIFT 0 #define HINIC_API_CMD_STATUS_HEADER_CHAIN_ID_SHIFT 16 #define HINIC_API_CMD_STATUS_HEADER_VALID_MASK 0xFFU #define HINIC_API_CMD_STATUS_HEADER_CHAIN_ID_MASK 0xFFU #define HINIC_API_CMD_STATUS_VALID_CODE 0xFF #define HINIC_API_CMD_STATUS_HEADER_GET(val, member) \ (((val) >> HINIC_API_CMD_STATUS_HEADER_##member##_SHIFT) & \ HINIC_API_CMD_STATUS_HEADER_##member##_MASK) #define HINIC_API_CMD_CHAIN_REQ_RESTART_SHIFT 1 #define HINIC_API_CMD_CHAIN_REQ_WB_TRIGGER_SHIFT 2 #define HINIC_API_CMD_CHAIN_REQ_RESTART_MASK 0x1U #define HINIC_API_CMD_CHAIN_REQ_WB_TRIGGER_MASK 0x1U #define HINIC_API_CMD_CHAIN_REQ_SET(val, member) \ (((val) & HINIC_API_CMD_CHAIN_REQ_##member##_MASK) << \ HINIC_API_CMD_CHAIN_REQ_##member##_SHIFT) #define HINIC_API_CMD_CHAIN_REQ_GET(val, member) \ (((val) >> HINIC_API_CMD_CHAIN_REQ_##member##_SHIFT) & \ HINIC_API_CMD_CHAIN_REQ_##member##_MASK) #define HINIC_API_CMD_CHAIN_REQ_CLEAR(val, member) \ ((val) & (~(HINIC_API_CMD_CHAIN_REQ_##member##_MASK \ << HINIC_API_CMD_CHAIN_REQ_##member##_SHIFT))) #define HINIC_API_CMD_CHAIN_CTRL_RESTART_EN_SHIFT 1 #define HINIC_API_CMD_CHAIN_CTRL_XOR_ERR_SHIFT 2 #define HINIC_API_CMD_CHAIN_CTRL_AEQE_EN_SHIFT 4 #define HINIC_API_CMD_CHAIN_CTRL_AEQ_ID_SHIFT 8 #define HINIC_API_CMD_CHAIN_CTRL_XOR_CHK_EN_SHIFT 28 #define HINIC_API_CMD_CHAIN_CTRL_CELL_SIZE_SHIFT 30 #define HINIC_API_CMD_CHAIN_CTRL_RESTART_EN_MASK 0x1U #define HINIC_API_CMD_CHAIN_CTRL_XOR_ERR_MASK 0x1U #define HINIC_API_CMD_CHAIN_CTRL_AEQE_EN_MASK 0x1U #define HINIC_API_CMD_CHAIN_CTRL_AEQ_ID_MASK 0x3U #define HINIC_API_CMD_CHAIN_CTRL_XOR_CHK_EN_MASK 0x3U #define HINIC_API_CMD_CHAIN_CTRL_CELL_SIZE_MASK 0x3U #define HINIC_API_CMD_CHAIN_CTRL_SET(val, member) \ (((val) & HINIC_API_CMD_CHAIN_CTRL_##member##_MASK) << \ HINIC_API_CMD_CHAIN_CTRL_##member##_SHIFT) #define HINIC_API_CMD_CHAIN_CTRL_CLEAR(val, member) \ ((val) & (~(HINIC_API_CMD_CHAIN_CTRL_##member##_MASK \ << HINIC_API_CMD_CHAIN_CTRL_##member##_SHIFT))) #define HINIC_API_CMD_RESP_HEAD_VALID_MASK 0xFF #define HINIC_API_CMD_RESP_HEAD_VALID_CODE 0xFF #define HINIC_API_CMD_RESP_HEADER_VALID(val) \ (((val) & HINIC_API_CMD_RESP_HEAD_VALID_MASK) == \ HINIC_API_CMD_RESP_HEAD_VALID_CODE) #define HINIC_API_CMD_RESP_HEAD_STATUS_SHIFT 8 #define HINIC_API_CMD_RESP_HEAD_STATUS_MASK 0xFFU #define HINIC_API_CMD_RESP_HEAD_ERR_CODE 0x1 #define HINIC_API_CMD_RESP_HEAD_ERR(val) \ ((((val) >> HINIC_API_CMD_RESP_HEAD_STATUS_SHIFT) & \ HINIC_API_CMD_RESP_HEAD_STATUS_MASK) == \ HINIC_API_CMD_RESP_HEAD_ERR_CODE) #define HINIC_API_CMD_RESP_HEAD_CHAIN_ID_SHIFT 16 #define HINIC_API_CMD_RESP_HEAD_CHAIN_ID_MASK 0xFF #define HINIC_API_CMD_RESP_RESERVED 3 #define HINIC_API_CMD_RESP_HEAD_CHAIN_ID(val) \ (((val) >> HINIC_API_CMD_RESP_HEAD_CHAIN_ID_SHIFT) & \ HINIC_API_CMD_RESP_HEAD_CHAIN_ID_MASK) #define HINIC_API_CMD_RESP_HEAD_DRIVER_PRIV_SHIFT 40 #define HINIC_API_CMD_RESP_HEAD_DRIVER_PRIV_MASK 0xFFFFFFU #define HINIC_API_CMD_RESP_HEAD_DRIVER_PRIV(val) \ ((u16)(((val) >> HINIC_API_CMD_RESP_HEAD_DRIVER_PRIV_SHIFT) & \ HINIC_API_CMD_RESP_HEAD_DRIVER_PRIV_MASK)) #define HINIC_API_CMD_STATUS_HEAD_VALID_MASK 0xFFU #define HINIC_API_CMD_STATUS_HEAD_VALID_SHIFT 0 #define HINIC_API_CMD_STATUS_HEAD_CHAIN_ID_MASK 0xFFU #define HINIC_API_CMD_STATUS_HEAD_CHAIN_ID_VALID_SHIFT 16 #define HINIC_API_CMD_STATUS_CONS_IDX_MASK 0xFFFFFFU #define HINIC_API_CMD_STATUS_CONS_IDX_SHIFT 0 #define HINIC_API_CMD_STATUS_FSM_MASK 0xFU #define HINIC_API_CMD_STATUS_FSM_SHIFT 24 #define HINIC_API_CMD_STATUS_CHKSUM_ERR_MASK 0x3U #define HINIC_API_CMD_STATUS_CHKSUM_ERR_SHIFT 28 #define HINIC_API_CMD_STATUS_CPLD_ERR_MASK 0x1U #define HINIC_API_CMD_STATUS_CPLD_ERR_SHIFT 30 #define HINIC_API_CMD_STATUS_CHAIN_ID(val) \ (((val) >> HINIC_API_CMD_STATUS_HEAD_CHAIN_ID_VALID_SHIFT) & \ HINIC_API_CMD_STATUS_HEAD_VALID_MASK) #define HINIC_API_CMD_STATUS_CONS_IDX(val) \ ((val) & HINIC_API_CMD_STATUS_CONS_IDX_MASK) #define HINIC_API_CMD_STATUS_CHKSUM_ERR(val) \ (((val) >> HINIC_API_CMD_STATUS_CHKSUM_ERR_SHIFT) & \ HINIC_API_CMD_STATUS_CHKSUM_ERR_MASK) #define HINIC_API_CMD_STATUS_GET(val, member) \ (((val) >> HINIC_API_CMD_STATUS_##member##_SHIFT) & \ HINIC_API_CMD_STATUS_##member##_MASK) enum hinic_api_cmd_chain_type { /* write command with completion notification */ HINIC_API_CMD_WRITE = 0, /* read command with completion notification */ HINIC_API_CMD_READ = 1, /* write to mgmt cpu command with completion */ HINIC_API_CMD_WRITE_TO_MGMT_CPU = 2, /* multi read command with completion notification - not used */ HINIC_API_CMD_MULTI_READ = 3, /* write command without completion notification */ HINIC_API_CMD_POLL_WRITE = 4, /* read command without completion notification */ HINIC_API_CMD_POLL_READ = 5, /* read from mgmt cpu command with completion */ HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU = 6, HINIC_API_CMD_MAX, }; struct hinic_api_cmd_status { u64 header; u32 buf_desc; u32 cell_addr_hi; u32 cell_addr_lo; u32 rsvd0; u64 rsvd1; }; /* HW struct */ struct hinic_api_cmd_cell { u64 ctrl; /* address is 64 bit in HW struct */ u64 next_cell_paddr; u64 desc; /* HW struct */ union { struct { u64 hw_cmd_paddr; } write; struct { u64 hw_wb_resp_paddr; u64 hw_cmd_paddr; } read; }; }; struct hinic_api_cmd_resp_fmt { u64 header; u64 rsvd[3]; u64 resp_data; }; struct hinic_api_cmd_cell_ctxt { struct hinic_api_cmd_cell *cell_vaddr; void *api_cmd_vaddr; struct hinic_api_cmd_resp_fmt *resp; struct completion done; int status; u32 saved_prod_idx; }; struct hinic_api_cmd_chain_attr { struct hinic_hwdev *hwdev; enum hinic_api_cmd_chain_type chain_type; u32 num_cells; u16 rsp_size; u16 cell_size; }; struct hinic_api_cmd_chain { struct hinic_hwdev *hwdev; enum hinic_api_cmd_chain_type chain_type; u32 num_cells; u16 cell_size; u16 rsp_size; /* HW members is 24 bit format */ u32 prod_idx; u32 cons_idx; struct semaphore sem; /* Async cmd can not be scheduling */ spinlock_t async_lock; dma_addr_t wb_status_paddr; struct hinic_api_cmd_status *wb_status; dma_addr_t head_cell_paddr; struct hinic_api_cmd_cell *head_node; struct hinic_api_cmd_cell_ctxt *cell_ctxt; struct hinic_api_cmd_cell *curr_node; struct hinic_dma_addr_align cells_addr; u8 *cell_vaddr_base; u64 cell_paddr_base; u8 *rsp_vaddr_base; u64 rsp_paddr_base; u8 *buf_vaddr_base; u64 buf_paddr_base; u64 cell_size_align; u64 rsp_size_align; u64 buf_size_align; }; int hinic_api_cmd_write(struct hinic_api_cmd_chain *chain, enum hinic_node_id dest, void *cmd, u16 size); int hinic_api_cmd_read(struct hinic_api_cmd_chain *chain, enum hinic_node_id dest, void *cmd, u16 size, void *ack, u16 ack_size); int hinic_api_cmd_init(struct hinic_hwdev *hwdev, struct hinic_api_cmd_chain **chain); void hinic_api_cmd_free(struct hinic_api_cmd_chain **chain); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_cfg.c000066400000000000000000001733261502027332500212200ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hwdev.h" #include "hinic_hw_mgmt.h" #include "hinic_hwif.h" #include "hinic_mbox.h" #include "hinic_cfg.h" #include "hinic_nic_cfg.h" #include "hinic_mgmt_interface.h" #include "hinic_multi_host_mgmt.h" uint g_rdma_mtts_num; uint g_rdma_qps_num; uint g_rdma_mpts_num; uint g_vfs_num; module_param(g_rdma_mtts_num, uint, 0444); MODULE_PARM_DESC(g_rdma_mtts_num, "number of roce used mtts, use default value when pass 0"); module_param(g_rdma_qps_num, uint, 0444); MODULE_PARM_DESC(g_rdma_qps_num, "number of roce used qps, use default value when pass 0"); module_param(g_rdma_mpts_num, uint, 0444); MODULE_PARM_DESC(g_rdma_mpts_num, "number of roce used mpts, use default value when pass 0"); module_param(g_vfs_num, uint, 0444); MODULE_PARM_DESC(g_vfs_num, "number of used vfs, use default value when pass 0 "); static uint intr_mode; uint timer_enable = 1; uint bloomfilter_enable; uint g_test_qpc_num; uint g_test_qpc_resvd_num; uint g_test_pagesize_reorder; uint g_test_xid_alloc_mode = 1; uint g_test_gpa_check_enable = 1; uint g_test_qpc_alloc_mode = 2; uint g_test_scqc_alloc_mode = 2; uint g_test_max_conn; uint g_test_max_cache_conn; uint g_test_scqc_num; uint g_test_mpt_num; uint g_test_mpt_resvd; uint g_test_scq_resvd; uint g_test_hash_num; uint g_test_reorder_num; static void set_cfg_test_param(struct cfg_mgmt_info *cfg_mgmt) { cfg_mgmt->svc_cap.timer_en = (u8)timer_enable; cfg_mgmt->svc_cap.bloomfilter_en = (u8)bloomfilter_enable; cfg_mgmt->svc_cap.test_qpc_num = g_test_qpc_num; cfg_mgmt->svc_cap.test_qpc_resvd_num = g_test_qpc_resvd_num; cfg_mgmt->svc_cap.test_page_size_reorder = g_test_pagesize_reorder; cfg_mgmt->svc_cap.test_xid_alloc_mode = (bool)g_test_xid_alloc_mode; cfg_mgmt->svc_cap.test_gpa_check_enable = (bool)g_test_gpa_check_enable; cfg_mgmt->svc_cap.test_qpc_alloc_mode = (u8)g_test_qpc_alloc_mode; cfg_mgmt->svc_cap.test_scqc_alloc_mode = (u8)g_test_scqc_alloc_mode; cfg_mgmt->svc_cap.test_max_conn_num = g_test_max_conn; cfg_mgmt->svc_cap.test_max_cache_conn_num = g_test_max_cache_conn; cfg_mgmt->svc_cap.test_scqc_num = g_test_scqc_num; cfg_mgmt->svc_cap.test_mpt_num = g_test_mpt_num; cfg_mgmt->svc_cap.test_scq_resvd_num = g_test_scq_resvd; cfg_mgmt->svc_cap.test_mpt_recvd_num = g_test_mpt_resvd; cfg_mgmt->svc_cap.test_hash_num = g_test_hash_num; cfg_mgmt->svc_cap.test_reorder_num = g_test_reorder_num; } int hinic_sync_time(void *hwdev, u64 time) { struct hinic_sync_time_info time_info = {0}; u16 out_size = sizeof(time_info); int err; time_info.mstime = time; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_SYNC_TIME, &time_info, sizeof(time_info), &time_info, &out_size, 0); if (err || time_info.status || !out_size) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to sync time to mgmt, err: %d, status: 0x%x, out size: 0x%x\n", err, time_info.status, out_size); return -EFAULT; } return err; } void hinic_sync_time_async(void *hwdev, u64 time) { struct hinic_sync_time_info time_info = { 0 }; time_info.mstime = time; hinic_msg_to_mgmt_async(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_SYNC_TIME, &time_info, sizeof(time_info)); } static void parse_sf_en_cap(struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct dev_sf_svc_attr *attr = &cap->sf_svc_attr; if (type == TYPE_PPF) { /* For PPF's SF EN flag, we assign it in get_dynamic_res_cap(). * we only save its VF's flag. */ attr->sf_en_vf = dev_cap->sf_en_vf; } else if (type == TYPE_PF) { if (dev_cap->sf_en_pf) cap->sf_en = true; else cap->sf_en = false; attr->sf_en_vf = dev_cap->sf_en_vf; } else { /* VF gets SF_EN_VF from PPF/PF */ if (dev_cap->sf_en_vf) cap->sf_en = true; else cap->sf_en = false; attr->sf_en_vf = 0; } } static void parse_pub_res_cap(struct hinic_hwdev *hwdev, struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct dev_sf_svc_attr *attr = &cap->sf_svc_attr; cap->svc_type = dev_cap->svc_cap_en; cap->chip_svc_type = cap->svc_type; if (dev_cap->sf_svc_attr & SF_SVC_FT_BIT) attr->ft_en = true; else attr->ft_en = false; if (dev_cap->sf_svc_attr & SF_SVC_RDMA_BIT) attr->rdma_en = true; else attr->rdma_en = false; cap->host_id = dev_cap->host_id; cap->ep_id = dev_cap->ep_id; cap->max_cos_id = dev_cap->max_cos_id; cap->cos_valid_bitmap = dev_cap->valid_cos_bitmap; cap->er_id = dev_cap->er_id; cap->port_id = dev_cap->port_id; cap->force_up = dev_cap->force_up; parse_sf_en_cap(cap, dev_cap, type); /* PF/PPF */ if (type == TYPE_PF || type == TYPE_PPF) { cap->max_vf = dev_cap->max_vf; cap->pf_num = dev_cap->pf_num; cap->pf_id_start = dev_cap->pf_id_start; cap->vf_num = dev_cap->vf_num; cap->vf_id_start = dev_cap->vf_id_start; /* FC need max queue number, but max queue number info is in * l2nic cap, we also put max queue num info in public cap, so * FC can get correct max queue number info. */ cap->max_sqs = dev_cap->nic_max_sq + 1; cap->max_rqs = dev_cap->nic_max_rq + 1; } else { cap->max_vf = 0; cap->max_sqs = dev_cap->nic_max_sq; cap->max_rqs = dev_cap->nic_max_rq; } cap->host_total_function = dev_cap->host_total_func; cap->host_oq_id_mask_val = dev_cap->host_oq_id_mask_val; cap->max_connect_num = dev_cap->max_conn_num; cap->max_stick2cache_num = dev_cap->max_stick2cache_num; cap->bfilter_start_addr = dev_cap->max_bfilter_start_addr; cap->bfilter_len = dev_cap->bfilter_len; cap->hash_bucket_num = dev_cap->hash_bucket_num; cap->dev_ver_info.cfg_file_ver = dev_cap->cfg_file_ver; cap->net_port_mode = dev_cap->net_port_mode; /* FC does not use VF */ if (cap->net_port_mode == CFG_NET_MODE_FC) cap->max_vf = 0; sdk_info(hwdev->dev_hdl, "Get public resource capbility, svc_cap_en: 0x%x\n", dev_cap->svc_cap_en); sdk_info(hwdev->dev_hdl, "Host_id=0x%x, ep_id=0x%x, max_cos_id=0x%x, cos_bitmap=0x%x, er_id=0x%x, port_id=0x%x\n", cap->host_id, cap->ep_id, cap->max_cos_id, cap->cos_valid_bitmap, cap->er_id, cap->port_id); sdk_info(hwdev->dev_hdl, "Host_total_function=0x%x, host_oq_id_mask_val=0x%x, net_port_mode=0x%x, max_vf=0x%x\n", cap->host_total_function, cap->host_oq_id_mask_val, cap->net_port_mode, cap->max_vf); sdk_info(hwdev->dev_hdl, "Pf_num=0x%x, pf_id_start=0x%x, vf_num=0x%x, vf_id_start=0x%x\n", cap->pf_num, cap->pf_id_start, cap->vf_num, cap->vf_id_start); /* Check parameters from firmware */ if (cap->max_sqs > HINIC_CFG_MAX_QP || cap->max_rqs > HINIC_CFG_MAX_QP) { sdk_info(hwdev->dev_hdl, "Number of qp exceed limit[1-%d]: sq: %d, rq: %d\n", HINIC_CFG_MAX_QP, cap->max_sqs, cap->max_rqs); cap->max_sqs = HINIC_CFG_MAX_QP; cap->max_rqs = HINIC_CFG_MAX_QP; } } static void parse_dynamic_share_res_cap(struct hinic_hwdev *hwdev, struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct host_shared_resource_cap *shared_cap = &cap->shared_res_cap; shared_cap->host_pctxs = dev_cap->host_pctx_num; if (dev_cap->host_sf_en) cap->sf_en = true; else cap->sf_en = false; shared_cap->host_cctxs = dev_cap->host_ccxt_num; shared_cap->host_scqs = dev_cap->host_scq_num; shared_cap->host_srqs = dev_cap->host_srq_num; shared_cap->host_mpts = dev_cap->host_mpt_num; sdk_info(hwdev->dev_hdl, "Dynamic share resource capbility, host_pctxs=0x%x, host_cctxs=0x%x, host_scqs=0x%x, host_srqs=0x%x, host_mpts=0x%x\n", shared_cap->host_pctxs, shared_cap->host_cctxs, shared_cap->host_scqs, shared_cap->host_srqs, shared_cap->host_mpts); } static void parse_l2nic_res_cap(struct hinic_hwdev *hwdev, struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct nic_service_cap *nic_cap = &cap->nic_cap; /* PF/PPF */ if (type == TYPE_PF || type == TYPE_PPF) { nic_cap->max_sqs = dev_cap->nic_max_sq + 1; nic_cap->max_rqs = dev_cap->nic_max_rq + 1; nic_cap->vf_max_sqs = dev_cap->nic_vf_max_sq + 1; nic_cap->vf_max_rqs = dev_cap->nic_vf_max_rq + 1; nic_cap->max_queue_allowed = 0; nic_cap->dynamic_qp = 0; } else { nic_cap->max_sqs = dev_cap->nic_max_sq; nic_cap->max_rqs = dev_cap->nic_max_rq; nic_cap->vf_max_sqs = 0; nic_cap->vf_max_rqs = 0; nic_cap->max_queue_allowed = dev_cap->max_queue_allowed; nic_cap->dynamic_qp = dev_cap->ovs_dq_en; } if (dev_cap->nic_lro_en) nic_cap->lro_en = true; else nic_cap->lro_en = false; nic_cap->lro_sz = dev_cap->nic_lro_sz; nic_cap->tso_sz = dev_cap->nic_tso_sz; sdk_info(hwdev->dev_hdl, "L2nic resource capbility, max_sqs=0x%x, max_rqs=0x%x, vf_max_sqs=0x%x, vf_max_rqs=0x%x, max_queue_allowed=0x%x\n", nic_cap->max_sqs, nic_cap->max_rqs, nic_cap->vf_max_sqs, nic_cap->vf_max_rqs, nic_cap->max_queue_allowed); /* Check parameters from firmware */ if (nic_cap->max_sqs > HINIC_CFG_MAX_QP || nic_cap->max_rqs > HINIC_CFG_MAX_QP) { sdk_info(hwdev->dev_hdl, "Number of qp exceed limit[1-%d]: sq: %d, rq: %d\n", HINIC_CFG_MAX_QP, nic_cap->max_sqs, nic_cap->max_rqs); nic_cap->max_sqs = HINIC_CFG_MAX_QP; nic_cap->max_rqs = HINIC_CFG_MAX_QP; } } static void parse_roce_res_cap(struct hinic_hwdev *hwdev, struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct dev_roce_svc_own_cap *roce_cap = &cap->rdma_cap.dev_rdma_cap.roce_own_cap; roce_cap->max_qps = dev_cap->roce_max_qp; roce_cap->max_cqs = dev_cap->roce_max_cq; roce_cap->max_srqs = dev_cap->roce_max_srq; roce_cap->max_mpts = dev_cap->roce_max_mpt; roce_cap->num_cos = dev_cap->max_cos_id + 1; /* PF/PPF */ if (type == TYPE_PF || type == TYPE_PPF) { roce_cap->vf_max_qps = dev_cap->roce_vf_max_qp; roce_cap->vf_max_cqs = dev_cap->roce_vf_max_cq; roce_cap->vf_max_srqs = dev_cap->roce_vf_max_srq; roce_cap->vf_max_mpts = dev_cap->roce_vf_max_mpt; } else { roce_cap->vf_max_qps = 0; roce_cap->vf_max_cqs = 0; roce_cap->vf_max_srqs = 0; roce_cap->vf_max_mpts = 0; } roce_cap->cmtt_cl_start = dev_cap->roce_cmtt_cl_start; roce_cap->cmtt_cl_end = dev_cap->roce_cmtt_cl_end; roce_cap->cmtt_cl_sz = dev_cap->roce_cmtt_cl_size; roce_cap->dmtt_cl_start = dev_cap->roce_dmtt_cl_start; roce_cap->dmtt_cl_end = dev_cap->roce_dmtt_cl_end; roce_cap->dmtt_cl_sz = dev_cap->roce_dmtt_cl_size; roce_cap->wqe_cl_start = dev_cap->roce_wqe_cl_start; roce_cap->wqe_cl_end = dev_cap->roce_wqe_cl_end; roce_cap->wqe_cl_sz = dev_cap->roce_wqe_cl_size; sdk_info(hwdev->dev_hdl, "Get roce resource capbility\n"); sdk_info(hwdev->dev_hdl, "Max_qps=0x%x, max_cqs=0x%x, max_srqs=0x%x, max_mpts=0x%x\n", roce_cap->max_qps, roce_cap->max_cqs, roce_cap->max_srqs, roce_cap->max_mpts); sdk_info(hwdev->dev_hdl, "Vf_max_qps=0x%x, vf_max_cqs=0x%x, vf_max_srqs= 0x%x, vf_max_mpts= 0x%x\n", roce_cap->vf_max_qps, roce_cap->vf_max_cqs, roce_cap->vf_max_srqs, roce_cap->vf_max_mpts); sdk_info(hwdev->dev_hdl, "Cmtt_start=0x%x, cmtt_end=0x%x, cmtt_sz=0x%x\n", roce_cap->cmtt_cl_start, roce_cap->cmtt_cl_end, roce_cap->cmtt_cl_sz); sdk_info(hwdev->dev_hdl, "Dmtt_start=0x%x, dmtt_end=0x%x, dmtt_sz=0x%x\n", roce_cap->dmtt_cl_start, roce_cap->dmtt_cl_end, roce_cap->dmtt_cl_sz); sdk_info(hwdev->dev_hdl, "Wqe_start=0x%x, wqe_end=0x%x, wqe_sz=0x%x\n", roce_cap->wqe_cl_start, roce_cap->wqe_cl_end, roce_cap->wqe_cl_sz); if (roce_cap->max_qps == 0) { roce_cap->max_qps = 1024; roce_cap->max_cqs = 2048; roce_cap->max_srqs = 1024; roce_cap->max_mpts = 1024; if (type == TYPE_PF || type == TYPE_PPF) { roce_cap->vf_max_qps = 512; roce_cap->vf_max_cqs = 1024; roce_cap->vf_max_srqs = 512; roce_cap->vf_max_mpts = 512; } } } static void parse_iwarp_res_cap(struct hinic_hwdev *hwdev, struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct dev_iwarp_svc_own_cap *iwarp_cap = &cap->rdma_cap.dev_rdma_cap.iwarp_own_cap; iwarp_cap->max_qps = dev_cap->iwarp_max_qp; iwarp_cap->max_cqs = dev_cap->iwarp_max_cq; iwarp_cap->max_mpts = dev_cap->iwarp_max_mpt; iwarp_cap->num_cos = dev_cap->max_cos_id + 1; /* PF/PPF */ if (type == TYPE_PF || type == TYPE_PPF) { iwarp_cap->vf_max_qps = dev_cap->iwarp_vf_max_qp; iwarp_cap->vf_max_cqs = dev_cap->iwarp_vf_max_cq; iwarp_cap->vf_max_mpts = dev_cap->iwarp_vf_max_mpt; } else { iwarp_cap->vf_max_qps = 0; iwarp_cap->vf_max_cqs = 0; iwarp_cap->vf_max_mpts = 0; } iwarp_cap->cmtt_cl_start = dev_cap->iwarp_cmtt_cl_start; iwarp_cap->cmtt_cl_end = dev_cap->iwarp_cmtt_cl_end; iwarp_cap->cmtt_cl_sz = dev_cap->iwarp_cmtt_cl_size; iwarp_cap->dmtt_cl_start = dev_cap->iwarp_dmtt_cl_start; iwarp_cap->dmtt_cl_end = dev_cap->iwarp_dmtt_cl_end; iwarp_cap->dmtt_cl_sz = dev_cap->iwarp_dmtt_cl_size; iwarp_cap->wqe_cl_start = dev_cap->iwarp_wqe_cl_start; iwarp_cap->wqe_cl_end = dev_cap->iwarp_wqe_cl_end; iwarp_cap->wqe_cl_sz = dev_cap->iwarp_wqe_cl_size; sdk_info(hwdev->dev_hdl, "Get iwrap resource capbility\n"); sdk_info(hwdev->dev_hdl, "Max_qps=0x%x, max_cqs=0x%x, max_mpts=0x%x\n", iwarp_cap->max_qps, iwarp_cap->max_cqs, iwarp_cap->max_mpts); sdk_info(hwdev->dev_hdl, "Vf_max_qps=0x%x, vf_max_cqs=0x%x, vf_max_mpts=0x%x\n", iwarp_cap->vf_max_qps, iwarp_cap->vf_max_cqs, iwarp_cap->vf_max_mpts); sdk_info(hwdev->dev_hdl, "Cmtt_start=0x%x, cmtt_end=0x%x, cmtt_sz=0x%x\n", iwarp_cap->cmtt_cl_start, iwarp_cap->cmtt_cl_end, iwarp_cap->cmtt_cl_sz); sdk_info(hwdev->dev_hdl, "Dmtt_start=0x%x, dmtt_end=0x%x, dmtt_sz=0x%x\n", iwarp_cap->dmtt_cl_start, iwarp_cap->dmtt_cl_end, iwarp_cap->dmtt_cl_sz); sdk_info(hwdev->dev_hdl, "Wqe_start=0x%x, wqe_end=0x%x, wqe_sz=0x%x\n", iwarp_cap->wqe_cl_start, iwarp_cap->wqe_cl_end, iwarp_cap->wqe_cl_sz); if (iwarp_cap->max_qps == 0) { iwarp_cap->max_qps = 8; iwarp_cap->max_cqs = 16; iwarp_cap->max_mpts = 8; if (type == TYPE_PF || type == TYPE_PPF) { iwarp_cap->vf_max_qps = 8; iwarp_cap->vf_max_cqs = 16; iwarp_cap->vf_max_mpts = 8; } } } static void parse_fcoe_res_cap(struct hinic_hwdev *hwdev, struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct dev_fcoe_svc_cap *fcoe_cap = &cap->fcoe_cap.dev_fcoe_cap; fcoe_cap->max_qps = dev_cap->fcoe_max_qp; fcoe_cap->max_cqs = dev_cap->fcoe_max_cq; fcoe_cap->max_srqs = dev_cap->fcoe_max_srq; fcoe_cap->max_cctxs = dev_cap->fcoe_max_cctx; fcoe_cap->cctxs_id_start = dev_cap->fcoe_cctx_id_start; fcoe_cap->vp_id_start = dev_cap->fcoe_vp_id_start; fcoe_cap->vp_id_end = dev_cap->fcoe_vp_id_end; sdk_info(hwdev->dev_hdl, "Get fcoe resource capbility\n"); sdk_info(hwdev->dev_hdl, "Max_qps=0x%x, max_cqs=0x%x, max_srqs=0x%x, max_cctxs=0x%x, cctxs_id_start=0x%x\n", fcoe_cap->max_qps, fcoe_cap->max_cqs, fcoe_cap->max_srqs, fcoe_cap->max_cctxs, fcoe_cap->cctxs_id_start); sdk_info(hwdev->dev_hdl, "Vp_id_start=0x%x, vp_id_end=0x%x\n", fcoe_cap->vp_id_start, fcoe_cap->vp_id_end); } static void parse_toe_res_cap(struct hinic_hwdev *hwdev, struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct dev_toe_svc_cap *toe_cap = &cap->toe_cap.dev_toe_cap; toe_cap->max_pctxs = dev_cap->toe_max_pctx; toe_cap->max_cqs = dev_cap->toe_max_cq; toe_cap->max_srqs = dev_cap->toe_max_srq; toe_cap->srq_id_start = dev_cap->toe_srq_id_start; toe_cap->num_cos = dev_cap->max_cos_id + 1; sdk_info(hwdev->dev_hdl, "Get toe resource capbility, max_pctxs=0x%x, max_cqs=0x%x, max_srqs=0x%x, srq_id_start=0x%x\n", toe_cap->max_pctxs, toe_cap->max_cqs, toe_cap->max_srqs, toe_cap->srq_id_start); } static void parse_fc_res_cap(struct hinic_hwdev *hwdev, struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct dev_fc_svc_cap *fc_cap = &cap->fc_cap.dev_fc_cap; fc_cap->max_parent_qpc_num = dev_cap->fc_max_pctx; fc_cap->scq_num = dev_cap->fc_max_scq; fc_cap->srq_num = dev_cap->fc_max_srq; fc_cap->max_child_qpc_num = dev_cap->fc_max_cctx; fc_cap->child_qpc_id_start = dev_cap->fc_cctx_id_start; fc_cap->vp_id_start = dev_cap->fc_vp_id_start; fc_cap->vp_id_end = dev_cap->fc_vp_id_end; sdk_info(hwdev->dev_hdl, "Get fc resource capbility\n"); sdk_info(hwdev->dev_hdl, "Max_parent_qpc_num=0x%x, scq_num=0x%x, srq_num=0x%x, max_child_qpc_num=0x%x, child_qpc_id_start=0x%x\n", fc_cap->max_parent_qpc_num, fc_cap->scq_num, fc_cap->srq_num, fc_cap->max_child_qpc_num, fc_cap->child_qpc_id_start); sdk_info(hwdev->dev_hdl, "Vp_id_start=0x%x, vp_id_end=0x%x\n", fc_cap->vp_id_start, fc_cap->vp_id_end); } static void parse_ovs_res_cap(struct hinic_hwdev *hwdev, struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct ovs_service_cap *ovs_cap = &cap->ovs_cap; ovs_cap->dev_ovs_cap.max_pctxs = dev_cap->ovs_max_qpc; ovs_cap->dev_ovs_cap.max_cqs = 0; if (type == TYPE_PF || type == TYPE_PPF) ovs_cap->dev_ovs_cap.dynamic_qp_en = dev_cap->ovs_dq_en; sdk_info(hwdev->dev_hdl, "Get ovs resource capbility, max_qpc: 0x%x\n", ovs_cap->dev_ovs_cap.max_pctxs); } static void parse_acl_res_cap(struct service_cap *cap, struct hinic_dev_cap *dev_cap, enum func_type type) { struct acl_service_cap *acl_cap = &cap->acl_cap; acl_cap->dev_acl_cap.max_pctxs = 1024 * 1024; acl_cap->dev_acl_cap.max_cqs = 8; } static void parse_dev_cap(struct hinic_hwdev *dev, struct hinic_dev_cap *dev_cap, enum func_type type) { struct service_cap *cap = &dev->cfg_mgmt->svc_cap; /* Public resource */ parse_pub_res_cap(dev, cap, dev_cap, type); /* PPF managed dynamic resource */ if (type == TYPE_PPF) parse_dynamic_share_res_cap(dev, cap, dev_cap, type); /* L2 NIC resource */ if (IS_NIC_TYPE(dev)) parse_l2nic_res_cap(dev, cap, dev_cap, type); /* FCoE/IOE/TOE/FC without virtulization */ if (type == TYPE_PF || type == TYPE_PPF) { if (IS_FC_TYPE(dev)) parse_fc_res_cap(dev, cap, dev_cap, type); if (IS_FCOE_TYPE(dev)) parse_fcoe_res_cap(dev, cap, dev_cap, type); if (IS_TOE_TYPE(dev)) parse_toe_res_cap(dev, cap, dev_cap, type); } /* RoCE resource */ if (IS_ROCE_TYPE(dev)) parse_roce_res_cap(dev, cap, dev_cap, type); /* iWARP resource */ if (IS_IWARP_TYPE(dev)) parse_iwarp_res_cap(dev, cap, dev_cap, type); if (IS_OVS_TYPE(dev)) parse_ovs_res_cap(dev, cap, dev_cap, type); if (IS_ACL_TYPE(dev)) parse_acl_res_cap(cap, dev_cap, type); } static int get_cap_from_fw(struct hinic_hwdev *dev, enum func_type type) { struct hinic_dev_cap dev_cap = {0}; u16 out_len = sizeof(dev_cap); int err; dev_cap.version = HINIC_CMD_VER_FUNC_ID; err = hinic_global_func_id_get(dev, &dev_cap.func_id); if (err) return err; sdk_info(dev->dev_hdl, "Get cap from fw, func_idx: %d\n", dev_cap.func_id); err = hinic_msg_to_mgmt_sync(dev, HINIC_MOD_CFGM, HINIC_CFG_NIC_CAP, &dev_cap, sizeof(dev_cap), &dev_cap, &out_len, 0); if (err || dev_cap.status || !out_len) { sdk_err(dev->dev_hdl, "Failed to get capability from FW, err: %d, status: 0x%x, out size: 0x%x\n", err, dev_cap.status, out_len); return -EFAULT; } parse_dev_cap(dev, &dev_cap, type); return 0; } static int get_cap_from_pf(struct hinic_hwdev *dev, enum func_type type) { struct hinic_dev_cap dev_cap = {0}; u16 in_len, out_len; int err; in_len = sizeof(dev_cap); out_len = in_len; err = hinic_msg_to_mgmt_sync(dev, HINIC_MOD_CFGM, HINIC_CFG_MBOX_CAP, &dev_cap, in_len, &dev_cap, &out_len, 0); if (err || dev_cap.status || !out_len) { sdk_err(dev->dev_hdl, "Failed to get capability from PF, err: %d, status: 0x%x, out size: 0x%x\n", err, dev_cap.status, out_len); return -EFAULT; } parse_dev_cap(dev, &dev_cap, type); return 0; } static int get_dev_cap(struct hinic_hwdev *dev) { int err; enum func_type type = HINIC_FUNC_TYPE(dev); switch (type) { case TYPE_PF: case TYPE_PPF: err = get_cap_from_fw(dev, type); if (err) { sdk_err(dev->dev_hdl, "Failed to get PF/PPF capability\n"); return err; } break; case TYPE_VF: err = get_cap_from_pf(dev, type); if (err) { sdk_err(dev->dev_hdl, "Failed to get VF capability\n"); return err; } break; default: sdk_err(dev->dev_hdl, "Unsupported PCI Function type: %d\n", type); return -EINVAL; } return 0; } static void nic_param_fix(struct hinic_hwdev *dev) { struct nic_service_cap *nic_cap = &dev->cfg_mgmt->svc_cap.nic_cap; if ((hinic_func_type(dev) == TYPE_VF) && nic_cap->max_queue_allowed != 0) { nic_cap->max_rqs = nic_cap->max_queue_allowed; nic_cap->max_sqs = nic_cap->max_queue_allowed; } } static void rdma_param_fix(struct hinic_hwdev *dev) { struct service_cap *cap = &dev->cfg_mgmt->svc_cap; struct rdma_service_cap *rdma_cap = &cap->rdma_cap; struct dev_roce_svc_own_cap *roce_cap = &rdma_cap->dev_rdma_cap.roce_own_cap; struct dev_iwarp_svc_own_cap *iwarp_cap = &rdma_cap->dev_rdma_cap.iwarp_own_cap; rdma_cap->log_mtt = LOG_MTT_SEG; rdma_cap->log_rdmarc = LOG_RDMARC_SEG; rdma_cap->reserved_qps = RDMA_RSVD_QPS; rdma_cap->max_sq_sg = RDMA_MAX_SQ_SGE; /* RoCE */ if (IS_ROCE_TYPE(dev)) { roce_cap->qpc_entry_sz = ROCE_QPC_ENTRY_SZ; roce_cap->max_wqes = ROCE_MAX_WQES; roce_cap->max_rq_sg = ROCE_MAX_RQ_SGE; roce_cap->max_sq_inline_data_sz = ROCE_MAX_SQ_INLINE_DATA_SZ; roce_cap->max_rq_desc_sz = ROCE_MAX_RQ_DESC_SZ; roce_cap->rdmarc_entry_sz = ROCE_RDMARC_ENTRY_SZ; roce_cap->max_qp_init_rdma = ROCE_MAX_QP_INIT_RDMA; roce_cap->max_qp_dest_rdma = ROCE_MAX_QP_DEST_RDMA; roce_cap->max_srq_wqes = ROCE_MAX_SRQ_WQES; roce_cap->reserved_srqs = ROCE_RSVD_SRQS; roce_cap->max_srq_sge = ROCE_MAX_SRQ_SGE; roce_cap->srqc_entry_sz = ROCE_SRQC_ENTERY_SZ; roce_cap->max_msg_sz = ROCE_MAX_MSG_SZ; } else { iwarp_cap->qpc_entry_sz = IWARP_QPC_ENTRY_SZ; iwarp_cap->max_wqes = IWARP_MAX_WQES; iwarp_cap->max_rq_sg = IWARP_MAX_RQ_SGE; iwarp_cap->max_sq_inline_data_sz = IWARP_MAX_SQ_INLINE_DATA_SZ; iwarp_cap->max_rq_desc_sz = IWARP_MAX_RQ_DESC_SZ; iwarp_cap->max_irq_depth = IWARP_MAX_IRQ_DEPTH; iwarp_cap->irq_entry_size = IWARP_IRQ_ENTRY_SZ; iwarp_cap->max_orq_depth = IWARP_MAX_ORQ_DEPTH; iwarp_cap->orq_entry_size = IWARP_ORQ_ENTRY_SZ; iwarp_cap->max_rtoq_depth = IWARP_MAX_RTOQ_DEPTH; iwarp_cap->rtoq_entry_size = IWARP_RTOQ_ENTRY_SZ; iwarp_cap->max_ackq_depth = IWARP_MAX_ACKQ_DEPTH; iwarp_cap->ackq_entry_size = IWARP_ACKQ_ENTRY_SZ; iwarp_cap->max_msg_sz = IWARP_MAX_MSG_SZ; } rdma_cap->max_sq_desc_sz = RDMA_MAX_SQ_DESC_SZ; rdma_cap->wqebb_size = WQEBB_SZ; rdma_cap->max_cqes = RDMA_MAX_CQES; rdma_cap->reserved_cqs = RDMA_RSVD_CQS; rdma_cap->cqc_entry_sz = RDMA_CQC_ENTRY_SZ; rdma_cap->cqe_size = RDMA_CQE_SZ; rdma_cap->reserved_mrws = RDMA_RSVD_MRWS; rdma_cap->mpt_entry_sz = RDMA_MPT_ENTRY_SZ; /* 2^8 - 1 * +------------------------+-----------+ * | 4B | 1M(20b) | Key(8b) | * +------------------------+-----------+ * key = 8bit key + 24bit index, * now Lkey of SGE uses 2bit(bit31 and bit30), so key only have 10bit, * we use original 8bits directly for simpilification */ rdma_cap->max_fmr_maps = 255; rdma_cap->num_mtts = (g_rdma_mtts_num > 0 ? g_rdma_mtts_num : RDMA_NUM_MTTS); rdma_cap->log_mtt_seg = LOG_MTT_SEG; rdma_cap->mtt_entry_sz = MTT_ENTRY_SZ; rdma_cap->log_rdmarc_seg = LOG_RDMARC_SEG; rdma_cap->local_ca_ack_delay = LOCAL_ACK_DELAY; rdma_cap->num_ports = RDMA_NUM_PORTS; rdma_cap->db_page_size = DB_PAGE_SZ; rdma_cap->direct_wqe_size = DWQE_SZ; rdma_cap->num_pds = NUM_PD; rdma_cap->reserved_pds = RSVD_PD; rdma_cap->max_xrcds = MAX_XRCDS; rdma_cap->reserved_xrcds = RSVD_XRCDS; rdma_cap->max_gid_per_port = MAX_GID_PER_PORT; rdma_cap->gid_entry_sz = GID_ENTRY_SZ; rdma_cap->reserved_lkey = RSVD_LKEY; rdma_cap->num_comp_vectors = (u32)dev->cfg_mgmt->eq_info.num_ceq; rdma_cap->page_size_cap = PAGE_SZ_CAP; rdma_cap->flags = (RDMA_BMME_FLAG_LOCAL_INV | RDMA_BMME_FLAG_REMOTE_INV | RDMA_BMME_FLAG_FAST_REG_WR | RDMA_DEV_CAP_FLAG_XRC | RDMA_DEV_CAP_FLAG_MEM_WINDOW | RDMA_BMME_FLAG_TYPE_2_WIN | RDMA_BMME_FLAG_WIN_TYPE_2B | RDMA_DEV_CAP_FLAG_ATOMIC); rdma_cap->max_frpl_len = MAX_FRPL_LEN; rdma_cap->max_pkeys = MAX_PKEYS; } static void fcoe_param_fix(struct hinic_hwdev *dev) { struct service_cap *cap = &dev->cfg_mgmt->svc_cap; struct fcoe_service_cap *fcoe_cap = &cap->fcoe_cap; fcoe_cap->qpc_basic_size = FCOE_PCTX_SZ; fcoe_cap->childc_basic_size = FCOE_CCTX_SZ; fcoe_cap->sqe_size = FCOE_SQE_SZ; fcoe_cap->scqc_basic_size = FCOE_SCQC_SZ; fcoe_cap->scqe_size = FCOE_SCQE_SZ; fcoe_cap->srqc_size = FCOE_SRQC_SZ; fcoe_cap->srqe_size = FCOE_SRQE_SZ; } static void toe_param_fix(struct hinic_hwdev *dev) { struct service_cap *cap = &dev->cfg_mgmt->svc_cap; struct toe_service_cap *toe_cap = &cap->toe_cap; toe_cap->pctx_sz = TOE_PCTX_SZ; toe_cap->scqc_sz = TOE_CQC_SZ; } static void fc_param_fix(struct hinic_hwdev *dev) { struct service_cap *cap = &dev->cfg_mgmt->svc_cap; struct fc_service_cap *fc_cap = &cap->fc_cap; fc_cap->parent_qpc_size = FC_PCTX_SZ; fc_cap->child_qpc_size = FC_CCTX_SZ; fc_cap->sqe_size = FC_SQE_SZ; fc_cap->scqc_size = FC_SCQC_SZ; fc_cap->scqe_size = FC_SCQE_SZ; fc_cap->srqc_size = FC_SRQC_SZ; fc_cap->srqe_size = FC_SRQE_SZ; } static void ovs_param_fix(struct hinic_hwdev *dev) { struct service_cap *cap = &dev->cfg_mgmt->svc_cap; struct ovs_service_cap *ovs_cap = &cap->ovs_cap; ovs_cap->pctx_sz = OVS_PCTX_SZ; ovs_cap->scqc_sz = OVS_SCQC_SZ; } static void acl_param_fix(struct hinic_hwdev *dev) { struct service_cap *cap = &dev->cfg_mgmt->svc_cap; struct acl_service_cap *acl_cap = &cap->acl_cap; acl_cap->pctx_sz = ACL_PCTX_SZ; acl_cap->scqc_sz = ACL_SCQC_SZ; } static void init_service_param(struct hinic_hwdev *dev) { if (IS_NIC_TYPE(dev)) nic_param_fix(dev); if (IS_RDMA_TYPE(dev)) rdma_param_fix(dev); if (IS_FCOE_TYPE(dev)) fcoe_param_fix(dev); if (IS_TOE_TYPE(dev)) toe_param_fix(dev); if (IS_FC_TYPE(dev)) fc_param_fix(dev); if (IS_OVS_TYPE(dev)) ovs_param_fix(dev); if (IS_ACL_TYPE(dev)) acl_param_fix(dev); } static void cfg_get_eq_num(struct hinic_hwdev *dev) { struct cfg_eq_info *eq_info = &dev->cfg_mgmt->eq_info; eq_info->num_ceq = dev->hwif->attr.num_ceqs; eq_info->num_ceq_remain = eq_info->num_ceq; } static int cfg_init_eq(struct hinic_hwdev *dev) { struct cfg_mgmt_info *cfg_mgmt = dev->cfg_mgmt; struct cfg_eq *eq; u8 num_ceq, i = 0; cfg_get_eq_num(dev); num_ceq = cfg_mgmt->eq_info.num_ceq; sdk_info(dev->dev_hdl, "Cfg mgmt: ceqs=0x%x, remain=0x%x\n", cfg_mgmt->eq_info.num_ceq, cfg_mgmt->eq_info.num_ceq_remain); if (!num_ceq) { sdk_err(dev->dev_hdl, "Ceq num cfg in fw is zero\n"); return -EFAULT; } eq = kcalloc(num_ceq, sizeof(*eq), GFP_KERNEL); if (!eq) return -ENOMEM; for (i = 0; i < num_ceq; ++i) { eq[i].eqn = i; eq[i].free = CFG_FREE; eq[i].type = SERVICE_T_MAX; } cfg_mgmt->eq_info.eq = eq; mutex_init(&cfg_mgmt->eq_info.eq_mutex); return 0; } int hinic_dev_ver_info(void *hwdev, struct dev_version_info *ver) { struct hinic_hwdev *dev = hwdev; struct cfg_mgmt_info *cfg_mgmt; if (!hwdev || !ver) return -EINVAL; cfg_mgmt = dev->cfg_mgmt; memcpy(ver, &cfg_mgmt->svc_cap.dev_ver_info, sizeof(*ver)); return 0; } EXPORT_SYMBOL(hinic_dev_ver_info); int hinic_vector_to_eqn(void *hwdev, enum hinic_service_type type, int vector) { struct hinic_hwdev *dev = hwdev; struct cfg_mgmt_info *cfg_mgmt; struct cfg_eq *eq; int eqn = -EINVAL; if (!hwdev || vector < 0) return -EINVAL; if (type != SERVICE_T_ROCE && type != SERVICE_T_IWARP) { sdk_err(dev->dev_hdl, "Service type: %d, only RDMA service could get eqn by vector\n", type); return -EINVAL; } cfg_mgmt = dev->cfg_mgmt; vector = (vector % cfg_mgmt->eq_info.num_ceq) + CFG_RDMA_CEQ_BASE; eq = cfg_mgmt->eq_info.eq; if ((eq[vector].type == SERVICE_T_ROCE || eq[vector].type == SERVICE_T_IWARP) && eq[vector].free == CFG_BUSY) eqn = eq[vector].eqn; return eqn; } EXPORT_SYMBOL(hinic_vector_to_eqn); static int cfg_init_interrupt(struct hinic_hwdev *dev) { struct cfg_mgmt_info *cfg_mgmt = dev->cfg_mgmt; struct cfg_irq_info *irq_info = &cfg_mgmt->irq_param_info; u16 intr_num = dev->hwif->attr.num_irqs; if (!intr_num) { sdk_err(dev->dev_hdl, "Irq num cfg in fw is zero\n"); return -EFAULT; } irq_info->alloc_info = kcalloc(intr_num, sizeof(*irq_info->alloc_info), GFP_KERNEL); if (!irq_info->alloc_info) return -ENOMEM; irq_info->num_irq_hw = intr_num; /* Production requires VF only surppots MSI-X */ if (HINIC_FUNC_TYPE(dev) == TYPE_VF) cfg_mgmt->svc_cap.interrupt_type = INTR_TYPE_MSIX; else cfg_mgmt->svc_cap.interrupt_type = intr_mode; mutex_init(&irq_info->irq_mutex); return 0; } static int cfg_enable_interrupt(struct hinic_hwdev *dev) { struct cfg_mgmt_info *cfg_mgmt = dev->cfg_mgmt; u16 nreq = cfg_mgmt->irq_param_info.num_irq_hw; void *pcidev = dev->pcidev_hdl; struct irq_alloc_info_st *irq_info; struct msix_entry *entry; u16 i = 0; int actual_irq; irq_info = cfg_mgmt->irq_param_info.alloc_info; sdk_info(dev->dev_hdl, "Interrupt type: %d, irq num: %d\n", cfg_mgmt->svc_cap.interrupt_type, nreq); switch (cfg_mgmt->svc_cap.interrupt_type) { case INTR_TYPE_MSIX: if (!nreq) { sdk_err(dev->dev_hdl, "Interrupt number cannot be zero\n"); return -EINVAL; } entry = kcalloc(nreq, sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; for (i = 0; i < nreq; i++) entry[i].entry = i; actual_irq = pci_enable_msix_range(pcidev, entry, VECTOR_THRESHOLD, nreq); if (actual_irq < 0) { sdk_err(dev->dev_hdl, "Alloc msix entries with threshold 2 failed\n"); kfree(entry); return -ENOMEM; } nreq = (u16)actual_irq; cfg_mgmt->irq_param_info.num_total = nreq; cfg_mgmt->irq_param_info.num_irq_remain = nreq; sdk_info(dev->dev_hdl, "Request %d msix vector success\n", nreq); for (i = 0; i < nreq; ++i) { /* u16 driver uses to specify entry, OS writes */ irq_info[i].info.msix_entry_idx = entry[i].entry; /* u32 kernel uses to write allocated vector */ irq_info[i].info.irq_id = entry[i].vector; irq_info[i].type = SERVICE_T_MAX; irq_info[i].free = CFG_FREE; } kfree(entry); break; default: sdk_err(dev->dev_hdl, "Unsupport interrupt type %d\n", cfg_mgmt->svc_cap.interrupt_type); break; } return 0; } int hinic_alloc_irqs(void *hwdev, enum hinic_service_type type, u16 num, struct irq_info *irq_info_array, u16 *act_num) { struct hinic_hwdev *dev = hwdev; struct cfg_mgmt_info *cfg_mgmt; struct cfg_irq_info *irq_info; struct irq_alloc_info_st *alloc_info; int max_num_irq; u16 free_num_irq; int i, j; if (!hwdev || !irq_info_array || !act_num) return -EINVAL; cfg_mgmt = dev->cfg_mgmt; irq_info = &cfg_mgmt->irq_param_info; alloc_info = irq_info->alloc_info; max_num_irq = irq_info->num_total; free_num_irq = irq_info->num_irq_remain; mutex_lock(&irq_info->irq_mutex); if (num > free_num_irq) { if (free_num_irq == 0) { sdk_err(dev->dev_hdl, "no free irq resource in cfg mgmt\n"); mutex_unlock(&irq_info->irq_mutex); return -ENOMEM; } sdk_warn(dev->dev_hdl, "only %d irq resource in cfg mgmt\n", free_num_irq); num = free_num_irq; } *act_num = 0; for (i = 0; i < num; i++) { for (j = 0; j < max_num_irq; j++) { if (alloc_info[j].free == CFG_FREE) { if (irq_info->num_irq_remain == 0) { sdk_err(dev->dev_hdl, "No free irq resource in cfg mgmt\n"); mutex_unlock(&irq_info->irq_mutex); return -EINVAL; } alloc_info[j].type = type; alloc_info[j].free = CFG_BUSY; irq_info_array[i].msix_entry_idx = alloc_info[j].info.msix_entry_idx; irq_info_array[i].irq_id = alloc_info[j].info.irq_id; (*act_num)++; irq_info->num_irq_remain--; break; } } } mutex_unlock(&irq_info->irq_mutex); return 0; } EXPORT_SYMBOL(hinic_alloc_irqs); void hinic_free_irq(void *hwdev, enum hinic_service_type type, u32 irq_id) { struct hinic_hwdev *dev = hwdev; struct cfg_mgmt_info *cfg_mgmt; struct cfg_irq_info *irq_info; struct irq_alloc_info_st *alloc_info; int max_num_irq; int i; if (!hwdev) return; cfg_mgmt = dev->cfg_mgmt; irq_info = &cfg_mgmt->irq_param_info; alloc_info = irq_info->alloc_info; max_num_irq = irq_info->num_total; mutex_lock(&irq_info->irq_mutex); for (i = 0; i < max_num_irq; i++) { if (irq_id == alloc_info[i].info.irq_id && type == alloc_info[i].type) { if (alloc_info[i].free == CFG_BUSY) { alloc_info[i].free = CFG_FREE; irq_info->num_irq_remain++; if (irq_info->num_irq_remain > max_num_irq) { sdk_err(dev->dev_hdl, "Find target, but over range\n"); mutex_unlock(&irq_info->irq_mutex); return; } break; } } } if (i >= max_num_irq) sdk_warn(dev->dev_hdl, "Irq %d don't need to free\n", irq_id); mutex_unlock(&irq_info->irq_mutex); } EXPORT_SYMBOL(hinic_free_irq); int hinic_vector_to_irq(void *hwdev, enum hinic_service_type type, int vector) { struct hinic_hwdev *dev = hwdev; struct cfg_mgmt_info *cfg_mgmt; struct irq_alloc_info_st *irq_info; int irq = -EINVAL; if (!hwdev) return -EINVAL; cfg_mgmt = dev->cfg_mgmt; if (type != SERVICE_T_ROCE && type != SERVICE_T_IWARP) { sdk_err(dev->dev_hdl, "Service type: %u, only RDMA service could get eqn by vector\n", type); return -EINVAL; } /* Current RDMA CEQ are 2 - 31, will change in the future */ vector = ((vector % cfg_mgmt->eq_info.num_ceq) + CFG_RDMA_CEQ_BASE); irq_info = cfg_mgmt->irq_param_info.alloc_info; if (irq_info[vector].type == SERVICE_T_ROCE || irq_info[vector].type == SERVICE_T_IWARP) if (irq_info[vector].free == CFG_BUSY) irq = (int)irq_info[vector].info.irq_id; return irq; } EXPORT_SYMBOL(hinic_vector_to_irq); int hinic_alloc_ceqs(void *hwdev, enum hinic_service_type type, int num, int *ceq_id_array, int *act_num) { struct hinic_hwdev *dev = hwdev; struct cfg_mgmt_info *cfg_mgmt; struct cfg_eq_info *eq; int free_ceq; int i, j; if (!hwdev || !ceq_id_array || !act_num) return -EINVAL; cfg_mgmt = dev->cfg_mgmt; eq = &cfg_mgmt->eq_info; free_ceq = eq->num_ceq_remain; mutex_lock(&eq->eq_mutex); if (num > free_ceq) { if (free_ceq <= 0) { sdk_err(dev->dev_hdl, "No free ceq resource in cfg mgmt\n"); mutex_unlock(&eq->eq_mutex); return -ENOMEM; } sdk_warn(dev->dev_hdl, "Only %d ceq resource in cfg mgmt\n", free_ceq); } *act_num = 0; num = min(num, eq->num_ceq - CFG_RDMA_CEQ_BASE); for (i = 0; i < num; i++) { if (eq->num_ceq_remain == 0) { sdk_warn(dev->dev_hdl, "Alloc %d ceqs, less than required %d ceqs\n", *act_num, num); mutex_unlock(&eq->eq_mutex); return 0; } for (j = CFG_RDMA_CEQ_BASE; j < eq->num_ceq; j++) { if (eq->eq[j].free == CFG_FREE) { eq->eq[j].type = type; eq->eq[j].free = CFG_BUSY; eq->num_ceq_remain--; ceq_id_array[i] = eq->eq[j].eqn; (*act_num)++; break; } } } mutex_unlock(&eq->eq_mutex); return 0; } EXPORT_SYMBOL(hinic_alloc_ceqs); void hinic_free_ceq(void *hwdev, enum hinic_service_type type, int ceq_id) { struct hinic_hwdev *dev = hwdev; struct cfg_mgmt_info *cfg_mgmt; struct cfg_eq_info *eq; u8 num_ceq; u8 i = 0; if (!hwdev) return; cfg_mgmt = dev->cfg_mgmt; eq = &cfg_mgmt->eq_info; num_ceq = eq->num_ceq; mutex_lock(&eq->eq_mutex); for (i = 0; i < num_ceq; i++) { if (ceq_id == eq->eq[i].eqn && type == cfg_mgmt->eq_info.eq[i].type) { if (eq->eq[i].free == CFG_BUSY) { eq->eq[i].free = CFG_FREE; eq->num_ceq_remain++; if (eq->num_ceq_remain > num_ceq) eq->num_ceq_remain %= num_ceq; mutex_unlock(&eq->eq_mutex); return; } } } if (i >= num_ceq) sdk_warn(dev->dev_hdl, "ceq %d don't need to free\n", ceq_id); mutex_unlock(&eq->eq_mutex); } EXPORT_SYMBOL(hinic_free_ceq); static int cfg_mbx_pf_proc_vf_msg(void *hwdev, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *dev = hwdev; struct hinic_dev_cap *dev_cap = buf_out; struct service_cap *cap = &dev->cfg_mgmt->svc_cap; struct nic_service_cap *nic_cap = &cap->nic_cap; struct dev_roce_svc_own_cap *roce_cap = &cap->rdma_cap.dev_rdma_cap.roce_own_cap; struct dev_iwarp_svc_own_cap *iwarp_cap = &cap->rdma_cap.dev_rdma_cap.iwarp_own_cap; struct dev_ovs_svc_cap *ovs_cap = &cap->ovs_cap.dev_ovs_cap; struct hinic_dev_cap dev_cap_tmp = {0}; u16 out_len = 0; u16 func_id; int err; memset(dev_cap, 0, sizeof(*dev_cap)); if (cap->sf_svc_attr.ft_en) dev_cap->sf_svc_attr |= SF_SVC_FT_BIT; else dev_cap->sf_svc_attr &= ~SF_SVC_FT_BIT; if (cap->sf_svc_attr.rdma_en) dev_cap->sf_svc_attr |= SF_SVC_RDMA_BIT; else dev_cap->sf_svc_attr &= ~SF_SVC_RDMA_BIT; dev_cap->sf_en_vf = cap->sf_svc_attr.sf_en_vf; dev_cap->host_id = cap->host_id; dev_cap->ep_id = cap->ep_id; dev_cap->intr_type = cap->interrupt_type; dev_cap->max_cos_id = cap->max_cos_id; dev_cap->er_id = cap->er_id; dev_cap->port_id = cap->port_id; dev_cap->max_vf = cap->max_vf; dev_cap->svc_cap_en = cap->chip_svc_type; dev_cap->host_total_func = cap->host_total_function; dev_cap->host_oq_id_mask_val = cap->host_oq_id_mask_val; dev_cap->net_port_mode = cap->net_port_mode; /* Parameters below is uninitialized because NIC and ROCE not use it * max_connect_num * max_stick2cache_num * bfilter_start_addr * bfilter_len * hash_bucket_num * cfg_file_ver */ /* NIC VF resources */ dev_cap->nic_max_sq = nic_cap->vf_max_sqs; dev_cap->nic_max_rq = nic_cap->vf_max_rqs; /* ROCE VF resources */ dev_cap->roce_max_qp = roce_cap->vf_max_qps; dev_cap->roce_max_cq = roce_cap->vf_max_cqs; dev_cap->roce_max_srq = roce_cap->vf_max_srqs; dev_cap->roce_max_mpt = roce_cap->vf_max_mpts; dev_cap->roce_cmtt_cl_start = roce_cap->cmtt_cl_start; dev_cap->roce_cmtt_cl_end = roce_cap->cmtt_cl_end; dev_cap->roce_cmtt_cl_size = roce_cap->cmtt_cl_sz; dev_cap->roce_dmtt_cl_start = roce_cap->dmtt_cl_start; dev_cap->roce_dmtt_cl_end = roce_cap->dmtt_cl_end; dev_cap->roce_dmtt_cl_size = roce_cap->dmtt_cl_sz; dev_cap->roce_wqe_cl_start = roce_cap->wqe_cl_start; dev_cap->roce_wqe_cl_end = roce_cap->wqe_cl_end; dev_cap->roce_wqe_cl_size = roce_cap->wqe_cl_sz; /* Iwarp VF resources */ dev_cap->iwarp_max_qp = iwarp_cap->vf_max_qps; dev_cap->iwarp_max_cq = iwarp_cap->vf_max_cqs; dev_cap->iwarp_max_mpt = iwarp_cap->vf_max_mpts; /* OVS VF resources */ dev_cap->ovs_max_qpc = ovs_cap->max_pctxs; dev_cap->ovs_dq_en = ovs_cap->dynamic_qp_en; *out_size = sizeof(*dev_cap); if (!IS_OVS_TYPE(dev)) return 0; out_len = sizeof(dev_cap_tmp); /* fixed qnum in ovs mode */ func_id = vf_id + hinic_glb_pf_vf_offset(hwdev); dev_cap_tmp.func_id = func_id; err = hinic_pf_msg_to_mgmt_sync(dev, HINIC_MOD_CFGM, HINIC_CFG_FUNC_CAP, &dev_cap_tmp, sizeof(dev_cap_tmp), &dev_cap_tmp, &out_len, 0); if (err && err != HINIC_DEV_BUSY_ACTIVE_FW && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) { sdk_err(dev->dev_hdl, "Get func_id: %u capability from FW failed, err: %d, status: 0x%x, out_size: 0x%x\n", func_id, err, dev_cap_tmp.status, out_len); return -EFAULT; } else if (err) { return err; } dev_cap->nic_max_sq = dev_cap_tmp.nic_max_sq + 1; dev_cap->nic_max_rq = dev_cap_tmp.nic_max_rq + 1; dev_cap->max_queue_allowed = dev_cap_tmp.max_queue_allowed; sdk_info(dev->dev_hdl, "func_id(%u) %s qnum %u max_queue_allowed %u\n", func_id, (ovs_cap->dynamic_qp_en ? "dynamic" : "fixed"), dev_cap->nic_max_sq, dev_cap->max_queue_allowed); return 0; } static int cfg_mbx_ppf_proc_msg(void *hwdev, u16 pf_id, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *dev = hwdev; sdk_info(dev->dev_hdl, "ppf receive other pf cfgmgmt cmd %d mbox msg\n", cmd); return hinic_ppf_process_mbox_msg(hwdev, pf_id, vf_id, HINIC_MOD_CFGM, cmd, buf_in, in_size, buf_out, out_size); } static int cfg_mbx_vf_proc_msg(void *hwdev, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *dev = hwdev; *out_size = 0; sdk_err(dev->dev_hdl, "VF msg callback not supported\n"); return -EOPNOTSUPP; } static int cfg_mbx_init(struct hinic_hwdev *dev, struct cfg_mgmt_info *cfg_mgmt) { int err; enum func_type type = dev->hwif->attr.func_type; if (type == TYPE_PF) { err = hinic_register_pf_mbox_cb(dev, HINIC_MOD_CFGM, cfg_mbx_pf_proc_vf_msg); if (err) { sdk_err(dev->dev_hdl, "PF: Register PF mailbox callback failed\n"); return err; } } else if (type == TYPE_PPF) { err = hinic_register_ppf_mbox_cb(dev, HINIC_MOD_CFGM, cfg_mbx_ppf_proc_msg); if (err) { sdk_err(dev->dev_hdl, "PPF: Register PPF mailbox callback failed\n"); return err; } err = hinic_register_pf_mbox_cb(dev, HINIC_MOD_CFGM, cfg_mbx_pf_proc_vf_msg); if (err) { sdk_err(dev->dev_hdl, "PPF: Register PF mailbox callback failed\n"); hinic_unregister_ppf_mbox_cb(dev, HINIC_MOD_CFGM); return err; } } else if (type == TYPE_VF) { err = hinic_register_vf_mbox_cb(dev, HINIC_MOD_CFGM, cfg_mbx_vf_proc_msg); if (err) { sdk_err(dev->dev_hdl, "VF: Register VF mailbox callback failed\n"); return err; } } else { sdk_err(dev->dev_hdl, "Invalid func_type: %d, not supported\n", type); return -EINVAL; } return 0; } static void cfg_mbx_cleanup(struct hinic_hwdev *dev) { hinic_unregister_ppf_mbox_cb(dev, HINIC_MOD_CFGM); hinic_unregister_pf_mbox_cb(dev, HINIC_MOD_CFGM); hinic_unregister_vf_mbox_cb(dev, HINIC_MOD_CFGM); } static int init_cfg_mgmt(struct hinic_hwdev *dev) { int err; struct cfg_mgmt_info *cfg_mgmt; cfg_mgmt = kzalloc(sizeof(*cfg_mgmt), GFP_KERNEL); if (!cfg_mgmt) return -ENOMEM; dev->cfg_mgmt = cfg_mgmt; cfg_mgmt->hwdev = dev; err = cfg_init_eq(dev); if (err) { sdk_err(dev->dev_hdl, "Failed to init cfg event queue, err: %d\n", err); goto free_mgmt_mem; } err = cfg_init_interrupt(dev); if (err) { sdk_err(dev->dev_hdl, "Failed to init cfg interrupt, err: %d\n", err); goto free_eq_mem; } err = cfg_enable_interrupt(dev); if (err) { sdk_err(dev->dev_hdl, "Failed to enable cfg interrupt, err: %d\n", err); goto free_interrupt_mem; } return 0; free_interrupt_mem: kfree(cfg_mgmt->irq_param_info.alloc_info); cfg_mgmt->irq_param_info.alloc_info = NULL; free_eq_mem: kfree(cfg_mgmt->eq_info.eq); cfg_mgmt->eq_info.eq = NULL; free_mgmt_mem: kfree(cfg_mgmt); return err; } static void free_cfg_mgmt(struct hinic_hwdev *dev) { struct cfg_mgmt_info *cfg_mgmt = dev->cfg_mgmt; /* if the allocated resource were recycled */ if (cfg_mgmt->irq_param_info.num_irq_remain != cfg_mgmt->irq_param_info.num_total || cfg_mgmt->eq_info.num_ceq_remain != cfg_mgmt->eq_info.num_ceq) sdk_err(dev->dev_hdl, "Can't reclaim all irq and event queue, please check\n"); switch (cfg_mgmt->svc_cap.interrupt_type) { case INTR_TYPE_MSIX: pci_disable_msix(dev->pcidev_hdl); break; case INTR_TYPE_MSI: pci_disable_msi(dev->pcidev_hdl); break; case INTR_TYPE_INT: default: break; } kfree(cfg_mgmt->irq_param_info.alloc_info); cfg_mgmt->irq_param_info.alloc_info = NULL; kfree(cfg_mgmt->eq_info.eq); cfg_mgmt->eq_info.eq = NULL; kfree(cfg_mgmt); } static int init_capability(struct hinic_hwdev *dev) { struct cfg_mgmt_info *cfg_mgmt = dev->cfg_mgmt; int err; set_cfg_test_param(cfg_mgmt); err = cfg_mbx_init(dev, cfg_mgmt); if (err) { sdk_err(dev->dev_hdl, "Configure mailbox init failed, err: %d\n", err); return err; } cfg_mgmt->svc_cap.sf_svc_attr.ft_pf_en = false; cfg_mgmt->svc_cap.sf_svc_attr.rdma_pf_en = false; err = get_dev_cap(dev); if (err) { cfg_mbx_cleanup(dev); return err; } init_service_param(dev); sdk_info(dev->dev_hdl, "Init capability success\n"); return 0; } static void free_capability(struct hinic_hwdev *dev) { cfg_mbx_cleanup(dev); sdk_info(dev->dev_hdl, "Free capability success"); } /* 0 - MSIx, 1 - MSI, 2 - INTx */ enum intr_type hinic_intr_type(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return INTR_TYPE_NONE; return dev->cfg_mgmt->svc_cap.interrupt_type; } EXPORT_SYMBOL(hinic_intr_type); bool hinic_support_nic(void *hwdev, struct nic_service_cap *cap) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_NIC_TYPE(dev)) return false; if (cap) memcpy(cap, &dev->cfg_mgmt->svc_cap.nic_cap, sizeof(*cap)); return true; } EXPORT_SYMBOL(hinic_support_nic); bool hinic_support_roce(void *hwdev, struct rdma_service_cap *cap) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_ROCE_TYPE(dev)) return false; if (cap) memcpy(cap, &dev->cfg_mgmt->svc_cap.rdma_cap, sizeof(*cap)); return true; } EXPORT_SYMBOL(hinic_support_roce); bool hinic_support_fcoe(void *hwdev, struct fcoe_service_cap *cap) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_FCOE_TYPE(dev)) return false; if (cap) memcpy(cap, &dev->cfg_mgmt->svc_cap.fcoe_cap, sizeof(*cap)); return true; } EXPORT_SYMBOL(hinic_support_fcoe); /* Only PPF support it, PF is not */ bool hinic_support_toe(void *hwdev, struct toe_service_cap *cap) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_TOE_TYPE(dev)) return false; if (cap) memcpy(cap, &dev->cfg_mgmt->svc_cap.toe_cap, sizeof(*cap)); return true; } EXPORT_SYMBOL(hinic_support_toe); bool hinic_support_iwarp(void *hwdev, struct rdma_service_cap *cap) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_IWARP_TYPE(dev)) return false; if (cap) memcpy(cap, &dev->cfg_mgmt->svc_cap.rdma_cap, sizeof(*cap)); return true; } EXPORT_SYMBOL(hinic_support_iwarp); bool hinic_support_fc(void *hwdev, struct fc_service_cap *cap) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_FC_TYPE(dev)) return false; if (cap) memcpy(cap, &dev->cfg_mgmt->svc_cap.fc_cap, sizeof(*cap)); return true; } EXPORT_SYMBOL(hinic_support_fc); bool hinic_support_fic(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_FIC_TYPE(dev)) return false; return true; } EXPORT_SYMBOL(hinic_support_fic); bool hinic_support_ovs(void *hwdev, struct ovs_service_cap *cap) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_OVS_TYPE(dev)) return false; if (cap) memcpy(cap, &dev->cfg_mgmt->svc_cap.ovs_cap, sizeof(*cap)); return true; } EXPORT_SYMBOL(hinic_support_ovs); bool hinic_support_acl(void *hwdev, struct acl_service_cap *cap) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_ACL_TYPE(dev)) return false; if (cap) memcpy(cap, &dev->cfg_mgmt->svc_cap.acl_cap, sizeof(*cap)); return true; } EXPORT_SYMBOL(hinic_support_acl); bool hinic_support_rdma(void *hwdev, struct rdma_service_cap *cap) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_RDMA_TYPE(dev)) return false; if (cap) memcpy(cap, &dev->cfg_mgmt->svc_cap.rdma_cap, sizeof(*cap)); return true; } EXPORT_SYMBOL(hinic_support_rdma); bool hinic_support_ft(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_FT_TYPE(dev)) return false; return true; } EXPORT_SYMBOL(hinic_support_ft); bool hinic_support_dynamic_q(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; return dev->cfg_mgmt->svc_cap.nic_cap.dynamic_qp ? true : false; } bool hinic_func_for_mgmt(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (dev->cfg_mgmt->svc_cap.chip_svc_type >= CFG_SVC_NIC_BIT0) return false; else return true; } bool hinic_func_for_hwpt(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (IS_HWPT_TYPE(dev)) return true; else return false; } bool hinic_func_for_pt(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (dev->cfg_mgmt->svc_cap.force_up) return true; else return false; } int cfg_set_func_sf_en(void *hwdev, u32 enbits, u32 enmask) { struct hinic_hwdev *dev = hwdev; struct nic_misc_func_sf_enbits *func_sf_enbits; u16 out_size = sizeof(*func_sf_enbits); u16 glb_func_idx; u16 api_info_len; int err; api_info_len = sizeof(struct nic_misc_func_sf_enbits); func_sf_enbits = kzalloc(api_info_len, GFP_KERNEL); if (!func_sf_enbits) { sdk_err(dev->dev_hdl, "Alloc cfg api info failed\n"); return -ENOMEM; } err = hinic_global_func_id_get(dev, &glb_func_idx); if (err) { kfree(func_sf_enbits); return err; } func_sf_enbits->stateful_enbits = enbits; func_sf_enbits->stateful_enmask = enmask; func_sf_enbits->function_id = glb_func_idx; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_MISC_SET_FUNC_SF_ENBITS, (void *)func_sf_enbits, api_info_len, (void *)func_sf_enbits, &out_size, VSW_UP_CFG_TIMEOUT); if (err || !out_size || func_sf_enbits->status) { sdk_err(dev->dev_hdl, "Failed to set stateful enable, err: %d, status: 0x%x, out_size: 0x%x\n", err, func_sf_enbits->status, out_size); kfree(func_sf_enbits); return -EFAULT; } kfree(func_sf_enbits); return 0; } int cfg_get_func_sf_en(void *hwdev, u32 *enbits) { struct nic_misc_func_sf_enbits *func_sf_enbits; struct hinic_hwdev *dev = hwdev; u16 out_size = sizeof(*func_sf_enbits); u16 glb_func_idx; u16 api_info_len; int err; api_info_len = sizeof(struct nic_misc_func_sf_enbits); func_sf_enbits = kzalloc(api_info_len, GFP_KERNEL); if (!func_sf_enbits) { sdk_err(dev->dev_hdl, "Alloc cfg api info failed\n"); return -ENOMEM; } err = hinic_global_func_id_get(dev, &glb_func_idx); if (err) { kfree(func_sf_enbits); return err; } func_sf_enbits->function_id = glb_func_idx; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_MISC_GET_FUNC_SF_ENBITS, (void *)func_sf_enbits, api_info_len, (void *)func_sf_enbits, &out_size, VSW_UP_CFG_TIMEOUT); if (err || !out_size || func_sf_enbits->status) { sdk_err(dev->dev_hdl, "Failed to get stateful enable, err: %d, status: 0x%x, out_size: 0x%x\n", err, func_sf_enbits->status, out_size); kfree(func_sf_enbits); return -EFAULT; } *enbits = func_sf_enbits->stateful_enbits; kfree(func_sf_enbits); return 0; } int hinic_set_toe_enable(void *hwdev, bool enable) { u32 enbits; u32 enmask; if (!hwdev) return -EINVAL; enbits = VSW_SET_STATEFUL_BITS_TOE((u16)enable); enmask = VSW_SET_STATEFUL_BITS_TOE(0x1U); return cfg_set_func_sf_en(hwdev, enbits, enmask); } EXPORT_SYMBOL(hinic_set_toe_enable); bool hinic_get_toe_enable(void *hwdev) { int err; u32 enbits; if (!hwdev) return false; err = cfg_get_func_sf_en(hwdev, &enbits); if (err) return false; return VSW_GET_STATEFUL_BITS_TOE(enbits); } EXPORT_SYMBOL(hinic_get_toe_enable); int hinic_set_fcoe_enable(void *hwdev, bool enable) { u32 enbits; u32 enmask; if (!hwdev) return -EINVAL; enbits = VSW_SET_STATEFUL_BITS_FCOE((u16)enable); enmask = VSW_SET_STATEFUL_BITS_FCOE(0x1U); return cfg_set_func_sf_en(hwdev, enbits, enmask); } EXPORT_SYMBOL(hinic_set_fcoe_enable); bool hinic_get_fcoe_enable(void *hwdev) { int err; u32 enbits; if (!hwdev) return false; err = cfg_get_func_sf_en(hwdev, &enbits); if (err) return false; return VSW_GET_STATEFUL_BITS_FCOE(enbits); } EXPORT_SYMBOL(hinic_get_fcoe_enable); bool hinic_get_stateful_enable(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; return dev->cfg_mgmt->svc_cap.sf_en; } EXPORT_SYMBOL(hinic_get_stateful_enable); u8 hinic_host_oq_id_mask(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting host oq id mask\n"); return 0; } return dev->cfg_mgmt->svc_cap.host_oq_id_mask_val; } EXPORT_SYMBOL(hinic_host_oq_id_mask); u8 hinic_host_id(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting host id\n"); return 0; } return dev->cfg_mgmt->svc_cap.host_id; } EXPORT_SYMBOL(hinic_host_id); u16 hinic_host_total_func(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting host total function number\n"); return 0; } return dev->cfg_mgmt->svc_cap.host_total_function; } EXPORT_SYMBOL(hinic_host_total_func); u16 hinic_func_max_qnum(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting function max queue number\n"); return 0; } return dev->cfg_mgmt->svc_cap.max_sqs; } EXPORT_SYMBOL(hinic_func_max_qnum); u16 hinic_func_max_nic_qnum(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting function max queue number\n"); return 0; } return dev->cfg_mgmt->svc_cap.nic_cap.max_sqs; } EXPORT_SYMBOL(hinic_func_max_nic_qnum); u8 hinic_ep_id(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting ep id\n"); return 0; } return dev->cfg_mgmt->svc_cap.ep_id; } EXPORT_SYMBOL(hinic_ep_id); u8 hinic_er_id(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting er id\n"); return 0; } return dev->cfg_mgmt->svc_cap.er_id; } EXPORT_SYMBOL(hinic_er_id); u8 hinic_physical_port_id(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting physical port id\n"); return 0; } return dev->cfg_mgmt->svc_cap.port_id; } EXPORT_SYMBOL(hinic_physical_port_id); u8 hinic_func_max_vf(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting max vf number\n"); return 0; } return dev->cfg_mgmt->svc_cap.max_vf; } EXPORT_SYMBOL(hinic_func_max_vf); u8 hinic_max_num_cos(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting max cos number\n"); return 0; } return (u8)(dev->cfg_mgmt->svc_cap.max_cos_id + 1); } EXPORT_SYMBOL(hinic_max_num_cos); u8 hinic_cos_valid_bitmap(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting cos valid bitmap\n"); return 0; } return (u8)(dev->cfg_mgmt->svc_cap.cos_valid_bitmap); } EXPORT_SYMBOL(hinic_cos_valid_bitmap); u8 hinic_net_port_mode(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting net port mode\n"); return 0; } return dev->cfg_mgmt->svc_cap.net_port_mode; } EXPORT_SYMBOL(hinic_net_port_mode); bool hinic_is_hwdev_mod_inited(void *hwdev, enum hinic_hwdev_init_state state) { struct hinic_hwdev *dev = hwdev; if (!hwdev || state >= HINIC_HWDEV_MAX_INVAL_INITED) return false; return !!test_bit(state, &dev->func_state); } static int hinic_os_dep_init(struct hinic_hwdev *hwdev) { hwdev->workq = create_singlethread_workqueue(HINIC_HW_WQ_NAME); if (!hwdev->workq) { sdk_err(hwdev->dev_hdl, "Failed to initialize hardware workqueue\n"); return -EFAULT; } return 0; } static void hinic_os_dep_deinit(struct hinic_hwdev *hwdev) { destroy_workqueue(hwdev->workq); } void hinic_ppf_hwdev_unreg(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return; down(&dev->ppf_sem); dev->ppf_hwdev = NULL; up(&dev->ppf_sem); sdk_info(dev->dev_hdl, "Unregister PPF hwdev\n"); } void hinic_ppf_hwdev_reg(void *hwdev, void *ppf_hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return; down(&dev->ppf_sem); dev->ppf_hwdev = ppf_hwdev; up(&dev->ppf_sem); sdk_info(dev->dev_hdl, "Register PPF hwdev\n"); } static int __vf_func_init(struct hinic_hwdev *hwdev) { int err; err = hinic_vf_mbox_random_id_init(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init vf mbox random id\n"); return err; } err = hinic_vf_func_init(hwdev); if (err) nic_err(hwdev->dev_hdl, "Failed to init nic mbox\n"); return err; } static int __hilink_phy_init(struct hinic_hwdev *hwdev) { int err; if (!HINIC_IS_VF(hwdev)) { err = hinic_phy_init_status_judge(hwdev); if (err) { sdk_info(hwdev->dev_hdl, "Phy init failed\n"); return err; } if (hinic_support_nic(hwdev, NULL)) hinic_hilink_info_show(hwdev); } return 0; } /* Return: * 0: all success * >0: partitial success * <0: all failed */ int hinic_init_hwdev(struct hinic_init_para *para) { struct hinic_hwdev *hwdev; int err; if (!(*para->hwdev)) { hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); if (!hwdev) return -ENOMEM; *para->hwdev = hwdev; hwdev->adapter_hdl = para->adapter_hdl; hwdev->pcidev_hdl = para->pcidev_hdl; hwdev->dev_hdl = para->dev_hdl; hwdev->chip_node = para->chip_node; hwdev->ppf_hwdev = para->ppf_hwdev; sema_init(&hwdev->ppf_sem, 1); sema_init(&hwdev->func_sem, 1); hwdev->func_ref = 0; hwdev->chip_fault_stats = vzalloc(HINIC_CHIP_FAULT_SIZE); if (!hwdev->chip_fault_stats) goto alloc_chip_fault_stats_err; err = hinic_init_hwif(hwdev, para->cfg_reg_base, para->intr_reg_base, para->db_base_phy, para->db_base, para->dwqe_mapping); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init hwif\n"); goto init_hwif_err; } } else { hwdev = *para->hwdev; } /* detect slave host according to BAR reg */ detect_host_mode_pre(hwdev); if (IS_BMGW_SLAVE_HOST(hwdev) && (!hinic_get_master_host_mbox_enable(hwdev))) { set_bit(HINIC_HWDEV_NONE_INITED, &hwdev->func_state); sdk_info(hwdev->dev_hdl, "Master host not ready, init hwdev later\n"); return (1 << HINIC_HWDEV_ALL_INITED); } err = hinic_os_dep_init(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init os dependent\n"); goto os_dep_init_err; } hinic_set_chip_present(hwdev); hinic_init_heartbeat(hwdev); err = init_cfg_mgmt(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init config mgmt\n"); goto init_cfg_mgmt_err; } err = hinic_init_comm_ch(hwdev); if (err) { if (!(hwdev->func_state & HINIC_HWDEV_INIT_MODES_MASK)) { sdk_err(hwdev->dev_hdl, "Failed to init communication channel\n"); goto init_comm_ch_err; } else { sdk_err(hwdev->dev_hdl, "Init communication channel partitail failed\n"); return hwdev->func_state & HINIC_HWDEV_INIT_MODES_MASK; } } err = init_capability(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init capability\n"); goto init_cap_err; } if (hwdev->cfg_mgmt->svc_cap.force_up) hwdev->feature_cap |= HINIC_FUNC_FORCE_LINK_UP; err = __vf_func_init(hwdev); if (err) goto vf_func_init_err; err = __hilink_phy_init(hwdev); if (err) goto hilink_phy_init_err; set_bit(HINIC_HWDEV_ALL_INITED, &hwdev->func_state); sdk_info(hwdev->dev_hdl, "Init hwdev success\n"); return 0; hilink_phy_init_err: hinic_vf_func_free(hwdev); vf_func_init_err: free_capability(hwdev); init_cap_err: return (hwdev->func_state & HINIC_HWDEV_INIT_MODES_MASK); init_comm_ch_err: free_cfg_mgmt(hwdev); init_cfg_mgmt_err: hinic_destroy_heartbeat(hwdev); hinic_os_dep_deinit(hwdev); os_dep_init_err: hinic_free_hwif(hwdev); init_hwif_err: vfree(hwdev->chip_fault_stats); alloc_chip_fault_stats_err: kfree(hwdev); *para->hwdev = NULL; return -EFAULT; } /** * hinic_set_vf_dev_cap - Set max queue num for VF * @hwdev: the HW device for VF */ int hinic_set_vf_dev_cap(void *hwdev) { int err; struct hinic_hwdev *dev; enum func_type type; if (!hwdev) return -EFAULT; dev = (struct hinic_hwdev *)hwdev; type = HINIC_FUNC_TYPE(dev); if (type != TYPE_VF) return -EPERM; err = get_dev_cap(dev); if (err) return err; nic_param_fix(dev); return 0; } void hinic_free_hwdev(void *hwdev) { struct hinic_hwdev *dev = hwdev; enum hinic_hwdev_init_state state = HINIC_HWDEV_ALL_INITED; int flag = 0; if (!hwdev) return; if (test_bit(HINIC_HWDEV_ALL_INITED, &dev->func_state)) { clear_bit(HINIC_HWDEV_ALL_INITED, &dev->func_state); /* BM slave function not need to exec rx_tx_flush */ if (dev->func_mode != FUNC_MOD_MULTI_BM_SLAVE) hinic_func_rx_tx_flush(hwdev); hinic_vf_func_free(hwdev); free_capability(dev); } while (state > HINIC_HWDEV_NONE_INITED) { if (test_bit(state, &dev->func_state)) { flag = 1; break; } state--; } if (flag) { hinic_uninit_comm_ch(dev); free_cfg_mgmt(dev); hinic_destroy_heartbeat(dev); hinic_os_dep_deinit(dev); } clear_bit(HINIC_HWDEV_NONE_INITED, &dev->func_state); hinic_free_hwif(dev); vfree(dev->chip_fault_stats); kfree(dev); } void hinic_set_api_stop(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return; dev->chip_present_flag = HINIC_CHIP_ABSENT; sdk_info(dev->dev_hdl, "Set card absent\n"); hinic_force_complete_all(dev); sdk_info(dev->dev_hdl, "All messages interacting with the chip will stop\n"); } void hinic_shutdown_hwdev(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return; if (IS_SLAVE_HOST(dev)) set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false); } u32 hinic_func_pf_num(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting pf number capability\n"); return 0; } return dev->cfg_mgmt->svc_cap.pf_num; } u64 hinic_get_func_feature_cap(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting function feature capability\n"); return 0; } return dev->feature_cap; } enum hinic_func_mode hinic_get_func_mode(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting function mode\n"); return 0; } return dev->func_mode; } EXPORT_SYMBOL(hinic_get_func_mode); enum hinic_service_mode hinic_get_service_mode(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!dev) { pr_err("Hwdev pointer is NULL for getting service mode\n"); return HINIC_WORK_MODE_INVALID; } return dev->board_info.service_mode; } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_cfg.h000066400000000000000000000305261502027332500212170ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef __CFG_MGT_H__ #define __CFG_MGT_H__ #include "hinic_ctx_def.h" enum { CFG_FREE = 0, CFG_BUSY = 1 }; /* start position for CEQs allocation, Max number of CEQs is 32 */ /*lint -save -e849*/ enum { CFG_RDMA_CEQ_BASE = 0 }; /*lint -restore*/ enum { CFG_NET_MODE_ETH = 0, /* Eth */ CFG_NET_MODE_FIC = 1, /* FIC */ CFG_NET_MODE_FC = 2 /* FC */ }; enum { SF_SVC_FT_BIT = (1 << 0), SF_SVC_RDMA_BIT = (1 << 1), }; /* RDMA resource */ #define K_UNIT BIT(10) #define M_UNIT BIT(20) #define G_UNIT BIT(30) /* number of PFs and VFs */ #define HOST_PF_NUM 4 #define HOST_VF_NUM 0 #define HOST_OQID_MASK_VAL 2 /* L2NIC */ #define L2NIC_SQ_DEPTH (4 * K_UNIT) #define L2NIC_RQ_DEPTH (4 * K_UNIT) #define HINIC_CFG_MAX_QP 128 /* RDMA */ #define RDMA_RSVD_QPS 2 #define ROCE_MAX_WQES (16 * K_UNIT - 1) #define IWARP_MAX_WQES (8 * K_UNIT) #define RDMA_MAX_SQ_SGE 8 #define ROCE_MAX_RQ_SGE 8 #define IWARP_MAX_RQ_SGE 2 #define RDMA_MAX_SQ_DESC_SZ (1 * K_UNIT) /* (256B(cache_line_len) - 16B(ctrl_seg_len) - 64B(max_task_seg_len)) */ #define ROCE_MAX_SQ_INLINE_DATA_SZ 192 #define IWARP_MAX_SQ_INLINE_DATA_SZ 108 #define ROCE_MAX_RQ_DESC_SZ 128 #define IWARP_MAX_RQ_DESC_SZ 64 #define IWARP_MAX_IRQ_DEPTH 1024 #define IWARP_IRQ_ENTRY_SZ 64 #define IWARP_MAX_ORQ_DEPTH 1024 #define IWARP_ORQ_ENTRY_SZ 32 #define IWARP_MAX_RTOQ_DEPTH 1024 #define IWARP_RTOQ_ENTRY_SZ 32 #define IWARP_MAX_ACKQ_DEPTH 1024 #define IWARP_ACKQ_ENTRY_SZ 16 #define ROCE_QPC_ENTRY_SZ 512 #define IWARP_QPC_ENTRY_SZ 1024 #define WQEBB_SZ 64 #define ROCE_RDMARC_ENTRY_SZ 32 #define ROCE_MAX_QP_INIT_RDMA 128 #define ROCE_MAX_QP_DEST_RDMA 128 #define ROCE_MAX_SRQ_WQES (16 * K_UNIT - 1) #define ROCE_RSVD_SRQS 0 #define ROCE_MAX_SRQ_SGE 7 #define ROCE_SRQC_ENTERY_SZ 64 #define RDMA_MAX_CQES (64 * K_UNIT - 1) #define RDMA_RSVD_CQS 0 #define RDMA_CQC_ENTRY_SZ 128 #define RDMA_CQE_SZ 32 #define RDMA_RSVD_MRWS 128 #define RDMA_MPT_ENTRY_SZ 64 #define RDMA_NUM_MTTS (1 * G_UNIT) #define LOG_MTT_SEG 5 #define MTT_ENTRY_SZ 8 #define LOG_RDMARC_SEG 3 #define LOCAL_ACK_DELAY 15 #define RDMA_NUM_PORTS 1 #define ROCE_MAX_MSG_SZ (2 * G_UNIT) #define IWARP_MAX_MSG_SZ (1 * G_UNIT) #define DB_PAGE_SZ (4 * K_UNIT) #define DWQE_SZ 256 #define NUM_PD (128 * K_UNIT) #define RSVD_PD 0 #define MAX_XRCDS (64 * K_UNIT) #define RSVD_XRCDS 0 #define MAX_GID_PER_PORT 16 #define GID_ENTRY_SZ 32 #define RSVD_LKEY ((RDMA_RSVD_MRWS - 1) << 8) #define NUM_COMP_VECTORS 32 #define PAGE_SZ_CAP ((1UL << 12) | (1UL << 13) | (1UL << 14) | \ (1UL << 16) | (1UL << 18) | (1UL << 20) | \ (1UL << 22)) #define ROCE_MODE 1 #define MAX_FRPL_LEN 511 #define MAX_PKEYS 1 /* FCoE */ #define FCOE_PCTX_SZ 256 #define FCOE_CCTX_SZ 256 #define FCOE_SQE_SZ 128 #define FCOE_SCQC_SZ 64 #define FCOE_SCQE_SZ 64 #define FCOE_SRQC_SZ 64 #define FCOE_SRQE_SZ 32 /* ToE */ #define TOE_PCTX_SZ 1024 #define TOE_CQC_SZ 64 /* IoE */ #define IOE_PCTX_SZ 512 /* FC */ #define FC_PCTX_SZ 256 #define FC_CCTX_SZ 256 #define FC_SQE_SZ 128 #define FC_SCQC_SZ 64 #define FC_SCQE_SZ 64 #define FC_SRQC_SZ 64 #define FC_SRQE_SZ 32 /* OVS */ #define OVS_PCTX_SZ 256 #define OVS_SCQC_SZ 64 /* ACL */ #define ACL_PCTX_SZ 512 #define ACL_SCQC_SZ 64 struct dev_sf_svc_attr { bool ft_en; /* business enable flag (not include RDMA) */ bool ft_pf_en; /* In FPGA Test VF resource is in PF or not, * 0 - VF, 1 - PF, VF doesn't need this bit. */ bool rdma_en; bool rdma_pf_en;/* In FPGA Test VF RDMA resource is in PF or not, * 0 - VF, 1 - PF, VF doesn't need this bit. */ u8 sf_en_vf; /* SF_EN for PPF/PF's VF */ }; struct host_shared_resource_cap { u32 host_pctxs; /* Parent Context max 1M, IOE and FCoE max 8K flows */ u32 host_cctxs; /* Child Context: max 8K */ u32 host_scqs; /* shared CQ, chip interface module uses 1 SCQ * TOE/IOE/FCoE each uses 1 SCQ * RoCE/IWARP uses multiple SCQs * So 6 SCQ least */ u32 host_srqs; /* SRQ number: 256K */ u32 host_mpts; /* MR number:1M */ }; /* device capability */ struct service_cap { struct dev_sf_svc_attr sf_svc_attr; enum cfg_svc_type_en svc_type; /* user input service type */ enum cfg_svc_type_en chip_svc_type; /* HW supported service type */ /* Host global resources */ u16 host_total_function; u8 host_oq_id_mask_val; u8 host_id; u8 ep_id; /* DO NOT get interrupt_type from firmware */ enum intr_type interrupt_type; u8 intr_chip_en; u8 max_cos_id; /* PF/VF's max cos id */ u8 cos_valid_bitmap; u8 er_id; /* PF/VF's ER */ u8 port_id; /* PF/VF's physical port */ u8 max_vf; /* max VF number that PF supported */ u8 force_up; bool sf_en; /* stateful business status */ u8 timer_en; /* 0:disable, 1:enable */ u8 bloomfilter_en; /* 0:disable, 1:enable*/ u16 max_sqs; u16 max_rqs; /* For test */ u32 test_qpc_num; u32 test_qpc_resvd_num; u32 test_page_size_reorder; bool test_xid_alloc_mode; bool test_gpa_check_enable; u8 test_qpc_alloc_mode; u8 test_scqc_alloc_mode; u32 test_max_conn_num; u32 test_max_cache_conn_num; u32 test_scqc_num; u32 test_mpt_num; u32 test_scq_resvd_num; u32 test_mpt_recvd_num; u32 test_hash_num; u32 test_reorder_num; u32 max_connect_num; /* PF/VF maximum connection number(1M) */ /* The maximum connections which can be stick to cache memory, max 1K */ u16 max_stick2cache_num; /* Starting address in cache memory for bloom filter, 64Bytes aligned */ u16 bfilter_start_addr; /* Length for bloom filter, aligned on 64Bytes. The size is length*64B. * Bloom filter memory size + 1 must be power of 2. * The maximum memory size of bloom filter is 4M */ u16 bfilter_len; /* The size of hash bucket tables, align on 64 entries. * Be used to AND (&) the hash value. Bucket Size +1 must be power of 2. * The maximum number of hash bucket is 4M */ u16 hash_bucket_num; u8 net_port_mode; /* 0:ETH,1:FIC,2:4FC */ u32 pf_num; u32 pf_id_start; u32 vf_num; /* max numbers of vf in current host */ u32 vf_id_start; struct host_shared_resource_cap shared_res_cap; /* shared capability */ struct dev_version_info dev_ver_info; /* version */ struct nic_service_cap nic_cap; /* NIC capability */ struct rdma_service_cap rdma_cap; /* RDMA capability */ struct fcoe_service_cap fcoe_cap; /* FCoE capability */ struct toe_service_cap toe_cap; /* ToE capability */ struct fc_service_cap fc_cap; /* FC capability */ struct ovs_service_cap ovs_cap; /* OVS capability */ struct acl_service_cap acl_cap; /* ACL capability */ }; struct cfg_eq { enum hinic_service_type type; int eqn; int free; /* 1 - alocated, 0- freed */ }; struct cfg_eq_info { struct cfg_eq *eq; u8 num_ceq; u8 num_ceq_remain; /* mutex used for allocate EQs */ struct mutex eq_mutex; }; struct irq_alloc_info_st { enum hinic_service_type type; int free; /* 1 - alocated, 0- freed */ struct irq_info info; }; struct cfg_irq_info { struct irq_alloc_info_st *alloc_info; u16 num_total; u16 num_irq_remain; u16 num_irq_hw; /* device max irq number */ /* mutex used for allocate EQs */ struct mutex irq_mutex; }; #define VECTOR_THRESHOLD 2 struct cfg_mgmt_info { struct hinic_hwdev *hwdev; struct service_cap svc_cap; struct cfg_eq_info eq_info; /* EQ */ struct cfg_irq_info irq_param_info; /* IRQ */ u32 func_seq_num; /* temporary */ }; enum cfg_sub_cmd { /* PPF(PF) <-> FW */ HINIC_CFG_NIC_CAP = 0, CFG_FW_VERSION, CFG_UCODE_VERSION, HINIC_CFG_FUNC_CAP, HINIC_CFG_MBOX_CAP = 6, }; struct hinic_dev_cap { u8 status; u8 version; u8 rsvd0[6]; /* Public resource */ u8 sf_svc_attr; u8 host_id; u8 sf_en_pf; u8 sf_en_vf; u8 ep_id; u8 intr_type; u8 max_cos_id; u8 er_id; u8 port_id; u8 max_vf; u16 svc_cap_en; u16 host_total_func; u8 host_oq_id_mask_val; u8 max_vf_cos_id; u32 max_conn_num; u16 max_stick2cache_num; u16 max_bfilter_start_addr; u16 bfilter_len; u16 hash_bucket_num; u8 cfg_file_ver; u8 net_port_mode; u8 valid_cos_bitmap; /* every bit indicate cos is valid */ u8 force_up; u32 pf_num; u32 pf_id_start; u32 vf_num; u32 vf_id_start; /* shared resource */ u32 host_pctx_num; u8 host_sf_en; u8 rsvd2[3]; u32 host_ccxt_num; u32 host_scq_num; u32 host_srq_num; u32 host_mpt_num; /* l2nic */ u16 nic_max_sq; u16 nic_max_rq; u16 nic_vf_max_sq; u16 nic_vf_max_rq; u8 nic_lro_en; u8 nic_lro_sz; u8 nic_tso_sz; u8 max_queue_allowed; /* RoCE */ u32 roce_max_qp; u32 roce_max_cq; u32 roce_max_srq; u32 roce_max_mpt; u32 roce_vf_max_qp; u32 roce_vf_max_cq; u32 roce_vf_max_srq; u32 roce_vf_max_mpt; u32 roce_cmtt_cl_start; u32 roce_cmtt_cl_end; u32 roce_cmtt_cl_size; u32 roce_dmtt_cl_start; u32 roce_dmtt_cl_end; u32 roce_dmtt_cl_size; u32 roce_wqe_cl_start; u32 roce_wqe_cl_end; u32 roce_wqe_cl_size; /* IWARP */ u32 iwarp_max_qp; u32 iwarp_max_cq; u32 iwarp_max_mpt; u32 iwarp_vf_max_qp; u32 iwarp_vf_max_cq; u32 iwarp_vf_max_mpt; u32 iwarp_cmtt_cl_start; u32 iwarp_cmtt_cl_end; u32 iwarp_cmtt_cl_size; u32 iwarp_dmtt_cl_start; u32 iwarp_dmtt_cl_end; u32 iwarp_dmtt_cl_size; u32 iwarp_wqe_cl_start; u32 iwarp_wqe_cl_end; u32 iwarp_wqe_cl_size; /* FCoE */ u32 fcoe_max_qp; u32 fcoe_max_cq; u32 fcoe_max_srq; u32 fcoe_max_cctx; u32 fcoe_cctx_id_start; u8 fcoe_vp_id_start; u8 fcoe_vp_id_end; u8 rsvd4[2]; /* OVS */ u32 ovs_max_qpc; u8 ovs_dq_en; u8 rsvd5[3]; /* ToE */ u32 toe_max_pctx; u32 toe_max_cq; u32 toe_max_srq; u32 toe_srq_id_start; /* FC */ u32 fc_max_pctx; u32 fc_max_scq; u32 fc_max_srq; u32 fc_max_cctx; u32 fc_cctx_id_start; u8 fc_vp_id_start; u8 fc_vp_id_end; u16 func_id; }; #define VSW_UP_CFG_TIMEOUT (0xFF00000) #define VSW_SET_STATEFUL_BITS_TOE(flag) \ ((flag) << VSW_STATEFUL_TOE_EN) #define VSW_SET_STATEFUL_BITS_FCOE(flag) \ ((flag) << VSW_STATEFUL_FCOE_EN) #define VSW_SET_STATEFUL_BITS_IWARP(flag) \ ((flag) << VSW_STATEFUL_IWARP_EN) #define VSW_SET_STATEFUL_BITS_ROCE(flag) \ ((flag) << VSW_STATEFUL_ROCE_EN) #define VSW_GET_STATEFUL_BITS_TOE(flag) \ ((bool)(((flag) >> VSW_STATEFUL_TOE_EN) & 0x1U)) #define VSW_GET_STATEFUL_BITS_FCOE(flag) \ ((bool)(((flag) >> VSW_STATEFUL_FCOE_EN) & 0x1U)) #define VSW_GET_STATEFUL_BITS_IWARP(flag) \ ((bool)(((flag) >> VSW_STATEFUL_IWARP_EN) & 0x1U)) #define VSW_GET_STATEFUL_BITS_ROCE(flag) \ ((bool)(((flag) >> VSW_STATEFUL_ROCE_EN) & 0x1U)) enum tag_vsw_major_cmd { VSW_MAJOR_MISC = 10, /* 0~9 reserved for driver */ VSW_MAJOR_L2SWITCH, VSW_MAJOR_L2MULTICAST, VSW_MAJOR_QOS, VSW_MAJOR_PKTSUPS, VSW_MAJOR_VLANFILTER, VSW_MAJOR_MACFILTER, VSW_MAJOR_IPFILTER, VSW_MAJOR_VLANMAPPING, VSW_MAJOR_ETHTRUNK, VSW_MAJOR_MIRROR, VSW_MAJOR_DFX, VSW_MAJOR_ACL, }; enum tag_vsw_minor_misc_cmd { VSW_MINOR_MISC_INIT_FUNC = 0, VSW_MINOR_MISC_SET_FUNC_SF_ENBITS, VSW_MINOR_MISC_GET_FUNC_SF_ENBITS, VSW_MINOR_MISC_CMD_MAX, }; /* vswitch eth-trunk sub-command */ enum tag_nic_stateful_enbits { VSW_STATEFUL_TOE_EN = 0, VSW_STATEFUL_FCOE_EN = 1, VSW_STATEFUL_IWARP_EN = 2, VSW_STATEFUL_ROCE_EN = 3, }; /* function stateful enable parameters */ struct nic_misc_func_sf_enbits { u8 status; u8 version; u8 rsvd0[6]; u32 function_id; u32 stateful_enbits; /* b0:toe, b1:fcoe, b2:iwarp, b3:roce */ u32 stateful_enmask; /* b0:toe, b1:fcoe, b2:iwarp, b3:roce */ }; #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_cmdq.c000066400000000000000000001215451502027332500214010ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwdev.h" #include "hinic_hwif.h" #include "hinic_nic_io.h" #include "hinic_eqs.h" #include "hinic_wq.h" #include "hinic_cmdq.h" #define CMDQ_CMD_TIMEOUT 5000 /* millisecond */ #define CMDQ_CMD_RETRY_TIMEOUT 1000 /* millisecond */ #define UPPER_8_BITS(data) (((data) >> 8) & 0xFF) #define LOWER_8_BITS(data) ((data) & 0xFF) #define CMDQ_DB_INFO_HI_PROD_IDX_SHIFT 0 #define CMDQ_DB_INFO_QUEUE_TYPE_SHIFT 23 #define CMDQ_DB_INFO_CMDQ_TYPE_SHIFT 24 #define CMDQ_DB_INFO_SRC_TYPE_SHIFT 27 #define CMDQ_DB_INFO_HI_PROD_IDX_MASK 0xFFU #define CMDQ_DB_INFO_QUEUE_TYPE_MASK 0x1U #define CMDQ_DB_INFO_CMDQ_TYPE_MASK 0x7U #define CMDQ_DB_INFO_SRC_TYPE_MASK 0x1FU #define CMDQ_DB_INFO_SET(val, member) \ (((val) & CMDQ_DB_INFO_##member##_MASK) \ << CMDQ_DB_INFO_##member##_SHIFT) #define CMDQ_CTRL_PI_SHIFT 0 #define CMDQ_CTRL_CMD_SHIFT 16 #define CMDQ_CTRL_MOD_SHIFT 24 #define CMDQ_CTRL_ACK_TYPE_SHIFT 29 #define CMDQ_CTRL_HW_BUSY_BIT_SHIFT 31 #define CMDQ_CTRL_PI_MASK 0xFFFFU #define CMDQ_CTRL_CMD_MASK 0xFFU #define CMDQ_CTRL_MOD_MASK 0x1FU #define CMDQ_CTRL_ACK_TYPE_MASK 0x3U #define CMDQ_CTRL_HW_BUSY_BIT_MASK 0x1U #define CMDQ_CTRL_SET(val, member) \ (((val) & CMDQ_CTRL_##member##_MASK) \ << CMDQ_CTRL_##member##_SHIFT) #define CMDQ_CTRL_GET(val, member) \ (((val) >> CMDQ_CTRL_##member##_SHIFT) \ & CMDQ_CTRL_##member##_MASK) #define CMDQ_WQE_HEADER_BUFDESC_LEN_SHIFT 0 #define CMDQ_WQE_HEADER_COMPLETE_FMT_SHIFT 15 #define CMDQ_WQE_HEADER_DATA_FMT_SHIFT 22 #define CMDQ_WQE_HEADER_COMPLETE_REQ_SHIFT 23 #define CMDQ_WQE_HEADER_COMPLETE_SECT_LEN_SHIFT 27 #define CMDQ_WQE_HEADER_CTRL_LEN_SHIFT 29 #define CMDQ_WQE_HEADER_HW_BUSY_BIT_SHIFT 31 #define CMDQ_WQE_HEADER_BUFDESC_LEN_MASK 0xFFU #define CMDQ_WQE_HEADER_COMPLETE_FMT_MASK 0x1U #define CMDQ_WQE_HEADER_DATA_FMT_MASK 0x1U #define CMDQ_WQE_HEADER_COMPLETE_REQ_MASK 0x1U #define CMDQ_WQE_HEADER_COMPLETE_SECT_LEN_MASK 0x3U #define CMDQ_WQE_HEADER_CTRL_LEN_MASK 0x3U #define CMDQ_WQE_HEADER_HW_BUSY_BIT_MASK 0x1U #define CMDQ_WQE_HEADER_SET(val, member) \ (((val) & CMDQ_WQE_HEADER_##member##_MASK) \ << CMDQ_WQE_HEADER_##member##_SHIFT) #define CMDQ_WQE_HEADER_GET(val, member) \ (((val) >> CMDQ_WQE_HEADER_##member##_SHIFT) \ & CMDQ_WQE_HEADER_##member##_MASK) #define CMDQ_CTXT_CURR_WQE_PAGE_PFN_SHIFT 0 #define CMDQ_CTXT_EQ_ID_SHIFT 56 #define CMDQ_CTXT_CEQ_ARM_SHIFT 61 #define CMDQ_CTXT_CEQ_EN_SHIFT 62 #define CMDQ_CTXT_HW_BUSY_BIT_SHIFT 63 #define CMDQ_CTXT_CURR_WQE_PAGE_PFN_MASK 0xFFFFFFFFFFFFF #define CMDQ_CTXT_EQ_ID_MASK 0x1F #define CMDQ_CTXT_CEQ_ARM_MASK 0x1 #define CMDQ_CTXT_CEQ_EN_MASK 0x1 #define CMDQ_CTXT_HW_BUSY_BIT_MASK 0x1 #define CMDQ_CTXT_PAGE_INFO_SET(val, member) \ (((u64)(val) & CMDQ_CTXT_##member##_MASK) \ << CMDQ_CTXT_##member##_SHIFT) #define CMDQ_CTXT_PAGE_INFO_GET(val, member) \ (((u64)(val) >> CMDQ_CTXT_##member##_SHIFT) \ & CMDQ_CTXT_##member##_MASK) #define CMDQ_CTXT_WQ_BLOCK_PFN_SHIFT 0 #define CMDQ_CTXT_CI_SHIFT 52 #define CMDQ_CTXT_WQ_BLOCK_PFN_MASK 0xFFFFFFFFFFFFF #define CMDQ_CTXT_CI_MASK 0xFFF #define CMDQ_CTXT_BLOCK_INFO_SET(val, member) \ (((u64)(val) & CMDQ_CTXT_##member##_MASK) \ << CMDQ_CTXT_##member##_SHIFT) #define CMDQ_CTXT_BLOCK_INFO_GET(val, member) \ (((u64)(val) >> CMDQ_CTXT_##member##_SHIFT) \ & CMDQ_CTXT_##member##_MASK) #define SAVED_DATA_ARM_SHIFT 31 #define SAVED_DATA_ARM_MASK 0x1U #define SAVED_DATA_SET(val, member) \ (((val) & SAVED_DATA_##member##_MASK) \ << SAVED_DATA_##member##_SHIFT) #define SAVED_DATA_CLEAR(val, member) \ ((val) & (~(SAVED_DATA_##member##_MASK \ << SAVED_DATA_##member##_SHIFT))) #define WQE_ERRCODE_VAL_SHIFT 20 #define WQE_ERRCODE_VAL_MASK 0xF #define WQE_ERRCODE_GET(val, member) \ (((val) >> WQE_ERRCODE_##member##_SHIFT) & \ WQE_ERRCODE_##member##_MASK) #define CEQE_CMDQ_TYPE_SHIFT 0 #define CEQE_CMDQ_TYPE_MASK 0x7 #define CEQE_CMDQ_GET(val, member) \ (((val) >> CEQE_CMDQ_##member##_SHIFT) & CEQE_CMDQ_##member##_MASK) #define WQE_COMPLETED(ctrl_info) CMDQ_CTRL_GET(ctrl_info, HW_BUSY_BIT) #define WQE_HEADER(wqe) ((struct hinic_cmdq_header *)(wqe)) #define CMDQ_DB_PI_OFF(pi) (((u16)LOWER_8_BITS(pi)) << 3) #define CMDQ_DB_ADDR(db_base, pi) \ (((u8 *)(db_base) + HINIC_DB_OFF) + CMDQ_DB_PI_OFF(pi)) #define CMDQ_PFN_SHIFT 12 #define CMDQ_PFN(addr) ((addr) >> CMDQ_PFN_SHIFT) #define FIRST_DATA_TO_WRITE_LAST sizeof(u64) #define WQE_LCMD_SIZE 64 #define WQE_SCMD_SIZE 64 #define COMPLETE_LEN 3 #define CMDQ_WQEBB_SIZE 64 #define CMDQ_WQE_SIZE 64 #define CMDQ_WQ_PAGE_SIZE 4096 #define WQE_NUM_WQEBBS(wqe_size, wq) \ ((u16)(ALIGN((u32)(wqe_size), (wq)->wqebb_size) / (wq)->wqebb_size)) #define cmdq_to_cmdqs(cmdq) container_of((cmdq) - (cmdq)->cmdq_type, \ struct hinic_cmdqs, cmdq[0]) #define CMDQ_SEND_CMPT_CODE 10 #define CMDQ_COMPLETE_CMPT_CODE 11 #define HINIC_GET_CMDQ_FREE_WQEBBS(cmdq_wq) \ atomic_read(&(cmdq_wq)->delta) enum cmdq_scmd_type { CMDQ_SET_ARM_CMD = 2, }; enum cmdq_wqe_type { WQE_LCMD_TYPE, WQE_SCMD_TYPE, }; enum ctrl_sect_len { CTRL_SECT_LEN = 1, CTRL_DIRECT_SECT_LEN = 2, }; enum bufdesc_len { BUFDESC_LCMD_LEN = 2, BUFDESC_SCMD_LEN = 3, }; enum data_format { DATA_SGE, DATA_DIRECT, }; enum completion_format { COMPLETE_DIRECT, COMPLETE_SGE, }; enum completion_request { CEQ_SET = 1, }; enum cmdq_cmd_type { SYNC_CMD_DIRECT_RESP, SYNC_CMD_SGE_RESP, ASYNC_CMD, }; bool hinic_cmdq_idle(struct hinic_cmdq *cmdq) { struct hinic_wq *wq = cmdq->wq; return (atomic_read(&wq->delta) == wq->q_depth ? true : false); } struct hinic_cmd_buf *hinic_alloc_cmd_buf(void *hwdev) { struct hinic_cmdqs *cmdqs; struct hinic_cmd_buf *cmd_buf; void *dev; if (!hwdev) { pr_err("Failed to alloc cmd buf, invalid hwdev\n"); return NULL; } cmdqs = ((struct hinic_hwdev *)hwdev)->cmdqs; dev = ((struct hinic_hwdev *)hwdev)->dev_hdl; cmd_buf = kzalloc(sizeof(*cmd_buf), GFP_ATOMIC); if (!cmd_buf) return NULL; cmd_buf->buf = dma_pool_alloc(cmdqs->cmd_buf_pool, GFP_ATOMIC, &cmd_buf->dma_addr); if (!cmd_buf->buf) { sdk_err(dev, "Failed to allocate cmdq cmd buf from the pool\n"); goto alloc_pci_buf_err; } return cmd_buf; alloc_pci_buf_err: kfree(cmd_buf); return NULL; } EXPORT_SYMBOL(hinic_alloc_cmd_buf); void hinic_free_cmd_buf(void *hwdev, struct hinic_cmd_buf *cmd_buf) { struct hinic_cmdqs *cmdqs; if (!hwdev || !cmd_buf) { pr_err("Failed to free cmd buf\n"); return; } cmdqs = ((struct hinic_hwdev *)hwdev)->cmdqs; dma_pool_free(cmdqs->cmd_buf_pool, cmd_buf->buf, cmd_buf->dma_addr); kfree(cmd_buf); } EXPORT_SYMBOL(hinic_free_cmd_buf); static int cmdq_wqe_size(enum cmdq_wqe_type wqe_type) { int wqe_size = 0; switch (wqe_type) { case WQE_LCMD_TYPE: wqe_size = WQE_LCMD_SIZE; break; case WQE_SCMD_TYPE: wqe_size = WQE_SCMD_SIZE; break; } return wqe_size; } static int cmdq_get_wqe_size(enum bufdesc_len len) { int wqe_size = 0; switch (len) { case BUFDESC_LCMD_LEN: wqe_size = WQE_LCMD_SIZE; break; case BUFDESC_SCMD_LEN: wqe_size = WQE_SCMD_SIZE; break; } return wqe_size; } static void cmdq_set_completion(struct hinic_cmdq_completion *complete, struct hinic_cmd_buf *buf_out) { struct hinic_sge_resp *sge_resp = &complete->sge_resp; hinic_set_sge(&sge_resp->sge, buf_out->dma_addr, HINIC_CMDQ_BUF_SIZE); } static void cmdq_set_lcmd_bufdesc(struct hinic_cmdq_wqe_lcmd *wqe, struct hinic_cmd_buf *buf_in) { hinic_set_sge(&wqe->buf_desc.sge, buf_in->dma_addr, buf_in->size); } static void cmdq_set_inline_wqe_data(struct hinic_cmdq_inline_wqe *wqe, const void *buf_in, u32 in_size) { struct hinic_cmdq_wqe_scmd *wqe_scmd = &wqe->wqe_scmd; wqe_scmd->buf_desc.buf_len = in_size; memcpy(wqe_scmd->buf_desc.data, buf_in, in_size); } static void cmdq_fill_db(struct hinic_cmdq_db *db, enum hinic_cmdq_type cmdq_type, u16 prod_idx) { db->db_info = CMDQ_DB_INFO_SET(UPPER_8_BITS(prod_idx), HI_PROD_IDX) | CMDQ_DB_INFO_SET(HINIC_DB_CMDQ_TYPE, QUEUE_TYPE) | CMDQ_DB_INFO_SET(cmdq_type, CMDQ_TYPE) | CMDQ_DB_INFO_SET(HINIC_DB_SRC_CMDQ_TYPE, SRC_TYPE); } static void cmdq_set_db(struct hinic_cmdq *cmdq, enum hinic_cmdq_type cmdq_type, u16 prod_idx) { struct hinic_cmdq_db db; cmdq_fill_db(&db, cmdq_type, prod_idx); /* The data that is written to HW should be in Big Endian Format */ db.db_info = cpu_to_be32(db.db_info); wmb(); /* write all before the doorbell */ writel(db.db_info, CMDQ_DB_ADDR(cmdq->db_base, prod_idx)); } static void cmdq_wqe_fill(void *dst, const void *src) { memcpy((u8 *)dst + FIRST_DATA_TO_WRITE_LAST, (u8 *)src + FIRST_DATA_TO_WRITE_LAST, CMDQ_WQE_SIZE - FIRST_DATA_TO_WRITE_LAST); wmb(); /* The first 8 bytes should be written last */ *(u64 *)dst = *(u64 *)src; } static void cmdq_prepare_wqe_ctrl(struct hinic_cmdq_wqe *wqe, int wrapped, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, u16 prod_idx, enum completion_format complete_format, enum data_format data_format, enum bufdesc_len buf_len) { struct hinic_ctrl *ctrl; enum ctrl_sect_len ctrl_len; struct hinic_cmdq_wqe_lcmd *wqe_lcmd; struct hinic_cmdq_wqe_scmd *wqe_scmd; u32 saved_data = WQE_HEADER(wqe)->saved_data; if (data_format == DATA_SGE) { wqe_lcmd = &wqe->wqe_lcmd; wqe_lcmd->status.status_info = 0; ctrl = &wqe_lcmd->ctrl; ctrl_len = CTRL_SECT_LEN; } else { wqe_scmd = &wqe->inline_wqe.wqe_scmd; wqe_scmd->status.status_info = 0; ctrl = &wqe_scmd->ctrl; ctrl_len = CTRL_DIRECT_SECT_LEN; } ctrl->ctrl_info = CMDQ_CTRL_SET(prod_idx, PI) | CMDQ_CTRL_SET(cmd, CMD) | CMDQ_CTRL_SET(mod, MOD) | CMDQ_CTRL_SET(ack_type, ACK_TYPE); WQE_HEADER(wqe)->header_info = CMDQ_WQE_HEADER_SET(buf_len, BUFDESC_LEN) | CMDQ_WQE_HEADER_SET(complete_format, COMPLETE_FMT) | CMDQ_WQE_HEADER_SET(data_format, DATA_FMT) | CMDQ_WQE_HEADER_SET(CEQ_SET, COMPLETE_REQ) | CMDQ_WQE_HEADER_SET(COMPLETE_LEN, COMPLETE_SECT_LEN) | CMDQ_WQE_HEADER_SET(ctrl_len, CTRL_LEN) | CMDQ_WQE_HEADER_SET((u32)wrapped, HW_BUSY_BIT); if (cmd == CMDQ_SET_ARM_CMD && mod == HINIC_MOD_COMM) { saved_data &= SAVED_DATA_CLEAR(saved_data, ARM); WQE_HEADER(wqe)->saved_data = saved_data | SAVED_DATA_SET(1, ARM); } else { saved_data &= SAVED_DATA_CLEAR(saved_data, ARM); WQE_HEADER(wqe)->saved_data = saved_data; } } static void cmdq_set_lcmd_wqe(struct hinic_cmdq_wqe *wqe, enum cmdq_cmd_type cmd_type, struct hinic_cmd_buf *buf_in, struct hinic_cmd_buf *buf_out, int wrapped, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, u16 prod_idx) { struct hinic_cmdq_wqe_lcmd *wqe_lcmd = &wqe->wqe_lcmd; enum completion_format complete_format = COMPLETE_DIRECT; switch (cmd_type) { case SYNC_CMD_SGE_RESP: if (buf_out) { complete_format = COMPLETE_SGE; cmdq_set_completion(&wqe_lcmd->completion, buf_out); } break; case SYNC_CMD_DIRECT_RESP: complete_format = COMPLETE_DIRECT; wqe_lcmd->completion.direct_resp = 0; break; case ASYNC_CMD: complete_format = COMPLETE_DIRECT; wqe_lcmd->completion.direct_resp = 0; wqe_lcmd->buf_desc.saved_async_buf = (u64)(buf_in); break; } cmdq_prepare_wqe_ctrl(wqe, wrapped, ack_type, mod, cmd, prod_idx, complete_format, DATA_SGE, BUFDESC_LCMD_LEN); cmdq_set_lcmd_bufdesc(wqe_lcmd, buf_in); } static void cmdq_set_inline_wqe(struct hinic_cmdq_wqe *wqe, enum cmdq_cmd_type cmd_type, void *buf_in, u16 in_size, struct hinic_cmd_buf *buf_out, int wrapped, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, u16 prod_idx) { struct hinic_cmdq_wqe_scmd *wqe_scmd = &wqe->inline_wqe.wqe_scmd; enum completion_format complete_format = COMPLETE_DIRECT; switch (cmd_type) { case SYNC_CMD_SGE_RESP: complete_format = COMPLETE_SGE; cmdq_set_completion(&wqe_scmd->completion, buf_out); break; case SYNC_CMD_DIRECT_RESP: complete_format = COMPLETE_DIRECT; wqe_scmd->completion.direct_resp = 0; break; default: break; } cmdq_prepare_wqe_ctrl(wqe, wrapped, ack_type, mod, cmd, prod_idx, complete_format, DATA_DIRECT, BUFDESC_SCMD_LEN); cmdq_set_inline_wqe_data(&wqe->inline_wqe, buf_in, in_size); } static void cmdq_update_cmd_status(struct hinic_cmdq *cmdq, u16 prod_idx, struct hinic_cmdq_wqe *wqe) { struct hinic_cmdq_cmd_info *cmd_info; struct hinic_cmdq_wqe_lcmd *wqe_lcmd; u32 status_info; wqe_lcmd = &wqe->wqe_lcmd; cmd_info = &cmdq->cmd_infos[prod_idx]; if (cmd_info->errcode) { status_info = be32_to_cpu(wqe_lcmd->status.status_info); *cmd_info->errcode = WQE_ERRCODE_GET(status_info, VAL); } if (cmd_info->direct_resp && cmd_info->cmd_type == HINIC_CMD_TYPE_DIRECT_RESP) *cmd_info->direct_resp = cpu_to_be64(wqe_lcmd->completion.direct_resp); } static int hinic_cmdq_sync_timeout_check(struct hinic_cmdq *cmdq, struct hinic_cmdq_wqe *wqe, u16 pi, enum hinic_mod_type mod, u8 cmd) { struct hinic_cmdq_wqe_lcmd *wqe_lcmd; struct hinic_ctrl *ctrl; u32 ctrl_info; wqe_lcmd = &wqe->wqe_lcmd; ctrl = &wqe_lcmd->ctrl; ctrl_info = be32_to_cpu((ctrl)->ctrl_info); if (!WQE_COMPLETED(ctrl_info)) { sdk_info(cmdq->hwdev->dev_hdl, "Cmdq sync command check busy bit not set, mod: %u, cmd: 0x%x\n", mod, cmd); return -EFAULT; } cmdq_update_cmd_status(cmdq, pi, wqe); sdk_info(cmdq->hwdev->dev_hdl, "Cmdq sync command check succeed, mod: %u, cmd: 0x%x\n", mod, cmd); return 0; } static void __clear_cmd_info(struct hinic_cmdq_cmd_info *cmd_info, const int *errcode, struct completion *done, u64 *out_param) { if (cmd_info->errcode == errcode) cmd_info->errcode = NULL; if (cmd_info->done == done) cmd_info->done = NULL; if (cmd_info->direct_resp == out_param) cmd_info->direct_resp = NULL; } static int cmdq_retry_get_ack(struct hinic_hwdev *hwdev, struct completion *done, u8 ceq_id) { ulong timeo = msecs_to_jiffies(CMDQ_CMD_RETRY_TIMEOUT); int err; init_completion(done); err = hinic_reschedule_eq(hwdev, HINIC_CEQ, ceq_id); if (err) return err; if (!wait_for_completion_timeout(done, timeo)) return -ETIMEDOUT; return 0; } static int cmdq_sync_cmd_direct_resp(struct hinic_cmdq *cmdq, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, struct hinic_cmd_buf *buf_in, u64 *out_param, u32 timeout) { struct hinic_wq *wq = cmdq->wq; struct hinic_cmdq_wqe *curr_wqe, wqe; struct hinic_cmdq_cmd_info *cmd_info; struct completion done; u16 curr_prod_idx, next_prod_idx, num_wqebbs; int wrapped, errcode = 0, wqe_size = cmdq_wqe_size(WQE_LCMD_TYPE); int cmpt_code = CMDQ_SEND_CMPT_CODE; ulong timeo; u64 curr_msg_id; int err; num_wqebbs = WQE_NUM_WQEBBS(wqe_size, wq); /* Keep wrapped and doorbell index correct. bh - for tasklet(ceq) */ spin_lock_bh(&cmdq->cmdq_lock); /* in order to save a wqebb for setting arm_bit when * send cmdq commands frequently resulting in cmdq full */ if (HINIC_GET_CMDQ_FREE_WQEBBS(wq) < num_wqebbs + 1) { spin_unlock_bh(&cmdq->cmdq_lock); return -EBUSY; } /* WQE_SIZE = WQEBB_SIZE, we will get the wq element and not shadow */ curr_wqe = hinic_get_wqe(cmdq->wq, num_wqebbs, &curr_prod_idx); if (!curr_wqe) { spin_unlock_bh(&cmdq->cmdq_lock); sdk_err(cmdq->hwdev->dev_hdl, "Can not get avalible wqebb, mod: %u, cmd: 0x%x\n", mod, cmd); return -EBUSY; } memset(&wqe, 0, sizeof(wqe)); wrapped = cmdq->wrapped; next_prod_idx = curr_prod_idx + num_wqebbs; if (next_prod_idx >= wq->q_depth) { cmdq->wrapped = !cmdq->wrapped; next_prod_idx -= wq->q_depth; } cmd_info = &cmdq->cmd_infos[curr_prod_idx]; init_completion(&done); cmd_info->done = &done; cmd_info->errcode = &errcode; cmd_info->direct_resp = out_param; cmd_info->cmpt_code = &cmpt_code; cmdq_set_lcmd_wqe(&wqe, SYNC_CMD_DIRECT_RESP, buf_in, NULL, wrapped, ack_type, mod, cmd, curr_prod_idx); /* The data that is written to HW should be in Big Endian Format */ hinic_cpu_to_be32(&wqe, wqe_size); /* CMDQ WQE is not shadow, therefore wqe will be written to wq */ cmdq_wqe_fill(curr_wqe, &wqe); cmd_info->cmd_type = HINIC_CMD_TYPE_DIRECT_RESP; (cmd_info->cmdq_msg_id)++; curr_msg_id = cmd_info->cmdq_msg_id; cmdq_set_db(cmdq, HINIC_CMDQ_SYNC, next_prod_idx); spin_unlock_bh(&cmdq->cmdq_lock); timeo = msecs_to_jiffies(timeout ? timeout : CMDQ_CMD_TIMEOUT); if (!wait_for_completion_timeout(&done, timeo) && cmdq_retry_get_ack(cmdq->hwdev, &done, HINIC_CEQ_ID_CMDQ)) { spin_lock_bh(&cmdq->cmdq_lock); if (cmd_info->cmpt_code == &cmpt_code) cmd_info->cmpt_code = NULL; if (cmpt_code == CMDQ_COMPLETE_CMPT_CODE) { sdk_info(cmdq->hwdev->dev_hdl, "Cmdq direct sync command has been completed\n"); spin_unlock_bh(&cmdq->cmdq_lock); goto timeout_check_ok; } if (curr_msg_id == cmd_info->cmdq_msg_id) { err = hinic_cmdq_sync_timeout_check(cmdq, curr_wqe, curr_prod_idx, mod, cmd); if (err) cmd_info->cmd_type = HINIC_CMD_TYPE_TIMEOUT; else cmd_info->cmd_type = HINIC_CMD_TYPE_FAKE_TIMEOUT; } else { err = -ETIMEDOUT; sdk_err(cmdq->hwdev->dev_hdl, "Cmdq sync command current msg id dismatch with cmd_info msg id, mod: %u, cmd: 0x%x\n", mod, cmd); } __clear_cmd_info(cmd_info, &errcode, &done, out_param); spin_unlock_bh(&cmdq->cmdq_lock); if (!err) goto timeout_check_ok; sdk_err(cmdq->hwdev->dev_hdl, "Cmdq sync command timeout, mod: %d cmd: 0x%x prod idx: 0x%x\n", mod, cmd, curr_prod_idx); hinic_dump_ceq_info(cmdq->hwdev); return -ETIMEDOUT; } timeout_check_ok: smp_rmb(); /* read error code after completion */ if (errcode > 1) return errcode; return 0; } static int cmdq_sync_cmd_detail_resp(struct hinic_cmdq *cmdq, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, struct hinic_cmd_buf *buf_in, struct hinic_cmd_buf *buf_out, u32 timeout) { struct hinic_wq *wq = cmdq->wq; struct hinic_cmdq_wqe *curr_wqe, wqe; struct hinic_cmdq_cmd_info *cmd_info; struct completion done; u16 curr_prod_idx, next_prod_idx, num_wqebbs; int wrapped, errcode = 0, wqe_size = cmdq_wqe_size(WQE_LCMD_TYPE); int cmpt_code = CMDQ_SEND_CMPT_CODE; ulong timeo; u64 curr_msg_id; int err; num_wqebbs = WQE_NUM_WQEBBS(wqe_size, wq); /* Keep wrapped and doorbell index correct. bh - for tasklet(ceq) */ spin_lock_bh(&cmdq->cmdq_lock); /* in order to save a wqebb for setting arm_bit when * send cmdq commands frequently resulting in cmdq full */ if (HINIC_GET_CMDQ_FREE_WQEBBS(wq) < num_wqebbs + 1) { spin_unlock_bh(&cmdq->cmdq_lock); return -EBUSY; } /* WQE_SIZE = WQEBB_SIZE, we will get the wq element and not shadow*/ curr_wqe = hinic_get_wqe(cmdq->wq, num_wqebbs, &curr_prod_idx); if (!curr_wqe) { spin_unlock_bh(&cmdq->cmdq_lock); sdk_err(cmdq->hwdev->dev_hdl, "Can not get avalible wqebb, mod: %u, cmd: 0x%x\n", mod, cmd); return -EBUSY; } memset(&wqe, 0, sizeof(wqe)); wrapped = cmdq->wrapped; next_prod_idx = curr_prod_idx + num_wqebbs; if (next_prod_idx >= wq->q_depth) { cmdq->wrapped = !cmdq->wrapped; next_prod_idx -= wq->q_depth; } cmd_info = &cmdq->cmd_infos[curr_prod_idx]; init_completion(&done); cmd_info->done = &done; cmd_info->errcode = &errcode; cmd_info->cmpt_code = &cmpt_code; cmdq_set_lcmd_wqe(&wqe, SYNC_CMD_SGE_RESP, buf_in, buf_out, wrapped, ack_type, mod, cmd, curr_prod_idx); hinic_cpu_to_be32(&wqe, wqe_size); cmdq_wqe_fill(curr_wqe, &wqe); cmd_info->cmd_type = HINIC_CMD_TYPE_SGE_RESP; (cmd_info->cmdq_msg_id)++; curr_msg_id = cmd_info->cmdq_msg_id; cmdq_set_db(cmdq, HINIC_CMDQ_SYNC, next_prod_idx); spin_unlock_bh(&cmdq->cmdq_lock); timeo = msecs_to_jiffies(timeout ? timeout : CMDQ_CMD_TIMEOUT); if (!wait_for_completion_timeout(&done, timeo) && cmdq_retry_get_ack(cmdq->hwdev, &done, HINIC_CEQ_ID_CMDQ)) { spin_lock_bh(&cmdq->cmdq_lock); if (cmd_info->cmpt_code == &cmpt_code) cmd_info->cmpt_code = NULL; if (cmpt_code == CMDQ_COMPLETE_CMPT_CODE) { sdk_info(cmdq->hwdev->dev_hdl, "Cmdq detail sync command has been completed\n"); spin_unlock_bh(&cmdq->cmdq_lock); goto timeout_check_ok; } if (curr_msg_id == cmd_info->cmdq_msg_id) { err = hinic_cmdq_sync_timeout_check(cmdq, curr_wqe, curr_prod_idx, mod, cmd); if (err) cmd_info->cmd_type = HINIC_CMD_TYPE_TIMEOUT; else cmd_info->cmd_type = HINIC_CMD_TYPE_FAKE_TIMEOUT; } else { err = -ETIMEDOUT; sdk_err(cmdq->hwdev->dev_hdl, "Cmdq sync command current msg id dismatch with cmd_info msg id, mod: %u, cmd: 0x%x\n", mod, cmd); } if (cmd_info->errcode == &errcode) cmd_info->errcode = NULL; if (cmd_info->done == &done) cmd_info->done = NULL; spin_unlock_bh(&cmdq->cmdq_lock); if (!err) goto timeout_check_ok; sdk_err(cmdq->hwdev->dev_hdl, "Cmdq sync command timeout, mod: %d cmd: 0x%x prod idx: 0x%x\n", mod, cmd, curr_prod_idx); hinic_dump_ceq_info(cmdq->hwdev); return -ETIMEDOUT; } timeout_check_ok: smp_rmb(); /* read error code after completion */ if (errcode > 1) return errcode; return 0; } static int cmdq_async_cmd(struct hinic_cmdq *cmdq, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, struct hinic_cmd_buf *buf_in) { struct hinic_wq *wq = cmdq->wq; int wqe_size = cmdq_wqe_size(WQE_LCMD_TYPE); u16 curr_prod_idx, next_prod_idx, num_wqebbs; struct hinic_cmdq_wqe *curr_wqe, wqe; int wrapped; num_wqebbs = WQE_NUM_WQEBBS(wqe_size, wq); spin_lock_bh(&cmdq->cmdq_lock); /* WQE_SIZE = WQEBB_SIZE, we will get the wq element and not shadow */ curr_wqe = hinic_get_wqe(cmdq->wq, num_wqebbs, &curr_prod_idx); if (!curr_wqe) { spin_unlock_bh(&cmdq->cmdq_lock); return -EBUSY; } memset(&wqe, 0, sizeof(wqe)); wrapped = cmdq->wrapped; next_prod_idx = curr_prod_idx + num_wqebbs; if (next_prod_idx >= cmdq->wq->q_depth) { cmdq->wrapped = !cmdq->wrapped; next_prod_idx -= cmdq->wq->q_depth; } cmdq_set_lcmd_wqe(&wqe, ASYNC_CMD, buf_in, NULL, wrapped, ack_type, mod, cmd, curr_prod_idx); /* The data that is written to HW should be in Big Endian Format */ hinic_cpu_to_be32(&wqe, wqe_size); cmdq_wqe_fill(curr_wqe, &wqe); cmdq->cmd_infos[curr_prod_idx].cmd_type = HINIC_CMD_TYPE_ASYNC; cmdq_set_db(cmdq, HINIC_CMDQ_ASYNC, next_prod_idx); spin_unlock_bh(&cmdq->cmdq_lock); return 0; } static int cmdq_set_arm_bit(struct hinic_cmdq *cmdq, void *buf_in, u16 in_size) { struct hinic_wq *wq = cmdq->wq; struct hinic_cmdq_wqe *curr_wqe, wqe; u16 curr_prod_idx, next_prod_idx, num_wqebbs; int wrapped, wqe_size = cmdq_wqe_size(WQE_SCMD_TYPE); num_wqebbs = WQE_NUM_WQEBBS(wqe_size, wq); /* Keep wrapped and doorbell index correct. bh - for tasklet(ceq) */ spin_lock_bh(&cmdq->cmdq_lock); /* WQE_SIZE = WQEBB_SIZE, we will get the wq element and not shadow */ curr_wqe = hinic_get_wqe(cmdq->wq, num_wqebbs, &curr_prod_idx); if (!curr_wqe) { spin_unlock_bh(&cmdq->cmdq_lock); sdk_err(cmdq->hwdev->dev_hdl, "Can not get avalible wqebb setting arm\n"); return -EBUSY; } memset(&wqe, 0, sizeof(wqe)); wrapped = cmdq->wrapped; next_prod_idx = curr_prod_idx + num_wqebbs; if (next_prod_idx >= wq->q_depth) { cmdq->wrapped = !cmdq->wrapped; next_prod_idx -= wq->q_depth; } cmdq_set_inline_wqe(&wqe, SYNC_CMD_DIRECT_RESP, buf_in, in_size, NULL, wrapped, HINIC_ACK_TYPE_CMDQ, HINIC_MOD_COMM, CMDQ_SET_ARM_CMD, curr_prod_idx); /* The data that is written to HW should be in Big Endian Format */ hinic_cpu_to_be32(&wqe, wqe_size); /* cmdq wqe is not shadow, therefore wqe will be written to wq */ cmdq_wqe_fill(curr_wqe, &wqe); cmdq->cmd_infos[curr_prod_idx].cmd_type = HINIC_CMD_TYPE_SET_ARM; cmdq_set_db(cmdq, cmdq->cmdq_type, next_prod_idx); spin_unlock_bh(&cmdq->cmdq_lock); return 0; } static int cmdq_params_valid(void *hwdev, struct hinic_cmd_buf *buf_in) { if (!buf_in || !hwdev) { pr_err("Invalid CMDQ buffer addr\n"); return -EINVAL; } if (!buf_in->size || buf_in->size > HINIC_CMDQ_MAX_DATA_SIZE) { pr_err("Invalid CMDQ buffer size: 0x%x\n", buf_in->size); return -EINVAL; } return 0; } #define WAIT_CMDQ_ENABLE_TIMEOUT 300 static int wait_cmdqs_enable(struct hinic_cmdqs *cmdqs) { unsigned long end; end = jiffies + msecs_to_jiffies(WAIT_CMDQ_ENABLE_TIMEOUT); do { if (cmdqs->status & HINIC_CMDQ_ENABLE) return 0; } while (time_before(jiffies, end) && cmdqs->hwdev->chip_present_flag && !cmdqs->disable_flag); cmdqs->disable_flag = 1; return -EBUSY; } int hinic_cmdq_direct_resp(void *hwdev, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, struct hinic_cmd_buf *buf_in, u64 *out_param, u32 timeout) { struct hinic_cmdqs *cmdqs; int err = cmdq_params_valid(hwdev, buf_in); if (err) { pr_err("Invalid CMDQ parameters\n"); return err; } cmdqs = ((struct hinic_hwdev *)hwdev)->cmdqs; if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag) || !hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_CMDQ_INITED)) return -EPERM; err = wait_cmdqs_enable(cmdqs); if (err) { sdk_err(cmdqs->hwdev->dev_hdl, "Cmdq is disable\n"); return err; } err = hinic_func_own_get(hwdev); if (err) return err; err = cmdq_sync_cmd_direct_resp(&cmdqs->cmdq[HINIC_CMDQ_SYNC], ack_type, mod, cmd, buf_in, out_param, timeout); hinic_func_own_free(hwdev); if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag)) return -ETIMEDOUT; else return err; } EXPORT_SYMBOL(hinic_cmdq_direct_resp); int hinic_cmdq_detail_resp(void *hwdev, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, struct hinic_cmd_buf *buf_in, struct hinic_cmd_buf *buf_out, u32 timeout) { struct hinic_cmdqs *cmdqs; int err = cmdq_params_valid(hwdev, buf_in); if (err) return err; cmdqs = ((struct hinic_hwdev *)hwdev)->cmdqs; if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag) || !hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_CMDQ_INITED)) return -EPERM; err = wait_cmdqs_enable(cmdqs); if (err) { sdk_err(cmdqs->hwdev->dev_hdl, "Cmdq is disable\n"); return err; } err = cmdq_sync_cmd_detail_resp(&cmdqs->cmdq[HINIC_CMDQ_SYNC], ack_type, mod, cmd, buf_in, buf_out, timeout); if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag)) return -ETIMEDOUT; else return err; } EXPORT_SYMBOL(hinic_cmdq_detail_resp); int hinic_cmdq_async(void *hwdev, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, struct hinic_cmd_buf *buf_in) { struct hinic_cmdqs *cmdqs; int err = cmdq_params_valid(hwdev, buf_in); if (err) return err; cmdqs = ((struct hinic_hwdev *)hwdev)->cmdqs; if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag) || !hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_CMDQ_INITED)) return -EPERM; err = wait_cmdqs_enable(cmdqs); if (err) { sdk_err(cmdqs->hwdev->dev_hdl, "Cmdq is disable\n"); return err; } return cmdq_async_cmd(&cmdqs->cmdq[HINIC_CMDQ_ASYNC], ack_type, mod, cmd, buf_in); } EXPORT_SYMBOL(hinic_cmdq_async); int hinic_set_arm_bit(void *hwdev, enum hinic_set_arm_type q_type, u16 q_id) { struct hinic_cmdqs *cmdqs; struct hinic_cmdq *cmdq; struct hinic_cmdq_arm_bit arm_bit; enum hinic_cmdq_type cmdq_type = HINIC_CMDQ_SYNC; u16 in_size; int err; if (!hwdev) return -EINVAL; if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag) || !hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_CMDQ_INITED)) return -EPERM; cmdqs = ((struct hinic_hwdev *)hwdev)->cmdqs; if (!(cmdqs->status & HINIC_CMDQ_ENABLE)) return -EBUSY; if (q_type == HINIC_SET_ARM_CMDQ) { if (q_id >= HINIC_MAX_CMDQ_TYPES) return -EFAULT; cmdq_type = q_id; } /* sq is using interrupt now, so we only need to set arm bit for cmdq, * remove comment below if need to set sq arm bit * else * cmdq_type = HINIC_CMDQ_SYNC; */ cmdq = &cmdqs->cmdq[cmdq_type]; arm_bit.q_type = q_type; arm_bit.q_id = q_id; in_size = sizeof(arm_bit); err = cmdq_set_arm_bit(cmdq, &arm_bit, in_size); if (err) { sdk_err(cmdqs->hwdev->dev_hdl, "Failed to set arm for q_type: %d, qid %d\n", q_type, q_id); return err; } return 0; } EXPORT_SYMBOL(hinic_set_arm_bit); static void clear_wqe_complete_bit(struct hinic_cmdq *cmdq, struct hinic_cmdq_wqe *wqe, u16 ci) { struct hinic_cmdq_wqe_lcmd *wqe_lcmd; struct hinic_cmdq_inline_wqe *inline_wqe; struct hinic_cmdq_wqe_scmd *wqe_scmd; struct hinic_ctrl *ctrl; u32 header_info = be32_to_cpu(WQE_HEADER(wqe)->header_info); int buf_len = CMDQ_WQE_HEADER_GET(header_info, BUFDESC_LEN); int wqe_size = cmdq_get_wqe_size(buf_len); u16 num_wqebbs; if (wqe_size == WQE_LCMD_SIZE) { wqe_lcmd = &wqe->wqe_lcmd; ctrl = &wqe_lcmd->ctrl; } else { inline_wqe = &wqe->inline_wqe; wqe_scmd = &inline_wqe->wqe_scmd; ctrl = &wqe_scmd->ctrl; } /* clear HW busy bit */ ctrl->ctrl_info = 0; cmdq->cmd_infos[ci].cmd_type = HINIC_CMD_TYPE_NONE; wmb(); /* verify wqe is clear */ num_wqebbs = WQE_NUM_WQEBBS(wqe_size, cmdq->wq); hinic_put_wqe(cmdq->wq, num_wqebbs); } static void cmdq_sync_cmd_handler(struct hinic_cmdq *cmdq, struct hinic_cmdq_wqe *wqe, u16 cons_idx) { u16 prod_idx = cons_idx; spin_lock(&cmdq->cmdq_lock); cmdq_update_cmd_status(cmdq, prod_idx, wqe); if (cmdq->cmd_infos[prod_idx].cmpt_code) { *cmdq->cmd_infos[prod_idx].cmpt_code = CMDQ_COMPLETE_CMPT_CODE; cmdq->cmd_infos[prod_idx].cmpt_code = NULL; } /* make sure cmpt_code operation before done operation */ smp_rmb(); if (cmdq->cmd_infos[prod_idx].done) { complete(cmdq->cmd_infos[prod_idx].done); cmdq->cmd_infos[prod_idx].done = NULL; } spin_unlock(&cmdq->cmdq_lock); clear_wqe_complete_bit(cmdq, wqe, cons_idx); } static void cmdq_async_cmd_handler(struct hinic_hwdev *hwdev, struct hinic_cmdq *cmdq, struct hinic_cmdq_wqe *wqe, u16 ci) { u64 buf = wqe->wqe_lcmd.buf_desc.saved_async_buf; int addr_sz = sizeof(u64); hinic_be32_to_cpu((void *)&buf, addr_sz); if (buf) hinic_free_cmd_buf(hwdev, (struct hinic_cmd_buf *)buf); clear_wqe_complete_bit(cmdq, wqe, ci); } static int cmdq_arm_ceq_handler(struct hinic_cmdq *cmdq, struct hinic_cmdq_wqe *wqe, u16 ci) { struct hinic_cmdq_inline_wqe *inline_wqe = &wqe->inline_wqe; struct hinic_cmdq_wqe_scmd *wqe_scmd = &inline_wqe->wqe_scmd; struct hinic_ctrl *ctrl = &wqe_scmd->ctrl; u32 ctrl_info = be32_to_cpu((ctrl)->ctrl_info); if (!WQE_COMPLETED(ctrl_info)) return -EBUSY; clear_wqe_complete_bit(cmdq, wqe, ci); return 0; } #define HINIC_CMDQ_WQE_HEAD_LEN 32 static void hinic_dump_cmdq_wqe_head(struct hinic_hwdev *hwdev, struct hinic_cmdq_wqe *wqe) { u32 i; u32 *data = (u32 *)wqe; for (i = 0; i < (HINIC_CMDQ_WQE_HEAD_LEN / sizeof(u32)); i += 4) { sdk_info(hwdev->dev_hdl, "wqe data: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", data[i], data[i + 1], data[i + 2], data[i + 3]);/*lint !e679*/ } } void hinic_cmdq_ceq_handler(void *handle, u32 ceqe_data) { struct hinic_cmdqs *cmdqs = ((struct hinic_hwdev *)handle)->cmdqs; enum hinic_cmdq_type cmdq_type = CEQE_CMDQ_GET(ceqe_data, TYPE); struct hinic_cmdq *cmdq = &cmdqs->cmdq[cmdq_type]; struct hinic_hwdev *hwdev = cmdqs->hwdev; struct hinic_cmdq_wqe *wqe; struct hinic_cmdq_wqe_lcmd *wqe_lcmd; struct hinic_ctrl *ctrl; struct hinic_cmdq_cmd_info *cmd_info; u32 ctrl_info; u16 ci; int set_arm = 1; while ((wqe = hinic_read_wqe(cmdq->wq, 1, &ci)) != NULL) { cmd_info = &cmdq->cmd_infos[ci]; if (cmd_info->cmd_type == HINIC_CMD_TYPE_NONE) { set_arm = 1; break; } else if (cmd_info->cmd_type == HINIC_CMD_TYPE_TIMEOUT || cmd_info->cmd_type == HINIC_CMD_TYPE_FAKE_TIMEOUT) { if (cmd_info->cmd_type == HINIC_CMD_TYPE_TIMEOUT) { sdk_info(hwdev->dev_hdl, "Cmdq timeout, q_id: %u, ci: %u\n", cmdq_type, ci); hinic_dump_cmdq_wqe_head(hwdev, wqe); } set_arm = 1; clear_wqe_complete_bit(cmdq, wqe, ci); } else if (cmd_info->cmd_type == HINIC_CMD_TYPE_SET_ARM) { /* arm_bit was set until here */ set_arm = 0; if (cmdq_arm_ceq_handler(cmdq, wqe, ci)) break; } else { set_arm = 1; /* only arm bit is using scmd wqe, the wqe is lcmd */ wqe_lcmd = &wqe->wqe_lcmd; ctrl = &wqe_lcmd->ctrl; ctrl_info = be32_to_cpu((ctrl)->ctrl_info); if (!WQE_COMPLETED(ctrl_info)) break; /* This memory barrier is needed to keep us from reading * any other fields out of the cmdq wqe until we have * verified the command has been processed and * written back. */ dma_rmb(); if (cmdq_type == HINIC_CMDQ_ASYNC) cmdq_async_cmd_handler(hwdev, cmdq, wqe, ci); else cmdq_sync_cmd_handler(cmdq, wqe, ci); } } if (set_arm) hinic_set_arm_bit(hwdev, HINIC_SET_ARM_CMDQ, cmdq_type); } static void cmdq_init_queue_ctxt(struct hinic_cmdq *cmdq, struct hinic_cmdq_pages *cmdq_pages, struct hinic_cmdq_ctxt *cmdq_ctxt) { struct hinic_cmdqs *cmdqs = cmdq_to_cmdqs(cmdq); struct hinic_hwdev *hwdev = cmdqs->hwdev; struct hinic_wq *wq = cmdq->wq; struct hinic_cmdq_ctxt_info *ctxt_info = &cmdq_ctxt->ctxt_info; u64 wq_first_page_paddr, cmdq_first_block_paddr, pfn; u16 start_ci = (u16)wq->cons_idx; /* The data in the HW is in Big Endian Format */ wq_first_page_paddr = be64_to_cpu(*wq->block_vaddr); pfn = CMDQ_PFN(wq_first_page_paddr); ctxt_info->curr_wqe_page_pfn = CMDQ_CTXT_PAGE_INFO_SET(1, HW_BUSY_BIT) | CMDQ_CTXT_PAGE_INFO_SET(1, CEQ_EN) | CMDQ_CTXT_PAGE_INFO_SET(1, CEQ_ARM) | CMDQ_CTXT_PAGE_INFO_SET(HINIC_CEQ_ID_CMDQ, EQ_ID) | CMDQ_CTXT_PAGE_INFO_SET(pfn, CURR_WQE_PAGE_PFN); /* If only use one page, use 0-level CLA */ if (cmdq->wq->num_q_pages != 1) { cmdq_first_block_paddr = cmdq_pages->cmdq_page_paddr; pfn = CMDQ_PFN(cmdq_first_block_paddr); } ctxt_info->wq_block_pfn = CMDQ_CTXT_BLOCK_INFO_SET(start_ci, CI) | CMDQ_CTXT_BLOCK_INFO_SET(pfn, WQ_BLOCK_PFN); cmdq_ctxt->func_idx = hinic_global_func_id_hw(hwdev); cmdq_ctxt->ppf_idx = HINIC_HWIF_PPF_IDX(hwdev->hwif); cmdq_ctxt->cmdq_id = cmdq->cmdq_type; } bool hinic_cmdq_check_vf_ctxt(struct hinic_hwdev *hwdev, struct hinic_cmdq_ctxt *cmdq_ctxt) { struct hinic_cmdq_ctxt_info *ctxt_info = &cmdq_ctxt->ctxt_info; u64 curr_pg_pfn, wq_block_pfn; if (cmdq_ctxt->ppf_idx != hinic_ppf_idx(hwdev) || cmdq_ctxt->cmdq_id > HINIC_MAX_CMDQ_TYPES) return false; curr_pg_pfn = CMDQ_CTXT_PAGE_INFO_GET(ctxt_info->curr_wqe_page_pfn, CURR_WQE_PAGE_PFN); wq_block_pfn = CMDQ_CTXT_BLOCK_INFO_GET(ctxt_info->wq_block_pfn, WQ_BLOCK_PFN); /* VF must use 0-level CLA */ if (curr_pg_pfn != wq_block_pfn) return false; return true; } static int init_cmdq(struct hinic_cmdq *cmdq, struct hinic_hwdev *hwdev, struct hinic_wq *wq, enum hinic_cmdq_type q_type) { void __iomem *db_base; int err = 0; cmdq->wq = wq; cmdq->cmdq_type = q_type; cmdq->wrapped = 1; cmdq->hwdev = hwdev; spin_lock_init(&cmdq->cmdq_lock); cmdq->cmd_infos = kcalloc(wq->q_depth, sizeof(*cmdq->cmd_infos), GFP_KERNEL); if (!cmdq->cmd_infos) { err = -ENOMEM; goto cmd_infos_err; } err = hinic_alloc_db_addr(hwdev, &db_base, NULL); if (err) goto alloc_db_err; cmdq->db_base = (u8 *)db_base; return 0; alloc_db_err: kfree(cmdq->cmd_infos); cmd_infos_err: return err; } static void free_cmdq(struct hinic_hwdev *hwdev, struct hinic_cmdq *cmdq) { hinic_free_db_addr(hwdev, cmdq->db_base, NULL); kfree(cmdq->cmd_infos); } int hinic_set_cmdq_ctxts(struct hinic_hwdev *hwdev) { struct hinic_cmdqs *cmdqs = hwdev->cmdqs; struct hinic_cmdq_ctxt *cmdq_ctxt, cmdq_ctxt_out = {0}; enum hinic_cmdq_type cmdq_type; u16 in_size; u16 out_size = sizeof(*cmdq_ctxt); int err; cmdq_type = HINIC_CMDQ_SYNC; for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { cmdq_ctxt = &cmdqs->cmdq[cmdq_type].cmdq_ctxt; cmdq_ctxt->func_idx = hinic_global_func_id_hw(hwdev); in_size = sizeof(*cmdq_ctxt); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_CMDQ_CTXT_SET, cmdq_ctxt, in_size, &cmdq_ctxt_out, &out_size, 0); if (err || !out_size || cmdq_ctxt_out.status) { sdk_err(hwdev->dev_hdl, "Failed to set cmdq ctxt, err: %d, status: 0x%x, out_size: 0x%x\n", err, cmdq_ctxt_out.status, out_size); return -EFAULT; } } cmdqs->status |= HINIC_CMDQ_ENABLE; cmdqs->disable_flag = 0; return 0; } void hinic_cmdq_flush_cmd(struct hinic_hwdev *hwdev, struct hinic_cmdq *cmdq) { struct hinic_cmdq_wqe *wqe; struct hinic_cmdq_cmd_info *cmdq_info; u16 ci, wqe_left, i; u64 buf; spin_lock_bh(&cmdq->cmdq_lock); wqe_left = cmdq->wq->q_depth - (u16)atomic_read(&cmdq->wq->delta); ci = MASKED_WQE_IDX(cmdq->wq, cmdq->wq->cons_idx); for (i = 0; i < wqe_left; i++, ci++) { ci = MASKED_WQE_IDX(cmdq->wq, ci); cmdq_info = &cmdq->cmd_infos[ci]; if (cmdq_info->cmd_type == HINIC_CMD_TYPE_SET_ARM) continue; if (cmdq->cmdq_type == HINIC_CMDQ_ASYNC) { wqe = hinic_get_wqebb_addr(cmdq->wq, ci); buf = wqe->wqe_lcmd.buf_desc.saved_async_buf; wqe->wqe_lcmd.buf_desc.saved_async_buf = 0; hinic_be32_to_cpu((void *)&buf, sizeof(u64)); if (buf) hinic_free_cmd_buf(hwdev, (struct hinic_cmd_buf *)buf); } else { if (cmdq_info->done) { complete(cmdq_info->done); cmdq_info->done = NULL; cmdq_info->cmpt_code = NULL; cmdq_info->direct_resp = NULL; cmdq_info->errcode = NULL; } } } spin_unlock_bh(&cmdq->cmdq_lock); } int hinic_reinit_cmdq_ctxts(struct hinic_hwdev *hwdev) { struct hinic_cmdqs *cmdqs = hwdev->cmdqs; enum hinic_cmdq_type cmdq_type; cmdq_type = HINIC_CMDQ_SYNC; for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { hinic_cmdq_flush_cmd(hwdev, &cmdqs->cmdq[cmdq_type]); cmdqs->cmdq[cmdq_type].wrapped = 1; hinic_wq_wqe_pg_clear(cmdqs->cmdq[cmdq_type].wq); } return hinic_set_cmdq_ctxts(hwdev); } int hinic_cmdqs_init(struct hinic_hwdev *hwdev) { struct hinic_cmdqs *cmdqs; struct hinic_cmdq_ctxt *cmdq_ctxt; enum hinic_cmdq_type type, cmdq_type; size_t saved_wqs_size; u32 max_wqe_size; int err; cmdqs = kzalloc(sizeof(*cmdqs), GFP_KERNEL); if (!cmdqs) return -ENOMEM; hwdev->cmdqs = cmdqs; cmdqs->hwdev = hwdev; saved_wqs_size = HINIC_MAX_CMDQ_TYPES * sizeof(struct hinic_wq); cmdqs->saved_wqs = kzalloc(saved_wqs_size, GFP_KERNEL); if (!cmdqs->saved_wqs) { sdk_err(hwdev->dev_hdl, "Failed to allocate saved wqs\n"); err = -ENOMEM; goto alloc_wqs_err; } cmdqs->cmd_buf_pool = dma_pool_create("hinic_cmdq", hwdev->dev_hdl, HINIC_CMDQ_BUF_SIZE, HINIC_CMDQ_BUF_SIZE, 0ULL); if (!cmdqs->cmd_buf_pool) { sdk_err(hwdev->dev_hdl, "Failed to create cmdq buffer pool\n"); err = -ENOMEM; goto pool_create_err; } max_wqe_size = (u32)cmdq_wqe_size(WQE_LCMD_TYPE); err = hinic_cmdq_alloc(&cmdqs->cmdq_pages, cmdqs->saved_wqs, hwdev->dev_hdl, HINIC_MAX_CMDQ_TYPES, hwdev->wq_page_size, CMDQ_WQEBB_SIZE, HINIC_CMDQ_DEPTH, max_wqe_size); if (err) { sdk_err(hwdev->dev_hdl, "Failed to allocate cmdq\n"); goto cmdq_alloc_err; } cmdq_type = HINIC_CMDQ_SYNC; for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { err = init_cmdq(&cmdqs->cmdq[cmdq_type], hwdev, &cmdqs->saved_wqs[cmdq_type], cmdq_type); if (err) { sdk_err(hwdev->dev_hdl, "Failed to initialize cmdq type: %d\n", cmdq_type); goto init_cmdq_err; } cmdq_ctxt = &cmdqs->cmdq[cmdq_type].cmdq_ctxt; cmdq_init_queue_ctxt(&cmdqs->cmdq[cmdq_type], &cmdqs->cmdq_pages, cmdq_ctxt); } err = hinic_set_cmdq_ctxts(hwdev); if (err) goto init_cmdq_err; return 0; init_cmdq_err: type = HINIC_CMDQ_SYNC; for (; type < cmdq_type; type++) free_cmdq(hwdev, &cmdqs->cmdq[type]); hinic_cmdq_free(&cmdqs->cmdq_pages, cmdqs->saved_wqs, HINIC_MAX_CMDQ_TYPES); cmdq_alloc_err: dma_pool_destroy(cmdqs->cmd_buf_pool); pool_create_err: kfree(cmdqs->saved_wqs); alloc_wqs_err: kfree(cmdqs); return err; } void hinic_cmdqs_free(struct hinic_hwdev *hwdev) { struct hinic_cmdqs *cmdqs = hwdev->cmdqs; enum hinic_cmdq_type cmdq_type = HINIC_CMDQ_SYNC; cmdqs->status &= ~HINIC_CMDQ_ENABLE; for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { hinic_cmdq_flush_cmd(hwdev, &cmdqs->cmdq[cmdq_type]); free_cmdq(cmdqs->hwdev, &cmdqs->cmdq[cmdq_type]); } hinic_cmdq_free(&cmdqs->cmdq_pages, cmdqs->saved_wqs, HINIC_MAX_CMDQ_TYPES); dma_pool_destroy(cmdqs->cmd_buf_pool); kfree(cmdqs->saved_wqs); kfree(cmdqs); } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_cmdq.h000066400000000000000000000100621502027332500213750ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_CMDQ_H_ #define HINIC_CMDQ_H_ #define HINIC_DB_OFF 0x00000800 #define HINIC_SCMD_DATA_LEN 16 #define HINIC_CMDQ_DEPTH 4096 #define HINIC_CMDQ_BUF_SIZE 2048U #define HINIC_CMDQ_BUF_HW_RSVD 8 #define HINIC_CMDQ_MAX_DATA_SIZE \ (HINIC_CMDQ_BUF_SIZE - HINIC_CMDQ_BUF_HW_RSVD) enum hinic_cmdq_type { HINIC_CMDQ_SYNC, HINIC_CMDQ_ASYNC, HINIC_MAX_CMDQ_TYPES, }; enum hinic_db_src_type { HINIC_DB_SRC_CMDQ_TYPE, HINIC_DB_SRC_L2NIC_SQ_TYPE, }; enum hinic_cmdq_db_type { HINIC_DB_SQ_RQ_TYPE, HINIC_DB_CMDQ_TYPE, }; /* CMDQ WQE CTRLS */ struct hinic_cmdq_header { u32 header_info; u32 saved_data; }; struct hinic_scmd_bufdesc { u32 buf_len; u32 rsvd; u8 data[HINIC_SCMD_DATA_LEN]; }; struct hinic_lcmd_bufdesc { struct hinic_sge sge; u32 rsvd1; u64 saved_async_buf; u64 rsvd3; }; struct hinic_cmdq_db { u32 db_info; u32 rsvd; }; struct hinic_status { u32 status_info; }; struct hinic_ctrl { u32 ctrl_info; }; struct hinic_sge_resp { struct hinic_sge sge; u32 rsvd; }; struct hinic_cmdq_completion { /* HW Format */ union { struct hinic_sge_resp sge_resp; u64 direct_resp; }; }; struct hinic_cmdq_wqe_scmd { struct hinic_cmdq_header header; struct hinic_cmdq_db db; struct hinic_status status; struct hinic_ctrl ctrl; struct hinic_cmdq_completion completion; struct hinic_scmd_bufdesc buf_desc; }; struct hinic_cmdq_wqe_lcmd { struct hinic_cmdq_header header; struct hinic_status status; struct hinic_ctrl ctrl; struct hinic_cmdq_completion completion; struct hinic_lcmd_bufdesc buf_desc; }; struct hinic_cmdq_inline_wqe { struct hinic_cmdq_wqe_scmd wqe_scmd; }; struct hinic_cmdq_wqe { /* HW Format */ union { struct hinic_cmdq_inline_wqe inline_wqe; struct hinic_cmdq_wqe_lcmd wqe_lcmd; }; }; struct hinic_cmdq_arm_bit { u32 q_type; u32 q_id; }; struct hinic_cmdq_ctxt_info { u64 curr_wqe_page_pfn; u64 wq_block_pfn; }; struct hinic_cmdq_ctxt { u8 status; u8 version; u8 rsvd0[6]; u16 func_idx; u8 cmdq_id; u8 ppf_idx; u8 rsvd1[4]; struct hinic_cmdq_ctxt_info ctxt_info; }; enum hinic_cmdq_status { HINIC_CMDQ_ENABLE = BIT(0), }; enum hinic_cmdq_cmd_type { HINIC_CMD_TYPE_NONE, HINIC_CMD_TYPE_SET_ARM, HINIC_CMD_TYPE_DIRECT_RESP, HINIC_CMD_TYPE_SGE_RESP, HINIC_CMD_TYPE_ASYNC, HINIC_CMD_TYPE_TIMEOUT, HINIC_CMD_TYPE_FAKE_TIMEOUT, }; struct hinic_cmdq_cmd_info { enum hinic_cmdq_cmd_type cmd_type; struct completion *done; int *errcode; int *cmpt_code; u64 *direct_resp; u64 cmdq_msg_id; }; struct hinic_cmdq { struct hinic_wq *wq; enum hinic_cmdq_type cmdq_type; int wrapped; /* spinlock for send cmdq commands */ spinlock_t cmdq_lock; /* doorbell area */ u8 __iomem *db_base; struct hinic_cmdq_ctxt cmdq_ctxt; struct hinic_cmdq_cmd_info *cmd_infos; struct hinic_hwdev *hwdev; }; struct hinic_cmdqs { struct hinic_hwdev *hwdev; struct dma_pool *cmd_buf_pool; struct hinic_wq *saved_wqs; struct hinic_cmdq_pages cmdq_pages; struct hinic_cmdq cmdq[HINIC_MAX_CMDQ_TYPES]; u32 status; u32 disable_flag; }; void hinic_cmdq_ceq_handler(void *hwdev, u32 ceqe_data); int hinic_reinit_cmdq_ctxts(struct hinic_hwdev *hwdev); bool hinic_cmdq_idle(struct hinic_cmdq *cmdq); int hinic_cmdqs_init(struct hinic_hwdev *hwdev); void hinic_cmdqs_free(struct hinic_hwdev *hwdev); bool hinic_cmdq_check_vf_ctxt(struct hinic_hwdev *hwdev, struct hinic_cmdq_ctxt *cmdq_ctxt); void hinic_cmdq_flush_cmd(struct hinic_hwdev *hwdev, struct hinic_cmdq *cmdq); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_csr.h000066400000000000000000000154051502027332500212460ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_CSR_H #define HINIC_CSR_H #define HINIC_CSR_GLOBAL_BASE_ADDR 0x4000 /* HW interface registers */ #define HINIC_CSR_FUNC_ATTR0_ADDR 0x0 #define HINIC_CSR_FUNC_ATTR1_ADDR 0x4 #define HINIC_CSR_FUNC_ATTR2_ADDR 0x8 #define HINIC_CSR_FUNC_ATTR4_ADDR 0x10 #define HINIC_CSR_FUNC_ATTR5_ADDR 0x14 #define HINIC_FUNC_CSR_MAILBOX_DATA_OFF 0x80 #define HINIC_FUNC_CSR_MAILBOX_CONTROL_OFF 0x0100 #define HINIC_FUNC_CSR_MAILBOX_INT_OFFSET_OFF 0x0104 #define HINIC_FUNC_CSR_MAILBOX_RESULT_H_OFF 0x0108 #define HINIC_FUNC_CSR_MAILBOX_RESULT_L_OFF 0x010C #define HINIC_CSR_DMA_ATTR_TBL_BASE 0xC80 #define HINIC_ELECTION_BASE 0x200 #define HINIC_CSR_DMA_ATTR_TBL_STRIDE 0x4 #define HINIC_CSR_DMA_ATTR_TBL_ADDR(idx) \ (HINIC_CSR_DMA_ATTR_TBL_BASE \ + (idx) * HINIC_CSR_DMA_ATTR_TBL_STRIDE) #define HINIC_PPF_ELECTION_STRIDE 0x4 #define HINIC_CSR_MAX_PORTS 4 #define HINIC_CSR_PPF_ELECTION_ADDR \ (HINIC_CSR_GLOBAL_BASE_ADDR + HINIC_ELECTION_BASE) #define HINIC_CSR_GLOBAL_MPF_ELECTION_ADDR \ (HINIC_CSR_GLOBAL_BASE_ADDR + HINIC_ELECTION_BASE + \ HINIC_CSR_MAX_PORTS * HINIC_PPF_ELECTION_STRIDE) /* MSI-X registers */ #define HINIC_CSR_MSIX_CTRL_BASE 0x2000 #define HINIC_CSR_MSIX_CNT_BASE 0x2004 #define HINIC_CSR_MSIX_STRIDE 0x8 #define HINIC_CSR_MSIX_CTRL_ADDR(idx) \ (HINIC_CSR_MSIX_CTRL_BASE + (idx) * HINIC_CSR_MSIX_STRIDE) #define HINIC_CSR_MSIX_CNT_ADDR(idx) \ (HINIC_CSR_MSIX_CNT_BASE + (idx) * HINIC_CSR_MSIX_STRIDE) /* EQ registers */ #define HINIC_AEQ_MTT_OFF_BASE_ADDR 0x200 #define HINIC_CEQ_MTT_OFF_BASE_ADDR 0x400 #define HINIC_EQ_MTT_OFF_STRIDE 0x40 #define HINIC_CSR_AEQ_MTT_OFF(id) \ (HINIC_AEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE) #define HINIC_CSR_CEQ_MTT_OFF(id) \ (HINIC_CEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE) #define HINIC_CSR_EQ_PAGE_OFF_STRIDE 8 #define HINIC_AEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \ (HINIC_CSR_AEQ_MTT_OFF(q_id) + \ (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE) #define HINIC_AEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \ (HINIC_CSR_AEQ_MTT_OFF(q_id) + \ (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4) #define HINIC_CEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \ (HINIC_CSR_CEQ_MTT_OFF(q_id) + \ (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE) #define HINIC_CEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \ (HINIC_CSR_CEQ_MTT_OFF(q_id) + \ (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4) #define HINIC_EQ_HI_PHYS_ADDR_REG(type, q_id, pg_num) \ ((u32)(((type) == HINIC_AEQ) ? \ HINIC_AEQ_HI_PHYS_ADDR_REG(q_id, pg_num) : \ HINIC_CEQ_HI_PHYS_ADDR_REG(q_id, pg_num))) #define HINIC_EQ_LO_PHYS_ADDR_REG(type, q_id, pg_num) \ ((u32)(((type) == HINIC_AEQ) ? \ HINIC_AEQ_LO_PHYS_ADDR_REG(q_id, pg_num) : \ HINIC_CEQ_LO_PHYS_ADDR_REG(q_id, pg_num))) #define HINIC_AEQ_CTRL_0_ADDR_BASE 0xE00 #define HINIC_AEQ_CTRL_1_ADDR_BASE 0xE04 #define HINIC_AEQ_CONS_IDX_0_ADDR_BASE 0xE08 #define HINIC_AEQ_CONS_IDX_1_ADDR_BASE 0xE0C #define HINIC_EQ_OFF_STRIDE 0x80 #define HINIC_CSR_AEQ_CTRL_0_ADDR(idx) \ (HINIC_AEQ_CTRL_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) #define HINIC_CSR_AEQ_CTRL_1_ADDR(idx) \ (HINIC_AEQ_CTRL_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) #define HINIC_CSR_AEQ_CONS_IDX_ADDR(idx) \ (HINIC_AEQ_CONS_IDX_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) #define HINIC_CSR_AEQ_PROD_IDX_ADDR(idx) \ (HINIC_AEQ_CONS_IDX_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) #define HINIC_CEQ_CTRL_0_ADDR_BASE 0x1000 #define HINIC_CEQ_CTRL_1_ADDR_BASE 0x1004 #define HINIC_CEQ_CONS_IDX_0_ADDR_BASE 0x1008 #define HINIC_CEQ_CONS_IDX_1_ADDR_BASE 0x100C /* For multi-host mgmt * CEQ_CTRL_0_ADDR: bit26~29: uP write vf mode is normal(0x0),bmgw(0x1), * vmgw(0x2) */ #define HINIC_CSR_CEQ_CTRL_0_ADDR(idx) \ (HINIC_CEQ_CTRL_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) #define HINIC_CSR_CEQ_CTRL_1_ADDR(idx) \ (HINIC_CEQ_CTRL_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) #define HINIC_CSR_CEQ_CONS_IDX_ADDR(idx) \ (HINIC_CEQ_CONS_IDX_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) #define HINIC_CSR_CEQ_PROD_IDX_ADDR(idx) \ (HINIC_CEQ_CONS_IDX_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) /* API CMD registers */ #define HINIC_CSR_API_CMD_BASE 0xF000 #define HINIC_CSR_API_CMD_STRIDE 0x100 #define HINIC_CSR_API_CMD_CHAIN_HEAD_HI_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x0 + (idx) * HINIC_CSR_API_CMD_STRIDE) #define HINIC_CSR_API_CMD_CHAIN_HEAD_LO_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x4 + (idx) * HINIC_CSR_API_CMD_STRIDE) #define HINIC_CSR_API_CMD_STATUS_HI_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x8 + (idx) * HINIC_CSR_API_CMD_STRIDE) #define HINIC_CSR_API_CMD_STATUS_LO_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0xC + (idx) * HINIC_CSR_API_CMD_STRIDE) #define HINIC_CSR_API_CMD_CHAIN_NUM_CELLS_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x10 + (idx) * HINIC_CSR_API_CMD_STRIDE) #define HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x14 + (idx) * HINIC_CSR_API_CMD_STRIDE) #define HINIC_CSR_API_CMD_CHAIN_PI_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x1C + (idx) * HINIC_CSR_API_CMD_STRIDE) #define HINIC_CSR_API_CMD_CHAIN_REQ_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x20 + (idx) * HINIC_CSR_API_CMD_STRIDE) #define HINIC_CSR_API_CMD_STATUS_0_ADDR(idx) \ (HINIC_CSR_API_CMD_BASE + 0x30 + (idx) * HINIC_CSR_API_CMD_STRIDE) /* VF control registers in pf */ #define HINIC_PF_CSR_VF_FLUSH_BASE 0x1F400 #define HINIC_PF_CSR_VF_FLUSH_STRIDE 0x4 #define HINIC_GLB_DMA_SO_RO_REPLACE_ADDR 0x488C #define HINIC_ICPL_RESERVD_ADDR 0x9204 #define HINIC_PF_CSR_VF_FLUSH_OFF(idx) \ (HINIC_PF_CSR_VF_FLUSH_BASE + (idx) * HINIC_PF_CSR_VF_FLUSH_STRIDE) #define HINIC_IPSU_CHANNEL_NUM 7 #define HINIC_IPSU_CHANNEL0_ADDR 0x404 #define HINIC_IPSU_CHANNEL_OFFSET 0x14 #define HINIC_IPSU_DIP_OFFSET 13 #define HINIC_IPSU_SIP_OFFSET 14 #define HINIC_IPSU_DIP_SIP_MASK \ ((0x1 << HINIC_IPSU_SIP_OFFSET) | (0x1 << HINIC_IPSU_DIP_OFFSET)) #define HINIC_IPSURX_VXLAN_DPORT_ADDR 0x6d4 /* For multi-host mgmt * 0x75C0: bit0~3: uP write, host mode is bmwg or normal host * bit4~7: master host ppf write when function initializing * bit8~23: only for slave host PXE * 0x75C4: slave host status * bit0~7: host 0~7 functions status */ #define HINIC_HOST_MODE_ADDR 0x75C0 #define HINIC_MULT_HOST_SLAVE_STATUS_ADDR 0x75C4 #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_ctx_def.h000066400000000000000000000166731502027332500221030ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef __HINIC_CTX_DEF_H__ #define __HINIC_CTX_DEF_H__ #define MASKED_SQ_IDX(sq, idx) ((idx) & (sq)->wq->mask) #define HINIC_CEQE_QN_MASK 0x3FFU #define HINIC_Q_CTXT_MAX 42 #define HINIC_RQ_CQ_MAX 128 #define MAX_WQE_SIZE(max_sge, wqebb_size) \ (((max_sge) <= 2) ? (wqebb_size) : \ ((ALIGN(((max_sge) - 2), 4) / 4 + 1) * (wqebb_size))) /* performance: ci addr RTE_CACHE_SIZE(64B) alignment */ #define HINIC_CI_Q_ADDR_SIZE 64 #define CI_TABLE_SIZE(num_qps, pg_sz) \ (ALIGN((num_qps) * HINIC_CI_Q_ADDR_SIZE, pg_sz)) #define HINIC_CI_VADDR(base_addr, q_id) ((u8 *)(base_addr) + \ (q_id) * HINIC_CI_Q_ADDR_SIZE) #define HINIC_CI_PADDR(base_paddr, q_id) ((base_paddr) + \ (q_id) * HINIC_CI_Q_ADDR_SIZE) #define Q_CTXT_SIZE 48 #define TSO_LRO_CTXT_SIZE 240 #define SQ_CTXT_OFFSET(max_sqs, max_rqs, q_id) \ (((max_rqs) + (max_sqs)) * TSO_LRO_CTXT_SIZE \ + (q_id) * Q_CTXT_SIZE) #define RQ_CTXT_OFFSET(max_sqs, max_rqs, q_id) \ (((max_rqs) + (max_sqs)) * TSO_LRO_CTXT_SIZE \ + (max_sqs) * Q_CTXT_SIZE + (q_id) * Q_CTXT_SIZE) #define SQ_CTXT_SIZE(num_sqs) ((u16)(sizeof(struct hinic_qp_ctxt_header) \ + (num_sqs) * sizeof(struct hinic_sq_ctxt))) #define RQ_CTXT_SIZE(num_rqs) ((u16)(sizeof(struct hinic_qp_ctxt_header) \ + (num_rqs) * sizeof(struct hinic_rq_ctxt))) #define SQ_CTXT_CEQ_ATTR_CEQ_ID_SHIFT 8 #define SQ_CTXT_CEQ_ATTR_GLOBAL_SQ_ID_SHIFT 13 #define SQ_CTXT_CEQ_ATTR_EN_SHIFT 23 #define SQ_CTXT_CEQ_ATTR_ARM_SHIFT 31 #define SQ_CTXT_CEQ_ATTR_CEQ_ID_MASK 0x1FU #define SQ_CTXT_CEQ_ATTR_GLOBAL_SQ_ID_MASK 0x3FFU #define SQ_CTXT_CEQ_ATTR_EN_MASK 0x1U #define SQ_CTXT_CEQ_ATTR_ARM_MASK 0x1U #define SQ_CTXT_CEQ_ATTR_SET(val, member) \ (((val) & SQ_CTXT_CEQ_ATTR_##member##_MASK) \ << SQ_CTXT_CEQ_ATTR_##member##_SHIFT) #define SQ_CTXT_CI_IDX_SHIFT 11 #define SQ_CTXT_CI_OWNER_SHIFT 23 #define SQ_CTXT_CI_IDX_MASK 0xFFFU #define SQ_CTXT_CI_OWNER_MASK 0x1U #define SQ_CTXT_CI_SET(val, member) \ (((val) & SQ_CTXT_CI_##member##_MASK) \ << SQ_CTXT_CI_##member##_SHIFT) #define SQ_CTXT_WQ_PAGE_HI_PFN_SHIFT 0 #define SQ_CTXT_WQ_PAGE_PI_SHIFT 20 #define SQ_CTXT_WQ_PAGE_HI_PFN_MASK 0xFFFFFU #define SQ_CTXT_WQ_PAGE_PI_MASK 0xFFFU #define SQ_CTXT_WQ_PAGE_SET(val, member) \ (((val) & SQ_CTXT_WQ_PAGE_##member##_MASK) \ << SQ_CTXT_WQ_PAGE_##member##_SHIFT) #define SQ_CTXT_PREF_CACHE_THRESHOLD_SHIFT 0 #define SQ_CTXT_PREF_CACHE_MAX_SHIFT 14 #define SQ_CTXT_PREF_CACHE_MIN_SHIFT 25 #define SQ_CTXT_PREF_CACHE_THRESHOLD_MASK 0x3FFFU #define SQ_CTXT_PREF_CACHE_MAX_MASK 0x7FFU #define SQ_CTXT_PREF_CACHE_MIN_MASK 0x7FU #define SQ_CTXT_PREF_WQ_PFN_HI_SHIFT 0 #define SQ_CTXT_PREF_CI_SHIFT 20 #define SQ_CTXT_PREF_WQ_PFN_HI_MASK 0xFFFFFU #define SQ_CTXT_PREF_CI_MASK 0xFFFU #define SQ_CTXT_PREF_SET(val, member) \ (((val) & SQ_CTXT_PREF_##member##_MASK) \ << SQ_CTXT_PREF_##member##_SHIFT) #define SQ_CTXT_WQ_BLOCK_PFN_HI_SHIFT 0 #define SQ_CTXT_WQ_BLOCK_PFN_HI_MASK 0x7FFFFFU #define SQ_CTXT_WQ_BLOCK_SET(val, member) \ (((val) & SQ_CTXT_WQ_BLOCK_##member##_MASK) \ << SQ_CTXT_WQ_BLOCK_##member##_SHIFT) #define RQ_CTXT_CEQ_ATTR_EN_SHIFT 0 #define RQ_CTXT_CEQ_ATTR_OWNER_SHIFT 1 #define RQ_CTXT_CEQ_ATTR_EN_MASK 0x1U #define RQ_CTXT_CEQ_ATTR_OWNER_MASK 0x1U #define RQ_CTXT_CEQ_ATTR_SET(val, member) \ (((val) & RQ_CTXT_CEQ_ATTR_##member##_MASK) \ << RQ_CTXT_CEQ_ATTR_##member##_SHIFT) #define RQ_CTXT_PI_IDX_SHIFT 0 #define RQ_CTXT_PI_INTR_SHIFT 22 #define RQ_CTXT_PI_CEQ_ARM_SHIFT 31 #define RQ_CTXT_PI_IDX_MASK 0xFFFU #define RQ_CTXT_PI_INTR_MASK 0x3FFU #define RQ_CTXT_PI_CEQ_ARM_MASK 0x1U #define RQ_CTXT_PI_SET(val, member) \ (((val) & RQ_CTXT_PI_##member##_MASK) << \ RQ_CTXT_PI_##member##_SHIFT) #define RQ_CTXT_WQ_PAGE_HI_PFN_SHIFT 0 #define RQ_CTXT_WQ_PAGE_CI_SHIFT 20 #define RQ_CTXT_WQ_PAGE_HI_PFN_MASK 0xFFFFFU #define RQ_CTXT_WQ_PAGE_CI_MASK 0xFFFU #define RQ_CTXT_WQ_PAGE_SET(val, member) \ (((val) & RQ_CTXT_WQ_PAGE_##member##_MASK) << \ RQ_CTXT_WQ_PAGE_##member##_SHIFT) #define RQ_CTXT_PREF_CACHE_THRESHOLD_SHIFT 0 #define RQ_CTXT_PREF_CACHE_MAX_SHIFT 14 #define RQ_CTXT_PREF_CACHE_MIN_SHIFT 25 #define RQ_CTXT_PREF_CACHE_THRESHOLD_MASK 0x3FFFU #define RQ_CTXT_PREF_CACHE_MAX_MASK 0x7FFU #define RQ_CTXT_PREF_CACHE_MIN_MASK 0x7FU #define RQ_CTXT_PREF_WQ_PFN_HI_SHIFT 0 #define RQ_CTXT_PREF_CI_SHIFT 20 #define RQ_CTXT_PREF_WQ_PFN_HI_MASK 0xFFFFFU #define RQ_CTXT_PREF_CI_MASK 0xFFFU #define RQ_CTXT_PREF_SET(val, member) \ (((val) & RQ_CTXT_PREF_##member##_MASK) << \ RQ_CTXT_PREF_##member##_SHIFT) #define RQ_CTXT_WQ_BLOCK_PFN_HI_SHIFT 0 #define RQ_CTXT_WQ_BLOCK_PFN_HI_MASK 0x7FFFFFU #define RQ_CTXT_WQ_BLOCK_SET(val, member) \ (((val) & RQ_CTXT_WQ_BLOCK_##member##_MASK) << \ RQ_CTXT_WQ_BLOCK_##member##_SHIFT) #define SIZE_16BYTES(size) (ALIGN((size), 16) >> 4) #define WQ_PAGE_PFN_SHIFT 12 #define WQ_BLOCK_PFN_SHIFT 9 #define WQ_PAGE_PFN(page_addr) ((page_addr) >> WQ_PAGE_PFN_SHIFT) #define WQ_BLOCK_PFN(page_addr) ((page_addr) >> WQ_BLOCK_PFN_SHIFT) enum sq_cflag { CFLAG_DATA_PATH = 0, }; enum hinic_qp_ctxt_type { HINIC_QP_CTXT_TYPE_SQ, HINIC_QP_CTXT_TYPE_RQ, }; /* service type relates define */ enum cfg_svc_type_en { CFG_SVC_NIC_BIT0 = (1 << 0), CFG_SVC_ROCE_BIT1 = (1 << 1), CFG_SVC_FCOE_BIT2 = (1 << 2), CFG_SVC_TOE_BIT3 = (1 << 3), CFG_SVC_IWARP_BIT4 = (1 << 4), CFG_SVC_FC_BIT5 = (1 << 5), CFG_SVC_FIC_BIT6 = (1 << 6), CFG_SVC_OVS_BIT7 = (1 << 7), CFG_SVC_ACL_BIT8 = (1 << 8), CFG_SVC_IOE_BIT9 = (1 << 9), CFG_SVC_HWPT_BIT10 = (1 << 10), CFG_SVC_FT_EN = (CFG_SVC_FCOE_BIT2 | CFG_SVC_TOE_BIT3 | CFG_SVC_FC_BIT5 | CFG_SVC_IOE_BIT9), CFG_SVC_RDMA_EN = (CFG_SVC_ROCE_BIT1 | CFG_SVC_IWARP_BIT4) }; #define IS_NIC_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_NIC_BIT0) #define IS_ROCE_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_ROCE_BIT1) #define IS_FCOE_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_FCOE_BIT2) #define IS_TOE_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_TOE_BIT3) #define IS_IWARP_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_IWARP_BIT4) #define IS_FC_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_FC_BIT5) #define IS_FIC_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_FIC_BIT6) #define IS_OVS_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_OVS_BIT7) #define IS_ACL_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_ACL_BIT8) #define IS_IOE_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_IOE_BIT9) #define IS_FT_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_FT_EN) #define IS_RDMA_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_RDMA_EN) #define IS_HWPT_TYPE(dev) \ ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_HWPT_BIT10) #endif /* __HINIC_CTX_DEF_H__ */ hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_dbg.h000066400000000000000000000056411502027332500212140ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_NIC_DBG_H_ #define HINIC_NIC_DBG_H_ u16 hinic_dbg_get_qp_num(void *hwdev); void *hinic_dbg_get_qp_handle(void *hwdev, u16 q_id); void *hinic_dbg_get_sq_wq_handle(void *hwdev, u16 q_id); void *hinic_dbg_get_rq_wq_handle(void *hwdev, u16 q_id); u16 hinic_dbg_get_sq_pi(void *hwdev, u16 q_id); u16 hinic_dbg_get_rq_hw_pi(void *hwdev, u16 q_id); u16 hinic_dbg_get_rq_sw_pi(void *hwdev, u16 q_id); void *hinic_dbg_get_sq_ci_addr(void *hwdev, u16 q_id); u64 hinic_dbg_get_sq_cla_addr(void *hwdev, u16 q_id); u64 hinic_dbg_get_rq_cla_addr(void *hwdev, u16 q_id); int hinic_dbg_get_sq_db_addr(void *hwdev, u16 q_id, u64 **map_addr, u64 *phy_addr, u32 *pg_idx); u16 hinic_dbg_get_global_qpn(const void *hwdev); int hinic_dbg_get_sq_wqe_info(void *hwdev, u16 q_id, u16 idx, u16 wqebb_cnt, u8 *wqe, u16 *wqe_size); int hinic_dbg_get_rq_wqe_info(void *hwdev, u16 q_id, u16 idx, u16 wqebb_cnt, u8 *wqe, u16 *wqe_size); int hinic_dbg_lt_rd_16byte(void *hwdev, u8 dest, u8 instance, u32 lt_index, u8 *data); int hinic_dbg_lt_wr_16byte_mask(void *hwdev, u8 dest, u8 instance, u32 lt_index, u8 *data, u16 mask); int hinic_sm_ctr_rd32(void *hwdev, u8 node, u8 instance, u32 ctr_id, u32 *value); int hinic_sm_ctr_rd32_clear(void *hwdev, u8 node, u8 instance, u32 ctr_id, u32 *value); int hinic_sm_ctr_wr32(void *hwdev, u8 node, u8 instance, u32 ctr_id, u32 value); int hinic_sm_ctr_rd64(void *hwdev, u8 node, u8 instance, u32 ctr_id, u64 *value); int hinic_sm_ctr_wr64(void *hwdev, u8 node, u8 instance, u32 ctr_id, u64 value); int hinic_sm_ctr_rd64_pair(void *hwdev, u8 node, u8 instance, u32 ctr_id, u64 *value1, u64 *value2); int hinic_sm_ctr_wr64_pair(void *hwdev, u8 node, u8 instance, u32 ctr_id, u64 value1, u64 value2); int hinic_api_csr_rd32(void *hwdev, u8 dest, u32 addr, u32 *val); int hinic_api_csr_wr32(void *hwdev, u8 dest, u32 addr, u32 val); int hinic_api_csr_rd64(void *hwdev, u8 dest, u32 addr, u64 *val); int hinic_api_csr_wr64(void *hwdev, u8 dest, u32 addr, u64 val); int hinic_dbg_get_hw_stats(const void *hwdev, u8 *hw_stats, u16 *out_size); u16 hinic_dbg_clear_hw_stats(void *hwdev, u32 *out_size); void hinic_get_chip_fault_stats(const void *hwdev, u8 *chip_fault_stats, int offset); int hinic_dbg_get_pf_bw_limit(void *hwdev, u32 *pf_bw_limit); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_dbgtool_knl.c000066400000000000000000000541721502027332500227540ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hwdev.h" #include "hinic_hw_mgmt.h" #include "hinic_nic_dev.h" #include "hinic_lld.h" #include "hinic_dbgtool_knl.h" struct ffm_intr_info { u8 node_id; /* error level of the interrupt source */ u8 err_level; /* Classification by interrupt source properties */ u16 err_type; u32 err_csr_addr; u32 err_csr_value; }; #define DBGTOOL_MSG_MAX_SIZE 2048ULL #define HINIC_SELF_CMD_UP2PF_FFM 0x26 void *g_hinic_card_node_array[MAX_CARD_NUM] = {0}; void *g_hinic_card_vir_addr[MAX_CARD_NUM] = {0}; u64 g_hinic_card_phy_addr[MAX_CARD_NUM] = {0}; /* lock for g_hinic_card_vir_addr */ struct mutex g_hinic_addr_lock; int g_hinic_card_id; /* dbgtool character device name, class name, dev path */ #define CHR_DEV_DBGTOOL "dbgtool_chr_dev" #define CLASS_DBGTOOL "dbgtool_class" #define DBGTOOL_DEV_PATH "/dev/dbgtool_chr_dev" struct dbgtool_k_glb_info { struct semaphore dbgtool_sem; struct ffm_record_info *ffm; }; static dev_t dbgtool_dev_id; /* device id */ static struct cdev dbgtool_chr_dev; /* struct of char device */ /*lint -save -e104 -e808*/ static struct class *dbgtool_d_class; /* struct of char class */ /*lint -restore*/ static int g_dbgtool_init_flag; static int g_dbgtool_ref_cnt; static int dbgtool_knl_open(struct inode *pnode, struct file *pfile) { return 0; } static int dbgtool_knl_release(struct inode *pnode, struct file *pfile) { return 0; } static ssize_t dbgtool_knl_read(struct file *pfile, char __user *ubuf, size_t size, loff_t *ppos) { return 0; } static ssize_t dbgtool_knl_write(struct file *pfile, const char __user *ubuf, size_t size, loff_t *ppos) { return 0; } static bool is_valid_phy_addr(u64 offset) { int i; for (i = 0; i < MAX_CARD_NUM; i++) { if (offset == g_hinic_card_phy_addr[i]) return true; } return false; } int hinic_mem_mmap(struct file *filp, struct vm_area_struct *vma) { unsigned long vmsize = vma->vm_end - vma->vm_start; phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; phys_addr_t phy_addr; if (vmsize > (PAGE_SIZE * (1 << DBGTOOL_PAGE_ORDER))) { pr_err("Map size = %lu is bigger than alloc\n", vmsize); return -EAGAIN; } if (offset && !is_valid_phy_addr((u64)offset) && !hinic_is_valid_bar_addr((u64)offset)) { pr_err("offset is invalid"); return -EAGAIN; } /* old version of tool set vma->vm_pgoff to 0 */ phy_addr = offset ? offset : g_hinic_card_phy_addr[g_hinic_card_id]; if (!phy_addr) { pr_err("Card_id = %d physical address is 0\n", g_hinic_card_id); return -EAGAIN; } if (remap_pfn_range(vma, vma->vm_start, (phy_addr >> PAGE_SHIFT), vmsize, vma->vm_page_prot)) return -EAGAIN; return 0; } /** * dbgtool_knl_api_cmd_read - used for read operations * @para: the dbgtool parameter * @g_func_handle_array: global function handle * Return: 0 - success, negative - failure */ static long dbgtool_knl_api_cmd_read(struct dbgtool_param *para, void **g_func_handle_array) { long ret = 0; u8 *cmd; u16 size; void *ack; u16 ack_size; u32 pf_id; void *hwdev; pf_id = para->param.api_rd.pf_id; if (pf_id >= 16) { pr_err("PF id(0x%x) too big\n", pf_id); return -EFAULT; } /* obtaining pf_id chipif pointer */ hwdev = g_func_handle_array[pf_id]; if (!hwdev) { pr_err("PF id(0x%x) handle null in api cmd read\n", pf_id); return -EFAULT; } /* alloc cmd and ack memory */ size = para->param.api_rd.size; if (para->param.api_rd.size == 0 || size > DBGTOOL_MSG_MAX_SIZE) { pr_err("Read cmd size invalid or more than 2M\n"); return -EINVAL; } cmd = kzalloc((unsigned long long)size, GFP_KERNEL); if (!cmd) return -ENOMEM; ack_size = para->param.api_rd.ack_size; if (para->param.api_rd.ack_size == 0) { pr_err("Read cmd ack size is 0\n"); ret = -ENOMEM; goto alloc_ack_mem_fail; } ack = kzalloc((unsigned long long)ack_size, GFP_KERNEL); if (!ack) { ret = -ENOMEM; goto alloc_ack_mem_fail; } /* cmd content copied from user-mode */ if (copy_from_user(cmd, para->param.api_rd.cmd, (unsigned long)size)) { pr_err("Copy cmd from user fail\n"); ret = -EFAULT; goto copy_user_cmd_fail; } /* Invoke the api cmd interface read content*/ ret = hinic_api_cmd_read_ack(hwdev, para->param.api_rd.dest, cmd, size, ack, ack_size); if (ret) { pr_err("Api send single cmd ack fail!\n"); goto api_rd_fail; } /* Copy the contents of the ack to the user state */ if (copy_to_user(para->param.api_rd.ack, ack, ack_size)) { pr_err("Copy ack to user fail\n"); ret = -EFAULT; } api_rd_fail: copy_user_cmd_fail: kfree(ack); alloc_ack_mem_fail: kfree(cmd); return ret; } /** * dbgtool_knl_api_cmd_write - used for write operations * @para: the dbgtool parameter * @g_func_handle_array: global function handle * Return: 0 - success, negative - failure */ static long dbgtool_knl_api_cmd_write(struct dbgtool_param *para, void **g_func_handle_array) { long ret = 0; u8 *cmd; u16 size; u32 pf_id; void *hwdev; pf_id = para->param.api_wr.pf_id; if (pf_id >= 16) { pr_err("PF id(0x%x) too big\n", pf_id); return -EFAULT; } /* obtaining chipif pointer according to pf_id */ hwdev = g_func_handle_array[pf_id]; if (!hwdev) { pr_err("PF id(0x%x) handle null\n", pf_id); return -EFAULT; } /* alloc cmd memory */ size = para->param.api_wr.size; if (para->param.api_wr.size == 0 || size > DBGTOOL_MSG_MAX_SIZE) { pr_err("Write cmd size invalid or more than 2M\n"); return -EINVAL; } cmd = kzalloc((unsigned long long)size, GFP_KERNEL); if (!cmd) return -ENOMEM; /* cmd content copied from user-mode */ if (copy_from_user(cmd, para->param.api_wr.cmd, (unsigned long)size)) { pr_err("Copy cmd from user fail\n"); ret = -EFAULT; goto copy_user_cmd_fail; } /* api cmd interface is invoked to write the content */ ret = hinic_api_cmd_write_nack(hwdev, para->param.api_wr.dest, cmd, size); if (ret) pr_err("Api send single cmd nack fail\n"); copy_user_cmd_fail: kfree(cmd); return ret; } void hinic_chipif_get_all_pf_dev_info(struct pf_dev_info *dev_info, int card_idx, void **g_func_handle_array) { u32 func_idx; struct hinic_hwdev *hwdev; if (!dev_info) { pr_err("Params error!\n"); return; } /* pf at most 16 */ for (func_idx = 0; func_idx < 16; func_idx++) { hwdev = (struct hinic_hwdev *)g_func_handle_array[func_idx]; dev_info[func_idx].phy_addr = g_hinic_card_phy_addr[card_idx]; if (!hwdev) { dev_info[func_idx].bar0_size = 0; dev_info[func_idx].bus = 0; dev_info[func_idx].slot = 0; dev_info[func_idx].func = 0; } else { dev_info[func_idx].bar0_size = pci_resource_len (((struct pci_dev *)hwdev->pcidev_hdl), 0); dev_info[func_idx].bus = ((struct pci_dev *) hwdev->pcidev_hdl)->bus->number; dev_info[func_idx].slot = PCI_SLOT(((struct pci_dev *)hwdev->pcidev_hdl) ->devfn); dev_info[func_idx].func = PCI_FUNC(((struct pci_dev *)hwdev->pcidev_hdl) ->devfn); } } } /** * dbgtool_knl_pf_dev_info_get - Obtain the pf sdk_info * @para: the dbgtool parameter * @g_func_handle_array: global function handle * Return: 0 - success, negative - failure */ static long dbgtool_knl_pf_dev_info_get(struct dbgtool_param *para, void **g_func_handle_array) { struct pf_dev_info dev_info[16] = { {0} }; unsigned char *tmp; int i; mutex_lock(&g_hinic_addr_lock); if (!g_hinic_card_vir_addr[g_hinic_card_id]) { g_hinic_card_vir_addr[g_hinic_card_id] = (void *)__get_free_pages(GFP_KERNEL, DBGTOOL_PAGE_ORDER); if (!g_hinic_card_vir_addr[g_hinic_card_id]) { pr_err("Alloc dbgtool api chain fail!\n"); mutex_unlock(&g_hinic_addr_lock); return -EFAULT; } memset(g_hinic_card_vir_addr[g_hinic_card_id], 0, PAGE_SIZE * (1 << DBGTOOL_PAGE_ORDER)); g_hinic_card_phy_addr[g_hinic_card_id] = virt_to_phys(g_hinic_card_vir_addr[g_hinic_card_id]); if (!g_hinic_card_phy_addr[g_hinic_card_id]) { pr_err("phy addr for card %d is 0\n", g_hinic_card_id); free_pages((unsigned long)g_hinic_card_vir_addr[g_hinic_card_id], DBGTOOL_PAGE_ORDER); g_hinic_card_vir_addr[g_hinic_card_id] = NULL; mutex_unlock(&g_hinic_addr_lock); return -EFAULT; } tmp = g_hinic_card_vir_addr[g_hinic_card_id]; for (i = 0; i < (1 << DBGTOOL_PAGE_ORDER); i++) { SetPageReserved(virt_to_page(tmp)); tmp += PAGE_SIZE; } } mutex_unlock(&g_hinic_addr_lock); hinic_chipif_get_all_pf_dev_info(dev_info, g_hinic_card_id, g_func_handle_array); /* Copy the dev_info to user mode */ if (copy_to_user(para->param.dev_info, dev_info, (unsigned int)sizeof(dev_info))) { pr_err("Copy dev_info to user fail\n"); return -EFAULT; } return 0; } /** * dbgtool_knl_ffm_info_rd - Read ffm information * @para: the dbgtool parameter * @dbgtool_info: the dbgtool info * Return: 0 - success, negative - failure */ static long dbgtool_knl_ffm_info_rd(struct dbgtool_param *para, struct dbgtool_k_glb_info *dbgtool_info) { /* Copy the ffm_info to user mode */ if (copy_to_user(para->param.ffm_rd, dbgtool_info->ffm, (unsigned int)sizeof(struct ffm_record_info))) { pr_err("Copy ffm_info to user fail\n"); return -EFAULT; } return 0; } /** * dbgtool_knl_ffm_info_clr - Clear FFM information * @para: unused * @dbgtool_info: the dbgtool info */ static void dbgtool_knl_ffm_info_clr(struct dbgtool_param *para, struct dbgtool_k_glb_info *dbgtool_info) { dbgtool_info->ffm->ffm_num = 0; } /** * dbgtool_knl_msg_to_up - After receiving dbgtool command sends a message to uP * @para: the dbgtool parameter * @g_func_handle_array: global function handle * Return: 0 - success, negative - failure */ static long dbgtool_knl_msg_to_up(struct dbgtool_param *para, void **g_func_handle_array) { long ret = 0; void *buf_in; void *buf_out; u16 out_size; u8 pf_id; if (para->param.msg2up.in_size > DBGTOOL_MSG_MAX_SIZE) { pr_err("User data(%d) more than 2KB\n", para->param.msg2up.in_size); return -EFAULT; } pf_id = para->param.msg2up.pf_id; /* pf at most 16 */ if (pf_id >= 16) { pr_err("PF id(0x%x) too big in message to mgmt\n", pf_id); return -EFAULT; } if (!g_func_handle_array[pf_id]) { pr_err("PF id(0x%x) handle null in message to mgmt\n", pf_id); return -EFAULT; } /* alloc buf_in and buf_out memory, apply for 2K */ buf_in = kzalloc(DBGTOOL_MSG_MAX_SIZE, GFP_KERNEL); if (!buf_in) return -ENOMEM; buf_out = kzalloc(DBGTOOL_MSG_MAX_SIZE, 0); if (!buf_out) { ret = -ENOMEM; goto alloc_buf_out_mem_fail; } /* copy buf_in from the user state */ if (copy_from_user(buf_in, para->param.msg2up.buf_in, (unsigned long)para->param.msg2up.in_size)) { pr_err("Copy buf_in from user fail\n"); ret = -EFAULT; goto copy_user_buf_in_fail; } out_size = DBGTOOL_MSG_MAX_SIZE; /* Invoke the pf2up communication interface */ ret = hinic_msg_to_mgmt_sync(g_func_handle_array[pf_id], para->param.msg2up.mod, para->param.msg2up.cmd, buf_in, para->param.msg2up.in_size, buf_out, &out_size, 0); if (ret) goto msg_2_up_fail; /* Copy the out_size and buf_out content to user mode */ if (copy_to_user(para->param.msg2up.out_size, &out_size, (unsigned int)sizeof(out_size))) { pr_err("Copy out_size to user fail\n"); ret = -EFAULT; goto copy_out_size_fail; } if (copy_to_user(para->param.msg2up.buf_out, buf_out, out_size)) { pr_err("Copy buf_out to user fail\n"); ret = -EFAULT; } copy_out_size_fail: msg_2_up_fail: copy_user_buf_in_fail: kfree(buf_out); alloc_buf_out_mem_fail: kfree(buf_in); return ret; } long hinic_dbgtool_knl_free_mem(int id) { unsigned char *tmp; int i; mutex_lock(&g_hinic_addr_lock); if (!g_hinic_card_vir_addr[id]) { mutex_unlock(&g_hinic_addr_lock); return 0; } tmp = g_hinic_card_vir_addr[id]; for (i = 0; i < (1 << DBGTOOL_PAGE_ORDER); i++) { ClearPageReserved(virt_to_page(tmp)); tmp += PAGE_SIZE; } free_pages((unsigned long)g_hinic_card_vir_addr[id], DBGTOOL_PAGE_ORDER); g_hinic_card_vir_addr[id] = NULL; g_hinic_card_phy_addr[id] = 0; mutex_unlock(&g_hinic_addr_lock); return 0; } /*lint -save -e771 -e794*/ /** * dbgtool_knl_unlocked_ioctl - dbgtool ioctl entry * @pfile: the pointer to file * @cmd: the command type */ static long dbgtool_knl_unlocked_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) { long ret = 0; unsigned int real_cmd; struct dbgtool_param param; struct dbgtool_k_glb_info *dbgtool_info; struct card_node *card_info = NULL; int i; (void)memset(¶m, 0, sizeof(param)); if (copy_from_user(¶m, (void *)arg, sizeof(param))) { pr_err("Copy param from user fail\n"); return -EFAULT; } param.chip_name[IFNAMSIZ - 1] = '\0'; for (i = 0; i < MAX_CARD_NUM; i++) { card_info = (struct card_node *)g_hinic_card_node_array[i]; if (!card_info) continue; if (!strncmp(param.chip_name, card_info->chip_name, IFNAMSIZ)) break; } if (i == MAX_CARD_NUM || !card_info) { pr_err("Can't find this card %s\n", param.chip_name); return -EFAULT; } g_hinic_card_id = i; dbgtool_info = (struct dbgtool_k_glb_info *)card_info->dbgtool_info; down(&dbgtool_info->dbgtool_sem); real_cmd = _IOC_NR(cmd); switch (real_cmd) { case DBGTOOL_CMD_API_RD: ret = dbgtool_knl_api_cmd_read(¶m, card_info->func_handle_array); break; case DBGTOOL_CMD_API_WR: ret = dbgtool_knl_api_cmd_write(¶m, card_info->func_handle_array); break; case DBGTOOL_CMD_FFM_RD: ret = dbgtool_knl_ffm_info_rd(¶m, dbgtool_info); break; case DBGTOOL_CMD_FFM_CLR: dbgtool_knl_ffm_info_clr(¶m, dbgtool_info); break; case DBGTOOL_CMD_PF_DEV_INFO_GET: ret = dbgtool_knl_pf_dev_info_get(¶m, card_info->func_handle_array); break; case DBGTOOL_CMD_MSG_2_UP: ret = dbgtool_knl_msg_to_up(¶m, card_info->func_handle_array); break; case DBGTOOL_CMD_FREE_MEM: ret = hinic_dbgtool_knl_free_mem(i); break; default: pr_err("Dbgtool cmd(x%x) not support now\n", real_cmd); ret = -EFAULT; } up(&dbgtool_info->dbgtool_sem); return ret; } /** * ffm_intr_msg_record - FFM interruption records sent up * @handle: the function handle * @buf_in: the pointer to input buffer * @buf_out: the pointer to outputput buffer */ static void ffm_intr_msg_record(void *handle, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct dbgtool_k_glb_info *dbgtool_info; struct ffm_intr_info *intr; u32 ffm_idx; struct tm tm; struct card_node *card_info = NULL; struct hinic_hwdev *hwdev = handle; bool flag = false; int i, j; for (i = 0; i < MAX_CARD_NUM; i++) { card_info = (struct card_node *)g_hinic_card_node_array[i]; if (!card_info) continue; for (j = 0; j < MAX_FUNCTION_NUM; j++) { if (handle == card_info->func_handle_array[j]) { flag = true; break; } } if (flag) break; } if (i == MAX_CARD_NUM || !card_info) { pr_err("Id(%d) can't find this card\n", i); return; } dbgtool_info = (struct dbgtool_k_glb_info *)card_info->dbgtool_info; if (!dbgtool_info) { pr_err("Dbgtool info is null\n"); return; } intr = (struct ffm_intr_info *)buf_in; if (!dbgtool_info->ffm) return; ffm_idx = dbgtool_info->ffm->ffm_num; if (ffm_idx < FFM_RECORD_NUM_MAX) { nic_info(hwdev->dev_hdl, "%s: recv intr, ffm_idx: %d\n", __func__, ffm_idx); dbgtool_info->ffm->ffm[ffm_idx].node_id = intr->node_id; dbgtool_info->ffm->ffm[ffm_idx].err_level = intr->err_level; dbgtool_info->ffm->ffm[ffm_idx].err_type = intr->err_type; dbgtool_info->ffm->ffm[ffm_idx].err_csr_addr = intr->err_csr_addr; dbgtool_info->ffm->ffm[ffm_idx].err_csr_value = intr->err_csr_value; /* Calculate the time in date value to tm */ time64_to_tm(ktime_to_ms(ktime_get_real()) / MSEC_PER_SEC, 0, &tm); /* tm_year starts from 1900; 0->1900, 1->1901, and so on */ dbgtool_info->ffm->ffm[ffm_idx].year = (u16)(tm.tm_year + 1900); /* tm_mon starts from 0, 0 indicates January, and so on */ dbgtool_info->ffm->ffm[ffm_idx].mon = (u8)tm.tm_mon + 1; dbgtool_info->ffm->ffm[ffm_idx].mday = (u8)tm.tm_mday; dbgtool_info->ffm->ffm[ffm_idx].hour = (u8)tm.tm_hour; dbgtool_info->ffm->ffm[ffm_idx].min = (u8)tm.tm_min; dbgtool_info->ffm->ffm[ffm_idx].sec = (u8)tm.tm_sec; dbgtool_info->ffm->ffm_num++; } } /*lint -restore*/ /*lint -save -e785 -e438*/ static const struct file_operations dbgtool_file_operations = { .owner = THIS_MODULE, .open = dbgtool_knl_open, .release = dbgtool_knl_release, .read = dbgtool_knl_read, .write = dbgtool_knl_write, .unlocked_ioctl = dbgtool_knl_unlocked_ioctl, .mmap = hinic_mem_mmap, }; /** * hinic_dbgtool_knl_init - dbgtool character device init * @hwdev: the pointer to hardware device * @chip_node: the pointer to card node * Return: 0 - success, negative - failure */ int hinic_dbgtool_knl_init(void *vhwdev, void *chip_node) { int ret = 0; int id; struct dbgtool_k_glb_info *dbgtool_info; struct device *pdevice; struct card_node *chip_info = (struct card_node *)chip_node; struct hinic_hwdev *hwdev = vhwdev; if (hinic_func_type(hwdev) == TYPE_VF) return 0; ret = sysfs_create_file(&((struct device *)(hwdev->dev_hdl))->kobj, &chip_info->dbgtool_attr_file); if (ret) { pr_err("Failed to sysfs create file\n"); return ret; } chip_info->func_handle_array[hinic_global_func_id(hwdev)] = hwdev; hinic_comm_recv_mgmt_self_cmd_reg(hwdev, HINIC_SELF_CMD_UP2PF_FFM, ffm_intr_msg_record); if (chip_info->dbgtool_info) { chip_info->func_num++; return 0; } dbgtool_info = (struct dbgtool_k_glb_info *) kzalloc(sizeof(struct dbgtool_k_glb_info), GFP_KERNEL); if (!dbgtool_info) { pr_err("Failed to allocate dbgtool_info\n"); ret = -EFAULT; goto dbgtool_info_fail; } chip_info->dbgtool_info = dbgtool_info; /* FFM init */ dbgtool_info->ffm = (struct ffm_record_info *) kzalloc(sizeof(struct ffm_record_info), GFP_KERNEL); if (!dbgtool_info->ffm) { pr_err("Failed to allocate cell contexts for a chain\n"); ret = -EFAULT; goto dbgtool_info_ffm_fail; } sema_init(&dbgtool_info->dbgtool_sem, 1); ret = sscanf(chip_info->chip_name, HINIC_CHIP_NAME "%d", &id); if (ret <= 0) { pr_err("Failed to get hinic id\n"); goto sscanf_chdev_fail; } g_hinic_card_node_array[id] = chip_info; chip_info->func_num++; if (g_dbgtool_init_flag) { g_dbgtool_ref_cnt++; /* already initialized */ return 0; } /*alloc device id*/ ret = alloc_chrdev_region(&(dbgtool_dev_id), 0, 1, CHR_DEV_DBGTOOL); if (ret) { pr_err("Alloc dbgtool chrdev region fail, ret=0x%x\n", ret); goto alloc_chdev_fail; } /*init device*/ cdev_init(&(dbgtool_chr_dev), &dbgtool_file_operations); /*add device*/ ret = cdev_add(&(dbgtool_chr_dev), dbgtool_dev_id, 1); if (ret) { pr_err("Add dgbtool dev fail, ret=0x%x\n", ret); goto cdev_add_fail; } /*lint -save -e160*/ dbgtool_d_class = class_create(CLASS_DBGTOOL); /*lint -restore*/ if (IS_ERR(dbgtool_d_class)) { pr_err("Create dgbtool class fail\n"); ret = -EFAULT; goto cls_create_fail; } /* Export device information to user space * (/sys/class/class name/device name) */ pdevice = device_create(dbgtool_d_class, NULL, dbgtool_dev_id, NULL, CHR_DEV_DBGTOOL); if (IS_ERR(pdevice)) { pr_err("Create dgbtool device fail\n"); ret = -EFAULT; goto dev_create_fail; } g_dbgtool_init_flag = 1; g_dbgtool_ref_cnt = 1; mutex_init(&g_hinic_addr_lock); return 0; dev_create_fail: class_destroy(dbgtool_d_class); cls_create_fail: cdev_del(&(dbgtool_chr_dev)); cdev_add_fail: unregister_chrdev_region(dbgtool_dev_id, 1); alloc_chdev_fail: g_hinic_card_node_array[id] = NULL; sscanf_chdev_fail: kfree(dbgtool_info->ffm); dbgtool_info_ffm_fail: kfree(dbgtool_info); dbgtool_info = NULL; chip_info->dbgtool_info = NULL; dbgtool_info_fail: hinic_comm_recv_up_self_cmd_unreg(hwdev, HINIC_SELF_CMD_UP2PF_FFM); chip_info->func_handle_array[hinic_global_func_id(hwdev)] = NULL; sysfs_remove_file(&((struct device *)(hwdev->dev_hdl))->kobj, &chip_info->dbgtool_attr_file); return ret; } /** * hinic_dbgtool_knl_deinit - dbgtool character device deinit * @hwdev: the pointer to hardware device * @chip_node: the pointer to card node */ void hinic_dbgtool_knl_deinit(void *vhwdev, void *chip_node) { struct dbgtool_k_glb_info *dbgtool_info; struct card_node *chip_info = (struct card_node *)chip_node; int id; int err; struct hinic_hwdev *hwdev = vhwdev; if (hinic_func_type(hwdev) == TYPE_VF) return; hinic_comm_recv_up_self_cmd_unreg(hwdev, HINIC_SELF_CMD_UP2PF_FFM); chip_info->func_handle_array[hinic_global_func_id(hwdev)] = NULL; sysfs_remove_file(&((struct device *)(hwdev->dev_hdl))->kobj, &chip_info->dbgtool_attr_file); chip_info->func_num--; if (chip_info->func_num) return; err = sscanf(chip_info->chip_name, HINIC_CHIP_NAME "%d", &id); if (err <= 0) pr_err("Failed to get hinic id\n"); g_hinic_card_node_array[id] = NULL; dbgtool_info = chip_info->dbgtool_info; /* FFM deinit */ kfree(dbgtool_info->ffm); dbgtool_info->ffm = NULL; kfree(dbgtool_info); chip_info->dbgtool_info = NULL; (void)hinic_dbgtool_knl_free_mem(id); if (g_dbgtool_init_flag) { if ((--g_dbgtool_ref_cnt)) return; } if (!dbgtool_d_class) return; device_destroy(dbgtool_d_class, dbgtool_dev_id); class_destroy(dbgtool_d_class); dbgtool_d_class = NULL; cdev_del(&(dbgtool_chr_dev)); unregister_chrdev_region(dbgtool_dev_id, 1); g_dbgtool_init_flag = 0; } /*lint -restore*/ hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_dbgtool_knl.h000066400000000000000000000050261502027332500227530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef __DBGTOOL_KNL_H__ #define __DBGTOOL_KNL_H__ #define DBG_TOOL_MAGIC 'w' /* dbgtool command type */ /* You can add the required dbgtool through these commands * can invoke all X86 kernel mode driver interface */ enum dbgtool_cmd { DBGTOOL_CMD_API_RD = 0, DBGTOOL_CMD_API_WR, DBGTOOL_CMD_FFM_RD, DBGTOOL_CMD_FFM_CLR, DBGTOOL_CMD_PF_DEV_INFO_GET, DBGTOOL_CMD_MSG_2_UP, DBGTOOL_CMD_FREE_MEM, DBGTOOL_CMD_NUM }; struct api_cmd_rd { u32 pf_id; u8 dest; u8 *cmd; u16 size; void *ack; u16 ack_size; }; struct api_cmd_wr { u32 pf_id; u8 dest; u8 *cmd; u16 size; }; struct pf_dev_info { u64 bar0_size; u8 bus; u8 slot; u8 func; u64 phy_addr; }; /* Interrupt at most records, interrupt will be recorded in the FFM */ #define FFM_RECORD_NUM_MAX 64 struct ffm_intr_tm_info { u8 node_id; /* error level of the interrupt source */ u8 err_level; /* Classification by interrupt source properties */ u16 err_type; u32 err_csr_addr; u32 err_csr_value; u8 sec; /* second*/ u8 min; /* minute */ u8 hour; /* hour */ u8 mday; /* day */ u8 mon; /* month */ u16 year; /* year */ }; struct ffm_record_info { u32 ffm_num; struct ffm_intr_tm_info ffm[FFM_RECORD_NUM_MAX]; }; struct msg_2_up { u8 pf_id; /* which pf sends messages to the up */ u8 mod; u8 cmd; void *buf_in; u16 in_size; void *buf_out; u16 *out_size; }; struct dbgtool_param { union { struct api_cmd_rd api_rd; struct api_cmd_wr api_wr; struct pf_dev_info *dev_info; struct ffm_record_info *ffm_rd; struct msg_2_up msg2up; } param; char chip_name[16]; }; #define MAX_CARD_NUM 64 #define DBGTOOL_PAGE_ORDER 10 int hinic_dbgtool_knl_init(void *vhwdev, void *chip_node); void hinic_dbgtool_knl_deinit(void *vhwdev, void *chip_node); int hinic_mem_mmap(struct file *filp, struct vm_area_struct *vma); void hinic_chipif_get_all_pf_dev_info(struct pf_dev_info *dev_info, int card_id, void **g_func_handle_array); long hinic_dbgtool_knl_free_mem(int id); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_dcb.c000066400000000000000000001310151502027332500211760ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_lld.h" #include "hinic_nic_cfg.h" #include "hinic_nic_dev.h" #include "hinic_dcb.h" #define DCB_HW_CFG_CHG 0 #define DCB_HW_CFG_NO_CHG 1 #define DCB_HW_CFG_ERR 2 #define DCB_CFG_CHG_PG_TX 0x1 #define DCB_CFG_CHG_PG_RX 0x2 #define DCB_CFG_CHG_PFC 0x4 #define DCB_CFG_CHG_UP_COS 0x8 u8 hinic_dcb_get_tc(struct hinic_dcb_config *dcb_cfg, int dir, u8 up) { struct hinic_tc_cfg *tc_cfg = &dcb_cfg->tc_cfg[0]; u8 tc = dcb_cfg->pg_tcs; if (!tc) return 0; for (tc--; tc; tc--) { if (BIT(up) & tc_cfg[tc].path[dir].up_map) break; } return tc; } #define UP_MAPPING(prio) ((u8)(1U << ((HINIC_DCB_UP_MAX - 1) - (prio)))) void hinic_dcb_config_init(struct hinic_nic_dev *nic_dev, struct hinic_dcb_config *dcb_cfg) { struct hinic_tc_cfg *tc; int i; memset(dcb_cfg->tc_cfg, 0, sizeof(dcb_cfg->tc_cfg)); tc = &dcb_cfg->tc_cfg[0]; /* All TC mapping to PG0 */ for (i = 0; i < dcb_cfg->pg_tcs; i++) { tc = &dcb_cfg->tc_cfg[i]; tc->path[HINIC_DCB_CFG_TX].pg_id = 0; tc->path[HINIC_DCB_CFG_TX].bw_pct = 100; tc->path[HINIC_DCB_CFG_TX].up_map = UP_MAPPING(i); tc->path[HINIC_DCB_CFG_RX].pg_id = 0; tc->path[HINIC_DCB_CFG_RX].bw_pct = 100; tc->path[HINIC_DCB_CFG_RX].up_map = UP_MAPPING(i); tc->pfc_en = false; } for (; i < HINIC_DCB_UP_MAX; i++) { tc->path[HINIC_DCB_CFG_TX].up_map |= UP_MAPPING(i); tc->path[HINIC_DCB_CFG_RX].up_map |= UP_MAPPING(i); } memset(dcb_cfg->bw_pct, 0, sizeof(dcb_cfg->bw_pct)); /* Use PG0 in default, PG0's bw is 100% */ dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][0] = 100; dcb_cfg->bw_pct[HINIC_DCB_CFG_RX][0] = 100; dcb_cfg->pfc_state = false; } void hinic_init_ieee_settings(struct hinic_nic_dev *nic_dev) { struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg; struct ieee_ets *ets = &nic_dev->hinic_ieee_ets_default; struct ieee_pfc *pfc = &nic_dev->hinic_ieee_pfc; struct hinic_tc_attr *tc_attr; u8 i; memset(ets, 0x0, sizeof(struct ieee_ets)); memset(&nic_dev->hinic_ieee_ets, 0x0, sizeof(struct ieee_ets)); ets->ets_cap = dcb_cfg->pg_tcs; for (i = 0; i < HINIC_DCB_TC_MAX; i++) { tc_attr = &dcb_cfg->tc_cfg[i].path[HINIC_DCB_CFG_TX]; ets->tc_tsa[i] = tc_attr->prio_type ? IEEE8021Q_TSA_STRICT : IEEE8021Q_TSA_ETS; ets->tc_tx_bw[i] = nic_dev->dcb_cfg.bw_pct[HINIC_DCB_CFG_TX][i]; ets->tc_rx_bw[i] = nic_dev->dcb_cfg.bw_pct[HINIC_DCB_CFG_RX][i]; ets->prio_tc[i] = hinic_dcb_get_tc(dcb_cfg, HINIC_DCB_CFG_TX, i); } memcpy(&nic_dev->hinic_ieee_ets, ets, sizeof(struct ieee_ets)); memset(pfc, 0x0, sizeof(struct ieee_pfc)); pfc->pfc_cap = dcb_cfg->pfc_tcs; for (i = 0; i < dcb_cfg->pfc_tcs; i++) { if (dcb_cfg->tc_cfg[i].pfc_en) pfc->pfc_en |= (u8)BIT(i); } } static int hinic_set_up_cos_map(struct hinic_nic_dev *nic_dev, u8 num_cos, u8 *cos_up) { u8 up_valid_bitmap, up_cos[HINIC_DCB_UP_MAX] = {0}; u8 i; up_valid_bitmap = 0; for (i = 0; i < num_cos; i++) { if (cos_up[i] >= HINIC_DCB_UP_MAX) { hinic_info(nic_dev, drv, "Invalid up %d mapping to cos %d\n", cos_up[i], i); return -EFAULT; } if (i > 0 && cos_up[i] >= cos_up[i - 1]) { hinic_info(nic_dev, drv, "Invalid priority order, should be descending cos[%d]=%d, cos[%d]=%d\n", i, cos_up[i], i - 1, cos_up[i - 1]); return -EINVAL; } up_valid_bitmap |= (u8)BIT(cos_up[i]); if (i == (num_cos - 1)) up_cos[cos_up[i]] = nic_dev->default_cos_id; else up_cos[cos_up[i]] = i; /* reverse up and cos */ } for (i = 0; i < HINIC_DCB_UP_MAX; i++) { if (up_valid_bitmap & (u8)BIT(i)) continue; up_cos[i] = nic_dev->default_cos_id; } nic_dev->up_valid_bitmap = up_valid_bitmap; memcpy(nic_dev->up_cos, up_cos, sizeof(up_cos)); return hinic_sq_cos_mapping(nic_dev->netdev); } static int hinic_init_up_cos_map(struct hinic_nic_dev *nic_dev, u8 num_cos) { u8 default_map[HINIC_DCB_COS_MAX] = {0}; bool setted = false; u8 max_cos, cos_id, up; int err; max_cos = hinic_max_num_cos(nic_dev->hwdev); if (!max_cos || ((max_cos - 1) < nic_dev->default_cos_id)) { hinic_err(nic_dev, drv, "Max_cos is %d, default cos id %d\n", max_cos, nic_dev->default_cos_id); return -EFAULT; } err = hinic_get_chip_cos_up_map(nic_dev->pdev, &setted, default_map); if (err) { hinic_err(nic_dev, drv, "Get chip cos_up map failed\n"); return -EFAULT; } if (!setted) { /* Use (max_cos-1)~0 as default user priority and mapping * to cos0~(max_cos-1) */ up = nic_dev->max_cos - 1; for (cos_id = 0; cos_id < nic_dev->max_cos; cos_id++, up--) default_map[cos_id] = up; } return hinic_set_up_cos_map(nic_dev, num_cos, default_map); } int hinic_dcb_init(struct hinic_nic_dev *nic_dev) { struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg; u8 num_cos, support_cos = 0, default_cos = 0; u8 i, cos_valid_bitmap; int err; if (HINIC_FUNC_IS_VF(nic_dev->hwdev)) return 0; cos_valid_bitmap = hinic_cos_valid_bitmap(nic_dev->hwdev); if (!cos_valid_bitmap) { hinic_err(nic_dev, drv, "None cos supported\n"); return -EFAULT; } for (i = 0; i < HINIC_DCB_COS_MAX; i++) { if (cos_valid_bitmap & BIT(i)) { support_cos++; default_cos = i; /* Find max cos id as default cos */ } } hinic_info(nic_dev, drv, "Support num cos %d, default cos %d\n", support_cos, default_cos); num_cos = (u8)(1U << ilog2(support_cos)); if (num_cos != support_cos) hinic_info(nic_dev, drv, "Adjust num_cos from %d to %d\n", support_cos, num_cos); nic_dev->dcbx_cap = 0; nic_dev->max_cos = num_cos; nic_dev->default_cos_id = default_cos; dcb_cfg->pfc_tcs = nic_dev->max_cos; dcb_cfg->pg_tcs = nic_dev->max_cos; err = hinic_init_up_cos_map(nic_dev, num_cos); if (err) { hinic_info(nic_dev, drv, "Initialize up_cos mapping failed\n"); return -EFAULT; } hinic_dcb_config_init(nic_dev, dcb_cfg); nic_dev->dcb_changes = DCB_CFG_CHG_PFC | DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX | DCB_CFG_CHG_UP_COS; nic_dev->dcbx_cap = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_CEE; memcpy(&nic_dev->tmp_dcb_cfg, &nic_dev->dcb_cfg, sizeof(nic_dev->tmp_dcb_cfg)); memcpy(&nic_dev->save_dcb_cfg, &nic_dev->dcb_cfg, sizeof(nic_dev->save_dcb_cfg)); hinic_init_ieee_settings(nic_dev); sema_init(&nic_dev->dcb_sem, 1); return 0; } void hinic_set_prio_tc_map(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; u8 prio, tc; for (prio = 0; prio < HINIC_DCB_UP_MAX; prio++) { tc = nic_dev->up_cos[prio]; if (tc == nic_dev->default_cos_id) tc = nic_dev->max_cos - 1; netdev_set_prio_tc_map(netdev, prio, tc); } } int hinic_setup_tc(struct net_device *netdev, u8 tc) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err; if (!FUNC_SUPPORT_DCB(nic_dev->hwdev)) { nicif_err(nic_dev, drv, netdev, "Current function don't support DCB\n"); return -EOPNOTSUPP; } if (tc > nic_dev->dcb_cfg.pg_tcs) { nicif_err(nic_dev, drv, netdev, "Invalid num_tc: %d, max tc: %d\n", tc, nic_dev->dcb_cfg.pg_tcs); return -EINVAL; } if (netif_running(netdev)) { err = hinic_close(netdev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to close device\n"); return -EFAULT; } } if (tc) { if (tc & (tc - 1)) { nicif_err(nic_dev, drv, netdev, "Invalid num_tc: %d, must be power of 2\n", tc); return -EINVAL; } netdev_set_num_tc(netdev, tc); hinic_set_prio_tc_map(nic_dev); set_bit(HINIC_DCB_ENABLE, &nic_dev->flags); } else { netdev_reset_tc(netdev); clear_bit(HINIC_DCB_ENABLE, &nic_dev->flags); } hinic_sq_cos_mapping(netdev); if (netif_running(netdev)) { err = hinic_open(netdev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to open device\n"); return -EFAULT; } } else { hinic_update_num_qps(netdev); } hinic_configure_dcb(netdev); return 0; } u8 hinic_setup_dcb_tool(struct net_device *netdev, u8 *dcb_en, bool wr_flag) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err = 0; if (wr_flag) { if (nic_dev->max_qps < nic_dev->dcb_cfg.pg_tcs && *dcb_en) { nicif_err(nic_dev, drv, netdev, "max_qps: %d is less than %d\n", nic_dev->max_qps, nic_dev->dcb_cfg.pg_tcs); return 1; } if (*dcb_en) set_bit(HINIC_DCB_ENABLE, &nic_dev->flags); else clear_bit(HINIC_DCB_ENABLE, &nic_dev->flags); /*hinic_setup_tc need get the nic_mutex lock again */ mutex_unlock(&nic_dev->nic_mutex); /* kill the rtnl assert warning */ rtnl_lock(); err = hinic_setup_tc(netdev, *dcb_en ? nic_dev->dcb_cfg.pg_tcs : 0); rtnl_unlock(); mutex_lock(&nic_dev->nic_mutex); if (!err) nicif_info(nic_dev, drv, netdev, "%s DCB\n", *dcb_en ? "Enable" : "Disable"); } else { *dcb_en = (u8)test_bit(HINIC_DCB_ENABLE, &nic_dev->flags); } return !!err; } static u8 hinic_dcbnl_get_state(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); return !!test_bit(HINIC_DCB_ENABLE, &nic_dev->flags); } static u8 hinic_dcbnl_set_state(struct net_device *netdev, u8 state) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u8 curr_state = !!test_bit(HINIC_DCB_ENABLE, &nic_dev->flags); int err = 0; if (!(nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_CEE)) return 1; if (state == curr_state) return 0; if (nic_dev->max_qps < nic_dev->dcb_cfg.pg_tcs && state) { nicif_err(nic_dev, drv, netdev, "max_qps: %d is less than %d\n", nic_dev->max_qps, nic_dev->dcb_cfg.pg_tcs); return 1; } err = hinic_setup_tc(netdev, state ? nic_dev->dcb_cfg.pg_tcs : 0); if (!err) nicif_info(nic_dev, drv, netdev, "%s DCB\n", state ? "Enable" : "Disable"); return !!err; } static void hinic_dcbnl_get_perm_hw_addr(struct net_device *netdev, u8 *perm_addr) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err; memset(perm_addr, 0xff, MAX_ADDR_LEN); err = hinic_get_default_mac(nic_dev->hwdev, perm_addr); if (err) nicif_err(nic_dev, drv, netdev, "Failed to get default mac\n"); } void hinic_dcbnl_set_ets_tc_tool(struct net_device *netdev, u8 tc[], bool flag) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_tc_cfg *cfg = nic_dev->tmp_dcb_cfg.tc_cfg; struct hinic_tc_cfg *tc_conf = nic_dev->dcb_cfg.tc_cfg; u8 i, tc_tmp, j; if (flag) { /*need to clear first */ for (i = 0; i < HINIC_DCB_TC_MAX; i++) { cfg[i].path[HINIC_DCB_CFG_TX].up_map = 0; cfg[i].path[HINIC_DCB_CFG_RX].up_map = 0; } for (i = 0; i < HINIC_DCB_TC_MAX; i++) { tc_tmp = tc[i]; cfg[tc_tmp].path[HINIC_DCB_CFG_TX].up_map |= (u8)BIT(i); cfg[tc_tmp].path[HINIC_DCB_CFG_RX].up_map |= (u8)BIT(i); cfg[tc_tmp].path[HINIC_DCB_CFG_TX].pg_id = (u8)tc_tmp; cfg[tc_tmp].path[HINIC_DCB_CFG_RX].pg_id = (u8)tc_tmp; } } else { for (i = 0; i < HINIC_DCB_TC_MAX; i++) { for (j = 0; j < HINIC_DCB_TC_MAX; j++) { if (tc_conf[i].path[HINIC_DCB_CFG_TX].up_map & (u8)BIT(j)) { tc[j] = i; } } } } } void hinic_dcbnl_set_ets_pecent_tool(struct net_device *netdev, u8 percent[], bool flag) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int i; if (flag) { for (i = 0; i < HINIC_DCB_COS_MAX; i++) { nic_dev->tmp_dcb_cfg.bw_pct[HINIC_DCB_CFG_TX][i] = percent[i]; nic_dev->tmp_dcb_cfg.bw_pct[HINIC_DCB_CFG_RX][i] = percent[i]; } } else { for (i = 0; i < HINIC_DCB_COS_MAX; i++) percent[i] = nic_dev->dcb_cfg.bw_pct[HINIC_DCB_CFG_TX][i]; } } static void hinic_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc, u8 prio, u8 pg_id, u8 bw_pct, u8 up_map) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (tc > HINIC_DCB_TC_MAX - 1) return; if (prio != DCB_ATTR_VALUE_UNDEFINED) nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[0].prio_type = prio; if (pg_id != DCB_ATTR_VALUE_UNDEFINED) nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[0].pg_id = pg_id; if (bw_pct != DCB_ATTR_VALUE_UNDEFINED) nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[0].bw_pct = bw_pct; /* if all priority mapping to the same tc, * up_map is 0xFF, and it's a valid value */ nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[0].up_map = up_map; } static void hinic_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id, u8 bw_pct) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (bwg_id > HINIC_DCB_PG_MAX - 1) return; nic_dev->tmp_dcb_cfg.bw_pct[0][bwg_id] = bw_pct; } static void hinic_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc, u8 prio, u8 pg_id, u8 bw_pct, u8 up_map) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (tc > HINIC_DCB_TC_MAX - 1) return; if (prio != DCB_ATTR_VALUE_UNDEFINED) nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[1].prio_type = prio; if (pg_id != DCB_ATTR_VALUE_UNDEFINED) nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[1].pg_id = pg_id; if (bw_pct != DCB_ATTR_VALUE_UNDEFINED) nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[1].bw_pct = bw_pct; nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[1].up_map = up_map; } static void hinic_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id, u8 bw_pct) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (bwg_id > HINIC_DCB_PG_MAX - 1) return; nic_dev->tmp_dcb_cfg.bw_pct[1][bwg_id] = bw_pct; } static void hinic_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int tc, u8 *prio, u8 *pg_id, u8 *bw_pct, u8 *up_map) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (tc > HINIC_DCB_TC_MAX - 1) return; *prio = nic_dev->dcb_cfg.tc_cfg[tc].path[0].prio_type; *pg_id = nic_dev->dcb_cfg.tc_cfg[tc].path[0].pg_id; *bw_pct = nic_dev->dcb_cfg.tc_cfg[tc].path[0].bw_pct; *up_map = nic_dev->dcb_cfg.tc_cfg[tc].path[0].up_map; } static void hinic_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id, u8 *bw_pct) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (bwg_id > HINIC_DCB_PG_MAX - 1) return; *bw_pct = nic_dev->dcb_cfg.bw_pct[0][bwg_id]; } static void hinic_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int tc, u8 *prio, u8 *pg_id, u8 *bw_pct, u8 *up_map) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (tc > HINIC_DCB_TC_MAX - 1) return; *prio = nic_dev->dcb_cfg.tc_cfg[tc].path[1].prio_type; *pg_id = nic_dev->dcb_cfg.tc_cfg[tc].path[1].pg_id; *bw_pct = nic_dev->dcb_cfg.tc_cfg[tc].path[1].bw_pct; *up_map = nic_dev->dcb_cfg.tc_cfg[tc].path[1].up_map; } static void hinic_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id, u8 *bw_pct) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (bwg_id > HINIC_DCB_PG_MAX - 1) return; *bw_pct = nic_dev->dcb_cfg.bw_pct[1][bwg_id]; } void hinic_dcbnl_set_pfc_cfg_tool(struct net_device *netdev, u8 setting) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u8 i; for (i = 0; i < HINIC_DCB_TC_MAX; i++) { nic_dev->tmp_dcb_cfg.tc_cfg[i].pfc_en = !!(setting & BIT(i)); if (nic_dev->tmp_dcb_cfg.tc_cfg[i].pfc_en != nic_dev->dcb_cfg.tc_cfg[i].pfc_en) { nic_dev->tmp_dcb_cfg.pfc_state = true; } } } void hinic_dcbnl_set_ets_strict_tool(struct net_device *netdev, u8 *setting, bool flag) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_tc_cfg *cfg = nic_dev->tmp_dcb_cfg.tc_cfg; struct hinic_tc_cfg *conf = nic_dev->dcb_cfg.tc_cfg; u8 i; if (flag) { for (i = 0; i < HINIC_DCB_COS_MAX; i++) { cfg[i].path[HINIC_DCB_CFG_TX].prio_type = !!(*setting & BIT(i)) ? 2 : 0; cfg[i].path[HINIC_DCB_CFG_RX].prio_type = !!(*setting & BIT(i)) ? 2 : 0; } } else { for (i = 0; i < HINIC_DCB_COS_MAX; i++) { *setting = *setting | (u8)((u32)(!!(conf[i].path[0].prio_type)) << i); } } } void hinic_dcbnl_set_pfc_en_tool(struct net_device *netdev, u8 *value, bool flag) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (flag) nic_dev->tmp_dcb_cfg.pfc_state = !!(*value); else *value = nic_dev->tmp_dcb_cfg.pfc_state; } void hinic_dcbnl_set_ets_en_tool(struct net_device *netdev, u8 *value, bool flag) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (flag) { if (*value) set_bit(HINIC_ETS_ENABLE, &nic_dev->flags); else clear_bit(HINIC_ETS_ENABLE, &nic_dev->flags); } else { *value = (u8)test_bit(HINIC_ETS_ENABLE, &nic_dev->flags); } } static void hinic_dcbnl_set_pfc_cfg(struct net_device *netdev, int prio, u8 setting) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); nic_dev->tmp_dcb_cfg.tc_cfg[prio].pfc_en = !!setting; if (nic_dev->tmp_dcb_cfg.tc_cfg[prio].pfc_en != nic_dev->dcb_cfg.tc_cfg[prio].pfc_en) nic_dev->tmp_dcb_cfg.pfc_state = true; } void hinic_dcbnl_get_pfc_cfg_tool(struct net_device *netdev, u8 *setting) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u8 i; for (i = 0; i < HINIC_DCB_TC_MAX; i++) { *setting = *setting | (u8)((u32)(nic_dev->dcb_cfg.tc_cfg[i].pfc_en) << i); } } void hinic_dcbnl_get_tc_num_tool(struct net_device *netdev, u8 *tc_num) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); *tc_num = nic_dev->max_cos; } static void hinic_dcbnl_get_pfc_cfg(struct net_device *netdev, int prio, u8 *setting) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (prio > HINIC_DCB_TC_MAX - 1) return; *setting = nic_dev->dcb_cfg.tc_cfg[prio].pfc_en; } static u8 hinic_dcbnl_getcap(struct net_device *netdev, int cap_id, u8 *dcb_cap) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); switch (cap_id) { case DCB_CAP_ATTR_PG: *dcb_cap = true; break; case DCB_CAP_ATTR_PFC: *dcb_cap = true; break; case DCB_CAP_ATTR_UP2TC: *dcb_cap = false; break; case DCB_CAP_ATTR_PG_TCS: *dcb_cap = 0x80; break; case DCB_CAP_ATTR_PFC_TCS: *dcb_cap = 0x80; break; case DCB_CAP_ATTR_GSP: *dcb_cap = true; break; case DCB_CAP_ATTR_BCN: *dcb_cap = false; break; case DCB_CAP_ATTR_DCBX: *dcb_cap = nic_dev->dcbx_cap; break; default: *dcb_cap = false; break; } return 0; } static u8 hinic_sync_tc_cfg(struct hinic_tc_cfg *tc_dst, struct hinic_tc_cfg *tc_src, int dir) { u8 tc_dir_change = (dir == HINIC_DCB_CFG_TX) ? DCB_CFG_CHG_PG_TX : DCB_CFG_CHG_PG_RX; u8 changes = 0; if (tc_dst->path[dir].prio_type != tc_src->path[dir].prio_type) { tc_dst->path[dir].prio_type = tc_src->path[dir].prio_type; changes |= tc_dir_change; } if (tc_dst->path[dir].pg_id != tc_src->path[dir].pg_id) { tc_dst->path[dir].pg_id = tc_src->path[dir].pg_id; changes |= tc_dir_change; } if (tc_dst->path[dir].bw_pct != tc_src->path[dir].bw_pct) { tc_dst->path[dir].bw_pct = tc_src->path[dir].bw_pct; changes |= tc_dir_change; } if (tc_dst->path[dir].up_map != tc_src->path[dir].up_map) { tc_dst->path[dir].up_map = tc_src->path[dir].up_map; changes |= (tc_dir_change | DCB_CFG_CHG_PFC); } return changes; } static u8 hinic_sync_dcb_cfg(struct hinic_nic_dev *nic_dev) { struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg; struct hinic_dcb_config *tmp_dcb_cfg = &nic_dev->tmp_dcb_cfg; struct hinic_tc_cfg *tc_dst, *tc_src; u8 changes = 0; int i; for (i = 0; i < HINIC_DCB_UP_MAX; i++) { tc_src = &tmp_dcb_cfg->tc_cfg[i]; tc_dst = &dcb_cfg->tc_cfg[i]; changes |= hinic_sync_tc_cfg(tc_dst, tc_src, HINIC_DCB_CFG_TX); changes |= hinic_sync_tc_cfg(tc_dst, tc_src, HINIC_DCB_CFG_RX); } for (i = 0; i < HINIC_DCB_PG_MAX; i++) { if (dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][i] != tmp_dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][i]) { dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][i] = tmp_dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][i]; changes |= DCB_CFG_CHG_PG_TX; } if (dcb_cfg->bw_pct[HINIC_DCB_CFG_RX][i] != tmp_dcb_cfg->bw_pct[HINIC_DCB_CFG_RX][i]) { dcb_cfg->bw_pct[HINIC_DCB_CFG_RX][i] = tmp_dcb_cfg->bw_pct[HINIC_DCB_CFG_RX][i]; changes |= DCB_CFG_CHG_PG_RX; } } for (i = 0; i < HINIC_DCB_UP_MAX; i++) { if (dcb_cfg->tc_cfg[i].pfc_en != tmp_dcb_cfg->tc_cfg[i].pfc_en) { dcb_cfg->tc_cfg[i].pfc_en = tmp_dcb_cfg->tc_cfg[i].pfc_en; changes |= DCB_CFG_CHG_PFC; } } if (dcb_cfg->pfc_state != tmp_dcb_cfg->pfc_state) { dcb_cfg->pfc_state = tmp_dcb_cfg->pfc_state; changes |= DCB_CFG_CHG_PFC; } return changes; } static void hinic_dcb_get_pfc_map(struct hinic_nic_dev *nic_dev, struct hinic_dcb_config *dcb_cfg, u8 *pfc_map) { u8 i, up; u8 pfc_en = 0, outof_range_pfc = 0; for (i = 0; i < dcb_cfg->pfc_tcs; i++) { up = (HINIC_DCB_UP_MAX - 1) - i; if (dcb_cfg->tc_cfg[up].pfc_en) *pfc_map |= (u8)BIT(up); } for (i = 0; i < HINIC_DCB_UP_MAX; i++) { up = (HINIC_DCB_UP_MAX - 1) - i; if (dcb_cfg->tc_cfg[up].pfc_en) pfc_en |= (u8)BIT(up); } *pfc_map = pfc_en & nic_dev->up_valid_bitmap; outof_range_pfc = pfc_en & (~nic_dev->up_valid_bitmap); if (outof_range_pfc) hinic_info(nic_dev, drv, "PFC setting out of range, 0x%x will be ignored\n", outof_range_pfc); } static bool is_cos_in_use(u8 cos, u8 up_valid_bitmap, u8 *up_cos) { u32 i; for (i = 0; i < HINIC_DCB_UP_MAX; i++) { if (!(up_valid_bitmap & BIT(i))) continue; if (cos == up_cos[i]) return true; } return false; } static void hinic_dcb_adjust_up_bw(struct hinic_nic_dev *nic_dev, u8 *up_pgid, u8 *up_bw) { u8 tmp_cos, pg_id; u16 bw_all; u8 bw_remain, cos_cnt; for (pg_id = 0; pg_id < HINIC_DCB_PG_MAX; pg_id++) { bw_all = 0; cos_cnt = 0; /* Find all up mapping to the same pg */ for (tmp_cos = 0; tmp_cos < HINIC_DCB_UP_MAX; tmp_cos++) { if (!is_cos_in_use(tmp_cos, nic_dev->up_valid_bitmap, nic_dev->up_cos)) continue; if (up_pgid[tmp_cos] == pg_id) { bw_all += up_bw[tmp_cos]; cos_cnt++; } } if (bw_all <= 100 || !cos_cnt) continue; /* Calculate up percent of bandwidth group, The sum of * percentages for priorities in the same priority group * must be 100 */ bw_remain = 100 % cos_cnt; for (tmp_cos = 0; tmp_cos < HINIC_DCB_UP_MAX; tmp_cos++) { if (!is_cos_in_use(tmp_cos, nic_dev->up_valid_bitmap, nic_dev->up_cos)) continue; if (up_pgid[tmp_cos] == pg_id) { up_bw[tmp_cos] = (u8)(100 * up_bw[tmp_cos] / bw_all + (u8)!!bw_remain); if (bw_remain) bw_remain--; } } } } static void hinic_dcb_dump_configuration(struct hinic_nic_dev *nic_dev, u8 *up_tc, u8 *up_pgid, u8 *up_bw, u8 *pg_bw, u8 *up_strict, u8 *bw_pct) { u8 i; u8 cos; for (i = 0; i < HINIC_DCB_UP_MAX; i++) { if (!(nic_dev->up_valid_bitmap & BIT(i))) continue; cos = nic_dev->up_cos[i]; hinic_info(nic_dev, drv, "up: %d, cos: %d, tc: %d, pgid: %d, bw: %d, tsa: %d\n", i, cos, up_tc[cos], up_pgid[cos], up_bw[cos], up_strict[cos]); } for (i = 0; i < HINIC_DCB_PG_MAX; i++) hinic_info(nic_dev, drv, "pgid: %d, bw: %d\n", i, pg_bw[i]); } /* Ucode thread timeout is 210ms, must be lagger then 210ms */ #define HINIC_WAIT_PORT_IO_STOP 250 static int hinic_stop_port_traffic_flow(struct hinic_nic_dev *nic_dev) { int err = 0; down(&nic_dev->dcb_sem); if (nic_dev->disable_port_cnt++ != 0) goto out; err = hinic_force_port_disable(nic_dev); if (err) { hinic_err(nic_dev, drv, "Failed to disable port\n"); goto set_port_err; } err = hinic_set_port_funcs_state(nic_dev->hwdev, false); if (err) { hinic_err(nic_dev, drv, "Failed to disable all functions in port\n"); goto set_port_funcs_err; } hinic_info(nic_dev, drv, "Stop port traffic flow\n"); goto out; set_port_funcs_err: hinic_force_set_port_state(nic_dev, !!netif_running(nic_dev->netdev)); set_port_err: out: if (err) nic_dev->disable_port_cnt--; up(&nic_dev->dcb_sem); return err; } static int hinic_start_port_traffic_flow(struct hinic_nic_dev *nic_dev) { int err; down(&nic_dev->dcb_sem); nic_dev->disable_port_cnt--; if (nic_dev->disable_port_cnt > 0) { up(&nic_dev->dcb_sem); return 0; } nic_dev->disable_port_cnt = 0; up(&nic_dev->dcb_sem); err = hinic_force_set_port_state(nic_dev, !!netif_running(nic_dev->netdev)); if (err) hinic_err(nic_dev, drv, "Failed to disable port\n"); err = hinic_set_port_funcs_state(nic_dev->hwdev, true); if (err) hinic_err(nic_dev, drv, "Failed to disable all functions in port\n"); hinic_info(nic_dev, drv, "Start port traffic flow\n"); return err; } static int __set_hw_cos_up_map(struct hinic_nic_dev *nic_dev) { u8 cos, cos_valid_bitmap, cos_up_map[HINIC_DCB_COS_MAX] = {0}; u8 i; int err; cos_valid_bitmap = 0; for (i = 0; i < HINIC_DCB_UP_MAX; i++) { if (!(nic_dev->up_valid_bitmap & BIT(i))) continue; cos = nic_dev->up_cos[i]; cos_up_map[cos] = i; cos_valid_bitmap |= (u8)BIT(cos); } err = hinic_dcb_set_cos_up_map(nic_dev->hwdev, cos_valid_bitmap, cos_up_map); if (err) { hinic_info(nic_dev, drv, "Set cos_up map failed\n"); return err; } return 0; } static int __set_hw_ets(struct hinic_nic_dev *nic_dev) { struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg; struct ieee_ets *my_ets = &nic_dev->hinic_ieee_ets; struct hinic_tc_attr *tc_attr; u8 up_tc[HINIC_DCB_UP_MAX] = {0}; u8 up_pgid[HINIC_DCB_UP_MAX] = {0}; u8 up_bw[HINIC_DCB_UP_MAX] = {0}; u8 pg_bw[HINIC_DCB_UP_MAX] = {0}; u8 up_strict[HINIC_DCB_UP_MAX] = {0}; u8 i, tc, cos; int err; for (i = 0; i < HINIC_DCB_UP_MAX; i++) { if (!(nic_dev->up_valid_bitmap & BIT(i))) continue; cos = nic_dev->up_cos[i]; if ((nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) { up_tc[cos] = my_ets->prio_tc[i]; up_pgid[cos] = my_ets->prio_tc[i]; up_bw[cos] = 100; up_strict[i] = (my_ets->tc_tsa[cos] == IEEE8021Q_TSA_STRICT) ? HINIC_DCB_TSA_TC_SP : HINIC_DCB_TSA_TC_DWRR; } else { tc = hinic_dcb_get_tc(dcb_cfg, HINIC_DCB_CFG_TX, i); tc_attr = &dcb_cfg->tc_cfg[tc].path[HINIC_DCB_CFG_TX]; up_tc[cos] = tc; up_pgid[cos] = tc_attr->pg_id; up_bw[cos] = tc_attr->bw_pct; up_strict[cos] = tc_attr->prio_type ? HINIC_DCB_TSA_TC_SP : HINIC_DCB_TSA_TC_DWRR; } } hinic_dcb_adjust_up_bw(nic_dev, up_pgid, up_bw); if (nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) { for (i = 0; i < HINIC_DCB_PG_MAX; i++) pg_bw[i] = my_ets->tc_tx_bw[i]; } else { for (i = 0; i < HINIC_DCB_PG_MAX; i++) pg_bw[i] = dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][i]; } if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) hinic_dcb_dump_configuration(nic_dev, up_tc, up_pgid, up_bw, pg_bw, up_strict, pg_bw); err = hinic_dcb_set_ets(nic_dev->hwdev, up_tc, pg_bw, up_pgid, up_bw, up_strict); if (err) { hinic_err(nic_dev, drv, "Failed to set ets with mode: %d\n", nic_dev->dcbx_cap); return err; } hinic_info(nic_dev, drv, "Set ets to hw done with mode: %d\n", nic_dev->dcbx_cap); return 0; } u8 hinic_dcbnl_set_ets_tool(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u8 state = DCB_HW_CFG_CHG; int err; nic_dev->dcb_changes |= hinic_sync_dcb_cfg(nic_dev); if (!nic_dev->dcb_changes) return DCB_HW_CFG_CHG; err = hinic_stop_port_traffic_flow(nic_dev); if (err) return DCB_HW_CFG_ERR; /* wait all traffic flow stopped */ if (netdev->reg_state == NETREG_REGISTERED) msleep(HINIC_WAIT_PORT_IO_STOP); if (nic_dev->dcb_changes & DCB_CFG_CHG_UP_COS) { err = __set_hw_cos_up_map(nic_dev); if (err) { hinic_info(nic_dev, drv, "Set cos_up map to hardware failed\n"); state = DCB_HW_CFG_ERR; goto out; } nic_dev->dcb_changes &= (~DCB_CFG_CHG_UP_COS); } if (nic_dev->dcb_changes & (DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX)) { err = __set_hw_ets(nic_dev); if (err) { state = DCB_HW_CFG_ERR; goto out; } nic_dev->dcb_changes &= (~(DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX)); } out: hinic_start_port_traffic_flow(nic_dev); return state; } static int hinic_dcbnl_set_df_ieee_cfg(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct ieee_ets *ets_default = &nic_dev->hinic_ieee_ets_default; struct ieee_pfc *my_pfc = &nic_dev->hinic_ieee_pfc; struct ieee_ets *my_ets = &nic_dev->hinic_ieee_ets; struct ieee_pfc pfc = {0}; int err1 = 0; int err2 = 0; u8 flag = 0; if (!(nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) return 0; if (memcmp(my_ets, ets_default, sizeof(struct ieee_ets))) flag |= (u8)BIT(0); if (my_pfc->pfc_en) flag |= (u8)BIT(1); if (!flag) return 0; err1 = hinic_stop_port_traffic_flow(nic_dev); if (err1) return err1; if (netdev->reg_state == NETREG_REGISTERED) msleep(HINIC_WAIT_PORT_IO_STOP); if (flag & BIT(0)) { memcpy(my_ets, ets_default, sizeof(struct ieee_ets)); err1 = __set_hw_ets(nic_dev); } if (flag & BIT(1)) { my_pfc->pfc_en = 0; err2 = hinic_dcb_set_pfc(nic_dev->hwdev, false, pfc.pfc_en); if (err2) nicif_err(nic_dev, drv, netdev, "Failed to set pfc\n"); } hinic_start_port_traffic_flow(nic_dev); return (err1 || err2) ? -EINVAL : 0; } u8 hinic_dcbnl_set_pfc_tool(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg; u8 state = DCB_HW_CFG_CHG; int err; nic_dev->dcb_changes |= hinic_sync_dcb_cfg(nic_dev); if (!nic_dev->dcb_changes) return DCB_HW_CFG_CHG; if (nic_dev->dcb_changes & DCB_CFG_CHG_PFC) { u8 pfc_map = 0; hinic_dcb_get_pfc_map(nic_dev, dcb_cfg, &pfc_map); err = hinic_dcb_set_pfc(nic_dev->hwdev, dcb_cfg->pfc_state, pfc_map); if (err) { hinic_info(nic_dev, drv, "Failed to %s PFC\n", dcb_cfg->pfc_state ? "enable" : "disable"); state = DCB_HW_CFG_ERR; goto out; } if (dcb_cfg->pfc_state) hinic_info(nic_dev, drv, "Set PFC: 0x%x to hw done\n", pfc_map); else hinic_info(nic_dev, drv, "Disable PFC, enable tx/rx pause\n"); nic_dev->dcb_changes &= (~DCB_CFG_CHG_PFC); } out: return state; } u8 hinic_dcbnl_set_all(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg; u8 state = DCB_HW_CFG_CHG; int err; if (!(nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_CEE)) return DCB_HW_CFG_ERR; nic_dev->dcb_changes |= hinic_sync_dcb_cfg(nic_dev); if (!nic_dev->dcb_changes) return DCB_HW_CFG_NO_CHG; err = hinic_stop_port_traffic_flow(nic_dev); if (err) return DCB_HW_CFG_ERR; /* wait all traffic flow stopped */ if (netdev->reg_state == NETREG_REGISTERED) msleep(HINIC_WAIT_PORT_IO_STOP); if (nic_dev->dcb_changes & DCB_CFG_CHG_UP_COS) { err = __set_hw_cos_up_map(nic_dev); if (err) { hinic_info(nic_dev, drv, "Set cos_up map to hardware failed\n"); state = DCB_HW_CFG_ERR; goto out; } nic_dev->dcb_changes &= (~DCB_CFG_CHG_UP_COS); } if (nic_dev->dcb_changes & (DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX)) { err = __set_hw_ets(nic_dev); if (err) { state = DCB_HW_CFG_ERR; goto out; } nic_dev->dcb_changes &= (~(DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX)); } if (nic_dev->dcb_changes & DCB_CFG_CHG_PFC) { u8 pfc_map = 0; hinic_dcb_get_pfc_map(nic_dev, dcb_cfg, &pfc_map); err = hinic_dcb_set_pfc(nic_dev->hwdev, dcb_cfg->pfc_state, pfc_map); if (err) { hinic_info(nic_dev, drv, "Failed to %s PFC\n", dcb_cfg->pfc_state ? "enable" : "disable"); state = DCB_HW_CFG_ERR; goto out; } if (dcb_cfg->pfc_state) hinic_info(nic_dev, drv, "Set PFC: 0x%x to hw done\n", pfc_map); else hinic_info(nic_dev, drv, "Disable PFC, enable tx/rx pause\n"); nic_dev->dcb_changes &= (~DCB_CFG_CHG_PFC); } out: hinic_start_port_traffic_flow(nic_dev); return state; } static int hinic_dcbnl_ieee_get_ets(struct net_device *netdev, struct ieee_ets *ets) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct ieee_ets *my_ets = &nic_dev->hinic_ieee_ets; ets->ets_cap = my_ets->ets_cap; memcpy(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw)); memcpy(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw)); memcpy(ets->prio_tc, my_ets->prio_tc, sizeof(ets->prio_tc)); memcpy(ets->tc_tsa, my_ets->tc_tsa, sizeof(ets->tc_tsa)); return 0; } static int hinic_dcbnl_ieee_set_ets(struct net_device *netdev, struct ieee_ets *ets) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg; struct ieee_ets *my_ets = &nic_dev->hinic_ieee_ets; struct ieee_ets back_ets; int err, i; u8 max_tc = 0; u16 total_bw = 0; if (!(nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) return -EINVAL; if (!memcmp(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw)) && !memcmp(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw)) && !memcmp(ets->prio_tc, my_ets->prio_tc, sizeof(ets->prio_tc)) && !memcmp(ets->tc_tsa, my_ets->tc_tsa, sizeof(ets->tc_tsa))) return 0; for (i = 0; i < HINIC_DCB_TC_MAX; i++) total_bw += ets->tc_tx_bw[i]; if (!total_bw) return -EINVAL; for (i = 0; i < dcb_cfg->pg_tcs; i++) { if (ets->prio_tc[i] > max_tc) max_tc = ets->prio_tc[i]; } if (max_tc) max_tc++; if (max_tc > dcb_cfg->pg_tcs) return -EINVAL; max_tc = max_tc ? dcb_cfg->pg_tcs : 0; memcpy(&back_ets, my_ets, sizeof(struct ieee_ets)); memcpy(my_ets->tc_tx_bw, ets->tc_tx_bw, sizeof(ets->tc_tx_bw)); memcpy(my_ets->tc_rx_bw, ets->tc_rx_bw, sizeof(ets->tc_rx_bw)); memcpy(my_ets->prio_tc, ets->prio_tc, sizeof(ets->prio_tc)); memcpy(my_ets->tc_tsa, ets->tc_tsa, sizeof(ets->tc_tsa)); if (max_tc != netdev_get_num_tc(netdev)) { err = hinic_setup_tc(netdev, max_tc); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to setup tc with max_tc: %d, err: %d\n", max_tc, err); memcpy(my_ets, &back_ets, sizeof(struct ieee_ets)); return err; } } err = hinic_stop_port_traffic_flow(nic_dev); if (err) return err; if (netdev->reg_state == NETREG_REGISTERED) msleep(HINIC_WAIT_PORT_IO_STOP); err = __set_hw_ets(nic_dev); hinic_start_port_traffic_flow(nic_dev); return err; } static int hinic_dcbnl_ieee_get_pfc(struct net_device *netdev, struct ieee_pfc *pfc) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct ieee_pfc *my_pfc = &nic_dev->hinic_ieee_pfc; pfc->pfc_en = my_pfc->pfc_en; pfc->pfc_cap = my_pfc->pfc_cap; return 0; } static int hinic_dcbnl_ieee_set_pfc(struct net_device *netdev, struct ieee_pfc *pfc) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg; struct ieee_pfc *my_pfc = &nic_dev->hinic_ieee_pfc; struct ieee_ets *my_ets = &nic_dev->hinic_ieee_ets; int err, i; u8 pfc_map, max_tc; u8 outof_range_pfc = 0; bool pfc_en; if (!(nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) return -EINVAL; if (my_pfc->pfc_en == pfc->pfc_en) return 0; pfc_map = pfc->pfc_en & nic_dev->up_valid_bitmap; outof_range_pfc = pfc->pfc_en & (~nic_dev->up_valid_bitmap); if (outof_range_pfc) nicif_info(nic_dev, drv, netdev, "pfc setting out of range, 0x%x will be ignored\n", outof_range_pfc); err = hinic_stop_port_traffic_flow(nic_dev); if (err) return err; if (netdev->reg_state == NETREG_REGISTERED) msleep(HINIC_WAIT_PORT_IO_STOP); pfc_en = pfc_map ? true : false; max_tc = 0; for (i = 0; i < dcb_cfg->pg_tcs; i++) { if (my_ets->prio_tc[i] > max_tc) max_tc = my_ets->prio_tc[i]; } pfc_en = max_tc ? pfc_en : false; err = hinic_dcb_set_pfc(nic_dev->hwdev, pfc_en, pfc_map); if (err) { hinic_info(nic_dev, drv, "Failed to set pfc to hw with pfc_map: 0x%x err: %d\n", pfc_map, err); hinic_start_port_traffic_flow(nic_dev); return err; } hinic_start_port_traffic_flow(nic_dev); my_pfc->pfc_en = pfc->pfc_en; hinic_info(nic_dev, drv, "Set pfc successfully with pfc_map: 0x%x, pfc_en: %d\n", pfc_map, pfc_en); return 0; } static int hinic_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg; if (!test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) return -EINVAL; switch (tcid) { case DCB_NUMTCS_ATTR_PG: *num = dcb_cfg->pg_tcs; break; case DCB_NUMTCS_ATTR_PFC: *num = dcb_cfg->pfc_tcs; break; default: return -EINVAL; } return 0; } static int hinic_dcbnl_setnumtcs(struct net_device *netdev, int tcid, u8 num) { return -EINVAL; } static u8 hinic_dcbnl_getpfcstate(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); return (u8)nic_dev->dcb_cfg.pfc_state; } static void hinic_dcbnl_setpfcstate(struct net_device *netdev, u8 state) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); nic_dev->tmp_dcb_cfg.pfc_state = !!state; } static u8 hinic_dcbnl_getdcbx(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); return nic_dev->dcbx_cap; } static u8 hinic_dcbnl_setdcbx(struct net_device *netdev, u8 mode) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err; if (((mode & DCB_CAP_DCBX_VER_IEEE) && (mode & DCB_CAP_DCBX_VER_CEE)) || ((mode & DCB_CAP_DCBX_LLD_MANAGED) && (!(mode & DCB_CAP_DCBX_HOST)))) { nicif_info(nic_dev, drv, netdev, "Set dcbx failed with invalid mode: %d\n", mode); return 1; } if (nic_dev->dcbx_cap == mode) return 0; nic_dev->dcbx_cap = mode; if (mode & DCB_CAP_DCBX_VER_CEE) { u8 mask = DCB_CFG_CHG_PFC | DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX; nic_dev->dcb_changes |= mask; hinic_dcbnl_set_all(netdev); } else if (mode & DCB_CAP_DCBX_VER_IEEE) { if (netdev_get_num_tc(netdev)) { err = hinic_setup_tc(netdev, 0); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to setup tc with mode: %d\n", mode); return 1; } } hinic_dcbnl_set_df_ieee_cfg(netdev); hinic_force_port_relink(nic_dev->hwdev); } else { err = hinic_setup_tc(netdev, 0); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to setup tc with mode: %d\n", mode); return 1; } } nicif_info(nic_dev, drv, netdev, "Change dcbx mode to 0x%x\n", mode); return 0; } const struct dcbnl_rtnl_ops hinic_dcbnl_ops = { /* IEEE 802.1Qaz std */ .ieee_getets = hinic_dcbnl_ieee_get_ets, .ieee_setets = hinic_dcbnl_ieee_set_ets, .ieee_getpfc = hinic_dcbnl_ieee_get_pfc, .ieee_setpfc = hinic_dcbnl_ieee_set_pfc, /* CEE std */ .getstate = hinic_dcbnl_get_state, .setstate = hinic_dcbnl_set_state, .getpermhwaddr = hinic_dcbnl_get_perm_hw_addr, .setpgtccfgtx = hinic_dcbnl_set_pg_tc_cfg_tx, .setpgbwgcfgtx = hinic_dcbnl_set_pg_bwg_cfg_tx, .setpgtccfgrx = hinic_dcbnl_set_pg_tc_cfg_rx, .setpgbwgcfgrx = hinic_dcbnl_set_pg_bwg_cfg_rx, .getpgtccfgtx = hinic_dcbnl_get_pg_tc_cfg_tx, .getpgbwgcfgtx = hinic_dcbnl_get_pg_bwg_cfg_tx, .getpgtccfgrx = hinic_dcbnl_get_pg_tc_cfg_rx, .getpgbwgcfgrx = hinic_dcbnl_get_pg_bwg_cfg_rx, .setpfccfg = hinic_dcbnl_set_pfc_cfg, .getpfccfg = hinic_dcbnl_get_pfc_cfg, .setall = hinic_dcbnl_set_all, .getcap = hinic_dcbnl_getcap, .getnumtcs = hinic_dcbnl_getnumtcs, .setnumtcs = hinic_dcbnl_setnumtcs, .getpfcstate = hinic_dcbnl_getpfcstate, .setpfcstate = hinic_dcbnl_setpfcstate, /* DCBX configuration */ .getdcbx = hinic_dcbnl_getdcbx, .setdcbx = hinic_dcbnl_setdcbx, }; int hinic_dcb_reset_hw_config(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; u8 state; hinic_dcb_config_init(nic_dev, &nic_dev->tmp_dcb_cfg); state = hinic_dcbnl_set_all(netdev); if (state == DCB_HW_CFG_ERR) return -EFAULT; if (state == DCB_HW_CFG_CHG) hinic_info(nic_dev, drv, "Reset hardware DCB configuration done\n"); return 0; } void hinic_configure_dcb(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err; if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) { memcpy(&nic_dev->tmp_dcb_cfg, &nic_dev->save_dcb_cfg, sizeof(nic_dev->tmp_dcb_cfg)); hinic_dcbnl_set_all(netdev); } else { memcpy(&nic_dev->save_dcb_cfg, &nic_dev->tmp_dcb_cfg, sizeof(nic_dev->save_dcb_cfg)); err = hinic_dcb_reset_hw_config(nic_dev); if (err) nicif_warn(nic_dev, drv, netdev, "Failed to reset hw dcb configuration\n"); } } static bool __is_cos_up_map_change(struct hinic_nic_dev *nic_dev, u8 *cos_up) { u8 cos, up; for (cos = 0; cos < nic_dev->max_cos; cos++) { up = cos_up[cos]; if (BIT(up) != (nic_dev->up_valid_bitmap & BIT(up))) return true; } return false; } int __set_cos_up_map(struct hinic_nic_dev *nic_dev, u8 *cos_up) { struct net_device *netdev; u8 state; int err = 0; if (!nic_dev || !cos_up) return -EINVAL; netdev = nic_dev->netdev; if (test_and_set_bit(HINIC_DCB_UP_COS_SETTING, &nic_dev->dcb_flags)) { nicif_err(nic_dev, drv, netdev, "Cos_up map setting in inprocess, please try again later\n"); return -EFAULT; } nicif_info(nic_dev, drv, netdev, "Set cos2up: %d%d%d%d%d%d%d%d\n", cos_up[0], cos_up[1], cos_up[2], cos_up[3], cos_up[4], cos_up[5], cos_up[6], cos_up[7]); if (!__is_cos_up_map_change(nic_dev, cos_up)) { nicif_err(nic_dev, drv, netdev, "Same mapping, don't need to change anything\n"); err = 0; goto out; } err = hinic_set_up_cos_map(nic_dev, nic_dev->max_cos, cos_up); if (err) { err = -EFAULT; goto out; } nic_dev->dcb_changes = DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX | DCB_CFG_CHG_PFC | DCB_CFG_CHG_UP_COS; if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) { /* Change map in kernel */ hinic_set_prio_tc_map(nic_dev); state = hinic_dcbnl_set_all(netdev); if (state == DCB_HW_CFG_ERR) { nicif_err(nic_dev, drv, netdev, "Reconfig dcb to hw failed\n"); err = -EFAULT; } } out: clear_bit(HINIC_DCB_UP_COS_SETTING, &nic_dev->dcb_flags); return err; } int hinic_get_num_cos(struct hinic_nic_dev *nic_dev, u8 *num_cos) { if (!nic_dev || !num_cos) return -EINVAL; *num_cos = nic_dev->max_cos; return 0; } int hinic_get_cos_up_map(struct hinic_nic_dev *nic_dev, u8 *num_cos, u8 *cos_up) { u8 up, cos; if (!nic_dev || !cos_up) return -EINVAL; for (cos = 0; cos < HINIC_DCB_COS_MAX; cos++) { for (up = 0; up < HINIC_DCB_UP_MAX; up++) { if (!(nic_dev->up_valid_bitmap & BIT(up))) continue; if (nic_dev->up_cos[up] == cos || nic_dev->up_cos[up] == nic_dev->default_cos_id) cos_up[cos] = up; } } *num_cos = nic_dev->max_cos; return 0; } static int __stop_port_flow(void *uld_array[], u32 num_dev) { struct hinic_nic_dev *tmp_dev; u32 i, idx; int err; for (idx = 0; idx < num_dev; idx++) { tmp_dev = (struct hinic_nic_dev *)uld_array[idx]; err = hinic_stop_port_traffic_flow(tmp_dev); if (err) { nicif_err(tmp_dev, drv, tmp_dev->netdev, "Stop port traffic flow failed\n"); goto stop_port_err; } } /* wait all traffic flow stopped */ msleep(HINIC_WAIT_PORT_IO_STOP); return 0; stop_port_err: for (i = 0; i < idx; i++) { tmp_dev = (struct hinic_nic_dev *)uld_array[i]; hinic_start_port_traffic_flow(tmp_dev); } return err; } static void __start_port_flow(void *uld_array[], u32 num_dev) { struct hinic_nic_dev *tmp_dev; u32 idx; for (idx = 0; idx < num_dev; idx++) { tmp_dev = (struct hinic_nic_dev *)uld_array[idx]; hinic_start_port_traffic_flow(tmp_dev); } } /* for hinicadm tool, need to chang all port of the chip */ int hinic_set_cos_up_map(struct hinic_nic_dev *nic_dev, u8 *cos_up) { void *uld_array[HINIC_MAX_PF_NUM]; struct hinic_nic_dev *tmp_dev; u8 num_cos, old_cos_up[HINIC_DCB_COS_MAX] = {0}; u32 i, idx, num_dev = 0; int err, rollback_err; /* Save old map, in case of set failed */ err = hinic_get_cos_up_map(nic_dev, &num_cos, old_cos_up); if (err || !num_cos) { nicif_err(nic_dev, drv, nic_dev->netdev, "Get old cos_up map failed\n"); return -EFAULT; } if (!memcmp(cos_up, old_cos_up, sizeof(u8) * num_cos)) { nicif_info(nic_dev, drv, nic_dev->netdev, "Same cos2up map, don't need to change anything\n"); return 0; } /* Get all pf of this chip */ err = hinic_get_pf_uld_array(nic_dev->pdev, &num_dev, uld_array); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Get all pf private handle failed\n"); return -EFAULT; } err = __stop_port_flow(uld_array, num_dev); if (err) return -EFAULT; for (idx = 0; idx < num_dev; idx++) { tmp_dev = (struct hinic_nic_dev *)uld_array[idx]; err = __set_cos_up_map(tmp_dev, cos_up); if (err) { nicif_err(tmp_dev, drv, tmp_dev->netdev, "Set cos_up map to hw failed\n"); goto set_err; } } __start_port_flow(uld_array, num_dev); hinic_set_chip_cos_up_map(nic_dev->pdev, cos_up); return 0; set_err: /* undo all settings */ for (i = 0; i < idx; i++) { tmp_dev = (struct hinic_nic_dev *)uld_array[i]; rollback_err = __set_cos_up_map(tmp_dev, old_cos_up); if (rollback_err) nicif_err(tmp_dev, drv, tmp_dev->netdev, "Undo cos_up map to hw failed\n"); } __start_port_flow(uld_array, num_dev); return err; } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_dcb.h000066400000000000000000000045531502027332500212110ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_DCB_H_ #define HINIC_DCB_H_ #define HINIC_DCB_CFG_TX 0 #define HINIC_DCB_CFG_RX 1 /* IEEE8021QAZ Transmission selection algorithm identifiers */ #define IEEE8021Q_TSA_STRICT 0x0 #define IEEE8021Q_TSA_CBSHAPER 0x1 #define IEEE8021Q_TSA_ETS 0x2 #define IEEE8021Q_TSA_VENDOR 0xFF enum HINIC_DCB_FLAGS { HINIC_DCB_UP_COS_SETTING, HINIC_DCB_TRAFFIC_STOPPED, }; extern const struct dcbnl_rtnl_ops hinic_dcbnl_ops; u8 hinic_dcb_get_tc(struct hinic_dcb_config *dcb_cfg, int dir, u8 up); int hinic_dcb_init(struct hinic_nic_dev *nic_dev); int hinic_dcb_reset_hw_config(struct hinic_nic_dev *nic_dev); int hinic_setup_tc(struct net_device *netdev, u8 tc); void hinic_configure_dcb(struct net_device *netdev); int hinic_set_cos_up_map(struct hinic_nic_dev *nic_dev, u8 *cos_up); int hinic_get_num_cos(struct hinic_nic_dev *nic_dev, u8 *num_cos); int hinic_get_cos_up_map(struct hinic_nic_dev *nic_dev, u8 *num_cos, u8 *cos_up); u8 hinic_setup_dcb_tool(struct net_device *netdev, u8 *dcb_en, bool wr_flag); void hinic_dcbnl_set_pfc_en_tool(struct net_device *netdev, u8 *value, bool flag); void hinic_dcbnl_set_pfc_cfg_tool(struct net_device *netdev, u8 setting); void hinic_dcbnl_get_pfc_cfg_tool(struct net_device *netdev, u8 *setting); u8 hinic_dcbnl_set_pfc_tool(struct net_device *netdev); void hinic_dcbnl_get_tc_num_tool(struct net_device *netdev, u8 *tc_num); void hinic_dcbnl_set_ets_tc_tool(struct net_device *netdev, u8 tc[], bool flag); void hinic_dcbnl_set_ets_pecent_tool(struct net_device *netdev, u8 percent[], bool flag); void hinic_dcbnl_set_ets_en_tool(struct net_device *netdev, u8 *value, bool flag); void hinic_dcbnl_set_ets_strict_tool(struct net_device *netdev, u8 *setting, bool flag); u8 hinic_dcbnl_set_ets_tool(struct net_device *netdev); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_dfx_def.h000066400000000000000000000054521502027332500220570ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef __HINIC_DFX_DEF_H__ #define __HINIC_DFX_DEF_H__ enum module_name { SEND_TO_NIC_DRIVER = 1, SEND_TO_HW_DRIVER, SEND_TO_UCODE, SEND_TO_UP, SEND_TO_SM, HINICADM_OVS_DRIVER = 6, HINICADM_ROCE_DRIVER, HINICADM_TOE_DRIVER, HINICADM_IWAP_DRIVER, HINICADM_FC_DRIVER, HINICADM_FCOE_DRIVER, }; enum driver_cmd_type { TX_INFO = 1, Q_NUM, TX_WQE_INFO, TX_MAPPING, RX_INFO, RX_WQE_INFO, RX_CQE_INFO, UPRINT_FUNC_EN, UPRINT_FUNC_RESET, UPRINT_SET_PATH, UPRINT_GET_STATISTICS, FUNC_TYPE, GET_FUNC_IDX, GET_INTER_NUM, CLOSE_TX_STREAM, GET_DRV_VERSION, CLEAR_FUNC_STASTIC, GET_HW_STATS, CLEAR_HW_STATS, GET_SELF_TEST_RES, GET_CHIP_FAULT_STATS, GET_NUM_COS, SET_COS_UP_MAP, GET_COS_UP_MAP, GET_CHIP_ID, GET_SINGLE_CARD_INFO, GET_FIRMWARE_ACTIVE_STATUS, ROCE_DFX_FUNC, GET_DEVICE_ID, GET_PF_DEV_INFO, CMD_FREE_MEM, GET_LOOPBACK_MODE = 32, SET_LOOPBACK_MODE, SET_LINK_MODE, SET_PF_BW_LIMIT, GET_PF_BW_LIMIT, ROCE_CMD, GET_POLL_WEIGHT, SET_POLL_WEIGHT, GET_HOMOLOGUE, SET_HOMOLOGUE, GET_SSET_COUNT, GET_SSET_ITEMS, IS_DRV_IN_VM, LRO_ADPT_MGMT, SET_INTER_COAL_PARAM, GET_INTER_COAL_PARAM, GET_CHIP_INFO, GET_NIC_STATS_LEN, GET_NIC_STATS_STRING, GET_NIC_STATS_INFO, GET_PF_ID, SET_DCB_CFG, SET_PFC_PRIORITY, GET_PFC_INFO, SET_PFC_CONTROL, SET_ETS, GET_ETS_INFO, GET_SUPPORT_UP, GET_SUPPORT_TC, RSS_CFG = 0x40, RSS_INDIR, PORT_ID, GET_WIN_STAT = 0x60, WIN_CSR_READ = 0x61, WIN_CSR_WRITE = 0x62, WIN_API_CMD_RD = 0x63, GET_NICTOOL_CAP = 0x74, VM_COMPAT_TEST = 0xFF }; enum hinic_nic_link_mode { HINIC_LINK_MODE_AUTO = 0, HINIC_LINK_MODE_UP, HINIC_LINK_MODE_DOWN, HINIC_LINK_MODE_MAX }; enum api_chain_cmd_type { API_CSR_READ, API_CSR_WRITE, API_CSR_READ_8B, API_CSR_WRITE_8B, }; enum sm_cmd_type { SM_CTR_RD32 = 1, SM_CTR_RD64_PAIR, SM_CTR_RD64 }; enum hinic_show_set { HINIC_SHOW_SSET_IO_STATS = 1, }; #define HINIC_SHOW_ITEM_LEN 32 struct hinic_show_item { char name[HINIC_SHOW_ITEM_LEN]; u8 hexadecimal; /* 0: decimal , 1: Hexadecimal */ u8 rsvd[7]; u64 value; }; #define UP_UPDATEFW_TIME_OUT_VAL 20000U #define UCODE_COMP_TIME_OUT_VAL 0xFF00000 #define NIC_TOOL_MAGIC 'x' enum hinic_nictool_drv_cap { NICTOOL_SUPPORT_API_CSR = 0x1, }; #endif /* __HINIC_DFX_DEF_H__ */ hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_eqs.c000066400000000000000000001130251502027332500212370ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwdev.h" #include "hinic_hwif.h" #include "hinic_csr.h" #include "hinic_eqs.h" #define HINIC_EQS_WQ_NAME "hinic_eqs" #define AEQ_CTRL_0_INTR_IDX_SHIFT 0 #define AEQ_CTRL_0_FUNC_BUSY_SHIFT 10 #define AEQ_CTRL_0_DMA_ATTR_SHIFT 12 #define AEQ_CTRL_0_PCI_INTF_IDX_SHIFT 20 #define AEQ_CTRL_0_QPS_NUM_SHIFT 22 #define AEQ_CTRL_0_INTR_MODE_SHIFT 31 #define AEQ_CTRL_0_INTR_IDX_MASK 0x3FFU #define AEQ_CTRL_0_FUNC_BUSY_MASK 0x1U #define AEQ_CTRL_0_DMA_ATTR_MASK 0x3FU #define AEQ_CTRL_0_PCI_INTF_IDX_MASK 0x3U #define AEQ_CTRL_0_QPS_NUM_MASK 0xFFU #define AEQ_CTRL_0_INTR_MODE_MASK 0x1U #define AEQ_CTRL_0_GET(val, member) \ (((val) >> AEQ_CTRL_0_##member##_SHIFT) & \ AEQ_CTRL_0_##member##_MASK) #define AEQ_CTRL_0_SET(val, member) \ (((val) & AEQ_CTRL_0_##member##_MASK) << \ AEQ_CTRL_0_##member##_SHIFT) #define AEQ_CTRL_0_CLEAR(val, member) \ ((val) & (~(AEQ_CTRL_0_##member##_MASK \ << AEQ_CTRL_0_##member##_SHIFT))) #define AEQ_CTRL_1_LEN_SHIFT 0 #define AEQ_CTRL_1_FUNC_OWN_SHIFT 21 #define AEQ_CTRL_1_ELEM_SIZE_SHIFT 24 #define AEQ_CTRL_1_PAGE_SIZE_SHIFT 28 #define AEQ_CTRL_1_LEN_MASK 0x1FFFFFU #define AEQ_CTRL_1_FUNC_OWN_MASK 0x1U #define AEQ_CTRL_1_ELEM_SIZE_MASK 0x3U #define AEQ_CTRL_1_PAGE_SIZE_MASK 0xFU #define AEQ_CTRL_1_GET(val, member) \ (((val) >> AEQ_CTRL_1_##member##_SHIFT) & \ AEQ_CTRL_1_##member##_MASK) #define AEQ_CTRL_1_SET(val, member) \ (((val) & AEQ_CTRL_1_##member##_MASK) << \ AEQ_CTRL_1_##member##_SHIFT) #define AEQ_CTRL_1_CLEAR(val, member) \ ((val) & (~(AEQ_CTRL_1_##member##_MASK \ << AEQ_CTRL_1_##member##_SHIFT))) #define HINIC_EQ_PROD_IDX_MASK 0xFFFFF #define HINIC_TASK_PROCESS_EQE_LIMIT 1024 #define HINIC_EQ_UPDATE_CI_STEP 64 static uint g_aeq_len = HINIC_DEFAULT_AEQ_LEN; module_param(g_aeq_len, uint, 0444); MODULE_PARM_DESC(g_aeq_len, "aeq depth, valid range is " __stringify(HINIC_MIN_AEQ_LEN) " - " __stringify(HINIC_MAX_AEQ_LEN)); static uint g_ceq_len = HINIC_DEFAULT_CEQ_LEN; module_param(g_ceq_len, uint, 0444); MODULE_PARM_DESC(g_ceq_len, "ceq depth, valid range is " __stringify(HINIC_MIN_CEQ_LEN) " - " __stringify(HINIC_MAX_CEQ_LEN)); static uint g_num_ceqe_in_tasklet = HINIC_TASK_PROCESS_EQE_LIMIT; module_param(g_num_ceqe_in_tasklet, uint, 0444); MODULE_PARM_DESC(g_num_ceqe_in_tasklet, "The max number of ceqe can be processed in tasklet, default = 1024"); #define CEQ_CTRL_0_INTR_IDX_SHIFT 0 #define CEQ_CTRL_0_DMA_ATTR_SHIFT 12 #define CEQ_CTRL_0_LIMIT_KICK_SHIFT 20 #define CEQ_CTRL_0_PCI_INTF_IDX_SHIFT 24 #define CEQ_CTRL_0_INTR_MODE_SHIFT 31 #define CEQ_CTRL_0_INTR_IDX_MASK 0x3FFU #define CEQ_CTRL_0_DMA_ATTR_MASK 0x3FU #define CEQ_CTRL_0_LIMIT_KICK_MASK 0xFU #define CEQ_CTRL_0_PCI_INTF_IDX_MASK 0x3U #define CEQ_CTRL_0_INTR_MODE_MASK 0x1U #define CEQ_CTRL_0_SET(val, member) \ (((val) & CEQ_CTRL_0_##member##_MASK) << \ CEQ_CTRL_0_##member##_SHIFT) #define CEQ_CTRL_1_LEN_SHIFT 0 #define CEQ_CTRL_1_PAGE_SIZE_SHIFT 28 #define CEQ_CTRL_1_LEN_MASK 0x1FFFFFU #define CEQ_CTRL_1_PAGE_SIZE_MASK 0xFU #define CEQ_CTRL_1_SET(val, member) \ (((val) & CEQ_CTRL_1_##member##_MASK) << \ CEQ_CTRL_1_##member##_SHIFT) #define EQ_ELEM_DESC_TYPE_SHIFT 0 #define EQ_ELEM_DESC_SRC_SHIFT 7 #define EQ_ELEM_DESC_SIZE_SHIFT 8 #define EQ_ELEM_DESC_WRAPPED_SHIFT 31 #define EQ_ELEM_DESC_TYPE_MASK 0x7FU #define EQ_ELEM_DESC_SRC_MASK 0x1U #define EQ_ELEM_DESC_SIZE_MASK 0xFFU #define EQ_ELEM_DESC_WRAPPED_MASK 0x1U #define EQ_ELEM_DESC_GET(val, member) \ (((val) >> EQ_ELEM_DESC_##member##_SHIFT) & \ EQ_ELEM_DESC_##member##_MASK) #define EQ_CONS_IDX_CONS_IDX_SHIFT 0 #define EQ_CONS_IDX_XOR_CHKSUM_SHIFT 24 #define EQ_CONS_IDX_INT_ARMED_SHIFT 31 #define EQ_CONS_IDX_CONS_IDX_MASK 0x1FFFFFU #define EQ_CONS_IDX_XOR_CHKSUM_MASK 0xFU #define EQ_CONS_IDX_INT_ARMED_MASK 0x1U #define EQ_CONS_IDX_SET(val, member) \ (((val) & EQ_CONS_IDX_##member##_MASK) << \ EQ_CONS_IDX_##member##_SHIFT) #define EQ_CONS_IDX_CLEAR(val, member) \ ((val) & (~(EQ_CONS_IDX_##member##_MASK \ << EQ_CONS_IDX_##member##_SHIFT))) #define EQ_WRAPPED(eq) ((u32)(eq)->wrapped << EQ_VALID_SHIFT) #define EQ_CONS_IDX(eq) ((eq)->cons_idx | \ ((u32)(eq)->wrapped << EQ_WRAPPED_SHIFT)) #define EQ_CONS_IDX_REG_ADDR(eq) (((eq)->type == HINIC_AEQ) ? \ HINIC_CSR_AEQ_CONS_IDX_ADDR((eq)->q_id) : \ HINIC_CSR_CEQ_CONS_IDX_ADDR((eq)->q_id)) #define EQ_PROD_IDX_REG_ADDR(eq) (((eq)->type == HINIC_AEQ) ? \ HINIC_CSR_AEQ_PROD_IDX_ADDR((eq)->q_id) : \ HINIC_CSR_CEQ_PROD_IDX_ADDR((eq)->q_id)) #define GET_EQ_NUM_PAGES(eq, size) \ ((u16)(ALIGN((u32)((eq)->eq_len * (eq)->elem_size), \ (size)) / (size))) #define GET_EQ_NUM_ELEMS(eq, pg_size) ((pg_size) / (u32)(eq)->elem_size) #define GET_EQ_ELEMENT(eq, idx) \ (((u8 *)(eq)->virt_addr[(idx) / (eq)->num_elem_in_pg]) + \ (u32)(((idx) & ((eq)->num_elem_in_pg - 1)) * (eq)->elem_size)) #define GET_AEQ_ELEM(eq, idx) ((struct hinic_aeq_elem *)\ GET_EQ_ELEMENT((eq), (idx))) #define GET_CEQ_ELEM(eq, idx) ((u32 *)GET_EQ_ELEMENT((eq), (idx))) #define GET_CURR_AEQ_ELEM(eq) GET_AEQ_ELEM((eq), (eq)->cons_idx) #define GET_CURR_CEQ_ELEM(eq) GET_CEQ_ELEM((eq), (eq)->cons_idx) #define PAGE_IN_4K(page_size) ((page_size) >> 12) #define EQ_SET_HW_PAGE_SIZE_VAL(eq) \ ((u32)ilog2(PAGE_IN_4K((eq)->page_size))) #define ELEMENT_SIZE_IN_32B(eq) (((eq)->elem_size) >> 5) #define EQ_SET_HW_ELEM_SIZE_VAL(eq) ((u32)ilog2(ELEMENT_SIZE_IN_32B(eq))) #define AEQ_DMA_ATTR_DEFAULT 0 #define CEQ_DMA_ATTR_DEFAULT 0 #define CEQ_LMT_KICK_DEFAULT 0 #define EQ_MSIX_RESEND_TIMER_CLEAR 1 #define EQ_WRAPPED_SHIFT 20 #define EQ_VALID_SHIFT 31 #define CEQE_TYPE_SHIFT 23 #define CEQE_TYPE_MASK 0x7 #define CEQE_TYPE(type) (((type) >> CEQE_TYPE_SHIFT) & \ CEQE_TYPE_MASK) #define CEQE_DATA_MASK 0x3FFFFFF #define CEQE_DATA(data) ((data) & CEQE_DATA_MASK) #define EQ_MIN_PAGE_SIZE 0x1000U #define aeq_to_aeqs(eq) \ container_of((eq) - (eq)->q_id, struct hinic_aeqs, aeq[0]) #define ceq_to_ceqs(eq) \ container_of((eq) - (eq)->q_id, struct hinic_ceqs, ceq[0]) static irqreturn_t aeq_interrupt(int irq, void *data); static irqreturn_t ceq_interrupt(int irq, void *data); /** * hinic_qps_num_set - set the number of queues that are actually opened, * and instructs the migration driver to migrate specified queues * during VF live migration. * * @hwdev: the pointer to hw device * @num_qps: number of queue */ void hinic_qps_num_set(void *hwdev, u32 num_qps) { struct hinic_hwif *hwif = ((struct hinic_hwdev *)hwdev)->hwif; u32 addr, val, ctrl; addr = HINIC_CSR_AEQ_CTRL_0_ADDR(0); val = hinic_hwif_read_reg(hwif, addr); val = AEQ_CTRL_0_CLEAR(val, QPS_NUM); ctrl = AEQ_CTRL_0_SET(num_qps, QPS_NUM); val |= ctrl; hinic_hwif_write_reg(hwif, addr, val); } u32 hinic_func_busy_state_get(struct hinic_hwdev *hwdev) { struct hinic_hwif *hwif = hwdev->hwif; u32 addr, val; addr = HINIC_CSR_AEQ_CTRL_0_ADDR(0); val = hinic_hwif_read_reg(hwif, addr); return AEQ_CTRL_0_GET(val, FUNC_BUSY); } void hinic_func_busy_state_set(struct hinic_hwdev *hwdev, u32 cfg) { struct hinic_hwif *hwif = hwdev->hwif; u32 addr, val, ctrl; addr = HINIC_CSR_AEQ_CTRL_0_ADDR(0); val = hinic_hwif_read_reg(hwif, addr); val = AEQ_CTRL_0_CLEAR(val, FUNC_BUSY); ctrl = AEQ_CTRL_0_SET(cfg, FUNC_BUSY); val |= ctrl; hinic_hwif_write_reg(hwif, addr, val); } u32 hinic_func_own_bit_get(struct hinic_hwdev *hwdev) { struct hinic_hwif *hwif = hwdev->hwif; u32 addr, val; addr = HINIC_CSR_AEQ_CTRL_1_ADDR(0); val = hinic_hwif_read_reg(hwif, addr); return AEQ_CTRL_1_GET(val, FUNC_OWN); } void hinic_func_own_bit_set(struct hinic_hwdev *hwdev, u32 cfg) { struct hinic_hwif *hwif = hwdev->hwif; u32 addr, val, ctrl; addr = HINIC_CSR_AEQ_CTRL_1_ADDR(0); val = hinic_hwif_read_reg(hwif, addr); val = AEQ_CTRL_1_CLEAR(val, FUNC_OWN); ctrl = AEQ_CTRL_1_SET(cfg, FUNC_OWN); val |= ctrl; hinic_hwif_write_reg(hwif, addr, val); } static void ceq_tasklet(ulong eq_tasklet); static u8 eq_cons_idx_checksum_set(u32 val) { u8 checksum = 0; u8 idx; for (idx = 0; idx < 32; idx += 4) checksum ^= ((val >> idx) & 0xF); return checksum & 0xF; } /** * hinic_aeq_register_hw_cb - register aeq callback for specific event * @hwdev: pointer to hw device * @event: event for the handler * @hw_cb: callback function * Return: 0 - success, negative - failure */ int hinic_aeq_register_hw_cb(void *hwdev, enum hinic_aeq_type event, hinic_aeq_hwe_cb hwe_cb) { struct hinic_aeqs *aeqs; if (!hwdev || !hwe_cb || event >= HINIC_MAX_AEQ_EVENTS) return -EINVAL; aeqs = ((struct hinic_hwdev *)hwdev)->aeqs; aeqs->aeq_hwe_cb[event] = hwe_cb; set_bit(HINIC_AEQ_HW_CB_REG, &aeqs->aeq_hw_cb_state[event]); return 0; } EXPORT_SYMBOL(hinic_aeq_register_hw_cb); /** * hinic_aeq_unregister_hw_cb - unregister the aeq callback for specific event * @hwdev: pointer to hw device * @event: event for the handler */ void hinic_aeq_unregister_hw_cb(void *hwdev, enum hinic_aeq_type event) { struct hinic_aeqs *aeqs; if (!hwdev || event >= HINIC_MAX_AEQ_EVENTS) return; aeqs = ((struct hinic_hwdev *)hwdev)->aeqs; clear_bit(HINIC_AEQ_HW_CB_REG, &aeqs->aeq_hw_cb_state[event]); while (test_bit(HINIC_AEQ_HW_CB_RUNNING, &aeqs->aeq_hw_cb_state[event])) usleep_range(900, 1000); aeqs->aeq_hwe_cb[event] = NULL; } EXPORT_SYMBOL(hinic_aeq_unregister_hw_cb); /** * hinic_aeq_register_sw_cb - register aeq callback for sw event * @hwdev: pointer to hw device * @event: soft event for the handler * @sw_cb: callback function * Return: 0 - success, negative - failure */ int hinic_aeq_register_swe_cb(void *hwdev, enum hinic_aeq_sw_type event, hinic_aeq_swe_cb aeq_swe_cb) { struct hinic_aeqs *aeqs; if (!hwdev || !aeq_swe_cb || event >= HINIC_MAX_AEQ_SW_EVENTS) return -EINVAL; aeqs = ((struct hinic_hwdev *)hwdev)->aeqs; aeqs->aeq_swe_cb[event] = aeq_swe_cb; set_bit(HINIC_AEQ_SW_CB_REG, &aeqs->aeq_sw_cb_state[event]); return 0; } EXPORT_SYMBOL(hinic_aeq_register_swe_cb); /** * hinic_aeq_unregister_sw_cb - unregister the aeq callback for sw event * @hwdev: pointer to hw device * @event: soft event for the handler */ void hinic_aeq_unregister_swe_cb(void *hwdev, enum hinic_aeq_sw_type event) { struct hinic_aeqs *aeqs; if (!hwdev || event >= HINIC_MAX_AEQ_SW_EVENTS) return; aeqs = ((struct hinic_hwdev *)hwdev)->aeqs; clear_bit(HINIC_AEQ_SW_CB_REG, &aeqs->aeq_sw_cb_state[event]); while (test_bit(HINIC_AEQ_SW_CB_RUNNING, &aeqs->aeq_sw_cb_state[event])) usleep_range(900, 1000); aeqs->aeq_swe_cb[event] = NULL; } EXPORT_SYMBOL(hinic_aeq_unregister_swe_cb); /** * hinic_ceq_register_sw_cb - register ceq callback for specific event * @hwdev: pointer to hw device * @event: event for the handler * @callback: callback function * Return: 0 - success, negative - failure */ int hinic_ceq_register_cb(void *hwdev, enum hinic_ceq_event event, hinic_ceq_event_cb callback) { struct hinic_ceqs *ceqs; if (!hwdev || event >= HINIC_MAX_CEQ_EVENTS) return -EINVAL; ceqs = ((struct hinic_hwdev *)hwdev)->ceqs; ceqs->ceq_cb[event] = callback; set_bit(HINIC_CEQ_CB_REG, &ceqs->ceq_cb_state[event]); return 0; } EXPORT_SYMBOL(hinic_ceq_register_cb); /** * hinic_ceq_unregister_cb - unregister ceq callback for specific event * @hwdev: pointer to hw device * @event: event for the handler */ void hinic_ceq_unregister_cb(void *hwdev, enum hinic_ceq_event event) { struct hinic_ceqs *ceqs; if (!hwdev || event >= HINIC_MAX_CEQ_EVENTS) return; ceqs = ((struct hinic_hwdev *)hwdev)->ceqs; clear_bit(HINIC_CEQ_CB_REG, &ceqs->ceq_cb_state[event]); while (test_bit(HINIC_CEQ_CB_RUNNING, &ceqs->ceq_cb_state[event])) usleep_range(900, 1000); ceqs->ceq_cb[event] = NULL; } EXPORT_SYMBOL(hinic_ceq_unregister_cb); /** * set_eq_cons_idx - write the cons idx to the hw * @eq: The event queue to update the cons idx for * @arm_state: arm state value */ static void set_eq_cons_idx(struct hinic_eq *eq, u32 arm_state) { u32 eq_wrap_ci, val; u32 addr = EQ_CONS_IDX_REG_ADDR(eq); eq_wrap_ci = EQ_CONS_IDX(eq); /* other filed is resverd, set to 0 */ val = EQ_CONS_IDX_SET(eq_wrap_ci, CONS_IDX) | EQ_CONS_IDX_SET(arm_state, INT_ARMED); val |= EQ_CONS_IDX_SET(eq_cons_idx_checksum_set(val), XOR_CHKSUM); hinic_hwif_write_reg(eq->hwdev->hwif, addr, val); } /** * ceq_event_handler - handle for the ceq events * @ceqs: eqs part of the chip * @ceqe: ceq element of the event */ static void ceq_event_handler(struct hinic_ceqs *ceqs, u32 ceqe) { struct hinic_hwdev *hwdev = ceqs->hwdev; enum hinic_ceq_event event = CEQE_TYPE(ceqe); u32 ceqe_data = CEQE_DATA(ceqe); if (event >= HINIC_MAX_CEQ_EVENTS) { sdk_err(hwdev->dev_hdl, "Ceq unknown event: %d, ceqe date: 0x%x\n", event, ceqe_data); return; } set_bit(HINIC_CEQ_CB_RUNNING, &ceqs->ceq_cb_state[event]); if (ceqs->ceq_cb[event] && test_bit(HINIC_CEQ_CB_REG, &ceqs->ceq_cb_state[event])) ceqs->ceq_cb[event](hwdev, ceqe_data); clear_bit(HINIC_CEQ_CB_RUNNING, &ceqs->ceq_cb_state[event]); } /** * aeq_irq_handler - handler for the aeq event * @eq: the async event queue of the event */ static bool aeq_irq_handler(struct hinic_eq *eq) { struct hinic_aeqs *aeqs = aeq_to_aeqs(eq); struct hinic_aeq_elem *aeqe_pos; enum hinic_aeq_type event; enum hinic_aeq_sw_type sw_event; enum hinic_ucode_event_type ucode_event; u64 aeqe_data; u32 aeqe_desc; u32 i, eqe_cnt = 0; u8 size; u8 lev; for (i = 0; i < HINIC_TASK_PROCESS_EQE_LIMIT; i++) { aeqe_pos = GET_CURR_AEQ_ELEM(eq); /* Data in HW is in Big endian Format */ aeqe_desc = be32_to_cpu(aeqe_pos->desc); /* HW updates wrapped bit, when it adds eq element event */ if (EQ_ELEM_DESC_GET(aeqe_desc, WRAPPED) == eq->wrapped) return false; /* This memory barrier is needed to keep us from reading * any other fields out of the cmdq wqe until we have * verified the command has been processed and * written back. */ dma_rmb(); event = EQ_ELEM_DESC_GET(aeqe_desc, TYPE); if (EQ_ELEM_DESC_GET(aeqe_desc, SRC)) { ucode_event = (enum hinic_ucode_event_type)event; /* SW event uses only the first 8B */ sw_event = ucode_event >= HINIC_NIC_FATAL_ERROR_MAX ? HINIC_STATEFULL_EVENT : HINIC_STATELESS_EVENT; aeqe_data = be64_to_cpu((*(u64 *)aeqe_pos->aeqe_data)); set_bit(HINIC_AEQ_SW_CB_RUNNING, &aeqs->aeq_sw_cb_state[sw_event]); if (aeqs->aeq_swe_cb[sw_event] && test_bit(HINIC_AEQ_SW_CB_REG, &aeqs->aeq_sw_cb_state[sw_event])) { lev = aeqs->aeq_swe_cb[sw_event](aeqs->hwdev, ucode_event, aeqe_data); } clear_bit(HINIC_AEQ_SW_CB_RUNNING, &aeqs->aeq_sw_cb_state[sw_event]); } else { if (event < HINIC_MAX_AEQ_EVENTS) { size = EQ_ELEM_DESC_GET(aeqe_desc, SIZE); set_bit(HINIC_AEQ_HW_CB_RUNNING, &aeqs->aeq_hw_cb_state[event]); if (aeqs->aeq_hwe_cb[event] && test_bit(HINIC_AEQ_HW_CB_REG, &aeqs->aeq_hw_cb_state[event])) aeqs->aeq_hwe_cb[event](aeqs->hwdev, aeqe_pos->aeqe_data, size); clear_bit(HINIC_AEQ_HW_CB_RUNNING, &aeqs->aeq_hw_cb_state[event]); } else { sdk_warn(eq->hwdev->dev_hdl, "Unknown aeq hw event %d\n", event); } } eq->cons_idx++; if (eq->cons_idx == eq->eq_len) { eq->cons_idx = 0; eq->wrapped = !eq->wrapped; } if (++eqe_cnt >= HINIC_EQ_UPDATE_CI_STEP) { eqe_cnt = 0; set_eq_cons_idx(eq, HINIC_EQ_NOT_ARMED); } } return true; } /** * ceq_irq_handler - handler for the ceq event * @eq: the completion event queue of the event * Return: true - success, false - failure */ static bool ceq_irq_handler(struct hinic_eq *eq) { struct hinic_ceqs *ceqs = ceq_to_ceqs(eq); u32 ceqe, eqe_cnt = 0; u32 i; for (i = 0; i < g_num_ceqe_in_tasklet; i++) { ceqe = *(GET_CURR_CEQ_ELEM(eq)); ceqe = be32_to_cpu(ceqe); /* HW updates wrapped bit, when it adds eq element event */ if (EQ_ELEM_DESC_GET(ceqe, WRAPPED) == eq->wrapped) return false; ceq_event_handler(ceqs, ceqe); eq->cons_idx++; if (eq->cons_idx == eq->eq_len) { eq->cons_idx = 0; eq->wrapped = !eq->wrapped; } if (++eqe_cnt >= HINIC_EQ_UPDATE_CI_STEP) { eqe_cnt = 0; set_eq_cons_idx(eq, HINIC_EQ_NOT_ARMED); } } return true; } static void reschedule_eq_handler(struct hinic_eq *eq) { if (eq->type == HINIC_AEQ) { struct hinic_aeqs *aeqs = aeq_to_aeqs(eq); queue_work(aeqs->workq, &eq->aeq_work); } else { tasklet_schedule(&eq->ceq_tasklet); } } int hinic_reschedule_eq(struct hinic_hwdev *hwdev, enum hinic_eq_type type, u16 eq_id) { if (type == HINIC_AEQ) { if (eq_id >= hwdev->aeqs->num_aeqs) return -EINVAL; reschedule_eq_handler(&hwdev->aeqs->aeq[eq_id]); } else { if (eq_id >= hwdev->ceqs->num_ceqs) return -EINVAL; reschedule_eq_handler(&hwdev->ceqs->ceq[eq_id]); } return 0; } /** * eq_irq_handler - handler for the eq event * @data: the event queue of the event * Return: true - success, false - failure */ static bool eq_irq_handler(void *data) { struct hinic_eq *eq = (struct hinic_eq *)data; bool uncompleted; if (eq->type == HINIC_AEQ) uncompleted = aeq_irq_handler(eq); else uncompleted = ceq_irq_handler(eq); set_eq_cons_idx(eq, uncompleted ? HINIC_EQ_NOT_ARMED : HINIC_EQ_ARMED); return uncompleted; } static struct hinic_eq *find_eq(struct hinic_hwdev *hwdev, int msix_entry_idx) { struct hinic_aeqs *aeqs = hwdev->aeqs; struct hinic_ceqs *ceqs = hwdev->ceqs; int i; for (i = 0; i < aeqs->num_aeqs; i++) { struct hinic_eq *eq = &aeqs->aeq[i]; if (eq->eq_irq.msix_entry_idx == msix_entry_idx) return eq; } for (i = 0; i < ceqs->num_ceqs; i++) { struct hinic_eq *eq = &ceqs->ceq[i]; if (eq->eq_irq.msix_entry_idx == msix_entry_idx) return eq; } return NULL; } /* for windows */ bool hinic_eq_intr_handler(void *hwdev, int msix_entry_idx) { struct hinic_eq *eq; eq = find_eq(hwdev, msix_entry_idx); if (!eq) { pr_err("Can't find eq in eq interrupt handler\n"); return false; } return eq_irq_handler(eq); } /** * eq_irq_work - eq work for the event * @work: the work that is associated with the eq */ static void eq_irq_work(struct work_struct *work) { struct hinic_eq *eq = container_of(work, struct hinic_eq, aeq_work); if (eq_irq_handler(eq)) reschedule_eq_handler(eq); } /** * aeq_interrupt - aeq interrupt handler * @irq: irq number * @data: the async event queue of the event */ static irqreturn_t aeq_interrupt(int irq, void *data) { struct hinic_eq *aeq = (struct hinic_eq *)data; struct hinic_hwdev *hwdev = aeq->hwdev; struct hinic_aeqs *aeqs = aeq_to_aeqs(aeq); /* clear resend timer cnt register */ hinic_misx_intr_clear_resend_bit(hwdev, aeq->eq_irq.msix_entry_idx, EQ_MSIX_RESEND_TIMER_CLEAR); queue_work(aeqs->workq, &aeq->aeq_work); return IRQ_HANDLED; } /** * ceq_tasklet - ceq tasklet for the event * @ceq_data: data that will be used by the tasklet(ceq) */ static void ceq_tasklet(ulong ceq_data) { struct hinic_eq *eq = (struct hinic_eq *)ceq_data; eq->soft_intr_jif = jiffies; if (eq_irq_handler(eq)) reschedule_eq_handler(eq); } /** * ceq_interrupt - ceq interrupt handler * @irq: irq number * @data: the completion event queue of the event */ static irqreturn_t ceq_interrupt(int irq, void *data) { struct hinic_eq *ceq = (struct hinic_eq *)data; ceq->hard_intr_jif = jiffies; /* clear resend timer counters */ hinic_misx_intr_clear_resend_bit(ceq->hwdev, ceq->eq_irq.msix_entry_idx, EQ_MSIX_RESEND_TIMER_CLEAR); tasklet_schedule(&ceq->ceq_tasklet); return IRQ_HANDLED; } struct hinic_ceq_ctrl_reg { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 q_id; u32 ctrl0; u32 ctrl1; }; static int set_ceq_ctrl_reg(struct hinic_hwdev *hwdev, u16 q_id, u32 ctrl0, u32 ctrl1) { struct hinic_ceq_ctrl_reg ceq_ctrl = {0}; u16 in_size = sizeof(ceq_ctrl); u16 out_size = sizeof(ceq_ctrl); int err; err = hinic_global_func_id_get(hwdev, &ceq_ctrl.func_id); if (err) return err; ceq_ctrl.q_id = q_id; ceq_ctrl.ctrl0 = ctrl0; ceq_ctrl.ctrl1 = ctrl1; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_CEQ_CTRL_REG_WR_BY_UP, &ceq_ctrl, in_size, &ceq_ctrl, &out_size, 0); if (err || !out_size || ceq_ctrl.status) { sdk_err(hwdev->dev_hdl, "Failed to set ceq %d ctrl reg, err: %d status: 0x%x, out_size: 0x%x\n", q_id, err, ceq_ctrl.status, out_size); return -EFAULT; } return 0; } /** * set_eq_ctrls - setting eq's ctrls registers * @eq: the event queue for setting * Return: 0 - success, negative - failure */ static int set_eq_ctrls(struct hinic_eq *eq) { enum hinic_eq_type type = eq->type; struct hinic_hwif *hwif = eq->hwdev->hwif; struct irq_info *eq_irq = &eq->eq_irq; u32 addr, val, ctrl0, ctrl1, page_size_val, elem_size; u32 pci_intf_idx = HINIC_PCI_INTF_IDX(hwif); int err; if (type == HINIC_AEQ) { /* set ctrl0 */ addr = HINIC_CSR_AEQ_CTRL_0_ADDR(eq->q_id); val = hinic_hwif_read_reg(hwif, addr); val = AEQ_CTRL_0_CLEAR(val, INTR_IDX) & AEQ_CTRL_0_CLEAR(val, DMA_ATTR) & AEQ_CTRL_0_CLEAR(val, PCI_INTF_IDX) & AEQ_CTRL_0_CLEAR(val, INTR_MODE); if (HINIC_IS_VF(eq->hwdev)) { val = AEQ_CTRL_0_CLEAR(val, FUNC_BUSY) & AEQ_CTRL_1_CLEAR(val, FUNC_OWN); } ctrl0 = AEQ_CTRL_0_SET(eq_irq->msix_entry_idx, INTR_IDX) | AEQ_CTRL_0_SET(AEQ_DMA_ATTR_DEFAULT, DMA_ATTR) | AEQ_CTRL_0_SET(pci_intf_idx, PCI_INTF_IDX) | AEQ_CTRL_0_SET(HINIC_INTR_MODE_ARMED, INTR_MODE); val |= ctrl0; hinic_hwif_write_reg(hwif, addr, val); /* set ctrl1 */ addr = HINIC_CSR_AEQ_CTRL_1_ADDR(eq->q_id); page_size_val = EQ_SET_HW_PAGE_SIZE_VAL(eq); elem_size = EQ_SET_HW_ELEM_SIZE_VAL(eq); ctrl1 = AEQ_CTRL_1_SET(eq->eq_len, LEN) | AEQ_CTRL_1_SET(elem_size, ELEM_SIZE) | AEQ_CTRL_1_SET(page_size_val, PAGE_SIZE); hinic_hwif_write_reg(hwif, addr, ctrl1); } else { ctrl0 = CEQ_CTRL_0_SET(eq_irq->msix_entry_idx, INTR_IDX) | CEQ_CTRL_0_SET(CEQ_DMA_ATTR_DEFAULT, DMA_ATTR) | CEQ_CTRL_0_SET(CEQ_LMT_KICK_DEFAULT, LIMIT_KICK) | CEQ_CTRL_0_SET(pci_intf_idx, PCI_INTF_IDX) | CEQ_CTRL_0_SET(HINIC_INTR_MODE_ARMED, INTR_MODE); page_size_val = EQ_SET_HW_PAGE_SIZE_VAL(eq); ctrl1 = CEQ_CTRL_1_SET(eq->eq_len, LEN) | CEQ_CTRL_1_SET(page_size_val, PAGE_SIZE); /* set ceq ctrl reg through mgmt cpu */ err = set_ceq_ctrl_reg(eq->hwdev, eq->q_id, ctrl0, ctrl1); if (err) return err; } return 0; } /** * ceq_elements_init - Initialize all the elements in the ceq * @eq: the event queue * @init_val: value to init with it the elements */ static void ceq_elements_init(struct hinic_eq *eq, u32 init_val) { u32 i; u32 *ceqe; for (i = 0; i < eq->eq_len; i++) { ceqe = GET_CEQ_ELEM(eq, i); *(ceqe) = cpu_to_be32(init_val); } wmb(); /* Write the init values */ } /** * aeq_elements_init - initialize all the elements in the aeq * @eq: the event queue * @init_val: value to init with it the elements */ static void aeq_elements_init(struct hinic_eq *eq, u32 init_val) { struct hinic_aeq_elem *aeqe; u32 i; for (i = 0; i < eq->eq_len; i++) { aeqe = GET_AEQ_ELEM(eq, i); aeqe->desc = cpu_to_be32(init_val); } wmb(); /* Write the init values */ } /** * alloc_eq_pages - allocate the pages for the queue * @eq: the event queue */ static int alloc_eq_pages(struct hinic_eq *eq) { struct hinic_hwif *hwif = eq->hwdev->hwif; u32 init_val; u64 dma_addr_size, virt_addr_size; u16 pg_num, i; u32 reg; int err; u8 flag = 0; dma_addr_size = eq->num_pages * sizeof(*eq->dma_addr); virt_addr_size = eq->num_pages * sizeof(*eq->virt_addr); eq->dma_addr = kzalloc(dma_addr_size, GFP_KERNEL); if (!eq->dma_addr) return -ENOMEM; eq->virt_addr = kzalloc(virt_addr_size, GFP_KERNEL); if (!eq->virt_addr) { err = -ENOMEM; goto virt_addr_alloc_err; } eq->dma_addr_for_free = kzalloc(dma_addr_size, GFP_KERNEL); if (!eq->dma_addr_for_free) { err = -ENOMEM; goto dma_addr_free_alloc_err; } eq->virt_addr_for_free = kzalloc(virt_addr_size, GFP_KERNEL); if (!eq->virt_addr_for_free) { err = -ENOMEM; goto virt_addr_free_alloc_err; } for (pg_num = 0; pg_num < eq->num_pages; pg_num++) { eq->virt_addr_for_free[pg_num] = dma_alloc_coherent (eq->hwdev->dev_hdl, eq->page_size, &eq->dma_addr_for_free[pg_num], GFP_KERNEL); if (!eq->virt_addr_for_free[pg_num]) { err = -ENOMEM; goto dma_alloc_err; } eq->dma_addr[pg_num] = eq->dma_addr_for_free[pg_num]; eq->virt_addr[pg_num] = eq->virt_addr_for_free[pg_num]; if (eq->dma_addr_for_free[pg_num] & (eq->page_size - 1)) { sdk_info(eq->hwdev->dev_hdl, "Address is not aligned to %u-bytes as hardware required\n", eq->page_size); sdk_info(eq->hwdev->dev_hdl, "Change eq's page size %u\n", ((eq->page_size) >> 1)); eq->dma_addr[pg_num] = ALIGN (eq->dma_addr_for_free[pg_num], (u64)((eq->page_size) >> 1)); eq->virt_addr[pg_num] = eq->virt_addr_for_free[pg_num] + ((u64)eq->dma_addr[pg_num] - (u64)eq->dma_addr_for_free[pg_num]); flag = 1; } reg = HINIC_EQ_HI_PHYS_ADDR_REG(eq->type, eq->q_id, pg_num); hinic_hwif_write_reg(hwif, reg, upper_32_bits(eq->dma_addr[pg_num])); reg = HINIC_EQ_LO_PHYS_ADDR_REG(eq->type, eq->q_id, pg_num); hinic_hwif_write_reg(hwif, reg, lower_32_bits(eq->dma_addr[pg_num])); } if (flag) { eq->page_size = eq->page_size >> 1; eq->eq_len = eq->eq_len >> 1; } eq->num_elem_in_pg = GET_EQ_NUM_ELEMS(eq, eq->page_size); if (eq->num_elem_in_pg & (eq->num_elem_in_pg - 1)) { sdk_err(eq->hwdev->dev_hdl, "Number element in eq page != power of 2\n"); err = -EINVAL; goto dma_alloc_err; } init_val = EQ_WRAPPED(eq); if (eq->type == HINIC_AEQ) aeq_elements_init(eq, init_val); else ceq_elements_init(eq, init_val); return 0; dma_alloc_err: for (i = 0; i < pg_num; i++) dma_free_coherent(eq->hwdev->dev_hdl, eq->page_size, eq->virt_addr_for_free[i], eq->dma_addr_for_free[i]); kfree(eq->virt_addr_for_free); virt_addr_free_alloc_err: kfree(eq->dma_addr_for_free); dma_addr_free_alloc_err: kfree(eq->virt_addr); virt_addr_alloc_err: kfree(eq->dma_addr); return err; } /** * free_eq_pages - free the pages of the queue * @eq: the event queue */ static void free_eq_pages(struct hinic_eq *eq) { struct hinic_hwdev *hwdev = eq->hwdev; u16 pg_num; for (pg_num = 0; pg_num < eq->num_pages; pg_num++) dma_free_coherent(hwdev->dev_hdl, eq->orig_page_size, eq->virt_addr_for_free[pg_num], eq->dma_addr_for_free[pg_num]); kfree(eq->virt_addr_for_free); kfree(eq->dma_addr_for_free); kfree(eq->virt_addr); kfree(eq->dma_addr); } static inline u32 get_page_size(struct hinic_eq *eq) { u32 total_size; u16 count, n = 0; total_size = ALIGN((eq->eq_len * eq->elem_size), EQ_MIN_PAGE_SIZE); if (total_size <= (HINIC_EQ_MAX_PAGES * EQ_MIN_PAGE_SIZE)) return EQ_MIN_PAGE_SIZE; count = (u16)(ALIGN((total_size / HINIC_EQ_MAX_PAGES), EQ_MIN_PAGE_SIZE) / EQ_MIN_PAGE_SIZE); if (!(count & (count - 1))) return EQ_MIN_PAGE_SIZE * count; while (count) { count >>= 1; n++; } return EQ_MIN_PAGE_SIZE << n; } /** * init_eq - initialize eq * @eq: the event queue * @hwdev: the pointer to hw device * @q_id: Queue id number * @q_len: the number of EQ elements * @type: the type of the event queue, ceq or aeq * @entry: msix entry associated with the event queue * Return: 0 - Success, Negative - failure */ static int init_eq(struct hinic_eq *eq, struct hinic_hwdev *hwdev, u16 q_id, u32 q_len, enum hinic_eq_type type, struct irq_info *entry) { int err = 0; eq->hwdev = hwdev; eq->q_id = q_id; eq->type = type; eq->eq_len = q_len; /* clear eq_len to force eqe drop in hardware */ if (eq->type == HINIC_AEQ) hinic_hwif_write_reg(eq->hwdev->hwif, HINIC_CSR_AEQ_CTRL_1_ADDR(eq->q_id), 0); else set_ceq_ctrl_reg(eq->hwdev, eq->q_id, 0, 0); eq->cons_idx = 0; eq->wrapped = 0; eq->elem_size = (type == HINIC_AEQ) ? HINIC_AEQE_SIZE : HINIC_CEQE_SIZE; eq->page_size = get_page_size(eq); eq->orig_page_size = eq->page_size; eq->num_pages = GET_EQ_NUM_PAGES(eq, eq->page_size); if (eq->num_pages > HINIC_EQ_MAX_PAGES) { sdk_err(hwdev->dev_hdl, "Number pages: %d too many pages for eq\n", eq->num_pages); return -EINVAL; } err = alloc_eq_pages(eq); if (err) { sdk_err(hwdev->dev_hdl, "Failed to allocate pages for eq\n"); return err; } eq->eq_irq.msix_entry_idx = entry->msix_entry_idx; eq->eq_irq.irq_id = entry->irq_id; err = set_eq_ctrls(eq); if (err) { sdk_err(hwdev->dev_hdl, "Failed to allocate pages for eq\n"); goto init_eq_ctrls_err; } hinic_hwif_write_reg(eq->hwdev->hwif, EQ_PROD_IDX_REG_ADDR(eq), 0); set_eq_cons_idx(eq, HINIC_EQ_ARMED); if (type == HINIC_AEQ) INIT_WORK(&eq->aeq_work, eq_irq_work); else tasklet_init(&eq->ceq_tasklet, ceq_tasklet, (ulong)eq); if (type == HINIC_AEQ) { err = snprintf(eq->irq_name, sizeof(eq->irq_name), "hinic_aeq%d@pci:%s", eq->q_id, pci_name(hwdev->pcidev_hdl)); if (err <= 0 || err >= (int)sizeof(eq->irq_name)) { sdk_err(hwdev->dev_hdl, "Failed snprintf irq_name, function return(%d) and dest_len(%d)\n", err, (int)sizeof(eq->irq_name)); err = -EINVAL; goto req_irq_err; } err = request_irq(entry->irq_id, aeq_interrupt, 0UL, eq->irq_name, eq); } else { err = snprintf(eq->irq_name, sizeof(eq->irq_name), "hinic_ceq%d@pci:%s", eq->q_id, pci_name(hwdev->pcidev_hdl)); if (err <= 0 || err >= (int)sizeof(eq->irq_name)) { sdk_err(hwdev->dev_hdl, "Failed snprintf irq_name, function return(%d) and dest_len(%d)\n", err, (int)sizeof(eq->irq_name)); err = -EINVAL; goto req_irq_err; } err = request_irq(entry->irq_id, ceq_interrupt, 0UL, eq->irq_name, eq); } if (err) { sdk_err(hwdev->dev_hdl, "Failed to request irq for the eq, err: %d\n", err); goto req_irq_err; } hinic_set_msix_state(hwdev, entry->msix_entry_idx, HINIC_MSIX_ENABLE); return 0; init_eq_ctrls_err: req_irq_err: free_eq_pages(eq); return err; } /** * remove_eq - remove eq * @eq: the event queue */ static void remove_eq(struct hinic_eq *eq) { struct irq_info *entry = &eq->eq_irq; hinic_set_msix_state(eq->hwdev, entry->msix_entry_idx, HINIC_MSIX_DISABLE); synchronize_irq(entry->irq_id); free_irq(entry->irq_id, eq); if (eq->type == HINIC_AEQ) { cancel_work_sync(&eq->aeq_work); /* clear eq_len to avoid hw access host memory */ hinic_hwif_write_reg(eq->hwdev->hwif, HINIC_CSR_AEQ_CTRL_1_ADDR(eq->q_id), 0); } else { tasklet_kill(&eq->ceq_tasklet); set_ceq_ctrl_reg(eq->hwdev, eq->q_id, 0, 0); } /* update cons_idx to avoid invalid interrupt */ eq->cons_idx = hinic_hwif_read_reg(eq->hwdev->hwif, EQ_PROD_IDX_REG_ADDR(eq)); set_eq_cons_idx(eq, HINIC_EQ_NOT_ARMED); free_eq_pages(eq); } /** * hinic_aeqs_init - init all the aeqs * @hwdev: the pointer to hw device * @num_aeqs: number of AEQs * @msix_entries: msix entries associated with the event queues * Return: 0 - Success, Negative - failure */ int hinic_aeqs_init(struct hinic_hwdev *hwdev, u16 num_aeqs, struct irq_info *msix_entries) { struct hinic_aeqs *aeqs; int err; u16 i, q_id; u32 aeq_len; aeqs = kzalloc(sizeof(*aeqs), GFP_KERNEL); if (!aeqs) return -ENOMEM; hwdev->aeqs = aeqs; aeqs->hwdev = hwdev; aeqs->num_aeqs = num_aeqs; aeqs->workq = create_singlethread_workqueue(HINIC_EQS_WQ_NAME); if (!aeqs->workq) { sdk_err(hwdev->dev_hdl, "Failed to initialize aeq workqueue\n"); err = -ENOMEM; goto create_work_err; } if (g_aeq_len < HINIC_MIN_AEQ_LEN || g_aeq_len > HINIC_MAX_AEQ_LEN) { sdk_warn(hwdev->dev_hdl, "Module Parameter g_aeq_len value %d out of range, resetting to %d\n", g_aeq_len, HINIC_DEFAULT_AEQ_LEN); g_aeq_len = HINIC_DEFAULT_AEQ_LEN; } if (HINIC_FUNC_TYPE(hwdev) == TYPE_VF && hwdev->hwif->chip_mode != CHIP_MODE_NORMAL) aeq_len = HINIC_VMGW_DEFAULT_AEQ_LEN; else aeq_len = g_aeq_len; for (q_id = 0; q_id < num_aeqs; q_id++) { err = init_eq(&aeqs->aeq[q_id], hwdev, q_id, aeq_len, HINIC_AEQ, &msix_entries[q_id]); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init aeq %d\n", q_id); goto init_aeq_err; } } return 0; init_aeq_err: for (i = 0; i < q_id; i++) remove_eq(&aeqs->aeq[i]); destroy_workqueue(aeqs->workq); create_work_err: kfree(aeqs); return err; } /** * hinic_aeqs_free - free all the aeqs * @hwdev: the pointer to hw device */ void hinic_aeqs_free(struct hinic_hwdev *hwdev) { struct hinic_aeqs *aeqs = hwdev->aeqs; enum hinic_aeq_type aeq_event = HINIC_HW_INTER_INT; enum hinic_aeq_sw_type sw_aeq_event = HINIC_STATELESS_EVENT; u16 q_id; for (q_id = 0; q_id < aeqs->num_aeqs; q_id++) remove_eq(&aeqs->aeq[q_id]); for (; sw_aeq_event < HINIC_MAX_AEQ_SW_EVENTS; sw_aeq_event++) hinic_aeq_unregister_swe_cb(hwdev, sw_aeq_event); for (; aeq_event < HINIC_MAX_AEQ_EVENTS; aeq_event++) hinic_aeq_unregister_hw_cb(hwdev, aeq_event); destroy_workqueue(aeqs->workq); kfree(aeqs); } /** * hinic_ceqs_init - init all the ceqs * @hwdev: the pointer to hw device * @num_ceqs: number of CEQs * @msix_entries: msix entries associated with the event queues * Return: 0 - Success, Negative - failure */ int hinic_ceqs_init(struct hinic_hwdev *hwdev, u16 num_ceqs, struct irq_info *msix_entries) { struct hinic_ceqs *ceqs; int err; u16 i, q_id; u32 ceq_len; ceqs = kzalloc(sizeof(*ceqs), GFP_KERNEL); if (!ceqs) return -ENOMEM; hwdev->ceqs = ceqs; ceqs->hwdev = hwdev; ceqs->num_ceqs = num_ceqs; if (g_ceq_len < HINIC_MIN_CEQ_LEN || g_ceq_len > HINIC_MAX_CEQ_LEN) { sdk_warn(hwdev->dev_hdl, "Module Parameter g_ceq_len value %d out of range, resetting to %d\n", g_ceq_len, HINIC_DEFAULT_CEQ_LEN); g_ceq_len = HINIC_DEFAULT_CEQ_LEN; } if (HINIC_FUNC_TYPE(hwdev) == TYPE_VF && hwdev->hwif->chip_mode != CHIP_MODE_NORMAL) ceq_len = HINIC_VMGW_DEFAULT_CEQ_LEN; else ceq_len = g_ceq_len; if (!g_num_ceqe_in_tasklet) { sdk_warn(hwdev->dev_hdl, "Module Parameter g_num_ceqe_in_tasklet can not be zero, resetting to %d\n", HINIC_TASK_PROCESS_EQE_LIMIT); g_num_ceqe_in_tasklet = HINIC_TASK_PROCESS_EQE_LIMIT; } for (q_id = 0; q_id < num_ceqs; q_id++) { err = init_eq(&ceqs->ceq[q_id], hwdev, q_id, ceq_len, HINIC_CEQ, &msix_entries[q_id]); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init ceq %d\n", q_id); goto init_ceq_err; } } return 0; init_ceq_err: for (i = 0; i < q_id; i++) remove_eq(&ceqs->ceq[i]); kfree(ceqs); return err; } /** * hinic_ceqs_free - free all the ceqs * @hwdev: the pointer to hw device */ void hinic_ceqs_free(struct hinic_hwdev *hwdev) { struct hinic_ceqs *ceqs = hwdev->ceqs; enum hinic_ceq_event ceq_event = HINIC_CMDQ; u16 q_id; for (q_id = 0; q_id < ceqs->num_ceqs; q_id++) remove_eq(&ceqs->ceq[q_id]); for (; ceq_event < HINIC_MAX_CEQ_EVENTS; ceq_event++) hinic_ceq_unregister_cb(hwdev, ceq_event); kfree(ceqs); } void hinic_get_ceq_irqs(struct hinic_hwdev *hwdev, struct irq_info *irqs, u16 *num_irqs) { struct hinic_ceqs *ceqs = hwdev->ceqs; u16 q_id; for (q_id = 0; q_id < ceqs->num_ceqs; q_id++) { irqs[q_id].irq_id = ceqs->ceq[q_id].eq_irq.irq_id; irqs[q_id].msix_entry_idx = ceqs->ceq[q_id].eq_irq.msix_entry_idx; } *num_irqs = ceqs->num_ceqs; } void hinic_get_aeq_irqs(struct hinic_hwdev *hwdev, struct irq_info *irqs, u16 *num_irqs) { struct hinic_aeqs *aeqs = hwdev->aeqs; u16 q_id; for (q_id = 0; q_id < aeqs->num_aeqs; q_id++) { irqs[q_id].irq_id = aeqs->aeq[q_id].eq_irq.irq_id; irqs[q_id].msix_entry_idx = aeqs->aeq[q_id].eq_irq.msix_entry_idx; } *num_irqs = aeqs->num_aeqs; } void hinic_dump_aeq_info(struct hinic_hwdev *hwdev) { struct hinic_aeq_elem *aeqe_pos; struct hinic_eq *eq; u32 addr, ci, pi; int q_id; for (q_id = 0; q_id < hwdev->aeqs->num_aeqs; q_id++) { eq = &hwdev->aeqs->aeq[q_id]; addr = EQ_CONS_IDX_REG_ADDR(eq); ci = hinic_hwif_read_reg(hwdev->hwif, addr); addr = EQ_PROD_IDX_REG_ADDR(eq); pi = hinic_hwif_read_reg(hwdev->hwif, addr); aeqe_pos = GET_CURR_AEQ_ELEM(eq); sdk_err(hwdev->dev_hdl, "Aeq id: %d, ci: 0x%08x, pi: 0x%x, work_state: 0x%x, wrap: %d, desc: 0x%x\n", q_id, ci, pi, work_busy(&eq->aeq_work), eq->wrapped, be32_to_cpu(aeqe_pos->desc)); } } void hinic_dump_ceq_info(struct hinic_hwdev *hwdev) { struct hinic_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]; addr = EQ_CONS_IDX_REG_ADDR(eq); ci = hinic_hwif_read_reg(hwdev->hwif, addr); addr = EQ_PROD_IDX_REG_ADDR(eq); pi = hinic_hwif_read_reg(hwdev->hwif, addr); sdk_err(hwdev->dev_hdl, "Ceq id: %d, ci: 0x%08x, sw_ci: 0x%08x, pi: 0x%x, tasklet_state: 0x%lx, wrap: %d, ceqe: 0x%x\n", q_id, ci, eq->cons_idx, pi, tasklet_state(&eq->ceq_tasklet), eq->wrapped, be32_to_cpu(*(GET_CURR_CEQ_ELEM(eq)))); sdk_err(hwdev->dev_hdl, "Ceq last response hard interrupt time: %u\n", jiffies_to_msecs(jiffies - eq->hard_intr_jif)); sdk_err(hwdev->dev_hdl, "Ceq last response soft interrupt time: %u\n", jiffies_to_msecs(jiffies - eq->soft_intr_jif)); } } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_eqs.h000066400000000000000000000077051502027332500212530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_EQS_H #include #define HINIC_EQS_H #define HINIC_EQ_PAGE_SIZE 0x00001000 #define HINIC_HW_MAX_AEQS 4 #define HINIC_MAX_AEQS 3 #define HINIC_MAX_CEQS 32 #define HINIC_EQ_MAX_PAGES 8 #define HINIC_AEQE_SIZE 64 #define HINIC_CEQE_SIZE 4 #define HINIC_AEQE_DESC_SIZE 4 #define HINIC_AEQE_DATA_SIZE \ (HINIC_AEQE_SIZE - HINIC_AEQE_DESC_SIZE) #define HINIC_DEFAULT_AEQ_LEN 0x10000 #define HINIC_DEFAULT_CEQ_LEN 0x10000 #define HINIC_VMGW_DEFAULT_AEQ_LEN 128 #define HINIC_VMGW_DEFAULT_CEQ_LEN 1024 #define HINIC_MIN_AEQ_LEN 64 #define HINIC_MAX_AEQ_LEN (512 * 1024) #define HINIC_MIN_CEQ_LEN 64 #define HINIC_MAX_CEQ_LEN (1024 * 1024) #define HINIC_CEQ_ID_CMDQ 0 #define EQ_IRQ_NAME_LEN 64 enum hinic_eq_type { HINIC_AEQ, HINIC_CEQ }; enum hinic_eq_intr_mode { HINIC_INTR_MODE_ARMED, HINIC_INTR_MODE_ALWAYS, }; enum hinic_eq_ci_arm_state { HINIC_EQ_NOT_ARMED, HINIC_EQ_ARMED, }; struct hinic_eq { struct hinic_hwdev *hwdev; u16 q_id; enum hinic_eq_type type; u32 page_size; u32 orig_page_size; u32 eq_len; u32 cons_idx; u16 wrapped; u16 elem_size; u16 num_pages; u32 num_elem_in_pg; struct irq_info eq_irq; char irq_name[EQ_IRQ_NAME_LEN]; dma_addr_t *dma_addr; u8 **virt_addr; dma_addr_t *dma_addr_for_free; u8 **virt_addr_for_free; struct work_struct aeq_work; struct tasklet_struct ceq_tasklet; u64 hard_intr_jif; u64 soft_intr_jif; }; struct hinic_aeq_elem { u8 aeqe_data[HINIC_AEQE_DATA_SIZE]; u32 desc; }; enum hinic_aeq_cb_state { HINIC_AEQ_HW_CB_REG = 0, HINIC_AEQ_HW_CB_RUNNING, HINIC_AEQ_SW_CB_REG, HINIC_AEQ_SW_CB_RUNNING, }; struct hinic_aeqs { struct hinic_hwdev *hwdev; hinic_aeq_hwe_cb aeq_hwe_cb[HINIC_MAX_AEQ_EVENTS]; hinic_aeq_swe_cb aeq_swe_cb[HINIC_MAX_AEQ_SW_EVENTS]; unsigned long aeq_hw_cb_state[HINIC_MAX_AEQ_EVENTS]; unsigned long aeq_sw_cb_state[HINIC_MAX_AEQ_SW_EVENTS]; struct hinic_eq aeq[HINIC_MAX_AEQS]; u16 num_aeqs; struct workqueue_struct *workq; }; enum hinic_ceq_cb_state { HINIC_CEQ_CB_REG = 0, HINIC_CEQ_CB_RUNNING, }; struct hinic_ceqs { struct hinic_hwdev *hwdev; hinic_ceq_event_cb ceq_cb[HINIC_MAX_CEQ_EVENTS]; void *ceq_data[HINIC_MAX_CEQ_EVENTS]; unsigned long ceq_cb_state[HINIC_MAX_CEQ_EVENTS]; struct hinic_eq ceq[HINIC_MAX_CEQS]; u16 num_ceqs; }; enum hinic_msg_pipe_state { PIPE_STATE_IDLE, PIPE_STATE_BUSY, PIPE_STATE_SUSPEND, }; #define PIPE_CYCLE_MAX 10000 u32 hinic_func_busy_state_get(struct hinic_hwdev *hwdev); void hinic_func_busy_state_set(struct hinic_hwdev *hwdev, u32 cfg); u32 hinic_func_own_bit_get(struct hinic_hwdev *hwdev); void hinic_func_own_bit_set(struct hinic_hwdev *hwdev, u32 cfg); int hinic_aeqs_init(struct hinic_hwdev *hwdev, u16 num_aeqs, struct irq_info *msix_entries); void hinic_aeqs_free(struct hinic_hwdev *hwdev); int hinic_ceqs_init(struct hinic_hwdev *hwdev, u16 num_ceqs, struct irq_info *msix_entries); void hinic_ceqs_free(struct hinic_hwdev *hwdev); void hinic_get_ceq_irqs(struct hinic_hwdev *hwdev, struct irq_info *irqs, u16 *num_irqs); void hinic_get_aeq_irqs(struct hinic_hwdev *hwdev, struct irq_info *irqs, u16 *num_irqs); void hinic_dump_ceq_info(struct hinic_hwdev *hwdev); void hinic_dump_aeq_info(struct hinic_hwdev *hwdev); int hinic_reschedule_eq(struct hinic_hwdev *hwdev, enum hinic_eq_type type, u16 eq_id); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_ethtool.c000066400000000000000000002106171502027332500221320ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw_mgmt.h" #include "hinic_hw.h" #include "hinic_nic_cfg.h" #include "hinic_nic_dev.h" #include "hinic_dfx_def.h" #include "hinic_tx.h" #include "hinic_rx.h" #include "hinic_qp.h" #ifndef SET_ETHTOOL_OPS #define SET_ETHTOOL_OPS(netdev, ops) \ ((netdev)->ethtool_ops = (ops)) #endif struct hinic_stats { char name[ETH_GSTRING_LEN]; u32 size; int offset; }; #define ARRAY_LEN(arr) ((int)((int)sizeof(arr) / (int)sizeof(arr[0]))) #define HINIC_NETDEV_STAT(_stat_item) { \ .name = #_stat_item, \ .size = sizeof_field(struct rtnl_link_stats64, _stat_item), \ .offset = offsetof(struct rtnl_link_stats64, _stat_item) \ } static struct hinic_stats hinic_netdev_stats[] = { HINIC_NETDEV_STAT(rx_packets), HINIC_NETDEV_STAT(tx_packets), HINIC_NETDEV_STAT(rx_bytes), HINIC_NETDEV_STAT(tx_bytes), HINIC_NETDEV_STAT(rx_errors), HINIC_NETDEV_STAT(tx_errors), HINIC_NETDEV_STAT(rx_dropped), HINIC_NETDEV_STAT(tx_dropped), HINIC_NETDEV_STAT(multicast), HINIC_NETDEV_STAT(collisions), HINIC_NETDEV_STAT(rx_length_errors), HINIC_NETDEV_STAT(rx_over_errors), HINIC_NETDEV_STAT(rx_crc_errors), HINIC_NETDEV_STAT(rx_frame_errors), HINIC_NETDEV_STAT(rx_fifo_errors), HINIC_NETDEV_STAT(rx_missed_errors), HINIC_NETDEV_STAT(tx_aborted_errors), HINIC_NETDEV_STAT(tx_carrier_errors), HINIC_NETDEV_STAT(tx_fifo_errors), HINIC_NETDEV_STAT(tx_heartbeat_errors), }; #define HINIC_NIC_STAT(_stat_item) { \ .name = #_stat_item, \ .size = sizeof_field(struct hinic_nic_stats, _stat_item), \ .offset = offsetof(struct hinic_nic_stats, _stat_item) \ } static struct hinic_stats hinic_nic_dev_stats[] = { HINIC_NIC_STAT(netdev_tx_timeout), }; static struct hinic_stats hinic_nic_dev_stats_extern[] = { HINIC_NIC_STAT(tx_carrier_off_drop), HINIC_NIC_STAT(tx_invalid_qid), }; #define HINIC_RXQ_STAT(_stat_item) { \ .name = "rxq%d_"#_stat_item, \ .size = sizeof_field(struct hinic_rxq_stats, _stat_item), \ .offset = offsetof(struct hinic_rxq_stats, _stat_item) \ } #define HINIC_TXQ_STAT(_stat_item) { \ .name = "txq%d_"#_stat_item, \ .size = sizeof_field(struct hinic_txq_stats, _stat_item), \ .offset = offsetof(struct hinic_txq_stats, _stat_item) \ } /*lint -save -e786*/ static struct hinic_stats hinic_rx_queue_stats[] = { HINIC_RXQ_STAT(packets), HINIC_RXQ_STAT(bytes), HINIC_RXQ_STAT(errors), HINIC_RXQ_STAT(csum_errors), HINIC_RXQ_STAT(other_errors), HINIC_RXQ_STAT(dropped), HINIC_RXQ_STAT(rx_buf_empty), }; static struct hinic_stats hinic_rx_queue_stats_extern[] = { HINIC_RXQ_STAT(alloc_skb_err), HINIC_RXQ_STAT(alloc_rx_buf_err), HINIC_RXQ_STAT(map_rx_buf_err), }; static struct hinic_stats hinic_tx_queue_stats[] = { HINIC_TXQ_STAT(packets), HINIC_TXQ_STAT(bytes), HINIC_TXQ_STAT(busy), HINIC_TXQ_STAT(wake), HINIC_TXQ_STAT(dropped), HINIC_TXQ_STAT(big_frags_pkts), HINIC_TXQ_STAT(big_udp_pkts), }; static struct hinic_stats hinic_tx_queue_stats_extern[] = { HINIC_TXQ_STAT(ufo_pkt_unsupport), HINIC_TXQ_STAT(ufo_linearize_err), HINIC_TXQ_STAT(ufo_alloc_skb_err), HINIC_TXQ_STAT(skb_pad_err), HINIC_TXQ_STAT(frag_len_overflow), HINIC_TXQ_STAT(offload_cow_skb_err), HINIC_TXQ_STAT(alloc_cpy_frag_err), HINIC_TXQ_STAT(map_cpy_frag_err), HINIC_TXQ_STAT(map_frag_err), HINIC_TXQ_STAT(frag_size_err), HINIC_TXQ_STAT(unknown_tunnel_pkt), };/*lint -restore*/ #define HINIC_FUNC_STAT(_stat_item) { \ .name = #_stat_item, \ .size = sizeof_field(struct hinic_vport_stats, _stat_item), \ .offset = offsetof(struct hinic_vport_stats, _stat_item) \ } static struct hinic_stats hinic_function_stats[] = { HINIC_FUNC_STAT(tx_unicast_pkts_vport), HINIC_FUNC_STAT(tx_unicast_bytes_vport), HINIC_FUNC_STAT(tx_multicast_pkts_vport), HINIC_FUNC_STAT(tx_multicast_bytes_vport), HINIC_FUNC_STAT(tx_broadcast_pkts_vport), HINIC_FUNC_STAT(tx_broadcast_bytes_vport), HINIC_FUNC_STAT(rx_unicast_pkts_vport), HINIC_FUNC_STAT(rx_unicast_bytes_vport), HINIC_FUNC_STAT(rx_multicast_pkts_vport), HINIC_FUNC_STAT(rx_multicast_bytes_vport), HINIC_FUNC_STAT(rx_broadcast_pkts_vport), HINIC_FUNC_STAT(rx_broadcast_bytes_vport), HINIC_FUNC_STAT(tx_discard_vport), HINIC_FUNC_STAT(rx_discard_vport), HINIC_FUNC_STAT(tx_err_vport), HINIC_FUNC_STAT(rx_err_vport), }; #define HINIC_PORT_STAT(_stat_item) { \ .name = #_stat_item, \ .size = sizeof_field(struct hinic_phy_port_stats, _stat_item), \ .offset = offsetof(struct hinic_phy_port_stats, _stat_item) \ } static struct hinic_stats hinic_port_stats[] = { HINIC_PORT_STAT(mac_rx_total_pkt_num), HINIC_PORT_STAT(mac_rx_total_oct_num), HINIC_PORT_STAT(mac_rx_bad_pkt_num), HINIC_PORT_STAT(mac_rx_bad_oct_num), HINIC_PORT_STAT(mac_rx_good_pkt_num), HINIC_PORT_STAT(mac_rx_good_oct_num), HINIC_PORT_STAT(mac_rx_uni_pkt_num), HINIC_PORT_STAT(mac_rx_multi_pkt_num), HINIC_PORT_STAT(mac_rx_broad_pkt_num), HINIC_PORT_STAT(mac_tx_total_pkt_num), HINIC_PORT_STAT(mac_tx_total_oct_num), HINIC_PORT_STAT(mac_tx_bad_pkt_num), HINIC_PORT_STAT(mac_tx_bad_oct_num), HINIC_PORT_STAT(mac_tx_good_pkt_num), HINIC_PORT_STAT(mac_tx_good_oct_num), HINIC_PORT_STAT(mac_tx_uni_pkt_num), HINIC_PORT_STAT(mac_tx_multi_pkt_num), HINIC_PORT_STAT(mac_tx_broad_pkt_num), HINIC_PORT_STAT(mac_rx_fragment_pkt_num), HINIC_PORT_STAT(mac_rx_undersize_pkt_num), HINIC_PORT_STAT(mac_rx_undermin_pkt_num), HINIC_PORT_STAT(mac_rx_64_oct_pkt_num), HINIC_PORT_STAT(mac_rx_65_127_oct_pkt_num), HINIC_PORT_STAT(mac_rx_128_255_oct_pkt_num), HINIC_PORT_STAT(mac_rx_256_511_oct_pkt_num), HINIC_PORT_STAT(mac_rx_512_1023_oct_pkt_num), HINIC_PORT_STAT(mac_rx_1024_1518_oct_pkt_num), HINIC_PORT_STAT(mac_rx_1519_2047_oct_pkt_num), HINIC_PORT_STAT(mac_rx_2048_4095_oct_pkt_num), HINIC_PORT_STAT(mac_rx_4096_8191_oct_pkt_num), HINIC_PORT_STAT(mac_rx_8192_9216_oct_pkt_num), HINIC_PORT_STAT(mac_rx_9217_12287_oct_pkt_num), HINIC_PORT_STAT(mac_rx_12288_16383_oct_pkt_num), HINIC_PORT_STAT(mac_rx_1519_max_good_pkt_num), HINIC_PORT_STAT(mac_rx_1519_max_bad_pkt_num), HINIC_PORT_STAT(mac_rx_oversize_pkt_num), HINIC_PORT_STAT(mac_rx_jabber_pkt_num), HINIC_PORT_STAT(mac_rx_pause_num), HINIC_PORT_STAT(mac_rx_pfc_pkt_num), HINIC_PORT_STAT(mac_rx_pfc_pri0_pkt_num), HINIC_PORT_STAT(mac_rx_pfc_pri1_pkt_num), HINIC_PORT_STAT(mac_rx_pfc_pri2_pkt_num), HINIC_PORT_STAT(mac_rx_pfc_pri3_pkt_num), HINIC_PORT_STAT(mac_rx_pfc_pri4_pkt_num), HINIC_PORT_STAT(mac_rx_pfc_pri5_pkt_num), HINIC_PORT_STAT(mac_rx_pfc_pri6_pkt_num), HINIC_PORT_STAT(mac_rx_pfc_pri7_pkt_num), HINIC_PORT_STAT(mac_rx_control_pkt_num), HINIC_PORT_STAT(mac_rx_sym_err_pkt_num), HINIC_PORT_STAT(mac_rx_fcs_err_pkt_num), HINIC_PORT_STAT(mac_rx_send_app_good_pkt_num), HINIC_PORT_STAT(mac_rx_send_app_bad_pkt_num), HINIC_PORT_STAT(mac_tx_fragment_pkt_num), HINIC_PORT_STAT(mac_tx_undersize_pkt_num), HINIC_PORT_STAT(mac_tx_undermin_pkt_num), HINIC_PORT_STAT(mac_tx_64_oct_pkt_num), HINIC_PORT_STAT(mac_tx_65_127_oct_pkt_num), HINIC_PORT_STAT(mac_tx_128_255_oct_pkt_num), HINIC_PORT_STAT(mac_tx_256_511_oct_pkt_num), HINIC_PORT_STAT(mac_tx_512_1023_oct_pkt_num), HINIC_PORT_STAT(mac_tx_1024_1518_oct_pkt_num), HINIC_PORT_STAT(mac_tx_1519_2047_oct_pkt_num), HINIC_PORT_STAT(mac_tx_2048_4095_oct_pkt_num), HINIC_PORT_STAT(mac_tx_4096_8191_oct_pkt_num), HINIC_PORT_STAT(mac_tx_8192_9216_oct_pkt_num), HINIC_PORT_STAT(mac_tx_9217_12287_oct_pkt_num), HINIC_PORT_STAT(mac_tx_12288_16383_oct_pkt_num), HINIC_PORT_STAT(mac_tx_1519_max_good_pkt_num), HINIC_PORT_STAT(mac_tx_1519_max_bad_pkt_num), HINIC_PORT_STAT(mac_tx_oversize_pkt_num), HINIC_PORT_STAT(mac_tx_jabber_pkt_num), HINIC_PORT_STAT(mac_tx_pause_num), HINIC_PORT_STAT(mac_tx_pfc_pkt_num), HINIC_PORT_STAT(mac_tx_pfc_pri0_pkt_num), HINIC_PORT_STAT(mac_tx_pfc_pri1_pkt_num), HINIC_PORT_STAT(mac_tx_pfc_pri2_pkt_num), HINIC_PORT_STAT(mac_tx_pfc_pri3_pkt_num), HINIC_PORT_STAT(mac_tx_pfc_pri4_pkt_num), HINIC_PORT_STAT(mac_tx_pfc_pri5_pkt_num), HINIC_PORT_STAT(mac_tx_pfc_pri6_pkt_num), HINIC_PORT_STAT(mac_tx_pfc_pri7_pkt_num), HINIC_PORT_STAT(mac_tx_control_pkt_num), HINIC_PORT_STAT(mac_tx_err_all_pkt_num), HINIC_PORT_STAT(mac_tx_from_app_good_pkt_num), HINIC_PORT_STAT(mac_tx_from_app_bad_pkt_num), }; u32 hinic_get_io_stats_size(struct hinic_nic_dev *nic_dev) { return ARRAY_LEN(hinic_nic_dev_stats) + ARRAY_LEN(hinic_nic_dev_stats_extern) + (ARRAY_LEN(hinic_tx_queue_stats) + ARRAY_LEN(hinic_tx_queue_stats_extern) + ARRAY_LEN(hinic_rx_queue_stats) + ARRAY_LEN(hinic_rx_queue_stats_extern)) * nic_dev->max_qps; } #define GET_VALUE_OF_PTR(size, ptr) ( \ (size) == sizeof(u64) ? *(u64 *)(ptr) : \ (size) == sizeof(u32) ? *(u32 *)(ptr) : \ (size) == sizeof(u16) ? *(u16 *)(ptr) : *(u8 *)(ptr) \ ) #define DEV_STATS_PACK(items, item_idx, array, stats_ptr) { \ int j; \ for (j = 0; j < ARRAY_LEN(array); j++) { \ memcpy((items)[item_idx].name, (array)[j].name, \ HINIC_SHOW_ITEM_LEN); \ (items)[item_idx].hexadecimal = 0; \ (items)[item_idx].value = \ GET_VALUE_OF_PTR((array)[j].size, \ (char *)(stats_ptr) + (array)[j].offset); \ item_idx++; \ } \ } #define QUEUE_STATS_PACK(items, item_idx, array, stats_ptr, qid) { \ int j, err; \ for (j = 0; j < ARRAY_LEN(array); j++) { \ memcpy((items)[item_idx].name, (array)[j].name, \ HINIC_SHOW_ITEM_LEN); \ err = snprintf((items)[item_idx].name, HINIC_SHOW_ITEM_LEN,\ (array)[j].name, (qid)); \ if (err <= 0 || err >= HINIC_SHOW_ITEM_LEN) \ pr_err("Failed snprintf: func_ret(%d), dest_len(%d)\n",\ err, HINIC_SHOW_ITEM_LEN); \ (items)[item_idx].hexadecimal = 0; \ (items)[item_idx].value = \ GET_VALUE_OF_PTR((array)[j].size, \ (char *)(stats_ptr) + (array)[j].offset); \ item_idx++; \ } \ } void hinic_get_io_stats(struct hinic_nic_dev *nic_dev, struct hinic_show_item *items) { int item_idx = 0; u16 qid; DEV_STATS_PACK(items, item_idx, hinic_nic_dev_stats, &nic_dev->stats); DEV_STATS_PACK(items, item_idx, hinic_nic_dev_stats_extern, &nic_dev->stats); for (qid = 0; qid < nic_dev->max_qps; qid++) { QUEUE_STATS_PACK(items, item_idx, hinic_tx_queue_stats, &nic_dev->txqs[qid].txq_stats, qid); QUEUE_STATS_PACK(items, item_idx, hinic_tx_queue_stats_extern, &nic_dev->txqs[qid].txq_stats, qid); } for (qid = 0; qid < nic_dev->max_qps; qid++) { QUEUE_STATS_PACK(items, item_idx, hinic_rx_queue_stats, &nic_dev->rxqs[qid].rxq_stats, qid); QUEUE_STATS_PACK(items, item_idx, hinic_rx_queue_stats_extern, &nic_dev->rxqs[qid].rxq_stats, qid); } } #define LP_DEFAULT_TIME 5 /* seconds */ #define LP_PKT_LEN 1514 #define OBJ_STR_MAX_LEN 32 #define SET_LINK_STR_MAX_LEN 128 #define PORT_DOWN_ERR_IDX 0 enum diag_test_index { INTERNAL_LP_TEST = 0, EXTERNAL_LP_TEST = 1, DIAG_TEST_MAX = 2, }; static char hinic_test_strings[][ETH_GSTRING_LEN] = { "Internal lb test (on/offline)", "External lb test (external_lb)", }; struct hw2ethtool_link_mode { enum ethtool_link_mode_bit_indices link_mode_bit; u32 speed; enum hinic_link_mode hw_link_mode; }; static struct hw2ethtool_link_mode hw_to_ethtool_link_mode_table[HINIC_LINK_MODE_NUMBERS] = { { .link_mode_bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, .speed = SPEED_10000, .hw_link_mode = HINIC_10GE_BASE_KR, }, { .link_mode_bit = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, .speed = SPEED_40000, .hw_link_mode = HINIC_40GE_BASE_KR4, }, { .link_mode_bit = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, .speed = SPEED_40000, .hw_link_mode = HINIC_40GE_BASE_CR4, }, { .link_mode_bit = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, .speed = SPEED_100000, .hw_link_mode = HINIC_100GE_BASE_KR4, }, { .link_mode_bit = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, .speed = SPEED_100000, .hw_link_mode = HINIC_100GE_BASE_CR4, }, { .link_mode_bit = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, .speed = SPEED_25000, .hw_link_mode = HINIC_25GE_BASE_KR_S, }, { .link_mode_bit = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, .speed = SPEED_25000, .hw_link_mode = HINIC_25GE_BASE_CR_S, }, { .link_mode_bit = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, .speed = SPEED_25000, .hw_link_mode = HINIC_25GE_BASE_KR, }, { .link_mode_bit = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, .speed = SPEED_25000, .hw_link_mode = HINIC_25GE_BASE_CR, }, { .link_mode_bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, .speed = SPEED_1000, .hw_link_mode = HINIC_GE_BASE_KX, }, }; u32 hw_to_ethtool_speed[LINK_SPEED_LEVELS] = { SPEED_10, SPEED_100, SPEED_1000, SPEED_10000, SPEED_25000, SPEED_40000, SPEED_100000 }; static int hinic_ethtool_to_hw_speed_level(u32 speed) { int i; for (i = 0; i < LINK_SPEED_LEVELS; i++) { if (hw_to_ethtool_speed[i] == speed) break; } return i; } static int hinic_get_link_mode_index(enum hinic_link_mode link_mode) { int i = 0; for (i = 0; i < HINIC_LINK_MODE_NUMBERS; i++) { if (link_mode == hw_to_ethtool_link_mode_table[i].hw_link_mode) break; } return i; } static int hinic_is_support_speed(enum hinic_link_mode supported_link, u32 speed) { enum hinic_link_mode link_mode; int idx; for (link_mode = 0; link_mode < HINIC_LINK_MODE_NUMBERS; link_mode++) { if (!(supported_link & ((u32)1 << link_mode))) continue; idx = hinic_get_link_mode_index(link_mode); if (idx >= HINIC_LINK_MODE_NUMBERS) continue; if (hw_to_ethtool_link_mode_table[idx].speed == speed) return 1; } return 0; } #define GET_SUPPORTED_MODE 0 #define GET_ADVERTISED_MODE 1 struct cmd_link_settings { u64 supported; u64 advertising; u32 speed; u8 duplex; u8 port; u8 autoneg; }; #define ETHTOOL_ADD_SUPPORTED_SPEED_LINK_MODE(ecmd, mode) \ ((ecmd)->supported |= \ (1UL << hw_to_ethtool_link_mode_table[mode].link_mode_bit)) #define ETHTOOL_ADD_ADVERTISED_SPEED_LINK_MODE(ecmd, mode) \ ((ecmd)->advertising |= \ (1UL << hw_to_ethtool_link_mode_table[mode].link_mode_bit)) #define ETHTOOL_ADD_SUPPORTED_LINK_MODE(ecmd, mode) \ ((ecmd)->supported |= SUPPORTED_##mode) #define ETHTOOL_ADD_ADVERTISED_LINK_MODE(ecmd, mode) \ ((ecmd)->advertising |= ADVERTISED_##mode) #define ETHTOOL_TEST_LINK_MODE_SUPPORTED(ecmd, mode) \ ((ecmd)->supported & SUPPORTED_##Autoneg) static void hinic_link_port_type(struct cmd_link_settings *link_settings, enum hinic_port_type port_type) { switch (port_type) { case HINIC_PORT_ELEC: case HINIC_PORT_TP: ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, TP); ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, TP); link_settings->port = PORT_TP; break; case HINIC_PORT_AOC: case HINIC_PORT_FIBRE: ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, FIBRE); ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, FIBRE); link_settings->port = PORT_FIBRE; break; case HINIC_PORT_COPPER: ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, FIBRE); ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, FIBRE); link_settings->port = PORT_DA; break; case HINIC_PORT_BACKPLANE: ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, Backplane); ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, Backplane); link_settings->port = PORT_NONE; break; default: link_settings->port = PORT_OTHER; break; } } static void hinic_add_ethtool_link_mode(struct cmd_link_settings *link_settings, enum hinic_link_mode hw_link_mode, u32 name) { enum hinic_link_mode link_mode; int idx = 0; for (link_mode = 0; link_mode < HINIC_LINK_MODE_NUMBERS; link_mode++) { if (hw_link_mode & ((u32)1 << link_mode)) { idx = hinic_get_link_mode_index(link_mode); if (idx >= HINIC_LINK_MODE_NUMBERS) continue; if (name == GET_SUPPORTED_MODE) ETHTOOL_ADD_SUPPORTED_SPEED_LINK_MODE (link_settings, idx); else ETHTOOL_ADD_ADVERTISED_SPEED_LINK_MODE (link_settings, idx); } } } static int hinic_link_speed_set(struct hinic_nic_dev *nic_dev, struct cmd_link_settings *link_settings, struct nic_port_info *port_info) { struct net_device *netdev = nic_dev->netdev; enum hinic_link_mode supported_link = 0, advertised_link = 0; u8 link_state = 0; int err; err = hinic_get_link_mode(nic_dev->hwdev, &supported_link, &advertised_link); if (err || supported_link == HINIC_SUPPORTED_UNKNOWN || advertised_link == HINIC_SUPPORTED_UNKNOWN) { nicif_err(nic_dev, drv, netdev, "Failed to get supported link modes\n"); return err; } hinic_add_ethtool_link_mode(link_settings, supported_link, GET_SUPPORTED_MODE); hinic_add_ethtool_link_mode(link_settings, advertised_link, GET_ADVERTISED_MODE); err = hinic_get_link_state(nic_dev->hwdev, &link_state); if (!err && link_state) { link_settings->speed = port_info->speed < LINK_SPEED_LEVELS ? 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; } return 0; } static int get_link_settings(struct net_device *netdev, struct cmd_link_settings *link_settings) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct nic_port_info port_info = {0}; struct nic_pause_config nic_pause = {0}; int err; err = hinic_get_port_info(nic_dev->hwdev, &port_info); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to get port info\n"); return err; } err = hinic_link_speed_set(nic_dev, link_settings, &port_info); if (err) return err; hinic_link_port_type(link_settings, port_info.port_type); link_settings->autoneg = port_info.autoneg_state; if (port_info.autoneg_cap) ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, Autoneg); if (port_info.autoneg_state) ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, Autoneg); if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) { err = hinic_get_pause_info(nic_dev->hwdev, &nic_pause); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to get pauseparam from hw\n"); return err; } ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, Pause); if (nic_pause.rx_pause && nic_pause.tx_pause) { ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, Pause); } else if (nic_pause.tx_pause) { ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, Asym_Pause); } else if (nic_pause.rx_pause) { ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, Pause); ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, Asym_Pause); } } return 0; } static int hinic_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd) { struct cmd_link_settings settings = {0}; struct ethtool_link_settings *base = &cmd->base; int err; ethtool_link_ksettings_zero_link_mode(cmd, supported); ethtool_link_ksettings_zero_link_mode(cmd, advertising); err = get_link_settings(netdev, &settings); if (err) return err; bitmap_copy(cmd->link_modes.supported, (unsigned long *)&settings.supported, __ETHTOOL_LINK_MODE_MASK_NBITS); bitmap_copy(cmd->link_modes.advertising, (unsigned long *)&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 int hinic_is_speed_legal(struct hinic_nic_dev *nic_dev, u32 speed) { struct net_device *netdev = nic_dev->netdev; enum hinic_link_mode supported_link = 0, advertised_link = 0; enum nic_speed_level speed_level = 0; int err; err = hinic_get_link_mode(nic_dev->hwdev, &supported_link, &advertised_link); if (err || supported_link == HINIC_SUPPORTED_UNKNOWN || advertised_link == HINIC_SUPPORTED_UNKNOWN) { nicif_err(nic_dev, drv, netdev, "Failed to get supported link modes\n"); return -EAGAIN; } speed_level = hinic_ethtool_to_hw_speed_level(speed); if (speed_level >= LINK_SPEED_LEVELS || !hinic_is_support_speed(supported_link, speed)) { nicif_err(nic_dev, drv, netdev, "Not supported speed: %d\n", speed); return -EINVAL; } return 0; } static int hinic_set_settings_to_hw(struct hinic_nic_dev *nic_dev, u32 set_settings, u8 autoneg, u32 speed) { struct net_device *netdev = nic_dev->netdev; struct hinic_link_ksettings settings = {0}; enum nic_speed_level speed_level = 0; char set_link_str[SET_LINK_STR_MAX_LEN] = {0}; int err = 0; err = snprintf(set_link_str, sizeof(set_link_str), "%s", (set_settings & HILINK_LINK_SET_AUTONEG) ? (autoneg ? "autong enable " : "autong disable ") : ""); if (err < 0 || err >= SET_LINK_STR_MAX_LEN) { nicif_err(nic_dev, drv, netdev, "Failed to snprintf link state, function return(%d) and dest_len(%d)\n", err, SET_LINK_STR_MAX_LEN); return -EFAULT; } if (set_settings & HILINK_LINK_SET_SPEED) { speed_level = hinic_ethtool_to_hw_speed_level(speed); err = snprintf(set_link_str, sizeof(set_link_str), "%sspeed %d ", set_link_str, speed); if (err <= 0 || err >= SET_LINK_STR_MAX_LEN) { nicif_err(nic_dev, drv, netdev, "Failed to snprintf link speed, function return(%d) and dest_len(%d)\n", err, SET_LINK_STR_MAX_LEN); return -EFAULT; } } settings.valid_bitmap = set_settings; settings.autoneg = autoneg; settings.speed = speed_level; err = hinic_set_link_settings(nic_dev->hwdev, &settings); if (err != HINIC_MGMT_CMD_UNSUPPORTED) { if (err) nicif_err(nic_dev, drv, netdev, "Set %sfailed\n", set_link_str); else nicif_info(nic_dev, drv, netdev, "Set %ssuccess\n", set_link_str); return err; } if (set_settings & HILINK_LINK_SET_AUTONEG) { err = hinic_set_autoneg(nic_dev->hwdev, (autoneg == AUTONEG_ENABLE)); if (err) nicif_err(nic_dev, drv, netdev, "%s autoneg failed\n", (autoneg == AUTONEG_ENABLE) ? "Enable" : "Disable"); else nicif_info(nic_dev, drv, netdev, "%s autoneg success\n", (autoneg == AUTONEG_ENABLE) ? "Enable" : "Disable"); } if (!err && (set_settings & HILINK_LINK_SET_SPEED)) { err = hinic_set_speed(nic_dev->hwdev, speed_level); if (err) nicif_err(nic_dev, drv, netdev, "Set speed %d failed\n", speed); else nicif_info(nic_dev, drv, netdev, "Set speed %d success\n", speed); } return err; } static int set_link_settings(struct net_device *netdev, u8 autoneg, u32 speed) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct nic_port_info port_info = {0}; u32 set_settings = 0; int err = 0; if (!FUNC_SUPPORT_PORT_SETTING(nic_dev->hwdev)) { nicif_err(nic_dev, drv, netdev, "Not support set link settings\n"); return -EOPNOTSUPP; } err = hinic_get_port_info(nic_dev->hwdev, &port_info); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to get current settings\n"); return -EAGAIN; } /* Alwayse set autonegation */ if (port_info.autoneg_cap) set_settings |= HILINK_LINK_SET_AUTONEG; if (autoneg == AUTONEG_ENABLE) { if (!port_info.autoneg_cap) { nicif_err(nic_dev, drv, netdev, "Not support autoneg\n"); return -EOPNOTSUPP; } } else if (speed != (u32)SPEED_UNKNOWN) { /* Set speed only when autoneg is disable */ err = hinic_is_speed_legal(nic_dev, speed); if (err) return err; set_settings |= HILINK_LINK_SET_SPEED; } else { nicif_err(nic_dev, drv, netdev, "Need to set speed when autoneg is off\n"); return -EOPNOTSUPP; } if (set_settings) err = hinic_set_settings_to_hw(nic_dev, set_settings, autoneg, speed); else nicif_info(nic_dev, drv, netdev, "Nothing changed, exiting without setting anything\n"); return err; } static int hinic_set_link_ksettings(struct net_device *netdev, const struct ethtool_link_ksettings *cmd) { /* Only support to set autoneg and speed */ return set_link_settings(netdev, cmd->base.autoneg, cmd->base.speed); } static void hinic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct pci_dev *pdev = nic_dev->pdev; u8 mgmt_ver[HINIC_MGMT_VERSION_MAX_LEN] = {0}; int err; strscpy(info->driver, HINIC_DRV_NAME, sizeof(info->driver)); strscpy(info->version, HINIC_DRV_VERSION, sizeof(info->version)); strscpy(info->bus_info, pci_name(pdev), sizeof(info->bus_info)); err = hinic_get_mgmt_version(nic_dev->hwdev, mgmt_ver); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to get fw version\n"); return; } err = snprintf(info->fw_version, sizeof(info->fw_version), "%s", mgmt_ver); if (err <= 0 || err >= (int)sizeof(info->fw_version)) nicif_err(nic_dev, drv, netdev, "Failed to snprintf fw_version, function return(%d) and dest_len(%d)\n", err, (int)sizeof(info->fw_version)); } static u32 hinic_get_msglevel(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); return nic_dev->msg_enable; } static void hinic_set_msglevel(struct net_device *netdev, u32 data) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); nic_dev->msg_enable = data; nicif_info(nic_dev, drv, netdev, "Set message level: 0x%x\n", data); } static int hinic_nway_reset(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct nic_port_info port_info = {0}; int err; if (!FUNC_SUPPORT_PORT_SETTING(nic_dev->hwdev)) { nicif_err(nic_dev, drv, netdev, "Current function don't support to restart autoneg\n"); return -EOPNOTSUPP; } err = hinic_get_port_info(nic_dev->hwdev, &port_info); if (err) { nicif_err(nic_dev, drv, netdev, "Get autonegotiation state failed\n"); return -EFAULT; } if (!port_info.autoneg_state) { nicif_err(nic_dev, drv, netdev, "Autonegotiation is off, don't support to restart it\n"); return -EINVAL; } err = hinic_set_autoneg(nic_dev->hwdev, true); if (err) { nicif_err(nic_dev, drv, netdev, "Restart autonegotiation failed\n"); return -EFAULT; } nicif_info(nic_dev, drv, netdev, "Restart autonegotiation success\n"); return 0; } static void hinic_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring, struct netlink_ext_ack *extack) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); ring->rx_max_pending = HINIC_MAX_QUEUE_DEPTH; ring->tx_max_pending = HINIC_MAX_QUEUE_DEPTH; ring->rx_pending = nic_dev->rxqs[0].q_depth; ring->tx_pending = nic_dev->txqs[0].q_depth; } static void hinic_update_qp_depth(struct hinic_nic_dev *nic_dev, u16 sq_depth, u16 rq_depth) { u16 i; nic_dev->sq_depth = sq_depth; nic_dev->rq_depth = rq_depth; for (i = 0; i < nic_dev->max_qps; i++) { nic_dev->txqs[i].q_depth = sq_depth; nic_dev->txqs[i].q_mask = sq_depth - 1; nic_dev->rxqs[i].q_depth = rq_depth; nic_dev->rxqs[i].q_mask = rq_depth - 1; } } static int hinic_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring, struct netlink_ext_ack *extack) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 new_sq_depth, new_rq_depth; int err; if (ring->rx_jumbo_pending || ring->rx_mini_pending) { nicif_err(nic_dev, drv, netdev, "Unsupported rx_jumbo_pending/rx_mini_pending\n"); return -EINVAL; } if (ring->tx_pending > HINIC_MAX_QUEUE_DEPTH || ring->tx_pending < HINIC_MIN_QUEUE_DEPTH || ring->rx_pending > HINIC_MAX_QUEUE_DEPTH || ring->rx_pending < HINIC_MIN_QUEUE_DEPTH) { nicif_err(nic_dev, drv, netdev, "Queue depth out of range [%d-%d]\n", HINIC_MIN_QUEUE_DEPTH, HINIC_MAX_QUEUE_DEPTH); return -EINVAL; } new_sq_depth = (u16)(1U << (u16)ilog2(ring->tx_pending)); new_rq_depth = (u16)(1U << (u16)ilog2(ring->rx_pending)); if (new_sq_depth == nic_dev->sq_depth && new_rq_depth == nic_dev->rq_depth) return 0; nicif_info(nic_dev, drv, netdev, "Change Tx/Rx ring depth from %d/%d to %d/%d\n", nic_dev->sq_depth, nic_dev->rq_depth, new_sq_depth, new_rq_depth); if (!netif_running(netdev)) { hinic_update_qp_depth(nic_dev, new_sq_depth, new_rq_depth); } else { nicif_info(nic_dev, drv, netdev, "Restarting netdev\n"); err = hinic_close(netdev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to close netdev\n"); return -EFAULT; } hinic_update_qp_depth(nic_dev, new_sq_depth, new_rq_depth); err = hinic_open(netdev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to open netdev\n"); return -EFAULT; } } return 0; } static u16 hinic_max_channels(struct hinic_nic_dev *nic_dev) { u8 tcs = (u8)netdev_get_num_tc(nic_dev->netdev); return tcs ? nic_dev->max_qps / tcs : nic_dev->max_qps; } static u16 hinic_curr_channels(struct hinic_nic_dev *nic_dev) { if (netif_running(nic_dev->netdev)) return nic_dev->num_rss ? nic_dev->num_rss : 1; else return min_t(u16, hinic_max_channels(nic_dev), nic_dev->rss_limit); } static void hinic_get_channels(struct net_device *netdev, struct ethtool_channels *channels) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); channels->max_rx = 0; channels->max_tx = 0; channels->max_other = 0; channels->max_combined = hinic_max_channels(nic_dev); channels->rx_count = 0; channels->tx_count = 0; channels->other_count = 0; channels->combined_count = hinic_curr_channels(nic_dev); } void hinic_update_num_qps(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 num_qps; u8 tcs; /* change num_qps to change counter in ethtool -S */ tcs = (u8)netdev_get_num_tc(nic_dev->netdev); num_qps = (u16)(nic_dev->rss_limit * (tcs ? tcs : 1)); nic_dev->num_qps = min_t(u16, nic_dev->max_qps, num_qps); } static int hinic_set_channels(struct net_device *netdev, struct ethtool_channels *channels) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); unsigned int count = channels->combined_count; int err; if (!count) { nicif_err(nic_dev, drv, netdev, "Unsupported combined_count=0\n"); return -EINVAL; } if (channels->tx_count || channels->rx_count || channels->other_count) { nicif_err(nic_dev, drv, netdev, "Setting rx/tx/other count not supported\n"); return -EINVAL; } if (!test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) { nicif_err(nic_dev, drv, netdev, "This function don't support RSS, only support 1 queue pair\n"); return -EOPNOTSUPP; } if (count > hinic_max_channels(nic_dev)) { nicif_err(nic_dev, drv, netdev, "Combined count %d exceed limit %d\n", count, hinic_max_channels(nic_dev)); return -EINVAL; } nicif_info(nic_dev, drv, netdev, "Set max combined queue number from %d to %d\n", nic_dev->rss_limit, count); nic_dev->rss_limit = (u16)count; if (netif_running(netdev)) { nicif_info(nic_dev, drv, netdev, "Restarting netdev\n"); err = hinic_close(netdev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to close netdev\n"); return -EFAULT; } /* Discard user configured rss */ hinic_set_default_rss_indir(netdev); err = hinic_open(netdev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to open netdev\n"); return -EFAULT; } } else { /* Discard user configured rss */ hinic_set_default_rss_indir(netdev); hinic_update_num_qps(netdev); } return 0; } static int hinic_get_sset_count(struct net_device *netdev, int sset) { int count = 0, q_num = 0; struct hinic_nic_dev *nic_dev = netdev_priv(netdev); switch (sset) { case ETH_SS_TEST: return ARRAY_LEN(hinic_test_strings); case ETH_SS_STATS: q_num = nic_dev->num_qps; count = ARRAY_LEN(hinic_netdev_stats) + ARRAY_LEN(hinic_nic_dev_stats) + (ARRAY_LEN(hinic_tx_queue_stats) + ARRAY_LEN(hinic_rx_queue_stats)) * q_num; if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) count += ARRAY_LEN(hinic_function_stats); if (!HINIC_FUNC_IS_VF(nic_dev->hwdev) && FUNC_SUPPORT_PORT_SETTING(nic_dev->hwdev)) count += ARRAY_LEN(hinic_port_stats); return count; default: return -EOPNOTSUPP; } } #define COALESCE_ALL_QUEUE 0xFFFF #define COALESCE_MAX_PENDING_LIMIT (255 * COALESCE_PENDING_LIMIT_UNIT) #define COALESCE_MAX_TIMER_CFG (255 * COALESCE_TIMER_CFG_UNIT) #define COALESCE_PENDING_LIMIT_UNIT 8 #define COALESCE_TIMER_CFG_UNIT 9 static int __hinic_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal, u16 queue) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_intr_coal_info *interrupt_info; if (queue == COALESCE_ALL_QUEUE) { /* get tx/rx irq0 as default parameters */ interrupt_info = &nic_dev->intr_coalesce[0]; } else { if (queue >= nic_dev->num_qps) { nicif_err(nic_dev, drv, netdev, "Invalid queue_id: %d\n", queue); return -EINVAL; } interrupt_info = &nic_dev->intr_coalesce[queue]; } /* coalescs_timer is in unit of 9us */ coal->rx_coalesce_usecs = interrupt_info->coalesce_timer_cfg * COALESCE_TIMER_CFG_UNIT; /* coalescs_frams is in unit of 8 */ coal->rx_max_coalesced_frames = interrupt_info->pending_limt * COALESCE_PENDING_LIMIT_UNIT; /* tx/rx use the same interrupt */ coal->tx_coalesce_usecs = coal->rx_coalesce_usecs; coal->tx_max_coalesced_frames = coal->rx_max_coalesced_frames; coal->use_adaptive_rx_coalesce = nic_dev->adaptive_rx_coal; coal->pkt_rate_high = (u32)interrupt_info->pkt_rate_high; coal->rx_coalesce_usecs_high = interrupt_info->rx_usecs_high * COALESCE_TIMER_CFG_UNIT; coal->rx_max_coalesced_frames_high = interrupt_info->rx_pending_limt_high * COALESCE_PENDING_LIMIT_UNIT; coal->pkt_rate_low = (u32)interrupt_info->pkt_rate_low; coal->rx_coalesce_usecs_low = interrupt_info->rx_usecs_low * COALESCE_TIMER_CFG_UNIT; coal->rx_max_coalesced_frames_low = interrupt_info->rx_pending_limt_low * COALESCE_PENDING_LIMIT_UNIT; return 0; } static int set_queue_coalesce(struct hinic_nic_dev *nic_dev, u16 q_id, struct hinic_intr_coal_info *coal) { struct hinic_intr_coal_info *intr_coal; struct nic_interrupt_info interrupt_info = {0}; struct net_device *netdev = nic_dev->netdev; int err; intr_coal = &nic_dev->intr_coalesce[q_id]; if (intr_coal->coalesce_timer_cfg != coal->coalesce_timer_cfg || intr_coal->pending_limt != coal->pending_limt) intr_coal->user_set_intr_coal_flag = 1; intr_coal->coalesce_timer_cfg = coal->coalesce_timer_cfg; intr_coal->pending_limt = coal->pending_limt; intr_coal->pkt_rate_low = coal->pkt_rate_low; intr_coal->rx_usecs_low = coal->rx_usecs_low; intr_coal->rx_pending_limt_low = coal->rx_pending_limt_low; intr_coal->pkt_rate_high = coal->pkt_rate_high; intr_coal->rx_usecs_high = coal->rx_usecs_high; intr_coal->rx_pending_limt_high = coal->rx_pending_limt_high; /* netdev not running or qp not in using, * don't need to set coalesce to hw */ if (!test_bit(HINIC_INTF_UP, &nic_dev->flags) || q_id >= nic_dev->num_qps || nic_dev->adaptive_rx_coal) return 0; interrupt_info.msix_index = nic_dev->irq_cfg[q_id].msix_entry_idx; interrupt_info.lli_set = 0; interrupt_info.interrupt_coalesc_set = 1; interrupt_info.coalesc_timer_cfg = intr_coal->coalesce_timer_cfg; interrupt_info.pending_limt = intr_coal->pending_limt; interrupt_info.resend_timer_cfg = intr_coal->resend_timer_cfg; nic_dev->rxqs[q_id].last_coalesc_timer_cfg = intr_coal->coalesce_timer_cfg; nic_dev->rxqs[q_id].last_pending_limt = intr_coal->pending_limt; err = hinic_set_interrupt_cfg(nic_dev->hwdev, interrupt_info); if (err) nicif_warn(nic_dev, drv, netdev, "Failed to set queue%d coalesce", q_id); return err; } static int is_coalesce_legal(struct net_device *netdev, const struct ethtool_coalesce *coal) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct ethtool_coalesce tmp_coal = {0}; if (coal->rx_coalesce_usecs != coal->tx_coalesce_usecs) { nicif_err(nic_dev, drv, netdev, "tx-usecs must be equal to rx-usecs\n"); return -EINVAL; } if (coal->rx_max_coalesced_frames != coal->tx_max_coalesced_frames) { nicif_err(nic_dev, drv, netdev, "tx-frames must be equal to rx-frames\n"); return -EINVAL; } tmp_coal.cmd = coal->cmd; tmp_coal.rx_coalesce_usecs = coal->rx_coalesce_usecs; tmp_coal.rx_max_coalesced_frames = coal->rx_max_coalesced_frames; tmp_coal.tx_coalesce_usecs = coal->tx_coalesce_usecs; tmp_coal.tx_max_coalesced_frames = coal->tx_max_coalesced_frames; tmp_coal.use_adaptive_rx_coalesce = coal->use_adaptive_rx_coalesce; tmp_coal.pkt_rate_low = coal->pkt_rate_low; tmp_coal.rx_coalesce_usecs_low = coal->rx_coalesce_usecs_low; tmp_coal.rx_max_coalesced_frames_low = coal->rx_max_coalesced_frames_low; tmp_coal.pkt_rate_high = coal->pkt_rate_high; tmp_coal.rx_coalesce_usecs_high = coal->rx_coalesce_usecs_high; tmp_coal.rx_max_coalesced_frames_high = coal->rx_max_coalesced_frames_high; if (memcmp(coal, &tmp_coal, sizeof(struct ethtool_coalesce))) { nicif_err(nic_dev, drv, netdev, "Only support to change rx/tx-usecs and rx/tx-frames\n"); return -EOPNOTSUPP; } if (coal->rx_coalesce_usecs > COALESCE_MAX_TIMER_CFG) { nicif_err(nic_dev, drv, netdev, "rx_coalesce_usecs out of range[%d-%d]\n", 0, COALESCE_MAX_TIMER_CFG); return -EOPNOTSUPP; } if (coal->rx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT) { nicif_err(nic_dev, drv, netdev, "rx_max_coalesced_frames out of range[%d-%d]\n", 0, COALESCE_MAX_PENDING_LIMIT); return -EOPNOTSUPP; } if (coal->rx_coalesce_usecs_low > COALESCE_MAX_TIMER_CFG) { nicif_err(nic_dev, drv, netdev, "rx_coalesce_usecs_low out of range[%d-%d]\n", 0, COALESCE_MAX_TIMER_CFG); return -EOPNOTSUPP; } if (coal->rx_max_coalesced_frames_low > COALESCE_MAX_PENDING_LIMIT) { nicif_err(nic_dev, drv, netdev, "rx_max_coalesced_frames_low out of range[%d-%d]\n", 0, COALESCE_MAX_PENDING_LIMIT); return -EOPNOTSUPP; } if (coal->rx_coalesce_usecs_high > COALESCE_MAX_TIMER_CFG) { nicif_err(nic_dev, drv, netdev, "rx_coalesce_usecs_high out of range[%d-%d]\n", 0, COALESCE_MAX_TIMER_CFG); return -EOPNOTSUPP; } if (coal->rx_max_coalesced_frames_high > COALESCE_MAX_PENDING_LIMIT) { nicif_err(nic_dev, drv, netdev, "rx_max_coalesced_frames_high out of range[%d-%d]\n", 0, COALESCE_MAX_PENDING_LIMIT); return -EOPNOTSUPP; } if (coal->rx_coalesce_usecs_low / COALESCE_TIMER_CFG_UNIT >= coal->rx_coalesce_usecs_high / COALESCE_TIMER_CFG_UNIT) { nicif_err(nic_dev, drv, netdev, "coalesce_usecs_high(%u) must more than coalesce_usecs_low(%u), after dividing %d usecs unit\n", coal->rx_coalesce_usecs_high, coal->rx_coalesce_usecs_low, COALESCE_TIMER_CFG_UNIT); return -EOPNOTSUPP; } if (coal->rx_max_coalesced_frames_low / COALESCE_PENDING_LIMIT_UNIT >= coal->rx_max_coalesced_frames_high / COALESCE_PENDING_LIMIT_UNIT) { nicif_err(nic_dev, drv, netdev, "coalesced_frames_high(%u) must more than coalesced_frames_low(%u), after dividing %d frames unit\n", coal->rx_max_coalesced_frames_high, coal->rx_max_coalesced_frames_low, COALESCE_PENDING_LIMIT_UNIT); return -EOPNOTSUPP; } if (coal->pkt_rate_low >= coal->pkt_rate_high) { nicif_err(nic_dev, drv, netdev, "pkt_rate_high(%u) must more than pkt_rate_low(%u)\n", coal->pkt_rate_high, coal->pkt_rate_low); return -EOPNOTSUPP; } return 0; } #define CHECK_COALESCE_ALIGN(coal, item, unit) \ do { \ if ((coal)->item % (unit)) \ nicif_warn(nic_dev, drv, netdev, \ "%s in %d units, change to %d\n", \ #item, (unit), ALIGN_DOWN((coal)->item, unit));\ } while (0) #define CHECK_COALESCE_CHANGED(coal, item, unit, ori_val, obj_str) \ do { \ if (((coal)->item / (unit)) != (ori_val)) \ nicif_info(nic_dev, drv, netdev, \ "Change %s from %d to %d %s\n", \ #item, (ori_val) * (unit), \ ALIGN_DOWN((coal)->item, unit), (obj_str));\ } while (0) #define CHECK_PKT_RATE_CHANGED(coal, item, ori_val, obj_str) \ do { \ if ((coal)->item != (ori_val)) \ nicif_info(nic_dev, drv, netdev, \ "Change %s from %llu to %u %s\n", \ #item, (ori_val), (coal)->item, (obj_str));\ } while (0) static int __hinic_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal, u16 queue) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_intr_coal_info intr_coal = {0}; struct hinic_intr_coal_info *ori_intr_coal; char obj_str[OBJ_STR_MAX_LEN] = {0}; u16 i; int err = 0; err = is_coalesce_legal(netdev, coal); if (err) return err; CHECK_COALESCE_ALIGN(coal, rx_coalesce_usecs, COALESCE_TIMER_CFG_UNIT); CHECK_COALESCE_ALIGN(coal, rx_max_coalesced_frames, COALESCE_PENDING_LIMIT_UNIT); CHECK_COALESCE_ALIGN(coal, rx_coalesce_usecs_high, COALESCE_TIMER_CFG_UNIT); CHECK_COALESCE_ALIGN(coal, rx_max_coalesced_frames_high, COALESCE_PENDING_LIMIT_UNIT); CHECK_COALESCE_ALIGN(coal, rx_coalesce_usecs_low, COALESCE_TIMER_CFG_UNIT); CHECK_COALESCE_ALIGN(coal, rx_max_coalesced_frames_low, COALESCE_PENDING_LIMIT_UNIT); if (queue == COALESCE_ALL_QUEUE) { ori_intr_coal = &nic_dev->intr_coalesce[0]; err = snprintf(obj_str, sizeof(obj_str), "for netdev"); if (err <= 0 || err >= OBJ_STR_MAX_LEN) { nicif_err(nic_dev, drv, netdev, "Failed to snprintf string, function return(%d) and dest_len(%d)\n", err, OBJ_STR_MAX_LEN); return -EFAULT; } } else { ori_intr_coal = &nic_dev->intr_coalesce[queue]; err = snprintf(obj_str, sizeof(obj_str), "for queue %d", queue); if (err <= 0 || err >= OBJ_STR_MAX_LEN) { nicif_err(nic_dev, drv, netdev, "Failed to snprintf string, function return(%d) and dest_len(%d)\n", err, OBJ_STR_MAX_LEN); return -EFAULT; } } CHECK_COALESCE_CHANGED(coal, rx_coalesce_usecs, COALESCE_TIMER_CFG_UNIT, ori_intr_coal->coalesce_timer_cfg, obj_str); CHECK_COALESCE_CHANGED(coal, rx_max_coalesced_frames, COALESCE_PENDING_LIMIT_UNIT, ori_intr_coal->pending_limt, obj_str); CHECK_PKT_RATE_CHANGED(coal, pkt_rate_high, ori_intr_coal->pkt_rate_high, obj_str); CHECK_COALESCE_CHANGED(coal, rx_coalesce_usecs_high, COALESCE_TIMER_CFG_UNIT, ori_intr_coal->rx_usecs_high, obj_str); CHECK_COALESCE_CHANGED(coal, rx_max_coalesced_frames_high, COALESCE_PENDING_LIMIT_UNIT, ori_intr_coal->rx_pending_limt_high, obj_str); CHECK_PKT_RATE_CHANGED(coal, pkt_rate_low, ori_intr_coal->pkt_rate_low, obj_str); CHECK_COALESCE_CHANGED(coal, rx_coalesce_usecs_low, COALESCE_TIMER_CFG_UNIT, ori_intr_coal->rx_usecs_low, obj_str); CHECK_COALESCE_CHANGED(coal, rx_max_coalesced_frames_low, COALESCE_PENDING_LIMIT_UNIT, ori_intr_coal->rx_pending_limt_low, obj_str); intr_coal.coalesce_timer_cfg = (u8)(coal->rx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT); intr_coal.pending_limt = (u8)(coal->rx_max_coalesced_frames / COALESCE_PENDING_LIMIT_UNIT); nic_dev->adaptive_rx_coal = coal->use_adaptive_rx_coalesce; intr_coal.pkt_rate_high = coal->pkt_rate_high; intr_coal.rx_usecs_high = (u8)(coal->rx_coalesce_usecs_high / COALESCE_TIMER_CFG_UNIT); intr_coal.rx_pending_limt_high = (u8)(coal->rx_max_coalesced_frames_high / COALESCE_PENDING_LIMIT_UNIT); intr_coal.pkt_rate_low = coal->pkt_rate_low; intr_coal.rx_usecs_low = (u8)(coal->rx_coalesce_usecs_low / COALESCE_TIMER_CFG_UNIT); intr_coal.rx_pending_limt_low = (u8)(coal->rx_max_coalesced_frames_low / COALESCE_PENDING_LIMIT_UNIT); /* coalesce timer or pending set to zero will disable coalesce */ if (!nic_dev->adaptive_rx_coal && (!intr_coal.coalesce_timer_cfg || !intr_coal.pending_limt)) nicif_warn(nic_dev, drv, netdev, "Coalesce will be disabled\n"); if (queue == COALESCE_ALL_QUEUE) { for (i = 0; i < nic_dev->max_qps; i++) set_queue_coalesce(nic_dev, i, &intr_coal); } else { if (queue >= nic_dev->num_qps) { nicif_err(nic_dev, drv, netdev, "Invalid queue_id: %d\n", queue); return -EINVAL; } set_queue_coalesce(nic_dev, queue, &intr_coal); } return 0; } static int hinic_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { return __hinic_get_coalesce(netdev, coal, COALESCE_ALL_QUEUE); } static int hinic_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { return __hinic_set_coalesce(netdev, coal, COALESCE_ALL_QUEUE); } static int hinic_get_per_queue_coalesce(struct net_device *netdev, u32 queue, struct ethtool_coalesce *coal) { return __hinic_get_coalesce(netdev, coal, queue); } static int hinic_set_per_queue_coalesce(struct net_device *netdev, u32 queue, struct ethtool_coalesce *coal) { return __hinic_set_coalesce(netdev, coal, queue); } static void get_drv_queue_stats(struct hinic_nic_dev *nic_dev, u64 *data) { struct hinic_txq_stats txq_stats; struct hinic_rxq_stats rxq_stats; u16 i = 0, j = 0, qid = 0; char *p; for (qid = 0; qid < nic_dev->num_qps; qid++) { if (!nic_dev->txqs) break; hinic_txq_get_stats(&nic_dev->txqs[qid], &txq_stats); for (j = 0; j < ARRAY_LEN(hinic_tx_queue_stats); j++, i++) { p = (char *)(&txq_stats) + hinic_tx_queue_stats[j].offset; data[i] = (hinic_tx_queue_stats[j].size == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } } for (qid = 0; qid < nic_dev->num_qps; qid++) { if (!nic_dev->rxqs) break; hinic_rxq_get_stats(&nic_dev->rxqs[qid], &rxq_stats); for (j = 0; j < ARRAY_LEN(hinic_rx_queue_stats); j++, i++) { p = (char *)(&rxq_stats) + hinic_rx_queue_stats[j].offset; data[i] = (hinic_rx_queue_stats[j].size == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } } } static void hinic_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct rtnl_link_stats64 temp; const struct rtnl_link_stats64 *net_stats; struct hinic_phy_port_stats *port_stats; struct hinic_nic_stats *nic_stats; struct hinic_vport_stats vport_stats = {0}; u16 i = 0, j = 0; char *p; int err; net_stats = dev_get_stats(netdev, &temp); for (j = 0; j < ARRAY_LEN(hinic_netdev_stats); j++, i++) { p = (char *)(net_stats) + hinic_netdev_stats[j].offset; data[i] = (hinic_netdev_stats[j].size == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } nic_stats = &nic_dev->stats; for (j = 0; j < ARRAY_LEN(hinic_nic_dev_stats); j++, i++) { p = (char *)(nic_stats) + hinic_nic_dev_stats[j].offset; data[i] = (hinic_nic_dev_stats[j].size == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) { err = hinic_get_vport_stats(nic_dev->hwdev, &vport_stats); if (err) nicif_err(nic_dev, drv, netdev, "Failed to get function stats from fw\n"); for (j = 0; j < ARRAY_LEN(hinic_function_stats); j++, i++) { p = (char *)(&vport_stats) + hinic_function_stats[j].offset; data[i] = (hinic_function_stats[j].size == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } } if (!HINIC_FUNC_IS_VF(nic_dev->hwdev) && FUNC_SUPPORT_PORT_SETTING(nic_dev->hwdev)) { port_stats = kzalloc(sizeof(*port_stats), GFP_KERNEL); if (!port_stats) { memset(&data[i], 0, ARRAY_LEN(hinic_port_stats) * sizeof(*data)); i += ARRAY_LEN(hinic_port_stats); goto get_drv_stats; } err = hinic_get_phy_port_stats(nic_dev->hwdev, port_stats); if (err) nicif_err(nic_dev, drv, netdev, "Failed to get port stats from fw\n"); for (j = 0; j < ARRAY_LEN(hinic_port_stats); j++, i++) { p = (char *)(port_stats) + hinic_port_stats[j].offset; data[i] = (hinic_port_stats[j].size == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } kfree(port_stats); } get_drv_stats: get_drv_queue_stats(nic_dev, data + i); } static void hinic_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { u16 i = 0, j = 0; struct hinic_nic_dev *nic_dev = netdev_priv(netdev); char *p = (char *)data; switch (stringset) { case ETH_SS_TEST: memcpy(data, *hinic_test_strings, sizeof(hinic_test_strings)); return; case ETH_SS_STATS: for (i = 0; i < ARRAY_LEN(hinic_netdev_stats); i++) { memcpy(p, hinic_netdev_stats[i].name, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } for (i = 0; i < ARRAY_LEN(hinic_nic_dev_stats); i++) { memcpy(p, hinic_nic_dev_stats[i].name, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) { for (i = 0; i < ARRAY_LEN(hinic_function_stats); i++) { memcpy(p, hinic_function_stats[i].name, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } } if (!HINIC_FUNC_IS_VF(nic_dev->hwdev) && FUNC_SUPPORT_PORT_SETTING(nic_dev->hwdev)) { for (i = 0; i < ARRAY_LEN(hinic_port_stats); i++) { memcpy(p, hinic_port_stats[i].name, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } } for (i = 0; i < nic_dev->num_qps; i++) { for (j = 0; j < ARRAY_LEN(hinic_tx_queue_stats); j++) { sprintf(p, hinic_tx_queue_stats[j].name, i); p += ETH_GSTRING_LEN; } } for (i = 0; i < nic_dev->num_qps; i++) { for (j = 0; j < ARRAY_LEN(hinic_rx_queue_stats); j++) { sprintf(p, hinic_rx_queue_stats[j].name, i); p += ETH_GSTRING_LEN; } } return; default: nicif_err(nic_dev, drv, netdev, "Invalid string set %d", stringset); return; } } static int hinic_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u8 port; int err; if (!FUNC_SUPPORT_PORT_SETTING(nic_dev->hwdev)) { nicif_err(nic_dev, drv, netdev, "Current function don't support to set LED status\n"); return -EOPNOTSUPP; } port = hinic_physical_port_id(nic_dev->hwdev); switch (state) { case ETHTOOL_ID_ACTIVE: err = hinic_set_led_status(nic_dev->hwdev, port, HINIC_LED_TYPE_LINK, HINIC_LED_MODE_FORCE_2HZ); if (err) nicif_err(nic_dev, drv, netdev, "Set LED blinking in 2HZ failed\n"); else nicif_info(nic_dev, drv, netdev, "Set LED blinking in 2HZ success\n"); break; case ETHTOOL_ID_INACTIVE: err = hinic_reset_led_status(nic_dev->hwdev, port); if (err) nicif_err(nic_dev, drv, netdev, "Reset LED to original status failed\n"); else nicif_info(nic_dev, drv, netdev, "Reset LED to original status success\n"); break; default: return -EOPNOTSUPP; } if (err) return -EFAULT; else return 0; } static void hinic_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct nic_pause_config nic_pause = {0}; int err; err = hinic_get_pause_info(nic_dev->hwdev, &nic_pause); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to get pauseparam from hw\n"); } else { pause->autoneg = nic_pause.auto_neg; pause->rx_pause = nic_pause.rx_pause; pause->tx_pause = nic_pause.tx_pause; } } static int hinic_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct nic_pause_config nic_pause = {0}; struct nic_port_info port_info = {0}; int err; if (!FUNC_SUPPORT_PORT_SETTING(nic_dev->hwdev)) { nicif_err(nic_dev, drv, netdev, "Not support to set pause parameters\n"); return -EOPNOTSUPP; } err = hinic_get_port_info(nic_dev->hwdev, &port_info); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to get auto-negotiation state\n"); return -EFAULT; } if (pause->autoneg != port_info.autoneg_state) { nicif_err(nic_dev, drv, netdev, "To change autoneg please use: ethtool -s autoneg \n"); return -EOPNOTSUPP; } nic_pause.auto_neg = pause->autoneg; nic_pause.rx_pause = pause->rx_pause; nic_pause.tx_pause = pause->tx_pause; err = hinic_set_pause_info(nic_dev->hwdev, nic_pause); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to set pauseparam\n"); return err; } nicif_info(nic_dev, drv, netdev, "Set pause options, tx: %s, rx: %s\n", pause->tx_pause ? "on" : "off", pause->rx_pause ? "on" : "off"); return 0; } static int hinic_run_lp_test(struct hinic_nic_dev *nic_dev, u32 test_time) { u32 i; u8 j; u32 cnt = test_time * 5; struct sk_buff *skb = NULL; struct sk_buff *skb_tmp = NULL; u8 *test_data = NULL; u8 *lb_test_rx_buf = nic_dev->lb_test_rx_buf; struct net_device *netdev = nic_dev->netdev; skb_tmp = alloc_skb(LP_PKT_LEN, GFP_ATOMIC); if (!skb_tmp) return -ENOMEM; test_data = __skb_put(skb_tmp, LP_PKT_LEN); memset(test_data, 0xFF, (2 * ETH_ALEN)); test_data[ETH_ALEN] = 0xFE; test_data[2 * ETH_ALEN] = 0x08; test_data[2 * ETH_ALEN + 1] = 0x0; for (i = ETH_HLEN; i < LP_PKT_LEN; i++) test_data[i] = i & 0xFF; skb_tmp->queue_mapping = 0; skb_tmp->ip_summed = CHECKSUM_COMPLETE; skb_tmp->dev = netdev; for (i = 0; i < cnt; i++) { nic_dev->lb_test_rx_idx = 0; memset(lb_test_rx_buf, 0, (LP_PKT_CNT * LP_PKT_LEN)); for (j = 0; j < LP_PKT_CNT; j++) { skb = pskb_copy(skb_tmp, GFP_ATOMIC); if (!skb) { dev_kfree_skb_any(skb_tmp); nicif_err(nic_dev, drv, netdev, "Copy skb failed for loopback test\n"); return -ENOMEM; } /* mark index for every pkt */ skb->data[LP_PKT_LEN - 1] = j; if (hinic_lb_xmit_frame(skb, netdev)) { dev_kfree_skb_any(skb); dev_kfree_skb_any(skb_tmp); nicif_err(nic_dev, drv, netdev, "Xmit pkt failed for loopback test\n"); return -EBUSY; } } /* wait till all pkt received to rx buffer */ msleep(200); for (j = 0; j < LP_PKT_CNT; j++) { if (memcmp((lb_test_rx_buf + (j * LP_PKT_LEN)), skb_tmp->data, (LP_PKT_LEN - 1)) || (*(lb_test_rx_buf + ((j * LP_PKT_LEN) + (LP_PKT_LEN - 1))) != j)) { dev_kfree_skb_any(skb_tmp); nicif_err(nic_dev, drv, netdev, "Compare pkt failed in loopback test(index=0x%02x, data[%d]=0x%02x)\n", (j + (i * LP_PKT_CNT)), (LP_PKT_LEN - 1), *(lb_test_rx_buf + (((j * LP_PKT_LEN) + (LP_PKT_LEN - 1))))); return -EIO; } } } dev_kfree_skb_any(skb_tmp); nicif_info(nic_dev, drv, netdev, "Loopback test succeed\n"); return 0; } void hinic_lp_test(struct net_device *netdev, struct ethtool_test *eth_test, u64 *data, u32 test_time) { int err = 0; u8 link_status = 0; u8 *lb_test_rx_buf = NULL; struct ethtool_test test = {0}; enum diag_test_index test_index = 0; struct hinic_nic_dev *nic_dev = netdev_priv(netdev); memset(data, 0, (DIAG_TEST_MAX * sizeof(u64))); /* Do not support loopback test when netdev is closed. */ if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, netdev, "Do not support loopback test when netdev is closed.\n"); eth_test->flags |= ETH_TEST_FL_FAILED; data[PORT_DOWN_ERR_IDX] = 1; return; } test.flags = eth_test->flags; if (test_time == 0) test_time = LP_DEFAULT_TIME; netif_carrier_off(netdev); netif_tx_disable(netdev); if (!(test.flags & ETH_TEST_FL_EXTERNAL_LB)) { test_index = INTERNAL_LP_TEST; if (hinic_set_loopback_mode(nic_dev->hwdev, true)) { nicif_err(nic_dev, drv, netdev, "Failed to set port loopback mode before loopback test\n"); err = 1; goto resume_link; } } else { test_index = EXTERNAL_LP_TEST; } lb_test_rx_buf = vmalloc(LP_PKT_CNT * LP_PKT_LEN); if (!lb_test_rx_buf) { err = 1; } else { nic_dev->lb_test_rx_buf = lb_test_rx_buf; nic_dev->lb_pkt_len = LP_PKT_LEN; set_bit(HINIC_LP_TEST, &nic_dev->flags); if (hinic_run_lp_test(nic_dev, test_time)) err = 1; clear_bit(HINIC_LP_TEST, &nic_dev->flags); msleep(100); vfree(lb_test_rx_buf); nic_dev->lb_test_rx_buf = NULL; } if (!(test.flags & ETH_TEST_FL_EXTERNAL_LB)) { if (hinic_set_loopback_mode(nic_dev->hwdev, false)) { nicif_err(nic_dev, drv, netdev, "Failed to cancel port loopback mode after loopback test\n"); err = 1; goto resume_link; } } resume_link: if (err) { eth_test->flags |= ETH_TEST_FL_FAILED; data[test_index] = 1; } netif_tx_wake_all_queues(netdev); err = hinic_get_link_state(nic_dev->hwdev, &link_status); if (!err && link_status) netif_carrier_on(netdev); } static void hinic_diag_test(struct net_device *netdev, struct ethtool_test *eth_test, u64 *data) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (!FUNC_SUPPORT_PORT_SETTING(nic_dev->hwdev)) { nicif_err(nic_dev, drv, netdev, "Current function don't support self test\n"); return; } hinic_lp_test(netdev, eth_test, data, 0); } static int hinic_get_module_info(struct net_device *netdev, struct ethtool_modinfo *modinfo) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u8 sfp_type; u8 sfp_type_ext; int err; err = hinic_get_sfp_type(nic_dev->hwdev, &sfp_type, &sfp_type_ext); if (err) return err; switch (sfp_type) { case MODULE_TYPE_SFP: modinfo->type = ETH_MODULE_SFF_8472; modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; break; case MODULE_TYPE_QSFP: modinfo->type = ETH_MODULE_SFF_8436; modinfo->eeprom_len = STD_SFP_INFO_MAX_SIZE; break; case MODULE_TYPE_QSFP_PLUS: if (sfp_type_ext >= 0x3) { modinfo->type = ETH_MODULE_SFF_8636; modinfo->eeprom_len = STD_SFP_INFO_MAX_SIZE; } else { modinfo->type = ETH_MODULE_SFF_8436; modinfo->eeprom_len = STD_SFP_INFO_MAX_SIZE; } break; case MODULE_TYPE_QSFP28: modinfo->type = ETH_MODULE_SFF_8636; modinfo->eeprom_len = STD_SFP_INFO_MAX_SIZE; break; default: nicif_warn(nic_dev, drv, netdev, "Optical module unknown: 0x%x\n", sfp_type); return -EINVAL; } return 0; } static int hinic_get_module_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee, u8 *data) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u8 sfp_data[STD_SFP_INFO_MAX_SIZE]; u16 len; int err; if (!ee->len || ((ee->len + ee->offset) > STD_SFP_INFO_MAX_SIZE)) return -EINVAL; memset(data, 0, ee->len); err = hinic_get_sfp_eeprom(nic_dev->hwdev, sfp_data, &len); if (err) return err; memcpy(data, sfp_data + ee->offset, ee->len); return 0; } static int set_l4_rss_hash_ops(struct ethtool_rxnfc *cmd, struct nic_rss_type *rss_type) { u8 rss_l4_en = 0; switch (cmd->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: rss_l4_en = 0; break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): rss_l4_en = 1; break; default: return -EINVAL; } switch (cmd->flow_type) { case TCP_V4_FLOW: rss_type->tcp_ipv4 = rss_l4_en; break; case TCP_V6_FLOW: rss_type->tcp_ipv6 = rss_l4_en; break; case UDP_V4_FLOW: rss_type->udp_ipv4 = rss_l4_en; break; case UDP_V6_FLOW: rss_type->udp_ipv6 = rss_l4_en; break; default: return -EINVAL; } return 0; } static int hinic_set_rss_hash_opts(struct hinic_nic_dev *nic_dev, struct ethtool_rxnfc *cmd) { struct nic_rss_type *rss_type = &nic_dev->rss_type; int err; if (!test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) { cmd->data = 0; nicif_err(nic_dev, drv, nic_dev->netdev, "RSS is disable, not support to set flow-hash\n"); return -EOPNOTSUPP; } /* RSS does not support anything other than hashing * to queues on src and dst IPs and ports */ if (cmd->data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) return -EINVAL; /* We need at least the IP SRC and DEST fields for hashing */ if (!(cmd->data & RXH_IP_SRC) || !(cmd->data & RXH_IP_DST)) return -EINVAL; err = hinic_get_rss_type(nic_dev->hwdev, nic_dev->rss_tmpl_idx, rss_type); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to get rss type\n"); return -EFAULT; } switch (cmd->flow_type) { case TCP_V4_FLOW: case TCP_V6_FLOW: case UDP_V4_FLOW: case UDP_V6_FLOW: err = set_l4_rss_hash_ops(cmd, rss_type); if (err) return err; break; case IPV4_FLOW: rss_type->ipv4 = 1; break; case IPV6_FLOW: rss_type->ipv6 = 1; break; default: nicif_err(nic_dev, drv, nic_dev->netdev, "Unsupported flow type\n"); return -EINVAL; } err = hinic_set_rss_type(nic_dev->hwdev, nic_dev->rss_tmpl_idx, *rss_type); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to set rss type\n"); return -EFAULT; } nicif_info(nic_dev, drv, nic_dev->netdev, "Set rss hash options success\n"); return 0; } static int hinic_get_rss_hash_opts(struct hinic_nic_dev *nic_dev, struct ethtool_rxnfc *cmd) { struct nic_rss_type rss_type = {0}; int err; cmd->data = 0; if (!test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) return 0; err = hinic_get_rss_type(nic_dev->hwdev, nic_dev->rss_tmpl_idx, &rss_type); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to get rss type\n"); return err; } cmd->data = RXH_IP_SRC | RXH_IP_DST; switch (cmd->flow_type) { case TCP_V4_FLOW: if (rss_type.tcp_ipv4) cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; break; case TCP_V6_FLOW: if (rss_type.tcp_ipv6) cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; break; case UDP_V4_FLOW: if (rss_type.udp_ipv4) cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; break; case UDP_V6_FLOW: if (rss_type.udp_ipv6) cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; break; case IPV4_FLOW: case IPV6_FLOW: break; default: nicif_err(nic_dev, drv, nic_dev->netdev, "Unsupported flow type\n"); cmd->data = 0; return -EINVAL; } return 0; } static int hinic_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, u32 *rule_locs) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err = 0; switch (cmd->cmd) { case ETHTOOL_GRXRINGS: cmd->data = nic_dev->num_qps; break; case ETHTOOL_GRXFH: err = hinic_get_rss_hash_opts(nic_dev, cmd); break; default: err = -EOPNOTSUPP; break; } return err; } static int hinic_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err = 0; switch (cmd->cmd) { case ETHTOOL_SRXFH: err = hinic_set_rss_hash_opts(nic_dev, cmd); break; default: err = -EOPNOTSUPP; break; } return err; } static u32 hinic_get_rxfh_indir_size(struct net_device *netdev) { return HINIC_RSS_INDIR_SIZE; } static int __set_rss_rxfh(struct net_device *netdev, const u32 *indir, const u8 *key) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err, i; if (indir) { if (!nic_dev->rss_indir_user) { nic_dev->rss_indir_user = kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE, GFP_KERNEL); if (!nic_dev->rss_indir_user) { nicif_err(nic_dev, drv, netdev, "Failed to alloc memory for rss_indir_usr\n"); return -ENOMEM; } } memcpy(nic_dev->rss_indir_user, indir, sizeof(u32) * HINIC_RSS_INDIR_SIZE); err = hinic_rss_set_indir_tbl(nic_dev->hwdev, nic_dev->rss_tmpl_idx, indir); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to set rss indir table\n"); return -EFAULT; } nicif_info(nic_dev, drv, netdev, "Change rss indir success\n"); } if (key) { if (!nic_dev->rss_hkey_user) { /* We request double spaces for the hash key, * the second one holds the key of Big Edian * format. */ nic_dev->rss_hkey_user = kzalloc(HINIC_RSS_KEY_SIZE * 2, GFP_KERNEL); if (!nic_dev->rss_hkey_user) { nicif_err(nic_dev, drv, netdev, "Failed to alloc memory for rss_hkey_user\n"); return -ENOMEM; } /* The second space is for big edian hash key */ nic_dev->rss_hkey_user_be = (u32 *)(nic_dev->rss_hkey_user + HINIC_RSS_KEY_SIZE); } memcpy(nic_dev->rss_hkey_user, key, HINIC_RSS_KEY_SIZE); /* make a copy of the key, and convert it to Big Endian */ memcpy(nic_dev->rss_hkey_user_be, key, HINIC_RSS_KEY_SIZE); for (i = 0; i < HINIC_RSS_KEY_SIZE / 4; i++) nic_dev->rss_hkey_user_be[i] = cpu_to_be32(nic_dev->rss_hkey_user_be[i]); err = hinic_rss_set_template_tbl(nic_dev->hwdev, nic_dev->rss_tmpl_idx, key); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to set rss key\n"); return -EFAULT; } nicif_info(nic_dev, drv, netdev, "Change rss key success\n"); } return 0; } static u32 hinic_get_rxfh_key_size(struct net_device *netdev) { return HINIC_RSS_KEY_SIZE; } static int hinic_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err = 0; if (!test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) return -EOPNOTSUPP; if (hfunc) { u8 hash_engine_type = 0; err = hinic_rss_get_hash_engine(nic_dev->hwdev, nic_dev->rss_tmpl_idx, &hash_engine_type); if (err) return -EFAULT; *hfunc = hash_engine_type ? ETH_RSS_HASH_TOP : ETH_RSS_HASH_XOR; } if (indir) { err = hinic_rss_get_indir_tbl(nic_dev->hwdev, nic_dev->rss_tmpl_idx, indir); if (err) return -EFAULT; } if (key) err = hinic_rss_get_template_tbl(nic_dev->hwdev, nic_dev->rss_tmpl_idx, key); return err; } static int hinic_set_rxfh(struct net_device *netdev, const u32 *indir, const u8 *key, const u8 hfunc) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err = 0; if (!test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Not support to set rss parameters when rss is disable\n"); return -EOPNOTSUPP; } if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags) && indir) { nicif_err(nic_dev, drv, netdev, "Not support to set indir when DCB is enabled\n"); return -EOPNOTSUPP; } if (hfunc != ETH_RSS_HASH_NO_CHANGE) { if (hfunc != ETH_RSS_HASH_TOP && hfunc != ETH_RSS_HASH_XOR) { nicif_err(nic_dev, drv, netdev, "Not support to set hfunc type except TOP and XOR\n"); return -EOPNOTSUPP; } nic_dev->rss_hash_engine = (hfunc == ETH_RSS_HASH_XOR) ? HINIC_RSS_HASH_ENGINE_TYPE_XOR : HINIC_RSS_HASH_ENGINE_TYPE_TOEP; err = hinic_rss_set_hash_engine (nic_dev->hwdev, nic_dev->rss_tmpl_idx, nic_dev->rss_hash_engine); if (err) return -EFAULT; nicif_info(nic_dev, drv, netdev, "Change hfunc to RSS_HASH_%s success\n", (hfunc == ETH_RSS_HASH_XOR) ? "XOR" : "TOP"); } err = __set_rss_rxfh(netdev, indir, key); return err; } static const struct ethtool_ops hinic_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_PKT_RATE_RX_USECS, .get_link_ksettings = hinic_get_link_ksettings, .set_link_ksettings = hinic_set_link_ksettings, .get_drvinfo = hinic_get_drvinfo, .get_msglevel = hinic_get_msglevel, .set_msglevel = hinic_set_msglevel, .nway_reset = hinic_nway_reset, .get_link = ethtool_op_get_link, .get_ringparam = hinic_get_ringparam, .set_ringparam = hinic_set_ringparam, .get_pauseparam = hinic_get_pauseparam, .set_pauseparam = hinic_set_pauseparam, .get_sset_count = hinic_get_sset_count, .get_coalesce = hinic_get_coalesce, .set_coalesce = hinic_set_coalesce, .get_per_queue_coalesce = hinic_get_per_queue_coalesce, .set_per_queue_coalesce = hinic_set_per_queue_coalesce, .get_ethtool_stats = hinic_get_ethtool_stats, .get_strings = hinic_get_strings, .set_phys_id = hinic_set_phys_id, .self_test = hinic_diag_test, .get_rxnfc = hinic_get_rxnfc, .set_rxnfc = hinic_set_rxnfc, .get_channels = hinic_get_channels, .set_channels = hinic_set_channels, .get_module_info = hinic_get_module_info, .get_module_eeprom = hinic_get_module_eeprom, .get_rxfh_indir_size = hinic_get_rxfh_indir_size, .get_rxfh_key_size = hinic_get_rxfh_key_size, .get_rxfh = hinic_get_rxfh, .set_rxfh = hinic_set_rxfh, }; static const struct ethtool_ops hinicvf_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_MAX_FRAMES | ETHTOOL_COALESCE_USECS_LOW_HIGH | ETHTOOL_COALESCE_MAX_FRAMES_LOW_HIGH | ETHTOOL_COALESCE_PKT_RATE_RX_USECS, .get_link_ksettings = hinic_get_link_ksettings, .get_drvinfo = hinic_get_drvinfo, .get_msglevel = hinic_get_msglevel, .set_msglevel = hinic_set_msglevel, .get_link = ethtool_op_get_link, .get_ringparam = hinic_get_ringparam, .set_ringparam = hinic_set_ringparam, .get_sset_count = hinic_get_sset_count, .get_coalesce = hinic_get_coalesce, .set_coalesce = hinic_set_coalesce, .get_per_queue_coalesce = hinic_get_per_queue_coalesce, .set_per_queue_coalesce = hinic_set_per_queue_coalesce, .get_ethtool_stats = hinic_get_ethtool_stats, .get_strings = hinic_get_strings, .get_rxnfc = hinic_get_rxnfc, .set_rxnfc = hinic_set_rxnfc, .get_channels = hinic_get_channels, .set_channels = hinic_set_channels, .get_rxfh_indir_size = hinic_get_rxfh_indir_size, .get_rxfh_key_size = hinic_get_rxfh_key_size, .get_rxfh = hinic_get_rxfh, .set_rxfh = hinic_set_rxfh, }; void hinic_set_ethtool_ops(struct net_device *netdev) { SET_ETHTOOL_OPS(netdev, &hinic_ethtool_ops); } void hinicvf_set_ethtool_ops(struct net_device *netdev) { SET_ETHTOOL_OPS(netdev, &hinicvf_ethtool_ops); } /*lint -e766*/ hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_hw.h000066400000000000000000000475741502027332500211110ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_HW_H_ #define HINIC_HW_H_ enum hinic_mod_type { HINIC_MOD_COMM = 0, /* HW communication module */ HINIC_MOD_L2NIC = 1, /* L2NIC module */ HINIC_MOD_ROCE = 2, HINIC_MOD_IWARP = 3, HINIC_MOD_TOE = 4, HINIC_MOD_FLR = 5, HINIC_MOD_FCOE = 6, HINIC_MOD_CFGM = 7, /* Configuration module */ HINIC_MOD_CQM = 8, HINIC_MOD_VSWITCH = 9, HINIC_MOD_FC = 10, HINIC_MOD_OVS = 11, HINIC_MOD_FIC = 12, HINIC_MOD_MIGRATE = 13, HINIC_MOD_HILINK = 14, HINIC_MOD_HW_MAX = 16, /* hardware max module id */ /* Software module id, for PF/VF and multi-host */ HINIC_MOD_SW_FUNC = 17, HINIC_MOD_MAX, }; struct hinic_cmd_buf { void *buf; dma_addr_t dma_addr; u16 size; }; enum hinic_ack_type { HINIC_ACK_TYPE_CMDQ, HINIC_ACK_TYPE_SHARE_CQN, HINIC_ACK_TYPE_APP_CQN, HINIC_MOD_ACK_MAX = 15, }; #define HINIC_MGMT_CMD_UNSUPPORTED 0xFF int hinic_msg_to_mgmt_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); /* for pxe, ovs */ int hinic_msg_to_mgmt_poll_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); /* PF/VF send msg to uP by api cmd, and return immediately */ int hinic_msg_to_mgmt_async(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size); int hinic_mbox_to_vf(void *hwdev, enum hinic_mod_type mod, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int hinic_api_cmd_write_nack(void *hwdev, u8 dest, void *cmd, u16 size); int hinic_api_cmd_read_ack(void *hwdev, u8 dest, void *cmd, u16 size, void *ack, u16 ack_size); /* PF/VF send cmd to ucode by cmdq, and return if success. * timeout=0, use default timeout. */ int hinic_cmdq_direct_resp(void *hwdev, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, struct hinic_cmd_buf *buf_in, u64 *out_param, u32 timeout); /* 1. whether need the timeout parameter * 2. out_param indicates the status of the microcode processing command */ /* PF/VF send cmd to ucode by cmdq, and return detailed result. * timeout=0, use default timeout. */ int hinic_cmdq_detail_resp(void *hwdev, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, struct hinic_cmd_buf *buf_in, struct hinic_cmd_buf *buf_out, u32 timeout); /* PF/VF send cmd to ucode by cmdq, and return immediately */ int hinic_cmdq_async(void *hwdev, enum hinic_ack_type ack_type, enum hinic_mod_type mod, u8 cmd, struct hinic_cmd_buf *buf_in); int hinic_ppf_tmr_start(void *hwdev); int hinic_ppf_tmr_stop(void *hwdev); /* CLP */ int hinic_clp_to_mgmt(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); /* FOR windows */ bool hinic_eq_intr_handler(void *hwdev, int msix_entry_idx); enum hinic_ceq_event { HINIC_NON_L2NIC_SCQ, HINIC_NON_L2NIC_ECQ, HINIC_NON_L2NIC_NO_CQ_EQ, HINIC_CMDQ, HINIC_L2NIC_SQ, HINIC_L2NIC_RQ, HINIC_MAX_CEQ_EVENTS, }; typedef void (*hinic_ceq_event_cb)(void *handle, u32 ceqe_data); int hinic_ceq_register_cb(void *hwdev, enum hinic_ceq_event event, hinic_ceq_event_cb callback); void hinic_ceq_unregister_cb(void *hwdev, enum hinic_ceq_event event); enum hinic_aeq_type { HINIC_HW_INTER_INT = 0, HINIC_MBX_FROM_FUNC = 1, HINIC_MSG_FROM_MGMT_CPU = 2, HINIC_API_RSP = 3, HINIC_API_CHAIN_STS = 4, HINIC_MBX_SEND_RSLT = 5, HINIC_MAX_AEQ_EVENTS }; enum hinic_aeq_sw_type { HINIC_STATELESS_EVENT = 0, HINIC_STATEFULL_EVENT = 1, HINIC_MAX_AEQ_SW_EVENTS }; typedef void (*hinic_aeq_hwe_cb)(void *handle, u8 *data, u8 size); int hinic_aeq_register_hw_cb(void *hwdev, enum hinic_aeq_type event, hinic_aeq_hwe_cb hwe_cb); void hinic_aeq_unregister_hw_cb(void *hwdev, enum hinic_aeq_type event); typedef u8 (*hinic_aeq_swe_cb)(void *handle, u8 event, u64 data); int hinic_aeq_register_swe_cb(void *hwdev, enum hinic_aeq_sw_type event, hinic_aeq_swe_cb aeq_swe_cb); void hinic_aeq_unregister_swe_cb(void *hwdev, enum hinic_aeq_sw_type event); typedef void (*hinic_mgmt_msg_cb)(void *hwdev, void *pri_handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); int hinic_register_mgmt_msg_cb(void *hwdev, enum hinic_mod_type mod, void *pri_handle, hinic_mgmt_msg_cb callback); void hinic_unregister_mgmt_msg_cb(void *hwdev, enum hinic_mod_type mod); struct hinic_cmd_buf *hinic_alloc_cmd_buf(void *hwdev); void hinic_free_cmd_buf(void *hwdev, struct hinic_cmd_buf *buf); int hinic_alloc_db_phy_addr(void *hwdev, u64 *db_base, u64 *dwqe_base); void hinic_free_db_phy_addr(void *hwdev, u64 db_base, u64 dwqe_base); int hinic_alloc_db_addr(void *hwdev, void __iomem **db_base, void __iomem **dwqe_base); void hinic_free_db_addr(void *hwdev, void __iomem *db_base, void __iomem *dwqe_base); struct nic_interrupt_info { u32 lli_set; u32 interrupt_coalesc_set; u16 msix_index; u8 lli_credit_limit; u8 lli_timer_cfg; u8 pending_limt; u8 coalesc_timer_cfg; u8 resend_timer_cfg; }; int hinic_get_interrupt_cfg(void *hwdev, struct nic_interrupt_info *interrupt_info); int hinic_set_interrupt_cfg_direct(void *hwdev, struct nic_interrupt_info *interrupt_info); int hinic_set_interrupt_cfg(void *hwdev, struct nic_interrupt_info interrupt_info); /* The driver code implementation interface */ void hinic_misx_intr_clear_resend_bit(void *hwdev, u16 msix_idx, u8 clear_resend_en); struct hinic_sq_attr { u8 dma_attr_off; u8 pending_limit; u8 coalescing_time; u8 intr_en; u16 intr_idx; u32 l2nic_sqn; u64 ci_dma_base; }; int hinic_set_ci_table(void *hwdev, u16 q_id, struct hinic_sq_attr *attr); int hinic_set_root_ctxt(void *hwdev, u16 rq_depth, u16 sq_depth, int rx_buf_sz); int hinic_clean_root_ctxt(void *hwdev); void hinic_record_pcie_error(void *hwdev); int hinic_func_rx_tx_flush(void *hwdev); int hinic_func_tmr_bitmap_set(void *hwdev, bool enable); struct hinic_init_para { /* Record hinic_pcidev or NDIS_Adapter pointer address */ void *adapter_hdl; /* Record pcidev or Handler pointer address * for example: ioremap interface input parameter */ void *pcidev_hdl; /* Record pcidev->dev or Handler pointer address which used to * dma address application or dev_err print the parameter */ void *dev_hdl; void *cfg_reg_base; /* Configure virtual address, bar0/1 */ /* interrupt configuration register address, bar2/3 */ void *intr_reg_base; u64 db_base_phy; void *db_base; /* the doorbell address, bar4/5 higher 4M space */ void *dwqe_mapping; /* direct wqe 4M, follow the doorbell address */ void **hwdev; void *chip_node; /* In bmgw x86 host, driver can't send message to mgmt cpu directly, * need to trasmit message ppf mbox to bmgw arm host. */ void *ppf_hwdev; }; #define MAX_FUNCTION_NUM 512 #define HINIC_MAX_PF_NUM 16 #define HINIC_MAX_COS 8 #define INIT_FAILED 0 #define INIT_SUCCESS 1 #define MAX_DRV_BUF_SIZE 4096 struct hinic_cmd_get_light_module_abs { u8 status; u8 version; u8 rsvd0[6]; u8 port_id; u8 abs_status; /* 0:present, 1:absent */ u8 rsv[2]; }; #define MODULE_TYPE_SFP 0x3 #define MODULE_TYPE_QSFP28 0x11 #define MODULE_TYPE_QSFP 0x0C #define MODULE_TYPE_QSFP_PLUS 0x0D #define SFP_INFO_MAX_SIZE 512 struct hinic_cmd_get_sfp_qsfp_info { u8 status; u8 version; u8 rsvd0[6]; u8 port_id; u8 wire_type; u16 out_len; u8 sfp_qsfp_info[SFP_INFO_MAX_SIZE]; }; #define STD_SFP_INFO_MAX_SIZE 640 struct hinic_cmd_get_std_sfp_info { u8 status; u8 version; u8 rsvd0[6]; u8 port_id; u8 wire_type; u16 eeprom_len; u32 rsvd; u8 sfp_info[STD_SFP_INFO_MAX_SIZE]; }; #define HINIC_MAX_PORT_ID 4 struct hinic_port_routine_cmd { int up_send_sfp_info; int up_send_sfp_abs; struct hinic_cmd_get_sfp_qsfp_info sfp_info; struct hinic_cmd_get_light_module_abs abs; }; struct card_node { struct list_head node; struct list_head func_list; char chip_name[IFNAMSIZ]; void *log_info; void *dbgtool_info; void *func_handle_array[MAX_FUNCTION_NUM]; unsigned char dp_bus_num; u8 func_num; struct attribute dbgtool_attr_file; bool cos_up_setted; u8 cos_up[HINIC_MAX_COS]; bool ppf_state; u8 pf_bus_num[HINIC_MAX_PF_NUM]; bool disable_vf_load[HINIC_MAX_PF_NUM]; u32 vf_mbx_old_rand_id[MAX_FUNCTION_NUM]; u32 vf_mbx_rand_id[MAX_FUNCTION_NUM]; struct hinic_port_routine_cmd rt_cmd[HINIC_MAX_PORT_ID]; /* mutex used for copy sfp info */ struct mutex sfp_mutex; }; enum hinic_hwdev_init_state { HINIC_HWDEV_NONE_INITED = 0, HINIC_HWDEV_CLP_INITED, HINIC_HWDEV_AEQ_INITED, HINIC_HWDEV_MGMT_INITED, HINIC_HWDEV_MBOX_INITED, HINIC_HWDEV_CMDQ_INITED, HINIC_HWDEV_COMM_CH_INITED, HINIC_HWDEV_ALL_INITED, HINIC_HWDEV_MAX_INVAL_INITED }; enum hinic_func_mode { FUNC_MOD_NORMAL_HOST, FUNC_MOD_MULTI_BM_MASTER, FUNC_MOD_MULTI_BM_SLAVE, FUNC_MOD_MULTI_VM_MASTER, FUNC_MOD_MULTI_VM_SLAVE, }; enum hinic_func_cap { /* send message to mgmt cpu directly */ HINIC_FUNC_MGMT = 1 << 0, /* setting port attribute, pause/speed etc. */ HINIC_FUNC_PORT = 1 << 1, /* Enable SR-IOV in default */ HINIC_FUNC_SRIOV_EN_DFLT = 1 << 2, /* Can't change VF num */ HINIC_FUNC_SRIOV_NUM_FIX = 1 << 3, /* Fcorce pf/vf link up */ HINIC_FUNC_FORCE_LINK_UP = 1 << 4, /* Support rate limit */ HINIC_FUNC_SUPP_RATE_LIMIT = 1 << 5, HINIC_FUNC_SUPP_DFX_REG = 1 << 6, /* Support promisc/multicast/all-multi */ HINIC_FUNC_SUPP_RX_MODE = 1 << 7, /* Set vf mac and vlan by ip link */ HINIC_FUNC_SUPP_SET_VF_MAC_VLAN = 1 << 8, /* Support set mac by ifconfig */ HINIC_FUNC_SUPP_CHANGE_MAC = 1 << 9, /* OVS don't support SCTP_CRC/HW_VLAN/LRO */ HINIC_FUNC_OFFLOAD_OVS_UNSUPP = 1 << 10, /* OVS don't support encap-tso/encap-csum */ HINIC_FUNC_SUPP_ENCAP_TSO_CSUM = 1 << 11, }; #define FUNC_SUPPORT_MGMT(hwdev) \ (!!(hinic_get_func_feature_cap(hwdev) & HINIC_FUNC_MGMT)) #define FUNC_SUPPORT_PORT_SETTING(hwdev) \ (!!(hinic_get_func_feature_cap(hwdev) & HINIC_FUNC_PORT)) #define FUNC_SUPPORT_DCB(hwdev) \ (FUNC_SUPPORT_PORT_SETTING(hwdev)) #define FUNC_ENABLE_SRIOV_IN_DEFAULT(hwdev) \ (!!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_SRIOV_EN_DFLT)) #define FUNC_SRIOV_FIX_NUM_VF(hwdev) \ (!!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_SRIOV_NUM_FIX)) #define FUNC_SUPPORT_RX_MODE(hwdev) \ (!!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_SUPP_RX_MODE)) #define FUNC_SUPPORT_RATE_LIMIT(hwdev) \ (!!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_SUPP_RATE_LIMIT)) #define FUNC_SUPPORT_SET_VF_MAC_VLAN(hwdev) \ (!!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_SUPP_SET_VF_MAC_VLAN)) #define FUNC_SUPPORT_CHANGE_MAC(hwdev) \ (!!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_SUPP_CHANGE_MAC)) #define FUNC_FORCE_LINK_UP(hwdev) \ (!!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_FORCE_LINK_UP)) #define FUNC_SUPPORT_SCTP_CRC(hwdev) \ (!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_OFFLOAD_OVS_UNSUPP)) #define FUNC_SUPPORT_HW_VLAN(hwdev) \ (!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_OFFLOAD_OVS_UNSUPP)) #define FUNC_SUPPORT_LRO(hwdev) \ (!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_OFFLOAD_OVS_UNSUPP)) #define FUNC_SUPPORT_ENCAP_TSO_CSUM(hwdev) \ (!!(hinic_get_func_feature_cap(hwdev) & \ HINIC_FUNC_SUPP_ENCAP_TSO_CSUM)) int hinic_init_hwdev(struct hinic_init_para *para); int hinic_set_vf_dev_cap(void *hwdev); void hinic_free_hwdev(void *hwdev); void hinic_shutdown_hwdev(void *hwdev); void hinic_set_api_stop(void *hwdev); void hinic_ppf_hwdev_unreg(void *hwdev); void hinic_ppf_hwdev_reg(void *hwdev, void *ppf_hwdev); void hinic_qps_num_set(void *hwdev, u32 num_qps); bool hinic_is_hwdev_mod_inited(void *hwdev, enum hinic_hwdev_init_state state); enum hinic_func_mode hinic_get_func_mode(void *hwdev); u64 hinic_get_func_feature_cap(void *hwdev); enum hinic_service_mode { HINIC_WORK_MODE_OVS = 0, HINIC_WORK_MODE_UNKNOWN, HINIC_WORK_MODE_NIC, HINIC_WORK_MODE_INVALID = 0xFF, }; enum hinic_service_mode hinic_get_service_mode(void *hwdev); int hinic_slq_init(void *dev, int num_wqs); void hinic_slq_uninit(void *dev); int hinic_slq_alloc(void *dev, u16 wqebb_size, u16 q_depth, u16 page_size, u64 *cla_addr, void **handle); void hinic_slq_free(void *dev, void *handle); u64 hinic_slq_get_addr(void *handle, u16 index); u64 hinic_slq_get_first_pageaddr(void *handle); typedef void (*comm_up_self_msg_proc)(void *handle, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); void hinic_comm_recv_mgmt_self_cmd_reg(void *hwdev, u8 cmd, comm_up_self_msg_proc proc); void hinic_comm_recv_up_self_cmd_unreg(void *hwdev, u8 cmd); int hinic_micro_log_path_set(void *hwdev, u8 *log_path); int hinic_micro_log_func_en(void *hwdev, u8 is_en); /* defined by chip */ enum hinic_fault_type { FAULT_TYPE_CHIP, FAULT_TYPE_UCODE, FAULT_TYPE_MEM_RD_TIMEOUT, FAULT_TYPE_MEM_WR_TIMEOUT, FAULT_TYPE_REG_RD_TIMEOUT, FAULT_TYPE_REG_WR_TIMEOUT, FAULT_TYPE_PHY_FAULT, FAULT_TYPE_MAX, }; /* defined by chip */ enum hinic_fault_err_level { /* default err_level=FAULT_LEVEL_FATAL if * type==FAULT_TYPE_MEM_RD_TIMEOUT || FAULT_TYPE_MEM_WR_TIMEOUT || * FAULT_TYPE_REG_RD_TIMEOUT || FAULT_TYPE_REG_WR_TIMEOUT || * FAULT_TYPE_UCODE * other: err_level in event.chip.err_level if type==FAULT_TYPE_CHIP */ FAULT_LEVEL_FATAL, FAULT_LEVEL_SERIOUS_RESET, FAULT_LEVEL_SERIOUS_FLR, FAULT_LEVEL_GENERAL, FAULT_LEVEL_SUGGESTION, FAULT_LEVEL_MAX }; enum hinic_fault_source_type { /* same as FAULT_TYPE_CHIP */ HINIC_FAULT_SRC_HW_MGMT_CHIP = 0, /* same as FAULT_TYPE_UCODE */ HINIC_FAULT_SRC_HW_MGMT_UCODE, /* same as FAULT_TYPE_MEM_RD_TIMEOUT */ HINIC_FAULT_SRC_HW_MGMT_MEM_RD_TIMEOUT, /* same as FAULT_TYPE_MEM_WR_TIMEOUT */ HINIC_FAULT_SRC_HW_MGMT_MEM_WR_TIMEOUT, /* same as FAULT_TYPE_REG_RD_TIMEOUT */ HINIC_FAULT_SRC_HW_MGMT_REG_RD_TIMEOUT, /* same as FAULT_TYPE_REG_WR_TIMEOUT */ HINIC_FAULT_SRC_HW_MGMT_REG_WR_TIMEOUT, HINIC_FAULT_SRC_SW_MGMT_UCODE, HINIC_FAULT_SRC_MGMT_WATCHDOG, HINIC_FAULT_SRC_MGMT_RESET = 8, HINIC_FAULT_SRC_HW_PHY_FAULT, HINIC_FAULT_SRC_HOST_HEARTBEAT_LOST = 20, HINIC_FAULT_SRC_TYPE_MAX, }; struct hinic_fault_sw_mgmt { u8 event_id; u64 event_data; }; union hinic_fault_hw_mgmt { u32 val[4]; /* valid only type==FAULT_TYPE_CHIP */ struct { u8 node_id; /* enum hinic_fault_err_level */ u8 err_level; u16 err_type; u32 err_csr_addr; u32 err_csr_value; /* func_id valid only err_level==FAULT_LEVEL_SERIOUS_FLR */ u16 func_id; u16 rsvd2; } chip; /* valid only type==FAULT_TYPE_UCODE */ struct { u8 cause_id; u8 core_id; u8 c_id; u8 rsvd3; u32 epc; u32 rsvd4; u32 rsvd5; } ucode; /* valid only type==FAULT_TYPE_MEM_RD_TIMEOUT || * FAULT_TYPE_MEM_WR_TIMEOUT */ struct { u32 err_csr_ctrl; u32 err_csr_data; u32 ctrl_tab; u32 mem_index; } mem_timeout; /* valid only type==FAULT_TYPE_REG_RD_TIMEOUT || * FAULT_TYPE_REG_WR_TIMEOUT */ struct { u32 err_csr; u32 rsvd6; u32 rsvd7; u32 rsvd8; } reg_timeout; struct { /* 0: read; 1: write */ u8 op_type; u8 port_id; u8 dev_ad; u8 rsvd9; u32 csr_addr; u32 op_data; u32 rsvd10; } phy_fault; }; /* defined by chip */ struct hinic_fault_event { /* enum hinic_fault_type */ u8 type; u8 fault_level; /* sdk write fault level for uld event */ u8 rsvd0[2]; union hinic_fault_hw_mgmt event; }; struct hinic_dcb_state { u8 dcb_on; u8 default_cos; u8 up_cos[8]; }; enum link_err_type { LINK_ERR_MODULE_UNRECOGENIZED, LINK_ERR_NUM, }; enum port_module_event_type { HINIC_PORT_MODULE_CABLE_PLUGGED, HINIC_PORT_MODULE_CABLE_UNPLUGGED, HINIC_PORT_MODULE_LINK_ERR, HINIC_PORT_MODULE_MAX_EVENT, }; struct hinic_port_module_event { enum port_module_event_type type; enum link_err_type err_type; }; struct hinic_event_link_info { u8 valid; u8 port_type; u8 autoneg_cap; u8 autoneg_state; u8 duplex; u8 speed; }; struct hinic_mctp_host_info { u8 major_cmd; u8 sub_cmd; u8 rsvd[2]; u32 data_len; void *data; }; /* multi host mgmt event sub cmd */ enum hinic_mhost_even_type { HINIC_MHOST_NIC_STATE_CHANGE = 1, }; struct hinic_mhost_nic_func_state { u8 status; u8 enable; u16 func_idx; }; struct hinic_multi_host_mgmt_event { u16 sub_cmd; u16 rsvd[3]; void *data; }; enum hinic_event_type { HINIC_EVENT_LINK_DOWN = 0, HINIC_EVENT_LINK_UP = 1, HINIC_EVENT_HEART_LOST = 2, HINIC_EVENT_FAULT = 3, HINIC_EVENT_NOTIFY_VF_DCB_STATE = 4, HINIC_EVENT_DCB_STATE_CHANGE = 5, HINIC_EVENT_FMW_ACT_NTC = 6, HINIC_EVENT_PORT_MODULE_EVENT = 7, HINIC_EVENT_MCTP_GET_HOST_INFO, HINIC_EVENT_MULTI_HOST_MGMT, HINIC_EVENT_INIT_MIGRATE_PF, HINIC_EVENT_MGMT_WATCHDOG_EVENT, }; struct hinic_event_info { enum hinic_event_type type; union { struct hinic_event_link_info link_info; struct hinic_fault_event info; struct hinic_dcb_state dcb_state; struct hinic_port_module_event module_event; u8 vf_default_cos; struct hinic_mctp_host_info mctp_info; struct hinic_multi_host_mgmt_event mhost_mgmt; }; }; enum hinic_ucode_event_type { HINIC_INTERNAL_TSO_FATAL_ERROR = 0x0, HINIC_INTERNAL_LRO_FATAL_ERROR = 0x1, HINIC_INTERNAL_TX_FATAL_ERROR = 0x2, HINIC_INTERNAL_RX_FATAL_ERROR = 0x3, HINIC_INTERNAL_OTHER_FATAL_ERROR = 0x4, HINIC_NIC_FATAL_ERROR_MAX = 0x8, }; typedef void (*hinic_event_handler)(void *handle, struct hinic_event_info *event); /* only register once */ void hinic_event_register(void *dev, void *pri_handle, hinic_event_handler callback); void hinic_event_unregister(void *dev); void hinic_detect_hw_present(void *hwdev); void hinic_set_chip_absent(void *hwdev); int hinic_get_chip_present_flag(void *hwdev); void hinic_set_pcie_order_cfg(void *handle); int hinic_get_mgmt_channel_status(void *handle); enum hinic_led_mode { HINIC_LED_MODE_ON, HINIC_LED_MODE_OFF, HINIC_LED_MODE_FORCE_1HZ, HINIC_LED_MODE_FORCE_2HZ, HINIC_LED_MODE_FORCE_4HZ, HINIC_LED_MODE_1HZ, HINIC_LED_MODE_2HZ, HINIC_LED_MODE_4HZ, HINIC_LED_MODE_INVALID, }; enum hinic_led_type { HINIC_LED_TYPE_LINK, HINIC_LED_TYPE_LOW_SPEED, HINIC_LED_TYPE_HIGH_SPEED, HINIC_LED_TYPE_INVALID, }; int hinic_reset_led_status(void *hwdev, u8 port); int hinic_set_led_status(void *hwdev, u8 port, enum hinic_led_type type, enum hinic_led_mode mode); struct hinic_board_info { u32 board_type; u32 port_num; u32 port_speed; u32 pcie_width; u32 host_num; u32 pf_num; u32 vf_total_num; u32 tile_num; u32 qcm_num; u32 core_num; u32 work_mode; u32 service_mode; u32 pcie_mode; u32 cfg_addr; u32 boot_sel; u32 board_id; }; int hinic_get_board_info(void *hwdev, struct hinic_board_info *info); bool hinic_get_ppf_status(void *hwdev); struct hw_pf_info { u16 glb_func_idx; u16 glb_pf_vf_offset; u8 p2p_idx; u8 itf_idx; u16 max_vfs; u16 max_queue_num; u16 ovs_q_vf_num[9]; u32 resv; }; struct hinic_hw_pf_infos { u8 num_pfs; u8 rsvd1[3]; struct hw_pf_info infos[16]; }; int hinic_get_hw_pf_infos(void *hwdev, struct hinic_hw_pf_infos *infos); int hinic_set_ip_check(void *hwdev, bool ip_check_ctl); int hinic_mbox_to_host_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int hinic_mbox_ppf_to_vf(void *hwdev, enum hinic_mod_type mod, u16 func_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int hinic_get_card_present_state(void *hwdev, bool *card_present_state); void hinic_migrate_report(void *dev); int hinic_set_vxlan_udp_dport(void *hwdev, u32 udp_port); bool is_multi_vm_slave(void *hwdev); bool is_multi_bm_slave(void *hwdev); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_hw_mgmt.h000066400000000000000000000362601502027332500221230ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_HW_MGMT_H_ #define HINIC_HW_MGMT_H_ /* show each drivers only such as nic_service_cap, * toe_service_cap structure, but not show service_cap */ enum hinic_service_type { SERVICE_T_NIC = 0, SERVICE_T_OVS, SERVICE_T_ROCE, SERVICE_T_TOE, SERVICE_T_IWARP, SERVICE_T_FC, SERVICE_T_FCOE, SERVICE_T_MIGRATE, SERVICE_T_PT, SERVICE_T_HWPT, SERVICE_T_MAX, /* Only used for interruption resource management, * mark the request module */ SERVICE_T_INTF = (1 << 15), SERVICE_T_CQM = (1 << 16), }; /* NIC service capability * 1, The chip supports NIC RQ is 1K * 2, PF/VF RQ specifications: * disable RSS: * disable VMDq: Each PF/VF at most 8 RQ * enable the VMDq: Each PF/VF at most 1K RQ * enable the RSS: * disable VMDq: each PF at most 64 RQ, VF at most 32 RQ * enable the VMDq: Each PF/VF at most 1K RQ * * 3, The chip supports NIC SQ is 1K * 4, PF/VF SQ specifications: * disable RSS: * disable VMDq: Each PF/VF at most 8 SQ * enable the VMDq: Each PF/VF at most 1K SQ * enable the RSS: * disable VMDq: each PF at most 64 SQ, VF at most 32 SQ * enable the VMDq: Each PF/VF at most 1K SQ */ struct nic_service_cap { /* PF resources */ u16 max_sqs; u16 max_rqs; /* VF resources, vf obtain through the MailBox mechanism from * according PF */ u16 vf_max_sqs; u16 vf_max_rqs; bool lro_en; /* LRO feature enable bit*/ u8 lro_sz; /* LRO context space: n*16B */ u8 tso_sz; /* TSO context space: n*16B */ u16 max_queue_allowed; u16 dynamic_qp; /* support dynamic queue */ }; struct dev_roce_svc_own_cap { u32 max_qps; u32 max_cqs; u32 max_srqs; u32 max_mpts; u32 vf_max_qps; u32 vf_max_cqs; u32 vf_max_srqs; u32 vf_max_mpts; u32 cmtt_cl_start; u32 cmtt_cl_end; u32 cmtt_cl_sz; u32 dmtt_cl_start; u32 dmtt_cl_end; u32 dmtt_cl_sz; u32 wqe_cl_start; u32 wqe_cl_end; u32 wqe_cl_sz; u32 qpc_entry_sz; u32 max_wqes; u32 max_rq_sg; u32 max_sq_inline_data_sz; u32 max_rq_desc_sz; u32 rdmarc_entry_sz; u32 max_qp_init_rdma; u32 max_qp_dest_rdma; u32 max_srq_wqes; u32 reserved_srqs; u32 max_srq_sge; u32 srqc_entry_sz; u32 max_msg_sz; /* Message size 2GB */ u8 num_cos; }; struct dev_iwarp_svc_own_cap { u32 max_qps; u32 max_cqs; u32 max_mpts; u32 vf_max_qps; u32 vf_max_cqs; u32 vf_max_mpts; u32 cmtt_cl_start; u32 cmtt_cl_end; u32 cmtt_cl_sz; u32 dmtt_cl_start; u32 dmtt_cl_end; u32 dmtt_cl_sz; u32 wqe_cl_start; u32 wqe_cl_end; u32 wqe_cl_sz; u32 max_rq_sg; u32 max_sq_inline_data_sz; u32 max_rq_desc_sz; u32 max_irq_depth; u32 irq_entry_size; /* 64B */ u32 max_orq_depth; u32 orq_entry_size; /* 32B */ u32 max_rtoq_depth; u32 rtoq_entry_size; /* 32B */ u32 max_ackq_depth; u32 ackq_entry_size; /* 16B */ u32 max_msg_sz; /* Message size 1GB */ u32 max_wqes; /* 8K */ u32 qpc_entry_sz; /* 1K */ /* true:CQM uses static allocation; * false:CQM uses dynamic allocation. * Currently, only consider the QPC */ bool alloc_flag; u8 num_cos; }; /* RDMA service capability structure */ struct dev_rdma_svc_cap { /* ROCE service unique parameter structure */ struct dev_roce_svc_own_cap roce_own_cap; /* IWARP service unique parameter structure */ struct dev_iwarp_svc_own_cap iwarp_own_cap; }; /* Defines the RDMA service capability flag */ enum { RDMA_BMME_FLAG_LOCAL_INV = (1 << 0), RDMA_BMME_FLAG_REMOTE_INV = (1 << 1), RDMA_BMME_FLAG_FAST_REG_WR = (1 << 2), RDMA_BMME_FLAG_RESERVED_LKEY = (1 << 3), RDMA_BMME_FLAG_TYPE_2_WIN = (1 << 4), RDMA_BMME_FLAG_WIN_TYPE_2B = (1 << 5), RDMA_DEV_CAP_FLAG_XRC = (1 << 6), RDMA_DEV_CAP_FLAG_MEM_WINDOW = (1 << 7), RDMA_DEV_CAP_FLAG_ATOMIC = (1 << 8), RDMA_DEV_CAP_FLAG_APM = (1 << 9), }; /* RDMA services */ struct rdma_service_cap { struct dev_rdma_svc_cap dev_rdma_cap; u8 log_mtt; /* 1. the number of MTT PA must be integer power of 2 * 2. represented by logarithm. Each MTT table can * contain 1, 2, 4, 8, and 16 PA) */ u8 log_rdmarc; /* 1. the number of RDMArc PA must be integer power of 2 * 2. represented by logarithm. Each MTT table can * contain 1, 2, 4, 8, and 16 PA) */ u32 reserved_qps; /* Number of reserved QP */ u32 max_sq_sg; /* Maximum SGE number of SQ (8) */ u32 max_sq_desc_sz; /* WQE maximum size of SQ(1024B), inline maximum * size if 960B(944B aligned to the 960B), * 960B=>wqebb alignment=>1024B */ u32 wqebb_size; /* Currently, the supports 64B and 128B, * defined as 64Bytes */ u32 max_cqes; /* Size of the depth of the CQ (64K-1) */ u32 reserved_cqs; /* Number of reserved CQ */ u32 cqc_entry_sz; /* Size of the CQC (64B/128B) */ u32 cqe_size; /* Size of CQE (32B) */ u32 reserved_mrws; /* Number of reserved MR/MR Window */ u32 mpt_entry_sz; /* MPT table size (64B) */ u32 max_fmr_maps; /* max MAP of FMR, * (1 << (32-ilog2(num_mpt)))-1; */ u32 num_mtts; /* Number of MTT table (4M), * is actually MTT seg number */ /* MTT table number of Each MTT seg(3) */ u32 log_mtt_seg; u32 mtt_entry_sz; /* MTT table size 8B, including 1 PA(64bits) */ u32 log_rdmarc_seg; /* table number of each RDMArc seg(3) */ /* Timeout time. Formula:Tr=4.096us*2(local_ca_ack_delay), [Tr,4Tr] */ u32 local_ca_ack_delay; u32 num_ports; /* Physical port number */ u32 db_page_size; /* Size of the DB (4KB) */ u32 direct_wqe_size; /* Size of the DWQE (256B) */ u32 num_pds; /* Maximum number of PD (128K) */ u32 reserved_pds; /* Number of reserved PD*/ u32 max_xrcds; /* Maximum number of xrcd (64K) */ u32 reserved_xrcds; /* Number of reserved xrcd */ u32 max_gid_per_port; /* gid number (16) of each port */ u32 gid_entry_sz; /* RoCE v2 GID table is 32B, * compatible RoCE v1 expansion */ u32 reserved_lkey; /* local_dma_lkey */ u32 num_comp_vectors; /* Number of complete vector (32) */ u32 page_size_cap; /* Supports 4K,8K,64K,256K,1M,4M page_size */ u32 flags; /* RDMA some identity */ u32 max_frpl_len; /* Maximum number of pages frmr registration */ u32 max_pkeys; /* Number of supported pkey group */ }; /* PF/VF FCoE service resource structure defined */ struct dev_fcoe_svc_cap { /* PF resources */ u32 max_qps; u32 max_cqs; u32 max_srqs; /* Child Context(Task IO) * For FCoE/IOE services, at most 8K */ u32 max_cctxs; u32 cctxs_id_start; u8 vp_id_start; u8 vp_id_end; }; /* FCoE services */ struct fcoe_service_cap { struct dev_fcoe_svc_cap dev_fcoe_cap; /* SQ */ u32 qpc_basic_size; u32 childc_basic_size; u32 sqe_size; /* SCQ */ u32 scqc_basic_size; u32 scqe_size; /* SRQ */ u32 srqc_size; u32 srqe_size; }; /* PF/VF ToE service resource structure */ struct dev_toe_svc_cap { /* PF resources*/ u32 max_pctxs; /* Parent Context: max specifications 1M */ u32 max_cqs; u32 max_srqs; u32 srq_id_start; u8 num_cos; }; /* ToE services */ struct toe_service_cap { struct dev_toe_svc_cap dev_toe_cap; bool alloc_flag; u32 pctx_sz;/* 1KB */ u32 scqc_sz;/* 64B */ }; /* PF FC service resource structure defined */ struct dev_fc_svc_cap { /* PF Parent QPC */ u32 max_parent_qpc_num; /* max number is 2048 */ /* PF Child QPC */ u32 max_child_qpc_num; /* max number is 2048 */ u32 child_qpc_id_start; /* PF SCQ */ u32 scq_num; /* 16 */ /* PF supports SRQ*/ u32 srq_num; /* Number of SRQ is 2 */ u8 vp_id_start; u8 vp_id_end; }; /* FC services*/ struct fc_service_cap { struct dev_fc_svc_cap dev_fc_cap; /* Parent QPC */ u32 parent_qpc_size; /* 256B */ /* Child QPC */ u32 child_qpc_size; /* 256B */ /* SQ */ u32 sqe_size; /* 128B(in linked list mode) */ /* SCQ */ u32 scqc_size; /* Size of the Context 32B */ u32 scqe_size; /* 64B */ /* SRQ */ u32 srqc_size; /* Size of SRQ Context (64B) */ u32 srqe_size; /* 32B */ }; /* PF OVS service resource structure defined */ struct dev_ovs_svc_cap { /* PF resources */ u32 max_pctxs; /* Parent Context: max specifications 1M */ u32 max_cqs; u8 dynamic_qp_en; /* VF resources */ u32 vf_max_pctxs; /* Parent Context: max specifications 1M */ u32 vf_max_cqs; }; /* OVS services */ struct ovs_service_cap { struct dev_ovs_svc_cap dev_ovs_cap; bool alloc_flag; u32 pctx_sz; /* 512B */ u32 scqc_sz; /* 64B */ }; /* PF ACL service resource structure */ struct dev_acl_svc_cap { /* PF resources */ u32 max_pctxs; /* Parent Context: max specifications 1M */ u32 max_cqs; /* VF resources */ u32 vf_max_pctxs; /* Parent Context: max specifications 1M */ u32 vf_max_cqs; }; /* ACL services */ struct acl_service_cap { struct dev_acl_svc_cap dev_acl_cap; bool alloc_flag; u32 pctx_sz; /* 512B */ u32 scqc_sz; /* 64B */ }; enum hinic_chip_mode { CHIP_MODE_NORMAL, CHIP_MODE_BMGW, CHIP_MODE_VMGW, }; bool hinic_support_nic(void *hwdev, struct nic_service_cap *cap); bool hinic_support_roce(void *hwdev, struct rdma_service_cap *cap); bool hinic_support_fcoe(void *hwdev, struct fcoe_service_cap *cap); /* PPF support,PF not support */ bool hinic_support_toe(void *hwdev, struct toe_service_cap *cap); bool hinic_support_iwarp(void *hwdev, struct rdma_service_cap *cap); bool hinic_support_fc(void *hwdev, struct fc_service_cap *cap); bool hinic_support_fic(void *hwdev); bool hinic_support_ovs(void *hwdev, struct ovs_service_cap *cap); bool hinic_support_acl(void *hwdev, struct acl_service_cap *cap); bool hinic_support_rdma(void *hwdev, struct rdma_service_cap *cap); bool hinic_support_ft(void *hwdev); bool hinic_func_for_mgmt(void *hwdev); bool hinic_support_dynamic_q(void *hwdev); int hinic_set_toe_enable(void *hwdev, bool enable); bool hinic_get_toe_enable(void *hwdev); int hinic_set_fcoe_enable(void *hwdev, bool enable); bool hinic_get_fcoe_enable(void *hwdev); bool hinic_get_stateful_enable(void *hwdev); /* Service interface for obtaining service_cap public fields */ /* Obtain service_cap.host_oq_id_mask_val */ u8 hinic_host_oq_id_mask(void *hwdev); u8 hinic_host_id(void *hwdev);/* Obtain service_cap.host_id */ /* Obtain service_cap.host_total_function */ u16 hinic_host_total_func(void *hwdev); /* Obtain service_cap.nic_cap.dev_nic_cap.max_sqs */ u16 hinic_func_max_nic_qnum(void *hwdev); /* Obtain service_cap.dev_cap.max_sqs */ u16 hinic_func_max_qnum(void *hwdev); u8 hinic_ep_id(void *hwdev);/* Obtain service_cap.ep_id */ u8 hinic_er_id(void *hwdev);/* Obtain service_cap.er_id */ u8 hinic_physical_port_id(void *hwdev);/* Obtain service_cap.port_id */ u8 hinic_func_max_vf(void *hwdev);/* Obtain service_cap.max_vf */ u32 hinic_func_pf_num(void *hwdev);/* Obtain service_cap.pf_num */ u8 hinic_max_num_cos(void *hwdev); u8 hinic_cos_valid_bitmap(void *hwdev); u8 hinic_net_port_mode(void *hwdev);/* Obtain service_cap.net_port_mode */ /* The following information is obtained from the bar space * which is recorded by SDK layer. * Here provide parameter query interface for service */ /* func_attr.glb_func_idx, global function index */ u16 hinic_global_func_id(void *hwdev); /* func_attr.intr_num, MSI-X table entry in function */ u16 hinic_intr_num(void *hwdev); enum intr_type { INTR_TYPE_MSIX, INTR_TYPE_MSI, INTR_TYPE_INT, INTR_TYPE_NONE, /* PXE,OVS need single thread processing, * synchronization messages must use poll wait mechanism interface */ }; enum intr_type hinic_intr_type(void *hwdev); u8 hinic_pf_id_of_vf(void *hwdev); /* func_attr.p2p_idx, belongs to which pf */ u8 hinic_pcie_itf_id(void *hwdev); /* func_attr.itf_idx, pcie interface index */ u8 hinic_vf_in_pf(void *hwdev); /* func_attr.vf_in_pf, the vf offset in pf */ enum func_type { TYPE_PF, TYPE_VF, TYPE_PPF, TYPE_UNKNOWN, }; /* func_attr.func_type, 0-PF 1-VF 2-PPF */ enum func_type hinic_func_type(void *hwdev); u8 hinic_ceq_num(void *hwdev); /* func_attr.ceq_num, ceq num in one function */ /* func_attr.dma_attr_entry_num, dma attribute entry num */ u8 hinic_dma_attr_entry_num(void *hwdev); /* The PF func_attr.glb_pf_vf_offset, * PF use only */ u16 hinic_glb_pf_vf_offset(void *hwdev); /* func_attr.mpf_idx, mpf global function index, * This value is valid only when it is PF */ u8 hinic_mpf_idx(void *hwdev); u8 hinic_ppf_idx(void *hwdev); enum hinic_msix_state { HINIC_MSIX_ENABLE, HINIC_MSIX_DISABLE, }; void hinic_set_msix_state(void *hwdev, u16 msix_idx, enum hinic_msix_state flag); enum hinic_msix_state hinic_get_msix_state(void *hwdev, u16 msix_idx); /* Define the version information structure */ struct dev_version_info { u8 up_ver; /* uP version, directly read from uP * is not configured to file */ u8 ucode_ver; /* The microcode version, * read through the CMDq from microcode */ u8 cfg_file_ver;/* uP configuration file version */ u8 sdk_ver; /* SDK driver version */ u8 hw_ver; /* Hardware version */ }; /* Obtain service_cap.dev_version_info */ int hinic_dev_ver_info(void *hwdev, struct dev_version_info *ver); int hinic_vector_to_eqn(void *hwdev, enum hinic_service_type type, int vector); /* Defines the IRQ information structure */ struct irq_info { u16 msix_entry_idx; /* IRQ corresponding index number */ u32 irq_id; /* the IRQ number from OS */ }; int hinic_alloc_irqs(void *hwdev, enum hinic_service_type type, u16 req_num, struct irq_info *irq_info_array, u16 *resp_num); void hinic_free_irq(void *hwdev, enum hinic_service_type type, u32 irq_id); int hinic_alloc_ceqs(void *hwdev, enum hinic_service_type type, int req_num, int *ceq_id_array, int *resp_num); void hinic_free_ceq(void *hwdev, enum hinic_service_type type, int ceq_id); int hinic_sync_time(void *hwdev, u64 time); void hinic_sync_time_async(void *hwdev, u64 time); struct hinic_micro_log_info { int (*init)(void *hwdev); void (*deinit)(void *hwdev); }; int hinic_register_micro_log(struct hinic_micro_log_info *micro_log_info); void hinic_unregister_micro_log(struct hinic_micro_log_info *micro_log_info); void hinic_disable_mgmt_msg_report(void *hwdev); void hinic_set_func_deinit_flag(void *hwdev); void hinic_flush_mgmt_workq(void *hwdev); enum func_nic_state { HINIC_FUNC_NIC_DEL, HINIC_FUNC_NIC_ADD, }; struct hinic_func_nic_state { u8 state; u8 rsvd0; u16 func_idx; u8 rsvd1[16]; }; int hinic_set_func_nic_state(void *hwdev, struct hinic_func_nic_state *state); int hinic_get_func_nic_enable(void *hwdev, u16 glb_func_idx, bool *en); bool hinic_get_master_host_mbox_enable(void *hwdev); bool hinic_get_slave_host_enable(void *hwdev, u8 host_id); int hinic_func_own_get(void *hwdev); void hinic_func_own_free(void *hwdev); int hinic_global_func_id_get(void *hwdev, u16 *func_id); u16 hinic_pf_id_of_vf_hw(void *hwdev); u16 hinic_global_func_id_hw(void *hwdev); bool hinic_func_for_pt(void *hwdev); bool hinic_func_for_hwpt(void *hwdev); u32 hinic_get_db_size(void *cfg_reg_base, enum hinic_chip_mode *chip_mode); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_hwdev.c000066400000000000000000003704031502027332500215710ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwdev.h" #include "hinic_csr.h" #include "hinic_hwif.h" #include "hinic_msix_attr.h" #include "hinic_nic_io.h" #include "hinic_eqs.h" #include "hinic_api_cmd.h" #include "hinic_mgmt.h" #include "hinic_mbox.h" #include "hinic_wq.h" #include "hinic_cmdq.h" #include "hinic_nic_cfg.h" #include "hinic_hwif.h" #include "hinic_mgmt_interface.h" #include "hinic_multi_host_mgmt.h" #define HINIC_DEAULT_EQ_MSIX_PENDING_LIMIT 0 #define HINIC_DEAULT_EQ_MSIX_COALESC_TIMER_CFG 0xFF #define HINIC_DEAULT_EQ_MSIX_RESEND_TIMER_CFG 7 #define HINIC_WAIT_IO_STATUS_TIMEOUT 100 #define HINIC_FLR_TIMEOUT 1000 #define HINIC_HT_GPA_PAGE_SIZE 4096UL #define HINIC_PPF_HT_GPA_SET_RETRY_TIMES 10 #define HINIC_OK_FLAG_OK 0 #define HINIC_OK_FLAG_FAILED 1 #define HINIC_GET_SFP_INFO_REAL_TIME 0x1 #define HINIC_GLB_SO_RO_CFG_SHIFT 0x0 #define HINIC_GLB_SO_RO_CFG_MASK 0x1 #define HINIC_DISABLE_ORDER 0 #define HINIC_GLB_DMA_SO_RO_GET(val, member) \ (((val) >> HINIC_GLB_##member##_SHIFT) & HINIC_GLB_##member##_MASK) #define HINIC_GLB_DMA_SO_R0_CLEAR(val, member) \ ((val) & (~(HINIC_GLB_##member##_MASK << HINIC_GLB_##member##_SHIFT))) #define HINIC_GLB_DMA_SO_R0_SET(val, member) \ (((val) & HINIC_GLB_##member##_MASK) << HINIC_GLB_##member##_SHIFT) #define HINIC_MGMT_CHANNEL_STATUS_SHIFT 0x0 #define HINIC_MGMT_CHANNEL_STATUS_MASK 0x1 #define HINIC_ACTIVE_STATUS_MASK 0x80000000 #define HINIC_ACTIVE_STATUS_CLEAR 0x7FFFFFFF #define HINIC_ACTIVE_UCODE 0x1F80 /* bit7~bit12 */ #define HINIC_GET_MGMT_CHANNEL_STATUS(val, member) \ (((val) >> HINIC_##member##_SHIFT) & HINIC_##member##_MASK) #define HINIC_CLEAR_MGMT_CHANNEL_STATUS(val, member) \ ((val) & (~(HINIC_##member##_MASK << HINIC_##member##_SHIFT))) #define HINIC_SET_MGMT_CHANNEL_STATUS(val, member) \ (((val) & HINIC_##member##_MASK) << HINIC_##member##_SHIFT) #define HINIC_BOARD_IS_PHY(hwdev) \ ((hwdev)->board_info.board_type == 4 && \ (hwdev)->board_info.board_id == 24) struct comm_info_ht_gpa_set { u8 status; u8 version; u8 rsvd0[6]; u32 rsvd1; u32 rsvd2; u64 page_pa0; u64 page_pa1; }; struct comm_info_eqm_fix { u8 status; u8 version; u8 rsvd0[6]; u32 chunk_num; u32 search_gpa_num; }; struct comm_info_eqm_cfg { u8 status; u8 version; u8 rsvd0[6]; u32 ppf_id; u32 page_size; u32 valid; }; struct comm_info_eqm_search_gpa { u8 status; u8 version; u8 rsvd0[6]; u32 start_idx; u32 num; u32 resv0; u32 resv1; u64 gpa_hi52[0]; /*lint !e1501 */ }; struct hinic_cons_idx_attr { u8 status; u8 version; u8 rsvd0[6]; u16 func_idx; u8 dma_attr_off; u8 pending_limit; u8 coalescing_time; u8 intr_en; u16 intr_idx; u32 l2nic_sqn; u32 sq_id; u64 ci_addr; }; struct hinic_clear_doorbell { u8 status; u8 version; u8 rsvd0[6]; u16 func_idx; u8 ppf_idx; u8 rsvd1; }; struct hinic_clear_resource { u8 status; u8 version; u8 rsvd0[6]; u16 func_idx; u8 ppf_idx; u8 rsvd1; }; struct hinic_msix_config { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 msix_index; u8 pending_cnt; u8 coalesct_timer_cnt; u8 lli_tmier_cnt; u8 lli_credit_cnt; u8 resend_timer_cnt; u8 rsvd1[3]; }; enum func_tmr_bitmap_status { FUNC_TMR_BITMAP_DISABLE, FUNC_TMR_BITMAP_ENABLE, }; struct hinic_func_tmr_bitmap_op { u8 status; u8 version; u8 rsvd0[6]; u16 func_idx; u8 op_id; /* 0:start; 1:stop */ u8 ppf_idx; u32 rsvd1; }; struct hinic_ppf_tmr_op { u8 status; u8 version; u8 rsvd0[6]; u8 ppf_idx; u8 op_id; /* 0: stop timer; 1:start timer */ u8 rsvd1[2]; u32 rsvd2; }; struct hinic_cmd_set_res_state { u8 status; u8 version; u8 rsvd0[6]; u16 func_idx; u8 state; u8 rsvd1; u32 rsvd2; }; int hinic_hw_rx_buf_size[] = { HINIC_RX_BUF_SIZE_32B, HINIC_RX_BUF_SIZE_64B, HINIC_RX_BUF_SIZE_96B, HINIC_RX_BUF_SIZE_128B, HINIC_RX_BUF_SIZE_192B, HINIC_RX_BUF_SIZE_256B, HINIC_RX_BUF_SIZE_384B, HINIC_RX_BUF_SIZE_512B, HINIC_RX_BUF_SIZE_768B, HINIC_RX_BUF_SIZE_1K, HINIC_RX_BUF_SIZE_1_5K, HINIC_RX_BUF_SIZE_2K, HINIC_RX_BUF_SIZE_3K, HINIC_RX_BUF_SIZE_4K, HINIC_RX_BUF_SIZE_8K, HINIC_RX_BUF_SIZE_16K, }; /* vf-pf dma attr table */ struct hinic_vf_dma_attr_table { u8 status; u8 version; u8 rsvd0[6]; u16 func_idx; u8 func_dma_entry_num; u8 entry_idx; u8 st; u8 at; u8 ph; u8 no_snooping; u8 tph_en; u8 resv1[3]; }; struct hinic_led_info { u8 status; u8 version; u8 rsvd0[6]; u8 port; u8 type; u8 mode; u8 reset; }; struct hinic_comm_board_info { u8 status; u8 version; u8 rsvd0[6]; struct hinic_board_info info; u32 rsvd1[4]; }; #define PHY_DOING_INIT_TIMEOUT (15 * 1000) struct hinic_phy_init_status { u8 status; u8 version; u8 rsvd0[6]; u8 init_status; u8 rsvd1[3]; }; enum phy_init_status_type { PHY_INIT_DOING = 0, PHY_INIT_SUCCESS = 1, PHY_INIT_FAIL = 2, PHY_NONSUPPORT = 3, }; struct hinic_update_active { u8 status; u8 version; u8 rsvd0[6]; u32 update_flag; u32 update_status; }; enum hinic_bios_cfg_op_code { HINIC_BIOS_CFG_GET = 0, HINIC_BIOS_CFG_PF_BW_LIMIT = 0x1 << 6, }; struct hinic_bios_cfg_cmd { u8 status; u8 version; u8 rsvd0[6]; u32 op_code; u32 signature; u8 rsvd1[12]; u32 pf_bw_limit; u8 rsvd2[5]; u8 func_valid; u8 func_idx; u8 rsvd3; }; struct hinic_mgmt_watchdog_info { u8 status; u8 version; u8 rsvd0[6]; u32 curr_time_h; u32 curr_time_l; u32 task_id; u32 rsv; u32 reg[13]; u32 pc; u32 lr; u32 cpsr; u32 stack_top; u32 stack_bottom; u32 sp; u32 curr_used; u32 peak_used; u32 is_overflow; u32 stack_actlen; u8 data[1024]; }; struct hinic_fmw_act_ntc { u8 status; u8 version; u8 rsvd0[6]; u32 rsvd1[5]; }; struct hinic_ppf_state { u8 status; u8 version; u8 rsvd0[6]; u8 ppf_state; u8 rsvd1[3]; }; #define HINIC_PAGE_SIZE_HW(pg_size) ((u8)ilog2((u32)((pg_size) >> 12))) struct hinic_wq_page_size { u8 status; u8 version; u8 rsvd0[6]; u16 func_idx; u8 ppf_idx; /* real_size=4KB*2^page_size, range(0~20) must be checked by driver */ u8 page_size; u32 rsvd1; }; #define MAX_PCIE_DFX_BUF_SIZE 1024 struct hinic_pcie_dfx_ntc { u8 status; u8 version; u8 rsvd0[6]; int len; u32 rsvd; }; struct hinic_pcie_dfx_info { u8 status; u8 version; u8 rsvd0[6]; u8 host_id; u8 last; u8 rsvd[2]; u32 offset; u8 data[MAX_PCIE_DFX_BUF_SIZE]; }; struct hinic_hw_pf_infos_cmd { u8 status; u8 version; u8 rsvd0[6]; struct hinic_hw_pf_infos infos; }; enum hinic_sdi_mode_ops { HINIC_SDI_INFO_SET = 1U << 0, /* 1-save, 0-read */ HINIC_SDI_INFO_MODE = 1U << 1, }; struct hinic_sdi_mode_info { u8 status; u8 version; u8 rsvd0[6]; /* Op-Code: * Bit0: 0 - read configuration, 1 - write configuration * Bit1: 0 - ignored, 1 - get/set SDI Mode */ u32 opcode; u32 signature; u16 cur_sdi_mode; u16 cfg_sdi_mode; u32 rsvd1[29]; }; struct hinic_reg_info { u8 status; u8 version; u8 rsvd0[6]; u32 reg_addr; u32 val_length; u32 data[2]; }; #define HINIC_DMA_ATTR_ENTRY_ST_SHIFT 0 #define HINIC_DMA_ATTR_ENTRY_AT_SHIFT 8 #define HINIC_DMA_ATTR_ENTRY_PH_SHIFT 10 #define HINIC_DMA_ATTR_ENTRY_NO_SNOOPING_SHIFT 12 #define HINIC_DMA_ATTR_ENTRY_TPH_EN_SHIFT 13 #define HINIC_DMA_ATTR_ENTRY_ST_MASK 0xFF #define HINIC_DMA_ATTR_ENTRY_AT_MASK 0x3 #define HINIC_DMA_ATTR_ENTRY_PH_MASK 0x3 #define HINIC_DMA_ATTR_ENTRY_NO_SNOOPING_MASK 0x1 #define HINIC_DMA_ATTR_ENTRY_TPH_EN_MASK 0x1 #define HINIC_DMA_ATTR_ENTRY_SET(val, member) \ (((u32)(val) & HINIC_DMA_ATTR_ENTRY_##member##_MASK) << \ HINIC_DMA_ATTR_ENTRY_##member##_SHIFT) #define HINIC_DMA_ATTR_ENTRY_CLEAR(val, member) \ ((val) & (~(HINIC_DMA_ATTR_ENTRY_##member##_MASK \ << HINIC_DMA_ATTR_ENTRY_##member##_SHIFT))) #define HINIC_PCIE_ST_DISABLE 0 #define HINIC_PCIE_AT_DISABLE 0 #define HINIC_PCIE_PH_DISABLE 0 #define PCIE_MSIX_ATTR_ENTRY 0 struct hinic_cmd_fault_event { u8 status; u8 version; u8 rsvd0[6]; struct hinic_fault_event event; }; static void hinic_enable_mgmt_channel(void *hwdev, void *buf_out); static void hinic_set_mgmt_channel_status(void *handle, bool state); #define HINIC_QUEUE_MIN_DEPTH 6 #define HINIC_QUEUE_MAX_DEPTH 12 #define HINIC_MAX_RX_BUFFER_SIZE 15 #define CAP_INFO_MAC_LEN 512 #define VENDOR_MAX_LEN 17 static bool check_root_ctxt(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size) { struct hinic_root_ctxt *root_ctxt; if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) return false; root_ctxt = (struct hinic_root_ctxt *)buf_in; if (root_ctxt->ppf_idx != HINIC_HWIF_PPF_IDX(hwdev->hwif)) return false; if (root_ctxt->set_cmdq_depth) { if (root_ctxt->cmdq_depth >= HINIC_QUEUE_MIN_DEPTH && root_ctxt->cmdq_depth <= HINIC_QUEUE_MAX_DEPTH) return true; return false; } if (root_ctxt->rq_depth >= HINIC_QUEUE_MIN_DEPTH && root_ctxt->rq_depth <= HINIC_QUEUE_MAX_DEPTH && root_ctxt->sq_depth >= HINIC_QUEUE_MIN_DEPTH && root_ctxt->sq_depth <= HINIC_QUEUE_MAX_DEPTH && root_ctxt->rx_buf_sz <= HINIC_MAX_RX_BUFFER_SIZE) return true; if (!root_ctxt->rq_depth && !root_ctxt->sq_depth && !root_ctxt->rx_buf_sz) return true; return false; } static bool check_cmdq_ctxt(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size) { if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) return false; return hinic_cmdq_check_vf_ctxt(hwdev, buf_in); } static bool check_set_wq_page_size(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size) { struct hinic_wq_page_size *page_size_info = buf_in; if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) return false; if (page_size_info->ppf_idx != hinic_ppf_idx(hwdev)) return false; if (((1U << page_size_info->page_size) * 0x1000) != HINIC_DEFAULT_WQ_PAGE_SIZE) return false; return true; } static bool __mbox_check_tmr_bitmap(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size) { struct hinic_func_tmr_bitmap_op *bitmap_op = buf_in; if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) return false; if (bitmap_op->op_id == FUNC_TMR_BITMAP_ENABLE) { if (!hinic_get_ppf_status(hwdev)) { sdk_err(hwdev->dev_hdl, "PPF timer is not init, can't enable %d timer bitmap\n", func_idx); return false; } } if (bitmap_op->ppf_idx != hinic_ppf_idx(hwdev)) return false; return true; } struct vf_cmd_check_handle hw_cmd_support_vf[] = { {HINIC_MGMT_CMD_START_FLR, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_DMA_ATTR_SET, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_CMDQ_CTXT_SET, check_cmdq_ctxt}, {HINIC_MGMT_CMD_CMDQ_CTXT_GET, check_cmdq_ctxt}, {HINIC_MGMT_CMD_VAT_SET, check_root_ctxt}, {HINIC_MGMT_CMD_VAT_GET, check_root_ctxt}, {HINIC_MGMT_CMD_L2NIC_SQ_CI_ATTR_SET, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_L2NIC_SQ_CI_ATTR_GET, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_RES_STATE_SET, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_CEQ_CTRL_REG_WR_BY_UP, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_MSI_CTRL_REG_WR_BY_UP, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_MSI_CTRL_REG_RD_BY_UP, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_L2NIC_RESET, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_FAST_RECYCLE_MODE_SET, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_PAGESIZE_SET, check_set_wq_page_size}, {HINIC_MGMT_CMD_PAGESIZE_GET, hinic_mbox_check_func_id_8B}, {HINIC_MGMT_CMD_GET_PPF_STATE, NULL}, {HINIC_MGMT_CMD_FUNC_TMR_BITMAT_SET, __mbox_check_tmr_bitmap}, {HINIC_MGMT_CMD_GET_BOARD_INFO, NULL}, {HINIC_MGMT_CMD_GET_SDI_MODE, NULL}, }; struct hinic_mgmt_status_log { u8 status; const char *log; }; static struct hinic_mgmt_status_log mgmt_status_log[] = { {HINIC_MGMT_STATUS_ERR_PARAM, "Invalid parameter"}, {HINIC_MGMT_STATUS_ERR_FAILED, "Operation failed"}, {HINIC_MGMT_STATUS_ERR_PORT, "Invalid port"}, {HINIC_MGMT_STATUS_ERR_TIMEOUT, "Operation time out"}, {HINIC_MGMT_STATUS_ERR_NOMATCH, "Version not match"}, {HINIC_MGMT_STATUS_ERR_EXIST, "Entry exists"}, {HINIC_MGMT_STATUS_ERR_NOMEM, "Out of memory"}, {HINIC_MGMT_STATUS_ERR_INIT, "Feature not initialized"}, {HINIC_MGMT_STATUS_ERR_FAULT, "Invalid address"}, {HINIC_MGMT_STATUS_ERR_PERM, "Operation not permitted"}, {HINIC_MGMT_STATUS_ERR_EMPTY, "Table empty"}, {HINIC_MGMT_STATUS_ERR_FULL, "Table full"}, {HINIC_MGMT_STATUS_ERR_NOT_FOUND, "Not found"}, {HINIC_MGMT_STATUS_ERR_BUSY, "Device or resource busy "}, {HINIC_MGMT_STATUS_ERR_RESOURCE, "No resources for operation "}, {HINIC_MGMT_STATUS_ERR_CONFIG, "Invalid configuration"}, {HINIC_MGMT_STATUS_ERR_UNAVAIL, "Feature unavailable"}, {HINIC_MGMT_STATUS_ERR_CRC, "CRC check failed"}, {HINIC_MGMT_STATUS_ERR_NXIO, "No such device or address"}, {HINIC_MGMT_STATUS_ERR_ROLLBACK, "Chip rollback fail"}, {HINIC_MGMT_STATUS_ERR_LEN, "Length too short or too long"}, {HINIC_MGMT_STATUS_ERR_UNSUPPORT, "Feature not supported"}, }; static void __print_status_info(struct hinic_hwdev *dev, enum hinic_mod_type mod, u8 cmd, int index) { if (mod == HINIC_MOD_COMM) { sdk_err(dev->dev_hdl, "Mgmt process mod(0x%x) cmd(0x%x) fail: %s", mod, cmd, mgmt_status_log[index].log); } else if (mod == HINIC_MOD_L2NIC || mod == HINIC_MOD_HILINK) { if (HINIC_IS_VF(dev) && (cmd == HINIC_PORT_CMD_SET_MAC || cmd == HINIC_PORT_CMD_DEL_MAC || cmd == HINIC_PORT_CMD_UPDATE_MAC) && mgmt_status_log[index].status == HINIC_PF_SET_VF_ALREADY) return; nic_err(dev->dev_hdl, "Mgmt process mod(0x%x) cmd(0x%x) fail: %s", mod, cmd, mgmt_status_log[index].log); } } static bool hinic_status_need_special_handle(struct hinic_hwdev *dev, enum hinic_mod_type mod, u8 cmd, u8 status) { if (mod == HINIC_MOD_L2NIC) { /* optical module isn't plugged in */ if ((cmd == HINIC_PORT_CMD_GET_STD_SFP_INFO || cmd == HINIC_PORT_CMD_GET_SFP_INFO) && status == HINIC_MGMT_STATUS_ERR_NXIO) return true; if ((cmd == HINIC_PORT_CMD_SET_MAC || cmd == HINIC_PORT_CMD_UPDATE_MAC) && status == HINIC_MGMT_STATUS_ERR_EXIST) return true; } if (status == HINIC_MGMT_STATUS_ERR_UNSUPPORT) { if (mod == HINIC_MOD_L2NIC) sdk_warn(dev->dev_hdl, "Mgmt command: mod(0x%x) cmd(0x%x) not supported\n", mod, cmd); else sdk_warn(dev->dev_hdl, "Mgmt command: mod(0x%x) cmd(0x%x) not supported\n", mod, cmd); return true; } return false; } static void hinic_print_status_info(void *hwdev, enum hinic_mod_type mod, u8 cmd, const void *buf_out) { struct hinic_hwdev *dev = hwdev; int i, size; u8 status; if (!buf_out) return; if (mod != HINIC_MOD_COMM && mod != HINIC_MOD_L2NIC && mod != HINIC_MOD_HILINK) return; status = *(u8 *)buf_out; if (!status) return; if (hinic_status_need_special_handle(dev, mod, cmd, status)) return; size = ARRAY_SIZE(mgmt_status_log); for (i = 0; i < size; i++) { if (status == mgmt_status_log[i].status) { __print_status_info(dev, mod, cmd, i); return; } } if (mod == HINIC_MOD_COMM) { sdk_err(dev->dev_hdl, "Mgmt process mod(0x%x) cmd(0x%x) return driver unknown status(0x%x)\n", mod, cmd, status); } else if (mod == HINIC_MOD_L2NIC || mod == HINIC_MOD_HILINK) { nic_err(dev->dev_hdl, "Mgmt process mod(0x%x) cmd(0x%x) return driver unknown status(0x%x)\n", mod, cmd, status); } } void hinic_set_chip_present(void *hwdev) { ((struct hinic_hwdev *)hwdev)->chip_present_flag = HINIC_CHIP_PRESENT; } void hinic_set_chip_absent(void *hwdev) { struct hinic_hwdev *dev = hwdev; sdk_err(dev->dev_hdl, "Card not present\n"); dev->chip_present_flag = HINIC_CHIP_ABSENT; } int hinic_get_chip_present_flag(void *hwdev) { int flag; if (!hwdev) return -EINVAL; flag = ((struct hinic_hwdev *)hwdev)->chip_present_flag; return flag; } EXPORT_SYMBOL(hinic_get_chip_present_flag); void hinic_force_complete_all(void *hwdev) { struct hinic_hwdev *dev = (struct hinic_hwdev *)hwdev; struct hinic_recv_msg *recv_resp_msg; set_bit(HINIC_HWDEV_STATE_BUSY, &dev->func_state); if (hinic_func_type(dev) != TYPE_VF && hinic_is_hwdev_mod_inited(dev, HINIC_HWDEV_MGMT_INITED)) { recv_resp_msg = &dev->pf_to_mgmt->recv_resp_msg_from_mgmt; spin_lock_bh(&dev->pf_to_mgmt->sync_event_lock); if (dev->pf_to_mgmt->event_flag == SEND_EVENT_START) { complete(&recv_resp_msg->recv_done); dev->pf_to_mgmt->event_flag = SEND_EVENT_TIMEOUT; } spin_unlock_bh(&dev->pf_to_mgmt->sync_event_lock); } /* only flush sync cmdq to avoid blocking remove */ if (hinic_is_hwdev_mod_inited(dev, HINIC_HWDEV_CMDQ_INITED)) hinic_cmdq_flush_cmd(hwdev, &dev->cmdqs->cmdq[HINIC_CMDQ_SYNC]); clear_bit(HINIC_HWDEV_STATE_BUSY, &dev->func_state); } void hinic_detect_hw_present(void *hwdev) { u32 addr, attr1; addr = HINIC_CSR_FUNC_ATTR1_ADDR; attr1 = hinic_hwif_read_reg(((struct hinic_hwdev *)hwdev)->hwif, addr); if (attr1 == HINIC_PCIE_LINK_DOWN) { hinic_set_chip_absent(hwdev); hinic_force_complete_all(hwdev); } } void hinic_record_pcie_error(void *hwdev) { struct hinic_hwdev *dev = (struct hinic_hwdev *)hwdev; if (!hwdev) return; atomic_inc(&dev->hw_stats.fault_event_stats.pcie_fault_stats); } static int __func_send_mbox(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { int err; if (hinic_func_type(hwdev) == TYPE_VF) err = hinic_mbox_to_pf(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, timeout); else if (NEED_MBOX_FORWARD(hwdev)) err = hinic_mbox_to_host_sync(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, timeout); else err = -EFAULT; return err; } static int __pf_to_mgmt_pre_handle(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in) { struct hinic_update_active *active_info = buf_in; if (hinic_get_mgmt_channel_status(hwdev)) { if (mod == HINIC_MOD_COMM || mod == HINIC_MOD_L2NIC || mod == HINIC_MOD_CFGM || mod == HINIC_MOD_HILINK) return HINIC_DEV_BUSY_ACTIVE_FW; else return -EBUSY; } /* When only hot activation of ucode, mgmt channel can still be used * normally, otherwise it is not allowed to send commands to mgmt until * the hot activation is completed */ if (mod == HINIC_MOD_COMM && cmd == HINIC_MGMT_CMD_ACTIVATE_FW && (active_info->update_flag & ~HINIC_ACTIVE_UCODE)) { hinic_set_mgmt_channel_status(hwdev, true); /* Sleep 2s wait other pf's mgmt messages to complete */ msleep(2000); } return 0; } static void __pf_to_mgmt_after_handle(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, int sw_status, void *mgmt_status) { /* if activate fw is failed, set channel valid */ if (mod == HINIC_MOD_COMM && cmd == HINIC_MGMT_CMD_ACTIVATE_FW) { if (sw_status) hinic_set_mgmt_channel_status(hwdev, false); else hinic_enable_mgmt_channel(hwdev, mgmt_status); } } int hinic_pf_msg_to_mgmt_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { struct hinic_hwdev *dev = hwdev; int err; if (!hwdev) return -EINVAL; if (!((struct hinic_hwdev *)hwdev)->chip_present_flag) return -EPERM; if (NEED_MBOX_FORWARD(dev)) { if (!hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MBOX_INITED)) { return -EPERM; } err = __func_send_mbox(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, timeout); } else { if (!hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MGMT_INITED)) return -EPERM; if (in_size > HINIC_MSG_TO_MGMT_MAX_LEN) return -EINVAL; err = __pf_to_mgmt_pre_handle(hwdev, mod, cmd, buf_in); if (err) return err; err = hinic_pf_to_mgmt_sync(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, timeout); __pf_to_mgmt_after_handle(hwdev, mod, cmd, err, buf_out); } return err; } static bool is_sfp_info_cmd_cached(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_cmd_get_sfp_qsfp_info *sfp_info = NULL; struct hinic_port_routine_cmd *rt_cmd = NULL; struct card_node *chip_node = hwdev->chip_node; sfp_info = buf_in; if (sfp_info->port_id >= HINIC_MAX_PORT_ID || *out_size < sizeof(*sfp_info)) return false; if (sfp_info->version == HINIC_GET_SFP_INFO_REAL_TIME) return false; rt_cmd = &chip_node->rt_cmd[sfp_info->port_id]; mutex_lock(&chip_node->sfp_mutex); memcpy(buf_out, &rt_cmd->sfp_info, sizeof(*sfp_info)); mutex_unlock(&chip_node->sfp_mutex); return true; } static bool is_sfp_abs_cmd_cached(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_cmd_get_light_module_abs *abs = NULL; struct hinic_port_routine_cmd *rt_cmd = NULL; struct card_node *chip_node = hwdev->chip_node; abs = buf_in; if (abs->port_id >= HINIC_MAX_PORT_ID || *out_size < sizeof(*abs)) return false; if (abs->version == HINIC_GET_SFP_INFO_REAL_TIME) return false; rt_cmd = &chip_node->rt_cmd[abs->port_id]; mutex_lock(&chip_node->sfp_mutex); memcpy(buf_out, &rt_cmd->abs, sizeof(*abs)); mutex_unlock(&chip_node->sfp_mutex); return true; } static bool driver_processed_cmd(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct card_node *chip_node = hwdev->chip_node; if (mod == HINIC_MOD_L2NIC) { if (cmd == HINIC_PORT_CMD_GET_SFP_INFO && chip_node->rt_cmd->up_send_sfp_info) { return is_sfp_info_cmd_cached(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size); } else if (cmd == HINIC_PORT_CMD_GET_SFP_ABS && chip_node->rt_cmd->up_send_sfp_abs) { return is_sfp_abs_cmd_cached(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size); } } return false; } int hinic_msg_to_mgmt_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { struct hinic_hwdev *dev = hwdev; unsigned long end; int err; if (!hwdev) return -EINVAL; if (!(dev->chip_present_flag)) return -EPERM; end = jiffies + msecs_to_jiffies(HINIC_DEV_ACTIVE_FW_TIMEOUT); if (hinic_func_type(hwdev) == TYPE_VF || NEED_MBOX_FORWARD(dev)) { if (!hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MBOX_INITED)) return -EPERM; do { if (!hinic_get_chip_present_flag(hwdev)) break; err = __func_send_mbox(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, timeout); if (err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) { hinic_print_status_info(hwdev, mod, cmd, buf_out); return err; } msleep(1000); } while (time_before(jiffies, end)); err = __func_send_mbox(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, timeout); } else { if (driver_processed_cmd(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size)) return 0; do { if (!hinic_get_mgmt_channel_status(hwdev) || !hinic_get_chip_present_flag(hwdev)) break; msleep(1000); } while (time_before(jiffies, end)); err = hinic_pf_msg_to_mgmt_sync(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, timeout); } hinic_print_status_info(hwdev, mod, cmd, buf_out); return err; } EXPORT_SYMBOL(hinic_msg_to_mgmt_sync); /* PF/VF send msg to uP by api cmd, and return immediately */ int hinic_msg_to_mgmt_async(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size) { int err; if (!hwdev) return -EINVAL; if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag) || !hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MGMT_INITED) || hinic_get_mgmt_channel_status(hwdev)) return -EPERM; if (hinic_func_type(hwdev) == TYPE_VF) { err = -EFAULT; sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Mailbox don't support async cmd\n"); } else { err = hinic_pf_to_mgmt_async(hwdev, mod, cmd, buf_in, in_size); } return err; } EXPORT_SYMBOL(hinic_msg_to_mgmt_async); int hinic_msg_to_mgmt_no_ack(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size) { struct hinic_hwdev *dev = hwdev; int err; if (!hwdev) return -EINVAL; if (!(dev->chip_present_flag)) return -EPERM; if (hinic_func_type(hwdev) == TYPE_VF || NEED_MBOX_FORWARD(dev)) { if (!hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MBOX_INITED)) return -EPERM; if (hinic_func_type(hwdev) == TYPE_VF) err = hinic_mbox_to_pf_no_ack(hwdev, mod, cmd, buf_in, in_size); else err = hinic_mbox_to_host_no_ack(hwdev, mod, cmd, buf_in, in_size); } else { err = hinic_pf_to_mgmt_no_ack(hwdev, mod, cmd, buf_in, in_size); } return err; } int hinic_mbox_to_vf(void *hwdev, enum hinic_mod_type mod, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { int err; if (!hwdev) return -EINVAL; err = __hinic_mbox_to_vf(hwdev, mod, vf_id, cmd, buf_in, in_size, buf_out, out_size, timeout); if (err == MBOX_ERRCODE_UNKNOWN_DES_FUNC) { /* VF already in error condiction */ sdk_warn(((struct hinic_hwdev *)hwdev)->dev_hdl, "VF%d not initialized, disconnect it\n", vf_id); hinic_unregister_vf_msg_handler(hwdev, vf_id); } return err; } EXPORT_SYMBOL(hinic_mbox_to_vf); int hinic_clp_to_mgmt(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *dev = hwdev; int err; if (!dev) return -EINVAL; if (!dev->chip_present_flag) return -EPERM; if (hinic_func_type(hwdev) == TYPE_VF || NEED_MBOX_FORWARD(dev)) return -EINVAL; if (!hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_CLP_INITED)) return -EPERM; err = hinic_pf_clp_to_mgmt(dev, mod, cmd, buf_in, in_size, buf_out, out_size); return err; } /** * hinic_cpu_to_be32 - convert data to big endian 32 bit format * @data: the data to convert * @len: length of data to convert, must be Multiple of 4B */ void hinic_cpu_to_be32(void *data, int len) { int i, chunk_sz = sizeof(u32); u32 *mem = data; if (!data) return; len = len / chunk_sz; for (i = 0; i < len; i++) { *mem = cpu_to_be32(*mem); mem++; } } EXPORT_SYMBOL(hinic_cpu_to_be32); /** * hinic_be32_to_cpu - convert data from big endian 32 bit format * @data: the data to convert * @len: length of data to convert */ void hinic_be32_to_cpu(void *data, int len) { int i, chunk_sz = sizeof(u32); u32 *mem = data; if (!data) return; len = len / chunk_sz; for (i = 0; i < len; i++) { *mem = be32_to_cpu(*mem); mem++; } } EXPORT_SYMBOL(hinic_be32_to_cpu); /** * hinic_set_sge - set dma area in scatter gather entry * @sge: scatter gather entry * @addr: dma address * @len: length of relevant data in the dma address */ void hinic_set_sge(struct hinic_sge *sge, dma_addr_t addr, u32 len) { sge->hi_addr = upper_32_bits(addr); sge->lo_addr = lower_32_bits(addr); sge->len = len; } /** * hinic_sge_to_dma - get dma address from scatter gather entry * @sge: scatter gather entry * * Return dma address of sg entry */ dma_addr_t hinic_sge_to_dma(struct hinic_sge *sge) { return (dma_addr_t)((((u64)sge->hi_addr) << 32) | sge->lo_addr); } int hinic_set_ci_table(void *hwdev, u16 q_id, struct hinic_sq_attr *attr) { struct hinic_cons_idx_attr cons_idx_attr = {0}; u16 out_size = sizeof(cons_idx_attr); int err; if (!hwdev || !attr) return -EINVAL; err = hinic_global_func_id_get(hwdev, &cons_idx_attr.func_idx); if (err) return err; cons_idx_attr.dma_attr_off = attr->dma_attr_off; cons_idx_attr.pending_limit = attr->pending_limit; cons_idx_attr.coalescing_time = attr->coalescing_time; if (attr->intr_en) { cons_idx_attr.intr_en = attr->intr_en; cons_idx_attr.intr_idx = attr->intr_idx; } cons_idx_attr.l2nic_sqn = attr->l2nic_sqn; cons_idx_attr.sq_id = q_id; cons_idx_attr.ci_addr = attr->ci_dma_base; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_L2NIC_SQ_CI_ATTR_SET, &cons_idx_attr, sizeof(cons_idx_attr), &cons_idx_attr, &out_size, 0); if (err || !out_size || cons_idx_attr.status) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to set ci attribute table, err: %d, status: 0x%x, out_size: 0x%x\n", err, cons_idx_attr.status, out_size); return -EFAULT; } return 0; } EXPORT_SYMBOL(hinic_set_ci_table); static int hinic_set_cmdq_depth(struct hinic_hwdev *hwdev, u16 cmdq_depth) { struct hinic_root_ctxt root_ctxt = {0}; u16 out_size = sizeof(root_ctxt); int err; err = hinic_global_func_id_get(hwdev, &root_ctxt.func_idx); if (err) return err; root_ctxt.ppf_idx = hinic_ppf_idx(hwdev); root_ctxt.set_cmdq_depth = 1; root_ctxt.cmdq_depth = (u8)ilog2(cmdq_depth); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_VAT_SET, &root_ctxt, sizeof(root_ctxt), &root_ctxt, &out_size, 0); if (err || !out_size || root_ctxt.status) { sdk_err(hwdev->dev_hdl, "Failed to set cmdq depth, err: %d, status: 0x%x, out_size: 0x%x\n", err, root_ctxt.status, out_size); return -EFAULT; } return 0; } static u16 get_hw_rx_buf_size(int rx_buf_sz) { u16 num_hw_types = sizeof(hinic_hw_rx_buf_size) / sizeof(hinic_hw_rx_buf_size[0]); u16 i; for (i = 0; i < num_hw_types; i++) { if (hinic_hw_rx_buf_size[i] == rx_buf_sz) return i; } pr_err("Chip can't support rx buf size of %d\n", rx_buf_sz); return DEFAULT_RX_BUF_SIZE; /* default 2K */ } int hinic_set_root_ctxt(void *hwdev, u16 rq_depth, u16 sq_depth, int rx_buf_sz) { struct hinic_root_ctxt root_ctxt = {0}; u16 out_size = sizeof(root_ctxt); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &root_ctxt.func_idx); if (err) return err; root_ctxt.ppf_idx = hinic_ppf_idx(hwdev); root_ctxt.set_cmdq_depth = 0; root_ctxt.cmdq_depth = 0; root_ctxt.lro_en = 1; root_ctxt.rq_depth = (u16)ilog2(rq_depth); root_ctxt.rx_buf_sz = get_hw_rx_buf_size(rx_buf_sz); root_ctxt.sq_depth = (u16)ilog2(sq_depth); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_VAT_SET, &root_ctxt, sizeof(root_ctxt), &root_ctxt, &out_size, 0); if (err || !out_size || root_ctxt.status) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to set root context, err: %d, status: 0x%x, out_size: 0x%x\n", err, root_ctxt.status, out_size); return -EFAULT; } return 0; } EXPORT_SYMBOL(hinic_set_root_ctxt); int hinic_clean_root_ctxt(void *hwdev) { struct hinic_root_ctxt root_ctxt = {0}; u16 out_size = sizeof(root_ctxt); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &root_ctxt.func_idx); if (err) return err; root_ctxt.ppf_idx = hinic_ppf_idx(hwdev); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_VAT_SET, &root_ctxt, sizeof(root_ctxt), &root_ctxt, &out_size, 0); if (err || !out_size || root_ctxt.status) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to clean root context, err: %d, status: 0x%x, out_size: 0x%x\n", err, root_ctxt.status, out_size); return -EFAULT; } return 0; } EXPORT_SYMBOL(hinic_clean_root_ctxt); static int wait_for_flr_finish(struct hinic_hwif *hwif) { u32 cnt = 0; enum hinic_pf_status status; while (cnt < HINIC_FLR_TIMEOUT) { status = hinic_get_pf_status(hwif); if (status == HINIC_PF_STATUS_FLR_FINISH_FLAG) { hinic_set_pf_status(hwif, HINIC_PF_STATUS_ACTIVE_FLAG); return 0; } usleep_range(9900, 10000); cnt++; } return -EFAULT; } #define HINIC_WAIT_CMDQ_IDLE_TIMEOUT 5000 static int wait_cmdq_stop(struct hinic_hwdev *hwdev) { enum hinic_cmdq_type cmdq_type; struct hinic_cmdqs *cmdqs = hwdev->cmdqs; u32 cnt = 0; int err = 0; if (!(cmdqs->status & HINIC_CMDQ_ENABLE)) return 0; cmdqs->status &= ~HINIC_CMDQ_ENABLE; while (cnt < HINIC_WAIT_CMDQ_IDLE_TIMEOUT && hwdev->chip_present_flag) { err = 0; cmdq_type = HINIC_CMDQ_SYNC; for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { if (!hinic_cmdq_idle(&cmdqs->cmdq[cmdq_type])) { err = -EBUSY; break; } } if (!err) return 0; usleep_range(500, 1000); cnt++; } cmdq_type = HINIC_CMDQ_SYNC; for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { if (!hinic_cmdq_idle(&cmdqs->cmdq[cmdq_type])) sdk_err(hwdev->dev_hdl, "Cmdq %d busy\n", cmdq_type); } cmdqs->status |= HINIC_CMDQ_ENABLE; return err; } static int hinic_vf_rx_tx_flush(struct hinic_hwdev *hwdev) { struct hinic_clear_resource clr_res = {0}; int err; err = wait_cmdq_stop(hwdev); if (err) sdk_warn(hwdev->dev_hdl, "Cmdq is still working, please check CMDQ timeout value is reasonable\n"); err = hinic_global_func_id_get(hwdev, &clr_res.func_idx); if (err) return err; clr_res.ppf_idx = HINIC_HWIF_PPF_IDX(hwdev->hwif); err = hinic_mbox_to_pf_no_ack(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_START_FLR, &clr_res, sizeof(clr_res)); if (err) sdk_warn(hwdev->dev_hdl, "Failed to notice flush message\n"); /* PF firstly set VF doorbell flush csr to be disabled. After PF finish * VF resources flush, PF will set VF doorbell flush csr to be enabled. */ err = wait_until_doorbell_flush_states(hwdev->hwif, DISABLE_DOORBELL); if (err) sdk_warn(hwdev->dev_hdl, "Wait doorbell flush disable timeout\n"); err = wait_until_doorbell_flush_states(hwdev->hwif, ENABLE_DOORBELL); if (err) sdk_warn(hwdev->dev_hdl, "Wait doorbell flush enable timeout\n"); err = hinic_reinit_cmdq_ctxts(hwdev); if (err) sdk_warn(hwdev->dev_hdl, "Failed to reinit cmdq\n"); return 0; } static void hinic_pf_set_vf_db_flush(struct hinic_hwdev *hwdev, u16 vf_id, enum hinic_doorbell_ctrl val) { u32 addr, vf_attr4; addr = HINIC_PF_CSR_VF_FLUSH_OFF(vf_id); vf_attr4 = hinic_hwif_read_reg(hwdev->hwif, addr); vf_attr4 = HINIC_AF4_CLEAR(vf_attr4, DOORBELL_CTRL); vf_attr4 |= HINIC_AF4_SET(val, DOORBELL_CTRL); hinic_hwif_write_reg(hwdev->hwif, addr, vf_attr4); } static int hinic_vf_rx_tx_flush_in_pf(struct hinic_hwdev *hwdev, u16 vf_id) { struct hinic_clear_doorbell clear_db = {0}; struct hinic_clear_resource clr_res = {0}; u16 glb_vf_func_id; u16 out_size; int err; int ret = 0; /* disable vf doorbell flush csr */ hinic_pf_set_vf_db_flush(hwdev, vf_id, DISABLE_DOORBELL); /* doorbell flush */ out_size = sizeof(clear_db); glb_vf_func_id = HINIC_HWIF_GLOBAL_VF_OFFSET(hwdev->hwif) + vf_id; clear_db.func_idx = glb_vf_func_id; clear_db.ppf_idx = HINIC_HWIF_PPF_IDX(hwdev->hwif); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_FLUSH_DOORBELL, &clear_db, sizeof(clear_db), &clear_db, &out_size, 0); if (err || !out_size || clear_db.status) { sdk_warn(hwdev->dev_hdl, "Failed to flush doorbell, err: %d, status: 0x%x, out_size: 0x%x\n", err, clear_db.status, out_size); if (err) ret = err; else ret = -EFAULT; } /* wait ucode stop I/O */ msleep(100); /* notice up begine vf flush */ out_size = sizeof(clr_res); clr_res.func_idx = glb_vf_func_id; clr_res.ppf_idx = HINIC_HWIF_PPF_IDX(hwdev->hwif); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_START_FLR, &clr_res, sizeof(clr_res), &clr_res, &out_size, 0); if (err || !out_size || clr_res.status) { sdk_warn(hwdev->dev_hdl, "Failed to start flr, err: %d, status: 0x%x, out_size: 0x%x\n", err, clr_res.status, out_size); ret = err ? err : (-EFAULT); } /* enable vf doorbell flush csr */ hinic_pf_set_vf_db_flush(hwdev, vf_id, ENABLE_DOORBELL); return ret; } static int hinic_pf_rx_tx_flush(struct hinic_hwdev *hwdev) { struct hinic_hwif *hwif = hwdev->hwif; struct hinic_clear_doorbell clear_db = {0}; struct hinic_clear_resource clr_res = {0}; u16 out_size, func_id; int err; int ret = 0; /* wait ucode stop I/O */ msleep(100); err = wait_cmdq_stop(hwdev); if (err) { sdk_warn(hwdev->dev_hdl, "CMDQ is still working, please check CMDQ timeout value is reasonable\n"); ret = err; } hinic_disable_doorbell(hwif); out_size = sizeof(clear_db); func_id = hinic_global_func_id_hw(hwdev); clear_db.func_idx = func_id; clear_db.ppf_idx = HINIC_HWIF_PPF_IDX(hwif); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_FLUSH_DOORBELL, &clear_db, sizeof(clear_db), &clear_db, &out_size, 0); if (err || !out_size || clear_db.status) { sdk_warn(hwdev->dev_hdl, "Failed to flush doorbell, err: %d, status: 0x%x, out_size: 0x%x\n", err, clear_db.status, out_size); ret = err ? err : (-EFAULT); } hinic_set_pf_status(hwif, HINIC_PF_STATUS_FLR_START_FLAG); clr_res.func_idx = func_id; clr_res.ppf_idx = HINIC_HWIF_PPF_IDX(hwif); err = hinic_msg_to_mgmt_no_ack(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_START_FLR, &clr_res, sizeof(clr_res)); if (err) { sdk_warn(hwdev->dev_hdl, "Failed to notice flush message\n"); ret = err; } err = wait_for_flr_finish(hwif); if (err) { sdk_warn(hwdev->dev_hdl, "Wait firmware FLR timeout\n"); ret = err; } hinic_enable_doorbell(hwif); err = hinic_reinit_cmdq_ctxts(hwdev); if (err) { sdk_warn(hwdev->dev_hdl, "Failed to reinit cmdq\n"); ret = err; } return ret; } int hinic_func_rx_tx_flush(void *hwdev) { struct hinic_hwdev *dev = hwdev; if (!hwdev) return -EINVAL; if (!dev->chip_present_flag) return 0; if (HINIC_FUNC_TYPE(dev) == TYPE_VF) return hinic_vf_rx_tx_flush(dev); else return hinic_pf_rx_tx_flush(dev); } EXPORT_SYMBOL(hinic_func_rx_tx_flush); int hinic_get_interrupt_cfg(void *hwdev, struct nic_interrupt_info *interrupt_info) { struct hinic_hwdev *nic_hwdev = hwdev; struct hinic_msix_config msix_cfg = {0}; u16 out_size = sizeof(msix_cfg); int err; if (!hwdev || !interrupt_info) return -EINVAL; err = hinic_global_func_id_get(hwdev, &msix_cfg.func_id); if (err) return err; msix_cfg.msix_index = interrupt_info->msix_index; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_MSI_CTRL_REG_RD_BY_UP, &msix_cfg, sizeof(msix_cfg), &msix_cfg, &out_size, 0); if (err || !out_size || msix_cfg.status) { sdk_err(nic_hwdev->dev_hdl, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n", err, msix_cfg.status, out_size); return -EINVAL; } interrupt_info->lli_credit_limit = msix_cfg.lli_credit_cnt; interrupt_info->lli_timer_cfg = msix_cfg.lli_tmier_cnt; interrupt_info->pending_limt = msix_cfg.pending_cnt; interrupt_info->coalesc_timer_cfg = msix_cfg.coalesct_timer_cnt; interrupt_info->resend_timer_cfg = msix_cfg.resend_timer_cnt; return 0; } EXPORT_SYMBOL(hinic_get_interrupt_cfg); int hinic_set_interrupt_cfg_direct(void *hwdev, struct nic_interrupt_info *interrupt_info) { struct hinic_hwdev *nic_hwdev = hwdev; struct hinic_msix_config msix_cfg = {0}; u16 out_size = sizeof(msix_cfg); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &msix_cfg.func_id); if (err) return err; msix_cfg.msix_index = (u16)interrupt_info->msix_index; msix_cfg.lli_credit_cnt = interrupt_info->lli_credit_limit; msix_cfg.lli_tmier_cnt = interrupt_info->lli_timer_cfg; msix_cfg.pending_cnt = interrupt_info->pending_limt; msix_cfg.coalesct_timer_cnt = interrupt_info->coalesc_timer_cfg; msix_cfg.resend_timer_cnt = interrupt_info->resend_timer_cfg; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_MSI_CTRL_REG_WR_BY_UP, &msix_cfg, sizeof(msix_cfg), &msix_cfg, &out_size, 0); if (err || !out_size || msix_cfg.status) { sdk_err(nic_hwdev->dev_hdl, "Failed to set interrupt config, err: %d, status: 0x%x, out size: 0x%x\n", err, msix_cfg.status, out_size); return -EFAULT; } return 0; } int hinic_set_interrupt_cfg(void *hwdev, struct nic_interrupt_info interrupt_info) { struct nic_interrupt_info temp_info; int err; if (!hwdev) return -EINVAL; temp_info.msix_index = interrupt_info.msix_index; err = hinic_get_interrupt_cfg(hwdev, &temp_info); if (err) return -EINVAL; if (!interrupt_info.lli_set) { interrupt_info.lli_credit_limit = temp_info.lli_credit_limit; interrupt_info.lli_timer_cfg = temp_info.lli_timer_cfg; } if (!interrupt_info.interrupt_coalesc_set) { interrupt_info.pending_limt = temp_info.pending_limt; interrupt_info.coalesc_timer_cfg = temp_info.coalesc_timer_cfg; interrupt_info.resend_timer_cfg = temp_info.resend_timer_cfg; } return hinic_set_interrupt_cfg_direct(hwdev, &interrupt_info); } EXPORT_SYMBOL(hinic_set_interrupt_cfg); void hinic_misx_intr_clear_resend_bit(void *hwdev, u16 msix_idx, u8 clear_resend_en) { struct hinic_hwif *hwif; u32 msix_ctrl = 0, addr; if (!hwdev) return; hwif = ((struct hinic_hwdev *)hwdev)->hwif; msix_ctrl = HINIC_MSIX_CNT_SET(clear_resend_en, RESEND_TIMER); addr = HINIC_CSR_MSIX_CNT_ADDR(msix_idx); hinic_hwif_write_reg(hwif, addr, msix_ctrl); } EXPORT_SYMBOL(hinic_misx_intr_clear_resend_bit); static int init_aeqs_msix_attr(struct hinic_hwdev *hwdev) { struct hinic_aeqs *aeqs = hwdev->aeqs; struct nic_interrupt_info info = {0}; struct hinic_eq *eq; int q_id; int err; info.lli_set = 0; info.interrupt_coalesc_set = 1; info.pending_limt = HINIC_DEAULT_EQ_MSIX_PENDING_LIMIT; info.coalesc_timer_cfg = HINIC_DEAULT_EQ_MSIX_COALESC_TIMER_CFG; info.resend_timer_cfg = HINIC_DEAULT_EQ_MSIX_RESEND_TIMER_CFG; for (q_id = aeqs->num_aeqs - 1; q_id >= 0; q_id--) { eq = &aeqs->aeq[q_id]; info.msix_index = eq->eq_irq.msix_entry_idx; err = hinic_set_interrupt_cfg_direct(hwdev, &info); if (err) { sdk_err(hwdev->dev_hdl, "Failed to set msix attr for aeq %d\n", q_id); return -EFAULT; } } hinic_set_mbox_seg_ack_mod(hwdev, HINIC_MBOX_SEND_MSG_INT); return 0; } static int init_ceqs_msix_attr(struct hinic_hwdev *hwdev) { struct hinic_ceqs *ceqs = hwdev->ceqs; struct nic_interrupt_info info = {0}; struct hinic_eq *eq; u16 q_id; int err; info.lli_set = 0; info.interrupt_coalesc_set = 1; info.pending_limt = HINIC_DEAULT_EQ_MSIX_PENDING_LIMIT; info.coalesc_timer_cfg = HINIC_DEAULT_EQ_MSIX_COALESC_TIMER_CFG; info.resend_timer_cfg = HINIC_DEAULT_EQ_MSIX_RESEND_TIMER_CFG; for (q_id = 0; q_id < ceqs->num_ceqs; q_id++) { eq = &ceqs->ceq[q_id]; info.msix_index = eq->eq_irq.msix_entry_idx; err = hinic_set_interrupt_cfg(hwdev, info); if (err) { sdk_err(hwdev->dev_hdl, "Failed to set msix attr for ceq %d\n", q_id); return -EFAULT; } } return 0; } /** * set_pf_dma_attr_entry - set the dma attributes for entry * @hwdev: the pointer to hw device * @entry_idx: the entry index in the dma table * @st: PCIE TLP steering tag * @at: PCIE TLP AT field * @ph: PCIE TLP Processing Hint field * @no_snooping: PCIE TLP No snooping * @tph_en: PCIE TLP Processing Hint Enable */ static void set_pf_dma_attr_entry(struct hinic_hwdev *hwdev, u32 entry_idx, u8 st, u8 at, u8 ph, enum hinic_pcie_nosnoop no_snooping, enum hinic_pcie_tph tph_en) { u32 addr, val, dma_attr_entry; /* Read Modify Write */ addr = HINIC_CSR_DMA_ATTR_TBL_ADDR(entry_idx); val = hinic_hwif_read_reg(hwdev->hwif, addr); val = HINIC_DMA_ATTR_ENTRY_CLEAR(val, ST) & HINIC_DMA_ATTR_ENTRY_CLEAR(val, AT) & HINIC_DMA_ATTR_ENTRY_CLEAR(val, PH) & HINIC_DMA_ATTR_ENTRY_CLEAR(val, NO_SNOOPING) & HINIC_DMA_ATTR_ENTRY_CLEAR(val, TPH_EN); dma_attr_entry = HINIC_DMA_ATTR_ENTRY_SET(st, ST) | HINIC_DMA_ATTR_ENTRY_SET(at, AT) | HINIC_DMA_ATTR_ENTRY_SET(ph, PH) | HINIC_DMA_ATTR_ENTRY_SET(no_snooping, NO_SNOOPING) | HINIC_DMA_ATTR_ENTRY_SET(tph_en, TPH_EN); val |= dma_attr_entry; hinic_hwif_write_reg(hwdev->hwif, addr, val); } static int set_vf_dma_attr_entry(struct hinic_hwdev *hwdev, u8 entry_idx, u8 st, u8 at, u8 ph, enum hinic_pcie_nosnoop no_snooping, enum hinic_pcie_tph tph_en) { struct hinic_vf_dma_attr_table attr = {0}; u16 out_size = sizeof(attr); int err; err = hinic_global_func_id_get(hwdev, &attr.func_idx); if (err) return err; attr.func_dma_entry_num = hinic_dma_attr_entry_num(hwdev); attr.entry_idx = entry_idx; attr.st = st; attr.at = at; attr.ph = ph; attr.no_snooping = no_snooping; attr.tph_en = tph_en; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_DMA_ATTR_SET, &attr, sizeof(attr), &attr, &out_size, 0); if (err || !out_size || attr.status) { sdk_err(hwdev->dev_hdl, "Failed to set dma attribute, err: %d, status: 0x%x, out_size: 0x%x\n", err, attr.status, out_size); return -EFAULT; } return 0; } /** * dma_attr_table_init - initialize the default dma attributes * @hwdev: the pointer to hw device * Return: 0 - success, negative - failure */ static int dma_attr_table_init(struct hinic_hwdev *hwdev) { int err = 0; if (HINIC_IS_VF(hwdev)) err = set_vf_dma_attr_entry(hwdev, PCIE_MSIX_ATTR_ENTRY, HINIC_PCIE_ST_DISABLE, HINIC_PCIE_AT_DISABLE, HINIC_PCIE_PH_DISABLE, HINIC_PCIE_SNOOP, HINIC_PCIE_TPH_DISABLE); else set_pf_dma_attr_entry(hwdev, PCIE_MSIX_ATTR_ENTRY, HINIC_PCIE_ST_DISABLE, HINIC_PCIE_AT_DISABLE, HINIC_PCIE_PH_DISABLE, HINIC_PCIE_SNOOP, HINIC_PCIE_TPH_DISABLE); return err; } static int resources_state_set(struct hinic_hwdev *hwdev, enum hinic_res_state state) { struct hinic_cmd_set_res_state res_state = {0}; u16 out_size = sizeof(res_state); int err; err = hinic_global_func_id_get(hwdev, &res_state.func_idx); if (err) return err; res_state.state = state; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_RES_STATE_SET, &res_state, sizeof(res_state), &res_state, &out_size, 0); if (err || !out_size || res_state.status) { sdk_err(hwdev->dev_hdl, "Failed to set resources state, err: %d, status: 0x%x, out_size: 0x%x\n", err, res_state.status, out_size); return -EFAULT; } return 0; } static void comm_mgmt_msg_handler(void *hwdev, void *pri_handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt = pri_handle; u8 cmd_idx; u32 *mem; u16 i; for (cmd_idx = 0; cmd_idx < pf_to_mgmt->proc.cmd_num; cmd_idx++) { if (cmd == pf_to_mgmt->proc.info[cmd_idx].cmd) { if (!pf_to_mgmt->proc.info[cmd_idx].proc) { sdk_warn(pf_to_mgmt->hwdev->dev_hdl, "PF recv up comm msg handle null, cmd(0x%x)\n", cmd); } else { pf_to_mgmt->proc.info[cmd_idx].proc(hwdev, buf_in, in_size, buf_out, out_size); } return; } } sdk_warn(pf_to_mgmt->hwdev->dev_hdl, "Received mgmt cpu event: 0x%x\n", cmd); mem = buf_in; for (i = 0; i < (in_size / sizeof(u32)); i++) { pr_info("0x%x\n", *mem); mem++; } *out_size = 0; } static int hinic_vf_get_ppf_init_state(void *handle, void *buf_out, u16 *out_size) { struct hinic_hwdev *hwdev = handle; struct hinic_ppf_state *ppf_state = buf_out; struct card_node *chip_node = hwdev->chip_node; ppf_state->ppf_state = (u8)chip_node->ppf_state; *out_size = sizeof(*ppf_state); return 0; } int hinic_get_sdi_mode(struct hinic_hwdev *hwdev, u16 *cur_mode) { struct hinic_sdi_mode_info sdi_mode = {0}; u16 out_size = sizeof(sdi_mode); int err; sdi_mode.opcode = HINIC_SDI_INFO_MODE & (~HINIC_SDI_INFO_SET); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_GET_SDI_MODE, &sdi_mode, sizeof(sdi_mode), &sdi_mode, &out_size, 0); if ((sdi_mode.status != HINIC_MGMT_CMD_UNSUPPORTED && sdi_mode.status) || err || !out_size) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get sdi mode info, err: %d, status: 0x%x, out size: 0x%x\n", err, sdi_mode.status, out_size); return -EFAULT; } *cur_mode = sdi_mode.cur_sdi_mode; return sdi_mode.status; } int comm_pf_mbox_handler(void *handle, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { int err = 0; u8 size = ARRAY_SIZE(hw_cmd_support_vf); if (!hinic_mbox_check_cmd_valid(handle, hw_cmd_support_vf, vf_id, cmd, buf_in, in_size, size)) { sdk_err(((struct hinic_hwdev *)handle)->dev_hdl, "PF Receive VF(%d) common cmd(0x%x) or mbox len(0x%x) is invalid\n", vf_id + hinic_glb_pf_vf_offset(handle), cmd, in_size); err = HINIC_MBOX_VF_CMD_ERROR; return err; } if (cmd == HINIC_MGMT_CMD_START_FLR) { *out_size = 0; err = hinic_vf_rx_tx_flush_in_pf(handle, vf_id); } else if (cmd == HINIC_MGMT_CMD_GET_PPF_STATE) { err = hinic_vf_get_ppf_init_state(handle, buf_out, out_size); } else { err = hinic_pf_msg_to_mgmt_sync(handle, HINIC_MOD_COMM, cmd, buf_in, in_size, buf_out, out_size, 0U); if (err && err != HINIC_DEV_BUSY_ACTIVE_FW && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) sdk_err(((struct hinic_hwdev *)handle)->dev_hdl, "PF mbox common cmd %d callback handler err: %d\n", cmd, err); } return err; } static int hinic_comm_aeqs_init(struct hinic_hwdev *hwdev) { struct irq_info aeq_irqs[HINIC_MAX_AEQS] = {{0} }; u16 num_aeqs, resp_num_irq = 0, i; int err; num_aeqs = HINIC_HWIF_NUM_AEQS(hwdev->hwif); if (num_aeqs > HINIC_MAX_AEQS) { sdk_warn(hwdev->dev_hdl, "Adjust aeq num to %d\n", HINIC_MAX_AEQS); num_aeqs = HINIC_MAX_AEQS; } err = hinic_alloc_irqs(hwdev, SERVICE_T_INTF, num_aeqs, aeq_irqs, &resp_num_irq); if (err) { sdk_err(hwdev->dev_hdl, "Failed to alloc aeq irqs, num_aeqs: %d\n", num_aeqs); return err; } if (resp_num_irq < num_aeqs) { sdk_warn(hwdev->dev_hdl, "Adjust aeq num to %d\n", resp_num_irq); num_aeqs = resp_num_irq; } err = hinic_aeqs_init(hwdev, num_aeqs, aeq_irqs); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init aeqs\n"); goto aeqs_init_err; } set_bit(HINIC_HWDEV_AEQ_INITED, &hwdev->func_state); return 0; aeqs_init_err: for (i = 0; i < num_aeqs; i++) hinic_free_irq(hwdev, SERVICE_T_INTF, aeq_irqs[i].irq_id); return err; } static void hinic_comm_aeqs_free(struct hinic_hwdev *hwdev) { struct irq_info aeq_irqs[HINIC_MAX_AEQS] = {{0} }; u16 num_irqs, i; clear_bit(HINIC_HWDEV_AEQ_INITED, &hwdev->func_state); hinic_get_aeq_irqs(hwdev, aeq_irqs, &num_irqs); hinic_aeqs_free(hwdev); for (i = 0; i < num_irqs; i++) hinic_free_irq(hwdev, SERVICE_T_INTF, aeq_irqs[i].irq_id); } static int hinic_comm_ceqs_init(struct hinic_hwdev *hwdev) { struct irq_info ceq_irqs[HINIC_MAX_CEQS] = {{0} }; u16 num_ceqs, resp_num_irq = 0, i; int err; num_ceqs = HINIC_HWIF_NUM_CEQS(hwdev->hwif); if (num_ceqs > HINIC_MAX_CEQS) { sdk_warn(hwdev->dev_hdl, "Adjust ceq num to %d\n", HINIC_MAX_CEQS); num_ceqs = HINIC_MAX_CEQS; } err = hinic_alloc_irqs(hwdev, SERVICE_T_INTF, num_ceqs, ceq_irqs, &resp_num_irq); if (err) { sdk_err(hwdev->dev_hdl, "Failed to alloc ceq irqs, num_ceqs: %d\n", num_ceqs); return err; } if (resp_num_irq < num_ceqs) { sdk_warn(hwdev->dev_hdl, "Adjust ceq num to %d\n", resp_num_irq); num_ceqs = resp_num_irq; } err = hinic_ceqs_init(hwdev, num_ceqs, ceq_irqs); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init ceqs, err:%d\n", err); goto ceqs_init_err; } return 0; ceqs_init_err: for (i = 0; i < num_ceqs; i++) hinic_free_irq(hwdev, SERVICE_T_INTF, ceq_irqs[i].irq_id); return err; } static void hinic_comm_ceqs_free(struct hinic_hwdev *hwdev) { struct irq_info ceq_irqs[HINIC_MAX_CEQS] = {{0} }; u16 num_irqs; int i; hinic_get_ceq_irqs(hwdev, ceq_irqs, &num_irqs); hinic_ceqs_free(hwdev); for (i = 0; i < num_irqs; i++) hinic_free_irq(hwdev, SERVICE_T_INTF, ceq_irqs[i].irq_id); } static int hinic_comm_func_to_func_init(struct hinic_hwdev *hwdev) { int err; err = hinic_func_to_func_init(hwdev); if (err) return err; hinic_aeq_register_hw_cb(hwdev, HINIC_MBX_FROM_FUNC, hinic_mbox_func_aeqe_handler); hinic_aeq_register_hw_cb(hwdev, HINIC_MBX_SEND_RSLT, hinic_mbox_self_aeqe_handler); if (!HINIC_IS_VF(hwdev)) { hinic_register_pf_mbox_cb(hwdev, HINIC_MOD_COMM, comm_pf_mbox_handler); hinic_register_pf_mbox_cb(hwdev, HINIC_MOD_SW_FUNC, sw_func_pf_mbox_handler); } else { hinic_register_pf_mbox_cb(hwdev, HINIC_MOD_COMM, vf_to_pf_handler); } set_bit(HINIC_HWDEV_MBOX_INITED, &hwdev->func_state); return 0; } static void hinic_comm_func_to_func_free(struct hinic_hwdev *hwdev) { hinic_aeq_unregister_hw_cb(hwdev, HINIC_MBX_FROM_FUNC); hinic_aeq_unregister_hw_cb(hwdev, HINIC_MBX_SEND_RSLT); hinic_unregister_pf_mbox_cb(hwdev, HINIC_MOD_COMM); hinic_unregister_pf_mbox_cb(hwdev, HINIC_MOD_SW_FUNC); hinic_func_to_func_free(hwdev); } static int hinic_comm_pf_to_mgmt_init(struct hinic_hwdev *hwdev) { int err; if (hinic_func_type(hwdev) == TYPE_VF || !FUNC_SUPPORT_MGMT(hwdev)) return 0; /* VF do not support send msg to mgmt directly */ err = hinic_pf_to_mgmt_init(hwdev); if (err) return err; hinic_aeq_register_hw_cb(hwdev, HINIC_MSG_FROM_MGMT_CPU, hinic_mgmt_msg_aeqe_handler); hinic_register_mgmt_msg_cb(hwdev, HINIC_MOD_COMM, hwdev->pf_to_mgmt, comm_mgmt_msg_handler); set_bit(HINIC_HWDEV_MGMT_INITED, &hwdev->func_state); return 0; } static void hinic_comm_pf_to_mgmt_free(struct hinic_hwdev *hwdev) { if (hinic_func_type(hwdev) == TYPE_VF || !FUNC_SUPPORT_MGMT(hwdev)) return; /* VF do not support send msg to mgmt directly */ hinic_unregister_mgmt_msg_cb(hwdev, HINIC_MOD_COMM); hinic_aeq_unregister_hw_cb(hwdev, HINIC_MSG_FROM_MGMT_CPU); hinic_pf_to_mgmt_free(hwdev); } static int hinic_comm_clp_to_mgmt_init(struct hinic_hwdev *hwdev) { int err; if (hinic_func_type(hwdev) == TYPE_VF || !FUNC_SUPPORT_MGMT(hwdev)) return 0; err = hinic_clp_pf_to_mgmt_init(hwdev); if (err) return err; set_bit(HINIC_HWDEV_CLP_INITED, &hwdev->func_state); return 0; } static void hinic_comm_clp_to_mgmt_free(struct hinic_hwdev *hwdev) { if (hinic_func_type(hwdev) == TYPE_VF || !FUNC_SUPPORT_MGMT(hwdev)) return; clear_bit(HINIC_HWDEV_CLP_INITED, &hwdev->func_state); hinic_clp_pf_to_mgmt_free(hwdev); } static int hinic_comm_cmdqs_init(struct hinic_hwdev *hwdev) { int err; err = hinic_cmdqs_init(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init cmd queues\n"); return err; } hinic_ceq_register_cb(hwdev, HINIC_CMDQ, hinic_cmdq_ceq_handler); err = hinic_set_cmdq_depth(hwdev, HINIC_CMDQ_DEPTH); if (err) { sdk_err(hwdev->dev_hdl, "Failed to set cmdq depth\n"); goto set_cmdq_depth_err; } return 0; set_cmdq_depth_err: hinic_cmdqs_free(hwdev); return err; } static void hinic_comm_cmdqs_free(struct hinic_hwdev *hwdev) { hinic_ceq_unregister_cb(hwdev, HINIC_CMDQ); hinic_cmdqs_free(hwdev); } static int hinic_sync_mgmt_func_state(struct hinic_hwdev *hwdev) { int err; hinic_set_pf_status(hwdev->hwif, HINIC_PF_STATUS_ACTIVE_FLAG); err = resources_state_set(hwdev, HINIC_RES_ACTIVE); if (err) { sdk_err(hwdev->dev_hdl, "Failed to set function resources state\n"); goto resources_state_set_err; } return 0; resources_state_set_err: hinic_set_pf_status(hwdev->hwif, HINIC_PF_STATUS_INIT); return err; } static void hinic_unsync_mgmt_func_state(struct hinic_hwdev *hwdev) { hinic_set_pf_status(hwdev->hwif, HINIC_PF_STATUS_INIT); resources_state_set(hwdev, HINIC_RES_CLEAN); } int hinic_l2nic_reset_base(struct hinic_hwdev *hwdev, u16 reset_flag) { struct hinic_l2nic_reset l2nic_reset = {0}; u16 out_size = sizeof(l2nic_reset); int err = 0; err = hinic_set_vport_enable(hwdev, false); if (err) return err; msleep(100); sdk_info(hwdev->dev_hdl, "L2nic reset flag 0x%x\n", reset_flag); err = hinic_global_func_id_get(hwdev, &l2nic_reset.func_id); if (err) return err; l2nic_reset.reset_flag = reset_flag; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_L2NIC_RESET, &l2nic_reset, sizeof(l2nic_reset), &l2nic_reset, &out_size, 0); if (err || !out_size || l2nic_reset.status) { sdk_err(hwdev->dev_hdl, "Failed to reset L2NIC resources, err: %d, status: 0x%x, out_size: 0x%x\n", err, l2nic_reset.status, out_size); return -EIO; } return 0; } EXPORT_SYMBOL(hinic_l2nic_reset_base); static int hinic_l2nic_reset(struct hinic_hwdev *hwdev) { return hinic_l2nic_reset_base(hwdev, 0); } static int __get_func_misc_info(struct hinic_hwdev *hwdev) { int err; err = hinic_get_board_info(hwdev, &hwdev->board_info); if (err) { /* For the pf/vf of slave host, return error */ if (hinic_pcie_itf_id(hwdev)) return err; /* VF can't get board info in early version */ if (!HINIC_IS_VF(hwdev)) { sdk_err(hwdev->dev_hdl, "Get board info failed\n"); return err; } memset(&hwdev->board_info, 0xff, sizeof(struct hinic_board_info)); } err = hinic_get_mgmt_version(hwdev, hwdev->mgmt_ver); if (err) { sdk_err(hwdev->dev_hdl, "Get mgmt cpu version failed\n"); return err; } return 0; } /* initialize communication channel */ int hinic_init_comm_ch(struct hinic_hwdev *hwdev) { int err; u16 func_id; if (IS_BMGW_SLAVE_HOST(hwdev) && (!hinic_get_master_host_mbox_enable(hwdev))) { sdk_err(hwdev->dev_hdl, "Master host not initialized\n"); return -EFAULT; } err = hinic_comm_clp_to_mgmt_init(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init clp\n"); return err; } err = hinic_comm_aeqs_init(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init async event queues\n"); goto aeqs_init_err; } err = hinic_comm_pf_to_mgmt_init(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init msg\n"); goto msg_init_err; } err = hinic_comm_func_to_func_init(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init mailbox\n"); goto func_to_func_init_err; } err = init_aeqs_msix_attr(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init aeqs msix attr\n"); goto aeqs_msix_attr_init_err; } err = __get_func_misc_info(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to get function misc information\n"); goto get_func_info_err; } /* detect master host chip mode according board type and host id */ err = rectify_host_mode(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to rectify host mode\n"); goto rectify_mode_err; } err = hinic_l2nic_reset(hwdev); if (err) goto l2nic_reset_err; if (IS_MULTI_HOST(hwdev)) { err = hinic_multi_host_mgmt_init(hwdev); if (err) goto multi_host_mgmt_init_err; } dma_attr_table_init(hwdev); err = hinic_comm_ceqs_init(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init completion event queues\n"); goto ceqs_init_err; } err = init_ceqs_msix_attr(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init ceqs msix attr\n"); goto init_eqs_msix_err; } /* set default wq page_size */ hwdev->wq_page_size = HINIC_DEFAULT_WQ_PAGE_SIZE; err = hinic_global_func_id_get(hwdev, &func_id); if (err) goto get_func_id_err; err = hinic_set_wq_page_size(hwdev, func_id, hwdev->wq_page_size); if (err) { sdk_err(hwdev->dev_hdl, "Failed to set wq page size\n"); goto init_wq_pg_size_err; } err = hinic_comm_cmdqs_init(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to init cmd queues\n"); goto cmdq_init_err; } set_bit(HINIC_HWDEV_CMDQ_INITED, &hwdev->func_state); err = hinic_sync_mgmt_func_state(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Failed to synchronize mgmt function state\n"); goto sync_mgmt_func_err; } err = hinic_aeq_register_swe_cb(hwdev, HINIC_STATELESS_EVENT, hinic_nic_sw_aeqe_handler); if (err) { sdk_err(hwdev->dev_hdl, "Failed to register ucode aeqe handler\n"); goto register_ucode_aeqe_err; } set_bit(HINIC_HWDEV_COMM_CH_INITED, &hwdev->func_state); return 0; register_ucode_aeqe_err: hinic_unsync_mgmt_func_state(hwdev); sync_mgmt_func_err: return err; cmdq_init_err: if (HINIC_FUNC_TYPE(hwdev) != TYPE_VF) hinic_set_wq_page_size(hwdev, func_id, HINIC_HW_WQ_PAGE_SIZE); init_wq_pg_size_err: get_func_id_err: init_eqs_msix_err: hinic_comm_ceqs_free(hwdev); ceqs_init_err: if (IS_MULTI_HOST(hwdev)) hinic_multi_host_mgmt_free(hwdev); multi_host_mgmt_init_err: l2nic_reset_err: rectify_mode_err: get_func_info_err: aeqs_msix_attr_init_err: func_to_func_init_err: return err; msg_init_err: hinic_comm_aeqs_free(hwdev); aeqs_init_err: hinic_comm_clp_to_mgmt_free(hwdev); return err; } static void __uninit_comm_module(struct hinic_hwdev *hwdev, enum hinic_hwdev_init_state init_state) { u16 func_id; switch (init_state) { case HINIC_HWDEV_COMM_CH_INITED: hinic_aeq_unregister_swe_cb(hwdev, HINIC_STATELESS_EVENT); hinic_unsync_mgmt_func_state(hwdev); break; case HINIC_HWDEV_CMDQ_INITED: hinic_comm_cmdqs_free(hwdev); /* VF can set page size of 256K only, any other value * will return error in pf, pf will set all vf's page * size to 4K when disable sriov */ if (HINIC_FUNC_TYPE(hwdev) != TYPE_VF) { func_id = hinic_global_func_id_hw(hwdev); hinic_set_wq_page_size(hwdev, func_id, HINIC_HW_WQ_PAGE_SIZE); } hinic_comm_ceqs_free(hwdev); if (IS_MULTI_HOST(hwdev)) hinic_multi_host_mgmt_free(hwdev); break; case HINIC_HWDEV_MBOX_INITED: hinic_comm_func_to_func_free(hwdev); break; case HINIC_HWDEV_MGMT_INITED: hinic_comm_pf_to_mgmt_free(hwdev); break; case HINIC_HWDEV_AEQ_INITED: hinic_comm_aeqs_free(hwdev); break; case HINIC_HWDEV_CLP_INITED: hinic_comm_clp_to_mgmt_free(hwdev); break; default: break; } } #define HINIC_FUNC_STATE_BUSY_TIMEOUT 300 void hinic_uninit_comm_ch(struct hinic_hwdev *hwdev) { enum hinic_hwdev_init_state init_state = HINIC_HWDEV_COMM_CH_INITED; int cnt; while (init_state > HINIC_HWDEV_NONE_INITED) { if (!test_bit(init_state, &hwdev->func_state)) { init_state--; continue; } clear_bit(init_state, &hwdev->func_state); cnt = 0; while (test_bit(HINIC_HWDEV_STATE_BUSY, &hwdev->func_state) && cnt++ <= HINIC_FUNC_STATE_BUSY_TIMEOUT) usleep_range(900, 1000); __uninit_comm_module(hwdev, init_state); init_state--; } } int hinic_slq_init(void *dev, int num_wqs) { struct hinic_hwdev *hwdev = dev; int err; if (!dev) return -EINVAL; hwdev->wqs = kzalloc(sizeof(*hwdev->wqs), GFP_KERNEL); if (!hwdev->wqs) return -ENOMEM; err = hinic_wqs_alloc(hwdev->wqs, num_wqs, hwdev->dev_hdl); if (err) { sdk_err(hwdev->dev_hdl, "Failed to alloc wqs\n"); kfree(hwdev->wqs); hwdev->wqs = NULL; } return err; } EXPORT_SYMBOL(hinic_slq_init); void hinic_slq_uninit(void *dev) { struct hinic_hwdev *hwdev = dev; if (!hwdev) return; hinic_wqs_free(hwdev->wqs); kfree(hwdev->wqs); } EXPORT_SYMBOL(hinic_slq_uninit); int hinic_slq_alloc(void *dev, u16 wqebb_size, u16 q_depth, u16 page_size, u64 *cla_addr, void **handle) { struct hinic_hwdev *hwdev = dev; struct hinic_wq *wq; int err; if (!dev || !cla_addr || !handle) return -EINVAL; wq = kzalloc(sizeof(*wq), GFP_KERNEL); if (!wq) return -ENOMEM; err = hinic_wq_allocate(hwdev->wqs, wq, wqebb_size, hwdev->wq_page_size, q_depth, 0); if (err) { sdk_err(hwdev->dev_hdl, "Failed to alloc wq\n"); kfree(wq); return -EFAULT; } *cla_addr = wq->block_paddr; *handle = wq; return 0; } EXPORT_SYMBOL(hinic_slq_alloc); void hinic_slq_free(void *dev, void *handle) { struct hinic_hwdev *hwdev = dev; if (!hwdev || !handle) return; hinic_wq_free(hwdev->wqs, handle); kfree(handle); } EXPORT_SYMBOL(hinic_slq_free); u64 hinic_slq_get_addr(void *handle, u16 index) { if (!handle) return 0; /* NULL of wqe addr */ return (u64)hinic_get_wqebb_addr(handle, index); } EXPORT_SYMBOL(hinic_slq_get_addr); u64 hinic_slq_get_first_pageaddr(void *handle) { struct hinic_wq *wq = handle; if (!handle) return 0; /* NULL of wqe addr */ return hinic_get_first_wqe_page_addr(wq); } EXPORT_SYMBOL(hinic_slq_get_first_pageaddr); int hinic_func_tmr_bitmap_set(void *hwdev, bool en) { struct hinic_func_tmr_bitmap_op bitmap_op = {0}; u16 out_size = sizeof(bitmap_op); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &bitmap_op.func_idx); if (err) return err; bitmap_op.ppf_idx = hinic_ppf_idx(hwdev); if (en) bitmap_op.op_id = FUNC_TMR_BITMAP_ENABLE; else bitmap_op.op_id = FUNC_TMR_BITMAP_DISABLE; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_FUNC_TMR_BITMAT_SET, &bitmap_op, sizeof(bitmap_op), &bitmap_op, &out_size, 0); if (err || !out_size || bitmap_op.status) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to set timer bitmap, err: %d, status: 0x%x, out_size: 0x%x\n", err, bitmap_op.status, out_size); return -EFAULT; } return 0; } EXPORT_SYMBOL(hinic_func_tmr_bitmap_set); static int ppf_ht_gpa_set(struct hinic_hwdev *hwdev, struct hinic_page_addr *pg0, struct hinic_page_addr *pg1) { struct comm_info_ht_gpa_set ht_gpa_set = {0}; u16 out_size = sizeof(ht_gpa_set); int ret; pg0->virt_addr = dma_alloc_coherent(hwdev->dev_hdl, HINIC_HT_GPA_PAGE_SIZE, &pg0->phys_addr, GFP_KERNEL); if (!pg0->virt_addr) { sdk_err(hwdev->dev_hdl, "Alloc pg0 page addr failed\n"); return -EFAULT; } pg1->virt_addr = dma_alloc_coherent(hwdev->dev_hdl, HINIC_HT_GPA_PAGE_SIZE, &pg1->phys_addr, GFP_KERNEL); if (!pg1->virt_addr) { sdk_err(hwdev->dev_hdl, "Alloc pg1 page addr failed\n"); return -EFAULT; } ht_gpa_set.page_pa0 = pg0->phys_addr; ht_gpa_set.page_pa1 = pg1->phys_addr; sdk_info(hwdev->dev_hdl, "PPF ht gpa set: page_addr0.pa=0x%llx, page_addr1.pa=0x%llx\n", pg0->phys_addr, pg1->phys_addr); ret = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_PPF_HT_GPA_SET, &ht_gpa_set, sizeof(ht_gpa_set), &ht_gpa_set, &out_size, 0); if (ret || !out_size || ht_gpa_set.status) { sdk_warn(hwdev->dev_hdl, "PPF ht gpa set failed, ret: %d, status: 0x%x, out_size: 0x%x\n", ret, ht_gpa_set.status, out_size); return -EFAULT; } hwdev->page_pa0.phys_addr = pg0->phys_addr; hwdev->page_pa0.virt_addr = pg0->virt_addr; hwdev->page_pa1.phys_addr = pg1->phys_addr; hwdev->page_pa1.virt_addr = pg1->virt_addr; return 0; } int hinic_ppf_ht_gpa_init(struct hinic_hwdev *hwdev) { int ret; int i; int j; int size; struct hinic_page_addr page_addr0[HINIC_PPF_HT_GPA_SET_RETRY_TIMES]; struct hinic_page_addr page_addr1[HINIC_PPF_HT_GPA_SET_RETRY_TIMES]; size = HINIC_PPF_HT_GPA_SET_RETRY_TIMES * sizeof(page_addr0[0]); memset(page_addr0, 0, size); memset(page_addr1, 0, size); for (i = 0; i < HINIC_PPF_HT_GPA_SET_RETRY_TIMES; i++) { ret = ppf_ht_gpa_set(hwdev, &page_addr0[i], &page_addr1[i]); if (!ret) break; } for (j = 0; j < i; j++) { if (page_addr0[j].virt_addr) { dma_free_coherent(hwdev->dev_hdl, HINIC_HT_GPA_PAGE_SIZE, page_addr0[j].virt_addr, page_addr0[j].phys_addr); page_addr0[j].virt_addr = NULL; } if (page_addr1[j].virt_addr) { dma_free_coherent(hwdev->dev_hdl, HINIC_HT_GPA_PAGE_SIZE, page_addr1[j].virt_addr, page_addr1[j].phys_addr); page_addr1[j].virt_addr = NULL; } } if (i >= HINIC_PPF_HT_GPA_SET_RETRY_TIMES) { sdk_err(hwdev->dev_hdl, "PPF ht gpa init failed, retry times: %d\n", i); return -EFAULT; } return 0; } void hinic_ppf_ht_gpa_deinit(struct hinic_hwdev *hwdev) { if (hwdev->page_pa0.virt_addr) { dma_free_coherent(hwdev->dev_hdl, HINIC_HT_GPA_PAGE_SIZE, hwdev->page_pa0.virt_addr, hwdev->page_pa0.phys_addr); hwdev->page_pa0.virt_addr = NULL; } if (hwdev->page_pa1.virt_addr) { dma_free_coherent(hwdev->dev_hdl, HINIC_HT_GPA_PAGE_SIZE, hwdev->page_pa1.virt_addr, hwdev->page_pa1.phys_addr); hwdev->page_pa1.virt_addr = NULL; } } static int set_ppf_tmr_status(struct hinic_hwdev *hwdev, enum ppf_tmr_status status) { struct hinic_ppf_tmr_op op = {0}; u16 out_size = sizeof(op); int err = 0; if (!hwdev) return -EINVAL; if (hinic_func_type(hwdev) != TYPE_PPF) return -EFAULT; if (status == HINIC_PPF_TMR_FLAG_START) { err = hinic_ppf_ht_gpa_init(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "PPF ht gpa init fail!\n"); return -EFAULT; } } else { hinic_ppf_ht_gpa_deinit(hwdev); } op.op_id = status; op.ppf_idx = hinic_ppf_idx(hwdev); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_PPF_TMR_SET, &op, sizeof(op), &op, &out_size, 0); if (err || !out_size || op.status) { sdk_err(hwdev->dev_hdl, "Failed to set ppf timer, err: %d, status: 0x%x, out_size: 0x%x\n", err, op.status, out_size); return -EFAULT; } return 0; } int hinic_ppf_tmr_start(void *hwdev) { if (!hwdev) { pr_err("Hwdev pointer is NULL for starting ppf timer\n"); return -EINVAL; } return set_ppf_tmr_status(hwdev, HINIC_PPF_TMR_FLAG_START); } EXPORT_SYMBOL(hinic_ppf_tmr_start); int hinic_ppf_tmr_stop(void *hwdev) { if (!hwdev) { pr_err("Hwdev pointer is NULL for stop ppf timer\n"); return -EINVAL; } return set_ppf_tmr_status(hwdev, HINIC_PPF_TMR_FLAG_STOP); } EXPORT_SYMBOL(hinic_ppf_tmr_stop); int mqm_eqm_try_alloc_mem(struct hinic_hwdev *hwdev, u32 page_size, u32 page_num) { struct hinic_page_addr *page_addr = hwdev->mqm_att.brm_srch_page_addr; u32 valid_num = 0; u32 flag = 1; u32 i = 0; for (i = 0; i < page_num; i++) { page_addr->virt_addr = dma_alloc_coherent(hwdev->dev_hdl, page_size, &page_addr->phys_addr, GFP_KERNEL); if (!page_addr->virt_addr) { flag = 0; break; } valid_num++; page_addr++; } if (flag == 1) { hwdev->mqm_att.page_size = page_size; hwdev->mqm_att.page_num = page_num; } else { page_addr = hwdev->mqm_att.brm_srch_page_addr; for (i = 0; i < valid_num; i++) { dma_free_coherent(hwdev->dev_hdl, page_size, page_addr->virt_addr, page_addr->phys_addr); page_addr++; } return -EFAULT; } return 0; } int mqm_eqm_alloc_page_mem(struct hinic_hwdev *hwdev) { int ret = 0; /* apply for 64KB page, number is chunk_num/16 */ ret = mqm_eqm_try_alloc_mem(hwdev, 64 * 1024, hwdev->mqm_att.chunk_num >> 4); if (!ret) return 0; /* apply for 8KB page, number is chunk_num/2 */ ret = mqm_eqm_try_alloc_mem(hwdev, 8 * 1024, hwdev->mqm_att.chunk_num >> 1); if (!ret) return 0; /* apply for 4KB page, number is chunk_num */ ret = mqm_eqm_try_alloc_mem(hwdev, 4 * 1024, hwdev->mqm_att.chunk_num); if (!ret) return 0; return ret; } void mqm_eqm_free_page_mem(struct hinic_hwdev *hwdev) { u32 i; struct hinic_page_addr *page_addr; u32 page_size; page_size = hwdev->mqm_att.page_size; page_addr = hwdev->mqm_att.brm_srch_page_addr; for (i = 0; i < hwdev->mqm_att.page_num; i++) { dma_free_coherent(hwdev->dev_hdl, page_size, page_addr->virt_addr, page_addr->phys_addr); page_addr++; } } int mqm_eqm_set_cfg_2_hw(struct hinic_hwdev *hwdev, u32 valid) { struct comm_info_eqm_cfg info_eqm_cfg = {0}; u16 out_size = sizeof(info_eqm_cfg); int err; info_eqm_cfg.ppf_id = hinic_global_func_id_hw(hwdev); info_eqm_cfg.page_size = hwdev->mqm_att.page_size; info_eqm_cfg.valid = valid; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_MQM_CFG_INFO_SET, &info_eqm_cfg, sizeof(info_eqm_cfg), &info_eqm_cfg, &out_size, 0); if (err || !out_size || info_eqm_cfg.status) { sdk_err(hwdev->dev_hdl, "Failed to init func table, err: %d, status: 0x%x, out_size: 0x%x\n", err, info_eqm_cfg.status, out_size); return -EFAULT; } return 0; } #define EQM_DATA_BUF_SIZE 1024 int mqm_eqm_set_page_2_hw(struct hinic_hwdev *hwdev) { struct comm_info_eqm_search_gpa *info; struct hinic_page_addr *page_addr; void *send_buf; u16 send_buf_size; u32 i; u64 *gpa_hi52; u64 gpa; u32 num; u32 start_idx; int err = 0; u32 valid_page_num; u16 out_size; send_buf_size = sizeof(struct comm_info_eqm_search_gpa) + EQM_DATA_BUF_SIZE; send_buf = kzalloc(send_buf_size, GFP_KERNEL); if (!send_buf) { sdk_err(hwdev->dev_hdl, "Alloc virtual mem failed\r\n"); return -EFAULT; } page_addr = hwdev->mqm_att.brm_srch_page_addr; info = (struct comm_info_eqm_search_gpa *)send_buf; valid_page_num = 0; gpa_hi52 = info->gpa_hi52; num = 0; start_idx = 0; for (i = 0; i < hwdev->mqm_att.page_num; i++) { gpa = page_addr->phys_addr >> 12; gpa_hi52[num] = gpa; num++; if (num == 128) { info->num = num; info->start_idx = start_idx; out_size = send_buf_size; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_MQM_SRCH_GPA_SET, info, (u16)send_buf_size, info, &out_size, 0); if (err || !out_size || info->status) { sdk_err(hwdev->dev_hdl, "Set mqm srch gpa fail, err: %d, status: 0x%x, out_size: 0x%x\n", err, info->status, out_size); err = -EFAULT; goto set_page_2_hw_end; } gpa_hi52 = info->gpa_hi52; num = 0; start_idx = i + 1; } page_addr++; valid_page_num++; } if (0 != (valid_page_num & 0x7f)) { info->num = num; info->start_idx = start_idx; out_size = send_buf_size; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_MQM_SRCH_GPA_SET, info, (u16)send_buf_size, info, &out_size, 0); if (err || !out_size || info->status) { sdk_err(hwdev->dev_hdl, "Set mqm srch gpa fail, err: %d, status: 0x%x, out_size: 0x%x\n", err, info->status, out_size); err = -EFAULT; goto set_page_2_hw_end; } } set_page_2_hw_end: kfree(send_buf); return err; } int mqm_eqm_init(struct hinic_hwdev *hwdev) { struct comm_info_eqm_fix info_eqm_fix = {0}; u16 len = sizeof(info_eqm_fix); int ret; if (hwdev->hwif->attr.func_type != TYPE_PPF) return 0; ret = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_MQM_FIX_INFO_GET, &info_eqm_fix, sizeof(info_eqm_fix), &info_eqm_fix, &len, 0); if (ret || !len || info_eqm_fix.status) { sdk_err(hwdev->dev_hdl, "Get mqm fix info failed, err: %d, status: 0x%x, out_size: 0x%x\n", ret, info_eqm_fix.status, len); return -EFAULT; } if (!(info_eqm_fix.chunk_num)) return 0; hwdev->mqm_att.chunk_num = info_eqm_fix.chunk_num; hwdev->mqm_att.search_gpa_num = info_eqm_fix.search_gpa_num; hwdev->mqm_att.page_size = 0; hwdev->mqm_att.page_num = 0; hwdev->mqm_att.brm_srch_page_addr = kcalloc(hwdev->mqm_att.chunk_num, sizeof(struct hinic_page_addr), GFP_KERNEL); if (!(hwdev->mqm_att.brm_srch_page_addr)) { sdk_err(hwdev->dev_hdl, "Alloc virtual mem failed\n"); return -EFAULT; } ret = mqm_eqm_alloc_page_mem(hwdev); if (ret) { sdk_err(hwdev->dev_hdl, "Alloc eqm page mem failed\n"); goto err_page; } ret = mqm_eqm_set_page_2_hw(hwdev); if (ret) { sdk_err(hwdev->dev_hdl, "Set page to hw failed\n"); goto err_ecmd; } ret = mqm_eqm_set_cfg_2_hw(hwdev, 1); if (ret) { sdk_err(hwdev->dev_hdl, "Set page to hw failed\n"); goto err_ecmd; } return 0; err_ecmd: mqm_eqm_free_page_mem(hwdev); err_page: kfree(hwdev->mqm_att.brm_srch_page_addr); return ret; } void mqm_eqm_deinit(struct hinic_hwdev *hwdev) { int ret; if (hwdev->hwif->attr.func_type != TYPE_PPF) return; if (!(hwdev->mqm_att.chunk_num)) return; mqm_eqm_free_page_mem(hwdev); kfree(hwdev->mqm_att.brm_srch_page_addr); ret = mqm_eqm_set_cfg_2_hw(hwdev, 0); if (ret) { sdk_err(hwdev->dev_hdl, "Set mqm eqm cfg to chip fail, err: %d\n", ret); return; } hwdev->mqm_att.chunk_num = 0; hwdev->mqm_att.search_gpa_num = 0; hwdev->mqm_att.page_num = 0; hwdev->mqm_att.page_size = 0; } int hinic_ppf_ext_db_init(void *dev) { struct hinic_hwdev *hwdev = dev; int ret; if (!dev) return -EINVAL; ret = mqm_eqm_init(hwdev); if (ret) { sdk_err(hwdev->dev_hdl, "MQM eqm init failed\n"); return -EFAULT; } return 0; } EXPORT_SYMBOL(hinic_ppf_ext_db_init); int hinic_ppf_ext_db_deinit(void *dev) { struct hinic_hwdev *hwdev = dev; if (!dev) return -EINVAL; if (hwdev->hwif->attr.func_type != TYPE_PPF) return -EFAULT; mqm_eqm_deinit(hwdev); return 0; } EXPORT_SYMBOL(hinic_ppf_ext_db_deinit); int hinic_set_wq_page_size(struct hinic_hwdev *hwdev, u16 func_idx, u32 page_size) { struct hinic_wq_page_size page_size_info = {0}; u16 out_size = sizeof(page_size_info); int err; page_size_info.func_idx = func_idx; page_size_info.ppf_idx = hinic_ppf_idx(hwdev); page_size_info.page_size = HINIC_PAGE_SIZE_HW(page_size); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_PAGESIZE_SET, &page_size_info, sizeof(page_size_info), &page_size_info, &out_size, 0); if (err || !out_size || page_size_info.status) { sdk_err(hwdev->dev_hdl, "Failed to set wq page size, err: %d, status: 0x%x, out_size: 0x%0x\n", err, page_size_info.status, out_size); return -EFAULT; } return 0; } enum hinic_event_cmd { /* hilink event */ HINIC_EVENT_LINK_STATUS_CHANGE = 1, HINIC_EVENT_LINK_ERR, HINIC_EVENT_CABLE_PLUG, HINIC_EVENT_HILINK_INFO, /* reserved for hilink */ /* driver event, pf & vf communicate */ HINIC_EVENT_HEARTBEAT_LOST = 31, HINIC_EVENT_SET_VF_COS, /* mgmt event */ HINIC_EVENT_MGMT_FAULT = 61, HINIC_EVENT_MGMT_WATCHDOG, HINIC_EVENT_MGMT_FMW_ACT_NTC, HINIC_EVENT_MGMT_RESET, HINIC_EVENT_MGMT_PCIE_DFX, HINIC_EVENT_MCTP_HOST_INFO, HINIC_EVENT_SFP_INFO_REPORT, HINIC_EVENT_SFP_ABS_REPORT, HINIC_EVENT_MAX_TYPE, }; struct hinic_event_convert { u8 mod; u8 cmd; enum hinic_event_cmd event; }; static struct hinic_event_convert __event_convert[] = { /* hilink event */ { .mod = HINIC_MOD_L2NIC, .cmd = HINIC_PORT_CMD_LINK_STATUS_REPORT, .event = HINIC_EVENT_LINK_STATUS_CHANGE, }, { .mod = HINIC_MOD_L2NIC, .cmd = HINIC_PORT_CMD_LINK_ERR_EVENT, .event = HINIC_EVENT_LINK_ERR, }, { .mod = HINIC_MOD_L2NIC, .cmd = HINIC_PORT_CMD_CABLE_PLUG_EVENT, .event = HINIC_EVENT_CABLE_PLUG, }, { .mod = HINIC_MOD_HILINK, .cmd = HINIC_HILINK_CMD_GET_LINK_INFO, .event = HINIC_EVENT_HILINK_INFO, }, /* driver triggered event */ { .mod = HINIC_MOD_L2NIC, .cmd = HINIC_MGMT_CMD_HEART_LOST_REPORT, .event = HINIC_EVENT_HEARTBEAT_LOST, }, { .mod = HINIC_MOD_L2NIC, .cmd = HINIC_PORT_CMD_SET_VF_COS, .event = HINIC_EVENT_SET_VF_COS, }, /* mgmt event */ { .mod = HINIC_MOD_COMM, .cmd = HINIC_MGMT_CMD_FAULT_REPORT, .event = HINIC_EVENT_MGMT_FAULT, }, { .mod = HINIC_MOD_COMM, .cmd = HINIC_MGMT_CMD_WATCHDOG_INFO, .event = HINIC_EVENT_MGMT_WATCHDOG, }, { .mod = HINIC_MOD_COMM, .cmd = HINIC_MGMT_CMD_FMW_ACT_NTC, .event = HINIC_EVENT_MGMT_FMW_ACT_NTC, }, { .mod = HINIC_MOD_L2NIC, .cmd = HINIC_PORT_CMD_MGMT_RESET, .event = HINIC_EVENT_MGMT_RESET, }, { .mod = HINIC_MOD_COMM, .cmd = HINIC_MGMT_CMD_PCIE_DFX_NTC, .event = HINIC_EVENT_MGMT_PCIE_DFX, }, { .mod = HINIC_MOD_COMM, .cmd = HINIC_MGMT_CMD_GET_HOST_INFO, .event = HINIC_EVENT_MCTP_HOST_INFO, }, { .mod = HINIC_MOD_L2NIC, .cmd = HINIC_PORT_CMD_GET_SFP_INFO, .event = HINIC_EVENT_SFP_INFO_REPORT, }, { .mod = HINIC_MOD_L2NIC, .cmd = HINIC_PORT_CMD_GET_SFP_ABS, .event = HINIC_EVENT_SFP_ABS_REPORT, }, }; static enum hinic_event_cmd __get_event_type(u8 mod, u8 cmd) { int idx; int arr_size = ARRAY_SIZE(__event_convert); for (idx = 0; idx < arr_size; idx++) { if (__event_convert[idx].mod == mod && __event_convert[idx].cmd == cmd) return __event_convert[idx].event; } return HINIC_EVENT_MAX_TYPE; } bool hinic_mgmt_event_ack_first(u8 mod, u8 cmd) { if ((mod == HINIC_MOD_COMM && cmd == HINIC_MGMT_CMD_GET_HOST_INFO) || (mod == HINIC_MOD_COMM && cmd == HINIC_MGMT_CMD_HEARTBEAT_EVENT)) return false; if (mod == HINIC_MOD_COMM || mod == HINIC_MOD_L2NIC || mod == HINIC_MOD_HILINK) return true; return false; } #define FAULT_SHOW_STR_LEN 16 static void fault_report_show(struct hinic_hwdev *hwdev, struct hinic_fault_event *event) { char fault_type[FAULT_TYPE_MAX][FAULT_SHOW_STR_LEN + 1] = { "chip", "ucode", "mem rd timeout", "mem wr timeout", "reg rd timeout", "reg wr timeout", "phy fault"}; char fault_level[FAULT_LEVEL_MAX][FAULT_SHOW_STR_LEN + 1] = { "fatal", "reset", "flr", "general", "suggestion"}; char type_str[FAULT_SHOW_STR_LEN + 1]; char level_str[FAULT_SHOW_STR_LEN + 1]; u8 level; u32 pos, base; struct hinic_fault_event_stats *fault; u8 node_id; sdk_err(hwdev->dev_hdl, "Fault event report received, func_id: %d\n", hinic_global_func_id(hwdev)); memset(type_str, 0, FAULT_SHOW_STR_LEN + 1); if (event->type < FAULT_TYPE_MAX) strscpy(type_str, fault_type[event->type], FAULT_SHOW_STR_LEN); else strscpy(type_str, "Unknown", FAULT_SHOW_STR_LEN); sdk_err(hwdev->dev_hdl, "Fault type: %d [%s]\n", event->type, type_str); sdk_err(hwdev->dev_hdl, "Fault val[0]: 0x%08x, val[1]: 0x%08x, val[2]: 0x%08x, val[3]: 0x%08x\n", event->event.val[0], event->event.val[1], event->event.val[2], event->event.val[3]); fault = &hwdev->hw_stats.fault_event_stats; switch (event->type) { case FAULT_TYPE_CHIP: memset(level_str, 0, FAULT_SHOW_STR_LEN + 1); level = event->event.chip.err_level; if (level < FAULT_LEVEL_MAX) strscpy(level_str, fault_level[level], FAULT_SHOW_STR_LEN); else strscpy(level_str, "Unknown", FAULT_SHOW_STR_LEN); if (level == FAULT_LEVEL_SERIOUS_FLR) { sdk_err(hwdev->dev_hdl, "err_level: %d [%s], flr func_id: %d\n", level, level_str, event->event.chip.func_id); atomic_inc(&fault->fault_type_stat[event->type]); } sdk_err(hwdev->dev_hdl, "module_id: 0x%x, err_type: 0x%x, err_level: %d[%s], err_csr_addr: 0x%08x, err_csr_value: 0x%08x\n", event->event.chip.node_id, event->event.chip.err_type, level, level_str, event->event.chip.err_csr_addr, event->event.chip.err_csr_value); node_id = event->event.chip.node_id; atomic_inc(&fault->chip_fault_stats[node_id][level]); base = event->event.chip.node_id * FAULT_LEVEL_MAX * HINIC_CHIP_ERROR_TYPE_MAX; pos = base + HINIC_CHIP_ERROR_TYPE_MAX * level + event->event.chip.err_type; if (pos < HINIC_CHIP_FAULT_SIZE) hwdev->chip_fault_stats[pos]++; break; case FAULT_TYPE_UCODE: atomic_inc(&fault->fault_type_stat[event->type]); sdk_err(hwdev->dev_hdl, "cause_id: %d, core_id: %d, c_id: %d, epc: 0x%08x\n", event->event.ucode.cause_id, event->event.ucode.core_id, event->event.ucode.c_id, event->event.ucode.epc); break; case FAULT_TYPE_MEM_RD_TIMEOUT: case FAULT_TYPE_MEM_WR_TIMEOUT: atomic_inc(&fault->fault_type_stat[event->type]); sdk_err(hwdev->dev_hdl, "err_csr_ctrl: 0x%08x, err_csr_data: 0x%08x, ctrl_tab: 0x%08x, mem_index: 0x%08x\n", event->event.mem_timeout.err_csr_ctrl, event->event.mem_timeout.err_csr_data, event->event.mem_timeout.ctrl_tab, event->event.mem_timeout.mem_index); break; case FAULT_TYPE_REG_RD_TIMEOUT: case FAULT_TYPE_REG_WR_TIMEOUT: atomic_inc(&fault->fault_type_stat[event->type]); sdk_err(hwdev->dev_hdl, "err_csr: 0x%08x\n", event->event.reg_timeout.err_csr); break; case FAULT_TYPE_PHY_FAULT: atomic_inc(&fault->fault_type_stat[event->type]); sdk_err(hwdev->dev_hdl, "op_type: %u, port_id: %u, dev_ad: %u, csr_addr: 0x%08x, op_data: 0x%08x\n", event->event.phy_fault.op_type, event->event.phy_fault.port_id, event->event.phy_fault.dev_ad, event->event.phy_fault.csr_addr, event->event.phy_fault.op_data); break; default: break; } } void hinic_migrate_report(void *dev) { struct hinic_hwdev *hwdev = (struct hinic_hwdev *)dev; struct hinic_event_info event_info = {0}; if (!dev) return; event_info.type = HINIC_EVENT_INIT_MIGRATE_PF; if (hwdev->event_callback) hwdev->event_callback(hwdev->event_pri_handle, &event_info); } EXPORT_SYMBOL(hinic_migrate_report); static void fault_event_handler(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_cmd_fault_event *fault_event; struct hinic_event_info event_info; u8 fault_level; if (in_size != sizeof(*fault_event)) { sdk_err(hwdev->dev_hdl, "Invalid fault event report, length: %d, should be %ld\n", in_size, sizeof(*fault_event)); return; } fault_event = buf_in; fault_report_show(hwdev, &fault_event->event); if (fault_event->event.type == HINIC_FAULT_SRC_HW_MGMT_CHIP) fault_level = fault_event->event.event.chip.err_level; else fault_level = FAULT_LEVEL_FATAL; if (hwdev->event_callback) { event_info.type = HINIC_EVENT_FAULT; memcpy(&event_info.info, &fault_event->event, sizeof(event_info.info)); event_info.info.fault_level = fault_level; hwdev->event_callback(hwdev->event_pri_handle, &event_info); } } static void heartbeat_lost_event_handler(struct hinic_hwdev *hwdev) { struct hinic_event_info event_info = {0}; atomic_inc(&hwdev->hw_stats.heart_lost_stats); sdk_err(hwdev->dev_hdl, "Heart lost report received, func_id: %d\n", hinic_global_func_id(hwdev)); if (hwdev->event_callback) { event_info.type = HINIC_EVENT_HEART_LOST; hwdev->event_callback(hwdev->event_pri_handle, &event_info); } } static void link_status_event_handler(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_port_link_status *link_status, *ret_link_status; struct hinic_event_info event_info = {0}; struct hinic_event_link_info *link_info = &event_info.link_info; struct nic_port_info port_info = {0}; int err; /* Ignore link change event */ if (FUNC_FORCE_LINK_UP(hwdev)) return; link_status = buf_in; sdk_info(hwdev->dev_hdl, "Link status report received, func_id: %d, status: %d\n", hinic_global_func_id(hwdev), link_status->link); if (link_status->link) atomic_inc(&hwdev->hw_stats.link_event_stats.link_up_stats); else atomic_inc(&hwdev->hw_stats.link_event_stats.link_down_stats); /* link event reported only after set vport enable */ if (hinic_func_type(hwdev) != TYPE_VF && link_status->link == HINIC_EVENT_LINK_UP) { err = hinic_get_port_info(hwdev, &port_info); if (err) { nic_warn(hwdev->dev_hdl, "Failed to get port info\n"); } else { link_info->valid = 1; link_info->port_type = port_info.port_type; link_info->autoneg_cap = port_info.autoneg_cap; link_info->autoneg_state = port_info.autoneg_state; link_info->duplex = port_info.duplex; link_info->speed = port_info.speed; hinic_refresh_nic_cfg(hwdev, &port_info); } } if (!hwdev->event_callback) return; event_info.type = link_status->link ? HINIC_EVENT_LINK_UP : HINIC_EVENT_LINK_DOWN; hwdev->event_callback(hwdev->event_pri_handle, &event_info); if (hinic_func_type(hwdev) != TYPE_VF) { hinic_notify_all_vfs_link_changed(hwdev, link_status->link); ret_link_status = buf_out; ret_link_status->status = 0; *out_size = sizeof(*ret_link_status); } } static void module_status_event(struct hinic_hwdev *hwdev, enum hinic_event_cmd cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_cable_plug_event *plug_event; struct hinic_link_err_event *link_err; struct hinic_event_info event_info = {0}; struct hinic_port_routine_cmd *rt_cmd; struct card_node *chip_node = hwdev->chip_node; event_info.type = HINIC_EVENT_PORT_MODULE_EVENT; if (cmd == HINIC_EVENT_CABLE_PLUG) { plug_event = buf_in; if (plug_event->port_id < HINIC_MAX_PORT_ID) { rt_cmd = &chip_node->rt_cmd[plug_event->port_id]; mutex_lock(&chip_node->sfp_mutex); rt_cmd->up_send_sfp_abs = false; rt_cmd->up_send_sfp_info = false; mutex_unlock(&chip_node->sfp_mutex); } event_info.module_event.type = plug_event->plugged ? HINIC_PORT_MODULE_CABLE_PLUGGED : HINIC_PORT_MODULE_CABLE_UNPLUGGED; *out_size = sizeof(*plug_event); plug_event = buf_out; plug_event->status = 0; } else if (cmd == HINIC_EVENT_LINK_ERR) { link_err = buf_in; event_info.module_event.type = HINIC_PORT_MODULE_LINK_ERR; event_info.module_event.err_type = link_err->err_type; *out_size = sizeof(*link_err); link_err = buf_out; link_err->status = 0; } else { sdk_warn(hwdev->dev_hdl, "Unknown module event: %d\n", cmd); return; } if (!hwdev->event_callback) return; hwdev->event_callback(hwdev->event_pri_handle, &event_info); } void hinic_notify_dcb_state_event(struct hinic_hwdev *hwdev, struct hinic_dcb_state *dcb_state) { struct hinic_event_info event_info = {0}; sdk_info(hwdev->dev_hdl, "DCB %s, default cos %d, up2cos %d%d%d%d%d%d%d%d\n", dcb_state->dcb_on ? "on" : "off", dcb_state->default_cos, dcb_state->up_cos[0], dcb_state->up_cos[1], dcb_state->up_cos[2], dcb_state->up_cos[3], dcb_state->up_cos[4], dcb_state->up_cos[5], dcb_state->up_cos[6], dcb_state->up_cos[7]); /* Saved in sdk for statefull module */ hinic_save_dcb_state(hwdev, dcb_state); if (!hwdev->event_callback) return; event_info.type = HINIC_EVENT_DCB_STATE_CHANGE; memcpy(&event_info.dcb_state, dcb_state, sizeof(event_info.dcb_state)); hwdev->event_callback(hwdev->event_pri_handle, &event_info); } static void sw_watchdog_timeout_info_show(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_mgmt_watchdog_info *watchdog_info; u32 *dump_addr, *reg, stack_len, i, j; if (in_size != sizeof(*watchdog_info)) { sdk_err(hwdev->dev_hdl, "Invalid mgmt watchdog report, length: %d, should be %ld\n", in_size, sizeof(*watchdog_info)); return; } watchdog_info = buf_in; sdk_err(hwdev->dev_hdl, "Mgmt deadloop time: 0x%x 0x%x, task id: 0x%x, sp: 0x%x\n", watchdog_info->curr_time_h, watchdog_info->curr_time_l, watchdog_info->task_id, watchdog_info->sp); sdk_err(hwdev->dev_hdl, "Stack current used: 0x%x, peak used: 0x%x, overflow flag: 0x%x, top: 0x%x, bottom: 0x%x\n", watchdog_info->curr_used, watchdog_info->peak_used, watchdog_info->is_overflow, watchdog_info->stack_top, watchdog_info->stack_bottom); sdk_err(hwdev->dev_hdl, "Mgmt pc: 0x%08x, lr: 0x%08x, cpsr:0x%08x\n", watchdog_info->pc, watchdog_info->lr, watchdog_info->cpsr); sdk_err(hwdev->dev_hdl, "Mgmt register info\n"); for (i = 0; i < 3; i++) { reg = watchdog_info->reg + (u64)(u32)(4 * i); sdk_err(hwdev->dev_hdl, "0x%08x 0x%08x 0x%08x 0x%08x\n", *(reg), *(reg + 1), *(reg + 2), *(reg + 3)); } sdk_err(hwdev->dev_hdl, "0x%08x\n", watchdog_info->reg[12]); if (watchdog_info->stack_actlen <= 1024) { stack_len = watchdog_info->stack_actlen; } else { sdk_err(hwdev->dev_hdl, "Oops stack length: 0x%x is wrong\n", watchdog_info->stack_actlen); stack_len = 1024; } sdk_err(hwdev->dev_hdl, "Mgmt dump stack, 16Bytes per line(start from sp)\n"); for (i = 0; i < (stack_len / 16); i++) { dump_addr = (u32 *)(watchdog_info->data + ((u64)(u32)(i * 16))); sdk_err(hwdev->dev_hdl, "0x%08x 0x%08x 0x%08x 0x%08x\n", *dump_addr, *(dump_addr + 1), *(dump_addr + 2), *(dump_addr + 3)); } for (j = 0; j < ((stack_len % 16) / 4); j++) { dump_addr = (u32 *)(watchdog_info->data + ((u64)(u32)(i * 16 + j * 4))); sdk_err(hwdev->dev_hdl, "0x%08x ", *dump_addr); } *out_size = sizeof(*watchdog_info); watchdog_info = buf_out; watchdog_info->status = 0; } static void mgmt_watchdog_timeout_event_handler(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_event_info event_info = { 0 }; sw_watchdog_timeout_info_show(hwdev, buf_in, in_size, buf_out, out_size); if (hwdev->event_callback) { event_info.type = HINIC_EVENT_MGMT_WATCHDOG_EVENT; hwdev->event_callback(hwdev->event_pri_handle, &event_info); } } static void port_sfp_info_event(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_cmd_get_sfp_qsfp_info *sfp_info = buf_in; struct hinic_port_routine_cmd *rt_cmd; struct card_node *chip_node = hwdev->chip_node; if (in_size != sizeof(*sfp_info)) { sdk_err(hwdev->dev_hdl, "Invalid sfp info cmd, length: %d, should be %ld\n", in_size, sizeof(*sfp_info)); return; } if (sfp_info->port_id >= HINIC_MAX_PORT_ID) { sdk_err(hwdev->dev_hdl, "Invalid sfp port id: %d, max port is %d\n", sfp_info->port_id, HINIC_MAX_PORT_ID - 1); return; } rt_cmd = &chip_node->rt_cmd[sfp_info->port_id]; mutex_lock(&chip_node->sfp_mutex); memcpy(&rt_cmd->sfp_info, sfp_info, sizeof(rt_cmd->sfp_info)); rt_cmd->up_send_sfp_info = true; mutex_unlock(&chip_node->sfp_mutex); } static void port_sfp_abs_event(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_cmd_get_light_module_abs *sfp_abs = buf_in; struct hinic_port_routine_cmd *rt_cmd; struct card_node *chip_node = hwdev->chip_node; if (in_size != sizeof(*sfp_abs)) { sdk_err(hwdev->dev_hdl, "Invalid sfp absent cmd, length: %d, should be %ld\n", in_size, sizeof(*sfp_abs)); return; } if (sfp_abs->port_id >= HINIC_MAX_PORT_ID) { sdk_err(hwdev->dev_hdl, "Invalid sfp port id: %d, max port is %d\n", sfp_abs->port_id, HINIC_MAX_PORT_ID - 1); return; } rt_cmd = &chip_node->rt_cmd[sfp_abs->port_id]; mutex_lock(&chip_node->sfp_mutex); memcpy(&rt_cmd->abs, sfp_abs, sizeof(rt_cmd->abs)); rt_cmd->up_send_sfp_abs = true; mutex_unlock(&chip_node->sfp_mutex); } static void mgmt_reset_event_handler(struct hinic_hwdev *hwdev) { sdk_info(hwdev->dev_hdl, "Mgmt is reset\n"); /* mgmt reset only occurred when hot update or Mgmt deadloop, * if Mgmt deadloop, mgmt will report an event with * mod=0, cmd=0x56, and will reported fault to os, * so mgmt reset event don't need to report fault */ } static void hinic_fmw_act_ntc_handler(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_event_info event_info = {0}; struct hinic_fmw_act_ntc *notice_info; if (in_size != sizeof(*notice_info)) { sdk_err(hwdev->dev_hdl, "Invalid mgmt firmware active notice, length: %d, should be %ld\n", in_size, sizeof(*notice_info)); return; } if (!hwdev->event_callback) return; event_info.type = HINIC_EVENT_FMW_ACT_NTC; hwdev->event_callback(hwdev->event_pri_handle, &event_info); *out_size = sizeof(*notice_info); notice_info = buf_out; notice_info->status = 0; } static void hinic_pcie_dfx_event_handler(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_pcie_dfx_ntc *notice_info = buf_in; struct hinic_pcie_dfx_info *dfx_info; u16 size = 0; u16 cnt = 0; u32 num = 0; u32 i, j; int err; u32 *reg; if (in_size != sizeof(*notice_info)) { sdk_err(hwdev->dev_hdl, "Invalid mgmt firmware active notice, length: %d, should be %ld\n", in_size, sizeof(*notice_info)); return; } dfx_info = kzalloc(sizeof(*dfx_info), GFP_KERNEL); if (!dfx_info) return; ((struct hinic_pcie_dfx_ntc *)buf_out)->status = 0; *out_size = sizeof(*notice_info); num = (u32)(notice_info->len / 1024); sdk_info(hwdev->dev_hdl, "INFO LEN: %d\n", notice_info->len); sdk_info(hwdev->dev_hdl, "PCIE DFX:\n"); dfx_info->host_id = 0; for (i = 0; i < num; i++) { dfx_info->offset = i * MAX_PCIE_DFX_BUF_SIZE; if (i == (num - 1)) dfx_info->last = 1; size = sizeof(*dfx_info); err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_PCIE_DFX_GET, dfx_info, sizeof(*dfx_info), dfx_info, &size, 0); if (err || dfx_info->status || !size) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get pcie dfx info, err: %d, status: 0x%x, out size: 0x%x\n", err, dfx_info->status, size); kfree(dfx_info); return; } reg = (u32 *)dfx_info->data; for (j = 0; j < 256; j = j + 8) { /*lint -save -e661 -e662*/ sdk_info(hwdev->dev_hdl, "0x%04x: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", cnt, reg[j], reg[(u32)(j + 1)], reg[(u32)(j + 2)], reg[(u32)(j + 3)], reg[(u32)(j + 4)], reg[(u32)(j + 5)], reg[(u32)(j + 6)], reg[(u32)(j + 7)]); /*lint -restore*/ cnt = cnt + 32; } memset(dfx_info->data, 0, MAX_PCIE_DFX_BUF_SIZE); } kfree(dfx_info); } struct hinic_mctp_get_host_info { u8 status; u8 version; u8 rsvd0[6]; u8 huawei_cmd; u8 sub_cmd; u8 rsvd[2]; u32 actual_len; u8 data[1024]; }; static void hinic_mctp_get_host_info_event_handler(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_event_info event_info = {0}; struct hinic_mctp_get_host_info *mctp_out, *mctp_in; struct hinic_mctp_host_info *host_info; if (in_size != sizeof(*mctp_in)) { sdk_err(hwdev->dev_hdl, "Invalid mgmt mctp info, length: %d, should be %ld\n", in_size, sizeof(*mctp_in)); return; } *out_size = sizeof(*mctp_out); mctp_out = buf_out; mctp_out->status = 0; if (!hwdev->event_callback) { mctp_out->status = HINIC_MGMT_STATUS_ERR_INIT; return; } mctp_in = buf_in; host_info = &event_info.mctp_info; host_info->major_cmd = mctp_in->huawei_cmd; host_info->sub_cmd = mctp_in->sub_cmd; host_info->data = mctp_out->data; event_info.type = HINIC_EVENT_MCTP_GET_HOST_INFO; hwdev->event_callback(hwdev->event_pri_handle, &event_info); mctp_out->actual_len = host_info->data_len; } static char *__hw_to_char_fec[HILINK_FEC_MAX_TYPE] = {"RS-FEC", "BASE-FEC", "NO-FEC"}; static char *__hw_to_char_port_type[LINK_PORT_MAX_TYPE] = { "Unknown", "Fibre", "Electric", "Direct Attach Copper", "AOC", "Back plane", "BaseT" }; static void __print_cable_info(struct hinic_hwdev *hwdev, struct hinic_link_info *info) { char tmp_str[CAP_INFO_MAC_LEN] = {0}; char tmp_vendor[VENDOR_MAX_LEN] = {0}; char *port_type = "Unknown port type"; int i; int err = 0; if (info->cable_absent) { sdk_info(hwdev->dev_hdl, "Cable unpresent\n"); return; } if (info->port_type < LINK_PORT_MAX_TYPE) port_type = __hw_to_char_port_type[info->port_type]; else sdk_info(hwdev->dev_hdl, "Unknown port type: %u\n", info->port_type); if (info->port_type == LINK_PORT_FIBRE) { if (info->port_sub_type == FIBRE_SUBTYPE_SR) port_type = "Fibre-SR"; else if (info->port_sub_type == FIBRE_SUBTYPE_LR) port_type = "Fibre-LR"; } for (i = sizeof(info->vendor_name) - 1; i >= 0; i--) { if (info->vendor_name[i] == ' ') info->vendor_name[i] = '\0'; else break; } memcpy(tmp_vendor, info->vendor_name, sizeof(info->vendor_name)); err = snprintf(tmp_str, sizeof(tmp_str), "Vendor: %s, %s, length: %um, max_speed: %uGbps", tmp_vendor, port_type, info->cable_length, info->cable_max_speed); if (err <= 0 || err >= CAP_INFO_MAC_LEN) { sdk_err(hwdev->dev_hdl, "Failed snprintf cable vendor info, function return(%d) and dest_len(%d)\n", err, CAP_INFO_MAC_LEN); return; } if (info->port_type == LINK_PORT_FIBRE || info->port_type == LINK_PORT_AOC) { err = snprintf(tmp_str, sizeof(tmp_str), "%s, %s, Temperature: %u", tmp_str, info->sfp_type ? "SFP" : "QSFP", info->cable_temp); if (err <= 0 || err >= CAP_INFO_MAC_LEN) { sdk_err(hwdev->dev_hdl, "Failed snprintf cable Temp, function return(%d) and dest_len(%d)\n", err, CAP_INFO_MAC_LEN); return; } if (info->sfp_type) { err = snprintf(tmp_str, sizeof(tmp_str), "%s, rx power: %uuW, tx power: %uuW", tmp_str, info->power[0], info->power[1]); } else { err = snprintf(tmp_str, sizeof(tmp_str), "%s, rx power: %uuw %uuW %uuW %uuW", tmp_str, info->power[0], info->power[1], info->power[2], info->power[3]); } if (err <= 0 || err >= CAP_INFO_MAC_LEN) { sdk_err(hwdev->dev_hdl, "Failed snprintf power info, function return(%d) and dest_len(%d)\n", err, CAP_INFO_MAC_LEN); return; } } sdk_info(hwdev->dev_hdl, "Cable information: %s\n", tmp_str); } static void __hi30_lane_info(struct hinic_hwdev *hwdev, struct hilink_lane *lane) { struct hi30_ffe_data *ffe_data; struct hi30_ctle_data *ctle_data; ffe_data = (struct hi30_ffe_data *)lane->hi30_ffe; ctle_data = (struct hi30_ctle_data *)lane->hi30_ctle; sdk_info(hwdev->dev_hdl, "TX_FFE: PRE1=%s%d; PRE2=%s%d; MAIN=%d; POST1=%s%d; POST1X=%s%d\n", (ffe_data->PRE1 & 0x10) ? "-" : "", (int)(ffe_data->PRE1 & 0xf), (ffe_data->PRE2 & 0x10) ? "-" : "", (int)(ffe_data->PRE2 & 0xf), (int)ffe_data->MAIN, (ffe_data->POST1 & 0x10) ? "-" : "", (int)(ffe_data->POST1 & 0xf), (ffe_data->POST2 & 0x10) ? "-" : "", (int)(ffe_data->POST2 & 0xf)); sdk_info(hwdev->dev_hdl, "RX_CTLE: Gain1~3=%u %u %u; Boost1~3=%u %u %u; Zero1~3=%u %u %u; Squelch1~3=%u %u %u\n", ctle_data->ctlebst[0], ctle_data->ctlebst[1], ctle_data->ctlebst[2], ctle_data->ctlecmband[0], ctle_data->ctlecmband[1], ctle_data->ctlecmband[2], ctle_data->ctlermband[0], ctle_data->ctlermband[1], ctle_data->ctlermband[2], ctle_data->ctleza[0], ctle_data->ctleza[1], ctle_data->ctleza[2]); } static void __print_hi30_status(struct hinic_hwdev *hwdev, struct hinic_link_info *info) { struct hilink_lane *lane; int lane_used_num = 0, i; for (i = 0; i < HILINK_MAX_LANE; i++) { lane = (struct hilink_lane *)(info->lane2 + i * sizeof(*lane)); if (!lane->lane_used) continue; __hi30_lane_info(hwdev, lane); lane_used_num++; } /* in new firmware, all lane info setted in lane2 */ if (lane_used_num) return; /* compatible old firmware */ __hi30_lane_info(hwdev, (struct hilink_lane *)info->lane1); } static void __print_link_info(struct hinic_hwdev *hwdev, struct hinic_link_info *info, enum hilink_info_print_event type) { char *fec = "None"; if (info->fec < HILINK_FEC_MAX_TYPE) fec = __hw_to_char_fec[info->fec]; else sdk_info(hwdev->dev_hdl, "Unknown fec type: %u\n", info->fec); if (type == HILINK_EVENT_LINK_UP || !info->an_state) { sdk_info(hwdev->dev_hdl, "Link information: speed %dGbps, %s, autoneg %s\n", info->speed, fec, info->an_state ? "on" : "off"); } else { sdk_info(hwdev->dev_hdl, "Link information: antoneg: %s\n", info->an_state ? "on" : "off"); } } static char *hilink_info_report_type[HILINK_EVENT_MAX_TYPE] = { "", "link up", "link down", "cable plugged" }; static void print_hilink_info(struct hinic_hwdev *hwdev, enum hilink_info_print_event type, struct hinic_link_info *info) { __print_cable_info(hwdev, info); __print_link_info(hwdev, info, type); __print_hi30_status(hwdev, info); if (type == HILINK_EVENT_LINK_UP) return; if (type == HILINK_EVENT_CABLE_PLUGGED) { sdk_info(hwdev->dev_hdl, "alos: %u, rx_los: %u\n", info->alos, info->rx_los); return; } sdk_info(hwdev->dev_hdl, "PMA ctrl: %s, MAC tx %s, MAC rx %s, PMA debug info reg: 0x%x, PMA signal ok reg: 0x%x, RF/LF status reg: 0x%x\n", info->pma_status == 1 ? "off" : "on", info->mac_tx_en ? "enable" : "disable", info->mac_rx_en ? "enable" : "disable", info->pma_dbg_info_reg, info->pma_signal_ok_reg, info->rf_lf_status_reg); sdk_info(hwdev->dev_hdl, "alos: %u, rx_los: %u, PCS block counter reg: 0x%x, PCS link: 0x%x, MAC link: 0x%x PCS_err_cnt: 0x%x\n", info->alos, info->rx_los, info->pcs_err_blk_cnt_reg, info->pcs_link_reg, info->mac_link_reg, info->pcs_err_cnt); } static int hinic_print_hilink_info(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hilink_link_info *hilink_info = buf_in; struct hinic_link_info *info; enum hilink_info_print_event type; if (in_size != sizeof(*hilink_info)) { sdk_err(hwdev->dev_hdl, "Invalid hilink info message size %d, should be %ld\n", in_size, sizeof(*hilink_info)); return -EINVAL; } ((struct hinic_hilink_link_info *)buf_out)->status = 0; *out_size = sizeof(*hilink_info); info = &hilink_info->info; type = hilink_info->info_type; if (type < HILINK_EVENT_LINK_UP || type >= HILINK_EVENT_MAX_TYPE) { sdk_info(hwdev->dev_hdl, "Invalid hilink info report, type: %d\n", type); return -EINVAL; } sdk_info(hwdev->dev_hdl, "Hilink info report after %s\n", hilink_info_report_type[type]); print_hilink_info(hwdev, type, info); return 0; } int hinic_hilink_info_show(struct hinic_hwdev *hwdev) { struct hinic_link_info hilink_info = { {0} }; int err; err = hinic_get_hilink_link_info(hwdev, &hilink_info); if (err) { if (err == HINIC_MGMT_CMD_UNSUPPORTED) sdk_info(hwdev->dev_hdl, "Unsupport to get hilink info\n"); return err; } if (hilink_info.cable_absent) { sdk_info(hwdev->dev_hdl, "Cable unpresent\n"); return 0; } sdk_info(hwdev->dev_hdl, "Current state of hilink info:\n"); print_hilink_info(hwdev, HILINK_EVENT_MAX_TYPE, &hilink_info); return 0; } /* public process for this event: * pf link change event * pf heart lost event ,TBD * pf fault report event * vf link change event * vf heart lost event, TBD * vf fault report event, TBD */ static void _event_handler(struct hinic_hwdev *hwdev, enum hinic_event_cmd cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_vf_dcb_state *vf_dcb; if (!hwdev) return; *out_size = 0; switch (cmd) { case HINIC_EVENT_LINK_STATUS_CHANGE: link_status_event_handler(hwdev, buf_in, in_size, buf_out, out_size); break; case HINIC_EVENT_CABLE_PLUG: case HINIC_EVENT_LINK_ERR: module_status_event(hwdev, cmd, buf_in, in_size, buf_out, out_size); break; case HINIC_EVENT_HILINK_INFO: hinic_print_hilink_info(hwdev, buf_in, in_size, buf_out, out_size); break; case HINIC_EVENT_MGMT_FAULT: fault_event_handler(hwdev, buf_in, in_size, buf_out, out_size); break; case HINIC_EVENT_HEARTBEAT_LOST: heartbeat_lost_event_handler(hwdev); break; case HINIC_EVENT_SET_VF_COS: vf_dcb = buf_in; if (!vf_dcb) break; hinic_notify_dcb_state_event(hwdev, &vf_dcb->state); break; case HINIC_EVENT_MGMT_WATCHDOG: mgmt_watchdog_timeout_event_handler(hwdev, buf_in, in_size, buf_out, out_size); break; case HINIC_EVENT_MGMT_RESET: mgmt_reset_event_handler(hwdev); break; case HINIC_EVENT_MGMT_FMW_ACT_NTC: hinic_fmw_act_ntc_handler(hwdev, buf_in, in_size, buf_out, out_size); break; case HINIC_EVENT_MGMT_PCIE_DFX: hinic_pcie_dfx_event_handler(hwdev, buf_in, in_size, buf_out, out_size); break; case HINIC_EVENT_MCTP_HOST_INFO: hinic_mctp_get_host_info_event_handler(hwdev, buf_in, in_size, buf_out, out_size); break; case HINIC_EVENT_SFP_INFO_REPORT: port_sfp_info_event(hwdev, buf_in, in_size, buf_out, out_size); break; case HINIC_EVENT_SFP_ABS_REPORT: port_sfp_abs_event(hwdev, buf_in, in_size, buf_out, out_size); break; default: sdk_warn(hwdev->dev_hdl, "Unsupported event %d to process\n", cmd); break; } } /* vf link change event * vf fault report event, TBD */ static int vf_nic_event_handler(void *hwdev, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { enum hinic_event_cmd type = __get_event_type(HINIC_MOD_L2NIC, cmd); if (type == HINIC_EVENT_MAX_TYPE) { sdk_warn(((struct hinic_hwdev *)hwdev)->dev_hdl, "Unsupport L2NIC event: cmd %d\n", cmd); *out_size = 0; return -EINVAL; } _event_handler(hwdev, type, buf_in, in_size, buf_out, out_size); return 0; } static int vf_comm_event_handler(void *hwdev, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { enum hinic_event_cmd type = __get_event_type(HINIC_MOD_COMM, cmd); if (type == HINIC_EVENT_MAX_TYPE) { sdk_warn(((struct hinic_hwdev *)hwdev)->dev_hdl, "Unsupport COMM event: cmd %d\n", cmd); *out_size = 0; return -EFAULT; } _event_handler(hwdev, type, buf_in, in_size, buf_out, out_size); return 0; } /* pf link change event */ static void pf_nic_event_handler(void *hwdev, void *pri_handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { enum hinic_event_cmd type = __get_event_type(HINIC_MOD_L2NIC, cmd); if (type == HINIC_EVENT_MAX_TYPE) { sdk_warn(((struct hinic_hwdev *)hwdev)->dev_hdl, "Unsupport L2NIC event: cmd %d\n", cmd); *out_size = 0; return; } _event_handler(hwdev, type, buf_in, in_size, buf_out, out_size); } static void pf_hilink_event_handler(void *hwdev, void *pri_handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { enum hinic_event_cmd type = __get_event_type(HINIC_MOD_HILINK, cmd); if (type == HINIC_EVENT_MAX_TYPE) { sdk_warn(((struct hinic_hwdev *)hwdev)->dev_hdl, "Unsupport HILINK event: cmd %d\n", cmd); *out_size = 0; return; } _event_handler(hwdev, type, buf_in, in_size, buf_out, out_size); } /* pf fault report event */ static void pf_fault_event_handler(void *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { _event_handler(hwdev, HINIC_EVENT_MGMT_FAULT, buf_in, in_size, buf_out, out_size); } static void mgmt_watchdog_event_handler(void *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { _event_handler(hwdev, HINIC_EVENT_MGMT_WATCHDOG, buf_in, in_size, buf_out, out_size); } static void mgmt_fmw_act_event_handler(void *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { _event_handler(hwdev, HINIC_EVENT_MGMT_FMW_ACT_NTC, buf_in, in_size, buf_out, out_size); } static void mgmt_pcie_dfx_event_handler(void *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { _event_handler(hwdev, HINIC_EVENT_MGMT_PCIE_DFX, buf_in, in_size, buf_out, out_size); } static void mgmt_get_mctp_event_handler(void *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { _event_handler(hwdev, HINIC_EVENT_MCTP_HOST_INFO, buf_in, in_size, buf_out, out_size); } static void pf_event_register(struct hinic_hwdev *hwdev) { if (hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MGMT_INITED)) { hinic_register_mgmt_msg_cb(hwdev, HINIC_MOD_L2NIC, hwdev, pf_nic_event_handler); hinic_register_mgmt_msg_cb(hwdev, HINIC_MOD_HILINK, hwdev, pf_hilink_event_handler); hinic_comm_recv_mgmt_self_cmd_reg(hwdev, HINIC_MGMT_CMD_FAULT_REPORT, pf_fault_event_handler); hinic_comm_recv_mgmt_self_cmd_reg(hwdev, HINIC_MGMT_CMD_WATCHDOG_INFO, mgmt_watchdog_event_handler); hinic_comm_recv_mgmt_self_cmd_reg(hwdev, HINIC_MGMT_CMD_FMW_ACT_NTC, mgmt_fmw_act_event_handler); hinic_comm_recv_mgmt_self_cmd_reg(hwdev, HINIC_MGMT_CMD_PCIE_DFX_NTC, mgmt_pcie_dfx_event_handler); hinic_comm_recv_mgmt_self_cmd_reg(hwdev, HINIC_MGMT_CMD_GET_HOST_INFO, mgmt_get_mctp_event_handler); } } void hinic_event_register(void *dev, void *pri_handle, hinic_event_handler callback) { struct hinic_hwdev *hwdev = dev; if (!dev) { pr_err("Hwdev pointer is NULL for register event\n"); return; } hwdev->event_callback = callback; hwdev->event_pri_handle = pri_handle; if (hinic_func_type(hwdev) != TYPE_VF) { pf_event_register(hwdev); } else { hinic_register_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC, vf_nic_event_handler); hinic_register_vf_mbox_cb(hwdev, HINIC_MOD_COMM, vf_comm_event_handler); } } void hinic_event_unregister(void *dev) { struct hinic_hwdev *hwdev = dev; hwdev->event_callback = NULL; hwdev->event_pri_handle = NULL; if (hinic_func_type(hwdev) != TYPE_VF) { hinic_unregister_mgmt_msg_cb(hwdev, HINIC_MOD_L2NIC); hinic_unregister_mgmt_msg_cb(hwdev, HINIC_MOD_HILINK); hinic_comm_recv_up_self_cmd_unreg(hwdev, HINIC_MGMT_CMD_FAULT_REPORT); hinic_comm_recv_up_self_cmd_unreg(hwdev, HINIC_MGMT_CMD_WATCHDOG_INFO); hinic_comm_recv_up_self_cmd_unreg(hwdev, HINIC_MGMT_CMD_FMW_ACT_NTC); hinic_comm_recv_up_self_cmd_unreg(hwdev, HINIC_MGMT_CMD_PCIE_DFX_NTC); hinic_comm_recv_up_self_cmd_unreg(hwdev, HINIC_MGMT_CMD_GET_HOST_INFO); } else { hinic_unregister_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC); hinic_unregister_vf_mbox_cb(hwdev, HINIC_MOD_COMM); } } /* 0 - heartbeat lost, 1 - normal */ static u8 hinic_get_heartbeat_status(struct hinic_hwdev *hwdev) { struct hinic_hwif *hwif = hwdev->hwif; u32 attr1; /* suprise remove should be set 1 */ if (!hinic_get_chip_present_flag(hwdev)) return 1; attr1 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR1_ADDR); if (attr1 == HINIC_PCIE_LINK_DOWN) { sdk_err(hwdev->dev_hdl, "Detect pcie is link down\n"); hinic_set_chip_absent(hwdev); hinic_force_complete_all(hwdev); /* should notify chiperr to pangea when detecting pcie link down */ return 1; } return HINIC_AF1_GET(attr1, MGMT_INIT_STATUS); } static void hinic_heartbeat_event_handler(struct work_struct *work) { struct hinic_hwdev *hwdev = container_of(work, struct hinic_hwdev, timer_work); u16 out = 0; _event_handler(hwdev, HINIC_EVENT_HEARTBEAT_LOST, NULL, 0, &out, &out); } static void hinic_heartbeat_timer_handler(struct timer_list *t) { struct hinic_hwdev *hwdev = from_timer(hwdev, t, heartbeat_timer); if (!hinic_get_heartbeat_status(hwdev)) { hwdev->heartbeat_lost = 1; queue_work(hwdev->workq, &hwdev->timer_work); } else { mod_timer(&hwdev->heartbeat_timer, jiffies + msecs_to_jiffies(HINIC_HEARTBEAT_PERIOD)); } } void hinic_init_heartbeat(struct hinic_hwdev *hwdev) { timer_setup(&hwdev->heartbeat_timer, hinic_heartbeat_timer_handler, 0); hwdev->heartbeat_timer.expires = jiffies + msecs_to_jiffies(HINIC_HEARTBEAT_START_EXPIRE); add_timer(&hwdev->heartbeat_timer); INIT_WORK(&hwdev->timer_work, hinic_heartbeat_event_handler); } void hinic_destroy_heartbeat(struct hinic_hwdev *hwdev) { del_timer_sync(&hwdev->heartbeat_timer); } u8 hinic_nic_sw_aeqe_handler(void *handle, u8 event, u64 data) { struct hinic_hwdev *hwdev = (struct hinic_hwdev *)handle; u8 event_level = FAULT_LEVEL_MAX; switch (event) { case HINIC_INTERNAL_TSO_FATAL_ERROR: case HINIC_INTERNAL_LRO_FATAL_ERROR: case HINIC_INTERNAL_TX_FATAL_ERROR: case HINIC_INTERNAL_RX_FATAL_ERROR: case HINIC_INTERNAL_OTHER_FATAL_ERROR: atomic_inc(&hwdev->hw_stats.nic_ucode_event_stats[event]); sdk_err(hwdev->dev_hdl, "SW aeqe event type: 0x%x, data: 0x%llx\n", event, data); event_level = FAULT_LEVEL_FATAL; break; default: sdk_err(hwdev->dev_hdl, "Unsupported sw event %d to process\n", event); } return event_level; } struct hinic_fast_recycled_mode { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 fast_recycled_mode; /* 1: enable fast recycle, available * in dpdk mode, * 0: normal mode, available in kernel * nic mode */ u8 rsvd1; }; int hinic_enable_fast_recycle(void *hwdev, bool enable) { struct hinic_fast_recycled_mode fast_recycled_mode = {0}; u16 out_size = sizeof(fast_recycled_mode); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &fast_recycled_mode.func_id); if (err) return err; fast_recycled_mode.fast_recycled_mode = enable ? 1 : 0; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_FAST_RECYCLE_MODE_SET, &fast_recycled_mode, sizeof(fast_recycled_mode), &fast_recycled_mode, &out_size, 0); if (err || fast_recycled_mode.status || !out_size) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to set recycle mode, err: %d, status: 0x%x, out size: 0x%x\n", err, fast_recycled_mode.status, out_size); return -EFAULT; } return 0; } void hinic_set_pcie_order_cfg(void *handle) { struct hinic_hwdev *hwdev = handle; u32 val; if (!hwdev) return; val = hinic_hwif_read_reg(hwdev->hwif, HINIC_GLB_DMA_SO_RO_REPLACE_ADDR); if (HINIC_GLB_DMA_SO_RO_GET(val, SO_RO_CFG)) { val = HINIC_GLB_DMA_SO_R0_CLEAR(val, SO_RO_CFG); val |= HINIC_GLB_DMA_SO_R0_SET(HINIC_DISABLE_ORDER, SO_RO_CFG); hinic_hwif_write_reg(hwdev->hwif, HINIC_GLB_DMA_SO_RO_REPLACE_ADDR, val); } } int _set_led_status(struct hinic_hwdev *hwdev, u8 port, enum hinic_led_type type, enum hinic_led_mode mode, u8 reset) { struct hinic_led_info led_info = {0}; u16 out_size = sizeof(led_info); int err; led_info.port = port; led_info.reset = reset; led_info.type = type; led_info.mode = mode; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_SET_LED_STATUS, &led_info, sizeof(led_info), &led_info, &out_size, 0); if (err || led_info.status || !out_size) { sdk_err(hwdev->dev_hdl, "Failed to set led status, err: %d, status: 0x%x, out size: 0x%x\n", err, led_info.status, out_size); return -EFAULT; } return 0; } int hinic_set_led_status(void *hwdev, u8 port, enum hinic_led_type type, enum hinic_led_mode mode) { int err; if (!hwdev) return -EFAULT; err = _set_led_status(hwdev, port, type, mode, 0); if (err) return err; return 0; } int hinic_reset_led_status(void *hwdev, u8 port) { int err; if (!hwdev) return -EFAULT; err = _set_led_status(hwdev, port, HINIC_LED_TYPE_INVALID, HINIC_LED_MODE_INVALID, 1); if (err) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to reset led status\n"); return err; } return 0; } int hinic_get_board_info(void *hwdev, struct hinic_board_info *info) { struct hinic_comm_board_info board_info = {0}; u16 out_size = sizeof(board_info); int err; if (!hwdev || !info) return -EINVAL; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_GET_BOARD_INFO, &board_info, sizeof(board_info), &board_info, &out_size, 0); if (err || board_info.status || !out_size) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get board info, err: %d, status: 0x%x, out size: 0x%x\n", err, board_info.status, out_size); return -EFAULT; } memcpy(info, &board_info.info, sizeof(*info)); return 0; } EXPORT_SYMBOL(hinic_get_board_info); int hinic_get_phy_init_status(void *hwdev, enum phy_init_status_type *init_status) { struct hinic_phy_init_status phy_info = {0}; u16 out_size = sizeof(phy_info); int err; if (!hwdev || !init_status) return -EINVAL; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_GET_PHY_INIT_STATUS, &phy_info, sizeof(phy_info), &phy_info, &out_size, 0); if ((phy_info.status != HINIC_MGMT_CMD_UNSUPPORTED && phy_info.status) || err || !out_size) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get phy info, err: %d, status: 0x%x, out size: 0x%x\n", err, phy_info.status, out_size); return -EFAULT; } *init_status = phy_info.init_status; return phy_info.status; } int hinic_phy_init_status_judge(void *hwdev) { enum phy_init_status_type init_status; int ret; unsigned long end; /* It's not a phy, so don't judge phy status */ if (!HINIC_BOARD_IS_PHY((struct hinic_hwdev *)hwdev)) return 0; end = jiffies + msecs_to_jiffies(PHY_DOING_INIT_TIMEOUT); do { ret = hinic_get_phy_init_status(hwdev, &init_status); if (ret == HINIC_MGMT_CMD_UNSUPPORTED) return 0; else if (ret) return -EFAULT; switch (init_status) { case PHY_INIT_SUCCESS: sdk_info(((struct hinic_hwdev *)hwdev)->dev_hdl, "Phy init is success\n"); return 0; case PHY_NONSUPPORT: sdk_info(((struct hinic_hwdev *)hwdev)->dev_hdl, "Phy init is nonsupport\n"); return 0; case PHY_INIT_FAIL: sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Phy init is failed\n"); return -EIO; case PHY_INIT_DOING: msleep(250); break; default: sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Phy init is invalid, init_status: %d\n", init_status); return -EINVAL; } } while (time_before(jiffies, end)); sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Phy init is timeout\n"); return -ETIMEDOUT; } static void hinic_set_mgmt_channel_status(void *handle, bool state) { struct hinic_hwdev *hwdev = handle; u32 val; if (!hwdev || hinic_func_type(hwdev) == TYPE_VF || !(hwdev->feature_cap & HINIC_FUNC_SUPP_DFX_REG)) return; val = hinic_hwif_read_reg(hwdev->hwif, HINIC_ICPL_RESERVD_ADDR); val = HINIC_CLEAR_MGMT_CHANNEL_STATUS(val, MGMT_CHANNEL_STATUS); val |= HINIC_SET_MGMT_CHANNEL_STATUS((u32)state, MGMT_CHANNEL_STATUS); hinic_hwif_write_reg(hwdev->hwif, HINIC_ICPL_RESERVD_ADDR, val); } int hinic_get_mgmt_channel_status(void *handle) { struct hinic_hwdev *hwdev = handle; u32 val; if (!hwdev) return true; if (hinic_func_type(hwdev) == TYPE_VF || !(hwdev->feature_cap & HINIC_FUNC_SUPP_DFX_REG)) return false; val = hinic_hwif_read_reg(hwdev->hwif, HINIC_ICPL_RESERVD_ADDR); return HINIC_GET_MGMT_CHANNEL_STATUS(val, MGMT_CHANNEL_STATUS); } static void hinic_enable_mgmt_channel(void *hwdev, void *buf_out) { struct hinic_hwdev *dev = hwdev; struct hinic_update_active *active_info = buf_out; if (!active_info || hinic_func_type(hwdev) == TYPE_VF || !(dev->feature_cap & HINIC_FUNC_SUPP_DFX_REG)) return; if (!active_info->status && (active_info->update_status & HINIC_ACTIVE_STATUS_MASK)) { active_info->update_status &= HINIC_ACTIVE_STATUS_CLEAR; return; } hinic_set_mgmt_channel_status(hwdev, false); } int hinic_get_bios_pf_bw_limit(void *hwdev, u32 *pf_bw_limit) { struct hinic_hwdev *dev = hwdev; struct hinic_bios_cfg_cmd cfg = {0}; u16 out_size = sizeof(cfg); u16 func_id; int err; if (!hwdev || !pf_bw_limit) return -EINVAL; if (HINIC_FUNC_TYPE(dev) == TYPE_VF || !FUNC_SUPPORT_RATE_LIMIT(hwdev)) return 0; err = hinic_global_func_id_get(hwdev, &func_id); if (err) return err; cfg.func_valid = 1; cfg.func_idx = (u8)func_id; cfg.op_code = HINIC_BIOS_CFG_GET | HINIC_BIOS_CFG_PF_BW_LIMIT; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_BIOS_NV_DATA_MGMT, &cfg, sizeof(cfg), &cfg, &out_size, 0); if (err || cfg.status || !out_size) { sdk_err(dev->dev_hdl, "Failed to get bios pf bandwidth limit, err: %d, status: 0x%x, out size: 0x%x\n", err, cfg.status, out_size); return -EIO; } /* Check data is valid or not */ if (cfg.signature != 0x19e51822) { sdk_err(dev->dev_hdl, "Invalid bios configureration data, signature: 0x%x\n", cfg.signature); return -EINVAL; } if (cfg.pf_bw_limit > 100) { sdk_err(dev->dev_hdl, "Invalid bios cfg pf bandwidth limit: %d\n", cfg.pf_bw_limit); return -EINVAL; } *pf_bw_limit = cfg.pf_bw_limit; return 0; } bool hinic_get_ppf_status(void *hwdev) { struct hinic_ppf_state ppf_state = {0}; struct hinic_hwdev *dev = hwdev; struct card_node *chip_node; u16 out_size = sizeof(ppf_state); int err; if (!hwdev) return false; chip_node = (struct card_node *)dev->chip_node; if (!HINIC_IS_VF(dev)) return chip_node->ppf_state; err = hinic_mbox_to_pf(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_GET_PPF_STATE, &ppf_state, sizeof(ppf_state), &ppf_state, &out_size, 0); if (err || ppf_state.status || !out_size) { sdk_err(dev->dev_hdl, "Failed to get ppf state, err: %d, status: 0x%x, out size: 0x%x\n", err, ppf_state.status, out_size); return false; } return (bool)ppf_state.ppf_state; } void hinic_set_func_deinit_flag(void *hwdev) { struct hinic_hwdev *dev = hwdev; set_bit(HINIC_HWDEV_FUNC_DEINIT, &dev->func_state); } int hinic_get_hw_pf_infos(void *hwdev, struct hinic_hw_pf_infos *infos) { struct hinic_hw_pf_infos_cmd pf_infos = {0}; u16 out_size = sizeof(pf_infos); int err; if (!hwdev || !infos) return -EINVAL; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_GET_HW_PF_INFOS, &pf_infos, sizeof(pf_infos), &pf_infos, &out_size, 0); if ((pf_infos.status != HINIC_MGMT_CMD_UNSUPPORTED && pf_infos.status) || err || !out_size) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get hw pf information, err: %d, status: 0x%x, out size: 0x%x\n", err, pf_infos.status, out_size); return -EFAULT; } if (!pf_infos.status) memcpy(infos, &pf_infos.infos, sizeof(*infos)); return pf_infos.status; } EXPORT_SYMBOL(hinic_get_hw_pf_infos); int hinic_set_ip_check(void *hwdev, bool ip_check_ctl) { u32 val = 0; int ret; int i; if (!hwdev) return -EINVAL; if (hinic_func_type(hwdev) == TYPE_VF) return 0; for (i = 0; i <= HINIC_IPSU_CHANNEL_NUM; i++) { ret = hinic_api_csr_rd32(hwdev, HINIC_NODE_ID_IPSU, (HINIC_IPSU_CHANNEL0_ADDR + i * HINIC_IPSU_CHANNEL_OFFSET), &val); if (ret) return ret; val = be32_to_cpu(val); if (ip_check_ctl) val |= HINIC_IPSU_DIP_SIP_MASK; else val &= (~HINIC_IPSU_DIP_SIP_MASK); val = cpu_to_be32(val); ret = hinic_api_csr_wr32(hwdev, HINIC_NODE_ID_IPSU, (HINIC_IPSU_CHANNEL0_ADDR + i * HINIC_IPSU_CHANNEL_OFFSET), val); if (ret) return ret; } return 0; } int hinic_get_card_present_state(void *hwdev, bool *card_present_state) { u32 addr, attr1; if (!hwdev || !card_present_state) return -EINVAL; addr = HINIC_CSR_FUNC_ATTR1_ADDR; attr1 = hinic_hwif_read_reg(((struct hinic_hwdev *)hwdev)->hwif, addr); if (attr1 == HINIC_PCIE_LINK_DOWN) { sdk_warn(((struct hinic_hwdev *)hwdev)->dev_hdl, "Card is not present\n"); *card_present_state = (bool)0; } else { *card_present_state = (bool)1; } return 0; } EXPORT_SYMBOL(hinic_get_card_present_state); void hinic_disable_mgmt_msg_report(void *hwdev) { struct hinic_hwdev *hw_dev = (struct hinic_hwdev *)hwdev; hinic_set_pf_status(hw_dev->hwif, HINIC_PF_STATUS_INIT); } int hinic_set_vxlan_udp_dport(void *hwdev, u32 udp_port) { u32 val = 0; int ret; if (!hwdev) return -EINVAL; if (hinic_func_type(hwdev) == TYPE_VF) return 0; ret = hinic_api_csr_rd32(hwdev, HINIC_NODE_ID_IPSU, HINIC_IPSURX_VXLAN_DPORT_ADDR, &val); if (ret) return ret; nic_info(((struct hinic_hwdev *)hwdev)->dev_hdl, "Update VxLAN UDP dest port: cur port:%u, new port:%u", be32_to_cpu(val), udp_port); if (be32_to_cpu(val) == udp_port) return 0; udp_port = cpu_to_be32(udp_port); ret = hinic_api_csr_wr32(hwdev, HINIC_NODE_ID_IPSU, HINIC_IPSURX_VXLAN_DPORT_ADDR, udp_port); if (ret) return ret; return 0; } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_hwdev.h000066400000000000000000000256661502027332500216060ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_HWDEV_H_ #define HINIC_HWDEV_H_ #include "hinic_port_cmd.h" /* to use 0-level CLA, page size must be: 64B(wqebb) * 4096(max_q_depth) */ #define HINIC_DEFAULT_WQ_PAGE_SIZE 0x40000 #define HINIC_HW_WQ_PAGE_SIZE 0x1000 #define HINIC_MSG_TO_MGMT_MAX_LEN 2016 #define HINIC_MGMT_STATUS_ERR_OK 0 /* Ok */ #define HINIC_MGMT_STATUS_ERR_PARAM 1 /* Invalid parameter */ #define HINIC_MGMT_STATUS_ERR_FAILED 2 /* Operation failed */ #define HINIC_MGMT_STATUS_ERR_PORT 3 /* Invalid port */ #define HINIC_MGMT_STATUS_ERR_TIMEOUT 4 /* Operation time out */ #define HINIC_MGMT_STATUS_ERR_NOMATCH 5 /* Version not match */ #define HINIC_MGMT_STATUS_ERR_EXIST 6 /* Entry exists */ #define HINIC_MGMT_STATUS_ERR_NOMEM 7 /* Out of memory */ #define HINIC_MGMT_STATUS_ERR_INIT 8 /* Feature not initialized */ #define HINIC_MGMT_STATUS_ERR_FAULT 9 /* Invalid address */ #define HINIC_MGMT_STATUS_ERR_PERM 10 /* Operation not permitted */ #define HINIC_MGMT_STATUS_ERR_EMPTY 11 /* Table empty */ #define HINIC_MGMT_STATUS_ERR_FULL 12 /* Table full */ #define HINIC_MGMT_STATUS_ERR_NOT_FOUND 13 /* Not found */ #define HINIC_MGMT_STATUS_ERR_BUSY 14 /* Device or resource busy */ #define HINIC_MGMT_STATUS_ERR_RESOURCE 15 /* No resources for operation */ #define HINIC_MGMT_STATUS_ERR_CONFIG 16 /* Invalid configuration */ #define HINIC_MGMT_STATUS_ERR_UNAVAIL 17 /* Feature unavailable */ #define HINIC_MGMT_STATUS_ERR_CRC 18 /* CRC check failed */ #define HINIC_MGMT_STATUS_ERR_NXIO 19 /* No such device or address */ #define HINIC_MGMT_STATUS_ERR_ROLLBACK 20 /* Chip rollback fail */ #define HINIC_MGMT_STATUS_ERR_LEN 32 /* Length too short or too long */ #define HINIC_MGMT_STATUS_ERR_UNSUPPORT 0xFF /* Feature not supported */ #define HINIC_CHIP_PRESENT 1 #define HINIC_CHIP_ABSENT 0 struct cfg_mgmt_info; struct rdma_comp_resource; struct hinic_hwif; struct hinic_nic_io; struct hinic_wqs; struct hinic_aeqs; struct hinic_ceqs; struct hinic_mbox_func_to_func; struct hinic_msg_pf_to_mgmt; struct hinic_cmdqs; struct hinic_multi_host_mgmt; struct hinic_root_ctxt { u8 status; u8 version; u8 rsvd0[6]; u16 func_idx; u16 rsvd1; u8 set_cmdq_depth; u8 cmdq_depth; u8 lro_en; u8 rsvd2; u8 ppf_idx; u8 rsvd3; u16 rq_depth; u16 rx_buf_sz; u16 sq_depth; }; struct hinic_page_addr { void *virt_addr; u64 phys_addr; }; struct mqm_addr_trans_tbl_info { u32 chunk_num; u32 search_gpa_num; u32 page_size; u32 page_num; struct hinic_page_addr *brm_srch_page_addr; }; #define HINIC_PCIE_LINK_DOWN 0xFFFFFFFF #define HINIC_DEV_ACTIVE_FW_TIMEOUT (35 * 1000) #define HINIC_DEV_BUSY_ACTIVE_FW 0xFE #define HINIC_HW_WQ_NAME "hinic_hardware" #define HINIC_HEARTBEAT_PERIOD 1000 #define HINIC_HEARTBEAT_START_EXPIRE 5000 #define HINIC_CHIP_ERROR_TYPE_MAX 1024 #define HINIC_CHIP_FAULT_SIZE \ (HINIC_NODE_ID_MAX * FAULT_LEVEL_MAX * HINIC_CHIP_ERROR_TYPE_MAX) enum hinic_node_id { HINIC_NODE_ID_CPI = 0, HINIC_NODE_ID_IPSU = 4, HINIC_NODE_ID_MGMT_HOST = 21, /* Host CPU send API to uP */ HINIC_NODE_ID_MAX = 22 }; #define HINIC_HWDEV_INIT_MODES_MASK ((1UL << HINIC_HWDEV_ALL_INITED) - 1) enum hinic_hwdev_func_state { HINIC_HWDEV_FUNC_INITED = HINIC_HWDEV_ALL_INITED, HINIC_HWDEV_FUNC_DEINIT, HINIC_HWDEV_STATE_BUSY = 31, }; struct hinic_cqm_stats { atomic_t cqm_cmd_alloc_cnt; atomic_t cqm_cmd_free_cnt; atomic_t cqm_send_cmd_box_cnt; atomic_t cqm_send_cmd_imm_cnt; atomic_t cqm_db_addr_alloc_cnt; atomic_t cqm_db_addr_free_cnt; atomic_t cqm_fc_srq_create_cnt; atomic_t cqm_srq_create_cnt; atomic_t cqm_rq_create_cnt; atomic_t cqm_qpc_mpt_create_cnt; atomic_t cqm_nonrdma_queue_create_cnt; atomic_t cqm_rdma_queue_create_cnt; atomic_t cqm_rdma_table_create_cnt; atomic_t cqm_qpc_mpt_delete_cnt; atomic_t cqm_nonrdma_queue_delete_cnt; atomic_t cqm_rdma_queue_delete_cnt; atomic_t cqm_rdma_table_delete_cnt; atomic_t cqm_func_timer_clear_cnt; atomic_t cqm_func_hash_buf_clear_cnt; atomic_t cqm_scq_callback_cnt; atomic_t cqm_ecq_callback_cnt; atomic_t cqm_nocq_callback_cnt; atomic_t cqm_aeq_callback_cnt[112]; }; struct hinic_link_event_stats { atomic_t link_down_stats; atomic_t link_up_stats; }; struct hinic_fault_event_stats { atomic_t chip_fault_stats[HINIC_NODE_ID_MAX][FAULT_LEVEL_MAX]; atomic_t fault_type_stat[FAULT_TYPE_MAX]; atomic_t pcie_fault_stats; }; struct hinic_hw_stats { atomic_t heart_lost_stats; atomic_t nic_ucode_event_stats[HINIC_NIC_FATAL_ERROR_MAX]; struct hinic_cqm_stats cqm_stats; struct hinic_link_event_stats link_event_stats; struct hinic_fault_event_stats fault_event_stats; }; #define HINIC_NORMAL_HOST_CAP (HINIC_FUNC_MGMT | HINIC_FUNC_PORT | \ HINIC_FUNC_SUPP_RATE_LIMIT | \ HINIC_FUNC_SUPP_DFX_REG | \ HINIC_FUNC_SUPP_RX_MODE | \ HINIC_FUNC_SUPP_SET_VF_MAC_VLAN | \ HINIC_FUNC_SUPP_CHANGE_MAC | \ HINIC_FUNC_SUPP_ENCAP_TSO_CSUM) #define HINIC_MULTI_BM_MASTER (HINIC_FUNC_MGMT | HINIC_FUNC_PORT | \ HINIC_FUNC_SUPP_DFX_REG | \ HINIC_FUNC_SUPP_RX_MODE | \ HINIC_FUNC_SUPP_SET_VF_MAC_VLAN | \ HINIC_FUNC_SUPP_CHANGE_MAC) #define HINIC_MULTI_BM_SLAVE (HINIC_FUNC_SRIOV_EN_DFLT | \ HINIC_FUNC_SRIOV_NUM_FIX | \ HINIC_FUNC_FORCE_LINK_UP | \ HINIC_FUNC_OFFLOAD_OVS_UNSUPP) #define HINIC_MULTI_VM_MASTER (HINIC_FUNC_MGMT | HINIC_FUNC_PORT | \ HINIC_FUNC_SUPP_DFX_REG | \ HINIC_FUNC_SUPP_RX_MODE | \ HINIC_FUNC_SUPP_SET_VF_MAC_VLAN | \ HINIC_FUNC_SUPP_CHANGE_MAC) #define HINIC_MULTI_VM_SLAVE (HINIC_FUNC_MGMT | \ HINIC_FUNC_SUPP_DFX_REG | \ HINIC_FUNC_SRIOV_EN_DFLT | \ HINIC_FUNC_SUPP_RX_MODE | \ HINIC_FUNC_SUPP_CHANGE_MAC | \ HINIC_FUNC_OFFLOAD_OVS_UNSUPP) #define MULTI_HOST_CHIP_MODE_SHIFT 0 #define MULTI_HOST_MASTER_MBX_STS_SHIFT 0x4 #define MULTI_HOST_PRIV_DATA_SHIFT 0x8 #define MULTI_HOST_CHIP_MODE_MASK 0xF #define MULTI_HOST_MASTER_MBX_STS_MASK 0xF #define MULTI_HOST_PRIV_DATA_MASK 0xFFFF #define MULTI_HOST_REG_SET(val, member) \ (((val) & MULTI_HOST_##member##_MASK) \ << MULTI_HOST_##member##_SHIFT) #define MULTI_HOST_REG_GET(val, member) \ (((val) >> MULTI_HOST_##member##_SHIFT) \ & MULTI_HOST_##member##_MASK) #define MULTI_HOST_REG_CLEAR(val, member) \ ((val) & (~(MULTI_HOST_##member##_MASK \ << MULTI_HOST_##member##_SHIFT))) #define HINIC_BOARD_TYPE_MULTI_HOST_ETH_25GE 12 /* new version of roce qp not limited by power of 2 */ #define HINIC_CMD_VER_ROCE_QP 1 /* new version for add function id in multi-host */ #define HINIC_CMD_VER_FUNC_ID 2 struct hinic_hwdev { void *adapter_hdl; /* pointer to hinic_pcidev or NDIS_Adapter */ void *pcidev_hdl; /* pointer to pcidev or Handler */ void *dev_hdl; /* pointer to pcidev->dev or Handler, for * sdk_err() or dma_alloc() */ u32 wq_page_size; void *cqm_hdl; void *chip_node; struct hinic_hwif *hwif; /* include void __iomem *bar */ struct hinic_nic_io *nic_io; struct cfg_mgmt_info *cfg_mgmt; struct rdma_comp_resource *rdma_comp_res; struct hinic_wqs *wqs; /* for FC slq */ struct mqm_addr_trans_tbl_info mqm_att; struct hinic_aeqs *aeqs; struct hinic_ceqs *ceqs; struct hinic_mbox_func_to_func *func_to_func; struct hinic_msg_pf_to_mgmt *pf_to_mgmt; struct hinic_clp_pf_to_mgmt *clp_pf_to_mgmt; struct hinic_cmdqs *cmdqs; struct hinic_page_addr page_pa0; struct hinic_page_addr page_pa1; hinic_event_handler event_callback; void *event_pri_handle; struct work_struct timer_work; struct workqueue_struct *workq; struct timer_list heartbeat_timer; /* true represent heartbeat lost, false represent heartbeat restore */ u32 heartbeat_lost; int chip_present_flag; struct hinic_hw_stats hw_stats; u8 *chip_fault_stats; u32 statufull_ref_cnt; ulong func_state; u64 feature_cap; /* enum hinic_func_cap */ enum hinic_func_mode func_mode; struct hinic_multi_host_mgmt *mhost_mgmt; /* In bmgw x86 host, driver can't send message to mgmt cpu directly, * need to trasmit message ppf mbox to bmgw arm host. */ struct semaphore ppf_sem; void *ppf_hwdev; struct semaphore func_sem; int func_ref; struct hinic_board_info board_info; #define MGMT_VERSION_MAX_LEN 32 u8 mgmt_ver[MGMT_VERSION_MAX_LEN]; u64 fw_support_func_flag; }; int hinic_init_comm_ch(struct hinic_hwdev *hwdev); void hinic_uninit_comm_ch(struct hinic_hwdev *hwdev); int hinic_ppf_ext_db_init(void *dev); int hinic_ppf_ext_db_deinit(void *dev); enum hinic_set_arm_type { HINIC_SET_ARM_CMDQ, HINIC_SET_ARM_SQ, HINIC_SET_ARM_TYPE_NUM, }; int hinic_set_arm_bit(void *hwdev, enum hinic_set_arm_type q_type, u16 q_id); void hinic_set_chip_present(void *hwdev); void hinic_force_complete_all(void *hwdev); void hinic_init_heartbeat(struct hinic_hwdev *hwdev); void hinic_destroy_heartbeat(struct hinic_hwdev *hwdev); u8 hinic_nic_sw_aeqe_handler(void *handle, u8 event, u64 data); int hinic_enable_fast_recycle(void *hwdev, bool enable); int hinic_l2nic_reset_base(struct hinic_hwdev *hwdev, u16 reset_flag); enum l2nic_resource_type { RES_TYPE_NIC_FUNC = 0, RES_TYPE_FLUSH_BIT, RES_TYPE_PF_BW_CFG, RES_TYPE_MQM, RES_TYPE_SMF, RES_TYPE_CMDQ_ROOTCTX, RES_TYPE_SQ_CI_TABLE, RES_TYPE_CEQ, RES_TYPE_MBOX, RES_TYPE_AEQ, }; void hinic_notify_dcb_state_event(struct hinic_hwdev *hwdev, struct hinic_dcb_state *dcb_state); int hinic_pf_msg_to_mgmt_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int hinic_pf_send_clp_cmd(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); int hinic_get_bios_pf_bw_limit(void *hwdev, u32 *pf_bw_limit); bool hinic_mgmt_event_ack_first(u8 mod, u8 cmd); int hinic_set_wq_page_size(struct hinic_hwdev *hwdev, u16 func_idx, u32 page_size); int hinic_phy_init_status_judge(void *hwdev); int hinic_hilink_info_show(struct hinic_hwdev *hwdev); int hinic_api_csr_rd32(void *hwdev, u8 dest, u32 addr, u32 *val); int hinic_api_csr_wr32(void *hwdev, u8 dest, u32 addr, u32 val); int hinic_ppf_process_mbox_msg(struct hinic_hwdev *hwdev, u16 pf_idx, u16 vf_id, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); #define HINIC_SDI_MODE_UNKNOWN 0 #define HINIC_SDI_MODE_BM 1 #define HINIC_SDI_MODE_VM 2 #define HINIC_SDI_MODE_MAX 3 int hinic_get_sdi_mode(struct hinic_hwdev *hwdev, u16 *cur_mode); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_hwif.c000066400000000000000000000536431502027332500214150ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwdev.h" #include "hinic_csr.h" #include "hinic_hwif.h" #include "hinic_eqs.h" #define WAIT_HWIF_READY_TIMEOUT 10000 #define HINIC_SELFTEST_RESULT 0x883C /* For UEFI driver, this function can only read BAR0 */ u32 hinic_hwif_read_reg(struct hinic_hwif *hwif, u32 reg) { return be32_to_cpu(readl(hwif->cfg_regs_base + reg)); } /* For UEFI driver, this function can only write BAR0 */ void hinic_hwif_write_reg(struct hinic_hwif *hwif, u32 reg, u32 val) { writel(cpu_to_be32(val), hwif->cfg_regs_base + reg); } /** * hwif_ready - test if the HW initialization passed * @hwdev: the pointer to hw device * Return: 0 - success, negative - failure */ static int hwif_ready(struct hinic_hwdev *hwdev) { u32 addr, attr1; addr = HINIC_CSR_FUNC_ATTR1_ADDR; attr1 = hinic_hwif_read_reg(hwdev->hwif, addr); if (attr1 == HINIC_PCIE_LINK_DOWN) return -EBUSY; if (!HINIC_AF1_GET(attr1, MGMT_INIT_STATUS)) return -EBUSY; if (HINIC_IS_VF(hwdev)) { if (!HINIC_AF1_GET(attr1, PF_INIT_STATUS)) return -EBUSY; } return 0; } static int wait_hwif_ready(struct hinic_hwdev *hwdev) { ulong timeout = 0; do { if (!hwif_ready(hwdev)) return 0; usleep_range(999, 1000); timeout++; } while (timeout <= WAIT_HWIF_READY_TIMEOUT); sdk_err(hwdev->dev_hdl, "Wait for hwif timeout\n"); return -EBUSY; } /** * set_hwif_attr - set the attributes as members in hwif * @hwif: the hardware interface of a pci function device * @attr0: the first attribute that was read from the hw * @attr1: the second attribute that was read from the hw * @attr2: the third attribute that was read from the hw */ static void set_hwif_attr(struct hinic_hwif *hwif, u32 attr0, u32 attr1, u32 attr2) { hwif->attr.func_global_idx = HINIC_AF0_GET(attr0, FUNC_GLOBAL_IDX); hwif->attr.port_to_port_idx = HINIC_AF0_GET(attr0, P2P_IDX); hwif->attr.pci_intf_idx = HINIC_AF0_GET(attr0, PCI_INTF_IDX); hwif->attr.vf_in_pf = HINIC_AF0_GET(attr0, VF_IN_PF); hwif->attr.func_type = HINIC_AF0_GET(attr0, FUNC_TYPE); hwif->attr.ppf_idx = HINIC_AF1_GET(attr1, PPF_IDX); hwif->attr.num_aeqs = BIT(HINIC_AF1_GET(attr1, AEQS_PER_FUNC)); hwif->attr.num_ceqs = BIT(HINIC_AF1_GET(attr1, CEQS_PER_FUNC)); hwif->attr.num_irqs = BIT(HINIC_AF1_GET(attr1, IRQS_PER_FUNC)); hwif->attr.num_dma_attr = BIT(HINIC_AF1_GET(attr1, DMA_ATTR_PER_FUNC)); hwif->attr.global_vf_id_of_pf = HINIC_AF2_GET(attr2, GLOBAL_VF_ID_OF_PF); } /** * get_hwif_attr - read and set the attributes as members in hwif * @hwif: the hardware interface of a pci function device */ static void get_hwif_attr(struct hinic_hwif *hwif) { u32 addr, attr0, attr1, attr2; addr = HINIC_CSR_FUNC_ATTR0_ADDR; attr0 = hinic_hwif_read_reg(hwif, addr); addr = HINIC_CSR_FUNC_ATTR1_ADDR; attr1 = hinic_hwif_read_reg(hwif, addr); addr = HINIC_CSR_FUNC_ATTR2_ADDR; attr2 = hinic_hwif_read_reg(hwif, addr); set_hwif_attr(hwif, attr0, attr1, attr2); } void hinic_set_pf_status(struct hinic_hwif *hwif, enum hinic_pf_status status) { u32 attr5 = HINIC_AF5_SET(status, PF_STATUS); u32 addr = HINIC_CSR_FUNC_ATTR5_ADDR; if (hwif->attr.func_type == TYPE_VF) return; hinic_hwif_write_reg(hwif, addr, attr5); } enum hinic_pf_status hinic_get_pf_status(struct hinic_hwif *hwif) { u32 attr5 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR5_ADDR); return HINIC_AF5_GET(attr5, PF_STATUS); } enum hinic_doorbell_ctrl hinic_get_doorbell_ctrl_status(struct hinic_hwif *hwif) { u32 attr4 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR); return HINIC_AF4_GET(attr4, DOORBELL_CTRL); } enum hinic_outbound_ctrl hinic_get_outbound_ctrl_status(struct hinic_hwif *hwif) { u32 attr4 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR); return HINIC_AF4_GET(attr4, OUTBOUND_CTRL); } void hinic_enable_doorbell(struct hinic_hwif *hwif) { u32 addr, attr4; addr = HINIC_CSR_FUNC_ATTR4_ADDR; attr4 = hinic_hwif_read_reg(hwif, addr); attr4 = HINIC_AF4_CLEAR(attr4, DOORBELL_CTRL); attr4 |= HINIC_AF4_SET(ENABLE_DOORBELL, DOORBELL_CTRL); hinic_hwif_write_reg(hwif, addr, attr4); } void hinic_disable_doorbell(struct hinic_hwif *hwif) { u32 addr, attr4; addr = HINIC_CSR_FUNC_ATTR4_ADDR; attr4 = hinic_hwif_read_reg(hwif, addr); attr4 = HINIC_AF4_CLEAR(attr4, DOORBELL_CTRL); attr4 |= HINIC_AF4_SET(DISABLE_DOORBELL, DOORBELL_CTRL); hinic_hwif_write_reg(hwif, addr, attr4); } void hinic_enable_outbound(struct hinic_hwif *hwif) { u32 addr, attr4; addr = HINIC_CSR_FUNC_ATTR4_ADDR; attr4 = hinic_hwif_read_reg(hwif, addr); attr4 = HINIC_AF4_CLEAR(attr4, OUTBOUND_CTRL); attr4 |= HINIC_AF4_SET(ENABLE_OUTBOUND, OUTBOUND_CTRL); hinic_hwif_write_reg(hwif, addr, attr4); } void hinic_disable_outbound(struct hinic_hwif *hwif) { u32 addr, attr4; addr = HINIC_CSR_FUNC_ATTR4_ADDR; attr4 = hinic_hwif_read_reg(hwif, addr); attr4 = HINIC_AF4_CLEAR(attr4, OUTBOUND_CTRL); attr4 |= HINIC_AF4_SET(DISABLE_OUTBOUND, OUTBOUND_CTRL); hinic_hwif_write_reg(hwif, addr, attr4); } /** * set_ppf - try to set hwif as ppf and set the type of hwif in this case * @hwif: the hardware interface of a pci function device */ static void set_ppf(struct hinic_hwif *hwif) { struct hinic_func_attr *attr = &hwif->attr; u32 addr, val, ppf_election; /* Read Modify Write */ addr = HINIC_CSR_PPF_ELECTION_ADDR; val = hinic_hwif_read_reg(hwif, addr); val = HINIC_PPF_ELECTION_CLEAR(val, IDX); ppf_election = HINIC_PPF_ELECTION_SET(attr->func_global_idx, IDX); val |= ppf_election; hinic_hwif_write_reg(hwif, addr, val); /* Check PPF */ val = hinic_hwif_read_reg(hwif, addr); attr->ppf_idx = HINIC_PPF_ELECTION_GET(val, IDX); if (attr->ppf_idx == attr->func_global_idx) attr->func_type = TYPE_PPF; } /** * get_mpf - get the mpf index into the hwif * @hwif: the hardware interface of a pci function device */ static void get_mpf(struct hinic_hwif *hwif) { struct hinic_func_attr *attr = &hwif->attr; u32 mpf_election, addr; addr = HINIC_CSR_GLOBAL_MPF_ELECTION_ADDR; mpf_election = hinic_hwif_read_reg(hwif, addr); attr->mpf_idx = HINIC_MPF_ELECTION_GET(mpf_election, IDX); } /** * set_mpf - try to set hwif as mpf and set the mpf idx in hwif * @hwif: the hardware interface of a pci function device */ static void set_mpf(struct hinic_hwif *hwif) { struct hinic_func_attr *attr = &hwif->attr; u32 addr, val, mpf_election; /* Read Modify Write */ addr = HINIC_CSR_GLOBAL_MPF_ELECTION_ADDR; val = hinic_hwif_read_reg(hwif, addr); val = HINIC_MPF_ELECTION_CLEAR(val, IDX); mpf_election = HINIC_MPF_ELECTION_SET(attr->func_global_idx, IDX); val |= mpf_election; hinic_hwif_write_reg(hwif, addr, val); } static void init_db_area_idx(struct hinic_hwif *hwif) { struct hinic_free_db_area *free_db_area; u32 db_max_areas; u32 i; free_db_area = &hwif->free_db_area; db_max_areas = hwif->db_size / HINIC_DB_PAGE_SIZE; for (i = 0; i < db_max_areas; i++) free_db_area->db_idx[i] = i; free_db_area->num_free = db_max_areas; spin_lock_init(&free_db_area->idx_lock); } static int get_db_idx(struct hinic_hwif *hwif, u32 *idx) { struct hinic_free_db_area *free_db_area = &hwif->free_db_area; u32 db_max_areas = hwif->db_size / HINIC_DB_PAGE_SIZE; u32 pos; u32 pg_idx; spin_lock(&free_db_area->idx_lock); retry: if (free_db_area->num_free == 0) { spin_unlock(&free_db_area->idx_lock); return -ENOMEM; } free_db_area->num_free--; pos = free_db_area->alloc_pos++; pos &= db_max_areas - 1; pg_idx = free_db_area->db_idx[pos]; free_db_area->db_idx[pos] = 0xFFFFFFFF; /* pg_idx out of range */ if (pg_idx >= db_max_areas) goto retry; spin_unlock(&free_db_area->idx_lock); *idx = pg_idx; return 0; } static void free_db_idx(struct hinic_hwif *hwif, u32 idx) { struct hinic_free_db_area *free_db_area = &hwif->free_db_area; u32 db_max_areas = hwif->db_size / HINIC_DB_PAGE_SIZE; u32 pos; if (idx >= db_max_areas) return; spin_lock(&free_db_area->idx_lock); pos = free_db_area->return_pos++; pos &= db_max_areas - 1; free_db_area->db_idx[pos] = idx; free_db_area->num_free++; spin_unlock(&free_db_area->idx_lock); } void hinic_free_db_addr(void *hwdev, void __iomem *db_base, void __iomem *dwqe_base) { struct hinic_hwif *hwif; u32 idx; if (!hwdev || !db_base) return; hwif = ((struct hinic_hwdev *)hwdev)->hwif; idx = DB_IDX(db_base, hwif->db_base); #if defined(__aarch64__) /* No need to unmap */ #else if (dwqe_base && hwif->chip_mode == CHIP_MODE_NORMAL) io_mapping_unmap(dwqe_base); #endif free_db_idx(hwif, idx); } EXPORT_SYMBOL(hinic_free_db_addr); int hinic_alloc_db_addr(void *hwdev, void __iomem **db_base, void __iomem **dwqe_base) { struct hinic_hwif *hwif; u64 offset; u32 idx; int err; if (!hwdev || !db_base) return -EINVAL; hwif = ((struct hinic_hwdev *)hwdev)->hwif; err = get_db_idx(hwif, &idx); if (err) return -EFAULT; *db_base = hwif->db_base + idx * HINIC_DB_PAGE_SIZE; if (!dwqe_base || hwif->chip_mode != CHIP_MODE_NORMAL) return 0; offset = ((u64)idx) << PAGE_SHIFT; #if defined(__aarch64__) *dwqe_base = hwif->dwqe_mapping + offset; #else *dwqe_base = io_mapping_map_wc(hwif->dwqe_mapping, offset, HINIC_DB_PAGE_SIZE); #endif if (!(*dwqe_base)) { hinic_free_db_addr(hwdev, *db_base, NULL); return -EFAULT; } return 0; } EXPORT_SYMBOL(hinic_alloc_db_addr); void hinic_free_db_phy_addr(void *hwdev, u64 db_base, u64 dwqe_base) { struct hinic_hwif *hwif; u32 idx; if (!hwdev) return; hwif = ((struct hinic_hwdev *)hwdev)->hwif; idx = DB_IDX(db_base, hwif->db_base_phy); free_db_idx(hwif, idx); } EXPORT_SYMBOL(hinic_free_db_phy_addr); int hinic_alloc_db_phy_addr(void *hwdev, u64 *db_base, u64 *dwqe_base) { struct hinic_hwif *hwif; u32 idx; int err; if (!hwdev || !db_base || !dwqe_base) return -EINVAL; hwif = ((struct hinic_hwdev *)hwdev)->hwif; err = get_db_idx(hwif, &idx); if (err) return -EFAULT; *db_base = hwif->db_base_phy + idx * HINIC_DB_PAGE_SIZE; if (hwif->chip_mode == CHIP_MODE_NORMAL) *dwqe_base = *db_base + HINIC_DB_DWQE_SIZE; return 0; } EXPORT_SYMBOL(hinic_alloc_db_phy_addr); enum hinic_msix_state hinic_get_msix_state(void *hwdev, u16 msix_idx) { struct hinic_hwif *hwif = NULL; u32 offset = msix_idx * HINIC_PCI_MSIX_ENTRY_SIZE + HINIC_PCI_MSIX_ENTRY_VECTOR_CTRL; u32 mask_bits; if (!hwdev) return HINIC_MSIX_DISABLE; hwif = ((struct hinic_hwdev *)hwdev)->hwif; mask_bits = readl(hwif->intr_regs_base + offset); return !!(mask_bits & HINIC_PCI_MSIX_ENTRY_CTRL_MASKBIT); } void hinic_set_msix_state(void *hwdev, u16 msix_idx, enum hinic_msix_state flag) { struct hinic_hwif *hwif; u32 offset = msix_idx * HINIC_PCI_MSIX_ENTRY_SIZE + HINIC_PCI_MSIX_ENTRY_VECTOR_CTRL; u32 mask_bits; if (!hwdev) return; hwif = ((struct hinic_hwdev *)hwdev)->hwif; mask_bits = readl(hwif->intr_regs_base + offset); mask_bits &= ~HINIC_PCI_MSIX_ENTRY_CTRL_MASKBIT; if (flag) mask_bits |= HINIC_PCI_MSIX_ENTRY_CTRL_MASKBIT; writel(mask_bits, hwif->intr_regs_base + offset); } EXPORT_SYMBOL(hinic_set_msix_state); static void disable_all_msix(struct hinic_hwdev *hwdev) { u16 num_irqs = hwdev->hwif->attr.num_irqs; u16 i; for (i = 0; i < num_irqs; i++) hinic_set_msix_state(hwdev, i, HINIC_MSIX_DISABLE); } int wait_until_doorbell_flush_states(struct hinic_hwif *hwif, enum hinic_doorbell_ctrl states) { enum hinic_doorbell_ctrl db_ctrl; u32 cnt = 0; if (!hwif) return -EFAULT; while (cnt < HINIC_WAIT_DOORBELL_AND_OUTBOUND_TIMEOUT) { db_ctrl = hinic_get_doorbell_ctrl_status(hwif); if (db_ctrl == states) return 0; usleep_range(900, 1000); cnt++; } return -EFAULT; } EXPORT_SYMBOL(wait_until_doorbell_flush_states); static int wait_until_doorbell_and_outbound_enabled(struct hinic_hwif *hwif) { enum hinic_doorbell_ctrl db_ctrl; enum hinic_outbound_ctrl outbound_ctrl; u32 cnt = 0; while (cnt < HINIC_WAIT_DOORBELL_AND_OUTBOUND_TIMEOUT) { db_ctrl = hinic_get_doorbell_ctrl_status(hwif); outbound_ctrl = hinic_get_outbound_ctrl_status(hwif); if (outbound_ctrl == ENABLE_OUTBOUND && db_ctrl == ENABLE_DOORBELL) return 0; usleep_range(900, 1000); cnt++; } return -EFAULT; } static void __print_selftest_reg(struct hinic_hwdev *hwdev) { u32 addr, attr0, attr1; addr = HINIC_CSR_FUNC_ATTR1_ADDR; attr1 = hinic_hwif_read_reg(hwdev->hwif, addr); if (attr1 == HINIC_PCIE_LINK_DOWN) { sdk_err(hwdev->dev_hdl, "PCIE is link down\n"); return; } addr = HINIC_CSR_FUNC_ATTR0_ADDR; attr0 = hinic_hwif_read_reg(hwdev->hwif, addr); if (HINIC_AF0_GET(attr0, FUNC_TYPE) != TYPE_VF && !HINIC_AF0_GET(attr0, PCI_INTF_IDX)) sdk_err(hwdev->dev_hdl, "Selftest reg: 0x%08x\n", hinic_hwif_read_reg(hwdev->hwif, HINIC_SELFTEST_RESULT)); } /** * hinic_init_hwif - initialize the hw interface * @hwdev: the pointer to hw device * @cfg_reg_base: configuration base address * Return: 0 - success, negative - failure */ int hinic_init_hwif(struct hinic_hwdev *hwdev, void *cfg_reg_base, void *intr_reg_base, u64 db_base_phy, void *db_base, void *dwqe_mapping) { struct hinic_hwif *hwif; int err; hwif = kzalloc(sizeof(*hwif), GFP_KERNEL); if (!hwif) return -ENOMEM; hwdev->hwif = hwif; hwif->pdev = hwdev->pcidev_hdl; hwif->cfg_regs_base = cfg_reg_base; hwif->intr_regs_base = intr_reg_base; hwif->db_base_phy = db_base_phy; hwif->db_base = db_base; hwif->dwqe_mapping = dwqe_mapping; hwif->db_size = hinic_get_db_size(cfg_reg_base, &hwif->chip_mode); sdk_info(hwdev->dev_hdl, "Doorbell size: 0x%x, chip mode: %d\n", hwif->db_size, hwif->chip_mode); init_db_area_idx(hwif); err = wait_hwif_ready(hwdev); if (err) { sdk_err(hwdev->dev_hdl, "Chip status is not ready\n"); __print_selftest_reg(hwdev); goto hwif_ready_err; } get_hwif_attr(hwif); err = wait_until_doorbell_and_outbound_enabled(hwif); if (err) { sdk_err(hwdev->dev_hdl, "Hw doorbell/outbound is disabled\n"); goto hwif_ready_err; } if (!HINIC_IS_VF(hwdev)) { set_ppf(hwif); if (HINIC_IS_PPF(hwdev)) set_mpf(hwif); get_mpf(hwif); } disable_all_msix(hwdev); /* disable mgmt cpu report any event */ hinic_set_pf_status(hwdev->hwif, HINIC_PF_STATUS_INIT); sdk_info(hwdev->dev_hdl, "global_func_idx: %d, func_type: %d, host_id: %d, ppf: %d, mpf: %d\n", hwif->attr.func_global_idx, hwif->attr.func_type, hwif->attr.pci_intf_idx, hwif->attr.ppf_idx, hwif->attr.mpf_idx); return 0; hwif_ready_err: kfree(hwif); return err; } /** * hinic_free_hwif - free the hw interface * @hwdev: the pointer to hw device */ void hinic_free_hwif(struct hinic_hwdev *hwdev) { kfree(hwdev->hwif); } int hinic_dma_alloc_coherent_align(void *dev_hdl, u64 size, u64 align, unsigned int flag, struct hinic_dma_addr_align *mem_align) { void *vaddr, *align_vaddr; dma_addr_t paddr, align_paddr; u64 real_size = size; vaddr = dma_alloc_coherent(dev_hdl, real_size, &paddr, flag); if (!vaddr) return -ENOMEM; align_paddr = ALIGN(paddr, align); /* align */ if (align_paddr == paddr) { align_vaddr = vaddr; goto out; } dma_free_coherent(dev_hdl, real_size, vaddr, paddr); /* realloc memory for align */ real_size = size + align; vaddr = dma_alloc_coherent(dev_hdl, real_size, &paddr, flag); if (!vaddr) return -ENOMEM; align_paddr = ALIGN(paddr, align); align_vaddr = (void *)((u64)vaddr + (align_paddr - paddr)); out: mem_align->real_size = (u32)real_size; mem_align->ori_vaddr = vaddr; mem_align->ori_paddr = paddr; mem_align->align_vaddr = align_vaddr; mem_align->align_paddr = align_paddr; return 0; } void hinic_dma_free_coherent_align(void *dev_hdl, struct hinic_dma_addr_align *mem_align) { dma_free_coherent(dev_hdl, mem_align->real_size, mem_align->ori_vaddr, mem_align->ori_paddr); } u16 hinic_global_func_id(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.func_global_idx; } EXPORT_SYMBOL(hinic_global_func_id); /** * get function id from register,used by sriov hot migration process * @hwdev: the pointer to hw device */ u16 hinic_global_func_id_hw(void *hwdev) { u32 addr, attr0; struct hinic_hwdev *dev; dev = (struct hinic_hwdev *)hwdev; addr = HINIC_CSR_FUNC_ATTR0_ADDR; attr0 = hinic_hwif_read_reg(dev->hwif, addr); return HINIC_AF0_GET(attr0, FUNC_GLOBAL_IDX); } static int func_busy_state_check(struct hinic_hwdev *hwdev) { u32 func_state; int cycle; /* set BUSY before src vm suspend and clear it before dst vm resume */ cycle = PIPE_CYCLE_MAX; func_state = hinic_func_busy_state_get(hwdev); while (func_state && cycle) { msleep(20); cycle--; if (!cycle) { sdk_err(hwdev->dev_hdl, "busy_state suspend timeout"); return -ETIMEDOUT; } func_state = hinic_func_busy_state_get(hwdev); } return 0; } int hinic_func_own_get(void *hwdev) { struct hinic_hwdev *dev = (struct hinic_hwdev *)hwdev; u32 func_state; int err; if (!HINIC_IS_VF(dev)) return 0; restart: down(&dev->func_sem); dev->func_ref++; hinic_func_own_bit_set(dev, 1); func_state = hinic_func_busy_state_get(hwdev); if (func_state) { dev->func_ref--; if (dev->func_ref == 0) hinic_func_own_bit_set(dev, 0); up(&dev->func_sem); err = func_busy_state_check(dev); if (err) return err; goto restart; } up(&dev->func_sem); return 0; } void hinic_func_own_free(void *hwdev) { struct hinic_hwdev *dev = (struct hinic_hwdev *)hwdev; if (!HINIC_IS_VF(dev)) return; down(&dev->func_sem); dev->func_ref--; if (dev->func_ref == 0) hinic_func_own_bit_set(dev, 0); up(&dev->func_sem); } /** * get function id, used by sriov hot migratition process. * @hwdev: the pointer to hw device * @func_id: function id */ int hinic_global_func_id_get(void *hwdev, u16 *func_id) { struct hinic_hwdev *dev = (struct hinic_hwdev *)hwdev; int err; /* only vf get func_id from chip reg for sriov migrate */ if (!HINIC_IS_VF(dev)) { *func_id = hinic_global_func_id(hwdev); return 0; } err = func_busy_state_check(dev); if (err) return err; *func_id = hinic_global_func_id_hw(dev); return 0; } u16 hinic_intr_num(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.num_irqs; } EXPORT_SYMBOL(hinic_intr_num); u8 hinic_pf_id_of_vf(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.port_to_port_idx; } EXPORT_SYMBOL(hinic_pf_id_of_vf); u16 hinic_pf_id_of_vf_hw(void *hwdev) { u32 addr, attr0; struct hinic_hwdev *dev; dev = (struct hinic_hwdev *)hwdev; addr = HINIC_CSR_FUNC_ATTR0_ADDR; attr0 = hinic_hwif_read_reg(dev->hwif, addr); return HINIC_AF0_GET(attr0, P2P_IDX); } u8 hinic_pcie_itf_id(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.pci_intf_idx; } EXPORT_SYMBOL(hinic_pcie_itf_id); u8 hinic_vf_in_pf(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.vf_in_pf; } EXPORT_SYMBOL(hinic_vf_in_pf); enum func_type hinic_func_type(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.func_type; } EXPORT_SYMBOL(hinic_func_type); u8 hinic_ceq_num(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.num_ceqs; } EXPORT_SYMBOL(hinic_ceq_num); u8 hinic_dma_attr_entry_num(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.num_dma_attr; } EXPORT_SYMBOL(hinic_dma_attr_entry_num); u16 hinic_glb_pf_vf_offset(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.global_vf_id_of_pf; } EXPORT_SYMBOL(hinic_glb_pf_vf_offset); u8 hinic_mpf_idx(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.mpf_idx; } EXPORT_SYMBOL(hinic_mpf_idx); u8 hinic_ppf_idx(void *hwdev) { struct hinic_hwif *hwif; if (!hwdev) return 0; hwif = ((struct hinic_hwdev *)hwdev)->hwif; return hwif->attr.ppf_idx; } EXPORT_SYMBOL(hinic_ppf_idx); #define CEQ_CTRL_0_CHIP_MODE_SHIFT 26 #define CEQ_CTRL_0_CHIP_MODE_MASK 0xFU #define CEQ_CTRL_0_GET(val, member) \ (((val) >> CEQ_CTRL_0_##member##_SHIFT) & \ CEQ_CTRL_0_##member##_MASK) /** * hinic_get_db_size - get db size ceq ctrl: bit26~29: uP write vf mode is * normal(0x0), bmgw(0x1) or vmgw(0x2) and normal mode db size is 512k, * bmgw or vmgw mode db size is 256k * @cfg_reg_base: pointer to cfg_reg_base * @chip_mode: pointer to chip_mode */ u32 hinic_get_db_size(void *cfg_reg_base, enum hinic_chip_mode *chip_mode) { u32 attr0, ctrl0; attr0 = be32_to_cpu(readl((u8 __iomem *)cfg_reg_base + HINIC_CSR_FUNC_ATTR0_ADDR)); /* PF is always normal mode & db size is 512K */ if (HINIC_AF0_GET(attr0, FUNC_TYPE) != TYPE_VF) { *chip_mode = CHIP_MODE_NORMAL; return HINIC_DB_DWQE_SIZE; } ctrl0 = be32_to_cpu(readl((u8 __iomem *)cfg_reg_base + HINIC_CSR_CEQ_CTRL_0_ADDR(0))); *chip_mode = CEQ_CTRL_0_GET(ctrl0, CHIP_MODE); switch (*chip_mode) { case CHIP_MODE_VMGW: case CHIP_MODE_BMGW: return HINIC_GW_VF_DB_SIZE; default: return HINIC_DB_DWQE_SIZE; } } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_hwif.h000066400000000000000000000056561502027332500214230ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_HWIF_H #define HINIC_HWIF_H #include "hinic_hwdev.h" #define HINIC_WAIT_DOORBELL_AND_OUTBOUND_TIMEOUT 60000 struct hinic_free_db_area { u32 db_idx[HINIC_DB_MAX_AREAS]; u32 num_free; u32 alloc_pos; u32 return_pos; /* spinlock for allocating doorbell area */ spinlock_t idx_lock; }; struct hinic_func_attr { u16 func_global_idx; u8 port_to_port_idx; u8 pci_intf_idx; u8 vf_in_pf; enum func_type func_type; u8 mpf_idx; u8 ppf_idx; u16 num_irqs; /* max: 2 ^ 15 */ u8 num_aeqs; /* max: 2 ^ 3 */ u8 num_ceqs; /* max: 2 ^ 7 */ u8 num_dma_attr; /* max: 2 ^ 6 */ u16 global_vf_id_of_pf; }; struct hinic_hwif { u8 __iomem *cfg_regs_base; u8 __iomem *intr_regs_base; u64 db_base_phy; u8 __iomem *db_base; #if defined(__aarch64__) void __iomem *dwqe_mapping; #else struct io_mapping *dwqe_mapping; #endif struct hinic_free_db_area free_db_area; struct hinic_func_attr attr; void *pdev; enum hinic_chip_mode chip_mode; u32 db_size; }; struct hinic_dma_addr_align { u32 real_size; void *ori_vaddr; dma_addr_t ori_paddr; void *align_vaddr; dma_addr_t align_paddr; }; u32 hinic_hwif_read_reg(struct hinic_hwif *hwif, u32 reg); void hinic_hwif_write_reg(struct hinic_hwif *hwif, u32 reg, u32 val); void hinic_set_pf_status(struct hinic_hwif *hwif, enum hinic_pf_status status); enum hinic_pf_status hinic_get_pf_status(struct hinic_hwif *hwif); enum hinic_doorbell_ctrl hinic_get_doorbell_ctrl_status(struct hinic_hwif *hwif); enum hinic_outbound_ctrl hinic_get_outbound_ctrl_status(struct hinic_hwif *hwif); void hinic_enable_doorbell(struct hinic_hwif *hwif); void hinic_disable_doorbell(struct hinic_hwif *hwif); void hinic_enable_outbound(struct hinic_hwif *hwif); void hinic_disable_outbound(struct hinic_hwif *hwif); int hinic_init_hwif(struct hinic_hwdev *hwdev, void *cfg_reg_base, void *intr_reg_base, u64 db_base_phy, void *db_base, void *dwqe_mapping); void hinic_free_hwif(struct hinic_hwdev *hwdev); int wait_until_doorbell_flush_states(struct hinic_hwif *hwif, enum hinic_doorbell_ctrl states); int hinic_dma_alloc_coherent_align(void *dev_hdl, u64 size, u64 align, unsigned int flag, struct hinic_dma_addr_align *mem_align); void hinic_dma_free_coherent_align(void *dev_hdl, struct hinic_dma_addr_align *mem_align); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_lld.c000066400000000000000000002135521502027332500212300ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw_mgmt.h" #include "hinic_hw.h" #include "hinic_lld.h" #include "hinic_pci_id_tbl.h" #include "hinic_nic_dev.h" #include "hinic_sriov.h" #include "hinic_dbgtool_knl.h" #include "hinic_nictool.h" #define HINIC_PCI_CFG_REG_BAR 0 #define HINIC_PCI_INTR_REG_BAR 2 #define HINIC_PCI_DB_BAR 4 #define HINIC_PCI_VENDOR_ID 0x19e5 #define SELF_TEST_BAR_ADDR_OFFSET 0x883c #define HINIC_SECOND_BASE 1000 #define HINIC_SYNC_YEAR_OFFSET 1900 #define HINIC_SYNC_MONTH_OFFSET 1 #define HINIC_MINUTE_BASE 60 #define HINIC_WAIT_TOOL_CNT_TIMEOUT 10000 #define HINIC_WAIT_SRIOV_CFG_TIMEOUT 15000 #define HINIC_DRV_DESC "Huawei(R) Intelligent Network Interface Card Driver" #define HINICVF_DRV_DESC "Huawei(R) Intelligent Virtual Function Network Driver" MODULE_AUTHOR("Huawei Technologies CO., Ltd"); MODULE_DESCRIPTION(HINIC_DRV_DESC); MODULE_VERSION(HINIC_DRV_VERSION); MODULE_LICENSE("GPL"); #ifdef CONFIG_PCI_IOV static bool disable_vf_load; module_param(disable_vf_load, bool, 0444); MODULE_PARM_DESC(disable_vf_load, "Disable virtual functions probe or not - default is false"); #endif /* CONFIG_PCI_IOV */ enum { HINIC_FUNC_IN_REMOVE = BIT(0), HINIC_FUNC_PRB_ERR = BIT(1), HINIC_FUNC_PRB_DELAY = BIT(2), }; /* Structure pcidev private */ struct hinic_pcidev { struct pci_dev *pcidev; void *hwdev; struct card_node *chip_node; struct hinic_lld_dev lld_dev; /* Record the service object address, * such as hinic_dev and toe_dev, fc_dev */ void *uld_dev[SERVICE_T_MAX]; /* Record the service object name */ char uld_dev_name[SERVICE_T_MAX][IFNAMSIZ]; /* It is a the global variable for driver to manage * all function device linked list */ struct list_head node; void __iomem *cfg_reg_base; void __iomem *intr_reg_base; u64 db_base_phy; void __iomem *db_base; #if defined(__aarch64__) void __iomem *dwqe_mapping; #else struct io_mapping *dwqe_mapping; #endif /* lock for attach/detach uld */ struct mutex pdev_mutex; struct hinic_sriov_info sriov_info; u32 init_state; /* setted when uld driver processing event */ unsigned long state; struct pci_device_id id; unsigned long flag; struct work_struct slave_nic_work; struct workqueue_struct *slave_nic_init_workq; struct delayed_work slave_nic_init_dwork; enum hinic_chip_mode chip_mode; bool nic_cur_enable; bool nic_des_enable; struct timer_list syncfw_time_timer; int card_id; }; #define HINIC_EVENT_PROCESS_TIMEOUT 10000 #define FIND_BIT(num, n) (((num) & (1UL << (n))) ? 1 : 0) #define SET_BIT(num, n) ((num) | (1UL << (n))) #define CLEAR_BIT(num, n) ((num) & (~(1UL << (n)))) #define MAX_CARD_ID 64 static u64 card_bit_map; LIST_HEAD(g_hinic_chip_list); struct hinic_uld_info g_uld_info[SERVICE_T_MAX]; static const char *s_uld_name[SERVICE_T_MAX] = { "nic", "ovs", "roce", "toe", "iwarp", "fc", "fcoe", "migrate"}; enum hinic_lld_status { HINIC_NODE_CHANGE = BIT(0), }; struct hinic_lld_lock { /* lock for chip list */ struct mutex lld_mutex; unsigned long status; atomic_t dev_ref_cnt; }; static struct hinic_lld_lock g_lld_lock; #define WAIT_LLD_DEV_HOLD_TIMEOUT (10 * 60 * 1000) /* 10minutes */ #define WAIT_LLD_DEV_NODE_CHANGED (10 * 60 * 1000) /* 10minutes */ #define WAIT_LLD_DEV_REF_CNT_EMPTY (2 * 60 * 1000) /* 2minutes */ /* node in chip_node will changed, tools or driver can't get node * during this situation */ static void lld_lock_chip_node(void) { u32 loop_cnt; mutex_lock(&g_lld_lock.lld_mutex); loop_cnt = 0; while (loop_cnt < WAIT_LLD_DEV_NODE_CHANGED) { if (!test_and_set_bit(HINIC_NODE_CHANGE, &g_lld_lock.status)) break; loop_cnt++; if (loop_cnt % 10000 == 0) pr_warn("Wait for lld node change complete for %us\n", loop_cnt / 1000); usleep_range(900, 1000); } if (loop_cnt == WAIT_LLD_DEV_NODE_CHANGED) pr_warn("Wait for lld node change complete timeout when try to get lld lock\n"); loop_cnt = 0; while (loop_cnt < WAIT_LLD_DEV_REF_CNT_EMPTY) { if (!atomic_read(&g_lld_lock.dev_ref_cnt)) break; loop_cnt++; if (loop_cnt % 10000 == 0) pr_warn("Wait for lld dev unused for %us, reference count: %d\n", loop_cnt / 1000, atomic_read(&g_lld_lock.dev_ref_cnt)); usleep_range(900, 1000); } if (loop_cnt == WAIT_LLD_DEV_REF_CNT_EMPTY) pr_warn("Wait for lld dev unused timeout\n"); mutex_unlock(&g_lld_lock.lld_mutex); } static void lld_unlock_chip_node(void) { clear_bit(HINIC_NODE_CHANGE, &g_lld_lock.status); } /* When tools or other drivers want to get node of chip_node, use this function * to prevent node be freed */ static void lld_dev_hold(void) { u32 loop_cnt = 0; /* ensure there have not any chip node in changing */ mutex_lock(&g_lld_lock.lld_mutex); while (loop_cnt < WAIT_LLD_DEV_HOLD_TIMEOUT) { if (!test_bit(HINIC_NODE_CHANGE, &g_lld_lock.status)) break; loop_cnt++; if (loop_cnt % 10000 == 0) pr_warn("Wait lld node change complete for %us\n", loop_cnt / 1000); usleep_range(900, 1000); } if (loop_cnt == WAIT_LLD_DEV_HOLD_TIMEOUT) pr_warn("Wait lld node change complete timeout when try to hode lld dev\n"); atomic_inc(&g_lld_lock.dev_ref_cnt); mutex_unlock(&g_lld_lock.lld_mutex); } static void lld_dev_put(void) { atomic_dec(&g_lld_lock.dev_ref_cnt); } static void hinic_lld_lock_init(void) { mutex_init(&g_lld_lock.lld_mutex); atomic_set(&g_lld_lock.dev_ref_cnt, 0); } static atomic_t tool_used_cnt; void hinic_tool_cnt_inc(void) { atomic_inc(&tool_used_cnt); } void hinic_tool_cnt_dec(void) { atomic_dec(&tool_used_cnt); } static int attach_uld(struct hinic_pcidev *dev, enum hinic_service_type type, struct hinic_uld_info *uld_info) { void *uld_dev = NULL; int err; mutex_lock(&dev->pdev_mutex); if (dev->init_state < HINIC_INIT_STATE_HWDEV_INITED) { sdk_err(&dev->pcidev->dev, "SDK init failed, can not attach uld\n"); err = -EFAULT; goto out_unlock; } if (dev->uld_dev[type]) { sdk_err(&dev->pcidev->dev, "%s driver has attached to pcie device\n", s_uld_name[type]); err = 0; goto out_unlock; } if ((hinic_get_func_mode(dev->hwdev) == FUNC_MOD_NORMAL_HOST) && type == SERVICE_T_OVS && !hinic_support_ovs(dev->hwdev, NULL)) { sdk_warn(&dev->pcidev->dev, "Dev not support %s\n", s_uld_name[type]); err = 0; goto out_unlock; } err = uld_info->probe(&dev->lld_dev, &uld_dev, dev->uld_dev_name[type]); if (err || !uld_dev) { sdk_err(&dev->pcidev->dev, "Failed to add object for %s driver to pcie device\n", s_uld_name[type]); goto probe_failed; } dev->uld_dev[type] = uld_dev; mutex_unlock(&dev->pdev_mutex); sdk_info(&dev->pcidev->dev, "Attach %s driver to pcie device succeed\n", s_uld_name[type]); return 0; probe_failed: out_unlock: mutex_unlock(&dev->pdev_mutex); return err; } static void detach_uld(struct hinic_pcidev *dev, enum hinic_service_type type) { struct hinic_uld_info *uld_info = &g_uld_info[type]; u32 cnt = 0; mutex_lock(&dev->pdev_mutex); if (!dev->uld_dev[type]) { mutex_unlock(&dev->pdev_mutex); return; } while (cnt < HINIC_EVENT_PROCESS_TIMEOUT) { if (!test_and_set_bit(type, &dev->state)) break; usleep_range(900, 1000); cnt++; } uld_info->remove(&dev->lld_dev, dev->uld_dev[type]); dev->uld_dev[type] = NULL; if (cnt < HINIC_EVENT_PROCESS_TIMEOUT) clear_bit(type, &dev->state); sdk_info(&dev->pcidev->dev, "Detach %s driver from pcie device succeed\n", s_uld_name[type]); mutex_unlock(&dev->pdev_mutex); } static void attach_ulds(struct hinic_pcidev *dev) { enum hinic_service_type type; for (type = SERVICE_T_OVS; type < SERVICE_T_MAX; type++) { if (g_uld_info[type].probe) attach_uld(dev, type, &g_uld_info[type]); } } static void detach_ulds(struct hinic_pcidev *dev) { enum hinic_service_type type; for (type = SERVICE_T_MAX - 1; type > SERVICE_T_NIC; type--) { if (g_uld_info[type].probe) detach_uld(dev, type); } } int hinic_register_uld(enum hinic_service_type type, struct hinic_uld_info *uld_info) { struct card_node *chip_node; struct hinic_pcidev *dev; if (type >= SERVICE_T_MAX) { pr_err("Unknown type %d of up layer driver to register\n", type); return -EINVAL; } if (!uld_info || !uld_info->probe || !uld_info->remove) { pr_err("Invalid information of %s driver to register\n", s_uld_name[type]); return -EINVAL; } lld_dev_hold(); if (g_uld_info[type].probe) { pr_err("%s driver has registered\n", s_uld_name[type]); lld_dev_put(); return -EINVAL; } memcpy(&g_uld_info[type], uld_info, sizeof(*uld_info)); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { list_for_each_entry(dev, &chip_node->func_list, node) { if (attach_uld(dev, type, uld_info)) { sdk_err(&dev->pcidev->dev, "Attach %s driver to pcie device failed\n", s_uld_name[type]); continue; } } } lld_dev_put(); pr_info("Register %s driver succeed\n", s_uld_name[type]); return 0; } EXPORT_SYMBOL(hinic_register_uld); void hinic_unregister_uld(enum hinic_service_type type) { struct card_node *chip_node; struct hinic_pcidev *dev; struct hinic_uld_info *uld_info; if (type >= SERVICE_T_MAX) { pr_err("Unknown type %d of up layer driver to unregister\n", type); return; } lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { list_for_each_entry(dev, &chip_node->func_list, node) { detach_uld(dev, type); } } uld_info = &g_uld_info[type]; memset(uld_info, 0, sizeof(*uld_info)); lld_dev_put(); } EXPORT_SYMBOL(hinic_unregister_uld); #define HINIC_SYNFW_TIME_PERIOD (60 * 60 * 1000) static void hinic_syncfw_timer_handler(struct timer_list *t) { struct hinic_pcidev *pci_adapter = from_timer(pci_adapter, t, syncfw_time_timer); u64 tv_msec; tv_msec = ktime_to_ms(ktime_get_real()); hinic_sync_time_async(pci_adapter->hwdev, tv_msec); mod_timer(&pci_adapter->syncfw_time_timer, jiffies + msecs_to_jiffies(HINIC_SYNFW_TIME_PERIOD)); } void hinic_init_syncfw_timer(struct hinic_pcidev *pci_adapter) { if (hinic_get_func_mode(pci_adapter->hwdev) != FUNC_MOD_NORMAL_HOST || hinic_func_type(pci_adapter->hwdev) != TYPE_PPF) return; timer_setup(&pci_adapter->syncfw_time_timer, hinic_syncfw_timer_handler, 0); pci_adapter->syncfw_time_timer.expires = jiffies + msecs_to_jiffies(HINIC_SYNFW_TIME_PERIOD); add_timer(&pci_adapter->syncfw_time_timer); } void hinic_destroy_syncfw_timer(struct hinic_pcidev *pci_adapter) { if (hinic_get_func_mode(pci_adapter->hwdev) != FUNC_MOD_NORMAL_HOST || hinic_func_type(pci_adapter->hwdev) != TYPE_PPF) return; del_timer_sync(&pci_adapter->syncfw_time_timer); } static void hinic_sync_time_to_fmw(struct hinic_pcidev *pdev_pri) { struct tm tm = {0}; u64 tv_msec; int err; tv_msec = ktime_to_ms(ktime_get_real()); err = hinic_sync_time(pdev_pri->hwdev, tv_msec); if (err) { sdk_err(&pdev_pri->pcidev->dev, "Synchronize UTC time to firmware failed, errno:%d.\n", err); } else { time64_to_tm(tv_msec / MSEC_PER_SEC, 0, &tm); sdk_info(&pdev_pri->pcidev->dev, "Synchronize UTC time to firmware succeed. UTC time %ld-%02d-%02d %02d:%02d:%02d.\n", tm.tm_year + HINIC_SYNC_YEAR_OFFSET, tm.tm_mon + HINIC_SYNC_MONTH_OFFSET, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } } enum hinic_ver_incompat_mode { /* New driver can't compat with old firmware */ VER_INCOMP_NEW_DRV_OLD_FW, /* New Firmware can't compat with old driver */ VER_INCOMP_NEW_FW_OLD_DRV, }; struct hinic_version_incompat { char *version; char *advise; u32 incompat_mode; }; struct hinic_version_incompat ver_incompat_table[] = { { .version = "1.2.2.0", .advise = "Mechanism of cos changed", .incompat_mode = BIT(VER_INCOMP_NEW_DRV_OLD_FW), }, { .version = "1.2.3.0", .advise = "Driver get sevice mode from firmware", .incompat_mode = BIT(VER_INCOMP_NEW_DRV_OLD_FW), }, }; #define MAX_VER_FIELD_LEN 4 #define MAX_VER_SPLIT_NUM 4 static void __version_split(const char *str, int *split_num, char rst[][MAX_VER_FIELD_LEN]) { const char delim = '.'; const char *src; int cnt = 0; u16 idx, end, token_len; idx = 0; while (idx < strlen(str)) { for (end = idx; end < strlen(str); end++) { if (*(str + end) == delim) break; /* find */ } if (end != idx) { token_len = min_t(u16, end - idx, MAX_VER_FIELD_LEN - 1); src = str + idx; memcpy(rst[cnt], src, token_len); if (++cnt >= MAX_VER_SPLIT_NUM) break; } idx = end + 1; /* skip delim */ } *split_num = cnt; } int hinic_version_cmp(char *ver1, char *ver2) { char ver1_split[MAX_VER_SPLIT_NUM][MAX_VER_FIELD_LEN] = { {0} }; char ver2_split[MAX_VER_SPLIT_NUM][MAX_VER_FIELD_LEN] = { {0} }; int split1_num, split2_num; int ver1_num, ver2_num; int split, err; /* To compat older firmware version */ if (ver1[0] == 'B') return -1; if (ver2[0] == 'B') return 1; __version_split(ver1, &split1_num, ver1_split); __version_split(ver2, &split2_num, ver2_split); if (split1_num != MAX_VER_SPLIT_NUM || split2_num != MAX_VER_SPLIT_NUM) { pr_err("Invalid version %s or %s\n", ver1, ver2); return 0; } for (split = 0; split < MAX_VER_SPLIT_NUM; split++) { err = kstrtoint(ver1_split[split], 0, &ver1_num); err |= kstrtoint(ver2_split[split], 0, &ver2_num); if (err) { pr_err("Failed to parse version: %s, %s\n", ver1_split[split], ver2_split[split]); return 0; } if (ver1_num > ver2_num) return 1; else if (ver1_num < ver2_num) return -1; } return 0; } static int __version_mismatch(struct hinic_pcidev *pcidev, char *cur_fw_ver, char *cur_drv_ver, struct hinic_version_incompat *ver_incompat, int start_entry) { struct hinic_version_incompat *ver_incmp_tmp; int fw_ver_comp; int i, num_entry; fw_ver_comp = hinic_version_cmp(cur_fw_ver, ver_incompat->version); if (fw_ver_comp <= 0) { /* Check if new driver compatible with old fw */ for (i = start_entry; i >= 0; i--) { ver_incmp_tmp = &ver_incompat_table[i]; if (hinic_version_cmp(cur_fw_ver, ver_incmp_tmp->version) >= 0) break; /* Not need to check anymore */ if (ver_incmp_tmp->incompat_mode & BIT(VER_INCOMP_NEW_DRV_OLD_FW)) { sdk_err(&pcidev->pcidev->dev, "Version incompatible: %s, please update firmware to %s, or use %s driver\n", ver_incmp_tmp->advise, cur_drv_ver, cur_fw_ver); return -EINVAL; } } return 0; } /* check if old driver compatible with new firmware */ num_entry = (int)sizeof(ver_incompat_table) / (int)sizeof(ver_incompat_table[0]); for (i = start_entry + 1; i < num_entry; i++) { ver_incmp_tmp = &ver_incompat_table[i]; if (hinic_version_cmp(cur_fw_ver, ver_incmp_tmp->version) < 0) break; /* Not need to check anymore */ if (ver_incmp_tmp->incompat_mode & BIT(VER_INCOMP_NEW_FW_OLD_DRV)) { sdk_err(&pcidev->pcidev->dev, "Version incompatible: %s, please update driver to %s, or use %s firmware\n", ver_incmp_tmp->advise, cur_fw_ver, cur_drv_ver); return -EINVAL; } } return 0; } static void hinic_ignore_minor_version(char *version) { char ver_split[MAX_VER_SPLIT_NUM][MAX_VER_FIELD_LEN] = { {0} }; int max_ver_len, split_num = 0; int err; __version_split(version, &split_num, ver_split); if (split_num != MAX_VER_SPLIT_NUM) return; max_ver_len = (int)strlen(version) + 1; memset(version, 0, max_ver_len); err = snprintf(version, max_ver_len, "%s.%s.%s.0", ver_split[0], ver_split[1], ver_split[2]); if (err <= 0 || err >= max_ver_len) pr_err("Failed to snprintf version, function return(%d) and dest_len(%d)\n", err, max_ver_len); } static int hinic_detect_version_compatible(struct hinic_pcidev *pcidev) { struct hinic_fw_version fw_ver = { {0} }; struct hinic_version_incompat *ver_incompat; char drv_ver[MAX_VER_SPLIT_NUM * MAX_VER_FIELD_LEN] = {0}; int idx, num_entry, drv_ver_len; int ver_mismatch; int err; err = hinic_get_fw_version(pcidev->hwdev, &fw_ver); if (err) { sdk_err(&pcidev->pcidev->dev, "Failed to get firmware version\n"); return err; } drv_ver_len = min_t(int, (int)sizeof(drv_ver) - 1, (int)strlen(HINIC_DRV_VERSION)); memcpy(drv_ver, HINIC_DRV_VERSION, drv_ver_len); sdk_info(&pcidev->pcidev->dev, "Version info: driver %s, firmware %s\n", drv_ver, fw_ver.mgmt_ver); hinic_ignore_minor_version(fw_ver.mgmt_ver); hinic_ignore_minor_version(drv_ver); ver_mismatch = hinic_version_cmp(drv_ver, fw_ver.mgmt_ver); if (!ver_mismatch) return 0; num_entry = (int)sizeof(ver_incompat_table) / (int)sizeof(ver_incompat_table[0]); for (idx = num_entry - 1; idx >= 0; idx--) { ver_incompat = &ver_incompat_table[idx]; if (hinic_version_cmp(drv_ver, ver_incompat->version) < 0) continue; /* Find older verion of driver in table */ return __version_mismatch(pcidev, fw_ver.mgmt_ver, drv_ver, ver_incompat, idx); } return 0; } struct mctp_hdr { u16 resp_code; u16 reason_code; u32 manufacture_id; u8 cmd_rsvd; u8 major_cmd; u8 sub_cmd; u8 spc_field; }; struct mctp_bdf_info { struct mctp_hdr hdr; /* spc_field: pf index */ u8 rsvd; u8 bus; u8 device; u8 function; }; enum mctp_resp_code { /* COMMAND_COMPLETED = 0, */ /* COMMAND_FAILED = 1, */ /* COMMAND_UNAVALILABLE = 2, */ COMMAND_UNSUPPORTED = 3, }; static void __mctp_set_hdr(struct mctp_hdr *hdr, struct hinic_mctp_host_info *mctp_info) { u32 manufacture_id = 0x07DB; hdr->cmd_rsvd = 0; hdr->major_cmd = mctp_info->major_cmd; hdr->sub_cmd = mctp_info->sub_cmd; hdr->manufacture_id = cpu_to_be32(manufacture_id); hdr->resp_code = cpu_to_be16(hdr->resp_code); hdr->reason_code = cpu_to_be16(hdr->reason_code); } static void __mctp_get_bdf(struct hinic_pcidev *pci_adapter, struct hinic_mctp_host_info *mctp_info) { struct pci_dev *pdev = pci_adapter->pcidev; struct mctp_bdf_info *bdf_info = mctp_info->data; bdf_info->bus = pdev->bus->number; bdf_info->device = (u8)(pdev->devfn >> 3); /* 5bits in devfn */ bdf_info->function = (u8)(pdev->devfn & 0x7); /* 3bits in devfn */ memset(&bdf_info->hdr, 0, sizeof(bdf_info->hdr)); __mctp_set_hdr(&bdf_info->hdr, mctp_info); bdf_info->hdr.spc_field = (u8)hinic_global_func_id_hw(pci_adapter->hwdev); mctp_info->data_len = sizeof(*bdf_info); } #define MCTP_MAJOR_CMD_PUBLIC 0x0 #define MCTP_MAJOR_CMD_NIC 0x1 #define MCTP_PUBLIC_SUB_CMD_BDF 0x1 #define MCTP_PUBLIC_SUB_CMD_DRV 0x4 #define MCTP_NIC_SUB_CMD_IP 0x1 static void __mctp_get_host_info(struct hinic_pcidev *dev, struct hinic_mctp_host_info *mctp_info) { struct mctp_hdr *hdr; switch ((((u16)mctp_info->major_cmd) << 8) | mctp_info->sub_cmd) { case (MCTP_MAJOR_CMD_PUBLIC << 8 | MCTP_PUBLIC_SUB_CMD_BDF): __mctp_get_bdf(dev, mctp_info); break; default: hdr = mctp_info->data; hdr->reason_code = COMMAND_UNSUPPORTED; __mctp_set_hdr(hdr, mctp_info); mctp_info->data_len = sizeof(*hdr); break; } } static bool __is_pcidev_match_chip_name(const char *ifname, struct hinic_pcidev *dev, struct card_node *chip_node, enum func_type type) { if (!strncmp(chip_node->chip_name, ifname, IFNAMSIZ)) { if (type == TYPE_UNKNOWN) { if (dev->init_state < HINIC_INIT_STATE_HW_PART_INITED) return false; } else { if (dev->init_state < HINIC_INIT_STATE_HW_PART_INITED || hinic_func_type(dev->hwdev) != type) return false; } return true; } return false; } static struct hinic_pcidev *_get_pcidev_by_chip_name(char *ifname, enum func_type type) { struct card_node *chip_node; struct hinic_pcidev *dev; lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; if (__is_pcidev_match_chip_name(ifname, dev, chip_node, type)) { lld_dev_put(); return dev; } } } lld_dev_put(); return NULL; } static struct hinic_pcidev *hinic_get_pcidev_by_chip_name(char *ifname) { struct hinic_pcidev *dev, *dev_hw_init; /* find hw init device first */ dev_hw_init = _get_pcidev_by_chip_name(ifname, TYPE_UNKNOWN); if (dev_hw_init) { if (hinic_func_type(dev_hw_init->hwdev) == TYPE_PPF) return dev_hw_init; } dev = _get_pcidev_by_chip_name(ifname, TYPE_PPF); if (dev) { if (dev_hw_init && dev_hw_init->init_state >= dev->init_state) return dev_hw_init; return dev; } dev = _get_pcidev_by_chip_name(ifname, TYPE_PF); if (dev) { if (dev_hw_init && dev_hw_init->init_state >= dev->init_state) return dev_hw_init; return dev; } dev = _get_pcidev_by_chip_name(ifname, TYPE_VF); if (dev) return dev; return NULL; } static bool __is_pcidev_match_dev_name(const char *ifname, struct hinic_pcidev *dev, enum hinic_service_type type) { struct hinic_nic_dev *nic_dev; enum hinic_service_type i; if (type == SERVICE_T_MAX) { for (i = SERVICE_T_OVS; i < SERVICE_T_MAX; i++) { if (!strncmp(dev->uld_dev_name[i], ifname, IFNAMSIZ)) return true; } } else { if (!strncmp(dev->uld_dev_name[type], ifname, IFNAMSIZ)) return true; } nic_dev = dev->uld_dev[SERVICE_T_NIC]; if (nic_dev) { if (!strncmp(nic_dev->netdev->name, ifname, IFNAMSIZ)) return true; } return false; } static struct hinic_pcidev * hinic_get_pcidev_by_dev_name(char *ifname, enum hinic_service_type type) { struct card_node *chip_node; struct hinic_pcidev *dev; lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; if (__is_pcidev_match_dev_name(ifname, dev, type)) { lld_dev_put(); return dev; } } } lld_dev_put(); return NULL; } static struct hinic_pcidev *hinic_get_pcidev_by_ifname(char *ifname) { struct hinic_pcidev *dev; /* support search hwdev by chip name, net device name, * or fc device name */ /* Find pcidev by chip_name first */ dev = hinic_get_pcidev_by_chip_name(ifname); if (dev) return dev; /* If ifname not a chip name, * find pcidev by FC name or netdevice name */ return hinic_get_pcidev_by_dev_name(ifname, SERVICE_T_MAX); } int hinic_get_chip_name_by_hwdev(void *hwdev, char *ifname) { struct card_node *chip_node; struct hinic_pcidev *dev; if (!hwdev || !ifname) return -EINVAL; lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; if (dev->hwdev == hwdev) { strscpy(ifname, chip_node->chip_name, IFNAMSIZ - 1); ifname[IFNAMSIZ - 1] = 0; lld_dev_put(); return 0; } } } lld_dev_put(); return -ENXIO; } EXPORT_SYMBOL(hinic_get_chip_name_by_hwdev); static struct card_node *hinic_get_chip_node_by_hwdev(const void *hwdev) { struct card_node *chip_node = NULL; struct card_node *node_tmp = NULL; struct hinic_pcidev *dev; if (!hwdev) return NULL; lld_dev_hold(); list_for_each_entry(node_tmp, &g_hinic_chip_list, node) { if (!chip_node) { list_for_each_entry(dev, &node_tmp->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; if (dev->hwdev == hwdev) { chip_node = node_tmp; break; } } } } lld_dev_put(); return chip_node; } int hinic_get_pf_uld_array(struct pci_dev *pdev, u32 *dev_cnt, void *array[]) { struct hinic_pcidev *dev = pci_get_drvdata(pdev); struct card_node *chip_node; u32 cnt; if (!dev || !hinic_support_nic(dev->hwdev, NULL)) return -EINVAL; lld_dev_hold(); cnt = 0; chip_node = dev->chip_node; list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; if (dev->init_state < HINIC_INIT_STATE_NIC_INITED) continue; if (HINIC_FUNC_IS_VF(dev->hwdev)) continue; array[cnt] = dev->uld_dev[SERVICE_T_NIC]; cnt++; } lld_dev_put(); *dev_cnt = cnt; return 0; } int hinic_get_chip_cos_up_map(struct pci_dev *pdev, bool *is_setted, u8 *cos_up) { struct hinic_pcidev *dev = pci_get_drvdata(pdev); struct card_node *chip_node; if (!dev) return -EINVAL; chip_node = dev->chip_node; *is_setted = chip_node->cos_up_setted; if (chip_node->cos_up_setted) memcpy(cos_up, chip_node->cos_up, sizeof(chip_node->cos_up)); return 0; } int hinic_set_chip_cos_up_map(struct pci_dev *pdev, u8 *cos_up) { struct hinic_pcidev *dev = pci_get_drvdata(pdev); struct card_node *chip_node; if (!dev) return -EINVAL; chip_node = dev->chip_node; chip_node->cos_up_setted = true; memcpy(chip_node->cos_up, cos_up, sizeof(chip_node->cos_up)); return 0; } void *hinic_get_hwdev_by_ifname(char *ifname) { struct hinic_pcidev *dev; dev = hinic_get_pcidev_by_ifname(ifname); if (dev) return dev->hwdev; return NULL; } void *hinic_get_uld_dev_by_ifname(char *ifname, enum hinic_service_type type) { struct hinic_pcidev *dev; if (type >= SERVICE_T_MAX) { pr_err("Service type: %d is error\n", type); return NULL; } dev = hinic_get_pcidev_by_dev_name(ifname, type); if (dev) return dev->uld_dev[type]; return NULL; } void *hinic_get_uld_by_chip_name(char *ifname, enum hinic_service_type type) { struct hinic_pcidev *dev; /* support search hwdev by chip name, net device name, * or fc device name, Find pcidev by chip_name first */ dev = hinic_get_pcidev_by_chip_name(ifname); if (dev) return dev->uld_dev[type]; return NULL; } /* NOTICE: nictool can't use this function, because this function can't keep * tool context mutual exclusive with remove context */ void *hinic_get_ppf_uld_by_pdev(struct pci_dev *pdev, enum hinic_service_type type) { struct hinic_pcidev *pci_adapter; struct card_node *chip_node; struct hinic_pcidev *dev; if (!pdev) return NULL; pci_adapter = pci_get_drvdata(pdev); if (!pci_adapter) return NULL; chip_node = pci_adapter->chip_node; lld_dev_hold(); list_for_each_entry(dev, &chip_node->func_list, node) { /* can't test HINIC_FUNC_IN_REMOVE bit in dev->flag, because * TOE will call this function when detach toe driver */ if (hinic_func_type(dev->hwdev) == TYPE_PPF) { lld_dev_put(); return dev->uld_dev[type]; } } lld_dev_put(); return NULL; } EXPORT_SYMBOL(hinic_get_ppf_uld_by_pdev); void *hinic_get_ppf_hwdev_by_pdev(struct pci_dev *pdev) { struct hinic_pcidev *pci_adapter; struct card_node *chip_node; struct hinic_pcidev *dev; if (!pdev) return NULL; pci_adapter = pci_get_drvdata(pdev); if (!pci_adapter) return NULL; chip_node = pci_adapter->chip_node; lld_dev_hold(); list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag) || dev->init_state < HINIC_INIT_STATE_HW_IF_INITED) continue; if (dev->hwdev && hinic_func_type(dev->hwdev) == TYPE_PPF) { lld_dev_put(); return dev->hwdev; } } lld_dev_put(); return NULL; } void hinic_get_all_chip_id(void *id_info) { struct nic_card_id *card_id = (struct nic_card_id *)id_info; struct card_node *chip_node; int i = 0; int id, err; lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { err = sscanf(chip_node->chip_name, HINIC_CHIP_NAME "%d", &id); if (err <= 0) pr_err("Failed to get hinic id\n"); card_id->id[i] = id; i++; } lld_dev_put(); card_id->num = i; } static bool __is_func_valid(struct hinic_pcidev *dev) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) return false; if (dev->init_state < HINIC_INIT_STATE_HWDEV_INITED) return false; if (HINIC_FUNC_IS_VF(dev->hwdev)) return false; return true; } bool hinic_is_valid_bar_addr(u64 offset) { struct card_node *chip_node = NULL; struct hinic_pcidev *dev; lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { list_for_each_entry(dev, &chip_node->func_list, node) { if (hinic_func_type(dev->hwdev) == TYPE_VF) continue; if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; if (offset == pci_resource_start(dev->pcidev, 0)) { lld_dev_put(); return true; } } } lld_dev_put(); return false; } void hinic_get_card_info(void *hwdev, void *bufin) { struct card_node *chip_node = NULL; struct card_info *info = (struct card_info *)bufin; struct hinic_nic_dev *nic_dev; struct hinic_pcidev *dev; void *fun_hwdev; u32 i = 0; info->pf_num = 0; chip_node = hinic_get_chip_node_by_hwdev(hwdev); if (!chip_node) return; lld_dev_hold(); list_for_each_entry(dev, &chip_node->func_list, node) { if (!__is_func_valid(dev)) continue; fun_hwdev = dev->hwdev; if (((hinic_support_fc(fun_hwdev, NULL)) || (hinic_support_fcoe(fun_hwdev, NULL))) && dev->uld_dev[SERVICE_T_FC]) { info->pf[i].pf_type |= (u32)BIT(SERVICE_T_FC); strscpy(info->pf[i].name, dev->uld_dev_name[SERVICE_T_FC], IFNAMSIZ); } if (hinic_support_nic(fun_hwdev, NULL)) { nic_dev = dev->uld_dev[SERVICE_T_NIC]; if (nic_dev) { info->pf[i].pf_type |= (u32)BIT(SERVICE_T_NIC); strscpy(info->pf[i].name, nic_dev->netdev->name, IFNAMSIZ); } } if ((hinic_support_ovs(fun_hwdev, NULL)) && dev->uld_dev[SERVICE_T_OVS]) info->pf[i].pf_type |= (u32)BIT(SERVICE_T_OVS); if ((hinic_support_roce(fun_hwdev, NULL)) && dev->uld_dev[SERVICE_T_ROCE]) info->pf[i].pf_type |= (u32)BIT(SERVICE_T_ROCE); if ((hinic_support_toe(fun_hwdev, NULL)) && dev->uld_dev[SERVICE_T_TOE]) info->pf[i].pf_type |= (u32)BIT(SERVICE_T_TOE); if (hinic_func_for_mgmt(fun_hwdev)) strscpy(info->pf[i].name, "FOR_MGMT", IFNAMSIZ); if (hinic_func_for_pt(fun_hwdev)) info->pf[i].pf_type |= (u32)BIT(SERVICE_T_PT); if (hinic_func_for_hwpt(fun_hwdev)) info->pf[i].pf_type |= (u32)BIT(SERVICE_T_HWPT); strscpy(info->pf[i].bus_info, pci_name(dev->pcidev), sizeof(info->pf[i].bus_info)); info->pf_num++; i = info->pf_num; } lld_dev_put(); } void hinic_get_card_func_info_by_card_name(const char *chip_name, struct hinic_card_func_info *card_func) { struct card_node *chip_node = NULL; struct hinic_pcidev *dev; struct func_pdev_info *pdev_info; card_func->num_pf = 0; lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { if (strncmp(chip_node->chip_name, chip_name, IFNAMSIZ)) continue; list_for_each_entry(dev, &chip_node->func_list, node) { if (hinic_func_type(dev->hwdev) == TYPE_VF) continue; if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; pdev_info = &card_func->pdev_info[card_func->num_pf]; pdev_info->bar0_size = pci_resource_len(dev->pcidev, 0); pdev_info->bar0_phy_addr = pci_resource_start(dev->pcidev, 0); card_func->num_pf++; if (card_func->num_pf >= MAX_SIZE) break; } } lld_dev_put(); } int hinic_get_device_id(void *hwdev, u16 *dev_id) { struct card_node *chip_node = NULL; struct hinic_pcidev *dev; u16 vendor_id = 0; u16 device_id = 0; chip_node = hinic_get_chip_node_by_hwdev(hwdev); if (!chip_node) return -ENODEV; lld_dev_hold(); list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; pci_read_config_word(dev->pcidev, 0, &vendor_id); if (vendor_id == HINIC_PCI_VENDOR_ID) { pci_read_config_word(dev->pcidev, 2, &device_id); break; } } lld_dev_put(); *dev_id = device_id; return 0; } int hinic_get_pf_id(void *hwdev, u32 port_id, u32 *pf_id, u32 *isvalid) { struct card_node *chip_node = NULL; struct hinic_pcidev *dev; chip_node = hinic_get_chip_node_by_hwdev(hwdev); if (!chip_node) return -ENODEV; lld_dev_hold(); list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag) || dev->init_state < HINIC_INIT_STATE_HWDEV_INITED) continue; if (hinic_physical_port_id(dev->hwdev) == port_id) { *pf_id = hinic_global_func_id(dev->hwdev); *isvalid = 1; break; } } lld_dev_put(); return 0; } void hinic_get_fc_devname(char *devname) { struct card_node *chip_node; struct hinic_pcidev *dev; lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; if (dev->init_state < HINIC_INIT_STATE_NIC_INITED) continue; if (HINIC_FUNC_IS_VF(dev->hwdev)) continue; if (dev->uld_dev[SERVICE_T_FC]) { strscpy(devname, dev->uld_dev_name[SERVICE_T_FC], IFNAMSIZ); lld_dev_put(); return; } } } lld_dev_put(); } enum hinic_init_state hinic_get_init_state(struct pci_dev *pdev) { struct hinic_pcidev *dev = pci_get_drvdata(pdev); if (dev) return dev->init_state; return HINIC_INIT_STATE_NONE; } enum hinic_init_state hinic_get_init_state_by_ifname(char *ifname) { struct hinic_pcidev *dev; dev = hinic_get_pcidev_by_ifname(ifname); if (dev) return dev->init_state; pr_err("Can not get device %s\n", ifname); return HINIC_INIT_STATE_NONE; } int hinic_get_self_test_result(char *ifname, u32 *result) { struct hinic_pcidev *dev = NULL; dev = hinic_get_pcidev_by_ifname(ifname); if (!dev) { pr_err("Get pcidev failed by ifname: %s\n", ifname); return -EFAULT; } *result = be32_to_cpu(readl((u8 __iomem *)(dev->cfg_reg_base) + SELF_TEST_BAR_ADDR_OFFSET)); return 0; } struct net_device *hinic_get_netdev_by_lld(struct hinic_lld_dev *lld_dev) { struct hinic_pcidev *pci_adapter; struct hinic_nic_dev *nic_dev; if (!lld_dev || !hinic_support_nic(lld_dev->hwdev, NULL)) return NULL; pci_adapter = pci_get_drvdata(lld_dev->pdev); nic_dev = pci_adapter->uld_dev[SERVICE_T_NIC]; if (!nic_dev) { sdk_err(&pci_adapter->pcidev->dev, "There's no net device attached on the pci device\n"); return NULL; } return nic_dev->netdev; } EXPORT_SYMBOL(hinic_get_netdev_by_lld); void *hinic_get_hwdev_by_netdev(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (!nic_dev || !netdev) return NULL; return nic_dev->hwdev; } EXPORT_SYMBOL(hinic_get_hwdev_by_netdev); struct net_device *hinic_get_netdev_by_pcidev(struct pci_dev *pdev) { struct hinic_pcidev *pci_adapter; struct hinic_nic_dev *nic_dev; if (!pdev) return NULL; pci_adapter = pci_get_drvdata(pdev); if (!pci_adapter || !hinic_support_nic(pci_adapter->hwdev, NULL)) return NULL; nic_dev = pci_adapter->uld_dev[SERVICE_T_NIC]; if (!nic_dev) { sdk_err(&pci_adapter->pcidev->dev, "There`s no net device attached on the pci device\n"); return NULL; } return nic_dev->netdev; } EXPORT_SYMBOL(hinic_get_netdev_by_pcidev); struct hinic_sriov_info *hinic_get_sriov_info_by_pcidev(struct pci_dev *pdev) { struct hinic_pcidev *pci_adapter = pci_get_drvdata(pdev); return &pci_adapter->sriov_info; } bool hinic_is_in_host(void) { struct card_node *chip_node; struct hinic_pcidev *dev; lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; if (dev->init_state > HINIC_INIT_STATE_PCI_INITED && hinic_func_type(dev->hwdev) != TYPE_VF) { lld_dev_put(); return true; } } } lld_dev_put(); return false; } int hinic_attach_nic(struct hinic_lld_dev *lld_dev) { struct hinic_pcidev *dev; if (!lld_dev) return -EINVAL; dev = container_of(lld_dev, struct hinic_pcidev, lld_dev); return attach_uld(dev, SERVICE_T_NIC, &g_uld_info[SERVICE_T_NIC]); } EXPORT_SYMBOL(hinic_attach_nic); void hinic_detach_nic(struct hinic_lld_dev *lld_dev) { struct hinic_pcidev *dev; if (!lld_dev) return; dev = container_of(lld_dev, struct hinic_pcidev, lld_dev); detach_uld(dev, SERVICE_T_NIC); } EXPORT_SYMBOL(hinic_detach_nic); int hinic_attach_roce(struct hinic_lld_dev *lld_dev) { struct hinic_pcidev *dev; if (!lld_dev) return -EINVAL; dev = container_of(lld_dev, struct hinic_pcidev, lld_dev); return attach_uld(dev, SERVICE_T_ROCE, &g_uld_info[SERVICE_T_ROCE]); } EXPORT_SYMBOL(hinic_attach_roce); void hinic_detach_roce(struct hinic_lld_dev *lld_dev) { struct hinic_pcidev *dev; if (!lld_dev) return; dev = container_of(lld_dev, struct hinic_pcidev, lld_dev); detach_uld(dev, SERVICE_T_ROCE); } EXPORT_SYMBOL(hinic_detach_roce); static int __set_nic_rss_state(struct hinic_pcidev *dev, bool enable) { void *nic_uld; int err = 0; if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) return 0; nic_uld = dev->uld_dev[SERVICE_T_NIC]; if (!hinic_support_nic(dev->hwdev, NULL) || !nic_uld) return 0; if (hinic_func_type(dev->hwdev) == TYPE_VF) return 0; if (enable) err = hinic_enable_func_rss(nic_uld); else err = hinic_disable_func_rss(nic_uld); if (err) { sdk_err(&dev->pcidev->dev, "Failed to %s rss\n", enable ? "enable" : "disable"); } return err; } int hinic_disable_nic_rss(struct hinic_lld_dev *lld_dev) { struct hinic_pcidev *adapter; if (!lld_dev) return -EINVAL; adapter = container_of(lld_dev, struct hinic_pcidev, lld_dev); return __set_nic_rss_state(adapter, false); } EXPORT_SYMBOL(hinic_disable_nic_rss); int hinic_enable_nic_rss(struct hinic_lld_dev *lld_dev) { struct hinic_pcidev *adapter; if (!lld_dev) return -EINVAL; adapter = container_of(lld_dev, struct hinic_pcidev, lld_dev); return __set_nic_rss_state(adapter, true); } EXPORT_SYMBOL(hinic_enable_nic_rss); struct pci_device_id *hinic_get_pci_device_id(struct pci_dev *pdev) { struct hinic_pcidev *adapter; if (!pdev) return NULL; adapter = pci_get_drvdata(pdev); return &adapter->id; } EXPORT_SYMBOL(hinic_get_pci_device_id); static int __set_nic_func_state(struct hinic_pcidev *pci_adapter) { struct pci_dev *pdev = pci_adapter->pcidev; u16 func_id; int err; bool enable_nic; err = hinic_global_func_id_get(pci_adapter->hwdev, &func_id); if (err) return err; err = hinic_get_func_nic_enable(pci_adapter->hwdev, func_id, &enable_nic); if (err) { sdk_err(&pdev->dev, "Failed to get nic state\n"); return err; } if (enable_nic) { if (is_multi_bm_slave(pci_adapter->hwdev)) hinic_set_vf_dev_cap(pci_adapter->hwdev); err = attach_uld(pci_adapter, SERVICE_T_NIC, &g_uld_info[SERVICE_T_NIC]); if (err) { sdk_err(&pdev->dev, "Failed to initialize NIC\n"); return err; } if (pci_adapter->init_state < HINIC_INIT_STATE_NIC_INITED) pci_adapter->init_state = HINIC_INIT_STATE_NIC_INITED; } else { detach_uld(pci_adapter, SERVICE_T_NIC); } return 0; } int hinic_ovs_set_vf_nic_state(struct hinic_lld_dev *lld_dev, u16 vf_func_id, bool en) { struct hinic_pcidev *dev, *des_dev; struct hinic_nic_dev *uld_dev; int err = -EFAULT; if (!lld_dev) return -EINVAL; dev = pci_get_drvdata(lld_dev->pdev); if (!dev) return -EFAULT; /* find func_idx pci_adapter and disable or enable nic */ lld_dev_hold(); list_for_each_entry(des_dev, &dev->chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag)) continue; if (des_dev->init_state < HINIC_INIT_STATE_DBGTOOL_INITED && !test_bit(HINIC_FUNC_PRB_ERR, &des_dev->flag)) continue; if (hinic_global_func_id(des_dev->hwdev) != vf_func_id) continue; if (des_dev->init_state < HINIC_INIT_STATE_DBGTOOL_INITED) { break; } sdk_info(&dev->pcidev->dev, "Receive event: %s vf%d nic\n", en ? "enable" : "disable", vf_func_id); err = 0; if (en) { if (des_dev->uld_dev[SERVICE_T_NIC]) { sdk_err(&des_dev->pcidev->dev, "%s driver has attached to pcie device, cannot set VF max_queue_num\n", s_uld_name[SERVICE_T_NIC]); } else { err = hinic_set_vf_dev_cap(des_dev->hwdev); if (err) { sdk_err(&des_dev->pcidev->dev, "%s driver Set VF max_queue_num failed, err=%d\n", s_uld_name[SERVICE_T_NIC], err); break; } } err = attach_uld(des_dev, SERVICE_T_NIC, &g_uld_info[SERVICE_T_NIC]); if (err) { sdk_err(&des_dev->pcidev->dev, "Failed to initialize NIC\n"); break; } uld_dev = (struct hinic_nic_dev *) (des_dev->uld_dev[SERVICE_T_NIC]); uld_dev->in_vm = true; uld_dev->is_vm_slave = is_multi_vm_slave(uld_dev->hwdev); uld_dev->is_bm_slave = is_multi_bm_slave(uld_dev->hwdev); if (des_dev->init_state < HINIC_INIT_STATE_NIC_INITED) des_dev->init_state = HINIC_INIT_STATE_NIC_INITED; } else { detach_uld(des_dev, SERVICE_T_NIC); } break; } lld_dev_put(); return err; } EXPORT_SYMBOL(hinic_ovs_set_vf_nic_state); static void slave_host_mgmt_work(struct work_struct *work) { struct hinic_pcidev *pci_adapter = container_of(work, struct hinic_pcidev, slave_nic_work); __set_nic_func_state(pci_adapter); } static void __multi_host_mgmt(struct hinic_pcidev *dev, struct hinic_multi_host_mgmt_event *mhost_mgmt) { struct hinic_pcidev *des_dev; struct hinic_mhost_nic_func_state *nic_state = {0}; switch (mhost_mgmt->sub_cmd) { case HINIC_MHOST_NIC_STATE_CHANGE: nic_state = mhost_mgmt->data; nic_state->status = 0; /* find func_idx pci_adapter and disable or enable nic */ lld_dev_hold(); list_for_each_entry(des_dev, &dev->chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &des_dev->flag)) continue; if (des_dev->init_state < HINIC_INIT_STATE_DBGTOOL_INITED && !test_bit(HINIC_FUNC_PRB_ERR, &des_dev->flag)) continue; if (hinic_global_func_id_hw(des_dev->hwdev) != nic_state->func_idx) continue; if (des_dev->init_state < HINIC_INIT_STATE_DBGTOOL_INITED) { nic_state->status = test_bit(HINIC_FUNC_PRB_ERR, &des_dev->flag) ? 1 : 0; break; } sdk_info(&dev->pcidev->dev, "Receive nic state changed event, state: %d\n", nic_state->enable); /* schedule_work */ schedule_work(&des_dev->slave_nic_work); break; } lld_dev_put(); break; default: sdk_warn(&dev->pcidev->dev, "Received unknown multi-host mgmt event %d\n", mhost_mgmt->sub_cmd); break; } } static void send_uld_dev_event(struct hinic_pcidev *dev, struct hinic_event_info *event) { enum hinic_service_type type; for (type = SERVICE_T_NIC; type < SERVICE_T_MAX; type++) { if (test_and_set_bit(type, &dev->state)) { sdk_warn(&dev->pcidev->dev, "Event: 0x%x can't handler, %s is in detach\n", event->type, s_uld_name[type]); continue; } if (g_uld_info[type].event) g_uld_info[type].event(&dev->lld_dev, dev->uld_dev[type], event); clear_bit(type, &dev->state); } } static void send_event_to_all_pf(struct hinic_pcidev *dev, struct hinic_event_info *event) { struct hinic_pcidev *des_dev = NULL; lld_dev_hold(); list_for_each_entry(des_dev, &dev->chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &des_dev->flag) || des_dev->init_state < HINIC_INIT_STATE_HW_IF_INITED) continue; if (hinic_func_type(des_dev->hwdev) == TYPE_VF) continue; send_uld_dev_event(des_dev, event); } lld_dev_put(); } static void send_event_to_dst_pf(struct hinic_pcidev *dev, u16 func_id, struct hinic_event_info *event) { struct hinic_pcidev *des_dev = NULL; lld_dev_hold(); list_for_each_entry(des_dev, &dev->chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &des_dev->flag) || des_dev->init_state < HINIC_INIT_STATE_HW_IF_INITED) continue; if (hinic_func_type(des_dev->hwdev) == TYPE_VF) continue; if (hinic_global_func_id(des_dev->hwdev) == func_id) { send_uld_dev_event(des_dev, event); break; } } lld_dev_put(); } void hinic_event_process(void *adapter, struct hinic_event_info *event) { struct hinic_pcidev *dev = adapter; u16 func_id; switch (event->type) { case HINIC_EVENT_FMW_ACT_NTC: hinic_sync_time_to_fmw(dev); break; case HINIC_EVENT_MCTP_GET_HOST_INFO: __mctp_get_host_info(dev, &event->mctp_info); break; case HINIC_EVENT_MULTI_HOST_MGMT: __multi_host_mgmt(dev, &event->mhost_mgmt); break; case HINIC_EVENT_FAULT: if (event->info.fault_level == FAULT_LEVEL_SERIOUS_FLR && event->info.event.chip.func_id < HINIC_MAX_PF_NUM) { func_id = event->info.event.chip.func_id; send_event_to_dst_pf(adapter, func_id, event); } else { send_uld_dev_event(adapter, event); } break; case HINIC_EVENT_MGMT_WATCHDOG_EVENT: send_event_to_all_pf(adapter, event); break; default: send_uld_dev_event(adapter, event); break; } } static int mapping_bar(struct pci_dev *pdev, struct hinic_pcidev *pci_adapter) { u32 db_dwqe_size; u64 dwqe_addr; pci_adapter->cfg_reg_base = pci_ioremap_bar(pdev, HINIC_PCI_CFG_REG_BAR); if (!pci_adapter->cfg_reg_base) { sdk_err(&pci_adapter->pcidev->dev, "Failed to map configuration regs\n"); return -ENOMEM; } pci_adapter->intr_reg_base = pci_ioremap_bar(pdev, HINIC_PCI_INTR_REG_BAR); if (!pci_adapter->intr_reg_base) { sdk_err(&pci_adapter->pcidev->dev, "Failed to map interrupt regs\n"); goto map_intr_bar_err; } db_dwqe_size = hinic_get_db_size(pci_adapter->cfg_reg_base, &pci_adapter->chip_mode); pci_adapter->db_base_phy = pci_resource_start(pdev, HINIC_PCI_DB_BAR); pci_adapter->db_base = ioremap(pci_adapter->db_base_phy, db_dwqe_size); if (!pci_adapter->db_base) { sdk_err(&pci_adapter->pcidev->dev, "Failed to map doorbell regs\n"); goto map_db_err; } if (pci_adapter->chip_mode != CHIP_MODE_NORMAL) return 0; dwqe_addr = pci_adapter->db_base_phy + db_dwqe_size; #if defined(__aarch64__) /* arm do not support call ioremap_wc() */ pci_adapter->dwqe_mapping = ioremap(dwqe_addr, db_dwqe_size); #else pci_adapter->dwqe_mapping = io_mapping_create_wc(dwqe_addr, db_dwqe_size); #endif if (!pci_adapter->dwqe_mapping) { sdk_err(&pci_adapter->pcidev->dev, "Failed to io_mapping_create_wc\n"); goto mapping_dwqe_err; } return 0; mapping_dwqe_err: iounmap(pci_adapter->db_base); map_db_err: iounmap(pci_adapter->intr_reg_base); map_intr_bar_err: iounmap(pci_adapter->cfg_reg_base); return -ENOMEM; } static void unmapping_bar(struct hinic_pcidev *pci_adapter) { if (pci_adapter->chip_mode == CHIP_MODE_NORMAL) { #if defined(__aarch64__) iounmap(pci_adapter->dwqe_mapping); #else io_mapping_free(pci_adapter->dwqe_mapping); #endif } iounmap(pci_adapter->db_base); iounmap(pci_adapter->intr_reg_base); iounmap(pci_adapter->cfg_reg_base); } static int alloc_chip_node(struct hinic_pcidev *pci_adapter) { struct card_node *chip_node; unsigned char i; unsigned char parent_bus_number = 0; int err; if (!pci_is_root_bus(pci_adapter->pcidev->bus)) parent_bus_number = pci_adapter->pcidev->bus->parent->number; if (parent_bus_number != 0) { list_for_each_entry(chip_node, &g_hinic_chip_list, node) { if (chip_node->dp_bus_num == parent_bus_number) { pci_adapter->chip_node = chip_node; return 0; } } } else if (pci_adapter->pcidev->device == HINIC_DEV_ID_1822_VF || pci_adapter->pcidev->device == HINIC_DEV_ID_1822_VF_HV) { list_for_each_entry(chip_node, &g_hinic_chip_list, node) { if (chip_node) { pci_adapter->chip_node = chip_node; return 0; } } } for (i = 0; i < MAX_CARD_ID; i++) { if (!FIND_BIT(card_bit_map, i)) { card_bit_map = (u64)SET_BIT(card_bit_map, i); break; } } if (i == MAX_CARD_ID) { sdk_err(&pci_adapter->pcidev->dev, "Failed to alloc card id\n"); return -EFAULT; } chip_node = kzalloc(sizeof(*chip_node), GFP_KERNEL); if (!chip_node) goto alloc_chip_err; chip_node->dbgtool_attr_file.name = kzalloc(IFNAMSIZ, GFP_KERNEL); if (!(chip_node->dbgtool_attr_file.name)) { sdk_err(&pci_adapter->pcidev->dev, "Failed to alloc dbgtool attr file name\n"); goto alloc_dbgtool_attr_file_err; } /* parent bus number */ chip_node->dp_bus_num = parent_bus_number; err = snprintf(chip_node->chip_name, IFNAMSIZ, "%s%d", HINIC_CHIP_NAME, i); if (err <= 0 || err >= IFNAMSIZ) { sdk_err(&pci_adapter->pcidev->dev, "Failed to snprintf chip_name, function return(%d) and dest_len(%d)\n", err, IFNAMSIZ); goto alloc_dbgtool_attr_file_err; } err = snprintf((char *)chip_node->dbgtool_attr_file.name, IFNAMSIZ, "%s%d", HINIC_CHIP_NAME, i); if (err <= 0 || err >= IFNAMSIZ) { sdk_err(&pci_adapter->pcidev->dev, "Failed to snprintf dbgtool_attr_file_name, function return(%d) and dest_len(%d)\n", err, IFNAMSIZ); goto alloc_dbgtool_attr_file_err; } sdk_info(&pci_adapter->pcidev->dev, "Add new chip %s to global list succeed\n", chip_node->chip_name); list_add_tail(&chip_node->node, &g_hinic_chip_list); INIT_LIST_HEAD(&chip_node->func_list); pci_adapter->chip_node = chip_node; mutex_init(&chip_node->sfp_mutex); return 0; alloc_dbgtool_attr_file_err: kfree(chip_node); alloc_chip_err: card_bit_map = CLEAR_BIT(card_bit_map, i); return -ENOMEM; } static void free_chip_node(struct hinic_pcidev *pci_adapter) { struct card_node *chip_node = pci_adapter->chip_node; u32 id; int err; if (!(card_bit_map & BIT(pci_adapter->card_id))) return; if (list_empty(&chip_node->func_list)) { list_del(&chip_node->node); sdk_info(&pci_adapter->pcidev->dev, "Delete chip %s from global list succeed\n", chip_node->chip_name); err = sscanf(chip_node->chip_name, HINIC_CHIP_NAME "%u", &id); if (err <= 0) sdk_err(&pci_adapter->pcidev->dev, "Failed to get hinic id\n"); card_bit_map = CLEAR_BIT(card_bit_map, id); kfree(chip_node->dbgtool_attr_file.name); kfree(chip_node); } } static bool hinic_get_vf_load_state(struct pci_dev *pdev) { unsigned char parent_bus_number; struct card_node *chip_node; u8 id; if (!pdev->is_virtfn) return false; /* vf used in vm */ if (pci_is_root_bus(pdev->bus)) return disable_vf_load; parent_bus_number = pdev->bus->parent->number; lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { if (chip_node->dp_bus_num == parent_bus_number) { for (id = 0; id < HINIC_MAX_PF_NUM; id++) { if (chip_node->pf_bus_num[id] == pdev->bus->number) { lld_dev_put(); return chip_node->disable_vf_load[id]; } } } } lld_dev_put(); return disable_vf_load; } static void hinic_set_vf_load_state(struct hinic_pcidev *pci_adapter, bool vf_load_state) { struct card_node *chip_node; u16 func_id; if (hinic_func_type(pci_adapter->hwdev) == TYPE_VF) return; /* The VF on the BM slave side must be probed */ if (is_multi_bm_slave(pci_adapter->hwdev)) vf_load_state = false; func_id = hinic_global_func_id_hw(pci_adapter->hwdev); chip_node = pci_adapter->chip_node; chip_node->disable_vf_load[func_id] = vf_load_state; chip_node->pf_bus_num[func_id] = pci_adapter->pcidev->bus->number; sdk_info(&pci_adapter->pcidev->dev, "Current function support %s, %s vf load in host\n", (hinic_support_ovs(pci_adapter->hwdev, NULL) ? "ovs" : "nic"), (vf_load_state ? "disable" : "enable")); } int hinic_ovs_set_vf_load_state(struct pci_dev *pdev) { struct hinic_pcidev *pci_adapter; if (!pdev) { pr_err("pdev is null\n"); return -EINVAL; } pci_adapter = pci_get_drvdata(pdev); if (!pci_adapter) { pr_err("pci_adapter is null\n"); return -EFAULT; } hinic_set_vf_load_state(pci_adapter, disable_vf_load); return 0; } EXPORT_SYMBOL(hinic_ovs_set_vf_load_state); static int hinic_config_deft_mrss(struct pci_dev *pdev) { return 0; } static int hinic_config_pci_cto(struct pci_dev *pdev) { return 0; } static int hinic_pci_init(struct pci_dev *pdev) { struct hinic_pcidev *pci_adapter = NULL; int err; err = hinic_config_deft_mrss(pdev); if (err) { sdk_err(&pdev->dev, "Failed to configure Max Read Request Size\n"); return err; } err = hinic_config_pci_cto(pdev); if (err) { sdk_err(&pdev->dev, "Failed to configure Completion timeout\n"); return err; } pci_adapter = kzalloc(sizeof(*pci_adapter), GFP_KERNEL); if (!pci_adapter) return -ENOMEM; pci_adapter->pcidev = pdev; mutex_init(&pci_adapter->pdev_mutex); pci_set_drvdata(pdev, pci_adapter); #ifdef CONFIG_PCI_IOV if (pdev->is_virtfn && hinic_get_vf_load_state(pdev)) { sdk_info(&pdev->dev, "VFs are not binded to hinic\n"); return 0; } #endif err = pci_enable_device(pdev); if (err) { sdk_err(&pdev->dev, "Failed to enable PCI device\n"); goto pci_enable_err; } err = pci_request_regions(pdev, HINIC_DRV_NAME); if (err) { sdk_err(&pdev->dev, "Failed to request regions\n"); goto pci_regions_err; } // pci_enable_pcie_error_reporting(pdev); pci_set_master(pdev); err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); if (err) { sdk_warn(&pdev->dev, "Couldn't set 64-bit DMA mask\n"); err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (err) { sdk_err(&pdev->dev, "Failed to set DMA mask\n"); goto dma_mask_err; } } err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); if (err) { sdk_warn(&pdev->dev, "Couldn't set 64-bit coherent DMA mask\n"); err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (err) { sdk_err(&pdev->dev, "Failed to set coherent DMA mask\n"); goto dma_consistnet_mask_err; } } return 0; dma_consistnet_mask_err: dma_mask_err: pci_clear_master(pdev); // pci_disable_pcie_error_reporting(pdev); pci_release_regions(pdev); pci_regions_err: pci_disable_device(pdev); pci_enable_err: pci_set_drvdata(pdev, NULL); kfree(pci_adapter); return err; } static void hinic_pci_deinit(struct pci_dev *pdev) { struct hinic_pcidev *pci_adapter = pci_get_drvdata(pdev); pci_clear_master(pdev); pci_release_regions(pdev); // pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); kfree(pci_adapter); } static void hinic_notify_ppf_unreg(struct hinic_pcidev *pci_adapter) { struct card_node *chip_node = pci_adapter->chip_node; struct hinic_pcidev *dev; if (hinic_func_type(pci_adapter->hwdev) != TYPE_PPF) return; lld_lock_chip_node(); list_for_each_entry(dev, &chip_node->func_list, node) { hinic_ppf_hwdev_unreg(dev->hwdev); } lld_unlock_chip_node(); } static void hinic_notify_ppf_reg(struct hinic_pcidev *pci_adapter) { struct card_node *chip_node = pci_adapter->chip_node; struct hinic_pcidev *dev; if (hinic_func_type(pci_adapter->hwdev) != TYPE_PPF) return; lld_lock_chip_node(); list_for_each_entry(dev, &chip_node->func_list, node) { hinic_ppf_hwdev_reg(dev->hwdev, pci_adapter->hwdev); } lld_unlock_chip_node(); } #ifdef CONFIG_X86 /** * cfg_order_reg - when cpu model is haswell or broadwell, should configure dma * order register to zero * @pci_adapter: pci adapter */ /*lint -save -e40 */ void cfg_order_reg(struct hinic_pcidev *pci_adapter) { u8 cpu_model[] = {0x3c, 0x3f, 0x45, 0x46, 0x3d, 0x47, 0x4f, 0x56}; struct cpuinfo_x86 *cpuinfo; u32 i; if (HINIC_FUNC_IS_VF(pci_adapter->hwdev)) return; cpuinfo = &cpu_data(0); for (i = 0; i < sizeof(cpu_model); i++) { if (cpu_model[i] == cpuinfo->x86_model) hinic_set_pcie_order_cfg(pci_adapter->hwdev); } } /*lint -restore*/ #endif static int hinic_func_init(struct pci_dev *pdev, struct hinic_pcidev *pci_adapter) { struct hinic_init_para init_para; bool vf_load_state; int err; init_para.adapter_hdl = pci_adapter; init_para.pcidev_hdl = pdev; init_para.dev_hdl = &pdev->dev; init_para.cfg_reg_base = pci_adapter->cfg_reg_base; init_para.intr_reg_base = pci_adapter->intr_reg_base; init_para.db_base = pci_adapter->db_base; init_para.db_base_phy = pci_adapter->db_base_phy; init_para.dwqe_mapping = pci_adapter->dwqe_mapping; init_para.hwdev = &pci_adapter->hwdev; init_para.chip_node = pci_adapter->chip_node; init_para.ppf_hwdev = hinic_get_ppf_hwdev_by_pdev(pdev); err = hinic_init_hwdev(&init_para); if (err < 0) { pci_adapter->hwdev = NULL; sdk_err(&pdev->dev, "Failed to initialize hardware device\n"); return -EFAULT; } else if (err > 0) { if (err == (1 << HINIC_HWDEV_ALL_INITED) && pci_adapter->init_state < HINIC_INIT_STATE_HW_IF_INITED) { pci_adapter->init_state = HINIC_INIT_STATE_HW_IF_INITED; sdk_info(&pdev->dev, "Initialize hardware device later\n"); queue_delayed_work(pci_adapter->slave_nic_init_workq, &pci_adapter->slave_nic_init_dwork, HINIC_SLAVE_NIC_DELAY_TIME); set_bit(HINIC_FUNC_PRB_DELAY, &pci_adapter->flag); } else if (err != (1 << HINIC_HWDEV_ALL_INITED)) { sdk_err(&pdev->dev, "Initialize hardware device partitial failed\n"); hinic_detect_version_compatible(pci_adapter); hinic_notify_ppf_reg(pci_adapter); pci_adapter->init_state = HINIC_INIT_STATE_HW_PART_INITED; } return -EFAULT; } hinic_notify_ppf_reg(pci_adapter); pci_adapter->init_state = HINIC_INIT_STATE_HWDEV_INITED; vf_load_state = hinic_support_ovs(pci_adapter->hwdev, NULL) ? true : disable_vf_load; hinic_set_vf_load_state(pci_adapter, vf_load_state); hinic_qps_num_set(pci_adapter->hwdev, 0); pci_adapter->lld_dev.pdev = pdev; pci_adapter->lld_dev.hwdev = pci_adapter->hwdev; pci_adapter->sriov_info.pdev = pdev; pci_adapter->sriov_info.hwdev = pci_adapter->hwdev; hinic_event_register(pci_adapter->hwdev, pci_adapter, hinic_event_process); if (!HINIC_FUNC_IS_VF(pci_adapter->hwdev)) hinic_sync_time_to_fmw(pci_adapter); hinic_init_syncfw_timer(pci_adapter); /* dbgtool init */ lld_lock_chip_node(); err = hinic_dbgtool_knl_init(pci_adapter->hwdev, pci_adapter->chip_node); if (err) { lld_unlock_chip_node(); sdk_err(&pdev->dev, "Failed to initialize dbgtool\n"); hinic_destroy_syncfw_timer(pci_adapter); hinic_event_unregister(pci_adapter->hwdev); return err; } lld_unlock_chip_node(); pci_adapter->init_state = HINIC_INIT_STATE_DBGTOOL_INITED; err = hinic_detect_version_compatible(pci_adapter); if (err) return err; if (!HINIC_FUNC_IS_VF(pci_adapter->hwdev) && FUNC_ENABLE_SRIOV_IN_DEFAULT(pci_adapter->hwdev)) { hinic_pci_sriov_enable(pdev, hinic_func_max_vf(pci_adapter->hwdev)); } /* NIC is base driver, probe firstly */ err = __set_nic_func_state(pci_adapter); if (err) return err; attach_ulds(pci_adapter); #ifdef CONFIG_X86 cfg_order_reg(pci_adapter); #endif sdk_info(&pdev->dev, "Pcie device probed\n"); pci_adapter->init_state = HINIC_INIT_STATE_ALL_INITED; return 0; } static void hinic_func_deinit(struct pci_dev *pdev) { struct hinic_pcidev *pci_adapter = pci_get_drvdata(pdev); /* When function deinit, disable mgmt initiative report events firstly, * then flush mgmt work-queue. */ hinic_disable_mgmt_msg_report(pci_adapter->hwdev); if (pci_adapter->init_state >= HINIC_INIT_STATE_HW_PART_INITED) hinic_flush_mgmt_workq(pci_adapter->hwdev); hinic_set_func_deinit_flag(pci_adapter->hwdev); if (pci_adapter->init_state >= HINIC_INIT_STATE_NIC_INITED) { detach_ulds(pci_adapter); detach_uld(pci_adapter, SERVICE_T_NIC); } if (pci_adapter->init_state >= HINIC_INIT_STATE_DBGTOOL_INITED) { lld_lock_chip_node(); hinic_dbgtool_knl_deinit(pci_adapter->hwdev, pci_adapter->chip_node); lld_unlock_chip_node(); hinic_destroy_syncfw_timer(pci_adapter); hinic_event_unregister(pci_adapter->hwdev); } hinic_notify_ppf_unreg(pci_adapter); if (pci_adapter->init_state >= HINIC_INIT_STATE_HW_IF_INITED) { /* Remove the current node from node-list first, * then it's safe to free hwdev */ lld_lock_chip_node(); list_del(&pci_adapter->node); lld_unlock_chip_node(); hinic_free_hwdev(pci_adapter->hwdev); } } static void wait_tool_unused(void) { u32 loop_cnt = 0; while (loop_cnt < HINIC_WAIT_TOOL_CNT_TIMEOUT) { if (!atomic_read(&tool_used_cnt)) return; usleep_range(9900, 10000); loop_cnt++; } } static inline void wait_sriov_cfg_complete(struct hinic_pcidev *pci_adapter) { struct hinic_sriov_info *sriov_info; u32 loop_cnt = 0; sriov_info = &pci_adapter->sriov_info; set_bit(HINIC_FUNC_REMOVE, &sriov_info->state); usleep_range(9900, 10000); while (loop_cnt < HINIC_WAIT_SRIOV_CFG_TIMEOUT) { if (!test_bit(HINIC_SRIOV_ENABLE, &sriov_info->state) && !test_bit(HINIC_SRIOV_DISABLE, &sriov_info->state)) return; usleep_range(9900, 10000); loop_cnt++; } } static void hinic_remove(struct pci_dev *pdev) { struct hinic_pcidev *pci_adapter = pci_get_drvdata(pdev); if (!pci_adapter) return; sdk_info(&pdev->dev, "Pcie device remove begin\n"); #ifdef CONFIG_PCI_IOV if (pdev->is_virtfn && hinic_get_vf_load_state(pdev)) { pci_set_drvdata(pdev, NULL); kfree(pci_adapter); return; } #endif cancel_delayed_work_sync(&pci_adapter->slave_nic_init_dwork); flush_workqueue(pci_adapter->slave_nic_init_workq); destroy_workqueue(pci_adapter->slave_nic_init_workq); if (pci_adapter->init_state >= HINIC_INIT_STATE_HW_IF_INITED) hinic_detect_hw_present(pci_adapter->hwdev); switch (pci_adapter->init_state) { case HINIC_INIT_STATE_ALL_INITED: case HINIC_INIT_STATE_NIC_INITED: /* Don't support hotplug when SR-IOV is enabled now. * So disable SR-IOV capability as normal. */ if (!HINIC_FUNC_IS_VF(pci_adapter->hwdev)) { wait_sriov_cfg_complete(pci_adapter); hinic_pci_sriov_disable(pdev); } fallthrough; case HINIC_INIT_STATE_DBGTOOL_INITED: case HINIC_INIT_STATE_HWDEV_INITED: case HINIC_INIT_STATE_HW_PART_INITED: case HINIC_INIT_STATE_HW_IF_INITED: case HINIC_INIT_STATE_PCI_INITED: set_bit(HINIC_FUNC_IN_REMOVE, &pci_adapter->flag); lld_lock_chip_node(); cancel_work_sync(&pci_adapter->slave_nic_work); lld_unlock_chip_node(); wait_tool_unused(); if (pci_adapter->init_state >= HINIC_INIT_STATE_HW_IF_INITED) hinic_func_deinit(pdev); lld_lock_chip_node(); if (pci_adapter->init_state < HINIC_INIT_STATE_HW_IF_INITED) list_del(&pci_adapter->node); hinic_tool_k_uninit(); free_chip_node(pci_adapter); lld_unlock_chip_node(); unmapping_bar(pci_adapter); hinic_pci_deinit(pdev); break; default: break; } sdk_info(&pdev->dev, "Pcie device removed\n"); } static void slave_host_init_delay_work(struct work_struct *work) { struct delayed_work *delay = to_delayed_work(work); struct hinic_pcidev *pci_adapter = container_of(delay, struct hinic_pcidev, slave_nic_init_dwork); struct pci_dev *pdev = pci_adapter->pcidev; struct card_node *chip_node = pci_adapter->chip_node; int found = 0; struct hinic_pcidev *ppf_pcidev = NULL; int err; if (!hinic_get_master_host_mbox_enable(pci_adapter->hwdev)) { queue_delayed_work(pci_adapter->slave_nic_init_workq, &pci_adapter->slave_nic_init_dwork, HINIC_SLAVE_NIC_DELAY_TIME); return; } if (hinic_func_type(pci_adapter->hwdev) == TYPE_PPF) { err = hinic_func_init(pdev, pci_adapter); clear_bit(HINIC_FUNC_PRB_DELAY, &pci_adapter->flag); if (err) set_bit(HINIC_FUNC_PRB_ERR, &pci_adapter->flag); return; } /* Make sure the PPF must be the first one */ lld_dev_hold(); list_for_each_entry(ppf_pcidev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &ppf_pcidev->flag) || ppf_pcidev->init_state < HINIC_INIT_STATE_HW_IF_INITED) continue; if (hinic_func_type(ppf_pcidev->hwdev) == TYPE_PPF) { found = 1; break; } } lld_dev_put(); if (found && ppf_pcidev->init_state == HINIC_INIT_STATE_ALL_INITED) { err = hinic_func_init(pdev, pci_adapter); clear_bit(HINIC_FUNC_PRB_DELAY, &pci_adapter->flag); if (err) set_bit(HINIC_FUNC_PRB_ERR, &pci_adapter->flag); } else { queue_delayed_work(pci_adapter->slave_nic_init_workq, &pci_adapter->slave_nic_init_dwork, HINIC_SLAVE_NIC_DELAY_TIME); } } static int hinic_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct hinic_pcidev *pci_adapter; int err; sdk_info(&pdev->dev, "Pcie device probe begin\n"); err = hinic_pci_init(pdev); if (err) return err; #ifdef CONFIG_PCI_IOV if (pdev->is_virtfn && hinic_get_vf_load_state(pdev)) return 0; #endif pci_adapter = pci_get_drvdata(pdev); clear_bit(HINIC_FUNC_PRB_ERR, &pci_adapter->flag); clear_bit(HINIC_FUNC_PRB_DELAY, &pci_adapter->flag); err = mapping_bar(pdev, pci_adapter); if (err) { sdk_err(&pdev->dev, "Failed to map bar\n"); goto map_bar_failed; } pci_adapter->id = *id; INIT_WORK(&pci_adapter->slave_nic_work, slave_host_mgmt_work); pci_adapter->slave_nic_init_workq = create_singlethread_workqueue(HINIC_SLAVE_NIC_DELAY); if (!pci_adapter->slave_nic_init_workq) { sdk_err(&pdev->dev, "Failed to create work queue: %s\n", HINIC_SLAVE_NIC_DELAY); goto ceate_nic_delay_work_fail; } INIT_DELAYED_WORK(&pci_adapter->slave_nic_init_dwork, slave_host_init_delay_work); /* if chip information of pcie function exist, * add the function into chip */ lld_lock_chip_node(); err = alloc_chip_node(pci_adapter); if (err) { sdk_err(&pdev->dev, "Failed to add new chip node to global list\n"); goto alloc_chip_node_fail; } sscanf(pci_adapter->chip_node->chip_name, HINIC_CHIP_NAME "%d", &pci_adapter->card_id); err = hinic_tool_k_init(); if (err) { sdk_warn(&pdev->dev, "Failed to init nictool"); goto init_nictool_err; } list_add_tail(&pci_adapter->node, &pci_adapter->chip_node->func_list); lld_unlock_chip_node(); pci_adapter->init_state = HINIC_INIT_STATE_PCI_INITED; err = hinic_func_init(pdev, pci_adapter); if (err) goto func_init_err; return 0; func_init_err: if (!test_bit(HINIC_FUNC_PRB_DELAY, &pci_adapter->flag)) set_bit(HINIC_FUNC_PRB_ERR, &pci_adapter->flag); return 0; init_nictool_err: free_chip_node(pci_adapter); alloc_chip_node_fail: lld_unlock_chip_node(); ceate_nic_delay_work_fail: unmapping_bar(pci_adapter); map_bar_failed: hinic_pci_deinit(pdev); sdk_err(&pdev->dev, "Pcie device probe failed\n"); return err; } /*lint -save -e133 -e10*/ static const struct pci_device_id hinic_pci_table[] = { {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_PF), HINIC_BOARD_25GE}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_VF), 0}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_VF_HV), 0}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_SMTIO), HINIC_BOARD_PG_SM_25GE}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_PANGEA_100GE), HINIC_BOARD_PG_100GE}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_PANGEA_TP_10GE), HINIC_BOARD_PG_TP_10GE}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_KR_40GE), HINIC_BOARD_40GE}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_KR_100GE), HINIC_BOARD_100GE}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_KR_25GE), HINIC_BOARD_25GE}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_MULTI_HOST), HINIC_BOARD_25GE}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_100GE), HINIC_BOARD_100GE}, {PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_DUAL_25GE), HINIC_BOARD_25GE}, {0, 0} }; /*lint -restore*/ MODULE_DEVICE_TABLE(pci, hinic_pci_table); /** * hinic_io_error_detected - called when PCI error is detected * @pdev: Pointer to PCI device * @state: The current pci connection state * * This function is called after a PCI bus error affecting * this device has been detected. * * Since we only need error detecting not error handling, so we * always return PCI_ERS_RESULT_CAN_RECOVER to tell the AER * driver that we don't need reset(error handling). */ static pci_ers_result_t hinic_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { struct hinic_pcidev *pci_adapter; if (state == pci_channel_io_perm_failure) return PCI_ERS_RESULT_DISCONNECT; sdk_err(&pdev->dev, "Uncorrectable error detected, log and cleanup error status: 0x%08x\n", state); pci_adapter = pci_get_drvdata(pdev); if (pci_adapter) hinic_record_pcie_error(pci_adapter->hwdev); return PCI_ERS_RESULT_CAN_RECOVER; } static void hinic_shutdown(struct pci_dev *pdev) { struct hinic_pcidev *pci_adapter = pci_get_drvdata(pdev); sdk_info(&pdev->dev, "Shutdown device\n"); if (pci_adapter) hinic_shutdown_hwdev(pci_adapter->hwdev); pci_disable_device(pdev); if (pci_adapter) hinic_set_api_stop(pci_adapter->hwdev); } /* Cause we only need error detecting not error handling, so only error_detected * callback is enough. */ static struct pci_error_handlers hinic_err_handler = { .error_detected = hinic_io_error_detected, }; static struct pci_driver hinic_driver = { .name = HINIC_DRV_NAME, .id_table = hinic_pci_table, .probe = hinic_probe, .remove = hinic_remove, .shutdown = hinic_shutdown, .sriov_configure = hinic_pci_sriov_configure, .err_handler = &hinic_err_handler }; static int __init hinic_lld_init(void) { pr_info("%s - version %s\n", HINIC_DRV_DESC, HINIC_DRV_VERSION); memset(g_uld_info, 0, sizeof(g_uld_info)); atomic_set(&tool_used_cnt, 0); hinic_lld_lock_init(); /* register nic driver information first, and add net device in * nic_probe called by hinic_probe. */ hinic_register_uld(SERVICE_T_NIC, &nic_uld_info); return pci_register_driver(&hinic_driver); } static void __exit hinic_lld_exit(void) { pci_unregister_driver(&hinic_driver); hinic_unregister_uld(SERVICE_T_NIC); } module_init(hinic_lld_init); module_exit(hinic_lld_exit); int hinic_register_micro_log(struct hinic_micro_log_info *micro_log_info) { struct card_node *chip_node; struct hinic_pcidev *dev; if (!micro_log_info || !micro_log_info->init || !micro_log_info->deinit) { pr_err("Invalid information of micro log info to register\n"); return -EINVAL; } lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag) || dev->init_state < HINIC_INIT_STATE_HW_IF_INITED) continue; if (hinic_func_type(dev->hwdev) == TYPE_PPF) { if (micro_log_info->init(dev->hwdev)) { sdk_err(&dev->pcidev->dev, "micro log init failed\n"); continue; } } } } lld_dev_put(); pr_info("Register micro log succeed\n"); return 0; } EXPORT_SYMBOL(hinic_register_micro_log); void hinic_unregister_micro_log(struct hinic_micro_log_info *micro_log_info) { struct card_node *chip_node; struct hinic_pcidev *dev; if (!micro_log_info) return; lld_dev_hold(); list_for_each_entry(chip_node, &g_hinic_chip_list, node) { list_for_each_entry(dev, &chip_node->func_list, node) { if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag) || dev->init_state < HINIC_INIT_STATE_HW_IF_INITED) continue; if (hinic_func_type(dev->hwdev) == TYPE_PPF) micro_log_info->deinit(dev->hwdev); } } lld_dev_put(); pr_info("Unregister micro log succeed\n"); } EXPORT_SYMBOL(hinic_unregister_micro_log); hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_lld.h000066400000000000000000000107351502027332500212330ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_LLD_H_ #define HINIC_LLD_H_ #define HINIC_SLAVE_NIC_DELAY "hinic_slave_nic_delay" #define HINIC_SLAVE_NIC_DELAY_TIME (5 * HZ) struct hinic_lld_dev { struct pci_dev *pdev; void *hwdev; }; enum hinic_init_state { HINIC_INIT_STATE_NONE, HINIC_INIT_STATE_PCI_INITED, HINIC_INIT_STATE_HW_IF_INITED, HINIC_INIT_STATE_HW_PART_INITED, HINIC_INIT_STATE_HWDEV_INITED, HINIC_INIT_STATE_DBGTOOL_INITED, HINIC_INIT_STATE_NIC_INITED, HINIC_INIT_STATE_ALL_INITED, }; struct hinic_uld_info { /* uld_dev: should not return null even the function capability * is not support the up layer driver * uld_dev_name: NIC driver should copy net device name. * FC driver could copy fc device name. * other up layer driver don`t need copy anything */ int (*probe)(struct hinic_lld_dev *lld_dev, void **uld_dev, char *uld_dev_name); void (*remove)(struct hinic_lld_dev *lld_dev, void *uld_dev); int (*suspend)(struct hinic_lld_dev *lld_dev, void *uld_dev, pm_message_t state); int (*resume)(struct hinic_lld_dev *lld_dev, void *uld_dev); void (*event)(struct hinic_lld_dev *lld_dev, void *uld_dev, struct hinic_event_info *event); int (*ioctl)(void *uld_dev, u32 cmd, void *buf_in, u32 in_size, void *buf_out, u32 *out_size); }; /* Used for the ULD HiNIC PCIe driver registration interface, * the original interface is service_register_interface */ int hinic_register_uld(enum hinic_service_type uld_type, struct hinic_uld_info *uld_info); /* Used for the ULD HiNIC PCIe driver unregistration interface, * the original interface is service_unregister_interface */ void hinic_unregister_uld(enum hinic_service_type uld_type); void *hinic_get_ppf_uld_by_pdev(struct pci_dev *pdev, enum hinic_service_type type); /* used for TOE/IWARP */ struct net_device *hinic_get_netdev_by_lld(struct hinic_lld_dev *lld_dev); /* used for TOE/IWARP */ void *hinic_get_hwdev_by_netdev(struct net_device *netdev); struct net_device *hinic_get_netdev_by_pcidev(struct pci_dev *pdev); void *hinic_get_hwdev_by_ifname(char *ifname); int hinic_get_chip_name_by_hwdev(void *hwdev, char *ifname); void *hinic_get_uld_dev_by_ifname(char *ifname, enum hinic_service_type type); void *hinic_get_uld_by_chip_name(char *ifname, enum hinic_service_type type); int hinic_get_pf_uld_array(struct pci_dev *pdev, u32 *dev_cnt, void *array[]); int hinic_set_chip_cos_up_map(struct pci_dev *pdev, u8 *cos_up); int hinic_get_chip_cos_up_map(struct pci_dev *pdev, bool *is_setted, u8 *cos_up); void hinic_get_all_chip_id(void *card_id); void hinic_get_card_info(void *hwdev, void *bufin); int hinic_get_device_id(void *hwdev, u16 *dev_id); void hinic_get_fc_devname(char *devname); int hinic_get_pf_id(void *hwdev, u32 port_id, u32 *pf_id, u32 *isvalid); void hinic_tool_cnt_inc(void); void hinic_tool_cnt_dec(void); struct hinic_sriov_info; struct hinic_sriov_info *hinic_get_sriov_info_by_pcidev(struct pci_dev *pdev); /* for dpdk */ void *hinic_get_pci_dev(u16 bdf); void hinic_dpdk_pcie_remove(void *pdev); int hinic_dpdk_pcie_probe(void *pdev); int hinic_attach_nic(struct hinic_lld_dev *lld_dev); void hinic_detach_nic(struct hinic_lld_dev *lld_dev); int hinic_attach_roce(struct hinic_lld_dev *lld_dev); void hinic_detach_roce(struct hinic_lld_dev *lld_dev); int hinic_disable_nic_rss(struct hinic_lld_dev *lld_dev); int hinic_enable_nic_rss(struct hinic_lld_dev *lld_dev); int hinic_ovs_set_vf_nic_state(struct hinic_lld_dev *lld_dev, u16 vf_func_id, bool en); int hinic_ovs_set_vf_load_state(struct pci_dev *pdev); int hinic_get_self_test_result(char *ifname, u32 *result); enum hinic_init_state hinic_get_init_state_by_ifname(char *ifname); enum hinic_init_state hinic_get_init_state(struct pci_dev *pdev); extern struct hinic_uld_info g_uld_info[SERVICE_T_MAX]; struct pci_device_id *hinic_get_pci_device_id(struct pci_dev *pdev); bool hinic_is_in_host(void); bool hinic_is_valid_bar_addr(u64 offset); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_main.c000066400000000000000000002461551502027332500214060ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw_mgmt.h" #include "hinic_hw.h" #include "hinic_dbg.h" #include "hinic_nic_cfg.h" #include "hinic_nic_dev.h" #include "hinic_tx.h" #include "hinic_rx.h" #include "hinic_qp.h" #include "hinic_dcb.h" #include "hinic_lld.h" #include "hinic_sriov.h" #include "hinic_pci_id_tbl.h" static u16 num_qps; module_param(num_qps, ushort, 0444); MODULE_PARM_DESC(num_qps, "Number of Queue Pairs (default unset)"); static u16 ovs_num_qps = 16; module_param(ovs_num_qps, ushort, 0444); MODULE_PARM_DESC(ovs_num_qps, "Number of Queue Pairs in ovs mode (default=16)"); #define DEFAULT_POLL_WEIGHT 64 static unsigned int poll_weight = DEFAULT_POLL_WEIGHT; module_param(poll_weight, uint, 0444); MODULE_PARM_DESC(poll_weight, "Number packets for NAPI budget (default=64)"); #define HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT 2 #define HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG 32 #define HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG 7 /* suit for sdi3.0 vm mode, change this define for test best performance */ #define SDI_VM_PENDING_LIMT 2 #define SDI_VM_COALESCE_TIMER_CFG 16 #define SDI_VM_RX_PKT_RATE_HIGH 1000000 #define SDI_VM_RX_PKT_RATE_LOW 30000 #define SDI_VM_RX_USECS_HIGH 56 #define SDI_VM_RX_PENDING_LIMT_HIGH 20 #define SDI_VM_RX_USECS_LOW 16 #define SDI_VM_RX_PENDING_LIMT_LOW 2 /* if qp_coalesc_use_drv_params_switch !=0, use user setting params */ static unsigned char qp_coalesc_use_drv_params_switch; module_param(qp_coalesc_use_drv_params_switch, byte, 0444); MODULE_PARM_DESC(qp_coalesc_use_drv_params_switch, "QP MSI-X Interrupt coalescing parameter switch (default=0, not use drv parameter)"); static unsigned char qp_pending_limit = HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT; module_param(qp_pending_limit, byte, 0444); MODULE_PARM_DESC(qp_pending_limit, "QP MSI-X Interrupt coalescing parameter pending_limit (default=2)"); static unsigned char qp_coalesc_timer_cfg = HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG; module_param(qp_coalesc_timer_cfg, byte, 0444); MODULE_PARM_DESC(qp_coalesc_timer_cfg, "QP MSI-X Interrupt coalescing parameter coalesc_timer_cfg (default=32)"); /* For arm64 server, the best known configuration of lro max wqe number * is 4 (8K), for x86_64 server, it is 8 (16K). You can also * configure these values by hinicadm. */ static unsigned char set_max_wqe_num; module_param(set_max_wqe_num, byte, 0444); MODULE_PARM_DESC(set_max_wqe_num, "Set lro max wqe number, valid range is 1 - 32, default is 4(arm) / 8(x86)"); #define DEFAULT_RX_BUFF_LEN 2 u16 rx_buff = DEFAULT_RX_BUFF_LEN; module_param(rx_buff, ushort, 0444); MODULE_PARM_DESC(rx_buff, "Set rx_buff size, buffer len must be 2^n. 2 - 16, default is 2KB"); static u32 set_lro_timer; module_param(set_lro_timer, uint, 0444); MODULE_PARM_DESC(set_lro_timer, "Set lro timer in micro second, valid range is 1 - 1024, default is 16"); static unsigned char set_link_status_follow = HINIC_LINK_FOLLOW_STATUS_MAX; module_param(set_link_status_follow, byte, 0444); MODULE_PARM_DESC(set_link_status_follow, "Set link status follow port status. 0 - default, 1 - follow, 2 - separate, other - unset. (default unset)"); static unsigned int lro_replenish_thld = 256; module_param(lro_replenish_thld, uint, 0444); MODULE_PARM_DESC(lro_replenish_thld, "Number wqe for lro replenish buffer (default=256)"); static bool l2nic_interrupt_switch = true; module_param(l2nic_interrupt_switch, bool, 0644); MODULE_PARM_DESC(l2nic_interrupt_switch, "Control whether execute l2nic io interrupt switch or not, default is true"); static unsigned char lro_en_status = HINIC_LRO_STATUS_UNSET; module_param(lro_en_status, byte, 0444); MODULE_PARM_DESC(lro_en_status, "lro enable status. 0 - disable, 1 - enable, other - unset. (default unset)"); static unsigned char qp_pending_limit_low = HINIC_RX_PENDING_LIMIT_LOW; module_param(qp_pending_limit_low, byte, 0444); MODULE_PARM_DESC(qp_pending_limit_low, "MSI-X adaptive low coalesce pending limit, range is 0 - 255"); static unsigned char qp_coalesc_timer_low = HINIC_RX_COAL_TIME_LOW; module_param(qp_coalesc_timer_low, byte, 0444); MODULE_PARM_DESC(qp_coalesc_timer_low, "MSI-X adaptive low coalesce time, range is 0 - 255"); static unsigned char qp_pending_limit_high = HINIC_RX_PENDING_LIMIT_HIGH; module_param(qp_pending_limit_high, byte, 0444); MODULE_PARM_DESC(qp_pending_limit_high, "MSI-X adaptive high coalesce pending limit, range is 0 - 255"); static unsigned char qp_coalesc_timer_high = HINIC_RX_COAL_TIME_HIGH; module_param(qp_coalesc_timer_high, byte, 0444); MODULE_PARM_DESC(qp_coalesc_timer_high, "MSI-X adaptive high coalesce time, range is 0 - 255"); #define HINIC_NIC_DEV_WQ_NAME "hinic_nic_dev_wq" #define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_LINK | \ NETIF_MSG_RX_ERR) #define QID_MASKED(q_id, nic_dev) ((q_id) & ((nic_dev)->num_qps - 1)) #define VLAN_BITMAP_BYTE_SIZE(nic_dev) (sizeof(*(nic_dev)->vlan_bitmap)) #define VLAN_BITMAP_BITS_SIZE(nic_dev) (VLAN_BITMAP_BYTE_SIZE(nic_dev) * 8) #define VLAN_NUM_BITMAPS(nic_dev) (VLAN_N_VID / \ VLAN_BITMAP_BITS_SIZE(nic_dev)) #define VLAN_BITMAP_SIZE(nic_dev) (VLAN_N_VID / \ VLAN_BITMAP_BYTE_SIZE(nic_dev)) #define VID_LINE(nic_dev, vid) ((vid) / VLAN_BITMAP_BITS_SIZE(nic_dev)) #define VID_COL(nic_dev, vid) ((vid) & (VLAN_BITMAP_BITS_SIZE(nic_dev) - 1)) enum hinic_rx_mod { HINIC_RX_MODE_UC = 1 << 0, HINIC_RX_MODE_MC = 1 << 1, HINIC_RX_MODE_BC = 1 << 2, HINIC_RX_MODE_MC_ALL = 1 << 3, HINIC_RX_MODE_PROMISC = 1 << 4, }; enum hinic_rx_buff_len { RX_BUFF_VALID_2KB = 2, RX_BUFF_VALID_4KB = 4, RX_BUFF_VALID_8KB = 8, RX_BUFF_VALID_16KB = 16, }; #define HINIC_AVG_PKT_SMALL 256U #define HINIC_MODERATONE_DELAY HZ #define CONVERT_UNIT 1024 #define HINIC_MAX_VLAN_DEPTH_OFFLOAD_SUPPORT 2 #define HINIC_VLAN_CLEAR_OFFLOAD (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \ NETIF_F_SCTP_CRC | NETIF_F_RXCSUM | \ NETIF_F_ALL_TSO) int hinic_netdev_event(struct notifier_block *notifier, unsigned long event, void *ptr) { struct net_device *ndev = netdev_notifier_info_to_dev(ptr); struct net_device *real_dev, *ret; struct hinic_nic_dev *nic_dev; u16 vlan_depth; if (!is_vlan_dev(ndev)) return NOTIFY_DONE; dev_hold(ndev); switch (event) { case NETDEV_REGISTER: real_dev = vlan_dev_real_dev(ndev); nic_dev = hinic_get_uld_dev_by_ifname(real_dev->name, SERVICE_T_NIC); if (!nic_dev) goto out; vlan_depth = 1; ret = vlan_dev_priv(ndev)->real_dev; while (is_vlan_dev(ret)) { ret = vlan_dev_priv(ret)->real_dev; vlan_depth++; } if (vlan_depth == HINIC_MAX_VLAN_DEPTH_OFFLOAD_SUPPORT) { ndev->vlan_features &= (~HINIC_VLAN_CLEAR_OFFLOAD); } else if (vlan_depth > HINIC_MAX_VLAN_DEPTH_OFFLOAD_SUPPORT) { ndev->hw_features &= (~HINIC_VLAN_CLEAR_OFFLOAD); ndev->features &= (~HINIC_VLAN_CLEAR_OFFLOAD); } break; default: break; }; out: dev_put(ndev); return NOTIFY_DONE; } /* used for netdev notifier register/unregister */ DEFINE_MUTEX(g_hinic_netdev_notifiers_mutex); static int hinic_netdev_notifiers_ref_cnt; static struct notifier_block hinic_netdev_notifier = { .notifier_call = hinic_netdev_event, }; static void hinic_register_notifier(struct hinic_nic_dev *nic_dev) { int err; mutex_lock(&g_hinic_netdev_notifiers_mutex); hinic_netdev_notifiers_ref_cnt++; if (hinic_netdev_notifiers_ref_cnt == 1) { err = register_netdevice_notifier(&hinic_netdev_notifier); if (err) { hinic_info(nic_dev, drv, "Register netdevice notifier failed, err: %d\n", err); hinic_netdev_notifiers_ref_cnt--; } } mutex_unlock(&g_hinic_netdev_notifiers_mutex); } static void hinic_unregister_notifier(struct hinic_nic_dev *nic_dev) { mutex_lock(&g_hinic_netdev_notifiers_mutex); if (hinic_netdev_notifiers_ref_cnt == 1) unregister_netdevice_notifier(&hinic_netdev_notifier); if (hinic_netdev_notifiers_ref_cnt) hinic_netdev_notifiers_ref_cnt--; mutex_unlock(&g_hinic_netdev_notifiers_mutex); } void hinic_link_status_change(struct hinic_nic_dev *nic_dev, bool status) { struct net_device *netdev = nic_dev->netdev; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags) || test_bit(HINIC_LP_TEST, &nic_dev->flags)) return; if (status) { if (netif_carrier_ok(netdev)) return; nic_dev->link_status = status; netif_carrier_on(netdev); nicif_info(nic_dev, link, netdev, "Link is up\n"); } else { if (!netif_carrier_ok(netdev)) return; nic_dev->link_status = status; netif_carrier_off(netdev); nicif_info(nic_dev, link, netdev, "Link is down\n"); } } static void hinic_heart_lost(struct hinic_nic_dev *nic_dev) { nic_dev->heart_status = false; } static int hinic_setup_qps_resources(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; int err; err = hinic_setup_all_tx_resources(netdev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to create Tx queues\n"); return err; } err = hinic_setup_all_rx_resources(netdev, nic_dev->qps_irq_info); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to create Rx queues\n"); goto create_rxqs_err; } return 0; create_rxqs_err: hinic_free_all_tx_resources(netdev); return err; } static int hinic_configure(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; int err; /* rx rss init */ err = hinic_rx_configure(netdev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to configure rx\n"); return err; } return 0; } static void hinic_remove_configure(struct hinic_nic_dev *nic_dev) { hinic_rx_remove_configure(nic_dev->netdev); } static void hinic_setup_dcb_qps(struct hinic_nic_dev *nic_dev, u16 max_qps) { struct net_device *netdev = nic_dev->netdev; u16 num_rss; u8 num_tcs; u8 i; if (!test_bit(HINIC_DCB_ENABLE, &nic_dev->flags) || !test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) return; num_tcs = (u8)netdev_get_num_tc(netdev); /* For now, we don't support to change num_tcs */ if (num_tcs != nic_dev->max_cos || max_qps < num_tcs) { nicif_err(nic_dev, drv, netdev, "Invalid num_tcs: %d or num_qps: %d, disable DCB\n", num_tcs, max_qps); netdev_reset_tc(netdev); clear_bit(HINIC_DCB_ENABLE, &nic_dev->flags); /* if we can't enable rss or get enough num_qps, * need to sync default configure to hw */ hinic_configure_dcb(netdev); } else { /* We bind sq with cos but not tc */ num_rss = (u16)(max_qps / nic_dev->max_cos); num_rss = min_t(u16, num_rss, nic_dev->rss_limit); for (i = 0; i < nic_dev->max_cos; i++) netdev_set_tc_queue(netdev, i, num_rss, (u16)(num_rss * i)); nic_dev->num_rss = num_rss; nic_dev->num_qps = (u16)(num_tcs * num_rss); } } /* determin num_qps from rss_tmpl_id/irq_num/dcb_en */ static int hinic_setup_num_qps(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; u32 irq_size; u16 resp_irq_num, i; int err; if (test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) { nic_dev->num_rss = nic_dev->rss_limit; nic_dev->num_qps = nic_dev->rss_limit; } else { nic_dev->num_rss = 0; nic_dev->num_qps = 1; } hinic_setup_dcb_qps(nic_dev, nic_dev->max_qps); irq_size = sizeof(*nic_dev->qps_irq_info) * nic_dev->num_qps; if (!irq_size) { nicif_err(nic_dev, drv, netdev, "Cannot allocate zero size entries\n"); return -EINVAL; } nic_dev->qps_irq_info = kzalloc(irq_size, GFP_KERNEL); if (!nic_dev->qps_irq_info) return -ENOMEM; err = hinic_alloc_irqs(nic_dev->hwdev, SERVICE_T_NIC, nic_dev->num_qps, nic_dev->qps_irq_info, &resp_irq_num); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to alloc irqs\n"); kfree(nic_dev->qps_irq_info); return err; } /* available irq number is less than rq numbers, adjust rq numbers */ if (resp_irq_num < nic_dev->num_qps) { nic_dev->num_qps = resp_irq_num; nic_dev->num_rss = nic_dev->num_qps; hinic_setup_dcb_qps(nic_dev, nic_dev->num_qps); nicif_warn(nic_dev, drv, netdev, "Can not get enough irqs, adjust num_qps to %d\n", nic_dev->num_qps); /* after adjust num_qps, free the remaind irq */ for (i = nic_dev->num_qps; i < resp_irq_num; i++) hinic_free_irq(nic_dev->hwdev, SERVICE_T_NIC, nic_dev->qps_irq_info[i].irq_id); } nicif_info(nic_dev, drv, netdev, "Finally num_qps: %d, num_rss: %d\n", nic_dev->num_qps, nic_dev->num_rss); return 0; } static void hinic_destroy_num_qps(struct hinic_nic_dev *nic_dev) { u16 i; for (i = 0; i < nic_dev->num_qps; i++) hinic_free_irq(nic_dev->hwdev, SERVICE_T_NIC, nic_dev->qps_irq_info[i].irq_id); kfree(nic_dev->qps_irq_info); } static int hinic_poll(struct napi_struct *napi, int budget) { int tx_pkts, rx_pkts; struct hinic_irq *irq_cfg = container_of(napi, struct hinic_irq, napi); struct hinic_nic_dev *nic_dev = netdev_priv(irq_cfg->netdev); rx_pkts = hinic_rx_poll(irq_cfg->rxq, budget); tx_pkts = hinic_tx_poll(irq_cfg->txq, budget); if (tx_pkts >= budget || rx_pkts >= budget) return budget; set_bit(HINIC_RESEND_ON, &irq_cfg->intr_flag); rx_pkts += hinic_rx_poll(irq_cfg->rxq, budget - rx_pkts); tx_pkts += hinic_tx_poll(irq_cfg->txq, budget - tx_pkts); if (rx_pkts >= budget || tx_pkts >= budget) { clear_bit(HINIC_RESEND_ON, &irq_cfg->intr_flag); return budget; } napi_complete(napi); if (!test_and_set_bit(HINIC_INTR_ON, &irq_cfg->intr_flag)) { if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) hinic_set_msix_state(nic_dev->hwdev, irq_cfg->msix_entry_idx, HINIC_MSIX_ENABLE); else if (!nic_dev->in_vm && (hinic_get_func_mode(nic_dev->hwdev) == FUNC_MOD_NORMAL_HOST)) enable_irq(irq_cfg->irq_id); } return max(tx_pkts, rx_pkts); } static void qp_add_napi(struct hinic_irq *irq_cfg) { struct hinic_nic_dev *nic_dev = netdev_priv(irq_cfg->netdev); netif_napi_add_tx_weight(nic_dev->netdev, &irq_cfg->napi, hinic_poll, nic_dev->poll_weight); napi_enable(&irq_cfg->napi); } static void qp_del_napi(struct hinic_irq *irq_cfg) { napi_disable(&irq_cfg->napi); netif_napi_del(&irq_cfg->napi); } static irqreturn_t qp_irq(int irq, void *data) { struct hinic_irq *irq_cfg = (struct hinic_irq *)data; struct hinic_nic_dev *nic_dev = netdev_priv(irq_cfg->netdev); u16 msix_entry_idx = irq_cfg->msix_entry_idx; if (napi_schedule_prep(&irq_cfg->napi)) { if (l2nic_interrupt_switch) { /* Disable the interrupt until napi will be completed */ if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) { hinic_set_msix_state(nic_dev->hwdev, msix_entry_idx, HINIC_MSIX_DISABLE); } else if (!nic_dev->in_vm && (hinic_get_func_mode(nic_dev->hwdev) == FUNC_MOD_NORMAL_HOST)) { disable_irq_nosync(irq_cfg->irq_id); } clear_bit(HINIC_INTR_ON, &irq_cfg->intr_flag); } hinic_misx_intr_clear_resend_bit(nic_dev->hwdev, msix_entry_idx, 1); clear_bit(HINIC_RESEND_ON, &irq_cfg->intr_flag); __napi_schedule(&irq_cfg->napi); } else if (!test_bit(HINIC_RESEND_ON, &irq_cfg->intr_flag)) { hinic_misx_intr_clear_resend_bit(nic_dev->hwdev, msix_entry_idx, 1); } return IRQ_HANDLED; } static int hinic_request_irq(struct hinic_irq *irq_cfg, u16 q_id) { struct hinic_nic_dev *nic_dev = netdev_priv(irq_cfg->netdev); struct nic_interrupt_info info = {0}; int err; qp_add_napi(irq_cfg); info.msix_index = irq_cfg->msix_entry_idx; info.lli_set = 0; info.interrupt_coalesc_set = 1; info.pending_limt = nic_dev->intr_coalesce[q_id].pending_limt; info.coalesc_timer_cfg = nic_dev->intr_coalesce[q_id].coalesce_timer_cfg; info.resend_timer_cfg = nic_dev->intr_coalesce[q_id].resend_timer_cfg; nic_dev->rxqs[q_id].last_coalesc_timer_cfg = nic_dev->intr_coalesce[q_id].coalesce_timer_cfg; nic_dev->rxqs[q_id].last_pending_limt = nic_dev->intr_coalesce[q_id].pending_limt; err = hinic_set_interrupt_cfg(nic_dev->hwdev, info); if (err) { nicif_err(nic_dev, drv, irq_cfg->netdev, "Failed to set RX interrupt coalescing attribute\n"); qp_del_napi(irq_cfg); return err; } err = request_irq(irq_cfg->irq_id, &qp_irq, 0, irq_cfg->irq_name, irq_cfg); if (err) { nicif_err(nic_dev, drv, irq_cfg->netdev, "Failed to request Rx irq\n"); qp_del_napi(irq_cfg); return err; } /* assign the mask for this irq */ irq_set_affinity_hint(irq_cfg->irq_id, &irq_cfg->affinity_mask); return 0; } static int set_interrupt_moder(struct hinic_nic_dev *nic_dev, u16 q_id, u8 coalesc_timer_cfg, u8 pending_limt) { struct nic_interrupt_info interrupt_info = {0}; int err; if (coalesc_timer_cfg == nic_dev->rxqs[q_id].last_coalesc_timer_cfg && pending_limt == nic_dev->rxqs[q_id].last_pending_limt) return 0; /* netdev not running or qp not in using, * don't need to set coalesce to hw */ if (!test_bit(HINIC_INTF_UP, &nic_dev->flags) || q_id >= nic_dev->num_qps) return 0; interrupt_info.lli_set = 0; interrupt_info.interrupt_coalesc_set = 1; interrupt_info.coalesc_timer_cfg = coalesc_timer_cfg; interrupt_info.pending_limt = pending_limt; interrupt_info.msix_index = nic_dev->irq_cfg[q_id].msix_entry_idx; interrupt_info.resend_timer_cfg = nic_dev->intr_coalesce[q_id].resend_timer_cfg; err = hinic_set_interrupt_cfg(nic_dev->hwdev, interrupt_info); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed modifying moderation for Queue: %d\n", q_id); } else { nic_dev->rxqs[q_id].last_coalesc_timer_cfg = coalesc_timer_cfg; nic_dev->rxqs[q_id].last_pending_limt = pending_limt; } return err; } static void __calc_coal_para(struct hinic_nic_dev *nic_dev, struct hinic_intr_coal_info *q_coal, u64 rate, u8 *coalesc_timer_cfg, u8 *pending_limt) { if (rate < q_coal->pkt_rate_low) { *coalesc_timer_cfg = q_coal->rx_usecs_low; *pending_limt = q_coal->rx_pending_limt_low; } else if (rate > q_coal->pkt_rate_high) { *coalesc_timer_cfg = q_coal->rx_usecs_high; *pending_limt = q_coal->rx_pending_limt_high; } else { *coalesc_timer_cfg = (u8)((rate - q_coal->pkt_rate_low) * (q_coal->rx_usecs_high - q_coal->rx_usecs_low) / (q_coal->pkt_rate_high - q_coal->pkt_rate_low) + q_coal->rx_usecs_low); if (nic_dev->in_vm) *pending_limt = (u8)((rate - q_coal->pkt_rate_low) * (q_coal->rx_pending_limt_high - q_coal->rx_pending_limt_low) / (q_coal->pkt_rate_high - q_coal->pkt_rate_low) + q_coal->rx_pending_limt_low); else *pending_limt = q_coal->rx_pending_limt_low; } } static void update_queue_coal(struct hinic_nic_dev *nic_dev, u16 qid, u64 rate, u64 avg_pkt_size, u64 tx_rate) { struct hinic_intr_coal_info *q_coal; u8 coalesc_timer_cfg, pending_limt; q_coal = &nic_dev->intr_coalesce[qid]; if ((rate > HINIC_RX_RATE_THRESH && avg_pkt_size > HINIC_AVG_PKT_SMALL) || (nic_dev->in_vm && rate > HINIC_RX_RATE_THRESH)) { __calc_coal_para(nic_dev, q_coal, rate, &coalesc_timer_cfg, &pending_limt); } else { coalesc_timer_cfg = HINIC_LOWEST_LATENCY; pending_limt = q_coal->rx_pending_limt_low; } set_interrupt_moder(nic_dev, qid, coalesc_timer_cfg, pending_limt); } #define SDI_VM_PPS_3W 30000 #define SDI_VM_PPS_5W 50000 #define SDI_VM_BPS_100MB 12500000 #define SDI_VM_BPS_1GB 125000000 static void update_queue_coal_sdi_vm(struct hinic_nic_dev *nic_dev, u16 qid, u64 rx_pps, u64 rx_bps, u64 tx_pps, u64 tx_bps) { struct hinic_intr_coal_info *q_coal = NULL; u8 coalesc_timer_cfg, pending_limt; q_coal = &nic_dev->intr_coalesce[qid]; if (qp_coalesc_use_drv_params_switch == 0) { if (rx_pps < SDI_VM_PPS_3W && tx_pps < SDI_VM_PPS_3W && rx_bps < SDI_VM_BPS_100MB && tx_bps < SDI_VM_BPS_100MB) { set_interrupt_moder(nic_dev, qid, 0, 0); } else if (tx_pps > SDI_VM_PPS_3W && tx_pps < SDI_VM_PPS_5W && tx_bps > SDI_VM_BPS_1GB) { set_interrupt_moder(nic_dev, qid, 7, 7); } else { __calc_coal_para(nic_dev, q_coal, rx_pps, &coalesc_timer_cfg, &pending_limt); set_interrupt_moder(nic_dev, qid, coalesc_timer_cfg, pending_limt); } } else { __calc_coal_para(nic_dev, q_coal, rx_pps, &coalesc_timer_cfg, &pending_limt); set_interrupt_moder(nic_dev, qid, coalesc_timer_cfg, pending_limt); } } static void hinic_auto_moderation_work(struct work_struct *work) { struct delayed_work *delay = to_delayed_work(work); struct hinic_nic_dev *nic_dev = container_of(delay, struct hinic_nic_dev, moderation_task); unsigned long period = (unsigned long)(jiffies - nic_dev->last_moder_jiffies); u64 rx_packets, rx_bytes, rx_pkt_diff, rate, avg_pkt_size; u64 tx_packets, tx_bytes, tx_pkt_diff, tx_rate, rx_bps, tx_bps; u16 qid; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) return; queue_delayed_work(nic_dev->workq, &nic_dev->moderation_task, HINIC_MODERATONE_DELAY); if (!nic_dev->adaptive_rx_coal || !period) return; for (qid = 0; qid < nic_dev->num_qps; qid++) { rx_packets = nic_dev->rxqs[qid].rxq_stats.packets; rx_bytes = nic_dev->rxqs[qid].rxq_stats.bytes; tx_packets = nic_dev->txqs[qid].txq_stats.packets; tx_bytes = nic_dev->txqs[qid].txq_stats.bytes; rx_pkt_diff = rx_packets - nic_dev->rxqs[qid].last_moder_packets; avg_pkt_size = rx_pkt_diff ? ((unsigned long)(rx_bytes - nic_dev->rxqs[qid].last_moder_bytes)) / rx_pkt_diff : 0; rate = rx_pkt_diff * HZ / period; tx_pkt_diff = tx_packets - nic_dev->txqs[qid].last_moder_packets; tx_rate = tx_pkt_diff * HZ / period; rx_bps = (unsigned long)(rx_bytes - nic_dev->rxqs[qid].last_moder_bytes) * HZ / period; tx_bps = (unsigned long)(tx_bytes - nic_dev->txqs[qid].last_moder_bytes) * HZ / period; if ((nic_dev->is_vm_slave && nic_dev->in_vm) || nic_dev->is_bm_slave) { update_queue_coal_sdi_vm(nic_dev, qid, rate, rx_bps, tx_rate, tx_bps); } else { update_queue_coal(nic_dev, qid, rate, avg_pkt_size, tx_rate); } nic_dev->rxqs[qid].last_moder_packets = rx_packets; nic_dev->rxqs[qid].last_moder_bytes = rx_bytes; nic_dev->txqs[qid].last_moder_packets = tx_packets; nic_dev->txqs[qid].last_moder_bytes = tx_bytes; } nic_dev->last_moder_jiffies = jiffies; } static void hinic_release_irq(struct hinic_irq *irq_cfg) { irq_set_affinity_hint(irq_cfg->irq_id, NULL); synchronize_irq(irq_cfg->irq_id); free_irq(irq_cfg->irq_id, irq_cfg); qp_del_napi(irq_cfg); } static int hinic_qps_irq_init(struct hinic_nic_dev *nic_dev) { struct pci_dev *pdev = nic_dev->pdev; struct irq_info *qp_irq_info; struct hinic_irq *irq_cfg; u16 q_id, i; u32 local_cpu; int err; nic_dev->irq_cfg = kcalloc(nic_dev->num_qps, sizeof(*nic_dev->irq_cfg), GFP_KERNEL); if (!nic_dev->irq_cfg) return -ENOMEM; for (q_id = 0; q_id < nic_dev->num_qps; q_id++) { qp_irq_info = &nic_dev->qps_irq_info[q_id]; irq_cfg = &nic_dev->irq_cfg[q_id]; irq_cfg->irq_id = qp_irq_info->irq_id; irq_cfg->msix_entry_idx = qp_irq_info->msix_entry_idx; irq_cfg->netdev = nic_dev->netdev; irq_cfg->txq = &nic_dev->txqs[q_id]; irq_cfg->rxq = &nic_dev->rxqs[q_id]; nic_dev->rxqs[q_id].irq_cfg = irq_cfg; if (nic_dev->force_affinity) { irq_cfg->affinity_mask = nic_dev->affinity_mask; } else { local_cpu = cpumask_local_spread(q_id, dev_to_node(&pdev->dev)); cpumask_set_cpu(local_cpu, &irq_cfg->affinity_mask); } err = snprintf(irq_cfg->irq_name, sizeof(irq_cfg->irq_name), "%s_qp%d", nic_dev->netdev->name, q_id); if (err <= 0 || err >= (int)sizeof(irq_cfg->irq_name)) { nic_err(&pdev->dev, "Failed snprintf irq_name, function return(%d) and dest_len(%d)\n", err, (int)sizeof(irq_cfg->irq_name)); goto req_tx_irq_err; } set_bit(HINIC_INTR_ON, &irq_cfg->intr_flag); err = hinic_request_irq(irq_cfg, q_id); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to request Rx irq\n"); goto req_tx_irq_err; } hinic_set_msix_state(nic_dev->hwdev, irq_cfg->msix_entry_idx, HINIC_MSIX_ENABLE); } INIT_DELAYED_WORK(&nic_dev->moderation_task, hinic_auto_moderation_work); return 0; req_tx_irq_err: for (i = 0; i < q_id; i++) { hinic_set_msix_state(nic_dev->hwdev, nic_dev->irq_cfg[i].msix_entry_idx, HINIC_MSIX_DISABLE); hinic_release_irq(&nic_dev->irq_cfg[i]); } kfree(nic_dev->irq_cfg); return err; } static void hinic_qps_irq_deinit(struct hinic_nic_dev *nic_dev) { u16 q_id; for (q_id = 0; q_id < nic_dev->num_qps; q_id++) { hinic_set_msix_state(nic_dev->hwdev, nic_dev->irq_cfg[q_id].msix_entry_idx, HINIC_MSIX_DISABLE); hinic_release_irq(&nic_dev->irq_cfg[q_id]); } kfree(nic_dev->irq_cfg); } int hinic_force_port_disable(struct hinic_nic_dev *nic_dev) { int err; down(&nic_dev->port_state_sem); err = hinic_set_port_enable(nic_dev->hwdev, false); if (!err) nic_dev->force_port_disable = true; up(&nic_dev->port_state_sem); return err; } int hinic_force_set_port_state(struct hinic_nic_dev *nic_dev, bool enable) { int err = 0; down(&nic_dev->port_state_sem); nic_dev->force_port_disable = false; err = hinic_set_port_enable(nic_dev->hwdev, enable); up(&nic_dev->port_state_sem); return err; } int hinic_maybe_set_port_state(struct hinic_nic_dev *nic_dev, bool enable) { int err; down(&nic_dev->port_state_sem); /* Do nothing when force disable * Port will disable when call force port disable * and should not enable port when in force mode */ if (nic_dev->force_port_disable) { up(&nic_dev->port_state_sem); return 0; } err = hinic_set_port_enable(nic_dev->hwdev, enable); up(&nic_dev->port_state_sem); return err; } static void hinic_print_link_message(struct hinic_nic_dev *nic_dev, u8 link_status) { if (nic_dev->link_status == link_status) return; nic_dev->link_status = link_status; nicif_info(nic_dev, link, nic_dev->netdev, "Link is %s\n", (link_status ? "up" : "down")); } int hinic_open(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u8 link_status = 0; int err; if (test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_info(nic_dev, drv, netdev, "Netdev already open, do nothing\n"); return 0; } err = hinic_setup_num_qps(nic_dev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to setup num_qps\n"); return err; } err = hinic_create_qps(nic_dev->hwdev, nic_dev->num_qps, nic_dev->sq_depth, nic_dev->rq_depth, nic_dev->qps_irq_info, HINIC_MAX_SQ_BUFDESCS); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to create queue pairs\n"); goto create_qps_err; } err = hinic_setup_qps_resources(nic_dev); if (err) goto setup_qps_resources_err; err = hinic_init_qp_ctxts(nic_dev->hwdev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to init qp ctxts\n"); goto init_qp_ctxts_err; } err = hinic_set_port_mtu(nic_dev->hwdev, netdev->mtu); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to set mtu\n"); goto mtu_err; } err = hinic_configure(nic_dev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to configure txrx\n"); goto cfg_err; } err = hinic_qps_irq_init(nic_dev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to qps irq init\n"); goto qps_irqs_init_err; } err = hinic_set_vport_enable(nic_dev->hwdev, true); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to enable vport\n"); goto vport_enable_err; } err = hinic_maybe_set_port_state(nic_dev, true); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to enable port\n"); goto port_enable_err; } set_bit(HINIC_INTF_UP, &nic_dev->flags); netif_set_real_num_tx_queues(netdev, nic_dev->num_qps); netif_set_real_num_rx_queues(netdev, nic_dev->num_qps); netif_tx_wake_all_queues(netdev); queue_delayed_work(nic_dev->workq, &nic_dev->moderation_task, HINIC_MODERATONE_DELAY); err = hinic_get_link_state(nic_dev->hwdev, &link_status); if (!err && link_status) { hinic_update_pf_bw(nic_dev->hwdev); netif_carrier_on(netdev); } hinic_print_link_message(nic_dev, link_status); if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) hinic_notify_all_vfs_link_changed(nic_dev->hwdev, link_status); nicif_info(nic_dev, drv, nic_dev->netdev, "Netdev is up\n"); return 0; port_enable_err: hinic_set_vport_enable(nic_dev->hwdev, false); vport_enable_err: hinic_flush_sq_res(nic_dev->hwdev); /* After set vport disable 100ms, no packets will be send to host */ msleep(100); hinic_qps_irq_deinit(nic_dev); qps_irqs_init_err: hinic_remove_configure(nic_dev); cfg_err: mtu_err: hinic_free_qp_ctxts(nic_dev->hwdev); init_qp_ctxts_err: hinic_free_all_rx_resources(netdev); hinic_free_all_tx_resources(netdev); setup_qps_resources_err: hinic_free_qps(nic_dev->hwdev); create_qps_err: hinic_destroy_num_qps(nic_dev); return err; } int hinic_close(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (!test_and_clear_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_info(nic_dev, drv, netdev, "Netdev already close, do nothing\n"); return 0; } netif_carrier_off(netdev); netif_tx_disable(netdev); cancel_delayed_work_sync(&nic_dev->moderation_task); if (hinic_get_chip_present_flag(nic_dev->hwdev)) { if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) hinic_notify_all_vfs_link_changed(nic_dev->hwdev, 0); hinic_maybe_set_port_state(nic_dev, false); hinic_set_vport_enable(nic_dev->hwdev, false); hinic_flush_txqs(netdev); hinic_flush_sq_res(nic_dev->hwdev); /* After set vport disable 100ms, * no packets will be send to host */ msleep(100); } hinic_qps_irq_deinit(nic_dev); hinic_remove_configure(nic_dev); if (hinic_get_chip_present_flag(nic_dev->hwdev)) hinic_free_qp_ctxts(nic_dev->hwdev); mutex_lock(&nic_dev->nic_mutex); hinic_free_all_rx_resources(netdev); hinic_free_all_tx_resources(netdev); hinic_free_qps(nic_dev->hwdev); hinic_destroy_num_qps(nic_dev); mutex_unlock(&nic_dev->nic_mutex); nicif_info(nic_dev, drv, nic_dev->netdev, "Netdev is down\n"); return 0; } static inline u32 calc_toeplitz_rss(u32 sip, u32 dip, u32 sport, u32 dport, const u32 *rss_key) { u32 i, port, rss = 0; port = (sport << 16) | dport; /* The key - SIP, DIP, SPORT, DPORT */ for (i = 0; i < 32; i++) if (sip & ((u32)1 << (u32)(31 - i))) rss ^= (rss_key[0] << i) | (u32)((u64)rss_key[1] >> (32 - i)); for (i = 0; i < 32; i++) if (dip & ((u32)1 << (u32)(31 - i))) rss ^= (rss_key[1] << i) | (u32)((u64)rss_key[2] >> (32 - i)); for (i = 0; i < 32; i++) if (port & ((u32)1 << (u32)(31 - i))) rss ^= (rss_key[2] << i) | (u32)((u64)rss_key[3] >> (32 - i)); return rss; } static u16 select_queue_by_toeplitz(struct net_device *dev, struct sk_buff *skb, unsigned int num_tx_queues) { struct hinic_nic_dev *nic_dev = netdev_priv(dev); struct tcphdr *tcphdr; struct iphdr *iphdr; u32 hash = 0; if (skb_rx_queue_recorded(skb)) { hash = skb_get_rx_queue(skb); while (unlikely(hash >= num_tx_queues)) hash -= num_tx_queues; return (u16)hash; } /*lint -save -e778*/ if (vlan_get_protocol(skb) == htons(ETH_P_IP)) { iphdr = ip_hdr(skb); if (iphdr->protocol == IPPROTO_UDP || iphdr->protocol == IPPROTO_TCP) { tcphdr = tcp_hdr(skb); hash = calc_toeplitz_rss(ntohl(iphdr->daddr), ntohl(iphdr->saddr), ntohs(tcphdr->dest), ntohs(tcphdr->source), nic_dev->rss_hkey_user_be); } } /*lint -restore*/ return (u16)nic_dev->rss_indir_user[hash & 0xFF]; } static u16 hinic_select_queue(struct net_device *netdev, struct sk_buff *skb, struct net_device *sb_dev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); skb->priority = skb->vlan_tci >> VLAN_PRIO_SHIFT; if (netdev_get_num_tc(netdev) || !nic_dev->rss_hkey_user_be) goto fallback; if (nic_dev->rss_hash_engine == HINIC_RSS_HASH_ENGINE_TYPE_TOEP && test_bit(HINIC_SAME_RXTX, &nic_dev->flags)) return select_queue_by_toeplitz(netdev, skb, netdev->real_num_tx_queues); fallback: return netdev_pick_tx(netdev, skb, NULL); } static void hinic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_txq_stats *txq_stats; struct hinic_rxq_stats *rxq_stats; struct hinic_txq *txq; struct hinic_rxq *rxq; u64 bytes, packets, dropped, errors; unsigned int start; int i; bytes = 0; packets = 0; dropped = 0; for (i = 0; i < nic_dev->max_qps; i++) { if (!nic_dev->txqs) break; txq = &nic_dev->txqs[i]; txq_stats = &txq->txq_stats; do { start = u64_stats_fetch_begin(&txq_stats->syncp); bytes += txq_stats->bytes; packets += txq_stats->packets; dropped += txq_stats->dropped; } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); } stats->tx_packets = packets; stats->tx_bytes = bytes; stats->tx_dropped = dropped; bytes = 0; packets = 0; errors = 0; dropped = 0; for (i = 0; i < nic_dev->max_qps; i++) { if (!nic_dev->rxqs) break; rxq = &nic_dev->rxqs[i]; rxq_stats = &rxq->rxq_stats; do { start = u64_stats_fetch_begin(&rxq_stats->syncp); bytes += rxq_stats->bytes; packets += rxq_stats->packets; errors += rxq_stats->csum_errors + rxq_stats->other_errors; dropped += rxq_stats->dropped; } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); } stats->rx_packets = packets; stats->rx_bytes = bytes; stats->rx_errors = errors; stats->rx_dropped = dropped; } static void hinic_tx_timeout(struct net_device *netdev, unsigned int txqueue) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 msix_idx; u8 q_id; HINIC_NIC_STATS_INC(nic_dev, netdev_tx_timeout); nicif_err(nic_dev, drv, netdev, "Tx timeout\n"); for (q_id = 0; q_id < nic_dev->num_qps; q_id++) { if (!netif_xmit_stopped(netdev_get_tx_queue(netdev, q_id))) continue; msix_idx = nic_dev->irq_cfg[q_id].msix_entry_idx; nicif_info(nic_dev, drv, netdev, "txq%d: sw_pi: %d, hw_ci: %d, sw_ci: %d, napi->state: 0x%lx, msix mask: %d, intr_flag: 0x%lx\n", q_id, hinic_dbg_get_sq_pi(nic_dev->hwdev, q_id), hinic_get_sq_hw_ci(nic_dev->hwdev, q_id), hinic_get_sq_local_ci(nic_dev->hwdev, q_id), nic_dev->irq_cfg[q_id].napi.state, hinic_get_msix_state(nic_dev->hwdev, msix_idx), nic_dev->irq_cfg[q_id].intr_flag); } } static int hinic_change_mtu(struct net_device *netdev, int new_mtu) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u32 mtu = (u32)new_mtu; int err = 0; err = hinic_set_port_mtu(nic_dev->hwdev, mtu); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to change port mtu to %d\n", new_mtu); } else { nicif_info(nic_dev, drv, nic_dev->netdev, "Change mtu from %d to %d\n", netdev->mtu, new_mtu); netdev->mtu = mtu; } return err; } static int hinic_set_mac_addr(struct net_device *netdev, void *addr) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct sockaddr *saddr = addr; u16 func_id; int err; if (!FUNC_SUPPORT_CHANGE_MAC(nic_dev->hwdev)) { nicif_warn(nic_dev, drv, netdev, "Current function don't support to set mac\n"); return -EOPNOTSUPP; } if (!is_valid_ether_addr(saddr->sa_data)) return -EADDRNOTAVAIL; if (ether_addr_equal(netdev->dev_addr, saddr->sa_data)) { nicif_info(nic_dev, drv, netdev, "Already using mac address %pM\n", saddr->sa_data); return 0; } err = hinic_global_func_id_get(nic_dev->hwdev, &func_id); if (err) return err; err = hinic_update_mac(nic_dev->hwdev, netdev->dev_addr, saddr->sa_data, 0, func_id); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to update mac, err: %d\n", err); return err == HINIC_PF_SET_VF_ALREADY ? -EPERM : err; } eth_hw_addr_set(netdev, saddr->sa_data); nicif_info(nic_dev, drv, netdev, "Set new mac address %pM\n", saddr->sa_data); return 0; } static int hinic_vlan_rx_add_vid(struct net_device *netdev, __always_unused __be16 proto, u16 vid) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); unsigned long *vlan_bitmap = nic_dev->vlan_bitmap; u16 func_id; u32 col, line; int err; col = VID_COL(nic_dev, vid); line = VID_LINE(nic_dev, vid); err = hinic_global_func_id_get(nic_dev->hwdev, &func_id); if (err) goto end; err = hinic_add_vlan(nic_dev->hwdev, vid, func_id); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to add vlan%d\n", vid); goto end; } set_bit(col, &vlan_bitmap[line]); nicif_info(nic_dev, drv, netdev, "Add vlan %d\n", vid); end: return err; } static int hinic_vlan_rx_kill_vid(struct net_device *netdev, __always_unused __be16 proto, u16 vid) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); unsigned long *vlan_bitmap = nic_dev->vlan_bitmap; u16 func_id; int err, col, line; col = VID_COL(nic_dev, vid); line = VID_LINE(nic_dev, vid); err = hinic_global_func_id_get(nic_dev->hwdev, &func_id); if (err) goto end; err = hinic_del_vlan(nic_dev->hwdev, vid, func_id); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to delete vlan\n"); goto end; } clear_bit(col, &vlan_bitmap[line]); nicif_info(nic_dev, drv, netdev, "Remove vlan %d\n", vid); end: return err; } #define FEATURES_OP_STR(op) ((op) ? "Enable" : "Disable") static int set_feature_tso(struct hinic_nic_dev *nic_dev, netdev_features_t wanted_features, netdev_features_t features, netdev_features_t *failed_features) { netdev_features_t changed = wanted_features ^ features; bool en = !!(wanted_features & NETIF_F_TSO); int err; if (!(changed & NETIF_F_TSO)) return 0; err = hinic_set_tx_tso(nic_dev->hwdev, en); if (err) { hinic_err(nic_dev, drv, "%s tso failed\n", FEATURES_OP_STR(en)); *failed_features |= NETIF_F_TSO; } else { hinic_info(nic_dev, drv, "%s tso success\n", FEATURES_OP_STR(en)); } return err; } static int set_feature_cvlan(struct hinic_nic_dev *nic_dev, netdev_features_t wanted_features, netdev_features_t features, netdev_features_t *failed_features) { netdev_features_t changed = wanted_features ^ features; netdev_features_t vlan_feature = NETIF_F_HW_VLAN_CTAG_RX; bool en = !!(wanted_features & vlan_feature); int err; if (!(changed & vlan_feature)) return 0; err = hinic_set_rx_vlan_offload(nic_dev->hwdev, en); if (err) { hinic_err(nic_dev, drv, "%s rxvlan failed\n", FEATURES_OP_STR(en)); *failed_features |= vlan_feature; } else { hinic_info(nic_dev, drv, "%s rxvlan success\n", FEATURES_OP_STR(en)); } return err; } static int set_feature_rxcsum(struct hinic_nic_dev *nic_dev, netdev_features_t wanted_features, netdev_features_t features, netdev_features_t *failed_features) { netdev_features_t changed = wanted_features ^ features; bool en = !!(wanted_features & NETIF_F_RXCSUM); int err; if (!(changed & NETIF_F_RXCSUM)) return 0; /* hw should always enable rx csum */ err = hinic_set_rx_csum_offload(nic_dev->hwdev, HINIC_RX_CSUM_OFFLOAD_EN); if (err) { hinic_err(nic_dev, drv, "%s rx csum failed\n", FEATURES_OP_STR(en)); *failed_features |= NETIF_F_RXCSUM; } else { hinic_info(nic_dev, drv, "%s rx csum success\n", FEATURES_OP_STR(en)); } return err; } static int set_feature_lro(struct hinic_nic_dev *nic_dev, netdev_features_t wanted_features, netdev_features_t features, netdev_features_t *failed_features) { netdev_features_t changed = wanted_features ^ features; bool en = !!(wanted_features & NETIF_F_LRO); u32 lro_timer, lro_buf_size; int err; if (!(changed & NETIF_F_LRO)) return 0; lro_timer = nic_dev->adaptive_cfg.lro.timer; lro_buf_size = nic_dev->adaptive_cfg.lro.buffer_size; err = hinic_set_rx_lro_state(nic_dev->hwdev, en, lro_timer, lro_buf_size / nic_dev->rx_buff_len); if (err) { hinic_err(nic_dev, drv, "%s lro failed\n", FEATURES_OP_STR(en)); *failed_features |= NETIF_F_LRO; } else { hinic_info(nic_dev, drv, "%s lro success\n", FEATURES_OP_STR(en)); } return err; } static int set_features(struct hinic_nic_dev *nic_dev, netdev_features_t pre_features, netdev_features_t features) { netdev_features_t failed_features = 0; u32 err; err = (u32)set_feature_tso(nic_dev, features, pre_features, &failed_features); err |= (u32)set_feature_cvlan(nic_dev, features, pre_features, &failed_features); err |= (u32)set_feature_rxcsum(nic_dev, features, pre_features, &failed_features); err |= (u32)set_feature_lro(nic_dev, features, pre_features, &failed_features); if (err) { nic_dev->netdev->features = features ^ failed_features; return -EIO; } return 0; } static int hinic_set_features(struct net_device *netdev, netdev_features_t features) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); return set_features(nic_dev, nic_dev->netdev->features, features); } static netdev_features_t hinic_fix_features(struct net_device *netdev, netdev_features_t features) { /* If Rx checksum is disabled, then LRO should also be disabled */ if (!(features & NETIF_F_RXCSUM)) features &= ~NETIF_F_LRO; return features; } static int hinic_set_default_hw_feature(struct hinic_nic_dev *nic_dev) { int err; if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) { if (FUNC_SUPPORT_DCB(nic_dev->hwdev)) { err = hinic_dcb_reset_hw_config(nic_dev); if (err) { nic_err(&nic_dev->pdev->dev, "Failed to reset hw dcb configuration\n"); return -EFAULT; } } if (FUNC_SUPPORT_PORT_SETTING(nic_dev->hwdev)) { err = hinic_reset_port_link_cfg(nic_dev->hwdev); if (err) return -EFAULT; } hinic_set_anti_attack(nic_dev->hwdev, true); if (set_link_status_follow < HINIC_LINK_FOLLOW_STATUS_MAX && FUNC_SUPPORT_PORT_SETTING(nic_dev->hwdev)) { err = hinic_set_link_status_follow(nic_dev->hwdev, set_link_status_follow); if (err == HINIC_MGMT_CMD_UNSUPPORTED) nic_warn(&nic_dev->pdev->dev, "Current version of firmware don't support to set link status follow port status\n"); } } /* enable all hw features in netdev->features */ return set_features(nic_dev, ~nic_dev->netdev->features, nic_dev->netdev->features); } static int hinic_setup_tc_mqprio(struct net_device *dev, struct tc_mqprio_qopt *mqprio) { mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; return hinic_setup_tc(dev, mqprio->num_tc); } static int __hinic_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data) { switch (type) { case TC_SETUP_QDISC_MQPRIO: return hinic_setup_tc_mqprio(dev, type_data); default: return -EOPNOTSUPP; } } #ifdef CONFIG_NET_POLL_CONTROLLER static void hinic_netpoll(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 i; for (i = 0; i < nic_dev->num_qps; i++) napi_schedule(&nic_dev->irq_cfg[i].napi); } #endif /* CONFIG_NET_POLL_CONTROLLER */ static int hinic_uc_sync(struct net_device *netdev, u8 *addr) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 func_id; int err; err = hinic_global_func_id_get(nic_dev->hwdev, &func_id); if (err) return err; err = hinic_set_mac(nic_dev->hwdev, addr, 0, func_id); return err; } static int hinic_uc_unsync(struct net_device *netdev, u8 *addr) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 func_id; int err; /* The addr is in use */ if (ether_addr_equal(addr, netdev->dev_addr)) return 0; err = hinic_global_func_id_get(nic_dev->hwdev, &func_id); if (err) return err; err = hinic_del_mac(nic_dev->hwdev, addr, 0, func_id); return err; } static void hinic_clean_mac_list_filter(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; struct hinic_mac_filter *f, *ftmp; list_for_each_entry_safe(f, ftmp, &nic_dev->uc_filter_list, list) { if (f->state == HINIC_MAC_HW_SYNCED) hinic_uc_unsync(netdev, f->addr); list_del(&f->list); kfree(f); } list_for_each_entry_safe(f, ftmp, &nic_dev->mc_filter_list, list) { if (f->state == HINIC_MAC_HW_SYNCED) hinic_uc_unsync(netdev, f->addr); list_del(&f->list); kfree(f); } } static struct hinic_mac_filter *hinic_find_mac(struct list_head *filter_list, u8 *addr) { struct hinic_mac_filter *f; list_for_each_entry(f, filter_list, list) { if (ether_addr_equal(addr, f->addr)) return f; } return NULL; } static struct hinic_mac_filter *hinic_add_filter(struct hinic_nic_dev *nic_dev, struct list_head *mac_filter_list, u8 *addr) { struct hinic_mac_filter *f; f = kzalloc(sizeof(*f), GFP_ATOMIC); if (!f) goto out; memcpy(f->addr, addr, ETH_ALEN); INIT_LIST_HEAD(&f->list); list_add_tail(&f->list, mac_filter_list); f->state = HINIC_MAC_WAIT_HW_SYNC; set_bit(HINIC_MAC_FILTER_CHANGED, &nic_dev->flags); out: return f; } static void hinic_del_filter(struct hinic_nic_dev *nic_dev, struct hinic_mac_filter *f) { set_bit(HINIC_MAC_FILTER_CHANGED, &nic_dev->flags); if (f->state == HINIC_MAC_WAIT_HW_SYNC) { /* have not added to hw, delete it directly */ list_del(&f->list); kfree(f); return; } f->state = HINIC_MAC_WAIT_HW_UNSYNC; } static struct hinic_mac_filter *hinic_mac_filter_entry_clone(struct hinic_mac_filter *src) { struct hinic_mac_filter *f; f = kzalloc(sizeof(*f), GFP_ATOMIC); if (!f) return NULL; *f = *src; INIT_LIST_HEAD(&f->list); return f; } static void hinic_undo_del_filter_entries(struct list_head *filter_list, struct list_head *from) { struct hinic_mac_filter *f, *ftmp; list_for_each_entry_safe(f, ftmp, from, list) { if (hinic_find_mac(filter_list, f->addr)) continue; if (f->state == HINIC_MAC_HW_SYNCED) f->state = HINIC_MAC_WAIT_HW_UNSYNC; list_move_tail(&f->list, filter_list); } } static void hinic_undo_add_filter_entries(struct list_head *filter_list, struct list_head *from) { struct hinic_mac_filter *f, *ftmp, *tmp; list_for_each_entry_safe(f, ftmp, from, list) { tmp = hinic_find_mac(filter_list, f->addr); if (tmp && tmp->state == HINIC_MAC_HW_SYNCED) tmp->state = HINIC_MAC_WAIT_HW_SYNC; } } static void hinic_cleanup_filter_list(struct list_head *head) { struct hinic_mac_filter *f, *ftmp; list_for_each_entry_safe(f, ftmp, head, list) { list_del(&f->list); kfree(f); } } static int hinic_mac_filter_sync_hw(struct hinic_nic_dev *nic_dev, struct list_head *del_list, struct list_head *add_list) { struct net_device *netdev = nic_dev->netdev; struct hinic_mac_filter *f, *ftmp; int err = 0, add_count = 0; if (!list_empty(del_list)) { list_for_each_entry_safe(f, ftmp, del_list, list) { err = hinic_uc_unsync(netdev, f->addr); if (err) { /* ignore errors when delete mac */ nic_err(&nic_dev->pdev->dev, "Failed to delete mac\n"); } list_del(&f->list); kfree(f); } } if (!list_empty(add_list)) { list_for_each_entry_safe(f, ftmp, add_list, list) { err = hinic_uc_sync(netdev, f->addr); if (err) { nic_err(&nic_dev->pdev->dev, "Failed to add mac\n"); return err; } add_count++; list_del(&f->list); kfree(f); } } return add_count; } static int hinic_mac_filter_sync(struct hinic_nic_dev *nic_dev, struct list_head *mac_filter_list, bool uc) { struct net_device *netdev = nic_dev->netdev; struct list_head tmp_del_list, tmp_add_list; struct hinic_mac_filter *f, *ftmp, *fclone; int err = 0, add_count = 0; INIT_LIST_HEAD(&tmp_del_list); INIT_LIST_HEAD(&tmp_add_list); list_for_each_entry_safe(f, ftmp, mac_filter_list, list) { if (f->state != HINIC_MAC_WAIT_HW_UNSYNC) continue; f->state = HINIC_MAC_HW_UNSYNCED; list_move_tail(&f->list, &tmp_del_list); } list_for_each_entry_safe(f, ftmp, mac_filter_list, list) { if (f->state != HINIC_MAC_WAIT_HW_SYNC) continue; fclone = hinic_mac_filter_entry_clone(f); if (!fclone) { err = -ENOMEM; break; } f->state = HINIC_MAC_HW_SYNCED; list_add_tail(&fclone->list, &tmp_add_list); } if (err) { hinic_undo_del_filter_entries(mac_filter_list, &tmp_del_list); hinic_undo_add_filter_entries(mac_filter_list, &tmp_add_list); nicif_err(nic_dev, drv, netdev, "Failed to clone mac_filter_entry\n"); } if (err) { hinic_cleanup_filter_list(&tmp_del_list); hinic_cleanup_filter_list(&tmp_add_list); return -ENOMEM; } add_count = hinic_mac_filter_sync_hw(nic_dev, &tmp_del_list, &tmp_add_list); if (list_empty(&tmp_add_list)) return add_count; /* there are errors when add mac to hw, delete all mac in hw */ hinic_undo_add_filter_entries(mac_filter_list, &tmp_add_list); /* VF don't support to enter promisc mode, * so we can't delete any other uc mac */ if (!HINIC_FUNC_IS_VF(nic_dev->hwdev) || !uc) { list_for_each_entry_safe(f, ftmp, mac_filter_list, list) { if (f->state != HINIC_MAC_HW_SYNCED) continue; fclone = hinic_mac_filter_entry_clone(f); if (!fclone) break; f->state = HINIC_MAC_WAIT_HW_SYNC; list_add_tail(&fclone->list, &tmp_del_list); } } hinic_cleanup_filter_list(&tmp_add_list); hinic_mac_filter_sync_hw(nic_dev, &tmp_del_list, &tmp_add_list); /* need to enter promisc/allmulti mode */ return -ENOMEM; } static void hinic_mac_filter_sync_all(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; int add_count; if (test_bit(HINIC_MAC_FILTER_CHANGED, &nic_dev->flags)) { clear_bit(HINIC_MAC_FILTER_CHANGED, &nic_dev->flags); add_count = hinic_mac_filter_sync(nic_dev, &nic_dev->uc_filter_list, true); if (add_count < 0 && !HINIC_FUNC_IS_VF(nic_dev->hwdev)) { set_bit(HINIC_PROMISC_FORCE_ON, &nic_dev->rx_mod_state); nicif_info(nic_dev, drv, netdev, "Promisc mode forced on\n"); } else if (add_count) { clear_bit(HINIC_PROMISC_FORCE_ON, &nic_dev->rx_mod_state); } add_count = hinic_mac_filter_sync(nic_dev, &nic_dev->mc_filter_list, false); if (add_count < 0) { set_bit(HINIC_ALLMULTI_FORCE_ON, &nic_dev->rx_mod_state); nicif_info(nic_dev, drv, netdev, "All multicast mode forced on\n"); } else if (add_count) { clear_bit(HINIC_ALLMULTI_FORCE_ON, &nic_dev->rx_mod_state); } } } #define HINIC_DEFAULT_RX_MODE (HINIC_RX_MODE_UC | HINIC_RX_MODE_MC | \ HINIC_RX_MODE_BC) static void hinic_update_mac_filter(struct hinic_nic_dev *nic_dev, struct netdev_hw_addr_list *src_list, struct list_head *filter_list) { struct netdev_hw_addr *ha; struct hinic_mac_filter *f, *ftmp, *filter; /* add addr if not already in the filter list */ netif_addr_lock_bh(nic_dev->netdev); netdev_hw_addr_list_for_each(ha, src_list) { filter = hinic_find_mac(filter_list, ha->addr); if (!filter) hinic_add_filter(nic_dev, filter_list, ha->addr); else if (filter->state == HINIC_MAC_WAIT_HW_UNSYNC) filter->state = HINIC_MAC_HW_SYNCED; } netif_addr_unlock_bh(nic_dev->netdev); /* delete addr if not in netdev list */ list_for_each_entry_safe(f, ftmp, filter_list, list) { bool found = false; netif_addr_lock_bh(nic_dev->netdev); netdev_hw_addr_list_for_each(ha, src_list) if (ether_addr_equal(ha->addr, f->addr)) { found = true; break; } netif_addr_unlock_bh(nic_dev->netdev); if (found) continue; hinic_del_filter(nic_dev, f); } } static void __update_mac_filter(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; if (test_and_clear_bit(HINIC_UPDATE_MAC_FILTER, &nic_dev->flags)) { hinic_update_mac_filter(nic_dev, &netdev->uc, &nic_dev->uc_filter_list); hinic_update_mac_filter(nic_dev, &netdev->mc, &nic_dev->mc_filter_list); } } static void hinic_set_rx_mode_work(struct work_struct *work) { struct hinic_nic_dev *nic_dev = container_of(work, struct hinic_nic_dev, rx_mode_work); struct net_device *netdev = nic_dev->netdev; int promisc_en = 0, allmulti_en = 0; int err = 0; __update_mac_filter(nic_dev); hinic_mac_filter_sync_all(nic_dev); /* VF don't support to enter promisc mode */ if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) { promisc_en = !!(netdev->flags & IFF_PROMISC) || test_bit(HINIC_PROMISC_FORCE_ON, &nic_dev->rx_mod_state); } allmulti_en = !!(netdev->flags & IFF_ALLMULTI) || test_bit(HINIC_ALLMULTI_FORCE_ON, &nic_dev->rx_mod_state); if (promisc_en != test_bit(HINIC_HW_PROMISC_ON, &nic_dev->rx_mod_state) || allmulti_en != test_bit(HINIC_HW_ALLMULTI_ON, &nic_dev->rx_mod_state)) { enum hinic_rx_mod rx_mod = HINIC_DEFAULT_RX_MODE; rx_mod |= (promisc_en ? HINIC_RX_MODE_PROMISC : 0); rx_mod |= (allmulti_en ? HINIC_RX_MODE_MC_ALL : 0); /* FOR DEBUG */ if (promisc_en != test_bit(HINIC_HW_PROMISC_ON, &nic_dev->rx_mod_state)) nicif_info(nic_dev, drv, netdev, "%s promisc mode\n", promisc_en ? "Enter" : "Left"); if (allmulti_en != test_bit(HINIC_HW_ALLMULTI_ON, &nic_dev->rx_mod_state)) nicif_info(nic_dev, drv, netdev, "%s all_multi mode\n", allmulti_en ? "Enter" : "Left"); err = hinic_set_rx_mode(nic_dev->hwdev, rx_mod); if (!err) { promisc_en ? set_bit(HINIC_HW_PROMISC_ON, &nic_dev->rx_mod_state) : clear_bit(HINIC_HW_PROMISC_ON, &nic_dev->rx_mod_state); allmulti_en ? set_bit(HINIC_HW_ALLMULTI_ON, &nic_dev->rx_mod_state) : clear_bit(HINIC_HW_ALLMULTI_ON, &nic_dev->rx_mod_state); } else { nicif_err(nic_dev, drv, netdev, "Failed to set rx_mode\n"); } } } static void hinic_nic_set_rx_mode(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (netdev_uc_count(netdev) != nic_dev->netdev_uc_cnt || netdev_mc_count(netdev) != nic_dev->netdev_mc_cnt) { set_bit(HINIC_UPDATE_MAC_FILTER, &nic_dev->flags); nic_dev->netdev_uc_cnt = netdev_uc_count(netdev); nic_dev->netdev_mc_cnt = netdev_mc_count(netdev); } if (FUNC_SUPPORT_RX_MODE(nic_dev->hwdev)) queue_work(nic_dev->workq, &nic_dev->rx_mode_work); } static const struct net_device_ops hinic_netdev_ops = { .ndo_open = hinic_open, .ndo_stop = hinic_close, .ndo_start_xmit = hinic_xmit_frame, .ndo_get_stats64 = hinic_get_stats64, .ndo_tx_timeout = hinic_tx_timeout, .ndo_select_queue = hinic_select_queue, .ndo_change_mtu = hinic_change_mtu, .ndo_set_mac_address = hinic_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_vlan_rx_add_vid = hinic_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = hinic_vlan_rx_kill_vid, .ndo_set_vf_mac = hinic_ndo_set_vf_mac, .ndo_set_vf_vlan = hinic_ndo_set_vf_vlan, .ndo_set_vf_rate = hinic_ndo_set_vf_bw, .ndo_set_vf_spoofchk = hinic_ndo_set_vf_spoofchk, .ndo_set_vf_trust = hinic_ndo_set_vf_trust, .ndo_get_vf_config = hinic_ndo_get_vf_config, .ndo_setup_tc = __hinic_setup_tc, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = hinic_netpoll, #endif /* CONFIG_NET_POLL_CONTROLLER */ .ndo_set_rx_mode = hinic_nic_set_rx_mode, .ndo_set_vf_link_state = hinic_ndo_set_vf_link_state, .ndo_fix_features = hinic_fix_features, .ndo_set_features = hinic_set_features, }; static const struct net_device_ops hinicvf_netdev_ops = { .ndo_open = hinic_open, .ndo_stop = hinic_close, .ndo_start_xmit = hinic_xmit_frame, .ndo_get_stats64 = hinic_get_stats64, .ndo_tx_timeout = hinic_tx_timeout, .ndo_select_queue = hinic_select_queue, .ndo_change_mtu = hinic_change_mtu, .ndo_set_mac_address = hinic_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_vlan_rx_add_vid = hinic_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = hinic_vlan_rx_kill_vid, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = hinic_netpoll, #endif /* CONFIG_NET_POLL_CONTROLLER */ .ndo_set_rx_mode = hinic_nic_set_rx_mode, .ndo_fix_features = hinic_fix_features, .ndo_set_features = hinic_set_features, }; static void netdev_feature_init(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); netdev_features_t hw_features; netdev->features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_RXCSUM; if (FUNC_SUPPORT_SCTP_CRC(nic_dev->hwdev)) netdev->features |= NETIF_F_SCTP_CRC; netdev->vlan_features = netdev->features; if (FUNC_SUPPORT_ENCAP_TSO_CSUM(nic_dev->hwdev)) netdev->features |= NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM; if (FUNC_SUPPORT_HW_VLAN(nic_dev->hwdev)) { netdev->features |= NETIF_F_HW_VLAN_CTAG_TX; netdev->features |= NETIF_F_HW_VLAN_CTAG_RX; } /* copy netdev features into list of user selectable features */ hw_features = netdev->hw_features; hw_features |= netdev->features; if (FUNC_SUPPORT_LRO(nic_dev->hwdev)) { /* LRO is disable in default, only set hw features */ hw_features |= NETIF_F_LRO; /* Enable LRO */ if (nic_dev->adaptive_cfg.lro.enable && !HINIC_FUNC_IS_VF(nic_dev->hwdev)) netdev->features |= NETIF_F_LRO; } netdev->hw_features = hw_features; /* Set after hw_features because this could not be part of * hw_features */ netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; netdev->priv_flags |= IFF_UNICAST_FLT; if (FUNC_SUPPORT_ENCAP_TSO_CSUM(nic_dev->hwdev)) { netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SCTP_CRC | NETIF_F_SG; netdev->hw_enc_features |= NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN | NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_UDP_TUNNEL; } } #define MOD_PARA_VALIDATE_NUM_QPS(nic_dev, num_qps, out_qps) { \ if ((num_qps) > (nic_dev)->max_qps) \ nic_warn(&(nic_dev)->pdev->dev, \ "Module Parameter %s value %d is out of range, "\ "Maximum value for the device: %d, using %d\n",\ #num_qps, num_qps, (nic_dev)->max_qps, \ (nic_dev)->max_qps); \ if (!(num_qps) || (num_qps) > (nic_dev)->max_qps) \ out_qps = (nic_dev)->max_qps; \ else \ out_qps = num_qps; \ } static void hinic_try_to_enable_rss(struct hinic_nic_dev *nic_dev) { u8 prio_tc[HINIC_DCB_UP_MAX] = {0}; int i, node, err = 0; u16 num_cpus = 0; enum hinic_service_mode service_mode = hinic_get_service_mode(nic_dev->hwdev); nic_dev->max_qps = hinic_func_max_nic_qnum(nic_dev->hwdev); if (nic_dev->max_qps <= 1) { clear_bit(HINIC_RSS_ENABLE, &nic_dev->flags); nic_dev->rss_limit = nic_dev->max_qps; nic_dev->num_qps = nic_dev->max_qps; nic_dev->num_rss = nic_dev->max_qps; return; } err = hinic_rss_template_alloc(nic_dev->hwdev, &nic_dev->rss_tmpl_idx); if (err) { if (err == -ENOSPC) nic_warn(&nic_dev->pdev->dev, "Failed to alloc tmpl_idx for rss, table is full\n"); else nic_err(&nic_dev->pdev->dev, "Failed to alloc tmpl_idx for rss, can't enable rss for this function\n"); clear_bit(HINIC_RSS_ENABLE, &nic_dev->flags); nic_dev->max_qps = 1; nic_dev->rss_limit = nic_dev->max_qps; nic_dev->num_qps = nic_dev->max_qps; nic_dev->num_rss = nic_dev->max_qps; return; } set_bit(HINIC_RSS_ENABLE, &nic_dev->flags); nic_dev->max_qps = hinic_func_max_nic_qnum(nic_dev->hwdev); MOD_PARA_VALIDATE_NUM_QPS(nic_dev, num_qps, nic_dev->num_qps); /* To reduce memory footprint in ovs mode. * VF can't get board info correctly with early pf driver. */ if ((hinic_get_func_mode(nic_dev->hwdev) == FUNC_MOD_NORMAL_HOST) && service_mode == HINIC_WORK_MODE_OVS && hinic_func_type(nic_dev->hwdev) != TYPE_VF) MOD_PARA_VALIDATE_NUM_QPS(nic_dev, ovs_num_qps, nic_dev->num_qps); for (i = 0; i < (int)num_online_cpus(); i++) { node = (int)cpu_to_node(i); if (node == dev_to_node(&nic_dev->pdev->dev)) num_cpus++; } if (!num_cpus) num_cpus = (u16)num_online_cpus(); nic_dev->num_qps = min_t(u16, nic_dev->num_qps, num_cpus); nic_dev->rss_limit = nic_dev->num_qps; nic_dev->num_rss = nic_dev->num_qps; hinic_init_rss_parameters(nic_dev->netdev); hinic_set_hw_rss_parameters(nic_dev->netdev, 0, 0, prio_tc); } static int hinic_sw_init(struct hinic_nic_dev *adapter) { struct net_device *netdev = adapter->netdev; u8 mac[ETH_ALEN]; u16 func_id; int err = 0; sema_init(&adapter->port_state_sem, 1); err = hinic_dcb_init(adapter); if (err) { nic_err(&adapter->pdev->dev, "Failed to init dcb\n"); return -EFAULT; } if (HINIC_FUNC_IS_VF(adapter->hwdev)) { err = hinic_sq_cos_mapping(netdev); if (err) { nic_err(&adapter->pdev->dev, "Failed to set sq_cos_mapping\n"); return -EFAULT; } } adapter->sq_depth = HINIC_SQ_DEPTH; adapter->rq_depth = HINIC_RQ_DEPTH; hinic_try_to_enable_rss(adapter); err = hinic_get_default_mac(adapter->hwdev, mac); if (err) { nic_err(&adapter->pdev->dev, "Failed to get MAC address\n"); goto get_mac_err; } eth_hw_addr_set(netdev, mac); if (!is_valid_ether_addr(netdev->dev_addr)) { if (!HINIC_FUNC_IS_VF(adapter->hwdev)) { nic_err(&adapter->pdev->dev, "Invalid MAC address\n"); err = -EIO; goto err_mac; } nic_info(&adapter->pdev->dev, "Invalid MAC address %pM, using random\n", netdev->dev_addr); eth_hw_addr_random(netdev); } err = hinic_global_func_id_get(adapter->hwdev, &func_id); if (err) goto func_id_err; err = hinic_set_mac(adapter->hwdev, netdev->dev_addr, 0, func_id); /* When this is VF driver, we must consider that PF has already set VF * MAC, and we can't consider this condition is error status during * driver probe procedure. */ if (err && err != HINIC_PF_SET_VF_ALREADY) { nic_err(&adapter->pdev->dev, "Failed to set default MAC\n"); goto set_mac_err; } /* MTU range: 256 - 9600 */ netdev->min_mtu = HINIC_MIN_MTU_SIZE; netdev->max_mtu = HINIC_MAX_JUMBO_FRAME_SIZE; return 0; set_mac_err: func_id_err: err_mac: get_mac_err: if (test_bit(HINIC_RSS_ENABLE, &adapter->flags)) hinic_rss_template_free(adapter->hwdev, adapter->rss_tmpl_idx); return err; } static void hinic_assign_netdev_ops(struct hinic_nic_dev *adapter) { if (!HINIC_FUNC_IS_VF(adapter->hwdev)) { adapter->netdev->netdev_ops = &hinic_netdev_ops; if (FUNC_SUPPORT_DCB(adapter->hwdev)) adapter->netdev->dcbnl_ops = &hinic_dcbnl_ops; hinic_set_ethtool_ops(adapter->netdev); } else { adapter->netdev->netdev_ops = &hinicvf_netdev_ops; hinicvf_set_ethtool_ops(adapter->netdev); } adapter->netdev->watchdog_timeo = 5 * HZ; } #define HINIC_DFT_PG_10GE_TXRX_MSIX_PENDING_LIMIT 1 #define HINIC_DFT_PG_10GE_TXRX_MSIX_COALESC_TIMER 1 #define HINIC_DFT_PG_25GE_TXRX_MSIX_PENDING_LIMIT 2 #define HINIC_DFT_PG_25GE_TXRX_MSIX_COALESC_TIMER 2 #define HINIC_DFT_PG_ARM_25GE_TXRX_MSIX_COALESC_TIMER 3 #define HINIC_DFT_PG_100GE_TXRX_MSIX_PENDING_LIMIT 2 #define HINIC_DFT_PG_100GE_TXRX_MSIX_COALESC_TIMER 2 #define HINIC_DFT_PG_ARM_100GE_TXRX_MSIX_COALESC_TIMER 3 static void update_queue_coal_param(struct hinic_nic_dev *nic_dev, struct pci_device_id *id, u16 qid) { struct hinic_intr_coal_info *info = NULL; info = &nic_dev->intr_coalesce[qid]; if (!nic_dev->intr_coal_set_flag) { switch (id->driver_data) { case HINIC_BOARD_PG_TP_10GE: info->pending_limt = HINIC_DFT_PG_10GE_TXRX_MSIX_PENDING_LIMIT; info->coalesce_timer_cfg = HINIC_DFT_PG_10GE_TXRX_MSIX_COALESC_TIMER; break; case HINIC_BOARD_PG_SM_25GE: info->pending_limt = HINIC_DFT_PG_25GE_TXRX_MSIX_PENDING_LIMIT; info->coalesce_timer_cfg = HINIC_DFT_PG_ARM_25GE_TXRX_MSIX_COALESC_TIMER; break; case HINIC_BOARD_PG_100GE: info->pending_limt = HINIC_DFT_PG_100GE_TXRX_MSIX_PENDING_LIMIT; info->coalesce_timer_cfg = HINIC_DFT_PG_ARM_100GE_TXRX_MSIX_COALESC_TIMER; break; default: info->pending_limt = qp_pending_limit; info->coalesce_timer_cfg = qp_coalesc_timer_cfg; break; } } info->resend_timer_cfg = HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG; info->pkt_rate_high = HINIC_RX_RATE_HIGH; info->rx_usecs_high = qp_coalesc_timer_high; info->rx_pending_limt_high = qp_pending_limit_high; info->pkt_rate_low = HINIC_RX_RATE_LOW; info->rx_usecs_low = qp_coalesc_timer_low; info->rx_pending_limt_low = qp_pending_limit_low; if (nic_dev->in_vm) { if (qp_pending_limit_high == HINIC_RX_PENDING_LIMIT_HIGH) qp_pending_limit_high = HINIC_RX_PENDING_LIMIT_HIGH_VM; info->pkt_rate_low = HINIC_RX_RATE_LOW_VM; info->rx_pending_limt_high = qp_pending_limit_high; } /* suit for sdi3.0 vm mode vf drv or bm mode pf/vf drv */ if ((nic_dev->is_vm_slave && nic_dev->in_vm) || nic_dev->is_bm_slave) { info->pkt_rate_high = SDI_VM_RX_PKT_RATE_HIGH; info->pkt_rate_low = SDI_VM_RX_PKT_RATE_LOW; if (qp_coalesc_use_drv_params_switch == 0) { /* if arm server, maybe need to change this value * again */ info->pending_limt = SDI_VM_PENDING_LIMT; info->coalesce_timer_cfg = SDI_VM_COALESCE_TIMER_CFG; info->rx_usecs_high = SDI_VM_RX_USECS_HIGH; info->rx_pending_limt_high = SDI_VM_RX_PENDING_LIMT_HIGH; info->rx_usecs_low = SDI_VM_RX_USECS_LOW; info->rx_pending_limt_low = SDI_VM_RX_PENDING_LIMT_LOW; } else { info->rx_usecs_high = qp_coalesc_timer_high; info->rx_pending_limt_high = qp_pending_limit_high; info->rx_usecs_low = qp_coalesc_timer_low; info->rx_pending_limt_low = qp_pending_limit_low; } } } static void init_intr_coal_param(struct hinic_nic_dev *nic_dev) { struct pci_device_id *id; u16 i; id = hinic_get_pci_device_id(nic_dev->pdev); switch (id->driver_data) { case HINIC_BOARD_10GE: case HINIC_BOARD_PG_TP_10GE: nic_dev->his_link_speed = SPEED_10000; break; case HINIC_BOARD_25GE: case HINIC_BOARD_PG_SM_25GE: nic_dev->his_link_speed = SPEED_25000; break; case HINIC_BOARD_40GE: nic_dev->his_link_speed = SPEED_40000; break; case HINIC_BOARD_100GE: case HINIC_BOARD_PG_100GE: nic_dev->his_link_speed = SPEED_100000; break; default: break; } for (i = 0; i < nic_dev->max_qps; i++) update_queue_coal_param(nic_dev, id, i); } static int hinic_init_intr_coalesce(struct hinic_nic_dev *nic_dev) { u64 size; if (qp_pending_limit != HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT || qp_coalesc_timer_cfg != HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG) nic_dev->intr_coal_set_flag = 1; else nic_dev->intr_coal_set_flag = 0; size = sizeof(*nic_dev->intr_coalesce) * nic_dev->max_qps; if (!size) { nic_err(&nic_dev->pdev->dev, "Cannot allocate zero size intr coalesce\n"); return -EINVAL; } nic_dev->intr_coalesce = kzalloc(size, GFP_KERNEL); if (!nic_dev->intr_coalesce) return -ENOMEM; init_intr_coal_param(nic_dev); if (test_bit(HINIC_INTR_ADAPT, &nic_dev->flags)) nic_dev->adaptive_rx_coal = 1; else nic_dev->adaptive_rx_coal = 0; return 0; } static void hinic_free_intr_coalesce(struct hinic_nic_dev *nic_dev) { kfree(nic_dev->intr_coalesce); } static int hinic_alloc_qps(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; int err; err = hinic_alloc_txqs(netdev); if (err) { nic_err(&nic_dev->pdev->dev, "Failed to alloc txqs\n"); return err; } err = hinic_alloc_rxqs(netdev); if (err) { nic_err(&nic_dev->pdev->dev, "Failed to alloc rxqs\n"); goto alloc_rxqs_err; } err = hinic_init_intr_coalesce(nic_dev); if (err) { nic_err(&nic_dev->pdev->dev, "Failed to init_intr_coalesce\n"); goto init_intr_err; } return 0; init_intr_err: hinic_free_rxqs(netdev); alloc_rxqs_err: hinic_free_txqs(netdev); return err; } static void hinic_destroy_qps(struct hinic_nic_dev *nic_dev) { hinic_free_intr_coalesce(nic_dev); hinic_free_rxqs(nic_dev->netdev); hinic_free_txqs(nic_dev->netdev); } static int hinic_validate_parameters(struct hinic_lld_dev *lld_dev) { struct pci_dev *pdev = lld_dev->pdev; /* Check poll_weight value, default poll_weight is 64. * The poll_weight isn't more than max queue depth, * so the valid value range is 1~4096. */ if (!poll_weight) { nic_warn(&pdev->dev, "Module Parameter poll_weight can not be 0, resetting to %d\n", DEFAULT_POLL_WEIGHT); poll_weight = DEFAULT_POLL_WEIGHT; } if (poll_weight > HINIC_MAX_QUEUE_DEPTH) { nic_warn(&pdev->dev, "Module Parameter poll_weight value %u is out of 1~%d, resetting to max value %d\n", poll_weight, HINIC_MAX_QUEUE_DEPTH, HINIC_MAX_QUEUE_DEPTH); poll_weight = HINIC_MAX_QUEUE_DEPTH; } /* check rx_buff value, default rx_buff is 2KB. * Invalid rx_buff include 2KB/4KB/8KB/16KB. */ if (rx_buff != RX_BUFF_VALID_2KB && rx_buff != RX_BUFF_VALID_4KB && rx_buff != RX_BUFF_VALID_8KB && rx_buff != RX_BUFF_VALID_16KB) { nic_warn(&pdev->dev, "Module Parameter rx_buff value %d is out of range, must be 2^n. Valid range is 2 - 16, resetting to %dKB", rx_buff, DEFAULT_RX_BUFF_LEN); rx_buff = DEFAULT_RX_BUFF_LEN; } if (qp_coalesc_timer_high <= qp_coalesc_timer_low) { nic_warn(&pdev->dev, "Module Parameter qp_coalesc_timer_high: %d, qp_coalesc_timer_low: %d is invalid, resetting to default\n", qp_coalesc_timer_high, qp_coalesc_timer_low); qp_coalesc_timer_high = HINIC_RX_COAL_TIME_HIGH; qp_coalesc_timer_low = HINIC_RX_COAL_TIME_LOW; } if (qp_pending_limit_high <= qp_pending_limit_low) { nic_warn(&pdev->dev, "Module Parameter qp_pending_limit_high: %d, qp_pending_limit_low: %d is invalid, resetting to default\n", qp_pending_limit_high, qp_pending_limit_low); qp_pending_limit_high = HINIC_RX_PENDING_LIMIT_HIGH; qp_pending_limit_low = HINIC_RX_PENDING_LIMIT_LOW; } return 0; } static void check_lro_module_param(struct hinic_nic_dev *nic_dev) { struct hinic_lro_cfg *lro = &nic_dev->adaptive_cfg.lro; /* Use module parameters first. */ if (set_lro_timer != 0 && set_lro_timer >= HINIC_LRO_RX_TIMER_LOWER && set_lro_timer <= HINIC_LRO_RX_TIMER_UPPER) lro->timer = set_lro_timer; /* Use module parameters first. */ if (set_max_wqe_num != 0 && set_max_wqe_num <= HINIC_LRO_MAX_WQE_NUM_UPPER && set_max_wqe_num >= HINIC_LRO_MAX_WQE_NUM_LOWER) lro->buffer_size = set_max_wqe_num * nic_dev->rx_buff_len; } static void decide_rss_cfg(struct hinic_nic_dev *nic_dev) { struct hinic_environment_info *info = &nic_dev->env_info; switch (info->cpu) { case HINIC_CPU_ARM_GENERIC: set_bit(HINIC_SAME_RXTX, &nic_dev->flags); break; case HINIC_CPU_X86_GENERIC: clear_bit(HINIC_SAME_RXTX, &nic_dev->flags); break; default: clear_bit(HINIC_SAME_RXTX, &nic_dev->flags); break; } } static void decide_lro_cfg(struct hinic_nic_dev *nic_dev) { struct hinic_environment_info *info = &nic_dev->env_info; struct hinic_lro_cfg *lro = &nic_dev->adaptive_cfg.lro; if (lro_en_status < HINIC_LRO_STATUS_UNSET) { lro->enable = lro_en_status; } else { /* LRO will be opened in all Huawei OS */ switch (info->os) { case HINIC_OS_HUAWEI: lro->enable = 1; break; case HINIC_OS_NON_HUAWEI: lro->enable = 0; break; default: lro->enable = 0; break; } } switch (info->board) { case HINIC_BOARD_25GE: lro->timer = HINIC_LRO_RX_TIMER_DEFAULT_25GE; break; case HINIC_BOARD_100GE: lro->timer = HINIC_LRO_RX_TIMER_DEFAULT_100GE; break; case HINIC_BOARD_PG_TP_10GE: lro->timer = HINIC_LRO_RX_TIMER_DEFAULT_PG_10GE; break; case HINIC_BOARD_PG_SM_25GE: lro->timer = HINIC_LRO_RX_TIMER_DEFAULT; break; case HINIC_BOARD_PG_100GE: lro->timer = HINIC_LRO_RX_TIMER_DEFAULT_PG_100GE; break; default: lro->timer = HINIC_LRO_RX_TIMER_DEFAULT; break; } /* Use module parameters first. */ switch (info->cpu) { case HINIC_CPU_ARM_GENERIC: lro->buffer_size = HINIC_LRO_MAX_WQE_NUM_DEFAULT_ARM * nic_dev->rx_buff_len; break; case HINIC_CPU_X86_GENERIC: lro->buffer_size = HINIC_LRO_MAX_WQE_NUM_DEFAULT_X86 * nic_dev->rx_buff_len; break; default: lro->buffer_size = HINIC_LRO_MAX_WQE_NUM_DEFAULT * nic_dev->rx_buff_len; break; } /* lro buffer_size need modify according board type */ switch (info->board) { case HINIC_BOARD_PG_TP_10GE: case HINIC_BOARD_PG_SM_25GE: case HINIC_BOARD_PG_100GE: lro->buffer_size = HINIC_LRO_WQE_NUM_PANGEA_DEFAULT * nic_dev->rx_buff_len; break; default: break; } check_lro_module_param(nic_dev); nic_info(&nic_dev->pdev->dev, "LRO default configuration: enable %u, timer %u, buffer size %u\n", lro->enable, lro->timer, lro->buffer_size); } static void decide_intr_cfg(struct hinic_nic_dev *nic_dev) { struct pci_device_id *id; id = hinic_get_pci_device_id(nic_dev->pdev); switch (id->driver_data) { case HINIC_BOARD_PG_TP_10GE: case HINIC_BOARD_PG_SM_25GE: case HINIC_BOARD_PG_100GE: clear_bit(HINIC_INTR_ADAPT, &nic_dev->flags); break; default: set_bit(HINIC_INTR_ADAPT, &nic_dev->flags); break; } } static void adaptive_configuration_init(struct hinic_nic_dev *nic_dev) { struct pci_device_id *id; id = hinic_get_pci_device_id(nic_dev->pdev); if (id) nic_dev->env_info.board = id->driver_data; else nic_dev->env_info.board = HINIC_BOARD_UNKNOWN; nic_dev->env_info.os = HINIC_OS_HUAWEI; #if defined(__aarch64__) nic_dev->env_info.cpu = HINIC_CPU_ARM_GENERIC; #elif defined(__x86_64__) nic_dev->env_info.cpu = HINIC_CPU_X86_GENERIC; #else nic_dev->env_info.cpu = HINIC_CPU_UNKNOWN; #endif nic_info(&nic_dev->pdev->dev, "Board type %u, OS type %u, CPU type %u\n", nic_dev->env_info.board, nic_dev->env_info.os, nic_dev->env_info.cpu); decide_lro_cfg(nic_dev); decide_rss_cfg(nic_dev); decide_intr_cfg(nic_dev); } static int nic_probe(struct hinic_lld_dev *lld_dev, void **uld_dev, char *uld_dev_name) { struct pci_dev *pdev = lld_dev->pdev; struct hinic_nic_dev *nic_dev; struct net_device *netdev; u16 max_qps; u32 page_num; int err; /* *uld_dev should always no be NULL */ *uld_dev = lld_dev; if (!hinic_support_nic(lld_dev->hwdev, NULL)) { nic_info(&pdev->dev, "Hw don't support nic\n"); return 0; } err = hinic_validate_parameters(lld_dev); if (err) return -EINVAL; max_qps = hinic_func_max_nic_qnum(lld_dev->hwdev); netdev = alloc_etherdev_mq(sizeof(*nic_dev), max_qps); if (!netdev) { nic_err(&pdev->dev, "Failed to allocate ETH device\n"); return -ENOMEM; } SET_NETDEV_DEV(netdev, &pdev->dev); nic_dev = (struct hinic_nic_dev *)netdev_priv(netdev); nic_dev->hwdev = lld_dev->hwdev; nic_dev->pdev = pdev; nic_dev->poll_weight = (int)poll_weight; nic_dev->msg_enable = DEFAULT_MSG_ENABLE; nic_dev->heart_status = true; nic_dev->in_vm = !hinic_is_in_host(); nic_dev->is_vm_slave = is_multi_vm_slave(lld_dev->hwdev); nic_dev->is_bm_slave = is_multi_bm_slave(lld_dev->hwdev); nic_dev->lro_replenish_thld = lro_replenish_thld; nic_dev->rx_buff_len = (u16)(rx_buff * CONVERT_UNIT); page_num = (RX_BUFF_NUM_PER_PAGE * nic_dev->rx_buff_len) / PAGE_SIZE; nic_dev->page_order = page_num > 0 ? ilog2(page_num) : 0; mutex_init(&nic_dev->nic_mutex); adaptive_configuration_init(nic_dev); nic_dev->vlan_bitmap = kzalloc(VLAN_BITMAP_SIZE(nic_dev), GFP_KERNEL); if (!nic_dev->vlan_bitmap) { err = -ENOMEM; goto vlan_bitmap_err; } nic_dev->netdev = netdev; hinic_assign_netdev_ops(nic_dev); netdev_feature_init(netdev); /* get nic cap from hw */ hinic_support_nic(lld_dev->hwdev, &nic_dev->nic_cap); err = hinic_init_nic_hwdev(nic_dev->hwdev, nic_dev->rx_buff_len); if (err) { nic_err(&pdev->dev, "Failed to init nic hwdev\n"); goto init_nic_hwdev_err; } err = hinic_set_super_cqe_state(nic_dev->hwdev, true); if (err) { nic_err(&pdev->dev, "Failed to set super cqe\n"); goto set_supper_cqe_err; } err = hinic_sw_init(nic_dev); if (err) goto sw_init_err; err = hinic_alloc_qps(nic_dev); if (err) { nic_err(&pdev->dev, "Failed to alloc qps\n"); goto alloc_qps_err; } nic_dev->workq = create_singlethread_workqueue(HINIC_NIC_DEV_WQ_NAME); if (!nic_dev->workq) { nic_err(&pdev->dev, "Failed to initialize AEQ workqueue\n"); err = -ENOMEM; goto create_workq_err; } INIT_LIST_HEAD(&nic_dev->uc_filter_list); INIT_LIST_HEAD(&nic_dev->mc_filter_list); INIT_WORK(&nic_dev->rx_mode_work, hinic_set_rx_mode_work); err = hinic_set_default_hw_feature(nic_dev); if (err) goto set_features_err; hinic_register_notifier(nic_dev); err = register_netdev(netdev); if (err) { nic_err(&pdev->dev, "Failed to register netdev\n"); err = -ENOMEM; goto netdev_err; } netif_carrier_off(netdev); *uld_dev = nic_dev; nicif_info(nic_dev, probe, netdev, "Register netdev succeed\n"); return 0; netdev_err: hinic_unregister_notifier(nic_dev); set_features_err: destroy_workqueue(nic_dev->workq); create_workq_err: hinic_destroy_qps(nic_dev); alloc_qps_err: hinic_del_mac(nic_dev->hwdev, netdev->dev_addr, 0, hinic_global_func_id_hw(nic_dev->hwdev)); sw_init_err: (void)hinic_set_super_cqe_state(nic_dev->hwdev, false); set_supper_cqe_err: hinic_free_nic_hwdev(nic_dev->hwdev); init_nic_hwdev_err: kfree(nic_dev->vlan_bitmap); vlan_bitmap_err: free_netdev(netdev); return err; } static void nic_remove(struct hinic_lld_dev *lld_dev, void *adapter) { struct hinic_nic_dev *nic_dev = adapter; struct net_device *netdev; if (!nic_dev || !hinic_support_nic(lld_dev->hwdev, NULL)) return; netdev = nic_dev->netdev; unregister_netdev(netdev); hinic_unregister_notifier(nic_dev); cancel_work_sync(&nic_dev->rx_mode_work); destroy_workqueue(nic_dev->workq); hinic_destroy_qps(nic_dev); hinic_clean_mac_list_filter(nic_dev); hinic_del_mac(nic_dev->hwdev, netdev->dev_addr, 0, hinic_global_func_id_hw(nic_dev->hwdev)); if (test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) hinic_rss_template_free(nic_dev->hwdev, nic_dev->rss_tmpl_idx); (void)hinic_set_super_cqe_state(nic_dev->hwdev, false); hinic_free_nic_hwdev(nic_dev->hwdev); kfree(nic_dev->vlan_bitmap); free_netdev(netdev); } int hinic_disable_func_rss(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; int err, err_netdev = 0; nicif_info(nic_dev, drv, netdev, "Start to disable RSS\n"); if (!test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) { nicif_info(nic_dev, drv, netdev, "RSS not enabled, do nothing\n"); return 0; } if (netif_running(netdev)) { err_netdev = hinic_close(netdev); if (err_netdev) { nicif_err(nic_dev, drv, netdev, "Failed to close netdev\n"); return -EFAULT; } } /* free rss template */ err = hinic_rss_template_free(nic_dev->hwdev, nic_dev->rss_tmpl_idx); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to free RSS template\n"); } else { nicif_info(nic_dev, drv, netdev, "Success to free RSS template\n"); clear_bit(HINIC_RSS_ENABLE, &nic_dev->flags); } if (netif_running(netdev)) { err_netdev = hinic_open(netdev); if (err_netdev) nicif_err(nic_dev, drv, netdev, "Failed to open netdev\n"); } return err ? err : err_netdev; } int hinic_enable_func_rss(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; int err, err_netdev = 0; nicif_info(nic_dev, drv, netdev, "Start to enable RSS\n"); if (test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) { nicif_info(nic_dev, drv, netdev, "RSS already enabled, do nothing\n"); return 0; } if (netif_running(netdev)) { err_netdev = hinic_close(netdev); if (err_netdev) { nicif_err(nic_dev, drv, netdev, "Failed to close netdev\n"); return -EFAULT; } } err = hinic_rss_template_alloc(nic_dev->hwdev, &nic_dev->rss_tmpl_idx); if (err) { if (err == -ENOSPC) nicif_warn(nic_dev, drv, netdev, "Failed to alloc RSS template, table is full\n"); else nicif_err(nic_dev, drv, netdev, "Failed to alloc RSS template\n"); } else { set_bit(HINIC_RSS_ENABLE, &nic_dev->flags); nicif_info(nic_dev, drv, netdev, "Success to alloc RSS template\n"); } if (netif_running(netdev)) { err_netdev = hinic_open(netdev); if (err_netdev) nicif_err(nic_dev, drv, netdev, "Failed to open netdev\n"); } return err ? err : err_netdev; } static const char *hinic_module_link_err[LINK_ERR_NUM] = { "Unrecognized module", }; static void hinic_port_module_event_handler(struct hinic_nic_dev *nic_dev, struct hinic_event_info *event) { enum port_module_event_type type = event->module_event.type; enum link_err_type err_type = event->module_event.err_type; switch (type) { case HINIC_PORT_MODULE_CABLE_PLUGGED: case HINIC_PORT_MODULE_CABLE_UNPLUGGED: nicif_info(nic_dev, link, nic_dev->netdev, "Port module event: Cable %s\n", type == HINIC_PORT_MODULE_CABLE_PLUGGED ? "plugged" : "unplugged"); break; case HINIC_PORT_MODULE_LINK_ERR: if (err_type >= LINK_ERR_NUM) { nicif_info(nic_dev, link, nic_dev->netdev, "Link failed, Unknown error type: 0x%x\n", err_type); } else { nicif_info(nic_dev, link, nic_dev->netdev, "Link failed, error type: 0x%x: %s\n", err_type, hinic_module_link_err[err_type]); } break; default: nicif_err(nic_dev, link, nic_dev->netdev, "Unknown port module type %d\n", type); break; } } static void hinic_intr_coalesc_change(struct hinic_nic_dev *nic_dev, struct hinic_event_info *event) { u32 hw_to_os_speed[LINK_SPEED_LEVELS] = {SPEED_10, SPEED_100, SPEED_1000, SPEED_10000, SPEED_25000, SPEED_40000, SPEED_100000}; u8 qid, coalesc_timer_cfg, pending_limt; struct pci_device_id *id; u32 speed; int err; if (nic_dev->adaptive_rx_coal) return; speed = hw_to_os_speed[event->link_info.speed]; if (speed == nic_dev->his_link_speed) return; id = hinic_get_pci_device_id(nic_dev->pdev); switch (id->driver_data) { case HINIC_BOARD_PG_TP_10GE: return; case HINIC_BOARD_PG_SM_25GE: if (speed == SPEED_10000) { pending_limt = HINIC_DFT_PG_10GE_TXRX_MSIX_PENDING_LIMIT; coalesc_timer_cfg = HINIC_DFT_PG_10GE_TXRX_MSIX_COALESC_TIMER; } else if (speed == SPEED_25000) { pending_limt = HINIC_DFT_PG_25GE_TXRX_MSIX_PENDING_LIMIT; coalesc_timer_cfg = HINIC_DFT_PG_ARM_25GE_TXRX_MSIX_COALESC_TIMER; } else { pending_limt = HINIC_DFT_PG_25GE_TXRX_MSIX_PENDING_LIMIT; coalesc_timer_cfg = HINIC_DFT_PG_25GE_TXRX_MSIX_COALESC_TIMER; } break; case HINIC_BOARD_PG_100GE: return; default: return; } for (qid = 0; qid < nic_dev->num_qps; qid++) { if (!nic_dev->intr_coalesce[qid].user_set_intr_coal_flag) { err = set_interrupt_moder(nic_dev, qid, coalesc_timer_cfg, pending_limt); if (!err) { nic_dev->intr_coalesce[qid].pending_limt = pending_limt; nic_dev->intr_coalesce[qid].coalesce_timer_cfg = coalesc_timer_cfg; } } } nic_dev->his_link_speed = speed; } void nic_event(struct hinic_lld_dev *lld_dev, void *adapter, struct hinic_event_info *event) { struct hinic_nic_dev *nic_dev = adapter; struct net_device *netdev; enum hinic_event_type type; if (!nic_dev || !event || !hinic_support_nic(lld_dev->hwdev, NULL)) return; netdev = nic_dev->netdev; type = event->type; switch (type) { case HINIC_EVENT_LINK_DOWN: hinic_link_status_change(nic_dev, false); break; case HINIC_EVENT_LINK_UP: hinic_link_status_change(nic_dev, true); hinic_intr_coalesc_change(nic_dev, event); break; case HINIC_EVENT_HEART_LOST: hinic_heart_lost(nic_dev); hinic_link_status_change(nic_dev, false); break; case HINIC_EVENT_FAULT: if (event->info.fault_level == FAULT_LEVEL_SERIOUS_FLR && event->info.event.chip.func_id == hinic_global_func_id(lld_dev->hwdev)) hinic_link_status_change(nic_dev, false); break; case HINIC_EVENT_DCB_STATE_CHANGE: if (nic_dev->default_cos_id == event->dcb_state.default_cos) break; /* PF notify to vf, don't need to handle this event */ if (!HINIC_FUNC_IS_VF(nic_dev->hwdev)) break; nicif_info(nic_dev, drv, netdev, "Change default cos %d to %d\n", nic_dev->default_cos_id, event->dcb_state.default_cos); nic_dev->default_cos_id = event->dcb_state.default_cos; hinic_set_sq_default_cos(netdev, nic_dev->default_cos_id); break; case HINIC_EVENT_PORT_MODULE_EVENT: hinic_port_module_event_handler(nic_dev, event); break; case HINIC_EVENT_MGMT_WATCHDOG_EVENT: hinic_link_status_change(nic_dev, false); break; default: break; } } struct hinic_uld_info nic_uld_info = { .probe = nic_probe, .remove = nic_remove, .suspend = NULL, .resume = NULL, .event = nic_event, .ioctl = nic_ioctl, }; /*lint -e766*/ hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_mbox.c000066400000000000000000001361551502027332500214250ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwdev.h" #include "hinic_csr.h" #include "hinic_hwif.h" #include "hinic_eqs.h" #include "hinic_mbox.h" #define HINIC_MBOX_INT_DST_FUNC_SHIFT 0 #define HINIC_MBOX_INT_DST_AEQN_SHIFT 10 #define HINIC_MBOX_INT_SRC_RESP_AEQN_SHIFT 12 #define HINIC_MBOX_INT_STAT_DMA_SHIFT 14 /* The size of data to be send (unit of 4 bytes) */ #define HINIC_MBOX_INT_TX_SIZE_SHIFT 20 /* SO_RO(strong order, relax order) */ #define HINIC_MBOX_INT_STAT_DMA_SO_RO_SHIFT 25 #define HINIC_MBOX_INT_WB_EN_SHIFT 28 #define HINIC_MBOX_INT_DST_FUNC_MASK 0x3FF #define HINIC_MBOX_INT_DST_AEQN_MASK 0x3 #define HINIC_MBOX_INT_SRC_RESP_AEQN_MASK 0x3 #define HINIC_MBOX_INT_STAT_DMA_MASK 0x3F #define HINIC_MBOX_INT_TX_SIZE_MASK 0x1F #define HINIC_MBOX_INT_STAT_DMA_SO_RO_MASK 0x3 #define HINIC_MBOX_INT_WB_EN_MASK 0x1 #define HINIC_MBOX_INT_SET(val, field) \ (((val) & HINIC_MBOX_INT_##field##_MASK) << \ HINIC_MBOX_INT_##field##_SHIFT) enum hinic_mbox_tx_status { TX_NOT_DONE = 1, }; #define HINIC_MBOX_CTRL_TRIGGER_AEQE_SHIFT 0 /* specifies the issue request for the message data. * 0 - Tx request is done; * 1 - Tx request is in process. */ #define HINIC_MBOX_CTRL_TX_STATUS_SHIFT 1 #define HINIC_MBOX_CTRL_TRIGGER_AEQE_MASK 0x1 #define HINIC_MBOX_CTRL_TX_STATUS_MASK 0x1 #define HINIC_MBOX_CTRL_SET(val, field) \ (((val) & HINIC_MBOX_CTRL_##field##_MASK) << \ HINIC_MBOX_CTRL_##field##_SHIFT) #define HINIC_MBOX_HEADER_MSG_LEN_SHIFT 0 #define HINIC_MBOX_HEADER_MODULE_SHIFT 11 #define HINIC_MBOX_HEADER_SEG_LEN_SHIFT 16 #define HINIC_MBOX_HEADER_NO_ACK_SHIFT 22 #define HINIC_MBOX_HEADER_SEQID_SHIFT 24 #define HINIC_MBOX_HEADER_LAST_SHIFT 30 /* specifies the mailbox message direction * 0 - send * 1 - receive */ #define HINIC_MBOX_HEADER_DIRECTION_SHIFT 31 #define HINIC_MBOX_HEADER_CMD_SHIFT 32 #define HINIC_MBOX_HEADER_MSG_ID_SHIFT 40 #define HINIC_MBOX_HEADER_STATUS_SHIFT 48 #define HINIC_MBOX_HEADER_SRC_GLB_FUNC_IDX_SHIFT 54 #define HINIC_MBOX_HEADER_MSG_LEN_MASK 0x7FF #define HINIC_MBOX_HEADER_MODULE_MASK 0x1F #define HINIC_MBOX_HEADER_SEG_LEN_MASK 0x3F #define HINIC_MBOX_HEADER_NO_ACK_MASK 0x1 #define HINIC_MBOX_HEADER_SEQID_MASK 0x3F #define HINIC_MBOX_HEADER_LAST_MASK 0x1 #define HINIC_MBOX_HEADER_DIRECTION_MASK 0x1 #define HINIC_MBOX_HEADER_CMD_MASK 0xFF #define HINIC_MBOX_HEADER_MSG_ID_MASK 0xFF #define HINIC_MBOX_HEADER_STATUS_MASK 0x3F #define HINIC_MBOX_HEADER_SRC_GLB_FUNC_IDX_MASK 0x3FF #define HINIC_MBOX_HEADER_GET(val, field) \ (((val) >> HINIC_MBOX_HEADER_##field##_SHIFT) & \ HINIC_MBOX_HEADER_##field##_MASK) #define HINIC_MBOX_HEADER_SET(val, field) \ ((u64)((val) & HINIC_MBOX_HEADER_##field##_MASK) << \ HINIC_MBOX_HEADER_##field##_SHIFT) #define MBOX_SEGLEN_MASK \ HINIC_MBOX_HEADER_SET(HINIC_MBOX_HEADER_SEG_LEN_MASK, SEG_LEN) #define HINIC_MBOX_SEG_LEN 48 #define HINIC_MBOX_COMP_TIME 8000U #define MBOX_MSG_POLLING_TIMEOUT 8000 #define MBOX_MSG_RETRY_ACK_TIMEOUT 1000 #define HINIC_MBOX_DATA_SIZE 2040 #define MBOX_MAX_BUF_SZ 2048UL #define MBOX_HEADER_SZ 8 #define MBOX_INFO_SZ 4 /* MBOX size is 64B, 8B for mbox_header, 4B reserved */ #define MBOX_SEG_LEN 48 #define MBOX_SEG_LEN_ALIGN 4 #define MBOX_WB_STATUS_LEN 16UL /* mbox write back status is 16B, only first 4B is used */ #define MBOX_WB_STATUS_ERRCODE_MASK 0xFFFF #define MBOX_WB_STATUS_MASK 0xFF #define MBOX_WB_ERROR_CODE_MASK 0xFF00 #define MBOX_WB_STATUS_FINISHED_SUCCESS 0xFF #define MBOX_WB_STATUS_FINISHED_WITH_ERR 0xFE #define MBOX_WB_STATUS_NOT_FINISHED 0x00 #define MBOX_STATUS_FINISHED(wb) \ (((wb) & MBOX_WB_STATUS_MASK) != MBOX_WB_STATUS_NOT_FINISHED) #define MBOX_STATUS_SUCCESS(wb) \ (((wb) & MBOX_WB_STATUS_MASK) == MBOX_WB_STATUS_FINISHED_SUCCESS) #define MBOX_STATUS_ERRCODE(wb) \ ((wb) & MBOX_WB_ERROR_CODE_MASK) #define SEQ_ID_START_VAL 0 #define SEQ_ID_MAX_VAL 42 #define MBOX_LAST_SEG_MAX_LEN (MBOX_MAX_BUF_SZ - \ SEQ_ID_MAX_VAL * MBOX_SEG_LEN) #define DST_AEQ_IDX_DEFAULT_VAL 0 #define SRC_AEQ_IDX_DEFAULT_VAL 0 #define NO_DMA_ATTRIBUTE_VAL 0 #define HINIC_MGMT_RSP_AEQN 0 #define HINIC_MBOX_RSP_AEQN 2 #define HINIC_MBOX_RECV_AEQN 0 #define MBOX_MSG_NO_DATA_LEN 1 #define MBOX_BODY_FROM_HDR(header) ((u8 *)(header) + MBOX_HEADER_SZ) #define MBOX_AREA(hwif) \ ((hwif)->cfg_regs_base + HINIC_FUNC_CSR_MAILBOX_DATA_OFF) #define IS_PF_OR_PPF_SRC(src_func_idx) ((src_func_idx) < HINIC_MAX_PF_FUNCS) #define MBOX_RESPONSE_ERROR 0x1 #define MBOX_MSG_ID_MASK 0xFF #define MBOX_MSG_ID(func_to_func) ((func_to_func)->send_msg_id) #define MBOX_MSG_ID_INC(func_to_func) (MBOX_MSG_ID(func_to_func) = \ (MBOX_MSG_ID(func_to_func) + 1) & MBOX_MSG_ID_MASK) #define FUNC_ID_OFF_SET_8B 8 #define FUNC_ID_OFF_SET_10B 10 enum hinic_hwif_direction_type { HINIC_HWIF_DIRECT_SEND = 0, HINIC_HWIF_RESPONSE = 1, }; enum mbox_seg_type { NOT_LAST_SEG, LAST_SEG, }; enum mbox_ordering_type { STRONG_ORDER, }; enum mbox_write_back_type { WRITE_BACK = 1, }; enum mbox_aeq_trig_type { NOT_TRIGGER, TRIGGER, }; struct hinic_set_random_id { u8 status; u8 version; u8 rsvd0[6]; u8 vf_in_pf; u8 rsvd1; u16 func_idx; u32 random_id; }; static bool check_func_id(struct hinic_hwdev *hwdev, u16 src_func_idx, const void *buf_in, u16 in_size, u16 offset) { u16 func_idx; if (in_size < offset + sizeof(func_idx)) { sdk_warn(hwdev->dev_hdl, "Receive mailbox msg len: %d less than %ld Bytes is invalid\n", in_size, offset + sizeof(func_idx)); return false; } func_idx = *((u16 *)((u8 *)buf_in + offset)); if (src_func_idx != func_idx) { sdk_warn(hwdev->dev_hdl, "Reveive mailbox function id(0x%x) not equal to msg function id(0x%x)\n", src_func_idx, func_idx); return false; } return true; } bool hinic_mbox_check_func_id_8B(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size) { return check_func_id(hwdev, func_idx, buf_in, in_size, FUNC_ID_OFF_SET_8B); } bool hinic_mbox_check_func_id_10B(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size) { return check_func_id(hwdev, func_idx, buf_in, in_size, FUNC_ID_OFF_SET_10B); } static int send_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func, enum hinic_mod_type mod, u16 cmd, void *msg, u16 msg_len, u16 dst_func, enum hinic_hwif_direction_type direction, enum hinic_mbox_ack_type ack_type, struct mbox_msg_info *msg_info); /** * hinic_register_ppf_mbox_cb - register mbox callback for ppf * @hwdev: the pointer to hw device * @mod: specific mod that the callback will handle * @callback: callback function * Return: 0 - success, negative - failure */ int hinic_register_ppf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, hinic_ppf_mbox_cb callback) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; if (mod >= HINIC_MOD_MAX) return -EFAULT; func_to_func->ppf_mbox_cb[mod] = callback; set_bit(HINIC_PPF_MBOX_CB_REG, &func_to_func->ppf_mbox_cb_state[mod]); return 0; } /** * hinic_register_pf_mbox_cb - register mbox callback for pf * @hwdev: the pointer to hw device * @mod: specific mod that the callback will handle * @callback: callback function * Return: 0 - success, negative - failure */ int hinic_register_pf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, hinic_pf_mbox_cb callback) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; if (mod >= HINIC_MOD_MAX) return -EFAULT; func_to_func->pf_mbox_cb[mod] = callback; set_bit(HINIC_PF_MBOX_CB_REG, &func_to_func->pf_mbox_cb_state[mod]); return 0; } /** * hinic_register_vf_mbox_cb - register mbox callback for vf * @hwdev: the pointer to hw device * @mod: specific mod that the callback will handle * @callback: callback function * Return: 0 - success, negative - failure */ int hinic_register_vf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, hinic_vf_mbox_cb callback) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; if (mod >= HINIC_MOD_MAX) return -EFAULT; func_to_func->vf_mbox_cb[mod] = callback; set_bit(HINIC_VF_MBOX_CB_REG, &func_to_func->vf_mbox_cb_state[mod]); return 0; } /** * hinic_register_ppf_to_pf_mbox_cb - register mbox callback for pf from ppf * @hwdev: the pointer to hw device * @mod: specific mod that the callback will handle * @callback: callback function * Return: 0 - success, negative - failure */ int hinic_register_ppf_to_pf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, hinic_pf_recv_from_ppf_mbox_cb callback) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; if (mod >= HINIC_MOD_MAX) return -EFAULT; func_to_func->pf_recv_from_ppf_mbox_cb[mod] = callback; set_bit(HINIC_PPF_TO_PF_MBOX_CB_REG, &func_to_func->ppf_to_pf_mbox_cb_state[mod]); return 0; } /** * hinic_unregister_ppf_mbox_cb - unregister the mbox callback for ppf * @hwdev: the pointer to hw device * @mod: specific mod that the callback will handle */ void hinic_unregister_ppf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; clear_bit(HINIC_PPF_MBOX_CB_REG, &func_to_func->ppf_mbox_cb_state[mod]); while (test_bit(HINIC_PPF_MBOX_CB_RUNNING, &func_to_func->ppf_mbox_cb_state[mod])) usleep_range(900, 1000); func_to_func->ppf_mbox_cb[mod] = NULL; } /** * hinic_unregister_ppf_mbox_cb - unregister the mbox callback for pf * @hwdev: the pointer to hw device * @mod: specific mod that the callback will handle */ void hinic_unregister_pf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; clear_bit(HINIC_PF_MBOX_CB_REG, &func_to_func->pf_mbox_cb_state[mod]); while (test_bit(HINIC_PF_MBOX_CB_RUNNING, &func_to_func->pf_mbox_cb_state[mod])) usleep_range(900, 1000); func_to_func->pf_mbox_cb[mod] = NULL; } /** * hinic_unregister_vf_mbox_cb - unregister the mbox callback for vf * @hwdev:the pointer to hw device * @mod:specific mod that the callback will handle */ void hinic_unregister_vf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; clear_bit(HINIC_VF_MBOX_CB_REG, &func_to_func->vf_mbox_cb_state[mod]); while (test_bit(HINIC_VF_MBOX_CB_RUNNING, &func_to_func->vf_mbox_cb_state[mod])) usleep_range(900, 1000); func_to_func->vf_mbox_cb[mod] = NULL; } /** * hinic_unregister_ppf_mbox_cb - unregister the mbox callback for pf from ppf * @hwdev: the pointer to hw device * @mod: specific mod that the callback will handle */ void hinic_unregister_ppf_to_pf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; clear_bit(HINIC_PPF_TO_PF_MBOX_CB_REG, &func_to_func->ppf_to_pf_mbox_cb_state[mod]); while (test_bit(HINIC_PPF_TO_PF_MBOX_CB_RUNNIG, &func_to_func->ppf_to_pf_mbox_cb_state[mod])) usleep_range(900, 1000); func_to_func->pf_recv_from_ppf_mbox_cb[mod] = NULL; } int vf_to_pf_handler(void *handle, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_mbox_func_to_func *func_to_func = handle; sdk_warn(func_to_func->hwdev->dev_hdl, "Not support vf command yet/n"); return -EFAULT; } static int recv_vf_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, struct hinic_recv_mbox *recv_mbox, void *buf_out, u16 *out_size) { hinic_vf_mbox_cb cb; int ret; if (recv_mbox->mod >= HINIC_MOD_MAX) { sdk_warn(func_to_func->hwdev->dev_hdl, "Receive illegal mbox message, mod = %d\n", recv_mbox->mod); return -EINVAL; } set_bit(HINIC_VF_MBOX_CB_RUNNING, &func_to_func->vf_mbox_cb_state[recv_mbox->mod]); cb = func_to_func->vf_mbox_cb[recv_mbox->mod]; if (cb && test_bit(HINIC_VF_MBOX_CB_REG, &func_to_func->vf_mbox_cb_state[recv_mbox->mod])) { ret = cb(func_to_func->hwdev, recv_mbox->cmd, recv_mbox->mbox, recv_mbox->mbox_len, buf_out, out_size); } else { sdk_warn(func_to_func->hwdev->dev_hdl, "VF mbox cb is not registered\n"); ret = -EINVAL; } clear_bit(HINIC_VF_MBOX_CB_RUNNING, &func_to_func->vf_mbox_cb_state[recv_mbox->mod]); return ret; } static int recv_pf_from_ppf_handler(struct hinic_mbox_func_to_func *func_to_func, struct hinic_recv_mbox *recv_mbox, void *buf_out, u16 *out_size) { hinic_pf_recv_from_ppf_mbox_cb cb; enum hinic_mod_type mod = recv_mbox->mod; int ret; if (mod >= HINIC_MOD_MAX) { sdk_warn(func_to_func->hwdev->dev_hdl, "Receive illegal mbox message, mod = %d\n", mod); return -EINVAL; } set_bit(HINIC_PPF_TO_PF_MBOX_CB_RUNNIG, &func_to_func->ppf_to_pf_mbox_cb_state[mod]); cb = func_to_func->pf_recv_from_ppf_mbox_cb[mod]; if (cb && test_bit(HINIC_PPF_TO_PF_MBOX_CB_REG, &func_to_func->ppf_to_pf_mbox_cb_state[mod])) { ret = cb(func_to_func->hwdev, recv_mbox->cmd, recv_mbox->mbox, recv_mbox->mbox_len, buf_out, out_size); } else { sdk_warn(func_to_func->hwdev->dev_hdl, "PF recvice ppf mailbox callback is not registered\n"); ret = -EINVAL; } clear_bit(HINIC_PPF_TO_PF_MBOX_CB_RUNNIG, &func_to_func->ppf_to_pf_mbox_cb_state[mod]); return ret; } static int recv_ppf_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, struct hinic_recv_mbox *recv_mbox, u8 pf_id, void *buf_out, u16 *out_size) { hinic_ppf_mbox_cb cb; u16 vf_id = 0; int ret; if (recv_mbox->mod >= HINIC_MOD_MAX) { sdk_warn(func_to_func->hwdev->dev_hdl, "Receive illegal mbox message, mod = %d\n", recv_mbox->mod); return -EINVAL; } set_bit(HINIC_PPF_MBOX_CB_RUNNING, &func_to_func->ppf_mbox_cb_state[recv_mbox->mod]); cb = func_to_func->ppf_mbox_cb[recv_mbox->mod]; if (cb && test_bit(HINIC_PPF_MBOX_CB_REG, &func_to_func->ppf_mbox_cb_state[recv_mbox->mod])) { ret = cb(func_to_func->hwdev, pf_id, vf_id, recv_mbox->cmd, recv_mbox->mbox, recv_mbox->mbox_len, buf_out, out_size); } else { sdk_warn(func_to_func->hwdev->dev_hdl, "PPF mbox cb is not registered, mod = %d\n", recv_mbox->mod); ret = -EINVAL; } clear_bit(HINIC_PPF_MBOX_CB_RUNNING, &func_to_func->ppf_mbox_cb_state[recv_mbox->mod]); return ret; } static int recv_pf_from_vf_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, struct hinic_recv_mbox *recv_mbox, u16 src_func_idx, void *buf_out, u16 *out_size) { hinic_pf_mbox_cb cb; u16 vf_id = 0; int ret; if (recv_mbox->mod >= HINIC_MOD_MAX) { sdk_warn(func_to_func->hwdev->dev_hdl, "Receive illegal mbox message, mod = %d\n", recv_mbox->mod); return -EINVAL; } set_bit(HINIC_PF_MBOX_CB_RUNNING, &func_to_func->pf_mbox_cb_state[recv_mbox->mod]); cb = func_to_func->pf_mbox_cb[recv_mbox->mod]; if (cb && test_bit(HINIC_PF_MBOX_CB_REG, &func_to_func->pf_mbox_cb_state[recv_mbox->mod])) { vf_id = src_func_idx - hinic_glb_pf_vf_offset(func_to_func->hwdev); ret = cb(func_to_func->hwdev, vf_id, recv_mbox->cmd, recv_mbox->mbox, recv_mbox->mbox_len, buf_out, out_size); } else { sdk_warn(func_to_func->hwdev->dev_hdl, "PF mbox mod(0x%x) cb is not registered\n", recv_mbox->mod); ret = -EINVAL; } clear_bit(HINIC_PF_MBOX_CB_RUNNING, &func_to_func->pf_mbox_cb_state[recv_mbox->mod]); return ret; } bool hinic_mbox_check_cmd_valid(struct hinic_hwdev *hwdev, struct vf_cmd_check_handle *cmd_handle, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, u8 size) { u16 src_idx = vf_id + hinic_glb_pf_vf_offset(hwdev); int i; for (i = 0; i < size; i++) { if (cmd == cmd_handle[i].cmd) { if (cmd_handle[i].check_cmd) return cmd_handle[i].check_cmd(hwdev, src_idx, buf_in, in_size); else return true; } } sdk_err(hwdev->dev_hdl, "Unsupported vf cmd %d\n", cmd); return false; } static void recv_func_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, struct hinic_recv_mbox *recv_mbox, u16 src_func_idx) { struct hinic_hwdev *dev = func_to_func->hwdev; struct mbox_msg_info msg_info = {0}; u16 out_size = MBOX_MAX_BUF_SZ; void *buf_out = recv_mbox->buf_out; int err = 0; if (HINIC_IS_VF(dev)) { err = recv_vf_mbox_handler(func_to_func, recv_mbox, buf_out, &out_size); } else { /* pf/ppf process */ if (IS_PF_OR_PPF_SRC(src_func_idx)) { if (HINIC_IS_PPF(dev)) { err = recv_ppf_mbox_handler(func_to_func, recv_mbox, (u8)src_func_idx, buf_out, &out_size); if (err) goto out; } else { err = recv_pf_from_ppf_handler(func_to_func, recv_mbox, buf_out, &out_size); if (err) goto out; } /* The source is neither PF nor PPF, so it is from VF */ } else { err = recv_pf_from_vf_mbox_handler(func_to_func, recv_mbox, src_func_idx, buf_out, &out_size); } } out: if (recv_mbox->ack_type == MBOX_ACK) { msg_info.msg_id = recv_mbox->msg_info.msg_id; if (err == HINIC_DEV_BUSY_ACTIVE_FW || err == HINIC_MBOX_PF_BUSY_ACTIVE_FW) msg_info.status = HINIC_MBOX_PF_BUSY_ACTIVE_FW; else if (err == HINIC_MBOX_VF_CMD_ERROR) msg_info.status = HINIC_MBOX_VF_CMD_ERROR; else if (err) msg_info.status = HINIC_MBOX_PF_SEND_ERR; /* if not data need to response, set out_size to 1 */ if (!out_size || err) out_size = MBOX_MSG_NO_DATA_LEN; send_mbox_to_func(func_to_func, recv_mbox->mod, recv_mbox->cmd, buf_out, out_size, src_func_idx, HINIC_HWIF_RESPONSE, MBOX_ACK, &msg_info); } kfree(recv_mbox->buf_out); kfree(recv_mbox->mbox); kfree(recv_mbox); } static bool check_mbox_seq_id_and_seg_len(struct hinic_recv_mbox *recv_mbox, u8 seq_id, u8 seg_len) { if (seq_id > SEQ_ID_MAX_VAL || seg_len > MBOX_SEG_LEN) return false; else if (seq_id == SEQ_ID_MAX_VAL && seg_len > MBOX_LAST_SEG_MAX_LEN) return false; if (seq_id == 0) { recv_mbox->seq_id = seq_id; } else { if (seq_id != recv_mbox->seq_id + 1) return false; recv_mbox->seq_id = seq_id; } return true; } static void resp_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, struct hinic_recv_mbox *recv_mbox) { spin_lock(&func_to_func->mbox_lock); if (recv_mbox->msg_info.msg_id == func_to_func->send_msg_id && func_to_func->event_flag == EVENT_START) complete(&recv_mbox->recv_done); else sdk_err(func_to_func->hwdev->dev_hdl, "Mbox response timeout, current send msg id(0x%x), recv msg id(0x%x), status(0x%x)\n", func_to_func->send_msg_id, recv_mbox->msg_info.msg_id, recv_mbox->msg_info.status); spin_unlock(&func_to_func->mbox_lock); } static void recv_func_mbox_work_handler(struct work_struct *work) { struct hinic_mbox_work *mbox_work = container_of(work, struct hinic_mbox_work, work); recv_func_mbox_handler(mbox_work->func_to_func, mbox_work->recv_mbox, mbox_work->src_func_idx); kfree(mbox_work); } static void recv_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, void *header, struct hinic_recv_mbox *recv_mbox) { u64 mbox_header = *((u64 *)header); void *mbox_body = MBOX_BODY_FROM_HDR(header); struct hinic_recv_mbox *rcv_mbox_temp = NULL; u16 src_func_idx; struct hinic_mbox_work *mbox_work; int pos; u8 seq_id, seg_len; seq_id = HINIC_MBOX_HEADER_GET(mbox_header, SEQID); seg_len = HINIC_MBOX_HEADER_GET(mbox_header, SEG_LEN); src_func_idx = HINIC_MBOX_HEADER_GET(mbox_header, SRC_GLB_FUNC_IDX); if (!check_mbox_seq_id_and_seg_len(recv_mbox, seq_id, seg_len)) { sdk_err(func_to_func->hwdev->dev_hdl, "Mailbox sequence and segment check fail, src func id: 0x%x, front id: 0x%x, current id: 0x%x, seg len: 0x%x\n", src_func_idx, recv_mbox->seq_id, seq_id, seg_len); recv_mbox->seq_id = SEQ_ID_MAX_VAL; return; } pos = seq_id * MBOX_SEG_LEN; memcpy((u8 *)recv_mbox->mbox + pos, mbox_body, HINIC_MBOX_HEADER_GET(mbox_header, SEG_LEN)); if (!HINIC_MBOX_HEADER_GET(mbox_header, LAST)) return; recv_mbox->cmd = HINIC_MBOX_HEADER_GET(mbox_header, CMD); recv_mbox->mod = HINIC_MBOX_HEADER_GET(mbox_header, MODULE); recv_mbox->mbox_len = HINIC_MBOX_HEADER_GET(mbox_header, MSG_LEN); recv_mbox->ack_type = HINIC_MBOX_HEADER_GET(mbox_header, NO_ACK); recv_mbox->msg_info.msg_id = HINIC_MBOX_HEADER_GET(mbox_header, MSG_ID); recv_mbox->msg_info.status = HINIC_MBOX_HEADER_GET(mbox_header, STATUS); recv_mbox->seq_id = SEQ_ID_MAX_VAL; if (HINIC_MBOX_HEADER_GET(mbox_header, DIRECTION) == HINIC_HWIF_RESPONSE) { resp_mbox_handler(func_to_func, recv_mbox); return; } rcv_mbox_temp = kzalloc(sizeof(*rcv_mbox_temp), GFP_KERNEL); if (!rcv_mbox_temp) return; memcpy(rcv_mbox_temp, recv_mbox, sizeof(*rcv_mbox_temp)); rcv_mbox_temp->mbox = kzalloc(MBOX_MAX_BUF_SZ, GFP_KERNEL); if (!rcv_mbox_temp->mbox) goto rcv_mbox_msg_err; memcpy(rcv_mbox_temp->mbox, recv_mbox->mbox, MBOX_MAX_BUF_SZ); rcv_mbox_temp->buf_out = kzalloc(MBOX_MAX_BUF_SZ, GFP_KERNEL); if (!rcv_mbox_temp->buf_out) { sdk_err(func_to_func->hwdev->dev_hdl, "Allocate receive mbox out buffer memory failed.\n"); goto rcv_mbox_buf_err; } mbox_work = kzalloc(sizeof(*mbox_work), GFP_KERNEL); if (!mbox_work) { sdk_err(func_to_func->hwdev->dev_hdl, "Allocate mbox work memory failed.\n"); goto mbox_work_err; } mbox_work->func_to_func = func_to_func; mbox_work->recv_mbox = rcv_mbox_temp; mbox_work->src_func_idx = src_func_idx; INIT_WORK(&mbox_work->work, recv_func_mbox_work_handler); queue_work(func_to_func->workq, &mbox_work->work); return; mbox_work_err: kfree(rcv_mbox_temp->buf_out); rcv_mbox_buf_err: kfree(rcv_mbox_temp->mbox); rcv_mbox_msg_err: kfree(rcv_mbox_temp); } int set_vf_mbox_random_id(struct hinic_hwdev *hwdev, u16 func_id) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; struct hinic_set_random_id rand_info = {0}; u16 out_size = sizeof(rand_info); int ret; rand_info.version = HINIC_CMD_VER_FUNC_ID; rand_info.func_idx = func_id; rand_info.vf_in_pf = (u8)(func_id - hinic_glb_pf_vf_offset(hwdev)); get_random_bytes(&rand_info.random_id, sizeof(u32)); func_to_func->vf_mbx_rand_id[func_id] = rand_info.random_id; ret = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, HINIC_MGMT_CMD_SET_VF_RANDOM_ID, &rand_info, sizeof(rand_info), &rand_info, &out_size, 0); if ((rand_info.status != HINIC_MGMT_CMD_UNSUPPORTED && rand_info.status) || !out_size || ret) { sdk_err(hwdev->dev_hdl, "Failed to set vf random id, err: %d, status: 0x%x, out size: 0x%x\n", ret, rand_info.status, out_size); return -EINVAL; } if (rand_info.status == HINIC_MGMT_CMD_UNSUPPORTED) return rand_info.status; func_to_func->vf_mbx_old_rand_id[func_id] = func_to_func->vf_mbx_rand_id[func_id]; return 0; } static void update_random_id_work_handler(struct work_struct *work) { struct hinic_mbox_work *mbox_work = container_of(work, struct hinic_mbox_work, work); struct hinic_mbox_func_to_func *func_to_func = mbox_work->func_to_func; u16 src = mbox_work->src_func_idx; int err; err = set_vf_mbox_random_id(func_to_func->hwdev, src); if (err) sdk_warn(func_to_func->hwdev->dev_hdl, "Update vf id(0x%x) random id fail\n", mbox_work->src_func_idx); kfree(mbox_work); } bool check_vf_mbox_random_id(struct hinic_mbox_func_to_func *func_to_func, u8 *header) { struct hinic_hwdev *hwdev = func_to_func->hwdev; u64 mbox_header = *((u64 *)header); struct hinic_mbox_work *mbox_work; u32 random_id; u16 offset, src = HINIC_MBOX_HEADER_GET(mbox_header, SRC_GLB_FUNC_IDX); int vf_in_pf; if (IS_PF_OR_PPF_SRC(src) || !func_to_func->support_vf_random) return true; if (!HINIC_IS_PPF(hwdev)) { offset = hinic_glb_pf_vf_offset(hwdev); vf_in_pf = src - offset; if (vf_in_pf < 1 || vf_in_pf > hinic_func_max_vf(hwdev)) { sdk_warn(hwdev->dev_hdl, "Receive vf id(0x%x) is invalid, vf id should be from 0x%x to 0x%x\n", src, (offset + 1), (hinic_func_max_vf(hwdev) + offset)); return false; } } random_id = be32_to_cpu(*(u32 *)(header + MBOX_SEG_LEN + MBOX_HEADER_SZ)); if (random_id == func_to_func->vf_mbx_rand_id[src] || random_id == func_to_func->vf_mbx_old_rand_id[src]) return true; sdk_warn(hwdev->dev_hdl, "Receive func_id(0x%x) mailbox random id(0x%x) mismatch with pf reserve(0x%x)\n", src, random_id, func_to_func->vf_mbx_rand_id[src]); mbox_work = kzalloc(sizeof(*mbox_work), GFP_KERNEL); if (!mbox_work) return false; mbox_work->func_to_func = func_to_func; mbox_work->src_func_idx = src; INIT_WORK(&mbox_work->work, update_random_id_work_handler); queue_work(func_to_func->workq, &mbox_work->work); return false; } void hinic_mbox_func_aeqe_handler(void *handle, u8 *header, u8 size) { struct hinic_mbox_func_to_func *func_to_func; struct hinic_recv_mbox *recv_mbox; u64 mbox_header = *((u64 *)header); u64 src, dir; func_to_func = ((struct hinic_hwdev *)handle)->func_to_func; dir = HINIC_MBOX_HEADER_GET(mbox_header, DIRECTION); src = HINIC_MBOX_HEADER_GET(mbox_header, SRC_GLB_FUNC_IDX); if (src >= HINIC_MAX_FUNCTIONS) { sdk_err(func_to_func->hwdev->dev_hdl, "Mailbox source function id: %u is invalid\n", (u32)src); return; } if (!check_vf_mbox_random_id(func_to_func, header)) return; recv_mbox = (dir == HINIC_HWIF_DIRECT_SEND) ? &func_to_func->mbox_send[src] : &func_to_func->mbox_resp[src]; recv_mbox_handler(func_to_func, (u64 *)header, recv_mbox); } void hinic_mbox_self_aeqe_handler(void *handle, u8 *header, u8 size) { struct hinic_mbox_func_to_func *func_to_func; struct hinic_send_mbox *send_mbox; func_to_func = ((struct hinic_hwdev *)handle)->func_to_func; send_mbox = &func_to_func->send_mbox; complete(&send_mbox->send_done); } static void clear_mbox_status(struct hinic_send_mbox *mbox) { *mbox->wb_status = 0; /* clear mailbox write back status */ wmb(); } static void mbox_copy_header(struct hinic_hwdev *hwdev, struct hinic_send_mbox *mbox, u64 *header) { u32 *data = (u32 *)header; u32 i, idx_max = MBOX_HEADER_SZ / sizeof(u32); for (i = 0; i < idx_max; i++) __raw_writel(*(data + i), mbox->data + i * sizeof(u32)); } static void mbox_copy_send_data(struct hinic_hwdev *hwdev, struct hinic_send_mbox *mbox, void *seg, u16 seg_len) { u32 *data = seg; u32 data_len, chk_sz = sizeof(u32); u32 i, idx_max; u8 mbox_max_buf[MBOX_SEG_LEN] = {0}; /* The mbox message should be aligned in 4 bytes. */ if (seg_len % chk_sz) { memcpy(mbox_max_buf, seg, seg_len); data = (u32 *)mbox_max_buf; } data_len = seg_len; idx_max = ALIGN(data_len, chk_sz) / chk_sz; for (i = 0; i < idx_max; i++) { __raw_writel(*(data + i), mbox->data + MBOX_HEADER_SZ + i * sizeof(u32)); } } static void write_mbox_msg_attr(struct hinic_mbox_func_to_func *func_to_func, u16 dst_func, u16 dst_aeqn, u16 rsp_aeq, u16 seg_len, int poll) { u32 mbox_int, mbox_ctrl; mbox_int = HINIC_MBOX_INT_SET(dst_func, DST_FUNC) | HINIC_MBOX_INT_SET(dst_aeqn, DST_AEQN) | HINIC_MBOX_INT_SET(rsp_aeq, SRC_RESP_AEQN) | HINIC_MBOX_INT_SET(NO_DMA_ATTRIBUTE_VAL, STAT_DMA) | HINIC_MBOX_INT_SET(ALIGN(MBOX_SEG_LEN + MBOX_HEADER_SZ + MBOX_INFO_SZ, MBOX_SEG_LEN_ALIGN) >> 2, TX_SIZE) | HINIC_MBOX_INT_SET(STRONG_ORDER, STAT_DMA_SO_RO) | HINIC_MBOX_INT_SET(WRITE_BACK, WB_EN); hinic_hwif_write_reg(func_to_func->hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_INT_OFFSET_OFF, mbox_int); wmb(); /* writing the mbox int attributes */ mbox_ctrl = HINIC_MBOX_CTRL_SET(TX_NOT_DONE, TX_STATUS); if (poll) mbox_ctrl |= HINIC_MBOX_CTRL_SET(NOT_TRIGGER, TRIGGER_AEQE); else mbox_ctrl |= HINIC_MBOX_CTRL_SET(TRIGGER, TRIGGER_AEQE); hinic_hwif_write_reg(func_to_func->hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_CONTROL_OFF, mbox_ctrl); } void dump_mox_reg(struct hinic_hwdev *hwdev) { u32 val; val = hinic_hwif_read_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_CONTROL_OFF); sdk_err(hwdev->dev_hdl, "Mailbox control reg: 0x%x\n", val); val = hinic_hwif_read_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_INT_OFFSET_OFF); sdk_err(hwdev->dev_hdl, "Mailbox interrupt offset: 0x%x\n", val); } static u16 get_mbox_status(struct hinic_send_mbox *mbox) { /* write back is 16B, but only use first 4B */ u64 wb_val = be64_to_cpu(*mbox->wb_status); rmb(); /* verify reading before check */ return (u16)(wb_val & MBOX_WB_STATUS_ERRCODE_MASK); } static u16 mbox_msg_ack_aeqn(struct hinic_hwdev *hwdev, enum hinic_hwif_direction_type seq_dir) { u8 num_aeqs = hwdev->hwif->attr.num_aeqs; u16 dst_aeqn; if (num_aeqs >= HINIC_HW_MAX_AEQS) dst_aeqn = HINIC_MBOX_RSP_AEQN; else dst_aeqn = 0; return dst_aeqn; } static int mbox_retry_get_ack(struct hinic_mbox_func_to_func *func_to_func, struct completion *done, u16 aeq_id) { ulong timeo = msecs_to_jiffies(MBOX_MSG_RETRY_ACK_TIMEOUT); int err; init_completion(done); err = hinic_reschedule_eq(func_to_func->hwdev, HINIC_AEQ, aeq_id); if (err) return err; if (!wait_for_completion_timeout(done, timeo)) return -ETIMEDOUT; return 0; } static int send_mbox_seg(struct hinic_mbox_func_to_func *func_to_func, u64 header, u16 dst_func, void *seg, u16 seg_len, int poll, void *msg_info) { struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox; struct hinic_hwdev *hwdev = func_to_func->hwdev; u8 num_aeqs = hwdev->hwif->attr.num_aeqs; u16 dst_aeqn, wb_status = 0, errcode, rsp_aeq; u16 seq_dir = HINIC_MBOX_HEADER_GET(header, DIRECTION); struct completion *done = &send_mbox->send_done; ulong jif; u32 cnt = 0; if (num_aeqs >= HINIC_HW_MAX_AEQS) dst_aeqn = (seq_dir == HINIC_HWIF_DIRECT_SEND) ? HINIC_MBOX_RECV_AEQN : HINIC_MBOX_RSP_AEQN; else dst_aeqn = 0; rsp_aeq = (dst_aeqn == 0) ? 0 : HINIC_MBOX_RSP_AEQN; if (!poll) init_completion(done); clear_mbox_status(send_mbox); mbox_copy_header(hwdev, send_mbox, &header); mbox_copy_send_data(hwdev, send_mbox, seg, seg_len); write_mbox_msg_attr(func_to_func, dst_func, dst_aeqn, rsp_aeq, seg_len, poll); wmb(); /* writing the mbox msg attributes */ if (poll) { while (cnt < MBOX_MSG_POLLING_TIMEOUT) { wb_status = get_mbox_status(send_mbox); if (MBOX_STATUS_FINISHED(wb_status)) break; usleep_range(900, 1000); cnt++; } if (cnt == MBOX_MSG_POLLING_TIMEOUT) { sdk_err(hwdev->dev_hdl, "Send mailbox segment timeout, wb status: 0x%x\n", wb_status); dump_mox_reg(hwdev); return -ETIMEDOUT; } } else { jif = msecs_to_jiffies(HINIC_MBOX_COMP_TIME); if (!wait_for_completion_timeout(done, jif) && mbox_retry_get_ack(func_to_func, done, rsp_aeq)) { sdk_err(hwdev->dev_hdl, "Send mailbox segment timeout\n"); dump_mox_reg(hwdev); hinic_dump_aeq_info(hwdev); return -ETIMEDOUT; } wb_status = get_mbox_status(send_mbox); } if (!MBOX_STATUS_SUCCESS(wb_status)) { sdk_err(hwdev->dev_hdl, "Send mailbox segment to function %d error, wb status: 0x%x\n", dst_func, wb_status); errcode = MBOX_STATUS_ERRCODE(wb_status); return errcode ? errcode : -EFAULT; } return 0; } static int send_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func, enum hinic_mod_type mod, u16 cmd, void *msg, u16 msg_len, u16 dst_func, enum hinic_hwif_direction_type direction, enum hinic_mbox_ack_type ack_type, struct mbox_msg_info *msg_info) { struct hinic_hwdev *hwdev = func_to_func->hwdev; int err = 0; u32 seq_id = 0; u16 seg_len = MBOX_SEG_LEN; u16 left = msg_len; u8 *msg_seg = (u8 *)msg; u64 header = 0; down(&func_to_func->msg_send_sem); header = HINIC_MBOX_HEADER_SET(msg_len, MSG_LEN) | HINIC_MBOX_HEADER_SET(mod, MODULE) | HINIC_MBOX_HEADER_SET(seg_len, SEG_LEN) | HINIC_MBOX_HEADER_SET(ack_type, NO_ACK) | HINIC_MBOX_HEADER_SET(SEQ_ID_START_VAL, SEQID) | HINIC_MBOX_HEADER_SET(NOT_LAST_SEG, LAST) | HINIC_MBOX_HEADER_SET(direction, DIRECTION) | HINIC_MBOX_HEADER_SET(cmd, CMD) | /* The vf's offset to it's associated pf */ HINIC_MBOX_HEADER_SET(msg_info->msg_id, MSG_ID) | HINIC_MBOX_HEADER_SET(msg_info->status, STATUS) | HINIC_MBOX_HEADER_SET(hinic_global_func_id_hw(hwdev), SRC_GLB_FUNC_IDX); while (!(HINIC_MBOX_HEADER_GET(header, LAST))) { if (left <= HINIC_MBOX_SEG_LEN) { header &= ~MBOX_SEGLEN_MASK; header |= HINIC_MBOX_HEADER_SET(left, SEG_LEN); header |= HINIC_MBOX_HEADER_SET(LAST_SEG, LAST); seg_len = left; } err = send_mbox_seg(func_to_func, header, dst_func, msg_seg, seg_len, func_to_func->send_ack_mod, msg_info); if (err) { sdk_err(hwdev->dev_hdl, "Failed to send mbox seg, seq_id=0x%llx\n", HINIC_MBOX_HEADER_GET(header, SEQID)); goto send_err; } left -= HINIC_MBOX_SEG_LEN; msg_seg += HINIC_MBOX_SEG_LEN; seq_id++; header &= ~(HINIC_MBOX_HEADER_SET(HINIC_MBOX_HEADER_SEQID_MASK, SEQID)); header |= HINIC_MBOX_HEADER_SET(seq_id, SEQID); } send_err: up(&func_to_func->msg_send_sem); return err; } static void set_mbox_to_func_event(struct hinic_mbox_func_to_func *func_to_func, enum mbox_event_state event_flag) { spin_lock(&func_to_func->mbox_lock); func_to_func->event_flag = event_flag; spin_unlock(&func_to_func->mbox_lock); } int hinic_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func, enum hinic_mod_type mod, u16 cmd, u16 dst_func, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { /* use mbox_resp to hole data which responsed from other function */ struct hinic_recv_mbox *mbox_for_resp; struct mbox_msg_info msg_info = {0}; ulong timeo; int err; if (!func_to_func->hwdev->chip_present_flag) return -EPERM; mbox_for_resp = &func_to_func->mbox_resp[dst_func]; down(&func_to_func->mbox_send_sem); init_completion(&mbox_for_resp->recv_done); msg_info.msg_id = MBOX_MSG_ID_INC(func_to_func); set_mbox_to_func_event(func_to_func, EVENT_START); err = send_mbox_to_func(func_to_func, mod, cmd, buf_in, in_size, dst_func, HINIC_HWIF_DIRECT_SEND, MBOX_ACK, &msg_info); if (err) { sdk_err(func_to_func->hwdev->dev_hdl, "Send mailbox mod %d cmd 0x%x failed, msg_id: %d\n", mod, cmd, msg_info.msg_id); set_mbox_to_func_event(func_to_func, EVENT_FAIL); goto send_err; } timeo = msecs_to_jiffies(timeout ? timeout : HINIC_MBOX_COMP_TIME); if (!wait_for_completion_timeout(&mbox_for_resp->recv_done, timeo) && mbox_retry_get_ack(func_to_func, &mbox_for_resp->recv_done, mbox_msg_ack_aeqn(func_to_func->hwdev, HINIC_HWIF_DIRECT_SEND))) { set_mbox_to_func_event(func_to_func, EVENT_TIMEOUT); sdk_err(func_to_func->hwdev->dev_hdl, "Send mbox msg mod %d cmd 0x%x timeout, msg_id: %d\n", mod, cmd, msg_info.msg_id); hinic_dump_aeq_info(func_to_func->hwdev); err = -ETIMEDOUT; goto send_err; } set_mbox_to_func_event(func_to_func, EVENT_END); if (mbox_for_resp->msg_info.status) { err = mbox_for_resp->msg_info.status; if (err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) sdk_err(func_to_func->hwdev->dev_hdl, "Mbox response error(0x%x)\n", mbox_for_resp->msg_info.status); goto send_err; } if (buf_out && out_size) { if (*out_size < mbox_for_resp->mbox_len) { sdk_err(func_to_func->hwdev->dev_hdl, "Invalid response mbox message length: %d for mod %d cmd 0x%x, should less than: %d\n", mbox_for_resp->mbox_len, mod, cmd, *out_size); err = -EFAULT; goto send_err; } if (mbox_for_resp->mbox_len) memcpy(buf_out, mbox_for_resp->mbox, mbox_for_resp->mbox_len); *out_size = mbox_for_resp->mbox_len; } send_err: up(&func_to_func->mbox_send_sem); return err; } static int mbox_func_params_valid(struct hinic_mbox_func_to_func *func_to_func, void *buf_in, u16 in_size) { if (!buf_in || !in_size) return -EINVAL; if (in_size > HINIC_MBOX_DATA_SIZE) { sdk_err(func_to_func->hwdev->dev_hdl, "Mbox msg len(%d) exceed limit(%d)\n", in_size, HINIC_MBOX_DATA_SIZE); return -EINVAL; } return 0; } int hinic_mbox_to_host(struct hinic_hwdev *hwdev, u16 dest_host_ppf_id, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; int err; err = mbox_func_params_valid(func_to_func, buf_in, in_size); if (err) return err; if (!HINIC_IS_PPF(hwdev)) { sdk_err(hwdev->dev_hdl, "Params error, only ppf can send message to other host, func_type: %d\n", hinic_func_type(hwdev)); return -EINVAL; } return hinic_mbox_to_func(func_to_func, mod, cmd, dest_host_ppf_id, buf_in, in_size, buf_out, out_size, timeout); } int hinic_mbox_to_ppf(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; int err = mbox_func_params_valid(func_to_func, buf_in, in_size); if (err) return err; if (HINIC_IS_VF(hwdev) || HINIC_IS_PPF(hwdev)) { sdk_err(hwdev->dev_hdl, "Params error, func_type: %d\n", hinic_func_type(hwdev)); return -EINVAL; } return hinic_mbox_to_func(func_to_func, mod, cmd, hinic_ppf_idx(hwdev), buf_in, in_size, buf_out, out_size, timeout); } int hinic_mbox_to_pf(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; int err = mbox_func_params_valid(func_to_func, buf_in, in_size); if (err) return err; if (!HINIC_IS_VF(hwdev)) { sdk_err(hwdev->dev_hdl, "Params error, func_type: %d\n", hinic_func_type(hwdev)); return -EINVAL; } err = hinic_func_own_get(hwdev); if (err) return err; /* port_to_port_idx - imply which PCIE interface PF is connected */ err = hinic_mbox_to_func(func_to_func, mod, cmd, hinic_pf_id_of_vf_hw(hwdev), buf_in, in_size, buf_out, out_size, timeout); hinic_func_own_free(hwdev); return err; } int hinic_mbox_to_func_no_ack(struct hinic_hwdev *hwdev, u16 func_idx, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size) { struct mbox_msg_info msg_info = {0}; int err = mbox_func_params_valid(hwdev->func_to_func, buf_in, in_size); if (err) return err; down(&hwdev->func_to_func->mbox_send_sem); err = send_mbox_to_func(hwdev->func_to_func, mod, cmd, buf_in, in_size, func_idx, HINIC_HWIF_DIRECT_SEND, MBOX_NO_ACK, &msg_info); if (err) sdk_err(hwdev->dev_hdl, "Send mailbox no ack failed\n"); up(&hwdev->func_to_func->mbox_send_sem); return err; } int hinic_mbox_to_pf_no_ack(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size) { int err; err = hinic_func_own_get(hwdev); if (err) return err; err = hinic_mbox_to_func_no_ack(hwdev, hinic_pf_id_of_vf_hw(hwdev), mod, cmd, buf_in, in_size); hinic_func_own_free(hwdev); return err; } int __hinic_mbox_to_vf(void *hwdev, enum hinic_mod_type mod, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { struct hinic_mbox_func_to_func *func_to_func; int err; u16 dst_func_idx; if (!hwdev) return -EINVAL; func_to_func = ((struct hinic_hwdev *)hwdev)->func_to_func; err = mbox_func_params_valid(func_to_func, buf_in, in_size); if (err) return err; if (HINIC_IS_VF((struct hinic_hwdev *)hwdev)) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Params error, func_type: %d\n", hinic_func_type(hwdev)); return -EINVAL; } if (!vf_id) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "VF id(%d) error!\n", vf_id); return -EINVAL; } /* vf_offset_to_pf + vf_id is the vf's global function id of vf in * this pf */ dst_func_idx = hinic_glb_pf_vf_offset(hwdev) + vf_id; return hinic_mbox_to_func(func_to_func, mod, cmd, dst_func_idx, buf_in, in_size, buf_out, out_size, timeout); } int hinic_mbox_ppf_to_vf(void *hwdev, enum hinic_mod_type mod, u16 func_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { struct hinic_mbox_func_to_func *func_to_func; int err; if (!hwdev) return -EINVAL; func_to_func = ((struct hinic_hwdev *)hwdev)->func_to_func; err = mbox_func_params_valid(func_to_func, buf_in, in_size); if (err) return err; if (HINIC_IS_VF((struct hinic_hwdev *)hwdev)) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Params error, func_type: %d\n", hinic_func_type(hwdev)); return -EINVAL; } return hinic_mbox_to_func(func_to_func, mod, cmd, func_id, buf_in, in_size, buf_out, out_size, timeout); } EXPORT_SYMBOL(hinic_mbox_ppf_to_vf); int hinic_mbox_ppf_to_pf(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u16 dst_pf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; int err; err = mbox_func_params_valid(func_to_func, buf_in, in_size); if (err) return err; if (!HINIC_IS_PPF(hwdev)) { sdk_err(hwdev->dev_hdl, "Params error, func_type: %d\n", hinic_func_type(hwdev)); return -EINVAL; } if (hinic_ppf_idx(hwdev) == dst_pf_id) { sdk_err(hwdev->dev_hdl, "Params error, dst_pf_id(0x%x) is ppf\n", dst_pf_id); return -EINVAL; } return hinic_mbox_to_func(func_to_func, mod, cmd, dst_pf_id, buf_in, in_size, buf_out, out_size, timeout); } static int init_mbox_info(struct hinic_recv_mbox *mbox_info) { int err; mbox_info->seq_id = SEQ_ID_MAX_VAL; mbox_info->mbox = kzalloc(MBOX_MAX_BUF_SZ, GFP_KERNEL); if (!mbox_info->mbox) return -ENOMEM; mbox_info->buf_out = kzalloc(MBOX_MAX_BUF_SZ, GFP_KERNEL); if (!mbox_info->buf_out) { err = -ENOMEM; goto alloc_buf_out_err; } return 0; alloc_buf_out_err: kfree(mbox_info->mbox); return err; } static void clean_mbox_info(struct hinic_recv_mbox *mbox_info) { kfree(mbox_info->buf_out); kfree(mbox_info->mbox); } static int alloc_mbox_info(struct hinic_recv_mbox *mbox_info) { u16 func_idx, i; int err; for (func_idx = 0; func_idx < HINIC_MAX_FUNCTIONS; func_idx++) { err = init_mbox_info(&mbox_info[func_idx]); if (err) { pr_err("Failed to init mbox info\n"); goto init_mbox_info_err; } } return 0; init_mbox_info_err: for (i = 0; i < func_idx; i++) clean_mbox_info(&mbox_info[i]); return err; } static void free_mbox_info(struct hinic_recv_mbox *mbox_info) { u16 func_idx; for (func_idx = 0; func_idx < HINIC_MAX_FUNCTIONS; func_idx++) clean_mbox_info(&mbox_info[func_idx]); } static void prepare_send_mbox(struct hinic_mbox_func_to_func *func_to_func) { struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox; send_mbox->data = MBOX_AREA(func_to_func->hwdev->hwif); } static int alloc_mbox_wb_status(struct hinic_mbox_func_to_func *func_to_func) { struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox; struct hinic_hwdev *hwdev = func_to_func->hwdev; u32 addr_h, addr_l; send_mbox->wb_vaddr = dma_alloc_coherent(hwdev->dev_hdl, MBOX_WB_STATUS_LEN, &send_mbox->wb_paddr, GFP_KERNEL); if (!send_mbox->wb_vaddr) return -ENOMEM; send_mbox->wb_status = send_mbox->wb_vaddr; addr_h = upper_32_bits(send_mbox->wb_paddr); addr_l = lower_32_bits(send_mbox->wb_paddr); hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_H_OFF, addr_h); hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_L_OFF, addr_l); return 0; } static void free_mbox_wb_status(struct hinic_mbox_func_to_func *func_to_func) { struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox; struct hinic_hwdev *hwdev = func_to_func->hwdev; hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_H_OFF, 0); hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_L_OFF, 0); dma_free_coherent(hwdev->dev_hdl, MBOX_WB_STATUS_LEN, send_mbox->wb_vaddr, send_mbox->wb_paddr); } int hinic_vf_mbox_random_id_init(struct hinic_hwdev *hwdev) { u8 vf_in_pf; int err = 0; if (hinic_func_type(hwdev) == TYPE_VF) return 0; for (vf_in_pf = 1; vf_in_pf <= hinic_func_max_vf(hwdev); vf_in_pf++) { err = set_vf_mbox_random_id(hwdev, hinic_glb_pf_vf_offset(hwdev) + vf_in_pf); if (err) break; } if (err == HINIC_MGMT_CMD_UNSUPPORTED) { hwdev->func_to_func->support_vf_random = false; err = 0; sdk_warn(hwdev->dev_hdl, "Mgmt unsupport set vf random id\n"); } else if (!err) { hwdev->func_to_func->support_vf_random = true; sdk_info(hwdev->dev_hdl, "PF Set vf random id success\n"); } return err; } void hinic_set_mbox_seg_ack_mod(struct hinic_hwdev *hwdev, enum hinic_mbox_send_mod mod) { if (!hwdev || !hwdev->func_to_func) return; hwdev->func_to_func->send_ack_mod = mod; } int hinic_func_to_func_init(struct hinic_hwdev *hwdev) { struct hinic_mbox_func_to_func *func_to_func; struct card_node *chip_node; int err; func_to_func = kzalloc(sizeof(*func_to_func), GFP_KERNEL); if (!func_to_func) return -ENOMEM; hwdev->func_to_func = func_to_func; func_to_func->hwdev = hwdev; chip_node = hwdev->chip_node; func_to_func->vf_mbx_rand_id = chip_node->vf_mbx_rand_id; func_to_func->vf_mbx_old_rand_id = chip_node->vf_mbx_old_rand_id; sema_init(&func_to_func->mbox_send_sem, 1); sema_init(&func_to_func->msg_send_sem, 1); spin_lock_init(&func_to_func->mbox_lock); func_to_func->workq = create_singlethread_workqueue(HINIC_MBOX_WQ_NAME); if (!func_to_func->workq) { sdk_err(hwdev->dev_hdl, "Failed to initialize MBOX workqueue\n"); err = -ENOMEM; goto create_mbox_workq_err; } err = alloc_mbox_info(func_to_func->mbox_send); if (err) { sdk_err(hwdev->dev_hdl, "Alloc mem for mbox_active fail\n"); goto alloc_mbox_for_send_err; } err = alloc_mbox_info(func_to_func->mbox_resp); if (err) { sdk_err(hwdev->dev_hdl, "Alloc mem for mbox_passive fail\n"); goto alloc_mbox_for_resp_err; } err = alloc_mbox_wb_status(func_to_func); if (err) { sdk_err(hwdev->dev_hdl, "Failed to alloc mbox write back status\n"); goto alloc_wb_status_err; } prepare_send_mbox(func_to_func); func_to_func->send_ack_mod = HINIC_MBOX_SEND_MSG_POLL; return 0; alloc_wb_status_err: free_mbox_info(func_to_func->mbox_resp); alloc_mbox_for_resp_err: free_mbox_info(func_to_func->mbox_send); alloc_mbox_for_send_err: destroy_workqueue(func_to_func->workq); create_mbox_workq_err: kfree(func_to_func); return err; } void hinic_func_to_func_free(struct hinic_hwdev *hwdev) { struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; /* destroy workqueue before free related mbox resources in case of * illegal resource access */ destroy_workqueue(func_to_func->workq); free_mbox_wb_status(func_to_func); free_mbox_info(func_to_func->mbox_resp); free_mbox_info(func_to_func->mbox_send); kfree(func_to_func); } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_mbox.h000066400000000000000000000165061502027332500214270ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_MBOX_H_ #define HINIC_MBOX_H_ #define HINIC_MBOX_PF_SEND_ERR 0x1 #define HINIC_MBOX_PF_BUSY_ACTIVE_FW 0x2 #define HINIC_MBOX_VF_CMD_ERROR 0x3 #define HINIC_MAX_FUNCTIONS 512 #define HINIC_MAX_PF_FUNCS 16 #define HINIC_MBOX_WQ_NAME "hinic_mbox" enum hinic_mbox_seg_errcode { MBOX_ERRCODE_NO_ERRORS = 0, /* VF send the mailbox data to the wrong destination functions */ MBOX_ERRCODE_VF_TO_WRONG_FUNC = 0x100, /* PPF send the mailbox data to the wrong destination functions */ MBOX_ERRCODE_PPF_TO_WRONG_FUNC = 0x200, /* PF send the mailbox data to the wrong destination functions */ MBOX_ERRCODE_PF_TO_WRONG_FUNC = 0x300, /* The mailbox data size is set to all zero */ MBOX_ERRCODE_ZERO_DATA_SIZE = 0x400, /* The sender function attribute has not been learned by CPI hardware */ MBOX_ERRCODE_UNKNOWN_SRC_FUNC = 0x500, /* The receiver function attr has not been learned by CPI hardware */ MBOX_ERRCODE_UNKNOWN_DES_FUNC = 0x600, }; enum hinic_mbox_ack_type { MBOX_ACK, MBOX_NO_ACK, }; struct mbox_msg_info { u8 msg_id; u8 status; /*can only use 6 bit*/ }; struct hinic_recv_mbox { struct completion recv_done; void *mbox; u8 cmd; enum hinic_mod_type mod; u16 mbox_len; void *buf_out; enum hinic_mbox_ack_type ack_type; struct mbox_msg_info msg_info; u8 seq_id; }; struct hinic_send_mbox { struct completion send_done; u8 *data; u64 *wb_status; /* write back status */ void *wb_vaddr; dma_addr_t wb_paddr; }; typedef int (*hinic_vf_mbox_cb)(void *handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); typedef int (*hinic_pf_mbox_cb)(void *handle, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); typedef int (*hinic_ppf_mbox_cb)(void *handle, u16 pf_idx, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); typedef int (*hinic_pf_recv_from_ppf_mbox_cb)(void *handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); enum mbox_event_state { EVENT_START = 0, EVENT_FAIL, EVENT_TIMEOUT, EVENT_END, }; enum hinic_mbox_cb_state { HINIC_VF_MBOX_CB_REG = 0, HINIC_VF_MBOX_CB_RUNNING, HINIC_PF_MBOX_CB_REG, HINIC_PF_MBOX_CB_RUNNING, HINIC_PPF_MBOX_CB_REG, HINIC_PPF_MBOX_CB_RUNNING, HINIC_PPF_TO_PF_MBOX_CB_REG, HINIC_PPF_TO_PF_MBOX_CB_RUNNIG, }; enum hinic_mbox_send_mod { HINIC_MBOX_SEND_MSG_INT, HINIC_MBOX_SEND_MSG_POLL, }; struct hinic_mbox_func_to_func { struct hinic_hwdev *hwdev; struct semaphore mbox_send_sem; struct semaphore msg_send_sem; struct hinic_send_mbox send_mbox; struct workqueue_struct *workq; struct hinic_recv_mbox mbox_resp[HINIC_MAX_FUNCTIONS]; struct hinic_recv_mbox mbox_send[HINIC_MAX_FUNCTIONS]; hinic_vf_mbox_cb vf_mbox_cb[HINIC_MOD_MAX]; hinic_pf_mbox_cb pf_mbox_cb[HINIC_MOD_MAX]; hinic_ppf_mbox_cb ppf_mbox_cb[HINIC_MOD_MAX]; hinic_pf_recv_from_ppf_mbox_cb pf_recv_from_ppf_mbox_cb[HINIC_MOD_MAX]; unsigned long ppf_to_pf_mbox_cb_state[HINIC_MOD_MAX]; unsigned long ppf_mbox_cb_state[HINIC_MOD_MAX]; unsigned long pf_mbox_cb_state[HINIC_MOD_MAX]; unsigned long vf_mbox_cb_state[HINIC_MOD_MAX]; u8 send_msg_id; enum mbox_event_state event_flag; /* lock for mbox event flag */ spinlock_t mbox_lock; u32 *vf_mbx_old_rand_id; u32 *vf_mbx_rand_id; bool support_vf_random; enum hinic_mbox_send_mod send_ack_mod; }; struct hinic_mbox_work { struct work_struct work; u16 src_func_idx; struct hinic_mbox_func_to_func *func_to_func; struct hinic_recv_mbox *recv_mbox; }; struct vf_cmd_check_handle { u8 cmd; bool (*check_cmd)(struct hinic_hwdev *hwdev, u16 src_func_idx, void *buf_in, u16 in_size); }; int hinic_register_ppf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, hinic_ppf_mbox_cb callback); int hinic_register_pf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, hinic_pf_mbox_cb callback); int hinic_register_vf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, hinic_vf_mbox_cb callback); int hinic_register_ppf_to_pf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, hinic_pf_recv_from_ppf_mbox_cb callback); void hinic_unregister_ppf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod); void hinic_unregister_pf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod); void hinic_unregister_vf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod); void hinic_unregister_ppf_to_pf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod); void hinic_mbox_func_aeqe_handler(void *handle, u8 *header, u8 size); void hinic_mbox_self_aeqe_handler(void *handle, u8 *header, u8 size); int hinic_vf_mbox_random_id_init(struct hinic_hwdev *hwdev); bool hinic_mbox_check_func_id_8B(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size); bool hinic_mbox_check_func_id_10B(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size); bool hinic_mbox_check_cmd_valid(struct hinic_hwdev *hwdev, struct vf_cmd_check_handle *cmd_handle, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, u8 size); int hinic_func_to_func_init(struct hinic_hwdev *hwdev); void hinic_func_to_func_free(struct hinic_hwdev *hwdev); int hinic_mbox_to_host(struct hinic_hwdev *hwdev, u16 dest_host_ppf_id, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int hinic_mbox_to_ppf(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int hinic_mbox_to_pf(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int hinic_mbox_to_func_no_ack(struct hinic_hwdev *hwdev, u16 func_idx, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size); int hinic_mbox_to_pf_no_ack(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size); int hinic_mbox_ppf_to_pf(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u16 dst_pf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int hinic_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func, enum hinic_mod_type mod, u16 cmd, u16 dst_func, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int __hinic_mbox_to_vf(void *hwdev, enum hinic_mod_type mod, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int vf_to_pf_handler(void *handle, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); void hinic_set_mbox_seg_ack_mod(struct hinic_hwdev *hwdev, enum hinic_mbox_send_mod mod); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_mgmt.c000066400000000000000000001146751502027332500214270ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwdev.h" #include "hinic_hwif.h" #include "hinic_api_cmd.h" #include "hinic_mgmt.h" #include "hinic_nic_cfg.h" #include "hinic_mgmt_interface.h" #include "hinic_eqs.h" #define BUF_OUT_DEFAULT_SIZE 1 #define SEGMENT_LEN 48 #define MGMT_MSG_MAX_SEQ_ID (ALIGN(HINIC_MSG_TO_MGMT_MAX_LEN, \ SEGMENT_LEN) / SEGMENT_LEN) #define MAX_PF_MGMT_BUF_SIZE 2048UL #define MGMT_MSG_LAST_SEG_MAX_LEN (MAX_PF_MGMT_BUF_SIZE - \ SEGMENT_LEN * MGMT_MSG_MAX_SEQ_ID) #define MGMT_MSG_SIZE_MIN 20 #define MGMT_MSG_SIZE_STEP 16 #define MGMT_MSG_RSVD_FOR_DEV 8 #define MGMT_MSG_TIMEOUT 5000 /* millisecond */ #define SYNC_MSG_ID_MASK 0x1FF #define ASYNC_MSG_ID_MASK 0x1FF #define ASYNC_MSG_FLAG 0x200 #define MSG_NO_RESP 0xFFFF #define MAX_MSG_SZ 2016 #define MAX_CMD_BUF_SIZE 2048ULL #define MSG_SZ_IS_VALID(in_size) ((in_size) <= MAX_MSG_SZ) #define SYNC_MSG_ID(pf_to_mgmt) ((pf_to_mgmt)->sync_msg_id) #define SYNC_MSG_ID_INC(pf_to_mgmt) (SYNC_MSG_ID(pf_to_mgmt) = \ (SYNC_MSG_ID(pf_to_mgmt) + 1) & SYNC_MSG_ID_MASK) #define ASYNC_MSG_ID(pf_to_mgmt) ((pf_to_mgmt)->async_msg_id) #define ASYNC_MSG_ID_INC(pf_to_mgmt) (ASYNC_MSG_ID(pf_to_mgmt) = \ ((ASYNC_MSG_ID(pf_to_mgmt) + 1) & ASYNC_MSG_ID_MASK) \ | ASYNC_MSG_FLAG) static void pf_to_mgmt_send_event_set(struct hinic_msg_pf_to_mgmt *pf_to_mgmt, int event_flag) { spin_lock_bh(&pf_to_mgmt->sync_event_lock); pf_to_mgmt->event_flag = event_flag; spin_unlock_bh(&pf_to_mgmt->sync_event_lock); } /** * hinic_register_mgmt_msg_cb - register sync msg handler for a module * @hwdev: the pointer to hw device * @mod: module in the chip that this handler will handle its sync messages * @pri_handle: pri handle function * @callback: the handler for a sync message that will handle messages * Return: 0 - success, negative - failure */ int hinic_register_mgmt_msg_cb(void *hwdev, enum hinic_mod_type mod, void *pri_handle, hinic_mgmt_msg_cb callback) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt; if (mod >= HINIC_MOD_HW_MAX || !hwdev) return -EFAULT; pf_to_mgmt = ((struct hinic_hwdev *)hwdev)->pf_to_mgmt; if (!pf_to_mgmt) return -EINVAL; pf_to_mgmt->recv_mgmt_msg_cb[mod] = callback; pf_to_mgmt->recv_mgmt_msg_data[mod] = pri_handle; set_bit(HINIC_MGMT_MSG_CB_REG, &pf_to_mgmt->mgmt_msg_cb_state[mod]); return 0; } EXPORT_SYMBOL(hinic_register_mgmt_msg_cb); /** * hinic_unregister_mgmt_msg_cb - unregister sync msg handler for a module * @hwdev: the pointer to hw device * @mod: module in the chip that this handler will handle its sync messages */ void hinic_unregister_mgmt_msg_cb(void *hwdev, enum hinic_mod_type mod) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt; if (!hwdev || mod >= HINIC_MOD_HW_MAX) return; pf_to_mgmt = ((struct hinic_hwdev *)hwdev)->pf_to_mgmt; if (!pf_to_mgmt) return; clear_bit(HINIC_MGMT_MSG_CB_REG, &pf_to_mgmt->mgmt_msg_cb_state[mod]); while (test_bit(HINIC_MGMT_MSG_CB_RUNNING, &pf_to_mgmt->mgmt_msg_cb_state[mod])) usleep_range(900, 1000); pf_to_mgmt->recv_mgmt_msg_cb[mod] = NULL; pf_to_mgmt->recv_mgmt_msg_data[mod] = NULL; } EXPORT_SYMBOL(hinic_unregister_mgmt_msg_cb); void hinic_comm_recv_mgmt_self_cmd_reg(void *hwdev, u8 cmd, comm_up_self_msg_proc proc) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt; u8 cmd_idx; if (!hwdev || !proc) return; pf_to_mgmt = ((struct hinic_hwdev *)hwdev)->pf_to_mgmt; if (!pf_to_mgmt) return; cmd_idx = pf_to_mgmt->proc.cmd_num; if (cmd_idx >= HINIC_COMM_SELF_CMD_MAX) { sdk_err(pf_to_mgmt->hwdev->dev_hdl, "Register recv up process failed(cmd=0x%x)\r\n", cmd); return; } pf_to_mgmt->proc.info[cmd_idx].cmd = cmd; pf_to_mgmt->proc.info[cmd_idx].proc = proc; pf_to_mgmt->proc.cmd_num++; } void hinic_comm_recv_up_self_cmd_unreg(void *hwdev, u8 cmd) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt; u8 cmd_idx; if (!hwdev) return; pf_to_mgmt = ((struct hinic_hwdev *)hwdev)->pf_to_mgmt; if (!pf_to_mgmt) return; cmd_idx = pf_to_mgmt->proc.cmd_num; if (cmd_idx >= HINIC_COMM_SELF_CMD_MAX) { sdk_err(pf_to_mgmt->hwdev->dev_hdl, "Unregister recv up process failed(cmd=0x%x)\r\n", cmd); return; } for (cmd_idx = 0; cmd_idx < HINIC_COMM_SELF_CMD_MAX; cmd_idx++) { if (cmd == pf_to_mgmt->proc.info[cmd_idx].cmd) { pf_to_mgmt->proc.info[cmd_idx].cmd = 0; pf_to_mgmt->proc.info[cmd_idx].proc = NULL; pf_to_mgmt->proc.cmd_num--; } } } /** * mgmt_msg_len - calculate the total message length * @msg_data_len: the length of the message data * Return: the total message length */ static u16 mgmt_msg_len(u16 msg_data_len) { /* u64 - the size of the header */ u16 msg_size; msg_size = (u16)(MGMT_MSG_RSVD_FOR_DEV + sizeof(u64) + msg_data_len); if (msg_size > MGMT_MSG_SIZE_MIN) msg_size = MGMT_MSG_SIZE_MIN + ALIGN((msg_size - MGMT_MSG_SIZE_MIN), MGMT_MSG_SIZE_STEP); else msg_size = MGMT_MSG_SIZE_MIN; return msg_size; } /** * prepare_header - prepare the header of the message * @pf_to_mgmt: PF to MGMT channel * @header: pointer of the header to prepare * @msg_len: the length of the message * @mod: module in the chip that will get the message * @ack_type: message ack type * @direction: the direction of the original message * @cmd: cmd type * @msg_id: message id */ static void prepare_header(struct hinic_msg_pf_to_mgmt *pf_to_mgmt, u64 *header, u16 msg_len, enum hinic_mod_type mod, enum hinic_msg_ack_type ack_type, enum hinic_msg_direction_type direction, enum hinic_mgmt_cmd cmd, u32 msg_id) { struct hinic_hwif *hwif = pf_to_mgmt->hwdev->hwif; *header = HINIC_MSG_HEADER_SET(msg_len, MSG_LEN) | HINIC_MSG_HEADER_SET(mod, MODULE) | HINIC_MSG_HEADER_SET(msg_len, SEG_LEN) | HINIC_MSG_HEADER_SET(ack_type, NO_ACK) | HINIC_MSG_HEADER_SET(0, ASYNC_MGMT_TO_PF) | HINIC_MSG_HEADER_SET(0, SEQID) | HINIC_MSG_HEADER_SET(LAST_SEGMENT, LAST) | HINIC_MSG_HEADER_SET(direction, DIRECTION) | HINIC_MSG_HEADER_SET(cmd, CMD) | HINIC_MSG_HEADER_SET(HINIC_PCI_INTF_IDX(hwif), PCI_INTF_IDX) | HINIC_MSG_HEADER_SET(hwif->attr.port_to_port_idx, P2P_IDX) | HINIC_MSG_HEADER_SET(msg_id, MSG_ID); } static void clp_prepare_header(struct hinic_hwdev *hwdev, u64 *header, u16 msg_len, enum hinic_mod_type mod, enum hinic_msg_ack_type ack_type, enum hinic_msg_direction_type direction, enum hinic_mgmt_cmd cmd, u32 msg_id) { struct hinic_hwif *hwif = hwdev->hwif; *header = HINIC_MSG_HEADER_SET(msg_len, MSG_LEN) | HINIC_MSG_HEADER_SET(mod, MODULE) | HINIC_MSG_HEADER_SET(msg_len, SEG_LEN) | HINIC_MSG_HEADER_SET(ack_type, NO_ACK) | HINIC_MSG_HEADER_SET(0, ASYNC_MGMT_TO_PF) | HINIC_MSG_HEADER_SET(0, SEQID) | HINIC_MSG_HEADER_SET(LAST_SEGMENT, LAST) | HINIC_MSG_HEADER_SET(direction, DIRECTION) | HINIC_MSG_HEADER_SET(cmd, CMD) | HINIC_MSG_HEADER_SET(HINIC_PCI_INTF_IDX(hwif), PCI_INTF_IDX) | HINIC_MSG_HEADER_SET(hwif->attr.port_to_port_idx, P2P_IDX) | HINIC_MSG_HEADER_SET(msg_id, MSG_ID); } /** * prepare_mgmt_cmd - prepare the mgmt command * @mgmt_cmd: pointer to the command to prepare * @header: pointer of the header to prepare * @msg: the data of the message * @msg_len: the length of the message */ static void prepare_mgmt_cmd(u8 *mgmt_cmd, u64 *header, const void *msg, int msg_len) { memset(mgmt_cmd, 0, MGMT_MSG_RSVD_FOR_DEV); mgmt_cmd += MGMT_MSG_RSVD_FOR_DEV; memcpy(mgmt_cmd, header, sizeof(*header)); mgmt_cmd += sizeof(*header); memcpy(mgmt_cmd, msg, msg_len); } /** * send_msg_to_mgmt_async - send async message * @pf_to_mgmt: PF to MGMT channel * @mod: module in the chip that will get the message * @cmd: command of the message * @msg: the data of the message * @msg_len: the length of the message * @direction: the direction of the original message * @resp_msg_id: msg id to response for * Return: 0 - success, negative - failure */ static int send_msg_to_mgmt_async(struct hinic_msg_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod, u8 cmd, void *msg, u16 msg_len, enum hinic_msg_direction_type direction, u16 resp_msg_id) { void *mgmt_cmd = pf_to_mgmt->async_msg_buf; struct hinic_api_cmd_chain *chain; u64 header; u16 cmd_size = mgmt_msg_len(msg_len); if (!hinic_get_chip_present_flag(pf_to_mgmt->hwdev)) return -EFAULT; if (cmd_size > MAX_MSG_SZ) return -EINVAL; if (direction == HINIC_MSG_RESPONSE) prepare_header(pf_to_mgmt, &header, msg_len, mod, HINIC_MSG_ACK, direction, cmd, resp_msg_id); else prepare_header(pf_to_mgmt, &header, msg_len, mod, HINIC_MSG_ACK, direction, cmd, ASYNC_MSG_ID(pf_to_mgmt)); prepare_mgmt_cmd((u8 *)mgmt_cmd, &header, msg, msg_len); chain = pf_to_mgmt->cmd_chain[HINIC_API_CMD_WRITE_ASYNC_TO_MGMT_CPU]; return hinic_api_cmd_write(chain, HINIC_NODE_ID_MGMT_HOST, mgmt_cmd, cmd_size); } int hinic_pf_to_mgmt_async(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt; void *dev = ((struct hinic_hwdev *)hwdev)->dev_hdl; int err; pf_to_mgmt = ((struct hinic_hwdev *)hwdev)->pf_to_mgmt; /* Lock the async_msg_buf */ spin_lock_bh(&pf_to_mgmt->async_msg_lock); ASYNC_MSG_ID_INC(pf_to_mgmt); err = send_msg_to_mgmt_async(pf_to_mgmt, mod, cmd, buf_in, in_size, HINIC_MSG_DIRECT_SEND, MSG_NO_RESP); spin_unlock_bh(&pf_to_mgmt->async_msg_lock); if (err) { sdk_err(dev, "Failed to send async mgmt msg\n"); return err; } return 0; } /** * send_msg_to_mgmt_sync - send async message * @pf_to_mgmt: PF to MGMT channel * @mod: module in the chip that will get the message * @cmd: command of the message * @msg: the msg data * @msg_len: the msg data length * @ack_type: message ack type * @direction: the direction of the original message * @resp_msg_id: msg id to response for * Return: 0 - success, negative - failure */ static int send_msg_to_mgmt_sync(struct hinic_msg_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod, u8 cmd, void *msg, u16 msg_len, enum hinic_msg_ack_type ack_type, enum hinic_msg_direction_type direction, u16 resp_msg_id) { void *mgmt_cmd = pf_to_mgmt->sync_msg_buf; struct hinic_api_cmd_chain *chain; u64 header; u16 cmd_size = mgmt_msg_len(msg_len); if (!hinic_get_chip_present_flag(pf_to_mgmt->hwdev)) return -EFAULT; if (cmd_size > MAX_MSG_SZ) return -EINVAL; if (direction == HINIC_MSG_RESPONSE) prepare_header(pf_to_mgmt, &header, msg_len, mod, ack_type, direction, cmd, resp_msg_id); else prepare_header(pf_to_mgmt, &header, msg_len, mod, ack_type, direction, cmd, SYNC_MSG_ID_INC(pf_to_mgmt)); if (ack_type == HINIC_MSG_ACK) pf_to_mgmt_send_event_set(pf_to_mgmt, SEND_EVENT_START); prepare_mgmt_cmd((u8 *)mgmt_cmd, &header, msg, msg_len); chain = pf_to_mgmt->cmd_chain[HINIC_API_CMD_WRITE_TO_MGMT_CPU]; return hinic_api_cmd_write(chain, HINIC_NODE_ID_MGMT_HOST, mgmt_cmd, cmd_size); } int hinic_pf_to_mgmt_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt; void *dev = ((struct hinic_hwdev *)hwdev)->dev_hdl; struct hinic_recv_msg *recv_msg; struct hinic_msg_head *msg_head; struct completion *recv_done; ulong timeo; int err; ulong ret; /* set aeq fix num to 3, need to ensure response aeq id < 3*/ if (mod == HINIC_MOD_COMM || mod == HINIC_MOD_L2NIC) { msg_head = buf_in; if (msg_head->resp_aeq_num >= HINIC_MAX_AEQS) msg_head->resp_aeq_num = 0; } pf_to_mgmt = ((struct hinic_hwdev *)hwdev)->pf_to_mgmt; /* Lock the sync_msg_buf */ down(&pf_to_mgmt->sync_msg_lock); recv_msg = &pf_to_mgmt->recv_resp_msg_from_mgmt; recv_done = &recv_msg->recv_done; init_completion(recv_done); err = send_msg_to_mgmt_sync(pf_to_mgmt, mod, cmd, buf_in, in_size, HINIC_MSG_ACK, HINIC_MSG_DIRECT_SEND, MSG_NO_RESP); if (err) { sdk_err(dev, "Failed to send sync msg mod %d cmd 0x%x to mgmt, sync_msg_id: %d\n", mod, cmd, pf_to_mgmt->sync_msg_id); pf_to_mgmt_send_event_set(pf_to_mgmt, SEND_EVENT_FAIL); goto unlock_sync_msg; } timeo = msecs_to_jiffies(timeout ? timeout : MGMT_MSG_TIMEOUT); ret = wait_for_completion_timeout(recv_done, timeo); if (!ret) { sdk_err(dev, "Mgmt response sync msg mod %d cmd 0x%x timeout, sync_msg_id: %d\n", mod, cmd, pf_to_mgmt->sync_msg_id); hinic_dump_aeq_info((struct hinic_hwdev *)hwdev); err = -ETIMEDOUT; pf_to_mgmt_send_event_set(pf_to_mgmt, SEND_EVENT_TIMEOUT); goto unlock_sync_msg; } pf_to_mgmt_send_event_set(pf_to_mgmt, SEND_EVENT_END); if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag)) { up(&pf_to_mgmt->sync_msg_lock); return -ETIMEDOUT; } if (buf_out && out_size) { if (*out_size < recv_msg->msg_len) { sdk_err(dev, "Invalid response message length: %d for mod %d cmd 0x%x from mgmt, should less than: %d\n", recv_msg->msg_len, mod, cmd, *out_size); err = -EFAULT; goto unlock_sync_msg; } if (recv_msg->msg_len) memcpy(buf_out, recv_msg->msg, recv_msg->msg_len); *out_size = recv_msg->msg_len; } unlock_sync_msg: up(&pf_to_mgmt->sync_msg_lock); return err; } static int __get_clp_reg(void *hwdev, enum clp_data_type data_type, enum clp_reg_type reg_type, u32 *reg_addr) { struct hinic_hwdev *dev = hwdev; u32 offset; offset = HINIC_CLP_REG_GAP * hinic_pcie_itf_id(dev); switch (reg_type) { case HINIC_CLP_BA_HOST: *reg_addr = (data_type == HINIC_CLP_REQ_HOST) ? HINIC_CLP_REG(REQ_SRAM_BA) : HINIC_CLP_REG(RSP_SRAM_BA); break; case HINIC_CLP_SIZE_HOST: *reg_addr = HINIC_CLP_REG(SRAM_SIZE); break; case HINIC_CLP_LEN_HOST: *reg_addr = (data_type == HINIC_CLP_REQ_HOST) ? HINIC_CLP_REG(REQ) : HINIC_CLP_REG(RSP); break; case HINIC_CLP_START_REQ_HOST: *reg_addr = HINIC_CLP_REG(REQ); break; case HINIC_CLP_READY_RSP_HOST: *reg_addr = HINIC_CLP_REG(RSP); break; default: *reg_addr = 0; break; } if (*reg_addr == 0) return -EINVAL; *reg_addr += offset; return 0; } static int hinic_read_clp_reg(struct hinic_hwdev *hwdev, enum clp_data_type data_type, enum clp_reg_type reg_type, u32 *read_value) { int err; u32 reg_addr, reg_value; if (data_type == HINIC_CLP_REQ_HOST && reg_type == HINIC_CLP_READY_RSP_HOST) return -EINVAL; if (data_type == HINIC_CLP_RSP_HOST && reg_type == HINIC_CLP_START_REQ_HOST) return -EINVAL; err = __get_clp_reg(hwdev, data_type, reg_type, ®_addr); if (err) return err; reg_value = hinic_hwif_read_reg(hwdev->hwif, reg_addr); switch (reg_type) { case HINIC_CLP_BA_HOST: reg_value = ((reg_value >> HINIC_CLP_OFFSET(SRAM_BASE)) & HINIC_CLP_MASK(SRAM_BASE)); break; case HINIC_CLP_SIZE_HOST: reg_value = ((reg_value >> HINIC_CLP_OFFSET(SRAM_SIZE)) & HINIC_CLP_MASK(SRAM_SIZE)); break; case HINIC_CLP_LEN_HOST: reg_value = ((reg_value >> HINIC_CLP_OFFSET(LEN)) & HINIC_CLP_MASK(LEN)); break; case HINIC_CLP_START_REQ_HOST: reg_value = ((reg_value >> HINIC_CLP_OFFSET(START)) & HINIC_CLP_MASK(START)); break; case HINIC_CLP_READY_RSP_HOST: reg_value = ((reg_value >> HINIC_CLP_OFFSET(READY)) & HINIC_CLP_MASK(READY)); break; default: break; } *read_value = reg_value; return 0; } static int __check_reg_value(enum clp_reg_type reg_type, u32 value) { if (reg_type == HINIC_CLP_BA_HOST && value > HINIC_CLP_SRAM_BASE_REG_MAX) return -EINVAL; if (reg_type == HINIC_CLP_SIZE_HOST && value > HINIC_CLP_SRAM_SIZE_REG_MAX) return -EINVAL; if (reg_type == HINIC_CLP_LEN_HOST && value > HINIC_CLP_LEN_REG_MAX) return -EINVAL; if (reg_type == HINIC_CLP_START_REQ_HOST && value > HINIC_CLP_START_OR_READY_REG_MAX) return -EINVAL; if (reg_type == HINIC_CLP_READY_RSP_HOST && value > HINIC_CLP_START_OR_READY_REG_MAX) return -EINVAL; return 0; } static void hinic_write_clp_reg(struct hinic_hwdev *hwdev, enum clp_data_type data_type, enum clp_reg_type reg_type, u32 value) { u32 reg_addr, reg_value; if (data_type == HINIC_CLP_REQ_HOST && reg_type == HINIC_CLP_READY_RSP_HOST) return; if (data_type == HINIC_CLP_RSP_HOST && reg_type == HINIC_CLP_START_REQ_HOST) return; if (__check_reg_value(reg_type, value)) return; if (__get_clp_reg(hwdev, data_type, reg_type, ®_addr)) return; reg_value = hinic_hwif_read_reg(hwdev->hwif, reg_addr); switch (reg_type) { case HINIC_CLP_LEN_HOST: reg_value = reg_value & (~(HINIC_CLP_MASK(LEN) << HINIC_CLP_OFFSET(LEN))); reg_value = reg_value | (value << HINIC_CLP_OFFSET(LEN)); break; case HINIC_CLP_START_REQ_HOST: reg_value = reg_value & (~(HINIC_CLP_MASK(START) << HINIC_CLP_OFFSET(START))); reg_value = reg_value | (value << HINIC_CLP_OFFSET(START)); break; case HINIC_CLP_READY_RSP_HOST: reg_value = reg_value & (~(HINIC_CLP_MASK(READY) << HINIC_CLP_OFFSET(READY))); reg_value = reg_value | (value << HINIC_CLP_OFFSET(READY)); break; default: return; } hinic_hwif_write_reg(hwdev->hwif, reg_addr, reg_value); } static int hinic_read_clp_data(struct hinic_hwdev *hwdev, void *buf_out, u16 *out_size) { int err; u32 reg = HINIC_CLP_DATA(RSP); u32 ready, delay_cnt; u32 *ptr = (u32 *)buf_out; u32 temp_out_size = 0; err = hinic_read_clp_reg(hwdev, HINIC_CLP_RSP_HOST, HINIC_CLP_READY_RSP_HOST, &ready); if (err) return err; delay_cnt = 0; while (ready == 0) { usleep_range(9000, 10000); delay_cnt++; err = hinic_read_clp_reg(hwdev, HINIC_CLP_RSP_HOST, HINIC_CLP_READY_RSP_HOST, &ready); if (err || delay_cnt > HINIC_CLP_DELAY_CNT_MAX) { sdk_err(hwdev->dev_hdl, "Timeout with delay_cnt: %d\n", delay_cnt); return -EINVAL; } } err = hinic_read_clp_reg(hwdev, HINIC_CLP_RSP_HOST, HINIC_CLP_LEN_HOST, &temp_out_size); if (err) return err; if (temp_out_size > HINIC_CLP_SRAM_SIZE_REG_MAX || !temp_out_size) { sdk_err(hwdev->dev_hdl, "Invalid temp_out_size: %d\n", temp_out_size); return -EINVAL; } *out_size = (u16)(temp_out_size & 0xffff); for (; temp_out_size > 0; temp_out_size--) { *ptr = hinic_hwif_read_reg(hwdev->hwif, reg); ptr++; reg = reg + 4; } hinic_write_clp_reg(hwdev, HINIC_CLP_RSP_HOST, HINIC_CLP_READY_RSP_HOST, (u32)0x0); hinic_write_clp_reg(hwdev, HINIC_CLP_RSP_HOST, HINIC_CLP_LEN_HOST, (u32)0x0); return 0; } static int hinic_write_clp_data(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size) { int err; u32 reg = HINIC_CLP_DATA(REQ); u32 start = 1; u32 delay_cnt = 0; u32 *ptr = (u32 *)buf_in; err = hinic_read_clp_reg(hwdev, HINIC_CLP_REQ_HOST, HINIC_CLP_START_REQ_HOST, &start); if (err) return err; while (start == 1) { usleep_range(9000, 10000); delay_cnt++; err = hinic_read_clp_reg(hwdev, HINIC_CLP_REQ_HOST, HINIC_CLP_START_REQ_HOST, &start); if (err || delay_cnt > HINIC_CLP_DELAY_CNT_MAX) return -EINVAL; } hinic_write_clp_reg(hwdev, HINIC_CLP_REQ_HOST, HINIC_CLP_LEN_HOST, in_size); hinic_write_clp_reg(hwdev, HINIC_CLP_REQ_HOST, HINIC_CLP_START_REQ_HOST, (u32)0x1); for (; in_size > 0; in_size--) { hinic_hwif_write_reg(hwdev->hwif, reg, *ptr); ptr++; reg = reg + 4; } return 0; } static int hinic_check_clp_init_status(struct hinic_hwdev *hwdev) { int err; u32 reg_value = 0; err = hinic_read_clp_reg(hwdev, HINIC_CLP_REQ_HOST, HINIC_CLP_BA_HOST, ®_value); if (err || !reg_value) { sdk_err(hwdev->dev_hdl, "Wrong req ba value: 0x%x\n", reg_value); return -EINVAL; } err = hinic_read_clp_reg(hwdev, HINIC_CLP_RSP_HOST, HINIC_CLP_BA_HOST, ®_value); if (err || !reg_value) { sdk_err(hwdev->dev_hdl, "Wrong rsp ba value: 0x%x\n", reg_value); return -EINVAL; } err = hinic_read_clp_reg(hwdev, HINIC_CLP_REQ_HOST, HINIC_CLP_SIZE_HOST, ®_value); if (err || !reg_value) { sdk_err(hwdev->dev_hdl, "Wrong req size\n"); return -EINVAL; } err = hinic_read_clp_reg(hwdev, HINIC_CLP_RSP_HOST, HINIC_CLP_SIZE_HOST, ®_value); if (err || !reg_value) { sdk_err(hwdev->dev_hdl, "Wrong rsp size\n"); return -EINVAL; } return 0; } static void hinic_clear_clp_data(struct hinic_hwdev *hwdev, enum clp_data_type data_type) { u32 reg = (data_type == HINIC_CLP_REQ_HOST) ? HINIC_CLP_DATA(REQ) : HINIC_CLP_DATA(RSP); u32 count = HINIC_CLP_INPUT_BUFFER_LEN_HOST / HINIC_CLP_DATA_UNIT_HOST; for (; count > 0; count--) { hinic_hwif_write_reg(hwdev->hwif, reg, 0x0); reg = reg + 4; } } int hinic_pf_clp_to_mgmt(void *hwdev, enum hinic_mod_type mod, u8 cmd, const void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_clp_pf_to_mgmt *clp_pf_to_mgmt; struct hinic_hwdev *dev = hwdev; u64 header; u16 real_size; u8 *clp_msg_buf; int err; clp_pf_to_mgmt = ((struct hinic_hwdev *)hwdev)->clp_pf_to_mgmt; clp_msg_buf = clp_pf_to_mgmt->clp_msg_buf; /* 4 bytes alignment */ if (in_size % HINIC_CLP_DATA_UNIT_HOST) real_size = (in_size + (u16)sizeof(header) + HINIC_CLP_DATA_UNIT_HOST); else real_size = in_size + (u16)sizeof(header); real_size = real_size / HINIC_CLP_DATA_UNIT_HOST; if (real_size > (HINIC_CLP_INPUT_BUFFER_LEN_HOST / HINIC_CLP_DATA_UNIT_HOST)) { sdk_err(dev->dev_hdl, "Invalid real_size: %d\n", real_size); return -EINVAL; } down(&clp_pf_to_mgmt->clp_msg_lock); err = hinic_check_clp_init_status(dev); if (err) { sdk_err(dev->dev_hdl, "Check clp init status failed\n"); up(&clp_pf_to_mgmt->clp_msg_lock); return err; } hinic_clear_clp_data(dev, HINIC_CLP_RSP_HOST); hinic_write_clp_reg(dev, HINIC_CLP_RSP_HOST, HINIC_CLP_READY_RSP_HOST, 0x0); /* Send request */ memset(clp_msg_buf, 0x0, HINIC_CLP_INPUT_BUFFER_LEN_HOST); clp_prepare_header(dev, &header, in_size, mod, 0, 0, cmd, 0); memcpy(clp_msg_buf, &header, sizeof(header)); clp_msg_buf += sizeof(header); memcpy(clp_msg_buf, buf_in, in_size); clp_msg_buf = clp_pf_to_mgmt->clp_msg_buf; hinic_clear_clp_data(dev, HINIC_CLP_REQ_HOST); err = hinic_write_clp_data(hwdev, clp_pf_to_mgmt->clp_msg_buf, real_size); if (err) { sdk_err(dev->dev_hdl, "Send clp request failed\n"); up(&clp_pf_to_mgmt->clp_msg_lock); return -EINVAL; } /* Get response */ clp_msg_buf = clp_pf_to_mgmt->clp_msg_buf; memset(clp_msg_buf, 0x0, HINIC_CLP_INPUT_BUFFER_LEN_HOST); err = hinic_read_clp_data(hwdev, clp_msg_buf, &real_size); hinic_clear_clp_data(dev, HINIC_CLP_RSP_HOST); if (err) { sdk_err(dev->dev_hdl, "Read clp response failed\n"); up(&clp_pf_to_mgmt->clp_msg_lock); return -EINVAL; } real_size = (u16)((real_size * HINIC_CLP_DATA_UNIT_HOST) & 0xffff); if (real_size <= sizeof(header) || real_size > HINIC_CLP_INPUT_BUFFER_LEN_HOST) { sdk_err(dev->dev_hdl, "Invalid response size: %d", real_size); up(&clp_pf_to_mgmt->clp_msg_lock); return -EINVAL; } real_size = real_size - sizeof(header); if (real_size != *out_size) { sdk_err(dev->dev_hdl, "Invalid real_size: %d, out_size: %d\n", real_size, *out_size); up(&clp_pf_to_mgmt->clp_msg_lock); return -EINVAL; } memcpy(buf_out, (clp_msg_buf + sizeof(header)), real_size); up(&clp_pf_to_mgmt->clp_msg_lock); return 0; } int hinic_msg_to_mgmt_poll_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { return 0; } /* This function is only used by txrx flush */ int hinic_pf_to_mgmt_no_ack(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt; void *dev = ((struct hinic_hwdev *)hwdev)->dev_hdl; int err = -EINVAL; if (!hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MGMT_INITED)) { sdk_err(dev, "Mgmt module not initialized\n"); return -EINVAL; } pf_to_mgmt = ((struct hinic_hwdev *)hwdev)->pf_to_mgmt; if (!MSG_SZ_IS_VALID(in_size)) { sdk_err(dev, "Mgmt msg buffer size: %d is not valid\n", in_size); return -EINVAL; } if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag)) return -EPERM; /* Lock the sync_msg_buf */ down(&pf_to_mgmt->sync_msg_lock); err = send_msg_to_mgmt_sync(pf_to_mgmt, mod, cmd, buf_in, in_size, HINIC_MSG_NO_ACK, HINIC_MSG_DIRECT_SEND, MSG_NO_RESP); up(&pf_to_mgmt->sync_msg_lock); return err; } /** * api cmd write or read bypass defaut use poll, if want to use aeq interrupt, * please set wb_trigger_aeqe to 1 */ int hinic_api_cmd_write_nack(void *hwdev, u8 dest, void *cmd, u16 size) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt; struct hinic_api_cmd_chain *chain; if (!hwdev || !size || !cmd) return -EINVAL; if (size > MAX_CMD_BUF_SIZE) return -EINVAL; if (!hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MGMT_INITED) || hinic_get_mgmt_channel_status(hwdev)) return -EPERM; pf_to_mgmt = ((struct hinic_hwdev *)hwdev)->pf_to_mgmt; chain = pf_to_mgmt->cmd_chain[HINIC_API_CMD_POLL_WRITE]; if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag)) return -EPERM; return hinic_api_cmd_write(chain, dest, cmd, size); } EXPORT_SYMBOL(hinic_api_cmd_write_nack); int hinic_api_cmd_read_ack(void *hwdev, u8 dest, void *cmd, u16 size, void *ack, u16 ack_size) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt; struct hinic_api_cmd_chain *chain; if (!hwdev || !cmd || (ack_size && !ack)) return -EINVAL; if (size > MAX_CMD_BUF_SIZE) return -EINVAL; if (!hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MGMT_INITED) || hinic_get_mgmt_channel_status(hwdev)) return -EPERM; pf_to_mgmt = ((struct hinic_hwdev *)hwdev)->pf_to_mgmt; chain = pf_to_mgmt->cmd_chain[HINIC_API_CMD_POLL_READ]; if (!(((struct hinic_hwdev *)hwdev)->chip_present_flag)) return -EPERM; return hinic_api_cmd_read(chain, dest, cmd, size, ack, ack_size); } EXPORT_SYMBOL(hinic_api_cmd_read_ack); static void __send_mgmt_ack(struct hinic_msg_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, u16 msg_id) { u16 buf_size; if (!in_size) buf_size = BUF_OUT_DEFAULT_SIZE; else buf_size = in_size; spin_lock_bh(&pf_to_mgmt->async_msg_lock); /* MGMT sent sync msg, send the response */ send_msg_to_mgmt_async(pf_to_mgmt, mod, cmd, buf_in, buf_size, HINIC_MSG_RESPONSE, msg_id); spin_unlock_bh(&pf_to_mgmt->async_msg_lock); } /** * mgmt_recv_msg_handler - handler for message from mgmt cpu * @pf_to_mgmt: PF to MGMT channel * @mod: module in the chip that will get the message * @cmd: command of the message * @buf_in: the buffer of recv msg * @in_size: the size of buffer * @msg_id: message id * @need_resp: the flag of need resp */ static void mgmt_recv_msg_handler(struct hinic_msg_pf_to_mgmt *pf_to_mgmt, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, u16 msg_id, int need_resp) { void *dev = pf_to_mgmt->hwdev->dev_hdl; void *buf_out = pf_to_mgmt->mgmt_ack_buf; enum hinic_mod_type tmp_mod = mod; bool ack_first = false; u16 out_size = 0; memset(buf_out, 0, MAX_PF_MGMT_BUF_SIZE); if (mod >= HINIC_MOD_HW_MAX) { sdk_warn(dev, "Receive illegal message from mgmt cpu, mod = %d\n", mod); goto resp; } set_bit(HINIC_MGMT_MSG_CB_RUNNING, &pf_to_mgmt->mgmt_msg_cb_state[tmp_mod]); if (!pf_to_mgmt->recv_mgmt_msg_cb[mod] || !test_bit(HINIC_MGMT_MSG_CB_REG, &pf_to_mgmt->mgmt_msg_cb_state[tmp_mod])) { sdk_warn(dev, "Receive mgmt callback is null, mod = %d\n", mod); clear_bit(HINIC_MGMT_MSG_CB_RUNNING, &pf_to_mgmt->mgmt_msg_cb_state[tmp_mod]); goto resp; } ack_first = hinic_mgmt_event_ack_first(mod, cmd); if (ack_first && need_resp) { /* send ack to mgmt first to avoid command timeout in * mgmt(100ms in mgmt); * mgmt to host command don't need any response data from host, * just need ack from host */ __send_mgmt_ack(pf_to_mgmt, mod, cmd, buf_out, in_size, msg_id); } pf_to_mgmt->recv_mgmt_msg_cb[tmp_mod](pf_to_mgmt->hwdev, pf_to_mgmt->recv_mgmt_msg_data[tmp_mod], cmd, buf_in, in_size, buf_out, &out_size); clear_bit(HINIC_MGMT_MSG_CB_RUNNING, &pf_to_mgmt->mgmt_msg_cb_state[tmp_mod]); resp: if (!ack_first && need_resp) __send_mgmt_ack(pf_to_mgmt, mod, cmd, buf_out, out_size, msg_id); } /** * mgmt_resp_msg_handler - handler for response message from mgmt cpu * @pf_to_mgmt: PF to MGMT channel * @recv_msg: received message details */ static void mgmt_resp_msg_handler(struct hinic_msg_pf_to_mgmt *pf_to_mgmt, struct hinic_recv_msg *recv_msg) { void *dev = pf_to_mgmt->hwdev->dev_hdl; /* delete async msg */ if (recv_msg->msg_id & ASYNC_MSG_FLAG) return; spin_lock_bh(&pf_to_mgmt->sync_event_lock); if (recv_msg->msg_id == pf_to_mgmt->sync_msg_id && pf_to_mgmt->event_flag == SEND_EVENT_START) { complete(&recv_msg->recv_done); } else if (recv_msg->msg_id != pf_to_mgmt->sync_msg_id) { sdk_err(dev, "Send msg id(0x%x) recv msg id(0x%x) dismatch, event state: %d\n", pf_to_mgmt->sync_msg_id, recv_msg->msg_id, pf_to_mgmt->event_flag); } else { sdk_err(dev, "Wait timeout, send msg id(0x%x) recv msg id(0x%x), event state: %d\n", pf_to_mgmt->sync_msg_id, recv_msg->msg_id, pf_to_mgmt->event_flag); } spin_unlock_bh(&pf_to_mgmt->sync_event_lock); } static void recv_mgmt_msg_work_handler(struct work_struct *work) { struct hinic_mgmt_msg_handle_work *mgmt_work = container_of(work, struct hinic_mgmt_msg_handle_work, work); mgmt_recv_msg_handler(mgmt_work->pf_to_mgmt, mgmt_work->mod, mgmt_work->cmd, mgmt_work->msg, mgmt_work->msg_len, mgmt_work->msg_id, !mgmt_work->async_mgmt_to_pf); kfree(mgmt_work->msg); kfree(mgmt_work); } static bool check_mgmt_seq_id_and_seg_len(struct hinic_recv_msg *recv_msg, u8 seq_id, u8 seg_len) { if (seq_id > MGMT_MSG_MAX_SEQ_ID || seg_len > SEGMENT_LEN) return false; else if (seq_id == MGMT_MSG_MAX_SEQ_ID && seg_len > MGMT_MSG_LAST_SEG_MAX_LEN) return false; if (seq_id == 0) { recv_msg->seq_id = seq_id; } else { if (seq_id != recv_msg->seq_id + 1) return false; recv_msg->seq_id = seq_id; } return true; } /** * recv_mgmt_msg_handler - handler a message from mgmt cpu * @pf_to_mgmt: PF to MGMT channel * @header: the header of the message * @recv_msg: received message details */ static void recv_mgmt_msg_handler(struct hinic_msg_pf_to_mgmt *pf_to_mgmt, u8 *header, struct hinic_recv_msg *recv_msg) { struct hinic_mgmt_msg_handle_work *mgmt_work; u64 mbox_header = *((u64 *)header); void *msg_body = header + sizeof(mbox_header); u8 seq_id, seq_len; u32 offset; u64 dir; /* Don't need to get anything from hw when cmd is async */ dir = HINIC_MSG_HEADER_GET(mbox_header, DIRECTION); if (dir == HINIC_MSG_RESPONSE && HINIC_MSG_HEADER_GET(mbox_header, MSG_ID) & ASYNC_MSG_FLAG) return; seq_len = HINIC_MSG_HEADER_GET(mbox_header, SEG_LEN); seq_id = HINIC_MSG_HEADER_GET(mbox_header, SEQID); if (!check_mgmt_seq_id_and_seg_len(recv_msg, seq_id, seq_len)) { sdk_err(pf_to_mgmt->hwdev->dev_hdl, "Mgmt msg sequence id and segment length check fail, front seq_id: 0x%x, current seq_id: 0x%x, seg len: 0x%x\n", recv_msg->seq_id, seq_id, seq_len); /* set seq_id to invalid seq_id */ recv_msg->seq_id = MGMT_MSG_MAX_SEQ_ID; return; } offset = seq_id * SEGMENT_LEN; memcpy((u8 *)recv_msg->msg + offset, msg_body, seq_len); if (!HINIC_MSG_HEADER_GET(mbox_header, LAST)) return; recv_msg->cmd = HINIC_MSG_HEADER_GET(mbox_header, CMD); recv_msg->mod = HINIC_MSG_HEADER_GET(mbox_header, MODULE); recv_msg->async_mgmt_to_pf = HINIC_MSG_HEADER_GET(mbox_header, ASYNC_MGMT_TO_PF); recv_msg->msg_len = HINIC_MSG_HEADER_GET(mbox_header, MSG_LEN); recv_msg->msg_id = HINIC_MSG_HEADER_GET(mbox_header, MSG_ID); recv_msg->seq_id = MGMT_MSG_MAX_SEQ_ID; if (HINIC_MSG_HEADER_GET(mbox_header, DIRECTION) == HINIC_MSG_RESPONSE) { mgmt_resp_msg_handler(pf_to_mgmt, recv_msg); return; } mgmt_work = kzalloc(sizeof(*mgmt_work), GFP_KERNEL); if (!mgmt_work) return; if (recv_msg->msg_len) { mgmt_work->msg = kzalloc(recv_msg->msg_len, GFP_KERNEL); if (!mgmt_work->msg) { kfree(mgmt_work); return; } } mgmt_work->pf_to_mgmt = pf_to_mgmt; mgmt_work->msg_len = recv_msg->msg_len; memcpy(mgmt_work->msg, recv_msg->msg, recv_msg->msg_len); mgmt_work->msg_id = recv_msg->msg_id; mgmt_work->mod = recv_msg->mod; mgmt_work->cmd = recv_msg->cmd; mgmt_work->async_mgmt_to_pf = recv_msg->async_mgmt_to_pf; INIT_WORK(&mgmt_work->work, recv_mgmt_msg_work_handler); queue_work(pf_to_mgmt->workq, &mgmt_work->work); } /** * hinic_mgmt_msg_aeqe_handler - handler for a mgmt message event * @hwdev: the pointer to hw device * @header: the header of the message * @size: unused */ void hinic_mgmt_msg_aeqe_handler(void *hwdev, u8 *header, u8 size) { struct hinic_hwdev *dev = (struct hinic_hwdev *)hwdev; struct hinic_msg_pf_to_mgmt *pf_to_mgmt; struct hinic_recv_msg *recv_msg; bool is_send_dir = false; pf_to_mgmt = dev->pf_to_mgmt; is_send_dir = (HINIC_MSG_HEADER_GET(*(u64 *)header, DIRECTION) == HINIC_MSG_DIRECT_SEND) ? true : false; recv_msg = is_send_dir ? &pf_to_mgmt->recv_msg_from_mgmt : &pf_to_mgmt->recv_resp_msg_from_mgmt; recv_mgmt_msg_handler(pf_to_mgmt, header, recv_msg); } /** * alloc_recv_msg - allocate received message memory * @recv_msg: pointer that will hold the allocated data * Return: 0 - success, negative - failure */ static int alloc_recv_msg(struct hinic_recv_msg *recv_msg) { recv_msg->seq_id = MGMT_MSG_MAX_SEQ_ID; recv_msg->msg = kzalloc(MAX_PF_MGMT_BUF_SIZE, GFP_KERNEL); if (!recv_msg->msg) return -ENOMEM; return 0; } /** * free_recv_msg - free received message memory * @recv_msg: pointer that holds the allocated data */ static void free_recv_msg(struct hinic_recv_msg *recv_msg) { kfree(recv_msg->msg); } /** * alloc_msg_buf - allocate all the message buffers of PF to MGMT channel * @pf_to_mgmt: PF to MGMT channel * Return: 0 - success, negative - failure */ static int alloc_msg_buf(struct hinic_msg_pf_to_mgmt *pf_to_mgmt) { int err; void *dev = pf_to_mgmt->hwdev->dev_hdl; err = alloc_recv_msg(&pf_to_mgmt->recv_msg_from_mgmt); if (err) { sdk_err(dev, "Failed to allocate recv msg\n"); return err; } err = alloc_recv_msg(&pf_to_mgmt->recv_resp_msg_from_mgmt); if (err) { sdk_err(dev, "Failed to allocate resp recv msg\n"); goto alloc_msg_for_resp_err; } pf_to_mgmt->async_msg_buf = kzalloc(MAX_PF_MGMT_BUF_SIZE, GFP_KERNEL); if (!pf_to_mgmt->async_msg_buf) { err = -ENOMEM; goto async_msg_buf_err; } pf_to_mgmt->sync_msg_buf = kzalloc(MAX_PF_MGMT_BUF_SIZE, GFP_KERNEL); if (!pf_to_mgmt->sync_msg_buf) { err = -ENOMEM; goto sync_msg_buf_err; } pf_to_mgmt->mgmt_ack_buf = kzalloc(MAX_PF_MGMT_BUF_SIZE, GFP_KERNEL); if (!pf_to_mgmt->mgmt_ack_buf) { err = -ENOMEM; goto ack_msg_buf_err; } return 0; ack_msg_buf_err: kfree(pf_to_mgmt->sync_msg_buf); sync_msg_buf_err: kfree(pf_to_mgmt->async_msg_buf); async_msg_buf_err: free_recv_msg(&pf_to_mgmt->recv_resp_msg_from_mgmt); alloc_msg_for_resp_err: free_recv_msg(&pf_to_mgmt->recv_msg_from_mgmt); return err; } /** * free_msg_buf - free all the message buffers of PF to MGMT channel * @pf_to_mgmt: PF to MGMT channel */ static void free_msg_buf(struct hinic_msg_pf_to_mgmt *pf_to_mgmt) { kfree(pf_to_mgmt->mgmt_ack_buf); kfree(pf_to_mgmt->sync_msg_buf); kfree(pf_to_mgmt->async_msg_buf); free_recv_msg(&pf_to_mgmt->recv_resp_msg_from_mgmt); free_recv_msg(&pf_to_mgmt->recv_msg_from_mgmt); } /** * hinic_pf_to_mgmt_init - initialize PF to MGMT channel * @hwdev: the pointer to hw device * Return: 0 - success, negative - failure */ int hinic_pf_to_mgmt_init(struct hinic_hwdev *hwdev) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt; void *dev = hwdev->dev_hdl; int err; pf_to_mgmt = kzalloc(sizeof(*pf_to_mgmt), GFP_KERNEL); if (!pf_to_mgmt) return -ENOMEM; hwdev->pf_to_mgmt = pf_to_mgmt; pf_to_mgmt->hwdev = hwdev; spin_lock_init(&pf_to_mgmt->async_msg_lock); spin_lock_init(&pf_to_mgmt->sync_event_lock); sema_init(&pf_to_mgmt->sync_msg_lock, 1); pf_to_mgmt->workq = create_singlethread_workqueue(HINIC_MGMT_WQ_NAME); if (!pf_to_mgmt->workq) { sdk_err(dev, "Failed to initialize MGMT workqueue\n"); err = -ENOMEM; goto create_mgmt_workq_err; } err = alloc_msg_buf(pf_to_mgmt); if (err) { sdk_err(dev, "Failed to allocate msg buffers\n"); goto alloc_msg_buf_err; } err = hinic_api_cmd_init(hwdev, pf_to_mgmt->cmd_chain); if (err) { sdk_err(dev, "Failed to init the api cmd chains\n"); goto api_cmd_init_err; } return 0; api_cmd_init_err: free_msg_buf(pf_to_mgmt); alloc_msg_buf_err: destroy_workqueue(pf_to_mgmt->workq); create_mgmt_workq_err: kfree(pf_to_mgmt); return err; } /** * hinic_pf_to_mgmt_free - free PF to MGMT channel * @hwdev: the pointer to hw device */ void hinic_pf_to_mgmt_free(struct hinic_hwdev *hwdev) { struct hinic_msg_pf_to_mgmt *pf_to_mgmt = hwdev->pf_to_mgmt; /* destroy workqueue before free related pf_to_mgmt resources in case of * illegal resource access */ destroy_workqueue(pf_to_mgmt->workq); hinic_api_cmd_free(pf_to_mgmt->cmd_chain); free_msg_buf(pf_to_mgmt); kfree(pf_to_mgmt); } void hinic_flush_mgmt_workq(void *hwdev) { struct hinic_hwdev *dev = (struct hinic_hwdev *)hwdev; flush_workqueue(dev->aeqs->workq); if (hinic_func_type(dev) != TYPE_VF && hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MGMT_INITED)) flush_workqueue(dev->pf_to_mgmt->workq); } int hinic_clp_pf_to_mgmt_init(struct hinic_hwdev *hwdev) { struct hinic_clp_pf_to_mgmt *clp_pf_to_mgmt; clp_pf_to_mgmt = kzalloc(sizeof(*clp_pf_to_mgmt), GFP_KERNEL); if (!clp_pf_to_mgmt) return -ENOMEM; clp_pf_to_mgmt->clp_msg_buf = kzalloc(HINIC_CLP_INPUT_BUFFER_LEN_HOST, GFP_KERNEL); if (!clp_pf_to_mgmt->clp_msg_buf) { kfree(clp_pf_to_mgmt); return -ENOMEM; } sema_init(&clp_pf_to_mgmt->clp_msg_lock, 1); hwdev->clp_pf_to_mgmt = clp_pf_to_mgmt; return 0; } void hinic_clp_pf_to_mgmt_free(struct hinic_hwdev *hwdev) { struct hinic_clp_pf_to_mgmt *clp_pf_to_mgmt = hwdev->clp_pf_to_mgmt; kfree(clp_pf_to_mgmt->clp_msg_buf); kfree(clp_pf_to_mgmt); } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_mgmt.h000066400000000000000000000153361502027332500214260ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_MGMT_H_ #define HINIC_MGMT_H_ #define HINIC_MSG_HEADER_MSG_LEN_SHIFT 0 #define HINIC_MSG_HEADER_MODULE_SHIFT 11 #define HINIC_MSG_HEADER_SEG_LEN_SHIFT 16 #define HINIC_MSG_HEADER_NO_ACK_SHIFT 22 #define HINIC_MSG_HEADER_ASYNC_MGMT_TO_PF_SHIFT 23 #define HINIC_MSG_HEADER_SEQID_SHIFT 24 #define HINIC_MSG_HEADER_LAST_SHIFT 30 #define HINIC_MSG_HEADER_DIRECTION_SHIFT 31 #define HINIC_MSG_HEADER_CMD_SHIFT 32 #define HINIC_MSG_HEADER_PCI_INTF_IDX_SHIFT 48 #define HINIC_MSG_HEADER_P2P_IDX_SHIFT 50 #define HINIC_MSG_HEADER_MSG_ID_SHIFT 54 #define HINIC_MSG_HEADER_MSG_LEN_MASK 0x7FF #define HINIC_MSG_HEADER_MODULE_MASK 0x1F #define HINIC_MSG_HEADER_SEG_LEN_MASK 0x3F #define HINIC_MSG_HEADER_NO_ACK_MASK 0x1 #define HINIC_MSG_HEADER_ASYNC_MGMT_TO_PF_MASK 0x1 #define HINIC_MSG_HEADER_SEQID_MASK 0x3F #define HINIC_MSG_HEADER_LAST_MASK 0x1 #define HINIC_MSG_HEADER_DIRECTION_MASK 0x1 #define HINIC_MSG_HEADER_CMD_MASK 0xFF #define HINIC_MSG_HEADER_PCI_INTF_IDX_MASK 0x3 #define HINIC_MSG_HEADER_P2P_IDX_MASK 0xF #define HINIC_MSG_HEADER_MSG_ID_MASK 0x3FF #define HINIC_MSG_HEADER_GET(val, member) \ (((val) >> HINIC_MSG_HEADER_##member##_SHIFT) & \ HINIC_MSG_HEADER_##member##_MASK) #define HINIC_MSG_HEADER_SET(val, member) \ ((u64)((val) & HINIC_MSG_HEADER_##member##_MASK) << \ HINIC_MSG_HEADER_##member##_SHIFT) #define HINIC_MGMT_WQ_NAME "hinic_mgmt" enum clp_data_type { HINIC_CLP_REQ_HOST = 0, HINIC_CLP_RSP_HOST = 1 }; enum clp_reg_type { HINIC_CLP_BA_HOST = 0, HINIC_CLP_SIZE_HOST = 1, HINIC_CLP_LEN_HOST = 2, HINIC_CLP_START_REQ_HOST = 3, HINIC_CLP_READY_RSP_HOST = 4 }; #define HINIC_CLP_REG_GAP 0x20 #define HINIC_CLP_INPUT_BUFFER_LEN_HOST 4096UL #define HINIC_CLP_DATA_UNIT_HOST 4UL #define HINIC_BAR01_GLOABAL_CTL_OFFSET 0x4000 #define HINIC_BAR01_CLP_OFFSET 0x5000 #define HINIC_CLP_SRAM_SIZE_REG (HINIC_BAR01_GLOABAL_CTL_OFFSET + 0x220) #define HINIC_CLP_REQ_SRAM_BA_REG (HINIC_BAR01_GLOABAL_CTL_OFFSET + 0x224) #define HINIC_CLP_RSP_SRAM_BA_REG (HINIC_BAR01_GLOABAL_CTL_OFFSET + 0x228) #define HINIC_CLP_REQ_REG (HINIC_BAR01_GLOABAL_CTL_OFFSET + 0x22c) #define HINIC_CLP_RSP_REG (HINIC_BAR01_GLOABAL_CTL_OFFSET + 0x230) #define HINIC_CLP_REG(member) (HINIC_CLP_##member##_REG) #define HINIC_CLP_REQ_DATA (HINIC_BAR01_CLP_OFFSET) #define HINIC_CLP_RSP_DATA (HINIC_BAR01_CLP_OFFSET + 0x1000) #define HINIC_CLP_DATA(member) (HINIC_CLP_##member##_DATA) #define HINIC_CLP_SRAM_SIZE_OFFSET 16 #define HINIC_CLP_SRAM_BASE_OFFSET 0 #define HINIC_CLP_LEN_OFFSET 0 #define HINIC_CLP_START_OFFSET 31 #define HINIC_CLP_READY_OFFSET 31 #define HINIC_CLP_OFFSET(member) (HINIC_CLP_##member##_OFFSET) #define HINIC_CLP_SRAM_SIZE_BIT_LEN 0x7ffUL #define HINIC_CLP_SRAM_BASE_BIT_LEN 0x7ffffffUL #define HINIC_CLP_LEN_BIT_LEN 0x7ffUL #define HINIC_CLP_START_BIT_LEN 0x1UL #define HINIC_CLP_READY_BIT_LEN 0x1UL #define HINIC_CLP_MASK(member) (HINIC_CLP_##member##_BIT_LEN) #define HINIC_CLP_DELAY_CNT_MAX 200UL #define HINIC_CLP_SRAM_SIZE_REG_MAX 0x3ff #define HINIC_CLP_SRAM_BASE_REG_MAX 0x7ffffff #define HINIC_CLP_LEN_REG_MAX 0x3ff #define HINIC_CLP_START_OR_READY_REG_MAX 0x1 enum hinic_msg_direction_type { HINIC_MSG_DIRECT_SEND = 0, HINIC_MSG_RESPONSE = 1 }; enum hinic_msg_segment_type { NOT_LAST_SEGMENT = 0, LAST_SEGMENT = 1, }; enum hinic_mgmt_msg_type { ASYNC_MGMT_MSG = 0, SYNC_MGMT_MSG = 1, }; enum hinic_msg_ack_type { HINIC_MSG_ACK = 0, HINIC_MSG_NO_ACK = 1, }; struct hinic_recv_msg { void *msg; struct completion recv_done; u16 msg_len; enum hinic_mod_type mod; u8 cmd; u8 seq_id; u16 msg_id; int async_mgmt_to_pf; }; #define HINIC_COMM_SELF_CMD_MAX 8 struct comm_up_self_msg_sub_info { u8 cmd; comm_up_self_msg_proc proc; }; struct comm_up_self_msg_info { u8 cmd_num; struct comm_up_self_msg_sub_info info[HINIC_COMM_SELF_CMD_MAX]; }; enum comm_pf_to_mgmt_event_state { SEND_EVENT_UNINIT = 0, SEND_EVENT_START, SEND_EVENT_FAIL, SEND_EVENT_TIMEOUT, SEND_EVENT_END, }; enum hinic_mgmt_msg_cb_state { HINIC_MGMT_MSG_CB_REG = 0, HINIC_MGMT_MSG_CB_RUNNING, }; struct hinic_clp_pf_to_mgmt { struct semaphore clp_msg_lock; void *clp_msg_buf; }; struct hinic_msg_pf_to_mgmt { struct hinic_hwdev *hwdev; /* Async cmd can not be scheduling */ spinlock_t async_msg_lock; struct semaphore sync_msg_lock; struct workqueue_struct *workq; void *async_msg_buf; void *sync_msg_buf; void *mgmt_ack_buf; struct hinic_recv_msg recv_msg_from_mgmt; struct hinic_recv_msg recv_resp_msg_from_mgmt; u16 async_msg_id; u16 sync_msg_id; struct hinic_api_cmd_chain *cmd_chain[HINIC_API_CMD_MAX]; hinic_mgmt_msg_cb recv_mgmt_msg_cb[HINIC_MOD_HW_MAX]; void *recv_mgmt_msg_data[HINIC_MOD_HW_MAX]; unsigned long mgmt_msg_cb_state[HINIC_MOD_HW_MAX]; void (*async_msg_cb[HINIC_MOD_HW_MAX])(void *handle, enum hinic_mgmt_cmd cmd, void *priv_data, u32 msg_id, void *buf_out, u32 out_size); void *async_msg_cb_data[HINIC_MOD_HW_MAX]; struct comm_up_self_msg_info proc; /* lock when sending msg */ spinlock_t sync_event_lock; enum comm_pf_to_mgmt_event_state event_flag; }; struct hinic_mgmt_msg_handle_work { struct work_struct work; struct hinic_msg_pf_to_mgmt *pf_to_mgmt; void *msg; u16 msg_len; enum hinic_mod_type mod; u8 cmd; u16 msg_id; int async_mgmt_to_pf; }; int hinic_pf_to_mgmt_no_ack(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size); void hinic_mgmt_msg_aeqe_handler(void *handle, u8 *header, u8 size); int hinic_pf_to_mgmt_init(struct hinic_hwdev *hwdev); void hinic_pf_to_mgmt_free(struct hinic_hwdev *hwdev); int hinic_pf_to_mgmt_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); int hinic_pf_to_mgmt_async(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size); int hinic_pf_clp_to_mgmt(void *hwdev, enum hinic_mod_type mod, u8 cmd, const void *buf_in, u16 in_size, void *buf_out, u16 *out_size); int hinic_clp_pf_to_mgmt_init(struct hinic_hwdev *hwdev); void hinic_clp_pf_to_mgmt_free(struct hinic_hwdev *hwdev); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_mgmt_interface.h000066400000000000000000000347551502027332500234540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_MGMT_INTERFACE_H #define HINIC_MGMT_INTERFACE_H #include #include #include "hinic_port_cmd.h" /* up to driver event */ #define HINIC_PORT_CMD_MGMT_RESET 0x0 struct hinic_msg_head { u8 status; u8 version; u8 resp_aeq_num; u8 rsvd0[5]; }; struct hinic_register_vf { u8 status; u8 version; u8 rsvd0[6]; }; struct hinic_tx_rate_cfg { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u32 tx_rate; }; struct hinic_tx_rate_cfg_max_min { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u32 min_rate; u32 max_rate; u8 rsvd2[8]; }; struct hinic_port_mac_get { u16 func_id; u8 mac[ETH_ALEN]; int ret; }; struct hinic_function_table { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rx_wqe_buf_size; u32 mtu; }; struct hinic_cmd_qpn { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 base_qpn; }; struct hinic_port_mac_set { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 vlan_id; u16 rsvd1; u8 mac[ETH_ALEN]; }; struct hinic_port_mac_update { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 vlan_id; u16 rsvd1; u8 old_mac[ETH_ALEN]; u16 rsvd2; u8 new_mac[ETH_ALEN]; }; struct hinic_vport_state { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u8 state; u8 rsvd2[3]; }; struct hinic_port_state { u8 status; u8 version; u8 rsvd0[6]; u8 state; u8 rsvd1; u16 func_id; }; struct hinic_spoofchk_set { u8 status; u8 version; u8 rsvd0[6]; u8 state; u8 rsvd1; u16 func_id; }; struct hinic_mtu { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u32 mtu; }; struct hinic_vlan_config { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 vlan_id; }; struct hinic_speed_cmd { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 speed; }; struct hinic_link_mode_cmd { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u16 supported; /* 0xFFFF represent Invalid value */ u16 advertised; }; struct hinic_set_autoneg_cmd { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 enable; /* 1: enable , 0: disable */ }; struct hinic_set_link_status { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 enable; }; struct hinic_get_link { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 link_status; u8 rsvd1; }; struct hinic_link_status_report { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 link_status; u8 port_id; }; struct hinic_port_info { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u8 port_type; u8 autoneg_cap; u8 autoneg_state; u8 duplex; u8 speed; u8 resv2[3]; }; struct hinic_tso_config { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u8 tso_en; u8 resv2[3]; }; struct hinic_lro_config { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u8 lro_ipv4_en; u8 lro_ipv6_en; u8 lro_max_wqe_num; u8 resv2[13]; }; struct hinic_lro_timer { u8 status; u8 version; u8 rsvd0[6]; u8 type; /* 0: set timer value, 1: get timer value */ u8 enable; /* when set lro time, enable should be 1 */ u16 rsvd1; u32 timer; }; struct hinic_checksum_offload { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u32 rx_csum_offload; }; struct hinic_vlan_offload { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 vlan_rx_offload; u8 rsvd1[5]; }; struct hinic_vlan_filter { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 rsvd1[2]; u32 vlan_filter_ctrl; }; struct hinic_pause_config { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u32 auto_neg; u32 rx_pause; u32 tx_pause; }; struct hinic_rx_mode_config { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u32 rx_mode; }; /* rss */ struct nic_rss_indirect_tbl { u32 group_index; u32 offset; u32 size; u32 rsvd; u8 entry[HINIC_RSS_INDIR_SIZE]; }; struct nic_rss_context_tbl { u32 group_index; u32 offset; u32 size; u32 rsvd; u32 ctx; }; struct hinic_rss_config { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 rss_en; u8 template_id; u8 rq_priority_number; u8 rsvd1[3]; u8 prio_tc[HINIC_DCB_UP_MAX]; }; struct hinic_rss_template_mgmt { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 cmd; u8 template_id; u8 rsvd1[4]; }; struct hinic_rss_indir_table { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 template_id; u8 rsvd1; u8 indir[HINIC_RSS_INDIR_SIZE]; }; struct hinic_rss_template_key { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 template_id; u8 rsvd1; u8 key[HINIC_RSS_KEY_SIZE]; }; struct hinic_rss_engine_type { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 template_id; u8 hash_engine; u8 rsvd1[4]; }; struct hinic_rss_context_table { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 template_id; u8 rsvd1; u32 context; }; struct hinic_up_ets_cfg { u8 status; u8 version; u8 rsvd0[6]; u8 port_id; u8 rsvd1[3]; u8 up_tc[HINIC_DCB_UP_MAX]; u8 pg_bw[HINIC_DCB_PG_MAX]; u8 pgid[HINIC_DCB_UP_MAX]; u8 up_bw[HINIC_DCB_UP_MAX]; u8 prio[HINIC_DCB_PG_MAX]; }; struct hinic_set_pfc { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 pfc_en; u8 pfc_bitmap; u8 rsvd1[4]; }; struct hinic_set_micro_pfc { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 micro_pfc_en; u8 rsvd1; u8 cfg_rq_max; u8 cfg_rq_depth; u16 rq_sm_thd; }; struct hinic_cos_up_map { u8 status; u8 version; u8 rsvd0[6]; u8 port_id; /* every bit indicate index of map is valid or not */ u8 cos_valid_mask; u16 rsvd1; /* user priority in cos(index:cos, value: up) */ u8 map[HINIC_DCB_COS_MAX]; }; struct hinic_set_rq_iq_mapping { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u8 map[64]; u32 num_rqs; u32 rq_depth; }; #define HINIC_PFC_SET_FUNC_THD 0 #define HINIC_PFC_SET_GLB_THD 1 struct hinic_pfc_thd { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 op_type; u16 func_thd; u16 glb_thd; }; /* set iq enable to ucode */ struct hinic_cmd_enable_iq { u8 rq_depth; u8 num_rq; u16 glb_rq_id; u16 q_id; u16 lower_thd; u16 force_en; /* 1: force unlock, 0: depend on condition */ u16 prod_idx; }; /* set iq enable to mgmt cpu */ struct hinic_cmd_enable_iq_mgmt { u8 status; u8 version; u8 rsvd0[6]; u8 rq_depth; u8 num_rq; u16 glb_rq_id; u16 q_id; u16 lower_thd; u16 force_en; /* 1: force unlock, 0: depend on condition */ u16 prod_idx; }; struct hinic_port_link_status { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 link; u8 port_id; }; struct hinic_cable_plug_event { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 plugged; /* 0: unplugged, 1: plugged */ u8 port_id; }; struct hinic_link_err_event { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 err_type; u8 port_id; }; struct hinic_sync_time_info { u8 status; u8 version; u8 rsvd0[6]; u64 mstime; }; #define HINIC_PORT_STATS_VERSION 0 struct hinic_port_stats_info { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u32 stats_version; u32 stats_size; }; struct hinic_port_stats { u8 status; u8 version; u8 rsvd[6]; struct hinic_phy_port_stats stats; }; struct hinic_cmd_vport_stats { u8 status; u8 version; u8 rsvd0[6]; struct hinic_vport_stats stats; }; struct hinic_vf_vlan_config { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 vlan_id; u8 qos; u8 rsvd1[7]; }; struct hinic_port_ipsu_mac { u8 status; u8 version; u8 rsvd0[6]; u16 index; u16 func_id; u16 vlan_id; u8 mac[ETH_ALEN]; }; /* get or set loopback mode, need to modify by base API */ #define HINIC_INTERNAL_LP_MODE 5 #define LOOP_MODE_MIN 1 #define LOOP_MODE_MAX 6 struct hinic_port_loopback { u8 status; u8 version; u8 rsvd[6]; u32 mode; u32 en; }; #define HINIC_COMPILE_TIME_LEN 20 struct hinic_version_info { u8 status; u8 version; u8 rsvd[6]; u8 ver[HINIC_FW_VERSION_NAME]; u8 time[HINIC_COMPILE_TIME_LEN]; }; #define ANTI_ATTACK_DEFAULT_CIR 500000 #define ANTI_ATTACK_DEFAULT_XIR 600000 #define ANTI_ATTACK_DEFAULT_CBS 10000000 #define ANTI_ATTACK_DEFAULT_XBS 12000000 /* set physical port Anti-Attack rate */ struct hinic_port_anti_attack_rate { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 enable; /* 1: enable rate-limiting, 0: disable rate-limiting */ u32 cir; /* Committed Information Rate */ u32 xir; /* eXtended Information Rate */ u32 cbs; /* Committed Burst Size */ u32 xbs; /* eXtended Burst Size */ }; struct hinic_clear_sq_resource { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; }; struct hinic_l2nic_reset { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 reset_flag; }; struct hinic_super_cqe { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 super_cqe_en; }; struct hinic_capture_info { u8 status; u8 version; u8 rsvd[6]; u32 op_type; u32 func_id; u32 is_en_trx; u32 offset_cos; u32 data_vlan; }; struct hinic_port_rt_cmd { u8 status; u8 version; u8 rsvd0[6]; u8 pf_id; u8 enable; u8 rsvd1[6]; }; struct fw_support_func { u8 status; u8 version; u8 rsvd0[6]; u64 flag; u64 rsvd; }; struct hinic_vf_dcb_state { u8 status; u8 version; u8 rsvd0[6]; struct hinic_dcb_state state; }; struct hinic_port_funcs_state { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; /* pf_id */ u8 drop_en; u8 rsvd1; }; struct hinic_reset_link_cfg { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; }; struct hinic_force_pkt_drop { u8 status; u8 version; u8 rsvd0[6]; u8 port; u8 rsvd1[3]; }; struct hinic_set_link_follow { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u8 follow_status; u8 rsvd2[3]; }; int hinic_init_function_table(void *hwdev, u16 rx_buf_sz); int hinic_get_base_qpn(void *hwdev, u16 *global_qpn); int hinic_get_fw_support_func(void *hwdev); int hinic_vf_func_init(struct hinic_hwdev *hwdev); void hinic_vf_func_free(struct hinic_hwdev *hwdev); void hinic_unregister_vf_msg_handler(void *hwdev, u16 vf_id); int hinic_set_port_routine_cmd_report(void *hwdev, bool enable); int hinic_refresh_nic_cfg(void *hwdev, struct nic_port_info *port_info); int hinic_save_dcb_state(struct hinic_hwdev *hwdev, struct hinic_dcb_state *dcb_state); void hinic_clear_vf_infos(void *hwdev, u16 vf_id); /* OVS module interface, for BMGW cpath command */ enum hinic_hiovs_cmd { OVS_SET_CPATH_VLAN = 39, OVS_GET_CPATH_VLAN = 40, OVS_DEL_CPATH_VLAN = 43, }; struct cmd_cpath_vlan { u8 status; u8 version; u8 rsvd0[6]; u16 vlan_id; u16 pf_id; }; /* HILINK module interface */ /* cmd of mgmt CPU message for HILINK module */ enum hinic_hilink_cmd { HINIC_HILINK_CMD_GET_LINK_INFO = 0x3, HINIC_HILINK_CMD_SET_LINK_SETTINGS = 0x8, }; enum hilink_info_print_event { HILINK_EVENT_LINK_UP = 1, HILINK_EVENT_LINK_DOWN, HILINK_EVENT_CABLE_PLUGGED, HILINK_EVENT_MAX_TYPE, }; enum hinic_link_port_type { LINK_PORT_FIBRE = 1, LINK_PORT_ELECTRIC, LINK_PORT_COPPER, LINK_PORT_AOC, LINK_PORT_BACKPLANE, LINK_PORT_BASET, LINK_PORT_MAX_TYPE, }; enum hilink_fibre_subtype { FIBRE_SUBTYPE_SR = 1, FIBRE_SUBTYPE_LR, FIBRE_SUBTYPE_MAX, }; enum hilink_fec_type { HILINK_FEC_RSFEC, HILINK_FEC_BASEFEC, HILINK_FEC_NOFEC, HILINK_FEC_MAX_TYPE, }; struct hi30_ffe_data { u8 PRE2; u8 PRE1; u8 POST1; u8 POST2; u8 MAIN; }; struct hi30_ctle_data { u8 ctlebst[3]; u8 ctlecmband[3]; u8 ctlermband[3]; u8 ctleza[3]; u8 ctlesqh[3]; u8 ctleactgn[3]; u8 ctlepassgn; }; struct hi30_dfe_data { u8 fix_tap1_cen; u8 fix_tap1_edg; u8 dfefxtap[6]; u8 dfefloattap[6]; }; struct hilink_sfp_power { u32 rx_power; u32 tx_power; u32 rsvd; u32 is_invalid; }; #define HILINK_MAX_LANE 4 struct hilink_lane { u8 lane_used; u8 hi30_ffe[5]; u8 hi30_ctle[19]; u8 hi30_dfe[14]; u8 rsvd4; }; struct hinic_link_info { u8 vendor_name[16]; /* port type: * 1 - fiber; 2 - electric; 3 - copper; 4 - AOC; 5 - backplane; * 6 - baseT; 0xffff - unknown * * port subtype: * Only when port_type is fiber: * 1 - SR; 2 - LR */ u32 port_type; u32 port_sub_type; u32 cable_length; u8 cable_temp; u8 cable_max_speed;/* 1(G)/10(G)/25(G)... */ u8 sfp_type; /* 0 - qsfp; 1 - sfp */ u8 rsvd0; u32 power[4]; /* uW; if is sfp, only power[2] is valid */ u8 an_state; /* 0 - off; 1 - on */ u8 fec; /* 0 - RSFEC; 1 - BASEFEC; 2 - NOFEC */ u16 speed; /* 1(G)/10(G)/25(G)... */ u8 cable_absent; /* 0 - cable present; 1 - cable unpresent */ u8 alos; /* 0 - yes; 1 - no */ u8 rx_los; /* 0 - yes; 1 - no */ u8 pma_status; u32 pma_dbg_info_reg; /* pma debug info: */ u32 pma_signal_ok_reg; /* signal ok: */ u32 pcs_err_blk_cnt_reg; /* error block counter: */ u32 rf_lf_status_reg; /* RF/LF status: */ u8 pcs_link_reg; /* pcs link: */ u8 mac_link_reg; /* mac link: */ u8 mac_tx_en; u8 mac_rx_en; u32 pcs_err_cnt; /* struct hinic_hilink_lane: 40 bytes */ u8 lane1[40]; /* 25GE lane in old firmware */ u8 rsvd1[266]; /* hilink machine state */ u8 lane2[HILINK_MAX_LANE * 40]; /* max 4 lane for 40GE/100GE */ u8 rsvd2[2]; }; struct hinic_hilink_link_info { u8 status; u8 version; u8 rsvd0[6]; u16 port_id; u8 info_type; /* 1: link up 2: link down 3 cable plugged */ u8 rsvd1; struct hinic_link_info info; u8 rsvd2[352]; }; struct hinic_link_ksettings_info { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 rsvd1; u32 valid_bitmap; u32 speed; /* enum nic_speed_level */ u8 autoneg; /* 0 - off; 1 - on */ u8 fec; /* 0 - RSFEC; 1 - BASEFEC; 2 - NOFEC */ u8 rsvd2[18]; /* reserved for duplex, port, etc. */ }; enum hinic_tx_promsic { HINIC_TX_PROMISC_ENABLE = 0, HINIC_TX_PROMISC_DISABLE = 1, }; struct hinic_promsic_info { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 cfg; u8 rsvd1; }; struct hinic_netq_cfg_msg { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u8 netq_en; u8 rsvd; }; /* add/del rxq filter msg */ struct hinic_rq_filter_msg { u8 status; u8 version; u8 rsvd0[6]; u16 func_id; u16 qid; u8 filter_type; u8 qflag;/*0:stdq, 1:defq, 2: netq*/ u8 mac[6]; struct { u8 inner_mac[6]; u32 vni; } vxlan; }; int hinic_get_hilink_link_info(void *hwdev, struct hinic_link_info *info); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_msix_attr.c000066400000000000000000000110751502027332500224630ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwif.h" #include "hinic_csr.h" #include "hinic_msix_attr.h" #define VALID_MSIX_IDX(attr, msix_index) ((msix_index) < (attr)->num_irqs) /** * hinic_msix_attr_set - set message attribute of msix entry * @hwif: the hardware interface of a pci function device * @msix_index: msix_index * @pending_limit: the maximum pending interrupt events (unit 8) * @coalesc_timer: coalesc period for interrupt (unit 8 us) * @lli_timer_cfg: replenishing period for low latency credit (unit 8 us) * @lli_credit_limit: maximum credits for low latency msix messages (unit 8) * @resend_timer: maximum wait for resending msix message * (unit coalesc period) * Return: 0 - success, negative - failure */ int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index, u8 pending_limit, u8 coalesc_timer, u8 lli_timer_cfg, u8 lli_credit_limit, u8 resend_timer) { u32 msix_ctrl, addr; if (!VALID_MSIX_IDX(&hwif->attr, msix_index)) return -EINVAL; msix_ctrl = HINIC_MSIX_ATTR_SET(pending_limit, PENDING_LIMIT) | HINIC_MSIX_ATTR_SET(coalesc_timer, COALESC_TIMER) | HINIC_MSIX_ATTR_SET(lli_timer_cfg, LLI_TIMER) | HINIC_MSIX_ATTR_SET(lli_credit_limit, LLI_CREDIT) | HINIC_MSIX_ATTR_SET(resend_timer, RESEND_TIMER); addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index); hinic_hwif_write_reg(hwif, addr, msix_ctrl); return 0; } /** * hinic_msix_attr_get - get message attribute of msix entry * @hwif: the hardware interface of a pci function device * @msix_index: msix_index * @pending_limit: the maximum pending interrupt events (unit 8) * @coalesc_timer_cfg: coalesc period for interrupt (unit 8 us) * @lli_timer_cfg: replenishing period for low latency credit (unit 8 us) * @lli_credit_limit: maximum credits for low latency msix messages (unit 8) * @resend_timer_cfg: maximum wait for resending msix message * (unit coalesc period) * Return: 0 - success, negative - failure */ int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index, u8 *pending_limit, u8 *coalesc_timer_cfg, u8 *lli_timer_cfg, u8 *lli_credit_limit, u8 *resend_timer_cfg) { u32 addr, val; if (!VALID_MSIX_IDX(&hwif->attr, msix_index)) return -EINVAL; addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index); val = hinic_hwif_read_reg(hwif, addr); *pending_limit = HINIC_MSIX_ATTR_GET(val, PENDING_LIMIT); *coalesc_timer_cfg = HINIC_MSIX_ATTR_GET(val, COALESC_TIMER); *lli_timer_cfg = HINIC_MSIX_ATTR_GET(val, LLI_TIMER); *lli_credit_limit = HINIC_MSIX_ATTR_GET(val, LLI_CREDIT); *resend_timer_cfg = HINIC_MSIX_ATTR_GET(val, RESEND_TIMER); return 0; } /** * hinic_msix_attr_cnt_set - set message attribute counters of msix entry * @hwif: the hardware interface of a pci function device * @msix_index: msix_index * @lli_timer_cnt: replenishing period for low latency interrupt (unit 8 us) * @lli_credit_cnt: maximum credits for low latency msix messages (unit 8) * @coalesc_timer_cnt: coalesc period for interrupt (unit 8 us) * @pending_cnt: the maximum pending interrupt events (unit 8) * @resend_timer_cnt: maximum wait for resending msix message * (unit coalesc period) * Return: 0 - success, negative - failure */ int hinic_msix_attr_cnt_set(struct hinic_hwif *hwif, u16 msix_index, u8 lli_timer_cnt, u8 lli_credit_cnt, u8 coalesc_timer_cnt, u8 pending_cnt, u8 resend_timer_cnt) { u32 msix_ctrl, addr; if (!VALID_MSIX_IDX(&hwif->attr, msix_index)) return -EINVAL; msix_ctrl = HINIC_MSIX_CNT_SET(lli_timer_cnt, LLI_TIMER) | HINIC_MSIX_CNT_SET(lli_credit_cnt, LLI_CREDIT) | HINIC_MSIX_CNT_SET(coalesc_timer_cnt, COALESC_TIMER) | HINIC_MSIX_CNT_SET(pending_cnt, PENDING) | HINIC_MSIX_CNT_SET(resend_timer_cnt, RESEND_TIMER); addr = HINIC_CSR_MSIX_CNT_ADDR(msix_index); hinic_hwif_write_reg(hwif, addr, msix_ctrl); return 0; } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_msix_attr.h000066400000000000000000000046131502027332500224700ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_MSIX_ATTR_H_ #define HINIC_MSIX_ATTR_H_ #define HINIC_MSIX_PENDING_LIMIT_SHIFT 0 #define HINIC_MSIX_COALESC_TIMER_SHIFT 8 #define HINIC_MSIX_LLI_TIMER_SHIFT 16 #define HINIC_MSIX_LLI_CREDIT_SHIFT 24 #define HINIC_MSIX_RESEND_TIMER_SHIFT 29 #define HINIC_MSIX_PENDING_LIMIT_MASK 0xFFU #define HINIC_MSIX_COALESC_TIMER_MASK 0xFFU #define HINIC_MSIX_LLI_TIMER_MASK 0xFFU #define HINIC_MSIX_LLI_CREDIT_MASK 0x1FU #define HINIC_MSIX_RESEND_TIMER_MASK 0x7U #define HINIC_MSIX_ATTR_GET(val, member) \ (((val) >> HINIC_MSIX_##member##_SHIFT) \ & HINIC_MSIX_##member##_MASK) #define HINIC_MSIX_ATTR_SET(val, member) \ (((val) & HINIC_MSIX_##member##_MASK) \ << HINIC_MSIX_##member##_SHIFT) #define HINIC_MSIX_CNT_LLI_TIMER_SHIFT 0 #define HINIC_MSIX_CNT_LLI_CREDIT_SHIFT 8 #define HINIC_MSIX_CNT_COALESC_TIMER_SHIFT 8 #define HINIC_MSIX_CNT_PENDING_SHIFT 8 #define HINIC_MSIX_CNT_RESEND_TIMER_SHIFT 29 #define HINIC_MSIX_CNT_LLI_TIMER_MASK 0xFFU #define HINIC_MSIX_CNT_LLI_CREDIT_MASK 0xFFU #define HINIC_MSIX_CNT_COALESC_TIMER_MASK 0xFFU #define HINIC_MSIX_CNT_PENDING_MASK 0x1FU #define HINIC_MSIX_CNT_RESEND_TIMER_MASK 0x7U #define HINIC_MSIX_CNT_SET(val, member) \ (((val) & HINIC_MSIX_CNT_##member##_MASK) << \ HINIC_MSIX_CNT_##member##_SHIFT) int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index, u8 pending_limit, u8 coalesc_timer, u8 lli_timer_cfg, u8 lli_credit_limit, u8 resend_timer); int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index, u8 *pending_limit, u8 *coalesc_timer_cfg, u8 *lli_timer_cfg, u8 *lli_credit_limit, u8 *resend_timer_cfg); int hinic_msix_attr_cnt_set(struct hinic_hwif *hwif, u16 msix_index, u8 pending_limit, u8 coalesc_timer, u8 lli_timer_cfg, u8 lli_credit_limit, u8 resend_timer); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_multi_host_mgmt.c000066400000000000000000000632461502027332500236730ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwdev.h" #include "hinic_csr.h" #include "hinic_hwif.h" #include "hinic_nic_io.h" #include "hinic_api_cmd.h" #include "hinic_mgmt.h" #include "hinic_mbox.h" #include "hinic_nic_cfg.h" #include "hinic_hwif.h" #include "hinic_mgmt_interface.h" #include "hinic_multi_host_mgmt.h" #define SLAVE_HOST_STATUS_CLEAR(host_id, val) \ ((val) & (~(1U << (host_id)))) #define SLAVE_HOST_STATUS_SET(host_id, enable) \ (((u8)(enable) & 1U) << (host_id)) #define SLAVE_HOST_STATUS_GET(host_id, val) (!!((val) & (1U << (host_id)))) #define MULTI_HOST_PPF_GET(host_id, val) (((val) >> ((host_id) * 4 + 16)) & 0xf) static inline u8 get_master_host_ppf_idx(struct hinic_hwdev *hwdev) { u32 reg_val; reg_val = hinic_hwif_read_reg(hwdev->hwif, HINIC_MULT_HOST_SLAVE_STATUS_ADDR); /* master host sets host_id to 0 */ return MULTI_HOST_PPF_GET(0, reg_val); } void set_slave_host_enable(struct hinic_hwdev *hwdev, u8 host_id, bool enable) { u32 reg_val; if (HINIC_FUNC_TYPE(hwdev) != TYPE_PPF) return; reg_val = hinic_hwif_read_reg(hwdev->hwif, HINIC_MULT_HOST_SLAVE_STATUS_ADDR); reg_val = SLAVE_HOST_STATUS_CLEAR(host_id, reg_val); reg_val |= SLAVE_HOST_STATUS_SET(host_id, enable); hinic_hwif_write_reg(hwdev->hwif, HINIC_MULT_HOST_SLAVE_STATUS_ADDR, reg_val); sdk_info(hwdev->dev_hdl, "Set slave host %d status %d, reg value: 0x%x\n", host_id, enable, reg_val); } bool hinic_get_slave_host_enable(void *hwdev, u8 host_id) { u32 reg_val; struct hinic_hwdev *dev = hwdev; if (HINIC_FUNC_TYPE(dev) != TYPE_PPF) return false; reg_val = hinic_hwif_read_reg(dev->hwif, HINIC_MULT_HOST_SLAVE_STATUS_ADDR); return SLAVE_HOST_STATUS_GET(host_id, reg_val); } EXPORT_SYMBOL(hinic_get_slave_host_enable); void set_master_host_mbox_enable(struct hinic_hwdev *hwdev, bool enable) { u32 reg_val; if (!IS_MASTER_HOST(hwdev) || HINIC_FUNC_TYPE(hwdev) != TYPE_PPF) return; reg_val = hinic_hwif_read_reg(hwdev->hwif, HINIC_HOST_MODE_ADDR); reg_val = MULTI_HOST_REG_CLEAR(reg_val, MASTER_MBX_STS); reg_val |= MULTI_HOST_REG_SET((u8)enable, MASTER_MBX_STS); hinic_hwif_write_reg(hwdev->hwif, HINIC_HOST_MODE_ADDR, reg_val); sdk_info(hwdev->dev_hdl, "multi-host status %d, reg value: 0x%x\n", enable, reg_val); } bool hinic_get_master_host_mbox_enable(void *hwdev) { u32 reg_val; struct hinic_hwdev *dev = hwdev; if (!hwdev) return false; if (!IS_SLAVE_HOST(dev) || HINIC_FUNC_TYPE(dev) == TYPE_VF) return true; reg_val = hinic_hwif_read_reg(dev->hwif, HINIC_HOST_MODE_ADDR); return !!MULTI_HOST_REG_GET(reg_val, MASTER_MBX_STS); } void set_func_host_mode(struct hinic_hwdev *hwdev, enum hinic_func_mode mode) { switch (mode) { case FUNC_MOD_MULTI_BM_MASTER: sdk_info(hwdev->dev_hdl, "Detect multi-host BM master host\n"); hwdev->func_mode = FUNC_MOD_MULTI_BM_MASTER; hwdev->feature_cap = HINIC_MULTI_BM_MASTER; break; case FUNC_MOD_MULTI_BM_SLAVE: sdk_info(hwdev->dev_hdl, "Detect multi-host BM slave host\n"); hwdev->func_mode = FUNC_MOD_MULTI_BM_SLAVE; hwdev->feature_cap = HINIC_MULTI_BM_SLAVE; break; case FUNC_MOD_MULTI_VM_MASTER: sdk_info(hwdev->dev_hdl, "Detect multi-host VM master host\n"); hwdev->func_mode = FUNC_MOD_MULTI_VM_MASTER; hwdev->feature_cap = HINIC_MULTI_VM_MASTER; break; case FUNC_MOD_MULTI_VM_SLAVE: sdk_info(hwdev->dev_hdl, "Detect multi-host VM slave host\n"); hwdev->func_mode = FUNC_MOD_MULTI_VM_SLAVE; hwdev->feature_cap = HINIC_MULTI_VM_SLAVE; break; default: hwdev->func_mode = FUNC_MOD_NORMAL_HOST; hwdev->feature_cap = HINIC_NORMAL_HOST_CAP; break; } } bool is_multi_vm_slave(void *hwdev) { struct hinic_hwdev *hw_dev = hwdev; if (!hwdev) return false; return (hw_dev->func_mode == FUNC_MOD_MULTI_VM_SLAVE) ? true : false; } bool is_multi_bm_slave(void *hwdev) { struct hinic_hwdev *hw_dev = hwdev; if (!hwdev) return false; return (hw_dev->func_mode == FUNC_MOD_MULTI_BM_SLAVE) ? true : false; } int rectify_host_mode(struct hinic_hwdev *hwdev) { u16 cur_sdi_mode; int err; if (hwdev->board_info.board_type != HINIC_BOARD_TYPE_MULTI_HOST_ETH_25GE) return 0; sdk_info(hwdev->dev_hdl, "Rectify host mode, host_id: %d\n", hinic_pcie_itf_id(hwdev)); err = hinic_get_sdi_mode(hwdev, &cur_sdi_mode); if (err == HINIC_MGMT_CMD_UNSUPPORTED) cur_sdi_mode = HINIC_SDI_MODE_BM; else if (err) return err; switch (cur_sdi_mode) { case HINIC_SDI_MODE_BM: if (hinic_pcie_itf_id(hwdev) == 0) set_func_host_mode(hwdev, FUNC_MOD_MULTI_BM_MASTER); else set_func_host_mode(hwdev, FUNC_MOD_MULTI_BM_SLAVE); break; case HINIC_SDI_MODE_VM: if (hinic_pcie_itf_id(hwdev) == 0) set_func_host_mode(hwdev, FUNC_MOD_MULTI_VM_MASTER); else set_func_host_mode(hwdev, FUNC_MOD_MULTI_VM_SLAVE); break; default: sdk_warn(hwdev->dev_hdl, "Unknown sdi mode %d\n", cur_sdi_mode); break; } return 0; } void detect_host_mode_pre(struct hinic_hwdev *hwdev) { enum hinic_chip_mode chip_mode; /* all pf can set HOST_MODE REG, so don't trust HOST_MODE REG for host0, * get chip mode from mgmt cpu for host0 * VF have not right to read HOST_MODE REG, detect mode from board info */ if (hinic_pcie_itf_id(hwdev) == 0 || HINIC_FUNC_TYPE(hwdev) == TYPE_VF) { set_func_host_mode(hwdev, FUNC_MOD_NORMAL_HOST); return; } chip_mode = hinic_hwif_read_reg(hwdev->hwif, HINIC_HOST_MODE_ADDR); switch (MULTI_HOST_REG_GET(chip_mode, CHIP_MODE)) { case CHIP_MODE_VMGW: set_func_host_mode(hwdev, FUNC_MOD_MULTI_VM_SLAVE); /* mbox has not initialized, set slave host disable */ set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false); break; case CHIP_MODE_BMGW: set_func_host_mode(hwdev, FUNC_MOD_MULTI_BM_SLAVE); /* mbox has not initialized, set slave host disable */ set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false); break; default: set_func_host_mode(hwdev, FUNC_MOD_NORMAL_HOST); break; } } int __mbox_to_host(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout, enum hinic_mbox_ack_type ack_type) { struct hinic_hwdev *mbox_hwdev = hwdev; u8 dst_host_func_idx; int err; if (!IS_MULTI_HOST(hwdev) || HINIC_IS_VF(hwdev)) return -EPERM; if (hinic_func_type(hwdev) == TYPE_PF) { down(&hwdev->ppf_sem); mbox_hwdev = hwdev->ppf_hwdev; if (!mbox_hwdev) { err = -EINVAL; goto release_lock; } if (!hinic_is_hwdev_mod_inited(mbox_hwdev, HINIC_HWDEV_MBOX_INITED)) { err = -EPERM; goto release_lock; } } if (!mbox_hwdev->chip_present_flag) { err = -EPERM; goto release_lock; } if (!hinic_get_master_host_mbox_enable(hwdev)) { sdk_err(hwdev->dev_hdl, "Master host not initialized\n"); err = -EFAULT; goto release_lock; } if (!mbox_hwdev->mhost_mgmt) { /* send to master host in default */ dst_host_func_idx = get_master_host_ppf_idx(hwdev); } else { dst_host_func_idx = IS_MASTER_HOST(hwdev) ? mbox_hwdev->mhost_mgmt->shost_ppf_idx : mbox_hwdev->mhost_mgmt->mhost_ppf_idx; } if (ack_type == MBOX_ACK) err = hinic_mbox_to_host(mbox_hwdev, dst_host_func_idx, mod, cmd, buf_in, in_size, buf_out, out_size, timeout); else err = hinic_mbox_to_func_no_ack(mbox_hwdev, dst_host_func_idx, mod, cmd, buf_in, in_size); release_lock: if (hinic_func_type(hwdev) == TYPE_PF) up(&hwdev->ppf_sem); return err; } int hinic_mbox_to_host_sync(void *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { if (!hwdev) return -EINVAL; return __mbox_to_host((struct hinic_hwdev *)hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, timeout, MBOX_ACK); } EXPORT_SYMBOL(hinic_mbox_to_host_sync); int hinic_mbox_to_host_no_ack(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size) { return __mbox_to_host(hwdev, mod, cmd, buf_in, in_size, NULL, NULL, 0, MBOX_NO_ACK); } static int __get_func_nic_state_from_pf(struct hinic_hwdev *hwdev, u16 glb_func_idx, u8 *en); int sw_func_pf_mbox_handler(void *handle, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *hwdev = handle; struct hinic_slave_func_nic_state *nic_state, *out_state; int err; switch (cmd) { case HINIC_SW_CMD_GET_SLAVE_FUNC_NIC_STATE: nic_state = buf_in; out_state = buf_out; *out_size = sizeof(*nic_state); /* find nic state in ppf func_nic_en bitmap */ err = __get_func_nic_state_from_pf(hwdev, nic_state->func_idx, &out_state->enable); if (err) out_state->status = 1; else out_state->status = 0; break; default: break; } return 0; } static int __master_host_sw_func_handler(struct hinic_hwdev *hwdev, u16 pf_idx, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_multi_host_mgmt *mhost_mgmt = hwdev->mhost_mgmt; struct register_slave_host *slave_host, *out_shost; int err = 0; if (!mhost_mgmt) return -ENXIO; switch (cmd) { case HINIC_SW_CMD_SLAVE_HOST_PPF_REGISTER: slave_host = buf_in; out_shost = buf_out; *out_size = sizeof(*slave_host); mhost_mgmt->shost_registered = true; mhost_mgmt->shost_host_idx = slave_host->host_id; mhost_mgmt->shost_ppf_idx = slave_host->ppf_idx; bitmap_copy((ulong *)out_shost->funcs_nic_en, mhost_mgmt->func_nic_en, HINIC_MAX_FUNCTIONS); sdk_info(hwdev->dev_hdl, "slave host register ppf, host_id: %d, ppf_idx: %d\n", slave_host->host_id, slave_host->ppf_idx); out_shost->status = 0; break; case HINIC_SW_CMD_SLAVE_HOST_PPF_UNREGISTER: slave_host = buf_in; mhost_mgmt->shost_registered = false; sdk_info(hwdev->dev_hdl, "slave host unregister ppf, host_id: %d, ppf_idx: %d\n", slave_host->host_id, slave_host->ppf_idx); *out_size = sizeof(*slave_host); ((struct register_slave_host *)buf_out)->status = 0; break; default: err = -EINVAL; break; } return err; } static int __event_set_func_nic_state(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_event_info event_info = {0}; struct hinic_mhost_nic_func_state nic_state = {0}; struct hinic_slave_func_nic_state *out_state, *func_nic_state = buf_in; event_info.type = HINIC_EVENT_MULTI_HOST_MGMT; event_info.mhost_mgmt.sub_cmd = HINIC_MHOST_NIC_STATE_CHANGE; event_info.mhost_mgmt.data = &nic_state; nic_state.func_idx = func_nic_state->func_idx; nic_state.enable = func_nic_state->enable; if (!hwdev->event_callback) return -EFAULT; hwdev->event_callback(hwdev->event_pri_handle, &event_info); *out_size = sizeof(*out_state); out_state = buf_out; out_state->status = nic_state.status; return nic_state.status; } static int multi_host_event_handler(struct hinic_hwdev *hwdev, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { int err; switch (cmd) { case HINIC_SW_CMD_SET_SLAVE_FUNC_NIC_STATE: err = __event_set_func_nic_state(hwdev, buf_in, in_size, buf_out, out_size); break; default: err = -EOPNOTSUPP; break; } return err; } static int sw_fwd_msg_to_vf(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_host_fwd_head *fwd_head; u16 fwd_head_len; void *msg; int err; fwd_head = buf_in; fwd_head_len = sizeof(struct hinic_host_fwd_head); msg = (void *)((u8 *)buf_in + fwd_head_len); err = hinic_mbox_ppf_to_vf(hwdev, fwd_head->mod, fwd_head->dst_glb_func_idx, fwd_head->cmd, msg, in_size - fwd_head_len, buf_out, out_size, 0); if (err) nic_err(hwdev->dev_hdl, "Fwd msg to func %u failed, err: %d\n", fwd_head->dst_glb_func_idx, err); return err; } static int __slave_host_sw_func_handler(struct hinic_hwdev *hwdev, u16 pf_idx, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_multi_host_mgmt *mhost_mgmt = hwdev->mhost_mgmt; struct hinic_slave_func_nic_state *nic_state; int err = 0; if (!mhost_mgmt) return -ENXIO; switch (cmd) { case HINIC_SW_CMD_SET_SLAVE_FUNC_NIC_STATE: nic_state = buf_in; *out_size = sizeof(*nic_state); ((struct hinic_slave_func_nic_state *)buf_out)->status = 0; sdk_info(hwdev->dev_hdl, "slave func %d %s nic\n", nic_state->func_idx, nic_state->enable ? "register" : "unregister"); if (nic_state->enable) set_bit(nic_state->func_idx, mhost_mgmt->func_nic_en); else clear_bit(nic_state->func_idx, mhost_mgmt->func_nic_en); multi_host_event_handler(hwdev, cmd, buf_in, in_size, buf_out, out_size); break; case HINIC_SW_CMD_SEND_MSG_TO_VF: err = sw_fwd_msg_to_vf(hwdev, buf_in, in_size, buf_out, out_size); break; case HINIC_SW_CMD_MIGRATE_READY: hinic_migrate_report(hwdev); break; default: err = -EINVAL; break; } return err; } int sw_func_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *hwdev = handle; int err; if (IS_MASTER_HOST(hwdev)) err = __master_host_sw_func_handler(hwdev, pf_idx, cmd, buf_in, in_size, buf_out, out_size); else if (IS_SLAVE_HOST(hwdev)) err = __slave_host_sw_func_handler(hwdev, pf_idx, cmd, buf_in, in_size, buf_out, out_size); else err = -EINVAL; if (err) sdk_err(hwdev->dev_hdl, "PPF process sw funcs cmd %d failed, err: %d\n", cmd, err); return err; } int __ppf_process_mbox_msg(struct hinic_hwdev *hwdev, u16 pf_idx, u16 vf_id, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { int err; if (IS_SLAVE_HOST(hwdev)) { err = hinic_mbox_to_host_sync(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, 0); if (err) sdk_err(hwdev->dev_hdl, "send to mpf failed, err: %d\n", err); } else if (IS_MASTER_HOST(hwdev)) { if (mod == HINIC_MOD_COMM && cmd == HINIC_MGMT_CMD_START_FLR) err = hinic_pf_to_mgmt_no_ack(hwdev, mod, cmd, buf_in, in_size); else err = hinic_pf_msg_to_mgmt_sync(hwdev, mod, cmd, buf_in, in_size, buf_out, out_size, 0U); if (err && err != HINIC_DEV_BUSY_ACTIVE_FW && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) sdk_err(hwdev->dev_hdl, "PF mbox common callback handler err: %d\n", err); } else { /* not support */ err = -EFAULT; } return err; } int hinic_ppf_process_mbox_msg(struct hinic_hwdev *hwdev, u16 pf_idx, u16 vf_id, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { bool same_host = false; int err = -EFAULT; /* modify same_host according to hinic_get_hw_pf_infos */ switch (hwdev->func_mode) { case FUNC_MOD_MULTI_VM_MASTER: case FUNC_MOD_MULTI_BM_MASTER: if (!same_host) err = __ppf_process_mbox_msg(hwdev, pf_idx, vf_id, mod, cmd, buf_in, in_size, buf_out, out_size); else sdk_warn(hwdev->dev_hdl, "Don't support ppf mbox message in BM master\n"); break; case FUNC_MOD_MULTI_VM_SLAVE: case FUNC_MOD_MULTI_BM_SLAVE: same_host = true; if (same_host) err = __ppf_process_mbox_msg(hwdev, pf_idx, vf_id, mod, cmd, buf_in, in_size, buf_out, out_size); else sdk_warn(hwdev->dev_hdl, "Receive control message from BM master, don't support for now\n"); break; default: sdk_warn(hwdev->dev_hdl, "Don't support ppf mbox message\n"); break; } return err; } int comm_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { return hinic_ppf_process_mbox_msg(handle, pf_idx, vf_id, HINIC_MOD_COMM, cmd, buf_in, in_size, buf_out, out_size); } void comm_ppf_to_pf_handler(void *handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *hwdev = handle; sdk_err(hwdev->dev_hdl, "pf receive ppf common mbox msg, don't supported for now\n"); } int hilink_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { return hinic_ppf_process_mbox_msg(handle, pf_idx, vf_id, HINIC_MOD_HILINK, cmd, buf_in, in_size, buf_out, out_size); } int hinic_nic_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { return hinic_ppf_process_mbox_msg(handle, pf_idx, vf_id, HINIC_MOD_L2NIC, cmd, buf_in, in_size, buf_out, out_size); } void hinic_nic_ppf_to_pf_handler(void *handle, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct hinic_hwdev *hwdev = handle; sdk_err(hwdev->dev_hdl, "ppf receive other pf l2nic mbox msg, don't supported for now\n"); } int hinic_register_slave_ppf(struct hinic_hwdev *hwdev, bool registered) { struct register_slave_host host_info = {0}; u16 out_size = sizeof(host_info); u8 cmd; int err; if (!IS_SLAVE_HOST(hwdev)) return -EINVAL; cmd = registered ? HINIC_SW_CMD_SLAVE_HOST_PPF_REGISTER : HINIC_SW_CMD_SLAVE_HOST_PPF_UNREGISTER; host_info.host_id = hinic_pcie_itf_id(hwdev); host_info.ppf_idx = hinic_ppf_idx(hwdev); err = hinic_mbox_to_host_sync(hwdev, HINIC_MOD_SW_FUNC, cmd, &host_info, sizeof(host_info), &host_info, &out_size, 0); if (err || !out_size || host_info.status) { sdk_err(hwdev->dev_hdl, "Failed to %s slave host, err: %d, out_size: 0x%x, status: 0x%x\n", registered ? "register" : "unregister", err, out_size, host_info.status); return -EFAULT; } bitmap_copy(hwdev->mhost_mgmt->func_nic_en, (ulong *)host_info.funcs_nic_en, HINIC_MAX_FUNCTIONS); return 0; } static int get_host_id_by_func_id(struct hinic_hwdev *hwdev, u16 func_idx, u8 *host_id) { struct hinic_hw_pf_infos *pf_infos; u16 vf_id_start, vf_id_end; int i; if (!hwdev || !host_id || !hwdev->mhost_mgmt) return -EINVAL; pf_infos = &hwdev->mhost_mgmt->pf_infos; for (i = 0; i < pf_infos->num_pfs; i++) { if (func_idx == pf_infos->infos[i].glb_func_idx) { *host_id = pf_infos->infos[i].itf_idx; return 0; } vf_id_start = pf_infos->infos[i].glb_pf_vf_offset + 1; vf_id_end = pf_infos->infos[i].glb_pf_vf_offset + pf_infos->infos[i].max_vfs; if (func_idx >= vf_id_start && func_idx <= vf_id_end) { *host_id = pf_infos->infos[i].itf_idx; return 0; } } return -EFAULT; } int set_slave_func_nic_state(struct hinic_hwdev *hwdev, u16 func_idx, u8 en) { struct hinic_slave_func_nic_state nic_state = {0}; u16 out_size = sizeof(nic_state); int err; nic_state.func_idx = func_idx; nic_state.enable = en; err = hinic_mbox_to_host_sync(hwdev, HINIC_MOD_SW_FUNC, HINIC_SW_CMD_SET_SLAVE_FUNC_NIC_STATE, &nic_state, sizeof(nic_state), &nic_state, &out_size, 0); if (err == MBOX_ERRCODE_UNKNOWN_DES_FUNC) { sdk_warn(hwdev->dev_hdl, "Can not notify func %d nic state because slave host not initialized\n", func_idx); } else if (err || !out_size || nic_state.status) { sdk_err(hwdev->dev_hdl, "Failed to set slave host functions nic state, err: %d, out_size: 0x%x, status: 0x%x\n", err, out_size, nic_state.status); return -EFAULT; } return 0; } int hinic_set_func_nic_state(void *hwdev, struct hinic_func_nic_state *state) { struct hinic_hwdev *ppf_hwdev = hwdev; struct hinic_multi_host_mgmt *mhost_mgmt; u8 host_id = 0; bool host_enable; int err; int old_state; if (!hwdev || !state) return -EINVAL; if (hinic_func_type(hwdev) != TYPE_PPF) ppf_hwdev = ((struct hinic_hwdev *)hwdev)->ppf_hwdev; if (!ppf_hwdev || !IS_MASTER_HOST(ppf_hwdev)) return -EINVAL; mhost_mgmt = ppf_hwdev->mhost_mgmt; if (!mhost_mgmt || state->func_idx >= HINIC_MAX_FUNCTIONS) return -EINVAL; old_state = test_bit(state->func_idx, mhost_mgmt->func_nic_en) ? 1 : 0; if (state->state == HINIC_FUNC_NIC_DEL) clear_bit(state->func_idx, mhost_mgmt->func_nic_en); else if (state->state == HINIC_FUNC_NIC_ADD) set_bit(state->func_idx, mhost_mgmt->func_nic_en); else return -EINVAL; err = get_host_id_by_func_id(ppf_hwdev, state->func_idx, &host_id); if (err) { sdk_err(ppf_hwdev->dev_hdl, "Failed to get function %d host id, err: %d\n", state->func_idx, err); old_state ? set_bit(state->func_idx, mhost_mgmt->func_nic_en) : clear_bit(state->func_idx, mhost_mgmt->func_nic_en); return -EFAULT; } host_enable = hinic_get_slave_host_enable(hwdev, host_id); sdk_info(ppf_hwdev->dev_hdl, "Set slave host %d(status: %d) func %d %s nic\n", host_id, host_enable, state->func_idx, state->state ? "enable" : "disable"); if (!host_enable) return 0; /* notify slave host */ err = set_slave_func_nic_state(hwdev, state->func_idx, state->state); if (err) { old_state ? set_bit(state->func_idx, mhost_mgmt->func_nic_en) : clear_bit(state->func_idx, mhost_mgmt->func_nic_en); return err; } return 0; } EXPORT_SYMBOL(hinic_set_func_nic_state); static int __get_func_nic_state_from_pf(struct hinic_hwdev *hwdev, u16 glb_func_idx, u8 *en) { struct hinic_multi_host_mgmt *mhost_mgmt; struct hinic_hwdev *ppf_hwdev = hwdev; if (hinic_func_type(hwdev) != TYPE_PPF) ppf_hwdev = ((struct hinic_hwdev *)hwdev)->ppf_hwdev; if (!ppf_hwdev || !ppf_hwdev->mhost_mgmt) return -EFAULT; mhost_mgmt = ppf_hwdev->mhost_mgmt; *en = !!(test_bit(glb_func_idx, mhost_mgmt->func_nic_en)); sdk_info(ppf_hwdev->dev_hdl, "slave host func %d nic %d\n", glb_func_idx, *en); return 0; } int hinic_get_func_nic_enable(void *hwdev, u16 glb_func_idx, bool *en) { struct hinic_slave_func_nic_state nic_state = {0}; u16 out_size = sizeof(nic_state); u8 nic_en; int err; if (!hwdev || !en) return -EINVAL; /*if card mode is OVS, VFs donot need attach_uld, so return false.*/ if (!IS_SLAVE_HOST((struct hinic_hwdev *)hwdev)) { if (hinic_func_type(hwdev) == TYPE_VF && hinic_support_ovs(hwdev, NULL)) { *en = false; } else { *en = true; } return 0; } if (hinic_func_type(hwdev) == TYPE_VF) { nic_state.func_idx = glb_func_idx; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_SW_FUNC, HINIC_SW_CMD_GET_SLAVE_FUNC_NIC_STATE, &nic_state, sizeof(nic_state), &nic_state, &out_size, 0); if (err || !out_size || nic_state.status) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get func %d nic state, err: %d, out_size: 0x%x, status: 0x%x\n", glb_func_idx, err, out_size, nic_state.status); return -EFAULT; } *en = !!nic_state.enable; return 0; } /* pf in slave host should be probe in CHIP_MODE_VMGW * mode for pxe install */ if (IS_VM_SLAVE_HOST((struct hinic_hwdev *)hwdev)) { *en = true; return 0; } /* pf/ppf get function nic state in sdk diretly */ err = __get_func_nic_state_from_pf(hwdev, glb_func_idx, &nic_en); if (err) return err; *en = !!nic_en; return 0; } int hinic_multi_host_mgmt_init(struct hinic_hwdev *hwdev) { int err; if (!IS_MULTI_HOST(hwdev) || !HINIC_IS_PPF(hwdev)) return 0; hwdev->mhost_mgmt = kzalloc(sizeof(*hwdev->mhost_mgmt), GFP_KERNEL); if (!hwdev->mhost_mgmt) return -ENOMEM; hwdev->mhost_mgmt->mhost_ppf_idx = get_master_host_ppf_idx(hwdev); hwdev->mhost_mgmt->shost_ppf_idx = 0; hwdev->mhost_mgmt->shost_host_idx = 2; err = hinic_get_hw_pf_infos(hwdev, &hwdev->mhost_mgmt->pf_infos); if (err) goto out_free_mhost_mgmt; hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_COMM, comm_ppf_mbox_handler); hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_L2NIC, hinic_nic_ppf_mbox_handler); hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_HILINK, hilink_ppf_mbox_handler); hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_SW_FUNC, sw_func_ppf_mbox_handler); bitmap_zero(hwdev->mhost_mgmt->func_nic_en, HINIC_MAX_FUNCTIONS); /* Slave host: * register slave host ppf functions * Get function's nic state */ if (IS_SLAVE_HOST(hwdev)) { /* PXE don't support to receive mbox from master host */ set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), true); if ((IS_VM_SLAVE_HOST(hwdev) && hinic_get_master_host_mbox_enable(hwdev)) || IS_BMGW_SLAVE_HOST(hwdev)) { err = hinic_register_slave_ppf(hwdev, true); if (err) { set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false); goto out_free_mhost_mgmt; } } } else { /* slave host can send message to mgmt cpu after setup master * mbox */ set_master_host_mbox_enable(hwdev, true); } return 0; out_free_mhost_mgmt: kfree(hwdev->mhost_mgmt); hwdev->mhost_mgmt = NULL; return err; } int hinic_multi_host_mgmt_free(struct hinic_hwdev *hwdev) { if (!IS_MULTI_HOST(hwdev) || !HINIC_IS_PPF(hwdev)) return 0; if (IS_SLAVE_HOST(hwdev)) { hinic_register_slave_ppf(hwdev, false); set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false); } else { set_master_host_mbox_enable(hwdev, false); } hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_COMM); hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_L2NIC); hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_HILINK); hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_SW_FUNC); kfree(hwdev->mhost_mgmt); hwdev->mhost_mgmt = NULL; return 0; } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_multi_host_mgmt.h000066400000000000000000000055371502027332500236770ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef __HINIC_MULTI_HOST_MGMT_H_ #define __HINIC_MULTI_HOST_MGMT_H_ #define IS_BMGW_MASTER_HOST(hwdev) \ ((hwdev)->func_mode == FUNC_MOD_MULTI_BM_MASTER) #define IS_BMGW_SLAVE_HOST(hwdev) \ ((hwdev)->func_mode == FUNC_MOD_MULTI_BM_SLAVE) #define IS_VM_MASTER_HOST(hwdev) \ ((hwdev)->func_mode == FUNC_MOD_MULTI_VM_MASTER) #define IS_VM_SLAVE_HOST(hwdev) \ ((hwdev)->func_mode == FUNC_MOD_MULTI_VM_SLAVE) #define IS_MASTER_HOST(hwdev) \ (IS_BMGW_MASTER_HOST(hwdev) || IS_VM_MASTER_HOST(hwdev)) #define IS_SLAVE_HOST(hwdev) \ (IS_BMGW_SLAVE_HOST(hwdev) || IS_VM_SLAVE_HOST(hwdev)) #define IS_MULTI_HOST(hwdev) \ (IS_BMGW_MASTER_HOST(hwdev) || IS_BMGW_SLAVE_HOST(hwdev) || \ IS_VM_MASTER_HOST(hwdev) || IS_VM_SLAVE_HOST(hwdev)) #define NEED_MBOX_FORWARD(hwdev) IS_BMGW_SLAVE_HOST(hwdev) struct hinic_multi_host_mgmt { struct hinic_hwdev *hwdev; /* slave host registered */ bool shost_registered; u8 shost_host_idx; u8 shost_ppf_idx; /* slave host functios support nic enable */ DECLARE_BITMAP(func_nic_en, HINIC_MAX_FUNCTIONS); u8 mhost_ppf_idx; struct hinic_hw_pf_infos pf_infos; }; struct hinic_host_fwd_head { unsigned short dst_glb_func_idx; unsigned char dst_itf_idx; unsigned char mod; unsigned char cmd; unsigned char rsv[3]; }; int hinic_multi_host_mgmt_init(struct hinic_hwdev *hwdev); int hinic_multi_host_mgmt_free(struct hinic_hwdev *hwdev); int hinic_mbox_to_host_no_ack(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u8 cmd, void *buf_in, u16 in_size); struct register_slave_host { u8 status; u8 version; u8 rsvd[6]; u8 host_id; u8 ppf_idx; u8 rsvd2[6]; /* for max 512 functions */ u64 funcs_nic_en[8]; u64 rsvd3[8]; }; struct hinic_slave_func_nic_state { u8 status; u8 version; u8 rsvd[6]; u16 func_idx; u8 enable; u8 rsvd1; u32 rsvd2[2]; }; void set_master_host_mbox_enable(struct hinic_hwdev *hwdev, bool enable); void set_slave_host_enable(struct hinic_hwdev *hwdev, u8 host_id, bool enable); void set_func_host_mode(struct hinic_hwdev *hwdev, enum hinic_func_mode mode); int rectify_host_mode(struct hinic_hwdev *hwdev); void detect_host_mode_pre(struct hinic_hwdev *hwdev); int sw_func_pf_mbox_handler(void *handle, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_nic.h000066400000000000000000000040671502027332500212320ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_NIC_H_ #define HINIC_NIC_H_ #include "hinic_wq.h" #define SET_VPORT_MBOX_TIMEOUT (30 * 1000) #define SET_VPORT_MGMT_TIMEOUT (25 * 1000) struct hinic_sq { struct hinic_wq *wq; u16 q_id; u8 owner; void *cons_idx_addr; u8 __iomem *db_addr; u16 msix_entry_idx; }; struct hinic_rq { struct hinic_wq *wq; u16 *pi_virt_addr; dma_addr_t pi_dma_addr; u16 q_id; u32 irq_id; u16 msix_entry_idx; dma_addr_t cqe_dma_addr; }; struct hinic_qp { struct hinic_sq sq; struct hinic_rq rq; }; struct vf_data_storage { u8 vf_mac_addr[ETH_ALEN]; bool registered; bool pf_set_mac; u16 pf_vlan; u8 pf_qos; u32 max_rate; u32 min_rate; bool link_forced; bool link_up; /* only valid if VF link is forced */ bool spoofchk; bool trust; }; struct hinic_nic_cfg { struct semaphore cfg_lock; /* Valid when pfc is disable */ bool pause_set; struct nic_pause_config nic_pause; u8 pfc_en; u8 pfc_bitmap; struct nic_port_info port_info; /* percentage of pf link bandwidth */ u32 pf_bw_limit; }; struct hinic_nic_io { struct hinic_hwdev *hwdev; u16 global_qpn; u8 link_status; struct hinic_wqs wqs; struct hinic_wq *sq_wq; struct hinic_wq *rq_wq; u16 max_qps; u16 num_qps; u16 sq_depth; u16 rq_depth; struct hinic_qp *qps; void *ci_vaddr_base; dma_addr_t ci_dma_base; u16 max_vfs; struct vf_data_storage *vf_infos; struct hinic_dcb_state dcb_state; struct hinic_nic_cfg nic_cfg; u16 rx_buff_len; }; #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_nic_cfg.c000066400000000000000000003243031502027332500220420ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hwdev.h" #include "hinic_hw_mgmt.h" #include "hinic_mbox.h" #include "hinic_nic_io.h" #include "hinic_nic_cfg.h" #include "hinic_nic.h" #include "hinic_mgmt_interface.h" #include "hinic_hwif.h" static unsigned char set_vf_link_state; module_param(set_vf_link_state, byte, 0444); MODULE_PARM_DESC(set_vf_link_state, "Set vf link state, 0 represents link auto, 1 represents link always up, 2 represents link always down. - default is 0."); #define l2nic_msg_to_mgmt_sync(hwdev, cmd, buf_in, in_size, buf_out, out_size)\ hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, cmd, \ buf_in, in_size, \ buf_out, out_size, 0) #define l2nic_msg_to_mgmt_async(hwdev, cmd, buf_in, in_size) \ hinic_msg_to_mgmt_async(hwdev, HINIC_MOD_L2NIC, cmd, buf_in, in_size) #define CPATH_FUNC_ID_VALID_LIMIT 2 #define CHECK_IPSU_15BIT 0X8000 static int hinic_set_rx_lro_timer(void *hwdev, u32 timer_value); static bool check_func_table(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size) { struct hinic_function_table *function_table; if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) return false; function_table = (struct hinic_function_table *)buf_in; if (!function_table->rx_wqe_buf_size) return false; return true; } static bool check_rxcsum_setting(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size) { struct hinic_checksum_offload *rx_csum_cfg = NULL; if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) return false; rx_csum_cfg = buf_in; if (rx_csum_cfg->rx_csum_offload != HINIC_RX_CSUM_OFFLOAD_EN) return false; return true; } static bool check_force_pkt_drop(struct hinic_hwdev *hwdev, u16 func_idx, void *buf_in, u16 in_size) { struct hinic_force_pkt_drop *pkt_drop = buf_in; if (pkt_drop->port != hinic_physical_port_id(hwdev)) return false; return true; } struct vf_cmd_check_handle nic_cmd_support_vf[] = { {HINIC_PORT_CMD_VF_REGISTER, NULL}, {HINIC_PORT_CMD_VF_UNREGISTER, NULL}, {HINIC_PORT_CMD_CHANGE_MTU, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_ADD_VLAN, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_DEL_VLAN, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_MAC, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_MAC, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_DEL_MAC, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_RX_MODE, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_PAUSE_INFO, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_LINK_STATE, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_LRO, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_RX_CSUM, check_rxcsum_setting}, {HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_VPORT_STAT, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_CLEAN_VPORT_STAT, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_RSS_TEMPLATE_INDIR_TBL, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_RSS_HASH_ENGINE, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_RSS_HASH_ENGINE, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_RSS_CTX_TBL, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_RSS_CTX_TBL, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_RSS_TEMP_MGR, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_RSS_CFG, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_INIT_FUNC, check_func_table}, {HINIC_PORT_CMD_SET_LLI_PRI, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_MGMT_VERSION, NULL}, {HINIC_PORT_CMD_GET_BOOT_VERSION, NULL}, {HINIC_PORT_CMD_GET_MICROCODE_VERSION, NULL}, {HINIC_PORT_CMD_GET_VPORT_ENABLE, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_VPORT_ENABLE, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_LRO, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_GLOBAL_QPN, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_TSO, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_RQ_IQ_MAP, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_LINK_STATUS_REPORT, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_UPDATE_MAC, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_PORT_INFO, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_IPSU_MAC, hinic_mbox_check_func_id_10B}, {HINIC_PORT_CMD_GET_IPSU_MAC, hinic_mbox_check_func_id_10B}, {HINIC_PORT_CMD_GET_LINK_MODE, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_CLEAR_SQ_RES, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_SUPER_CQE, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_GET_VF_COS, NULL}, {HINIC_PORT_CMD_SET_VHD_CFG, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_SET_VLAN_FILTER, hinic_mbox_check_func_id_8B}, {HINIC_PORT_CMD_FORCE_PKT_DROP, check_force_pkt_drop}, }; int hinic_init_function_table(void *hwdev, u16 rx_buf_sz) { struct hinic_function_table function_table = {0}; u16 out_size = sizeof(function_table); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &function_table.func_id); if (err) return err; function_table.version = HINIC_CMD_VER_FUNC_ID; function_table.mtu = 0x3FFF; /* default, max mtu */ function_table.rx_wqe_buf_size = rx_buf_sz; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_INIT_FUNC, &function_table, sizeof(function_table), &function_table, &out_size, 0); if (err || function_table.status || !out_size) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to init func table, err: %d, status: 0x%x, out size: 0x%x\n", err, function_table.status, out_size); return -EFAULT; } return 0; } int hinic_get_base_qpn(void *hwdev, u16 *global_qpn) { struct hinic_cmd_qpn cmd_qpn = {0}; u16 out_size = sizeof(cmd_qpn); int err; if (!hwdev || !global_qpn) return -EINVAL; err = hinic_global_func_id_get(hwdev, &cmd_qpn.func_id); if (err) return err; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_GET_GLOBAL_QPN, &cmd_qpn, sizeof(cmd_qpn), &cmd_qpn, &out_size, 0); if (err || !out_size || cmd_qpn.status) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get base qpn, err: %d, status: 0x%x, out size: 0x%x\n", err, cmd_qpn.status, out_size); return -EINVAL; } *global_qpn = cmd_qpn.base_qpn; return 0; } int hinic_get_fw_support_func(void *hwdev) { struct fw_support_func support_flag = {0}; struct hinic_hwdev *dev = hwdev; u16 out_size = sizeof(support_flag); int err; if (!hwdev) return -EINVAL; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_GET_FW_SUPPORT_FLAG, &support_flag, sizeof(support_flag), &support_flag, &out_size, 0); if (support_flag.status == HINIC_MGMT_CMD_UNSUPPORTED) { nic_info(dev->dev_hdl, "Current firmware doesn't support to get function capability\n"); support_flag.flag = 0; } else if (support_flag.status || err || !out_size) { nic_err(dev->dev_hdl, "Failed to get function capability, err: %d, status: 0x%x, out size: 0x%x\n", err, support_flag.status, out_size); return -EFAULT; } dev->fw_support_func_flag = support_flag.flag; return 0; } #define HINIC_ADD_VLAN_IN_MAC 0x8000 #define HINIC_VLAN_ID_MASK 0x7FFF #define PF_SET_VF_MAC(hwdev, status) \ (HINIC_FUNC_TYPE(hwdev) == TYPE_VF && \ (status) == HINIC_PF_SET_VF_ALREADY) static int hinic_check_mac_status(struct hinic_hwdev *hwdev, u8 status, u16 vlan_id) { if ((status && status != HINIC_MGMT_STATUS_EXIST) || (vlan_id & CHECK_IPSU_15BIT && status == HINIC_MGMT_STATUS_EXIST)) { if (PF_SET_VF_MAC(hwdev, status)) return 0; return -EINVAL; } return 0; } int hinic_set_mac(void *hwdev, const u8 *mac_addr, u16 vlan_id, u16 func_id) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_port_mac_set mac_info = {0}; u16 out_size = sizeof(mac_info); int err; if (!hwdev || !mac_addr) return -EINVAL; if ((vlan_id & HINIC_VLAN_ID_MASK) >= VLAN_N_VID) { nic_err(nic_hwdev->dev_hdl, "Invalid VLAN number: %d\n", (vlan_id & HINIC_VLAN_ID_MASK)); return -EINVAL; } mac_info.func_id = func_id; mac_info.vlan_id = vlan_id; memcpy(mac_info.mac, mac_addr, ETH_ALEN); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_MAC, &mac_info, sizeof(mac_info), &mac_info, &out_size); if (err || !out_size || hinic_check_mac_status(hwdev, mac_info.status, mac_info.vlan_id)) { nic_err(nic_hwdev->dev_hdl, "Failed to update MAC, err: %d, status: 0x%x, out size: 0x%x\n", err, mac_info.status, out_size); return -EIO; } if (PF_SET_VF_MAC(nic_hwdev, mac_info.status)) { nic_warn(nic_hwdev->dev_hdl, "PF has already set VF mac, Ignore set operation\n"); return HINIC_PF_SET_VF_ALREADY; } if (mac_info.status == HINIC_MGMT_STATUS_EXIST) { nic_warn(nic_hwdev->dev_hdl, "MAC is repeated. Ignore update operation\n"); return 0; } return 0; } EXPORT_SYMBOL(hinic_set_mac); int hinic_del_mac(void *hwdev, const u8 *mac_addr, u16 vlan_id, u16 func_id) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_port_mac_set mac_info = {0}; u16 out_size = sizeof(mac_info); int err; if (!hwdev || !mac_addr) return -EINVAL; if ((vlan_id & HINIC_VLAN_ID_MASK) >= VLAN_N_VID) { nic_err(nic_hwdev->dev_hdl, "Invalid VLAN number: %d\n", (vlan_id & HINIC_VLAN_ID_MASK)); return -EINVAL; } mac_info.func_id = func_id; mac_info.vlan_id = vlan_id; memcpy(mac_info.mac, mac_addr, ETH_ALEN); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_DEL_MAC, &mac_info, sizeof(mac_info), &mac_info, &out_size); if (err || !out_size || (mac_info.status && !PF_SET_VF_MAC(nic_hwdev, mac_info.status))) { nic_err(nic_hwdev->dev_hdl, "Failed to delete MAC, err: %d, status: 0x%x, out size: 0x%x\n", err, mac_info.status, out_size); return -EIO; } if (PF_SET_VF_MAC(nic_hwdev, mac_info.status)) { nic_warn(nic_hwdev->dev_hdl, "PF has already set VF mac, Ignore delete operation\n"); return HINIC_PF_SET_VF_ALREADY; } return 0; } EXPORT_SYMBOL(hinic_del_mac); int hinic_update_mac(void *hwdev, const u8 *old_mac, u8 *new_mac, u16 vlan_id, u16 func_id) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_port_mac_update mac_info = {0}; u16 out_size = sizeof(mac_info); int err; if (!hwdev || !old_mac || !new_mac) return -EINVAL; if ((vlan_id & HINIC_VLAN_ID_MASK) >= VLAN_N_VID) { nic_err(nic_hwdev->dev_hdl, "Invalid VLAN number: %d\n", (vlan_id & HINIC_VLAN_ID_MASK)); return -EINVAL; } mac_info.func_id = func_id; mac_info.vlan_id = vlan_id; memcpy(mac_info.old_mac, old_mac, ETH_ALEN); memcpy(mac_info.new_mac, new_mac, ETH_ALEN); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_UPDATE_MAC, &mac_info, sizeof(mac_info), &mac_info, &out_size); if (err || !out_size || hinic_check_mac_status(hwdev, mac_info.status, mac_info.vlan_id)) { nic_err(nic_hwdev->dev_hdl, "Failed to update MAC, err: %d, status: 0x%x, out size: 0x%x\n", err, mac_info.status, out_size); return -EIO; } if (PF_SET_VF_MAC(nic_hwdev, mac_info.status)) { nic_warn(nic_hwdev->dev_hdl, "PF has already set VF MAC. Ignore update operation\n"); return HINIC_PF_SET_VF_ALREADY; } if (mac_info.status == HINIC_MGMT_STATUS_EXIST) { nic_warn(nic_hwdev->dev_hdl, "MAC is repeated. Ignore update operation\n"); return 0; } return 0; } int hinic_update_mac_vlan(void *hwdev, u16 old_vlan, u16 new_vlan, int vf_id) { struct hinic_hwdev *dev = hwdev; struct vf_data_storage *vf_info; u16 func_id, vlan_id; int err; if (!hwdev || old_vlan >= VLAN_N_VID || new_vlan >= VLAN_N_VID) return -EINVAL; vf_info = dev->nic_io->vf_infos + HW_VF_ID_TO_OS(vf_id); if (!vf_info->pf_set_mac) return 0; if (!FW_SUPPORT_MAC_REUSE_FUNC(dev)) { nic_info(dev->dev_hdl, "Current firmware doesn't support mac reuse\n"); return 0; } func_id = hinic_glb_pf_vf_offset(dev) + (u16)vf_id; vlan_id = old_vlan; if (vlan_id) vlan_id |= HINIC_ADD_VLAN_IN_MAC; err = hinic_del_mac(dev, vf_info->vf_mac_addr, vlan_id, func_id); if (err) { nic_err(dev->dev_hdl, "Failed to delete VF %d MAC %pM vlan %d\n", HW_VF_ID_TO_OS(vf_id), vf_info->vf_mac_addr, vlan_id); return err; } vlan_id = new_vlan; if (vlan_id) vlan_id |= HINIC_ADD_VLAN_IN_MAC; err = hinic_set_mac(dev, vf_info->vf_mac_addr, vlan_id, func_id); if (err) { nic_err(dev->dev_hdl, "Failed to add VF %d MAC %pM vlan %d\n", HW_VF_ID_TO_OS(vf_id), vf_info->vf_mac_addr, vlan_id); goto out; } return 0; out: vlan_id = old_vlan; if (vlan_id) vlan_id |= HINIC_ADD_VLAN_IN_MAC; hinic_set_mac(dev, vf_info->vf_mac_addr, vlan_id, func_id); return err; } int hinic_get_default_mac(void *hwdev, u8 *mac_addr) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_port_mac_set mac_info = {0}; u16 out_size = sizeof(mac_info); int err; if (!hwdev || !mac_addr) return -EINVAL; err = hinic_global_func_id_get(hwdev, &mac_info.func_id); if (err) return err; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_MAC, &mac_info, sizeof(mac_info), &mac_info, &out_size); if (err || !out_size || mac_info.status) { nic_err(nic_hwdev->dev_hdl, "Failed to get mac, err: %d, status: 0x%x, out size: 0x%x\n", err, mac_info.status, out_size); return -EINVAL; } memcpy(mac_addr, mac_info.mac, ETH_ALEN); return 0; } int hinic_set_port_mtu(void *hwdev, u32 new_mtu) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_mtu mtu_info = {0}; u16 out_size = sizeof(mtu_info); int err; if (!hwdev) return -EINVAL; if (new_mtu < HINIC_MIN_MTU_SIZE) { nic_err(nic_hwdev->dev_hdl, "Invalid mtu size, mtu size < %dbytes\n", HINIC_MIN_MTU_SIZE); return -EINVAL; } if (new_mtu > HINIC_MAX_JUMBO_FRAME_SIZE) { nic_err(nic_hwdev->dev_hdl, "Invalid mtu size, mtu size > %dbytes\n", HINIC_MAX_JUMBO_FRAME_SIZE); return -EINVAL; } err = hinic_global_func_id_get(hwdev, &mtu_info.func_id); if (err) return err; mtu_info.mtu = new_mtu; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_CHANGE_MTU, &mtu_info, sizeof(mtu_info), &mtu_info, &out_size); if (err || !out_size || mtu_info.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set mtu, err: %d, status: 0x%x, out size: 0x%x\n", err, mtu_info.status, out_size); return -EINVAL; } return 0; } int hinic_hiovs_set_cpath_vlan(void *hwdev, u16 vlan_id, u16 pf_id) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct cmd_cpath_vlan cpath_vlan_info = {0}; u16 out_size = sizeof(cpath_vlan_info); int err; if (!hwdev) return -EINVAL; cpath_vlan_info.pf_id = pf_id; cpath_vlan_info.vlan_id = vlan_id; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_OVS, OVS_SET_CPATH_VLAN, &cpath_vlan_info, sizeof(cpath_vlan_info), &cpath_vlan_info, &out_size, 0); if (err || !out_size || cpath_vlan_info.status) { sdk_err(nic_hwdev->dev_hdl, "Failed to set cpath vlan, err: %d, status: 0x%x, out_size: 0x%0x\n", err, cpath_vlan_info.status, out_size); return -EFAULT; } return 0; } int hinic_hiovs_del_cpath_vlan(void *hwdev, u16 vlan_id, u16 pf_id) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct cmd_cpath_vlan cpath_vlan_info = {0}; u16 out_size = sizeof(cpath_vlan_info); int err; if (!hwdev) return -EINVAL; cpath_vlan_info.pf_id = pf_id; cpath_vlan_info.vlan_id = vlan_id; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_OVS, OVS_DEL_CPATH_VLAN, &cpath_vlan_info, sizeof(cpath_vlan_info), &cpath_vlan_info, &out_size, 0); if (err || !out_size || cpath_vlan_info.status) { sdk_err(nic_hwdev->dev_hdl, "Failed to delte cpath vlan, err: %d, status: 0x%x, out_size: 0x%0x\n", err, cpath_vlan_info.status, out_size); return -EFAULT; } return 0; } int hinic_enable_netq(void *hwdev, u8 en) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_netq_cfg_msg netq_cfg = {0}; u16 out_size = sizeof(netq_cfg); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &netq_cfg.func_id); if (err) return err; netq_cfg.netq_en = en; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_NETQ, &netq_cfg, sizeof(netq_cfg), &netq_cfg, &out_size); if (netq_cfg.status == HINIC_MGMT_CMD_UNSUPPORTED) { err = HINIC_MGMT_CMD_UNSUPPORTED; nic_warn(nic_hwdev->dev_hdl, "Not support enable netq\n"); } else if (err || !out_size || netq_cfg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to enable netq, err: %d, status: 0x%x, out size: 0x%x\n", err, netq_cfg.status, out_size); } return err; } int hinic_add_hw_rqfilter(void *hwdev, struct hinic_rq_filter_info *filter_info) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rq_filter_msg filter_msg = {0}; u16 out_size = sizeof(filter_msg); int err; if (!hwdev || !filter_info) return -EINVAL; switch (filter_info->filter_type) { case HINIC_RQ_FILTER_TYPE_MAC_ONLY: memcpy(filter_msg.mac, filter_info->mac, ETH_ALEN); break; case HINIC_RQ_FILTER_TYPE_VXLAN: memcpy(filter_msg.mac, filter_info->mac, ETH_ALEN); memcpy(filter_msg.vxlan.inner_mac, filter_info->vxlan.inner_mac, ETH_ALEN); filter_msg.vxlan.vni = filter_info->vxlan.vni; break; default: nic_warn(nic_hwdev->dev_hdl, "No support filter type: 0x%x\n", filter_info->filter_type); return -EINVAL; } err = hinic_global_func_id_get(hwdev, &filter_msg.func_id); if (err) return err; filter_msg.filter_type = filter_info->filter_type; filter_msg.qid = filter_info->qid; filter_msg.qflag = filter_info->qflag; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_ADD_RQ_FILTER, &filter_msg, sizeof(filter_msg), &filter_msg, &out_size); if (filter_msg.status == HINIC_MGMT_CMD_UNSUPPORTED) { err = HINIC_MGMT_CMD_UNSUPPORTED; nic_warn(nic_hwdev->dev_hdl, "Not support add rxq filter\n"); } else if (err || !out_size || filter_msg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to add RX qfilter, err: %d, status: 0x%x, out size: 0x%x\n", err, filter_msg.status, out_size); return -EINVAL; } return err; } int hinic_del_hw_rqfilter(void *hwdev, struct hinic_rq_filter_info *filter_info) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rq_filter_msg filter_msg = {0}; u16 out_size = sizeof(filter_msg); int err; if (!hwdev || !filter_info) return -EINVAL; switch (filter_info->filter_type) { case HINIC_RQ_FILTER_TYPE_MAC_ONLY: memcpy(filter_msg.mac, filter_info->mac, ETH_ALEN); break; case HINIC_RQ_FILTER_TYPE_VXLAN: memcpy(filter_msg.mac, filter_info->mac, ETH_ALEN); memcpy(filter_msg.vxlan.inner_mac, filter_info->vxlan.inner_mac, ETH_ALEN); filter_msg.vxlan.vni = filter_info->vxlan.vni; break; default: nic_warn(nic_hwdev->dev_hdl, "No support filter type: 0x%x\n", filter_info->filter_type); return -EINVAL; } err = hinic_global_func_id_get(hwdev, &filter_msg.func_id); if (err) return err; filter_msg.filter_type = filter_info->filter_type; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_DEL_RQ_FILTER, &filter_msg, sizeof(filter_msg), &filter_msg, &out_size); if (filter_msg.status == HINIC_MGMT_CMD_UNSUPPORTED) { err = HINIC_MGMT_CMD_UNSUPPORTED; nic_warn(nic_hwdev->dev_hdl, "Not support del rxq filter\n"); } else if (err || !out_size || filter_msg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to delte RX qfilter, err: %d, status: 0x%x, out size: 0x%x\n", err, filter_msg.status, out_size); return -EINVAL; } return err; } int hinic_add_vlan(void *hwdev, u16 vlan_id, u16 func_id) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_vlan_config vlan_info = {0}; u16 out_size = sizeof(vlan_info); int err; if (!hwdev) return -EINVAL; vlan_info.func_id = func_id; vlan_info.vlan_id = vlan_id; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_ADD_VLAN, &vlan_info, sizeof(vlan_info), &vlan_info, &out_size); if (err || !out_size || vlan_info.status) { nic_err(nic_hwdev->dev_hdl, "Failed to add vlan, err: %d, status: 0x%x, out size: 0x%x\n", err, vlan_info.status, out_size); return -EINVAL; } return 0; } int hinic_del_vlan(void *hwdev, u16 vlan_id, u16 func_id) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_vlan_config vlan_info = {0}; u16 out_size = sizeof(vlan_info); int err; if (!hwdev) return -EINVAL; vlan_info.func_id = func_id; vlan_info.vlan_id = vlan_id; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_DEL_VLAN, &vlan_info, sizeof(vlan_info), &vlan_info, &out_size); if (err || !out_size || vlan_info.status) { nic_err(nic_hwdev->dev_hdl, "Failed to delte vlan, err: %d, status: 0x%x, out size: 0x%x\n", err, vlan_info.status, out_size); return -EINVAL; } return 0; } int hinic_set_vlan_fliter(void *hwdev, u32 vlan_filter_ctrl) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_vlan_filter vlan_filter = {0}; u16 out_size = sizeof(vlan_filter); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &vlan_filter.func_id); if (err) return err; vlan_filter.vlan_filter_ctrl = vlan_filter_ctrl; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_VLAN_FILTER, &vlan_filter, sizeof(vlan_filter), &vlan_filter, &out_size); if (vlan_filter.status == HINIC_MGMT_CMD_UNSUPPORTED) { err = HINIC_MGMT_CMD_UNSUPPORTED; } else if ((err == HINIC_MBOX_VF_CMD_ERROR) && HINIC_IS_VF(nic_hwdev)) { err = HINIC_MGMT_CMD_UNSUPPORTED; } else if (err || !out_size || vlan_filter.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set vlan filter, err: %d, status: 0x%x, out size: 0x%x\n", err, vlan_filter.status, out_size); err = -EINVAL; } return err; } int hinic_get_port_info(void *hwdev, struct nic_port_info *port_info) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_port_info port_msg = {0}; u16 out_size = sizeof(port_msg); int err; if (!hwdev || !port_info) return -EINVAL; err = hinic_global_func_id_get(hwdev, &port_msg.func_id); if (err) return err; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_PORT_INFO, &port_msg, sizeof(port_msg), &port_msg, &out_size); if (err || !out_size || port_msg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to get port info, err: %d, status: 0x%x, out size: 0x%x\n", err, port_msg.status, out_size); return -EINVAL; } port_info->autoneg_cap = port_msg.autoneg_cap; port_info->autoneg_state = port_msg.autoneg_state; port_info->duplex = port_msg.duplex; port_info->port_type = port_msg.port_type; port_info->speed = port_msg.speed; return 0; } EXPORT_SYMBOL(hinic_get_port_info); int hinic_set_autoneg(void *hwdev, bool enable) { struct hinic_hwdev *dev = hwdev; struct hinic_set_autoneg_cmd autoneg = {0}; u16 out_size = sizeof(autoneg); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &autoneg.func_id); if (err) return err; autoneg.enable = enable; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_AUTONEG, &autoneg, sizeof(autoneg), &autoneg, &out_size); if (err || !out_size || autoneg.status) { nic_err(dev->dev_hdl, "Failed to %s autoneg, err: %d, status: 0x%x, out size: 0x%x\n", enable ? "enable" : "disable", err, autoneg.status, out_size); return -EINVAL; } return 0; } int hinic_force_port_relink(void *hwdev) { struct hinic_hwdev *dev = hwdev; int err; /* Force port link down and link up */ err = hinic_set_port_link_status(hwdev, false); if (err) { nic_err(dev->dev_hdl, "Failed to set port link down\n"); return -EFAULT; } err = hinic_set_port_link_status(hwdev, true); if (err) { nic_err(dev->dev_hdl, "Failed to set port link up\n"); return -EFAULT; } return 0; } int hinic_get_link_mode(void *hwdev, enum hinic_link_mode *supported, enum hinic_link_mode *advertised) { struct hinic_hwdev *dev = hwdev; struct hinic_link_mode_cmd link_mode = {0}; u16 out_size = sizeof(link_mode); int err; if (!hwdev || !supported || !advertised) return -EINVAL; err = hinic_global_func_id_get(hwdev, &link_mode.func_id); if (err) return err; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_LINK_MODE, &link_mode, sizeof(link_mode), &link_mode, &out_size); if (err || !out_size || link_mode.status) { nic_err(dev->dev_hdl, "Failed to get link mode, err: %d, status: 0x%x, out size: 0x%x\n", err, link_mode.status, out_size); return -EINVAL; } *supported = link_mode.supported; *advertised = link_mode.advertised; return 0; } int hinic_set_port_link_status(void *hwdev, bool enable) { struct hinic_hwdev *dev = hwdev; struct hinic_set_link_status link_status = {0}; u16 out_size = sizeof(link_status); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &link_status.func_id); if (err) return err; link_status.enable = enable; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_PORT_LINK_STATUS, &link_status, sizeof(link_status), &link_status, &out_size); if (err || !out_size || link_status.status) { nic_err(dev->dev_hdl, "Failed to %s port link status, err: %d, status: 0x%x, out size: 0x%x\n", enable ? "Enable" : "Disable", err, link_status.status, out_size); return -EINVAL; } return 0; } int hinic_set_speed(void *hwdev, enum nic_speed_level speed) { struct hinic_hwdev *dev = hwdev; struct hinic_speed_cmd speed_info = {0}; u16 out_size = sizeof(speed_info); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &speed_info.func_id); if (err) return err; speed_info.speed = speed; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_SPEED, &speed_info, sizeof(speed_info), &speed_info, &out_size); if (err || !out_size || speed_info.status) { nic_err(dev->dev_hdl, "Failed to set speed, err: %d, status: 0x%x, out size: 0x%x\n", err, speed_info.status, out_size); return -EINVAL; } return 0; } int hinic_get_speed(void *hwdev, enum nic_speed_level *speed) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_speed_cmd speed_info = {0}; u16 out_size = sizeof(speed_info); int err; if (!hwdev || !speed) return -EINVAL; err = hinic_global_func_id_get(hwdev, &speed_info.func_id); if (err) return err; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_SPEED, &speed_info, sizeof(speed_info), &speed_info, &out_size); if (err || !out_size || speed_info.status) { nic_err(nic_hwdev->dev_hdl, "Failed to get speed, err: %d, status: 0x%x, out size: 0x%x\n", err, speed_info.status, out_size); return -EINVAL; } *speed = speed_info.speed; return 0; } EXPORT_SYMBOL(hinic_get_speed); int hinic_get_link_state(void *hwdev, u8 *link_state) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_get_link get_link = {0}; u16 out_size = sizeof(get_link); int err; if (!hwdev || !link_state) return -EINVAL; if (FUNC_FORCE_LINK_UP(hwdev)) { *link_state = 1; return 0; } err = hinic_global_func_id_get(hwdev, &get_link.func_id); if (err) return err; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_LINK_STATE, &get_link, sizeof(get_link), &get_link, &out_size); if (err || !out_size || get_link.status) { nic_err(nic_hwdev->dev_hdl, "Failed to get link state, err: %d, status: 0x%x, out size: 0x%x\n", err, get_link.status, out_size); return -EINVAL; } *link_state = get_link.link_status; return 0; } static int hinic_set_hw_pause_info(void *hwdev, struct nic_pause_config nic_pause) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_pause_config pause_info = {0}; u16 out_size = sizeof(pause_info); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &pause_info.func_id); if (err) return err; pause_info.auto_neg = nic_pause.auto_neg; pause_info.rx_pause = nic_pause.rx_pause; pause_info.tx_pause = nic_pause.tx_pause; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_PAUSE_INFO, &pause_info, sizeof(pause_info), &pause_info, &out_size); if (err || !out_size || pause_info.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set pause info, err: %d, status: 0x%x, out size: 0x%x\n", err, pause_info.status, out_size); return -EINVAL; } return 0; } int hinic_set_pause_info(void *hwdev, struct nic_pause_config nic_pause) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_nic_cfg *nic_cfg; int err; if (!hwdev) return -EINVAL; nic_cfg = &nic_hwdev->nic_io->nic_cfg; if (nic_cfg->pfc_en) { nic_err(nic_hwdev->dev_hdl, "Failed to set pause, please disable pfc first\n"); return -EPERM; } down(&nic_cfg->cfg_lock); err = hinic_set_hw_pause_info(hwdev, nic_pause); if (err) { up(&nic_cfg->cfg_lock); return err; } nic_cfg->pfc_en = 0; nic_cfg->pfc_bitmap = 0; nic_cfg->pause_set = true; nic_cfg->nic_pause.auto_neg = nic_pause.auto_neg; nic_cfg->nic_pause.rx_pause = nic_pause.rx_pause; nic_cfg->nic_pause.tx_pause = nic_pause.tx_pause; up(&nic_cfg->cfg_lock); return 0; } int hinic_get_hw_pause_info(void *hwdev, struct nic_pause_config *nic_pause) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_pause_config pause_info = {0}; u16 out_size = sizeof(pause_info); int err; if (!hwdev || !nic_pause) return -EINVAL; err = hinic_global_func_id_get(hwdev, &pause_info.func_id); if (err) return err; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_PAUSE_INFO, &pause_info, sizeof(pause_info), &pause_info, &out_size); if (err || !out_size || pause_info.status) { nic_err(nic_hwdev->dev_hdl, "Failed to get pause info, err: %d, status: 0x%x, out size: 0x%x\n", err, pause_info.status, out_size); return -EINVAL; } 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 hinic_get_pause_info(void *hwdev, struct nic_pause_config *nic_pause) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_nic_cfg *nic_cfg = &nic_hwdev->nic_io->nic_cfg; int err = 0; err = hinic_get_hw_pause_info(hwdev, nic_pause); if (err) return err; if (nic_cfg->pause_set || !nic_pause->auto_neg) { nic_pause->rx_pause = nic_cfg->nic_pause.rx_pause; nic_pause->tx_pause = nic_cfg->nic_pause.tx_pause; } return 0; } int hinic_set_rx_mode(void *hwdev, u32 enable) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rx_mode_config rx_mode_cfg = {0}; u16 out_size = sizeof(rx_mode_cfg); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &rx_mode_cfg.func_id); if (err) return err; rx_mode_cfg.rx_mode = enable; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_RX_MODE, &rx_mode_cfg, sizeof(rx_mode_cfg), &rx_mode_cfg, &out_size); if (err || !out_size || rx_mode_cfg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set rx mode, err: %d, status: 0x%x, out size: 0x%x\n", err, rx_mode_cfg.status, out_size); return -EINVAL; } return 0; } /* offload feature */ int hinic_set_rx_vlan_offload(void *hwdev, u8 en) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_vlan_offload vlan_cfg = {0}; u16 out_size = sizeof(vlan_cfg); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &vlan_cfg.func_id); if (err) return err; vlan_cfg.vlan_rx_offload = en; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD, &vlan_cfg, sizeof(vlan_cfg), &vlan_cfg, &out_size); if (err || !out_size || vlan_cfg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set rx vlan offload, err: %d, status: 0x%x, out size: 0x%x\n", err, vlan_cfg.status, out_size); return -EINVAL; } return 0; } int hinic_set_rx_csum_offload(void *hwdev, u32 en) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_checksum_offload rx_csum_cfg = {0}; u16 out_size = sizeof(rx_csum_cfg); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &rx_csum_cfg.func_id); if (err) return err; rx_csum_cfg.rx_csum_offload = en; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_RX_CSUM, &rx_csum_cfg, sizeof(rx_csum_cfg), &rx_csum_cfg, &out_size); if (err || !out_size || rx_csum_cfg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set rx csum offload, err: %d, status: 0x%x, out size: 0x%x\n", err, rx_csum_cfg.status, out_size); return -EINVAL; } return 0; } int hinic_set_tx_tso(void *hwdev, u8 tso_en) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_tso_config tso_cfg = {0}; u16 out_size = sizeof(tso_cfg); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &tso_cfg.func_id); if (err) return err; tso_cfg.tso_en = tso_en; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_TSO, &tso_cfg, sizeof(tso_cfg), &tso_cfg, &out_size); if (err || !out_size || tso_cfg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set tso, err: %d, status: 0x%x, out size: 0x%x\n", err, tso_cfg.status, out_size); return -EINVAL; } return 0; } int hinic_set_rx_lro_state(void *hwdev, u8 lro_en, u32 lro_timer, u32 wqe_num) { struct hinic_hwdev *nic_hwdev = hwdev; u8 ipv4_en = 0, ipv6_en = 0; int err; if (!hwdev) return -EINVAL; ipv4_en = lro_en ? 1 : 0; ipv6_en = lro_en ? 1 : 0; nic_info(nic_hwdev->dev_hdl, "Set LRO max wqe number to %u\n", wqe_num); err = hinic_set_rx_lro(hwdev, ipv4_en, ipv6_en, (u8)wqe_num); if (err) return err; /* we don't set LRO timer for VF */ if (hinic_func_type(hwdev) == TYPE_VF) return 0; nic_info(nic_hwdev->dev_hdl, "Set LRO timer to %u\n", lro_timer); return hinic_set_rx_lro_timer(hwdev, lro_timer); } static int hinic_set_rx_lro_timer(void *hwdev, u32 timer_value) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_lro_timer lro_timer = {0}; u16 out_size = sizeof(lro_timer); int err; if (!hwdev) return -EINVAL; lro_timer.status = 0; lro_timer.type = 0; lro_timer.enable = 1; lro_timer.timer = timer_value; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_LRO_TIMER, &lro_timer, sizeof(lro_timer), &lro_timer, &out_size); if (lro_timer.status == 0xFF) { /* For this case, we think status (0xFF) is OK */ lro_timer.status = 0; nic_err(nic_hwdev->dev_hdl, "Set lro timer not supported by the current FW version, it will be 1ms default\n"); } if (err || !out_size || lro_timer.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set lro timer, err: %d, status: 0x%x, out size: 0x%x\n", err, lro_timer.status, out_size); return -EINVAL; } return 0; } int hinic_set_rx_lro(void *hwdev, u8 ipv4_en, u8 ipv6_en, u8 max_wqe_num) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_lro_config lro_cfg = {0}; u16 out_size = sizeof(lro_cfg); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &lro_cfg.func_id); if (err) return err; lro_cfg.lro_ipv4_en = ipv4_en; lro_cfg.lro_ipv6_en = ipv6_en; lro_cfg.lro_max_wqe_num = max_wqe_num; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_LRO, &lro_cfg, sizeof(lro_cfg), &lro_cfg, &out_size); if (err || !out_size || lro_cfg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set lro offload, err: %d, status: 0x%x, out size: 0x%x\n", err, lro_cfg.status, out_size); return -EINVAL; } return 0; } static int hinic_dcb_set_hw_pfc(void *hwdev, u8 pfc_en, u8 pfc_bitmap) { struct hinic_hwdev *dev = hwdev; struct hinic_set_pfc pfc = {0}; u16 out_size = sizeof(pfc); int err; err = hinic_global_func_id_get(hwdev, &pfc.func_id); if (err) return err; pfc.pfc_bitmap = pfc_bitmap; pfc.pfc_en = pfc_en; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_PFC, &pfc, sizeof(pfc), &pfc, &out_size); if (err || pfc.status || !out_size) { nic_err(dev->dev_hdl, "Failed to set pfc, err: %d, status: 0x%x, out size: 0x%x\n", err, pfc.status, out_size); return -EINVAL; } return 0; } /* dcbtool */ int hinic_dcb_set_pfc(void *hwdev, u8 pfc_en, u8 pfc_bitmap) { struct hinic_hwdev *dev = hwdev; struct hinic_nic_cfg *nic_cfg = &dev->nic_io->nic_cfg; int err; down(&nic_cfg->cfg_lock); err = hinic_dcb_set_hw_pfc(hwdev, pfc_en, pfc_bitmap); if (err) { up(&nic_cfg->cfg_lock); return err; } nic_cfg->pfc_en = pfc_en; nic_cfg->pfc_bitmap = pfc_bitmap; /* pause settings is opposite from pfc */ nic_cfg->nic_pause.rx_pause = pfc_en ? 0 : 1; nic_cfg->nic_pause.tx_pause = pfc_en ? 0 : 1; up(&nic_cfg->cfg_lock); return 0; } int hinic_dcb_get_pfc(void *hwdev, u8 *pfc_en_bitmap) { return 0; } int hinic_dcb_set_ets(void *hwdev, u8 *up_tc, u8 *pg_bw, u8 *pgid, u8 *up_bw, u8 *prio) { struct hinic_up_ets_cfg ets = {0}; u16 out_size = sizeof(ets); u16 up_bw_t = 0; u8 pg_bw_t = 0; int i, err; for (i = 0; i < HINIC_DCB_TC_MAX; i++) { up_bw_t += *(up_bw + i); pg_bw_t += *(pg_bw + i); if (*(up_tc + i) > HINIC_DCB_TC_MAX) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Invalid up %d mapping tc: %d\n", i, *(up_tc + i)); return -EINVAL; } } if (pg_bw_t != 100 || (up_bw_t % 100) != 0) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Invalid pg_bw: %d or up_bw: %d\n", pg_bw_t, up_bw_t); return -EINVAL; } ets.port_id = 0; /* reserved */ memcpy(ets.up_tc, up_tc, HINIC_DCB_TC_MAX); memcpy(ets.pg_bw, pg_bw, HINIC_DCB_UP_MAX); memcpy(ets.pgid, pgid, HINIC_DCB_UP_MAX); memcpy(ets.up_bw, up_bw, HINIC_DCB_UP_MAX); memcpy(ets.prio, prio, HINIC_DCB_UP_MAX); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_ETS, &ets, sizeof(ets), &ets, &out_size); if (err || ets.status || !out_size) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to set ets, err: %d, status: 0x%x, out size: 0x%x\n", err, ets.status, out_size); return -EINVAL; } return 0; } int hinic_dcb_get_ets(void *hwdev, u8 *up_tc, u8 *pg_bw, u8 *pgid, u8 *up_bw, u8 *prio) { return 0; } int hinic_dcb_set_cos_up_map(void *hwdev, u8 cos_valid_bitmap, u8 *cos_up) { struct hinic_cos_up_map map = {0}; u16 out_size = sizeof(map); int err; if (!hwdev || !cos_up) return -EINVAL; map.port_id = hinic_physical_port_id(hwdev); map.cos_valid_mask = cos_valid_bitmap; memcpy(map.map, cos_up, sizeof(map.map)); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_COS_UP_MAP, &map, sizeof(map), &map, &out_size); if (err || map.status || !out_size) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to set cos2up map, err: %d, status: 0x%x, out size: 0x%x\n", err, map.status, out_size); return -EFAULT; } return 0; } int hinic_dcb_set_rq_iq_mapping(void *hwdev, u32 num_rqs, u8 *map) { struct hinic_hwdev *dev; struct hinic_nic_io *nic_io; struct hinic_set_rq_iq_mapping rq_iq_mapping = {0}; u16 out_size = sizeof(rq_iq_mapping); int err; if (!hwdev || num_rqs > HINIC_MAX_NUM_RQ) return -EINVAL; dev = hwdev; nic_io = dev->nic_io; hinic_qps_num_set(dev, nic_io->num_qps); err = hinic_global_func_id_get(hwdev, &rq_iq_mapping.func_id); if (err) return err; rq_iq_mapping.num_rqs = num_rqs; rq_iq_mapping.rq_depth = (u16)ilog2(nic_io->rq_depth); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_RQ_IQ_MAP, &rq_iq_mapping, sizeof(rq_iq_mapping), &rq_iq_mapping, &out_size); if (err || !out_size || rq_iq_mapping.status) { nic_err(dev->dev_hdl, "Failed to set rq cos mapping, err: %d, status: 0x%x, out size: 0x%x\n", err, rq_iq_mapping.status, out_size); return -EINVAL; } return 0; } EXPORT_SYMBOL(hinic_dcb_set_rq_iq_mapping); /* nictool */ int hinic_set_lro_aging_timer(void *hwdev, u8 timer_en, u32 period) { return 0; } int hinic_get_rx_lro(void *hwdev, struct nic_lro_info *cfg) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_lro_config lro_cfg = {0}; u16 out_size = sizeof(lro_cfg); int err; if (!hwdev || !cfg) return -EINVAL; err = hinic_global_func_id_get(hwdev, &lro_cfg.func_id); if (err) return err; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_LRO, &lro_cfg, sizeof(lro_cfg), &lro_cfg, &out_size); if (err || !out_size || lro_cfg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set lro offload, err: %d, status: 0x%x, out size: 0x%x\n", err, lro_cfg.status, out_size); return -EINVAL; } cfg->func_id = lro_cfg.func_id; cfg->lro_ipv4_en = lro_cfg.lro_ipv4_en; cfg->lro_ipv6_en = lro_cfg.lro_ipv6_en; cfg->lro_max_wqe_num = lro_cfg.lro_max_wqe_num; return 0; } int hinic_get_jumbo_frame_size(void *hwdev, u32 *jumbo_size) { return 0; } int hinic_set_jumbo_frame_size(void *hwdev, u32 jumbo_size) { return 0; } int hinic_set_loopback_mode_ex(void *hwdev, u32 mode, u32 enable) { struct hinic_port_loopback lb = {0}; u16 out_size = sizeof(lb); int err; lb.mode = mode; lb.en = enable; if (mode < LOOP_MODE_MIN || mode > LOOP_MODE_MAX) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Invalid loopback mode %d to set\n", mode); return -EINVAL; } err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_LOOPBACK_MODE, &lb, sizeof(lb), &lb, &out_size); if (err || !out_size || lb.status) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to set loopback mode %d en %d, err: %d, status: 0x%x, out size: 0x%x\n", mode, enable, err, lb.status, out_size); return -EINVAL; } nic_info(((struct hinic_hwdev *)hwdev)->dev_hdl, "Set loopback mode %d en %d succeed\n", mode, enable); return 0; } int hinic_get_loopback_mode_ex(void *hwdev, u32 *mode, u32 *enable) { struct hinic_port_loopback lb = {0}; u16 out_size = sizeof(lb); int err; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_LOOPBACK_MODE, &lb, sizeof(lb), &lb, &out_size); if (err || !out_size || lb.status) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get loopback mode, err: %d, status: 0x%x, out size: 0x%x\n", err, lb.status, out_size); return -EINVAL; } *mode = lb.mode; *enable = lb.en; return 0; } int hinic_set_loopback_mode(void *hwdev, bool enable) { return hinic_set_loopback_mode_ex(hwdev, HINIC_INTERNAL_LP_MODE, enable); } int hinic_get_port_enable_state(void *hwdev, bool *enable) { return 0; } int hinic_get_vport_enable_state(void *hwdev, bool *enable) { return 0; } int hinic_set_lli_state(void *hwdev, u8 lli_state) { return 0; } int hinic_set_vport_enable(void *hwdev, bool enable) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_vport_state en_state = {0}; u16 out_size = sizeof(en_state); int err; u32 timeout; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &en_state.func_id); if (err) return err; en_state.state = enable ? 1 : 0; if (HINIC_IS_VF(nic_hwdev)) timeout = SET_VPORT_MBOX_TIMEOUT; else timeout = SET_VPORT_MGMT_TIMEOUT; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_SET_VPORT_ENABLE, &en_state, sizeof(en_state), &en_state, &out_size, timeout); if (err || !out_size || en_state.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set vport state, err: %d, status: 0x%x, out size: 0x%x\n", err, en_state.status, out_size); return -EINVAL; } return 0; } EXPORT_SYMBOL(hinic_set_vport_enable); #define NIC_PORT_DISABLE 0x0 #define NIC_PORT_ENABLE 0x3 int hinic_set_port_enable(void *hwdev, bool enable) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_port_state en_state = {0}; u16 out_size = sizeof(en_state); int err; if (!hwdev) return -EINVAL; if (HINIC_IS_VF(nic_hwdev)) return 0; err = hinic_global_func_id_get(hwdev, &en_state.func_id); if (err) return err; en_state.version = HINIC_CMD_VER_FUNC_ID; en_state.state = enable ? NIC_PORT_ENABLE : NIC_PORT_DISABLE; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_PORT_ENABLE, &en_state, sizeof(en_state), &en_state, &out_size); if (err || !out_size || en_state.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set port state, err: %d, status: 0x%x, out size: 0x%x\n", err, en_state.status, out_size); return -EINVAL; } return 0; } EXPORT_SYMBOL(hinic_set_port_enable); /* rss */ int hinic_set_rss_type(void *hwdev, u32 tmpl_idx, struct nic_rss_type rss_type) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct nic_rss_context_tbl *ctx_tbl; struct hinic_cmd_buf *cmd_buf; u32 ctx = 0; u64 out_param; int err; if (!hwdev) return -EINVAL; cmd_buf = hinic_alloc_cmd_buf(hwdev); if (!cmd_buf) { nic_err(nic_hwdev->dev_hdl, "Failed to allocate cmd buf\n"); return -ENOMEM; } ctx |= HINIC_RSS_TYPE_SET(1, VALID) | HINIC_RSS_TYPE_SET(rss_type.ipv4, IPV4) | HINIC_RSS_TYPE_SET(rss_type.ipv6, IPV6) | HINIC_RSS_TYPE_SET(rss_type.ipv6_ext, IPV6_EXT) | HINIC_RSS_TYPE_SET(rss_type.tcp_ipv4, TCP_IPV4) | HINIC_RSS_TYPE_SET(rss_type.tcp_ipv6, TCP_IPV6) | HINIC_RSS_TYPE_SET(rss_type.tcp_ipv6_ext, TCP_IPV6_EXT) | HINIC_RSS_TYPE_SET(rss_type.udp_ipv4, UDP_IPV4) | HINIC_RSS_TYPE_SET(rss_type.udp_ipv6, UDP_IPV6); cmd_buf->size = sizeof(struct nic_rss_context_tbl); ctx_tbl = (struct nic_rss_context_tbl *)cmd_buf->buf; ctx_tbl->group_index = cpu_to_be32(tmpl_idx); ctx_tbl->offset = 0; ctx_tbl->size = sizeof(u32); ctx_tbl->size = cpu_to_be32(ctx_tbl->size); ctx_tbl->rsvd = 0; ctx_tbl->ctx = cpu_to_be32(ctx); /* cfg the rss context table by command queue */ err = hinic_cmdq_direct_resp(hwdev, HINIC_ACK_TYPE_CMDQ, HINIC_MOD_L2NIC, HINIC_UCODE_CMD_SET_RSS_CONTEXT_TABLE, cmd_buf, &out_param, 0); hinic_free_cmd_buf(hwdev, cmd_buf); if (err || out_param != 0) { nic_err(nic_hwdev->dev_hdl, "Failed to set rss context table, err: %d\n", err); return -EFAULT; } return 0; } int hinic_get_rss_type(void *hwdev, u32 tmpl_idx, struct nic_rss_type *rss_type) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rss_context_table ctx_tbl = {0}; u16 out_size = sizeof(ctx_tbl); int err; if (!hwdev || !rss_type) return -EINVAL; err = hinic_global_func_id_get(hwdev, &ctx_tbl.func_id); if (err) return err; ctx_tbl.template_id = (u8)tmpl_idx; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_RSS_CTX_TBL, &ctx_tbl, sizeof(ctx_tbl), &ctx_tbl, &out_size); if (err || !out_size || ctx_tbl.status) { nic_err(nic_hwdev->dev_hdl, "Failed to get hash type, err: %d, status: 0x%x, out size: 0x%x\n", err, ctx_tbl.status, out_size); return -EINVAL; } rss_type->ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV4); rss_type->ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV6); rss_type->ipv6_ext = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV6_EXT); rss_type->tcp_ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV4); rss_type->tcp_ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV6); rss_type->tcp_ipv6_ext = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV6_EXT); rss_type->udp_ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV4); rss_type->udp_ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV6); return 0; } int hinic_rss_set_template_tbl(void *hwdev, u32 tmpl_idx, const u8 *temp) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rss_template_key temp_key = {0}; u16 out_size = sizeof(temp_key); int err; if (!hwdev || !temp) return -EINVAL; err = hinic_global_func_id_get(hwdev, &temp_key.func_id); if (err) return err; temp_key.template_id = (u8)tmpl_idx; memcpy(temp_key.key, temp, HINIC_RSS_KEY_SIZE); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL, &temp_key, sizeof(temp_key), &temp_key, &out_size); if (err || !out_size || temp_key.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set hash key, err: %d, status: 0x%x, out size: 0x%x\n", err, temp_key.status, out_size); return -EINVAL; } return 0; } int hinic_rss_get_template_tbl(void *hwdev, u32 tmpl_idx, u8 *temp) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rss_template_key temp_key = {0}; u16 out_size = sizeof(temp_key); int err; if (!hwdev || !temp) return -EINVAL; err = hinic_global_func_id_get(hwdev, &temp_key.func_id); if (err) return err; temp_key.template_id = (u8)tmpl_idx; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL, &temp_key, sizeof(temp_key), &temp_key, &out_size); if (err || !out_size || temp_key.status) { nic_err(nic_hwdev->dev_hdl, "Failed to get hash key, err: %d, status: 0x%x, out size: 0x%x\n", err, temp_key.status, out_size); return -EINVAL; } memcpy(temp, temp_key.key, HINIC_RSS_KEY_SIZE); return 0; } int hinic_rss_get_hash_engine(void *hwdev, u8 tmpl_idx, u8 *type) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rss_engine_type hash_type = {0}; u16 out_size = sizeof(hash_type); int err; if (!hwdev || !type) return -EINVAL; err = hinic_global_func_id_get(hwdev, &hash_type.func_id); if (err) return err; hash_type.template_id = tmpl_idx; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_RSS_HASH_ENGINE, &hash_type, sizeof(hash_type), &hash_type, &out_size); if (err || !out_size || hash_type.status) { nic_err(nic_hwdev->dev_hdl, "Failed to get hash engine, err: %d, status: 0x%x, out size: 0x%x\n", err, hash_type.status, out_size); return -EINVAL; } *type = hash_type.hash_engine; return 0; } int hinic_rss_set_hash_engine(void *hwdev, u8 tmpl_idx, u8 type) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rss_engine_type hash_type = {0}; u16 out_size = sizeof(hash_type); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &hash_type.func_id); if (err) return err; hash_type.hash_engine = type; hash_type.template_id = tmpl_idx; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_RSS_HASH_ENGINE, &hash_type, sizeof(hash_type), &hash_type, &out_size); if (err || !out_size || hash_type.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set hash engine, err: %d, status: 0x%x, out size: 0x%x\n", err, hash_type.status, out_size); return -EINVAL; } return 0; } int hinic_rss_set_indir_tbl(void *hwdev, u32 tmpl_idx, const u32 *indir_table) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct nic_rss_indirect_tbl *indir_tbl; struct hinic_cmd_buf *cmd_buf; u32 i; u32 *temp; u32 indir_size; u64 out_param; int err; if (!hwdev || !indir_table) return -EINVAL; cmd_buf = hinic_alloc_cmd_buf(hwdev); if (!cmd_buf) { nic_err(nic_hwdev->dev_hdl, "Failed to allocate cmd buf\n"); return -ENOMEM; } cmd_buf->size = sizeof(struct nic_rss_indirect_tbl); indir_tbl = (struct nic_rss_indirect_tbl *)cmd_buf->buf; indir_tbl->group_index = cpu_to_be32(tmpl_idx); for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) { indir_tbl->entry[i] = (u8)(*(indir_table + i)); if (0x3 == (i & 0x3)) { temp = (u32 *)&indir_tbl->entry[i - 3]; *temp = cpu_to_be32(*temp); } } /* cfg the rss indirect table by command queue */ indir_size = HINIC_RSS_INDIR_SIZE / 2; indir_tbl->offset = 0; indir_tbl->size = cpu_to_be32(indir_size); err = hinic_cmdq_direct_resp(hwdev, HINIC_ACK_TYPE_CMDQ, HINIC_MOD_L2NIC, HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE, cmd_buf, &out_param, 0); if (err || out_param != 0) { nic_err(nic_hwdev->dev_hdl, "Failed to set rss indir table\n"); err = -EFAULT; goto free_buf; } indir_tbl->offset = cpu_to_be32(indir_size); indir_tbl->size = cpu_to_be32(indir_size); memcpy(&indir_tbl->entry[0], &indir_tbl->entry[indir_size], indir_size); err = hinic_cmdq_direct_resp(hwdev, HINIC_ACK_TYPE_CMDQ, HINIC_MOD_L2NIC, HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE, cmd_buf, &out_param, 0); if (err || out_param != 0) { nic_err(nic_hwdev->dev_hdl, "Failed to set rss indir table\n"); err = -EFAULT; } free_buf: hinic_free_cmd_buf(hwdev, cmd_buf); return err; } int hinic_rss_get_indir_tbl(void *hwdev, u32 tmpl_idx, u32 *indir_table) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rss_indir_table rss_cfg = {0}; u16 out_size = sizeof(rss_cfg); int err = 0, i; err = hinic_global_func_id_get(hwdev, &rss_cfg.func_id); if (err) return err; rss_cfg.template_id = (u8)tmpl_idx; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL, &rss_cfg, sizeof(rss_cfg), &rss_cfg, &out_size); if (err || !out_size || rss_cfg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to get indir table, err: %d, status: 0x%x, out size: 0x%x\n", err, rss_cfg.status, out_size); return -EINVAL; } hinic_be32_to_cpu(rss_cfg.indir, HINIC_RSS_INDIR_SIZE); for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) indir_table[i] = rss_cfg.indir[i]; return 0; } int hinic_rss_cfg(void *hwdev, u8 rss_en, u8 tmpl_idx, u8 tc_num, u8 *prio_tc) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rss_config rss_cfg = {0}; u16 out_size = sizeof(rss_cfg); int err; /* micro code required: number of TC should be power of 2 */ if (!hwdev || !prio_tc || (tc_num & (tc_num - 1))) return -EINVAL; err = hinic_global_func_id_get(hwdev, &rss_cfg.func_id); if (err) return err; rss_cfg.rss_en = rss_en; rss_cfg.template_id = tmpl_idx; rss_cfg.rq_priority_number = tc_num ? (u8)ilog2(tc_num) : 0; memcpy(rss_cfg.prio_tc, prio_tc, HINIC_DCB_UP_MAX); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_RSS_CFG, &rss_cfg, sizeof(rss_cfg), &rss_cfg, &out_size); if (err || !out_size || rss_cfg.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set rss cfg, err: %d, status: 0x%x, out size: 0x%x\n", err, rss_cfg.status, out_size); return -EINVAL; } return 0; } int hinic_get_vport_stats(void *hwdev, struct hinic_vport_stats *stats) { struct hinic_port_stats_info stats_info = {0}; struct hinic_cmd_vport_stats vport_stats = {0}; u16 out_size = sizeof(vport_stats); int err; err = hinic_global_func_id_get(hwdev, &stats_info.func_id); if (err) return err; stats_info.stats_version = HINIC_PORT_STATS_VERSION; stats_info.stats_size = sizeof(vport_stats); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_VPORT_STAT, &stats_info, sizeof(stats_info), &vport_stats, &out_size); if (err || !out_size || vport_stats.status) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get function statistics, err: %d, status: 0x%x, out size: 0x%x\n", err, vport_stats.status, out_size); return -EFAULT; } memcpy(stats, &vport_stats.stats, sizeof(*stats)); return 0; } int hinic_get_phy_port_stats(void *hwdev, struct hinic_phy_port_stats *stats) { struct hinic_port_stats *port_stats; struct hinic_port_stats_info stats_info = {0}; u16 out_size = sizeof(*port_stats); int err; port_stats = kzalloc(sizeof(*port_stats), GFP_KERNEL); if (!port_stats) return -ENOMEM; stats_info.stats_version = HINIC_PORT_STATS_VERSION; stats_info.stats_size = sizeof(*port_stats); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_PORT_STATISTICS, &stats_info, sizeof(stats_info), port_stats, &out_size); if (err || !out_size || port_stats->status) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get port statistics, err: %d, status: 0x%x, out size: 0x%x\n", err, port_stats->status, out_size); err = -EINVAL; goto out; } memcpy(stats, &port_stats->stats, sizeof(*stats)); out: kfree(port_stats); return err; } int hinic_get_mgmt_version(void *hwdev, u8 *mgmt_ver) { struct hinic_hwdev *dev = hwdev; struct hinic_version_info up_ver = {0}; u16 out_size; int err; out_size = sizeof(up_ver); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_MGMT_VERSION, &up_ver, sizeof(up_ver), &up_ver, &out_size); if (err || !out_size || up_ver.status) { nic_err(dev->dev_hdl, "Failed to get mgmt version, err: %d, status: 0x%x, out size: 0x%x\n", err, up_ver.status, out_size); return -EINVAL; } err = snprintf(mgmt_ver, HINIC_MGMT_VERSION_MAX_LEN, "%s", up_ver.ver); if (err <= 0 || err >= HINIC_MGMT_VERSION_MAX_LEN) { nic_err(dev->dev_hdl, "Failed to snprintf fw version, function return(%d) and dest_len(%d)\n", err, HINIC_MGMT_VERSION_MAX_LEN); return -EINVAL; } return 0; } int hinic_get_fw_version(void *hwdev, struct hinic_fw_version *fw_ver) { struct hinic_hwdev *dev = hwdev; struct hinic_version_info ver_info = {0}; u16 out_size = sizeof(ver_info); int err; if (!hwdev || !fw_ver) return -EINVAL; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_MGMT_VERSION, &ver_info, sizeof(ver_info), &ver_info, &out_size); if (err || !out_size || ver_info.status) { nic_err(dev->dev_hdl, "Failed to get mgmt version, err: %d, status: 0x%x, out size: 0x%x\n", err, ver_info.status, out_size); return -EINVAL; } memcpy(fw_ver->mgmt_ver, ver_info.ver, HINIC_FW_VERSION_NAME); out_size = sizeof(ver_info); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_BOOT_VERSION, &ver_info, sizeof(ver_info), &ver_info, &out_size); if (err || !out_size || ver_info.status) { nic_err(dev->dev_hdl, "Failed to get boot versionerr: %d, status: 0x%x, out size: 0x%x\n", err, ver_info.status, out_size); return -EINVAL; } memcpy(fw_ver->boot_ver, ver_info.ver, HINIC_FW_VERSION_NAME); out_size = sizeof(ver_info); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_MICROCODE_VERSION, &ver_info, sizeof(ver_info), &ver_info, &out_size); if (err || !out_size || ver_info.status) { nic_err(dev->dev_hdl, "Failed to get microcode version, err: %d, status: 0x%x, out size: 0x%x\n", err, ver_info.status, out_size); return -EINVAL; } memcpy(fw_ver->microcode_ver, ver_info.ver, HINIC_FW_VERSION_NAME); return 0; } EXPORT_SYMBOL(hinic_get_fw_version); int hinic_rss_template_alloc(void *hwdev, u8 *tmpl_idx) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rss_template_mgmt template_mgmt = {0}; u16 out_size = sizeof(template_mgmt); int err; if (!hwdev || !tmpl_idx) return -EINVAL; err = hinic_global_func_id_get(hwdev, &template_mgmt.func_id); if (err) return err; template_mgmt.cmd = NIC_RSS_CMD_TEMP_ALLOC; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_RSS_TEMP_MGR, &template_mgmt, sizeof(template_mgmt), &template_mgmt, &out_size); if (err || !out_size || template_mgmt.status) { if (template_mgmt.status == HINIC_MGMT_STATUS_ERR_FULL) { nic_warn(nic_hwdev->dev_hdl, "Failed to alloc rss template, table is full\n"); return -ENOSPC; } nic_err(nic_hwdev->dev_hdl, "Failed to alloc rss template, err: %d, status: 0x%x, out size: 0x%x\n", err, template_mgmt.status, out_size); return -EINVAL; } *tmpl_idx = template_mgmt.template_id; return 0; } int hinic_rss_template_free(void *hwdev, u8 tmpl_idx) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_rss_template_mgmt template_mgmt = {0}; u16 out_size = sizeof(template_mgmt); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &template_mgmt.func_id); if (err) return err; template_mgmt.template_id = tmpl_idx; template_mgmt.cmd = NIC_RSS_CMD_TEMP_FREE; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_RSS_TEMP_MGR, &template_mgmt, sizeof(template_mgmt), &template_mgmt, &out_size); if (err || !out_size || template_mgmt.status) { nic_err(nic_hwdev->dev_hdl, "Failed to free rss template, err: %d, status: 0x%x, out size: 0x%x\n", err, template_mgmt.status, out_size); return -EINVAL; } return 0; } int hinic_set_port_funcs_state(void *hwdev, bool enable) { struct hinic_hwdev *dev = (struct hinic_hwdev *)hwdev; struct hinic_port_funcs_state state = {0}; u16 out_size = sizeof(state); int err = 0; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &state.func_id); if (err) return err; state.drop_en = enable ? 0 : 1; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_PORT_FUNCS_STATE, &state, sizeof(state), &state, &out_size); if (err || !out_size || state.status) { nic_err(dev->dev_hdl, "Failed to %s all functions in port, err: %d, status: 0x%x, out size: 0x%x\n", enable ? "enable" : "disable", err, state.status, out_size); return -EINVAL; } return 0; } int hinic_reset_port_link_cfg(void *hwdev) { struct hinic_hwdev *dev = hwdev; struct hinic_reset_link_cfg reset_cfg = {0}; u16 out_size = sizeof(reset_cfg); int err; err = hinic_global_func_id_get(hwdev, &reset_cfg.func_id); if (err) return err; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_RESET_LINK_CFG, &reset_cfg, sizeof(reset_cfg), &reset_cfg, &out_size); if (err || !out_size || reset_cfg.status) { nic_err(dev->dev_hdl, "Failed to reset port link configure, err: %d, status: 0x%x, out size: 0x%x\n", err, reset_cfg.status, out_size); return -EFAULT; } return 0; } int hinic_save_vf_mac(void *hwdev, u16 vf_id, u8 *mac) { struct hinic_nic_io *nic_io; if (!hwdev || !mac) return -EINVAL; nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; memcpy(nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].vf_mac_addr, mac, ETH_ALEN); return 0; } static int hinic_change_vf_mtu_msg_handler(struct hinic_hwdev *hwdev, u16 vf_id, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { int err; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_CHANGE_MTU, buf_in, in_size, buf_out, out_size, 0); if (err) { nic_err(hwdev->dev_hdl, "Failed to set VF %u mtu\n", vf_id); return err; } return 0; } static bool is_ether_addr_zero(const u8 *addr) { return !(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]); } static int hinic_get_vf_mac_msg_handler(struct hinic_nic_io *nic_io, u16 vf, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct vf_data_storage *vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf); struct hinic_port_mac_set *mac_info = buf_out; int err; if (nic_io->hwdev->func_mode == FUNC_MOD_MULTI_BM_SLAVE || nic_io->hwdev->func_mode == FUNC_MOD_MULTI_VM_SLAVE || (hinic_support_ovs(nic_io->hwdev, NULL))) { err = hinic_pf_msg_to_mgmt_sync(nic_io->hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_GET_MAC, buf_in, in_size, buf_out, out_size, 0); if (!err) { if (is_ether_addr_zero(&mac_info->mac[0])) memcpy(mac_info->mac, vf_info->vf_mac_addr, ETH_ALEN); } return err; } memcpy(mac_info->mac, vf_info->vf_mac_addr, ETH_ALEN); mac_info->status = 0; *out_size = sizeof(*mac_info); return 0; } static int hinic_set_vf_mac_msg_handler(struct hinic_nic_io *nic_io, u16 vf, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct vf_data_storage *vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf); struct hinic_port_mac_set *mac_in = buf_in; struct hinic_port_mac_set *mac_out = buf_out; int err; if (vf_info->pf_set_mac && !(vf_info->trust) && is_valid_ether_addr(mac_in->mac)) { nic_warn(nic_io->hwdev->dev_hdl, "PF has already set VF %d MAC address\n", HW_VF_ID_TO_OS(vf)); mac_out->status = HINIC_PF_SET_VF_ALREADY; *out_size = sizeof(*mac_out); return 0; } err = hinic_pf_msg_to_mgmt_sync(nic_io->hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_SET_MAC, buf_in, in_size, buf_out, out_size, 0); if ((err && err != HINIC_DEV_BUSY_ACTIVE_FW && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) || !(*out_size)) { nic_err(nic_io->hwdev->dev_hdl, "Failed to set VF %d MAC address, err: %d, status: 0x%x, out size: 0x%x\n", HW_VF_ID_TO_OS(vf), err, mac_out->status, *out_size); return -EFAULT; } return err; } static int hinic_del_vf_mac_msg_handler(struct hinic_nic_io *nic_io, u16 vf, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct vf_data_storage *vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf); struct hinic_port_mac_set *mac_in = buf_in; struct hinic_port_mac_set *mac_out = buf_out; int err; if (vf_info->pf_set_mac && !(vf_info->trust) && is_valid_ether_addr(mac_in->mac) && !memcmp(vf_info->vf_mac_addr, mac_in->mac, ETH_ALEN)) { nic_warn(nic_io->hwdev->dev_hdl, "PF has already set VF mac\n"); mac_out->status = HINIC_PF_SET_VF_ALREADY; *out_size = sizeof(*mac_out); return 0; } err = hinic_pf_msg_to_mgmt_sync(nic_io->hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_DEL_MAC, buf_in, in_size, buf_out, out_size, 0); if ((err && err != HINIC_DEV_BUSY_ACTIVE_FW && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) || !(*out_size)) { nic_err(nic_io->hwdev->dev_hdl, "Failed to delete VF %d MAC, err: %d, status: 0x%x, out size: 0x%x\n", HW_VF_ID_TO_OS(vf), err, mac_out->status, *out_size); return -EFAULT; } return err; } static int hinic_update_vf_mac_msg_handler(struct hinic_nic_io *nic_io, u16 vf, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { struct vf_data_storage *vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf); struct hinic_port_mac_update *mac_in = buf_in; struct hinic_port_mac_update *mac_out = buf_out; int err; if (!is_valid_ether_addr(mac_in->new_mac)) { nic_err(nic_io->hwdev->dev_hdl, "Update VF MAC is invalid\n"); return -EINVAL; } if (vf_info->pf_set_mac && !(vf_info->trust)) { nic_warn(nic_io->hwdev->dev_hdl, "PF has already set VF mac\n"); mac_out->status = HINIC_PF_SET_VF_ALREADY; *out_size = sizeof(*mac_out); return 0; } err = hinic_pf_msg_to_mgmt_sync(nic_io->hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_UPDATE_MAC, buf_in, in_size, buf_out, out_size, 0); if ((err && err != HINIC_DEV_BUSY_ACTIVE_FW && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) || !(*out_size)) { nic_warn(nic_io->hwdev->dev_hdl, "Failed to update VF %d MAC, err: %d, status: 0x%x, out size: 0x%x\n", HW_VF_ID_TO_OS(vf), err, mac_out->status, *out_size); return -EFAULT; } return err; } /*lint -save -e734*/ static int hinic_set_vf_vlan(struct hinic_hwdev *hwdev, bool add, u16 vid, u8 qos, int vf_id) { struct hinic_vf_vlan_config vf_vlan = {0}; u8 cmd; u16 out_size = sizeof(vf_vlan); int err; /* VLAN 0 is a special case, don't allow it to be removed */ if (!vid && !add) return 0; vf_vlan.func_id = hinic_glb_pf_vf_offset(hwdev) + vf_id; vf_vlan.vlan_id = vid; vf_vlan.qos = qos; if (add) cmd = HINIC_PORT_CMD_SET_VF_VLAN; else cmd = HINIC_PORT_CMD_CLR_VF_VLAN; err = l2nic_msg_to_mgmt_sync(hwdev, cmd, &vf_vlan, sizeof(vf_vlan), &vf_vlan, &out_size); if (err || !out_size || vf_vlan.status) { nic_err(hwdev->dev_hdl, "Failed to set VF %d vlan, err: %d, status: 0x%x, out size: 0x%x\n", HW_VF_ID_TO_OS(vf_id), err, vf_vlan.status, out_size); return -EFAULT; } return 0; } /*lint -restore*/ static int hinic_init_vf_config(struct hinic_hwdev *hwdev, u16 vf_id) { struct vf_data_storage *vf_info; u16 func_id, vlan_id; int err = 0; vf_info = hwdev->nic_io->vf_infos + HW_VF_ID_TO_OS(vf_id); if (vf_info->pf_set_mac) { func_id = hinic_glb_pf_vf_offset(hwdev) + vf_id; if (FW_SUPPORT_MAC_REUSE_FUNC(hwdev)) { vlan_id = vf_info->pf_vlan; if (vlan_id) vlan_id |= HINIC_ADD_VLAN_IN_MAC; } else { vlan_id = 0; } err = hinic_set_mac(hwdev, vf_info->vf_mac_addr, vlan_id, func_id); if (err) { nic_err(hwdev->dev_hdl, "Failed to set VF %d MAC\n", HW_VF_ID_TO_OS(vf_id)); return err; } } if (hinic_vf_info_vlanprio(hwdev, vf_id)) { err = hinic_set_vf_vlan(hwdev, true, vf_info->pf_vlan, vf_info->pf_qos, vf_id); if (err) { nic_err(hwdev->dev_hdl, "Failed to add VF %d VLAN_QOS\n", HW_VF_ID_TO_OS(vf_id)); return err; } } if (vf_info->max_rate) { err = hinic_set_vf_tx_rate(hwdev, vf_id, vf_info->max_rate, vf_info->min_rate); if (err) { nic_err(hwdev->dev_hdl, "Failed to set VF %d max rate %d, min rate %d\n", HW_VF_ID_TO_OS(vf_id), vf_info->max_rate, vf_info->min_rate); return err; } } return 0; } static int hinic_register_vf_msg_handler(void *hwdev, u16 vf_id, void *buf_out, u16 *out_size) { struct hinic_hwdev *hw_dev = hwdev; struct hinic_nic_io *nic_io = hw_dev->nic_io; struct hinic_register_vf *register_info = buf_out; int err; if (vf_id > nic_io->max_vfs) { nic_err(hw_dev->dev_hdl, "Register VF id %d exceed limit[0-%d]\n", HW_VF_ID_TO_OS(vf_id), HW_VF_ID_TO_OS(nic_io->max_vfs)); register_info->status = EFAULT; return -EFAULT; } *out_size = sizeof(*register_info); err = hinic_init_vf_config(hw_dev, vf_id); if (err) { register_info->status = EFAULT; return err; } nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].registered = true; return 0; } void hinic_unregister_vf_msg_handler(void *hwdev, u16 vf_id) { struct hinic_hwdev *hw_dev = (struct hinic_hwdev *)hwdev; struct hinic_nic_io *nic_io = hw_dev->nic_io; if (vf_id > nic_io->max_vfs) return; nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].registered = false; } static void hinic_get_vf_link_status_msg_handler(struct hinic_nic_io *nic_io, u16 vf_id, void *buf_out, u16 *out_size) { struct vf_data_storage *vf_infos = nic_io->vf_infos; struct hinic_get_link *get_link = buf_out; bool link_forced, link_up; link_forced = vf_infos[HW_VF_ID_TO_OS(vf_id)].link_forced; link_up = vf_infos[HW_VF_ID_TO_OS(vf_id)].link_up; if (link_forced) get_link->link_status = link_up ? HINIC_LINK_UP : HINIC_LINK_DOWN; else get_link->link_status = nic_io->link_status; get_link->status = 0; *out_size = sizeof(*get_link); } static void hinic_get_vf_cos_msg_handler(struct hinic_nic_io *nic_io, u16 vf_id, void *buf_out, u16 *out_size) { struct hinic_vf_dcb_state *dcb_state = buf_out; memcpy(&dcb_state->state, &nic_io->dcb_state, sizeof(nic_io->dcb_state)); dcb_state->status = 0; *out_size = sizeof(*dcb_state); } /* pf receive message from vf */ int nic_pf_mbox_handler(void *hwdev, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { u8 size = ARRAY_SIZE(nic_cmd_support_vf); struct hinic_nic_io *nic_io; int err = 0; u32 timeout = 0; if (!hwdev) return -EFAULT; if (!hinic_mbox_check_cmd_valid(hwdev, nic_cmd_support_vf, vf_id, cmd, buf_in, in_size, size)) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "PF Receive VF nic cmd(0x%x) or mbox len(0x%x) is invalid\n", cmd, in_size); err = HINIC_MBOX_VF_CMD_ERROR; return err; } nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; switch (cmd) { case HINIC_PORT_CMD_VF_REGISTER: err = hinic_register_vf_msg_handler(hwdev, vf_id, buf_out, out_size); break; case HINIC_PORT_CMD_VF_UNREGISTER: *out_size = 0; hinic_unregister_vf_msg_handler(hwdev, vf_id); break; case HINIC_PORT_CMD_CHANGE_MTU: err = hinic_change_vf_mtu_msg_handler(hwdev, vf_id, buf_in, in_size, buf_out, out_size); break; case HINIC_PORT_CMD_GET_MAC: hinic_get_vf_mac_msg_handler(nic_io, vf_id, buf_in, in_size, buf_out, out_size); break; case HINIC_PORT_CMD_SET_MAC: err = hinic_set_vf_mac_msg_handler(nic_io, vf_id, buf_in, in_size, buf_out, out_size); break; case HINIC_PORT_CMD_DEL_MAC: err = hinic_del_vf_mac_msg_handler(nic_io, vf_id, buf_in, in_size, buf_out, out_size); break; case HINIC_PORT_CMD_UPDATE_MAC: err = hinic_update_vf_mac_msg_handler(nic_io, vf_id, buf_in, in_size, buf_out, out_size); break; case HINIC_PORT_CMD_GET_LINK_STATE: hinic_get_vf_link_status_msg_handler(nic_io, vf_id, buf_out, out_size); break; case HINIC_PORT_CMD_GET_VF_COS: hinic_get_vf_cos_msg_handler(nic_io, vf_id, buf_out, out_size); break; default: /* pass through */ if (cmd == HINIC_PORT_CMD_SET_VPORT_ENABLE) timeout = SET_VPORT_MGMT_TIMEOUT; err = hinic_pf_msg_to_mgmt_sync(nic_io->hwdev, HINIC_MOD_L2NIC, cmd, buf_in, in_size, buf_out, out_size, timeout); break; } if (err && err != HINIC_DEV_BUSY_ACTIVE_FW && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) nic_err(nic_io->hwdev->dev_hdl, "PF receive VF L2NIC cmd: %d process error, err: %d\n", cmd, err); return err; } static int hinic_init_vf_infos(struct hinic_nic_io *nic_io, u16 vf_id) { struct vf_data_storage *vf_infos = nic_io->vf_infos; u8 vf_link_state; if (set_vf_link_state > HINIC_IFLA_VF_LINK_STATE_DISABLE) { nic_warn(nic_io->hwdev->dev_hdl, "Module Parameter set_vf_link_state value %d is out of range, resetting to %d\n", set_vf_link_state, HINIC_IFLA_VF_LINK_STATE_AUTO); set_vf_link_state = HINIC_IFLA_VF_LINK_STATE_AUTO; } vf_link_state = hinic_support_ovs(nic_io->hwdev, NULL) ? HINIC_IFLA_VF_LINK_STATE_ENABLE : set_vf_link_state; if (FUNC_FORCE_LINK_UP(nic_io->hwdev)) vf_link_state = HINIC_IFLA_VF_LINK_STATE_ENABLE; switch (vf_link_state) { case HINIC_IFLA_VF_LINK_STATE_AUTO: vf_infos[vf_id].link_forced = false; break; case HINIC_IFLA_VF_LINK_STATE_ENABLE: vf_infos[vf_id].link_forced = true; vf_infos[vf_id].link_up = true; break; case HINIC_IFLA_VF_LINK_STATE_DISABLE: vf_infos[vf_id].link_forced = true; vf_infos[vf_id].link_up = false; break; default: nic_err(nic_io->hwdev->dev_hdl, "Input parameter set_vf_link_state error: %d\n", vf_link_state); return -EINVAL; } return 0; } int hinic_init_vf_hw(void *hwdev, u16 start_vf_id, u16 end_vf_id) { u16 i, func_idx; int err; /* vf use 256K as default wq page size, and can't change it */ for (i = start_vf_id; i <= end_vf_id; i++) { func_idx = hinic_glb_pf_vf_offset(hwdev) + i; err = hinic_set_wq_page_size(hwdev, func_idx, HINIC_DEFAULT_WQ_PAGE_SIZE); if (err) return err; } return 0; } int hinic_deinit_vf_hw(void *hwdev, u16 start_vf_id, u16 end_vf_id) { u16 func_idx, idx; for (idx = start_vf_id; idx <= end_vf_id; idx++) { func_idx = hinic_glb_pf_vf_offset(hwdev) + idx; hinic_set_wq_page_size(hwdev, func_idx, HINIC_HW_WQ_PAGE_SIZE); hinic_clear_vf_infos(hwdev, idx); } return 0; } int hinic_vf_func_init(struct hinic_hwdev *hwdev) { struct hinic_nic_io *nic_io; int err = 0; struct hinic_register_vf register_info = {0}; u32 size; u16 i, out_size = sizeof(register_info); hwdev->nic_io = kzalloc(sizeof(*hwdev->nic_io), GFP_KERNEL); if (!hwdev->nic_io) return -ENOMEM; nic_io = hwdev->nic_io; nic_io->hwdev = hwdev; sema_init(&nic_io->nic_cfg.cfg_lock, 1); if (hinic_func_type(hwdev) == TYPE_VF) { err = hinic_mbox_to_pf(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_VF_REGISTER, ®ister_info, sizeof(register_info), ®ister_info, &out_size, 0); if (err || register_info.status || !out_size) { nic_err(hwdev->dev_hdl, "Failed to register VF, err: %d, status: 0x%x, out size: 0x%x\n", err, register_info.status, out_size); hinic_unregister_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC); err = -EIO; goto out_free_nic_io; } } else { nic_io->max_vfs = hinic_func_max_vf(hwdev); size = sizeof(*nic_io->vf_infos) * nic_io->max_vfs; if (size != 0) { nic_io->vf_infos = kzalloc(size, GFP_KERNEL); if (!nic_io->vf_infos) { err = -ENOMEM; goto out_free_nic_io; } for (i = 0; i < nic_io->max_vfs; i++) { err = hinic_init_vf_infos(nic_io, i); if (err) goto init_vf_infos_err; } err = hinic_register_pf_mbox_cb(hwdev, HINIC_MOD_L2NIC, nic_pf_mbox_handler); if (err) goto register_pf_mbox_cb_err; } } return 0; register_pf_mbox_cb_err: init_vf_infos_err: kfree(nic_io->vf_infos); out_free_nic_io: kfree(hwdev->nic_io); hwdev->nic_io = NULL; return err; } void hinic_vf_func_free(struct hinic_hwdev *hwdev) { struct hinic_register_vf unregister = {0}; u16 out_size = sizeof(unregister); int err; if (hinic_func_type(hwdev) == TYPE_VF) { err = hinic_mbox_to_pf(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_VF_UNREGISTER, &unregister, sizeof(unregister), &unregister, &out_size, 0); if (err || !out_size || unregister.status) nic_err(hwdev->dev_hdl, "Failed to unregister VF, err: %d, status: 0x%x, out_size: 0x%x\n", err, unregister.status, out_size); } else { if (hwdev->nic_io->vf_infos) { hinic_unregister_pf_mbox_cb(hwdev, HINIC_MOD_L2NIC); kfree(hwdev->nic_io->vf_infos); } } kfree(hwdev->nic_io); hwdev->nic_io = NULL; } /*lint -save -e734*/ /* this function just be called by hinic_ndo_set_vf_mac, others are * not permitted */ int hinic_set_vf_mac(void *hwdev, int vf, unsigned char *mac_addr) { struct hinic_hwdev *hw_dev = (struct hinic_hwdev *)hwdev; struct hinic_nic_io *nic_io = hw_dev->nic_io; struct vf_data_storage *vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf); int del_vf_mac = is_zero_ether_addr(mac_addr); u16 func_id; int err; /* duplicate request, so just return success */ if (!memcmp(vf_info->vf_mac_addr, mac_addr, ETH_ALEN)) return 0; func_id = hinic_glb_pf_vf_offset(hw_dev) + vf; if (del_vf_mac) err = hinic_del_mac(hwdev, vf_info->vf_mac_addr, 0, func_id); else err = hinic_update_mac(hw_dev, vf_info->vf_mac_addr, mac_addr, 0, func_id); if (err) return err; memcpy(vf_info->vf_mac_addr, mac_addr, ETH_ALEN); vf_info->pf_set_mac = !del_vf_mac; return 0; } int hinic_add_vf_vlan(void *hwdev, int vf_id, u16 vlan, u8 qos) { struct hinic_hwdev *hw_dev = (struct hinic_hwdev *)hwdev; struct hinic_nic_io *nic_io = hw_dev->nic_io; int err; err = hinic_set_vf_vlan(hw_dev, true, vlan, qos, vf_id); if (err) return err; nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan = vlan; nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_qos = qos; nic_info(hw_dev->dev_hdl, "Setting VLAN %d, QOS 0x%x on VF %d\n", vlan, qos, HW_VF_ID_TO_OS(vf_id)); return 0; } int hinic_kill_vf_vlan(void *hwdev, int vf_id) { struct hinic_hwdev *hw_dev = (struct hinic_hwdev *)hwdev; struct hinic_nic_io *nic_io = hw_dev->nic_io; int err; err = hinic_set_vf_vlan(hw_dev, false, nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan, nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_qos, vf_id); if (err) return err; nic_info(hw_dev->dev_hdl, "Remove VLAN %d on VF %d\n", nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan, HW_VF_ID_TO_OS(vf_id)); nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan = 0; nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_qos = 0; return 0; } u16 hinic_vf_info_vlanprio(void *hwdev, int vf_id) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; u16 pf_vlan = nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan; u8 pf_qos = nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_qos; u16 vlanprio = pf_vlan | pf_qos << HINIC_VLAN_PRIORITY_SHIFT; return vlanprio; } /*lint -restore*/ bool hinic_vf_is_registered(void *hwdev, u16 vf_id) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; return nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].registered; } void hinic_get_vf_config(void *hwdev, u16 vf_id, struct ifla_vf_info *ivi) { struct hinic_hwdev *hw_dev = (struct hinic_hwdev *)hwdev; struct vf_data_storage *vfinfo; vfinfo = hw_dev->nic_io->vf_infos + HW_VF_ID_TO_OS(vf_id); ivi->vf = HW_VF_ID_TO_OS(vf_id); memcpy(ivi->mac, vfinfo->vf_mac_addr, ETH_ALEN); ivi->vlan = vfinfo->pf_vlan; ivi->qos = vfinfo->pf_qos; ivi->spoofchk = vfinfo->spoofchk; ivi->trusted = vfinfo->trust; ivi->max_tx_rate = vfinfo->max_rate; ivi->min_tx_rate = vfinfo->min_rate; if (!vfinfo->link_forced) ivi->linkstate = IFLA_VF_LINK_STATE_AUTO; else if (vfinfo->link_up) ivi->linkstate = IFLA_VF_LINK_STATE_ENABLE; else ivi->linkstate = IFLA_VF_LINK_STATE_DISABLE; } void hinic_clear_vf_infos(void *hwdev, u16 vf_id) { struct hinic_hwdev *hw_dev = (struct hinic_hwdev *)hwdev; struct vf_data_storage *vf_infos; u16 func_id; func_id = hinic_glb_pf_vf_offset(hwdev) + vf_id; vf_infos = hw_dev->nic_io->vf_infos + HW_VF_ID_TO_OS(vf_id); if (vf_infos->pf_set_mac) hinic_del_mac(hwdev, vf_infos->vf_mac_addr, 0, func_id); if (hinic_vf_info_vlanprio(hwdev, vf_id)) hinic_kill_vf_vlan(hwdev, vf_id); if (vf_infos->max_rate) hinic_set_vf_tx_rate(hwdev, vf_id, 0, 0); if (vf_infos->spoofchk) hinic_set_vf_spoofchk(hwdev, vf_id, false); if (vf_infos->trust) hinic_set_vf_trust(hwdev, vf_id, false); memset(vf_infos, 0, sizeof(*vf_infos)); /* set vf_infos to default */ hinic_init_vf_infos(hw_dev->nic_io, HW_VF_ID_TO_OS(vf_id)); } static void hinic_notify_vf_link_status(struct hinic_hwdev *hwdev, u16 vf_id, u8 link_status) { struct hinic_port_link_status link = {0}; struct vf_data_storage *vf_infos = hwdev->nic_io->vf_infos; u16 out_size = sizeof(link); int err; if (vf_infos[HW_VF_ID_TO_OS(vf_id)].registered) { link.link = link_status; link.func_id = hinic_glb_pf_vf_offset(hwdev) + vf_id; err = hinic_mbox_to_vf(hwdev, HINIC_MOD_L2NIC, vf_id, HINIC_PORT_CMD_LINK_STATUS_REPORT, &link, sizeof(link), &link, &out_size, 0); if (err || !out_size || link.status) nic_err(hwdev->dev_hdl, "Send link change event to VF %d failed, err: %d, status: 0x%x, out_size: 0x%x\n", HW_VF_ID_TO_OS(vf_id), err, link.status, out_size); } } /* send link change event mbox msg to active vfs under the pf */ void hinic_notify_all_vfs_link_changed(void *hwdev, u8 link_status) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; u16 i; nic_io->link_status = link_status; for (i = 1; i <= nic_io->max_vfs; i++) { if (!nic_io->vf_infos[HW_VF_ID_TO_OS(i)].link_forced) hinic_notify_vf_link_status(nic_io->hwdev, i, link_status); } } void hinic_save_pf_link_status(void *hwdev, u8 link_status) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; nic_io->link_status = link_status; } int hinic_set_vf_link_state(void *hwdev, u16 vf_id, int link) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct vf_data_storage *vf_infos = nic_io->vf_infos; u8 link_status = 0; switch (link) { case HINIC_IFLA_VF_LINK_STATE_AUTO: vf_infos[HW_VF_ID_TO_OS(vf_id)].link_forced = false; vf_infos[HW_VF_ID_TO_OS(vf_id)].link_up = nic_io->link_status ? true : false; link_status = nic_io->link_status; break; case HINIC_IFLA_VF_LINK_STATE_ENABLE: vf_infos[HW_VF_ID_TO_OS(vf_id)].link_forced = true; vf_infos[HW_VF_ID_TO_OS(vf_id)].link_up = true; link_status = HINIC_LINK_UP; break; case HINIC_IFLA_VF_LINK_STATE_DISABLE: vf_infos[HW_VF_ID_TO_OS(vf_id)].link_forced = true; vf_infos[HW_VF_ID_TO_OS(vf_id)].link_up = false; link_status = HINIC_LINK_DOWN; break; default: return -EINVAL; } /* Notify the VF of its new link state */ hinic_notify_vf_link_status(hwdev, vf_id, link_status); return 0; } int hinic_set_vf_spoofchk(void *hwdev, u16 vf_id, bool spoofchk) { struct hinic_hwdev *hw_dev = hwdev; struct hinic_nic_io *nic_io = NULL; struct hinic_spoofchk_set spoofchk_cfg = {0}; struct vf_data_storage *vf_infos = NULL; u16 out_size = sizeof(spoofchk_cfg); int err = 0; if (!hwdev) return -EINVAL; nic_io = hw_dev->nic_io; vf_infos = nic_io->vf_infos; spoofchk_cfg.func_id = hinic_glb_pf_vf_offset(hwdev) + vf_id; spoofchk_cfg.state = spoofchk ? 1 : 0; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_ENABLE_SPOOFCHK, &spoofchk_cfg, sizeof(spoofchk_cfg), &spoofchk_cfg, &out_size, 0); if (spoofchk_cfg.status == HINIC_MGMT_CMD_UNSUPPORTED) { err = HINIC_MGMT_CMD_UNSUPPORTED; } else if (err || !out_size || spoofchk_cfg.status) { nic_err(hw_dev->dev_hdl, "Failed to set VF(%d) spoofchk, err: %d, status: 0x%x, out size: 0x%x\n", HW_VF_ID_TO_OS(vf_id), err, spoofchk_cfg.status, out_size); err = -EINVAL; } vf_infos[HW_VF_ID_TO_OS(vf_id)].spoofchk = spoofchk; return err; } int hinic_set_vf_trust(void *hwdev, u16 vf_id, bool trust) { struct hinic_hwdev *hw_dev = hwdev; struct hinic_nic_io *nic_io = NULL; struct vf_data_storage *vf_infos = NULL; if (!hwdev) return -EINVAL; nic_io = hw_dev->nic_io; vf_infos = nic_io->vf_infos; vf_infos[HW_VF_ID_TO_OS(vf_id)].trust = trust; return 0; } bool hinic_vf_info_spoofchk(void *hwdev, int vf_id) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; bool spoofchk = nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].spoofchk; return spoofchk; } bool hinic_vf_info_trust(void *hwdev, int vf_id) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; bool trust = nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].trust; return trust; } static int hinic_set_vf_rate_limit(void *hwdev, u16 vf_id, u32 tx_rate) { struct hinic_hwdev *hw_dev = hwdev; struct hinic_nic_io *nic_io = hw_dev->nic_io; struct hinic_tx_rate_cfg rate_cfg = {0}; u16 out_size = sizeof(rate_cfg); int err; rate_cfg.func_id = hinic_glb_pf_vf_offset(hwdev) + vf_id; rate_cfg.tx_rate = tx_rate; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_SET_VF_RATE, &rate_cfg, sizeof(rate_cfg), &rate_cfg, &out_size, 0); if (err || !out_size || rate_cfg.status) { nic_err(hw_dev->dev_hdl, "Failed to set VF(%d) rate(%d), err: %d, status: 0x%x, out size: 0x%x\n", HW_VF_ID_TO_OS(vf_id), tx_rate, err, rate_cfg.status, out_size); if (rate_cfg.status) return rate_cfg.status; return -EIO; } nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].max_rate = tx_rate; nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].min_rate = 0; return 0; } static int hinic_set_vf_tx_rate_max_min(void *hwdev, u16 vf_id, u32 max_rate, u32 min_rate) { struct hinic_hwdev *hw_dev = hwdev; struct hinic_nic_io *nic_io = hw_dev->nic_io; struct hinic_tx_rate_cfg_max_min rate_cfg = {0}; u16 out_size = sizeof(rate_cfg); int err; rate_cfg.func_id = hinic_glb_pf_vf_offset(hwdev) + vf_id; rate_cfg.max_rate = max_rate; rate_cfg.min_rate = min_rate; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE, &rate_cfg, sizeof(rate_cfg), &rate_cfg, &out_size, 0); if ((rate_cfg.status != HINIC_MGMT_CMD_UNSUPPORTED && rate_cfg.status) || err || !out_size) { nic_err(hw_dev->dev_hdl, "Failed to set VF(%d) max rate(%d), min rate(%d), err: %d, status: 0x%x, out size: 0x%x\n", HW_VF_ID_TO_OS(vf_id), max_rate, min_rate, err, rate_cfg.status, out_size); return -EIO; } if (!rate_cfg.status) { nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].max_rate = max_rate; nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].min_rate = min_rate; } return rate_cfg.status; } int hinic_set_vf_tx_rate(void *hwdev, u16 vf_id, u32 max_rate, u32 min_rate) { struct hinic_hwdev *hw_dev = hwdev; int err; err = hinic_set_vf_tx_rate_max_min(hwdev, vf_id, max_rate, min_rate); if (err != HINIC_MGMT_CMD_UNSUPPORTED) return err; if (min_rate) { nic_err(hw_dev->dev_hdl, "Current firmware don't support to set min tx rate\n"); return -EINVAL; } nic_info(hw_dev->dev_hdl, "Current firmware don't support to set min tx rate, force min_tx_rate = max_tx_rate\n"); return hinic_set_vf_rate_limit(hwdev, vf_id, max_rate); } int hinic_set_dcb_state(void *hwdev, struct hinic_dcb_state *dcb_state) { struct hinic_hwdev *hw_dev = hwdev; struct hinic_nic_io *nic_io; struct vf_data_storage *vf_infos; struct hinic_vf_dcb_state vf_dcb = {0}; u16 vf_id, out_size = 0; int err; if (!hwdev || !dcb_state || !hw_dev->nic_io) return -EINVAL; nic_io = hw_dev->nic_io; if (!memcmp(&nic_io->dcb_state, dcb_state, sizeof(nic_io->dcb_state))) return 0; memcpy(&vf_dcb.state, dcb_state, sizeof(vf_dcb.state)); /* save in sdk, vf will get dcb state when probing */ hinic_save_dcb_state(hwdev, dcb_state); /* notify statefull in pf, than notify all vf */ hinic_notify_dcb_state_event(hwdev, dcb_state); /* not vf supported, don't need to notify vf */ if (!nic_io->vf_infos) return 0; vf_infos = nic_io->vf_infos; for (vf_id = 0; vf_id < nic_io->max_vfs; vf_id++) { if (vf_infos[vf_id].registered) { vf_dcb.status = 0; out_size = sizeof(vf_dcb); err = hinic_mbox_to_vf(hwdev, HINIC_MOD_L2NIC, OS_VF_ID_TO_HW(vf_id), HINIC_PORT_CMD_SET_VF_COS, &vf_dcb, sizeof(vf_dcb), &vf_dcb, &out_size, 0); if (err || vf_dcb.status || !out_size) nic_err(hw_dev->dev_hdl, "Failed to notify dcb state to VF %d, err: %d, status: 0x%x, out size: 0x%x\n", vf_id, err, vf_dcb.status, out_size); } } return 0; } int hinic_get_dcb_state(void *hwdev, struct hinic_dcb_state *dcb_state) { struct hinic_hwdev *hw_dev = hwdev; struct hinic_nic_io *nic_io; if (!hwdev || !dcb_state) return -EINVAL; nic_io = hw_dev->nic_io; memcpy(dcb_state, &nic_io->dcb_state, sizeof(*dcb_state)); return 0; } EXPORT_SYMBOL(hinic_get_dcb_state); int hinic_save_dcb_state(struct hinic_hwdev *hwdev, struct hinic_dcb_state *dcb_state) { struct hinic_nic_io *nic_io; if (!hwdev || !dcb_state) return -EINVAL; if (!hwdev->nic_io) return -EINVAL; nic_io = hwdev->nic_io; memcpy(&nic_io->dcb_state, dcb_state, sizeof(*dcb_state)); return 0; } int hinic_get_pf_dcb_state(void *hwdev, struct hinic_dcb_state *dcb_state) { struct hinic_hwdev *hw_dev = hwdev; struct hinic_vf_dcb_state vf_dcb = {0}; u16 out_size = sizeof(vf_dcb); int err; if (!hwdev || !dcb_state) return -EINVAL; if (hinic_func_type(hwdev) != TYPE_VF) { nic_err(hw_dev->dev_hdl, "Only vf need to get pf dcb state\n"); return -EINVAL; } err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_GET_VF_COS, &vf_dcb, sizeof(vf_dcb), &vf_dcb, &out_size, 0); if (err || !out_size || vf_dcb.status) { nic_err(hw_dev->dev_hdl, "Failed to get vf default cos, err: %d, status: 0x%x, out size: 0x%x\n", err, vf_dcb.status, out_size); return -EFAULT; } memcpy(dcb_state, &vf_dcb.state, sizeof(*dcb_state)); /* Save dcb_state in hw for statefull module */ hinic_save_dcb_state(hwdev, dcb_state); return 0; } EXPORT_SYMBOL(hinic_get_pf_dcb_state); int hinic_set_ipsu_mac(void *hwdev, u16 index, u8 *mac_addr, u16 vlan_id, u16 func_id) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_port_ipsu_mac mac_info = {0}; u16 out_size = sizeof(mac_info); int err; if (!hwdev || !mac_addr) return -EINVAL; mac_info.index = index; mac_info.func_id = func_id; mac_info.vlan_id = vlan_id; memcpy(mac_info.mac, mac_addr, ETH_ALEN); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_IPSU_MAC, &mac_info, sizeof(mac_info), &mac_info, &out_size); if (err || !out_size || mac_info.status) { nic_err(nic_hwdev->dev_hdl, "Failed to set IPSU MAC(index %d), err: %d, status: 0x%x, out size: 0x%x\n", index, err, mac_info.status, out_size); return -EINVAL; } return 0; } int hinic_get_ipsu_mac(void *hwdev, u16 index, u8 *mac_addr, u16 *vlan_id, u16 *func_id) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_port_ipsu_mac mac_info = {0}; u16 out_size = sizeof(mac_info); int err; if (!hwdev || !mac_addr) return -EINVAL; mac_info.index = index; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_IPSU_MAC, &mac_info, sizeof(mac_info), &mac_info, &out_size); if (err || !out_size || mac_info.status) { nic_err(nic_hwdev->dev_hdl, "Failed to get IPSU MAC(index %d), err: %d, status: 0x%x, out size: 0x%x\n", index, err, mac_info.status, out_size); return -EINVAL; } *func_id = mac_info.func_id; *vlan_id = mac_info.vlan_id; memcpy(mac_addr, mac_info.mac, ETH_ALEN); return 0; } int hinic_set_anti_attack(void *hwdev, bool enable) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_port_anti_attack_rate rate = {0}; u16 out_size = sizeof(rate); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &rate.func_id); if (err) return err; rate.enable = enable; rate.cir = ANTI_ATTACK_DEFAULT_CIR; rate.xir = ANTI_ATTACK_DEFAULT_XIR; rate.cbs = ANTI_ATTACK_DEFAULT_CBS; rate.xbs = ANTI_ATTACK_DEFAULT_XBS; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_ANTI_ATTACK_RATE, &rate, sizeof(rate), &rate, &out_size); if (err || !out_size || rate.status) { nic_err(nic_hwdev->dev_hdl, "Can't %s port Anti-Attack rate limit err: %d, status: 0x%x, out size: 0x%x\n", (enable ? "enable" : "disable"), err, rate.status, out_size); return -EINVAL; } nic_info(nic_hwdev->dev_hdl, "%s port Anti-Attack rate limit succeed\n", (enable ? "Enable" : "Disable")); return 0; } int hinic_flush_sq_res(void *hwdev) { struct hinic_hwdev *dev = hwdev; struct hinic_clear_sq_resource sq_res = {0}; u16 out_size = sizeof(sq_res); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &sq_res.func_id); if (err) return err; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_CLEAR_SQ_RES, &sq_res, sizeof(sq_res), &sq_res, &out_size); if (err || !out_size || sq_res.status) { nic_err(dev->dev_hdl, "Failed to clear sq resources, err: %d, status: 0x%x, out size: 0x%x\n", err, sq_res.status, out_size); return -EINVAL; } return 0; } EXPORT_SYMBOL(hinic_flush_sq_res); static int __set_pf_bw(struct hinic_hwdev *hwdev, u8 speed_level); int hinic_refresh_nic_cfg(void *hwdev, struct nic_port_info *port_info) { struct hinic_hwdev *dev = hwdev; struct hinic_nic_cfg *nic_cfg = &dev->nic_io->nic_cfg; int err = 0; down(&nic_cfg->cfg_lock); /* Enable PFC will disable pause */ if (nic_cfg->pfc_en) { err = hinic_dcb_set_hw_pfc(hwdev, nic_cfg->pfc_en, nic_cfg->pfc_bitmap); if (err) nic_err(dev->dev_hdl, "Failed to set pfc\n"); } else if (!port_info->autoneg_state || nic_cfg->pause_set) { nic_cfg->nic_pause.auto_neg = port_info->autoneg_state; err = hinic_set_hw_pause_info(hwdev, nic_cfg->nic_pause); if (err) nic_err(dev->dev_hdl, "Failed to set pause\n"); } if (FUNC_SUPPORT_RATE_LIMIT(hwdev)) { err = __set_pf_bw(hwdev, port_info->speed); if (err) nic_err(dev->dev_hdl, "Failed to set pf bandwidth limit\n"); } up(&nic_cfg->cfg_lock); return err; } int hinic_set_super_cqe_state(void *hwdev, bool enable) { struct hinic_hwdev *nic_hwdev = (struct hinic_hwdev *)hwdev; struct hinic_super_cqe super_cqe = {0}; u16 out_size = sizeof(super_cqe); int err; if (!hwdev) return -EINVAL; err = hinic_global_func_id_get(hwdev, &super_cqe.func_id); if (err) return err; super_cqe.super_cqe_en = enable; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_SUPER_CQE, &super_cqe, sizeof(super_cqe), &super_cqe, &out_size); if (err || !out_size || super_cqe.status) { nic_err(nic_hwdev->dev_hdl, "Can't %s surper cqe, err: %d, status: 0x%x, out size: 0x%x\n", (enable ? "enable" : "disable"), err, super_cqe.status, out_size); return -EINVAL; } nic_info(nic_hwdev->dev_hdl, "%s super cqe succeed\n", (enable ? "Enable" : "Disable")); return 0; } int hinic_set_port_routine_cmd_report(void *hwdev, bool enable) { struct hinic_port_rt_cmd rt_cmd = { 0 }; struct hinic_hwdev *dev = hwdev; u16 out_size = sizeof(rt_cmd); int err; if (!hwdev) return -EINVAL; rt_cmd.pf_id = (u8)hinic_global_func_id(hwdev); rt_cmd.enable = enable; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_PORT_REPORT, &rt_cmd, sizeof(rt_cmd), &rt_cmd, &out_size); if (rt_cmd.status == HINIC_MGMT_CMD_UNSUPPORTED) { nic_info(dev->dev_hdl, "Current firmware doesn't support to set port routine command report\n"); } else if (rt_cmd.status || err || !out_size) { nic_err(dev->dev_hdl, "Failed to set port routine command report, err: %d, status: 0x%x, out size: 0x%x\n", err, rt_cmd.status, out_size); return -EFAULT; } return 0; } int hinic_set_func_capture_en(void *hwdev, u16 func_id, bool cap_en) { struct hinic_hwdev *dev = hwdev; struct hinic_capture_info cap_info = {0}; u16 out_size = sizeof(cap_info); int err; if (!hwdev) return -EINVAL; cap_info.op_type = 2; /* function capture */ cap_info.is_en_trx = cap_en; cap_info.func_id = func_id; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_UCAPTURE_OPT, &cap_info, sizeof(cap_info), &cap_info, &out_size); if (err || !out_size || cap_info.status) { nic_err(dev->dev_hdl, "Failed to set function capture attr, err: %d, status: 0x%x, out size: 0x%x\n", err, cap_info.status, out_size); return -EINVAL; } return 0; } EXPORT_SYMBOL(hinic_set_func_capture_en); int hinic_force_drop_tx_pkt(void *hwdev) { struct hinic_hwdev *dev = hwdev; struct hinic_force_pkt_drop pkt_drop = {0}; u16 out_size = sizeof(pkt_drop); int err; if (!hwdev) return -EINVAL; pkt_drop.port = hinic_physical_port_id(hwdev); err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_FORCE_PKT_DROP, &pkt_drop, sizeof(pkt_drop), &pkt_drop, &out_size); if ((pkt_drop.status != HINIC_MGMT_CMD_UNSUPPORTED && pkt_drop.status) || err || !out_size) { nic_err(dev->dev_hdl, "Failed to set force tx packets drop, err: %d, status: 0x%x, out size: 0x%x\n", err, pkt_drop.status, out_size); return -EFAULT; } return pkt_drop.status; } u32 hw_speed_convert[LINK_SPEED_LEVELS] = { 10, 100, 1000, 10000, 25000, 40000, 100000 }; static int __set_pf_bw(struct hinic_hwdev *hwdev, u8 speed_level) { struct hinic_nic_cfg *nic_cfg = &hwdev->nic_io->nic_cfg; struct hinic_tx_rate_cfg rate_cfg = {0}; u32 pf_bw = 0; u16 out_size = sizeof(rate_cfg); int err; if (speed_level >= LINK_SPEED_LEVELS) { nic_err(hwdev->dev_hdl, "Invalid speed level: %d\n", speed_level); return -EINVAL; } if (nic_cfg->pf_bw_limit == 100) { pf_bw = 0; /* unlimit bandwidth */ } else { pf_bw = (hw_speed_convert[speed_level] / 100) * nic_cfg->pf_bw_limit; /* bandwidth limit is very small but not unlimit in this case */ if (pf_bw == 0) pf_bw = 1; } err = hinic_global_func_id_get(hwdev, &rate_cfg.func_id); if (err) return err; rate_cfg.tx_rate = pf_bw; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_SET_VF_RATE, &rate_cfg, sizeof(rate_cfg), &rate_cfg, &out_size, 0); if (err || !out_size || rate_cfg.status) { nic_err(hwdev->dev_hdl, "Failed to set rate(%d), err: %d, status: 0x%x, out size: 0x%x\n", pf_bw, err, rate_cfg.status, out_size); if (rate_cfg.status) return rate_cfg.status; return -EIO; } return 0; } int hinic_update_pf_bw(void *hwdev) { struct hinic_hwdev *dev = hwdev; struct nic_port_info port_info = {0}; int err; if (hinic_func_type(hwdev) == TYPE_VF || !(FUNC_SUPPORT_RATE_LIMIT(hwdev))) return 0; err = hinic_get_port_info(hwdev, &port_info); if (err) { nic_err(dev->dev_hdl, "Failed to get port info\n"); return -EIO; } err = __set_pf_bw(hwdev, port_info.speed); if (err) { nic_err(dev->dev_hdl, "Failed to set pf bandwidth\n"); return err; } return 0; } int hinic_set_pf_bw_limit(void *hwdev, u32 bw_limit) { struct hinic_hwdev *dev = hwdev; struct hinic_nic_cfg *nic_cfg; u32 old_bw_limit; u8 link_state = 0; int err; if (!hwdev) return -EINVAL; if (hinic_func_type(hwdev) == TYPE_VF) return 0; if (bw_limit > 100) { nic_err(dev->dev_hdl, "Invalid bandwidth: %d\n", bw_limit); return -EINVAL; } err = hinic_get_link_state(hwdev, &link_state); if (err) { nic_err(dev->dev_hdl, "Failed to get link state\n"); return -EIO; } if (!link_state) { nic_err(dev->dev_hdl, "Link status must be up when set pf tx rate\n"); return -EINVAL; } nic_cfg = &dev->nic_io->nic_cfg; old_bw_limit = nic_cfg->pf_bw_limit; nic_cfg->pf_bw_limit = bw_limit; err = hinic_update_pf_bw(hwdev); if (err) { nic_cfg->pf_bw_limit = old_bw_limit; return err; } return 0; } /* Set link status follow port status */ int hinic_set_link_status_follow(void *hwdev, enum hinic_link_follow_status status) { struct hinic_hwdev *dev = hwdev; struct hinic_set_link_follow follow = {0}; u16 out_size = sizeof(follow); int err; if (!hwdev) return -EINVAL; if (status >= HINIC_LINK_FOLLOW_STATUS_MAX) { nic_err(dev->dev_hdl, "Invalid link follow status: %d\n", status); return -EINVAL; } err = hinic_global_func_id_get(hwdev, &follow.func_id); if (err) return err; follow.follow_status = status; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_SET_LINK_FOLLOW, &follow, sizeof(follow), &follow, &out_size); if ((follow.status != HINIC_MGMT_CMD_UNSUPPORTED && follow.status) || err || !out_size) { nic_err(dev->dev_hdl, "Failed to set link status follow port status, err: %d, status: 0x%x, out size: 0x%x\n", err, follow.status, out_size); return -EFAULT; } return follow.status; } EXPORT_SYMBOL(hinic_set_link_status_follow); /* HILINK module */ #define HINIC_MGMT_DEFAULT_SIZE 1 static int __hilink_msg_to_mgmt_sync(void *hwdev, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout) { int err; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_HILINK, cmd, buf_in, in_size, buf_out, out_size, timeout); if (err) return err; if (*out_size == HINIC_MGMT_DEFAULT_SIZE && buf_out) *((u8 *)(buf_out)) = HINIC_MGMT_CMD_UNSUPPORTED; return 0; } int hinic_get_hilink_link_info(void *hwdev, struct hinic_link_info *info) { struct hinic_hilink_link_info link_info = {0}; u16 out_size = sizeof(link_info); int err; link_info.port_id = hinic_physical_port_id(hwdev); err = __hilink_msg_to_mgmt_sync(hwdev, HINIC_HILINK_CMD_GET_LINK_INFO, &link_info, sizeof(link_info), &link_info, &out_size, 0); if ((link_info.status != HINIC_MGMT_CMD_UNSUPPORTED && link_info.status) || err || !out_size) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get hilink info, err: %d, status: 0x%x, out size: 0x%x\n", err, link_info.status, out_size); return -EFAULT; } if (!link_info.status) memcpy(info, &link_info.info, sizeof(*info)); else if (link_info.status == HINIC_MGMT_CMD_UNSUPPORTED) nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Unsupported command: mod: %d, cmd: %d\n", HINIC_MOD_HILINK, HINIC_HILINK_CMD_GET_LINK_INFO); return link_info.status; } int hinic_set_link_settings(void *hwdev, struct hinic_link_ksettings *settings) { struct hinic_link_ksettings_info info = {0}; u16 out_size = sizeof(info); int err; err = hinic_global_func_id_get(hwdev, &info.func_id); if (err) return err; info.valid_bitmap = settings->valid_bitmap; info.autoneg = settings->autoneg; info.speed = settings->speed; info.fec = settings->fec; err = __hilink_msg_to_mgmt_sync(hwdev, HINIC_HILINK_CMD_SET_LINK_SETTINGS, &info, sizeof(info), &info, &out_size, 0); if ((info.status != HINIC_MGMT_CMD_UNSUPPORTED && info.status) || err || !out_size) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to set link settings, err: %d, status: 0x%x, out size: 0x%x\n", err, info.status, out_size); return -EFAULT; } return info.status; } int hinic_disable_tx_promisc(void *hwdev) { struct hinic_promsic_info info = {0}; u16 out_size = sizeof(info); int err; err = hinic_global_func_id_get(hwdev, &info.func_id); if (err) return err; info.cfg = HINIC_TX_PROMISC_DISABLE; err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_L2NIC, HINIC_PORT_CMD_DISABLE_PROMISC, &info, sizeof(info), &info, &out_size, 0); if (err || !out_size || info.status) { if (info.status == HINIC_MGMT_CMD_UNSUPPORTED) { nic_info(((struct hinic_hwdev *)hwdev)->dev_hdl, "Unsupported to disable TX promisc\n"); return 0; } nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to disable multihost promisc, err: %d, status: 0x%x, out size: 0x%x\n", err, info.status, out_size); return -EFAULT; } return 0; } static bool hinic_if_sfp_absent(void *hwdev) { struct card_node *chip_node = ((struct hinic_hwdev *)hwdev)->chip_node; struct hinic_port_routine_cmd *rt_cmd; struct hinic_cmd_get_light_module_abs sfp_abs = {0}; u8 port_id = hinic_physical_port_id(hwdev); u16 out_size = sizeof(sfp_abs); int err; bool sfp_abs_valid; bool sfp_abs_status; rt_cmd = &chip_node->rt_cmd[port_id]; mutex_lock(&chip_node->sfp_mutex); sfp_abs_valid = rt_cmd->up_send_sfp_abs; sfp_abs_status = (bool)rt_cmd->abs.abs_status; if (sfp_abs_valid) { mutex_unlock(&chip_node->sfp_mutex); return sfp_abs_status; } mutex_unlock(&chip_node->sfp_mutex); sfp_abs.port_id = port_id; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_SFP_ABS, &sfp_abs, sizeof(sfp_abs), &sfp_abs, &out_size); if (sfp_abs.status || err || !out_size) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get port%d sfp absent status, err: %d, status: 0x%x, out size: 0x%x\n", port_id, err, sfp_abs.status, out_size); return true; } return ((sfp_abs.abs_status == 0) ? false : true); } int hinic_get_sfp_eeprom(void *hwdev, u8 *data, u16 *len) { struct hinic_cmd_get_std_sfp_info sfp_info = {0}; u8 port_id; u16 out_size = sizeof(sfp_info); int err; if (!hwdev || !data || !len) return -EINVAL; port_id = hinic_physical_port_id(hwdev); if (port_id >= HINIC_MAX_PORT_ID) return -EINVAL; if (hinic_if_sfp_absent(hwdev)) return -ENXIO; sfp_info.port_id = port_id; err = l2nic_msg_to_mgmt_sync(hwdev, HINIC_PORT_CMD_GET_STD_SFP_INFO, &sfp_info, sizeof(sfp_info), &sfp_info, &out_size); if (sfp_info.status || err || !out_size) { nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get port%d sfp eeprom information, err: %d, status: 0x%x, out size: 0x%x\n", port_id, err, sfp_info.status, out_size); return -EIO; } *len = min_t(u16, sfp_info.eeprom_len, STD_SFP_INFO_MAX_SIZE); memcpy(data, sfp_info.sfp_info, STD_SFP_INFO_MAX_SIZE); return 0; } int hinic_get_sfp_type(void *hwdev, u8 *data0, u8 *data1) { struct card_node *chip_node = NULL; struct hinic_port_routine_cmd *rt_cmd; u8 sfp_data[STD_SFP_INFO_MAX_SIZE]; u16 len; u8 port_id; int err; if (!hwdev || !data0 || !data1) return -EINVAL; port_id = hinic_physical_port_id(hwdev); if (port_id >= HINIC_MAX_PORT_ID) return -EINVAL; if (hinic_if_sfp_absent(hwdev)) return -ENXIO; chip_node = ((struct hinic_hwdev *)hwdev)->chip_node; rt_cmd = &chip_node->rt_cmd[port_id]; mutex_lock(&chip_node->sfp_mutex); if (rt_cmd->up_send_sfp_info) { *data0 = rt_cmd->sfp_info.sfp_qsfp_info[0]; *data1 = rt_cmd->sfp_info.sfp_qsfp_info[1]; mutex_unlock(&chip_node->sfp_mutex); return 0; } mutex_unlock(&chip_node->sfp_mutex); err = hinic_get_sfp_eeprom(hwdev, sfp_data, &len); if (err) return err; *data0 = sfp_data[0]; *data1 = sfp_data[1]; return 0; } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_nic_cfg.h000066400000000000000000000421671502027332500220540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_CFG_H #define HINIC_CFG_H #define OS_VF_ID_TO_HW(os_vf_id) ((os_vf_id) + 1) #define HW_VF_ID_TO_OS(hw_vf_id) ((hw_vf_id) - 1) #define FW_SUPPORT_MAC_REUSE 0x1 #define FW_SUPPORT_MAC_REUSE_FUNC(hwdev) \ ((hwdev)->fw_support_func_flag & FW_SUPPORT_MAC_REUSE) #define HINIC_VLAN_PRIORITY_SHIFT 13 #define HINIC_RSS_INDIR_SIZE 256 #define HINIC_DCB_TC_MAX 0x8 #define HINIC_DCB_UP_MAX 0x8 #define HINIC_DCB_COS_MAX 0x8 #define HINIC_DCB_PG_MAX 0x8 #define HINIC_DCB_TSA_TC_SP 2 #define HINIC_DCB_TSA_TC_DWRR 0 #define HINIC_RSS_KEY_SIZE 40 #define HINIC_MAX_NUM_RQ 128 #define HINIC_MIN_MTU_SIZE 256 #define HINIC_MAX_JUMBO_FRAME_SIZE 9600 #define HINIC_LRO_MAX_WQE_NUM_UPPER 32 #define HINIC_LRO_MAX_WQE_NUM_LOWER 1 #define HINIC_LRO_MAX_WQE_NUM_DEFAULT_ARM 4 #define HINIC_LRO_MAX_WQE_NUM_DEFAULT_X86 8 #define HINIC_LRO_MAX_WQE_NUM_DEFAULT 8 #define HINIC_LRO_WQE_NUM_PANGEA_DEFAULT 32 #define HINIC_LRO_RX_TIMER_UPPER 1024 #define HINIC_LRO_RX_TIMER_LOWER 1 #define HINIC_LRO_RX_TIMER_DEFAULT 16 #define HINIC_LRO_RX_TIMER_DEFAULT_25GE 16 #define HINIC_LRO_RX_TIMER_DEFAULT_100GE 64 #define HINIC_LRO_RX_TIMER_DEFAULT_PG_10GE 10 #define HINIC_LRO_RX_TIMER_DEFAULT_PG_100GE 8 #if defined(__aarch64__) #define HINIC_LOWEST_LATENCY 1 #define HINIC_RX_RATE_LOW 400000 #define HINIC_RX_COAL_TIME_LOW 20 #define HINIC_RX_PENDING_LIMIT_LOW 2 #define HINIC_RX_RATE_HIGH 1000000 #define HINIC_RX_COAL_TIME_HIGH 225 #define HINIC_RX_PENDING_LIMIT_HIGH 50 #define HINIC_RX_RATE_THRESH 35000 #define HINIC_TX_RATE_THRESH 35000 #define HINIC_RX_RATE_LOW_VM 400000 #define HINIC_RX_PENDING_LIMIT_HIGH_VM 50 #else #define HINIC_LOWEST_LATENCY 1 #define HINIC_RX_RATE_LOW 400000 #define HINIC_RX_COAL_TIME_LOW 16 #define HINIC_RX_PENDING_LIMIT_LOW 2 #define HINIC_RX_RATE_HIGH 1000000 #define HINIC_RX_COAL_TIME_HIGH 225 #define HINIC_RX_PENDING_LIMIT_HIGH 8 #define HINIC_RX_RATE_THRESH 50000 #define HINIC_TX_RATE_THRESH 50000 #define HINIC_RX_RATE_LOW_VM 100000 #define HINIC_RX_PENDING_LIMIT_HIGH_VM 87 #endif enum hinic_board_type { HINIC_BOARD_UNKNOWN = 0, HINIC_BOARD_10GE = 1, HINIC_BOARD_25GE = 2, HINIC_BOARD_40GE = 3, HINIC_BOARD_100GE = 4, HINIC_BOARD_PG_TP_10GE = 5, HINIC_BOARD_PG_SM_25GE = 6, HINIC_BOARD_PG_100GE = 7, }; enum hinic_os_type { HINIC_OS_UNKNOWN = 0, HINIC_OS_HUAWEI = 1, HINIC_OS_NON_HUAWEI = 2, }; enum hinic_cpu_type { HINIC_CPU_UNKNOWN = 0, HINIC_CPU_X86_GENERIC = 1, HINIC_CPU_ARM_GENERIC = 2, }; struct hinic_adaptive_rx_cfg { u32 lowest_lat; u32 rate_low; u32 coal_time_low; u32 pending_limit_low; u32 rate_high; u32 coal_time_high; u32 pending_limit_high; u32 rate_thresh; }; struct hinic_lro_cfg { u32 enable; u32 timer; u32 buffer_size; }; struct hinic_environment_info { enum hinic_board_type board; enum hinic_os_type os; enum hinic_cpu_type cpu; }; struct hinic_adaptive_cfg { struct hinic_adaptive_rx_cfg adaptive_rx; struct hinic_lro_cfg lro; }; enum hinic_rss_hash_type { HINIC_RSS_HASH_ENGINE_TYPE_XOR = 0, HINIC_RSS_HASH_ENGINE_TYPE_TOEP, HINIC_RSS_HASH_ENGINE_TYPE_MAX, }; struct ifla_vf_info; struct hinic_dcb_state; struct nic_port_info { u8 port_type; u8 autoneg_cap; u8 autoneg_state; u8 duplex; u8 speed; }; enum nic_media_type { MEDIA_UNKNOWN = -1, MEDIA_FIBRE = 0, MEDIA_COPPER, MEDIA_BACKPLANE }; enum nic_speed_level { LINK_SPEED_10MB = 0, LINK_SPEED_100MB, LINK_SPEED_1GB, LINK_SPEED_10GB, LINK_SPEED_25GB, LINK_SPEED_40GB, LINK_SPEED_100GB, LINK_SPEED_LEVELS, }; enum hinic_link_mode { HINIC_10GE_BASE_KR = 0, HINIC_40GE_BASE_KR4 = 1, HINIC_40GE_BASE_CR4 = 2, HINIC_100GE_BASE_KR4 = 3, HINIC_100GE_BASE_CR4 = 4, HINIC_25GE_BASE_KR_S = 5, HINIC_25GE_BASE_CR_S = 6, HINIC_25GE_BASE_KR = 7, HINIC_25GE_BASE_CR = 8, HINIC_GE_BASE_KX = 9, HINIC_LINK_MODE_NUMBERS, HINIC_SUPPORTED_UNKNOWN = 0xFFFF, }; enum hinic_port_type { HINIC_PORT_TP, /* BASET */ HINIC_PORT_AUI, HINIC_PORT_MII, HINIC_PORT_FIBRE, /* OPTICAL */ HINIC_PORT_BNC, HINIC_PORT_ELEC, HINIC_PORT_COPPER, /* PORT_DA */ HINIC_PORT_AOC, HINIC_PORT_BACKPLANE, HINIC_PORT_NONE = 0xEF, HINIC_PORT_OTHER = 0xFF, }; enum hinic_link_status { HINIC_LINK_DOWN = 0, HINIC_LINK_UP }; struct nic_pause_config { u32 auto_neg; u32 rx_pause; u32 tx_pause; }; struct nic_lro_info { u16 func_id; u8 lro_ipv4_en; u8 lro_ipv6_en; u8 lro_max_wqe_num; u8 lro_timer_en; u32 lro_period; }; struct nic_rss_type { u8 tcp_ipv6_ext; u8 ipv6_ext; u8 tcp_ipv6; u8 ipv6; u8 tcp_ipv4; u8 ipv4; u8 udp_ipv6; u8 udp_ipv4; }; struct hinic_vport_stats { u64 tx_unicast_pkts_vport; u64 tx_unicast_bytes_vport; u64 tx_multicast_pkts_vport; u64 tx_multicast_bytes_vport; u64 tx_broadcast_pkts_vport; u64 tx_broadcast_bytes_vport; u64 rx_unicast_pkts_vport; u64 rx_unicast_bytes_vport; u64 rx_multicast_pkts_vport; u64 rx_multicast_bytes_vport; u64 rx_broadcast_pkts_vport; u64 rx_broadcast_bytes_vport; u64 tx_discard_vport; u64 rx_discard_vport; u64 tx_err_vport; u64 rx_err_vport; }; struct hinic_phy_port_stats { u64 mac_rx_total_pkt_num; u64 mac_rx_total_oct_num; u64 mac_rx_bad_pkt_num; u64 mac_rx_bad_oct_num; u64 mac_rx_good_pkt_num; u64 mac_rx_good_oct_num; u64 mac_rx_uni_pkt_num; u64 mac_rx_multi_pkt_num; u64 mac_rx_broad_pkt_num; u64 mac_tx_total_pkt_num; u64 mac_tx_total_oct_num; u64 mac_tx_bad_pkt_num; u64 mac_tx_bad_oct_num; u64 mac_tx_good_pkt_num; u64 mac_tx_good_oct_num; u64 mac_tx_uni_pkt_num; u64 mac_tx_multi_pkt_num; u64 mac_tx_broad_pkt_num; u64 mac_rx_fragment_pkt_num; u64 mac_rx_undersize_pkt_num; u64 mac_rx_undermin_pkt_num; u64 mac_rx_64_oct_pkt_num; u64 mac_rx_65_127_oct_pkt_num; u64 mac_rx_128_255_oct_pkt_num; u64 mac_rx_256_511_oct_pkt_num; u64 mac_rx_512_1023_oct_pkt_num; u64 mac_rx_1024_1518_oct_pkt_num; u64 mac_rx_1519_2047_oct_pkt_num; u64 mac_rx_2048_4095_oct_pkt_num; u64 mac_rx_4096_8191_oct_pkt_num; u64 mac_rx_8192_9216_oct_pkt_num; u64 mac_rx_9217_12287_oct_pkt_num; u64 mac_rx_12288_16383_oct_pkt_num; u64 mac_rx_1519_max_bad_pkt_num; u64 mac_rx_1519_max_good_pkt_num; u64 mac_rx_oversize_pkt_num; u64 mac_rx_jabber_pkt_num; u64 mac_rx_pause_num; u64 mac_rx_pfc_pkt_num; u64 mac_rx_pfc_pri0_pkt_num; u64 mac_rx_pfc_pri1_pkt_num; u64 mac_rx_pfc_pri2_pkt_num; u64 mac_rx_pfc_pri3_pkt_num; u64 mac_rx_pfc_pri4_pkt_num; u64 mac_rx_pfc_pri5_pkt_num; u64 mac_rx_pfc_pri6_pkt_num; u64 mac_rx_pfc_pri7_pkt_num; u64 mac_rx_control_pkt_num; u64 mac_rx_y1731_pkt_num; u64 mac_rx_sym_err_pkt_num; u64 mac_rx_fcs_err_pkt_num; u64 mac_rx_send_app_good_pkt_num; u64 mac_rx_send_app_bad_pkt_num; u64 mac_tx_fragment_pkt_num; u64 mac_tx_undersize_pkt_num; u64 mac_tx_undermin_pkt_num; u64 mac_tx_64_oct_pkt_num; u64 mac_tx_65_127_oct_pkt_num; u64 mac_tx_128_255_oct_pkt_num; u64 mac_tx_256_511_oct_pkt_num; u64 mac_tx_512_1023_oct_pkt_num; u64 mac_tx_1024_1518_oct_pkt_num; u64 mac_tx_1519_2047_oct_pkt_num; u64 mac_tx_2048_4095_oct_pkt_num; u64 mac_tx_4096_8191_oct_pkt_num; u64 mac_tx_8192_9216_oct_pkt_num; u64 mac_tx_9217_12287_oct_pkt_num; u64 mac_tx_12288_16383_oct_pkt_num; u64 mac_tx_1519_max_bad_pkt_num; u64 mac_tx_1519_max_good_pkt_num; u64 mac_tx_oversize_pkt_num; u64 mac_tx_jabber_pkt_num; u64 mac_tx_pause_num; u64 mac_tx_pfc_pkt_num; u64 mac_tx_pfc_pri0_pkt_num; u64 mac_tx_pfc_pri1_pkt_num; u64 mac_tx_pfc_pri2_pkt_num; u64 mac_tx_pfc_pri3_pkt_num; u64 mac_tx_pfc_pri4_pkt_num; u64 mac_tx_pfc_pri5_pkt_num; u64 mac_tx_pfc_pri6_pkt_num; u64 mac_tx_pfc_pri7_pkt_num; u64 mac_tx_control_pkt_num; u64 mac_tx_y1731_pkt_num; u64 mac_tx_1588_pkt_num; u64 mac_tx_err_all_pkt_num; u64 mac_tx_from_app_good_pkt_num; u64 mac_tx_from_app_bad_pkt_num; u64 mac_rx_higig2_ext_pkt_num; u64 mac_rx_higig2_message_pkt_num; u64 mac_rx_higig2_error_pkt_num; u64 mac_rx_higig2_cpu_ctrl_pkt_num; u64 mac_rx_higig2_unicast_pkt_num; u64 mac_rx_higig2_broadcast_pkt_num; u64 mac_rx_higig2_l2_multicast_pkt_num; u64 mac_rx_higig2_l3_multicast_pkt_num; u64 mac_tx_higig2_message_pkt_num; u64 mac_tx_higig2_ext_pkt_num; u64 mac_tx_higig2_cpu_ctrl_pkt_num; u64 mac_tx_higig2_unicast_pkt_num; u64 mac_tx_higig2_broadcast_pkt_num; u64 mac_tx_higig2_l2_multicast_pkt_num; u64 mac_tx_higig2_l3_multicast_pkt_num; }; enum hinic_rq_filter_type { HINIC_RQ_FILTER_TYPE_NONE = 0x0, HINIC_RQ_FILTER_TYPE_MAC_ONLY = (1 << 0), HINIC_RQ_FILTER_TYPE_VLAN_ONLY = (1 << 1), HINIC_RQ_FILTER_TYPE_VLANMAC = (1 << 2), HINIC_RQ_FILTER_TYPE_VXLAN = (1 << 3), HINIC_RQ_FILTER_TYPE_GENEVE = (1 << 4), }; struct hinic_rq_filter_info { u16 qid; u8 filter_type;/* 1: mac, 8: vxlan */ u8 qflag;/*0:stdq, 1:defq, 2: netq*/ u8 mac[ETH_ALEN]; struct { u8 inner_mac[ETH_ALEN]; u32 vni; } vxlan; }; #define HINIC_MGMT_VERSION_MAX_LEN 32 #define HINIC_FW_VERSION_NAME 16 #define HINIC_FW_VERSION_SECTION_CNT 4 #define HINIC_FW_VERSION_SECTION_BORDER 0xFF struct hinic_fw_version { u8 mgmt_ver[HINIC_FW_VERSION_NAME]; u8 microcode_ver[HINIC_FW_VERSION_NAME]; u8 boot_ver[HINIC_FW_VERSION_NAME]; }; enum hinic_valid_link_settings { HILINK_LINK_SET_SPEED = 0x1, HILINK_LINK_SET_AUTONEG = 0x2, HILINK_LINK_SET_FEC = 0x4, }; struct hinic_link_ksettings { u32 valid_bitmap; u32 speed; /* enum nic_speed_level */ u8 autoneg; /* 0 - off; 1 - on */ u8 fec; /* 0 - RSFEC; 1 - BASEFEC; 2 - NOFEC */ }; enum hinic_link_follow_status { HINIC_LINK_FOLLOW_DEFAULT, HINIC_LINK_FOLLOW_PORT, HINIC_LINK_FOLLOW_SEPARATE, HINIC_LINK_FOLLOW_STATUS_MAX, }; enum hinic_lro_en_status { HINIC_LRO_STATUS_DISABLE, HINIC_LRO_STATUS_ENABLE, HINIC_LRO_STATUS_UNSET, }; #define HINIC_VLAN_FILTER_EN BIT(0) #define HINIC_BROADCAST_FILTER_EX_EN BIT(1) #define HINIC_RX_CSUM_OFFLOAD_EN 0xFFF /* Set mac_vlan table */ int hinic_set_mac(void *hwdev, const u8 *mac_addr, u16 vlan_id, u16 func_id); int hinic_del_mac(void *hwdev, const u8 *mac_addr, u16 vlan_id, u16 func_id); int hinic_update_mac(void *hwdev, const u8 *old_mac, u8 *new_mac, u16 vlan_id, u16 func_id); int hinic_update_mac_vlan(void *hwdev, u16 old_vlan, u16 new_vlan, int vf_id); /* Obtaining the permanent mac */ int hinic_get_default_mac(void *hwdev, u8 *mac_addr); /* Check whether the current solution is using this interface, * the current code does not invoke the sdk interface to set mtu */ int hinic_set_port_mtu(void *hwdev, u32 new_mtu); /* Set vlan leaf table */ int hinic_add_vlan(void *hwdev, u16 vlan_id, u16 func_id); int hinic_set_vlan_fliter(void *hwdev, u32 vlan_filter_ctrl); int hinic_del_vlan(void *hwdev, u16 vlan_id, u16 func_id); int hinic_get_port_info(void *hwdev, struct nic_port_info *port_info); int hinic_set_autoneg(void *hwdev, bool enable); int hinic_force_port_relink(void *hwdev); int hinic_get_link_mode(void *hwdev, enum hinic_link_mode *supported, enum hinic_link_mode *advertised); int hinic_set_port_link_status(void *hwdev, bool enable); int hinic_set_speed(void *hwdev, enum nic_speed_level speed); /* SPEED_UNKNOWN = -1,SPEED_10MB_LINK = 0 */ int hinic_get_speed(void *hwdev, enum nic_speed_level *speed); int hinic_get_link_state(void *hwdev, u8 *link_state); int hinic_set_pause_info(void *hwdev, struct nic_pause_config nic_pause); int hinic_get_hw_pause_info(void *hwdev, struct nic_pause_config *nic_pause); int hinic_get_pause_info(void *hwdev, struct nic_pause_config *nic_pause); int hinic_set_rx_mode(void *hwdev, u32 enable); /* offload feature */ int hinic_set_rx_vlan_offload(void *hwdev, u8 en); int hinic_set_rx_csum_offload(void *hwdev, u32 en); int hinic_set_tx_tso(void *hwdev, u8 tso_en); /* Linux NIC used */ int hinic_set_rx_lro_state(void *hwdev, u8 lro_en, u32 lro_timer, u32 wqe_num); /* Win NIC used */ int hinic_set_rx_lro(void *hwdev, u8 ipv4_en, u8 ipv6_en, u8 max_wqe_num); /* Related command dcbtool */ int hinic_dcb_set_pfc(void *hwdev, u8 pfc_en, u8 pfc_bitmap); int hinic_dcb_get_pfc(void *hwdev, u8 *pfc_en_bitmap); int hinic_dcb_set_ets(void *hwdev, u8 *up_tc, u8 *pg_bw, u8 *pgid, u8 *up_bw, u8 *prio); int hinic_dcb_get_ets(void *hwdev, u8 *up_tc, u8 *pg_bw, u8 *pgid, u8 *up_bw, u8 *prio); int hinic_dcb_set_cos_up_map(void *hwdev, u8 cos_valid_bitmap, u8 *cos_up); int hinic_dcb_set_rq_iq_mapping(void *hwdev, u32 num_rqs, u8 *map); /* nictool adaptation interface*/ int hinic_set_lro_aging_timer(void *hwdev, u8 timer_en, u32 period); /* There should be output parameters, add the * output parameter struct nic_up_offload *cfg */ int hinic_get_rx_lro(void *hwdev, struct nic_lro_info *lro_info); int hinic_get_jumbo_frame_size(void *hwdev, u32 *jumbo_size); int hinic_set_jumbo_frame_size(void *hwdev, u32 jumbo_size); int hinic_set_loopback_mode(void *hwdev, bool enable); int hinic_set_loopback_mode_ex(void *hwdev, u32 mode, u32 enable); int hinic_get_loopback_mode_ex(void *hwdev, u32 *mode, u32 *enable); int hinic_get_port_enable_state(void *hwdev, bool *enable); int hinic_get_vport_enable_state(void *hwdev, bool *enable); int hinic_set_lli_state(void *hwdev, u8 lli_state); int hinic_set_vport_enable(void *hwdev, bool enable); int hinic_set_port_enable(void *hwdev, bool enable); /* rss */ int hinic_set_rss_type(void *hwdev, u32 tmpl_idx, struct nic_rss_type rss_type); int hinic_get_rss_type(void *hwdev, u32 tmpl_idx, struct nic_rss_type *rss_type); int hinic_rss_set_template_tbl(void *hwdev, u32 tmpl_idx, const u8 *temp); int hinic_rss_get_template_tbl(void *hwdev, u32 tmpl_idx, u8 *temp); int hinic_rss_get_hash_engine(void *hwdev, u8 tmpl_idx, u8 *type); int hinic_rss_set_hash_engine(void *hwdev, u8 tmpl_idx, u8 type); int hinic_rss_get_indir_tbl(void *hwdev, u32 tmpl_idx, u32 *indir_table); int hinic_rss_set_indir_tbl(void *hwdev, u32 tmpl_idx, const u32 *indir_table); int hinic_rss_cfg(void *hwdev, u8 rss_en, u8 tmpl_idx, u8 tc_num, u8 *prio_tc); int hinic_rss_template_alloc(void *hwdev, u8 *tmpl_idx); int hinic_rss_template_free(void *hwdev, u8 tmpl_idx); /* disable or enable traffic of all functions in the same port */ int hinic_set_port_funcs_state(void *hwdev, bool enable); int hinic_reset_port_link_cfg(void *hwdev); int hinic_get_vport_stats(void *hwdev, struct hinic_vport_stats *stats); int hinic_get_phy_port_stats(void *hwdev, struct hinic_phy_port_stats *stats); int hinic_get_mgmt_version(void *hwdev, u8 *mgmt_ver); int hinic_get_fw_version(void *hwdev, struct hinic_fw_version *fw_ver); int hinic_save_vf_mac(void *hwdev, u16 vf_id, u8 *mac); int hinic_add_vf_vlan(void *hwdev, int vf_id, u16 vlan, u8 qos); int hinic_kill_vf_vlan(void *hwdev, int vf_id); int hinic_set_vf_mac(void *hwdev, int vf_id, unsigned char *mac_addr); u16 hinic_vf_info_vlanprio(void *hwdev, int vf_id); bool hinic_vf_is_registered(void *hwdev, u16 vf_id); void hinic_get_vf_config(void *hwdev, u16 vf_id, struct ifla_vf_info *ivi); void hinic_notify_all_vfs_link_changed(void *hwdev, u8 link); void hinic_save_pf_link_status(void *hwdev, u8 link); int hinic_set_vf_link_state(void *hwdev, u16 vf_id, int link); int hinic_set_vf_spoofchk(void *hwdev, u16 vf_id, bool spoofchk); bool hinic_vf_info_spoofchk(void *hwdev, int vf_id); int hinic_set_vf_trust(void *hwdev, u16 vf_id, bool trust); bool hinic_vf_info_trust(void *hwdev, int vf_id); int hinic_set_vf_tx_rate(void *hwdev, u16 vf_id, u32 max_rate, u32 min_rate); int hinic_init_vf_hw(void *hwdev, u16 start_vf_id, u16 end_vf_id); int hinic_deinit_vf_hw(void *hwdev, u16 start_vf_id, u16 end_vf_id); int hinic_set_dcb_state(void *hwdev, struct hinic_dcb_state *dcb_state); int hinic_get_dcb_state(void *hwdev, struct hinic_dcb_state *dcb_state); int hinic_get_pf_dcb_state(void *hwdev, struct hinic_dcb_state *dcb_state); int hinic_set_ipsu_mac(void *hwdev, u16 index, u8 *mac_addr, u16 vlan_id, u16 func_id); int hinic_get_ipsu_mac(void *hwdev, u16 index, u8 *mac_addr, u16 *vlan_id, u16 *func_id); int hinic_set_anti_attack(void *hwdev, bool enable); int hinic_flush_sq_res(void *hwdev); int hinic_set_super_cqe_state(void *hwdev, bool enable); int hinic_set_func_capture_en(void *hwdev, u16 func_id, bool cap_en); int hinic_force_drop_tx_pkt(void *hwdev); int hinic_update_pf_bw(void *hwdev); int hinic_set_pf_bw_limit(void *hwdev, u32 bw_limit); int hinic_set_link_status_follow(void *hwdev, enum hinic_link_follow_status status); int hinic_disable_tx_promisc(void *hwdev); /* HILINK module */ int hinic_set_link_settings(void *hwdev, struct hinic_link_ksettings *settings); int hinic_enable_netq(void *hwdev, u8 en); int hinic_add_hw_rqfilter(void *hwdev, struct hinic_rq_filter_info *filter_info); int hinic_del_hw_rqfilter(void *hwdev, struct hinic_rq_filter_info *filter_info); int hinic_get_sfp_eeprom(void *hwdev, u8 *data, u16 *len); int hinic_get_sfp_type(void *hwdev, u8 *data0, u8 *data1); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_nic_dbg.c000066400000000000000000000144231502027332500220360ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include "ossl_knl.h" #include "hinic_hw_mgmt.h" #include "hinic_hw.h" #include "hinic_hwdev.h" #include "hinic_hwif.h" #include "hinic_wq.h" #include "hinic_nic_cfg.h" #include "hinic_mgmt_interface.h" #include "hinic_nic_io.h" #include "hinic_nic.h" #include "hinic_dbg.h" #define INVALID_PI 0xFFFF u16 hinic_dbg_get_qp_num(void *hwdev) { struct hinic_nic_io *nic_io; if (!hwdev) return 0; nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; if (!nic_io) return 0; return nic_io->num_qps; } void *hinic_dbg_get_qp_handle(void *hwdev, u16 q_id) { struct hinic_nic_io *nic_io; if (!hwdev) return NULL; nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; if (!nic_io) return NULL; if (q_id >= nic_io->num_qps) return NULL; return &nic_io->qps[q_id]; } void *hinic_dbg_get_sq_wq_handle(void *hwdev, u16 q_id) { struct hinic_qp *qp = hinic_dbg_get_qp_handle(hwdev, q_id); if (!qp) return NULL; return qp->sq.wq; } void *hinic_dbg_get_rq_wq_handle(void *hwdev, u16 q_id) { struct hinic_qp *qp = hinic_dbg_get_qp_handle(hwdev, q_id); if (!qp) return NULL; return qp->rq.wq; } u16 hinic_dbg_get_sq_pi(void *hwdev, u16 q_id) { struct hinic_wq *wq = hinic_dbg_get_sq_wq_handle(hwdev, q_id); if (!wq) return 0; return ((u16)wq->prod_idx) & wq->mask; } u16 hinic_dbg_get_rq_hw_pi(void *hwdev, u16 q_id) { struct hinic_qp *qp = hinic_dbg_get_qp_handle(hwdev, q_id); if (qp) return cpu_to_be16(*qp->rq.pi_virt_addr); nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Get rq hw pi failed\n"); return INVALID_PI; } u16 hinic_dbg_get_rq_sw_pi(void *hwdev, u16 q_id) { struct hinic_wq *wq = hinic_dbg_get_rq_wq_handle(hwdev, q_id); if (!wq) return 0; return ((u16)wq->prod_idx) & wq->mask; } void *hinic_dbg_get_sq_ci_addr(void *hwdev, u16 q_id) { struct hinic_qp *qp = hinic_dbg_get_qp_handle(hwdev, q_id); if (!qp) return NULL; return qp->sq.cons_idx_addr; } u64 hinic_dbg_get_sq_cla_addr(void *hwdev, u16 q_id) { struct hinic_qp *qp = hinic_dbg_get_qp_handle(hwdev, q_id); if (!qp) return 0; return qp->sq.wq->block_paddr; } u64 hinic_dbg_get_rq_cla_addr(void *hwdev, u16 q_id) { struct hinic_qp *qp = hinic_dbg_get_qp_handle(hwdev, q_id); if (!qp) return 0; return qp->rq.wq->block_paddr; } int hinic_dbg_get_sq_db_addr(void *hwdev, u16 q_id, u64 **map_addr, u64 *phy_addr, u32 *pg_idx) { struct hinic_qp *qp; struct hinic_hwif *hwif; qp = hinic_dbg_get_qp_handle(hwdev, q_id); if (!qp) return -EFAULT; hwif = ((struct hinic_hwdev *)hwdev)->hwif; *map_addr = (u64 *)qp->sq.db_addr; *pg_idx = DB_IDX(qp->sq.db_addr, hwif->db_base); *phy_addr = hwif->db_base_phy + (*pg_idx) * HINIC_DB_PAGE_SIZE; return 0; } u16 hinic_dbg_get_global_qpn(const void *hwdev) { if (!hwdev) return 0; return ((struct hinic_hwdev *)hwdev)->nic_io->global_qpn; } static int get_wqe_info(struct hinic_wq *wq, u16 idx, u16 wqebb_cnt, u8 *wqe, u16 *wqe_size) { void *src_wqe; u32 offset; u16 i; if (idx + wqebb_cnt > wq->q_depth) return -EFAULT; if (*wqe_size != (u16)(wq->wqebb_size * wqebb_cnt)) { pr_err("Unexpect out buf size from user: %d, expect: %d\n", *wqe_size, (u16)(wq->wqebb_size * wqebb_cnt)); return -EFAULT; } for (i = 0; i < wqebb_cnt; i++) { src_wqe = (void *)hinic_slq_get_addr(wq, idx + i); offset = i * wq->wqebb_size; memcpy(wqe + offset, src_wqe, wq->wqebb_size); } return 0; } int hinic_dbg_get_sq_wqe_info(void *hwdev, u16 q_id, u16 idx, u16 wqebb_cnt, u8 *wqe, u16 *wqe_size) { struct hinic_wq *wq; int err; wq = hinic_dbg_get_sq_wq_handle(hwdev, q_id); if (!wq) return -EFAULT; err = get_wqe_info(wq, idx, wqebb_cnt, wqe, wqe_size); return err; } int hinic_dbg_get_rq_wqe_info(void *hwdev, u16 q_id, u16 idx, u16 wqebb_cnt, u8 *wqe, u16 *wqe_size) { struct hinic_wq *wq; int err; wq = hinic_dbg_get_rq_wq_handle(hwdev, q_id); if (!wq) return -EFAULT; err = get_wqe_info(wq, idx, wqebb_cnt, wqe, wqe_size); return err; } int hinic_dbg_get_hw_stats(const void *hwdev, u8 *hw_stats, u16 *out_size) { if (!hw_stats || *out_size != sizeof(struct hinic_hw_stats)) { pr_err("Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(struct hinic_hw_stats)); return -EFAULT; } memcpy(hw_stats, &((struct hinic_hwdev *)hwdev)->hw_stats, sizeof(struct hinic_hw_stats)); return 0; } u16 hinic_dbg_clear_hw_stats(void *hwdev, u32 *out_size) { if (*out_size != sizeof(struct hinic_hw_stats)) { pr_err("Unexpect out buf size from user :%d, expect: %lu\n", *out_size, sizeof(struct hinic_hw_stats)); return -EFAULT; } memset((void *)&((struct hinic_hwdev *)hwdev)->hw_stats, 0, sizeof(struct hinic_hw_stats)); memset((void *)((struct hinic_hwdev *)hwdev)->chip_fault_stats, 0, HINIC_CHIP_FAULT_SIZE); return 0; } void hinic_get_chip_fault_stats(const void *hwdev, u8 *chip_fault_stats, int offset) { if (offset < 0 || offset > HINIC_CHIP_FAULT_SIZE) { pr_err("Invalid chip offset value: %d\n", offset); return; } if (offset + MAX_DRV_BUF_SIZE <= HINIC_CHIP_FAULT_SIZE) memcpy(chip_fault_stats, ((struct hinic_hwdev *)hwdev)->chip_fault_stats + offset, MAX_DRV_BUF_SIZE); else memcpy(chip_fault_stats, ((struct hinic_hwdev *)hwdev)->chip_fault_stats + offset, HINIC_CHIP_FAULT_SIZE - offset); } int hinic_dbg_get_pf_bw_limit(void *hwdev, u32 *pf_bw_limit) { struct hinic_hwdev *dev = hwdev; struct hinic_nic_cfg *nic_cfg; if (!hwdev) return -EINVAL; if (!dev->nic_io) return -EINVAL; nic_cfg = &dev->nic_io->nic_cfg; *pf_bw_limit = nic_cfg->pf_bw_limit; return 0; } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_nic_dev.h000066400000000000000000000154621502027332500220710ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_NIC_DEV_H #define HINIC_NIC_DEV_H #include #include #include #include #include "ossl_knl.h" #include "hinic_nic_io.h" #include "hinic_nic_cfg.h" #include "hinic_tx.h" #include "hinic_rx.h" #define HINIC_DRV_NAME "hinic" #define HINIC_CHIP_NAME "hinic" #define HINIC_DRV_VERSION "2.3.2.17" struct vf_data_storage; #define HINIC_FUNC_IS_VF(hwdev) (hinic_func_type(hwdev) == TYPE_VF) enum hinic_flags { HINIC_INTF_UP, HINIC_MAC_FILTER_CHANGED, HINIC_LP_TEST, HINIC_RSS_ENABLE, HINIC_DCB_ENABLE, HINIC_SAME_RXTX, HINIC_INTR_ADAPT, HINIC_UPDATE_MAC_FILTER, HINIC_ETS_ENABLE, }; #define RX_BUFF_NUM_PER_PAGE 2 #define HINIC_MAX_MAC_NUM 3 #define LP_PKT_CNT 64 struct hinic_mac_addr { u8 addr[ETH_ALEN]; u16 state; }; enum hinic_rx_mode_state { HINIC_HW_PROMISC_ON, HINIC_HW_ALLMULTI_ON, HINIC_PROMISC_FORCE_ON, HINIC_ALLMULTI_FORCE_ON, }; enum mac_filter_state { HINIC_MAC_WAIT_HW_SYNC, HINIC_MAC_HW_SYNCED, HINIC_MAC_WAIT_HW_UNSYNC, HINIC_MAC_HW_UNSYNCED, }; struct hinic_mac_filter { struct list_head list; u8 addr[ETH_ALEN]; unsigned long state; }; /* TC bandwidth allocation per direction */ struct hinic_tc_attr { u8 pg_id; /* Priority Group(PG) ID */ u8 bw_pct; /* % of PG's bandwidth */ u8 up_map; /* User Priority to Traffic Class mapping */ u8 prio_type; }; /* User priority configuration */ struct hinic_tc_cfg { struct hinic_tc_attr path[2]; /* One each for Tx/Rx */ bool pfc_en; }; struct hinic_dcb_config { u8 pg_tcs; u8 pfc_tcs; bool pfc_state; struct hinic_tc_cfg tc_cfg[HINIC_DCB_TC_MAX]; u8 bw_pct[2][HINIC_DCB_PG_MAX]; /* One each for Tx/Rx */ }; enum hinic_intr_flags { HINIC_INTR_ON, HINIC_RESEND_ON, }; struct hinic_irq { struct net_device *netdev; /* IRQ corresponding index number */ u16 msix_entry_idx; u32 irq_id; /* The IRQ number from OS */ char irq_name[IFNAMSIZ + 16]; struct napi_struct napi; cpumask_t affinity_mask; struct hinic_txq *txq; struct hinic_rxq *rxq; unsigned long intr_flag; }; struct hinic_intr_coal_info { u8 pending_limt; u8 coalesce_timer_cfg; u8 resend_timer_cfg; u64 pkt_rate_low; u8 rx_usecs_low; u8 rx_pending_limt_low; u64 pkt_rate_high; u8 rx_usecs_high; u8 rx_pending_limt_high; u8 user_set_intr_coal_flag; }; #define HINIC_NIC_STATS_INC(nic_dev, field) \ { \ u64_stats_update_begin(&(nic_dev)->stats.syncp); \ (nic_dev)->stats.field++; \ u64_stats_update_end(&(nic_dev)->stats.syncp); \ } struct hinic_nic_stats { u64 netdev_tx_timeout; /* Subdivision statistics show in private tool */ u64 tx_carrier_off_drop; u64 tx_invalid_qid; struct u64_stats_sync syncp; }; struct hinic_nic_dev { struct pci_dev *pdev; struct net_device *netdev; void *hwdev; int poll_weight; unsigned long *vlan_bitmap; u16 num_qps; u16 max_qps; u32 msg_enable; unsigned long flags; u16 sq_depth; u16 rq_depth; /* mapping from priority */ u8 sq_cos_mapping[HINIC_DCB_UP_MAX]; u8 default_cos_id; struct hinic_txq *txqs; struct hinic_rxq *rxqs; struct nic_service_cap nic_cap; struct irq_info *qps_irq_info; struct hinic_irq *irq_cfg; struct work_struct rx_mode_work; struct delayed_work moderation_task; struct workqueue_struct *workq; struct list_head uc_filter_list; struct list_head mc_filter_list; unsigned long rx_mod_state; int netdev_uc_cnt; int netdev_mc_cnt; int lb_test_rx_idx; int lb_pkt_len; u8 *lb_test_rx_buf; u8 rss_tmpl_idx; u16 num_rss; u16 rss_limit; u8 rss_hash_engine; struct nic_rss_type rss_type; u8 *rss_hkey_user; /* hkey in big endian */ u32 *rss_hkey_user_be; u32 *rss_indir_user; u8 dcbx_cap; u32 dcb_changes; u8 max_cos; u8 up_valid_bitmap; u8 up_cos[HINIC_DCB_UP_MAX]; struct ieee_ets hinic_ieee_ets_default; struct ieee_ets hinic_ieee_ets; struct ieee_pfc hinic_ieee_pfc; struct hinic_dcb_config dcb_cfg; struct hinic_dcb_config tmp_dcb_cfg; struct hinic_dcb_config save_dcb_cfg; unsigned long dcb_flags; int disable_port_cnt; /* lock for disable or enable traffic flow */ struct semaphore dcb_sem; bool heart_status; struct hinic_intr_coal_info *intr_coalesce; unsigned long last_moder_jiffies; u32 adaptive_rx_coal; u8 intr_coal_set_flag; u32 his_link_speed; /* interrupt coalesce must be different in virtual machine */ bool in_vm; bool is_vm_slave; int is_bm_slave; struct hinic_nic_stats stats; /* lock for nic resource */ struct mutex nic_mutex; bool force_port_disable; struct semaphore port_state_sem; u8 link_status; struct hinic_environment_info env_info; struct hinic_adaptive_cfg adaptive_cfg; /* pangea cpu affinity setting */ bool force_affinity; cpumask_t affinity_mask; u32 lro_replenish_thld; u16 rx_buff_len; u32 page_order; }; extern struct hinic_uld_info nic_uld_info; int hinic_open(struct net_device *netdev); int hinic_close(struct net_device *netdev); void hinic_set_ethtool_ops(struct net_device *netdev); void hinicvf_set_ethtool_ops(struct net_device *netdev); void hinic_update_num_qps(struct net_device *netdev); int nic_ioctl(void *uld_dev, u32 cmd, void *buf_in, u32 in_size, void *buf_out, u32 *out_size); int hinic_force_port_disable(struct hinic_nic_dev *nic_dev); int hinic_force_set_port_state(struct hinic_nic_dev *nic_dev, bool enable); int hinic_maybe_set_port_state(struct hinic_nic_dev *nic_dev, bool enable); void hinic_link_status_change(struct hinic_nic_dev *nic_dev, bool status); int hinic_disable_func_rss(struct hinic_nic_dev *nic_dev); int hinic_enable_func_rss(struct hinic_nic_dev *nic_dev); #define hinic_msg(level, nic_dev, msglvl, format, arg...) \ do { \ if ((nic_dev)->netdev && (nic_dev)->netdev->reg_state \ == NETREG_REGISTERED) \ nicif_##level((nic_dev), msglvl, (nic_dev)->netdev, \ format, ## arg); \ else \ nic_##level(&(nic_dev)->pdev->dev, \ format, ## arg); \ } while (0) #define hinic_info(nic_dev, msglvl, format, arg...) \ hinic_msg(info, nic_dev, msglvl, format, ## arg) #define hinic_warn(nic_dev, msglvl, format, arg...) \ hinic_msg(warn, nic_dev, msglvl, format, ## arg) #define hinic_err(nic_dev, msglvl, format, arg...) \ hinic_msg(err, nic_dev, msglvl, format, ## arg) #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_nic_io.c000066400000000000000000000631621502027332500217150ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include "ossl_knl.h" #include "hinic_wq.h" #include "hinic_hw_mgmt.h" #include "hinic_hw.h" #include "hinic_hwdev.h" #include "hinic_nic_cfg.h" #include "hinic_mgmt_interface.h" #include "hinic_nic_io.h" #include "hinic_nic.h" #include "hinic_ctx_def.h" #include "hinic_wq.h" #include "hinic_cmdq.h" #define HINIC_DEAULT_TX_CI_PENDING_LIMIT 0 #define HINIC_DEAULT_TX_CI_COALESCING_TIME 0 static unsigned char tx_pending_limit = HINIC_DEAULT_TX_CI_PENDING_LIMIT; module_param(tx_pending_limit, byte, 0444); MODULE_PARM_DESC(tx_pending_limit, "TX CI coalescing parameter pending_limit (default=0)"); static unsigned char tx_coalescing_time = HINIC_DEAULT_TX_CI_COALESCING_TIME; module_param(tx_coalescing_time, byte, 0444); MODULE_PARM_DESC(tx_coalescing_time, "TX CI coalescing parameter coalescing_time (default=0)"); #define WQ_PREFETCH_MAX 4 #define WQ_PREFETCH_MIN 1 #define WQ_PREFETCH_THRESHOLD 256 struct hinic_qp_ctxt_header { u16 num_queues; u16 queue_type; u32 addr_offset; }; struct hinic_sq_ctxt { u32 ceq_attr; u32 ci_owner; u32 wq_pfn_hi; u32 wq_pfn_lo; u32 pref_cache; u32 pref_owner; u32 pref_wq_pfn_hi_ci; u32 pref_wq_pfn_lo; u32 rsvd8; u32 rsvd9; u32 wq_block_pfn_hi; u32 wq_block_pfn_lo; }; struct hinic_rq_ctxt { u32 ceq_attr; u32 pi_intr_attr; u32 wq_pfn_hi_ci; u32 wq_pfn_lo; u32 pref_cache; u32 pref_owner; u32 pref_wq_pfn_hi_ci; u32 pref_wq_pfn_lo; u32 pi_paddr_hi; u32 pi_paddr_lo; u32 wq_block_pfn_hi; u32 wq_block_pfn_lo; }; struct hinic_sq_ctxt_block { struct hinic_qp_ctxt_header cmdq_hdr; struct hinic_sq_ctxt sq_ctxt[HINIC_Q_CTXT_MAX]; }; struct hinic_rq_ctxt_block { struct hinic_qp_ctxt_header cmdq_hdr; struct hinic_rq_ctxt rq_ctxt[HINIC_Q_CTXT_MAX]; }; struct hinic_sq_db { u32 db_info; }; struct hinic_addr { u32 addr_hi; u32 addr_lo; }; struct hinic_clean_queue_ctxt { struct hinic_qp_ctxt_header cmdq_hdr; u32 ctxt_size; struct hinic_addr cqe_dma_addr[HINIC_RQ_CQ_MAX]; }; static int init_sq(struct hinic_sq *sq, struct hinic_wq *wq, u16 q_id, u16 sq_msix_idx, void *cons_idx_addr, void __iomem *db_addr) { sq->wq = wq; sq->q_id = q_id; sq->owner = 1; sq->msix_entry_idx = sq_msix_idx; sq->cons_idx_addr = cons_idx_addr; sq->db_addr = db_addr; return 0; } static int init_rq(struct hinic_rq *rq, void *dev_hdl, struct hinic_wq *wq, u16 q_id, u16 rq_msix_idx) { rq->wq = wq; rq->q_id = q_id; rq->cqe_dma_addr = 0; rq->msix_entry_idx = rq_msix_idx; rq->pi_virt_addr = dma_alloc_coherent(dev_hdl, PAGE_SIZE, &rq->pi_dma_addr, GFP_KERNEL); if (!rq->pi_virt_addr) return -ENOMEM; return 0; } void hinic_rq_cqe_addr_set(void *hwdev, u16 qid, dma_addr_t cqe_dma_ddr) { struct hinic_hwdev *dev = (struct hinic_hwdev *)hwdev; struct hinic_nic_io *nic_io; nic_io = dev->nic_io; nic_io->qps[qid].rq.cqe_dma_addr = cqe_dma_ddr; } static void clean_rq(struct hinic_rq *rq, void *dev_hdl) { dma_free_coherent(dev_hdl, PAGE_SIZE, rq->pi_virt_addr, rq->pi_dma_addr); } static int create_qp(struct hinic_nic_io *nic_io, struct hinic_qp *qp, u16 q_id, u16 qp_msix_idx, int max_sq_sge) { struct hinic_sq *sq = &qp->sq; struct hinic_rq *rq = &qp->rq; void __iomem *db_addr; int err; err = hinic_wq_allocate(&nic_io->wqs, &nic_io->sq_wq[q_id], HINIC_SQ_WQEBB_SIZE, nic_io->hwdev->wq_page_size, nic_io->sq_depth, MAX_WQE_SIZE(max_sq_sge, HINIC_SQ_WQEBB_SIZE)); if (err) { nic_err(nic_io->hwdev->dev_hdl, "Failed to allocate WQ for SQ\n"); return err; } err = hinic_wq_allocate(&nic_io->wqs, &nic_io->rq_wq[q_id], HINIC_RQ_WQE_SIZE, nic_io->hwdev->wq_page_size, nic_io->rq_depth, HINIC_RQ_WQE_SIZE); if (err) { nic_err(nic_io->hwdev->dev_hdl, "Failed to allocate WQ for RQ\n"); goto rq_alloc_err; } /* we don't use direct wqe for sq */ err = hinic_alloc_db_addr(nic_io->hwdev, &db_addr, NULL); if (err) { nic_err(nic_io->hwdev->dev_hdl, "Failed to alloc sq doorbell addr\n"); goto alloc_db_err; } err = init_sq(sq, &nic_io->sq_wq[q_id], q_id, qp_msix_idx, HINIC_CI_VADDR(nic_io->ci_vaddr_base, q_id), db_addr); if (err != 0) { nic_err(nic_io->hwdev->dev_hdl, "Failed to init sq\n"); goto sq_init_err; } err = init_rq(rq, nic_io->hwdev->dev_hdl, &nic_io->rq_wq[q_id], q_id, qp_msix_idx); if (err) { nic_err(nic_io->hwdev->dev_hdl, "Failed to init rq\n"); goto rq_init_err; } return 0; rq_init_err: sq_init_err: hinic_free_db_addr(nic_io->hwdev, db_addr, NULL); alloc_db_err: hinic_wq_free(&nic_io->wqs, &nic_io->rq_wq[q_id]); rq_alloc_err: hinic_wq_free(&nic_io->wqs, &nic_io->sq_wq[q_id]); return err; } static void destroy_qp(struct hinic_nic_io *nic_io, struct hinic_qp *qp) { clean_rq(&qp->rq, nic_io->hwdev->dev_hdl); hinic_free_db_addr(nic_io->hwdev, qp->sq.db_addr, NULL); hinic_wq_free(&nic_io->wqs, qp->sq.wq); hinic_wq_free(&nic_io->wqs, qp->rq.wq); } /* alloc qps and init qps ctxt */ int hinic_create_qps(void *dev, u16 num_qp, u16 sq_depth, u16 rq_depth, struct irq_info *qps_msix_arry, int max_sq_sge) { struct hinic_hwdev *hwdev = dev; struct hinic_nic_io *nic_io; u16 q_id, i, max_qps; int err; if (!hwdev || !qps_msix_arry) return -EFAULT; max_qps = hinic_func_max_qnum(hwdev); if (num_qp > max_qps) { nic_err(hwdev->dev_hdl, "Create number of qps: %d > max number of qps: %d\n", num_qp, max_qps); return -EINVAL; } nic_io = hwdev->nic_io; nic_io->max_qps = max_qps; nic_io->num_qps = num_qp; nic_io->sq_depth = sq_depth; nic_io->rq_depth = rq_depth; err = hinic_wqs_alloc(&nic_io->wqs, 2 * num_qp, hwdev->dev_hdl); if (err) { nic_err(hwdev->dev_hdl, "Failed to allocate WQS for IO\n"); return err; } nic_io->qps = kcalloc(num_qp, sizeof(*nic_io->qps), GFP_KERNEL); if (!nic_io->qps) { err = -ENOMEM; goto alloc_qps_err; } nic_io->ci_vaddr_base = dma_alloc_coherent(hwdev->dev_hdl, CI_TABLE_SIZE(num_qp, PAGE_SIZE), &nic_io->ci_dma_base, GFP_KERNEL); if (!nic_io->ci_vaddr_base) { err = -ENOMEM; goto ci_base_err; } nic_io->sq_wq = kcalloc(num_qp, sizeof(*nic_io->sq_wq), GFP_KERNEL); if (!nic_io->sq_wq) { err = -ENOMEM; goto sq_wq_err; } nic_io->rq_wq = kcalloc(num_qp, sizeof(*nic_io->rq_wq), GFP_KERNEL); if (!nic_io->rq_wq) { err = -ENOMEM; goto rq_wq_err; } for (q_id = 0; q_id < num_qp; q_id++) { err = create_qp(nic_io, &nic_io->qps[q_id], q_id, qps_msix_arry[q_id].msix_entry_idx, max_sq_sge); if (err) { nic_err(hwdev->dev_hdl, "Failed to allocate qp %d, err: %d\n", q_id, err); goto create_qp_err; } } return 0; create_qp_err: for (i = 0; i < q_id; i++) destroy_qp(nic_io, &nic_io->qps[i]); kfree(nic_io->rq_wq); rq_wq_err: kfree(nic_io->sq_wq); sq_wq_err: dma_free_coherent(hwdev->dev_hdl, CI_TABLE_SIZE(num_qp, PAGE_SIZE), nic_io->ci_vaddr_base, nic_io->ci_dma_base); ci_base_err: kfree(nic_io->qps); alloc_qps_err: hinic_wqs_free(&nic_io->wqs); return err; } EXPORT_SYMBOL(hinic_create_qps); void hinic_free_qps(void *dev) { struct hinic_hwdev *hwdev = dev; struct hinic_nic_io *nic_io; u16 i; if (!hwdev) return; nic_io = hwdev->nic_io; for (i = 0; i < nic_io->num_qps; i++) destroy_qp(nic_io, &nic_io->qps[i]); kfree(nic_io->rq_wq); kfree(nic_io->sq_wq); dma_free_coherent(hwdev->dev_hdl, CI_TABLE_SIZE(nic_io->num_qps, PAGE_SIZE), nic_io->ci_vaddr_base, nic_io->ci_dma_base); kfree(nic_io->qps); hinic_wqs_free(&nic_io->wqs); } EXPORT_SYMBOL(hinic_free_qps); void hinic_qp_prepare_cmdq_header(struct hinic_qp_ctxt_header *qp_ctxt_hdr, enum hinic_qp_ctxt_type ctxt_type, u16 num_queues, u16 max_queues, u16 q_id) { qp_ctxt_hdr->queue_type = ctxt_type; qp_ctxt_hdr->num_queues = num_queues; if (ctxt_type == HINIC_QP_CTXT_TYPE_SQ) qp_ctxt_hdr->addr_offset = SQ_CTXT_OFFSET(max_queues, max_queues, q_id); else qp_ctxt_hdr->addr_offset = RQ_CTXT_OFFSET(max_queues, max_queues, q_id); qp_ctxt_hdr->addr_offset = SIZE_16BYTES(qp_ctxt_hdr->addr_offset); hinic_cpu_to_be32(qp_ctxt_hdr, sizeof(*qp_ctxt_hdr)); } void hinic_sq_prepare_ctxt(struct hinic_sq *sq, u16 global_qpn, struct hinic_sq_ctxt *sq_ctxt) { struct hinic_wq *wq = sq->wq; u64 wq_page_addr; u64 wq_page_pfn, wq_block_pfn; u32 wq_page_pfn_hi, wq_page_pfn_lo; u32 wq_block_pfn_hi, wq_block_pfn_lo; u16 pi_start, ci_start; ci_start = (u16)wq->cons_idx; pi_start = (u16)wq->prod_idx; /* read the first page from the HW table */ wq_page_addr = be64_to_cpu(*wq->block_vaddr); wq_page_pfn = WQ_PAGE_PFN(wq_page_addr); wq_page_pfn_hi = upper_32_bits(wq_page_pfn); wq_page_pfn_lo = lower_32_bits(wq_page_pfn); /* If only one page, use 0-level CLA */ if (wq->num_q_pages == 1) wq_block_pfn = WQ_BLOCK_PFN(wq_page_addr); else wq_block_pfn = WQ_BLOCK_PFN(wq->block_paddr); wq_block_pfn_hi = upper_32_bits(wq_block_pfn); wq_block_pfn_lo = lower_32_bits(wq_block_pfn); sq_ctxt->ceq_attr = SQ_CTXT_CEQ_ATTR_SET(global_qpn, GLOBAL_SQ_ID) | SQ_CTXT_CEQ_ATTR_SET(0, EN); sq_ctxt->ci_owner = SQ_CTXT_CI_SET(ci_start, IDX) | SQ_CTXT_CI_SET(1, OWNER); sq_ctxt->wq_pfn_hi = SQ_CTXT_WQ_PAGE_SET(wq_page_pfn_hi, HI_PFN) | SQ_CTXT_WQ_PAGE_SET(pi_start, PI); sq_ctxt->wq_pfn_lo = wq_page_pfn_lo; sq_ctxt->pref_cache = SQ_CTXT_PREF_SET(WQ_PREFETCH_MIN, CACHE_MIN) | SQ_CTXT_PREF_SET(WQ_PREFETCH_MAX, CACHE_MAX) | SQ_CTXT_PREF_SET(WQ_PREFETCH_THRESHOLD, CACHE_THRESHOLD); sq_ctxt->pref_owner = 1; sq_ctxt->pref_wq_pfn_hi_ci = SQ_CTXT_PREF_SET(ci_start, CI) | SQ_CTXT_PREF_SET(wq_page_pfn_hi, WQ_PFN_HI); sq_ctxt->pref_wq_pfn_lo = wq_page_pfn_lo; sq_ctxt->wq_block_pfn_hi = SQ_CTXT_WQ_BLOCK_SET(wq_block_pfn_hi, PFN_HI); sq_ctxt->wq_block_pfn_lo = wq_block_pfn_lo; hinic_cpu_to_be32(sq_ctxt, sizeof(*sq_ctxt)); } void hinic_rq_prepare_ctxt(struct hinic_rq *rq, struct hinic_rq_ctxt *rq_ctxt) { struct hinic_wq *wq = rq->wq; u64 wq_page_addr; u64 wq_page_pfn, wq_block_pfn; u32 wq_page_pfn_hi, wq_page_pfn_lo; u32 wq_block_pfn_hi, wq_block_pfn_lo; u16 pi_start, ci_start; ci_start = (u16)wq->cons_idx; pi_start = (u16)wq->prod_idx; pi_start = pi_start & wq->mask; /* read the first page from the HW table */ wq_page_addr = be64_to_cpu(*wq->block_vaddr); wq_page_pfn = WQ_PAGE_PFN(wq_page_addr); wq_page_pfn_hi = upper_32_bits(wq_page_pfn); wq_page_pfn_lo = lower_32_bits(wq_page_pfn); if (wq->num_q_pages == 1) wq_block_pfn = WQ_BLOCK_PFN(wq_page_addr); else wq_block_pfn = WQ_BLOCK_PFN(wq->block_paddr); wq_block_pfn_hi = upper_32_bits(wq_block_pfn); wq_block_pfn_lo = lower_32_bits(wq_block_pfn); rq_ctxt->ceq_attr = RQ_CTXT_CEQ_ATTR_SET(0, EN) | RQ_CTXT_CEQ_ATTR_SET(1, OWNER); rq_ctxt->pi_intr_attr = RQ_CTXT_PI_SET(pi_start, IDX) | RQ_CTXT_PI_SET(rq->msix_entry_idx, INTR); rq_ctxt->wq_pfn_hi_ci = RQ_CTXT_WQ_PAGE_SET(wq_page_pfn_hi, HI_PFN) | RQ_CTXT_WQ_PAGE_SET(ci_start, CI); rq_ctxt->wq_pfn_lo = wq_page_pfn_lo; rq_ctxt->pref_cache = RQ_CTXT_PREF_SET(WQ_PREFETCH_MIN, CACHE_MIN) | RQ_CTXT_PREF_SET(WQ_PREFETCH_MAX, CACHE_MAX) | RQ_CTXT_PREF_SET(WQ_PREFETCH_THRESHOLD, CACHE_THRESHOLD); rq_ctxt->pref_owner = 1; rq_ctxt->pref_wq_pfn_hi_ci = RQ_CTXT_PREF_SET(wq_page_pfn_hi, WQ_PFN_HI) | RQ_CTXT_PREF_SET(ci_start, CI); rq_ctxt->pref_wq_pfn_lo = wq_page_pfn_lo; rq_ctxt->pi_paddr_hi = upper_32_bits(rq->pi_dma_addr); rq_ctxt->pi_paddr_lo = lower_32_bits(rq->pi_dma_addr); rq_ctxt->wq_block_pfn_hi = RQ_CTXT_WQ_BLOCK_SET(wq_block_pfn_hi, PFN_HI); rq_ctxt->wq_block_pfn_lo = wq_block_pfn_lo; hinic_cpu_to_be32(rq_ctxt, sizeof(*rq_ctxt)); } static int init_sq_ctxts(struct hinic_nic_io *nic_io) { struct hinic_hwdev *hwdev = nic_io->hwdev; struct hinic_sq_ctxt_block *sq_ctxt_block; struct hinic_sq_ctxt *sq_ctxt; struct hinic_cmd_buf *cmd_buf; struct hinic_qp *qp; u64 out_param = 0; u16 q_id, curr_id, global_qpn, max_ctxts, i; int err = 0; cmd_buf = hinic_alloc_cmd_buf(hwdev); if (!cmd_buf) { nic_err(hwdev->dev_hdl, "Failed to allocate cmd buf\n"); return -ENOMEM; } q_id = 0; while (q_id < nic_io->num_qps) { sq_ctxt_block = cmd_buf->buf; sq_ctxt = sq_ctxt_block->sq_ctxt; max_ctxts = (nic_io->num_qps - q_id) > HINIC_Q_CTXT_MAX ? HINIC_Q_CTXT_MAX : (nic_io->num_qps - q_id); hinic_qp_prepare_cmdq_header(&sq_ctxt_block->cmdq_hdr, HINIC_QP_CTXT_TYPE_SQ, max_ctxts, nic_io->max_qps, q_id); for (i = 0; i < max_ctxts; i++) { curr_id = q_id + i; qp = &nic_io->qps[curr_id]; global_qpn = nic_io->global_qpn + curr_id; hinic_sq_prepare_ctxt(&qp->sq, global_qpn, &sq_ctxt[i]); } cmd_buf->size = SQ_CTXT_SIZE(max_ctxts); err = hinic_cmdq_direct_resp(hwdev, HINIC_ACK_TYPE_CMDQ, HINIC_MOD_L2NIC, HINIC_UCODE_CMD_MODIFY_QUEUE_CONTEXT, cmd_buf, &out_param, 0); if (err || out_param != 0) { nic_err(hwdev->dev_hdl, "Failed to set SQ ctxts, err: %d, out_param: 0x%llx\n", err, out_param); err = -EFAULT; break; } q_id += max_ctxts; } hinic_free_cmd_buf(hwdev, cmd_buf); return err; } static int init_rq_ctxts(struct hinic_nic_io *nic_io) { struct hinic_hwdev *hwdev = nic_io->hwdev; struct hinic_rq_ctxt_block *rq_ctxt_block; struct hinic_rq_ctxt *rq_ctxt; struct hinic_cmd_buf *cmd_buf; struct hinic_qp *qp; u64 out_param = 0; u16 q_id, curr_id, max_ctxts, i; int err = 0; cmd_buf = hinic_alloc_cmd_buf(hwdev); if (!cmd_buf) { nic_err(hwdev->dev_hdl, "Failed to allocate cmd buf\n"); return -ENOMEM; } q_id = 0; while (q_id < nic_io->num_qps) { rq_ctxt_block = cmd_buf->buf; rq_ctxt = rq_ctxt_block->rq_ctxt; max_ctxts = (nic_io->num_qps - q_id) > HINIC_Q_CTXT_MAX ? HINIC_Q_CTXT_MAX : (nic_io->num_qps - q_id); hinic_qp_prepare_cmdq_header(&rq_ctxt_block->cmdq_hdr, HINIC_QP_CTXT_TYPE_RQ, max_ctxts, nic_io->max_qps, q_id); for (i = 0; i < max_ctxts; i++) { curr_id = q_id + i; qp = &nic_io->qps[curr_id]; hinic_rq_prepare_ctxt(&qp->rq, &rq_ctxt[i]); } cmd_buf->size = RQ_CTXT_SIZE(max_ctxts); err = hinic_cmdq_direct_resp(hwdev, HINIC_ACK_TYPE_CMDQ, HINIC_MOD_L2NIC, HINIC_UCODE_CMD_MODIFY_QUEUE_CONTEXT, cmd_buf, &out_param, 0); if (err || out_param != 0) { nic_err(hwdev->dev_hdl, "Failed to set RQ ctxts, err: %d, out_param: 0x%llx\n", err, out_param); err = -EFAULT; break; } q_id += max_ctxts; } hinic_free_cmd_buf(hwdev, cmd_buf); return err; } static int init_qp_ctxts(struct hinic_nic_io *nic_io) { int err; err = init_sq_ctxts(nic_io); if (err) return err; err = init_rq_ctxts(nic_io); if (err) return err; return 0; } static int clean_queue_offload_ctxt(struct hinic_nic_io *nic_io, enum hinic_qp_ctxt_type ctxt_type) { struct hinic_hwdev *hwdev = nic_io->hwdev; struct hinic_clean_queue_ctxt *ctxt_block; struct hinic_cmd_buf *cmd_buf; dma_addr_t cqe_dma_addr; struct hinic_addr *addr; u64 out_param = 0; int i, err; cmd_buf = hinic_alloc_cmd_buf(hwdev); if (!cmd_buf) { nic_err(hwdev->dev_hdl, "Failed to allocate cmd buf\n"); return -ENOMEM; } ctxt_block = cmd_buf->buf; ctxt_block->cmdq_hdr.num_queues = nic_io->max_qps; ctxt_block->cmdq_hdr.queue_type = ctxt_type; ctxt_block->cmdq_hdr.addr_offset = 0; /* TSO/LRO ctxt size: 0x0:0B; 0x1:160B; 0x2:200B; 0x3:240B */ ctxt_block->ctxt_size = 0x3; if ((hinic_func_type(hwdev) == TYPE_VF) && ctxt_type == HINIC_QP_CTXT_TYPE_RQ) { addr = ctxt_block->cqe_dma_addr; for (i = 0; i < nic_io->max_qps; i++) { cqe_dma_addr = nic_io->qps[i].rq.cqe_dma_addr; addr[i].addr_hi = upper_32_bits(cqe_dma_addr); addr[i].addr_lo = lower_32_bits(cqe_dma_addr); } } hinic_cpu_to_be32(ctxt_block, sizeof(*ctxt_block)); cmd_buf->size = sizeof(*ctxt_block); err = hinic_cmdq_direct_resp(hwdev, HINIC_ACK_TYPE_CMDQ, HINIC_MOD_L2NIC, HINIC_UCODE_CMD_CLEAN_QUEUE_CONTEXT, cmd_buf, &out_param, 0); if ((err) || (out_param)) { nic_err(hwdev->dev_hdl, "Failed to clean queue offload ctxts, err: %d, out_param: 0x%llx\n", err, out_param); err = -EFAULT; } hinic_free_cmd_buf(hwdev, cmd_buf); return err; } static int clean_qp_offload_ctxt(struct hinic_nic_io *nic_io) { /* clean LRO/TSO context space */ return (clean_queue_offload_ctxt(nic_io, HINIC_QP_CTXT_TYPE_SQ) || clean_queue_offload_ctxt(nic_io, HINIC_QP_CTXT_TYPE_RQ)); } /* init qps ctxt and set sq ci attr and arm all sq */ int hinic_init_qp_ctxts(void *dev) { struct hinic_hwdev *hwdev = dev; struct hinic_nic_io *nic_io; struct hinic_sq_attr sq_attr; u16 q_id; int err; if (!hwdev) return -EINVAL; nic_io = hwdev->nic_io; err = init_qp_ctxts(nic_io); if (err) { nic_err(hwdev->dev_hdl, "Failed to init QP ctxts\n"); return err; } /* clean LRO/TSO context space */ err = clean_qp_offload_ctxt(nic_io); if (err) { nic_err(hwdev->dev_hdl, "Failed to clean qp offload ctxts\n"); return err; } err = hinic_set_root_ctxt(hwdev, nic_io->rq_depth, nic_io->sq_depth, nic_io->rx_buff_len); if (err) { nic_err(hwdev->dev_hdl, "Failed to set root context\n"); return err; } for (q_id = 0; q_id < nic_io->num_qps; q_id++) { sq_attr.ci_dma_base = HINIC_CI_PADDR(nic_io->ci_dma_base, q_id) >> 2; sq_attr.pending_limit = tx_pending_limit; sq_attr.coalescing_time = tx_coalescing_time; sq_attr.intr_en = 1; sq_attr.intr_idx = nic_io->qps[q_id].sq.msix_entry_idx; sq_attr.l2nic_sqn = q_id; sq_attr.dma_attr_off = 0; err = hinic_set_ci_table(hwdev, q_id, &sq_attr); if (err) { nic_err(hwdev->dev_hdl, "Failed to set ci table\n"); goto set_cons_idx_table_err; } } return 0; set_cons_idx_table_err: hinic_clean_root_ctxt(hwdev); return err; } EXPORT_SYMBOL(hinic_init_qp_ctxts); void hinic_free_qp_ctxts(void *hwdev) { int err; if (!hwdev) return; hinic_qps_num_set(hwdev, 0); err = hinic_clean_root_ctxt(hwdev); if (err) nic_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to clean root ctxt\n"); } EXPORT_SYMBOL(hinic_free_qp_ctxts); int hinic_init_nic_hwdev(void *hwdev, u16 rx_buff_len) { struct hinic_hwdev *dev = hwdev; struct hinic_nic_io *nic_io; u16 global_qpn; int err; if (!hwdev) return -EINVAL; if (is_multi_bm_slave(hwdev) && hinic_support_dynamic_q(hwdev)) { err = hinic_reinit_cmdq_ctxts(dev); if (err) { nic_err(dev->dev_hdl, "Failed to reinit cmdq\n"); return err; } } nic_io = dev->nic_io; err = hinic_get_base_qpn(hwdev, &global_qpn); if (err) { nic_err(dev->dev_hdl, "Failed to get base qpn\n"); return err; } nic_io->global_qpn = global_qpn; nic_io->rx_buff_len = rx_buff_len; err = hinic_init_function_table(hwdev, nic_io->rx_buff_len); if (err) { nic_err(dev->dev_hdl, "Failed to init function table\n"); return err; } err = hinic_enable_fast_recycle(hwdev, false); if (err) { nic_err(dev->dev_hdl, "Failed to disable fast recycle\n"); return err; } /* get default pf bandwidth from firmware witch setted by bios */ err = hinic_get_bios_pf_bw_limit(hwdev, &nic_io->nic_cfg.pf_bw_limit); if (err) { nic_err(dev->dev_hdl, "Failed to get pf bandwidth limit\n"); return err; } if (dev->func_mode == FUNC_MOD_MULTI_BM_MASTER || dev->func_mode == FUNC_MOD_MULTI_VM_MASTER) { if (hinic_func_type(dev) != TYPE_VF) { err = hinic_disable_tx_promisc(dev); if (err) { nic_err(dev->dev_hdl, "Failed to set tx promisc\n"); return err; } } } /* VFs don't set port routine command report */ if (hinic_func_type(dev) != TYPE_VF) { /* Get the fw support mac reuse flag */ err = hinic_get_fw_support_func(hwdev); if (err) { nic_err(dev->dev_hdl, "Failed to get function capability\n"); return err; } /* Inform mgmt to send sfp's information to driver */ err = hinic_set_port_routine_cmd_report(hwdev, true); } return err; } EXPORT_SYMBOL(hinic_init_nic_hwdev); void hinic_free_nic_hwdev(void *hwdev) { if (hinic_func_type(hwdev) != TYPE_VF) hinic_set_port_routine_cmd_report(hwdev, false); } EXPORT_SYMBOL(hinic_free_nic_hwdev); int hinic_enable_tx_irq(void *hwdev, u16 q_id) { return hinic_set_arm_bit(hwdev, HINIC_SET_ARM_SQ, q_id); } EXPORT_SYMBOL(hinic_enable_tx_irq); int hinic_rx_tx_flush(void *hwdev) { return hinic_func_rx_tx_flush(hwdev); } EXPORT_SYMBOL(hinic_rx_tx_flush); int hinic_get_sq_free_wqebbs(void *hwdev, u16 q_id) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct hinic_wq *wq = &nic_io->sq_wq[q_id]; return atomic_read(&wq->delta) - 1; } EXPORT_SYMBOL(hinic_get_sq_free_wqebbs); int hinic_get_rq_free_wqebbs(void *hwdev, u16 q_id) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; return nic_io->rq_wq[q_id].delta.counter - 1; } EXPORT_SYMBOL(hinic_get_rq_free_wqebbs); u16 hinic_get_sq_local_ci(void *hwdev, u16 q_id) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; return (u16)(nic_io->sq_wq[q_id].cons_idx & nic_io->sq_wq[q_id].mask); } EXPORT_SYMBOL(hinic_get_sq_local_ci); u16 hinic_get_sq_hw_ci(void *hwdev, u16 q_id) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct hinic_sq *sq = &nic_io->qps[q_id].sq; return MASKED_SQ_IDX(sq, be16_to_cpu(*(u16 *)(sq->cons_idx_addr))); } EXPORT_SYMBOL(hinic_get_sq_hw_ci); void *hinic_get_sq_wqe(void *hwdev, u16 q_id, int wqebb_cnt, u16 *pi, u8 *owner) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct hinic_sq *sq = &nic_io->qps[q_id].sq; void *wqe; wqe = hinic_get_wqe(sq->wq, wqebb_cnt, pi); if (wqe) { *owner = sq->owner; if ((*pi + wqebb_cnt) >= nic_io->sq_depth) sq->owner = !sq->owner; } return wqe; } EXPORT_SYMBOL(hinic_get_sq_wqe); void hinic_return_sq_wqe(void *hwdev, u16 q_id, int num_wqebbs, u8 owner) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct hinic_sq *sq = &nic_io->qps[q_id].sq; if (owner != sq->owner) sq->owner = owner; atomic_add(num_wqebbs, &sq->wq->delta); sq->wq->prod_idx -= num_wqebbs; } EXPORT_SYMBOL(hinic_return_sq_wqe); void hinic_update_sq_pi(void *hwdev, u16 q_id, int num_wqebbs, u16 *pi, u8 *owner) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct hinic_sq *sq = &nic_io->qps[q_id].sq; *pi = MASKED_WQE_IDX(sq->wq, sq->wq->prod_idx); atomic_sub(num_wqebbs, &sq->wq->delta); sq->wq->prod_idx += num_wqebbs; *owner = sq->owner; if ((*pi + num_wqebbs) >= nic_io->sq_depth) sq->owner = !sq->owner; } EXPORT_SYMBOL(hinic_update_sq_pi); static void sq_prepare_db(struct hinic_sq *sq, struct hinic_sq_db *db, u16 prod_idx, int cos) { u32 hi_prod_idx = SQ_DB_PI_HIGH(MASKED_SQ_IDX(sq, prod_idx)); db->db_info = SQ_DB_INFO_SET(hi_prod_idx, HI_PI) | SQ_DB_INFO_SET(SQ_DB, TYPE) | SQ_DB_INFO_SET(CFLAG_DATA_PATH, CFLAG) | SQ_DB_INFO_SET(cos, COS) | SQ_DB_INFO_SET(sq->q_id, QID); } static void sq_write_db(struct hinic_sq *sq, u16 prod_idx, int cos) { struct hinic_sq_db sq_db; sq_prepare_db(sq, &sq_db, prod_idx, cos); /* Data should be written to HW in Big Endian Format */ sq_db.db_info = cpu_to_be32(sq_db.db_info); wmb(); /* Write all before the doorbell */ writel(sq_db.db_info, SQ_DB_ADDR(sq, prod_idx)); } void hinic_send_sq_wqe(void *hwdev, u16 q_id, void *wqe, int wqebb_cnt, int cos) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct hinic_sq *sq = &nic_io->qps[q_id].sq; if (wqebb_cnt != 1) hinic_write_wqe(sq->wq, wqe, wqebb_cnt); sq_write_db(sq, MASKED_SQ_IDX(sq, sq->wq->prod_idx), cos); } EXPORT_SYMBOL(hinic_send_sq_wqe); void hinic_update_sq_local_ci(void *hwdev, u16 q_id, int wqebb_cnt) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct hinic_sq *sq = &nic_io->qps[q_id].sq; sq->wq->cons_idx += wqebb_cnt; atomic_add(wqebb_cnt, &sq->wq->delta); } EXPORT_SYMBOL(hinic_update_sq_local_ci); void *hinic_get_rq_wqe(void *hwdev, u16 q_id, u16 *pi) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct hinic_rq *rq = &nic_io->qps[q_id].rq; return hinic_get_wqe(rq->wq, 1, pi); } EXPORT_SYMBOL(hinic_get_rq_wqe); void hinic_return_rq_wqe(void *hwdev, u16 q_id, int num_wqebbs) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct hinic_rq *rq = &nic_io->qps[q_id].rq; atomic_add(num_wqebbs, &rq->wq->delta); rq->wq->prod_idx -= num_wqebbs; } EXPORT_SYMBOL(hinic_return_rq_wqe); void hinic_update_rq_delta(void *hwdev, u16 q_id, int num_wqebbs) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; nic_io->qps[q_id].rq.wq->delta.counter -= num_wqebbs; } EXPORT_SYMBOL(hinic_update_rq_delta); void hinic_update_rq_hw_pi(void *hwdev, u16 q_id, u16 pi) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; struct hinic_rq *rq = &nic_io->qps[q_id].rq; *rq->pi_virt_addr = cpu_to_be16(pi & rq->wq->mask); } EXPORT_SYMBOL(hinic_update_rq_hw_pi); u16 hinic_get_rq_local_ci(void *hwdev, u16 q_id) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; return (u16)(nic_io->rq_wq[q_id].cons_idx & nic_io->rq_wq[q_id].mask); } EXPORT_SYMBOL(hinic_get_rq_local_ci); void hinic_update_rq_local_ci(void *hwdev, u16 q_id, int wqe_cnt) { struct hinic_nic_io *nic_io = ((struct hinic_hwdev *)hwdev)->nic_io; nic_io->qps[q_id].rq.wq->cons_idx += wqe_cnt; nic_io->qps[q_id].rq.wq->delta.counter += wqe_cnt; } EXPORT_SYMBOL(hinic_update_rq_local_ci); hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_nic_io.h000066400000000000000000000061531502027332500217170ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_HW_NIC_IO_H_ #define HINIC_HW_NIC_IO_H_ #include "hinic_hw_mgmt.h" #include "hinic_qe_def.h" #define HINIC_RX_BUF_SHIFT 11 #define HINIC_RX_BUF_LEN 2048 /* buffer len must be 2^n */ #define SQ_CTRL_SET(val, member) ((u32)(val) << SQ_CTRL_##member##_SHIFT) int hinic_init_nic_hwdev(void *hwdev, u16 rx_buff_len); void hinic_free_nic_hwdev(void *hwdev); /* alloc qps resource */ int hinic_create_qps(void *hwdev, u16 qp_num, u16 sq_depth, u16 rq_depth, struct irq_info *rq_msix_arry, int max_sq_sge); void hinic_free_qps(void *hwdev); /* init qps ctxt and set sq ci attr and arm all sq */ int hinic_init_qp_ctxts(void *hwdev); void hinic_free_qp_ctxts(void *hwdev); /* function table and root context set */ int hinic_set_parameters(void *hwdev, u8 *mac, u16 rx_buf_size, u32 mtu); void hinic_clear_parameters(void *hwdev); /* The function is internally invoked. set_arm_bit function */ int hinic_enable_tx_irq(void *hwdev, u16 q_id); int hinic_rx_tx_flush(void *hwdev); /* Obtain sq/rq number of idle wqebb */ int hinic_get_sq_free_wqebbs(void *hwdev, u16 q_id); int hinic_get_rq_free_wqebbs(void *hwdev, u16 q_id); u16 hinic_get_sq_local_ci(void *hwdev, u16 q_id); u16 hinic_get_sq_hw_ci(void *hwdev, u16 q_id); void *hinic_get_sq_wqe(void *hwdev, u16 q_id, int wqebb_cnt, u16 *pi, u8 *owner); void hinic_return_sq_wqe(void *hwdev, u16 q_id, int num_wqebbs, u8 owner); void hinic_update_sq_pi(void *hwdev, u16 q_id, int num_wqebbs, u16 *pi, u8 *owner); /* including cross-page process and press the doorbell */ void hinic_send_sq_wqe(void *hwdev, u16 q_id, void *wqe, int wqebb_cnt, int cos); void hinic_update_sq_local_ci(void *hwdev, u16 q_id, int wqebb_cnt); /* Refreshes the rq buff */ void *hinic_get_rq_wqe(void *hwdev, u16 q_id, u16 *pi); /* gupdate rq pi, is the latest pi, function does not need to calculate */ void hinic_return_rq_wqe(void *hwdev, u16 q_id, int num_wqebbs); void hinic_update_rq_delta(void *hwdev, u16 q_id, int num_wqebbs); void hinic_update_rq_hw_pi(void *hwdev, u16 q_id, u16 pi); u16 hinic_get_rq_local_ci(void *hwdev, u16 q_id); /* Clear rx done is not performed */ void hinic_update_rq_local_ci(void *hwdev, u16 q_id, int wqe_cnt); struct hinic_sge { u32 hi_addr; u32 lo_addr; u32 len; }; void hinic_cpu_to_be32(void *data, int len); void hinic_be32_to_cpu(void *data, int len); void hinic_set_sge(struct hinic_sge *sge, dma_addr_t addr, u32 len); dma_addr_t hinic_sge_to_dma(struct hinic_sge *sge); void hinic_rq_cqe_addr_set(void *hwdev, u16 qid, dma_addr_t cqe_dma_ddr); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_nictool.c000066400000000000000000001700241502027332500221200ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_lld.h" #include "hinic_nic_dev.h" #include "hinic_dbg.h" #include "hinic_nictool.h" #include "hinic_qp.h" #include "hinic_dcb.h" #include "hinic_dbgtool_knl.h" #define HIADM_DEV_PATH "/dev/nictool_dev" #define HIADM_DEV_CLASS "nictool_class" #define HIADM_DEV_NAME "nictool_dev" #define HINIC_CMDQ_BUF_MAX_SIZE 2048U #define MSG_MAX_IN_SIZE (2048 * 1024) #define MSG_MAX_OUT_SIZE (2048 * 1024) static dev_t g_dev_id = {0}; /*lint -save -e104 -e808*/ static struct class *g_nictool_class; /*lint -restore*/ static struct cdev g_nictool_cdev; static int g_nictool_init_flag; static int g_nictool_ref_cnt; typedef int (*nic_driv_module)(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size); struct nic_drv_module_handle { enum driver_cmd_type driv_cmd_name; nic_driv_module driv_func; }; typedef int (*hw_driv_module)(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size); struct hw_drv_module_handle { enum driver_cmd_type driv_cmd_name; hw_driv_module driv_func; }; static void free_buff_in(void *hwdev, struct msg_module *nt_msg, void *buf_in) { if (!buf_in) return; if (nt_msg->module == SEND_TO_UCODE) hinic_free_cmd_buf(hwdev, buf_in); else kfree(buf_in); } static int alloc_buff_in(void *hwdev, struct msg_module *nt_msg, u32 in_size, void **buf_in) { void *msg_buf; if (!in_size) return 0; if (nt_msg->module == SEND_TO_UCODE) { struct hinic_cmd_buf *cmd_buf; if (in_size > HINIC_CMDQ_BUF_MAX_SIZE) { pr_err("Cmdq in size(%u) more than 2KB\n", in_size); return -ENOMEM; } cmd_buf = hinic_alloc_cmd_buf(hwdev); if (!cmd_buf) { pr_err("Alloc cmdq cmd buffer failed in %s\n", __func__); return -ENOMEM; } msg_buf = cmd_buf->buf; *buf_in = (void *)cmd_buf; cmd_buf->size = (u16)in_size; } else { if (in_size > MSG_MAX_IN_SIZE) { pr_err("In size(%u) more than 2M\n", in_size); return -ENOMEM; } msg_buf = kzalloc(in_size, GFP_KERNEL); *buf_in = msg_buf; } if (!(*buf_in)) { pr_err("Alloc buffer in failed\n"); return -ENOMEM; } if (copy_from_user(msg_buf, nt_msg->in_buff, in_size)) { pr_err("%s:%d: Copy from user failed\n", __func__, __LINE__); free_buff_in(hwdev, nt_msg, *buf_in); return -EFAULT; } return 0; } static void free_buff_out(void *hwdev, struct msg_module *nt_msg, void *buf_out) { if (!buf_out) return; if (nt_msg->module == SEND_TO_UCODE && !nt_msg->ucode_cmd.ucode_db.ucode_imm) hinic_free_cmd_buf(hwdev, buf_out); else kfree(buf_out); } static int alloc_buff_out(void *hwdev, struct msg_module *nt_msg, u32 out_size, void **buf_out) { if (!out_size) return 0; if (nt_msg->module == SEND_TO_UCODE && !nt_msg->ucode_cmd.ucode_db.ucode_imm) { struct hinic_cmd_buf *cmd_buf; if (out_size > HINIC_CMDQ_BUF_MAX_SIZE) { pr_err("Cmdq out size(%u) more than 2KB\n", out_size); return -ENOMEM; } cmd_buf = hinic_alloc_cmd_buf(hwdev); *buf_out = (void *)cmd_buf; } else { if (out_size > MSG_MAX_OUT_SIZE) { pr_err("out size(%u) more than 2M\n", out_size); return -ENOMEM; } *buf_out = kzalloc(out_size, GFP_KERNEL); } if (!(*buf_out)) { pr_err("Alloc buffer out failed\n"); return -ENOMEM; } return 0; } static int copy_buf_out_to_user(struct msg_module *nt_msg, u32 out_size, void *buf_out) { int ret = 0; void *msg_out; if (nt_msg->module == SEND_TO_UCODE && !nt_msg->ucode_cmd.ucode_db.ucode_imm) msg_out = ((struct hinic_cmd_buf *)buf_out)->buf; else msg_out = buf_out; if (copy_to_user(nt_msg->out_buf, msg_out, out_size)) ret = -EFAULT; return ret; } static int hinic_dbg_get_sq_info(struct hinic_nic_dev *nic_dev, u16 q_id, struct hinic_dbg_sq_info *sq_info, u32 *msg_size); static int hinic_dbg_get_rq_info(struct hinic_nic_dev *nic_dev, u16 q_id, struct hinic_dbg_rq_info *rq_info, u32 *msg_size); static int get_tx_info(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u16 q_id; int err; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get tx info\n"); return -EFAULT; } if (!buf_in || !buf_out || in_size != sizeof(int)) return -EINVAL; q_id = *((u16 *)buf_in); err = hinic_dbg_get_sq_info(nic_dev, q_id, buf_out, out_size); return err; } static int get_q_num(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u16 num_qp; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get queue number\n"); return -EFAULT; } if (!buf_out) return -EFAULT; num_qp = hinic_dbg_get_qp_num(nic_dev->hwdev); if (!num_qp) return -EFAULT; if (*out_size != sizeof(u16)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(u16)); return -EFAULT; } *((u16 *)buf_out) = num_qp; return 0; } static int get_tx_wqe_info(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_wqe_info *info = buf_in; u16 q_id = 0; u16 idx = 0, wqebb_cnt = 1; int err; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get tx wqe info\n"); return -EFAULT; } if (!info || !buf_out || in_size != sizeof(*info)) return -EFAULT; q_id = (u16)info->q_id; idx = (u16)info->wqe_id; err = hinic_dbg_get_sq_wqe_info(nic_dev->hwdev, q_id, idx, wqebb_cnt, buf_out, (u16 *)out_size); return err; } static int get_rx_info(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u16 q_id; int err; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get rx info\n"); return -EFAULT; } if (!buf_in || !buf_out || in_size != sizeof(int)) return -EINVAL; q_id = *((u16 *)buf_in); err = hinic_dbg_get_rq_info(nic_dev, q_id, buf_out, out_size); for (q_id = 0; q_id < nic_dev->num_qps; q_id++) { nicif_info(nic_dev, drv, nic_dev->netdev, "qid: %u, coalesc_timer:0x%x, pending_limit: 0x%x\n", q_id, nic_dev->rxqs[q_id].last_coalesc_timer_cfg, nic_dev->rxqs[q_id].last_pending_limt); } return err; } static int get_rx_wqe_info(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_wqe_info *info = buf_in; u16 q_id = 0; u16 idx = 0, wqebb_cnt = 1; int err; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get rx wqe info\n"); return -EFAULT; } if (!info || !buf_out || in_size != sizeof(*info)) return -EFAULT; q_id = (u16)info->q_id; idx = (u16)info->wqe_id; err = hinic_dbg_get_rq_wqe_info(nic_dev->hwdev, q_id, idx, wqebb_cnt, buf_out, (u16 *)out_size); return err; } static int get_inter_num(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u16 intr_num; intr_num = hinic_intr_num(nic_dev->hwdev); if (*out_size != sizeof(u16)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(u16)); return -EFAULT; } *(u16 *)buf_out = intr_num; return 0; } static void clean_nicdev_stats(struct hinic_nic_dev *nic_dev) { u64_stats_update_begin(&nic_dev->stats.syncp); nic_dev->stats.netdev_tx_timeout = 0; nic_dev->stats.tx_carrier_off_drop = 0; nic_dev->stats.tx_invalid_qid = 0; u64_stats_update_end(&nic_dev->stats.syncp); } static int clear_func_static(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { int i; if (*out_size != 0) { pr_err("Unexpect out buf size from user: %d, expect: 0\n", *out_size); return -EINVAL; } clean_nicdev_stats(nic_dev); for (i = 0; i < nic_dev->max_qps; i++) { hinic_rxq_clean_stats(&nic_dev->rxqs[i].rxq_stats); hinic_txq_clean_stats(&nic_dev->txqs[i].txq_stats); } return 0; } static int get_num_cos(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u8 *num_cos = buf_out; if (!buf_out || !out_size) return -EINVAL; if (*out_size != sizeof(*num_cos)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*num_cos)); return -EFAULT; } return hinic_get_num_cos(nic_dev, num_cos); } static int get_dcb_cos_up_map(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_cos_up_map *map = buf_out; if (!buf_out || !out_size) return -EINVAL; if (*out_size != sizeof(*map)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*map)); return -EFAULT; } return hinic_get_cos_up_map(nic_dev, &map->num_cos, map->cos_up); } static int set_dcb_cos_up_map(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_cos_up_map *map = buf_in; if (!buf_in || !out_size || in_size != sizeof(*map)) return -EINVAL; if (*out_size != sizeof(*map)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*map)); return -EINVAL; } return hinic_set_cos_up_map(nic_dev, map->cos_up); } static int get_rx_cqe_info(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_wqe_info *info = buf_in; u16 q_id = 0; u16 idx = 0; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get rx cqe info\n"); return -EFAULT; } if (!info || !buf_out || in_size != sizeof(*info)) return -EFAULT; if (*out_size != sizeof(struct hinic_rq_cqe)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(struct hinic_rq_cqe)); return -EFAULT; } q_id = (u16)info->q_id; idx = (u16)info->wqe_id; if (q_id >= nic_dev->num_qps || idx >= nic_dev->rxqs[q_id].q_depth) return -EFAULT; memcpy(buf_out, nic_dev->rxqs[q_id].rx_info[idx].cqe, sizeof(struct hinic_rq_cqe)); return 0; } static int hinic_dbg_get_sq_info(struct hinic_nic_dev *nic_dev, u16 q_id, struct hinic_dbg_sq_info *sq_info, u32 *msg_size) { int err; if (!nic_dev) return -EINVAL; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get sq info\n"); return -EFAULT; } if (q_id >= nic_dev->num_qps) { nicif_err(nic_dev, drv, nic_dev->netdev, "Input queue id is larger than the actual queue number\n"); return -EINVAL; } if (*msg_size != sizeof(*sq_info)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *msg_size, sizeof(*sq_info)); return -EFAULT; } sq_info->q_id = q_id; sq_info->pi = hinic_dbg_get_sq_pi(nic_dev->hwdev, q_id); sq_info->ci = hinic_get_sq_local_ci(nic_dev->hwdev, q_id); sq_info->fi = hinic_get_sq_hw_ci(nic_dev->hwdev, q_id); sq_info->q_depth = nic_dev->txqs[q_id].q_depth; /* pi_reverse */ sq_info->weqbb_size = HINIC_SQ_WQEBB_SIZE; /* priority */ sq_info->ci_addr = hinic_dbg_get_sq_ci_addr(nic_dev->hwdev, q_id); sq_info->cla_addr = hinic_dbg_get_sq_cla_addr(nic_dev->hwdev, q_id); sq_info->slq_handle = hinic_dbg_get_sq_wq_handle(nic_dev->hwdev, q_id); /* direct wqe */ err = hinic_dbg_get_sq_db_addr(nic_dev->hwdev, q_id, &sq_info->db_addr.map_addr, &sq_info->db_addr.phy_addr, &sq_info->pg_idx); sq_info->glb_sq_id = hinic_dbg_get_global_qpn(nic_dev->hwdev) + q_id; return err; } static int hinic_dbg_get_rq_info(struct hinic_nic_dev *nic_dev, u16 q_id, struct hinic_dbg_rq_info *rq_info, u32 *msg_size) { if (!nic_dev) return -EINVAL; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get rq info\n"); return -EFAULT; } if (q_id >= nic_dev->num_qps) { nicif_err(nic_dev, drv, nic_dev->netdev, "Input queue id is larger than the actual queue number\n"); return -EINVAL; } if (*msg_size != sizeof(*rq_info)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *msg_size, sizeof(*rq_info)); return -EFAULT; } rq_info->q_id = q_id; rq_info->glb_rq_id = hinic_dbg_get_global_qpn(nic_dev->hwdev) + q_id; rq_info->hw_pi = hinic_dbg_get_rq_hw_pi(nic_dev->hwdev, q_id); rq_info->ci = (u16)nic_dev->rxqs[q_id].cons_idx & nic_dev->rxqs[q_id].q_mask; rq_info->sw_pi = nic_dev->rxqs[q_id].next_to_update; rq_info->wqebb_size = HINIC_RQ_WQE_SIZE; rq_info->q_depth = nic_dev->rxqs[q_id].q_depth; rq_info->buf_len = nic_dev->rxqs[q_id].buf_len; rq_info->slq_handle = hinic_dbg_get_rq_wq_handle(nic_dev->hwdev, q_id); if (!rq_info->slq_handle) { nicif_err(nic_dev, drv, nic_dev->netdev, "Get rq slq handle null\n"); return -EFAULT; } rq_info->ci_wqe_page_addr = hinic_slq_get_first_pageaddr(rq_info->slq_handle); rq_info->ci_cla_tbl_addr = hinic_dbg_get_rq_cla_addr(nic_dev->hwdev, q_id); rq_info->msix_idx = nic_dev->rxqs[q_id].msix_entry_idx; rq_info->msix_vector = nic_dev->rxqs[q_id].irq_id; return 0; } static int get_loopback_mode(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_nic_loop_mode *mode = buf_out; int err; if (!out_size || !mode) return -EFAULT; if (*out_size != sizeof(*mode)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*mode)); return -EFAULT; } err = hinic_get_loopback_mode_ex(nic_dev->hwdev, &mode->loop_mode, &mode->loop_ctrl); return err; } static int set_loopback_mode(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_nic_loop_mode *mode = buf_in; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't set loopback mode\n"); return -EFAULT; } if (!mode || !out_size || in_size != sizeof(*mode)) return -EFAULT; if (*out_size != sizeof(*mode)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*mode)); return -EINVAL; } return hinic_set_loopback_mode_ex(nic_dev->hwdev, mode->loop_mode, mode->loop_ctrl); } static int set_link_mode(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { enum hinic_nic_link_mode *link = buf_in; u8 link_status; if (!test_bit(HINIC_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't set link mode\n"); return -EFAULT; } if (!link || !out_size || in_size != sizeof(*link)) return -EFAULT; if (*out_size != sizeof(*link)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*link)); return -EINVAL; } switch (*link) { case HINIC_LINK_MODE_AUTO: if (hinic_get_link_state(nic_dev->hwdev, &link_status)) link_status = false; hinic_link_status_change(nic_dev, (bool)link_status); nicif_info(nic_dev, drv, nic_dev->netdev, "Set link mode: auto succeed, now is link %s\n", (link_status ? "up" : "down")); break; case HINIC_LINK_MODE_UP: hinic_link_status_change(nic_dev, true); nicif_info(nic_dev, drv, nic_dev->netdev, "Set link mode: up succeed\n"); break; case HINIC_LINK_MODE_DOWN: hinic_link_status_change(nic_dev, false); nicif_info(nic_dev, drv, nic_dev->netdev, "Set link mode: down succeed\n"); break; default: nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid link mode %d to set\n", *link); return -EINVAL; } return 0; } static int set_dcb_cfg(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { union _dcb_ctl dcb_ctl = {.data = 0}; int err; if (!buf_in || !buf_out || *out_size != sizeof(u32) || in_size != sizeof(u32)) return -EINVAL; dcb_ctl.data = *((u32 *)buf_in); err = hinic_setup_dcb_tool(nic_dev->netdev, &dcb_ctl.dcb_data.dcb_en, !!dcb_ctl.dcb_data.wr_flag); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to setup dcb state to %d\n", !!dcb_ctl.dcb_data.dcb_en); err = EINVAL; } dcb_ctl.dcb_data.err = (u8)err; *((u32 *)buf_out) = (u32)dcb_ctl.data; return 0; } int get_pfc_info(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { union _pfc pfc = {.data = 0}; if (!buf_in || !buf_out || *out_size != sizeof(u32) || in_size != sizeof(u32)) return -EINVAL; pfc.data = *((u32 *)buf_in); hinic_dcbnl_set_pfc_en_tool(nic_dev->netdev, &pfc.pfc_data.pfc_en, false); hinic_dcbnl_get_pfc_cfg_tool(nic_dev->netdev, &pfc.pfc_data.pfc_priority); hinic_dcbnl_get_tc_num_tool(nic_dev->netdev, &pfc.pfc_data.num_of_tc); *((u32 *)buf_out) = (u32)pfc.data; return 0; } int set_pfc_control(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u8 pfc_en = 0; u8 err = 0; if (!buf_in || !buf_out || *out_size != sizeof(u8) || in_size != sizeof(u8)) return -EINVAL; pfc_en = *((u8 *)buf_in); if (!(test_bit(HINIC_DCB_ENABLE, &nic_dev->flags))) { nicif_err(nic_dev, drv, nic_dev->netdev, "Need to enable dcb first\n"); err = 0xff; goto exit; } hinic_dcbnl_set_pfc_en_tool(nic_dev->netdev, &pfc_en, true); err = hinic_dcbnl_set_pfc_tool(nic_dev->netdev); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to set pfc to %s\n", pfc_en ? "enable" : "disable"); } exit: *((u8 *)buf_out) = (u8)err; return 0; } int set_ets(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct _ets ets = {0}; u8 err = 0; u8 i; u8 support_tc = nic_dev->max_cos; if (!buf_in || !buf_out || *out_size != sizeof(u8) || in_size != sizeof(struct _ets)) return -EINVAL; memcpy(&ets, buf_in, sizeof(struct _ets)); if (!(test_bit(HINIC_DCB_ENABLE, &nic_dev->flags))) { nicif_err(nic_dev, drv, nic_dev->netdev, "Need to enable dcb first\n"); err = 0xff; goto exit; } if (ets.flag_com.ets_flag.flag_ets_enable) { hinic_dcbnl_set_ets_en_tool(nic_dev->netdev, &ets.ets_en, true); if (!ets.ets_en) goto exit; } if (!(test_bit(HINIC_ETS_ENABLE, &nic_dev->flags))) { nicif_err(nic_dev, drv, nic_dev->netdev, "Need to enable ets first\n"); err = 0xff; goto exit; } if (ets.flag_com.ets_flag.flag_ets_cos) { for (i = 0; i < HINIC_DCB_COS_MAX; i++) { if (ets.tc[i] >= HINIC_DCB_TC_MAX) { nicif_err(nic_dev, drv, nic_dev->netdev, "ETS tc id %d out of range\n", ets.tc[i]); err = 0xFF; goto exit; } } hinic_dcbnl_set_ets_tc_tool(nic_dev->netdev, ets.tc, true); } if (ets.flag_com.ets_flag.flag_ets_percent) { for (i = support_tc; i < HINIC_DCB_TC_MAX; i++) { if (ets.ets_percent[i]) { nicif_err(nic_dev, drv, nic_dev->netdev, "ETS setting out of range\n"); break; } } hinic_dcbnl_set_ets_pecent_tool(nic_dev->netdev, ets.ets_percent, true); } if (ets.flag_com.ets_flag.flag_ets_strict) hinic_dcbnl_set_ets_strict_tool(nic_dev->netdev, &ets.strict, true); err = hinic_dcbnl_set_ets_tool(nic_dev->netdev); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to set ets [%d]\n", err); } exit: *((u8 *)buf_out) = err; return 0; } int get_support_up(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u8 *up_num = buf_out; u8 support_up = 0; u8 i; u8 up_valid_bitmap = nic_dev->up_valid_bitmap; if (!buf_in || !buf_out || !out_size) return -EINVAL; if (*out_size != sizeof(*up_num)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*up_num)); return -EFAULT; } for (i = 0; i < HINIC_DCB_UP_MAX; i++) { if (up_valid_bitmap & BIT(i)) support_up++; } *up_num = support_up; return 0; } int get_support_tc(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u8 *tc_num = buf_out; if (!buf_in || !buf_out || !out_size) return -EINVAL; if (*out_size != sizeof(*tc_num)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*tc_num)); return -EFAULT; } hinic_dcbnl_get_tc_num_tool(nic_dev->netdev, tc_num); return 0; } int get_ets_info(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct _ets *ets = buf_out; if (!buf_in || !buf_out || *out_size != sizeof(*ets)) return -EINVAL; hinic_dcbnl_set_ets_pecent_tool(nic_dev->netdev, ets->ets_percent, false); hinic_dcbnl_set_ets_tc_tool(nic_dev->netdev, ets->tc, false); hinic_dcbnl_set_ets_en_tool(nic_dev->netdev, &ets->ets_en, false); hinic_dcbnl_set_ets_strict_tool(nic_dev->netdev, &ets->strict, false); ets->err = 0; return 0; } int set_pfc_priority(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u8 pfc_prority = 0; u8 err = 0; if (!buf_in || !buf_out || *out_size != sizeof(u8) || in_size != sizeof(u8)) return -EINVAL; pfc_prority = *((u8 *)buf_in); if (!((test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) && nic_dev->tmp_dcb_cfg.pfc_state)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Need to enable pfc first\n"); err = 0xff; goto exit; } hinic_dcbnl_set_pfc_cfg_tool(nic_dev->netdev, pfc_prority); err = hinic_dcbnl_set_pfc_tool(nic_dev->netdev); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to set pfc to %x priority\n", pfc_prority); } exit: *((u8 *)buf_out) = (u8)err; return 0; } static int set_pf_bw_limit(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u32 pf_bw_limit = 0; int err; if (hinic_func_type(nic_dev->hwdev) == TYPE_VF) { nicif_err(nic_dev, drv, nic_dev->netdev, "To set VF bandwidth rate, please use ip link cmd\n"); return -EINVAL; } if (!buf_in || !buf_out || in_size != sizeof(u32) || *out_size != sizeof(u8)) return -EINVAL; pf_bw_limit = *((u32 *)buf_in); err = hinic_set_pf_bw_limit(nic_dev->hwdev, pf_bw_limit); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to set pf bandwidth limit to %d%%\n", pf_bw_limit); if (err < 0) return err; } *((u8 *)buf_out) = (u8)err; return 0; } static int get_pf_bw_limit(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u32 pf_bw_limit = 0; int err; if (hinic_func_type(nic_dev->hwdev) == TYPE_VF) { nicif_err(nic_dev, drv, nic_dev->netdev, "To get VF bandwidth rate, please use ip link cmd\n"); return -EINVAL; } if (!buf_out || *out_size != sizeof(u32)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user :%d, expect: %lu\n", *out_size, sizeof(u32)); return -EFAULT; } err = hinic_dbg_get_pf_bw_limit(nic_dev->hwdev, &pf_bw_limit); if (err) return err; *((u32 *)buf_out) = pf_bw_limit; return 0; } static int get_poll_weight(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_nic_poll_weight *weight_info = buf_out; if (!buf_out || *out_size != sizeof(*weight_info)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user :%d, expect: %lu\n", *out_size, sizeof(*weight_info)); return -EFAULT; } weight_info->poll_weight = nic_dev->poll_weight; return 0; } static int set_poll_weight(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_nic_poll_weight *weight_info = buf_in; if (!buf_in || in_size != sizeof(*weight_info) || *out_size != sizeof(u32)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect in buf size: %u or out buf size: %d from user, expect: %lu\n", in_size, *out_size, sizeof(*weight_info)); return -EFAULT; } nic_dev->poll_weight = weight_info->poll_weight; return 0; } static int get_homologue(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_homologues *homo = buf_out; if (!buf_out || *out_size != sizeof(*homo)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*homo)); return -EFAULT; } if (test_bit(HINIC_SAME_RXTX, &nic_dev->flags)) homo->homo_state = HINIC_HOMOLOGUES_ON; else homo->homo_state = HINIC_HOMOLOGUES_OFF; return 0; } static int set_homologue(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_homologues *homo = buf_in; if (!buf_in || in_size != sizeof(*homo) || *out_size != sizeof(*homo)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect in buf size: %d or out buf size: %d from user, expect: %lu\n", in_size, *out_size, sizeof(*homo)); return -EFAULT; } if (homo->homo_state == HINIC_HOMOLOGUES_ON) { set_bit(HINIC_SAME_RXTX, &nic_dev->flags); } else if (homo->homo_state == HINIC_HOMOLOGUES_OFF) { clear_bit(HINIC_SAME_RXTX, &nic_dev->flags); } else { pr_err("Invalid parameters\n"); return -EFAULT; } return 0; } static int get_sset_count(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u32 count; if (!buf_in || !buf_out || in_size != sizeof(u32) || *out_size != sizeof(u32)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid parameters\n"); return -EINVAL; } switch (*((u32 *)buf_in)) { case HINIC_SHOW_SSET_IO_STATS: count = hinic_get_io_stats_size(nic_dev); break; default: count = 0; break; } *((u32 *)buf_out) = count; return 0; } static int get_sset_stats(struct hinic_nic_dev *nic_dev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_show_item *items = buf_out; u32 sset, count, size; int err; if (!buf_in || in_size != sizeof(u32) || !out_size || !buf_out) return -EINVAL; size = sizeof(u32); err = get_sset_count(nic_dev, buf_in, in_size, &count, &size); if (err) return -EINVAL; if (count * sizeof(*items) != *out_size) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, count * sizeof(*items)); return -EINVAL; } sset = *((u32 *)buf_in); switch (sset) { case HINIC_SHOW_SSET_IO_STATS: hinic_get_io_stats(nic_dev, items); break; default: nicif_err(nic_dev, drv, nic_dev->netdev, "Unknown %d to get stats\n", sset); err = -EINVAL; break; } return err; } static int get_func_type(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u16 func_typ; func_typ = hinic_func_type(hwdev); if (!buf_out || *out_size != sizeof(u16)) { pr_err("Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(u16)); return -EFAULT; } *(u16 *)buf_out = func_typ; return 0; } static int get_func_id(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u16 func_id; if (!buf_out || *out_size != sizeof(u16)) { pr_err("Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(u16)); return -EFAULT; } func_id = hinic_global_func_id_hw(hwdev); *(u16 *)buf_out = func_id; return 0; } static int get_chip_faults_stats(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { int offset = 0; struct chip_fault_stats *fault_info; if (!buf_in || !buf_out || *out_size != sizeof(*fault_info) || in_size != sizeof(*fault_info)) { pr_err("Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*fault_info)); return -EFAULT; } fault_info = (struct chip_fault_stats *)buf_in; offset = fault_info->offset; fault_info = (struct chip_fault_stats *)buf_out; hinic_get_chip_fault_stats(hwdev, fault_info->chip_faults, offset); return 0; } static int get_hw_stats(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { return hinic_dbg_get_hw_stats(hwdev, buf_out, (u16 *)out_size); } static int clear_hw_stats(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { return hinic_dbg_clear_hw_stats(hwdev, out_size); } static int get_drv_version(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct drv_version_info *ver_info; char ver_str[MAX_VER_INFO_LEN] = {0}; int err; if (*out_size != sizeof(*ver_info)) { pr_err("Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(*ver_info)); return -EFAULT; } err = snprintf(ver_str, sizeof(ver_str), "%s [compiled with the kernel]", HINIC_DRV_VERSION); if (err <= 0 || err >= MAX_VER_INFO_LEN) { pr_err("Failed snprintf driver version, function return(%d) and dest_len(%d)\n", err, MAX_VER_INFO_LEN); return -EFAULT; } ver_info = (struct drv_version_info *)buf_out; memcpy(ver_info->ver, ver_str, sizeof(ver_str)); return 0; } static int get_self_test(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { return 0; } static int get_chip_id_test(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { return 0; } static int get_single_card_info(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { if (!buf_in || !buf_out || in_size != sizeof(struct card_info) || *out_size != sizeof(struct card_info)) { pr_err("Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(struct card_info)); return -EFAULT; } hinic_get_card_info(hwdev, buf_out); return 0; } static int get_device_id(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u16 dev_id; int err; if (!buf_out || *out_size != sizeof(u16)) { pr_err("Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(u16)); return -EFAULT; } err = hinic_get_device_id(hwdev, &dev_id); if (err) return err; *((u32 *)buf_out) = dev_id; return 0; } static int is_driver_in_vm(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { bool in_host; if (!buf_out || (*out_size != sizeof(u8))) return -EINVAL; in_host = hinic_is_in_host(); if (in_host) *((u8 *)buf_out) = 0; else *((u8 *)buf_out) = 1; return 0; } static int get_pf_id(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic_pf_info *pf_info; u32 port_id = 0; int err; if (!buf_out || (*out_size != sizeof(*pf_info)) || !buf_in || in_size != sizeof(u32)) return -EINVAL; port_id = *((u32 *)buf_in); pf_info = (struct hinic_pf_info *)buf_out; err = hinic_get_pf_id(hwdev, port_id, &pf_info->pf_id, &pf_info->isvalid); if (err) return err; return 0; } static int __get_card_usr_api_chain_mem(int card_idx) { unsigned char *tmp; int i; mutex_lock(&g_hinic_addr_lock); g_hinic_card_id = card_idx; if (!g_hinic_card_vir_addr[card_idx]) { g_hinic_card_vir_addr[card_idx] = (void *)__get_free_pages(GFP_KERNEL, DBGTOOL_PAGE_ORDER); if (!g_hinic_card_vir_addr[card_idx]) { pr_err("Alloc api chain memory fail for card %d\n", card_idx); mutex_unlock(&g_hinic_addr_lock); return -EFAULT; } memset(g_hinic_card_vir_addr[card_idx], 0, PAGE_SIZE * (1 << DBGTOOL_PAGE_ORDER)); g_hinic_card_phy_addr[card_idx] = virt_to_phys(g_hinic_card_vir_addr[card_idx]); if (!g_hinic_card_phy_addr[card_idx]) { pr_err("phy addr for card %d is 0\n", card_idx); free_pages((unsigned long)g_hinic_card_vir_addr[card_idx], DBGTOOL_PAGE_ORDER); g_hinic_card_vir_addr[card_idx] = NULL; mutex_unlock(&g_hinic_addr_lock); return -EFAULT; } tmp = g_hinic_card_vir_addr[card_idx]; for (i = 0; i < (1 << DBGTOOL_PAGE_ORDER); i++) { SetPageReserved(virt_to_page(tmp)); tmp += PAGE_SIZE; } } mutex_unlock(&g_hinic_addr_lock); return 0; } static int get_pf_dev_info(char *dev_name, struct msg_module *nt_msg) { struct pf_dev_info dev_info[16] = { {0} }; struct card_node *card_info = NULL; int i; int err; if (nt_msg->len_info.out_buff_len != sizeof(dev_info) || nt_msg->len_info.in_buff_len != sizeof(dev_info)) { pr_err("Invalid out_buf_size %d or Invalid in_buf_size %d, expect %lu\n", nt_msg->len_info.out_buff_len, nt_msg->len_info.in_buff_len, (sizeof(dev_info) * 16)); return -EINVAL; } for (i = 0; i < MAX_CARD_NUM; i++) { card_info = (struct card_node *)g_hinic_card_node_array[i]; if (!card_info) continue; if (!strncmp(dev_name, card_info->chip_name, IFNAMSIZ)) break; } if (i == MAX_CARD_NUM || !card_info) { pr_err("Can't find this card %s\n", dev_name); return -EFAULT; } err = __get_card_usr_api_chain_mem(i); if (err) { pr_err("Faile to get api chain memory for userspace %s\n", dev_name); return -EFAULT; } hinic_chipif_get_all_pf_dev_info(dev_info, i, card_info->func_handle_array); /* Copy the dev_info to user mode */ if (copy_to_user(nt_msg->out_buf, dev_info, sizeof(dev_info))) { pr_err("Copy dev_info to user fail\n"); return -EFAULT; } return 0; } static int knl_free_mem(char *dev_name, struct msg_module *nt_msg) { struct card_node *card_info = NULL; int i; for (i = 0; i < MAX_CARD_NUM; i++) { card_info = (struct card_node *)g_hinic_card_node_array[i]; if (!card_info) continue; if (!strncmp(dev_name, card_info->chip_name, IFNAMSIZ)) break; } if (i == MAX_CARD_NUM || !card_info) { pr_err("Can't find this card %s\n", dev_name); return -EFAULT; } hinic_dbgtool_knl_free_mem(i); return 0; } extern void hinic_get_card_func_info_by_card_name(const char *chip_name, struct hinic_card_func_info *card_func); static int get_card_func_info(char *dev_name, struct msg_module *nt_msg) { struct hinic_card_func_info card_func_info = {0}; int id, err; if (nt_msg->len_info.out_buff_len != sizeof(card_func_info) || nt_msg->len_info.in_buff_len != sizeof(card_func_info)) { pr_err("Invalid out_buf_size %d or Invalid in_buf_size %d, expect %lu\n", nt_msg->len_info.out_buff_len, nt_msg->len_info.in_buff_len, sizeof(card_func_info)); return -EINVAL; } err = memcmp(dev_name, HINIC_CHIP_NAME, strlen(HINIC_CHIP_NAME)); if (err) { pr_err("Invalid chip name %s\n", dev_name); return err; } err = sscanf(dev_name, HINIC_CHIP_NAME "%d", &id); if (err <= 0) { pr_err("Failed to get hinic id\n"); return err; } if (id >= MAX_CARD_NUM) { pr_err("chip id %d exceed limit[0-%d]\n", id, MAX_CARD_NUM - 1); return -EINVAL; } hinic_get_card_func_info_by_card_name(dev_name, &card_func_info); if (!card_func_info.num_pf) { pr_err("None function found for %s\n", dev_name); return -EFAULT; } err = __get_card_usr_api_chain_mem(id); if (err) { pr_err("Faile to get api chain memory for userspace %s\n", dev_name); return -EFAULT; } card_func_info.usr_api_phy_addr = g_hinic_card_phy_addr[id]; /* Copy the dev_info to user mode */ if (copy_to_user(nt_msg->out_buf, &card_func_info, sizeof(card_func_info))) { pr_err("Copy dev_info to user fail\n"); return -EFAULT; } return 0; } #define GET_FIRMWARE_ACTIVE_STATUS_TIMEOUT 30 static int get_firmware_active_status(void *hwdev, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u32 loop_cnt = 0; if (*out_size != 0) { pr_err("Unexpect out buf size from user: %d, expect: 0\n", *out_size); return -EINVAL; } while (loop_cnt < GET_FIRMWARE_ACTIVE_STATUS_TIMEOUT) { if (!hinic_get_mgmt_channel_status(hwdev)) return 0; msleep(1000); loop_cnt++; } if (loop_cnt == GET_FIRMWARE_ACTIVE_STATUS_TIMEOUT) return -ETIMEDOUT; return 0; } struct nic_drv_module_handle nic_driv_module_cmd_handle[] = { {TX_INFO, get_tx_info}, {Q_NUM, get_q_num}, {TX_WQE_INFO, get_tx_wqe_info}, {RX_INFO, get_rx_info}, {RX_WQE_INFO, get_rx_wqe_info}, {RX_CQE_INFO, get_rx_cqe_info}, {GET_INTER_NUM, get_inter_num}, {CLEAR_FUNC_STASTIC, clear_func_static}, {GET_NUM_COS, get_num_cos}, {GET_COS_UP_MAP, get_dcb_cos_up_map}, {SET_COS_UP_MAP, set_dcb_cos_up_map}, {GET_LOOPBACK_MODE, get_loopback_mode}, {SET_LOOPBACK_MODE, set_loopback_mode}, {SET_LINK_MODE, set_link_mode}, {SET_PF_BW_LIMIT, set_pf_bw_limit}, {GET_PF_BW_LIMIT, get_pf_bw_limit}, {GET_POLL_WEIGHT, get_poll_weight}, {SET_POLL_WEIGHT, set_poll_weight}, {GET_HOMOLOGUE, get_homologue}, {SET_HOMOLOGUE, set_homologue}, {GET_SSET_COUNT, get_sset_count}, {GET_SSET_ITEMS, get_sset_stats}, {SET_PFC_CONTROL, set_pfc_control}, {SET_ETS, set_ets}, {GET_ETS_INFO, get_ets_info}, {SET_PFC_PRIORITY, set_pfc_priority}, {SET_DCB_CFG, set_dcb_cfg}, {GET_PFC_INFO, get_pfc_info}, {GET_SUPPORT_UP, get_support_up}, {GET_SUPPORT_TC, get_support_tc}, }; struct hw_drv_module_handle hw_driv_module_cmd_handle[] = { {FUNC_TYPE, get_func_type}, {GET_FUNC_IDX, get_func_id}, {GET_DRV_VERSION, get_drv_version}, {GET_HW_STATS, get_hw_stats}, {CLEAR_HW_STATS, clear_hw_stats}, {GET_SELF_TEST_RES, get_self_test}, {GET_CHIP_FAULT_STATS, get_chip_faults_stats}, {GET_CHIP_ID, get_chip_id_test}, {GET_SINGLE_CARD_INFO, get_single_card_info}, {GET_FIRMWARE_ACTIVE_STATUS, get_firmware_active_status}, {GET_DEVICE_ID, get_device_id}, {IS_DRV_IN_VM, is_driver_in_vm}, {GET_PF_ID, get_pf_id}, }; static int send_to_nic_driver(struct hinic_nic_dev *nic_dev, u32 cmd, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { int index, num_cmds = sizeof(nic_driv_module_cmd_handle) / sizeof(nic_driv_module_cmd_handle[0]); enum driver_cmd_type cmd_type = (enum driver_cmd_type)cmd; int err = 0; mutex_lock(&nic_dev->nic_mutex); for (index = 0; index < num_cmds; index++) { if (cmd_type == nic_driv_module_cmd_handle[index].driv_cmd_name) { err = nic_driv_module_cmd_handle[index].driv_func (nic_dev, buf_in, in_size, buf_out, out_size); break; } } mutex_unlock(&nic_dev->nic_mutex); if (index == num_cmds) return -EINVAL; return err; } static int send_to_hw_driver(void *hwdev, struct msg_module *nt_msg, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { int index, num_cmds = sizeof(hw_driv_module_cmd_handle) / sizeof(hw_driv_module_cmd_handle[0]); enum driver_cmd_type cmd_type = (enum driver_cmd_type)(nt_msg->msg_formate); int err = 0; for (index = 0; index < num_cmds; index++) { if (cmd_type == hw_driv_module_cmd_handle[index].driv_cmd_name) { err = hw_driv_module_cmd_handle[index].driv_func (hwdev, buf_in, in_size, buf_out, out_size); break; } } if (index == num_cmds) return -EINVAL; return err; } static int send_to_ucode(void *hwdev, struct msg_module *nt_msg, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { int ret = 0; if (nt_msg->ucode_cmd.ucode_db.ucode_imm) { ret = hinic_cmdq_direct_resp (hwdev, nt_msg->ucode_cmd.ucode_db.cmdq_ack_type, nt_msg->ucode_cmd.ucode_db.comm_mod_type, nt_msg->ucode_cmd.ucode_db.ucode_cmd_type, buf_in, buf_out, 0); if (ret) pr_err("Send direct cmdq err: %d\n", ret); } else { ret = hinic_cmdq_detail_resp (hwdev, nt_msg->ucode_cmd.ucode_db.cmdq_ack_type, nt_msg->ucode_cmd.ucode_db.comm_mod_type, nt_msg->ucode_cmd.ucode_db.ucode_cmd_type, buf_in, buf_out, 0); if (ret) pr_err("Send detail cmdq err: %d\n", ret); } return ret; } enum api_csr_op_width { OP_WIDTH_4B, OP_WIDTH_8B, OP_WIDTH_UNKNOWN, }; static int api_csr_read(void *hwdev, struct msg_module *nt_msg, void *buf_in, u32 in_size, void *buf_out, u32 *out_size, enum api_csr_op_width width) { struct up_log_msg_st *up_log_msg = (struct up_log_msg_st *)buf_in; u32 op_bytes = (width == OP_WIDTH_4B ? sizeof(u32) : sizeof(u64)); int ret = 0; u32 rd_len, rd_addr, rd_cnt; u32 offset = 0; u8 node_id; u32 i; if (!buf_in || !buf_out || in_size != sizeof(*up_log_msg) || *out_size != up_log_msg->rd_len || width >= OP_WIDTH_UNKNOWN) return -EINVAL; rd_len = up_log_msg->rd_len; rd_addr = up_log_msg->addr; node_id = (u8)nt_msg->up_cmd.up_db.comm_mod_type; rd_cnt = rd_len / op_bytes; if (rd_len % op_bytes) rd_cnt++; for (i = 0; i < rd_cnt; i++) { if (width == OP_WIDTH_4B) ret = hinic_api_csr_rd32(hwdev, node_id, rd_addr + offset, (u32 *)(((u8 *)buf_out) + offset)); else ret = hinic_api_csr_rd64(hwdev, node_id, rd_addr + offset, (u64 *)(((u8 *)buf_out) + offset)); if (ret) { pr_err("Read csr failed, err: %d, node_id: %d, csr addr: 0x%08x\n", ret, node_id, rd_addr + offset); return ret; } offset += op_bytes; } *out_size = rd_len; return ret; } static int api_csr_write(void *hwdev, struct msg_module *nt_msg, void *buf_in, u32 in_size, void *buf_out, u32 *out_size, enum api_csr_op_width width) { struct csr_write_st *csr_write_msg = (struct csr_write_st *)buf_in; u32 op_bytes = (width == OP_WIDTH_4B ? sizeof(u32) : sizeof(u64)); int ret = 0; u32 rd_len, rd_addr, rd_cnt; u32 offset = 0; u8 node_id; u32 i; u8 *data = NULL; if (!buf_in || in_size != sizeof(*csr_write_msg) || width >= OP_WIDTH_UNKNOWN) return -EINVAL; rd_len = csr_write_msg->rd_len; rd_addr = csr_write_msg->addr; node_id = (u8)nt_msg->up_cmd.up_db.comm_mod_type; if (rd_len % op_bytes) { pr_err("Csr length must be a multiple of %d\n", op_bytes); return -EFAULT; } rd_cnt = rd_len / op_bytes; data = kzalloc(rd_len, GFP_KERNEL); if (!data) { pr_err("No more memory\n"); return -EFAULT; } if (copy_from_user(data, (void *)csr_write_msg->data, rd_len)) { pr_err("Copy information from user failed\n"); kfree(data); return -EFAULT; } for (i = 0; i < rd_cnt; i++) { if (width == OP_WIDTH_4B) ret = hinic_api_csr_wr32(hwdev, node_id, rd_addr + offset, *((u32 *)(data + offset))); else ret = hinic_api_csr_wr64(hwdev, node_id, rd_addr + offset, *((u64 *)(data + offset))); if (ret) { pr_err("Write csr failed, ret: %d, node_id: %d, csr addr: 0x%08x\n", ret, rd_addr + offset, node_id); kfree(data); return ret; } offset += op_bytes; } *out_size = 0; kfree(data); return ret; } static u32 get_up_timeout_val(enum hinic_mod_type mod, u8 cmd) { if (mod == HINIC_MOD_L2NIC && cmd == HINIC_PORT_CMD_UPDATE_FW) return UP_UPDATEFW_TIME_OUT_VAL; else return UP_COMP_TIME_OUT_VAL; } static int check_useparam_valid(struct msg_module *nt_msg, void *buf_in) { struct csr_write_st *csr_write_msg = (struct csr_write_st *)buf_in; u32 rd_len = csr_write_msg->rd_len; if (rd_len > TOOL_COUNTER_MAX_LEN) { pr_err("Csr read or write len is invalid\n"); return -EINVAL; } return 0; } static int send_to_up(void *hwdev, struct msg_module *nt_msg, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { int ret = 0; if (nt_msg->up_cmd.up_db.up_api_type == API_CMD || nt_msg->up_cmd.up_db.up_api_type == API_CLP) { enum hinic_mod_type mod; u8 cmd; u32 timeout; mod = (enum hinic_mod_type)nt_msg->up_cmd.up_db.comm_mod_type; cmd = nt_msg->up_cmd.up_db.chipif_cmd; timeout = get_up_timeout_val(mod, cmd); if (nt_msg->up_cmd.up_db.up_api_type == API_CMD) ret = hinic_msg_to_mgmt_sync(hwdev, mod, cmd, buf_in, (u16)in_size, buf_out, (u16 *)out_size, timeout); else ret = hinic_clp_to_mgmt(hwdev, mod, cmd, buf_in, (u16)in_size, buf_out, (u16 *)out_size); if (ret) { pr_err("Message to mgmt cpu return fail, mod: %d, cmd: %d\n", mod, cmd); return ret; } } else if (nt_msg->up_cmd.up_db.up_api_type == API_CHAIN) { if (check_useparam_valid(nt_msg, buf_in)) return -EINVAL; switch (nt_msg->up_cmd.up_db.chipif_cmd) { case API_CSR_WRITE: ret = api_csr_write(hwdev, nt_msg, buf_in, in_size, buf_out, out_size, OP_WIDTH_4B); break; case API_CSR_READ: ret = api_csr_read(hwdev, nt_msg, buf_in, in_size, buf_out, out_size, OP_WIDTH_4B); break; case API_CSR_WRITE_8B: ret = api_csr_write(hwdev, nt_msg, buf_in, in_size, buf_out, out_size, OP_WIDTH_8B); break; case API_CSR_READ_8B: ret = api_csr_read(hwdev, nt_msg, buf_in, in_size, buf_out, out_size, OP_WIDTH_8B); break; default: pr_err("Unsupported chipif cmd: %d\n", nt_msg->up_cmd.up_db.chipif_cmd); ret = -EINVAL; break; } } return ret; } static int sm_rd32(void *hwdev, u32 id, u8 instance, u8 node, struct sm_out_st *buf_out) { u32 val1; int ret; ret = hinic_sm_ctr_rd32(hwdev, node, instance, id, &val1); if (ret) { pr_err("Get sm ctr information (32 bits)failed\n"); val1 = 0xffffffff; } buf_out->val1 = val1; return ret; } static int sm_rd64_pair(void *hwdev, u32 id, u8 instance, u8 node, struct sm_out_st *buf_out) { u64 val1 = 0, val2 = 0; int ret; ret = hinic_sm_ctr_rd64_pair(hwdev, node, instance, id, &val1, &val2); if (ret) { pr_err("Get sm ctr information (64 bits pair)failed\n"); val1 = 0xffffffff; } buf_out->val1 = val1; buf_out->val2 = val2; return ret; } static int sm_rd64(void *hwdev, u32 id, u8 instance, u8 node, struct sm_out_st *buf_out) { u64 val1; int ret; ret = hinic_sm_ctr_rd64(hwdev, node, instance, id, &val1); if (ret) { pr_err("Get sm ctr information (64 bits)failed\n"); val1 = 0xffffffff; } buf_out->val1 = val1; return ret; } typedef int (*sm_module)(void *hwdev, u32 id, u8 instance, u8 node, struct sm_out_st *buf_out); struct sm_module_handle { enum sm_cmd_type sm_cmd_name; sm_module sm_func; }; static struct sm_module_handle sm_module_cmd_handle[] = { {SM_CTR_RD32, sm_rd32}, {SM_CTR_RD64_PAIR, sm_rd64_pair}, {SM_CTR_RD64, sm_rd64} }; static int send_to_sm(void *hwdev, struct msg_module *nt_msg, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct sm_in_st *sm_in = buf_in; struct sm_out_st *sm_out = buf_out; u32 msg_formate = nt_msg->msg_formate; int index, num_cmds = sizeof(sm_module_cmd_handle) / sizeof(sm_module_cmd_handle[0]); int ret = 0; if (!buf_in || !buf_out || in_size != sizeof(*sm_in) || *out_size != sizeof(*sm_out)) return -EINVAL; for (index = 0; index < num_cmds; index++) { if (msg_formate == sm_module_cmd_handle[index].sm_cmd_name) ret = sm_module_cmd_handle[index].sm_func(hwdev, (u32)sm_in->id, (u8)sm_in->instance, (u8)sm_in->node, sm_out); } if (ret) pr_err("Get sm information fail\n"); *out_size = sizeof(struct sm_out_st); return ret; } static bool is_hwdev_cmd_support(unsigned int mod, char *ifname, u32 up_api_type) { void *hwdev; hwdev = hinic_get_hwdev_by_ifname(ifname); if (!hwdev) { pr_err("Can not get the device %s correctly\n", ifname); return false; } switch (mod) { case SEND_TO_UP: case SEND_TO_SM: if (FUNC_SUPPORT_MGMT(hwdev)) { if (up_api_type == API_CLP) { if (!hinic_is_hwdev_mod_inited (hwdev, HINIC_HWDEV_CLP_INITED)) { pr_err("CLP have not initialized\n"); return false; } } else if (!hinic_is_hwdev_mod_inited (hwdev, HINIC_HWDEV_MGMT_INITED)) { pr_err("MGMT have not initialized\n"); return false; } } else if (!hinic_is_hwdev_mod_inited (hwdev, HINIC_HWDEV_MBOX_INITED)) { pr_err("MBOX have not initialized\n"); return false; } if (mod == SEND_TO_SM && ((hinic_func_type(hwdev) == TYPE_VF) || (!hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_MGMT_INITED)))) { pr_err("Current function do not support this cmd\n"); return false; } break; case SEND_TO_UCODE: if (!hinic_is_hwdev_mod_inited(hwdev, HINIC_HWDEV_CMDQ_INITED)) { pr_err("CMDQ have not initialized\n"); return false; } break; default: return false; } return true; } static bool nictool_k_is_cmd_support(unsigned int mod, char *ifname, u32 up_api_type) { enum hinic_init_state init_state = hinic_get_init_state_by_ifname(ifname); bool support = true; if (init_state == HINIC_INIT_STATE_NONE) return false; if (mod == SEND_TO_NIC_DRIVER) { if (init_state < HINIC_INIT_STATE_NIC_INITED) { pr_err("NIC driver have not initialized\n"); return false; } } else if (mod >= SEND_TO_UCODE && mod <= SEND_TO_SM) { return is_hwdev_cmd_support(mod, ifname, up_api_type); } else if ((mod >= HINICADM_OVS_DRIVER && mod <= HINICADM_FCOE_DRIVER) || mod == SEND_TO_HW_DRIVER) { if (init_state < HINIC_INIT_STATE_HWDEV_INITED) { pr_err("Hwdev have not initialized\n"); return false; } } else { pr_err("Unsupport mod %d\n", mod); support = false; } return support; } static int alloc_tmp_buf(void *hwdev, struct msg_module *nt_msg, u32 in_size, void **buf_in, u32 out_size, void **buf_out) { int ret; ret = alloc_buff_in(hwdev, nt_msg, in_size, buf_in); if (ret) { pr_err("Alloc tool cmd buff in failed\n"); return ret; } ret = alloc_buff_out(hwdev, nt_msg, out_size, buf_out); if (ret) { pr_err("Alloc tool cmd buff out failed\n"); goto out_free_buf_in; } return 0; out_free_buf_in: free_buff_in(hwdev, nt_msg, *buf_in); return ret; } static void free_tmp_buf(void *hwdev, struct msg_module *nt_msg, void *buf_in, void *buf_out) { free_buff_out(hwdev, nt_msg, buf_out); free_buff_in(hwdev, nt_msg, buf_in); } static int get_self_test_cmd(struct msg_module *nt_msg) { int ret; u32 res = 0; ret = hinic_get_self_test_result(nt_msg->device_name, &res); if (ret) { pr_err("Get self test result failed\n"); return -EFAULT; } ret = copy_buf_out_to_user(nt_msg, sizeof(res), &res); if (ret) pr_err("%s:%d:: Copy to user failed\n", __func__, __LINE__); return ret; } static int get_all_chip_id_cmd(struct msg_module *nt_msg) { struct nic_card_id card_id; memset(&card_id, 0, sizeof(card_id)); hinic_get_all_chip_id((void *)&card_id); if (copy_to_user(nt_msg->out_buf, &card_id, sizeof(card_id))) { pr_err("Copy chip id to user failed\n"); return -EFAULT; } return 0; } int nic_ioctl(void *uld_dev, u32 cmd, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { return send_to_nic_driver(uld_dev, cmd, buf_in, in_size, buf_out, out_size); } static void *__get_dev_support_nic_cmd(struct msg_module *nt_msg, enum hinic_service_type type) { void *uld_dev = NULL; /* set/get qos must use chip_name(hinic0) */ switch (nt_msg->msg_formate) { case GET_COS_UP_MAP: case SET_COS_UP_MAP: case GET_NUM_COS: uld_dev = hinic_get_uld_by_chip_name(nt_msg->device_name, type); if (!uld_dev) pr_err("Get/set cos_up must use chip_name(hinic0)\n"); return uld_dev; default: break; } uld_dev = hinic_get_uld_dev_by_ifname(nt_msg->device_name, type); if (!uld_dev) pr_err("Can not get the uld dev correctly: %s, nic driver may be not register\n", nt_msg->device_name); return uld_dev; } static void *get_support_uld_dev(struct msg_module *nt_msg, enum hinic_service_type type) { char *service_name[SERVICE_T_MAX] = {"NIC", "OVS", "ROCE", "TOE", "IWARP", "FC", "FCOE"}; void *hwdev = NULL; void *uld_dev = NULL; switch (nt_msg->module) { case SEND_TO_NIC_DRIVER: hwdev = hinic_get_hwdev_by_ifname(nt_msg->device_name); if (!hinic_support_nic(hwdev, NULL)) { pr_err("Current function don't support NIC\n"); return NULL; } return __get_dev_support_nic_cmd(nt_msg, type); default: break; } uld_dev = hinic_get_uld_dev_by_ifname(nt_msg->device_name, type); if (!uld_dev) pr_err("Can not get the uld dev correctly: %s, %s driver may be not register\n", nt_msg->device_name, service_name[type]); return uld_dev; } static int get_service_drv_version(void *hwdev, struct msg_module *nt_msg, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { enum hinic_service_type type; int ret = 0; type = nt_msg->module - SEND_TO_SM; *out_size = sizeof(struct drv_version_info); if (!g_uld_info[type].ioctl) return ret; ret = g_uld_info[type].ioctl(NULL, nt_msg->msg_formate, buf_in, in_size, buf_out, out_size); if (ret) return ret; if (copy_to_user(nt_msg->out_buf, buf_out, *out_size)) return -EFAULT; return ret; } static int send_to_service_driver(struct msg_module *nt_msg, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { enum hinic_service_type type; void *uld_dev; int ret = -EINVAL; if (nt_msg->module == SEND_TO_NIC_DRIVER) type = SERVICE_T_NIC; else type = nt_msg->module - SEND_TO_SM; if (type < SERVICE_T_MAX) { uld_dev = get_support_uld_dev(nt_msg, type); if (!uld_dev) return -EINVAL; if (g_uld_info[type].ioctl) ret = g_uld_info[type].ioctl(uld_dev, nt_msg->msg_formate, buf_in, in_size, buf_out, out_size); } else { pr_err("Ioctl input module id: %d is incorrectly\n", nt_msg->module); } return ret; } static int nictool_exec_cmd(void *hwdev, struct msg_module *nt_msg, void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { int ret; switch (nt_msg->module) { case SEND_TO_HW_DRIVER: ret = send_to_hw_driver(hwdev, nt_msg, buf_in, in_size, buf_out, out_size); break; case SEND_TO_UP: ret = send_to_up(hwdev, nt_msg, buf_in, in_size, buf_out, out_size); break; case SEND_TO_UCODE: ret = send_to_ucode(hwdev, nt_msg, buf_in, in_size, buf_out, out_size); break; case SEND_TO_SM: ret = send_to_sm(hwdev, nt_msg, buf_in, in_size, buf_out, out_size); break; default: ret = send_to_service_driver(nt_msg, buf_in, in_size, buf_out, out_size); break; } return ret; } static int get_nictool_drv_cap(struct msg_module *nt_msg) { int ret; u64 support = 0; if (nt_msg->len_info.out_buff_len != sizeof(u64)) { pr_err("Unexpect out buf size from user: %d, expect: %lu\n", nt_msg->len_info.out_buff_len, sizeof(u64)); return -EINVAL; } support |= NICTOOL_SUPPORT_API_CSR; ret = copy_buf_out_to_user(nt_msg, sizeof(support), &support); if (ret) pr_err("%s:%d:: Copy to user failed\n", __func__, __LINE__); return ret; } static bool hinic_is_special_handling_cmd(struct msg_module *nt_msg, int *ret) { bool handled = true; if (nt_msg->module != SEND_TO_HW_DRIVER) return false; switch (nt_msg->msg_formate) { case GET_SELF_TEST_RES: *ret = get_self_test_cmd(nt_msg); break; case GET_CHIP_ID: *ret = get_all_chip_id_cmd(nt_msg); break; case GET_PF_DEV_INFO: *ret = get_pf_dev_info(nt_msg->device_name, nt_msg); break; case CMD_FREE_MEM: *ret = knl_free_mem(nt_msg->device_name, nt_msg); break; case GET_CHIP_INFO: *ret = get_card_func_info(nt_msg->device_name, nt_msg); break; case GET_NICTOOL_CAP: *ret = get_nictool_drv_cap(nt_msg); break; default: handled = false; break; } return handled; } static long nictool_k_unlocked_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) { void *hwdev; struct msg_module nt_msg; void *buf_out = NULL; void *buf_in = NULL; u32 out_size_expect = 0; u32 out_size = 0; u32 in_size = 0; unsigned int cmd_raw = 0; int ret = 0; memset(&nt_msg, 0, sizeof(nt_msg)); if (copy_from_user(&nt_msg, (void *)arg, sizeof(nt_msg))) { pr_err("Copy information from user failed\n"); return -EFAULT; } /* end with '\0' */ nt_msg.device_name[IFNAMSIZ - 1] = '\0'; cmd_raw = nt_msg.module; out_size_expect = nt_msg.len_info.out_buff_len; in_size = nt_msg.len_info.in_buff_len; hinic_tool_cnt_inc(); if (hinic_is_special_handling_cmd(&nt_msg, &ret)) goto out_free_lock; if (cmd_raw == HINICADM_FC_DRIVER && nt_msg.msg_formate == GET_CHIP_ID) hinic_get_fc_devname(nt_msg.device_name); if (!nictool_k_is_cmd_support(cmd_raw, nt_msg.device_name, nt_msg.up_cmd.up_db.up_api_type)) { ret = -EFAULT; goto out_free_lock; } /* get the netdevice */ hwdev = hinic_get_hwdev_by_ifname(nt_msg.device_name); if (!hwdev) { pr_err("Can not get the device %s correctly\n", nt_msg.device_name); ret = -ENODEV; goto out_free_lock; } ret = alloc_tmp_buf(hwdev, &nt_msg, in_size, &buf_in, out_size_expect, &buf_out); if (ret) { pr_err("Alloc tmp buff failed\n"); goto out_free_lock; } out_size = out_size_expect; if (nt_msg.msg_formate == GET_DRV_VERSION && (cmd_raw == HINICADM_FC_DRIVER || cmd_raw == HINICADM_TOE_DRIVER)) { ret = get_service_drv_version(hwdev, &nt_msg, buf_in, in_size, buf_out, &out_size); goto out_free_buf; } ret = nictool_exec_cmd(hwdev, &nt_msg, buf_in, in_size, buf_out, &out_size); if (ret) goto out_free_buf; ret = copy_buf_out_to_user(&nt_msg, out_size_expect, buf_out); if (ret) pr_err("Copy information to user failed\n"); out_free_buf: free_tmp_buf(hwdev, &nt_msg, buf_in, buf_out); out_free_lock: hinic_tool_cnt_dec(); return (long)ret; } static int nictool_k_open(struct inode *pnode, struct file *pfile) { return 0; } static ssize_t nictool_k_read(struct file *pfile, char __user *ubuf, size_t size, loff_t *ppos) { return 0; } static ssize_t nictool_k_write(struct file *pfile, const char __user *ubuf, size_t size, loff_t *ppos) { return 0; } static const struct file_operations fifo_operations = { .owner = THIS_MODULE, .open = nictool_k_open, .read = nictool_k_read, .write = nictool_k_write, .unlocked_ioctl = nictool_k_unlocked_ioctl, .mmap = hinic_mem_mmap, }; static int if_nictool_exist(void) { struct file *fp = NULL; int exist = 0; fp = filp_open(HIADM_DEV_PATH, O_RDONLY, 0); if (IS_ERR(fp)) { exist = 0; } else { (void)filp_close(fp, NULL); exist = 1; } return exist; } /** * hinic_tool_k_init - initialize the hw interface */ int hinic_tool_k_init(void) { int ret; struct device *pdevice; if (g_nictool_init_flag) { g_nictool_ref_cnt++; /* already initialized */ return 0; } if (if_nictool_exist()) { pr_err("Nictool device exists\n"); return 0; } ret = alloc_chrdev_region(&g_dev_id, 0, 1, HIADM_DEV_NAME); if (ret < 0) { pr_err("Register nictool_dev fail(0x%x)\n", ret); return ret; } /* Create equipment */ /*lint -save -e160*/ g_nictool_class = class_create(HIADM_DEV_CLASS); /*lint -restore*/ if (IS_ERR(g_nictool_class)) { pr_err("Create nictool_class fail\n"); ret = -EFAULT; goto class_create_err; } /* Initializing the character device */ cdev_init(&g_nictool_cdev, &fifo_operations); /* Add devices to the operating system */ ret = cdev_add(&g_nictool_cdev, g_dev_id, 1); if (ret < 0) { pr_err("Add nictool_dev to operating system fail(0x%x)\n", ret); goto cdev_add_err; } /* Export device information to user space * (/sys/class/class name/device name) */ pdevice = device_create(g_nictool_class, NULL, g_dev_id, NULL, HIADM_DEV_NAME); if (IS_ERR(pdevice)) { pr_err("Export nictool device information to user space fail\n"); ret = -EFAULT; goto device_create_err; } g_nictool_init_flag = 1; g_nictool_ref_cnt = 1; pr_info("Register nictool_dev to system succeed\n"); return 0; device_create_err: cdev_del(&g_nictool_cdev); cdev_add_err: class_destroy(g_nictool_class); class_create_err: g_nictool_class = NULL; unregister_chrdev_region(g_dev_id, 1); return ret; } void hinic_tool_k_uninit(void) { if (g_nictool_init_flag) { if ((--g_nictool_ref_cnt)) return; } g_nictool_init_flag = 0; if (!g_nictool_class || IS_ERR(g_nictool_class)) return; cdev_del(&g_nictool_cdev); device_destroy(g_nictool_class, g_dev_id); class_destroy(g_nictool_class); g_nictool_class = NULL; unregister_chrdev_region(g_dev_id, 1); pr_info("Unregister nictool_dev succeed\n"); } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_nictool.h000066400000000000000000000105661502027332500221310ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_NICTOOL_H_ #define HINIC_NICTOOL_H_ #include "hinic_dfx_def.h" /* completion timeout interval, unit is jiffies*/ #define UP_COMP_TIME_OUT_VAL 10000U struct sm_in_st { int node; int id; int instance; }; struct sm_out_st { u64 val1; u64 val2; }; struct up_log_msg_st { u32 rd_len; u32 addr; }; struct csr_write_st { u32 rd_len; u32 addr; u8 *data; }; struct ipsurx_stats_info { u32 addr; u32 rd_cnt; }; struct ucode_cmd_st { union { struct { u32 comm_mod_type : 8; u32 ucode_cmd_type : 4; u32 cmdq_ack_type : 3; u32 ucode_imm : 1; u32 len : 16; } ucode_db; u32 value; }; }; struct up_cmd_st { union { struct { u32 comm_mod_type : 8; u32 chipif_cmd : 8; u32 up_api_type : 16; } up_db; u32 value; }; }; struct _dcb_data { u8 wr_flag; u8 dcb_en; u8 err; u8 rsvd; }; union _dcb_ctl { struct _dcb_data dcb_data; u32 data; }; struct _pfc_data { u8 pfc_en; u8 pfc_priority; u8 num_of_tc; u8 err; }; union _pfc { struct _pfc_data pfc_data; u32 data; }; union _flag_com { struct _ets_flag { u8 flag_ets_enable : 1; u8 flag_ets_percent : 1; u8 flag_ets_cos : 1; u8 flag_ets_strict : 1; u8 rev : 4; } ets_flag; u8 data; }; struct _ets { u8 ets_en; u8 err; u8 strict; u8 tc[8]; u8 ets_percent[8]; union _flag_com flag_com; }; #define API_CMD 0x1 #define API_CHAIN 0x2 #define API_CLP 0x3 struct msg_module { char device_name[IFNAMSIZ]; unsigned int module; union { u32 msg_formate; struct ucode_cmd_st ucode_cmd; struct up_cmd_st up_cmd; }; struct { u32 in_buff_len; u32 out_buff_len; } len_info; u32 res; void *in_buff; void *out_buf; }; #define MAX_VER_INFO_LEN 128 struct drv_version_info { char ver[MAX_VER_INFO_LEN]; }; struct chip_fault_stats { int offset; u8 chip_faults[MAX_DRV_BUF_SIZE]; }; struct hinic_wqe_info { int q_id; void *slq_handle; unsigned int wqe_id; }; struct hinic_cos_up_map { u8 cos_up[HINIC_DCB_UP_MAX]; u8 num_cos; }; struct hinic_tx_hw_page { u64 phy_addr; u64 *map_addr; }; struct hinic_dbg_sq_info { u16 q_id; u16 pi; u16 ci; /* sw_ci */ u16 fi; /* hw_ci */ u32 q_depth; u16 pi_reverse; u16 weqbb_size; u8 priority; u16 *ci_addr; u64 cla_addr; void *slq_handle; struct hinic_tx_hw_page direct_wqe; struct hinic_tx_hw_page db_addr; u32 pg_idx; u32 glb_sq_id; }; struct hinic_dbg_rq_info { u16 q_id; u16 glb_rq_id; u16 hw_pi; u16 ci; /* sw_ci */ u16 sw_pi; u16 wqebb_size; u16 q_depth; u16 buf_len; void *slq_handle; u64 ci_wqe_page_addr; u64 ci_cla_tbl_addr; u16 msix_idx; u32 msix_vector; }; #define BUSINFO_LEN 32 struct pf_info { char name[IFNAMSIZ]; char bus_info[BUSINFO_LEN]; u32 pf_type; }; #define MAX_SIZE 16 struct card_info { struct pf_info pf[MAX_SIZE]; u32 pf_num; }; struct nic_card_id { u32 id[MAX_SIZE]; u32 num; }; struct func_pdev_info { u64 bar0_phy_addr; u64 bar0_size; u64 rsvd1[4]; }; struct hinic_card_func_info { u32 num_pf; u32 rsvd0; u64 usr_api_phy_addr; struct func_pdev_info pdev_info[MAX_SIZE]; }; #define MAX_CARD_NUM 64 extern void *g_hinic_card_node_array[MAX_CARD_NUM]; extern void *g_hinic_card_vir_addr[MAX_CARD_NUM]; extern u64 g_hinic_card_phy_addr[MAX_CARD_NUM]; extern struct mutex g_hinic_addr_lock; extern int g_hinic_card_id; struct hinic_nic_loop_mode { u32 loop_mode; u32 loop_ctrl; }; struct hinic_nic_poll_weight { int poll_weight; }; enum hinic_homologues_state { HINIC_HOMOLOGUES_OFF = 0, HINIC_HOMOLOGUES_ON = 1, }; struct hinic_homologues { enum hinic_homologues_state homo_state; }; struct hinic_pf_info { u32 isvalid; u32 pf_id; }; int hinic_tool_k_init(void); void hinic_tool_k_uninit(void); u32 hinic_get_io_stats_size(struct hinic_nic_dev *nic_dev); void hinic_get_io_stats(struct hinic_nic_dev *nic_dev, struct hinic_show_item *items); #define TOOL_COUNTER_MAX_LEN 512 #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_pci_id_tbl.h000066400000000000000000000025741502027332500225520ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_PCI_ID_TBL_H #define HINIC_PCI_ID_TBL_H #define PCI_VENDOR_ID_HUAWEI 0x19e5 #define HINIC_DEV_ID_1822_PF 0x1822 #define HINIC_DEV_ID_1822_VF 0x375E #define HINIC_DEV_ID_1822_VF_HV 0x379E #define HINIC_DEV_ID_1822_SMTIO 0x020B #define HINIC_DEV_ID_1822_PANGEA_100GE 0x0208 #define HINIC_DEV_ID_1822_PANGEA_TP_10GE 0x0204 #define HINIC_DEV_ID_1822_KR_40GE 0x020D #define HINIC_DEV_ID_1822_KR_100GE 0x0205 #define HINIC_DEV_ID_1822_DUAL_25GE 0x0206 #define HINIC_DEV_ID_1822_KR_25GE 0x0210 #define HINIC_DEV_ID_1822_MULTI_HOST 0x0211 #define HINIC_DEV_ID_1822_100GE 0x0200 #define HINIC_DEV_ID_1822_100GE_MULTI_HOST 0x0201 #define HIFC_DEV_ID_1822_8G 0x0212 #define HIFC_DEV_ID_1822_16G 0x0203 #define HIFC_DEV_ID_1822_32G 0x0202 #define HIFC_DEV_ID_1822_SMTIO 0x020C #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_port_cmd.h000066400000000000000000000365361502027332500222760ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef __HINIC_PORT_CMD_H__ #define __HINIC_PORT_CMD_H__ /* cmd of mgmt CPU message for NIC module */ enum hinic_port_cmd { HINIC_PORT_CMD_VF_REGISTER = 0x0, /* not defined in base line, only for PFD and VFD */ HINIC_PORT_CMD_VF_UNREGISTER = 0x1, /* not defined in base line, only for PFD and VFD */ HINIC_PORT_CMD_CHANGE_MTU = 0x2, HINIC_PORT_CMD_ADD_VLAN = 0x3, HINIC_PORT_CMD_DEL_VLAN, HINIC_PORT_CMD_SET_PFC = 0x5, HINIC_PORT_CMD_GET_PFC, HINIC_PORT_CMD_SET_ETS, HINIC_PORT_CMD_GET_ETS, HINIC_PORT_CMD_SET_MAC = 0x9, HINIC_PORT_CMD_GET_MAC, HINIC_PORT_CMD_DEL_MAC, HINIC_PORT_CMD_SET_RX_MODE = 0xc, HINIC_PORT_CMD_SET_ANTI_ATTACK_RATE = 0xd, HINIC_PORT_CMD_GET_AUTONEG_CAP = 0xf, /* not defined in base line */ HINIC_PORT_CMD_GET_AUTONET_STATE, /* not defined in base line */ HINIC_PORT_CMD_GET_SPEED, /* not defined in base line */ HINIC_PORT_CMD_GET_DUPLEX, /* not defined in base line */ HINIC_PORT_CMD_GET_MEDIA_TYPE, /* not defined in base line */ HINIC_PORT_CMD_GET_PAUSE_INFO = 0x14, HINIC_PORT_CMD_SET_PAUSE_INFO, HINIC_PORT_CMD_GET_LINK_STATE = 0x18, HINIC_PORT_CMD_SET_LRO = 0x19, HINIC_PORT_CMD_SET_RX_CSUM = 0x1a, HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD = 0x1b, HINIC_PORT_CMD_GET_PORT_STATISTICS = 0x1c, HINIC_PORT_CMD_CLEAR_PORT_STATISTICS, HINIC_PORT_CMD_GET_VPORT_STAT, HINIC_PORT_CMD_CLEAN_VPORT_STAT, HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL = 0x25, HINIC_PORT_CMD_SET_RSS_TEMPLATE_INDIR_TBL, HINIC_PORT_CMD_SET_PORT_ENABLE = 0x29, HINIC_PORT_CMD_GET_PORT_ENABLE, HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL = 0x2b, HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL, HINIC_PORT_CMD_SET_RSS_HASH_ENGINE, HINIC_PORT_CMD_GET_RSS_HASH_ENGINE, HINIC_PORT_CMD_GET_RSS_CTX_TBL, HINIC_PORT_CMD_SET_RSS_CTX_TBL, HINIC_PORT_CMD_RSS_TEMP_MGR, /* 0x36 ~ 0x40 have defined in base line */ HINIC_PORT_CMD_RSS_CFG = 0x42, HINIC_PORT_CMD_GET_PHY_TYPE = 0x44, HINIC_PORT_CMD_INIT_FUNC = 0x45, HINIC_PORT_CMD_SET_LLI_PRI = 0x46, HINIC_PORT_CMD_GET_LOOPBACK_MODE = 0x48, HINIC_PORT_CMD_SET_LOOPBACK_MODE, HINIC_PORT_CMD_GET_JUMBO_FRAME_SIZE = 0x4a, HINIC_PORT_CMD_SET_JUMBO_FRAME_SIZE, /* 0x4c ~ 0x57 have defined in base line */ HINIC_PORT_CMD_DISABLE_PROMISC = 0x4c, HINIC_PORT_CMD_ENABLE_SPOOFCHK = 0x4e, HINIC_PORT_CMD_GET_MGMT_VERSION = 0x58, HINIC_PORT_CMD_GET_BOOT_VERSION, HINIC_PORT_CMD_GET_MICROCODE_VERSION, HINIC_PORT_CMD_GET_PORT_TYPE = 0x5b, /* not defined in base line */ HINIC_PORT_CMD_GET_VPORT_ENABLE = 0x5c, HINIC_PORT_CMD_SET_VPORT_ENABLE, HINIC_PORT_CMD_GET_PORT_ID_BY_FUNC_ID = 0x5e, HINIC_PORT_CMD_SET_LED_TEST = 0x5f, HINIC_PORT_CMD_SET_LLI_STATE = 0x60, HINIC_PORT_CMD_SET_LLI_TYPE, HINIC_PORT_CMD_GET_LLI_CFG, HINIC_PORT_CMD_GET_LRO = 0x63, HINIC_PORT_CMD_GET_DMA_CS = 0x64, HINIC_PORT_CMD_SET_DMA_CS, HINIC_PORT_CMD_GET_GLOBAL_QPN = 0x66, HINIC_PORT_CMD_SET_PFC_MISC = 0x67, HINIC_PORT_CMD_GET_PFC_MISC, HINIC_PORT_CMD_SET_VF_RATE = 0x69, HINIC_PORT_CMD_SET_VF_VLAN, HINIC_PORT_CMD_CLR_VF_VLAN, /* 0x6c,0x6e have defined in base line */ HINIC_PORT_CMD_SET_UCAPTURE_OPT = 0x6F, HINIC_PORT_CMD_SET_TSO = 0x70, HINIC_PORT_CMD_SET_PHY_POWER = 0x71, HINIC_PORT_CMD_UPDATE_FW = 0x72, HINIC_PORT_CMD_SET_RQ_IQ_MAP = 0x73, /* not defined in base line */ HINIC_PORT_CMD_SET_PFC_THD = 0x75, /* not defined in base line */ HINIC_PORT_CMD_SET_PORT_LINK_STATUS = 0x76, HINIC_PORT_CMD_SET_CGE_PAUSE_TIME_CFG = 0x77, HINIC_PORT_CMD_GET_FW_SUPPORT_FLAG = 0x79, HINIC_PORT_CMD_SET_PORT_REPORT = 0x7B, HINIC_PORT_CMD_LINK_STATUS_REPORT = 0xa0, HINIC_PORT_CMD_SET_LOSSLESS_ETH = 0xa3, HINIC_PORT_CMD_UPDATE_MAC = 0xa4, HINIC_PORT_CMD_GET_UART_LOG = 0xa5, HINIC_PORT_CMD_SET_UART_LOG, HINIC_PORT_CMD_GET_PORT_INFO = 0xaa, HINIC_MISC_SET_FUNC_SF_ENBITS = 0xab, /* not defined in base line */ HINIC_MISC_GET_FUNC_SF_ENBITS, /* not defined in base line */ HINIC_PORT_CMD_GET_SFP_INFO = 0xad, HINIC_PORT_CMD_SET_NETQ = 0xc1, HINIC_PORT_CMD_ADD_RQ_FILTER = 0xc2, HINIC_PORT_CMD_DEL_RQ_FILTER = 0xc3, HINIC_PORT_CMD_GET_FW_LOG = 0xca, HINIC_PORT_CMD_SET_IPSU_MAC = 0xcb, HINIC_PORT_CMD_GET_IPSU_MAC = 0xcc, HINIC_PORT_CMD_SET_XSFP_STATUS = 0xD4, HINIC_PORT_CMD_SET_IQ_ENABLE = 0xd6, HINIC_PORT_CMD_GET_LINK_MODE = 0xD9, HINIC_PORT_CMD_SET_SPEED = 0xDA, HINIC_PORT_CMD_SET_AUTONEG = 0xDB, HINIC_PORT_CMD_CLEAR_SQ_RES = 0xDD, HINIC_PORT_CMD_SET_SUPER_CQE = 0xDE, HINIC_PORT_CMD_SET_VF_COS = 0xDF, HINIC_PORT_CMD_GET_VF_COS = 0xE1, HINIC_PORT_CMD_CABLE_PLUG_EVENT = 0xE5, HINIC_PORT_CMD_LINK_ERR_EVENT = 0xE6, HINIC_PORT_CMD_SET_PORT_FUNCS_STATE = 0xE7, HINIC_PORT_CMD_SET_COS_UP_MAP = 0xE8, HINIC_PORT_CMD_RESET_LINK_CFG = 0xEB, HINIC_PORT_CMD_GET_STD_SFP_INFO = 0xF0, HINIC_PORT_CMD_FORCE_PKT_DROP = 0xF3, HINIC_PORT_CMD_SET_LRO_TIMER = 0xF4, HINIC_PORT_CMD_SET_VHD_CFG = 0xF7, HINIC_PORT_CMD_SET_LINK_FOLLOW = 0xF8, HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE = 0xF9, HINIC_PORT_CMD_SET_RXQ_LRO_ADPT = 0xFA, HINIC_PORT_CMD_GET_SFP_ABS = 0xFB, HINIC_PORT_CMD_Q_FILTER = 0xFC, HINIC_PORT_CMD_TCAM_FILTER = 0xFE, HINIC_PORT_CMD_SET_VLAN_FILTER = 0xFF, }; /* cmd of mgmt CPU message for HW module */ enum hinic_mgmt_cmd { HINIC_MGMT_CMD_RESET_MGMT = 0x0, HINIC_MGMT_CMD_START_FLR = 0x1, HINIC_MGMT_CMD_FLUSH_DOORBELL = 0x2, HINIC_MGMT_CMD_GET_IO_STATUS = 0x3, HINIC_MGMT_CMD_DMA_ATTR_SET = 0x4, HINIC_MGMT_CMD_CMDQ_CTXT_SET = 0x10, HINIC_MGMT_CMD_CMDQ_CTXT_GET, HINIC_MGMT_CMD_VAT_SET = 0x12, HINIC_MGMT_CMD_VAT_GET, HINIC_MGMT_CMD_L2NIC_SQ_CI_ATTR_SET = 0x14, HINIC_MGMT_CMD_L2NIC_SQ_CI_ATTR_GET, HINIC_MGMT_CMD_MQM_FIX_INFO_GET = 0x16, HINIC_MGMT_CMD_MQM_CFG_INFO_SET = 0x18, HINIC_MGMT_CMD_MQM_SRCH_GPA_SET = 0x20, HINIC_MGMT_CMD_PPF_TMR_SET = 0x22, HINIC_MGMT_CMD_PPF_HT_GPA_SET = 0x23, HINIC_MGMT_CMD_RES_STATE_SET = 0x24, HINIC_MGMT_CMD_FUNC_CACHE_OUT = 0x25, HINIC_MGMT_CMD_FFM_SET = 0x26, HINIC_MGMT_CMD_SMF_TMR_CLEAR = 0x27, /* 0x29 not defined in base line, * only used in open source driver */ HINIC_MGMT_CMD_FUNC_RES_CLEAR = 0x29, HINIC_MGMT_CMD_FUNC_TMR_BITMAT_SET = 0x32, HINIC_MGMT_CMD_CEQ_CTRL_REG_WR_BY_UP = 0x33, HINIC_MGMT_CMD_MSI_CTRL_REG_WR_BY_UP, HINIC_MGMT_CMD_MSI_CTRL_REG_RD_BY_UP, HINIC_MGMT_CMD_VF_RANDOM_ID_SET = 0x36, HINIC_MGMT_CMD_FAULT_REPORT = 0x37, HINIC_MGMT_CMD_HEART_LOST_REPORT = 0x38, HINIC_MGMT_CMD_VPD_SET = 0x40, HINIC_MGMT_CMD_VPD_GET, HINIC_MGMT_CMD_LABEL_SET, HINIC_MGMT_CMD_LABEL_GET, HINIC_MGMT_CMD_SATIC_MAC_SET, HINIC_MGMT_CMD_SATIC_MAC_GET, HINIC_MGMT_CMD_SYNC_TIME = 0x46, HINIC_MGMT_CMD_REG_READ = 0x48, HINIC_MGMT_CMD_SET_LED_STATUS = 0x4A, HINIC_MGMT_CMD_L2NIC_RESET = 0x4b, HINIC_MGMT_CMD_FAST_RECYCLE_MODE_SET = 0x4d, HINIC_MGMT_CMD_BIOS_NV_DATA_MGMT = 0x4E, HINIC_MGMT_CMD_ACTIVATE_FW = 0x4F, HINIC_MGMT_CMD_PAGESIZE_SET = 0x50, HINIC_MGMT_CMD_PAGESIZE_GET = 0x51, HINIC_MGMT_CMD_GET_BOARD_INFO = 0x52, HINIC_MGMT_CMD_WATCHDOG_INFO = 0x56, HINIC_MGMT_CMD_FMW_ACT_NTC = 0x57, HINIC_MGMT_CMD_SET_VF_RANDOM_ID = 0x61, HINIC_MGMT_CMD_GET_PPF_STATE = 0x63, HINIC_MGMT_CMD_PCIE_DFX_NTC = 0x65, HINIC_MGMT_CMD_PCIE_DFX_GET = 0x66, HINIC_MGMT_CMD_GET_HOST_INFO = 0x67, HINIC_MGMT_CMD_GET_PHY_INIT_STATUS = 0x6A, HINIC_MGMT_CMD_HEARTBEAT_SUPPORTED = 0x6B, HINIC_MGMT_CMD_HEARTBEAT_EVENT = 0x6C, HINIC_MGMT_CMD_GET_HW_PF_INFOS = 0x6D, HINIC_MGMT_CMD_GET_SDI_MODE = 0x6E, HINIC_MGMT_CMD_ENABLE_MIGRATE = 0x6F, }; /* uCode relates commands */ enum hinic_ucode_cmd { HINIC_UCODE_CMD_MODIFY_QUEUE_CONTEXT = 0, HINIC_UCODE_CMD_CLEAN_QUEUE_CONTEXT, HINIC_UCODE_CMD_ARM_SQ, HINIC_UCODE_CMD_ARM_RQ, HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE, HINIC_UCODE_CMD_SET_RSS_CONTEXT_TABLE, HINIC_UCODE_CMD_GET_RSS_INDIR_TABLE, HINIC_UCODE_CMD_GET_RSS_CONTEXT_TABLE, HINIC_UCODE_CMD_SET_IQ_ENABLE, HINIC_UCODE_CMD_SET_RQ_FLUSH = 10 }; /* software cmds, vf->pf and multi-host */ enum hinic_sw_funcs_cmd { HINIC_SW_CMD_SLAVE_HOST_PPF_REGISTER = 0x0, HINIC_SW_CMD_SLAVE_HOST_PPF_UNREGISTER = 0x1, HINIC_SW_CMD_GET_SLAVE_FUNC_NIC_STATE = 0x2, HINIC_SW_CMD_SET_SLAVE_FUNC_NIC_STATE = 0x3, HINIC_SW_CMD_SEND_MSG_TO_VF = 0x4, HINIC_SW_CMD_MIGRATE_READY = 0x5, }; enum sq_l4offload_type { OFFLOAD_DISABLE = 0, TCP_OFFLOAD_ENABLE = 1, SCTP_OFFLOAD_ENABLE = 2, UDP_OFFLOAD_ENABLE = 3, }; enum sq_vlan_offload_flag { VLAN_OFFLOAD_DISABLE = 0, VLAN_OFFLOAD_ENABLE = 1, }; enum sq_pkt_parsed_flag { PKT_NOT_PARSED = 0, PKT_PARSED = 1, }; enum sq_l3_type { UNKNOWN_L3TYPE = 0, IPV6_PKT = 1, IPV4_PKT_NO_CHKSUM_OFFLOAD = 2, IPV4_PKT_WITH_CHKSUM_OFFLOAD = 3, }; enum sq_md_type { UNKNOWN_MD_TYPE = 0, }; enum sq_l2type { ETHERNET = 0, }; enum sq_tunnel_l4_type { NOT_TUNNEL, TUNNEL_UDP_NO_CSUM, TUNNEL_UDP_CSUM, }; #define NIC_RSS_CMD_TEMP_ALLOC 0x01 #define NIC_RSS_CMD_TEMP_FREE 0x02 #define HINIC_RSS_TYPE_VALID_SHIFT 23 #define HINIC_RSS_TYPE_TCP_IPV6_EXT_SHIFT 24 #define HINIC_RSS_TYPE_IPV6_EXT_SHIFT 25 #define HINIC_RSS_TYPE_TCP_IPV6_SHIFT 26 #define HINIC_RSS_TYPE_IPV6_SHIFT 27 #define HINIC_RSS_TYPE_TCP_IPV4_SHIFT 28 #define HINIC_RSS_TYPE_IPV4_SHIFT 29 #define HINIC_RSS_TYPE_UDP_IPV6_SHIFT 30 #define HINIC_RSS_TYPE_UDP_IPV4_SHIFT 31 #define HINIC_RSS_TYPE_SET(val, member) \ (((u32)(val) & 0x1) << HINIC_RSS_TYPE_##member##_SHIFT) #define HINIC_RSS_TYPE_GET(val, member) \ (((u32)(val) >> HINIC_RSS_TYPE_##member##_SHIFT) & 0x1) enum hinic_speed { HINIC_SPEED_10MB_LINK = 0, HINIC_SPEED_100MB_LINK, HINIC_SPEED_1000MB_LINK, HINIC_SPEED_10GB_LINK, HINIC_SPEED_25GB_LINK, HINIC_SPEED_40GB_LINK, HINIC_SPEED_100GB_LINK, HINIC_SPEED_UNKNOWN = 0xFF, }; /* In order to adapt different linux version */ enum { HINIC_IFLA_VF_LINK_STATE_AUTO, /* link state of the uplink */ HINIC_IFLA_VF_LINK_STATE_ENABLE, /* link always up */ HINIC_IFLA_VF_LINK_STATE_DISABLE, /* link always down */ }; #define HINIC_AF0_FUNC_GLOBAL_IDX_SHIFT 0 #define HINIC_AF0_P2P_IDX_SHIFT 10 #define HINIC_AF0_PCI_INTF_IDX_SHIFT 14 #define HINIC_AF0_VF_IN_PF_SHIFT 16 #define HINIC_AF0_FUNC_TYPE_SHIFT 24 #define HINIC_AF0_FUNC_GLOBAL_IDX_MASK 0x3FF #define HINIC_AF0_P2P_IDX_MASK 0xF #define HINIC_AF0_PCI_INTF_IDX_MASK 0x3 #define HINIC_AF0_VF_IN_PF_MASK 0xFF #define HINIC_AF0_FUNC_TYPE_MASK 0x1 #define HINIC_AF0_GET(val, member) \ (((val) >> HINIC_AF0_##member##_SHIFT) & HINIC_AF0_##member##_MASK) #define HINIC_AF1_PPF_IDX_SHIFT 0 #define HINIC_AF1_AEQS_PER_FUNC_SHIFT 8 #define HINIC_AF1_CEQS_PER_FUNC_SHIFT 12 #define HINIC_AF1_IRQS_PER_FUNC_SHIFT 20 #define HINIC_AF1_DMA_ATTR_PER_FUNC_SHIFT 24 #define HINIC_AF1_MGMT_INIT_STATUS_SHIFT 30 #define HINIC_AF1_PF_INIT_STATUS_SHIFT 31 #define HINIC_AF1_PPF_IDX_MASK 0x1F #define HINIC_AF1_AEQS_PER_FUNC_MASK 0x3 #define HINIC_AF1_CEQS_PER_FUNC_MASK 0x7 #define HINIC_AF1_IRQS_PER_FUNC_MASK 0xF #define HINIC_AF1_DMA_ATTR_PER_FUNC_MASK 0x7 #define HINIC_AF1_MGMT_INIT_STATUS_MASK 0x1 #define HINIC_AF1_PF_INIT_STATUS_MASK 0x1 #define HINIC_AF1_GET(val, member) \ (((val) >> HINIC_AF1_##member##_SHIFT) & HINIC_AF1_##member##_MASK) #define HINIC_AF2_GLOBAL_VF_ID_OF_PF_SHIFT 16 #define HINIC_AF2_GLOBAL_VF_ID_OF_PF_MASK 0x3FF #define HINIC_AF2_GET(val, member) \ (((val) >> HINIC_AF2_##member##_SHIFT) & HINIC_AF2_##member##_MASK) #define HINIC_AF4_OUTBOUND_CTRL_SHIFT 0 #define HINIC_AF4_DOORBELL_CTRL_SHIFT 1 #define HINIC_AF4_OUTBOUND_CTRL_MASK 0x1 #define HINIC_AF4_DOORBELL_CTRL_MASK 0x1 #define HINIC_AF4_GET(val, member) \ (((val) >> HINIC_AF4_##member##_SHIFT) & HINIC_AF4_##member##_MASK) #define HINIC_AF4_SET(val, member) \ (((val) & HINIC_AF4_##member##_MASK) << HINIC_AF4_##member##_SHIFT) #define HINIC_AF4_CLEAR(val, member) \ ((val) & (~(HINIC_AF4_##member##_MASK << \ HINIC_AF4_##member##_SHIFT))) #define HINIC_AF5_PF_STATUS_SHIFT 0 #define HINIC_AF5_PF_STATUS_MASK 0xFFFF #define HINIC_AF5_SET(val, member) \ (((val) & HINIC_AF5_##member##_MASK) << HINIC_AF5_##member##_SHIFT) #define HINIC_AF5_GET(val, member) \ (((val) >> HINIC_AF5_##member##_SHIFT) & HINIC_AF5_##member##_MASK) #define HINIC_AF5_CLEAR(val, member) \ ((val) & (~(HINIC_AF5_##member##_MASK << \ HINIC_AF5_##member##_SHIFT))) #define HINIC_PPF_ELECTION_IDX_SHIFT 0 #define HINIC_PPF_ELECTION_IDX_MASK 0x1F #define HINIC_PPF_ELECTION_SET(val, member) \ (((val) & HINIC_PPF_ELECTION_##member##_MASK) << \ HINIC_PPF_ELECTION_##member##_SHIFT) #define HINIC_PPF_ELECTION_GET(val, member) \ (((val) >> HINIC_PPF_ELECTION_##member##_SHIFT) & \ HINIC_PPF_ELECTION_##member##_MASK) #define HINIC_PPF_ELECTION_CLEAR(val, member) \ ((val) & (~(HINIC_PPF_ELECTION_##member##_MASK \ << HINIC_PPF_ELECTION_##member##_SHIFT))) #define HINIC_MPF_ELECTION_IDX_SHIFT 0 #define HINIC_MPF_ELECTION_IDX_MASK 0x1F #define HINIC_MPF_ELECTION_SET(val, member) \ (((val) & HINIC_MPF_ELECTION_##member##_MASK) << \ HINIC_MPF_ELECTION_##member##_SHIFT) #define HINIC_MPF_ELECTION_GET(val, member) \ (((val) >> HINIC_MPF_ELECTION_##member##_SHIFT) & \ HINIC_MPF_ELECTION_##member##_MASK) #define HINIC_MPF_ELECTION_CLEAR(val, member) \ ((val) & (~(HINIC_MPF_ELECTION_##member##_MASK \ << HINIC_MPF_ELECTION_##member##_SHIFT))) #define HINIC_HWIF_NUM_AEQS(hwif) ((hwif)->attr.num_aeqs) #define HINIC_HWIF_NUM_CEQS(hwif) ((hwif)->attr.num_ceqs) #define HINIC_HWIF_NUM_IRQS(hwif) ((hwif)->attr.num_irqs) #define HINIC_HWIF_GLOBAL_IDX(hwif) ((hwif)->attr.func_global_idx) #define HINIC_HWIF_GLOBAL_VF_OFFSET(hwif) ((hwif)->attr.global_vf_id_of_pf) #define HINIC_HWIF_PPF_IDX(hwif) ((hwif)->attr.ppf_idx) #define HINIC_PCI_INTF_IDX(hwif) ((hwif)->attr.pci_intf_idx) #define HINIC_FUNC_TYPE(dev) ((dev)->hwif->attr.func_type) #define HINIC_IS_PF(dev) (HINIC_FUNC_TYPE(dev) == TYPE_PF) #define HINIC_IS_VF(dev) (HINIC_FUNC_TYPE(dev) == TYPE_VF) #define HINIC_IS_PPF(dev) (HINIC_FUNC_TYPE(dev) == TYPE_PPF) #define DB_IDX(db, db_base) \ ((u32)(((ulong)(db) - (ulong)(db_base)) / \ HINIC_DB_PAGE_SIZE)) enum hinic_pcie_nosnoop { HINIC_PCIE_SNOOP = 0, HINIC_PCIE_NO_SNOOP = 1, }; enum hinic_pcie_tph { HINIC_PCIE_TPH_DISABLE = 0, HINIC_PCIE_TPH_ENABLE = 1, }; enum hinic_outbound_ctrl { ENABLE_OUTBOUND = 0x0, DISABLE_OUTBOUND = 0x1, }; enum hinic_doorbell_ctrl { ENABLE_DOORBELL = 0x0, DISABLE_DOORBELL = 0x1, }; enum hinic_pf_status { HINIC_PF_STATUS_INIT = 0x0, HINIC_PF_STATUS_ACTIVE_FLAG = 0x11, HINIC_PF_STATUS_FLR_START_FLAG = 0x12, HINIC_PF_STATUS_FLR_FINISH_FLAG = 0x13, }; /* total doorbell or direct wqe size is 512kB, db num: 128, dwqe: 128 */ #define HINIC_DB_DWQE_SIZE 0x00080000 /* BMGW & VMGW VF db size 256k, have no dwqe space */ #define HINIC_GW_VF_DB_SIZE 0x00040000 /* db/dwqe page size: 4K */ #define HINIC_DB_PAGE_SIZE 0x00001000ULL #define HINIC_DB_MAX_AREAS (HINIC_DB_DWQE_SIZE / HINIC_DB_PAGE_SIZE) #define HINIC_PCI_MSIX_ENTRY_SIZE 16 #define HINIC_PCI_MSIX_ENTRY_VECTOR_CTRL 12 #define HINIC_PCI_MSIX_ENTRY_CTRL_MASKBIT 1 #endif /* __HINIC_PORT_CMD_H__ */ hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_qe_def.h000066400000000000000000000344141502027332500217030ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef __HINIC_QE_DEF_H__ #define __HINIC_QE_DEF_H__ #define HINIC_SQ_WQEBB_SIZE 64 #define HINIC_RQ_WQE_SIZE 32 #define HINIC_SQ_WQEBB_SHIFT 6 #define HINIC_RQ_WQEBB_SHIFT 5 #define HINIC_MAX_QUEUE_DEPTH 4096 #define HINIC_MIN_QUEUE_DEPTH 128 #define HINIC_TXD_ALIGN 1 #define HINIC_RXD_ALIGN 1 #define HINIC_SQ_DEPTH 1024 #define HINIC_RQ_DEPTH 1024 #define HINIC_RQ_WQE_MAX_SIZE 32 #define SIZE_8BYTES(size) (ALIGN((u32)(size), 8) >> 3)//lint !e767 /************** SQ_CTRL ***************/ #define SQ_CTRL_BUFDESC_SECT_LEN_SHIFT 0 #define SQ_CTRL_TASKSECT_LEN_SHIFT 16 #define SQ_CTRL_DATA_FORMAT_SHIFT 22 #define SQ_CTRL_LEN_SHIFT 29 #define SQ_CTRL_OWNER_SHIFT 31 #define SQ_CTRL_BUFDESC_SECT_LEN_MASK 0xFFU #define SQ_CTRL_TASKSECT_LEN_MASK 0x1FU #define SQ_CTRL_DATA_FORMAT_MASK 0x1U #define SQ_CTRL_LEN_MASK 0x3U #define SQ_CTRL_OWNER_MASK 0x1U #define SQ_CTRL_GET(val, member) (((val) >> SQ_CTRL_##member##_SHIFT) \ & SQ_CTRL_##member##_MASK) #define SQ_CTRL_CLEAR(val, member) ((val) & \ (~(SQ_CTRL_##member##_MASK << \ SQ_CTRL_##member##_SHIFT))) #define SQ_CTRL_QUEUE_INFO_PLDOFF_SHIFT 2 #define SQ_CTRL_QUEUE_INFO_UFO_SHIFT 10 #define SQ_CTRL_QUEUE_INFO_TSO_SHIFT 11 #define SQ_CTRL_QUEUE_INFO_TCPUDP_CS_SHIFT 12 #define SQ_CTRL_QUEUE_INFO_MSS_SHIFT 13 #define SQ_CTRL_QUEUE_INFO_SCTP_SHIFT 27 #define SQ_CTRL_QUEUE_INFO_UC_SHIFT 28 #define SQ_CTRL_QUEUE_INFO_PRI_SHIFT 29 #define SQ_CTRL_QUEUE_INFO_PLDOFF_MASK 0xFFU #define SQ_CTRL_QUEUE_INFO_UFO_MASK 0x1U #define SQ_CTRL_QUEUE_INFO_TSO_MASK 0x1U #define SQ_CTRL_QUEUE_INFO_TCPUDP_CS_MASK 0x1U #define SQ_CTRL_QUEUE_INFO_MSS_MASK 0x3FFFU #define SQ_CTRL_QUEUE_INFO_SCTP_MASK 0x1U #define SQ_CTRL_QUEUE_INFO_UC_MASK 0x1U #define SQ_CTRL_QUEUE_INFO_PRI_MASK 0x7U #define SQ_CTRL_QUEUE_INFO_SET(val, member) \ (((u32)(val) & SQ_CTRL_QUEUE_INFO_##member##_MASK) \ << SQ_CTRL_QUEUE_INFO_##member##_SHIFT) #define SQ_CTRL_QUEUE_INFO_GET(val, member) \ (((val) >> SQ_CTRL_QUEUE_INFO_##member##_SHIFT) \ & SQ_CTRL_QUEUE_INFO_##member##_MASK) #define SQ_CTRL_QUEUE_INFO_CLEAR(val, member) \ ((val) & (~(SQ_CTRL_QUEUE_INFO_##member##_MASK << \ SQ_CTRL_QUEUE_INFO_##member##_SHIFT))) #define SQ_TASK_INFO0_L2HDR_LEN_SHIFT 0 #define SQ_TASK_INFO0_L4OFFLOAD_SHIFT 8 #define SQ_TASK_INFO0_INNER_L3TYPE_SHIFT 10 #define SQ_TASK_INFO0_VLAN_OFFLOAD_SHIFT 12 #define SQ_TASK_INFO0_PARSE_FLAG_SHIFT 13 #define SQ_TASK_INFO0_UFO_AVD_SHIFT 14 #define SQ_TASK_INFO0_TSO_UFO_SHIFT 15 #define SQ_TASK_INFO0_VLAN_TAG_SHIFT 16 #define SQ_TASK_INFO0_L2HDR_LEN_MASK 0xFFU #define SQ_TASK_INFO0_L4OFFLOAD_MASK 0x3U #define SQ_TASK_INFO0_INNER_L3TYPE_MASK 0x3U #define SQ_TASK_INFO0_VLAN_OFFLOAD_MASK 0x1U #define SQ_TASK_INFO0_PARSE_FLAG_MASK 0x1U #define SQ_TASK_INFO0_UFO_AVD_MASK 0x1U #define SQ_TASK_INFO0_TSO_UFO_MASK 0x1U #define SQ_TASK_INFO0_VLAN_TAG_MASK 0xFFFFU #define SQ_TASK_INFO0_SET(val, member) \ (((u32)(val) & SQ_TASK_INFO0_##member##_MASK) << \ SQ_TASK_INFO0_##member##_SHIFT) #define SQ_TASK_INFO0_GET(val, member) \ (((val) >> SQ_TASK_INFO0_##member##_SHIFT) & \ SQ_TASK_INFO0_##member##_MASK) #define SQ_TASK_INFO1_MD_TYPE_SHIFT 8 #define SQ_TASK_INFO1_INNER_L4LEN_SHIFT 16 #define SQ_TASK_INFO1_INNER_L3LEN_SHIFT 24 #define SQ_TASK_INFO1_MD_TYPE_MASK 0xFFU #define SQ_TASK_INFO1_INNER_L4LEN_MASK 0xFFU #define SQ_TASK_INFO1_INNER_L3LEN_MASK 0xFFU #define SQ_TASK_INFO1_SET(val, member) \ (((val) & SQ_TASK_INFO1_##member##_MASK) << \ SQ_TASK_INFO1_##member##_SHIFT) #define SQ_TASK_INFO1_GET(val, member) \ (((val) >> SQ_TASK_INFO1_##member##_SHIFT) & \ SQ_TASK_INFO1_##member##_MASK) #define SQ_TASK_INFO2_TUNNEL_L4LEN_SHIFT 0 #define SQ_TASK_INFO2_OUTER_L3LEN_SHIFT 8 #define SQ_TASK_INFO2_TUNNEL_L4TYPE_SHIFT 16 #define SQ_TASK_INFO2_OUTER_L3TYPE_SHIFT 24 #define SQ_TASK_INFO2_TUNNEL_L4LEN_MASK 0xFFU #define SQ_TASK_INFO2_OUTER_L3LEN_MASK 0xFFU #define SQ_TASK_INFO2_TUNNEL_L4TYPE_MASK 0x7U #define SQ_TASK_INFO2_OUTER_L3TYPE_MASK 0x3U #define SQ_TASK_INFO2_SET(val, member) \ (((val) & SQ_TASK_INFO2_##member##_MASK) << \ SQ_TASK_INFO2_##member##_SHIFT) #define SQ_TASK_INFO2_GET(val, member) \ (((val) >> SQ_TASK_INFO2_##member##_SHIFT) & \ SQ_TASK_INFO2_##member##_MASK) #define SQ_TASK_INFO4_L2TYPE_SHIFT 31 #define SQ_TASK_INFO4_L2TYPE_MASK 0x1U #define SQ_TASK_INFO4_SET(val, member) \ (((u32)(val) & SQ_TASK_INFO4_##member##_MASK) << \ SQ_TASK_INFO4_##member##_SHIFT) /********************* SQ_DB *********************/ #define SQ_DB_OFF 0x00000800 #define SQ_DB_INFO_HI_PI_SHIFT 0 #define SQ_DB_INFO_QID_SHIFT 8 #define SQ_DB_INFO_CFLAG_SHIFT 23 #define SQ_DB_INFO_COS_SHIFT 24 #define SQ_DB_INFO_TYPE_SHIFT 27 #define SQ_DB_INFO_HI_PI_MASK 0xFFU #define SQ_DB_INFO_QID_MASK 0x3FFU #define SQ_DB_INFO_CFLAG_MASK 0x1U #define SQ_DB_INFO_COS_MASK 0x7U #define SQ_DB_INFO_TYPE_MASK 0x1FU #define SQ_DB_INFO_SET(val, member) \ (((u32)(val) & SQ_DB_INFO_##member##_MASK) << \ SQ_DB_INFO_##member##_SHIFT) #define SQ_DB_PI_LOW_MASK 0xFF #define SQ_DB_PI_LOW(pi) ((pi) & SQ_DB_PI_LOW_MASK) #define SQ_DB_PI_HI_SHIFT 8 #define SQ_DB_PI_HIGH(pi) ((pi) >> SQ_DB_PI_HI_SHIFT) #define SQ_DB_ADDR(sq, pi) ((u64 *)((sq)->db_addr + SQ_DB_OFF) + \ SQ_DB_PI_LOW(pi)) #define SQ_DB 1 #define SQ_CFLAG_DP 0 /* CFLAG_DATA_PATH */ /*********************** RQ_CTRL ******************/ #define RQ_CTRL_BUFDESC_SECT_LEN_SHIFT 0 #define RQ_CTRL_COMPLETE_FORMAT_SHIFT 15 #define RQ_CTRL_COMPLETE_LEN_SHIFT 27 #define RQ_CTRL_LEN_SHIFT 29 #define RQ_CTRL_BUFDESC_SECT_LEN_MASK 0xFFU #define RQ_CTRL_COMPLETE_FORMAT_MASK 0x1U #define RQ_CTRL_COMPLETE_LEN_MASK 0x3U #define RQ_CTRL_LEN_MASK 0x3U #define RQ_CTRL_SET(val, member) \ (((val) & RQ_CTRL_##member##_MASK) << \ RQ_CTRL_##member##_SHIFT) #define RQ_CTRL_GET(val, member) \ (((val) >> RQ_CTRL_##member##_SHIFT) & \ RQ_CTRL_##member##_MASK) #define RQ_CTRL_CLEAR(val, member) \ ((val) & (~(RQ_CTRL_##member##_MASK << \ RQ_CTRL_##member##_SHIFT))) #define RQ_CQE_STATUS_CSUM_ERR_SHIFT 0 #define RQ_CQE_STATUS_NUM_LRO_SHIFT 16 #define RQ_CQE_STATUS_LRO_PUSH_SHIFT 25 #define RQ_CQE_STATUS_LRO_ENTER_SHIFT 26 #define RQ_CQE_STATUS_LRO_INTR_SHIFT 27 #define RQ_CQE_STATUS_BP_EN_SHIFT 30 #define RQ_CQE_STATUS_RXDONE_SHIFT 31 #define RQ_CQE_STATUS_FLUSH_SHIFT 28 #define RQ_CQE_STATUS_CSUM_ERR_MASK 0xFFFFU #define RQ_CQE_STATUS_NUM_LRO_MASK 0xFFU #define RQ_CQE_STATUS_LRO_PUSH_MASK 0X1U #define RQ_CQE_STATUS_LRO_ENTER_MASK 0X1U #define RQ_CQE_STATUS_LRO_INTR_MASK 0X1U #define RQ_CQE_STATUS_BP_EN_MASK 0X1U #define RQ_CQE_STATUS_RXDONE_MASK 0x1U #define RQ_CQE_STATUS_FLUSH_MASK 0x1U #define RQ_CQE_STATUS_GET(val, member) \ (((val) >> RQ_CQE_STATUS_##member##_SHIFT) & \ RQ_CQE_STATUS_##member##_MASK) #define RQ_CQE_STATUS_CLEAR(val, member) \ ((val) & (~(RQ_CQE_STATUS_##member##_MASK << \ RQ_CQE_STATUS_##member##_SHIFT))) #define RQ_CQE_SGE_VLAN_SHIFT 0 #define RQ_CQE_SGE_LEN_SHIFT 16 #define RQ_CQE_SGE_VLAN_MASK 0xFFFFU #define RQ_CQE_SGE_LEN_MASK 0xFFFFU #define RQ_CQE_SGE_GET(val, member) \ (((val) >> RQ_CQE_SGE_##member##_SHIFT) & \ RQ_CQE_SGE_##member##_MASK) #define RQ_CQE_PKT_NUM_SHIFT 1 #define RQ_CQE_PKT_FIRST_LEN_SHIFT 19 #define RQ_CQE_PKT_LAST_LEN_SHIFT 6 #define RQ_CQE_SUPER_CQE_EN_SHIFT 0 #define RQ_CQE_PKT_FIRST_LEN_MASK 0x1FFFU #define RQ_CQE_PKT_LAST_LEN_MASK 0x1FFFU #define RQ_CQE_PKT_NUM_MASK 0x1FU #define RQ_CQE_SUPER_CQE_EN_MASK 0x1 #define RQ_CQE_PKT_NUM_GET(val, member) \ (((val) >> RQ_CQE_PKT_##member##_SHIFT) & \ RQ_CQE_PKT_##member##_MASK) #define HINIC_GET_RQ_CQE_PKT_NUM(pkt_info) RQ_CQE_PKT_NUM_GET(pkt_info, NUM) #define RQ_CQE_SUPER_CQE_EN_GET(val, member) \ (((val) >> RQ_CQE_##member##_SHIFT) & \ RQ_CQE_##member##_MASK) #define HINIC_GET_SUPER_CQE_EN(pkt_info) \ RQ_CQE_SUPER_CQE_EN_GET(pkt_info, SUPER_CQE_EN) #define HINIC_GET_SUPER_CQE_EN_BE(pkt_info) ((pkt_info) & 0x1000000U) #define RQ_CQE_PKT_LEN_GET(val, member) \ (((val) >> RQ_CQE_PKT_##member##_SHIFT) & \ RQ_CQE_PKT_##member##_MASK) #define RQ_CQE_OFFOLAD_TYPE_VLAN_EN_SHIFT 21 #define RQ_CQE_OFFOLAD_TYPE_VLAN_EN_MASK 0x1U #define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_SHIFT 0 #define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_MASK 0xFFFU #define RQ_CQE_OFFOLAD_TYPE_PKT_UMBCAST_SHIFT 19 #define RQ_CQE_OFFOLAD_TYPE_PKT_UMBCAST_MASK 0x3U #define RQ_CQE_OFFOLAD_TYPE_RSS_TYPE_SHIFT 24 #define RQ_CQE_OFFOLAD_TYPE_RSS_TYPE_MASK 0xFFU #define RQ_CQE_OFFOLAD_TYPE_GET(val, member) \ (((val) >> RQ_CQE_OFFOLAD_TYPE_##member##_SHIFT) & \ RQ_CQE_OFFOLAD_TYPE_##member##_MASK) #define RQ_CQE_PKT_TYPES_NON_L2_MASK 0x800U #define RQ_CQE_PKT_TYPES_L2_MASK 0x7FU #define RQ_CQE_STATUS_CSUM_BYPASS_VAL 0x80 #define RQ_CQE_STATUS_CSUM_ERR_IP_MASK 0x31U #define RQ_CQE_STATUS_CSUM_ERR_L4_MASK 0x4EU #define SECT_SIZE_BYTES(size) ((size) << 3) #define HINIC_PF_SET_VF_ALREADY 0x4 #define HINIC_MGMT_STATUS_EXIST 0x6 #define WQS_BLOCKS_PER_PAGE 4 #define WQ_SIZE(wq) ((u32)((u64)(wq)->q_depth * (wq)->wqebb_size)) #define WQE_PAGE_NUM(wq, idx) (((idx) >> ((wq)->wqebbs_per_page_shift)) & \ ((wq)->num_q_pages - 1)) #define WQE_PAGE_OFF(wq, idx) ((u64)((wq)->wqebb_size) * \ ((idx) & ((wq)->num_wqebbs_per_page - 1))) #define WQ_PAGE_ADDR_SIZE sizeof(u64) #define WQ_PAGE_ADDR_SIZE_SHIFT 3 #define WQ_PAGE_ADDR(wq, idx) \ ((u8 *)(*(u64 *)((u64)((wq)->shadow_block_vaddr) + \ (WQE_PAGE_NUM(wq, idx) << WQ_PAGE_ADDR_SIZE_SHIFT)))) #define WQ_BLOCK_SIZE 4096UL #define WQS_PAGE_SIZE (WQS_BLOCKS_PER_PAGE * WQ_BLOCK_SIZE) #define WQ_MAX_PAGES (WQ_BLOCK_SIZE >> WQ_PAGE_ADDR_SIZE_SHIFT) #define CMDQ_BLOCKS_PER_PAGE 8 #define CMDQ_BLOCK_SIZE 512UL #define CMDQ_PAGE_SIZE ALIGN((CMDQ_BLOCKS_PER_PAGE * \ CMDQ_BLOCK_SIZE), PAGE_SIZE) #define ADDR_4K_ALIGNED(addr) (0 == ((addr) & 0xfff)) #define ADDR_256K_ALIGNED(addr) (0 == ((addr) & 0x3ffff)) #define WQ_BASE_VADDR(wqs, wq) \ ((u64 *)(((u64)((wqs)->page_vaddr[(wq)->page_idx])) \ + (wq)->block_idx * WQ_BLOCK_SIZE)) #define WQ_BASE_PADDR(wqs, wq) (((wqs)->page_paddr[(wq)->page_idx]) \ + (u64)(wq)->block_idx * WQ_BLOCK_SIZE) #define WQ_BASE_ADDR(wqs, wq) \ ((u64 *)(((u64)((wqs)->shadow_page_vaddr[(wq)->page_idx])) \ + (wq)->block_idx * WQ_BLOCK_SIZE)) #define CMDQ_BASE_VADDR(cmdq_pages, wq) \ ((u64 *)(((u64)((cmdq_pages)->cmdq_page_vaddr)) \ + (wq)->block_idx * CMDQ_BLOCK_SIZE)) #define CMDQ_BASE_PADDR(cmdq_pages, wq) \ (((u64)((cmdq_pages)->cmdq_page_paddr)) \ + (u64)(wq)->block_idx * CMDQ_BLOCK_SIZE) #define CMDQ_BASE_ADDR(cmdq_pages, wq) \ ((u64 *)(((u64)((cmdq_pages)->cmdq_shadow_page_vaddr)) \ + (wq)->block_idx * CMDQ_BLOCK_SIZE)) #define MASKED_WQE_IDX(wq, idx) ((idx) & (wq)->mask) #define WQE_SHADOW_PAGE(wq, wqe) \ ((u16)(((ulong)(wqe) - (ulong)(wq)->shadow_wqe) \ / (wq)->max_wqe_size)) #define WQE_IN_RANGE(wqe, start, end) \ (((ulong)(wqe) >= (ulong)(start)) && \ ((ulong)(wqe) < (ulong)(end))) #define WQ_NUM_PAGES(num_wqs) \ (ALIGN((u32)num_wqs, WQS_BLOCKS_PER_PAGE) / WQS_BLOCKS_PER_PAGE) /* Qe buffer relates define */ enum hinic_rx_buf_size { HINIC_RX_BUF_SIZE_32B = 0x20, HINIC_RX_BUF_SIZE_64B = 0x40, HINIC_RX_BUF_SIZE_96B = 0x60, HINIC_RX_BUF_SIZE_128B = 0x80, HINIC_RX_BUF_SIZE_192B = 0xC0, HINIC_RX_BUF_SIZE_256B = 0x100, HINIC_RX_BUF_SIZE_384B = 0x180, HINIC_RX_BUF_SIZE_512B = 0x200, HINIC_RX_BUF_SIZE_768B = 0x300, HINIC_RX_BUF_SIZE_1K = 0x400, HINIC_RX_BUF_SIZE_1_5K = 0x600, HINIC_RX_BUF_SIZE_2K = 0x800, HINIC_RX_BUF_SIZE_3K = 0xC00, HINIC_RX_BUF_SIZE_4K = 0x1000, HINIC_RX_BUF_SIZE_8K = 0x2000, HINIC_RX_BUF_SIZE_16K = 0x4000, }; enum ppf_tmr_status { HINIC_PPF_TMR_FLAG_STOP, HINIC_PPF_TMR_FLAG_START, }; enum hinic_res_state { HINIC_RES_CLEAN = 0, HINIC_RES_ACTIVE = 1, }; #define DEFAULT_RX_BUF_SIZE ((u16)0xB) #define BUF_DESC_SIZE_SHIFT 4 #define HINIC_SQ_WQE_SIZE(num_sge) \ (sizeof(struct hinic_sq_ctrl) + \ sizeof(struct hinic_sq_task) + \ (u32)((num_sge) << BUF_DESC_SIZE_SHIFT)) #define HINIC_SQ_WQEBB_CNT(num_sge) \ ((int)(ALIGN(HINIC_SQ_WQE_SIZE((u32)num_sge), \ HINIC_SQ_WQEBB_SIZE) >> HINIC_SQ_WQEBB_SHIFT)) #define HINIC_GET_RX_VLAN_OFFLOAD_EN(offload_type) \ RQ_CQE_OFFOLAD_TYPE_GET(offload_type, VLAN_EN) #define HINIC_GET_RSS_TYPES(offload_type) \ RQ_CQE_OFFOLAD_TYPE_GET(offload_type, RSS_TYPE) #define HINIC_GET_PKT_TYPES(offload_type) \ RQ_CQE_OFFOLAD_TYPE_GET(offload_type, PKT_TYPE) #define HINIC_GET_RX_PKT_TYPE(offload_type) \ RQ_CQE_OFFOLAD_TYPE_GET(offload_type, PKT_TYPE) #define HINIC_GET_RX_PKT_UMBCAST(offload_type) \ RQ_CQE_OFFOLAD_TYPE_GET(offload_type, PKT_UMBCAST) #define HINIC_GET_RX_VLAN_TAG(vlan_len) \ RQ_CQE_SGE_GET(vlan_len, VLAN) #define HINIC_GET_RX_PKT_LEN(vlan_len) \ RQ_CQE_SGE_GET(vlan_len, LEN) #define HINIC_GET_RX_CSUM_ERR(status) \ RQ_CQE_STATUS_GET(status, CSUM_ERR) #define HINIC_GET_RX_DONE(status) \ RQ_CQE_STATUS_GET(status, RXDONE) #define HINIC_GET_RX_FLUSH(status) \ RQ_CQE_STATUS_GET(status, FLUSH) #define HINIC_GET_RX_BP_EN(status) \ RQ_CQE_STATUS_GET(status, BP_EN) #define HINIC_GET_RX_NUM_LRO(status) \ RQ_CQE_STATUS_GET(status, NUM_LRO) #define HINIC_PKT_TYPES_UNKNOWN(pkt_types) \ ((pkt_types) & RQ_CQE_PKT_TYPES_NON_L2_MASK) #define HINIC_PKT_TYPES_L2(pkt_types) \ ((pkt_types) & RQ_CQE_PKT_TYPES_L2_MASK) #define HINIC_CSUM_ERR_BYPASSED(csum_err) \ ((csum_err) == RQ_CQE_STATUS_CSUM_BYPASS_VAL) #define HINIC_CSUM_ERR_IP(csum_err) \ ((csum_err) & RQ_CQE_STATUS_CSUM_ERR_IP_MASK) #define HINIC_CSUM_ERR_L4(csum_err) \ ((csum_err) & RQ_CQE_STATUS_CSUM_ERR_L4_MASK) #define TX_MSS_DEFAULT 0x3E00 #define TX_MSS_MIN 0x50 enum sq_wqe_type { SQ_NORMAL_WQE = 0, }; enum rq_completion_fmt { RQ_COMPLETE_SGE = 1 }; #endif /* __HINIC_QE_DEF_H__ */ hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_qp.c000066400000000000000000000141421502027332500210670ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include #include #include #include #include #include "hinic_nic_io.h" #include "hinic_qp.h" #define BUF_DESC_SHIFT 1 #define BUF_DESC_SIZE(nr_descs) (((u32)nr_descs) << BUF_DESC_SHIFT) void hinic_prepare_sq_ctrl(struct hinic_sq_ctrl *ctrl, u32 queue_info, int nr_descs, u8 owner) { u32 ctrl_size, task_size, bufdesc_size; ctrl_size = SIZE_8BYTES(sizeof(struct hinic_sq_ctrl)); task_size = SIZE_8BYTES(sizeof(struct hinic_sq_task)); bufdesc_size = BUF_DESC_SIZE(nr_descs); ctrl->ctrl_fmt = SQ_CTRL_SET(bufdesc_size, BUFDESC_SECT_LEN) | SQ_CTRL_SET(task_size, TASKSECT_LEN) | SQ_CTRL_SET(SQ_NORMAL_WQE, DATA_FORMAT) | SQ_CTRL_SET(ctrl_size, LEN) | SQ_CTRL_SET(owner, OWNER); ctrl->ctrl_fmt = be32_to_cpu(ctrl->ctrl_fmt); ctrl->queue_info = queue_info; ctrl->queue_info |= SQ_CTRL_QUEUE_INFO_SET(1U, UC); if (!SQ_CTRL_QUEUE_INFO_GET(ctrl->queue_info, MSS)) { ctrl->queue_info |= SQ_CTRL_QUEUE_INFO_SET(TX_MSS_DEFAULT, MSS); } else if (SQ_CTRL_QUEUE_INFO_GET(ctrl->queue_info, MSS) < TX_MSS_MIN) { /* mss should not less than 80 */ ctrl->queue_info = SQ_CTRL_QUEUE_INFO_CLEAR(ctrl->queue_info, MSS); ctrl->queue_info |= SQ_CTRL_QUEUE_INFO_SET(TX_MSS_MIN, MSS); } ctrl->queue_info = be32_to_cpu(ctrl->queue_info); } int hinic_get_rx_done(struct hinic_rq_cqe *cqe) { u32 status; int rx_done; status = be32_to_cpu(cqe->status); rx_done = RQ_CQE_STATUS_GET(status, RXDONE); if (!rx_done) return 0; return 1; } void hinic_clear_rx_done(struct hinic_rq_cqe *cqe, u32 status_old) { u32 status; status = RQ_CQE_STATUS_CLEAR(status_old, RXDONE); cqe->status = cpu_to_be32(status); /* Make sure Rxdone has been set */ wmb(); } int hinic_get_super_cqe_en(struct hinic_rq_cqe *cqe) { u32 pkt_info; int super_cqe_en; pkt_info = be32_to_cpu(cqe->pkt_info); super_cqe_en = RQ_CQE_SUPER_CQE_EN_GET(pkt_info, SUPER_CQE_EN); if (!super_cqe_en) return 0; return 1; } u32 hinic_get_pkt_len(struct hinic_rq_cqe *cqe) { u32 vlan_len = be32_to_cpu(cqe->vlan_len); return RQ_CQE_SGE_GET(vlan_len, LEN); } u32 hinic_get_pkt_num(struct hinic_rq_cqe *cqe) { u32 pkt_num = be32_to_cpu(cqe->pkt_info); return RQ_CQE_PKT_NUM_GET(pkt_num, NUM); } u32 hinic_get_pkt_len_for_super_cqe(struct hinic_rq_cqe *cqe, bool last) { u32 pkt_len = be32_to_cpu(cqe->pkt_info); if (!last) return RQ_CQE_PKT_LEN_GET(pkt_len, FIRST_LEN); else return RQ_CQE_PKT_LEN_GET(pkt_len, LAST_LEN); } void hinic_prepare_rq_wqe(void *wqe, u16 pi, dma_addr_t buf_addr, dma_addr_t cqe_dma) { struct hinic_rq_wqe *rq_wqe = (struct hinic_rq_wqe *)wqe; struct hinic_rq_ctrl *ctrl = &rq_wqe->ctrl; struct hinic_rq_cqe_sect *cqe_sect = &rq_wqe->cqe_sect; struct hinic_rq_bufdesc *buf_desc = &rq_wqe->buf_desc; u32 rq_ceq_len = sizeof(struct hinic_rq_cqe); ctrl->ctrl_fmt = RQ_CTRL_SET(SIZE_8BYTES(sizeof(*ctrl)), LEN) | RQ_CTRL_SET(SIZE_8BYTES(sizeof(*cqe_sect)), COMPLETE_LEN) | RQ_CTRL_SET(SIZE_8BYTES(sizeof(*buf_desc)), BUFDESC_SECT_LEN) | RQ_CTRL_SET(RQ_COMPLETE_SGE, COMPLETE_FORMAT); hinic_set_sge(&cqe_sect->sge, cqe_dma, rq_ceq_len); buf_desc->addr_high = upper_32_bits(buf_addr); buf_desc->addr_low = lower_32_bits(buf_addr); } void hinic_set_cs_inner_l4(struct hinic_sq_task *task, u32 *queue_info, enum sq_l4offload_type l4_offload, u32 l4_len, u32 offset) { u32 tcp_udp_cs = 0, sctp = 0; u32 mss = TX_MSS_DEFAULT; /* tcp_udp_cs should be setted to calculate outter checksum when vxlan * packets without inner l3 and l4 */ if (unlikely(l4_offload == SCTP_OFFLOAD_ENABLE)) sctp = 1; else tcp_udp_cs = 1; task->pkt_info0 |= SQ_TASK_INFO0_SET(l4_offload, L4OFFLOAD); task->pkt_info1 |= SQ_TASK_INFO1_SET(l4_len, INNER_L4LEN); *queue_info |= SQ_CTRL_QUEUE_INFO_SET(offset, PLDOFF) | SQ_CTRL_QUEUE_INFO_SET(tcp_udp_cs, TCPUDP_CS) | SQ_CTRL_QUEUE_INFO_SET(sctp, SCTP); *queue_info = SQ_CTRL_QUEUE_INFO_CLEAR(*queue_info, MSS); *queue_info |= SQ_CTRL_QUEUE_INFO_SET(mss, MSS); } void hinic_set_tso_inner_l4(struct hinic_sq_task *task, u32 *queue_info, enum sq_l4offload_type l4_offload, u32 l4_len, u32 offset, u32 ip_ident, u32 mss) { u32 tso = 0, ufo = 0; if (l4_offload == TCP_OFFLOAD_ENABLE) tso = 1; else if (l4_offload == UDP_OFFLOAD_ENABLE) ufo = 1; task->ufo_v6_identify = be32_to_cpu(ip_ident); /* just keep the same code style here */ task->pkt_info0 |= SQ_TASK_INFO0_SET(l4_offload, L4OFFLOAD); task->pkt_info0 |= SQ_TASK_INFO0_SET(tso || ufo, TSO_UFO); task->pkt_info1 |= SQ_TASK_INFO1_SET(l4_len, INNER_L4LEN); *queue_info |= SQ_CTRL_QUEUE_INFO_SET(offset, PLDOFF) | SQ_CTRL_QUEUE_INFO_SET(tso, TSO) | SQ_CTRL_QUEUE_INFO_SET(ufo, UFO) | SQ_CTRL_QUEUE_INFO_SET(!!l4_offload, TCPUDP_CS); /* cs must be calculate by hw if tso is enable */ *queue_info = SQ_CTRL_QUEUE_INFO_CLEAR(*queue_info, MSS); /* qsf was initialized in prepare_sq_wqe */ *queue_info |= SQ_CTRL_QUEUE_INFO_SET(mss, MSS); } void hinic_set_vlan_tx_offload(struct hinic_sq_task *task, u32 *queue_info, u16 vlan_tag, u16 vlan_pri) { task->pkt_info0 |= SQ_TASK_INFO0_SET(vlan_tag, VLAN_TAG) | SQ_TASK_INFO0_SET(1U, VLAN_OFFLOAD); *queue_info |= SQ_CTRL_QUEUE_INFO_SET(vlan_pri, PRI); } void hinic_task_set_tx_offload_valid(struct hinic_sq_task *task, u32 l2hdr_len) { task->pkt_info0 |= SQ_TASK_INFO0_SET(l2hdr_len, L2HDR_LEN); } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_qp.h000066400000000000000000000067571502027332500211110ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_QP_H #define HINIC_QP_H #include "hinic_qe_def.h" #include "hinic_port_cmd.h" /* frags and linner */ #define HINIC_MAX_SQ_BUFDESCS (MAX_SKB_FRAGS + 1) #define HINIC_MAX_SQ_SGE 17 #define HINIC_MAX_SKB_NR_FRAGE (HINIC_MAX_SQ_SGE - 1) #define HINIC_GSO_MAX_SIZE 65536 struct hinic_sq_ctrl { u32 ctrl_fmt; u32 queue_info; }; struct hinic_sq_task { u32 pkt_info0; u32 pkt_info1; u32 pkt_info2; u32 ufo_v6_identify; u32 pkt_info4; u32 rsvd5; }; struct hinic_sq_bufdesc { u32 hi_addr; u32 lo_addr; u32 len; u32 rsvd; }; struct hinic_sq_wqe { struct hinic_sq_ctrl ctrl; struct hinic_sq_task task; struct hinic_sq_bufdesc buf_descs[HINIC_MAX_SQ_BUFDESCS]; }; struct hinic_rq_ctrl { u32 ctrl_fmt; }; struct hinic_rq_cqe { u32 status; u32 vlan_len; u32 offload_type; u32 hash_val; u32 rsvd4; u32 rsvd5; u32 rsvd6; u32 pkt_info; }; struct hinic_rq_cqe_sect { struct hinic_sge sge; u32 rsvd; }; struct hinic_rq_bufdesc { u32 addr_high; u32 addr_low; }; struct hinic_rq_wqe { struct hinic_rq_ctrl ctrl; u32 rsvd; struct hinic_rq_cqe_sect cqe_sect; struct hinic_rq_bufdesc buf_desc; }; void hinic_prepare_sq_ctrl(struct hinic_sq_ctrl *ctrl, u32 queue_info, int nr_descs, u8 owner); u32 hinic_get_pkt_len(struct hinic_rq_cqe *cqe); int hinic_get_super_cqe_en(struct hinic_rq_cqe *cqe); u32 hinic_get_pkt_len_for_super_cqe(struct hinic_rq_cqe *cqe, bool last); u32 hinic_get_pkt_num(struct hinic_rq_cqe *cqe); int hinic_get_rx_done(struct hinic_rq_cqe *cqe); void hinic_clear_rx_done(struct hinic_rq_cqe *cqe, u32 status_old); void hinic_prepare_rq_wqe(void *wqe, u16 pi, dma_addr_t buf_addr, dma_addr_t cqe_dma); static inline void hinic_task_set_outter_l3(struct hinic_sq_task *task, enum sq_l3_type l3_type, u32 network_len) { task->pkt_info2 |= SQ_TASK_INFO2_SET(l3_type, OUTER_L3TYPE) | SQ_TASK_INFO2_SET(network_len, OUTER_L3LEN); } static inline void hinic_task_set_tunnel_l4(struct hinic_sq_task *task, enum sq_tunnel_l4_type l4_type, u32 tunnel_len) { task->pkt_info2 |= SQ_TASK_INFO2_SET(l4_type, TUNNEL_L4TYPE) | SQ_TASK_INFO2_SET(tunnel_len, TUNNEL_L4LEN); } static inline void hinic_task_set_inner_l3(struct hinic_sq_task *task, enum sq_l3_type l3_type, u32 network_len) { task->pkt_info0 |= SQ_TASK_INFO0_SET(l3_type, INNER_L3TYPE); task->pkt_info1 |= SQ_TASK_INFO1_SET(network_len, INNER_L3LEN); } void hinic_set_cs_inner_l4(struct hinic_sq_task *task, u32 *queue_info, enum sq_l4offload_type l4_offload, u32 l4_len, u32 offset); void hinic_set_tso_inner_l4(struct hinic_sq_task *task, u32 *queue_info, enum sq_l4offload_type l4_offload, u32 l4_len, u32 offset, u32 ip_ident, u32 mss); void hinic_set_vlan_tx_offload(struct hinic_sq_task *task, u32 *queue_info, u16 vlan_tag, u16 vlan_pri); void hinic_task_set_tx_offload_valid(struct hinic_sq_task *task, u32 l2hdr_len); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_rx.c000066400000000000000000000661521502027332500211100ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_nic_io.h" #include "hinic_nic_cfg.h" #include "hinic_nic_dev.h" #include "hinic_qp.h" #include "hinic_rx.h" static void hinic_clear_rss_config_user(struct hinic_nic_dev *nic_dev); #define HINIC_RX_HDR_SIZE 256 #define HINIC_RX_IPV6_PKT 7 #define HINIC_RX_VXLAN_PKT 0xb #define RXQ_STATS_INC(rxq, field) \ { \ u64_stats_update_begin(&(rxq)->rxq_stats.syncp); \ (rxq)->rxq_stats.field++; \ u64_stats_update_end(&(rxq)->rxq_stats.syncp); \ } static bool rx_alloc_mapped_page(struct hinic_rxq *rxq, struct hinic_rx_info *rx_info) { struct net_device *netdev = rxq->netdev; struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct pci_dev *pdev = nic_dev->pdev; struct page *page = rx_info->page; dma_addr_t dma = rx_info->buf_dma_addr; if (likely(dma)) return true; /* alloc new page for storage */ page = dev_alloc_pages(nic_dev->page_order); if (unlikely(!page)) { RXQ_STATS_INC(rxq, alloc_rx_buf_err); return false; } /* map page for use */ dma = dma_map_page(&pdev->dev, page, 0, rxq->dma_rx_buff_size, DMA_FROM_DEVICE); /* if mapping failed free memory back to system since * there isn't much point in holding memory we can't use */ if (unlikely(dma_mapping_error(&pdev->dev, dma))) { RXQ_STATS_INC(rxq, map_rx_buf_err); __free_pages(page, nic_dev->page_order); return false; } rx_info->page = page; rx_info->buf_dma_addr = dma; rx_info->page_offset = 0; return true; } static int hinic_rx_fill_wqe(struct hinic_rxq *rxq) { struct net_device *netdev = rxq->netdev; struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_rq_wqe *rq_wqe; struct hinic_rx_info *rx_info; dma_addr_t dma_addr = 0; u16 pi = 0; int rq_wqe_len; int i; for (i = 0; i < rxq->q_depth; i++) { rx_info = &rxq->rx_info[i]; rq_wqe = hinic_get_rq_wqe(nic_dev->hwdev, rxq->q_id, &pi); if (!rq_wqe) { nicif_err(nic_dev, drv, netdev, "Failed to get rq wqe, rxq id: %d, wqe id: %d\n", rxq->q_id, i); break; } hinic_prepare_rq_wqe(rq_wqe, pi, dma_addr, rx_info->cqe_dma); rq_wqe_len = sizeof(struct hinic_rq_wqe); hinic_cpu_to_be32(rq_wqe, rq_wqe_len); rx_info->rq_wqe = rq_wqe; } hinic_return_rq_wqe(nic_dev->hwdev, rxq->q_id, rxq->q_depth); return i; } static int hinic_rx_fill_buffers(struct hinic_rxq *rxq) { struct net_device *netdev = rxq->netdev; struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_rq_wqe *rq_wqe; struct hinic_rx_info *rx_info; dma_addr_t dma_addr; int i; int free_wqebbs = rxq->delta - 1; for (i = 0; i < free_wqebbs; i++) { rx_info = &rxq->rx_info[rxq->next_to_update]; if (unlikely(!rx_alloc_mapped_page(rxq, rx_info))) break; dma_addr = rx_info->buf_dma_addr + rx_info->page_offset; rq_wqe = rx_info->rq_wqe; rq_wqe->buf_desc.addr_high = cpu_to_be32(upper_32_bits(dma_addr)); rq_wqe->buf_desc.addr_low = cpu_to_be32(lower_32_bits(dma_addr)); rxq->next_to_update = (rxq->next_to_update + 1) & rxq->q_mask; } if (likely(i)) { /* Write all the wqes before pi update */ wmb(); hinic_update_rq_hw_pi(nic_dev->hwdev, rxq->q_id, rxq->next_to_update); rxq->delta -= i; rxq->next_to_alloc = rxq->next_to_update; } else if (free_wqebbs == rxq->q_depth - 1) { RXQ_STATS_INC(rxq, rx_buf_empty); } return i; } void hinic_rx_free_buffers(struct hinic_rxq *rxq) { u16 i; struct hinic_nic_dev *nic_dev = netdev_priv(rxq->netdev); struct hinic_rx_info *rx_info; /* Free all the Rx ring sk_buffs */ for (i = 0; i < rxq->q_depth; i++) { rx_info = &rxq->rx_info[i]; if (rx_info->buf_dma_addr) { dma_unmap_page(rxq->dev, rx_info->buf_dma_addr, rxq->dma_rx_buff_size, DMA_FROM_DEVICE); rx_info->buf_dma_addr = 0; } if (rx_info->page) { __free_pages(rx_info->page, nic_dev->page_order); rx_info->page = NULL; } } } static void hinic_reuse_rx_page(struct hinic_rxq *rxq, struct hinic_rx_info *old_rx_info) { struct hinic_rx_info *new_rx_info; u16 nta = rxq->next_to_alloc; new_rx_info = &rxq->rx_info[nta]; /* update, and store next to alloc */ nta++; rxq->next_to_alloc = (nta < rxq->q_depth) ? nta : 0; new_rx_info->page = old_rx_info->page; new_rx_info->page_offset = old_rx_info->page_offset; new_rx_info->buf_dma_addr = old_rx_info->buf_dma_addr; /* sync the buffer for use by the device */ dma_sync_single_range_for_device(rxq->dev, new_rx_info->buf_dma_addr, new_rx_info->page_offset, rxq->buf_len, DMA_FROM_DEVICE); } static bool hinic_add_rx_frag(struct hinic_rxq *rxq, struct hinic_rx_info *rx_info, struct sk_buff *skb, u32 size) { struct page *page; u8 *va; page = rx_info->page; va = (u8 *)page_address(page) + rx_info->page_offset; prefetch(va); #if L1_CACHE_BYTES < 128 prefetch(va + L1_CACHE_BYTES); #endif dma_sync_single_range_for_cpu(rxq->dev, rx_info->buf_dma_addr, rx_info->page_offset, rxq->buf_len, DMA_FROM_DEVICE); if (size <= HINIC_RX_HDR_SIZE && !skb_is_nonlinear(skb)) { memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); /*lint !e666*/ /* page is not reserved, we can reuse buffer as-is */ if (likely(page_to_nid(page) == numa_node_id())) return true; /* this page cannot be reused so discard it */ put_page(page); return false; } skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, (int)rx_info->page_offset, (int)size, rxq->buf_len); /* avoid re-using remote pages */ if (unlikely(page_to_nid(page) != numa_node_id())) return false; /* if we are only owner of page we can reuse it */ if (unlikely(page_count(page) != 1)) return false; /* flip page offset to other buffer */ rx_info->page_offset ^= rxq->buf_len; page_ref_inc(page); return true; } static void __packaging_skb(struct hinic_rxq *rxq, struct sk_buff *head_skb, u8 sge_num, u32 pkt_len) { struct hinic_rx_info *rx_info; struct sk_buff *skb; u8 frag_num = 0; u32 size; u16 sw_ci; sw_ci = ((u32)rxq->cons_idx) & rxq->q_mask; skb = head_skb; while (sge_num) { rx_info = &rxq->rx_info[sw_ci]; sw_ci = (sw_ci + 1) & rxq->q_mask; if (unlikely(pkt_len > rxq->buf_len)) { size = rxq->buf_len; pkt_len -= rxq->buf_len; } else { size = pkt_len; } if (unlikely(frag_num == MAX_SKB_FRAGS)) { frag_num = 0; if (skb == head_skb) skb = skb_shinfo(skb)->frag_list; else skb = skb->next; } if (unlikely(skb != head_skb)) { head_skb->len += size; head_skb->data_len += size; head_skb->truesize += rxq->buf_len; } if (likely(hinic_add_rx_frag(rxq, rx_info, skb, size))) { hinic_reuse_rx_page(rxq, rx_info); } else { /* we are not reusing the buffer so unmap it */ dma_unmap_page(rxq->dev, rx_info->buf_dma_addr, rxq->dma_rx_buff_size, DMA_FROM_DEVICE); } /* clear contents of buffer_info */ rx_info->buf_dma_addr = 0; rx_info->page = NULL; sge_num--; frag_num++; } } static struct sk_buff *hinic_fetch_rx_buffer(struct hinic_rxq *rxq, u32 pkt_len) { struct sk_buff *head_skb, *cur_skb, *skb = NULL; struct net_device *netdev = rxq->netdev; u8 sge_num, skb_num; u16 wqebb_cnt = 0; head_skb = netdev_alloc_skb_ip_align(netdev, HINIC_RX_HDR_SIZE); if (unlikely(!head_skb)) return NULL; sge_num = (u8)(pkt_len >> rxq->rx_buff_shift) + ((pkt_len & (rxq->buf_len - 1)) ? 1 : 0); if (likely(sge_num <= MAX_SKB_FRAGS)) skb_num = 1; else skb_num = (sge_num / MAX_SKB_FRAGS) + ((sge_num % MAX_SKB_FRAGS) ? 1 : 0); while (unlikely(skb_num > 1)) { cur_skb = netdev_alloc_skb_ip_align(netdev, HINIC_RX_HDR_SIZE); if (unlikely(!cur_skb)) goto alloc_skb_fail; if (!skb) { skb_shinfo(head_skb)->frag_list = cur_skb; skb = cur_skb; } else { skb->next = cur_skb; skb = cur_skb; } skb_num--; } prefetchw(head_skb->data); wqebb_cnt = sge_num; __packaging_skb(rxq, head_skb, sge_num, pkt_len); rxq->cons_idx += wqebb_cnt; rxq->delta += wqebb_cnt; return head_skb; alloc_skb_fail: dev_kfree_skb_any(head_skb); return NULL; } void hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats) { struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; unsigned int start; u64_stats_update_begin(&stats->syncp); do { start = u64_stats_fetch_begin(&rxq_stats->syncp); stats->bytes = rxq_stats->bytes; stats->packets = rxq_stats->packets; stats->errors = rxq_stats->csum_errors + rxq_stats->other_errors; stats->csum_errors = rxq_stats->csum_errors; stats->other_errors = rxq_stats->other_errors; stats->dropped = rxq_stats->dropped; stats->rx_buf_empty = rxq_stats->rx_buf_empty; } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); u64_stats_update_end(&stats->syncp); } void hinic_rxq_clean_stats(struct hinic_rxq_stats *rxq_stats) { u64_stats_update_begin(&rxq_stats->syncp); rxq_stats->bytes = 0; rxq_stats->packets = 0; rxq_stats->errors = 0; rxq_stats->csum_errors = 0; rxq_stats->other_errors = 0; rxq_stats->dropped = 0; rxq_stats->alloc_skb_err = 0; rxq_stats->alloc_rx_buf_err = 0; rxq_stats->map_rx_buf_err = 0; rxq_stats->rx_buf_empty = 0; u64_stats_update_end(&rxq_stats->syncp); } static void rxq_stats_init(struct hinic_rxq *rxq) { struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; u64_stats_init(&rxq_stats->syncp); hinic_rxq_clean_stats(rxq_stats); } static void hinic_pull_tail(struct sk_buff *skb) { skb_frag_t *frag = &skb_shinfo(skb)->frags[0]; unsigned char *va; /* it is valid to use page_address instead of kmap since we are * working with pages allocated out of the lomem pool per * alloc_page(GFP_ATOMIC) */ va = skb_frag_address(frag); /* align pull length to size of long to optimize memcpy performance */ skb_copy_to_linear_data(skb, va, HINIC_RX_HDR_SIZE); /* update all of the pointers */ skb_frag_size_sub(frag, HINIC_RX_HDR_SIZE); frag->bv_offset += HINIC_RX_HDR_SIZE; skb->data_len -= HINIC_RX_HDR_SIZE; skb->tail += HINIC_RX_HDR_SIZE; } static void hinic_rx_csum(struct hinic_rxq *rxq, u32 status, struct sk_buff *skb) { struct net_device *netdev = rxq->netdev; u32 csum_err; csum_err = HINIC_GET_RX_CSUM_ERR(status); if (unlikely(csum_err == HINIC_RX_CSUM_IPSU_OTHER_ERR)) rxq->rxq_stats.other_errors++; if (!(netdev->features & NETIF_F_RXCSUM)) return; if (!csum_err) { skb->ip_summed = CHECKSUM_UNNECESSARY; } else { /* pkt type is recognized by HW, and csum is err */ if (!(csum_err & (HINIC_RX_CSUM_HW_CHECK_NONE | HINIC_RX_CSUM_IPSU_OTHER_ERR))) rxq->rxq_stats.csum_errors++; skb->ip_summed = CHECKSUM_NONE; } } static void hinic_rx_gro(struct hinic_rxq *rxq, u32 offload_type, struct sk_buff *skb) { struct net_device *netdev = rxq->netdev; bool l2_tunnel; if (!(netdev->features & NETIF_F_GRO)) return; l2_tunnel = HINIC_GET_RX_PKT_TYPE(offload_type) == HINIC_RX_VXLAN_PKT ? 1 : 0; if (l2_tunnel && skb->ip_summed == CHECKSUM_UNNECESSARY) /* If we checked the outer header let the stack know */ skb->csum_level = 1; } static void hinic_copy_lp_data(struct hinic_nic_dev *nic_dev, struct sk_buff *skb) { struct net_device *netdev = nic_dev->netdev; u8 *lb_buf = nic_dev->lb_test_rx_buf; void *frag_data; int lb_len = nic_dev->lb_pkt_len; int pkt_offset, frag_len, i; if (nic_dev->lb_test_rx_idx == LP_PKT_CNT) { nic_dev->lb_test_rx_idx = 0; nicif_warn(nic_dev, rx_err, netdev, "Loopback test warning, recive too more test pkt\n"); } if (skb->len != nic_dev->lb_pkt_len) { nicif_warn(nic_dev, rx_err, netdev, "Wrong packet length\n"); nic_dev->lb_test_rx_idx++; return; } pkt_offset = nic_dev->lb_test_rx_idx * lb_len; frag_len = (int)skb_headlen(skb); memcpy((lb_buf + pkt_offset), skb->data, frag_len); pkt_offset += frag_len; for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { frag_data = skb_frag_address(&skb_shinfo(skb)->frags[i]); frag_len = (int)skb_frag_size(&skb_shinfo(skb)->frags[i]); memcpy((lb_buf + pkt_offset), frag_data, frag_len); pkt_offset += frag_len; } nic_dev->lb_test_rx_idx++; } int recv_one_pkt(struct hinic_rxq *rxq, struct hinic_rq_cqe *rx_cqe, u32 pkt_len, u32 vlan_len, u32 status) { struct sk_buff *skb; struct net_device *netdev = rxq->netdev; struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u32 offload_type; skb = hinic_fetch_rx_buffer(rxq, pkt_len); if (unlikely(!skb)) { RXQ_STATS_INC(rxq, alloc_skb_err); return -ENOMEM; } /* place header in linear portion of buffer */ if (skb_is_nonlinear(skb)) hinic_pull_tail(skb); hinic_rx_csum(rxq, status, skb); offload_type = be32_to_cpu(rx_cqe->offload_type); hinic_rx_gro(rxq, offload_type, skb); if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && HINIC_GET_RX_VLAN_OFFLOAD_EN(offload_type)) { u16 vid = HINIC_GET_RX_VLAN_TAG(vlan_len); /* if the packet is a vlan pkt, the vid may be 0 */ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); } if (unlikely(test_bit(HINIC_LP_TEST, &nic_dev->flags))) hinic_copy_lp_data(nic_dev, skb); skb_record_rx_queue(skb, rxq->q_id); skb->protocol = eth_type_trans(skb, netdev); if (skb_has_frag_list(skb)) { napi_gro_flush(&rxq->irq_cfg->napi, false); netif_receive_skb(skb); } else { napi_gro_receive(&rxq->irq_cfg->napi, skb); } return 0; } void rx_pass_super_cqe(struct hinic_rxq *rxq, u32 index, u32 pkt_num, struct hinic_rq_cqe *cqe) { u8 sge_num = 0; u32 pkt_len; while (index < pkt_num) { pkt_len = hinic_get_pkt_len_for_super_cqe (cqe, index == (pkt_num - 1)); sge_num += (u8)(pkt_len >> rxq->rx_buff_shift) + ((pkt_len & (rxq->buf_len - 1)) ? 1 : 0); index++; } rxq->cons_idx += sge_num; rxq->delta += sge_num; } static inline int __recv_supper_cqe(struct hinic_rxq *rxq, struct hinic_rq_cqe *rx_cqe, u32 pkt_info, u32 vlan_len, u32 status, int *pkts, u64 *rx_bytes, u32 *dropped) { u32 pkt_len; int i, pkt_num = 0; pkt_num = HINIC_GET_RQ_CQE_PKT_NUM(pkt_info); i = 0; while (i < pkt_num) { pkt_len = ((i == (pkt_num - 1)) ? RQ_CQE_PKT_LEN_GET(pkt_info, LAST_LEN) : RQ_CQE_PKT_LEN_GET(pkt_info, FIRST_LEN)); if (unlikely(recv_one_pkt(rxq, rx_cqe, pkt_len, vlan_len, status))) { if (i) { rx_pass_super_cqe(rxq, i, pkt_num, rx_cqe); *dropped += (pkt_num - i); } break; } *rx_bytes += pkt_len; (*pkts)++; i++; } if (!i) return -EFAULT; return 0; } #define LRO_PKT_HDR_LEN_IPV4 66 #define LRO_PKT_HDR_LEN_IPV6 86 #define LRO_PKT_HDR_LEN(cqe) \ (HINIC_GET_RX_PKT_TYPE(be32_to_cpu((cqe)->offload_type)) == \ HINIC_RX_IPV6_PKT ? LRO_PKT_HDR_LEN_IPV6 : LRO_PKT_HDR_LEN_IPV4) int hinic_rx_poll(struct hinic_rxq *rxq, int budget) { struct hinic_nic_dev *nic_dev = netdev_priv(rxq->netdev); u32 status, pkt_len, vlan_len, pkt_info, dropped = 0; struct hinic_rq_cqe *rx_cqe; u64 rx_bytes = 0; u16 sw_ci, num_lro; int pkts = 0, nr_pkts = 0; u16 num_wqe = 0; while (likely(pkts < budget)) { sw_ci = ((u32)rxq->cons_idx) & rxq->q_mask; rx_cqe = rxq->rx_info[sw_ci].cqe; status = be32_to_cpu(rx_cqe->status); if (!HINIC_GET_RX_DONE(status)) break; /* make sure we read rx_done before packet length */ rmb(); vlan_len = be32_to_cpu(rx_cqe->vlan_len); pkt_info = be32_to_cpu(rx_cqe->pkt_info); pkt_len = HINIC_GET_RX_PKT_LEN(vlan_len); if (unlikely(HINIC_GET_SUPER_CQE_EN(pkt_info))) { if (unlikely(__recv_supper_cqe(rxq, rx_cqe, pkt_info, vlan_len, status, &pkts, &rx_bytes, &dropped))) break; nr_pkts += (int)HINIC_GET_RQ_CQE_PKT_NUM(pkt_info); } else { if (recv_one_pkt(rxq, rx_cqe, pkt_len, vlan_len, status)) break; rx_bytes += pkt_len; pkts++; nr_pkts++; num_lro = HINIC_GET_RX_NUM_LRO(status); if (num_lro) { rx_bytes += ((num_lro - 1) * LRO_PKT_HDR_LEN(rx_cqe)); num_wqe += (u16)(pkt_len >> rxq->rx_buff_shift) + ((pkt_len & (rxq->buf_len - 1)) ? 1 : 0); } } rx_cqe->status = 0; if (num_wqe >= nic_dev->lro_replenish_thld) break; } if (rxq->delta >= HINIC_RX_BUFFER_WRITE) hinic_rx_fill_buffers(rxq); u64_stats_update_begin(&rxq->rxq_stats.syncp); rxq->rxq_stats.packets += nr_pkts; rxq->rxq_stats.bytes += rx_bytes; rxq->rxq_stats.dropped += dropped; u64_stats_update_end(&rxq->rxq_stats.syncp); return pkts; } static int rx_alloc_cqe(struct hinic_rxq *rxq) { struct hinic_nic_dev *nic_dev = netdev_priv(rxq->netdev); struct pci_dev *pdev = nic_dev->pdev; struct hinic_rx_info *rx_info; struct hinic_rq_cqe *cqe_va; dma_addr_t cqe_pa; u32 cqe_mem_size; int idx; cqe_mem_size = sizeof(*rx_info->cqe) * rxq->q_depth; rxq->cqe_start_vaddr = dma_alloc_coherent(&pdev->dev, cqe_mem_size, &rxq->cqe_start_paddr, GFP_KERNEL); if (!rxq->cqe_start_vaddr) { nicif_err(nic_dev, drv, rxq->netdev, "Failed to allocate cqe dma\n"); return -ENOMEM; } cqe_va = (struct hinic_rq_cqe *)rxq->cqe_start_vaddr; cqe_pa = rxq->cqe_start_paddr; for (idx = 0; idx < rxq->q_depth; idx++) { rx_info = &rxq->rx_info[idx]; rx_info->cqe = cqe_va; rx_info->cqe_dma = cqe_pa; cqe_va++; cqe_pa += sizeof(*rx_info->cqe); } hinic_rq_cqe_addr_set(nic_dev->hwdev, rxq->q_id, rxq->cqe_start_paddr); return 0; } static void rx_free_cqe(struct hinic_rxq *rxq) { struct hinic_nic_dev *nic_dev = netdev_priv(rxq->netdev); struct pci_dev *pdev = nic_dev->pdev; u32 cqe_mem_size; cqe_mem_size = sizeof(struct hinic_rq_cqe) * rxq->q_depth; dma_free_coherent(&pdev->dev, cqe_mem_size, rxq->cqe_start_vaddr, rxq->cqe_start_paddr); } static int hinic_setup_rx_resources(struct hinic_rxq *rxq, struct net_device *netdev, struct irq_info *entry) { struct hinic_nic_dev *nic_dev = netdev_priv(rxq->netdev); u64 rx_info_sz; int err, pkts; rxq->irq_id = entry->irq_id; rxq->msix_entry_idx = entry->msix_entry_idx; rxq->next_to_alloc = 0; rxq->next_to_update = 0; rxq->delta = rxq->q_depth; rxq->q_mask = rxq->q_depth - 1; rxq->cons_idx = 0; rx_info_sz = rxq->q_depth * sizeof(*rxq->rx_info); if (!rx_info_sz) { nicif_err(nic_dev, drv, netdev, "Cannot allocate zero size rx info\n"); return -EINVAL; } rxq->rx_info = kzalloc(rx_info_sz, GFP_KERNEL); if (!rxq->rx_info) return -ENOMEM; err = rx_alloc_cqe(rxq); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to allocate Rx cqe\n"); goto rx_cqe_err; } pkts = hinic_rx_fill_wqe(rxq); if (pkts != rxq->q_depth) { nicif_err(nic_dev, drv, netdev, "Failed to fill rx wqe\n"); err = -ENOMEM; goto rx_pkts_err; } pkts = hinic_rx_fill_buffers(rxq); if (!pkts) { nicif_err(nic_dev, drv, netdev, "Failed to allocate Rx buffer\n"); err = -ENOMEM; goto rx_pkts_err; } return 0; rx_pkts_err: rx_free_cqe(rxq); rx_cqe_err: kfree(rxq->rx_info); return err; } static void hinic_free_rx_resources(struct hinic_rxq *rxq) { hinic_rx_free_buffers(rxq); rx_free_cqe(rxq); kfree(rxq->rx_info); } int hinic_setup_all_rx_resources(struct net_device *netdev, struct irq_info *msix_entries) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 i, q_id; int err; for (q_id = 0; q_id < nic_dev->num_qps; q_id++) { err = hinic_setup_rx_resources(&nic_dev->rxqs[q_id], nic_dev->netdev, &msix_entries[q_id]); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to set up rxq resource\n"); goto init_rxq_err; } } return 0; init_rxq_err: for (i = 0; i < q_id; i++) hinic_free_rx_resources(&nic_dev->rxqs[i]); return err; } void hinic_free_all_rx_resources(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 q_id; for (q_id = 0; q_id < nic_dev->num_qps; q_id++) hinic_free_rx_resources(&nic_dev->rxqs[q_id]); } int hinic_alloc_rxqs(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct pci_dev *pdev = nic_dev->pdev; struct hinic_rxq *rxq; u16 num_rxqs = nic_dev->max_qps; u16 q_id; u64 rxq_size; rxq_size = num_rxqs * sizeof(*nic_dev->rxqs); if (!rxq_size) { nic_err(&pdev->dev, "Cannot allocate zero size rxqs\n"); return -EINVAL; } nic_dev->rxqs = kzalloc(rxq_size, GFP_KERNEL); if (!nic_dev->rxqs) return -ENOMEM; for (q_id = 0; q_id < num_rxqs; q_id++) { rxq = &nic_dev->rxqs[q_id]; rxq->netdev = netdev; rxq->dev = &pdev->dev; rxq->q_id = q_id; rxq->buf_len = nic_dev->rx_buff_len; rxq->rx_buff_shift = ilog2(nic_dev->rx_buff_len); rxq->dma_rx_buff_size = RX_BUFF_NUM_PER_PAGE * nic_dev->rx_buff_len; rxq->q_depth = nic_dev->rq_depth; rxq->q_mask = nic_dev->rq_depth - 1; rxq_stats_init(rxq); } return 0; } void hinic_free_rxqs(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); hinic_clear_rss_config_user(nic_dev); kfree(nic_dev->rxqs); } void hinic_init_rss_parameters(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); nic_dev->rss_hash_engine = HINIC_RSS_HASH_ENGINE_TYPE_XOR; nic_dev->rss_type.tcp_ipv6_ext = 1; nic_dev->rss_type.ipv6_ext = 1; nic_dev->rss_type.tcp_ipv6 = 1; nic_dev->rss_type.ipv6 = 1; nic_dev->rss_type.tcp_ipv4 = 1; nic_dev->rss_type.ipv4 = 1; nic_dev->rss_type.udp_ipv6 = 1; nic_dev->rss_type.udp_ipv4 = 1; } void hinic_set_default_rss_indir(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (!nic_dev->rss_indir_user) return; nicif_info(nic_dev, drv, netdev, "Discard user configured Rx flow hash indirection\n"); kfree(nic_dev->rss_indir_user); nic_dev->rss_indir_user = NULL; } static void hinic_maybe_reconfig_rss_indir(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int i; if (!nic_dev->rss_indir_user) return; if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) goto discard_user_rss_indir; for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) { if (nic_dev->rss_indir_user[i] >= nic_dev->num_qps) goto discard_user_rss_indir; } return; discard_user_rss_indir: hinic_set_default_rss_indir(netdev); } static void hinic_clear_rss_config_user(struct hinic_nic_dev *nic_dev) { kfree(nic_dev->rss_hkey_user); nic_dev->rss_hkey_user_be = NULL; nic_dev->rss_hkey_user = NULL; kfree(nic_dev->rss_indir_user); nic_dev->rss_indir_user = NULL; } static void hinic_fillout_indir_tbl(struct hinic_nic_dev *nic_dev, u8 num_tcs, u32 *indir) { u16 num_rss, tc_group_size; int i; if (num_tcs) tc_group_size = HINIC_RSS_INDIR_SIZE / num_tcs; else tc_group_size = HINIC_RSS_INDIR_SIZE; num_rss = nic_dev->num_rss; for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) indir[i] = (i / tc_group_size) * num_rss + i % num_rss; } static void hinic_rss_deinit(struct hinic_nic_dev *nic_dev) { u8 prio_tc[HINIC_DCB_UP_MAX] = {0}; hinic_rss_cfg(nic_dev->hwdev, 0, nic_dev->rss_tmpl_idx, 0, prio_tc); } int hinic_set_hw_rss_parameters(struct net_device *netdev, u8 rss_en, u8 num_tc, u8 *prio_tc) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u8 tmpl_idx = 0xFF; u8 default_rss_key[HINIC_RSS_KEY_SIZE] = { 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa}; u32 *indir_tbl; u8 *hkey; int err; tmpl_idx = nic_dev->rss_tmpl_idx; /* RSS key */ if (nic_dev->rss_hkey_user) hkey = nic_dev->rss_hkey_user; else hkey = default_rss_key; err = hinic_rss_set_template_tbl(nic_dev->hwdev, tmpl_idx, hkey); if (err) return err; hinic_maybe_reconfig_rss_indir(netdev); indir_tbl = kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE, GFP_KERNEL); if (!indir_tbl) { nicif_err(nic_dev, drv, netdev, "Failed to allocate set hw rss indir_tbl\n"); return -ENOMEM; } if (nic_dev->rss_indir_user) memcpy(indir_tbl, nic_dev->rss_indir_user, sizeof(u32) * HINIC_RSS_INDIR_SIZE); else hinic_fillout_indir_tbl(nic_dev, num_tc, indir_tbl); err = hinic_rss_set_indir_tbl(nic_dev->hwdev, tmpl_idx, indir_tbl); if (err) goto out; err = hinic_set_rss_type(nic_dev->hwdev, tmpl_idx, nic_dev->rss_type); if (err) goto out; err = hinic_rss_set_hash_engine(nic_dev->hwdev, tmpl_idx, nic_dev->rss_hash_engine); if (err) goto out; err = hinic_rss_cfg(nic_dev->hwdev, rss_en, tmpl_idx, num_tc, prio_tc); if (err) goto out; kfree(indir_tbl); return 0; out: kfree(indir_tbl); return err; } static int hinic_rss_init(struct hinic_nic_dev *nic_dev) { struct net_device *netdev = nic_dev->netdev; u32 *indir_tbl; u8 cos, num_tc = 0; u8 prio_tc[HINIC_DCB_UP_MAX] = {0}; int err; if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) { num_tc = nic_dev->max_cos; for (cos = 0; cos < HINIC_DCB_COS_MAX; cos++) { if (cos < HINIC_DCB_COS_MAX - nic_dev->max_cos) prio_tc[cos] = nic_dev->max_cos - 1; else prio_tc[cos] = (HINIC_DCB_COS_MAX - 1) - cos; } } else { num_tc = 0; } indir_tbl = kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE, GFP_KERNEL); if (!indir_tbl) return -ENOMEM; if (nic_dev->rss_indir_user) memcpy(indir_tbl, nic_dev->rss_indir_user, sizeof(u32) * HINIC_RSS_INDIR_SIZE); else hinic_fillout_indir_tbl(nic_dev, num_tc, indir_tbl); err = hinic_set_hw_rss_parameters(netdev, 1, num_tc, prio_tc); if (err) { kfree(indir_tbl); return err; } kfree(indir_tbl); return 0; } int hinic_update_hw_tc_map(struct net_device *netdev, u8 num_tc, u8 *prio_tc) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u8 tmpl_idx = nic_dev->rss_tmpl_idx; /* RSS must be enable when dcb is enabled */ return hinic_rss_cfg(nic_dev->hwdev, 1, tmpl_idx, num_tc, prio_tc); } int hinic_rx_configure(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int err; if (test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) { err = hinic_rss_init(nic_dev); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to init rss\n"); return -EFAULT; } } err = hinic_dcb_set_rq_iq_mapping(nic_dev->hwdev, hinic_func_max_qnum(nic_dev->hwdev), NULL); if (err) { nicif_err(nic_dev, drv, netdev, "Failed to set rq_iq mapping\n"); goto set_rq_cos_mapping_err; } return 0; set_rq_cos_mapping_err: if (test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) hinic_rss_deinit(nic_dev); return err; } void hinic_rx_remove_configure(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); if (test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) hinic_rss_deinit(nic_dev); } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_rx.h000066400000000000000000000057231502027332500211120ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_RX_H #define HINIC_RX_H /* rx cqe checksum err */ #define HINIC_RX_CSUM_IP_CSUM_ERR BIT(0) #define HINIC_RX_CSUM_TCP_CSUM_ERR BIT(1) #define HINIC_RX_CSUM_UDP_CSUM_ERR BIT(2) #define HINIC_RX_CSUM_IGMP_CSUM_ERR BIT(3) #define HINIC_RX_CSUM_ICMPV4_CSUM_ERR BIT(4) #define HINIC_RX_CSUM_ICMPV6_CSUM_ERR BIT(5) #define HINIC_RX_CSUM_SCTP_CRC_ERR BIT(6) #define HINIC_RX_CSUM_HW_CHECK_NONE BIT(7) #define HINIC_RX_CSUM_IPSU_OTHER_ERR BIT(8) #define HINIC_SUPPORT_LRO_ADAP_QPS_MAX 16 #define HINIC_RX_BUFFER_WRITE 16 struct hinic_rxq_stats { u64 packets; u64 bytes; u64 errors; u64 csum_errors; u64 other_errors; u64 dropped; u64 rx_buf_empty; u64 alloc_skb_err; u64 alloc_rx_buf_err; u64 map_rx_buf_err; struct u64_stats_sync syncp; }; struct hinic_rx_info { dma_addr_t buf_dma_addr; struct hinic_rq_cqe *cqe; dma_addr_t cqe_dma; struct page *page; u32 page_offset; struct hinic_rq_wqe *rq_wqe; }; struct hinic_rxq { struct net_device *netdev; u16 q_id; u16 q_depth; u16 q_mask; u16 buf_len; u32 rx_buff_shift; u32 dma_rx_buff_size; struct hinic_rxq_stats rxq_stats; u16 cons_idx; u16 delta; u32 irq_id; u16 msix_entry_idx; struct hinic_rx_info *rx_info; struct hinic_irq *irq_cfg; u16 next_to_alloc; u16 next_to_update; struct device *dev; /* device for DMA mapping */ unsigned long status; dma_addr_t cqe_start_paddr; void *cqe_start_vaddr; u64 last_moder_packets; u64 last_moder_bytes; u8 last_coalesc_timer_cfg; u8 last_pending_limt; }; void hinic_rxq_clean_stats(struct hinic_rxq_stats *rxq_stats); void hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats); int hinic_alloc_rxqs(struct net_device *netdev); void hinic_free_rxqs(struct net_device *netdev); void hinic_init_rss_parameters(struct net_device *netdev); void hinic_set_default_rss_indir(struct net_device *netdev); int hinic_setup_all_rx_resources(struct net_device *netdev, struct irq_info *msix_entries); void hinic_free_all_rx_resources(struct net_device *netdev); void hinic_rx_remove_configure(struct net_device *netdev); int hinic_rx_configure(struct net_device *netdev); int hinic_set_hw_rss_parameters(struct net_device *netdev, u8 rss_en, u8 num_tc, u8 *prio_tc); int hinic_update_hw_tc_map(struct net_device *netdev, u8 num_tc, u8 *prio_tc); int hinic_rx_poll(struct hinic_rxq *rxq, int budget); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_sm_lt.h000066400000000000000000000130721502027332500215730ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef __CHIPIF_SM_LT_H__ #define __CHIPIF_SM_LT_H__ #define SM_LT_LOAD 0x12 #define SM_LT_STORE 0x14 #define SM_LT_NUM_OFFSET 13 #define SM_LT_ABUF_FLG_OFFSET 12 #define SM_LT_BC_OFFSET 11 #define SM_LT_ENTRY_16B 16 #define SM_LT_ENTRY_32B 32 #define SM_LT_ENTRY_48B 48 #define SM_LT_ENTRY_64B 64 #define TBL_LT_OFFSET_DEFAULT 0 #define SM_CACHE_LINE_SHFT 4 /* log2(16) */ #define SM_CACHE_LINE_SIZE 16 /* the size of cache line */ #define MAX_SM_LT_READ_LINE_NUM 4 #define MAX_SM_LT_WRITE_LINE_NUM 3 #define SM_LT_FULL_BYTEENB 0xFFFF #define TBL_GET_ENB3_MASK(bitmask) ((u16)(((bitmask) >> 32) & 0xFFFF)) #define TBL_GET_ENB2_MASK(bitmask) ((u16)(((bitmask) >> 16) & 0xFFFF)) #define TBL_GET_ENB1_MASK(bitmask) ((u16)((bitmask) & 0xFFFF)) enum { SM_LT_NUM_0 = 0, /* lt num = 0, load/store 16B */ SM_LT_NUM_1, /* lt num = 1, load/store 32B */ SM_LT_NUM_2, /* lt num = 2, load/store 48B */ SM_LT_NUM_3 /* lt num = 3, load 64B */ }; /* lt load request */ union sml_lt_req_head { struct { u32 offset : 8; u32 pad : 3; u32 bc : 1; u32 abuf_flg : 1; u32 num : 2; u32 ack : 1; u32 op_id : 5; u32 instance : 6; u32 src : 5; } bs; u32 value; }; struct sml_lt_load_req { u32 extra; union sml_lt_req_head head; u32 index; u32 pad0; u32 pad1; }; struct sml_lt_store_req { u32 extra; union sml_lt_req_head head; u32 index; u32 byte_enb[2]; u8 write_data[48]; }; enum { SM_LT_OFFSET_1 = 1, SM_LT_OFFSET_2, SM_LT_OFFSET_3, SM_LT_OFFSET_4, SM_LT_OFFSET_5, SM_LT_OFFSET_6, SM_LT_OFFSET_7, SM_LT_OFFSET_8, SM_LT_OFFSET_9, SM_LT_OFFSET_10, SM_LT_OFFSET_11, SM_LT_OFFSET_12, SM_LT_OFFSET_13, SM_LT_OFFSET_14, SM_LT_OFFSET_15 }; static inline void sml_lt_store_memcpy(u32 *dst, u32 *src, u8 num) { switch (num) { case SM_LT_NUM_2: *(dst + SM_LT_OFFSET_11) = *(src + SM_LT_OFFSET_11); *(dst + SM_LT_OFFSET_10) = *(src + SM_LT_OFFSET_10); *(dst + SM_LT_OFFSET_9) = *(src + SM_LT_OFFSET_9); *(dst + SM_LT_OFFSET_8) = *(src + SM_LT_OFFSET_8); fallthrough; case SM_LT_NUM_1: *(dst + SM_LT_OFFSET_7) = *(src + SM_LT_OFFSET_7); *(dst + SM_LT_OFFSET_6) = *(src + SM_LT_OFFSET_6); *(dst + SM_LT_OFFSET_5) = *(src + SM_LT_OFFSET_5); *(dst + SM_LT_OFFSET_4) = *(src + SM_LT_OFFSET_4); fallthrough; case SM_LT_NUM_0: *(dst + SM_LT_OFFSET_3) = *(src + SM_LT_OFFSET_3); *(dst + SM_LT_OFFSET_2) = *(src + SM_LT_OFFSET_2); *(dst + SM_LT_OFFSET_1) = *(src + SM_LT_OFFSET_1); *dst = *src; break; default: break; } } static inline void sml_lt_load_memcpy(u32 *dst, u32 *src, u8 num) { switch (num) { case SM_LT_NUM_3: *(dst + SM_LT_OFFSET_15) = *(src + SM_LT_OFFSET_15); *(dst + SM_LT_OFFSET_14) = *(src + SM_LT_OFFSET_14); *(dst + SM_LT_OFFSET_13) = *(src + SM_LT_OFFSET_13); *(dst + SM_LT_OFFSET_12) = *(src + SM_LT_OFFSET_12); fallthrough; case SM_LT_NUM_2: *(dst + SM_LT_OFFSET_11) = *(src + SM_LT_OFFSET_11); *(dst + SM_LT_OFFSET_10) = *(src + SM_LT_OFFSET_10); *(dst + SM_LT_OFFSET_9) = *(src + SM_LT_OFFSET_9); *(dst + SM_LT_OFFSET_8) = *(src + SM_LT_OFFSET_8); fallthrough; case SM_LT_NUM_1: *(dst + SM_LT_OFFSET_7) = *(src + SM_LT_OFFSET_7); *(dst + SM_LT_OFFSET_6) = *(src + SM_LT_OFFSET_6); *(dst + SM_LT_OFFSET_5) = *(src + SM_LT_OFFSET_5); *(dst + SM_LT_OFFSET_4) = *(src + SM_LT_OFFSET_4); fallthrough; case SM_LT_NUM_0: *(dst + SM_LT_OFFSET_3) = *(src + SM_LT_OFFSET_3); *(dst + SM_LT_OFFSET_2) = *(src + SM_LT_OFFSET_2); *(dst + SM_LT_OFFSET_1) = *(src + SM_LT_OFFSET_1); *dst = *src; break; default: break; } } enum HINIC_CSR_API_DATA_OPERATION_ID { HINIC_CSR_OPERATION_WRITE_CSR = 0x1E, HINIC_CSR_OPERATION_READ_CSR = 0x1F }; enum HINIC_CSR_API_DATA_NEED_RESPONSE_DATA { HINIC_CSR_NO_RESP_DATA = 0, HINIC_CSR_NEED_RESP_DATA = 1 }; enum HINIC_CSR_API_DATA_DATA_SIZE { HINIC_CSR_DATA_SZ_32 = 0, HINIC_CSR_DATA_SZ_64 = 1 }; struct hinic_csr_request_api_data { u32 dw0; union { struct { u32 reserved1 : 13; /* this field indicates the write/read data size: * 2'b00: 32 bits * 2'b01: 64 bits * 2'b10~2'b11:reserved */ u32 data_size : 2; /* this field indicates that requestor expect receive a * response data or not. * 1'b0: expect not to receive a response data. * 1'b1: expect to receive a response data. */ u32 need_response : 1; /* this field indicates the operation that the requestor * expected. * 5'b1_1110: write value to csr space. * 5'b1_1111: read register from csr space. */ u32 operation_id : 5; u32 reserved2 : 6; /* this field specifies the Src node ID for this API * request message. */ u32 src_node_id : 5; } bits; u32 val32; } dw1; union { struct { /* it specifies the CSR address. */ u32 csr_addr : 26; u32 reserved3 : 6; } bits; u32 val32; } dw2; /* if data_size=2'b01, it is high 32 bits of write data. else, it is * 32'hFFFF_FFFF. */ u32 csr_write_data_h; /* the low 32 bits of write data. */ u32 csr_write_data_l; }; #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_sml_counter.c000066400000000000000000000204151502027332500230010ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hwdev.h" #include "hinic_sml_counter.h" static void sml_ctr_htonl_n(u32 *node, u32 len) { u32 i; for (i = 0; i < len; i++) { *node = cpu_to_be32(*node); node++; } } static void hinic_sml_ctr_read_build_req(struct chipif_sml_ctr_rd_req *msg, u8 instance_id, u8 op_id, u8 ack, u32 ctr_id, u32 init_val) { msg->head.value = 0; msg->head.bs.instance = instance_id; msg->head.bs.op_id = op_id; msg->head.bs.ack = ack; msg->head.value = cpu_to_be32(msg->head.value); msg->ctr_id = ctr_id; msg->ctr_id = cpu_to_be32(msg->ctr_id); msg->initial = init_val; } static void hinic_sml_ctr_write_build_req(struct chipif_sml_ctr_wr_req *msg, u8 instance_id, u8 op_id, u8 ack, u32 ctr_id, u64 val1, u64 val2) { msg->head.value = 0; msg->head.bs.instance = instance_id; msg->head.bs.op_id = op_id; msg->head.bs.ack = ack; msg->head.value = cpu_to_be32(msg->head.value); msg->ctr_id = ctr_id; msg->ctr_id = cpu_to_be32(msg->ctr_id); msg->value1_h = val1 >> 32; msg->value1_l = val1 & 0xFFFFFFFF; msg->value2_h = val2 >> 32; msg->value2_l = val2 & 0xFFFFFFFF; } /** * hinic_sm_ctr_rd32 - small single 32 counter read * @hwdev: the pointer to hw device * @node: the node id * @instance: instance value * @ctr_id: counter id * @value: read counter value ptr * Return: 0 - success, negative - failure */ int hinic_sm_ctr_rd32(void *hwdev, u8 node, u8 instance, u32 ctr_id, u32 *value) { struct chipif_sml_ctr_rd_req req; union ctr_rd_rsp rsp; int ret; if (!hwdev || !value) return -EFAULT; hinic_sml_ctr_read_build_req(&req, instance, CHIPIF_SM_CTR_OP_READ, CHIPIF_ACK, ctr_id, 0); ret = hinic_api_cmd_read_ack(hwdev, node, (u8 *)&req, (unsigned short)sizeof(req), (void *)&rsp, (unsigned short)sizeof(rsp)); if (ret) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Sm 32bit counter read fail, err(%d)\n", ret); return ret; } sml_ctr_htonl_n((u32 *)&rsp, 4); *value = rsp.bs_ss32_rsp.value1; return 0; } EXPORT_SYMBOL(hinic_sm_ctr_rd32); /** * hinic_sm_ctr_rd32_clear - small single 32 counter read and clear to zero * @hwdev: the pointer to hw device * @node: the node id * @instance: instance value * @ctr_id: counter id * @value: read counter value ptr * Return: 0 - success, negative - failure * according to ACN error code (ERR_OK, ERR_PARAM, ERR_FAILED...etc) */ int hinic_sm_ctr_rd32_clear(void *hwdev, u8 node, u8 instance, u32 ctr_id, u32 *value) { struct chipif_sml_ctr_rd_req req; union ctr_rd_rsp rsp; int ret; if (!hwdev || !value) return -EFAULT; hinic_sml_ctr_read_build_req(&req, instance, CHIPIF_SM_CTR_OP_READ_CLEAR, CHIPIF_ACK, ctr_id, 0); ret = hinic_api_cmd_read_ack(hwdev, node, (u8 *)&req, (unsigned short)sizeof(req), (void *)&rsp, (unsigned short)sizeof(rsp)); if (ret) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Sm 32bit counter clear fail, err(%d)\n", ret); return ret; } sml_ctr_htonl_n((u32 *)&rsp, 4); *value = rsp.bs_ss32_rsp.value1; return 0; } EXPORT_SYMBOL(hinic_sm_ctr_rd32_clear); /** * hinic_sm_ctr_wr32 - small single 32 counter write * @hwdev: the pointer to hw device * @node: the node id * @instance: instance value * @ctr_id: counter id * @value: write counter value * Return: 0 - success, negative - failure */ int hinic_sm_ctr_wr32(void *hwdev, u8 node, u8 instance, u32 ctr_id, u32 value) { struct chipif_sml_ctr_wr_req req; struct chipif_sml_ctr_wr_rsp rsp; if (!hwdev) return -EFAULT; hinic_sml_ctr_write_build_req(&req, instance, CHIPIF_SM_CTR_OP_WRITE, CHIPIF_NOACK, ctr_id, (u64)value, 0ULL); return hinic_api_cmd_read_ack(hwdev, node, (u8 *)&req, (unsigned short)sizeof(req), (void *)&rsp, (unsigned short)sizeof(rsp)); } /** * hinic_sm_ctr_rd64 - big counter 64 read * @hwdev: the pointer to hw device * @node: the node id * @instance: instance value * @ctr_id: counter id * @value: read counter value ptr * Return: 0 - success, negative - failure */ int hinic_sm_ctr_rd64(void *hwdev, u8 node, u8 instance, u32 ctr_id, u64 *value) { struct chipif_sml_ctr_rd_req req; union ctr_rd_rsp rsp; int ret; if (!hwdev || !value) return -EFAULT; hinic_sml_ctr_read_build_req(&req, instance, CHIPIF_SM_CTR_OP_READ, CHIPIF_ACK, ctr_id, 0); ret = hinic_api_cmd_read_ack(hwdev, node, (u8 *)&req, (unsigned short)sizeof(req), (void *)&rsp, (unsigned short)sizeof(rsp)); if (ret) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Sm 64bit counter read fail err(%d)\n", ret); return ret; } sml_ctr_htonl_n((u32 *)&rsp, 4); *value = ((u64)rsp.bs_bs64_rsp.value1 << 32) | rsp.bs_bs64_rsp.value2; return 0; } /** * hinic_sm_ctr_wr64 - big single 64 counter write * @hwdev: the pointer to hw device * @node: the node id * @instance: instance value * @ctr_id: counter id * @value: write counter value * Return: 0 - success, negative - failure */ int hinic_sm_ctr_wr64(void *hwdev, u8 node, u8 instance, u32 ctr_id, u64 value) { struct chipif_sml_ctr_wr_req req; struct chipif_sml_ctr_wr_rsp rsp; if (!hwdev) return -EFAULT; hinic_sml_ctr_write_build_req(&req, instance, CHIPIF_SM_CTR_OP_WRITE, CHIPIF_NOACK, ctr_id, value, 0ULL); return hinic_api_cmd_read_ack(hwdev, node, (u8 *)&req, (unsigned short)sizeof(req), (void *)&rsp, (unsigned short)sizeof(rsp)); } /** * hinic_sm_ctr_rd64_pair - big pair 128 counter read * @hwdev: the pointer to hw device * @node: the node id * @instance: instance value * @ctr_id: counter id * @value1: read counter value ptr * @value2: read counter value ptr * Return: 0 - success, negative - failure */ int hinic_sm_ctr_rd64_pair(void *hwdev, u8 node, u8 instance, u32 ctr_id, u64 *value1, u64 *value2) { struct chipif_sml_ctr_rd_req req; union ctr_rd_rsp rsp; int ret; if (!value1) { pr_err("value1 is NULL for read 64 bit pair\n"); return -EFAULT; } if (!value2) { pr_err("value2 is NULL for read 64 bit pair\n"); return -EFAULT; } if (!hwdev || (0 != (ctr_id & 0x1))) { pr_err("Hwdev is NULL or ctr_id(%d) is odd number for read 64 bit pair\n", ctr_id); return -EFAULT; } hinic_sml_ctr_read_build_req(&req, instance, CHIPIF_SM_CTR_OP_READ, CHIPIF_ACK, ctr_id, 0); ret = hinic_api_cmd_read_ack(hwdev, node, (u8 *)&req, (unsigned short)sizeof(req), (void *)&rsp, (unsigned short)sizeof(rsp)); if (ret) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Sm 64 bit rd pair ret(%d)\n", ret); return ret; } sml_ctr_htonl_n((u32 *)&rsp, 4); *value1 = ((u64)rsp.bs_bp64_rsp.val1_h << 32) | rsp.bs_bp64_rsp.val1_l; *value2 = ((u64)rsp.bs_bp64_rsp.val2_h << 32) | rsp.bs_bp64_rsp.val2_l; return 0; } /** * hinic_sm_ctr_wr64_pair - big pair 128 counter write * @hwdev: the pointer to hw device * @node: the node id * @instance: instance value * @ctr_id: counter id * @value1: write counter value * @value2: write counter value * Return: 0 - success, negative - failure */ int hinic_sm_ctr_wr64_pair(void *hwdev, u8 node, u8 instance, u32 ctr_id, u64 value1, u64 value2) { struct chipif_sml_ctr_wr_req req; struct chipif_sml_ctr_wr_rsp rsp; /* pair pattern ctr_id must be even number */ if (!hwdev || (0 != (ctr_id & 0x1))) { pr_err("Handle is NULL or ctr_id(%d) is odd number for write 64 bit pair\n", ctr_id); return -EFAULT; } hinic_sml_ctr_write_build_req(&req, instance, CHIPIF_SM_CTR_OP_WRITE, CHIPIF_NOACK, ctr_id, value1, value2); return hinic_api_cmd_read_ack(hwdev, node, (u8 *)&req, (unsigned short)sizeof(req), (void *)&rsp, (unsigned short)sizeof(rsp)); } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_sml_counter.h000066400000000000000000000042231502027332500230050ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef __CHIPIF_SML_COUNTER_H__ #define __CHIPIF_SML_COUNTER_H__ #define CHIPIF_FUNC_PF 0 #define CHIPIF_FUNC_VF 1 #define CHIPIF_FUNC_PPF 2 #define CHIPIF_ACK 1 #define CHIPIF_NOACK 0 #define CHIPIF_SM_CTR_OP_READ 0x2 #define CHIPIF_SM_CTR_OP_READ_CLEAR 0x6 #define CHIPIF_SM_CTR_OP_WRITE 0x3 #define SMALL_CNT_READ_RSP_SIZE 16 /* request head */ union chipif_sml_ctr_req_head { struct { u32 pad : 15; u32 ack : 1; u32 op_id : 5; u32 instance : 6; u32 src : 5; } bs; u32 value; }; /* counter read request struct */ struct chipif_sml_ctr_rd_req { u32 extra; union chipif_sml_ctr_req_head head; u32 ctr_id; u32 initial; u32 pad; }; /* counter read response union */ union ctr_rd_rsp { struct { u32 value1 : 16; u32 pad0 : 16; u32 pad1[3]; } bs_ss16_rsp; struct { u32 value1; u32 pad[3]; } bs_ss32_rsp; struct { u32 value1 : 20; u32 pad0 : 12; u32 value2 : 12; u32 pad1 : 20; u32 pad2[2]; } bs_sp_rsp; struct { u32 value1; u32 value2; u32 pad[2]; } bs_bs64_rsp; struct { u32 val1_h; u32 val1_l; u32 val2_h; u32 val2_l; } bs_bp64_rsp; }; /* resopnse head */ union sml_ctr_rsp_head { struct { u32 pad : 30; /* reserve */ u32 code : 2; /* error code */ } bs; u32 value; }; /* counter write request struct */ struct chipif_sml_ctr_wr_req { u32 extra; union chipif_sml_ctr_req_head head; u32 ctr_id; u32 rsv1; u32 rsv2; u32 value1_h; u32 value1_l; u32 value2_h; u32 value2_l; }; /*counter write response struct */ struct chipif_sml_ctr_wr_rsp { union sml_ctr_rsp_head head; u32 pad[3]; }; #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_sml_lt.c000066400000000000000000000170631502027332500217460ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_sm_lt.h" #include "hinic_hw.h" #include "hinic_hwdev.h" #include "hinic_hw_mgmt.h" #include "hinic_hwif.h" #include "hinic_dbg.h" #define ACK 1 #define NOACK 0 #define LT_LOAD16_API_SIZE (16 + 4) #define LT_STORE16_API_SIZE (32 + 4) #define HINIC_API_RD_8B 8 #define HINIC_API_RD_4B 4 static inline void sm_lt_build_head(union sml_lt_req_head *head, u8 instance_id, u8 op_id, u8 ack, u8 offset, u8 num) { head->value = 0; head->bs.instance = instance_id; head->bs.op_id = op_id; head->bs.ack = ack; head->bs.num = num; head->bs.abuf_flg = 0; head->bs.bc = 1; head->bs.offset = offset; head->value = cpu_to_be32(head->value); } static inline void sm_lt_load_build_req(struct sml_lt_load_req *req, u8 instance_id, u8 op_id, u8 ack, u32 lt_index, u8 offset, u8 num) { sm_lt_build_head(&req->head, instance_id, op_id, ack, offset, num); req->extra = 0; req->index = lt_index; req->index = cpu_to_be32(req->index); } static inline void sm_lt_store_build_req(struct sml_lt_store_req *req, u8 instance_id, u8 op_id, u8 ack, u32 lt_index, u8 offset, u8 num, u16 byte_enb3, u16 byte_enb2, u16 byte_enb1, u8 *data) { sm_lt_build_head(&req->head, instance_id, op_id, ack, offset, num); req->index = lt_index; req->index = cpu_to_be32(req->index); req->extra = 0; req->byte_enb[0] = (u32)(byte_enb3); req->byte_enb[0] = cpu_to_be32(req->byte_enb[0]); req->byte_enb[1] = cpu_to_be32((((u32)byte_enb2) << 16) | byte_enb1); sml_lt_store_memcpy((u32 *)req->write_data, (u32 *)(void *)data, num); } int hinic_dbg_lt_rd_16byte(void *hwdev, u8 dest, u8 instance, u32 lt_index, u8 *data) { struct sml_lt_load_req req; int ret; if (!hwdev) return -EFAULT; sm_lt_load_build_req(&req, instance, SM_LT_LOAD, ACK, lt_index, 0, 0); ret = hinic_api_cmd_read_ack(hwdev, dest, &req, LT_LOAD16_API_SIZE, (void *)data, 16); if (ret) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Read linear table 16byte fail, err: %d\n", ret); return -EFAULT; } return 0; } EXPORT_SYMBOL(hinic_dbg_lt_rd_16byte); int hinic_dbg_lt_wr_16byte_mask(void *hwdev, u8 dest, u8 instance, u32 lt_index, u8 *data, u16 mask) { struct sml_lt_store_req req; int ret; if (!hwdev || !data) return -EFAULT; sm_lt_store_build_req(&req, instance, SM_LT_STORE, NOACK, lt_index, 0, 0, 0, 0, mask, data); ret = hinic_api_cmd_write_nack(hwdev, dest, &req, LT_STORE16_API_SIZE); if (ret) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Write linear table 16byte fail, err: %d\n", ret); return -EFAULT; } return 0; } EXPORT_SYMBOL(hinic_dbg_lt_wr_16byte_mask); int hinic_api_csr_rd32(void *hwdev, u8 dest, u32 addr, u32 *val) { struct hinic_hwdev *dev = hwdev; struct hinic_csr_request_api_data api_data = { 0 }; u32 csr_val = 0; u16 in_size = sizeof(api_data); int ret; if (!hwdev || !val) return -EFAULT; if (dest == HINIC_NODE_ID_CPI) { *val = readl(dev->hwif->cfg_regs_base + addr); return 0; } api_data.dw0 = 0; api_data.dw1.bits.operation_id = HINIC_CSR_OPERATION_READ_CSR; api_data.dw1.bits.need_response = HINIC_CSR_NEED_RESP_DATA; api_data.dw1.bits.data_size = HINIC_CSR_DATA_SZ_32; api_data.dw1.val32 = cpu_to_be32(api_data.dw1.val32); api_data.dw2.bits.csr_addr = addr; api_data.dw2.val32 = cpu_to_be32(api_data.dw2.val32); ret = hinic_api_cmd_read_ack(hwdev, dest, (u8 *)(&api_data), in_size, &csr_val, HINIC_API_RD_4B); if (ret) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Read 32 bit csr failed, dest %d addr 0x%x, ret: 0x%x\n", dest, addr, ret); return ret; } *val = csr_val; return 0; } EXPORT_SYMBOL(hinic_api_csr_rd32); int hinic_api_csr_wr32(void *hwdev, u8 dest, u32 addr, u32 val) { struct hinic_hwdev *dev = hwdev; struct hinic_csr_request_api_data api_data = { 0 }; u16 in_size = sizeof(api_data); int ret; if (!hwdev) return -EFAULT; if (dest == HINIC_NODE_ID_CPI) { writel(val, dev->hwif->cfg_regs_base + addr); return 0; } api_data.dw1.bits.operation_id = HINIC_CSR_OPERATION_WRITE_CSR; api_data.dw1.bits.need_response = HINIC_CSR_NO_RESP_DATA; api_data.dw1.bits.data_size = HINIC_CSR_DATA_SZ_32; api_data.dw1.val32 = cpu_to_be32(api_data.dw1.val32); api_data.dw2.bits.csr_addr = addr; api_data.dw2.val32 = cpu_to_be32(api_data.dw2.val32); api_data.csr_write_data_h = 0xffffffff; api_data.csr_write_data_l = val; ret = hinic_api_cmd_write_nack(hwdev, dest, (u8 *)(&api_data), in_size); if (ret) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Write 32 bit csr failed, dest %d addr 0x%x val 0x%x\n", dest, addr, val); return ret; } return 0; } EXPORT_SYMBOL(hinic_api_csr_wr32); int hinic_api_csr_rd64(void *hwdev, u8 dest, u32 addr, u64 *val) { struct hinic_csr_request_api_data api_data = { 0 }; u64 csr_val = 0; u16 in_size = sizeof(api_data); int ret; if (!hwdev || !val) return -EFAULT; if (dest == HINIC_NODE_ID_CPI) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Unsupport to read 64 bit csr from cpi\n"); return -EOPNOTSUPP; } api_data.dw0 = 0; api_data.dw1.bits.operation_id = HINIC_CSR_OPERATION_READ_CSR; api_data.dw1.bits.need_response = HINIC_CSR_NEED_RESP_DATA; api_data.dw1.bits.data_size = HINIC_CSR_DATA_SZ_64; api_data.dw1.val32 = cpu_to_be32(api_data.dw1.val32); api_data.dw2.bits.csr_addr = addr; api_data.dw2.val32 = cpu_to_be32(api_data.dw2.val32); ret = hinic_api_cmd_read_ack(hwdev, dest, (u8 *)(&api_data), in_size, &csr_val, HINIC_API_RD_8B); if (ret) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Read 64 bit csr failed, dest %d addr 0x%x\n", dest, addr); return ret; } *val = csr_val; return 0; } EXPORT_SYMBOL(hinic_api_csr_rd64); int hinic_api_csr_wr64(void *hwdev, u8 dest, u32 addr, u64 val) { struct hinic_csr_request_api_data api_data = { 0 }; u16 in_size = sizeof(api_data); int ret; if (!hwdev || !val) return -EFAULT; if (dest == HINIC_NODE_ID_CPI) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Unsupport to write 64 bit csr from cpi\n"); return -EOPNOTSUPP; } api_data.dw0 = 0; api_data.dw1.bits.operation_id = HINIC_CSR_OPERATION_WRITE_CSR; api_data.dw1.bits.need_response = HINIC_CSR_NO_RESP_DATA; api_data.dw1.bits.data_size = HINIC_CSR_DATA_SZ_64; api_data.dw1.val32 = cpu_to_be32(api_data.dw1.val32); api_data.dw2.bits.csr_addr = addr; api_data.dw2.val32 = cpu_to_be32(api_data.dw2.val32); api_data.csr_write_data_h = cpu_to_be32(upper_32_bits(val)); api_data.csr_write_data_l = cpu_to_be32(lower_32_bits(val)); ret = hinic_api_cmd_write_nack(hwdev, dest, (u8 *)(&api_data), in_size); if (ret) { sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Write 64 bit csr failed, dest %d addr 0x%x val 0x%llx\n", dest, addr, val); return ret; } return 0; } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_sriov.c000066400000000000000000000312311502027332500216070ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_nic_cfg.h" #include "hinic_nic_dev.h" #include "hinic_sriov.h" #include "hinic_lld.h" int hinic_pci_sriov_disable(struct pci_dev *dev) { #ifdef CONFIG_PCI_IOV struct hinic_sriov_info *sriov_info; u16 tmp_vfs; sriov_info = hinic_get_sriov_info_by_pcidev(dev); /* if SR-IOV is already disabled then nothing will be done */ if (!sriov_info->sriov_enabled) return 0; if (test_and_set_bit(HINIC_SRIOV_DISABLE, &sriov_info->state)) { nic_err(&sriov_info->pdev->dev, "SR-IOV disable in process, please wait\n"); return -EPERM; } /* If our VFs are assigned we cannot shut down SR-IOV * without causing issues, so just leave the hardware * available but disabled */ if (pci_vfs_assigned(sriov_info->pdev)) { clear_bit(HINIC_SRIOV_DISABLE, &sriov_info->state); nic_warn(&sriov_info->pdev->dev, "Unloading driver while VFs are assigned - VFs will not be deallocated\n"); return -EPERM; } sriov_info->sriov_enabled = false; /* disable iov and allow time for transactions to clear */ pci_disable_sriov(sriov_info->pdev); tmp_vfs = (u16)sriov_info->num_vfs; sriov_info->num_vfs = 0; hinic_deinit_vf_hw(sriov_info->hwdev, OS_VF_ID_TO_HW(0), OS_VF_ID_TO_HW(tmp_vfs - 1)); clear_bit(HINIC_SRIOV_DISABLE, &sriov_info->state); #endif return 0; } int hinic_pci_sriov_enable(struct pci_dev *dev, int num_vfs) { #ifdef CONFIG_PCI_IOV struct hinic_sriov_info *sriov_info; int err = 0; int pre_existing_vfs = 0; sriov_info = hinic_get_sriov_info_by_pcidev(dev); if (test_and_set_bit(HINIC_SRIOV_ENABLE, &sriov_info->state)) { nic_err(&sriov_info->pdev->dev, "SR-IOV enable in process, please wait, num_vfs %d\n", num_vfs); return -EPERM; } pre_existing_vfs = pci_num_vf(sriov_info->pdev); if (num_vfs > pci_sriov_get_totalvfs(sriov_info->pdev)) { clear_bit(HINIC_SRIOV_ENABLE, &sriov_info->state); return -ERANGE; } if (pre_existing_vfs && pre_existing_vfs != num_vfs) { err = hinic_pci_sriov_disable(sriov_info->pdev); if (err) { clear_bit(HINIC_SRIOV_ENABLE, &sriov_info->state); return err; } } else if (pre_existing_vfs == num_vfs) { clear_bit(HINIC_SRIOV_ENABLE, &sriov_info->state); return num_vfs; } err = hinic_init_vf_hw(sriov_info->hwdev, OS_VF_ID_TO_HW(0), OS_VF_ID_TO_HW((u16)num_vfs - 1)); if (err) { nic_err(&sriov_info->pdev->dev, "Failed to init vf in hardware before enable sriov, error %d\n", err); clear_bit(HINIC_SRIOV_ENABLE, &sriov_info->state); return err; } err = pci_enable_sriov(sriov_info->pdev, num_vfs); if (err) { nic_err(&sriov_info->pdev->dev, "Failed to enable SR-IOV, error %d\n", err); clear_bit(HINIC_SRIOV_ENABLE, &sriov_info->state); return err; } sriov_info->sriov_enabled = true; sriov_info->num_vfs = num_vfs; clear_bit(HINIC_SRIOV_ENABLE, &sriov_info->state); return num_vfs; #else return 0; #endif } static bool hinic_is_support_sriov_configure(struct pci_dev *pdev) { enum hinic_init_state state = hinic_get_init_state(pdev); struct hinic_sriov_info *sriov_info; if (state < HINIC_INIT_STATE_NIC_INITED) { nic_err(&pdev->dev, "NIC device not initialized, don't support to configure sriov\n"); return false; } sriov_info = hinic_get_sriov_info_by_pcidev(pdev); if (FUNC_SRIOV_FIX_NUM_VF(sriov_info->hwdev)) { nic_err(&pdev->dev, "Don't support to changed sriov configuration\n"); return false; } return true; } int hinic_pci_sriov_configure(struct pci_dev *dev, int num_vfs) { struct hinic_sriov_info *sriov_info; if (!hinic_is_support_sriov_configure(dev)) return -EFAULT; sriov_info = hinic_get_sriov_info_by_pcidev(dev); if (test_bit(HINIC_FUNC_REMOVE, &sriov_info->state)) return -EFAULT; if (!num_vfs) return hinic_pci_sriov_disable(dev); else return hinic_pci_sriov_enable(dev, num_vfs); } int hinic_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) { struct hinic_nic_dev *adapter = netdev_priv(netdev); struct hinic_sriov_info *sriov_info; int err; if (!FUNC_SUPPORT_SET_VF_MAC_VLAN(adapter->hwdev)) { nicif_err(adapter, drv, netdev, "Current function don't support to set vf mac\n"); return -EOPNOTSUPP; } sriov_info = hinic_get_sriov_info_by_pcidev(adapter->pdev); if (is_multicast_ether_addr(mac) || /*lint !e574*/ vf >= sriov_info->num_vfs) /*lint !e574*/ return -EINVAL; err = hinic_set_vf_mac(sriov_info->hwdev, OS_VF_ID_TO_HW(vf), mac); if (err) { nicif_info(adapter, drv, netdev, "Failed to set MAC %pM on VF %d\n", mac, vf); return err; } if (is_zero_ether_addr(mac)) nicif_info(adapter, drv, netdev, "Removing MAC on VF %d\n", vf); else nicif_info(adapter, drv, netdev, "Setting MAC %pM on VF %d\n", mac, vf); nicif_info(adapter, drv, netdev, "Reload the VF driver to make this change effective\n"); return 0; } /*lint -save -e574 -e734*/ static int set_hw_vf_vlan(struct hinic_sriov_info *sriov_info, u16 cur_vlanprio, int vf, u16 vlan, u8 qos) { int err = 0; u16 old_vlan = cur_vlanprio & VLAN_VID_MASK; if (vlan || qos) { if (cur_vlanprio) { err = hinic_kill_vf_vlan(sriov_info->hwdev, OS_VF_ID_TO_HW(vf)); if (err) { nic_err(&sriov_info->pdev->dev, "Failed to delete vf %d old vlan %d\n", vf, old_vlan); return err; } } err = hinic_add_vf_vlan(sriov_info->hwdev, OS_VF_ID_TO_HW(vf), vlan, qos); if (err) { nic_err(&sriov_info->pdev->dev, "Failed to add vf %d new vlan %d\n", vf, vlan); return err; } } else { err = hinic_kill_vf_vlan(sriov_info->hwdev, OS_VF_ID_TO_HW(vf)); if (err) { nic_err(&sriov_info->pdev->dev, "Failed to delete vf %d vlan %d\n", vf, old_vlan); return err; } } return hinic_update_mac_vlan(sriov_info->hwdev, old_vlan, vlan, OS_VF_ID_TO_HW(vf)); } int hinic_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, __be16 vlan_proto) { struct hinic_nic_dev *adapter = netdev_priv(netdev); struct hinic_sriov_info *sriov_info; u16 vlanprio, cur_vlanprio; if (!FUNC_SUPPORT_SET_VF_MAC_VLAN(adapter->hwdev)) { nicif_err(adapter, drv, netdev, "Current function don't support to set vf vlan\n"); return -EOPNOTSUPP; } sriov_info = hinic_get_sriov_info_by_pcidev(adapter->pdev); if (vf >= sriov_info->num_vfs || vlan > 4095 || qos > 7) return -EINVAL; if (vlan_proto != htons(ETH_P_8021Q)) return -EPROTONOSUPPORT; vlanprio = vlan | qos << HINIC_VLAN_PRIORITY_SHIFT; cur_vlanprio = hinic_vf_info_vlanprio(sriov_info->hwdev, OS_VF_ID_TO_HW(vf)); /* duplicate request, so just return success */ if (vlanprio == cur_vlanprio) return 0; return set_hw_vf_vlan(sriov_info, cur_vlanprio, vf, vlan, qos); } int hinic_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting) { struct hinic_nic_dev *adapter = netdev_priv(netdev); struct hinic_sriov_info *sriov_info; int err = 0; bool cur_spoofchk; sriov_info = hinic_get_sriov_info_by_pcidev(adapter->pdev); if (vf >= sriov_info->num_vfs) return -EINVAL; cur_spoofchk = hinic_vf_info_spoofchk(sriov_info->hwdev, OS_VF_ID_TO_HW(vf)); /* same request, so just return success */ if ((setting && cur_spoofchk) || (!setting && !cur_spoofchk)) return 0; err = hinic_set_vf_spoofchk(sriov_info->hwdev, OS_VF_ID_TO_HW(vf), setting); if (!err) { nicif_info(adapter, drv, netdev, "Set VF %d spoofchk %s\n", vf, setting ? "on" : "off"); } else if (err == HINIC_MGMT_CMD_UNSUPPORTED) { nicif_err(adapter, drv, netdev, "Current firmware doesn't support to set vf spoofchk, need to upgrade latest firmware version\n"); err = -EOPNOTSUPP; } return err; } int hinic_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting) { struct hinic_nic_dev *adapter = netdev_priv(netdev); struct hinic_sriov_info *sriov_info; int err = 0; bool cur_trust; sriov_info = hinic_get_sriov_info_by_pcidev(adapter->pdev); if (vf >= sriov_info->num_vfs) return -EINVAL; cur_trust = hinic_vf_info_trust(sriov_info->hwdev, OS_VF_ID_TO_HW(vf)); /* same request, so just return success */ if ((setting && cur_trust) || (!setting && !cur_trust)) return 0; err = hinic_set_vf_trust(sriov_info->hwdev, OS_VF_ID_TO_HW(vf), setting); if (!err) nicif_info(adapter, drv, netdev, "Set VF %d trusted %s succeed\n", vf, setting ? "on" : "off"); else nicif_err(adapter, drv, netdev, "Failed set VF %d trusted %s\n", vf, setting ? "on" : "off"); return err; } int hinic_ndo_get_vf_config(struct net_device *netdev, int vf, struct ifla_vf_info *ivi) { struct hinic_nic_dev *adapter = netdev_priv(netdev); struct hinic_sriov_info *sriov_info; sriov_info = hinic_get_sriov_info_by_pcidev(adapter->pdev); if (vf >= sriov_info->num_vfs) return -EINVAL; hinic_get_vf_config(sriov_info->hwdev, OS_VF_ID_TO_HW(vf), ivi); return 0; } /** * hinic_ndo_set_vf_link_state * @netdev: network interface device structure * @vf_id: VF identifier * @link: required link state * Return: 0 - success, negative - failure * Set the link state of a specified VF, regardless of physical link state */ int hinic_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link) { struct hinic_nic_dev *adapter = netdev_priv(netdev); struct hinic_sriov_info *sriov_info; static const char * const vf_link[] = {"auto", "enable", "disable"}; int err; if (FUNC_FORCE_LINK_UP(adapter->hwdev)) { nicif_err(adapter, drv, netdev, "Current function don't support to set vf link state\n"); return -EOPNOTSUPP; } sriov_info = hinic_get_sriov_info_by_pcidev(adapter->pdev); /* validate the request */ if (vf_id >= sriov_info->num_vfs) { nicif_err(adapter, drv, netdev, "Invalid VF Identifier %d\n", vf_id); return -EINVAL; } err = hinic_set_vf_link_state(sriov_info->hwdev, OS_VF_ID_TO_HW(vf_id), link); if (!err) nicif_info(adapter, drv, netdev, "Set VF %d link state: %s\n", vf_id, vf_link[link]); return err; } #define HINIC_TX_RATE_TABLE_FULL 12 int hinic_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate, int max_tx_rate) { struct hinic_nic_dev *adapter = netdev_priv(netdev); struct nic_port_info port_info = {0}; struct hinic_sriov_info *sriov_info; u8 link_status = 0; u32 speeds[] = {SPEED_10, SPEED_100, SPEED_1000, SPEED_10000, SPEED_25000, SPEED_40000, SPEED_100000}; int err = 0; if (!FUNC_SUPPORT_RATE_LIMIT(adapter->hwdev)) { nicif_err(adapter, drv, netdev, "Current function don't support to set vf rate limit\n"); return -EOPNOTSUPP; } sriov_info = hinic_get_sriov_info_by_pcidev(adapter->pdev); /* verify VF is active */ if (vf >= sriov_info->num_vfs) { nicif_err(adapter, drv, netdev, "VF number must be less than %d\n", sriov_info->num_vfs); return -EINVAL; } if (max_tx_rate < min_tx_rate) { nicif_err(adapter, drv, netdev, "Invalid rate, max rate %d must greater than min rate %d\n", max_tx_rate, min_tx_rate); return -EINVAL; } err = hinic_get_link_state(adapter->hwdev, &link_status); if (err) { nicif_err(adapter, drv, netdev, "Get link status failed when set vf tx rate\n"); return -EIO; } if (!link_status) { nicif_err(adapter, drv, netdev, "Link status must be up when set vf tx rate\n"); return -EINVAL; } err = hinic_get_port_info(adapter->hwdev, &port_info); if (err || port_info.speed > LINK_SPEED_100GB) return -EIO; /* rate limit cannot be less than 0 and greater than link speed */ if (max_tx_rate < 0 || max_tx_rate > speeds[port_info.speed]) { nicif_err(adapter, drv, netdev, "Set vf max tx rate must be in [0 - %d]\n", speeds[port_info.speed]); return -EINVAL; } err = hinic_set_vf_tx_rate(adapter->hwdev, OS_VF_ID_TO_HW(vf), max_tx_rate, min_tx_rate); if (err) { nicif_err(adapter, drv, netdev, "Unable to set VF %d max rate %d min rate %d%s\n", vf, max_tx_rate, min_tx_rate, err == HINIC_TX_RATE_TABLE_FULL ? ", tx rate profile is full" : ""); return -EIO; } nicif_info(adapter, drv, netdev, "Set VF %d max tx rate %d min tx rate %d successfully\n", vf, max_tx_rate, min_tx_rate); return 0; } /*lint -restore*/ hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_sriov.h000066400000000000000000000031651502027332500216210ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_SRIOV_H #define HINIC_SRIOV_H enum hinic_sriov_state { HINIC_SRIOV_DISABLE, HINIC_SRIOV_ENABLE, HINIC_FUNC_REMOVE, }; struct hinic_sriov_info { struct pci_dev *pdev; void *hwdev; bool sriov_enabled; unsigned int num_vfs; unsigned long state; }; int hinic_pci_sriov_disable(struct pci_dev *dev); int hinic_pci_sriov_enable(struct pci_dev *dev, int num_vfs); int hinic_pci_sriov_configure(struct pci_dev *dev, int num_vfs); int hinic_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac); int hinic_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, __be16 vlan_proto); int hinic_ndo_get_vf_config(struct net_device *netdev, int vf, struct ifla_vf_info *ivi); int hinic_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting); int hinic_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting); int hinic_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link); int hinic_ndo_set_vf_bw(struct net_device *netdev, int vf, int min_tx_rate, int max_tx_rate); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_tx.c000066400000000000000000000712261502027332500211100ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_nic_io.h" #include "hinic_nic_dev.h" #include "hinic_qp.h" #include "hinic_tx.h" #include "hinic_dbg.h" #define MIN_SKB_LEN 32 #define MAX_PAYLOAD_OFFSET 221 #define NIC_QID(q_id, nic_dev) ((q_id) & ((nic_dev)->num_qps - 1)) #define TXQ_STATS_INC(txq, field) \ { \ u64_stats_update_begin(&(txq)->txq_stats.syncp); \ (txq)->txq_stats.field++; \ u64_stats_update_end(&(txq)->txq_stats.syncp); \ } void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats) { struct hinic_txq_stats *txq_stats = &txq->txq_stats; unsigned int start; u64_stats_update_begin(&stats->syncp); do { start = u64_stats_fetch_begin(&txq_stats->syncp); stats->bytes = txq_stats->bytes; stats->packets = txq_stats->packets; stats->busy = txq_stats->busy; stats->wake = txq_stats->wake; stats->dropped = txq_stats->dropped; stats->big_frags_pkts = txq_stats->big_frags_pkts; stats->big_udp_pkts = txq_stats->big_udp_pkts; } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); u64_stats_update_end(&stats->syncp); } void hinic_txq_clean_stats(struct hinic_txq_stats *txq_stats) { u64_stats_update_begin(&txq_stats->syncp); txq_stats->bytes = 0; txq_stats->packets = 0; txq_stats->busy = 0; txq_stats->wake = 0; txq_stats->dropped = 0; txq_stats->big_frags_pkts = 0; txq_stats->big_udp_pkts = 0; txq_stats->ufo_pkt_unsupport = 0; txq_stats->ufo_linearize_err = 0; txq_stats->ufo_alloc_skb_err = 0; txq_stats->skb_pad_err = 0; txq_stats->frag_len_overflow = 0; txq_stats->offload_cow_skb_err = 0; txq_stats->alloc_cpy_frag_err = 0; txq_stats->map_cpy_frag_err = 0; txq_stats->map_frag_err = 0; txq_stats->frag_size_err = 0; txq_stats->unknown_tunnel_pkt = 0; u64_stats_update_end(&txq_stats->syncp); } static void txq_stats_init(struct hinic_txq *txq) { struct hinic_txq_stats *txq_stats = &txq->txq_stats; u64_stats_init(&txq_stats->syncp); hinic_txq_clean_stats(txq_stats); } inline void hinic_set_buf_desc(struct hinic_sq_bufdesc *buf_descs, dma_addr_t addr, u32 len) { buf_descs->hi_addr = cpu_to_be32(upper_32_bits(addr)); buf_descs->lo_addr = cpu_to_be32(lower_32_bits(addr)); buf_descs->len = cpu_to_be32(len); } static int tx_map_skb(struct hinic_nic_dev *nic_dev, struct sk_buff *skb, struct hinic_txq *txq, struct hinic_tx_info *tx_info, struct hinic_sq_bufdesc *buf_descs, u16 skb_nr_frags) { struct pci_dev *pdev = nic_dev->pdev; struct hinic_dma_len *dma_len = tx_info->dma_len; skb_frag_t *frag = NULL; u16 base_nr_frags; int j, i = 0; int node, err = 0; u32 nsize, cpy_nsize = 0; u8 *vaddr, *cpy_buff = NULL; if (unlikely(skb_nr_frags > HINIC_MAX_SKB_NR_FRAGE)) { for (i = HINIC_MAX_SKB_NR_FRAGE; i <= skb_nr_frags; i++) cpy_nsize += skb_frag_size(&skb_shinfo(skb)->frags[i - 1]); if (!cpy_nsize) { TXQ_STATS_INC(txq, alloc_cpy_frag_err); return -EINVAL; } node = dev_to_node(&nic_dev->pdev->dev); if (node == NUMA_NO_NODE) cpy_buff = kzalloc(cpy_nsize, GFP_ATOMIC | __GFP_NOWARN); else cpy_buff = kzalloc_node(cpy_nsize, GFP_ATOMIC | __GFP_NOWARN, node); if (!cpy_buff) { TXQ_STATS_INC(txq, alloc_cpy_frag_err); return -ENOMEM; } tx_info->cpy_buff = cpy_buff; for (i = HINIC_MAX_SKB_NR_FRAGE; i <= skb_nr_frags; i++) { frag = &skb_shinfo(skb)->frags[i - 1]; nsize = skb_frag_size(frag); vaddr = kmap_local_page(skb_frag_page(frag)); memcpy(cpy_buff, vaddr + frag->bv_offset, nsize); kunmap_local(vaddr); cpy_buff += nsize; } } dma_len[0].dma = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); if (dma_mapping_error(&pdev->dev, dma_len[0].dma)) { TXQ_STATS_INC(txq, map_frag_err); err = -EFAULT; goto map_single_err; } dma_len[0].len = skb_headlen(skb); hinic_set_buf_desc(&buf_descs[0], dma_len[0].dma, dma_len[0].len); if (skb_nr_frags > HINIC_MAX_SKB_NR_FRAGE) base_nr_frags = HINIC_MAX_SKB_NR_FRAGE - 1; else base_nr_frags = skb_nr_frags; for (i = 0; i < base_nr_frags; ) { frag = &(skb_shinfo(skb)->frags[i]); nsize = skb_frag_size(frag); i++; dma_len[i].dma = skb_frag_dma_map(&pdev->dev, frag, 0, nsize, DMA_TO_DEVICE); if (dma_mapping_error(&pdev->dev, dma_len[i].dma)) { TXQ_STATS_INC(txq, map_frag_err); i--; err = -EFAULT; goto frag_map_err; } dma_len[i].len = nsize; hinic_set_buf_desc(&buf_descs[i], dma_len[i].dma, dma_len[i].len); } if (skb_nr_frags > HINIC_MAX_SKB_NR_FRAGE) { dma_len[HINIC_MAX_SKB_NR_FRAGE].dma = dma_map_single(&pdev->dev, tx_info->cpy_buff, cpy_nsize, DMA_TO_DEVICE); if (dma_mapping_error(&pdev->dev, dma_len[HINIC_MAX_SKB_NR_FRAGE].dma)) { TXQ_STATS_INC(txq, map_cpy_frag_err); err = -EFAULT; goto fusion_map_err; } dma_len[HINIC_MAX_SKB_NR_FRAGE].len = cpy_nsize; hinic_set_buf_desc(&buf_descs[HINIC_MAX_SKB_NR_FRAGE], dma_len[HINIC_MAX_SKB_NR_FRAGE].dma, dma_len[HINIC_MAX_SKB_NR_FRAGE].len); } return 0; fusion_map_err: frag_map_err: for (j = 0; j < i;) { j++; dma_unmap_page(&pdev->dev, dma_len[j].dma, dma_len[j].len, DMA_TO_DEVICE); } dma_unmap_single(&pdev->dev, dma_len[0].dma, dma_len[0].len, DMA_TO_DEVICE); map_single_err: kfree(tx_info->cpy_buff); tx_info->cpy_buff = NULL; return err; } static inline void tx_unmap_skb(struct hinic_nic_dev *nic_dev, struct sk_buff *skb, struct hinic_dma_len *dma_len, u16 valid_nr_frags) { struct pci_dev *pdev = nic_dev->pdev; int i; u16 nr_frags = valid_nr_frags; if (nr_frags > HINIC_MAX_SKB_NR_FRAGE) nr_frags = HINIC_MAX_SKB_NR_FRAGE; for (i = 0; i < nr_frags; ) { i++; dma_unmap_page(&pdev->dev, dma_len[i].dma, dma_len[i].len, DMA_TO_DEVICE); } dma_unmap_single(&pdev->dev, dma_len[0].dma, dma_len[0].len, DMA_TO_DEVICE); } union hinic_ip { struct iphdr *v4; struct ipv6hdr *v6; unsigned char *hdr; }; union hinic_l4 { struct tcphdr *tcp; struct udphdr *udp; unsigned char *hdr; }; #define TRANSPORT_OFFSET(l4_hdr, skb) ((u32)((l4_hdr) - (skb)->data)) static void get_inner_l3_l4_type(struct sk_buff *skb, union hinic_ip *ip, union hinic_l4 *l4, enum tx_offload_type offload_type, enum sq_l3_type *l3_type, u8 *l4_proto) { unsigned char *exthdr; if (ip->v4->version == 4) { *l3_type = (offload_type == TX_OFFLOAD_CSUM) ? IPV4_PKT_NO_CHKSUM_OFFLOAD : IPV4_PKT_WITH_CHKSUM_OFFLOAD; *l4_proto = ip->v4->protocol; } else if (ip->v4->version == 6) { *l3_type = IPV6_PKT; exthdr = ip->hdr + sizeof(*ip->v6); *l4_proto = ip->v6->nexthdr; if (exthdr != l4->hdr) { __be16 frag_off = 0; ipv6_skip_exthdr(skb, (int)(exthdr - skb->data), l4_proto, &frag_off); } } else { *l3_type = UNKNOWN_L3TYPE; *l4_proto = 0; } } static void get_inner_l4_info(struct sk_buff *skb, union hinic_l4 *l4, enum tx_offload_type offload_type, u8 l4_proto, enum sq_l4offload_type *l4_offload, u32 *l4_len, u32 *offset) { *offset = 0; *l4_len = 0; *l4_offload = OFFLOAD_DISABLE; switch (l4_proto) { case IPPROTO_TCP: *l4_offload = TCP_OFFLOAD_ENABLE; *l4_len = l4->tcp->doff * 4; /* doff in unit of 4B */ /* To keep same with TSO, payload offset begins from paylaod */ *offset = *l4_len + TRANSPORT_OFFSET(l4->hdr, skb); break; case IPPROTO_UDP: *l4_offload = UDP_OFFLOAD_ENABLE; *l4_len = sizeof(struct udphdr); *offset = TRANSPORT_OFFSET(l4->hdr, skb); break; case IPPROTO_SCTP: /* only csum offload support sctp */ if (offload_type != TX_OFFLOAD_CSUM) break; *l4_offload = SCTP_OFFLOAD_ENABLE; *l4_len = sizeof(struct sctphdr); /* To keep same with UFO, payload offset * begins from L4 header */ *offset = TRANSPORT_OFFSET(l4->hdr, skb); break; default: break; } } static int hinic_tx_csum(struct hinic_txq *txq, struct hinic_sq_task *task, u32 *queue_info, struct sk_buff *skb) { union hinic_ip ip; union hinic_l4 l4; enum sq_l3_type l3_type; enum sq_l4offload_type l4_offload; u32 network_hdr_len; u32 offset, l4_len; u8 l4_proto; if (skb->ip_summed != CHECKSUM_PARTIAL) return 0; if (skb->encapsulation) { u32 l4_tunnel_len; u32 tunnel_type = TUNNEL_UDP_NO_CSUM; ip.hdr = skb_network_header(skb); if (ip.v4->version == 4) { l3_type = IPV4_PKT_NO_CHKSUM_OFFLOAD; l4_proto = ip.v4->protocol; } else if (ip.v4->version == 6) { unsigned char *exthdr; __be16 frag_off; l3_type = IPV6_PKT; exthdr = ip.hdr + sizeof(*ip.v6); l4_proto = ip.v6->nexthdr; l4.hdr = skb_transport_header(skb); if (l4.hdr != exthdr) ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto, &frag_off); } else { l3_type = UNKNOWN_L3TYPE; l4_proto = IPPROTO_RAW; } hinic_task_set_outter_l3(task, l3_type, skb_network_header_len(skb)); switch (l4_proto) { case IPPROTO_UDP: l4_tunnel_len = skb_inner_network_offset(skb) - skb_transport_offset(skb); ip.hdr = skb_inner_network_header(skb); l4.hdr = skb_inner_transport_header(skb); network_hdr_len = skb_inner_network_header_len(skb); break; case IPPROTO_IPIP: case IPPROTO_IPV6: tunnel_type = NOT_TUNNEL; l4_tunnel_len = 0; ip.hdr = skb_inner_network_header(skb); l4.hdr = skb_transport_header(skb); network_hdr_len = skb_network_header_len(skb); break; default: TXQ_STATS_INC(txq, unknown_tunnel_pkt); /* Unsupport tunnel packet, disable csum offload */ skb_checksum_help(skb); return 0; } hinic_task_set_tunnel_l4(task, tunnel_type, l4_tunnel_len); } else { ip.hdr = skb_network_header(skb); l4.hdr = skb_transport_header(skb); network_hdr_len = skb_network_header_len(skb); } get_inner_l3_l4_type(skb, &ip, &l4, TX_OFFLOAD_CSUM, &l3_type, &l4_proto); get_inner_l4_info(skb, &l4, TX_OFFLOAD_CSUM, l4_proto, &l4_offload, &l4_len, &offset); hinic_task_set_inner_l3(task, l3_type, network_hdr_len); hinic_set_cs_inner_l4(task, queue_info, l4_offload, l4_len, offset); return 1; } static __sum16 csum_magic(union hinic_ip *ip, unsigned short proto) { return (ip->v4->version == 4) ? csum_tcpudp_magic(ip->v4->saddr, ip->v4->daddr, 0, proto, 0) : csum_ipv6_magic(&ip->v6->saddr, &ip->v6->daddr, 0, proto, 0); } static int hinic_tso(struct hinic_sq_task *task, u32 *queue_info, struct sk_buff *skb) { union hinic_ip ip; union hinic_l4 l4; enum sq_l3_type l3_type; enum sq_l4offload_type l4_offload; u32 network_hdr_len; u32 offset, l4_len; u32 ip_identify = 0; u8 l4_proto; int err; if (!skb_is_gso(skb)) return 0; err = skb_cow_head(skb, 0); if (err < 0) return err; if (skb->encapsulation) { u32 l4_tunnel_len; u32 tunnel_type = 0; u32 gso_type = skb_shinfo(skb)->gso_type; ip.hdr = skb_network_header(skb); l4.hdr = skb_transport_header(skb); network_hdr_len = skb_inner_network_header_len(skb); if (ip.v4->version == 4) l3_type = IPV4_PKT_WITH_CHKSUM_OFFLOAD; else if (ip.v4->version == 6) l3_type = IPV6_PKT; else l3_type = 0; hinic_task_set_outter_l3(task, l3_type, skb_network_header_len(skb)); if (gso_type & SKB_GSO_UDP_TUNNEL_CSUM) { l4.udp->check = ~csum_magic(&ip, IPPROTO_UDP); tunnel_type = TUNNEL_UDP_CSUM; } else if (gso_type & SKB_GSO_UDP_TUNNEL) { tunnel_type = TUNNEL_UDP_NO_CSUM; } l4_tunnel_len = skb_inner_network_offset(skb) - skb_transport_offset(skb); hinic_task_set_tunnel_l4(task, tunnel_type, l4_tunnel_len); ip.hdr = skb_inner_network_header(skb); l4.hdr = skb_inner_transport_header(skb); } else { ip.hdr = skb_network_header(skb); l4.hdr = skb_transport_header(skb); network_hdr_len = skb_network_header_len(skb); } get_inner_l3_l4_type(skb, &ip, &l4, TX_OFFLOAD_TSO, &l3_type, &l4_proto); if (l4_proto == IPPROTO_TCP) l4.tcp->check = ~csum_magic(&ip, IPPROTO_TCP); get_inner_l4_info(skb, &l4, TX_OFFLOAD_TSO, l4_proto, &l4_offload, &l4_len, &offset); hinic_task_set_inner_l3(task, l3_type, network_hdr_len); hinic_set_tso_inner_l4(task, queue_info, l4_offload, l4_len, offset, ip_identify, skb_shinfo(skb)->gso_size); return 1; } static enum tx_offload_type hinic_tx_offload(struct hinic_txq *txq, struct sk_buff *skb, struct hinic_sq_task *task, u32 *queue_info, u8 avd_flag) { enum tx_offload_type offload = 0; int tso_cs_en; u16 vlan_tag; task->pkt_info0 = 0; task->pkt_info1 = 0; task->pkt_info2 = 0; tso_cs_en = hinic_tso(task, queue_info, skb); if (tso_cs_en < 0) { offload = TX_OFFLOAD_INVALID; return offload; } else if (tso_cs_en) { offload |= TX_OFFLOAD_TSO; } else { tso_cs_en = hinic_tx_csum(txq, task, queue_info, skb); if (tso_cs_en) offload |= TX_OFFLOAD_CSUM; } if (unlikely(skb_vlan_tag_present(skb))) { vlan_tag = skb_vlan_tag_get(skb); hinic_set_vlan_tx_offload(task, queue_info, vlan_tag, vlan_tag >> VLAN_PRIO_SHIFT); offload |= TX_OFFLOAD_VLAN; } if (unlikely(SQ_CTRL_QUEUE_INFO_GET(*queue_info, PLDOFF) > MAX_PAYLOAD_OFFSET)) { offload = TX_OFFLOAD_INVALID; return offload; } if (avd_flag == HINIC_TX_UFO_AVD) task->pkt_info0 |= SQ_TASK_INFO0_SET(1, UFO_AVD); if (offload) { hinic_task_set_tx_offload_valid(task, skb_network_offset(skb)); task->pkt_info0 = be32_to_cpu(task->pkt_info0); task->pkt_info1 = be32_to_cpu(task->pkt_info1); task->pkt_info2 = be32_to_cpu(task->pkt_info2); } return offload; } static inline void __get_pkt_stats(struct hinic_tx_info *tx_info, struct sk_buff *skb) { u32 ihs, hdr_len; if (skb_is_gso(skb)) { #if (defined(HAVE_SKB_INNER_TRANSPORT_HEADER) && \ defined(HAVE_SK_BUFF_ENCAPSULATION)) if (skb->encapsulation) { #ifdef HAVE_SKB_INNER_TRANSPORT_OFFSET ihs = skb_inner_transport_offset(skb) + inner_tcp_hdrlen(skb); #else ihs = (skb_inner_transport_header(skb) - skb->data) + inner_tcp_hdrlen(skb); #endif } else { #endif ihs = skb_transport_offset(skb) + tcp_hdrlen(skb); #if (defined(HAVE_SKB_INNER_TRANSPORT_HEADER) && \ defined(HAVE_SK_BUFF_ENCAPSULATION)) } #endif hdr_len = (skb_shinfo(skb)->gso_segs - 1) * ihs; tx_info->num_bytes = skb->len + (u64)hdr_len; } else { tx_info->num_bytes = skb->len > ETH_ZLEN ? skb->len : ETH_ZLEN; } tx_info->num_pkts = 1; } inline u8 hinic_get_vlan_pri(struct sk_buff *skb) { u16 vlan_tci = 0; int err; err = vlan_get_tag(skb, &vlan_tci); if (err) return 0; return (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; } static void *__try_to_get_wqe(struct net_device *netdev, u16 q_id, int wqebb_cnt, u16 *pi, u8 *owner) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); void *wqe = NULL; netif_stop_subqueue(netdev, q_id); /* We need to check again in a case another CPU has just * made room available. */ if (unlikely(hinic_get_sq_free_wqebbs(nic_dev->hwdev, q_id) >= wqebb_cnt)) { netif_start_subqueue(netdev, q_id); /* there have enough wqebbs after queue is wake up */ wqe = hinic_get_sq_wqe(nic_dev->hwdev, q_id, wqebb_cnt, pi, owner); } return wqe; } #define HINIC_FRAG_STATUS_OK 0 #define HINIC_FRAG_STATUS_IGNORE 1 static netdev_tx_t hinic_send_one_skb(struct sk_buff *skb, struct net_device *netdev, struct hinic_txq *txq, u8 *flag, u8 avd_flag) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_tx_info *tx_info; struct hinic_sq_wqe *wqe = NULL; enum tx_offload_type offload = 0; u16 q_id = txq->q_id; u32 queue_info = 0; u8 owner = 0; u16 pi = 0; int err, wqebb_cnt; u16 num_sge = 0; u16 original_nr_frags; u16 new_nr_frags; u16 i; int frag_err = HINIC_FRAG_STATUS_OK; /* skb->dev will not initialized when calling netdev_alloc_skb_ip_align * and parameter of length is largger then PAGE_SIZE(under redhat7.3), * but skb->dev will be used in vlan_get_tag or somewhere */ if (unlikely(!skb->dev)) skb->dev = netdev; if (unlikely(skb->len < MIN_SKB_LEN)) { if (skb_pad(skb, (int)(MIN_SKB_LEN - skb->len))) { TXQ_STATS_INC(txq, skb_pad_err); goto tx_skb_pad_err; } skb->len = MIN_SKB_LEN; } original_nr_frags = skb_shinfo(skb)->nr_frags; new_nr_frags = original_nr_frags; /* If size of lastest frags are all zero, should ignore this frags. * If size of some frag in the middle is zero, should drop this skb. */ for (i = 0; i < original_nr_frags; i++) { if ((skb_frag_size(&skb_shinfo(skb)->frags[i])) && frag_err == HINIC_FRAG_STATUS_OK) continue; if ((!skb_frag_size(&skb_shinfo(skb)->frags[i])) && frag_err == HINIC_FRAG_STATUS_OK) { frag_err = HINIC_FRAG_STATUS_IGNORE; new_nr_frags = i + 1; continue; } if ((!skb_frag_size(&skb_shinfo(skb)->frags[i])) && frag_err == HINIC_FRAG_STATUS_IGNORE) continue; if ((skb_frag_size(&skb_shinfo(skb)->frags[i])) && frag_err == HINIC_FRAG_STATUS_IGNORE) { TXQ_STATS_INC(txq, frag_size_err); goto tx_drop_pkts; } } num_sge = new_nr_frags + 1; /* if skb->len is more than 65536B but num_sge is 1, * driver will drop it */ if (unlikely(skb->len > HINIC_GSO_MAX_SIZE && num_sge == 1)) { TXQ_STATS_INC(txq, frag_len_overflow); goto tx_drop_pkts; } /* if sge number more than 17, driver will set 17 sges */ if (unlikely(num_sge > HINIC_MAX_SQ_SGE)) { TXQ_STATS_INC(txq, big_frags_pkts); num_sge = HINIC_MAX_SQ_SGE; } wqebb_cnt = HINIC_SQ_WQEBB_CNT(num_sge); if (likely(hinic_get_sq_free_wqebbs(nic_dev->hwdev, q_id) >= wqebb_cnt)) { if (likely(wqebb_cnt == 1)) { hinic_update_sq_pi(nic_dev->hwdev, q_id, wqebb_cnt, &pi, &owner); wqe = txq->tx_info[pi].wqe; } else { wqe = hinic_get_sq_wqe(nic_dev->hwdev, q_id, wqebb_cnt, &pi, &owner); } } else { wqe = __try_to_get_wqe(netdev, q_id, wqebb_cnt, &pi, &owner); if (likely(!wqe)) { TXQ_STATS_INC(txq, busy); return NETDEV_TX_BUSY; } } tx_info = &txq->tx_info[pi]; tx_info->skb = skb; tx_info->wqebb_cnt = wqebb_cnt; tx_info->valid_nr_frags = new_nr_frags; __get_pkt_stats(tx_info, skb); offload = hinic_tx_offload(txq, skb, &wqe->task, &queue_info, avd_flag); if (unlikely(offload == TX_OFFLOAD_INVALID)) { hinic_return_sq_wqe(nic_dev->hwdev, q_id, wqebb_cnt, owner); TXQ_STATS_INC(txq, offload_cow_skb_err); goto tx_drop_pkts; } err = tx_map_skb(nic_dev, skb, txq, tx_info, wqe->buf_descs, new_nr_frags); if (err) { hinic_return_sq_wqe(nic_dev->hwdev, q_id, wqebb_cnt, owner); goto tx_drop_pkts; } hinic_prepare_sq_ctrl(&wqe->ctrl, queue_info, num_sge, owner); hinic_send_sq_wqe(nic_dev->hwdev, q_id, wqe, wqebb_cnt, nic_dev->sq_cos_mapping[hinic_get_vlan_pri(skb)]); return NETDEV_TX_OK; tx_drop_pkts: dev_kfree_skb_any(skb); tx_skb_pad_err: TXQ_STATS_INC(txq, dropped); *flag = HINIC_TX_DROPPED; return NETDEV_TX_OK; } netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 q_id = skb_get_queue_mapping(skb); struct hinic_txq *txq; u8 flag = 0; if (unlikely(!nic_dev->heart_status)) { dev_kfree_skb_any(skb); HINIC_NIC_STATS_INC(nic_dev, tx_carrier_off_drop); return NETDEV_TX_OK; } txq = &nic_dev->txqs[q_id]; return hinic_send_one_skb(skb, netdev, txq, &flag, HINIC_TX_NON_AVD); } netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 q_id = skb_get_queue_mapping(skb); struct hinic_txq *txq; u8 flag = 0; if (unlikely(!netif_carrier_ok(netdev) || !nic_dev->heart_status)) { dev_kfree_skb_any(skb); HINIC_NIC_STATS_INC(nic_dev, tx_carrier_off_drop); return NETDEV_TX_OK; } if (unlikely(q_id >= nic_dev->num_qps)) { txq = &nic_dev->txqs[0]; HINIC_NIC_STATS_INC(nic_dev, tx_invalid_qid); goto tx_drop_pkts; } txq = &nic_dev->txqs[q_id]; return hinic_send_one_skb(skb, netdev, txq, &flag, HINIC_TX_NON_AVD); tx_drop_pkts: dev_kfree_skb_any(skb); u64_stats_update_begin(&txq->txq_stats.syncp); txq->txq_stats.dropped++; u64_stats_update_end(&txq->txq_stats.syncp); return NETDEV_TX_OK; } static inline void tx_free_skb(struct hinic_nic_dev *nic_dev, struct sk_buff *skb, struct hinic_tx_info *tx_info) { tx_unmap_skb(nic_dev, skb, tx_info->dma_len, tx_info->valid_nr_frags); kfree(tx_info->cpy_buff); tx_info->cpy_buff = NULL; dev_kfree_skb_any(skb); } static void free_all_tx_skbs(struct hinic_txq *txq) { struct hinic_nic_dev *nic_dev = netdev_priv(txq->netdev); struct hinic_tx_info *tx_info; u16 ci; int free_wqebbs = hinic_get_sq_free_wqebbs(nic_dev->hwdev, txq->q_id) + 1; while (free_wqebbs < txq->q_depth) { ci = hinic_get_sq_local_ci(nic_dev->hwdev, txq->q_id); tx_info = &txq->tx_info[ci]; tx_free_skb(nic_dev, tx_info->skb, tx_info); hinic_update_sq_local_ci(nic_dev->hwdev, txq->q_id, tx_info->wqebb_cnt); free_wqebbs += tx_info->wqebb_cnt; } } int hinic_tx_poll(struct hinic_txq *txq, int budget) { struct hinic_nic_dev *nic_dev = netdev_priv(txq->netdev); struct sk_buff *skb; struct hinic_tx_info *tx_info; u64 tx_bytes = 0, wake = 0; int pkts = 0, nr_pkts = 0, wqebb_cnt = 0; u16 hw_ci, sw_ci = 0, q_id = txq->q_id; hw_ci = hinic_get_sq_hw_ci(nic_dev->hwdev, q_id); dma_rmb(); sw_ci = hinic_get_sq_local_ci(nic_dev->hwdev, q_id); do { tx_info = &txq->tx_info[sw_ci]; /* Whether all of the wqebb of this wqe is completed */ if (hw_ci == sw_ci || ((hw_ci - sw_ci) & txq->q_mask) < tx_info->wqebb_cnt) { break; } sw_ci = (u16)(sw_ci + tx_info->wqebb_cnt) & txq->q_mask; prefetch(&txq->tx_info[sw_ci]); wqebb_cnt += tx_info->wqebb_cnt; skb = tx_info->skb; tx_bytes += tx_info->num_bytes; nr_pkts += tx_info->num_pkts; pkts++; tx_free_skb(nic_dev, skb, tx_info); } while (likely(pkts < budget)); hinic_update_sq_local_ci(nic_dev->hwdev, q_id, wqebb_cnt); if (unlikely(__netif_subqueue_stopped(nic_dev->netdev, q_id) && hinic_get_sq_free_wqebbs(nic_dev->hwdev, q_id) >= 1 && test_bit(HINIC_INTF_UP, &nic_dev->flags))) { struct netdev_queue *netdev_txq = netdev_get_tx_queue(txq->netdev, q_id); __netif_tx_lock(netdev_txq, smp_processor_id()); /* To avoid re-waking subqueue with xmit_frame */ if (__netif_subqueue_stopped(nic_dev->netdev, q_id)) { netif_wake_subqueue(nic_dev->netdev, q_id); wake++; } __netif_tx_unlock(netdev_txq); } u64_stats_update_begin(&txq->txq_stats.syncp); txq->txq_stats.bytes += tx_bytes; txq->txq_stats.packets += nr_pkts; txq->txq_stats.wake += wake; u64_stats_update_end(&txq->txq_stats.syncp); return pkts; } int hinic_setup_tx_wqe(struct hinic_txq *txq) { struct net_device *netdev = txq->netdev; struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_sq_wqe *wqe; struct hinic_tx_info *tx_info; u16 pi = 0; int i; u8 owner = 0; for (i = 0; i < txq->q_depth; i++) { tx_info = &txq->tx_info[i]; wqe = hinic_get_sq_wqe(nic_dev->hwdev, txq->q_id, 1, &pi, &owner); if (!wqe) { nicif_err(nic_dev, drv, netdev, "Failed to get SQ wqe\n"); break; } tx_info->wqe = wqe; } hinic_return_sq_wqe(nic_dev->hwdev, txq->q_id, txq->q_depth, owner); return i; } int hinic_setup_all_tx_resources(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_txq *txq; u64 tx_info_sz; u16 i, q_id; int err; for (q_id = 0; q_id < nic_dev->num_qps; q_id++) { txq = &nic_dev->txqs[q_id]; tx_info_sz = txq->q_depth * sizeof(*txq->tx_info); if (!tx_info_sz) { nicif_err(nic_dev, drv, netdev, "Cannot allocate zero size txq%d info\n", q_id); err = -EINVAL; goto init_txq_err; } txq->tx_info = kzalloc(tx_info_sz, GFP_KERNEL); if (!txq->tx_info) { err = -ENOMEM; goto init_txq_err; } err = hinic_setup_tx_wqe(txq); if (err != txq->q_depth) { nicif_err(nic_dev, drv, netdev, "Failed to setup Tx: %d wqe\n", q_id); q_id++; goto init_txq_err; } } return 0; init_txq_err: for (i = 0; i < q_id; i++) { txq = &nic_dev->txqs[i]; kfree(txq->tx_info); } return err; } void hinic_free_all_tx_resources(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_txq *txq; u16 q_id; for (q_id = 0; q_id < nic_dev->num_qps; q_id++) { txq = &nic_dev->txqs[q_id]; free_all_tx_skbs(txq); kfree(txq->tx_info); } } void hinic_set_sq_default_cos(struct net_device *netdev, u8 cos_id) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); int up; for (up = HINIC_DCB_UP_MAX - 1; up >= 0; up--) nic_dev->sq_cos_mapping[up] = nic_dev->default_cos_id; } int hinic_sq_cos_mapping(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct hinic_dcb_state dcb_state = {0}; u8 default_cos = 0; int err; if (HINIC_FUNC_IS_VF(nic_dev->hwdev)) { err = hinic_get_pf_dcb_state(nic_dev->hwdev, &dcb_state); if (err) { hinic_info(nic_dev, drv, "Failed to get vf default cos\n"); return err; } default_cos = dcb_state.default_cos; nic_dev->default_cos_id = default_cos; hinic_set_sq_default_cos(nic_dev->netdev, default_cos); } else { default_cos = nic_dev->default_cos_id; if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) memcpy(nic_dev->sq_cos_mapping, nic_dev->up_cos, sizeof(nic_dev->sq_cos_mapping)); else hinic_set_sq_default_cos(nic_dev->netdev, default_cos); dcb_state.dcb_on = !!test_bit(HINIC_DCB_ENABLE, &nic_dev->flags); dcb_state.default_cos = default_cos; memcpy(dcb_state.up_cos, nic_dev->sq_cos_mapping, sizeof(dcb_state.up_cos)); err = hinic_set_dcb_state(nic_dev->hwdev, &dcb_state); if (err) hinic_info(nic_dev, drv, "Failed to set vf default cos\n"); } return err; } int hinic_alloc_txqs(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); struct pci_dev *pdev = nic_dev->pdev; struct hinic_txq *txq; u16 q_id, num_txqs = nic_dev->max_qps; u64 txq_size; txq_size = num_txqs * sizeof(*nic_dev->txqs); if (!txq_size) { nic_err(&pdev->dev, "Cannot allocate zero size txqs\n"); return -EINVAL; } nic_dev->txqs = kzalloc(txq_size, GFP_KERNEL); if (!nic_dev->txqs) { nic_err(&pdev->dev, "Failed to allocate txqs\n"); return -ENOMEM; } for (q_id = 0; q_id < num_txqs; q_id++) { txq = &nic_dev->txqs[q_id]; txq->netdev = netdev; txq->q_id = q_id; txq->q_depth = nic_dev->sq_depth; txq->q_mask = nic_dev->sq_depth - 1; txq_stats_init(txq); } return 0; } void hinic_free_txqs(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); kfree(nic_dev->txqs); } /* should stop transmit any packets before calling this function */ #define HINIC_FLUSH_QUEUE_TIMEOUT 1000 static bool hinic_get_hw_handle_status(void *hwdev, u16 q_id) { u16 sw_pi = 0, hw_ci = 0; sw_pi = hinic_dbg_get_sq_pi(hwdev, q_id); hw_ci = hinic_get_sq_hw_ci(hwdev, q_id); return sw_pi == hw_ci; } int hinic_stop_sq(struct hinic_txq *txq) { struct hinic_nic_dev *nic_dev = netdev_priv(txq->netdev); unsigned long timeout; int err; timeout = msecs_to_jiffies(HINIC_FLUSH_QUEUE_TIMEOUT) + jiffies; do { if (hinic_get_hw_handle_status(nic_dev->hwdev, txq->q_id)) return 0; usleep_range(900, 1000); } while (time_before(jiffies, timeout)); /* force hardware to drop packets */ timeout = msecs_to_jiffies(HINIC_FLUSH_QUEUE_TIMEOUT) + jiffies; do { if (hinic_get_hw_handle_status(nic_dev->hwdev, txq->q_id)) return 0; err = hinic_force_drop_tx_pkt(nic_dev->hwdev); if (err) break; usleep_range(9900, 10000); } while (time_before(jiffies, timeout)); /* Avoid msleep takes too long and get a fake result */ if (hinic_get_hw_handle_status(nic_dev->hwdev, txq->q_id)) return 0; return -EFAULT; } void hinic_flush_txqs(struct net_device *netdev) { struct hinic_nic_dev *nic_dev = netdev_priv(netdev); u16 qid; int err; for (qid = 0; qid < nic_dev->num_qps; qid++) { err = hinic_stop_sq(&nic_dev->txqs[qid]); if (err) nicif_err(nic_dev, drv, netdev, "Failed to stop sq%d\n", qid); } } /*lint -e766*/ hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_tx.h000066400000000000000000000052721502027332500211130ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_TX_H #define HINIC_TX_H enum tx_offload_type { TX_OFFLOAD_TSO = BIT(0), TX_OFFLOAD_CSUM = BIT(1), TX_OFFLOAD_VLAN = BIT(2), TX_OFFLOAD_INVALID = BIT(3), }; struct hinic_txq_stats { u64 packets; u64 bytes; u64 busy; u64 wake; u64 dropped; u64 big_frags_pkts; u64 big_udp_pkts; /* Subdivision statistics show in private tool */ u64 ufo_pkt_unsupport; u64 ufo_linearize_err; u64 ufo_alloc_skb_err; u64 skb_pad_err; u64 frag_len_overflow; u64 offload_cow_skb_err; u64 alloc_cpy_frag_err; u64 map_cpy_frag_err; u64 map_frag_err; u64 frag_size_err; u64 unknown_tunnel_pkt; struct u64_stats_sync syncp; }; struct hinic_dma_len { dma_addr_t dma; u32 len; }; #define MAX_SGE_NUM_PER_WQE 17 struct hinic_tx_info { struct sk_buff *skb; int wqebb_cnt; int num_sge; void *wqe; u8 *cpy_buff; u16 valid_nr_frags; u16 num_pkts; u64 num_bytes; struct hinic_dma_len dma_len[MAX_SGE_NUM_PER_WQE]; }; struct hinic_txq { struct net_device *netdev; u16 q_id; u16 q_depth; u16 q_mask; struct hinic_txq_stats txq_stats; u64 last_moder_packets; u64 last_moder_bytes; struct hinic_tx_info *tx_info; }; enum hinic_tx_xmit_status { HINIC_TX_OK = 0, HINIC_TX_DROPPED = 1, HINIC_TX_BUSY = 2, }; enum hinic_tx_avd_type { HINIC_TX_NON_AVD = 0, HINIC_TX_UFO_AVD = 1, }; void hinic_txq_clean_stats(struct hinic_txq_stats *txq_stats); void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats); netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev); netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); int hinic_setup_all_tx_resources(struct net_device *netdev); void hinic_free_all_tx_resources(struct net_device *netdev); void hinic_set_sq_default_cos(struct net_device *netdev, u8 cos_id); int hinic_sq_cos_mapping(struct net_device *netdev); int hinic_alloc_txqs(struct net_device *netdev); void hinic_free_txqs(struct net_device *netdev); int hinic_tx_poll(struct hinic_txq *txq, int budget); u8 hinic_get_vlan_pri(struct sk_buff *skb); void hinic_flush_txqs(struct net_device *netdev); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_wq.c000066400000000000000000000400111502027332500210700ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt #include #include #include #include #include #include #include #include #include #include #include "ossl_knl.h" #include "hinic_hw.h" #include "hinic_hw_mgmt.h" #include "hinic_hwif.h" #include "hinic_wq.h" #include "hinic_qe_def.h" #define WQS_MAX_NUM_BLOCKS 256 #define WQS_FREE_BLOCKS_SIZE(wqs) (WQS_MAX_NUM_BLOCKS * \ sizeof((wqs)->free_blocks[0])) static int wqs_next_block(struct hinic_wqs *wqs, u32 *page_idx, u32 *block_idx); static void wqs_return_block(struct hinic_wqs *wqs, u32 page_idx, u32 block_idx); static int queue_alloc_page(void *handle, u64 **vaddr, u64 *paddr, u64 **shadow_vaddr, u64 page_sz) { dma_addr_t dma_addr = 0; *vaddr = dma_alloc_coherent(handle, page_sz, &dma_addr, GFP_KERNEL); if (!*vaddr) { sdk_err(handle, "Failed to allocate dma to wqs page\n"); return -ENOMEM; } if (!ADDR_4K_ALIGNED(dma_addr)) { sdk_err(handle, "Cla is not 4k aligned\n"); goto shadow_vaddr_err; } *paddr = (u64)dma_addr; /* use vzalloc for big mem, shadow_vaddr only used at initialization */ *shadow_vaddr = vzalloc(page_sz); if (!*shadow_vaddr) { sdk_err(handle, "Failed to allocate shadow page vaddr\n"); goto shadow_vaddr_err; } return 0; shadow_vaddr_err: dma_free_coherent(handle, page_sz, *vaddr, dma_addr); return -ENOMEM; } static int wqs_allocate_page(struct hinic_wqs *wqs, u32 page_idx) { return queue_alloc_page(wqs->dev_hdl, &wqs->page_vaddr[page_idx], &wqs->page_paddr[page_idx], &wqs->shadow_page_vaddr[page_idx], WQS_PAGE_SIZE); } static void wqs_free_page(struct hinic_wqs *wqs, u32 page_idx) { dma_free_coherent(wqs->dev_hdl, WQS_PAGE_SIZE, wqs->page_vaddr[page_idx], (dma_addr_t)wqs->page_paddr[page_idx]); vfree(wqs->shadow_page_vaddr[page_idx]); } static int cmdq_allocate_page(struct hinic_cmdq_pages *cmdq_pages) { return queue_alloc_page(cmdq_pages->dev_hdl, &cmdq_pages->cmdq_page_vaddr, &cmdq_pages->cmdq_page_paddr, &cmdq_pages->cmdq_shadow_page_vaddr, CMDQ_PAGE_SIZE); } static void cmdq_free_page(struct hinic_cmdq_pages *cmdq_pages) { dma_free_coherent(cmdq_pages->dev_hdl, CMDQ_PAGE_SIZE, cmdq_pages->cmdq_page_vaddr, (dma_addr_t)cmdq_pages->cmdq_page_paddr); vfree(cmdq_pages->cmdq_shadow_page_vaddr); } static int alloc_wqes_shadow(struct hinic_wq *wq) { u64 size; /* if wq->max_wqe_size == 0, we don't need to alloc shadow */ if (wq->max_wqe_size <= wq->wqebb_size) return 0; size = (u64)wq->num_q_pages * wq->max_wqe_size; wq->shadow_wqe = kzalloc(size, GFP_KERNEL); if (!wq->shadow_wqe) { pr_err("Failed to allocate shadow wqe\n"); return -ENOMEM; } size = wq->num_q_pages * sizeof(wq->prod_idx); wq->shadow_idx = kzalloc(size, GFP_KERNEL); if (!wq->shadow_idx) { pr_err("Failed to allocate shadow index\n"); goto shadow_idx_err; } return 0; shadow_idx_err: kfree(wq->shadow_wqe); return -ENOMEM; } static void free_wqes_shadow(struct hinic_wq *wq) { if (wq->max_wqe_size <= wq->wqebb_size) return; kfree(wq->shadow_idx); kfree(wq->shadow_wqe); } static void free_wq_pages(void *handle, struct hinic_wq *wq, u32 num_q_pages) { u32 i; for (i = 0; i < num_q_pages; i++) hinic_dma_free_coherent_align(handle, &wq->mem_align[i]); free_wqes_shadow(wq); wq->block_vaddr = NULL; wq->shadow_block_vaddr = NULL; kfree(wq->mem_align); } static int alloc_wq_pages(void *dev_hdl, struct hinic_wq *wq) { struct hinic_dma_addr_align *mem_align; u64 *vaddr, *paddr; u32 i, num_q_pages; int err; vaddr = wq->shadow_block_vaddr; paddr = wq->block_vaddr; num_q_pages = ALIGN(WQ_SIZE(wq), wq->wq_page_size) / wq->wq_page_size; if (num_q_pages > WQ_MAX_PAGES) { sdk_err(dev_hdl, "Number(%d) wq pages exceeds the limit\n", num_q_pages); return -EINVAL; } if (num_q_pages & (num_q_pages - 1)) { sdk_err(dev_hdl, "Wq num(%d) q pages must be power of 2\n", num_q_pages); return -EINVAL; } wq->num_q_pages = num_q_pages; err = alloc_wqes_shadow(wq); if (err) { sdk_err(dev_hdl, "Failed to allocate wqe shadow\n"); return err; } wq->mem_align = kcalloc(wq->num_q_pages, sizeof(*wq->mem_align), GFP_KERNEL); if (!wq->mem_align) { free_wqes_shadow(wq); return -ENOMEM; } for (i = 0; i < num_q_pages; i++) { mem_align = &wq->mem_align[i]; err = hinic_dma_alloc_coherent_align(dev_hdl, wq->wq_page_size, wq->wq_page_size, GFP_KERNEL, mem_align); if (err) { sdk_err(dev_hdl, "Failed to allocate wq page\n"); goto alloc_wq_pages_err; } *paddr = cpu_to_be64(mem_align->align_paddr); *vaddr = (u64)mem_align->align_vaddr; paddr++; vaddr++; } return 0; alloc_wq_pages_err: free_wq_pages(dev_hdl, wq, i); return -ENOMEM; } int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq, u32 wqebb_size, u32 wq_page_size, u16 q_depth, u32 max_wqe_size) { u32 num_wqebbs_per_page; int err; if (wqebb_size == 0) { sdk_err(wqs->dev_hdl, "Wqebb_size must be >0\n"); return -EINVAL; } if (q_depth & (q_depth - 1)) { sdk_err(wqs->dev_hdl, "Wq q_depth(%d) isn't power of 2\n", q_depth); return -EINVAL; } if (wq_page_size & (wq_page_size - 1)) { sdk_err(wqs->dev_hdl, "Wq page_size(%d) isn't power of 2\n", wq_page_size); return -EINVAL; } num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size; if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) { sdk_err(wqs->dev_hdl, "Num(%d) wqebbs per page isn't power of 2\n", num_wqebbs_per_page); return -EINVAL; } err = wqs_next_block(wqs, &wq->page_idx, &wq->block_idx); if (err) { sdk_err(wqs->dev_hdl, "Failed to get free wqs next block\n"); return err; } wq->wqebb_size = wqebb_size; wq->wq_page_size = wq_page_size; wq->q_depth = q_depth; wq->max_wqe_size = max_wqe_size; wq->num_wqebbs_per_page = num_wqebbs_per_page; wq->wqebbs_per_page_shift = (u32)ilog2(num_wqebbs_per_page); wq->block_vaddr = WQ_BASE_VADDR(wqs, wq); wq->shadow_block_vaddr = WQ_BASE_ADDR(wqs, wq); wq->block_paddr = WQ_BASE_PADDR(wqs, wq); err = alloc_wq_pages(wqs->dev_hdl, wq); if (err) { sdk_err(wqs->dev_hdl, "Failed to allocate wq pages\n"); goto alloc_wq_pages_err; } atomic_set(&wq->delta, q_depth); wq->cons_idx = 0; wq->prod_idx = 0; wq->mask = q_depth - 1; return 0; alloc_wq_pages_err: wqs_return_block(wqs, wq->page_idx, wq->block_idx); return err; } void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq) { free_wq_pages(wqs->dev_hdl, wq, wq->num_q_pages); wqs_return_block(wqs, wq->page_idx, wq->block_idx); } static int wqs_next_block(struct hinic_wqs *wqs, u32 *page_idx, u32 *block_idx) { u32 pos; spin_lock(&wqs->alloc_blocks_lock); if (wqs->num_free_blks <= 0) { spin_unlock(&wqs->alloc_blocks_lock); return -ENOMEM; } wqs->num_free_blks--; pos = wqs->alloc_blk_pos++; pos &= WQS_MAX_NUM_BLOCKS - 1; *page_idx = wqs->free_blocks[pos].page_idx; *block_idx = wqs->free_blocks[pos].block_idx; wqs->free_blocks[pos].page_idx = 0xFFFFFFFF; wqs->free_blocks[pos].block_idx = 0xFFFFFFFF; spin_unlock(&wqs->alloc_blocks_lock); return 0; } static void wqs_return_block(struct hinic_wqs *wqs, u32 page_idx, u32 block_idx) { u32 pos; spin_lock(&wqs->alloc_blocks_lock); wqs->num_free_blks++; pos = wqs->return_blk_pos++; pos &= WQS_MAX_NUM_BLOCKS - 1; wqs->free_blocks[pos].page_idx = page_idx; wqs->free_blocks[pos].block_idx = block_idx; spin_unlock(&wqs->alloc_blocks_lock); } static void init_wqs_blocks_arr(struct hinic_wqs *wqs) { u32 page_idx, blk_idx, pos = 0; for (page_idx = 0; page_idx < wqs->num_pages; page_idx++) { for (blk_idx = 0; blk_idx < WQS_BLOCKS_PER_PAGE; blk_idx++) { wqs->free_blocks[pos].page_idx = page_idx; wqs->free_blocks[pos].block_idx = blk_idx; pos++; } } wqs->alloc_blk_pos = 0; wqs->return_blk_pos = 0; wqs->num_free_blks = WQS_MAX_NUM_BLOCKS; spin_lock_init(&wqs->alloc_blocks_lock); } void hinic_wq_wqe_pg_clear(struct hinic_wq *wq) { u64 *block_vaddr; u32 pg_idx; block_vaddr = wq->shadow_block_vaddr; atomic_set(&wq->delta, wq->q_depth); wq->cons_idx = 0; wq->prod_idx = 0; for (pg_idx = 0; pg_idx < wq->num_q_pages; pg_idx++) memset((void *)(*(block_vaddr + pg_idx)), 0, wq->wq_page_size); } int hinic_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages, struct hinic_wq *wq, void *dev_hdl, int cmdq_blocks, u32 wq_page_size, u32 wqebb_size, u16 q_depth, u32 max_wqe_size) { int i, j, err = -ENOMEM; if (q_depth & (q_depth - 1)) { sdk_err(dev_hdl, "Cmdq q_depth(%d) isn't power of 2\n", q_depth); return -EINVAL; } cmdq_pages->dev_hdl = dev_hdl; err = cmdq_allocate_page(cmdq_pages); if (err) { sdk_err(dev_hdl, "Failed to allocate CMDQ page\n"); return err; } for (i = 0; i < cmdq_blocks; i++) { wq[i].page_idx = 0; wq[i].block_idx = (u32)i; wq[i].wqebb_size = wqebb_size; wq[i].wq_page_size = wq_page_size; wq[i].q_depth = q_depth; wq[i].max_wqe_size = max_wqe_size; wq[i].num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size; wq[i].wqebbs_per_page_shift = (u32)ilog2(wq[i].num_wqebbs_per_page); wq[i].block_vaddr = CMDQ_BASE_VADDR(cmdq_pages, &wq[i]); wq[i].shadow_block_vaddr = CMDQ_BASE_ADDR(cmdq_pages, &wq[i]); wq[i].block_paddr = CMDQ_BASE_PADDR(cmdq_pages, &wq[i]); err = alloc_wq_pages(cmdq_pages->dev_hdl, &wq[i]); if (err) { sdk_err(dev_hdl, "Failed to alloc CMDQ blocks\n"); goto cmdq_block_err; } atomic_set(&wq[i].delta, q_depth); wq[i].cons_idx = 0; wq[i].prod_idx = 0; wq[i].mask = q_depth - 1; } return 0; cmdq_block_err: for (j = 0; j < i; j++) free_wq_pages(cmdq_pages->dev_hdl, &wq[j], wq[j].num_q_pages); cmdq_free_page(cmdq_pages); return err; } void hinic_cmdq_free(struct hinic_cmdq_pages *cmdq_pages, struct hinic_wq *wq, int cmdq_blocks) { int i; for (i = 0; i < cmdq_blocks; i++) free_wq_pages(cmdq_pages->dev_hdl, &wq[i], wq[i].num_q_pages); cmdq_free_page(cmdq_pages); } static int alloc_page_addr(struct hinic_wqs *wqs) { u64 size = wqs->num_pages * sizeof(*wqs->page_paddr); wqs->page_paddr = kzalloc(size, GFP_KERNEL); if (!wqs->page_paddr) return -ENOMEM; size = wqs->num_pages * sizeof(*wqs->page_vaddr); wqs->page_vaddr = kzalloc(size, GFP_KERNEL); if (!wqs->page_vaddr) goto page_vaddr_err; size = wqs->num_pages * sizeof(*wqs->shadow_page_vaddr); wqs->shadow_page_vaddr = kzalloc(size, GFP_KERNEL); if (!wqs->shadow_page_vaddr) goto page_shadow_vaddr_err; return 0; page_shadow_vaddr_err: kfree(wqs->page_vaddr); page_vaddr_err: kfree(wqs->page_paddr); return -ENOMEM; } static void free_page_addr(struct hinic_wqs *wqs) { kfree(wqs->shadow_page_vaddr); kfree(wqs->page_vaddr); kfree(wqs->page_paddr); } int hinic_wqs_alloc(struct hinic_wqs *wqs, int num_wqs, void *dev_hdl) { u32 i, page_idx; int err; wqs->dev_hdl = dev_hdl; wqs->num_pages = WQ_NUM_PAGES(num_wqs); if (alloc_page_addr(wqs)) { sdk_err(dev_hdl, "Failed to allocate mem for page addresses\n"); return -ENOMEM; } for (page_idx = 0; page_idx < wqs->num_pages; page_idx++) { err = wqs_allocate_page(wqs, page_idx); if (err) { sdk_err(dev_hdl, "Failed wq page allocation\n"); goto wq_allocate_page_err; } } wqs->free_blocks = kzalloc(WQS_FREE_BLOCKS_SIZE(wqs), GFP_KERNEL); if (!wqs->free_blocks) { err = -ENOMEM; goto alloc_blocks_err; } init_wqs_blocks_arr(wqs); return 0; alloc_blocks_err: wq_allocate_page_err: for (i = 0; i < page_idx; i++) wqs_free_page(wqs, i); free_page_addr(wqs); return err; } void hinic_wqs_free(struct hinic_wqs *wqs) { u32 page_idx; for (page_idx = 0; page_idx < wqs->num_pages; page_idx++) wqs_free_page(wqs, page_idx); free_page_addr(wqs); kfree(wqs->free_blocks); } static void copy_wqe_to_shadow(struct hinic_wq *wq, void *shadow_addr, int num_wqebbs, u16 prod_idx) { u8 *shadow_wqebb_addr, *wqe_page_addr, *wqebb_addr; u32 i, offset; u16 idx; for (i = 0; i < (u32)num_wqebbs; i++) { offset = i * wq->wqebb_size; shadow_wqebb_addr = (u8 *)shadow_addr + offset; idx = MASKED_WQE_IDX(wq, prod_idx + i); wqe_page_addr = WQ_PAGE_ADDR(wq, idx); wqebb_addr = wqe_page_addr + WQE_PAGE_OFF(wq, MASKED_WQE_IDX(wq, idx)); memcpy(shadow_wqebb_addr, wqebb_addr, wq->wqebb_size); } } static void copy_wqe_from_shadow(struct hinic_wq *wq, void *shadow_addr, int num_wqebbs, u16 prod_idx) { u8 *shadow_wqebb_addr, *wqe_page_addr, *wqebb_addr; u32 i, offset; u16 idx; for (i = 0; i < (u32)num_wqebbs; i++) { offset = i * wq->wqebb_size; shadow_wqebb_addr = (u8 *)shadow_addr + offset; idx = MASKED_WQE_IDX(wq, prod_idx + i); wqe_page_addr = WQ_PAGE_ADDR(wq, idx); wqebb_addr = wqe_page_addr + WQE_PAGE_OFF(wq, MASKED_WQE_IDX(wq, idx)); memcpy(wqebb_addr, shadow_wqebb_addr, wq->wqebb_size); } } void *hinic_get_wqebb_addr(struct hinic_wq *wq, u16 index) { return WQ_PAGE_ADDR(wq, index) + WQE_PAGE_OFF(wq, index); } u64 hinic_get_first_wqe_page_addr(struct hinic_wq *wq) { return be64_to_cpu(*wq->block_vaddr); } void *hinic_get_wqe(struct hinic_wq *wq, int num_wqebbs, u16 *prod_idx) { u32 curr_pg, end_pg; u16 curr_prod_idx, end_prod_idx; if (atomic_sub_return(num_wqebbs, &wq->delta) < 0) { atomic_add(num_wqebbs, &wq->delta); return NULL; } /* use original cur_pi and end_pi, no need queue depth mask as * WQE_PAGE_NUM will do num_queue_pages mask */ curr_prod_idx = (u16)wq->prod_idx; wq->prod_idx += num_wqebbs; /* end prod index should points to the last wqebb of wqe, * therefore minus 1 */ end_prod_idx = (u16)wq->prod_idx - 1; curr_pg = WQE_PAGE_NUM(wq, curr_prod_idx); end_pg = WQE_PAGE_NUM(wq, end_prod_idx); *prod_idx = MASKED_WQE_IDX(wq, curr_prod_idx); /* If we only have one page, still need to get shadown wqe when * wqe rolling-over page */ if (curr_pg != end_pg || MASKED_WQE_IDX(wq, end_prod_idx) < *prod_idx) { u32 offset = curr_pg * wq->max_wqe_size; u8 *shadow_addr = wq->shadow_wqe + offset; wq->shadow_idx[curr_pg] = *prod_idx; return shadow_addr; } return WQ_PAGE_ADDR(wq, *prod_idx) + WQE_PAGE_OFF(wq, *prod_idx); } void hinic_put_wqe(struct hinic_wq *wq, int num_wqebbs) { atomic_add(num_wqebbs, &wq->delta); wq->cons_idx += num_wqebbs; } void *hinic_read_wqe(struct hinic_wq *wq, int num_wqebbs, u16 *cons_idx) { u32 curr_pg, end_pg; u16 curr_cons_idx, end_cons_idx; if ((atomic_read(&wq->delta) + num_wqebbs) > wq->q_depth) return NULL; curr_cons_idx = (u16)wq->cons_idx; curr_cons_idx = MASKED_WQE_IDX(wq, curr_cons_idx); end_cons_idx = MASKED_WQE_IDX(wq, curr_cons_idx + num_wqebbs - 1); curr_pg = WQE_PAGE_NUM(wq, curr_cons_idx); end_pg = WQE_PAGE_NUM(wq, end_cons_idx); *cons_idx = curr_cons_idx; if (curr_pg != end_pg) { u32 offset = curr_pg * wq->max_wqe_size; u8 *shadow_addr = wq->shadow_wqe + offset; copy_wqe_to_shadow(wq, shadow_addr, num_wqebbs, *cons_idx); return shadow_addr; } return WQ_PAGE_ADDR(wq, *cons_idx) + WQE_PAGE_OFF(wq, *cons_idx); } static inline int wqe_shadow(struct hinic_wq *wq, const void *wqe) { void *end_wqe_shadow_addr; u32 wqe_shadow_size = wq->num_q_pages * wq->max_wqe_size; end_wqe_shadow_addr = &wq->shadow_wqe[wqe_shadow_size]; return WQE_IN_RANGE(wqe, wq->shadow_wqe, end_wqe_shadow_addr); } void hinic_write_wqe(struct hinic_wq *wq, void *wqe, int num_wqebbs) { u16 curr_pg; u16 prod_idx; if (wqe_shadow(wq, wqe)) { curr_pg = WQE_SHADOW_PAGE(wq, wqe); prod_idx = wq->shadow_idx[curr_pg]; copy_wqe_from_shadow(wq, wqe, num_wqebbs, prod_idx); } } hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/hinic_wq.h000066400000000000000000000054261502027332500211100ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef HINIC_WQ_H #define HINIC_WQ_H struct hinic_free_block { u32 page_idx; u32 block_idx; }; struct hinic_wq { /* The addresses are 64 bit in the HW */ u64 block_paddr; u64 *shadow_block_vaddr; u64 *block_vaddr; u32 wqebb_size; u32 wq_page_size; u16 q_depth; u32 max_wqe_size; u32 num_wqebbs_per_page; /* performance: replace mul/div as shift; * num_wqebbs_per_page must be power of 2 */ u32 wqebbs_per_page_shift; u32 page_idx; u32 block_idx; u32 num_q_pages; struct hinic_dma_addr_align *mem_align; int cons_idx; int prod_idx; atomic_t delta; u16 mask; u8 *shadow_wqe; u16 *shadow_idx; }; struct hinic_cmdq_pages { /* The addresses are 64 bit in the HW */ u64 cmdq_page_paddr; u64 *cmdq_page_vaddr; u64 *cmdq_shadow_page_vaddr; void *dev_hdl; }; struct hinic_wqs { /* The addresses are 64 bit in the HW */ u64 *page_paddr; u64 **page_vaddr; u64 **shadow_page_vaddr; struct hinic_free_block *free_blocks; u32 alloc_blk_pos; u32 return_blk_pos; int num_free_blks; /* for allocate blocks */ spinlock_t alloc_blocks_lock; u32 num_pages; void *dev_hdl; }; void hinic_wq_wqe_pg_clear(struct hinic_wq *wq); int hinic_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages, struct hinic_wq *wq, void *dev_hdl, int cmdq_blocks, u32 wq_page_size, u32 wqebb_size, u16 q_depth, u32 max_wqe_size); void hinic_cmdq_free(struct hinic_cmdq_pages *cmdq_pages, struct hinic_wq *wq, int cmdq_blocks); int hinic_wqs_alloc(struct hinic_wqs *wqs, int num_wqs, void *dev_hdl); void hinic_wqs_free(struct hinic_wqs *wqs); int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq, u32 wqebb_size, u32 wq_page_size, u16 q_depth, u32 max_wqe_size); void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq); void *hinic_get_wqebb_addr(struct hinic_wq *wq, u16 index); u64 hinic_get_first_wqe_page_addr(struct hinic_wq *wq); void *hinic_get_wqe(struct hinic_wq *wq, int num_wqebbs, u16 *prod_idx); void hinic_put_wqe(struct hinic_wq *wq, int num_wqebbs); void *hinic_read_wqe(struct hinic_wq *wq, int num_wqebbs, u16 *cons_idx); void hinic_write_wqe(struct hinic_wq *wq, void *wqe, int num_wqebbs); #endif hinic-dkms-0+git20250605+6.6.0-95.0.0/hinic/ossl_knl.h000066400000000000000000000042771502027332500211360ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0*/ /* Huawei HiNIC PCI Express Linux driver * Copyright(c) 2017 Huawei Technologies Co., Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * */ #ifndef OSSL_KNL_H #define OSSL_KNL_H #include #include #include #include #include #include #include #include #include #include #include #include #define sdk_err(dev, format, ...) \ dev_err(dev, "[COMM]" format, ##__VA_ARGS__) #define sdk_warn(dev, format, ...) \ dev_warn(dev, "[COMM]" format, ##__VA_ARGS__) #define sdk_notice(dev, format, ...) \ dev_notice(dev, "[COMM]" format, ##__VA_ARGS__) #define sdk_info(dev, format, ...) \ dev_info(dev, "[COMM]" format, ##__VA_ARGS__) #define nic_err(dev, format, ...) \ dev_err(dev, "[NIC]" format, ##__VA_ARGS__) #define nic_warn(dev, format, ...) \ dev_warn(dev, "[NIC]" format, ##__VA_ARGS__) #define nic_notice(dev, format, ...) \ dev_notice(dev, "[NIC]" format, ##__VA_ARGS__) #define nic_info(dev, format, ...) \ dev_info(dev, "[NIC]" format, ##__VA_ARGS__) #define nicif_err(priv, type, dev, fmt, args...) \ netif_level(err, priv, type, dev, "[NIC]" fmt, ##args) #define nicif_warn(priv, type, dev, fmt, args...) \ netif_level(warn, priv, type, dev, "[NIC]" fmt, ##args) #define nicif_notice(priv, type, dev, fmt, args...) \ netif_level(notice, priv, type, dev, "[NIC]" fmt, ##args) #define nicif_info(priv, type, dev, fmt, args...) \ netif_level(info, priv, type, dev, "[NIC]" fmt, ##args) #define nicif_dbg(priv, type, dev, fmt, args...) \ netif_level(dbg, priv, type, dev, "[NIC]" fmt, ##args) #define tasklet_state(tasklet) ((tasklet)->state) #endif /* OSSL_KNL_H */