[PATCH 10/15] yunsilicon: add RDMA PD/MR

From: Xin Tian

Date: Mon Oct 12 2026 - 07:54:30 EST


Add protection domains and memory regions: the PD allocation verbs, the
kernel and user MR registration paths (including dmabuf), and the DMA
address translation helpers they build on.

Signed-off-by: Xin Tian <tianx@xxxxxxxxxxxxxx>
---
drivers/infiniband/hw/xsc/Makefile | 2 +-
drivers/infiniband/hw/xsc/main.c | 44 +++
drivers/infiniband/hw/xsc/mem.c | 161 +++++++++
drivers/infiniband/hw/xsc/mr.c | 313 ++++++++++++++++++
.../net/ethernet/yunsilicon/xsc/pci/Makefile | 2 +-
drivers/net/ethernet/yunsilicon/xsc/pci/mr.c | 212 ++++++++++++
drivers/net/ethernet/yunsilicon/xsc/pci/mr.h | 18 +
drivers/net/ethernet/yunsilicon/xsc/pci/pd.c | 55 +++
drivers/net/ethernet/yunsilicon/xsc/pci/pd.h | 14 +
9 files changed, 819 insertions(+), 2 deletions(-)
create mode 100644 drivers/infiniband/hw/xsc/mem.c
create mode 100644 drivers/infiniband/hw/xsc/mr.c
create mode 100644 drivers/net/ethernet/yunsilicon/xsc/pci/mr.c
create mode 100644 drivers/net/ethernet/yunsilicon/xsc/pci/mr.h
create mode 100644 drivers/net/ethernet/yunsilicon/xsc/pci/pd.c
create mode 100644 drivers/net/ethernet/yunsilicon/xsc/pci/pd.h

diff --git a/drivers/infiniband/hw/xsc/Makefile b/drivers/infiniband/hw/xsc/Makefile
index 6c4ec1ca0..b535a6653 100644
--- a/drivers/infiniband/hw/xsc/Makefile
+++ b/drivers/infiniband/hw/xsc/Makefile
@@ -6,4 +6,4 @@ ccflags-y := -I$(srctree)/drivers/net/ethernet/yunsilicon/xsc

obj-$(CONFIG_INFINIBAND_XSC) += xsc_ib.o

-xsc_ib-y := main.o
+xsc_ib-y := main.o mem.o mr.o
diff --git a/drivers/infiniband/hw/xsc/main.c b/drivers/infiniband/hw/xsc/main.c
index 9d53a89be..bd2c23b51 100644
--- a/drivers/infiniband/hw/xsc/main.c
+++ b/drivers/infiniband/hw/xsc/main.c
@@ -382,6 +382,42 @@ static int xsc_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma
reg_size, vma->vm_page_prot);
}

+int xsc_ib_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
+{
+ struct ib_device *ibdev = ibpd->device;
+ struct xsc_ib_alloc_pd_resp resp;
+ struct xsc_ib_pd *pd;
+ int err;
+
+ pd = to_xpd(ibpd);
+
+ err = xsc_core_alloc_pd(to_xdev(ibdev)->xdev, &pd->pdn);
+ if (err)
+ return err;
+
+ if (udata) {
+ resp.pdn = pd->pdn;
+ if (ib_copy_to_udata(udata, &resp, sizeof(resp))) {
+ xsc_core_dealloc_pd(to_xdev(ibdev)->xdev, pd->pdn);
+ return -EFAULT;
+ }
+ } else {
+ pd->pa_lkey = 0;
+ }
+
+ return 0;
+}
+
+int xsc_ib_dealloc_pd(struct ib_pd *pd, struct ib_udata *udata)
+{
+ struct xsc_ib_dev *dev = to_xdev(pd->device);
+ struct xsc_ib_pd *xpd = to_xpd(pd);
+
+ xsc_core_dealloc_pd(dev->xdev, xpd->pdn);
+
+ return 0;
+}
+
static int xsc_port_immutable(struct ib_device *ibdev, u32 port_num,
struct ib_port_immutable *immutable)
{
@@ -525,16 +561,23 @@ static void xsc_ib_dev_setting(struct xsc_ib_dev *dev)
dev->ib_dev.ops.dealloc_ucontext = xsc_ib_dealloc_ucontext;
dev->ib_dev.ops.mmap = xsc_ib_mmap;

+ dev->ib_dev.ops.alloc_pd = xsc_ib_alloc_pd;
+ dev->ib_dev.ops.dealloc_pd = xsc_ib_dealloc_pd;

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_dma_mr = xsc_ib_get_dma_mr;
+ dev->ib_dev.ops.reg_user_mr = xsc_ib_reg_user_mr; /* optional */
+ dev->ib_dev.ops.reg_user_mr_dmabuf = xsc_ib_reg_user_mr_dmabuf;
+ dev->ib_dev.ops.dereg_mr = xsc_ib_dereg_mr;

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);
+ dev->ib_dev.ops INIT_RDMA_OBJ_SIZE(ib_pd, xsc_ib_pd, ibpd);
}

static void xsc_get_port_state(struct net_device *ndev, enum xsc_dev_event *ev)
@@ -647,6 +690,7 @@ static int init_one(struct xsc_core_device *xdev,



+ xsc_reg_local_dma_mr(xdev);

err = ib_register_device(&dev->ib_dev, dev->ib_dev.name,
dev->xdev->device);
diff --git a/drivers/infiniband/hw/xsc/mem.c b/drivers/infiniband/hw/xsc/mem.c
new file mode 100644
index 000000000..2fbce9943
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/mem.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <rdma/ib_umem.h>
+#include "xsc_ib.h"
+
+/**
+ * __xsc_ib_cont_pages - scan a umem for compound pages
+ * @umem: umem object to scan
+ * @addr: ib virtual address requested by the user
+ * @max_page_shift: upper limit for the detected page shift, 0 for none
+ * @count: number of PAGE_SIZE pages covered by umem
+ * @shift: page shift for the compound pages found in the region
+ * @ncont: number of compound pages
+ */
+static void __xsc_ib_cont_pages(struct ib_umem *umem, u64 addr,
+ unsigned long max_page_shift,
+ int *count, int *shift, int *ncont)
+{
+ unsigned long tmp;
+ unsigned long m;
+ u64 base = ~0, p = 0;
+ u64 len, pfn;
+ int i = 0;
+ struct scatterlist *sg;
+ int entry;
+ /* TODO: need peer mem support */
+ unsigned long page_shift = PAGE_SHIFT;
+
+ addr = addr >> page_shift;
+ tmp = (unsigned long)addr;
+ m = find_first_bit(&tmp, BITS_PER_LONG);
+ if (max_page_shift)
+ m = min_t(unsigned long, max_page_shift - page_shift, m);
+ for_each_sgtable_dma_sg(&umem->sgt_append.sgt, sg, entry) {
+ len = sg_dma_len(sg) >> page_shift;
+ pfn = sg_dma_address(sg) >> page_shift;
+ if (base + p != pfn) {
+ /* If either the offset or the new
+ * base are unaligned update m
+ */
+ tmp = (unsigned long)(pfn | p);
+ if (!IS_ALIGNED(tmp, 1 << m))
+ m = find_first_bit(&tmp, BITS_PER_LONG);
+
+ base = pfn;
+ p = 0;
+ }
+
+ p += len;
+ i += len;
+ }
+
+ if (i) {
+ m = min_t(unsigned long, ilog2(roundup_pow_of_two(i)), m);
+ *ncont = DIV_ROUND_UP(i, (1 << m));
+ } else {
+ m = 0;
+ *ncont = 0;
+ }
+ *shift = page_shift + m;
+ *count = i;
+}
+
+void xsc_ib_cont_pages(struct ib_umem *umem, u64 addr,
+ int *count, int *shift, int *ncont)
+{
+ /* no limit for page_shift */
+ __xsc_ib_cont_pages(umem, addr, 0, count, shift, ncont);
+}
+
+static void __xsc_ib_populate_pas(struct xsc_ib_dev *dev, struct ib_umem *umem,
+ int page_shift, size_t offset, size_t num_pages,
+ __be64 *pas, int access_flags, bool need_to_devide)
+{
+ unsigned long umem_page_shift = PAGE_SHIFT;
+ int shift = page_shift - umem_page_shift;
+ int mask = (1 << shift) - 1;
+ int i = 0;
+ int k, idx;
+ u64 cur = 0;
+ u64 base;
+ int len;
+ struct scatterlist *sg;
+ int entry;
+
+ for_each_sgtable_dma_sg(&umem->sgt_append.sgt, sg, entry) {
+ len = sg_dma_len(sg) >> umem_page_shift;
+ if (need_to_devide)
+ len = sg_dma_len(sg) >> PAGE_SHIFT_4K;
+ else
+ len = sg_dma_len(sg) >> umem_page_shift;
+ base = sg_dma_address(sg);
+
+ /* Skip elements below offset */
+ if (i + len < offset << shift) {
+ i += len;
+ continue;
+ }
+
+ /* Skip pages below offset */
+ if (i < offset << shift) {
+ k = (offset << shift) - i;
+ i = offset << shift;
+ } else {
+ k = 0;
+ }
+
+ for (; k < len; k++) {
+ if (!(i & mask)) {
+ if (need_to_devide)
+ cur = base + (k << PAGE_SHIFT_4K);
+ else
+ cur = base + (k << umem_page_shift);
+ cur |= access_flags;
+ idx = (i >> shift) - offset;
+
+ pas[idx] = cpu_to_be64(cur);
+ ibdev_dbg(&dev->ib_dev, "pas[%d] 0x%llx\n",
+ i >> shift, be64_to_cpu(pas[idx]));
+ }
+ i++;
+
+ /* Stop after num_pages reached */
+ if (i >> shift >= offset + num_pages)
+ return;
+ }
+ }
+}
+
+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)
+{
+ return __xsc_ib_populate_pas(dev, umem, page_shift, 0,
+ npages, pas, 0, need_to_devide);
+}
+
+int xsc_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset)
+{
+ u64 page_size;
+ u64 page_mask;
+ u64 off_size;
+ u64 off_mask;
+ u64 buf_off;
+
+ page_size = 1 << page_shift;
+ page_mask = page_size - 1;
+ buf_off = addr & page_mask;
+ off_size = page_size >> 6;
+ off_mask = off_size - 1;
+
+ if (buf_off & off_mask)
+ return -EINVAL;
+
+ *offset = buf_off >> ilog2(off_size);
+ return 0;
+}
diff --git a/drivers/infiniband/hw/xsc/mr.c b/drivers/infiniband/hw/xsc/mr.c
new file mode 100644
index 000000000..efd685e57
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/mr.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#include <linux/kref.h>
+#include <linux/random.h>
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-resv.h>
+#include <rdma/ib_umem.h>
+#include <rdma/iter.h>
+#include "common/xsc_cmd.h"
+#include <linux/dma-direct.h>
+#include "xsc_ib.h"
+
+enum {
+ DEF_CACHE_SIZE = 10,
+};
+
+struct ib_mr *xsc_ib_get_dma_mr(struct ib_pd *pd, int acc)
+{
+ struct xsc_ib_dev *dev = to_xdev(pd->device);
+ struct xsc_core_device *xdev = dev->xdev;
+ struct xsc_register_mr_mbox_in *in;
+ struct xsc_register_mr_request *req;
+ struct xsc_ib_mr *mr;
+ int err;
+
+ mr = kzalloc_obj(*mr, GFP_KERNEL);
+ if (!mr)
+ return ERR_PTR(-ENOMEM);
+
+ in = kzalloc_obj(*in, GFP_KERNEL);
+ if (!in) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ req = &in->req;
+ req->acc = convert_access(acc);
+ req->va_base = 0;
+ req->map_en = !(XSC_MPT_MAP_EN);
+
+ err = xsc_core_create_mkey(xdev, &mr->xmr);
+ if (err)
+ goto err_in;
+ req->mkey = cpu_to_be32(mr->xmr.key);
+ err = xsc_core_register_mr(xdev, &mr->xmr, in, sizeof(*in));
+ if (err)
+ goto err_reg_mr;
+ kfree(in);
+ mr->ibmr.lkey = mr->xmr.key;
+ mr->ibmr.rkey = mr->xmr.key;
+ mr->umem = NULL;
+
+ return &mr->ibmr;
+err_reg_mr:
+ xsc_core_destroy_mkey(xdev, &mr->xmr);
+err_in:
+ kfree(in);
+
+err_free:
+ kfree(mr);
+
+ return ERR_PTR(err);
+}
+
+static struct xsc_ib_mr *reg_create(struct ib_pd *pd, u64 virt_addr,
+ u64 length, struct ib_umem *umem,
+ int npages, int page_shift,
+ int access_flags)
+{
+ struct xsc_ib_dev *dev = to_xdev(pd->device);
+ struct xsc_register_mr_mbox_in *in;
+ struct xsc_ib_mr *mr;
+ struct ib_block_iter biter;
+ __be64 *pas;
+ int inlen;
+ int err;
+
+ mr = kzalloc_obj(*mr, GFP_KERNEL);
+ if (!mr) {
+ err = -ENOMEM;
+ goto err_0;
+ }
+
+ inlen = sizeof(*in) + sizeof(*in->req.pas) * npages;
+ in = xsc_vzalloc(inlen);
+ if (!in) {
+ err = -ENOMEM;
+ goto err_1;
+ }
+ err = xsc_core_create_mkey(dev->xdev, &mr->xmr);
+ if (err) {
+ ibdev_warn(&dev->ib_dev, "create mkey failed\n");
+ goto err_2;
+ }
+
+ pas = in->req.pas;
+ rdma_umem_for_each_dma_block(umem, &biter, 1UL << page_shift) {
+ *pas = cpu_to_be64(rdma_block_iter_dma_address(&biter));
+ pas++;
+ }
+
+ in->req.acc = convert_access(access_flags);
+ in->req.pa_num = cpu_to_be32(npages);
+ in->req.pdn = cpu_to_be32(to_xpd(pd)->pdn);
+ in->req.va_base = cpu_to_be64(virt_addr);
+ in->req.map_en = XSC_MPT_MAP_EN;
+ in->req.len = cpu_to_be64(length);
+ in->req.page_mode = xsc_get_mr_page_mode(dev->xdev, page_shift);
+ ibdev_dbg(&dev->ib_dev, "read_flush hwconfig %s\n",
+ dev->xdev->read_flush ? "enable" : "disable");
+ in->req.is_gpu = 0;
+ in->req.mkey = cpu_to_be32(mr->xmr.key);
+ err = xsc_core_register_mr(dev->xdev, &mr->xmr, in, inlen);
+ if (err) {
+ ibdev_warn(&dev->ib_dev, "register mr failed, err = %d\n", err);
+ goto err_reg_mr;
+ }
+ mr->umem = umem;
+ xsc_vfree(in);
+
+ ibdev_dbg(&dev->ib_dev, "mkey = 0x%x\n", mr->xmr.key);
+
+ return mr;
+err_reg_mr:
+ xsc_core_destroy_mkey(dev->xdev, &mr->xmr);
+err_2:
+ xsc_vfree(in);
+err_1:
+ kfree(mr);
+err_0:
+ return ERR_PTR(err);
+}
+
+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)
+{
+ struct xsc_ib_dev *dev = to_xdev(pd->device);
+ struct xsc_ib_mr *mr = NULL;
+ struct ib_umem *umem;
+ unsigned long pgsz;
+ int page_shift;
+ int npages;
+ int err;
+
+ if (dmah)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (length > dev->xdev->caps.max_mr_size) {
+ ibdev_err(&dev->ib_dev, "reg user mr length(%llu) exceeded.\n", length);
+ return ERR_PTR(-EINVAL);
+ }
+
+ ibdev_dbg(&dev->ib_dev, "start 0x%llx, virt_addr 0x%llx, length 0x%llx\n",
+ start, virt_addr, length);
+
+ umem = ib_umem_get_va(&dev->ib_dev, start, length, access_flags);
+ if (IS_ERR(umem)) {
+ ibdev_dbg(&dev->ib_dev, "umem get failed\n");
+ return (void *)umem;
+ }
+
+ pgsz = ib_umem_find_best_pgsz(umem, XSC_MR_PAGE_CAP_MASK, virt_addr);
+ if (!pgsz) {
+ ibdev_warn(&dev->ib_dev, "find best page size failed\n");
+ err = -EINVAL;
+ goto error;
+ }
+
+ page_shift = order_base_2(pgsz);
+ npages = ib_umem_num_dma_blocks(umem, pgsz);
+
+ ibdev_dbg(&dev->ib_dev, "npages %d, page_shift %d\n", npages, page_shift);
+
+ mr = reg_create(pd, virt_addr, length, umem, npages, page_shift,
+ access_flags);
+ if (IS_ERR(mr)) {
+ err = PTR_ERR(mr);
+ goto error;
+ }
+
+ ibdev_dbg(&dev->ib_dev, "mkey 0x%x\n", mr->xmr.key);
+
+ mr->umem = umem;
+ mr->npages = npages;
+ mr->ibmr.lkey = mr->xmr.key;
+ mr->ibmr.rkey = mr->xmr.key;
+ mr->ibmr.length = length;
+ return &mr->ibmr;
+
+error:
+ ib_umem_release(umem);
+ return ERR_PTR(err);
+}
+
+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)
+{
+ struct xsc_ib_dev *dev = to_xdev(pd->device);
+ struct ib_umem_dmabuf *umem_dmabuf;
+ struct ib_umem *umem;
+ struct xsc_ib_mr *mr;
+ unsigned long pgsz;
+ int page_shift;
+ int npages;
+ int err;
+
+ if (!IS_ENABLED(CONFIG_INFINIBAND_USER_MEM))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (dmah)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ umem_dmabuf = ib_umem_dmabuf_get_pinned(&dev->ib_dev,
+ offset, length,
+ fd, access_flags);
+ if (IS_ERR(umem_dmabuf)) {
+ ibdev_dbg(&dev->ib_dev, "umem_dmabuf get failed (%ld)\n",
+ PTR_ERR(umem_dmabuf));
+ return ERR_CAST(umem_dmabuf);
+ }
+ umem = &umem_dmabuf->umem;
+
+ pgsz = ib_umem_find_best_pgsz(umem, XSC_MR_PAGE_CAP_MASK, virt_addr);
+ if (!pgsz) {
+ ibdev_warn(&dev->ib_dev, "find best page size failed\n");
+ err = -EINVAL;
+ goto err_release;
+ }
+
+ page_shift = order_base_2(pgsz);
+ npages = ib_umem_num_dma_blocks(umem, pgsz);
+
+ mr = reg_create(pd, virt_addr, length, umem, npages, page_shift,
+ access_flags);
+ if (IS_ERR(mr)) {
+ err = PTR_ERR(mr);
+ goto err_release;
+ }
+
+ mr->umem = umem;
+ mr->npages = npages;
+ mr->ibmr.lkey = mr->xmr.key;
+ mr->ibmr.rkey = mr->xmr.key;
+ mr->ibmr.length = length;
+
+ return &mr->ibmr;
+err_release:
+ ib_umem_release(umem);
+ return ERR_PTR(err);
+}
+
+int xsc_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata)
+{
+ struct xsc_ib_dev *dev = to_xdev(ibmr->device);
+ struct xsc_ib_mr *mr = to_xmr(ibmr);
+ struct ib_umem *umem = mr->umem;
+ int err;
+
+ ibdev_dbg(&dev->ib_dev, "dereg mkey = 0x%x\n", mr->xmr.key);
+
+ if (mr->npages) {
+ err = xsc_core_dereg_mr(dev->xdev, &mr->xmr);
+ if (err) {
+ ibdev_warn(&dev->ib_dev, "failed to dereg mr 0x%x (%d)\n",
+ mr->xmr.key, err);
+ return err;
+ }
+ }
+ err = xsc_core_destroy_mkey(dev->xdev, &mr->xmr);
+ if (err) {
+ ibdev_warn(&dev->ib_dev, "failed to destroy mkey 0x%x (%d)\n",
+ mr->xmr.key, err);
+ return err;
+ }
+
+ if (umem)
+ ib_umem_release(umem);
+
+ kfree(mr);
+
+ return 0;
+}
+
+void xsc_reg_local_dma_mr(struct xsc_core_device *dev)
+{
+ struct xsc_register_mr_mbox_in in;
+ int err = 0;
+
+ in.req.pdn = 0;
+ in.req.pa_num = 0;
+ in.req.len = 0;
+ in.req.mkey = cpu_to_be32(0xFF);
+ in.req.acc = XSC_PERM_LOCAL_WRITE | XSC_PERM_LOCAL_READ;
+ in.req.page_mode = xsc_get_mr_page_mode(dev, PAGE_SHIFT_4K);
+ in.req.is_gpu = 0;
+ in.req.map_en = !(XSC_MPT_MAP_EN);
+ in.req.va_base = 0;
+
+ err = xsc_core_register_mr(dev, NULL, &in, sizeof(in));
+ if (err)
+ dev_err(&dev->pdev->dev, "\n");
+}
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/Makefile b/drivers/net/ethernet/yunsilicon/xsc/pci/Makefile
index 87481db3f..065d52ebf 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 link.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 pd.o mr.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/mr.c b/drivers/net/ethernet/yunsilicon/xsc/pci/mr.c
new file mode 100644
index 000000000..5c68f0fd8
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/mr.c
@@ -0,0 +1,212 @@
+// 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 <linux/slab.h>
+
+#include "common/xsc_core.h"
+#include "common/xsc_cmd_api.h"
+#include "hal/xsc_hal.h"
+#include "mr.h"
+
+int xsc_core_create_mkey(struct xsc_core_device *xdev, struct xsc_core_mr *mr)
+{
+ struct xsc_create_mkey_mbox_in in;
+ struct xsc_create_mkey_mbox_out out;
+ u8 key;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+
+ spin_lock(&xdev->dev_res->mkey_lock);
+ key = 0x80 + xdev->dev_res->mkey_key++;
+ spin_unlock(&xdev->dev_res->mkey_lock);
+
+ in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_CREATE_MKEY);
+ err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
+ if (err) {
+ pci_err(xdev->pdev, "failed to create mkey, err = %d\n", err);
+ return err;
+ }
+
+ if (out.hdr.status) {
+ pci_err(xdev->pdev, "failed to create mkey, status = %d\n", out.hdr.status);
+ return xsc_cmd_status_to_err(&out.hdr);
+ }
+
+ mr->key = xsc_idx_to_mkey(xdev, be32_to_cpu(out.mkey) & 0xffffff) | key;
+ pci_dbg(xdev->pdev, "out 0x%x, key 0x%x, mkey 0x%x\n",
+ be32_to_cpu(out.mkey), key, mr->key);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xsc_core_create_mkey);
+
+int xsc_core_destroy_mkey(struct xsc_core_device *xdev, struct xsc_core_mr *mr)
+{
+ struct xsc_destroy_mkey_mbox_in in;
+ struct xsc_destroy_mkey_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+
+ in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_DESTROY_MKEY);
+ in.mkey = cpu_to_be32(mr->key);
+
+ err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return xsc_cmd_status_to_err(&out.hdr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xsc_core_destroy_mkey);
+
+static int xsc_set_mpt_via_cmdq(struct xsc_core_device *xdev,
+ struct xsc_register_mr_mbox_in *in_cmd,
+ u32 *mtt_base)
+{
+ struct xsc_register_mr_request *req = &in_cmd->req;
+ struct xsc_set_mpt_mbox_in *in;
+ struct xsc_set_mpt_mbox_out out;
+ u64 mem_size;
+ int err;
+
+ in = kzalloc_obj(*in, GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ mem_size = be64_to_cpu(req->len);
+ in->mpt_item.pdn = req->pdn;
+ in->mpt_item.pa_num = req->pa_num;
+ in->mpt_item.len = cpu_to_be32((u32)mem_size);
+ in->mpt_item.len_h = cpu_to_be32((u32)(mem_size >> 32));
+ in->mpt_item.mkey = req->mkey;
+ in->mpt_item.is_gpu = req->is_gpu;
+ in->mpt_item.acc = req->acc;
+ in->mpt_item.page_mode = req->page_mode;
+ in->mpt_item.map_en = req->map_en;
+ in->mpt_item.va_base = req->va_base;
+ in->hdr.opcode = cpu_to_be16(XSC_CMD_OP_SET_MPT);
+
+ memset(&out, 0, sizeof(out));
+ err = xsc_cmd_exec(xdev, in, sizeof(*in), &out, sizeof(out));
+ kfree(in);
+ if (err || out.hdr.status) {
+ pci_err(xdev->pdev, "failed to set mpt, err = %d, status = %d\n",
+ err, out.hdr.status);
+ return -EINVAL;
+ }
+
+ *mtt_base = be32_to_cpu(out.mtt_base);
+ return 0;
+}
+
+#define PA_NUM_PER_CMD 1024
+
+static int xsc_set_mtt_via_cmdq(struct xsc_core_device *xdev,
+ struct xsc_register_mr_mbox_in *in_cmd,
+ u32 mtt_base)
+{
+ struct xsc_register_mr_request *req = &in_cmd->req;
+ struct xsc_set_mtt_mbox_in *seg_in;
+ struct xsc_set_mtt_mbox_out seg_out;
+ int tot_pg_num = be32_to_cpu(req->pa_num);
+ int tot_seg_num, seg_pa_num, seg_idx;
+ int pa_idx_base = 0;
+ int in_len, err, i;
+
+ tot_seg_num = (tot_pg_num & (PA_NUM_PER_CMD - 1)) ?
+ ((tot_pg_num >> 10) + 1) : (tot_pg_num >> 10);
+
+ for (seg_idx = 0; seg_idx < tot_seg_num; seg_idx++) {
+ seg_pa_num = (seg_idx != tot_seg_num - 1) ? PA_NUM_PER_CMD :
+ (tot_pg_num - ((tot_seg_num - 1) << 10));
+ in_len = (seg_pa_num << 3) + sizeof(*seg_in);
+ seg_in = kzalloc(in_len, GFP_KERNEL);
+ if (!seg_in)
+ return -ENOMEM;
+
+ seg_in->mtt_setting.mtt_base = cpu_to_be32(mtt_base);
+ seg_in->mtt_setting.pa_num = cpu_to_be32(seg_pa_num);
+ for (i = 0; i < seg_pa_num; i++)
+ seg_in->mtt_setting.pas[i] = req->pas[pa_idx_base + i];
+ seg_in->hdr.opcode = cpu_to_be16(XSC_CMD_OP_SET_MTT);
+
+ memset(&seg_out, 0, sizeof(seg_out));
+ pci_dbg(xdev->pdev,
+ "set mtt seg %d, pa_num %d, pa_idx_base %d, tot_seg %d\n",
+ seg_idx, seg_pa_num, pa_idx_base, tot_seg_num);
+ err = xsc_cmd_exec(xdev, seg_in, in_len, &seg_out, sizeof(seg_out));
+ kfree(seg_in);
+ if (err || seg_out.hdr.status) {
+ pci_err(xdev->pdev, "failed to set mtt seg %d, err = %d, status = %d\n",
+ seg_idx, err, seg_out.hdr.status);
+ return -EINVAL;
+ }
+
+ pa_idx_base += seg_pa_num;
+ mtt_base += seg_pa_num;
+ }
+
+ return 0;
+}
+
+static int xsc_dereg_mr_via_cmdq(struct xsc_core_device *xdev, u32 mkey)
+{
+ struct xsc_unregister_mr_mbox_in in;
+ struct xsc_unregister_mr_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_DEREG_MR);
+ in.mkey = cpu_to_be32(xsc_mkey_to_idx(xdev, mkey));
+
+ err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
+ if (err || out.hdr.status) {
+ pci_err(xdev->pdev, "failed to dereg mr, err = %d, status = %d\n",
+ err, out.hdr.status);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int xsc_core_register_mr(struct xsc_core_device *xdev, struct xsc_core_mr *mr,
+ struct xsc_register_mr_mbox_in *in, int inlen)
+{
+ u32 mtt_base;
+ int err;
+
+ in->hdr.opcode = cpu_to_be16(XSC_CMD_OP_REG_MR);
+
+ err = xsc_set_mpt_via_cmdq(xdev, in, &mtt_base);
+ if (err) {
+ pci_err(xdev->pdev, "failed to set mpt via cmdq\n");
+ return err;
+ }
+
+ err = xsc_set_mtt_via_cmdq(xdev, in, mtt_base);
+ if (err) {
+ pci_err(xdev->pdev, "failed to set mtt via cmdq\n");
+ xsc_dereg_mr_via_cmdq(xdev, be32_to_cpu(in->req.mkey));
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xsc_core_register_mr);
+
+int xsc_core_dereg_mr(struct xsc_core_device *xdev, struct xsc_core_mr *mr)
+{
+ return xsc_dereg_mr_via_cmdq(xdev, mr->key);
+}
+EXPORT_SYMBOL_GPL(xsc_core_dereg_mr);
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/mr.h b/drivers/net/ethernet/yunsilicon/xsc/pci/mr.h
new file mode 100644
index 000000000..edafecda6
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/mr.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#ifndef __XSC_MR_H
+#define __XSC_MR_H
+
+#include "common/xsc_core.h"
+#include "common/xsc_cmd.h"
+
+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);
+
+#endif /* __XSC_MR_H */
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/pd.c b/drivers/net/ethernet/yunsilicon/xsc/pci/pd.c
new file mode 100644
index 000000000..7969dfefc
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/pd.c
@@ -0,0 +1,55 @@
+// 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"
+#include "pd.h"
+
+int xsc_core_alloc_pd(struct xsc_core_device *xdev, u32 *pdn)
+{
+ struct xsc_alloc_pd_mbox_in in;
+ struct xsc_alloc_pd_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_ALLOC_PD);
+
+ err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return xsc_cmd_status_to_err(&out.hdr);
+
+ *pdn = be32_to_cpu(out.pdn) & 0xffffff;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xsc_core_alloc_pd);
+
+int xsc_core_dealloc_pd(struct xsc_core_device *xdev, u32 pdn)
+{
+ struct xsc_dealloc_pd_mbox_in in;
+ struct xsc_dealloc_pd_mbox_out out;
+ int err;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_DEALLOC_PD);
+ in.pdn = cpu_to_be32(pdn);
+
+ err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
+ if (err)
+ return err;
+
+ if (out.hdr.status)
+ return xsc_cmd_status_to_err(&out.hdr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xsc_core_dealloc_pd);
diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/pd.h b/drivers/net/ethernet/yunsilicon/xsc/pci/pd.h
new file mode 100644
index 000000000..d9a99baea
--- /dev/null
+++ b/drivers/net/ethernet/yunsilicon/xsc/pci/pd.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021-2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#ifndef __XSC_PD_H
+#define __XSC_PD_H
+
+#include "common/xsc_core.h"
+
+int xsc_core_alloc_pd(struct xsc_core_device *xdev, u32 *pdn);
+int xsc_core_dealloc_pd(struct xsc_core_device *xdev, u32 pdn);
+
+#endif /* __XSC_PD_H */