Thread (21 messages) 21 messages, 4 authors, 5d ago

[PATCH 09/15] yunsilicon: add RDMA device support

flat view
COOLING5d

From: Xin Tian <hidden>
Date: 2026-10-12 11:44:21
Also in: linux-rdma, lkml
Subsystem: infiniband subsystem, networking drivers, the rest · Maintainers: Jason Gunthorpe, Leon Romanovsky, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

Add the RDMA driver's uverbs ABI header (include/uapi/rdma/xsc-abi.h),
the ib_device scaffolding and device registration: query_device,
query_port, query_pkey, ucontext allocation and mmap, port attributes and
the netdev notifier that tracks the port state.

The driver reports RDMA_DRIVER_XSC, which is added to the rdma_driver_id
enum at the end so existing IDs are untouched.

Signed-off-by: Xin Tian <redacted>
---
 drivers/infiniband/Kconfig                    |   1 +
 drivers/infiniband/hw/Makefile                |   1 +
 drivers/infiniband/hw/xsc/Kconfig             |  17 +
 drivers/infiniband/hw/xsc/Makefile            |   9 +
 drivers/infiniband/hw/xsc/main.c              | 797 ++++++++++++++++++
 drivers/infiniband/hw/xsc/xsc_ib.h            | 542 ++++++++++++
 .../net/ethernet/yunsilicon/xsc/common/cq.h   |  30 +
 .../ethernet/yunsilicon/xsc/common/driver.h   |  95 +++
 .../net/ethernet/yunsilicon/xsc/common/qp.h   | 104 +++
 .../net/ethernet/yunsilicon/xsc/common/srq.h  |  54 ++
 .../net/ethernet/yunsilicon/xsc/pci/Makefile  |   2 +-
 .../net/ethernet/yunsilicon/xsc/pci/link.c    |  35 +
 include/uapi/rdma/ib_user_ioctl_verbs.h       |   1 +
 include/uapi/rdma/xsc-abi.h                   | 295 +++++++
 14 files changed, 1982 insertions(+), 1 deletion(-)
 create mode 100644 drivers/infiniband/hw/xsc/Kconfig
 create mode 100644 drivers/infiniband/hw/xsc/Makefile
 create mode 100644 drivers/infiniband/hw/xsc/main.c
 create mode 100644 drivers/infiniband/hw/xsc/xsc_ib.h
 create mode 100644 drivers/net/ethernet/yunsilicon/xsc/common/cq.h
 create mode 100644 drivers/net/ethernet/yunsilicon/xsc/common/driver.h
 create mode 100644 drivers/net/ethernet/yunsilicon/xsc/common/qp.h
 create mode 100644 drivers/net/ethernet/yunsilicon/xsc/common/srq.h
 create mode 100644 drivers/net/ethernet/yunsilicon/xsc/pci/link.c
 create mode 100644 include/uapi/rdma/xsc-abi.h
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index 086195758..b3cc506db 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -100,6 +100,7 @@ source "drivers/infiniband/hw/ocrdma/Kconfig"
 source "drivers/infiniband/hw/qedr/Kconfig"
 source "drivers/infiniband/hw/usnic/Kconfig"
 source "drivers/infiniband/hw/vmw_pvrdma/Kconfig"
+source "drivers/infiniband/hw/xsc/Kconfig"
 source "drivers/infiniband/sw/rdmavt/Kconfig"
 endif # !UML
 source "drivers/infiniband/sw/rxe/Kconfig"
diff --git a/drivers/infiniband/hw/Makefile b/drivers/infiniband/hw/Makefile
index c42b22ac3..1bcd2d50a 100644
--- a/drivers/infiniband/hw/Makefile
+++ b/drivers/infiniband/hw/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_INFINIBAND_BNXT_RE)	+= bnxt_re/
 obj-$(CONFIG_INFINIBAND_BNG_RE)		+= bng_re/
 obj-$(CONFIG_INFINIBAND_ERDMA)		+= erdma/
 obj-$(CONFIG_INFINIBAND_IONIC)		+= ionic/
+obj-$(CONFIG_INFINIBAND_XSC)	+= xsc/
diff --git a/drivers/infiniband/hw/xsc/Kconfig b/drivers/infiniband/hw/xsc/Kconfig
new file mode 100644
index 000000000..5cf46df2d
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd.
+# All rights reserved.
+
+config INFINIBAND_XSC
+	tristate "Yunsilicon XSC RDMA driver"
+	depends on INFINIBAND && PCI && INET
+	depends on YUNSILICON_XSC_PCI && YUNSILICON_XSC_ETH
+	help
+	  This driver provides RDMA support for Yunsilicon XSC devices.
+	  It uses the auxiliary device published by the XSC PCI core and,
+	  for now, the netdev registered by the XSC ethernet driver to
+	  derive the port state and GID.  Because of that netdev
+	  dependency it also depends on YUNSILICON_XSC_ETH.
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called xsc_ib.
diff --git a/drivers/infiniband/hw/xsc/Makefile b/drivers/infiniband/hw/xsc/Makefile
new file mode 100644
index 000000000..6c4ec1ca0
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd.
+# All rights reserved.
+
+ccflags-y := -I$(srctree)/drivers/net/ethernet/yunsilicon/xsc
+
+obj-$(CONFIG_INFINIBAND_XSC) += xsc_ib.o
+
+xsc_ib-y := main.o
diff --git a/drivers/infiniband/hw/xsc/main.c b/drivers/infiniband/hw/xsc/main.c
new file mode 100644
index 000000000..9d53a89be
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/main.c
@@ -0,0 +1,797 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/namei.h>
+#include <linux/kobject.h>
+#include <linux/kernfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/io-mapping.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include "common/xsc_core.h"
+#include "common/xsc_hsi.h"
+#include "common/xsc_cmd.h"
+#include "common/driver.h"
+
+#include <rdma/ib_user_verbs.h>
+#include <rdma/ib_smi.h>
+#include <rdma/ib_umem.h>
+
+#include <rdma/xsc-abi.h>
+#include "xsc_ib.h"
+
+#define DRIVER_NAME "xsc_ib"
+
+#define XSC_IB_GID_TBL_LEN	256
+#define XSC_IB_MAX_PKEYS	0x80
+
+MODULE_DESCRIPTION("Yunsilicon HCA IB driver");
+MODULE_LICENSE("GPL");
+
+static int xsc_ib_query_device(struct ib_device *ibdev,
+			       struct ib_device_attr *props,
+			       struct ib_udata *udata)
+{
+	size_t udata_outlen = (udata) ? udata->outlen : 0;
+	struct xsc_ib_dev *dev = to_xdev(ibdev);
+	struct xsc_ib_query_device_resp resp;
+	union xsc_ib_fw_ver fw_ver;
+	int err = -ENOMEM;
+	size_t resp_len;
+	int max_rq_sg;
+	u64 max_tso;
+	u64 flags;
+
+	err = ib_is_udata_in_empty(udata);
+	if (err)
+		return err;
+
+	memset(&resp, 0, sizeof(resp));
+	memset(props, 0, sizeof(*props));
+
+	resp_len = sizeof(resp.comp_mask) + sizeof(resp.response_length);
+	/*check param*/
+	if (udata_outlen && udata_outlen < resp_len)
+		return -EINVAL;
+
+	resp.response_length = resp_len;
+
+	fw_ver.data = 0;
+	fw_ver.s.ver_major = dev->xdev->fw_version_major;
+	fw_ver.s.ver_minor = dev->xdev->fw_version_minor;
+	fw_ver.s.ver_patch = dev->xdev->fw_version_patch;
+	fw_ver.s.ver_tweak = dev->xdev->fw_version_tweak;
+	props->fw_ver = fw_ver.data;
+
+	props->device_cap_flags    = IB_DEVICE_CHANGE_PHY_PORT |
+		IB_DEVICE_PORT_ACTIVE_EVENT		|
+		IB_DEVICE_SYS_IMAGE_GUID		|
+		IB_DEVICE_RC_RNR_NAK_GEN;
+	props->kernel_cap_flags =  IBK_BLOCK_MULTICAST_LOOPBACK;
+	props->kernel_cap_flags |= IBK_LOCAL_DMA_LKEY;
+
+	flags = dev->xdev->caps.flags;
+	if (flags & XSC_DEV_CAP_FLAG_BAD_PKEY_CNTR)
+		props->device_cap_flags |= IB_DEVICE_BAD_PKEY_CNTR;
+	if (flags & XSC_DEV_CAP_FLAG_BAD_QKEY_CNTR)
+		props->device_cap_flags |= IB_DEVICE_BAD_QKEY_CNTR;
+	if (flags & XSC_DEV_CAP_FLAG_APM)
+		props->device_cap_flags |= IB_DEVICE_AUTO_PATH_MIG;
+	props->page_size_cap	   = dev->xdev->caps.min_page_sz;
+	props->max_mr_size	   = xsc_get_max_mr_size(dev->xdev);
+	props->max_qp		   = dev->xdev->caps.max_qp;
+	props->max_qp_wr	   = xsc_get_max_qp_depth(dev->xdev);
+	max_rq_sg = dev->xdev->caps.max_rq_desc_sz / sizeof(struct xsc_wqe_data_seg);
+
+	props->max_send_sge = dev->xdev->caps.send_ds_num - XSC_CTRL_SEG_NUM -
+		XSC_RADDR_SEG_NUM;
+	props->max_recv_sge = dev->xdev->caps.recv_ds_num;
+	props->max_sge_rd	   = 1;/*max sge per read wqe*/
+	props->max_cq		   = dev->xdev->caps.max_cq;
+	props->max_cqe		   = dev->xdev->caps.max_cqes;
+	props->max_mr		   = (1 << dev->xdev->caps.log_max_mkey) - 1;
+	props->max_pd		   = dev->xdev->caps.max_pd;
+	props->max_qp_rd_atom	   = dev->xdev->caps.max_ra_res_qp;
+	props->max_qp_init_rd_atom = dev->xdev->caps.max_ra_req_qp;
+	props->max_res_rd_atom	   = props->max_qp_rd_atom * props->max_qp;
+	props->max_srq		   =
+		dev->xdev->caps.log_max_srq ? (1 << dev->xdev->caps.log_max_srq) : 0;
+	props->max_srq_wr	   = dev->xdev->caps.max_srq_wqes - 1;
+	props->max_srq_sge	   = dev->xdev->caps.log_max_srq ? (max_rq_sg - 1) : 0;
+	props->local_ca_ack_delay  = dev->xdev->caps.local_ca_ack_delay;
+	props->atomic_cap	   = is_support_rdma_atomic(dev->xdev) ?
+		IB_ATOMIC_HCA : IB_ATOMIC_NONE;
+	props->masked_atomic_cap   = IB_ATOMIC_HCA;
+	props->max_mcast_grp	   =
+		dev->xdev->caps.log_max_mcg ? (1 << dev->xdev->caps.log_max_mcg) : 0;
+	props->max_mcast_qp_attach = dev->xdev->caps.max_qp_mcg;
+	props->max_total_mcast_qp_attach = props->max_mcast_qp_attach *
+					   props->max_mcast_grp;
+
+	props->sys_image_guid = dev->xdev->board_info->guid;
+	props->vendor_id = dev->xdev->pdev->vendor;
+	props->vendor_part_id = dev->xdev->pdev->device;
+	props->hw_ver = ((dev->xdev->chip_ver_l & 0xffff) << 16) |
+		(dev->xdev->hotfix_num & 0xffff);
+	props->max_pkeys = XSC_IB_MAX_PKEYS;
+	props->max_wq_type_rq = dev->xdev->caps.max_qp;
+	props->hca_core_clock = dev->xdev->caps.hca_core_clock * 1000; /* KHz */
+	props->rss_caps.max_rwq_indirection_tables =
+		dev->xdev->caps.max_rwq_indirection_tables;
+	props->rss_caps.max_rwq_indirection_table_size =
+		dev->xdev->caps.max_rwq_indirection_table_size;
+	props->rss_caps.supported_qpts = 1 << IB_QPT_RAW_PACKET;
+
+	/*response tso_caps extend param*/
+	if (offsetofend(typeof(resp), tso_caps) <= udata_outlen) {
+		max_tso = dev->xdev->caps.log_max_tso ? (1 << dev->xdev->caps.log_max_tso) : 0;
+		if (max_tso) {
+			resp.tso_caps.max_tso = max_tso;
+			resp.tso_caps.supported_qpts |= 1 << IB_QPT_RAW_PACKET;
+			resp.response_length += sizeof(resp.tso_caps);
+		}
+	}
+
+	/*response rss_caps extend param*/
+	if (offsetofend(typeof(resp), rss_caps) <= udata_outlen) {
+		resp.rss_caps.rx_hash_function = XSC_RX_HASH_FUNC_TOEPLITZ;
+		resp.rss_caps.rx_hash_fields_mask =
+			XSC_RX_HASH_SRC_IPV4 |
+			XSC_RX_HASH_DST_IPV4 |
+			XSC_RX_HASH_SRC_IPV6 |
+			XSC_RX_HASH_DST_IPV6 |
+			XSC_RX_HASH_SRC_PORT_TCP |
+			XSC_RX_HASH_DST_PORT_TCP |
+			XSC_RX_HASH_SRC_PORT_UDP |
+			XSC_RX_HASH_DST_PORT_UDP |
+			XSC_RX_HASH_INNER;
+		resp.response_length += sizeof(resp.rss_caps);
+	}
+
+	/*response packet pacing caps*/
+	if (offsetofend(typeof(resp), packet_pacing_caps) <= udata_outlen) {
+		resp.packet_pacing_caps.qp_rate_limit_max =
+			dev->xdev->caps.qp_rate_limit_max;
+		resp.packet_pacing_caps.qp_rate_limit_min =
+			dev->xdev->caps.qp_rate_limit_min;
+		resp.packet_pacing_caps.supported_qpts |= 1 << IB_QPT_RAW_PACKET;
+
+		resp.response_length += sizeof(resp.packet_pacing_caps);
+	}
+
+	/*copy response data to user*/
+	if (udata_outlen) {
+		err = ib_copy_to_udata(udata, &resp, resp.response_length);
+		if (err) {
+			ibdev_err(&dev->ib_dev, "copy response info to udata fail,err=%d\n", err);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void xsc_calc_link_info(struct xsc_core_device *xdev,
+			       struct ib_port_attr *props)
+{
+	switch (xsc_get_link_speed(xdev)) {
+	case MODULE_SPEED_10G:
+		props->active_speed = XSC_RDMA_LINK_SPEED_10GB;
+		props->active_width = 1;
+		break;
+	case MODULE_SPEED_25G:
+		props->active_speed = XSC_RDMA_LINK_SPEED_25GB;
+		props->active_width = 1;
+		break;
+	case MODULE_SPEED_40G_R4:
+		props->active_speed = XSC_RDMA_LINK_SPEED_10GB;
+		props->active_width = 2;
+		break;
+	case MODULE_SPEED_50G_R:
+		props->active_speed = XSC_RDMA_LINK_SPEED_50GB;
+		props->active_width = 1;
+		break;
+	case MODULE_SPEED_50G_R2:
+		props->active_speed = XSC_RDMA_LINK_SPEED_50GB;
+		props->active_width = 1;
+		break;
+	case MODULE_SPEED_100G_R2:
+		props->active_speed = XSC_RDMA_LINK_SPEED_25GB;
+		props->active_width = 2;
+		break;
+	case MODULE_SPEED_100G_R4:
+		props->active_speed = XSC_RDMA_LINK_SPEED_25GB;
+		props->active_width = 2;
+		break;
+	case MODULE_SPEED_200G_R4:
+		props->active_speed = XSC_RDMA_LINK_SPEED_50GB;
+		props->active_width = 2;
+		break;
+	case MODULE_SPEED_200G_R8:
+		props->active_speed = XSC_RDMA_LINK_SPEED_25GB;
+		props->active_width = 4;
+		break;
+	case MODULE_SPEED_400G_R8:
+		props->active_speed = XSC_RDMA_LINK_SPEED_50GB;
+		props->active_width = 4;
+		break;
+	case MODULE_SPEED_400G_R4:
+		props->active_speed = XSC_RDMA_LINK_SPEED_100GB;
+		props->active_width = 2;
+		break;
+	default:
+		props->active_speed = XSC_RDMA_LINK_SPEED_25GB;
+		props->active_width = 1;
+		break;
+	}
+}
+
+static enum rdma_link_layer xsc_ib_port_link_layer(struct ib_device *ibdev, u32 port)
+{
+	return IB_LINK_LAYER_ETHERNET;
+}
+
+int xsc_ib_query_port(struct ib_device *ibdev, u32 port,
+		      struct ib_port_attr *props)
+{
+	struct xsc_ib_dev *dev = to_xdev(ibdev);
+	struct net_device *ndev = dev->netdev;
+	struct xsc_core_device *xdev = dev->xdev;
+
+	if (port < 1 || port > xdev->caps.num_ports) {
+		ibdev_warn(&dev->ib_dev, "invalid port number %d\n", port);
+		return -EINVAL;
+	}
+
+	memset(props, 0, sizeof(*props));
+
+	if (netif_running(ndev) && netif_carrier_ok(ndev))
+		props->state = IB_PORT_ACTIVE;
+	else
+		props->state = IB_PORT_DOWN;
+
+	props->max_mtu = is_support_8k_mtu(xdev) ? IB_MTU_8192 : IB_MTU_4096;
+	props->active_mtu = min(props->max_mtu, xsc_net_to_ib_mtu(ndev->mtu));
+	props->gid_tbl_len = XSC_IB_GID_TBL_LEN;
+	props->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_OTHER_LOCAL_CHANGES_SUP;
+	props->max_msg_sz = SZ_1G;
+	props->bad_pkey_cntr = 0;
+	props->qkey_viol_cntr = 0;
+	props->pkey_tbl_len = 1;
+	props->lid = 0;
+	props->sm_lid = 0;
+	props->lmc = 0;
+	props->max_vl_num = 0;
+	props->sm_sl = 0;
+	props->subnet_timeout = 0;
+	props->init_type_reply = 0;
+	if (!is_support_rdma(xdev)) {
+		props->active_width = 1;
+		props->active_speed = XSC_RDMA_LINK_SPEED_25GB;
+	} else {
+		xsc_calc_link_info(xdev, props);
+	}
+
+	props->phys_state = netif_carrier_ok(ndev) ? XSC_RDMA_PHY_STATE_LINK_UP :
+				XSC_RDMA_PHY_STATE_DISABLED;
+	return 0;
+}
+
+static int xsc_ib_query_pkey(struct ib_device *ibdev, u32 port, u16 index,
+			     u16 *pkey)
+{
+	*pkey = 0xffff;
+	return 0;
+}
+
+int xsc_ib_alloc_ucontext(struct ib_ucontext *uctx,
+			  struct ib_udata *udata)
+{
+	struct ib_device *ibdev = uctx->device;
+	struct xsc_ib_dev *dev = to_xdev(ibdev);
+	struct xsc_ib_alloc_ucontext_req req;
+	struct xsc_ib_alloc_ucontext_resp resp;
+	int err;
+
+	if (!dev->ib_active)
+		return -EAGAIN;
+
+	err = ib_copy_from_udata(&req, udata, sizeof(req));
+	if (err)
+		return err;
+
+	memset(&resp, 0, sizeof(resp));
+	resp.qp_tab_size      = dev->xdev->caps.max_qp;
+	resp.cache_line_size  = L1_CACHE_BYTES;
+	resp.max_sq_desc_sz = dev->xdev->caps.max_sq_desc_sz;
+	resp.max_rq_desc_sz = dev->xdev->caps.max_rq_desc_sz;
+	resp.max_send_wqebb = dev->xdev->caps.max_wqes;
+	resp.max_recv_wr = dev->xdev->caps.max_wqes;
+	xsc_get_db_addr(dev->xdev, &resp.qpm_tx_db, &resp.qpm_rx_db,
+			&resp.cqm_armdb, &resp.cqm_next_cid_reg, NULL);
+	resp.send_ds_num = dev->xdev->caps.send_ds_num;
+	resp.recv_ds_num = dev->xdev->caps.recv_ds_num;
+	resp.cmds_supp_uhw |= XSC_USER_CMDS_SUPP_UHW_QUERY_DEVICE;
+	resp.device_id = dev->xdev->pdev->device;
+
+	if (req.abi_ver >= ALLOC_UCONTEXT_VER_V1) {
+		xsc_get_multidb_info(dev->xdev, &resp.multidb_num, &resp.tx_multidb_base);
+		resp.rdma_proto_mode = dev->xdev->rdma_proto_mode;
+	}
+
+	if (is_support_cqe64(dev->xdev))
+		resp.hw_feature_flag |= XSC_HW_FEATURE_FLAG_SUPPORT_CQE64;
+
+	resp.num_ports = dev->xdev->caps.num_ports;
+
+	return ib_copy_to_udata(udata, &resp, min_t(int, udata->outlen, sizeof(resp)));
+}
+
+void xsc_ib_dealloc_ucontext(struct ib_ucontext *ibcontext)
+{
+}
+
+static int xsc_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma)
+{
+	struct xsc_ib_dev *dev = to_xdev(ibcontext->device);
+	struct xsc_core_device *xdev = dev->xdev;
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	resource_size_t reg_base;
+	resource_size_t reg_size = vma->vm_end - vma->vm_start;
+	u64 tx_db = 0;
+	u64 rx_db = 0;
+	u64 cq_db = 0;
+	u64 cq_reg = 0;
+
+	xsc_get_db_addr(xdev, &tx_db, &rx_db, &cq_db, &cq_reg, NULL);
+	dev_dbg(&xdev->pdev->dev, "offset:0x%lx\n", offset);
+
+	if (offset == (tx_db & PAGE_MASK))
+		reg_base = pci_resource_start(xdev->pdev, xdev->bar_num) + (tx_db & PAGE_MASK);
+	else if (offset == (rx_db & PAGE_MASK))
+		reg_base = pci_resource_start(xdev->pdev, xdev->bar_num) + (rx_db & PAGE_MASK);
+	else if (offset == (cq_reg & PAGE_MASK))
+		reg_base = pci_resource_start(xdev->pdev, xdev->bar_num) + (cq_reg & PAGE_MASK);
+	else if (offset == (cq_db & PAGE_MASK))
+		reg_base = pci_resource_start(xdev->pdev, xdev->bar_num) + (cq_db & PAGE_MASK);
+	else if (offset == (QPM_TX_MDB_BASE_REG_ADDR & PAGE_MASK))
+		reg_base = pci_resource_start(xdev->pdev, xdev->bar_num) +
+			(QPM_TX_MDB_BASE_REG_ADDR & PAGE_MASK);
+	else if (offset == (xdev->caps.mdb_base & PAGE_MASK))
+		reg_base = pci_resource_start(xdev->pdev, xdev->bar_num) +
+			(xdev->caps.mdb_base & PAGE_MASK);
+	else
+		return -EINVAL;
+
+	dev_dbg(&xdev->pdev->dev, "regbase:0x%llx", reg_base);
+
+	reg_base = (xsc_core_is_pf(xdev) && !is_pf_bar_compressed(xdev)) ?
+		reg_base - 0xA0000000 : reg_base;
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	return remap_pfn_range(vma, vma->vm_start, reg_base >> PAGE_SHIFT,
+			       reg_size, vma->vm_page_prot);
+}
+
+static int xsc_port_immutable(struct ib_device *ibdev, u32 port_num,
+			      struct ib_port_immutable *immutable)
+{
+	struct ib_port_attr attr;
+	int err;
+
+	err = ib_query_port(ibdev, port_num, &attr);
+	if (err)
+		return err;
+
+	immutable->pkey_tbl_len = attr.pkey_tbl_len;
+	immutable->gid_tbl_len = attr.gid_tbl_len;
+	immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE |
+				    RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP;
+	immutable->max_mad_size = IB_MGMT_MAD_SIZE * 2;
+
+	return 0;
+}
+
+static struct net_device *xsc_get_netdev(struct ib_device *ibdev, u32 port_num)
+{
+	struct xsc_ib_dev *xsc_ib_dev = to_xdev(ibdev);
+	struct net_device *dev = xsc_ib_dev->netdev;
+
+	if (dev)
+		dev_hold(dev);
+
+	return dev;
+}
+
+static void xsc_get_guid(const u8 *dev_addr, u8 *guid)
+{
+	u8 mac[ETH_ALEN];
+
+	/* MAC-48 to EUI-64 mapping */
+	memcpy(mac, dev_addr, ETH_ALEN);
+	guid[0] = mac[0] ^ 2;
+	guid[1] = mac[1];
+	guid[2] = mac[2];
+	guid[3] = 0xff;
+	guid[4] = 0xfe;
+	guid[5] = mac[3];
+	guid[6] = mac[4];
+	guid[7] = mac[5];
+}
+
+static void init_node_data(struct xsc_ib_dev *dev)
+{
+	strscpy(dev->ib_dev.node_desc, "xsc_node_desc",
+		sizeof(dev->ib_dev.node_desc));
+	xsc_get_guid(dev->netdev->dev_addr, (u8 *)&dev->ib_dev.node_guid);
+}
+
+static void xsc_ib_event(struct xsc_core_device *dev, void *context,
+			 enum xsc_dev_event event, unsigned long data)
+{
+	struct xsc_ib_dev *ibdev = (struct xsc_ib_dev *)context;
+	struct ib_event ibev;
+	u8 port = 0;
+
+	switch (event) {
+	case XSC_DEV_EVENT_SYS_ERROR:
+		ibdev->ib_active = false;
+		ibev.event = IB_EVENT_DEVICE_FATAL;
+		break;
+
+	case XSC_DEV_EVENT_PORT_UP:
+		ibev.event = IB_EVENT_PORT_ACTIVE;
+		port = *(u8 *)data;
+		break;
+
+	case XSC_DEV_EVENT_PORT_DOWN:
+		ibev.event = IB_EVENT_PORT_ERR;
+		port = *(u8 *)data;
+		break;
+
+	case XSC_DEV_EVENT_PORT_INITIALIZED:
+		/* not used by ULPs */
+		return;
+
+	case XSC_DEV_EVENT_LID_CHANGE:
+		ibev.event = IB_EVENT_LID_CHANGE;
+		port = *(u8 *)data;
+		break;
+
+	case XSC_DEV_EVENT_PKEY_CHANGE:
+		ibev.event = IB_EVENT_PKEY_CHANGE;
+		port = *(u8 *)data;
+		break;
+
+	case XSC_DEV_EVENT_GUID_CHANGE:
+		ibev.event = IB_EVENT_GID_CHANGE;
+		port = *(u8 *)data;
+		break;
+
+	case XSC_DEV_EVENT_CLIENT_REREG:
+		ibev.event = IB_EVENT_CLIENT_REREGISTER;
+		port = *(u8 *)data;
+		break;
+	}
+
+	ibev.device	      = &ibdev->ib_dev;
+	ibev.element.port_num = port;
+
+	if (ibdev->ib_active)
+		ib_dispatch_event(&ibev);
+}
+
+static void xsc_ib_get_dev_fw_str(struct ib_device *ibdev, char *str)
+{
+	struct xsc_core_device *dev = to_xdev(ibdev)->xdev;
+	u8 ver_major = dev->fw_version_major;
+	u8 ver_minor = dev->fw_version_minor;
+	u16 ver_patch = dev->fw_version_patch;
+	u32 ver_tweak = dev->fw_version_tweak;
+
+	if (ver_tweak == 0) {
+		snprintf(str, IB_FW_VERSION_NAME_MAX, "v%u.%u.%u",
+			 ver_major,  ver_minor, ver_patch);
+	} else {
+		snprintf(str, IB_FW_VERSION_NAME_MAX, "v%u.%u.%u+%u",
+			 ver_major, ver_minor, ver_patch, ver_tweak);
+	}
+}
+
+static void xsc_ib_disassociate_ucontext(struct ib_ucontext *ibcontext)
+{
+}
+
+static void xsc_ib_dev_setting(struct xsc_ib_dev *dev)
+{
+	dev->ib_dev.ops.owner		= THIS_MODULE;
+	dev->ib_dev.ops.uverbs_abi_ver	= XSC_IB_UVERBS_ABI_VERSION;
+	dev->ib_dev.ops.driver_id = RDMA_DRIVER_XSC;
+	dev->ib_dev.ops.disassociate_ucontext = xsc_ib_disassociate_ucontext;
+	dev->ib_dev.ops.query_device	= xsc_ib_query_device;
+	dev->ib_dev.ops.query_port	= xsc_ib_query_port;
+	dev->ib_dev.ops.query_pkey	= xsc_ib_query_pkey;
+
+	dev->ib_dev.ops.alloc_ucontext	= xsc_ib_alloc_ucontext;
+	dev->ib_dev.ops.dealloc_ucontext = xsc_ib_dealloc_ucontext;
+	dev->ib_dev.ops.mmap		= xsc_ib_mmap;
+
+
+	dev->ib_dev.ops.get_link_layer	= xsc_ib_port_link_layer;
+	dev->ib_dev.ops.get_netdev		= xsc_get_netdev;
+
+
+	dev->ib_dev.ops.get_port_immutable		= xsc_port_immutable;
+
+	dev->ib_dev.ops.get_dev_fw_str	= xsc_ib_get_dev_fw_str;
+
+	dev->ib_dev.ops INIT_RDMA_OBJ_SIZE(ib_ucontext, xsc_ib_ucontext, ibucontext);
+}
+
+static void xsc_get_port_state(struct net_device *ndev, enum xsc_dev_event *ev)
+{
+	*ev = XSC_DEV_EVENT_PORT_DOWN;
+	if (netif_running(ndev) && netif_carrier_ok(ndev))
+		*ev = XSC_DEV_EVENT_PORT_UP;
+}
+
+static int xsc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	struct xsc_ib_dev *ibdev = container_of(this, struct xsc_ib_dev, nb);
+	struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
+	enum xsc_dev_event ev;
+	u8 port = 1;
+
+	if (ndev != ibdev->netdev)
+		goto done;
+
+	ibdev_dbg(&ibdev->ib_dev, "netdev notfiy event:%ld\n", event);
+	switch (event) {
+	case NETDEV_CHANGE:
+	case NETDEV_UP:
+	case NETDEV_DOWN:
+		xsc_get_port_state(ibdev->netdev, &ev);
+		xsc_ib_event(ibdev->xdev, ibdev, ev, (unsigned long)&port);
+		break;
+	default:
+		break;
+	}
+done:
+	return NOTIFY_DONE;
+}
+
+static int xsc_register_netdev_notifier(struct xsc_ib_dev *ibdev)
+{
+	ibdev->nb.notifier_call = xsc_netdev_event;
+	return register_netdevice_notifier(&ibdev->nb);
+}
+
+static int xsc_unregister_netdev_notifier(struct xsc_ib_dev *ibdev)
+{
+	return unregister_netdevice_notifier(&ibdev->nb);
+}
+
+static int init_one(struct xsc_core_device *xdev,
+		    struct xsc_ib_dev **ibdev)
+{
+	struct xsc_ib_dev *dev;
+	int err;
+
+	dev = (struct xsc_ib_dev *)ib_alloc_device(xsc_ib_dev, ib_dev);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->xdev = xdev;
+	dev->netdev = xdev->netdev;
+	strscpy(dev->ib_dev.name, "xscale_%d", IB_DEVICE_NAME_MAX);
+
+	memcpy(dev->xdev->priv.ibdev_name, dev->ib_dev.name, IB_DEVICE_NAME_MAX);
+
+	if (!xdev->caps.msix_enable)
+		dev->num_comp_vectors = 1;
+	else
+		dev->num_comp_vectors = xdev->dev_res->eq_table.num_comp_vectors;
+
+	dev->ib_dev.node_type		= RDMA_NODE_IB_CA;
+	dev->ib_dev.local_dma_lkey	= 0xFF;
+	dev->num_ports		= xdev->caps.num_ports;
+	dev->ib_dev.phys_port_cnt     = dev->num_ports;
+	dev->ib_dev.num_comp_vectors	= dev->num_comp_vectors;
+	dev->ib_dev.dev.parent = &xdev->pdev->dev;
+	xsc_ib_dev_setting(dev);
+	dev->xdev->priv.cm_dscp = DSCP_PCP_UNSET;
+	dev->xdev->priv.cm_pcp = DSCP_PCP_UNSET;
+	dev->xdev->priv.force_pcp = DSCP_PCP_UNSET;
+	dev->xdev->priv.force_dscp = DSCP_PCP_UNSET;
+	dev->xdev->rdma_proto_mode = RDMA_PROTO_ROCEV2;
+	dev->xdev->cm_udp_dst_port = ROCE_V2_UDP_DPORT;
+	dev->xdev->veroce_udp_dst_port = VEROCE_UDP_DPORT;
+
+	dev->ib_dev.uverbs_cmd_mask	=
+		(1ull << IB_USER_VERBS_CMD_GET_CONTEXT)		|
+		(1ull << IB_USER_VERBS_CMD_QUERY_DEVICE)	|
+		(1ull << IB_USER_VERBS_CMD_QUERY_PORT)		|
+		(1ull << IB_USER_VERBS_CMD_ALLOC_PD)		|
+		(1ull << IB_USER_VERBS_CMD_DEALLOC_PD)		|
+		(1ull << IB_USER_VERBS_CMD_CREATE_AH)		|
+		(1ull << IB_USER_VERBS_CMD_DESTROY_AH)		|
+		(1ull << IB_USER_VERBS_CMD_REG_MR)		|
+		(1ull << IB_USER_VERBS_CMD_REREG_MR)		|
+		(1ull << IB_USER_VERBS_CMD_DEREG_MR)		|
+		(1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL)	|
+		(1ull << IB_USER_VERBS_CMD_CREATE_CQ)		|
+		(1ull << IB_USER_VERBS_CMD_RESIZE_CQ)		|
+		(1ull << IB_USER_VERBS_CMD_DESTROY_CQ)		|
+		(1ull << IB_USER_VERBS_CMD_CREATE_QP)		|
+		(1ull << IB_USER_VERBS_CMD_MODIFY_QP)		|
+		(1ull << IB_USER_VERBS_CMD_QUERY_QP)		|
+		(1ull << IB_USER_VERBS_CMD_DESTROY_QP)		|
+		(1ull << IB_USER_VERBS_CMD_ATTACH_MCAST)	|
+		(1ull << IB_USER_VERBS_CMD_DETACH_MCAST)	|
+		(1ull << IB_USER_VERBS_CMD_CREATE_SRQ)		|
+		(1ull << IB_USER_VERBS_CMD_MODIFY_SRQ)		|
+		(1ull << IB_USER_VERBS_CMD_QUERY_SRQ)		|
+		(1ull << IB_USER_VERBS_CMD_DESTROY_SRQ)		|
+		(1ull << IB_USER_VERBS_CMD_OPEN_QP);
+
+	init_node_data(dev);
+
+
+
+
+	err = ib_register_device(&dev->ib_dev, dev->ib_dev.name,
+				 dev->xdev->device);
+	if (err)
+		goto err_free;
+
+	rdma_roce_rescan_device(&dev->ib_dev);
+	dev->ib_active = true;
+	*ibdev = dev;
+
+	xsc_register_netdev_notifier(dev);
+
+
+
+	return 0;
+
+err_free:
+	ib_dealloc_device((struct ib_device *)dev);
+
+	return err;
+}
+
+static void remove_one(struct xsc_core_device *xdev, void *intf_ctx)
+{
+	struct xsc_ib_dev *dev = (struct xsc_ib_dev *)intf_ctx;
+
+	xsc_unregister_netdev_notifier(dev);
+	ib_unregister_device(&dev->ib_dev);
+	ib_dealloc_device(&dev->ib_dev);
+}
+
+static void init_iommu_state(struct xsc_ib_dev *dev)
+{
+	struct iommu_domain *domain;
+
+	dev->iommu_state = XSC_IB_IOMMU_MAP_DISABLE;
+	domain = iommu_get_domain_for_dev(dev->ib_dev.dma_device);
+	if (domain) {
+		if (domain->type & __IOMMU_DOMAIN_DMA_API)
+			dev->iommu_state = XSC_IB_IOMMU_MAP_NORMAL;
+	} else {
+		/* Try to allocate DMA memory; if the DMA address differs from
+		 * the physical address, an IOMMU maps it through an unknown
+		 * domain.
+		 */
+		dma_addr_t dma_addr;
+		void *tmp;
+
+		tmp = dma_alloc_coherent(dev->ib_dev.dma_device, PAGE_SIZE,
+					 &dma_addr, GFP_KERNEL);
+		if (tmp) {
+			if (virt_to_phys(tmp) != dma_addr)
+				dev->iommu_state = XSC_IB_IOMMU_MAP_UNKNOWN_DOMAIN;
+			dma_free_coherent(dev->ib_dev.dma_device, PAGE_SIZE,
+					  tmp, dma_addr);
+		}
+	}
+
+	ibdev_dbg(&dev->ib_dev, "iommu dma map state=%d\n", dev->iommu_state);
+}
+
+static int xsc_ib_probe(struct auxiliary_device *adev,
+			const struct auxiliary_device_id *adev_id)
+{
+	struct xsc_adev *xsc_adev = container_of(adev, struct xsc_adev, adev);
+	struct xsc_core_device *xdev = xsc_adev->xdev;
+	struct xsc_ib_dev *ibdev = NULL;
+	int ret;
+
+	ret = init_one(xdev, &ibdev);
+	if (ret) {
+		dev_err(&adev->dev, "failed to add RDMA device, ret = %d\n",
+			ret);
+		return ret;
+	}
+
+	init_iommu_state(ibdev);
+	auxiliary_set_drvdata(adev, ibdev);
+
+	return 0;
+}
+
+static void xsc_ib_remove(struct auxiliary_device *adev)
+{
+	struct xsc_ib_dev *ibdev = auxiliary_get_drvdata(adev);
+
+	if (!ibdev)
+		return;
+
+	remove_one(ibdev->xdev, ibdev);
+}
+
+static const struct auxiliary_device_id xsc_ib_id_table[] = {
+	{ .name = XSC_PCI_DRV_NAME "." XSC_IB_ADEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(auxiliary, xsc_ib_id_table);
+
+static struct auxiliary_driver xsc_ib_driver = {
+	.name = XSC_IB_ADEV_NAME,
+	.probe = xsc_ib_probe,
+	.remove = xsc_ib_remove,
+	.id_table = xsc_ib_id_table,
+};
+
+static int xsc_ib_reboot_event_handler(struct notifier_block *nb, unsigned long action, void *data)
+{
+	pr_info("xsc ib driver recv %lu event\n", action);
+
+	xsc_remove_rdma_driver();
+
+	return NOTIFY_OK;
+}
+
+struct notifier_block xsc_ib_nb = {
+	.notifier_call = xsc_ib_reboot_event_handler,
+	.next = NULL,
+	.priority = 2,
+};
+
+void xsc_remove_rdma_driver(void)
+{
+	auxiliary_driver_unregister(&xsc_ib_driver);
+}
+
+static int __init xsc_ib_init(void)
+{
+	int ret;
+
+	ret = auxiliary_driver_register(&xsc_ib_driver);
+	if (ret) {
+		pr_err("failed to register auxiliary driver\n");
+		return ret;
+	}
+
+	register_reboot_notifier(&xsc_ib_nb);
+
+	return 0;
+}
+
+static void __exit xsc_ib_cleanup(void)
+{
+	unregister_reboot_notifier(&xsc_ib_nb);
+	xsc_remove_rdma_driver();
+}
+
+module_init(xsc_ib_init);
+module_exit(xsc_ib_cleanup);
diff --git a/drivers/infiniband/hw/xsc/xsc_ib.h b/drivers/infiniband/hw/xsc/xsc_ib.h
new file mode 100644
index 000000000..a2ae382e2
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/xsc_ib.h
@@ -0,0 +1,542 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#ifndef XSC_IB_H
+#define XSC_IB_H
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_smi.h>
+#include <rdma/ib_pack.h>
+#include <rdma/ib_mad.h>
+#include "common/xsc_core.h"
+#include "common/driver.h"
+#include "common/cq.h"
+#include "common/srq.h"
+#include "common/qp.h"
+#include <linux/types.h>
+#include <linux/iommu.h>
+#include <linux/dma-direct.h>
+#include <linux/dma-mapping.h>
+#include <crypto/hash.h>
+#include <rdma/uverbs_ioctl.h>
+
+#define	XSC_PAGE_SHIFT_4K	12
+#define	XSC_PAGE_SHIFT_64K	16
+#define	XSC_PAGE_SHIFT_2M	21
+#define	XSC_PAGE_SHIFT_1G	30
+#define XSC_PAGE_SZ_4K		BIT(XSC_PAGE_SHIFT_4K)
+#define XSC_PAGE_SZ_64K		BIT(XSC_PAGE_SHIFT_64K)
+#define XSC_PAGE_SZ_2M		BIT(XSC_PAGE_SHIFT_2M)
+#define XSC_PAGE_SZ_1G		BIT(XSC_PAGE_SHIFT_1G)
+#define XSC_MR_PAGE_CAP_MASK	(XSC_PAGE_SZ_4K |	\
+				XSC_PAGE_SZ_64K |	\
+				XSC_PAGE_SZ_2M |	\
+				XSC_PAGE_SZ_1G)
+
+
+struct xsc_ib_ucontext {
+	struct ib_ucontext	ibucontext;
+};
+
+struct xsc_ib_pd {
+	struct ib_pd		ibpd;
+	u32			pdn;
+	u32			pa_lkey;
+};
+
+struct wr_list {
+	u16	opcode;
+	u16	next;
+};
+
+struct xsc_ib_wq {
+	u64		       *wrid;
+	u32		       *wr_data;
+	struct wr_list	       *w_list;
+	unsigned long	       *wqe_head;
+
+	/* serialize post to the work queue
+	 */
+	spinlock_t		lock;
+	int			wqe_cnt;
+	int			ds_cnt;
+	int			max_post;
+	int			max_gs;
+	int			offset;
+	int			wqe_shift;
+	unsigned int		head;
+	unsigned int		tail;
+	u32			cur_post;
+	u32			last_poll;
+	void		       *qend;
+	void		*hdr_buf;
+	u32			hdr_size;
+	dma_addr_t	hdr_dma;
+	int			mad_queue_depth;
+	int			mad_index;
+	u32			*wr_opcode;
+	u32			*need_flush;
+	atomic_t	flush_wqe_cnt;
+	int			*prev_ce_wqe;
+	int			last_comp_ce_wqe;
+	int			last_post_ce_wqe;
+};
+
+enum {
+	XSC_QP_USER,
+	XSC_QP_KERNEL,
+};
+
+struct xsc_ib_qp {
+	struct ib_qp		ibqp;
+	struct xsc_core_qp	xqp;
+	struct xsc_buf		buf;
+
+	struct xsc_ib_wq	rq;
+
+	u32			doorbell_qpn;
+	u8			sq_signal_bits;
+	struct xsc_ib_wq	sq;
+
+	struct ib_umem	       *umem;
+	int			buf_size;
+
+	/* serialize qp state modifications
+	 */
+	struct mutex		mutex;
+	u32			flags;
+	u8			port;
+	u8			alt_port;
+	u8			atomic_rd_en;
+	u8			resp_depth;
+	u8			state;
+	int			max_inline_data;
+	int			has_rq;
+
+	int			create_type;
+	u32			pa_lkey;
+	/* For QP1 */
+	struct ib_ud_header	qp1_hdr;
+	u32			send_psn;
+	struct xsc_qp_context	ctx;
+	struct ib_cq		*send_cq;
+	struct ib_cq		*recv_cq;
+	/* For qp resources */
+	spinlock_t		lock;
+
+	struct list_head	cq_recv_list;
+	struct list_head	cq_send_list;
+	bool		post_send_invalid;
+	bool		post_recv_invalid;
+	struct ib_recv_wr *srq_head_wr;
+	struct ib_recv_wr *srq_tail_wr;
+	bool has_trig_cq_comp_handle;
+};
+
+struct xsc_ib_cq_buf {
+	struct xsc_buf		buf;
+	struct ib_umem		*umem;
+	int			cqe_size;
+};
+
+enum xsc_ib_qp_flags {
+	XSC_IB_QP_BLOCK_MULTICAST_LOOPBACK     = 1 << 0,
+	XSC_IB_QP_SIGNATURE_HANDLING           = 1 << 1,
+};
+
+struct xsc_ib_cq {
+	struct ib_cq		ibcq;
+	struct xsc_core_cq	xcq;
+	struct xsc_ib_cq_buf	buf;
+
+	/* serialize access to the CQ
+	 */
+	spinlock_t		lock;
+
+	/* protect resize cq
+	 */
+	struct mutex		resize_mutex;
+	struct xsc_ib_cq_resize *resize_buf;
+	struct ib_umem	       *resize_umem;
+	int			cqe_size;
+	struct list_head	send_qp_list;
+	struct list_head	recv_qp_list;
+	struct list_head	err_state_qp_list;
+	u32			create_flags;
+	int			invalid;
+};
+
+enum xsc_srq_wr_per_qp {
+	XSC_SRQ_MIN_WR_NUM = 32,
+	XSC_SRQ_MAX_WR_NUM = 128,
+};
+
+enum {
+	XSC_SRQ_USER,
+	XSC_SRQ_KERNEL,
+};
+
+struct xsc_ib_srq {
+	struct ib_srq		ibsrq;
+	struct xsc_core_srq	xsrq;
+	u64		       *wrid;
+	/* protect SRQ hanlding
+	 */
+	spinlock_t		lock;
+	int			head;
+	int			tail;
+	struct ib_umem	       *umem;
+	/* serialize arming a SRQ
+	 */
+	struct mutex		mutex;
+	int			qp_num;
+	int			create_type;
+};
+
+void xsc_restore_cache_srq_wrs(struct xsc_ib_srq *srq, struct xsc_ib_qp *qp);
+void xsc_free_one_cache_srq_wr(struct xsc_ib_srq *srq, struct xsc_ib_qp *qp, u64 wr_id);
+void xsc_free_all_cache_wrs(struct ib_recv_wr *wr);
+void xsc_post_cache_wrs(struct xsc_ib_srq *srq, struct xsc_ib_qp *qp, int wr_num, bool init);
+
+struct xsc_ib_mr {
+	struct ib_mr		ibmr;
+	struct xsc_core_mr	xmr;
+	struct ib_umem	       *umem;
+	int			npages;
+};
+
+#define MAX_OPCODE	IB_WR_MASKED_ATOMIC_FETCH_AND_ADD
+
+struct xsc_ib_dev {
+	struct ib_device		ib_dev;
+	struct net_device	*netdev;
+	struct xsc_core_device *xdev;
+	int				num_ports;
+	int				num_comp_vectors;
+	u8				ib_active;
+	u32				crc_32_table[256];
+	int iommu_state;
+	struct notifier_block nb;
+	u32				opcode_map[MAX_OPCODE + 1];
+};
+
+union xsc_ib_fw_ver {
+	u64 data;
+	struct {
+		u8	ver_major;
+		u8	ver_minor;
+		u16	ver_patch;
+		u32	ver_tweak;
+	} s;
+};
+
+struct xsc_err_state_qp_node {
+	struct list_head	entry;
+	u32					qp_id;
+	bool				is_sq;
+};
+
+struct xsc_ib_ah {
+	struct ib_ah		ibah;
+	struct xsc_av		av;
+};
+
+/*
+ * Conversion helpers.
+ *
+ * The first group goes from a uverbs object to the private structure that
+ * embeds it; the second goes from the core object to the private structure
+ * that embeds that one.  Both directions exist for cq, qp and srq, so the
+ * names spell out which object is being converted.
+ */
+static inline struct xsc_ib_dev *to_xdev(struct ib_device *ibdev)
+{
+	return container_of(ibdev, struct xsc_ib_dev, ib_dev);
+}
+
+static inline struct xsc_ib_pd *to_xpd(struct ib_pd *ibpd)
+{
+	return container_of(ibpd, struct xsc_ib_pd, ibpd);
+}
+
+static inline struct xsc_ib_mr *to_xmr(struct ib_mr *ibmr)
+{
+	return container_of(ibmr, struct xsc_ib_mr, ibmr);
+}
+
+static inline struct xsc_ib_ah *to_xah(struct ib_ah *ibah)
+{
+	return container_of(ibah, struct xsc_ib_ah, ibah);
+}
+
+static inline struct xsc_ib_cq *to_xcq(struct ib_cq *ibcq)
+{
+	return container_of(ibcq, struct xsc_ib_cq, ibcq);
+}
+
+static inline struct xsc_ib_qp *to_xqp(struct ib_qp *ibqp)
+{
+	return container_of(ibqp, struct xsc_ib_qp, ibqp);
+}
+
+static inline struct xsc_ib_srq *to_xsrq(struct ib_srq *ibsrq)
+{
+	return container_of(ibsrq, struct xsc_ib_srq, ibsrq);
+}
+
+static inline struct xsc_ib_cq *core_to_xcq(struct xsc_core_cq *xcq)
+{
+	return container_of(xcq, struct xsc_ib_cq, xcq);
+}
+
+static inline struct xsc_ib_qp *core_to_xqp(struct xsc_core_qp *xqp)
+{
+	return container_of(xqp, struct xsc_ib_qp, xqp);
+}
+
+static inline struct xsc_ib_srq *core_to_xsrq(struct xsc_core_srq *xsrq)
+{
+	return container_of(xsrq, struct xsc_ib_srq, xsrq);
+}
+
+void build_opcode_map(struct xsc_ib_dev *dev);
+
+int xsc_ib_create_ah(struct ib_ah *ibah,
+		     struct rdma_ah_init_attr *init_attr,
+		     struct ib_udata *udata);
+int xsc_ib_destroy_ah(struct ib_ah *ibah, u32 flags);
+int xsc_ib_create_cq(struct ib_cq *ibcq,
+		     const struct ib_cq_init_attr *attr,
+		     struct uverbs_attr_bundle *attrs);
+int xsc_ib_destroy_cq(struct ib_cq *cq, struct ib_udata *udata);
+int xsc_ib_destroy_qp(struct ib_qp *qp, struct ib_udata *udata);
+int xsc_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata);
+int xsc_ib_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata);
+void xsc_ib_dealloc_ucontext(struct ib_ucontext *ibcontext);
+int xsc_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata);
+int xsc_ib_dealloc_pd(struct ib_pd *pd, struct ib_udata *udata);
+
+int xsc_ib_create_srq(struct ib_srq *ib_srq,
+		      struct ib_srq_init_attr *init_attr,
+		      struct ib_udata *udata);
+int xsc_ib_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
+		      enum ib_srq_attr_mask attr_mask, struct ib_udata *udata);
+int xsc_ib_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr);
+int xsc_ib_post_srq_recv(struct ib_srq *ibsrq, const struct ib_recv_wr *wr,
+			 const struct ib_recv_wr **bad_wr);
+int xsc_ib_destroy_srq(struct ib_srq *srq, struct ib_udata *udata);
+
+int xsc_ib_query_port(struct ib_device *ibdev, u32 port,
+		      struct ib_port_attr *props);
+
+int xsc_ib_create_qp(struct ib_qp *ibqp,
+		     struct ib_qp_init_attr *init_attr,
+		     struct ib_udata *udata);
+
+void __xsc_ib_cq_clean(struct xsc_ib_cq *cq, u32 qpn);
+void xsc_ib_cq_clean(struct xsc_ib_cq *cq, u32 qpn);
+
+int xsc_ib_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr);
+int xsc_ib_err_state_qp(struct xsc_ib_dev *dev, struct xsc_ib_qp *qp,
+			enum xsc_qp_state cur_state, enum xsc_qp_state state);
+int xsc_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+		     int attr_mask, struct ib_udata *udata);
+int xsc_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr_mask,
+		    struct ib_qp_init_attr *qp_init_attr);
+
+int xsc_ib_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr,
+		     const struct ib_send_wr **bad_wr);
+int xsc_ib_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *wr,
+		     const struct ib_recv_wr **bad_wr);
+
+void *xsc_get_send_wqe(struct xsc_ib_qp *qp, int n);
+int xsc_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc);
+int xsc_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags);
+struct ib_mr *xsc_ib_get_dma_mr(struct ib_pd *pd, int acc);
+struct ib_mr *xsc_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
+				 u64 virt_addr, int access_flags,
+				 struct ib_dmah *dmah,
+				 struct ib_udata *udata);
+int xsc_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset);
+void xsc_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift,
+		       int *ncont);
+void xsc_ib_populate_pas(struct xsc_ib_dev *dev, struct ib_umem *umem,
+			 int page_shift, __be64 *pas, int npages, bool need_to_devide);
+struct ib_mr *xsc_ib_reg_user_mr_dmabuf(struct ib_pd *pd, u64 offset,
+					u64 length, u64 virt_addr,
+					int fd, int access_flags,
+					struct ib_dmah *dmah,
+					struct uverbs_attr_bundle *attrs);
+
+void xsc_ib_drain_rq(struct ib_qp *qp);
+void xsc_ib_drain_sq(struct ib_qp *qp);
+
+static inline u8 convert_access(int acc)
+{
+	return (acc & IB_ACCESS_REMOTE_ATOMIC ? XSC_PERM_ATOMIC       : 0) |
+	       (acc & IB_ACCESS_REMOTE_WRITE  ? XSC_PERM_REMOTE_WRITE : 0) |
+	       (acc & IB_ACCESS_REMOTE_READ   ? XSC_PERM_REMOTE_READ  : 0) |
+	       (acc & IB_ACCESS_LOCAL_WRITE   ? XSC_PERM_LOCAL_WRITE  : 0) |
+	       XSC_PERM_LOCAL_READ;
+}
+
+#define IB_MTU_8192	6
+
+static inline enum ib_mtu xsc_net_to_ib_mtu(unsigned int mtu)
+{
+	mtu = mtu - (IB_GRH_BYTES + IB_UDP_BYTES + IB_BTH_BYTES +
+		     IB_EXT_XRC_BYTES + IB_EXT_ATOMICETH_BYTES +
+		     IB_ICRC_BYTES);
+	if (mtu >= 8192)
+		return IB_MTU_8192;
+	else if (mtu >= ib_mtu_enum_to_int(IB_MTU_4096))
+		return IB_MTU_4096;
+	else if (mtu >= ib_mtu_enum_to_int(IB_MTU_1024))
+		return IB_MTU_1024;
+	else
+		return 0;
+}
+
+#define XSC_IB_IOMMU_MAP_DISABLE 0
+#define XSC_IB_IOMMU_MAP_UNKNOWN_DOMAIN 1
+#define XSC_IB_IOMMU_MAP_NORMAL 2
+
+static inline int xsc_ib_iommu_dma_map(struct ib_device *ibdev)
+{
+	return to_xdev(ibdev)->iommu_state;
+}
+
+static inline void *xsc_ib_iova_to_virt(struct ib_device *ibdev, dma_addr_t iova)
+{
+	phys_addr_t phyaddr;
+	struct iommu_domain *domain;
+
+	domain = iommu_get_domain_for_dev(ibdev->dma_device);
+	if (likely(domain)) {
+		phyaddr = iommu_iova_to_phys(domain, iova);
+		phyaddr |= iova & (PAGE_SIZE - 1);
+	} else {
+		phyaddr = dma_to_phys(ibdev->dma_device, iova);
+	}
+
+	return phys_to_virt(phyaddr);
+}
+
+struct ib_mad_list_head {
+	struct list_head list;
+	struct ib_cqe cqe;
+	struct ib_mad_queue *mad_queue;
+};
+
+#define IB_MAD_SEND_REQ_MAX_SG 2
+struct ib_mad_send_wr_private {
+	struct ib_mad_list_head mad_list;
+	struct list_head agent_list;
+	struct ib_mad_agent_private *mad_agent_priv;
+	struct ib_mad_send_buf send_buf;
+	u64 header_mapping;
+	u64 payload_mapping;
+	struct ib_ud_wr send_wr;
+	struct ib_sge sg_list[IB_MAD_SEND_REQ_MAX_SG];
+	__be64 tid;
+	unsigned long timeout;
+	int max_retries;
+	int retries_left;
+	int retry;
+	int refcount;
+	enum ib_wc_status status;
+
+	/* RMPP control */
+	struct list_head rmpp_list;
+	struct ib_rmpp_segment *last_ack_seg;
+	struct ib_rmpp_segment *cur_seg;
+	int last_ack;
+	int seg_num;
+	int newwin;
+	int pad;
+};
+
+struct ib_mad_private_header {
+	struct ib_mad_list_head mad_list;
+	struct ib_mad_recv_wc recv_wc;
+	struct ib_wc wc;
+	u64 mapping;
+} __packed;
+
+struct ib_mad_private {
+	struct ib_mad_private_header header;
+	size_t mad_size;
+	struct ib_grh grh;
+	DECLARE_FLEX_ARRAY(u8, mad);
+} __packed;
+
+static inline void *xsc_ib_send_mad_sg_virt_addr(struct ib_device *ibdev,
+						 const struct ib_send_wr *wr,
+						 int sg)
+{
+	struct ib_mad_send_wr_private *mad_send_wr;
+	struct ib_mad_list_head *mad_list;
+	int iommu_state = xsc_ib_iommu_dma_map(ibdev);
+
+	/* direct dma mapping */
+	if (!iommu_state)
+		return phys_to_virt(dma_to_phys(ibdev->dma_device, wr->sg_list[sg].addr));
+
+	if (iommu_state == XSC_IB_IOMMU_MAP_NORMAL)
+		return xsc_ib_iova_to_virt(ibdev, wr->sg_list[sg].addr);
+
+	mad_list = container_of(wr->wr_cqe, struct ib_mad_list_head, cqe);
+	mad_send_wr = container_of(mad_list, struct ib_mad_send_wr_private,
+				   mad_list);
+
+	/* sg_list[0] */
+	if (sg == 0)
+		return mad_send_wr->send_buf.mad;
+
+	/* sg_list[1] */
+	if (mad_send_wr->send_buf.seg_count)
+		return ib_get_rmpp_segment(&mad_send_wr->send_buf,
+					   mad_send_wr->seg_num);
+	return mad_send_wr->send_buf.mad + mad_send_wr->send_buf.hdr_len;
+}
+
+static inline void *xsc_ib_recv_mad_sg_virt_addr(struct ib_device *ibdev,
+						 struct ib_wc *wc,
+						 u64 sg_addr)
+{
+	struct ib_mad_private_header *mad_priv_hdr;
+	struct ib_mad_private *recv;
+	struct ib_mad_list_head *mad_list;
+	int iommu_state = xsc_ib_iommu_dma_map(ibdev);
+
+	/* direct dma mapping */
+	if (!iommu_state)
+		return phys_to_virt(dma_to_phys(ibdev->dma_device, sg_addr));
+
+	if (iommu_state == XSC_IB_IOMMU_MAP_NORMAL)
+		return xsc_ib_iova_to_virt(ibdev, sg_addr);
+
+	mad_list = container_of(wc->wr_cqe, struct ib_mad_list_head, cqe);
+	mad_priv_hdr = container_of(mad_list, struct ib_mad_private_header, mad_list);
+	recv = container_of(mad_priv_hdr, struct ib_mad_private, header);
+	return &recv->grh;
+}
+
+struct xsc_ib_burst_info {
+	__u32       max_burst_sz;
+	__u16       typical_pkt_sz;
+	__u16       reserved;
+};
+
+struct xsc_ib_modify_qp {
+	__u32		reserved;
+	struct xsc_ib_burst_info  burst_info;
+	__u32		profile;
+};
+
+#endif /* XSC_IB_H */
diff --git a/drivers/net/ethernet/yunsilicon/xsc/common/cq.h b/drivers/net/ethernet/yunsilicon/xsc/common/cq.h
new file mode 100644
index 000000000..5217000c5
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/common/cq.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#ifndef XSC_CORE_CQ_H
+#define XSC_CORE_CQ_H
+
+#include "common/xsc_core.h"
+
+enum {
+	XSC_CQE_OWNER_MASK	= 1,
+};
+
+enum {
+	CQE_SIZE_64 = 0,
+	CQE_SIZE_128 = 1,
+};
+
+enum {
+	XSC_CQ_DB_REQ_NOT_SOL		= 1,
+	XSC_CQ_DB_REQ_NOT		= 0,
+};
+
+void xsc_init_cq_table(struct xsc_core_device *xdev);
+int xsc_core_create_cq(struct xsc_core_device *xdev, struct xsc_core_cq *cq,
+		       struct xsc_create_cq_ex_mbox_in *in, int inlen);
+int xsc_core_destroy_cq(struct xsc_core_device *xdev, struct xsc_core_cq *cq);
+
+#endif /* XSC_CORE_CQ_H */
diff --git a/drivers/net/ethernet/yunsilicon/xsc/common/driver.h b/drivers/net/ethernet/yunsilicon/xsc/common/driver.h
new file mode 100644
index 000000000..b3e91c868
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/common/driver.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#ifndef XSC_DRIVER_H
+#define XSC_DRIVER_H
+
+#include <linux/kernel.h>
+#include <linux/completion.h>
+#include <linux/pci.h>
+#include <linux/spinlock_types.h>
+#include <linux/semaphore.h>
+#include <linux/vmalloc.h>
+#include <linux/radix-tree.h>
+#include <rdma/ib_verbs.h>
+
+#include "common/xsc_device.h"
+#include "common/xsc_core.h"
+#include "common/xsc_cmd_api.h"
+#include "common/xsc_cmd.h"
+#include "common/xsc_hsi.h"
+#include "common/srq.h"
+
+struct rxe_bth {
+	u8			opcode;
+	u8			flags;
+	__be16			pkey;
+	__be32			qpn;
+	__be32			apsn;
+};
+
+struct rxe_deth {
+	__be32			qkey;
+	__be32			src_qp;
+};
+
+static inline void *xsc_vzalloc(unsigned long size)
+{
+	void *rtn;
+
+	rtn = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
+	if (!rtn)
+		rtn = vzalloc(size);
+	return rtn;
+}
+
+static inline void xsc_vfree(const void *addr)
+{
+	if (addr && is_vmalloc_addr(addr))
+		vfree(addr);
+	else
+		kfree(addr);
+}
+
+int xsc_buf_alloc(struct xsc_core_device *xdev, unsigned long size,
+		  unsigned long max_direct, struct xsc_buf *buf);
+void xsc_buf_free(struct xsc_core_device *xdev, struct xsc_buf *buf);
+void xsc_fill_page_array(struct xsc_buf *buf, __be64 *pas, unsigned int npages);
+
+int xsc_core_create_mkey(struct xsc_core_device *xdev, struct xsc_core_mr *mr);
+int xsc_core_destroy_mkey(struct xsc_core_device *xdev, struct xsc_core_mr *mr);
+int xsc_core_register_mr(struct xsc_core_device *xdev, struct xsc_core_mr *mr,
+			 struct xsc_register_mr_mbox_in *in, int inlen);
+int xsc_core_dereg_mr(struct xsc_core_device *xdev, struct xsc_core_mr *mr);
+void xsc_reg_local_dma_mr(struct xsc_core_device *xdev);
+
+int xsc_core_alloc_pd(struct xsc_core_device *xdev, u32 *pdn);
+int xsc_core_dealloc_pd(struct xsc_core_device *xdev, u32 pdn);
+
+int xsc_core_create_srq(struct xsc_core_device *xdev, struct xsc_core_srq *srq);
+int xsc_core_destroy_srq(struct xsc_core_device *xdev, struct xsc_core_srq *srq);
+
+int xsc_core_create_cq(struct xsc_core_device *xdev, struct xsc_core_cq *cq,
+		       struct xsc_create_cq_ex_mbox_in *in, int inlen);
+int xsc_core_destroy_cq(struct xsc_core_device *xdev, struct xsc_core_cq *cq);
+
+int xsc_core_create_qp(struct xsc_core_device *xdev,
+		       struct xsc_core_qp *qp,
+		       struct xsc_create_qp_mbox_in *in,
+		       int inlen);
+int xsc_core_destroy_qp(struct xsc_core_device *xdev,
+			struct xsc_core_qp *qp);
+int xsc_core_qp_modify(struct xsc_core_device *xdev, enum xsc_qp_state cur_state,
+		       enum xsc_qp_state new_state,
+		       struct xsc_modify_qp_mbox_in *in, int sqd_event,
+		       struct xsc_core_qp *qp);
+int xsc_core_qp_query(struct xsc_core_device *xdev, struct xsc_core_qp *qp,
+		      struct xsc_query_qp_mbox_out *out, int outlen);
+void xsc_qp_event(struct xsc_core_device *xdev, u32 qpn, int event_type);
+
+int xsc_vector2eqn(struct xsc_core_device *xdev, int vector, int *eqn,
+		   unsigned int *irqn);
+
+#endif /* XSC_DRIVER_H */
diff --git a/drivers/net/ethernet/yunsilicon/xsc/common/qp.h b/drivers/net/ethernet/yunsilicon/xsc/common/qp.h
new file mode 100644
index 000000000..4855c97e4
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/common/qp.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#ifndef XSC_QP_H
+#define XSC_QP_H
+
+#include <linux/radix-tree.h>
+
+#include "common/xsc_core.h"
+#include "common/xsc_cmd.h"
+#include "common/xsc_hsi.h"
+
+enum {
+	XSC_QP_PM_MIGRATED		= 0x3,
+	XSC_QP_PM_ARMED			= 0x0,
+	XSC_QP_PM_REARM			= 0x1
+};
+
+enum {
+	XSC_WQE_CTRL_CQ_UPDATE		= 2 << 2,
+	XSC_WQE_CTRL_SOLICITED		= 1 << 1,
+};
+
+struct xsc_av {
+	union {
+		struct {
+			__be32	qkey;
+			__be32	reserved;
+		} qkey;
+		__be64	dc_key;
+	} key;
+	__be32	dqp_dct;
+	u8	stat_rate_sl;
+	u8	fl_mlid;
+	union {
+		__be16	rlid;
+		__be16  udp_sport;
+	};
+	u8	reserved0[4];
+	u8	rmac[6];
+	u8	tclass;
+	u8	hop_limit;
+	__be32	grh_gid_fl;
+	u8	rgid[16];
+};
+
+struct xsc_qp_path {
+	u8			fl;
+	u8			rsvd3;
+	u8			free_ar;
+	u8			pkey_index;
+	u8			rsvd0;
+	u8			grh_mlid;
+	__be16			rlid;
+	u8			ackto_lt;
+	u8			mgid_index;
+	u8			static_rate;
+	u8			hop_limit;
+	__be32			tclass_flowlabel;
+	u8			rgid[16];
+	u8			rsvd1[4];
+	u8			sl;
+	u8			port;
+	u8			rsvd2[6];
+	u8			dmac[6];
+	u8			smac[6];
+	__be16		af_type;
+	__be32		sip[4];
+	__be32		dip[4];
+	__be16			sport;
+	u8			ecn_dscp;
+	u8			vlan_valid;
+	__be16			vlan_id;
+	u8			dci_cfi_prio_sl; /* not left moved yet. */
+};
+
+static inline struct xsc_core_qp *__xsc_qp_lookup(struct xsc_core_device *xdev, u32 qpn)
+{
+	return radix_tree_lookup(&xdev->dev_res->qp_table.tree, qpn);
+}
+
+int xsc_core_create_resource_common(struct xsc_core_device *xdev,
+				    struct xsc_core_qp *qp);
+void xsc_core_destroy_resource_common(struct xsc_core_device *xdev,
+				      struct xsc_core_qp *qp);
+void xsc_init_qp_table(struct xsc_core_device *xdev);
+void xsc_qp_event(struct xsc_core_device *xdev, u32 qpn, int event_type);
+
+int xsc_core_create_qp(struct xsc_core_device *xdev,
+		       struct xsc_core_qp *qp,
+		       struct xsc_create_qp_mbox_in *in,
+		       int inlen);
+int xsc_core_destroy_qp(struct xsc_core_device *xdev,
+			struct xsc_core_qp *qp);
+int xsc_core_qp_modify(struct xsc_core_device *xdev, enum xsc_qp_state cur_state,
+		       enum xsc_qp_state new_state,
+		       struct xsc_modify_qp_mbox_in *in, int sqd_event,
+		       struct xsc_core_qp *qp);
+int xsc_core_qp_query(struct xsc_core_device *xdev, struct xsc_core_qp *qp,
+		      struct xsc_query_qp_mbox_out *out, int outlen);
+
+#endif /* XSC_QP_H */
diff --git a/drivers/net/ethernet/yunsilicon/xsc/common/srq.h b/drivers/net/ethernet/yunsilicon/xsc/common/srq.h
new file mode 100644
index 000000000..0a2e660e1
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/common/srq.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#ifndef XSC_SRQ_H
+#define XSC_SRQ_H
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "common/xsc_core.h"
+
+struct ib_qp;
+struct ib_recv_wr;
+
+#define XSC_MAX_SRQ_NUM 8192
+#define XSC_MAX_SRQ_LOG_SIZE 13
+
+struct xsc_srq_qp_node {
+	struct list_head	entry;
+	u32			qp_id;
+	struct ib_qp		*ibqp;
+};
+
+struct xsc_srq_cache_wr {
+	int wr_num;
+	int max_wr_num;
+	struct ib_recv_wr *head_wr;
+	struct ib_recv_wr *tail_wr;
+};
+
+struct xsc_core_srq {
+	u32 srqn;
+	int max;
+	size_t max_gs;
+	size_t max_avail_gather;
+	int wqe_shift;
+	void (*event)(struct xsc_core_srq *srq, enum xsc_event e);
+
+	/* protect ctrl list */
+	spinlock_t		lock;
+	struct list_head	ctrl_list;
+	u16 uid;
+
+	struct list_head	srq_qp_list;
+	struct xsc_srq_cache_wr *cache_wr;
+};
+
+int xsc_core_create_srq(struct xsc_core_device *xdev, struct xsc_core_srq *srq);
+int xsc_core_destroy_srq(struct xsc_core_device *xdev, struct xsc_core_srq *srq);
+
+#endif /* XSC_SRQ_H */
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/Makefile b/drivers/net/ethernet/yunsilicon/xsc/pci/Makefile
index b01fca3ac..87481db3f 100644
--- a/drivers/net/ethernet/yunsilicon/xsc/pci/Makefile
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/Makefile
@@ -6,4 +6,4 @@ ccflags-y += -I$(srctree)/drivers/net/ethernet/yunsilicon/xsc
 
 obj-$(CONFIG_YUNSILICON_XSC_PCI) += xsc_pci.o
 
-xsc_pci-y := main.o cmdq.o hw.o qp.o cq.o alloc.o eq.o pci_irq.o adev.o vport.o hal/andes_impl.o
+xsc_pci-y := main.o cmdq.o hw.o qp.o cq.o link.o alloc.o eq.o pci_irq.o adev.o vport.o hal/andes_impl.o
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/link.c b/drivers/net/ethernet/yunsilicon/xsc/pci/link.c
new file mode 100644
index 000000000..b991eddae
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/link.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "common/xsc_core.h"
+#include "common/xsc_cmd_api.h"
+
+int xsc_get_link_speed(struct xsc_core_device *xdev)
+{
+	struct xsc_event_query_linkinfo_mbox_out out;
+	struct xsc_event_linkinfo linkinfo;
+	struct xsc_inbox_hdr in;
+	int err;
+
+	memset(&in, 0, sizeof(in));
+	memset(&out, 0, sizeof(out));
+	in.opcode = cpu_to_be16(XSC_CMD_OP_QUERY_LINK_INFO);
+
+	err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
+	if (err || out.hdr.status) {
+		pci_err(xdev->pdev, "failed to get link info, err=%d, status=%d\n",
+			err, out.hdr.status);
+		return MODULE_SPEED_25G;
+	}
+
+	linkinfo = out.ctx;
+	linkinfo.linkspeed = be32_to_cpu(linkinfo.linkspeed);
+
+	return linkinfo.linkspeed;
+}
+EXPORT_SYMBOL_GPL(xsc_get_link_speed);
diff --git a/include/uapi/rdma/ib_user_ioctl_verbs.h b/include/uapi/rdma/ib_user_ioctl_verbs.h
index 21f86cc7b..53fc99057 100644
--- a/include/uapi/rdma/ib_user_ioctl_verbs.h
+++ b/include/uapi/rdma/ib_user_ioctl_verbs.h
@@ -257,6 +257,7 @@ enum rdma_driver_id {
 	RDMA_DRIVER_ERDMA,
 	RDMA_DRIVER_MANA,
 	RDMA_DRIVER_IONIC,
+	RDMA_DRIVER_XSC,
 };
 
 enum ib_uverbs_gid_type {
diff --git a/include/uapi/rdma/xsc-abi.h b/include/uapi/rdma/xsc-abi.h
new file mode 100644
index 000000000..c4d0db30e
--- /dev/null
+++ b/include/uapi/rdma/xsc-abi.h
@@ -0,0 +1,295 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * Copyright (c) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#ifndef __XSC_ABI_H__
+#define __XSC_ABI_H__
+
+#include <linux/types.h>
+#include <linux/if_ether.h>	/* For ETH_ALEN. */
+
+/* Increment this value if any changes that break userspace ABI
+ * compatibility are made.
+ */
+#define XSC_IB_UVERBS_ABI_VERSION	1
+
+/* Make sure that all structs defined in this file remain laid out so
+ * that they pack the same way on 32-bit and 64-bit architectures (to
+ * avoid incompatibility between 32-bit userspace and 64-bit kernels).
+ * In particular do not use pointer types -- pass pointers in __u64
+ * instead.
+ */
+
+enum {
+	XSC_QP_FLAG_SIGNATURE		= 1 << 0,
+	XSC_QP_FLAG_SCATTER_CQE	= 1 << 1,
+	XSC_QP_FLAG_TUNNEL_OFFLOADS	= 1 << 2,
+	XSC_QP_FLAG_BFREG_INDEX	= 1 << 3,
+	XSC_QP_FLAG_TYPE_DCT		= 1 << 4,
+	XSC_QP_FLAG_TYPE_DCI		= 1 << 5,
+	XSC_QP_FLAG_TIR_ALLOW_SELF_LB_UC = 1 << 6,
+	XSC_QP_FLAG_TIR_ALLOW_SELF_LB_MC = 1 << 7,
+	XSC_QP_FLAG_ALLOW_SCATTER_CQE	= 1 << 8,
+	XSC_QP_FLAG_RAWPACKET_TSO       = 1 << 9,
+	XSC_QP_FLAG_RAWPACKET_TX	= 1 << 10,
+	XSC_QP_FLAG_RAWPACKET_SNIFFER	= 1 << 11,
+	XSC_QP_FLAG_WORC		= 1 << 12,
+};
+
+#define ALLOC_UCONTEXT_VER_V1 1
+#define ALLOC_UCONTEXT_VER_V2 2
+
+#define CURRENT_UCONTEXT_ABI_VER	ALLOC_UCONTEXT_VER_V2
+
+struct xsc_ib_alloc_ucontext_req {
+	__u32	abi_ver;
+	__u32	rsvd1;
+};
+
+enum xsc_user_cmds_supp_uhw {
+	XSC_USER_CMDS_SUPP_UHW_QUERY_DEVICE = 1 << 0,
+	XSC_USER_CMDS_SUPP_UHW_CREATE_AH    = 1 << 1,
+};
+
+enum {
+	XSC_HW_FEATURE_FLAG_SUPPORT_CQE64 = 1 << 0,
+};
+
+struct xsc_ib_alloc_ucontext_resp {
+	__u32	qp_tab_size;
+	__u32	cache_line_size;
+	__u16	max_sq_desc_sz;
+	__u16	max_rq_desc_sz;
+	__u32	max_send_wqebb;
+	__u32	max_recv_wr;
+	__u16	num_ports;
+	__u16	device_id;
+	__u64	qpm_tx_db;
+	__u64	qpm_rx_db;
+	__u64	cqm_next_cid_reg;
+	__u64	cqm_armdb;
+	__u32	send_ds_num;
+	__u32	recv_ds_num;
+	__u32   cmds_supp_uhw;
+	__u32	pad;
+	__u32	multidb_num;
+	__u32	tx_multidb_base;
+	__u32	rdma_proto_mode;
+	__u32	hw_feature_flag;
+	__u32	reserved[32];
+};
+
+struct xsc_ib_create_qp {
+	__u64	buf_addr;
+	__u64	db_addr;
+	__u32	sq_wqe_count;
+	__u32	rq_wqe_count;
+	__u32	rq_wqe_shift;
+	__u32	flags;
+
+	int	dmabuf_fd;
+	size_t	dmabuf_sz;
+};
+
+struct xsc_ib_create_qp_resp {
+	__u32	uuar_index;
+	__u32 reserved;
+};
+
+struct xsc_ib_create_srq {
+	__u64	buf_addr;
+	__u64	db_addr;
+	__u32	flags;
+	__u32	reserved0;
+	__u32	uidx;
+	__u32	reserved1;
+};
+
+struct xsc_ib_create_srq_resp {
+	__u32	srqn;
+	__u32	reserved;
+};
+
+struct xsc_ib_create_cq {
+	__u64 buf_addr;
+	__u64 db_addr;
+	__u32	cqe_size;
+
+	int	dmabuf_fd;
+	size_t	dmabuf_sz;
+};
+
+struct xsc_ib_create_cq_resp {
+	__u32	cqn;
+	__u32	reserved;
+};
+
+struct xsc_ib_create_ah_resp {
+	__u32	response_length;
+	__u8	dmac[ETH_ALEN];
+	__u8	reserved[6];
+};
+
+struct xsc_ib_alloc_pd_resp {
+	__u32	pdn;
+};
+
+struct xsc_ib_tso_caps {
+	__u32 max_tso; /* Maximum tso payload size in bytes */
+
+	/* Corresponding bit will be set if qp type from
+	 * 'enum ib_qp_type' is supported, e.g.
+	 * supported_qpts |= 1 << IB_QPT_UD
+	 */
+	__u32 supported_qpts;
+};
+
+/* RX Hash function flags */
+enum xsc_rx_hash_function_flags {
+	XSC_RX_HASH_FUNC_TOEPLITZ	= 1 << 0,
+};
+
+enum xsc_rdma_link_speed {
+	XSC_RDMA_LINK_SPEED_2_5GB	= 1 << 0,
+	XSC_RDMA_LINK_SPEED_5GB		= 1 << 1,
+	XSC_RDMA_LINK_SPEED_10GB	= 1 << 3,
+	XSC_RDMA_LINK_SPEED_14GB	= 1 << 4,
+	XSC_RDMA_LINK_SPEED_25GB	= 1 << 5,
+	XSC_RDMA_LINK_SPEED_50GB	= 1 << 6,
+	XSC_RDMA_LINK_SPEED_100GB	= 1 << 7,
+};
+
+enum xsc_rdma_phys_state {
+	XSC_RDMA_PHY_STATE_SLEEP	= 1,
+	XSC_RDMA_PHY_STATE_POLLING,
+	XSC_RDMA_PHY_STATE_DISABLED,
+	XSC_RDMA_PHY_STATE_PORT_CONFIGURATION_TRAINNING,
+	XSC_RDMA_PHY_STATE_LINK_UP,
+	XSC_RDMA_PHY_STATE_LINK_ERROR_RECOVERY,
+	XSC_RDMA_PHY_STATE_PHY_TEST,
+};
+
+/*
+ * RX Hash flags, these flags allows to set which incoming packet's field should
+ * participates in RX Hash. Each flag represent certain packet's field,
+ * when the flag is set the field that is represented by the flag will
+ * participate in RX Hash calculation.
+ * Note: *IPV4 and *IPV6 flags can't be enabled together on the same QP
+ * and *TCP and *UDP flags can't be enabled together on the same QP.
+ */
+enum xsc_rx_hash_fields {
+	XSC_RX_HASH_SRC_IPV4	= 1 << 0,
+	XSC_RX_HASH_DST_IPV4	= 1 << 1,
+	XSC_RX_HASH_SRC_IPV6	= 1 << 2,
+	XSC_RX_HASH_DST_IPV6	= 1 << 3,
+	XSC_RX_HASH_SRC_PORT_TCP	= 1 << 4,
+	XSC_RX_HASH_DST_PORT_TCP	= 1 << 5,
+	XSC_RX_HASH_SRC_PORT_UDP	= 1 << 6,
+	XSC_RX_HASH_DST_PORT_UDP	= 1 << 7,
+	XSC_RX_HASH_IPSEC_SPI		= 1 << 8,
+	/* Save bits for future fields */
+	XSC_RX_HASH_INNER		= (1UL << 31),
+};
+
+struct xsc_ib_rss_caps {
+	__aligned_u64 rx_hash_fields_mask; /* enum xsc_rx_hash_fields */
+	__u8 rx_hash_function; /* enum xsc_rx_hash_function_flags */
+	__u8 reserved[7];
+};
+
+enum xsc_ib_cqe_comp_res_format {
+	XSC_IB_CQE_RES_FORMAT_HASH	= 1 << 0,
+	XSC_IB_CQE_RES_FORMAT_CSUM	= 1 << 1,
+	XSC_IB_CQE_RES_FORMAT_CSUM_STRIDX = 1 << 2,
+};
+
+struct xsc_ib_cqe_comp_caps {
+	__u32 max_num;
+	__u32 supported_format; /* enum xsc_ib_cqe_comp_res_format */
+};
+
+enum xsc_ib_packet_pacing_cap_flags {
+	XSC_IB_PP_SUPPORT_BURST	= 1 << 0,
+};
+
+struct xsc_packet_pacing_caps {
+	__u32 qp_rate_limit_min;
+	__u32 qp_rate_limit_max; /* In kpbs */
+
+	/* Corresponding bit will be set if qp type from
+	 * 'enum ib_qp_type' is supported, e.g.
+	 * supported_qpts |= 1 << IB_QPT_RAW_PACKET
+	 */
+	__u32 supported_qpts;
+	__u8  cap_flags; /* enum xsc_ib_packet_pacing_cap_flags */
+	__u8  reserved[3];
+};
+
+enum xsc_ib_mpw_caps {
+	MPW_RESERVED		= 1 << 0,
+	XSC_IB_ALLOW_MPW	= 1 << 1,
+	XSC_IB_SUPPORT_EMPW	= 1 << 2,
+};
+
+enum xsc_ib_sw_parsing_offloads {
+	XSC_IB_SW_PARSING = 1 << 0,
+	XSC_IB_SW_PARSING_CSUM = 1 << 1,
+	XSC_IB_SW_PARSING_LSO = 1 << 2,
+};
+
+struct xsc_ib_sw_parsing_caps {
+	__u32 sw_parsing_offloads; /* enum xsc_ib_sw_parsing_offloads */
+
+	/* Corresponding bit will be set if qp type from
+	 * 'enum ib_qp_type' is supported, e.g.
+	 * supported_qpts |= 1 << IB_QPT_RAW_PACKET
+	 */
+	__u32 supported_qpts;
+};
+
+struct xsc_ib_striding_rq_caps {
+	__u32 min_single_stride_log_num_of_bytes;
+	__u32 max_single_stride_log_num_of_bytes;
+	__u32 min_single_wqe_log_num_of_strides;
+	__u32 max_single_wqe_log_num_of_strides;
+
+	/* Corresponding bit will be set if qp type from
+	 * 'enum ib_qp_type' is supported, e.g.
+	 * supported_qpts |= 1 << IB_QPT_RAW_PACKET
+	 */
+	__u32 supported_qpts;
+	__u32 reserved;
+};
+
+enum xsc_ib_query_dev_resp_flags {
+	/* Support 128B CQE compression */
+	XSC_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_COMP = 1 << 0,
+	XSC_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_PAD  = 1 << 1,
+};
+
+enum xsc_ib_tunnel_offloads {
+	XSC_IB_TUNNELED_OFFLOADS_VXLAN  = 1 << 0,
+	XSC_IB_TUNNELED_OFFLOADS_GRE    = 1 << 1,
+	XSC_IB_TUNNELED_OFFLOADS_GENEVE = 1 << 2,
+	XSC_IB_TUNNELED_OFFLOADS_MPLS_GRE = 1 << 3,
+	XSC_IB_TUNNELED_OFFLOADS_MPLS_UDP = 1 << 4,
+};
+
+struct xsc_ib_query_device_resp {
+	__u32	comp_mask;
+	__u32	response_length;
+	struct	xsc_ib_tso_caps tso_caps;
+	struct	xsc_ib_rss_caps rss_caps;
+	struct	xsc_ib_cqe_comp_caps cqe_comp_caps;
+	struct	xsc_packet_pacing_caps packet_pacing_caps;
+	__u32	xsc_ib_support_multi_pkt_send_wqes;
+	__u32	flags; /* Use enum xsc_ib_query_dev_resp_flags */
+	struct xsc_ib_sw_parsing_caps sw_parsing_caps;
+	struct xsc_ib_striding_rq_caps striding_rq_caps;
+	__u32	tunnel_offloads_caps; /* enum xsc_ib_tunnel_offloads */
+	__u32	reserved;
+};
+
+#endif /* __XSC_ABI_H__ */
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help