Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,42 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
# Copyright (c) 2019-2020 Marvell International Ltd.
obj-$(CONFIG_QED) := qed.o
qed-y := \
qed_chain.o \
qed_cxt.o \
qed_dcbx.o \
qed_debug.o \
qed_dev.o \
qed_devlink.o \
qed_hw.o \
qed_init_fw_funcs.o \
qed_init_ops.o \
qed_int.o \
qed_l2.o \
qed_main.o \
qed_mcp.o \
qed_mng_tlv.o \
qed_ptp.o \
qed_selftest.o \
qed_sp_commands.o \
qed_spq.o
qed-$(CONFIG_QED_FCOE) += qed_fcoe.o
qed-$(CONFIG_QED_ISCSI) += qed_iscsi.o
qed-$(CONFIG_QED_LL2) += qed_ll2.o
qed-$(CONFIG_QED_OOO) += qed_ooo.o
qed-$(CONFIG_QED_NVMETCP) += \
qed_nvmetcp.o \
qed_nvmetcp_fw_funcs.o
qed-$(CONFIG_QED_RDMA) += \
qed_iwarp.o \
qed_rdma.o \
qed_roce.o
qed-$(CONFIG_QED_SRIOV) += \
qed_sriov.o \
qed_vf.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,371 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* Copyright (c) 2020 Marvell International Ltd. */
#include <linux/dma-mapping.h>
#include <linux/qed/qed_chain.h>
#include <linux/vmalloc.h>
#include "qed_dev_api.h"
static void qed_chain_init(struct qed_chain *chain,
const struct qed_chain_init_params *params,
u32 page_cnt)
{
memset(chain, 0, sizeof(*chain));
chain->elem_size = params->elem_size;
chain->intended_use = params->intended_use;
chain->mode = params->mode;
chain->cnt_type = params->cnt_type;
chain->elem_per_page = ELEMS_PER_PAGE(params->elem_size,
params->page_size);
chain->usable_per_page = USABLE_ELEMS_PER_PAGE(params->elem_size,
params->page_size,
params->mode);
chain->elem_unusable = UNUSABLE_ELEMS_PER_PAGE(params->elem_size,
params->mode);
chain->elem_per_page_mask = chain->elem_per_page - 1;
chain->next_page_mask = chain->usable_per_page &
chain->elem_per_page_mask;
chain->page_size = params->page_size;
chain->page_cnt = page_cnt;
chain->capacity = chain->usable_per_page * page_cnt;
chain->size = chain->elem_per_page * page_cnt;
if (params->ext_pbl_virt) {
chain->pbl_sp.table_virt = params->ext_pbl_virt;
chain->pbl_sp.table_phys = params->ext_pbl_phys;
chain->b_external_pbl = true;
}
}
static void qed_chain_init_next_ptr_elem(const struct qed_chain *chain,
void *virt_curr, void *virt_next,
dma_addr_t phys_next)
{
struct qed_chain_next *next;
u32 size;
size = chain->elem_size * chain->usable_per_page;
next = virt_curr + size;
DMA_REGPAIR_LE(next->next_phys, phys_next);
next->next_virt = virt_next;
}
static void qed_chain_init_mem(struct qed_chain *chain, void *virt_addr,
dma_addr_t phys_addr)
{
chain->p_virt_addr = virt_addr;
chain->p_phys_addr = phys_addr;
}
static void qed_chain_free_next_ptr(struct qed_dev *cdev,
struct qed_chain *chain)
{
struct device *dev = &cdev->pdev->dev;
struct qed_chain_next *next;
dma_addr_t phys, phys_next;
void *virt, *virt_next;
u32 size, i;
size = chain->elem_size * chain->usable_per_page;
virt = chain->p_virt_addr;
phys = chain->p_phys_addr;
for (i = 0; i < chain->page_cnt; i++) {
if (!virt)
break;
next = virt + size;
virt_next = next->next_virt;
phys_next = HILO_DMA_REGPAIR(next->next_phys);
dma_free_coherent(dev, chain->page_size, virt, phys);
virt = virt_next;
phys = phys_next;
}
}
static void qed_chain_free_single(struct qed_dev *cdev,
struct qed_chain *chain)
{
if (!chain->p_virt_addr)
return;
dma_free_coherent(&cdev->pdev->dev, chain->page_size,
chain->p_virt_addr, chain->p_phys_addr);
}
static void qed_chain_free_pbl(struct qed_dev *cdev, struct qed_chain *chain)
{
struct device *dev = &cdev->pdev->dev;
struct addr_tbl_entry *entry;
u32 i;
if (!chain->pbl.pp_addr_tbl)
return;
for (i = 0; i < chain->page_cnt; i++) {
entry = chain->pbl.pp_addr_tbl + i;
if (!entry->virt_addr)
break;
dma_free_coherent(dev, chain->page_size, entry->virt_addr,
entry->dma_map);
}
if (!chain->b_external_pbl)
dma_free_coherent(dev, chain->pbl_sp.table_size,
chain->pbl_sp.table_virt,
chain->pbl_sp.table_phys);
vfree(chain->pbl.pp_addr_tbl);
chain->pbl.pp_addr_tbl = NULL;
}
/**
* qed_chain_free() - Free chain DMA memory.
*
* @cdev: Main device structure.
* @chain: Chain to free.
*/
void qed_chain_free(struct qed_dev *cdev, struct qed_chain *chain)
{
switch (chain->mode) {
case QED_CHAIN_MODE_NEXT_PTR:
qed_chain_free_next_ptr(cdev, chain);
break;
case QED_CHAIN_MODE_SINGLE:
qed_chain_free_single(cdev, chain);
break;
case QED_CHAIN_MODE_PBL:
qed_chain_free_pbl(cdev, chain);
break;
default:
return;
}
qed_chain_init_mem(chain, NULL, 0);
}
static int
qed_chain_alloc_sanity_check(struct qed_dev *cdev,
const struct qed_chain_init_params *params,
u32 page_cnt)
{
u64 chain_size;
chain_size = ELEMS_PER_PAGE(params->elem_size, params->page_size);
chain_size *= page_cnt;
if (!chain_size)
return -EINVAL;
/* The actual chain size can be larger than the maximal possible value
* after rounding up the requested elements number to pages, and after
* taking into account the unusuable elements (next-ptr elements).
* The size of a "u16" chain can be (U16_MAX + 1) since the chain
* size/capacity fields are of u32 type.
*/
switch (params->cnt_type) {
case QED_CHAIN_CNT_TYPE_U16:
if (chain_size > U16_MAX + 1)
break;
return 0;
case QED_CHAIN_CNT_TYPE_U32:
if (chain_size > U32_MAX)
break;
return 0;
default:
return -EINVAL;
}
DP_NOTICE(cdev,
"The actual chain size (0x%llx) is larger than the maximal possible value\n",
chain_size);
return -EINVAL;
}
static int qed_chain_alloc_next_ptr(struct qed_dev *cdev,
struct qed_chain *chain)
{
struct device *dev = &cdev->pdev->dev;
void *virt, *virt_prev = NULL;
dma_addr_t phys;
u32 i;
for (i = 0; i < chain->page_cnt; i++) {
virt = dma_alloc_coherent(dev, chain->page_size, &phys,
GFP_KERNEL);
if (!virt)
return -ENOMEM;
if (i == 0) {
qed_chain_init_mem(chain, virt, phys);
qed_chain_reset(chain);
} else {
qed_chain_init_next_ptr_elem(chain, virt_prev, virt,
phys);
}
virt_prev = virt;
}
/* Last page's next element should point to the beginning of the
* chain.
*/
qed_chain_init_next_ptr_elem(chain, virt_prev, chain->p_virt_addr,
chain->p_phys_addr);
return 0;
}
static int qed_chain_alloc_single(struct qed_dev *cdev,
struct qed_chain *chain)
{
dma_addr_t phys;
void *virt;
virt = dma_alloc_coherent(&cdev->pdev->dev, chain->page_size,
&phys, GFP_KERNEL);
if (!virt)
return -ENOMEM;
qed_chain_init_mem(chain, virt, phys);
qed_chain_reset(chain);
return 0;
}
static int qed_chain_alloc_pbl(struct qed_dev *cdev, struct qed_chain *chain)
{
struct device *dev = &cdev->pdev->dev;
struct addr_tbl_entry *addr_tbl;
dma_addr_t phys, pbl_phys;
__le64 *pbl_virt;
u32 page_cnt, i;
size_t size;
void *virt;
page_cnt = chain->page_cnt;
size = array_size(page_cnt, sizeof(*addr_tbl));
if (unlikely(size == SIZE_MAX))
return -EOVERFLOW;
addr_tbl = vzalloc(size);
if (!addr_tbl)
return -ENOMEM;
chain->pbl.pp_addr_tbl = addr_tbl;
if (chain->b_external_pbl) {
pbl_virt = chain->pbl_sp.table_virt;
goto alloc_pages;
}
size = array_size(page_cnt, sizeof(*pbl_virt));
if (unlikely(size == SIZE_MAX))
return -EOVERFLOW;
pbl_virt = dma_alloc_coherent(dev, size, &pbl_phys, GFP_KERNEL);
if (!pbl_virt)
return -ENOMEM;
chain->pbl_sp.table_virt = pbl_virt;
chain->pbl_sp.table_phys = pbl_phys;
chain->pbl_sp.table_size = size;
alloc_pages:
for (i = 0; i < page_cnt; i++) {
virt = dma_alloc_coherent(dev, chain->page_size, &phys,
GFP_KERNEL);
if (!virt)
return -ENOMEM;
if (i == 0) {
qed_chain_init_mem(chain, virt, phys);
qed_chain_reset(chain);
}
/* Fill the PBL table with the physical address of the page */
pbl_virt[i] = cpu_to_le64(phys);
/* Keep the virtual address of the page */
addr_tbl[i].virt_addr = virt;
addr_tbl[i].dma_map = phys;
}
return 0;
}
/**
* qed_chain_alloc() - Allocate and initialize a chain.
*
* @cdev: Main device structure.
* @chain: Chain to be processed.
* @params: Chain initialization parameters.
*
* Return: 0 on success, negative errno otherwise.
*/
int qed_chain_alloc(struct qed_dev *cdev, struct qed_chain *chain,
struct qed_chain_init_params *params)
{
u32 page_cnt;
int rc;
if (!params->page_size)
params->page_size = QED_CHAIN_PAGE_SIZE;
if (params->mode == QED_CHAIN_MODE_SINGLE)
page_cnt = 1;
else
page_cnt = QED_CHAIN_PAGE_CNT(params->num_elems,
params->elem_size,
params->page_size,
params->mode);
rc = qed_chain_alloc_sanity_check(cdev, params, page_cnt);
if (rc) {
DP_NOTICE(cdev,
"Cannot allocate a chain with the given arguments:\n");
DP_NOTICE(cdev,
"[use_mode %d, mode %d, cnt_type %d, num_elems %d, elem_size %zu, page_size %u]\n",
params->intended_use, params->mode, params->cnt_type,
params->num_elems, params->elem_size,
params->page_size);
return rc;
}
qed_chain_init(chain, params, page_cnt);
switch (params->mode) {
case QED_CHAIN_MODE_NEXT_PTR:
rc = qed_chain_alloc_next_ptr(cdev, chain);
break;
case QED_CHAIN_MODE_SINGLE:
rc = qed_chain_alloc_single(cdev, chain);
break;
case QED_CHAIN_MODE_PBL:
rc = qed_chain_alloc_pbl(cdev, chain);
break;
default:
return -EINVAL;
}
if (!rc)
return 0;
qed_chain_free(cdev, chain);
return rc;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,367 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_CXT_H
#define _QED_CXT_H
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/qed/qed_if.h>
#include "qed_hsi.h"
#include "qed.h"
struct qed_cxt_info {
void *p_cxt;
u32 iid;
enum protocol_type type;
};
#define MAX_TID_BLOCKS 512
struct qed_tid_mem {
u32 tid_size;
u32 num_tids_per_block;
u32 waste;
u8 *blocks[MAX_TID_BLOCKS]; /* 4K */
};
/**
* qed_cxt_get_cid_info(): Returns the context info for a specific cidi.
*
* @p_hwfn: HW device data.
* @p_info: In/out.
*
* Return: Int.
*/
int qed_cxt_get_cid_info(struct qed_hwfn *p_hwfn,
struct qed_cxt_info *p_info);
/**
* qed_cxt_get_tid_mem_info(): Returns the tid mem info.
*
* @p_hwfn: HW device data.
* @p_info: in/out.
*
* Return: int.
*/
int qed_cxt_get_tid_mem_info(struct qed_hwfn *p_hwfn,
struct qed_tid_mem *p_info);
#define QED_CXT_TCP_ULP_TID_SEG PROTOCOLID_TCP_ULP
#define QED_CXT_ROCE_TID_SEG PROTOCOLID_ROCE
#define QED_CXT_FCOE_TID_SEG PROTOCOLID_FCOE
enum qed_cxt_elem_type {
QED_ELEM_CXT,
QED_ELEM_SRQ,
QED_ELEM_TASK,
QED_ELEM_XRC_SRQ,
};
u32 qed_cxt_get_proto_cid_count(struct qed_hwfn *p_hwfn,
enum protocol_type type, u32 *vf_cid);
/**
* qed_cxt_set_pf_params(): Set the PF params for cxt init.
*
* @p_hwfn: HW device data.
* @rdma_tasks: Requested maximum.
*
* Return: int.
*/
int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn, u32 rdma_tasks);
/**
* qed_cxt_cfg_ilt_compute(): Compute ILT init parameters.
*
* @p_hwfn: HW device data.
* @last_line: Last_line.
*
* Return: Int
*/
int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn, u32 *last_line);
/**
* qed_cxt_cfg_ilt_compute_excess(): How many lines can be decreased.
*
* @p_hwfn: HW device data.
* @used_lines: Used lines.
*
* Return: Int.
*/
u32 qed_cxt_cfg_ilt_compute_excess(struct qed_hwfn *p_hwfn, u32 used_lines);
/**
* qed_cxt_mngr_alloc(): Allocate and init the context manager struct.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_cxt_mngr_alloc(struct qed_hwfn *p_hwfn);
/**
* qed_cxt_mngr_free() - Context manager free.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_cxt_mngr_free(struct qed_hwfn *p_hwfn);
/**
* qed_cxt_tables_alloc(): Allocate ILT shadow, Searcher T2, acquired map.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_cxt_tables_alloc(struct qed_hwfn *p_hwfn);
/**
* qed_cxt_mngr_setup(): Reset the acquired CIDs.
*
* @p_hwfn: HW device data.
*/
void qed_cxt_mngr_setup(struct qed_hwfn *p_hwfn);
/**
* qed_cxt_hw_init_common(): Initailze ILT and DQ, common phase, per path.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_cxt_hw_init_common(struct qed_hwfn *p_hwfn);
/**
* qed_cxt_hw_init_pf(): Initailze ILT and DQ, PF phase, per path.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
*
* Return: Void.
*/
void qed_cxt_hw_init_pf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
/**
* qed_qm_init_pf(): Initailze the QM PF phase, per path.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @is_pf_loading: Is pf pending.
*
* Return: Void.
*/
void qed_qm_init_pf(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, bool is_pf_loading);
/**
* qed_qm_reconf(): Reconfigures QM pf on the fly.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
*
* Return: Int.
*/
int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
#define QED_CXT_PF_CID (0xff)
/**
* qed_cxt_release_cid(): Release a cid.
*
* @p_hwfn: HW device data.
* @cid: Cid.
*
* Return: Void.
*/
void qed_cxt_release_cid(struct qed_hwfn *p_hwfn, u32 cid);
/**
* _qed_cxt_release_cid(): Release a cid belonging to a vf-queue.
*
* @p_hwfn: HW device data.
* @cid: Cid.
* @vfid: Engine relative index. QED_CXT_PF_CID if belongs to PF.
*
* Return: Void.
*/
void _qed_cxt_release_cid(struct qed_hwfn *p_hwfn, u32 cid, u8 vfid);
/**
* qed_cxt_acquire_cid(): Acquire a new cid of a specific protocol type.
*
* @p_hwfn: HW device data.
* @type: Type.
* @p_cid: Pointer cid.
*
* Return: Int.
*/
int qed_cxt_acquire_cid(struct qed_hwfn *p_hwfn,
enum protocol_type type, u32 *p_cid);
/**
* _qed_cxt_acquire_cid(): Acquire a new cid of a specific protocol type
* for a vf-queue.
*
* @p_hwfn: HW device data.
* @type: Type.
* @p_cid: Pointer cid.
* @vfid: Engine relative index. QED_CXT_PF_CID if belongs to PF.
*
* Return: Int.
*/
int _qed_cxt_acquire_cid(struct qed_hwfn *p_hwfn,
enum protocol_type type, u32 *p_cid, u8 vfid);
int qed_cxt_dynamic_ilt_alloc(struct qed_hwfn *p_hwfn,
enum qed_cxt_elem_type elem_type, u32 iid);
u32 qed_cxt_get_proto_tid_count(struct qed_hwfn *p_hwfn,
enum protocol_type type);
u32 qed_cxt_get_proto_cid_start(struct qed_hwfn *p_hwfn,
enum protocol_type type);
int qed_cxt_free_proto_ilt(struct qed_hwfn *p_hwfn, enum protocol_type proto);
#define QED_CTX_WORKING_MEM 0
#define QED_CTX_FL_MEM 1
int qed_cxt_get_task_ctx(struct qed_hwfn *p_hwfn,
u32 tid, u8 ctx_type, void **task_ctx);
/* Max number of connection types in HW (DQ/CDU etc.) */
#define MAX_CONN_TYPES PROTOCOLID_COMMON
#define NUM_TASK_TYPES 2
#define NUM_TASK_PF_SEGMENTS 4
#define NUM_TASK_VF_SEGMENTS 1
/* PF per protocl configuration object */
#define TASK_SEGMENTS (NUM_TASK_PF_SEGMENTS + NUM_TASK_VF_SEGMENTS)
#define TASK_SEGMENT_VF (NUM_TASK_PF_SEGMENTS)
struct qed_tid_seg {
u32 count;
u8 type;
bool has_fl_mem;
};
struct qed_conn_type_cfg {
u32 cid_count;
u32 cids_per_vf;
struct qed_tid_seg tid_seg[TASK_SEGMENTS];
};
/* ILT Client configuration,
* Per connection type (protocol) resources (cids, tis, vf cids etc.)
* 1 - for connection context (CDUC) and for each task context we need two
* values, for regular task context and for force load memory
*/
#define ILT_CLI_PF_BLOCKS (1 + NUM_TASK_PF_SEGMENTS * 2)
#define ILT_CLI_VF_BLOCKS (1 + NUM_TASK_VF_SEGMENTS * 2)
#define CDUC_BLK (0)
#define SRQ_BLK (0)
#define CDUT_SEG_BLK(n) (1 + (u8)(n))
#define CDUT_FL_SEG_BLK(n, X) (1 + (n) + NUM_TASK_ ## X ## _SEGMENTS)
struct ilt_cfg_pair {
u32 reg;
u32 val;
};
struct qed_ilt_cli_blk {
u32 total_size; /* 0 means not active */
u32 real_size_in_page;
u32 start_line;
u32 dynamic_line_offset;
u32 dynamic_line_cnt;
};
struct qed_ilt_client_cfg {
bool active;
/* ILT boundaries */
struct ilt_cfg_pair first;
struct ilt_cfg_pair last;
struct ilt_cfg_pair p_size;
/* ILT client blocks for PF */
struct qed_ilt_cli_blk pf_blks[ILT_CLI_PF_BLOCKS];
u32 pf_total_lines;
/* ILT client blocks for VFs */
struct qed_ilt_cli_blk vf_blks[ILT_CLI_VF_BLOCKS];
u32 vf_total_lines;
};
struct qed_cid_acquired_map {
u32 start_cid;
u32 max_count;
unsigned long *cid_map;
};
struct qed_src_t2 {
struct phys_mem_desc *dma_mem;
u32 num_pages;
u64 first_free;
u64 last_free;
};
struct qed_cxt_mngr {
/* Per protocl configuration */
struct qed_conn_type_cfg conn_cfg[MAX_CONN_TYPES];
/* computed ILT structure */
struct qed_ilt_client_cfg clients[MAX_ILT_CLIENTS];
/* Task type sizes */
u32 task_type_size[NUM_TASK_TYPES];
/* total number of VFs for this hwfn -
* ALL VFs are symmetric in terms of HW resources
*/
u32 vf_count;
u32 first_vf_in_pf;
/* Acquired CIDs */
struct qed_cid_acquired_map acquired[MAX_CONN_TYPES];
struct qed_cid_acquired_map
acquired_vf[MAX_CONN_TYPES][MAX_NUM_VFS];
/* ILT shadow table */
struct phys_mem_desc *ilt_shadow;
u32 ilt_shadow_size;
u32 pf_start_line;
/* Mutex for a dynamic ILT allocation */
struct mutex mutex;
/* SRC T2 */
struct qed_src_t2 src_t2;
/* total number of SRQ's for this hwfn */
u32 srq_count;
u32 xrc_srq_count;
/* Maximal number of L2 steering filters */
u32 arfs_count;
u16 iscsi_task_pages;
u16 fcoe_task_pages;
u16 roce_task_pages;
u16 eth_task_pages;
u16 task_ctx_size;
u16 conn_ctx_size;
};
u16 qed_get_cdut_num_pf_init_pages(struct qed_hwfn *p_hwfn);
u16 qed_get_cdut_num_vf_init_pages(struct qed_hwfn *p_hwfn);
u16 qed_get_cdut_num_pf_work_pages(struct qed_hwfn *p_hwfn);
u16 qed_get_cdut_num_vf_work_pages(struct qed_hwfn *p_hwfn);
u32 qed_cxt_get_ilt_page_size(struct qed_hwfn *p_hwfn,
enum ilt_clients ilt_client);
u32 qed_cxt_get_total_srq_count(struct qed_hwfn *p_hwfn);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,107 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_DCBX_H
#define _QED_DCBX_H
#include <linux/types.h>
#include <linux/slab.h>
#include "qed.h"
#include "qed_hsi.h"
#include "qed_hw.h"
#include "qed_mcp.h"
#include "qed_reg_addr.h"
#define DCBX_CONFIG_MAX_APP_PROTOCOL 4
enum qed_mib_read_type {
QED_DCBX_OPERATIONAL_MIB,
QED_DCBX_REMOTE_MIB,
QED_DCBX_LOCAL_MIB,
QED_DCBX_REMOTE_LLDP_MIB,
QED_DCBX_LOCAL_LLDP_MIB
};
struct qed_dcbx_app_data {
bool enable; /* DCB enabled */
u8 update; /* Update indication */
u8 priority; /* Priority */
u8 tc; /* Traffic Class */
bool dont_add_vlan0; /* Do not insert a vlan tag with id 0 */
};
#define QED_DCBX_VERSION_DISABLED 0
#define QED_DCBX_VERSION_IEEE 1
#define QED_DCBX_VERSION_CEE 2
struct qed_dcbx_set {
#define QED_DCBX_OVERRIDE_STATE BIT(0)
#define QED_DCBX_OVERRIDE_PFC_CFG BIT(1)
#define QED_DCBX_OVERRIDE_ETS_CFG BIT(2)
#define QED_DCBX_OVERRIDE_APP_CFG BIT(3)
#define QED_DCBX_OVERRIDE_DSCP_CFG BIT(4)
u32 override_flags;
bool enabled;
struct qed_dcbx_admin_params config;
u32 ver_num;
};
struct qed_dcbx_results {
bool dcbx_enabled;
u8 pf_id;
struct qed_dcbx_app_data arr[DCBX_MAX_PROTOCOL_TYPE];
};
struct qed_dcbx_app_metadata {
enum dcbx_protocol_type id;
char *name;
enum qed_pci_personality personality;
};
struct qed_dcbx_info {
struct lldp_status_params_s lldp_remote[LLDP_MAX_LLDP_AGENTS];
struct lldp_config_params_s lldp_local[LLDP_MAX_LLDP_AGENTS];
struct dcbx_local_params local_admin;
struct qed_dcbx_results results;
struct dcbx_mib operational;
struct dcbx_mib remote;
struct qed_dcbx_set set;
struct qed_dcbx_get get;
u8 dcbx_cap;
};
struct qed_dcbx_mib_meta_data {
struct lldp_config_params_s *lldp_local;
struct lldp_status_params_s *lldp_remote;
struct dcbx_local_params *local_admin;
struct dcbx_mib *mib;
size_t size;
u32 addr;
};
extern const struct qed_eth_dcbnl_ops qed_dcbnl_ops_pass;
#ifdef CONFIG_DCB
int qed_dcbx_get_config_params(struct qed_hwfn *p_hwfn,
struct qed_dcbx_set *params);
int qed_dcbx_config_params(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
struct qed_dcbx_set *params, bool hw_commit);
#endif
/* QED local interface routines */
int
qed_dcbx_mib_update_event(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
enum qed_mib_read_type type);
int qed_dcbx_info_alloc(struct qed_hwfn *p_hwfn);
void qed_dcbx_info_free(struct qed_hwfn *p_hwfn);
void qed_dcbx_set_pf_update_params(struct qed_dcbx_results *p_src,
struct pf_update_ramrod_data *p_dest);
#define QED_DCBX_DEFAULT_TC 0
u8 qed_dcbx_get_priority_tc(struct qed_hwfn *p_hwfn, u8 pri);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015 QLogic Corporation
* Copyright (c) 2019-2021 Marvell International Ltd.
*/
#ifndef _QED_DEBUG_H
#define _QED_DEBUG_H
enum qed_dbg_features {
DBG_FEATURE_GRC,
DBG_FEATURE_IDLE_CHK,
DBG_FEATURE_MCP_TRACE,
DBG_FEATURE_REG_FIFO,
DBG_FEATURE_IGU_FIFO,
DBG_FEATURE_PROTECTION_OVERRIDE,
DBG_FEATURE_FW_ASSERTS,
DBG_FEATURE_ILT,
DBG_FEATURE_NUM
};
/* Forward Declaration */
struct qed_dev;
struct qed_hwfn;
int qed_dbg_grc(struct qed_dev *cdev, void *buffer, u32 *num_dumped_bytes);
int qed_dbg_grc_size(struct qed_dev *cdev);
int qed_dbg_idle_chk(struct qed_dev *cdev, void *buffer,
u32 *num_dumped_bytes);
int qed_dbg_idle_chk_size(struct qed_dev *cdev);
int qed_dbg_reg_fifo(struct qed_dev *cdev, void *buffer,
u32 *num_dumped_bytes);
int qed_dbg_reg_fifo_size(struct qed_dev *cdev);
int qed_dbg_igu_fifo(struct qed_dev *cdev, void *buffer,
u32 *num_dumped_bytes);
int qed_dbg_igu_fifo_size(struct qed_dev *cdev);
int qed_dbg_protection_override(struct qed_dev *cdev, void *buffer,
u32 *num_dumped_bytes);
int qed_dbg_protection_override_size(struct qed_dev *cdev);
int qed_dbg_fw_asserts(struct qed_dev *cdev, void *buffer,
u32 *num_dumped_bytes);
int qed_dbg_fw_asserts_size(struct qed_dev *cdev);
int qed_dbg_ilt(struct qed_dev *cdev, void *buffer, u32 *num_dumped_bytes);
int qed_dbg_ilt_size(struct qed_dev *cdev);
int qed_dbg_mcp_trace(struct qed_dev *cdev, void *buffer,
u32 *num_dumped_bytes);
int qed_dbg_mcp_trace_size(struct qed_dev *cdev);
int qed_dbg_phy_size(struct qed_dev *cdev);
int qed_dbg_all_data(struct qed_dev *cdev, void *buffer);
int qed_dbg_all_data_size(struct qed_dev *cdev);
u8 qed_get_debug_engine(struct qed_dev *cdev);
void qed_set_debug_engine(struct qed_dev *cdev, int engine_number);
int qed_dbg_feature(struct qed_dev *cdev, void *buffer,
enum qed_dbg_features feature, u32 *num_dumped_bytes);
int qed_dbg_feature_size(struct qed_dev *cdev, enum qed_dbg_features feature);
void qed_dbg_pf_init(struct qed_dev *cdev);
void qed_dbg_pf_exit(struct qed_dev *cdev);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,529 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_DEV_API_H
#define _QED_DEV_API_H
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/qed/qed_chain.h>
#include <linux/qed/qed_if.h>
#include "qed_int.h"
/**
* qed_init_dp(): Initialize the debug level.
*
* @cdev: Qed dev pointer.
* @dp_module: Module debug parameter.
* @dp_level: Module debug level.
*
* Return: Void.
*/
void qed_init_dp(struct qed_dev *cdev,
u32 dp_module,
u8 dp_level);
/**
* qed_init_struct(): Initialize the device structure to
* its defaults.
*
* @cdev: Qed dev pointer.
*
* Return: Void.
*/
void qed_init_struct(struct qed_dev *cdev);
/**
* qed_resc_free: Free device resources.
*
* @cdev: Qed dev pointer.
*
* Return: Void.
*/
void qed_resc_free(struct qed_dev *cdev);
/**
* qed_resc_alloc(): Alloc device resources.
*
* @cdev: Qed dev pointer.
*
* Return: Int.
*/
int qed_resc_alloc(struct qed_dev *cdev);
/**
* qed_resc_setup(): Setup device resources.
*
* @cdev: Qed dev pointer.
*
* Return: Void.
*/
void qed_resc_setup(struct qed_dev *cdev);
enum qed_override_force_load {
QED_OVERRIDE_FORCE_LOAD_NONE,
QED_OVERRIDE_FORCE_LOAD_ALWAYS,
QED_OVERRIDE_FORCE_LOAD_NEVER,
};
struct qed_drv_load_params {
/* Indicates whether the driver is running over a crash kernel.
* As part of the load request, this will be used for providing the
* driver role to the MFW.
* In case of a crash kernel over PDA - this should be set to false.
*/
bool is_crash_kernel;
/* The timeout value that the MFW should use when locking the engine for
* the driver load process.
* A value of '0' means the default value, and '255' means no timeout.
*/
u8 mfw_timeout_val;
#define QED_LOAD_REQ_LOCK_TO_DEFAULT 0
#define QED_LOAD_REQ_LOCK_TO_NONE 255
/* Avoid engine reset when first PF loads on it */
bool avoid_eng_reset;
/* Allow overriding the default force load behavior */
enum qed_override_force_load override_force_load;
};
struct qed_hw_init_params {
/* Tunneling parameters */
struct qed_tunnel_info *p_tunn;
bool b_hw_start;
/* Interrupt mode [msix, inta, etc.] to use */
enum qed_int_mode int_mode;
/* NPAR tx switching to be used for vports for tx-switching */
bool allow_npar_tx_switch;
/* Binary fw data pointer in binary fw file */
const u8 *bin_fw_data;
/* Driver load parameters */
struct qed_drv_load_params *p_drv_load_params;
};
/**
* qed_hw_init(): Init Qed hardware.
*
* @cdev: Qed dev pointer.
* @p_params: Pointers to params.
*
* Return: Int.
*/
int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params);
/**
* qed_hw_timers_stop_all(): Stop the timers HW block.
*
* @cdev: Qed dev pointer.
*
* Return: void.
*/
void qed_hw_timers_stop_all(struct qed_dev *cdev);
/**
* qed_hw_stop(): Stop Qed hardware.
*
* @cdev: Qed dev pointer.
*
* Return: int.
*/
int qed_hw_stop(struct qed_dev *cdev);
/**
* qed_hw_stop_fastpath(): Should be called incase
* slowpath is still required for the device,
* but fastpath is not.
*
* @cdev: Qed dev pointer.
*
* Return: Int.
*/
int qed_hw_stop_fastpath(struct qed_dev *cdev);
/**
* qed_hw_start_fastpath(): Restart fastpath traffic,
* only if hw_stop_fastpath was called.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_hw_start_fastpath(struct qed_hwfn *p_hwfn);
/**
* qed_hw_prepare(): Prepare Qed hardware.
*
* @cdev: Qed dev pointer.
* @personality: Personality to initialize.
*
* Return: Int.
*/
int qed_hw_prepare(struct qed_dev *cdev,
int personality);
/**
* qed_hw_remove(): Remove Qed hardware.
*
* @cdev: Qed dev pointer.
*
* Return: Void.
*/
void qed_hw_remove(struct qed_dev *cdev);
/**
* qed_ptt_acquire(): Allocate a PTT window.
*
* @p_hwfn: HW device data.
*
* Return: struct qed_ptt.
*
* Should be called at the entry point to the driver (at the beginning of an
* exported function).
*/
struct qed_ptt *qed_ptt_acquire(struct qed_hwfn *p_hwfn);
/**
* qed_ptt_acquire_context(): Allocate a PTT window honoring the context
* atomicy.
*
* @p_hwfn: HW device data.
* @is_atomic: Hint from the caller - if the func can sleep or not.
*
* Context: The function should not sleep in case is_atomic == true.
* Return: struct qed_ptt.
*
* Should be called at the entry point to the driver
* (at the beginning of an exported function).
*/
struct qed_ptt *qed_ptt_acquire_context(struct qed_hwfn *p_hwfn,
bool is_atomic);
/**
* qed_ptt_release(): Release PTT Window.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
*
* Return: Void.
*
* Should be called at the end of a flow - at the end of the function that
* acquired the PTT.
*/
void qed_ptt_release(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt);
void qed_reset_vport_stats(struct qed_dev *cdev);
enum qed_dmae_address_type_t {
QED_DMAE_ADDRESS_HOST_VIRT,
QED_DMAE_ADDRESS_HOST_PHYS,
QED_DMAE_ADDRESS_GRC
};
/**
* qed_dmae_host2grc(): Copy data from source addr to
* dmae registers using the given ptt.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @source_addr: Source address.
* @grc_addr: GRC address (dmae_data_offset).
* @size_in_dwords: Size.
* @p_params: (default parameters will be used in case of NULL).
*
* Return: Int.
*/
int
qed_dmae_host2grc(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u64 source_addr,
u32 grc_addr,
u32 size_in_dwords,
struct qed_dmae_params *p_params);
/**
* qed_dmae_grc2host(): Read data from dmae data offset
* to source address using the given ptt.
*
* @p_ptt: P_ptt.
* @grc_addr: GRC address (dmae_data_offset).
* @dest_addr: Destination Address.
* @size_in_dwords: Size.
* @p_params: (default parameters will be used in case of NULL).
*
* Return: Int.
*/
int qed_dmae_grc2host(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
u32 grc_addr, dma_addr_t dest_addr, u32 size_in_dwords,
struct qed_dmae_params *p_params);
/**
* qed_dmae_host2host(): Copy data from to source address
* to a destination adrress (for SRIOV) using the given
* ptt.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @source_addr: Source address.
* @dest_addr: Destination address.
* @size_in_dwords: size.
* @p_params: (default parameters will be used in case of NULL).
*
* Return: Int.
*/
int qed_dmae_host2host(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
dma_addr_t source_addr,
dma_addr_t dest_addr,
u32 size_in_dwords, struct qed_dmae_params *p_params);
int qed_chain_alloc(struct qed_dev *cdev, struct qed_chain *chain,
struct qed_chain_init_params *params);
void qed_chain_free(struct qed_dev *cdev, struct qed_chain *chain);
/**
* qed_fw_l2_queue(): Get absolute L2 queue ID.
*
* @p_hwfn: HW device data.
* @src_id: Relative to p_hwfn.
* @dst_id: Absolute per engine.
*
* Return: Int.
*/
int qed_fw_l2_queue(struct qed_hwfn *p_hwfn,
u16 src_id,
u16 *dst_id);
/**
* qed_fw_vport(): Get absolute vport ID.
*
* @p_hwfn: HW device data.
* @src_id: Relative to p_hwfn.
* @dst_id: Absolute per engine.
*
* Return: Int.
*/
int qed_fw_vport(struct qed_hwfn *p_hwfn,
u8 src_id,
u8 *dst_id);
/**
* qed_fw_rss_eng(): Get absolute RSS engine ID.
*
* @p_hwfn: HW device data.
* @src_id: Relative to p_hwfn.
* @dst_id: Absolute per engine.
*
* Return: Int.
*/
int qed_fw_rss_eng(struct qed_hwfn *p_hwfn,
u8 src_id,
u8 *dst_id);
/**
* qed_llh_get_num_ppfid(): Return the allocated number of LLH filter
* banks that are allocated to the PF.
*
* @cdev: Qed dev pointer.
*
* Return: u8 Number of LLH filter banks.
*/
u8 qed_llh_get_num_ppfid(struct qed_dev *cdev);
enum qed_eng {
QED_ENG0,
QED_ENG1,
QED_BOTH_ENG,
};
/**
* qed_llh_set_ppfid_affinity(): Set the engine affinity for the given
* LLH filter bank.
*
* @cdev: Qed dev pointer.
* @ppfid: Relative within the allocated ppfids ('0' is the default one).
* @eng: Engine.
*
* Return: Int.
*/
int qed_llh_set_ppfid_affinity(struct qed_dev *cdev,
u8 ppfid, enum qed_eng eng);
/**
* qed_llh_set_roce_affinity(): Set the RoCE engine affinity.
*
* @cdev: Qed dev pointer.
* @eng: Engine.
*
* Return: Int.
*/
int qed_llh_set_roce_affinity(struct qed_dev *cdev, enum qed_eng eng);
/**
* qed_llh_add_mac_filter(): Add a LLH MAC filter into the given filter
* bank.
*
* @cdev: Qed dev pointer.
* @ppfid: Relative within the allocated ppfids ('0' is the default one).
* @mac_addr: MAC to add.
*
* Return: Int.
*/
int qed_llh_add_mac_filter(struct qed_dev *cdev,
u8 ppfid, const u8 mac_addr[ETH_ALEN]);
/**
* qed_llh_remove_mac_filter(): Remove a LLH MAC filter from the given
* filter bank.
*
* @cdev: Qed dev pointer.
* @ppfid: Ppfid.
* @mac_addr: MAC to remove
*
* Return: Void.
*/
void qed_llh_remove_mac_filter(struct qed_dev *cdev,
u8 ppfid, u8 mac_addr[ETH_ALEN]);
enum qed_llh_prot_filter_type_t {
QED_LLH_FILTER_ETHERTYPE,
QED_LLH_FILTER_TCP_SRC_PORT,
QED_LLH_FILTER_TCP_DEST_PORT,
QED_LLH_FILTER_TCP_SRC_AND_DEST_PORT,
QED_LLH_FILTER_UDP_SRC_PORT,
QED_LLH_FILTER_UDP_DEST_PORT,
QED_LLH_FILTER_UDP_SRC_AND_DEST_PORT
};
/**
* qed_llh_add_protocol_filter(): Add a LLH protocol filter into the
* given filter bank.
*
* @cdev: Qed dev pointer.
* @ppfid: Relative within the allocated ppfids ('0' is the default one).
* @type: Type of filters and comparing.
* @source_port_or_eth_type: Source port or ethertype to add.
* @dest_port: Destination port to add.
*
* Return: Int.
*/
int
qed_llh_add_protocol_filter(struct qed_dev *cdev,
u8 ppfid,
enum qed_llh_prot_filter_type_t type,
u16 source_port_or_eth_type, u16 dest_port);
/**
* qed_llh_remove_protocol_filter(): Remove a LLH protocol filter from
* the given filter bank.
*
* @cdev: Qed dev pointer.
* @ppfid: Relative within the allocated ppfids ('0' is the default one).
* @type: Type of filters and comparing.
* @source_port_or_eth_type: Source port or ethertype to add.
* @dest_port: Destination port to add.
*/
void
qed_llh_remove_protocol_filter(struct qed_dev *cdev,
u8 ppfid,
enum qed_llh_prot_filter_type_t type,
u16 source_port_or_eth_type, u16 dest_port);
/**
* qed_final_cleanup(): Cleanup of previous driver remains prior to load.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @id: For PF, engine-relative. For VF, PF-relative.
* @is_vf: True iff cleanup is made for a VF.
*
* Return: Int.
*/
int qed_final_cleanup(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, u16 id, bool is_vf);
/**
* qed_get_queue_coalesce(): Retrieve coalesce value for a given queue.
*
* @p_hwfn: HW device data.
* @coal: Store coalesce value read from the hardware.
* @handle: P_handle.
*
* Return: Int.
**/
int qed_get_queue_coalesce(struct qed_hwfn *p_hwfn, u16 *coal, void *handle);
/**
* qed_set_queue_coalesce(): Configure coalesce parameters for Rx and
* Tx queue. The fact that we can configure coalescing to up to 511, but on
* varying accuracy [the bigger the value the less accurate] up to a mistake
* of 3usec for the highest values.
* While the API allows setting coalescing per-qid, all queues sharing a SB
* should be in same range [i.e., either 0-0x7f, 0x80-0xff or 0x100-0x1ff]
* otherwise configuration would break.
*
* @rx_coal: Rx Coalesce value in micro seconds.
* @tx_coal: TX Coalesce value in micro seconds.
* @p_handle: P_handle.
*
* Return: Int.
**/
int
qed_set_queue_coalesce(u16 rx_coal, u16 tx_coal, void *p_handle);
/**
* qed_pglueb_set_pfid_enable(): Enable or disable PCI BUS MASTER.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @b_enable: True/False.
*
* Return: Int.
*/
int qed_pglueb_set_pfid_enable(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, bool b_enable);
/**
* qed_db_recovery_add(): add doorbell information to the doorbell
* recovery mechanism.
*
* @cdev: Qed dev pointer.
* @db_addr: Doorbell address.
* @db_data: Address of where db_data is stored.
* @db_width: Doorbell is 32b pr 64b.
* @db_space: Doorbell recovery addresses are user or kernel space.
*
* Return: Int.
*/
int qed_db_recovery_add(struct qed_dev *cdev,
void __iomem *db_addr,
void *db_data,
enum qed_db_rec_width db_width,
enum qed_db_rec_space db_space);
/**
* qed_db_recovery_del() - remove doorbell information from the doorbell
* recovery mechanism. db_data serves as key (db_addr is not unique).
*
* @cdev: Qed dev pointer.
* @db_addr: doorbell address.
* @db_data: address where db_data is stored. Serves as key for the
* entry to delete.
*
* Return: Int.
*/
int qed_db_recovery_del(struct qed_dev *cdev,
void __iomem *db_addr, void *db_data);
const char *qed_hw_get_resc_name(enum qed_resources res_id);
#endif
@@ -0,0 +1,245 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Marvell/Qlogic FastLinQ NIC driver
*
* Copyright (C) 2020 Marvell International Ltd.
*/
#include <linux/kernel.h>
#include <linux/qed/qed_if.h>
#include <linux/vmalloc.h>
#include "qed.h"
#include "qed_devlink.h"
enum qed_devlink_param_id {
QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
QED_DEVLINK_PARAM_ID_IWARP_CMT,
};
struct qed_fw_fatal_ctx {
enum qed_hw_err_type err_type;
};
int qed_report_fatal_error(struct devlink *devlink, enum qed_hw_err_type err_type)
{
struct qed_devlink *qdl = devlink_priv(devlink);
struct qed_fw_fatal_ctx fw_fatal_ctx = {
.err_type = err_type,
};
if (qdl->fw_reporter)
devlink_health_report(qdl->fw_reporter,
"Fatal error occurred", &fw_fatal_ctx);
return 0;
}
static int
qed_fw_fatal_reporter_dump(struct devlink_health_reporter *reporter,
struct devlink_fmsg *fmsg, void *priv_ctx,
struct netlink_ext_ack *extack)
{
struct qed_devlink *qdl = devlink_health_reporter_priv(reporter);
struct qed_fw_fatal_ctx *fw_fatal_ctx = priv_ctx;
struct qed_dev *cdev = qdl->cdev;
u32 dbg_data_buf_size;
u8 *p_dbg_data_buf;
int err;
/* Having context means that was a dump request after fatal,
* so we enable extra debugging while gathering the dump,
* just in case
*/
cdev->print_dbg_data = fw_fatal_ctx ? true : false;
dbg_data_buf_size = qed_dbg_all_data_size(cdev);
p_dbg_data_buf = vzalloc(dbg_data_buf_size);
if (!p_dbg_data_buf) {
DP_NOTICE(cdev,
"Failed to allocate memory for a debug data buffer\n");
return -ENOMEM;
}
err = qed_dbg_all_data(cdev, p_dbg_data_buf);
if (err) {
DP_NOTICE(cdev, "Failed to obtain debug data\n");
vfree(p_dbg_data_buf);
return err;
}
devlink_fmsg_binary_pair_put(fmsg, "dump_data", p_dbg_data_buf,
dbg_data_buf_size);
vfree(p_dbg_data_buf);
return 0;
}
static int
qed_fw_fatal_reporter_recover(struct devlink_health_reporter *reporter,
void *priv_ctx,
struct netlink_ext_ack *extack)
{
struct qed_devlink *qdl = devlink_health_reporter_priv(reporter);
struct qed_dev *cdev = qdl->cdev;
qed_recovery_process(cdev);
return 0;
}
#define QED_REPORTER_FW_GRACEFUL_PERIOD 0
static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = {
.name = "fw_fatal",
.recover = qed_fw_fatal_reporter_recover,
.dump = qed_fw_fatal_reporter_dump,
.default_graceful_period = QED_REPORTER_FW_GRACEFUL_PERIOD,
};
void qed_fw_reporters_create(struct devlink *devlink)
{
struct qed_devlink *dl = devlink_priv(devlink);
dl->fw_reporter = devlink_health_reporter_create(devlink,
&qed_fw_fatal_reporter_ops, dl);
if (IS_ERR(dl->fw_reporter)) {
DP_NOTICE(dl->cdev, "Failed to create fw reporter, err = %ld\n",
PTR_ERR(dl->fw_reporter));
dl->fw_reporter = NULL;
}
}
void qed_fw_reporters_destroy(struct devlink *devlink)
{
struct qed_devlink *dl = devlink_priv(devlink);
struct devlink_health_reporter *rep;
rep = dl->fw_reporter;
if (!IS_ERR_OR_NULL(rep))
devlink_health_reporter_destroy(rep);
}
static int qed_dl_param_get(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct qed_devlink *qed_dl = devlink_priv(dl);
struct qed_dev *cdev;
cdev = qed_dl->cdev;
ctx->val.vbool = cdev->iwarp_cmt;
return 0;
}
static int qed_dl_param_set(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack)
{
struct qed_devlink *qed_dl = devlink_priv(dl);
struct qed_dev *cdev;
cdev = qed_dl->cdev;
cdev->iwarp_cmt = ctx->val.vbool;
return 0;
}
static const struct devlink_param qed_devlink_params[] = {
DEVLINK_PARAM_DRIVER(QED_DEVLINK_PARAM_ID_IWARP_CMT,
"iwarp_cmt", DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
qed_dl_param_get, qed_dl_param_set, NULL),
};
static int qed_devlink_info_get(struct devlink *devlink,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct qed_devlink *qed_dl = devlink_priv(devlink);
struct qed_dev *cdev = qed_dl->cdev;
struct qed_dev_info *dev_info;
char buf[100];
int err;
dev_info = &cdev->common_dev_info;
memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num));
buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0;
if (buf[0]) {
err = devlink_info_board_serial_number_put(req, buf);
if (err)
return err;
}
snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_3),
GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_2),
GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_1),
GET_MFW_FIELD(dev_info->mfw_rev, QED_MFW_VERSION_0));
err = devlink_info_version_stored_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, buf);
if (err)
return err;
snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
dev_info->fw_major,
dev_info->fw_minor,
dev_info->fw_rev,
dev_info->fw_eng);
return devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW_APP, buf);
}
static const struct devlink_ops qed_dl_ops = {
.info_get = qed_devlink_info_get,
};
struct devlink *qed_devlink_register(struct qed_dev *cdev)
{
struct qed_devlink *qdevlink;
struct devlink *dl;
int rc;
dl = devlink_alloc(&qed_dl_ops, sizeof(struct qed_devlink),
&cdev->pdev->dev);
if (!dl)
return ERR_PTR(-ENOMEM);
qdevlink = devlink_priv(dl);
qdevlink->cdev = cdev;
rc = devlink_params_register(dl, qed_devlink_params,
ARRAY_SIZE(qed_devlink_params));
if (rc)
goto err_unregister;
cdev->iwarp_cmt = false;
qed_fw_reporters_create(dl);
devlink_register(dl);
return dl;
err_unregister:
devlink_free(dl);
return ERR_PTR(rc);
}
void qed_devlink_unregister(struct devlink *devlink)
{
if (!devlink)
return;
devlink_unregister(devlink);
qed_fw_reporters_destroy(devlink);
devlink_params_unregister(devlink, qed_devlink_params,
ARRAY_SIZE(qed_devlink_params));
devlink_free(devlink);
}
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Marvell/Qlogic FastLinQ NIC driver
*
* Copyright (C) 2020 Marvell International Ltd.
*/
#ifndef _QED_DEVLINK_H
#define _QED_DEVLINK_H
#include <linux/qed/qed_if.h>
#include <net/devlink.h>
struct devlink *qed_devlink_register(struct qed_dev *cdev);
void qed_devlink_unregister(struct devlink *devlink);
void qed_fw_reporters_create(struct devlink *devlink);
void qed_fw_reporters_destroy(struct devlink *devlink);
int qed_report_fatal_error(struct devlink *dl, enum qed_hw_err_type err_type);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_FCOE_H
#define _QED_FCOE_H
#include <linux/types.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/qed/qed_fcoe_if.h>
#include <linux/qed/qed_chain.h>
#include "qed.h"
#include "qed_hsi.h"
#include "qed_mcp.h"
#include "qed_sp.h"
struct qed_fcoe_info {
spinlock_t lock; /* Connection resources. */
struct list_head free_list;
};
#if IS_ENABLED(CONFIG_QED_FCOE)
int qed_fcoe_alloc(struct qed_hwfn *p_hwfn);
void qed_fcoe_setup(struct qed_hwfn *p_hwfn);
void qed_fcoe_free(struct qed_hwfn *p_hwfn);
/**
* qed_get_protocol_stats_fcoe(): Fills provided statistics
* struct with statistics.
*
* @cdev: Qed dev pointer.
* @stats: Points to struct that will be filled with statistics.
* @is_atomic: Hint from the caller - if the func can sleep or not.
*
* Context: The function should not sleep in case is_atomic == true.
* Return: Void.
*/
void qed_get_protocol_stats_fcoe(struct qed_dev *cdev,
struct qed_mcp_fcoe_stats *stats,
bool is_atomic);
#else /* CONFIG_QED_FCOE */
static inline int qed_fcoe_alloc(struct qed_hwfn *p_hwfn)
{
return -EINVAL;
}
static inline void qed_fcoe_setup(struct qed_hwfn *p_hwfn) {}
static inline void qed_fcoe_free(struct qed_hwfn *p_hwfn) {}
static inline void qed_get_protocol_stats_fcoe(struct qed_dev *cdev,
struct qed_mcp_fcoe_stats *stats,
bool is_atomic)
{
}
#endif /* CONFIG_QED_FCOE */
#endif /* _QED_FCOE_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,922 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#include <linux/types.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/qed/qed_chain.h>
#include "qed.h"
#include "qed_hsi.h"
#include "qed_hw.h"
#include "qed_reg_addr.h"
#include "qed_sriov.h"
#define QED_BAR_ACQUIRE_TIMEOUT_USLEEP_CNT 1000
#define QED_BAR_ACQUIRE_TIMEOUT_USLEEP 1000
#define QED_BAR_ACQUIRE_TIMEOUT_UDELAY_CNT 100000
#define QED_BAR_ACQUIRE_TIMEOUT_UDELAY 10
/* Invalid values */
#define QED_BAR_INVALID_OFFSET (cpu_to_le32(-1))
struct qed_ptt {
struct list_head list_entry;
unsigned int idx;
struct pxp_ptt_entry pxp;
u8 hwfn_id;
};
struct qed_ptt_pool {
struct list_head free_list;
spinlock_t lock; /* ptt synchronized access */
struct qed_ptt ptts[PXP_EXTERNAL_BAR_PF_WINDOW_NUM];
};
int qed_ptt_pool_alloc(struct qed_hwfn *p_hwfn)
{
struct qed_ptt_pool *p_pool = kmalloc_obj(*p_pool);
int i;
if (!p_pool)
return -ENOMEM;
INIT_LIST_HEAD(&p_pool->free_list);
for (i = 0; i < PXP_EXTERNAL_BAR_PF_WINDOW_NUM; i++) {
p_pool->ptts[i].idx = i;
p_pool->ptts[i].pxp.offset = QED_BAR_INVALID_OFFSET;
p_pool->ptts[i].pxp.pretend.control = 0;
p_pool->ptts[i].hwfn_id = p_hwfn->my_id;
if (i >= RESERVED_PTT_MAX)
list_add(&p_pool->ptts[i].list_entry,
&p_pool->free_list);
}
p_hwfn->p_ptt_pool = p_pool;
spin_lock_init(&p_pool->lock);
return 0;
}
void qed_ptt_pool_free(struct qed_hwfn *p_hwfn)
{
kfree(p_hwfn->p_ptt_pool);
p_hwfn->p_ptt_pool = NULL;
}
struct qed_ptt *qed_ptt_acquire(struct qed_hwfn *p_hwfn)
{
return qed_ptt_acquire_context(p_hwfn, false);
}
struct qed_ptt *qed_ptt_acquire_context(struct qed_hwfn *p_hwfn, bool is_atomic)
{
struct qed_ptt *p_ptt;
unsigned int i, count;
if (is_atomic)
count = QED_BAR_ACQUIRE_TIMEOUT_UDELAY_CNT;
else
count = QED_BAR_ACQUIRE_TIMEOUT_USLEEP_CNT;
/* Take the free PTT from the list */
for (i = 0; i < count; i++) {
spin_lock_bh(&p_hwfn->p_ptt_pool->lock);
if (!list_empty(&p_hwfn->p_ptt_pool->free_list)) {
p_ptt = list_first_entry(&p_hwfn->p_ptt_pool->free_list,
struct qed_ptt, list_entry);
list_del(&p_ptt->list_entry);
spin_unlock_bh(&p_hwfn->p_ptt_pool->lock);
DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
"allocated ptt %d\n", p_ptt->idx);
return p_ptt;
}
spin_unlock_bh(&p_hwfn->p_ptt_pool->lock);
if (is_atomic)
udelay(QED_BAR_ACQUIRE_TIMEOUT_UDELAY);
else
usleep_range(QED_BAR_ACQUIRE_TIMEOUT_USLEEP,
QED_BAR_ACQUIRE_TIMEOUT_USLEEP * 2);
}
DP_NOTICE(p_hwfn, "PTT acquire timeout - failed to allocate PTT\n");
return NULL;
}
void qed_ptt_release(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{
spin_lock_bh(&p_hwfn->p_ptt_pool->lock);
list_add(&p_ptt->list_entry, &p_hwfn->p_ptt_pool->free_list);
spin_unlock_bh(&p_hwfn->p_ptt_pool->lock);
}
u32 qed_ptt_get_hw_addr(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{
/* The HW is using DWORDS and we need to translate it to Bytes */
return le32_to_cpu(p_ptt->pxp.offset) << 2;
}
static u32 qed_ptt_config_addr(struct qed_ptt *p_ptt)
{
return PXP_PF_WINDOW_ADMIN_PER_PF_START +
p_ptt->idx * sizeof(struct pxp_ptt_entry);
}
u32 qed_ptt_get_bar_addr(struct qed_ptt *p_ptt)
{
return PXP_EXTERNAL_BAR_PF_WINDOW_START +
p_ptt->idx * PXP_EXTERNAL_BAR_PF_WINDOW_SINGLE_SIZE;
}
void qed_ptt_set_win(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, u32 new_hw_addr)
{
u32 prev_hw_addr;
prev_hw_addr = qed_ptt_get_hw_addr(p_hwfn, p_ptt);
if (new_hw_addr == prev_hw_addr)
return;
/* Update PTT entery in admin window */
DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
"Updating PTT entry %d to offset 0x%x\n",
p_ptt->idx, new_hw_addr);
/* The HW is using DWORDS and the address is in Bytes */
p_ptt->pxp.offset = cpu_to_le32(new_hw_addr >> 2);
REG_WR(p_hwfn,
qed_ptt_config_addr(p_ptt) +
offsetof(struct pxp_ptt_entry, offset),
le32_to_cpu(p_ptt->pxp.offset));
}
static u32 qed_set_ptt(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, u32 hw_addr)
{
u32 win_hw_addr = qed_ptt_get_hw_addr(p_hwfn, p_ptt);
u32 offset;
offset = hw_addr - win_hw_addr;
if (p_ptt->hwfn_id != p_hwfn->my_id)
DP_NOTICE(p_hwfn,
"ptt[%d] of hwfn[%02x] is used by hwfn[%02x]!\n",
p_ptt->idx, p_ptt->hwfn_id, p_hwfn->my_id);
/* Verify the address is within the window */
if (hw_addr < win_hw_addr ||
offset >= PXP_EXTERNAL_BAR_PF_WINDOW_SINGLE_SIZE) {
qed_ptt_set_win(p_hwfn, p_ptt, hw_addr);
offset = 0;
}
return qed_ptt_get_bar_addr(p_ptt) + offset;
}
struct qed_ptt *qed_get_reserved_ptt(struct qed_hwfn *p_hwfn,
enum reserved_ptts ptt_idx)
{
if (ptt_idx >= RESERVED_PTT_MAX) {
DP_NOTICE(p_hwfn,
"Requested PTT %d is out of range\n", ptt_idx);
return NULL;
}
return &p_hwfn->p_ptt_pool->ptts[ptt_idx];
}
void qed_wr(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 hw_addr, u32 val)
{
u32 bar_addr = qed_set_ptt(p_hwfn, p_ptt, hw_addr);
REG_WR(p_hwfn, bar_addr, val);
DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
"bar_addr 0x%x, hw_addr 0x%x, val 0x%x\n",
bar_addr, hw_addr, val);
}
u32 qed_rd(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 hw_addr)
{
u32 bar_addr = qed_set_ptt(p_hwfn, p_ptt, hw_addr);
u32 val = REG_RD(p_hwfn, bar_addr);
DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
"bar_addr 0x%x, hw_addr 0x%x, val 0x%x\n",
bar_addr, hw_addr, val);
return val;
}
static void qed_memcpy_hw(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
void *addr, u32 hw_addr, size_t n, bool to_device)
{
u32 dw_count, *host_addr, hw_offset;
size_t quota, done = 0;
u32 __iomem *reg_addr;
while (done < n) {
quota = min_t(size_t, n - done,
PXP_EXTERNAL_BAR_PF_WINDOW_SINGLE_SIZE);
if (IS_PF(p_hwfn->cdev)) {
qed_ptt_set_win(p_hwfn, p_ptt, hw_addr + done);
hw_offset = qed_ptt_get_bar_addr(p_ptt);
} else {
hw_offset = hw_addr + done;
}
dw_count = quota / 4;
host_addr = (u32 *)((u8 *)addr + done);
reg_addr = (u32 __iomem *)REG_ADDR(p_hwfn, hw_offset);
if (to_device)
while (dw_count--)
DIRECT_REG_WR(reg_addr++, *host_addr++);
else
while (dw_count--)
*host_addr++ = DIRECT_REG_RD(reg_addr++);
done += quota;
}
}
void qed_memcpy_from(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, void *dest, u32 hw_addr, size_t n)
{
DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
"hw_addr 0x%x, dest %p hw_addr 0x%x, size %lu\n",
hw_addr, dest, hw_addr, (unsigned long)n);
qed_memcpy_hw(p_hwfn, p_ptt, dest, hw_addr, n, false);
}
void qed_memcpy_to(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, u32 hw_addr, void *src, size_t n)
{
DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
"hw_addr 0x%x, hw_addr 0x%x, src %p size %lu\n",
hw_addr, hw_addr, src, (unsigned long)n);
qed_memcpy_hw(p_hwfn, p_ptt, src, hw_addr, n, true);
}
void qed_fid_pretend(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u16 fid)
{
u16 control = 0;
SET_FIELD(control, PXP_PRETEND_CMD_IS_CONCRETE, 1);
SET_FIELD(control, PXP_PRETEND_CMD_PRETEND_FUNCTION, 1);
/* Every pretend undos previous pretends, including
* previous port pretend.
*/
SET_FIELD(control, PXP_PRETEND_CMD_PORT, 0);
SET_FIELD(control, PXP_PRETEND_CMD_USE_PORT, 0);
SET_FIELD(control, PXP_PRETEND_CMD_PRETEND_PORT, 1);
if (!GET_FIELD(fid, PXP_CONCRETE_FID_VFVALID))
fid = GET_FIELD(fid, PXP_CONCRETE_FID_PFID);
p_ptt->pxp.pretend.control = cpu_to_le16(control);
p_ptt->pxp.pretend.fid.concrete_fid.fid = cpu_to_le16(fid);
REG_WR(p_hwfn,
qed_ptt_config_addr(p_ptt) +
offsetof(struct pxp_ptt_entry, pretend),
*(u32 *)&p_ptt->pxp.pretend);
}
void qed_port_pretend(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, u8 port_id)
{
u16 control = 0;
SET_FIELD(control, PXP_PRETEND_CMD_PORT, port_id);
SET_FIELD(control, PXP_PRETEND_CMD_USE_PORT, 1);
SET_FIELD(control, PXP_PRETEND_CMD_PRETEND_PORT, 1);
p_ptt->pxp.pretend.control = cpu_to_le16(control);
REG_WR(p_hwfn,
qed_ptt_config_addr(p_ptt) +
offsetof(struct pxp_ptt_entry, pretend),
*(u32 *)&p_ptt->pxp.pretend);
}
void qed_port_unpretend(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{
u16 control = 0;
SET_FIELD(control, PXP_PRETEND_CMD_PORT, 0);
SET_FIELD(control, PXP_PRETEND_CMD_USE_PORT, 0);
SET_FIELD(control, PXP_PRETEND_CMD_PRETEND_PORT, 1);
p_ptt->pxp.pretend.control = cpu_to_le16(control);
REG_WR(p_hwfn,
qed_ptt_config_addr(p_ptt) +
offsetof(struct pxp_ptt_entry, pretend),
*(u32 *)&p_ptt->pxp.pretend);
}
void qed_port_fid_pretend(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, u8 port_id, u16 fid)
{
u16 control = 0;
SET_FIELD(control, PXP_PRETEND_CMD_PORT, port_id);
SET_FIELD(control, PXP_PRETEND_CMD_USE_PORT, 1);
SET_FIELD(control, PXP_PRETEND_CMD_PRETEND_PORT, 1);
SET_FIELD(control, PXP_PRETEND_CMD_IS_CONCRETE, 1);
SET_FIELD(control, PXP_PRETEND_CMD_PRETEND_FUNCTION, 1);
if (!GET_FIELD(fid, PXP_CONCRETE_FID_VFVALID))
fid = GET_FIELD(fid, PXP_CONCRETE_FID_PFID);
p_ptt->pxp.pretend.control = cpu_to_le16(control);
p_ptt->pxp.pretend.fid.concrete_fid.fid = cpu_to_le16(fid);
REG_WR(p_hwfn,
qed_ptt_config_addr(p_ptt) +
offsetof(struct pxp_ptt_entry, pretend),
*(u32 *)&p_ptt->pxp.pretend);
}
u32 qed_vfid_to_concrete(struct qed_hwfn *p_hwfn, u8 vfid)
{
u32 concrete_fid = 0;
SET_FIELD(concrete_fid, PXP_CONCRETE_FID_PFID, p_hwfn->rel_pf_id);
SET_FIELD(concrete_fid, PXP_CONCRETE_FID_VFID, vfid);
SET_FIELD(concrete_fid, PXP_CONCRETE_FID_VFVALID, 1);
return concrete_fid;
}
/* DMAE */
#define QED_DMAE_FLAGS_IS_SET(params, flag) \
((params) != NULL && GET_FIELD((params)->flags, QED_DMAE_PARAMS_##flag))
static void qed_dmae_opcode(struct qed_hwfn *p_hwfn,
const u8 is_src_type_grc,
const u8 is_dst_type_grc,
struct qed_dmae_params *p_params)
{
u8 src_pfid, dst_pfid, port_id;
u16 opcode_b = 0;
u32 opcode = 0;
/* Whether the source is the PCIe or the GRC.
* 0- The source is the PCIe
* 1- The source is the GRC.
*/
SET_FIELD(opcode, DMAE_CMD_SRC,
(is_src_type_grc ? dmae_cmd_src_grc : dmae_cmd_src_pcie));
src_pfid = QED_DMAE_FLAGS_IS_SET(p_params, SRC_PF_VALID) ?
p_params->src_pfid : p_hwfn->rel_pf_id;
SET_FIELD(opcode, DMAE_CMD_SRC_PF_ID, src_pfid);
/* The destination of the DMA can be: 0-None 1-PCIe 2-GRC 3-None */
SET_FIELD(opcode, DMAE_CMD_DST,
(is_dst_type_grc ? dmae_cmd_dst_grc : dmae_cmd_dst_pcie));
dst_pfid = QED_DMAE_FLAGS_IS_SET(p_params, DST_PF_VALID) ?
p_params->dst_pfid : p_hwfn->rel_pf_id;
SET_FIELD(opcode, DMAE_CMD_DST_PF_ID, dst_pfid);
/* Whether to write a completion word to the completion destination:
* 0-Do not write a completion word
* 1-Write the completion word
*/
SET_FIELD(opcode, DMAE_CMD_COMP_WORD_EN, 1);
SET_FIELD(opcode, DMAE_CMD_SRC_ADDR_RESET, 1);
if (QED_DMAE_FLAGS_IS_SET(p_params, COMPLETION_DST))
SET_FIELD(opcode, DMAE_CMD_COMP_FUNC, 1);
/* swapping mode 3 - big endian */
SET_FIELD(opcode, DMAE_CMD_ENDIANITY_MODE, DMAE_CMD_ENDIANITY);
port_id = (QED_DMAE_FLAGS_IS_SET(p_params, PORT_VALID)) ?
p_params->port_id : p_hwfn->port_id;
SET_FIELD(opcode, DMAE_CMD_PORT_ID, port_id);
/* reset source address in next go */
SET_FIELD(opcode, DMAE_CMD_SRC_ADDR_RESET, 1);
/* reset dest address in next go */
SET_FIELD(opcode, DMAE_CMD_DST_ADDR_RESET, 1);
/* SRC/DST VFID: all 1's - pf, otherwise VF id */
if (QED_DMAE_FLAGS_IS_SET(p_params, SRC_VF_VALID)) {
SET_FIELD(opcode, DMAE_CMD_SRC_VF_ID_VALID, 1);
SET_FIELD(opcode_b, DMAE_CMD_SRC_VF_ID, p_params->src_vfid);
} else {
SET_FIELD(opcode_b, DMAE_CMD_SRC_VF_ID, 0xFF);
}
if (QED_DMAE_FLAGS_IS_SET(p_params, DST_VF_VALID)) {
SET_FIELD(opcode, DMAE_CMD_DST_VF_ID_VALID, 1);
SET_FIELD(opcode_b, DMAE_CMD_DST_VF_ID, p_params->dst_vfid);
} else {
SET_FIELD(opcode_b, DMAE_CMD_DST_VF_ID, 0xFF);
}
p_hwfn->dmae_info.p_dmae_cmd->opcode = cpu_to_le32(opcode);
p_hwfn->dmae_info.p_dmae_cmd->opcode_b = cpu_to_le16(opcode_b);
}
u32 qed_dmae_idx_to_go_cmd(u8 idx)
{
/* All the DMAE 'go' registers form an array in internal memory */
return DMAE_REG_GO_C0 + (idx << 2);
}
static int qed_dmae_post_command(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt)
{
struct dmae_cmd *p_command = p_hwfn->dmae_info.p_dmae_cmd;
u8 idx_cmd = p_hwfn->dmae_info.channel, i;
int qed_status = 0;
/* verify address is not NULL */
if ((((!p_command->dst_addr_lo) && (!p_command->dst_addr_hi)) ||
((!p_command->src_addr_lo) && (!p_command->src_addr_hi)))) {
DP_NOTICE(p_hwfn,
"source or destination address 0 idx_cmd=%d\n"
"opcode = [0x%08x,0x%04x] len=0x%x src=0x%x:%x dst=0x%x:%x\n",
idx_cmd,
le32_to_cpu(p_command->opcode),
le16_to_cpu(p_command->opcode_b),
le16_to_cpu(p_command->length_dw),
le32_to_cpu(p_command->src_addr_hi),
le32_to_cpu(p_command->src_addr_lo),
le32_to_cpu(p_command->dst_addr_hi),
le32_to_cpu(p_command->dst_addr_lo));
return -EINVAL;
}
DP_VERBOSE(p_hwfn,
NETIF_MSG_HW,
"Posting DMAE command [idx %d]: opcode = [0x%08x,0x%04x] len=0x%x src=0x%x:%x dst=0x%x:%x\n",
idx_cmd,
le32_to_cpu(p_command->opcode),
le16_to_cpu(p_command->opcode_b),
le16_to_cpu(p_command->length_dw),
le32_to_cpu(p_command->src_addr_hi),
le32_to_cpu(p_command->src_addr_lo),
le32_to_cpu(p_command->dst_addr_hi),
le32_to_cpu(p_command->dst_addr_lo));
/* Copy the command to DMAE - need to do it before every call
* for source/dest address no reset.
* The first 9 DWs are the command registers, the 10 DW is the
* GO register, and the rest are result registers
* (which are read only by the client).
*/
for (i = 0; i < DMAE_CMD_SIZE; i++) {
u32 data = (i < DMAE_CMD_SIZE_TO_FILL) ?
*(((u32 *)p_command) + i) : 0;
qed_wr(p_hwfn, p_ptt,
DMAE_REG_CMD_MEM +
(idx_cmd * DMAE_CMD_SIZE * sizeof(u32)) +
(i * sizeof(u32)), data);
}
qed_wr(p_hwfn, p_ptt, qed_dmae_idx_to_go_cmd(idx_cmd), DMAE_GO_VALUE);
return qed_status;
}
int qed_dmae_info_alloc(struct qed_hwfn *p_hwfn)
{
dma_addr_t *p_addr = &p_hwfn->dmae_info.completion_word_phys_addr;
struct dmae_cmd **p_cmd = &p_hwfn->dmae_info.p_dmae_cmd;
u32 **p_buff = &p_hwfn->dmae_info.p_intermediate_buffer;
u32 **p_comp = &p_hwfn->dmae_info.p_completion_word;
*p_comp = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
sizeof(u32), p_addr, GFP_KERNEL);
if (!*p_comp)
goto err;
p_addr = &p_hwfn->dmae_info.dmae_cmd_phys_addr;
*p_cmd = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
sizeof(struct dmae_cmd),
p_addr, GFP_KERNEL);
if (!*p_cmd)
goto err;
p_addr = &p_hwfn->dmae_info.intermediate_buffer_phys_addr;
*p_buff = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
sizeof(u32) * DMAE_MAX_RW_SIZE,
p_addr, GFP_KERNEL);
if (!*p_buff)
goto err;
p_hwfn->dmae_info.channel = p_hwfn->rel_pf_id;
return 0;
err:
qed_dmae_info_free(p_hwfn);
return -ENOMEM;
}
void qed_dmae_info_free(struct qed_hwfn *p_hwfn)
{
dma_addr_t p_phys;
/* Just make sure no one is in the middle */
mutex_lock(&p_hwfn->dmae_info.mutex);
if (p_hwfn->dmae_info.p_completion_word) {
p_phys = p_hwfn->dmae_info.completion_word_phys_addr;
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
sizeof(u32),
p_hwfn->dmae_info.p_completion_word, p_phys);
p_hwfn->dmae_info.p_completion_word = NULL;
}
if (p_hwfn->dmae_info.p_dmae_cmd) {
p_phys = p_hwfn->dmae_info.dmae_cmd_phys_addr;
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
sizeof(struct dmae_cmd),
p_hwfn->dmae_info.p_dmae_cmd, p_phys);
p_hwfn->dmae_info.p_dmae_cmd = NULL;
}
if (p_hwfn->dmae_info.p_intermediate_buffer) {
p_phys = p_hwfn->dmae_info.intermediate_buffer_phys_addr;
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
sizeof(u32) * DMAE_MAX_RW_SIZE,
p_hwfn->dmae_info.p_intermediate_buffer,
p_phys);
p_hwfn->dmae_info.p_intermediate_buffer = NULL;
}
mutex_unlock(&p_hwfn->dmae_info.mutex);
}
static int qed_dmae_operation_wait(struct qed_hwfn *p_hwfn)
{
u32 wait_cnt_limit = 10000, wait_cnt = 0;
int qed_status = 0;
barrier();
while (*p_hwfn->dmae_info.p_completion_word != DMAE_COMPLETION_VAL) {
udelay(DMAE_MIN_WAIT_TIME);
cond_resched();
if (++wait_cnt > wait_cnt_limit) {
DP_NOTICE(p_hwfn->cdev,
"Timed-out waiting for operation to complete. Completion word is 0x%08x expected 0x%08x.\n",
*p_hwfn->dmae_info.p_completion_word,
DMAE_COMPLETION_VAL);
qed_status = -EBUSY;
break;
}
/* to sync the completion_word since we are not
* using the volatile keyword for p_completion_word
*/
barrier();
}
if (qed_status == 0)
*p_hwfn->dmae_info.p_completion_word = 0;
return qed_status;
}
static int qed_dmae_execute_sub_operation(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u64 src_addr,
u64 dst_addr,
u8 src_type,
u8 dst_type,
u32 length_dw)
{
dma_addr_t phys = p_hwfn->dmae_info.intermediate_buffer_phys_addr;
struct dmae_cmd *cmd = p_hwfn->dmae_info.p_dmae_cmd;
int qed_status = 0;
switch (src_type) {
case QED_DMAE_ADDRESS_GRC:
case QED_DMAE_ADDRESS_HOST_PHYS:
cmd->src_addr_hi = cpu_to_le32(upper_32_bits(src_addr));
cmd->src_addr_lo = cpu_to_le32(lower_32_bits(src_addr));
break;
/* for virtual source addresses we use the intermediate buffer. */
case QED_DMAE_ADDRESS_HOST_VIRT:
cmd->src_addr_hi = cpu_to_le32(upper_32_bits(phys));
cmd->src_addr_lo = cpu_to_le32(lower_32_bits(phys));
memcpy(&p_hwfn->dmae_info.p_intermediate_buffer[0],
(void *)(uintptr_t)src_addr,
length_dw * sizeof(u32));
break;
default:
return -EINVAL;
}
switch (dst_type) {
case QED_DMAE_ADDRESS_GRC:
case QED_DMAE_ADDRESS_HOST_PHYS:
cmd->dst_addr_hi = cpu_to_le32(upper_32_bits(dst_addr));
cmd->dst_addr_lo = cpu_to_le32(lower_32_bits(dst_addr));
break;
/* for virtual source addresses we use the intermediate buffer. */
case QED_DMAE_ADDRESS_HOST_VIRT:
cmd->dst_addr_hi = cpu_to_le32(upper_32_bits(phys));
cmd->dst_addr_lo = cpu_to_le32(lower_32_bits(phys));
break;
default:
return -EINVAL;
}
cmd->length_dw = cpu_to_le16((u16)length_dw);
qed_dmae_post_command(p_hwfn, p_ptt);
qed_status = qed_dmae_operation_wait(p_hwfn);
if (qed_status) {
DP_NOTICE(p_hwfn,
"qed_dmae_host2grc: Wait Failed. source_addr 0x%llx, grc_addr 0x%llx, size_in_dwords 0x%x\n",
src_addr, dst_addr, length_dw);
return qed_status;
}
if (dst_type == QED_DMAE_ADDRESS_HOST_VIRT)
memcpy((void *)(uintptr_t)(dst_addr),
&p_hwfn->dmae_info.p_intermediate_buffer[0],
length_dw * sizeof(u32));
return 0;
}
static int qed_dmae_execute_command(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u64 src_addr, u64 dst_addr,
u8 src_type, u8 dst_type,
u32 size_in_dwords,
struct qed_dmae_params *p_params)
{
dma_addr_t phys = p_hwfn->dmae_info.completion_word_phys_addr;
u16 length_cur = 0, i = 0, cnt_split = 0, length_mod = 0;
struct dmae_cmd *cmd = p_hwfn->dmae_info.p_dmae_cmd;
u64 src_addr_split = 0, dst_addr_split = 0;
u16 length_limit = DMAE_MAX_RW_SIZE;
int qed_status = 0;
u32 offset = 0;
if (p_hwfn->cdev->recov_in_prog) {
DP_VERBOSE(p_hwfn,
NETIF_MSG_HW,
"Recovery is in progress. Avoid DMAE transaction [{src: addr 0x%llx, type %d}, {dst: addr 0x%llx, type %d}, size %d].\n",
src_addr, src_type, dst_addr, dst_type,
size_in_dwords);
/* Let the flow complete w/o any error handling */
return 0;
}
qed_dmae_opcode(p_hwfn,
(src_type == QED_DMAE_ADDRESS_GRC),
(dst_type == QED_DMAE_ADDRESS_GRC),
p_params);
cmd->comp_addr_lo = cpu_to_le32(lower_32_bits(phys));
cmd->comp_addr_hi = cpu_to_le32(upper_32_bits(phys));
cmd->comp_val = cpu_to_le32(DMAE_COMPLETION_VAL);
/* Check if the grc_addr is valid like < MAX_GRC_OFFSET */
cnt_split = size_in_dwords / length_limit;
length_mod = size_in_dwords % length_limit;
src_addr_split = src_addr;
dst_addr_split = dst_addr;
for (i = 0; i <= cnt_split; i++) {
offset = length_limit * i;
if (!QED_DMAE_FLAGS_IS_SET(p_params, RW_REPL_SRC)) {
if (src_type == QED_DMAE_ADDRESS_GRC)
src_addr_split = src_addr + offset;
else
src_addr_split = src_addr + (offset * 4);
}
if (dst_type == QED_DMAE_ADDRESS_GRC)
dst_addr_split = dst_addr + offset;
else
dst_addr_split = dst_addr + (offset * 4);
length_cur = (cnt_split == i) ? length_mod : length_limit;
/* might be zero on last iteration */
if (!length_cur)
continue;
qed_status = qed_dmae_execute_sub_operation(p_hwfn,
p_ptt,
src_addr_split,
dst_addr_split,
src_type,
dst_type,
length_cur);
if (qed_status) {
qed_hw_err_notify(p_hwfn, p_ptt, QED_HW_ERR_DMAE_FAIL,
"qed_dmae_execute_sub_operation Failed with error 0x%x. source_addr 0x%llx, destination addr 0x%llx, size_in_dwords 0x%x\n",
qed_status, src_addr,
dst_addr, length_cur);
break;
}
}
return qed_status;
}
int qed_dmae_host2grc(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u64 source_addr, u32 grc_addr, u32 size_in_dwords,
struct qed_dmae_params *p_params)
{
u32 grc_addr_in_dw = grc_addr / sizeof(u32);
int rc;
mutex_lock(&p_hwfn->dmae_info.mutex);
rc = qed_dmae_execute_command(p_hwfn, p_ptt, source_addr,
grc_addr_in_dw,
QED_DMAE_ADDRESS_HOST_VIRT,
QED_DMAE_ADDRESS_GRC,
size_in_dwords, p_params);
mutex_unlock(&p_hwfn->dmae_info.mutex);
return rc;
}
int qed_dmae_grc2host(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 grc_addr,
dma_addr_t dest_addr, u32 size_in_dwords,
struct qed_dmae_params *p_params)
{
u32 grc_addr_in_dw = grc_addr / sizeof(u32);
int rc;
mutex_lock(&p_hwfn->dmae_info.mutex);
rc = qed_dmae_execute_command(p_hwfn, p_ptt, grc_addr_in_dw,
dest_addr, QED_DMAE_ADDRESS_GRC,
QED_DMAE_ADDRESS_HOST_VIRT,
size_in_dwords, p_params);
mutex_unlock(&p_hwfn->dmae_info.mutex);
return rc;
}
int qed_dmae_host2host(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
dma_addr_t source_addr,
dma_addr_t dest_addr,
u32 size_in_dwords, struct qed_dmae_params *p_params)
{
int rc;
mutex_lock(&(p_hwfn->dmae_info.mutex));
rc = qed_dmae_execute_command(p_hwfn, p_ptt, source_addr,
dest_addr,
QED_DMAE_ADDRESS_HOST_PHYS,
QED_DMAE_ADDRESS_HOST_PHYS,
size_in_dwords, p_params);
mutex_unlock(&(p_hwfn->dmae_info.mutex));
return rc;
}
void qed_hw_err_notify(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
enum qed_hw_err_type err_type, const char *fmt, ...)
{
char buf[QED_HW_ERR_MAX_STR_SIZE];
va_list vl;
int len;
if (fmt) {
va_start(vl, fmt);
len = vsnprintf(buf, QED_HW_ERR_MAX_STR_SIZE, fmt, vl);
va_end(vl);
if (len > QED_HW_ERR_MAX_STR_SIZE - 1)
len = QED_HW_ERR_MAX_STR_SIZE - 1;
DP_NOTICE(p_hwfn, "%s", buf);
}
/* Fan failure cannot be masked by handling of another HW error */
if (p_hwfn->cdev->recov_in_prog &&
err_type != QED_HW_ERR_FAN_FAIL) {
DP_VERBOSE(p_hwfn,
NETIF_MSG_DRV,
"Recovery is in progress. Avoid notifying about HW error %d.\n",
err_type);
return;
}
qed_hw_error_occurred(p_hwfn, err_type);
if (fmt)
qed_mcp_send_raw_debug_data(p_hwfn, p_ptt, buf, len);
}
int qed_dmae_sanity(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, const char *phase)
{
u32 size = PAGE_SIZE / 2, val;
int rc = 0;
dma_addr_t p_phys;
void *p_virt;
u32 *p_tmp;
p_virt = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
2 * size, &p_phys, GFP_KERNEL);
if (!p_virt) {
DP_NOTICE(p_hwfn,
"DMAE sanity [%s]: failed to allocate memory\n",
phase);
return -ENOMEM;
}
/* Fill the bottom half of the allocated memory with a known pattern */
for (p_tmp = (u32 *)p_virt;
p_tmp < (u32 *)((u8 *)p_virt + size); p_tmp++) {
/* Save the address itself as the value */
val = (u32)(uintptr_t)p_tmp;
*p_tmp = val;
}
/* Zero the top half of the allocated memory */
memset((u8 *)p_virt + size, 0, size);
DP_VERBOSE(p_hwfn,
QED_MSG_SP,
"DMAE sanity [%s]: src_addr={phys 0x%llx, virt %p}, dst_addr={phys 0x%llx, virt %p}, size 0x%x\n",
phase,
(u64)p_phys,
p_virt, (u64)(p_phys + size), (u8 *)p_virt + size, size);
rc = qed_dmae_host2host(p_hwfn, p_ptt, p_phys, p_phys + size,
size / 4, NULL);
if (rc) {
DP_NOTICE(p_hwfn,
"DMAE sanity [%s]: qed_dmae_host2host() failed. rc = %d.\n",
phase, rc);
goto out;
}
/* Verify that the top half of the allocated memory has the pattern */
for (p_tmp = (u32 *)((u8 *)p_virt + size);
p_tmp < (u32 *)((u8 *)p_virt + (2 * size)); p_tmp++) {
/* The corresponding address in the bottom half */
val = (u32)(uintptr_t)p_tmp - size;
if (*p_tmp != val) {
DP_NOTICE(p_hwfn,
"DMAE sanity [%s]: addr={phys 0x%llx, virt %p}, read_val 0x%08x, expected_val 0x%08x\n",
phase,
(u64)p_phys + ((u8 *)p_tmp - (u8 *)p_virt),
p_tmp, *p_tmp, val);
rc = -EINVAL;
goto out;
}
}
out:
dma_free_coherent(&p_hwfn->cdev->pdev->dev, 2 * size, p_virt, p_phys);
return rc;
}
@@ -0,0 +1,327 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_HW_H
#define _QED_HW_H
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "qed.h"
#include "qed_dev_api.h"
/* Forward decleration */
struct qed_ptt;
enum reserved_ptts {
RESERVED_PTT_EDIAG,
RESERVED_PTT_USER_SPACE,
RESERVED_PTT_MAIN,
RESERVED_PTT_DPC,
RESERVED_PTT_MAX
};
enum _dmae_cmd_dst_mask {
DMAE_CMD_DST_MASK_NONE = 0,
DMAE_CMD_DST_MASK_PCIE = 1,
DMAE_CMD_DST_MASK_GRC = 2
};
enum _dmae_cmd_src_mask {
DMAE_CMD_SRC_MASK_PCIE = 0,
DMAE_CMD_SRC_MASK_GRC = 1
};
enum _dmae_cmd_crc_mask {
DMAE_CMD_COMP_CRC_EN_MASK_NONE = 0,
DMAE_CMD_COMP_CRC_EN_MASK_SET = 1
};
/* definitions for DMA constants */
#define DMAE_GO_VALUE 0x1
#define DMAE_COMPLETION_VAL 0xD1AE
#define DMAE_CMD_ENDIANITY 0x2
#define DMAE_CMD_SIZE 14
#define DMAE_CMD_SIZE_TO_FILL (DMAE_CMD_SIZE - 5)
#define DMAE_MIN_WAIT_TIME 0x2
#define DMAE_MAX_CLIENTS 32
/**
* qed_gtt_init(): Initialize GTT windows.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_gtt_init(struct qed_hwfn *p_hwfn);
/**
* qed_ptt_pool_alloc(): Allocate and initialize PTT pool.
*
* @p_hwfn: HW device data.
*
* Return: struct _qed_status - success (0), negative - error.
*/
int qed_ptt_pool_alloc(struct qed_hwfn *p_hwfn);
/**
* qed_ptt_pool_free(): Free PTT pool.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_ptt_pool_free(struct qed_hwfn *p_hwfn);
/**
* qed_ptt_get_hw_addr(): Get PTT's GRC/HW address.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt
*
* Return: u32.
*/
u32 qed_ptt_get_hw_addr(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt);
/**
* qed_ptt_get_bar_addr(): Get PPT's external BAR address.
*
* @p_ptt: P_ptt
*
* Return: u32.
*/
u32 qed_ptt_get_bar_addr(struct qed_ptt *p_ptt);
/**
* qed_ptt_set_win(): Set PTT Window's GRC BAR address
*
* @p_hwfn: HW device data.
* @new_hw_addr: New HW address.
* @p_ptt: P_Ptt
*
* Return: Void.
*/
void qed_ptt_set_win(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 new_hw_addr);
/**
* qed_get_reserved_ptt(): Get a specific reserved PTT.
*
* @p_hwfn: HW device data.
* @ptt_idx: Ptt Index.
*
* Return: struct qed_ptt *.
*/
struct qed_ptt *qed_get_reserved_ptt(struct qed_hwfn *p_hwfn,
enum reserved_ptts ptt_idx);
/**
* qed_wr(): Write value to BAR using the given ptt.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @val: Val.
* @hw_addr: HW address
*
* Return: Void.
*/
void qed_wr(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 hw_addr,
u32 val);
/**
* qed_rd(): Read value from BAR using the given ptt.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @hw_addr: HW address
*
* Return: Void.
*/
u32 qed_rd(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 hw_addr);
/**
* qed_memcpy_from(): Copy n bytes from BAR using the given ptt.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @dest: Destination.
* @hw_addr: HW address.
* @n: N
*
* Return: Void.
*/
void qed_memcpy_from(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
void *dest,
u32 hw_addr,
size_t n);
/**
* qed_memcpy_to(): Copy n bytes to BAR using the given ptt
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @hw_addr: HW address.
* @src: Source.
* @n: N
*
* Return: Void.
*/
void qed_memcpy_to(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 hw_addr,
void *src,
size_t n);
/**
* qed_fid_pretend(): pretend to another function when
* accessing the ptt window. There is no way to unpretend
* a function. The only way to cancel a pretend is to
* pretend back to the original function.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @fid: fid field of pxp_pretend structure. Can contain
* either pf / vf, port/path fields are don't care.
*
* Return: Void.
*/
void qed_fid_pretend(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u16 fid);
/**
* qed_port_pretend(): Pretend to another port when accessing the ptt window
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @port_id: The port to pretend to
*
* Return: Void.
*/
void qed_port_pretend(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u8 port_id);
/**
* qed_port_unpretend(): Cancel any previously set port pretend
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
*
* Return: Void.
*/
void qed_port_unpretend(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt);
/**
* qed_port_fid_pretend(): Pretend to another port and another function
* when accessing the ptt window
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @port_id: The port to pretend to
* @fid: fid field of pxp_pretend structure. Can contain either pf / vf.
*
* Return: Void.
*/
void qed_port_fid_pretend(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, u8 port_id, u16 fid);
/**
* qed_vfid_to_concrete(): Build a concrete FID for a given VF ID
*
* @p_hwfn: HW device data.
* @vfid: VFID.
*
* Return: Void.
*/
u32 qed_vfid_to_concrete(struct qed_hwfn *p_hwfn, u8 vfid);
/**
* qed_dmae_idx_to_go_cmd(): Map the idx to dmae cmd
* this is declared here since other files will require it.
*
* @idx: Index
*
* Return: Void.
*/
u32 qed_dmae_idx_to_go_cmd(u8 idx);
/**
* qed_dmae_info_alloc(): Init the dmae_info structure
* which is part of p_hwfn.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_dmae_info_alloc(struct qed_hwfn *p_hwfn);
/**
* qed_dmae_info_free(): Free the dmae_info structure
* which is part of p_hwfn.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_dmae_info_free(struct qed_hwfn *p_hwfn);
union qed_qm_pq_params {
struct {
u8 q_idx;
} iscsi;
struct {
u8 tc;
} core;
struct {
u8 is_vf;
u8 vf_id;
u8 tc;
} eth;
struct {
u8 dcqcn;
u8 qpid; /* roce relative */
} roce;
};
int qed_init_fw_data(struct qed_dev *cdev,
const u8 *fw_data);
int qed_dmae_sanity(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, const char *phase);
#define QED_HW_ERR_MAX_STR_SIZE 256
/**
* qed_hw_err_notify(): Notify upper layer driver and management FW
* about a HW error.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @err_type: Err Type.
* @fmt: Debug data buffer to send to the MFW
* @...: buffer format args
*
* Return void.
*/
void __printf(4, 5) __cold qed_hw_err_notify(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
enum qed_hw_err_type err_type,
const char *fmt, ...);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,656 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#include <linux/types.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "qed.h"
#include "qed_hsi.h"
#include "qed_hw.h"
#include "qed_init_ops.h"
#include "qed_iro_hsi.h"
#include "qed_reg_addr.h"
#include "qed_sriov.h"
#define QED_INIT_MAX_POLL_COUNT 100
#define QED_INIT_POLL_PERIOD_US 500
static u32 pxp_global_win[] = {
0,
0,
0x1c02, /* win 2: addr=0x1c02000, size=4096 bytes */
0x1c80, /* win 3: addr=0x1c80000, size=4096 bytes */
0x1d00, /* win 4: addr=0x1d00000, size=4096 bytes */
0x1d01, /* win 5: addr=0x1d01000, size=4096 bytes */
0x1d02, /* win 6: addr=0x1d02000, size=4096 bytes */
0x1d80, /* win 7: addr=0x1d80000, size=4096 bytes */
0x1d81, /* win 8: addr=0x1d81000, size=4096 bytes */
0x1d82, /* win 9: addr=0x1d82000, size=4096 bytes */
0x1e00, /* win 10: addr=0x1e00000, size=4096 bytes */
0x1e01, /* win 11: addr=0x1e01000, size=4096 bytes */
0x1e80, /* win 12: addr=0x1e80000, size=4096 bytes */
0x1f00, /* win 13: addr=0x1f00000, size=4096 bytes */
0x1c08, /* win 14: addr=0x1c08000, size=4096 bytes */
0,
0,
0,
0,
};
/* IRO Array */
static const u32 iro_arr[] = {
0x00000000, 0x00000000, 0x00080000,
0x00004478, 0x00000008, 0x00080000,
0x00003288, 0x00000088, 0x00880000,
0x000058a8, 0x00000020, 0x00200000,
0x00003188, 0x00000008, 0x00080000,
0x00000b00, 0x00000008, 0x00040000,
0x00000a80, 0x00000008, 0x00040000,
0x00000000, 0x00000008, 0x00020000,
0x00000080, 0x00000008, 0x00040000,
0x00000084, 0x00000008, 0x00020000,
0x00005798, 0x00000004, 0x00040000,
0x00004e50, 0x00000000, 0x00780000,
0x00003e40, 0x00000000, 0x00780000,
0x00004500, 0x00000000, 0x00780000,
0x00003210, 0x00000000, 0x00780000,
0x00003b50, 0x00000000, 0x00780000,
0x00007f58, 0x00000000, 0x00780000,
0x00005fd8, 0x00000000, 0x00080000,
0x00007100, 0x00000000, 0x00080000,
0x0000af20, 0x00000000, 0x00080000,
0x00004398, 0x00000000, 0x00080000,
0x0000a5a0, 0x00000000, 0x00080000,
0x0000bde8, 0x00000000, 0x00080000,
0x00000020, 0x00000004, 0x00040000,
0x00005688, 0x00000010, 0x00100000,
0x0000c210, 0x00000030, 0x00300000,
0x0000b108, 0x00000038, 0x00380000,
0x00003d20, 0x00000080, 0x00400000,
0x0000bf60, 0x00000000, 0x00040000,
0x00004560, 0x00040080, 0x00040000,
0x000001f8, 0x00000004, 0x00040000,
0x00003d60, 0x00000080, 0x00200000,
0x00008960, 0x00000040, 0x00300000,
0x0000e840, 0x00000060, 0x00600000,
0x00004698, 0x00000080, 0x00380000,
0x000107b8, 0x000000c0, 0x00c00000,
0x000001f8, 0x00000002, 0x00020000,
0x0000a260, 0x00000000, 0x01080000,
0x0000a368, 0x00000008, 0x00080000,
0x000001c0, 0x00000008, 0x00080000,
0x000001f8, 0x00000008, 0x00080000,
0x00000ac0, 0x00000008, 0x00080000,
0x00002578, 0x00000008, 0x00080000,
0x000024f8, 0x00000008, 0x00080000,
0x00000280, 0x00000008, 0x00080000,
0x00000680, 0x00080018, 0x00080000,
0x00000b78, 0x00080018, 0x00020000,
0x0000c600, 0x00000058, 0x003c0000,
0x00012038, 0x00000020, 0x00100000,
0x00011b00, 0x00000048, 0x00180000,
0x00009650, 0x00000050, 0x00200000,
0x00008b10, 0x00000040, 0x00280000,
0x000116c0, 0x00000018, 0x00100000,
0x0000c808, 0x00000048, 0x00380000,
0x00011790, 0x00000020, 0x00200000,
0x000046d0, 0x00000080, 0x00100000,
0x00003618, 0x00000010, 0x00100000,
0x0000a9e8, 0x00000008, 0x00010000,
0x000097a0, 0x00000008, 0x00010000,
0x00011a10, 0x00000008, 0x00010000,
0x0000e9f8, 0x00000008, 0x00010000,
0x00012648, 0x00000008, 0x00010000,
0x000121c8, 0x00000008, 0x00010000,
0x0000af08, 0x00000030, 0x00100000,
0x0000d748, 0x00000028, 0x00280000,
0x00009e68, 0x00000018, 0x00180000,
0x00009fe8, 0x00000008, 0x00080000,
0x00013ea8, 0x00000008, 0x00080000,
0x00012f18, 0x00000018, 0x00180000,
0x0000dfe8, 0x00500288, 0x00100000,
0x000131a0, 0x00000138, 0x00280000,
};
void qed_init_iro_array(struct qed_dev *cdev)
{
cdev->iro_arr = iro_arr + E4_IRO_ARR_OFFSET;
}
void qed_init_store_rt_reg(struct qed_hwfn *p_hwfn, u32 rt_offset, u32 val)
{
if (rt_offset >= RUNTIME_ARRAY_SIZE) {
DP_ERR(p_hwfn,
"Avoid storing %u in rt_data at index %u!\n",
val, rt_offset);
return;
}
p_hwfn->rt_data.init_val[rt_offset] = val;
p_hwfn->rt_data.b_valid[rt_offset] = true;
}
void qed_init_store_rt_agg(struct qed_hwfn *p_hwfn,
u32 rt_offset, u32 *p_val, size_t size)
{
size_t i;
if ((rt_offset + size - 1) >= RUNTIME_ARRAY_SIZE) {
DP_ERR(p_hwfn,
"Avoid storing values in rt_data at indices %u-%u!\n",
rt_offset,
(u32)(rt_offset + size - 1));
return;
}
for (i = 0; i < size / sizeof(u32); i++) {
p_hwfn->rt_data.init_val[rt_offset + i] = p_val[i];
p_hwfn->rt_data.b_valid[rt_offset + i] = true;
}
}
static int qed_init_rt(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 addr, u16 rt_offset, u16 size, bool b_must_dmae)
{
u32 *p_init_val = &p_hwfn->rt_data.init_val[rt_offset];
bool *p_valid = &p_hwfn->rt_data.b_valid[rt_offset];
u16 i, j, segment;
int rc = 0;
/* Since not all RT entries are initialized, go over the RT and
* for each segment of initialized values use DMA.
*/
for (i = 0; i < size; i++) {
if (!p_valid[i])
continue;
/* In case there isn't any wide-bus configuration here,
* simply write the data instead of using dmae.
*/
if (!b_must_dmae) {
qed_wr(p_hwfn, p_ptt, addr + (i << 2), p_init_val[i]);
p_valid[i] = false;
continue;
}
/* Start of a new segment */
for (segment = 1; i + segment < size; segment++)
if (!p_valid[i + segment])
break;
rc = qed_dmae_host2grc(p_hwfn, p_ptt,
(uintptr_t)(p_init_val + i),
addr + (i << 2), segment, NULL);
if (rc)
return rc;
/* invalidate after writing */
for (j = i; j < (u32)(i + segment); j++)
p_valid[j] = false;
/* Jump over the entire segment, including invalid entry */
i += segment;
}
return rc;
}
int qed_init_alloc(struct qed_hwfn *p_hwfn)
{
struct qed_rt_data *rt_data = &p_hwfn->rt_data;
if (IS_VF(p_hwfn->cdev))
return 0;
rt_data->b_valid = kzalloc_objs(bool, RUNTIME_ARRAY_SIZE);
if (!rt_data->b_valid)
return -ENOMEM;
rt_data->init_val = kcalloc(RUNTIME_ARRAY_SIZE, sizeof(u32),
GFP_KERNEL);
if (!rt_data->init_val) {
kfree(rt_data->b_valid);
rt_data->b_valid = NULL;
return -ENOMEM;
}
return 0;
}
void qed_init_free(struct qed_hwfn *p_hwfn)
{
kfree(p_hwfn->rt_data.init_val);
p_hwfn->rt_data.init_val = NULL;
kfree(p_hwfn->rt_data.b_valid);
p_hwfn->rt_data.b_valid = NULL;
}
static int qed_init_array_dmae(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 addr,
u32 dmae_data_offset,
u32 size,
const u32 *buf,
bool b_must_dmae,
bool b_can_dmae)
{
int rc = 0;
/* Perform DMAE only for lengthy enough sections or for wide-bus */
if (!b_can_dmae || (!b_must_dmae && (size < 16))) {
const u32 *data = buf + dmae_data_offset;
u32 i;
for (i = 0; i < size; i++)
qed_wr(p_hwfn, p_ptt, addr + (i << 2), data[i]);
} else {
rc = qed_dmae_host2grc(p_hwfn, p_ptt,
(uintptr_t)(buf + dmae_data_offset),
addr, size, NULL);
}
return rc;
}
static int qed_init_fill_dmae(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 addr, u32 fill_count)
{
static u32 zero_buffer[DMAE_MAX_RW_SIZE];
struct qed_dmae_params params = {};
memset(zero_buffer, 0, sizeof(u32) * DMAE_MAX_RW_SIZE);
/* invoke the DMAE virtual/physical buffer API with
* 1. DMAE init channel
* 2. addr,
* 3. p_hwfb->temp_data,
* 4. fill_count
*/
SET_FIELD(params.flags, QED_DMAE_PARAMS_RW_REPL_SRC, 0x1);
return qed_dmae_host2grc(p_hwfn, p_ptt,
(uintptr_t)(&zero_buffer[0]),
addr, fill_count, &params);
}
static void qed_init_fill(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 addr, u32 fill, u32 fill_count)
{
u32 i;
for (i = 0; i < fill_count; i++, addr += sizeof(u32))
qed_wr(p_hwfn, p_ptt, addr, fill);
}
static int qed_init_cmd_array(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct init_write_op *cmd,
bool b_must_dmae, bool b_can_dmae)
{
u32 dmae_array_offset = le32_to_cpu(cmd->args.array_offset);
u32 data = le32_to_cpu(cmd->data);
u32 addr = GET_FIELD(data, INIT_WRITE_OP_ADDRESS) << 2;
u32 offset, output_len, input_len, max_size;
struct qed_dev *cdev = p_hwfn->cdev;
union init_array_hdr *hdr;
const u32 *array_data;
int rc = 0;
u32 size;
array_data = cdev->fw_data->arr_data;
hdr = (union init_array_hdr *)(array_data + dmae_array_offset);
data = le32_to_cpu(hdr->raw.data);
switch (GET_FIELD(data, INIT_ARRAY_RAW_HDR_TYPE)) {
case INIT_ARR_ZIPPED:
offset = dmae_array_offset + 1;
input_len = GET_FIELD(data,
INIT_ARRAY_ZIPPED_HDR_ZIPPED_SIZE);
max_size = MAX_ZIPPED_SIZE * 4;
memset(p_hwfn->unzip_buf, 0, max_size);
output_len = qed_unzip_data(p_hwfn, input_len,
(u8 *)&array_data[offset],
max_size, (u8 *)p_hwfn->unzip_buf);
if (output_len) {
rc = qed_init_array_dmae(p_hwfn, p_ptt, addr, 0,
output_len,
p_hwfn->unzip_buf,
b_must_dmae, b_can_dmae);
} else {
DP_NOTICE(p_hwfn, "Failed to unzip dmae data\n");
rc = -EINVAL;
}
break;
case INIT_ARR_PATTERN:
{
u32 repeats = GET_FIELD(data,
INIT_ARRAY_PATTERN_HDR_REPETITIONS);
u32 i;
size = GET_FIELD(data, INIT_ARRAY_PATTERN_HDR_PATTERN_SIZE);
for (i = 0; i < repeats; i++, addr += size << 2) {
rc = qed_init_array_dmae(p_hwfn, p_ptt, addr,
dmae_array_offset + 1,
size, array_data,
b_must_dmae, b_can_dmae);
if (rc)
break;
}
break;
}
case INIT_ARR_STANDARD:
size = GET_FIELD(data, INIT_ARRAY_STANDARD_HDR_SIZE);
rc = qed_init_array_dmae(p_hwfn, p_ptt, addr,
dmae_array_offset + 1,
size, array_data,
b_must_dmae, b_can_dmae);
break;
}
return rc;
}
/* init_ops write command */
static int qed_init_cmd_wr(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct init_write_op *p_cmd, bool b_can_dmae)
{
u32 data = le32_to_cpu(p_cmd->data);
bool b_must_dmae = GET_FIELD(data, INIT_WRITE_OP_WIDE_BUS);
u32 addr = GET_FIELD(data, INIT_WRITE_OP_ADDRESS) << 2;
union init_write_args *arg = &p_cmd->args;
int rc = 0;
/* Sanitize */
if (b_must_dmae && !b_can_dmae) {
DP_NOTICE(p_hwfn,
"Need to write to %08x for Wide-bus but DMAE isn't allowed\n",
addr);
return -EINVAL;
}
switch (GET_FIELD(data, INIT_WRITE_OP_SOURCE)) {
case INIT_SRC_INLINE:
data = le32_to_cpu(p_cmd->args.inline_val);
qed_wr(p_hwfn, p_ptt, addr, data);
break;
case INIT_SRC_ZEROS:
data = le32_to_cpu(p_cmd->args.zeros_count);
if (b_must_dmae || (b_can_dmae && (data >= 64)))
rc = qed_init_fill_dmae(p_hwfn, p_ptt, addr, data);
else
qed_init_fill(p_hwfn, p_ptt, addr, 0, data);
break;
case INIT_SRC_ARRAY:
rc = qed_init_cmd_array(p_hwfn, p_ptt, p_cmd,
b_must_dmae, b_can_dmae);
break;
case INIT_SRC_RUNTIME:
qed_init_rt(p_hwfn, p_ptt, addr,
le16_to_cpu(arg->runtime.offset),
le16_to_cpu(arg->runtime.size),
b_must_dmae);
break;
}
return rc;
}
static inline bool comp_eq(u32 val, u32 expected_val)
{
return val == expected_val;
}
static inline bool comp_and(u32 val, u32 expected_val)
{
return (val & expected_val) == expected_val;
}
static inline bool comp_or(u32 val, u32 expected_val)
{
return (val | expected_val) > 0;
}
/* init_ops read/poll commands */
static void qed_init_cmd_rd(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, struct init_read_op *cmd)
{
bool (*comp_check)(u32 val, u32 expected_val);
u32 delay = QED_INIT_POLL_PERIOD_US, val;
u32 data, addr, poll;
int i;
data = le32_to_cpu(cmd->op_data);
addr = GET_FIELD(data, INIT_READ_OP_ADDRESS) << 2;
poll = GET_FIELD(data, INIT_READ_OP_POLL_TYPE);
val = qed_rd(p_hwfn, p_ptt, addr);
if (poll == INIT_POLL_NONE)
return;
switch (poll) {
case INIT_POLL_EQ:
comp_check = comp_eq;
break;
case INIT_POLL_OR:
comp_check = comp_or;
break;
case INIT_POLL_AND:
comp_check = comp_and;
break;
default:
DP_ERR(p_hwfn, "Invalid poll comparison type %08x\n",
cmd->op_data);
return;
}
data = le32_to_cpu(cmd->expected_val);
for (i = 0;
i < QED_INIT_MAX_POLL_COUNT && !comp_check(val, data);
i++) {
udelay(delay);
val = qed_rd(p_hwfn, p_ptt, addr);
}
if (i == QED_INIT_MAX_POLL_COUNT) {
DP_ERR(p_hwfn,
"Timeout when polling reg: 0x%08x [ Waiting-for: %08x Got: %08x (comparison %08x)]\n",
addr, le32_to_cpu(cmd->expected_val),
val, le32_to_cpu(cmd->op_data));
}
}
/* init_ops callbacks entry point */
static int qed_init_cmd_cb(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct init_callback_op *p_cmd)
{
int rc;
switch (p_cmd->callback_id) {
case DMAE_READY_CB:
rc = qed_dmae_sanity(p_hwfn, p_ptt, "engine_phase");
break;
default:
DP_NOTICE(p_hwfn, "Unexpected init op callback ID %d\n",
p_cmd->callback_id);
return -EINVAL;
}
return rc;
}
static u8 qed_init_cmd_mode_match(struct qed_hwfn *p_hwfn,
u16 *p_offset, int modes)
{
struct qed_dev *cdev = p_hwfn->cdev;
const u8 *modes_tree_buf;
u8 arg1, arg2, tree_val;
modes_tree_buf = cdev->fw_data->modes_tree_buf;
tree_val = modes_tree_buf[(*p_offset)++];
switch (tree_val) {
case INIT_MODE_OP_NOT:
return qed_init_cmd_mode_match(p_hwfn, p_offset, modes) ^ 1;
case INIT_MODE_OP_OR:
arg1 = qed_init_cmd_mode_match(p_hwfn, p_offset, modes);
arg2 = qed_init_cmd_mode_match(p_hwfn, p_offset, modes);
return arg1 | arg2;
case INIT_MODE_OP_AND:
arg1 = qed_init_cmd_mode_match(p_hwfn, p_offset, modes);
arg2 = qed_init_cmd_mode_match(p_hwfn, p_offset, modes);
return arg1 & arg2;
default:
tree_val -= MAX_INIT_MODE_OPS;
return (modes & BIT(tree_val)) ? 1 : 0;
}
}
static u32 qed_init_cmd_mode(struct qed_hwfn *p_hwfn,
struct init_if_mode_op *p_cmd, int modes)
{
u16 offset = le16_to_cpu(p_cmd->modes_buf_offset);
if (qed_init_cmd_mode_match(p_hwfn, &offset, modes))
return 0;
else
return GET_FIELD(le32_to_cpu(p_cmd->op_data),
INIT_IF_MODE_OP_CMD_OFFSET);
}
static u32 qed_init_cmd_phase(struct init_if_phase_op *p_cmd,
u32 phase, u32 phase_id)
{
u32 data = le32_to_cpu(p_cmd->phase_data);
u32 op_data = le32_to_cpu(p_cmd->op_data);
if (!(GET_FIELD(data, INIT_IF_PHASE_OP_PHASE) == phase &&
(GET_FIELD(data, INIT_IF_PHASE_OP_PHASE_ID) == ANY_PHASE_ID ||
GET_FIELD(data, INIT_IF_PHASE_OP_PHASE_ID) == phase_id)))
return GET_FIELD(op_data, INIT_IF_PHASE_OP_CMD_OFFSET);
else
return 0;
}
int qed_init_run(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, int phase, int phase_id, int modes)
{
bool b_dmae = (phase != PHASE_ENGINE);
struct qed_dev *cdev = p_hwfn->cdev;
u32 cmd_num, num_init_ops;
union init_op *init_ops;
int rc = 0;
num_init_ops = cdev->fw_data->init_ops_size;
init_ops = cdev->fw_data->init_ops;
p_hwfn->unzip_buf = kzalloc(MAX_ZIPPED_SIZE * 4, GFP_ATOMIC);
if (!p_hwfn->unzip_buf)
return -ENOMEM;
for (cmd_num = 0; cmd_num < num_init_ops; cmd_num++) {
union init_op *cmd = &init_ops[cmd_num];
u32 data = le32_to_cpu(cmd->raw.op_data);
switch (GET_FIELD(data, INIT_CALLBACK_OP_OP)) {
case INIT_OP_WRITE:
rc = qed_init_cmd_wr(p_hwfn, p_ptt, &cmd->write,
b_dmae);
break;
case INIT_OP_READ:
qed_init_cmd_rd(p_hwfn, p_ptt, &cmd->read);
break;
case INIT_OP_IF_MODE:
cmd_num += qed_init_cmd_mode(p_hwfn, &cmd->if_mode,
modes);
break;
case INIT_OP_IF_PHASE:
cmd_num += qed_init_cmd_phase(&cmd->if_phase,
phase, phase_id);
break;
case INIT_OP_DELAY:
/* qed_init_run is always invoked from
* sleep-able context
*/
udelay(le32_to_cpu(cmd->delay.delay));
break;
case INIT_OP_CALLBACK:
rc = qed_init_cmd_cb(p_hwfn, p_ptt, &cmd->callback);
if (phase == PHASE_ENGINE &&
cmd->callback.callback_id == DMAE_READY_CB)
b_dmae = true;
break;
}
if (rc)
break;
}
kfree(p_hwfn->unzip_buf);
p_hwfn->unzip_buf = NULL;
return rc;
}
void qed_gtt_init(struct qed_hwfn *p_hwfn)
{
u32 gtt_base;
u32 i;
/* Set the global windows */
gtt_base = PXP_PF_WINDOW_ADMIN_START + PXP_PF_WINDOW_ADMIN_GLOBAL_START;
for (i = 0; i < ARRAY_SIZE(pxp_global_win); i++)
if (pxp_global_win[i])
REG_WR(p_hwfn, gtt_base + i * PXP_GLOBAL_ENTRY_SIZE,
pxp_global_win[i]);
}
int qed_init_fw_data(struct qed_dev *cdev, const u8 *data)
{
struct qed_fw_data *fw = cdev->fw_data;
struct bin_buffer_hdr *buf_hdr;
u32 offset, len;
if (!data) {
DP_NOTICE(cdev, "Invalid fw data\n");
return -EINVAL;
}
/* First Dword contains metadata and should be skipped */
buf_hdr = (struct bin_buffer_hdr *)data;
offset = buf_hdr[BIN_BUF_INIT_FW_VER_INFO].offset;
fw->fw_ver_info = (struct fw_ver_info *)(data + offset);
offset = buf_hdr[BIN_BUF_INIT_CMD].offset;
fw->init_ops = (union init_op *)(data + offset);
offset = buf_hdr[BIN_BUF_INIT_VAL].offset;
fw->arr_data = (u32 *)(data + offset);
offset = buf_hdr[BIN_BUF_INIT_MODE_TREE].offset;
fw->modes_tree_buf = (u8 *)(data + offset);
len = buf_hdr[BIN_BUF_INIT_CMD].length;
fw->init_ops_size = len / sizeof(struct init_raw_op);
offset = buf_hdr[BIN_BUF_INIT_OVERLAYS].offset;
fw->fw_overlays = (u32 *)(data + offset);
len = buf_hdr[BIN_BUF_INIT_OVERLAYS].length;
fw->fw_overlays_len = len;
return 0;
}
@@ -0,0 +1,94 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_INIT_OPS_H
#define _QED_INIT_OPS_H
#include <linux/types.h>
#include <linux/slab.h>
#include "qed.h"
/**
* qed_init_iro_array(): init iro_arr.
*
* @cdev: Qed dev pointer.
*
* Return: Void.
*/
void qed_init_iro_array(struct qed_dev *cdev);
/**
* qed_init_run(): Run the init-sequence.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @phase: Phase.
* @phase_id: Phase ID.
* @modes: Mode.
*
* Return: _qed_status_t
*/
int qed_init_run(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
int phase,
int phase_id,
int modes);
/**
* qed_init_alloc(): Allocate RT array, Store 'values' ptrs.
*
* @p_hwfn: HW device data.
*
* Return: _qed_status_t.
*/
int qed_init_alloc(struct qed_hwfn *p_hwfn);
/**
* qed_init_free(): Init HW function deallocate.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_init_free(struct qed_hwfn *p_hwfn);
/**
* qed_init_store_rt_reg(): Store a configuration value in the RT array.
*
* @p_hwfn: HW device data.
* @rt_offset: RT offset.
* @val: Val.
*
* Return: Void.
*/
void qed_init_store_rt_reg(struct qed_hwfn *p_hwfn,
u32 rt_offset,
u32 val);
#define STORE_RT_REG(hwfn, offset, val) \
qed_init_store_rt_reg(hwfn, offset, val)
#define OVERWRITE_RT_REG(hwfn, offset, val) \
qed_init_store_rt_reg(hwfn, offset, val)
void qed_init_store_rt_agg(struct qed_hwfn *p_hwfn,
u32 rt_offset,
u32 *val,
size_t size);
#define STORE_RT_REG_AGG(hwfn, offset, val) \
qed_init_store_rt_agg(hwfn, offset, (u32 *)&(val), sizeof(val))
/**
* qed_gtt_init(): Initialize GTT global windows and set admin window
* related params of GTT/PTT to default values.
*
* @p_hwfn: HW device data.
*
* Return Void.
*/
void qed_gtt_init(struct qed_hwfn *p_hwfn);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,459 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_INT_H
#define _QED_INT_H
#include <linux/types.h>
#include <linux/slab.h>
#include "qed.h"
/* Fields of IGU PF CONFIGURATION REGISTER */
#define IGU_PF_CONF_FUNC_EN (0x1 << 0) /* function enable */
#define IGU_PF_CONF_MSI_MSIX_EN (0x1 << 1) /* MSI/MSIX enable */
#define IGU_PF_CONF_INT_LINE_EN (0x1 << 2) /* INT enable */
#define IGU_PF_CONF_ATTN_BIT_EN (0x1 << 3) /* attention enable */
#define IGU_PF_CONF_SINGLE_ISR_EN (0x1 << 4) /* single ISR mode enable */
#define IGU_PF_CONF_SIMD_MODE (0x1 << 5) /* simd all ones mode */
/* Fields of IGU VF CONFIGURATION REGISTER */
#define IGU_VF_CONF_FUNC_EN (0x1 << 0) /* function enable */
#define IGU_VF_CONF_MSI_MSIX_EN (0x1 << 1) /* MSI/MSIX enable */
#define IGU_VF_CONF_SINGLE_ISR_EN (0x1 << 4) /* single ISR mode enable */
#define IGU_VF_CONF_PARENT_MASK (0xF) /* Parent PF */
#define IGU_VF_CONF_PARENT_SHIFT 5 /* Parent PF */
/* Igu control commands
*/
enum igu_ctrl_cmd {
IGU_CTRL_CMD_TYPE_RD,
IGU_CTRL_CMD_TYPE_WR,
MAX_IGU_CTRL_CMD
};
/* Control register for the IGU command register
*/
struct igu_ctrl_reg {
u32 ctrl_data;
#define IGU_CTRL_REG_FID_MASK 0xFFFF /* Opaque_FID */
#define IGU_CTRL_REG_FID_SHIFT 0
#define IGU_CTRL_REG_PXP_ADDR_MASK 0xFFF /* Command address */
#define IGU_CTRL_REG_PXP_ADDR_SHIFT 16
#define IGU_CTRL_REG_RESERVED_MASK 0x1
#define IGU_CTRL_REG_RESERVED_SHIFT 28
#define IGU_CTRL_REG_TYPE_MASK 0x1 /* use enum igu_ctrl_cmd */
#define IGU_CTRL_REG_TYPE_SHIFT 31
};
enum qed_coalescing_fsm {
QED_COAL_RX_STATE_MACHINE,
QED_COAL_TX_STATE_MACHINE
};
/**
* qed_int_igu_enable_int(): Enable device interrupts.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @int_mode: Interrupt mode to use.
*
* Return: Void.
*/
void qed_int_igu_enable_int(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
enum qed_int_mode int_mode);
/**
* qed_int_igu_disable_int(): Disable device interrupts.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
*
* Return: Void.
*/
void qed_int_igu_disable_int(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt);
/**
* qed_int_igu_read_sisr_reg(): Reads the single isr multiple dpc
* register from igu.
*
* @p_hwfn: HW device data.
*
* Return: u64.
*/
u64 qed_int_igu_read_sisr_reg(struct qed_hwfn *p_hwfn);
#define QED_SP_SB_ID 0xffff
/**
* qed_int_sb_init(): Initializes the sb_info structure.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @sb_info: points to an uninitialized (but allocated) sb_info structure
* @sb_virt_addr: SB Virtual address.
* @sb_phy_addr: SB Physial address.
* @sb_id: the sb_id to be used (zero based in driver)
* should use QED_SP_SB_ID for SP Status block
*
* Return: int.
*
* Once the structure is initialized it can be passed to sb related functions.
*/
int qed_int_sb_init(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_sb_info *sb_info,
void *sb_virt_addr,
dma_addr_t sb_phy_addr,
u16 sb_id);
/**
* qed_int_sb_setup(): Setup the sb.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @sb_info: Initialized sb_info structure.
*
* Return: Void.
*/
void qed_int_sb_setup(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_sb_info *sb_info);
/**
* qed_int_sb_release(): Releases the sb_info structure.
*
* @p_hwfn: HW device data.
* @sb_info: Points to an allocated sb_info structure.
* @sb_id: The sb_id to be used (zero based in driver)
* should never be equal to QED_SP_SB_ID
* (SP Status block).
*
* Return: int.
*
* Once the structure is released, it's memory can be freed.
*/
int qed_int_sb_release(struct qed_hwfn *p_hwfn,
struct qed_sb_info *sb_info,
u16 sb_id);
/**
* qed_int_sp_dpc(): To be called when an interrupt is received on the
* default status block.
*
* @t: Tasklet.
*
* Return: Void.
*
*/
void qed_int_sp_dpc(struct tasklet_struct *t);
/**
* qed_int_get_num_sbs(): Get the number of status blocks configured
* for this funciton in the igu.
*
* @p_hwfn: HW device data.
* @p_sb_cnt_info: Pointer to SB count info.
*
* Return: Void.
*/
void qed_int_get_num_sbs(struct qed_hwfn *p_hwfn,
struct qed_sb_cnt_info *p_sb_cnt_info);
/**
* qed_int_disable_post_isr_release(): Performs the cleanup post ISR
* release. The API need to be called after releasing all slowpath IRQs
* of the device.
*
* @cdev: Qed dev pointer.
*
* Return: Void.
*/
void qed_int_disable_post_isr_release(struct qed_dev *cdev);
/**
* qed_int_attn_clr_enable: Sets whether the general behavior is
* preventing attentions from being reasserted, or following the
* attributes of the specific attention.
*
* @cdev: Qed dev pointer.
* @clr_enable: Clear enable
*
* Return: Void.
*
*/
void qed_int_attn_clr_enable(struct qed_dev *cdev, bool clr_enable);
/**
* qed_int_get_sb_dbg: Read debug information regarding a given SB
*
* @p_hwfn: hw function pointer
* @p_ptt: ptt resource
* @p_sb: pointer to status block for which we want to get info
* @p_info: pointer to struct to fill with information regarding SB
*
* Return: 0 with status block info filled on success, otherwise return error
*/
int qed_int_get_sb_dbg(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
struct qed_sb_info *p_sb, struct qed_sb_info_dbg *p_info);
/**
* qed_db_rec_handler(): Doorbell Recovery handler.
* Run doorbell recovery in case of PF overflow (and flush DORQ if
* needed).
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
*
* Return: Int.
*/
int qed_db_rec_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
#define QED_CAU_DEF_RX_TIMER_RES 0
#define QED_CAU_DEF_TX_TIMER_RES 0
#define QED_SB_ATT_IDX 0x0001
#define QED_SB_EVENT_MASK 0x0003
#define SB_ALIGNED_SIZE(p_hwfn) \
ALIGNED_TYPE_SIZE(struct status_block, p_hwfn)
#define QED_SB_INVALID_IDX 0xffff
struct qed_igu_block {
u8 status;
#define QED_IGU_STATUS_FREE 0x01
#define QED_IGU_STATUS_VALID 0x02
#define QED_IGU_STATUS_PF 0x04
#define QED_IGU_STATUS_DSB 0x08
u8 vector_number;
u8 function_id;
u8 is_pf;
/* Index inside IGU [meant for back reference] */
u16 igu_sb_id;
struct qed_sb_info *sb_info;
};
struct qed_igu_info {
struct qed_igu_block entry[MAX_TOT_SB_PER_PATH];
u16 igu_dsb_id;
struct qed_sb_cnt_info usage;
bool b_allow_pf_vf_change;
};
/**
* qed_int_igu_reset_cam(): Make sure the IGU CAM reflects the resources
* provided by MFW.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
*
* Return: Void.
*/
int qed_int_igu_reset_cam(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
/**
* qed_get_igu_sb_id(): Translate the weakly-defined client sb-id into
* an IGU sb-id
*
* @p_hwfn: HW device data.
* @sb_id: user provided sb_id.
*
* Return: An index inside IGU CAM where the SB resides.
*/
u16 qed_get_igu_sb_id(struct qed_hwfn *p_hwfn, u16 sb_id);
/**
* qed_get_igu_free_sb(): Return a pointer to an unused valid SB
*
* @p_hwfn: HW device data.
* @b_is_pf: True iff we want a SB belonging to a PF.
*
* Return: Point to an igu_block, NULL if none is available.
*/
struct qed_igu_block *qed_get_igu_free_sb(struct qed_hwfn *p_hwfn,
bool b_is_pf);
void qed_int_igu_init_pure_rt(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
bool b_set,
bool b_slowpath);
void qed_int_igu_init_rt(struct qed_hwfn *p_hwfn);
/**
* qed_int_igu_read_cam(): Reads the IGU CAM.
* This function needs to be called during hardware
* prepare. It reads the info from igu cam to know which
* status block is the default / base status block etc.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
*
* Return: Int.
*/
int qed_int_igu_read_cam(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt);
typedef int (*qed_int_comp_cb_t)(struct qed_hwfn *p_hwfn,
void *cookie);
/**
* qed_int_register_cb(): Register callback func for slowhwfn statusblock.
*
* @p_hwfn: HW device data.
* @comp_cb: Function to be called when there is an
* interrupt on the sp sb
* @cookie: Passed to the callback function
* @sb_idx: (OUT) parameter which gives the chosen index
* for this protocol.
* @p_fw_cons: Pointer to the actual address of the
* consumer for this protocol.
*
* Return: Int.
*
* Every protocol that uses the slowhwfn status block
* should register a callback function that will be called
* once there is an update of the sp status block.
*/
int qed_int_register_cb(struct qed_hwfn *p_hwfn,
qed_int_comp_cb_t comp_cb,
void *cookie,
u8 *sb_idx,
__le16 **p_fw_cons);
/**
* qed_int_unregister_cb(): Unregisters callback function from sp sb.
*
* @p_hwfn: HW device data.
* @pi: Producer Index.
*
* Return: Int.
*
* Partner of qed_int_register_cb -> should be called
* when no longer required.
*/
int qed_int_unregister_cb(struct qed_hwfn *p_hwfn,
u8 pi);
/**
* qed_int_get_sp_sb_id(): Get the slowhwfn sb id.
*
* @p_hwfn: HW device data.
*
* Return: u16.
*/
u16 qed_int_get_sp_sb_id(struct qed_hwfn *p_hwfn);
/**
* qed_int_igu_init_pure_rt_single(): Status block cleanup.
* Should be called for each status
* block that will be used -> both PF / VF.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @igu_sb_id: IGU status block id.
* @opaque: Opaque fid of the sb owner.
* @b_set: Set(1) / Clear(0).
*
* Return: Void.
*/
void qed_int_igu_init_pure_rt_single(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u16 igu_sb_id,
u16 opaque,
bool b_set);
/**
* qed_int_cau_conf_sb(): Configure cau for a given status block.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @sb_phys: SB Physical.
* @igu_sb_id: IGU status block id.
* @vf_number: VF number
* @vf_valid: VF valid or not.
*
* Return: Void.
*/
void qed_int_cau_conf_sb(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
dma_addr_t sb_phys,
u16 igu_sb_id,
u16 vf_number,
u8 vf_valid);
/**
* qed_int_alloc(): QED interrupt alloc.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
*
* Return: Int.
*/
int qed_int_alloc(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt);
/**
* qed_int_free(): QED interrupt free.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_int_free(struct qed_hwfn *p_hwfn);
/**
* qed_int_setup(): QED interrupt setup.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
*
* Return: Void.
*/
void qed_int_setup(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt);
/**
* qed_int_igu_enable(): Enable Interrupt & Attention for hw function.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @int_mode: Interrut mode
*
* Return: Int.
*/
int qed_int_igu_enable(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
enum qed_int_mode int_mode);
/**
* qed_init_cau_sb_entry(): Initialize CAU status block entry.
*
* @p_hwfn: HW device data.
* @p_sb_entry: Pointer SB entry.
* @pf_id: PF number
* @vf_number: VF number
* @vf_valid: VF valid or not.
*
* Return: Void.
*/
void qed_init_cau_sb_entry(struct qed_hwfn *p_hwfn,
struct cau_sb_entry *p_sb_entry,
u8 pf_id,
u16 vf_number,
u8 vf_valid);
int qed_int_set_timer_res(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
u8 timer_res, u16 sb_id, bool tx);
#define QED_MAPPING_MEMORY_SIZE(dev) (NUM_OF_SBS(dev))
int qed_pglueb_rbc_attn_handler(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
bool hw_init);
#endif
@@ -0,0 +1,500 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2019-2021 Marvell International Ltd.
*/
#ifndef _QED_IRO_HSI_H
#define _QED_IRO_HSI_H
#include <linux/types.h>
enum {
IRO_YSTORM_FLOW_CONTROL_MODE_GTT,
IRO_PSTORM_PKT_DUPLICATION_CFG,
IRO_TSTORM_PORT_STAT,
IRO_TSTORM_LL2_PORT_STAT,
IRO_TSTORM_PKT_DUPLICATION_CFG,
IRO_USTORM_VF_PF_CHANNEL_READY_GTT,
IRO_USTORM_FLR_FINAL_ACK_GTT,
IRO_USTORM_EQE_CONS_GTT,
IRO_USTORM_ETH_QUEUE_ZONE_GTT,
IRO_USTORM_COMMON_QUEUE_CONS_GTT,
IRO_XSTORM_PQ_INFO,
IRO_XSTORM_INTEG_TEST_DATA,
IRO_YSTORM_INTEG_TEST_DATA,
IRO_PSTORM_INTEG_TEST_DATA,
IRO_TSTORM_INTEG_TEST_DATA,
IRO_MSTORM_INTEG_TEST_DATA,
IRO_USTORM_INTEG_TEST_DATA,
IRO_XSTORM_OVERLAY_BUF_ADDR,
IRO_YSTORM_OVERLAY_BUF_ADDR,
IRO_PSTORM_OVERLAY_BUF_ADDR,
IRO_TSTORM_OVERLAY_BUF_ADDR,
IRO_MSTORM_OVERLAY_BUF_ADDR,
IRO_USTORM_OVERLAY_BUF_ADDR,
IRO_TSTORM_LL2_RX_PRODS_GTT,
IRO_CORE_LL2_TSTORM_PER_QUEUE_STAT,
IRO_CORE_LL2_USTORM_PER_QUEUE_STAT,
IRO_CORE_LL2_PSTORM_PER_QUEUE_STAT,
IRO_MSTORM_QUEUE_STAT,
IRO_MSTORM_TPA_TIMEOUT_US,
IRO_MSTORM_ETH_VF_PRODS,
IRO_MSTORM_ETH_PF_PRODS_GTT,
IRO_MSTORM_ETH_PF_STAT,
IRO_USTORM_QUEUE_STAT,
IRO_USTORM_ETH_PF_STAT,
IRO_PSTORM_QUEUE_STAT,
IRO_PSTORM_ETH_PF_STAT,
IRO_PSTORM_CTL_FRAME_ETHTYPE_GTT,
IRO_TSTORM_ETH_PRS_INPUT,
IRO_ETH_RX_RATE_LIMIT,
IRO_TSTORM_ETH_RSS_UPDATE_GTT,
IRO_XSTORM_ETH_QUEUE_ZONE_GTT,
IRO_YSTORM_TOE_CQ_PROD,
IRO_USTORM_TOE_CQ_PROD,
IRO_USTORM_TOE_GRQ_PROD,
IRO_TSTORM_SCSI_CMDQ_CONS_GTT,
IRO_TSTORM_SCSI_BDQ_EXT_PROD_GTT,
IRO_MSTORM_SCSI_BDQ_EXT_PROD_GTT,
IRO_TSTORM_ISCSI_RX_STATS,
IRO_MSTORM_ISCSI_RX_STATS,
IRO_USTORM_ISCSI_RX_STATS,
IRO_XSTORM_ISCSI_TX_STATS,
IRO_YSTORM_ISCSI_TX_STATS,
IRO_PSTORM_ISCSI_TX_STATS,
IRO_TSTORM_FCOE_RX_STATS,
IRO_PSTORM_FCOE_TX_STATS,
IRO_PSTORM_RDMA_QUEUE_STAT,
IRO_TSTORM_RDMA_QUEUE_STAT,
IRO_XSTORM_RDMA_ASSERT_LEVEL,
IRO_YSTORM_RDMA_ASSERT_LEVEL,
IRO_PSTORM_RDMA_ASSERT_LEVEL,
IRO_TSTORM_RDMA_ASSERT_LEVEL,
IRO_MSTORM_RDMA_ASSERT_LEVEL,
IRO_USTORM_RDMA_ASSERT_LEVEL,
IRO_XSTORM_IWARP_RXMIT_STATS,
IRO_TSTORM_ROCE_EVENTS_STAT,
IRO_YSTORM_ROCE_DCQCN_RECEIVED_STATS,
IRO_YSTORM_ROCE_ERROR_STATS,
IRO_PSTORM_ROCE_DCQCN_SENT_STATS,
IRO_USTORM_ROCE_CQE_STATS,
};
/* Pstorm LiteL2 queue statistics */
#define CORE_LL2_PSTORM_PER_QUEUE_STAT_OFFSET(core_tx_stats_id) \
(IRO[IRO_CORE_LL2_PSTORM_PER_QUEUE_STAT].base \
+ ((core_tx_stats_id) * IRO[IRO_CORE_LL2_PSTORM_PER_QUEUE_STAT].m1))
#define CORE_LL2_PSTORM_PER_QUEUE_STAT_SIZE \
(IRO[IRO_CORE_LL2_PSTORM_PER_QUEUE_STAT].size)
/* Tstorm LightL2 queue statistics */
#define CORE_LL2_TSTORM_PER_QUEUE_STAT_OFFSET(core_rx_queue_id) \
(IRO[IRO_CORE_LL2_TSTORM_PER_QUEUE_STAT].base \
+ ((core_rx_queue_id) * IRO[IRO_CORE_LL2_TSTORM_PER_QUEUE_STAT].m1))
#define CORE_LL2_TSTORM_PER_QUEUE_STAT_SIZE \
(IRO[IRO_CORE_LL2_TSTORM_PER_QUEUE_STAT].size)
/* Ustorm LiteL2 queue statistics */
#define CORE_LL2_USTORM_PER_QUEUE_STAT_OFFSET(core_rx_queue_id) \
(IRO[IRO_CORE_LL2_USTORM_PER_QUEUE_STAT].base \
+ ((core_rx_queue_id) * IRO[IRO_CORE_LL2_USTORM_PER_QUEUE_STAT].m1))
#define CORE_LL2_USTORM_PER_QUEUE_STAT_SIZE \
(IRO[IRO_CORE_LL2_USTORM_PER_QUEUE_STAT].size)
/* Tstorm Eth limit Rx rate */
#define ETH_RX_RATE_LIMIT_OFFSET(pf_id) \
(IRO[IRO_ETH_RX_RATE_LIMIT].base \
+ ((pf_id) * IRO[IRO_ETH_RX_RATE_LIMIT].m1))
#define ETH_RX_RATE_LIMIT_SIZE (IRO[IRO_ETH_RX_RATE_LIMIT].size)
/* Mstorm ETH PF queues producers */
#define MSTORM_ETH_PF_PRODS_GTT_OFFSET(queue_id) \
(IRO[IRO_MSTORM_ETH_PF_PRODS_GTT].base \
+ ((queue_id) * IRO[IRO_MSTORM_ETH_PF_PRODS_GTT].m1))
#define MSTORM_ETH_PF_PRODS_GTT_SIZE (IRO[IRO_MSTORM_ETH_PF_PRODS_GTT].size)
/* Mstorm pf statistics */
#define MSTORM_ETH_PF_STAT_OFFSET(pf_id) \
(IRO[IRO_MSTORM_ETH_PF_STAT].base \
+ ((pf_id) * IRO[IRO_MSTORM_ETH_PF_STAT].m1))
#define MSTORM_ETH_PF_STAT_SIZE (IRO[IRO_MSTORM_ETH_PF_STAT].size)
/* Mstorm ETH VF queues producers offset in RAM. Used in default VF zone
* size mode.
*/
#define MSTORM_ETH_VF_PRODS_OFFSET(vf_id, vf_queue_id) \
(IRO[IRO_MSTORM_ETH_VF_PRODS].base \
+ ((vf_id) * IRO[IRO_MSTORM_ETH_VF_PRODS].m1) \
+ ((vf_queue_id) * IRO[IRO_MSTORM_ETH_VF_PRODS].m2))
#define MSTORM_ETH_VF_PRODS_SIZE (IRO[IRO_MSTORM_ETH_VF_PRODS].size)
/* Mstorm Integration Test Data */
#define MSTORM_INTEG_TEST_DATA_OFFSET (IRO[IRO_MSTORM_INTEG_TEST_DATA].base)
#define MSTORM_INTEG_TEST_DATA_SIZE (IRO[IRO_MSTORM_INTEG_TEST_DATA].size)
/* Mstorm iSCSI RX stats */
#define MSTORM_ISCSI_RX_STATS_OFFSET(storage_func_id) \
(IRO[IRO_MSTORM_ISCSI_RX_STATS].base \
+ ((storage_func_id) * IRO[IRO_MSTORM_ISCSI_RX_STATS].m1))
#define MSTORM_ISCSI_RX_STATS_SIZE (IRO[IRO_MSTORM_ISCSI_RX_STATS].size)
/* Mstorm overlay buffer host address */
#define MSTORM_OVERLAY_BUF_ADDR_OFFSET (IRO[IRO_MSTORM_OVERLAY_BUF_ADDR].base)
#define MSTORM_OVERLAY_BUF_ADDR_SIZE (IRO[IRO_MSTORM_OVERLAY_BUF_ADDR].size)
/* Mstorm queue statistics */
#define MSTORM_QUEUE_STAT_OFFSET(stat_counter_id) \
(IRO[IRO_MSTORM_QUEUE_STAT].base \
+ ((stat_counter_id) * IRO[IRO_MSTORM_QUEUE_STAT].m1))
#define MSTORM_QUEUE_STAT_SIZ (IRO[IRO_MSTORM_QUEUE_STAT].size)
/* Mstorm error level for assert */
#define MSTORM_RDMA_ASSERT_LEVEL_OFFSET(pf_id) \
(IRO[IRO_MSTORM_RDMA_ASSERT_LEVEL].base \
+ ((pf_id) * IRO[IRO_MSTORM_RDMA_ASSERT_LEVEL].m1))
#define MSTORM_RDMA_ASSERT_LEVEL_SIZE (IRO[IRO_MSTORM_RDMA_ASSERT_LEVEL].size)
/* Mstorm bdq-external-producer of given BDQ resource ID, BDqueue-id */
#define MSTORM_SCSI_BDQ_EXT_PROD_GTT_OFFSET(storage_func_id, bdq_id) \
(IRO[IRO_MSTORM_SCSI_BDQ_EXT_PROD_GTT].base \
+ ((storage_func_id) * IRO[IRO_MSTORM_SCSI_BDQ_EXT_PROD_GTT].m1) \
+ ((bdq_id) * IRO[IRO_MSTORM_SCSI_BDQ_EXT_PROD_GTT].m2))
#define MSTORM_SCSI_BDQ_EXT_PROD_GTT_SIZE \
(IRO[IRO_MSTORM_SCSI_BDQ_EXT_PROD_GTT].size)
/* TPA agregation timeout in us resolution (on ASIC) */
#define MSTORM_TPA_TIMEOUT_US_OFFSET (IRO[IRO_MSTORM_TPA_TIMEOUT_US].base)
#define MSTORM_TPA_TIMEOUT_US_SIZE (IRO[IRO_MSTORM_TPA_TIMEOUT_US].size)
/* Control frame's EthType configuration for TX control frame security */
#define PSTORM_CTL_FRAME_ETHTYPE_GTT_OFFSET(ethtype_id) \
(IRO[IRO_PSTORM_CTL_FRAME_ETHTYPE_GTT].base \
+ ((ethtype_id) * IRO[IRO_PSTORM_CTL_FRAME_ETHTYPE_GTT].m1))
#define PSTORM_CTL_FRAME_ETHTYPE_GTT_SIZE \
(IRO[IRO_PSTORM_CTL_FRAME_ETHTYPE_GTT].size)
/* Pstorm pf statistics */
#define PSTORM_ETH_PF_STAT_OFFSET(pf_id) \
(IRO[IRO_PSTORM_ETH_PF_STAT].base \
+ ((pf_id) * IRO[IRO_PSTORM_ETH_PF_STAT].m1))
#define PSTORM_ETH_PF_STAT_SIZE (IRO[IRO_PSTORM_ETH_PF_STAT].size)
/* Pstorm FCoE TX stats */
#define PSTORM_FCOE_TX_STATS_OFFSET(pf_id) \
(IRO[IRO_PSTORM_FCOE_TX_STATS].base \
+ ((pf_id) * IRO[IRO_PSTORM_FCOE_TX_STATS].m1))
#define PSTORM_FCOE_TX_STATS_SIZE (IRO[IRO_PSTORM_FCOE_TX_STATS].size)
/* Pstorm Integration Test Data */
#define PSTORM_INTEG_TEST_DATA_OFFSET (IRO[IRO_PSTORM_INTEG_TEST_DATA].base)
#define PSTORM_INTEG_TEST_DATA_SIZE (IRO[IRO_PSTORM_INTEG_TEST_DATA].size)
/* Pstorm iSCSI TX stats */
#define PSTORM_ISCSI_TX_STATS_OFFSET(storage_func_id) \
(IRO[IRO_PSTORM_ISCSI_TX_STATS].base \
+ ((storage_func_id) * IRO[IRO_PSTORM_ISCSI_TX_STATS].m1))
#define PSTORM_ISCSI_TX_STATS_SIZE (IRO[IRO_PSTORM_ISCSI_TX_STATS].size)
/* Pstorm overlay buffer host address */
#define PSTORM_OVERLAY_BUF_ADDR_OFFSET (IRO[IRO_PSTORM_OVERLAY_BUF_ADDR].base)
#define PSTORM_OVERLAY_BUF_ADDR_SIZE (IRO[IRO_PSTORM_OVERLAY_BUF_ADDR].size)
/* Pstorm LL2 packet duplication configuration. Use pstorm_pkt_dup_cfg
* data type.
*/
#define PSTORM_PKT_DUPLICATION_CFG_OFFSET(pf_id) \
(IRO[IRO_PSTORM_PKT_DUPLICATION_CFG].base \
+ ((pf_id) * IRO[IRO_PSTORM_PKT_DUPLICATION_CFG].m1))
#define PSTORM_PKT_DUPLICATION_CFG_SIZE \
(IRO[IRO_PSTORM_PKT_DUPLICATION_CFG].size)
/* Pstorm queue statistics */
#define PSTORM_QUEUE_STAT_OFFSET(stat_counter_id) \
(IRO[IRO_PSTORM_QUEUE_STAT].base \
+ ((stat_counter_id) * IRO[IRO_PSTORM_QUEUE_STAT].m1))
#define PSTORM_QUEUE_STAT_SIZE (IRO[IRO_PSTORM_QUEUE_STAT].size)
/* Pstorm error level for assert */
#define PSTORM_RDMA_ASSERT_LEVEL_OFFSET(pf_id) \
(IRO[IRO_PSTORM_RDMA_ASSERT_LEVEL].base \
+ ((pf_id) * IRO[IRO_PSTORM_RDMA_ASSERT_LEVEL].m1))
#define PSTORM_RDMA_ASSERT_LEVEL_SIZE (IRO[IRO_PSTORM_RDMA_ASSERT_LEVEL].size)
/* Pstorm RDMA queue statistics */
#define PSTORM_RDMA_QUEUE_STAT_OFFSET(rdma_stat_counter_id) \
(IRO[IRO_PSTORM_RDMA_QUEUE_STAT].base \
+ ((rdma_stat_counter_id) * IRO[IRO_PSTORM_RDMA_QUEUE_STAT].m1))
#define PSTORM_RDMA_QUEUE_STAT_SIZE (IRO[IRO_PSTORM_RDMA_QUEUE_STAT].size)
/* DCQCN Sent Statistics */
#define PSTORM_ROCE_DCQCN_SENT_STATS_OFFSET(roce_pf_id) \
(IRO[IRO_PSTORM_ROCE_DCQCN_SENT_STATS].base \
+ ((roce_pf_id) * IRO[IRO_PSTORM_ROCE_DCQCN_SENT_STATS].m1))
#define PSTORM_ROCE_DCQCN_SENT_STATS_SIZE \
(IRO[IRO_PSTORM_ROCE_DCQCN_SENT_STATS].size)
/* Tstorm last parser message */
#define TSTORM_ETH_PRS_INPUT_OFFSET (IRO[IRO_TSTORM_ETH_PRS_INPUT].base)
#define TSTORM_ETH_PRS_INPUT_SIZE (IRO[IRO_TSTORM_ETH_PRS_INPUT].size)
/* RSS indirection table entry update command per PF offset in TSTORM PF BAR0.
* Use eth_tstorm_rss_update_data for update.
*/
#define TSTORM_ETH_RSS_UPDATE_GTT_OFFSET(pf_id) \
(IRO[IRO_TSTORM_ETH_RSS_UPDATE_GTT].base \
+ ((pf_id) * IRO[IRO_TSTORM_ETH_RSS_UPDATE_GTT].m1))
#define TSTORM_ETH_RSS_UPDATE_GTT_SIZE\
(IRO[IRO_TSTORM_ETH_RSS_UPDATE_GTT].size)
/* Tstorm FCoE RX stats */
#define TSTORM_FCOE_RX_STATS_OFFSET(pf_id) \
(IRO[IRO_TSTORM_FCOE_RX_STATS].base \
+ ((pf_id) * IRO[IRO_TSTORM_FCOE_RX_STATS].m1))
#define TSTORM_FCOE_RX_STATS_SIZE (IRO[IRO_TSTORM_FCOE_RX_STATS].size)
/* Tstorm Integration Test Data */
#define TSTORM_INTEG_TEST_DATA_OFFSET (IRO[IRO_TSTORM_INTEG_TEST_DATA].base)
#define TSTORM_INTEG_TEST_DATA_SIZE (IRO[IRO_TSTORM_INTEG_TEST_DATA].size)
/* Tstorm iSCSI RX stats */
#define TSTORM_ISCSI_RX_STATS_OFFSET(storage_func_id) \
(IRO[IRO_TSTORM_ISCSI_RX_STATS].base \
+ ((storage_func_id) * IRO[IRO_TSTORM_ISCSI_RX_STATS].m1))
#define TSTORM_ISCSI_RX_STATS_SIZE (IRO[IRO_TSTORM_ISCSI_RX_STATS].size)
/* Tstorm ll2 port statistics */
#define TSTORM_LL2_PORT_STAT_OFFSET(port_id) \
(IRO[IRO_TSTORM_LL2_PORT_STAT].base \
+ ((port_id) * IRO[IRO_TSTORM_LL2_PORT_STAT].m1))
#define TSTORM_LL2_PORT_STAT_SIZE (IRO[IRO_TSTORM_LL2_PORT_STAT].size)
/* Tstorm producers */
#define TSTORM_LL2_RX_PRODS_GTT_OFFSET(core_rx_queue_id) \
(IRO[IRO_TSTORM_LL2_RX_PRODS_GTT].base \
+ ((core_rx_queue_id) * IRO[IRO_TSTORM_LL2_RX_PRODS_GTT].m1))
#define TSTORM_LL2_RX_PRODS_GTT_SIZE (IRO[IRO_TSTORM_LL2_RX_PRODS_GTT].size)
/* Tstorm overlay buffer host address */
#define TSTORM_OVERLAY_BUF_ADDR_OFFSET (IRO[IRO_TSTORM_OVERLAY_BUF_ADDR].base)
#define TSTORM_OVERLAY_BUF_ADDR_SIZE (IRO[IRO_TSTORM_OVERLAY_BUF_ADDR].size)
/* Tstorm LL2 packet duplication configuration.
* Use tstorm_pkt_dup_cfg data type.
*/
#define TSTORM_PKT_DUPLICATION_CFG_OFFSET(pf_id) \
(IRO[IRO_TSTORM_PKT_DUPLICATION_CFG].base \
+ ((pf_id) * IRO[IRO_TSTORM_PKT_DUPLICATION_CFG].m1))
#define TSTORM_PKT_DUPLICATION_CFG_SIZE \
(IRO[IRO_TSTORM_PKT_DUPLICATION_CFG].size)
/* Tstorm port statistics */
#define TSTORM_PORT_STAT_OFFSET(port_id) \
(IRO[IRO_TSTORM_PORT_STAT].base \
+ ((port_id) * IRO[IRO_TSTORM_PORT_STAT].m1))
#define TSTORM_PORT_STAT_SIZE (IRO[IRO_TSTORM_PORT_STAT].size)
/* Tstorm error level for assert */
#define TSTORM_RDMA_ASSERT_LEVEL_OFFSET(pf_id) \
(IRO[IRO_TSTORM_RDMA_ASSERT_LEVEL].base \
+ ((pf_id) * IRO[IRO_TSTORM_RDMA_ASSERT_LEVEL].m1))
#define TSTORM_RDMA_ASSERT_LEVEL_SIZE (IRO[IRO_TSTORM_RDMA_ASSERT_LEVEL].size)
/* Tstorm RDMA queue statistics */
#define TSTORM_RDMA_QUEUE_STAT_OFFSET(rdma_stat_counter_id) \
(IRO[IRO_TSTORM_RDMA_QUEUE_STAT].base \
+ ((rdma_stat_counter_id) * IRO[IRO_TSTORM_RDMA_QUEUE_STAT].m1))
#define TSTORM_RDMA_QUEUE_STAT_SIZE (IRO[IRO_TSTORM_RDMA_QUEUE_STAT].size)
/* Tstorm RoCE Event Statistics */
#define TSTORM_ROCE_EVENTS_STAT_OFFSET(roce_pf_id) \
(IRO[IRO_TSTORM_ROCE_EVENTS_STAT].base \
+ ((roce_pf_id) * IRO[IRO_TSTORM_ROCE_EVENTS_STAT].m1))
#define TSTORM_ROCE_EVENTS_STAT_SIZE (IRO[IRO_TSTORM_ROCE_EVENTS_STAT].size)
/* Tstorm (reflects M-Storm) bdq-external-producer of given function ID,
* BDqueue-id.
*/
#define TSTORM_SCSI_BDQ_EXT_PROD_GTT_OFFSET(storage_func_id, bdq_id) \
(IRO[IRO_TSTORM_SCSI_BDQ_EXT_PROD_GTT].base \
+ ((storage_func_id) * IRO[IRO_TSTORM_SCSI_BDQ_EXT_PROD_GTT].m1) \
+ ((bdq_id) * IRO[IRO_TSTORM_SCSI_BDQ_EXT_PROD_GTT].m2))
#define TSTORM_SCSI_BDQ_EXT_PROD_GTT_SIZE \
(IRO[IRO_TSTORM_SCSI_BDQ_EXT_PROD_GTT].size)
/* Tstorm cmdq-cons of given command queue-id */
#define TSTORM_SCSI_CMDQ_CONS_GTT_OFFSET(cmdq_queue_id) \
(IRO[IRO_TSTORM_SCSI_CMDQ_CONS_GTT].base \
+ ((cmdq_queue_id) * IRO[IRO_TSTORM_SCSI_CMDQ_CONS_GTT].m1))
#define TSTORM_SCSI_CMDQ_CONS_GTT_SIZE \
(IRO[IRO_TSTORM_SCSI_CMDQ_CONS_GTT].size)
/* Ustorm Common Queue ring consumer */
#define USTORM_COMMON_QUEUE_CONS_GTT_OFFSET(queue_zone_id) \
(IRO[IRO_USTORM_COMMON_QUEUE_CONS_GTT].base \
+ ((queue_zone_id) * IRO[IRO_USTORM_COMMON_QUEUE_CONS_GTT].m1))
#define USTORM_COMMON_QUEUE_CONS_GTT_SIZE \
(IRO[IRO_USTORM_COMMON_QUEUE_CONS_GTT].size)
/* Ustorm Event ring consumer */
#define USTORM_EQE_CONS_GTT_OFFSET(pf_id) \
(IRO[IRO_USTORM_EQE_CONS_GTT].base \
+ ((pf_id) * IRO[IRO_USTORM_EQE_CONS_GTT].m1))
#define USTORM_EQE_CONS_GTT_SIZE (IRO[IRO_USTORM_EQE_CONS_GTT].size)
/* Ustorm pf statistics */
#define USTORM_ETH_PF_STAT_OFFSET(pf_id) \
(IRO[IRO_USTORM_ETH_PF_STAT].base \
+ ((pf_id) * IRO[IRO_USTORM_ETH_PF_STAT].m1))
#define USTORM_ETH_PF_STAT_SIZE (IRO[IRO_USTORM_ETH_PF_STAT].size)
/* Ustorm eth queue zone */
#define USTORM_ETH_QUEUE_ZONE_GTT_OFFSET(queue_zone_id) \
(IRO[IRO_USTORM_ETH_QUEUE_ZONE_GTT].base \
+ ((queue_zone_id) * IRO[IRO_USTORM_ETH_QUEUE_ZONE_GTT].m1))
#define USTORM_ETH_QUEUE_ZONE_GTT_SIZE (IRO[IRO_USTORM_ETH_QUEUE_ZONE_GTT].size)
/* Ustorm Final flr cleanup ack */
#define USTORM_FLR_FINAL_ACK_GTT_OFFSET(pf_id) \
(IRO[IRO_USTORM_FLR_FINAL_ACK_GTT].base \
+ ((pf_id) * IRO[IRO_USTORM_FLR_FINAL_ACK_GTT].m1))
#define USTORM_FLR_FINAL_ACK_GTT_SIZE (IRO[IRO_USTORM_FLR_FINAL_ACK_GTT].size)
/* Ustorm Integration Test Data */
#define USTORM_INTEG_TEST_DATA_OFFSET (IRO[IRO_USTORM_INTEG_TEST_DATA].base)
#define USTORM_INTEG_TEST_DATA_SIZE (IRO[IRO_USTORM_INTEG_TEST_DATA].size)
/* Ustorm iSCSI RX stats */
#define USTORM_ISCSI_RX_STATS_OFFSET(storage_func_id) \
(IRO[IRO_USTORM_ISCSI_RX_STATS].base \
+ ((storage_func_id) * IRO[IRO_USTORM_ISCSI_RX_STATS].m1))
#define USTORM_ISCSI_RX_STATS_SIZE (IRO[IRO_USTORM_ISCSI_RX_STATS].size)
/* Ustorm overlay buffer host address */
#define USTORM_OVERLAY_BUF_ADDR_OFFSET (IRO[IRO_USTORM_OVERLAY_BUF_ADDR].base)
#define USTORM_OVERLAY_BUF_ADDR_SIZE (IRO[IRO_USTORM_OVERLAY_BUF_ADDR].size)
/* Ustorm queue statistics */
#define USTORM_QUEUE_STAT_OFFSET(stat_counter_id) \
(IRO[IRO_USTORM_QUEUE_STAT].base \
+ ((stat_counter_id) * IRO[IRO_USTORM_QUEUE_STAT].m1))
#define USTORM_QUEUE_STAT_SIZE (IRO[IRO_USTORM_QUEUE_STAT].size)
/* Ustorm error level for assert */
#define USTORM_RDMA_ASSERT_LEVEL_OFFSET(pf_id) \
(IRO[IRO_USTORM_RDMA_ASSERT_LEVEL].base \
+ ((pf_id) * IRO[IRO_USTORM_RDMA_ASSERT_LEVEL].m1))
#define USTORM_RDMA_ASSERT_LEVEL_SIZE (IRO[IRO_USTORM_RDMA_ASSERT_LEVEL].size)
/* RoCE CQEs Statistics */
#define USTORM_ROCE_CQE_STATS_OFFSET(roce_pf_id) \
(IRO[IRO_USTORM_ROCE_CQE_STATS].base \
+ ((roce_pf_id) * IRO[IRO_USTORM_ROCE_CQE_STATS].m1))
#define USTORM_ROCE_CQE_STATS_SIZE (IRO[IRO_USTORM_ROCE_CQE_STATS].size)
/* Ustorm cqe producer */
#define USTORM_TOE_CQ_PROD_OFFSET(rss_id) \
(IRO[IRO_USTORM_TOE_CQ_PROD].base \
+ ((rss_id) * IRO[IRO_USTORM_TOE_CQ_PROD].m1))
#define USTORM_TOE_CQ_PROD_SIZE (IRO[IRO_USTORM_TOE_CQ_PROD].size)
/* Ustorm grq producer */
#define USTORM_TOE_GRQ_PROD_OFFSET(pf_id) \
(IRO[IRO_USTORM_TOE_GRQ_PROD].base \
+ ((pf_id) * IRO[IRO_USTORM_TOE_GRQ_PROD].m1))
#define USTORM_TOE_GRQ_PROD_SIZE (IRO[IRO_USTORM_TOE_GRQ_PROD].size)
/* Ustorm VF-PF Channel ready flag */
#define USTORM_VF_PF_CHANNEL_READY_GTT_OFFSET(vf_id) \
(IRO[IRO_USTORM_VF_PF_CHANNEL_READY_GTT].base \
+ ((vf_id) * IRO[IRO_USTORM_VF_PF_CHANNEL_READY_GTT].m1))
#define USTORM_VF_PF_CHANNEL_READY_GTT_SIZE \
(IRO[IRO_USTORM_VF_PF_CHANNEL_READY_GTT].size)
/* Xstorm queue zone */
#define XSTORM_ETH_QUEUE_ZONE_GTT_OFFSET(queue_id) \
(IRO[IRO_XSTORM_ETH_QUEUE_ZONE_GTT].base \
+ ((queue_id) * IRO[IRO_XSTORM_ETH_QUEUE_ZONE_GTT].m1))
#define XSTORM_ETH_QUEUE_ZONE_GTT_SIZE (IRO[IRO_XSTORM_ETH_QUEUE_ZONE_GTT].size)
/* Xstorm Integration Test Data */
#define XSTORM_INTEG_TEST_DATA_OFFSET (IRO[IRO_XSTORM_INTEG_TEST_DATA].base)
#define XSTORM_INTEG_TEST_DATA_SIZE (IRO[IRO_XSTORM_INTEG_TEST_DATA].size)
/* Xstorm iSCSI TX stats */
#define XSTORM_ISCSI_TX_STATS_OFFSET(storage_func_id) \
(IRO[IRO_XSTORM_ISCSI_TX_STATS].base \
+ ((storage_func_id) * IRO[IRO_XSTORM_ISCSI_TX_STATS].m1))
#define XSTORM_ISCSI_TX_STATS_SIZE (IRO[IRO_XSTORM_ISCSI_TX_STATS].size)
/* Xstorm iWARP rxmit stats */
#define XSTORM_IWARP_RXMIT_STATS_OFFSET(pf_id) \
(IRO[IRO_XSTORM_IWARP_RXMIT_STATS].base \
+ ((pf_id) * IRO[IRO_XSTORM_IWARP_RXMIT_STATS].m1))
#define XSTORM_IWARP_RXMIT_STATS_SIZE (IRO[IRO_XSTORM_IWARP_RXMIT_STATS].size)
/* Xstorm overlay buffer host address */
#define XSTORM_OVERLAY_BUF_ADDR_OFFSET (IRO[IRO_XSTORM_OVERLAY_BUF_ADDR].base)
#define XSTORM_OVERLAY_BUF_ADDR_SIZE (IRO[IRO_XSTORM_OVERLAY_BUF_ADDR].size)
/* Xstorm common PQ info */
#define XSTORM_PQ_INFO_OFFSET(pq_id) \
(IRO[IRO_XSTORM_PQ_INFO].base \
+ ((pq_id) * IRO[IRO_XSTORM_PQ_INFO].m1))
#define XSTORM_PQ_INFO_SIZE (IRO[IRO_XSTORM_PQ_INFO].size)
/* Xstorm error level for assert */
#define XSTORM_RDMA_ASSERT_LEVEL_OFFSET(pf_id) \
(IRO[IRO_XSTORM_RDMA_ASSERT_LEVEL].base \
+ ((pf_id) * IRO[IRO_XSTORM_RDMA_ASSERT_LEVEL].m1))
#define XSTORM_RDMA_ASSERT_LEVEL_SIZE (IRO[IRO_XSTORM_RDMA_ASSERT_LEVEL].size)
/* Ystorm flow control mode. Use enum fw_flow_ctrl_mode */
#define YSTORM_FLOW_CONTROL_MODE_GTT_OFFSET \
(IRO[IRO_YSTORM_FLOW_CONTROL_MODE_GTT].base)
#define YSTORM_FLOW_CONTROL_MODE_GTT_SIZE \
(IRO[IRO_YSTORM_FLOW_CONTROL_MODE_GTT].size)
/* Ystorm Integration Test Data */
#define YSTORM_INTEG_TEST_DATA_OFFSET (IRO[IRO_YSTORM_INTEG_TEST_DATA].base)
#define YSTORM_INTEG_TEST_DATA_SIZE (IRO[IRO_YSTORM_INTEG_TEST_DATA].size)
/* Ystorm iSCSI TX stats */
#define YSTORM_ISCSI_TX_STATS_OFFSET(storage_func_id) \
(IRO[IRO_YSTORM_ISCSI_TX_STATS].base \
+ ((storage_func_id) * IRO[IRO_YSTORM_ISCSI_TX_STATS].m1))
#define YSTORM_ISCSI_TX_STATS_SIZE (IRO[IRO_YSTORM_ISCSI_TX_STATS].size)
/* Ystorm overlay buffer host address */
#define YSTORM_OVERLAY_BUF_ADDR_OFFSET (IRO[IRO_YSTORM_OVERLAY_BUF_ADDR].base)
#define YSTORM_OVERLAY_BUF_ADDR_SIZE (IRO[IRO_YSTORM_OVERLAY_BUF_ADDR].size)
/* Ystorm error level for assert */
#define YSTORM_RDMA_ASSERT_LEVEL_OFFSET(pf_id) \
(IRO[IRO_YSTORM_RDMA_ASSERT_LEVEL].base \
+ ((pf_id) * IRO[IRO_YSTORM_RDMA_ASSERT_LEVEL].m1))
#define YSTORM_RDMA_ASSERT_LEVEL_SIZE (IRO[IRO_YSTORM_RDMA_ASSERT_LEVEL].size)
/* DCQCN Received Statistics */
#define YSTORM_ROCE_DCQCN_RECEIVED_STATS_OFFSET(roce_pf_id) \
(IRO[IRO_YSTORM_ROCE_DCQCN_RECEIVED_STATS].base \
+ ((roce_pf_id) * IRO[IRO_YSTORM_ROCE_DCQCN_RECEIVED_STATS].m1))
#define YSTORM_ROCE_DCQCN_RECEIVED_STATS_SIZE \
(IRO[IRO_YSTORM_ROCE_DCQCN_RECEIVED_STATS].size)
/* RoCE Error Statistics */
#define YSTORM_ROCE_ERROR_STATS_OFFSET(roce_pf_id) \
(IRO[IRO_YSTORM_ROCE_ERROR_STATS].base \
+ ((roce_pf_id) * IRO[IRO_YSTORM_ROCE_ERROR_STATS].m1))
#define YSTORM_ROCE_ERROR_STATS_SIZE (IRO[IRO_YSTORM_ROCE_ERROR_STATS].size)
/* Ystorm cqe producer */
#define YSTORM_TOE_CQ_PROD_OFFSET(rss_id) \
(IRO[IRO_YSTORM_TOE_CQ_PROD].base \
+ ((rss_id) * IRO[IRO_YSTORM_TOE_CQ_PROD].m1))
#define YSTORM_TOE_CQ_PROD_SIZE (IRO[IRO_YSTORM_TOE_CQ_PROD].size)
/* Per-chip offsets in iro_arr in dwords */
#define E4_IRO_ARR_OFFSET 0
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,66 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_ISCSI_H
#define _QED_ISCSI_H
#include <linux/types.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/qed/tcp_common.h>
#include <linux/qed/qed_iscsi_if.h>
#include <linux/qed/qed_chain.h>
#include "qed.h"
#include "qed_hsi.h"
#include "qed_mcp.h"
#include "qed_sp.h"
struct qed_iscsi_info {
spinlock_t lock; /* Connection resources. */
struct list_head free_list;
u16 max_num_outstanding_tasks;
void *event_context;
iscsi_event_cb_t event_cb;
};
#if IS_ENABLED(CONFIG_QED_ISCSI)
int qed_iscsi_alloc(struct qed_hwfn *p_hwfn);
void qed_iscsi_setup(struct qed_hwfn *p_hwfn);
void qed_iscsi_free(struct qed_hwfn *p_hwfn);
/**
* qed_get_protocol_stats_iscsi(): Fills provided statistics
* struct with statistics.
*
* @cdev: Qed dev pointer.
* @stats: Points to struct that will be filled with statistics.
* @is_atomic: Hint from the caller - if the func can sleep or not.
*
* Context: The function should not sleep in case is_atomic == true.
* Return: Void.
*/
void qed_get_protocol_stats_iscsi(struct qed_dev *cdev,
struct qed_mcp_iscsi_stats *stats,
bool is_atomic);
#else /* IS_ENABLED(CONFIG_QED_ISCSI) */
static inline int qed_iscsi_alloc(struct qed_hwfn *p_hwfn)
{
return -EINVAL;
}
static inline void qed_iscsi_setup(struct qed_hwfn *p_hwfn) {}
static inline void qed_iscsi_free(struct qed_hwfn *p_hwfn) {}
static inline void
qed_get_protocol_stats_iscsi(struct qed_dev *cdev,
struct qed_mcp_iscsi_stats *stats,
bool is_atomic) {}
#endif /* IS_ENABLED(CONFIG_QED_ISCSI) */
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,206 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_IWARP_H
#define _QED_IWARP_H
enum qed_iwarp_qp_state {
QED_IWARP_QP_STATE_IDLE,
QED_IWARP_QP_STATE_RTS,
QED_IWARP_QP_STATE_TERMINATE,
QED_IWARP_QP_STATE_CLOSING,
QED_IWARP_QP_STATE_ERROR,
};
enum qed_iwarp_qp_state qed_roce2iwarp_state(enum qed_roce_qp_state state);
#define QED_IWARP_PREALLOC_CNT (256)
#define QED_IWARP_LL2_SYN_TX_SIZE (128)
#define QED_IWARP_LL2_SYN_RX_SIZE (256)
#define QED_IWARP_LL2_OOO_DEF_TX_SIZE (256)
#define QED_IWARP_MAX_OOO (16)
#define QED_IWARP_LL2_OOO_MAX_RX_SIZE (16384)
#define QED_IWARP_HANDLE_INVAL (0xff)
struct qed_iwarp_ll2_buff {
struct qed_iwarp_ll2_buff *piggy_buf;
void *data;
dma_addr_t data_phys_addr;
u32 buff_size;
};
struct qed_iwarp_ll2_mpa_buf {
struct list_head list_entry;
struct qed_iwarp_ll2_buff *ll2_buf;
struct unaligned_opaque_data data;
u16 tcp_payload_len;
u8 placement_offset;
};
/* In some cases a fpdu will arrive with only one byte of the header, in this
* case the fpdu_length will be partial (contain only higher byte and
* incomplete bytes will contain the invalid value
*/
#define QED_IWARP_INVALID_INCOMPLETE_BYTES 0xffff
struct qed_iwarp_fpdu {
struct qed_iwarp_ll2_buff *mpa_buf;
void *mpa_frag_virt;
dma_addr_t mpa_frag;
dma_addr_t pkt_hdr;
u16 mpa_frag_len;
u16 fpdu_length;
u16 incomplete_bytes;
u8 pkt_hdr_size;
};
struct qed_iwarp_info {
struct list_head listen_list; /* qed_iwarp_listener */
struct list_head ep_list; /* qed_iwarp_ep */
struct list_head ep_free_list; /* pre-allocated ep's */
struct list_head mpa_buf_list; /* list of mpa_bufs */
struct list_head mpa_buf_pending_list;
spinlock_t iw_lock; /* for iwarp resources */
spinlock_t qp_lock; /* for teardown races */
u32 rcv_wnd_scale;
u16 rcv_wnd_size;
u16 max_mtu;
u8 mac_addr[ETH_ALEN];
u8 crc_needed;
u8 tcp_flags;
u8 ll2_syn_handle;
u8 ll2_ooo_handle;
u8 ll2_mpa_handle;
u8 peer2peer;
enum mpa_negotiation_mode mpa_rev;
enum mpa_rtr_type rtr_type;
struct qed_iwarp_fpdu *partial_fpdus;
struct qed_iwarp_ll2_mpa_buf *mpa_bufs;
u8 *mpa_intermediate_buf;
u16 max_num_partial_fpdus;
};
enum qed_iwarp_ep_state {
QED_IWARP_EP_INIT,
QED_IWARP_EP_MPA_REQ_RCVD,
QED_IWARP_EP_MPA_OFFLOADED,
QED_IWARP_EP_ESTABLISHED,
QED_IWARP_EP_CLOSED
};
union async_output {
struct iwarp_eqe_data_mpa_async_completion mpa_response;
struct iwarp_eqe_data_tcp_async_completion mpa_request;
};
#define QED_MAX_PRIV_DATA_LEN (512)
struct qed_iwarp_ep_memory {
u8 in_pdata[QED_MAX_PRIV_DATA_LEN];
u8 out_pdata[QED_MAX_PRIV_DATA_LEN];
union async_output async_output;
};
/* Endpoint structure represents a TCP connection. This connection can be
* associated with a QP or not (in which case QP==NULL)
*/
struct qed_iwarp_ep {
struct list_head list_entry;
struct qed_rdma_qp *qp;
struct qed_iwarp_ep_memory *ep_buffer_virt;
dma_addr_t ep_buffer_phys;
enum qed_iwarp_ep_state state;
int sig;
struct qed_iwarp_cm_info cm_info;
enum tcp_connect_mode connect_mode;
enum mpa_rtr_type rtr_type;
enum mpa_negotiation_mode mpa_rev;
u32 tcp_cid;
u32 cid;
u16 mss;
u8 remote_mac_addr[6];
u8 local_mac_addr[6];
bool mpa_reply_processed;
/* For Passive side - syn packet related data */
u16 syn_ip_payload_length;
struct qed_iwarp_ll2_buff *syn;
dma_addr_t syn_phy_addr;
/* The event_cb function is called for asynchrounous events associated
* with the ep. It is initialized at different entry points depending
* on whether the ep is the tcp connection active side or passive side
* The cb_context is passed to the event_cb function.
*/
iwarp_event_handler event_cb;
void *cb_context;
};
struct qed_iwarp_listener {
struct list_head list_entry;
/* The event_cb function is called for connection requests.
* The cb_context is passed to the event_cb function.
*/
iwarp_event_handler event_cb;
void *cb_context;
u32 max_backlog;
u32 ip_addr[4];
u16 port;
u16 vlan;
u8 ip_version;
};
int qed_iwarp_alloc(struct qed_hwfn *p_hwfn);
int qed_iwarp_setup(struct qed_hwfn *p_hwfn,
struct qed_rdma_start_in_params *params);
void qed_iwarp_init_fw_ramrod(struct qed_hwfn *p_hwfn,
struct iwarp_init_func_ramrod_data *p_ramrod);
int qed_iwarp_stop(struct qed_hwfn *p_hwfn);
void qed_iwarp_resc_free(struct qed_hwfn *p_hwfn);
void qed_iwarp_init_devinfo(struct qed_hwfn *p_hwfn);
void qed_iwarp_init_hw(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
int qed_iwarp_create_qp(struct qed_hwfn *p_hwfn,
struct qed_rdma_qp *qp,
struct qed_rdma_create_qp_out_params *out_params);
int qed_iwarp_modify_qp(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp,
enum qed_iwarp_qp_state new_state, bool internal);
int qed_iwarp_destroy_qp(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp);
int qed_iwarp_fw_destroy(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp);
void qed_iwarp_query_qp(struct qed_rdma_qp *qp,
struct qed_rdma_query_qp_out_params *out_params);
int
qed_iwarp_connect(void *rdma_cxt,
struct qed_iwarp_connect_in *iparams,
struct qed_iwarp_connect_out *oparams);
int
qed_iwarp_create_listen(void *rdma_cxt,
struct qed_iwarp_listen_in *iparams,
struct qed_iwarp_listen_out *oparams);
int qed_iwarp_accept(void *rdma_cxt, struct qed_iwarp_accept_in *iparams);
int qed_iwarp_reject(void *rdma_cxt, struct qed_iwarp_reject_in *iparams);
int qed_iwarp_destroy_listen(void *rdma_cxt, void *handle);
int qed_iwarp_send_rtr(void *rdma_cxt, struct qed_iwarp_send_rtr_in *iparams);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,454 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_L2_H
#define _QED_L2_H
#include <linux/types.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/qed/qed_eth_if.h>
#include "qed.h"
#include "qed_hw.h"
#include "qed_sp.h"
struct qed_rss_params {
u8 update_rss_config;
u8 rss_enable;
u8 rss_eng_id;
u8 update_rss_capabilities;
u8 update_rss_ind_table;
u8 update_rss_key;
u8 rss_caps;
u8 rss_table_size_log;
/* Indirection table consist of rx queue handles */
void *rss_ind_table[QED_RSS_IND_TABLE_SIZE];
u32 rss_key[QED_RSS_KEY_SIZE];
};
struct qed_sge_tpa_params {
u8 max_buffers_per_cqe;
u8 update_tpa_en_flg;
u8 tpa_ipv4_en_flg;
u8 tpa_ipv6_en_flg;
u8 tpa_ipv4_tunn_en_flg;
u8 tpa_ipv6_tunn_en_flg;
u8 update_tpa_param_flg;
u8 tpa_pkt_split_flg;
u8 tpa_hdr_data_split_flg;
u8 tpa_gro_consistent_flg;
u8 tpa_max_aggs_num;
u16 tpa_max_size;
u16 tpa_min_size_to_start;
u16 tpa_min_size_to_cont;
};
enum qed_filter_opcode {
QED_FILTER_ADD,
QED_FILTER_REMOVE,
QED_FILTER_MOVE,
QED_FILTER_REPLACE, /* Delete all MACs and add new one instead */
QED_FILTER_FLUSH, /* Removes all filters */
};
enum qed_filter_ucast_type {
QED_FILTER_MAC,
QED_FILTER_VLAN,
QED_FILTER_MAC_VLAN,
QED_FILTER_INNER_MAC,
QED_FILTER_INNER_VLAN,
QED_FILTER_INNER_PAIR,
QED_FILTER_INNER_MAC_VNI_PAIR,
QED_FILTER_MAC_VNI_PAIR,
QED_FILTER_VNI,
};
struct qed_filter_ucast {
enum qed_filter_opcode opcode;
enum qed_filter_ucast_type type;
u8 is_rx_filter;
u8 is_tx_filter;
u8 vport_to_add_to;
u8 vport_to_remove_from;
unsigned char mac[ETH_ALEN];
u8 assert_on_error;
u16 vlan;
u32 vni;
};
struct qed_filter_mcast {
/* MOVE is not supported for multicast */
enum qed_filter_opcode opcode;
u8 vport_to_add_to;
u8 vport_to_remove_from;
u8 num_mc_addrs;
#define QED_MAX_MC_ADDRS 64
unsigned char mac[QED_MAX_MC_ADDRS][ETH_ALEN];
};
/**
* qed_eth_rx_queue_stop(): This ramrod closes an Rx queue.
*
* @p_hwfn: HW device data.
* @p_rxq: Handler of queue to close
* @eq_completion_only: If True completion will be on
* EQe, if False completion will be
* on EQe if p_hwfn opaque
* different from the RXQ opaque
* otherwise on CQe.
* @cqe_completion: If True completion will be receive on CQe.
*
* Return: Int.
*/
int
qed_eth_rx_queue_stop(struct qed_hwfn *p_hwfn,
void *p_rxq,
bool eq_completion_only, bool cqe_completion);
/**
* qed_eth_tx_queue_stop(): Closes a Tx queue.
*
* @p_hwfn: HW device data.
* @p_txq: handle to Tx queue needed to be closed.
*
* Return: Int.
*/
int qed_eth_tx_queue_stop(struct qed_hwfn *p_hwfn, void *p_txq);
enum qed_tpa_mode {
QED_TPA_MODE_NONE,
QED_TPA_MODE_UNUSED,
QED_TPA_MODE_GRO,
QED_TPA_MODE_MAX
};
struct qed_sp_vport_start_params {
enum qed_tpa_mode tpa_mode;
bool remove_inner_vlan;
bool tx_switching;
bool handle_ptp_pkts;
bool only_untagged;
bool drop_ttl0;
u8 max_buffers_per_cqe;
u32 concrete_fid;
u16 opaque_fid;
u8 vport_id;
u16 mtu;
bool check_mac;
bool check_ethtype;
};
int qed_sp_eth_vport_start(struct qed_hwfn *p_hwfn,
struct qed_sp_vport_start_params *p_params);
struct qed_filter_accept_flags {
u8 update_rx_mode_config;
u8 update_tx_mode_config;
u8 rx_accept_filter;
u8 tx_accept_filter;
#define QED_ACCEPT_NONE 0x01
#define QED_ACCEPT_UCAST_MATCHED 0x02
#define QED_ACCEPT_UCAST_UNMATCHED 0x04
#define QED_ACCEPT_MCAST_MATCHED 0x08
#define QED_ACCEPT_MCAST_UNMATCHED 0x10
#define QED_ACCEPT_BCAST 0x20
#define QED_ACCEPT_ANY_VNI 0x40
};
struct qed_arfs_config_params {
bool tcp;
bool udp;
bool ipv4;
bool ipv6;
enum qed_filter_config_mode mode;
};
struct qed_sp_vport_update_params {
u16 opaque_fid;
u8 vport_id;
u8 update_vport_active_rx_flg;
u8 vport_active_rx_flg;
u8 update_vport_active_tx_flg;
u8 vport_active_tx_flg;
u8 update_inner_vlan_removal_flg;
u8 inner_vlan_removal_flg;
u8 silent_vlan_removal_flg;
u8 update_default_vlan_enable_flg;
u8 default_vlan_enable_flg;
u8 update_default_vlan_flg;
u16 default_vlan;
u8 update_tx_switching_flg;
u8 tx_switching_flg;
u8 update_approx_mcast_flg;
u8 update_anti_spoofing_en_flg;
u8 anti_spoofing_en;
u8 update_accept_any_vlan_flg;
u8 accept_any_vlan;
u32 bins[8];
struct qed_rss_params *rss_params;
struct qed_filter_accept_flags accept_flags;
struct qed_sge_tpa_params *sge_tpa_params;
u8 update_ctl_frame_check;
u8 mac_chk_en;
u8 ethtype_chk_en;
};
int qed_sp_vport_update(struct qed_hwfn *p_hwfn,
struct qed_sp_vport_update_params *p_params,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_data);
/**
* qed_sp_vport_stop: This ramrod closes a VPort after all its
* RX and TX queues are terminated.
* An Assert is generated if any queues are left open.
*
* @p_hwfn: HW device data.
* @opaque_fid: Opaque FID
* @vport_id: VPort ID.
*
* Return: Int.
*/
int qed_sp_vport_stop(struct qed_hwfn *p_hwfn, u16 opaque_fid, u8 vport_id);
int qed_sp_eth_filter_ucast(struct qed_hwfn *p_hwfn,
u16 opaque_fid,
struct qed_filter_ucast *p_filter_cmd,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_data);
/**
* qed_sp_eth_rx_queues_update(): This ramrod updates an RX queue.
* It is used for setting the active state
* of the queue and updating the TPA and
* SGE parameters.
* @p_hwfn: HW device data.
* @pp_rxq_handlers: An array of queue handlers to be updated.
* @num_rxqs: number of queues to update.
* @complete_cqe_flg: Post completion to the CQE Ring if set.
* @complete_event_flg: Post completion to the Event Ring if set.
* @comp_mode: Comp mode.
* @p_comp_data: Pointer Comp data.
*
* Return: Int.
*
* Note At the moment - only used by non-linux VFs.
*/
int
qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn,
void **pp_rxq_handlers,
u8 num_rxqs,
u8 complete_cqe_flg,
u8 complete_event_flg,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_data);
/**
* qed_get_vport_stats(): Fills provided statistics
* struct with statistics.
*
* @cdev: Qed dev pointer.
* @stats: Points to struct that will be filled with statistics.
*
* Return: Void.
*/
void qed_get_vport_stats(struct qed_dev *cdev, struct qed_eth_stats *stats);
/**
* qed_get_vport_stats_context(): Fills provided statistics
* struct with statistics.
*
* @cdev: Qed dev pointer.
* @stats: Points to struct that will be filled with statistics.
* @is_atomic: Hint from the caller - if the func can sleep or not.
*
* Context: The function should not sleep in case is_atomic == true.
* Return: Void.
*/
void qed_get_vport_stats_context(struct qed_dev *cdev,
struct qed_eth_stats *stats,
bool is_atomic);
void qed_reset_vport_stats(struct qed_dev *cdev);
/**
* qed_arfs_mode_configure(): Enable or disable rfs mode.
* It must accept at least one of tcp or udp true
* and at least one of ipv4 or ipv6 true to enable
* rfs mode.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @p_cfg_params: arfs mode configuration parameters.
*
* Return. Void.
*/
void qed_arfs_mode_configure(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_arfs_config_params *p_cfg_params);
/**
* qed_configure_rfs_ntuple_filter(): This ramrod should be used to add
* or remove arfs hw filter
*
* @p_hwfn: HW device data.
* @p_cb: Used for QED_SPQ_MODE_CB,where client would initialize
* it with cookie and callback function address, if not
* using this mode then client must pass NULL.
* @p_params: Pointer to params.
*
* Return: Void.
*/
int
qed_configure_rfs_ntuple_filter(struct qed_hwfn *p_hwfn,
struct qed_spq_comp_cb *p_cb,
struct qed_ntuple_filter_params *p_params);
#define MAX_QUEUES_PER_QZONE (sizeof(unsigned long) * 8)
#define QED_QUEUE_CID_SELF (0xff)
/* Almost identical to the qed_queue_start_common_params,
* but here we maintain the SB index in IGU CAM.
*/
struct qed_queue_cid_params {
u8 vport_id;
u16 queue_id;
u8 stats_id;
};
/* Additional parameters required for initialization of the queue_cid
* and are relevant only for a PF initializing one for its VFs.
*/
struct qed_queue_cid_vf_params {
/* Should match the VF's relative index */
u8 vfid;
/* 0-based queue index. Should reflect the relative qzone the
* VF thinks is associated with it [in its range].
*/
u8 vf_qid;
/* Indicates a VF is legacy, making it differ in several things:
* - Producers would be placed in a different place.
* - Makes assumptions regarding the CIDs.
*/
u8 vf_legacy;
u8 qid_usage_idx;
};
struct qed_queue_cid {
/* For stats-id, the `rel' is actually absolute as well */
struct qed_queue_cid_params rel;
struct qed_queue_cid_params abs;
/* These have no 'relative' meaning */
u16 sb_igu_id;
u8 sb_idx;
u32 cid;
u16 opaque_fid;
bool b_is_rx;
/* VFs queues are mapped differently, so we need to know the
* relative queue associated with them [0-based].
* Notice this is relevant on the *PF* queue-cid of its VF's queues,
* and not on the VF itself.
*/
u8 vfid;
u8 vf_qid;
/* We need an additional index to differentiate between queues opened
* for same queue-zone, as VFs would have to communicate the info
* to the PF [otherwise PF has no way to differentiate].
*/
u8 qid_usage_idx;
u8 vf_legacy;
#define QED_QCID_LEGACY_VF_RX_PROD (BIT(0))
#define QED_QCID_LEGACY_VF_CID (BIT(1))
struct qed_hwfn *p_owner;
};
int qed_l2_alloc(struct qed_hwfn *p_hwfn);
void qed_l2_setup(struct qed_hwfn *p_hwfn);
void qed_l2_free(struct qed_hwfn *p_hwfn);
void qed_eth_queue_cid_release(struct qed_hwfn *p_hwfn,
struct qed_queue_cid *p_cid);
struct qed_queue_cid *
qed_eth_queue_to_cid(struct qed_hwfn *p_hwfn,
u16 opaque_fid,
struct qed_queue_start_common_params *p_params,
bool b_is_rx,
struct qed_queue_cid_vf_params *p_vf_params);
int
qed_sp_eth_vport_start(struct qed_hwfn *p_hwfn,
struct qed_sp_vport_start_params *p_params);
/**
* qed_eth_rxq_start_ramrod(): Starts an Rx queue, when queue_cid is
* already prepared
*
* @p_hwfn: HW device data.
* @p_cid: Pointer CID.
* @bd_max_bytes: Max bytes.
* @bd_chain_phys_addr: Chain physcial address.
* @cqe_pbl_addr: PBL address.
* @cqe_pbl_size: PBL size.
*
* Return: Int.
*/
int
qed_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn,
struct qed_queue_cid *p_cid,
u16 bd_max_bytes,
dma_addr_t bd_chain_phys_addr,
dma_addr_t cqe_pbl_addr, u16 cqe_pbl_size);
/**
* qed_eth_txq_start_ramrod(): Starts a Tx queue, where queue_cid is
* already prepared
*
* @p_hwfn: HW device data.
* @p_cid: Pointer CID.
* @pbl_addr: PBL address.
* @pbl_size: PBL size.
* @pq_id: Parameters for choosing the PQ for this Tx queue.
*
* Return: Int.
*/
int
qed_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn,
struct qed_queue_cid *p_cid,
dma_addr_t pbl_addr, u16 pbl_size, u16 pq_id);
u8 qed_mcast_bin_from_mac(u8 *mac);
int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u16 coalesce, struct qed_queue_cid *p_cid);
int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u16 coalesce, struct qed_queue_cid *p_cid);
int qed_get_rxq_coalesce(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_queue_cid *p_cid, u16 *p_hw_coal);
int qed_get_txq_coalesce(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_queue_cid *p_cid, u16 *p_hw_coal);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,260 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_LL2_H
#define _QED_LL2_H
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/qed/qed_chain.h>
#include <linux/qed/qed_ll2_if.h>
#include "qed.h"
#include "qed_hsi.h"
#include "qed_sp.h"
#define QED_MAX_NUM_OF_LL2_CONNECTIONS (4)
/* LL2 queues handles will be split as follows:
* first will be legacy queues, and then the ctx based queues.
*/
#define QED_MAX_NUM_OF_LL2_CONNS_PF (4)
#define QED_MAX_NUM_OF_LEGACY_LL2_CONNS_PF (3)
#define QED_MAX_NUM_OF_CTX_LL2_CONNS_PF \
(QED_MAX_NUM_OF_LL2_CONNS_PF - QED_MAX_NUM_OF_LEGACY_LL2_CONNS_PF)
#define QED_LL2_LEGACY_CONN_BASE_PF 0
#define QED_LL2_CTX_CONN_BASE_PF QED_MAX_NUM_OF_LEGACY_LL2_CONNS_PF
struct qed_ll2_rx_packet {
struct list_head list_entry;
struct core_rx_bd_with_buff_len *rxq_bd;
dma_addr_t rx_buf_addr;
u16 buf_length;
void *cookie;
u8 placement_offset;
u16 parse_flags;
u16 packet_length;
u16 vlan;
u32 opaque_data[2];
};
struct qed_ll2_tx_packet {
struct list_head list_entry;
u16 bd_used;
bool notify_fw;
void *cookie;
/* Flexible Array of bds_set determined by max_bds_per_packet */
struct {
struct core_tx_bd *txq_bd;
dma_addr_t tx_frag;
u16 frag_len;
} bds_set[];
};
struct qed_ll2_rx_queue {
/* Lock protecting the Rx queue manipulation */
spinlock_t lock;
struct qed_chain rxq_chain;
struct qed_chain rcq_chain;
u8 rx_sb_index;
u8 ctx_based;
bool b_cb_registered;
__le16 *p_fw_cons;
struct list_head active_descq;
struct list_head free_descq;
struct list_head posting_descq;
struct qed_ll2_rx_packet *descq_array;
void __iomem *set_prod_addr;
struct core_pwm_prod_update_data db_data;
};
struct qed_ll2_tx_queue {
/* Lock protecting the Tx queue manipulation */
spinlock_t lock;
struct qed_chain txq_chain;
u8 tx_sb_index;
bool b_cb_registered;
__le16 *p_fw_cons;
struct list_head active_descq;
struct list_head free_descq;
struct list_head sending_descq;
u16 cur_completing_bd_idx;
void __iomem *doorbell_addr;
struct core_db_data db_msg;
u16 bds_idx;
u16 cur_send_frag_num;
u16 cur_completing_frag_num;
bool b_completing_packet;
void *descq_mem; /* memory for variable sized qed_ll2_tx_packet*/
struct qed_ll2_tx_packet *cur_send_packet;
struct qed_ll2_tx_packet cur_completing_packet;
};
struct qed_ll2_info {
/* Lock protecting the state of LL2 */
struct mutex mutex;
struct qed_ll2_acquire_data_inputs input;
u32 cid;
u8 my_id;
u8 queue_id;
u8 tx_stats_id;
bool b_active;
enum core_tx_dest tx_dest;
u8 tx_stats_en;
bool main_func_queue;
struct qed_ll2_cbs cbs;
struct qed_ll2_rx_queue rx_queue;
struct qed_ll2_tx_queue tx_queue;
};
extern const struct qed_ll2_ops qed_ll2_ops_pass;
/**
* qed_ll2_acquire_connection(): Allocate resources,
* starts rx & tx (if relevant) queues pair.
* Provides connecion handler as output
* parameter.
*
* @cxt: Pointer to the hw-function [opaque to some].
* @data: Describes connection parameters.
*
* Return: Int.
*/
int qed_ll2_acquire_connection(void *cxt, struct qed_ll2_acquire_data *data);
/**
* qed_ll2_establish_connection(): start previously allocated LL2 queues pair
*
* @cxt: Pointer to the hw-function [opaque to some].
* @connection_handle: LL2 connection's handle obtained from
* qed_ll2_require_connection.
*
* Return: 0 on success, failure otherwise.
*/
int qed_ll2_establish_connection(void *cxt, u8 connection_handle);
/**
* qed_ll2_post_rx_buffer(): Submit buffers to LL2 Rx queue.
*
* @cxt: Pointer to the hw-function [opaque to some].
* @connection_handle: LL2 connection's handle obtained from
* qed_ll2_require_connection.
* @addr: RX (physical address) buffers to submit.
* @buf_len: Buffer Len.
* @cookie: Cookie.
* @notify_fw: Produce corresponding Rx BD immediately.
*
* Return: 0 on success, failure otherwise.
*/
int qed_ll2_post_rx_buffer(void *cxt,
u8 connection_handle,
dma_addr_t addr,
u16 buf_len, void *cookie, u8 notify_fw);
/**
* qed_ll2_prepare_tx_packet(): Request for start Tx BD
* to prepare Tx packet submission to FW.
*
* @cxt: Pointer to the hw-function [opaque to some].
* @connection_handle: Connection handle.
* @pkt: Info regarding the tx packet.
* @notify_fw: Issue doorbell to fw for this packet.
*
* Return: 0 on success, failure otherwise.
*/
int qed_ll2_prepare_tx_packet(void *cxt,
u8 connection_handle,
struct qed_ll2_tx_pkt_info *pkt,
bool notify_fw);
/**
* qed_ll2_release_connection(): Releases resources allocated for LL2
* connection.
*
* @cxt: Pointer to the hw-function [opaque to some].
* @connection_handle: LL2 connection's handle obtained from
* qed_ll2_require_connection.
*
* Return: Void.
*/
void qed_ll2_release_connection(void *cxt, u8 connection_handle);
/**
* qed_ll2_set_fragment_of_tx_packet(): Provides fragments to fill
* Tx BD of BDs requested by
* qed_ll2_prepare_tx_packet
*
* @cxt: Pointer to the hw-function [opaque to some].
* @connection_handle: LL2 connection's handle obtained from
* qed_ll2_require_connection.
* @addr: Address.
* @nbytes: Number of bytes.
*
* Return: 0 on success, failure otherwise.
*/
int qed_ll2_set_fragment_of_tx_packet(void *cxt,
u8 connection_handle,
dma_addr_t addr, u16 nbytes);
/**
* qed_ll2_terminate_connection(): Stops Tx/Rx queues
*
* @cxt: Pointer to the hw-function [opaque to some].
* @connection_handle: LL2 connection's handle obtained from
* qed_ll2_require_connection.
*
* Return: 0 on success, failure otherwise.
*/
int qed_ll2_terminate_connection(void *cxt, u8 connection_handle);
/**
* qed_ll2_get_stats(): Get LL2 queue's statistics
*
* @cxt: Pointer to the hw-function [opaque to some].
* @connection_handle: LL2 connection's handle obtained from
* qed_ll2_require_connection.
* @p_stats: Pointer Status.
*
* Return: 0 on success, failure otherwise.
*/
int qed_ll2_get_stats(void *cxt,
u8 connection_handle, struct qed_ll2_stats *p_stats);
/**
* qed_ll2_alloc(): Allocates LL2 connections set.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_ll2_alloc(struct qed_hwfn *p_hwfn);
/**
* qed_ll2_setup(): Inits LL2 connections set.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*
*/
void qed_ll2_setup(struct qed_hwfn *p_hwfn);
/**
* qed_ll2_free(): Releases LL2 connections set
*
* @p_hwfn: HW device data.
*
* Return: Void.
*
*/
void qed_ll2_free(struct qed_hwfn *p_hwfn);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,829 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* Copyright 2021 Marvell. All rights reserved. */
#include <linux/types.h>
#include <asm/byteorder.h>
#include <asm/param.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/qed/qed_nvmetcp_if.h>
#include "qed.h"
#include "qed_cxt.h"
#include "qed_dev_api.h"
#include "qed_hsi.h"
#include "qed_hw.h"
#include "qed_int.h"
#include "qed_nvmetcp.h"
#include "qed_ll2.h"
#include "qed_mcp.h"
#include "qed_sp.h"
#include "qed_reg_addr.h"
#include "qed_nvmetcp_fw_funcs.h"
static int qed_nvmetcp_async_event(struct qed_hwfn *p_hwfn, u8 fw_event_code,
u16 echo, union event_ring_data *data,
u8 fw_return_code)
{
if (p_hwfn->p_nvmetcp_info->event_cb) {
struct qed_nvmetcp_info *p_nvmetcp = p_hwfn->p_nvmetcp_info;
return p_nvmetcp->event_cb(p_nvmetcp->event_context,
fw_event_code, data);
} else {
DP_NOTICE(p_hwfn, "nvmetcp async completion is not set\n");
return -EINVAL;
}
}
static int qed_sp_nvmetcp_func_start(struct qed_hwfn *p_hwfn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_addr,
void *event_context,
nvmetcp_event_cb_t async_event_cb)
{
struct nvmetcp_init_ramrod_params *p_ramrod = NULL;
struct qed_nvmetcp_pf_params *p_params = NULL;
struct scsi_init_func_queues *p_queue = NULL;
struct nvmetcp_spe_func_init *p_init = NULL;
struct qed_sp_init_data init_data = {};
struct qed_spq_entry *p_ent = NULL;
int rc = 0;
u16 val;
u8 i;
/* Get SPQ entry */
init_data.cid = qed_spq_get_cid(p_hwfn);
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_addr;
rc = qed_sp_init_request(p_hwfn, &p_ent,
NVMETCP_RAMROD_CMD_ID_INIT_FUNC,
PROTOCOLID_TCP_ULP, &init_data);
if (rc)
return rc;
p_ramrod = &p_ent->ramrod.nvmetcp_init;
p_init = &p_ramrod->nvmetcp_init_spe;
p_params = &p_hwfn->pf_params.nvmetcp_pf_params;
p_queue = &p_init->q_params;
p_init->num_sq_pages_in_ring = p_params->num_sq_pages_in_ring;
p_init->num_r2tq_pages_in_ring = p_params->num_r2tq_pages_in_ring;
p_init->num_uhq_pages_in_ring = p_params->num_uhq_pages_in_ring;
p_init->ll2_rx_queue_id = RESC_START(p_hwfn, QED_LL2_RAM_QUEUE) +
p_params->ll2_ooo_queue_id;
SET_FIELD(p_init->flags, NVMETCP_SPE_FUNC_INIT_NVMETCP_MODE, 1);
p_init->func_params.log_page_size = ilog2(PAGE_SIZE);
p_init->func_params.num_tasks = cpu_to_le16(p_params->num_tasks);
p_init->debug_flags = p_params->debug_mode;
DMA_REGPAIR_LE(p_queue->glbl_q_params_addr,
p_params->glbl_q_params_addr);
p_queue->cq_num_entries = cpu_to_le16(QED_NVMETCP_FW_CQ_SIZE);
p_queue->num_queues = p_params->num_queues;
val = RESC_START(p_hwfn, QED_CMDQS_CQS);
p_queue->queue_relative_offset = cpu_to_le16((u16)val);
p_queue->cq_sb_pi = p_params->gl_rq_pi;
for (i = 0; i < p_params->num_queues; i++) {
val = qed_get_igu_sb_id(p_hwfn, i);
p_queue->cq_cmdq_sb_num_arr[i] = cpu_to_le16(val);
}
SET_FIELD(p_queue->q_validity,
SCSI_INIT_FUNC_QUEUES_CMD_VALID, 0);
p_queue->cmdq_num_entries = 0;
p_queue->bdq_resource_id = (u8)RESC_START(p_hwfn, QED_BDQ);
p_ramrod->tcp_init.two_msl_timer = cpu_to_le32(QED_TCP_TWO_MSL_TIMER);
p_ramrod->tcp_init.tx_sws_timer = cpu_to_le16(QED_TCP_SWS_TIMER);
p_init->half_way_close_timeout = cpu_to_le16(QED_TCP_HALF_WAY_CLOSE_TIMEOUT);
p_ramrod->tcp_init.max_fin_rt = QED_TCP_MAX_FIN_RT;
SET_FIELD(p_ramrod->nvmetcp_init_spe.params,
NVMETCP_SPE_FUNC_INIT_MAX_SYN_RT, QED_TCP_MAX_FIN_RT);
p_hwfn->p_nvmetcp_info->event_context = event_context;
p_hwfn->p_nvmetcp_info->event_cb = async_event_cb;
qed_spq_register_async_cb(p_hwfn, PROTOCOLID_TCP_ULP,
qed_nvmetcp_async_event);
return qed_spq_post(p_hwfn, p_ent, NULL);
}
static int qed_sp_nvmetcp_func_stop(struct qed_hwfn *p_hwfn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_addr)
{
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = qed_spq_get_cid(p_hwfn);
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_addr;
rc = qed_sp_init_request(p_hwfn, &p_ent,
NVMETCP_RAMROD_CMD_ID_DESTROY_FUNC,
PROTOCOLID_TCP_ULP, &init_data);
if (rc)
return rc;
rc = qed_spq_post(p_hwfn, p_ent, NULL);
qed_spq_unregister_async_cb(p_hwfn, PROTOCOLID_TCP_ULP);
return rc;
}
static int qed_fill_nvmetcp_dev_info(struct qed_dev *cdev,
struct qed_dev_nvmetcp_info *info)
{
struct qed_hwfn *hwfn = QED_AFFIN_HWFN(cdev);
int rc;
memset(info, 0, sizeof(*info));
rc = qed_fill_dev_info(cdev, &info->common);
info->port_id = MFW_PORT(hwfn);
info->num_cqs = FEAT_NUM(hwfn, QED_NVMETCP_CQ);
return rc;
}
static void qed_register_nvmetcp_ops(struct qed_dev *cdev,
struct qed_nvmetcp_cb_ops *ops,
void *cookie)
{
cdev->protocol_ops.nvmetcp = ops;
cdev->ops_cookie = cookie;
}
static int qed_nvmetcp_stop(struct qed_dev *cdev)
{
int rc;
if (!(cdev->flags & QED_FLAG_STORAGE_STARTED)) {
DP_NOTICE(cdev, "nvmetcp already stopped\n");
return 0;
}
if (!hash_empty(cdev->connections)) {
DP_NOTICE(cdev,
"Can't stop nvmetcp - not all connections were returned\n");
return -EINVAL;
}
/* Stop the nvmetcp */
rc = qed_sp_nvmetcp_func_stop(QED_AFFIN_HWFN(cdev), QED_SPQ_MODE_EBLOCK,
NULL);
cdev->flags &= ~QED_FLAG_STORAGE_STARTED;
return rc;
}
static int qed_nvmetcp_start(struct qed_dev *cdev,
struct qed_nvmetcp_tid *tasks,
void *event_context,
nvmetcp_event_cb_t async_event_cb)
{
struct qed_tid_mem *tid_info;
int rc;
if (cdev->flags & QED_FLAG_STORAGE_STARTED) {
DP_NOTICE(cdev, "nvmetcp already started;\n");
return 0;
}
rc = qed_sp_nvmetcp_func_start(QED_AFFIN_HWFN(cdev),
QED_SPQ_MODE_EBLOCK, NULL,
event_context, async_event_cb);
if (rc) {
DP_NOTICE(cdev, "Failed to start nvmetcp\n");
return rc;
}
cdev->flags |= QED_FLAG_STORAGE_STARTED;
hash_init(cdev->connections);
if (!tasks)
return 0;
tid_info = kzalloc_obj(*tid_info);
if (!tid_info) {
qed_nvmetcp_stop(cdev);
return -ENOMEM;
}
rc = qed_cxt_get_tid_mem_info(QED_AFFIN_HWFN(cdev), tid_info);
if (rc) {
DP_NOTICE(cdev, "Failed to gather task information\n");
qed_nvmetcp_stop(cdev);
kfree(tid_info);
return rc;
}
/* Fill task information */
tasks->size = tid_info->tid_size;
tasks->num_tids_per_block = tid_info->num_tids_per_block;
memcpy(tasks->blocks, tid_info->blocks,
MAX_TID_BLOCKS_NVMETCP * sizeof(u8 *));
kfree(tid_info);
return 0;
}
static struct qed_hash_nvmetcp_con *qed_nvmetcp_get_hash(struct qed_dev *cdev,
u32 handle)
{
struct qed_hash_nvmetcp_con *hash_con = NULL;
if (!(cdev->flags & QED_FLAG_STORAGE_STARTED))
return NULL;
hash_for_each_possible(cdev->connections, hash_con, node, handle) {
if (hash_con->con->icid == handle)
break;
}
if (!hash_con || hash_con->con->icid != handle)
return NULL;
return hash_con;
}
static int qed_sp_nvmetcp_conn_offload(struct qed_hwfn *p_hwfn,
struct qed_nvmetcp_conn *p_conn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_addr)
{
struct nvmetcp_spe_conn_offload *p_ramrod = NULL;
struct tcp_offload_params_opt2 *p_tcp = NULL;
struct qed_sp_init_data init_data = { 0 };
struct qed_spq_entry *p_ent = NULL;
dma_addr_t r2tq_pbl_addr;
dma_addr_t xhq_pbl_addr;
dma_addr_t uhq_pbl_addr;
u16 physical_q;
int rc = 0;
u8 i;
/* Get SPQ entry */
init_data.cid = p_conn->icid;
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_addr;
rc = qed_sp_init_request(p_hwfn, &p_ent,
NVMETCP_RAMROD_CMD_ID_OFFLOAD_CONN,
PROTOCOLID_TCP_ULP, &init_data);
if (rc)
return rc;
p_ramrod = &p_ent->ramrod.nvmetcp_conn_offload;
/* Transmission PQ is the first of the PF */
physical_q = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OFLD);
p_conn->physical_q0 = cpu_to_le16(physical_q);
p_ramrod->nvmetcp.physical_q0 = cpu_to_le16(physical_q);
/* nvmetcp Pure-ACK PQ */
physical_q = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_ACK);
p_conn->physical_q1 = cpu_to_le16(physical_q);
p_ramrod->nvmetcp.physical_q1 = cpu_to_le16(physical_q);
p_ramrod->conn_id = cpu_to_le16(p_conn->conn_id);
DMA_REGPAIR_LE(p_ramrod->nvmetcp.sq_pbl_addr, p_conn->sq_pbl_addr);
r2tq_pbl_addr = qed_chain_get_pbl_phys(&p_conn->r2tq);
DMA_REGPAIR_LE(p_ramrod->nvmetcp.r2tq_pbl_addr, r2tq_pbl_addr);
xhq_pbl_addr = qed_chain_get_pbl_phys(&p_conn->xhq);
DMA_REGPAIR_LE(p_ramrod->nvmetcp.xhq_pbl_addr, xhq_pbl_addr);
uhq_pbl_addr = qed_chain_get_pbl_phys(&p_conn->uhq);
DMA_REGPAIR_LE(p_ramrod->nvmetcp.uhq_pbl_addr, uhq_pbl_addr);
p_ramrod->nvmetcp.flags = p_conn->offl_flags;
p_ramrod->nvmetcp.default_cq = p_conn->default_cq;
p_ramrod->nvmetcp.initial_ack = 0;
DMA_REGPAIR_LE(p_ramrod->nvmetcp.nvmetcp.cccid_itid_table_addr,
p_conn->nvmetcp_cccid_itid_table_addr);
p_ramrod->nvmetcp.nvmetcp.cccid_max_range =
cpu_to_le16(p_conn->nvmetcp_cccid_max_range);
p_tcp = &p_ramrod->tcp;
qed_set_fw_mac_addr(&p_tcp->remote_mac_addr_hi,
&p_tcp->remote_mac_addr_mid,
&p_tcp->remote_mac_addr_lo, p_conn->remote_mac);
qed_set_fw_mac_addr(&p_tcp->local_mac_addr_hi,
&p_tcp->local_mac_addr_mid,
&p_tcp->local_mac_addr_lo, p_conn->local_mac);
p_tcp->vlan_id = cpu_to_le16(p_conn->vlan_id);
p_tcp->flags = cpu_to_le16(p_conn->tcp_flags);
p_tcp->ip_version = p_conn->ip_version;
if (p_tcp->ip_version == TCP_IPV6) {
for (i = 0; i < 4; i++) {
p_tcp->remote_ip[i] = cpu_to_le32(p_conn->remote_ip[i]);
p_tcp->local_ip[i] = cpu_to_le32(p_conn->local_ip[i]);
}
} else {
p_tcp->remote_ip[0] = cpu_to_le32(p_conn->remote_ip[0]);
p_tcp->local_ip[0] = cpu_to_le32(p_conn->local_ip[0]);
}
p_tcp->flow_label = cpu_to_le32(p_conn->flow_label);
p_tcp->ttl = p_conn->ttl;
p_tcp->tos_or_tc = p_conn->tos_or_tc;
p_tcp->remote_port = cpu_to_le16(p_conn->remote_port);
p_tcp->local_port = cpu_to_le16(p_conn->local_port);
p_tcp->mss = cpu_to_le16(p_conn->mss);
p_tcp->rcv_wnd_scale = p_conn->rcv_wnd_scale;
p_tcp->connect_mode = p_conn->connect_mode;
p_tcp->cwnd = cpu_to_le32(p_conn->cwnd);
p_tcp->ka_max_probe_cnt = p_conn->ka_max_probe_cnt;
p_tcp->ka_timeout = cpu_to_le32(p_conn->ka_timeout);
p_tcp->max_rt_time = cpu_to_le32(p_conn->max_rt_time);
p_tcp->ka_interval = cpu_to_le32(p_conn->ka_interval);
return qed_spq_post(p_hwfn, p_ent, NULL);
}
static int qed_sp_nvmetcp_conn_update(struct qed_hwfn *p_hwfn,
struct qed_nvmetcp_conn *p_conn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_addr)
{
struct nvmetcp_conn_update_ramrod_params *p_ramrod = NULL;
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc = -EINVAL;
u32 dval;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = p_conn->icid;
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_addr;
rc = qed_sp_init_request(p_hwfn, &p_ent,
NVMETCP_RAMROD_CMD_ID_UPDATE_CONN,
PROTOCOLID_TCP_ULP, &init_data);
if (rc)
return rc;
p_ramrod = &p_ent->ramrod.nvmetcp_conn_update;
p_ramrod->conn_id = cpu_to_le16(p_conn->conn_id);
p_ramrod->flags = p_conn->update_flag;
p_ramrod->max_seq_size = cpu_to_le32(p_conn->max_seq_size);
dval = p_conn->max_recv_pdu_length;
p_ramrod->max_recv_pdu_length = cpu_to_le32(dval);
dval = p_conn->max_send_pdu_length;
p_ramrod->max_send_pdu_length = cpu_to_le32(dval);
p_ramrod->first_seq_length = cpu_to_le32(p_conn->first_seq_length);
return qed_spq_post(p_hwfn, p_ent, NULL);
}
static int qed_sp_nvmetcp_conn_terminate(struct qed_hwfn *p_hwfn,
struct qed_nvmetcp_conn *p_conn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_addr)
{
struct nvmetcp_spe_conn_termination *p_ramrod = NULL;
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc = -EINVAL;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = p_conn->icid;
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_addr;
rc = qed_sp_init_request(p_hwfn, &p_ent,
NVMETCP_RAMROD_CMD_ID_TERMINATION_CONN,
PROTOCOLID_TCP_ULP, &init_data);
if (rc)
return rc;
p_ramrod = &p_ent->ramrod.nvmetcp_conn_terminate;
p_ramrod->conn_id = cpu_to_le16(p_conn->conn_id);
p_ramrod->abortive = p_conn->abortive_dsconnect;
return qed_spq_post(p_hwfn, p_ent, NULL);
}
static int qed_sp_nvmetcp_conn_clear_sq(struct qed_hwfn *p_hwfn,
struct qed_nvmetcp_conn *p_conn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_addr)
{
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc = -EINVAL;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = p_conn->icid;
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_addr;
rc = qed_sp_init_request(p_hwfn, &p_ent,
NVMETCP_RAMROD_CMD_ID_CLEAR_SQ,
PROTOCOLID_TCP_ULP, &init_data);
if (rc)
return rc;
return qed_spq_post(p_hwfn, p_ent, NULL);
}
static void __iomem *qed_nvmetcp_get_db_addr(struct qed_hwfn *p_hwfn, u32 cid)
{
return (u8 __iomem *)p_hwfn->doorbells +
qed_db_addr(cid, DQ_DEMS_LEGACY);
}
static int qed_nvmetcp_allocate_connection(struct qed_hwfn *p_hwfn,
struct qed_nvmetcp_conn **p_out_conn)
{
struct qed_chain_init_params params = {
.mode = QED_CHAIN_MODE_PBL,
.intended_use = QED_CHAIN_USE_TO_CONSUME_PRODUCE,
.cnt_type = QED_CHAIN_CNT_TYPE_U16,
};
struct qed_nvmetcp_pf_params *p_params = NULL;
struct qed_nvmetcp_conn *p_conn = NULL;
int rc = 0;
/* Try finding a free connection that can be used */
spin_lock_bh(&p_hwfn->p_nvmetcp_info->lock);
if (!list_empty(&p_hwfn->p_nvmetcp_info->free_list))
p_conn = list_first_entry(&p_hwfn->p_nvmetcp_info->free_list,
struct qed_nvmetcp_conn, list_entry);
if (p_conn) {
list_del(&p_conn->list_entry);
spin_unlock_bh(&p_hwfn->p_nvmetcp_info->lock);
*p_out_conn = p_conn;
return 0;
}
spin_unlock_bh(&p_hwfn->p_nvmetcp_info->lock);
/* Need to allocate a new connection */
p_params = &p_hwfn->pf_params.nvmetcp_pf_params;
p_conn = kzalloc_obj(*p_conn);
if (!p_conn)
return -ENOMEM;
params.num_elems = p_params->num_r2tq_pages_in_ring *
QED_CHAIN_PAGE_SIZE / sizeof(struct nvmetcp_wqe);
params.elem_size = sizeof(struct nvmetcp_wqe);
rc = qed_chain_alloc(p_hwfn->cdev, &p_conn->r2tq, &params);
if (rc)
goto nomem_r2tq;
params.num_elems = p_params->num_uhq_pages_in_ring *
QED_CHAIN_PAGE_SIZE / sizeof(struct iscsi_uhqe);
params.elem_size = sizeof(struct iscsi_uhqe);
rc = qed_chain_alloc(p_hwfn->cdev, &p_conn->uhq, &params);
if (rc)
goto nomem_uhq;
params.elem_size = sizeof(struct iscsi_xhqe);
rc = qed_chain_alloc(p_hwfn->cdev, &p_conn->xhq, &params);
if (rc)
goto nomem;
p_conn->free_on_delete = true;
*p_out_conn = p_conn;
return 0;
nomem:
qed_chain_free(p_hwfn->cdev, &p_conn->uhq);
nomem_uhq:
qed_chain_free(p_hwfn->cdev, &p_conn->r2tq);
nomem_r2tq:
kfree(p_conn);
return -ENOMEM;
}
static int qed_nvmetcp_acquire_connection(struct qed_hwfn *p_hwfn,
struct qed_nvmetcp_conn **p_out_conn)
{
struct qed_nvmetcp_conn *p_conn = NULL;
int rc = 0;
u32 icid;
spin_lock_bh(&p_hwfn->p_nvmetcp_info->lock);
rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_TCP_ULP, &icid);
spin_unlock_bh(&p_hwfn->p_nvmetcp_info->lock);
if (rc)
return rc;
rc = qed_nvmetcp_allocate_connection(p_hwfn, &p_conn);
if (rc) {
spin_lock_bh(&p_hwfn->p_nvmetcp_info->lock);
qed_cxt_release_cid(p_hwfn, icid);
spin_unlock_bh(&p_hwfn->p_nvmetcp_info->lock);
return rc;
}
p_conn->icid = icid;
p_conn->conn_id = (u16)icid;
p_conn->fw_cid = (p_hwfn->hw_info.opaque_fid << 16) | icid;
*p_out_conn = p_conn;
return rc;
}
static void qed_nvmetcp_release_connection(struct qed_hwfn *p_hwfn,
struct qed_nvmetcp_conn *p_conn)
{
spin_lock_bh(&p_hwfn->p_nvmetcp_info->lock);
list_add_tail(&p_conn->list_entry, &p_hwfn->p_nvmetcp_info->free_list);
qed_cxt_release_cid(p_hwfn, p_conn->icid);
spin_unlock_bh(&p_hwfn->p_nvmetcp_info->lock);
}
static void qed_nvmetcp_free_connection(struct qed_hwfn *p_hwfn,
struct qed_nvmetcp_conn *p_conn)
{
qed_chain_free(p_hwfn->cdev, &p_conn->xhq);
qed_chain_free(p_hwfn->cdev, &p_conn->uhq);
qed_chain_free(p_hwfn->cdev, &p_conn->r2tq);
kfree(p_conn);
}
int qed_nvmetcp_alloc(struct qed_hwfn *p_hwfn)
{
struct qed_nvmetcp_info *p_nvmetcp_info;
p_nvmetcp_info = kzalloc_obj(*p_nvmetcp_info);
if (!p_nvmetcp_info)
return -ENOMEM;
INIT_LIST_HEAD(&p_nvmetcp_info->free_list);
p_hwfn->p_nvmetcp_info = p_nvmetcp_info;
return 0;
}
void qed_nvmetcp_setup(struct qed_hwfn *p_hwfn)
{
spin_lock_init(&p_hwfn->p_nvmetcp_info->lock);
}
void qed_nvmetcp_free(struct qed_hwfn *p_hwfn)
{
struct qed_nvmetcp_conn *p_conn = NULL;
if (!p_hwfn->p_nvmetcp_info)
return;
while (!list_empty(&p_hwfn->p_nvmetcp_info->free_list)) {
p_conn = list_first_entry(&p_hwfn->p_nvmetcp_info->free_list,
struct qed_nvmetcp_conn, list_entry);
if (p_conn) {
list_del(&p_conn->list_entry);
qed_nvmetcp_free_connection(p_hwfn, p_conn);
}
}
kfree(p_hwfn->p_nvmetcp_info);
p_hwfn->p_nvmetcp_info = NULL;
}
static int qed_nvmetcp_acquire_conn(struct qed_dev *cdev,
u32 *handle,
u32 *fw_cid, void __iomem **p_doorbell)
{
struct qed_hash_nvmetcp_con *hash_con;
int rc;
/* Allocate a hashed connection */
hash_con = kzalloc_obj(*hash_con, GFP_ATOMIC);
if (!hash_con)
return -ENOMEM;
/* Acquire the connection */
rc = qed_nvmetcp_acquire_connection(QED_AFFIN_HWFN(cdev),
&hash_con->con);
if (rc) {
DP_NOTICE(cdev, "Failed to acquire Connection\n");
kfree(hash_con);
return rc;
}
/* Added the connection to hash table */
*handle = hash_con->con->icid;
*fw_cid = hash_con->con->fw_cid;
hash_add(cdev->connections, &hash_con->node, *handle);
if (p_doorbell)
*p_doorbell = qed_nvmetcp_get_db_addr(QED_AFFIN_HWFN(cdev),
*handle);
return 0;
}
static int qed_nvmetcp_release_conn(struct qed_dev *cdev, u32 handle)
{
struct qed_hash_nvmetcp_con *hash_con;
hash_con = qed_nvmetcp_get_hash(cdev, handle);
if (!hash_con) {
DP_NOTICE(cdev, "Failed to find connection for handle %d\n",
handle);
return -EINVAL;
}
hlist_del(&hash_con->node);
qed_nvmetcp_release_connection(QED_AFFIN_HWFN(cdev), hash_con->con);
kfree(hash_con);
return 0;
}
static int qed_nvmetcp_offload_conn(struct qed_dev *cdev, u32 handle,
struct qed_nvmetcp_params_offload *conn_info)
{
struct qed_hash_nvmetcp_con *hash_con;
struct qed_nvmetcp_conn *con;
hash_con = qed_nvmetcp_get_hash(cdev, handle);
if (!hash_con) {
DP_NOTICE(cdev, "Failed to find connection for handle %d\n",
handle);
return -EINVAL;
}
/* Update the connection with information from the params */
con = hash_con->con;
/* FW initializations */
con->layer_code = NVMETCP_SLOW_PATH_LAYER_CODE;
con->sq_pbl_addr = conn_info->sq_pbl_addr;
con->nvmetcp_cccid_max_range = conn_info->nvmetcp_cccid_max_range;
con->nvmetcp_cccid_itid_table_addr = conn_info->nvmetcp_cccid_itid_table_addr;
con->default_cq = conn_info->default_cq;
SET_FIELD(con->offl_flags, NVMETCP_CONN_OFFLOAD_PARAMS_TARGET_MODE, 0);
SET_FIELD(con->offl_flags, NVMETCP_CONN_OFFLOAD_PARAMS_NVMETCP_MODE, 1);
SET_FIELD(con->offl_flags, NVMETCP_CONN_OFFLOAD_PARAMS_TCP_ON_CHIP_1B, 1);
/* Networking and TCP stack initializations */
ether_addr_copy(con->local_mac, conn_info->src.mac);
ether_addr_copy(con->remote_mac, conn_info->dst.mac);
memcpy(con->local_ip, conn_info->src.ip, sizeof(con->local_ip));
memcpy(con->remote_ip, conn_info->dst.ip, sizeof(con->remote_ip));
con->local_port = conn_info->src.port;
con->remote_port = conn_info->dst.port;
con->vlan_id = conn_info->vlan_id;
if (conn_info->timestamp_en)
SET_FIELD(con->tcp_flags, TCP_OFFLOAD_PARAMS_OPT2_TS_EN, 1);
if (conn_info->delayed_ack_en)
SET_FIELD(con->tcp_flags, TCP_OFFLOAD_PARAMS_OPT2_DA_EN, 1);
if (conn_info->tcp_keep_alive_en)
SET_FIELD(con->tcp_flags, TCP_OFFLOAD_PARAMS_OPT2_KA_EN, 1);
if (conn_info->ecn_en)
SET_FIELD(con->tcp_flags, TCP_OFFLOAD_PARAMS_OPT2_ECN_EN, 1);
con->ip_version = conn_info->ip_version;
con->flow_label = QED_TCP_FLOW_LABEL;
con->ka_max_probe_cnt = conn_info->ka_max_probe_cnt;
con->ka_timeout = conn_info->ka_timeout;
con->ka_interval = conn_info->ka_interval;
con->max_rt_time = conn_info->max_rt_time;
con->ttl = conn_info->ttl;
con->tos_or_tc = conn_info->tos_or_tc;
con->mss = conn_info->mss;
con->cwnd = conn_info->cwnd;
con->rcv_wnd_scale = conn_info->rcv_wnd_scale;
con->connect_mode = 0;
return qed_sp_nvmetcp_conn_offload(QED_AFFIN_HWFN(cdev), con,
QED_SPQ_MODE_EBLOCK, NULL);
}
static int qed_nvmetcp_update_conn(struct qed_dev *cdev,
u32 handle,
struct qed_nvmetcp_params_update *conn_info)
{
struct qed_hash_nvmetcp_con *hash_con;
struct qed_nvmetcp_conn *con;
hash_con = qed_nvmetcp_get_hash(cdev, handle);
if (!hash_con) {
DP_NOTICE(cdev, "Failed to find connection for handle %d\n",
handle);
return -EINVAL;
}
/* Update the connection with information from the params */
con = hash_con->con;
SET_FIELD(con->update_flag,
ISCSI_CONN_UPDATE_RAMROD_PARAMS_INITIAL_R2T, 0);
SET_FIELD(con->update_flag,
ISCSI_CONN_UPDATE_RAMROD_PARAMS_IMMEDIATE_DATA, 1);
if (conn_info->hdr_digest_en)
SET_FIELD(con->update_flag, ISCSI_CONN_UPDATE_RAMROD_PARAMS_HD_EN, 1);
if (conn_info->data_digest_en)
SET_FIELD(con->update_flag, ISCSI_CONN_UPDATE_RAMROD_PARAMS_DD_EN, 1);
/* Placeholder - initialize pfv, cpda, hpda */
con->max_seq_size = conn_info->max_io_size;
con->max_recv_pdu_length = conn_info->max_recv_pdu_length;
con->max_send_pdu_length = conn_info->max_send_pdu_length;
con->first_seq_length = conn_info->max_io_size;
return qed_sp_nvmetcp_conn_update(QED_AFFIN_HWFN(cdev), con,
QED_SPQ_MODE_EBLOCK, NULL);
}
static int qed_nvmetcp_clear_conn_sq(struct qed_dev *cdev, u32 handle)
{
struct qed_hash_nvmetcp_con *hash_con;
hash_con = qed_nvmetcp_get_hash(cdev, handle);
if (!hash_con) {
DP_NOTICE(cdev, "Failed to find connection for handle %d\n",
handle);
return -EINVAL;
}
return qed_sp_nvmetcp_conn_clear_sq(QED_AFFIN_HWFN(cdev), hash_con->con,
QED_SPQ_MODE_EBLOCK, NULL);
}
static int qed_nvmetcp_destroy_conn(struct qed_dev *cdev,
u32 handle, u8 abrt_conn)
{
struct qed_hash_nvmetcp_con *hash_con;
hash_con = qed_nvmetcp_get_hash(cdev, handle);
if (!hash_con) {
DP_NOTICE(cdev, "Failed to find connection for handle %d\n",
handle);
return -EINVAL;
}
hash_con->con->abortive_dsconnect = abrt_conn;
return qed_sp_nvmetcp_conn_terminate(QED_AFFIN_HWFN(cdev), hash_con->con,
QED_SPQ_MODE_EBLOCK, NULL);
}
static const struct qed_nvmetcp_ops qed_nvmetcp_ops_pass = {
.common = &qed_common_ops_pass,
.ll2 = &qed_ll2_ops_pass,
.fill_dev_info = &qed_fill_nvmetcp_dev_info,
.register_ops = &qed_register_nvmetcp_ops,
.start = &qed_nvmetcp_start,
.stop = &qed_nvmetcp_stop,
.acquire_conn = &qed_nvmetcp_acquire_conn,
.release_conn = &qed_nvmetcp_release_conn,
.offload_conn = &qed_nvmetcp_offload_conn,
.update_conn = &qed_nvmetcp_update_conn,
.destroy_conn = &qed_nvmetcp_destroy_conn,
.clear_sq = &qed_nvmetcp_clear_conn_sq,
.add_src_tcp_port_filter = &qed_llh_add_src_tcp_port_filter,
.remove_src_tcp_port_filter = &qed_llh_remove_src_tcp_port_filter,
.add_dst_tcp_port_filter = &qed_llh_add_dst_tcp_port_filter,
.remove_dst_tcp_port_filter = &qed_llh_remove_dst_tcp_port_filter,
.clear_all_filters = &qed_llh_clear_all_filters,
.init_read_io = &init_nvmetcp_host_read_task,
.init_write_io = &init_nvmetcp_host_write_task,
.init_icreq_exchange = &init_nvmetcp_init_conn_req_task,
.init_task_cleanup = &init_cleanup_task_nvmetcp
};
const struct qed_nvmetcp_ops *qed_get_nvmetcp_ops(void)
{
return &qed_nvmetcp_ops_pass;
}
EXPORT_SYMBOL(qed_get_nvmetcp_ops);
void qed_put_nvmetcp_ops(void)
{
}
EXPORT_SYMBOL(qed_put_nvmetcp_ops);
@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* Copyright 2021 Marvell. All rights reserved. */
#ifndef _QED_NVMETCP_H
#define _QED_NVMETCP_H
#include <linux/types.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/qed/tcp_common.h>
#include <linux/qed/qed_nvmetcp_if.h>
#include <linux/qed/qed_chain.h>
#include "qed.h"
#include "qed_hsi.h"
#include "qed_mcp.h"
#include "qed_sp.h"
#define QED_NVMETCP_FW_CQ_SIZE (4 * 1024)
/* tcp parameters */
#define QED_TCP_FLOW_LABEL 0
#define QED_TCP_TWO_MSL_TIMER 4000
#define QED_TCP_HALF_WAY_CLOSE_TIMEOUT 10
#define QED_TCP_MAX_FIN_RT 2
#define QED_TCP_SWS_TIMER 5000
struct qed_nvmetcp_info {
spinlock_t lock; /* Connection resources. */
struct list_head free_list;
u16 max_num_outstanding_tasks;
void *event_context;
nvmetcp_event_cb_t event_cb;
};
struct qed_hash_nvmetcp_con {
struct hlist_node node;
struct qed_nvmetcp_conn *con;
};
struct qed_nvmetcp_conn {
struct list_head list_entry;
bool free_on_delete;
u16 conn_id;
u32 icid;
u32 fw_cid;
u8 layer_code;
u8 offl_flags;
u8 connect_mode;
dma_addr_t sq_pbl_addr;
struct qed_chain r2tq;
struct qed_chain xhq;
struct qed_chain uhq;
u8 local_mac[6];
u8 remote_mac[6];
u8 ip_version;
u8 ka_max_probe_cnt;
u16 vlan_id;
u16 tcp_flags;
u32 remote_ip[4];
u32 local_ip[4];
u32 flow_label;
u32 ka_timeout;
u32 ka_interval;
u32 max_rt_time;
u8 ttl;
u8 tos_or_tc;
u16 remote_port;
u16 local_port;
u16 mss;
u8 rcv_wnd_scale;
u32 rcv_wnd;
u32 cwnd;
u8 update_flag;
u8 default_cq;
u8 abortive_dsconnect;
u32 max_seq_size;
u32 max_recv_pdu_length;
u32 max_send_pdu_length;
u32 first_seq_length;
u16 physical_q0;
u16 physical_q1;
u16 nvmetcp_cccid_max_range;
dma_addr_t nvmetcp_cccid_itid_table_addr;
};
#if IS_ENABLED(CONFIG_QED_NVMETCP)
int qed_nvmetcp_alloc(struct qed_hwfn *p_hwfn);
void qed_nvmetcp_setup(struct qed_hwfn *p_hwfn);
void qed_nvmetcp_free(struct qed_hwfn *p_hwfn);
#else /* IS_ENABLED(CONFIG_QED_NVMETCP) */
static inline int qed_nvmetcp_alloc(struct qed_hwfn *p_hwfn)
{
return -EINVAL;
}
static inline void qed_nvmetcp_setup(struct qed_hwfn *p_hwfn) {}
static inline void qed_nvmetcp_free(struct qed_hwfn *p_hwfn) {}
#endif /* IS_ENABLED(CONFIG_QED_NVMETCP) */
#endif
@@ -0,0 +1,375 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* Copyright 2021 Marvell. All rights reserved. */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <asm/byteorder.h>
#include <linux/qed/common_hsi.h>
#include <linux/qed/storage_common.h>
#include <linux/qed/nvmetcp_common.h>
#include <linux/qed/qed_nvmetcp_if.h>
#include "qed_nvmetcp_fw_funcs.h"
#define NVMETCP_NUM_SGES_IN_CACHE 0x4
bool nvmetcp_is_slow_sgl(u16 num_sges, bool small_mid_sge)
{
return (num_sges > SCSI_NUM_SGES_SLOW_SGL_THR && small_mid_sge);
}
void init_scsi_sgl_context(struct scsi_sgl_params *ctx_sgl_params,
struct scsi_cached_sges *ctx_data_desc,
struct storage_sgl_task_params *sgl_params)
{
u8 num_sges_to_init = (u8)(sgl_params->num_sges > NVMETCP_NUM_SGES_IN_CACHE ?
NVMETCP_NUM_SGES_IN_CACHE : sgl_params->num_sges);
u8 sge_index;
/* sgl params */
ctx_sgl_params->sgl_addr.lo = cpu_to_le32(sgl_params->sgl_phys_addr.lo);
ctx_sgl_params->sgl_addr.hi = cpu_to_le32(sgl_params->sgl_phys_addr.hi);
ctx_sgl_params->sgl_total_length = cpu_to_le32(sgl_params->total_buffer_size);
ctx_sgl_params->sgl_num_sges = cpu_to_le16(sgl_params->num_sges);
for (sge_index = 0; sge_index < num_sges_to_init; sge_index++) {
ctx_data_desc->sge[sge_index].sge_addr.lo =
cpu_to_le32(sgl_params->sgl[sge_index].sge_addr.lo);
ctx_data_desc->sge[sge_index].sge_addr.hi =
cpu_to_le32(sgl_params->sgl[sge_index].sge_addr.hi);
ctx_data_desc->sge[sge_index].sge_len =
cpu_to_le32(sgl_params->sgl[sge_index].sge_len);
}
}
static inline u32 calc_rw_task_size(struct nvmetcp_task_params *task_params,
enum nvmetcp_task_type task_type)
{
u32 io_size;
if (task_type == NVMETCP_TASK_TYPE_HOST_WRITE)
io_size = task_params->tx_io_size;
else
io_size = task_params->rx_io_size;
if (unlikely(!io_size))
return 0;
return io_size;
}
static inline void init_sqe(struct nvmetcp_task_params *task_params,
struct storage_sgl_task_params *sgl_task_params,
enum nvmetcp_task_type task_type)
{
if (!task_params->sqe)
return;
memset(task_params->sqe, 0, sizeof(*task_params->sqe));
task_params->sqe->task_id = cpu_to_le16(task_params->itid);
switch (task_type) {
case NVMETCP_TASK_TYPE_HOST_WRITE: {
u32 buf_size = 0;
u32 num_sges = 0;
SET_FIELD(task_params->sqe->contlen_cdbsize,
NVMETCP_WQE_CDB_SIZE_OR_NVMETCP_CMD, 1);
SET_FIELD(task_params->sqe->flags, NVMETCP_WQE_WQE_TYPE,
NVMETCP_WQE_TYPE_NORMAL);
if (task_params->tx_io_size) {
if (task_params->send_write_incapsule)
buf_size = calc_rw_task_size(task_params, task_type);
if (nvmetcp_is_slow_sgl(sgl_task_params->num_sges,
sgl_task_params->small_mid_sge))
num_sges = NVMETCP_WQE_NUM_SGES_SLOWIO;
else
num_sges = min((u16)sgl_task_params->num_sges,
(u16)SCSI_NUM_SGES_SLOW_SGL_THR);
}
SET_FIELD(task_params->sqe->flags, NVMETCP_WQE_NUM_SGES, num_sges);
SET_FIELD(task_params->sqe->contlen_cdbsize, NVMETCP_WQE_CONT_LEN, buf_size);
} break;
case NVMETCP_TASK_TYPE_HOST_READ: {
SET_FIELD(task_params->sqe->flags, NVMETCP_WQE_WQE_TYPE,
NVMETCP_WQE_TYPE_NORMAL);
SET_FIELD(task_params->sqe->contlen_cdbsize,
NVMETCP_WQE_CDB_SIZE_OR_NVMETCP_CMD, 1);
} break;
case NVMETCP_TASK_TYPE_INIT_CONN_REQUEST: {
SET_FIELD(task_params->sqe->flags, NVMETCP_WQE_WQE_TYPE,
NVMETCP_WQE_TYPE_MIDDLE_PATH);
if (task_params->tx_io_size) {
SET_FIELD(task_params->sqe->contlen_cdbsize, NVMETCP_WQE_CONT_LEN,
task_params->tx_io_size);
SET_FIELD(task_params->sqe->flags, NVMETCP_WQE_NUM_SGES,
min((u16)sgl_task_params->num_sges,
(u16)SCSI_NUM_SGES_SLOW_SGL_THR));
}
} break;
case NVMETCP_TASK_TYPE_CLEANUP:
SET_FIELD(task_params->sqe->flags, NVMETCP_WQE_WQE_TYPE,
NVMETCP_WQE_TYPE_TASK_CLEANUP);
default:
break;
}
}
/* The following function initializes of NVMeTCP task params */
static inline void
init_nvmetcp_task_params(struct e5_nvmetcp_task_context *context,
struct nvmetcp_task_params *task_params,
enum nvmetcp_task_type task_type)
{
context->ystorm_st_context.state.cccid = task_params->host_cccid;
SET_FIELD(context->ustorm_st_context.error_flags, USTORM_NVMETCP_TASK_ST_CTX_NVME_TCP, 1);
context->ustorm_st_context.nvme_tcp_opaque_lo = cpu_to_le32(task_params->opq.lo);
context->ustorm_st_context.nvme_tcp_opaque_hi = cpu_to_le32(task_params->opq.hi);
}
/* The following function initializes default values to all tasks */
static inline void
init_default_nvmetcp_task(struct nvmetcp_task_params *task_params,
void *pdu_header, void *nvme_cmd,
enum nvmetcp_task_type task_type)
{
struct e5_nvmetcp_task_context *context = task_params->context;
const u8 val_byte = context->mstorm_ag_context.cdu_validation;
u8 dw_index;
memset(context, 0, sizeof(*context));
init_nvmetcp_task_params(context, task_params,
(enum nvmetcp_task_type)task_type);
/* Swapping requirements used below, will be removed in future FW versions */
if (task_type == NVMETCP_TASK_TYPE_HOST_WRITE ||
task_type == NVMETCP_TASK_TYPE_HOST_READ) {
for (dw_index = 0;
dw_index < QED_NVMETCP_CMN_HDR_SIZE / sizeof(u32);
dw_index++)
context->ystorm_st_context.pdu_hdr.task_hdr.reg[dw_index] =
cpu_to_le32(__swab32(((u32 *)pdu_header)[dw_index]));
for (dw_index = QED_NVMETCP_CMN_HDR_SIZE / sizeof(u32);
dw_index < QED_NVMETCP_CMD_HDR_SIZE / sizeof(u32);
dw_index++)
context->ystorm_st_context.pdu_hdr.task_hdr.reg[dw_index] =
cpu_to_le32(__swab32(((u32 *)nvme_cmd)[dw_index - 2]));
} else {
for (dw_index = 0;
dw_index < QED_NVMETCP_NON_IO_HDR_SIZE / sizeof(u32);
dw_index++)
context->ystorm_st_context.pdu_hdr.task_hdr.reg[dw_index] =
cpu_to_le32(__swab32(((u32 *)pdu_header)[dw_index]));
}
/* M-Storm Context: */
context->mstorm_ag_context.cdu_validation = val_byte;
context->mstorm_st_context.task_type = (u8)(task_type);
context->mstorm_ag_context.task_cid = cpu_to_le16(task_params->conn_icid);
/* Ustorm Context: */
SET_FIELD(context->ustorm_ag_context.flags1, E5_USTORM_NVMETCP_TASK_AG_CTX_R2T2RECV, 1);
context->ustorm_st_context.task_type = (u8)(task_type);
context->ustorm_st_context.cq_rss_number = task_params->cq_rss_number;
context->ustorm_ag_context.icid = cpu_to_le16(task_params->conn_icid);
}
/* The following function initializes the U-Storm Task Contexts */
static inline void
init_ustorm_task_contexts(struct ustorm_nvmetcp_task_st_ctx *ustorm_st_context,
struct e5_ustorm_nvmetcp_task_ag_ctx *ustorm_ag_context,
u32 remaining_recv_len,
u32 expected_data_transfer_len, u8 num_sges,
bool tx_dif_conn_err_en)
{
/* Remaining data to be received in bytes. Used in validations*/
ustorm_st_context->rem_rcv_len = cpu_to_le32(remaining_recv_len);
ustorm_ag_context->exp_data_acked = cpu_to_le32(expected_data_transfer_len);
ustorm_st_context->exp_data_transfer_len = cpu_to_le32(expected_data_transfer_len);
SET_FIELD(ustorm_st_context->reg1_map, REG1_NUM_SGES, num_sges);
SET_FIELD(ustorm_ag_context->flags2, E5_USTORM_NVMETCP_TASK_AG_CTX_DIF_ERROR_CF_EN,
tx_dif_conn_err_en ? 1 : 0);
}
/* The following function initializes Local Completion Contexts: */
static inline void
set_local_completion_context(struct e5_nvmetcp_task_context *context)
{
SET_FIELD(context->ystorm_st_context.state.flags,
YSTORM_NVMETCP_TASK_STATE_LOCAL_COMP, 1);
SET_FIELD(context->ustorm_st_context.flags,
USTORM_NVMETCP_TASK_ST_CTX_LOCAL_COMP, 1);
}
/* Common Fastpath task init function: */
static inline void
init_rw_nvmetcp_task(struct nvmetcp_task_params *task_params,
enum nvmetcp_task_type task_type,
void *pdu_header, void *nvme_cmd,
struct storage_sgl_task_params *sgl_task_params)
{
struct e5_nvmetcp_task_context *context = task_params->context;
u32 task_size = calc_rw_task_size(task_params, task_type);
bool slow_io = false;
u8 num_sges = 0;
init_default_nvmetcp_task(task_params, pdu_header, nvme_cmd, task_type);
/* Tx/Rx: */
if (task_params->tx_io_size) {
/* if data to transmit: */
init_scsi_sgl_context(&context->ystorm_st_context.state.sgl_params,
&context->ystorm_st_context.state.data_desc,
sgl_task_params);
slow_io = nvmetcp_is_slow_sgl(sgl_task_params->num_sges,
sgl_task_params->small_mid_sge);
num_sges =
(u8)(!slow_io ? min((u32)sgl_task_params->num_sges,
(u32)SCSI_NUM_SGES_SLOW_SGL_THR) :
NVMETCP_WQE_NUM_SGES_SLOWIO);
if (slow_io) {
SET_FIELD(context->ystorm_st_context.state.flags,
YSTORM_NVMETCP_TASK_STATE_SLOW_IO, 1);
}
} else if (task_params->rx_io_size) {
/* if data to receive: */
init_scsi_sgl_context(&context->mstorm_st_context.sgl_params,
&context->mstorm_st_context.data_desc,
sgl_task_params);
num_sges =
(u8)(!nvmetcp_is_slow_sgl(sgl_task_params->num_sges,
sgl_task_params->small_mid_sge) ?
min((u32)sgl_task_params->num_sges,
(u32)SCSI_NUM_SGES_SLOW_SGL_THR) :
NVMETCP_WQE_NUM_SGES_SLOWIO);
context->mstorm_st_context.rem_task_size = cpu_to_le32(task_size);
}
/* Ustorm context: */
init_ustorm_task_contexts(&context->ustorm_st_context,
&context->ustorm_ag_context,
/* Remaining Receive length is the Task Size */
task_size,
/* The size of the transmitted task */
task_size,
/* num_sges */
num_sges,
false);
/* Set exp_data_acked */
if (task_type == NVMETCP_TASK_TYPE_HOST_WRITE) {
if (task_params->send_write_incapsule)
context->ustorm_ag_context.exp_data_acked = task_size;
else
context->ustorm_ag_context.exp_data_acked = 0;
} else if (task_type == NVMETCP_TASK_TYPE_HOST_READ) {
context->ustorm_ag_context.exp_data_acked = 0;
}
context->ustorm_ag_context.exp_cont_len = 0;
init_sqe(task_params, sgl_task_params, task_type);
}
static void
init_common_initiator_read_task(struct nvmetcp_task_params *task_params,
struct nvme_tcp_cmd_pdu *cmd_pdu_header,
struct nvme_command *nvme_cmd,
struct storage_sgl_task_params *sgl_task_params)
{
init_rw_nvmetcp_task(task_params, NVMETCP_TASK_TYPE_HOST_READ,
cmd_pdu_header, nvme_cmd, sgl_task_params);
}
void init_nvmetcp_host_read_task(struct nvmetcp_task_params *task_params,
struct nvme_tcp_cmd_pdu *cmd_pdu_header,
struct nvme_command *nvme_cmd,
struct storage_sgl_task_params *sgl_task_params)
{
init_common_initiator_read_task(task_params, (void *)cmd_pdu_header,
(void *)nvme_cmd, sgl_task_params);
}
static void
init_common_initiator_write_task(struct nvmetcp_task_params *task_params,
struct nvme_tcp_cmd_pdu *cmd_pdu_header,
struct nvme_command *nvme_cmd,
struct storage_sgl_task_params *sgl_task_params)
{
init_rw_nvmetcp_task(task_params, NVMETCP_TASK_TYPE_HOST_WRITE,
cmd_pdu_header, nvme_cmd, sgl_task_params);
}
void init_nvmetcp_host_write_task(struct nvmetcp_task_params *task_params,
struct nvme_tcp_cmd_pdu *cmd_pdu_header,
struct nvme_command *nvme_cmd,
struct storage_sgl_task_params *sgl_task_params)
{
init_common_initiator_write_task(task_params, (void *)cmd_pdu_header,
(void *)nvme_cmd, sgl_task_params);
}
static void
init_common_login_request_task(struct nvmetcp_task_params *task_params,
void *login_req_pdu_header,
struct storage_sgl_task_params *tx_sgl_task_params,
struct storage_sgl_task_params *rx_sgl_task_params)
{
struct e5_nvmetcp_task_context *context = task_params->context;
init_default_nvmetcp_task(task_params, (void *)login_req_pdu_header, NULL,
NVMETCP_TASK_TYPE_INIT_CONN_REQUEST);
/* Ustorm Context: */
init_ustorm_task_contexts(&context->ustorm_st_context,
&context->ustorm_ag_context,
/* Remaining Receive length is the Task Size */
task_params->rx_io_size ?
rx_sgl_task_params->total_buffer_size : 0,
/* The size of the transmitted task */
task_params->tx_io_size ?
tx_sgl_task_params->total_buffer_size : 0,
0, /* num_sges */
0); /* tx_dif_conn_err_en */
/* SGL context: */
if (task_params->tx_io_size)
init_scsi_sgl_context(&context->ystorm_st_context.state.sgl_params,
&context->ystorm_st_context.state.data_desc,
tx_sgl_task_params);
if (task_params->rx_io_size)
init_scsi_sgl_context(&context->mstorm_st_context.sgl_params,
&context->mstorm_st_context.data_desc,
rx_sgl_task_params);
context->mstorm_st_context.rem_task_size =
cpu_to_le32(task_params->rx_io_size ?
rx_sgl_task_params->total_buffer_size : 0);
init_sqe(task_params, tx_sgl_task_params, NVMETCP_TASK_TYPE_INIT_CONN_REQUEST);
}
/* The following function initializes Login task in Host mode: */
void init_nvmetcp_init_conn_req_task(struct nvmetcp_task_params *task_params,
struct nvme_tcp_icreq_pdu *init_conn_req_pdu_hdr,
struct storage_sgl_task_params *tx_sgl_task_params,
struct storage_sgl_task_params *rx_sgl_task_params)
{
init_common_login_request_task(task_params, init_conn_req_pdu_hdr,
tx_sgl_task_params, rx_sgl_task_params);
}
void init_cleanup_task_nvmetcp(struct nvmetcp_task_params *task_params)
{
init_sqe(task_params, NULL, NVMETCP_TASK_TYPE_CLEANUP);
}
@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* Copyright 2021 Marvell. All rights reserved. */
#ifndef _QED_NVMETCP_FW_FUNCS_H
#define _QED_NVMETCP_FW_FUNCS_H
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <asm/byteorder.h>
#include <linux/qed/common_hsi.h>
#include <linux/qed/storage_common.h>
#include <linux/qed/nvmetcp_common.h>
#include <linux/qed/qed_nvmetcp_if.h>
#if IS_ENABLED(CONFIG_QED_NVMETCP)
void init_nvmetcp_host_read_task(struct nvmetcp_task_params *task_params,
struct nvme_tcp_cmd_pdu *cmd_pdu_header,
struct nvme_command *nvme_cmd,
struct storage_sgl_task_params *sgl_task_params);
void init_nvmetcp_host_write_task(struct nvmetcp_task_params *task_params,
struct nvme_tcp_cmd_pdu *cmd_pdu_header,
struct nvme_command *nvme_cmd,
struct storage_sgl_task_params *sgl_task_params);
void init_nvmetcp_init_conn_req_task(struct nvmetcp_task_params *task_params,
struct nvme_tcp_icreq_pdu *init_conn_req_pdu_hdr,
struct storage_sgl_task_params *tx_sgl_task_params,
struct storage_sgl_task_params *rx_sgl_task_params);
void init_cleanup_task_nvmetcp(struct nvmetcp_task_params *task_params);
#else /* IS_ENABLED(CONFIG_QED_NVMETCP) */
#endif /* IS_ENABLED(CONFIG_QED_NVMETCP) */
#endif /* _QED_NVMETCP_FW_FUNCS_H */
@@ -0,0 +1,454 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#include <linux/types.h>
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "qed.h"
#include "qed_iscsi.h"
#include "qed_ll2.h"
#include "qed_ooo.h"
#include "qed_cxt.h"
#include "qed_nvmetcp.h"
static struct qed_ooo_archipelago
*qed_ooo_seek_archipelago(struct qed_hwfn *p_hwfn,
struct qed_ooo_info
*p_ooo_info,
u32 cid)
{
u32 idx = (cid & 0xffff) - p_ooo_info->cid_base;
struct qed_ooo_archipelago *p_archipelago;
if (unlikely(idx >= p_ooo_info->max_num_archipelagos))
return NULL;
p_archipelago = &p_ooo_info->p_archipelagos_mem[idx];
if (unlikely(list_empty(&p_archipelago->isles_list)))
return NULL;
return p_archipelago;
}
static struct qed_ooo_isle *qed_ooo_seek_isle(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid, u8 isle)
{
struct qed_ooo_archipelago *p_archipelago = NULL;
struct qed_ooo_isle *p_isle = NULL;
u8 the_num_of_isle = 1;
p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid);
if (unlikely(!p_archipelago)) {
DP_NOTICE(p_hwfn,
"Connection %d is not found in OOO list\n", cid);
return NULL;
}
list_for_each_entry(p_isle, &p_archipelago->isles_list, list_entry) {
if (the_num_of_isle == isle)
return p_isle;
the_num_of_isle++;
}
return NULL;
}
void qed_ooo_save_history_entry(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
struct ooo_opaque *p_cqe)
{
struct qed_ooo_history *p_history = &p_ooo_info->ooo_history;
if (p_history->head_idx == p_history->num_of_cqes)
p_history->head_idx = 0;
p_history->p_cqes[p_history->head_idx] = *p_cqe;
p_history->head_idx++;
}
int qed_ooo_alloc(struct qed_hwfn *p_hwfn)
{
u16 max_num_archipelagos = 0, cid_base;
struct qed_ooo_info *p_ooo_info;
enum protocol_type proto;
u16 max_num_isles = 0;
u32 i;
switch (p_hwfn->hw_info.personality) {
case QED_PCI_ISCSI:
case QED_PCI_NVMETCP:
proto = PROTOCOLID_TCP_ULP;
break;
case QED_PCI_ETH_RDMA:
case QED_PCI_ETH_IWARP:
proto = PROTOCOLID_IWARP;
break;
default:
DP_NOTICE(p_hwfn,
"Failed to allocate qed_ooo_info: unknown personality\n");
return -EINVAL;
}
max_num_archipelagos = (u16)qed_cxt_get_proto_cid_count(p_hwfn, proto,
NULL);
max_num_isles = QED_MAX_NUM_ISLES + max_num_archipelagos;
cid_base = (u16)qed_cxt_get_proto_cid_start(p_hwfn, proto);
if (!max_num_archipelagos) {
DP_NOTICE(p_hwfn,
"Failed to allocate qed_ooo_info: unknown amount of connections\n");
return -EINVAL;
}
p_ooo_info = kzalloc_obj(*p_ooo_info);
if (!p_ooo_info)
return -ENOMEM;
p_ooo_info->cid_base = cid_base;
p_ooo_info->max_num_archipelagos = max_num_archipelagos;
INIT_LIST_HEAD(&p_ooo_info->free_buffers_list);
INIT_LIST_HEAD(&p_ooo_info->ready_buffers_list);
INIT_LIST_HEAD(&p_ooo_info->free_isles_list);
p_ooo_info->p_isles_mem = kzalloc_objs(struct qed_ooo_isle,
max_num_isles);
if (!p_ooo_info->p_isles_mem)
goto no_isles_mem;
for (i = 0; i < max_num_isles; i++) {
INIT_LIST_HEAD(&p_ooo_info->p_isles_mem[i].buffers_list);
list_add_tail(&p_ooo_info->p_isles_mem[i].list_entry,
&p_ooo_info->free_isles_list);
}
p_ooo_info->p_archipelagos_mem =
kzalloc_objs(struct qed_ooo_archipelago,
max_num_archipelagos);
if (!p_ooo_info->p_archipelagos_mem)
goto no_archipelagos_mem;
for (i = 0; i < max_num_archipelagos; i++)
INIT_LIST_HEAD(&p_ooo_info->p_archipelagos_mem[i].isles_list);
p_ooo_info->ooo_history.p_cqes =
kzalloc_objs(struct ooo_opaque,
QED_MAX_NUM_OOO_HISTORY_ENTRIES);
if (!p_ooo_info->ooo_history.p_cqes)
goto no_history_mem;
p_ooo_info->ooo_history.num_of_cqes = QED_MAX_NUM_OOO_HISTORY_ENTRIES;
p_hwfn->p_ooo_info = p_ooo_info;
return 0;
no_history_mem:
kfree(p_ooo_info->p_archipelagos_mem);
no_archipelagos_mem:
kfree(p_ooo_info->p_isles_mem);
no_isles_mem:
kfree(p_ooo_info);
return -ENOMEM;
}
void qed_ooo_release_connection_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info, u32 cid)
{
struct qed_ooo_archipelago *p_archipelago;
struct qed_ooo_buffer *p_buffer;
struct qed_ooo_isle *p_isle;
p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid);
if (!p_archipelago)
return;
while (!list_empty(&p_archipelago->isles_list)) {
p_isle = list_first_entry(&p_archipelago->isles_list,
struct qed_ooo_isle, list_entry);
list_del(&p_isle->list_entry);
while (!list_empty(&p_isle->buffers_list)) {
p_buffer = list_first_entry(&p_isle->buffers_list,
struct qed_ooo_buffer,
list_entry);
list_move_tail(&p_buffer->list_entry,
&p_ooo_info->free_buffers_list);
}
list_add_tail(&p_isle->list_entry,
&p_ooo_info->free_isles_list);
}
}
void qed_ooo_release_all_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info)
{
struct qed_ooo_archipelago *p_archipelago;
struct qed_ooo_buffer *p_buffer;
struct qed_ooo_isle *p_isle;
u32 i;
for (i = 0; i < p_ooo_info->max_num_archipelagos; i++) {
p_archipelago = &(p_ooo_info->p_archipelagos_mem[i]);
while (!list_empty(&p_archipelago->isles_list)) {
p_isle = list_first_entry(&p_archipelago->isles_list,
struct qed_ooo_isle,
list_entry);
list_del(&p_isle->list_entry);
while (!list_empty(&p_isle->buffers_list)) {
p_buffer =
list_first_entry(&p_isle->buffers_list,
struct qed_ooo_buffer,
list_entry);
list_move_tail(&p_buffer->list_entry,
&p_ooo_info->free_buffers_list);
}
list_add_tail(&p_isle->list_entry,
&p_ooo_info->free_isles_list);
}
}
if (!list_empty(&p_ooo_info->ready_buffers_list))
list_splice_tail_init(&p_ooo_info->ready_buffers_list,
&p_ooo_info->free_buffers_list);
}
void qed_ooo_setup(struct qed_hwfn *p_hwfn)
{
qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info);
memset(p_hwfn->p_ooo_info->ooo_history.p_cqes, 0,
p_hwfn->p_ooo_info->ooo_history.num_of_cqes *
sizeof(struct ooo_opaque));
p_hwfn->p_ooo_info->ooo_history.head_idx = 0;
}
void qed_ooo_free(struct qed_hwfn *p_hwfn)
{
struct qed_ooo_info *p_ooo_info = p_hwfn->p_ooo_info;
struct qed_ooo_buffer *p_buffer;
if (!p_ooo_info)
return;
qed_ooo_release_all_isles(p_hwfn, p_ooo_info);
while (!list_empty(&p_ooo_info->free_buffers_list)) {
p_buffer = list_first_entry(&p_ooo_info->free_buffers_list,
struct qed_ooo_buffer, list_entry);
list_del(&p_buffer->list_entry);
dma_free_coherent(&p_hwfn->cdev->pdev->dev,
p_buffer->rx_buffer_size,
p_buffer->rx_buffer_virt_addr,
p_buffer->rx_buffer_phys_addr);
kfree(p_buffer);
}
kfree(p_ooo_info->p_isles_mem);
kfree(p_ooo_info->p_archipelagos_mem);
kfree(p_ooo_info->ooo_history.p_cqes);
kfree(p_ooo_info);
p_hwfn->p_ooo_info = NULL;
}
void qed_ooo_put_free_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
struct qed_ooo_buffer *p_buffer)
{
list_add_tail(&p_buffer->list_entry, &p_ooo_info->free_buffers_list);
}
struct qed_ooo_buffer *qed_ooo_get_free_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info)
{
struct qed_ooo_buffer *p_buffer = NULL;
if (!list_empty(&p_ooo_info->free_buffers_list)) {
p_buffer = list_first_entry(&p_ooo_info->free_buffers_list,
struct qed_ooo_buffer, list_entry);
list_del(&p_buffer->list_entry);
}
return p_buffer;
}
void qed_ooo_put_ready_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
struct qed_ooo_buffer *p_buffer, u8 on_tail)
{
if (on_tail)
list_add_tail(&p_buffer->list_entry,
&p_ooo_info->ready_buffers_list);
else
list_add(&p_buffer->list_entry,
&p_ooo_info->ready_buffers_list);
}
struct qed_ooo_buffer *qed_ooo_get_ready_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info)
{
struct qed_ooo_buffer *p_buffer = NULL;
if (!list_empty(&p_ooo_info->ready_buffers_list)) {
p_buffer = list_first_entry(&p_ooo_info->ready_buffers_list,
struct qed_ooo_buffer, list_entry);
list_del(&p_buffer->list_entry);
}
return p_buffer;
}
void qed_ooo_delete_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid, u8 drop_isle, u8 drop_size)
{
struct qed_ooo_isle *p_isle = NULL;
u8 isle_idx;
for (isle_idx = 0; isle_idx < drop_size; isle_idx++) {
p_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid, drop_isle);
if (!p_isle) {
DP_NOTICE(p_hwfn,
"Isle %d is not found(cid %d)\n",
drop_isle, cid);
return;
}
if (list_empty(&p_isle->buffers_list))
DP_NOTICE(p_hwfn,
"Isle %d is empty(cid %d)\n", drop_isle, cid);
else
list_splice_tail_init(&p_isle->buffers_list,
&p_ooo_info->free_buffers_list);
list_del(&p_isle->list_entry);
p_ooo_info->cur_isles_number--;
list_add(&p_isle->list_entry, &p_ooo_info->free_isles_list);
}
}
void qed_ooo_add_new_isle(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid, u8 ooo_isle,
struct qed_ooo_buffer *p_buffer)
{
struct qed_ooo_archipelago *p_archipelago = NULL;
struct qed_ooo_isle *p_prev_isle = NULL;
struct qed_ooo_isle *p_isle = NULL;
if (ooo_isle > 1) {
p_prev_isle = qed_ooo_seek_isle(p_hwfn,
p_ooo_info, cid, ooo_isle - 1);
if (unlikely(!p_prev_isle)) {
DP_NOTICE(p_hwfn,
"Isle %d is not found(cid %d)\n",
ooo_isle - 1, cid);
return;
}
}
p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid);
if (unlikely(!p_archipelago && ooo_isle != 1)) {
DP_NOTICE(p_hwfn,
"Connection %d is not found in OOO list\n", cid);
return;
}
if (!list_empty(&p_ooo_info->free_isles_list)) {
p_isle = list_first_entry(&p_ooo_info->free_isles_list,
struct qed_ooo_isle, list_entry);
list_del(&p_isle->list_entry);
if (unlikely(!list_empty(&p_isle->buffers_list))) {
DP_NOTICE(p_hwfn, "Free isle is not empty\n");
INIT_LIST_HEAD(&p_isle->buffers_list);
}
} else {
DP_NOTICE(p_hwfn, "No more free isles\n");
return;
}
if (!p_archipelago) {
u32 idx = (cid & 0xffff) - p_ooo_info->cid_base;
p_archipelago = &p_ooo_info->p_archipelagos_mem[idx];
}
list_add(&p_buffer->list_entry, &p_isle->buffers_list);
p_ooo_info->cur_isles_number++;
p_ooo_info->gen_isles_number++;
if (p_ooo_info->cur_isles_number > p_ooo_info->max_isles_number)
p_ooo_info->max_isles_number = p_ooo_info->cur_isles_number;
if (!p_prev_isle)
list_add(&p_isle->list_entry, &p_archipelago->isles_list);
else
list_add(&p_isle->list_entry, &p_prev_isle->list_entry);
}
void qed_ooo_add_new_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid,
u8 ooo_isle,
struct qed_ooo_buffer *p_buffer, u8 buffer_side)
{
struct qed_ooo_isle *p_isle = NULL;
p_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid, ooo_isle);
if (unlikely(!p_isle)) {
DP_NOTICE(p_hwfn,
"Isle %d is not found(cid %d)\n", ooo_isle, cid);
return;
}
if (unlikely(buffer_side == QED_OOO_LEFT_BUF))
list_add(&p_buffer->list_entry, &p_isle->buffers_list);
else
list_add_tail(&p_buffer->list_entry, &p_isle->buffers_list);
}
void qed_ooo_join_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info, u32 cid, u8 left_isle)
{
struct qed_ooo_isle *p_right_isle = NULL;
struct qed_ooo_isle *p_left_isle = NULL;
p_right_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid,
left_isle + 1);
if (unlikely(!p_right_isle)) {
DP_NOTICE(p_hwfn,
"Right isle %d is not found(cid %d)\n",
left_isle + 1, cid);
return;
}
list_del(&p_right_isle->list_entry);
p_ooo_info->cur_isles_number--;
if (left_isle) {
p_left_isle = qed_ooo_seek_isle(p_hwfn, p_ooo_info, cid,
left_isle);
if (unlikely(!p_left_isle)) {
DP_NOTICE(p_hwfn,
"Left isle %d is not found(cid %d)\n",
left_isle, cid);
return;
}
list_splice_tail_init(&p_right_isle->buffers_list,
&p_left_isle->buffers_list);
} else {
list_splice_tail_init(&p_right_isle->buffers_list,
&p_ooo_info->ready_buffers_list);
}
list_add_tail(&p_right_isle->list_entry, &p_ooo_info->free_isles_list);
}
@@ -0,0 +1,172 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_OOO_H
#define _QED_OOO_H
#include <linux/types.h>
#include <linux/list.h>
#include <linux/slab.h>
#include "qed.h"
#define QED_MAX_NUM_ISLES 256
#define QED_MAX_NUM_OOO_HISTORY_ENTRIES 512
#define QED_OOO_LEFT_BUF 0
#define QED_OOO_RIGHT_BUF 1
struct qed_ooo_buffer {
struct list_head list_entry;
void *rx_buffer_virt_addr;
dma_addr_t rx_buffer_phys_addr;
u32 rx_buffer_size;
u16 packet_length;
u16 parse_flags;
u16 vlan;
u8 placement_offset;
};
struct qed_ooo_isle {
struct list_head list_entry;
struct list_head buffers_list;
};
struct qed_ooo_archipelago {
struct list_head isles_list;
};
struct qed_ooo_history {
struct ooo_opaque *p_cqes;
u32 head_idx;
u32 num_of_cqes;
};
struct qed_ooo_info {
struct list_head free_buffers_list;
struct list_head ready_buffers_list;
struct list_head free_isles_list;
struct qed_ooo_archipelago *p_archipelagos_mem;
struct qed_ooo_isle *p_isles_mem;
struct qed_ooo_history ooo_history;
u32 cur_isles_number;
u32 max_isles_number;
u32 gen_isles_number;
u16 max_num_archipelagos;
u16 cid_base;
};
#if IS_ENABLED(CONFIG_QED_OOO)
void qed_ooo_save_history_entry(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
struct ooo_opaque *p_cqe);
int qed_ooo_alloc(struct qed_hwfn *p_hwfn);
void qed_ooo_setup(struct qed_hwfn *p_hwfn);
void qed_ooo_free(struct qed_hwfn *p_hwfn);
void qed_ooo_release_connection_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid);
void qed_ooo_release_all_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info);
void qed_ooo_put_free_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
struct qed_ooo_buffer *p_buffer);
struct qed_ooo_buffer *
qed_ooo_get_free_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info);
void qed_ooo_put_ready_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
struct qed_ooo_buffer *p_buffer, u8 on_tail);
struct qed_ooo_buffer *
qed_ooo_get_ready_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info);
void qed_ooo_delete_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid, u8 drop_isle, u8 drop_size);
void qed_ooo_add_new_isle(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid,
u8 ooo_isle, struct qed_ooo_buffer *p_buffer);
void qed_ooo_add_new_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid,
u8 ooo_isle,
struct qed_ooo_buffer *p_buffer, u8 buffer_side);
void qed_ooo_join_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info, u32 cid,
u8 left_isle);
#else /* IS_ENABLED(CONFIG_QED_ISCSI) */
static inline void qed_ooo_save_history_entry(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
struct ooo_opaque *p_cqe) {}
static inline int qed_ooo_alloc(struct qed_hwfn *p_hwfn)
{
return -EINVAL;
}
static inline void qed_ooo_setup(struct qed_hwfn *p_hwfn) {}
static inline void qed_ooo_free(struct qed_hwfn *p_hwfn) {}
static inline void
qed_ooo_release_connection_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid) {}
static inline void qed_ooo_release_all_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info)
{}
static inline void qed_ooo_put_free_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
struct qed_ooo_buffer *p_buffer) {}
static inline struct qed_ooo_buffer *
qed_ooo_get_free_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info) { return NULL; }
static inline void qed_ooo_put_ready_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
struct qed_ooo_buffer *p_buffer,
u8 on_tail) {}
static inline struct qed_ooo_buffer *
qed_ooo_get_ready_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info) { return NULL; }
static inline void qed_ooo_delete_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid, u8 drop_isle, u8 drop_size) {}
static inline void qed_ooo_add_new_isle(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid, u8 ooo_isle,
struct qed_ooo_buffer *p_buffer) {}
static inline void qed_ooo_add_new_buffer(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info,
u32 cid, u8 ooo_isle,
struct qed_ooo_buffer *p_buffer,
u8 buffer_side) {}
static inline void qed_ooo_join_isles(struct qed_hwfn *p_hwfn,
struct qed_ooo_info *p_ooo_info, u32 cid,
u8 left_isle) {}
#endif /* IS_ENABLED(CONFIG_QED_ISCSI) */
#endif
@@ -0,0 +1,433 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#include <linux/types.h>
#include "qed.h"
#include "qed_dev_api.h"
#include "qed_hw.h"
#include "qed_l2.h"
#include "qed_mcp.h"
#include "qed_ptp.h"
#include "qed_reg_addr.h"
/* 16 nano second time quantas to wait before making a Drift adjustment */
#define QED_DRIFT_CNTR_TIME_QUANTA_SHIFT 0
/* Nano seconds to add/subtract when making a Drift adjustment */
#define QED_DRIFT_CNTR_ADJUSTMENT_SHIFT 28
/* Add/subtract the Adjustment_Value when making a Drift adjustment */
#define QED_DRIFT_CNTR_DIRECTION_SHIFT 31
#define QED_TIMESTAMP_MASK BIT(16)
/* Param mask for Hardware to detect/timestamp the L2/L4 unicast PTP packets */
#define QED_PTP_UCAST_PARAM_MASK 0x70F
static enum qed_resc_lock qed_ptcdev_to_resc(struct qed_hwfn *p_hwfn)
{
switch (MFW_PORT(p_hwfn)) {
case 0:
return QED_RESC_LOCK_PTP_PORT0;
case 1:
return QED_RESC_LOCK_PTP_PORT1;
case 2:
return QED_RESC_LOCK_PTP_PORT2;
case 3:
return QED_RESC_LOCK_PTP_PORT3;
default:
return QED_RESC_LOCK_RESC_INVALID;
}
}
static int qed_ptp_res_lock(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{
struct qed_resc_lock_params params;
enum qed_resc_lock resource;
int rc;
resource = qed_ptcdev_to_resc(p_hwfn);
if (resource == QED_RESC_LOCK_RESC_INVALID)
return -EINVAL;
qed_mcp_resc_lock_default_init(&params, NULL, resource, true);
rc = qed_mcp_resc_lock(p_hwfn, p_ptt, &params);
if (rc && rc != -EINVAL) {
return rc;
} else if (rc == -EINVAL) {
/* MFW doesn't support resource locking, first PF on the port
* has lock ownership.
*/
if (p_hwfn->abs_pf_id < p_hwfn->cdev->num_ports_in_engine)
return 0;
DP_INFO(p_hwfn, "PF doesn't have lock ownership\n");
return -EBUSY;
} else if (!params.b_granted) {
DP_INFO(p_hwfn, "Failed to acquire ptp resource lock\n");
return -EBUSY;
}
return 0;
}
static int qed_ptp_res_unlock(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{
struct qed_resc_unlock_params params;
enum qed_resc_lock resource;
int rc;
resource = qed_ptcdev_to_resc(p_hwfn);
if (resource == QED_RESC_LOCK_RESC_INVALID)
return -EINVAL;
qed_mcp_resc_lock_default_init(NULL, &params, resource, true);
rc = qed_mcp_resc_unlock(p_hwfn, p_ptt, &params);
if (rc == -EINVAL) {
/* MFW doesn't support locking, first PF has lock ownership */
if (p_hwfn->abs_pf_id < p_hwfn->cdev->num_ports_in_engine) {
rc = 0;
} else {
DP_INFO(p_hwfn, "PF doesn't have lock ownership\n");
return -EINVAL;
}
} else if (rc) {
DP_INFO(p_hwfn, "Failed to release the ptp resource lock\n");
}
return rc;
}
/* Read Rx timestamp */
static int qed_ptp_hw_read_rx_ts(struct qed_dev *cdev, u64 *timestamp)
{
struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
u32 val;
*timestamp = 0;
val = qed_rd(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID);
if (!(val & QED_TIMESTAMP_MASK)) {
DP_INFO(p_hwfn, "Invalid Rx timestamp, buf_seqid = %d\n", val);
return -EINVAL;
}
val = qed_rd(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_TS_LSB);
*timestamp = qed_rd(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_TS_MSB);
*timestamp <<= 32;
*timestamp |= val;
/* Reset timestamp register to allow new timestamp */
qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID,
QED_TIMESTAMP_MASK);
return 0;
}
/* Read Tx timestamp */
static int qed_ptp_hw_read_tx_ts(struct qed_dev *cdev, u64 *timestamp)
{
struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
u32 val;
*timestamp = 0;
val = qed_rd(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_SEQID);
if (!(val & QED_TIMESTAMP_MASK)) {
DP_VERBOSE(p_hwfn, QED_MSG_DEBUG,
"Invalid Tx timestamp, buf_seqid = %08x\n", val);
return -EINVAL;
}
val = qed_rd(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_TS_LSB);
*timestamp = qed_rd(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_TS_MSB);
*timestamp <<= 32;
*timestamp |= val;
/* Reset timestamp register to allow new timestamp */
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_SEQID, QED_TIMESTAMP_MASK);
return 0;
}
/* Read Phy Hardware Clock */
static int qed_ptp_hw_read_cc(struct qed_dev *cdev, u64 *phc_cycles)
{
struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
u32 temp = 0;
temp = qed_rd(p_hwfn, p_ptt, NIG_REG_TSGEN_SYNC_TIME_LSB);
*phc_cycles = qed_rd(p_hwfn, p_ptt, NIG_REG_TSGEN_SYNC_TIME_MSB);
*phc_cycles <<= 32;
*phc_cycles |= temp;
return 0;
}
/* Filter PTP protocol packets that need to be timestamped */
static int qed_ptp_hw_cfg_filters(struct qed_dev *cdev,
enum qed_ptp_filter_type rx_type,
enum qed_ptp_hwtstamp_tx_type tx_type)
{
struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
u32 rule_mask, enable_cfg = 0x0;
switch (rx_type) {
case QED_PTP_FILTER_NONE:
enable_cfg = 0x0;
rule_mask = 0x3FFF;
break;
case QED_PTP_FILTER_ALL:
enable_cfg = 0x7;
rule_mask = 0x3CAA;
break;
case QED_PTP_FILTER_V1_L4_EVENT:
enable_cfg = 0x3;
rule_mask = 0x3FFA;
break;
case QED_PTP_FILTER_V1_L4_GEN:
enable_cfg = 0x3;
rule_mask = 0x3FFE;
break;
case QED_PTP_FILTER_V2_L4_EVENT:
enable_cfg = 0x5;
rule_mask = 0x3FAA;
break;
case QED_PTP_FILTER_V2_L4_GEN:
enable_cfg = 0x5;
rule_mask = 0x3FEE;
break;
case QED_PTP_FILTER_V2_L2_EVENT:
enable_cfg = 0x5;
rule_mask = 0x3CFF;
break;
case QED_PTP_FILTER_V2_L2_GEN:
enable_cfg = 0x5;
rule_mask = 0x3EFF;
break;
case QED_PTP_FILTER_V2_EVENT:
enable_cfg = 0x5;
rule_mask = 0x3CAA;
break;
case QED_PTP_FILTER_V2_GEN:
enable_cfg = 0x5;
rule_mask = 0x3EEE;
break;
default:
DP_INFO(p_hwfn, "Invalid PTP filter type %d\n", rx_type);
return -EINVAL;
}
qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK,
QED_PTP_UCAST_PARAM_MASK);
qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_RULE_MASK, rule_mask);
qed_wr(p_hwfn, p_ptt, NIG_REG_RX_PTP_EN, enable_cfg);
if (tx_type == QED_PTP_HWTSTAMP_TX_OFF) {
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, 0x0);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0x7FF);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, 0x3FFF);
} else {
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, enable_cfg);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK,
QED_PTP_UCAST_PARAM_MASK);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, rule_mask);
}
/* Reset possibly old timestamps */
qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID,
QED_TIMESTAMP_MASK);
return 0;
}
/* Adjust the HW clock by a rate given in parts-per-billion (ppb) units.
* FW/HW accepts the adjustment value in terms of 3 parameters:
* Drift period - adjustment happens once in certain number of nano seconds.
* Drift value - time is adjusted by a certain value, for example by 5 ns.
* Drift direction - add or subtract the adjustment value.
* The routine translates ppb into the adjustment triplet in an optimal manner.
*/
static int qed_ptp_hw_adjfreq(struct qed_dev *cdev, s32 ppb)
{
s64 best_val = 0, val, best_period = 0, period, approx_dev, dif, dif2;
struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
u32 drift_ctr_cfg = 0, drift_state;
int drift_dir = 1;
if (ppb < 0) {
ppb = -ppb;
drift_dir = 0;
}
if (ppb > 1) {
s64 best_dif = ppb, best_approx_dev = 1;
/* Adjustment value is up to +/-7ns, find an optimal value in
* this range.
*/
for (val = 7; val > 0; val--) {
period = div_s64(val * 1000000000, ppb);
period -= 8;
period >>= 4;
if (period < 1)
period = 1;
if (period > 0xFFFFFFE)
period = 0xFFFFFFE;
/* Check both rounding ends for approximate error */
approx_dev = period * 16 + 8;
dif = ppb * approx_dev - val * 1000000000;
dif2 = dif + 16 * ppb;
if (dif < 0)
dif = -dif;
if (dif2 < 0)
dif2 = -dif2;
/* Determine which end gives better approximation */
if (dif * (approx_dev + 16) > dif2 * approx_dev) {
period++;
approx_dev += 16;
dif = dif2;
}
/* Track best approximation found so far */
if (best_dif * approx_dev > dif * best_approx_dev) {
best_dif = dif;
best_val = val;
best_period = period;
best_approx_dev = approx_dev;
}
}
} else if (ppb == 1) {
/* This is a special case as its the only value which wouldn't
* fit in a s64 variable. In order to prevent castings simple
* handle it separately.
*/
best_val = 4;
best_period = 0xee6b27f;
} else {
best_val = 0;
best_period = 0xFFFFFFF;
}
drift_ctr_cfg = (best_period << QED_DRIFT_CNTR_TIME_QUANTA_SHIFT) |
(((int)best_val) << QED_DRIFT_CNTR_ADJUSTMENT_SHIFT) |
(((int)drift_dir) << QED_DRIFT_CNTR_DIRECTION_SHIFT);
qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR, 0x1);
drift_state = qed_rd(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR);
if (drift_state & 1) {
qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_DRIFT_CNTR_CONF,
drift_ctr_cfg);
} else {
DP_INFO(p_hwfn, "Drift counter is not reset\n");
return -EINVAL;
}
qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR, 0x0);
return 0;
}
static int qed_ptp_hw_enable(struct qed_dev *cdev)
{
struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
struct qed_ptt *p_ptt;
int rc;
p_ptt = qed_ptt_acquire(p_hwfn);
if (!p_ptt) {
DP_NOTICE(p_hwfn, "Failed to acquire PTT for PTP\n");
return -EBUSY;
}
p_hwfn->p_ptp_ptt = p_ptt;
rc = qed_ptp_res_lock(p_hwfn, p_ptt);
if (rc) {
DP_INFO(p_hwfn,
"Couldn't acquire the resource lock, skip ptp enable for this PF\n");
qed_ptt_release(p_hwfn, p_ptt);
p_hwfn->p_ptp_ptt = NULL;
return rc;
}
/* Reset PTP event detection rules - will be configured in the IOCTL */
qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, 0x7FF);
qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_RULE_MASK, 0x3FFF);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0x7FF);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, 0x3FFF);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, 7);
qed_wr(p_hwfn, p_ptt, NIG_REG_RX_PTP_EN, 7);
qed_wr(p_hwfn, p_ptt, NIG_REG_TS_OUTPUT_ENABLE_PDA, 0x1);
/* Pause free running counter */
if (QED_IS_BB_B0(p_hwfn->cdev))
qed_wr(p_hwfn, p_ptt, NIG_REG_TIMESYNC_GEN_REG_BB, 2);
if (QED_IS_AH(p_hwfn->cdev))
qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREECNT_UPDATE_K2, 2);
qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREE_CNT_VALUE_LSB, 0);
qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREE_CNT_VALUE_MSB, 0);
/* Resume free running counter */
if (QED_IS_BB_B0(p_hwfn->cdev))
qed_wr(p_hwfn, p_ptt, NIG_REG_TIMESYNC_GEN_REG_BB, 4);
if (QED_IS_AH(p_hwfn->cdev)) {
qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREECNT_UPDATE_K2, 4);
qed_wr(p_hwfn, p_ptt, NIG_REG_PTP_LATCH_OSTS_PKT_TIME, 1);
}
/* Disable drift register */
qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_DRIFT_CNTR_CONF, 0x0);
qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_RST_DRIFT_CNTR, 0x0);
/* Reset possibly old timestamps */
qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID,
QED_TIMESTAMP_MASK);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_BUF_SEQID, QED_TIMESTAMP_MASK);
return 0;
}
static int qed_ptp_hw_disable(struct qed_dev *cdev)
{
struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt;
qed_ptp_res_unlock(p_hwfn, p_ptt);
/* Reset PTP event detection rules */
qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, 0x7FF);
qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_RULE_MASK, 0x3FFF);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0x7FF);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, 0x3FFF);
/* Disable the PTP feature */
qed_wr(p_hwfn, p_ptt, NIG_REG_RX_PTP_EN, 0x0);
qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, 0x0);
qed_ptt_release(p_hwfn, p_ptt);
p_hwfn->p_ptp_ptt = NULL;
return 0;
}
const struct qed_eth_ptp_ops qed_ptp_ops_pass = {
.cfg_filters = qed_ptp_hw_cfg_filters,
.read_rx_ts = qed_ptp_hw_read_rx_ts,
.read_tx_ts = qed_ptp_hw_read_tx_ts,
.read_cc = qed_ptp_hw_read_cc,
.adjfreq = qed_ptp_hw_adjfreq,
.disable = qed_ptp_hw_disable,
.enable = qed_ptp_hw_enable,
};
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* Copyright (c) 2020 Marvell International Ltd. */
#ifndef __QED_PTP_H
#define __QED_PTP_H
extern const struct qed_eth_ptp_ops qed_ptp_ops_pass;
#endif /* __QED_PTP_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,210 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_RDMA_H
#define _QED_RDMA_H
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/qed/qed_if.h>
#include <linux/qed/qed_rdma_if.h>
#include "qed.h"
#include "qed_dev_api.h"
#include "qed_hsi.h"
#include "qed_iwarp.h"
#include "qed_roce.h"
#define QED_RDMA_MAX_P_KEY (1)
#define QED_RDMA_MAX_WQE (0x7FFF)
#define QED_RDMA_MAX_SRQ_WQE_ELEM (0x7FFF)
#define QED_RDMA_PAGE_SIZE_CAPS (0xFFFFF000)
#define QED_RDMA_ACK_DELAY (15)
#define QED_RDMA_MAX_MR_SIZE (0x10000000000ULL)
#define QED_RDMA_MAX_CQS (RDMA_MAX_CQS)
#define QED_RDMA_MAX_MRS (RDMA_MAX_TIDS)
/* Add 1 for header element */
#define QED_RDMA_MAX_SRQ_ELEM_PER_WQE (RDMA_MAX_SGE_PER_RQ_WQE + 1)
#define QED_RDMA_MAX_SGE_PER_SRQ_WQE (RDMA_MAX_SGE_PER_RQ_WQE)
#define QED_RDMA_SRQ_WQE_ELEM_SIZE (16)
#define QED_RDMA_MAX_SRQS (32 * 1024)
#define QED_RDMA_MAX_CQE_32_BIT (0x7FFFFFFF - 1)
#define QED_RDMA_MAX_CQE_16_BIT (0x7FFF - 1)
/* Up to 2^16 XRC Domains are supported, but the actual number of supported XRC
* SRQs is much smaller so there's no need to have that many domains.
*/
#define QED_RDMA_MAX_XRCDS (roundup_pow_of_two(RDMA_MAX_XRC_SRQS))
enum qed_rdma_toggle_bit {
QED_RDMA_TOGGLE_BIT_CLEAR = 0,
QED_RDMA_TOGGLE_BIT_SET = 1
};
#define QED_RDMA_MAX_BMAP_NAME (10)
struct qed_bmap {
unsigned long *bitmap;
u32 max_count;
char name[QED_RDMA_MAX_BMAP_NAME];
};
struct qed_rdma_info {
/* spin lock to protect bitmaps */
spinlock_t lock;
struct qed_bmap cq_map;
struct qed_bmap pd_map;
struct qed_bmap xrcd_map;
struct qed_bmap tid_map;
struct qed_bmap qp_map;
struct qed_bmap srq_map;
struct qed_bmap xrc_srq_map;
struct qed_bmap cid_map;
struct qed_bmap tcp_cid_map;
struct qed_bmap real_cid_map;
struct qed_bmap dpi_map;
struct qed_bmap toggle_bits;
struct qed_rdma_events events;
struct qed_rdma_device *dev;
struct qed_rdma_port *port;
u32 last_tid;
u8 num_cnqs;
u32 num_qps;
u32 num_mrs;
u32 num_srqs;
u16 srq_id_offset;
u16 queue_zone_base;
u16 max_queue_zones;
enum protocol_type proto;
struct qed_iwarp_info iwarp;
u8 active:1;
};
struct qed_rdma_qp {
struct regpair qp_handle;
struct regpair qp_handle_async;
u32 qpid;
u16 icid;
enum qed_roce_qp_state cur_state;
enum qed_rdma_qp_type qp_type;
enum qed_iwarp_qp_state iwarp_state;
bool use_srq;
bool signal_all;
bool fmr_and_reserved_lkey;
bool incoming_rdma_read_en;
bool incoming_rdma_write_en;
bool incoming_atomic_en;
bool e2e_flow_control_en;
u16 pd;
u16 pkey;
u32 dest_qp;
u16 mtu;
u16 srq_id;
u8 traffic_class_tos;
u8 hop_limit_ttl;
u16 dpi;
u32 flow_label;
bool lb_indication;
u16 vlan_id;
u32 ack_timeout;
u8 retry_cnt;
u8 rnr_retry_cnt;
u8 min_rnr_nak_timer;
bool sqd_async;
union qed_gid sgid;
union qed_gid dgid;
enum roce_mode roce_mode;
u16 udp_src_port;
u8 stats_queue;
/* requeseter */
u8 max_rd_atomic_req;
u32 sq_psn;
u16 sq_cq_id;
u16 sq_num_pages;
dma_addr_t sq_pbl_ptr;
void *orq;
dma_addr_t orq_phys_addr;
u8 orq_num_pages;
bool req_offloaded;
bool has_req;
/* responder */
u8 max_rd_atomic_resp;
u32 rq_psn;
u16 rq_cq_id;
u16 rq_num_pages;
u16 xrcd_id;
dma_addr_t rq_pbl_ptr;
void *irq;
dma_addr_t irq_phys_addr;
u8 irq_num_pages;
bool resp_offloaded;
u32 cq_prod;
bool has_resp;
u8 remote_mac_addr[6];
u8 local_mac_addr[6];
void *shared_queue;
dma_addr_t shared_queue_phys_addr;
struct qed_iwarp_ep *ep;
u8 edpm_mode;
};
static inline bool qed_rdma_is_xrc_qp(struct qed_rdma_qp *qp)
{
if (qp->qp_type == QED_RDMA_QP_TYPE_XRC_TGT ||
qp->qp_type == QED_RDMA_QP_TYPE_XRC_INI)
return true;
return false;
}
#if IS_ENABLED(CONFIG_QED_RDMA)
void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
void qed_rdma_dpm_conf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
int qed_rdma_info_alloc(struct qed_hwfn *p_hwfn);
void qed_rdma_info_free(struct qed_hwfn *p_hwfn);
#else
static inline void qed_rdma_dpm_conf(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt) {}
static inline void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt) {}
static inline int qed_rdma_info_alloc(struct qed_hwfn *p_hwfn)
{return -EINVAL; }
static inline void qed_rdma_info_free(struct qed_hwfn *p_hwfn) {}
#endif
int
qed_rdma_bmap_alloc(struct qed_hwfn *p_hwfn,
struct qed_bmap *bmap, u32 max_count, char *name);
void
qed_rdma_bmap_free(struct qed_hwfn *p_hwfn, struct qed_bmap *bmap, bool check);
int
qed_rdma_bmap_alloc_id(struct qed_hwfn *p_hwfn,
struct qed_bmap *bmap, u32 *id_num);
void
qed_bmap_set_id(struct qed_hwfn *p_hwfn, struct qed_bmap *bmap, u32 id_num);
void
qed_bmap_release_id(struct qed_hwfn *p_hwfn, struct qed_bmap *bmap, u32 id_num);
int
qed_bmap_test_id(struct qed_hwfn *p_hwfn, struct qed_bmap *bmap, u32 id_num);
void qed_rdma_set_fw_mac(__le16 *p_fw_mac, const u8 *p_qed_mac);
bool qed_rdma_allocated_qps(struct qed_hwfn *p_hwfn);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_ROCE_H
#define _QED_ROCE_H
#include <linux/types.h>
#include <linux/slab.h>
#if IS_ENABLED(CONFIG_QED_RDMA)
void qed_roce_dpm_dcbx(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
#else
static inline void qed_roce_dpm_dcbx(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt) {}
#endif
int qed_roce_setup(struct qed_hwfn *p_hwfn);
void qed_roce_stop(struct qed_hwfn *p_hwfn);
int qed_roce_init_hw(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
int qed_roce_alloc_cid(struct qed_hwfn *p_hwfn, u16 *cid);
int qed_roce_destroy_qp(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp);
int qed_roce_query_qp(struct qed_hwfn *p_hwfn,
struct qed_rdma_qp *qp,
struct qed_rdma_query_qp_out_params *out_params);
int qed_roce_modify_qp(struct qed_hwfn *p_hwfn,
struct qed_rdma_qp *qp,
enum qed_roce_qp_state prev_state,
struct qed_rdma_modify_qp_in_params *params);
#endif
@@ -0,0 +1,185 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qed NIC Driver
* Copyright (c) 2015-2016 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#include <linux/crc32.h>
#include "qed.h"
#include "qed_dev_api.h"
#include "qed_mcp.h"
#include "qed_sp.h"
#include "qed_selftest.h"
int qed_selftest_memory(struct qed_dev *cdev)
{
int rc = 0, i;
for_each_hwfn(cdev, i) {
rc = qed_sp_heartbeat_ramrod(&cdev->hwfns[i]);
if (rc)
return rc;
}
return rc;
}
int qed_selftest_interrupt(struct qed_dev *cdev)
{
int rc = 0, i;
for_each_hwfn(cdev, i) {
rc = qed_sp_heartbeat_ramrod(&cdev->hwfns[i]);
if (rc)
return rc;
}
return rc;
}
int qed_selftest_register(struct qed_dev *cdev)
{
struct qed_hwfn *p_hwfn;
struct qed_ptt *p_ptt;
int rc = 0, i;
/* although performed by MCP, this test is per engine */
for_each_hwfn(cdev, i) {
p_hwfn = &cdev->hwfns[i];
p_ptt = qed_ptt_acquire(p_hwfn);
if (!p_ptt) {
DP_ERR(p_hwfn, "failed to acquire ptt\n");
return -EBUSY;
}
rc = qed_mcp_bist_register_test(p_hwfn, p_ptt);
qed_ptt_release(p_hwfn, p_ptt);
if (rc)
break;
}
return rc;
}
int qed_selftest_clock(struct qed_dev *cdev)
{
struct qed_hwfn *p_hwfn;
struct qed_ptt *p_ptt;
int rc = 0, i;
/* although performed by MCP, this test is per engine */
for_each_hwfn(cdev, i) {
p_hwfn = &cdev->hwfns[i];
p_ptt = qed_ptt_acquire(p_hwfn);
if (!p_ptt) {
DP_ERR(p_hwfn, "failed to acquire ptt\n");
return -EBUSY;
}
rc = qed_mcp_bist_clock_test(p_hwfn, p_ptt);
qed_ptt_release(p_hwfn, p_ptt);
if (rc)
break;
}
return rc;
}
int qed_selftest_nvram(struct qed_dev *cdev)
{
struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
u32 num_images, i, j, nvm_crc, calc_crc;
struct bist_nvm_image_att image_att;
u8 *buf = NULL;
__be32 val;
int rc;
if (!p_ptt) {
DP_ERR(p_hwfn, "failed to acquire ptt\n");
return -EBUSY;
}
/* Acquire from MFW the amount of available images */
rc = qed_mcp_bist_nvm_get_num_images(p_hwfn, p_ptt, &num_images);
if (rc || !num_images) {
DP_ERR(p_hwfn, "Failed getting number of images\n");
rc = -EINVAL;
goto err0;
}
/* Iterate over images and validate CRC */
for (i = 0; i < num_images; i++) {
/* This mailbox returns information about the image required for
* reading it.
*/
rc = qed_mcp_bist_nvm_get_image_att(p_hwfn, p_ptt,
&image_att, i);
if (rc) {
DP_ERR(p_hwfn,
"Failed getting image index %d attributes\n",
i);
goto err0;
}
/* After MFW crash dump is collected - the image's CRC stops
* being valid.
*/
if (image_att.image_type == NVM_TYPE_MDUMP)
continue;
DP_VERBOSE(p_hwfn, QED_MSG_SP, "image index %d, size %x\n",
i, image_att.len);
/* Allocate a buffer for holding the nvram image */
buf = kzalloc(image_att.len, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
goto err0;
}
/* Read image into buffer */
rc = qed_mcp_nvm_read(p_hwfn->cdev, image_att.nvm_start_addr,
buf, image_att.len);
if (rc) {
DP_ERR(p_hwfn,
"Failed reading image index %d from nvm.\n", i);
goto err1;
}
/* Convert the buffer into big-endian format (excluding the
* closing 4 bytes of CRC).
*/
for (j = 0; j < image_att.len - 4; j += 4) {
val = cpu_to_be32(*(u32 *)&buf[j]);
*(u32 *)&buf[j] = (__force u32)val;
}
/* Calc CRC for the "actual" image buffer, i.e. not including
* the last 4 CRC bytes.
*/
nvm_crc = *(u32 *)(buf + image_att.len - 4);
calc_crc = crc32(0xffffffff, buf, image_att.len - 4);
calc_crc = (__force u32)~cpu_to_be32(calc_crc);
DP_VERBOSE(p_hwfn, QED_MSG_SP,
"nvm crc 0x%x, calc_crc 0x%x\n", nvm_crc, calc_crc);
if (calc_crc != nvm_crc) {
rc = -EINVAL;
goto err1;
}
/* Done with this image; Free to prevent double release
* on subsequent failure.
*/
kfree(buf);
buf = NULL;
}
qed_ptt_release(p_hwfn, p_ptt);
return 0;
err1:
kfree(buf);
err0:
qed_ptt_release(p_hwfn, p_ptt);
return rc;
}
@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* Copyright (c) 2019-2020 Marvell International Ltd. */
#ifndef _QED_SELFTEST_API_H
#define _QED_SELFTEST_API_H
#include <linux/types.h>
/**
* qed_selftest_memory(): Perform memory test.
*
* @cdev: Qed dev pointer.
*
* Return: Int.
*/
int qed_selftest_memory(struct qed_dev *cdev);
/**
* qed_selftest_interrupt(): Perform interrupt test.
*
* @cdev: Qed dev pointer.
*
* Return: Int.
*/
int qed_selftest_interrupt(struct qed_dev *cdev);
/**
* qed_selftest_register(): Perform register test.
*
* @cdev: Qed dev pointer.
*
* Return: Int.
*/
int qed_selftest_register(struct qed_dev *cdev);
/**
* qed_selftest_clock(): Perform clock test.
*
* @cdev: Qed dev pointer.
*
* Return: Int.
*/
int qed_selftest_clock(struct qed_dev *cdev);
/**
* qed_selftest_nvram(): Perform nvram test.
*
* @cdev: Qed dev pointer.
*
* Return: Int.
*/
int qed_selftest_nvram(struct qed_dev *cdev);
#endif
@@ -0,0 +1,477 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_SP_H
#define _QED_SP_H
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/qed/qed_chain.h>
#include "qed.h"
#include "qed_hsi.h"
enum spq_mode {
QED_SPQ_MODE_BLOCK, /* Client will poll a designated mem. address */
QED_SPQ_MODE_CB, /* Client supplies a callback */
QED_SPQ_MODE_EBLOCK, /* QED should block until completion */
};
struct qed_spq_comp_cb {
void (*function)(struct qed_hwfn *p_hwfn,
void *cookie,
union event_ring_data *data,
u8 fw_return_code);
void *cookie;
};
/**
* qed_eth_cqe_completion(): handles the completion of a
* ramrod on the cqe ring.
*
* @p_hwfn: HW device data.
* @cqe: CQE.
*
* Return: Int.
*/
int qed_eth_cqe_completion(struct qed_hwfn *p_hwfn,
struct eth_slow_path_rx_cqe *cqe);
/* QED Slow-hwfn queue interface */
union ramrod_data {
struct pf_start_ramrod_data pf_start;
struct pf_update_ramrod_data pf_update;
struct rx_queue_start_ramrod_data rx_queue_start;
struct rx_queue_update_ramrod_data rx_queue_update;
struct rx_queue_stop_ramrod_data rx_queue_stop;
struct tx_queue_start_ramrod_data tx_queue_start;
struct tx_queue_stop_ramrod_data tx_queue_stop;
struct vport_start_ramrod_data vport_start;
struct vport_stop_ramrod_data vport_stop;
struct rx_update_gft_filter_ramrod_data rx_update_gft;
struct vport_update_ramrod_data vport_update;
struct core_rx_start_ramrod_data core_rx_queue_start;
struct core_rx_stop_ramrod_data core_rx_queue_stop;
struct core_tx_start_ramrod_data core_tx_queue_start;
struct core_tx_stop_ramrod_data core_tx_queue_stop;
struct vport_filter_update_ramrod_data vport_filter_update;
struct rdma_init_func_ramrod_data rdma_init_func;
struct rdma_close_func_ramrod_data rdma_close_func;
struct rdma_register_tid_ramrod_data rdma_register_tid;
struct rdma_deregister_tid_ramrod_data rdma_deregister_tid;
struct roce_create_qp_resp_ramrod_data roce_create_qp_resp;
struct roce_create_qp_req_ramrod_data roce_create_qp_req;
struct roce_modify_qp_resp_ramrod_data roce_modify_qp_resp;
struct roce_modify_qp_req_ramrod_data roce_modify_qp_req;
struct roce_query_qp_resp_ramrod_data roce_query_qp_resp;
struct roce_query_qp_req_ramrod_data roce_query_qp_req;
struct roce_destroy_qp_resp_ramrod_data roce_destroy_qp_resp;
struct roce_destroy_qp_req_ramrod_data roce_destroy_qp_req;
struct roce_init_func_ramrod_data roce_init_func;
struct rdma_create_cq_ramrod_data rdma_create_cq;
struct rdma_destroy_cq_ramrod_data rdma_destroy_cq;
struct rdma_srq_create_ramrod_data rdma_create_srq;
struct rdma_srq_destroy_ramrod_data rdma_destroy_srq;
struct rdma_srq_modify_ramrod_data rdma_modify_srq;
struct iwarp_create_qp_ramrod_data iwarp_create_qp;
struct iwarp_tcp_offload_ramrod_data iwarp_tcp_offload;
struct iwarp_mpa_offload_ramrod_data iwarp_mpa_offload;
struct iwarp_modify_qp_ramrod_data iwarp_modify_qp;
struct iwarp_init_func_ramrod_data iwarp_init_func;
struct fcoe_init_ramrod_params fcoe_init;
struct fcoe_conn_offload_ramrod_params fcoe_conn_ofld;
struct fcoe_conn_terminate_ramrod_params fcoe_conn_terminate;
struct fcoe_stat_ramrod_params fcoe_stat;
struct iscsi_init_ramrod_params iscsi_init;
struct iscsi_spe_conn_offload iscsi_conn_offload;
struct iscsi_conn_update_ramrod_params iscsi_conn_update;
struct iscsi_spe_conn_mac_update iscsi_conn_mac_update;
struct iscsi_spe_conn_termination iscsi_conn_terminate;
struct nvmetcp_init_ramrod_params nvmetcp_init;
struct nvmetcp_spe_conn_offload nvmetcp_conn_offload;
struct nvmetcp_conn_update_ramrod_params nvmetcp_conn_update;
struct nvmetcp_spe_conn_termination nvmetcp_conn_terminate;
struct vf_start_ramrod_data vf_start;
struct vf_stop_ramrod_data vf_stop;
};
#define EQ_MAX_CREDIT 0xffffffff
enum spq_priority {
QED_SPQ_PRIORITY_NORMAL,
QED_SPQ_PRIORITY_HIGH,
};
union qed_spq_req_comp {
struct qed_spq_comp_cb cb;
u64 *done_addr;
};
struct qed_spq_comp_done {
unsigned int done;
u8 fw_return_code;
};
struct qed_spq_entry {
struct list_head list;
u8 flags;
/* HSI slow path element */
struct slow_path_element elem;
union ramrod_data ramrod;
enum spq_priority priority;
/* pending queue for this entry */
struct list_head *queue;
enum spq_mode comp_mode;
struct qed_spq_comp_cb comp_cb;
struct qed_spq_comp_done comp_done; /* SPQ_MODE_EBLOCK */
/* Posted entry for unlimited list entry in EBLOCK mode */
struct qed_spq_entry *post_ent;
};
struct qed_eq {
struct qed_chain chain;
u8 eq_sb_index; /* index within the SB */
__le16 *p_fw_cons; /* ptr to index value */
};
struct qed_consq {
struct qed_chain chain;
};
typedef int (*qed_spq_async_comp_cb)(struct qed_hwfn *p_hwfn, u8 opcode,
__le16 echo, union event_ring_data *data,
u8 fw_return_code);
int
qed_spq_register_async_cb(struct qed_hwfn *p_hwfn,
enum protocol_type protocol_id,
qed_spq_async_comp_cb cb);
void
qed_spq_unregister_async_cb(struct qed_hwfn *p_hwfn,
enum protocol_type protocol_id);
struct qed_spq {
spinlock_t lock; /* SPQ lock */
struct list_head unlimited_pending;
struct list_head pending;
struct list_head completion_pending;
struct list_head free_pool;
struct qed_chain chain;
/* allocated dma-able memory for spq entries (+ramrod data) */
dma_addr_t p_phys;
struct qed_spq_entry *p_virt;
#define SPQ_RING_SIZE \
(CORE_SPQE_PAGE_SIZE_BYTES / sizeof(struct slow_path_element))
/* Bitmap for handling out-of-order completions */
DECLARE_BITMAP(p_comp_bitmap, SPQ_RING_SIZE);
u8 comp_bitmap_idx;
/* Statistics */
u32 unlimited_pending_count;
u32 normal_count;
u32 high_count;
u32 comp_sent_count;
u32 comp_count;
u32 cid;
u32 db_addr_offset;
struct core_db_data db_data;
qed_spq_async_comp_cb async_comp_cb[MAX_PROTOCOL_TYPE];
};
/**
* qed_spq_post(): Posts a Slow hwfn request to FW, or lacking that
* Pends it to the future list.
*
* @p_hwfn: HW device data.
* @p_ent: Ent.
* @fw_return_code: Return code from firmware.
*
* Return: Int.
*/
int qed_spq_post(struct qed_hwfn *p_hwfn,
struct qed_spq_entry *p_ent,
u8 *fw_return_code);
/**
* qed_spq_alloc(): Alloocates & initializes the SPQ and EQ.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_spq_alloc(struct qed_hwfn *p_hwfn);
/**
* qed_spq_setup(): Reset the SPQ to its start state.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_spq_setup(struct qed_hwfn *p_hwfn);
/**
* qed_spq_free(): Deallocates the given SPQ struct.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_spq_free(struct qed_hwfn *p_hwfn);
/**
* qed_spq_get_entry(): Obtain an entrry from the spq
* free pool list.
*
* @p_hwfn: HW device data.
* @pp_ent: PP ENT.
*
* Return: Int.
*/
int
qed_spq_get_entry(struct qed_hwfn *p_hwfn,
struct qed_spq_entry **pp_ent);
/**
* qed_spq_return_entry(): Return an entry to spq free pool list.
*
* @p_hwfn: HW device data.
* @p_ent: P ENT.
*
* Return: Void.
*/
void qed_spq_return_entry(struct qed_hwfn *p_hwfn,
struct qed_spq_entry *p_ent);
/**
* qed_eq_alloc(): Allocates & initializes an EQ struct.
*
* @p_hwfn: HW device data.
* @num_elem: number of elements in the eq.
*
* Return: Int.
*/
int qed_eq_alloc(struct qed_hwfn *p_hwfn, u16 num_elem);
/**
* qed_eq_setup(): Reset the EQ to its start state.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_eq_setup(struct qed_hwfn *p_hwfn);
/**
* qed_eq_free(): deallocates the given EQ struct.
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_eq_free(struct qed_hwfn *p_hwfn);
/**
* qed_eq_prod_update(): update the FW with default EQ producer.
*
* @p_hwfn: HW device data.
* @prod: Prod.
*
* Return: Void.
*/
void qed_eq_prod_update(struct qed_hwfn *p_hwfn,
u16 prod);
/**
* qed_eq_completion(): Completes currently pending EQ elements.
*
* @p_hwfn: HW device data.
* @cookie: Cookie.
*
* Return: Int.
*/
int qed_eq_completion(struct qed_hwfn *p_hwfn,
void *cookie);
/**
* qed_spq_completion(): Completes a single event.
*
* @p_hwfn: HW device data.
* @echo: echo value from cookie (used for determining completion).
* @fw_return_code: FW return code.
* @p_data: data from cookie (used in callback function if applicable).
*
* Return: Int.
*/
int qed_spq_completion(struct qed_hwfn *p_hwfn,
__le16 echo,
u8 fw_return_code,
union event_ring_data *p_data);
/**
* qed_spq_get_cid(): Given p_hwfn, return cid for the hwfn's SPQ.
*
* @p_hwfn: HW device data.
*
* Return: u32 - SPQ CID.
*/
u32 qed_spq_get_cid(struct qed_hwfn *p_hwfn);
/**
* qed_consq_alloc(): Allocates & initializes an ConsQ struct.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_consq_alloc(struct qed_hwfn *p_hwfn);
/**
* qed_consq_setup(): Reset the ConsQ to its start state.
*
* @p_hwfn: HW device data.
*
* Return Void.
*/
void qed_consq_setup(struct qed_hwfn *p_hwfn);
/**
* qed_consq_free(): deallocates the given ConsQ struct.
*
* @p_hwfn: HW device data.
*
* Return Void.
*/
void qed_consq_free(struct qed_hwfn *p_hwfn);
int qed_spq_pend_post(struct qed_hwfn *p_hwfn);
/* Slow-hwfn low-level commands (Ramrods) function definitions. */
#define QED_SP_EQ_COMPLETION 0x01
#define QED_SP_CQE_COMPLETION 0x02
struct qed_sp_init_data {
u32 cid;
u16 opaque_fid;
/* Information regarding operation upon sending & completion */
enum spq_mode comp_mode;
struct qed_spq_comp_cb *p_comp_data;
};
/**
* qed_sp_destroy_request(): Returns a SPQ entry to the pool / frees the
* entry if allocated. Should be called on in error
* flows after initializing the SPQ entry
* and before posting it.
*
* @p_hwfn: HW device data.
* @p_ent: Ent.
*
* Return: Void.
*/
void qed_sp_destroy_request(struct qed_hwfn *p_hwfn,
struct qed_spq_entry *p_ent);
int qed_sp_init_request(struct qed_hwfn *p_hwfn,
struct qed_spq_entry **pp_ent,
u8 cmd,
u8 protocol,
struct qed_sp_init_data *p_data);
/**
* qed_sp_pf_start(): PF Function Start Ramrod.
*
* @p_hwfn: HW device data.
* @p_ptt: P_ptt.
* @p_tunn: P_tunn.
* @allow_npar_tx_switch: Allow NPAR TX Switch.
*
* Return: Int.
*
* This ramrod is sent to initialize a physical function (PF). It will
* configure the function related parameters and write its completion to the
* event ring specified in the parameters.
*
* Ramrods complete on the common event ring for the PF. This ring is
* allocated by the driver on host memory and its parameters are written
* to the internal RAM of the UStorm by the Function Start Ramrod.
*
*/
int qed_sp_pf_start(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_tunnel_info *p_tunn,
bool allow_npar_tx_switch);
/**
* qed_sp_pf_update(): PF Function Update Ramrod.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*
* This ramrod updates function-related parameters. Every parameter can be
* updated independently, according to configuration flags.
*/
int qed_sp_pf_update(struct qed_hwfn *p_hwfn);
/**
* qed_sp_pf_update_stag(): Update firmware of new outer tag.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_sp_pf_update_stag(struct qed_hwfn *p_hwfn);
/**
* qed_sp_pf_update_ufp(): PF ufp update Ramrod.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_sp_pf_update_ufp(struct qed_hwfn *p_hwfn);
int qed_sp_pf_stop(struct qed_hwfn *p_hwfn);
int qed_sp_pf_update_tunn_cfg(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_tunnel_info *p_tunn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_data);
/**
* qed_sp_heartbeat_ramrod(): Send empty Ramrod.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_sp_heartbeat_ramrod(struct qed_hwfn *p_hwfn);
#endif
@@ -0,0 +1,595 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#include <linux/types.h>
#include <asm/byteorder.h>
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include "qed.h"
#include <linux/qed/qed_chain.h>
#include "qed_cxt.h"
#include "qed_dcbx.h"
#include "qed_hsi.h"
#include "qed_hw.h"
#include "qed_int.h"
#include "qed_reg_addr.h"
#include "qed_sp.h"
#include "qed_sriov.h"
void qed_sp_destroy_request(struct qed_hwfn *p_hwfn,
struct qed_spq_entry *p_ent)
{
/* qed_spq_get_entry() can either get an entry from the free_pool,
* or, if no entries are left, allocate a new entry and add it to
* the unlimited_pending list.
*/
if (p_ent->queue == &p_hwfn->p_spq->unlimited_pending)
kfree(p_ent);
else
qed_spq_return_entry(p_hwfn, p_ent);
}
int qed_sp_init_request(struct qed_hwfn *p_hwfn,
struct qed_spq_entry **pp_ent,
u8 cmd, u8 protocol, struct qed_sp_init_data *p_data)
{
u32 opaque_cid = p_data->opaque_fid << 16 | p_data->cid;
struct qed_spq_entry *p_ent = NULL;
int rc;
if (!pp_ent)
return -ENOMEM;
rc = qed_spq_get_entry(p_hwfn, pp_ent);
if (rc)
return rc;
p_ent = *pp_ent;
p_ent->elem.hdr.cid = cpu_to_le32(opaque_cid);
p_ent->elem.hdr.cmd_id = cmd;
p_ent->elem.hdr.protocol_id = protocol;
p_ent->priority = QED_SPQ_PRIORITY_NORMAL;
p_ent->comp_mode = p_data->comp_mode;
p_ent->comp_done.done = 0;
switch (p_ent->comp_mode) {
case QED_SPQ_MODE_EBLOCK:
p_ent->comp_cb.cookie = &p_ent->comp_done;
break;
case QED_SPQ_MODE_BLOCK:
if (!p_data->p_comp_data)
goto err;
p_ent->comp_cb.cookie = p_data->p_comp_data->cookie;
break;
case QED_SPQ_MODE_CB:
if (!p_data->p_comp_data)
p_ent->comp_cb.function = NULL;
else
p_ent->comp_cb = *p_data->p_comp_data;
break;
default:
DP_NOTICE(p_hwfn, "Unknown SPQE completion mode %d\n",
p_ent->comp_mode);
goto err;
}
DP_VERBOSE(p_hwfn,
QED_MSG_SPQ,
"Initialized: CID %08x %s:[%02x] %s:%02x data_addr %llx comp_mode [%s]\n",
opaque_cid, qed_get_ramrod_cmd_id_str(protocol, cmd),
cmd, qed_get_protocol_type_str(protocol), protocol,
(unsigned long long)(uintptr_t)&p_ent->ramrod,
D_TRINE(p_ent->comp_mode, QED_SPQ_MODE_EBLOCK,
QED_SPQ_MODE_BLOCK, "MODE_EBLOCK", "MODE_BLOCK",
"MODE_CB"));
memset(&p_ent->ramrod, 0, sizeof(p_ent->ramrod));
return 0;
err:
qed_sp_destroy_request(p_hwfn, p_ent);
return -EINVAL;
}
static enum tunnel_clss qed_tunn_clss_to_fw_clss(u8 type)
{
switch (type) {
case QED_TUNN_CLSS_MAC_VLAN:
return TUNNEL_CLSS_MAC_VLAN;
case QED_TUNN_CLSS_MAC_VNI:
return TUNNEL_CLSS_MAC_VNI;
case QED_TUNN_CLSS_INNER_MAC_VLAN:
return TUNNEL_CLSS_INNER_MAC_VLAN;
case QED_TUNN_CLSS_INNER_MAC_VNI:
return TUNNEL_CLSS_INNER_MAC_VNI;
case QED_TUNN_CLSS_MAC_VLAN_DUAL_STAGE:
return TUNNEL_CLSS_MAC_VLAN_DUAL_STAGE;
default:
return TUNNEL_CLSS_MAC_VLAN;
}
}
static void
qed_set_pf_update_tunn_mode(struct qed_tunnel_info *p_tun,
struct qed_tunnel_info *p_src, bool b_pf_start)
{
if (p_src->vxlan.b_update_mode || b_pf_start)
p_tun->vxlan.b_mode_enabled = p_src->vxlan.b_mode_enabled;
if (p_src->l2_gre.b_update_mode || b_pf_start)
p_tun->l2_gre.b_mode_enabled = p_src->l2_gre.b_mode_enabled;
if (p_src->ip_gre.b_update_mode || b_pf_start)
p_tun->ip_gre.b_mode_enabled = p_src->ip_gre.b_mode_enabled;
if (p_src->l2_geneve.b_update_mode || b_pf_start)
p_tun->l2_geneve.b_mode_enabled =
p_src->l2_geneve.b_mode_enabled;
if (p_src->ip_geneve.b_update_mode || b_pf_start)
p_tun->ip_geneve.b_mode_enabled =
p_src->ip_geneve.b_mode_enabled;
}
static void qed_set_tunn_cls_info(struct qed_tunnel_info *p_tun,
struct qed_tunnel_info *p_src)
{
int type;
p_tun->b_update_rx_cls = p_src->b_update_rx_cls;
p_tun->b_update_tx_cls = p_src->b_update_tx_cls;
type = qed_tunn_clss_to_fw_clss(p_src->vxlan.tun_cls);
p_tun->vxlan.tun_cls = type;
type = qed_tunn_clss_to_fw_clss(p_src->l2_gre.tun_cls);
p_tun->l2_gre.tun_cls = type;
type = qed_tunn_clss_to_fw_clss(p_src->ip_gre.tun_cls);
p_tun->ip_gre.tun_cls = type;
type = qed_tunn_clss_to_fw_clss(p_src->l2_geneve.tun_cls);
p_tun->l2_geneve.tun_cls = type;
type = qed_tunn_clss_to_fw_clss(p_src->ip_geneve.tun_cls);
p_tun->ip_geneve.tun_cls = type;
}
static void qed_set_tunn_ports(struct qed_tunnel_info *p_tun,
struct qed_tunnel_info *p_src)
{
p_tun->geneve_port.b_update_port = p_src->geneve_port.b_update_port;
p_tun->vxlan_port.b_update_port = p_src->vxlan_port.b_update_port;
if (p_src->geneve_port.b_update_port)
p_tun->geneve_port.port = p_src->geneve_port.port;
if (p_src->vxlan_port.b_update_port)
p_tun->vxlan_port.port = p_src->vxlan_port.port;
}
static void
__qed_set_ramrod_tunnel_param(u8 *p_tunn_cls,
struct qed_tunn_update_type *tun_type)
{
*p_tunn_cls = tun_type->tun_cls;
}
static void
qed_set_ramrod_tunnel_param(u8 *p_tunn_cls,
struct qed_tunn_update_type *tun_type,
u8 *p_update_port,
__le16 *p_port,
struct qed_tunn_update_udp_port *p_udp_port)
{
__qed_set_ramrod_tunnel_param(p_tunn_cls, tun_type);
if (p_udp_port->b_update_port) {
*p_update_port = 1;
*p_port = cpu_to_le16(p_udp_port->port);
}
}
static void
qed_tunn_set_pf_update_params(struct qed_hwfn *p_hwfn,
struct qed_tunnel_info *p_src,
struct pf_update_tunnel_config *p_tunn_cfg)
{
struct qed_tunnel_info *p_tun = &p_hwfn->cdev->tunnel;
qed_set_pf_update_tunn_mode(p_tun, p_src, false);
qed_set_tunn_cls_info(p_tun, p_src);
qed_set_tunn_ports(p_tun, p_src);
qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_vxlan,
&p_tun->vxlan,
&p_tunn_cfg->set_vxlan_udp_port_flg,
&p_tunn_cfg->vxlan_udp_port,
&p_tun->vxlan_port);
qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_l2geneve,
&p_tun->l2_geneve,
&p_tunn_cfg->set_geneve_udp_port_flg,
&p_tunn_cfg->geneve_udp_port,
&p_tun->geneve_port);
__qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_ipgeneve,
&p_tun->ip_geneve);
__qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_l2gre,
&p_tun->l2_gre);
__qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_ipgre,
&p_tun->ip_gre);
p_tunn_cfg->update_rx_pf_clss = p_tun->b_update_rx_cls;
}
static void qed_set_hw_tunn_mode(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_tunnel_info *p_tun)
{
qed_set_gre_enable(p_hwfn, p_ptt, p_tun->l2_gre.b_mode_enabled,
p_tun->ip_gre.b_mode_enabled);
qed_set_vxlan_enable(p_hwfn, p_ptt, p_tun->vxlan.b_mode_enabled);
qed_set_geneve_enable(p_hwfn, p_ptt, p_tun->l2_geneve.b_mode_enabled,
p_tun->ip_geneve.b_mode_enabled);
}
static void qed_set_hw_tunn_mode_port(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_tunnel_info *p_tunn)
{
if (p_tunn->vxlan_port.b_update_port)
qed_set_vxlan_dest_port(p_hwfn, p_ptt,
p_tunn->vxlan_port.port);
if (p_tunn->geneve_port.b_update_port)
qed_set_geneve_dest_port(p_hwfn, p_ptt,
p_tunn->geneve_port.port);
qed_set_hw_tunn_mode(p_hwfn, p_ptt, p_tunn);
}
static void
qed_tunn_set_pf_start_params(struct qed_hwfn *p_hwfn,
struct qed_tunnel_info *p_src,
struct pf_start_tunnel_config *p_tunn_cfg)
{
struct qed_tunnel_info *p_tun = &p_hwfn->cdev->tunnel;
if (!p_src)
return;
qed_set_pf_update_tunn_mode(p_tun, p_src, true);
qed_set_tunn_cls_info(p_tun, p_src);
qed_set_tunn_ports(p_tun, p_src);
qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_vxlan,
&p_tun->vxlan,
&p_tunn_cfg->set_vxlan_udp_port_flg,
&p_tunn_cfg->vxlan_udp_port,
&p_tun->vxlan_port);
qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_l2geneve,
&p_tun->l2_geneve,
&p_tunn_cfg->set_geneve_udp_port_flg,
&p_tunn_cfg->geneve_udp_port,
&p_tun->geneve_port);
__qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_ipgeneve,
&p_tun->ip_geneve);
__qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_l2gre,
&p_tun->l2_gre);
__qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_ipgre,
&p_tun->ip_gre);
}
int qed_sp_pf_start(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_tunnel_info *p_tunn,
bool allow_npar_tx_switch)
{
struct outer_tag_config_struct *outer_tag_config;
struct pf_start_ramrod_data *p_ramrod = NULL;
u16 sb = qed_int_get_sp_sb_id(p_hwfn);
u8 sb_index = p_hwfn->p_eq->eq_sb_index;
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
u8 page_cnt, i;
int rc;
/* update initial eq producer */
qed_eq_prod_update(p_hwfn,
qed_chain_get_prod_idx(&p_hwfn->p_eq->chain));
memset(&init_data, 0, sizeof(init_data));
init_data.cid = qed_spq_get_cid(p_hwfn);
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
rc = qed_sp_init_request(p_hwfn, &p_ent,
COMMON_RAMROD_PF_START,
PROTOCOLID_COMMON, &init_data);
if (rc)
return rc;
p_ramrod = &p_ent->ramrod.pf_start;
p_ramrod->event_ring_sb_id = cpu_to_le16(sb);
p_ramrod->event_ring_sb_index = sb_index;
p_ramrod->path_id = QED_PATH_ID(p_hwfn);
p_ramrod->dont_log_ramrods = 0;
p_ramrod->log_type_mask = cpu_to_le16(0xf);
if (test_bit(QED_MF_OVLAN_CLSS, &p_hwfn->cdev->mf_bits))
p_ramrod->mf_mode = MF_OVLAN;
else
p_ramrod->mf_mode = MF_NPAR;
outer_tag_config = &p_ramrod->outer_tag_config;
outer_tag_config->outer_tag.tci = cpu_to_le16(p_hwfn->hw_info.ovlan);
if (test_bit(QED_MF_8021Q_TAGGING, &p_hwfn->cdev->mf_bits)) {
outer_tag_config->outer_tag.tpid = cpu_to_le16(ETH_P_8021Q);
} else if (test_bit(QED_MF_8021AD_TAGGING, &p_hwfn->cdev->mf_bits)) {
outer_tag_config->outer_tag.tpid = cpu_to_le16(ETH_P_8021AD);
outer_tag_config->enable_stag_pri_change = 1;
}
outer_tag_config->pri_map_valid = 1;
for (i = 0; i < QED_MAX_PFC_PRIORITIES; i++)
outer_tag_config->inner_to_outer_pri_map[i] = i;
/* enable_stag_pri_change should be set if port is in BD mode or,
* UFP with Host Control mode.
*/
if (test_bit(QED_MF_UFP_SPECIFIC, &p_hwfn->cdev->mf_bits)) {
if (p_hwfn->ufp_info.pri_type == QED_UFP_PRI_OS)
outer_tag_config->enable_stag_pri_change = 1;
else
outer_tag_config->enable_stag_pri_change = 0;
outer_tag_config->outer_tag.tci |=
cpu_to_le16(((u16)p_hwfn->ufp_info.tc << 13));
}
/* Place EQ address in RAMROD */
DMA_REGPAIR_LE(p_ramrod->event_ring_pbl_addr,
qed_chain_get_pbl_phys(&p_hwfn->p_eq->chain));
page_cnt = (u8)qed_chain_get_page_cnt(&p_hwfn->p_eq->chain);
p_ramrod->event_ring_num_pages = page_cnt;
/* Place consolidation queue address in ramrod */
DMA_REGPAIR_LE(p_ramrod->consolid_q_pbl_base_addr,
qed_chain_get_pbl_phys(&p_hwfn->p_consq->chain));
page_cnt = (u8)qed_chain_get_page_cnt(&p_hwfn->p_consq->chain);
p_ramrod->consolid_q_num_pages = page_cnt;
qed_tunn_set_pf_start_params(p_hwfn, p_tunn, &p_ramrod->tunnel_config);
if (test_bit(QED_MF_INTER_PF_SWITCH, &p_hwfn->cdev->mf_bits))
p_ramrod->allow_npar_tx_switching = allow_npar_tx_switch;
switch (p_hwfn->hw_info.personality) {
case QED_PCI_ETH:
p_ramrod->personality = PERSONALITY_ETH;
break;
case QED_PCI_FCOE:
p_ramrod->personality = PERSONALITY_FCOE;
break;
case QED_PCI_ISCSI:
case QED_PCI_NVMETCP:
p_ramrod->personality = PERSONALITY_TCP_ULP;
break;
case QED_PCI_ETH_ROCE:
case QED_PCI_ETH_IWARP:
p_ramrod->personality = PERSONALITY_RDMA_AND_ETH;
break;
default:
DP_NOTICE(p_hwfn, "Unknown personality %d\n",
p_hwfn->hw_info.personality);
p_ramrod->personality = PERSONALITY_ETH;
}
if (p_hwfn->cdev->p_iov_info) {
struct qed_hw_sriov_info *p_iov = p_hwfn->cdev->p_iov_info;
p_ramrod->base_vf_id = (u8)p_iov->first_vf_in_pf;
p_ramrod->num_vfs = (u8)p_iov->total_vfs;
}
p_ramrod->hsi_fp_ver.major_ver_arr[ETH_VER_KEY] = ETH_HSI_VER_MAJOR;
p_ramrod->hsi_fp_ver.minor_ver_arr[ETH_VER_KEY] = ETH_HSI_VER_MINOR;
DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
"Setting event_ring_sb [id %04x index %02x], outer_tag.tci [%d]\n",
sb, sb_index, outer_tag_config->outer_tag.tci);
rc = qed_spq_post(p_hwfn, p_ent, NULL);
if (p_tunn)
qed_set_hw_tunn_mode_port(p_hwfn, p_ptt,
&p_hwfn->cdev->tunnel);
return rc;
}
int qed_sp_pf_update(struct qed_hwfn *p_hwfn)
{
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = qed_spq_get_cid(p_hwfn);
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = QED_SPQ_MODE_CB;
rc = qed_sp_init_request(p_hwfn, &p_ent,
COMMON_RAMROD_PF_UPDATE, PROTOCOLID_COMMON,
&init_data);
if (rc)
return rc;
qed_dcbx_set_pf_update_params(&p_hwfn->p_dcbx_info->results,
&p_ent->ramrod.pf_update);
return qed_spq_post(p_hwfn, p_ent, NULL);
}
int qed_sp_pf_update_ufp(struct qed_hwfn *p_hwfn)
{
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc;
if (p_hwfn->ufp_info.pri_type == QED_UFP_PRI_UNKNOWN) {
DP_INFO(p_hwfn, "Invalid priority type %d\n",
p_hwfn->ufp_info.pri_type);
return -EINVAL;
}
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = qed_spq_get_cid(p_hwfn);
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = QED_SPQ_MODE_CB;
rc = qed_sp_init_request(p_hwfn, &p_ent,
COMMON_RAMROD_PF_UPDATE, PROTOCOLID_COMMON,
&init_data);
if (rc)
return rc;
p_ent->ramrod.pf_update.update_enable_stag_pri_change = true;
if (p_hwfn->ufp_info.pri_type == QED_UFP_PRI_OS)
p_ent->ramrod.pf_update.enable_stag_pri_change = 1;
else
p_ent->ramrod.pf_update.enable_stag_pri_change = 0;
return qed_spq_post(p_hwfn, p_ent, NULL);
}
/* Set pf update ramrod command params */
int qed_sp_pf_update_tunn_cfg(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_tunnel_info *p_tunn,
enum spq_mode comp_mode,
struct qed_spq_comp_cb *p_comp_data)
{
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc;
if (IS_VF(p_hwfn->cdev))
return qed_vf_pf_tunnel_param_update(p_hwfn, p_tunn);
if (!p_tunn)
return -EINVAL;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = qed_spq_get_cid(p_hwfn);
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = comp_mode;
init_data.p_comp_data = p_comp_data;
rc = qed_sp_init_request(p_hwfn, &p_ent,
COMMON_RAMROD_PF_UPDATE, PROTOCOLID_COMMON,
&init_data);
if (rc)
return rc;
qed_tunn_set_pf_update_params(p_hwfn, p_tunn,
&p_ent->ramrod.pf_update.tunnel_config);
rc = qed_spq_post(p_hwfn, p_ent, NULL);
if (rc)
return rc;
qed_set_hw_tunn_mode_port(p_hwfn, p_ptt, &p_hwfn->cdev->tunnel);
return rc;
}
int qed_sp_pf_stop(struct qed_hwfn *p_hwfn)
{
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = qed_spq_get_cid(p_hwfn);
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
rc = qed_sp_init_request(p_hwfn, &p_ent,
COMMON_RAMROD_PF_STOP, PROTOCOLID_COMMON,
&init_data);
if (rc)
return rc;
return qed_spq_post(p_hwfn, p_ent, NULL);
}
int qed_sp_heartbeat_ramrod(struct qed_hwfn *p_hwfn)
{
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = qed_spq_get_cid(p_hwfn);
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
rc = qed_sp_init_request(p_hwfn, &p_ent,
COMMON_RAMROD_EMPTY, PROTOCOLID_COMMON,
&init_data);
if (rc)
return rc;
return qed_spq_post(p_hwfn, p_ent, NULL);
}
int qed_sp_pf_update_stag(struct qed_hwfn *p_hwfn)
{
struct qed_spq_entry *p_ent = NULL;
struct qed_sp_init_data init_data;
int rc;
/* Get SPQ entry */
memset(&init_data, 0, sizeof(init_data));
init_data.cid = qed_spq_get_cid(p_hwfn);
init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
init_data.comp_mode = QED_SPQ_MODE_CB;
rc = qed_sp_init_request(p_hwfn, &p_ent,
COMMON_RAMROD_PF_UPDATE, PROTOCOLID_COMMON,
&init_data);
if (rc)
return rc;
p_ent->ramrod.pf_update.update_mf_vlan_flag = true;
p_ent->ramrod.pf_update.mf_vlan = cpu_to_le16(p_hwfn->hw_info.ovlan);
if (test_bit(QED_MF_UFP_SPECIFIC, &p_hwfn->cdev->mf_bits))
p_ent->ramrod.pf_update.mf_vlan |=
cpu_to_le16(((u16)p_hwfn->ufp_info.tc << 13));
return qed_spq_post(p_hwfn, p_ent, NULL);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,501 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
/* QLogic qed NIC Driver
* Copyright (c) 2015-2017 QLogic Corporation
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
#ifndef _QED_SRIOV_H
#define _QED_SRIOV_H
#include <linux/types.h>
#include "qed_vf.h"
#define QED_ETH_VF_NUM_MAC_FILTERS 1
#define QED_ETH_VF_NUM_VLAN_FILTERS 2
#define QED_VF_ARRAY_LENGTH (3)
#ifdef CONFIG_QED_SRIOV
#define IS_VF(cdev) ((cdev)->b_is_vf)
#define IS_PF(cdev) (!((cdev)->b_is_vf))
#define IS_PF_SRIOV(p_hwfn) (!!((p_hwfn)->cdev->p_iov_info))
#else
#define IS_VF(cdev) (0)
#define IS_PF(cdev) (1)
#define IS_PF_SRIOV(p_hwfn) (0)
#endif
#define IS_PF_SRIOV_ALLOC(p_hwfn) (!!((p_hwfn)->pf_iov_info))
#define QED_MAX_VF_CHAINS_PER_PF 16
#define QED_ETH_MAX_VF_NUM_VLAN_FILTERS \
(MAX_NUM_VFS * QED_ETH_VF_NUM_VLAN_FILTERS)
enum qed_iov_vport_update_flag {
QED_IOV_VP_UPDATE_ACTIVATE,
QED_IOV_VP_UPDATE_VLAN_STRIP,
QED_IOV_VP_UPDATE_TX_SWITCH,
QED_IOV_VP_UPDATE_MCAST,
QED_IOV_VP_UPDATE_ACCEPT_PARAM,
QED_IOV_VP_UPDATE_RSS,
QED_IOV_VP_UPDATE_ACCEPT_ANY_VLAN,
QED_IOV_VP_UPDATE_SGE_TPA,
QED_IOV_VP_UPDATE_MAX,
};
struct qed_public_vf_info {
/* These copies will later be reflected in the bulletin board,
* but this copy should be newer.
*/
u8 forced_mac[ETH_ALEN];
u16 forced_vlan;
u8 mac[ETH_ALEN];
/* IFLA_VF_LINK_STATE_<X> */
int link_state;
/* Currently configured Tx rate in MB/sec. 0 if unconfigured */
int tx_rate;
/* Trusted VFs can configure promiscuous mode.
* Also store shadow promisc configuration if needed.
*/
bool is_trusted_configured;
bool is_trusted_request;
u8 rx_accept_mode;
u8 tx_accept_mode;
bool accept_any_vlan;
};
struct qed_iov_vf_init_params {
u16 rel_vf_id;
/* Number of requested Queues; Currently, don't support different
* number of Rx/Tx queues.
*/
u16 num_queues;
/* Allow the client to choose which qzones to use for Rx/Tx,
* and which queue_base to use for Tx queues on a per-queue basis.
* Notice values should be relative to the PF resources.
*/
u16 req_rx_queue[QED_MAX_VF_CHAINS_PER_PF];
u16 req_tx_queue[QED_MAX_VF_CHAINS_PER_PF];
};
/* This struct is part of qed_dev and contains data relevant to all hwfns;
* Initialized only if SR-IOV cpabability is exposed in PCIe config space.
*/
struct qed_hw_sriov_info {
int pos; /* capability position */
int nres; /* number of resources */
u32 cap; /* SR-IOV Capabilities */
u16 ctrl; /* SR-IOV Control */
u16 total_vfs; /* total VFs associated with the PF */
u16 num_vfs; /* number of vfs that have been started */
u16 initial_vfs; /* initial VFs associated with the PF */
u16 nr_virtfn; /* number of VFs available */
u16 offset; /* first VF Routing ID offset */
u16 stride; /* following VF stride */
u16 vf_device_id; /* VF device id */
u32 pgsz; /* page size for BAR alignment */
u8 link; /* Function Dependency Link */
u32 first_vf_in_pf;
};
/* This mailbox is maintained per VF in its PF contains all information
* required for sending / receiving a message.
*/
struct qed_iov_vf_mbx {
union vfpf_tlvs *req_virt;
dma_addr_t req_phys;
union pfvf_tlvs *reply_virt;
dma_addr_t reply_phys;
/* Address in VF where a pending message is located */
dma_addr_t pending_req;
/* Message from VF awaits handling */
bool b_pending_msg;
u8 *offset;
/* saved VF request header */
struct vfpf_first_tlv first_tlv;
};
#define QED_IOV_LEGACY_QID_RX (0)
#define QED_IOV_LEGACY_QID_TX (1)
#define QED_IOV_QID_INVALID (0xFE)
struct qed_vf_queue_cid {
bool b_is_tx;
struct qed_queue_cid *p_cid;
};
/* Describes a qzone associated with the VF */
struct qed_vf_queue {
u16 fw_rx_qid;
u16 fw_tx_qid;
struct qed_vf_queue_cid cids[MAX_QUEUES_PER_QZONE];
};
enum vf_state {
VF_FREE = 0, /* VF ready to be acquired holds no resc */
VF_ACQUIRED, /* VF, acquired, but not initialized */
VF_ENABLED, /* VF, Enabled */
VF_RESET, /* VF, FLR'd, pending cleanup */
VF_STOPPED /* VF, Stopped */
};
struct qed_vf_vlan_shadow {
bool used;
u16 vid;
};
struct qed_vf_shadow_config {
/* Shadow copy of all guest vlans */
struct qed_vf_vlan_shadow vlans[QED_ETH_VF_NUM_VLAN_FILTERS + 1];
/* Shadow copy of all configured MACs; Empty if forcing MACs */
u8 macs[QED_ETH_VF_NUM_MAC_FILTERS][ETH_ALEN];
u8 inner_vlan_removal;
};
/* PFs maintain an array of this structure, per VF */
struct qed_vf_info {
struct qed_iov_vf_mbx vf_mbx;
enum vf_state state;
bool b_init;
bool b_malicious;
u8 to_disable;
struct qed_bulletin bulletin;
dma_addr_t vf_bulletin;
/* PF saves a copy of the last VF acquire message */
struct vfpf_acquire_tlv acquire;
u32 concrete_fid;
u16 opaque_fid;
u16 mtu;
u8 vport_id;
u8 relative_vf_id;
u8 abs_vf_id;
#define QED_VF_ABS_ID(p_hwfn, p_vf) (QED_PATH_ID(p_hwfn) ? \
(p_vf)->abs_vf_id + MAX_NUM_VFS_BB : \
(p_vf)->abs_vf_id)
u8 vport_instance;
u8 num_rxqs;
u8 num_txqs;
u16 rx_coal;
u16 tx_coal;
u8 num_sbs;
u8 num_mac_filters;
u8 num_vlan_filters;
struct qed_vf_queue vf_queues[QED_MAX_VF_CHAINS_PER_PF];
u16 igu_sbs[QED_MAX_VF_CHAINS_PER_PF];
u8 num_active_rxqs;
struct qed_public_vf_info p_vf_info;
bool spoof_chk;
bool req_spoofchk_val;
/* Stores the configuration requested by VF */
struct qed_vf_shadow_config shadow_config;
/* A bitfield using bulletin's valid-map bits, used to indicate
* which of the bulletin board features have been configured.
*/
u64 configured_features;
#define QED_IOV_CONFIGURED_FEATURES_MASK ((1 << MAC_ADDR_FORCED) | \
(1 << VLAN_ADDR_FORCED))
};
/* This structure is part of qed_hwfn and used only for PFs that have sriov
* capability enabled.
*/
struct qed_pf_iov {
struct qed_vf_info vfs_array[MAX_NUM_VFS];
u64 pending_flr[QED_VF_ARRAY_LENGTH];
/* Allocate message address continuosuly and split to each VF */
void *mbx_msg_virt_addr;
dma_addr_t mbx_msg_phys_addr;
u32 mbx_msg_size;
void *mbx_reply_virt_addr;
dma_addr_t mbx_reply_phys_addr;
u32 mbx_reply_size;
void *p_bulletins;
dma_addr_t bulletins_phys;
u32 bulletins_size;
};
enum qed_iov_wq_flag {
QED_IOV_WQ_MSG_FLAG,
QED_IOV_WQ_SET_UNICAST_FILTER_FLAG,
QED_IOV_WQ_BULLETIN_UPDATE_FLAG,
QED_IOV_WQ_STOP_WQ_FLAG,
QED_IOV_WQ_FLR_FLAG,
QED_IOV_WQ_TRUST_FLAG,
QED_IOV_WQ_VF_FORCE_LINK_QUERY_FLAG,
};
extern const struct qed_iov_hv_ops qed_iov_ops_pass;
#ifdef CONFIG_QED_SRIOV
/**
* qed_iov_is_valid_vfid(): Check if given VF ID @vfid is valid
* w.r.t. @b_enabled_only value
* if b_enabled_only = true - only enabled
* VF id is valid.
* else any VF id less than max_vfs is valid.
*
* @p_hwfn: HW device data.
* @rel_vf_id: Relative VF ID.
* @b_enabled_only: consider only enabled VF.
* @b_non_malicious: true iff we want to validate vf isn't malicious.
*
* Return: bool - true for valid VF ID
*/
bool qed_iov_is_valid_vfid(struct qed_hwfn *p_hwfn,
int rel_vf_id,
bool b_enabled_only, bool b_non_malicious);
/**
* qed_iov_get_next_active_vf(): Given a VF index, return index of
* next [including that] active VF.
*
* @p_hwfn: HW device data.
* @rel_vf_id: VF ID.
*
* Return: MAX_NUM_VFS in case no further active VFs, otherwise index.
*/
u16 qed_iov_get_next_active_vf(struct qed_hwfn *p_hwfn, u16 rel_vf_id);
void qed_iov_bulletin_set_udp_ports(struct qed_hwfn *p_hwfn,
int vfid, u16 vxlan_port, u16 geneve_port);
/**
* qed_iov_hw_info(): Read sriov related information and allocated resources
* reads from configuration space, shmem, etc.
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_iov_hw_info(struct qed_hwfn *p_hwfn);
/**
* qed_add_tlv(): place a given tlv on the tlv buffer at next offset
*
* @p_hwfn: HW device data.
* @offset: offset.
* @type: Type
* @length: Length.
*
* Return: pointer to the newly placed tlv
*/
void *qed_add_tlv(struct qed_hwfn *p_hwfn, u8 **offset, u16 type, u16 length);
/**
* qed_dp_tlv_list(): list the types and lengths of the tlvs on the buffer
*
* @p_hwfn: HW device data.
* @tlvs_list: Tlvs_list.
*
* Return: Void.
*/
void qed_dp_tlv_list(struct qed_hwfn *p_hwfn, void *tlvs_list);
/**
* qed_sriov_vfpf_malicious(): Handle malicious VF/PF.
*
* @p_hwfn: HW device data.
* @p_data: Pointer to data.
*
* Return: Void.
*/
void qed_sriov_vfpf_malicious(struct qed_hwfn *p_hwfn,
struct fw_err_data *p_data);
/**
* qed_sriov_eqe_event(): Callback for SRIOV events.
*
* @p_hwfn: HW device data.
* @opcode: Opcode.
* @echo: Echo.
* @data: data
* @fw_return_code: FW return code.
*
* Return: Int.
*/
int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, u8 opcode, __le16 echo,
union event_ring_data *data, u8 fw_return_code);
/**
* qed_iov_alloc(): allocate sriov related resources
*
* @p_hwfn: HW device data.
*
* Return: Int.
*/
int qed_iov_alloc(struct qed_hwfn *p_hwfn);
/**
* qed_iov_setup(): setup sriov related resources
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_iov_setup(struct qed_hwfn *p_hwfn);
/**
* qed_iov_free(): free sriov related resources
*
* @p_hwfn: HW device data.
*
* Return: Void.
*/
void qed_iov_free(struct qed_hwfn *p_hwfn);
/**
* qed_iov_free_hw_info(): free sriov related memory that was
* allocated during hw_prepare
*
* @cdev: Qed dev pointer.
*
* Return: Void.
*/
void qed_iov_free_hw_info(struct qed_dev *cdev);
/**
* qed_iov_mark_vf_flr(): Mark structs of vfs that have been FLR-ed.
*
* @p_hwfn: HW device data.
* @disabled_vfs: bitmask of all VFs on path that were FLRed
*
* Return: true iff one of the PF's vfs got FLRed. false otherwise.
*/
bool qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, u32 *disabled_vfs);
/**
* qed_iov_search_list_tlvs(): Search extended TLVs in request/reply buffer.
*
* @p_hwfn: HW device data.
* @p_tlvs_list: Pointer to tlvs list
* @req_type: Type of TLV
*
* Return: pointer to tlv type if found, otherwise returns NULL.
*/
void *qed_iov_search_list_tlvs(struct qed_hwfn *p_hwfn,
void *p_tlvs_list, u16 req_type);
void qed_iov_wq_stop(struct qed_dev *cdev, bool schedule_first);
int qed_iov_wq_start(struct qed_dev *cdev);
void qed_schedule_iov(struct qed_hwfn *hwfn, enum qed_iov_wq_flag flag);
void qed_vf_start_iov_wq(struct qed_dev *cdev);
int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled);
void qed_inform_vf_link_state(struct qed_hwfn *hwfn);
#else
static inline bool
qed_iov_is_valid_vfid(struct qed_hwfn *p_hwfn,
int rel_vf_id, bool b_enabled_only, bool b_non_malicious)
{
return false;
}
static inline u16 qed_iov_get_next_active_vf(struct qed_hwfn *p_hwfn,
u16 rel_vf_id)
{
return MAX_NUM_VFS;
}
static inline void
qed_iov_bulletin_set_udp_ports(struct qed_hwfn *p_hwfn, int vfid,
u16 vxlan_port, u16 geneve_port)
{
}
static inline int qed_iov_hw_info(struct qed_hwfn *p_hwfn)
{
return 0;
}
static inline int qed_iov_alloc(struct qed_hwfn *p_hwfn)
{
return 0;
}
static inline void qed_iov_setup(struct qed_hwfn *p_hwfn)
{
}
static inline void qed_iov_free(struct qed_hwfn *p_hwfn)
{
}
static inline void qed_iov_free_hw_info(struct qed_dev *cdev)
{
}
static inline bool qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn,
u32 *disabled_vfs)
{
return false;
}
static inline void qed_iov_wq_stop(struct qed_dev *cdev, bool schedule_first)
{
}
static inline int qed_iov_wq_start(struct qed_dev *cdev)
{
return 0;
}
static inline void qed_schedule_iov(struct qed_hwfn *hwfn,
enum qed_iov_wq_flag flag)
{
}
static inline void qed_vf_start_iov_wq(struct qed_dev *cdev)
{
}
static inline int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled)
{
return 0;
}
static inline void qed_inform_vf_link_state(struct qed_hwfn *hwfn)
{
}
static inline void qed_sriov_vfpf_malicious(struct qed_hwfn *p_hwfn,
struct fw_err_data *p_data)
{
}
static inline int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, u8 opcode,
__le16 echo, union event_ring_data *data,
u8 fw_return_code)
{
return 0;
}
#endif
#define qed_for_each_vf(_p_hwfn, _i) \
for (_i = qed_iov_get_next_active_vf(_p_hwfn, 0); \
_i < MAX_NUM_VFS; \
_i = qed_iov_get_next_active_vf(_p_hwfn, _i + 1))
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff