From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by dpdk.space (Postfix) with ESMTP id 14C13A0471 for ; Wed, 19 Jun 2019 18:15:14 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 8504B1D058; Wed, 19 Jun 2019 17:50:41 +0200 (CEST) Received: from huawei.com (szxga07-in.huawei.com [45.249.212.35]) by dpdk.org (Postfix) with ESMTP id 5539F1C588 for ; Wed, 19 Jun 2019 17:50:25 +0200 (CEST) Received: from DGGEMS403-HUB.china.huawei.com (unknown [172.30.72.59]) by Forcepoint Email with ESMTP id 165016D99F288F4AC00D for ; Wed, 19 Jun 2019 23:50:20 +0800 (CST) Received: from tester_149.localdomain (10.175.119.39) by DGGEMS403-HUB.china.huawei.com (10.3.19.203) with Microsoft SMTP Server id 14.3.439.0; Wed, 19 Jun 2019 23:50:13 +0800 From: Ziyang Xuan To: CC: , , , , , Ziyang Xuan Date: Thu, 20 Jun 2019 00:02:14 +0800 Message-ID: <1d0d1cb3bdd9f22e07063f36d7545d26e8d8b9df.1560958308.git.xuanziyang2@huawei.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.175.119.39] X-CFilter-Loop: Reflected Subject: [dpdk-dev] [PATCH v5 07/15] net/hinic/base: add code about hardware operation X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Add code for hardware operation, including configuration, query and so on. Signed-off-by: Ziyang Xuan --- drivers/net/hinic/base/hinic_pmd_cfg.c | 208 ++++ drivers/net/hinic/base/hinic_pmd_cfg.h | 145 +++ drivers/net/hinic/base/hinic_pmd_hwdev.c | 1414 ++++++++++++++++++++++ drivers/net/hinic/base/hinic_pmd_hwdev.h | 485 ++++++++ 4 files changed, 2252 insertions(+) create mode 100644 drivers/net/hinic/base/hinic_pmd_cfg.c create mode 100644 drivers/net/hinic/base/hinic_pmd_cfg.h create mode 100644 drivers/net/hinic/base/hinic_pmd_hwdev.c create mode 100644 drivers/net/hinic/base/hinic_pmd_hwdev.h diff --git a/drivers/net/hinic/base/hinic_pmd_cfg.c b/drivers/net/hinic/base/hinic_pmd_cfg.c new file mode 100644 index 000000000..61537b12c --- /dev/null +++ b/drivers/net/hinic/base/hinic_pmd_cfg.c @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Huawei Technologies Co., Ltd + */ + +#include "hinic_compat.h" +#include "hinic_pmd_hwdev.h" +#include "hinic_pmd_hwif.h" +#include "hinic_pmd_mgmt.h" +#include "hinic_pmd_eqs.h" +#include "hinic_pmd_cfg.h" + +bool hinic_support_nic(struct hinic_hwdev *hwdev, struct nic_service_cap *cap) +{ + if (!IS_NIC_TYPE(hwdev)) + return false; + + if (cap) + memcpy(cap, &hwdev->cfg_mgmt->svc_cap.nic_cap, sizeof(*cap)); + + return true; +} + +static void hinic_parse_shared_res_cap(struct service_cap *cap, + struct hinic_dev_cap *dev_cap, + __rte_unused 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; + + PMD_DRV_LOG(INFO, "Get share resource capability:"); + PMD_DRV_LOG(INFO, "host_pctxs: 0x%x, host_cctxs: 0x%x, host_scqs: 0x%x, host_srqs: 0x%x, host_mpts: 0x%x", + shared_cap->host_pctxs, shared_cap->host_cctxs, + shared_cap->host_scqs, shared_cap->host_srqs, + shared_cap->host_mpts); +} + +static void hinic_parse_l2nic_res_cap(struct service_cap *cap, + struct hinic_dev_cap *dev_cap, + enum func_type type) +{ + struct nic_service_cap *nic_cap = &cap->nic_cap; + + 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; + } 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; + } + + 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; + + PMD_DRV_LOG(INFO, "Get l2nic resource capability:"); + PMD_DRV_LOG(INFO, "max_sqs: 0x%x, max_rqs: 0x%x, vf_max_sqs: 0x%x, vf_max_rqs: 0x%x", + nic_cap->max_sqs, nic_cap->max_rqs, + nic_cap->vf_max_sqs, nic_cap->vf_max_rqs); +} + +u16 hinic_func_max_qnum(void *hwdev) +{ + struct hinic_hwdev *dev = hwdev; + + return dev->cfg_mgmt->svc_cap.max_sqs; +} + +int init_cfg_mgmt(struct hinic_hwdev *hwdev) +{ + struct cfg_mgmt_info *cfg_mgmt; + + cfg_mgmt = kzalloc(sizeof(*cfg_mgmt), GFP_KERNEL); + if (!cfg_mgmt) + return -ENOMEM; + + hwdev->cfg_mgmt = cfg_mgmt; + cfg_mgmt->hwdev = hwdev; + + return 0; +} + +void free_cfg_mgmt(struct hinic_hwdev *hwdev) +{ + kfree(hwdev->cfg_mgmt); + hwdev->cfg_mgmt = NULL; +} + +static void hinic_parse_pub_res_cap(struct service_cap *cap, + struct hinic_dev_cap *dev_cap, + enum func_type type) +{ + cap->host_id = dev_cap->host_id; + cap->ep_id = dev_cap->ep_id; + cap->max_cos_id = dev_cap->max_cos_id; + cap->er_id = dev_cap->er_id; + cap->port_id = dev_cap->port_id; + + 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; + cap->max_sqs = dev_cap->nic_max_sq + 1; + cap->max_rqs = dev_cap->nic_max_rq + 1; + } + + cap->chip_svc_type = CFG_SVC_NIC_BIT0; + cap->host_total_function = dev_cap->host_total_func; + cap->host_oq_id_mask_val = dev_cap->host_oq_id_mask_val; + + PMD_DRV_LOG(INFO, "Get public resource capability:"); + PMD_DRV_LOG(INFO, "host_id: 0x%x, ep_id: 0x%x, intr_type: 0x%x, max_cos_id: 0x%x, er_id: 0x%x, port_id: 0x%x", + cap->host_id, cap->ep_id, cap->intr_chip_en, + cap->max_cos_id, cap->er_id, cap->port_id); + PMD_DRV_LOG(INFO, "host_total_function: 0x%x, host_oq_id_mask_val: 0x%x, max_vf: 0x%x", + cap->host_total_function, cap->host_oq_id_mask_val, + cap->max_vf); + PMD_DRV_LOG(INFO, "pf_num: 0x%x, pf_id_start: 0x%x, vf_num: 0x%x, vf_id_start: 0x%x", + cap->pf_num, cap->pf_id_start, + cap->vf_num, cap->vf_id_start); +} + +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 */ + hinic_parse_pub_res_cap(cap, dev_cap, type); + + /* PPF managed dynamic resource */ + if (type == TYPE_PPF) + hinic_parse_shared_res_cap(cap, dev_cap, type); + + /* L2 NIC resource */ + if (IS_NIC_TYPE(dev)) + hinic_parse_l2nic_res_cap(cap, dev_cap, type); +} + +static int get_cap_from_fw(struct hinic_hwdev *dev, enum func_type type) +{ + int err; + u16 in_len, out_len; + struct hinic_dev_cap dev_cap; + + memset(&dev_cap, 0, sizeof(dev_cap)); + in_len = sizeof(dev_cap); + out_len = in_len; + dev_cap.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + err = hinic_msg_to_mgmt_sync(dev, HINIC_MOD_CFGM, HINIC_CFG_NIC_CAP, + &dev_cap, in_len, &dev_cap, &out_len, 0); + if (err || dev_cap.mgmt_msg_head.status || !out_len) { + PMD_DRV_LOG(ERR, "Get capability from FW failed, err: %d, status: %d, out_len: %d", + err, dev_cap.mgmt_msg_head.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) { + PMD_DRV_LOG(ERR, "Get PF/PPF capability failed"); + return err; + } + break; + default: + PMD_DRV_LOG(ERR, "Unsupported PCI function type"); + return -EINVAL; + } + + return 0; +} + +int hinic_init_capability(struct hinic_hwdev *hwdev) +{ + return get_dev_cap(hwdev); +} diff --git a/drivers/net/hinic/base/hinic_pmd_cfg.h b/drivers/net/hinic/base/hinic_pmd_cfg.h new file mode 100644 index 000000000..1741ca44a --- /dev/null +++ b/drivers/net/hinic/base/hinic_pmd_cfg.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Huawei Technologies Co., Ltd + */ + +#ifndef _HINIC_PMD_CFG_H_ +#define _HINIC_PMD_CFG_H_ + +#define CFG_MAX_CMD_TIMEOUT 8000 /* ms */ + +#define IS_NIC_TYPE(dev) \ + ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_NIC_BIT0) + +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 */ +}; + +struct nic_service_cap { + /* PF resources */ + u16 max_sqs; + u16 max_rqs; + + /* VF resources, VF obtain them through the MailBox mechanism from + * corresponding 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 */ +}; + +/* service type relates define */ +enum cfg_svc_type_en { + CFG_SVC_NIC_BIT0 = (1 << 0), +}; + +/* device capability */ +struct service_cap { + 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; + u8 intr_chip_en; + u8 max_cos_id; /* PF/VF's max cos id */ + u8 er_id; /* PF/VF's ER */ + u8 port_id; /* PF/VF's physical port */ + u8 max_vf; /* max VF number that PF supported */ + bool sf_en; /* stateful business status */ + u16 max_sqs; + u16 max_rqs; + + u32 pf_num; + u32 pf_id_start; + u32 vf_num; + u32 vf_id_start; + + struct host_shared_resource_cap shared_res_cap; /* shared capability */ + struct nic_service_cap nic_cap; /* NIC capability */ +}; + +struct cfg_mgmt_info { + struct hinic_hwdev *hwdev; + struct service_cap svc_cap; +}; + +struct hinic_dev_cap { + struct hinic_mgmt_msg_head mgmt_msg_head; + + /* 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 rsvd1; + 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 rsvd3; + + u32 rsvd4[50]; +}; + +/* Obtain service_cap.nic_cap.dev_nic_cap.max_sqs */ +u16 hinic_func_max_qnum(void *hwdev); + +int init_cfg_mgmt(struct hinic_hwdev *hwdev); + +void free_cfg_mgmt(struct hinic_hwdev *hwdev); + +int hinic_init_capability(struct hinic_hwdev *hwdev); + +bool hinic_support_nic(struct hinic_hwdev *hwdev, struct nic_service_cap *cap); + +#endif /* _HINIC_PMD_CFG_H_ */ diff --git a/drivers/net/hinic/base/hinic_pmd_hwdev.c b/drivers/net/hinic/base/hinic_pmd_hwdev.c new file mode 100644 index 000000000..4f70bafe5 --- /dev/null +++ b/drivers/net/hinic/base/hinic_pmd_hwdev.c @@ -0,0 +1,1414 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Huawei Technologies Co., Ltd + */ + +#include +#include +#include +#include + +#include "hinic_compat.h" +#include "hinic_csr.h" +#include "hinic_pmd_hwdev.h" +#include "hinic_pmd_hwif.h" +#include "hinic_pmd_wq.h" +#include "hinic_pmd_cmdq.h" +#include "hinic_pmd_mgmt.h" +#include "hinic_pmd_niccfg.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_FLR_TIMEOUT 1000 + +#define FFM_RECORD_NUM_MAX 32 + +#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 + +#define HINIC_HASH_FUNC rte_jhash +#define HINIC_HASH_KEY_LEN (sizeof(dma_addr_t)) +#define HINIC_HASH_FUNC_INIT_VAL 0 + +static const char *__hw_to_char_fec[HILINK_FEC_MAX_TYPE] = { + "RS-FEC", "BASE-FEC", "NO-FEC"}; + +static const char *__hw_to_char_port_type[LINK_PORT_MAX_TYPE] = { + "Unknown", "Fibre", "Electric", "Direct Attach Copper", "AOC", + "Back plane", "BaseT" +}; + +static const char *hinic_module_link_err[LINK_ERR_NUM] = { + "Unrecognized module", +}; + +static void * +hinic_dma_mem_zalloc(struct hinic_hwdev *hwdev, size_t size, + dma_addr_t *dma_handle, unsigned int flag, unsigned int align) +{ + int rc, alloc_cnt; + const struct rte_memzone *mz; + char z_name[RTE_MEMZONE_NAMESIZE]; + hash_sig_t sig; + rte_iova_t iova; + + if (dma_handle == NULL || 0 == size) + return NULL; + + alloc_cnt = rte_atomic32_add_return(&hwdev->os_dep.dma_alloc_cnt, 1); + snprintf(z_name, sizeof(z_name), "%s_%d", + hwdev->pcidev_hdl->name, alloc_cnt); + + mz = rte_memzone_reserve_aligned(z_name, size, SOCKET_ID_ANY, + flag, align); + if (!mz) { + PMD_DRV_LOG(ERR, "Alloc dma able memory failed, errno: %d, ma_name: %s, size: 0x%zx", + rte_errno, z_name, size); + return NULL; + } + + iova = mz->iova; + + /* check if phys_addr already exist */ + sig = HINIC_HASH_FUNC(&iova, HINIC_HASH_KEY_LEN, + HINIC_HASH_FUNC_INIT_VAL); + rc = rte_hash_lookup_with_hash(hwdev->os_dep.dma_addr_hash, + &iova, sig); + if (rc >= 0) { + PMD_DRV_LOG(ERR, "Dma addr: %p already in hash table, error: %d, mz_name: %s", + (void *)iova, rc, z_name); + goto phys_addr_hash_err; + } + + /* record paddr in hash table */ + rte_spinlock_lock(&hwdev->os_dep.dma_hash_lock); + rc = rte_hash_add_key_with_hash_data(hwdev->os_dep.dma_addr_hash, + &iova, sig, + (void *)(u64)mz); + rte_spinlock_unlock(&hwdev->os_dep.dma_hash_lock); + if (rc) { + PMD_DRV_LOG(ERR, "Insert dma addr: %p hash failed, error: %d, mz_name: %s", + (void *)iova, rc, z_name); + goto phys_addr_hash_err; + } + *dma_handle = iova; + memset(mz->addr, 0, size); + + return mz->addr; + +phys_addr_hash_err: + (void)rte_memzone_free(mz); + + return NULL; +} + +static void +hinic_dma_mem_free(struct hinic_hwdev *hwdev, size_t size, + void *virt, dma_addr_t phys) +{ + int rc; + struct rte_memzone *mz = NULL; + struct rte_hash *hash; + hash_sig_t sig; + + if (virt == NULL || phys == 0) + return; + + hash = hwdev->os_dep.dma_addr_hash; + sig = HINIC_HASH_FUNC(&phys, HINIC_HASH_KEY_LEN, + HINIC_HASH_FUNC_INIT_VAL); + rc = rte_hash_lookup_with_hash_data(hash, &phys, sig, (void **)&mz); + if (rc < 0) { + PMD_DRV_LOG(ERR, "Can not find phys_addr: %p, error: %d", + (void *)phys, rc); + return; + } + + if (virt != mz->addr || size > mz->len) { + PMD_DRV_LOG(ERR, "Match mz_info failed: " + "mz.name: %s, mz.phys: %p, mz.virt: %p, mz.len: %zu, " + "phys: %p, virt: %p, size: %zu", + mz->name, (void *)mz->iova, mz->addr, mz->len, + (void *)phys, virt, size); + } + + rte_spinlock_lock(&hwdev->os_dep.dma_hash_lock); + (void)rte_hash_del_key_with_hash(hash, &phys, sig); + rte_spinlock_unlock(&hwdev->os_dep.dma_hash_lock); + + (void)rte_memzone_free(mz); +} + +void *dma_zalloc_coherent(void *hwdev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + return hinic_dma_mem_zalloc(hwdev, size, dma_handle, flag, + RTE_CACHE_LINE_SIZE); +} + +void *dma_zalloc_coherent_aligned(void *hwdev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + return hinic_dma_mem_zalloc(hwdev, size, dma_handle, flag, + HINIC_PAGE_SIZE); +} + +void *dma_zalloc_coherent_aligned256k(void *hwdev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + return hinic_dma_mem_zalloc(hwdev, size, dma_handle, flag, + HINIC_PAGE_SIZE * 64); +} + +void dma_free_coherent(void *hwdev, size_t size, void *virt, dma_addr_t phys) +{ + hinic_dma_mem_free(hwdev, size, virt, phys); +} + +void dma_free_coherent_volatile(void *hwdev, size_t size, + volatile void *virt, dma_addr_t phys) +{ + int rc; + struct rte_memzone *mz = NULL; + struct hinic_hwdev *dev = hwdev; + struct rte_hash *hash; + hash_sig_t sig; + + if (virt == NULL || phys == 0) + return; + + hash = dev->os_dep.dma_addr_hash; + sig = HINIC_HASH_FUNC(&phys, HINIC_HASH_KEY_LEN, + HINIC_HASH_FUNC_INIT_VAL); + rc = rte_hash_lookup_with_hash_data(hash, &phys, sig, (void **)&mz); + if (rc < 0) { + PMD_DRV_LOG(ERR, "Can not find phys_addr: %p, error: %d", + (void *)phys, rc); + return; + } + + if (virt != mz->addr || size > mz->len) { + PMD_DRV_LOG(ERR, "Match mz_info failed: " + "mz.name:%s, mz.phys:%p, mz.virt:%p, mz.len:%zu, " + "phys:%p, virt:%p, size:%zu", + mz->name, (void *)mz->iova, mz->addr, mz->len, + (void *)phys, virt, size); + } + + rte_spinlock_lock(&dev->os_dep.dma_hash_lock); + (void)rte_hash_del_key_with_hash(hash, &phys, sig); + rte_spinlock_unlock(&dev->os_dep.dma_hash_lock); + + (void)rte_memzone_free(mz); +} + +struct dma_pool *dma_pool_create(const char *name, void *dev, + size_t size, size_t align, size_t boundary) +{ + struct pci_pool *pool; + + pool = rte_zmalloc(NULL, sizeof(*pool), HINIC_MEM_ALLOC_ALIGNE_MIN); + if (!pool) + return NULL; + + pool->inuse = 0; + pool->elem_size = size; + pool->align = align; + pool->boundary = boundary; + pool->hwdev = dev; + strncpy(pool->name, name, (sizeof(pool->name) - 1)); + + return pool; +} + +void dma_pool_destroy(struct dma_pool *pool) +{ + if (!pool) + return; + + if (pool->inuse != 0) { + PMD_DRV_LOG(ERR, "Leak memory, dma_pool:%s, inuse_count:%u", + pool->name, pool->inuse); + } + + rte_free(pool); +} + +void *dma_pool_alloc(struct pci_pool *pool, int flags, dma_addr_t *dma_addr) +{ + void *buf; + + buf = hinic_dma_mem_zalloc(pool->hwdev, pool->elem_size, + dma_addr, flags, (u32)pool->align); + if (buf) + pool->inuse++; + + return buf; +} + +void dma_pool_free(struct pci_pool *pool, void *vaddr, dma_addr_t dma) +{ + pool->inuse--; + hinic_dma_mem_free(pool->hwdev, pool->elem_size, vaddr, dma); +} + + + +#define HINIC_MAX_DMA_ENTRIES 8192 +int hinic_osdep_init(struct hinic_hwdev *hwdev) +{ + struct rte_hash_parameters dh_params = { 0 }; + struct rte_hash *paddr_hash = NULL; + + rte_atomic32_set(&hwdev->os_dep.dma_alloc_cnt, 0); + rte_spinlock_init(&hwdev->os_dep.dma_hash_lock); + + dh_params.name = hwdev->pcidev_hdl->name; + dh_params.entries = HINIC_MAX_DMA_ENTRIES; + dh_params.key_len = HINIC_HASH_KEY_LEN; + dh_params.hash_func = HINIC_HASH_FUNC; + dh_params.hash_func_init_val = HINIC_HASH_FUNC_INIT_VAL; + dh_params.socket_id = SOCKET_ID_ANY; + + paddr_hash = rte_hash_find_existing(dh_params.name); + if (paddr_hash == NULL) { + paddr_hash = rte_hash_create(&dh_params); + if (paddr_hash == NULL) { + PMD_DRV_LOG(ERR, "Create nic_dev phys_addr hash table failed"); + return -ENOMEM; + } + } else { + PMD_DRV_LOG(INFO, "Using existing dma hash table %s", + dh_params.name); + } + hwdev->os_dep.dma_addr_hash = paddr_hash; + + return 0; +} + +void hinic_osdep_deinit(struct hinic_hwdev *hwdev) +{ + uint32_t iter = 0; + dma_addr_t key_pa; + struct rte_memzone *data_mz = NULL; + struct rte_hash *paddr_hash = hwdev->os_dep.dma_addr_hash; + + if (paddr_hash) { + /* iterate through the hash table */ + while (rte_hash_iterate(paddr_hash, (const void **)&key_pa, + (void **)&data_mz, &iter) >= 0) { + if (data_mz) { + PMD_DRV_LOG(WARNING, "Free leaked dma_addr: %p, mz: %s", + (void *)key_pa, data_mz->name); + (void)rte_memzone_free(data_mz); + } + } + + /* free phys_addr hash table */ + rte_hash_free(paddr_hash); + } +} + + + + + + + + + + + + + +/** + * hinic_set_ci_table - set ci attribute table + * @hwdev: the hardware interface of a nic device + * @q_id: Queue id of SQ + * @attr: Point to SQ CI attribute table + * @return + * 0 on success and ci attribute table is filled, + * negative error value otherwise. + **/ +int hinic_set_ci_table(void *hwdev, u16 q_id, struct hinic_sq_attr *attr) +{ + struct hinic_cons_idx_attr cons_idx_attr; + + memset(&cons_idx_attr, 0, sizeof(cons_idx_attr)); + cons_idx_attr.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + cons_idx_attr.func_idx = hinic_global_func_id(hwdev); + 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; + + return hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, + HINIC_MGMT_CMD_L2NIC_SQ_CI_ATTR_SET, + &cons_idx_attr, sizeof(cons_idx_attr), + NULL, NULL, 0); +} + +/** + * hinic_set_pagesize - set page size to vat table + * @hwdev: the hardware interface of a nic device + * @page_size: vat page size + * @return + * 0 on success, + * negative error value otherwise. + **/ +int hinic_set_pagesize(void *hwdev, u8 page_size) +{ + struct hinic_page_size cmd; + + if (page_size > HINIC_PAGE_SIZE_MAX) { + PMD_DRV_LOG(ERR, "Invalid page_size %u, bigger than %u", + page_size, HINIC_PAGE_SIZE_MAX); + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + cmd.func_idx = hinic_global_func_id(hwdev); + cmd.ppf_idx = hinic_ppf_idx(hwdev); + cmd.page_size = page_size; + + return hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, + HINIC_MGMT_CMD_PAGESIZE_SET, + &cmd, sizeof(cmd), + NULL, NULL, 0); +} + +static int wait_for_flr_finish(struct hinic_hwif *hwif) +{ + unsigned long end; + enum hinic_pf_status status; + + end = jiffies + msecs_to_jiffies(HINIC_FLR_TIMEOUT); + do { + 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; + } + + rte_delay_ms(10); + } while (time_before(jiffies, end)); + + return -EFAULT; +} + +#define HINIC_WAIT_CMDQ_IDLE_TIMEOUT 1000 + +static int wait_cmdq_stop(struct hinic_hwdev *hwdev) +{ + enum hinic_cmdq_type cmdq_type; + struct hinic_cmdqs *cmdqs = hwdev->cmdqs; + unsigned long end; + int err = 0; + + if (!(cmdqs->status & HINIC_CMDQ_ENABLE)) + return 0; + + cmdqs->status &= ~HINIC_CMDQ_ENABLE; + + end = jiffies + msecs_to_jiffies(HINIC_WAIT_CMDQ_IDLE_TIMEOUT); + do { + 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; + + rte_delay_ms(1); + } while (time_before(jiffies, end)); + + cmdqs->status |= HINIC_CMDQ_ENABLE; + + return err; +} + +/** + * hinic_pf_rx_tx_flush - clean up hardware resource + * @hwdev: the hardware interface of a nic device + * @return + * 0 on success, + * negative error value otherwise. + **/ +static int hinic_pf_rx_tx_flush(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_clear_doorbell clear_db; + struct hinic_clear_resource clr_res; + int err; + + rte_delay_ms(100); + + err = wait_cmdq_stop(hwdev); + if (err) { + PMD_DRV_LOG(ERR, "Cmdq is still working"); + return err; + } + + hinic_disable_doorbell(hwif); + memset(&clear_db, 0, sizeof(clear_db)); + clear_db.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + clear_db.func_idx = HINIC_HWIF_GLOBAL_IDX(hwif); + 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), NULL, NULL, 0); + if (err) + PMD_DRV_LOG(WARNING, "Flush doorbell failed"); + + hinic_set_pf_status(hwif, HINIC_PF_STATUS_FLR_START_FLAG); + memset(&clr_res, 0, sizeof(clr_res)); + clr_res.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + clr_res.func_idx = HINIC_HWIF_GLOBAL_IDX(hwif); + 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), NULL, NULL); + if (err) + PMD_DRV_LOG(WARNING, "Notice flush message failed"); + + err = wait_for_flr_finish(hwif); + if (err) + PMD_DRV_LOG(WARNING, "Wait firmware FLR timeout"); + + hinic_enable_doorbell(hwif); + + err = hinic_reinit_cmdq_ctxts(hwdev); + if (err) + PMD_DRV_LOG(WARNING, "Reinit cmdq failed"); + + return 0; +} + +int hinic_func_rx_tx_flush(struct hinic_hwdev *hwdev) +{ + return hinic_pf_rx_tx_flush(hwdev); +} + +/** + * hinic_get_interrupt_cfg - get interrupt configuration from NIC + * @hwdev: the hardware interface of a nic device + * @interrupt_info: Information of Interrupt aggregation + * Return: 0 on success, negative error value otherwise. + **/ +static int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev, + struct nic_interrupt_info *interrupt_info) +{ + struct hinic_msix_config msix_cfg; + u16 out_size = sizeof(msix_cfg); + int err; + + memset(&msix_cfg, 0, sizeof(msix_cfg)); + msix_cfg.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + msix_cfg.func_id = hinic_global_func_id(hwdev); + 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.mgmt_msg_head.status) { + PMD_DRV_LOG(ERR, "Get interrupt config failed, ret: %d", + msix_cfg.mgmt_msg_head.status); + 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; +} + +/** + * hinic_set_interrupt_cfg - set interrupt configuration to NIC + * @hwdev: the hardware interface of a nic device + * @interrupt_info: Information of Interrupt aggregation + * Return: 0 on success, negative error value otherwise. + **/ +int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev, + struct nic_interrupt_info interrupt_info) +{ + struct hinic_msix_config msix_cfg; + struct nic_interrupt_info temp_info; + u16 out_size = sizeof(msix_cfg); + int err; + + memset(&msix_cfg, 0, sizeof(msix_cfg)); + msix_cfg.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + msix_cfg.func_id = hinic_global_func_id(hwdev); + msix_cfg.msix_index = (u16)interrupt_info.msix_index; + + temp_info.msix_index = interrupt_info.msix_index; + + err = hinic_get_interrupt_cfg(hwdev, &temp_info); + if (err) + return -EINVAL; + + msix_cfg.lli_credit_cnt = temp_info.lli_credit_limit; + msix_cfg.lli_tmier_cnt = temp_info.lli_timer_cfg; + msix_cfg.pending_cnt = temp_info.pending_limt; + msix_cfg.coalesct_timer_cnt = temp_info.coalesc_timer_cfg; + msix_cfg.resend_timer_cnt = temp_info.resend_timer_cfg; + + if (interrupt_info.lli_set) { + msix_cfg.lli_credit_cnt = interrupt_info.lli_credit_limit; + msix_cfg.lli_tmier_cnt = interrupt_info.lli_timer_cfg; + } + + if (interrupt_info.interrupt_coalesc_set) { + 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.mgmt_msg_head.status) { + PMD_DRV_LOG(ERR, "Set interrupt config failed, ret: %d", + msix_cfg.mgmt_msg_head.status); + return -EINVAL; + } + + return 0; +} + +/** + * init_aeqs_msix_attr - Init interrupt attributes of aeq + * @hwdev: the hardware interface of a nic device + * @return + * 0 on success, + * negative error value otherwise. + **/ +int init_aeqs_msix_attr(void *hwdev) +{ + struct hinic_hwdev *nic_hwdev = hwdev; + struct hinic_aeqs *aeqs = nic_hwdev->aeqs; + 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 < aeqs->num_aeqs; q_id++) { + eq = &aeqs->aeq[q_id]; + info.msix_index = eq->eq_irq.msix_entry_idx; + err = hinic_set_interrupt_cfg(hwdev, info); + if (err) { + PMD_DRV_LOG(ERR, "Set msix attr for aeq %d failed", + q_id); + return -EFAULT; + } + } + + return 0; +} + +/** + * set_pf_dma_attr_entry - set the dma attributes for entry + * @hwdev: the pointer to the private hardware device object + * @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); +} + +/** + * dma_attr_table_init - initialize the the default dma attributes + * @hwdev: the pointer to the private hardware device object + **/ +static void dma_attr_table_init(struct hinic_hwdev *hwdev) +{ + if (HINIC_IS_VF(hwdev)) + return; + + 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); +} + +int hinic_init_attr_table(struct hinic_hwdev *hwdev) +{ + dma_attr_table_init(hwdev); + + return init_aeqs_msix_attr(hwdev); +} + +#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"}; + char fault_level[FAULT_LEVEL_MAX][FAULT_SHOW_STR_LEN + 1] = { + "fatal", "reset", "flr", "general", "suggestion"}; + char type_str[FAULT_SHOW_STR_LEN + 1] = { 0 }; + char level_str[FAULT_SHOW_STR_LEN + 1] = { 0 }; + u8 err_level; + + PMD_DRV_LOG(WARNING, "Fault event report received, func_id: %d", + hinic_global_func_id(hwdev)); + + if (event->type < FAULT_TYPE_MAX) + strncpy(type_str, fault_type[event->type], FAULT_SHOW_STR_LEN); + else + strncpy(type_str, "unknown", FAULT_SHOW_STR_LEN); + PMD_DRV_LOG(WARNING, "fault type: %d [%s]", + event->type, type_str); + PMD_DRV_LOG(WARNING, "fault val[0]: 0x%08x", + event->event.val[0]); + PMD_DRV_LOG(WARNING, "fault val[1]: 0x%08x", + event->event.val[1]); + PMD_DRV_LOG(WARNING, "fault val[2]: 0x%08x", + event->event.val[2]); + PMD_DRV_LOG(WARNING, "fault val[3]: 0x%08x", + event->event.val[3]); + + switch (event->type) { + case FAULT_TYPE_CHIP: + err_level = event->event.chip.err_level; + if (err_level < FAULT_LEVEL_MAX) + strncpy(level_str, fault_level[err_level], + FAULT_SHOW_STR_LEN); + else + strncpy(level_str, "unknown", + FAULT_SHOW_STR_LEN); + + PMD_DRV_LOG(WARNING, "err_level: %d [%s]", + err_level, level_str); + + if (err_level == FAULT_LEVEL_SERIOUS_FLR) { + PMD_DRV_LOG(WARNING, "flr func_id: %d", + event->event.chip.func_id); + } else { + PMD_DRV_LOG(WARNING, "node_id: %d", + event->event.chip.node_id); + PMD_DRV_LOG(WARNING, "err_type: %d", + event->event.chip.err_type); + PMD_DRV_LOG(WARNING, "err_csr_addr: %d", + event->event.chip.err_csr_addr); + PMD_DRV_LOG(WARNING, "err_csr_value: %d", + event->event.chip.err_csr_value); + } + break; + case FAULT_TYPE_UCODE: + PMD_DRV_LOG(WARNING, "cause_id: %d", + event->event.ucode.cause_id); + PMD_DRV_LOG(WARNING, "core_id: %d", + event->event.ucode.core_id); + PMD_DRV_LOG(WARNING, "c_id: %d", + event->event.ucode.c_id); + PMD_DRV_LOG(WARNING, "epc: %d", + event->event.ucode.epc); + break; + case FAULT_TYPE_MEM_RD_TIMEOUT: + case FAULT_TYPE_MEM_WR_TIMEOUT: + PMD_DRV_LOG(WARNING, "err_csr_ctrl: %d", + event->event.mem_timeout.err_csr_ctrl); + PMD_DRV_LOG(WARNING, "err_csr_data: %d", + event->event.mem_timeout.err_csr_data); + PMD_DRV_LOG(WARNING, "ctrl_tab: %d", + event->event.mem_timeout.ctrl_tab); + PMD_DRV_LOG(WARNING, "mem_index: %d", + event->event.mem_timeout.mem_index); + break; + case FAULT_TYPE_REG_RD_TIMEOUT: + case FAULT_TYPE_REG_WR_TIMEOUT: + PMD_DRV_LOG(WARNING, "err_csr: %d", + event->event.reg_timeout.err_csr); + break; + default: + break; + } +} + +static int resources_state_set(struct hinic_hwdev *hwdev, + enum hinic_res_state state) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_cmd_set_res_state res_state; + + memset(&res_state, 0, sizeof(res_state)); + res_state.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + res_state.func_idx = HINIC_HWIF_GLOBAL_IDX(hwif); + res_state.state = state; + + return hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, + HINIC_MGMT_CMD_RES_STATE_SET, + &res_state, sizeof(res_state), NULL, NULL, 0); +} + +/** + * hinic_activate_hwdev_state - Active host nic state and notify mgmt channel + * that host nic is ready. + * @hwdev: the hardware interface of a nic device + * @return + * 0 on success, + * negative error value otherwise. + **/ +int hinic_activate_hwdev_state(struct hinic_hwdev *hwdev) +{ + int rc = HINIC_OK; + + if (!hwdev) + return -EINVAL; + + if (!HINIC_IS_VF(hwdev)) + hinic_set_pf_status(hwdev->hwif, + HINIC_PF_STATUS_ACTIVE_FLAG); + + rc = resources_state_set(hwdev, HINIC_RES_ACTIVE); + if (rc) { + PMD_DRV_LOG(ERR, "Initialize resources state failed"); + return rc; + } + + return 0; +} + +/** + * hinic_deactivate_hwdev_state - Deactivate host nic state and notify mgmt + * channel that host nic is not ready. + * @hwdev: the pointer to the private hardware device object + **/ +void hinic_deactivate_hwdev_state(struct hinic_hwdev *hwdev) +{ + int rc = HINIC_OK; + + if (!hwdev) + return; + + rc = resources_state_set(hwdev, HINIC_RES_CLEAN); + if (rc) + PMD_DRV_LOG(ERR, "Deinit resources state failed"); + + if (!HINIC_IS_VF(hwdev)) + hinic_set_pf_status(hwdev->hwif, HINIC_PF_STATUS_INIT); +} + +int hinic_get_board_info(void *hwdev, struct hinic_board_info *info) +{ + struct hinic_comm_board_info board_info; + u16 out_size = sizeof(board_info); + int err; + + if (!hwdev || !info) + return -EINVAL; + + memset(&board_info, 0, sizeof(board_info)); + board_info.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + 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.mgmt_msg_head.status || !out_size) { + PMD_DRV_LOG(ERR, "Failed to get board info, err: %d, status: 0x%x, out size: 0x%x", + err, board_info.mgmt_msg_head.status, out_size); + return -EFAULT; + } + + memcpy(info, &board_info.info, sizeof(*info)); + return 0; +} + +/** + * hinic_l2nic_reset - Restore the initial state of NIC + * @hwdev: the hardware interface of a nic device + * @return + * 0 on success, + * negative error value otherwise. + **/ +int hinic_l2nic_reset(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_l2nic_reset l2nic_reset; + int err = 0; + + err = hinic_set_vport_enable(hwdev, false); + if (err) { + PMD_DRV_LOG(ERR, "Set vport disable failed"); + return err; + } + + rte_delay_ms(100); + + memset(&l2nic_reset, 0, sizeof(l2nic_reset)); + l2nic_reset.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + l2nic_reset.func_id = HINIC_HWIF_GLOBAL_IDX(hwif); + err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, + HINIC_MGMT_CMD_L2NIC_RESET, + &l2nic_reset, sizeof(l2nic_reset), + NULL, NULL, 0); + if (err || l2nic_reset.mgmt_msg_head.status) { + PMD_DRV_LOG(ERR, "Reset L2NIC resources failed"); + return -EFAULT; + } + + return 0; +} + +static void +hinic_show_sw_watchdog_timeout_info(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)) { + PMD_DRV_LOG(ERR, "Invalid mgmt watchdog report, length: %d, should be %zu", + in_size, sizeof(*watchdog_info)); + return; + } + + watchdog_info = (struct hinic_mgmt_watchdog_info *)buf_in; + + PMD_DRV_LOG(ERR, "Mgmt deadloop time: 0x%x 0x%x, task id: 0x%x, sp: 0x%x", + watchdog_info->curr_time_h, watchdog_info->curr_time_l, + watchdog_info->task_id, watchdog_info->sp); + PMD_DRV_LOG(ERR, "Stack current used: 0x%x, peak used: 0x%x, overflow flag: 0x%x, top: 0x%x, bottom: 0x%x", + watchdog_info->curr_used, watchdog_info->peak_used, + watchdog_info->is_overflow, watchdog_info->stack_top, + watchdog_info->stack_bottom); + + PMD_DRV_LOG(ERR, "Mgmt pc: 0x%08x, lr: 0x%08x, cpsr:0x%08x", + watchdog_info->pc, watchdog_info->lr, watchdog_info->cpsr); + + PMD_DRV_LOG(ERR, "Mgmt register info"); + + for (i = 0; i < 3; i++) { + reg = watchdog_info->reg + (u64)(u32)(4 * i); + PMD_DRV_LOG(ERR, "0x%08x 0x%08x 0x%08x 0x%08x", + *(reg), *(reg + 1), *(reg + 2), *(reg + 3)); + } + + PMD_DRV_LOG(ERR, "0x%08x", watchdog_info->reg[12]); + + if (watchdog_info->stack_actlen <= 1024) { + stack_len = watchdog_info->stack_actlen; + } else { + PMD_DRV_LOG(ERR, "Oops stack length: 0x%x is wrong", + watchdog_info->stack_actlen); + stack_len = 1024; + } + + PMD_DRV_LOG(ERR, "Mgmt dump stack, 16Bytes per line(start from sp)"); + for (i = 0; i < (stack_len / 16); i++) { + dump_addr = (u32 *)(watchdog_info->data + ((u64)(u32)(i * 16))); + PMD_DRV_LOG(ERR, "0x%08x 0x%08x 0x%08x 0x%08x", + *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))); + PMD_DRV_LOG(ERR, "0x%08x", *dump_addr); + } + + *out_size = sizeof(*watchdog_info); + watchdog_info = (struct hinic_mgmt_watchdog_info *)buf_out; + watchdog_info->mgmt_msg_head.status = 0; +} + +static void hinic_show_pcie_dfx_info(struct hinic_hwdev *hwdev, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_pcie_dfx_ntc *notice_info = + (struct hinic_pcie_dfx_ntc *)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)) { + PMD_DRV_LOG(ERR, "Invalid pcie dfx notice info, length: %d, should be %zu.", + in_size, sizeof(*notice_info)); + return; + } + + ((struct hinic_pcie_dfx_ntc *)buf_out)->mgmt_msg_head.status = 0; + *out_size = sizeof(*notice_info); + memset(&dfx_info, 0, sizeof(dfx_info)); + num = (u32)(notice_info->len / 1024); + PMD_DRV_LOG(INFO, "INFO LEN: %d", notice_info->len); + PMD_DRV_LOG(INFO, "PCIE DFX:"); + dfx_info.host_id = 0; + dfx_info.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + 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.mgmt_msg_head.status || !size) { + PMD_DRV_LOG(ERR, "Failed to get pcie dfx info, err: %d, status: 0x%x, out size: 0x%x", + err, dfx_info.mgmt_msg_head.status, size); + return; + } + + reg = (u32 *)dfx_info.data; + for (j = 0; j < 256; j = j + 8) { + PMD_DRV_LOG(ERR, "0x%04x: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", + 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)]); + cnt = cnt + 32; + } + memset(dfx_info.data, 0, MAX_PCIE_DFX_BUF_SIZE); + } +} + +static void +hinic_show_ffm_info(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size) +{ + struct ffm_intr_info *intr; + + if (in_size != sizeof(struct ffm_intr_info)) { + PMD_DRV_LOG(ERR, "Invalid input buffer len, length: %d, should be %zu.", + in_size, sizeof(struct ffm_intr_info)); + return; + } + + if (hwdev->ffm_num < FFM_RECORD_NUM_MAX) { + hwdev->ffm_num++; + intr = (struct ffm_intr_info *)buf_in; + PMD_DRV_LOG(WARNING, "node_id(%d),err_csr_addr(0x%x),err_csr_val(0x%x),err_level(0x%x),err_type(0x%x)", + intr->node_id, + intr->err_csr_addr, + intr->err_csr_value, + intr->err_level, + intr->err_type); + } +} + +void hinic_comm_async_event_handle(struct hinic_hwdev *hwdev, u8 cmd, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_cmd_fault_event *fault_event, *ret_fault_event; + + if (!hwdev) + return; + + *out_size = 0; + + switch (cmd) { + case HINIC_MGMT_CMD_FAULT_REPORT: + if (in_size != sizeof(*fault_event)) { + PMD_DRV_LOG(ERR, "Invalid fault event report, length: %d, should be %zu", + in_size, sizeof(*fault_event)); + return; + } + + fault_event = (struct hinic_cmd_fault_event *)buf_in; + fault_report_show(hwdev, &fault_event->event); + + if (hinic_func_type(hwdev) != TYPE_VF) { + ret_fault_event = + (struct hinic_cmd_fault_event *)buf_out; + ret_fault_event->mgmt_msg_head.status = 0; + *out_size = sizeof(*ret_fault_event); + } + break; + + case HINIC_MGMT_CMD_WATCHDOG_INFO: + hinic_show_sw_watchdog_timeout_info(buf_in, in_size, + buf_out, out_size); + break; + + case HINIC_MGMT_CMD_PCIE_DFX_NTC: + hinic_show_pcie_dfx_info(hwdev, buf_in, in_size, + buf_out, out_size); + break; + + case HINIC_MGMT_CMD_FFM_SET: + hinic_show_ffm_info(hwdev, buf_in, in_size); + break; + + default: + break; + } +} + +static void +hinic_cable_status_event(u8 cmd, void *buf_in, __rte_unused u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_cable_plug_event *plug_event; + struct hinic_link_err_event *link_err; + + if (cmd == HINIC_PORT_CMD_CABLE_PLUG_EVENT) { + plug_event = (struct hinic_cable_plug_event *)buf_in; + PMD_DRV_LOG(INFO, "Port module event: Cable %s", + plug_event->plugged ? "plugged" : "unplugged"); + + *out_size = sizeof(*plug_event); + plug_event = (struct hinic_cable_plug_event *)buf_out; + plug_event->mgmt_msg_head.status = 0; + } else if (cmd == HINIC_PORT_CMD_LINK_ERR_EVENT) { + link_err = (struct hinic_link_err_event *)buf_in; + if (link_err->err_type >= LINK_ERR_NUM) { + PMD_DRV_LOG(ERR, "Link failed, Unknown type: 0x%x", + link_err->err_type); + } else { + PMD_DRV_LOG(INFO, "Link failed, type: 0x%x: %s", + link_err->err_type, + hinic_module_link_err[link_err->err_type]); + } + + *out_size = sizeof(*link_err); + link_err = (struct hinic_link_err_event *)buf_out; + link_err->mgmt_msg_head.status = 0; + } +} + +static int hinic_link_event_process(struct hinic_hwdev *hwdev, + struct rte_eth_dev *eth_dev, u8 status) +{ + uint32_t port_speed[LINK_SPEED_MAX] = {ETH_SPEED_NUM_10M, + ETH_SPEED_NUM_100M, ETH_SPEED_NUM_1G, + ETH_SPEED_NUM_10G, ETH_SPEED_NUM_25G, + ETH_SPEED_NUM_40G, ETH_SPEED_NUM_100G}; + struct nic_port_info port_info; + struct rte_eth_link link; + int rc = HINIC_OK; + + if (!status) { + link.link_status = ETH_LINK_DOWN; + link.link_speed = 0; + link.link_duplex = ETH_LINK_HALF_DUPLEX; + link.link_autoneg = ETH_LINK_FIXED; + } else { + link.link_status = ETH_LINK_UP; + + memset(&port_info, 0, sizeof(port_info)); + rc = hinic_get_port_info(hwdev, &port_info); + if (rc) { + link.link_speed = ETH_SPEED_NUM_NONE; + link.link_duplex = ETH_LINK_FULL_DUPLEX; + link.link_autoneg = ETH_LINK_FIXED; + } else { + link.link_speed = port_speed[port_info.speed % + LINK_SPEED_MAX]; + link.link_duplex = port_info.duplex; + link.link_autoneg = port_info.autoneg_state; + } + } + (void)rte_eth_linkstatus_set(eth_dev, &link); + + return rc; +} + +static void hinic_lsc_process(struct hinic_hwdev *hwdev, + struct rte_eth_dev *rte_dev, u8 status) +{ + int ret; + + ret = hinic_link_event_process(hwdev, rte_dev, status); + /* check if link has changed, notify callback */ + if (ret == 0) + _rte_eth_dev_callback_process(rte_dev, + RTE_ETH_EVENT_INTR_LSC, + NULL); +} + +void hinic_l2nic_async_event_handle(struct hinic_hwdev *hwdev, + void *param, u8 cmd, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_port_link_status *in_link; + struct rte_eth_dev *eth_dev; + + if (!hwdev) + return; + + *out_size = 0; + + switch (cmd) { + case HINIC_PORT_CMD_LINK_STATUS_REPORT: + eth_dev = param; + in_link = (struct hinic_port_link_status *)buf_in; + PMD_DRV_LOG(INFO, "Link status event report, dev_name: %s, port_id: %d, link_status: %s", + eth_dev->data->name, eth_dev->data->port_id, + in_link->link ? "UP" : "DOWN"); + + hinic_lsc_process(hwdev, eth_dev, in_link->link); + break; + + case HINIC_PORT_CMD_CABLE_PLUG_EVENT: + case HINIC_PORT_CMD_LINK_ERR_EVENT: + hinic_cable_status_event(cmd, buf_in, in_size, + buf_out, out_size); + break; + + case HINIC_PORT_CMD_MGMT_RESET: + PMD_DRV_LOG(WARNING, "Mgmt is reset"); + break; + + default: + PMD_DRV_LOG(ERR, "Unsupported event %d to process", + cmd); + break; + } +} + +static void print_cable_info(struct hinic_link_info *info) +{ + char tmp_str[512] = {0}; + char tmp_vendor[17] = {0}; + const char *port_type = "Unknown port type"; + int i; + + if (info->cable_absent) { + PMD_DRV_LOG(INFO, "Cable unpresent"); + return; + } + + if (info->port_type < LINK_PORT_MAX_TYPE) + port_type = __hw_to_char_port_type[info->port_type]; + else + PMD_DRV_LOG(INFO, "Unknown port type: %u", + 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)); + snprintf(tmp_str, (sizeof(tmp_str) - 1), + "Vendor: %s, %s, %s, length: %um, max_speed: %uGbps", + tmp_vendor, info->sfp_type ? "SFP" : "QSFP", port_type, + info->cable_length, info->cable_max_speed); + if (info->port_type != LINK_PORT_COPPER) + snprintf(tmp_str + strlen(tmp_str), (sizeof(tmp_str) - 1), + "%s, Temperature: %u", tmp_str, + info->cable_temp); + + PMD_DRV_LOG(INFO, "Cable information: %s", tmp_str); +} + +static void print_hi30_status(struct hinic_link_info *info) +{ + struct hi30_ffe_data *ffe_data; + struct hi30_ctle_data *ctle_data; + + ffe_data = (struct hi30_ffe_data *)info->hi30_ffe; + ctle_data = (struct hi30_ctle_data *)info->hi30_ctle; + + PMD_DRV_LOG(INFO, "TX_FFE: PRE2=%s%d; PRE1=%s%d; MAIN=%d; POST1=%s%d; POST1X=%s%d", + (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)); + PMD_DRV_LOG(INFO, "RX_CTLE: Gain1~3=%u %u %u; Boost1~3=%u %u %u; Zero1~3=%u %u %u; Squelch1~3=%u %u %u", + 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_link_info(struct hinic_link_info *info, + enum hilink_info_print_event type) +{ + const char *fec = "None"; + + if (info->fec < HILINK_FEC_MAX_TYPE) + fec = __hw_to_char_fec[info->fec]; + else + PMD_DRV_LOG(INFO, "Unknown fec type: %u", + info->fec); + + if (type == HILINK_EVENT_LINK_UP || !info->an_state) { + PMD_DRV_LOG(INFO, "Link information: speed %dGbps, %s, autoneg %s", + info->speed, fec, info->an_state ? "on" : "off"); + } else { + PMD_DRV_LOG(INFO, "Link information: antoneg: %s", + info->an_state ? "on" : "off"); + } +} + +static const char *hilink_info_report_type[HILINK_EVENT_MAX_TYPE] = { + "", "link up", "link down", "cable plugged" +}; + +static void hinic_print_hilink_info(void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_hilink_link_info *hilink_info = + (struct hinic_hilink_link_info *)buf_in; + struct hinic_link_info *info; + enum hilink_info_print_event type; + + if (in_size != sizeof(*hilink_info)) { + PMD_DRV_LOG(ERR, "Invalid hilink info message size %d, should be %zu", + in_size, sizeof(*hilink_info)); + return; + } + + ((struct hinic_hilink_link_info *)buf_out)->mgmt_msg_head.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) { + PMD_DRV_LOG(INFO, "Invalid hilink info report, type: %d", + type); + return; + } + + PMD_DRV_LOG(INFO, "Hilink info report after %s", + hilink_info_report_type[type]); + + print_cable_info(info); + + print_link_info(info, type); + + print_hi30_status(info); + + if (type == HILINK_EVENT_LINK_UP) + return; + + if (type == HILINK_EVENT_CABLE_PLUGGED) { + PMD_DRV_LOG(INFO, "alos: %u, rx_los: %u", + info->alos, info->rx_los); + return; + } + + PMD_DRV_LOG(INFO, "PMA ctrl: %s, MAC tx %s, MAC rx %s, PMA debug inforeg: 0x%x, PMA signal ok reg: 0x%x, RF/LF status reg: 0x%x", + info->pma_status ? "on" : "off", + 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); + PMD_DRV_LOG(INFO, "alos: %u, rx_los: %u, PCS block counter reg: 0x%x,PCS link: 0x%x, MAC link: 0x%x PCS_err_cnt: 0x%x", + info->alos, info->rx_los, info->pcs_err_blk_cnt_reg, + info->pcs_link_reg, info->mac_link_reg, info->pcs_err_cnt); +} + +void hinic_hilink_async_event_handle(struct hinic_hwdev *hwdev, u8 cmd, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + if (!hwdev) + return; + + *out_size = 0; + + switch (cmd) { + case HINIC_HILINK_CMD_GET_LINK_INFO: + hinic_print_hilink_info(buf_in, in_size, buf_out, + out_size); + break; + + default: + PMD_DRV_LOG(ERR, "Unsupported event %d to process", + cmd); + break; + } +} diff --git a/drivers/net/hinic/base/hinic_pmd_hwdev.h b/drivers/net/hinic/base/hinic_pmd_hwdev.h new file mode 100644 index 000000000..6c21c475f --- /dev/null +++ b/drivers/net/hinic/base/hinic_pmd_hwdev.h @@ -0,0 +1,485 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Huawei Technologies Co., Ltd + */ + +#ifndef _HINIC_PMD_HWDEV_H_ +#define _HINIC_PMD_HWDEV_H_ + +#include "hinic_pmd_cmd.h" + +#define HINIC_PAGE_SIZE_MAX 20 + +#define HINIC_MGMT_CMD_UNSUPPORTED 0xFF +#define HINIC_PF_SET_VF_ALREADY 0x4 + +#define MAX_PCIE_DFX_BUF_SIZE 1024 + +/* dma pool */ +struct dma_pool { + u32 inuse; + size_t elem_size; + size_t align; + size_t boundary; + void *hwdev; + + char name[32]; +}; + +enum hinic_res_state { + HINIC_RES_CLEAN = 0, + HINIC_RES_ACTIVE = 1, +}; + +enum hilink_info_print_event { + HILINK_EVENT_LINK_UP = 1, + HILINK_EVENT_LINK_DOWN, + HILINK_EVENT_CABLE_PLUGGED, + HILINK_EVENT_MAX_TYPE, +}; + +struct hinic_port_link_status { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_id; + u8 link; + u8 port_id; +}; + +enum link_err_status { + LINK_ERR_MODULE_UNRECOGENIZED, + LINK_ERR_NUM, +}; + +struct hinic_cable_plug_event { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_id; + u8 plugged; /* 0: unplugged, 1: plugged */ + u8 port_id; +}; + +struct hinic_link_err_event { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_id; + u8 err_type; + u8 port_id; +}; + +struct hinic_cons_idx_attr { + struct hinic_mgmt_msg_head mgmt_msg_head; + + 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 { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_idx; + u8 ppf_idx; + u8 rsvd1; +}; + +struct hinic_clear_resource { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_idx; + u8 ppf_idx; + u8 rsvd1; +}; + +struct hinic_cmd_set_res_state { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_idx; + u8 state; + u8 rsvd1; + u32 rsvd2; +}; + +struct hinic_l2nic_reset { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_id; + u16 rsvd1; +}; + +struct hinic_page_size { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_idx; + u8 ppf_idx; + u8 page_size; + u32 rsvd; +}; + +struct hinic_msix_config { + struct hinic_mgmt_msg_head mgmt_msg_head; + + 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]; +}; + +/* 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_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 +}; + +/* defined by chip */ +struct hinic_fault_event { + /* enum hinic_fault_type */ + u8 type; + u8 rsvd0[3]; + union { + u32 val[4]; + /* valid only type==FAULT_TYPE_CHIP */ + struct { + u8 node_id; + /* enum hinic_fault_err_level */ + u8 err_level; + u8 err_type; + u8 rsvd1; + 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; + } event; +}; + +struct hinic_cmd_fault_event { + struct hinic_mgmt_msg_head mgmt_msg_head; + + struct hinic_fault_event event; +}; + +struct hinic_mgmt_watchdog_info { + struct hinic_mgmt_msg_head mgmt_msg_head; + + 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_pcie_dfx_ntc { + struct hinic_mgmt_msg_head mgmt_msg_head; + + int len; + u32 rsvd; +}; + +struct hinic_pcie_dfx_info { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u8 host_id; + u8 last; + u8 rsvd[2]; + u32 offset; + + u8 data[MAX_PCIE_DFX_BUF_SIZE]; +}; + +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; +}; + +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; +}; + +struct hinic_comm_board_info { + struct hinic_mgmt_msg_head mgmt_msg_head; + + struct hinic_board_info info; + + u32 rsvd1[5]; +}; + +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_ffe_data { + u8 PRE2; + u8 PRE1; + u8 POST1; + u8 POST2; + u8 MAIN; +}; + +enum hilink_fec_type { + HILINK_FEC_RSFEC, + HILINK_FEC_BASEFEC, + HILINK_FEC_NOFEC, + HILINK_FEC_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, +}; + +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; + + u8 lane_used; + u8 hi30_ffe[5]; + u8 hi30_ctle[19]; + u8 hi30_dfe[14]; + u8 rsvd4; +}; + +struct hinic_hilink_link_info { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 port_id; + u8 info_type; /* 1: link up 2: link down 3 cable plugged */ + u8 rsvd1; + + struct hinic_link_info info; + + u8 rsvd2[780]; +}; + +/* dma os dependency implementation */ +struct hinic_os_dep { + /* kernel dma alloc api */ + rte_atomic32_t dma_alloc_cnt; + rte_spinlock_t dma_hash_lock; + struct rte_hash *dma_addr_hash; +}; + +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; +}; + +struct hinic_sq_attr { + u8 dma_attr_off; + u8 pending_limit; + u8 coalescing_time; + u8 intr_en; + u16 intr_idx; + u32 l2nic_sqn; + /* bit[63:2] is addr's high 62bit, bit[0] is valid flag */ + u64 ci_dma_base; +}; + +struct hinic_hwdev { + struct rte_pci_device *pcidev_hdl; + u32 ffm_num; + + /* dma memory allocator */ + struct hinic_os_dep os_dep; + struct hinic_hwif *hwif; + struct cfg_mgmt_info *cfg_mgmt; + struct hinic_aeqs *aeqs; + struct hinic_msg_pf_to_mgmt *pf_to_mgmt; + struct hinic_cmdqs *cmdqs; + struct hinic_nic_io *nic_io; + +}; + +int hinic_osdep_init(struct hinic_hwdev *hwdev); + +void hinic_osdep_deinit(struct hinic_hwdev *hwdev); + +void dma_free_coherent_volatile(void *hwdev, size_t size, + volatile void *virt, dma_addr_t phys); + +int hinic_get_board_info(void *hwdev, struct hinic_board_info *info); + +int hinic_set_ci_table(void *hwdev, u16 q_id, struct hinic_sq_attr *attr); + +int hinic_func_rx_tx_flush(struct hinic_hwdev *hwdev); + +int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev, + struct nic_interrupt_info interrupt_info); + +int init_aeqs_msix_attr(void *hwdev); + +void hinic_comm_async_event_handle(struct hinic_hwdev *hwdev, u8 cmd, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size); + +void hinic_l2nic_async_event_handle(struct hinic_hwdev *hwdev, void *param, + u8 cmd, void *buf_in, u16 in_size, + void *buf_out, u16 *out_size); + +void hinic_hilink_async_event_handle(struct hinic_hwdev *hwdev, u8 cmd, + void *buf_in, u16 in_size, void *buf_out, + u16 *out_size); + +int hinic_init_attr_table(struct hinic_hwdev *hwdev); + +int hinic_activate_hwdev_state(struct hinic_hwdev *hwdev); + +void hinic_deactivate_hwdev_state(struct hinic_hwdev *hwdev); + +int hinic_l2nic_reset(struct hinic_hwdev *hwdev); + +int hinic_set_pagesize(void *hwdev, u8 page_size); + +#endif /* _HINIC_PMD_HWDEV_H_ */ -- 2.18.0 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by dpdk.space (Postfix) with ESMTP id D9CC5A0471 for ; Wed, 19 Jun 2019 18:21:18 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 489771C52F; Wed, 19 Jun 2019 18:11:29 +0200 (CEST) Received: from huawei.com (szxga06-in.huawei.com [45.249.212.32]) by dpdk.org (Postfix) with ESMTP id E80341C51F for ; Wed, 19 Jun 2019 18:11:08 +0200 (CEST) Received: from DGGEMS414-HUB.china.huawei.com (unknown [172.30.72.60]) by Forcepoint Email with ESMTP id 0F17A336CA08B35090E6 for ; Wed, 19 Jun 2019 23:46:06 +0800 (CST) Received: from tester_149.localdomain (10.175.119.39) by DGGEMS414-HUB.china.huawei.com (10.3.19.214) with Microsoft SMTP Server id 14.3.439.0; Wed, 19 Jun 2019 23:45:58 +0800 From: Ziyang Xuan To: CC: , , , , , Ziyang Xuan Date: Wed, 19 Jun 2019 23:58:01 +0800 Message-ID: <1d0d1cb3bdd9f22e07063f36d7545d26e8d8b9df.1560958308.git.xuanziyang2@huawei.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" X-Originating-IP: [10.175.119.39] X-CFilter-Loop: Reflected Subject: [dpdk-dev] [PATCH v5 07/15] net/hinic/base: add code about hardware operation X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Message-ID: <20190619155801.7o_WGJ0nk6Ea6mC--CF-n9WWen0AsdoXrSIHoJXbc8I@z> Add code for hardware operation, including configuration, query and so on. Signed-off-by: Ziyang Xuan --- drivers/net/hinic/base/hinic_pmd_cfg.c | 208 ++++ drivers/net/hinic/base/hinic_pmd_cfg.h | 145 +++ drivers/net/hinic/base/hinic_pmd_hwdev.c | 1414 ++++++++++++++++++++++ drivers/net/hinic/base/hinic_pmd_hwdev.h | 485 ++++++++ 4 files changed, 2252 insertions(+) create mode 100644 drivers/net/hinic/base/hinic_pmd_cfg.c create mode 100644 drivers/net/hinic/base/hinic_pmd_cfg.h create mode 100644 drivers/net/hinic/base/hinic_pmd_hwdev.c create mode 100644 drivers/net/hinic/base/hinic_pmd_hwdev.h diff --git a/drivers/net/hinic/base/hinic_pmd_cfg.c b/drivers/net/hinic/base/hinic_pmd_cfg.c new file mode 100644 index 000000000..61537b12c --- /dev/null +++ b/drivers/net/hinic/base/hinic_pmd_cfg.c @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Huawei Technologies Co., Ltd + */ + +#include "hinic_compat.h" +#include "hinic_pmd_hwdev.h" +#include "hinic_pmd_hwif.h" +#include "hinic_pmd_mgmt.h" +#include "hinic_pmd_eqs.h" +#include "hinic_pmd_cfg.h" + +bool hinic_support_nic(struct hinic_hwdev *hwdev, struct nic_service_cap *cap) +{ + if (!IS_NIC_TYPE(hwdev)) + return false; + + if (cap) + memcpy(cap, &hwdev->cfg_mgmt->svc_cap.nic_cap, sizeof(*cap)); + + return true; +} + +static void hinic_parse_shared_res_cap(struct service_cap *cap, + struct hinic_dev_cap *dev_cap, + __rte_unused 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; + + PMD_DRV_LOG(INFO, "Get share resource capability:"); + PMD_DRV_LOG(INFO, "host_pctxs: 0x%x, host_cctxs: 0x%x, host_scqs: 0x%x, host_srqs: 0x%x, host_mpts: 0x%x", + shared_cap->host_pctxs, shared_cap->host_cctxs, + shared_cap->host_scqs, shared_cap->host_srqs, + shared_cap->host_mpts); +} + +static void hinic_parse_l2nic_res_cap(struct service_cap *cap, + struct hinic_dev_cap *dev_cap, + enum func_type type) +{ + struct nic_service_cap *nic_cap = &cap->nic_cap; + + 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; + } 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; + } + + 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; + + PMD_DRV_LOG(INFO, "Get l2nic resource capability:"); + PMD_DRV_LOG(INFO, "max_sqs: 0x%x, max_rqs: 0x%x, vf_max_sqs: 0x%x, vf_max_rqs: 0x%x", + nic_cap->max_sqs, nic_cap->max_rqs, + nic_cap->vf_max_sqs, nic_cap->vf_max_rqs); +} + +u16 hinic_func_max_qnum(void *hwdev) +{ + struct hinic_hwdev *dev = hwdev; + + return dev->cfg_mgmt->svc_cap.max_sqs; +} + +int init_cfg_mgmt(struct hinic_hwdev *hwdev) +{ + struct cfg_mgmt_info *cfg_mgmt; + + cfg_mgmt = kzalloc(sizeof(*cfg_mgmt), GFP_KERNEL); + if (!cfg_mgmt) + return -ENOMEM; + + hwdev->cfg_mgmt = cfg_mgmt; + cfg_mgmt->hwdev = hwdev; + + return 0; +} + +void free_cfg_mgmt(struct hinic_hwdev *hwdev) +{ + kfree(hwdev->cfg_mgmt); + hwdev->cfg_mgmt = NULL; +} + +static void hinic_parse_pub_res_cap(struct service_cap *cap, + struct hinic_dev_cap *dev_cap, + enum func_type type) +{ + cap->host_id = dev_cap->host_id; + cap->ep_id = dev_cap->ep_id; + cap->max_cos_id = dev_cap->max_cos_id; + cap->er_id = dev_cap->er_id; + cap->port_id = dev_cap->port_id; + + 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; + cap->max_sqs = dev_cap->nic_max_sq + 1; + cap->max_rqs = dev_cap->nic_max_rq + 1; + } + + cap->chip_svc_type = CFG_SVC_NIC_BIT0; + cap->host_total_function = dev_cap->host_total_func; + cap->host_oq_id_mask_val = dev_cap->host_oq_id_mask_val; + + PMD_DRV_LOG(INFO, "Get public resource capability:"); + PMD_DRV_LOG(INFO, "host_id: 0x%x, ep_id: 0x%x, intr_type: 0x%x, max_cos_id: 0x%x, er_id: 0x%x, port_id: 0x%x", + cap->host_id, cap->ep_id, cap->intr_chip_en, + cap->max_cos_id, cap->er_id, cap->port_id); + PMD_DRV_LOG(INFO, "host_total_function: 0x%x, host_oq_id_mask_val: 0x%x, max_vf: 0x%x", + cap->host_total_function, cap->host_oq_id_mask_val, + cap->max_vf); + PMD_DRV_LOG(INFO, "pf_num: 0x%x, pf_id_start: 0x%x, vf_num: 0x%x, vf_id_start: 0x%x", + cap->pf_num, cap->pf_id_start, + cap->vf_num, cap->vf_id_start); +} + +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 */ + hinic_parse_pub_res_cap(cap, dev_cap, type); + + /* PPF managed dynamic resource */ + if (type == TYPE_PPF) + hinic_parse_shared_res_cap(cap, dev_cap, type); + + /* L2 NIC resource */ + if (IS_NIC_TYPE(dev)) + hinic_parse_l2nic_res_cap(cap, dev_cap, type); +} + +static int get_cap_from_fw(struct hinic_hwdev *dev, enum func_type type) +{ + int err; + u16 in_len, out_len; + struct hinic_dev_cap dev_cap; + + memset(&dev_cap, 0, sizeof(dev_cap)); + in_len = sizeof(dev_cap); + out_len = in_len; + dev_cap.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + err = hinic_msg_to_mgmt_sync(dev, HINIC_MOD_CFGM, HINIC_CFG_NIC_CAP, + &dev_cap, in_len, &dev_cap, &out_len, 0); + if (err || dev_cap.mgmt_msg_head.status || !out_len) { + PMD_DRV_LOG(ERR, "Get capability from FW failed, err: %d, status: %d, out_len: %d", + err, dev_cap.mgmt_msg_head.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) { + PMD_DRV_LOG(ERR, "Get PF/PPF capability failed"); + return err; + } + break; + default: + PMD_DRV_LOG(ERR, "Unsupported PCI function type"); + return -EINVAL; + } + + return 0; +} + +int hinic_init_capability(struct hinic_hwdev *hwdev) +{ + return get_dev_cap(hwdev); +} diff --git a/drivers/net/hinic/base/hinic_pmd_cfg.h b/drivers/net/hinic/base/hinic_pmd_cfg.h new file mode 100644 index 000000000..1741ca44a --- /dev/null +++ b/drivers/net/hinic/base/hinic_pmd_cfg.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Huawei Technologies Co., Ltd + */ + +#ifndef _HINIC_PMD_CFG_H_ +#define _HINIC_PMD_CFG_H_ + +#define CFG_MAX_CMD_TIMEOUT 8000 /* ms */ + +#define IS_NIC_TYPE(dev) \ + ((dev)->cfg_mgmt->svc_cap.chip_svc_type & CFG_SVC_NIC_BIT0) + +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 */ +}; + +struct nic_service_cap { + /* PF resources */ + u16 max_sqs; + u16 max_rqs; + + /* VF resources, VF obtain them through the MailBox mechanism from + * corresponding 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 */ +}; + +/* service type relates define */ +enum cfg_svc_type_en { + CFG_SVC_NIC_BIT0 = (1 << 0), +}; + +/* device capability */ +struct service_cap { + 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; + u8 intr_chip_en; + u8 max_cos_id; /* PF/VF's max cos id */ + u8 er_id; /* PF/VF's ER */ + u8 port_id; /* PF/VF's physical port */ + u8 max_vf; /* max VF number that PF supported */ + bool sf_en; /* stateful business status */ + u16 max_sqs; + u16 max_rqs; + + u32 pf_num; + u32 pf_id_start; + u32 vf_num; + u32 vf_id_start; + + struct host_shared_resource_cap shared_res_cap; /* shared capability */ + struct nic_service_cap nic_cap; /* NIC capability */ +}; + +struct cfg_mgmt_info { + struct hinic_hwdev *hwdev; + struct service_cap svc_cap; +}; + +struct hinic_dev_cap { + struct hinic_mgmt_msg_head mgmt_msg_head; + + /* 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 rsvd1; + 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 rsvd3; + + u32 rsvd4[50]; +}; + +/* Obtain service_cap.nic_cap.dev_nic_cap.max_sqs */ +u16 hinic_func_max_qnum(void *hwdev); + +int init_cfg_mgmt(struct hinic_hwdev *hwdev); + +void free_cfg_mgmt(struct hinic_hwdev *hwdev); + +int hinic_init_capability(struct hinic_hwdev *hwdev); + +bool hinic_support_nic(struct hinic_hwdev *hwdev, struct nic_service_cap *cap); + +#endif /* _HINIC_PMD_CFG_H_ */ diff --git a/drivers/net/hinic/base/hinic_pmd_hwdev.c b/drivers/net/hinic/base/hinic_pmd_hwdev.c new file mode 100644 index 000000000..4f70bafe5 --- /dev/null +++ b/drivers/net/hinic/base/hinic_pmd_hwdev.c @@ -0,0 +1,1414 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Huawei Technologies Co., Ltd + */ + +#include +#include +#include +#include + +#include "hinic_compat.h" +#include "hinic_csr.h" +#include "hinic_pmd_hwdev.h" +#include "hinic_pmd_hwif.h" +#include "hinic_pmd_wq.h" +#include "hinic_pmd_cmdq.h" +#include "hinic_pmd_mgmt.h" +#include "hinic_pmd_niccfg.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_FLR_TIMEOUT 1000 + +#define FFM_RECORD_NUM_MAX 32 + +#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 + +#define HINIC_HASH_FUNC rte_jhash +#define HINIC_HASH_KEY_LEN (sizeof(dma_addr_t)) +#define HINIC_HASH_FUNC_INIT_VAL 0 + +static const char *__hw_to_char_fec[HILINK_FEC_MAX_TYPE] = { + "RS-FEC", "BASE-FEC", "NO-FEC"}; + +static const char *__hw_to_char_port_type[LINK_PORT_MAX_TYPE] = { + "Unknown", "Fibre", "Electric", "Direct Attach Copper", "AOC", + "Back plane", "BaseT" +}; + +static const char *hinic_module_link_err[LINK_ERR_NUM] = { + "Unrecognized module", +}; + +static void * +hinic_dma_mem_zalloc(struct hinic_hwdev *hwdev, size_t size, + dma_addr_t *dma_handle, unsigned int flag, unsigned int align) +{ + int rc, alloc_cnt; + const struct rte_memzone *mz; + char z_name[RTE_MEMZONE_NAMESIZE]; + hash_sig_t sig; + rte_iova_t iova; + + if (dma_handle == NULL || 0 == size) + return NULL; + + alloc_cnt = rte_atomic32_add_return(&hwdev->os_dep.dma_alloc_cnt, 1); + snprintf(z_name, sizeof(z_name), "%s_%d", + hwdev->pcidev_hdl->name, alloc_cnt); + + mz = rte_memzone_reserve_aligned(z_name, size, SOCKET_ID_ANY, + flag, align); + if (!mz) { + PMD_DRV_LOG(ERR, "Alloc dma able memory failed, errno: %d, ma_name: %s, size: 0x%zx", + rte_errno, z_name, size); + return NULL; + } + + iova = mz->iova; + + /* check if phys_addr already exist */ + sig = HINIC_HASH_FUNC(&iova, HINIC_HASH_KEY_LEN, + HINIC_HASH_FUNC_INIT_VAL); + rc = rte_hash_lookup_with_hash(hwdev->os_dep.dma_addr_hash, + &iova, sig); + if (rc >= 0) { + PMD_DRV_LOG(ERR, "Dma addr: %p already in hash table, error: %d, mz_name: %s", + (void *)iova, rc, z_name); + goto phys_addr_hash_err; + } + + /* record paddr in hash table */ + rte_spinlock_lock(&hwdev->os_dep.dma_hash_lock); + rc = rte_hash_add_key_with_hash_data(hwdev->os_dep.dma_addr_hash, + &iova, sig, + (void *)(u64)mz); + rte_spinlock_unlock(&hwdev->os_dep.dma_hash_lock); + if (rc) { + PMD_DRV_LOG(ERR, "Insert dma addr: %p hash failed, error: %d, mz_name: %s", + (void *)iova, rc, z_name); + goto phys_addr_hash_err; + } + *dma_handle = iova; + memset(mz->addr, 0, size); + + return mz->addr; + +phys_addr_hash_err: + (void)rte_memzone_free(mz); + + return NULL; +} + +static void +hinic_dma_mem_free(struct hinic_hwdev *hwdev, size_t size, + void *virt, dma_addr_t phys) +{ + int rc; + struct rte_memzone *mz = NULL; + struct rte_hash *hash; + hash_sig_t sig; + + if (virt == NULL || phys == 0) + return; + + hash = hwdev->os_dep.dma_addr_hash; + sig = HINIC_HASH_FUNC(&phys, HINIC_HASH_KEY_LEN, + HINIC_HASH_FUNC_INIT_VAL); + rc = rte_hash_lookup_with_hash_data(hash, &phys, sig, (void **)&mz); + if (rc < 0) { + PMD_DRV_LOG(ERR, "Can not find phys_addr: %p, error: %d", + (void *)phys, rc); + return; + } + + if (virt != mz->addr || size > mz->len) { + PMD_DRV_LOG(ERR, "Match mz_info failed: " + "mz.name: %s, mz.phys: %p, mz.virt: %p, mz.len: %zu, " + "phys: %p, virt: %p, size: %zu", + mz->name, (void *)mz->iova, mz->addr, mz->len, + (void *)phys, virt, size); + } + + rte_spinlock_lock(&hwdev->os_dep.dma_hash_lock); + (void)rte_hash_del_key_with_hash(hash, &phys, sig); + rte_spinlock_unlock(&hwdev->os_dep.dma_hash_lock); + + (void)rte_memzone_free(mz); +} + +void *dma_zalloc_coherent(void *hwdev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + return hinic_dma_mem_zalloc(hwdev, size, dma_handle, flag, + RTE_CACHE_LINE_SIZE); +} + +void *dma_zalloc_coherent_aligned(void *hwdev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + return hinic_dma_mem_zalloc(hwdev, size, dma_handle, flag, + HINIC_PAGE_SIZE); +} + +void *dma_zalloc_coherent_aligned256k(void *hwdev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + return hinic_dma_mem_zalloc(hwdev, size, dma_handle, flag, + HINIC_PAGE_SIZE * 64); +} + +void dma_free_coherent(void *hwdev, size_t size, void *virt, dma_addr_t phys) +{ + hinic_dma_mem_free(hwdev, size, virt, phys); +} + +void dma_free_coherent_volatile(void *hwdev, size_t size, + volatile void *virt, dma_addr_t phys) +{ + int rc; + struct rte_memzone *mz = NULL; + struct hinic_hwdev *dev = hwdev; + struct rte_hash *hash; + hash_sig_t sig; + + if (virt == NULL || phys == 0) + return; + + hash = dev->os_dep.dma_addr_hash; + sig = HINIC_HASH_FUNC(&phys, HINIC_HASH_KEY_LEN, + HINIC_HASH_FUNC_INIT_VAL); + rc = rte_hash_lookup_with_hash_data(hash, &phys, sig, (void **)&mz); + if (rc < 0) { + PMD_DRV_LOG(ERR, "Can not find phys_addr: %p, error: %d", + (void *)phys, rc); + return; + } + + if (virt != mz->addr || size > mz->len) { + PMD_DRV_LOG(ERR, "Match mz_info failed: " + "mz.name:%s, mz.phys:%p, mz.virt:%p, mz.len:%zu, " + "phys:%p, virt:%p, size:%zu", + mz->name, (void *)mz->iova, mz->addr, mz->len, + (void *)phys, virt, size); + } + + rte_spinlock_lock(&dev->os_dep.dma_hash_lock); + (void)rte_hash_del_key_with_hash(hash, &phys, sig); + rte_spinlock_unlock(&dev->os_dep.dma_hash_lock); + + (void)rte_memzone_free(mz); +} + +struct dma_pool *dma_pool_create(const char *name, void *dev, + size_t size, size_t align, size_t boundary) +{ + struct pci_pool *pool; + + pool = rte_zmalloc(NULL, sizeof(*pool), HINIC_MEM_ALLOC_ALIGNE_MIN); + if (!pool) + return NULL; + + pool->inuse = 0; + pool->elem_size = size; + pool->align = align; + pool->boundary = boundary; + pool->hwdev = dev; + strncpy(pool->name, name, (sizeof(pool->name) - 1)); + + return pool; +} + +void dma_pool_destroy(struct dma_pool *pool) +{ + if (!pool) + return; + + if (pool->inuse != 0) { + PMD_DRV_LOG(ERR, "Leak memory, dma_pool:%s, inuse_count:%u", + pool->name, pool->inuse); + } + + rte_free(pool); +} + +void *dma_pool_alloc(struct pci_pool *pool, int flags, dma_addr_t *dma_addr) +{ + void *buf; + + buf = hinic_dma_mem_zalloc(pool->hwdev, pool->elem_size, + dma_addr, flags, (u32)pool->align); + if (buf) + pool->inuse++; + + return buf; +} + +void dma_pool_free(struct pci_pool *pool, void *vaddr, dma_addr_t dma) +{ + pool->inuse--; + hinic_dma_mem_free(pool->hwdev, pool->elem_size, vaddr, dma); +} + + + +#define HINIC_MAX_DMA_ENTRIES 8192 +int hinic_osdep_init(struct hinic_hwdev *hwdev) +{ + struct rte_hash_parameters dh_params = { 0 }; + struct rte_hash *paddr_hash = NULL; + + rte_atomic32_set(&hwdev->os_dep.dma_alloc_cnt, 0); + rte_spinlock_init(&hwdev->os_dep.dma_hash_lock); + + dh_params.name = hwdev->pcidev_hdl->name; + dh_params.entries = HINIC_MAX_DMA_ENTRIES; + dh_params.key_len = HINIC_HASH_KEY_LEN; + dh_params.hash_func = HINIC_HASH_FUNC; + dh_params.hash_func_init_val = HINIC_HASH_FUNC_INIT_VAL; + dh_params.socket_id = SOCKET_ID_ANY; + + paddr_hash = rte_hash_find_existing(dh_params.name); + if (paddr_hash == NULL) { + paddr_hash = rte_hash_create(&dh_params); + if (paddr_hash == NULL) { + PMD_DRV_LOG(ERR, "Create nic_dev phys_addr hash table failed"); + return -ENOMEM; + } + } else { + PMD_DRV_LOG(INFO, "Using existing dma hash table %s", + dh_params.name); + } + hwdev->os_dep.dma_addr_hash = paddr_hash; + + return 0; +} + +void hinic_osdep_deinit(struct hinic_hwdev *hwdev) +{ + uint32_t iter = 0; + dma_addr_t key_pa; + struct rte_memzone *data_mz = NULL; + struct rte_hash *paddr_hash = hwdev->os_dep.dma_addr_hash; + + if (paddr_hash) { + /* iterate through the hash table */ + while (rte_hash_iterate(paddr_hash, (const void **)&key_pa, + (void **)&data_mz, &iter) >= 0) { + if (data_mz) { + PMD_DRV_LOG(WARNING, "Free leaked dma_addr: %p, mz: %s", + (void *)key_pa, data_mz->name); + (void)rte_memzone_free(data_mz); + } + } + + /* free phys_addr hash table */ + rte_hash_free(paddr_hash); + } +} + + + + + + + + + + + + + +/** + * hinic_set_ci_table - set ci attribute table + * @hwdev: the hardware interface of a nic device + * @q_id: Queue id of SQ + * @attr: Point to SQ CI attribute table + * @return + * 0 on success and ci attribute table is filled, + * negative error value otherwise. + **/ +int hinic_set_ci_table(void *hwdev, u16 q_id, struct hinic_sq_attr *attr) +{ + struct hinic_cons_idx_attr cons_idx_attr; + + memset(&cons_idx_attr, 0, sizeof(cons_idx_attr)); + cons_idx_attr.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + cons_idx_attr.func_idx = hinic_global_func_id(hwdev); + 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; + + return hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, + HINIC_MGMT_CMD_L2NIC_SQ_CI_ATTR_SET, + &cons_idx_attr, sizeof(cons_idx_attr), + NULL, NULL, 0); +} + +/** + * hinic_set_pagesize - set page size to vat table + * @hwdev: the hardware interface of a nic device + * @page_size: vat page size + * @return + * 0 on success, + * negative error value otherwise. + **/ +int hinic_set_pagesize(void *hwdev, u8 page_size) +{ + struct hinic_page_size cmd; + + if (page_size > HINIC_PAGE_SIZE_MAX) { + PMD_DRV_LOG(ERR, "Invalid page_size %u, bigger than %u", + page_size, HINIC_PAGE_SIZE_MAX); + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + cmd.func_idx = hinic_global_func_id(hwdev); + cmd.ppf_idx = hinic_ppf_idx(hwdev); + cmd.page_size = page_size; + + return hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, + HINIC_MGMT_CMD_PAGESIZE_SET, + &cmd, sizeof(cmd), + NULL, NULL, 0); +} + +static int wait_for_flr_finish(struct hinic_hwif *hwif) +{ + unsigned long end; + enum hinic_pf_status status; + + end = jiffies + msecs_to_jiffies(HINIC_FLR_TIMEOUT); + do { + 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; + } + + rte_delay_ms(10); + } while (time_before(jiffies, end)); + + return -EFAULT; +} + +#define HINIC_WAIT_CMDQ_IDLE_TIMEOUT 1000 + +static int wait_cmdq_stop(struct hinic_hwdev *hwdev) +{ + enum hinic_cmdq_type cmdq_type; + struct hinic_cmdqs *cmdqs = hwdev->cmdqs; + unsigned long end; + int err = 0; + + if (!(cmdqs->status & HINIC_CMDQ_ENABLE)) + return 0; + + cmdqs->status &= ~HINIC_CMDQ_ENABLE; + + end = jiffies + msecs_to_jiffies(HINIC_WAIT_CMDQ_IDLE_TIMEOUT); + do { + 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; + + rte_delay_ms(1); + } while (time_before(jiffies, end)); + + cmdqs->status |= HINIC_CMDQ_ENABLE; + + return err; +} + +/** + * hinic_pf_rx_tx_flush - clean up hardware resource + * @hwdev: the hardware interface of a nic device + * @return + * 0 on success, + * negative error value otherwise. + **/ +static int hinic_pf_rx_tx_flush(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_clear_doorbell clear_db; + struct hinic_clear_resource clr_res; + int err; + + rte_delay_ms(100); + + err = wait_cmdq_stop(hwdev); + if (err) { + PMD_DRV_LOG(ERR, "Cmdq is still working"); + return err; + } + + hinic_disable_doorbell(hwif); + memset(&clear_db, 0, sizeof(clear_db)); + clear_db.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + clear_db.func_idx = HINIC_HWIF_GLOBAL_IDX(hwif); + 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), NULL, NULL, 0); + if (err) + PMD_DRV_LOG(WARNING, "Flush doorbell failed"); + + hinic_set_pf_status(hwif, HINIC_PF_STATUS_FLR_START_FLAG); + memset(&clr_res, 0, sizeof(clr_res)); + clr_res.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + clr_res.func_idx = HINIC_HWIF_GLOBAL_IDX(hwif); + 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), NULL, NULL); + if (err) + PMD_DRV_LOG(WARNING, "Notice flush message failed"); + + err = wait_for_flr_finish(hwif); + if (err) + PMD_DRV_LOG(WARNING, "Wait firmware FLR timeout"); + + hinic_enable_doorbell(hwif); + + err = hinic_reinit_cmdq_ctxts(hwdev); + if (err) + PMD_DRV_LOG(WARNING, "Reinit cmdq failed"); + + return 0; +} + +int hinic_func_rx_tx_flush(struct hinic_hwdev *hwdev) +{ + return hinic_pf_rx_tx_flush(hwdev); +} + +/** + * hinic_get_interrupt_cfg - get interrupt configuration from NIC + * @hwdev: the hardware interface of a nic device + * @interrupt_info: Information of Interrupt aggregation + * Return: 0 on success, negative error value otherwise. + **/ +static int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev, + struct nic_interrupt_info *interrupt_info) +{ + struct hinic_msix_config msix_cfg; + u16 out_size = sizeof(msix_cfg); + int err; + + memset(&msix_cfg, 0, sizeof(msix_cfg)); + msix_cfg.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + msix_cfg.func_id = hinic_global_func_id(hwdev); + 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.mgmt_msg_head.status) { + PMD_DRV_LOG(ERR, "Get interrupt config failed, ret: %d", + msix_cfg.mgmt_msg_head.status); + 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; +} + +/** + * hinic_set_interrupt_cfg - set interrupt configuration to NIC + * @hwdev: the hardware interface of a nic device + * @interrupt_info: Information of Interrupt aggregation + * Return: 0 on success, negative error value otherwise. + **/ +int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev, + struct nic_interrupt_info interrupt_info) +{ + struct hinic_msix_config msix_cfg; + struct nic_interrupt_info temp_info; + u16 out_size = sizeof(msix_cfg); + int err; + + memset(&msix_cfg, 0, sizeof(msix_cfg)); + msix_cfg.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + msix_cfg.func_id = hinic_global_func_id(hwdev); + msix_cfg.msix_index = (u16)interrupt_info.msix_index; + + temp_info.msix_index = interrupt_info.msix_index; + + err = hinic_get_interrupt_cfg(hwdev, &temp_info); + if (err) + return -EINVAL; + + msix_cfg.lli_credit_cnt = temp_info.lli_credit_limit; + msix_cfg.lli_tmier_cnt = temp_info.lli_timer_cfg; + msix_cfg.pending_cnt = temp_info.pending_limt; + msix_cfg.coalesct_timer_cnt = temp_info.coalesc_timer_cfg; + msix_cfg.resend_timer_cnt = temp_info.resend_timer_cfg; + + if (interrupt_info.lli_set) { + msix_cfg.lli_credit_cnt = interrupt_info.lli_credit_limit; + msix_cfg.lli_tmier_cnt = interrupt_info.lli_timer_cfg; + } + + if (interrupt_info.interrupt_coalesc_set) { + 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.mgmt_msg_head.status) { + PMD_DRV_LOG(ERR, "Set interrupt config failed, ret: %d", + msix_cfg.mgmt_msg_head.status); + return -EINVAL; + } + + return 0; +} + +/** + * init_aeqs_msix_attr - Init interrupt attributes of aeq + * @hwdev: the hardware interface of a nic device + * @return + * 0 on success, + * negative error value otherwise. + **/ +int init_aeqs_msix_attr(void *hwdev) +{ + struct hinic_hwdev *nic_hwdev = hwdev; + struct hinic_aeqs *aeqs = nic_hwdev->aeqs; + 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 < aeqs->num_aeqs; q_id++) { + eq = &aeqs->aeq[q_id]; + info.msix_index = eq->eq_irq.msix_entry_idx; + err = hinic_set_interrupt_cfg(hwdev, info); + if (err) { + PMD_DRV_LOG(ERR, "Set msix attr for aeq %d failed", + q_id); + return -EFAULT; + } + } + + return 0; +} + +/** + * set_pf_dma_attr_entry - set the dma attributes for entry + * @hwdev: the pointer to the private hardware device object + * @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); +} + +/** + * dma_attr_table_init - initialize the the default dma attributes + * @hwdev: the pointer to the private hardware device object + **/ +static void dma_attr_table_init(struct hinic_hwdev *hwdev) +{ + if (HINIC_IS_VF(hwdev)) + return; + + 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); +} + +int hinic_init_attr_table(struct hinic_hwdev *hwdev) +{ + dma_attr_table_init(hwdev); + + return init_aeqs_msix_attr(hwdev); +} + +#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"}; + char fault_level[FAULT_LEVEL_MAX][FAULT_SHOW_STR_LEN + 1] = { + "fatal", "reset", "flr", "general", "suggestion"}; + char type_str[FAULT_SHOW_STR_LEN + 1] = { 0 }; + char level_str[FAULT_SHOW_STR_LEN + 1] = { 0 }; + u8 err_level; + + PMD_DRV_LOG(WARNING, "Fault event report received, func_id: %d", + hinic_global_func_id(hwdev)); + + if (event->type < FAULT_TYPE_MAX) + strncpy(type_str, fault_type[event->type], FAULT_SHOW_STR_LEN); + else + strncpy(type_str, "unknown", FAULT_SHOW_STR_LEN); + PMD_DRV_LOG(WARNING, "fault type: %d [%s]", + event->type, type_str); + PMD_DRV_LOG(WARNING, "fault val[0]: 0x%08x", + event->event.val[0]); + PMD_DRV_LOG(WARNING, "fault val[1]: 0x%08x", + event->event.val[1]); + PMD_DRV_LOG(WARNING, "fault val[2]: 0x%08x", + event->event.val[2]); + PMD_DRV_LOG(WARNING, "fault val[3]: 0x%08x", + event->event.val[3]); + + switch (event->type) { + case FAULT_TYPE_CHIP: + err_level = event->event.chip.err_level; + if (err_level < FAULT_LEVEL_MAX) + strncpy(level_str, fault_level[err_level], + FAULT_SHOW_STR_LEN); + else + strncpy(level_str, "unknown", + FAULT_SHOW_STR_LEN); + + PMD_DRV_LOG(WARNING, "err_level: %d [%s]", + err_level, level_str); + + if (err_level == FAULT_LEVEL_SERIOUS_FLR) { + PMD_DRV_LOG(WARNING, "flr func_id: %d", + event->event.chip.func_id); + } else { + PMD_DRV_LOG(WARNING, "node_id: %d", + event->event.chip.node_id); + PMD_DRV_LOG(WARNING, "err_type: %d", + event->event.chip.err_type); + PMD_DRV_LOG(WARNING, "err_csr_addr: %d", + event->event.chip.err_csr_addr); + PMD_DRV_LOG(WARNING, "err_csr_value: %d", + event->event.chip.err_csr_value); + } + break; + case FAULT_TYPE_UCODE: + PMD_DRV_LOG(WARNING, "cause_id: %d", + event->event.ucode.cause_id); + PMD_DRV_LOG(WARNING, "core_id: %d", + event->event.ucode.core_id); + PMD_DRV_LOG(WARNING, "c_id: %d", + event->event.ucode.c_id); + PMD_DRV_LOG(WARNING, "epc: %d", + event->event.ucode.epc); + break; + case FAULT_TYPE_MEM_RD_TIMEOUT: + case FAULT_TYPE_MEM_WR_TIMEOUT: + PMD_DRV_LOG(WARNING, "err_csr_ctrl: %d", + event->event.mem_timeout.err_csr_ctrl); + PMD_DRV_LOG(WARNING, "err_csr_data: %d", + event->event.mem_timeout.err_csr_data); + PMD_DRV_LOG(WARNING, "ctrl_tab: %d", + event->event.mem_timeout.ctrl_tab); + PMD_DRV_LOG(WARNING, "mem_index: %d", + event->event.mem_timeout.mem_index); + break; + case FAULT_TYPE_REG_RD_TIMEOUT: + case FAULT_TYPE_REG_WR_TIMEOUT: + PMD_DRV_LOG(WARNING, "err_csr: %d", + event->event.reg_timeout.err_csr); + break; + default: + break; + } +} + +static int resources_state_set(struct hinic_hwdev *hwdev, + enum hinic_res_state state) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_cmd_set_res_state res_state; + + memset(&res_state, 0, sizeof(res_state)); + res_state.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + res_state.func_idx = HINIC_HWIF_GLOBAL_IDX(hwif); + res_state.state = state; + + return hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, + HINIC_MGMT_CMD_RES_STATE_SET, + &res_state, sizeof(res_state), NULL, NULL, 0); +} + +/** + * hinic_activate_hwdev_state - Active host nic state and notify mgmt channel + * that host nic is ready. + * @hwdev: the hardware interface of a nic device + * @return + * 0 on success, + * negative error value otherwise. + **/ +int hinic_activate_hwdev_state(struct hinic_hwdev *hwdev) +{ + int rc = HINIC_OK; + + if (!hwdev) + return -EINVAL; + + if (!HINIC_IS_VF(hwdev)) + hinic_set_pf_status(hwdev->hwif, + HINIC_PF_STATUS_ACTIVE_FLAG); + + rc = resources_state_set(hwdev, HINIC_RES_ACTIVE); + if (rc) { + PMD_DRV_LOG(ERR, "Initialize resources state failed"); + return rc; + } + + return 0; +} + +/** + * hinic_deactivate_hwdev_state - Deactivate host nic state and notify mgmt + * channel that host nic is not ready. + * @hwdev: the pointer to the private hardware device object + **/ +void hinic_deactivate_hwdev_state(struct hinic_hwdev *hwdev) +{ + int rc = HINIC_OK; + + if (!hwdev) + return; + + rc = resources_state_set(hwdev, HINIC_RES_CLEAN); + if (rc) + PMD_DRV_LOG(ERR, "Deinit resources state failed"); + + if (!HINIC_IS_VF(hwdev)) + hinic_set_pf_status(hwdev->hwif, HINIC_PF_STATUS_INIT); +} + +int hinic_get_board_info(void *hwdev, struct hinic_board_info *info) +{ + struct hinic_comm_board_info board_info; + u16 out_size = sizeof(board_info); + int err; + + if (!hwdev || !info) + return -EINVAL; + + memset(&board_info, 0, sizeof(board_info)); + board_info.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + 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.mgmt_msg_head.status || !out_size) { + PMD_DRV_LOG(ERR, "Failed to get board info, err: %d, status: 0x%x, out size: 0x%x", + err, board_info.mgmt_msg_head.status, out_size); + return -EFAULT; + } + + memcpy(info, &board_info.info, sizeof(*info)); + return 0; +} + +/** + * hinic_l2nic_reset - Restore the initial state of NIC + * @hwdev: the hardware interface of a nic device + * @return + * 0 on success, + * negative error value otherwise. + **/ +int hinic_l2nic_reset(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_l2nic_reset l2nic_reset; + int err = 0; + + err = hinic_set_vport_enable(hwdev, false); + if (err) { + PMD_DRV_LOG(ERR, "Set vport disable failed"); + return err; + } + + rte_delay_ms(100); + + memset(&l2nic_reset, 0, sizeof(l2nic_reset)); + l2nic_reset.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + l2nic_reset.func_id = HINIC_HWIF_GLOBAL_IDX(hwif); + err = hinic_msg_to_mgmt_sync(hwdev, HINIC_MOD_COMM, + HINIC_MGMT_CMD_L2NIC_RESET, + &l2nic_reset, sizeof(l2nic_reset), + NULL, NULL, 0); + if (err || l2nic_reset.mgmt_msg_head.status) { + PMD_DRV_LOG(ERR, "Reset L2NIC resources failed"); + return -EFAULT; + } + + return 0; +} + +static void +hinic_show_sw_watchdog_timeout_info(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)) { + PMD_DRV_LOG(ERR, "Invalid mgmt watchdog report, length: %d, should be %zu", + in_size, sizeof(*watchdog_info)); + return; + } + + watchdog_info = (struct hinic_mgmt_watchdog_info *)buf_in; + + PMD_DRV_LOG(ERR, "Mgmt deadloop time: 0x%x 0x%x, task id: 0x%x, sp: 0x%x", + watchdog_info->curr_time_h, watchdog_info->curr_time_l, + watchdog_info->task_id, watchdog_info->sp); + PMD_DRV_LOG(ERR, "Stack current used: 0x%x, peak used: 0x%x, overflow flag: 0x%x, top: 0x%x, bottom: 0x%x", + watchdog_info->curr_used, watchdog_info->peak_used, + watchdog_info->is_overflow, watchdog_info->stack_top, + watchdog_info->stack_bottom); + + PMD_DRV_LOG(ERR, "Mgmt pc: 0x%08x, lr: 0x%08x, cpsr:0x%08x", + watchdog_info->pc, watchdog_info->lr, watchdog_info->cpsr); + + PMD_DRV_LOG(ERR, "Mgmt register info"); + + for (i = 0; i < 3; i++) { + reg = watchdog_info->reg + (u64)(u32)(4 * i); + PMD_DRV_LOG(ERR, "0x%08x 0x%08x 0x%08x 0x%08x", + *(reg), *(reg + 1), *(reg + 2), *(reg + 3)); + } + + PMD_DRV_LOG(ERR, "0x%08x", watchdog_info->reg[12]); + + if (watchdog_info->stack_actlen <= 1024) { + stack_len = watchdog_info->stack_actlen; + } else { + PMD_DRV_LOG(ERR, "Oops stack length: 0x%x is wrong", + watchdog_info->stack_actlen); + stack_len = 1024; + } + + PMD_DRV_LOG(ERR, "Mgmt dump stack, 16Bytes per line(start from sp)"); + for (i = 0; i < (stack_len / 16); i++) { + dump_addr = (u32 *)(watchdog_info->data + ((u64)(u32)(i * 16))); + PMD_DRV_LOG(ERR, "0x%08x 0x%08x 0x%08x 0x%08x", + *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))); + PMD_DRV_LOG(ERR, "0x%08x", *dump_addr); + } + + *out_size = sizeof(*watchdog_info); + watchdog_info = (struct hinic_mgmt_watchdog_info *)buf_out; + watchdog_info->mgmt_msg_head.status = 0; +} + +static void hinic_show_pcie_dfx_info(struct hinic_hwdev *hwdev, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_pcie_dfx_ntc *notice_info = + (struct hinic_pcie_dfx_ntc *)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)) { + PMD_DRV_LOG(ERR, "Invalid pcie dfx notice info, length: %d, should be %zu.", + in_size, sizeof(*notice_info)); + return; + } + + ((struct hinic_pcie_dfx_ntc *)buf_out)->mgmt_msg_head.status = 0; + *out_size = sizeof(*notice_info); + memset(&dfx_info, 0, sizeof(dfx_info)); + num = (u32)(notice_info->len / 1024); + PMD_DRV_LOG(INFO, "INFO LEN: %d", notice_info->len); + PMD_DRV_LOG(INFO, "PCIE DFX:"); + dfx_info.host_id = 0; + dfx_info.mgmt_msg_head.resp_aeq_num = HINIC_AEQ1; + 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.mgmt_msg_head.status || !size) { + PMD_DRV_LOG(ERR, "Failed to get pcie dfx info, err: %d, status: 0x%x, out size: 0x%x", + err, dfx_info.mgmt_msg_head.status, size); + return; + } + + reg = (u32 *)dfx_info.data; + for (j = 0; j < 256; j = j + 8) { + PMD_DRV_LOG(ERR, "0x%04x: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", + 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)]); + cnt = cnt + 32; + } + memset(dfx_info.data, 0, MAX_PCIE_DFX_BUF_SIZE); + } +} + +static void +hinic_show_ffm_info(struct hinic_hwdev *hwdev, void *buf_in, u16 in_size) +{ + struct ffm_intr_info *intr; + + if (in_size != sizeof(struct ffm_intr_info)) { + PMD_DRV_LOG(ERR, "Invalid input buffer len, length: %d, should be %zu.", + in_size, sizeof(struct ffm_intr_info)); + return; + } + + if (hwdev->ffm_num < FFM_RECORD_NUM_MAX) { + hwdev->ffm_num++; + intr = (struct ffm_intr_info *)buf_in; + PMD_DRV_LOG(WARNING, "node_id(%d),err_csr_addr(0x%x),err_csr_val(0x%x),err_level(0x%x),err_type(0x%x)", + intr->node_id, + intr->err_csr_addr, + intr->err_csr_value, + intr->err_level, + intr->err_type); + } +} + +void hinic_comm_async_event_handle(struct hinic_hwdev *hwdev, u8 cmd, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_cmd_fault_event *fault_event, *ret_fault_event; + + if (!hwdev) + return; + + *out_size = 0; + + switch (cmd) { + case HINIC_MGMT_CMD_FAULT_REPORT: + if (in_size != sizeof(*fault_event)) { + PMD_DRV_LOG(ERR, "Invalid fault event report, length: %d, should be %zu", + in_size, sizeof(*fault_event)); + return; + } + + fault_event = (struct hinic_cmd_fault_event *)buf_in; + fault_report_show(hwdev, &fault_event->event); + + if (hinic_func_type(hwdev) != TYPE_VF) { + ret_fault_event = + (struct hinic_cmd_fault_event *)buf_out; + ret_fault_event->mgmt_msg_head.status = 0; + *out_size = sizeof(*ret_fault_event); + } + break; + + case HINIC_MGMT_CMD_WATCHDOG_INFO: + hinic_show_sw_watchdog_timeout_info(buf_in, in_size, + buf_out, out_size); + break; + + case HINIC_MGMT_CMD_PCIE_DFX_NTC: + hinic_show_pcie_dfx_info(hwdev, buf_in, in_size, + buf_out, out_size); + break; + + case HINIC_MGMT_CMD_FFM_SET: + hinic_show_ffm_info(hwdev, buf_in, in_size); + break; + + default: + break; + } +} + +static void +hinic_cable_status_event(u8 cmd, void *buf_in, __rte_unused u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_cable_plug_event *plug_event; + struct hinic_link_err_event *link_err; + + if (cmd == HINIC_PORT_CMD_CABLE_PLUG_EVENT) { + plug_event = (struct hinic_cable_plug_event *)buf_in; + PMD_DRV_LOG(INFO, "Port module event: Cable %s", + plug_event->plugged ? "plugged" : "unplugged"); + + *out_size = sizeof(*plug_event); + plug_event = (struct hinic_cable_plug_event *)buf_out; + plug_event->mgmt_msg_head.status = 0; + } else if (cmd == HINIC_PORT_CMD_LINK_ERR_EVENT) { + link_err = (struct hinic_link_err_event *)buf_in; + if (link_err->err_type >= LINK_ERR_NUM) { + PMD_DRV_LOG(ERR, "Link failed, Unknown type: 0x%x", + link_err->err_type); + } else { + PMD_DRV_LOG(INFO, "Link failed, type: 0x%x: %s", + link_err->err_type, + hinic_module_link_err[link_err->err_type]); + } + + *out_size = sizeof(*link_err); + link_err = (struct hinic_link_err_event *)buf_out; + link_err->mgmt_msg_head.status = 0; + } +} + +static int hinic_link_event_process(struct hinic_hwdev *hwdev, + struct rte_eth_dev *eth_dev, u8 status) +{ + uint32_t port_speed[LINK_SPEED_MAX] = {ETH_SPEED_NUM_10M, + ETH_SPEED_NUM_100M, ETH_SPEED_NUM_1G, + ETH_SPEED_NUM_10G, ETH_SPEED_NUM_25G, + ETH_SPEED_NUM_40G, ETH_SPEED_NUM_100G}; + struct nic_port_info port_info; + struct rte_eth_link link; + int rc = HINIC_OK; + + if (!status) { + link.link_status = ETH_LINK_DOWN; + link.link_speed = 0; + link.link_duplex = ETH_LINK_HALF_DUPLEX; + link.link_autoneg = ETH_LINK_FIXED; + } else { + link.link_status = ETH_LINK_UP; + + memset(&port_info, 0, sizeof(port_info)); + rc = hinic_get_port_info(hwdev, &port_info); + if (rc) { + link.link_speed = ETH_SPEED_NUM_NONE; + link.link_duplex = ETH_LINK_FULL_DUPLEX; + link.link_autoneg = ETH_LINK_FIXED; + } else { + link.link_speed = port_speed[port_info.speed % + LINK_SPEED_MAX]; + link.link_duplex = port_info.duplex; + link.link_autoneg = port_info.autoneg_state; + } + } + (void)rte_eth_linkstatus_set(eth_dev, &link); + + return rc; +} + +static void hinic_lsc_process(struct hinic_hwdev *hwdev, + struct rte_eth_dev *rte_dev, u8 status) +{ + int ret; + + ret = hinic_link_event_process(hwdev, rte_dev, status); + /* check if link has changed, notify callback */ + if (ret == 0) + _rte_eth_dev_callback_process(rte_dev, + RTE_ETH_EVENT_INTR_LSC, + NULL); +} + +void hinic_l2nic_async_event_handle(struct hinic_hwdev *hwdev, + void *param, u8 cmd, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_port_link_status *in_link; + struct rte_eth_dev *eth_dev; + + if (!hwdev) + return; + + *out_size = 0; + + switch (cmd) { + case HINIC_PORT_CMD_LINK_STATUS_REPORT: + eth_dev = param; + in_link = (struct hinic_port_link_status *)buf_in; + PMD_DRV_LOG(INFO, "Link status event report, dev_name: %s, port_id: %d, link_status: %s", + eth_dev->data->name, eth_dev->data->port_id, + in_link->link ? "UP" : "DOWN"); + + hinic_lsc_process(hwdev, eth_dev, in_link->link); + break; + + case HINIC_PORT_CMD_CABLE_PLUG_EVENT: + case HINIC_PORT_CMD_LINK_ERR_EVENT: + hinic_cable_status_event(cmd, buf_in, in_size, + buf_out, out_size); + break; + + case HINIC_PORT_CMD_MGMT_RESET: + PMD_DRV_LOG(WARNING, "Mgmt is reset"); + break; + + default: + PMD_DRV_LOG(ERR, "Unsupported event %d to process", + cmd); + break; + } +} + +static void print_cable_info(struct hinic_link_info *info) +{ + char tmp_str[512] = {0}; + char tmp_vendor[17] = {0}; + const char *port_type = "Unknown port type"; + int i; + + if (info->cable_absent) { + PMD_DRV_LOG(INFO, "Cable unpresent"); + return; + } + + if (info->port_type < LINK_PORT_MAX_TYPE) + port_type = __hw_to_char_port_type[info->port_type]; + else + PMD_DRV_LOG(INFO, "Unknown port type: %u", + 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)); + snprintf(tmp_str, (sizeof(tmp_str) - 1), + "Vendor: %s, %s, %s, length: %um, max_speed: %uGbps", + tmp_vendor, info->sfp_type ? "SFP" : "QSFP", port_type, + info->cable_length, info->cable_max_speed); + if (info->port_type != LINK_PORT_COPPER) + snprintf(tmp_str + strlen(tmp_str), (sizeof(tmp_str) - 1), + "%s, Temperature: %u", tmp_str, + info->cable_temp); + + PMD_DRV_LOG(INFO, "Cable information: %s", tmp_str); +} + +static void print_hi30_status(struct hinic_link_info *info) +{ + struct hi30_ffe_data *ffe_data; + struct hi30_ctle_data *ctle_data; + + ffe_data = (struct hi30_ffe_data *)info->hi30_ffe; + ctle_data = (struct hi30_ctle_data *)info->hi30_ctle; + + PMD_DRV_LOG(INFO, "TX_FFE: PRE2=%s%d; PRE1=%s%d; MAIN=%d; POST1=%s%d; POST1X=%s%d", + (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)); + PMD_DRV_LOG(INFO, "RX_CTLE: Gain1~3=%u %u %u; Boost1~3=%u %u %u; Zero1~3=%u %u %u; Squelch1~3=%u %u %u", + 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_link_info(struct hinic_link_info *info, + enum hilink_info_print_event type) +{ + const char *fec = "None"; + + if (info->fec < HILINK_FEC_MAX_TYPE) + fec = __hw_to_char_fec[info->fec]; + else + PMD_DRV_LOG(INFO, "Unknown fec type: %u", + info->fec); + + if (type == HILINK_EVENT_LINK_UP || !info->an_state) { + PMD_DRV_LOG(INFO, "Link information: speed %dGbps, %s, autoneg %s", + info->speed, fec, info->an_state ? "on" : "off"); + } else { + PMD_DRV_LOG(INFO, "Link information: antoneg: %s", + info->an_state ? "on" : "off"); + } +} + +static const char *hilink_info_report_type[HILINK_EVENT_MAX_TYPE] = { + "", "link up", "link down", "cable plugged" +}; + +static void hinic_print_hilink_info(void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_hilink_link_info *hilink_info = + (struct hinic_hilink_link_info *)buf_in; + struct hinic_link_info *info; + enum hilink_info_print_event type; + + if (in_size != sizeof(*hilink_info)) { + PMD_DRV_LOG(ERR, "Invalid hilink info message size %d, should be %zu", + in_size, sizeof(*hilink_info)); + return; + } + + ((struct hinic_hilink_link_info *)buf_out)->mgmt_msg_head.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) { + PMD_DRV_LOG(INFO, "Invalid hilink info report, type: %d", + type); + return; + } + + PMD_DRV_LOG(INFO, "Hilink info report after %s", + hilink_info_report_type[type]); + + print_cable_info(info); + + print_link_info(info, type); + + print_hi30_status(info); + + if (type == HILINK_EVENT_LINK_UP) + return; + + if (type == HILINK_EVENT_CABLE_PLUGGED) { + PMD_DRV_LOG(INFO, "alos: %u, rx_los: %u", + info->alos, info->rx_los); + return; + } + + PMD_DRV_LOG(INFO, "PMA ctrl: %s, MAC tx %s, MAC rx %s, PMA debug inforeg: 0x%x, PMA signal ok reg: 0x%x, RF/LF status reg: 0x%x", + info->pma_status ? "on" : "off", + 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); + PMD_DRV_LOG(INFO, "alos: %u, rx_los: %u, PCS block counter reg: 0x%x,PCS link: 0x%x, MAC link: 0x%x PCS_err_cnt: 0x%x", + info->alos, info->rx_los, info->pcs_err_blk_cnt_reg, + info->pcs_link_reg, info->mac_link_reg, info->pcs_err_cnt); +} + +void hinic_hilink_async_event_handle(struct hinic_hwdev *hwdev, u8 cmd, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + if (!hwdev) + return; + + *out_size = 0; + + switch (cmd) { + case HINIC_HILINK_CMD_GET_LINK_INFO: + hinic_print_hilink_info(buf_in, in_size, buf_out, + out_size); + break; + + default: + PMD_DRV_LOG(ERR, "Unsupported event %d to process", + cmd); + break; + } +} diff --git a/drivers/net/hinic/base/hinic_pmd_hwdev.h b/drivers/net/hinic/base/hinic_pmd_hwdev.h new file mode 100644 index 000000000..6c21c475f --- /dev/null +++ b/drivers/net/hinic/base/hinic_pmd_hwdev.h @@ -0,0 +1,485 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2017 Huawei Technologies Co., Ltd + */ + +#ifndef _HINIC_PMD_HWDEV_H_ +#define _HINIC_PMD_HWDEV_H_ + +#include "hinic_pmd_cmd.h" + +#define HINIC_PAGE_SIZE_MAX 20 + +#define HINIC_MGMT_CMD_UNSUPPORTED 0xFF +#define HINIC_PF_SET_VF_ALREADY 0x4 + +#define MAX_PCIE_DFX_BUF_SIZE 1024 + +/* dma pool */ +struct dma_pool { + u32 inuse; + size_t elem_size; + size_t align; + size_t boundary; + void *hwdev; + + char name[32]; +}; + +enum hinic_res_state { + HINIC_RES_CLEAN = 0, + HINIC_RES_ACTIVE = 1, +}; + +enum hilink_info_print_event { + HILINK_EVENT_LINK_UP = 1, + HILINK_EVENT_LINK_DOWN, + HILINK_EVENT_CABLE_PLUGGED, + HILINK_EVENT_MAX_TYPE, +}; + +struct hinic_port_link_status { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_id; + u8 link; + u8 port_id; +}; + +enum link_err_status { + LINK_ERR_MODULE_UNRECOGENIZED, + LINK_ERR_NUM, +}; + +struct hinic_cable_plug_event { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_id; + u8 plugged; /* 0: unplugged, 1: plugged */ + u8 port_id; +}; + +struct hinic_link_err_event { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_id; + u8 err_type; + u8 port_id; +}; + +struct hinic_cons_idx_attr { + struct hinic_mgmt_msg_head mgmt_msg_head; + + 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 { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_idx; + u8 ppf_idx; + u8 rsvd1; +}; + +struct hinic_clear_resource { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_idx; + u8 ppf_idx; + u8 rsvd1; +}; + +struct hinic_cmd_set_res_state { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_idx; + u8 state; + u8 rsvd1; + u32 rsvd2; +}; + +struct hinic_l2nic_reset { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_id; + u16 rsvd1; +}; + +struct hinic_page_size { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 func_idx; + u8 ppf_idx; + u8 page_size; + u32 rsvd; +}; + +struct hinic_msix_config { + struct hinic_mgmt_msg_head mgmt_msg_head; + + 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]; +}; + +/* 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_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 +}; + +/* defined by chip */ +struct hinic_fault_event { + /* enum hinic_fault_type */ + u8 type; + u8 rsvd0[3]; + union { + u32 val[4]; + /* valid only type==FAULT_TYPE_CHIP */ + struct { + u8 node_id; + /* enum hinic_fault_err_level */ + u8 err_level; + u8 err_type; + u8 rsvd1; + 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; + } event; +}; + +struct hinic_cmd_fault_event { + struct hinic_mgmt_msg_head mgmt_msg_head; + + struct hinic_fault_event event; +}; + +struct hinic_mgmt_watchdog_info { + struct hinic_mgmt_msg_head mgmt_msg_head; + + 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_pcie_dfx_ntc { + struct hinic_mgmt_msg_head mgmt_msg_head; + + int len; + u32 rsvd; +}; + +struct hinic_pcie_dfx_info { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u8 host_id; + u8 last; + u8 rsvd[2]; + u32 offset; + + u8 data[MAX_PCIE_DFX_BUF_SIZE]; +}; + +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; +}; + +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; +}; + +struct hinic_comm_board_info { + struct hinic_mgmt_msg_head mgmt_msg_head; + + struct hinic_board_info info; + + u32 rsvd1[5]; +}; + +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_ffe_data { + u8 PRE2; + u8 PRE1; + u8 POST1; + u8 POST2; + u8 MAIN; +}; + +enum hilink_fec_type { + HILINK_FEC_RSFEC, + HILINK_FEC_BASEFEC, + HILINK_FEC_NOFEC, + HILINK_FEC_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, +}; + +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; + + u8 lane_used; + u8 hi30_ffe[5]; + u8 hi30_ctle[19]; + u8 hi30_dfe[14]; + u8 rsvd4; +}; + +struct hinic_hilink_link_info { + struct hinic_mgmt_msg_head mgmt_msg_head; + + u16 port_id; + u8 info_type; /* 1: link up 2: link down 3 cable plugged */ + u8 rsvd1; + + struct hinic_link_info info; + + u8 rsvd2[780]; +}; + +/* dma os dependency implementation */ +struct hinic_os_dep { + /* kernel dma alloc api */ + rte_atomic32_t dma_alloc_cnt; + rte_spinlock_t dma_hash_lock; + struct rte_hash *dma_addr_hash; +}; + +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; +}; + +struct hinic_sq_attr { + u8 dma_attr_off; + u8 pending_limit; + u8 coalescing_time; + u8 intr_en; + u16 intr_idx; + u32 l2nic_sqn; + /* bit[63:2] is addr's high 62bit, bit[0] is valid flag */ + u64 ci_dma_base; +}; + +struct hinic_hwdev { + struct rte_pci_device *pcidev_hdl; + u32 ffm_num; + + /* dma memory allocator */ + struct hinic_os_dep os_dep; + struct hinic_hwif *hwif; + struct cfg_mgmt_info *cfg_mgmt; + struct hinic_aeqs *aeqs; + struct hinic_msg_pf_to_mgmt *pf_to_mgmt; + struct hinic_cmdqs *cmdqs; + struct hinic_nic_io *nic_io; + +}; + +int hinic_osdep_init(struct hinic_hwdev *hwdev); + +void hinic_osdep_deinit(struct hinic_hwdev *hwdev); + +void dma_free_coherent_volatile(void *hwdev, size_t size, + volatile void *virt, dma_addr_t phys); + +int hinic_get_board_info(void *hwdev, struct hinic_board_info *info); + +int hinic_set_ci_table(void *hwdev, u16 q_id, struct hinic_sq_attr *attr); + +int hinic_func_rx_tx_flush(struct hinic_hwdev *hwdev); + +int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev, + struct nic_interrupt_info interrupt_info); + +int init_aeqs_msix_attr(void *hwdev); + +void hinic_comm_async_event_handle(struct hinic_hwdev *hwdev, u8 cmd, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size); + +void hinic_l2nic_async_event_handle(struct hinic_hwdev *hwdev, void *param, + u8 cmd, void *buf_in, u16 in_size, + void *buf_out, u16 *out_size); + +void hinic_hilink_async_event_handle(struct hinic_hwdev *hwdev, u8 cmd, + void *buf_in, u16 in_size, void *buf_out, + u16 *out_size); + +int hinic_init_attr_table(struct hinic_hwdev *hwdev); + +int hinic_activate_hwdev_state(struct hinic_hwdev *hwdev); + +void hinic_deactivate_hwdev_state(struct hinic_hwdev *hwdev); + +int hinic_l2nic_reset(struct hinic_hwdev *hwdev); + +int hinic_set_pagesize(void *hwdev, u8 page_size); + +#endif /* _HINIC_PMD_HWDEV_H_ */ -- 2.18.0