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,15 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2018-2025, Advanced Micro Devices, Inc.
config INFINIBAND_IONIC
tristate "AMD Pensando DSC RDMA/RoCE Support"
depends on NETDEVICES && ETHERNET && PCI && INET && IONIC
help
This enables RDMA/RoCE support for the AMD Pensando family of
Distributed Services Cards (DSCs).
To learn more, visit our website at
<https://www.amd.com/en/products/accelerators/pensando.html>.
To compile this driver as a module, choose M here. The module
will be called ionic_rdma.
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
ccflags-y := -I $(srctree)/drivers/net/ethernet/pensando/ionic
obj-$(CONFIG_INFINIBAND_IONIC) += ionic_rdma.o
ionic_rdma-y := \
ionic_ibdev.o ionic_lif_cfg.o ionic_queue.o ionic_pgtbl.o ionic_admin.o \
ionic_controlpath.o ionic_datapath.o ionic_hw_stats.o
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,480 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#include <linux/dma-mapping.h>
#include "ionic_fw.h"
#include "ionic_ibdev.h"
static int ionic_v1_stat_normalize(struct ionic_v1_stat *hw_stats,
int hw_stats_count)
{
int hw_stat_i;
for (hw_stat_i = 0; hw_stat_i < hw_stats_count; ++hw_stat_i) {
struct ionic_v1_stat *stat = &hw_stats[hw_stat_i];
stat->type_off = be32_to_cpu(stat->be_type_off);
stat->name[sizeof(stat->name) - 1] = 0;
if (ionic_v1_stat_type(stat) == IONIC_V1_STAT_TYPE_NONE)
break;
}
return hw_stat_i;
}
static void ionic_fill_stats_desc(struct rdma_stat_desc *hw_stats_hdrs,
struct ionic_v1_stat *hw_stats,
int hw_stats_count)
{
int hw_stat_i;
for (hw_stat_i = 0; hw_stat_i < hw_stats_count; ++hw_stat_i) {
struct ionic_v1_stat *stat = &hw_stats[hw_stat_i];
hw_stats_hdrs[hw_stat_i].name = stat->name;
}
}
static u64 ionic_v1_stat_val(struct ionic_v1_stat *stat,
void *vals_buf, size_t vals_len)
{
unsigned int off = ionic_v1_stat_off(stat);
int type = ionic_v1_stat_type(stat);
#define __ionic_v1_stat_validate(__type) \
((off + sizeof(__type) <= vals_len) && \
(IS_ALIGNED(off, sizeof(__type))))
switch (type) {
case IONIC_V1_STAT_TYPE_8:
if (__ionic_v1_stat_validate(u8))
return *(u8 *)(vals_buf + off);
break;
case IONIC_V1_STAT_TYPE_LE16:
if (__ionic_v1_stat_validate(__le16))
return le16_to_cpu(*(__le16 *)(vals_buf + off));
break;
case IONIC_V1_STAT_TYPE_LE32:
if (__ionic_v1_stat_validate(__le32))
return le32_to_cpu(*(__le32 *)(vals_buf + off));
break;
case IONIC_V1_STAT_TYPE_LE64:
if (__ionic_v1_stat_validate(__le64))
return le64_to_cpu(*(__le64 *)(vals_buf + off));
break;
case IONIC_V1_STAT_TYPE_BE16:
if (__ionic_v1_stat_validate(__be16))
return be16_to_cpu(*(__be16 *)(vals_buf + off));
break;
case IONIC_V1_STAT_TYPE_BE32:
if (__ionic_v1_stat_validate(__be32))
return be32_to_cpu(*(__be32 *)(vals_buf + off));
break;
case IONIC_V1_STAT_TYPE_BE64:
if (__ionic_v1_stat_validate(__be64))
return be64_to_cpu(*(__be64 *)(vals_buf + off));
break;
}
return ~0ull;
#undef __ionic_v1_stat_validate
}
static int ionic_hw_stats_cmd(struct ionic_ibdev *dev,
dma_addr_t dma, size_t len, int qid, int op)
{
struct ionic_admin_wr wr = {
.work = COMPLETION_INITIALIZER_ONSTACK(wr.work),
.wqe = {
.op = op,
.len = cpu_to_le16(IONIC_ADMIN_STATS_HDRS_IN_V1_LEN),
.cmd.stats = {
.dma_addr = cpu_to_le64(dma),
.length = cpu_to_le32(len),
.id_ver = cpu_to_le32(qid),
},
}
};
if (dev->lif_cfg.admin_opcodes <= op)
return -EBADRQC;
ionic_admin_post(dev, &wr);
return ionic_admin_wait(dev, &wr, IONIC_ADMIN_F_INTERRUPT);
}
static int ionic_init_hw_stats(struct ionic_ibdev *dev)
{
dma_addr_t hw_stats_dma;
int rc, hw_stats_count;
if (dev->hw_stats_hdrs)
return 0;
dev->hw_stats_count = 0;
/* buffer for current values from the device */
dev->hw_stats_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!dev->hw_stats_buf) {
rc = -ENOMEM;
goto err_buf;
}
/* buffer for names, sizes, offsets of values */
dev->hw_stats = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!dev->hw_stats) {
rc = -ENOMEM;
goto err_hw_stats;
}
/* request the names, sizes, offsets */
hw_stats_dma = dma_map_single(dev->lif_cfg.hwdev, dev->hw_stats,
PAGE_SIZE, DMA_FROM_DEVICE);
rc = dma_mapping_error(dev->lif_cfg.hwdev, hw_stats_dma);
if (rc)
goto err_dma;
rc = ionic_hw_stats_cmd(dev, hw_stats_dma, PAGE_SIZE, 0,
IONIC_V1_ADMIN_STATS_HDRS);
if (rc)
goto err_cmd;
dma_unmap_single(dev->lif_cfg.hwdev, hw_stats_dma, PAGE_SIZE, DMA_FROM_DEVICE);
/* normalize and count the number of hw_stats */
hw_stats_count =
ionic_v1_stat_normalize(dev->hw_stats,
PAGE_SIZE / sizeof(*dev->hw_stats));
if (!hw_stats_count) {
rc = -ENODATA;
goto err_dma;
}
dev->hw_stats_count = hw_stats_count;
/* alloc and init array of names, for alloc_hw_stats */
dev->hw_stats_hdrs = kzalloc_objs(*dev->hw_stats_hdrs, hw_stats_count);
if (!dev->hw_stats_hdrs) {
rc = -ENOMEM;
goto err_dma;
}
ionic_fill_stats_desc(dev->hw_stats_hdrs, dev->hw_stats,
hw_stats_count);
return 0;
err_cmd:
dma_unmap_single(dev->lif_cfg.hwdev, hw_stats_dma, PAGE_SIZE, DMA_FROM_DEVICE);
err_dma:
kfree(dev->hw_stats);
err_hw_stats:
kfree(dev->hw_stats_buf);
err_buf:
dev->hw_stats_count = 0;
dev->hw_stats = NULL;
dev->hw_stats_buf = NULL;
dev->hw_stats_hdrs = NULL;
return rc;
}
static struct rdma_hw_stats *ionic_alloc_hw_stats(struct ib_device *ibdev,
u32 port)
{
struct ionic_ibdev *dev = to_ionic_ibdev(ibdev);
if (port != 1)
return NULL;
return rdma_alloc_hw_stats_struct(dev->hw_stats_hdrs,
dev->hw_stats_count,
RDMA_HW_STATS_DEFAULT_LIFESPAN);
}
static int ionic_get_hw_stats(struct ib_device *ibdev,
struct rdma_hw_stats *hw_stats,
u32 port, int index)
{
struct ionic_ibdev *dev = to_ionic_ibdev(ibdev);
dma_addr_t hw_stats_dma;
int rc, hw_stat_i;
if (port != 1)
return -EINVAL;
hw_stats_dma = dma_map_single(dev->lif_cfg.hwdev, dev->hw_stats_buf,
PAGE_SIZE, DMA_FROM_DEVICE);
rc = dma_mapping_error(dev->lif_cfg.hwdev, hw_stats_dma);
if (rc)
goto err_dma;
rc = ionic_hw_stats_cmd(dev, hw_stats_dma, PAGE_SIZE,
0, IONIC_V1_ADMIN_STATS_VALS);
if (rc)
goto err_cmd;
dma_unmap_single(dev->lif_cfg.hwdev, hw_stats_dma,
PAGE_SIZE, DMA_FROM_DEVICE);
for (hw_stat_i = 0; hw_stat_i < dev->hw_stats_count; ++hw_stat_i)
hw_stats->value[hw_stat_i] =
ionic_v1_stat_val(&dev->hw_stats[hw_stat_i],
dev->hw_stats_buf, PAGE_SIZE);
return hw_stat_i;
err_cmd:
dma_unmap_single(dev->lif_cfg.hwdev, hw_stats_dma,
PAGE_SIZE, DMA_FROM_DEVICE);
err_dma:
return rc;
}
static struct rdma_hw_stats *
ionic_counter_alloc_stats(struct rdma_counter *counter)
{
struct ionic_ibdev *dev = to_ionic_ibdev(counter->device);
struct ionic_counter *cntr;
int err;
cntr = kzalloc_obj(*cntr);
if (!cntr)
return NULL;
/* buffer for current values from the device */
cntr->vals = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!cntr->vals)
goto err_vals;
err = xa_alloc(&dev->counter_stats->xa_counters, &counter->id,
cntr,
XA_LIMIT(0, IONIC_MAX_QPID),
GFP_KERNEL);
if (err)
goto err_xa;
INIT_LIST_HEAD(&cntr->qp_list);
return rdma_alloc_hw_stats_struct(dev->counter_stats->stats_hdrs,
dev->counter_stats->queue_stats_count,
RDMA_HW_STATS_DEFAULT_LIFESPAN);
err_xa:
kfree(cntr->vals);
err_vals:
kfree(cntr);
return NULL;
}
static int ionic_counter_dealloc(struct rdma_counter *counter)
{
struct ionic_ibdev *dev = to_ionic_ibdev(counter->device);
struct ionic_counter *cntr;
cntr = xa_erase(&dev->counter_stats->xa_counters, counter->id);
if (!cntr)
return -EINVAL;
kfree(cntr->vals);
kfree(cntr);
return 0;
}
static int ionic_counter_bind_qp(struct rdma_counter *counter,
struct ib_qp *ibqp,
u32 port)
{
struct ionic_ibdev *dev = to_ionic_ibdev(counter->device);
struct ionic_qp *qp = to_ionic_qp(ibqp);
struct ionic_counter *cntr;
cntr = xa_load(&dev->counter_stats->xa_counters, counter->id);
if (!cntr)
return -EINVAL;
list_add_tail(&qp->qp_list_counter, &cntr->qp_list);
ibqp->counter = counter;
return 0;
}
static int ionic_counter_unbind_qp(struct ib_qp *ibqp, u32 port)
{
struct ionic_qp *qp = to_ionic_qp(ibqp);
if (ibqp->counter) {
list_del(&qp->qp_list_counter);
ibqp->counter = NULL;
}
return 0;
}
static int ionic_get_qp_stats(struct ib_device *ibdev,
struct rdma_hw_stats *hw_stats,
u32 counter_id)
{
struct ionic_ibdev *dev = to_ionic_ibdev(ibdev);
struct ionic_counter_stats *cs;
struct ionic_counter *cntr;
dma_addr_t hw_stats_dma;
struct ionic_qp *qp;
int rc, stat_i = 0;
cs = dev->counter_stats;
cntr = xa_load(&cs->xa_counters, counter_id);
if (!cntr)
return -EINVAL;
hw_stats_dma = dma_map_single(dev->lif_cfg.hwdev, cntr->vals,
PAGE_SIZE, DMA_FROM_DEVICE);
rc = dma_mapping_error(dev->lif_cfg.hwdev, hw_stats_dma);
if (rc)
return rc;
memset(hw_stats->value, 0, sizeof(u64) * hw_stats->num_counters);
list_for_each_entry(qp, &cntr->qp_list, qp_list_counter) {
rc = ionic_hw_stats_cmd(dev, hw_stats_dma, PAGE_SIZE,
qp->qpid,
IONIC_V1_ADMIN_QP_STATS_VALS);
if (rc)
goto err_cmd;
for (stat_i = 0; stat_i < cs->queue_stats_count; ++stat_i)
hw_stats->value[stat_i] +=
ionic_v1_stat_val(&cs->hdr[stat_i],
cntr->vals,
PAGE_SIZE);
}
dma_unmap_single(dev->lif_cfg.hwdev, hw_stats_dma, PAGE_SIZE, DMA_FROM_DEVICE);
return stat_i;
err_cmd:
dma_unmap_single(dev->lif_cfg.hwdev, hw_stats_dma, PAGE_SIZE, DMA_FROM_DEVICE);
return rc;
}
static int ionic_counter_update_stats(struct rdma_counter *counter)
{
return ionic_get_qp_stats(counter->device, counter->stats, counter->id);
}
static int ionic_alloc_counters(struct ionic_ibdev *dev)
{
struct ionic_counter_stats *cs = dev->counter_stats;
int rc, hw_stats_count;
dma_addr_t hdr_dma;
/* buffer for names, sizes, offsets of values */
cs->hdr = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!cs->hdr)
return -ENOMEM;
hdr_dma = dma_map_single(dev->lif_cfg.hwdev, cs->hdr,
PAGE_SIZE, DMA_FROM_DEVICE);
rc = dma_mapping_error(dev->lif_cfg.hwdev, hdr_dma);
if (rc)
goto err_dma;
rc = ionic_hw_stats_cmd(dev, hdr_dma, PAGE_SIZE, 0,
IONIC_V1_ADMIN_QP_STATS_HDRS);
if (rc)
goto err_cmd;
dma_unmap_single(dev->lif_cfg.hwdev, hdr_dma, PAGE_SIZE, DMA_FROM_DEVICE);
/* normalize and count the number of hw_stats */
hw_stats_count = ionic_v1_stat_normalize(cs->hdr,
PAGE_SIZE / sizeof(*cs->hdr));
if (!hw_stats_count) {
rc = -ENODATA;
goto err_dma;
}
cs->queue_stats_count = hw_stats_count;
/* alloc and init array of names */
cs->stats_hdrs = kzalloc_objs(*cs->stats_hdrs, hw_stats_count);
if (!cs->stats_hdrs) {
rc = -ENOMEM;
goto err_dma;
}
ionic_fill_stats_desc(cs->stats_hdrs, cs->hdr, hw_stats_count);
return 0;
err_cmd:
dma_unmap_single(dev->lif_cfg.hwdev, hdr_dma, PAGE_SIZE, DMA_FROM_DEVICE);
err_dma:
kfree(cs->hdr);
return rc;
}
static const struct ib_device_ops ionic_hw_stats_ops = {
.driver_id = RDMA_DRIVER_IONIC,
.alloc_hw_port_stats = ionic_alloc_hw_stats,
.get_hw_stats = ionic_get_hw_stats,
};
static const struct ib_device_ops ionic_counter_stats_ops = {
.counter_alloc_stats = ionic_counter_alloc_stats,
.counter_dealloc = ionic_counter_dealloc,
.counter_bind_qp = ionic_counter_bind_qp,
.counter_unbind_qp = ionic_counter_unbind_qp,
.counter_update_stats = ionic_counter_update_stats,
};
void ionic_stats_init(struct ionic_ibdev *dev)
{
u16 stats_type = dev->lif_cfg.stats_type;
int rc;
if (stats_type & IONIC_LIF_RDMA_STAT_GLOBAL) {
rc = ionic_init_hw_stats(dev);
if (rc)
ibdev_dbg(&dev->ibdev, "Failed to init hw stats\n");
else
ib_set_device_ops(&dev->ibdev, &ionic_hw_stats_ops);
}
if (stats_type & IONIC_LIF_RDMA_STAT_QP) {
dev->counter_stats = kzalloc_obj(*dev->counter_stats);
if (!dev->counter_stats)
return;
rc = ionic_alloc_counters(dev);
if (rc) {
ibdev_dbg(&dev->ibdev, "Failed to init counter stats\n");
kfree(dev->counter_stats);
dev->counter_stats = NULL;
return;
}
xa_init_flags(&dev->counter_stats->xa_counters, XA_FLAGS_ALLOC);
ib_set_device_ops(&dev->ibdev, &ionic_counter_stats_ops);
}
}
void ionic_stats_cleanup(struct ionic_ibdev *dev)
{
if (dev->counter_stats) {
xa_destroy(&dev->counter_stats->xa_counters);
kfree(dev->counter_stats->hdr);
kfree(dev->counter_stats->stats_hdrs);
kfree(dev->counter_stats);
dev->counter_stats = NULL;
}
kfree(dev->hw_stats);
kfree(dev->hw_stats_buf);
kfree(dev->hw_stats_hdrs);
}
@@ -0,0 +1,447 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <net/addrconf.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_mad.h>
#include "ionic_ibdev.h"
#define DRIVER_DESCRIPTION "AMD Pensando RoCE HCA driver"
#define DEVICE_DESCRIPTION "AMD Pensando RoCE HCA"
MODULE_AUTHOR("Allen Hubbe <allen.hubbe@amd.com>");
MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("NET_IONIC");
static int ionic_query_device(struct ib_device *ibdev,
struct ib_device_attr *attr,
struct ib_udata *udata)
{
struct ionic_ibdev *dev = to_ionic_ibdev(ibdev);
struct net_device *ndev;
int err;
err = ib_is_udata_in_empty(udata);
if (err)
return err;
ndev = ib_device_get_netdev(ibdev, 1);
addrconf_ifid_eui48((u8 *)&attr->sys_image_guid, ndev);
dev_put(ndev);
attr->max_mr_size = dev->lif_cfg.npts_per_lif * PAGE_SIZE / 2;
attr->page_size_cap = dev->lif_cfg.page_size_supported;
attr->vendor_id = to_pci_dev(dev->lif_cfg.hwdev)->vendor;
attr->vendor_part_id = to_pci_dev(dev->lif_cfg.hwdev)->device;
attr->hw_ver = ionic_lif_asic_rev(dev->lif_cfg.lif);
attr->fw_ver = 0;
attr->max_qp = dev->lif_cfg.qp_count;
attr->max_qp_wr = IONIC_MAX_DEPTH;
attr->device_cap_flags =
IB_DEVICE_MEM_WINDOW |
IB_DEVICE_MEM_MGT_EXTENSIONS |
IB_DEVICE_MEM_WINDOW_TYPE_2B |
0;
attr->max_send_sge =
min(ionic_v1_send_wqe_max_sge(dev->lif_cfg.max_stride, 0, false),
IONIC_SPEC_HIGH);
attr->max_recv_sge =
min(ionic_v1_recv_wqe_max_sge(dev->lif_cfg.max_stride, 0, false),
IONIC_SPEC_HIGH);
attr->max_sge_rd = attr->max_send_sge;
attr->max_cq = dev->lif_cfg.cq_count / dev->lif_cfg.udma_count;
attr->max_cqe = IONIC_MAX_CQ_DEPTH - IONIC_CQ_GRACE;
attr->max_mr = dev->lif_cfg.nmrs_per_lif;
attr->max_pd = IONIC_MAX_PD;
attr->max_qp_rd_atom = IONIC_MAX_RD_ATOM;
attr->max_ee_rd_atom = 0;
attr->max_res_rd_atom = IONIC_MAX_RD_ATOM;
attr->max_qp_init_rd_atom = IONIC_MAX_RD_ATOM;
attr->max_ee_init_rd_atom = 0;
attr->atomic_cap = IB_ATOMIC_GLOB;
attr->masked_atomic_cap = IB_ATOMIC_GLOB;
attr->max_mw = dev->lif_cfg.nmrs_per_lif;
attr->max_mcast_grp = 0;
attr->max_mcast_qp_attach = 0;
attr->max_ah = dev->lif_cfg.nahs_per_lif;
attr->max_fast_reg_page_list_len = dev->lif_cfg.npts_per_lif / 2;
attr->max_pkeys = IONIC_PKEY_TBL_LEN;
return ib_respond_empty_udata(udata);
}
static int ionic_query_port(struct ib_device *ibdev, u32 port,
struct ib_port_attr *attr)
{
struct net_device *ndev;
if (port != 1)
return -EINVAL;
ndev = ib_device_get_netdev(ibdev, port);
if (!ndev)
return -ENODEV;
if (netif_running(ndev) && netif_carrier_ok(ndev)) {
attr->state = IB_PORT_ACTIVE;
attr->phys_state = IB_PORT_PHYS_STATE_LINK_UP;
} else if (netif_running(ndev)) {
attr->state = IB_PORT_DOWN;
attr->phys_state = IB_PORT_PHYS_STATE_POLLING;
} else {
attr->state = IB_PORT_DOWN;
attr->phys_state = IB_PORT_PHYS_STATE_DISABLED;
}
attr->max_mtu = iboe_get_mtu(ndev->max_mtu);
attr->active_mtu = min(attr->max_mtu, iboe_get_mtu(ndev->mtu));
attr->gid_tbl_len = IONIC_GID_TBL_LEN;
attr->ip_gids = true;
attr->port_cap_flags = 0;
attr->max_msg_sz = 0x80000000;
attr->pkey_tbl_len = IONIC_PKEY_TBL_LEN;
attr->max_vl_num = 1;
attr->subnet_prefix = 0xfe80000000000000ull;
dev_put(ndev);
return ib_get_eth_speed(ibdev, port,
&attr->active_speed,
&attr->active_width);
}
static enum rdma_link_layer ionic_get_link_layer(struct ib_device *ibdev,
u32 port)
{
return IB_LINK_LAYER_ETHERNET;
}
static int ionic_query_pkey(struct ib_device *ibdev, u32 port, u16 index,
u16 *pkey)
{
if (port != 1)
return -EINVAL;
if (index != 0)
return -EINVAL;
*pkey = IB_DEFAULT_PKEY_FULL;
return 0;
}
static int ionic_modify_device(struct ib_device *ibdev, int mask,
struct ib_device_modify *attr)
{
struct ionic_ibdev *dev = to_ionic_ibdev(ibdev);
if (mask & ~IB_DEVICE_MODIFY_NODE_DESC)
return -EOPNOTSUPP;
if (mask & IB_DEVICE_MODIFY_NODE_DESC)
memcpy(dev->ibdev.node_desc, attr->node_desc,
IB_DEVICE_NODE_DESC_MAX);
return 0;
}
static int ionic_get_port_immutable(struct ib_device *ibdev, u32 port,
struct ib_port_immutable *attr)
{
if (port != 1)
return -EINVAL;
attr->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP;
attr->pkey_tbl_len = IONIC_PKEY_TBL_LEN;
attr->gid_tbl_len = IONIC_GID_TBL_LEN;
attr->max_mad_size = IB_MGMT_MAD_SIZE;
return 0;
}
static void ionic_get_dev_fw_str(struct ib_device *ibdev, char *str)
{
struct ionic_ibdev *dev = to_ionic_ibdev(ibdev);
ionic_lif_fw_version(dev->lif_cfg.lif, str, IB_FW_VERSION_NAME_MAX);
}
static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr,
char *buf)
{
struct ionic_ibdev *dev =
rdma_device_to_drv_device(device, struct ionic_ibdev, ibdev);
return sysfs_emit(buf, "0x%x\n", ionic_lif_asic_rev(dev->lif_cfg.lif));
}
static DEVICE_ATTR_RO(hw_rev);
static ssize_t hca_type_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct ionic_ibdev *dev =
rdma_device_to_drv_device(device, struct ionic_ibdev, ibdev);
return sysfs_emit(buf, "%.64s\n", dev->ibdev.node_desc);
}
static DEVICE_ATTR_RO(hca_type);
static struct attribute *ionic_rdma_attributes[] = {
&dev_attr_hw_rev.attr,
&dev_attr_hca_type.attr,
NULL
};
static const struct attribute_group ionic_rdma_attr_group = {
.attrs = ionic_rdma_attributes,
};
static void ionic_disassociate_ucontext(struct ib_ucontext *ibcontext)
{
/*
* Dummy define disassociate_ucontext so that it does not
* wait for user context before cleaning up hw resources.
*/
}
static const struct ib_device_ops ionic_dev_ops = {
.owner = THIS_MODULE,
.driver_id = RDMA_DRIVER_IONIC,
.uverbs_abi_ver = IONIC_ABI_VERSION,
.alloc_ucontext = ionic_alloc_ucontext,
.dealloc_ucontext = ionic_dealloc_ucontext,
.mmap = ionic_mmap,
.mmap_free = ionic_mmap_free,
.alloc_pd = ionic_alloc_pd,
.dealloc_pd = ionic_dealloc_pd,
.create_ah = ionic_create_ah,
.query_ah = ionic_query_ah,
.destroy_ah = ionic_destroy_ah,
.create_user_ah = ionic_create_ah,
.get_dma_mr = ionic_get_dma_mr,
.reg_user_mr = ionic_reg_user_mr,
.reg_user_mr_dmabuf = ionic_reg_user_mr_dmabuf,
.dereg_mr = ionic_dereg_mr,
.alloc_mr = ionic_alloc_mr,
.map_mr_sg = ionic_map_mr_sg,
.alloc_mw = ionic_alloc_mw,
.dealloc_mw = ionic_dealloc_mw,
.create_cq = ionic_create_cq,
.destroy_cq = ionic_destroy_cq,
.create_qp = ionic_create_qp,
.modify_qp = ionic_modify_qp,
.query_qp = ionic_query_qp,
.destroy_qp = ionic_destroy_qp,
.post_send = ionic_post_send,
.post_recv = ionic_post_recv,
.poll_cq = ionic_poll_cq,
.req_notify_cq = ionic_req_notify_cq,
.query_device = ionic_query_device,
.query_port = ionic_query_port,
.get_link_layer = ionic_get_link_layer,
.query_pkey = ionic_query_pkey,
.modify_device = ionic_modify_device,
.get_port_immutable = ionic_get_port_immutable,
.get_dev_fw_str = ionic_get_dev_fw_str,
.device_group = &ionic_rdma_attr_group,
.disassociate_ucontext = ionic_disassociate_ucontext,
INIT_RDMA_OBJ_SIZE(ib_ucontext, ionic_ctx, ibctx),
INIT_RDMA_OBJ_SIZE(ib_pd, ionic_pd, ibpd),
INIT_RDMA_OBJ_SIZE(ib_ah, ionic_ah, ibah),
INIT_RDMA_OBJ_SIZE(ib_cq, ionic_vcq, ibcq),
INIT_RDMA_OBJ_SIZE(ib_qp, ionic_qp, ibqp),
INIT_RDMA_OBJ_SIZE(ib_mw, ionic_mr, ibmw),
};
static void ionic_init_resids(struct ionic_ibdev *dev)
{
ionic_resid_init(&dev->inuse_cqid, dev->lif_cfg.cq_count);
dev->half_cqid_udma_shift =
order_base_2(dev->lif_cfg.cq_count / dev->lif_cfg.udma_count);
ionic_resid_init(&dev->inuse_pdid, IONIC_MAX_PD);
ionic_resid_init(&dev->inuse_ahid, dev->lif_cfg.nahs_per_lif);
ionic_resid_init(&dev->inuse_mrid, dev->lif_cfg.nmrs_per_lif);
/* skip reserved lkey */
dev->next_mrkey = 1;
ionic_resid_init(&dev->inuse_qpid, dev->lif_cfg.qp_count);
/* skip reserved SMI and GSI qpids */
dev->half_qpid_udma_shift =
order_base_2(dev->lif_cfg.qp_count / dev->lif_cfg.udma_count);
ionic_resid_init(&dev->inuse_dbid, dev->lif_cfg.dbid_count);
}
static void ionic_destroy_resids(struct ionic_ibdev *dev)
{
ionic_resid_destroy(&dev->inuse_cqid);
ionic_resid_destroy(&dev->inuse_pdid);
ionic_resid_destroy(&dev->inuse_ahid);
ionic_resid_destroy(&dev->inuse_mrid);
ionic_resid_destroy(&dev->inuse_qpid);
ionic_resid_destroy(&dev->inuse_dbid);
}
static void ionic_destroy_ibdev(struct ionic_ibdev *dev)
{
ionic_kill_rdma_admin(dev, false);
ib_unregister_device(&dev->ibdev);
ionic_stats_cleanup(dev);
ionic_destroy_rdma_admin(dev);
ionic_destroy_resids(dev);
WARN_ON(!xa_empty(&dev->qp_tbl));
xa_destroy(&dev->qp_tbl);
WARN_ON(!xa_empty(&dev->cq_tbl));
xa_destroy(&dev->cq_tbl);
ib_dealloc_device(&dev->ibdev);
}
static struct ionic_ibdev *ionic_create_ibdev(struct ionic_aux_dev *ionic_adev)
{
struct ib_device *ibdev;
struct ionic_ibdev *dev;
struct net_device *ndev;
int rc;
dev = ib_alloc_device(ionic_ibdev, ibdev);
if (!dev)
return ERR_PTR(-EINVAL);
ionic_fill_lif_cfg(ionic_adev->lif, &dev->lif_cfg);
xa_init_flags(&dev->qp_tbl, GFP_ATOMIC);
xa_init_flags(&dev->cq_tbl, GFP_ATOMIC);
ionic_init_resids(dev);
rc = ionic_rdma_reset_devcmd(dev);
if (rc)
goto err_reset;
rc = ionic_create_rdma_admin(dev);
if (rc)
goto err_admin;
ibdev = &dev->ibdev;
ibdev->dev.parent = dev->lif_cfg.hwdev;
strscpy(ibdev->name, "ionic_%d", IB_DEVICE_NAME_MAX);
strscpy(ibdev->node_desc, DEVICE_DESCRIPTION, IB_DEVICE_NODE_DESC_MAX);
ibdev->node_type = RDMA_NODE_IB_CA;
ibdev->phys_port_cnt = 1;
/* the first two eq are reserved for async events */
ibdev->num_comp_vectors = dev->lif_cfg.eq_count - 2;
ndev = ionic_lif_netdev(ionic_adev->lif);
addrconf_ifid_eui48((u8 *)&ibdev->node_guid, ndev);
rc = ib_device_set_netdev(ibdev, ndev, 1);
/* ionic_lif_netdev() returns ndev with refcount held */
dev_put(ndev);
if (rc)
goto err_admin;
ib_set_device_ops(&dev->ibdev, &ionic_dev_ops);
ionic_stats_init(dev);
rc = ib_register_device(ibdev, "ionic_%d", ibdev->dev.parent);
if (rc)
goto err_register;
return dev;
err_register:
ionic_stats_cleanup(dev);
err_admin:
ionic_kill_rdma_admin(dev, false);
ionic_destroy_rdma_admin(dev);
err_reset:
ionic_destroy_resids(dev);
xa_destroy(&dev->qp_tbl);
xa_destroy(&dev->cq_tbl);
ib_dealloc_device(&dev->ibdev);
return ERR_PTR(rc);
}
static int ionic_aux_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct ionic_aux_dev *ionic_adev;
struct ionic_ibdev *dev;
ionic_adev = container_of(adev, struct ionic_aux_dev, adev);
dev = ionic_create_ibdev(ionic_adev);
if (IS_ERR(dev))
return dev_err_probe(&adev->dev, PTR_ERR(dev),
"Failed to register ibdev\n");
auxiliary_set_drvdata(adev, dev);
ibdev_dbg(&dev->ibdev, "registered\n");
return 0;
}
static void ionic_aux_remove(struct auxiliary_device *adev)
{
struct ionic_ibdev *dev = auxiliary_get_drvdata(adev);
dev_dbg(&adev->dev, "unregister ibdev\n");
ionic_destroy_ibdev(dev);
dev_dbg(&adev->dev, "unregistered\n");
}
static const struct auxiliary_device_id ionic_aux_id_table[] = {
{ .name = "ionic.rdma", },
{},
};
MODULE_DEVICE_TABLE(auxiliary, ionic_aux_id_table);
static struct auxiliary_driver ionic_aux_r_driver = {
.name = "rdma",
.probe = ionic_aux_probe,
.remove = ionic_aux_remove,
.id_table = ionic_aux_id_table,
};
static int __init ionic_mod_init(void)
{
int rc;
ionic_evt_workq = create_workqueue(KBUILD_MODNAME "-evt");
if (!ionic_evt_workq)
return -ENOMEM;
rc = auxiliary_driver_register(&ionic_aux_r_driver);
if (rc)
goto err_aux;
return 0;
err_aux:
destroy_workqueue(ionic_evt_workq);
return rc;
}
static void __exit ionic_mod_exit(void)
{
auxiliary_driver_unregister(&ionic_aux_r_driver);
destroy_workqueue(ionic_evt_workq);
}
module_init(ionic_mod_init);
module_exit(ionic_mod_exit);
@@ -0,0 +1,517 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#ifndef _IONIC_IBDEV_H_
#define _IONIC_IBDEV_H_
#include <rdma/ib_verbs.h>
#include <rdma/ib_pack.h>
#include <rdma/iter.h>
#include <rdma/uverbs_ioctl.h>
#include <rdma/ionic-abi.h>
#include <ionic_api.h>
#include <ionic_regs.h>
#include "ionic_fw.h"
#include "ionic_queue.h"
#include "ionic_res.h"
#include "ionic_lif_cfg.h"
/* Config knobs */
#define IONIC_EQ_DEPTH 511
#define IONIC_EQ_COUNT 32
#define IONIC_AQ_DEPTH 63
#define IONIC_AQ_COUNT 4
#define IONIC_EQ_ISR_BUDGET 10
#define IONIC_EQ_WORK_BUDGET 1000
#define IONIC_MAX_RD_ATOM 16
#define IONIC_PKEY_TBL_LEN 1
#define IONIC_GID_TBL_LEN 256
#define IONIC_MAX_QPID 0xffffff
#define IONIC_SPEC_HIGH 8
#define IONIC_MAX_PD 1024
#define IONIC_SPEC_HIGH 8
#define IONIC_SQCMB_ORDER 5
#define IONIC_RQCMB_ORDER 0
#define IONIC_META_LAST ((void *)1ul)
#define IONIC_META_POSTED ((void *)2ul)
#define IONIC_CQ_GRACE 100
#define IONIC_ROCE_UDP_SPORT 28272
#define IONIC_DMA_LKEY 0
#define IONIC_DMA_RKEY IONIC_DMA_LKEY
#define IONIC_CMB_SUPPORTED \
(IONIC_CMB_ENABLE | IONIC_CMB_REQUIRE | IONIC_CMB_EXPDB | \
IONIC_CMB_WC | IONIC_CMB_UC)
/* resource is not reserved on the device, indicated in tbl_order */
#define IONIC_RES_INVALID -1
struct ionic_aq;
struct ionic_cq;
struct ionic_eq;
struct ionic_vcq;
enum ionic_admin_state {
IONIC_ADMIN_ACTIVE, /* submitting admin commands to queue */
IONIC_ADMIN_PAUSED, /* not submitting, but may complete normally */
IONIC_ADMIN_KILLED, /* not submitting, locally completed */
};
enum ionic_admin_flags {
IONIC_ADMIN_F_BUSYWAIT = BIT(0), /* Don't sleep */
IONIC_ADMIN_F_TEARDOWN = BIT(1), /* In destroy path */
IONIC_ADMIN_F_INTERRUPT = BIT(2), /* Interruptible w/timeout */
};
enum ionic_mmap_flag {
IONIC_MMAP_WC = BIT(0),
};
struct ionic_mmap_entry {
struct rdma_user_mmap_entry rdma_entry;
unsigned long size;
unsigned long pfn;
u8 mmap_flags;
};
struct ionic_ibdev {
struct ib_device ibdev;
struct ionic_lif_cfg lif_cfg;
struct xarray qp_tbl;
struct xarray cq_tbl;
struct ionic_resid_bits inuse_dbid;
struct ionic_resid_bits inuse_pdid;
struct ionic_resid_bits inuse_ahid;
struct ionic_resid_bits inuse_mrid;
struct ionic_resid_bits inuse_qpid;
struct ionic_resid_bits inuse_cqid;
u8 half_cqid_udma_shift;
u8 half_qpid_udma_shift;
u8 next_qpid_udma_idx;
u8 next_mrkey;
struct work_struct reset_work;
bool reset_posted;
u32 reset_cnt;
struct delayed_work admin_dwork;
struct ionic_aq **aq_vec;
atomic_t admin_state;
struct ionic_eq **eq_vec;
struct ionic_v1_stat *hw_stats;
void *hw_stats_buf;
struct rdma_stat_desc *hw_stats_hdrs;
struct ionic_counter_stats *counter_stats;
int hw_stats_count;
};
struct ionic_eq {
struct ionic_ibdev *dev;
u32 eqid;
u32 intr;
struct ionic_queue q;
int armed;
bool enable;
struct work_struct work;
int irq;
char name[32];
};
struct ionic_admin_wr {
struct completion work;
struct list_head aq_ent;
struct ionic_v1_admin_wqe wqe;
struct ionic_v1_cqe cqe;
struct ionic_aq *aq;
int status;
};
struct ionic_admin_wr_q {
struct ionic_admin_wr *wr;
int wqe_strides;
};
struct ionic_aq {
struct ionic_ibdev *dev;
struct ionic_vcq *vcq;
struct work_struct work;
atomic_t admin_state;
unsigned long stamp;
bool armed;
u32 aqid;
u32 cqid;
spinlock_t lock; /* for posting */
struct ionic_queue q;
struct ionic_admin_wr_q *q_wr;
struct list_head wr_prod;
struct list_head wr_post;
};
struct ionic_ctx {
struct ib_ucontext ibctx;
u32 dbid;
struct rdma_user_mmap_entry *mmap_dbell;
};
struct ionic_tbl_buf {
u32 tbl_limit;
u32 tbl_pages;
size_t tbl_size;
__le64 *tbl_buf;
dma_addr_t tbl_dma;
u8 page_size_log2;
};
struct ionic_pd {
struct ib_pd ibpd;
u32 pdid;
u32 flags;
};
struct ionic_cq {
struct ionic_vcq *vcq;
u32 cqid;
u32 eqid;
spinlock_t lock; /* for polling */
struct list_head poll_sq;
bool flush;
struct list_head flush_sq;
struct list_head flush_rq;
struct list_head ibkill_flush_ent;
struct ionic_queue q;
bool color;
int credit;
u16 arm_any_prod;
u16 arm_sol_prod;
struct kref cq_kref;
struct completion cq_rel_comp;
/* infrequently accessed, keep at end */
struct ib_umem *umem;
};
struct ionic_vcq {
struct ib_cq ibcq;
struct ionic_cq cq[2];
u8 udma_mask;
u8 poll_idx;
};
struct ionic_sq_meta {
u64 wrid;
u32 len;
u16 seq;
u8 ibop;
u8 ibsts;
u8 remote:1;
u8 signal:1;
u8 local_comp:1;
};
struct ionic_rq_meta {
struct ionic_rq_meta *next;
u64 wrid;
};
struct ionic_qp {
struct ib_qp ibqp;
enum ib_qp_state state;
u32 qpid;
u32 ahid;
u32 sq_cqid;
u32 rq_cqid;
u8 udma_idx;
u8 has_ah:1;
u8 has_sq:1;
u8 has_rq:1;
u8 sig_all:1;
struct list_head qp_list_counter;
struct list_head cq_poll_sq;
struct list_head cq_flush_sq;
struct list_head cq_flush_rq;
struct list_head ibkill_flush_ent;
spinlock_t sq_lock; /* for posting and polling */
struct ionic_queue sq;
struct ionic_sq_meta *sq_meta;
u16 *sq_msn_idx;
int sq_spec;
u16 sq_old_prod;
u16 sq_msn_prod;
u16 sq_msn_cons;
u8 sq_cmb;
bool sq_flush;
bool sq_flush_rcvd;
spinlock_t rq_lock; /* for posting and polling */
struct ionic_queue rq;
struct ionic_rq_meta *rq_meta;
struct ionic_rq_meta *rq_meta_head;
int rq_spec;
u16 rq_old_prod;
u8 rq_cmb;
bool rq_flush;
struct kref qp_kref;
struct completion qp_rel_comp;
/* infrequently accessed, keep at end */
int sgid_index;
int sq_cmb_order;
u32 sq_cmb_pgid;
phys_addr_t sq_cmb_addr;
struct rdma_user_mmap_entry *mmap_sq_cmb;
struct ib_umem *sq_umem;
int rq_cmb_order;
u32 rq_cmb_pgid;
phys_addr_t rq_cmb_addr;
struct rdma_user_mmap_entry *mmap_rq_cmb;
struct ib_umem *rq_umem;
int dcqcn_profile;
struct ib_ud_header *hdr;
};
struct ionic_ah {
struct ib_ah ibah;
u32 ahid;
int sgid_index;
struct ib_ud_header hdr;
};
struct ionic_mr {
union {
struct ib_mr ibmr;
struct ib_mw ibmw;
};
u32 mrid;
int flags;
struct ib_umem *umem;
struct ionic_tbl_buf buf;
bool created;
};
struct ionic_counter_stats {
int queue_stats_count;
struct ionic_v1_stat *hdr;
struct rdma_stat_desc *stats_hdrs;
struct xarray xa_counters;
};
struct ionic_counter {
void *vals;
struct list_head qp_list;
};
static inline struct ionic_ibdev *to_ionic_ibdev(struct ib_device *ibdev)
{
return container_of(ibdev, struct ionic_ibdev, ibdev);
}
static inline struct ionic_ctx *to_ionic_ctx(struct ib_ucontext *ibctx)
{
return container_of(ibctx, struct ionic_ctx, ibctx);
}
static inline struct ionic_ctx *to_ionic_ctx_uobj(struct ib_uobject *uobj)
{
if (!uobj)
return NULL;
if (!uobj->context)
return NULL;
return to_ionic_ctx(uobj->context);
}
static inline struct ionic_pd *to_ionic_pd(struct ib_pd *ibpd)
{
return container_of(ibpd, struct ionic_pd, ibpd);
}
static inline struct ionic_mr *to_ionic_mr(struct ib_mr *ibmr)
{
return container_of(ibmr, struct ionic_mr, ibmr);
}
static inline struct ionic_mr *to_ionic_mw(struct ib_mw *ibmw)
{
return container_of(ibmw, struct ionic_mr, ibmw);
}
static inline struct ionic_vcq *to_ionic_vcq(struct ib_cq *ibcq)
{
return container_of(ibcq, struct ionic_vcq, ibcq);
}
static inline struct ionic_cq *to_ionic_vcq_cq(struct ib_cq *ibcq,
uint8_t udma_idx)
{
return &to_ionic_vcq(ibcq)->cq[udma_idx];
}
static inline struct ionic_qp *to_ionic_qp(struct ib_qp *ibqp)
{
return container_of(ibqp, struct ionic_qp, ibqp);
}
static inline struct ionic_ah *to_ionic_ah(struct ib_ah *ibah)
{
return container_of(ibah, struct ionic_ah, ibah);
}
static inline u32 ionic_ctx_dbid(struct ionic_ibdev *dev,
struct ionic_ctx *ctx)
{
if (!ctx)
return dev->lif_cfg.dbid;
return ctx->dbid;
}
static inline u32 ionic_obj_dbid(struct ionic_ibdev *dev,
struct ib_uobject *uobj)
{
return ionic_ctx_dbid(dev, to_ionic_ctx_uobj(uobj));
}
static inline bool ionic_ibop_is_local(enum ib_wr_opcode op)
{
return op == IB_WR_LOCAL_INV || op == IB_WR_REG_MR;
}
static inline void ionic_qp_complete(struct kref *kref)
{
struct ionic_qp *qp = container_of(kref, struct ionic_qp, qp_kref);
complete(&qp->qp_rel_comp);
}
static inline void ionic_cq_complete(struct kref *kref)
{
struct ionic_cq *cq = container_of(kref, struct ionic_cq, cq_kref);
complete(&cq->cq_rel_comp);
}
/* ionic_admin.c */
extern struct workqueue_struct *ionic_evt_workq;
void ionic_admin_post(struct ionic_ibdev *dev, struct ionic_admin_wr *wr);
int ionic_admin_wait(struct ionic_ibdev *dev, struct ionic_admin_wr *wr,
enum ionic_admin_flags);
int ionic_rdma_reset_devcmd(struct ionic_ibdev *dev);
int ionic_create_rdma_admin(struct ionic_ibdev *dev);
void ionic_destroy_rdma_admin(struct ionic_ibdev *dev);
void ionic_kill_rdma_admin(struct ionic_ibdev *dev, bool fatal_path);
/* ionic_controlpath.c */
int ionic_create_cq_common(struct ionic_vcq *vcq,
struct ionic_tbl_buf *buf,
const struct ib_cq_init_attr *attr,
struct ionic_ctx *ctx,
struct ib_udata *udata,
struct ionic_qdesc *req_cq,
__u32 *resp_cqid,
int udma_idx);
void ionic_destroy_cq_common(struct ionic_ibdev *dev, struct ionic_cq *cq);
void ionic_flush_qp(struct ionic_ibdev *dev, struct ionic_qp *qp);
void ionic_notify_flush_cq(struct ionic_cq *cq);
int ionic_alloc_ucontext(struct ib_ucontext *ibctx, struct ib_udata *udata);
void ionic_dealloc_ucontext(struct ib_ucontext *ibctx);
int ionic_mmap(struct ib_ucontext *ibctx, struct vm_area_struct *vma);
void ionic_mmap_free(struct rdma_user_mmap_entry *rdma_entry);
int ionic_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata);
int ionic_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata);
int ionic_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr,
struct ib_udata *udata);
int ionic_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr);
int ionic_destroy_ah(struct ib_ah *ibah, u32 flags);
struct ib_mr *ionic_get_dma_mr(struct ib_pd *ibpd, int access);
struct ib_mr *ionic_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 length,
u64 addr, int access, struct ib_dmah *dmah,
struct ib_udata *udata);
struct ib_mr *ionic_reg_user_mr_dmabuf(struct ib_pd *ibpd, u64 offset,
u64 length, u64 addr, int fd, int access,
struct ib_dmah *dmah,
struct uverbs_attr_bundle *attrs);
int ionic_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata);
struct ib_mr *ionic_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type type,
u32 max_sg);
int ionic_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg, int sg_nents,
unsigned int *sg_offset);
int ionic_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata);
int ionic_dealloc_mw(struct ib_mw *ibmw);
int ionic_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
struct uverbs_attr_bundle *attrs);
int ionic_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata);
int ionic_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attr,
struct ib_udata *udata);
int ionic_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int mask,
struct ib_udata *udata);
int ionic_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int mask,
struct ib_qp_init_attr *init_attr);
int ionic_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata);
/* ionic_datapath.c */
int ionic_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
const struct ib_send_wr **bad);
int ionic_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr,
const struct ib_recv_wr **bad);
int ionic_poll_cq(struct ib_cq *ibcq, int nwc, struct ib_wc *wc);
int ionic_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags);
/* ionic_hw_stats.c */
void ionic_stats_init(struct ionic_ibdev *dev);
void ionic_stats_cleanup(struct ionic_ibdev *dev);
/* ionic_pgtbl.c */
__le64 ionic_pgtbl_dma(struct ionic_tbl_buf *buf, u64 va);
__be64 ionic_pgtbl_off(struct ionic_tbl_buf *buf, u64 va);
int ionic_pgtbl_page(struct ionic_tbl_buf *buf, u64 dma);
int ionic_pgtbl_init(struct ionic_ibdev *dev,
struct ionic_tbl_buf *buf,
struct ib_umem *umem,
dma_addr_t dma,
int limit,
u64 page_size);
void ionic_pgtbl_unbuf(struct ionic_ibdev *dev, struct ionic_tbl_buf *buf);
#endif /* _IONIC_IBDEV_H_ */
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#include <linux/kernel.h>
#include <ionic.h>
#include <ionic_lif.h>
#include "ionic_lif_cfg.h"
#define IONIC_MIN_RDMA_VERSION 0
#define IONIC_MAX_RDMA_VERSION 2
static u8 ionic_get_expdb(struct ionic_lif *lif)
{
u8 expdb_support = 0;
if (lif->ionic->idev.phy_cmb_expdb64_pages)
expdb_support |= IONIC_EXPDB_64B_WQE;
if (lif->ionic->idev.phy_cmb_expdb128_pages)
expdb_support |= IONIC_EXPDB_128B_WQE;
if (lif->ionic->idev.phy_cmb_expdb256_pages)
expdb_support |= IONIC_EXPDB_256B_WQE;
if (lif->ionic->idev.phy_cmb_expdb512_pages)
expdb_support |= IONIC_EXPDB_512B_WQE;
return expdb_support;
}
void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg)
{
union ionic_lif_identity *ident = &lif->ionic->ident.lif;
cfg->lif = lif;
cfg->hwdev = &lif->ionic->pdev->dev;
cfg->lif_index = lif->index;
cfg->lif_hw_index = lif->hw_index;
cfg->dbid = lif->kern_pid;
cfg->dbid_count = le32_to_cpu(lif->ionic->ident.dev.ndbpgs_per_lif);
cfg->dbpage = lif->kern_dbpage;
cfg->intr_ctrl = lif->ionic->idev.intr_ctrl;
cfg->db_phys = lif->ionic->bars[IONIC_PCI_BAR_DBELL].bus_addr;
if (IONIC_VERSION(ident->rdma.version, ident->rdma.minor_version) >=
IONIC_VERSION(2, 1))
cfg->page_size_supported =
le64_to_cpu(ident->rdma.page_size_cap);
else
cfg->page_size_supported = IONIC_PAGE_SIZE_SUPPORTED;
cfg->rdma_version = ident->rdma.version;
cfg->qp_opcodes = ident->rdma.qp_opcodes;
cfg->admin_opcodes = ident->rdma.admin_opcodes;
cfg->stats_type = le16_to_cpu(ident->rdma.stats_type);
cfg->npts_per_lif = le32_to_cpu(ident->rdma.npts_per_lif);
cfg->nmrs_per_lif = le32_to_cpu(ident->rdma.nmrs_per_lif);
cfg->nahs_per_lif = le32_to_cpu(ident->rdma.nahs_per_lif);
cfg->aq_base = le32_to_cpu(ident->rdma.aq_qtype.qid_base);
cfg->cq_base = le32_to_cpu(ident->rdma.cq_qtype.qid_base);
cfg->eq_base = le32_to_cpu(ident->rdma.eq_qtype.qid_base);
/*
* ionic_create_rdma_admin() may reduce aq_count or eq_count if
* it is unable to allocate all that were requested.
* aq_count is tunable; see ionic_aq_count
* eq_count is tunable; see ionic_eq_count
*/
cfg->aq_count = le32_to_cpu(ident->rdma.aq_qtype.qid_count);
cfg->eq_count = le32_to_cpu(ident->rdma.eq_qtype.qid_count);
cfg->cq_count = le32_to_cpu(ident->rdma.cq_qtype.qid_count);
cfg->qp_count = le32_to_cpu(ident->rdma.sq_qtype.qid_count);
cfg->dbid_count = le32_to_cpu(lif->ionic->ident.dev.ndbpgs_per_lif);
cfg->aq_qtype = ident->rdma.aq_qtype.qtype;
cfg->sq_qtype = ident->rdma.sq_qtype.qtype;
cfg->rq_qtype = ident->rdma.rq_qtype.qtype;
cfg->cq_qtype = ident->rdma.cq_qtype.qtype;
cfg->eq_qtype = ident->rdma.eq_qtype.qtype;
cfg->udma_qgrp_shift = ident->rdma.udma_shift;
cfg->udma_count = 2;
cfg->max_stride = ident->rdma.max_stride;
cfg->expdb_mask = ionic_get_expdb(lif);
cfg->sq_expdb =
!!(lif->qtype_info[IONIC_QTYPE_TXQ].features & IONIC_QIDENT_F_EXPDB);
cfg->rq_expdb =
!!(lif->qtype_info[IONIC_QTYPE_RXQ].features & IONIC_QIDENT_F_EXPDB);
}
struct net_device *ionic_lif_netdev(struct ionic_lif *lif)
{
struct net_device *netdev = lif->netdev;
dev_hold(netdev);
return netdev;
}
void ionic_lif_fw_version(struct ionic_lif *lif, char *str, size_t len)
{
strscpy(str, lif->ionic->idev.dev_info.fw_version, len);
}
u8 ionic_lif_asic_rev(struct ionic_lif *lif)
{
return lif->ionic->idev.dev_info.asic_rev;
}
@@ -0,0 +1,66 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#ifndef _IONIC_LIF_CFG_H_
#define IONIC_VERSION(a, b) (((a) << 16) + ((b) << 8))
#define IONIC_PAGE_SIZE_SUPPORTED 0x40201000 /* 4kb, 2Mb, 1Gb */
#define IONIC_EXPDB_64B_WQE BIT(0)
#define IONIC_EXPDB_128B_WQE BIT(1)
#define IONIC_EXPDB_256B_WQE BIT(2)
#define IONIC_EXPDB_512B_WQE BIT(3)
struct ionic_lif_cfg {
struct device *hwdev;
struct ionic_lif *lif;
int lif_index;
int lif_hw_index;
u32 dbid;
int dbid_count;
u64 __iomem *dbpage;
struct ionic_intr __iomem *intr_ctrl;
phys_addr_t db_phys;
u64 page_size_supported;
u32 npts_per_lif;
u32 nmrs_per_lif;
u32 nahs_per_lif;
u32 aq_base;
u32 cq_base;
u32 eq_base;
int aq_count;
int eq_count;
int cq_count;
int qp_count;
u16 stats_type;
u8 aq_qtype;
u8 sq_qtype;
u8 rq_qtype;
u8 cq_qtype;
u8 eq_qtype;
u8 udma_count;
u8 udma_qgrp_shift;
u8 rdma_version;
u8 qp_opcodes;
u8 admin_opcodes;
u8 max_stride;
bool sq_expdb;
bool rq_expdb;
u8 expdb_mask;
};
void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg);
struct net_device *ionic_lif_netdev(struct ionic_lif *lif);
void ionic_lif_fw_version(struct ionic_lif *lif, char *str, size_t len);
u8 ionic_lif_asic_rev(struct ionic_lif *lif);
#endif /* _IONIC_LIF_CFG_H_ */
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#include <linux/mman.h>
#include <linux/dma-mapping.h>
#include "ionic_fw.h"
#include "ionic_ibdev.h"
__le64 ionic_pgtbl_dma(struct ionic_tbl_buf *buf, u64 va)
{
u64 pg_mask = BIT_ULL(buf->page_size_log2) - 1;
u64 dma;
if (!buf->tbl_pages)
return cpu_to_le64(0);
if (buf->tbl_pages > 1)
return cpu_to_le64(buf->tbl_dma);
if (buf->tbl_buf)
dma = le64_to_cpu(buf->tbl_buf[0]);
else
dma = buf->tbl_dma;
return cpu_to_le64(dma + (va & pg_mask));
}
__be64 ionic_pgtbl_off(struct ionic_tbl_buf *buf, u64 va)
{
if (buf->tbl_pages > 1) {
u64 pg_mask = BIT_ULL(buf->page_size_log2) - 1;
return cpu_to_be64(va & pg_mask);
}
return 0;
}
int ionic_pgtbl_page(struct ionic_tbl_buf *buf, u64 dma)
{
if (unlikely(buf->tbl_pages == buf->tbl_limit))
return -ENOMEM;
if (buf->tbl_buf)
buf->tbl_buf[buf->tbl_pages] = cpu_to_le64(dma);
else
buf->tbl_dma = dma;
++buf->tbl_pages;
return 0;
}
static int ionic_tbl_buf_alloc(struct ionic_ibdev *dev,
struct ionic_tbl_buf *buf)
{
int rc;
buf->tbl_size = buf->tbl_limit * sizeof(*buf->tbl_buf);
buf->tbl_buf = kmalloc(buf->tbl_size, GFP_KERNEL);
if (!buf->tbl_buf)
return -ENOMEM;
buf->tbl_dma = dma_map_single(dev->lif_cfg.hwdev, buf->tbl_buf,
buf->tbl_size, DMA_TO_DEVICE);
rc = dma_mapping_error(dev->lif_cfg.hwdev, buf->tbl_dma);
if (rc) {
kfree(buf->tbl_buf);
return rc;
}
return 0;
}
static int ionic_pgtbl_umem(struct ionic_tbl_buf *buf, struct ib_umem *umem)
{
struct ib_block_iter biter;
u64 page_dma;
int rc;
rdma_umem_for_each_dma_block(umem, &biter, BIT_ULL(buf->page_size_log2)) {
page_dma = rdma_block_iter_dma_address(&biter);
rc = ionic_pgtbl_page(buf, page_dma);
if (rc)
return rc;
}
return 0;
}
void ionic_pgtbl_unbuf(struct ionic_ibdev *dev, struct ionic_tbl_buf *buf)
{
if (buf->tbl_buf)
dma_unmap_single(dev->lif_cfg.hwdev, buf->tbl_dma,
buf->tbl_size, DMA_TO_DEVICE);
kfree(buf->tbl_buf);
memset(buf, 0, sizeof(*buf));
}
int ionic_pgtbl_init(struct ionic_ibdev *dev,
struct ionic_tbl_buf *buf,
struct ib_umem *umem,
dma_addr_t dma,
int limit,
u64 page_size)
{
int rc;
memset(buf, 0, sizeof(*buf));
if (umem) {
limit = ib_umem_num_dma_blocks(umem, page_size);
buf->page_size_log2 = order_base_2(page_size);
}
if (limit < 1)
return -EINVAL;
buf->tbl_limit = limit;
/* skip pgtbl if contiguous / direct translation */
if (limit > 1) {
rc = ionic_tbl_buf_alloc(dev, buf);
if (rc)
return rc;
}
if (umem)
rc = ionic_pgtbl_umem(buf, umem);
else
rc = ionic_pgtbl_page(buf, dma);
if (rc)
goto err_unbuf;
return 0;
err_unbuf:
ionic_pgtbl_unbuf(dev, buf);
return rc;
}
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#include <linux/dma-mapping.h>
#include "ionic_queue.h"
int ionic_queue_init(struct ionic_queue *q, struct device *dma_dev,
int depth, size_t stride)
{
if (depth < 0 || depth > 0xffff)
return -EINVAL;
if (stride == 0 || stride > 0x10000)
return -EINVAL;
if (depth == 0)
depth = 1;
q->depth_log2 = order_base_2(depth + 1);
q->stride_log2 = order_base_2(stride);
if (q->depth_log2 + q->stride_log2 < PAGE_SHIFT)
q->depth_log2 = PAGE_SHIFT - q->stride_log2;
if (q->depth_log2 > 16 || q->stride_log2 > 16)
return -EINVAL;
q->size = BIT_ULL(q->depth_log2 + q->stride_log2);
q->mask = BIT(q->depth_log2) - 1;
q->ptr = dma_alloc_coherent(dma_dev, q->size, &q->dma, GFP_KERNEL);
if (!q->ptr)
return -ENOMEM;
/* it will always be page aligned, but just to be sure... */
if (!PAGE_ALIGNED(q->ptr)) {
dma_free_coherent(dma_dev, q->size, q->ptr, q->dma);
return -ENOMEM;
}
q->prod = 0;
q->cons = 0;
q->dbell = 0;
return 0;
}
void ionic_queue_destroy(struct ionic_queue *q, struct device *dma_dev)
{
dma_free_coherent(dma_dev, q->size, q->ptr, q->dma);
}
@@ -0,0 +1,234 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#ifndef _IONIC_QUEUE_H_
#define _IONIC_QUEUE_H_
#include <linux/io.h>
#include <ionic_regs.h>
#define IONIC_MAX_DEPTH 0xffff
#define IONIC_MAX_CQ_DEPTH 0xffff
#define IONIC_CQ_RING_ARM IONIC_DBELL_RING_1
#define IONIC_CQ_RING_SOL IONIC_DBELL_RING_2
/**
* struct ionic_queue - Ring buffer used between device and driver
* @size: Size of the buffer, in bytes
* @dma: Dma address of the buffer
* @ptr: Buffer virtual address
* @prod: Driver position in the queue
* @cons: Device position in the queue
* @mask: Capacity of the queue, subtracting the hole
* This value is equal to ((1 << depth_log2) - 1)
* @depth_log2: Log base two size depth of the queue
* @stride_log2: Log base two size of an element in the queue
* @dbell: Doorbell identifying bits
*/
struct ionic_queue {
size_t size;
dma_addr_t dma;
void *ptr;
u16 prod;
u16 cons;
u16 mask;
u8 depth_log2;
u8 stride_log2;
u64 dbell;
};
/**
* ionic_queue_init() - Initialize user space queue
* @q: Uninitialized queue structure
* @dma_dev: DMA device for mapping
* @depth: Depth of the queue
* @stride: Size of each element of the queue
*
* Return: status code
*/
int ionic_queue_init(struct ionic_queue *q, struct device *dma_dev,
int depth, size_t stride);
/**
* ionic_queue_destroy() - Destroy user space queue
* @q: Queue structure
* @dma_dev: DMA device for mapping
*
* Return: status code
*/
void ionic_queue_destroy(struct ionic_queue *q, struct device *dma_dev);
/**
* ionic_queue_empty() - Test if queue is empty
* @q: Queue structure
*
* This is only valid for to-device queues.
*
* Return: is empty
*/
static inline bool ionic_queue_empty(struct ionic_queue *q)
{
return q->prod == q->cons;
}
/**
* ionic_queue_length() - Get the current length of the queue
* @q: Queue structure
*
* This is only valid for to-device queues.
*
* Return: length
*/
static inline u16 ionic_queue_length(struct ionic_queue *q)
{
return (q->prod - q->cons) & q->mask;
}
/**
* ionic_queue_length_remaining() - Get the remaining length of the queue
* @q: Queue structure
*
* This is only valid for to-device queues.
*
* Return: length remaining
*/
static inline u16 ionic_queue_length_remaining(struct ionic_queue *q)
{
return q->mask - ionic_queue_length(q);
}
/**
* ionic_queue_full() - Test if queue is full
* @q: Queue structure
*
* This is only valid for to-device queues.
*
* Return: is full
*/
static inline bool ionic_queue_full(struct ionic_queue *q)
{
return q->mask == ionic_queue_length(q);
}
/**
* ionic_color_wrap() - Flip the color if prod is wrapped
* @prod: Queue index just after advancing
* @color: Queue color just prior to advancing the index
*
* Return: color after advancing the index
*/
static inline bool ionic_color_wrap(u16 prod, bool color)
{
/* logical xor color with (prod == 0) */
return color != (prod == 0);
}
/**
* ionic_queue_at() - Get the element at the given index
* @q: Queue structure
* @idx: Index in the queue
*
* The index must be within the bounds of the queue. It is not checked here.
*
* Return: pointer to element at index
*/
static inline void *ionic_queue_at(struct ionic_queue *q, u16 idx)
{
return q->ptr + ((unsigned long)idx << q->stride_log2);
}
/**
* ionic_queue_at_prod() - Get the element at the producer index
* @q: Queue structure
*
* Return: pointer to element at producer index
*/
static inline void *ionic_queue_at_prod(struct ionic_queue *q)
{
return ionic_queue_at(q, q->prod);
}
/**
* ionic_queue_at_cons() - Get the element at the consumer index
* @q: Queue structure
*
* Return: pointer to element at consumer index
*/
static inline void *ionic_queue_at_cons(struct ionic_queue *q)
{
return ionic_queue_at(q, q->cons);
}
/**
* ionic_queue_next() - Compute the next index
* @q: Queue structure
* @idx: Index
*
* Return: next index after idx
*/
static inline u16 ionic_queue_next(struct ionic_queue *q, u16 idx)
{
return (idx + 1) & q->mask;
}
/**
* ionic_queue_produce() - Increase the producer index
* @q: Queue structure
*
* Caller must ensure that the queue is not full. It is not checked here.
*/
static inline void ionic_queue_produce(struct ionic_queue *q)
{
q->prod = ionic_queue_next(q, q->prod);
}
/**
* ionic_queue_consume() - Increase the consumer index
* @q: Queue structure
*
* Caller must ensure that the queue is not empty. It is not checked here.
*
* This is only valid for to-device queues.
*/
static inline void ionic_queue_consume(struct ionic_queue *q)
{
q->cons = ionic_queue_next(q, q->cons);
}
/**
* ionic_queue_consume_entries() - Increase the consumer index by entries
* @q: Queue structure
* @entries: Number of entries to increment
*
* Caller must ensure that the queue is not empty. It is not checked here.
*
* This is only valid for to-device queues.
*/
static inline void ionic_queue_consume_entries(struct ionic_queue *q,
u16 entries)
{
q->cons = (q->cons + entries) & q->mask;
}
/**
* ionic_queue_dbell_init() - Initialize doorbell bits for queue id
* @q: Queue structure
* @qid: Queue identifying number
*/
static inline void ionic_queue_dbell_init(struct ionic_queue *q, u32 qid)
{
q->dbell = IONIC_DBELL_QID(qid);
}
/**
* ionic_queue_dbell_val() - Get current doorbell update value
* @q: Queue structure
*
* Return: current doorbell update value
*/
static inline u64 ionic_queue_dbell_val(struct ionic_queue *q)
{
return q->dbell | q->prod;
}
#endif /* _IONIC_QUEUE_H_ */
@@ -0,0 +1,154 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
#ifndef _IONIC_RES_H_
#define _IONIC_RES_H_
#include <linux/kernel.h>
#include <linux/idr.h>
/**
* struct ionic_resid_bits - Number allocator based on IDA
*
* @inuse: IDA handle
* @inuse_size: Highest ID limit for IDA
*/
struct ionic_resid_bits {
struct ida inuse;
unsigned int inuse_size;
};
/**
* ionic_resid_init() - Initialize a resid allocator
* @resid: Uninitialized resid allocator
* @size: Capacity of the allocator
*
* Return: Zero on success, or negative error number
*/
static inline void ionic_resid_init(struct ionic_resid_bits *resid,
unsigned int size)
{
resid->inuse_size = size;
ida_init(&resid->inuse);
}
/**
* ionic_resid_destroy() - Destroy a resid allocator
* @resid: Resid allocator
*/
static inline void ionic_resid_destroy(struct ionic_resid_bits *resid)
{
ida_destroy(&resid->inuse);
}
/**
* ionic_resid_get_shared() - Allocate an available shared resource id
* @resid: Resid allocator
* @min: Smallest valid resource id
* @size: One after largest valid resource id
*
* Return: Resource id, or negative error number
*/
static inline int ionic_resid_get_shared(struct ionic_resid_bits *resid,
unsigned int min,
unsigned int size)
{
return ida_alloc_range(&resid->inuse, min, size - 1, GFP_KERNEL);
}
/**
* ionic_resid_get() - Allocate an available resource id
* @resid: Resid allocator
*
* Return: Resource id, or negative error number
*/
static inline int ionic_resid_get(struct ionic_resid_bits *resid)
{
return ionic_resid_get_shared(resid, 0, resid->inuse_size);
}
/**
* ionic_resid_put() - Free a resource id
* @resid: Resid allocator
* @id: Resource id
*/
static inline void ionic_resid_put(struct ionic_resid_bits *resid, int id)
{
ida_free(&resid->inuse, id);
}
/**
* ionic_bitid_to_qid() - Transform a resource bit index into a queue id
* @bitid: Bit index
* @qgrp_shift: Log2 number of queues per queue group
* @half_qid_shift: Log2 of half the total number of queues
*
* Return: Queue id
*
* Udma-constrained queues (QPs and CQs) are associated with their udma by
* queue group. Even queue groups are associated with udma0, and odd queue
* groups with udma1.
*
* For allocating queue ids, we want to arrange the bits into two halves,
* with the even queue groups of udma0 in the lower half of the bitset,
* and the odd queue groups of udma1 in the upper half of the bitset.
* Then, one or two calls of find_next_zero_bit can examine all the bits
* for queues of an entire udma.
*
* For example, assuming eight queue groups with qgrp qids per group:
*
* bitid 0*qgrp..1*qgrp-1 : qid 0*qgrp..1*qgrp-1
* bitid 1*qgrp..2*qgrp-1 : qid 2*qgrp..3*qgrp-1
* bitid 2*qgrp..3*qgrp-1 : qid 4*qgrp..5*qgrp-1
* bitid 3*qgrp..4*qgrp-1 : qid 6*qgrp..7*qgrp-1
* bitid 4*qgrp..5*qgrp-1 : qid 1*qgrp..2*qgrp-1
* bitid 5*qgrp..6*qgrp-1 : qid 3*qgrp..4*qgrp-1
* bitid 6*qgrp..7*qgrp-1 : qid 5*qgrp..6*qgrp-1
* bitid 7*qgrp..8*qgrp-1 : qid 7*qgrp..8*qgrp-1
*
* There are three important ranges of bits in the qid. There is the udma
* bit "U" at qgrp_shift, which is the least significant bit of the group
* index, and determines which udma a queue is associated with.
* The bits of lesser significance we can call the idx bits "I", which are
* the index of the queue within the group. The bits of greater significance
* we can call the grp bits "G", which are other bits of the group index that
* do not determine the udma. Those bits are just rearranged in the bit index
* in the bitset. A bitid has the udma bit in the most significant place,
* then the grp bits, then the idx bits.
*
* bitid: 00000000000000 U GGG IIIIII
* qid: 00000000000000 GGG U IIIIII
*
* Transforming from bit index to qid, or from qid to bit index, can be
* accomplished by rearranging the bits by masking and shifting.
*/
static inline u32 ionic_bitid_to_qid(u32 bitid, u8 qgrp_shift,
u8 half_qid_shift)
{
u32 udma_bit =
(bitid & BIT(half_qid_shift)) >> (half_qid_shift - qgrp_shift);
u32 grp_bits = (bitid & GENMASK(half_qid_shift - 1, qgrp_shift)) << 1;
u32 idx_bits = bitid & (BIT(qgrp_shift) - 1);
return grp_bits | udma_bit | idx_bits;
}
/**
* ionic_qid_to_bitid() - Transform a queue id into a resource bit index
* @qid: queue index
* @qgrp_shift: Log2 number of queues per queue group
* @half_qid_shift: Log2 of half the total number of queues
*
* Return: Resource bit index
*
* This is the inverse of ionic_bitid_to_qid().
*/
static inline u32 ionic_qid_to_bitid(u32 qid, u8 qgrp_shift, u8 half_qid_shift)
{
u32 udma_bit = (qid & BIT(qgrp_shift)) << (half_qid_shift - qgrp_shift);
u32 grp_bits = (qid & GENMASK(half_qid_shift, qgrp_shift + 1)) >> 1;
u32 idx_bits = qid & (BIT(qgrp_shift) - 1);
return udma_bit | grp_bits | idx_bits;
}
#endif /* _IONIC_RES_H_ */